Наши партнеры

UnixForum





Библиотека сайта 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, который возвращает объект Java2D Shape, представляющий собой узел или ребро.
  • 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.