JavaFX doesn’t support drawing triangles out of the box, but there are three really simple ways to draw them anyway. In fact, we can create drawable triangles on the Pane, Canvas and Button objects.

In JavaFX, a triangle can be drawn in three ways:

  1. Creating a Polygon object with three points.
  2. Issuing draw commands strokePolygon() and fillPolygon() to a Canvas node
  3. Creating a shaped Button control by specifying their shape using CSS property ‘-fx-shape’

What you will get out of this article?

There’s more than one way to define a triangle, so I’ll go through drawing triangles in two ways. The first, obvious way is using three points (duh). But, sometimes it’s useful to get a triangle by specifying the angle between two lines, and a distance to the opposing edge.

For example, if you’re creating a bounding box for a field-of-view (it’s computationally cheaper than using an arc). Finally, I’ll go through creating shaped button controls.

Use these links to jump ahead if you need a specific example:

Everything’s easier with free code: In each section, I’ve included code. You’re welcome to use it to create objects for painting.

How to draw a triangle using lines on a Pane

JavaFX does a lot to improve the efficiency of its own drawing by keeping track of which areas of a Scene have and haven’t changed between frames. Unfortunately, that means it’s more difficult than it might seem to just create a Triangle object like the Rectangle or Circle, which extend Shape.

Each concrete Shape has a Helper class, which itself has an Accessor class. These help the JavaFX scene to render the shapes – but they’re also used directly by the Prism rendering pipeline. That means we’re not allowed to create or interact with them.

Instead, we’ll need to create a factory class that creates Polygon objects – triangles – from some inputs we’ll provide. The options are:

Creating a triangle polygon using spatial coordinates

The easiest way to create a triangle is by specifying the spatial coordinates of the triangle’s three points. And the temptation here is to create a triangle where we want to put it in the Scene.

But wait! Creating a triangle like that might make moving it around much harder down the line. If you create a triangle that’s positioned manually, JavaFX still considers its origin to be (0,0). Then, if you need to move the object later, you won’t be able to measure where it is (the layout and translate properties will still be zero).

That might be absolutely fine – sometimes you don’t need to move things once they’re created. But I think it’s worth bearing in mind. What I prefer to do is create a function that converts those points into layout-corrected values.

The Polygon object is then centred in a sensible way, and the layoutX and layoutY properties are appropriately updated.

Polygon createTriangle(Point2D p1, Point2D p2, Point2D p3){
    Point2D centre = p1.midpoint(p2).midpoint(p3);
    Point2D p1Corrected = p1.subtract(centre);
    Point2D p2Corrected = p2.subtract(centre);
    Point2D p3Corrected = p3.subtract(centre);
    Polygon polygon = new Polygon(
            p1Corrected.getX(), p1Corrected.getY(),
            p2Corrected.getX(), p2Corrected.getY(),
            p3Corrected.getX(), p3Corrected.getY()
    );
    polygon.setLayoutX(centre.getX());
    polygon.setLayoutY(centre.getY());
    return polygon;
}

One solid alternative to creating triangles where the origin is at the centre of the Polygon is to create triangles with one corner at (0,0). The triangle will still rotate based on its calculated center, but it will at least be positioned logically.

In the next section, I’ll be creating a field-of-view example, which includes code to change how a triangle rotates, which you can also apply to the example above.

Creating triangles from an angle and a distance.

A second option, if you need to specify a triangle for things like field-of-view, is to specify the origin of the triangle, an angle, and a distance. Obviously, you can only create symmetrical triangles, but that works in this case.

The general principle here is to just create the triangle, point up from the origin at the length requested, then setting the “origin” of the triangle, and the angle, to whatever was set. In the image above, the origin should be on the character at 25, 10 and the angle is 180° (pointing down).

Polygon createTriangle(Point2D origin, double length, double angle){
    Polygon fovTriangle = new Polygon(
            0d, 0d,
            (length * Math.tan(angle)), -length,
            -(length * Math.tan(angle)), -length
    );
    
    fovTriangle.setLayoutX(origin.getX());
    fovTriangle.setLayoutY(origin.getY());
    return fovTriangle;
}

Not quite finished! One quirk of the way JavaFX deals with rotation is that the default behaviour for a node is to calculate the centre of the bounding box as the centre of rotation as well.

That’s not really what we want from a field-of-view. Ideally, we want the triangle to pivot on our character. So we’ll need to set the centre of rotation ourselves to the same origin we defined for the triangle.

We don’t have enough access to the default rotate transform in the Node object to give us this flexibility. So, we’ll need to do a little more tweaking to set our own Rotate transform, which allows us to set a pivot point.

Check out the full implementation with custom rotation methods in the dropdown.

How to draw triangles on a Canvas

The basic rule for drawing triangles on a Canvas is that you’re either going to have to draw three lines, or create a polygon. While the three lines creates a simple solution, creating a polygon also has the option of providing a filled interior. Because creating a polygon gives a little more flexibility, that’s the approach I’ll use here.

To draw a polygon on a Canvas node, you’ll need to specify the x and y-coordinates separately as arrays of double values, alongside the number of points (obviously, here it’s three). The parameters are the same for both the strokePolygon() and fillPolygon() methods of the Canvas’s GraphicsContext.

Canvas fillPolygon() and strokePolygon() commands
Any extra values in the coordinate arrays are ignored.

This barely touches the surface of things you can achieve with the Canvas node. If you’re interested in learning more, I’ve written a comprehensive guide about the Canvas here. It’s both a helpful illustrated guide to the drawing commands, as well as having more depth on how the Canvas itself works.

Creating triangles by setting ‘-fx-shape’

Finally, If you want to use something like a button (in-built click functionality, for example), but shape it into a triangle, the easiest way to do it is to use the CSS -fx-shape property, specifying an SVG path as a String. You can either do that in Java code, or by referencing a CSS file you’re already using.

Button button= new Button();
button.setPrefHeight(200);
button.setPrefWidth(200);
button.setStyle("-fx-background-color: blue; -fx-shape: 'M 0 0 50 0 0 50 z'");

There are lots of ways to use vector graphics in JavaFX. I think it’s something that brings a lot of value to JavaFX in creating visually appealing applications. If you want to find out more, check out my top 3 tips for incorporating SVGs into your applications.

Conclusions

Although JavaFX doesn’t provide support for drawing triangles natively out of the box, there are three simple work arounds we can use to include them in our apps.

Creating a triangular Polygon object in a Pane is incredibly simple, and we can modify the points with which we create the triangle to make sure it has a sensible origin.

Equally, it you need the triangle to rotate with a pivot at a specific point, you can manually specify a Rotate transform, setting the pivot to the point on the triangle where the pivot needs to be.

Finally, you can create elegant buttons of almost any shape by specifying the CSS -fx-shape property. In this case, we’ve used it to shape the button as a triangle. It’s always worth adopting a flatter effect, as the alternative is a slightly strange looking clipped button.