【问题标题】:Possible JavaFX Canvas Bug when rendering lots of paths?渲染大量路径时可能出现 JavaFX Canvas 错误?
【发布时间】:2016-03-06 17:25:28
【问题描述】:

我正在尝试做一些 GIS 工作。我需要能够渲染大量信息(例如,130 万个三角形,加上每个三角形内渲染的图形)。我在 JavaFX 下测试了保留模式图形,但在这种规模下它的性能不够好,所以我尝试了画布。

下面的代码创建了一个平面二十面体(20 面图形)并递归地将其细分为越来越小的三角形。

在递归级别 0 到 4(GIS.java 的第 36 行),一切正常。但是,一旦我达到 5 个或更多,我开始看到非常奇怪的画布渲染。该图形被渲染到 y 轴的右侧(最左边的三角形 x=0),并且似乎在很大的部分中覆盖了自身。

我将此代码音译为 .Net,它运行良好(就像它在 Java 中的递归 0 到 4 一样),所以我不认为这是一个逻辑/算法问题。

提前为代码的长度道歉。我尽可能地删除了所有内容,但它仍然有相当多的代码。

有两个文件 - GIS.java 是主窗体,HexGrid.java 是创建网格并将其呈现到画布上的类。

还有一个有趣的注释:HexGrid.java 的第 98 到 105 行画了红线来显示 x 轴和 y 轴。我试图查看画布认为轴在哪里,因为渲染远远超出预期的位置。如果您取消注释第 105 行的 gc.stroke() 并使用递归 4 或更高版本,这也会炸毁画布,并且渲染与预期的相差甚远。在 3 次或更低的递归中,一切都按预期执行。

我希望我只是做错了什么!我真的希望在这个项目中坚持使用 JavaFX。

提前致谢。

********** GIS.java *******************

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class GIS extends Application{
    Pane drawPane;


    @Override
    public void start(Stage primaryStage) throws Exception {
        BorderPane root = new BorderPane();
        Scene primaryScene = new Scene(root, 900, 800);
        primaryStage.setScene(primaryScene);
        primaryStage.setWidth(1000);
        primaryStage.setHeight(700);
        primaryStage.minWidthProperty().setValue(1000);
        primaryStage.minHeightProperty().setValue(700);

        drawPane = new Pane();
        drawPane.setId("drawPane");
        drawPane.setPrefSize(1000, 800);
        drawPane.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        drawPane.setBackground(new Background(new BackgroundFill(Color.ALICEBLUE, CornerRadii.EMPTY, Insets.EMPTY)));
        root.setCenter(drawPane);

        HexGrid hexGrid = new HexGrid();

        //LEVEL OF RECURSION
        hexGrid.generate(5);

        drawWorldCanvas(hexGrid);
        primaryStage.show();

    }

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

    }

    private void drawWorldCanvas(HexGrid hexGrid) {
        drawPane.getChildren().add(hexGrid.getCanvas());
    }
}

***************** HexGrid.java *******************

import javafx.geometry.Point2D;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;

import java.io.*;
import java.time.LocalDateTime;
import java.util.ArrayList;

public class HexGrid {
    ArrayList<Point2D> _vertices = new ArrayList<>(1000);
    int _index = 0;
    FileWriter log;

    int TRIANGLE_SIDE_LENGTH = 250;

    private class TriangleFaceVertices {
        public int v1;  //these are indexes into the _vertices arrayList
        public int v2;
        public int v3;

        private TriangleFaceVertices(int v1, int v2, int v3) {
            this.v1 = v1;
            this.v2 = v2;
            this.v3 = v3;
        }
    }

    ArrayList<TriangleFaceVertices> _faces = new ArrayList<>(1000);

    public  HexGrid() {
        try {
            log = new FileWriter("log1.txt");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public void generate(int recursionLevel) {
        _vertices.clear();
        _faces.clear();

        addVertices();
        addFaces();
        divideTriangles(recursionLevel);
    }

    private void divideTriangles(int recursionLevel) {
        // refine triangles
        for (int i = 0; i < recursionLevel; i++)
        {
            ArrayList<TriangleFaceVertices> faces2 = new ArrayList<>(200000);
            for (TriangleFaceVertices face: _faces)
            {
                // replace triangle by 4 triangles
                int a = getMiddlePoint(face.v1, face.v2);
                int b = getMiddlePoint(face.v2, face.v3);
                int c = getMiddlePoint(face.v3, face.v1);

                faces2.add(new TriangleFaceVertices(face.v1, a, c));
                faces2.add(new TriangleFaceVertices(face.v2, b, a));
                faces2.add(new TriangleFaceVertices(face.v3, c, b));
                faces2.add(new TriangleFaceVertices(a, b, c));
            }
            _faces = faces2;
            System.out.println(LocalDateTime.now() + " : Iteration: " + i + " - Triangle Count: " + faces2.size());
        }
    }

    private int getMiddlePoint(int v1, int v2)
    {
        Point2D point1 = _vertices.get(v1);
        Point2D point2 = _vertices.get(v2);
        Point2D middle = new Point2D(
                (point1.getX() + point2.getX()) / 2.0,
                (point1.getY() + point2.getY()) / 2.0);

        return addVertex(middle);
    }

    private int addVertex(Point2D p)
    {
        _vertices.add(p);
        return _index++;
    }

    public Canvas getCanvas() {
        Canvas canvas = new Canvas(5000,2000);
        GraphicsContext gc = canvas.getGraphicsContext2D();

        Color landColor = Color.BEIGE;
        Color borderColor = Color.DARKKHAKI;
        gc.setFill(landColor);
        gc.setStroke(borderColor);

        gc.beginPath();
        gc.moveTo(0,0);
        gc.lineTo(2000,0);
        gc.closePath();
        //gc.stroke();

        for (TriangleFaceVertices face:_faces) {
            gc.beginPath();
            gc.moveTo(_vertices.get(face.v1).getX(), _vertices.get(face.v1).getY());
            gc.lineTo(_vertices.get(face.v2).getX(), _vertices.get(face.v2).getY());
            gc.lineTo(_vertices.get(face.v3).getX(), _vertices.get(face.v3).getY());
            gc.lineTo(_vertices.get(face.v1).getX(), _vertices.get(face.v1).getY());
            gc.closePath();
            gc.stroke();
            //gc.fill();
        }
        return canvas;
    }

    private void addFaces() {
        _faces.add(new TriangleFaceVertices(0, 2, 1)); //creates a triangle from vertices[0], [1], and [2]
        _faces.add(new TriangleFaceVertices(1, 2, 3)); 
        _faces.add(new TriangleFaceVertices(1, 3, 4));
        _faces.add(new TriangleFaceVertices(3, 5, 4));
        _faces.add(new TriangleFaceVertices(1, 7, 6));
        _faces.add(new TriangleFaceVertices(1, 4, 7));
        _faces.add(new TriangleFaceVertices(7, 4, 8));
        _faces.add(new TriangleFaceVertices(4, 9, 8));
        _faces.add(new TriangleFaceVertices(10, 7, 11));
        _faces.add(new TriangleFaceVertices(7, 8, 11));
        _faces.add(new TriangleFaceVertices(11, 8, 12));
        _faces.add(new TriangleFaceVertices(8, 13, 12));
        _faces.add(new TriangleFaceVertices(14, 11, 15));
        _faces.add(new TriangleFaceVertices(11, 12, 15));
        _faces.add(new TriangleFaceVertices(15, 12, 16));
        _faces.add(new TriangleFaceVertices(12, 17, 16));
        _faces.add(new TriangleFaceVertices(18, 15, 19));
        _faces.add(new TriangleFaceVertices(15, 16, 19));
        _faces.add(new TriangleFaceVertices(19, 16, 20));
        _faces.add(new TriangleFaceVertices(16, 21, 20));
    }

    private void addVertices(){
        float height = (float)(Math.sqrt(3.0) / 2.0) * TRIANGLE_SIDE_LENGTH;

        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH / 2.0, 0));              //0
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH, height));               //1
        addVertex(new Point2D(0, height));                                  //2
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH / 2.0, height * 2.0));   //3
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH * 1.5, height * 2.0));   //4
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH, height * 3.0));         //5
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH * 1.5, 0));              //6
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH * 2.0, height));         //7
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH * 2.5, height * 2.0));   //8
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH * 2.0, height * 3.0));   //9
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH * 2.5, 0));              //10
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH * 3.0, height));         //11
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH * 3.5, height * 2.0));   //12
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH * 3.0, height * 3.0));   //13
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH * 3.5, 0));              //14
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH * 4.0, height));         //15
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH * 4.5, height * 2.0));   //16
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH * 4.0, height * 3.0));   //17
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH * 4.5, 0));              //18
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH * 5.0, height));         //19
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH * 5.5, height * 2.0));   //20
        addVertex(new Point2D(TRIANGLE_SIDE_LENGTH * 5.0, height * 3.0));   //21

    }

}

【问题讨论】:

  • 您没有告诉我们您在哪个系统和哪个 Java 版本上获得了结果。我在带有 JDK 8u76 的 MacBook Pro Retina 上进行了尝试,但没有发现任何奇怪的行为。我只是想知道你为什么需要这么大的画布。在视网膜 Mac 上,这会产生 10000x4000 像素的图像。但无论如何,它似乎仍然有效。我升到了 8 级。
  • 有趣。它在我的 Windows 10 机器(JDK 8u72)和 MacBook Air(JDK 8u74)上都失败,输出相同(不正确)。输出顶部是否有五个三角形尖端?应该可以,但我的错误版本只显示了三个。
  • 将我的 windows 机器更新到 8u74 并得到相同的结果。
  • 我在顶部有五个提示,适用于所有级别。也许我应该注意我的机器中有 NVidia 卡,而不仅仅是 Intel 显卡。

标签: canvas javafx


【解决方案1】:

似乎确实有问题。您应该提交错误报告。

关于您的问题:不要使用路径,而是使用 e。 G。 strokeLine。无论如何,它的速度要快得多。

复制的简单示例:

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.paint.Color;
import javafx.stage.Stage;

public class Main extends Application {

    double width = 1000;
    double height = 1000;

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

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("Drawing Operations Test");
        Group root = new Group();
        Canvas canvas = new Canvas(width, height);
        GraphicsContext gc = canvas.getGraphicsContext2D();
        drawShapes(gc);
        root.getChildren().add(canvas);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    private void drawShapes(GraphicsContext gc) {

        double offset = 15; // <=== change this, e. g. 12

        gc.setStroke(Color.BLACK);
        gc.setFill(Color.BEIGE);

        for( int x=0; x < width; x+=offset) {
            for( int y=0; y < height; y+=offset) {

                gc.setLineWidth(0.5);

                gc.beginPath();
                gc.moveTo(x, y);
                gc.lineTo(x+offset, y+offset);
                gc.closePath();

                gc.stroke();
            }

        }

    }
}

你应该得到对角线。

一旦你降低偏移量,线条就会消失。

【讨论】:

  • 我从 16 下降到 2,递减 2,并且在我的机器上没有看到任何奇怪的行为。
  • 现在在另一台机器上试过了。 Win7 和 Java 1.8.u40 一切都很好。在我尝试的第一台机器上遇到了 Win7 和 Java 1.8.u60 的问题。问题中的代码也适用于另一台机器。这真的很奇怪。
  • 出现问题的机器有什么共同点?
  • 感谢您的简化示例。令人印象深刻的是,您可以这样概括我的问题。我确认我有任何低于 15 的偏移量的渲染问题。我将提交一份 JavaFX 错误报告,然后开始研究 StrokeLine。我是 JavaFX 新手,从未听说过。
  • 作为后续,转换为 StrokeLine 效果很好。再次感谢!
猜你喜欢
  • 2019-10-29
  • 1970-01-01
  • 2014-06-21
  • 2012-06-11
  • 2012-05-13
  • 2012-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多