Эффективный способ определения местоположения и / или выбора графического объекта в фиксированной сетке - PullRequest
1 голос
/ 14 февраля 2010

У меня есть панель, которая заполнена большим количеством кругов (Ellipse2D). Круги хранятся в двумерном массиве (строки и столбцы).

Моя цель - уметь рисовать круги, когда я перетаскиваю на них мышь. В конечном итоге я захочу использовать формы выделения, которые изменят цвет всех окружностей, содержащихся в форме выделения.

Я использую слушатель перетаскивания мышью, который непрерывно сканирует весь 2-мерный массив и проверяет, находится ли текущая точка внутри окружностей. Вот так:

        addMouseMotionListener(new MouseAdapter() {

        public void mouseDragged(MouseEvent e) {

            currentColor = ColorSliderPanel.getRGB();

            for (int x = 0; x < numColumns; x++) {
                for (int y = 0; y < numRows; y++) {
                    if (circle[x][y].contains(e.getX(), e.getY())) {

                        circle[x][y].setColor(currentColor);
                        repaint();
                    }
                }
            }

        }
    });

Приведенный выше код работает, но он действительно медленный (1000+ кружков), так как он проверяет каждый отдельный объект.

Должен быть лучший способ. Я немного читал о Quadtree, но я не уверен, что у quadtree больше лошадиных сил, чем мне нужно.

Спасибо

Я внес следующие изменения, основываясь на некоторых комментариях ниже. Circles теперь является линейным ArrayList. Метод рисования просто заполнить круг. Внесение этого изменения улучшило скорость на два порядка. Теперь это работает намного лучше. Хотя я все еще могу скользить по панели с умеренной скоростью и пропустить несколько кругов. Так что мне может понадобиться оптимизировать дальше.

  Graphics2D g2d = (Graphics2D) getGraphics();

            for (Circle2D c : circles) {
                if (c.contains(p)) {
                    c.setColor(currentColor);
                   //Graphics2D g2d = (Graphics2D) getGraphics(); (moved)
                    c.draw(g2d);
                }
            }

Ответы [ 4 ]

3 голосов
/ 14 февраля 2010

Я бы лично так поступил:

  • сделать линейный массив окружностей (или связанный список, на ваш выбор).
  • в слушателе событий вы выполняете линейную итерацию по массиву, нажимаете, проверяя каждый кружок относительно вашей позиции мыши, и если тест пройден, вы меняете цвет
  • вот самая большая оптимизация: поскольку мы говорим о рисовании с довольно высокой частотой (каждое движение мыши), вы хотите определить, какие круги имеют , которые нужно перерисовать. Поскольку вы перебираете массив выше, сохраняйте текущий счет самого большого ограничивающего прямоугольника 1011 *, который нужно изменить (прямоугольник, который окружает каждый круг, который нужно перерисовать)
  • теперь вы удаляете вычисленный выше прямоугольник и снова выполняете итерации по окружностям, рисуя только те круги, которые находятся внутри прямоугольника (возможно, обрезая определенные кусочки окружностей, но рисуя все, что умещается внутри прямоугольника и не касаясь его снаружи).

Примечание: Итерация по 1k + кружкам дважды будет почти мгновенной, ваша настоящая проблема в рисовании кружков (и ваш странный механизм хранения x / y). Графический ввод-вывод будет и всегда будет медленным, особенно способ Java сделать это.

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

1 голос
/ 14 февраля 2010

Этот пример содержит подвижные, изменяемые размеры, цветные узлы, которые можно выбрать, перетаскивая граничный прямоугольник. Реализация выделяет выделенные узлы, отображая прямоугольную границу. На картинке ниже выделены зеленый и черный узлы. Вместо этого изменение цвета будет простым изменением метода draw() на Node.

GraphPanel

1 голос
/ 14 февраля 2010

Перебор всего набора кругов для каждого вызова mouseDragged - невероятная трата времени. Один из вариантов, который у вас есть, - взять инициативу от JTable и разработать методы, которые позволят вам идентифицировать строку и столбец в точке, содержащейся в вашем MouseEvent. Если вы знаете ширину ваших столбцов и высоту строк, вам не составит труда определить ячейку для вашего круга.

1 голос
/ 14 февраля 2010

Несколько тысяч вызовов методов занимают что-то порядка 10 микросекунд на современном компьютере, что определенно не является человеческой задержкой, не говоря уже о «очень медленной».

Поэтому причина вашей проблемы с производительностью должна быть в другом месте - предположительно, в repaint (), которая вызывает вызов paint () контейнера. Если для этого потребуется несколько тысяч кругов, это может занять около секунды.

Вот почему, прежде чем оптимизировать, вы всегда должны измерять, где на самом деле находится узкое место вашей производительности.

...