只绘制应该在屏幕上的节点?
Draw only nodes that should be on screen?
我正在使用场景图(不是 canvas)在 JavaFX 中进行模拟,但在屏幕上仅绘制我需要的内容时遇到了问题。
这个模拟中有超过 1000 万个节点,但用户只需要同时在屏幕上看到这些节点的一小部分(最多 160,000 个节点)。我关心的所有节点都是 400x400 ImageViews
.
每个节点都是 Group
(节点块)的成员,该块包含大约 40,000 个节点,因此一次需要显示其中的 4 个或更少 'node chunks'。为了显示这些 'node chunks',它们被添加到静态 Pane
并且该窗格位于根节点中,即 Group
.
所以我的图表从第一个 parent 到最后一个 child 看起来是这样的:
根节点Group
\显示Pane
\(许多)节点块Group
\<= 40,000 ImageViews
由于显示窗格根据用户输入不断移动(平移和重新缩放),而且节点太多,应用程序没有达到我想要的速度 运行。 JavaFX 无法同时跟踪 1000 万多个节点是有道理的,所以我的解决方案是从显示窗格中删除所有 'node chunks';将它们保存在哈希图中,直到我需要绘制它们为止。
每个 'node chunk' 都已将其 LayoutX
和 LayoutY
设置为在网格中均匀分布在显示窗格中,如下所示:
在这个例子中,我需要抓取并显示 'node chunk' 7、8、12 和 13,因为这是用户看到的。
这是手动添加了 'node chunk' 0 的屏幕截图。黄绿色是放置 'node chunks' 1、5 和 6 的位置。
我的问题是:由于 'node chunks' 在需要时才添加到显示窗格中,因此我无法参考用户看到的显示窗格不断变化的部分的布局边界,所以我不知道需要显示哪个'node chunks'。
有没有简单的方法可以解决这个问题?还是我走错了路? (或两者)谢谢。
注意:这不是您确切问题的答案。
我不知道为每个图像创建 ImageView 的原因是什么。相反,我会为每个 nodeChunk(160000 张图像)绘制一张图像并为此 nodeChunk 创建一个 ImageView。
在功能上我看不出这两种方法有什么区别(你可以让我知道你为什么要使用每个图像视图)。但从性能来看,这种方法非常快速和流畅。
请在下面找到我所说的演示。
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
/**
* Combining 4,000,000 images
*/
public class ImageLoadingDemo extends Application {
SecureRandom rnd = new SecureRandom();
// I created 5 images of dimension 1 x 1 each with new color
String[] images = {"1.png", "2.png", "3.png", "4.png", "5.png"};
Map<Integer, BufferedImage> buffImages = new HashMap<>();
Map<Integer, Image> fxImages = new HashMap<>();
@Override
public void start(Stage primaryStage) throws IOException {
ScrollPane root = new ScrollPane();
root.setPannable(true);
Scene scene = new Scene(root, 800, 800);
primaryStage.setScene(scene);
primaryStage.show();
root.setContent(approach1()); // Fast & smooth rendering
// Your approach of creating ImageViews
// root.setContent(approach2()); // Very very very very very slow rendering
}
private Node approach1(){
// Creating 5 x 5 nodeChunk grid
GridPane grid = new GridPane();
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
ImageView nodeChunk = new ImageView(SwingFXUtils.toFXImage(createChunk(), null));
grid.add(nodeChunk, i, j);
}
}
return grid;
}
private BufferedImage createChunk() {
// Combining 160000 1px images into a single image of 400 x 400
BufferedImage chunck = new BufferedImage(400, 400, BufferedImage.TYPE_INT_ARGB);
Graphics2D paint;
paint = chunck.createGraphics();
paint.setPaint(Color.WHITE);
paint.fillRect(0, 0, chunck.getWidth(), chunck.getHeight());
paint.setBackground(Color.WHITE);
for (int i = 0; i < 400; i++) {
for (int j = 0; j < 400; j++) {
int index = rnd.nextInt(5); // Picking a random image
BufferedImage buffImage = buffImages.get(index);
if (buffImage == null) {
Image image = new Image(ImageLoadingDemo.class.getResourceAsStream(images[index]));
buffImages.put(index, SwingFXUtils.fromFXImage(image, null));
}
paint.drawImage(buffImage, i, j, null);
}
}
return chunck;
}
private Node approach2(){
GridPane grid = new GridPane();
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
grid.add(createGroup(), i, j);
}
}
return grid;
}
private Group createGroup() {
GridPane grid = new GridPane();
for (int i = 0; i < 400; i++) {
for (int j = 0; j < 400; j++) {
int index = rnd.nextInt(5);
Image fxImage = fxImages.get(index);
if (fxImage == null) {
fxImage = new Image(ImageLoadingDemo.class.getResourceAsStream(images[index]));
fxImages.put(index, fxImage);
}
grid.add(new ImageView(fxImage), i, j);
}
}
return new Group(grid);
}
public static void main(String[] args) {
Application.launch(args);
}
}
我最终为每个 'node chunk' 创建了一个矩形,将不透明度设置为零,然后检查是否与用户看到的显示窗格部分发生冲突。当与矩形发生碰撞时,相应的 'node chunk' 将添加到显示窗格中。
我正在使用场景图(不是 canvas)在 JavaFX 中进行模拟,但在屏幕上仅绘制我需要的内容时遇到了问题。
这个模拟中有超过 1000 万个节点,但用户只需要同时在屏幕上看到这些节点的一小部分(最多 160,000 个节点)。我关心的所有节点都是 400x400 ImageViews
.
每个节点都是 Group
(节点块)的成员,该块包含大约 40,000 个节点,因此一次需要显示其中的 4 个或更少 'node chunks'。为了显示这些 'node chunks',它们被添加到静态 Pane
并且该窗格位于根节点中,即 Group
.
所以我的图表从第一个 parent 到最后一个 child 看起来是这样的:
根节点Group
\显示Pane
\(许多)节点块Group
\<= 40,000 ImageViews
由于显示窗格根据用户输入不断移动(平移和重新缩放),而且节点太多,应用程序没有达到我想要的速度 运行。 JavaFX 无法同时跟踪 1000 万多个节点是有道理的,所以我的解决方案是从显示窗格中删除所有 'node chunks';将它们保存在哈希图中,直到我需要绘制它们为止。
每个 'node chunk' 都已将其 LayoutX
和 LayoutY
设置为在网格中均匀分布在显示窗格中,如下所示:
在这个例子中,我需要抓取并显示 'node chunk' 7、8、12 和 13,因为这是用户看到的。
这是手动添加了 'node chunk' 0 的屏幕截图。黄绿色是放置 'node chunks' 1、5 和 6 的位置。
我的问题是:由于 'node chunks' 在需要时才添加到显示窗格中,因此我无法参考用户看到的显示窗格不断变化的部分的布局边界,所以我不知道需要显示哪个'node chunks'。
有没有简单的方法可以解决这个问题?还是我走错了路? (或两者)谢谢。
注意:这不是您确切问题的答案。
我不知道为每个图像创建 ImageView 的原因是什么。相反,我会为每个 nodeChunk(160000 张图像)绘制一张图像并为此 nodeChunk 创建一个 ImageView。
在功能上我看不出这两种方法有什么区别(你可以让我知道你为什么要使用每个图像视图)。但从性能来看,这种方法非常快速和流畅。
请在下面找到我所说的演示。
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
/**
* Combining 4,000,000 images
*/
public class ImageLoadingDemo extends Application {
SecureRandom rnd = new SecureRandom();
// I created 5 images of dimension 1 x 1 each with new color
String[] images = {"1.png", "2.png", "3.png", "4.png", "5.png"};
Map<Integer, BufferedImage> buffImages = new HashMap<>();
Map<Integer, Image> fxImages = new HashMap<>();
@Override
public void start(Stage primaryStage) throws IOException {
ScrollPane root = new ScrollPane();
root.setPannable(true);
Scene scene = new Scene(root, 800, 800);
primaryStage.setScene(scene);
primaryStage.show();
root.setContent(approach1()); // Fast & smooth rendering
// Your approach of creating ImageViews
// root.setContent(approach2()); // Very very very very very slow rendering
}
private Node approach1(){
// Creating 5 x 5 nodeChunk grid
GridPane grid = new GridPane();
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
ImageView nodeChunk = new ImageView(SwingFXUtils.toFXImage(createChunk(), null));
grid.add(nodeChunk, i, j);
}
}
return grid;
}
private BufferedImage createChunk() {
// Combining 160000 1px images into a single image of 400 x 400
BufferedImage chunck = new BufferedImage(400, 400, BufferedImage.TYPE_INT_ARGB);
Graphics2D paint;
paint = chunck.createGraphics();
paint.setPaint(Color.WHITE);
paint.fillRect(0, 0, chunck.getWidth(), chunck.getHeight());
paint.setBackground(Color.WHITE);
for (int i = 0; i < 400; i++) {
for (int j = 0; j < 400; j++) {
int index = rnd.nextInt(5); // Picking a random image
BufferedImage buffImage = buffImages.get(index);
if (buffImage == null) {
Image image = new Image(ImageLoadingDemo.class.getResourceAsStream(images[index]));
buffImages.put(index, SwingFXUtils.fromFXImage(image, null));
}
paint.drawImage(buffImage, i, j, null);
}
}
return chunck;
}
private Node approach2(){
GridPane grid = new GridPane();
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
grid.add(createGroup(), i, j);
}
}
return grid;
}
private Group createGroup() {
GridPane grid = new GridPane();
for (int i = 0; i < 400; i++) {
for (int j = 0; j < 400; j++) {
int index = rnd.nextInt(5);
Image fxImage = fxImages.get(index);
if (fxImage == null) {
fxImage = new Image(ImageLoadingDemo.class.getResourceAsStream(images[index]));
fxImages.put(index, fxImage);
}
grid.add(new ImageView(fxImage), i, j);
}
}
return new Group(grid);
}
public static void main(String[] args) {
Application.launch(args);
}
}
我最终为每个 'node chunk' 创建了一个矩形,将不透明度设置为零,然后检查是否与用户看到的显示窗格部分发生冲突。当与矩形发生碰撞时,相应的 'node chunk' 将添加到显示窗格中。