JavaFX Vector Graphics with Shapes

JavaFX provides vector graphic parts that can be placed on the screen like GUI components. This section explains how to create graphics with them.

Using FXML Shapes

Drawing graphics with Canvas feels similar to Swing and AWT. In other words, you obtain a graphics context and call rendering methods to draw on the screen. The resulting graphic is simple bitmap graphics. Once it is drawn, that is the end of it.

Separate from bitmap graphics, JavaFX also includes features for vector graphics. Vector graphics are shapes that hold graphic information such as position and size. You can later change them and manipulate their position or size. Because they are stored as data and redrawn as needed, they do not become rough like bitmap graphics when enlarged or reduced.

These vector graphics are included in the javafx.scene.shape package. They can be used as FXML tags. Let’s look at a simple example.

<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.shape.*?>
<Pane xmlns="http://javafx.com/javafx"
    xmlns:fx="http://javafx.com/fxml"
    fx:controller="com.tuyano.libro.AppController">
    <Rectangle x="50" y="50" width="100" height="100"
        fill="RED" stroke="BLUE" strokeWidth="5"/>
    <Circle centerX="150" centerY="150" radius="50"
        fill="GREEN" stroke="CYAN" strokeWidth="5"/>
</Pane>

This example displays a rectangle and a circle. Write it in an FXML file and load it from Java to display it in a Scene. For example, if you create it as app.fxml, run it as follows.

Scene scene = new Scene(FXMLLoader.load(getClass().getResource("app.fxml")),300,300);
stage.setScene(scene);
stage.show();

A red rectangle and green circle are displayed in the window. Since shapes are used to freely place graphics in the window, layout containers such as <BorderLayout> are not meaningful here. Therefore, <Pane> is used as the root tag. <Pane> is the simplest container and has no layout function. It is useful when placing components such as shapes that do not need layout.

Main Shape FXML Tags

The example created a rectangle and a circle. There are other shapes as well. Important ones are summarized below.

Rectangle Shape: Rectangle

This is the rectangle shape used in the example.

Property Description
x Specifies the horizontal position.
y Specifies the vertical position.
width Specifies the width.
height Specifies the height.

Circle Shape: Circle

This was also used in the example. It displays a circle. Its properties differ slightly from those of a rectangle.

Property Description
centerX Specifies the horizontal position of the center.
centerY Specifies the vertical position of the center.
radius Specifies the radius.

Ellipse Shape: Ellipse

This shape draws an ellipse. Unlike a circle, vertical and horizontal radii can be specified separately.

Property Description
centerX Specifies the horizontal position of the center.
centerY Specifies the vertical position of the center.
radiusX Specifies the horizontal radius.
radiusY Specifies the vertical radius.

Arc Shape: Arc

This draws an arc, as if only part of a circle were cut out. In addition to ellipse properties, arc size-related properties are added.

Property Description
centerX Specifies the horizontal position of the center.
centerY Specifies the vertical position of the center.
radiusX Specifies the horizontal radius.
radiusY Specifies the vertical radius.
startAngle Specifies the angle of the arc start point, from 0 to 360.
length Specifies the size of the arc as an angle.
type Specifies the arc type: ROUND, CHORD, or OPEN.

Line Shape: Line

This shape draws a straight line connecting two points.

Property Description
startX Specifies the horizontal position of the start point.
startY Specifies the vertical position of the start point.
endX Specifies the horizontal position of the end point.
endY Specifies the vertical position of the end point.

Common Shape Properties

Other properties are common to all shapes. They mainly contain fill and line information. Lines have many properties, such as the state of sharp ends, but first remember these three.

Property Description
fill Specifies the fill color.
stroke Specifies the line color.
strokeWidth Specifies the line thickness.

Once you can use these, you can draw basic shapes. Try practicing by writing tags yourself.

Line and Curve Shapes

Circles and rectangles are very simple shapes, but more complex shapes may be needed. In such cases, use shapes such as straight-line polygons and curves.

These shapes require many position-related properties, so writing them becomes more complex. Let’s summarize how they are used.

Straight-Line Shapes: Polygon and Polyline

These draw polygons that connect multiple points with straight lines. Polygon draws a closed shape whose start and end points are connected, while Polyline draws an open shape whose ends are not connected. Their basic usage is the same.

When writing them in FXML, provide a <points> tag between the start and end tags, then write horizontal and vertical position information in order with <Double> tags inside it.

Curve Shapes: QuadCurve and CubicCurve

There are two curve shapes. QuadCurve draws a quadratic curve. It is drawn with the start and end points plus one control point, like an arc-like curve.

CubicCurve draws a cubic curve. It is drawn with two control points in addition to the start and end points. This corresponds to what are commonly called Bezier curves.

These shapes write the start point, end point, and control point positions as properties.

Property Description
startX Specifies the horizontal position of the start point.
startY Specifies the vertical position of the start point.
endX Specifies the horizontal position of the end point.
endY Specifies the vertical position of the end point.

For QuadCurve

Property Description
controlX Specifies the horizontal position of the control point.
controlY Specifies the vertical position of the control point.

For CubicCurve

Property Description
controlX1 Specifies the horizontal position of the first control point.
controlY1 Specifies the vertical position of the first control point.
controlX2 Specifies the horizontal position of the second control point.
controlY2 Specifies the vertical position of the second control point.

The following is an FXML sample that displays a Polygon and a CubicCurve.

<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.shape.*?>
<?import javafx.scene.shape.Ellipse?>
<?import javafx.scene.shape.Line?>
<?import javafx.scene.shape.Polygon?>
<?import javafx.scene.shape.CubicCurve?>
<Pane xmlns="http://javafx.com/javafx"
    xmlns:fx="http://javafx.com/fxml"
    fx:controller="com.devkuma.javafx.AppController">
 
    <Polygon fill="RED">
    <points>
        <Double fx:value="110.0" />
        <Double fx:value="10.0" />
        <Double fx:value="210.0" />
        <Double fx:value="20.0" />
        <Double fx:value="150.0" />
        <Double fx:value="100.0" />
    </points>
    </Polygon>
 
    <CubicCurve fill="YELLOW" stroke="BLUE" strokeWidth="5"
        startX="50" startY="50" endX="200" endY="200"
        controlX1="200" controlY1="50"
        controlX2="50" controlY2="200" />
 
</Pane>

Inside the <Polygon> tag is a <points> tag, and inside that, <Double> tags are used to write each vertex’s horizontal and vertical positions in order. The writing style can be a little hard to understand, so pay attention to the tag structure.

Using Shapes in Java Source Code

Without using FXML, you can create shape objects directly in Java source code and set them on the stage for display. Let’s try it.

The following is a sample.

package com.devkuma.javafx;
     
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.stage.Stage;
 
public class App extends Application {
     
    public static void main(String[] args) {
        launch(args);
    }
 
    @Override
    public void start(Stage stage) {
        Pane root = new Pane();
        createShape(root);
        Scene scene = new Scene(root,300,300);
        stage.setScene(scene);
        stage.show();
    }
     
    public void createShape(Pane root){
        Rectangle r = new Rectangle(50, 50, 100, 100);
        r.setFill(Color.RED);
        r.setStroke(Color.BLUE);
        r.setStrokeWidth(3);
        root.getChildren().add(r);
        Circle c = new Circle(150, 150, 50);
        c.setFill(Color.YELLOW);
        c.setStroke(Color.GREEN);
        c.setStrokeWidth(10);
        root.getChildren().add(c);
    }
}

Here, the createShape method draws a rectangle and a circle. Since it is the same as the earlier FXML sample, comparing the two makes the differences clear.

Creating Instances

The shape classes have the same names as the FXML tags, such as Rectangle and Circle. Instances are generally created by passing the required property values as arguments.

new Rectangle(x, y, width, height)
new Circle(centerX, centerY, radius)

These classes also have a default constructor with no arguments and several other constructors. The form that passes all required property values as arguments is the easiest to understand.

Setting Properties

After creation, call methods that set properties such as fill, stroke, and strokeWidth. For Rectangle, this looks like the following.

r.setFill (Color.RED);
r.setStroke (Color.BLUE);
r.setStrokeWidth (3);

Properties included in FXML tags can be set with methods named set plus the property name. Although not used here, values can also be obtained with methods named get plus the property name or is plus the property name.

Color Values

In this example, setFill and setStroke specify fields of the Color class. These were already used with Canvas. The basic idea, such as setting fill and line colors with Color, is almost the same as Canvas drawing.

Clipping

Now that the basics of creating shapes are clear, let’s add a few more features useful for making complex shapes.

First is clipping. Clipping is like opening a window and showing content through it. In short, you can cut out and display only part of the drawn graphics.

Clipping can be set with the setClip method of the Node class. Node is the superclass of containers such as BorderLayout and Pane, and also of the shape classes handled here.

"Node".setClip("Node");

setClip takes a Node instance as an argument. It sets the shape of that argument node as the clipping area for the component. The component display is cut out and shown in the shape of the node specified as the argument. The argument node itself is not drawn externally.

Try the following example by modifying the earlier createShape method.

public void createShape(Pane root){
    Rectangle r = new Rectangle(50, 50, 100, 100);
    r.setFill(Color.RED);
    r.setStroke(Color.BLUE);
    r.setStrokeWidth(3);
    root.getChildren().add(r);
    Circle c = new Circle(150, 150, 50);
    c.setFill(Color.YELLOW);
    c.setStroke(Color.GREEN);
    c.setStrokeWidth(10);
    root.getChildren().add(c);
    Circle clip = new Circle(120, 120, 75);
    root.setClip(clip);
}

Here, after creating a Rectangle and a Circle and setting them on root, a Circle for clipping is created.

root.setClip(clip);

It is then set as the clipping shape. The graphics are displayed cut out in the circular shape of the Circle set with setClip.

Creating Complex Shapes with Path

Another way to draw complex shapes is to use a Path. A path creates a shape by drawing lines and curves in sequence, as if drawing in one stroke.

This is created with the Path class. Create a Path instance, then create and set instances that become line graphics. The graphic parts that can be set on a Path are subclasses of PathElement, including the following.

MoveTo: Move the Drawing Position

new MoveTo(x, y)

Moves the drawing position in one step to the specified location.

LineTo: Draw a Straight Line

new LineTo(endX, endY)

Draws a straight line from the current drawing position to the specified point.

QuadCurveTo: Draw a Quadratic Curve

new new QuadCurveTo(CP x, CP y, endX, endY)

Draws a quadratic curve from the current drawing position. The arguments specify the control point and end position.

CubicCurveTo: Draw a Cubic Curve

new new QuadCurveTo(CP x1, CP y1, CP x2, CP y2, endX, endY)

Draws a cubic curve from the current drawing position. The arguments specify two control points and the end position.

The following example modifies the earlier createShape method. It creates and displays a path shape made from two straight lines and one cubic curve.

public void createShape(Pane root){
    Path path = new Path();
    MoveTo mt1 = new MoveTo(50, 50);
    path.getElements().add(mt1);
    LineTo lt1 = new LineTo(250, 50);
    path.getElements().add(lt1);
    CubicCurveTo cc1 = new CubicCurveTo(250, 250,50, 50, 50, 250);
    path.getElements().add(cc1);
    LineTo lt2 = new LineTo(50, 50);
    path.getElements().add(lt2);
    path.setFill(Color.RED);
    root.getChildren().add(path);
}

Here, a Path instance is created, and instances such as MoveTo, LineTo, and CubicCurveTo are added. Shapes are connected as follows.

path.getElements().add(mt1);

The getElements method of Path obtains the List (ObservableList) instance set on the Path.

Path manages the PathElement objects in this list, and by adding instances to it with the add method, drawing elements are added. Complex shapes that cannot be drawn with simple combinations of shapes can be drawn with Path.