Creating a timeline image with JavaFX

To create some timeline images for my book, I created this little JavaFX application to be able to easily update the content and recreate the image. Of course you can do the same in an image editor, but hey I’m a programmer and lazy, so I want a program to do the job for me ;-)

History of programming languages

Other timelines generated with this code below…

Source code

The sources are available on GitHub inside the repository with all the book example code.

The datasets used to generate the timelines, are defined in an enum in the format startYear, endYear, entries(year, label):

LANGUAGE_BIRTHDATES(1990, 2024, Map.ofEntries(
            entry(1990, "Ruby\n "),
            entry(1991, "Python\n "),
            entry(1995, "Java, JavaScript, PHP, Qt"),
            entry(2000, "C#"),
            entry(2009, "Go"),
            entry(2014, "Swift"))),

Drawing the timeline itself is achieved with Lines and a label added to a Pane:

public class JavaTimeline extends Pane {

    public JavaTimeline(int width, int height, int offset, DataSet dataSet) {
        // Set the background color
        this.setStyle("-fx-background-color: #ffffff");

        // Draw the main line from left to right
        int axisLength = width - (2 * offset);

        Line horizontalAxis = new Line(offset, offset * 3, axisLength, offset * 3);
        horizontalAxis.setStroke(Color.DARKGREY);
        horizontalAxis.setStrokeWidth(5);
        this.getChildren().add(horizontalAxis);

        // Draw the year lines
        int yearsToDraw = dataSet.getEndYear() - dataSet.getStartYear() + 1;
        double distanceBetweenYears = axisLength / yearsToDraw;

        for (int i = 0; i < yearsToDraw; i++) {
            int currentYear = dataSet.getStartYear() + i;
            double yearLineX = (offset * 3) + (i * distanceBetweenYears);

            Line yearLine = new Line(yearLineX, offset * 2, yearLineX, offset * 4);
            yearLine.setStroke(Color.DARKGREY);
            yearLine.setStrokeWidth(currentYear % 10 == 0 ? 4 : 2);
            this.getChildren().add(yearLine);

            // Add a label for every 10 year
            if (currentYear % 10 == 0) {
                Label yearLabel = new Label(String.valueOf(currentYear));
                yearLabel.setLayoutX(yearLineX - 20);
                yearLabel.setLayoutY(offset * 5);
                yearLabel.setStyle("-fx-font-weight: bold; -fx-font-size: 18px;");
                this.getChildren().add(yearLabel);
            }

            // Add a special notation
            String notation = dataSet.getEntries().get(currentYear);

            if (notation != null && !notation.isEmpty()) {
                Line notationLine = new Line(yearLineX, offset * 5, yearLineX, offset * 9);
                notationLine.setStroke(Color.RED);
                notationLine.setStrokeWidth(3);
                this.getChildren().add(notationLine);

                int notationLabelWidth = height - (offset * 9);

                Label notationLabel = new Label(String.valueOf(dataSet.getStartYear() + i) + " - " + notation);
                notationLabel.setLayoutX(yearLineX);
                notationLabel.setLayoutY((offset * 9));
                notationLabel.setPrefWidth(notationLabelWidth);
                notationLabel.setPrefHeight(40);
                notationLabel.setStyle("-fx-font-size: 14px; -fx-text-alignment: left;");
                notationLabel.getTransforms().add(new Rotate(70, 0, 20 / 2, 0, Rotate.Z_AXIS));
                this.getChildren().add(notationLabel);
            }
        }
    }
}

Other generated images