Andres is a DZone Zone Leader and has posted 143 posts at DZone. You can read more from them at their website. View Full User Profile

GraphicsBuilder Tutorial I: Shapes

02.02.2008
| 28512 views |
  • submit to reddit

Polygons and Donuts

We have seen that GraphicsBuilder has a polygon shape which can draw polygons, but what about regular polygons? those shapes that have a fixed set of sides and can be inscribed in a circle, like a pentagon or hexagon. With only the basic polygon shape you will have to compute all the points over and over again, its good then that GraphicsBuilder provides a base shape for this type of polygons.

def colors = ['red','darkOrange','blue','darkGreen']
(0..3).each { index ->
regularPolygon( cx: 50 + (index*110), cy: 50, radius: 40,
borderColor: 'black', sides: 3+index, fill: colors[index] )
regularPolygon( cx: 50 + (index*110), cy: 160, radius: 40,
borderColor: 'black', sides: 7+index, fill: colors[index] )
}

Here you can see a great feature of builders in Groovy, you can mix any groovy sentence with your building nodes. What we have here is a range iterated over each element, the value is used to calculate the offset of the next shape and the number of sides the polygon will have. Because these shapes are centered you may be tempted to draw a smaller shape in the same center and create a donut, GraphicsBuilder helps you again with a shape that does that, which will produce a circular donut if you do not specify a sides property.

def colors = ['red','darkOrange','blue','darkGreen']
(0..3).each { index ->
donut( cx: 50 + (index*110), cy: 50, or: 40, ir: 20,
borderColor: 'black', sides: 3+index, fill: colors[index] )
donut( cx: 50 + (index*110), cy: 160, or: 40, ir: 20,
borderColor: 'black', sides: 7+index, fill: colors[index] )
}

The only thing we changed from the previous example was the node name, from regularPolygon to donut and that's it.

Stars and Rays

Having code that can calculate the points for a regular polygon enables drawing other shapes that share similar properties, for example stars which have the same number of points as a polygon donut, but this time the inside shape is rotated in a way where each vertex is pointing to the corresponding middle point in the outer shape segments.

def colors = ['red','darkOrange','blue','darkGreen']
(0..3).each { index ->
star( cx: 50 + (index*110), cy: 50, or: 40, ir: 15, borderColor: 'black',
count: 2+index, fill: colors[index] )
star( cx: 50 + (index*110), cy: 140, or: 40, ir: 15, borderColor: 'black',
count: 7+index, fill: colors[index] )
}

Making a comparison against donuts, stars have a count property instead of sides, aside from that they have the same properties. What would happen if you draw lines from the center to each point of a regular polygon, joining the odd and even points with a segment, and leaving the event to odd joins out? you'll get a ray like shape.

def colors = ['red','darkOrange','blue','darkGreen']
(0..3).each { index ->
rays( cx: 50 + (index*110), cy: 50, radius: 40,
borderColor: 'black', rays: 2+index, fill: colors[index] )
rays( cx: 50 + (index*110), cy: 140, radius: 40, rounded: true,
borderColor: 'black', rays: 2+index, fill: colors[index] )
rays( cx: 50 + (index*110), cy: 230, radius: 40, extent: 0.75,
borderColor: 'black', rays: 2+index, fill: colors[index] )
rays( cx: 50 + (index*110), cy: 320, radius: 40, extent: 0.75, rounded: true,
borderColor: 'black', rays: 2+index, fill: colors[index] )
}

Again shared properties with donuts and stars. Rays can have around segment between points instead of a linear one. The angle of each ray can also be changed with an extent property, whose value must be in the range [0..1].

Triangles

In the last batch of geometrical shapes we find triangles: equilateral, isosceles and rectangle (scalene triangles can be drawn with a polygon operation). Triangles must have at least a set of coordinates (x,y) and a width property defined (will result in an equilateral triangle), if a height property is specified then it may be an isosceles triangle if a rightAngleAt property is not set, which would result in a rectangle triangle.


triangle( x: 10, y: 50, width: 50, borderColor: 'black', fill: 'red' )
triangle( x: 70, y: 50, width: 50, height: 20, borderColor: 'black', fill: 'orange' )
triangle( x: 130, y: 50, width: 50, rightAngleAt: 'start',
borderColor: 'black', fill: 'darkGreen' )
triangle( x: 190, y: 50, width: 50, rightAngleAt: 'end',
borderColor: 'black', fill: 'blue' )
triangle( x: 10, y: 90, width: 50, borderColor: 'black', fill: 'red', angle: 315 )
triangle( x: 70, y: 90, width: 50, height: 20,
borderColor: 'black', fill: 'orange', angle: 315 )
triangle( x: 130, y: 90, width: 50, rightAngleAt: 'start', angle: 315,
borderColor: 'black', fill: 'darkGreen' )
triangle( x: 190, y: 90, width: 50, rightAngleAt: 'end', angle: 315 ,
borderColor: 'black', fill: 'blue' )

All the shapes in this section (from polygons to triangles) accept an angle property to perform quick rotations. All shapes accept transformations like scaling and rotation which we will explore in another tutorial.

Published at DZone with permission of its author, Andres Almiray.

Comments

Christopher Brown replied on Mon, 2008/02/04 - 3:45am

Hi Andres,

How does this relate to https://scenegraph.dev.java.net/ ?

I'm assuming that with the Groovy graphics builder, you would need to use the builder on every UI repaint to completely repaint the image.  The scene graph API aims to create shapes that stay defined in memory between repaints, a bit like SVG without the XML, so with the SGAPI you define your circle once then have the choice of manipulating it through time.

Other questions: if not SGAPI-compatible, is there clipping support for the groovy graphics (i.e.: ways of making it easier to deal with) so that you don't waste time drawing shapes outside of the painting area?

Thanks,

Chris 

Mahesh Kumar Nayak replied on Fri, 2011/05/20 - 3:48pm

Hi,

I do complite agree with Christopher.

http://www.glinthost.com

Andres Almiray replied on Mon, 2008/02/04 - 12:44pm in response to: Christopher Brown

Chris, GraphicsBuilder and SceneGraph share many traits but they are essentially two different and unrelated implementations. When I saw the announcement of SceneGraph my first reaction was to rework GraphicsBuilder to support it but because their API is still in flux I refrained to do so. Perhaps when their API goes to 1.0 GraphicsBuilder will be able to export/import SGAPI code, that would be great.

On the UI repaint, it depends on what you are trying to accomplish, GB includes a basic component where you can place your drawings: GraphicsPanel. This component is smart enough to redraw whenever it needs to, and only those places that have actually changed, which means it honors the clip. All shapes take into consideration the curret clip, no need to waste cycles, there is also an option to specify custom clips with the clip() node. You can also render to images directly and use your own cache mechanism.

GB can also reuse shape definitions, either with a normal Groovy variable or with an special 'asShape' property which I didn't show in this tutorial, but now that the topic has surfaced I will make sure it is included in the next part.

If you care to know more about GraphicsBuilder before I finish the other parts of the tutorial, please visit the project's site, you will also find more info at my blog, you'll see that GB has more to offer that what has been shown so far here. 

Cheers,
Andres

Andres Almiray replied on Mon, 2008/02/04 - 12:48pm in response to: Christopher Brown

You may also find this link useful, the images are snapshots of GraphicsPanel in action, a custom clip has been set (so that the radial lines do not extend beyond the rounded rectangle), you will also see other options as paints (and multipaints, a unique feature so far).

Cheers,
Andres

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.