Version: 0.3.0
Companion to: layout-algorithm-specification.md Purpose: Traces two concrete fixtures through every stage of the nine-stage pipeline, producing exact intermediate values at each stage boundary. Intended to be read alongside the specification, one document per monitor.
All values here are confirmed by the project's unit test suite. If any value in this document conflicts with a passing test, the test is authoritative.
Two data files in docs/example-data are used:
meetings.ttl— three Persons, two Activities, states on Alice. Simple fixture: no installations, no mereologically transitions.aircraft684_diagram.ttl— Systems, component slots, installations, slot co-location. Complex fixture: multiple timebands with Y-position changes.
| Entity | Type | Key relationships |
|---|---|---|
alice |
Person | states: Active (2021-06-01 → 2023-06-01), Retired (2023-06-01 → 2025-02-01) |
bob |
Person | no bounds, no states |
carol |
Person | no bounds, no states |
project-meeting |
Activity | 2022-09-01 → 2023-02-01; participations: alice, bob, carol |
workshop |
Activity | 2023-04-15 → 2023-11-01; participations: alice, carol |
Persons are FusionOfIntegralParts (Person is a subclass), but have no visible internal participants in this diagram → rendered as plain bands. Activities render as fusion outlines spanning their participants.
Sub-step 1.1 — Metric breakpoints:
Seven breakpoints are collected from the ParticularPeriod timestamps:
| Index | Timestamp | Starting entities | Ending entities |
|---|---|---|---|
| 0 | 2021-06-01 | alice-state-active |
— |
| 1 | 2022-09-01 | project-meeting |
— |
| 2 | 2023-02-01 | — | project-meeting |
| 3 | 2023-04-15 | workshop |
— |
| 4 | 2023-06-01 | alice-state-retired |
alice-state-active |
| 5 | 2023-11-01 | — | workshop |
| 6 | 2025-02-01 | — | alice-state-retired |
Note: Activities and states both produce breakpoints. States (like
alice-state-active) are temporal parts of individuals and generate breakpoints where they start and end — these affect the internal participation/state track display, not Y positions or transitions.
Sub-steps 1.2–1.3 — Ordering constraints:
No entirelyAfter triples in this fixture. Metric inferred edges are generated from adjacent timestamps (e.g., alice-state-active ends at BP_4, alice-state-retired starts at BP_4 — same timestamp, no inferred edge between them; project-meeting ends at BP_2, workshop starts at BP_3 — adjacent timestamps, inferred edge generated).
Sub-step 1.4 — DAG: No cycles. Pipeline continues.
Sub-step 1.5 — Ordinal breakpoints: All entities have metric bounds. No ordinal breakpoints are injected.
Seven metric breakpoints → eight timebands.
includeFirst = true (bob.start === undefined, carol.start === undefined).
includeLast = true (bob.end === undefined, carol.end === undefined).
| Index | Left BP | Right BP | Notes |
|---|---|---|---|
| 0 | null | 2021-06-01 | Left-open; alice not yet active |
| 1 | 2021-06-01 | 2022-09-01 | alice-state-active begins |
| 2 | 2022-09-01 | 2023-02-01 | project-meeting active |
| 3 | 2023-02-01 | 2023-04-15 | gap between activities |
| 4 | 2023-04-15 | 2023-06-01 | workshop active |
| 5 | 2023-06-01 | 2023-11-01 | alice-state-retired active; workshop active |
| 6 | 2023-11-01 | 2025-02-01 | alice-state-retired active; workshop ended |
| 7 | 2025-02-01 | null | Right-open; alice-state-retired ended |
Eight timebands. ✓
| Entity | Changes across timebands |
|---|---|
alice |
participatingInFusions: adds project-meeting in TB_2, removes in TB_3; adds workshop in TB_4, removes in TB_6. activeStatePartIds: [alice-state-active] in TB_1–TB_4; [alice-state-retired] in TB_5–TB_6; [] in TB_0 and TB_7. No fusedIntoSlots ever set. |
bob |
participatingInFusions: project-meeting in TB_2 only. activeStatePartIds: always []. |
carol |
participatingInFusions: project-meeting in TB_2; workshop in TB_4 and TB_5. activeStatePartIds: always []. |
No installations. All fusedIntoSlots remain [] throughout.
None. No slot-to-slot fusions, no installations. No co-location in any timeband.
Settings: minBandHeight=30, paddingV=15, rootGap=5, minInternalRowHeight=20.
Pre-computation — sub-row counts:
alice: max concurrent = 2 (e.g. TB_2:alice-state-activesub-row +project-meetingparticipation). Height = max(30, 2×20) = 40.bob: max concurrent = 1 (TB_2:project-meetingparticipation). Height = max(30, 1×20) = 30.carol: max concurrent = 1. Height = 30.
Pass 1 — Global tree (all three are roots; alphabetical order):
alice: height=40, yStart=0 (cursor starts at 0)
bob: height=30, yStart=45 (40 + rootGap 5)
carol: height=30, yStart=80 (45 + 30 + rootGap 5)
No structural parent–child relationships. No internal node expansion needed.
Pass 2 — No co-location groups in any timeband. Assignments are identical to Pass 1 in all timebands.
Final Y layout (identical in all 8 timebands):
| Entity | yStart | height |
|---|---|---|
| alice | 0 | 40 |
| bob | 45 | 30 |
| carol | 80 | 30 |
Heights are constant — Stage 6 will detect zero transition groups for this fixture.
TransitionGroupList = [] (0 groups).
alice, bob, and carol have identical Y positions across all timebands — no yStart changes are detected at any breakpoint. The Activities and States produce breakpoints but do not move any entity's band vertically.
Settings: chevronOffset=20, minPaddingPx=20, minStraightSectionPx=10, paddingBetweenTransitionsPx=20.
alice, bob, and carol are open-started and open-ended (depth 0 via isPartOf). However, their participation entities (e.g. alice's participation-alice-meeting) have isPartOf project-meeting, giving nesting depth 1. Max nesting depth among open-started entities = 1.
paddingLeftPx = 1 × 20 + 20 = 40
paddingRightPx = 40 (same entities in TB_7)
No TransitionGroup → no transition sections. The 8 straight-A sections flow directly into each other. Activity outline boxes (project-meeting, workshop) span the relevant straight-A sections without needing a horizontal transition zone.
| # | Type | timebandIndex | minWidthPx |
|---|---|---|---|
| 1 | padding-left | null | 40 |
| 2 | straight-A | 0 | 10 |
| 3 | straight-A | 1 | 10 |
| 4 | straight-A | 2 | 10 |
| 5 | straight-A | 3 | 10 |
| 6 | straight-A | 4 | 10 |
| 7 | straight-A | 5 | 10 |
| 8 | straight-A | 6 | 10 |
| 9 | straight-A | 7 | 10 |
| 10 | padding-right | null | 40 |
Total totalMinWidthPx = 160 px. Section count = 2 + 8 + 0 + 0 = 10 ✓
This fixture has open-bounded timebands: TB_0 has no left metric boundary (null start), and TB_7 has no right metric boundary (null end). However, the 6 interior timebands (TB_1 through TB_6) are bounded by the 7 metric timestamps spanning from 2021-06-01 to 2025-02-01.
minLinearWidthPx is null (open-ended timebands exist). isNonLinear = true. The interior bounded sections receive width proportional to their duration; the open-ended sections receive minWidthPx.
For a canvas large enough to proportionally scale the interior: fixedWidthPx = 40 + 40 = 80 (paddings only). availableForBounded = (canvas − 80). totalBoundedDurationMs = 2025-02-01 − 2021-06-01 ≈ 113,270,400,000 ms. Each bounded interior section gets width proportional to its duration.
timeRange = { startMs: 2021-06-01, endMs: 2025-02-01 } (from the outermost metric-bounded entities at depth 0).
Pass A — Z-order:
No installations ever occur. Structural tree: three independent roots. DFS preorder on alphabetically-sorted roots:
alice → Z = 0
bob → Z = 1
carol → Z = 2
Pass B — Segments (alice, at canvas=160 = totalMinWidthPx):
All sections at minWidthPx. Cumulative left-X:
| Section # | Left X |
|---|---|
| 0 (padding-left) | 0 |
| 1 (SA-TB_0) | 40 |
| 2 (SA-TB_1) | 50 |
| 3 (SA-TB_2) | 60 |
| 4 (SA-TB_3) | 70 |
| 5 (SA-TB_4) | 80 |
| 6 (SA-TB_5) | 90 |
| 7 (SA-TB_6) | 100 |
| 8 (SA-TB_7) | 110 |
| 9 (padding-right) | 120 |
alice: yStart=0, height=40 → yPos = 0 + 40/2 = 20. nestingDepthStart = 0, unknownStart = true → no left inset. nestingDepthEnd = 0, unknownEnd = true → xPos = 160 − (0+1) × 10 = 150.
N=10 active sections → 11 segments:
| i | xPos | yPos | note |
|---|---|---|---|
| 0 | 0 | 20 | left body face; depth=0, unknownStart |
| 1 | 40 | 20 | SA-TB_0 left |
| 2 | 50 | 20 | SA-TB_1 left |
| 3 | 60 | 20 | SA-TB_2 left |
| 4 | 70 | 20 | SA-TB_3 left |
| 5 | 80 | 20 | SA-TB_4 left |
| 6 | 90 | 20 | SA-TB_5 left |
| 7 | 100 | 20 | SA-TB_6 left |
| 8 | 110 | 20 | SA-TB_7 left |
| 9 | 120 | 20 | padding-right left |
| 10 | 150 | 20 | right body face; depth=0, unknownEnd |
unknownStart = true, unknownEnd = true. Height = 40. Style: defaultBand.
bob: yPos = 45 + 15 = 60. carol: yPos = 80 + 15 = 95. Identical pattern.
Pass C — Style: All three persons → defaultBand.
| Entity | Type(s) | Key Relationships |
|---|---|---|
aircraft684 |
System | no bounds |
port-engine-component |
System, ReplaceableLifespan | isPartOf aircraft684 |
fuelpump-component |
ReplaceableLifespan | isPartOf port-engine-component |
engine329-fuelpump-component |
ReplaceableLifespan | isPartOf engine329 |
engine329 |
Artefact, System | no bounds; installed into port-engine-component |
fuelpump739 |
Artefact | no bounds; installed into engine329-fuelpump-component |
sysCompFusion |
FusionOfIntegralParts | links fuelpump-component ↔ engine329-fuelpump-component; ends 2020-01-01 |
engine329-installed |
Installed | links engine329 ↔ port-engine-component; ends 2020-01-01 |
fuelpump739-installed |
Installed | links fuelpump739 ↔ engine329-fuelpump-component; ends 2024-06-01 |
Sub-step 1.1 — Metric breakpoints:
| Index | Timestamp | Ending entities |
|---|---|---|
| 0 | 2020-01-01 | sysCompFusion, engine329-installed |
| 1 | 2024-06-01 | fuelpump739-installed |
Two breakpoints. No explicit entirelyAfter triples. Inferred edge: engine329-installed#end → fuelpump739-installed#end (BP_0 < BP_1). No cycles.
Sub-step 1.5 — No ordinal breakpoints (all entities with bounds have metric breakpoints).
Two metric breakpoints → three timebands.
includeFirst = true (engine329.start === undefined, fuelpump739.start === undefined, aircraft684.start === undefined — no metric starts).
includeLast = true (same entities have no metric ends).
| Index | Left BP | Right BP | Description |
|---|---|---|---|
| 0 | null | 2020-01-01 | Both installations and slot fusion active |
| 1 | 2020-01-01 | 2024-06-01 | engine329 uninstalled; fuelpump739 still installed |
| 2 | 2024-06-01 | null | fuelpump739 uninstalled; all standalone |
Three timebands. ✓
Only the rows that change across timebands are shown in detail:
| Entity | TB_0 | TB_1 | TB_2 |
|---|---|---|---|
engine329-installed (isActive) |
yes | no | no |
sysCompFusion (isActive) |
yes | no | no |
fuelpump739-installed (isActive) |
yes | yes | no |
engine329 fusedIntoSlots |
[port-engine-component] |
[] |
[] |
fuelpump739 fusedIntoSlots |
[engine329-fuelpump-component] |
[engine329-fuelpump-component] |
[] |
fuelpump-component activeSlotFusionIds |
[sysCompFusion] |
[] |
[] |
engine329-fuelpump-component activeSlotFusionIds |
[sysCompFusion] |
[] |
[] |
Structural entities (aircraft684, port-engine-component, fuelpump-component, engine329-fuelpump-component, engine329) are always active with fixed mereologicalParentId.
TB_0: Two groups arising from the active installation and slot fusion:
-
Slot-fusion group (groupId=
sysCompFusion):- Raw members from
activeSlotFusionIds:fuelpump-componentandengine329-fuelpump-component(both slots in the fusion). fuelpump739is installed intoengine329-fuelpump-component(a group member) → Union-Find merges its install pair into this group.- Final members:
{ engine329-fuelpump-component, fuelpump-component, fuelpump739 }. - Both
engine329-fuelpump-componentandfuelpump-componentareReplaceableLifespan→ multiple slots → alphabetically first =engine329-fuelpump-component(canonical).
- Raw members from
-
Install group (groupId=
install:port-engine-component:engine329):engine329.fusedIntoSlots = [port-engine-component]→ pair{ port-engine-component, engine329 }.- Exactly one slot member:
port-engine-component(canonical).
TB_1: One group:
fuelpump739.fusedIntoSlots = [engine329-fuelpump-component]→ pair{ engine329-fuelpump-component, fuelpump739 }.- Canonical:
engine329-fuelpump-component(the only slot member).
TB_2: No groups.
Settings: minBandHeight=30, paddingV=15, rootGap=5, minInternalRowHeight=20.
Pre-computation: All entities have subRowCount = 0 (no participations or state sub-rows). Height = max(30, 0×20) = 30 for all. Except containers: aircraft684 and port-engine-component expand in Pass 1 to contain their children.
The global structural tree (from invariant mereologicalParentId):
Root: aircraft684
└── port-engine-component [isPartOf]
└── fuelpump-component [isPartOf]
Root: engine329
└── engine329-fuelpump-component [isPartOf]
Root: fuelpump739 (leaf)
Installed entities (engine329, fuelpump739) are not tree-children of their host slots. Installations are handled by Pass 2 co-location, not by the structural tree.
Root ordering is alphabetical by IRI: aircraft684, engine329, fuelpump739.
Depth-first placement:
Derivation:
cursor=0
aircraft684 (internal) → cursor += 15 → cursor=15
port-engine-component (internal) → cursor += 15 → cursor=30
fuelpump-component (leaf): yStart=30, height=30; cursor += 30+5 → cursor=65
port-engine-component: recompute(children=[fuelpump-component])
minChildY=30, maxChildBottom=60
yStart=30−15=15, height=60+15−15=60
(port-engine-component is not a root, so cursor is not advanced by port-engine-component itself)
aircraft684: recompute(children=[port-engine-component])
minChildY=15, maxChildBottom=75
yStart=15−15=0, height=75+15−0=90
(aircraft684 is root) → cursor = aircraft684.yStart + aircraft684.height + rootGap = 0+90+5=95
cursor=95
engine329 (internal) → cursor += 15 → cursor=110
engine329-fuelpump-component (leaf): yStart=110, height=30; cursor += 30+5 → cursor=145
engine329: recompute(children=[engine329-fuelpump-component])
yStart=110−15=95, height=(110+30)+15−95=60
(engine329 is root) → cursor = 95+60+5=160
cursor=160
fuelpump739 (leaf): yStart=160, height=30; cursor=195
Pass 1 global base (before co-location):
aircraft684: yStart=0, height=90
port-engine-component: yStart=15, height=60
fuelpump-component: yStart=30, height=30
engine329: yStart=95, height=60
engine329-fuelpump-component: yStart=110, height=30
fuelpump739: yStart=160, height=30
The working assignments map starts as a copy of the Pass 1 global base. Groups are processed install groups first, then slot-fusion groups.
Step A — Install group { port-engine-component (canonical, yStart=15, height=60), engine329 (yStart=95, height=60) }:
delta = canonical.yStart − member.yStart = 15 − 95 = −80
shiftSubtree(engine329, −80):
engine329: yStart = 95 + (−80) = 15
engine329-fuelpump-component: yStart = 110 + (−80) = 30 ← entire subtree shifts
pin(engine329, { yStart: 15, height: 60 })
re-expand ancestors of engine329: none (engine329 is a root)
Assignments after install group:
aircraft684: yStart=0, height=90
port-engine-component: yStart=15, height=60
fuelpump-component: yStart=30, height=30
engine329: yStart=15, height=60 ← shifted
engine329-fuelpump-component: yStart=30, height=30 ← shifted with engine329's subtree
fuelpump739: yStart=160, height=30
Step B — Slot-fusion group { engine329-fuelpump-component (canonical, now yStart=30), fuelpump-component (yStart=30), fuelpump739 (yStart=160) }:
The canonical (engine329-fuelpump-component) is now at yStart=30 — it was moved by the install group in Step A.
Process fuelpump-component:
delta = 30 − 30 = 0 ← already aligned; no movement
Process fuelpump739:
delta = 30 − 160 = −130
shiftSubtree(fuelpump739, −130): fuelpump739 yStart = 160 + (−130) = 30
pin(fuelpump739, { yStart: 30, height: 30 })
re-expand ancestors of fuelpump739: none (fuelpump739 is a root)
Key insight: The install group runs first and shifts engine329's entire subtree (including
engine329-fuelpump-component) from 110→30. By the time the slot-fusion group runs, its canonical member already sits at 30, sofuelpump-componentrequires no movement (it was always at 30 in the global base). Onlyfuelpump739moves in the slot-fusion step.
TB_0 final Y layout (confirmed by tests):
aircraft684: yStart=0, height=90
port-engine-component: yStart=15, height=60 ←→ engine329 (co-located; install group)
fuelpump-component: yStart=30, height=30 ←→ engine329-fuelpump-component, fuelpump739 (co-located; slot-fusion)
engine329: yStart=15, height=60 [isInstalledSystem=true]
engine329-fuelpump-component: yStart=30, height=30
fuelpump739: yStart=30, height=30
Containment check: fuelpump-component [30..60] ⊂ port-engine-component [15..75] ⊂ aircraft684 [0..90]. ✓
Same global base. One co-location group: { engine329-fuelpump-component (canonical, yStart=110), fuelpump739 (yStart=160) }.
No install groups. Slot-fusion/install group:
delta = 110 − 160 = −50
shiftSubtree(fuelpump739, −50): fuelpump739 yStart = 160 + (−50) = 110
TB_1 final Y layout (confirmed by tests):
aircraft684: yStart=0, height=90
port-engine-component: yStart=15, height=60
fuelpump-component: yStart=30, height=30
engine329: yStart=95, height=60 [standalone; no active install]
engine329-fuelpump-component: yStart=110, height=30 ←→ fuelpump739 (co-located)
fuelpump739: yStart=110, height=30
No co-location groups. All assignments from global base applied directly.
TB_2 final Y layout (confirmed by tests):
aircraft684: yStart=0, height=90
port-engine-component: yStart=15, height=60
fuelpump-component: yStart=30, height=30
engine329: yStart=95, height=60
engine329-fuelpump-component: yStart=110, height=30
fuelpump739: yStart=160, height=30 [standalone root]
Y comparison TB_0 → TB_1:
| Entity | TB_0 yStart | TB_1 yStart | Changed? |
|---|---|---|---|
| aircraft684 | 0 | 0 | No |
| port-engine-component | 15 | 15 | No |
| fuelpump-component | 30 | 30 | No |
| engine329 | 15 | 95 | Yes |
| engine329-fuelpump-component | 30 | 110 | Yes |
| fuelpump739 | 30 | 110 | Yes |
Three transitions in one group (breakpointIndex=0):
| Entity | fromCenterY | toCenterY | height | direction |
|---|---|---|---|---|
| engine329 | 15+30=45 | 95+30=125 | 60 | exit (fusedIntoSlots shrank) |
| engine329-fuelpump-component | 30+15=45 | 110+15=125 | 30 | exit (fallback: toCenterY > fromCenterY) |
| fuelpump739 | 30+15=45 | 110+15=125 | 30 | exit (fallback) |
verticalTravel values: |125−45|=80, |125−45|=80, |125−45|=80
maxTravel = 80
widthPx = max(50, 80/4) = max(50, 20) = 50
tickEdge = 'left' (all exit)
Y comparison TB_1 → TB_2:
| Entity | TB_1 yStart | TB_2 yStart | Changed? |
|---|---|---|---|
| aircraft684 | 0 | 0 | No |
| port-engine-component | 15 | 15 | No |
| fuelpump-component | 30 | 30 | No |
| engine329 | 95 | 95 | No |
| engine329-fuelpump-component | 110 | 110 | No |
| fuelpump739 | 110 | 160 | Yes |
One transition in one group (breakpointIndex=1):
| Entity | fromCenterY | toCenterY | height | direction |
|---|---|---|---|---|
| fuelpump739 | 110+15=125 | 160+15=175 | 30 | exit (fusedIntoSlots shrank) |
verticalTravel = |175−125| = 50
widthPx = max(50, 50/4) = 50
tickEdge = 'left'
Settings: chevronOffset=20, minPaddingPx=20, minStraightSectionPx=10, paddingBetweenTransitionsPx=20.
Padding-left depth: In TB_0, fuelpump739 has start === undefined and the deepest chain: installed into engine329-fuelpump-component (+1), which is isPartOf engine329 (+1), which is installed into port-engine-component (+1), which is isPartOf aircraft684 (+1). Depth = 4.
paddingLeftPx = 4 × 20 + 20 = 100
Straight-B after BP_0: fuelpump739 transitions at both BP_0 and BP_1 (consecutive breakpoints) → straight-B is inserted between them.
Padding-right depth: In TB_2, fuelpump739 has no temporal hosting (depth 0, standalone). engine329 has no active installation (depth 0). Deepest chain: fuelpump-component → port-engine-component → aircraft684 = depth 2.
paddingRightPx = 2 × 20 + 20 = 60
| # | Type | timebandIndex | breakpointIndex | minWidthPx | Note |
|---|---|---|---|---|---|
| 1 | padding-left | null | null | 100 | depth=4 |
| 2 | straight-A | 0 | null | 10 | |
| 3 | transition | null | 0 | 50 | BP_0, tickEdge='left' |
| 4 | straight-B | null | null | 20 | fuelpump739 at both BP_0 and BP_1 |
| 5 | straight-A | 1 | null | 10 | |
| 6 | transition | null | 1 | 50 | BP_1, tickEdge='left' |
| 7 | straight-A | 2 | null | 10 | |
| 8 | padding-right | null | null | 60 | depth=2 |
Total totalMinWidthPx = 310 px. Section count = 2 + 3 + 2 + 1 = 8 ✓
Breakpoint tick placement: BP_0 and BP_1 both have tickEdge='left', so their ticks sit at the left edge of their transition sections:
- BP_0 tick at
x = 100 + 10 = 110 - BP_1 tick at
x = 100 + 10 + 50 + 20 + 10 + ...wait, let me compute cumulative lefts:- Section 1 (padding-left): x=0, width=100 → right at 100
- Section 2 (SA-TB_0): x=100, width=10 → right at 110
- Section 3 (transition BP_0): x=110, tickEdge='left' → tick at x=110
- Section 4 (SB): x=160, width=20 → right at 180
- Section 5 (SA-TB_1): x=180, width=10 → right at 190
- Section 6 (transition BP_1): x=190, tickEdge='left' → tick at x=190
- Section 7 (SA-TB_2): x=240, width=10 → right at 250
- Section 8 (padding-right): x=250, width=60 → total=310
This fixture has open-ended timebands: TB_0 has no left metric boundary (null), and TB_2 has no right metric boundary (null). minLinearWidthPx = null (full linearity impossible; cannot scale proportionally without bounded endpoints). isNonLinear = true.
Only TB_1 is fully metric-bounded (BP_0 = 2020-01-01 to BP_1 = 2024-06-01, duration ≈ 143,280,000,000 ms). The open-ended timebands TB_0 and TB_2 receive actualWidthPx = minWidthPx.
At canvas=310 (totalMinWidthPx): all sections at minimum width. actualWidthPx = minWidthPx throughout.
totalWidthPx = 310. totalHeightPx = max(yStart + height in TB_2) = max of aircraft684 (0+90=90), port-engine-component (15+60=75), fuelpump-component (30+30=60), engine329 (95+60=155), engine329-fuelpump-component (110+30=140), fuelpump739 (160+30=190) = 190 px.
timeRange from depth-0 entities (those with no isPartOf parent): aircraft684, engine329, fuelpump739 — all have undefined metric bounds → timeRange = { startMs: 0, endMs: 0 } (default; no metric timestamps on top-level entities).
Structural tree (from mereologicalParentId):
aircraft684 (root)
└── port-engine-component
└── fuelpump-component
engine329 (root)
└── engine329-fuelpump-component
fuelpump739 (root)
Virtual-installed edges (across all timebands):
- engine329 was installed into
port-engine-component→ add engine329 as virtual-installed child of port-engine-component. - fuelpump739 was installed into
engine329-fuelpump-component→ add fuelpump739 as virtual-installed child of engine329-fuelpump-component.
Augmented tree (structural children first alphabetically, then virtual-installed alphabetically):
aircraft684 (Z=0)
└── port-engine-component [structural] (Z=1)
├── fuelpump-component [structural] (Z=2)
└── engine329 [virtual-installed] (Z=3)
└── engine329-fuelpump-component [structural] (Z=4)
└── fuelpump739 [virtual-installed] (Z=5)
DFS preorder: aircraft684=0, port-engine-component=1, fuelpump-component=2, engine329=3, engine329-fuelpump-component=4, fuelpump739=5.
Section boundaries (all at minWidthPx):
| # | Section | Width | xLeft |
|---|---|---|---|
| 1 | padding-left | 100 | 0 |
| 2 | straight-A (TB_0) | 10 | 100 |
| 3 | transition (BP_0) | 50 | 110 |
| 4 | straight-B | 20 | 160 |
| 5 | straight-A (TB_1) | 10 | 180 |
| 6 | transition (BP_1) | 50 | 190 |
| 7 | straight-A (TB_2) | 10 | 240 |
| 8 | padding-right | 60 | 250 |
fuelpump739 center Y per timeband: TB_0 30+15=45, TB_1 110+15=125, TB_2 160+15=175.
nestingDepthStart = 4 (fp739 in TB_0: install-hop to engine329-fuelpump-component, then isPartOf to engine329, then install-hop to port-engine-component, then isPartOf to aircraft684). unknownStart = true → segments[0].xPos = 0 + 4 × 10 = 40.
nestingDepthEnd = 0 (fp739 in TB_2: no temporal hosting, standalone root). unknownEnd = true → segments[8].xPos = 310 − (0+1) × 10 = 300.
N=8 active sections → 9 segments:
| i | Vertex | xPos | yPos | Note |
|---|---|---|---|---|
| 0 | left body face | 40 | 45 | pre-baked; depth=4, unknownStart |
| 1 | SA-TB_0 left | 100 | 45 | T_s = TB_0 |
| 2 | TR-BP_0 left | 110 | 45 | source Y (TB_0 left edge of transition) |
| 3 | SB left | 160 | 125 | destination Y carried here (TB_1) |
| 4 | SA-TB_1 left | 180 | 125 | T_s = TB_1 |
| 5 | TR-BP_1 left | 190 | 125 | source Y (TB_1 left edge of transition) |
| 6 | SA-TB_2 left | 240 | 175 | destination Y carried here (TB_2) |
| 7 | PR left | 250 | 175 | T_s = TB_2 |
| 8 | right body face | 300 | 175 | pre-baked; depth=0, unknownEnd |
The renderer connects consecutive dots: the transition diagonal at BP_0 runs from segment[2] to segment[3] (yPos 45→125). No Y-averaging is needed.
engine329 center Y per timeband: TB_0 15+30=45, TB_1 95+30=125, TB_2 95+30=125.
nestingDepthStart = 2 (engine329 in TB_0: install-hop to port-engine-component, then isPartOf to aircraft684). unknownStart = true → segments[0].xPos = 0 + 2 × 10 = 20.
nestingDepthEnd = 0 (engine329 in TB_2: not installed). unknownEnd = true → segments[8].xPos = 310 − 10 = 300.
| i | Vertex | xPos | yPos | Note |
|---|---|---|---|---|
| 0 | left body face | 20 | 45 | depth=2, unknownStart |
| 1 | SA-TB_0 left | 100 | 45 | |
| 2 | TR-BP_0 left | 110 | 45 | source Y |
| 3 | SB left | 160 | 125 | destination Y carried here |
| 4 | SA-TB_1 left | 180 | 125 | |
| 5 | TR-BP_1 left | 190 | 125 | source = destination = 125 (no Y change) |
| 6 | SA-TB_2 left | 240 | 125 | |
| 7 | PR left | 250 | 125 | |
| 8 | right body face | 300 | 125 | depth=0, unknownEnd |
engine329 does not change Y at BP_1 (yStart=95 in both TB_1 and TB_2). Segments 5 and 6 have the same yPos → the BP_1 transition renders as a horizontal rectangle for this entity.
| Entity | isSystem | isSystemComponent | everHadFusedIntoSlots | Style key |
|---|---|---|---|---|
| aircraft684 | true | false | false | rootSystem |
| port-engine-component | true | true | false | componentSlot |
| fuelpump-component | false | true | false | componentSlot |
| engine329 | true | false | true | installedSystem |
| engine329-fuelpump-component | false | true | false | componentSlot |
| fuelpump739 | false | false | true | installedArtefact |
port-engine-componentis bothisSystemandisSystemComponent;isSystemComponenttakes precedence since thecomponentSlotcondition is checked beforerootSystemin the classification table.