Sposób tworzenia nowych algorytmów rozmieszczenia węzłów

Aby dodać do CASTa nowy algorytm rozmieszczenia węzłów, należy wykonać dwa duże kroki. Pierwszy z nich to napisanie samego algorytmu, a drugi - podpięcie algorytmu pod GUI CASTa.

Implementacja algorytmu

Każdy algorytm rozkładu węzłów powinien implementować interfejs pl.edu.agh.cast.schema.layout.algorith.ILayoutAlgorithm, który znajduje się w pluginie pl.edu.agh.cast.schema (należy go dodać do zależności (dependencies) tworzonego pluginu). Interfejs ten definiuje metodę layoutNodes, używaną do rozmieszczania elementów. Metoda ta dla podanej kolekcji obiektów typu pl.edu.agh.cast.model.visual.Node uruchamia algorytm, który wywołuje na tych obiektach metodę setLocation w celu ustawienia ich położenia.

W celu uproszczenia tworzenia algorytmów, zdefiniowane zostały dwie abstrakcyjne klasy: pl.edu.agh.cast.schema.layout.algorithm.AbstractLayoutAlgorithm i pl.edu.agh.cast.schema.layout.algorithm.AbstractGroupingLayoutAlgorithm. Zaleca się, aby nowe algorytmy rozszerzały jedną z tych dwóch klas.

AbstractLayoutAlgorithm dostarcza finalnej implementacji metody layoutNodes, która bierze pod uwagę stary środek ciężkości elementów. Klasa ta definiuje abstrakcyjną metodę setNodesPositions, która jest wywoływana do ułożenia elementów. Już po ułożeniu elementów przez setNodesPositions są one przemieszczane tak, aby ich środek ciężkości pokrywał się ze starym środkiem. Dodatkowo AbstractLayoutAlgorithm zapewnia też, że żaden z elementów nie zostanie przesunięty poza lewą oraz górną krawędź diagramu.

AbstractGroupingLayoutAlgorithm rozszerza AbstractLayoutAlgorithm i dostarcza dodatkowo metod pomocniczych dla algorytmów, które używają grupowania elementów (zapewne większość algorytmów będzie tego wymagała). Dostarcza on następujące metody:

  • getMainEntities - zwracającą listę węzłów oznaczonych jako główne
  • getNeighbors - zwracającą listę sąsiadów danego węzła (tj. węzłów z nim połączonych)
  • getNotConnected - zwracającą listę elementów, które nie posiadają połączeń z innymi elementami
  • isConnected - sprawdzającą, czy dwa elementy są ze sobą połączone
  • drawNotConnected - metodę układającą wejściową listę elementów po kolei w lewym górnym rogu (może być wygodnie użyta do narysowania elementów zwróconych przez getNotConnected)

Model używany w algorytmach

Algorytmy operują na kolekcjach obiektów typu Node, i tylko na nich. Najważniejsze metody klasy Node z punktu widzenia algorytmów:

  • getSourceConnectionGroups - zwraca listę obiektów ConnectionGroup, opisujących połączenia wychodzące od danego węzła
  • getTargetConnectionGroups - jw. dla połączeń przychodzących do danego węzła
  • getLocation - zwraca obecne położenie elementu; UWAGA: domyślne położenie to punkt o współrzędnych (-1, -1) - oznacza to brak ustawionego położenia; getLocation nigdy nie zwraca null
  • setLocation - ustawia nowe położenie elementu
  • isMainNode - informuje, czy element ma ustawioną flagę main entity; jest ona ustawiana dla elementów uznawanych za podejrzane/ważne, i niektóre algorytmy używają jej do rozróżniania i grupowania elementów
  • getDimensions - zwraca rozmiary elementu na diagramie; UWAGA: nie należy zakładać, że wszystkie elementy mają ten sam rozmiar

Połączenia między węzłami są opisywane przez obiekty typu pl.edu.agh.cast.model.visual.ConnectionGroup. Każdy Node posiada dwie kolekcje połączeń: przychodzących i wychodzących. Połączenia przychodzące to takie, dla których getTarget jest danym węzłem, zaś wychodzące - dla których getSource jest danym węzłem. W każdym połaczeniu dodatkowo znajduje się liczba połączeń wychodzących i przychodzących wykonanych z drugim węzłem.

Dla jasności, jeśli A zadzwonił do B 4 razy, a B do A 3 razy, to węzły te będą miały następujące obiekty ConnectionGroup:

  • A o sourceConnections + Obiekt1 (source: A, target: B, outgoing: 4, incoming: 3) o targetConnections + Obiekt2 (source: B, target: A, outgoing: 3, incoming: 4)
  • B o sourceConnections + Obiekt2 (source: B, target: A, outgoing: 3, incoming: 4) o targetConnections + Obiekt1 (source: A, target: B, outgoing: 4, incoming: 3)

Wpięcie algorytmu do GUI

Wpięcie algorytmu do GUI następuje wedle następujących kroków:

  • W pakiecie pl.edu.agh.cast.schema.action.layout należy stworzyć nową akcję rozszerzającą SetLayoutAction. Wewnątrz nowej akcji należy zaimplementować metodę getAlgorithm i w niej zwrócić instancję nowego algorytmu.
  • W pliku plugin.xml, w extension point org.eclipse.ui.editorActions, w editorContribution o id pl.edu.agh.cast.schema.setlayout należy dodać nową akcję o następujących parametrach:

    action class="pl.edu.agh.cast.action.layout.SetPeacockLayoutAction" icon="icons/pl/edu/agh/cast/icons/menu/diagram/x.gif" id="pl.edu.agh.cast.menu.diagram.peacocklayout.action" label="%action.label.10" menubarPath="diagram/schema" style="push" toolbarPath="diagram/schema"/

przy czym należy zmienić odpowiednio atrybuty class, id, label.

  • Do plików plugin.properties i plugin_pl.properties należy dodać zlokalizowaną nazwę algorytmu, taką jak podano w opisie akcji w atrybucie label

Wpięcie algorytmu do GUI poprzez plugin

Wpięcie algorytmu do GUI poprzez plugin następuje wedle następujących kroków:

  • Należy stworzyć nowy projekt typu plugin
  • Dodajemy plugin pl.edu.agh.cast oraz pl.edu.agh.cast.schema jako required plugins (zakładka Dependencies w edytorze plugin.xml). Dzięki temu zabiegowi nie musimy dodawać tych pluginów do build path, w trakcie kompilacji automatycznie dostępne bądą wszystkie wyeksportowane przez nie pakiety.
  • Tworzymy przynajmniej dwie klasy :

1. Klasa samego algorytmu, która rozszerza klasę AbstractLayoutAlgorithm? 2. Klasa akcji odpalanej po naciśnięciu przycisku, która rozszerza SetLayoutAction?

  • Definiujemy jeden extension-point :

    extension point="org.eclipse.ui.editorActions" editorContribution id="pl.edu.agh.cast.schema.setlayout" targetID="pl.edu.agh.cast.diagrams.schema.SchemaDiagramEditor" action class="klasa.rozszerzajaca.klase.SetLayoutAction" id="unikalny.identyfikator.naszej.akcji" label="nazwa.na.przyciku" menubarPath="diagram/schema" style="push" toolbarPath="diagram/schema" /action /editorContribution /extension

  • Dodajemy nasz plugin w opcjach run dla pluginu pl.edu.agh.cast