javajavafxdelayed-execution

How to slow down a program execution in java?


I created a JavaFX program to draw a Sierpinski Carpet recursively, but I want to see the recursive functions in action that is, to slow down the program to see it drawing in real-time. For that I tried something like this:

public void pause(int sleepTime)
{
    try
    {
        Thread.sleep(sleepTime);
    }
    catch(InterruptedException e)
    {
        System.err.println("Error in running rotation animation!");
        System.exit(-1);
    }
}

Then I invoked this function in my drawSierpinskiCarpet function something like this:

public void drawSeripinskiCarpet(GraphicsContext gc, int x, int y, int width, int height)
{
    if(width != height || width < 3)
        return;

    int size = width /= 3;
    gc.setFill(colors[index]);
    gc.fillRect(x + size, y + size, size, size);

    index++;

    if(index == colors.length)
    {
        index = 0;
    }

    pause(1);


    for(int i = 0 ; i < 3 ; i++)
    {
        for(int j = 0 ; j < 3 ; j++)
        {
            if(i == 1 && j == 1)
            {
                continue;
            }

            drawSeripinskiCarpet(gc, x + j * size, y + i * size, size, size);
        }
    }
}

But what happens is the program hang up for a few seconds, and then directly shows up the Carpet. I'm not able to see the program in execution.


Solution

  • Any modification on the GUI only yields visual results after it returns. In the meantime JavaFX cannot handle any events and freezes, until your operation is done.

    You can use a Timeline to execute the operation one step at a time though: Use a stack the data for one drawing execution; this allows you to do one drawing operation at a time in addition to modifying the stack for every frame of the Timeline.

    @Override
    public void start(Stage primaryStage) throws IOException {
        Canvas canvas = new Canvas(729, 729);
        final GraphicsContext gc = canvas.getGraphicsContext2D();
    
        Scene the_scene = new Scene(new StackPane(canvas));
    
        primaryStage.setScene(the_scene);
        primaryStage.show();
    
        final Color[] colors = new Color[] { Color.BLUE, Color.ORANGE, Color.GREEN, Color.RED, Color.BROWN,
                Color.MAGENTA, Color.YELLOW, Color.CYAN, Color.BLACK };
        class IndexHolder {
            int index = 0;
    
            Color getNextFill() {
                Color fill = colors[index];
                ++index;
                if (index >= colors.length) {
                    index = 0;
                }
                return fill;
            }
        }
        final IndexHolder indexHolder = new IndexHolder();
    
        class StackEntry {
            int x;
            int y;
            int size;
    
            public StackEntry(int x, int y, int size) {
                this.x = x;
                this.y = y;
                this.size = size;
            }
    
            void executeStep(List<StackEntry> stack, IndexHolder indexHolder, GraphicsContext gc) {
                int size = this.size /= 3;
                gc.setFill(indexHolder.getNextFill());
                gc.fillRect(x + size, y + size, size, size);
    
                if (size >= 3) {
                    // add new "calls" in reverse order here
                    // (first call in original needs to be popped from the stack first)
                    for (int i = 2; i >= 0; --i) {
                        for (int j = 2; j >= 0; --j) {
                            if (i == 1 && j == 1) {
                                continue;
                            }
    
                            stack.add(new StackEntry(x + j * size, y + i * size, size));
                        }
                    }
                }
            }
        }
    
        // finally create the animation
        final List<StackEntry> stack = new ArrayList<>();
        stack.add(new StackEntry(0, 0, (int) canvas.getHeight()));
    
        final Timeline timeline = new Timeline();
        timeline.setCycleCount(Animation.INDEFINITE);
        timeline.getKeyFrames().add(new KeyFrame(Duration.millis(1),
                evt -> {
                    // pop
                    StackEntry entry = stack.remove(stack.size() - 1);
    
                    entry.executeStep(stack, indexHolder, gc);
                    if (stack.isEmpty()) {
                        timeline.stop();
                    }
                }));
    
        timeline.play();
    
    }