|
| 1 | +# Algorithmus zur Positionierung von Elementen in einem Aktivitätsdiagramm |
| 2 | + |
| 3 | +Der Algorithmus positioniert Knoten eines Aktivitätsdiagramms rekursiv, wobei folgende Knotentypen behandelt werden: |
| 4 | + |
| 5 | +- **Start- und Endknoten** |
| 6 | +- **Aktivitätsknoten** (immer einen Eingang, genau einen Ausgang) |
| 7 | +- **Bedingungsknoten** (zwei Ausgänge: „wahr" und „falsch") |
| 8 | +- **Merge-Knoten** (zwei Eingänge) |
| 9 | + |
| 10 | +Alle Knoten werden vertikal in gleichen Abständen angeordnet (von oben nach unten). Zusätzlich wird bei Bedingungen der falsche Pfad um einen festen horizontalen Offset verschoben. Wichtig ist, dass auch bei Bedingungen, die auf bereits verschobenen (falschen) Pfaden auftreten, der zusätzliche Offset kumulativ angewendet wird – und dass alle nachfolgenden Knoten, egal ob auf dem ursprünglich „richtigen" (vertikalen) Pfad oder auf einem falschen Pfad, stets mindestens den maximal erreichten Offset übernehmen. |
| 11 | + |
| 12 | +--- |
| 13 | + |
| 14 | +### Parameter |
| 15 | + |
| 16 | +- **verticalSpacing:** Fester vertikaler Abstand zwischen den Knoten. |
| 17 | +- **horizontalOffset:** Fester horizontaler Versatz, der bei jedem Übergang in den falschen Pfad addiert wird. |
| 18 | +- **currentOffset:** Lokaler Offset, der den kumulierten Versatz entlang des aktuellen Pfades angibt. |
| 19 | +- **globalOffset:** Der bislang maximale Offset, der in falschen Pfaden gesetzt wurde und auch auf den „richtigen" (vertikalen) Pfad übernommen wird, um bereits verankerte falsche Pfade nicht zu "verrutschen". |
| 20 | + |
| 21 | +--- |
| 22 | + |
| 23 | +### Pseudocode |
| 24 | + |
| 25 | +```pseudo |
| 26 | +function layout(node, baseX, currentY, currentOffset, globalOffset): |
| 27 | + // Positioniere den aktuellen Knoten: |
| 28 | + // Der x-Wert entspricht dem Basiswert plus dem maximalen Offset (currentOffset oder globalOffset) |
| 29 | + node.x = baseX + max(currentOffset, globalOffset) |
| 30 | + node.y = currentY |
| 31 | +
|
| 32 | + // Bereite den nächsten vertikalen Startpunkt vor |
| 33 | + newY = currentY + verticalSpacing |
| 34 | +
|
| 35 | + if node.type == 'Bedingung': |
| 36 | + // RICHTIGER Pfad: Der True-Ausgang folgt vertikal ohne zusätzlichen Offset. |
| 37 | + if node.trueSuccessor exists: |
| 38 | + layout(node.trueSuccessor, baseX, newY, currentOffset, globalOffset) |
| 39 | +
|
| 40 | + // FALSCHER Pfad: Hier wird der currentOffset um den horizontalOffset erhöht. |
| 41 | + newFalseOffset = currentOffset + horizontalOffset |
| 42 | + // Aktualisiere globalOffset, sodass er den maximal erreichten Offset abbildet. |
| 43 | + newGlobalOffset = max(globalOffset, newFalseOffset) |
| 44 | + if node.falseSuccessor exists: |
| 45 | + layout(node.falseSuccessor, baseX, newY, newFalseOffset, newGlobalOffset) |
| 46 | +
|
| 47 | + else if node.type == 'Aktivität' or node.type == 'Start' or node.type == 'Merge': |
| 48 | + // Diese Knoten haben einen einzigen Ausgang. |
| 49 | + if node.successor exists: |
| 50 | + layout(node.successor, baseX, newY, currentOffset, globalOffset) |
| 51 | +
|
| 52 | + else if node.type == 'End': |
| 53 | + // Endknoten: Keine Nachfolger |
| 54 | + return |
| 55 | +``` |
| 56 | + |
| 57 | +--- |
| 58 | + |
| 59 | +### Funktionsweise und Besonderheiten |
| 60 | + |
| 61 | +1. **Start und Vertikale Platzierung:** |
| 62 | + Der Layout-Prozess beginnt mit dem Startknoten. Jeder Knoten wird so positioniert, dass seine x-Koordinate `baseX + max(currentOffset, globalOffset)` ist. Der y-Wert wird stets um den festen `verticalSpacing` erhöht. |
| 63 | + |
| 64 | +2. **Bedingungsknoten:** |
| 65 | + - Beim **wahren Ausgang** (True-Pfad) bleibt der `currentOffset` unverändert – der Pfad verläuft vertikal. |
| 66 | + - Beim **falschen Ausgang** (False-Pfad) wird der `currentOffset` um `horizontalOffset` erhöht. Dieser neue Offset wird dann als Basis für alle Knoten in diesem Pfad verwendet. |
| 67 | + - Gleichzeitig wird der `globalOffset` aktualisiert, sodass auch Knoten, die anschließend auf einem "richtigen" (vertikalen) Pfad folgen, den maximal erreichten Offset übernehmen. Das ist entscheidend, um auch dann, wenn auf dem vertikalen Pfad wieder eine Bedingung auftritt, alle zuvor verschobenen falschen Pfade mitzuschieben. |
| 68 | + |
| 69 | +3. **Konditionen auf falschen Pfaden:** |
| 70 | + Der Algorithmus behandelt Bedingungen auf falschen Pfaden genauso wie Bedingungen auf dem vertikalen Pfad. Das bedeutet: |
| 71 | + - Auch hier wird beim falschen Ausgang der `currentOffset` um den `horizontalOffset` erhöht und der `globalOffset` angepasst. |
| 72 | + - Somit wird sichergestellt, dass sich bei mehrfach verschachtelten Bedingungen die Verschiebung nach rechts stets kumulativ erhöht. |
| 73 | + |
| 74 | +4. **Merge-Knoten:** |
| 75 | + Bei Merge-Knoten, die zwei Eingänge besitzen, kann es nötig sein, die x-Position anhand der x-Werte beider Eingänge zu berechnen (beispielsweise den maximalen Wert). Im obigen Pseudocode wird angenommen, dass der bereits übergebene Offset ausreichend ist; je nach konkreter Implementierung kann hier noch eine Zusammenführungslogik ergänzt werden. |
| 76 | + |
| 77 | +--- |
| 78 | + |
| 79 | +### Beispielaufruf |
| 80 | + |
| 81 | +Der Algorithmus wird mit dem Startknoten initialisiert, z. B.: |
| 82 | + |
| 83 | +```pseudo |
| 84 | +layout(startNode, 0, 0, 0, 0) |
| 85 | +``` |
| 86 | + |
| 87 | +Dabei wird `baseX` als 0 gewählt und sowohl `currentOffset` als auch `globalOffset` starten bei 0. |
| 88 | + |
| 89 | +--- |
| 90 | + |
| 91 | +### Zusammenfassung |
| 92 | + |
| 93 | +- **Rekursives Layout:** Jeder Knoten wird unter Berücksichtigung von vertikalem Abstand und horizontalem Offset positioniert. |
| 94 | +- **Bedingungen:** |
| 95 | + - Der True-Pfad bleibt vertikal, während der False-Pfad um einen festen horizontalen Versatz nach rechts verschoben wird. |
| 96 | + - Dieser Versatz wird kumulativ weitergegeben, sodass auch bei Bedingungen auf bereits falschen Pfaden alle nachfolgenden Knoten entsprechend verschoben werden. |
| 97 | +- **globalOffset:** Er gewährleistet, dass alle Knoten – auch wenn sie später im vertikalen Pfad auftauchen – mindestens den maximal erreichten Offset übernehmen, um die Konsistenz des Layouts zu sichern. |
| 98 | + |
| 99 | +Diese Beschreibung sollte alle geforderten Layout-Regeln und das Verhalten bei verschachtelten Bedingungen abdecken. |
0 commit comments