56 KiB
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 is0.025 m. - Default clearance reserve is
0.02 m; default smoothing strength is1.00. - A formal result publishes a path only for
Successor explicitly verifiedFallbackToCoarsePath. - Movement-bound rejection is retryable; invalid input, singular coefficients, and non-finite geometry are terminal failures.
- Every algorithm uses a read-only snapshot of its strong-typed options and maps evaluated points to the original direction segment by local arc length, never by raw point index.
- 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 is4296 × 3120 pxwith 600 dpi metadata. - Chinese text uses
SimSun; English, numbers, Greek, and mathematics useTimes New Roman. - Fixed method colors are raw
#4D4D4D, B-spline#0072B2, Bézier#D55E00, quintic#009E73, and curvature limits#CC79A7. System.Drawing.Commonis a Windows-only report-rendering dependency; smoothing, validation, comparison, SVG, and CSV remain independent of its runtime availability.- Text SVG is the editable master, not a directly submittable IEEE artifact; submission conversion to font-embedded or outlined PDF/EPS is an explicit external publishing step.
- Offline timing uses one warm-up and five measured deterministic runs per scenario and method; ranking uses the measured median only.
- 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:
$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:
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:
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<double>(
new List<double> { 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<double> 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:
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:
public PathSmoothingRequest(
IReadOnlyList<CoarsePathPoint> coarsePath,
IReadOnlyList<PathSegment> 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:
public static PathSmoothingResult Success(
SmoothingMethod method, IReadOnlyList<SmoothedPathPoint> path,
IReadOnlyList<SmoothedPathSegment> segments, PathSmoothingDiagnostics diagnostics);
public static PathSmoothingResult Fallback(
SmoothingMethod attemptedMethod, IReadOnlyList<SmoothedPathPoint> path,
IReadOnlyList<SmoothedPathSegment> segments, PathSmoothingDiagnostics diagnostics);
public static PathSmoothingResult Failure(
PathSmoothingStatus status, PathSmoothingDiagnostics diagnostics);
PathSmoothingResult must copy all input lists into ReadOnlyCollection<T>. 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:
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
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)andPathGeometryAnalyzer.TryAnalyze(candidateSegments, spacing, out PathGeometryAnalysis analysis, out string reason). -
Step 1: Write analytic failing tests
Build reflection helpers that create:
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 -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:
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:
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
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
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<SmoothedPathPoint> 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.
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 -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:
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
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
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-specificIPathSmoother, analyzer, validator, retry scales. - Produces: one accepted candidate or stable
Infeasible/Faileddiagnostics with attempted strengths and optional rejected comparison geometry.
Use this exact internal algorithm interface:
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:
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 -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_runner.ps1
Expected: missing SmoothingAlgorithmRunner.
- Step 3: Implement the runner
Use this control flow:
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
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
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 -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:
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
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
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: Align retryable feasibility, option snapshots, and arc-length references
Context: Tasks 1–5 are already committed. This corrective task resolves the review-discovered contract gaps before adding the remaining algorithms.
Files:
- Create:
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingOptionsSnapshot.cs - Create:
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Processing/PathReferenceInterpolator.cs - Modify:
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingCandidate.cs - Modify:
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingAlgorithmInput.cs - Modify:
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingAlgorithmRunner.cs - Modify:
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/CubicBSplineSmoother.cs - Test:
ClumsyPilot/tests/verify_path_smoothing_runner.ps1 - Test:
ClumsyPilot/tests/verify_path_smoothing_bspline.ps1 - Test:
ClumsyPilot/tests/verify_path_smoothing_algorithm_input.ps1
Interfaces:
-
Produces:
SmoothingCandidateStatus.Success,RetryableInfeasible, orFailed. -
Produces: immutable method-specific option snapshots carried by
SmoothingAlgorithmInput. -
Produces:
PathReferenceInterpolator.TryInterpolateByArcLength(IReadOnlyList<SmoothingPoint2D> points, double targetArcLength, out SmoothingPoint2D reference, out string reason)for all three algorithms. -
Step 1: Write failing status, snapshot, and reference tests
The reflection tests must prove:
RetryableInfeasible attempts exactly 1.00, 0.75, 0.50, 0.25 and ends Infeasible
Failed attempts exactly once and ends Failed
request/config mutation after construction cannot change the internal option snapshot
custom EndpointTangentScale changes the B-spline endpoint handle
non-uniform source samples interpolate by local ArcLength, not point-index ratio
non-finite/non-positive option scalars and a threshold outside (0, π] are rejected before retry
Use a non-uniform source with local arc lengths 0.00, 0.05, 0.10, 0.125; at target arc 0.1125, the reference must lie halfway through the final interval regardless of point count.
- Step 2: Run and confirm RED
powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_algorithm_input.ps1
powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_runner.ps1
powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_bspline.ps1
Expected: missing candidate status/snapshot/interpolator assertions fail, and B-spline still ignores the configured endpoint scale.
- Step 3: Implement the retryable candidate state
Use the exact internal states:
internal enum SmoothingCandidateStatus
{
Success,
RetryableInfeasible,
Failed,
}
SmoothingCandidate.Success(...) requires complete segments. RetryableInfeasible(reason) and Failed(reason) carry no executable geometry. The runner continues only for RetryableInfeasible; it stops immediately for Failed. Exhausting retryable outcomes returns AlgorithmRunResult.Infeasible(...) even when no rejected comparison geometry exists.
- Step 4: Implement immutable options and arc-length interpolation
SmoothingOptionsSnapshot copies these six scalars from the request configuration into get-only values:
CubicBSplineEndpointTangentScale
BezierCornerHeadingThresholdRadians
BezierMaximumWindowLengthMeters
BezierHandleLengthRatio
QuinticKnotSpacingMeters
QuinticMinimumKnotSpacingMeters
Snapshot construction rejects non-finite values; all scales, ratios, windows, and knot lengths must be positive, the Bézier threshold must lie in (0, π], and quintic knot spacing must be at least its configured minimum. These are terminal input failures, not retryable geometry outcomes.
Use this construction boundary:
internal SmoothingOptionsSnapshot(PathSmoothingConfiguration configuration);
internal SmoothingAlgorithmInput(
PreparedPath originalPath,
PlanningGridMap map,
VehicleParameters vehicle,
double maximumCollisionCheckStepMeters,
double minimumClearanceReserveMeters,
SmoothingOptionsSnapshot options);
SmoothingAlgorithmInput.Options is get-only and never exposes the mutable public configuration objects.
PathReferenceInterpolator.TryInterpolateByArcLength locates the bracketing source samples by SmoothingPoint2D.ArcLength and linearly interpolates position, heading, unwrapped heading, and clearance. A normalized full-segment parameter maps to targetArc = u * segment.Points[last].ArcLength; local algorithms map their window or knot interval directly to its endpoint arc lengths.
- Step 5: Correct B-spline option and rejection semantics
Replace the hard-coded endpoint scale with input.Options.CubicBSplineEndpointTangentScale. Replace point-index reference interpolation with PathReferenceInterpolator. Evaluated-point movement excess returns RetryableInfeasible; invalid values, impossible endpoint-tangent construction, and non-finite controls remain Failed. Never clamp evaluated curve samples.
- Step 6: Run corrective and shared regression checks
dotnet build .\ClumsyPilot\ClumsyPilot.csproj --no-restore
powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_algorithm_input.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_geometry.ps1
powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_validation.ps1
Expected: all pass; retryable geometry rejection uses all four strengths, while numerical failure still uses one.
- Step 7: Commit
git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingOptionsSnapshot.cs ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Processing/PathReferenceInterpolator.cs ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingCandidate.cs ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingAlgorithmInput.cs ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/SmoothingAlgorithmRunner.cs ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Algorithms/CubicBSplineSmoother.cs ClumsyPilot/tests/verify_path_smoothing_algorithm_input.ps1 ClumsyPilot/tests/verify_path_smoothing_runner.ps1 ClumsyPilot/tests/verify_path_smoothing_bspline.ps1
git commit -m "fix: align smoothing feasibility and option flow"
Task 7: 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: the immutable Bézier heading-change threshold, maximum local window, and handle-length ratio from
SmoothingAlgorithmInput.Options. -
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 -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:
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;
For each evaluated point, map t to s_ref = s_entry + t * (s_exit - s_entry) and obtain the source reference through PathReferenceInterpolator. Compare displacement with max(0, reference.BodyClearance - MinimumClearanceReserveMeters). If any point exceeds that radius, return RetryableInfeasible with no executable geometry; do not pointwise clamp or project curve samples. Retain original samples outside merged windows.
Tests must set non-default threshold, window length, and handle ratio values and prove each option changes only its intended behavior.
- Step 4: Run Bézier and regression checks
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
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 8: 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. -
Consumes: immutable knot and minimum-knot spacing from
SmoothingAlgorithmInput.Options. -
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 -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:
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.
Map every interval sample to s_ref = s_knot0 + t * (s_knot1 - s_knot0) through PathReferenceInterpolator. Movement-bound excess returns RetryableInfeasible; singular coefficients, non-finite derivatives, and invalid spacing return Failed. Never clamp evaluated polynomial samples.
- Step 4: Run quintic, geometry, and validator checks
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
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 9: 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. Invalid input must include NaN, non-positive scales/windows/spacing, Bézier threshold outside (0, π], and KnotSpacingMeters < MinimumKnotSpacingMeters. Assert fallback points use CoarsePathFallback and status never equals Success.
- Step 2: Run and confirm RED
powershell -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_service.ps1
Expected: missing PathSmoothingService.
- Step 3: Implement orchestration
Use a stable method registry:
private IPathSmoother Resolve(SmoothingMethod method) =>
method switch
{
SmoothingMethod.CubicBSpline => _bSpline,
SmoothingMethod.LocalCubicBezier => _bezier,
SmoothingMethod.PiecewiseQuintic => _quintic,
_ => throw new ArgumentOutOfRangeException(nameof(method)),
};
Validate the full configuration and coarse path before constructing SmoothingOptionsSnapshot or starting finite retries; map every configuration-contract violation to InvalidInput. 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
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
git add -- ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Facade ClumsyPilot/tests/verify_path_smoothing_service.ps1
git commit -m "feat: expose validated path smoothing service"
Task 10: 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/SmoothingTimingSummary.cs - Create:
ClumsyPilot/ParkrobTrajplanner/PathSmoothing/Comparison/StableGeometryDigest.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.
-
Timing protocol: one unmeasured warm-up plus five measured deterministic executions per scenario and method; rank by measured median.
-
SmoothingTimingSummaryexposes a read-only five-valueMeasuredElapsedMilliseconds,MedianElapsedMilliseconds, andIsDeterministic. -
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.
For timing, assert the warm-up is excluded, exactly five samples remain, and a mismatch in status, point count, segment count, or stable geometry digest produces a non-deterministic diagnostic that excludes the method from recommendation.
- Step 2: Run and confirm RED
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:
double Median(IReadOnlyList<double> 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.
Run each method once for warm-up and five times for measurement against the same immutable prepared input. Use the first measured result as the canonical comparison geometry only after all five measured outputs match its stable status and geometry digest. StableGeometryDigest writes status, segment metadata, enum values, and every double through BitConverter.DoubleToInt64Bits in fixed little-endian order, then computes SHA-256; do not use GetHashCode(). Store all five elapsed values plus their median; only the median participates in the final lexicographic timing tie-break.
- Step 4: Run comparison and service tests
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
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 11: 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:
$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 -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:
{
"schemaVersion": 1,
"scenarios": [
{
"id": "straight",
"fixtureVersion": 1,
"configurationFingerprint": "sha256:<lowercase-hex>",
"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:
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
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
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 12: 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.
-
Portability boundary: text SVG is the editable master and requires exact fonts on the viewing machine; it is not claimed as a directly submittable IEEE vector file.
-
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 -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):
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.
Use 9 pt for coordinate ticks, axis labels, legend, and table body; use 10 pt for panel labels. Do not create any text smaller than 9 pt at final physical size.
- Step 4: Implement SVG and CSV
SVG text must use explicit runs:
<text><tspan font-family="SimSun">车辆曲率 </tspan><tspan font-family="Times New Roman">κ (m⁻¹)</tspan></text>
CSV header order is fixed:
ScenarioId,Method,Status,PathLengthMeters,MaximumAbsoluteVehicleCurvaturePerMeter,RootMeanSquareVehicleCurvaturePerMeter,TotalAbsoluteCurvatureVariationPerMeter,CurvatureVariationEnergy,MinimumBodyClearanceMeters,MedianElapsedMilliseconds,TimingSampleCount,IsDeterministic,RetryCount,AcceptedStrength
- Step 5: Run SVG/CSV and comparison checks
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
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 13: Windows font validation and 600 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,.csvexport; report failures do not mutate comparison results. -
Runtime boundary:
SmoothingPngRendereris Windows-only; SVG/CSV remain usable without GDI+. -
Submission boundary: conversion of the verified SVG master to font-embedded or outlined PDF/EPS is explicit external publishing work, not a hidden exporter side effect.
-
Step 1: Add the failing PNG/font verification
The script must assert:
PNG signature and CRC-valid chunks
IHDR width=4296 and height=3120
pHYs X=23622 and Y=23622 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 -ExecutionPolicy Bypass -File .\ClumsyPilot\tests\verify_path_smoothing_png.ps1
Expected: missing renderer and exporter.
- Step 3: Add the exact Windows rendering dependency
Add:
<PackageReference Include="System.Drawing.Common" Version="10.0.10" />
Then run:
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:
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 4296 × 3120, 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 23622 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
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
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 14: 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 -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:
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
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 -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 9–10 pt text, no Chinese mojibake, no clipping/overlap, curvature limits, and infeasible markers where applicable. Confirm the text SVG portability limitation and external PDF/EPS publishing step are documented.
- Step 6: Commit
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"