feat: validate p-rec-annot connector view eligibility

This commit is contained in:
codex
2026-08-07 09:48:22 +03:00
parent acfa6723c9
commit 01a0b711d0
3 changed files with 67 additions and 0 deletions
@@ -122,6 +122,7 @@ public interface IPlantConnectorPortReader
{
PlantConnectorPortReadResult Read(PlantConnectorRelationshipSnapshot connector);
}
public sealed record ConnectorViewportEligibility(bool Eligible, Point3Dto? PlacementPoint, string? FailureCode = null);
public sealed record LineNumberOwnedRow(string SourceStableId, int MarkerNumber, string Text, AnnotationPoint MarkerAttributePosition);
public sealed record LineNumberRowMutation(string SourceStableId, int PreviousMarkerNumber, int MarkerNumber, string Text, AnnotationPoint? MarkerAttributePosition);
public sealed record LineNumberTextAsset(string Layer, string TextStyle, double Size, string TextValue, Point2Dto Position);
@@ -0,0 +1,34 @@
using PRecAnnot.Abstractions;
namespace PRecAnnot.Core;
// Port of AutoLinenumberConnections.IsOKPrepareAnnotation's geometry gate.
// The caller performs model-to-paper transformation only after the connector
// is eligible, preserving legacy model-space containment semantics.
public sealed class ConnectorViewportEligibilityEvaluator
{
public ConnectorViewportEligibility Evaluate(PlantConnectorPortSnapshot ports, Point3Dto viewDirection, Bounds3Dto viewExtents, double parallelTolerance = 0.1)
{
if (parallelTolerance < 0) throw new ArgumentOutOfRangeException(nameof(parallelTolerance));
if (Parallel(ports.FirstDirection, viewDirection, parallelTolerance) || Parallel(ports.SecondDirection, viewDirection, parallelTolerance))
return new(false, null, "CONNECTOR_PORT_PARALLEL_TO_VIEW");
if (!Inside(ports.FirstPosition, viewExtents) || !Inside(ports.SecondPosition, viewExtents))
return new(false, null, "CONNECTOR_OUTSIDE_VIEW_EXTENTS");
// Legacy stores ports[1] as the insertion/model anchor.
return new(true, ports.SecondPosition);
}
private static bool Inside(Point3Dto point, Bounds3Dto bounds) =>
point.X >= bounds.Minimum.X && point.X <= bounds.Maximum.X &&
point.Y >= bounds.Minimum.Y && point.Y <= bounds.Maximum.Y &&
point.Z >= bounds.Minimum.Z && point.Z <= bounds.Maximum.Z;
private static bool Parallel(Point3Dto first, Point3Dto second, double tolerance)
{
var firstLength = Math.Sqrt(first.X * first.X + first.Y * first.Y + first.Z * first.Z);
var secondLength = Math.Sqrt(second.X * second.X + second.Y * second.Y + second.Z * second.Z);
if (firstLength == 0 || secondLength == 0) return true;
var cosine = Math.Abs((first.X * second.X + first.Y * second.Y + first.Z * second.Z) / (firstLength * secondLength));
return 1d - cosine <= tolerance;
}
}