CS456 Selection
Program - Selection
goal
Exercise the geometry of selection.
Application
Modify your application from the previous program so that whenever your panel component receives a mouse-up event it will take that point's position and process each of your Selectable objects in front to back order. Groups should work recursively inside their contents.

When an object is selected, a path of contents indices and/or character indices down to the selected location is returned. Control points/cursor location should be drawn for selected objects.

Selectable
You should create a Java interface called Selectable that contains the methods:
  • int [] select( double x, double y, int myIndex, AffineTransform transform) the select() method takes a point x,y and if the object or its contents are selected then it returns a path to the selected object. The x, y coordinates are in the coordinates of your panel, not in the transformed coordinates. If the object or its contents are not selected, then NULL is returned.

    The transform parameter contains the full transform from current contents coordinates to the coordinates of your panel.

  • Point2D [] controls() Returns an array of control points that can be used to modify the geometry of the object.
Select
Create a new Select object that is both Drawable and Selectable. This should be a subclass of Group. In addition to its "contents" attribute, it should also have a "selected" attribute which contains an array of integer indices into its contents. When its select() method is called it should behave as a Group (see below) but should also store the path returned by its child selection in its "selected" attribute. It its selected attribute changes, it should also call repaint() to make certain the control points are correctly updated on the screen using the paint method.

As part of its paint() method it should first draw all of its contents using the paint() method from group and should then check to see if it has a "selected" path. If it does then it should find that object using the path, retrieve its control points using the controls() method and draw the control points. Note that this must correctly handle the transformations of any nested groups.

Selectable objects
All of your Drawable objects from the previous assignment should now implement Selectable as well.

The objects should respond to select() and controls() in the following ways:

  • Group{ contents:[ ... ], sx:1.0, sy:1.0, rotate:0.0, tx:0.0, ty:0.0 } A For select() this object will recursively call select on its contents. Be sure to correctly account for the transformation by transforming the selection point by the inverse of the transformation on this group and by pass That will bring the selection point into the coordinate system of the group contents. If any of the contents are selected then this should take the selection path from the selected child and add this group's index onto the front and return the more complete path. This will recursively build a path to the selected object.

    This object has no controls of its own.

  • Line{ x1:10, y1:5, x2: 20, y2:40, thickness:3, color:{r:0,g:0,b:0} } This will select if the selection point is within three pixels of the line on either side. It returns an array containing only the myIndex parameter if selected, null otherwise. For control points, it returns its two end points.
  • Rect{ left:0, top:100, width:10, height:10, thickness:2, border:{r:0,g:0,b:0}, fill:{r:0,g:0,b:128} } If the rect is filled, then it is selected if the selection point is inside the rectangle. If it is not filled then the selection point must be within 3 pixels of one of the edges. For control points it returns its four corners.
  • Ellipse{ same as Rect } Behaves the same as Rect except that selection is based on the equation of the ellipse rather than the rectangle.
  • Text{ text:"Draw This", x:10,y:100, font:"Times", size:10 } This is selected if the selection point is anywhere in the bounding box of the drawn length of the string and the full ascent of the font. It returns only the left end of its baseline as the control point.
  • Polyline{ points:[ {x:10,y:10}, ... ], thickness:1, color:{r:100,g:0,b:0} } This is selected if the selection point is within 3 pixels of any of the line segments. It returns its points as the control points.
  • Polygon{ points: [ {x:0,y:0}, . . .], thickness:1, border:{r:100,g:0,b:0}, fill:{r:255,g:255,b:255} } We will not do polygon selection at this time.
hint
Be sure to correctly account for transformations. Remember that the distance of 3 pixels will be changed by any scaling factors. One way is to transform not only x,y into local coordinates but also (x+3,y) and (x,y+3). After all three points are transformed you can use the distances between them to estimate what "3 pixels" means in your local coordinate system.
grading
__ 2) Line selects correctly (including painting of control points)
__ 2) Rect selects correctly (including painting of control points)
__ 2) Ellipse selects correctly (including painting of control points)
__ 2) Text selects correctly (including painting of control points)
__ 2) Polyline selects correctly (including painting of control points)
__ 2) Rect selects correctly when inside of a translation group
__ 3) Rect selects correctly when inside of a translate and rotate
__ 2) Rect selects correctly when inside of full scale, rotate, translate.
__ 2) Line selects correctly (3 pixels) when inside of a scale and rotate.
test 1
Select{ contents:[
		Line{x1:10,y1:20,x2:30, y2:40, thickness:3, color:{r:255,g:255,b:0} },
		Rect{ left:5,top:10,width:30, height:50, thickness:1, border:{r:0,g:0,b:0} },
		Rect{ left:30,top:30,width:30, height:50, thickness:1, 
			border:{r:0,g:0,b:0}, fill:{r:100,g:100,b:200} },
		Ellipse{ left:5,top:70,width:30, height:50, thickness:1, border:{r:0,g:0,b:0} },
		Ellipse{ left:30,top:90,width:30, height:50, thickness:1, 
			border:{r:0,g:0,b:0}, fill:{r:100,g:100,b:200} },
		Text{ text:"Hello", x:5,y:180,font:"sans-serif",size:10 },
		Polyline{ points:[ {x:40,y:10}, {x:50,y:20}, {x:60,y:10} ],
			thickness:4, color:{r:100,g:255,b:100} },
		Polygon{ points:[ {x:5,y:220}, {x:35,y:220}, {x:20,y:250} ],
			thickness:2, border:{r:0,g:0,b:0}, fill:{r:200,g:200,b:255} }
	], selected: [1]
}
test 2
Select{ contents:[
		Line{x1:10,y1:20,x2:30, y2:40, thickness:3, color:{r:255,g:255,b:0} },
		Rect{ left:5,top:10,width:30, height:50, thickness:1, border:{r:0,g:0,b:0} },
		Rect{ left:30,top:30,width:30, height:50, thickness:1, 
			border:{r:0,g:0,b:0}, fill:{r:100,g:100,b:200} },
		Ellipse{ left:5,top:70,width:30, height:50, thickness:1, border:{r:0,g:0,b:0} },
		Ellipse{ left:30,top:90,width:30, height:50, thickness:1, 
			border:{r:0,g:0,b:0}, fill:{r:100,g:100,b:200} },
		Text{ text:"Hello", x:5,y:180,font:"sans-serif",size:10 },
		Polyline{ points:[ {x:40,y:10}, {x:50,y:20}, {x:60,y:10} ],
			thickness:4, color:{r:100,g:255,b:100} },
		Polygon{ points:[ {x:5,y:220}, {x:35,y:220}, {x:20,y:250} ],
			thickness:2, border:{r:0,g:0,b:0}, fill:{r:200,g:200,b:255} },
		Rect{ left:0,top:0,width:4,height:2, fill:{r:255, g:200,b:200} }
	], selected:[1],
	sx:1.0, sy:2.0, tx:400, ty:500, rotate:45
}