Рисовать только узлы, которые должны быть на экране? - PullRequest
0 голосов
/ 07 марта 2019

Я делаю симуляцию в JavaFX с использованием графа сцены (не холста), и у меня возникают проблемы при рисовании только того, что мне нужно на экране.

Эта симуляция содержит более 10 миллионов узлов, но пользователю нужно видеть только небольшую часть этих узлов одновременно (максимум 160 000 узлов). Все интересующие меня узлы имеют размер 400х400 ImageViews.

Каждый узел является членом Group (чанка узла), который содержит примерно 40 000 узлов, поэтому 4 или менее из этих «чанков узла» должны отображаться одновременно. Для отображения этих «чанков узла» они добавляются в статический Pane, и эта панель находится в корневом узле, который является Group.

Итак, мой график от первого до последнего потомка выглядит так:

Корневой узел Group \ Display Pane \ (много) Куски узла Group \ <= 40000 <code>ImageViews

Поскольку область отображения постоянно перемещается (панорамирование и масштабирование) в зависимости от пользовательского ввода, и существует так много узлов, приложение не запускается со скоростью, которая мне бы хотелось. Имеет смысл, что JavaFX испытывает проблемы с отслеживанием более 10 миллионов узлов одновременно, поэтому мое решение состояло в том, чтобы удалить все «чанки узлов» из области отображения; сохраняя их в хэш-карте, пока они мне не понадобятся.

Каждый 'узел чанка' имеет свои LayoutX и LayoutY s, которые должны быть равномерно распределены по панели отображения в сетке следующим образом:

Node chunk display

В этом примере мне нужно было бы захватить и отобразить «чанк узла» 7, 8, 12 и 13, поскольку именно это видит пользователь.

Вот скриншот с добавленным вручную «нод чанком» 0. Зеленовато-желтый цвет - это то место, где размещались бы «узлы» 1, 5 и 6.

chunk 1

Моя проблема: поскольку «чанки узла» не добавляются в область отображения до тех пор, пока они не понадобятся, я не могу сослаться на их границы компоновки в отношении постоянно меняющегося раздела панели отображения, который видят пользователи, поэтому я делаю не знаю, какие «узлы» должны быть отображены.

Есть ли простой способ решить эту проблему? Или я на неверном пути? (или оба) Спасибо.

Ответы [ 2 ]

1 голос
/ 07 марта 2019

Примечание: это не ответ на ваш точный вопрос.

Я не знаю, в чем причина создания ImageView для каждого изображения. Скорее я бы нарисовал одно изображение для каждого nodeChunk (160000 изображений) и создал бы один ImageView для этого nodeChunk.

Функционально я не вижу никакой разницы между этими двумя подходами (вы можете дать мне знать, почему вы хотите использовать каждый просмотр изображений). Но благодаря производительности этот подход очень быстрый и плавный.

Пожалуйста, найдите приведенную ниже демонстрацию того, что я говорю. enter image description here

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);
        }
    }
0 голосов
/ 07 марта 2019

Я закончил тем, что создал прямоугольник для каждого «чанка узла», установил непрозрачность на ноль, а затем проверил на столкновение с той частью панели отображения, которую видит пользователь. Когда произошло столкновение с прямоугольником, соответствующий «узел узла» затем добавляется на панель отображения.

...