On this page, we will begin our discussion of how the user will plot points on the Cartesian plane that we have worked so hard to generate. The user will want to specify the coordinates of a point and also customize other properties, such as its color and, in particular, its shape; for this purpose, we will develop the PlotShape facility.
Plotting is an operation that lends itself well to streaming. When we introduce plotting into the CartesianPlane class, we will use a stream of commands that follows a pattern suggested by the following example:
- Set point color;
- Set point shape;
- Draw point at (x1,y1);
- Draw point at (x2,y2);
- …
- Draw point at (xn,yn);
- Change point color;
- Change point shape;
- Draw point at (xn+1,yn+1);
- etc.
To support this streaming strategy, we will develop the PlotCommand facility.
We’ll start by defining the PlotShape and PlotCommand facilities, both of which will reside in the com.acmemail.judah.cartesian_plane package.
GitHub repository: Cartesian Plane Part 8
Previous page: Cartesian Plane Lesson 8 Page 1: Notification Manager
The PlotShape Facility
The foundation of the PlotShape facility is the PlotShape interface. This is a functional interface whose abstract method is Shape getShape(double xco, double yco). The coordinates are the x- and y-coordinates of the pixel at which a point is drawn (I anticipate that this will be used as the center point of the shape, but that’s up to the implementer). Users can choose an implementation of the interface we supply, or write their own. The declaration of the interface itself is straightforward:
Review: Shape is an interface in the Java AWT that describes a geometric figure. Classes in the AWT that implement Shape include Arc2D, CubicCurve2D, Ellipse2D, Line2D, Path2D, Polygon, and Rectangle2D.
public interface PlotShape
{
Shape getShape( double xco, double yco );
}
We will supply four pre-built implementations:
- PointShape: this class will encapsulate a single pixel drawn at the given coordinates. A single pixel is represented by a Line2D 1 pixel in length*. It has no constructors.
- CircleShape: this class
encapsulates a circle of given radius whose center is at the given pixel coordinates. A circle is represented by an Ellipse2D bounded by a square. It has a constructor that sets the circle’s radius, and a default constructor that sets the radius to the spacing between minor tick marks (recall that spacing is computed using GRID_UNIT / TICK_MINOR_MPU). - SquareShape: this class represents a square with a center at the
given coordinates. It is represented by a Rectangle2D with equal width and height. It has a constructor that sets the side length, and a default constructor that sets it to twice the spacing between minor tick marks. - DiamondShape: this class
encapsulates a four-sided polygon, enclosed in a rectangle, with vertices at the center of each side of the rectangle. It has a constructor that sets the width and height of the enclosing rectangle, and a default constructor that sets them to 2 times the minor-tick spacing.
*It’s worth noting that, while a 1 pixel line is the traditional way to represent a point, the results may vary depending on the implementation of the graphics context and the current values of properties contained in it. It might be worth exploring an alternative, such as a 1×1 rectangle.
Here’s the code for the four classes.
1 2 3 4 5 6 7 8 9 10 11 12 | public class PointShape implements PlotShape { /** Shape to use to plot a point. */ private final Line2D shape = new Line2D.Float(); @Override public Shape getShape( double xco, double yco ) { shape.setLine( xco, yco, xco, yco ); return shape; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | public class CircleShape implements PlotShape { /** PropertyManager singleton; declared here for convenience. */ private static final PropertyManager PMGR = PropertyManager.INSTANCE; private static final String GRID_UNIT = CPConstants.GRID_UNIT_PN; private static final String TIC_MINOR_MPU = CPConstants.TIC_MINOR_MPU_PN; /** Shape to use to plot a point. */ private final Ellipse2D shape = new Ellipse2D.Float(); /** Radius of the circle. */ private final float radius; /** * Length of a side of the bounding rectangle * that describes the circle. */ private final float side; /** * Default constructor. * Instantiate a circle with radius * equal to the spacing between minor ticks. */ public CircleShape() { this( getSpacing() ); } /** * Constructor. * Sets the radius of the circle * to a given value. * * @param radius the given value */ public CircleShape( float radius ) { this.radius = radius; side = 2 * radius; } @Override public Shape getShape( double xco, double yco ) { double cornerXco = xco - radius; double cornerYco = yco - radius; shape.setFrame( cornerXco, cornerYco, side, side ); return shape; } /** * Calculate the spacing between minor ticks * based on the current grid unit and * tick minor MPU. * * @return the current spacing between minor ticks */ private static float getSpacing() { float gridUnit = PMGR.asFloat( GRID_UNIT ); float mpu = PMGR.asFloat( TIC_MINOR_MPU ); float spacing = gridUnit / mpu; return spacing; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | public class SquareShape implements PlotShape { /** PropertyManager singleton; declared here for convenience. */ private static final PropertyManager PMGR = PropertyManager.INSTANCE; /** Grid unit property name; declared here for convenience. */ private static final String GRID_UNIT = CPConstants.GRID_UNIT_PN; /** Minor tick/LPU property name; declared here for convenience. */ private static final String TIC_MINOR_MPU = CPConstants.TIC_MINOR_MPU_PN; /** Shape to use to plot a point. */ private final Rectangle2D shape = new Rectangle2D.Float(); /** Length of a side of the square. */ private final float side; /** * Offset to position the upper-left corner of the square * so that the center of the square * is at the coordinates passed to the getShape method. */ private final float cornerOffset; /** * Default constructor. * Sets the side of the square * to the spacing between minor tick marks. */ public SquareShape() { this( getSpacing() * 2 ); } /** * Constructor. * Sets the side of the square * to a given value. * * @param side the given value */ public SquareShape( float side ) { this.side = side; cornerOffset = side / 2; } @Override public Shape getShape( double xco, double yco ) { double cornerXco = xco - cornerOffset; double cornerYco = yco - cornerOffset; shape.setFrame( cornerXco, cornerYco, side, side ); return shape; } /** * Calculate the spacing between minor ticks * based on the current grid unit and * tick minor MPU. * * @return the current spacing between minor ticks */ private static float getSpacing() { float gridUnit = PMGR.asFloat( GRID_UNIT ); float mpu = PMGR.asFloat( TIC_MINOR_MPU ); float spacing = gridUnit / mpu; return spacing; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | /** * Encapsulates a Shape to use * to plot points * on the CartesianPlane grid. * This Shape is constituted by * a rotated rectangle with a given width and height. * * @author Jack Straub * */ public class DiamondShape implements PlotShape { /** * PropertyManager instance. * Declared here for convenience. */ private static final PropertyManager pmgr = PropertyManager.INSTANCE; /** Shape to use to plot a point. */ private final Path2D shape = new Path2D.Float(); /** Width of bounding rectangle. */ private final float rectWidth; /** Height of bounding rectangle. */ private final float rectHeight; /** * Default constructor. * Set the width of the bounding rectangle * to 2 * minor tic spacing, * and height to major tic length. */ public DiamondShape() { this( pmgr.asFloat( CPConstants.TIC_MINOR_MPU_PN ) * 2, pmgr.asFloat( CPConstants.TIC_MAJOR_LEN_PN ) ); } /** * Constructor. * Sets the width and height of the rectangle * to the given values. * * @param rectWidth the given width * @param rectHeight the given height */ public DiamondShape( float rectWidth, float rectHeight ) { float rWidth = rectWidth; float rHeight = rectHeight; this.rectWidth = rWidth; this.rectHeight = rHeight; } @Override public Shape getShape( double xco, double yco ) { double leftXco = xco - rectWidth / 2f; double rightXco = leftXco + rectWidth; double topYco = yco - rectHeight / 2; double bottomYco = topYco + rectHeight; shape.reset(); shape.moveTo( leftXco, yco ); shape.lineTo( xco, topYco ); shape.lineTo( rightXco, yco ); shape.lineTo( xco, bottomYco ); shape.closePath(); return shape; } } |
The PlotCommand Facility
This facility allows command objects to be instantiated and then streamed to a utility that plots points on a graph. Examples of commands include “set color to red” and “plot a point in a given CartesianPlane.” It is built around the PlotCommand functional interface, whose abstract method is void execute(). The declaration of the interface looks like this:
public interface PlotCommand
{
void execute();
}
The facility is designed so that users can either create their own commands or use a prebuilt command. There are three prebuilt implementations of this interface:
- PlotPointCommand: this command directs the CartesianPlane object to plot a point at a given coordinate (note this is not a pixel location; CartesianPlane maps the given Cartesian coordinate to the appropriate pixel coordinate). It has one constructor: PlotPointCommand( CartesianPlane plane, float xco, float yco ), where plane is the Cartesian plane in which to draw the given point, and xco and yco are the coordinates of the given point.
- PlotShapeCommand: this command directs the CartesianPlane object to change the shape used to draw a point. It has one constructor, PlotShapeCommand(CartesianPlane plane, PlotShape shape), where plane is the target CartesianPlane object and shape is the shape to change to.
- PlotColorCommand: this command directs the CartesianPlane object to change the color used to draw a point. It has one constructor, PlotColorCommand(CartesianPlane plane, Color color), where plane is the target CartesianPlane object and color is the new color to apply.
This facility will require new methods in the CartesianPlane class:
- public void plotPoint( float userXco, float userYco )
Draw a point at the given Cartesian coordinates. - public void setPlotColor( Color color )
Set the drawing color to the given value. - public void setPlotShape( PlotShape plotShape )
Set the shape of the next point(s) to the given object.
We’ll discuss detailed changes to the CartesianPlane class on the next page. Here is the source code for the three prebuilt classes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | public class PlotPointCommand implements PlotCommand { private static final String format = "PlotCommand: (%4.2f,%4.2f)"; private final CartesianPlane plane; private final float xco; private final float yco; /** * Constructor. * Instantiates a command to plot a point in a * {@linkplain CartesianPlane}. * * @param plane the plane in which the plot is to be plotted * @param xco the x-coordinate of the point * @param yco the y-coordinate of the point */ public PlotPointCommand( CartesianPlane plane, float xco, float yco ) { this.plane = plane; this.xco = xco; this.yco = yco; } @Override public String toString() { String str = String.format( format, xco, yco ); return str; } @Override public void execute() { plane.plotPoint( xco, yco ); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public class PlotColorCommand implements PlotCommand { private final CartesianPlane plane; private final Color color; /** * Constructor. * Determines the plane in which the plot color * is to be set and the color to set it to. * * @param plane the plane in which the plot color is to be set * @param color the color to set the plot color to. */ public PlotColorCommand( CartesianPlane plane, Color color ) { this.plane = plane; this.color = color; } @Override public void execute() { plane.setPlotColor( color ); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public class PlotShapeCommand implements PlotCommand { private final CartesianPlane plane; private final PlotShape shape; /** * Constructor. * Determines the shape to be used * when plotting a point * in the Cartesian plane. * * @param plane the Cartesian plane in which the shape is to be used * @param shape the shape to use */ public PlotShapeCommand( CartesianPlane plane, PlotShape shape ) { this.plane = plane; this.shape = shape; } @Override public void execute() { plane.setPlotShape( shape ); } } |
Summary
On this page, we discussed two new facilities to support plotting in the Cartesian plane:
- PlotShape is a functional interface that allows users to specify the shape of a point drawn on the plane. Users can utilize a prebuilt shape or implement their own.
- PlotCommand is a functional interface that allows users to control a stream of commands to a plotting facility. Examples of commands include changing the color to draw with, changing the shape of the drawing, and drawing a point at coordinates (x, y).
On the next page, we will extend the CartesianPlane class to execute a stream of commands that control a plot on the plane.
encapsulates a circle of given radius whose center is at the given pixel coordinates. A circle is represented by an Ellipse2D bounded by a square. It has a constructor that sets the circle’s radius, and a default constructor that sets the radius to the spacing between minor tick marks (recall that spacing is computed using GRID_UNIT / TICK_MINOR_MPU).
given coordinates. It is represented by a Rectangle2D with equal width and height. It has a constructor that sets the side length, and a default constructor that sets it to twice the spacing between minor tick marks.
encapsulates a four-sided polygon, enclosed in a rectangle, with vertices at the center of each side of the rectangle. It has a constructor that sets the width and height of the enclosing rectangle, and a default constructor that sets them to 2 times the minor-tick spacing.