Reputation: 101
I have a Pane with a grid of canvases in it. When these canvases are colored and I change the scale of the pane white lines appear between the canvases.
The code currently has a scene with a pane in it. When the mouse is dragged it finds a canvas at that location using a HashMap that stores all the canvases by their x and y indexes, the found canvas is colored black. When the mouse is scrolled the scale of the pane is changed by 0.1.
Code:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class Main extends Application {
private static final double SIZE = 10;
public static void main(String[] args) {
launch();
}
@Override
public void start(Stage stage) {
Pane pane = new Pane();
Scene scene = new Scene(pane);
pane.setOnMouseDragged(mouseEvent -> {
int x = (int) (mouseEvent.getX() / SIZE);
int y = (int) (mouseEvent.getY() / SIZE);
Plot plot = new Plot(x, y);
Canvas canvas = canvases.computeIfAbsent(plot, plot1 -> {
Canvas newCanvas = new Canvas(SIZE, SIZE);
pane.getChildren().add(newCanvas);
newCanvas.setLayoutX(x * SIZE);
newCanvas.setLayoutY(y * SIZE);
return newCanvas;
});
canvas.getGraphicsContext2D().setFill(Color.BLACK);
canvas.getGraphicsContext2D().fillRect(0, 0, 50, 50);
});
scene.setOnScroll(scrollEvent -> {
double scale = pane.getScaleX() + (scrollEvent.getDeltaY() > 0 ? 0.01 : -0.01);
pane.setScaleX(scale);
pane.setScaleY(scale);
});
stage.setScene(scene);
stage.show();
}
private static class Plot {
private final int x;
private final int y;
private Plot(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Plot plot = (Plot) o;
return x == plot.x &&
y == plot.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
private final Map<Plot, Canvas> canvases = new HashMap<>();
}
Upvotes: 3
Views: 252
Reputation: 9989
My initial guess was it might be because of using Canvas node, that it has something related to border aliasing issue. So I tried replacing all the Canvas nodes to Rectangles. But still the problem exists. I believe there should be something happening related to layouting using x and y.
While I cannot tell you the exact reason for this issue, I can suggest an alternate approach. Please note that I dont know the end usage of this implemenation or why you keep the Canvases in map. So the below approach is to let you know or give you the direction about how you can fix it.
Alternate Approach #1:
Instead of using many canvas nodes, try defining only one Canvas node that fit in the pane and paint the canvas as you draw. On checking for the zooming, it does not have any issue in spacing (as it is only one node).
Please check the below demo:
[UPDATE]
Alternate Approach #2:
The other approach is to use Path node. This approach also uses only one node for the entire drawing. The other advantage of using this is you can create as many paths as you want if you want to pick different brush colors. The other advantage is you can draw in the negative coords just as you did in your initial implemenation.
The idea is pretty much same as in canvas approach. As you drag, you draw a Path of the box using the mouse cooridinate. I updated the code in the below demo.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class CanvasDrawing extends Application {
private static final double SIZE = 10;
public static void main(String[] args) {
launch();
}
@Override
public void start(Stage stage) {
Pane pane = new Pane();
pane.setStyle("-fx-background-color:white;");
// approachUsingCanvas(pane);
approachUsingPath(pane);
Scene scene = new Scene(pane,Color.RED);
scene.setOnScroll(scrollEvent -> {
double scale = pane.getScaleX() + (scrollEvent.getDeltaY() > 0 ? 0.01 : -0.01);
pane.setScaleX(scale);
pane.setScaleY(scale);
});
stage.setScene(scene);
stage.setTitle("Canvas Drawing");
stage.show();
}
private void approachUsingPath(Pane pane) {
Path path = new Path();
path.setStrokeWidth(1);
path.setStroke(Color.BLACK);
path.setFill(Color.BLACK);
pane.getChildren().add(path);
pane.setOnMouseDragged(mouseEvent -> {
int x = (int) (mouseEvent.getX() / SIZE);
int y = (int) (mouseEvent.getY() / SIZE);
Plot plot = new Plot(x, y);
if(!plots.contains(plot)){
plots.add(plot);
double sx = x * SIZE;
double sy = y * SIZE;
// Draw the box using Path
path.getElements().addAll(new MoveTo(sx,sy),
new LineTo(sx+SIZE,sy),
new LineTo(sx+SIZE,sy+SIZE),
new LineTo(sx,sy+SIZE),
new LineTo(sx,sy));
}
});
}
private void approachUsingCanvas(Pane pane){
Canvas canvas = new Canvas();
canvas.widthProperty().bind(pane.widthProperty());
canvas.heightProperty().bind(pane.heightProperty());
canvas.getGraphicsContext2D().setFill(Color.BLACK);
pane.getChildren().add(canvas);
pane.setOnMouseDragged(mouseEvent -> {
int x = (int) (mouseEvent.getX() / SIZE);
int y = (int) (mouseEvent.getY() / SIZE);
Plot plot = new Plot(x, y);
if(!plots.contains(plot)){
plots.add(plot);
double sx = x * SIZE;
double sy = y * SIZE;
canvas.getGraphicsContext2D().fillRect(sx, sy, SIZE, SIZE);
}
});
}
private static class Plot {
private final int x;
private final int y;
private Plot(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Plot plot = (Plot) o;
return x == plot.x &&
y == plot.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
private final List<Plot> plots = new ArrayList<>();
}
Upvotes: 2