如何在代码中在矩形前面渲染线条?

Dim*_*nik 4 java javafx

我是 javafx 新手,在制作 GridTiles16 网格时遇到问题。这就是我制作tiles16网格的方法:

private static void makeGrid(Group root, int gridSize, ArrayList<String> grid, GridTile16[][] gridTiles, Scene scene) {
    for (int i = 0; i < grid.size(); i++) {
        for (int j = 0; j < grid.get(i).length(); j++) {
            GridTile16 newGridTile = new GridTile16(i * gridSize, j * gridSize, gridSize, grid.get(i).charAt(j));
            root.getChildren().add(newGridTile);
            gridTiles[i][j] = newGridTile;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

GridTiles 类:

import javafx.scene.Group;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;

public class GridTile extends Group {

    protected int x;
    protected int y;
    protected int size;
    protected Rectangle rectangle;

    public GridTile(int x, int y, int size) {
        this.x = x;
        this.y = y;
        this.size = size;
        Rectangle rectangle = new Rectangle(x, y, size, size);
        rectangle.setFill(Color.BLACK);
        rectangle.setStroke(Color.WHITE);
        this.getChildren().add(rectangle);
        this.rectangle = rectangle;
    }

    public void resetColor() {
        this.rectangle.setFill(Color.BLACK);
    }

    public int getX() {
        return y/size;
    }

    public int getY() {
        return x/size;
    }
}
Run Code Online (Sandbox Code Playgroud)
import com.programs.GridTile;

import javafx.scene.paint.Color;
import javafx.scene.shape.Line;

public class GridTile16 extends GridTile {

    private boolean[] isFromLeftRightTopBot = new boolean[4];

    public GridTile16(int y, int x, int size, char c) {
        super(x, y , size);
        if (c != '.') {
            Line line = null;
            switch (c) {
                case '-':
                    line = new Line(x, y + size / 2, x + size, y + size / 2);
                    break;
                case '|':
                    line = new Line(x + size / 2, y, x + size / 2, y + size);
                    break;
                case '/':
                    line = new Line(x, y + size, x + size, y);
                    break;
                case '\\':
                    line = new Line(x, y, x + size, y + size);
                    break;
            }
            line.setStroke(Color.WHITE);
            line.setStrokeWidth(5);
            this.getChildren().add(line);
        }
    }

    public boolean getIsFromDirection(int direction) {
        return this.isFromLeftRightTopBot[direction];
    }

    public void setIsFromDirection(int direction) {
        this.isFromLeftRightTopBot[direction] = true;
        this.rectangle.setFill(Color.BLUE);
        Line newLine = null;
        if (direction == 0) {
            newLine = new Line(this.x, this.y + this.size / 2, this.x + this.size / 2, this.y + this.size / 2);
        } else if (direction == 1) {
            newLine = new Line(this.x + this.size, this.y + this.size / 2, this.x + this.size / 2, this.y + this.size / 2);
        } else if (direction == 2) {
            newLine = new Line(this.x + this.size / 2, this.y, this.x + this.size / 2, this.y + this.size / 2);
        } else if (direction == 3) {
            newLine = new Line(this.x + this.size / 2, this.y + this.size, this.x + this.size / 2, this.y + this.size / 2);
        }
        newLine.setStrokeWidth(2);
        newLine.setStroke(Color.RED);
        this.getChildren().add(newLine);
    }
}
Run Code Online (Sandbox Code Playgroud)

制作完所有瓷砖后,我得到一个像这样的网格:

网格

当我突出显示某些区域时,我希望线条部分突出一点,但这只有在下一个图块的矩形之前生成线条时才有可能。是否有一种更简单的方法来渲染所有矩形前面的所有线条,或者是否可以先制作一个矩形网格(没有线条的网格图块),然后添加线条。

我尝试解决问题和.toFront()功能.toBack(),我认为这些功能仅适用于当前图块,这已经很好了,因为线条是在矩形之前生成的。我还尝试修复它,.viewOrderProperty().set(<0/1>)它具有相同的问题,即它仅适用于直接父项,而不适用于父项的父项。

程序输入:

.|...\....
|.-.\.....
.....|-...
........|.
..........
.........\
..../.\\..
.-.-/..|..
.|....-|.\
..//.|....
Run Code Online (Sandbox Code Playgroud)

主要程序:

public class App extends Application {

    private static final int gridSize = 40;
    private static double zoomFactor = 1;

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

    @Override
    public void start(Stage stage) throws FileNotFoundException {

        Group root = new Group();
        Scene scene = new Scene(root, 400, 400, Color.BLACK);
        stage.setFullScreen(true);
        stage.setFullScreenExitHint("");
        stage.setTitle("Visualisation");
        stage.setScene(scene);
        stage.show();

        scene.widthProperty().addListener((obs, oldVal, newVal) -> {
            resetSize(scene, root);
        });
       
        scene.heightProperty().addListener((obs, oldVal, newVal) -> {
            resetSize(scene, root);
        });

        File file = new File(<INPUT>);
        Scanner scanner = new Scanner(file);
        ArrayList<String> grid = new ArrayList<>();
        while (scanner.hasNextLine()) {
            grid.add(scanner.nextLine());
        }
        scanner.close();
        GridTile16[][] gridTiles = new GridTile16[grid.size()][grid.get(0).length()];
        makeGrid(root, gridSize, grid, gridTiles, scene);

        resetSize(scene, root);
    }

    private static void resetSize(Scene scene, Group root) {
        root.setLayoutX((scene.getWidth() - root.prefWidth(-1)) / 2);
        root.setLayoutY((scene.getHeight() - root.prefHeight(-1)) / 2);

        double scaleX = scene.getWidth() / root.getBoundsInLocal().getWidth();
        double scaleY = scene.getHeight() / root.getBoundsInLocal().getHeight();
        zoomFactor = Math.min(scaleX, scaleY) - 0.01;
        updateTransforms(root);
    }

    private static void updateTransforms(Group root) {
        root.setScaleX(zoomFactor);
        root.setScaleY(zoomFactor);
    }
}

Run Code Online (Sandbox Code Playgroud)

jew*_*sea 5

如果只有两种颜色,则无需修改绘制顺序

您问题中的初始示例只有两种颜色(黑色和白色)。

在这种情况下,您无需担心内容的顺序。

场景中的所有内容都只是黑色背景上的白线。场景的背景已经是黑色的。在网格中创建矩形时,不要用黑色填充它们。目前,矩形的黑色部分会覆盖白线,从而模糊它们。如果矩形没有黑色部分,则不会发生这种情况。矩形边框上的白色描边仍会绘制在白线上,但白色上的白色仍然是白色,所以这并不重要。

在你的班级中替换它GridTile

rectangle.setFill(Color.BLACK);
Run Code Online (Sandbox Code Playgroud)

有了这个:

rectangle.setFill(null);
Run Code Online (Sandbox Code Playgroud)

结果是您寻求的输出:

线

关于绘画顺序的说明

事实证明,在这种情况下,绘图顺序是无关紧要的,使得有关如何设置绘图顺序的问题标题成为 xy问题

对于另一个问题,顺序可能实际上是相关的。提问者指出,即使是他们实际的全面应用也是如此。

从概念上讲,JavaFX 使用画家算法。链接文章中的讨论讨论了处理使用此类算法时可能出现的绘制顺序问题的各种方法(以一般方式,没有 JavaFX 实现细节)。

修改油漆顺序的解决方案

如果这些矩形的颜色与背景颜色不同,我将如何解决我的问题?因为在我的程序制作网格后,它会改变矩形和其他一些东西的颜色,并覆盖线条的突出部分。

潜在的解决方案:

  1. 将所有项目放在单个父项下,最后绘制的项目在子列表中排在最后。
  2. 将所有项目放置在单个父项下,最后绘制的项目分配的视图顺序高于其他项目。
  3. 使用多个父项,逐层堆叠在一起,最后绘制的项目位于最高层。
  4. 打开场景中的3D深度缓冲,并指定要放置在其他项目之上的项目(z 顺序),该顺序更靠近观看者。

我们以 3D 深度缓冲技术为例。它是一种与标准画家算法不同的处理 3D 排序的方式,它在某些方面类似于画家算法,但有些不同。好处是,它只是您设置的一个开关,实现细节全部由 JavaFX 和图形系统处理,因此您无需做太多操作即可使用它。

维基百科关于画家算法的文章描述了这样的情况:

画家算法的缺陷导致了Z缓冲技术的发展,该技术可以被视为画家算法的发展,通过逐像素地解决深度冲突,减少对基于深度的渲染顺序的需要。

创建场景时,将深度缓冲区设置为 true:

Scene scene = new Scene(root, 400, 400, true);
scene.setFill(Color.BLACK);
Run Code Online (Sandbox Code Playgroud)

绘制线条时,将它们平移到靠近观察者的位置:

line.setTranslateZ(-1);
Run Code Online (Sandbox Code Playgroud)

对于此示例,我还将线条绘制为不同的颜色(绿色),因此很明显它们完全覆盖了底层网格方块及其边框。

绿线

啊,但如果你仔细观察的话,现在我们仍然有一个问题。提供的图像是高DPI屏幕的屏幕截图,因此实际程序在屏幕上运行时不存在任何肉眼容易看到的缺陷。但是,当像此处的图像一样捕获屏幕并放大时,您可以看到有时会出现一个黑色间隙,其中深度缓冲一直在工作并且颜色在单个像素上混合。这是因为场景中的抗锯齿功能已关闭。因此,为了获得更好的结果,我们可以打开抗锯齿功能。

Scene scene = new Scene(root, 400, 400, true, SceneAntialiasing.BALANCED);
Run Code Online (Sandbox Code Playgroud)

然后我们得到这个结果,如果你仔细观察的话,它(也许)看起来更好。。。

别名绿线

但(对我来说)它看起来仍然不像你使用我上面建议的其他技术之一那样整洁(这让我感到惊讶)。因此,根据您的需要,您可以使用这种深度缓冲技术(这非常简单),或者您可以尝试其他技术之一(我目前不会尝试实现或更详细地解释)。

常问问题

由于这在使用更大的输入调整窗口大小时给我带来了更多的滞后,我将尝试实现您提供的其他建议。感谢您抽出时间回答我的问题。

深度缓冲比没有更密集。它在性能更高的图形加速器上效率更高,因此性能取决于您的硬件。也就是说,如今即使是最普通的图形硬件也能够处理大量数据。

如果您遇到性能问题,问题很可能是您编写低效的代码而不是其他任何问题。深度缓冲开关可能只会加剧您的性能问题,而不是开关成为性能问题的根本原因,因为开关在其他情况下表现良好。

有关性能的进一步讨论,请参阅此后续问题的答案中的文章:

一个有趣的发现是,当场景中存在大量弧形节点(例如数万个)时,当您设置任意节点的 z 平移值时,性能会变慢(在测试示例中约为 3 倍)即使您没有启用深度缓冲区,节点也会设置为非零值。我不知道为什么会出现这种情况,但在优化和分析性能时可能需要记住这一点。启用深度缓冲本身并没有对我的系统产生任何可测量的差异,但要使其按照您想要的方式运行,您需要调整节点的 z 值,当您有数万个弧时,这确实会对性能产生可测量的影响。要绘制的样式节点。

后续问题的答案提供了一个使用多个路径而不是多个节点进行渲染的示例。将一条路径放置在另一条路径之上以正确设置渲染顺序,而不是调整 z 平移值。具有路径的最小节点方法比使用具有调整的 z 平移值的许多节点的方法表现得更好。一旦基于节点渲染的解决方案扩展到数万个节点,性能就会提高几个数量级。分层路径解决方案不存在使用深度缓冲区的 z 平移调整解决方案中示例的小视觉异常。