From 727d0e525552925b3931ff7c70bf3eece8bbe59b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A2=81=E8=96=84=E4=BA=91?= Date: Tue, 28 Jul 2026 16:30:01 +0800 Subject: [PATCH] docs: plan path smoothing comparison --- .../2026-07-28-path-smoothing-comparison.md | 1038 +++++++++++++++++ 1 file changed, 1038 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-28-path-smoothing-comparison.md diff --git a/docs/superpowers/plans/2026-07-28-path-smoothing-comparison.md b/docs/superpowers/plans/2026-07-28-path-smoothing-comparison.md new file mode 100644 index 0000000..485c107 --- /dev/null +++ b/docs/superpowers/plans/2026-07-28-path-smoothing-comparison.md @@ -0,0 +1,1038 @@ +# Path Smoothing Comparison Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build a validated coarse-path smoothing module that compares cubic B-spline, local cubic Bézier, and piecewise quintic paths, then exports reproducible IEEE-style Chinese reports for existing and fast fixture scenarios. + +**Architecture:** Add `PathSmoothing` beside `CoarsePath`; keep contracts, shared geometry processing, algorithms, validation, comparison, and rendering isolated. Every algorithm consumes the same preprocessed direction segments, every candidate passes the same analyzer and full-footprint validator, and only accepted results reach the formal facade. Reporting consumes immutable comparison data and cannot affect planning status. + +**Tech Stack:** C# 10, .NET Standard 2.0, existing `PlanningGridMap`/`FootprintCollisionChecker`, PowerShell reflection tests, Newtonsoft.Json 13.0.4, StbImageWriteSharp 1.16.7, System.Drawing.Common 10.0.10 on Windows. + +## Global Constraints + +- Core units are m, rad, and 1/m; no mm or degree conversion inside smoothing algorithms. +- Preserve start, goal, direction-segment order, gear-switch count, and gear-switch poses exactly. +- Never differentiate, resample, or fit across a gear-switch duplicate pair. +- Default output spacing is `0.05 m`; default swept-collision step is `0.025 m`. +- A formal result publishes a path only for `Success` or explicitly verified `FallbackToCoarsePath`. +- Rejected candidates may appear only in comparison diagnostics, never in `PathSmoothingResult.Path`. +- No speed, acceleration, time, SQP, Frenet, chassis, sensor, localization, or UI dependencies. +- Figure size is `7.16 × 5.2 in`; PNG size is `2148 × 1560 px` with 300 dpi metadata. +- Chinese text uses `SimSun`; English, numbers, Greek, and mathematics use `Times New Roman`. +- Fixed method colors are raw `#4D4D4D`, B-spline `#0072B2`, Bézier `#D55E00`, quintic `#009E73`, and curvature limits `#CC79A7`. +- `System.Drawing.Common` is a Windows-only report-rendering dependency; smoothing, validation, comparison, SVG, and CSV remain independent of its runtime availability. +- Follow TDD for every task: failing verification first, minimal implementation second, full relevant verification before commit. + +--- + +### Task 1: Immutable smoothing contracts + +**Files:** +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Contracts/SmoothingMethod.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Contracts/PathSmoothingStatus.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Contracts/SmoothedPathPointSource.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Contracts/SmoothedPathPoint.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Contracts/SmoothedPathSegment.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Contracts/CubicBSplineOptions.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Contracts/LocalCubicBezierOptions.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Contracts/PiecewiseQuinticOptions.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Contracts/PathSmoothingConfiguration.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Contracts/PathSmoothingRequest.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Contracts/PathQualityMetrics.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Contracts/PathSmoothingDiagnostics.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Contracts/PathSmoothingResult.cs` +- Test: `ClumsyPilot/tests/verify_path_smoothing_contracts.ps1` + +**Interfaces:** +- Consumes: `CoarsePathPoint`, `PathSegment`, `PlanningGridMap`, `VehicleParameters`. +- Produces: `PathSmoothingRequest`; `PathSmoothingResult.Success(...)`; `PathSmoothingResult.Fallback(...)`; `PathSmoothingResult.Failure(...)`; all public enums and value objects used below. + +- [ ] **Step 1: Write the failing contract verification** + +Create a reflection script that loads `ClumsyPilot.dll`, resolves every type listed above, and verifies these exact defaults and invariants: + +```powershell +$configuration = [Activator]::CreateInstance($configurationType) +Assert-Near 0.05 $configuration.OutputSpacingMeters 'Default output spacing must be 0.05 m.' +Assert-Near 0.025 $configuration.MaximumCollisionCheckStepMeters 'Default collision step must be 0.025 m.' +Assert-Equal 4 $configuration.RetryStrengthScales.Count 'Retry schedule must contain four entries.' +Assert-Near 1.0 $configuration.RetryStrengthScales[0] 'First retry scale must be 1.0.' +Assert-Near 0.25 $configuration.RetryStrengthScales[3] 'Last retry scale must be 0.25.' + +$failed = $resultType.GetMethod('Failure').Invoke( + $null, + @([Enum]::Parse($statusType, 'InvalidInput'), + [Activator]::CreateInstance($diagnosticsType))) +Assert-Equal 0 $failed.Path.Count 'Failure must publish no path.' +Assert-Equal 0 $failed.Segments.Count 'Failure must publish no segments.' +``` + +Also construct a `SmoothedPathPoint` and assert all units/properties, construct two `SmoothedPathSegment` instances, and prove returned collections cannot be mutated. + +- [ ] **Step 2: Run the verification and confirm RED** + +Run: + +```powershell +dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_contracts.ps1 +``` + +Expected: build succeeds on existing code; script fails because `MultiWheelC.TrajectoryPlanning.PathSmoothing.PathSmoothingConfiguration` is missing. + +- [ ] **Step 3: Implement enums, immutable values, and result factories** + +Use namespace `MultiWheelC.TrajectoryPlanning.PathSmoothing`. Implement these exact public shapes: + +```csharp +public enum SmoothingMethod { CubicBSpline, LocalCubicBezier, PiecewiseQuintic } +public enum PathSmoothingStatus { Success, FallbackToCoarsePath, InvalidInput, Infeasible, Failed, Cancelled } +public enum SmoothedPathPointSource { Anchor, Interpolated, GearSwitch, CoarsePathFallback } + +public sealed class PathSmoothingConfiguration +{ + public PathSmoothingConfiguration() + { + OutputSpacingMeters = 0.05d; + MaximumCollisionCheckStepMeters = 0.025d; + MinimumClearanceReserveMeters = 0.02d; + SmoothingStrength = 1d; + AllowFallbackToCoarsePath = true; + RetryStrengthScales = new ReadOnlyCollection( + new List { 1d, 0.75d, 0.50d, 0.25d }); + } + public SmoothingMethod Method { get; set; } + public double OutputSpacingMeters { get; set; } + public double MaximumCollisionCheckStepMeters { get; set; } + public double MinimumClearanceReserveMeters { get; set; } + public double SmoothingStrength { get; set; } + public bool AllowFallbackToCoarsePath { get; set; } + public IReadOnlyList RetryStrengthScales { get; } + public CubicBSplineOptions CubicBSpline { get; } = new CubicBSplineOptions(); + public LocalCubicBezierOptions LocalCubicBezier { get; } = new LocalCubicBezierOptions(); + public PiecewiseQuinticOptions PiecewiseQuintic { get; } = new PiecewiseQuinticOptions(); +} +``` + +Use these exact option defaults: + +```csharp +public sealed class CubicBSplineOptions +{ + public double EndpointTangentScale { get; set; } = 1d / 3d; +} +public sealed class LocalCubicBezierOptions +{ + public double CornerHeadingThresholdRadians { get; set; } = Math.PI / 18d; + public double MaximumWindowLengthMeters { get; set; } = 0.60d; + public double HandleLengthRatio { get; set; } = 1d / 3d; +} +public sealed class PiecewiseQuinticOptions +{ + public double KnotSpacingMeters { get; set; } = 0.50d; + public double MinimumKnotSpacingMeters { get; set; } = 0.10d; +} +``` + +Define the request constructor exactly as: + +```csharp +public PathSmoothingRequest( + IReadOnlyList coarsePath, + IReadOnlyList segments, + PlanningGridMap map, + VehicleParameters vehicle, + PathSmoothingConfiguration configuration) +``` + +Define parameterless immutable-empty defaults for `PathQualityMetrics` and `PathSmoothingDiagnostics`, plus overloads that accept all measured values. The three result factories are: + +```csharp +public static PathSmoothingResult Success( + SmoothingMethod method, IReadOnlyList path, + IReadOnlyList segments, PathSmoothingDiagnostics diagnostics); +public static PathSmoothingResult Fallback( + SmoothingMethod attemptedMethod, IReadOnlyList path, + IReadOnlyList segments, PathSmoothingDiagnostics diagnostics); +public static PathSmoothingResult Failure( + PathSmoothingStatus status, PathSmoothingDiagnostics diagnostics); +``` + +`PathSmoothingResult` must copy all input lists into `ReadOnlyCollection`. Reject `Success`/`Fallback` factories with empty paths or segments; reject `Failure` with a success-like status. + +- [ ] **Step 4: Run contract verification and existing integration verification** + +Run: + +```powershell +dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_contracts.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_coarse_path_integration.ps1 +``` + +Expected: both scripts end with their passed messages. + +- [ ] **Step 5: Commit** + +```powershell +git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Contracts ClumsyPilot/tests/verify_path_smoothing_contracts.ps1 +git commit -m "feat: define path smoothing contracts" +``` + +### Task 2: Input preparation, direction splitting, resampling, and geometry analysis + +**Files:** +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Processing/PreparedPath.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Processing/PreparedDirectionSegment.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Processing/SmoothingPoint2D.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Processing/PathGeometryAnalysis.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Processing/PathSmoothingPreprocessor.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Processing/ArcLengthResampler.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Processing/PathGeometryAnalyzer.cs` +- Test: `ClumsyPilot/tests/verify_path_smoothing_geometry.ps1` + +**Interfaces:** +- Consumes: `PathSmoothingRequest`. +- Produces: `PathSmoothingPreprocessor.TryPrepare(request, out PreparedPath, out string)` and `PathGeometryAnalyzer.TryAnalyze(candidateSegments, spacing, out PathGeometryAnalysis analysis, out string reason)`. + +- [ ] **Step 1: Write analytic failing tests** + +Build reflection helpers that create: + +```text +forward straight: (0,0,0) → (1,0,0), expected κ = 0 +forward quarter circle: R=2, expected vehicle κ = +0.5 +reverse quarter circle: R=2, expected vehicle κ = -0.5 +gear switch: duplicated pose and arc length with opposite directions +``` + +Assert `0.05 m` spacing, exact final point, continuous `UnwrappedHeading`, correct curvature signs, and no derivative across the gear-switch pair. + +- [ ] **Step 2: Run and confirm RED** + +```powershell +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_geometry.ps1 +``` + +Expected: fails because `PathGeometryAnalyzer` is missing. + +- [ ] **Step 3: Implement deterministic preparation and resampling** + +Implement segment-local linear arc interpolation: + +```csharp +double ratio = (targetArc - left.ArcLength) / (right.ArcLength - left.ArcLength); +double x = left.X + ratio * (right.X - left.X); +double y = left.Y + ratio * (right.Y - left.Y); +double heading = left.UnwrappedHeading + + ratio * (right.UnwrappedHeading - left.UnwrappedHeading); +``` + +Always append the exact segment end. Preserve separate gear-switch endpoints. Reject non-finite values, non-monotonic arc length, illegal duplicates, and segment coverage gaps. + +- [ ] **Step 4: Implement shared geometry formulas** + +For each direction segment, recompute arc length from Euclidean position increments, unwrap heading from travel tangents, and use stable one-sided/central differences: + +```csharp +double geometricCurvature = deltaHeading / deltaArc; +double directionSign = direction == TravelDirection.Forward ? 1d : -1d; +double vehicleCurvature = directionSign * geometricCurvature; +double totalVariationIncrement = Math.Abs(currentCurvature - previousCurvature); +double variationEnergyIncrement = + Math.Pow((currentCurvature - previousCurvature) / deltaArc, 2d) * deltaArc; +``` + +At segment boundaries use only samples from that segment. Never include the gear-switch duplicate distance in a denominator. + +- [ ] **Step 5: Run geometry, contract, and utility checks** + +```powershell +dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_geometry.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_contracts.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_planning_utils.ps1 +``` + +Expected: all pass; circle curvature error stays within the tolerance encoded in the test. + +- [ ] **Step 6: Commit** + +```powershell +git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Processing ClumsyPilot/tests/verify_path_smoothing_geometry.ps1 +git commit -m "feat: add smoothing path geometry processing" +``` + +### Task 3: Full-footprint smoothing validator + +**Files:** +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Validation/SmoothedPathValidator.cs` +- Test: `ClumsyPilot/tests/verify_path_smoothing_validation.ps1` + +**Interfaces:** +- Consumes: analyzed `SmoothedPathPoint`/`SmoothedPathSegment`, original prepared endpoints, `PlanningGridMap`, `VehicleParameters`, collision step. +- Produces: `TryValidate(..., out IReadOnlyList pathWithClearance, out double minimumClearance, out string reason)`. + +- [ ] **Step 1: Write failing safety cases** + +Create an empty map, an occupied rectangle map, and paths that are: valid; point-colliding; swept-motion-colliding; outside bounds; over maximum curvature; changed at a gear switch; and overclaiming clearance. + +```powershell +Assert-False $collisionAccepted 'A smoothing candidate that cuts through an obstacle must be rejected.' +Assert-False $curvatureAccepted 'A smoothing candidate above vehicle maximum curvature must be rejected.' +Assert-False $switchAccepted 'A moved gear-switch pose must be rejected.' +Assert-True $validAccepted 'A valid straight candidate must pass.' +``` + +- [ ] **Step 2: Run and confirm RED** + +```powershell +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_validation.ps1 +``` + +Expected: missing `SmoothedPathValidator`. + +- [ ] **Step 3: Implement validator using existing collision semantics** + +For every point call `FootprintCollisionChecker.IsPoseCollisionFree`; for every non-duplicate adjacent pair call `IsSweptMotionCollisionFree`. Enforce: + +```csharp +if (Math.Abs(point.VehicleCurvature) > maximumCurvature + 1e-6d) + return Fail("平滑路径车辆曲率超过车辆上限。"); +if (!SamePose(candidateStart, originalStart) || !SamePose(candidateEnd, originalEnd)) + return Fail("平滑路径改变了方向段端点。"); +``` + +Replace candidate clearance with the conservative checked value; never retain a larger claimed value. + +- [ ] **Step 4: Run validator and existing collision checks** + +```powershell +dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_validation.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_coarse_path_collision.ps1 +``` + +Expected: both pass. + +- [ ] **Step 5: Commit** + +```powershell +git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Validation ClumsyPilot/tests/verify_path_smoothing_validation.ps1 +git commit -m "feat: validate smoothed vehicle paths" +``` + +### Task 4: Algorithm contract and finite retry runner + +**Files:** +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/IPathSmoother.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingCandidate.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingAlgorithmInput.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingAlgorithmRunner.cs` +- Test: `ClumsyPilot/tests/verify_path_smoothing_runner.ps1` + +**Interfaces:** +- Consumes: `PreparedPath`, method-specific `IPathSmoother`, analyzer, validator, retry scales. +- Produces: one accepted candidate or stable `Infeasible`/`Failed` diagnostics with attempted strengths and optional rejected comparison geometry. + +Use this exact internal algorithm interface: + +```csharp +internal interface IPathSmoother +{ + SmoothingMethod Method { get; } + SmoothingCandidate Smooth( + SmoothingAlgorithmInput input, + double effectiveStrength, + CancellationToken cancellationToken); +} +``` + +- [ ] **Step 1: Write failing fake-smoother tests** + +Use reflection plus internal test doubles exposed from a nested test helper to prove: + +```text +strengths attempted exactly: 1.00, 0.75, 0.50, 0.25 +stop immediately after first accepted candidate +do not retry numerical invalid input +cancel before the next attempt +retain rejected candidate only in comparison diagnostics +``` + +- [ ] **Step 2: Run and confirm RED** + +```powershell +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_runner.ps1 +``` + +Expected: missing `SmoothingAlgorithmRunner`. + +- [ ] **Step 3: Implement the runner** + +Use this control flow: + +```csharp +foreach (double scale in configuration.RetryStrengthScales) +{ + cancellationToken.ThrowIfCancellationRequested(); + SmoothingCandidate candidate = smoother.Smooth(input, configuration.SmoothingStrength * scale); + if (!candidate.Succeeded) return AlgorithmRunResult.Failed(candidate.Reason); + if (!_analyzer.TryAnalyze(candidate.Segments, configuration.OutputSpacingMeters, out PathGeometryAnalysis analyzed, out var reason)) + return AlgorithmRunResult.Failed(reason); + if (_validator.TryValidate(analyzed.Path, analyzed.Segments, input, out var safePath, out var clearance, out reason)) + return AlgorithmRunResult.Success(safePath, analyzed.Segments, metrics, scale); + rejected = candidate; + failures.Add(reason); +} +return AlgorithmRunResult.Infeasible(rejected, failures); +``` + +Convert `OperationCanceledException` to `Cancelled` at the facade boundary, not inside numerical algorithms. + +- [ ] **Step 4: Run runner, geometry, and validator tests** + +```powershell +dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_runner.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_geometry.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_validation.ps1 +``` + +Expected: all pass. + +- [ ] **Step 5: Commit** + +```powershell +git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms ClumsyPilot/tests/verify_path_smoothing_runner.ps1 +git commit -m "feat: add smoothing algorithm retry runner" +``` + +### Task 5: Clamped cubic B-spline smoother + +**Files:** +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/CubicBSplineSmoother.cs` +- Test: `ClumsyPilot/tests/verify_path_smoothing_bspline.ps1` + +**Interfaces:** +- Implements: `IPathSmoother.Method == SmoothingMethod.CubicBSpline`. +- Produces: one geometry-only candidate per input direction segment. + +- [ ] **Step 1: Write failing deterministic shape tests** + +Verify a straight segment remains collinear, a five-point corner becomes tangent-continuous, endpoints and travel tangents are exact, and every displacement stays within its supplied movement radius. + +- [ ] **Step 2: Run and confirm RED** + +```powershell +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_bspline.ps1 +``` + +Expected: missing `CubicBSplineSmoother`. + +- [ ] **Step 3: Implement clamped basis evaluation and constrained control points** + +Use degree 3, endpoint knot multiplicity 4, and Cox–de Boor basis evaluation. Fix the first/last control points to endpoints; place adjacent control points on endpoint travel tangents. Blend remaining control points toward local three-point averages: + +```csharp +SmoothingPoint2D target = (previous + current + next) / 3d; +SmoothingPoint2D proposed = current + strength * (target - current); +SmoothingPoint2D bounded = ClampDisplacement(current, proposed, allowedRadius); +``` + +Return the original straight samples when all perpendicular deviations are below `1e-9`. + +- [ ] **Step 4: Run algorithm and shared safety tests** + +```powershell +dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_bspline.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_runner.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_validation.ps1 +``` + +Expected: all pass. + +- [ ] **Step 5: Commit** + +```powershell +git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/CubicBSplineSmoother.cs ClumsyPilot/tests/verify_path_smoothing_bspline.ps1 +git commit -m "feat: add cubic b-spline path smoother" +``` + +### Task 6: Local cubic Bézier smoother + +**Files:** +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/LocalCubicBezierSmoother.cs` +- Test: `ClumsyPilot/tests/verify_path_smoothing_bezier.ps1` + +**Interfaces:** +- Implements: `IPathSmoother.Method == SmoothingMethod.LocalCubicBezier`. +- Consumes: heading-change threshold, maximum local window, handle-length ratio. + +- [ ] **Step 1: Write failing local-behavior tests** + +Assert no window is created for a straight line; one corner creates one replacement; overlapping windows merge; outside-window samples remain bitwise equal; endpoints and switch points remain fixed. + +- [ ] **Step 2: Run and confirm RED** + +```powershell +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_bezier.ps1 +``` + +Expected: missing `LocalCubicBezierSmoother`. + +- [ ] **Step 3: Implement window detection and Bézier evaluation** + +Detect corners from absolute change in travel tangent. Merge touching windows. For each window: + +```csharp +SmoothingPoint2D p1 = p0 + entryTangent * handleLength; +SmoothingPoint2D p2 = p3 - exitTangent * handleLength; +SmoothingPoint2D value = + Math.Pow(1d - t, 3d) * p0 + + 3d * Math.Pow(1d - t, 2d) * t * p1 + + 3d * (1d - t) * t * t * p2 + + t * t * t * p3; +``` + +Clamp each evaluated displacement to the interpolated movement radius and retain original samples outside merged windows. + +- [ ] **Step 4: Run Bézier and regression checks** + +```powershell +dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_bezier.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_runner.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_coarse_path_integration.ps1 +``` + +Expected: all pass. + +- [ ] **Step 5: Commit** + +```powershell +git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/LocalCubicBezierSmoother.cs ClumsyPilot/tests/verify_path_smoothing_bezier.ps1 +git commit -m "feat: add local cubic bezier smoother" +``` + +### Task 7: Piecewise quintic Hermite smoother + +**Files:** +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/PiecewiseQuinticSmoother.cs` +- Test: `ClumsyPilot/tests/verify_path_smoothing_quintic.ps1` + +**Interfaces:** +- Implements: `IPathSmoother.Method == SmoothingMethod.PiecewiseQuintic`. +- Produces: C2-connected local polynomial segments without crossing direction boundaries. + +- [ ] **Step 1: Write failing continuity and degeneracy tests** + +At every shared knot evaluate left/right position, first derivative, and second derivative; assert equality within `1e-6`. Verify exact endpoint pose, bounded movement, and stable failure for a segment shorter than the configured minimum knot spacing. + +- [ ] **Step 2: Run and confirm RED** + +```powershell +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_quintic.ps1 +``` + +Expected: missing `PiecewiseQuinticSmoother`. + +- [ ] **Step 3: Implement normalized quintic Hermite basis** + +For each knot interval use normalized `t ∈ [0,1]` and solve the six coefficients independently for X and Y from: + +```text +p(0)=p0, p(1)=p1 +p'(0)=v0, p'(1)=v1 +p''(0)=a0, p''(1)=a1 +``` + +Derive endpoint velocities from travel tangents times interval length. Blend shared accelerations once per knot and reuse the same value on both adjacent intervals. Reject singular or non-finite coefficients before sampling. + +- [ ] **Step 4: Run quintic, geometry, and validator checks** + +```powershell +dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_quintic.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_geometry.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_validation.ps1 +``` + +Expected: all pass. + +- [ ] **Step 5: Commit** + +```powershell +git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/PiecewiseQuinticSmoother.cs ClumsyPilot/tests/verify_path_smoothing_quintic.ps1 +git commit -m "feat: add piecewise quintic path smoother" +``` + +### Task 8: Formal smoothing facade and explicit coarse-path fallback + +**Files:** +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Facade/PathSmoothingService.cs` +- Test: `ClumsyPilot/tests/verify_path_smoothing_service.ps1` + +**Interfaces:** +- Produces: `PathSmoothingResult Smooth(PathSmoothingRequest, CancellationToken)`. +- Consumes: preprocessor, method registry, retry runner, analyzer, validator. + +- [ ] **Step 1: Write failing facade tests** + +Cover valid straight success, invalid input, cancellation, infeasible-without-fallback, and infeasible-with-verified-fallback. Assert fallback points use `CoarsePathFallback` and status never equals `Success`. + +- [ ] **Step 2: Run and confirm RED** + +```powershell +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_service.ps1 +``` + +Expected: missing `PathSmoothingService`. + +- [ ] **Step 3: Implement orchestration** + +Use a stable method registry: + +```csharp +private IPathSmoother Resolve(SmoothingMethod method) => + method switch + { + SmoothingMethod.CubicBSpline => _bSpline, + SmoothingMethod.LocalCubicBezier => _bezier, + SmoothingMethod.PiecewiseQuintic => _quintic, + _ => throw new ArgumentOutOfRangeException(nameof(method)), + }; +``` + +Validate the coarse path before smoothing. On allowed fallback, convert the revalidated coarse points to `SmoothedPathPoint` with `CoarsePathFallback`, re-run shared geometry analysis, and return `Fallback`, preserving the failed method diagnostics. + +- [ ] **Step 4: Run all core smoothing verifications** + +```powershell +dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_contracts.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_geometry.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_validation.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_service.ps1 +``` + +Expected: all pass. + +- [ ] **Step 5: Commit** + +```powershell +git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Facade ClumsyPilot/tests/verify_path_smoothing_service.ps1 +git commit -m "feat: expose validated path smoothing service" +``` + +### Task 9: Comparison metrics, isolation, and deterministic ranking + +**Files:** +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Comparison/PathSmoothingComparisonRequest.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Comparison/PathSmoothingComparisonEntry.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Comparison/PathSmoothingComparisonResult.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Comparison/SmoothingMethodRanker.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Facade/PathSmoothingComparisonService.cs` +- Test: `ClumsyPilot/tests/verify_path_smoothing_comparison.ps1` + +**Interfaces:** +- Produces: `Compare(PathSmoothingComparisonRequest, CancellationToken)`. +- Ranking order: feasible count; median variation energy; worst peak utilization; worst clearance loss; median length increase; median elapsed. + +- [ ] **Step 1: Write failing comparison and ranking tests** + +Create synthetic entries whose order changes at each tie-break level. Verify one method failure does not stop the remaining methods, cancellation does, and no feasible method produces a null recommendation. + +Also assert the result always contains one separately analyzed raw-path baseline plus exactly one entry for each requested method; the raw baseline is never treated as a candidate method in ranking. + +- [ ] **Step 2: Run and confirm RED** + +```powershell +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_comparison.ps1 +``` + +Expected: missing comparison service/ranker. + +- [ ] **Step 3: Implement metric normalization and lexicographic comparison** + +Use stable median: + +```csharp +double Median(IReadOnlyList values) +{ + double[] sorted = values.OrderBy(value => value).ToArray(); + int middle = sorted.Length / 2; + return sorted.Length % 2 == 1 + ? sorted[middle] + : (sorted[middle - 1] + sorted[middle]) / 2d; +} +``` + +Use absolute deltas when a raw denominator has magnitude below `1e-12`. The comparison service must force `AllowFallbackToCoarsePath = false` so fallback cannot masquerade as method success. + +- [ ] **Step 4: Run comparison and service tests** + +```powershell +dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_comparison.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_service.ps1 +``` + +Expected: all pass. + +- [ ] **Step 5: Commit** + +```powershell +git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Comparison ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Facade/PathSmoothingComparisonService.cs ClumsyPilot/tests/verify_path_smoothing_comparison.ps1 +git commit -m "feat: compare and rank smoothing methods" +``` + +### Task 10: Versioned fast fixtures and existing end-to-end scenarios + +**Files:** +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/SmoothingScenarioFixture.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/SmoothingScenarioFixtureLoader.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/SmoothingFixtureGenerator.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/SmoothingScenarioFactory.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/Fixtures/path-smoothing-fixtures.json` +- Create: `ClumsyPilot/tests/generate_path_smoothing_fixtures.ps1` +- Create: `ClumsyPilot/tests/verify_path_smoothing_fixtures.ps1` +- Create: `ClumsyPilot/tests/verify_path_smoothing_integration.ps1` + +**Interfaces:** +- Produces: eight immutable fast fixtures and four end-to-end comparison requests. +- Consumes: existing `CoarsePathScenarioFactory.Create(scenario)` for end-to-end requests. + +- [ ] **Step 1: Write failing fixture tests** + +Assert the exact IDs: + +```powershell +$expected = @( + 'straight', 'single-turn', 's-bend', 'large-heading-change', + 'rectangle-detour', 'multi-obstacle-detour', + 'narrow-corridor', 'forward-reverse-switch') +``` + +Verify unique IDs, version > 0, matching fingerprint, valid segment coverage, and that fixture-only loading has no reference to `HybridAStarPlanner`. + +- [ ] **Step 2: Write failing end-to-end tests** + +For `ExplicitEmpty`, `RectangleDetour`, `ManualAndTwoLeg`, and `ReverseGearSwitch`, call the existing planner then comparison service. Assert the raw path is unchanged and every successful smoothing entry preserves segment count and gear-switch count. + +- [ ] **Step 3: Run and confirm RED** + +```powershell +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_fixtures.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_integration.ps1 +``` + +Expected: missing fixture loader/factory. + +- [ ] **Step 4: Implement deterministic JSON fixtures** + +Serialize with invariant numbers and explicit schema: + +```json +{ + "schemaVersion": 1, + "scenarios": [ + { + "id": "straight", + "fixtureVersion": 1, + "configurationFingerprint": "sha256:", + "map": { + "xMinMm": 0, "xMaxMm": 6000, + "yMinMm": 0, "yMaxMm": 4000, + "resolutionMm": 50, + "obstacles": [] + }, + "vehicle": { "lengthMeters": 0.8, "widthMeters": 0.6, "safetyMarginMeters": 0.05, "maximumCurvaturePerMeter": 0.8333333333333334 }, + "path": [], + "segments": [] + } + ] +} +``` + +Populate all eight scenarios from explicitly verified successful coarse-path outputs by running: + +```powershell +dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\generate_path_smoothing_fixtures.ps1 -OutputPath .\ClumsyPilot\ParkrobTrajplanner\PathSmoothing\Test\Fixtures\path-smoothing-fixtures.json +``` + +`SmoothingFixtureGenerator.Generate(string outputPath, bool overwrite)` must refuse an existing target when `overwrite` is false. The script maps `-Overwrite` to that argument; normal tests call only `LoadAndVerify` and never regenerate data. Each obstacle is serialized as a typed circle or axis-aligned rectangle DTO so the loader can rebuild the exact `PlanningGridMap` without Hybrid A*. + +- [ ] **Step 5: Run fixture and integration checks** + +```powershell +dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_fixtures.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_integration.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_coarse_path_integration.ps1 +``` + +Expected: all pass. Record actual end-to-end elapsed values as diagnostics, not hardware-dependent pass thresholds. + +- [ ] **Step 6: Commit** + +```powershell +git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test ClumsyPilot/tests/generate_path_smoothing_fixtures.ps1 ClumsyPilot/tests/verify_path_smoothing_fixtures.ps1 ClumsyPilot/tests/verify_path_smoothing_integration.ps1 +git commit -m "test: add path smoothing scenarios and fixtures" +``` + +### Task 11: Shared figure model, IEEE SVG, and UTF-8 CSV + +**Files:** +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/IeeeFigureStyle.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureModel.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFigureModelBuilder.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingSvgRenderer.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingCsvWriter.cs` +- Test: `ClumsyPilot/tests/verify_path_smoothing_svg_csv.ps1` + +**Interfaces:** +- Consumes: immutable `PathSmoothingComparisonResult`, map, start/goal, scenario label. +- Produces: one immutable figure model, UTF-8 SVG, and UTF-8-BOM CSV. + +- [ ] **Step 1: Write failing style and serialization tests** + +Assert exact colors, line styles, panel labels, view box, equal XY scale, legend order, XML escaping, UTF-8 Chinese, BOM bytes `EF BB BF`, and invariant decimal points. + +Include one rejected candidate in the model and assert its normal method-colored curve plus violation cross markers are present while its metrics row says `Infeasible`. Include one numerical failure and assert its legend/row remains but no fake path element is emitted. + +- [ ] **Step 2: Run and confirm RED** + +```powershell +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_svg_csv.ps1 +``` + +Expected: missing `IeeeFigureStyle`. + +- [ ] **Step 3: Implement one physical layout model** + +Define all coordinates in typographic points (`72 pt/in`): + +```csharp +public const double FigureWidthPoints = 7.16d * 72d; +public const double FigureHeightPoints = 5.20d * 72d; +public const string RawColor = "#4D4D4D"; +public const string BSplineColor = "#0072B2"; +public const string BezierColor = "#D55E00"; +public const string QuinticColor = "#009E73"; +public const string LimitColor = "#CC79A7"; +``` + +Allocate 60% width to panel `(a)`, split the right side between `(b)` curvature and `(c)` metrics, and compute one world-to-panel transform with equal X/Y scale. + +- [ ] **Step 4: Implement SVG and CSV** + +SVG text must use explicit runs: + +```xml +车辆曲率 κ (m⁻¹) +``` + +CSV header order is fixed: + +```text +ScenarioId,Method,Status,PathLengthMeters,MaximumAbsoluteVehicleCurvaturePerMeter,RootMeanSquareVehicleCurvaturePerMeter,TotalAbsoluteCurvatureVariationPerMeter,CurvatureVariationEnergy,MinimumBodyClearanceMeters,ElapsedMilliseconds,RetryCount,AcceptedStrength +``` + +- [ ] **Step 5: Run SVG/CSV and comparison checks** + +```powershell +dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_svg_csv.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_comparison.ps1 +``` + +Expected: all pass. + +- [ ] **Step 6: Commit** + +```powershell +git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization ClumsyPilot/tests/verify_path_smoothing_svg_csv.ps1 +git commit -m "feat: render ieee smoothing svg and metrics" +``` + +### Task 12: Windows font validation and 300 dpi PNG export + +**Files:** +- Modify: `ClumsyPilot/ClumsyPilot.csproj` +- Modify: `ClumsyPilot/ParkrobTrajplanner/Map/Test/Visualization/ValidatedPngWriter.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingFontResolver.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingPngRenderer.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingReportExportRequest.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingReportExportResult.cs` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization/SmoothingReportExporter.cs` +- Test: `ClumsyPilot/tests/verify_path_smoothing_png.ps1` + +**Interfaces:** +- Produces: atomic `.svg`, `.png`, `.csv` export; report failures do not mutate comparison results. +- Runtime boundary: `SmoothingPngRenderer` is Windows-only; SVG/CSV remain usable without GDI+. + +- [ ] **Step 1: Add the failing PNG/font verification** + +The script must assert: + +```text +PNG signature and CRC-valid chunks +IHDR width=2148 and height=1560 +pHYs X=11811 and Y=11811 pixels/meter +SimSun and Times New Roman were resolved by exact family name +mixed sample "粗路径 κ(s) X (m) −π" produced non-empty glyph bounds +missing-font test returns FontUnavailable +atomic export leaves no .tmp file +``` + +- [ ] **Step 2: Run and confirm RED** + +```powershell +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_png.ps1 +``` + +Expected: missing renderer and exporter. + +- [ ] **Step 3: Add the exact Windows rendering dependency** + +Add: + +```xml + +``` + +Then run: + +```powershell +dotnet restore .\ClumsyPilot\ClumsyPilot.csproj +``` + +Expected: restore exits 0. Do not add cross-platform fallback switches; Microsoft documents `System.Drawing.Common` as Windows-only. + +- [ ] **Step 4: Implement exact font resolution and mixed-run baseline layout** + +Use `InstalledFontCollection` to require `SimSun` and `Times New Roman`. Split text by Unicode category/CJK ranges, measure each run with typographic `StringFormat`, and align runs by font-family ascent: + +```csharp +float baseline = top + maxAscentPixels; +float runTop = baseline - family.GetCellAscent(style) * emPixels / + family.GetEmHeight(style); +``` + +Return `FontUnavailable` before creating output files if either exact family is absent. + +- [ ] **Step 5: Render the shared model and write validated PNG** + +Render at `2148 × 1560`, opaque white background, anti-aliased geometry, and no gradients/shadows. Convert bitmap pixels to RGBA and use the existing validated PNG path, extending it to insert a `pHYs` chunk with `11811` pixels/meter and correct CRC before `IDAT`. + +Export all three files to temporary siblings, validate each, then rename into place. On failure, remove only those exact temporary siblings. + +- [ ] **Step 6: Run PNG, map-image, and build checks** + +```powershell +dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_png.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_planning_map_image.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_svg_csv.ps1 +``` + +Expected: all pass; existing map PNG export remains valid. + +- [ ] **Step 7: Commit** + +```powershell +git add -- ClumsyPilot/ClumsyPilot.csproj ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Visualization ClumsyPilot/tests/verify_path_smoothing_png.ps1 +git commit -m "feat: export ieee smoothing png reports" +``` + +### Task 13: Module documentation, report batch entry, and full verification + +**Files:** +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/README.md` +- Create: `ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/PathSmoothingComparisonDemo.cs` +- Create: `ClumsyPilot/tests/run_path_smoothing_comparison.ps1` +- Create: `ClumsyPilot/tests/verify_path_smoothing_documentation.ps1` + +**Interfaces:** +- Produces: documented formal call example and a developer-only batch command that writes reports under `ClumsyPilot/obj/path_smoothing_reports`. + +- [ ] **Step 1: Write failing documentation and batch-entry verification** + +Assert README contains exact sections for units, facade usage, status handling, fallback, fixture freshness, IEEE colors/fonts, Windows PNG limitation, SQP boundary, and output files. Assert the batch runner offers `-FixtureOnly` and never writes under source directories. + +- [ ] **Step 2: Run and confirm RED** + +```powershell +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_documentation.ps1 +``` + +Expected: missing README and demo. + +- [ ] **Step 3: Write README and batch demo** + +Document this minimal formal flow: + +```csharp +if (coarseResult.PlanningResult.Status != PlanningStatus.Success) + return; +var smoothing = new PathSmoothingService().Smooth( + new PathSmoothingRequest( + coarseResult.PlanningResult.Path, + coarseResult.PlanningResult.Segments, + coarseResult.MapResult.Map, + job.Vehicle, + smoothingConfiguration), + cancellationToken); +if (smoothing.Status == PathSmoothingStatus.Success || + smoothing.Status == PathSmoothingStatus.FallbackToCoarsePath) + ConsumeSpatialReference(smoothing.Path, smoothing.Segments); +``` + +The batch script builds once, runs eight fixtures by default, optionally runs the four end-to-end cases, prints one-line metrics per method, and writes only beneath `ClumsyPilot/obj/path_smoothing_reports`. + +- [ ] **Step 4: Run the complete verification suite** + +```powershell +dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_planning_utils.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_planning_map_adapter.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_planning_map_factory.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_planning_map_image.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_coarse_path_collision.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_coarse_path_search.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_coarse_path_integration.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_contracts.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_geometry.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_validation.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_runner.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_bspline.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_bezier.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_quintic.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_service.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_comparison.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_fixtures.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_integration.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_svg_csv.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_png.ps1 +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_documentation.ps1 +``` + +Expected: build exits 0 and every script prints its passed message with no terminating errors. + +- [ ] **Step 5: Generate and inspect representative reports** + +```powershell +powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\run_path_smoothing_comparison.ps1 -FixtureOnly +``` + +Inspect `straight`, `rectangle-detour`, and `forward-reverse-switch` PNG/SVG files. Confirm four-method legend order, equal path axes, readable 8–10 pt text, no Chinese mojibake, no clipping/overlap, curvature limits, and infeasible markers where applicable. + +- [ ] **Step 6: Commit** + +```powershell +git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/README.md ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Test/PathSmoothingComparisonDemo.cs ClumsyPilot/tests/run_path_smoothing_comparison.ps1 ClumsyPilot/tests/verify_path_smoothing_documentation.ps1 +git commit -m "docs: document path smoothing comparison workflow" +```