Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,76 @@ same-face crowding (no port-spacing-by-width work):
- The gallery entry must be wired into `GalleryCatalog`/`GalleryIndex` like existing examples, so
it renders via `gallery.ps1` and appears in `docs/gallery/README.md`.

## Unify all text rendering onto a single `LayoutLabel`/`LayoutText` primitive

Today eight `LayoutNode` types carry text to render, and only one of them —
`LayoutLabel` — is a fully-resolved primitive (`X`/`Y`/`Align`/`Weight`/`Style`/`FontSize` all
precomputed by the layout engine; a renderer just paints it, no discretion involved). Every other
text-bearing type hands the renderer a raw string plus surrounding geometry and expects the
renderer to *derive* the text position itself:

- `LayoutBox` — `Label`, `Keyword`, `Compartments[].Title`/`Rows` (renderer computes title cursor Y,
keyword/label stacking, and whether to center — see the box-title-centering discussion below).
- `LayoutPort` — `ExternalLabel`, `InternalLabel` (renderer computes the per-`Side` offset/anchor).
- `LayoutLine` — `MidpointLabel` (renderer computes the midpoint placement and an occluding
background rect, see below).
- `LayoutLifeline` — `Label` (renderer computes header-box text position).
- `LayoutGrid` — `LayoutGridCell.Text` (renderer computes per-cell text position/alignment).
- `LayoutBand` — `Label` (renderer computes edge-header position).
- `LayoutBadge` — `Label` (renderer computes beside-badge position).

Because `SvgRenderer` and `SkiaRasterRenderer` each independently reimplement these formulas, they
can silently drift (confirmed: both renderers duplicated `RenderBoxTitle`/`ResolveTitleAreaTop`
verbatim, which is exactly the kind of duplication that let a box-title-centering gap go unnoticed
in one renderer's math without the other necessarily agreeing).

**Proposed direction (large, deliberately deferred — not a quick fix):** have the layout engine
itself emit fully-resolved `LayoutLabel` (or a renamed `LayoutText`) nodes for *all* of the above —
i.e. `LayoutBox`/`LayoutPort`/`LayoutLine`/etc. stop carrying raw label strings and instead the
engine synthesizes child `LayoutLabel` nodes at already-resolved positions, alongside the
geometry-only parent node. This is the same pattern `LayoutLabel` already proves works — it just
isn't used everywhere yet. Renderers would then have zero discretion over text *positioning*
anywhere in the tree, only over drawing mechanics (glyph shaping, `SKPaint` vs. SVG `<text>`
fitting, anti-aliasing) — which is where renderer-specific discretion actually belongs.

This would also need `LayoutLabel` to gain an optional background-color field: today the
occluding background painted behind `LayoutLine.MidpointLabel` (so the label reads cleanly over a
crossing connector) is a separate, hardcoded mechanism in each renderer
(`RenderLineLabel`/`RenderLineMidpointLabel`, and the SVG `label-bg` filter), always using
`theme.BackgroundColor` — not a general, configurable property on `LayoutLabel` itself. Folding
that occlusion behavior into a first-class `LayoutLabel.BackgroundColor` (or similar) would let one
rendering path cover both the plain-label and occluded-label cases.

**Scope note:** this is a genuine breaking change to the `LayoutTree` model (every box/port/
line/grid/band/badge construction site across the layout engine would need to synthesize label
children instead of carrying raw strings), so it needs a proper planning pass before
implementation — not an opportunistic fix. A smaller, lower-risk interim step (extracting today's
duplicated per-renderer formulas into shared static helpers, e.g. alongside `BoxMetrics`, so both
renderers call one function instead of two independent copies) can close the immediate
divergence-risk gap without committing to the full model change — see the leaf-box title
centering item below, which is exactly this kind of interim fix.

## Leaf-box title should be vertically centered — resolved

**Resolved.** Added `BoxMetrics.TitleCursorTop(LayoutBox, Theme)`, a shared formula both
`SvgRenderer.RenderBoxTitle` and `SkiaRasterRenderer.RenderBoxTitle` now call. For a leaf box
(`Children.Count == 0 && Compartments.Count == 0`) the title block is centered vertically within
the box's own content height (excluding port-reserved `ContentInset*` margins); containers with
children or compartments keep the existing top-pinned header behavior unchanged. This also closed
the renderer-duplication gap for this one formula, as the "interim step" described above.

## Canvas margin: box edges can sit flush against the rendered viewport boundary — resolved

**Resolved.** Root cause: `MergeRegionDecomposer.LevelFootprint`'s cross-axis extent computation
used `graph.Nodes[i].Width`/`Height`, which for `LayoutDirection.Down`/`Up` are axis-swapped by
`AxisTransform.NormalizeInputAxes` (`SwapNodeAxes`) and no longer represent the node's true screen
width/height. `InterconnectionLayoutEngine.Place`'s equivalent (and correct) formula uses its own
local, never-swapped `nodes` parameter, so it never hit this. Fixed by reading
`RealWidth`/`RealHeight` (the caller's true, never-swapped dimensions, already carried on
`LayerNode` for exactly this purpose) instead of `Width`/`Height` in the cross-axis loop. Confirmed
fix via `boundary-ports-showcase-vertical.svg`: `viewBox` width grew from `312` (flush against the
`Operator` box's right edge at `x=311`) to the expected `331` (full padding on both sides).

## Process note

When a future session identifies further deferred/advisory work, add it here rather than letting
Expand Down
Binary file modified docs/gallery/boundary-ports-showcase-deep-chain.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions docs/gallery/boundary-ports-showcase-deep-chain.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/gallery/boundary-ports-showcase-horizontal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions docs/gallery/boundary-ports-showcase-horizontal.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/gallery/boundary-ports-showcase-vertical.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions docs/gallery/boundary-ports-showcase-vertical.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 12 additions & 12 deletions docs/gallery/containment-packed.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading