feat: validate p-rec-annot connector view eligibility
This commit is contained in:
@@ -122,6 +122,7 @@ public interface IPlantConnectorPortReader
|
|||||||
{
|
{
|
||||||
PlantConnectorPortReadResult Read(PlantConnectorRelationshipSnapshot connector);
|
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 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 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);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
using PRecAnnot.Abstractions;
|
||||||
|
using PRecAnnot.Core;
|
||||||
|
|
||||||
|
namespace PRecAnnot.Core.Tests;
|
||||||
|
|
||||||
|
public sealed class ConnectorViewportEligibilityEvaluatorTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void UsesSecondPortAsLegacyPlacementAnchorWhenBothPortsAreVisible()
|
||||||
|
{
|
||||||
|
var result = new ConnectorViewportEligibilityEvaluator().Evaluate(Ports(new(1, 2, 3), new(4, 5, 6), new(1, 0, 0), new(0, 1, 0)), new(0, 0, 1), Bounds());
|
||||||
|
Assert.True(result.Eligible);
|
||||||
|
Assert.Equal(new Point3Dto(4, 5, 6), result.PlacementPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RejectsPortParallelToViewDirection()
|
||||||
|
{
|
||||||
|
var result = new ConnectorViewportEligibilityEvaluator().Evaluate(Ports(new(), new(), new(0, 0, 1), new(1, 0, 0)), new(0, 0, 1), Bounds());
|
||||||
|
Assert.Equal("CONNECTOR_PORT_PARALLEL_TO_VIEW", result.FailureCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RejectsConnectorWhenAnyPortLiesOutsideModelViewExtents()
|
||||||
|
{
|
||||||
|
var result = new ConnectorViewportEligibilityEvaluator().Evaluate(Ports(new(0, 0, 0), new(11, 0, 0), new(1, 0, 0), new(0, 1, 0)), new(0, 0, 1), Bounds());
|
||||||
|
Assert.Equal("CONNECTOR_OUTSIDE_VIEW_EXTENTS", result.FailureCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Bounds3Dto Bounds() => new(new(-10, -10, -10), new(10, 10, 10));
|
||||||
|
private static PlantConnectorPortSnapshot Ports(Point3Dto firstPosition, Point3Dto secondPosition, Point3Dto firstDirection, Point3Dto secondDirection) => new("c", firstPosition, secondPosition, firstDirection, secondDirection);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user