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.
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();
}