How-To's > How do I work with shapes?


Shapes and the elements related to them are part of the javafx.scene.shape.Shape class, which provides variables to create the stroke and fill of the shape, among other variables. All shapes inherit from the Node class.

See the following topics for more information about working with shapes.

Related How-To Topics and Tutorials

API Documentation

How do I create a line?


Lines are a type of shape in the JavaFX Script programming language, and, as such, you can use several different shape classes to create them, depending on the type of line you want.

Single Straight Line

The Line class can be used for straight line segments in (x,y) coordinate space. You define the starting coordinates and ending coordinates of the line, as in the following example:

Straight line

import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

Stage {
    title: "line"
    style: StageStyle.UNDECORATED
    scene: Scene {
    fill:Color.GRAY
        width: 100
        height: 100
        content: [
            Line {
                startX: 0 startY: 0
                endX: 100 endY: 100
                stroke:Color.ORANGE
                strokeWidth:3
            }
        ]
    }
 }
    

Note: The default stage style is DECORATED. Decorated windows are always subject to minimum sizes, as determined by the window system. In the previous example, the window size is wider at runtime than the width set for the scene, and the line will not end in the bottom right corner, as you might expect. Minimum decorated window sizes apply to desktop and Web Start applications, not to browser applets.

Series of Connected Straight Lines

The Polyline class constructs a series of x, y coordinates to mark the horizontal and vertical position of each point. Lines are connected automatically between each consecutive set of points. Unlike polygons, the last line is not connected with the first line.

Here is an example of a polyline arrow.

Polyline arrow

Polyline {
    points: [
        10.0, 45.0,
        55.0, 45.0,
        50.0, 20.0,
        92.0, 50.0,
        50.0, 80.0,
        55.0, 55.0,
        10.0, 55.0,
    ]
    stroke:Color.DARKGREEN;
    strokeWidth:6;
    fill:Color.FORESTGREEN;
}
  

Curved Line

You can create curved lines in several ways:

  • Use the CubicCurve and QuadCurve classes to create cubic and quadratic Bezier curves respectively. See the API documentation for code examples.
  • Use the Path class to create connected curved lines. See How do I create a nonstandard shape?
  • The ShapeSubtract or ShapeIntersect classes can be used to create shapes by subtraction or intersection. ou can use these classes to create a curve from part of a circle or ellipse. See the API documentation for code examples.

Tips

  • For rendering wide lines, use the Rectangle class instead of the Line class, or use the Line class with the strokeWidth variable. The Rectangle class is the better option for performance reasons.
  • If you want a line to have different colors of fill and stroke, use the Rectangle class, because the Line class does not have an interior.
  • Lines inherit variables that control animation and effects from javafx.scene.Node. See the topics about animation and effects for more information.

Examples

API Documentation

How do I create a rectangle or square?


Create a rectangle or square by using the Rectangle class.

Use the following variables to determine the size, shape, and position of the rectangle or square:

  • width – The width of the rectangle
  • height – The height of the rectangle
  • x – The horizontal position in the scene graph of the upper left corner of the rectangle, where the value 0 is the leftmost pixel
  • y – The vertical position in the scene graph of the upper left corner of the rectangle, where the value 0 is the topmost pixel

Example Code

Rectangle

Rectangle {
    x: 10
    y: 30
    height: 40
    width:40
    fill: Color.web("#1F6592")
}
  

Tips

  • To create a square, make the values for height and width the same.
  • To create a rectangle with rounded corners, use the arcHeight and arcWidth variables, which control the degree of rounding. The default is 0.0 for no rounding. If the value for arcHeight and arcWidth is the same as or greater than a square's height and width respectively, the shape appears as a circle.
  • If you add a stroke to a rectangle, using the stroke and strokeWidth variables inherited from the Shape class, the stroke is added to the outside of the boundaries of the rectangle set by height and width.
  • Because rectangles are nodes, you can apply animations and effects to them, including drop shadows.

Examples

API Documentation

How do I create a diamond?


The easiest way to create a diamond is to create a square and use the rotate variable, inherited from the Node class, to rotate it 45 degree.

Example Code

The screen capture on the left shows the code without the rotate variable. The screen capture on the right shows the same code with the rotate variable set to 45 degrees. Note that the rotation point is the center of the object.

Diamond using the rotate variable of the Node class

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.shape.Rectangle;
import javafx.scene.paint.Color;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Line;

Stage {
    title: "Diamond"
    width: 300
    height: 200
    scene: Scene {
        content: [
            Rectangle {
                translateX:50
                translateY:50
                width: 50
                height: 50
                fill:
                    RadialGradient {
                        stops: [
                            Stop {
                                color : Color.web("#80CAFA")
                                offset: 0.0
                             }
                             Stop {
                                 color : Color.web("#1F6592")
                                 offset: 1.0
                              }
                        ]
                    }
                rotate:45

            }
            Line {
                startX: 50
                startY:20
                endX:50
                endY:140
            }
            Line {
                startX: 20
                startY: 50
                endX: 140
                endY: 50
            }
        ]
    }
}

Tips

  • The rotation factor in Node.rotate is not included in layoutBounds, which makes the rotate variable ideal for rotating the entire node after all effects and transforms have been applied.
  • You can specify a pivot point by using the rotation transformation instead of Node.rotate. For more information about the rotation transformation, see the topic How do I rotate objects?

API Documentation

How do I create a circle?


Create a circle by using the Circle class.

Use the following variables to determine the size and position of the circle:

  • radius – The radius of the circle (from the center of the circle to the edge)
  • centerX – The horizontal position in the scene graph of the center of the circle, where the value 0 is the leftmost pixel
  • centerY – The vertical position in the scene graph of the center of the circle, where the value 0 is the topmost pixel

To create stroke and fill for the circle, see the topic How do I work with fills and strokes?

Example Code

Circle

Circle {
    centerX: 192 centerY: 174
    radius: 30
    fill:Color.DARKORANGE
    stroke:Color.ORANGERED
    strokeWidth:1
}
    

Tips

  • Because circles inherit variables from the Shape class and Node class, you can apply animations and effects to them, including drop shadows.

Examples

API Documentation

How do I create an ellipse?


Create an ellipse by using the Ellipse class.

Use the following variables to determine the size, shape, and position of the ellipse:

  • radiusX – The width of the ellipse from its center to its edge, in pixels
  • radiusY – The height of the ellipse from its center to its edge, in pixels
  • centerX – The horizontal position in the scene graph of the center of the ellipse, where the value 0 is the leftmost pixel
  • centerY – The vertical position in the scene graph of the center of the ellipse, where the value 0 is the topmost pixel

Example Code

Ellipse

Ellipse {
    centerX: 100
    centerY:  50
    radiusX: 80
    radiusY: 30
    fill: Color.web("#1F6592")
}
  

Tips

  • Because ellipses inherit variables from the Shape class and Node class, you can apply animations and effects to them, including drop shadows.
  • You can also create an ellipse by using the Rectangle class and setting the arcHeight and arcWidth variables to the same value as the height and width respectively. This strategy would be useful if you wanted to dynamically transform an ellipse into a rectangle or the reverse.

Examples

API Documentation

How do I create a polygon?


Create a polygon by using the Polygon class. A polygon has as many straight sides as you want to create.

Polygons are constructed with a series of x, y coordinates to mark the horizontal and vertical position of each point. Lines are connected automatically between each consecutive set of points, and the last point in the list is connected to the first point automatically.

Example Code

The following code produces a polygon that has its lines crossing, with the following result. The red lines show the lines that are drawn between each two points.

Polygon

Polygon {
    points: [
        10.0,  10.0, //the first coordinate is x, the second is y
        40.0, 40.0,
        10.0, 100.0,
        40.0, 120.0
    ]
}
  

To create stroke and fill for the polygon, see the topic How do I work with fills and strokes?

Tips

  • Because the Polygon class inherits variables from the Shape class and Node class, you can apply animations and effects to them, including drop shadows.
  • If you don't want to connect the last point with the first point, use the Polyline class.

Examples

API Documentation

How do I create a nonstandard shape?


You can create nonstandard shapes in several ways:

  • Use the Path class to create a shape of as many lines and curves as you like. You can even create gaps in a line.
  • Subtract one shape from another or use the intersections of two shapes, using the ShapeSubtract or ShapeIntersect class respectively.
  • Use the SVGPath class to create a simple shape that is constructed by parsing SVG path data from a string. With this class, you can draw a path with a drawing tool that produces SVG-format files, then copy and paste the SVG path to reproduce the shape in JavaFX Script.

The remainder of this topic will focus on the Path class.

The Path class uses a positions variable to construct the coordinates and the components of the shape.

You can use any of the following classes as components of the shape.

Straight Lines

  • HLineTo – Creates a horizontal line from the current point to x (HLineTo API)
  • VLineTo – Creates a vertical line from the current point to y (VLineTo API)
  • LineTo – Draws a straight line from the current coordinate to the new coordinates (LineTo API)

Curves

  • ArcTo – Forms an arc from the previous coordinates to the specified x and y coordinates by using the specified radius. (ArcTo API)
  • CubicCurveTo – Creates a cubic Bézier curve, which requires three intermediate Bézier control points. The curve intersects both the current coordinates and the specified coordinates (x,y), using the specified points (controlX1,controlY1) and (controlX2,controlY2). All coordinates are specified in double precision. (CubicCurveTo API)
  • QuadCurveTo – Creates a quadratic Bézier curve, which requires two intermediate Bézier control points. The curve intersects both the current coordinates and the specified coordinates (x, y), using the specified point (controlX, controlY). All coordinates are specified in double precision. (QuadCurveTo API)

Other Components

  • MoveTo – Moves to the specified coordinates without creating a line. The MoveTo class is used to create the starting point of the path. Used in the middle of the list of path points, it creates a gap in the line and is considered a new starting point by ClosePath. (MoveTo API)
  • ClosePath – Connects the current point to the last starting point. (ClosePath API)

Example Code

The following graphic provides an example of every class that can be used in the points variable. The code, which appears below the graphic, uses a single Path instance, yet the continuity of the line is broken by inserting a MoveTo instance in the middle of the list of points. Each number in red in the graphic corresponds to the number at the beginning of each comment line in the code.

Nonstandard shape using Path class

The following code example shows how the path was created.

Path {
    elements: [
        //1 Starting point:
        MoveTo { x: 50  y: 10 }
        //2 Vertical line:
        VLineTo { y: 50 }
        //3 Norizontal line:
        HLineTo { x: 100 }
        //4 Line connecting previous to current points:
        LineTo { x: 150  y: 100}
        //5 Arc of radius 50:
        ArcTo { x: 200  y: 150  radiusX: 50  radiusY: 50 }
        //6 Gap in the line and a new starting point:
        MoveTo {x: 250 y: 200 }
        //7 Cubic Bezier curve:
        CubicCurveTo {
            controlX1:   250  controlY1:   250
            controlX2: 300  controlY2: 300
                    x: 350          y:  250
        }
        //8 Quadratic Bezier curve:
        QuadCurveTo {
            controlX: 525.0  controlY: 550.0
            x: 500.0  y: 100.0
        }
        //9 Connect to the most recent MoveTo instance
        ClosePath{ }
    ]
    fill:Color.BLUE;
}

Tips

  • To create stroke and fill for the shape, see the topic How do I work with files and strokes? The default fill value is null instead of black for the Path class. In the preceding graphic, a fill variable is specified. Note how the fill works in the part of the path that is not closed. The side with the smaller angles between each two points is filled.
  • The QuadCurve and CubicCurve classes can be used instead of Path to define open Bezier curves.
  • The Path class can also be used to draw lines.
  • A Path or SVGPath object or any other shape can be used with the PathTransition class to create animation along a path.

Examples

API Documentation

How do I create a morphing effect from one shape to another?


Use the DelegateShape class to create a morphing effect from any shape to any other shape.

Example Code

The following code example shows how to use a morphing effect to change a rectangle to a circle and back again.

Example of morphing a shape

import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.DelegateShape;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;
import javafx.scene.Scene;

var shape1 = Rectangle { x: 10  y: 10  width: 100  height: 50 };
var shape2 = Circle { centerX: 40  centerY: 40  radius: 20 };
var geom : Shape = shape1;
Timeline {
    repeatCount: Timeline.INDEFINITE
    autoReverse: true
    keyFrames: [
        KeyFrame { time: 0s values: geom => shape1 }
        KeyFrame { time: 2s values: geom => shape2 
			tween Interpolator.LINEAR }
    ]
}.play();

Stage {
    scene:  Scene {
        height:70
        width:70
        content:
            DelegateShape {
                shape: bind geom
                fill: Color.web("#80CAFA") 
            }
    }
}

API Documentation

How do I clip shapes?


Use the clip variable of the Node class to create a clipping path to restrict the display area of another shape. Any node can be used as a clipping path, which is applied to the node. Only the pixels of the main node that lie inside the clipping path are displayed.

Clipping is particularly useful when modifying the shape of an image. For example, suppose that a text object is used as a clipping path for an image node, the only parts of the image that are displayed are the parts that lie within the boundaries of the letters in the text string.

Example Code 1: Image With Rounded Corners

In the following example, a typical photo with square corners is clipped by a rounded rectangle of the same size, resulting in a photo with rounded corners.

Photo with rounded edges

import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.shape.Rectangle;

// Import the image
def image = Image {
        url: "{__DIR__}Vacation.jpg"
    }

// The clipping node: text above a rectangle
def clipnode =
    Rectangle {
        x: 20
        y: 20
        height: image.height
        width: image.width
        arcHeight: 40
        arcWidth: 40
    }

// Display the image with the clipping node
def clippedimage = ImageView {
        image: image
        x: 20
        y: 20
        clip:  clipnode
    }


Stage {
    title: "Clipping"
    width: image.width + 40
    height: image.height + 70
    scene: Scene {
        content: [
            clippedimage
        ]
    }
}

Example Code 2: Image Clipped by a Group Node

In the following example, the image is imported with an Image class instance and displayed in an ImageView node. The ImageView node is clipped by a Group node, which contains text and a rounded rectangle. The application produces the following clipped image:

Clipped image

A background rectangle is added to provide contrast for the clipped image:

Example of clipping

import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextOrigin;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.Group;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.TextAlignment;
import javafx.scene.text.FontWeight;
import javafx.scene.paint.Color;

// Import the image
def image = Image {
    url: "{__DIR__}Vacation.jpg"
}

// The clipping node: text above a rectangle
def gp = Group {
    content: [ Text {
            content: "vacation"
            textOrigin: TextOrigin.TOP
            textAlignment: TextAlignment.CENTER
            font: Font.font("Arial Black", FontWeight.BOLD, 84)
            x: 40
            y: 20
        },
        // The bar under the text
        Rectangle {
            x: 25
            y: 92
            height: 58
            width: image.width - 10
            arcHeight: 10
            arcWidth: 10
        }
    ]
}

// Display the image with the clipping node
def cliptext = ImageView {
        image: image
        x: 20
        y: 20
        clip:  gp
    }

// Background rectangle
def background = Rectangle {
            x: 20
            y: 20
            height: 140
            width: image.width 
            arcHeight: 14
            arcWidth: 14
            fill: Color.BLACK
            stroke:Color.MISTYROSE
            strokeWidth: 4
            opacity: 0.7
            }

Stage {
    title: "Clipping"
    width: image.width + 40
    height: image.height + 50
    scene: Scene {
        content: [
            background,
            cliptext
        ]
    }
}

Tips

  • To use more than one node to clip another node, create a Group node, as shown in this example.
  • If the clipping node is positioned so that it does not overlap the node to be clipped, neither node will be displayed.
  • You do not need to specify a fill color for the clipping node, because that node will become transparent and display the clipped node underneath.
  • To produce an animation effect, bind a position or size of the clipping area to the KeyFrame variable in a Timeline object.

Differences Between Clipping and Shape Intersection and Subtraction

The ShapeIntersect and ShapeSubtract classes can be applied only to shapes and text with a fill area. The two classes are in the Desktop profile of the API, which means that they cannot be used for mobile deployment. The clip variable of the Node class can be applied to any node, and the Node class is in the Common profile of the API. This flexibility enables the clip variable to be used on mobile devices. Note, however, that currently several open issues in JIRA apply to the use of clipping on mobile devices.

The clip variable of the Node class has the following similarities to shape intersection and shape subtraction, which can cause some confusion.

Both the ShapeIntersect class and the clip variable of the Node class display only the area that is common to both objects. However, the ShapeIntersect operation ignores the fill color of individual shapes and applies a default fill color to the resulting shape. The transparency of the upper layer cannot be created with the ShapeIntersect class.

The clip variable can also produce an effect that looks like shape subtraction, in that the clipping path enables an underlying object to become visible. However, the resulting path is different. The ShapeSubtract operation removes pixels from the layer it is subtracted from. In contrast, the clipping operation changes the path of the underlying layer to match that of the clipping node, as demonstrated in the following example.

Example Code 3: Shape Subtraction That Mimics Clipping

The following example shows the use of the ShapeSubtract class to create text cut out from a rectangle, revealing an image placed underneath. The visual effect somewhat resembles the clipping effect produced by the clip variable of the Node class, in that an underlying layer is allowed to show through. However, in the ShapeSubtract operation, the shape of the image is not altered by the text or the interior rectangle.

In this example, the image is clipped with a rounded black rectangle of the same size to round its corners. The text and a smaller rounded rectangle are subtracted from the larger black rectangle, leaving cutout shapes, which then overlay an image. Because the ShapeSubtract class works only with shapes, the image itself cannot be included in the ShapeSubtract operation.

Image with rounded corners and a ShapeSubtract shape superimposed

import javafx.scene.Scene;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextOrigin;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.TextAlignment;
import javafx.scene.text.FontWeight;
import javafx.scene.shape.ShapeSubtract;
import javafx.stage.Stage;
import javafx.scene.paint.Color;

// Import the image
def image = Image {
    url: "{__DIR__}Vacation.jpg"
}

//Rounded rectangle for rounding image corners and shape cutout
def roundedrectangle = Rectangle {
        x: 10
        y: 10
        width: bind image.width
        height: bind image.height
        arcHeight: 20
        arcWidth: 20
    }

// Display the image with rounded corners
def roundedimage = ImageView {
        image: image
        x: 10
        y: 10
        clip:  roundedrectangle
    }

// Create the rounded rectangle with text subtracted
def cutoutbox = ShapeSubtract {
    a: roundedrectangle
    b: [ Text {
            content: "vacation"

            textOrigin: TextOrigin.TOP
            textAlignment: TextAlignment.CENTER
            font: Font.font("Arial Black", FontWeight.EXTRA_BOLD, 36)
            x: 260
            y: 12
        },
        // The bar under the text
        Rectangle {
            x: 15
            y: 50
            width: bind image.width - 10
            height: bind image.height - 50
            arcHeight: 10
            arcWidth: 10
        }
    ]
}

Stage {
    title: "Cutouts with Shape Subtraction"
    width: image.width + 20
    height: image.height + 50
    scene: Scene {
        fill: Color.DARKGREY
        content: [
            roundedimage, cutoutbox
        ]
    }
}

API Documentation

Related How-To Topics

Last Updated: January 2010
[Return to How-To's Home]



Rate This Article
Leave a Comment or Suggest a Topic

Do you have comments about this article? We welcome your participation in our community. Please keep your comments civil and on point. You may optionally provide your email address to be notified of replies—your information is not used for any other purpose. By submitting a comment, you agree to these Terms of Use.

 

English
日本語
한국어
简体中文
русский
Português do Brasil