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; }
}
}