
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.layout.BorderPane;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;

/**
 *  When run as a program, this class opens a window on the screen that
 *  shows a set of nested rectangles that seems to be moving infinitely 
 *  inward towards the center.  The animation continues until the user
 *  closes the window.
 */
public class MovingRects extends Application {

    /**
     * Draws a set of nested rectangles. This subroutine is called 60 times per
     * second and is responsible for redrawing the entire drawing area.  The
     * parameter g is used for drawing. The frameNumber starts at zero and
     * increases by 1 each time this subroutine is called.  The parameters width
     * and height give the size of the drawing area, in pixels.  
     * The sizes and positions of the rectangles that are drawn depend
     * on the frame number, giving the illusion of motion.
     */
    public void drawFrame(GraphicsContext g, int frameNumber, double elapsedSeconds, int width, int height) {

        g.setFill(Color.WHITE);
        g.fillRect(0,0,width,height);  // Fill drawing area with white.

        double inset; // Gap between edges of drawing area and the outer rectangle.

        double rectWidth, rectHeight;   // The size of one of the rectangles.

        g.setStroke(Color.BLACK);  // Draw the rectangle outlines in black.

        inset = frameNumber % 15 + 0.5; // The "+ 0.5" is a technicality to produce a sharper image.

        rectWidth = width - 2*inset;
        rectHeight = height - 2*inset;

        while (rectWidth >= 0 && rectHeight >= 0) {
            g.strokeRect(inset, inset, rectWidth, rectHeight);
            inset += 15;       // rectangles are 15 pixels apart
            rectWidth -= 30;
            rectHeight -= 30;
        }

    }

    //------ Implementation details: DO NOT EXPECT TO UNDERSTAND THIS ------

    public void start(Stage stage) {
        int width = 800;   // The width of the image.  You can modify this value!
        int height = 600;  // The height of the image. You can modify this value!
        Canvas canvas = new Canvas(width,height);
        drawFrame(canvas.getGraphicsContext2D(), 0, 0, width, height);
        BorderPane root = new BorderPane(canvas);
        root.setStyle("-fx-border-width: 4px; -fx-border-color: #444");
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.setTitle("Infinte Moving Rects"); // STRING APPEARS IN WINDOW TITLEBAR!
        stage.show();
        stage.setResizable(false);
        AnimationTimer anim = new AnimationTimer() {
            private int frameNum;
            private long startTime = -1;
            private long previousTime;
            public void handle(long now) {
                if (startTime < 0) {
                    startTime = previousTime = now;
                    drawFrame(canvas.getGraphicsContext2D(), 0, 0, width, height);
                }
                else if (now - previousTime > 0.95e9/60) {
                       // The test in the else-if is to guard against a bug that has shown
                       // up in some versions of JavaFX on some computers.  The bug allows
                       // the handle() method to be called many times more than the 60 times
                       // per second that is specified in the JavaFX documentation.
                    frameNum++;
                    drawFrame(canvas.getGraphicsContext2D(), frameNum, (now-startTime)/1e9, width, height);
                    previousTime = now;
                }
            }
        };
        anim.start();
    } 

    public static void main(String[] args) {
        launch();
    }

} // end MovingRects

