James Murphy
James Murphy

Reputation: 798

Why won't gc.clearRect clear the canvas?

I have some code in a MazeUI class that creates a couple of fields in JavaFX for entering the height and width of a Maze to be generated along with a Button and the event for the button. On clicking Generate Maze the code should generate a random maze.

Now this works fine. But the line to clear the canvas after the Maze is first drawn using gc.clearRect(x, y, z, a) doesn't appear to work and Maze's are re-drawn on top of one another on subsequent clicks:

package dojo.maze.ui;

import dojo.maze.generator.MazeGenerator;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.effect.BoxBlur;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.shape.StrokeLineJoin;
import javafx.stage.Stage;

public class MazeUI extends Application {

private MazeGenerator generator;
private Integer height = 15;
private Integer width = 15;

@Override
public void start(Stage primaryStage) throws Exception {
    primaryStage.setTitle("Dojo: Solving a Maze");

    Group root = new Group();

    drawMazeView(root);

    primaryStage.setScene(new Scene(root, Color.WHITE));
    primaryStage.show();
}


private GraphicsContext initialiseGraphicsContext(Canvas canvas) {
    GraphicsContext gc = canvas.getGraphicsContext2D();
    gc.setFill(Color.BLACK);
    gc.fillRect(0,0,600,600);
    gc.setStroke(Color.GREEN);
    gc.setLineCap( StrokeLineCap.ROUND );
    gc.setLineJoin( StrokeLineJoin.ROUND );
    gc.setLineWidth(3);

    BoxBlur blur = new BoxBlur();
    blur.setWidth(1);
    blur.setHeight(1);
    blur.setIterations(1);
    gc.setEffect(blur);
    return gc;
}

private void drawMazeView(Group root) {
    Canvas canvas = new Canvas(800, 800);
    GraphicsContext gc = initialiseGraphicsContext(canvas);

    GridPane.setConstraints(canvas, 0, 6);

    GridPane grid = new GridPane();
    grid.setPadding(new Insets(10, 10, 10, 10));
    grid.setVgap(5);
    grid.setHgap(5);

    Label heightLabel = new Label("Height:");
    final TextField heightTextField = new TextField();
    HBox hbHeight = new HBox();
    hbHeight.getChildren().addAll(heightLabel, heightTextField);
    hbHeight.setSpacing(10);

    GridPane.setConstraints(hbHeight, 0, 0);

    Label widthLabel = new Label("Label:");
    final TextField widthTextField = new TextField();
    HBox hbWidth = new HBox();
    hbWidth.getChildren().addAll(widthLabel, widthTextField);
    hbWidth.setSpacing(10);

    GridPane.setConstraints(hbWidth, 0, 2);

    VBox fieldsBox = new VBox();
    fieldsBox.getChildren().addAll(hbHeight, hbWidth);

    // Create button that allows you to generate a new maze
    Button btn = new Button();
    btn.setText("Generate Random Maze");
    btn.setOnAction(new EventHandler<ActionEvent>() {

        @Override
        public void handle(ActionEvent event) {
            System.out.println("Button clicked!");
            height = Integer.valueOf(heightTextField.getText());
            width = Integer.valueOf(widthTextField.getText());

            // clear old maze
            gc.clearRect(0, 0,
                    canvas.getHeight(),
                    canvas.getWidth());

            generator = new MazeGenerator(height, width);

            drawMaze(gc);
        }
    });

    GridPane.setConstraints(btn, 0, 4);

    grid.getChildren().addAll(fieldsBox, btn, canvas);
    root.getChildren().add(grid);
}

void drawMaze(GraphicsContext gc) {
    int[][] maze = generator.maze();
    int xPos = 20,
        yPos = 20,
        length = 40,
        gap = 10;

    for (int i = 0; i < width; i++) {
        xPos = 20;
        // draw the north edge
        for (int j = 0; j < height; j++) {
            if ((maze[j][i] & 1) == 0) {
                gc.strokeLine(xPos, yPos, xPos + length, yPos); // horizontal
            }
            xPos += length + gap;

            System.out.print((maze[j][i] & 1) == 0 ? "+---" : "+   ");
        }
        System.out.println("+");

        xPos = 20;
        // draw the west edge
        for (int j = 0; j < height; j++) {
            if ((maze[j][i] & 8) == 0) {
                gc.strokeLine(xPos, yPos, xPos, yPos + length); // vertical
            }
            xPos += length + gap;

            System.out.print((maze[j][i] & 8) == 0 ? "|   " : "    ");
        }
        gc.strokeLine(xPos, yPos, xPos, yPos + length); // vertical
        System.out.println("|");
        yPos += length + gap;
    }
    // draw the bottom line

    xPos = 20; // reset x pos to western edge

    for (int j = 0; j < height; j++) {
        gc.strokeLine(xPos, yPos, xPos + length, yPos); // horizontal
        System.out.print("+---");
        xPos += length + gap;
    }
    System.out.println("+");
}

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

Code for MazeGenerator:

package dojo.maze.generator;

import java.util.Arrays;
import java.util.Collections;

/*
 * recursive backtracking algorithm
 * shamelessly borrowed from the ruby at
 * http://weblog.jamisbuck.org/2010/12/27/maze-generation-recursive-backtracking
*/
public class MazeGenerator {

private final int x;
private final int y;
private final int[][] maze;

public static final int[][] mazeProblemOne = new int[][] {{2,5,2,3,7,3,1,6,7,3,3,5,6,3,5},{4,10,3,5,12,6,3,9,10,3,5,12,8,6,13},{12,6,5,12,12,14,3,1,6,3,9,10,3,9,12},{12,12,12,12,12,12,6,3,9,4,6,7,1,6,9},{14,9,10,9,12,12,12,6,5,12,12,10,5,12,4},{12,2,7,5,12,12,10,9,12,10,13,4,10,9,12},{12,6,9,12,12,12,2,5,10,5,10,11,3,3,13},{12,10,5,12,10,11,3,13,4,10,5,6,3,1,12},{12,6,9,10,5,4,6,9,12,6,9,12,6,3,9},{10,9,6,1,12,10,11,3,9,12,6,13,12,2,5},{6,7,9,6,9,6,3,3,3,9,8,12,12,6,13},{8,12,6,9,6,13,6,3,7,3,3,13,12,12,12},{6,9,10,5,12,12,12,4,10,3,5,8,12,12,8},{14,3,5,12,8,12,10,11,3,5,10,5,12,10,5},{10,1,10,11,3,9,2,3,3,11,1,10,11,3,9}};
public static final int[][] mazeProblemTwo = new int[][] {{2,5,6,5,6,3,1,6,3,3,7,5,2,3,5},{4,10,9,12,14,3,3,9,6,5,12,10,3,3,9},{14,3,3,9,8,6,3,5,12,12,10,3,3,3,5},{12,6,3,3,5,12,4,10,9,10,3,3,3,3,13},{12,12,6,5,12,12,10,3,3,3,5,6,3,3,9},{12,10,9,12,10,9,6,5,6,3,13,10,3,3,5},{10,3,5,12,6,5,12,10,9,4,14,3,3,1,12},{4,6,9,12,12,10,9,6,3,11,13,6,3,3,9},{12,12,2,13,14,3,5,8,6,5,8,10,3,3,5},{12,10,3,9,12,4,12,6,9,12,6,5,6,3,9},{14,7,3,1,10,13,12,12,4,12,12,10,9,6,1},{12,10,3,7,1,12,10,11,9,12,12,2,3,11,5},{12,2,5,12,6,9,2,5,6,9,10,3,3,5,12},{10,5,12,12,12,6,3,13,12,6,7,1,6,9,12},{2,11,9,10,11,9,2,9,10,9,10,3,11,3,9}};
public static final int[][] mazeProblemThree = new int[][] {{2,5,2,3,7,3,3,3,5,6,3,1,6,7,1},{4,10,3,5,12,2,5,6,9,10,3,3,9,14,5},{10,7,5,12,10,5,14,9,6,5,6,3,5,12,12},{6,9,8,10,3,9,12,6,13,12,10,5,12,12,12},{12,6,3,7,1,6,9,12,12,10,3,9,12,8,12},{12,12,4,10,5,10,5,8,14,3,5,2,11,3,13},{12,10,13,4,10,5,10,5,10,5,10,5,6,5,12},{14,1,12,10,5,14,1,12,6,9,4,10,9,12,12},{14,5,10,5,12,10,5,12,12,2,15,3,1,12,12},{8,12,6,9,10,5,12,12,10,3,9,6,3,9,12},{6,9,14,3,3,9,12,10,3,3,3,9,4,6,9},{10,5,12,6,3,3,13,6,3,3,1,6,11,9,4},{6,9,12,10,5,6,11,9,6,3,3,9,6,3,13},{14,5,12,4,12,10,1,6,9,6,5,2,13,4,12},{8,10,11,9,10,3,3,11,3,9,10,3,9,10,9}};
public static final int[][] mazeProblemFour = new int[][] {{2,3,3,5,4,6,7,3,3,5,6,5,6,3,5},{6,3,1,12,12,12,10,3,5,10,9,12,10,5,12},{12,6,3,9,14,9,4,6,9,2,3,11,1,12,12},{14,9,2,3,11,5,12,10,3,5,6,3,3,9,12},{10,5,6,5,2,11,11,5,4,12,12,6,7,1,12},{4,10,9,10,3,5,6,9,12,10,9,8,12,6,13},{14,5,4,6,3,9,12,6,11,5,6,3,9,12,8},{12,10,9,12,4,6,9,10,5,12,14,3,5,10,5},{12,6,5,12,12,12,6,3,9,12,8,6,13,4,12},{14,9,10,9,12,12,10,3,5,10,5,12,10,13,12},{12,2,7,5,10,11,3,3,9,6,9,12,2,9,12},{10,3,9,10,3,3,5,4,6,11,3,9,6,3,13},{6,5,6,7,3,5,12,10,9,6,3,3,9,6,9},{12,10,9,10,5,8,12,6,5,10,5,6,5,10,5},{10,3,3,1,10,3,11,9,10,3,9,8,10,3,9}};
public static final int[][] mazeProblemFive = new int[][] {{2,3,5,6,3,3,7,5,6,3,3,3,7,3,5},{6,5,12,12,6,3,9,8,10,5,4,6,9,4,12},{12,12,12,12,10,3,5,6,5,10,13,10,5,14,9},{12,10,9,10,5,4,10,9,14,1,12,4,12,10,5},{10,5,6,5,10,15,3,1,14,5,14,9,10,5,8},{6,9,12,10,5,8,6,5,8,10,9,6,5,14,5},{10,3,9,4,12,6,9,10,3,3,3,9,12,8,12},{6,3,7,13,12,10,7,5,2,7,7,1,10,3,13},{14,1,12,8,10,5,12,12,6,9,12,6,3,3,9},{8,6,13,6,3,9,12,12,12,2,13,12,6,3,5},{6,9,8,12,4,6,9,12,14,5,8,10,9,4,12},{12,6,3,9,10,9,6,9,8,10,7,5,6,11,9},{12,10,3,3,3,5,14,3,3,5,12,12,10,5,4},{14,1,6,5,6,9,10,3,1,12,12,10,1,10,13},{10,3,9,10,11,3,3,3,3,9,10,3,3,3,9}};

public MazeGenerator(int x, int y) {
    this.x = x;
    this.y = y;
    maze = new int[this.x][this.y];
    generateMaze(0, 0);
}

public void display() {
    for (int i = 0; i < y; i++) {
        // draw the bbc.north edge
        for (int j = 0; j < x; j++) {
            System.out.print((maze[j][i] & 1) == 0 ? "+---" : "+   ");
        }
        System.out.println("+");
        // draw the west edge
        for (int j = 0; j < x; j++) {
            System.out.print((maze[j][i] & 8) == 0 ? "|   " : "    ");
        }
        System.out.println("|");
    }
    // draw the bottom line
    for (int j = 0; j < x; j++) {
        System.out.print("+---");
    }
    System.out.println("+");
}

private void generateMaze(int cx, int cy) {
    DIR[] dirs = DIR.values();
    Collections.shuffle(Arrays.asList(dirs));
    for (DIR dir : dirs) {
        int nx = cx + dir.dx;
        int ny = cy + dir.dy;
        if (between(nx, x) && between(ny, y)
                && (maze[nx][ny] == 0)) {
            maze[cx][cy] |= dir.bit;
            maze[nx][ny] |= dir.opposite.bit;
            generateMaze(nx, ny);
        }
    }
}

private static boolean between(int v, int upper) {
    return (v >= 0) && (v < upper);
}

public int[][] maze() {
    return maze;
}

private enum DIR {
    N(1, 0, -1), S(2, 0, 1), E(4, 1, 0), W(8, -1, 0);
    private final int bit;
    private final int dx;
    private final int dy;
    private DIR opposite;

    // use the static initializer to resolve forward references
    static {
        N.opposite = S;
        S.opposite = N;
        E.opposite = W;
        W.opposite = E;
    }

    private DIR(int bit, int dx, int dy) {
        this.bit = bit;
        this.dx = dx;
        this.dy = dy;
    }
};

public static void main(String[] args) {
    int x = args.length >= 1 ? (Integer.parseInt(args[0])) : 8;
    int y = args.length == 2 ? (Integer.parseInt(args[1])) : 8;
    MazeGenerator maze = new MazeGenerator(x, y);
    maze.display();
}
}

How do I fix the code so that it correctly clears the Maze after each button click? Am I missing something?

Upvotes: 1

Views: 1344

Answers (2)

Yervant
Yervant

Reputation: 245

For people looking for an answer, another reason why it may not be working is if you set the GraphicsContext.globalBlendMode to something like BlendMode.SCREEN.

Since clearRect() paints the canvas with transparent pixels, a transparent pixel would have no effect in that BlendMode.

Set the gc.globalBlendMode = BlendMode.SRC_OVER right before calling clearRect()

Upvotes: 0

Roland
Roland

Reputation: 18415

Remove the BoxBlur effect before you clear the canvas, then re-apply it. Clearing the canvas simply paints with a transparent color. So the BoxBlur effect will affect that too.

And what jewelsea said. You'd have found it yourself if you'd only reduced the code to a minimum ;-)

Upvotes: 2

Related Questions