Main Page

Previous Next


Classes that define geometric shapes are contained in the java.awt.geom package, so to use them in a class we will need an import statement for this package at the beginning of the class file. You can add one to right now if you like. While the classes that define shapes are in java.awt.geom, the Shape interface is defined in java.awt, so you will usually need to import class names from both packages into your source file.

Any class that implements the Shape interface defines a shape – visually it will be some composite of straight lines and curves. Straight lines, rectangles, ellipses and curves are all shapes.

A graphic context knows how to draw Shape objects. To draw a shape on a component, you just need to pass the object defining the shape to the draw()method for the Graphics2D object for the component. To look at this in detail, we'll split the shapes into three groups, straight lines and rectangles, arcs and ellipses, and freeform curves. First though, we must take a look at how points are defined.

Classes Defining Points

There are two classes in the java.awt.geom package that define points, Point2D.Float and Point2D.Double. From the class names you can see that these are both inner classes to the class Point2D, which also happens to be an abstract base class for both too. The Point2D.Float class defines a point from a pair of (x,y) coordinates of type float, whereas the Point2D.Double class defines a point as a coordinate pair of type double. The Point class in the java.awt package also defines a point, but in terms of a coordinate pair of type int. This class also has Point2D as a base.

Click To expand

The Point class actually predates the Point2D class, but the class was redefined to make it a subclass of Point2D when Point2D was introduced, hence the somewhat unusual class hierarchy with only two of the subclasses as inner classes. The merit of this arrangement is that all of the subclasses inherit the methods defined in the Point2D class, so operations on each of the three kinds of point are the same.

The three subclasses of Point2D define a default constructor that defines the point 0,0, and a constructor that accept a pair of coordinates of the type appropriate to the class type.

The operations that each of the three concrete point classes inherit are:

  1. Accessing coordinate values:
    The getX() and getY() methods return the x and y coordinates of a point as type double, regardless of how the coordinates are stored. These are abstract methods in the Point2D class so they are defined in each of the subclasses. Although you get coordinates as double values from all three concrete classes via these methods you can always access the coordinates with their original type directly since the coordinates are stored in public fields with the same names, x and y, in each case.

  2. Calculating the distance between two points:
    You have no less that three overloaded versions of the distance() method for calculating the distance between two points, and returning it as type double:

    distance(double x1, double y1, double x2, double y2)

    This is a static version of the method that calculates the distance between the points x1,y1 and x2,y2.

    distance(double xNext, double yNext)

    Calculates the distance from the current point (the object for which the method is called) and the point xNext,yNext.

    distance(Point2D nextPoint)

    Calculates the distance from the current point to the point, nextPoint. The argument can be any of the subclass types, Point, Point2D.Float or Point2D.Double.

    Here's how you might calculate the distance between two points:

    Point2D.Double p1 = new Point2D.Double(2.5, 3.5);
    Point p2 = new Point(20, 30);
    double lineLength = p1.distance(p2);

    You could also have calculated this distance without creating the points by using the static method:

    double lineLength = Point2D.distance(2.5, 3.5, 20, 30);

    Corresponding to each of the three distance() methods there is a convenience method, distanceSq(), with the same parameter list that returns the square of the distance as type double.

  3. Comparing points:
    The equals() method compares the current point with the point object referenced by the argument and returns true if they are equal and false otherwise.

  4. Setting a new location for a point:
    The inherited setLocation()method comes in two versions. One accepts an argument that is a reference of type Point2D, and sets the coordinate values of the current point to those of the point passed as an argument. The other accepts two arguments of type double that are the x and y coordinates of the new location. The Point class also defines a version of setLocation() that accepts two arguments of type int to define the new coordinates.

Lines and Rectangles

The java.awt.geom package contains the following classes for shapes that are straight lines and rectangles:




This is an abstract base class defining a line between two points. There are two concrete subclasses – Line2D.Float and Line2D.Double that define lines in terms of user coordinates of type float and double respectively. You can see from their names that the subclasses are nested classes to the abstract base class, Line2D.


This is the abstract base class for the Rectangle2D.Double and Rectangle2D.Float classes that define rectangles. A rectangle is defined by the coordinates of the position of its top-left corner plus its width and height. The Rectangle2D class is also the abstract base class for the Rectangle class in the java.awt package, which stores the position coordinates and the height and width as values of type int.


This is the abstract base class for RoundRectangle2D.Double and RoundRectangle2D.Float classes that define rectangles with rounded corners. The rounded corners are specified by a width and height.

As with the classes defining points, the Rectangle class that is defined in the java.awt package predates the Rectangle2D class, but the definition of the Rectangle class was changed to make Rectangle2D a base for compatibility reasons. Note that there is no equivalent to the Rectangle class for lines defined by integer coordinates. If you are browsing the documentation, you may notice there is a Line interface, but this is nothing to do with geometry.

Click To expand

You can define a line by supplying two Point2D objects to a constructor, or two pairs of (x, y) coordinates. For example, here's how you define a line by two coordinate pairs:

Line2D.float line = new Line2D.Float(5.0f, 100.0f, 50.0f, 150.0f);

This draws a line from the point (5.0, 100.0) to the point (50.0, 150.0). You could also create the same line using Point2D.Float objects:

Point2D.Float p1 = new Point2D.Float(5.0f, 100.0f);
Point2D.Float p2 = new Point2D.Float(50.0f, 150.0f);
Line2D.float line = new Line2D.Float(p1, p2);

You draw a line using the draw() method for a Graphics2D object, for example:

g2D.draw(line);        // Draw the line

To create a rectangle, you specify the coordinates of its top-left corner, and the width and height:

float width = 120.0f;
float height = 90.0f;
Rectangle2D.Float rectangle = new Rectangle2D.Float(50.0f, 150.0f, width, height);

The default constructor creates a rectangle at the origin with a zero width and height. You can set the position, width, and height of a rectangle by calling its setRect() method. There are three versions of this method. One of them accepts arguments for the coordinates of the top-left corner and the width and height as float values, exactly as in the constructor. Another accepts the same arguments but of type double. The third accepts an argument of type Rectangle2D so you can pass either type of Rectangle2D to it.

A Rectangle2D object has getX() and getY() methods for retrieving the coordinates of the top-left corner, and getWidth() and getHeight() methods that return the width and height.

A round rectangle is a rectangle with rounded corners. The corners are defined by a width and a height and are essentially a quarter segment of an ellipse (we will get to ellipses later). Of course, if the corner width and height are equal then the corner will be a quarter of a circle.

You can define a round rectangle using coordinates of type double with the statements:

Point2D.Double position = new Point2D.Double(10, 10);
double width = 200.0;
double height = 100;
double cornerWidth = 15.0;
double cornerHeight = 10.0;
RoundRectangle2D.Double roundRect = new RoundRectangle2D.Double(
                          position.x, position.y,      // Position of top-left
                          width, height,               // Rectangle width & height
                          cornerWidth, cornerHeight);  // Corner width & height

The only difference between this and defining an ordinary rectangle is the addition of the width and height to be applied for the corner rounding.

Combining Rectangles

You can combine two rectangles to produce a new rectangle that is either the union of the two original rectangles or the intersection. Let's take a couple of specifics to see how this works. We can create two rectangles with the statements:

float width = 120.0f;
float height = 90.0f;
Rectangle2D.Float rect1 = new Rectangle2D.Float(50.0f, 150.0f, width, height);
Rectangle2D.Float rect2 = new Rectangle2D.Float(80.0f, 180.0f, width, height);

We can obtain the intersection of the two rectangles with the statement:

Rectangle2D.Float rect3 = rect1.createIntersection(rect2);

The effect is illustrated in the diagram below by the shaded rectangle. Of course, the result is the same if we call the method for rect2 with rect1 as the argument. If the rectangles don't overlap the rectangle that is returned will be the rectangle from the bottom right of one rectangle to the top right of the other that does not overlap either.

The following statement produces the union of the two rectangles:

Rectangle2D.Float rect3 = rect1.createUnion(rect2);

The result is shown in the diagram by the rectangle with the heavy boundary that encloses the other two.

Click To expand

Testing Rectangles

Perhaps the simplest test you can apply is for an empty rectangle. The isEmpty() method that is implemented in all the rectangle classes returns true if the Rectangle2D object is empty – which is when either the width or the height (or both) are zero.

You can also test whether a point lies inside any type of rectangle object by calling its contains()method. There are contains() methods for all the rectangle classes that accept a Point2D argument or a pair of (x, y) coordinates of a type matching that of the rectangle class: they return true if the point lies within the rectangle. Each shape class defines a getBounds2D() method that returns a Rectangle2D object that encloses the shape.

This method is frequently used in association with the contains() method to test efficiently whether the cursor lies within a particular shape. Testing whether the cursor is within the enclosing rectangle will be a lot faster in general than testing whether it is within the precise boundary of the shape, and is good enough for many purposes – when selecting a particular shape on the screen to manipulate it in some way for instance.

There are also versions of the contains() method to test whether a given rectangle lies within the area occupied by a rectangle object – this obviously enables you to test whether a shape lies within another shape. The given rectangle can be passed to the contains() method as the coordinates of its top-left corner and its height and width as type double, or as a Rectangle2D reference. The method returns true if the rectangle object completely contains the given rectangle.

Let's try drawing a few simple lines and rectangles by inserting some code in the paint() method for the view in Sketcher.

Try It Out – Drawing Lines and Rectangles

Begin by adding an import statement to for the java.awt.geom package:

import java.awt.geom.*;

Now replace the previous code in the paint() method in the SketchView class with the following:

  public void paint(Graphics g) {
    // Temporary code
    Graphics2D g2D = (Graphics2D)g;                // Get a Java 2D device context

    g2D.setPaint(;                       // Draw in red

    // Position width and height of first rectangle
    Point2D.Float p1 = new Point2D.Float(50.0f, 10.0f);
    float width1 = 60;
    float height1 = 80;

    // Create and draw the first rectangle
    Rectangle2D.Float rect = new Rectangle2D.Float(p1.x, p1.y, width1, height1);

    // Position width and height of second rectangle
    Point2D.Float p2 = new Point2D.Float(150.0f, 100.0f);
    float width2 = width1 + 30;
    float height2 = height1 + 40;

    // Create and draw the second rectangle
    g2D.draw(new Rectangle2D.Float(
                       (float)(p2.getX()), (float)(p2.getY()), width2, height2));
    g2D.setPaint(;                        // Draw in blue

    // Draw lines to join corresponding corners of the rectangles
    Line2D.Float line = new Line2D.Float(p1,p2);

    p1.setLocation(p1.x + width1, p1.y);
    p2.setLocation(p2.x + width2, p2.y);
    g2D.draw(new Line2D.Float(p1,p2));

    p1.setLocation(p1.x, p1.y + height1);
    p2.setLocation(p2.x, p2.y + height2);
    g2D.draw(new Line2D.Float(p1,p2));

    p1.setLocation(p1.x – width1, p1.y);
    p2.setLocation(p2.x – width2, p2.y);
    g2D.draw(new Line2D.Float(p1, p2));

    p1.setLocation(p1.x, p1.y – height1);
    p2.setLocation(p2.x, p2.y – height2);
    g2D.draw(new Line2D.Float(p1, p2));
    g2D.drawString("Lines and rectangles", 60, 250); // Draw some text

If you type this in correctly and recompile SketchView class, the Sketcher window will look like:

How It Works

After casting the graphics context object that is passed to the paint() method to type Graphics2D we set the drawing color to red. All subsequent drawing that we do will be in red until we change the color with another call to setPaint(). We define a Point2D.Float object to represent the position of the first rectangle, and we define variables to hold the width and height of the rectangle. We use these to create the rectangle by passing them as arguments to the constructor that we have seen before, and display the rectangle by passing the rect object to the draw()method for the graphics context, g2D. The second rectangle is defined by essentially the same process, except that this time we create the Rectangle2D.Float object in the argument expression for the draw() method.

Note that we have to cast the values returned by the getX() and getY() members of the Point2D object as they are returned as type double. It is generally more convenient to reference the x and y fields directly as we do in the rest of the code.

We change the drawing color to blue so that you can see quite clearly the lines we are drawing. We use the setLocation() method for the point objects to move the point on each rectangle to successive corners, and draw a line at each position. The caption also appears in blue since that is the color in effect when we call the drawString() method to output the text string.

Arcs and Ellipses

There are shape classes defining both arcs and ellipses. The abstract class representing a generic ellipse is:




This is the abstract base class for the Ellipse2D.Double and Ellipse2D.Float classes that define ellipses. An ellipse is defined by the top-left corner, width and height of the rectangle that encloses it.

Click To expand

The class representing an elliptic arc is:




This is the abstract base class for the Arc2D.Double and Arc2D.Float classes that define arcs as a portion of an ellipse. The full ellipse is defined by the position of the top-left corner and the width and height of the rectangle that encloses it. The arc length is defined by a start angle measured in degrees anti-clockwise relative to the horizontal axis of the full ellipse, plus an angular extent measured anti-clockwise from the start angle in degrees. An arc can be OPEN, which means the ends are not connected; CHORD, which means the ends are connected by a straight line, or PIE which means the ends are connected by straight lines to the center of the whole ellipse. These constants are defined in Arc2D.

Arcs and ellipses are closely related since an arc is just a segment of an ellipse. To define an ellipse you supply the data necessary to define the enclosing rectangle – the coordinates of the top-left corner, the width, and the height. To define an arc you supply the data to define the ellipse, plus additional data that defines the segment that you want. The seventh argument to the arc constructor determines the type, whether OPEN, CHORD, or PIE.

You could define an ellipse with the statements:

Point2D.Double position = new Point2D.Double(10,10);
double width = 200.0;
double height = 100;
Ellipse2D.Double ellipse = new Ellipse2D.Double(
                            position.x, position.y, // Top-left corner
                            width, height);         // width & height of rectangle

You could define an arc that is a segment of the previous ellipse with the statement:

Arc2D.Double arc = new Arc2D.Double(
                            position.x, position.y, // Top-left corner
                            width, height,          // width & height of rectangle
                            0.0, 90.0,              // Start and extent angles
                            Arc2D.OPEN);            // Arc is open

This defines the upper-right quarter segment of the whole ellipse as an open arc. The angles are measured anticlockwise from the horizontal in degrees. As we saw earlier the first angular argument is where the arc starts, and the second is the angular extent of the arc.

Of course, a circle is just an ellipse where the width and height are the same, so the following statement defines a circle with a diameter of 150:

double diameter = 150.0;
Ellipse2D.Double circle = new Ellipse2D.Double(
                          position.x, position.y,   // Top-left corner
                          diameter, diameter);      // width & height of rectangle

This presumes the point position is defined somewhere. You will often want to define a circle by its center and radius – adjusting the arguments to the constructor a little does this easily:

Point2D.Double center = new Point2D.Double(200, 200);
double radius = 150;
Ellipse2D.Double newCircle = new Ellipse2D.Double(
              center.x-radius, center.y-radius,     // Top-left corner
              2*radius, 2*radius);                  // width & height of rectangle

The fields storing the coordinates of the top-left corner of the enclosing rectangle and the width and height are public members of Ellipse2D and Arc2D objects. They are x, y, width and height respectively. An Arc2D object also has public members, start and extent, that store the angles.

Try It Out – Drawing Arcs and Ellipses

Let's modify the paint()method in once again to draw some arcs and ellipses.

  public void paint(Graphics g) {
    // Temporary code
    Graphics2D g2D = (Graphics2D)g;                // Get a Java 2D device context
    Point2D.Double position = new Point2D.Double(50,10);  // Initial position
    double width = 150;                                   // Width of ellipse
    double height = 100;                                  // Height of ellipse
    double start = 30;                                    // Start angle for arc
    double extent = 120;                                  // Extent of arc
    double diameter = 40;                                 // Diameter of circle

    // Define open arc as an upper segment of an ellipse
    Arc2D.Double top = new Arc2D.Double(position.x, position.y,
                                     width, height,
                                     start, extent,

    // Define open arc as lower segment of ellipse shifted up relative to 1st
    Arc2D.Double bottom = new Arc2D.Double(
                                     position.x, position.y – height + diameter,
                                     width, height,
                                     start + 180, extent,
    // Create a circle centered between the two arcs
    Ellipse2D.Double circle1 = new Ellipse2D.Double(
                                     position.x + width/2 – diameter/2,position.y,
                                     diameter, diameter);

    // Create a second circle concentric with the first and half the diameter
    Ellipse2D.Double circle2 = new Ellipse2D.Double(
                   position.x + width/2 – diameter/4, position.y + diameter/4,
                   diameter/2, diameter/2);

    // Draw all the shapes
    g2D.setPaint(;                       // Draw in black

    g2D.setPaint(;                        // Draw in blue
    g2D.drawString("Arcs and ellipses", 80, 100);    // Draw some text

Running Sketcher with this version of the paint() method in SketchView will produce the window shown here.

How It Works

This time we create all the shapes first and then draw them. The two arcs are segments of ellipses of the same height and width. The lower segment is shifted up with respect to the first so that they intersect, and the distance between the top of the rectangle for the first and the bottom of the rectangle for the second is diameter, which is the diameter of the first circle we create.

Both circles are created centered between the two arcs and are concentric. Finally we draw all the shapes – the arcs in black and the circles in blue.

Next time we change the code in Sketcher, we will be building the application as it should be, so remove the temporary code from the paint() method and the code that sets the background color in the ColorAction inner class to the SketchFrame class.


There are two classes that define arbitrary curves, one defining a quadratic or second order curve and the other defining a cubic curve. The cubic curve just happens to be a Bézier curve (so called because it was developed by a Frenchman, Monsieur P. Bézier, and first applied in the context of defining contours for programming numerically-controlled machine tools). The classes defining these curves are:




This is the abstract base class for the QuadCurve2D.Double and QuadCurve2D.Float classes that define a quadratic curve segment. The curve is defined by its end points plus a control point that defines the tangent at each end. The tangents are the lines from the end points to the control point.


This is the abstract base class for the CubicCurve2D.Double and CubicCurve2D.Float classes that define a cubic curve segment. The curve is defined by its end points plus two control points that define the tangent at each end. The tangents are the lines from the end points to the corresponding control point.

Click To expand

In general, there are many other methods for modeling arbitrary curves, but the two defined in Java have the merit that they are both easy to understand, and the effect on the curve segment when the control point is moved is quite intuitive.

An object of each curve type defines a curve segment between two points. The control points – one for a QuadCurve2D curve and two for a CubicCurve2D curve – control the direction and magnitude of the tangents at the end points. A QuadCurve2D curve constructor has six parameters corresponding to the coordinates of the starting point for the segment, the coordinates of the control point and the coordinates of the end point. We can define a QuadCurve2D curve from a point start to a point end, plus a control point, control, with the statements:

Point2D.Double startQ = new Point2D.Double(50, 150);
Point2D.Double endQ = new Point2D.Double(150, 150);
Point2D.Double control = new Point2D.Double(80,100);

QuadCurve2D.Double quadCurve 
        = new QuadCurve2D.Double(startQ.x, startQ.y,      // Segment start point
                                 control.x, control.y,    // Control point
                                 endQ.x, endQ.y);         // Segment end point

The QuadCurve2D subclasses have public members storing the end points and the control point so you can access them directly. The coordinates of the start and end points are stored in the fields, x1, y1, x2, and y2. The coordinates of the control point are stored in ctrlx and ctrly.

Defining a cubic curve segment is very similar – you just have two control points, one for each end of the segment. The arguments are the (x, y) coordinates of the start point, the control point for the start of the segment, the control point for the end of the segment and finally the end point. We could define a cubic curve with the statements:

Point2D.Double startC = new Point2D.Double(50, 300);
Point2D.Double endC = new Point2D.Double(150, 300);
Point2D.Double controlStart = new Point2D.Double(80, 250);
Point2D.Double controlEnd = new Point2D.Double(160, 250);

CubicCurve2D.Double cubicCurve = new CubicCurve2D.Double(
                       startC.x, startC.y,              // Segment start point
                       controlStart.x, controlStart.y,  // Control point for start
                       controlEnd.x, controlEnd.y,      // Control point for end
                       endC.x, endC.y);                 // Segment end point

The cubic curve classes also have public members for all the points: x1, y1, x2 and y2 for the end points, and ctrlx1, ctrly1, ctrlx2 and ctrly2 for the corresponding control points.

We can understand these better if we try them out. This time let's do it with an applet.

Try It Out – Drawing Curves

We can define an applet to display the curves we used as examples above:

import javax.swing.JApplet;
import javax.swing.JComponent;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Container;
import java.awt.Graphics;

import java.awt.geom.Point2D;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.QuadCurve2D;

public class CurveApplet extends JApplet {
  // Initialize the applet
  public void init() {
    pane = new CurvePane();                       // Create pane containing curves
    Container content = getContentPane();         // Get the content pane

    // Add the pane displaying the curves to the content pane for the applet
    content.add(pane);                  // BorderLayout.CENTER is default position

  // Class defining a pane on which to draw
  class CurvePane extends JComponent {
    // Constructor
    public CurvePane() {
      quadCurve = new QuadCurve2D.Double(               // Create quadratic curve
                       startQ.x, startQ.y,              // Segment start point
                       control.x, control.y,            // Control point
                       endQ.x, endQ.y);                 // Segment end point

      cubicCurve = new CubicCurve2D.Double(             // Create cubic curve
                       startC.x, startC.y,              // Segment start point
                       controlStart.x, controlStart.y,  // Control point for start
                       controlEnd.x, controlEnd.y,      // Control point for end
                           endC.x, endC.y);             // Segment end point

    public void paint(Graphics g) {
      Graphics2D g2D = (Graphics2D)g;                   // Get a 2D device context

      // Draw the curves
  // Points for quadratic curve
  Point2D.Double startQ = new Point2D.Double(50, 75);         // Start point
  Point2D.Double endQ = new Point2D.Double(150, 75);          // End point
  Point2D.Double control = new Point2D.Double(80, 25);        // Control point

  // Points for cubic curve
  Point2D.Double startC = new Point2D.Double(50, 150);         // Start point
  Point2D.Double endC = new Point2D.Double(150, 150);          // End point
  Point2D.Double controlStart = new Point2D.Double(80, 100);  // 1st control point
  Point2D.Double controlEnd = new Point2D.Double(160, 100);   // 2nd control point
  QuadCurve2D.Double quadCurve;                          // Quadratic curve
  CubicCurve2D.Double cubicCurve;                        // Cubic curve
  CurvePane pane = new CurvePane();                      // Pane to contain curves

You will need an HTML file to run the applet. The contents can be something like:

<applet code="CurveApplet.class" width=300 height=300></applet>

This will display the applet in appletviewer. If you want to display it in your browser, you need to convert the HTML using the HTMLConverter program. If you don't already have it you can download it from the web site.

If you run the applet using appletviewer, you will get a window looking like that here.

How It Works

We need an object of our own class type so that we can implement the paint() method for it. We define the inner class CurvePane for this purpose with JComponent as the base class so it is a Swing component. We create an object of this class (which is a member of the CurveApplet class) and add it to the content pane for the applet using its inherited add() method. The layout manager for the content pane is BorderLayout, and the default positioning is BorderLayout.CENTER so the CurvePane object fills the content pane.

The points defining the quadratic and cubic curves are defined as fields in the CurveApplet class and these are referenced in the paint() method for the CurvePane class to create the objects representing curves. These points are used in the CurvePane class constructor to create the curves. We draw the curves by calling the draw() method for the Graphics2D object and passing a reference to a curve object as the argument.

It's hard to see how the control points affect the shape of the curve, so let's add some code to draw the control points.

Try It Out – Displaying the Control Points

We will mark the position of each control point by drawing a small circle around it. We can define a marker using an inner class of CurveApplet that we can define as:

  // Inner class defining a control point marker
  class Marker {
    public Marker(Point2D.Double control)  {
      center = control;                   // Save control point as circle center

      // Create circle around control point
      circle = new Ellipse2D.Double(control.x-radius, control.y-radius,
                                    2.0*radius, 2.0*radius); 

      // Draw the marker
      public void draw(Graphics2D g2D) {

     // Get center of marker – the control point position
      Point2D.Double getCenter() {
        return center;

    Ellipse2D.Double circle;               // Circle around control point
    Point2D.Double center;                 // Circle center – the control point
    static final double radius = 3;        // Radius of circle

The argument to the constructor is the control point that is to be marked. The constructor stores this control point in the member, center, and creates an Ellipse2D.Double object that is the circle to mark the control point. The class also has a method, draw(), to draw the marker using the Graphics2D object reference that is passed to it. The getCenter() method returns the center of the marker as a Point2D.Double reference. We will use this method when we draw tangent lines from the end points of a curve to the corresponding control points.

We will add fields to the CurveApplet class to define the markers for the control points. These definitions should follow the members that defines the points:

   // Markers for control points
   Marker ctrlQuad = new Marker(control);
   Marker ctrlCubic1 = new Marker(controlStart);
   Marker ctrlCubic2 = new Marker(controlEnd);

We can now add code to the paint()method for the CurvePane class to draw the markers and the tangents from the endpoints of the curve segments:

     public void paint(Graphics g) {
      // Code to draw curves as before...

      // Create and draw the markers showing the control points
      g2D.setPaint(;                   // Set the color
      // Draw tangents from the curve end points to the control marker centers
      Line2D.Double tangent = new Line2D.Double(startQ, ctrlQuad.getCenter());
      tangent = new Line2D.Double(endQ, ctrlQuad.getCenter());

      tangent = new Line2D.Double(startC, ctrlCubic1.getCenter());
      tangent = new Line2D.Double(endC, ctrlCubic2.getCenter());

If you recompile the applet with these changes, when you execute it again you should see the window shown here.

How It Works

In the Marker class constructor, the top-left corner of the rectangle enclosing the circle for a control point is obtained by subtracting the radius from the x and y coordinates of the control point. We then create an Ellipse2D.Double object with the width and height as twice the value of radius – which is the diameter of the circle.

In the paint()method we call the draw() method for each of the Marker objects to draw a red circle around each control point. The tangents are just lines from the endpoints of each curve segment to the centers of the corresponding Marker objects.

It would be good to see what happens to a curve segment when you move the control points around. Then we could really see how the control points affect the shape of the curve. That's not as difficult to implement as it might sound, so let's give it a try.

Try It Out – Moving the Control Points

We will arrange to allow a control point to be moved by positioning the cursor on it, pressing a mouse button and dragging it around. Releasing the mouse button will stop the process for that control point, so you will then be free to manipulate another one. To do this we will add another inner class to CurveApplet that will handle mouse events:

class MouseHandler extends MouseInputAdapter {
  public void mousePressed(MouseEvent e) {
    // Check if the cursor is inside any marker
    if(ctrlQuad.contains(e.getX(), e.getY()))
      selected = ctrlQuad;
    else if(ctrlCubic1.contains(e.getX(), e.getY()))
      selected = ctrlCubic1;
    else if(ctrlCubic2.contains(e.getX(), e.getY()))
      selected = ctrlCubic2;

  public void mouseReleased(MouseEvent e) {
    selected = null;                             // Deselect any selected marker
  public void mouseDragged(MouseEvent e) {
    if(selected != null) {                       // If a marker is selected
      // Set the marker to current cursor position
      selected.setLocation(e.getX(), e.getY());
      pane.repaint();                             // Redraw pane contents

  Marker selected = null;                 // Stores reference to selected marker

We need to add two import statements to the beginning of the source file, one because we reference the MouseInputAdapter class, and the other because we refer to the MouseEvent class:

import javax.swing.event.MouseInputAdapter;
import java.awt.event.MouseEvent;

The mousePressed()method calls a method contains() that should test whether the point defined by the arguments is inside the marker. We can implement this in the Marker class like this:

    // Test if a point x,y is inside the marker
    public boolean contains(double x, double y) {
      return circle.contains(x,y); 

This just calls the contains()method for the circle object that is the marker. This will return true if the point (x, y) is inside.

The mouseDragged()method calls a method setLocation() for the selected Marker object, so we need to implement this in the Marker class, too:

    // Sets a new control point location
    public void setLocation(double x, double y) {
      center.x = x;                      // Update control point
      center.y = y;                      // coordinates
      circle.x = x-radius;               // Change circle position
      circle.y = y-radius;               // correspondingly

After updating the coordinates of the point, center, we also update the position of circle by setting its data member directly. We can do this because x and y are public members of the Ellipse2D.Double class.

We can create a MouseHandler object in the init() method for the applet and set it as the listener for mouse events for the pane object:

public void init() {
  pane = new CurvePane();                   // Create pane containing curves
  Container content = getContentPane();     // Get the content pane

  // Add the pane displaying the curves to the content pane for the applet
  content.add(pane);                 // BorderLayout.CENTER is default position

  MouseHandler handler = new MouseHandler();    // Create the listener
  pane.addMouseListener(handler);               // Monitor mouse button presses
  pane.addMouseMotionListener(handler);         // as well as movement

Of course, to make the effect of moving the control points apparent, we must update the curve objects before we draw them. We can add the following code to the paint() method to do this:

  public void paint(Graphics g) {
    Graphics2D g2D = (Graphics2D)g;                   // Get a 2D device context

    // Update the curves with the current control point positions
    quadCurve.ctrlx = ctrlQuad.getCenter().x;
    quadCurve.ctrly = ctrlQuad.getCenter().y;
    cubicCurve.ctrlx1 = ctrlCubic1.getCenter().x;
    cubicCurve.ctrly1 = ctrlCubic1.getCenter().y;
    cubicCurve.ctrlx2 = ctrlCubic2.getCenter().x;
    cubicCurve.ctrly2 = ctrlCubic2.getCenter().y;

    // Rest of the method as before...

We can update the data members that store the control point coordinates for the curves directly because they are public members of each curve class. We get the coordinates of the new positions for the control points from their markers by calling the getCenter() method for each, and then accessing the appropriate data member of the Point2D.Double object that is returned.

If you recompile the applet with these changes and run it again you should get something like the window here.

You should be able to drag the control points around with the mouse. If it is a bit difficult to select the control points, just make the value of radius a bit larger. Note how the angle of the tangent as well as its length affects the shape of the curve.

How It Works

In the MouseHandler class, the mousePressed() method will be called when you press a mouse button. In this method we check whether the current cursor position is within any of the markers enclosing the control points. We do this by calling the contains() method for each marker object and passing the coordinates of the cursor position to it. The getX() and getY() methods for the MouseEvent object supply the coordinates of the current cursor position. If one of the markers does enclose the cursor, we store a reference to the Marker object in the selected member of the MouseHandler class for use by the mouseDragged() method.

In the mouseDragged()method, we set the location for the Marker object referenced by selected to the current cursor position and call repaint() for the pane object. The repaint()method causes the paint()method to be called for the component, so everything will be redrawn, taking account of the modified control point position.

Releasing the mouse button will cause the mouseReleased() method to be called. In here we just set the selected field back to null so no Marker object is selected. Remarkably easy, wasn't it?

Complex Paths

You can define a more complex shape as an object of type GeneralPath. A GeneralPath object can be a composite of lines, Quad2D curves, and Cubic2D curves, or even other GeneralPath objects.

The process for determining whether a point is inside or outside a GeneralPath object is specified by the winding rule for the object. There are two winding rules that you can specify by constants defined in the class:

Winding Rule



In this case, a point is interior to a GeneralPath object if the boundary is crossed an odd number of times by a line from a point exterior to the GeneralPath to the point in question. You can use this winding rule for any kind of shape, including shapes with holes.


In this case, whether a point is inside or outside a path is determined by considering how the path crosses a line drawn from the point in question to infinity taking account of the direction in which the path is drawn. Looking along the line from the point, the point is interior to the GeneralPath object if the difference between the number of times the line is crossed by a boundary from left to right, and the number of times from right to left is non-zero. You can use this rule for general paths without holes – in other words, shapes that are bounded by a single contiguous path. This rule does not work for shapes bounded by more than one contiguous path – with holes in other words – since the result will vary depending on the direction in which each path is drawn. However, it does work for a closed re-entrant path – a path that intersects itself.

These winding rules are illustrated below:

Click To expand

The safe option is WIND_EVEN_ODD.

There are four constructors for GeneralPath objects:




Defines a general path with a default winding rule of WIND_NON_ZERO.

GeneralPath(int rule)

Creates an object with the winding rule specified by the argument. This can be WIND_NON_ZERO or WIND_EVEN_ODD.

GeneralPath(int rule, int capacity)

Creates an object with the winding rule specified by the first argument and the number of path segments specified by the second argument. In any event, the capacity is increased when necessary.

GeneralPath(Shape shape)

Creates an object from the object passed as an argument.

We can create a GeneralPath object with the statement:

GeneralPath p = new GeneralPath(GeneralPath.WIND_EVEN_ODD);

A GeneralPath object embodies the notion of a current point of type Point2D from which the next path segment will be drawn. You set the initial current point by passing a pair of (x, y) coordinates as values of type float to the moveTo()method for the object. For example:

p.moveTo(10.0f,10.0f);          // Set the current point to 10,10

A segment is added to the general path, starting at the current point, and the end of each segment that you add becomes the new current point that is used to start the next segment. Of course, if you want disconnected segments in a path, you can call moveTo() to move the current point to wherever you want before you add a new segment. If you need to get the current position at any time, you can call the getCurrentPoint() method for a GeneralPath object and get the current point as type Point2D.

You can use the following methods to add segments to a GeneralPath object:

Methods to Add Segments


lineTo(float x, float y)

Draws a line from the current point to the point (x,y).

quadTo(float ctrlx, float ctrly, float x2, float y2)

Draws a quadratic curve segment from the current point to the point (x2,y2) with (ctrlx,ctrly) as the control point.

curveTo(float ctrlx1, float ctrly1, float ctrlx2, float ctrly2, float x2, float y2)

Draws a Bezier curve segment from the current point with control point (ctrlx1,ctrly1) to (x2,y2) with (ctrlx2,ctrly2) as the control point.

Each of these methods updates the current point to be the end of the segment that is added. A path can consist of several subpaths since a new subpath is started by a moveTo() call. The closePath() method closes the current subpath by connecting the current point after the last segment to the point defined by the previous moveTo()call.

Let's illustrate how this works with a simple example. We could create a triangle with the following statements:

GeneralPath p = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
p.moveTo(50.0f, 50.0f);                          // Start point for path
p.lineTo(150.0f, 50.0f);                         // Line from 50,50 to 150,50
p.lineTo(150.0f, 250.0f);                        // Line from 150,50 to 150,250
p.closePath();                                   // Line from 150,250 back to start

The first line segment starts at the current position set by the moveTo() call. Each subsequent segment begins at the endpoint of the previous segment. The closePath() call joins the latest endpoint to the point set by the previous moveTo() – which in this case is the beginning of the path. The process is much the same using quadTo() or curveTo() calls and of course you can intermix them in any sequence you like.

Once you have created a path for a GeneralPath object by calling its methods to add segments to the path, you can remove them all by calling its reset() method. This empties the path.

The GeneralPath class implements the Shape interface, so a Graphics2D object knows how to draw a path. You just pass a reference to the draw() method for the graphics context. To draw the path, p, that we defined above in the graphics context g2D, you would write:

g2D.draw(p);   // Draw path p

Let's try an example.

Try It Out – Reaching for the Stars

You won't usually want to construct a GeneralPath object as we did above. You will probably want to create a particular shape, a triangle or a star say, and then draw it at various points on a component. You might think you can do this by subclassing GeneralPath, but unfortunately GeneralPath is declared as final so subclassing is not allowed. However, you can always add a GeneralPath object as a member of your class. Let's draw some stars using our own Star class. We will use a GeneralPath object to create the star shown in the diagram.

Here's the code for a class defining the star:

import java.awt.geom.Point2D;
import java.awt.geom.GeneralPath;
import java.awt.Shape;

class Star {
  public Star(float x, float y) {
    start = new Point2D.Float(x, y);                      // store start point

  // Create the path from start
  void createStar() {
    Point2D.Float point = start;
    p = new GeneralPath(GeneralPath.WIND_NON_ZERO);
    p.moveTo(point.x, point.y);
    p.lineTo(point.x + 20.0f, point.y – 5.0f);            // Line from start to A
    point = (Point2D.Float)p.getCurrentPoint();
    p.lineTo(point.x + 5.0f, point.y – 20.0f);            // Line from A to B
    point = (Point2D.Float)p.getCurrentPoint();
    p.lineTo(point.x + 5.0f, point.y + 20.0f);            // Line from B to C
    point = (Point2D.Float)p.getCurrentPoint();
    p.lineTo(point.x + 20.0f, point.y + 5.0f);            // Line from C to D
    point = (Point2D.Float)p.getCurrentPoint();
    p.lineTo(point.x – 20.0f, point.y + 5.0f);            // Line from D to E
    point = (Point2D.Float)p.getCurrentPoint();
    p.lineTo(point.x – 5.0f, point.y + 20.0f);            // Line from E to F
    point = (Point2D.Float)p.getCurrentPoint();
    p.lineTo(point.x – 5.0f, point.y – 20.0f);            // Line from F to g
    p.closePath();                                        // Line from G to start

  Shape atLocation(float x, float y) {
    start.setLocation(x, y);                              // Store new start
    p.reset();                                            // Erase current path
    createStar();                                         // create new path
    return p;                                             // Return the path

  // Make the path available
  Shape getShape() { 
    return p; 

  private Point2D.Float start;                           // Start point for star
  private GeneralPath p;                                 // Star path

We can draw stars on an applet:

import javax.swing.JApplet;
import javax.swing.JComponent;
import java.awt.Graphics;
import java.awt.Graphics2D;

public class StarApplet extends JApplet {
  // Initialize the applet
  public void init() {
    getContentPane().add(pane);     // BorderLayout.CENTER is default position

  // Class defining a pane on which to draw
  class StarPane extends JComponent {
    public void paint(Graphics g) {
      Graphics2D g2D = (Graphics2D)g;
      Star star = new Star(0,0);                 // Create a star
      float delta = 60f;                         // Increment between stars
      float starty = 0f;                         // Starting y position

      // Draw 3 rows of 4 stars
      for(int yCount = 0; yCount < 3; yCount++) {
        starty += delta;                        // Increment row position
        float startx = 0f;                      // Start x position in a row

        // Draw a row of 4 stars
        for(int xCount = 0; xCount<4; xCount++) {
          g2D.draw(star.atLocation(startx += delta, starty));

  StarPane pane = new StarPane();              // Pane containing stars

The HTML file for this applet could contain:

<applet code="StarApplet.class" width=360 height=240></applet>

This is large enough to accommodate our stars. If you compile and run the applet, you should see the AppletViewer window shown here.

Click To expand

How It Works

The Star class has a GeneralPath object, p, as a member. The constructor sets the coordinates of the start point from the arguments, and calls the createStar() method that creates the path for the star. The first line is drawn relative to the start point that is set by the call to moveTo() for p. For each subsequent line, we retrieve the current position by calling getCurrentPoint() for p and drawing the line relative to that. The last line to complete the star is drawn by calling closePath().

We always need a Shape reference to draw a Star object, so we have included a getShape() method in the class that simply returns a reference to the current GeneralPath object as type Shape. The atLocation() method recreates the path at the new position specified by the arguments and returns a reference to it.

The StarApplet class draws stars on a component defined by the inner class StarPane. We draw the stars using the paint() method for the StarPane object, which is a member of the StarApplet class. Each star is drawn in the nested loop with the position specified by (x, y). The y coordinate defines the vertical position of a row, so this is incremented by delta on each iteration of the outer loop. The coordinate x is the position of a star within a row so this is incremented by delta on each iteration of the inner loop.

Previous Next
JavaScript Editor Java Tutorials Free JavaScript Editor