Библиотека сайта rus-linux.net
Фреймворк Violet
Глава 22 из книги "Архитектура приложений с открытым исходным кодом", том 1.
Оригинал: "Violet", глава из книги "The Architecture of Open Source Applications"
Автор: Cay Horstmann
Перевод: Н.Ромоданов
22.2. Графический фреймворк
Violet базируется на универсальном фреймворке редактирования графов, который может выдавить изображения и позволяет редактировать узлы и ребра изображений произвольной формы. Редактор Violet для UML использует узлы для отображения классов, объектов, границ активации (в диаграммах последовательностей), и так далее, а ребра — для различных дуг в диаграммах UML. Другой экземпляр графического фреймворка может отображать диаграммы «сущность - отношение» или синтаксические диаграммы.
Рис.22.2: Простой экземпляр фреймворка редактирования
Для того чтобы проиллюстрировать фреймворк, рассмотрим редактор для очень простых графов с черно-белыми круглыми узлами и прямыми ребрами (рис.22.2). В классе SimpleGraph
определяются прототипные объекты для типов узлов и ребер, иллюстрирующие шаблон прототипорования prototype:
public class SimpleGraph extends AbstractGraph { public Node[] getNodePrototypes() { return new Node[] { new CircleNode(Color.BLACK), new CircleNode(Color.WHITE) }; } public Edge[] getEdgePrototypes() { return new Edge[] { new LineEdge() }; } }
Прототипные объекты используются для рисования кнопок узлов и ребер в верхней части рисунка 22.2. Они клонируются всякий раз, когда пользователь добавляет в граф новый экземпляр узла или ребра. Узел Node
и ребро Edge
являются интерфейсами со следующими ключевыми методами:
- В обоих интерфейсах есть метод
getShape
, который возвращает объект Java2DShape
, представляющий собой узел или ребро. - The интерфейс
Edge
имеет методы, с помощью которых можно получить начальный и конечный узел ребра. - Метод
getConnectionPoint
в интерфейсе типа Node вычисляет оптимальные точки подсоединения к границе узла (смотрите рис.22.3). - Метод
getConnectionPoints
интерфейсаEdge
позволяет получать две конечные точки ребра. Этот метод необходим, чтобы рисовать «грабберы», которыми помечается текущее выбранное ребро. - Узел может иметь потомков, которые перемещаются вместе с родителем. Предлагается ряд методов для нумерации и управления потомками.
Рис.22.3: Поиск точки присоединения на границе формы узла Node
Удобные классы AbstractNode
и AbstractEdge
реализуют ряд таких методов, а классы RectangularNode
и SegmentedLineEdge
обеспечивают всю полную реализацию прямоугольных узлов со строкой-заголовком и ребрами, которые состоят из отрезков прямой.
В случае нашего простого редактора графов нам нужны подклассы CircleNode
и LineEdge
, в которых есть метод draw
, метод contains
и метод getConnectionPoint
, в которых описывается форма границы узла. Ниже приведен код, а на рис 22,4 показана диаграмма классов для этих классов (нарисованных, конечно, с помощью Violet).
public class CircleNode extends AbstractNode { public CircleNode(Color aColor) { size = DEFAULT_SIZE; x = 0; y = 0; color = aColor; } public void draw(Graphics2D g2) { Ellipse2D circle = new Ellipse2D.Double(x, y, size, size); Color oldColor = g2.getColor(); g2.setColor(color); g2.fill(circle); g2.setColor(oldColor); g2.draw(circle); } public boolean contains(Point2D p) { Ellipse2D circle = new Ellipse2D.Double(x, y, size, size); return circle.contains(p); } public Point2D getConnectionPoint(Point2D other) { double centerX = x + size / 2; double centerY = y + size / 2; double dx = other.getX() - centerX; double dy = other.getY() - centerY; double distance = Math.sqrt(dx * dx + dy * dy); if (distance == 0) return other; else return new Point2D.Double( centerX + dx * (size / 2) / distance, centerY + dy * (size / 2) / distance); } private double x, y, size, color; private static final int DEFAULT_SIZE = 20; } public class LineEdge extends AbstractEdge { public void draw(Graphics2D g2) { g2.draw(getConnectionPoints()); } public boolean contains(Point2D aPoint) { final double MAX_DIST = 2; return getConnectionPoints().ptSegDist(aPoint) < MAX_DIST; } }
Рис.22.4: Диаграмма классов для простого графа
В целом, Violet предоставляет простой фреймворк для создания редакторов графов. Чтобы получить экземпляр редактора, определяются классы узлов и ребер и предоставляются методы в классе графа, с помощью которых можно получить прототипы объектов узел и ребро.
Конечно, есть и другие графовые фреймворки, например, JGraph [Ald02] и JUNG2. Однако эти фреймворки гораздо более сложные и они являются фреймворками для рисования графов, а не для создания приложений, которые рисуют графы.
Продолжение статьи: 22.3. Использование свойств JavaBeans.