Give the Streckenplaner real track CRUD and a self-fitting layout - #13
Merged
Conversation
The 2D planner was a single hard-coded 8-segment stadium loop. It now builds
arbitrary plans: append Gerade/Kurve/Weiche/Bahnhof, duplicate, reorder, delete,
or start from an Oval/Acht/Punkt-zu-Punkt template, with a Weiche splitting the
plan into a Stammgleis and its own branch strand that can be built on further.
Core:
- Domain gains CurveDirection/SwitchRoute and the strand/branch fields on
TrackSegment; Application gains the pure TrackPlanService (append/insert-switch/
duplicate/reorder/delete/templates) behind ITrackPlanService, TrackSegmentDto +
mapping, and ITrackSegmentRepository; Infrastructure persists segments via
SqliteTrackSegmentRepository on the existing BaseRepository.
- Deleting a Weiche cascades through its branch, including nested Weichen.
Layout (Trackify/Helpers/TrackGeometry.cs) is now turtle graphics over the strand
graph instead of a closed form, in two passes: the walk lays every strand out in
unbounded world coordinates, then the emit pass bakes in the scale/offset that
centers the finished plan on the 900x600 canvas and shrinks it (never enlarges)
when it would reach into the margin the labels and sensor markers need. A plan can
therefore grow in any direction without walking off the visible area — before,
the walk started at a fixed (150,300) and the Oval sat in the left third.
Position fixes that came out of that:
- A curve's outward normal is derived from the tangent, like a straight's, instead
of "away from the arc center" — the latter flips between left and right curves,
so speed labels and sensor markers changed sides mid-plan.
- The direction arrow is emitted as path data (TrackSegment.ArrowPathData) rather
than a triangle rotated by a bound RenderTransform: a {Binding} inside a
transform has no DataContext to inherit — transforms aren't in the visual tree —
so the angle never arrived, and the old geometry's negative coordinates left the
arrow's origin up to the renderer.
- The Acht template's second lobe leads with its turn. Leading with straights, as
the first lobe does, retraced its opening Gerade and Bahnhof piece-for-piece from
the same anchor pose: two segments stacked at identical coordinates.
- A strand nothing branches into (a branch whose Weiche is gone, e.g. from a plan
saved mid-write) is parked on its own lane below the plan instead of silently
keeping the position it had in the previous layout.
- ApplyPlan computes the layout once instead of twice (segments + ballast path).
UI: the planner page is a splitter-resizable canvas / strand list / segment
inspector, collapsing to a stacked layout with the inspector as a bottom sheet on
narrow screens; zoom is a bound ZoomFactor driving a Viewbox, not a ScrollViewer
call. New StrandList and BlueprintFrame, Column/RowSplitterBehavior, and refreshed
design tokens and palette.
Verified: all four buildable Uno heads (desktop/windows/android/browserwasm), CLI,
dotnet test (40 passing, incl. new TrackPlanServiceTests), desktop launch smoke
test. The canvas itself cannot be pixel-verified in this environment, so the
visuals still need a look on a real device.
Co-Authored-By: Claude <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


The 2D planner was a single hard-coded 8-segment stadium loop. It now builds arbitrary plans: append Gerade/Kurve/Weiche/Bahnhof, duplicate, reorder, delete, or start from an Oval/Acht/Punkt-zu-Punkt template, with a Weiche splitting the plan into a Stammgleis and its own branch strand that can be built on further.
Core:
Layout (Trackify/Helpers/TrackGeometry.cs) is now turtle graphics over the strand graph instead of a closed form, in two passes: the walk lays every strand out in unbounded world coordinates, then the emit pass bakes in the scale/offset that centers the finished plan on the 900x600 canvas and shrinks it (never enlarges) when it would reach into the margin the labels and sensor markers need. A plan can therefore grow in any direction without walking off the visible area — before, the walk started at a fixed (150,300) and the Oval sat in the left third.
Position fixes that came out of that:
UI: the planner page is a splitter-resizable canvas / strand list / segment inspector, collapsing to a stacked layout with the inspector as a bottom sheet on narrow screens; zoom is a bound ZoomFactor driving a Viewbox, not a ScrollViewer call. New StrandList and BlueprintFrame, Column/RowSplitterBehavior, and refreshed design tokens and palette.
Verified: all four buildable Uno heads (desktop/windows/android/browserwasm), CLI, dotnet test (40 passing, incl. new TrackPlanServiceTests), desktop launch smoke test. The canvas itself cannot be pixel-verified in this environment, so the visuals still need a look on a real device.