fix: cover Local G2 window targets before splits
This commit is contained in:
@@ -106,12 +106,25 @@ internal sealed class LocalG2WindowPlanner
|
|||||||
double firstEvent = transitions[0].LocalArcLengthMeters;
|
double firstEvent = transitions[0].LocalArcLengthMeters;
|
||||||
double lastEvent = transitions[transitions.Count - 1].LocalArcLengthMeters;
|
double lastEvent = transitions[transitions.Count - 1].LocalArcLengthMeters;
|
||||||
double anchor = (firstEvent + lastEvent) / 2d;
|
double anchor = (firstEvent + lastEvent) / 2d;
|
||||||
foreach (double target in BuildTargets(options, segmentLength))
|
IReadOnlyList<double> targets = BuildTargets(options, segmentLength);
|
||||||
|
double[] ratios = { 0.5d, 0.4d, 0.6d };
|
||||||
|
for (int ratioIndex = 0; ratioIndex < ratios.Length; ratioIndex++)
|
||||||
{
|
{
|
||||||
if (variants.Count >= options.MaximumCandidatesPerRegion) break;
|
for (int targetIndex = 0; targetIndex < targets.Count; targetIndex++)
|
||||||
AddIfLegal(variants, target, 0.5d, anchor, firstEvent, lastEvent, segmentLength, true, options);
|
{
|
||||||
AddIfLegal(variants, target, 0.4d, anchor, firstEvent, lastEvent, segmentLength, false, options);
|
if (variants.Count >= options.MaximumCandidatesPerRegion)
|
||||||
AddIfLegal(variants, target, 0.6d, anchor, firstEvent, lastEvent, segmentLength, false, options);
|
return new ReadOnlyCollection<LocalG2WindowVariant>(variants);
|
||||||
|
AddIfLegal(
|
||||||
|
variants,
|
||||||
|
targets[targetIndex],
|
||||||
|
ratios[ratioIndex],
|
||||||
|
anchor,
|
||||||
|
firstEvent,
|
||||||
|
lastEvent,
|
||||||
|
segmentLength,
|
||||||
|
ratioIndex == 0,
|
||||||
|
options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return new ReadOnlyCollection<LocalG2WindowVariant>(variants);
|
return new ReadOnlyCollection<LocalG2WindowVariant>(variants);
|
||||||
}
|
}
|
||||||
@@ -160,10 +173,10 @@ internal sealed class LocalG2WindowPlanner
|
|||||||
double[] requested =
|
double[] requested =
|
||||||
{
|
{
|
||||||
options.PreferredWindowLengthMeters,
|
options.PreferredWindowLengthMeters,
|
||||||
0.75d * options.PreferredWindowLengthMeters,
|
|
||||||
1.25d * options.PreferredWindowLengthMeters,
|
|
||||||
options.MinimumWindowLengthMeters,
|
options.MinimumWindowLengthMeters,
|
||||||
options.MaximumWindowLengthMeters,
|
options.MaximumWindowLengthMeters,
|
||||||
|
0.75d * options.PreferredWindowLengthMeters,
|
||||||
|
1.25d * options.PreferredWindowLengthMeters,
|
||||||
};
|
};
|
||||||
var targets = new List<double>(requested.Length);
|
var targets = new List<double>(requested.Length);
|
||||||
for (int index = 0; index < requested.Length; index++)
|
for (int index = 0; index < requested.Length; index++)
|
||||||
@@ -262,6 +275,10 @@ internal sealed class LocalG2WindowPlanner
|
|||||||
transitions = new[] { Transition(0.1d, 0) };
|
transitions = new[] { Transition(0.1d, 0) };
|
||||||
segmentLength = 1d;
|
segmentLength = 1d;
|
||||||
break;
|
break;
|
||||||
|
case "InteriorCoverage":
|
||||||
|
transitions = new[] { Transition(1d, 0) };
|
||||||
|
segmentLength = 2d;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException(nameof(scenario));
|
throw new ArgumentOutOfRangeException(nameof(scenario));
|
||||||
}
|
}
|
||||||
@@ -305,7 +322,25 @@ internal sealed class LocalG2WindowPlanner
|
|||||||
exactEnvelope &= Math.Abs(region.MaximumEndArcLengthMeters - maximumEnd) <= 1e-9d;
|
exactEnvelope &= Math.Abs(region.MaximumEndArcLengthMeters - maximumEnd) <= 1e-9d;
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalG2WindowVariant first = regions[0].WindowVariants[0];
|
IReadOnlyList<LocalG2WindowVariant> firstVariants = regions[0].WindowVariants;
|
||||||
|
bool representativeTargetsFirst =
|
||||||
|
firstVariants.Count >= 3 &&
|
||||||
|
Math.Abs(WindowLength(firstVariants[0]) - 0.50d) <= 1e-9d &&
|
||||||
|
Math.Abs(WindowLength(firstVariants[1]) - 0.20d) <= 1e-9d &&
|
||||||
|
Math.Abs(WindowLength(firstVariants[2]) - 0.80d) <= 1e-9d;
|
||||||
|
bool hasAsymmetricVariant = false;
|
||||||
|
for (int index = 0; index < firstVariants.Count; index++)
|
||||||
|
{
|
||||||
|
if (Math.Abs(
|
||||||
|
firstVariants[index].LeftWindowLengthMeters -
|
||||||
|
firstVariants[index].RightWindowLengthMeters) > 1e-9d)
|
||||||
|
{
|
||||||
|
hasAsymmetricVariant = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalG2WindowVariant first = firstVariants[0];
|
||||||
return new WindowPlanningTestSnapshot(
|
return new WindowPlanningTestSnapshot(
|
||||||
regions.Count,
|
regions.Count,
|
||||||
string.Join(",", counts),
|
string.Join(",", counts),
|
||||||
@@ -313,7 +348,10 @@ internal sealed class LocalG2WindowPlanner
|
|||||||
exactEnvelope,
|
exactEnvelope,
|
||||||
first.LeftWindowLengthMeters,
|
first.LeftWindowLengthMeters,
|
||||||
first.RightWindowLengthMeters,
|
first.RightWindowLengthMeters,
|
||||||
string.Join("|", signature));
|
string.Join("|", signature),
|
||||||
|
firstVariants.Count,
|
||||||
|
representativeTargetsFirst,
|
||||||
|
hasAsymmetricVariant);
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class WindowPlanningTestSnapshot
|
public sealed class WindowPlanningTestSnapshot
|
||||||
@@ -325,7 +363,10 @@ internal sealed class LocalG2WindowPlanner
|
|||||||
bool exactEnvelope,
|
bool exactEnvelope,
|
||||||
double firstLeftLength,
|
double firstLeftLength,
|
||||||
double firstRightLength,
|
double firstRightLength,
|
||||||
string signature)
|
string signature,
|
||||||
|
int firstRegionVariantCount,
|
||||||
|
bool representativeTargetsFirst,
|
||||||
|
bool hasAsymmetricVariant)
|
||||||
{
|
{
|
||||||
RegionCount = regionCount;
|
RegionCount = regionCount;
|
||||||
TransitionCounts = transitionCounts;
|
TransitionCounts = transitionCounts;
|
||||||
@@ -334,6 +375,9 @@ internal sealed class LocalG2WindowPlanner
|
|||||||
FirstLeftLength = firstLeftLength;
|
FirstLeftLength = firstLeftLength;
|
||||||
FirstRightLength = firstRightLength;
|
FirstRightLength = firstRightLength;
|
||||||
Signature = signature;
|
Signature = signature;
|
||||||
|
FirstRegionVariantCount = firstRegionVariantCount;
|
||||||
|
RepresentativeTargetsFirst = representativeTargetsFirst;
|
||||||
|
HasAsymmetricVariant = hasAsymmetricVariant;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int RegionCount { get; }
|
public int RegionCount { get; }
|
||||||
@@ -343,8 +387,14 @@ internal sealed class LocalG2WindowPlanner
|
|||||||
public double FirstLeftLength { get; }
|
public double FirstLeftLength { get; }
|
||||||
public double FirstRightLength { get; }
|
public double FirstRightLength { get; }
|
||||||
public string Signature { get; }
|
public string Signature { get; }
|
||||||
|
public int FirstRegionVariantCount { get; }
|
||||||
|
public bool RepresentativeTargetsFirst { get; }
|
||||||
|
public bool HasAsymmetricVariant { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static double WindowLength(LocalG2WindowVariant variant) =>
|
||||||
|
variant.EndArcLengthMeters - variant.StartArcLengthMeters;
|
||||||
|
|
||||||
private static CurvatureTransition Transition(double arcLength, int index)
|
private static CurvatureTransition Transition(double arcLength, int index)
|
||||||
{
|
{
|
||||||
return new CurvatureTransition(
|
return new CurvatureTransition(
|
||||||
|
|||||||
@@ -116,7 +116,15 @@ Assert-True ($boundary.FirstRightLength -gt $boundary.FirstLeftLength) `
|
|||||||
Assert-True ($boundary.MaximumWindowLength -le 0.80 + 1e-9) `
|
Assert-True ($boundary.MaximumWindowLength -le 0.80 + 1e-9) `
|
||||||
'No candidate window may exceed 0.80 m total length.'
|
'No candidate window may exceed 0.80 m total length.'
|
||||||
|
|
||||||
foreach ($snapshot in @($separated, $mergeable, $partition, $boundary)) {
|
$coverage = $planScenario.Invoke($null, @('InteriorCoverage'))
|
||||||
|
Assert-True $coverage.RepresentativeTargetsFirst `
|
||||||
|
'Preferred, minimum, and maximum balanced targets must precede asymmetric variants.'
|
||||||
|
Assert-True $coverage.HasAsymmetricVariant `
|
||||||
|
'Default window planning must retain a legal asymmetric variant after balanced coverage.'
|
||||||
|
Assert-True ($coverage.FirstRegionVariantCount -le 12) `
|
||||||
|
'Window planning must obey the configured default cap.'
|
||||||
|
|
||||||
|
foreach ($snapshot in @($separated, $mergeable, $partition, $boundary, $coverage)) {
|
||||||
Assert-True $snapshot.ExactEnvelope 'Region envelope must equal the extrema of actual legal variants.'
|
Assert-True $snapshot.ExactEnvelope 'Region envelope must equal the extrema of actual legal variants.'
|
||||||
Assert-True ($snapshot.MaximumWindowLength -le 0.80 + 1e-9) `
|
Assert-True ($snapshot.MaximumWindowLength -le 0.80 + 1e-9) `
|
||||||
'MaximumWindowLengthMeters is a total, not a per-side length.'
|
'MaximumWindowLengthMeters is a total, not a per-side length.'
|
||||||
|
|||||||
Reference in New Issue
Block a user