2D Shapes / Outlines / Lines with thickness
Monkey Forums/Monkey Code/2D Shapes / Outlines / Lines with thickness
| ||
I became slightly distracted from what I was meant to be doing and ended up writing some shape functions. There are two versions provided for drawing a thick line, one uses sin cos, the other uses vector math. Haven't done a speed test yet so I have left them both in there. Use as you wish, enjoy :) Could do with some optimising but I need to get back to what I was meant to be doing! [edit] You may notice that after some DrawPoly commands there is a DrawLine command. This is to hide the seams that you get on HTLM5 target so it can be deleted for other targets. |
| ||
[edit] Added a Star and StarOutline to the code above |
| ||
thanks the star could be useful |
| ||
no problem, I'm taking shape requests if you have any? This is a lot more fun that what I should be doing lol |
| ||
I just noticed there is a demo in the code, it looks great, cant think of any shapes at the moment |
| ||
cheers, yea I though it would be good to show what the functions can do. Im tempted to add an Arrow... but I've just had another idea I want to try so maybe later.. |
| ||
Ooh, nice. I'm also thinking the star could be useful! Perhaps a spiral with options like line thickness, number of rotations and spread of "rings" would be an idea? |
| ||
Perhaps a spiral with options like line thickness, number of rotations and spread of "rings" would be an idea? Yea that sounds good! [edit] Just fixed an error in the DrawPie function. |
| ||
hi! The pie functions' start and end points base themselves on the screen coordinate system, not the mathematical coordinate system. Therefore, Y is inverted! DrawArcOutline doesn't do this, so if you're drawing a pie and an arc directly afterwards with the same arguments, the pieces won't line up. Thanks for the code, I'm borrowing those two functions and probably will extend it to draw rounded rectangles. |
| ||
Whew. Okay, I changed some routines around so that they start from 3 O'clock and run counter-clockwise, which is how I expect these routines should work. I swapped Cos/Sin in noodle's code in order to achieve this. In the following code, DrawArc and DrawPie will draw to the same place when given the same arguments. Furthermore, included are 2 rounded rectangle routines, DrawRoundedRect, and FillRoundedRect. Their usage should be fairly obvious, however, they will not draw correctly if the absolute value of the width or heights is less than r*2. There are multiple ways to solve this; I plan on adding in code to automatically reduce the radius size of the corners if W or H is too small, but I haven't yet because I don't know whether Floats are passed to the function by value or by reference, and I don't want to waste an extra Local variable if I don't have to. [monkeycode] Import mojo Function DrawRectOutline:Void(x:Float, y:Float, w:Float, h:Float) DrawLine(x,y,x+w,y) DrawLine(x,y,x,y+h) DrawLine(x,y+h,x+w,y+h) DrawLine(x+w,y,x+w,y+h) End Function Function DrawArc:Void(x:Float, y:Float, xRad:Float, yRad:Float, aStart:Float=0.0, aEnd:Float = 360.0, segments:Int=8) Local x1:Float = x + ( Cos( aStart ) * xRad ) Local y1:Float = y - ( Sin( aStart ) * yRad ) Local x2:Float Local y2:Float Local div:Float = ( aEnd - aStart ) / segments For Local i:Int = 1 To segments x2 = x + ( Cos( aStart + ( i * div )) * xRad ) y2 = y - ( Sin( aStart + ( i * div )) * yRad ) DrawLine(x1, y1, x2, y2) x1 = x2 y1 = y2 Next End Function '/ Draws a circle with sides or pie segment Function DrawPie:Void(x:Float, y:Float, xRadius:Float, yRadius:Float, aStart:Float=0.0, aEnd:Float=360.0, segments:Int=8 ) Local points:Float[( segments * 2 ) + 4 ] points[0] = x points[1] = y points[2] = x + Cos(aStart) * xRadius points[3] = y - Sin(aStart) * yRadius Local div:Float = (aEnd - aStart) / segments For Local i:Int = 1 To segments points[( i + 1 ) * 2 ] = x + Cos( aStart + ( i * div )) * xRadius points[(( i + 1 ) * 2 ) + 1 ] = y - Sin( aStart + ( i * div )) * yRadius Next DrawPoly( points ) End Function Function DrawRoundedRect:Void (x:Float, y:Float, w:Float, h:Float, r:Float=8, segments:Int=8) DrawLine(x,y+r,x,y+h-r) 'Left DrawLine(x+r,y,x+w-r,y) 'Top DrawLine(x+r,y+h,x+w-r,y+h) 'Bottom DrawLine(x+w,y+r,x+w,y+h-r) 'Right DrawArc(x+r,y+r,r,r,90,180,segments) 'UL DrawArc(x+w-r,y+r,r,r,0,90,segments) 'UR DrawArc(x+r,y+h-r,r,r,180,270,segments) 'LL DrawArc(x+w-r,y+h-r,r,r,270,360,segments) 'LR End Function Function FillRoundedRect:Void(x:Float, y:Float, w:Float, h:Float, r:Float=8, segments:Int=8) DrawPie(x+r,y+r,r,r,90,180,segments) 'UL DrawPie(x+w-r,y+r,r,r,0,90,segments) 'UR DrawPie(x+r,y+h-r,r,r,180,270,segments) 'LL DrawPie(x+w-r,y+h-r,r,r,270,360,segments) 'LR 'We could do this with just 2 calls, but the rects would overlap and create a dark box in the center if alpha were to be <1 DrawRect(x+r,y,w-r-r,r) 'Top DrawRect(x+r,y+h-r,w-r-r,r) 'Bottom DrawRect(x,y+r,r,h-r-r) 'Left DrawRect(x+w-r,y+r,r,h-r-r) 'Right DrawRect(x+r,y+r,w-r-r,h-r-r) 'Center End Function [/monkeycode] Someone else, feel free to modify this code to work with the line thickness stuff. I'm not sure how slow that would be, so I left it out. |
| ||
thanks nice job, I had been meaning to revisit this and improve it, now I don't need to :) I hope you don't mind, I made a small change to your FillRoundedRect: [monkeycode] Function FillRoundedRect:Void(x:Float, y:Float, w:Float, h:Float, r:Float=8, segments:Int=8) DrawPie(x+r,y+r,r,r,90,180,segments) 'UL DrawPie(x+w-r,y+r,r,r,0,90,segments) 'UR DrawPie(x+r,y+h-r,r,r,180,270,segments) 'LL DrawPie(x+w-r,y+h-r,r,r,270,360,segments) 'LR DrawRect( x, y + r, w, h - r - r ) 'Centre + Left/Right DrawRect( x + r, y, w - r - r, r ) 'Top DrawRect( x + r, y + h - r, w - r - r, r ) 'Bottom End Function [/monkeycode] and you can add this to the top of the RoundedRect Functions to reduce the radius to prevent errors. [monkeycode] If h < w If r > ( h / 2.0 ) Then r = ( h / 2.0 ) Else If r > ( w / 2.0 ) Then r = ( w / 2.0 ) Endif [/monkeycode] |
| ||
cool! I was holding off on setting "r" to a new value within these routines until I knew whether or not it would automatically copy the value back to the caller (see http://www.monkeycoder.co.nz/Community/posts.php?topic=3282 ). Now, everything's fantastic. Just FYI, the checks for radius size fail if any of the values are negative. To keep it relatively sane (ie, the shape still stays within bounds even though the corners invert), only change the radius from checking absolute value, like so: [monkeycode] If h < w If r > Abs( h / 2.0 ) Then r = ( h / 2.0 ) Else If r > Abs( w / 2.0 ) Then r = ( w / 2.0 ) End If [/monkeycode] |
| ||
Yes, I didn't consider negative values. I quickly wrote a Draw Heart Function with outline, might prove useful. (Not extensively tested yet) [monkeycode] Function DrawHeart : Void( x : Float, y : Float, w : Float, h : Float, segments : Int = 8 ) 'Draws 4 Sided Pie. Diamond, hearts centre. DrawPie( x, y, w, h, 0, 360.0, 4 ) 'Draws 2 Pies for the hearts ears. Radius = 1 / Sqrt( 2 ) = 0.707. Angles are larger than 180 to prevent seams. DrawPie( x - ( 0.5 * w ), y - ( 0.5 * h ), 0.707 * w, 0.707 * h, 0, 270, segments ) DrawPie( x + ( 0.5 * w ), y - ( 0.5 * h ), 0.707 * w, 0.707 * h, -90, 180, segments ) End Function Function DrawHeartOutline : Void( x : Float, y : Float, w : Float, h : Float, segments : Int = 8 ) 'Draws 2 Sided Arc. Bottom of Diamond, hearts centre. DrawArc( x, y, w, h, 180.0, 360.0, 2 ) 'Draws 2 Arcs for the hearts ears. 1 / Sqrt( 2 ) = 0.707 DrawArc( x - ( 0.5 * w ), y - ( 0.5 * h ), 0.707 * w, 0.707 * h, 45, 225, segments ) DrawArc( x + ( 0.5 * w ), y - ( 0.5 * h ), 0.707 * w, 0.707 * h, -45, 135, segments ) End Function [/monkeycode] I have also just written a Draw Spiral Function, outline only at the moment and still a WIP: (increasing the passed aEnd value loops the spiral more than once) [monkeycode] Function DrawSpiralOutline : Void( x : Float, y : Float, xRad : Float, yRad : Float, aStart : Float=0.0, aEnd : Float = 360.0, segments : Int = 8 ) Local x1 : Float = x Local y1 : Float = y Local x2 : Float Local y2 : Float Local div : Float = ( aEnd - aStart ) / segments For Local i : Int = 1 To segments x2 = x + ( Cos( aStart + ( i * div )) * ( xRad * ( Float( i ) / segments ))) y2 = y - ( Sin( aStart + ( i * div )) * ( yRad * ( Float( i ) / segments ))) DrawLine( x1, y1, x2, y2) x1 = x2 y1 = y2 Next End Function [/monkeycode] |
| ||
NoOdle! You are my most favorite person of the week! Or maybe even month! I was picking my brains on how to render a pie chart and i diddnt even know the 'DrawPoly()' existed. This makes my task alot easier, Thank you! |
| ||
Yes these are very handy functions, I use them to help render my custom GUI module. +1 |