Back to
Joshy's latest doodle, I managed to get something out after meddling with GraphicsBuilder's recent updates. So here goes
There were some minor problems along the way, like shadow filter not working as expected (this filter is not based on the one provided by Swingx), that is why I choose to use shapes instead of images, updating the border while dragging instead of applying a shadow filter.
Let's step into each element of this groodle, first we will look at each shape, starting with the frog face. If it looks familiar is because it is based on the
multipaint sphere example
def width = 150 def height = 150 def cx = width/2 + 10 def cy = height/2 + 10 antialias on group( name: "frogface" ){ circle( cx: cx, cy: cx, r: width/2, bc: no ){ multiPaint { colorPaint( color('green') ) radialGradient( cx: cx, cy: cy, r: width/2 ) { stop( s: 0, c: color(r: 6, g: 160, b: 76, a: 127) ) stop( s: 1, c: color(a: 204) ) } radialGradient( cx: cx, cy: cy, fx: 55, fy: 35, r: width/1.4 ){ stop( s: 0, c: color('white').derive(a:0.5) ) stop( s: 0.5, c: color('white').derive(a:0) ) } } } // left eye circle( cx: 40, cy: 50, r: 20, bc: 'black', f: 'white' ) circle( cx: 43, cy: 53, r: 12, bc: 'none', f: 'black' ) // right eye circle( cx: 130, cy: 50, r: 20, bc: 'black', f: 'white' ) circle( cx: 127, cy: 53, r: 12, bc: 'none', f: 'black' ) // nostrils circle( cx: 75, cy: 80, r: 4, bc: 'black', f: 'black' ) circle( cx: 95, cy: 80, r: 4, bc: 'black', f: 'black' ) // mouth ellipse( cx: 85, cy: 120, rx: 30, ry: 10, bc: 'black', f: 'black' ){ transformations { rotate( x: 85, y: 120, angle: -10 ) } } transformations { translate( x: 250, y: 60 ) } }Well, that was easy, now for each draggable piece of clothing. They are simple shapes, some area operations, and the hat has a pair of filters to give it a little depth (these pieces of clothing are not as nice as Joshy's hand drawn ones)
// eye patch add( bc: 'black', f: 'black', id: 'eyepatch' ) { rect( x: 10, y: 55, w: 150, h: 12 ) circle( cx: 40, cy: 80, r: 20 ) } // goatee triangle( x: 60, y: 210, w: 40, angle: 180, f: 'brown', id: 'goatee' ) // moustache add( f: 'brown', id: 'moustache' ){ ellipse( cx: 65, cy: 140, rx: 25, ry: 8 ){ transformations { rotate( x: 65, y: 140, angle: -12 ) } } ellipse( cx: 105, cy: 140, rx: 25, ry: 8 ){ transformations { rotate( x: 105, y: 140, angle: 12 ) } } } // hat add( f: 'orange', id: 'hat' ){ triangle( x: 70, y: 230, w: 140, h: 40 ) rect( x: 100, y: 180, w: 80, h: 40 ) filters { shapeBurst( merge: true, type: 'up' ){ linearColormap( color1: color('orange').darker(), color2: 'orange' ) } lights() } }
One last touch before ending the build closure is attaching a Swing button (not shown in the image) that will reset the position of all pieces of clothing. Thanks to Groovy's closures and having ids associated with each piece the code is pretty straight forward.
swingView { button( 'Reset', x: 5, y: 5, actionPerformed: {e -> ['eyepatch','goatee','moustache','hat'].each { gb."$it".txs.clear() } }) }
We reuse the each/closure trick outside the building closure to assign the mouse event handlers to each piece
['eyepatch','goatee','moustache','hat'].each { def shape = gb."$it" shape.mouseDragged = dragShape shape.mousePressed = startDrag shape.mouseReleased = endDrag shape.mouseExited = endDrag }
Alright, so now we must define 3 closures to handle each event and then we are done, right? well almost. It turns out it is not so easy to do drag&,drop with gfx, at least not in as many lines as its jfx counterpart, because as of today d&d support is not automatically included. If you look closely at the jfx example you will notice that it is just a matter of binding
e.dragTransition.[x,y], hopefully it will be too in gfx during the next days. For now let me show you how it is done, there is definitely room for improvement so if anyone notices something please let me know!

def dragShape = { e -> def shape = e.target def bounds = shape.bounds def location = shape.props.location def dx = e.event.x - shape.props.dragPoint.x def dy = e.event.y - shape.props.dragPoint.y if( !shape.props.dragging ){ // start of drag if( !shape.txs['location'] ){ shape.txs
As you can see the code is a bit verbose right now but it gets the job done, looks similar to the
revisited dragging example previously posted. I have posted the code for this and the other groodles
here, you can take them for a spin as soon as GraphicsBuilder 0.5 is released.
Keep on Groovying!