如何对齐 X 轴和 Y 轴来调整放大的网格?

问题描述 投票:0回答:1

我正在做这个绘图项目,但我遇到了这个问题:每次放大或缩小时,我的 x 轴和 y 轴都不合适。知道如何解决这个问题吗?

这是我的代码:

    import javafx.application.Application;
    import javafx.scene.Group;
    import javafx.scene.Scene;
    import javafx.scene.canvas.Canvas;
    import javafx.scene.canvas.GraphicsContext;
    import javafx.scene.input.ScrollEvent;
    import javafx.scene.paint.Color;
    import javafx.stage.Stage;

    public class GraphingCal extends Application {

    private static final int CANVAS_WIDTH = 900;
    private static final int CANVAS_HEIGHT = 700;
    private static final double INITIAL_GRID_SIZE = 20;
    private static final double ZOOM_FACTOR = 1.1;
    private static final double MIN_GRID_SIZE = INITIAL_GRID_SIZE * 0.6;
    private static final double MAX_GRID_SIZE = INITIAL_GRID_SIZE / 0.6;

    private double gridSize = INITIAL_GRID_SIZE;
    private double xOffset = 0;
    private double yOffset = 0;
    private double prevX;
    private double prevY;

    @Override
    public void start(Stage primaryStage) {
        Group root = new Group();
        Scene scene = new Scene(root, CANVAS_WIDTH, CANVAS_HEIGHT);

        Canvas canvas = new Canvas(CANVAS_WIDTH, CANVAS_HEIGHT);
        GraphicsContext gc = canvas.getGraphicsContext2D();
        drawGrid(gc);

        canvas.setOnMousePressed(e -> {
            prevX = e.getX();
            prevY = e.getY();
        });

        canvas.setOnMouseDragged(e -> {
            double dx = e.getX() - prevX;
            double dy = e.getY() - prevY;
            xOffset += dx;
            yOffset += dy;
            prevX = e.getX();
            prevY = e.getY();
            drawGrid(gc);
        });

         canvas.setOnScroll((event) -> {
            double mouseX = event.getX();
            double mouseY = event.getY();

            double zoomFactor = event.getDeltaY() > 0 ? ZOOM_FACTOR : 1 / ZOOM_FACTOR;

            double newGridSize = gridSize * zoomFactor;

            if (newGridSize >= MIN_GRID_SIZE && newGridSize <= MAX_GRID_SIZE) {
                // Calculate the change in grid size
                double deltaGridSize = newGridSize - gridSize;

                xOffset -= mouseX * deltaGridSize / gridSize;
                yOffset -= mouseY * deltaGridSize / gridSize;

                gridSize = newGridSize;
            } else {
                gridSize = INITIAL_GRID_SIZE;
            }

            drawGrid(gc);
        });

        root.getChildren().add(canvas);
        primaryStage.setTitle("Graphing Calculator");
        primaryStage.setScene(scene);
        primaryStage.show();
     }

     private void drawGrid(GraphicsContext gc) {
        gc.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

        // Calculate zoomed center positions
        double centerX = CANVAS_WIDTH / 3.0 + xOffset;
        double centerY = CANVAS_HEIGHT / 7.0 + yOffset;

        // Calculate the distance between each highlighted line
        double highlightedLineDistanceX = gridSize * 5;
        double highlightedLineDistanceY = gridSize * 5;

        // Draw main grid lines
        gc.setStroke(Color.LIGHTGRAY);
        for (double x = (xOffset % gridSize + CANVAS_WIDTH) % gridSize; x < CANVAS_WIDTH; x +=      gridSize) {
            gc.strokeLine(x, 0, x, CANVAS_HEIGHT);
        }
        for (double y = (yOffset % gridSize + CANVAS_HEIGHT) % gridSize; y < CANVAS_HEIGHT; y += gridSize) {
            gc.strokeLine(0, y, CANVAS_WIDTH, y);
        }

        // Draw highlighted lines aligned with x-axis scale
        gc.setStroke(Color.rgb(128, 128, 128));
        for (double x = (xOffset % highlightedLineDistanceX + CANVAS_WIDTH) % highlightedLineDistanceX; x < CANVAS_WIDTH; x += highlightedLineDistanceX) {
            gc.strokeLine(x, 0, x, CANVAS_HEIGHT);
        }

        // Draw highlighted lines aligned with y-axis scale
        for (double y = (yOffset % highlightedLineDistanceY + CANVAS_HEIGHT) % highlightedLineDistanceY; y < CANVAS_HEIGHT; y += highlightedLineDistanceY) {
            gc.strokeLine(0, y, CANVAS_WIDTH, y);
        }

        // Draw x-axis and y-axis
        gc.setStroke(Color.BLACK);
        gc.strokeLine(0, centerY, CANVAS_WIDTH, centerY); // x-axis
        gc.strokeLine(centerX, 0, centerX, CANVAS_HEIGHT); // y-axis

        // Draw axis labels
        gc.setFill(Color.BLACK);
        gc.fillText("0", centerX + 5, centerY + 5); // Origin label
        gc.fillText("x", CANVAS_WIDTH - 10, centerY + 15); // x-axis label
        gc.fillText("y", centerX + 5, 15); // y-axis label

        // Calculate start and end scale positions based on yOffset and gridSize
        int startScaleY = (int) Math.ceil((-yOffset - CANVAS_HEIGHT / 7.0) / (highlightedLineDistanceY));
        int endScaleY = (int) Math.ceil((CANVAS_HEIGHT - yOffset - CANVAS_HEIGHT / 7.0) / (highlightedLineDistanceY));

        // Draw scale for y-axis
        for (int i = startScaleY; i <= endScaleY; i++) {
            double y = CANVAS_HEIGHT / 7.0 + highlightedLineDistanceY * i + yOffset;
            if (i != 0) {
                gc.strokeLine(centerX + 5, y, centerX - 5, y);
                gc.fillText(String.valueOf(i * -5), centerX - 30, y + 3);
            }

        }

        // Calculate start and end scale positions based on xOffset and gridSize
        int startScaleX = (int) Math.ceil((-xOffset - CANVAS_WIDTH / 3.0) / (highlightedLineDistanceX));
        int endScaleX = (int) Math.ceil((CANVAS_WIDTH - xOffset - CANVAS_WIDTH / 3.0) / (highlightedLineDistanceX));

        // Draw scale for x-axis
        for (int i = startScaleX; i <= endScaleX; i++) {
            double x = CANVAS_WIDTH / 3.0 + highlightedLineDistanceX * i + xOffset;
            if (i != 0) {
                gc.strokeLine(x, centerY - 5, x, centerY + 5);
                gc.fillText(String.valueOf(i * 5), x - 3, centerY + 20);
            }
        }
    }

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

我尝试根据当前缩放级别和偏移来计算它们的位置,以适应放大和缩小时发生的间隙。

javafx graphics2d graphing
1个回答
0
投票

(对我来说)理解你想要做的核心逻辑有点困难。 所以我尝试从头开始做同样的事情,坚持你遵循的相同原则。

我所依赖的关键逻辑是:

  • 始终以 100% 比例计算我的拖动偏移量
  • 计算我的中心 X 和 Y 相对于偏移量和比例
  • 计算第一条水平线和垂直线距左上角的偏移量

一旦掌握了以上因素,就很容易画出你喜欢的画布了。

请查看以下演示代码以供参考。我添加了一些条件功能,只是为了展示一旦具备上述三个因素,绘图的灵活性。令人信服的部分是我们不会在画布边界之外绘制任何内容;)

enter image description here

enter image description here

import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class GraphingCalModified extends Application {

    private static final int CANVAS_WIDTH = 900;
    private static final int CANVAS_HEIGHT = 700;
    private static final int INITIAL_GRID_SIZE = 20;
    private static final double MIN_GRID_SIZE = INITIAL_GRID_SIZE * 0.6;
    private static final double MAX_GRID_SIZE = INITIAL_GRID_SIZE / 0.6;
    private static final double SCALE_FACTOR = .1;
    private int gridSize = INITIAL_GRID_SIZE;
    private double prevX;
    private double prevY;

    private DoubleProperty scale = new SimpleDoubleProperty(1);
    private DoubleProperty xOffset = new SimpleDoubleProperty();
    private DoubleProperty yOffset = new SimpleDoubleProperty();

    private int mouseX, mouseY;

    private boolean showMousePos, showOverflowX, showOverflowY;

    int highlightedLineDistanceX = gridSize * 5;
    int highlightedLineDistanceY = gridSize * 5;

    @Override
    public void start(Stage primaryStage) {
        Label scaleLabel = new Label((scale.intValue() * 100) + "%");
        scaleLabel.setMouseTransparent(true);
        scaleLabel.setStyle("-fx-background-color:grey;-fx-opacity:.8;-fx-text-fill:white;-fx-background-radius:10px;-fx-padding:5px;");
        scale.addListener((obs, old, val) -> scaleLabel.setText((int) (val.doubleValue() * 100) + "%"));

        Canvas canvas = new Canvas(CANVAS_WIDTH, CANVAS_HEIGHT);
        GraphicsContext gc = canvas.getGraphicsContext2D();

        final CheckBox showMousePosition = new CheckBox("Show Mouse Position");
        showMousePosition.selectedProperty().addListener((obs, old, val) -> {
            showMousePos = val;
        });
        showMousePosition.setSelected(true);

        final CheckBox overflowX = new CheckBox("Show Overflow X axis");
        overflowX.selectedProperty().addListener((obs, old, val) -> {
            showOverflowX = val;
            drawGrid(gc);
        });
        overflowX.setSelected(true);

        final CheckBox overflowY = new CheckBox("Show Overflow Y axis");
        overflowY.selectedProperty().addListener((obs, old, val) -> {
            showOverflowY = val;
            drawGrid(gc);
        });
        overflowY.setSelected(true);

        HBox controls = new HBox(10, scaleLabel, showMousePosition, overflowX, overflowY);
        controls.setStyle("-fx-font-size:20px;");
        controls.setAlignment(Pos.CENTER_LEFT);

        StackPane canvasBox = new StackPane(canvas);
        canvasBox.setStyle("-fx-border-width:1px;-fx-border-color:lightgray;");
        VBox box = new VBox(10, controls, new Group(canvasBox));
        box.setAlignment(Pos.CENTER);
        box.setPadding(new Insets(10));

        drawGrid(gc);

        canvas.setOnMousePressed(e -> {
            prevX = e.getX();
            prevY = e.getY();
        });

        canvas.setOnMouseDragged(e -> {
            mouseX = (int) e.getX();
            mouseY = (int) e.getY();
            double dx = e.getX() - prevX;
            double dy = e.getY() - prevY;
            xOffset.set(xOffset.get() + (dx / scale.get()));
            yOffset.set(yOffset.get() + (dy / scale.get()));
            prevX = e.getX();
            prevY = e.getY();

            drawGrid(gc);
        });

        canvas.setOnScroll((event) -> {
            mouseX = (int) event.getX();
            mouseY = (int) event.getY();

            Point before = gridPointAtMouse();

            // Calculate the scale and the grid size
            double tempScale = event.getDeltaY() > 0 ? scale.get() + SCALE_FACTOR : scale.get() - SCALE_FACTOR;
            double newGridSize = INITIAL_GRID_SIZE * tempScale;
            if (newGridSize >= MIN_GRID_SIZE && newGridSize <= MAX_GRID_SIZE) {
                scale.set(tempScale);
                gridSize = (int) newGridSize;
            } else {
                gridSize = INITIAL_GRID_SIZE;
                scale.set(1);
            }

            // Calculate the distance between each highlighted line
            highlightedLineDistanceX = gridSize * 5;
            highlightedLineDistanceY = gridSize * 5;

            // Calculate the offset that need to be moved to ensure the mouse stays at same grid point.
            Point after = gridPointAtMouse();
            xOffset.set(xOffset.get() + (after.x - before.x) / scale.get());
            yOffset.set(yOffset.get() + (after.y - before.y) / scale.get());

            drawGrid(gc);
        });

        canvas.setOnMouseMoved(event -> {
            mouseX = (int) event.getX();
            mouseY = (int) event.getY();
            drawGrid(gc);
        });

        canvas.setOnMouseExited(event -> {
            mouseX = -1;
            mouseY = -1;
            drawGrid(gc);
        });

        primaryStage.setTitle("Graphing Calculator");
        primaryStage.setScene(new Scene(box));
        primaryStage.show();
    }

    private void drawGrid(GraphicsContext gc) {
        gc.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);

        // Calculate zoomed center position
        final Point zoomedCenter = zoomedCenter();
        int centerX = zoomedCenter.x;
        int centerY = zoomedCenter.y;

        boolean xVisible = true;
        boolean yVisible = true;

        // Calculate the first visible vertical line x offset
        int nx = Math.abs(centerX / gridSize);
        int startX;
        if (centerX < 0) {
            startX = centerX + (nx * gridSize);
            yVisible = false;
        } else {
            startX = centerX - (nx * gridSize);
            if (centerX > CANVAS_WIDTH) {
                yVisible = false;
            }
        }

        // Calculate the first visible horizontal line x offset
        int ny = Math.abs(centerY / gridSize);
        int startY;
        if (centerY < 0) {
            startY = centerY + (ny * gridSize);
            xVisible = false;
        } else {
            startY = centerY - (ny * gridSize);
            if (centerY > CANVAS_HEIGHT) {
                xVisible = false;
            }
        }

        // Draw all vertical lines
        for (int i = startX; i <= CANVAS_WIDTH; i = i + gridSize) {
            if (i == centerX) {
                // Don't draw the y axis, we will draw this on top of all lines.
                continue;
            } else if (Math.abs(i - centerX) % highlightedLineDistanceX == 0) {
                gc.setStroke(Color.rgb(128, 128, 128));
            } else {
                gc.setStroke(Color.LIGHTGRAY);
            }
            gc.strokeLine(i, 0, i, CANVAS_HEIGHT);
        }

        // Draw all horizontal lines
        for (int i = startY; i <= CANVAS_HEIGHT; i = i + gridSize) {
            if (i == centerY) {
                // Don't draw the x axis, we will draw this on top of all lines.
                continue;
            } else if (Math.abs(i - centerY) % highlightedLineDistanceY == 0) {
                gc.setStroke(Color.rgb(128, 128, 128));
            } else {
                gc.setStroke(Color.LIGHTGRAY);
            }
            gc.strokeLine(0, i, CANVAS_WIDTH, i);
        }

        // Draw x-axis and y-axis
        gc.setStroke(Color.BLACK);
        if (xVisible) {
            gc.strokeLine(0, centerY, CANVAS_WIDTH, centerY); // x-axis
        }
        if (yVisible) {
            gc.strokeLine(centerX, 0, centerX, CANVAS_HEIGHT); // y-axis
        }

        // Draw labels
        gc.setFill(Color.BLACK);
        gc.fillText("0", centerX + 5, centerY + 15); // Origin label
        gc.fillText("x", CANVAS_WIDTH - 10, centerY + 15); // x-axis label
        gc.fillText("y", centerX + 5, 15); // y-axis label

        // Draw labels on x axis
        for (int i = startX; i <= CANVAS_WIDTH; i = i + gridSize) {
            if (Math.abs(i - centerX) % highlightedLineDistanceX == 0 && i != centerX) {
                String val = String.valueOf((i - centerX) / highlightedLineDistanceX);
                if (xVisible) {
                    // Labels on X axis
                    gc.strokeLine(i, centerY - 5, i, centerY + 5);
                    gc.fillText(val, i - 3, centerY + 20);
                } else if (showOverflowX) {
                    // Labels on top edge
                    gc.strokeLine(i, 0, i, 5);
                    gc.fillText(val, i - 3, 20);

                    // Labels on bottom edge
                    gc.strokeLine(i, CANVAS_HEIGHT - 5, i, CANVAS_HEIGHT);
                    gc.fillText(val, i - 3, CANVAS_HEIGHT - 10);
                }
            }
        }

        // Draw labels on y axis
        for (int i = startY; i <= CANVAS_HEIGHT; i = i + gridSize) {
            if (Math.abs(i - centerY) % highlightedLineDistanceY == 0 && i != centerY) {
                String val = String.valueOf((i - centerY) / highlightedLineDistanceY);
                if (yVisible) {
                    // Labels on Y axis
                    gc.strokeLine(centerX - 5, i, centerX + 5, i);
                    gc.fillText(val, centerX + 10, i);
                } else if (showOverflowY) {
                    // Labels on left edge
                    gc.strokeLine(0, i, 5, i);
                    gc.fillText(val, 10, i);

                    // Labels on right edge
                    gc.strokeLine(CANVAS_WIDTH - 5, i, CANVAS_WIDTH, i);
                    gc.fillText(val, CANVAS_WIDTH - 20, i);
                }
            }
        }

        // Draw mouse position on grid
        if (mouseX > 0 && showMousePos) {
            gc.setFill(Color.RED);
            int realX = (int) ((centerX - mouseX) / scale.get()) * -1;
            int realY = (int) ((centerY - mouseY) / scale.get()) * -1;
            gc.fillText(realX + ", " + realY, mouseX + 20, mouseY + 20);
        }
    }

    private Point zoomedCenter() {
        return new Point((int) (CANVAS_WIDTH / 3.0 + (xOffset.get() * scale.get())),
                (int) (CANVAS_HEIGHT / 7.0 + (yOffset.get() * scale.get())));
    }

    private Point gridPointAtMouse() {
        Point center = zoomedCenter();
        int realX = (int) ((center.x - mouseX) / scale.get()) * -1;
        int realY = (int) ((center.y - mouseY) / scale.get()) * -1;
        return new Point(realX, realY);
    }

    record Point(int x, int y) {
    }

    public static void main(String[] args) {
        launch(args);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.