Problem
Das TreeLayout (com.top_logic.graphic.flow.data.TreeLayout) packt alle Kinder eines Knotens standardmäßig in eine einzige vertikale Spalte. Bei hohem Fan-out (viele Kinder unter einem Eltern-Knoten) wächst die Diagrammhöhe linear mit der Kinderzahl, sodass das Diagramm auf normalen Bildschirmen nicht mehr darstellbar ist.
Lösung: Neue Konfigurations-Optionen am `TreeLayout`
Am TreeLayout (sowohl in der TreeLayout-Konfiguration als auch als Parameter der TL-Script-Funktion tree(...)) stehen folgende neuen Properties zur Verfügung:
Allgemein
- childSplitThreshold (int, Default 0): Schwelle, ab der die Kinder eines Eltern-Knotens in ein 2D-Sub-Grid verteilt werden statt in eine einzelne Spalte. 0 deaktiviert den Sub-Grid-Modus (Legacy-Verhalten, eine Spalte pro Eltern-Knoten).
- rowWise (boolean, Default false): Wählt zwischen den beiden Sub-Grid-Algorithmen. Wirkung beider Algorithmen siehe unten.
== Column-wise (Default, rowWise=false) ==
Kinder werden spaltenmajor in ein Raster aus R = childSplitThreshold Zeilen × C = ⌈M/R⌉ Sub-Spalten verteilt. Jede Sub-Spalte hat einen eigenen vertikalen Bus; Folge-Spalten-Busse hängen über eine gemeinsame Bottom-Brücke am Primär-Bus.
- bridgeGapY (double, Default 20): Y-Abstand zwischen dem tiefsten Sub-Spalten-Bottom und der gemeinsamen Bottom-Brücke. Nur in column-wise relevant.
Eigenschaft: Sehr kompakt. Subtree-Knoten eines Sub-Grid-Kindes liegen aber zwischen den Sub-Spalten, sodass die Tiefe↔X-Korrespondenz („Knoten gleicher Tiefe liegen auf derselben X-Position“) für Tiefe ≥ 2 nicht mehr erhalten bleibt.
== Row-wise (rowWise=true) ==
Kinder werden zeilenmajor auf C Sub-Spalten verteilt. Das Sub-Grid enthält nur die direkten Kinder; alle Subtrees der Sub-Grid-Kinder werden in eine gemeinsame Folgespalte rechts neben dem Sub-Grid (postGridX) verlegt. Es gibt genau einen vertikalen Bus rechts außerhalb des Sub-Grids, der sowohl die Eltern→Sub-Grid-Kinder-Verbindungen als auch die Sub-Grid-Kind→Subtree-Verbindungen trägt.
Zusätzliche Optionen:
- subGridCols (int, Default 0): Anzahl der Sub-Spalten im row-wise Sub-Grid. Falls 0`, wird `childSplitThreshold als Sub-Spalten-Zahl verwendet.
- subGridStartCol (int, Default 0): Sub-Spalte, in der das erste Kind (Index 0) landet; Kind n landet in Sub-Spalte (n + subGridStartCol) mod C. Werte außerhalb [0, C-1] werden modulo C normalisiert. Nützlich z. B., um die obere linke Sub-Grid-Zelle für eine bessere optische Balance mit dem Eltern-Knoten freizulassen.
Eigenschaft: Tiefe↔X bleibt ab Tiefe 2 erhalten — alle Subtree-Knoten (Enkel von Eltern-Knoten) liegen auf derselben X-Position postGridX, Tiefe-3-Knoten weiter rechts usw. Adaptiver Y-Stack: jedes Kind wird so dicht wie möglich gepackt, unter Wahrung von Box-Clearance, Bus-Stub-Clearance, Nicht-Überlappung von Sub-Grid-Kind→Subtree-Stems mit späteren Sub-Grid-Boxen, und Nicht-Überlappung der Bus-Segmente am gemeinsamen childBusX. Anchor-Position im Knoten wird berücksichtigt (anchor.y, anchor.height), sodass Knoten mit Label-oberhalb-Anchor (z. B. Beschriftung über kleinerem Symbol) korrekt behandelt werden: der Eltern-Stem trifft die Anchor-Rechtskante (nicht die Box-Rechtskante), und die Stub-Clearance verwendet die Anchor-Mittellinie (nicht die Box-Mittellinie).
Wann welche Variante?
- column-wise: Für Diagramme mit gleichförmigen Knoten und kompaktem Layout-Bedarf; ohne Anforderung an Tiefe↔X-Konsistenz.
- row-wise: Für Diagramme, in denen „Enkel“-Knoten optisch eindeutig einer Tiefenebene zugeordnet werden sollen; bei Knoten mit Beschriftungen über dem Anchor-Bereich; wenn nur ein einziger Bus rechts des Eltern-Bereichs gewünscht ist.
Implementierung (intern)
- data.proto: Properties childSplitThreshold, rowWise, subGridCols, subGridStartCol, bridgeGapY an TreeLayout.
- TreeRenderInfo: Bottom-up-Layout pro Subtree; GridInfo mit Kind-Enum (COLUMN_WISE / ROW_WISE). Für row-wise zusätzlich postGridX, childBusX, adaptive Y-Constraints (Stub-Clearance, gleiche-Spalte-Box- Clearance, Bus-Top-Clearance gegen prevBusBottom für Anchor-Seite und curYPost-Push für Grandchild-Seite, Past-Stem-Crossing-Clearance).
- TreeNode: optionales _busXOverride für Sub-Grid-Kinder, deren eigene Out-Verbindungen auf den gemeinsamen childBusX umgeleitet werden.
- TreeLayoutOperations: drawGridBuses dispatcht zu drawGridBusesColumnWise (Primary-Bus + Bottom-Bridge + Folge-Spalten-Busse) bzw. drawGridBusesRowWise (einzelner Bus); barPosition nutzt gi.getBarPositionFor(child) mit Fallback auf _busXOverride. Eltern-Stem nutzt jetzt die Anchor-Rechtskante.
- FlowFactory.tree(...): alle neuen Optionen als optionale Parameter.
Tests
TestTreeLayout (10 Tests, alle grün) deckt ab:
- Linear-Modus (Legacy).
- testGridFanout: column-wise, 12 Kinder, einige mit Subtrees.
- testGridFanoutRowWise: row-wise, dieselbe Topologie.
- testGridFanoutRowWiseVaryingHeight: unterschiedliche Knotenhöhen.
- testGridFanoutRowWiseLabelAboveAnchor: Box wider als Anchor; verifiziert Anchor-Rechtskanten-Anbindung.
- testGridFanoutRowWiseMixedTallNodeWithSubtree: Bus-Disjunktion am gemeinsamen childBusX bei tiefem Anchor und flachen Folge-Subtrees.
- testGridFanoutRowWiseStartCol: subGridStartCol=1-Variante.