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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions Quantum.Debug/BankingProfileTrainPoseFixtures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ namespace Quantum.Debug
{
public sealed class BankingProfileTrainPoseFixture
{
private readonly TrackEvaluator _runtimeEvaluator;
private readonly TrainCarTransformProvider _runtimeProvider;

public BankingProfileTrainPoseFixture(
string name,
TrackDocument document,
Expand Down Expand Up @@ -42,6 +45,8 @@ public BankingProfileTrainPoseFixture(
Definition = definition ?? throw new ArgumentNullException(nameof(definition));
LeadDistance = leadDistance;
CenterlineSampleDistances = Array.AsReadOnly(sampleDistances);
_runtimeEvaluator = new TrackEvaluator(new CompiledTrackRuntime(Document));
_runtimeProvider = new TrainCarTransformProvider(_runtimeEvaluator);
}

public string Name { get; }
Expand All @@ -58,9 +63,7 @@ public BankingProfileTrainPoseFixture(

public TrainPoseResult EvaluateTrainPose()
{
var evaluator = new TrackEvaluator(Document);
var provider = new TrainCarTransformProvider(evaluator);
return provider.EvaluateTrainPose(
return _runtimeProvider.EvaluateTrainPose(
LeadDistance,
Definition,
BankingProfile);
Expand All @@ -74,7 +77,7 @@ public TrainPoseExportV1Dto ExportTrainPose()
public DebugViewportSnapshotV1Dto BuildDebugViewportSnapshot()
{
ExportTrackFrame[] sampledFrames = BankingProfileSampler.SampleFramesAtDistances(
Document,
_runtimeEvaluator,
BankingProfile,
CenterlineSampleDistances);
TrainPoseResult trainPose = EvaluateTrainPose();
Expand Down
5 changes: 3 additions & 2 deletions Quantum.Debug/ProfileBankedHeartlineProofScenario.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,11 @@ private static double MeasureLength(IReadOnlyList<Vector3d> controlPoints)
var points = controlPoints.ToList();
var weights = Enumerable.Repeat(1.0, points.Count).ToList();
var curve = new GSharkNurbsCurveAdapter(points, weights, SpatialDegree);
TrackSamplingOptions samplingOptions = TrackSamplingOptions.Default;
return new ArcLengthLUT(
curve,
TrackSamplingOptions.DefaultArcLengthSamples,
TrackSamplingOptions.DefaultArcLengthTolerance).TotalLength;
samplingOptions.ArcLengthSamples,
samplingOptions.ArcLengthTolerance).TotalLength;
}

private static double[] BuildSampleDistances()
Expand Down
5 changes: 3 additions & 2 deletions Quantum.Debug/SpatialLayoutProofScenario.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,11 @@ private static double MeasureLength(IReadOnlyList<Vector3d> controlPoints)
var points = controlPoints.ToList();
var weights = Enumerable.Repeat(1.0, points.Count).ToList();
var curve = new GSharkNurbsCurveAdapter(points, weights, SpatialDegree);
TrackSamplingOptions samplingOptions = TrackSamplingOptions.Default;
return new ArcLengthLUT(
curve,
TrackSamplingOptions.DefaultArcLengthSamples,
TrackSamplingOptions.DefaultArcLengthTolerance).TotalLength;
samplingOptions.ArcLengthSamples,
samplingOptions.ArcLengthTolerance).TotalLength;
}

private static double[] BuildFrameDistances()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,11 @@ private static double MeasureSpatialLength(Vector3d[] points)
points.ToList(),
Enumerable.Repeat(1.0, points.Length).ToList(),
degree: 3);
TrackSamplingOptions samplingOptions = TrackSamplingOptions.Default;
return new ArcLengthLUT(
curve,
TrackSamplingOptions.DefaultArcLengthSamples,
TrackSamplingOptions.DefaultArcLengthTolerance).TotalLength;
samplingOptions.ArcLengthSamples,
samplingOptions.ArcLengthTolerance).TotalLength;
}

private static TrackLayoutVector3dV2Dto Vector(double x, double y, double z)
Expand Down
4 changes: 2 additions & 2 deletions Quantum.Tests/IO/TrackLayoutPackageV1ProofFixtures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,8 @@ private static double MeasureNurbsLength(
new List<Vector3d>(controlPoints),
new List<double>(weights),
degree),
TrackSamplingOptions.DefaultArcLengthSamples,
TrackSamplingOptions.DefaultArcLengthTolerance);
TrackSamplingOptions.Default.ArcLengthSamples,
TrackSamplingOptions.Default.ArcLengthTolerance);
return curve.Length;
}

Expand Down
145 changes: 145 additions & 0 deletions Quantum.Tests/Track/CanonicalTransportedFrameTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,59 @@ public void TrainBodiesAndBogies_MatchCanonicalFramesAtTheirStations()
}
}

[Fact]
public void DefaultTransportDensity_ImplicitAndExplicitRuntimeOptionsMatch()
{
TrackDocument document = CreateThreeDimensionalDensityDocument();
double[] distances = BuildUniformDistances(document.TotalLength, sampleCount: 21);

ExportTrackFrame[] implicitDefault = new TrackEvaluator(
new CompiledTrackRuntime(document))
.EvaluateFramesAtDistances(distances);
ExportTrackFrame[] explicitDefault = new TrackEvaluator(
new CompiledTrackRuntime(document, TrackSamplingOptions.Default))
.EvaluateFramesAtDistances(distances);

for (int i = 0; i < distances.Length; i++)
{
AssertFrameNear(implicitDefault[i], explicitDefault[i]);
}
}

[Fact]
public void TransportSampleDensity_PreservesCenterlineAndConvergesOrientation()
{
TrackDocument document = CreateThreeDimensionalDensityDocument();
double[] distances = BuildUniformDistances(document.TotalLength, sampleCount: 31);

ExportTrackFrame[] sparse = SampleWithTransportSamples(
document,
transportSamplesPerSegment: 2,
distances);
ExportTrackFrame[] medium = SampleWithTransportSamples(
document,
transportSamplesPerSegment: 16,
distances);
ExportTrackFrame[] dense = SampleWithTransportSamples(
document,
transportSamplesPerSegment: 128,
distances);

AssertCenterlineSamplesNear(sparse, dense);
AssertCenterlineSamplesNear(medium, dense);

double sparseToDense = MaxNormalOrBinormalAngleDeltaRadians(sparse, dense);
double mediumToDense = MaxNormalOrBinormalAngleDeltaRadians(medium, dense);

Assert.True(
sparseToDense > 1e-4,
$"Expected sparse transport density to produce a measurable orientation delta, got {sparseToDense:R} radians.");
Assert.True(
mediumToDense < sparseToDense,
$"Expected denser transport sampling to converge: sparse={sparseToDense:R}, medium={mediumToDense:R}.");
Assert.InRange(mediumToDense, 0.0, 0.05);
}

private static TrackDocument CreateSmoothTwoSegmentDocument()
{
var first = new CubicBezierCurve(
Expand All @@ -238,6 +291,98 @@ private static TrackDocument CreateSmoothTwoSegmentDocument()
});
}

private static TrackDocument CreateThreeDimensionalDensityDocument()
{
var curve = new CubicBezierCurve(
new Vector3d(0.0, 0.0, 0.0),
new Vector3d(12.0, -4.0, 18.0),
new Vector3d(-6.0, 22.0, 30.0),
new Vector3d(28.0, 16.0, 44.0));

return new TrackDocument(new TrackSegment[]
{
new CurvedSegment(
new ArcLengthLUT(curve).TotalLength,
id: "transport-density-3d",
spline: curve)
});
}

private static double[] BuildUniformDistances(double totalLength, int sampleCount)
{
var distances = new double[sampleCount];
for (int i = 0; i < distances.Length; i++)
{
distances[i] = totalLength * i / (sampleCount - 1.0);
}

distances[distances.Length - 1] = totalLength;
return distances;
}

private static ExportTrackFrame[] SampleWithTransportSamples(
TrackDocument document,
int transportSamplesPerSegment,
IReadOnlyList<double> distances)
{
var options = new TrackSamplingOptions(
TrackSamplingOptions.Default.ArcLengthSamples,
TrackSamplingOptions.Default.ArcLengthTolerance,
transportSamplesPerSegment);
var evaluator = new TrackEvaluator(new CompiledTrackRuntime(document, options));
return evaluator.EvaluateFramesAtDistances(distances);
}

private static void AssertCenterlineSamplesNear(
IReadOnlyList<ExportTrackFrame> expected,
IReadOnlyList<ExportTrackFrame> actual)
{
Assert.Equal(expected.Count, actual.Count);
for (int i = 0; i < expected.Count; i++)
{
AssertNear(expected[i].Distance, actual[i].Distance);
AssertVectorNear(expected[i].Position, actual[i].Position);
AssertVectorNear(expected[i].Tangent, actual[i].Tangent);
}
}

private static double MaxNormalOrBinormalAngleDeltaRadians(
IReadOnlyList<ExportTrackFrame> expected,
IReadOnlyList<ExportTrackFrame> actual)
{
Assert.Equal(expected.Count, actual.Count);
double max = 0.0;

for (int i = 0; i < expected.Count; i++)
{
max = System.Math.Max(max, AngleDeltaRadians(expected[i].Normal, actual[i].Normal));
max = System.Math.Max(max, AngleDeltaRadians(expected[i].Binormal, actual[i].Binormal));
}

return max;
}

private static double AngleDeltaRadians(Vector3d expected, Vector3d actual)
{
double dot = Vector3d.Dot(expected.Normalized(), actual.Normalized());
return System.Math.Acos(Clamp(dot, -1.0, 1.0));
}

private static double Clamp(double value, double min, double max)
{
if (value < min)
{
return min;
}

if (value > max)
{
return max;
}

return value;
}

private static void AssertValidFrame(ExportTrackFrame frame)
{
Assert.True(IsFinite(frame.Position));
Expand Down
5 changes: 3 additions & 2 deletions Quantum.Tests/Track/SpatialSectionTrackAuthoringTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,11 @@ private static double MeasureLength(
{
weights ??= Enumerable.Repeat(1.0, controlPoints.Count).ToList();
var curve = new GSharkNurbsCurveAdapter(controlPoints, weights, degree);
TrackSamplingOptions samplingOptions = TrackSamplingOptions.Default;
return new ArcLengthLUT(
curve,
TrackSamplingOptions.DefaultArcLengthSamples,
TrackSamplingOptions.DefaultArcLengthTolerance).TotalLength;
samplingOptions.ArcLengthSamples,
samplingOptions.ArcLengthTolerance).TotalLength;
}

private static TrackStartPose CreateArbitraryStartPose()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,10 +470,11 @@ private static SpatialSectionDefinition CreateSpatial(
var points = controlPoints.ToList();
var weights = Enumerable.Repeat(1.0, points.Count).ToList();
var curve = new GSharkNurbsCurveAdapter(points, weights, degree);
TrackSamplingOptions samplingOptions = TrackSamplingOptions.Default;
double length = new ArcLengthLUT(
curve,
TrackSamplingOptions.DefaultArcLengthSamples,
TrackSamplingOptions.DefaultArcLengthTolerance).TotalLength;
samplingOptions.ArcLengthSamples,
samplingOptions.ArcLengthTolerance).TotalLength;
return new SpatialSectionDefinition(id, length, points, degree, weights);
}

Expand Down
19 changes: 19 additions & 0 deletions Quantum.Tests/Track/TrainRuntimeIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,25 @@ public void RepeatedRuntimePoseEvaluation_DoesNotRereadArcLengthCompilationMetad
Assert.Equal(readsAfterCompilation, curve.LengthReadCount);
}

[Fact]
public void DocumentProviderPoseEvaluation_CompilesOneRuntimeSnapshotPerCall()
{
var curve = new CountingArcLengthLineCurve(30.0);
var document = new TrackDocument(new[]
{
new StraightSegment(curve.Length, "counting", spline: curve)
});
var provider = new TrainCarTransformProvider(new TrackEvaluator(document));
TrainConsistDefinition definition = CreateConsistDefinition();
int readsBeforePoseEvaluation = curve.LengthReadCount;

provider.EvaluateTrainPose(24.0, definition);
Assert.Equal(readsBeforePoseEvaluation + 1, curve.LengthReadCount);

provider.EvaluateTrainPose(24.0, definition);
Assert.Equal(readsBeforePoseEvaluation + 2, curve.LengthReadCount);
}

[Fact]
public void InvalidTrainDistances_DocumentAndRuntimeProvidersThrowEquivalentExceptions()
{
Expand Down
Loading
Loading