Fix connector routing and enhance shape-aware layout features#9
Conversation
… a box face When several connectors converge on the same box face and their source boxes don't overlap the target on the perpendicular axis, FacingAnchors/ AlignedCoordinate independently clamps each connector's anchor to the same boundary point, so the connectors visually merge and overlap instead of landing at distinct points along the face. The batch Route(boxes, connections, options) overload now detects when a box face is claimed by more than one connector and redistributes their anchors evenly across the face (ordered by each connector's counterpart box position), matching the approach already used by the layered layout's PortDistributor for intra-package edges. The single-connection overload is unchanged and documented as unsuitable when several connectors may share a face. Adds a regression test reproducing the exact box geometry from DictionaryMark's generated diagram, confirming independent routing collapses the three connectors onto an identical target point while batch routing spreads them apart in source order.
…ons API - ConnectorRouter: batch Route() now routes connections sequentially and treats each already-routed line segments as thin soft obstacles for subsequent connections, so parallel connectors fan out into separate corridors instead of overlapping along a shared trunk. - HierarchicalLayoutAlgorithm.RouteCrossContainerEdges now batches all of a scope cross-container edges into a single ConnectorRouter.Route call instead of looping the single-connection overload, fixing the same anchor-collapse/corridor-overlap bug at the library own high-level entry point. - LayoutEngine.Layout no longer takes a separate LayoutOptions argument. LayoutGraph is already an IPropertyHolder, so a caller-supplied options object at this entry point was a second, potentially conflicting way to configure the exact same settings the graph itself can carry. Callers now configure the graph directly (e.g. graph.Set(CoreOptions.Algorithm, "layered")); ILayoutAlgorithm.Apply own LayoutOptions parameter is unchanged, since it is the cascading mechanism for nested-scope options, not a duplicate of graph-level configuration. - Updated LayoutEngineTests, GalleryWriter/GalleryShowcaseTests, and the design docs/XML docs/user guide examples to match. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ppearance - ConnectorRouter/OrthogonalEdgeRouter: fix corridor-overlap collision between routed connectors sharing a corridor. - HierarchicalLayoutAlgorithm: fix anchor-coordination gap where leaf-routed and cross-container edges were routed as two independent, anchor-unaware batches, causing multiple connectors to land on the exact same shared-face anchor point. Conflicted direct-direct edges are now promoted into the cross-container routing batch so all edges touching a shared face are coordinated together. - LayoutGraphNode: add Shape, Keyword, and Compartments properties so a caller can select a node's full box appearance on the input graph, before layout, instead of only on the placed LayoutBox. LayeredLayoutAlgorithm, ContainmentLayoutAlgorithm, and HierarchicalLayoutAlgorithm all propagate the three properties unchanged onto the placed box (or view node); each defaults to a plain rectangle with no keyword or compartments, so existing flat-graph behavior is unaffected. - Add regression tests for both routing fixes and propagation tests for the new box-appearance properties across all three algorithms. - Add a "Box appearance" gallery diagram/section/test demonstrating Shape, Keyword, and Compartments on a folder container with keyworded and compartmented leaf boxes. - Update the Layout Graph design doc, requirements, and user guide to document the new properties. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…thm containers HierarchicalLayoutAlgorithm previously reserved a hardcoded 24px title band above a labelled container's children, regardless of theme or whether the container also carries a Keyword. At default theme metrics a container with both a label and a keyword line actually needs ~44px, so the fixed band would let the label overflow into the children's content. - LayoutGraphNode: add a nullable TitleHeight override (logical pixels). Null (the default) preserves the existing generic default band; a caller sets it explicitly to match a specific theme's computed title-area height when needed. - HierarchicalLayoutAlgorithm: resolve the title band via a new ResolveTitleHeight helper (node.TitleHeight ?? DefaultContainerTitleHeight), applied consistently in both container sizing and child-offset composition. Still gated on the container carrying a Label, matching existing behavior. - Add regression tests: override replaces the default band height and offsets nested children accordingly; an unlabelled container ignores the override. - Update the HierarchicalLayoutAlgorithm and Layout Graph design docs, the Layout Graph requirements, and the user guide. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- add shape-geometry hints and shape-aware face routing so connectors respect folder tabs and rounded-corner extents - distribute shared-face anchors across usable extents, project folder top anchors to the recessed body top, and fall back when a natural face is unusable - stop soft-obstacle detours from publishing leave-and-return loops and update tests and compliance documents Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds a Shape-aware connectors gallery section demonstrating the folder tab-avoidance / surface-projection anchor fix: an external Client node connects into a Utilities folder container from above, and the connector correctly lands on the folder's recessed top edge instead of floating above it or scraping the tab. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… case The previous version connected Client to a descendant inside the folder (a cross-container edge), which is excluded from the root scope's own leaf view and so ignored the Direction override entirely -- Client landed beside the folder instead of above it, exercising a right-face anchor rather than the intended top-face tab-avoidance path. Connect directly to the folder node itself instead: a direct root member, so the edge belongs to the root's own leaf view and its ranking honors the downward flow direction, reliably placing Client above the folder and exercising the actual tab-avoidance/surface-projection behavior the section is meant to demonstrate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
RenderBoxTitle/RenderBoxCompartments in both SvgRenderer and SkiaRasterRenderer positioned the keyword/label/compartment text starting at box.Y, ignoring the folder tab's recessed body top. For wide folders this made the title text float in the empty notch above the actual outline instead of sitting inside the main body, contrary to standard SysML/UML folder notation where the tab is purely decorative and never carries text. Both renderers now resolve the title area's top via a shared ResolveTitleAreaTop helper that offsets by the resolved folder tab height for Shape.Folder boxes (reusing the existing FolderTabHeight override / BoxMetrics fallback), leaving all other shapes unaffected. Added SvgRenderer_Render_FolderBox_TitleRecessedBelowTab regression test. Regenerated gallery (folder-top-face-anchor.svg, box-appearance.svg) to confirm keyword/title text now renders below the tab in both showcases. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Container children were composed using only the ordinary label/keyword title band, so a Folder container's tab height was never reserved above its nested children -- once the title text was recessed below the tab (previous commit), children could overlap the recessed title. HierarchicalLayoutAlgorithm now adds a ResolveContentOffsetHeight helper that folds the tab height into both the container's effective size and its children's vertical offset for Shape.Folder containers. Also closes the note-shape gap flagged in NoteGeometry's own doc comment: the folded top-right corner was not excluded from the connectable extent, so a connector could anchor inside the cut-off triangle instead of the real outline. NoteGeometry now excludes the fold-affected portion of the top face (near the right edge) and the right face (near the top edge), mirroring FolderGeometry's tab-exclusion pattern; the remaining extents lie exactly on the bounding box so no projection offset is needed. Added a new "shape-gallery" showcase diagram/section demonstrating every BoxShape side by side, each with content appropriate to it (rectangle and rounded-rectangle with a keyword and compartment, a folder with a nested child, a note with free-form text), proving every shape reserves enough space for its own content without overlapping its non-rectangular features. Added regression tests for both fixes and regenerated the gallery. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ConnectorRouter Root cause: LayeredLayoutAlgorithm.Apply mapped each LayoutGraphNode to an abstract LayerNode(Width, Height), discarding all shape metadata (Shape, FolderTabWidth, FolderTabHeight, RoundedCornerRadius). Its own internal routing pipeline under Engine/Layered/ (PortDistributor, LongEdgeJoiner, AxisTransform, etc.) therefore anchored connectors purely to the plain bounding rectangle on every face, with no awareness of shapes such as Folder (raised tab) or Note (folded corner). This is the default leaf-edge algorithm used for the vast majority of same-scope edges, including the common case of two siblings where one is a Folder-shaped container (e.g. Client -> pkg in the FolderTopFaceAnchor gallery diagram), so those connectors visibly floated above the real drawn shape outline (y=140, the bounding-box top) instead of terminating at the recessed body-top surface (y=164 = box.Y + tabHeight) that ConnectorRouter already computes correctly for containment/cross-container edges. Fix: extend LayerNode with shape metadata (Shape, RoundedCornerRadius, FolderTabWidth, FolderTabHeight, Label, RealWidth, RealHeight), thread it through LayeredLayoutAlgorithm.Apply and SwapNodeAxes, and reuse (rather than duplicate) ConnectorRouter's already-tested shape geometry classes (RectangleGeometry, RoundedRectangleGeometry, NoteGeometry, FolderGeometry, ResolveShapeGeometry) by widening them from private to internal. Add a direction-to-real-face resolver (Right: src=Right/tgt=Left, Left: src=Left/tgt=Right, Down: src=Bottom/tgt=Top, Up: src=Top/tgt=Bottom) in the new ShapeAnchorSupport.cs, and use it to: - Restrict PortDistributor's evenly-spaced port band to each shape's usable connectable extents on the relevant real face (instead of the full bounding span), for non-dummy, non-Rectangle-shaped nodes. - Apply each shape's ProjectToSurface inward offset in LongEdgeJoiner so the final routed segment terminates on the true recessed/excluded-adjusted surface rather than the bounding box. Plain Rectangle-shaped (and shapeless) nodes and dummy nodes (used for long-edge splitting) take the original, unmodified code path, so existing diagrams are unaffected (verified via full gallery regeneration - only folder-top-face-anchor.svg changed, with the Client -> pkg connector now terminating at y=164 instead of y=140). Added 14 new tests across PortDistributorTests, LongEdgeJoinerTests, and LayeredLayoutAlgorithmTests (one per LayoutDirection plus Note fold-exclusion plus Rectangle byte-identical regression), verified the inward-projection sign for all 4 directions x source/target by tracing AxisTransform's abstract-to- screen mapping, and updated design docs (connector-router.md, layered-pipeline.md, layered-layout-algorithm.md), requirements (docs/reqstream/rendering-layout/), verification docs, and .reviewmark.yaml to record that shape-aware anchoring now applies uniformly to both ConnectorRouter-routed edges and LayeredLayoutAlgorithm's own leaf-edge routing. Full test suite: 217/217 passing in Layout.Tests (was 203), no regressions in any other project. fix.ps1/lint.ps1 clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ength Root cause: OrthogonalEdgeRouter.AStar priced a soft-obstacle move with a flat, boolean-gated 60-unit penalty (SoftObstaclePenalty) regardless of how much of the move actually overlapped an already-routed connector's corridor. In narrow-gap real-world geometry (e.g. a small box sitting just below a much wider one across a 48px gap), the sparse Hanan grid collapses a connector's entire multi-hundred-pixel horizontal corridor into a single grid move, so a 500+px visual overlap cost exactly the same flat 60 units as a trivial few-pixel overlap. Meanwhile the bounded cost of detouring to a nearby alternate lane (roughly 2x TurnPenalty + 2x the lane offset) was consistently a little higher than that flat 60, so A* always kept the long overlap no matter how visually merged the result looked. This was traced and reproduced exactly against the reported DictionaryMark diagram (OtsDependencies -> DictionaryMark, three fan-out connectors merging onto identical y=1271 corridors for hundreds of pixels). Fix: replace the flat SoftObstaclePenalty with a cost proportional to the overlap length between the candidate move and each soft obstacle. Added SoftObstacleOverlapLength(...), mirroring SegmentBlocked's branch logic but returning the summed overlapping length instead of a bool, and two named constants (SoftObstacleBaseCost = 10.0, SoftObstaclePerUnitLengthCost = 1.0) replacing the single flat penalty. A short, incidental overlap therefore stays cheap (never worth a pointless detour, preserving the legitimate endpoint-adjacent shared-approach-corridor behavior), while an extended overlap becomes proportionally expensive and a bounded-cost lane change is preferred instead - for any number of fan-out/fan-in connectors and any box geometry, not a special case tuned to the reported 3-edge scenario. Tests added (TDD, confirmed red against the flat-penalty code, green after the fix): - ConnectorRouterTests: three new batch-API regression tests reproducing the exact reported defect shape (narrow vertical gap, 3/5 fan-out edges, and a fan-in variant with distinct source boxes), asserting via a new AssertNoCollinearOverlapAcrossLines helper that no two connectors' interior segments ride the same grid line for more than a trivial span. - OrthogonalEdgeRouterTests: RouteWithStatus_LongSoftObstacleOverlap_ PrefersAlternateLane pins the cost-function fix directly (a synthetic soft obstacle spanning the entire corridor now triggers a detour), with a RouteWithStatus_ShortSoftObstacleOverlap_KeepsStraightRoute companion confirming short/incidental overlaps remain tolerated. Full existing suite (386 tests across all TFMs) passes with zero regressions; before the fix, 3 of the 5 new tests failed as expected (red step) while the other 2 (short-overlap tolerance and the distinct-source- boxes fan-in variant) already passed. `pwsh ./gallery.ps1` produced no SVG diffs (no existing gallery sample exercises this narrow-gap geometry). Companion artifacts updated: design docs for ConnectorRouter and OrthogonalEdgeRouter, a new OrthogonalEdgeRouter requirement (Rendering-Layout-OrthogonalEdgeRouter-AvoidsExtendedSoftOverlap) in the reqstream YAML, and the corresponding verification docs (new test-scenario bullet, requirements-coverage entry, and an additional-regression-coverage note on the ConnectorRouter side). Real-world verification: rebuilt SysML2Tools (Release, clean) against this branch's Rendering source via its ProjectReference, then regenerated DictionaryMark's DictionaryMarkSystemView.svg/.png through the SysML2Tools CLI. Numerically confirmed in the raw SVG path data that the third OtsDependencies -> DictionaryMark connector (previously overlapping a second connector's corridor for 529.6px at y=1271) now takes an 8px vertical detour to y=1263, eliminating the overlap; the other two connectors' short endpoint-adjacent touch at y=1271 remains (by design, non-overlapping). Visually confirmed via the regenerated PNG that the three connectors now separate into distinct rows with no other regressions (nesting and labels intact). DictionaryMark's generated SVG/PNG are gitignored, so no cleanup was required there; both DictionaryMark and SysML2Tools working trees were confirmed clean before and after. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…anchor gallery caption The folder-top-face-anchor showcase connects two root-scope nodes directly to the folder itself, not across a containment boundary, so describing it as a 'cross-container edge' was inaccurate. Reworded the gallery caption, GalleryCatalog description, and GalleryShowcaseTests doc comment to correctly describe it as an edge approaching a folder container from above. Found by the formal-review agent (GPT-5.4-mini) during pre-merge review of the Rendering-Gallery review set; all other 18 affected review sets and the full-diff code review passed with no issues. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This pull request improves shape-aware diagram routing and rendering consistency across the layout pipeline by propagating per-node shape metadata and optional resolved geometry hints (rounded-corner radius, folder tab width/height) from LayoutGraphNode through layout algorithms into LayoutBox, and by enhancing both ConnectorRouter and the layered pipeline’s internal routing stages to honor connectable extents and surface projections for non-rectangular shapes. It also simplifies the default LayoutEngine.Layout facade to take configuration exclusively from the LayoutGraph, with accompanying documentation, requirements traceability, gallery outputs, and expanded regression tests.
Changes:
- Added/propagated box appearance and shape-geometry hint fields (
Shape,Keyword,Compartments,RoundedCornerRadius,FolderTabWidth,FolderTabHeight) through graph → algorithms → placed boxes, and updated SVG/Skia renderers to consume them. - Made same-scope layered routing shape-aware by reusing
ConnectorRouter’s internal shape geometry (extent restriction + endpoint projection) inPortDistributor/LongEdgeJoiner. - Enhanced orthogonal routing with soft obstacles (length-proportional overlap cost) and route cleanup; simplified
LayoutEngine.LayoutAPI and updated docs/reqstream/verification + gallery artifacts accordingly.
Reviewed changes
Copilot reviewed 56 out of 59 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| test/DemaConsulting.Rendering.Tests/LayoutTests.cs | Expands LayoutBox construction test to include new shape-geometry hint fields. |
| test/DemaConsulting.Rendering.Tests/LayoutGraphTests.cs | Adds test ensuring shape-geometry hints default to null and are settable on graph nodes. |
| test/DemaConsulting.Rendering.Svg.Tests/SvgRendererTests.cs | Adds regression test verifying folder title/keyword text is recessed below the tab. |
| test/DemaConsulting.Rendering.Layout.Tests/LayoutEngineTests.cs | Updates tests for LayoutEngine.Layout(graph) API and graph-driven algorithm selection. |
| test/DemaConsulting.Rendering.Layout.Tests/LayeredLayoutAlgorithmTests.cs | Adds propagation tests and multiple shape-aware routing regression tests for layered output. |
| test/DemaConsulting.Rendering.Layout.Tests/HierarchicalLayoutAlgorithmTests.cs | Adds tests for TitleHeight override behavior, folder tab child offset, and shared-face routing coordination. |
| test/DemaConsulting.Rendering.Layout.Tests/Engine/OrthogonalEdgeRouterTests.cs | Adds tests for soft-obstacle behavior and eliminating redundant waypoint revisits. |
| test/DemaConsulting.Rendering.Layout.Tests/Engine/Layered/PortDistributorTests.cs | Adds shape-aware port-distribution tests (folder tab exclusion, note fold exclusion) and refactors helpers for direction. |
| test/DemaConsulting.Rendering.Layout.Tests/Engine/Layered/LongEdgeJoinerTests.cs | Adds shape-aware endpoint projection tests and refactors helpers for direction. |
| test/DemaConsulting.Rendering.Layout.Tests/ContainmentLayoutAlgorithmTests.cs | Adds propagation test for shape/keyword/compartments and geometry hints through containment packing. |
| test/DemaConsulting.Rendering.Layout.Tests/ConnectorRouterTests.cs | Extends test helper to construct shaped boxes with optional geometry hints; adds many shape-aware and batching regressions. |
| test/DemaConsulting.Rendering.Gallery/GalleryWriter.cs | Updates gallery writer to use graph-configured layout via LayoutEngine.Layout(graph). |
| test/DemaConsulting.Rendering.Gallery/GalleryShowcaseTests.cs | Updates showcase tests for graph-driven algorithm/direction selection; adds new gallery render tests. |
| test/DemaConsulting.Rendering.Gallery/GalleryDiagrams.cs | Adds new gallery diagrams demonstrating box appearance and shape-aware connector routing. |
| test/DemaConsulting.Rendering.Gallery/GalleryCatalog.cs | Registers new gallery sections and SVG outputs. |
| src/DemaConsulting.Rendering/NamespaceDoc.cs | Updates public-facing docs to reflect the new LayoutEngine.Layout(graph) facade. |
| src/DemaConsulting.Rendering/LayoutTree/LayoutBox.cs | Extends LayoutBox to carry optional resolved shape-geometry hints. |
| src/DemaConsulting.Rendering/Graph/LayoutGraphNode.cs | Adds Shape, Keyword, Compartments, TitleHeight, and shape-geometry hint properties to graph nodes. |
| src/DemaConsulting.Rendering.Svg/SvgRenderer.cs | Resolves folder/tab and rounded-corner geometry from LayoutBox hints; recesses folder title area below tab. |
| src/DemaConsulting.Rendering.Skia/SkiaRasterRenderer.cs | Mirrors SVG renderer’s hint resolution and folder title-area recession for raster output. |
| src/DemaConsulting.Rendering.Layout/NamespaceDoc.cs | Updates layout package namespace docs to match the new facade usage. |
| src/DemaConsulting.Rendering.Layout/LayoutEngine.cs | Removes LayoutOptions parameter from facade; resolves algorithm from graph and seeds algorithms with empty options. |
| src/DemaConsulting.Rendering.Layout/LayeredLayoutAlgorithm.cs | Threads shape metadata/hints into engine nodes and propagates appearance/hints into placed boxes. |
| src/DemaConsulting.Rendering.Layout/HierarchicalLayoutAlgorithm.cs | Adds container title-height override, folder tab child-offset reservation, and unified routing for cross-container + leaf edges. |
| src/DemaConsulting.Rendering.Layout/Engine/OrthogonalEdgeRouter.cs | Adds soft obstacles, proportional overlap cost, axis expansion for soft obstacles, and revisit-removal cleanup. |
| src/DemaConsulting.Rendering.Layout/Engine/Layered/ShapeAnchorSupport.cs | New helper bridging layered pipeline direction/axes to ConnectorRouter shape geometry APIs. |
| src/DemaConsulting.Rendering.Layout/Engine/Layered/PortDistributor.cs | Uses ConnectorRouter shape geometry to restrict port bands for shaped nodes while keeping rectangle fast path. |
| src/DemaConsulting.Rendering.Layout/Engine/Layered/LongEdgeJoiner.cs | Projects shaped endpoints inward to real outlines using ConnectorRouter shape geometry. |
| src/DemaConsulting.Rendering.Layout/Engine/Layered/LayeredGraph.cs | Ensures node axis swapping preserves all new LayerNode shape metadata fields. |
| src/DemaConsulting.Rendering.Layout/Engine/InterconnectionLayoutEngine.cs | Expands LayerNode to carry shape metadata and real (non-swapped) dimensions for geometry resolution. |
| src/DemaConsulting.Rendering.Layout/ContainmentLayoutAlgorithm.cs | Propagates node appearance and geometry hints into packed boxes. |
| docs/verification/rendering/layout-tree.md | Adds verification coverage for LayoutBox shape-geometry hints. |
| docs/verification/rendering/layout-graph.md | Adds verification coverage for node shape-geometry hints storage and propagation through algorithms. |
| docs/verification/rendering-layout/layered-layout-algorithm.md | Adds verification scenarios/requirement mapping for shape-aware routing in layered layout. |
| docs/verification/rendering-layout/engine/orthogonal-edge-router.md | Adds verification scenarios/requirement mapping for soft obstacles and revisit prevention. |
| docs/verification/rendering-layout/engine/layered-pipeline.md | Adds verification scenarios/requirement mapping for shape-aware anchor restriction and projection stages. |
| docs/verification/rendering-layout/connector-router.md | Documents new shape-aware anchor behavior and shared-face distribution; notes additional regressions. |
| docs/user_guide/introduction.md | Updates facade usage, explains appearance selection, container title height override, and folder hint partials. |
| docs/reqstream/rendering/layout-tree.yaml | Adds requirement for placed-box shape-geometry hints. |
| docs/reqstream/rendering/layout-graph.yaml | Adds requirements for box appearance, container title-height override, and shape-geometry hints. |
| docs/reqstream/rendering-layout/layered-layout-algorithm.yaml | Adds requirement for shape-aware routing in LayeredLayoutAlgorithm. |
| docs/reqstream/rendering-layout/engine/orthogonal-edge-router.yaml | Adds requirements for no waypoint revisits and avoiding extended soft-obstacle overlap. |
| docs/reqstream/rendering-layout/engine/layered-pipeline.yaml | Adds requirement for layered pipeline shape-aware anchors/projection matching ConnectorRouter. |
| docs/reqstream/rendering-layout/connector-router.yaml | Adds requirements for shape-aware anchors and shared-face distribution over connectable extents. |
| docs/gallery/shape-gallery.svg | New rendered gallery artifact demonstrating all shapes with content clearance. |
| docs/gallery/gallery.md | Adds new gallery sections for box appearance and shape-aware connectors. |
| docs/gallery/folder-top-face-anchor.svg | New rendered gallery artifact demonstrating folder tab-aware anchoring. |
| docs/gallery/box-appearance.svg | New rendered gallery artifact demonstrating shape/keyword/compartments appearance selection. |
| docs/design/rendering/layout-tree.md | Updates design to include LayoutBox shape-geometry hints. |
| docs/design/rendering/layout-graph.md | Updates design to include node appearance fields, title-height override, and geometry hints propagation. |
| docs/design/rendering-layout/layered-layout-algorithm.md | Updates design to reflect shape-aware routing via layered pipeline. |
| docs/design/rendering-layout/hierarchical-layout-algorithm.md | Updates design to document title-height override and default title-band behavior. |
| docs/design/rendering-layout/engine/orthogonal-edge-router.md | Updates design to document soft obstacles, proportional overlap cost, and revisit cleanup. |
| docs/design/rendering-layout/engine/layered-pipeline.md | Updates design to document LayerNode metadata and cross-unit dependency on ConnectorRouter geometry. |
| docs/design/rendering-layout/default-layout.md | Updates design for new LayoutEngine API and graph-only configuration model. |
| docs/design/rendering-layout/connector-router.md | Expands design doc for shape geometry abstraction, anchor selection, and batch distribution/steering behavior. |
| .reviewmark.yaml | Registers connector-router design doc for review tracking/traceability. |
| .agent-logs/developer-connector-router-quality-retry-20260704-2205.md | Adds agent execution/report log for prior work. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…API simplification A prior commit (8a1ce80) removed the separate LayoutOptions argument from LayoutEngine.Layout and renamed/removed several LayoutEngineTests methods, but did not update the reqstream requirements or verification doc for the DefaultLayout unit to match. This left 4 requirements referencing nonexistent test names (Layout_OptionsDeclareLayered_..., Layout_ OptionsDeclareContainment_..., Layout_GraphDeclarationOverridesOptions, Layout_NullOptions_Throws), causing CI's reqstream --enforce check to report them unsatisfied: - Rendering-Layout-DefaultLayout - Rendering-Layout-LayoutEngine-Resolution - Rendering-Layout-LayoutEngine-FlatEquivalence - Rendering-Layout-LayoutEngine-Validation Updated docs/reqstream/rendering-layout/default-layout.yaml and docs/verification/rendering-layout/default-layout.md to reference the actual current test names (Layout_GraphDeclaresLayered_..., Layout_ GraphDeclaresContainment_..., Layout_NullGraph_Throws, Layout_ NullRegistry_Throws) and to describe resolution against an explicit graph-level declaration only, matching the simplified single-argument Layout API. Verified locally with 'dotnet reqstream --enforce' that all 4 requirements are now satisfied; full test suite (668 tests x 3 TFMs) and fix.ps1/lint.ps1 remain clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- OrthogonalEdgeRouter.AStar: hoist the 'softObstacles ?? []' null-coalesce out of the per-neighbor A* loop into a single resolved local, avoiding a fresh empty-array allocation on every iteration when no soft obstacles are supplied. - PortDistributorTests: rename the misleadingly-named 'localX'/'localY' locals (derived from the internal AugPortY*/AugY axis fields) to 'portOffsetAlongTopFace'/'portOffsetAlongRightFace' with a clarifying comment about the Down-direction axis swap, so the variable names match the real-world axis they represent rather than the internal field names they're read from. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- LayoutGraphNode.Compartments: guard the setter so a null assignment is coerced to an empty list, keeping the non-nullable public property contract safe against a caller assigning null (only a compile-time warning previously, would have thrown at runtime on first iteration). Added a regression test proving the coercion. - PortDistributor.Apply: the target-side port-band branch is already guarded by 'else' of augNodes[ni].IsDummy, so ni is always a valid real- node index; replaced the 'nodes[ni < n ? ni : 0]' fallback with a direct 'nodes[ni]' and removed the now-unused 'n' local, avoiding a silent wrong-node computation if the invariant ever broke. - SvgRenderer/SkiaRasterRenderer ResolveRoundedCornerRadius/ ResolveFolderTabWidth/ResolveFolderTabHeight: clamp caller-supplied LayoutBox shape-geometry hints to a non-negative value before use, mirroring ConnectorRouter's existing hint-resolution clamping, so a negative hint can no longer produce invalid SVG attributes, inverted folder recesses, or invalid Skia rounded-rect radii. - ConnectorRouterTests: removed a stray extra blank line for formatting consistency. Verified: full test suite (669 tests x 3 TFMs) passing, fix.ps1/lint.ps1 clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This pull request introduces significant improvements to the shape-aware routing logic in the
ConnectorRouterand updates the layered layout pipeline to leverage these enhancements. The changes ensure that connectors and ports respect the true geometric extents and projections of various node shapes (including folders with tabs, rounded rectangles, and notes), improving both the visual accuracy and flexibility of connector routing. Documentation and requirement tracking are updated to reflect these new behaviors, and cross-unit dependencies are now clearly documented.ConnectorRouter and Layered Pipeline shape-awareness:
ConnectorRouterto independently resolve folder tab width and height hints, allowing partial hints to maintain shape-aware routing; the router now computes connectable extents and surface projections per face for all supported shapes (Rectangle,RoundedRectangle,Note,Folder). [1] [2]PortDistributor,LongEdgeJoiner) to internally consumeConnectorRouter's shape-geometry resolution, ensuring consistent connectable-extent restrictions and surface projections for both cross-container and same-scope edges. [1] [2] [3]Documentation and requirements:
connector-router.mddesign document to describe the new shape geometry abstraction, anchor selection logic, shared-face distribution in batch routing, and the cross-unit dependency with the layered pipeline. [1] [2] [3].reviewmark.yamlfor traceability.Default layout API simplification:
LayoutEngine.LayoutAPI to remove the redundantoptionsparameter, ensuring all configuration is carried by theLayoutGraphitself and clarifying the algorithm resolution logic in the documentation. [1] [2] [3] [4]Quality and compliance: