using System; namespace OpenNest.Geometry { /// /// Tuning for . Every default is a design choice /// under measurement, not a calibrated safe envelope: until calibration /// establishes one, every alignment pair still requires operator confirmation. /// Values are expressed in the declared units of the drawings being aligned. /// public sealed class AlignmentOptions { /// Chord-error tolerance used when flattening arcs for measurement. public double FlattenTolerance { get; set; } = 0.01; /// /// Target arclength between resampled contour samples. Must resolve the /// intended residual gate: a residual below roughly half this spacing is /// not meaningful. /// public double SamplingSpacing { get; set; } = 0.5; /// Upper bound on samples per contour ring. public int MaxSamplesPerRing { get; set; } = 4000; /// Upper bound on total samples across all rings of one drawing. public int MaxTotalSamples { get; set; } = 20000; /// Trim fraction: worst-distance correspondences excluded from each ICP fit. public double TrimFraction { get; set; } = 0.10; /// Fit iterations per candidate start. public int MaxIterations { get; set; } = 40; /// Pose change (radians) treated as converged. public double RotationEpsilon { get; set; } = 1e-7; /// Translation change in units treated as converged. public double TranslationEpsilon { get; set; } = 1e-7; /// /// Correspondences farther than this count as unmatched (changed geometry), /// not as fit error. Defaults to a generous envelope; the review gate /// reports coverage instead of silently clamping it. /// public double OutlierDistance { get; set; } = 10.0; /// Minimum distinct samples required to attempt a fit at all. public int MinSamples { get; set; } = 8; } /// Why an alignment is not trustworthy. Absence of all flags is not a /// calibrated safe envelope; it only means no review reason fired. [Flags] public enum AlignmentReasons { None = 0, /// The fit stopped on an iteration/work limit or stagnated without converging. FailedConvergence = 1, /// The revised geometry has too few usable samples, too little outer /// support, or too little of the target matched to it. InsufficientSupport = 2, /// Significant changed/unmatched boundary spans in either direction. SignificantBoundaryChange = 4, /// Two or more genuinely distinct poses fit near-equally (symmetry, /// repeated features). The reported transform is still valid geometry. UnresolvedAlternatives = 8, /// Input geometry is invalid: nonfinite coordinates, degenerate or /// unclosed rings, unsupported topology, zero usable perimeter. InvalidGeometry = 16, /// A reflected candidate fits as well as the best rigid one, or the /// input cannot exclude reflection. Reflection is never applied silently. ReflectionUncertain = 32, } /// /// Structured outcome of aligning one revised drawing to one old drawing. /// The transform maps NEW points into the OLD drawing-local frame: /// reflect about the declared local axis (only when is /// true, which the automatic aligner never sets), then rotate by /// (radians), then translate by . /// Diagnostics are in the drawings' declared units. A diagnostic IoU is /// bounded to [0, 1] and is not a calibrated probability. /// public sealed class AlignmentResult { public bool Converged { get; init; } public double Rotation { get; init; } public Vector Translation { get; init; } public bool Reflection { get; init; } /// Review reasons; combined across candidate selection. Empty does /// not license skipping the operator overlay — no calibrated envelope /// exists yet. public AlignmentReasons Reasons { get; init; } /// Trimmed RMS of matched sample-to-segment residuals. public double ResidualRms { get; init; } public double ResidualP50 { get; init; } public double ResidualP90 { get; init; } /// Fraction of NEW samples matching OLD within the outlier bound, and vice versa. public double NewToOldCoverage { get; init; } public double OldToNewCoverage { get; init; } /// Number of distinct converged candidate poses clustered as equivalent. public int EquivalentCandidateCount { get; init; } public int Iterations { get; init; } public int NewSampleCount { get; init; } public int OldSampleCount { get; init; } /// Bounded diagnostic intersection-over-union of the material regions, /// or null when regions are unavailable or degenerate. Never a gate input. public double? DiagnosticIoU { get; init; } public string FailureMessage { get; init; } } }