moonlight-triangulation 1.4.0.3 → 1.4.0.4
raw patch · 44 files changed
+8872/−2766 lines, 44 filesdep ~basedep ~containersdep ~deepseqPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: base, containers, deepseq, moonlight-triangulation
API changes (from Hackage documentation)
- Moonlight.Triangulation: MinkowskiInvalidSegment :: ExactGeometryError -> MinkowskiError
- Moonlight.Triangulation: MinkowskiLineIntersection :: ExactIntersectionError -> MinkowskiError
- Moonlight.Triangulation.Internal.Minkowski.Types: MinkowskiInvalidSegment :: ExactGeometryError -> MinkowskiError
- Moonlight.Triangulation.Internal.Minkowski.Types: MinkowskiLineIntersection :: ExactIntersectionError -> MinkowskiError
- Moonlight.Triangulation.Internal.Overlay.Arrangement: ArrangementMetrics :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> ArrangementMetrics
- Moonlight.Triangulation.Internal.Overlay.Arrangement: AtomicEdge :: ExactSegment -> OverlayEdgeOrigin -> Maybe (leftLabel, leftLabel) -> Maybe (rightLabel, rightLabel) -> AtomicEdge leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Arrangement: CertifiedArrangement :: Vector (AtomicEdge leftLabel rightLabel) -> Vector OverlayVertexSeed -> Vector (Int, Int) -> Set ExactPoint -> ArrangementMetrics -> CertifiedArrangement leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Arrangement: OverlayVertexSeed :: ExactPoint -> Point -> OverlayVertexOrigin -> OverlayVertexSeed
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [arrangementEmbeddingCandidates] :: ArrangementMetrics -> Int
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [arrangementExactCrossings] :: ArrangementMetrics -> Int
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [arrangementInputSegments] :: ArrangementMetrics -> Int
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [arrangementOverlapIntervals] :: ArrangementMetrics -> Int
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [arrangementRelationEvents] :: ArrangementMetrics -> Int
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [arrangementSweepMaximumHeight] :: ArrangementMetrics -> Int
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [arrangementTotalRelationChecks] :: ArrangementMetrics -> Int
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [atomicEdgeLeftTransition] :: AtomicEdge leftLabel rightLabel -> Maybe (leftLabel, leftLabel)
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [atomicEdgeOrigin] :: AtomicEdge leftLabel rightLabel -> OverlayEdgeOrigin
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [atomicEdgeRightTransition] :: AtomicEdge leftLabel rightLabel -> Maybe (rightLabel, rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [atomicEdgeSegment] :: AtomicEdge leftLabel rightLabel -> ExactSegment
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [certifiedAtomicEdges] :: CertifiedArrangement leftLabel rightLabel -> Vector (AtomicEdge leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [certifiedConstraints] :: CertifiedArrangement leftLabel rightLabel -> Vector (Int, Int)
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [certifiedInexactEmbeddingPoints] :: CertifiedArrangement leftLabel rightLabel -> Set ExactPoint
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [certifiedMetrics] :: CertifiedArrangement leftLabel rightLabel -> ArrangementMetrics
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [certifiedVertexSeeds] :: CertifiedArrangement leftLabel rightLabel -> Vector OverlayVertexSeed
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [seedEmbeddedPoint] :: OverlayVertexSeed -> Point
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [seedExactPoint] :: OverlayVertexSeed -> ExactPoint
- Moonlight.Triangulation.Internal.Overlay.Arrangement: [seedOrigin] :: OverlayVertexSeed -> OverlayVertexOrigin
- Moonlight.Triangulation.Internal.Overlay.Arrangement: atomicEdgeFrom :: AtomicEdge leftLabel rightLabel -> ExactPoint
- Moonlight.Triangulation.Internal.Overlay.Arrangement: atomicEdgeTo :: AtomicEdge leftLabel rightLabel -> ExactPoint
- Moonlight.Triangulation.Internal.Overlay.Arrangement: atomicKey :: AtomicEdge leftLabel rightLabel -> ExactEdgeKey
- Moonlight.Triangulation.Internal.Overlay.Arrangement: canonicalEdgeKey :: ExactPoint -> ExactPoint -> ExactEdgeKey
- Moonlight.Triangulation.Internal.Overlay.Arrangement: certifyArrangement :: (Ord leftLabel, Ord rightLabel) => PlanarLayer leftLabel -> PlanarLayer rightLabel -> Either (OverlayError leftLabel rightLabel) (CertifiedArrangement leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Arrangement: compareAround :: ExactPoint -> ExactPoint -> ExactPoint -> Ordering
- Moonlight.Triangulation.Internal.Overlay.Arrangement: data ArrangementMetrics
- Moonlight.Triangulation.Internal.Overlay.Arrangement: data AtomicEdge leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Arrangement: data CertifiedArrangement leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Arrangement: data OverlayVertexSeed
- Moonlight.Triangulation.Internal.Overlay.Arrangement: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Arrangement.OverlayVertexSeed
- Moonlight.Triangulation.Internal.Overlay.Arrangement: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.Overlay.Arrangement.OverlayVertexSeed
- Moonlight.Triangulation.Internal.Overlay.Arrangement: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Arrangement.OverlayVertexSeed
- Moonlight.Triangulation.Internal.Overlay.Arrangement: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Arrangement.OverlayVertexSeed
- Moonlight.Triangulation.Internal.Overlay.Arrangement: instance Moonlight.Triangulation.Internal.Types.HasPosition Moonlight.Triangulation.Internal.Overlay.Arrangement.OverlayVertexSeed
- Moonlight.Triangulation.Internal.Overlay.Arrangement: type ExactEdgeKey = (ExactPoint, ExactPoint)
- Moonlight.Triangulation.Internal.Overlay.Embedding: DraftId :: Int -> DraftId (entity :: DraftEntity)
- Moonlight.Triangulation.Internal.Overlay.Embedding: DraftIncidence :: DraftSegmentId -> DraftSegmentId -> SegmentRelation -> DraftIncidence
- Moonlight.Triangulation.Internal.Overlay.Embedding: DraftNeighborhood :: DraftVertexId -> NonEmpty DraftVertexId -> DraftNeighborhood
- Moonlight.Triangulation.Internal.Overlay.Embedding: DraftReferenceMissing :: DraftReference -> OverlayEmbeddingObstruction
- Moonlight.Triangulation.Internal.Overlay.Embedding: DraftSegmentReference :: DraftSegmentId -> DraftReference
- Moonlight.Triangulation.Internal.Overlay.Embedding: DraftVertexReference :: DraftVertexId -> DraftReference
- Moonlight.Triangulation.Internal.Overlay.Embedding: ExactArrangementDraft :: Map DraftVertexId ExactPoint -> Map DraftSegmentId (DraftVertexId, DraftVertexId) -> Map DraftSourceId [(ExactRational, DraftVertexId)] -> [DraftIncidence] -> [DraftNeighborhood] -> ExactArrangementDraft
- Moonlight.Triangulation.Internal.Overlay.Embedding: GlobalNoNewCrossing :: EmbeddingObligation
- Moonlight.Triangulation.Internal.Overlay.Embedding: GlobalRelationAdded :: DraftSegmentId -> DraftSegmentId -> SegmentRelation -> OverlayEmbeddingObstruction
- Moonlight.Triangulation.Internal.Overlay.Embedding: GlobalRelationChanged :: DraftSegmentId -> DraftSegmentId -> SegmentRelation -> SegmentRelation -> OverlayEmbeddingObstruction
- Moonlight.Triangulation.Internal.Overlay.Embedding: GlobalRelationRemoved :: DraftSegmentId -> DraftSegmentId -> SegmentRelation -> OverlayEmbeddingObstruction
- Moonlight.Triangulation.Internal.Overlay.Embedding: IncidenceRelationChanged :: DraftSegmentId -> DraftSegmentId -> SegmentRelation -> SegmentRelation -> SegmentRelation -> OverlayEmbeddingObstruction
- Moonlight.Triangulation.Internal.Overlay.Embedding: LocalEmbeddingCertificate :: Int -> Int -> Int -> Int -> Map DraftVertexId QueryPoint -> EmbeddingResidual -> LocalEmbeddingCertificate
- Moonlight.Triangulation.Internal.Overlay.Embedding: NeighborhoodRotationChanged :: DraftVertexId -> NonEmpty DraftVertexId -> NonEmpty DraftVertexId -> OverlayEmbeddingObstruction
- Moonlight.Triangulation.Internal.Overlay.Embedding: ProjectedSegmentCollapsed :: DraftSegmentId -> DraftVertexId -> DraftVertexId -> OverlayEmbeddingObstruction
- Moonlight.Triangulation.Internal.Overlay.Embedding: RoundedVerticesCollide :: DraftVertexId -> DraftVertexId -> Point -> OverlayEmbeddingObstruction
- Moonlight.Triangulation.Internal.Overlay.Embedding: SplitOrderNotPreserved :: DraftSourceId -> DraftVertexId -> DraftVertexId -> OverlayEmbeddingObstruction
- Moonlight.Triangulation.Internal.Overlay.Embedding: VertexProjectionRefused :: DraftVertexId -> PointValidationError -> OverlayEmbeddingObstruction
- Moonlight.Triangulation.Internal.Overlay.Embedding: [certificateIncidenceRelationPreservationCount] :: LocalEmbeddingCertificate -> Int
- Moonlight.Triangulation.Internal.Overlay.Embedding: [certificateNeighborhoodRotationPreservationCount] :: LocalEmbeddingCertificate -> Int
- Moonlight.Triangulation.Internal.Overlay.Embedding: [certificateResidual] :: LocalEmbeddingCertificate -> EmbeddingResidual
- Moonlight.Triangulation.Internal.Overlay.Embedding: [certificateRoundedVertexDistinctnessCount] :: LocalEmbeddingCertificate -> Int
- Moonlight.Triangulation.Internal.Overlay.Embedding: [certificateRoundedVertices] :: LocalEmbeddingCertificate -> Map DraftVertexId QueryPoint
- Moonlight.Triangulation.Internal.Overlay.Embedding: [certificateSplitOrderPreservationCount] :: LocalEmbeddingCertificate -> Int
- Moonlight.Triangulation.Internal.Overlay.Embedding: [draftIncidenceFirstSegment] :: DraftIncidence -> DraftSegmentId
- Moonlight.Triangulation.Internal.Overlay.Embedding: [draftIncidenceRelation] :: DraftIncidence -> SegmentRelation
- Moonlight.Triangulation.Internal.Overlay.Embedding: [draftIncidenceSecondSegment] :: DraftIncidence -> DraftSegmentId
- Moonlight.Triangulation.Internal.Overlay.Embedding: [draftIncidences] :: ExactArrangementDraft -> [DraftIncidence]
- Moonlight.Triangulation.Internal.Overlay.Embedding: [draftNeighborhoodCenter] :: DraftNeighborhood -> DraftVertexId
- Moonlight.Triangulation.Internal.Overlay.Embedding: [draftNeighborhoodNeighbors] :: DraftNeighborhood -> NonEmpty DraftVertexId
- Moonlight.Triangulation.Internal.Overlay.Embedding: [draftNeighborhoods] :: ExactArrangementDraft -> [DraftNeighborhood]
- Moonlight.Triangulation.Internal.Overlay.Embedding: [draftSegments] :: ExactArrangementDraft -> Map DraftSegmentId (DraftVertexId, DraftVertexId)
- Moonlight.Triangulation.Internal.Overlay.Embedding: [draftSourceMemberships] :: ExactArrangementDraft -> Map DraftSourceId [(ExactRational, DraftVertexId)]
- Moonlight.Triangulation.Internal.Overlay.Embedding: [draftVertices] :: ExactArrangementDraft -> Map DraftVertexId ExactPoint
- Moonlight.Triangulation.Internal.Overlay.Embedding: certifyLocalEmbedding :: ExactArrangementDraft -> Either (NonEmpty OverlayEmbeddingObstruction) LocalEmbeddingCertificate
- Moonlight.Triangulation.Internal.Overlay.Embedding: data DraftIncidence
- Moonlight.Triangulation.Internal.Overlay.Embedding: data DraftNeighborhood
- Moonlight.Triangulation.Internal.Overlay.Embedding: data DraftReference
- Moonlight.Triangulation.Internal.Overlay.Embedding: data EmbeddingObligation
- Moonlight.Triangulation.Internal.Overlay.Embedding: data EmbeddingResidual
- Moonlight.Triangulation.Internal.Overlay.Embedding: data ExactArrangementDraft
- Moonlight.Triangulation.Internal.Overlay.Embedding: data LocalEmbeddingCertificate
- Moonlight.Triangulation.Internal.Overlay.Embedding: data OverlayEmbeddingObstruction
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.Overlay.Embedding.DraftId entity)
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Embedding.DraftIncidence
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Embedding.DraftNeighborhood
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Embedding.DraftReference
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Embedding.EmbeddingObligation
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Embedding.EmbeddingResidual
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Embedding.ExactArrangementDraft
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Embedding.LocalEmbeddingCertificate
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Embedding.OverlayEmbeddingObstruction
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Base.Applicative Moonlight.Triangulation.Internal.Overlay.Embedding.Validation
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Base.Functor Moonlight.Triangulation.Internal.Overlay.Embedding.Validation
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.Overlay.Embedding.DraftId entity)
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Embedding.DraftIncidence
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Embedding.DraftNeighborhood
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Embedding.DraftReference
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Embedding.EmbeddingObligation
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Embedding.EmbeddingResidual
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Embedding.ExactArrangementDraft
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Embedding.LocalEmbeddingCertificate
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Embedding.OverlayEmbeddingObstruction
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.Overlay.Embedding.DraftId entity)
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.Overlay.Embedding.DraftIncidence
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.Overlay.Embedding.DraftNeighborhood
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.Overlay.Embedding.DraftReference
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.Overlay.Embedding.EmbeddingObligation
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.Overlay.Embedding.EmbeddingResidual
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Enum.Bounded Moonlight.Triangulation.Internal.Overlay.Embedding.EmbeddingObligation
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Enum.Enum Moonlight.Triangulation.Internal.Overlay.Embedding.EmbeddingObligation
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.Overlay.Embedding.DraftId entity)
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Embedding.DraftIncidence
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Embedding.DraftNeighborhood
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Embedding.DraftReference
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Embedding.EmbeddingObligation
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Embedding.EmbeddingResidual
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Embedding.ExactArrangementDraft
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Embedding.LocalEmbeddingCertificate
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Embedding.OverlayEmbeddingObstruction
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.Overlay.Embedding.DraftId entity)
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Embedding.DraftIncidence
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Embedding.DraftNeighborhood
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Embedding.DraftReference
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Embedding.EmbeddingObligation
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Embedding.EmbeddingResidual
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Embedding.ExactArrangementDraft
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Embedding.LocalEmbeddingCertificate
- Moonlight.Triangulation.Internal.Overlay.Embedding: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Embedding.OverlayEmbeddingObstruction
- Moonlight.Triangulation.Internal.Overlay.Embedding: milestoneOneResidual :: EmbeddingResidual
- Moonlight.Triangulation.Internal.Overlay.Embedding: newtype DraftId (entity :: DraftEntity)
- Moonlight.Triangulation.Internal.Overlay.Embedding: residualUndischargedObligations :: EmbeddingResidual -> NonEmpty EmbeddingObligation
- Moonlight.Triangulation.Internal.Overlay.Embedding: type DraftSegmentId = DraftId 'DraftSegmentEntity
- Moonlight.Triangulation.Internal.Overlay.Embedding: type DraftSourceId = DraftId 'DraftSourceEntity
- Moonlight.Triangulation.Internal.Overlay.Embedding: type DraftVertexId = DraftId 'DraftVertexEntity
- Moonlight.Triangulation.Internal.Overlay.Resident: CanonicalOverlayDiagonals :: OverlayDiagonalSchedule
- Moonlight.Triangulation.Internal.Overlay.Resident: FlipFirstAdmissibleDiagonal :: OverlayDiagonalSchedule
- Moonlight.Triangulation.Internal.Overlay.Resident: data OverlayDiagonalSchedule
- Moonlight.Triangulation.Internal.Overlay.Resident: edgeSupport :: (Ord leftLabel, Ord rightLabel) => OverlayResult leftLabel rightLabel -> UndirectedEdgeId -> Either OverlayCellWitness (OverlayCellSupport leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Resident: faceCarriesExactArea :: OverlayResult leftLabel rightLabel -> FaceId -> Bool
- Moonlight.Triangulation.Internal.Overlay.Resident: faceLabels :: OverlayResult leftLabel rightLabel -> FaceId -> Either OverlayCellWitness (leftLabel, rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Resident: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Resident.OverlayDiagonalSchedule
- Moonlight.Triangulation.Internal.Overlay.Resident: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.Overlay.Resident.OverlayDiagonalSchedule
- Moonlight.Triangulation.Internal.Overlay.Resident: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Resident.OverlayDiagonalSchedule
- Moonlight.Triangulation.Internal.Overlay.Resident: regionFaceLabels :: OverlayResult leftLabel rightLabel -> FaceId -> Either RegionPublicationError (leftLabel, rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Resident: residentOverlay :: (Ord leftLabel, Ord rightLabel) => OverlayDiagonalSchedule -> (leftLabel, rightLabel) -> CertifiedArrangement leftLabel rightLabel -> Either (OverlayError leftLabel rightLabel) (OverlayResult leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Resident: vertexSupport :: (Ord leftLabel, Ord rightLabel) => OverlayResult leftLabel rightLabel -> VertexId -> Either OverlayCellWitness (OverlayCellSupport leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: BoundaryHoleLoop :: Int -> BoundaryLoopRef
- Moonlight.Triangulation.Internal.Overlay.Types: BoundaryOuterLoop :: BoundaryLoopRef
- Moonlight.Triangulation.Internal.Overlay.Types: BoundaryRef :: Int -> BoundaryLoopRef -> Int -> BoundaryRef (feature :: BoundaryFeature) (operand :: OverlayOperand)
- Moonlight.Triangulation.Internal.Overlay.Types: BoundedOverlayCell :: PolygonComponent -> OverlayCellGeometry
- Moonlight.Triangulation.Internal.Overlay.Types: ClosedIntersectionSelection :: OverlaySelectionKind
- Moonlight.Triangulation.Internal.Overlay.Types: ClosedUnionSelection :: OverlaySelectionKind
- Moonlight.Triangulation.Internal.Overlay.Types: LeftOverlayOperand :: OverlayOperand
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayArrangementInvalid :: OverlayArrangementObstruction leftLabel rightLabel -> OverlayError leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayAtomicConstraintMissing :: ExactPoint -> ExactPoint -> OverlayCellWitness
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayAtomicConstraintOrientationMismatch :: UndirectedEdgeId -> ExactPoint -> ExactPoint -> OverlayCellWitness
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayBoundary :: OverlayEdgeOrigin -> OverlayEdge
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayBoundaryEdgeNotConstrained :: UndirectedEdgeId -> OverlayCellWitness
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayBuildFailed :: CdtError -> OverlayError leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayCell :: leftLabel -> rightLabel -> OverlayCellGeometry -> OverlayCell leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayCellCycleDidNotClose :: ExactPoint -> ExactPoint -> OverlayArrangementObstruction leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayCellFace :: OverlayCellId -> OverlayFace
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayCellId :: Int -> OverlayCellId
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayCellPayloadMissing :: OverlayCellId -> OverlayCellWitness
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayCellSupport :: OverlaySupport leftLabel -> OverlaySupport rightLabel -> OverlayCellSupport leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayCollapsedFace :: OverlayCellId -> OverlayFace
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayCollapsedFacesAmbiguous :: NonEmpty FaceId -> NonEmpty OverlayCellId -> OverlayArrangementObstruction leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayCollapsedFacesUnowned :: NonEmpty FaceId -> OverlayArrangementObstruction leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayDiagonal :: OverlayEdge
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayDuplicateCellSignature :: PolygonComponent -> OverlayArrangementObstruction leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayEdgeOrigin :: [BoundaryEdgeRef 'LeftOverlayOperand] -> [BoundaryEdgeRef 'RightOverlayOperand] -> OverlayEdgeOrigin
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayEdgeSupportMissing :: UndirectedEdgeId -> OverlayCellWitness
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayEmbeddedVertexCountMismatch :: Int -> Int -> OverlayCellWitness
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayEmbeddingRefused :: NonEmpty OverlayEmbeddingObstruction -> OverlayError leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayExactArithmetic :: ExactArithmeticError -> OverlayError leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayExactVertexMissing :: ExactPoint -> OverlayCellWitness
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayFaceComponentEmpty :: OverlayArrangementObstruction leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayLeftSourceSideConflict :: OverlayEdgeOrigin -> NonEmpty leftLabel -> OverlayArrangementObstruction leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayProvenanceIncomplete :: OverlayCellWitness -> OverlayError leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayReceipt :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> OverlayReceipt
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayRegionPublicationFailed :: RegionPublicationError -> OverlayError leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayResidentFaceArity :: FaceId -> Int -> OverlayArrangementObstruction leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayResidentFaceLabelConflict :: FaceId -> UndirectedEdgeId -> (leftLabel, rightLabel) -> (leftLabel, rightLabel) -> OverlayArrangementObstruction leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayResidentFaceOrientationReversed :: FaceId -> OverlayArrangementObstruction leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayResidentFaceUnassigned :: FaceId -> OverlayCellWitness
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayResult :: Triangulation 'Constrained OverlayVertex () OverlayEdge OverlayFace -> Vector (OverlayCell leftLabel rightLabel) -> (leftLabel, rightLabel) -> OverlayReceipt -> OverlayResult leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayRightSourceSideConflict :: OverlayEdgeOrigin -> NonEmpty rightLabel -> OverlayArrangementObstruction leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayRotationDegenerate :: ExactPoint -> OverlayArrangementObstruction leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlaySegmentEventsInvalid :: ExactSegmentEventObstruction -> OverlayError leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlaySelectionContainsUnboundedCell :: OverlaySelectionKind -> OverlaySelectionError
- Moonlight.Triangulation.Internal.Overlay.Types: OverlaySelectionInvalid :: CellSelectionError -> OverlaySelectionError
- Moonlight.Triangulation.Internal.Overlay.Types: OverlaySelectionProvenance :: OverlayCellWitness -> OverlaySelectionError
- Moonlight.Triangulation.Internal.Overlay.Types: OverlaySupport :: NonEmpty label -> OverlaySupport label
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayTransitionSourceMismatch :: FaceId -> UndirectedEdgeId -> (leftLabel, rightLabel) -> (leftLabel, rightLabel) -> OverlayArrangementObstruction leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayUnexpectedConstraint :: UndirectedEdgeId -> OverlayCellWitness
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayVertex :: ExactPoint -> OverlayVertexOrigin -> OverlayVertex
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayVertexOrigin :: [BoundaryVertexRef 'LeftOverlayOperand] -> [BoundaryVertexRef 'RightOverlayOperand] -> [BoundaryEdgeRef 'LeftOverlayOperand] -> [BoundaryEdgeRef 'RightOverlayOperand] -> OverlayVertexOrigin
- Moonlight.Triangulation.Internal.Overlay.Types: OverlayVertexSupportMissing :: VertexId -> OverlayCellWitness
- Moonlight.Triangulation.Internal.Overlay.Types: RegularizedDifferenceSelection :: OverlaySelectionKind
- Moonlight.Triangulation.Internal.Overlay.Types: RightOverlayOperand :: OverlayOperand
- Moonlight.Triangulation.Internal.Overlay.Types: UnboundedOverlayCell :: [ExactLoop] -> OverlayCellGeometry
- Moonlight.Triangulation.Internal.Overlay.Types: [boundaryRefComponent] :: BoundaryRef (feature :: BoundaryFeature) (operand :: OverlayOperand) -> Int
- Moonlight.Triangulation.Internal.Overlay.Types: [boundaryRefLocalIndex] :: BoundaryRef (feature :: BoundaryFeature) (operand :: OverlayOperand) -> Int
- Moonlight.Triangulation.Internal.Overlay.Types: [boundaryRefLoop] :: BoundaryRef (feature :: BoundaryFeature) (operand :: OverlayOperand) -> BoundaryLoopRef
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayArrangementCells] :: OverlayReceipt -> Int
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayAtomicEdges] :: OverlayReceipt -> Int
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayCellGeometry] :: OverlayCell leftLabel rightLabel -> OverlayCellGeometry
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayCellLeft] :: OverlayCell leftLabel rightLabel -> leftLabel
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayCellRight] :: OverlayCell leftLabel rightLabel -> rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayEdgeLeftSources] :: OverlayEdgeOrigin -> [BoundaryEdgeRef 'LeftOverlayOperand]
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayEdgeRightSources] :: OverlayEdgeOrigin -> [BoundaryEdgeRef 'RightOverlayOperand]
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayEmbeddingCandidates] :: OverlayReceipt -> Int
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayExactCrossings] :: OverlayReceipt -> Int
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayExactPoint] :: OverlayVertex -> ExactPoint
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayFaceCellId] :: OverlayFace -> OverlayCellId
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayInputSegments] :: OverlayReceipt -> Int
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayOriginLeftEdges] :: OverlayVertexOrigin -> [BoundaryEdgeRef 'LeftOverlayOperand]
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayOriginLeftVertices] :: OverlayVertexOrigin -> [BoundaryVertexRef 'LeftOverlayOperand]
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayOriginRightEdges] :: OverlayVertexOrigin -> [BoundaryEdgeRef 'RightOverlayOperand]
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayOriginRightVertices] :: OverlayVertexOrigin -> [BoundaryVertexRef 'RightOverlayOperand]
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayOutputVertices] :: OverlayReceipt -> Int
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayOverlapIntervals] :: OverlayReceipt -> Int
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayRelationEvents] :: OverlayReceipt -> Int
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayResidentFaces] :: OverlayReceipt -> Int
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayResultCells] :: OverlayResult leftLabel rightLabel -> Vector (OverlayCell leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayResultOutsideLabels] :: OverlayResult leftLabel rightLabel -> (leftLabel, rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayResultReceipt] :: OverlayResult leftLabel rightLabel -> OverlayReceipt
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayResultTriangulation] :: OverlayResult leftLabel rightLabel -> Triangulation 'Constrained OverlayVertex () OverlayEdge OverlayFace
- Moonlight.Triangulation.Internal.Overlay.Types: [overlaySupportLeft] :: OverlayCellSupport leftLabel rightLabel -> OverlaySupport leftLabel
- Moonlight.Triangulation.Internal.Overlay.Types: [overlaySupportRight] :: OverlayCellSupport leftLabel rightLabel -> OverlaySupport rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: [overlaySweepMaximumHeight] :: OverlayReceipt -> Int
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayTotalRelationChecks] :: OverlayReceipt -> Int
- Moonlight.Triangulation.Internal.Overlay.Types: [overlayVertexOrigin] :: OverlayVertex -> OverlayVertexOrigin
- Moonlight.Triangulation.Internal.Overlay.Types: data BoundaryLoopRef
- Moonlight.Triangulation.Internal.Overlay.Types: data BoundaryRef (feature :: BoundaryFeature) (operand :: OverlayOperand)
- Moonlight.Triangulation.Internal.Overlay.Types: data OverlayArrangementObstruction leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: data OverlayCell leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: data OverlayCellGeometry
- Moonlight.Triangulation.Internal.Overlay.Types: data OverlayCellSupport leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: data OverlayCellWitness
- Moonlight.Triangulation.Internal.Overlay.Types: data OverlayEdge
- Moonlight.Triangulation.Internal.Overlay.Types: data OverlayEdgeOrigin
- Moonlight.Triangulation.Internal.Overlay.Types: data OverlayError leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: data OverlayFace
- Moonlight.Triangulation.Internal.Overlay.Types: data OverlayOperand
- Moonlight.Triangulation.Internal.Overlay.Types: data OverlayReceipt
- Moonlight.Triangulation.Internal.Overlay.Types: data OverlayResult leftLabel rightLabel
- Moonlight.Triangulation.Internal.Overlay.Types: data OverlaySelectionError
- Moonlight.Triangulation.Internal.Overlay.Types: data OverlaySelectionKind
- Moonlight.Triangulation.Internal.Overlay.Types: data OverlayVertex
- Moonlight.Triangulation.Internal.Overlay.Types: data OverlayVertexOrigin
- Moonlight.Triangulation.Internal.Overlay.Types: instance (Control.DeepSeq.NFData leftLabel, Control.DeepSeq.NFData rightLabel) => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.Overlay.Types.OverlayArrangementObstruction leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance (Control.DeepSeq.NFData leftLabel, Control.DeepSeq.NFData rightLabel) => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.Overlay.Types.OverlayCell leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance (Control.DeepSeq.NFData leftLabel, Control.DeepSeq.NFData rightLabel) => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellSupport leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance (Control.DeepSeq.NFData leftLabel, Control.DeepSeq.NFData rightLabel) => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.Overlay.Types.OverlayError leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance (Control.DeepSeq.NFData leftLabel, Control.DeepSeq.NFData rightLabel) => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.Overlay.Types.OverlayResult leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance (GHC.Internal.Classes.Eq leftLabel, GHC.Internal.Classes.Eq rightLabel) => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.Overlay.Types.OverlayArrangementObstruction leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance (GHC.Internal.Classes.Eq leftLabel, GHC.Internal.Classes.Eq rightLabel) => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.Overlay.Types.OverlayCell leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance (GHC.Internal.Classes.Eq leftLabel, GHC.Internal.Classes.Eq rightLabel) => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellSupport leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance (GHC.Internal.Classes.Eq leftLabel, GHC.Internal.Classes.Eq rightLabel) => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.Overlay.Types.OverlayError leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance (GHC.Internal.Classes.Ord leftLabel, GHC.Internal.Classes.Ord rightLabel) => GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.Overlay.Types.OverlayCell leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance (GHC.Internal.Classes.Ord leftLabel, GHC.Internal.Classes.Ord rightLabel) => GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellSupport leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance (GHC.Internal.Show.Show leftLabel, GHC.Internal.Show.Show rightLabel) => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.Overlay.Types.OverlayArrangementObstruction leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance (GHC.Internal.Show.Show leftLabel, GHC.Internal.Show.Show rightLabel) => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.Overlay.Types.OverlayCell leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance (GHC.Internal.Show.Show leftLabel, GHC.Internal.Show.Show rightLabel) => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellSupport leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance (GHC.Internal.Show.Show leftLabel, GHC.Internal.Show.Show rightLabel) => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.Overlay.Types.OverlayError leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.Overlay.Types.BoundaryRef feature operand)
- Moonlight.Triangulation.Internal.Overlay.Types: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Types.BoundaryLoopRef
- Moonlight.Triangulation.Internal.Overlay.Types: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellGeometry
- Moonlight.Triangulation.Internal.Overlay.Types: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellId
- Moonlight.Triangulation.Internal.Overlay.Types: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellWitness
- Moonlight.Triangulation.Internal.Overlay.Types: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Types.OverlayEdge
- Moonlight.Triangulation.Internal.Overlay.Types: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Types.OverlayEdgeOrigin
- Moonlight.Triangulation.Internal.Overlay.Types: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Types.OverlayFace
- Moonlight.Triangulation.Internal.Overlay.Types: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Types.OverlayReceipt
- Moonlight.Triangulation.Internal.Overlay.Types: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Types.OverlaySelectionError
- Moonlight.Triangulation.Internal.Overlay.Types: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Types.OverlaySelectionKind
- Moonlight.Triangulation.Internal.Overlay.Types: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Types.OverlayVertex
- Moonlight.Triangulation.Internal.Overlay.Types: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.Overlay.Types.OverlayVertexOrigin
- Moonlight.Triangulation.Internal.Overlay.Types: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.Overlay.Types.OverlaySupport label)
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.Overlay.Types.BoundaryRef feature operand)
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Types.BoundaryLoopRef
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellGeometry
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellId
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellWitness
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Types.OverlayEdge
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Types.OverlayEdgeOrigin
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Types.OverlayFace
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Types.OverlayReceipt
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Types.OverlaySelectionError
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Types.OverlaySelectionKind
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Types.OverlayVertex
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.Overlay.Types.OverlayVertexOrigin
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.Overlay.Types.OverlaySupport label)
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.Overlay.Types.BoundaryRef feature operand)
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.Overlay.Types.BoundaryLoopRef
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellGeometry
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellId
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.Overlay.Types.OverlayEdge
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.Overlay.Types.OverlayEdgeOrigin
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.Overlay.Types.OverlayFace
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.Overlay.Types.OverlayReceipt
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.Overlay.Types.OverlaySelectionKind
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.Overlay.Types.OverlayVertex
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.Overlay.Types.OverlayVertexOrigin
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.Overlay.Types.OverlaySupport label)
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.Overlay.Types.BoundaryRef feature operand)
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.Overlay.Types.OverlayArrangementObstruction leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.Overlay.Types.OverlayCell leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellSupport leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.Overlay.Types.OverlayError leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.Overlay.Types.OverlayResult leftLabel rightLabel)
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.Overlay.Types.OverlaySupport label)
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Types.BoundaryLoopRef
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellGeometry
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellId
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellWitness
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Types.OverlayEdge
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Types.OverlayEdgeOrigin
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Types.OverlayFace
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Types.OverlayReceipt
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Types.OverlaySelectionError
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Types.OverlaySelectionKind
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Types.OverlayVertex
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.Overlay.Types.OverlayVertexOrigin
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.Overlay.Types.BoundaryRef feature operand)
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Types.BoundaryLoopRef
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellGeometry
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellId
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Types.OverlayCellWitness
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Types.OverlayEdge
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Types.OverlayEdgeOrigin
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Types.OverlayFace
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Types.OverlayReceipt
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Types.OverlaySelectionError
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Types.OverlaySelectionKind
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Types.OverlayVertex
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.Overlay.Types.OverlayVertexOrigin
- Moonlight.Triangulation.Internal.Overlay.Types: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.Overlay.Types.OverlaySupport label)
- Moonlight.Triangulation.Internal.Overlay.Types: newtype OverlayCellId
- Moonlight.Triangulation.Internal.Overlay.Types: newtype OverlaySupport label
- Moonlight.Triangulation.Internal.Overlay.Types: overlaySupportLabels :: OverlaySupport label -> NonEmpty label
- Moonlight.Triangulation.Internal.Overlay.Types: type BoundaryEdgeRef (operand :: OverlayOperand) = BoundaryRef 'BoundaryEdgeFeature operand
- Moonlight.Triangulation.Internal.Overlay.Types: type BoundaryVertexRef (operand :: OverlayOperand) = BoundaryRef 'BoundaryVertexFeature operand
- Moonlight.Triangulation.Minkowski: MinkowskiInvalidSegment :: ExactGeometryError -> MinkowskiError
- Moonlight.Triangulation.Minkowski: MinkowskiLineIntersection :: ExactIntersectionError -> MinkowskiError
+ Moonlight.Triangulation: AffineForm :: ExactRational -> ExactRational -> ExactRational -> AffineForm
+ Moonlight.Triangulation: BoundedPowerDual :: ExactSegment -> PowerDualEdge
+ Moonlight.Triangulation: CoincidentDominatedBy :: label -> PowerCellDisposition label
+ Moonlight.Triangulation: CoincidentEquivalentTo :: label -> PowerCellDisposition label
+ Moonlight.Triangulation: CollapsedPowerDual :: ExactPoint -> PowerDualEdge
+ Moonlight.Triangulation: DuplicatePowerSiteLabel :: label -> PowerDiagramError label
+ Moonlight.Triangulation: EmptyPowerCell :: PowerCellDisposition label
+ Moonlight.Triangulation: ExactRayZeroDirection :: ExactPoint -> ExactGeometryError
+ Moonlight.Triangulation: FullLinePowerDual :: ExactAffineLine -> PowerDualEdge
+ Moonlight.Triangulation: LayerCoverageGap :: CoverGap -> LayerCoverageError label
+ Moonlight.Triangulation: LayerCoverageGapPublicationFailed :: RegionPublicationError -> LayerCoverageError label
+ Moonlight.Triangulation: LayerCoverageOverlayFailed :: OverlayError Bool label -> LayerCoverageError label
+ Moonlight.Triangulation: LowerDimensionalPowerCell :: NonEmpty ExactPoint -> PowerCellDisposition label
+ Moonlight.Triangulation: MinkowskiClipFailed :: ExactClipError -> MinkowskiError
+ Moonlight.Triangulation: MinkowskiInvalidHalfPlane :: ExactHalfPlaneError -> MinkowskiError
+ Moonlight.Triangulation: PowerBisectorInvalid :: label -> label -> ExactHalfPlaneError -> PowerDiagramError label
+ Moonlight.Triangulation: PowerCellClipFailed :: label -> ExactClipError -> PowerDiagramError label
+ Moonlight.Triangulation: PowerDiagramOutsideLabelCollides :: label -> PowerDiagramError label
+ Moonlight.Triangulation: PowerDiagramReceipt :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> PowerDiagramReceipt
+ Moonlight.Triangulation: PowerDomainInvalid :: ExactHalfPlaneError -> PowerDiagramError label
+ Moonlight.Triangulation: PowerRegularTopologyFailed :: RegularTopologyError label -> PowerDiagramError label
+ Moonlight.Triangulation: PowerSitePositionInvalid :: label -> PointValidationError -> PowerDiagramError label
+ Moonlight.Triangulation: PowerWeightNonFinite :: NonFiniteValue -> PowerWeightError
+ Moonlight.Triangulation: PublishedPowerCell :: ConvexPolygon -> PowerCellDisposition label
+ Moonlight.Triangulation: RegularSiteCoincidentDominatedBy :: label -> RegularSiteDisposition label
+ Moonlight.Triangulation: RegularSiteCoincidentEquivalentTo :: label -> RegularSiteDisposition label
+ Moonlight.Triangulation: RegularSiteHidden :: RegularSiteDisposition label
+ Moonlight.Triangulation: RegularSiteLowerDimensional :: RegularSiteDisposition label
+ Moonlight.Triangulation: RegularSiteVisible :: RegularSiteDisposition label
+ Moonlight.Triangulation: RegularTopologyAxisInvalid :: label -> label -> ExactHalfPlaneError -> RegularTopologyError label
+ Moonlight.Triangulation: RegularTopologyDualRayInvalid :: label -> label -> ExactGeometryError -> RegularTopologyError label
+ Moonlight.Triangulation: RegularTopologyDualSegmentInvalid :: label -> label -> ExactGeometryError -> RegularTopologyError label
+ Moonlight.Triangulation: RegularTopologyDualVertexInvalid :: label -> label -> label -> ExactIntersectionError -> RegularTopologyError label
+ Moonlight.Triangulation: RegularTopologyFlatBoundary :: label -> label -> label -> RegularTopologyError label
+ Moonlight.Triangulation: RegularTopologyNonManifoldEdge :: label -> label -> Int -> RegularTopologyError label
+ Moonlight.Triangulation: RegularTopologySlopeHullInvalid :: MinkowskiError -> RegularTopologyError label
+ Moonlight.Triangulation: RegularTopologySlopeVertexMissing :: ExactPoint -> RegularTopologyError label
+ Moonlight.Triangulation: RegularTriangulationReceipt :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> RegularTriangulationReceipt
+ Moonlight.Triangulation: UnboundedPowerDual :: ExactRay -> PowerDualEdge
+ Moonlight.Triangulation: UpperEnvelopeEmptyForms :: UpperEnvelopeError label
+ Moonlight.Triangulation: UpperEnvelopePowerConstructionFailed :: PowerDiagramError label -> UpperEnvelopeError label
+ Moonlight.Triangulation: UpperEnvelopeWindowHullFailed :: MinkowskiError -> UpperEnvelopeError label
+ Moonlight.Triangulation: UpperEnvelopeWindowOverlayFailed :: OverlayError (Maybe label) Bool -> UpperEnvelopeError label
+ Moonlight.Triangulation: [affineFormConstant] :: AffineForm -> ExactRational
+ Moonlight.Triangulation: [affineFormXCoefficient] :: AffineForm -> ExactRational
+ Moonlight.Triangulation: [affineFormYCoefficient] :: AffineForm -> ExactRational
+ Moonlight.Triangulation: [powerDiagramActiveBoundaries] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [powerDiagramBoundaryCompatibilityChecks] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [powerDiagramCoincidentDominatedCells] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [powerDiagramCoincidentEquivalentCells] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [powerDiagramDomainVertices] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [powerDiagramEmptyCells] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [powerDiagramExactIntersections] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [powerDiagramFinalCoordinateBits] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [powerDiagramFinalDenominatorBits] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [powerDiagramLowerDimensionalCells] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [powerDiagramMaximumAffineCoefficientBits] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [powerDiagramMaximumCellConstraints] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [powerDiagramMaximumInputBits] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [powerDiagramOracleCells] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [powerDiagramPeakIntermediateCoordinateBits] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [powerDiagramPublishedCells] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [powerDiagramRegularEdges] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [powerDiagramRegularFaces] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [powerDiagramSubmittedSiteConstraints] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: [regularTriangulationCoincidentSites] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation: [regularTriangulationEdges] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation: [regularTriangulationFaces] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation: [regularTriangulationHiddenSites] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation: [regularTriangulationInputSites] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation: [regularTriangulationLowerDimensionalSites] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation: [regularTriangulationPeakHullFaces] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation: [regularTriangulationRepresentativeSites] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation: [regularTriangulationVisibleSites] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation: boundedPowerDiagram :: Ord label => ConvexPolygon -> NonEmpty (PowerSite label) -> Either (PowerDiagramError label) (BoundedPowerDiagram label, PowerDiagramReceipt)
+ Moonlight.Triangulation: coverGapRegion :: CoverGap -> PlanarRegion
+ Moonlight.Triangulation: data AffineForm
+ Moonlight.Triangulation: data BoundedPowerDiagram label
+ Moonlight.Triangulation: data CoverGap
+ Moonlight.Triangulation: data ExactRay
+ Moonlight.Triangulation: data LayerCoverageError label
+ Moonlight.Triangulation: data PowerCellDisposition label
+ Moonlight.Triangulation: data PowerDiagramError label
+ Moonlight.Triangulation: data PowerDiagramReceipt
+ Moonlight.Triangulation: data PowerDualEdge
+ Moonlight.Triangulation: data PowerSite label
+ Moonlight.Triangulation: data PowerWeight
+ Moonlight.Triangulation: data PowerWeightError
+ Moonlight.Triangulation: data RegularEdge label
+ Moonlight.Triangulation: data RegularFace label
+ Moonlight.Triangulation: data RegularSiteDisposition label
+ Moonlight.Triangulation: data RegularTopologyError label
+ Moonlight.Triangulation: data RegularTriangulation label
+ Moonlight.Triangulation: data RegularTriangulationReceipt
+ Moonlight.Triangulation: data ScopedDirectedEdge (scope :: k)
+ Moonlight.Triangulation: data ScopedFace (scope :: k)
+ Moonlight.Triangulation: data ScopedTriangulation (scope :: k) (mode :: ConstraintMode) vertex directed undirected face
+ Moonlight.Triangulation: data ScopedUndirectedEdge (scope :: k)
+ Moonlight.Triangulation: data ScopedVertex (scope :: k)
+ Moonlight.Triangulation: data UpperEnvelopeError label
+ Moonlight.Triangulation: exactRay :: ExactPoint -> ExactVector -> Either ExactGeometryError ExactRay
+ Moonlight.Triangulation: exactRayDirection :: ExactRay -> ExactVector
+ Moonlight.Triangulation: exactRayOrigin :: ExactRay -> ExactPoint
+ Moonlight.Triangulation: layerCovers :: Ord label => PlanarLayer label -> PolygonComponent -> Either (LayerCoverageError label) ()
+ Moonlight.Triangulation: overlayAll :: Ord label => NonEmpty (PlanarLayer label) -> Either (OverlayError (NonEmpty label) (NonEmpty label)) (PlanarLayer (NonEmpty label))
+ Moonlight.Triangulation: overlayConfusion :: (Ord leftLabel, Ord rightLabel) => OverlayResult leftLabel rightLabel -> Map (leftLabel, rightLabel) ExactArea
+ Moonlight.Triangulation: overlayMass :: ((leftLabel, rightLabel) -> Bool) -> OverlayResult leftLabel rightLabel -> Either RegionPublicationError ExactArea
+ Moonlight.Triangulation: polygonComponentArea :: PolygonComponent -> ExactArea
+ Moonlight.Triangulation: powerCellDisposition :: Ord label => label -> BoundedPowerDiagram label -> Maybe (PowerCellDisposition label)
+ Moonlight.Triangulation: powerCellDispositions :: BoundedPowerDiagram label -> [(label, PowerCellDisposition label)]
+ Moonlight.Triangulation: powerDiagramFinalCoordinateBitGrowth :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: powerDiagramInputSites :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: powerDiagramPeakIntermediateBitGrowth :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation: powerDiagramPlanarLayer :: Ord label => label -> BoundedPowerDiagram label -> Either (PowerDiagramError label) (PlanarLayer label)
+ Moonlight.Triangulation: powerSite :: label -> Point -> PowerWeight -> Either (PowerDiagramError label) (PowerSite label)
+ Moonlight.Triangulation: powerSiteLabel :: PowerSite label -> label
+ Moonlight.Triangulation: powerSitePosition :: PowerSite label -> Point
+ Moonlight.Triangulation: powerSiteWeight :: PowerSite label -> PowerWeight
+ Moonlight.Triangulation: powerWeight :: Double -> Either PowerWeightError PowerWeight
+ Moonlight.Triangulation: powerWeightExact :: PowerWeight -> ExactRational
+ Moonlight.Triangulation: reflectConvexPolygon :: ConvexPolygon -> ConvexPolygon
+ Moonlight.Triangulation: regularEdgeDual :: RegularEdge label -> PowerDualEdge
+ Moonlight.Triangulation: regularEdgeLabels :: RegularEdge label -> (label, label)
+ Moonlight.Triangulation: regularEdges :: RegularTriangulation label -> [RegularEdge label]
+ Moonlight.Triangulation: regularFaceDualPoint :: RegularFace label -> ExactPoint
+ Moonlight.Triangulation: regularFaceLabels :: RegularFace label -> (label, label, label)
+ Moonlight.Triangulation: regularFaces :: RegularTriangulation label -> [RegularFace label]
+ Moonlight.Triangulation: regularNeighbours :: Ord label => label -> RegularTriangulation label -> Set label
+ Moonlight.Triangulation: regularSiteDisposition :: Ord label => label -> RegularTriangulation label -> Maybe (RegularSiteDisposition label)
+ Moonlight.Triangulation: regularTriangulation :: Ord label => NonEmpty (PowerSite label) -> Either (PowerDiagramError label) (RegularTriangulation label, RegularTriangulationReceipt)
+ Moonlight.Triangulation: scopedAdjacentEdge :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedFace scope -> Maybe (ScopedDirectedEdge scope)
+ Moonlight.Triangulation: scopedAsUndirected :: forall {k} (scope :: k). ScopedDirectedEdge scope -> ScopedUndirectedEdge scope
+ Moonlight.Triangulation: scopedDestination :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedDirectedEdge scope -> ScopedVertex scope
+ Moonlight.Triangulation: scopedDirectedEdgeData :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedDirectedEdge scope -> directed
+ Moonlight.Triangulation: scopedDirectedEdges :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> [ScopedDirectedEdge scope]
+ Moonlight.Triangulation: scopedFaceData :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedFace scope -> face
+ Moonlight.Triangulation: scopedFaceDirectedEdges :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedFace scope -> [ScopedDirectedEdge scope]
+ Moonlight.Triangulation: scopedFaceVertices :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedFace scope -> [ScopedVertex scope]
+ Moonlight.Triangulation: scopedFaces :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> [ScopedFace scope]
+ Moonlight.Triangulation: scopedIncidentFace :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedDirectedEdge scope -> ScopedFace scope
+ Moonlight.Triangulation: scopedInnerFaces :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> [ScopedFace scope]
+ Moonlight.Triangulation: scopedIsBoundaryEdge :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedUndirectedEdge scope -> Bool
+ Moonlight.Triangulation: scopedIsConstraintEdge :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedUndirectedEdge scope -> Bool
+ Moonlight.Triangulation: scopedIsOuterDirectedEdge :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedDirectedEdge scope -> Bool
+ Moonlight.Triangulation: scopedNext :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedDirectedEdge scope -> ScopedDirectedEdge scope
+ Moonlight.Triangulation: scopedOrigin :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedDirectedEdge scope -> ScopedVertex scope
+ Moonlight.Triangulation: scopedOuterFace :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedFace scope
+ Moonlight.Triangulation: scopedPrevious :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedDirectedEdge scope -> ScopedDirectedEdge scope
+ Moonlight.Triangulation: scopedReverseEdge :: forall {k} (scope :: k). ScopedDirectedEdge scope -> ScopedDirectedEdge scope
+ Moonlight.Triangulation: scopedUndirectedEdgeData :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedUndirectedEdge scope -> undirected
+ Moonlight.Triangulation: scopedUndirectedEdges :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> [ScopedUndirectedEdge scope]
+ Moonlight.Triangulation: scopedUndirectedEndpoints :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedUndirectedEdge scope -> (ScopedVertex scope, ScopedVertex scope)
+ Moonlight.Triangulation: scopedVertexData :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedVertex scope -> vertex
+ Moonlight.Triangulation: scopedVertexOutEdge :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedVertex scope -> Maybe (ScopedDirectedEdge scope)
+ Moonlight.Triangulation: scopedVertexOutgoingEdges :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedVertex scope -> [ScopedDirectedEdge scope]
+ Moonlight.Triangulation: scopedVertexPoint :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> ScopedVertex scope -> Point
+ Moonlight.Triangulation: scopedVertices :: forall {k} (scope :: k) (mode :: ConstraintMode) vertex directed undirected face. ScopedTriangulation scope mode vertex directed undirected face -> [ScopedVertex scope]
+ Moonlight.Triangulation: upperEnvelope :: Ord label => PolygonComponent -> Map label AffineForm -> Either (UpperEnvelopeError label) (PlanarLayer (Maybe label))
+ Moonlight.Triangulation: withScopedTriangulation :: forall {k} (mode :: ConstraintMode) vertex directed undirected face result. Triangulation mode vertex directed undirected face -> (forall (scope :: k). () => ScopedTriangulation scope mode vertex directed undirected face -> result) -> result
+ Moonlight.Triangulation.Internal.Minkowski.Convex: convexPolygonComponent :: ConvexPolygon -> PolygonComponent
+ Moonlight.Triangulation.Internal.Minkowski.Convex: convexPolygonFromRetained :: ExactRetainedPolygon -> ConvexPolygon
+ Moonlight.Triangulation.Internal.Minkowski.Types: MinkowskiClipFailed :: ExactClipError -> MinkowskiError
+ Moonlight.Triangulation.Internal.Minkowski.Types: MinkowskiInvalidHalfPlane :: ExactHalfPlaneError -> MinkowskiError
+ Moonlight.Triangulation.Internal.RegularTriangulation: DistinctSlopeGenerators :: NonEmpty (ExactPowerGenerator label) -> DistinctSlopeGenerators label
+ Moonlight.Triangulation.Internal.RegularTriangulation: ExactPowerGenerator :: label -> ExactRational -> ExactRational -> ExactRational -> ExactPowerGenerator label
+ Moonlight.Triangulation.Internal.RegularTriangulation: GeneratorDualCollapsed :: ExactPoint -> GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.RegularTriangulation: GeneratorDualLine :: ExactAffineLine -> GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.RegularTriangulation: GeneratorDualRay :: ExactRay -> GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.RegularTriangulation: GeneratorDualSegment :: ExactSegment -> GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.RegularTriangulation: GeneratorRegularEdge :: ExactPowerGenerator label -> ExactPowerGenerator label -> GeneratorDualGeometry -> GeneratorRegularEdge label
+ Moonlight.Triangulation.Internal.RegularTriangulation: GeneratorRegularFace :: label -> label -> label -> ExactPoint -> GeneratorRegularFace label
+ Moonlight.Triangulation.Internal.RegularTriangulation: GeneratorRegularReceipt :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> GeneratorRegularReceipt
+ Moonlight.Triangulation.Internal.RegularTriangulation: GeneratorRegularTopology :: NonEmpty (ExactPowerGenerator label, RegularGeneratorDisposition) -> [GeneratorRegularFace label] -> [GeneratorRegularEdge label] -> GeneratorRegularReceipt -> GeneratorRegularTopology label
+ Moonlight.Triangulation.Internal.RegularTriangulation: RegularGeneratorHidden :: RegularGeneratorDisposition
+ Moonlight.Triangulation.Internal.RegularTriangulation: RegularGeneratorLowerDimensional :: RegularGeneratorDisposition
+ Moonlight.Triangulation.Internal.RegularTriangulation: RegularGeneratorVisible :: RegularGeneratorDisposition
+ Moonlight.Triangulation.Internal.RegularTriangulation: RegularTopologyAxisInvalid :: label -> label -> ExactHalfPlaneError -> RegularTopologyError label
+ Moonlight.Triangulation.Internal.RegularTriangulation: RegularTopologyDualRayInvalid :: label -> label -> ExactGeometryError -> RegularTopologyError label
+ Moonlight.Triangulation.Internal.RegularTriangulation: RegularTopologyDualSegmentInvalid :: label -> label -> ExactGeometryError -> RegularTopologyError label
+ Moonlight.Triangulation.Internal.RegularTriangulation: RegularTopologyDualVertexInvalid :: label -> label -> label -> ExactIntersectionError -> RegularTopologyError label
+ Moonlight.Triangulation.Internal.RegularTriangulation: RegularTopologyFlatBoundary :: label -> label -> label -> RegularTopologyError label
+ Moonlight.Triangulation.Internal.RegularTriangulation: RegularTopologyNonManifoldEdge :: label -> label -> Int -> RegularTopologyError label
+ Moonlight.Triangulation.Internal.RegularTriangulation: RegularTopologySlopeHullInvalid :: MinkowskiError -> RegularTopologyError label
+ Moonlight.Triangulation.Internal.RegularTriangulation: RegularTopologySlopeVertexMissing :: ExactPoint -> RegularTopologyError label
+ Moonlight.Triangulation.Internal.RegularTriangulation: [exactPowerGeneratorConstant] :: ExactPowerGenerator label -> ExactRational
+ Moonlight.Triangulation.Internal.RegularTriangulation: [exactPowerGeneratorLabel] :: ExactPowerGenerator label -> label
+ Moonlight.Triangulation.Internal.RegularTriangulation: [exactPowerGeneratorXCoefficient] :: ExactPowerGenerator label -> ExactRational
+ Moonlight.Triangulation.Internal.RegularTriangulation: [exactPowerGeneratorYCoefficient] :: ExactPowerGenerator label -> ExactRational
+ Moonlight.Triangulation.Internal.RegularTriangulation: [generatorRegularDispositions] :: GeneratorRegularTopology label -> NonEmpty (ExactPowerGenerator label, RegularGeneratorDisposition)
+ Moonlight.Triangulation.Internal.RegularTriangulation: [generatorRegularEdgeCount] :: GeneratorRegularReceipt -> Int
+ Moonlight.Triangulation.Internal.RegularTriangulation: [generatorRegularEdgeDual] :: GeneratorRegularEdge label -> GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.RegularTriangulation: [generatorRegularEdgeFirst] :: GeneratorRegularEdge label -> ExactPowerGenerator label
+ Moonlight.Triangulation.Internal.RegularTriangulation: [generatorRegularEdgeSecond] :: GeneratorRegularEdge label -> ExactPowerGenerator label
+ Moonlight.Triangulation.Internal.RegularTriangulation: [generatorRegularEdges] :: GeneratorRegularTopology label -> [GeneratorRegularEdge label]
+ Moonlight.Triangulation.Internal.RegularTriangulation: [generatorRegularFaceCount] :: GeneratorRegularReceipt -> Int
+ Moonlight.Triangulation.Internal.RegularTriangulation: [generatorRegularFaceDualPoint] :: GeneratorRegularFace label -> ExactPoint
+ Moonlight.Triangulation.Internal.RegularTriangulation: [generatorRegularFaceFirst] :: GeneratorRegularFace label -> label
+ Moonlight.Triangulation.Internal.RegularTriangulation: [generatorRegularFaceSecond] :: GeneratorRegularFace label -> label
+ Moonlight.Triangulation.Internal.RegularTriangulation: [generatorRegularFaceThird] :: GeneratorRegularFace label -> label
+ Moonlight.Triangulation.Internal.RegularTriangulation: [generatorRegularFaces] :: GeneratorRegularTopology label -> [GeneratorRegularFace label]
+ Moonlight.Triangulation.Internal.RegularTriangulation: [generatorRegularHiddenSites] :: GeneratorRegularReceipt -> Int
+ Moonlight.Triangulation.Internal.RegularTriangulation: [generatorRegularInputSites] :: GeneratorRegularReceipt -> Int
+ Moonlight.Triangulation.Internal.RegularTriangulation: [generatorRegularLowerDimensionalSites] :: GeneratorRegularReceipt -> Int
+ Moonlight.Triangulation.Internal.RegularTriangulation: [generatorRegularPeakHullFaces] :: GeneratorRegularReceipt -> Int
+ Moonlight.Triangulation.Internal.RegularTriangulation: [generatorRegularReceipt] :: GeneratorRegularTopology label -> GeneratorRegularReceipt
+ Moonlight.Triangulation.Internal.RegularTriangulation: [generatorRegularVisibleSites] :: GeneratorRegularReceipt -> Int
+ Moonlight.Triangulation.Internal.RegularTriangulation: data ExactPowerGenerator label
+ Moonlight.Triangulation.Internal.RegularTriangulation: data GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.RegularTriangulation: data GeneratorRegularEdge label
+ Moonlight.Triangulation.Internal.RegularTriangulation: data GeneratorRegularFace label
+ Moonlight.Triangulation.Internal.RegularTriangulation: data GeneratorRegularReceipt
+ Moonlight.Triangulation.Internal.RegularTriangulation: data GeneratorRegularTopology label
+ Moonlight.Triangulation.Internal.RegularTriangulation: data RegularGeneratorDisposition
+ Moonlight.Triangulation.Internal.RegularTriangulation: data RegularTopologyError label
+ Moonlight.Triangulation.Internal.RegularTriangulation: exactGeneratorAxis :: ExactPowerGenerator label -> ExactPowerGenerator label -> Either ExactHalfPlaneError ExactAffineLine
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularReceipt
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.RegularTriangulation.RegularGeneratorDisposition
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.RegularTriangulation.ExactPowerGenerator label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularEdge label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularFace label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularTopology label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.RegularTriangulation.RegularTopologyError label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.RegularTriangulation.LiftedPoint label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularReceipt
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.RegularTriangulation.RegularGeneratorDisposition
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.RegularTriangulation.ExactPowerGenerator label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularEdge label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularFace label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularTopology label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.RegularTriangulation.RegularTopologyError label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.RegularTriangulation.LiftedPoint label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularReceipt
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.RegularTriangulation.RegularGeneratorDisposition
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.RegularTriangulation.ExactPowerGenerator label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularEdge label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularFace label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.RegularTriangulation.ExactPowerGenerator label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularEdge label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularFace label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularTopology label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.RegularTriangulation.RegularTopologyError label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularReceipt
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.RegularTriangulation.RegularGeneratorDisposition
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularReceipt
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.RegularTriangulation.RegularGeneratorDisposition
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.RegularTriangulation.ExactPowerGenerator label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularEdge label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularFace label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.RegularTriangulation.GeneratorRegularTopology label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.RegularTriangulation.RegularTopologyError label)
+ Moonlight.Triangulation.Internal.RegularTriangulation: newtype DistinctSlopeGenerators label
+ Moonlight.Triangulation.Internal.RegularTriangulation: regularGeneratorTopology :: Ord label => DistinctSlopeGenerators label -> Either (RegularTopologyError label) (GeneratorRegularTopology label)
+ Moonlight.Triangulation.Minkowski: MinkowskiClipFailed :: ExactClipError -> MinkowskiError
+ Moonlight.Triangulation.Minkowski: MinkowskiInvalidHalfPlane :: ExactHalfPlaneError -> MinkowskiError
+ Moonlight.Triangulation.Minkowski: reflectConvexPolygon :: ConvexPolygon -> ConvexPolygon
+ Moonlight.Triangulation.Overlay: LayerCoverageGap :: CoverGap -> LayerCoverageError label
+ Moonlight.Triangulation.Overlay: LayerCoverageGapPublicationFailed :: RegionPublicationError -> LayerCoverageError label
+ Moonlight.Triangulation.Overlay: LayerCoverageOverlayFailed :: OverlayError Bool label -> LayerCoverageError label
+ Moonlight.Triangulation.Overlay: coverGapRegion :: CoverGap -> PlanarRegion
+ Moonlight.Triangulation.Overlay: data CoverGap
+ Moonlight.Triangulation.Overlay: data LayerCoverageError label
+ Moonlight.Triangulation.Overlay: instance Control.DeepSeq.NFData Moonlight.Triangulation.Overlay.CoverGap
+ Moonlight.Triangulation.Overlay: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Overlay.LayerCoverageError label)
+ Moonlight.Triangulation.Overlay: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Overlay.CoverGap
+ Moonlight.Triangulation.Overlay: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Overlay.LayerCoverageError label)
+ Moonlight.Triangulation.Overlay: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Overlay.CoverGap
+ Moonlight.Triangulation.Overlay: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Overlay.LayerCoverageError label)
+ Moonlight.Triangulation.Overlay: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Overlay.CoverGap
+ Moonlight.Triangulation.Overlay: instance GHC.Internal.Show.Show Moonlight.Triangulation.Overlay.CoverGap
+ Moonlight.Triangulation.Overlay: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Overlay.LayerCoverageError label)
+ Moonlight.Triangulation.Overlay: layerCovers :: Ord label => PlanarLayer label -> PolygonComponent -> Either (LayerCoverageError label) ()
+ Moonlight.Triangulation.Overlay: overlayAll :: Ord label => NonEmpty (PlanarLayer label) -> Either (OverlayError (NonEmpty label) (NonEmpty label)) (PlanarLayer (NonEmpty label))
+ Moonlight.Triangulation.Overlay: overlayConfusion :: (Ord leftLabel, Ord rightLabel) => OverlayResult leftLabel rightLabel -> Map (leftLabel, rightLabel) ExactArea
+ Moonlight.Triangulation.Overlay: overlayMass :: ((leftLabel, rightLabel) -> Bool) -> OverlayResult leftLabel rightLabel -> Either RegionPublicationError ExactArea
+ Moonlight.Triangulation.PowerDiagram: AffineForm :: ExactRational -> ExactRational -> ExactRational -> AffineForm
+ Moonlight.Triangulation.PowerDiagram: BoundedPowerDual :: ExactSegment -> PowerDualEdge
+ Moonlight.Triangulation.PowerDiagram: CoincidentDominatedBy :: label -> PowerCellDisposition label
+ Moonlight.Triangulation.PowerDiagram: CoincidentEquivalentTo :: label -> PowerCellDisposition label
+ Moonlight.Triangulation.PowerDiagram: CollapsedPowerDual :: ExactPoint -> PowerDualEdge
+ Moonlight.Triangulation.PowerDiagram: DuplicatePowerSiteLabel :: label -> PowerDiagramError label
+ Moonlight.Triangulation.PowerDiagram: EmptyPowerCell :: PowerCellDisposition label
+ Moonlight.Triangulation.PowerDiagram: FullLinePowerDual :: ExactAffineLine -> PowerDualEdge
+ Moonlight.Triangulation.PowerDiagram: LowerDimensionalPowerCell :: NonEmpty ExactPoint -> PowerCellDisposition label
+ Moonlight.Triangulation.PowerDiagram: PowerBisectorInvalid :: label -> label -> ExactHalfPlaneError -> PowerDiagramError label
+ Moonlight.Triangulation.PowerDiagram: PowerCellClipFailed :: label -> ExactClipError -> PowerDiagramError label
+ Moonlight.Triangulation.PowerDiagram: PowerDiagramOutsideLabelCollides :: label -> PowerDiagramError label
+ Moonlight.Triangulation.PowerDiagram: PowerDiagramReceipt :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> PowerDiagramReceipt
+ Moonlight.Triangulation.PowerDiagram: PowerDomainInvalid :: ExactHalfPlaneError -> PowerDiagramError label
+ Moonlight.Triangulation.PowerDiagram: PowerRegularTopologyFailed :: RegularTopologyError label -> PowerDiagramError label
+ Moonlight.Triangulation.PowerDiagram: PowerSitePositionInvalid :: label -> PointValidationError -> PowerDiagramError label
+ Moonlight.Triangulation.PowerDiagram: PowerWeightNonFinite :: NonFiniteValue -> PowerWeightError
+ Moonlight.Triangulation.PowerDiagram: PublishedPowerCell :: ConvexPolygon -> PowerCellDisposition label
+ Moonlight.Triangulation.PowerDiagram: RegularSiteCoincidentDominatedBy :: label -> RegularSiteDisposition label
+ Moonlight.Triangulation.PowerDiagram: RegularSiteCoincidentEquivalentTo :: label -> RegularSiteDisposition label
+ Moonlight.Triangulation.PowerDiagram: RegularSiteHidden :: RegularSiteDisposition label
+ Moonlight.Triangulation.PowerDiagram: RegularSiteLowerDimensional :: RegularSiteDisposition label
+ Moonlight.Triangulation.PowerDiagram: RegularSiteVisible :: RegularSiteDisposition label
+ Moonlight.Triangulation.PowerDiagram: RegularTopologyAxisInvalid :: label -> label -> ExactHalfPlaneError -> RegularTopologyError label
+ Moonlight.Triangulation.PowerDiagram: RegularTopologyDualRayInvalid :: label -> label -> ExactGeometryError -> RegularTopologyError label
+ Moonlight.Triangulation.PowerDiagram: RegularTopologyDualSegmentInvalid :: label -> label -> ExactGeometryError -> RegularTopologyError label
+ Moonlight.Triangulation.PowerDiagram: RegularTopologyDualVertexInvalid :: label -> label -> label -> ExactIntersectionError -> RegularTopologyError label
+ Moonlight.Triangulation.PowerDiagram: RegularTopologyFlatBoundary :: label -> label -> label -> RegularTopologyError label
+ Moonlight.Triangulation.PowerDiagram: RegularTopologyNonManifoldEdge :: label -> label -> Int -> RegularTopologyError label
+ Moonlight.Triangulation.PowerDiagram: RegularTopologySlopeHullInvalid :: MinkowskiError -> RegularTopologyError label
+ Moonlight.Triangulation.PowerDiagram: RegularTopologySlopeVertexMissing :: ExactPoint -> RegularTopologyError label
+ Moonlight.Triangulation.PowerDiagram: RegularTriangulationReceipt :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> RegularTriangulationReceipt
+ Moonlight.Triangulation.PowerDiagram: UnboundedPowerDual :: ExactRay -> PowerDualEdge
+ Moonlight.Triangulation.PowerDiagram: UpperEnvelopeEmptyForms :: UpperEnvelopeError label
+ Moonlight.Triangulation.PowerDiagram: UpperEnvelopePowerConstructionFailed :: PowerDiagramError label -> UpperEnvelopeError label
+ Moonlight.Triangulation.PowerDiagram: UpperEnvelopeWindowHullFailed :: MinkowskiError -> UpperEnvelopeError label
+ Moonlight.Triangulation.PowerDiagram: UpperEnvelopeWindowOverlayFailed :: OverlayError (Maybe label) Bool -> UpperEnvelopeError label
+ Moonlight.Triangulation.PowerDiagram: [affineFormConstant] :: AffineForm -> ExactRational
+ Moonlight.Triangulation.PowerDiagram: [affineFormXCoefficient] :: AffineForm -> ExactRational
+ Moonlight.Triangulation.PowerDiagram: [affineFormYCoefficient] :: AffineForm -> ExactRational
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramActiveBoundaries] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramBoundaryCompatibilityChecks] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramCoincidentDominatedCells] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramCoincidentEquivalentCells] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramDomainVertices] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramEmptyCells] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramExactIntersections] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramFinalCoordinateBits] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramFinalDenominatorBits] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramLowerDimensionalCells] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramMaximumAffineCoefficientBits] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramMaximumCellConstraints] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramMaximumInputBits] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramOracleCells] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramPeakIntermediateCoordinateBits] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramPublishedCells] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramRegularEdges] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramRegularFaces] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [powerDiagramSubmittedSiteConstraints] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [regularTriangulationCoincidentSites] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [regularTriangulationEdges] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [regularTriangulationFaces] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [regularTriangulationHiddenSites] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [regularTriangulationInputSites] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [regularTriangulationLowerDimensionalSites] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [regularTriangulationPeakHullFaces] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [regularTriangulationRepresentativeSites] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: [regularTriangulationVisibleSites] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: boundedPowerDiagram :: Ord label => ConvexPolygon -> NonEmpty (PowerSite label) -> Either (PowerDiagramError label) (BoundedPowerDiagram label, PowerDiagramReceipt)
+ Moonlight.Triangulation.PowerDiagram: data AffineForm
+ Moonlight.Triangulation.PowerDiagram: data BoundedPowerDiagram label
+ Moonlight.Triangulation.PowerDiagram: data PowerCellDisposition label
+ Moonlight.Triangulation.PowerDiagram: data PowerDiagramError label
+ Moonlight.Triangulation.PowerDiagram: data PowerDiagramReceipt
+ Moonlight.Triangulation.PowerDiagram: data PowerDualEdge
+ Moonlight.Triangulation.PowerDiagram: data PowerSite label
+ Moonlight.Triangulation.PowerDiagram: data PowerWeight
+ Moonlight.Triangulation.PowerDiagram: data PowerWeightError
+ Moonlight.Triangulation.PowerDiagram: data RegularEdge label
+ Moonlight.Triangulation.PowerDiagram: data RegularFace label
+ Moonlight.Triangulation.PowerDiagram: data RegularSiteDisposition label
+ Moonlight.Triangulation.PowerDiagram: data RegularTopologyError label
+ Moonlight.Triangulation.PowerDiagram: data RegularTriangulation label
+ Moonlight.Triangulation.PowerDiagram: data RegularTriangulationReceipt
+ Moonlight.Triangulation.PowerDiagram: data UpperEnvelopeError label
+ Moonlight.Triangulation.PowerDiagram: instance Control.DeepSeq.NFData Moonlight.Triangulation.PowerDiagram.AffineForm
+ Moonlight.Triangulation.PowerDiagram: instance Control.DeepSeq.NFData Moonlight.Triangulation.PowerDiagram.PowerDiagramReceipt
+ Moonlight.Triangulation.PowerDiagram: instance Control.DeepSeq.NFData Moonlight.Triangulation.PowerDiagram.PowerDualEdge
+ Moonlight.Triangulation.PowerDiagram: instance Control.DeepSeq.NFData Moonlight.Triangulation.PowerDiagram.PowerWeight
+ Moonlight.Triangulation.PowerDiagram: instance Control.DeepSeq.NFData Moonlight.Triangulation.PowerDiagram.PowerWeightError
+ Moonlight.Triangulation.PowerDiagram: instance Control.DeepSeq.NFData Moonlight.Triangulation.PowerDiagram.RegularTriangulationReceipt
+ Moonlight.Triangulation.PowerDiagram: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.PowerDiagram.BoundedPowerDiagram label)
+ Moonlight.Triangulation.PowerDiagram: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.PowerDiagram.PowerCellDisposition label)
+ Moonlight.Triangulation.PowerDiagram: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.PowerDiagram.PowerDiagramError label)
+ Moonlight.Triangulation.PowerDiagram: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.PowerDiagram.PowerSite label)
+ Moonlight.Triangulation.PowerDiagram: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.PowerDiagram.RegularEdge label)
+ Moonlight.Triangulation.PowerDiagram: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.PowerDiagram.RegularFace label)
+ Moonlight.Triangulation.PowerDiagram: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.PowerDiagram.RegularSiteDisposition label)
+ Moonlight.Triangulation.PowerDiagram: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.PowerDiagram.RegularTriangulation label)
+ Moonlight.Triangulation.PowerDiagram: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.PowerDiagram.UpperEnvelopeError label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.PowerDiagram.AffineForm
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.PowerDiagram.PowerDiagramReceipt
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.PowerDiagram.PowerDualEdge
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.PowerDiagram.PowerWeight
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.PowerDiagram.PowerWeightError
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.PowerDiagram.RegularTriangulationReceipt
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.PowerDiagram.BoundedPowerDiagram label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.PowerDiagram.PowerCellDisposition label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.PowerDiagram.PowerDiagramError label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.PowerDiagram.PowerSite label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.PowerDiagram.RegularEdge label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.PowerDiagram.RegularFace label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.PowerDiagram.RegularSiteDisposition label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.PowerDiagram.RegularTriangulation label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.PowerDiagram.UpperEnvelopeError label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.PowerDiagram.AffineForm
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.PowerDiagram.PowerDiagramReceipt
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.PowerDiagram.PowerDualEdge
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.PowerDiagram.PowerWeight
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.PowerDiagram.PowerWeightError
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.PowerDiagram.RegularTriangulationReceipt
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.PowerDiagram.PowerCellDisposition label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.PowerDiagram.PowerSite label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.PowerDiagram.RegularEdge label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.PowerDiagram.RegularFace label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.PowerDiagram.RegularSiteDisposition label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.PowerDiagram.BoundedPowerDiagram label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.PowerDiagram.PowerCellDisposition label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.PowerDiagram.PowerDiagramError label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.PowerDiagram.PowerSite label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.PowerDiagram.RegularEdge label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.PowerDiagram.RegularFace label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.PowerDiagram.RegularSiteDisposition label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.PowerDiagram.RegularTriangulation label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.PowerDiagram.UpperEnvelopeError label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.PowerDiagram.AffineForm
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.PowerDiagram.PowerDiagramReceipt
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.PowerDiagram.PowerDualEdge
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.PowerDiagram.PowerWeight
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.PowerDiagram.PowerWeightError
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.PowerDiagram.RegularTriangulationReceipt
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Show.Show Moonlight.Triangulation.PowerDiagram.AffineForm
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Show.Show Moonlight.Triangulation.PowerDiagram.PowerDiagramReceipt
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Show.Show Moonlight.Triangulation.PowerDiagram.PowerDualEdge
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Show.Show Moonlight.Triangulation.PowerDiagram.PowerWeight
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Show.Show Moonlight.Triangulation.PowerDiagram.PowerWeightError
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Show.Show Moonlight.Triangulation.PowerDiagram.RegularTriangulationReceipt
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.PowerDiagram.BoundedPowerDiagram label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.PowerDiagram.PowerCellDisposition label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.PowerDiagram.PowerDiagramError label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.PowerDiagram.PowerSite label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.PowerDiagram.RegularEdge label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.PowerDiagram.RegularFace label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.PowerDiagram.RegularSiteDisposition label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.PowerDiagram.RegularTriangulation label)
+ Moonlight.Triangulation.PowerDiagram: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.PowerDiagram.UpperEnvelopeError label)
+ Moonlight.Triangulation.PowerDiagram: powerCellDisposition :: Ord label => label -> BoundedPowerDiagram label -> Maybe (PowerCellDisposition label)
+ Moonlight.Triangulation.PowerDiagram: powerCellDispositions :: BoundedPowerDiagram label -> [(label, PowerCellDisposition label)]
+ Moonlight.Triangulation.PowerDiagram: powerDiagramFinalCoordinateBitGrowth :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: powerDiagramInputSites :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: powerDiagramPeakIntermediateBitGrowth :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.PowerDiagram: powerDiagramPlanarLayer :: Ord label => label -> BoundedPowerDiagram label -> Either (PowerDiagramError label) (PlanarLayer label)
+ Moonlight.Triangulation.PowerDiagram: powerSite :: label -> Point -> PowerWeight -> Either (PowerDiagramError label) (PowerSite label)
+ Moonlight.Triangulation.PowerDiagram: powerSiteLabel :: PowerSite label -> label
+ Moonlight.Triangulation.PowerDiagram: powerSitePosition :: PowerSite label -> Point
+ Moonlight.Triangulation.PowerDiagram: powerSiteWeight :: PowerSite label -> PowerWeight
+ Moonlight.Triangulation.PowerDiagram: powerWeight :: Double -> Either PowerWeightError PowerWeight
+ Moonlight.Triangulation.PowerDiagram: powerWeightExact :: PowerWeight -> ExactRational
+ Moonlight.Triangulation.PowerDiagram: regularEdgeDual :: RegularEdge label -> PowerDualEdge
+ Moonlight.Triangulation.PowerDiagram: regularEdgeLabels :: RegularEdge label -> (label, label)
+ Moonlight.Triangulation.PowerDiagram: regularEdges :: RegularTriangulation label -> [RegularEdge label]
+ Moonlight.Triangulation.PowerDiagram: regularFaceDualPoint :: RegularFace label -> ExactPoint
+ Moonlight.Triangulation.PowerDiagram: regularFaceLabels :: RegularFace label -> (label, label, label)
+ Moonlight.Triangulation.PowerDiagram: regularFaces :: RegularTriangulation label -> [RegularFace label]
+ Moonlight.Triangulation.PowerDiagram: regularNeighbours :: Ord label => label -> RegularTriangulation label -> Set label
+ Moonlight.Triangulation.PowerDiagram: regularSiteDisposition :: Ord label => label -> RegularTriangulation label -> Maybe (RegularSiteDisposition label)
+ Moonlight.Triangulation.PowerDiagram: regularTriangulation :: Ord label => NonEmpty (PowerSite label) -> Either (PowerDiagramError label) (RegularTriangulation label, RegularTriangulationReceipt)
+ Moonlight.Triangulation.PowerDiagram: upperEnvelope :: Ord label => PolygonComponent -> Map label AffineForm -> Either (UpperEnvelopeError label) (PlanarLayer (Maybe label))
- Moonlight.Triangulation.Internal.Minkowski.Convex: erodeConvexBy :: ConvexPolygon -> ConvexPolygon -> Either MinkowskiError (Maybe ConvexPolygon)
+ Moonlight.Triangulation.Internal.Minkowski.Convex: erodeConvexBy :: ConvexPolygon -> ConvexPolygon -> Either MinkowskiError (Maybe ConvexPolygon, ExactClipReceipt)
Files
- CHANGELOG.md +61/−0
- README.md +37/−8
- bench/layer/Main.hs +8/−0
- bench/layer/Moonlight/Triangulation/LayerBench.hs +244/−0
- bench/power/Main.hs +9/−0
- bench/power/Moonlight/Triangulation/PowerBench.hs +289/−0
- docs/README.md +1/−0
- moonlight-triangulation.cabal +115/−11
- src-build/Moonlight/Triangulation/Internal/Join.hs +7/−20
- src-build/Moonlight/Triangulation/Internal/Join/Plan.hs +8/−29
- src-core/Moonlight/Triangulation/Internal/ExactRational.hs +23/−0
- src-dcel/Moonlight/Triangulation/Dcel.hs +10/−5
- src-dcel/Moonlight/Triangulation/Exact.hs +985/−1
- src-dcel/Moonlight/Triangulation/Handles/HandleDefs.hs +4/−5
- src-dcel/Moonlight/Triangulation/Handles/Scoped.hs +276/−0
- src-dcel/Moonlight/Triangulation/Internal/BoundaryCycle.hs +48/−0
- src-dcel/Moonlight/Triangulation/Internal/Tournament.hs +45/−0
- src-dcel/Moonlight/Triangulation/Region.hs +9/−6
- src-dcel/Moonlight/Triangulation/Valuation.hs +14/−1
- src-overlay/Moonlight/Triangulation/Embedding.hs +546/−0
- src-overlay/Moonlight/Triangulation/Internal/Overlay/Arrangement.hs +812/−0
- src-overlay/Moonlight/Triangulation/Internal/Overlay/Resident.hs +923/−0
- src-overlay/Moonlight/Triangulation/Internal/Overlay/Types.hs +274/−0
- src-parallel/Moonlight/Triangulation/Parallel.hs +3/−2
- src-public/Moonlight/Triangulation.hs +182/−5
- src-public/Moonlight/Triangulation/Internal/Minkowski/Convex.hs +64/−86
- src-public/Moonlight/Triangulation/Internal/Minkowski/Types.hs +4/−4
- src-public/Moonlight/Triangulation/Internal/Overlay/Arrangement.hs +0/−811
- src-public/Moonlight/Triangulation/Internal/Overlay/Embedding.hs +0/−539
- src-public/Moonlight/Triangulation/Internal/Overlay/Resident.hs +0/−923
- src-public/Moonlight/Triangulation/Internal/Overlay/Types.hs +0/−274
- src-public/Moonlight/Triangulation/Internal/RegularTriangulation.hs +1111/−0
- src-public/Moonlight/Triangulation/Minkowski.hs +10/−13
- src-public/Moonlight/Triangulation/Overlay.hs +189/−18
- src-public/Moonlight/Triangulation/PowerDiagram.hs +955/−0
- test/algebra/Main.hs +0/−2
- test/algebra/Moonlight/Triangulation/ExactClipRetentionSpec.hs +536/−0
- test/algebra/Moonlight/Triangulation/LayerOperationsSpec.hs +275/−0
- test/algebra/Moonlight/Triangulation/MinkowskiSpec.hs +25/−0
- test/algebra/Moonlight/Triangulation/PowerDiagramSpec.hs +734/−0
- test/coherence/Main.hs +4/−1
- test/exact-planar/Main.hs +15/−0
- test/native/Moonlight/Triangulation/ExactEmbeddingSpec.hs +7/−2
- test/public-components/Main.hs +10/−0
CHANGELOG.md view
@@ -10,6 +10,67 @@ The serialization format carries its own version tag, independent of the package version; any change to it is recorded here explicitly. +## 1.4.0.4 - 2026-08-28++* Add exact affine upper envelopes through the existing power-cell owner, with+ a no-overlay convex fast path and exact restriction to nonconvex or holed+ polygonal windows.+* Add balanced n-ary layer refinement, exact window-coverage certification+ carrying the uncovered region, and direct selected/confusion area folds.+ On the 16-layer benchmark fixture, balanced refinement is 13x faster and+ allocates 13.5x less than left association; direct mass and confusion are+ 14.6x and 16.5x faster than materializing polygonal regions.+* Add exact regular triangulations over the lifted affine generators, including+ per-label visible/lower-dimensional/hidden dispositions and weighted dual+ segments, rays, full lines, and collapsed degenerate edges. Coplanar hull+ triangles glue before extreme-site extraction, so implementation diagonals+ cannot promote a facet-interior generator.+* Route bounded power cells through the regular-neighbour graph. The production+ lane derives one radical axis per regular edge and clips one small neighbour+ section per visible site; globally hidden sites perform no HPI, while only+ lower-dimensional sites use the complete exact oracle.+* Make regular-topology descent output-sensitive: hull removal touches only the+ removed face stars, the next expansion comes from a strict conflict frontier,+ supporting-plane duals survive into publication, coplanar fans share one dual,+ and collinear classification is one monotone merge. Regular edges retain only+ their admitted generator pair; neighbour and clipping views are derived at+ their respective consumers rather than stored as competing authorities. On+ the retained GHC 9.14.1 `-O1` 512-site fixture, topology and full-construction+ allocation fall by 10.7% and 10.2%; the corresponding wall times improve by+ roughly 1%.+* Consolidate convex erosion and power-cell construction on one exact clipper+ that retains source affine lines, avoiding measured endpoint-coefficient+ swell without claiming smaller reduced output denominators.+* Replace repeated polygon scans with one exact angular closed-half-plane+ descent, including empty, point, segment, parallel, and coincident sections.+ Clip receipts report boundary compatibility, exact intersections, rational+ widths, regular incidence, oracle cells, and maximum per-cell constraints.+* Evaluate exact signs and affine intersections through integer cross-products+ with one rational normalization at publication. This removes intermediate+ `Ratio` normalization from the HPI predicate kernel.+* Consolidate affine and power inputs on direct exact score coefficients,+ publish already-admitted cells without revalidation, and fold clip receipts+ into one strict summary. On the retained GHC 9.14.1 `-O1` 169-site fixtures,+ construction falls from 171.4 ms / 351.2 MB to 79.2 ms / 105.4 MB for the+ well-conditioned family and from 142.3 ms / 296.7 MB to 65.6 ms / 82.4 MB+ for the near-parallel family. Mesh joins and n-ary overlays interpret one+ shared balanced tournament.+* Collapse four exact-planar test runners into one behavioral owner, return the+ canonical overlay obstruction directly from n-ary refinement, and share the+ bounded-cell and strict-turn folds. Direct mass and confusion now allocate+ 22% and 5% less on their accepted fixtures.+* Execute the exact-planar, documentation-example, and public-component suites+ in CI. Publish only the local embedding certifier; arrangement constructors+ and resident diagonal schedules live in the private `overlay-internal`+ component shared only with package-owned oracle tests.+* Add nominal rank-2 mesh scopes for zero-cost safe traversal, retain owning+ handles for escaping values, and document fixed identifiers as the unchecked+ issuing-mesh index lane. Convex reflection now preserves its counter-clockwise+ carrier invariant directly.+* Preserve a noncanonical zero-site mesh when it is joined with the canonical+ empty identity. The previous zero-count guard order broke structural right+ identity when both operands represented the empty support differently.+ ## 1.4.0.3 - 2026-08-28 * Restore public `dcel` and `build` sublibraries as safe restrictions of their
README.md view
@@ -12,7 +12,7 @@ Delaunay triangulation, constrained Delaunay (CDT), exact rational planar regions and labelled overlay, intrinsic valuations, polygonal Minkowski-morphology, the Voronoi dual, natural-neighbour interpolation, Ruppert+morphology, exact regular/power geometry, the Voronoi dual, natural-neighbour interpolation, Ruppert refinement, walk point location, convex hull, exact Shewchuk predicates, and versioned binary serialization. @@ -32,6 +32,7 @@ | `delaunayFromCoordinates` | Payloads have a separate plane | Defaults, points, payloads, duplicate policy | `BuildResult` / `BuildError` | | `insert` / `insertAt` / `insertMany` | An immutable mesh gains sites or payload replacements | Mesh plus payloads or explicit points | `InsertionResult` / `BuildResult` / `BuildError` | | `withSession` | Many inserts and removals should publish once | Mesh, peak added-site count, composed `Session` | Result, mesh, and `BuildStats` / `BuildError` |+| `withScopedTriangulation` | Local traversal must make cross-mesh identifiers unrepresentable | Mesh and rank-2 continuation | Zero-cost scoped mesh and identifiers | | `removeVertex` / `locateAndRemove` | A resident handle or exact position must be removed | Mesh plus `VertexId` / `Point` | `RemovalResult` / optional result / `BuildError` | | `siteRelation` | Supports need exact classification | Two meshes | `SiteRelation` | | `union` / `unions` | Unconstrained supports must join | Two meshes / mesh list | Union mesh / `BuildError` |@@ -49,12 +50,19 @@ | `locatePoint` / `locatePointWithHint` | An admitted point needs an exact cell classification | Mesh, `QueryPoint`, optional hint | `Location`, optionally with `LocationStats` | | `lineIntersections` | A segment needs its ordered mesh crossings | Mesh and admitted endpoints | `[Intersection]` | | `verticesInCircle` / `verticesInRectangle` | A metric window selects resident sites | Mesh and circle / rectangle | Handles / typed metric error |+| `exactClipRetainedPolygon` | A convex exact polygon must meet closed half-planes while retaining source lines | Retained polygon and half-plane list | Empty, point, segment, or polygon section plus receipt / `ExactClipError` | | `overlayLayers` | Layers need one exact arrangement | Two `PlanarLayer`s | `OverlayResult` / `OverlayError` |+| `overlayAll` | A nonempty layer family needs one tuple-labelled common refinement | `NonEmpty (PlanarLayer label)` | `PlanarLayer (NonEmpty label)` / `OverlayError` | | `overlayClosedUnion` / `overlayClosedIntersection` / `overlayRegularizedDifference` | Boolean output may retain lower cells | Two label predicates, `OverlayResult` | `ExactCellSet` / `OverlaySelectionError` | | `overlaySelectedRegion` | Selected faces must publish as polygons | Label-pair predicate, `OverlayResult` | `PlanarRegion` / `RegionPublicationError` |+| `overlayMass` / `overlayConfusion` | Only exact selected area or the finite label-pair matrix is needed | Predicate / `OverlayResult` | `ExactArea` / exact mass map |+| `layerCovers` | A labelled layer must cover an admitted polygonal window | `PlanarLayer`, `PolygonComponent` | `()` or `LayerCoverageError` carrying the exact gap | | `cellValuations` / `regionValuations` | Intrinsic measures are required | `ExactCellSet` / `PlanarRegion` | `PlanarValuations` / `ValuationError` | | `minkowskiSum` / `polygonOffset` | Regions must add / expand | Two regions / element and region | Region plus receipt / `MinkowskiError` | | `erodeBy` / `openWith` / `closeWith` | Regularized morphology is required | Structuring element, region | Region plus receipt / `MinkowskiError` |+| `regularTriangulation` | Weighted sites need unbounded topology and exact dual geometry | Nonempty `PowerSite` family | Opaque regular topology, dispositions, segments/rays/lines, and receipt / `PowerDiagramError` |+| `boundedPowerDiagram` | Labelled weighted sites need exact cells inside one finite convex domain | `ConvexPolygon`, nonempty `PowerSite` family | Total labelled dispositions plus rational-width receipt / `PowerDiagramError` |+| `upperEnvelope` | Exact affine forms need their labelled argmax decomposition in a polygonal window | `PolygonComponent`, `Map label AffineForm` | `PlanarLayer (Maybe label)` / `UpperEnvelopeError` | | `alphaShapeContainsFace` | One face at one radius is enough | `RadiusSquared`, mesh, `FaceId` | `Bool` | | `alphaFiltration` | Every critical radius matters | Delaunay mesh | `AlphaFiltration` / `AlphaFiltrationError` | | `fromExactCellSet` | Exact cells need a generic complex view | `ExactCellSet` | `DCELComplex` |@@ -64,6 +72,23 @@ [Interpret cell sets as incidence categories.](docs/category-observatory/README.md#cell-complex-and-category-interpretation) · [Compute persistent alpha topology.](docs/persistence-rose/README.md#exact-alpha-filtration-and-persistent-topology) +## Exact power cells and affine envelopes++`PowerWeight` admits a finite signed additive offset through its own+`PowerWeightError`; `PowerSite` admits its binary64 position once.+`regularTriangulation` constructs the exact lifted upper hull once. It retains+every label's visibility disposition and publishes regular faces plus weighted+dual segments, rays, full lines, or collapsed degenerate edges.+`boundedPowerDiagram` then clips only regular-neighbour inequalities against the+`ConvexPolygon`; globally hidden sites need no clip, while lower-dimensional+sites use the complete HPI oracle. It retains one full-dimensional,+lower-dimensional, empty, coincident-equivalent, or coincident-dominated result+per label. `powerDiagramPlanarLayer` is only the derived full-dimensional view.++`upperEnvelope` maps exact `c0 + cx*x + cy*y` forms into the same owner. Its+planar result contains only two-dimensional winning regions, with `Nothing`+outside the window; nonconvex windows and holes use the canonical overlay.+ ## Foreign bindings The C ABI and its Python, TypeScript, and Rust consumers are documented in the@@ -130,6 +155,11 @@ vector >= 0.13 && < 0.14 ``` +Use `withScopedTriangulation` for allocation-free local incidence traversal.+Use `Handles.Dynamic` when a handle must escape. Fixed `VertexId`/edge/face+values are unchecked resident indices and belong only with the mesh that issued+them.+ [`SetAlgebra.hs`](docs/examples/Moonlight/Triangulation/Example/SetAlgebra.hs) is the smallest compile-checked workflow. The [example index](docs/examples/README.md) adds constrained interiors,@@ -140,8 +170,8 @@ ```cabal build-depends:- moonlight-triangulation:dcel >= 1.4.1 && < 1.5,- moonlight-triangulation:build >= 1.4.1 && < 1.5+ moonlight-triangulation:dcel >= 1.4 && < 1.5,+ moonlight-triangulation:build >= 1.4 && < 1.5 ``` ## Architecture@@ -154,15 +184,14 @@ ## Consumer libraries The Cabal manifest owns component visibility and dependencies; this is its-consumer-facing projection. The default library reexports the complete safe-geometry surface. Use a public sublibrary directly when its smaller dependency-cone matters; the trusted implementation units remain private.+consumer-facing projection. Use a public sublibrary directly when its smaller+dependency cone matters; trusted implementation units remain private. | Dependency | Imports | Purpose | | --- | --- | --- |-| `moonlight-triangulation` | `Moonlight.Triangulation` and every safe `dcel`, `build`, and `dual` module | Broad construction, observation, set algebra, CDT, refinement, alpha, Voronoi, interpolation, and exact planar surface. |+| `moonlight-triangulation` | `Moonlight.Triangulation` plus the public `dcel`, `build`, and `dual` modules | Broad construction, observation, set algebra, CDT, refinement, alpha, Voronoi, interpolation, and exact planar surface. | | `moonlight-triangulation:core` | `.Scalar`, `.LineSideInfo` | Low-level exact scalars and paged storage primitives. |-| `moonlight-triangulation:dcel` | `.Types`, `.Dcel`, `.Handles.*`, `.PointLocation`, `.FloodFillIterator`, `.IntersectionIterator`, `.Alpha`, `.Exact`, `.Region`, `.Valuation` | Immutable mesh vocabulary, observations, typed spatial queries, exact geometry, and owning handles; no construction kernel. |+| `moonlight-triangulation:dcel` | `.Types`, `.Dcel`, `.Handles.*`, `.PointLocation`, `.FloodFillIterator`, `.IntersectionIterator`, `.Alpha`, `.Exact`, `.Region`, `.Valuation` | Immutable mesh vocabulary, exact geometry, scoped/owning observations, and the explicit fixed-index kernel; no construction kernel. | | `moonlight-triangulation:build` | `.BulkLoad`, `.Session`, `.Removal`, `.Cdt`, `.Refinement`, `.SetAlgebra` | Construction and persistent editing over the DCEL; no dual, serialization, concurrency, or Homology dependency. | | `moonlight-triangulation:dual` | `.Voronoi`, `.Voronoi.Handles`, `.Interpolation` | Voronoi observations and natural-neighbour interpolation. | | `moonlight-triangulation:parallel` | `.Parallel` | Bounded concurrent union; adds `async`. |
+ bench/layer/Main.hs view
@@ -0,0 +1,8 @@+module Main (main) where++import qualified Moonlight.Triangulation.LayerBench as LayerBench++main :: IO ()+main = do+ fixtures <- LayerBench.prepareFixtures+ LayerBench.runBenchmarks fixtures
+ bench/layer/Moonlight/Triangulation/LayerBench.hs view
@@ -0,0 +1,244 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}++-- | Timing and allocation evidence for exact affine envelopes and layer+-- composition. Manual lanes use only the pre-existing public operations a+-- caller otherwise has to compose and discard.+module Moonlight.Triangulation.LayerBench+ ( LayerBenchFixtures+ , prepareFixtures+ , runBenchmarks+ ) where++import Control.DeepSeq (NFData)+import Control.Monad (foldM)+import Data.Bifunctor (first)+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.Map.Strict as Map+import GHC.Generics (Generic)+import Moonlight.Triangulation+ ( AffineForm (..)+ , ExactArea+ , ExactRational+ , OverlayResult+ , PlanarLayer+ , PlanarRegion+ , PolygonComponent+ , exactAreaValue+ , exactLoop+ , exactPoint+ , layerCovers+ , overlayAll+ , overlayConfusion+ , overlayLayers+ , overlayMass+ , overlayPlanarLayer+ , overlaySelectedRegion+ , planarLayer+ , planarLayerOutsideLabel+ , planarLayerRegions+ , planarRegion+ , polygonComponent+ , regionValuations+ , upperEnvelope+ , valuationArea+ )+import Test.Tasty.Bench (bench, bgroup, defaultMain, nf)++data LayerBenchFixtures = LayerBenchFixtures+ { envelopeWindow :: !PolygonComponent+ , envelopeForms16 :: !(Map.Map Int AffineForm)+ , envelopeForms64 :: !(Map.Map Int AffineForm)+ , envelopeForms169 :: !(Map.Map Int AffineForm)+ , overlayLayers8 :: !(NonEmpty (PlanarLayer Bool))+ , overlayLayers16 :: !(NonEmpty (PlanarLayer Bool))+ , measuredOverlay :: !(OverlayResult Bool Bool)+ , coverageLayer :: !(PlanarLayer Bool)+ }+ deriving stock (Generic)+ deriving anyclass (NFData)++prepareFixtures :: IO LayerBenchFixtures+prepareFixtures = do+ window <- rectangleComponent (-20) (-20) 20 20+ layers8 <- prepareLayerFamily 8+ layers16 <- prepareLayerFamily 16+ left <- rectangleLayer 0 0 60 100+ right <- rectangleLayer 40 0 100 100+ massOverlay <- requireRight "mass benchmark overlay" (overlayLayers left right)+ windowRegion <- requireRight "coverage benchmark region" (planarRegion [window])+ completeCoverage <-+ requireRight+ "coverage benchmark layer"+ (planarLayer False (Map.singleton True windowRegion))+ pure+ LayerBenchFixtures+ { envelopeWindow = window+ , envelopeForms16 = affineForms 16+ , envelopeForms64 = affineForms 64+ , envelopeForms169 = affineForms 169+ , overlayLayers8 = layers8+ , overlayLayers16 = layers16+ , measuredOverlay = massOverlay+ , coverageLayer = completeCoverage+ }++runBenchmarks :: LayerBenchFixtures -> IO ()+runBenchmarks fixtures =+ defaultMain+ [ bgroup+ "exact affine upper envelope"+ [ bench "n=16" (nf (upperEnvelope (envelopeWindow fixtures)) (envelopeForms16 fixtures))+ , bench "n=64" (nf (upperEnvelope (envelopeWindow fixtures)) (envelopeForms64 fixtures))+ , bench "n=169" (nf (upperEnvelope (envelopeWindow fixtures)) (envelopeForms169 fixtures))+ ]+ , bgroup+ "n-ary overlay"+ [ bench "balanced/n=8" (nf balancedOverlay (overlayLayers8 fixtures))+ , bench "left-associated/n=8" (nf leftAssociatedOverlay (overlayLayers8 fixtures))+ , bench "balanced/n=16" (nf balancedOverlay (overlayLayers16 fixtures))+ , bench "left-associated/n=16" (nf leftAssociatedOverlay (overlayLayers16 fixtures))+ ]+ , bgroup+ "overlay analytics"+ [ bench "mass/direct" (nf directMass (measuredOverlay fixtures))+ , bench "mass/publish-and-value" (nf publishedMass (measuredOverlay fixtures))+ , bench "confusion/direct" (nf directConfusion (measuredOverlay fixtures))+ , bench "confusion/repeated-publication" (nf publishedConfusion (measuredOverlay fixtures))+ , bench+ "coverage/complete"+ (nf (coverageResult (coverageLayer fixtures)) (envelopeWindow fixtures))+ ]+ ]++balancedOverlay+ :: NonEmpty (PlanarLayer Bool)+ -> Either String (PlanarLayer (NonEmpty Bool))+balancedOverlay = first show . overlayAll++leftAssociatedOverlay+ :: NonEmpty (PlanarLayer Bool)+ -> Either String (PlanarLayer (NonEmpty Bool))+leftAssociatedOverlay (firstLayer :| remainingLayers) = do+ initial <- singletonLabelLayer firstLayer+ liftedRemaining <- traverse singletonLabelLayer remainingLayers+ foldM overlayNext initial liftedRemaining+ where+ overlayNext left right = do+ result <- first show (overlayLayers left right)+ flattenPublicLayer (overlayPlanarLayer result)++singletonLabelLayer+ :: PlanarLayer Bool+ -> Either String (PlanarLayer (NonEmpty Bool))+singletonLabelLayer layer =+ first show+ ( planarLayer+ (planarLayerOutsideLabel layer :| [])+ ( Map.fromList+ [ (label :| [], region)+ | (label, region) <- Map.toAscList (planarLayerRegions layer)+ ]+ )+ )++flattenPublicLayer+ :: PlanarLayer (NonEmpty Bool, NonEmpty Bool)+ -> Either String (PlanarLayer (NonEmpty Bool))+flattenPublicLayer layer =+ first show+ ( planarLayer+ (uncurry (<>) (planarLayerOutsideLabel layer))+ ( Map.fromList+ [ (leftLabels <> rightLabels, region)+ | ((leftLabels, rightLabels), region) <- Map.toAscList (planarLayerRegions layer)+ ]+ )+ )++directMass :: OverlayResult Bool Bool -> Either String ExactArea+directMass = first show . overlayMass (== (True, True))++publishedMass :: OverlayResult Bool Bool -> Either String ExactArea+publishedMass result = do+ region <- first show (overlaySelectedRegion (== (True, True)) result)+ valuationArea <$> first show (regionValuations region)++directConfusion :: OverlayResult Bool Bool -> Map.Map (Bool, Bool) ExactRational+directConfusion = fmap exactAreaValue . overlayConfusion++publishedConfusion+ :: OverlayResult Bool Bool+ -> Either String (Map.Map (Bool, Bool) ExactRational)+publishedConfusion result =+ Map.fromList+ <$> traverse+ (\labels -> (labels,) . exactAreaValue <$> publishedPairMass labels)+ [(False, True), (True, False), (True, True)]+ where+ publishedPairMass labels = do+ region <- first show (overlaySelectedRegion (== labels) result)+ valuationArea <$> first show (regionValuations region)++coverageResult+ :: PlanarLayer Bool+ -> PolygonComponent+ -> Either String ()+coverageResult layer = first show . layerCovers layer++prepareLayerFamily :: Int -> IO (NonEmpty (PlanarLayer Bool))+prepareLayerFamily count = do+ layers <- traverse prepareLayer [0 .. count - 1]+ case layers of+ firstLayer : remaining -> pure (firstLayer :| remaining)+ [] -> fail "layer benchmark requires a positive family size"+ where+ prepareLayer index =+ rectangleLayer+ (-64 + fromIntegral ((7 * index) `mod` 31))+ (-64 + fromIntegral ((11 * index) `mod` 29))+ (32 + fromIntegral ((13 * index) `mod` 31))+ (32 + fromIntegral ((17 * index) `mod` 29))++rectangleLayer :: Integer -> Integer -> Integer -> Integer -> IO (PlanarLayer Bool)+rectangleLayer minimumX minimumY maximumX maximumY = do+ region <- rectangleRegion minimumX minimumY maximumX maximumY+ requireRight "benchmark rectangle layer" (planarLayer False (Map.singleton True region))++rectangleRegion :: Integer -> Integer -> Integer -> Integer -> IO PlanarRegion+rectangleRegion minimumX minimumY maximumX maximumY = do+ component <- rectangleComponent minimumX minimumY maximumX maximumY+ requireRight "benchmark rectangle region" (planarRegion [component])++rectangleComponent :: Integer -> Integer -> Integer -> Integer -> IO PolygonComponent+rectangleComponent minimumX minimumY maximumX maximumY = do+ loop <-+ requireRight+ "benchmark rectangle loop"+ ( exactLoop+ ( exactPoint (fromInteger minimumX) (fromInteger minimumY)+ :| [ exactPoint (fromInteger maximumX) (fromInteger minimumY)+ , exactPoint (fromInteger maximumX) (fromInteger maximumY)+ , exactPoint (fromInteger minimumX) (fromInteger maximumY)+ ]+ )+ )+ requireRight "benchmark rectangle component" (polygonComponent loop [])++affineForms :: Int -> Map.Map Int AffineForm+affineForms count =+ Map.fromAscList+ [ (index, tangentForm index)+ | index <- [0 .. count - 1]+ ]+ where+ tangentForm index =+ let coordinateX = fromIntegral (index `mod` 17 - 8)+ coordinateY = fromIntegral (index `div` 17 - 5)+ in AffineForm+ (negate (coordinateX * coordinateX + coordinateY * coordinateY))+ (2 * coordinateX)+ (2 * coordinateY)++requireRight :: Show failure => String -> Either failure value -> IO value+requireRight label = either (fail . ((label <> ": ") <>) . show) pure
+ bench/power/Main.hs view
@@ -0,0 +1,9 @@+module Main (main) where++import qualified Moonlight.Triangulation.PowerBench as PowerBench++main :: IO ()+main = do+ cases <- PowerBench.prepareCases+ PowerBench.reportCases cases+ PowerBench.runBenchmarks cases
+ bench/power/Moonlight/Triangulation/PowerBench.hs view
@@ -0,0 +1,289 @@+-- | Tasty-bench lanes for the exact bounded power-diagram owner. The timed+-- lanes force the public result, while the one-shot report keeps the+-- structural receipt and a shared incremental allocation observation outside+-- the timing sample. One construction is revealed, its diagram is forced,+-- and only then is the additional receipt-reachable section measured.+module Moonlight.Triangulation.PowerBench+ ( PowerBenchCase+ , prepareCases+ , reportCases+ , runBenchmarks+ ) where++import BenchSupport (randomPoints, requireRight)+import Control.DeepSeq (force)+import Control.Exception (evaluate)+import Data.Foldable (traverse_)+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NonEmpty+import GHC.Conc.Sync (getAllocationCounter, setAllocationCounter)+import Moonlight.Triangulation+ ( BoundedPowerDiagram+ , ConvexPolygon+ , Point (Point)+ , PowerDiagramError+ , PowerDiagramReceipt+ , PowerSite+ , RegularTriangulation+ , RegularTriangulationReceipt+ , boundedPowerDiagram+ , convexPolygon+ , exactPoint+ , powerDiagramActiveBoundaries+ , powerDiagramCoincidentDominatedCells+ , powerDiagramCoincidentEquivalentCells+ , powerDiagramDomainVertices+ , powerDiagramBoundaryCompatibilityChecks+ , powerDiagramEmptyCells+ , powerDiagramExactIntersections+ , powerDiagramFinalCoordinateBitGrowth+ , powerDiagramFinalCoordinateBits+ , powerDiagramFinalDenominatorBits+ , powerDiagramInputSites+ , powerDiagramLowerDimensionalCells+ , powerDiagramMaximumAffineCoefficientBits+ , powerDiagramMaximumInputBits+ , powerDiagramPeakIntermediateBitGrowth+ , powerDiagramPeakIntermediateCoordinateBits+ , powerDiagramMaximumCellConstraints+ , powerDiagramPublishedCells+ , powerDiagramOracleCells+ , powerDiagramRegularEdges+ , powerDiagramRegularFaces+ , powerDiagramSubmittedSiteConstraints+ , powerSite+ , powerWeight+ , regularTriangulation+ )+import System.Mem (performGC)+import Test.Tasty.Bench (Benchmark, bench, bgroup, defaultMain, nf)++data PowerBenchFamily+ = WellConditioned+ | NearParallel+ | CollinearSlopes+ deriving stock (Eq, Ord, Show)++data PowerBenchCase = PowerBenchCase+ { powerBenchLabel :: !String+ , powerBenchDomain :: !ConvexPolygon+ , powerBenchSites :: !(NonEmpty (PowerSite Int))+ }++type PowerResult =+ Either (PowerDiagramError Int) (BoundedPowerDiagram Int, PowerDiagramReceipt)++type DiagramResult =+ Either (PowerDiagramError Int) (BoundedPowerDiagram Int)++type RegularResult =+ Either+ (PowerDiagramError Int)+ (RegularTriangulation Int, RegularTriangulationReceipt)++siteCounts :: [Int]+siteCounts = [16, 64, 128, 169, 256, 512]++families :: [PowerBenchFamily]+families = [WellConditioned, NearParallel, CollinearSlopes]++-- | Deterministic, fixed-domain fixture bands include the exhibit scale and a+-- larger scaling point. Ordinary and nearly parallel families expose exact+-- clipping cost; the collinear family exposes upper-chain classification.+prepareCases :: IO [PowerBenchCase]+prepareCases =+ traverse prepareCase [(family, count) | family <- families, count <- siteCounts]++prepareCase :: (PowerBenchFamily, Int) -> IO PowerBenchCase+prepareCase (family, siteCount) = do+ domain <-+ requireRight+ ( convexPolygon+ ( exactPoint (-1) (-1)+ :| [exactPoint 1 (-1), exactPoint 1 1, exactPoint (-1) 1]+ )+ )+ sites <-+ traverse (prepareSite family) (zip [0 ..] (fixturePoints family siteCount))+ case NonEmpty.nonEmpty sites of+ Nothing -> fail "power benchmark fixture requires at least one site"+ Just nonEmptySites ->+ pure+ PowerBenchCase+ { powerBenchLabel = familyLabel family <> "/n=" <> show siteCount+ , powerBenchDomain = domain+ , powerBenchSites = nonEmptySites+ }++fixturePoints :: PowerBenchFamily -> Int -> [Point]+fixturePoints family siteCount =+ case family of+ WellConditioned -> randomPoints 0x9e3779b97f4a7c15 siteCount+ NearParallel -> nearParallelPoints siteCount+ CollinearSlopes -> collinearSlopePoints siteCount++nearParallelPoints :: Int -> [Point]+nearParallelPoints siteCount =+ [ Point+ (fromIntegral index / fromIntegral (siteCount + 1) * 1.8 - 0.9)+ (fromIntegral index / fromIntegral (siteCount + 1) * 1.8e-6 - 0.9e-6)+ | index <- [1 .. siteCount]+ ]++collinearSlopePoints :: Int -> [Point]+collinearSlopePoints siteCount =+ [ Point (fromIntegral index / 1024 - 0.5) 0+ | index <- [0 .. siteCount - 1]+ ]++familyLabel :: PowerBenchFamily -> String+familyLabel WellConditioned = "power-well-conditioned"+familyLabel NearParallel = "power-near-parallel"+familyLabel CollinearSlopes = "power-collinear-slopes"++prepareSite :: PowerBenchFamily -> (Int, Point) -> IO (PowerSite Int)+prepareSite family (label, point) = do+ let weightValue =+ case family of+ CollinearSlopes+ | odd label -> -(2 / 1048576)+ | otherwise -> 0+ WellConditioned -> fromIntegral (label `mod` 9 - 4) / 256+ NearParallel -> fromIntegral (label `mod` 9 - 4) / 256+ weight <- requireRight (powerWeight weightValue)+ requireRight (powerSite label point weight)++-- | Print one receipt and one allocation probe per fixture. Tasty-bench+-- remains the authority for timing; this report is the derived structural+-- view that timing samples cannot carry.+reportCases :: [PowerBenchCase] -> IO ()+reportCases cases = do+ putStrLn "moonlight-triangulation exact bounded power-diagram benchmark"+ putStrLn+ "receipt-overhead-method: reveal one shared construction, force its diagram, then measure only the additional receipt-reachable allocation; strict work incurred while revealing the pair is not attributed"+ traverse_ reportCase cases++reportCase :: PowerBenchCase -> IO ()+reportCase benchmarkCase = do+ (sharedResult, allocation) <- measureSharedAllocation benchmarkCase+ case sharedResult of+ Left failure -> fail (powerBenchLabel benchmarkCase <> ": " <> show failure)+ Right (_, receipt) -> do+ putStrLn (renderReceipt (powerBenchLabel benchmarkCase) receipt)+ putStrLn+ ( powerBenchLabel benchmarkCase+ <> "-allocated-bytes/shared-full: "+ <> show (sharedFullAllocation allocation)+ )+ putStrLn+ ( powerBenchLabel benchmarkCase+ <> "-allocated-bytes/shared-through-diagram: "+ <> show (sharedDiagramAllocation allocation)+ )+ putStrLn+ ( powerBenchLabel benchmarkCase+ <> "-allocated-bytes/incremental-receipt-reachable-overhead: "+ <> show (sharedReceiptReachableAllocation allocation)+ )++runBenchmarks :: [PowerBenchCase] -> IO ()+runBenchmarks cases = defaultMain [bgroup "exact bounded power diagram" (concatMap caseBenchmarks cases)]++caseBenchmarks :: PowerBenchCase -> [Benchmark]+caseBenchmarks benchmarkCase =+ [ bench+ (powerBenchLabel benchmarkCase <> "/regular-topology")+ (nf constructRegularTopology benchmarkCase)+ , bench+ (powerBenchLabel benchmarkCase <> "/construct-with-receipt")+ (nf construct benchmarkCase)+ , bench+ (powerBenchLabel benchmarkCase <> "/construct-diagram-only")+ (nf constructDiagramOnly benchmarkCase)+ ]++construct :: PowerBenchCase -> PowerResult+construct benchmarkCase =+ boundedPowerDiagram+ (powerBenchDomain benchmarkCase)+ (powerBenchSites benchmarkCase)++constructDiagramOnly :: PowerBenchCase -> DiagramResult+constructDiagramOnly = fmap fst . construct++constructRegularTopology :: PowerBenchCase -> RegularResult+constructRegularTopology = regularTriangulation . powerBenchSites++data SharedPowerAllocation = SharedPowerAllocation+ { sharedFullAllocation :: !Integer+ , sharedDiagramAllocation :: !Integer+ , sharedReceiptReachableAllocation :: !Integer+ }++measureSharedAllocation+ :: PowerBenchCase+ -> IO (PowerResult, SharedPowerAllocation)+measureSharedAllocation benchmarkCase = do+ performGC+ setAllocationCounter maxBound+ allocationStart <- getAllocationCounter+ sharedResult <- evaluate (construct benchmarkCase)+ case sharedResult of+ Left failure -> do+ forcedFailure <- evaluate (force failure)+ allocationEnd <- getAllocationCounter+ let total = allocationDifference allocationStart allocationEnd+ pure+ ( Left forcedFailure+ , SharedPowerAllocation total total 0+ )+ Right (diagram, receipt) -> do+ forcedDiagram <- evaluate (force diagram)+ allocationAfterDiagram <- getAllocationCounter+ forcedReceipt <- evaluate (force receipt)+ allocationAfterReceipt <- getAllocationCounter+ pure+ ( Right (forcedDiagram, forcedReceipt)+ , SharedPowerAllocation+ { sharedFullAllocation =+ allocationDifference allocationStart allocationAfterReceipt+ , sharedDiagramAllocation =+ allocationDifference allocationStart allocationAfterDiagram+ , sharedReceiptReachableAllocation =+ allocationDifference allocationAfterDiagram allocationAfterReceipt+ }+ )++allocationDifference :: Integral amount => amount -> amount -> Integer+allocationDifference before after = toInteger (max 0 (before - after))++renderReceipt :: String -> PowerDiagramReceipt -> String+renderReceipt label receipt =+ unwords+ [ label <> "-receipt:"+ , field "input-sites" powerDiagramInputSites+ , field "domain-vertices" powerDiagramDomainVertices+ , field "submitted-site-constraints" powerDiagramSubmittedSiteConstraints+ , field "active-boundaries" powerDiagramActiveBoundaries+ , field "boundary-compatibility-checks" powerDiagramBoundaryCompatibilityChecks+ , field "exact-intersections" powerDiagramExactIntersections+ , field "published" powerDiagramPublishedCells+ , field "lower-dimensional" powerDiagramLowerDimensionalCells+ , field "empty" powerDiagramEmptyCells+ , field "coincident-equivalent" powerDiagramCoincidentEquivalentCells+ , field "coincident-dominated" powerDiagramCoincidentDominatedCells+ , field "regular-faces" powerDiagramRegularFaces+ , field "regular-edges" powerDiagramRegularEdges+ , field "oracle-cells" powerDiagramOracleCells+ , field "maximum-cell-constraints" powerDiagramMaximumCellConstraints+ , field "input-bits" powerDiagramMaximumInputBits+ , field "affine-bits" powerDiagramMaximumAffineCoefficientBits+ , field "peak-coordinate-bits" powerDiagramPeakIntermediateCoordinateBits+ , field "final-coordinate-bits" powerDiagramFinalCoordinateBits+ , field "final-denominator-bits" powerDiagramFinalDenominatorBits+ , field "peak-growth-bits" powerDiagramPeakIntermediateBitGrowth+ , field "final-growth-bits" powerDiagramFinalCoordinateBitGrowth+ ]+ where+ field :: Show value => String -> (PowerDiagramReceipt -> value) -> String+ field name project = name <> "=" <> show (project receipt)
docs/README.md view
@@ -8,6 +8,7 @@ | Which public operation should I call? | [Package operation index](../README.md#operations) | | Which library token should I depend on? | [Consumer libraries](../README.md#consumer-libraries) | | What algebraic laws do meshes satisfy? | [Package algebraic contract](../README.md#algebraic-contract) |+| How are exact power cells and affine envelopes represented? | [Package affine-cell guide](../README.md#exact-power-cells-and-affine-envelopes) | | What are the set-operation types? | [Package type signatures](../README.md#set-operation-types) | | Which execution schedule publishes each result? | [Mesh publication guide](./mesh-publication.md) | | How do I build and validate components locally? | [Development guide](./development.md) |
moonlight-triangulation.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.4 name: moonlight-triangulation-version: 1.4.0.3+version: 1.4.0.4 synopsis: Delaunay meshes and exact planar-region algebra. description: Delaunay and constrained Delaunay triangulation as a lawful finite-set algebra, together with exact rational planar@@ -106,7 +106,7 @@ source-repository this type: git location: https://github.com/PaleRoses/moonlight.git- tag: moonlight-triangulation-1.4.0.3+ tag: moonlight-triangulation-1.4.0.4 subdir: moonlight-triangulation flag warnings-as-errors@@ -185,6 +185,7 @@ Moonlight.Triangulation.Handles.HandleDefs Moonlight.Triangulation.Internal.HandleDefs Moonlight.Triangulation.Handles.Dynamic+ Moonlight.Triangulation.Handles.Scoped Moonlight.Triangulation.Handles.Iterators.CircularIterator Moonlight.Triangulation.Handles.Iterators.DynamicIterators Moonlight.Triangulation.Handles.Iterators.FixedIterators@@ -207,6 +208,7 @@ Moonlight.Triangulation.Internal.PointIndex Moonlight.Triangulation.Internal.Mutable Moonlight.Triangulation.Internal.OperationState+ Moonlight.Triangulation.Internal.Tournament Moonlight.Triangulation.Internal.Probe Moonlight.Triangulation.Internal.DcelOperations.CandidateArena Moonlight.Triangulation.Internal.DcelOperations.Chain@@ -251,6 +253,7 @@ , Moonlight.Triangulation.JoinSemilattice , Moonlight.Triangulation.Handles.HandleDefs , Moonlight.Triangulation.Handles.Dynamic+ , Moonlight.Triangulation.Handles.Scoped , Moonlight.Triangulation.Handles.Iterators.DynamicIterators , Moonlight.Triangulation.Handles.Iterators.FixedIterators , Moonlight.Triangulation.Handles.Iterators.HullIterator@@ -410,6 +413,27 @@ , moonlight-triangulation:dcel-internal ghc-options: -fexpose-all-unfoldings +-- Package-private overlay machine room. The main library and package-owned+-- oracle tests share these exact modules; external consumers receive only the+-- public embedding certifier and Overlay equations reexported across this wall.+library overlay-internal+ import: shared-properties+ visibility: private+ hs-source-dirs: src-overlay+ exposed-modules:+ Moonlight.Triangulation.Embedding+ Moonlight.Triangulation.Internal.Overlay.Arrangement+ Moonlight.Triangulation.Internal.Overlay.Resident+ Moonlight.Triangulation.Internal.Overlay.Types+ build-depends:+ base >= 4.19 && < 5+ , containers >= 0.8 && < 0.9+ , deepseq >= 1.5 && < 1.6+ , vector >= 0.13 && < 0.14+ , moonlight-triangulation:core+ , moonlight-triangulation:dcel-internal+ , moonlight-triangulation:build-internal+ library import: shared-properties hs-source-dirs:@@ -419,12 +443,10 @@ Moonlight.Triangulation.HintGenerator Moonlight.Triangulation.Minkowski Moonlight.Triangulation.Overlay- Moonlight.Triangulation.Internal.Overlay.Arrangement- Moonlight.Triangulation.Internal.Overlay.Embedding- Moonlight.Triangulation.Internal.Overlay.Resident- -- The default library is the broad safe surface. These are the exact modules- -- compiled by the focused components, not facade copies; consumers that want- -- a smaller dependency cone can name :dcel, :build, or :dual directly.+ Moonlight.Triangulation.PowerDiagram+ -- The default library is the broad surface. Scoped/owning handles are the safe+ -- path; Dcel is the explicitly unchecked fixed-index kernel. These are the+ -- exact focused-component modules, not facade copies. reexported-modules: Moonlight.Triangulation.Alpha , Moonlight.Triangulation.Types@@ -440,6 +462,7 @@ , Moonlight.Triangulation.JoinSemilattice , Moonlight.Triangulation.Handles.HandleDefs , Moonlight.Triangulation.Handles.Dynamic+ , Moonlight.Triangulation.Handles.Scoped , Moonlight.Triangulation.Handles.Iterators.DynamicIterators , Moonlight.Triangulation.Handles.Iterators.FixedIterators , Moonlight.Triangulation.Handles.Iterators.HullIterator@@ -456,10 +479,11 @@ , Moonlight.Triangulation.Voronoi , Moonlight.Triangulation.Voronoi.Handles , Moonlight.Triangulation.Interpolation+ , Moonlight.Triangulation.Embedding other-modules: Moonlight.Triangulation.Internal.Minkowski.Convex Moonlight.Triangulation.Internal.Minkowski.Types- Moonlight.Triangulation.Internal.Overlay.Types+ Moonlight.Triangulation.Internal.RegularTriangulation build-depends: base >= 4.19 && < 5 , containers >= 0.8 && < 0.9@@ -469,6 +493,7 @@ , moonlight-triangulation:dcel-internal , moonlight-triangulation:build-internal , moonlight-triangulation:dual+ , moonlight-triangulation:overlay-internal ghc-options: -fexpose-all-unfoldings library ffi@@ -567,6 +592,7 @@ , moonlight-triangulation:dcel-internal , moonlight-triangulation:build-internal , moonlight-triangulation:dual+ , moonlight-triangulation:overlay-internal common triangulation-serialization-test-slice other-modules: Moonlight.Triangulation.SerializationSpec@@ -609,6 +635,28 @@ moonlight-triangulation:core , moonlight-triangulation:dcel-internal +-- A test-only endpoint-reconstruction oracle demonstrating why the production+-- clipping owner retains original affine source lines.+common triangulation-exact-clip-retention-law-slice+ other-modules: Moonlight.Triangulation.ExactClipRetentionSpec+ build-depends:+ moonlight-triangulation:core+ , moonlight-triangulation:dcel-internal++common triangulation-power-diagram-law-slice+ other-modules: Moonlight.Triangulation.PowerDiagramSpec+ build-depends:+ containers >= 0.8 && < 0.9+ , moonlight-triangulation:core+ , moonlight-triangulation:dcel-internal++common triangulation-layer-operations-law-slice+ other-modules: Moonlight.Triangulation.LayerOperationsSpec+ build-depends:+ containers >= 0.8 && < 0.9+ , moonlight-triangulation:core+ , moonlight-triangulation:dcel-internal+ -- The agreement between the seam schedule and the reference rebuild. It names -- an internal schedule, so it reaches below the wall and travels with the -- schedule it names: a replacement kernel carries its own copy of this.@@ -659,6 +707,25 @@ Moonlight.Triangulation.Example.SetAlgebra build-depends: containers >= 0.8 && < 0.9 +-- One behavioral owner for exact clipping, power cells, morphology, affine+-- envelopes, coverage, n-ary overlay, and mass. The law slices remain+-- independently reusable by the compile-only coherence component.+test-suite moonlight-triangulation-exact-planar-test+ import:+ triangulation-test-properties,+ triangulation-test-support-slice,+ triangulation-algebra-fixture-slice,+ triangulation-exact-clip-retention-law-slice,+ triangulation-minkowski-law-slice,+ triangulation-power-diagram-law-slice,+ triangulation-layer-operations-law-slice+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs:+ test/exact-planar+ test/algebra+ test/support+ -- A real external shape over only the two focused public components. It proves -- that immutable observation and construction/editing compose without either -- the broad facade or a private implementation token.@@ -743,7 +810,6 @@ triangulation-algebra-law-slice, triangulation-region-algebra-law-slice, triangulation-valuation-law-slice,- triangulation-minkowski-law-slice, triangulation-algebra-schedule-slice type: exitcode-stdio-1.0 main-is: Main.hs@@ -775,7 +841,7 @@ , moonlight-triangulation:ffi -- This component owns only cross-slice compile coherence. Behavioral ownership--- remains in the four focused suites. The shared test-properties stanza keeps+-- remains in focused suites. The shared test-properties stanza keeps -- every test body at @-O0@, so the union retains type/module/instance collision -- detection without paying to optimize a second copy of every test body. test-suite moonlight-triangulation-coherence-test@@ -789,6 +855,9 @@ triangulation-region-algebra-law-slice, triangulation-valuation-law-slice, triangulation-minkowski-law-slice,+ triangulation-exact-clip-retention-law-slice,+ triangulation-power-diagram-law-slice,+ triangulation-layer-operations-law-slice, triangulation-algebra-schedule-slice, triangulation-parallel-test-slice type: exitcode-stdio-1.0@@ -1069,6 +1138,41 @@ , moonlight-triangulation:cell-complex , moonlight-triangulation:dcel-internal , vector >= 0.13 && < 0.14++-- Exact bounded power-cell construction. Tasty-bench owns the timing+-- distribution; the benchmark's one-shot report derives boundary/intersection and+-- rational-width receipts from the public power-diagram result.+benchmark moonlight-triangulation-power-bench+ import:+ triangulation-benchmark-properties,+ triangulation-benchmark-support-slice+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs:+ bench/power+ bench/support+ other-modules:+ Moonlight.Triangulation.PowerBench+ build-depends:+ tasty-bench >= 0.3 && < 0.6++-- Exact affine-envelope construction, balanced n-ary refinement, and direct+-- cell analytics against the public compositions callers would otherwise+-- materialize by hand.+benchmark moonlight-triangulation-layer-bench+ import:+ triangulation-benchmark-properties,+ triangulation-benchmark-support-slice+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs:+ bench/layer+ bench/support+ other-modules:+ Moonlight.Triangulation.LayerBench+ build-depends:+ containers >= 0.8 && < 0.9+ , tasty-bench >= 0.3 && < 0.6 benchmark moonlight-triangulation-build-bench import:
src-build/Moonlight/Triangulation/Internal/Join.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -7,19 +6,15 @@ module Moonlight.Triangulation.Internal.Join ( joinNormalForm , joinBalanced- , executeTournamentPlan- , TournamentPlan (..)- , planTournament ) where +import Control.Monad (void) import Data.Foldable (traverse_) import Data.List.NonEmpty (NonEmpty (..)) import Moonlight.Triangulation.BulkLoad (empty) import Moonlight.Triangulation.Internal.Join.Plan ( PairPlan (..)- , TournamentPlan (..) , planPair- , planTournament ) import Moonlight.Triangulation.Internal.Join.Seam (executeSeam) import Moonlight.Triangulation.Internal.Join.SiteSet@@ -35,6 +30,10 @@ ( insertAdmittedVertexAtCombining , withLocalSession )+import Moonlight.Triangulation.Internal.Tournament+ ( interpretTournament+ , planTournament+ ) import Moonlight.Triangulation.Types -- | A Delaunay representative of the union of two site sets.@@ -85,7 +84,7 @@ (siteSetSize sites) ( traverse_ (\(point, annotation) ->- () <$ insertAdmittedVertexAtCombining joinAnnotations point annotation+ void (insertAdmittedVertexAtCombining joinAnnotations point annotation) ) (siteSetAssocs sites) )@@ -112,19 +111,7 @@ -> Either BuildError (Triangulation 'Unconstrained annotation () () ()) joinBalanced [] = Right (empty unitElementDefaults) joinBalanced (first : rest) =- executeTournamentPlan (planTournament (first :| rest))+ interpretTournament joinNormalForm (planTournament (first :| rest)) {-# SPECIALIZE joinBalanced :: [Triangulation 'Unconstrained () () () ()] -> Either BuildError (Triangulation 'Unconstrained () () () ()) #-}--executeTournamentPlan- :: JoinSemilattice annotation- => TournamentPlan (Triangulation 'Unconstrained annotation () () ())- -> Either BuildError (Triangulation 'Unconstrained annotation () () ())-executeTournamentPlan tournament =- case tournament of- TournamentLeaf mesh -> Right mesh- TournamentNode left right -> do- leftMesh <- executeTournamentPlan left- rightMesh <- executeTournamentPlan right- joinNormalForm leftMesh rightMesh
src-build/Moonlight/Triangulation/Internal/Join/Plan.hs view
@@ -8,12 +8,10 @@ module Moonlight.Triangulation.Internal.Join.Plan ( PairPlan (..) , planPair- , TournamentPlan (..)- , planTournament ) where -import Data.List.NonEmpty (NonEmpty (..)) import Moonlight.Triangulation.Dcel (numVertices)+import Moonlight.Triangulation.Internal.BulkLoad (empty) import Moonlight.Triangulation.Internal.Join.Seam (SeamPlan, planSeam) import Moonlight.Triangulation.Internal.Join.SiteSet ( SiteSet@@ -26,6 +24,7 @@ import Moonlight.Triangulation.Internal.Types ( ConstraintMode (Unconstrained) , SiteRelation (..)+ , unitElementDefaults ) data PairPlan annotation@@ -45,6 +44,12 @@ -> Triangulation 'Unconstrained annotation () () () -> PairPlan annotation planPair left right+ -- Two zero-site values may differ structurally; the package-owned empty is+ -- the identity, so preserve the other representative when only one is it.+ | leftCount == 0 && rightCount == 0 =+ if left == empty unitElementDefaults+ then ReturnRightOperand+ else ReturnLeftOperand | leftCount == 0 = ReturnRightOperand | rightCount == 0 = ReturnLeftOperand | left == right = ReturnLeftOperand@@ -73,29 +78,3 @@ insertionIsCheaper :: Int -> Int -> Bool insertionIsCheaper addition base = addition <= 64 || addition <= base `quot` 8 {-# INLINE insertionIsCheaper #-}---- | A deterministic dependency graph. Leaves retain meshes as values; no site--- flattening occurs, so singleton and repeated-value shortcut semantics remain--- those of the binary operation. Duplicate operands need no planning pass:--- every adjacent pair reaches the binary operation, whose structural-equality--- shortcut already returns the operand verbatim, so a dedup here would buy a--- quadratic scan of whole meshes to skip work the executor skips anyway.-data TournamentPlan mesh- = TournamentLeaf !mesh- | TournamentNode !(TournamentPlan mesh) !(TournamentPlan mesh)--planTournament :: NonEmpty mesh -> TournamentPlan mesh-planTournament = buildBalanced . fmap TournamentLeaf- where- buildBalanced :: NonEmpty (TournamentPlan value) -> TournamentPlan value- buildBalanced (single :| []) = single- buildBalanced plans = buildBalanced (pairRound plans)-- pairRound :: NonEmpty (TournamentPlan value) -> NonEmpty (TournamentPlan value)- pairRound (left :| right : rest) =- TournamentNode left right :| pairTail rest- pairRound (single :| []) = single :| []-- pairTail :: [TournamentPlan value] -> [TournamentPlan value]- pairTail (left : right : rest) = TournamentNode left right : pairTail rest- pairTail rest = rest
src-core/Moonlight/Triangulation/Internal/ExactRational.hs view
@@ -15,6 +15,8 @@ , exactRationalFromDyadicRatio , exactRationalNumerator , exactRationalDenominator+ , exactRationalBitWidth+ , exactRationalDenominatorBitWidth , exactRationalIsZero , exactDivide , exactSignum@@ -122,6 +124,27 @@ exactRationalDenominator :: ExactRational -> Integer exactRationalDenominator (ExactRational value) = Ratio.denominator value {-# INLINE exactRationalDenominator #-}++-- | Maximum bit width of the reduced numerator magnitude and strictly+-- positive denominator. This is an observation, not an arithmetic bound.+exactRationalBitWidth :: ExactRational -> Int+exactRationalBitWidth value =+ max+ (integerBitWidth (abs (exactRationalNumerator value)))+ (exactRationalDenominatorBitWidth value)+{-# INLINE exactRationalBitWidth #-}++-- | Bit width of the reduced, strictly positive denominator.+exactRationalDenominatorBitWidth :: ExactRational -> Int+exactRationalDenominatorBitWidth =+ integerBitWidth . exactRationalDenominator+{-# INLINE exactRationalDenominatorBitWidth #-}++integerBitWidth :: Integer -> Int+integerBitWidth value+ | value <= 0 = 0+ | otherwise = I# (integerLog2# value) + 1+{-# INLINE integerBitWidth #-} -- | Test whether the exact value is zero. exactRationalIsZero :: ExactRational -> Bool
src-dcel/Moonlight/Triangulation/Dcel.hs view
@@ -1,8 +1,10 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE BangPatterns #-} --- | Constant-time observations and persistent payload updates over the--- immutable DCEL.+-- | Constant-time fixed-index observations and persistent payload updates over+-- the immutable DCEL. This is the explicit unchecked resident-index kernel;+-- ordinary consumers should use scoped or owning handles so indices cannot+-- cross mesh boundaries. module Moonlight.Triangulation.Dcel ( outerFace , numVertices@@ -370,8 +372,9 @@ -> [DirectedEdgeId] circularWalk triangulation start advance = build- ( \link stop ->- let go !remaining !current !visited+ ( \(link :: DirectedEdgeId -> result -> result) (stop :: result) ->+ let go :: Int -> DirectedEdgeId -> Bool -> result+ go !remaining !current !visited | remaining <= 0 = stop | visited && current == start = stop | otherwise = link current (go (remaining - 1) (advance current) True)@@ -380,7 +383,8 @@ {-# INLINE circularWalk #-} circularFold- :: Triangulation mode vertex directed undirected face -> DirectedEdgeId+ :: forall mode vertex directed undirected face a.+ Triangulation mode vertex directed undirected face -> DirectedEdgeId -> (DirectedEdgeId -> DirectedEdgeId) -> (a -> DirectedEdgeId -> a) -> a@@ -388,6 +392,7 @@ circularFold triangulation start advance step = go (numDirectedEdges triangulation + 1) start False where+ go :: Int -> DirectedEdgeId -> Bool -> a -> a go !remaining !current !visited !accumulator | remaining <= 0 = accumulator | visited && current == start = accumulator
src-dcel/Moonlight/Triangulation/Exact.hs view
@@ -8,12 +8,35 @@ , exactPoint , exactPointCoordinates , exactPointCross+ , exactPointBitWidth+ , ExactAffineLine+ , ExactHalfPlaneError (..)+ , exactAffineLine+ , exactAffineLineCoefficients+ , oppositeExactAffineLine+ , exactAffineLineIntersection+ , ExactClosedHalfPlane+ , exactClosedHalfPlane+ , exactClosedHalfPlaneFromDirectedEdge+ , exactClosedHalfPlaneLine+ , classifyExactPoint+ , ExactRetainedPolygon+ , exactRetainedPolygon+ , exactRetainedPolygonPoints+ , ExactClipDisposition (..)+ , ExactClipError (..)+ , ExactClipReceipt (..)+ , exactClipRetainedPolygon , ExactVector (..) , exactVectorFromPoints , addExactVectors , exactCross , compareExactVectorAngle , translateExactPoint+ , ExactRay+ , exactRay+ , exactRayOrigin+ , exactRayDirection , ExactSegment , ExactGeometryError (..) , exactSegment@@ -32,14 +55,31 @@ ) where import Control.DeepSeq (NFData)+import Control.Monad (foldM)+import qualified Data.List as List+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NonEmpty+import Data.Maybe (mapMaybe)+import qualified Data.Sequence as Sequence+import Data.Sequence (Seq, ViewL (..), ViewR (..), (|>)) import GHC.Generics (Generic) import Moonlight.Triangulation.Internal.Dyadic (integerRatioToDouble)+import Moonlight.Triangulation.Internal.BoundaryCycle+ ( admitsSimpleCycleEdgeRelation+ , cyclePairsNonEmpty+ , firstNonCounterClockwiseTurn+ , unorderedPairs+ ) import Moonlight.Triangulation.Internal.ExactRational ( ExactArithmeticError (..) , ExactRational , exactDivide+ , exactRational+ , exactRationalBitWidth , exactRationalDenominator+ , exactRationalDenominatorBitWidth , exactRationalFromFiniteDouble+ , exactRationalIsZero , exactRationalNumerator , exactSignum )@@ -61,17 +101,151 @@ deriving stock (Eq, Ord, Show, Generic) deriving anyclass (NFData) +-- | An exact affine line @a*x + b*y + c = 0@ with a nonzero normal.+data ExactAffineLine =+ ExactAffineLine !ExactRational !ExactRational !ExactRational+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | The closed left half-plane of an oriented affine line. A point belongs+-- when the line evaluation is nonnegative.+newtype ExactClosedHalfPlane = ExactClosedHalfPlane ExactAffineLine+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | One polygon vertex together with the original affine line supporting its+-- incoming edge. Intersections therefore never reconstruct a line from+-- already-derived endpoints.+data ExactRetainedVertex =+ ExactRetainedVertex !ExactPoint !ExactAffineLine+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | A strict counter-clockwise convex polygon whose incoming edges retain+-- their original supporting equations.+newtype ExactRetainedPolygon =+ ExactRetainedPolygon (NonEmpty ExactRetainedVertex)+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | Typed refusals from affine-line and retained-polygon admission.+data ExactHalfPlaneError+ = ExactAffineLineZeroNormal+ !ExactRational+ !ExactRational+ !ExactRational+ | ExactRetainedPolygonTooFewVertices !Int+ | ExactRetainedPolygonNonConvexTurn !Int !Ordering+ | ExactRetainedPolygonSelfRelation !Int !Int !SegmentRelation+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | Dimensional disposition after exact closed-half-plane clipping.+data ExactClipDisposition+ = ExactClipFullDimensional !ExactRetainedPolygon+ | ExactClipLowerDimensional !(NonEmpty ExactPoint)+ | ExactClipEmpty+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | Typed obstructions from an affine intersection that should be unique or+-- from an impossible orientation reversal during convex descent.+data ExactClipError+ = ExactClipIntersection !ExactIntersectionError+ | ExactClipOrientationReversed !ExactRational+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | Structural work and exact-rational width observations for one angular+-- half-plane descent. Submitted half-planes exclude the domain; active+-- boundaries include domain edges admitted to the deque after+-- equal-direction coalescence; zero records a single-plane domain rejection+-- before deque construction.+-- Compatibility checks count deque endpoint predicates; intersections count+-- successful affine-line intersections actually evaluated. Widths describe+-- observed reduced values, not bounds.+data ExactClipReceipt = ExactClipReceipt+ { exactClipSubmittedHalfPlanes :: !Int+ , exactClipActiveBoundaries :: !Int+ , exactClipBoundaryCompatibilityChecks :: !Int+ , exactClipExactIntersections :: !Int+ , exactClipInputCoordinateBits :: !Int+ , exactClipMaximumAffineCoefficientBits :: !Int+ , exactClipPeakIntermediateCoordinateBits :: !Int+ , exactClipFinalCoordinateBits :: !Int+ , exactClipFinalDenominatorBits :: !Int+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++instance Semigroup ExactClipReceipt where+ left <> right =+ ExactClipReceipt+ { exactClipSubmittedHalfPlanes =+ exactClipSubmittedHalfPlanes left + exactClipSubmittedHalfPlanes right+ , exactClipActiveBoundaries =+ exactClipActiveBoundaries left + exactClipActiveBoundaries right+ , exactClipBoundaryCompatibilityChecks =+ exactClipBoundaryCompatibilityChecks left+ + exactClipBoundaryCompatibilityChecks right+ , exactClipExactIntersections =+ exactClipExactIntersections left + exactClipExactIntersections right+ , exactClipInputCoordinateBits =+ max (exactClipInputCoordinateBits left) (exactClipInputCoordinateBits right)+ , exactClipMaximumAffineCoefficientBits =+ max+ (exactClipMaximumAffineCoefficientBits left)+ (exactClipMaximumAffineCoefficientBits right)+ , exactClipPeakIntermediateCoordinateBits =+ max+ (exactClipPeakIntermediateCoordinateBits left)+ (exactClipPeakIntermediateCoordinateBits right)+ , exactClipFinalCoordinateBits =+ max (exactClipFinalCoordinateBits left) (exactClipFinalCoordinateBits right)+ , exactClipFinalDenominatorBits =+ max (exactClipFinalDenominatorBits left) (exactClipFinalDenominatorBits right)+ }++instance Monoid ExactClipReceipt where+ mempty = ExactClipReceipt 0 0 0 0 0 0 0 0 0+ -- | A strict exact segment whose endpoints are distinct. data ExactSegment = ExactSegment !ExactPoint !ExactPoint deriving stock (Eq, Ord, Show, Generic) deriving anyclass (NFData) +-- | An exact half-line with an admitted nonzero direction.+data ExactRay = ExactRay !ExactPoint !ExactVector+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)+ -- | Witness-bearing refusals from exact segment construction. data ExactGeometryError = ExactSegmentEndpointsCoincide !ExactPoint+ | ExactRayZeroDirection !ExactPoint deriving stock (Eq, Ord, Show, Generic) deriving anyclass (NFData) +-- | Admit a half-line, rejecting the only direction that cannot carry one.+exactRay+ :: ExactPoint+ -> ExactVector+ -> Either ExactGeometryError ExactRay+exactRay originPoint direction@(ExactVector directionX directionY)+ | exactRationalIsZero directionX && exactRationalIsZero directionY =+ Left (ExactRayZeroDirection originPoint)+ | otherwise = Right (ExactRay originPoint direction)++-- | Finite endpoint of an exact ray.+exactRayOrigin :: ExactRay -> ExactPoint+exactRayOrigin (ExactRay originPoint _) = originPoint+{-# INLINE exactRayOrigin #-}++-- | Nonzero direction of an exact ray.+exactRayDirection :: ExactRay -> ExactVector+exactRayDirection (ExactRay _ direction) = direction+{-# INLINE exactRayDirection #-}+ -- | Witness-bearing refusals from exact line intersection. data ExactIntersectionError = ExactIntersectionAbsent !SegmentRelation@@ -96,6 +270,795 @@ exactPointCross (ExactPoint ax ay) (ExactPoint bx by) = ax * by - ay * bx {-# INLINE exactPointCross #-} +-- | Admit an affine line, refusing precisely the zero normal. Coefficients+-- are retained verbatim because their source identity, rather than a chosen+-- scalar normalization, is the point of this carrier.+exactAffineLine+ :: ExactRational+ -> ExactRational+ -> ExactRational+ -> Either ExactHalfPlaneError ExactAffineLine+exactAffineLine coefficientX coefficientY constant+ | exactRationalIsZero coefficientX+ && exactRationalIsZero coefficientY =+ Left+ ( ExactAffineLineZeroNormal+ coefficientX+ coefficientY+ constant+ )+ | otherwise =+ Right (ExactAffineLine coefficientX coefficientY constant)++-- | Read the retained coefficients @(a,b,c)@ of @a*x+b*y+c=0@.+exactAffineLineCoefficients+ :: ExactAffineLine+ -> (ExactRational, ExactRational, ExactRational)+exactAffineLineCoefficients (ExactAffineLine coefficientX coefficientY constant) =+ (coefficientX, coefficientY, constant)+{-# INLINE exactAffineLineCoefficients #-}++-- | Reverse the oriented normal without changing the geometric line.+-- Admission already proves the normal nonzero, so negation is total.+oppositeExactAffineLine :: ExactAffineLine -> ExactAffineLine+oppositeExactAffineLine (ExactAffineLine coefficientX coefficientY constant) =+ ExactAffineLine (negate coefficientX) (negate coefficientY) (negate constant)+{-# INLINE oppositeExactAffineLine #-}++-- | Intersect two admitted affine lines directly from their retained source+-- coefficients. A parallel pair retains the exact determinant witness.+exactAffineLineIntersection+ :: ExactAffineLine+ -> ExactAffineLine+ -> Either ExactIntersectionError ExactPoint+exactAffineLineIntersection+ (ExactAffineLine firstX firstY firstConstant)+ (ExactAffineLine secondX secondY secondConstant) =+ -- Descend in homogeneous integer coordinates and normalize each published+ -- coordinate once. Generic Ratio arithmetic would normalize every product+ -- in Cramer's rule even though those intermediate rationals are invisible.+ let (firstXNumerator, firstXDenominator) = exactRationalParts firstX+ (firstYNumerator, firstYDenominator) = exactRationalParts firstY+ (firstConstantNumerator, firstConstantDenominator) =+ exactRationalParts firstConstant+ (secondXNumerator, secondXDenominator) = exactRationalParts secondX+ (secondYNumerator, secondYDenominator) = exactRationalParts secondY+ (secondConstantNumerator, secondConstantDenominator) =+ exactRationalParts secondConstant+ determinantNumerator =+ firstXNumerator+ * secondYNumerator+ * secondXDenominator+ * firstYDenominator+ - secondXNumerator+ * firstYNumerator+ * firstXDenominator+ * secondYDenominator+ xNumerator =+ ( firstYNumerator+ * secondConstantNumerator+ * secondYDenominator+ * firstConstantDenominator+ - secondYNumerator+ * firstConstantNumerator+ * firstYDenominator+ * secondConstantDenominator+ )+ * firstXDenominator+ * secondXDenominator+ yNumerator =+ ( firstConstantNumerator+ * secondXNumerator+ * secondConstantDenominator+ * firstXDenominator+ - secondConstantNumerator+ * firstXNumerator+ * firstConstantDenominator+ * secondXDenominator+ )+ * firstYDenominator+ * secondYDenominator+ coordinateDenominator =+ determinantNumerator+ * firstConstantDenominator+ * secondConstantDenominator+ in if determinantNumerator == 0+ then Left (ExactIntersectionParallelOrDegenerate 0)+ else do+ x <- admitCoordinate xNumerator coordinateDenominator+ y <- admitCoordinate yNumerator coordinateDenominator+ pure (ExactPoint x y)+ where+ admitCoordinate numerator denominator =+ case exactRational numerator denominator of+ Left arithmeticError ->+ Left (ExactIntersectionArithmetic arithmeticError)+ Right coordinate -> Right coordinate++exactRationalParts :: ExactRational -> (Integer, Integer)+exactRationalParts value =+ (exactRationalNumerator value, exactRationalDenominator value)+{-# INLINE exactRationalParts #-}++-- | Orient an admitted affine line so its nonnegative side is retained.+exactClosedHalfPlane :: ExactAffineLine -> ExactClosedHalfPlane+exactClosedHalfPlane = ExactClosedHalfPlane+{-# INLINE exactClosedHalfPlane #-}++-- | Construct the closed half-plane to the left of a directed edge.+exactClosedHalfPlaneFromDirectedEdge+ :: ExactPoint+ -> ExactPoint+ -> Either ExactHalfPlaneError ExactClosedHalfPlane+exactClosedHalfPlaneFromDirectedEdge+ (ExactPoint fromX fromY)+ (ExactPoint toX toY) =+ exactClosedHalfPlane+ <$> exactAffineLine+ (fromY - toY)+ (toX - fromX)+ (toY * fromX - toX * fromY)++-- | Read the original affine boundary retained by a closed half-plane.+exactClosedHalfPlaneLine :: ExactClosedHalfPlane -> ExactAffineLine+exactClosedHalfPlaneLine (ExactClosedHalfPlane line) = line+{-# INLINE exactClosedHalfPlaneLine #-}++-- | Classify a point against a closed half-plane. 'GT' is strict interior,+-- 'EQ' lies on the boundary, and 'LT' is exterior.+classifyExactPoint :: ExactClosedHalfPlane -> ExactPoint -> Ordering+classifyExactPoint+ (ExactClosedHalfPlane (ExactAffineLine coefficientX coefficientY constant))+ (ExactPoint x y) =+ -- The common denominator is strictly positive, so its unnormalized integer+ -- numerator is already the authoritative sign witness.+ let (coefficientXNumerator, coefficientXDenominator) =+ exactRationalParts coefficientX+ (coefficientYNumerator, coefficientYDenominator) =+ exactRationalParts coefficientY+ (constantNumerator, constantDenominator) = exactRationalParts constant+ (xNumerator, xDenominator) = exactRationalParts x+ (yNumerator, yDenominator) = exactRationalParts y+ evaluationNumerator =+ coefficientXNumerator+ * xNumerator+ * coefficientYDenominator+ * yDenominator+ * constantDenominator+ + coefficientYNumerator+ * yNumerator+ * coefficientXDenominator+ * xDenominator+ * constantDenominator+ + constantNumerator+ * coefficientXDenominator+ * xDenominator+ * coefficientYDenominator+ * yDenominator+ in compare evaluationNumerator 0+{-# INLINE classifyExactPoint #-}++-- | Admit strict counter-clockwise convex points and retain the original+-- supporting line of every incoming edge.+exactRetainedPolygon+ :: NonEmpty ExactPoint+ -> Either ExactHalfPlaneError ExactRetainedPolygon+exactRetainedPolygon points+ | NonEmpty.length points < 3 =+ Left (ExactRetainedPolygonTooFewVertices (NonEmpty.length points))+ | otherwise =+ case firstNonCounterClockwiseTurn exactOrient2d points of+ Just (index, turn) ->+ Left (ExactRetainedPolygonNonConvexTurn index turn)+ Nothing -> do+ validateSimpleRetainedCycle points+ ExactRetainedPolygon+ <$> traverse retainIncomingLine (cyclePairsNonEmpty points)+ where+ retainIncomingLine (from, to) = do+ halfPlane <- exactClosedHalfPlaneFromDirectedEdge from to+ pure (ExactRetainedVertex to (exactClosedHalfPlaneLine halfPlane))++validateSimpleRetainedCycle+ :: NonEmpty ExactPoint+ -> Either ExactHalfPlaneError ()+validateSimpleRetainedCycle points =+ case+ [ (leftIndex, rightIndex, relation)+ | ( (leftIndex, (leftFrom, leftTo))+ , (rightIndex, (rightFrom, rightTo))+ ) <- unorderedPairs indexedEdges+ , let relation =+ exactSegmentRelation+ leftFrom+ leftTo+ rightFrom+ rightTo+ , not+ ( admitsSimpleCycleEdgeRelation+ segmentCount+ leftIndex+ rightIndex+ relation+ )+ ] of+ (leftIndex, rightIndex, relation) : _ ->+ Left+ ( ExactRetainedPolygonSelfRelation+ leftIndex+ rightIndex+ relation+ )+ [] -> Right ()+ where+ indexedEdges = zip [0 :: Int ..] (NonEmpty.toList (cyclePairsNonEmpty points))+ segmentCount = NonEmpty.length points++-- | Project the retained-edge carrier to its authoritative point cycle.+exactRetainedPolygonPoints :: ExactRetainedPolygon -> NonEmpty ExactPoint+exactRetainedPolygonPoints (ExactRetainedPolygon vertices) =+ fmap retainedVertexPoint vertices++-- | Intersect a retained convex polygon with exact closed half-planes.+-- Boundaries descend in exact angular order through one immutable deque;+-- every published edge retains one submitted source line, and derived+-- endpoints never become line coefficients.+exactClipRetainedPolygon+ :: ExactRetainedPolygon+ -> [ExactClosedHalfPlane]+ -> Either ExactClipError (ExactClipDisposition, ExactClipReceipt)+exactClipRetainedPolygon polygon halfPlanes = do+ let inputPoints = exactRetainedPolygonPoints polygon+ inputBits = maximumPointBitWidth inputPoints+ domainHalfPlanes = retainedPolygonHalfPlanes polygon+ allHalfPlanes = domainHalfPlanes <> halfPlanes+ initialReceipt =+ ExactClipReceipt+ { exactClipSubmittedHalfPlanes = length halfPlanes+ , exactClipActiveBoundaries =+ if null halfPlanes then NonEmpty.length inputPoints else 0+ , exactClipBoundaryCompatibilityChecks = 0+ , exactClipExactIntersections = 0+ , exactClipInputCoordinateBits = inputBits+ , exactClipMaximumAffineCoefficientBits =+ List.foldl'+ (\bits -> max bits . affineLineBitWidth . exactClosedHalfPlaneLine)+ 0+ allHalfPlanes+ , exactClipPeakIntermediateCoordinateBits = inputBits+ , exactClipFinalCoordinateBits = inputBits+ , exactClipFinalDenominatorBits =+ maximumPointDenominatorBitWidth inputPoints+ }+ if null halfPlanes+ then pure (ExactClipFullDimensional polygon, initialReceipt)+ else case List.find (`excludesExactDomain` inputPoints) halfPlanes of+ Just _ ->+ pure+ ( ExactClipEmpty+ , initialReceipt+ { exactClipFinalCoordinateBits = 0+ , exactClipFinalDenominatorBits = 0+ }+ )+ Nothing -> do+ let orderedBoundaries =+ coalesceAngularBoundaries (fmap angularBoundary allHalfPlanes)+ descentReceipt =+ initialReceipt+ { exactClipActiveBoundaries = length orderedBoundaries+ }+ (openDeque, descendedReceipt) <-+ foldM insertAngularBoundary (Sequence.empty, descentReceipt) orderedBoundaries+ (closedDeque, accumulatedReceipt) <- closeAngularBoundaryDeque openDeque descendedReceipt+ (finalState, finalizedReceipt) <-+ angularBoundaryDequeState orderedBoundaries closedDeque accumulatedReceipt+ let disposition = exactClipStateDisposition finalState+ finalPoints = exactClipDispositionPoints disposition+ finalReceipt =+ finalizedReceipt+ { exactClipFinalCoordinateBits = maybe 0 maximumPointBitWidth finalPoints+ , exactClipFinalDenominatorBits =+ maybe 0 maximumPointDenominatorBitWidth finalPoints+ }+ pure (disposition, finalReceipt)++-- | A linear functional attains its maximum over a convex polygon at a+-- vertex. One half-plane whose closed side contains no domain vertex is+-- therefore an exact emptiness certificate; recognizing it before angular+-- descent avoids sorting a section that has no global point to glue.+excludesExactDomain+ :: ExactClosedHalfPlane+ -> NonEmpty ExactPoint+ -> Bool+excludesExactDomain halfPlane =+ all ((== LT) . classifyExactPoint halfPlane)++-- | One original source boundary together with its exact counter-clockwise+-- direction. Equal-direction gluing retains one of these values verbatim.+data ExactAngularBoundary = ExactAngularBoundary+ { angularBoundaryLine :: !ExactAffineLine+ , angularBoundaryDirection :: !ExactVector+ }++angularBoundary :: ExactClosedHalfPlane -> ExactAngularBoundary+angularBoundary halfPlane =+ let line@(ExactAffineLine coefficientX coefficientY _) =+ exactClosedHalfPlaneLine halfPlane+ in ExactAngularBoundary+ { angularBoundaryLine = line+ , angularBoundaryDirection = ExactVector coefficientY (negate coefficientX)+ }++angularBoundaryHalfPlane :: ExactAngularBoundary -> ExactClosedHalfPlane+angularBoundaryHalfPlane = exactClosedHalfPlane . angularBoundaryLine+{-# INLINE angularBoundaryHalfPlane #-}++retainedPolygonHalfPlanes :: ExactRetainedPolygon -> [ExactClosedHalfPlane]+retainedPolygonHalfPlanes (ExactRetainedPolygon vertices) =+ fmap+ (exactClosedHalfPlane . retainedVertexIncomingLine)+ (NonEmpty.toList vertices)++-- | Descent over one angular stalk. A stricter parallel boundary replaces a+-- weaker one; coincident boundaries retain the least original source line so+-- input permutations cannot alter the glued carrier.+coalesceAngularBoundaries :: [ExactAngularBoundary] -> [ExactAngularBoundary]+coalesceAngularBoundaries =+ fmap strongestAngularBoundary+ . mapMaybe NonEmpty.nonEmpty+ . List.groupBy sameBoundaryDirection+ . List.sortBy compareAngularBoundaries+ where+ sameBoundaryDirection left right =+ compareExactVectorAngle+ (angularBoundaryDirection left)+ (angularBoundaryDirection right)+ == EQ++compareAngularBoundaries :: ExactAngularBoundary -> ExactAngularBoundary -> Ordering+compareAngularBoundaries left right =+ case+ compareExactVectorAngle+ (angularBoundaryDirection left)+ (angularBoundaryDirection right) of+ EQ -> compare (angularBoundaryLine left) (angularBoundaryLine right)+ ordering -> ordering++strongestAngularBoundary :: NonEmpty ExactAngularBoundary -> ExactAngularBoundary+strongestAngularBoundary (initial :| remaining) =+ List.foldl' chooseStrongerAngularBoundary initial remaining++chooseStrongerAngularBoundary+ :: ExactAngularBoundary+ -> ExactAngularBoundary+ -> ExactAngularBoundary+chooseStrongerAngularBoundary selected candidate =+ case compareParallelBoundaryStrength selected candidate of+ LT -> candidate+ GT -> selected+ EQ ->+ if angularBoundaryLine candidate < angularBoundaryLine selected+ then candidate+ else selected++-- | Compare same-direction boundaries. 'LT' means the right boundary is+-- stricter, 'GT' means the left is stricter, and 'EQ' means the closed+-- half-planes coincide. Cross multiplication avoids inventing a normalized+-- replacement line.+compareParallelBoundaryStrength+ :: ExactAngularBoundary+ -> ExactAngularBoundary+ -> Ordering+compareParallelBoundaryStrength left right =+ let ExactAffineLine leftX leftY leftConstant = angularBoundaryLine left+ ExactAffineLine rightX rightY rightConstant = angularBoundaryLine right+ compareScaled leftScale rightScale scaleSign =+ let raw = compare (rightConstant * leftScale) (rightScale * leftConstant)+ in if exactSignum scaleSign == LT then reverseOrdering raw else raw+ in if exactRationalIsZero leftX+ then compareScaled leftY rightY leftY+ else compareScaled leftX rightX leftX++reverseOrdering :: Ordering -> Ordering+reverseOrdering LT = GT+reverseOrdering EQ = EQ+reverseOrdering GT = LT++-- | One active source boundary and its cached intersection with the preceding+-- deque boundary. Every adjacency point is derived once and reused by both+-- compatibility trims and final publication.+data ExactAngularBoundaryNode = ExactAngularBoundaryNode+ { angularNodeBoundary :: !ExactAngularBoundary+ , angularNodePreviousIntersection :: !(Maybe ExactPoint)+ }++type ExactAngularBoundaryDeque = Seq ExactAngularBoundaryNode++insertAngularBoundary+ :: (ExactAngularBoundaryDeque, ExactClipReceipt)+ -> ExactAngularBoundary+ -> Either ExactClipError (ExactAngularBoundaryDeque, ExactClipReceipt)+insertAngularBoundary (boundaries, receipt) incoming = do+ (backTrimmed, backReceipt) <- trimAngularBack incoming boundaries receipt+ (frontTrimmed, frontReceipt) <- trimAngularFront incoming backTrimmed backReceipt+ appendAngularBoundary incoming frontTrimmed frontReceipt++appendAngularBoundary+ :: ExactAngularBoundary+ -> ExactAngularBoundaryDeque+ -> ExactClipReceipt+ -> Either ExactClipError (ExactAngularBoundaryDeque, ExactClipReceipt)+appendAngularBoundary incoming boundaries receipt =+ case Sequence.viewr boundaries of+ EmptyR ->+ Right+ ( Sequence.singleton (ExactAngularBoundaryNode incoming Nothing)+ , receipt+ )+ _ :> finalNode -> do+ (intersection, observedReceipt) <-+ observeAdjacentIntersection+ (angularNodeBoundary finalNode)+ incoming+ receipt+ pure+ ( boundaries+ |> ExactAngularBoundaryNode incoming intersection+ , observedReceipt+ )++trimAngularBack+ :: ExactAngularBoundary+ -> ExactAngularBoundaryDeque+ -> ExactClipReceipt+ -> Either ExactClipError (ExactAngularBoundaryDeque, ExactClipReceipt)+trimAngularBack incoming boundaries receipt =+ case Sequence.viewr boundaries of+ EmptyR -> Right (boundaries, receipt)+ remaining :> finalNode ->+ case Sequence.viewr remaining of+ EmptyR -> Right (boundaries, receipt)+ _ :> _ ->+ case angularNodePreviousIntersection finalNode of+ Nothing -> Right (boundaries, receipt)+ Just point ->+ let checkedReceipt = observeBoundaryCompatibility receipt+ in if classifyExactPoint (angularBoundaryHalfPlane incoming) point == LT+ then trimAngularBack incoming remaining checkedReceipt+ else Right (boundaries, checkedReceipt)++trimAngularFront+ :: ExactAngularBoundary+ -> ExactAngularBoundaryDeque+ -> ExactClipReceipt+ -> Either ExactClipError (ExactAngularBoundaryDeque, ExactClipReceipt)+trimAngularFront incoming boundaries receipt =+ case Sequence.viewl boundaries of+ EmptyL -> Right (boundaries, receipt)+ _ :< remaining ->+ case Sequence.viewl remaining of+ EmptyL -> Right (boundaries, receipt)+ secondNode :< suffix ->+ case angularNodePreviousIntersection secondNode of+ Nothing -> Right (boundaries, receipt)+ Just point ->+ let checkedReceipt = observeBoundaryCompatibility receipt+ in if classifyExactPoint (angularBoundaryHalfPlane incoming) point == LT+ then+ trimAngularFront+ incoming+ ( secondNode+ { angularNodePreviousIntersection = Nothing+ }+ Sequence.<| suffix+ )+ checkedReceipt+ else Right (boundaries, checkedReceipt)++closeAngularBoundaryDeque+ :: ExactAngularBoundaryDeque+ -> ExactClipReceipt+ -> Either ExactClipError (ExactAngularBoundaryDeque, ExactClipReceipt)+closeAngularBoundaryDeque boundaries receipt = do+ (backClosed, backReceipt) <- closeAngularBack boundaries receipt+ (frontClosed, frontReceipt) <- closeAngularFront backClosed backReceipt+ if Sequence.length frontClosed == Sequence.length boundaries+ then Right (frontClosed, frontReceipt)+ else closeAngularBoundaryDeque frontClosed frontReceipt++closeAngularBack+ :: ExactAngularBoundaryDeque+ -> ExactClipReceipt+ -> Either ExactClipError (ExactAngularBoundaryDeque, ExactClipReceipt)+closeAngularBack boundaries receipt =+ case (Sequence.viewl boundaries, Sequence.viewr boundaries) of+ (firstNode :< _, remaining :> finalNode) ->+ case Sequence.viewr remaining of+ _ :> _ ->+ case angularNodePreviousIntersection finalNode of+ Nothing -> Right (boundaries, receipt)+ Just point ->+ let checkedReceipt = observeBoundaryCompatibility receipt+ in if+ classifyExactPoint+ (angularBoundaryHalfPlane (angularNodeBoundary firstNode))+ point+ == LT+ then closeAngularBack remaining checkedReceipt+ else Right (boundaries, checkedReceipt)+ EmptyR -> Right (boundaries, receipt)+ _ -> Right (boundaries, receipt)++closeAngularFront+ :: ExactAngularBoundaryDeque+ -> ExactClipReceipt+ -> Either ExactClipError (ExactAngularBoundaryDeque, ExactClipReceipt)+closeAngularFront boundaries receipt =+ case (Sequence.viewl boundaries, Sequence.viewr boundaries) of+ (_ :< remaining, _ :> finalNode) ->+ case Sequence.viewl remaining of+ secondNode :< suffix ->+ case angularNodePreviousIntersection secondNode of+ Nothing -> Right (boundaries, receipt)+ Just point ->+ let checkedReceipt = observeBoundaryCompatibility receipt+ in if+ classifyExactPoint+ (angularBoundaryHalfPlane (angularNodeBoundary finalNode))+ point+ == LT+ then+ closeAngularFront+ ( secondNode+ { angularNodePreviousIntersection = Nothing+ }+ Sequence.<| suffix+ )+ checkedReceipt+ else Right (boundaries, checkedReceipt)+ EmptyL -> Right (boundaries, receipt)+ _ -> Right (boundaries, receipt)++observeAdjacentIntersection+ :: ExactAngularBoundary+ -> ExactAngularBoundary+ -> ExactClipReceipt+ -> Either ExactClipError (Maybe ExactPoint, ExactClipReceipt)+observeAdjacentIntersection left right receipt =+ let leftLine = angularBoundaryLine left+ rightLine = angularBoundaryLine right+ in case exactAffineLineIntersection leftLine rightLine of+ Left (ExactIntersectionParallelOrDegenerate _) ->+ Right (Nothing, receipt)+ Left obstruction -> Left (ExactClipIntersection obstruction)+ Right point ->+ pure+ ( Just point+ , receipt+ { exactClipExactIntersections =+ exactClipExactIntersections receipt + 1+ , exactClipPeakIntermediateCoordinateBits =+ max+ (exactClipPeakIntermediateCoordinateBits receipt)+ (exactPointBitWidth point)+ }+ )++observeBoundaryCompatibility :: ExactClipReceipt -> ExactClipReceipt+observeBoundaryCompatibility receipt =+ receipt+ { exactClipBoundaryCompatibilityChecks =+ exactClipBoundaryCompatibilityChecks receipt + 1+ }++angularBoundaryDequeState+ :: [ExactAngularBoundary]+ -> ExactAngularBoundaryDeque+ -> ExactClipReceipt+ -> Either ExactClipError (ExactClipState, ExactClipReceipt)+angularBoundaryDequeState allBoundaries boundaries receipt = do+ let nodeList = foldr (:) [] boundaries+ linearVertices =+ mapMaybe+ retainedLinearVertex+ (zip nodeList (drop 1 nodeList))+ (closingVertices, finalizedReceipt) <-+ case (Sequence.viewl boundaries, Sequence.viewr boundaries) of+ (firstNode :< _, _ :> finalNode) -> do+ (intersection, observedReceipt) <-+ observeAdjacentIntersection+ (angularNodeBoundary finalNode)+ (angularNodeBoundary firstNode)+ receipt+ pure+ ( maybe+ []+ (\point ->+ [ ExactRetainedVertex+ point+ (angularBoundaryLine (angularNodeBoundary finalNode))+ ])+ intersection+ , observedReceipt+ )+ _ -> Right ([], receipt)+ state <-+ retainedVerticesToClipState+ (normalizeRetainedVertices (linearVertices <> closingVertices))+ pure (validateLowerDimensionalState allBoundaries state, finalizedReceipt)+ where+ retainedLinearVertex (node, successor) =+ fmap+ (\point ->+ ExactRetainedVertex+ point+ (angularBoundaryLine (angularNodeBoundary node)))+ (angularNodePreviousIntersection successor)++validateLowerDimensionalState+ :: [ExactAngularBoundary]+ -> ExactClipState+ -> ExactClipState+validateLowerDimensionalState boundaries state =+ case state of+ ExactClipStateSegment from to ->+ if all (containsBoth from to) boundaries+ then state+ else ExactClipStateEmpty+ ExactClipStatePoint point ->+ if all+ (\boundary ->+ classifyExactPoint (angularBoundaryHalfPlane boundary) point /= LT)+ boundaries+ then state+ else ExactClipStateEmpty+ other -> other+ where+ containsBoth from to boundary =+ let halfPlane = angularBoundaryHalfPlane boundary+ in classifyExactPoint halfPlane from /= LT+ && classifyExactPoint halfPlane to /= LT++data ExactClipState+ = ExactClipStatePolygon !ExactRetainedPolygon+ | ExactClipStateSegment !ExactPoint !ExactPoint+ | ExactClipStatePoint !ExactPoint+ | ExactClipStateEmpty+exactSegmentClipState+ :: ExactPoint+ -> ExactPoint+ -> ExactClipState+exactSegmentClipState from to+ | from == to = ExactClipStatePoint from+ | otherwise = ExactClipStateSegment from to++retainedVerticesToClipState+ :: [ExactRetainedVertex]+ -> Either ExactClipError ExactClipState+retainedVerticesToClipState [] = Right ExactClipStateEmpty+retainedVerticesToClipState [vertex] =+ Right (ExactClipStatePoint (retainedVertexPoint vertex))+retainedVerticesToClipState [fromVertex, toVertex] =+ Right+ ( exactSegmentClipState+ (retainedVertexPoint fromVertex)+ (retainedVertexPoint toVertex)+ )+retainedVerticesToClipState (firstVertex : secondVertex : remaining) =+ let retained = firstVertex :| (secondVertex : remaining)+ twiceArea = retainedVerticesTwiceArea retained+ in case exactSignum twiceArea of+ GT -> Right (ExactClipStatePolygon (ExactRetainedPolygon retained))+ LT -> Left (ExactClipOrientationReversed twiceArea)+ EQ -> Right (lowerDimensionalClipState retained)++lowerDimensionalClipState+ :: NonEmpty ExactRetainedVertex+ -> ExactClipState+lowerDimensionalClipState vertices =+ let points = fmap retainedVertexPoint vertices+ initialPoint :| remainingPoints = points+ leastPoint = List.foldl' min initialPoint remainingPoints+ greatestPoint = List.foldl' max initialPoint remainingPoints+ in if leastPoint == greatestPoint+ then ExactClipStatePoint leastPoint+ else ExactClipStateSegment leastPoint greatestPoint++retainedVerticesTwiceArea+ :: NonEmpty ExactRetainedVertex+ -> ExactRational+retainedVerticesTwiceArea =+ List.foldl'+ (\area (from, to) ->+ area+ + exactPointCross+ (retainedVertexPoint from)+ (retainedVertexPoint to))+ 0+ . cyclePairsNonEmpty++normalizeRetainedVertices+ :: [ExactRetainedVertex]+ -> [ExactRetainedVertex]+normalizeRetainedVertices = stripClosingDuplicate . deduplicateAdjacent+ where+ deduplicateAdjacent [] = []+ deduplicateAdjacent (firstVertex : remaining) =+ firstVertex : deduplicateAfter firstVertex remaining++ deduplicateAfter _ [] = []+ deduplicateAfter previous (candidate : remaining)+ | retainedVertexPoint previous == retainedVertexPoint candidate =+ deduplicateAfter previous remaining+ | otherwise = candidate : deduplicateAfter candidate remaining++ stripClosingDuplicate vertices@(firstVertex : remainingVertices) =+ case reverse remainingVertices of+ finalVertex : reversedInterior+ | retainedVertexPoint finalVertex == retainedVertexPoint firstVertex ->+ finalVertex : reverse reversedInterior+ _ -> vertices+ stripClosingDuplicate [] = []++exactClipStateDisposition :: ExactClipState -> ExactClipDisposition+exactClipStateDisposition state =+ case state of+ ExactClipStatePolygon polygon -> ExactClipFullDimensional polygon+ ExactClipStateSegment from to ->+ ExactClipLowerDimensional (from :| [to])+ ExactClipStatePoint point -> ExactClipLowerDimensional (point :| [])+ ExactClipStateEmpty -> ExactClipEmpty++exactClipDispositionPoints+ :: ExactClipDisposition+ -> Maybe (NonEmpty ExactPoint)+exactClipDispositionPoints disposition =+ case disposition of+ ExactClipFullDimensional polygon ->+ Just (exactRetainedPolygonPoints polygon)+ ExactClipLowerDimensional points -> Just points+ ExactClipEmpty -> Nothing++retainedVertexPoint :: ExactRetainedVertex -> ExactPoint+retainedVertexPoint (ExactRetainedVertex point _) = point+{-# INLINE retainedVertexPoint #-}++retainedVertexIncomingLine :: ExactRetainedVertex -> ExactAffineLine+retainedVertexIncomingLine (ExactRetainedVertex _ line) = line+{-# INLINE retainedVertexIncomingLine #-}++affineLineBitWidth :: ExactAffineLine -> Int+affineLineBitWidth (ExactAffineLine coefficientX coefficientY constant) =+ max+ (exactRationalBitWidth coefficientX)+ (max (exactRationalBitWidth coefficientY) (exactRationalBitWidth constant))++maximumPointBitWidth :: NonEmpty ExactPoint -> Int+maximumPointBitWidth =+ List.foldl'+ (\maximumBits (ExactPoint x y) ->+ max maximumBits (max (exactRationalBitWidth x) (exactRationalBitWidth y)))+ 0++-- | Maximum reduced numerator-or-denominator width of either coordinate.+exactPointBitWidth :: ExactPoint -> Int+exactPointBitWidth (ExactPoint x y) =+ max (exactRationalBitWidth x) (exactRationalBitWidth y)+{-# INLINE exactPointBitWidth #-}++maximumPointDenominatorBitWidth :: NonEmpty ExactPoint -> Int+maximumPointDenominatorBitWidth =+ List.foldl'+ (\maximumBits (ExactPoint x y) ->+ max+ maximumBits+ ( max+ (exactRationalDenominatorBitWidth x)+ (exactRationalDenominatorBitWidth y)+ ))+ 0+ -- | Construct an exact segment, refusing coincident endpoints with their -- shared point as the witness. exactSegment@@ -244,7 +1207,7 @@ compareExactVectorAngle left right = case compare (vectorHalf left) (vectorHalf right) of EQ ->- case exactSignum (exactCross left right) of+ case exactCrossSign left right of GT -> LT LT -> GT EQ -> EQ@@ -255,6 +1218,27 @@ | exactSignum y == EQ && exactSignum x /= LT = False | otherwise = True {-# INLINE compareExactVectorAngle #-}++exactCrossSign :: ExactVector -> ExactVector -> Ordering+exactCrossSign (ExactVector leftX leftY) (ExactVector rightX rightY) =+ -- Reduced rational denominators are positive; compare the two products in a+ -- common integer scale without constructing and normalizing their ratios.+ let (leftXNumerator, leftXDenominator) = exactRationalParts leftX+ (leftYNumerator, leftYDenominator) = exactRationalParts leftY+ (rightXNumerator, rightXDenominator) = exactRationalParts rightX+ (rightYNumerator, rightYDenominator) = exactRationalParts rightY+ in compare+ ( leftXNumerator+ * rightYNumerator+ * leftYDenominator+ * rightXDenominator+ )+ ( leftYNumerator+ * rightXNumerator+ * leftXDenominator+ * rightYDenominator+ )+{-# INLINE exactCrossSign #-} scaleExactVector :: ExactRational -> ExactVector -> ExactVector scaleExactVector scale (ExactVector x y) =
src-dcel/Moonlight/Triangulation/Handles/HandleDefs.hs view
@@ -1,8 +1,7 @@--- | Opaque resident identifiers and lawful orientation operations.------ Identifiers can only be obtained from a triangulation. Their numeric--- projections support serialization and diagnostics without allowing a caller--- to manufacture a handle that belongs to no mesh.+-- | Opaque compact resident indices and lawful orientation operations. The+-- indices do not encode mesh ownership: use "Moonlight.Triangulation.Handles.Scoped"+-- for local traversal or "Moonlight.Triangulation.Handles.Dynamic" when a handle+-- must escape its mesh scope. module Moonlight.Triangulation.Handles.HandleDefs ( VertexId , unVertexId
+ src-dcel/Moonlight/Triangulation/Handles/Scoped.hs view
@@ -0,0 +1,276 @@+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RoleAnnotations #-}++-- | Zero-cost mesh-scoped identifiers. A scope is introduced only by+-- 'withScopedTriangulation', so an identifier from one mesh cannot be supplied+-- to observations of another mesh. Use owning handles when a handle must+-- escape the continuation.+module Moonlight.Triangulation.Handles.Scoped+ ( ScopedTriangulation+ , ScopedVertex+ , ScopedDirectedEdge+ , ScopedUndirectedEdge+ , ScopedFace+ , withScopedTriangulation+ , scopedVertices+ , scopedDirectedEdges+ , scopedUndirectedEdges+ , scopedFaces+ , scopedInnerFaces+ , scopedOuterFace+ , scopedVertexPoint+ , scopedVertexData+ , scopedDirectedEdgeData+ , scopedUndirectedEdgeData+ , scopedFaceData+ , scopedVertexOutEdge+ , scopedAdjacentEdge+ , scopedOrigin+ , scopedDestination+ , scopedReverseEdge+ , scopedAsUndirected+ , scopedNext+ , scopedPrevious+ , scopedIncidentFace+ , scopedIsOuterDirectedEdge+ , scopedIsBoundaryEdge+ , scopedIsConstraintEdge+ , scopedUndirectedEndpoints+ , scopedFaceDirectedEdges+ , scopedFaceVertices+ , scopedVertexOutgoingEdges+ ) where++import qualified Moonlight.Triangulation.Dcel as Dcel+import Moonlight.Triangulation.Handles.HandleDefs+ ( DirectedEdgeId+ , FaceId+ , UndirectedEdgeId+ , VertexId+ , asUndirected+ , reverseEdge+ )+import qualified Moonlight.Triangulation.Handles.Iterators.FixedIterators as Fixed+import Moonlight.Triangulation.Types+ ( Point+ , Triangulation+ )++type role ScopedTriangulation nominal nominal representational representational representational representational+newtype ScopedTriangulation scope mode vertex directed undirected face =+ ScopedTriangulation (Triangulation mode vertex directed undirected face)++type role ScopedVertex nominal+newtype ScopedVertex scope = ScopedVertex VertexId+ deriving stock (Eq, Ord, Show)++type role ScopedDirectedEdge nominal+newtype ScopedDirectedEdge scope = ScopedDirectedEdge DirectedEdgeId+ deriving stock (Eq, Ord, Show)++type role ScopedUndirectedEdge nominal+newtype ScopedUndirectedEdge scope = ScopedUndirectedEdge UndirectedEdgeId+ deriving stock (Eq, Ord, Show)++type role ScopedFace nominal+newtype ScopedFace scope = ScopedFace FaceId+ deriving stock (Eq, Ord, Show)++-- | Introduce one nominal scope for all identifiers derived from this mesh.+withScopedTriangulation+ :: Triangulation mode vertex directed undirected face+ -> (forall scope. ScopedTriangulation scope mode vertex directed undirected face -> result)+ -> result+withScopedTriangulation triangulation use = use (ScopedTriangulation triangulation)+{-# INLINE withScopedTriangulation #-}++scopedVertices+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> [ScopedVertex scope]+scopedVertices (ScopedTriangulation triangulation) =+ fmap ScopedVertex (Fixed.vertices triangulation)++scopedDirectedEdges+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> [ScopedDirectedEdge scope]+scopedDirectedEdges (ScopedTriangulation triangulation) =+ fmap ScopedDirectedEdge (Fixed.directedEdges triangulation)++scopedUndirectedEdges+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> [ScopedUndirectedEdge scope]+scopedUndirectedEdges (ScopedTriangulation triangulation) =+ fmap ScopedUndirectedEdge (Fixed.undirectedEdges triangulation)++scopedFaces+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> [ScopedFace scope]+scopedFaces (ScopedTriangulation triangulation) =+ fmap ScopedFace (Fixed.allFaces triangulation)++scopedInnerFaces+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> [ScopedFace scope]+scopedInnerFaces (ScopedTriangulation triangulation) =+ fmap ScopedFace (Fixed.innerFaces triangulation)++scopedOuterFace+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedFace scope+scopedOuterFace _ = ScopedFace Dcel.outerFace+{-# INLINE scopedOuterFace #-}++scopedVertexPoint+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedVertex scope+ -> Point+scopedVertexPoint (ScopedTriangulation triangulation) (ScopedVertex vertex) =+ Dcel.vertexPoint triangulation vertex+{-# INLINE scopedVertexPoint #-}++scopedVertexData+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedVertex scope+ -> vertex+scopedVertexData (ScopedTriangulation triangulation) (ScopedVertex vertex) =+ Dcel.vertexData triangulation vertex+{-# INLINE scopedVertexData #-}++scopedDirectedEdgeData+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedDirectedEdge scope+ -> directed+scopedDirectedEdgeData (ScopedTriangulation triangulation) (ScopedDirectedEdge edge) =+ Dcel.directedEdgeData triangulation edge+{-# INLINE scopedDirectedEdgeData #-}++scopedUndirectedEdgeData+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedUndirectedEdge scope+ -> undirected+scopedUndirectedEdgeData (ScopedTriangulation triangulation) (ScopedUndirectedEdge edge) =+ Dcel.undirectedEdgeData triangulation edge+{-# INLINE scopedUndirectedEdgeData #-}++scopedFaceData+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedFace scope+ -> face+scopedFaceData (ScopedTriangulation triangulation) (ScopedFace face) =+ Dcel.faceData triangulation face+{-# INLINE scopedFaceData #-}++scopedVertexOutEdge+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedVertex scope+ -> Maybe (ScopedDirectedEdge scope)+scopedVertexOutEdge (ScopedTriangulation triangulation) (ScopedVertex vertex) =+ ScopedDirectedEdge <$> Dcel.vertexOutEdge triangulation vertex++scopedAdjacentEdge+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedFace scope+ -> Maybe (ScopedDirectedEdge scope)+scopedAdjacentEdge (ScopedTriangulation triangulation) (ScopedFace face) =+ ScopedDirectedEdge <$> Dcel.adjacentEdge triangulation face++scopedOrigin+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedDirectedEdge scope+ -> ScopedVertex scope+scopedOrigin (ScopedTriangulation triangulation) (ScopedDirectedEdge edge) =+ ScopedVertex (Dcel.origin triangulation edge)+{-# INLINE scopedOrigin #-}++scopedDestination+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedDirectedEdge scope+ -> ScopedVertex scope+scopedDestination (ScopedTriangulation triangulation) (ScopedDirectedEdge edge) =+ ScopedVertex (Dcel.destination triangulation edge)+{-# INLINE scopedDestination #-}++scopedReverseEdge :: ScopedDirectedEdge scope -> ScopedDirectedEdge scope+scopedReverseEdge (ScopedDirectedEdge edge) = ScopedDirectedEdge (reverseEdge edge)+{-# INLINE scopedReverseEdge #-}++scopedAsUndirected :: ScopedDirectedEdge scope -> ScopedUndirectedEdge scope+scopedAsUndirected (ScopedDirectedEdge edge) = ScopedUndirectedEdge (asUndirected edge)+{-# INLINE scopedAsUndirected #-}++scopedNext+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedDirectedEdge scope+ -> ScopedDirectedEdge scope+scopedNext (ScopedTriangulation triangulation) (ScopedDirectedEdge edge) =+ ScopedDirectedEdge (Dcel.next triangulation edge)+{-# INLINE scopedNext #-}++scopedPrevious+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedDirectedEdge scope+ -> ScopedDirectedEdge scope+scopedPrevious (ScopedTriangulation triangulation) (ScopedDirectedEdge edge) =+ ScopedDirectedEdge (Dcel.previous triangulation edge)+{-# INLINE scopedPrevious #-}++scopedIncidentFace+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedDirectedEdge scope+ -> ScopedFace scope+scopedIncidentFace (ScopedTriangulation triangulation) (ScopedDirectedEdge edge) =+ ScopedFace (Dcel.incidentFace triangulation edge)+{-# INLINE scopedIncidentFace #-}++scopedIsOuterDirectedEdge+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedDirectedEdge scope+ -> Bool+scopedIsOuterDirectedEdge (ScopedTriangulation triangulation) (ScopedDirectedEdge edge) =+ Dcel.isOuterDirectedEdge triangulation edge+{-# INLINE scopedIsOuterDirectedEdge #-}++scopedIsBoundaryEdge+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedUndirectedEdge scope+ -> Bool+scopedIsBoundaryEdge (ScopedTriangulation triangulation) (ScopedUndirectedEdge edge) =+ Dcel.isBoundaryEdge triangulation edge+{-# INLINE scopedIsBoundaryEdge #-}++scopedIsConstraintEdge+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedUndirectedEdge scope+ -> Bool+scopedIsConstraintEdge (ScopedTriangulation triangulation) (ScopedUndirectedEdge edge) =+ Dcel.isConstraintEdge triangulation edge+{-# INLINE scopedIsConstraintEdge #-}++scopedUndirectedEndpoints+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedUndirectedEdge scope+ -> (ScopedVertex scope, ScopedVertex scope)+scopedUndirectedEndpoints (ScopedTriangulation triangulation) (ScopedUndirectedEdge edge) =+ let (firstVertex, secondVertex) = Dcel.undirectedEndpoints triangulation edge+ in (ScopedVertex firstVertex, ScopedVertex secondVertex)++scopedFaceDirectedEdges+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedFace scope+ -> [ScopedDirectedEdge scope]+scopedFaceDirectedEdges (ScopedTriangulation triangulation) (ScopedFace face) =+ fmap ScopedDirectedEdge (Dcel.faceDirectedEdges triangulation face)++scopedFaceVertices+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedFace scope+ -> [ScopedVertex scope]+scopedFaceVertices (ScopedTriangulation triangulation) (ScopedFace face) =+ fmap ScopedVertex (Dcel.faceVertices triangulation face)++scopedVertexOutgoingEdges+ :: ScopedTriangulation scope mode vertex directed undirected face+ -> ScopedVertex scope+ -> [ScopedDirectedEdge scope]+scopedVertexOutgoingEdges (ScopedTriangulation triangulation) (ScopedVertex vertex) =+ fmap ScopedDirectedEdge (Dcel.vertexOutgoingEdges triangulation vertex)
src-dcel/Moonlight/Triangulation/Internal/BoundaryCycle.hs view
@@ -11,6 +11,8 @@ , cyclePairs , cyclePairsNonEmpty , cyclicTriples+ , firstNonCounterClockwiseTurn+ , admitsSimpleCycleEdgeRelation ) where import Data.List (tails)@@ -21,6 +23,9 @@ import Data.Map.Strict (Map) import qualified Data.Set as Set import Data.Set (Set)+import Moonlight.Triangulation.Internal.SegmentRelation+ ( SegmentRelation (..)+ ) -- | Consume every supplied oriented edge exactly once and split point contacts -- into vertex-simple cycles. The outgoing lists carry the caller's required@@ -253,3 +258,46 @@ values (second : remaining <> [initial]) _ -> []+{-# INLINE cyclicTriples #-}++-- | Locate the first turn that is not strictly counter-clockwise. Convex+-- boundary admissions share this one indexed obstruction descent.+firstNonCounterClockwiseTurn+ :: (value -> value -> value -> Ordering)+ -> NonEmpty value+ -> Maybe (Int, Ordering)+firstNonCounterClockwiseTurn orientation =+ List.find ((/= GT) . snd)+ . zip [0 ..]+ . fmap (\(previousValue, current, nextValue) -> orientation previousValue current nextValue)+ . cyclicTriples+ . NonEmpty.toList+{-# INLINE firstNonCounterClockwiseTurn #-}++-- | Whether one exact relation between two indexed cycle edges is compatible+-- with a simple closed boundary. Only adjacent edges may meet, and then only+-- at their shared endpoint. Producers remain free to obtain relation sections+-- through the sweep plan or direct pairwise descent; this is the single+-- admissibility policy glued over both representations.+admitsSimpleCycleEdgeRelation+ :: Int+ -> Int+ -> Int+ -> SegmentRelation+ -> Bool+admitsSimpleCycleEdgeRelation segmentCount leftIndex rightIndex relation =+ relation == SegmentsDisjoint+ || ( cycleEdgesAreAdjacent+ segmentCount leftIndex rightIndex+ && relation == SegmentsShareEndpoint+ )+{-# INLINE admitsSimpleCycleEdgeRelation #-}++cycleEdgesAreAdjacent :: Int -> Int -> Int -> Bool+cycleEdgesAreAdjacent segmentCount leftIndex rightIndex =+ upperIndex == lowerIndex + 1+ || (lowerIndex == 0 && upperIndex == segmentCount - 1)+ where+ lowerIndex = min leftIndex rightIndex+ upperIndex = max leftIndex rightIndex+{-# INLINE cycleEdgesAreAdjacent #-}
+ src-dcel/Moonlight/Triangulation/Internal/Tournament.hs view
@@ -0,0 +1,45 @@+-- | One deterministic balanced binary descent shared by every pure+-- associative schedule in the package. Leaves retain their source values;+-- interpreters own only their binary operation.+module Moonlight.Triangulation.Internal.Tournament+ ( TournamentPlan (..)+ , planTournament+ , interpretTournament+ ) where++import Data.List.NonEmpty (NonEmpty (..))++data TournamentPlan value+ = TournamentLeaf !value+ | TournamentNode !(TournamentPlan value) !(TournamentPlan value)++planTournament :: NonEmpty value -> TournamentPlan value+planTournament = descendTournament . fmap TournamentLeaf++interpretTournament+ :: (value -> value -> Either failure value)+ -> TournamentPlan value+ -> Either failure value+interpretTournament combine tournament =+ case tournament of+ TournamentLeaf value -> Right value+ TournamentNode left right -> do+ leftValue <- interpretTournament combine left+ rightValue <- interpretTournament combine right+ combine leftValue rightValue++descendTournament :: NonEmpty (TournamentPlan value) -> TournamentPlan value+descendTournament (single :| []) = single+descendTournament plans = descendTournament (pairTournamentRound plans)++pairTournamentRound+ :: NonEmpty (TournamentPlan value)+ -> NonEmpty (TournamentPlan value)+pairTournamentRound (left :| right : rest) =+ TournamentNode left right :| pairTournamentTail rest+pairTournamentRound (single :| []) = single :| []++pairTournamentTail :: [TournamentPlan value] -> [TournamentPlan value]+pairTournamentTail (left : right : rest) =+ TournamentNode left right : pairTournamentTail rest+pairTournamentTail rest = rest
src-dcel/Moonlight/Triangulation/Region.hs view
@@ -46,7 +46,8 @@ , exactSegmentEndpoints ) import Moonlight.Triangulation.Internal.BoundaryCycle- ( cyclePairs+ ( admitsSimpleCycleEdgeRelation+ , cyclePairs , rotateCycleLeast , simplifyBoundaryCycle )@@ -238,7 +239,13 @@ [ (leftIndex, rightIndex, relation) | ((ExactSweepSegmentId leftIndex, ExactSweepSegmentId rightIndex), relation) <- Map.toAscList (exactSegmentRelationMap plan)- , not (adjacentSegment segmentCount leftIndex rightIndex && relation == SegmentsShareEndpoint)+ , not+ ( admitsSimpleCycleEdgeRelation+ segmentCount+ leftIndex+ rightIndex+ relation+ ) ] of (leftIndex, rightIndex, relation) : _ -> Left (RegionLoopSelfRelation leftIndex rightIndex relation)@@ -256,10 +263,6 @@ first (const (RegionLoopDegenerate (NonEmpty.toList points))) (exactSegment from to)) (cyclePairs points)--adjacentSegment :: Int -> Int -> Int -> Bool-adjacentSegment count left right =- right == left + 1 || (left == 0 && right == count - 1) loopWinding :: ExactLoop -> Ordering loopWinding (ExactLoop points) =
src-dcel/Moonlight/Triangulation/Valuation.hs view
@@ -11,6 +11,7 @@ , eulerCharacteristicValue , ExactArea , exactAreaValue+ , polygonComponentArea , ExactLengthTerm , lengthCoefficient , squaredLength@@ -118,9 +119,21 @@ deriving stock (Eq, Ord, Show, Generic) deriving anyclass (NFData) +instance Semigroup ExactArea where+ ExactArea left <> ExactArea right = ExactArea (left + right)++instance Monoid ExactArea where+ mempty = ExactArea 0+ exactAreaValue :: ExactArea -> ExactRational exactAreaValue (ExactArea value) = value +-- | Exact unsigned area of one already-admitted polygon component. Winding+-- and hole containment were discharged by 'polygonComponent', so this+-- observation performs no second geometric validation.+polygonComponentArea :: PolygonComponent -> ExactArea+polygonComponentArea = ExactArea . (oneHalf *) . componentDoubleArea+ data ExactLengthTerm = ExactLengthTerm { lengthCoefficient :: !ExactRational , squaredLength :: !ExactRational@@ -195,7 +208,7 @@ componentBoundaries <- traverse componentBoundaryData components let boundaryCover = overlappingPredecessors componentBoundaryBounds componentBoundaries- hasPotentialBoundaryContacts = any (not . null . snd) boundaryCover+ hasPotentialBoundaryContacts = not (all (null . snd) boundaryCover) euler <- regionEuler boundaryCover boundaryAtoms <- normalizedRegionBoundaryAtoms
+ src-overlay/Moonlight/Triangulation/Embedding.hs view
@@ -0,0 +1,546 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}++-- | Bounded certification that projection of a declared exact arrangement to+-- binary64 preserves its local incidence and rotation data.+module Moonlight.Triangulation.Embedding+ ( DraftId (..)+ , DraftVertexId+ , DraftSegmentId+ , DraftSourceId+ , DraftIncidence (..)+ , DraftNeighborhood (..)+ , ExactArrangementDraft (..)+ , DraftReference (..)+ , OverlayEmbeddingObstruction (..)+ , EmbeddingObligation (..)+ , EmbeddingResidual+ , residualUndischargedObligations+ , milestoneOneResidual+ , LocalEmbeddingCertificate+ , certificateRoundedVertexDistinctnessCount+ , certificateSplitOrderPreservationCount+ , certificateIncidenceRelationPreservationCount+ , certificateNeighborhoodRotationPreservationCount+ , certificateRoundedVertices+ , certificateResidual+ , certifyLocalEmbedding+ ) where++import Control.DeepSeq (NFData)+import qualified Data.List as List+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NonEmpty+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import GHC.Generics (Generic)+import Moonlight.Triangulation.Exact+ ( ExactPoint+ , SegmentRelation+ , exactPointToEmbeddingCandidate+ , exactSegmentRelation+ )+import Moonlight.Triangulation.Internal.BoundaryCycle+ ( cyclePairs+ , unorderedPairs+ )+import Moonlight.Triangulation.Internal.ExactRational (ExactRational)+import qualified Moonlight.Triangulation.Math as Math+ ( orient2d+ , segmentRelation+ )+import Moonlight.Triangulation.Types+ ( Point (..)+ , PointValidationError+ , QueryPoint+ , queryPointValue+ )++-- | Phantom draft kind; three incompatible identifiers share one scalar owner.+data DraftEntity = DraftVertexEntity | DraftSegmentEntity | DraftSourceEntity++newtype DraftId (entity :: DraftEntity) = DraftId Int+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++type DraftVertexId = DraftId 'DraftVertexEntity+type DraftSegmentId = DraftId 'DraftSegmentEntity+type DraftSourceId = DraftId 'DraftSourceEntity++-- | A declared relation between two atomic draft segments.+data DraftIncidence = DraftIncidence+ { -- | First declared atomic segment.+ draftIncidenceFirstSegment :: !DraftSegmentId+ , -- | Second declared atomic segment.+ draftIncidenceSecondSegment :: !DraftSegmentId+ , -- | Relation declared to hold in both exact and rounded geometry.+ draftIncidenceRelation :: !SegmentRelation+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | One exact vertex and the cyclic neighbor order declared around it.+data DraftNeighborhood = DraftNeighborhood+ { -- | Center of the declared local rotation.+ draftNeighborhoodCenter :: !DraftVertexId+ , -- | Neighbors in cyclic rotation order.+ draftNeighborhoodNeighbors :: !(NonEmpty DraftVertexId)+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | A hand-built exact arrangement draft. It contains only declared local+-- structure; no arrangement or global crossing search is derived here.+data ExactArrangementDraft = ExactArrangementDraft+ { -- | Exact coordinates keyed by draft-local vertex identity.+ draftVertices :: !(Map DraftVertexId ExactPoint)+ , -- | Atomic segment endpoint identities.+ draftSegments :: !(Map DraftSegmentId (DraftVertexId, DraftVertexId))+ , -- | Exact split parameters and vertices in source-segment order.+ draftSourceMemberships :: !(Map DraftSourceId [(ExactRational, DraftVertexId)])+ , -- | Segment relations declared to remain invariant under projection.+ draftIncidences :: ![DraftIncidence]+ , -- | Cyclic local rotations declared to retain their neighbor order.+ draftNeighborhoods :: ![DraftNeighborhood]+ }+ deriving stock (Eq, Show, Generic)+ deriving anyclass (NFData)++-- | The typed identity of a missing draft reference.+data DraftReference+ = DraftVertexReference !DraftVertexId+ | DraftSegmentReference !DraftSegmentId+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | A witness that prevents local embedding certification.+data OverlayEmbeddingObstruction+ = DraftReferenceMissing !DraftReference+ | VertexProjectionRefused !DraftVertexId !PointValidationError+ | RoundedVerticesCollide !DraftVertexId !DraftVertexId !Point+ | SplitOrderNotPreserved !DraftSourceId !DraftVertexId !DraftVertexId+ | IncidenceRelationChanged+ !DraftSegmentId+ !DraftSegmentId+ !SegmentRelation+ !SegmentRelation+ !SegmentRelation+ | NeighborhoodRotationChanged+ !DraftVertexId+ !(NonEmpty DraftVertexId)+ !(NonEmpty DraftVertexId)+ | GlobalRelationAdded+ !DraftSegmentId+ !DraftSegmentId+ !SegmentRelation+ | GlobalRelationRemoved+ !DraftSegmentId+ !DraftSegmentId+ !SegmentRelation+ | GlobalRelationChanged+ !DraftSegmentId+ !DraftSegmentId+ !SegmentRelation+ !SegmentRelation+ | ProjectedSegmentCollapsed+ !DraftSegmentId+ !DraftVertexId+ !DraftVertexId+ deriving stock (Eq, Show, Generic)+ deriving anyclass (NFData)++-- | An embedding obligation not discharged by the bounded local certifier.+data EmbeddingObligation+ = GlobalNoNewCrossing+ deriving stock (Bounded, Enum, Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | A nonempty collection of obligations deferred to a later owner.+newtype EmbeddingResidual = EmbeddingResidual (NonEmpty EmbeddingObligation)+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | Read the obligations that remain explicitly undischarged.+residualUndischargedObligations+ :: EmbeddingResidual+ -> NonEmpty EmbeddingObligation+residualUndischargedObligations (EmbeddingResidual obligations) = obligations++-- | The Milestone 1 residual: the arrangement sweep has not yet proved global+-- absence of new crossings.+milestoneOneResidual :: EmbeddingResidual+milestoneOneResidual = EmbeddingResidual (GlobalNoNewCrossing :| [])++-- | A certificate for exactly four local obligations on a declared draft:+-- vertex distinctness, source split order, declared incidences, and local+-- neighborhood rotation. Global absence of new crossings is unproved until+-- the Milestone 2 arrangement sweep supplies the complete obligation set.+data LocalEmbeddingCertificate = LocalEmbeddingCertificate+ { -- | Number of rounded vertex-pair distinctness checks discharged.+ certificateRoundedVertexDistinctnessCount :: !Int+ , -- | Number of adjacent source split-order checks discharged.+ certificateSplitOrderPreservationCount :: !Int+ , -- | Number of declared incidence checks discharged.+ certificateIncidenceRelationPreservationCount :: !Int+ , -- | Number of neighborhood rotation entries certified.+ certificateNeighborhoodRotationPreservationCount :: !Int+ , -- | The admitted candidate projection certified by the four local obligations.+ certificateRoundedVertices :: !(Map DraftVertexId QueryPoint)+ , -- | The necessarily nonempty global obligation residual.+ certificateResidual :: !EmbeddingResidual+ }+ deriving stock (Eq, Show, Generic)+ deriving anyclass (NFData)++-- | Certify the four bounded local embedding obligations, collecting every+-- witness within each obligation and preserving obligation order.+certifyLocalEmbedding+ :: ExactArrangementDraft+ -> Either (NonEmpty OverlayEmbeddingObstruction) LocalEmbeddingCertificate+certifyLocalEmbedding draft =+ case NonEmpty.nonEmpty (structuralObstructions draft) of+ Just obstructions -> Left obstructions+ Nothing ->+ case projectVertices (draftVertices draft) of+ Invalid obstructions -> Left obstructions+ Valid projectedVertices ->+ certifyProjectedDraft draft projectedVertices++data Validation value+ = Invalid !(NonEmpty OverlayEmbeddingObstruction)+ | Valid value++instance Functor Validation where+ fmap _ (Invalid obstructions) = Invalid obstructions+ fmap transform (Valid value) = Valid (transform value)++instance Applicative Validation where+ pure = Valid+ Invalid left <*> Invalid right = Invalid (left <> right)+ Invalid obstructions <*> Valid _ = Invalid obstructions+ Valid _ <*> Invalid obstructions = Invalid obstructions+ Valid transform <*> Valid value = Valid (transform value)++invalid :: OverlayEmbeddingObstruction -> Validation value+invalid obstruction = Invalid (obstruction :| [])++data ProjectedVertex = ProjectedVertex+ { projectedExactPoint :: !ExactPoint+ , projectedRoundedPoint :: !QueryPoint+ }++data ResolvedSegment = ResolvedSegment+ { resolvedSegmentId :: !DraftSegmentId+ , resolvedSegmentFrom :: !ResolvedVertex+ , resolvedSegmentTo :: !ResolvedVertex+ }++data ResolvedVertex = ResolvedVertex+ { resolvedVertexId :: !DraftVertexId+ , resolvedProjectedVertex :: !ProjectedVertex+ }++data ResolvedMembership = ResolvedMembership+ { resolvedMembershipParameter :: !ExactRational+ , resolvedMembershipVertex :: !ResolvedVertex+ }++structuralObstructions+ :: ExactArrangementDraft+ -> [OverlayEmbeddingObstruction]+structuralObstructions draft =+ segmentEndpointObstructions+ <> sourceMembershipObstructions+ <> incidenceObstructions+ <> neighborhoodObstructions+ where+ vertices = draftVertices draft+ segments = draftSegments draft+ missingVertex vertexId =+ [ DraftReferenceMissing (DraftVertexReference vertexId)+ | Map.notMember vertexId vertices+ ]+ missingSegment segmentId =+ [ DraftReferenceMissing (DraftSegmentReference segmentId)+ | Map.notMember segmentId segments+ ]+ segmentEndpointObstructions =+ concatMap+ (\(_, (from, to)) -> missingVertex from <> missingVertex to)+ (Map.toAscList segments)+ sourceMembershipObstructions =+ concatMap+ (concatMap (missingVertex . snd) . snd)+ (Map.toAscList (draftSourceMemberships draft))+ incidenceObstructions =+ concatMap+ ( \incidence ->+ missingSegment (draftIncidenceFirstSegment incidence)+ <> missingSegment (draftIncidenceSecondSegment incidence)+ )+ (draftIncidences draft)+ neighborhoodObstructions =+ concatMap+ ( \neighborhood ->+ missingVertex (draftNeighborhoodCenter neighborhood)+ <> concatMap missingVertex (draftNeighborhoodNeighbors neighborhood)+ )+ (draftNeighborhoods draft)++projectVertices+ :: Map DraftVertexId ExactPoint+ -> Validation (Map DraftVertexId ProjectedVertex)+projectVertices =+ Map.traverseWithKey+ ( \vertexId point ->+ case exactPointToEmbeddingCandidate point of+ Left projectionError ->+ invalid (VertexProjectionRefused vertexId projectionError)+ Right roundedPoint -> Valid (ProjectedVertex point roundedPoint)+ )++certifyProjectedDraft+ :: ExactArrangementDraft+ -> Map DraftVertexId ProjectedVertex+ -> Either (NonEmpty OverlayEmbeddingObstruction) LocalEmbeddingCertificate+certifyProjectedDraft draft projectedVertices =+ case resolvedFailures of+ Left obstruction -> Left (obstruction :| [])+ Right localFailures ->+ case NonEmpty.nonEmpty (collisionFailures <> localFailures) of+ Just obstructions -> Left obstructions+ Nothing ->+ Right+ LocalEmbeddingCertificate+ { certificateRoundedVertexDistinctnessCount = distinctnessCount+ , certificateSplitOrderPreservationCount = splitOrderCount+ , certificateIncidenceRelationPreservationCount = incidenceCount+ , certificateNeighborhoodRotationPreservationCount = neighborhoodCount+ , certificateRoundedVertices = Map.map projectedRoundedPoint projectedVertices+ , certificateResidual = milestoneOneResidual+ }+ where+ collisionFailures = roundedVertexCollisionObstructions projectedVertices+ resolvedFailures = do+ splitFailures <- traverse (uncurry resolveSource) (Map.toAscList (draftSourceMemberships draft))+ incidenceFailures <- traverse resolveIncidence (draftIncidences draft)+ neighborhoodFailures <- traverse resolveNeighborhood (draftNeighborhoods draft)+ pure (concat splitFailures <> concat incidenceFailures <> concat neighborhoodFailures)+ resolveVertex vertexId =+ case Map.lookup vertexId projectedVertices of+ Nothing -> Left (DraftReferenceMissing (DraftVertexReference vertexId))+ Just projectedVertex -> Right (ResolvedVertex vertexId projectedVertex)+ resolveSegment segmentId =+ case Map.lookup segmentId (draftSegments draft) of+ Nothing -> Left (DraftReferenceMissing (DraftSegmentReference segmentId))+ Just (from, to) ->+ ResolvedSegment segmentId+ <$> resolveVertex from+ <*> resolveVertex to+ resolveSource sourceId memberships =+ splitOrderObstructions sourceId+ <$> traverse+ ( \(parameter, vertexId) ->+ ResolvedMembership parameter <$> resolveVertex vertexId+ )+ memberships+ resolveIncidence incidence =+ incidenceRelationObstructions+ (draftIncidenceRelation incidence)+ <$> resolveSegment (draftIncidenceFirstSegment incidence)+ <*> resolveSegment (draftIncidenceSecondSegment incidence)+ resolveNeighborhood neighborhood =+ neighborhoodRotationObstructions+ <$> resolveVertex (draftNeighborhoodCenter neighborhood)+ <*> traverse resolveVertex (draftNeighborhoodNeighbors neighborhood)+ vertexCount = Map.size projectedVertices+ distinctnessCount = vertexCount * (vertexCount - 1) `quot` 2+ splitOrderCount =+ sum+ ( map+ (max 0 . subtract 1 . length)+ (Map.elems (draftSourceMemberships draft))+ )+ incidenceCount = length (draftIncidences draft)+ neighborhoodCount =+ sum+ ( map+ (length . draftNeighborhoodNeighbors)+ (draftNeighborhoods draft)+ )++roundedVertexCollisionObstructions+ :: Map DraftVertexId ProjectedVertex+ -> [OverlayEmbeddingObstruction]+roundedVertexCollisionObstructions projectedVertices =+ [ RoundedVerticesCollide leftId rightId (queryPointValue roundedPoint)+ | (roundedPoint, vertexIds) <- Map.toAscList verticesByRoundedPoint+ , (leftId, rightId) <- unorderedPairs (List.sort vertexIds)+ ]+ where+ verticesByRoundedPoint =+ Map.fromListWith (<>)+ [ (projectedRoundedPoint projectedVertex, [vertexId])+ | (vertexId, projectedVertex) <- Map.toAscList projectedVertices+ ]++splitOrderObstructions+ :: DraftSourceId+ -> [ResolvedMembership]+ -> [OverlayEmbeddingObstruction]+splitOrderObstructions sourceId memberships =+ case memberships of+ firstMembership : secondMembership : remainingMemberships ->+ let finalMembership =+ List.foldl' (\_ current -> current) secondMembership remainingMemberships+ sourceFrom = roundedMembershipPoint firstMembership+ sourceTo = roundedMembershipPoint finalMembership+ in [ SplitOrderNotPreserved+ sourceId+ (resolvedVertexId (resolvedMembershipVertex leftMembership))+ (resolvedVertexId (resolvedMembershipVertex rightMembership))+ | (leftMembership, rightMembership) <-+ zip+ memberships+ (drop 1 memberships)+ , compare+ (resolvedMembershipParameter leftMembership)+ (resolvedMembershipParameter rightMembership)+ /= roundedOrderAlong+ sourceFrom+ sourceTo+ (roundedMembershipPoint leftMembership)+ (roundedMembershipPoint rightMembership)+ ]+ _ -> []++roundedMembershipPoint :: ResolvedMembership -> Point+roundedMembershipPoint =+ queryPointValue+ . projectedRoundedPoint+ . resolvedProjectedVertex+ . resolvedMembershipVertex++roundedOrderAlong :: Point -> Point -> Point -> Point -> Ordering+roundedOrderAlong+ (Point sourceFromX sourceFromY)+ (Point sourceToX sourceToY)+ (Point leftX leftY)+ (Point rightX rightY) =+ let directionX = sourceToX - sourceFromX+ directionY = sourceToY - sourceFromY+ in if abs directionX >= abs directionY+ then+ if directionX >= 0+ then compare leftX rightX+ else compare rightX leftX+ else+ if directionY >= 0+ then compare leftY rightY+ else compare rightY leftY++incidenceRelationObstructions+ :: SegmentRelation+ -> ResolvedSegment+ -> ResolvedSegment+ -> [OverlayEmbeddingObstruction]+incidenceRelationObstructions declaredRelation firstSegment secondSegment =+ [ IncidenceRelationChanged+ (resolvedSegmentId firstSegment)+ (resolvedSegmentId secondSegment)+ declaredRelation+ exactRelation+ roundedRelation+ | exactRelation /= declaredRelation || roundedRelation /= declaredRelation+ ]+ where+ exactRelation =+ relationFor exactSegmentRelation projectedExactPoint firstSegment secondSegment+ roundedRelation =+ relationFor Math.segmentRelation roundedProjectedPoint firstSegment secondSegment++relationFor+ :: (point -> point -> point -> point -> SegmentRelation)+ -> (ProjectedVertex -> point)+ -> ResolvedSegment+ -> ResolvedSegment+ -> SegmentRelation+relationFor relation project firstSegment secondSegment =+ relation+ (project (resolvedProjectedVertex (resolvedSegmentFrom firstSegment)))+ (project (resolvedProjectedVertex (resolvedSegmentTo firstSegment)))+ (project (resolvedProjectedVertex (resolvedSegmentFrom secondSegment)))+ (project (resolvedProjectedVertex (resolvedSegmentTo secondSegment)))++neighborhoodRotationObstructions+ :: ResolvedVertex+ -> NonEmpty ResolvedVertex+ -> [OverlayEmbeddingObstruction]+neighborhoodRotationObstructions center exactRotation =+ [ NeighborhoodRotationChanged+ (resolvedVertexId center)+ exactVertexRotation+ roundedVertexRotation+ | roundedRotationDescents > 1+ ]+ where+ exactVertexRotation = fmap resolvedVertexId exactRotation+ roundedRotationDescents =+ List.foldl'+ (\descentCount (left, right) ->+ descentCount+ + if compareRoundedAround center left right == GT+ then 1+ else 0)+ (0 :: Int)+ (cyclePairs exactRotation)+ roundedVertexRotation =+ fmap resolvedVertexId+ (NonEmpty.sortBy (compareRoundedAround center) exactRotation)++compareRoundedAround+ :: ResolvedVertex+ -> ResolvedVertex+ -> ResolvedVertex+ -> Ordering+compareRoundedAround center left right =+ compareRoundedAroundWith+ ( Math.orient2d+ (roundedVertexPoint center)+ (roundedVertexPoint left)+ (roundedVertexPoint right)+ )+ center+ left+ right++compareRoundedAroundWith+ :: Ordering+ -> ResolvedVertex+ -> ResolvedVertex+ -> ResolvedVertex+ -> Ordering+compareRoundedAroundWith roundedOrientation center left right =+ case compare (roundedVectorHalf center left) (roundedVectorHalf center right) of+ EQ ->+ case roundedOrientation of+ GT -> LT+ LT -> GT+ EQ -> compare (roundedVertexPoint left) (roundedVertexPoint right)+ ordering -> ordering++roundedVectorHalf :: ResolvedVertex -> ResolvedVertex -> Bool+roundedVectorHalf center neighbor =+ let Point centerX centerY = roundedVertexPoint center+ Point neighborX neighborY = roundedVertexPoint neighbor+ deltaX = neighborX - centerX+ deltaY = neighborY - centerY+ in deltaY < 0 || (deltaY == 0 && deltaX < 0)++roundedVertexPoint :: ResolvedVertex -> Point+roundedVertexPoint = roundedProjectedPoint . resolvedProjectedVertex++roundedProjectedPoint :: ProjectedVertex -> Point+roundedProjectedPoint = queryPointValue . projectedRoundedPoint
+ src-overlay/Moonlight/Triangulation/Internal/Overlay/Arrangement.hs view
@@ -0,0 +1,812 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}++-- | Exact source normalization and binary64 embedding certification. This+-- module ends at the arrangement/resident seam: no DCEL face state crosses it.+module Moonlight.Triangulation.Internal.Overlay.Arrangement+ ( ExactEdgeKey+ , AtomicEdge (..)+ , atomicEdgeFrom+ , atomicEdgeTo+ , OverlayVertexSeed (..)+ , ArrangementMetrics (..)+ , CertifiedArrangement (..)+ , certifyArrangement+ , canonicalEdgeKey+ , atomicKey+ , compareAround+ ) where++import Data.Bifunctor (first)+import Data.List (sortBy)+import qualified Data.List as List+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NonEmpty+import qualified Data.Map.Strict as Map+import Data.Map.Strict (Map)+import qualified Data.Set as Set+import Data.Set (Set)+import qualified Data.Vector as V+import GHC.Generics (Generic)+import Moonlight.Triangulation.Exact+ ( ExactGeometryError+ , ExactPoint+ , ExactSegment+ , SegmentRelation (SegmentsShareEndpoint)+ , compareExactVectorAngle+ , exactPointCoordinates+ , exactPointFromQueryPoint+ , exactSegment+ , exactSegmentEndpoints+ , exactVectorFromPoints+ )+import Moonlight.Triangulation.Internal.BoundaryCycle+ ( consecutivePairs+ , orderedPair+ , unorderedPairs+ )+import Moonlight.Triangulation.Internal.ExactRational+ ( ExactRational+ , exactDivide+ , exactRationalIsZero+ )+import Moonlight.Triangulation.Internal.ExactSegmentEvents+ ( ExactSegmentEvent (..)+ , ExactSegmentEventPlan+ , ExactSweepSegmentId (..)+ , exactSegmentEventPlan+ , exactSegmentEvents+ , exactSegmentPairChecks+ , exactSegmentRelationMap+ , exactSegmentSplitPoints+ , exactSegmentSweepMaximumHeight+ )+import Moonlight.Triangulation.Embedding+ ( DraftId (..)+ , DraftIncidence (..)+ , DraftNeighborhood (..)+ , DraftReference (..)+ , DraftVertexId+ , ExactArrangementDraft (..)+ , LocalEmbeddingCertificate+ , certificateRoundedVertices+ , OverlayEmbeddingObstruction (..)+ , certifyLocalEmbedding+ )+import Moonlight.Triangulation.Internal.Overlay.Types+import Moonlight.Triangulation.Internal.Types+ ( HasPosition (..)+ , Point+ , queryPointValue+ )+import Moonlight.Triangulation.Region+ ( ExactLoop+ , PlanarLayer+ , PolygonComponent+ , exactLoopPoints+ , planarLayerOutsideLabel+ , planarLayerRegions+ , planarRegionComponents+ , polygonHoleLoops+ , polygonOuterLoop+ )++type ExactEdgeKey = (ExactPoint, ExactPoint)++data SourceBoundary leftLabel rightLabel+ = LeftSourceBoundary+ !ExactSegment+ !leftLabel+ !(BoundaryVertexRef 'LeftOverlayOperand)+ !(BoundaryVertexRef 'LeftOverlayOperand)+ !(BoundaryEdgeRef 'LeftOverlayOperand)+ | RightSourceBoundary+ !ExactSegment+ !rightLabel+ !(BoundaryVertexRef 'RightOverlayOperand)+ !(BoundaryVertexRef 'RightOverlayOperand)+ !(BoundaryEdgeRef 'RightOverlayOperand)++type SourceBoundaryConstructor operand label leftLabel rightLabel =+ ExactSegment+ -> label+ -> BoundaryVertexRef operand+ -> BoundaryVertexRef operand+ -> BoundaryEdgeRef operand+ -> SourceBoundary leftLabel rightLabel++data AtomicContribution leftLabel rightLabel =+ AtomicContribution !(SourceBoundary leftLabel rightLabel) !Bool++data AtomicEdge leftLabel rightLabel = AtomicEdge+ { atomicEdgeSegment :: !ExactSegment+ , atomicEdgeOrigin :: !OverlayEdgeOrigin+ , atomicEdgeLeftTransition :: !(Maybe (leftLabel, leftLabel))+ , atomicEdgeRightTransition :: !(Maybe (rightLabel, rightLabel))+ }++data OriginAccumulation = OriginAccumulation+ { accumulatedLeftVertices :: !(Set (BoundaryVertexRef 'LeftOverlayOperand))+ , accumulatedRightVertices :: !(Set (BoundaryVertexRef 'RightOverlayOperand))+ , accumulatedLeftEdges :: !(Set (BoundaryEdgeRef 'LeftOverlayOperand))+ , accumulatedRightEdges :: !(Set (BoundaryEdgeRef 'RightOverlayOperand))+ }++data OverlayVertexSeed = OverlayVertexSeed+ { seedExactPoint :: !ExactPoint+ , seedEmbeddedPoint :: !Point+ , seedOrigin :: !OverlayVertexOrigin+ }+ deriving stock (Eq, Ord, Show, Generic)++instance HasPosition OverlayVertexSeed where+ position = seedEmbeddedPoint++data ArrangementMetrics = ArrangementMetrics+ { arrangementInputSegments :: !Int+ , arrangementRelationEvents :: !Int+ , arrangementExactCrossings :: !Int+ , arrangementOverlapIntervals :: !Int+ , arrangementEmbeddingCandidates :: !Int+ , arrangementTotalRelationChecks :: !Int+ , arrangementSweepMaximumHeight :: !Int+ }++data CertifiedArrangement leftLabel rightLabel = CertifiedArrangement+ { certifiedAtomicEdges :: !(V.Vector (AtomicEdge leftLabel rightLabel))+ , certifiedVertexSeeds :: !(V.Vector OverlayVertexSeed)+ , certifiedConstraints :: !(V.Vector (Int, Int))+ , certifiedInexactEmbeddingPoints :: !(Set ExactPoint)+ , certifiedMetrics :: !ArrangementMetrics+ }++certifyArrangement+ :: (Ord leftLabel, Ord rightLabel)+ => PlanarLayer leftLabel+ -> PlanarLayer rightLabel+ -> Either+ (OverlayError leftLabel rightLabel)+ (CertifiedArrangement leftLabel rightLabel)+certifyArrangement leftLayer rightLayer = do+ sources <- flattenLayers leftLayer rightLayer+ sourcePlan <-+ first OverlaySegmentEventsInvalid+ (exactSegmentEventPlan (V.map sourceExactSegment sources))+ atomicEdges <- normalizeAtomicEdges leftLayer rightLayer sources sourcePlan+ let atomicVector = V.fromList atomicEdges+ pointIds = exactPointIds atomicVector+ origins = vertexOrigins sources atomicVector+ atomicRelations = atomicEndpointRelations atomicVector+ draft <- exactArrangementDraft sources sourcePlan atomicVector pointIds atomicRelations+ localCertificate <- first OverlayEmbeddingRefused (certifyLocalEmbedding draft)+ let projectedPoints = projectedExactPoints localCertificate+ inexactEmbeddingPoints =+ Set.fromList+ [ exactPoint+ | (exactPoint, vertexId) <- Map.toAscList pointIds+ , Map.lookup vertexId projectedPoints /= Just exactPoint+ ]+ projectedSegments <- projectedAtomicSegments atomicVector pointIds projectedPoints+ projectedPlan <-+ first OverlaySegmentEventsInvalid+ (exactSegmentEventPlan projectedSegments)+ dischargeGlobalRelations atomicRelations projectedPlan+ seeds <- overlayVertexSeeds pointIds origins localCertificate+ constraints <- atomicConstraints pointIds atomicVector+ let events = exactSegmentEvents sourcePlan+ pure+ CertifiedArrangement+ { certifiedAtomicEdges = atomicVector+ , certifiedVertexSeeds = seeds+ , certifiedConstraints = constraints+ , certifiedInexactEmbeddingPoints = inexactEmbeddingPoints+ , certifiedMetrics =+ ArrangementMetrics+ { arrangementInputSegments = V.length sources+ , arrangementRelationEvents = length events+ , arrangementExactCrossings =+ length [() | ExactProperCrossing {} <- events]+ , arrangementOverlapIntervals =+ length [() | ExactCollinearOverlap {} <- events]+ , arrangementEmbeddingCandidates = Map.size pointIds+ , arrangementTotalRelationChecks =+ exactSegmentPairChecks sourcePlan+ + exactSegmentPairChecks projectedPlan+ , arrangementSweepMaximumHeight =+ max+ (exactSegmentSweepMaximumHeight sourcePlan)+ (exactSegmentSweepMaximumHeight projectedPlan)+ }+ }+flattenLayers+ :: PlanarLayer leftLabel+ -> PlanarLayer rightLabel+ -> Either (OverlayError leftLabel rightLabel) (V.Vector (SourceBoundary leftLabel rightLabel))+flattenLayers leftLayer rightLayer = do+ leftSources <- flattenLayer LeftSourceBoundary leftLayer+ rightSources <- flattenLayer RightSourceBoundary rightLayer+ pure (V.fromList (leftSources <> rightSources))++flattenLayer+ :: SourceBoundaryConstructor operand label leftLabel rightLabel+ -> PlanarLayer label+ -> Either (OverlayError leftLabel rightLabel) [SourceBoundary leftLabel rightLabel]+flattenLayer makeBoundary layer =+ fmap concat+ ( traverse+ (\(componentIndex, (label, component)) ->+ flattenComponent makeBoundary componentIndex label component)+ (zip [0 ..] (labelledComponents layer))+ )+{-# INLINE flattenLayer #-}++labelledComponents :: PlanarLayer label -> [(label, PolygonComponent)]+labelledComponents layer =+ [ (label, component)+ | (label, region) <- Map.toAscList (planarLayerRegions layer)+ , component <- planarRegionComponents region+ ]++flattenComponent+ :: SourceBoundaryConstructor operand label leftLabel rightLabel+ -> Int+ -> label+ -> PolygonComponent+ -> Either (OverlayError leftLabel rightLabel) [SourceBoundary leftLabel rightLabel]+flattenComponent makeBoundary componentIndex label component = do+ outer <-+ flattenLoop+ makeBoundary+ componentIndex+ BoundaryOuterLoop+ label+ (polygonOuterLoop component)+ holes <-+ fmap concat+ ( traverse+ (\(holeIndex, loop) ->+ flattenLoop+ makeBoundary+ componentIndex+ (BoundaryHoleLoop holeIndex)+ label+ loop)+ (zip [0 ..] (polygonHoleLoops component))+ )+ pure (outer <> holes)+{-# INLINE flattenComponent #-}++flattenLoop+ :: SourceBoundaryConstructor operand label leftLabel rightLabel+ -> Int+ -> BoundaryLoopRef+ -> label+ -> ExactLoop+ -> Either (OverlayError leftLabel rightLabel) [SourceBoundary leftLabel rightLabel]+flattenLoop makeBoundary componentIndex loopRef label loop =+ traverse constructSource (indexedCycle (exactLoopPoints loop))+ where+ constructSource (edgeIndex, fromIndex, from, toIndex, to) = do+ segment <- first (sourceGeometryError from) (exactSegment from to)+ pure+ ( makeBoundary+ segment+ label+ (BoundaryRef componentIndex loopRef fromIndex)+ (BoundaryRef componentIndex loopRef toIndex)+ (BoundaryRef componentIndex loopRef edgeIndex)+ )+{-# INLINE flattenLoop #-}++sourceGeometryError+ :: ExactPoint+ -> ExactGeometryError+ -> OverlayError leftLabel rightLabel+sourceGeometryError point _ = OverlayArrangementInvalid (OverlayRotationDegenerate point)++indexedCycle :: NonEmpty value -> [(Int, Int, value, Int, value)]+indexedCycle (firstValue :| remaining) =+ let values = firstValue : remaining+ count = length values+ in [ (index, index, from, (index + 1) `mod` count, to)+ | (index, (from, to)) <- zip [0 ..] (zip values (remaining <> [firstValue]))+ ]++sourceExactSegment :: SourceBoundary leftLabel rightLabel -> ExactSegment+sourceExactSegment (LeftSourceBoundary segment _ _ _ _) = segment+sourceExactSegment (RightSourceBoundary segment _ _ _ _) = segment++normalizeAtomicEdges+ :: (Ord leftLabel, Ord rightLabel)+ => PlanarLayer leftLabel+ -> PlanarLayer rightLabel+ -> V.Vector (SourceBoundary leftLabel rightLabel)+ -> ExactSegmentEventPlan+ -> Either (OverlayError leftLabel rightLabel) [AtomicEdge leftLabel rightLabel]+normalizeAtomicEdges leftLayer rightLayer sources plan =+ fmap concat+ ( traverse+ (resolveAtomicContributions leftOutside rightOutside)+ (Map.toAscList grouped)+ )+ where+ leftOutside = planarLayerOutsideLabel leftLayer+ rightOutside = planarLayerOutsideLabel rightLayer+ grouped =+ V.ifoldl'+ (\groups sourceIndex source ->+ List.foldl'+ (insertAtomic source)+ groups+ (consecutivePairs (exactSegmentSplitPoints plan (ExactSweepSegmentId sourceIndex))))+ Map.empty+ sources+ insertAtomic+ :: SourceBoundary leftLabel' rightLabel'+ -> Map ExactEdgeKey [AtomicContribution leftLabel' rightLabel']+ -> (ExactPoint, ExactPoint)+ -> Map ExactEdgeKey [AtomicContribution leftLabel' rightLabel']+ insertAtomic source groups (from, to)+ | from == to = groups+ | otherwise =+ let key@(canonicalFrom, _) = canonicalEdgeKey from to+ contribution = AtomicContribution source (from == canonicalFrom)+ in Map.insertWith (<>) key [contribution] groups++resolveAtomicContributions+ :: (Ord leftLabel, Ord rightLabel)+ => leftLabel+ -> rightLabel+ -> (ExactEdgeKey, [AtomicContribution leftLabel rightLabel])+ -> Either (OverlayError leftLabel rightLabel) [AtomicEdge leftLabel rightLabel]+resolveAtomicContributions leftOutside rightOutside ((from, to), contributions) = do+ let+ ( leftLabelsOnLeft+ , leftLabelsOnRight+ , rightLabelsOnLeft+ , rightLabelsOnRight+ , leftSources+ , rightSources+ ) =+ List.foldl'+ collectContribution+ (Set.empty, Set.empty, Set.empty, Set.empty, Set.empty, Set.empty)+ contributions+ origin =+ OverlayEdgeOrigin+ (Set.toAscList leftSources)+ (Set.toAscList rightSources)+ leftTransition <-+ resolveTransition+ OverlayLeftSourceSideConflict+ origin+ leftOutside+ leftLabelsOnLeft+ leftLabelsOnRight+ rightTransition <-+ resolveTransition+ OverlayRightSourceSideConflict+ origin+ rightOutside+ rightLabelsOnLeft+ rightLabelsOnRight+ if transitionIsIdentity leftTransition && transitionIsIdentity rightTransition+ then Right []+ else do+ segment <- first (sourceGeometryError from) (exactSegment from to)+ Right+ [ AtomicEdge+ { atomicEdgeSegment = segment+ , atomicEdgeOrigin = origin+ , atomicEdgeLeftTransition = leftTransition+ , atomicEdgeRightTransition = rightTransition+ }+ ]+ where+ collectContribution+ :: (Ord leftLabel', Ord rightLabel')+ => ( Set leftLabel'+ , Set leftLabel'+ , Set rightLabel'+ , Set rightLabel'+ , Set (BoundaryEdgeRef 'LeftOverlayOperand)+ , Set (BoundaryEdgeRef 'RightOverlayOperand)+ )+ -> AtomicContribution leftLabel' rightLabel'+ -> ( Set leftLabel'+ , Set leftLabel'+ , Set rightLabel'+ , Set rightLabel'+ , Set (BoundaryEdgeRef 'LeftOverlayOperand)+ , Set (BoundaryEdgeRef 'RightOverlayOperand)+ )+ collectContribution+ ( !leftLeft+ , !leftRight+ , !rightLeft+ , !rightRight+ , !leftSources+ , !rightSources+ )+ contribution =+ case contribution of+ AtomicContribution (LeftSourceBoundary _ label _ _ reference) follows ->+ ( if follows then Set.insert label leftLeft else leftLeft+ , if follows then leftRight else Set.insert label leftRight+ , rightLeft+ , rightRight+ , Set.insert reference leftSources+ , rightSources+ )+ AtomicContribution (RightSourceBoundary _ label _ _ reference) follows ->+ ( leftLeft+ , leftRight+ , if follows then Set.insert label rightLeft else rightLeft+ , if follows then rightRight else Set.insert label rightRight+ , leftSources+ , Set.insert reference rightSources+ )++resolveTransition+ :: (OverlayEdgeOrigin -> NonEmpty label -> OverlayArrangementObstruction leftLabel rightLabel)+ -> OverlayEdgeOrigin+ -> label+ -> Set label+ -> Set label+ -> Either (OverlayError leftLabel rightLabel) (Maybe (label, label))+resolveTransition sideConflict origin outside labelsOnLeft labelsOnRight+ | Set.null labelsOnLeft && Set.null labelsOnRight = Right Nothing+ | otherwise =+ Just+ <$> ((,)+ <$> resolveSide sideConflict origin outside labelsOnLeft+ <*> resolveSide sideConflict origin outside labelsOnRight)+{-# INLINE resolveTransition #-}++transitionIsIdentity :: Eq label => Maybe (label, label) -> Bool+transitionIsIdentity Nothing = True+transitionIsIdentity (Just (leftLabel, rightLabel)) = leftLabel == rightLabel++resolveSide+ :: (OverlayEdgeOrigin -> NonEmpty label -> OverlayArrangementObstruction leftLabel rightLabel)+ -> OverlayEdgeOrigin+ -> label+ -> Set label+ -> Either (OverlayError leftLabel rightLabel) label+resolveSide sideConflict origin outside labels =+ case Set.toAscList labels of+ [] -> Right outside+ [label] -> Right label+ firstLabel : remaining ->+ Left+ ( OverlayArrangementInvalid+ (sideConflict origin (firstLabel :| remaining))+ )+{-# INLINE resolveSide #-}++exactPointIds+ :: V.Vector (AtomicEdge leftLabel rightLabel)+ -> Map ExactPoint DraftVertexId+exactPointIds edges =+ Map.fromAscList+ ( zip+ (Set.toAscList (V.foldl' collect Set.empty edges))+ (map DraftId [0 ..])+ )+ where+ collect+ :: Set ExactPoint+ -> AtomicEdge leftLabel rightLabel+ -> Set ExactPoint+ collect points edge = Set.insert (atomicEdgeFrom edge) (Set.insert (atomicEdgeTo edge) points)++vertexOrigins+ :: V.Vector (SourceBoundary leftLabel rightLabel)+ -> V.Vector (AtomicEdge leftLabel rightLabel)+ -> Map ExactPoint OverlayVertexOrigin+vertexOrigins sources atomicEdges =+ Map.map finalizeOrigin+ ( V.foldl'+ addAtomicOrigin+ (V.foldl' addSourceEndpoints Map.empty sources)+ atomicEdges+ )++emptyOrigin :: OriginAccumulation+emptyOrigin = OriginAccumulation Set.empty Set.empty Set.empty Set.empty++mergeOrigin :: OriginAccumulation -> OriginAccumulation -> OriginAccumulation+mergeOrigin left right =+ OriginAccumulation+ { accumulatedLeftVertices = accumulatedLeftVertices left <> accumulatedLeftVertices right+ , accumulatedRightVertices = accumulatedRightVertices left <> accumulatedRightVertices right+ , accumulatedLeftEdges = accumulatedLeftEdges left <> accumulatedLeftEdges right+ , accumulatedRightEdges = accumulatedRightEdges left <> accumulatedRightEdges right+ }++addSourceEndpoints+ :: Map ExactPoint OriginAccumulation+ -> SourceBoundary leftLabel rightLabel+ -> Map ExactPoint OriginAccumulation+addSourceEndpoints origins source =+ case source of+ LeftSourceBoundary segment _ fromReference toReference _ ->+ let (from, to) = exactSegmentEndpoints segment+ in insertOrigin to (emptyOrigin{accumulatedLeftVertices = Set.singleton toReference})+ (insertOrigin from (emptyOrigin{accumulatedLeftVertices = Set.singleton fromReference}) origins)+ RightSourceBoundary segment _ fromReference toReference _ ->+ let (from, to) = exactSegmentEndpoints segment+ in insertOrigin to (emptyOrigin{accumulatedRightVertices = Set.singleton toReference})+ (insertOrigin from (emptyOrigin{accumulatedRightVertices = Set.singleton fromReference}) origins)++addAtomicOrigin+ :: Map ExactPoint OriginAccumulation+ -> AtomicEdge leftLabel rightLabel+ -> Map ExactPoint OriginAccumulation+addAtomicOrigin origins edge =+ let origin = atomicEdgeOrigin edge+ accumulation =+ emptyOrigin+ { accumulatedLeftEdges = Set.fromList (overlayEdgeLeftSources origin)+ , accumulatedRightEdges = Set.fromList (overlayEdgeRightSources origin)+ }+ in insertOrigin (atomicEdgeTo edge) accumulation+ (insertOrigin (atomicEdgeFrom edge) accumulation origins)++insertOrigin+ :: ExactPoint+ -> OriginAccumulation+ -> Map ExactPoint OriginAccumulation+ -> Map ExactPoint OriginAccumulation+insertOrigin = Map.insertWith mergeOrigin++finalizeOrigin :: OriginAccumulation -> OverlayVertexOrigin+finalizeOrigin accumulated =+ OverlayVertexOrigin+ { overlayOriginLeftVertices = Set.toAscList (accumulatedLeftVertices accumulated)+ , overlayOriginRightVertices = Set.toAscList (accumulatedRightVertices accumulated)+ , overlayOriginLeftEdges = Set.toAscList (accumulatedLeftEdges accumulated)+ , overlayOriginRightEdges = Set.toAscList (accumulatedRightEdges accumulated)+ }++exactArrangementDraft+ :: V.Vector (SourceBoundary leftLabel rightLabel)+ -> ExactSegmentEventPlan+ -> V.Vector (AtomicEdge leftLabel rightLabel)+ -> Map ExactPoint DraftVertexId+ -> Map (ExactSweepSegmentId, ExactSweepSegmentId) SegmentRelation+ -> Either (OverlayError leftLabel rightLabel) ExactArrangementDraft+exactArrangementDraft sources sourcePlan atomicEdges pointIds atomicRelations = do+ draftSegmentsMap <-+ Map.fromList+ <$> traverse+ (\(segmentIndex, edge) -> do+ from <- requireDraftVertex pointIds (atomicEdgeFrom edge)+ to <- requireDraftVertex pointIds (atomicEdgeTo edge)+ pure (DraftId segmentIndex, (from, to)))+ (V.toList (V.indexed atomicEdges))+ memberships <-+ Map.fromList+ <$> traverse+ (\(sourceIndex, source) -> do+ values <-+ traverse+ (\point -> do+ parameter <- exactSourceParameter source point+ vertex <- requireDraftVertex pointIds point+ pure (parameter, vertex))+ (exactSegmentSplitPoints sourcePlan (ExactSweepSegmentId sourceIndex))+ pure (DraftId sourceIndex, values))+ (V.toList (V.indexed sources))+ neighborhoods <- buildDraftNeighborhoods atomicEdges pointIds+ let incidences =+ [ DraftIncidence+ (DraftId leftIndex)+ (DraftId rightIndex)+ relation+ | ((ExactSweepSegmentId leftIndex, ExactSweepSegmentId rightIndex), relation) <-+ Map.toAscList atomicRelations+ ]+ pure+ ExactArrangementDraft+ { draftVertices = Map.fromList [(vertexId, point) | (point, vertexId) <- Map.toAscList pointIds]+ , draftSegments = draftSegmentsMap+ , draftSourceMemberships = memberships+ , draftIncidences = incidences+ , draftNeighborhoods = neighborhoods+ }++requireDraftVertex+ :: Map ExactPoint DraftVertexId+ -> ExactPoint+ -> Either (OverlayError leftLabel rightLabel) DraftVertexId+requireDraftVertex pointIds point =+ case Map.lookup point pointIds of+ Just vertex -> Right vertex+ Nothing -> Left (OverlayProvenanceIncomplete (OverlayExactVertexMissing point))++exactSourceParameter+ :: SourceBoundary leftLabel rightLabel+ -> ExactPoint+ -> Either (OverlayError leftLabel rightLabel) ExactRational+exactSourceParameter source point =+ let (from, to) = exactSegmentEndpoints (sourceExactSegment source)+ (fromX, fromY) = exactPointCoordinates from+ (toX, toY) = exactPointCoordinates to+ (pointX, pointY) = exactPointCoordinates point+ (numerator, denominator) =+ if exactRationalIsZero (toX - fromX)+ then (pointY - fromY, toY - fromY)+ else (pointX - fromX, toX - fromX)+ in first OverlayExactArithmetic (exactDivide numerator denominator)++buildDraftNeighborhoods+ :: V.Vector (AtomicEdge leftLabel rightLabel)+ -> Map ExactPoint DraftVertexId+ -> Either (OverlayError leftLabel rightLabel) [DraftNeighborhood]+buildDraftNeighborhoods edges pointIds =+ traverse neighborhood (Map.toAscList pointIds)+ where+ neighborhood (centerPoint, centerId) = do+ let neighbors = Map.findWithDefault Set.empty centerPoint adjacency+ orderedPoints <-+ case NonEmpty.nonEmpty (sortBy (compareAround centerPoint) (Set.toList neighbors)) of+ Just points -> Right points+ Nothing -> Left (OverlayArrangementInvalid (OverlayRotationDegenerate centerPoint))+ orderedIds <- traverse (requireDraftVertex pointIds) orderedPoints+ pure (DraftNeighborhood centerId orderedIds)+ adjacency =+ V.foldl'+ (\graph edge ->+ Map.insertWith Set.union (atomicEdgeTo edge) (Set.singleton (atomicEdgeFrom edge))+ (Map.insertWith Set.union (atomicEdgeFrom edge) (Set.singleton (atomicEdgeTo edge)) graph))+ Map.empty+ edges++compareAround :: ExactPoint -> ExactPoint -> ExactPoint -> Ordering+compareAround center left right =+ case+ compareExactVectorAngle+ (exactVectorFromPoints center left)+ (exactVectorFromPoints center right) of+ EQ -> compare left right+ ordering -> ordering++-- | Normalization splits every source at every exact event and coalesces+-- duplicate intervals. Distinct atomics can therefore meet only at a stored+-- endpoint; their entire relation section is the endpoint-incidence index.+atomicEndpointRelations+ :: V.Vector (AtomicEdge leftLabel rightLabel)+ -> Map (ExactSweepSegmentId, ExactSweepSegmentId) SegmentRelation+atomicEndpointRelations edges =+ Map.fromList+ [ ( orderedPair+ (ExactSweepSegmentId leftIndex)+ (ExactSweepSegmentId rightIndex)+ , SegmentsShareEndpoint+ )+ | incident <- Map.elems incidenceByPoint+ , (leftIndex, rightIndex) <- unorderedPairs (Set.toAscList incident)+ ]+ where+ incidenceByPoint =+ V.ifoldl'+ (\incidence index edge ->+ Map.insertWith Set.union (atomicEdgeTo edge) (Set.singleton index)+ ( Map.insertWith Set.union+ (atomicEdgeFrom edge)+ (Set.singleton index)+ incidence+ ))+ Map.empty+ edges+projectedExactPoints+ :: LocalEmbeddingCertificate+ -> Map DraftVertexId ExactPoint+projectedExactPoints =+ Map.map exactPointFromQueryPoint . certificateRoundedVertices++projectedAtomicSegments+ :: V.Vector (AtomicEdge leftLabel rightLabel)+ -> Map ExactPoint DraftVertexId+ -> Map DraftVertexId ExactPoint+ -> Either (OverlayError leftLabel rightLabel) (V.Vector ExactSegment)+projectedAtomicSegments edges pointIds projected =+ V.imapM project edges+ where+ project segmentIndex edge = do+ fromId <- requireDraftVertex pointIds (atomicEdgeFrom edge)+ toId <- requireDraftVertex pointIds (atomicEdgeTo edge)+ from <- requireProjected fromId+ to <- requireProjected toId+ first+ (\_ ->+ OverlayEmbeddingRefused+ ( ProjectedSegmentCollapsed+ (DraftId segmentIndex)+ fromId+ toId+ :| []+ ))+ (exactSegment from to)+ requireProjected vertex =+ case Map.lookup vertex projected of+ Just point -> Right point+ Nothing ->+ Left+ ( OverlayEmbeddingRefused+ (DraftReferenceMissing (DraftVertexReference vertex) :| [])+ )++dischargeGlobalRelations+ :: Map (ExactSweepSegmentId, ExactSweepSegmentId) SegmentRelation+ -> ExactSegmentEventPlan+ -> Either (OverlayError leftLabel rightLabel) ()+dischargeGlobalRelations exactRelations projectedPlan =+ case NonEmpty.nonEmpty obstructions of+ Nothing -> Right ()+ Just failures -> Left (OverlayEmbeddingRefused failures)+ where+ projectedRelations = exactSegmentRelationMap projectedPlan+ keys = Set.toAscList (Map.keysSet exactRelations <> Map.keysSet projectedRelations)+ obstructions = concatMap compareRelation keys+ compareRelation key@(ExactSweepSegmentId leftId, ExactSweepSegmentId rightId) =+ case (Map.lookup key exactRelations, Map.lookup key projectedRelations) of+ (Nothing, Just projected) ->+ [GlobalRelationAdded (DraftId leftId) (DraftId rightId) projected]+ (Just exact, Nothing) ->+ [GlobalRelationRemoved (DraftId leftId) (DraftId rightId) exact]+ (Just exact, Just projected)+ | exact /= projected ->+ [GlobalRelationChanged (DraftId leftId) (DraftId rightId) exact projected]+ _ -> []++overlayVertexSeeds+ :: Map ExactPoint DraftVertexId+ -> Map ExactPoint OverlayVertexOrigin+ -> LocalEmbeddingCertificate+ -> Either (OverlayError leftLabel rightLabel) (V.Vector OverlayVertexSeed)+overlayVertexSeeds pointIds origins certificate =+ V.fromList+ <$> traverse+ (\(point, vertexId) -> do+ embedded <-+ case Map.lookup vertexId (certificateRoundedVertices certificate) of+ Just value -> Right value+ Nothing ->+ Left+ ( OverlayEmbeddingRefused+ (DraftReferenceMissing (DraftVertexReference vertexId) :| [])+ )+ origin <-+ case Map.lookup point origins of+ Just value -> Right value+ Nothing -> Left (OverlayProvenanceIncomplete (OverlayExactVertexMissing point))+ pure (OverlayVertexSeed point (queryPointValue embedded) origin))+ (Map.toAscList pointIds)++atomicConstraints+ :: Map ExactPoint DraftVertexId+ -> V.Vector (AtomicEdge leftLabel rightLabel)+ -> Either (OverlayError leftLabel rightLabel) (V.Vector (Int, Int))+atomicConstraints pointIds =+ V.mapM+ (\edge -> do+ DraftId from <- requireDraftVertex pointIds (atomicEdgeFrom edge)+ DraftId to <- requireDraftVertex pointIds (atomicEdgeTo edge)+ pure (from, to))+++canonicalEdgeKey :: ExactPoint -> ExactPoint -> ExactEdgeKey+canonicalEdgeKey = orderedPair++atomicEdgeFrom :: AtomicEdge leftLabel rightLabel -> ExactPoint+atomicEdgeFrom = fst . exactSegmentEndpoints . atomicEdgeSegment++atomicEdgeTo :: AtomicEdge leftLabel rightLabel -> ExactPoint+atomicEdgeTo = snd . exactSegmentEndpoints . atomicEdgeSegment++atomicKey :: AtomicEdge leftLabel rightLabel -> ExactEdgeKey+atomicKey edge = (atomicEdgeFrom edge, atomicEdgeTo edge)
+ src-overlay/Moonlight/Triangulation/Internal/Overlay/Resident.hs view
@@ -0,0 +1,923 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- | Resident DCEL descent and exact-cell gluing. The certified arrangement is+-- authoritative at this seam; diagonal schedules may change only its resident+-- triangulation, never its exact cell descriptors.+module Moonlight.Triangulation.Internal.Overlay.Resident+ ( OverlayDiagonalSchedule (..)+ , residentOverlay+ , faceLabels+ , faceCarriesExactArea+ , regionFaceLabels+ , vertexSupport+ , edgeSupport+ ) where++import Control.Monad (foldM)+import Data.Bifunctor (first)+import Data.Foldable (traverse_)+import Data.List (partition, sort, sortBy)+import qualified Data.List as List+import qualified Data.List.NonEmpty as NonEmpty+import qualified Data.Map.Strict as Map+import Data.Map.Strict (Map)+import Data.Maybe (listToMaybe, mapMaybe)+import qualified Data.Sequence as Seq+import qualified Data.Set as Set+import Data.Set (Set)+import qualified Data.Vector as V+import qualified Moonlight.Triangulation.Dcel as Dcel+import Moonlight.Triangulation.Dcel+ ( faceData+ , faceDirectedEdges+ , faceVertices+ , imapUndirectedEdges+ , incidentFace+ , isConstraintEdge+ , numInnerFaces+ , numVertices+ , outerFace+ , undirectedEndpoints+ , vertexData+ , vertexOutgoingEdges+ )+import Moonlight.Triangulation.Exact (ExactPoint, exactOrient2d)+import Moonlight.Triangulation.FloodFillIterator+ ( FaceComponent+ , componentBoundary+ , faceComponentFaces+ , faceComponentsBy+ )+import Moonlight.Triangulation.Internal.HandleDefs+ ( DirectedEdgeId+ , FaceId+ , UndirectedEdgeId+ , VertexId+ , asUndirected+ , directedPair+ , reverseEdge+ )+import Moonlight.Triangulation.Handles.Iterators.FixedIterators+ ( allFaces+ , innerFaces+ , undirectedEdges+ )+import Moonlight.Triangulation.Internal.BoxedPaged (boxedFromVector)+import Moonlight.Triangulation.Internal.BoundaryCycle+ ( consecutivePairs+ , traceOrientedBoundaryCircuits+ )+import Moonlight.Triangulation.Internal.Canonical (canonicalize)+import Moonlight.Triangulation.Internal.Cdt.Build (constrainedDelaunay)+import Moonlight.Triangulation.Internal.Cdt.Types (CdtError (..))+import Moonlight.Triangulation.Internal.Overlay.Arrangement+ ( ArrangementMetrics (..)+ , AtomicEdge (..)+ , atomicEdgeFrom+ , atomicEdgeTo+ , CertifiedArrangement (..)+ , ExactEdgeKey+ , OverlayVertexSeed (..)+ , atomicKey+ , canonicalEdgeKey+ , compareAround+ )+import Moonlight.Triangulation.Internal.Overlay.Types+import Moonlight.Triangulation.Internal.Region.Publication+ ( polygonComponentFromBoundaryCoordinates+ )+import Moonlight.Triangulation.Internal.Region.Types+ ( ExactLoop+ , PolygonComponent+ , RegionPublicationError (..)+ )+import Moonlight.Triangulation.Internal.Representation+ ( BuildResult (..)+ , Triangulation (..)+ )+import Moonlight.Triangulation.Internal.Types+ ( ConstraintMode (Constrained)+ , ElementDefaults (..)+ )+import Moonlight.Triangulation.Region (exactLoop)++data OverlayDiagonalSchedule+ = CanonicalOverlayDiagonals+ | FlipFirstAdmissibleDiagonal+ deriving stock (Eq, Ord, Show)++data ComponentDraft leftLabel rightLabel = ComponentDraft+ { componentDraftLabels :: !(leftLabel, rightLabel)+ , componentDraftFaces :: !FaceComponent+ , componentDraftPolygon :: !PolygonComponent+ , componentDraftTouchesOuter :: !Bool+ }++residentOverlay+ :: (Ord leftLabel, Ord rightLabel)+ => OverlayDiagonalSchedule+ -> (leftLabel, rightLabel)+ -> CertifiedArrangement leftLabel rightLabel+ -> Either+ (OverlayError leftLabel rightLabel)+ (OverlayResult leftLabel rightLabel)+residentOverlay diagonalSchedule outsidePair certified = do+ let atomicVector = certifiedAtomicEdges certified+ seeds = certifiedVertexSeeds certified+ constraints = certifiedConstraints certified+ metrics = certifiedMetrics certified+ defaults = ElementDefaults () () ()+ built <- first OverlayBuildFailed (constrainedDelaunay defaults seeds constraints)+ let resident = buildTriangulation built+ if numVertices resident == V.length seeds+ then Right ()+ else+ Left+ ( OverlayProvenanceIncomplete+ (OverlayEmbeddedVertexCountMismatch (V.length seeds) (numVertices resident))+ )+ canonicalResident <-+ first (OverlayBuildFailed . CdtBuildError) (canonicalize resident)+ let atomicByKey =+ Map.fromList+ [ (atomicKey edge, edge)+ | edge <- V.toList atomicVector+ ]+ validateAtomicConstraints canonicalResident atomicByKey+ scheduledResident <-+ applyDiagonalSchedule+ diagonalSchedule+ defaults+ seeds+ constraints+ canonicalResident+ labelledFaces <- labelResidentFaces outsidePair scheduledResident atomicByKey+ (fullDimensionalFaces, collapsedFaces) <-+ if Set.null (certifiedInexactEmbeddingPoints certified)+ then+ Right+ ( Set.fromList (innerFaces scheduledResident)+ , Set.empty+ )+ else+ exactFaceDimensions+ (certifiedInexactEmbeddingPoints certified)+ scheduledResident+ componentDrafts <-+ residentComponentDrafts+ scheduledResident+ labelledFaces+ fullDimensionalFaces+ let (unboundedDrafts, boundedDrafts) =+ partitionDrafts outsidePair componentDrafts+ numberedBounded <- numberBoundedComponents boundedDrafts+ let fullCellIdByFace =+ Map.fromList+ ( [ (face, OverlayCellId 0)+ | draftComponent <- unboundedDrafts+ , face <- faceComponentFaces (componentDraftFaces draftComponent)+ ]+ <> [ (face, cellId)+ | (cellId, draftComponent) <- numberedBounded+ , face <- faceComponentFaces (componentDraftFaces draftComponent)+ ]+ )+ collapsedCellIds <-+ descendCollapsedFaces+ scheduledResident+ atomicByKey+ fullCellIdByFace+ collapsedFaces+ let cellIdByFace = fullCellIdByFace <> collapsedCellIds+ unboundedLoops <-+ unboundedCellBoundaryLoops scheduledResident atomicByKey cellIdByFace+ let cells =+ V.fromList+ ( OverlayCell+ { overlayCellLeft = fst outsidePair+ , overlayCellRight = snd outsidePair+ , overlayCellGeometry = UnboundedOverlayCell unboundedLoops+ }+ : [ OverlayCell+ { overlayCellLeft = fst (componentDraftLabels draftComponent)+ , overlayCellRight = snd (componentDraftLabels draftComponent)+ , overlayCellGeometry =+ BoundedOverlayCell (componentDraftPolygon draftComponent)+ }+ | (_, draftComponent) <- numberedBounded+ ]+ )+ withFaces <- attachFaceCells collapsedFaces cellIdByFace scheduledResident+ let withVertices = attachOverlayVertices withFaces+ withEdges =+ imapUndirectedEdges+ OverlayDiagonal+ (\edge _ ->+ case Map.lookup (residentEdgeKey withVertices edge) atomicByKey of+ Nothing -> OverlayDiagonal+ Just atomic -> OverlayBoundary (atomicEdgeOrigin atomic))+ withVertices+ receipt =+ OverlayReceipt+ { overlayInputSegments = arrangementInputSegments metrics+ , overlayRelationEvents = arrangementRelationEvents metrics+ , overlayExactCrossings = arrangementExactCrossings metrics+ , overlayOverlapIntervals = arrangementOverlapIntervals metrics+ , overlayAtomicEdges = V.length atomicVector+ , overlayOutputVertices = numVertices withEdges+ , overlayArrangementCells = V.length cells+ , overlayResidentFaces = numInnerFaces withEdges+ , overlayEmbeddingCandidates = arrangementEmbeddingCandidates metrics+ , overlayTotalRelationChecks = arrangementTotalRelationChecks metrics+ , overlaySweepMaximumHeight = arrangementSweepMaximumHeight metrics+ }+ pure+ OverlayResult+ { overlayResultTriangulation = withEdges+ , overlayResultCells = cells+ , overlayResultOutsideLabels = outsidePair+ , overlayResultReceipt = receipt+ }++applyDiagonalSchedule+ :: OverlayDiagonalSchedule+ -> ElementDefaults () () ()+ -> V.Vector OverlayVertexSeed+ -> V.Vector (Int, Int)+ -> Triangulation 'Constrained OverlayVertexSeed () () ()+ -> Either+ (OverlayError leftLabel rightLabel)+ (Triangulation 'Constrained OverlayVertexSeed () () ())+applyDiagonalSchedule CanonicalOverlayDiagonals _ _ _ triangulation =+ Right triangulation+applyDiagonalSchedule+ FlipFirstAdmissibleDiagonal+ defaults+ seeds+ constraints+ triangulation =+ case firstAlternativeDiagonal triangulation of+ Nothing -> Right triangulation+ Just (from, to) -> do+ let indexByPoint =+ Map.fromList+ [ (seedExactPoint seed, index)+ | (index, seed) <- V.toList (V.indexed seeds)+ ]+ fromIndex <- requireSeedIndex indexByPoint from+ toIndex <- requireSeedIndex indexByPoint to+ rebuilt <-+ first OverlayBuildFailed+ (constrainedDelaunay defaults seeds (V.snoc constraints (fromIndex, toIndex)))+ first (OverlayBuildFailed . CdtBuildError)+ (canonicalize (buildTriangulation rebuilt))++requireSeedIndex+ :: Map ExactPoint Int+ -> ExactPoint+ -> Either (OverlayError leftLabel rightLabel) Int+requireSeedIndex indexByPoint point =+ case Map.lookup point indexByPoint of+ Just index -> Right index+ Nothing -> Left (OverlayProvenanceIncomplete (OverlayExactVertexMissing point))++firstAlternativeDiagonal+ :: Triangulation 'Constrained OverlayVertexSeed () () ()+ -> Maybe ExactEdgeKey+firstAlternativeDiagonal triangulation =+ listToMaybe+ (mapMaybe (alternativeDiagonal triangulation) (undirectedEdges triangulation))++alternativeDiagonal+ :: Triangulation 'Constrained OverlayVertexSeed () () ()+ -> UndirectedEdgeId+ -> Maybe ExactEdgeKey+alternativeDiagonal triangulation edge+ | isConstraintEdge triangulation edge = Nothing+ | leftFace == outerFace || rightFace == outerFace = Nothing+ | exactOrient2d c d b == GT && exactOrient2d d c a == GT =+ Just (canonicalEdgeKey c d)+ | otherwise = Nothing+ where+ (forward, backward) = directedPair edge+ leftFace = incidentFace triangulation forward+ rightFace = incidentFace triangulation backward+ a = seedExactPoint (vertexData triangulation (Dcel.origin triangulation forward))+ b = seedExactPoint (vertexData triangulation (Dcel.origin triangulation backward))+ c =+ seedExactPoint+ (vertexData triangulation (Dcel.origin triangulation (Dcel.previous triangulation forward)))+ d =+ seedExactPoint+ (vertexData triangulation (Dcel.origin triangulation (Dcel.previous triangulation backward)))+labelResidentFaces+ :: (Ord leftLabel, Ord rightLabel)+ => (leftLabel, rightLabel)+ -> Triangulation 'Constrained OverlayVertexSeed () () ()+ -> Map ExactEdgeKey (AtomicEdge leftLabel rightLabel)+ -> Either+ (OverlayError leftLabel rightLabel)+ (Map FaceId (leftLabel, rightLabel))+labelResidentFaces outsideLabels triangulation atomicByKey = do+ labelled <-+ descendFaceTransitions+ triangulation+ atomicByKey+ (Map.singleton outerFace outsideLabels)+ (Seq.singleton outerFace)+ case+ [ face+ | face <- innerFaces triangulation+ , Map.notMember face labelled+ ] of+ missing : _ ->+ Left (OverlayProvenanceIncomplete (OverlayResidentFaceUnassigned missing))+ [] -> Right (Map.delete outerFace labelled)++seedDirectedEndpoints+ :: Triangulation mode OverlayVertexSeed directed undirected face+ -> DirectedEdgeId+ -> ExactEdgeKey+seedDirectedEndpoints triangulation edge =+ ( seedExactPoint (vertexData triangulation (Dcel.origin triangulation edge))+ , seedExactPoint (vertexData triangulation (Dcel.destination triangulation edge))+ )++swapTransition :: (label, label) -> (label, label)+swapTransition (fromLabel, toLabel) = (toLabel, fromLabel)++descendFaceTransitions+ :: (Eq leftLabel, Eq rightLabel)+ => Triangulation 'Constrained OverlayVertexSeed () () ()+ -> Map ExactEdgeKey (AtomicEdge leftLabel rightLabel)+ -> Map FaceId (leftLabel, rightLabel)+ -> Seq.Seq FaceId+ -> Either+ (OverlayError leftLabel rightLabel)+ (Map FaceId (leftLabel, rightLabel))+descendFaceTransitions triangulation atomicByKey labelled queued =+ case Seq.viewl queued of+ Seq.EmptyL -> Right labelled+ face Seq.:< remaining ->+ case Map.lookup face labelled of+ Nothing ->+ Left (OverlayProvenanceIncomplete (OverlayResidentFaceUnassigned face))+ Just current -> do+ (nextLabels, nextQueue) <-+ foldM+ (descendFaceTransition triangulation atomicByKey face current)+ (labelled, remaining)+ (faceDirectedEdges triangulation face)+ descendFaceTransitions triangulation atomicByKey nextLabels nextQueue++descendFaceTransition+ :: (Eq leftLabel, Eq rightLabel)+ => Triangulation 'Constrained OverlayVertexSeed () () ()+ -> Map ExactEdgeKey (AtomicEdge leftLabel rightLabel)+ -> FaceId+ -> (leftLabel, rightLabel)+ -> (Map FaceId (leftLabel, rightLabel), Seq.Seq FaceId)+ -> DirectedEdgeId+ -> Either+ (OverlayError leftLabel rightLabel)+ (Map FaceId (leftLabel, rightLabel), Seq.Seq FaceId)+descendFaceTransition triangulation atomicByKey source current (labelled, queued) directed = do+ let edge = asUndirected directed+ destinationFace = incidentFace triangulation (reverseEdge directed)+ (leftTransition, rightTransition) <-+ case Map.lookup (residentSeedEdgeKey triangulation edge) atomicByKey of+ Nothing -> Right (Nothing, Nothing)+ Just atomic -> orientedTransitions edge directed atomic+ let+ expected =+ ( maybe (fst current) fst leftTransition+ , maybe (snd current) fst rightTransition+ )+ derived =+ ( maybe (fst current) snd leftTransition+ , maybe (snd current) snd rightTransition+ )+ if current == expected+ then+ case Map.lookup destinationFace labelled of+ Nothing ->+ Right+ ( Map.insert destinationFace derived labelled+ , queued Seq.|> destinationFace+ )+ Just existing+ | existing == derived -> Right (labelled, queued)+ | otherwise ->+ Left+ ( OverlayArrangementInvalid+ ( OverlayResidentFaceLabelConflict+ destinationFace+ edge+ existing+ derived+ )+ )+ else+ Left+ ( OverlayArrangementInvalid+ (OverlayTransitionSourceMismatch source edge current expected)+ )+ where+ orientedTransitions edge candidateDirection atomic+ | directedEndpoints == (atomicEdgeFrom atomic, atomicEdgeTo atomic) =+ Right+ ( atomicEdgeLeftTransition atomic+ , atomicEdgeRightTransition atomic+ )+ | directedEndpoints == (atomicEdgeTo atomic, atomicEdgeFrom atomic) =+ Right+ ( swapTransition <$> atomicEdgeLeftTransition atomic+ , swapTransition <$> atomicEdgeRightTransition atomic+ )+ | otherwise =+ Left+ ( OverlayProvenanceIncomplete+ ( OverlayAtomicConstraintOrientationMismatch+ edge+ (atomicEdgeFrom atomic)+ (atomicEdgeTo atomic)+ )+ )+ where+ directedEndpoints = seedDirectedEndpoints triangulation candidateDirection++exactFaceDimensions+ :: Set ExactPoint+ -> Triangulation 'Constrained OverlayVertexSeed directed undirected face+ -> Either+ (OverlayError leftLabel rightLabel)+ (Set FaceId, Set FaceId)+exactFaceDimensions inexactEmbeddingPoints triangulation = do+ orientations <-+ traverse+ ( \face ->+ (face,)+ <$> exactFaceOrientation+ inexactEmbeddingPoints+ triangulation+ face+ )+ (innerFaces triangulation)+ case [face | (face, LT) <- orientations] of+ reversed : _ ->+ Left+ ( OverlayArrangementInvalid+ (OverlayResidentFaceOrientationReversed reversed)+ )+ [] ->+ Right+ ( Set.fromList [face | (face, GT) <- orientations]+ , Set.fromList [face | (face, EQ) <- orientations]+ )++exactFaceOrientation+ :: Set ExactPoint+ -> Triangulation mode OverlayVertexSeed directed undirected face+ -> FaceId+ -> Either (OverlayError leftLabel rightLabel) Ordering+exactFaceOrientation inexactEmbeddingPoints triangulation face =+ case+ map+ (seedExactPoint . vertexData triangulation)+ (faceVertices triangulation face) of+ points@[firstPoint, secondPoint, thirdPoint]+ | any (`Set.member` inexactEmbeddingPoints) points ->+ Right (exactOrient2d firstPoint secondPoint thirdPoint)+ | otherwise -> Right GT+ vertices ->+ Left+ ( OverlayArrangementInvalid+ (OverlayResidentFaceArity face (length vertices))+ )++residentComponentDrafts+ :: forall leftLabel rightLabel.+ (Ord leftLabel, Ord rightLabel)+ => Triangulation 'Constrained OverlayVertexSeed () () ()+ -> Map FaceId (leftLabel, rightLabel)+ -> Set FaceId+ -> Either+ (OverlayError leftLabel rightLabel)+ [ComponentDraft leftLabel rightLabel]+residentComponentDrafts triangulation labels fullDimensionalFaces =+ traverse+ convert+ (faceComponentsBy triangulation labelFullDimensionalFace (const True))+ where+ exactPointAt :: VertexId -> Either RegionPublicationError ExactPoint+ exactPointAt vertex =+ Right (seedExactPoint (vertexData triangulation vertex))+ labelFullDimensionalFace :: FaceId -> Maybe (leftLabel, rightLabel)+ labelFullDimensionalFace face+ | Set.member face fullDimensionalFaces = Map.lookup face labels+ | otherwise = Nothing+ convert+ :: ((leftLabel, rightLabel), FaceComponent)+ -> Either+ (OverlayError leftLabel rightLabel)+ (ComponentDraft leftLabel rightLabel)+ convert (componentLabels, component) = do+ boundary <-+ first+ (OverlayRegionPublicationFailed . RegionBoundaryObstruction)+ (componentBoundary triangulation component)+ polygon <-+ first OverlayRegionPublicationFailed+ (polygonComponentFromBoundaryCoordinates exactPointAt boundary)+ pure+ ComponentDraft+ { componentDraftLabels = componentLabels+ , componentDraftFaces = component+ , componentDraftPolygon = polygon+ , componentDraftTouchesOuter = touchesOuter triangulation component+ }++touchesOuter+ :: Triangulation mode vertex directed undirected face+ -> FaceComponent+ -> Bool+touchesOuter triangulation component =+ any+ ( any ((== outerFace) . incidentFace triangulation . reverseEdge)+ . faceDirectedEdges triangulation+ )+ (faceComponentFaces component)++partitionDrafts+ :: (Eq leftLabel, Eq rightLabel)+ => (leftLabel, rightLabel)+ -> [ComponentDraft leftLabel rightLabel]+ -> ([ComponentDraft leftLabel rightLabel], [ComponentDraft leftLabel rightLabel])+partitionDrafts outsidePair =+ partition+ (\draftComponent ->+ componentDraftTouchesOuter draftComponent+ && componentDraftLabels draftComponent == outsidePair)++sortComponentDrafts+ :: (Ord leftLabel, Ord rightLabel)+ => [ComponentDraft leftLabel rightLabel]+ -> [ComponentDraft leftLabel rightLabel]+sortComponentDrafts =+ sortBy+ (\left right ->+ compare+ (componentDraftPolygon left, componentDraftLabels left)+ (componentDraftPolygon right, componentDraftLabels right))++numberBoundedComponents+ :: (Ord leftLabel, Ord rightLabel)+ => [ComponentDraft leftLabel rightLabel]+ -> Either+ (OverlayError leftLabel rightLabel)+ [(OverlayCellId, ComponentDraft leftLabel rightLabel)]+numberBoundedComponents drafts =+ case+ [ componentDraftPolygon left+ | (left, right) <- consecutivePairs ordered+ , componentSignature left == componentSignature right+ ] of+ duplicate : _ ->+ Left+ ( OverlayArrangementInvalid+ (OverlayDuplicateCellSignature duplicate)+ )+ [] ->+ Right+ ( zipWith+ (\index component -> (OverlayCellId index, component))+ [1 ..]+ ordered+ )+ where+ ordered = sortComponentDrafts drafts+ componentSignature+ :: ComponentDraft leftLabel' rightLabel'+ -> (PolygonComponent, (leftLabel', rightLabel'))+ componentSignature component =+ (componentDraftPolygon component, componentDraftLabels component)++-- | A binary64 triangle whose authoritative vertices are collinear is not an+-- exact two-cell. It may remain in the resident triangulation only when its+-- representation diagonals descend uniquely to one full-dimensional exact+-- cell; this prevents the artifact from gluing exact components through a+-- zero-area wedge.+descendCollapsedFaces+ :: forall leftLabel rightLabel.+ Triangulation 'Constrained OverlayVertexSeed () () ()+ -> Map ExactEdgeKey (AtomicEdge leftLabel rightLabel)+ -> Map FaceId OverlayCellId+ -> Set FaceId+ -> Either+ (OverlayError leftLabel rightLabel)+ (Map FaceId OverlayCellId)+descendCollapsedFaces triangulation atomicByKey fullCellIds collapsedFaces =+ Map.fromList . concat <$> traverse resolveComponent collapsedComponents+ where+ exactCellIds = Map.insert outerFace (OverlayCellId 0) fullCellIds+ collapsedComponents =+ fmap snd+ ( faceComponentsBy+ triangulation+ labelCollapsedFace+ isRepresentationDiagonal+ )++ labelCollapsedFace face+ | Set.member face collapsedFaces = Just ()+ | otherwise = Nothing++ isRepresentationDiagonal edge =+ Map.notMember (residentSeedEdgeKey triangulation edge) atomicByKey++ resolveComponent component =+ case faceComponentFaces component of+ [] ->+ Left+ ( OverlayArrangementInvalid+ OverlayFaceComponentEmpty+ )+ componentFaces@(firstFace : remainingFaces) -> do+ let componentWitness = firstFace NonEmpty.:| remainingFaces+ adjacentCellIds =+ Set.fromList+ [ cellId+ | face <- componentFaces+ , directed <- faceDirectedEdges triangulation face+ , isRepresentationDiagonal (asUndirected directed)+ , Just cellId <-+ [Map.lookup (incidentFace triangulation (reverseEdge directed)) exactCellIds]+ ]+ cellId <-+ case Set.toAscList adjacentCellIds of+ [singleCellId] -> Right singleCellId+ [] ->+ Left+ ( OverlayArrangementInvalid+ (OverlayCollapsedFacesUnowned componentWitness)+ )+ firstCellId : remainingCellIds ->+ Left+ ( OverlayArrangementInvalid+ ( OverlayCollapsedFacesAmbiguous+ componentWitness+ (firstCellId NonEmpty.:| remainingCellIds)+ )+ )+ pure (fmap (,cellId) componentFaces)++-- | Trace the finite boundary cycles of the unbounded exact cell from atomic+-- edges only. Resident hull edges and Delaunay diagonals are absent by+-- construction; the DCEL contributes only the already-proved incident-cell+-- gluing needed to orient each atomic edge with cell zero on its left.+unboundedCellBoundaryLoops+ :: Triangulation 'Constrained OverlayVertexSeed () () ()+ -> Map ExactEdgeKey (AtomicEdge leftLabel rightLabel)+ -> Map FaceId OverlayCellId+ -> Either (OverlayError leftLabel rightLabel) [ExactLoop]+unboundedCellBoundaryLoops triangulation atomicByKey cellIdByFace = do+ orientedEdges <-+ Set.fromList . concat+ <$> traverse orientAtomicEdge (undirectedEdges triangulation)+ traceBoundaryCycles orientedEdges+ where+ orientAtomicEdge edge+ | Map.notMember (residentSeedEdgeKey triangulation edge) atomicByKey = Right []+ | otherwise = do+ let (forward, backward) = directedPair edge+ forwardCell <- faceCellId (incidentFace triangulation forward)+ backwardCell <- faceCellId (incidentFace triangulation backward)+ pure+ ( case (forwardCell == OverlayCellId 0, backwardCell == OverlayCellId 0) of+ (True, False) -> [seedDirectedEndpoints triangulation forward]+ (False, True) -> [seedDirectedEndpoints triangulation backward]+ _ -> []+ )+ faceCellId face+ | face == outerFace = Right (OverlayCellId 0)+ | otherwise =+ case Map.lookup face cellIdByFace of+ Just cellId -> Right cellId+ Nothing -> Left (OverlayProvenanceIncomplete (OverlayResidentFaceUnassigned face))++traceBoundaryCycles+ :: Set ExactEdgeKey+ -> Either (OverlayError leftLabel rightLabel) [ExactLoop]+traceBoundaryCycles orientedEdges = do+ pointCycles <-+ traceOrientedBoundaryCircuits+ fst+ snd+ (\seed _ ->+ OverlayArrangementInvalid+ (uncurry OverlayCellCycleDidNotClose seed))+ outgoing+ orientedEdges+ sort+ <$> traverse+ ( first+ (OverlayRegionPublicationFailed . RegionValidationObstruction)+ . exactLoop+ )+ pointCycles+ where+ outgoing =+ Map.mapWithKey orderAroundOrigin+ ( Set.foldl'+ (\byOrigin edge@(from, _) -> Map.insertWith (<>) from [edge] byOrigin)+ Map.empty+ orientedEdges+ )+ orderAroundOrigin :: ExactPoint -> [ExactEdgeKey] -> [ExactEdgeKey]+ orderAroundOrigin origin =+ sortBy (\(_, left) (_, right) -> compareAround origin left right)++attachFaceCells+ :: forall leftLabel rightLabel.+ Set FaceId+ -> Map FaceId OverlayCellId+ -> Triangulation 'Constrained OverlayVertexSeed () () ()+ -> Either+ (OverlayError leftLabel rightLabel)+ (Triangulation 'Constrained OverlayVertexSeed () () OverlayFace)+attachFaceCells collapsedFaces cellIdByFace triangulation = do+ payloads <- V.fromList <$> traverse facePayload (allFaces triangulation)+ let outerPayload = OverlayCellFace (OverlayCellId 0)+ defaults = triElementDefaults triangulation+ pure+ triangulation+ { triFaceData = boxedFromVector (Just outerPayload) payloads+ , triElementDefaults = defaults{defaultFaceData = outerPayload}+ }+ where+ facePayload+ :: FaceId+ -> Either (OverlayError leftLabel rightLabel) OverlayFace+ facePayload face+ | face == outerFace = Right (OverlayCellFace (OverlayCellId 0))+ | otherwise =+ case Map.lookup face cellIdByFace of+ Just cellId+ | Set.member face collapsedFaces ->+ Right (OverlayCollapsedFace cellId)+ | otherwise -> Right (OverlayCellFace cellId)+ Nothing ->+ Left+ ( OverlayProvenanceIncomplete+ (OverlayResidentFaceUnassigned face)+ )++attachOverlayVertices+ :: Triangulation 'Constrained OverlayVertexSeed () () face+ -> Triangulation 'Constrained OverlayVertex () () face+attachOverlayVertices =+ Dcel.mapVertices+ (\seed -> OverlayVertex (seedExactPoint seed) (seedOrigin seed))++cellPayload+ :: V.Vector (OverlayCell leftLabel rightLabel)+ -> OverlayCellId+ -> Either OverlayCellWitness (OverlayCell leftLabel rightLabel)+cellPayload cells cellId@(OverlayCellId index) =+ case cells V.!? index of+ Just cell -> Right cell+ Nothing -> Left (OverlayCellPayloadMissing cellId)++faceLabels+ :: OverlayResult leftLabel rightLabel+ -> FaceId+ -> Either OverlayCellWitness (leftLabel, rightLabel)+faceLabels result face = do+ cell <-+ cellPayload+ (overlayResultCells result)+ (overlayFaceCellId (faceData (overlayResultTriangulation result) face))+ pure (overlayCellLeft cell, overlayCellRight cell)++-- | Whether a resident triangle has nonzero area under the authoritative+-- exact coordinate section. Binary64-only wedges are representation charts,+-- never exact two-cells.+faceCarriesExactArea+ :: OverlayResult leftLabel rightLabel+ -> FaceId+ -> Bool+faceCarriesExactArea result face =+ case faceData (overlayResultTriangulation result) face of+ OverlayCellFace {} -> True+ OverlayCollapsedFace {} -> False++regionFaceLabels+ :: OverlayResult leftLabel rightLabel+ -> FaceId+ -> Either RegionPublicationError (leftLabel, rightLabel)+regionFaceLabels result face =+ case faceLabels result face of+ Right labels -> Right labels+ Left _ -> Left (RegionFaceLabelMissing face)++vertexSupport+ :: (Ord leftLabel, Ord rightLabel)+ => OverlayResult leftLabel rightLabel+ -> VertexId+ -> Either OverlayCellWitness (OverlayCellSupport leftLabel rightLabel)+vertexSupport result vertex = do+ labels <-+ traverse+ (faceLabels result . incidentFace triangulation)+ (vertexOutgoingEdges triangulation vertex)+ case supportFromPairs labels of+ Just support -> Right support+ Nothing -> Left (OverlayVertexSupportMissing vertex)+ where+ triangulation = overlayResultTriangulation result++edgeSupport+ :: (Ord leftLabel, Ord rightLabel)+ => OverlayResult leftLabel rightLabel+ -> UndirectedEdgeId+ -> Either OverlayCellWitness (OverlayCellSupport leftLabel rightLabel)+edgeSupport result edge = do+ pairs <- traverse (faceLabels result . incidentFace triangulation) [forward, backward]+ case supportFromPairs pairs of+ Just support -> Right support+ Nothing -> Left (OverlayEdgeSupportMissing edge)+ where+ triangulation = overlayResultTriangulation result+ (forward, backward) = directedPair edge++supportFromPairs+ :: (Ord leftLabel, Ord rightLabel)+ => [(leftLabel, rightLabel)]+ -> Maybe (OverlayCellSupport leftLabel rightLabel)+supportFromPairs pairs = do+ let (leftLabels, rightLabels) =+ List.foldl'+ (\(left, right) (leftLabel, rightLabel) ->+ (Set.insert leftLabel left, Set.insert rightLabel right))+ (Set.empty, Set.empty)+ pairs+ leftSupport <- nonEmptySupport leftLabels+ rightSupport <- nonEmptySupport rightLabels+ pure (OverlayCellSupport leftSupport rightSupport)++nonEmptySupport :: Set label -> Maybe (OverlaySupport label)+nonEmptySupport labels = OverlaySupport <$> NonEmpty.nonEmpty (Set.toAscList labels)++validateAtomicConstraints+ :: Triangulation+ 'Constrained+ OverlayVertexSeed+ ()+ ()+ ()+ -> Map ExactEdgeKey (AtomicEdge leftLabel rightLabel)+ -> Either (OverlayError leftLabel rightLabel) ()+validateAtomicConstraints triangulation atomicByKey = do+ traverse_ requireAtomic (Map.keys atomicByKey)+ traverse_ requireExpectedConstraint (undirectedEdges triangulation)+ where+ residentByKey =+ Map.fromList+ [ (residentSeedEdgeKey triangulation edge, edge)+ | edge <- undirectedEdges triangulation+ ]+ requireAtomic key@(from, to) =+ case Map.lookup key residentByKey of+ Nothing -> Left (OverlayProvenanceIncomplete (OverlayAtomicConstraintMissing from to))+ Just edge+ | isConstraintEdge triangulation edge -> Right ()+ | otherwise -> Left (OverlayProvenanceIncomplete (OverlayBoundaryEdgeNotConstrained edge))+ requireExpectedConstraint edge+ | isConstraintEdge triangulation edge+ && Map.notMember (residentSeedEdgeKey triangulation edge) atomicByKey =+ Left (OverlayProvenanceIncomplete (OverlayUnexpectedConstraint edge))+ | otherwise = Right ()++residentSeedEdgeKey+ :: Triangulation mode OverlayVertexSeed directed undirected face+ -> UndirectedEdgeId+ -> ExactEdgeKey+residentSeedEdgeKey = residentEdgeKeyBy seedExactPoint++residentEdgeKey+ :: Triangulation mode OverlayVertex directed undirected face+ -> UndirectedEdgeId+ -> ExactEdgeKey+residentEdgeKey = residentEdgeKeyBy overlayExactPoint++residentEdgeKeyBy+ :: (vertex -> ExactPoint)+ -> Triangulation mode vertex directed undirected face+ -> UndirectedEdgeId+ -> ExactEdgeKey+residentEdgeKeyBy exactPointAt triangulation edge =+ let (from, to) = undirectedEndpoints triangulation edge+ in canonicalEdgeKey+ (exactPointAt (vertexData triangulation from))+ (exactPointAt (vertexData triangulation to))
+ src-overlay/Moonlight/Triangulation/Internal/Overlay/Types.hs view
@@ -0,0 +1,274 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE KindSignatures #-}++-- | Closed vocabulary and opaque carrier representation for exact labelled+-- overlay. Construction lives in the public build-tier module; this module+-- exists so every invariant-bearing payload shares one owner.+module Moonlight.Triangulation.Internal.Overlay.Types+ ( BoundaryLoopRef (..)+ , OverlayOperand (..)+ , BoundaryRef (..)+ , BoundaryVertexRef+ , BoundaryEdgeRef+ , OverlayVertexOrigin (..)+ , OverlayEdgeOrigin (..)+ , OverlaySupport (..)+ , overlaySupportLabels+ , OverlayCellSupport (..)+ , OverlayCellId (..)+ , OverlayCellGeometry (..)+ , OverlayCell (..)+ , OverlayFace (..)+ , OverlayVertex (..)+ , OverlayEdge (..)+ , OverlayReceipt (..)+ , OverlayArrangementObstruction (..)+ , OverlayCellWitness (..)+ , OverlayError (..)+ , OverlayResult (..)+ , OverlaySelectionKind (..)+ , OverlaySelectionError (..)+ ) where++import Control.DeepSeq (NFData)+import Data.List.NonEmpty (NonEmpty)+import Data.Vector (Vector)+import GHC.Generics (Generic)+import Moonlight.Triangulation.CellSet (CellSelectionError)+import Moonlight.Triangulation.Exact+ ( ExactPoint+ )+import Moonlight.Triangulation.Internal.HandleDefs+ ( FaceId+ , UndirectedEdgeId+ , VertexId+ )+import Moonlight.Triangulation.Internal.Cdt.Types (CdtError)+import Moonlight.Triangulation.Internal.ExactRational (ExactArithmeticError)+import Moonlight.Triangulation.Internal.ExactSegmentEvents (ExactSegmentEventObstruction)+import Moonlight.Triangulation.Embedding (OverlayEmbeddingObstruction)+import Moonlight.Triangulation.Internal.Region.Types+ ( ExactLoop+ , PolygonComponent+ , RegionPublicationError+ )+import Moonlight.Triangulation.Internal.Representation (Triangulation)+import Moonlight.Triangulation.Internal.Types+ ( ConstraintMode (Constrained)+ )++-- | Which cycle inside a polygon component supplied a source reference.+data BoundaryLoopRef+ = BoundaryOuterLoop+ | BoundaryHoleLoop !Int+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++data OverlayOperand+ = LeftOverlayOperand+ | RightOverlayOperand++-- | Phantom source cell kind; both provenance axes share one physical payload.+data BoundaryFeature = BoundaryVertexFeature | BoundaryEdgeFeature++data BoundaryRef (feature :: BoundaryFeature) (operand :: OverlayOperand) = BoundaryRef+ { boundaryRefComponent :: !Int+ , boundaryRefLoop :: !BoundaryLoopRef+ , boundaryRefLocalIndex :: !Int+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++type BoundaryVertexRef operand = BoundaryRef 'BoundaryVertexFeature operand+type BoundaryEdgeRef operand = BoundaryRef 'BoundaryEdgeFeature operand++-- | Complete typed source closure of one arrangement vertex.+data OverlayVertexOrigin = OverlayVertexOrigin+ { overlayOriginLeftVertices :: ![BoundaryVertexRef 'LeftOverlayOperand]+ , overlayOriginRightVertices :: ![BoundaryVertexRef 'RightOverlayOperand]+ , overlayOriginLeftEdges :: ![BoundaryEdgeRef 'LeftOverlayOperand]+ , overlayOriginRightEdges :: ![BoundaryEdgeRef 'RightOverlayOperand]+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | Nonempty typed source-edge provenance of one exact atomic interval.+data OverlayEdgeOrigin = OverlayEdgeOrigin+ { overlayEdgeLeftSources :: ![BoundaryEdgeRef 'LeftOverlayOperand]+ , overlayEdgeRightSources :: ![BoundaryEdgeRef 'RightOverlayOperand]+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | Sorted nonempty labels whose closures contain one relatively open cell.+newtype OverlaySupport label = OverlaySupport (NonEmpty label)+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++overlaySupportLabels :: OverlaySupport label -> NonEmpty label+overlaySupportLabels (OverlaySupport labels) = labels++data OverlayCellSupport leftLabel rightLabel = OverlayCellSupport+ { overlaySupportLeft :: !(OverlaySupport leftLabel)+ , overlaySupportRight :: !(OverlaySupport rightLabel)+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++newtype OverlayCellId = OverlayCellId Int+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++data OverlayCellGeometry+ = BoundedOverlayCell !PolygonComponent+ | UnboundedOverlayCell ![ExactLoop]+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++data OverlayCell leftLabel rightLabel = OverlayCell+ { overlayCellLeft :: !leftLabel+ , overlayCellRight :: !rightLabel+ , overlayCellGeometry :: !OverlayCellGeometry+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | A resident triangle either realizes an exact two-cell or collapses under+-- the authoritative exact coordinates. A collapsed triangle descends to the+-- unique adjacent exact cell through representation diagonals, but exact+-- region operations must not mistake its binary64 area for exact area.+data OverlayFace+ = OverlayCellFace+ { overlayFaceCellId :: !OverlayCellId+ }+ | OverlayCollapsedFace+ { overlayFaceCellId :: !OverlayCellId+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | Exact geometry and provenance are intrinsic to a vertex. Label support is+-- the finite union of incident cell descriptors and is therefore a view.+data OverlayVertex = OverlayVertex+ { overlayExactPoint :: !ExactPoint+ , overlayVertexOrigin :: !OverlayVertexOrigin+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | Boundary provenance is intrinsic; label support is derived from the two+-- incident face-cell references.+data OverlayEdge+ = OverlayBoundary !OverlayEdgeOrigin+ | OverlayDiagonal+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++data OverlayReceipt = OverlayReceipt+ { overlayInputSegments :: !Int+ , overlayRelationEvents :: !Int+ , overlayExactCrossings :: !Int+ , overlayOverlapIntervals :: !Int+ , overlayAtomicEdges :: !Int+ , overlayOutputVertices :: !Int+ , overlayArrangementCells :: !Int+ , overlayResidentFaces :: !Int+ , overlayEmbeddingCandidates :: !Int+ , overlayTotalRelationChecks :: !Int+ , overlaySweepMaximumHeight :: !Int+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++data OverlayArrangementObstruction leftLabel rightLabel+ = OverlayLeftSourceSideConflict+ !OverlayEdgeOrigin+ !(NonEmpty leftLabel)+ | OverlayRightSourceSideConflict+ !OverlayEdgeOrigin+ !(NonEmpty rightLabel)+ | OverlayRotationDegenerate !ExactPoint+ | OverlayFaceComponentEmpty+ | OverlayCellCycleDidNotClose !ExactPoint !ExactPoint+ | OverlayTransitionSourceMismatch+ !FaceId+ !UndirectedEdgeId+ !(leftLabel, rightLabel)+ !(leftLabel, rightLabel)+ | OverlayResidentFaceLabelConflict+ !FaceId+ !UndirectedEdgeId+ !(leftLabel, rightLabel)+ !(leftLabel, rightLabel)+ | OverlayResidentFaceArity !FaceId !Int+ | OverlayResidentFaceOrientationReversed !FaceId+ | OverlayCollapsedFacesUnowned !(NonEmpty FaceId)+ | OverlayCollapsedFacesAmbiguous+ !(NonEmpty FaceId)+ !(NonEmpty OverlayCellId)+ | OverlayDuplicateCellSignature !PolygonComponent+ deriving stock (Eq, Show, Generic)+ deriving anyclass (NFData)++data OverlayCellWitness+ = OverlayAtomicConstraintMissing !ExactPoint !ExactPoint+ | OverlayAtomicConstraintOrientationMismatch+ !UndirectedEdgeId+ !ExactPoint+ !ExactPoint+ | OverlayUnexpectedConstraint !UndirectedEdgeId+ | OverlayBoundaryEdgeNotConstrained !UndirectedEdgeId+ | OverlayResidentFaceUnassigned !FaceId+ | OverlayCellPayloadMissing !OverlayCellId+ | OverlayVertexSupportMissing !VertexId+ | OverlayEdgeSupportMissing !UndirectedEdgeId+ | OverlayEmbeddedVertexCountMismatch !Int !Int+ | OverlayExactVertexMissing !ExactPoint+ deriving stock (Eq, Show, Generic)+ deriving anyclass (NFData)++data OverlayError leftLabel rightLabel+ = OverlayExactArithmetic !ExactArithmeticError+ | OverlaySegmentEventsInvalid !ExactSegmentEventObstruction+ | OverlayArrangementInvalid !(OverlayArrangementObstruction leftLabel rightLabel)+ | OverlayEmbeddingRefused !(NonEmpty OverlayEmbeddingObstruction)+ | OverlayBuildFailed !CdtError+ | OverlayRegionPublicationFailed !RegionPublicationError+ | OverlayProvenanceIncomplete !OverlayCellWitness+ deriving stock (Eq, Show, Generic)+ deriving anyclass (NFData)++-- | The singular exact subdivision carrier. Its embedded DCEL is a derived+-- binary64 realization; exact coordinates remain in the vertex payload plane.+data OverlayResult leftLabel rightLabel = OverlayResult+ { overlayResultTriangulation+ :: !( Triangulation+ 'Constrained+ OverlayVertex+ ()+ OverlayEdge+ OverlayFace+ )+ , overlayResultCells :: !(Vector (OverlayCell leftLabel rightLabel))+ , overlayResultOutsideLabels :: !(leftLabel, rightLabel)+ , overlayResultReceipt :: !OverlayReceipt+ }+ deriving stock (Generic)+ deriving anyclass (NFData)++data OverlaySelectionKind+ = ClosedUnionSelection+ | ClosedIntersectionSelection+ | RegularizedDifferenceSelection+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++data OverlaySelectionError+ = OverlaySelectionContainsUnboundedCell !OverlaySelectionKind+ | OverlaySelectionProvenance !OverlayCellWitness+ | OverlaySelectionInvalid !CellSelectionError+ deriving stock (Eq, Show, Generic)+ deriving anyclass (NFData)
src-parallel/Moonlight/Triangulation/Parallel.hs view
@@ -13,11 +13,12 @@ import Control.Exception (evaluate) import Data.List.NonEmpty (NonEmpty) import Moonlight.Triangulation.Internal.Join+ ( joinNormalForm )+import Moonlight.Triangulation.Internal.Representation (Triangulation)+import Moonlight.Triangulation.Internal.Tournament ( TournamentPlan (..)- , joinNormalForm , planTournament )-import Moonlight.Triangulation.Internal.Representation (Triangulation) import Moonlight.Triangulation.Internal.Types (BuildError, ConstraintMode (Unconstrained)) import Moonlight.Triangulation.JoinSemilattice (JoinSemilattice)
src-public/Moonlight/Triangulation.hs view
@@ -32,10 +32,10 @@ , JoinSemilattice (..) , Coordinate2 (..) , mapCoordinate2- -- | An identifier is admitted by the triangulation that issued it. The- -- observations below index without a second bounds check, so the- -- constructors are withheld and the projections are not: an identifier- -- can be read, compared and carried, and can only be obtained from a mesh.+ -- | Fixed identifiers are compact resident indices. Their observations+ -- deliberately avoid a second bounds check and therefore require the+ -- issuing mesh; use the scoped section below or owning handles whenever+ -- mesh provenance is not already carried by the surrounding type. , VertexId , unVertexId , FaceId@@ -51,6 +51,42 @@ , directedPair , isNormalized + -- * Mesh-scoped observations — zero-cost nominal ownership; identifiers+ -- from distinct scopes cannot be mixed+ , ScopedTriangulation+ , ScopedVertex+ , ScopedDirectedEdge+ , ScopedUndirectedEdge+ , ScopedFace+ , withScopedTriangulation+ , scopedVertices+ , scopedDirectedEdges+ , scopedUndirectedEdges+ , scopedFaces+ , scopedInnerFaces+ , scopedOuterFace+ , scopedVertexPoint+ , scopedVertexData+ , scopedDirectedEdgeData+ , scopedUndirectedEdgeData+ , scopedFaceData+ , scopedVertexOutEdge+ , scopedAdjacentEdge+ , scopedOrigin+ , scopedDestination+ , scopedReverseEdge+ , scopedAsUndirected+ , scopedNext+ , scopedPrevious+ , scopedIncidentFace+ , scopedIsOuterDirectedEdge+ , scopedIsBoundaryEdge+ , scopedIsConstraintEdge+ , scopedUndirectedEndpoints+ , scopedFaceDirectedEdges+ , scopedFaceVertices+ , scopedVertexOutgoingEdges+ -- * Generation and editing — persistent verbs publish one new value; -- sessions compose many edits behind one sealed thaw/freeze boundary , empty@@ -381,6 +417,10 @@ , exactCross , compareExactVectorAngle , translateExactPoint+ , ExactRay+ , exactRay+ , exactRayOrigin+ , exactRayDirection , ExactSegment , ExactGeometryError (..) , exactSegment@@ -432,7 +472,11 @@ , OverlayError (..) , OverlaySelectionKind (..) , OverlaySelectionError (..)+ , CoverGap+ , coverGapRegion+ , LayerCoverageError (..) , overlayLayers+ , overlayAll , overlayEmbeddedTriangulation , overlayReceipt , overlayCells@@ -440,6 +484,9 @@ , overlayArrangementEdges , overlayPlanarLayer , overlaySelectedRegion+ , overlayMass+ , overlayConfusion+ , layerCovers , overlayClosedUnion , overlayClosedIntersection , overlayRegularizedDifference@@ -447,6 +494,7 @@ , eulerCharacteristicValue , ExactArea , exactAreaValue+ , polygonComponentArea , ExactLengthTerm , lengthCoefficient , squaredLength@@ -475,12 +523,52 @@ , MinkowskiError (..) , MinkowskiReceipt (..) , convexMinkowskiSum+ , reflectConvexPolygon , minkowskiSum , erodeBy , openWith , closeWith , polygonOffset , polygonInset+ , PowerWeight+ , PowerWeightError (..)+ , powerWeight+ , powerWeightExact+ , PowerSite+ , powerSite+ , powerSiteLabel+ , powerSitePosition+ , powerSiteWeight+ , PowerCellDisposition (..)+ , RegularSiteDisposition (..)+ , RegularFace+ , regularFaceLabels+ , regularFaceDualPoint+ , PowerDualEdge (..)+ , RegularEdge+ , regularEdgeLabels+ , regularEdgeDual+ , RegularTriangulation+ , regularTriangulation+ , regularSiteDisposition+ , regularFaces+ , regularEdges+ , regularNeighbours+ , RegularTriangulationReceipt (..)+ , BoundedPowerDiagram+ , boundedPowerDiagram+ , powerCellDisposition+ , powerCellDispositions+ , powerDiagramPlanarLayer+ , PowerDiagramError (..)+ , RegularTopologyError (..)+ , PowerDiagramReceipt (..)+ , powerDiagramInputSites+ , powerDiagramPeakIntermediateBitGrowth+ , powerDiagramFinalCoordinateBitGrowth+ , AffineForm (..)+ , UpperEnvelopeError (..)+ , upperEnvelope -- * Discharge — the invariants the constructors guarantee, checkable on a -- value built by any route; every violation is a value carrying its witness@@ -616,6 +704,7 @@ ( ExactGeometryError (..) , ExactIntersectionError (..) , ExactPoint+ , ExactRay , ExactSegment , ExactVector (..) , SegmentRelation (..)@@ -632,6 +721,9 @@ , exactPointFromPoint , exactPointFromQueryPoint , exactPointToEmbeddingCandidate+ , exactRay+ , exactRayDirection+ , exactRayOrigin , exactSegment , exactSegmentEndpoints , exactSegmentRelation@@ -671,6 +763,41 @@ ( hullEdges , hullVertices )+import Moonlight.Triangulation.Handles.Scoped+ ( ScopedDirectedEdge+ , ScopedFace+ , ScopedTriangulation+ , ScopedUndirectedEdge+ , ScopedVertex+ , scopedAdjacentEdge+ , scopedAsUndirected+ , scopedDestination+ , scopedDirectedEdgeData+ , scopedDirectedEdges+ , scopedFaceData+ , scopedFaceDirectedEdges+ , scopedFaceVertices+ , scopedFaces+ , scopedIncidentFace+ , scopedInnerFaces+ , scopedIsBoundaryEdge+ , scopedIsConstraintEdge+ , scopedIsOuterDirectedEdge+ , scopedNext+ , scopedOrigin+ , scopedOuterFace+ , scopedPrevious+ , scopedReverseEdge+ , scopedUndirectedEdgeData+ , scopedUndirectedEdges+ , scopedUndirectedEndpoints+ , scopedVertexData+ , scopedVertexOutEdge+ , scopedVertexOutgoingEdges+ , scopedVertexPoint+ , scopedVertices+ , withScopedTriangulation+ ) import Moonlight.Triangulation.Canonical (canonicalize) import Moonlight.Triangulation.Cdt ( CanonicalSegment@@ -834,6 +961,7 @@ , StructuringElement , closeWith , convexMinkowskiSum+ , reflectConvexPolygon , convexPolygon , convexPolygonPoints , erodeBy@@ -860,11 +988,16 @@ , triangleArea ) import Moonlight.Triangulation.Overlay- ( OverlayError (..)+ ( CoverGap+ , LayerCoverageError (..)+ , OverlayError (..) , OverlayReceipt (..) , OverlayResult , OverlaySelectionError (..) , OverlaySelectionKind (..)+ , coverGapRegion+ , layerCovers+ , overlayAll , overlayArrangementEdges , overlayArrangementVertices , overlayCells@@ -872,11 +1005,54 @@ , overlayClosedUnion , overlayEmbeddedTriangulation , overlayLayers+ , overlayMass+ , overlayConfusion , overlayPlanarLayer , overlayReceipt , overlayRegularizedDifference , overlaySelectedRegion )+import Moonlight.Triangulation.PowerDiagram+ ( AffineForm (..)+ , BoundedPowerDiagram+ , PowerDualEdge (..)+ , PowerCellDisposition (..)+ , PowerDiagramError (..)+ , PowerDiagramReceipt (..)+ , PowerSite+ , PowerWeight+ , PowerWeightError (..)+ , RegularEdge+ , RegularFace+ , RegularSiteDisposition (..)+ , RegularTriangulation+ , RegularTriangulationReceipt (..)+ , RegularTopologyError (..)+ , UpperEnvelopeError (..)+ , boundedPowerDiagram+ , powerCellDisposition+ , powerCellDispositions+ , powerDiagramPlanarLayer+ , powerDiagramInputSites+ , powerDiagramPeakIntermediateBitGrowth+ , powerDiagramFinalCoordinateBitGrowth+ , powerSite+ , powerSiteLabel+ , powerSitePosition+ , powerSiteWeight+ , powerWeight+ , powerWeightExact+ , regularEdgeDual+ , regularEdgeLabels+ , regularEdges+ , regularFaceDualPoint+ , regularFaceLabels+ , regularFaces+ , regularNeighbours+ , regularSiteDisposition+ , regularTriangulation+ , upperEnvelope+ ) import Moonlight.Triangulation.Payload ( PayloadTraversal , directedPayloads@@ -974,6 +1150,7 @@ , cellValuations , eulerCharacteristicValue , exactAreaValue+ , polygonComponentArea , exactLengthBounds , exactLengthExpression , exactLengthTerms
src-public/Moonlight/Triangulation/Internal/Minkowski/Convex.hs view
@@ -3,7 +3,9 @@ module Moonlight.Triangulation.Internal.Minkowski.Convex ( convexPolygon , convexPolygonPoints+ , convexPolygonComponent , convexPolygonRegion+ , convexPolygonFromRetained , admittedConvexLoop , structuringElement , structuringElementPolygon@@ -17,29 +19,34 @@ , subtractExactPoints ) where -import Control.Monad (foldM) import Data.Bifunctor (first) import qualified Data.List as List import Data.List.NonEmpty (NonEmpty (..)) import qualified Data.List.NonEmpty as NonEmpty import qualified Data.Set as Set import Moonlight.Triangulation.Exact- ( ExactPoint+ ( ExactClipDisposition (..)+ , ExactClipReceipt+ , ExactClosedHalfPlane+ , ExactPoint+ , ExactRetainedPolygon , ExactVector (..) , addExactVectors+ , exactClipRetainedPolygon+ , exactClosedHalfPlaneFromDirectedEdge , compareExactVectorAngle , exactVectorFromPoints- , exactSegment- , exactSupportingLineIntersection , exactOrient2d , exactPoint , exactPointCoordinates+ , exactRetainedPolygon+ , exactRetainedPolygonPoints , translateExactPoint ) import Moonlight.Triangulation.Internal.BoundaryCycle ( cyclePairs , cyclePairsNonEmpty- , cyclicTriples+ , firstNonCounterClockwiseTurn , rotateCycleLeast , rotateCycleLeastBy )@@ -70,31 +77,33 @@ convexPolygon submitted = do loop <- first MinkowskiInvalidConvexLoop (exactLoop submitted) let points = exactLoopPoints loop- case- [ (index, turn)- | (index, (previous, current, next)) <-- zip [0 :: Int ..] (cyclicTriples (NonEmpty.toList points))- , let turn = exactOrient2d previous current next- , turn /= GT- ] of- (index, turn) : _ -> Left (MinkowskiNonConvexTurn index turn)- [] -> Right (ConvexPolygon loop)+ case firstNonCounterClockwiseTurn exactOrient2d points of+ Just (index, turn) -> Left (MinkowskiNonConvexTurn index turn)+ Nothing -> Right (ConvexPolygon loop) convexPolygonPoints :: ConvexPolygon -> NonEmpty ExactPoint convexPolygonPoints (ConvexPolygon loop) = exactLoopPoints loop +convexPolygonComponent :: ConvexPolygon -> PolygonComponent+convexPolygonComponent (ConvexPolygon loop) = PolygonComponent loop []+ convexPolygonRegion :: ConvexPolygon -> PlanarRegion-convexPolygonRegion (ConvexPolygon loop) =- PlanarRegion [PolygonComponent loop []]+convexPolygonRegion = PlanarRegion . pure . convexPolygonComponent +-- | Total projection from the retained-line carrier. Strict convexity and+-- simplicity are already invariants of 'ExactRetainedPolygon'; only canonical+-- cycle rotation remains.+convexPolygonFromRetained :: ExactRetainedPolygon -> ConvexPolygon+convexPolygonFromRetained = admittedConvexPolygon . exactRetainedPolygonPoints++admittedConvexPolygon :: NonEmpty ExactPoint -> ConvexPolygon+admittedConvexPolygon = ConvexPolygon . ExactLoop . rotateCycleLeast+ admittedConvexLoop :: ExactLoop -> Maybe ConvexPolygon-admittedConvexLoop loop- | all ((== GT) . orderedTurn) (cyclicTriples (NonEmpty.toList (exactLoopPoints loop))) =- Just (ConvexPolygon loop)- | otherwise = Nothing- where- orderedTurn (previous, current, next) =- exactOrient2d previous current next+admittedConvexLoop loop =+ case firstNonCounterClockwiseTurn exactOrient2d (exactLoopPoints loop) of+ Nothing -> Just (ConvexPolygon loop)+ Just _ -> Nothing structuringElement :: ConvexPolygon@@ -131,7 +140,7 @@ directionList = NonEmpty.toList directions scanned = scanl translateExactPoint start directionList resultPoints = start :| take (length directionList - 1) (drop 1 scanned)- in ConvexPolygon (ExactLoop (rotateCycleLeast resultPoints))+ in admittedConvexPolygon resultPoints convexHullPolygon :: NonEmpty ExactPoint@@ -140,16 +149,13 @@ let points = NonEmpty.toList submitted in case convexHullPoints points of Nothing -> Left (MinkowskiConvexHullDegenerate points)- Just hullPoints -> Right (ConvexPolygon (ExactLoop (rotateCycleLeast hullPoints)))+ Just hullPoints -> Right (admittedConvexPolygon hullPoints) reflectConvexPolygon :: ConvexPolygon -> ConvexPolygon-reflectConvexPolygon polygon =- ConvexPolygon- ( ExactLoop- ( rotateCycleLeast- (NonEmpty.reverse (fmap negateExactPoint (convexPolygonPoints polygon)))- )- )+reflectConvexPolygon =+ admittedConvexPolygon+ . fmap negateExactPoint+ . convexPolygonPoints convexPolygonCentroid :: ConvexPolygon@@ -169,27 +175,35 @@ pure (exactPoint x y) -- | Erode one convex polygon by another through the strongest translated--- support half-plane for each source edge. Sutherland--Hodgman descent keeps--- the construction exact; a lower-dimensional residual is represented by the--- empty polygonal region at this two-dimensional publication boundary.+-- support half-plane for each source edge. The canonical sorted exact descent+-- retains those source lines; a lower-dimensional residual is represented by+-- the empty polygonal region at this two-dimensional publication boundary. erodeConvexBy :: ConvexPolygon -> ConvexPolygon- -> Either MinkowskiError (Maybe ConvexPolygon)+ -> Either MinkowskiError (Maybe ConvexPolygon, ExactClipReceipt) erodeConvexBy source kernel = do let sourcePoints = convexPolygonPoints source kernelPoints = convexPolygonPoints kernel firstKernel = NonEmpty.head kernelPoints initial =- map+ fmap (`subtractExactPoints` firstKernel)- (NonEmpty.toList sourcePoints)- halfPlanes =- [ strongestHalfPlane kernelPoints from to- | (from, to) <- cyclePairs sourcePoints- ]- clipped <- foldM clipPolygon initial halfPlanes- pure (ConvexPolygon . ExactLoop . rotateCycleLeast <$> convexHullPoints clipped)+ sourcePoints+ retained <- first MinkowskiInvalidHalfPlane (exactRetainedPolygon initial)+ halfPlanes <-+ traverse+ (uncurry (strongestHalfPlane kernelPoints))+ (cyclePairs sourcePoints)+ (disposition, clipReceipt) <-+ first MinkowskiClipFailed (exactClipRetainedPolygon retained halfPlanes)+ let eroded =+ case disposition of+ ExactClipFullDimensional clipped ->+ Just (convexPolygonFromRetained clipped)+ ExactClipLowerDimensional _ -> Nothing+ ExactClipEmpty -> Nothing+ pure (eroded, clipReceipt) edgeDirections :: NonEmpty ExactPoint -> NonEmpty ExactVector edgeDirections = fmap (uncurry exactVectorFromPoints) . cyclePairsNonEmpty@@ -247,7 +261,7 @@ :: NonEmpty ExactPoint -> ExactPoint -> ExactPoint- -> (ExactPoint, ExactPoint)+ -> Either MinkowskiError ExactClosedHalfPlane strongestHalfPlane kernelPoints from to = let direction = exactVectorFromPoints from to supportPoint =@@ -261,50 +275,17 @@ else selected) initial remaining- in ( subtractExactPoints from supportPoint- , subtractExactPoints to supportPoint- )+ in first MinkowskiInvalidHalfPlane+ ( exactClosedHalfPlaneFromDirectedEdge+ (subtractExactPoints from supportPoint)+ (subtractExactPoints to supportPoint)+ ) directionPointCross :: ExactVector -> ExactPoint -> ExactRational directionPointCross (ExactVector directionX directionY) point = let (x, y) = exactPointCoordinates point in directionX * y - directionY * x -clipPolygon- :: [ExactPoint]- -> (ExactPoint, ExactPoint)- -> Either MinkowskiError [ExactPoint]-clipPolygon [] _ = Right []-clipPolygon polygon halfPlane =- concat <$> traverse (clipEdge halfPlane) (cyclePairsList polygon)--clipEdge- :: (ExactPoint, ExactPoint)- -> (ExactPoint, ExactPoint)- -> Either MinkowskiError [ExactPoint]-clipEdge (boundaryFrom, boundaryTo) (from, to) =- case (inside from, inside to) of- (True, True) -> Right [to]- (True, False) -> (: []) <$> supportingLineIntersection from to boundaryFrom boundaryTo- (False, True) -> do- crossing <- supportingLineIntersection from to boundaryFrom boundaryTo- pure [crossing, to]- (False, False) -> Right []- where- inside point = exactOrient2d boundaryFrom boundaryTo point /= LT--supportingLineIntersection- :: ExactPoint- -> ExactPoint- -> ExactPoint- -> ExactPoint- -> Either MinkowskiError ExactPoint-supportingLineIntersection lineFrom lineTo boundaryFrom boundaryTo = do- clippedSegment <- first MinkowskiInvalidSegment (exactSegment lineFrom lineTo)- boundarySegment <- first MinkowskiInvalidSegment (exactSegment boundaryFrom boundaryTo)- first MinkowskiLineIntersection- (exactSupportingLineIntersection clippedSegment boundarySegment)- addExactPoints :: ExactPoint -> ExactPoint -> ExactPoint addExactPoints left right = let (leftX, leftY) = exactPointCoordinates left@@ -321,6 +302,3 @@ negateExactPoint point = let (x, y) = exactPointCoordinates point in exactPoint (negate x) (negate y)--cyclePairsList :: [value] -> [(value, value)]-cyclePairsList = maybe [] cyclePairs . NonEmpty.nonEmpty
src-public/Moonlight/Triangulation/Internal/Minkowski/Types.hs view
@@ -14,8 +14,8 @@ import Control.DeepSeq (NFData) import GHC.Generics (Generic) import Moonlight.Triangulation.Exact- ( ExactGeometryError- , ExactIntersectionError+ ( ExactClipError+ , ExactHalfPlaneError , ExactPoint ) import Moonlight.Triangulation.Internal.HandleDefs (FaceId)@@ -50,11 +50,11 @@ data MinkowskiError = MinkowskiInvalidConvexLoop !RegionValidationError- | MinkowskiInvalidSegment !ExactGeometryError+ | MinkowskiInvalidHalfPlane !ExactHalfPlaneError+ | MinkowskiClipFailed !ExactClipError | MinkowskiNonConvexTurn !Int !Ordering | MinkowskiOriginOutside !RegionPointLocation | MinkowskiExactArithmetic !ExactArithmeticError- | MinkowskiLineIntersection !ExactIntersectionError | MinkowskiOverlayFailed !(OverlayError Bool Bool) | MinkowskiPublicationFailed !RegionPublicationError | MinkowskiOverlayCellWitness !OverlayCellWitness
− src-public/Moonlight/Triangulation/Internal/Overlay/Arrangement.hs
@@ -1,811 +0,0 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DerivingStrategies #-}---- | Exact source normalization and binary64 embedding certification. This--- module ends at the arrangement/resident seam: no DCEL face state crosses it.-module Moonlight.Triangulation.Internal.Overlay.Arrangement- ( ExactEdgeKey- , AtomicEdge (..)- , atomicEdgeFrom- , atomicEdgeTo- , OverlayVertexSeed (..)- , ArrangementMetrics (..)- , CertifiedArrangement (..)- , certifyArrangement- , canonicalEdgeKey- , atomicKey- , compareAround- ) where--import Data.Bifunctor (first)-import Data.List (sortBy)-import qualified Data.List as List-import Data.List.NonEmpty (NonEmpty (..))-import qualified Data.List.NonEmpty as NonEmpty-import qualified Data.Map.Strict as Map-import Data.Map.Strict (Map)-import qualified Data.Set as Set-import Data.Set (Set)-import qualified Data.Vector as V-import GHC.Generics (Generic)-import Moonlight.Triangulation.Exact- ( ExactGeometryError- , ExactPoint- , ExactSegment- , SegmentRelation (SegmentsShareEndpoint)- , compareExactVectorAngle- , exactPointCoordinates- , exactPointFromQueryPoint- , exactSegment- , exactSegmentEndpoints- , exactVectorFromPoints- )-import Moonlight.Triangulation.Internal.BoundaryCycle- ( consecutivePairs- , orderedPair- , unorderedPairs- )-import Moonlight.Triangulation.Internal.ExactRational- ( ExactRational- , exactDivide- , exactRationalIsZero- )-import Moonlight.Triangulation.Internal.ExactSegmentEvents- ( ExactSegmentEvent (..)- , ExactSegmentEventPlan- , ExactSweepSegmentId (..)- , exactSegmentEventPlan- , exactSegmentEvents- , exactSegmentPairChecks- , exactSegmentRelationMap- , exactSegmentSplitPoints- , exactSegmentSweepMaximumHeight- )-import Moonlight.Triangulation.Internal.Overlay.Embedding- ( DraftId (..)- , DraftIncidence (..)- , DraftNeighborhood (..)- , DraftReference (..)- , DraftVertexId- , ExactArrangementDraft (..)- , LocalEmbeddingCertificate (..)- , OverlayEmbeddingObstruction (..)- , certifyLocalEmbedding- )-import Moonlight.Triangulation.Internal.Overlay.Types-import Moonlight.Triangulation.Internal.Types- ( HasPosition (..)- , Point- , queryPointValue- )-import Moonlight.Triangulation.Region- ( ExactLoop- , PlanarLayer- , PolygonComponent- , exactLoopPoints- , planarLayerOutsideLabel- , planarLayerRegions- , planarRegionComponents- , polygonHoleLoops- , polygonOuterLoop- )--type ExactEdgeKey = (ExactPoint, ExactPoint)--data SourceBoundary leftLabel rightLabel- = LeftSourceBoundary- !ExactSegment- !leftLabel- !(BoundaryVertexRef 'LeftOverlayOperand)- !(BoundaryVertexRef 'LeftOverlayOperand)- !(BoundaryEdgeRef 'LeftOverlayOperand)- | RightSourceBoundary- !ExactSegment- !rightLabel- !(BoundaryVertexRef 'RightOverlayOperand)- !(BoundaryVertexRef 'RightOverlayOperand)- !(BoundaryEdgeRef 'RightOverlayOperand)--type SourceBoundaryConstructor operand label leftLabel rightLabel =- ExactSegment- -> label- -> BoundaryVertexRef operand- -> BoundaryVertexRef operand- -> BoundaryEdgeRef operand- -> SourceBoundary leftLabel rightLabel--data AtomicContribution leftLabel rightLabel =- AtomicContribution !(SourceBoundary leftLabel rightLabel) !Bool--data AtomicEdge leftLabel rightLabel = AtomicEdge- { atomicEdgeSegment :: !ExactSegment- , atomicEdgeOrigin :: !OverlayEdgeOrigin- , atomicEdgeLeftTransition :: !(Maybe (leftLabel, leftLabel))- , atomicEdgeRightTransition :: !(Maybe (rightLabel, rightLabel))- }--data OriginAccumulation = OriginAccumulation- { accumulatedLeftVertices :: !(Set (BoundaryVertexRef 'LeftOverlayOperand))- , accumulatedRightVertices :: !(Set (BoundaryVertexRef 'RightOverlayOperand))- , accumulatedLeftEdges :: !(Set (BoundaryEdgeRef 'LeftOverlayOperand))- , accumulatedRightEdges :: !(Set (BoundaryEdgeRef 'RightOverlayOperand))- }--data OverlayVertexSeed = OverlayVertexSeed- { seedExactPoint :: !ExactPoint- , seedEmbeddedPoint :: !Point- , seedOrigin :: !OverlayVertexOrigin- }- deriving stock (Eq, Ord, Show, Generic)--instance HasPosition OverlayVertexSeed where- position = seedEmbeddedPoint--data ArrangementMetrics = ArrangementMetrics- { arrangementInputSegments :: !Int- , arrangementRelationEvents :: !Int- , arrangementExactCrossings :: !Int- , arrangementOverlapIntervals :: !Int- , arrangementEmbeddingCandidates :: !Int- , arrangementTotalRelationChecks :: !Int- , arrangementSweepMaximumHeight :: !Int- }--data CertifiedArrangement leftLabel rightLabel = CertifiedArrangement- { certifiedAtomicEdges :: !(V.Vector (AtomicEdge leftLabel rightLabel))- , certifiedVertexSeeds :: !(V.Vector OverlayVertexSeed)- , certifiedConstraints :: !(V.Vector (Int, Int))- , certifiedInexactEmbeddingPoints :: !(Set ExactPoint)- , certifiedMetrics :: !ArrangementMetrics- }--certifyArrangement- :: (Ord leftLabel, Ord rightLabel)- => PlanarLayer leftLabel- -> PlanarLayer rightLabel- -> Either- (OverlayError leftLabel rightLabel)- (CertifiedArrangement leftLabel rightLabel)-certifyArrangement leftLayer rightLayer = do- sources <- flattenLayers leftLayer rightLayer- sourcePlan <-- first OverlaySegmentEventsInvalid- (exactSegmentEventPlan (V.map sourceExactSegment sources))- atomicEdges <- normalizeAtomicEdges leftLayer rightLayer sources sourcePlan- let atomicVector = V.fromList atomicEdges- pointIds = exactPointIds atomicVector- origins = vertexOrigins sources atomicVector- atomicRelations = atomicEndpointRelations atomicVector- draft <- exactArrangementDraft sources sourcePlan atomicVector pointIds atomicRelations- localCertificate <- first OverlayEmbeddingRefused (certifyLocalEmbedding draft)- let projectedPoints = projectedExactPoints localCertificate- inexactEmbeddingPoints =- Set.fromList- [ exactPoint- | (exactPoint, vertexId) <- Map.toAscList pointIds- , Map.lookup vertexId projectedPoints /= Just exactPoint- ]- projectedSegments <- projectedAtomicSegments atomicVector pointIds projectedPoints- projectedPlan <-- first OverlaySegmentEventsInvalid- (exactSegmentEventPlan projectedSegments)- dischargeGlobalRelations atomicRelations projectedPlan- seeds <- overlayVertexSeeds pointIds origins localCertificate- constraints <- atomicConstraints pointIds atomicVector- let events = exactSegmentEvents sourcePlan- pure- CertifiedArrangement- { certifiedAtomicEdges = atomicVector- , certifiedVertexSeeds = seeds- , certifiedConstraints = constraints- , certifiedInexactEmbeddingPoints = inexactEmbeddingPoints- , certifiedMetrics =- ArrangementMetrics- { arrangementInputSegments = V.length sources- , arrangementRelationEvents = length events- , arrangementExactCrossings =- length [() | ExactProperCrossing {} <- events]- , arrangementOverlapIntervals =- length [() | ExactCollinearOverlap {} <- events]- , arrangementEmbeddingCandidates = Map.size pointIds- , arrangementTotalRelationChecks =- exactSegmentPairChecks sourcePlan- + exactSegmentPairChecks projectedPlan- , arrangementSweepMaximumHeight =- max- (exactSegmentSweepMaximumHeight sourcePlan)- (exactSegmentSweepMaximumHeight projectedPlan)- }- }-flattenLayers- :: PlanarLayer leftLabel- -> PlanarLayer rightLabel- -> Either (OverlayError leftLabel rightLabel) (V.Vector (SourceBoundary leftLabel rightLabel))-flattenLayers leftLayer rightLayer = do- leftSources <- flattenLayer LeftSourceBoundary leftLayer- rightSources <- flattenLayer RightSourceBoundary rightLayer- pure (V.fromList (leftSources <> rightSources))--flattenLayer- :: SourceBoundaryConstructor operand label leftLabel rightLabel- -> PlanarLayer label- -> Either (OverlayError leftLabel rightLabel) [SourceBoundary leftLabel rightLabel]-flattenLayer makeBoundary layer =- fmap concat- ( traverse- (\(componentIndex, (label, component)) ->- flattenComponent makeBoundary componentIndex label component)- (zip [0 ..] (labelledComponents layer))- )-{-# INLINE flattenLayer #-}--labelledComponents :: PlanarLayer label -> [(label, PolygonComponent)]-labelledComponents layer =- [ (label, component)- | (label, region) <- Map.toAscList (planarLayerRegions layer)- , component <- planarRegionComponents region- ]--flattenComponent- :: SourceBoundaryConstructor operand label leftLabel rightLabel- -> Int- -> label- -> PolygonComponent- -> Either (OverlayError leftLabel rightLabel) [SourceBoundary leftLabel rightLabel]-flattenComponent makeBoundary componentIndex label component = do- outer <-- flattenLoop- makeBoundary- componentIndex- BoundaryOuterLoop- label- (polygonOuterLoop component)- holes <-- fmap concat- ( traverse- (\(holeIndex, loop) ->- flattenLoop- makeBoundary- componentIndex- (BoundaryHoleLoop holeIndex)- label- loop)- (zip [0 ..] (polygonHoleLoops component))- )- pure (outer <> holes)-{-# INLINE flattenComponent #-}--flattenLoop- :: SourceBoundaryConstructor operand label leftLabel rightLabel- -> Int- -> BoundaryLoopRef- -> label- -> ExactLoop- -> Either (OverlayError leftLabel rightLabel) [SourceBoundary leftLabel rightLabel]-flattenLoop makeBoundary componentIndex loopRef label loop =- traverse constructSource (indexedCycle (exactLoopPoints loop))- where- constructSource (edgeIndex, fromIndex, from, toIndex, to) = do- segment <- first (sourceGeometryError from) (exactSegment from to)- pure- ( makeBoundary- segment- label- (BoundaryRef componentIndex loopRef fromIndex)- (BoundaryRef componentIndex loopRef toIndex)- (BoundaryRef componentIndex loopRef edgeIndex)- )-{-# INLINE flattenLoop #-}--sourceGeometryError- :: ExactPoint- -> ExactGeometryError- -> OverlayError leftLabel rightLabel-sourceGeometryError point _ = OverlayArrangementInvalid (OverlayRotationDegenerate point)--indexedCycle :: NonEmpty value -> [(Int, Int, value, Int, value)]-indexedCycle (firstValue :| remaining) =- let values = firstValue : remaining- count = length values- in [ (index, index, from, (index + 1) `mod` count, to)- | (index, (from, to)) <- zip [0 ..] (zip values (remaining <> [firstValue]))- ]--sourceExactSegment :: SourceBoundary leftLabel rightLabel -> ExactSegment-sourceExactSegment (LeftSourceBoundary segment _ _ _ _) = segment-sourceExactSegment (RightSourceBoundary segment _ _ _ _) = segment--normalizeAtomicEdges- :: (Ord leftLabel, Ord rightLabel)- => PlanarLayer leftLabel- -> PlanarLayer rightLabel- -> V.Vector (SourceBoundary leftLabel rightLabel)- -> ExactSegmentEventPlan- -> Either (OverlayError leftLabel rightLabel) [AtomicEdge leftLabel rightLabel]-normalizeAtomicEdges leftLayer rightLayer sources plan =- fmap concat- ( traverse- (resolveAtomicContributions leftOutside rightOutside)- (Map.toAscList grouped)- )- where- leftOutside = planarLayerOutsideLabel leftLayer- rightOutside = planarLayerOutsideLabel rightLayer- grouped =- V.ifoldl'- (\groups sourceIndex source ->- List.foldl'- (insertAtomic source)- groups- (consecutivePairs (exactSegmentSplitPoints plan (ExactSweepSegmentId sourceIndex))))- Map.empty- sources- insertAtomic- :: SourceBoundary leftLabel' rightLabel'- -> Map ExactEdgeKey [AtomicContribution leftLabel' rightLabel']- -> (ExactPoint, ExactPoint)- -> Map ExactEdgeKey [AtomicContribution leftLabel' rightLabel']- insertAtomic source groups (from, to)- | from == to = groups- | otherwise =- let key@(canonicalFrom, _) = canonicalEdgeKey from to- contribution = AtomicContribution source (from == canonicalFrom)- in Map.insertWith (<>) key [contribution] groups--resolveAtomicContributions- :: (Ord leftLabel, Ord rightLabel)- => leftLabel- -> rightLabel- -> (ExactEdgeKey, [AtomicContribution leftLabel rightLabel])- -> Either (OverlayError leftLabel rightLabel) [AtomicEdge leftLabel rightLabel]-resolveAtomicContributions leftOutside rightOutside ((from, to), contributions) = do- let- ( leftLabelsOnLeft- , leftLabelsOnRight- , rightLabelsOnLeft- , rightLabelsOnRight- , leftSources- , rightSources- ) =- List.foldl'- collectContribution- (Set.empty, Set.empty, Set.empty, Set.empty, Set.empty, Set.empty)- contributions- origin =- OverlayEdgeOrigin- (Set.toAscList leftSources)- (Set.toAscList rightSources)- leftTransition <-- resolveTransition- OverlayLeftSourceSideConflict- origin- leftOutside- leftLabelsOnLeft- leftLabelsOnRight- rightTransition <-- resolveTransition- OverlayRightSourceSideConflict- origin- rightOutside- rightLabelsOnLeft- rightLabelsOnRight- if transitionIsIdentity leftTransition && transitionIsIdentity rightTransition- then Right []- else do- segment <- first (sourceGeometryError from) (exactSegment from to)- Right- [ AtomicEdge- { atomicEdgeSegment = segment- , atomicEdgeOrigin = origin- , atomicEdgeLeftTransition = leftTransition- , atomicEdgeRightTransition = rightTransition- }- ]- where- collectContribution- :: (Ord leftLabel', Ord rightLabel')- => ( Set leftLabel'- , Set leftLabel'- , Set rightLabel'- , Set rightLabel'- , Set (BoundaryEdgeRef 'LeftOverlayOperand)- , Set (BoundaryEdgeRef 'RightOverlayOperand)- )- -> AtomicContribution leftLabel' rightLabel'- -> ( Set leftLabel'- , Set leftLabel'- , Set rightLabel'- , Set rightLabel'- , Set (BoundaryEdgeRef 'LeftOverlayOperand)- , Set (BoundaryEdgeRef 'RightOverlayOperand)- )- collectContribution- ( !leftLeft- , !leftRight- , !rightLeft- , !rightRight- , !leftSources- , !rightSources- )- contribution =- case contribution of- AtomicContribution (LeftSourceBoundary _ label _ _ reference) follows ->- ( if follows then Set.insert label leftLeft else leftLeft- , if follows then leftRight else Set.insert label leftRight- , rightLeft- , rightRight- , Set.insert reference leftSources- , rightSources- )- AtomicContribution (RightSourceBoundary _ label _ _ reference) follows ->- ( leftLeft- , leftRight- , if follows then Set.insert label rightLeft else rightLeft- , if follows then rightRight else Set.insert label rightRight- , leftSources- , Set.insert reference rightSources- )--resolveTransition- :: (OverlayEdgeOrigin -> NonEmpty label -> OverlayArrangementObstruction leftLabel rightLabel)- -> OverlayEdgeOrigin- -> label- -> Set label- -> Set label- -> Either (OverlayError leftLabel rightLabel) (Maybe (label, label))-resolveTransition sideConflict origin outside labelsOnLeft labelsOnRight- | Set.null labelsOnLeft && Set.null labelsOnRight = Right Nothing- | otherwise =- Just- <$> ((,)- <$> resolveSide sideConflict origin outside labelsOnLeft- <*> resolveSide sideConflict origin outside labelsOnRight)-{-# INLINE resolveTransition #-}--transitionIsIdentity :: Eq label => Maybe (label, label) -> Bool-transitionIsIdentity Nothing = True-transitionIsIdentity (Just (leftLabel, rightLabel)) = leftLabel == rightLabel--resolveSide- :: (OverlayEdgeOrigin -> NonEmpty label -> OverlayArrangementObstruction leftLabel rightLabel)- -> OverlayEdgeOrigin- -> label- -> Set label- -> Either (OverlayError leftLabel rightLabel) label-resolveSide sideConflict origin outside labels =- case Set.toAscList labels of- [] -> Right outside- [label] -> Right label- firstLabel : remaining ->- Left- ( OverlayArrangementInvalid- (sideConflict origin (firstLabel :| remaining))- )-{-# INLINE resolveSide #-}--exactPointIds- :: V.Vector (AtomicEdge leftLabel rightLabel)- -> Map ExactPoint DraftVertexId-exactPointIds edges =- Map.fromAscList- ( zip- (Set.toAscList (V.foldl' collect Set.empty edges))- (map DraftId [0 ..])- )- where- collect- :: Set ExactPoint- -> AtomicEdge leftLabel rightLabel- -> Set ExactPoint- collect points edge = Set.insert (atomicEdgeFrom edge) (Set.insert (atomicEdgeTo edge) points)--vertexOrigins- :: V.Vector (SourceBoundary leftLabel rightLabel)- -> V.Vector (AtomicEdge leftLabel rightLabel)- -> Map ExactPoint OverlayVertexOrigin-vertexOrigins sources atomicEdges =- Map.map finalizeOrigin- ( V.foldl'- addAtomicOrigin- (V.foldl' addSourceEndpoints Map.empty sources)- atomicEdges- )--emptyOrigin :: OriginAccumulation-emptyOrigin = OriginAccumulation Set.empty Set.empty Set.empty Set.empty--mergeOrigin :: OriginAccumulation -> OriginAccumulation -> OriginAccumulation-mergeOrigin left right =- OriginAccumulation- { accumulatedLeftVertices = accumulatedLeftVertices left <> accumulatedLeftVertices right- , accumulatedRightVertices = accumulatedRightVertices left <> accumulatedRightVertices right- , accumulatedLeftEdges = accumulatedLeftEdges left <> accumulatedLeftEdges right- , accumulatedRightEdges = accumulatedRightEdges left <> accumulatedRightEdges right- }--addSourceEndpoints- :: Map ExactPoint OriginAccumulation- -> SourceBoundary leftLabel rightLabel- -> Map ExactPoint OriginAccumulation-addSourceEndpoints origins source =- case source of- LeftSourceBoundary segment _ fromReference toReference _ ->- let (from, to) = exactSegmentEndpoints segment- in insertOrigin to (emptyOrigin{accumulatedLeftVertices = Set.singleton toReference})- (insertOrigin from (emptyOrigin{accumulatedLeftVertices = Set.singleton fromReference}) origins)- RightSourceBoundary segment _ fromReference toReference _ ->- let (from, to) = exactSegmentEndpoints segment- in insertOrigin to (emptyOrigin{accumulatedRightVertices = Set.singleton toReference})- (insertOrigin from (emptyOrigin{accumulatedRightVertices = Set.singleton fromReference}) origins)--addAtomicOrigin- :: Map ExactPoint OriginAccumulation- -> AtomicEdge leftLabel rightLabel- -> Map ExactPoint OriginAccumulation-addAtomicOrigin origins edge =- let origin = atomicEdgeOrigin edge- accumulation =- emptyOrigin- { accumulatedLeftEdges = Set.fromList (overlayEdgeLeftSources origin)- , accumulatedRightEdges = Set.fromList (overlayEdgeRightSources origin)- }- in insertOrigin (atomicEdgeTo edge) accumulation- (insertOrigin (atomicEdgeFrom edge) accumulation origins)--insertOrigin- :: ExactPoint- -> OriginAccumulation- -> Map ExactPoint OriginAccumulation- -> Map ExactPoint OriginAccumulation-insertOrigin = Map.insertWith mergeOrigin--finalizeOrigin :: OriginAccumulation -> OverlayVertexOrigin-finalizeOrigin accumulated =- OverlayVertexOrigin- { overlayOriginLeftVertices = Set.toAscList (accumulatedLeftVertices accumulated)- , overlayOriginRightVertices = Set.toAscList (accumulatedRightVertices accumulated)- , overlayOriginLeftEdges = Set.toAscList (accumulatedLeftEdges accumulated)- , overlayOriginRightEdges = Set.toAscList (accumulatedRightEdges accumulated)- }--exactArrangementDraft- :: V.Vector (SourceBoundary leftLabel rightLabel)- -> ExactSegmentEventPlan- -> V.Vector (AtomicEdge leftLabel rightLabel)- -> Map ExactPoint DraftVertexId- -> Map (ExactSweepSegmentId, ExactSweepSegmentId) SegmentRelation- -> Either (OverlayError leftLabel rightLabel) ExactArrangementDraft-exactArrangementDraft sources sourcePlan atomicEdges pointIds atomicRelations = do- draftSegmentsMap <-- Map.fromList- <$> traverse- (\(segmentIndex, edge) -> do- from <- requireDraftVertex pointIds (atomicEdgeFrom edge)- to <- requireDraftVertex pointIds (atomicEdgeTo edge)- pure (DraftId segmentIndex, (from, to)))- (V.toList (V.indexed atomicEdges))- memberships <-- Map.fromList- <$> traverse- (\(sourceIndex, source) -> do- values <-- traverse- (\point -> do- parameter <- exactSourceParameter source point- vertex <- requireDraftVertex pointIds point- pure (parameter, vertex))- (exactSegmentSplitPoints sourcePlan (ExactSweepSegmentId sourceIndex))- pure (DraftId sourceIndex, values))- (V.toList (V.indexed sources))- neighborhoods <- buildDraftNeighborhoods atomicEdges pointIds- let incidences =- [ DraftIncidence- (DraftId leftIndex)- (DraftId rightIndex)- relation- | ((ExactSweepSegmentId leftIndex, ExactSweepSegmentId rightIndex), relation) <-- Map.toAscList atomicRelations- ]- pure- ExactArrangementDraft- { draftVertices = Map.fromList [(vertexId, point) | (point, vertexId) <- Map.toAscList pointIds]- , draftSegments = draftSegmentsMap- , draftSourceMemberships = memberships- , draftIncidences = incidences- , draftNeighborhoods = neighborhoods- }--requireDraftVertex- :: Map ExactPoint DraftVertexId- -> ExactPoint- -> Either (OverlayError leftLabel rightLabel) DraftVertexId-requireDraftVertex pointIds point =- case Map.lookup point pointIds of- Just vertex -> Right vertex- Nothing -> Left (OverlayProvenanceIncomplete (OverlayExactVertexMissing point))--exactSourceParameter- :: SourceBoundary leftLabel rightLabel- -> ExactPoint- -> Either (OverlayError leftLabel rightLabel) ExactRational-exactSourceParameter source point =- let (from, to) = exactSegmentEndpoints (sourceExactSegment source)- (fromX, fromY) = exactPointCoordinates from- (toX, toY) = exactPointCoordinates to- (pointX, pointY) = exactPointCoordinates point- (numerator, denominator) =- if exactRationalIsZero (toX - fromX)- then (pointY - fromY, toY - fromY)- else (pointX - fromX, toX - fromX)- in first OverlayExactArithmetic (exactDivide numerator denominator)--buildDraftNeighborhoods- :: V.Vector (AtomicEdge leftLabel rightLabel)- -> Map ExactPoint DraftVertexId- -> Either (OverlayError leftLabel rightLabel) [DraftNeighborhood]-buildDraftNeighborhoods edges pointIds =- traverse neighborhood (Map.toAscList pointIds)- where- neighborhood (centerPoint, centerId) = do- let neighbors = Map.findWithDefault Set.empty centerPoint adjacency- orderedPoints <-- case NonEmpty.nonEmpty (sortBy (compareAround centerPoint) (Set.toList neighbors)) of- Just points -> Right points- Nothing -> Left (OverlayArrangementInvalid (OverlayRotationDegenerate centerPoint))- orderedIds <- traverse (requireDraftVertex pointIds) orderedPoints- pure (DraftNeighborhood centerId orderedIds)- adjacency =- V.foldl'- (\graph edge ->- Map.insertWith Set.union (atomicEdgeTo edge) (Set.singleton (atomicEdgeFrom edge))- (Map.insertWith Set.union (atomicEdgeFrom edge) (Set.singleton (atomicEdgeTo edge)) graph))- Map.empty- edges--compareAround :: ExactPoint -> ExactPoint -> ExactPoint -> Ordering-compareAround center left right =- case- compareExactVectorAngle- (exactVectorFromPoints center left)- (exactVectorFromPoints center right) of- EQ -> compare left right- ordering -> ordering---- | Normalization splits every source at every exact event and coalesces--- duplicate intervals. Distinct atomics can therefore meet only at a stored--- endpoint; their entire relation section is the endpoint-incidence index.-atomicEndpointRelations- :: V.Vector (AtomicEdge leftLabel rightLabel)- -> Map (ExactSweepSegmentId, ExactSweepSegmentId) SegmentRelation-atomicEndpointRelations edges =- Map.fromList- [ ( orderedPair- (ExactSweepSegmentId leftIndex)- (ExactSweepSegmentId rightIndex)- , SegmentsShareEndpoint- )- | incident <- Map.elems incidenceByPoint- , (leftIndex, rightIndex) <- unorderedPairs (Set.toAscList incident)- ]- where- incidenceByPoint =- V.ifoldl'- (\incidence index edge ->- Map.insertWith Set.union (atomicEdgeTo edge) (Set.singleton index)- ( Map.insertWith Set.union- (atomicEdgeFrom edge)- (Set.singleton index)- incidence- ))- Map.empty- edges-projectedExactPoints- :: LocalEmbeddingCertificate- -> Map DraftVertexId ExactPoint-projectedExactPoints =- Map.map exactPointFromQueryPoint . certificateRoundedVertices--projectedAtomicSegments- :: V.Vector (AtomicEdge leftLabel rightLabel)- -> Map ExactPoint DraftVertexId- -> Map DraftVertexId ExactPoint- -> Either (OverlayError leftLabel rightLabel) (V.Vector ExactSegment)-projectedAtomicSegments edges pointIds projected =- V.imapM project edges- where- project segmentIndex edge = do- fromId <- requireDraftVertex pointIds (atomicEdgeFrom edge)- toId <- requireDraftVertex pointIds (atomicEdgeTo edge)- from <- requireProjected fromId- to <- requireProjected toId- first- (\_ ->- OverlayEmbeddingRefused- ( ProjectedSegmentCollapsed- (DraftId segmentIndex)- fromId- toId- :| []- ))- (exactSegment from to)- requireProjected vertex =- case Map.lookup vertex projected of- Just point -> Right point- Nothing ->- Left- ( OverlayEmbeddingRefused- (DraftReferenceMissing (DraftVertexReference vertex) :| [])- )--dischargeGlobalRelations- :: Map (ExactSweepSegmentId, ExactSweepSegmentId) SegmentRelation- -> ExactSegmentEventPlan- -> Either (OverlayError leftLabel rightLabel) ()-dischargeGlobalRelations exactRelations projectedPlan =- case NonEmpty.nonEmpty obstructions of- Nothing -> Right ()- Just failures -> Left (OverlayEmbeddingRefused failures)- where- projectedRelations = exactSegmentRelationMap projectedPlan- keys = Set.toAscList (Map.keysSet exactRelations <> Map.keysSet projectedRelations)- obstructions = concatMap compareRelation keys- compareRelation key@(ExactSweepSegmentId leftId, ExactSweepSegmentId rightId) =- case (Map.lookup key exactRelations, Map.lookup key projectedRelations) of- (Nothing, Just projected) ->- [GlobalRelationAdded (DraftId leftId) (DraftId rightId) projected]- (Just exact, Nothing) ->- [GlobalRelationRemoved (DraftId leftId) (DraftId rightId) exact]- (Just exact, Just projected)- | exact /= projected ->- [GlobalRelationChanged (DraftId leftId) (DraftId rightId) exact projected]- _ -> []--overlayVertexSeeds- :: Map ExactPoint DraftVertexId- -> Map ExactPoint OverlayVertexOrigin- -> LocalEmbeddingCertificate- -> Either (OverlayError leftLabel rightLabel) (V.Vector OverlayVertexSeed)-overlayVertexSeeds pointIds origins certificate =- V.fromList- <$> traverse- (\(point, vertexId) -> do- embedded <-- case Map.lookup vertexId (certificateRoundedVertices certificate) of- Just value -> Right value- Nothing ->- Left- ( OverlayEmbeddingRefused- (DraftReferenceMissing (DraftVertexReference vertexId) :| [])- )- origin <-- case Map.lookup point origins of- Just value -> Right value- Nothing -> Left (OverlayProvenanceIncomplete (OverlayExactVertexMissing point))- pure (OverlayVertexSeed point (queryPointValue embedded) origin))- (Map.toAscList pointIds)--atomicConstraints- :: Map ExactPoint DraftVertexId- -> V.Vector (AtomicEdge leftLabel rightLabel)- -> Either (OverlayError leftLabel rightLabel) (V.Vector (Int, Int))-atomicConstraints pointIds =- V.mapM- (\edge -> do- DraftId from <- requireDraftVertex pointIds (atomicEdgeFrom edge)- DraftId to <- requireDraftVertex pointIds (atomicEdgeTo edge)- pure (from, to))---canonicalEdgeKey :: ExactPoint -> ExactPoint -> ExactEdgeKey-canonicalEdgeKey = orderedPair--atomicEdgeFrom :: AtomicEdge leftLabel rightLabel -> ExactPoint-atomicEdgeFrom = fst . exactSegmentEndpoints . atomicEdgeSegment--atomicEdgeTo :: AtomicEdge leftLabel rightLabel -> ExactPoint-atomicEdgeTo = snd . exactSegmentEndpoints . atomicEdgeSegment--atomicKey :: AtomicEdge leftLabel rightLabel -> ExactEdgeKey-atomicKey edge = (atomicEdgeFrom edge, atomicEdgeTo edge)
− src-public/Moonlight/Triangulation/Internal/Overlay/Embedding.hs
@@ -1,539 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DerivingStrategies #-}---- | Bounded certification of local binary64 embedding obligations for a--- declared exact arrangement draft.-module Moonlight.Triangulation.Internal.Overlay.Embedding- ( DraftId (..)- , DraftVertexId- , DraftSegmentId- , DraftSourceId- , DraftIncidence (..)- , DraftNeighborhood (..)- , ExactArrangementDraft (..)- , DraftReference (..)- , OverlayEmbeddingObstruction (..)- , EmbeddingObligation (..)- , EmbeddingResidual- , residualUndischargedObligations- , milestoneOneResidual- , LocalEmbeddingCertificate (..)- , certifyLocalEmbedding- ) where--import Control.DeepSeq (NFData)-import Data.List (sort, tails)-import qualified Data.List as List-import Data.List.NonEmpty (NonEmpty (..))-import qualified Data.List.NonEmpty as NonEmpty-import Data.Map.Strict (Map)-import qualified Data.Map.Strict as Map-import GHC.Generics (Generic)-import Moonlight.Triangulation.Exact- ( ExactPoint- , SegmentRelation- , exactPointToEmbeddingCandidate- , exactSegmentRelation- )-import Moonlight.Triangulation.Internal.BoundaryCycle (cyclePairs)-import Moonlight.Triangulation.Internal.ExactRational (ExactRational)-import qualified Moonlight.Triangulation.Math as Math- ( orient2d- , segmentRelation- )-import Moonlight.Triangulation.Types- ( Point (..)- , PointValidationError- , QueryPoint- , queryPointValue- )---- | Phantom draft kind; three incompatible identifiers share one scalar owner.-data DraftEntity = DraftVertexEntity | DraftSegmentEntity | DraftSourceEntity--newtype DraftId (entity :: DraftEntity) = DraftId Int- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)--type DraftVertexId = DraftId 'DraftVertexEntity-type DraftSegmentId = DraftId 'DraftSegmentEntity-type DraftSourceId = DraftId 'DraftSourceEntity---- | A declared relation between two atomic draft segments.-data DraftIncidence = DraftIncidence- { -- | First declared atomic segment.- draftIncidenceFirstSegment :: !DraftSegmentId- , -- | Second declared atomic segment.- draftIncidenceSecondSegment :: !DraftSegmentId- , -- | Relation declared to hold in both exact and rounded geometry.- draftIncidenceRelation :: !SegmentRelation- }- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)---- | One exact vertex and the cyclic neighbor order declared around it.-data DraftNeighborhood = DraftNeighborhood- { -- | Center of the declared local rotation.- draftNeighborhoodCenter :: !DraftVertexId- , -- | Neighbors in cyclic rotation order.- draftNeighborhoodNeighbors :: !(NonEmpty DraftVertexId)- }- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)---- | A hand-built exact arrangement draft. It contains only declared local--- structure; no arrangement or global crossing search is derived here.-data ExactArrangementDraft = ExactArrangementDraft- { -- | Exact coordinates keyed by draft-local vertex identity.- draftVertices :: !(Map DraftVertexId ExactPoint)- , -- | Atomic segment endpoint identities.- draftSegments :: !(Map DraftSegmentId (DraftVertexId, DraftVertexId))- , -- | Exact split parameters and vertices in source-segment order.- draftSourceMemberships :: !(Map DraftSourceId [(ExactRational, DraftVertexId)])- , -- | Segment relations declared to remain invariant under projection.- draftIncidences :: ![DraftIncidence]- , -- | Cyclic local rotations declared to retain their neighbor order.- draftNeighborhoods :: ![DraftNeighborhood]- }- deriving stock (Eq, Show, Generic)- deriving anyclass (NFData)---- | The typed identity of a missing draft reference.-data DraftReference- = DraftVertexReference !DraftVertexId- | DraftSegmentReference !DraftSegmentId- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)---- | A witness that prevents local embedding certification.-data OverlayEmbeddingObstruction- = DraftReferenceMissing !DraftReference- | VertexProjectionRefused !DraftVertexId !PointValidationError- | RoundedVerticesCollide !DraftVertexId !DraftVertexId !Point- | SplitOrderNotPreserved !DraftSourceId !DraftVertexId !DraftVertexId- | IncidenceRelationChanged- !DraftSegmentId- !DraftSegmentId- !SegmentRelation- !SegmentRelation- !SegmentRelation- | NeighborhoodRotationChanged- !DraftVertexId- !(NonEmpty DraftVertexId)- !(NonEmpty DraftVertexId)- | GlobalRelationAdded- !DraftSegmentId- !DraftSegmentId- !SegmentRelation- | GlobalRelationRemoved- !DraftSegmentId- !DraftSegmentId- !SegmentRelation- | GlobalRelationChanged- !DraftSegmentId- !DraftSegmentId- !SegmentRelation- !SegmentRelation- | ProjectedSegmentCollapsed- !DraftSegmentId- !DraftVertexId- !DraftVertexId- deriving stock (Eq, Show, Generic)- deriving anyclass (NFData)---- | An embedding obligation not discharged by the bounded local certifier.-data EmbeddingObligation- = GlobalNoNewCrossing- deriving stock (Bounded, Enum, Eq, Ord, Show, Generic)- deriving anyclass (NFData)---- | A nonempty collection of obligations deferred to a later owner.-newtype EmbeddingResidual = EmbeddingResidual (NonEmpty EmbeddingObligation)- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)---- | Read the obligations that remain explicitly undischarged.-residualUndischargedObligations- :: EmbeddingResidual- -> NonEmpty EmbeddingObligation-residualUndischargedObligations (EmbeddingResidual obligations) = obligations---- | The Milestone 1 residual: the arrangement sweep has not yet proved global--- absence of new crossings.-milestoneOneResidual :: EmbeddingResidual-milestoneOneResidual = EmbeddingResidual (GlobalNoNewCrossing :| [])---- | A certificate for exactly four local obligations on a declared draft:--- vertex distinctness, source split order, declared incidences, and local--- neighborhood rotation. Global absence of new crossings is unproved until--- the Milestone 2 arrangement sweep supplies the complete obligation set.-data LocalEmbeddingCertificate = LocalEmbeddingCertificate- { -- | Number of rounded vertex-pair distinctness checks discharged.- certificateRoundedVertexDistinctnessCount :: !Int- , -- | Number of adjacent source split-order checks discharged.- certificateSplitOrderPreservationCount :: !Int- , -- | Number of declared incidence checks discharged.- certificateIncidenceRelationPreservationCount :: !Int- , -- | Number of neighborhood rotation entries certified.- certificateNeighborhoodRotationPreservationCount :: !Int- , -- | The admitted candidate projection certified by the four local obligations.- certificateRoundedVertices :: !(Map DraftVertexId QueryPoint)- , -- | The necessarily nonempty global obligation residual.- certificateResidual :: !EmbeddingResidual- }- deriving stock (Eq, Show, Generic)- deriving anyclass (NFData)---- | Certify the four bounded local embedding obligations, collecting every--- witness within each obligation and preserving obligation order.-certifyLocalEmbedding- :: ExactArrangementDraft- -> Either (NonEmpty OverlayEmbeddingObstruction) LocalEmbeddingCertificate-certifyLocalEmbedding draft =- case NonEmpty.nonEmpty (structuralObstructions draft) of- Just obstructions -> Left obstructions- Nothing ->- case projectVertices (draftVertices draft) of- Invalid obstructions -> Left obstructions- Valid projectedVertices ->- certifyProjectedDraft draft projectedVertices--data Validation value- = Invalid !(NonEmpty OverlayEmbeddingObstruction)- | Valid value--instance Functor Validation where- fmap _ (Invalid obstructions) = Invalid obstructions- fmap transform (Valid value) = Valid (transform value)--instance Applicative Validation where- pure = Valid- Invalid left <*> Invalid right = Invalid (left <> right)- Invalid obstructions <*> Valid _ = Invalid obstructions- Valid _ <*> Invalid obstructions = Invalid obstructions- Valid transform <*> Valid value = Valid (transform value)--invalid :: OverlayEmbeddingObstruction -> Validation value-invalid obstruction = Invalid (obstruction :| [])--data ProjectedVertex = ProjectedVertex- { projectedExactPoint :: !ExactPoint- , projectedRoundedPoint :: !QueryPoint- }--data ResolvedSegment = ResolvedSegment- { resolvedSegmentId :: !DraftSegmentId- , resolvedSegmentFrom :: !ResolvedVertex- , resolvedSegmentTo :: !ResolvedVertex- }--data ResolvedVertex = ResolvedVertex- { resolvedVertexId :: !DraftVertexId- , resolvedProjectedVertex :: !ProjectedVertex- }--data ResolvedMembership = ResolvedMembership- { resolvedMembershipParameter :: !ExactRational- , resolvedMembershipVertex :: !ResolvedVertex- }--structuralObstructions- :: ExactArrangementDraft- -> [OverlayEmbeddingObstruction]-structuralObstructions draft =- segmentEndpointObstructions- <> sourceMembershipObstructions- <> incidenceObstructions- <> neighborhoodObstructions- where- vertices = draftVertices draft- segments = draftSegments draft- missingVertex vertexId =- [ DraftReferenceMissing (DraftVertexReference vertexId)- | Map.notMember vertexId vertices- ]- missingSegment segmentId =- [ DraftReferenceMissing (DraftSegmentReference segmentId)- | Map.notMember segmentId segments- ]- segmentEndpointObstructions =- concatMap- (\(_, (from, to)) -> missingVertex from <> missingVertex to)- (Map.toAscList segments)- sourceMembershipObstructions =- concatMap- (concatMap (missingVertex . snd) . snd)- (Map.toAscList (draftSourceMemberships draft))- incidenceObstructions =- concatMap- ( \incidence ->- missingSegment (draftIncidenceFirstSegment incidence)- <> missingSegment (draftIncidenceSecondSegment incidence)- )- (draftIncidences draft)- neighborhoodObstructions =- concatMap- ( \neighborhood ->- missingVertex (draftNeighborhoodCenter neighborhood)- <> concatMap missingVertex (draftNeighborhoodNeighbors neighborhood)- )- (draftNeighborhoods draft)--projectVertices- :: Map DraftVertexId ExactPoint- -> Validation (Map DraftVertexId ProjectedVertex)-projectVertices =- Map.traverseWithKey- ( \vertexId point ->- case exactPointToEmbeddingCandidate point of- Left projectionError ->- invalid (VertexProjectionRefused vertexId projectionError)- Right roundedPoint -> Valid (ProjectedVertex point roundedPoint)- )--certifyProjectedDraft- :: ExactArrangementDraft- -> Map DraftVertexId ProjectedVertex- -> Either (NonEmpty OverlayEmbeddingObstruction) LocalEmbeddingCertificate-certifyProjectedDraft draft projectedVertices =- case resolvedFailures of- Left obstruction -> Left (obstruction :| [])- Right localFailures ->- case NonEmpty.nonEmpty (collisionFailures <> localFailures) of- Just obstructions -> Left obstructions- Nothing ->- Right- LocalEmbeddingCertificate- { certificateRoundedVertexDistinctnessCount = distinctnessCount- , certificateSplitOrderPreservationCount = splitOrderCount- , certificateIncidenceRelationPreservationCount = incidenceCount- , certificateNeighborhoodRotationPreservationCount = neighborhoodCount- , certificateRoundedVertices = Map.map projectedRoundedPoint projectedVertices- , certificateResidual = milestoneOneResidual- }- where- collisionFailures = roundedVertexCollisionObstructions projectedVertices- resolvedFailures = do- splitFailures <- traverse (uncurry resolveSource) (Map.toAscList (draftSourceMemberships draft))- incidenceFailures <- traverse resolveIncidence (draftIncidences draft)- neighborhoodFailures <- traverse resolveNeighborhood (draftNeighborhoods draft)- pure (concat splitFailures <> concat incidenceFailures <> concat neighborhoodFailures)- resolveVertex vertexId =- case Map.lookup vertexId projectedVertices of- Nothing -> Left (DraftReferenceMissing (DraftVertexReference vertexId))- Just projectedVertex -> Right (ResolvedVertex vertexId projectedVertex)- resolveSegment segmentId =- case Map.lookup segmentId (draftSegments draft) of- Nothing -> Left (DraftReferenceMissing (DraftSegmentReference segmentId))- Just (from, to) ->- ResolvedSegment segmentId- <$> resolveVertex from- <*> resolveVertex to- resolveSource sourceId memberships =- splitOrderObstructions sourceId- <$> traverse- ( \(parameter, vertexId) ->- ResolvedMembership parameter <$> resolveVertex vertexId- )- memberships- resolveIncidence incidence =- incidenceRelationObstructions- (draftIncidenceRelation incidence)- <$> resolveSegment (draftIncidenceFirstSegment incidence)- <*> resolveSegment (draftIncidenceSecondSegment incidence)- resolveNeighborhood neighborhood =- neighborhoodRotationObstructions- <$> resolveVertex (draftNeighborhoodCenter neighborhood)- <*> traverse resolveVertex (draftNeighborhoodNeighbors neighborhood)- vertexCount = Map.size projectedVertices- distinctnessCount = vertexCount * (vertexCount - 1) `quot` 2- splitOrderCount =- sum- ( map- (max 0 . subtract 1 . length)- (Map.elems (draftSourceMemberships draft))- )- incidenceCount = length (draftIncidences draft)- neighborhoodCount =- sum- ( map- (length . draftNeighborhoodNeighbors)- (draftNeighborhoods draft)- )--roundedVertexCollisionObstructions- :: Map DraftVertexId ProjectedVertex- -> [OverlayEmbeddingObstruction]-roundedVertexCollisionObstructions projectedVertices =- [ RoundedVerticesCollide leftId rightId (queryPointValue roundedPoint)- | (roundedPoint, vertexIds) <- Map.toAscList verticesByRoundedPoint- , (leftId : remainingIds) <- tails (sort vertexIds)- , rightId <- remainingIds- ]- where- verticesByRoundedPoint =- Map.fromListWith (<>)- [ (projectedRoundedPoint projectedVertex, [vertexId])- | (vertexId, projectedVertex) <- Map.toAscList projectedVertices- ]--splitOrderObstructions- :: DraftSourceId- -> [ResolvedMembership]- -> [OverlayEmbeddingObstruction]-splitOrderObstructions sourceId memberships =- case memberships of- firstMembership : secondMembership : remainingMemberships ->- let finalMembership =- List.foldl' (\_ current -> current) secondMembership remainingMemberships- sourceFrom = roundedMembershipPoint firstMembership- sourceTo = roundedMembershipPoint finalMembership- in [ SplitOrderNotPreserved- sourceId- (resolvedVertexId (resolvedMembershipVertex leftMembership))- (resolvedVertexId (resolvedMembershipVertex rightMembership))- | (leftMembership, rightMembership) <-- zip- memberships- (drop 1 memberships)- , compare- (resolvedMembershipParameter leftMembership)- (resolvedMembershipParameter rightMembership)- /= roundedOrderAlong- sourceFrom- sourceTo- (roundedMembershipPoint leftMembership)- (roundedMembershipPoint rightMembership)- ]- _ -> []--roundedMembershipPoint :: ResolvedMembership -> Point-roundedMembershipPoint =- queryPointValue- . projectedRoundedPoint- . resolvedProjectedVertex- . resolvedMembershipVertex--roundedOrderAlong :: Point -> Point -> Point -> Point -> Ordering-roundedOrderAlong- (Point sourceFromX sourceFromY)- (Point sourceToX sourceToY)- (Point leftX leftY)- (Point rightX rightY) =- let directionX = sourceToX - sourceFromX- directionY = sourceToY - sourceFromY- in if abs directionX >= abs directionY- then- if directionX >= 0- then compare leftX rightX- else compare rightX leftX- else- if directionY >= 0- then compare leftY rightY- else compare rightY leftY--incidenceRelationObstructions- :: SegmentRelation- -> ResolvedSegment- -> ResolvedSegment- -> [OverlayEmbeddingObstruction]-incidenceRelationObstructions declaredRelation firstSegment secondSegment =- [ IncidenceRelationChanged- (resolvedSegmentId firstSegment)- (resolvedSegmentId secondSegment)- declaredRelation- exactRelation- roundedRelation- | exactRelation /= declaredRelation || roundedRelation /= declaredRelation- ]- where- exactRelation =- relationFor exactSegmentRelation projectedExactPoint firstSegment secondSegment- roundedRelation =- relationFor Math.segmentRelation roundedProjectedPoint firstSegment secondSegment--relationFor- :: (point -> point -> point -> point -> SegmentRelation)- -> (ProjectedVertex -> point)- -> ResolvedSegment- -> ResolvedSegment- -> SegmentRelation-relationFor relation project firstSegment secondSegment =- relation- (project (resolvedProjectedVertex (resolvedSegmentFrom firstSegment)))- (project (resolvedProjectedVertex (resolvedSegmentTo firstSegment)))- (project (resolvedProjectedVertex (resolvedSegmentFrom secondSegment)))- (project (resolvedProjectedVertex (resolvedSegmentTo secondSegment)))--neighborhoodRotationObstructions- :: ResolvedVertex- -> NonEmpty ResolvedVertex- -> [OverlayEmbeddingObstruction]-neighborhoodRotationObstructions center exactRotation =- [ NeighborhoodRotationChanged- (resolvedVertexId center)- exactVertexRotation- roundedVertexRotation- | roundedRotationDescents > 1- ]- where- exactVertexRotation = fmap resolvedVertexId exactRotation- roundedRotationDescents =- List.foldl'- (\descentCount (left, right) ->- descentCount- + if compareRoundedAround center left right == GT- then 1- else 0)- (0 :: Int)- (cyclePairs exactRotation)- roundedVertexRotation =- fmap resolvedVertexId- (NonEmpty.sortBy (compareRoundedAround center) exactRotation)--compareRoundedAround- :: ResolvedVertex- -> ResolvedVertex- -> ResolvedVertex- -> Ordering-compareRoundedAround center left right =- compareRoundedAroundWith- ( Math.orient2d- (roundedVertexPoint center)- (roundedVertexPoint left)- (roundedVertexPoint right)- )- center- left- right--compareRoundedAroundWith- :: Ordering- -> ResolvedVertex- -> ResolvedVertex- -> ResolvedVertex- -> Ordering-compareRoundedAroundWith roundedOrientation center left right =- case compare (roundedVectorHalf center left) (roundedVectorHalf center right) of- EQ ->- case roundedOrientation of- GT -> LT- LT -> GT- EQ -> compare (roundedVertexPoint left) (roundedVertexPoint right)- ordering -> ordering--roundedVectorHalf :: ResolvedVertex -> ResolvedVertex -> Bool-roundedVectorHalf center neighbor =- let Point centerX centerY = roundedVertexPoint center- Point neighborX neighborY = roundedVertexPoint neighbor- deltaX = neighborX - centerX- deltaY = neighborY - centerY- in deltaY < 0 || (deltaY == 0 && deltaX < 0)--roundedVertexPoint :: ResolvedVertex -> Point-roundedVertexPoint = roundedProjectedPoint . resolvedProjectedVertex--roundedProjectedPoint :: ProjectedVertex -> Point-roundedProjectedPoint = queryPointValue . projectedRoundedPoint
− src-public/Moonlight/Triangulation/Internal/Overlay/Resident.hs
@@ -1,923 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE ScopedTypeVariables #-}---- | Resident DCEL descent and exact-cell gluing. The certified arrangement is--- authoritative at this seam; diagonal schedules may change only its resident--- triangulation, never its exact cell descriptors.-module Moonlight.Triangulation.Internal.Overlay.Resident- ( OverlayDiagonalSchedule (..)- , residentOverlay- , faceLabels- , faceCarriesExactArea- , regionFaceLabels- , vertexSupport- , edgeSupport- ) where--import Control.Monad (foldM)-import Data.Bifunctor (first)-import Data.Foldable (traverse_)-import Data.List (partition, sort, sortBy)-import qualified Data.List as List-import qualified Data.List.NonEmpty as NonEmpty-import qualified Data.Map.Strict as Map-import Data.Map.Strict (Map)-import Data.Maybe (listToMaybe, mapMaybe)-import qualified Data.Sequence as Seq-import qualified Data.Set as Set-import Data.Set (Set)-import qualified Data.Vector as V-import qualified Moonlight.Triangulation.Dcel as Dcel-import Moonlight.Triangulation.Dcel- ( faceData- , faceDirectedEdges- , faceVertices- , imapUndirectedEdges- , incidentFace- , isConstraintEdge- , numInnerFaces- , numVertices- , outerFace- , undirectedEndpoints- , vertexData- , vertexOutgoingEdges- )-import Moonlight.Triangulation.Exact (ExactPoint, exactOrient2d)-import Moonlight.Triangulation.FloodFillIterator- ( FaceComponent- , componentBoundary- , faceComponentFaces- , faceComponentsBy- )-import Moonlight.Triangulation.Internal.HandleDefs- ( DirectedEdgeId- , FaceId- , UndirectedEdgeId- , VertexId- , asUndirected- , directedPair- , reverseEdge- )-import Moonlight.Triangulation.Handles.Iterators.FixedIterators- ( allFaces- , innerFaces- , undirectedEdges- )-import Moonlight.Triangulation.Internal.BoxedPaged (boxedFromVector)-import Moonlight.Triangulation.Internal.BoundaryCycle- ( consecutivePairs- , traceOrientedBoundaryCircuits- )-import Moonlight.Triangulation.Internal.Canonical (canonicalize)-import Moonlight.Triangulation.Internal.Cdt.Build (constrainedDelaunay)-import Moonlight.Triangulation.Internal.Cdt.Types (CdtError (..))-import Moonlight.Triangulation.Internal.Overlay.Arrangement- ( ArrangementMetrics (..)- , AtomicEdge (..)- , atomicEdgeFrom- , atomicEdgeTo- , CertifiedArrangement (..)- , ExactEdgeKey- , OverlayVertexSeed (..)- , atomicKey- , canonicalEdgeKey- , compareAround- )-import Moonlight.Triangulation.Internal.Overlay.Types-import Moonlight.Triangulation.Internal.Region.Publication- ( polygonComponentFromBoundaryCoordinates- )-import Moonlight.Triangulation.Internal.Region.Types- ( ExactLoop- , PolygonComponent- , RegionPublicationError (..)- )-import Moonlight.Triangulation.Internal.Representation- ( BuildResult (..)- , Triangulation (..)- )-import Moonlight.Triangulation.Internal.Types- ( ConstraintMode (Constrained)- , ElementDefaults (..)- )-import Moonlight.Triangulation.Region (exactLoop)--data OverlayDiagonalSchedule- = CanonicalOverlayDiagonals- | FlipFirstAdmissibleDiagonal- deriving stock (Eq, Ord, Show)--data ComponentDraft leftLabel rightLabel = ComponentDraft- { componentDraftLabels :: !(leftLabel, rightLabel)- , componentDraftFaces :: !FaceComponent- , componentDraftPolygon :: !PolygonComponent- , componentDraftTouchesOuter :: !Bool- }--residentOverlay- :: (Ord leftLabel, Ord rightLabel)- => OverlayDiagonalSchedule- -> (leftLabel, rightLabel)- -> CertifiedArrangement leftLabel rightLabel- -> Either- (OverlayError leftLabel rightLabel)- (OverlayResult leftLabel rightLabel)-residentOverlay diagonalSchedule outsidePair certified = do- let atomicVector = certifiedAtomicEdges certified- seeds = certifiedVertexSeeds certified- constraints = certifiedConstraints certified- metrics = certifiedMetrics certified- defaults = ElementDefaults () () ()- built <- first OverlayBuildFailed (constrainedDelaunay defaults seeds constraints)- let resident = buildTriangulation built- if numVertices resident == V.length seeds- then Right ()- else- Left- ( OverlayProvenanceIncomplete- (OverlayEmbeddedVertexCountMismatch (V.length seeds) (numVertices resident))- )- canonicalResident <-- first (OverlayBuildFailed . CdtBuildError) (canonicalize resident)- let atomicByKey =- Map.fromList- [ (atomicKey edge, edge)- | edge <- V.toList atomicVector- ]- validateAtomicConstraints canonicalResident atomicByKey- scheduledResident <-- applyDiagonalSchedule- diagonalSchedule- defaults- seeds- constraints- canonicalResident- labelledFaces <- labelResidentFaces outsidePair scheduledResident atomicByKey- (fullDimensionalFaces, collapsedFaces) <-- if Set.null (certifiedInexactEmbeddingPoints certified)- then- Right- ( Set.fromList (innerFaces scheduledResident)- , Set.empty- )- else- exactFaceDimensions- (certifiedInexactEmbeddingPoints certified)- scheduledResident- componentDrafts <-- residentComponentDrafts- scheduledResident- labelledFaces- fullDimensionalFaces- let (unboundedDrafts, boundedDrafts) =- partitionDrafts outsidePair componentDrafts- numberedBounded <- numberBoundedComponents boundedDrafts- let fullCellIdByFace =- Map.fromList- ( [ (face, OverlayCellId 0)- | draftComponent <- unboundedDrafts- , face <- faceComponentFaces (componentDraftFaces draftComponent)- ]- <> [ (face, cellId)- | (cellId, draftComponent) <- numberedBounded- , face <- faceComponentFaces (componentDraftFaces draftComponent)- ]- )- collapsedCellIds <-- descendCollapsedFaces- scheduledResident- atomicByKey- fullCellIdByFace- collapsedFaces- let cellIdByFace = fullCellIdByFace <> collapsedCellIds- unboundedLoops <-- unboundedCellBoundaryLoops scheduledResident atomicByKey cellIdByFace- let cells =- V.fromList- ( OverlayCell- { overlayCellLeft = fst outsidePair- , overlayCellRight = snd outsidePair- , overlayCellGeometry = UnboundedOverlayCell unboundedLoops- }- : [ OverlayCell- { overlayCellLeft = fst (componentDraftLabels draftComponent)- , overlayCellRight = snd (componentDraftLabels draftComponent)- , overlayCellGeometry =- BoundedOverlayCell (componentDraftPolygon draftComponent)- }- | (_, draftComponent) <- numberedBounded- ]- )- withFaces <- attachFaceCells collapsedFaces cellIdByFace scheduledResident- let withVertices = attachOverlayVertices withFaces- withEdges =- imapUndirectedEdges- OverlayDiagonal- (\edge _ ->- case Map.lookup (residentEdgeKey withVertices edge) atomicByKey of- Nothing -> OverlayDiagonal- Just atomic -> OverlayBoundary (atomicEdgeOrigin atomic))- withVertices- receipt =- OverlayReceipt- { overlayInputSegments = arrangementInputSegments metrics- , overlayRelationEvents = arrangementRelationEvents metrics- , overlayExactCrossings = arrangementExactCrossings metrics- , overlayOverlapIntervals = arrangementOverlapIntervals metrics- , overlayAtomicEdges = V.length atomicVector- , overlayOutputVertices = numVertices withEdges- , overlayArrangementCells = V.length cells- , overlayResidentFaces = numInnerFaces withEdges- , overlayEmbeddingCandidates = arrangementEmbeddingCandidates metrics- , overlayTotalRelationChecks = arrangementTotalRelationChecks metrics- , overlaySweepMaximumHeight = arrangementSweepMaximumHeight metrics- }- pure- OverlayResult- { overlayResultTriangulation = withEdges- , overlayResultCells = cells- , overlayResultOutsideLabels = outsidePair- , overlayResultReceipt = receipt- }--applyDiagonalSchedule- :: OverlayDiagonalSchedule- -> ElementDefaults () () ()- -> V.Vector OverlayVertexSeed- -> V.Vector (Int, Int)- -> Triangulation 'Constrained OverlayVertexSeed () () ()- -> Either- (OverlayError leftLabel rightLabel)- (Triangulation 'Constrained OverlayVertexSeed () () ())-applyDiagonalSchedule CanonicalOverlayDiagonals _ _ _ triangulation =- Right triangulation-applyDiagonalSchedule- FlipFirstAdmissibleDiagonal- defaults- seeds- constraints- triangulation =- case firstAlternativeDiagonal triangulation of- Nothing -> Right triangulation- Just (from, to) -> do- let indexByPoint =- Map.fromList- [ (seedExactPoint seed, index)- | (index, seed) <- V.toList (V.indexed seeds)- ]- fromIndex <- requireSeedIndex indexByPoint from- toIndex <- requireSeedIndex indexByPoint to- rebuilt <-- first OverlayBuildFailed- (constrainedDelaunay defaults seeds (V.snoc constraints (fromIndex, toIndex)))- first (OverlayBuildFailed . CdtBuildError)- (canonicalize (buildTriangulation rebuilt))--requireSeedIndex- :: Map ExactPoint Int- -> ExactPoint- -> Either (OverlayError leftLabel rightLabel) Int-requireSeedIndex indexByPoint point =- case Map.lookup point indexByPoint of- Just index -> Right index- Nothing -> Left (OverlayProvenanceIncomplete (OverlayExactVertexMissing point))--firstAlternativeDiagonal- :: Triangulation 'Constrained OverlayVertexSeed () () ()- -> Maybe ExactEdgeKey-firstAlternativeDiagonal triangulation =- listToMaybe- (mapMaybe (alternativeDiagonal triangulation) (undirectedEdges triangulation))--alternativeDiagonal- :: Triangulation 'Constrained OverlayVertexSeed () () ()- -> UndirectedEdgeId- -> Maybe ExactEdgeKey-alternativeDiagonal triangulation edge- | isConstraintEdge triangulation edge = Nothing- | leftFace == outerFace || rightFace == outerFace = Nothing- | exactOrient2d c d b == GT && exactOrient2d d c a == GT =- Just (canonicalEdgeKey c d)- | otherwise = Nothing- where- (forward, backward) = directedPair edge- leftFace = incidentFace triangulation forward- rightFace = incidentFace triangulation backward- a = seedExactPoint (vertexData triangulation (Dcel.origin triangulation forward))- b = seedExactPoint (vertexData triangulation (Dcel.origin triangulation backward))- c =- seedExactPoint- (vertexData triangulation (Dcel.origin triangulation (Dcel.previous triangulation forward)))- d =- seedExactPoint- (vertexData triangulation (Dcel.origin triangulation (Dcel.previous triangulation backward)))-labelResidentFaces- :: (Ord leftLabel, Ord rightLabel)- => (leftLabel, rightLabel)- -> Triangulation 'Constrained OverlayVertexSeed () () ()- -> Map ExactEdgeKey (AtomicEdge leftLabel rightLabel)- -> Either- (OverlayError leftLabel rightLabel)- (Map FaceId (leftLabel, rightLabel))-labelResidentFaces outsideLabels triangulation atomicByKey = do- labelled <-- descendFaceTransitions- triangulation- atomicByKey- (Map.singleton outerFace outsideLabels)- (Seq.singleton outerFace)- case- [ face- | face <- innerFaces triangulation- , Map.notMember face labelled- ] of- missing : _ ->- Left (OverlayProvenanceIncomplete (OverlayResidentFaceUnassigned missing))- [] -> Right (Map.delete outerFace labelled)--seedDirectedEndpoints- :: Triangulation mode OverlayVertexSeed directed undirected face- -> DirectedEdgeId- -> ExactEdgeKey-seedDirectedEndpoints triangulation edge =- ( seedExactPoint (vertexData triangulation (Dcel.origin triangulation edge))- , seedExactPoint (vertexData triangulation (Dcel.destination triangulation edge))- )--swapTransition :: (label, label) -> (label, label)-swapTransition (fromLabel, toLabel) = (toLabel, fromLabel)--descendFaceTransitions- :: (Eq leftLabel, Eq rightLabel)- => Triangulation 'Constrained OverlayVertexSeed () () ()- -> Map ExactEdgeKey (AtomicEdge leftLabel rightLabel)- -> Map FaceId (leftLabel, rightLabel)- -> Seq.Seq FaceId- -> Either- (OverlayError leftLabel rightLabel)- (Map FaceId (leftLabel, rightLabel))-descendFaceTransitions triangulation atomicByKey labelled queued =- case Seq.viewl queued of- Seq.EmptyL -> Right labelled- face Seq.:< remaining ->- case Map.lookup face labelled of- Nothing ->- Left (OverlayProvenanceIncomplete (OverlayResidentFaceUnassigned face))- Just current -> do- (nextLabels, nextQueue) <-- foldM- (descendFaceTransition triangulation atomicByKey face current)- (labelled, remaining)- (faceDirectedEdges triangulation face)- descendFaceTransitions triangulation atomicByKey nextLabels nextQueue--descendFaceTransition- :: (Eq leftLabel, Eq rightLabel)- => Triangulation 'Constrained OverlayVertexSeed () () ()- -> Map ExactEdgeKey (AtomicEdge leftLabel rightLabel)- -> FaceId- -> (leftLabel, rightLabel)- -> (Map FaceId (leftLabel, rightLabel), Seq.Seq FaceId)- -> DirectedEdgeId- -> Either- (OverlayError leftLabel rightLabel)- (Map FaceId (leftLabel, rightLabel), Seq.Seq FaceId)-descendFaceTransition triangulation atomicByKey source current (labelled, queued) directed = do- let edge = asUndirected directed- destinationFace = incidentFace triangulation (reverseEdge directed)- (leftTransition, rightTransition) <-- case Map.lookup (residentSeedEdgeKey triangulation edge) atomicByKey of- Nothing -> Right (Nothing, Nothing)- Just atomic -> orientedTransitions edge directed atomic- let- expected =- ( maybe (fst current) fst leftTransition- , maybe (snd current) fst rightTransition- )- derived =- ( maybe (fst current) snd leftTransition- , maybe (snd current) snd rightTransition- )- if current == expected- then- case Map.lookup destinationFace labelled of- Nothing ->- Right- ( Map.insert destinationFace derived labelled- , queued Seq.|> destinationFace- )- Just existing- | existing == derived -> Right (labelled, queued)- | otherwise ->- Left- ( OverlayArrangementInvalid- ( OverlayResidentFaceLabelConflict- destinationFace- edge- existing- derived- )- )- else- Left- ( OverlayArrangementInvalid- (OverlayTransitionSourceMismatch source edge current expected)- )- where- orientedTransitions edge candidateDirection atomic- | directedEndpoints == (atomicEdgeFrom atomic, atomicEdgeTo atomic) =- Right- ( atomicEdgeLeftTransition atomic- , atomicEdgeRightTransition atomic- )- | directedEndpoints == (atomicEdgeTo atomic, atomicEdgeFrom atomic) =- Right- ( swapTransition <$> atomicEdgeLeftTransition atomic- , swapTransition <$> atomicEdgeRightTransition atomic- )- | otherwise =- Left- ( OverlayProvenanceIncomplete- ( OverlayAtomicConstraintOrientationMismatch- edge- (atomicEdgeFrom atomic)- (atomicEdgeTo atomic)- )- )- where- directedEndpoints = seedDirectedEndpoints triangulation candidateDirection--exactFaceDimensions- :: Set ExactPoint- -> Triangulation 'Constrained OverlayVertexSeed directed undirected face- -> Either- (OverlayError leftLabel rightLabel)- (Set FaceId, Set FaceId)-exactFaceDimensions inexactEmbeddingPoints triangulation = do- orientations <-- traverse- ( \face ->- (face,)- <$> exactFaceOrientation- inexactEmbeddingPoints- triangulation- face- )- (innerFaces triangulation)- case [face | (face, LT) <- orientations] of- reversed : _ ->- Left- ( OverlayArrangementInvalid- (OverlayResidentFaceOrientationReversed reversed)- )- [] ->- Right- ( Set.fromList [face | (face, GT) <- orientations]- , Set.fromList [face | (face, EQ) <- orientations]- )--exactFaceOrientation- :: Set ExactPoint- -> Triangulation mode OverlayVertexSeed directed undirected face- -> FaceId- -> Either (OverlayError leftLabel rightLabel) Ordering-exactFaceOrientation inexactEmbeddingPoints triangulation face =- case- map- (seedExactPoint . vertexData triangulation)- (faceVertices triangulation face) of- points@[firstPoint, secondPoint, thirdPoint]- | any (`Set.member` inexactEmbeddingPoints) points ->- Right (exactOrient2d firstPoint secondPoint thirdPoint)- | otherwise -> Right GT- vertices ->- Left- ( OverlayArrangementInvalid- (OverlayResidentFaceArity face (length vertices))- )--residentComponentDrafts- :: forall leftLabel rightLabel.- (Ord leftLabel, Ord rightLabel)- => Triangulation 'Constrained OverlayVertexSeed () () ()- -> Map FaceId (leftLabel, rightLabel)- -> Set FaceId- -> Either- (OverlayError leftLabel rightLabel)- [ComponentDraft leftLabel rightLabel]-residentComponentDrafts triangulation labels fullDimensionalFaces =- traverse- convert- (faceComponentsBy triangulation labelFullDimensionalFace (const True))- where- exactPointAt :: VertexId -> Either RegionPublicationError ExactPoint- exactPointAt vertex =- Right (seedExactPoint (vertexData triangulation vertex))- labelFullDimensionalFace :: FaceId -> Maybe (leftLabel, rightLabel)- labelFullDimensionalFace face- | Set.member face fullDimensionalFaces = Map.lookup face labels- | otherwise = Nothing- convert- :: ((leftLabel, rightLabel), FaceComponent)- -> Either- (OverlayError leftLabel rightLabel)- (ComponentDraft leftLabel rightLabel)- convert (componentLabels, component) = do- boundary <-- first- (OverlayRegionPublicationFailed . RegionBoundaryObstruction)- (componentBoundary triangulation component)- polygon <-- first OverlayRegionPublicationFailed- (polygonComponentFromBoundaryCoordinates exactPointAt boundary)- pure- ComponentDraft- { componentDraftLabels = componentLabels- , componentDraftFaces = component- , componentDraftPolygon = polygon- , componentDraftTouchesOuter = touchesOuter triangulation component- }--touchesOuter- :: Triangulation mode vertex directed undirected face- -> FaceComponent- -> Bool-touchesOuter triangulation component =- any- ( any ((== outerFace) . incidentFace triangulation . reverseEdge)- . faceDirectedEdges triangulation- )- (faceComponentFaces component)--partitionDrafts- :: (Eq leftLabel, Eq rightLabel)- => (leftLabel, rightLabel)- -> [ComponentDraft leftLabel rightLabel]- -> ([ComponentDraft leftLabel rightLabel], [ComponentDraft leftLabel rightLabel])-partitionDrafts outsidePair =- partition- (\draftComponent ->- componentDraftTouchesOuter draftComponent- && componentDraftLabels draftComponent == outsidePair)--sortComponentDrafts- :: (Ord leftLabel, Ord rightLabel)- => [ComponentDraft leftLabel rightLabel]- -> [ComponentDraft leftLabel rightLabel]-sortComponentDrafts =- sortBy- (\left right ->- compare- (componentDraftPolygon left, componentDraftLabels left)- (componentDraftPolygon right, componentDraftLabels right))--numberBoundedComponents- :: (Ord leftLabel, Ord rightLabel)- => [ComponentDraft leftLabel rightLabel]- -> Either- (OverlayError leftLabel rightLabel)- [(OverlayCellId, ComponentDraft leftLabel rightLabel)]-numberBoundedComponents drafts =- case- [ componentDraftPolygon left- | (left, right) <- consecutivePairs ordered- , componentSignature left == componentSignature right- ] of- duplicate : _ ->- Left- ( OverlayArrangementInvalid- (OverlayDuplicateCellSignature duplicate)- )- [] ->- Right- ( zipWith- (\index component -> (OverlayCellId index, component))- [1 ..]- ordered- )- where- ordered = sortComponentDrafts drafts- componentSignature- :: ComponentDraft leftLabel' rightLabel'- -> (PolygonComponent, (leftLabel', rightLabel'))- componentSignature component =- (componentDraftPolygon component, componentDraftLabels component)---- | A binary64 triangle whose authoritative vertices are collinear is not an--- exact two-cell. It may remain in the resident triangulation only when its--- representation diagonals descend uniquely to one full-dimensional exact--- cell; this prevents the artifact from gluing exact components through a--- zero-area wedge.-descendCollapsedFaces- :: forall leftLabel rightLabel.- Triangulation 'Constrained OverlayVertexSeed () () ()- -> Map ExactEdgeKey (AtomicEdge leftLabel rightLabel)- -> Map FaceId OverlayCellId- -> Set FaceId- -> Either- (OverlayError leftLabel rightLabel)- (Map FaceId OverlayCellId)-descendCollapsedFaces triangulation atomicByKey fullCellIds collapsedFaces =- Map.fromList . concat <$> traverse resolveComponent collapsedComponents- where- exactCellIds = Map.insert outerFace (OverlayCellId 0) fullCellIds- collapsedComponents =- fmap snd- ( faceComponentsBy- triangulation- labelCollapsedFace- isRepresentationDiagonal- )-- labelCollapsedFace face- | Set.member face collapsedFaces = Just ()- | otherwise = Nothing-- isRepresentationDiagonal edge =- Map.notMember (residentSeedEdgeKey triangulation edge) atomicByKey-- resolveComponent component =- case faceComponentFaces component of- [] ->- Left- ( OverlayArrangementInvalid- OverlayFaceComponentEmpty- )- componentFaces@(firstFace : remainingFaces) -> do- let componentWitness = firstFace NonEmpty.:| remainingFaces- adjacentCellIds =- Set.fromList- [ cellId- | face <- componentFaces- , directed <- faceDirectedEdges triangulation face- , isRepresentationDiagonal (asUndirected directed)- , Just cellId <-- [Map.lookup (incidentFace triangulation (reverseEdge directed)) exactCellIds]- ]- cellId <-- case Set.toAscList adjacentCellIds of- [singleCellId] -> Right singleCellId- [] ->- Left- ( OverlayArrangementInvalid- (OverlayCollapsedFacesUnowned componentWitness)- )- firstCellId : remainingCellIds ->- Left- ( OverlayArrangementInvalid- ( OverlayCollapsedFacesAmbiguous- componentWitness- (firstCellId NonEmpty.:| remainingCellIds)- )- )- pure (fmap (,cellId) componentFaces)---- | Trace the finite boundary cycles of the unbounded exact cell from atomic--- edges only. Resident hull edges and Delaunay diagonals are absent by--- construction; the DCEL contributes only the already-proved incident-cell--- gluing needed to orient each atomic edge with cell zero on its left.-unboundedCellBoundaryLoops- :: Triangulation 'Constrained OverlayVertexSeed () () ()- -> Map ExactEdgeKey (AtomicEdge leftLabel rightLabel)- -> Map FaceId OverlayCellId- -> Either (OverlayError leftLabel rightLabel) [ExactLoop]-unboundedCellBoundaryLoops triangulation atomicByKey cellIdByFace = do- orientedEdges <-- Set.fromList . concat- <$> traverse orientAtomicEdge (undirectedEdges triangulation)- traceBoundaryCycles orientedEdges- where- orientAtomicEdge edge- | Map.notMember (residentSeedEdgeKey triangulation edge) atomicByKey = Right []- | otherwise = do- let (forward, backward) = directedPair edge- forwardCell <- faceCellId (incidentFace triangulation forward)- backwardCell <- faceCellId (incidentFace triangulation backward)- pure- ( case (forwardCell == OverlayCellId 0, backwardCell == OverlayCellId 0) of- (True, False) -> [seedDirectedEndpoints triangulation forward]- (False, True) -> [seedDirectedEndpoints triangulation backward]- _ -> []- )- faceCellId face- | face == outerFace = Right (OverlayCellId 0)- | otherwise =- case Map.lookup face cellIdByFace of- Just cellId -> Right cellId- Nothing -> Left (OverlayProvenanceIncomplete (OverlayResidentFaceUnassigned face))--traceBoundaryCycles- :: Set ExactEdgeKey- -> Either (OverlayError leftLabel rightLabel) [ExactLoop]-traceBoundaryCycles orientedEdges = do- pointCycles <-- traceOrientedBoundaryCircuits- fst- snd- (\seed _ ->- OverlayArrangementInvalid- (uncurry OverlayCellCycleDidNotClose seed))- outgoing- orientedEdges- sort- <$> traverse- ( first- (OverlayRegionPublicationFailed . RegionValidationObstruction)- . exactLoop- )- pointCycles- where- outgoing =- Map.mapWithKey orderAroundOrigin- ( Set.foldl'- (\byOrigin edge@(from, _) -> Map.insertWith (<>) from [edge] byOrigin)- Map.empty- orientedEdges- )- orderAroundOrigin :: ExactPoint -> [ExactEdgeKey] -> [ExactEdgeKey]- orderAroundOrigin origin =- sortBy (\(_, left) (_, right) -> compareAround origin left right)--attachFaceCells- :: forall leftLabel rightLabel.- Set FaceId- -> Map FaceId OverlayCellId- -> Triangulation 'Constrained OverlayVertexSeed () () ()- -> Either- (OverlayError leftLabel rightLabel)- (Triangulation 'Constrained OverlayVertexSeed () () OverlayFace)-attachFaceCells collapsedFaces cellIdByFace triangulation = do- payloads <- V.fromList <$> traverse facePayload (allFaces triangulation)- let outerPayload = OverlayCellFace (OverlayCellId 0)- defaults = triElementDefaults triangulation- pure- triangulation- { triFaceData = boxedFromVector (Just outerPayload) payloads- , triElementDefaults = defaults{defaultFaceData = outerPayload}- }- where- facePayload- :: FaceId- -> Either (OverlayError leftLabel rightLabel) OverlayFace- facePayload face- | face == outerFace = Right (OverlayCellFace (OverlayCellId 0))- | otherwise =- case Map.lookup face cellIdByFace of- Just cellId- | Set.member face collapsedFaces ->- Right (OverlayCollapsedFace cellId)- | otherwise -> Right (OverlayCellFace cellId)- Nothing ->- Left- ( OverlayProvenanceIncomplete- (OverlayResidentFaceUnassigned face)- )--attachOverlayVertices- :: Triangulation 'Constrained OverlayVertexSeed () () face- -> Triangulation 'Constrained OverlayVertex () () face-attachOverlayVertices =- Dcel.mapVertices- (\seed -> OverlayVertex (seedExactPoint seed) (seedOrigin seed))--cellPayload- :: V.Vector (OverlayCell leftLabel rightLabel)- -> OverlayCellId- -> Either OverlayCellWitness (OverlayCell leftLabel rightLabel)-cellPayload cells cellId@(OverlayCellId index) =- case cells V.!? index of- Just cell -> Right cell- Nothing -> Left (OverlayCellPayloadMissing cellId)--faceLabels- :: OverlayResult leftLabel rightLabel- -> FaceId- -> Either OverlayCellWitness (leftLabel, rightLabel)-faceLabels result face = do- cell <-- cellPayload- (overlayResultCells result)- (overlayFaceCellId (faceData (overlayResultTriangulation result) face))- pure (overlayCellLeft cell, overlayCellRight cell)---- | Whether a resident triangle has nonzero area under the authoritative--- exact coordinate section. Binary64-only wedges are representation charts,--- never exact two-cells.-faceCarriesExactArea- :: OverlayResult leftLabel rightLabel- -> FaceId- -> Bool-faceCarriesExactArea result face =- case faceData (overlayResultTriangulation result) face of- OverlayCellFace {} -> True- OverlayCollapsedFace {} -> False--regionFaceLabels- :: OverlayResult leftLabel rightLabel- -> FaceId- -> Either RegionPublicationError (leftLabel, rightLabel)-regionFaceLabels result face =- case faceLabels result face of- Right labels -> Right labels- Left _ -> Left (RegionFaceLabelMissing face)--vertexSupport- :: (Ord leftLabel, Ord rightLabel)- => OverlayResult leftLabel rightLabel- -> VertexId- -> Either OverlayCellWitness (OverlayCellSupport leftLabel rightLabel)-vertexSupport result vertex = do- labels <-- traverse- (faceLabels result . incidentFace triangulation)- (vertexOutgoingEdges triangulation vertex)- case supportFromPairs labels of- Just support -> Right support- Nothing -> Left (OverlayVertexSupportMissing vertex)- where- triangulation = overlayResultTriangulation result--edgeSupport- :: (Ord leftLabel, Ord rightLabel)- => OverlayResult leftLabel rightLabel- -> UndirectedEdgeId- -> Either OverlayCellWitness (OverlayCellSupport leftLabel rightLabel)-edgeSupport result edge = do- pairs <- traverse (faceLabels result . incidentFace triangulation) [forward, backward]- case supportFromPairs pairs of- Just support -> Right support- Nothing -> Left (OverlayEdgeSupportMissing edge)- where- triangulation = overlayResultTriangulation result- (forward, backward) = directedPair edge--supportFromPairs- :: (Ord leftLabel, Ord rightLabel)- => [(leftLabel, rightLabel)]- -> Maybe (OverlayCellSupport leftLabel rightLabel)-supportFromPairs pairs = do- let (leftLabels, rightLabels) =- List.foldl'- (\(left, right) (leftLabel, rightLabel) ->- (Set.insert leftLabel left, Set.insert rightLabel right))- (Set.empty, Set.empty)- pairs- leftSupport <- nonEmptySupport leftLabels- rightSupport <- nonEmptySupport rightLabels- pure (OverlayCellSupport leftSupport rightSupport)--nonEmptySupport :: Set label -> Maybe (OverlaySupport label)-nonEmptySupport labels = OverlaySupport <$> NonEmpty.nonEmpty (Set.toAscList labels)--validateAtomicConstraints- :: Triangulation- 'Constrained- OverlayVertexSeed- ()- ()- ()- -> Map ExactEdgeKey (AtomicEdge leftLabel rightLabel)- -> Either (OverlayError leftLabel rightLabel) ()-validateAtomicConstraints triangulation atomicByKey = do- traverse_ requireAtomic (Map.keys atomicByKey)- traverse_ requireExpectedConstraint (undirectedEdges triangulation)- where- residentByKey =- Map.fromList- [ (residentSeedEdgeKey triangulation edge, edge)- | edge <- undirectedEdges triangulation- ]- requireAtomic key@(from, to) =- case Map.lookup key residentByKey of- Nothing -> Left (OverlayProvenanceIncomplete (OverlayAtomicConstraintMissing from to))- Just edge- | isConstraintEdge triangulation edge -> Right ()- | otherwise -> Left (OverlayProvenanceIncomplete (OverlayBoundaryEdgeNotConstrained edge))- requireExpectedConstraint edge- | isConstraintEdge triangulation edge- && Map.notMember (residentSeedEdgeKey triangulation edge) atomicByKey =- Left (OverlayProvenanceIncomplete (OverlayUnexpectedConstraint edge))- | otherwise = Right ()--residentSeedEdgeKey- :: Triangulation mode OverlayVertexSeed directed undirected face- -> UndirectedEdgeId- -> ExactEdgeKey-residentSeedEdgeKey = residentEdgeKeyBy seedExactPoint--residentEdgeKey- :: Triangulation mode OverlayVertex directed undirected face- -> UndirectedEdgeId- -> ExactEdgeKey-residentEdgeKey = residentEdgeKeyBy overlayExactPoint--residentEdgeKeyBy- :: (vertex -> ExactPoint)- -> Triangulation mode vertex directed undirected face- -> UndirectedEdgeId- -> ExactEdgeKey-residentEdgeKeyBy exactPointAt triangulation edge =- let (from, to) = undirectedEndpoints triangulation edge- in canonicalEdgeKey- (exactPointAt (vertexData triangulation from))- (exactPointAt (vertexData triangulation to))
− src-public/Moonlight/Triangulation/Internal/Overlay/Types.hs
@@ -1,274 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DerivingStrategies #-}-{-# LANGUAGE KindSignatures #-}---- | Closed vocabulary and opaque carrier representation for exact labelled--- overlay. Construction lives in the public build-tier module; this module--- exists so every invariant-bearing payload shares one owner.-module Moonlight.Triangulation.Internal.Overlay.Types- ( BoundaryLoopRef (..)- , OverlayOperand (..)- , BoundaryRef (..)- , BoundaryVertexRef- , BoundaryEdgeRef- , OverlayVertexOrigin (..)- , OverlayEdgeOrigin (..)- , OverlaySupport (..)- , overlaySupportLabels- , OverlayCellSupport (..)- , OverlayCellId (..)- , OverlayCellGeometry (..)- , OverlayCell (..)- , OverlayFace (..)- , OverlayVertex (..)- , OverlayEdge (..)- , OverlayReceipt (..)- , OverlayArrangementObstruction (..)- , OverlayCellWitness (..)- , OverlayError (..)- , OverlayResult (..)- , OverlaySelectionKind (..)- , OverlaySelectionError (..)- ) where--import Control.DeepSeq (NFData)-import Data.List.NonEmpty (NonEmpty)-import Data.Vector (Vector)-import GHC.Generics (Generic)-import Moonlight.Triangulation.CellSet (CellSelectionError)-import Moonlight.Triangulation.Exact- ( ExactPoint- )-import Moonlight.Triangulation.Internal.HandleDefs- ( FaceId- , UndirectedEdgeId- , VertexId- )-import Moonlight.Triangulation.Internal.Cdt.Types (CdtError)-import Moonlight.Triangulation.Internal.ExactRational (ExactArithmeticError)-import Moonlight.Triangulation.Internal.ExactSegmentEvents (ExactSegmentEventObstruction)-import Moonlight.Triangulation.Internal.Overlay.Embedding (OverlayEmbeddingObstruction)-import Moonlight.Triangulation.Internal.Region.Types- ( ExactLoop- , PolygonComponent- , RegionPublicationError- )-import Moonlight.Triangulation.Internal.Representation (Triangulation)-import Moonlight.Triangulation.Internal.Types- ( ConstraintMode (Constrained)- )---- | Which cycle inside a polygon component supplied a source reference.-data BoundaryLoopRef- = BoundaryOuterLoop- | BoundaryHoleLoop !Int- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)--data OverlayOperand- = LeftOverlayOperand- | RightOverlayOperand---- | Phantom source cell kind; both provenance axes share one physical payload.-data BoundaryFeature = BoundaryVertexFeature | BoundaryEdgeFeature--data BoundaryRef (feature :: BoundaryFeature) (operand :: OverlayOperand) = BoundaryRef- { boundaryRefComponent :: !Int- , boundaryRefLoop :: !BoundaryLoopRef- , boundaryRefLocalIndex :: !Int- }- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)--type BoundaryVertexRef operand = BoundaryRef 'BoundaryVertexFeature operand-type BoundaryEdgeRef operand = BoundaryRef 'BoundaryEdgeFeature operand---- | Complete typed source closure of one arrangement vertex.-data OverlayVertexOrigin = OverlayVertexOrigin- { overlayOriginLeftVertices :: ![BoundaryVertexRef 'LeftOverlayOperand]- , overlayOriginRightVertices :: ![BoundaryVertexRef 'RightOverlayOperand]- , overlayOriginLeftEdges :: ![BoundaryEdgeRef 'LeftOverlayOperand]- , overlayOriginRightEdges :: ![BoundaryEdgeRef 'RightOverlayOperand]- }- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)---- | Nonempty typed source-edge provenance of one exact atomic interval.-data OverlayEdgeOrigin = OverlayEdgeOrigin- { overlayEdgeLeftSources :: ![BoundaryEdgeRef 'LeftOverlayOperand]- , overlayEdgeRightSources :: ![BoundaryEdgeRef 'RightOverlayOperand]- }- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)---- | Sorted nonempty labels whose closures contain one relatively open cell.-newtype OverlaySupport label = OverlaySupport (NonEmpty label)- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)--overlaySupportLabels :: OverlaySupport label -> NonEmpty label-overlaySupportLabels (OverlaySupport labels) = labels--data OverlayCellSupport leftLabel rightLabel = OverlayCellSupport- { overlaySupportLeft :: !(OverlaySupport leftLabel)- , overlaySupportRight :: !(OverlaySupport rightLabel)- }- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)--newtype OverlayCellId = OverlayCellId Int- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)--data OverlayCellGeometry- = BoundedOverlayCell !PolygonComponent- | UnboundedOverlayCell ![ExactLoop]- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)--data OverlayCell leftLabel rightLabel = OverlayCell- { overlayCellLeft :: !leftLabel- , overlayCellRight :: !rightLabel- , overlayCellGeometry :: !OverlayCellGeometry- }- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)---- | A resident triangle either realizes an exact two-cell or collapses under--- the authoritative exact coordinates. A collapsed triangle descends to the--- unique adjacent exact cell through representation diagonals, but exact--- region operations must not mistake its binary64 area for exact area.-data OverlayFace- = OverlayCellFace- { overlayFaceCellId :: !OverlayCellId- }- | OverlayCollapsedFace- { overlayFaceCellId :: !OverlayCellId- }- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)---- | Exact geometry and provenance are intrinsic to a vertex. Label support is--- the finite union of incident cell descriptors and is therefore a view.-data OverlayVertex = OverlayVertex- { overlayExactPoint :: !ExactPoint- , overlayVertexOrigin :: !OverlayVertexOrigin- }- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)---- | Boundary provenance is intrinsic; label support is derived from the two--- incident face-cell references.-data OverlayEdge- = OverlayBoundary !OverlayEdgeOrigin- | OverlayDiagonal- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)--data OverlayReceipt = OverlayReceipt- { overlayInputSegments :: !Int- , overlayRelationEvents :: !Int- , overlayExactCrossings :: !Int- , overlayOverlapIntervals :: !Int- , overlayAtomicEdges :: !Int- , overlayOutputVertices :: !Int- , overlayArrangementCells :: !Int- , overlayResidentFaces :: !Int- , overlayEmbeddingCandidates :: !Int- , overlayTotalRelationChecks :: !Int- , overlaySweepMaximumHeight :: !Int- }- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)--data OverlayArrangementObstruction leftLabel rightLabel- = OverlayLeftSourceSideConflict- !OverlayEdgeOrigin- !(NonEmpty leftLabel)- | OverlayRightSourceSideConflict- !OverlayEdgeOrigin- !(NonEmpty rightLabel)- | OverlayRotationDegenerate !ExactPoint- | OverlayFaceComponentEmpty- | OverlayCellCycleDidNotClose !ExactPoint !ExactPoint- | OverlayTransitionSourceMismatch- !FaceId- !UndirectedEdgeId- !(leftLabel, rightLabel)- !(leftLabel, rightLabel)- | OverlayResidentFaceLabelConflict- !FaceId- !UndirectedEdgeId- !(leftLabel, rightLabel)- !(leftLabel, rightLabel)- | OverlayResidentFaceArity !FaceId !Int- | OverlayResidentFaceOrientationReversed !FaceId- | OverlayCollapsedFacesUnowned !(NonEmpty FaceId)- | OverlayCollapsedFacesAmbiguous- !(NonEmpty FaceId)- !(NonEmpty OverlayCellId)- | OverlayDuplicateCellSignature !PolygonComponent- deriving stock (Eq, Show, Generic)- deriving anyclass (NFData)--data OverlayCellWitness- = OverlayAtomicConstraintMissing !ExactPoint !ExactPoint- | OverlayAtomicConstraintOrientationMismatch- !UndirectedEdgeId- !ExactPoint- !ExactPoint- | OverlayUnexpectedConstraint !UndirectedEdgeId- | OverlayBoundaryEdgeNotConstrained !UndirectedEdgeId- | OverlayResidentFaceUnassigned !FaceId- | OverlayCellPayloadMissing !OverlayCellId- | OverlayVertexSupportMissing !VertexId- | OverlayEdgeSupportMissing !UndirectedEdgeId- | OverlayEmbeddedVertexCountMismatch !Int !Int- | OverlayExactVertexMissing !ExactPoint- deriving stock (Eq, Show, Generic)- deriving anyclass (NFData)--data OverlayError leftLabel rightLabel- = OverlayExactArithmetic !ExactArithmeticError- | OverlaySegmentEventsInvalid !ExactSegmentEventObstruction- | OverlayArrangementInvalid !(OverlayArrangementObstruction leftLabel rightLabel)- | OverlayEmbeddingRefused !(NonEmpty OverlayEmbeddingObstruction)- | OverlayBuildFailed !CdtError- | OverlayRegionPublicationFailed !RegionPublicationError- | OverlayProvenanceIncomplete !OverlayCellWitness- deriving stock (Eq, Show, Generic)- deriving anyclass (NFData)---- | The singular exact subdivision carrier. Its embedded DCEL is a derived--- binary64 realization; exact coordinates remain in the vertex payload plane.-data OverlayResult leftLabel rightLabel = OverlayResult- { overlayResultTriangulation- :: !( Triangulation- 'Constrained- OverlayVertex- ()- OverlayEdge- OverlayFace- )- , overlayResultCells :: !(Vector (OverlayCell leftLabel rightLabel))- , overlayResultOutsideLabels :: !(leftLabel, rightLabel)- , overlayResultReceipt :: !OverlayReceipt- }- deriving stock (Generic)- deriving anyclass (NFData)--data OverlaySelectionKind- = ClosedUnionSelection- | ClosedIntersectionSelection- | RegularizedDifferenceSelection- deriving stock (Eq, Ord, Show, Generic)- deriving anyclass (NFData)--data OverlaySelectionError- = OverlaySelectionContainsUnboundedCell !OverlaySelectionKind- | OverlaySelectionProvenance !OverlayCellWitness- | OverlaySelectionInvalid !CellSelectionError- deriving stock (Eq, Show, Generic)- deriving anyclass (NFData)
+ src-public/Moonlight/Triangulation/Internal/RegularTriangulation.hs view
@@ -0,0 +1,1111 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}++-- | Exact upper-hull descent for affine generators. The public power owner+-- supplies admitted, distinct slopes; this module returns only topology and+-- exact dual geometry.+module Moonlight.Triangulation.Internal.RegularTriangulation+ ( ExactPowerGenerator (..)+ , DistinctSlopeGenerators (..)+ , RegularGeneratorDisposition (..)+ , GeneratorRegularFace (..)+ , GeneratorDualGeometry (..)+ , GeneratorRegularEdge (..)+ , GeneratorRegularTopology (..)+ , GeneratorRegularReceipt (..)+ , RegularTopologyError (..)+ , regularGeneratorTopology+ , exactGeneratorAxis+ ) where++import Control.DeepSeq (NFData)+import Data.Bifunctor (first)+import qualified Data.Foldable as Foldable+import qualified Data.IntMap.Strict as IntMap+import qualified Data.IntSet as IntSet+import qualified Data.List as List+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NonEmpty+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import Data.Maybe (mapMaybe)+import Data.Set (Set)+import qualified Data.Set as Set+import GHC.Generics (Generic)+import Moonlight.Triangulation.Exact+ ( ExactAffineLine+ , ExactGeometryError+ , ExactHalfPlaneError+ , ExactIntersectionError+ , ExactPoint+ , ExactRay+ , ExactSegment+ , ExactVector (..)+ , exactAffineLine+ , exactAffineLineCoefficients+ , exactAffineLineIntersection+ , exactPoint+ , exactPointCoordinates+ , exactRay+ , exactSegment+ )+import Moonlight.Triangulation.Internal.ExactRational+ ( ExactRational )+import Moonlight.Triangulation.Internal.BoundaryCycle+ ( consecutivePairs )+import Moonlight.Triangulation.Internal.Minkowski.Convex+ ( convexHullPolygon )+import Moonlight.Triangulation.Minkowski+ ( MinkowskiError+ , convexPolygonPoints+ )++-- | Exact affine score @constant + xCoefficient*x + yCoefficient*y@.+data ExactPowerGenerator label = ExactPowerGenerator+ { exactPowerGeneratorLabel :: !label+ , exactPowerGeneratorXCoefficient :: !ExactRational+ , exactPowerGeneratorYCoefficient :: !ExactRational+ , exactPowerGeneratorConstant :: !ExactRational+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | Package-internal witness that coincident affine slopes were resolved once.+newtype DistinctSlopeGenerators label =+ DistinctSlopeGenerators (NonEmpty (ExactPowerGenerator label))++data RegularGeneratorDisposition+ = RegularGeneratorVisible+ | RegularGeneratorLowerDimensional+ | RegularGeneratorHidden+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++data GeneratorRegularFace label = GeneratorRegularFace+ { generatorRegularFaceFirst :: !label+ , generatorRegularFaceSecond :: !label+ , generatorRegularFaceThird :: !label+ , generatorRegularFaceDualPoint :: !ExactPoint+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++data GeneratorDualGeometry+ = GeneratorDualSegment !ExactSegment+ | GeneratorDualRay !ExactRay+ | GeneratorDualLine !ExactAffineLine+ | GeneratorDualCollapsed !ExactPoint+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++data GeneratorRegularEdge label = GeneratorRegularEdge+ { generatorRegularEdgeFirst :: !(ExactPowerGenerator label)+ , generatorRegularEdgeSecond :: !(ExactPowerGenerator label)+ , generatorRegularEdgeDual :: !GeneratorDualGeometry+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++data GeneratorRegularTopology label = GeneratorRegularTopology+ { generatorRegularDispositions :: !(NonEmpty (ExactPowerGenerator label, RegularGeneratorDisposition))+ , generatorRegularFaces :: ![GeneratorRegularFace label]+ , generatorRegularEdges :: ![GeneratorRegularEdge label]+ , generatorRegularReceipt :: !GeneratorRegularReceipt+ }+ deriving stock (Eq, Show, Generic)+ deriving anyclass (NFData)++data GeneratorRegularReceipt = GeneratorRegularReceipt+ { generatorRegularInputSites :: !Int+ , generatorRegularVisibleSites :: !Int+ , generatorRegularLowerDimensionalSites :: !Int+ , generatorRegularHiddenSites :: !Int+ , generatorRegularFaceCount :: !Int+ , generatorRegularEdgeCount :: !Int+ , generatorRegularPeakHullFaces :: !Int+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++data RegularTopologyError label+ = RegularTopologySlopeHullInvalid !MinkowskiError+ | RegularTopologySlopeVertexMissing !ExactPoint+ | RegularTopologyAxisInvalid !label !label !ExactHalfPlaneError+ | RegularTopologyDualVertexInvalid !label !label !label !ExactIntersectionError+ | RegularTopologyDualSegmentInvalid !label !label !ExactGeometryError+ | RegularTopologyDualRayInvalid !label !label !ExactGeometryError+ | RegularTopologyFlatBoundary !label !label !label+ | RegularTopologyNonManifoldEdge !label !label !Int+ deriving stock (Eq, Show, Generic)+ deriving anyclass (NFData)++-- | Construct exact regular topology from distinct affine slopes. Full-rank+-- input uses one conflict graph; collinear slopes use the one-dimensional+-- upper chain; coplanar lifted input reuses the exact planar hull owner.+regularGeneratorTopology+ :: Ord label+ => DistinctSlopeGenerators label+ -> Either (RegularTopologyError label) (GeneratorRegularTopology label)+regularGeneratorTopology (DistinctSlopeGenerators generators) =+ let points = admittedLiftedPoints generators+ in case points of+ singleton :| [] ->+ pure (topologyFromParts points (Set.singleton singleton) Set.empty [] [] 0)+ firstPoint :| secondPoint : remaining ->+ case List.find (not . slopeCollinear firstPoint secondPoint) remaining of+ Nothing -> collinearSlopeTopology points firstPoint secondPoint+ Just thirdPoint ->+ case List.find (not . liftedCoplanar firstPoint secondPoint thirdPoint) remaining of+ Nothing -> coplanarLiftedTopology points+ Just fourthPoint -> fullRankTopology points firstPoint secondPoint thirdPoint fourthPoint++-- | The equality line of two distinct-slope affine generators.+exactGeneratorAxis+ :: ExactPowerGenerator label+ -> ExactPowerGenerator label+ -> Either ExactHalfPlaneError ExactAffineLine+exactGeneratorAxis firstGenerator secondGenerator =+ exactAffineLine+ ( exactPowerGeneratorXCoefficient firstGenerator+ - exactPowerGeneratorXCoefficient secondGenerator+ )+ ( exactPowerGeneratorYCoefficient firstGenerator+ - exactPowerGeneratorYCoefficient secondGenerator+ )+ ( exactPowerGeneratorConstant firstGenerator+ - exactPowerGeneratorConstant secondGenerator+ )++data LiftedPoint label = LiftedPoint+ { liftedPointId :: !Int+ , liftedPointGenerator :: !(ExactPowerGenerator label)+ }++instance Eq (LiftedPoint label) where+ left == right = liftedPointId left == liftedPointId right++instance Ord (LiftedPoint label) where+ compare left right = compare (liftedPointId left) (liftedPointId right)++admittedLiftedPoints+ :: Ord label+ => NonEmpty (ExactPowerGenerator label)+ -> NonEmpty (LiftedPoint label)+admittedLiftedPoints generators =+ let ordered = NonEmpty.sortWith exactPowerGeneratorLabel generators+ in NonEmpty.zipWith+ (\identifier generator -> LiftedPoint identifier generator)+ (0 :| [1 ..])+ ordered++liftedCoordinates+ :: LiftedPoint label+ -> (ExactRational, ExactRational, ExactRational)+liftedCoordinates point =+ let generator = liftedPointGenerator point+ in ( exactPowerGeneratorXCoefficient generator+ , exactPowerGeneratorYCoefficient generator+ , exactPowerGeneratorConstant generator+ )++slopePoint :: LiftedPoint label -> ExactPoint+slopePoint point =+ let (coordinateX, coordinateY, _) = liftedCoordinates point+ in exactPoint coordinateX coordinateY++slopeCollinear+ :: LiftedPoint label+ -> LiftedPoint label+ -> LiftedPoint label+ -> Bool+slopeCollinear firstPoint secondPoint thirdPoint =+ slopeOrientation firstPoint secondPoint thirdPoint == 0++slopeOrientation+ :: LiftedPoint label+ -> LiftedPoint label+ -> LiftedPoint label+ -> ExactRational+slopeOrientation firstPoint secondPoint thirdPoint =+ let (firstX, firstY, _) = liftedCoordinates firstPoint+ (secondX, secondY, _) = liftedCoordinates secondPoint+ (thirdX, thirdY, _) = liftedCoordinates thirdPoint+ in (secondX - firstX) * (thirdY - firstY)+ - (secondY - firstY) * (thirdX - firstX)++liftedCoplanar+ :: LiftedPoint label+ -> LiftedPoint label+ -> LiftedPoint label+ -> LiftedPoint label+ -> Bool+liftedCoplanar firstPoint secondPoint thirdPoint fourthPoint =+ liftedVolume firstPoint secondPoint thirdPoint fourthPoint == 0++liftedVolume+ :: LiftedPoint label+ -> LiftedPoint label+ -> LiftedPoint label+ -> LiftedPoint label+ -> ExactRational+liftedVolume firstPoint secondPoint thirdPoint fourthPoint =+ let (firstX, firstY, firstZ) = liftedCoordinates firstPoint+ (secondX, secondY, secondZ) = liftedCoordinates secondPoint+ (thirdX, thirdY, thirdZ) = liftedCoordinates thirdPoint+ (fourthX, fourthY, fourthZ) = liftedCoordinates fourthPoint+ secondX' = secondX - firstX+ secondY' = secondY - firstY+ secondZ' = secondZ - firstZ+ thirdX' = thirdX - firstX+ thirdY' = thirdY - firstY+ thirdZ' = thirdZ - firstZ+ fourthX' = fourthX - firstX+ fourthY' = fourthY - firstY+ fourthZ' = fourthZ - firstZ+ in secondX' * (thirdY' * fourthZ' - thirdZ' * fourthY')+ - secondY' * (thirdX' * fourthZ' - thirdZ' * fourthX')+ + secondZ' * (thirdX' * fourthY' - thirdY' * fourthX')++data HullFace label = HullFace+ { hullFaceId :: !Int+ , hullFaceFirst :: !(LiftedPoint label)+ , hullFaceSecond :: !(LiftedPoint label)+ , hullFaceThird :: !(LiftedPoint label)+ , hullFaceOutside :: !(Set (LiftedPoint label))+ , hullFaceCoplanar :: !(Set (LiftedPoint label))+ }++type HullEdge = (Int, Int)++data HullState label = HullState+ { hullFaces :: !(IntMap.IntMap (HullFace label))+ , hullEdgeFaces :: !(Map HullEdge IntSet.IntSet)+ , hullConflictFaces :: !IntSet.IntSet+ , hullNextFaceId :: !Int+ , hullPeakFaceCount :: !Int+ }++fullRankTopology+ :: NonEmpty (LiftedPoint label)+ -> LiftedPoint label+ -> LiftedPoint label+ -> LiftedPoint label+ -> LiftedPoint label+ -> Either (RegularTopologyError label) (GeneratorRegularTopology label)+fullRankTopology points firstPoint secondPoint thirdPoint fourthPoint = do+ let interior = liftedInteriorSum firstPoint secondPoint thirdPoint fourthPoint+ simplex = Set.fromList [firstPoint, secondPoint, thirdPoint, fourthPoint]+ initialFaces =+ zipWith+ (orientedHullFace interior)+ [0 ..]+ [ (firstPoint, secondPoint, thirdPoint)+ , (firstPoint, fourthPoint, secondPoint)+ , (firstPoint, thirdPoint, fourthPoint)+ , (secondPoint, fourthPoint, thirdPoint)+ ]+ initialState =+ List.foldl'+ insertHullFace+ (HullState IntMap.empty Map.empty IntSet.empty 4 4)+ initialFaces+ seededState =+ Foldable.foldl'+ (flip assignPointToHull)+ initialState+ (filter (`Set.notMember` simplex) (NonEmpty.toList points))+ finalState = descendHull interior seededState+ upperFaces = filter ((> 0) . hullFaceNormalZ) (IntMap.elems (hullFaces finalState))+ (canonicalFaces, visible, lowerDimensional) <- canonicalUpperFaces upperFaces+ (faces, edges) <- topologyFromHullFaces canonicalFaces+ pure+ ( topologyFromParts+ points+ visible+ lowerDimensional+ faces+ edges+ (hullPeakFaceCount finalState)+ )++liftedInteriorSum+ :: LiftedPoint label+ -> LiftedPoint label+ -> LiftedPoint label+ -> LiftedPoint label+ -> (ExactRational, ExactRational, ExactRational)+liftedInteriorSum firstPoint secondPoint thirdPoint fourthPoint =+ let (firstX, firstY, firstZ) = liftedCoordinates firstPoint+ (secondX, secondY, secondZ) = liftedCoordinates secondPoint+ (thirdX, thirdY, thirdZ) = liftedCoordinates thirdPoint+ (fourthX, fourthY, fourthZ) = liftedCoordinates fourthPoint+ in ( firstX + secondX + thirdX + fourthX+ , firstY + secondY + thirdY + fourthY+ , firstZ + secondZ + thirdZ + fourthZ+ )++orientedHullFace+ :: (ExactRational, ExactRational, ExactRational)+ -> Int+ -> (LiftedPoint label, LiftedPoint label, LiftedPoint label)+ -> HullFace label+orientedHullFace interior identifier (firstPoint, secondPoint, thirdPoint) =+ let provisional = HullFace identifier firstPoint secondPoint thirdPoint Set.empty Set.empty+ in if hullFaceVolumeAtInterior interior provisional < 0+ then provisional+ else HullFace identifier firstPoint thirdPoint secondPoint Set.empty Set.empty++hullFaceVolumeAtInterior+ :: (ExactRational, ExactRational, ExactRational)+ -> HullFace label+ -> ExactRational+hullFaceVolumeAtInterior (sumX, sumY, sumZ) face =+ let (firstX, firstY, firstZ) = liftedCoordinates (hullFaceFirst face)+ (secondX, secondY, secondZ) = liftedCoordinates (hullFaceSecond face)+ (thirdX, thirdY, thirdZ) = liftedCoordinates (hullFaceThird face)+ secondX' = secondX - firstX+ secondY' = secondY - firstY+ secondZ' = secondZ - firstZ+ thirdX' = thirdX - firstX+ thirdY' = thirdY - firstY+ thirdZ' = thirdZ - firstZ+ interiorX' = sumX - 4 * firstX+ interiorY' = sumY - 4 * firstY+ interiorZ' = sumZ - 4 * firstZ+ in secondX' * (thirdY' * interiorZ' - thirdZ' * interiorY')+ - secondY' * (thirdX' * interiorZ' - thirdZ' * interiorX')+ + secondZ' * (thirdX' * interiorY' - thirdY' * interiorX')++liftedVolumeAtFace :: HullFace label -> LiftedPoint label -> ExactRational+liftedVolumeAtFace face =+ liftedVolume+ (hullFaceFirst face)+ (hullFaceSecond face)+ (hullFaceThird face)++hullFaceNormalZ :: HullFace label -> ExactRational+hullFaceNormalZ face =+ slopeOrientation+ (hullFaceFirst face)+ (hullFaceSecond face)+ (hullFaceThird face)++hullFacePoints :: HullFace label -> [LiftedPoint label]+hullFacePoints face =+ [hullFaceFirst face, hullFaceSecond face, hullFaceThird face]++hullFaceDirectedEdges+ :: HullFace label+ -> [(LiftedPoint label, LiftedPoint label)]+hullFaceDirectedEdges face =+ [ (hullFaceFirst face, hullFaceSecond face)+ , (hullFaceSecond face, hullFaceThird face)+ , (hullFaceThird face, hullFaceFirst face)+ ]++hullEdgeKey :: LiftedPoint label -> LiftedPoint label -> HullEdge+hullEdgeKey firstPoint secondPoint =+ let firstId = liftedPointId firstPoint+ secondId = liftedPointId secondPoint+ in if firstId <= secondId then (firstId, secondId) else (secondId, firstId)++insertHullFace :: HullState label -> HullFace label -> HullState label+insertHullFace state face =+ state+ { hullFaces = IntMap.insert (hullFaceId face) face (hullFaces state)+ , hullEdgeFaces =+ List.foldl'+ (\incidence (fromPoint, toPoint) ->+ Map.insertWith+ IntSet.union+ (hullEdgeKey fromPoint toPoint)+ (IntSet.singleton (hullFaceId face))+ incidence)+ (hullEdgeFaces state)+ (hullFaceDirectedEdges face)+ , hullPeakFaceCount = max (hullPeakFaceCount state) (IntMap.size (hullFaces state) + 1)+ }++assignPointToHull :: LiftedPoint label -> HullState label -> HullState label+assignPointToHull point state =+ attachSelectedPoint point state+ (selectHullFace point (IntMap.elems (hullFaces state)))++data HullFaceSelection label+ = HullFaceInterior+ | HullFaceCoplanar !(HullFace label)+ | HullFaceVisible !(HullFace label) !ExactRational++selectHullFace+ :: LiftedPoint label+ -> [HullFace label]+ -> HullFaceSelection label+selectHullFace point = List.foldl' choose HullFaceInterior+ where+ choose selected face =+ let volume = liftedVolumeAtFace face point+ in case compare volume 0 of+ LT -> selected+ EQ ->+ case selected of+ HullFaceInterior -> HullFaceCoplanar face+ _ -> selected+ GT ->+ case selected of+ HullFaceVisible current currentVolume+ | volume < currentVolume+ || (volume == currentVolume && hullFaceId current < hullFaceId face) ->+ selected+ _ -> HullFaceVisible face volume++attachSelectedPoint+ :: LiftedPoint label+ -> HullState label+ -> HullFaceSelection label+ -> HullState label+attachSelectedPoint point state selection =+ case selection of+ HullFaceInterior -> state+ HullFaceCoplanar face ->+ state+ { hullFaces =+ IntMap.adjust+ (\selected -> selected{hullFaceCoplanar = Set.insert point (hullFaceCoplanar selected)})+ (hullFaceId face)+ (hullFaces state)+ }+ HullFaceVisible face _ ->+ state+ { hullFaces =+ IntMap.adjust+ (\selected -> selected{hullFaceOutside = Set.insert point (hullFaceOutside selected)})+ (hullFaceId face)+ (hullFaces state)+ , hullConflictFaces = IntSet.insert (hullFaceId face) (hullConflictFaces state)+ }++descendHull+ :: (ExactRational, ExactRational, ExactRational)+ -> HullState label+ -> HullState label+descendHull interior state =+ case nextHullExpansion state of+ Nothing -> state+ Just (seedFace, apex) ->+ let visible = visibleHullPatch state apex (IntSet.singleton (hullFaceId seedFace)) [hullFaceId seedFace]+ visibleFaces =+ mapMaybe (`IntMap.lookup` hullFaces state) (IntSet.toAscList visible)+ horizon = hullHorizon state visible visibleFaces+ candidates =+ Set.difference+ ( Set.delete apex+ ( Set.unions+ [ Set.unions+ [ hullFaceOutside face+ , hullFaceCoplanar face+ , Set.fromList (hullFacePoints face)+ ]+ | face <- visibleFaces+ ]+ )+ )+ ( Set.fromList+ [ point+ | (firstPoint, secondPoint) <- Map.elems horizon+ , point <- [firstPoint, secondPoint]+ ]+ )+ withoutVisible = removeHullFaces visibleFaces state+ (withHorizon, newFaceIds) =+ Map.foldl'+ (insertHorizonFace interior apex)+ (withoutVisible, [])+ horizon+ redistributed =+ Foldable.foldl'+ (assignPointToFaces newFaceIds)+ withHorizon+ candidates+ in descendHull interior redistributed++nextHullExpansion+ :: HullState label+ -> Maybe (HullFace label, LiftedPoint label)+nextHullExpansion state = do+ identifier <- IntSet.lookupMin (hullConflictFaces state)+ face <- IntMap.lookup identifier (hullFaces state)+ apex <- farthestOutsidePoint face+ pure (face, apex)++farthestOutsidePoint :: HullFace label -> Maybe (LiftedPoint label)+farthestOutsidePoint face =+ case Set.minView (hullFaceOutside face) of+ Nothing -> Nothing+ Just (initial, remaining) ->+ Just+ ( fst+ ( Foldable.foldl'+ (\(selected, selectedVolume) candidate ->+ let candidateVolume = liftedVolumeAtFace face candidate+ in if candidateVolume > selectedVolume+ then (candidate, candidateVolume)+ else (selected, selectedVolume))+ (initial, liftedVolumeAtFace face initial)+ remaining+ )+ )++visibleHullPatch+ :: HullState label+ -> LiftedPoint label+ -> IntSet.IntSet+ -> [Int]+ -> IntSet.IntSet+visibleHullPatch _ _ visited [] = visited+visibleHullPatch state apex visited (identifier : remaining) =+ case IntMap.lookup identifier (hullFaces state) of+ Nothing -> visibleHullPatch state apex visited remaining+ Just face ->+ let neighbours =+ IntSet.unions+ [ Map.findWithDefault IntSet.empty (hullEdgeKey fromPoint toPoint) (hullEdgeFaces state)+ | (fromPoint, toPoint) <- hullFaceDirectedEdges face+ ]+ newlyVisible =+ IntSet.filter+ (\candidateId ->+ IntSet.notMember candidateId visited+ && maybe+ False+ ((> 0) . (`liftedVolumeAtFace` apex))+ (IntMap.lookup candidateId (hullFaces state)))+ neighbours+ in visibleHullPatch+ state+ apex+ (IntSet.union visited newlyVisible)+ (IntSet.toList newlyVisible <> remaining)++hullHorizon+ :: HullState label+ -> IntSet.IntSet+ -> [HullFace label]+ -> Map HullEdge (LiftedPoint label, LiftedPoint label)+hullHorizon state visible visibleFaces =+ Map.fromList+ [ (hullEdgeKey fromPoint toPoint, (fromPoint, toPoint))+ | face <- visibleFaces+ , (fromPoint, toPoint) <- hullFaceDirectedEdges face+ , not+ ( IntSet.null+ ( IntSet.difference+ (Map.findWithDefault IntSet.empty (hullEdgeKey fromPoint toPoint) (hullEdgeFaces state))+ visible+ )+ )+ ]++removeHullFaces :: [HullFace label] -> HullState label -> HullState label+removeHullFaces removed state = List.foldl' removeHullFace state removed++removeHullFace :: HullState label -> HullFace label -> HullState label+removeHullFace state face =+ state+ { hullFaces = IntMap.delete identifier (hullFaces state)+ , hullEdgeFaces =+ List.foldl'+ (removeHullFaceFromEdge identifier)+ (hullEdgeFaces state)+ (hullFaceDirectedEdges face)+ , hullConflictFaces = IntSet.delete identifier (hullConflictFaces state)+ }+ where+ identifier = hullFaceId face++removeHullFaceFromEdge+ :: Int+ -> Map HullEdge IntSet.IntSet+ -> (LiftedPoint label, LiftedPoint label)+ -> Map HullEdge IntSet.IntSet+removeHullFaceFromEdge identifier incidence (fromPoint, toPoint) =+ Map.update+ (\incident ->+ let retained = IntSet.delete identifier incident+ in if IntSet.null retained then Nothing else Just retained)+ (hullEdgeKey fromPoint toPoint)+ incidence++insertHorizonFace+ :: (ExactRational, ExactRational, ExactRational)+ -> LiftedPoint label+ -> (HullState label, [Int])+ -> (LiftedPoint label, LiftedPoint label)+ -> (HullState label, [Int])+insertHorizonFace interior apex (state, identifiers) (firstPoint, secondPoint) =+ let identifier = hullNextFaceId state+ face = orientedHullFace interior identifier (firstPoint, secondPoint, apex)+ nextState =+ (insertHullFace state face)+ { hullNextFaceId = identifier + 1 }+ in (nextState, identifier : identifiers)++assignPointToFaces+ :: [Int]+ -> HullState label+ -> LiftedPoint label+ -> HullState label+assignPointToFaces identifiers state point =+ attachSelectedPoint point state+ (selectHullFace point (mapMaybe (`IntMap.lookup` hullFaces state) identifiers))++-- | Glue the triangular implementation facets on each exact supporting plane,+-- then recover only that plane's extreme slope vertices. This removes+-- simplex/horizon diagonals from the visibility decision while retaining a+-- deterministic triangulation for incidence and collapsed duals.+canonicalUpperFaces+ :: [HullFace label]+ -> Either+ (RegularTopologyError label)+ ( [(HullFace label, ExactPoint)]+ , Set (LiftedPoint label)+ , Set (LiftedPoint label)+ )+canonicalUpperFaces faces = do+ associations <- traverse upperFaceAssociation faces+ sections <-+ traverse canonicalUpperFaceGroup+ ( Map.elems+ ( List.foldl'+ (\groups (planeKey, dualFace) ->+ Map.insertWith (<>) planeKey (dualFace :| []) groups)+ Map.empty+ associations+ )+ )+ let (sectionFaces, visible, lowerDimensional) = mconcat sections+ canonicalFaces =+ zipWith+ (\identifier (face, dualPoint) ->+ (face{hullFaceId = identifier}, dualPoint))+ [0 ..]+ sectionFaces+ pure (canonicalFaces, visible, lowerDimensional)++canonicalUpperFaceGroup+ :: NonEmpty (HullFace label, ExactPoint)+ -> Either+ (RegularTopologyError label)+ ( [(HullFace label, ExactPoint)]+ , Set (LiftedPoint label)+ , Set (LiftedPoint label)+ )+canonicalUpperFaceGroup ((face, dualPoint) :| [])+ | Set.null (hullFaceCoplanar face) =+ Right ([(face, dualPoint)], Set.fromList (hullFacePoints face), Set.empty)+canonicalUpperFaceGroup ((firstFace, dualPoint) :| remainingDualFaces) = do+ let groupFaces = firstFace : fmap fst remainingDualFaces+ groupPoints =+ Set.unions+ [ Set.union+ (Set.fromList (hullFacePoints face))+ (hullFaceCoplanar face)+ | face <- groupFaces+ ]+ initialPoint = hullFaceFirst firstFace+ points = initialPoint :| Set.toAscList (Set.delete initialPoint groupPoints)+ pointsBySlope =+ Map.fromList [(slopePoint point, point) | point <- NonEmpty.toList points]+ hull <-+ first RegularTopologySlopeHullInvalid+ (convexHullPolygon (fmap slopePoint points))+ visiblePoints <-+ traverse+ (\point ->+ maybe+ (Left (RegularTopologySlopeVertexMissing point))+ Right+ (Map.lookup point pointsBySlope))+ (NonEmpty.toList (convexPolygonPoints hull))+ let visible = Set.fromList visiblePoints+ pure+ ( fmap (\fanFace -> (fanFace, dualPoint)) (coplanarFanFaces visiblePoints)+ , visible+ , Set.difference groupPoints visible+ )++hullFacePlaneKey+ :: HullFace label+ -> ExactPoint+ -> (ExactRational, ExactRational, ExactRational)+hullFacePlaneKey face dualPoint =+ let (dualX, dualY) = exactPointCoordinates dualPoint+ generator = liftedPointGenerator (hullFaceFirst face)+ support =+ exactPowerGeneratorXCoefficient generator * dualX+ + exactPowerGeneratorYCoefficient generator * dualY+ + exactPowerGeneratorConstant generator+ in (dualX, dualY, support)++upperFaceAssociation+ :: HullFace label+ -> Either+ (RegularTopologyError label)+ ( (ExactRational, ExactRational, ExactRational)+ , (HullFace label, ExactPoint)+ )+upperFaceAssociation face = do+ (_, dualPoint) <- hullFaceDual face+ pure (hullFacePlaneKey face dualPoint, (face, dualPoint))++coplanarLiftedTopology+ :: NonEmpty (LiftedPoint label)+ -> Either (RegularTopologyError label) (GeneratorRegularTopology label)+coplanarLiftedTopology points = do+ hull <-+ first RegularTopologySlopeHullInvalid+ (convexHullPolygon (fmap slopePoint points))+ let pointsBySlope = Map.fromList [(slopePoint point, point) | point <- NonEmpty.toList points]+ hullPoints <-+ traverse+ (\point -> maybe (Left (RegularTopologySlopeVertexMissing point)) Right (Map.lookup point pointsBySlope))+ (NonEmpty.toList (convexPolygonPoints hull))+ let visible = Set.fromList hullPoints+ lowerDimensional = Set.difference (Set.fromList (NonEmpty.toList points)) visible+ hullFaces' = coplanarFanFaces hullPoints+ dualFaces <- coplanarFanDualFaces hullFaces'+ (faces, edges) <- topologyFromHullFaces dualFaces+ pure+ ( topologyFromParts+ points+ visible+ lowerDimensional+ faces+ edges+ (length hullFaces')+ )++coplanarFanDualFaces+ :: [HullFace label]+ -> Either+ (RegularTopologyError label)+ [(HullFace label, ExactPoint)]+coplanarFanDualFaces [] = Right []+coplanarFanDualFaces faces@(firstFace : _) = do+ (_, dualPoint) <- hullFaceDual firstFace+ pure (fmap (\face -> (face, dualPoint)) faces)++coplanarFanFaces :: [LiftedPoint label] -> [HullFace label]+coplanarFanFaces (firstPoint : secondPoint : thirdPoint : remaining) =+ zipWith+ (\identifier (leftPoint, rightPoint) ->+ HullFace identifier firstPoint leftPoint rightPoint Set.empty Set.empty)+ [0 ..]+ (consecutivePairs (secondPoint : thirdPoint : remaining))+coplanarFanFaces _ = []++collinearSlopeTopology+ :: NonEmpty (LiftedPoint label)+ -> LiftedPoint label+ -> LiftedPoint label+ -> Either (RegularTopologyError label) (GeneratorRegularTopology label)+collinearSlopeTopology points firstPoint secondPoint = do+ let direction = slopeDifference firstPoint secondPoint+ ordered = List.sortOn (slopeParameter direction) (NonEmpty.toList points)+ visibleList = upperSlopeChain direction ordered+ visible = Set.fromList visibleList+ lowerDimensional =+ Set.difference+ (upperChainPointSet direction visibleList ordered)+ visible+ edges <- traverse fullLineEdge (consecutivePairs visibleList)+ pure (topologyFromParts points visible lowerDimensional [] edges 0)++slopeDifference+ :: LiftedPoint label+ -> LiftedPoint label+ -> (ExactRational, ExactRational)+slopeDifference firstPoint secondPoint =+ let (firstX, firstY, _) = liftedCoordinates firstPoint+ (secondX, secondY, _) = liftedCoordinates secondPoint+ in (secondX - firstX, secondY - firstY)++slopeParameter+ :: (ExactRational, ExactRational)+ -> LiftedPoint label+ -> ExactRational+slopeParameter (directionX, directionY) point =+ let (coordinateX, coordinateY, _) = liftedCoordinates point+ in directionX * coordinateX + directionY * coordinateY++upperSlopeChain+ :: (ExactRational, ExactRational)+ -> [LiftedPoint label]+ -> [LiftedPoint label]+upperSlopeChain direction = reverse . List.foldl' insertUpper []+ where+ insertUpper+ :: [LiftedPoint label]+ -> LiftedPoint label+ -> [LiftedPoint label]+ insertUpper (secondPoint : firstPoint : remaining) candidate+ | slopeChainTurn direction firstPoint secondPoint candidate >= 0 =+ insertUpper (firstPoint : remaining) candidate+ insertUpper retained candidate = candidate : retained++slopeChainTurn+ :: (ExactRational, ExactRational)+ -> LiftedPoint label+ -> LiftedPoint label+ -> LiftedPoint label+ -> ExactRational+slopeChainTurn direction firstPoint secondPoint thirdPoint =+ let firstT = slopeParameter direction firstPoint+ secondT = slopeParameter direction secondPoint+ thirdT = slopeParameter direction thirdPoint+ (_, _, firstConstant) = liftedCoordinates firstPoint+ (_, _, secondConstant) = liftedCoordinates secondPoint+ (_, _, thirdConstant) = liftedCoordinates thirdPoint+ in (secondT - firstT) * (thirdConstant - firstConstant)+ - (secondConstant - firstConstant) * (thirdT - firstT)++upperChainPointSet+ :: (ExactRational, ExactRational)+ -> [LiftedPoint label]+ -> [LiftedPoint label]+ -> Set (LiftedPoint label)+upperChainPointSet direction chain =+ snd+ . List.foldl'+ classifyPoint+ (consecutivePairs chain, Set.empty)+ where+ classifyPoint+ :: ([(LiftedPoint label, LiftedPoint label)], Set (LiftedPoint label))+ -> LiftedPoint label+ -> ([(LiftedPoint label, LiftedPoint label)], Set (LiftedPoint label))+ classifyPoint (remainingEdges, members) point =+ let pointParameter = slopeParameter direction point+ candidateEdges =+ List.dropWhile+ ((< pointParameter) . slopeParameter direction . snd)+ remainingEdges+ retainedMembers =+ case candidateEdges of+ (firstPoint, secondPoint) : _+ | slopeParameter direction firstPoint <= pointParameter+ && slopeChainTurn direction firstPoint secondPoint point == 0 ->+ Set.insert point members+ _ -> members+ in (candidateEdges, retainedMembers)++fullLineEdge+ :: (LiftedPoint label, LiftedPoint label)+ -> Either (RegularTopologyError label) (GeneratorRegularEdge label)+fullLineEdge (firstPoint, secondPoint) = do+ axis <- generatorAxis firstPoint secondPoint+ pure (regularEdge firstPoint secondPoint (GeneratorDualLine axis))++topologyFromHullFaces+ :: [(HullFace label, ExactPoint)]+ -> Either+ (RegularTopologyError label)+ ([GeneratorRegularFace label], [GeneratorRegularEdge label])+topologyFromHullFaces dualFaces = do+ let dualByFace = IntMap.fromList [(hullFaceId face, dual) | (face, dual) <- dualFaces]+ edgeIncidence =+ List.foldl'+ (\incidence (face, _) ->+ List.foldl'+ (\current (firstPoint, secondPoint, thirdPoint) ->+ Map.insertWith+ (<>)+ (hullEdgeKey firstPoint secondPoint)+ ((face, firstPoint, secondPoint, thirdPoint) :| [])+ current)+ incidence+ (faceEdgesWithOpposite face))+ Map.empty+ dualFaces+ edges <- traverse (dualEdge dualByFace) (Map.elems edgeIncidence)+ pure+ ( [ GeneratorRegularFace+ (pointLabel (hullFaceFirst face))+ (pointLabel (hullFaceSecond face))+ (pointLabel (hullFaceThird face))+ dual+ | (face, dual) <- dualFaces+ ]+ , edges+ )++hullFaceDual+ :: HullFace label+ -> Either (RegularTopologyError label) (HullFace label, ExactPoint)+hullFaceDual face = do+ let firstPoint = hullFaceFirst face+ secondPoint = hullFaceSecond face+ thirdPoint = hullFaceThird face+ firstAxis <- generatorAxis firstPoint secondPoint+ secondAxis <- generatorAxis firstPoint thirdPoint+ dual <-+ first+ ( RegularTopologyDualVertexInvalid+ (pointLabel firstPoint)+ (pointLabel secondPoint)+ (pointLabel thirdPoint)+ )+ (exactAffineLineIntersection firstAxis secondAxis)+ pure (face, dual)++faceEdgesWithOpposite+ :: HullFace label+ -> [(LiftedPoint label, LiftedPoint label, LiftedPoint label)]+faceEdgesWithOpposite face =+ [ (hullFaceFirst face, hullFaceSecond face, hullFaceThird face)+ , (hullFaceSecond face, hullFaceThird face, hullFaceFirst face)+ , (hullFaceThird face, hullFaceFirst face, hullFaceSecond face)+ ]++dualEdge+ :: IntMap.IntMap ExactPoint+ -> NonEmpty (HullFace label, LiftedPoint label, LiftedPoint label, LiftedPoint label)+ -> Either (RegularTopologyError label) (GeneratorRegularEdge label)+dualEdge dualByFace incidences =+ case incidences of+ (face, firstPoint, secondPoint, thirdPoint) :| [] -> do+ dual <-+ maybe+ (Left (RegularTopologyFlatBoundary (pointLabel firstPoint) (pointLabel secondPoint) (pointLabel thirdPoint)))+ Right+ (IntMap.lookup (hullFaceId face) dualByFace)+ ray <- boundaryDualRay dual firstPoint secondPoint thirdPoint+ pure (regularEdge firstPoint secondPoint (GeneratorDualRay ray))+ (firstFace, firstPoint, secondPoint, _) :| [(secondFace, _, _, _)] -> do+ firstDual <- requiredDual firstPoint secondPoint firstFace+ secondDual <- requiredDual firstPoint secondPoint secondFace+ if firstDual == secondDual+ then pure (regularEdge firstPoint secondPoint (GeneratorDualCollapsed firstDual))+ else do+ segment <-+ first+ (RegularTopologyDualSegmentInvalid (pointLabel firstPoint) (pointLabel secondPoint))+ (exactSegment firstDual secondDual)+ pure (regularEdge firstPoint secondPoint (GeneratorDualSegment segment))+ (_, firstPoint, secondPoint, _) :| _ ->+ Left+ ( RegularTopologyNonManifoldEdge+ (pointLabel firstPoint)+ (pointLabel secondPoint)+ (NonEmpty.length incidences)+ )+ where+ requiredDual+ :: LiftedPoint label+ -> LiftedPoint label+ -> HullFace label+ -> Either (RegularTopologyError label) ExactPoint+ requiredDual firstPoint secondPoint face =+ maybe+ (Left (RegularTopologyFlatBoundary (pointLabel firstPoint) (pointLabel secondPoint) (pointLabel (hullFaceThird face))))+ Right+ (IntMap.lookup (hullFaceId face) dualByFace)++boundaryDualRay+ :: ExactPoint+ -> LiftedPoint label+ -> LiftedPoint label+ -> LiftedPoint label+ -> Either (RegularTopologyError label) ExactRay+boundaryDualRay originPoint firstPoint secondPoint thirdPoint = do+ axis <- generatorAxis firstPoint secondPoint+ let (axisX, axisY, _) = exactAffineLineCoefficients axis+ candidate = ExactVector axisY (negate axisX)+ firstGenerator = liftedPointGenerator firstPoint+ thirdGenerator = liftedPointGenerator thirdPoint+ advantageX =+ exactPowerGeneratorXCoefficient firstGenerator+ - exactPowerGeneratorXCoefficient thirdGenerator+ advantageY =+ exactPowerGeneratorYCoefficient firstGenerator+ - exactPowerGeneratorYCoefficient thirdGenerator+ ExactVector candidateX candidateY = candidate+ derivative = advantageX * candidateX + advantageY * candidateY+ direction =+ if derivative > 0+ then candidate+ else ExactVector (negate candidateX) (negate candidateY)+ if derivative == 0+ then+ Left+ ( RegularTopologyFlatBoundary+ (pointLabel firstPoint)+ (pointLabel secondPoint)+ (pointLabel thirdPoint)+ )+ else+ first+ (RegularTopologyDualRayInvalid (pointLabel firstPoint) (pointLabel secondPoint))+ (exactRay originPoint direction)++generatorAxis+ :: LiftedPoint label+ -> LiftedPoint label+ -> Either (RegularTopologyError label) ExactAffineLine+generatorAxis firstPoint secondPoint =+ first+ (RegularTopologyAxisInvalid (pointLabel firstPoint) (pointLabel secondPoint))+ (exactGeneratorAxis (liftedPointGenerator firstPoint) (liftedPointGenerator secondPoint))++regularEdge+ :: LiftedPoint label+ -> LiftedPoint label+ -> GeneratorDualGeometry+ -> GeneratorRegularEdge label+regularEdge firstPoint secondPoint dual =+ if liftedPointId firstPoint <= liftedPointId secondPoint+ then GeneratorRegularEdge (liftedPointGenerator firstPoint) (liftedPointGenerator secondPoint) dual+ else GeneratorRegularEdge (liftedPointGenerator secondPoint) (liftedPointGenerator firstPoint) dual++pointLabel :: LiftedPoint label -> label+pointLabel = exactPowerGeneratorLabel . liftedPointGenerator++topologyFromParts+ :: NonEmpty (LiftedPoint label)+ -> Set (LiftedPoint label)+ -> Set (LiftedPoint label)+ -> [GeneratorRegularFace label]+ -> [GeneratorRegularEdge label]+ -> Int+ -> GeneratorRegularTopology label+topologyFromParts points visible lowerDimensional faces edges peakFaces =+ let dispositions =+ fmap+ (\point ->+ ( liftedPointGenerator point+ , if Set.member point visible+ then RegularGeneratorVisible+ else+ if Set.member point lowerDimensional+ then RegularGeneratorLowerDimensional+ else RegularGeneratorHidden+ ))+ points+ visibleCount = Set.size visible+ lowerCount = Set.size lowerDimensional+ inputCount = NonEmpty.length points+ in GeneratorRegularTopology+ { generatorRegularDispositions = dispositions+ , generatorRegularFaces = faces+ , generatorRegularEdges = edges+ , generatorRegularReceipt =+ GeneratorRegularReceipt+ { generatorRegularInputSites = inputCount+ , generatorRegularVisibleSites = visibleCount+ , generatorRegularLowerDimensionalSites = lowerCount+ , generatorRegularHiddenSites = inputCount - visibleCount - lowerCount+ , generatorRegularFaceCount = length faces+ , generatorRegularEdgeCount = length edges+ , generatorRegularPeakHullFaces = peakFaces+ }+ }
src-public/Moonlight/Triangulation/Minkowski.hs view
@@ -15,6 +15,7 @@ , MinkowskiError (..) , MinkowskiReceipt (..) , convexMinkowskiSum+ , reflectConvexPolygon , minkowskiSum , erodeBy , openWith@@ -41,6 +42,7 @@ ) import Moonlight.Triangulation.Exact ( ExactPoint+ , exactClipExactIntersections , exactPointCoordinates ) import Moonlight.Triangulation.Internal.HandleDefs@@ -48,12 +50,9 @@ ) import Moonlight.Triangulation.Handles.Iterators.FixedIterators (innerFaces) import Moonlight.Triangulation.Internal.ExactRational- ( ExactRational- , exactRationalDenominator- , exactRationalNumerator+ ( exactRationalBitWidth ) import Moonlight.Triangulation.Internal.BoundaryCycle (cyclePairs)-import Moonlight.Triangulation.Internal.Dyadic (integerBitLength) import Moonlight.Triangulation.Internal.Minkowski.Convex ( addExactPoints , admittedConvexLoop@@ -324,7 +323,8 @@ -> ConvexPolygon -> Either MinkowskiError (PlanarRegion, MinkowskiReceipt) convexErosion element source sourcePolygon = do- eroded <- erodeConvexBy sourcePolygon (structuringElementPolygon element)+ (eroded, clipReceipt) <-+ erodeConvexBy sourcePolygon (structuringElementPolygon element) let result = maybe emptyPlanarRegion convexPolygonRegion eroded outputCells = maybe 0 (const 1) eroded generatedEdges = maybe 0 (NonEmpty.length . convexPolygonPoints) eroded@@ -337,7 +337,8 @@ , minkowskiGeneratedPieces = outputCells , minkowskiGeneratedConvolutionEdges = generatedEdges , minkowskiOverlayPasses = 0- , minkowskiExactCrossings = 0+ , minkowskiExactCrossings =+ exactClipExactIntersections clipReceipt , minkowskiOutputCells = outputCells , minkowskiExactCoordinateBitGrowth = coordinateBitGrowth@@ -586,10 +587,6 @@ List.foldl' pointBits maximumBits . exactLoopPoints pointBits maximumBits point = let (x, y) = exactPointCoordinates point- in max maximumBits (max (rationalBits x) (rationalBits y))--rationalBits :: ExactRational -> Int-rationalBits value =- max- (integerBitLength (abs (exactRationalNumerator value)))- (integerBitLength (exactRationalDenominator value))+ in max+ maximumBits+ (max (exactRationalBitWidth x) (exactRationalBitWidth y))
src-public/Moonlight/Triangulation/Overlay.hs view
@@ -1,4 +1,6 @@ {-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} -- | Exact labelled common refinement. Source boundaries descend through one@@ -38,7 +40,11 @@ , OverlayResult , OverlaySelectionKind (..) , OverlaySelectionError (..)+ , CoverGap+ , coverGapRegion+ , LayerCoverageError (..) , overlayLayers+ , overlayAll , overlayEmbeddedTriangulation , overlayReceipt , overlayCells@@ -46,18 +52,28 @@ , overlayArrangementEdges , overlayPlanarLayer , overlaySelectedRegion+ , overlayMass+ , overlayConfusion+ , layerCovers , overlayClosedUnion , overlayClosedIntersection , overlayRegularizedDifference ) where +import Control.DeepSeq (NFData) import Data.Bifunctor (first) import Control.Monad (filterM)+import Data.List.NonEmpty (NonEmpty (..)) import qualified Data.Map.Strict as Map import qualified Data.Vector as V-import Moonlight.Triangulation.CellSet (ExactCellSet)+import GHC.Generics (Generic)+import Moonlight.Triangulation.CellSet+ ( CellSelectionError+ , ExactCellSet+ ) import qualified Moonlight.Triangulation.Dcel as Dcel import Moonlight.Triangulation.Dcel (vertexData)+import Moonlight.Triangulation.Exact (ExactPoint) import Moonlight.Triangulation.Internal.HandleDefs ( UndirectedEdgeId , VertexId@@ -85,19 +101,45 @@ import Moonlight.Triangulation.Internal.Types ( ConstraintMode (Constrained) )+import Moonlight.Triangulation.Internal.Tournament+ ( interpretTournament+ , planTournament+ ) import Moonlight.Triangulation.Region ( PlanarLayer , PlanarRegion+ , PolygonComponent , RegionPublicationError (..) , emptyPlanarRegion , planarLayerOutsideLabel , planarLayerRegions+ , planarRegionComponents ) import Moonlight.Triangulation.Internal.Region.Publication ( labelledPlanarLayerFromExactCoordinates , planarLayerFromAdmittedComponents )+import Moonlight.Triangulation.Valuation+ ( ExactArea+ , polygonComponentArea+ ) +-- | Exact full-dimensional part of a requested window not owned by any+-- bounded label region in the candidate layer.+newtype CoverGap = CoverGap PlanarRegion+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++coverGapRegion :: CoverGap -> PlanarRegion+coverGapRegion (CoverGap region) = region++data LayerCoverageError label+ = LayerCoverageOverlayFailed !(OverlayError Bool label)+ | LayerCoverageGapPublicationFailed !RegionPublicationError+ | LayerCoverageGap !CoverGap+ deriving stock (Eq, Show, Generic)+ deriving anyclass (NFData)+ -- | Construct the exact common refinement and its one faithful resident DCEL. overlayLayers :: (Ord leftLabel, Ord rightLabel)@@ -113,6 +155,65 @@ (planarLayerOutsideLabel leftLayer, planarLayerOutsideLabel rightLayer) certified +-- | Construct the common refinement of a nonempty family in input order.+-- Adjacent layers glue in a balanced tree, so an early boundary participates+-- in logarithmically many intermediate arrangements rather than every later+-- left-associated step. Each intermediate layer is projected directly from+-- its certified binary result.+overlayAll+ :: Ord label+ => NonEmpty (PlanarLayer label)+ -> Either+ (OverlayError (NonEmpty label) (NonEmpty label))+ (PlanarLayer (NonEmpty label))+overlayAll =+ interpretTournament overlayLayerPair+ . planTournament+ . fmap singletonLabelLayer++overlayLayerPair+ :: Ord label+ => PlanarLayer (NonEmpty label)+ -> PlanarLayer (NonEmpty label)+ -> Either+ (OverlayError (NonEmpty label) (NonEmpty label))+ (PlanarLayer (NonEmpty label))+overlayLayerPair leftLayer rightLayer =+ flattenNaryOverlay <$> overlayLayers leftLayer rightLayer++singletonLabelLayer :: Ord label => PlanarLayer label -> PlanarLayer (NonEmpty label)+singletonLabelLayer layer =+ planarLayerFromAdmittedComponents+ (planarLayerOutsideLabel layer :| [])+ [ (label :| [], component)+ | (label, region) <- Map.toAscList (planarLayerRegions layer)+ , component <- planarRegionComponents region+ ]++flattenNaryOverlay+ :: Ord label+ => OverlayResult (NonEmpty label) (NonEmpty label)+ -> PlanarLayer (NonEmpty label)+flattenNaryOverlay = projectOverlayLayerWith (uncurry (<>))++projectOverlayLayerWith+ :: (Eq leftLabel, Eq rightLabel, Ord projectedLabel)+ => ((leftLabel, rightLabel) -> projectedLabel)+ -> OverlayResult leftLabel rightLabel+ -> PlanarLayer projectedLabel+projectOverlayLayerWith projectLabel result =+ planarLayerFromAdmittedComponents+ (projectLabel (overlayResultOutsideLabels result))+ [ (projectLabel (overlayCellLabels cell), component)+ | cell <- V.toList (overlayResultCells result)+ , overlayCellLabels cell /= overlayResultOutsideLabels result+ , BoundedOverlayCell component <- [overlayCellGeometry cell]+ ]++overlayCellLabels :: OverlayCell leftLabel rightLabel -> (leftLabel, rightLabel)+overlayCellLabels cell = (overlayCellLeft cell, overlayCellRight cell)+{-# INLINE overlayCellLabels #-}+ -- | The binary64 realization used by existing DCEL observations. Exact overlay -- operations accept 'OverlayResult', never this projection. overlayEmbeddedTriangulation@@ -158,17 +259,7 @@ :: (Ord leftLabel, Ord rightLabel) => OverlayResult leftLabel rightLabel -> PlanarLayer (leftLabel, rightLabel)-overlayPlanarLayer result =- planarLayerFromAdmittedComponents- (overlayResultOutsideLabels result)- [ ( (overlayCellLeft cell, overlayCellRight cell)- , component- )- | cell <- V.toList (overlayResultCells result)- , (overlayCellLeft cell, overlayCellRight cell)- /= overlayResultOutsideLabels result- , BoundedOverlayCell component <- [overlayCellGeometry cell]- ]+overlayPlanarLayer = projectOverlayLayerWith id -- | Publish the selected two-dimensional cells. Internal arrangement edges -- between differently labelled but jointly selected cells dissolve because@@ -188,13 +279,92 @@ labelFace pure (Map.findWithDefault emptyPlanarRegion True (planarLayerRegions published)) where- triangulation = overlayResultTriangulation result- exactPointAt vertex = Right (overlayExactPoint (vertexData triangulation vertex))- labelFace face- | faceCarriesExactArea result face =- selected <$> regionFaceLabels result face- | otherwise = Right False+ triangulation = overlayResultTriangulation result+ exactPointAt :: VertexId -> Either RegionPublicationError ExactPoint+ exactPointAt vertex = Right (overlayExactPoint (vertexData triangulation vertex))+ labelFace face+ | faceCarriesExactArea result face =+ selected <$> regionFaceLabels result face+ | otherwise = Right False +-- | Sum selected exact bounded cells without publishing a polygonal union or+-- computing its perimeter. Selecting the unbounded outside pair is refused by+-- the same obstruction used by 'overlaySelectedRegion'.+overlayMass+ :: ((leftLabel, rightLabel) -> Bool)+ -> OverlayResult leftLabel rightLabel+ -> Either RegionPublicationError ExactArea+overlayMass selected result+ | selected (overlayResultOutsideLabels result) = Left RegionUnboundedSelection+ | otherwise =+ Right (foldBoundedOverlayCells selectedCellArea mempty result)+ where+ selectedCellArea area labels component+ | selected labels = area <> polygonComponentArea component+ | otherwise = area++-- | Exact finite confusion masses, grouped by label pair in one traversal.+-- The outside pair is absent because its complete planar cell is unbounded;+-- bounded cavities carrying that same pair are not misreported as its total.+overlayConfusion+ :: (Ord leftLabel, Ord rightLabel)+ => OverlayResult leftLabel rightLabel+ -> Map.Map (leftLabel, rightLabel) ExactArea+overlayConfusion result =+ foldBoundedOverlayCells accumulateCell Map.empty result+ where+ outsideLabels = overlayResultOutsideLabels result+ accumulateCell masses labels component+ | labels == outsideLabels = masses+ | otherwise = Map.insertWith (<>) labels (polygonComponentArea component) masses++foldBoundedOverlayCells+ :: (accumulator -> (leftLabel, rightLabel) -> PolygonComponent -> accumulator)+ -> accumulator+ -> OverlayResult leftLabel rightLabel+ -> accumulator+foldBoundedOverlayCells accumulate initial result =+ V.foldl' descend initial (overlayResultCells result)+ where+ descend accumulator cell =+ case overlayCellGeometry cell of+ BoundedOverlayCell component ->+ accumulate accumulator (overlayCellLabels cell) component+ UnboundedOverlayCell _ -> accumulator+{-# INLINE foldBoundedOverlayCells #-}++-- | Certify full-dimensional coverage of an admitted polygonal window. The+-- success path inspects certified cells only; an exact gap region is glued+-- solely when the obstruction actually exists.+layerCovers+ :: Ord label+ => PlanarLayer label+ -> PolygonComponent+ -> Either (LayerCoverageError label) ()+layerCovers layer window = do+ let outside = planarLayerOutsideLabel layer+ windowLayer = planarLayerFromAdmittedComponents False [(True, window)]+ result <- first LayerCoverageOverlayFailed (overlayLayers windowLayer layer)+ if V.any (isGapCell outside) (overlayResultCells result)+ then do+ gap <-+ first LayerCoverageGapPublicationFailed+ ( overlaySelectedRegion+ (\(insideWindow, label) ->+ insideWindow && label == outside)+ result+ )+ Left (LayerCoverageGap (CoverGap gap))+ else Right ()++isGapCell :: Eq label => label -> OverlayCell Bool label -> Bool+isGapCell outside cell =+ overlayCellLeft cell+ && overlayCellRight cell == outside+ && case overlayCellGeometry cell of+ BoundedOverlayCell _ -> True+ UnboundedOverlayCell _ -> False+ overlayClosedUnion :: (Ord leftLabel, Ord rightLabel) => (leftLabel -> Bool)@@ -270,6 +440,7 @@ -> Either OverlaySelectionError ExactCellSet closeSelectedCells selectedVertices selectedEdges selectFace result = let triangulation = overlayResultTriangulation result+ exactPointAt :: VertexId -> Either CellSelectionError ExactPoint exactPointAt vertex = Right (overlayExactPoint (vertexData triangulation vertex)) in do selectedFaces <-
+ src-public/Moonlight/Triangulation/PowerDiagram.hs view
@@ -0,0 +1,955 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}++-- | Exact regular topology and power cells. Labelled cell dispositions are+-- authoritative; regular topology and planar layers are derived views.+module Moonlight.Triangulation.PowerDiagram+ ( PowerWeight+ , PowerWeightError (..)+ , powerWeight+ , powerWeightExact+ , PowerSite+ , powerSite+ , powerSiteLabel+ , powerSitePosition+ , powerSiteWeight+ , PowerCellDisposition (..)+ , RegularSiteDisposition (..)+ , RegularFace+ , regularFaceLabels+ , regularFaceDualPoint+ , PowerDualEdge (..)+ , RegularEdge+ , regularEdgeLabels+ , regularEdgeDual+ , RegularTriangulation+ , regularTriangulation+ , regularSiteDisposition+ , regularFaces+ , regularEdges+ , regularNeighbours+ , RegularTriangulationReceipt (..)+ , BoundedPowerDiagram+ , boundedPowerDiagram+ , powerCellDisposition+ , powerCellDispositions+ , powerDiagramPlanarLayer+ , PowerDiagramError (..)+ , RegularTopologyError (..)+ , PowerDiagramReceipt (..)+ , powerDiagramInputSites+ , powerDiagramPeakIntermediateBitGrowth+ , powerDiagramFinalCoordinateBitGrowth+ , AffineForm (..)+ , UpperEnvelopeError (..)+ , upperEnvelope+ ) where++import Control.DeepSeq (NFData)+import Data.Bifunctor (first)+import qualified Data.Foldable as Foldable+import qualified Data.List as List+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NonEmpty+import qualified Data.Map.Strict as Map+import Data.Map.Strict (Map)+import Data.Ord (comparing)+import Data.Set (Set)+import qualified Data.Set as Set+import qualified Data.Vector as Vector+import GHC.Generics (Generic)+import Moonlight.Triangulation.Exact+ ( ExactClipDisposition (..)+ , ExactClipError+ , ExactClipReceipt (..)+ , ExactAffineLine+ , ExactClosedHalfPlane+ , ExactHalfPlaneError+ , ExactPoint+ , ExactRay+ , ExactRetainedPolygon+ , ExactSegment+ , exactClipRetainedPolygon+ , exactClosedHalfPlane+ , exactPointCoordinates+ , exactPointBitWidth+ , exactPointFromQueryPoint+ , exactRetainedPolygon+ , oppositeExactAffineLine+ )+import Moonlight.Triangulation.Internal.ExactRational+ ( ExactRational+ , exactRationalBitWidth+ , exactRationalFromFiniteDouble+ )+import Moonlight.Triangulation.Internal.BoundaryCycle+ ( consecutivePairs )+import Moonlight.Triangulation.Internal.RegularTriangulation+ ( DistinctSlopeGenerators (..)+ , ExactPowerGenerator (..)+ , GeneratorDualGeometry (..)+ , GeneratorRegularEdge (..)+ , GeneratorRegularFace (..)+ , GeneratorRegularReceipt (..)+ , GeneratorRegularTopology (..)+ , RegularGeneratorDisposition (..)+ , RegularTopologyError (..)+ , exactGeneratorAxis+ , regularGeneratorTopology+ )+import Moonlight.Triangulation.Internal.Minkowski.Convex+ ( convexHullPolygon+ , convexPolygonComponent+ , convexPolygonFromRetained+ )+import Moonlight.Triangulation.Internal.Overlay.Types+ ( OverlayCell (..)+ , OverlayCellGeometry (..)+ , OverlayError+ , OverlayResult (..)+ )+import Moonlight.Triangulation.Internal.Region.Publication+ ( planarLayerFromAdmittedComponents+ )+import Moonlight.Triangulation.Math (mkQueryPoint)+import Moonlight.Triangulation.Minkowski+ ( ConvexPolygon+ , MinkowskiError+ , convexPolygonPoints+ )+import Moonlight.Triangulation.Overlay+ ( overlayLayers )+import Moonlight.Triangulation.Region+ ( PlanarLayer+ , PolygonComponent+ , exactLoopPoints+ , polygonOuterLoop+ )+import Moonlight.Triangulation.Types+ ( NonFiniteValue+ , Point+ , PointValidationError+ , QueryPoint+ , classifyNonFinite+ , queryPointValue+ )++-- | An admitted signed additive power offset. Power distance is+-- @||x-p||^2-w@, so negative values are lawful and this is deliberately not a+-- squared-radius refinement.+newtype PowerWeight = PowerWeight ExactRational+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | The sole obstruction to admitting a signed binary64 power offset.+data PowerWeightError+ = PowerWeightNonFinite !NonFiniteValue+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | Admit a finite binary64 power offset exactly.+powerWeight :: Double -> Either PowerWeightError PowerWeight+powerWeight value =+ case classifyNonFinite value of+ Just obstruction -> Left (PowerWeightNonFinite obstruction)+ Nothing -> Right (PowerWeight (exactRationalFromFiniteDouble value))++-- | Exact rational value of an admitted power offset.+powerWeightExact :: PowerWeight -> ExactRational+powerWeightExact (PowerWeight value) = value++-- | One labelled, admitted weighted site. Construction validates and+-- canonicalizes the binary64 position once.+data PowerSite label = PowerSite+ { powerSiteLabel :: !label+ , powerSiteQueryPoint :: !QueryPoint+ , powerSiteWeight :: !PowerWeight+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++powerSite+ :: label+ -> Point+ -> PowerWeight+ -> Either (PowerDiagramError label) (PowerSite label)+powerSite label point weight =+ PowerSite label <$> first (PowerSitePositionInvalid label) (mkQueryPoint point) <*> pure weight++powerSitePosition :: PowerSite label -> Point+powerSitePosition = queryPointValue . powerSiteQueryPoint++-- | Exactly one authoritative result for each submitted label.+data PowerCellDisposition label+ = PublishedPowerCell !ConvexPolygon+ | LowerDimensionalPowerCell !(NonEmpty ExactPoint)+ | EmptyPowerCell+ | CoincidentEquivalentTo !label+ | CoincidentDominatedBy !label+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | Visibility of one submitted label in the exact regular subdivision.+data RegularSiteDisposition label+ = RegularSiteVisible+ | RegularSiteLowerDimensional+ | RegularSiteHidden+ | RegularSiteCoincidentEquivalentTo !label+ | RegularSiteCoincidentDominatedBy !label+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | One oriented regular face and its exact weighted-dual vertex.+data RegularFace label = RegularFace !label !label !label !ExactPoint+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++regularFaceLabels :: RegularFace label -> (label, label, label)+regularFaceLabels (RegularFace firstLabel secondLabel thirdLabel _) =+ (firstLabel, secondLabel, thirdLabel)++regularFaceDualPoint :: RegularFace label -> ExactPoint+regularFaceDualPoint (RegularFace _ _ _ dualPoint) = dualPoint++-- | Exact weighted Voronoi geometry dual to one regular edge.+data PowerDualEdge+ = BoundedPowerDual !ExactSegment+ | UnboundedPowerDual !ExactRay+ | FullLinePowerDual !ExactAffineLine+ | CollapsedPowerDual !ExactPoint+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | One unordered regular edge and its exact dual geometry.+data RegularEdge label = RegularEdge !label !label !PowerDualEdge+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++regularEdgeLabels :: RegularEdge label -> (label, label)+regularEdgeLabels (RegularEdge firstLabel secondLabel _) =+ (firstLabel, secondLabel)++regularEdgeDual :: RegularEdge label -> PowerDualEdge+regularEdgeDual (RegularEdge _ _ dual) = dual++-- | Exact regular subdivision, opaque so incidence and visibility cannot+-- disagree.+data RegularTriangulation label = RegularTriangulation+ { storedRegularDispositions :: !(Map label (RegularSiteDisposition label))+ , storedRegularFaces :: ![RegularFace label]+ , storedRegularEdges :: ![RegularEdge label]+ , storedRegularNeighbours :: !(Map label (Set label))+ }+ deriving stock (Eq, Show, Generic)+ deriving anyclass (NFData)++data RegularTriangulationReceipt = RegularTriangulationReceipt+ { regularTriangulationInputSites :: !Int+ , regularTriangulationRepresentativeSites :: !Int+ , regularTriangulationVisibleSites :: !Int+ , regularTriangulationLowerDimensionalSites :: !Int+ , regularTriangulationHiddenSites :: !Int+ , regularTriangulationCoincidentSites :: !Int+ , regularTriangulationFaces :: !Int+ , regularTriangulationEdges :: !Int+ , regularTriangulationPeakHullFaces :: !Int+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | Total labelled result, opaque so callers cannot omit a submitted label.+newtype BoundedPowerDiagram label =+ BoundedPowerDiagram (Map label (PowerCellDisposition label))+ deriving stock (Eq, Show, Generic)+ deriving anyclass (NFData)++data PowerDiagramError label+ = PowerSitePositionInvalid !label !PointValidationError+ | DuplicatePowerSiteLabel !label+ | PowerDomainInvalid !ExactHalfPlaneError+ | PowerBisectorInvalid !label !label !ExactHalfPlaneError+ | PowerRegularTopologyFailed !(RegularTopologyError label)+ | PowerCellClipFailed !label !ExactClipError+ | PowerDiagramOutsideLabelCollides !label+ deriving stock (Eq, Show, Generic)+ deriving anyclass (NFData)++data PowerDiagramReceipt = PowerDiagramReceipt+ { powerDiagramDomainVertices :: !Int+ , powerDiagramSubmittedSiteConstraints :: !Int+ , powerDiagramActiveBoundaries :: !Int+ , powerDiagramBoundaryCompatibilityChecks :: !Int+ , powerDiagramExactIntersections :: !Int+ , powerDiagramPublishedCells :: !Int+ , powerDiagramLowerDimensionalCells :: !Int+ , powerDiagramEmptyCells :: !Int+ , powerDiagramCoincidentEquivalentCells :: !Int+ , powerDiagramCoincidentDominatedCells :: !Int+ , powerDiagramRegularFaces :: !Int+ , powerDiagramRegularEdges :: !Int+ , powerDiagramOracleCells :: !Int+ , powerDiagramMaximumCellConstraints :: !Int+ , powerDiagramMaximumInputBits :: !Int+ , powerDiagramMaximumAffineCoefficientBits :: !Int+ , powerDiagramPeakIntermediateCoordinateBits :: !Int+ , powerDiagramFinalCoordinateBits :: !Int+ , powerDiagramFinalDenominatorBits :: !Int+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | Submitted sites, derived from the exhaustive disposition partition.+powerDiagramInputSites :: PowerDiagramReceipt -> Int+powerDiagramInputSites receipt =+ powerDiagramPublishedCells receipt+ + powerDiagramLowerDimensionalCells receipt+ + powerDiagramEmptyCells receipt+ + powerDiagramCoincidentEquivalentCells receipt+ + powerDiagramCoincidentDominatedCells receipt+{-# INLINE powerDiagramInputSites #-}++-- | Peak exact-coordinate width beyond the widest admitted input.+powerDiagramPeakIntermediateBitGrowth :: PowerDiagramReceipt -> Int+powerDiagramPeakIntermediateBitGrowth receipt =+ max+ 0+ ( powerDiagramPeakIntermediateCoordinateBits receipt+ - powerDiagramMaximumInputBits receipt+ )+{-# INLINE powerDiagramPeakIntermediateBitGrowth #-}++-- | Published coordinate width beyond the widest admitted input.+powerDiagramFinalCoordinateBitGrowth :: PowerDiagramReceipt -> Int+powerDiagramFinalCoordinateBitGrowth receipt =+ max+ 0+ ( powerDiagramFinalCoordinateBits receipt+ - powerDiagramMaximumInputBits receipt+ )+{-# INLINE powerDiagramFinalCoordinateBitGrowth #-}++-- | One exact affine form @c0 + cx*x + cy*y@.+data AffineForm = AffineForm+ { affineFormConstant :: !ExactRational+ , affineFormXCoefficient :: !ExactRational+ , affineFormYCoefficient :: !ExactRational+ }+ deriving stock (Eq, Ord, Show, Generic)+ deriving anyclass (NFData)++-- | Typed obstructions from exact affine argmax decomposition. Power-cell+-- construction remains the canonical geometric owner; window restriction is+-- the only additional boundary.+data UpperEnvelopeError label+ = UpperEnvelopeEmptyForms+ | UpperEnvelopeWindowHullFailed !MinkowskiError+ | UpperEnvelopePowerConstructionFailed !(PowerDiagramError label)+ | UpperEnvelopeWindowOverlayFailed !(OverlayError (Maybe label) Bool)+ deriving stock (Eq, Show, Generic)+ deriving anyclass (NFData)++-- | Construct the exact regular subdivision before any bounded clipping.+regularTriangulation+ :: Ord label+ => NonEmpty (PowerSite label)+ -> Either+ (PowerDiagramError label)+ (RegularTriangulation label, RegularTriangulationReceipt)+regularTriangulation submitted = do+ sortedSites <- validateAndSortSites submitted+ let generators = fmap (fst . prepareExactPowerGenerator) sortedSites+ section <- resolvedGeneratorSection generators+ pure (publishRegularSection section)++regularSiteDisposition+ :: Ord label+ => label+ -> RegularTriangulation label+ -> Maybe (RegularSiteDisposition label)+regularSiteDisposition label = Map.lookup label . storedRegularDispositions++regularFaces :: RegularTriangulation label -> [RegularFace label]+regularFaces = storedRegularFaces++regularEdges :: RegularTriangulation label -> [RegularEdge label]+regularEdges = storedRegularEdges++regularNeighbours+ :: Ord label+ => label+ -> RegularTriangulation label+ -> Set label+regularNeighbours label =+ Map.findWithDefault Set.empty label . storedRegularNeighbours++boundedPowerDiagram+ :: Ord label+ => ConvexPolygon+ -> NonEmpty (PowerSite label)+ -> Either (PowerDiagramError label) (BoundedPowerDiagram label, PowerDiagramReceipt)+boundedPowerDiagram domain submitted = do+ sortedSites <- validateAndSortSites submitted+ let preparedGenerators = fmap prepareExactPowerGenerator sortedSites+ generators = fmap fst preparedGenerators+ siteInputBits =+ Foldable.foldl' (\bits preparation -> max bits (snd preparation)) 0 preparedGenerators+ (dispositions, clipReceipt, regularReceipt, maximumCellConstraints) <-+ exactGeneratorDispositionsWith (<>) mempty domain generators+ let receipt =+ aggregateReceipt+ domain+ dispositions+ siteInputBits+ clipReceipt+ regularReceipt+ maximumCellConstraints+ pure (BoundedPowerDiagram dispositions, receipt)++-- | Full-dimensional labelled argmax regions for affine forms+-- @c0 + cx*x + cy*y@ inside an admitted polygonal window. The result is a+-- planar projection: lower-dimensional and empty winners intentionally have+-- no region. Use 'regularTriangulation' on corresponding weighted sites when+-- those dispositions or exact unbounded duals are required. Identical forms+-- choose the least label, independent of map construction order.+upperEnvelope+ :: Ord label+ => PolygonComponent+ -> Map label AffineForm+ -> Either (UpperEnvelopeError label) (PlanarLayer (Maybe label))+upperEnvelope window forms = do+ generators <- affineFormGenerators forms+ domain <-+ first UpperEnvelopeWindowHullFailed+ (convexHullPolygon (exactLoopPoints (polygonOuterLoop window)))+ (dispositions, _, _, _) <-+ first UpperEnvelopePowerConstructionFailed+ (exactGeneratorDispositionsWith discardClipReceipt () domain generators)+ let envelopeLayer = affineDispositionLayer dispositions+ if convexPolygonComponent domain == window+ then Right envelopeLayer+ else restrictEnvelopeToWindow window envelopeLayer++powerCellDisposition+ :: Ord label+ => label+ -> BoundedPowerDiagram label+ -> Maybe (PowerCellDisposition label)+powerCellDisposition label (BoundedPowerDiagram dispositions) =+ Map.lookup label dispositions++powerCellDispositions+ :: BoundedPowerDiagram label+ -> [(label, PowerCellDisposition label)]+powerCellDispositions (BoundedPowerDiagram dispositions) = Map.toAscList dispositions++-- | Publish only full-dimensional cells. The outside label is a caller-owned+-- view choice and may not collide with any submitted site label.+powerDiagramPlanarLayer+ :: Ord label+ => label+ -> BoundedPowerDiagram label+ -> Either (PowerDiagramError label) (PlanarLayer label)+powerDiagramPlanarLayer outside (BoundedPowerDiagram dispositions)+ | Map.member outside dispositions = Left (PowerDiagramOutsideLabelCollides outside)+ | otherwise =+ Right (publishedPowerLayer outside id dispositions)++affineDispositionLayer+ :: Ord label+ => Map label (PowerCellDisposition label)+ -> PlanarLayer (Maybe label)+affineDispositionLayer = publishedPowerLayer Nothing Just++publishedPowerLayer+ :: Ord publishedLabel+ => publishedLabel+ -> (label -> publishedLabel)+ -> Map label (PowerCellDisposition label)+ -> PlanarLayer publishedLabel+publishedPowerLayer outside publishLabel dispositions =+ planarLayerFromAdmittedComponents+ outside+ [ (publishLabel label, convexPolygonComponent polygon)+ | (label, PublishedPowerCell polygon) <- Map.toAscList dispositions+ ]++restrictEnvelopeToWindow+ :: Ord label+ => PolygonComponent+ -> PlanarLayer (Maybe label)+ -> Either (UpperEnvelopeError label) (PlanarLayer (Maybe label))+restrictEnvelopeToWindow window envelopeLayer = do+ let windowLayer = planarLayerFromAdmittedComponents False [(True, window)]+ clipped <-+ first UpperEnvelopeWindowOverlayFailed+ (overlayLayers envelopeLayer windowLayer)+ pure+ ( planarLayerFromAdmittedComponents+ Nothing+ [ (Just label, component)+ | cell <- Vector.toList (overlayResultCells clipped)+ , overlayCellRight cell+ , Just label <- [overlayCellLeft cell]+ , BoundedOverlayCell component <- [overlayCellGeometry cell]+ ]+ )++validateAndSortSites+ :: Ord label+ => NonEmpty (PowerSite label)+ -> Either (PowerDiagramError label) (NonEmpty (PowerSite label))+validateAndSortSites submitted =+ let sorted = NonEmpty.sortBy (comparing powerSiteLabel) submitted+ in case List.find (uncurry sameLabel) (consecutivePairs (NonEmpty.toList sorted)) of+ Just (duplicate, _) -> Left (DuplicatePowerSiteLabel (powerSiteLabel duplicate))+ Nothing -> Right sorted+ where+ sameLabel :: Eq label => PowerSite label -> PowerSite label -> Bool+ sameLabel left right = powerSiteLabel left == powerSiteLabel right++prepareExactPowerGenerator :: PowerSite label -> (ExactPowerGenerator label, Int)+prepareExactPowerGenerator site =+ let point = exactPointFromQueryPoint (powerSiteQueryPoint site)+ (coordinateX, coordinateY) = exactPointCoordinates point+ weight = powerWeightExact (powerSiteWeight site)+ in ( ExactPowerGenerator+ { exactPowerGeneratorLabel = powerSiteLabel site+ , exactPowerGeneratorXCoefficient = 2 * coordinateX+ , exactPowerGeneratorYCoefficient = 2 * coordinateY+ , exactPowerGeneratorConstant =+ weight - coordinateX * coordinateX - coordinateY * coordinateY+ }+ , max (exactPointBitWidth point) (exactRationalBitWidth weight)+ )++affineFormGenerators+ :: Map label AffineForm+ -> Either (UpperEnvelopeError label) (NonEmpty (ExactPowerGenerator label))+affineFormGenerators forms =+ case Map.minViewWithKey forms of+ Nothing -> Left UpperEnvelopeEmptyForms+ Just ((firstLabel, firstForm), remaining) ->+ Right+ ( affineFormGenerator firstLabel firstForm+ :| fmap (uncurry affineFormGenerator) (Map.toAscList remaining)+ )++affineFormGenerator+ :: label+ -> AffineForm+ -> ExactPowerGenerator label+affineFormGenerator label form =+ ExactPowerGenerator+ { exactPowerGeneratorLabel = label+ , exactPowerGeneratorXCoefficient = affineFormXCoefficient form+ , exactPowerGeneratorYCoefficient = affineFormYCoefficient form+ , exactPowerGeneratorConstant = affineFormConstant form+ }++exactGeneratorDispositionsWith+ :: Ord label+ => (summary -> ExactClipReceipt -> summary)+ -> summary+ -> ConvexPolygon+ -> NonEmpty (ExactPowerGenerator label)+ -> Either+ (PowerDiagramError label)+ ( Map label (PowerCellDisposition label)+ , summary+ , GeneratorRegularReceipt+ , Int+ )+exactGeneratorDispositionsWith summarizeReceipt initialSummary domain generators = do+ retainedDomain <-+ first PowerDomainInvalid (exactRetainedPolygon (convexPolygonPoints domain))+ section <- resolvedGeneratorSection generators+ let topology = resolvedRegularTopology section+ constraints <- prepareRegularConstraintSection topology+ clipped <-+ traverse+ (clipPowerCell retainedDomain section constraints)+ (generatorRegularDispositions topology)+ let (publishedAssociations, summary, maximumCellConstraints) =+ Foldable.foldl'+ (\(associations, accumulatedSummary, peak) (label, disposition, cellReceipt, cellAxes) ->+ let !combinedSummary = summarizeReceipt accumulatedSummary cellReceipt+ in ( (label, disposition) : associations+ , combinedSummary+ , max peak cellAxes+ ))+ ([], initialSummary, 0)+ clipped+ publishedDispositions = Map.fromList publishedAssociations+ dispositions =+ fmap coincidentPowerDisposition (resolvedCoincidentDispositions section)+ <> publishedDispositions+ pure+ ( dispositions+ , summary+ , generatorRegularReceipt topology+ , maximumCellConstraints+ )++discardClipReceipt :: () -> ExactClipReceipt -> ()+discardClipReceipt _ _ = ()++groupGeneratorsBySlope+ :: NonEmpty (ExactPowerGenerator label)+ -> NonEmpty (NonEmpty (ExactPowerGenerator label))+groupGeneratorsBySlope (initial :| remaining) =+ let initialSlope = exactGeneratorSlope initial+ (sameInitialSlope, otherGenerators) =+ List.partition ((== initialSlope) . exactGeneratorSlope) remaining+ otherGroups =+ Map.fromListWith+ (<>)+ [ (exactGeneratorSlope generator, generator :| [])+ | generator <- otherGenerators+ ]+ in (initial :| sameInitialSlope) :| Map.elems otherGroups++exactGeneratorSlope+ :: ExactPowerGenerator label+ -> (ExactRational, ExactRational)+exactGeneratorSlope generator =+ ( exactPowerGeneratorXCoefficient generator+ , exactPowerGeneratorYCoefficient generator+ )++resolveCoincidentGeneratorGroup+ :: Ord label+ => NonEmpty (ExactPowerGenerator label)+ -> (ExactPowerGenerator label, [(label, CoincidentGeneratorDisposition label)])+resolveCoincidentGeneratorGroup generators@(initial :| remaining) =+ let representative = List.foldl' chooseRepresentative initial remaining+ representativeLabel = exactPowerGeneratorLabel representative+ in ( representative+ , [ ( exactPowerGeneratorLabel generator+ , classifyCoincidentGenerator representative generator+ )+ | generator <- NonEmpty.toList generators+ , exactPowerGeneratorLabel generator /= representativeLabel+ ]+ )+ where+ chooseRepresentative+ :: Ord label+ => ExactPowerGenerator label+ -> ExactPowerGenerator label+ -> ExactPowerGenerator label+ chooseRepresentative selected candidate =+ case compare+ (exactPowerGeneratorConstant candidate)+ (exactPowerGeneratorConstant selected) of+ GT -> candidate+ LT -> selected+ EQ ->+ if exactPowerGeneratorLabel candidate < exactPowerGeneratorLabel selected+ then candidate+ else selected++classifyCoincidentGenerator+ :: ExactPowerGenerator label+ -> ExactPowerGenerator label+ -> CoincidentGeneratorDisposition label+classifyCoincidentGenerator representative candidate+ | exactPowerGeneratorConstant candidate == exactPowerGeneratorConstant representative =+ CoincidentGeneratorEquivalentTo (exactPowerGeneratorLabel representative)+ | otherwise = CoincidentGeneratorDominatedBy (exactPowerGeneratorLabel representative)++data CoincidentGeneratorDisposition label+ = CoincidentGeneratorEquivalentTo !label+ | CoincidentGeneratorDominatedBy !label++coincidentPowerDisposition+ :: CoincidentGeneratorDisposition label+ -> PowerCellDisposition label+coincidentPowerDisposition disposition =+ case disposition of+ CoincidentGeneratorEquivalentTo label -> CoincidentEquivalentTo label+ CoincidentGeneratorDominatedBy label -> CoincidentDominatedBy label++coincidentRegularDisposition+ :: CoincidentGeneratorDisposition label+ -> RegularSiteDisposition label+coincidentRegularDisposition disposition =+ case disposition of+ CoincidentGeneratorEquivalentTo label -> RegularSiteCoincidentEquivalentTo label+ CoincidentGeneratorDominatedBy label -> RegularSiteCoincidentDominatedBy label++data ResolvedGeneratorSection label = ResolvedGeneratorSection+ { resolvedCoincidentDispositions :: !(Map label (CoincidentGeneratorDisposition label))+ , resolvedRegularTopology :: !(GeneratorRegularTopology label)+ }++resolvedGeneratorSection+ :: Ord label+ => NonEmpty (ExactPowerGenerator label)+ -> Either (PowerDiagramError label) (ResolvedGeneratorSection label)+resolvedGeneratorSection generators = do+ let resolvedGroups = fmap resolveCoincidentGeneratorGroup (groupGeneratorsBySlope generators)+ representatives = fmap fst resolvedGroups+ coincidentDispositions =+ Map.fromList (concatMap snd (NonEmpty.toList resolvedGroups))+ topology <-+ first PowerRegularTopologyFailed+ (regularGeneratorTopology (DistinctSlopeGenerators representatives))+ pure+ ResolvedGeneratorSection+ { resolvedCoincidentDispositions = coincidentDispositions+ , resolvedRegularTopology = topology+ }++publishRegularSection+ :: Ord label+ => ResolvedGeneratorSection label+ -> (RegularTriangulation label, RegularTriangulationReceipt)+publishRegularSection section =+ let topology = resolvedRegularTopology section+ coincident = resolvedCoincidentDispositions section+ dispositions =+ Map.fromList+ [ ( exactPowerGeneratorLabel generator+ , publishGeneratorDisposition disposition+ )+ | (generator, disposition) <-+ NonEmpty.toList (generatorRegularDispositions topology)+ ]+ <> fmap coincidentRegularDisposition coincident+ faces = fmap publishGeneratorFace (generatorRegularFaces topology)+ edges = fmap publishGeneratorEdge (generatorRegularEdges topology)+ generatorReceipt = generatorRegularReceipt topology+ coincidentCount = Map.size coincident+ in ( RegularTriangulation+ { storedRegularDispositions = dispositions+ , storedRegularFaces = faces+ , storedRegularEdges = edges+ , storedRegularNeighbours = regularNeighbourSection topology+ }+ , RegularTriangulationReceipt+ { regularTriangulationInputSites =+ generatorRegularInputSites generatorReceipt + coincidentCount+ , regularTriangulationRepresentativeSites =+ generatorRegularInputSites generatorReceipt+ , regularTriangulationVisibleSites =+ generatorRegularVisibleSites generatorReceipt+ , regularTriangulationLowerDimensionalSites =+ generatorRegularLowerDimensionalSites generatorReceipt+ , regularTriangulationHiddenSites =+ generatorRegularHiddenSites generatorReceipt+ , regularTriangulationCoincidentSites = coincidentCount+ , regularTriangulationFaces = generatorRegularFaceCount generatorReceipt+ , regularTriangulationEdges = generatorRegularEdgeCount generatorReceipt+ , regularTriangulationPeakHullFaces =+ generatorRegularPeakHullFaces generatorReceipt+ }+ )++publishGeneratorDisposition+ :: RegularGeneratorDisposition+ -> RegularSiteDisposition label+publishGeneratorDisposition disposition =+ case disposition of+ RegularGeneratorVisible -> RegularSiteVisible+ RegularGeneratorLowerDimensional -> RegularSiteLowerDimensional+ RegularGeneratorHidden -> RegularSiteHidden++publishGeneratorFace :: GeneratorRegularFace label -> RegularFace label+publishGeneratorFace face =+ RegularFace+ (generatorRegularFaceFirst face)+ (generatorRegularFaceSecond face)+ (generatorRegularFaceThird face)+ (generatorRegularFaceDualPoint face)++publishGeneratorEdge :: GeneratorRegularEdge label -> RegularEdge label+publishGeneratorEdge edge =+ RegularEdge+ (exactPowerGeneratorLabel (generatorRegularEdgeFirst edge))+ (exactPowerGeneratorLabel (generatorRegularEdgeSecond edge))+ (case generatorRegularEdgeDual edge of+ GeneratorDualSegment segment -> BoundedPowerDual segment+ GeneratorDualRay ray -> UnboundedPowerDual ray+ GeneratorDualLine line -> FullLinePowerDual line+ GeneratorDualCollapsed point -> CollapsedPowerDual point)++regularNeighbourSection+ :: Ord label+ => GeneratorRegularTopology label+ -> Map label (Set label)+regularNeighbourSection =+ List.foldl' insertRegularNeighbourEdge Map.empty . generatorRegularEdges++insertRegularNeighbourEdge+ :: Ord label+ => Map label (Set label)+ -> GeneratorRegularEdge label+ -> Map label (Set label)+insertRegularNeighbourEdge neighbours edge =+ let firstLabel = exactPowerGeneratorLabel (generatorRegularEdgeFirst edge)+ secondLabel = exactPowerGeneratorLabel (generatorRegularEdgeSecond edge)+ in Map.insertWith Set.union firstLabel (Set.singleton secondLabel)+ (Map.insertWith Set.union secondLabel (Set.singleton firstLabel) neighbours)++prepareRadicalAxis+ :: ExactPowerGenerator label+ -> ExactPowerGenerator label+ -> Either (PowerDiagramError label) ExactAffineLine+prepareRadicalAxis firstGenerator secondGenerator =+ first+ ( PowerBisectorInvalid+ (exactPowerGeneratorLabel firstGenerator)+ (exactPowerGeneratorLabel secondGenerator)+ )+ (exactGeneratorAxis firstGenerator secondGenerator)++prepareRegularConstraintSection+ :: Ord label+ => GeneratorRegularTopology label+ -> Either (PowerDiagramError label) (Map label [ExactClosedHalfPlane])+prepareRegularConstraintSection =+ Foldable.foldlM prepareRegularConstraintEdge Map.empty . generatorRegularEdges++prepareRegularConstraintEdge+ :: Ord label+ => Map label [ExactClosedHalfPlane]+ -> GeneratorRegularEdge label+ -> Either (PowerDiagramError label) (Map label [ExactClosedHalfPlane])+prepareRegularConstraintEdge constraints edge = do+ let firstGenerator = generatorRegularEdgeFirst edge+ secondGenerator = generatorRegularEdgeSecond edge+ firstLabel = exactPowerGeneratorLabel firstGenerator+ secondLabel = exactPowerGeneratorLabel secondGenerator+ axis <- prepareRadicalAxis firstGenerator secondGenerator+ pure+ ( Map.insertWith (<>) secondLabel [exactClosedHalfPlane (oppositeExactAffineLine axis)]+ (Map.insertWith (<>) firstLabel [exactClosedHalfPlane axis] constraints)+ )++clipPowerCell+ :: Ord label+ => ExactRetainedPolygon+ -> ResolvedGeneratorSection label+ -> Map label [ExactClosedHalfPlane]+ -> (ExactPowerGenerator label, RegularGeneratorDisposition)+ -> Either+ (PowerDiagramError label)+ (label, PowerCellDisposition label, ExactClipReceipt, Int)+clipPowerCell retainedDomain section constraints (ownerGenerator, disposition) =+ let ownerLabel = exactPowerGeneratorLabel ownerGenerator+ topology = resolvedRegularTopology section+ in case disposition of+ RegularGeneratorHidden ->+ pure (ownerLabel, EmptyPowerCell, mempty, 0)+ RegularGeneratorVisible ->+ finishPowerCell+ retainedDomain+ ownerGenerator+ (Map.findWithDefault [] ownerLabel constraints)+ RegularGeneratorLowerDimensional -> do+ let competitors =+ [ generator+ | (generator, _) <-+ NonEmpty.toList (generatorRegularDispositions topology)+ , exactPowerGeneratorLabel generator /= ownerLabel+ ]+ halfPlanes <- traverse (preparedDirectHalfPlane ownerGenerator) competitors+ finishPowerCell retainedDomain ownerGenerator halfPlanes++preparedDirectHalfPlane+ :: ExactPowerGenerator label+ -> ExactPowerGenerator label+ -> Either (PowerDiagramError label) ExactClosedHalfPlane+preparedDirectHalfPlane owner competitor =+ exactClosedHalfPlane <$> prepareRadicalAxis owner competitor++finishPowerCell+ :: ExactRetainedPolygon+ -> ExactPowerGenerator label+ -> [ExactClosedHalfPlane]+ -> Either+ (PowerDiagramError label)+ (label, PowerCellDisposition label, ExactClipReceipt, Int)+finishPowerCell retainedDomain ownerGenerator halfPlanes = do+ (exactDisposition, receipt) <-+ first (PowerCellClipFailed (exactPowerGeneratorLabel ownerGenerator))+ (exactClipRetainedPolygon retainedDomain halfPlanes)+ pure+ ( exactPowerGeneratorLabel ownerGenerator+ , case exactDisposition of+ ExactClipFullDimensional retained ->+ PublishedPowerCell (convexPolygonFromRetained retained)+ ExactClipLowerDimensional points -> LowerDimensionalPowerCell points+ ExactClipEmpty -> EmptyPowerCell+ , receipt+ , length halfPlanes+ )++aggregateReceipt+ :: ConvexPolygon+ -> Map label (PowerCellDisposition label)+ -> Int+ -> ExactClipReceipt+ -> GeneratorRegularReceipt+ -> Int+ -> PowerDiagramReceipt+aggregateReceipt domain dispositions siteInputBits clipReceipt regularReceipt maximumCellConstraints =+ let inputBits = max siteInputBits (exactClipInputCoordinateBits clipReceipt)+ peakBits = exactClipPeakIntermediateCoordinateBits clipReceipt+ finalBits = exactClipFinalCoordinateBits clipReceipt+ dispositionCounts = countPowerDispositions dispositions+ in PowerDiagramReceipt+ { powerDiagramDomainVertices = NonEmpty.length (convexPolygonPoints domain)+ , powerDiagramSubmittedSiteConstraints = exactClipSubmittedHalfPlanes clipReceipt+ , powerDiagramActiveBoundaries = exactClipActiveBoundaries clipReceipt+ , powerDiagramBoundaryCompatibilityChecks = exactClipBoundaryCompatibilityChecks clipReceipt+ , powerDiagramExactIntersections = exactClipExactIntersections clipReceipt+ , powerDiagramPublishedCells = countedPublishedCells dispositionCounts+ , powerDiagramLowerDimensionalCells = countedLowerDimensionalCells dispositionCounts+ , powerDiagramEmptyCells = countedEmptyCells dispositionCounts+ , powerDiagramCoincidentEquivalentCells = countedCoincidentEquivalentCells dispositionCounts+ , powerDiagramCoincidentDominatedCells = countedCoincidentDominatedCells dispositionCounts+ , powerDiagramRegularFaces = generatorRegularFaceCount regularReceipt+ , powerDiagramRegularEdges = generatorRegularEdgeCount regularReceipt+ , powerDiagramOracleCells = generatorRegularLowerDimensionalSites regularReceipt+ , powerDiagramMaximumCellConstraints = maximumCellConstraints+ , powerDiagramMaximumInputBits = inputBits+ , powerDiagramMaximumAffineCoefficientBits = exactClipMaximumAffineCoefficientBits clipReceipt+ , powerDiagramPeakIntermediateCoordinateBits = peakBits+ , powerDiagramFinalCoordinateBits = finalBits+ , powerDiagramFinalDenominatorBits = exactClipFinalDenominatorBits clipReceipt+ }++data PowerDispositionCounts = PowerDispositionCounts+ { countedPublishedCells :: !Int+ , countedLowerDimensionalCells :: !Int+ , countedEmptyCells :: !Int+ , countedCoincidentEquivalentCells :: !Int+ , countedCoincidentDominatedCells :: !Int+ }++countPowerDispositions+ :: Map label (PowerCellDisposition label)+ -> PowerDispositionCounts+countPowerDispositions =+ Map.foldl'+ (\counts disposition -> case disposition of+ PublishedPowerCell _ ->+ counts {countedPublishedCells = countedPublishedCells counts + 1}+ LowerDimensionalPowerCell _ ->+ counts {countedLowerDimensionalCells = countedLowerDimensionalCells counts + 1}+ EmptyPowerCell ->+ counts {countedEmptyCells = countedEmptyCells counts + 1}+ CoincidentEquivalentTo _ ->+ counts {countedCoincidentEquivalentCells = countedCoincidentEquivalentCells counts + 1}+ CoincidentDominatedBy _ ->+ counts {countedCoincidentDominatedCells = countedCoincidentDominatedCells counts + 1})+ PowerDispositionCounts+ { countedPublishedCells = 0+ , countedLowerDimensionalCells = 0+ , countedEmptyCells = 0+ , countedCoincidentEquivalentCells = 0+ , countedCoincidentDominatedCells = 0+ }
test/algebra/Main.hs view
@@ -1,7 +1,6 @@ module Main (main) where import qualified Moonlight.Triangulation.AlgebraSpec as AlgebraSpec-import qualified Moonlight.Triangulation.MinkowskiSpec as MinkowskiSpec import qualified Moonlight.Triangulation.RegionAlgebraSpec as RegionAlgebraSpec import qualified Moonlight.Triangulation.ScheduleAgreementSpec as ScheduleAgreementSpec import qualified Moonlight.Triangulation.ValuationSpec as ValuationSpec@@ -11,5 +10,4 @@ AlgebraSpec.tests RegionAlgebraSpec.tests ValuationSpec.tests- MinkowskiSpec.tests ScheduleAgreementSpec.tests
+ test/algebra/Moonlight/Triangulation/ExactClipRetentionSpec.hs view
@@ -0,0 +1,536 @@+-- | Independent endpoint-reconstruction oracle for the retained-line clipper.+--+-- The oracle is deliberately test-only. Production has one clipping authority:+-- 'exactClipRetainedPolygon'.+module Moonlight.Triangulation.ExactClipRetentionSpec (tests) where++import Control.Monad (foldM, unless)+import Data.Foldable (traverse_)+import qualified Data.List as List+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NonEmpty+import Data.Maybe (maybeToList)+import Moonlight.Triangulation.Exact+ ( ExactAffineLine+ , ExactClipDisposition (..)+ , ExactClipReceipt (..)+ , ExactClosedHalfPlane+ , ExactHalfPlaneError (..)+ , ExactIntersectionError+ , ExactPoint+ , ExactRetainedPolygon+ , SegmentRelation (..)+ , classifyExactPoint+ , exactAffineLine+ , exactAffineLineCoefficients+ , exactAffineLineIntersection+ , exactClipRetainedPolygon+ , exactClosedHalfPlane+ , exactClosedHalfPlaneFromDirectedEdge+ , exactClosedHalfPlaneLine+ , exactOrient2d+ , exactPoint+ , exactPointCoordinates+ , exactRetainedPolygon+ , exactRetainedPolygonPoints+ )+import Moonlight.Triangulation.Internal.BoundaryCycle+ ( cyclePairsNonEmpty+ , cyclicTriples+ )+import Moonlight.Triangulation.Internal.ExactRational+ ( ExactRational+ , exactRational+ , exactRationalBitWidth+ , exactRationalDenominatorBitWidth+ )+import Support (assertEqual, integerPoint, requireRight)++tests :: IO ()+tests = do+ testAffineIntersectionCrossProducts+ testSelfIntersectingAllLeftCycleRefused+ testRetainedClosingLineSurvivesSecondClip+ testAngularHalfPlanePermutation+ testClosedDimensionalDispositions+ initialPolygon <-+ requireRight+ "retained-line fixture polygon"+ ( exactRetainedPolygon+ ( integerPoint (-1000) (-1000)+ :| [ integerPoint 1000 (-1000)+ , integerPoint 1000 1000+ , integerPoint (-1000) 1000+ ]+ )+ )+ halfPlanes <- traverse fixtureHalfPlane fixtureCoefficients+ (retainedDisposition, retainedReceipt) <-+ requireRight+ "retained-line fixture clipping"+ (exactClipRetainedPolygon initialPolygon halfPlanes)+ retainedPoints <-+ case retainedDisposition of+ ExactClipFullDimensional polygon -> pure (exactRetainedPolygonPoints polygon)+ other -> fail ("retained-line fixture lost full dimension: " <> show other)+ (endpointPoints, endpointReceipt) <-+ requireRight+ "endpoint-reconstruction oracle"+ (endpointClipSequence (exactRetainedPolygonPoints initialPolygon) halfPlanes)++ assertExactPointSet "final exact polygon" retainedPoints endpointPoints+ assertEqual+ "final reduced denominator width is representation invariant"+ (exactClipFinalDenominatorBits retainedReceipt)+ (endpointFinalDenominatorBits endpointReceipt)+ assertEqual "measured retained coefficient width" 21 (exactClipMaximumAffineCoefficientBits retainedReceipt)+ assertEqual "measured endpoint coefficient width" 32 (endpointPeakAffineCoefficientBits endpointReceipt)+ unless+ ( endpointPeakAffineCoefficientBits endpointReceipt+ > exactClipMaximumAffineCoefficientBits retainedReceipt+ )+ ( fail+ "endpoint reconstruction did not exhibit strictly larger affine coefficients"+ )++ -- Equality is checked at every local section, not merely after global gluing.+ -- This is the reason no honest coordinate-width counterexample can exist.+ traverse_+ (assertPrefixAgreement initialPolygon)+ (List.inits halfPlanes)+ putStrLn "exact retained-line clipping: ok (source coefficients 21 bits; endpoint oracle 32 bits)"++testAffineIntersectionCrossProducts :: IO ()+testAffineIntersectionCrossProducts = do+ oneHalf <- rationalCoefficient 1 2+ oneThird <- rationalCoefficient 1 3+ twoFifths <- rationalCoefficient 2 5+ negativeOneSeventh <- rationalCoefficient (-1) 7+ negativeOne <- rationalCoefficient (-1) 1+ oneEleventh <- rationalCoefficient 1 11+ firstLine <-+ requireRight+ "first rational intersection line"+ (exactAffineLine oneHalf oneThird negativeOne)+ secondLine <-+ requireRight+ "second rational intersection line"+ (exactAffineLine twoFifths negativeOneSeventh oneEleventh)+ intersection <-+ requireRight+ "homogeneous integer affine intersection"+ (exactAffineLineIntersection firstLine secondLine)+ expectedX <- requireRight "expected rational x" (exactRational 260 473)+ expectedY <- requireRight "expected rational y" (exactRational 1029 473)+ assertEqual+ "homogeneous integer intersection agrees with rational Cramer's rule"+ (exactPoint expectedX expectedY)+ intersection+ assertEqual+ "intersection remains on the first source line"+ EQ+ (classifyExactPoint (exactClosedHalfPlane firstLine) intersection)+ assertEqual+ "intersection remains on the second source line"+ EQ+ (classifyExactPoint (exactClosedHalfPlane secondLine) intersection)+ where+ rationalCoefficient :: Integer -> Integer -> IO ExactRational+ rationalCoefficient numerator denominator =+ requireRight+ "rational intersection coefficient"+ (exactRational numerator denominator)++testAngularHalfPlanePermutation :: IO ()+testAngularHalfPlanePermutation = do+ square <- retainedSquare+ halfPlanes <-+ traverse+ fixtureHalfPlane+ [ (1, 0, -2)+ , (2, 0, -4)+ , (-1, 0, 8)+ , (0, 1, -1)+ , (0, -1, 9)+ ]+ (expected, expectedReceipt) <-+ requireRight+ "angular half-plane canonical result"+ (exactClipRetainedPolygon square halfPlanes)+ case expected of+ ExactClipFullDimensional polygon ->+ assertExactPointSet+ "parallel descent retains the strongest boundaries"+ ( integerPoint 2 1+ :| [integerPoint 8 1, integerPoint 8 9, integerPoint 2 9]+ )+ (exactRetainedPolygonPoints polygon)+ other -> fail ("expected a full-dimensional canonical section, got " <> show other)+ traverse_+ (\permutation -> do+ (actual, receipt) <-+ requireRight+ "permuted angular half-plane intersection"+ (exactClipRetainedPolygon square permutation)+ assertEqual "angular half-plane permutation" expected actual+ assertEqual+ "submitted half-plane count"+ (length halfPlanes)+ (exactClipSubmittedHalfPlanes receipt))+ (List.permutations halfPlanes)+ unless (exactClipBoundaryCompatibilityChecks expectedReceipt > 0) $+ fail "angular descent reported no boundary compatibility work"++testClosedDimensionalDispositions :: IO ()+testClosedDimensionalDispositions = do+ square <- retainedSquare+ xAtOne <- traverse fixtureHalfPlane [(1, 0, -1), (-1, 0, 1)]+ (segmentDisposition, _) <-+ requireRight+ "closed segment half-plane intersection"+ (exactClipRetainedPolygon square xAtOne)+ case segmentDisposition of+ ExactClipLowerDimensional points ->+ assertEqual+ "opposing closed half-planes retain their shared segment"+ (List.sort [integerPoint 1 0, integerPoint 1 10])+ (List.sort (NonEmpty.toList points))+ other -> fail ("expected a closed segment, got " <> show other)++ yAtThree <- traverse fixtureHalfPlane [(0, 1, -3), (0, -1, 3)]+ (pointDisposition, _) <-+ requireRight+ "closed point half-plane intersection"+ (exactClipRetainedPolygon square (xAtOne <> yAtThree))+ case pointDisposition of+ ExactClipLowerDimensional points ->+ assertEqual+ "two zero-width closed sections retain their shared point"+ [integerPoint 1 3]+ (List.sort (NonEmpty.toList points))+ other -> fail ("expected a closed point, got " <> show other)++ incompatible <- traverse fixtureHalfPlane [(1, 0, -6), (-1, 0, 5)]+ (emptyDisposition, _) <-+ requireRight+ "incompatible closed half-plane intersection"+ (exactClipRetainedPolygon square incompatible)+ assertEqual "separated opposing boundaries are empty" ExactClipEmpty emptyDisposition++retainedSquare :: IO ExactRetainedPolygon+retainedSquare =+ requireRight+ "retained square"+ ( exactRetainedPolygon+ ( integerPoint 0 0+ :| [integerPoint 10 0, integerPoint 10 10, integerPoint 0 10]+ )+ )++testSelfIntersectingAllLeftCycleRefused :: IO ()+testSelfIntersectingAllLeftCycleRefused = do+ let points =+ integerPoint 0 0+ :| [ integerPoint 5 3+ , integerPoint (-1) 3+ , integerPoint 4 0+ , integerPoint 2 5+ ]+ turns =+ fmap+ (\(previous, current, next) -> exactOrient2d previous current next)+ (cyclicTriples (NonEmpty.toList points))+ assertEqual "self-intersection fixture has only local left turns" (replicate 5 GT) turns+ case exactRetainedPolygon points of+ Left (ExactRetainedPolygonSelfRelation 0 2 SegmentsProperlyCross) -> pure ()+ other ->+ fail+ ( "self-intersecting all-left retained polygon: expected edge 0/2 crossing, got "+ <> show other+ )++-- The first clip emits its first point twice. The closing occurrence carries+-- the source line of the retained bottom edge; preserving the opening record+-- instead corrupts that edge into the square's stale right boundary and makes+-- the second clip spuriously report parallel lines.+testRetainedClosingLineSurvivesSecondClip :: IO ()+testRetainedClosingLineSurvivesSecondClip = do+ square <-+ requireRight+ "retained square"+ ( exactRetainedPolygon+ ( integerPoint 0 0+ :| [ integerPoint 10 0+ , integerPoint 10 10+ , integerPoint 0 10+ ]+ )+ )+ diagonalLine <- requireRight "diagonal clipping line" (exactAffineLine (-1) (-1) 10)+ verticalLine <- requireRight "vertical clipping line" (exactAffineLine (-1) 0 5)+ let diagonalHalfPlane = exactClosedHalfPlane diagonalLine+ verticalHalfPlane = exactClosedHalfPlane verticalLine+ retainedHalfPlanes = diagonalHalfPlane :| [verticalHalfPlane]+ (diagonalDisposition, _) <-+ requireRight+ "retained square diagonal clip"+ (exactClipRetainedPolygon square [diagonalHalfPlane])+ diagonalPolygon <-+ requireFullDimensionalClip+ "retained square diagonal clip"+ diagonalDisposition+ assertExactPointSet+ "first clip retains the expected triangle"+ (integerPoint 0 0 :| [integerPoint 10 0, integerPoint 0 10])+ (exactRetainedPolygonPoints diagonalPolygon)+ (sequentialDisposition, _) <-+ requireRight+ "second clip over the retained first result"+ (exactClipRetainedPolygon diagonalPolygon [verticalHalfPlane])+ sequentialPolygon <-+ requireFullDimensionalClip+ "second clip over the retained first result"+ sequentialDisposition+ (combinedDisposition, combinedReceipt) <-+ requireRight+ "combined retained two-clip descent"+ (exactClipRetainedPolygon square (NonEmpty.toList retainedHalfPlanes))+ combinedPolygon <-+ requireFullDimensionalClip+ "combined retained two-clip descent"+ combinedDisposition+ let expectedFinalPoints =+ integerPoint 0 0+ :| [integerPoint 5 0, integerPoint 5 5, integerPoint 0 10]+ assertExactPointSet+ "second clip intersects the retained bottom and diagonal source lines"+ expectedFinalPoints+ (exactRetainedPolygonPoints sequentialPolygon)+ assertEqual+ "sequential and combined clipping preserve the same retained polygon"+ sequentialPolygon+ combinedPolygon+ assertEqual+ "combined clipping records both submitted half-planes"+ 2+ (exactClipSubmittedHalfPlanes combinedReceipt)+ assertRetainedVerticesInside+ retainedHalfPlanes+ (exactRetainedPolygonPoints combinedPolygon)++requireFullDimensionalClip+ :: String+ -> ExactClipDisposition+ -> IO ExactRetainedPolygon+requireFullDimensionalClip _ (ExactClipFullDimensional polygon) = pure polygon+requireFullDimensionalClip label disposition =+ fail (label <> ": expected a full-dimensional polygon, got " <> show disposition)++assertExactPointSet+ :: String+ -> NonEmpty ExactPoint+ -> NonEmpty ExactPoint+ -> IO ()+assertExactPointSet label expected actual =+ assertEqual+ label+ (List.sort (NonEmpty.toList expected))+ (List.sort (NonEmpty.toList actual))++assertRetainedVerticesInside+ :: NonEmpty ExactClosedHalfPlane+ -> NonEmpty ExactPoint+ -> IO ()+assertRetainedVerticesInside halfPlanes points =+ traverse_+ (\halfPlane ->+ traverse_+ (\point ->+ unless (classifyExactPoint halfPlane point /= LT) $+ fail ("retained vertex violates clipping half-plane: " <> show point))+ points)+ halfPlanes++data EndpointOracleError+ = EndpointOracleHalfPlane !ExactHalfPlaneError+ | EndpointOracleIntersection !ExactIntersectionError+ | EndpointOracleLostFullDimension ![ExactPoint]+ deriving stock (Eq, Show)++data EndpointOracleReceipt = EndpointOracleReceipt+ { endpointPeakAffineCoefficientBits :: !Int+ , endpointPeakCoordinateBits :: !Int+ , endpointFinalDenominatorBits :: !Int+ }+ deriving stock (Eq, Show)++data EndpointEdgeClip = EndpointEdgeClip+ { endpointEdgePoints :: ![ExactPoint]+ , endpointEdgeReconstructedCoefficientBits :: !(Maybe Int)+ }+ deriving stock (Eq, Show)++endpointClipSequence+ :: NonEmpty ExactPoint+ -> [ExactClosedHalfPlane]+ -> Either EndpointOracleError (NonEmpty ExactPoint, EndpointOracleReceipt)+endpointClipSequence initialPoints halfPlanes = do+ let initialReceipt =+ EndpointOracleReceipt+ { endpointPeakAffineCoefficientBits = 0+ , endpointPeakCoordinateBits = pointCycleBitWidth initialPoints+ , endpointFinalDenominatorBits = pointCycleDenominatorBitWidth initialPoints+ }+ (finalPoints, accumulatedReceipt) <-+ foldM endpointClipStep (initialPoints, initialReceipt) halfPlanes+ pure+ ( finalPoints+ , accumulatedReceipt+ { endpointFinalDenominatorBits =+ pointCycleDenominatorBitWidth finalPoints+ }+ )++endpointClipStep+ :: (NonEmpty ExactPoint, EndpointOracleReceipt)+ -> ExactClosedHalfPlane+ -> Either EndpointOracleError (NonEmpty ExactPoint, EndpointOracleReceipt)+endpointClipStep (points, receipt) halfPlane = do+ clippedEdges <-+ traverse (clipEndpointEdge halfPlane) (cyclePairsNonEmpty points)+ let clippedPoints = concatMap endpointEdgePoints clippedEdges+ stepCoefficientBits =+ maximumOrZero+ ( foldMap+ (maybeToList . endpointEdgeReconstructedCoefficientBits)+ clippedEdges+ )+ nextPoints <-+ maybe+ (Left (EndpointOracleLostFullDimension clippedPoints))+ Right+ (NonEmpty.nonEmpty clippedPoints)+ if NonEmpty.length nextPoints < 3+ then Left (EndpointOracleLostFullDimension clippedPoints)+ else+ Right+ ( nextPoints+ , receipt+ { endpointPeakAffineCoefficientBits =+ max+ (endpointPeakAffineCoefficientBits receipt)+ stepCoefficientBits+ , endpointPeakCoordinateBits =+ max+ (endpointPeakCoordinateBits receipt)+ (pointCycleBitWidth nextPoints)+ }+ )++clipEndpointEdge+ :: ExactClosedHalfPlane+ -> (ExactPoint, ExactPoint)+ -> Either EndpointOracleError EndpointEdgeClip+clipEndpointEdge halfPlane (from, to) =+ let fromInside = classifyExactPoint halfPlane from /= LT+ toInside = classifyExactPoint halfPlane to /= LT+ crossing = do+ reconstructedBoundary <-+ either+ (Left . EndpointOracleHalfPlane)+ (Right . exactClosedHalfPlaneLine)+ (exactClosedHalfPlaneFromDirectedEdge from to)+ point <-+ either+ (Left . EndpointOracleIntersection)+ Right+ ( exactAffineLineIntersection+ reconstructedBoundary+ (exactClosedHalfPlaneLine halfPlane)+ )+ pure (point, affineLineBitWidth reconstructedBoundary)+ in case (fromInside, toInside) of+ (True, True) -> Right (EndpointEdgeClip [to] Nothing)+ (True, False) ->+ (\(point, coefficientBits) ->+ EndpointEdgeClip [point] (Just coefficientBits))+ <$> crossing+ (False, True) ->+ (\(point, coefficientBits) ->+ EndpointEdgeClip [point, to] (Just coefficientBits))+ <$> crossing+ (False, False) -> Right (EndpointEdgeClip [] Nothing)++assertPrefixAgreement+ :: ExactRetainedPolygon+ -> [ExactClosedHalfPlane]+ -> IO ()+assertPrefixAgreement initialPolygon prefix = do+ (retainedDisposition, retainedReceipt) <-+ requireRight+ "retained-line prefix"+ (exactClipRetainedPolygon initialPolygon prefix)+ retainedPoints <-+ case retainedDisposition of+ ExactClipFullDimensional polygon -> pure (exactRetainedPolygonPoints polygon)+ other -> fail ("retained-line prefix lost full dimension: " <> show other)+ (endpointPoints, endpointReceipt) <-+ requireRight+ "endpoint prefix"+ (endpointClipSequence (exactRetainedPolygonPoints initialPolygon) prefix)+ assertExactPointSet "prefix exact polygon" retainedPoints endpointPoints+ assertEqual+ "prefix denominator width"+ (exactClipFinalDenominatorBits retainedReceipt)+ (endpointFinalDenominatorBits endpointReceipt)++fixtureHalfPlane+ :: (Integer, Integer, Integer)+ -> IO ExactClosedHalfPlane+fixtureHalfPlane (coefficientX, coefficientY, constant) =+ exactClosedHalfPlane+ <$> requireRight+ "fixture affine half-plane"+ ( exactAffineLine+ (fromInteger coefficientX)+ (fromInteger coefficientY)+ (fromInteger constant)+ )++fixtureCoefficients :: [(Integer, Integer, Integer)]+fixtureCoefficients =+ [ (1, 2, 1300)+ , (-3, 5, 1700)+ , (-7, -2, 1900)+ , (4, -9, 2200)+ , (11, 3, 1800)+ , (-5, 13, 2100)+ , (-17, -4, 2300)+ , (6, -19, 2400)+ , (23, 7, 2500)+ , (-8, 29, 2600)+ ]++affineLineBitWidth :: ExactAffineLine -> Int+affineLineBitWidth line =+ let (coefficientX, coefficientY, constant) = exactAffineLineCoefficients line+ in maximumOrZero (fmap exactRationalBitWidth [coefficientX, coefficientY, constant])++pointCycleBitWidth :: NonEmpty ExactPoint -> Int+pointCycleBitWidth =+ maximumOrZero+ . concatMap+ (\point ->+ let (x, y) = exactPointCoordinates point+ in fmap exactRationalBitWidth [x, y])++pointCycleDenominatorBitWidth :: NonEmpty ExactPoint -> Int+pointCycleDenominatorBitWidth =+ maximumOrZero+ . concatMap+ (\point ->+ let (x, y) = exactPointCoordinates point+ in fmap exactRationalDenominatorBitWidth [x, y])++maximumOrZero :: Foldable collection => collection Int -> Int+maximumOrZero = List.foldl' max 0
+ test/algebra/Moonlight/Triangulation/LayerOperationsSpec.hs view
@@ -0,0 +1,275 @@+-- | Exact affine-envelope and compositional layer laws through the public+-- facade. Every comparison is against an independently composed public view.+module Moonlight.Triangulation.LayerOperationsSpec (tests) where++import Control.Monad (unless)+import Data.Foldable (traverse_)+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NonEmpty+import qualified Data.Map.Strict as Map+import Moonlight.Triangulation+ ( AffineForm (..)+ , CoverGap+ , ExactPoint+ , ExactRational+ , LayerCoverageError (..)+ , PlanarLayer+ , PlanarRegion+ , PolygonComponent+ , RegionPublicationError (RegionUnboundedSelection)+ , UpperEnvelopeError (UpperEnvelopeEmptyForms)+ , coverGapRegion+ , exactAreaValue+ , exactLoop+ , exactLoopPoints+ , exactPoint+ , exactPointCoordinates+ , layerCovers+ , overlayAll+ , overlayConfusion+ , overlayLayers+ , overlayMass+ , overlaySelectedRegion+ , planarLayer+ , planarLayerLabelAt+ , planarLayerRegions+ , planarRegion+ , planarRegionComponents+ , polygonComponent+ , polygonHoleLoops+ , polygonOuterLoop+ , regionValuations+ , upperEnvelope+ , valuationArea+ )+import Moonlight.Triangulation.AlgebraFixtures+ ( insideLayer+ , rectangleComponent+ , rectangleRegion+ )+import Support (assertEqual, integerPoint, requireRight)++tests :: IO ()+tests = do+ testConvexUpperEnvelope+ testNonconvexAndHoledUpperEnvelope+ testEmptyUpperEnvelope+ testLayerCoverage+ testNaryOverlayLabels+ testOverlayMassAndConfusion+ putStrLn "layer operations: ok"++testConvexUpperEnvelope :: IO ()+testConvexUpperEnvelope = do+ window <- rectangleComponent 0 0 10 10+ let forms =+ Map.fromList+ [ (0 :: Int, AffineForm 0 (-1) 0)+ , (1, AffineForm 0 (-1) 0)+ , (2, AffineForm (-10) 1 0)+ , (3, AffineForm (-11) 1 0)+ ]+ layer <- requireRight "convex exact upper envelope" (upperEnvelope window forms)+ reordered <-+ requireRight+ "upper envelope is independent of map construction order"+ (upperEnvelope window (Map.fromList (reverse (Map.toList forms))))+ assertEqual "upper envelope map-order invariance" layer reordered+ assertEqual "identical and dominated forms own no cell" [Just 0, Just 2] (Map.keys (planarLayerRegions layer))+ assertEnvelopeDominance forms layer+ assertEqual "left affine winner" (Just 0) (planarLayerLabelAt layer (exactPoint 2 5))+ assertEqual "right affine winner" (Just 2) (planarLayerLabelAt layer (exactPoint 8 5))+ assertEqual "outside finite window" Nothing (planarLayerLabelAt layer (exactPoint (-1) 5))+ requireCoverage "upper envelope covers its convex window" layer window++testNonconvexAndHoledUpperEnvelope :: IO ()+testNonconvexAndHoledUpperEnvelope = do+ nonconvex <- lShapedComponent+ holed <- holedComponent+ let constantForm = Map.singleton (7 :: Int) (AffineForm 1 0 0)+ assertEnvelopeEqualsWindow "nonconvex envelope restriction" nonconvex constantForm+ assertEnvelopeEqualsWindow "holed envelope restriction" holed constantForm++testEmptyUpperEnvelope :: IO ()+testEmptyUpperEnvelope = do+ window <- rectangleComponent 0 0 1 1+ case upperEnvelope window (Map.empty :: Map.Map Int AffineForm) of+ Left UpperEnvelopeEmptyForms -> pure ()+ result -> fail ("empty upper envelope: unexpected result " <> show result)++testLayerCoverage :: IO ()+testLayerCoverage = do+ window <- rectangleComponent 0 0 10 10+ half <- rectangleRegion 0 0 5 10+ incomplete <- insideLayer half+ case layerCovers incomplete window of+ Left (LayerCoverageGap gap) -> assertGapArea 50 gap+ result -> fail ("incomplete layer coverage: unexpected result " <> show result)+ completeRegion <- requireRight "complete coverage region" (planarRegion [window])+ complete <- requireRight "complete coverage layer" (planarLayer False (Map.singleton True completeRegion))+ requireCoverage "complete layer" complete window++testNaryOverlayLabels :: IO ()+testNaryOverlayLabels = do+ vertical <- rectangleRegion 0 0 6 10 >>= insideLayer+ horizontal <- rectangleRegion 0 0 10 6 >>= insideLayer+ central <- rectangleRegion 2 2 8 8 >>= insideLayer+ let sourceLayers = vertical :| [horizontal, central]+ refined <- requireRight "balanced n-ary overlay" (overlayAll sourceLayers)+ assertNaryLabel sourceLayers refined (exactPoint 3 3)+ assertNaryLabel sourceLayers refined (exactPoint 7 3)+ assertNaryLabel sourceLayers refined (exactPoint 9 9)+ assertNaryLabel sourceLayers refined (exactPoint (-1) (-1))+ singleton <- requireRight "singleton n-ary overlay" (overlayAll (vertical :| []))+ assertNaryLabel (vertical :| []) singleton (exactPoint 3 3)++testOverlayMassAndConfusion :: IO ()+testOverlayMassAndConfusion = do+ left <- rectangleRegion 0 0 6 10 >>= insideLayer+ right <- rectangleRegion 4 0 10 10 >>= insideLayer+ result <- requireRight "mass overlay" (overlayLayers left right)+ direct <- requireRight "direct overlap mass" (overlayMass (== (True, True)) result)+ published <-+ requireRight+ "published overlap region"+ (overlaySelectedRegion (== (True, True)) result)+ publishedValues <- requireRight "published overlap valuation" (regionValuations published)+ assertEqual+ "direct mass equals published-region valuation"+ (exactAreaValue (valuationArea publishedValues))+ (exactAreaValue direct)+ assertEqual "overlap mass" 20 (exactAreaValue direct)+ case overlayMass (== (False, False)) result of+ Left RegionUnboundedSelection -> pure ()+ value -> fail ("unbounded overlay mass: unexpected result " <> show value)+ assertEqual+ "one-pass finite confusion masses"+ ( Map.fromList+ [ ((False, True), 40)+ , ((True, False), 40)+ , ((True, True), 20)+ ]+ )+ (fmap exactAreaValue (overlayConfusion result))+ traverse_+ (\(labels, expected) -> do+ measured <- requireRight "direct confusion-cell mass" (overlayMass (== labels) result)+ assertEqual "confusion entry equals direct mass" expected (exactAreaValue measured))+ (Map.toAscList (fmap exactAreaValue (overlayConfusion result)))++assertEnvelopeDominance+ :: Map.Map Int AffineForm+ -> PlanarLayer (Maybe Int)+ -> IO ()+assertEnvelopeDominance forms layer =+ traverse_ checkRegion (Map.toAscList (planarLayerRegions layer))+ where+ checkRegion :: (Maybe Int, PlanarRegion) -> IO ()+ checkRegion (Nothing, _) = fail "bounded upper-envelope region has the outside label"+ checkRegion (Just winner, region) =+ case Map.lookup winner forms of+ Nothing -> fail ("upper-envelope winner is absent from its input map: " <> show winner)+ Just winnerForm ->+ traverse_+ (checkWinnerAtPoint winner winnerForm)+ (concatMap componentBoundaryPoints (planarRegionComponents region))++ checkWinnerAtPoint+ :: Int+ -> AffineForm+ -> ExactPoint+ -> IO ()+ checkWinnerAtPoint winner winnerForm point =+ let winnerValue = affineValue winnerForm point+ in traverse_+ (\(candidate, candidateForm) ->+ unless (winnerValue >= affineValue candidateForm point) $+ fail+ ( "upper-envelope dominance failed: winner "+ <> show winner+ <> ", candidate "+ <> show candidate+ ))+ (Map.toAscList forms)++componentBoundaryPoints :: PolygonComponent -> [ExactPoint]+componentBoundaryPoints component =+ NonEmpty.toList (exactLoopPoints (polygonOuterLoop component))+ <> concatMap (NonEmpty.toList . exactLoopPoints) (polygonHoleLoops component)++affineValue+ :: AffineForm+ -> ExactPoint+ -> ExactRational+affineValue form point =+ let (coordinateX, coordinateY) = exactPointCoordinates point+ in affineFormConstant form+ + affineFormXCoefficient form * coordinateX+ + affineFormYCoefficient form * coordinateY++assertEnvelopeEqualsWindow+ :: String+ -> PolygonComponent+ -> Map.Map Int AffineForm+ -> IO ()+assertEnvelopeEqualsWindow label window forms = do+ expected <- requireRight (label <> " expected region") (planarRegion [window])+ layer <- requireRight label (upperEnvelope window forms)+ assertEqual label (Just expected) (Map.lookup (Just 7) (planarLayerRegions layer))+ requireCoverage (label <> " coverage") layer window++assertGapArea :: Integer -> CoverGap -> IO ()+assertGapArea expected gap = do+ values <- requireRight "gap valuation" (regionValuations (coverGapRegion gap))+ assertEqual "coverage obstruction retains exact gap" (fromInteger expected) (exactAreaValue (valuationArea values))++assertNaryLabel+ :: NonEmpty (PlanarLayer Bool)+ -> PlanarLayer (NonEmpty Bool)+ -> ExactPoint+ -> IO ()+assertNaryLabel sourceLayers refined point =+ assertEqual+ "n-ary label tuple follows source order"+ (fmap (`planarLayerLabelAt` point) sourceLayers)+ (planarLayerLabelAt refined point)++requireCoverage+ :: (Ord label, Show label)+ => String+ -> PlanarLayer label+ -> PolygonComponent+ -> IO ()+requireCoverage label layer window =+ case layerCovers layer window of+ Right () -> pure ()+ Left failure -> fail (label <> ": " <> show failure)++lShapedComponent :: IO PolygonComponent+lShapedComponent = do+ outer <-+ requireRight+ "L-shaped outer loop"+ ( exactLoop+ ( integerPoint 0 0+ :| [ integerPoint 4 0+ , integerPoint 4 1+ , integerPoint 1 1+ , integerPoint 1 4+ , integerPoint 0 4+ ]+ )+ )+ requireRight "L-shaped component" (polygonComponent outer [])++holedComponent :: IO PolygonComponent+holedComponent = do+ outer <-+ requireRight+ "holed outer loop"+ (exactLoop (integerPoint 0 0 :| [integerPoint 6 0, integerPoint 6 6, integerPoint 0 6]))+ hole <-+ requireRight+ "clockwise hole loop"+ (exactLoop (integerPoint 2 2 :| [integerPoint 2 4, integerPoint 4 4, integerPoint 4 2]))+ requireRight "holed component" (polygonComponent outer [hole])
test/algebra/Moonlight/Triangulation/MinkowskiSpec.hs view
@@ -18,13 +18,16 @@ , closeWith , convexMinkowskiSum , convexPolygon+ , convexPolygonPoints , erodeBy , minkowskiGeneratedPieces+ , minkowskiExactCrossings , minkowskiOperation , minkowskiOverlayPasses , minkowskiSum , openWith , polygonOffset+ , reflectConvexPolygon , structuringElement ) import Moonlight.Triangulation.Region@@ -45,6 +48,7 @@ tests :: IO () tests = do testConvexConvolution+ testConvexReflection testGeneralAddition testOffsetClosure testConvexMorphology@@ -52,6 +56,23 @@ testHoledAndNeckedErosion putStrLn "minkowski: ok" +testConvexReflection :: IO ()+testConvexReflection = do+ triangle <-+ requireRight+ "asymmetric convex triangle"+ (convexPolygon (exactPoint 0 0 :| [exactPoint 3 0, exactPoint 1 2]))+ let reflected = reflectConvexPolygon triangle+ readmitted <-+ requireRight+ "reflected convex polygon preserves counter-clockwise orientation"+ (convexPolygon (convexPolygonPoints reflected))+ assertEqual "reflected carrier remains admissible" reflected readmitted+ assertEqual+ "convex reflection is involutive"+ triangle+ (reflectConvexPolygon reflected)+ testConvexConvolution :: IO () testConvexConvolution = do leftComponent <- rectangleComponent 0 0 1 1@@ -183,6 +204,10 @@ expectedErosion <- rectangleRegion (-1) (-1) 1 1 assertEqual "convex support-half-plane erosion" expectedErosion eroded assertEqual "convex erosion needs no overlay" 0 (minkowskiOverlayPasses erosionReceipt)+ assertEqual+ "convex erosion reports exact HPI line intersections"+ 4+ (minkowskiExactCrossings erosionReceipt) exactFitSource <- rectangleRegion (-1) (-1) 1 1 (lowerDimensionalResidual, _) <- requireRight "exact-fit regularized erosion" (erodeBy element exactFitSource)
+ test/algebra/Moonlight/Triangulation/PowerDiagramSpec.hs view
@@ -0,0 +1,734 @@+-- | Exact bounded power-cell laws through the public owner.+module Moonlight.Triangulation.PowerDiagramSpec (tests) where++import Control.Monad (unless)+import Data.Foldable (traverse_)+import qualified Data.List as List+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NonEmpty+import qualified Data.Map.Strict as Map+import qualified Data.Set as Set+import Moonlight.Triangulation+ ( ExactPoint+ , ExactRational+ , ExactVector (..)+ , BoundedPowerDiagram+ , ConvexPolygon+ , Point (..)+ , PowerCellDisposition (..)+ , PowerDualEdge (..)+ , PowerDiagramError (..)+ , PowerSite+ , PowerWeight+ , PowerWeightError (..)+ , RegularSiteDisposition (..)+ , RegularEdge+ , boundedPowerDiagram+ , convexPolygon+ , convexPolygonPoints+ , exactPointCoordinates+ , exactPointFromPoint+ , exactRayDirection+ , exactRayOrigin+ , exactSegmentEndpoints+ , planarLayerRegions+ , powerCellDisposition+ , powerCellDispositions+ , powerDiagramActiveBoundaries+ , powerDiagramCoincidentEquivalentCells+ , powerDiagramCoincidentDominatedCells+ , powerDiagramEmptyCells+ , powerDiagramInputSites+ , powerDiagramLowerDimensionalCells+ , powerDiagramPlanarLayer+ , powerDiagramPublishedCells+ , powerDiagramOracleCells+ , powerDiagramMaximumCellConstraints+ , powerDiagramRegularEdges+ , powerDiagramSubmittedSiteConstraints+ , powerSite+ , powerSiteLabel+ , powerSitePosition+ , powerSiteWeight+ , powerWeight+ , powerWeightExact+ , regularEdgeDual+ , regularEdgeLabels+ , regularEdges+ , regularFaceDualPoint+ , regularFaces+ , regularNeighbours+ , regularSiteDisposition+ , regularTriangulation+ , regularTriangulationEdges+ , regularTriangulationFaces+ , regularTriangulationVisibleSites+ , translateExactPoint+ )+import Moonlight.Triangulation.Exact+ ( ExactClipDisposition (..)+ , ExactClosedHalfPlane+ , exactAffineLine+ , exactClipRetainedPolygon+ , exactClosedHalfPlane+ , exactRetainedPolygon+ , exactRetainedPolygonPoints+ )+import Support (assertEqual, integerPoint, requireRight)++tests :: IO ()+tests = do+ testEqualWeightsAndCommonShift+ testThreeSiteExactPartitionCoverage+ testRegularTriangleDualRays+ testRegularBoundedDualSegment+ testRegularCoplanarUpperFacet+ testRegularCollinearClassification+ testRegularLowerDimensionalAndHiddenSites+ testSparsePowerMatchesCompleteOracle+ testEqualCoincidentResolution+ testDominantCoincidentResolution+ testLowerDimensionalCell+ testDistinctEmptyCell+ testDyadicNearParallelBisectors+ testBinary64PrecisionSites+ testDuplicateLabels+ testNonFiniteWeight+ putStrLn "power diagram: ok"++testRegularTriangleDualRays :: IO ()+testRegularTriangleDualRays = do+ zero <- admittedWeight 0+ firstSite <- admittedSite "first" (Point 0 0) zero+ secondSite <- admittedSite "second" (Point 2 0) zero+ thirdSite <- admittedSite "third" (Point 0 2) zero+ let sites = firstSite :| [secondSite, thirdSite]+ (regular, receipt) <-+ requireRight "three-site regular topology" (regularTriangulation sites)+ assertEqual "three visible regular sites" 3 (regularTriangulationVisibleSites receipt)+ assertEqual "one regular face" 1 (length (regularFaces regular))+ assertEqual "three regular boundary edges" 3 (length (regularEdges regular))+ dualPoint <-+ case regularFaces regular of+ [face] -> pure (regularFaceDualPoint face)+ faces -> fail ("three-site topology expected one face, got " <> show (length faces))+ traverse_ (assertBoundaryDualRay sites dualPoint) (regularEdges regular)+ traverse_+ (\site ->+ let label = powerSiteLabel site+ in do+ assertEqual+ ("visible regular disposition for " <> label)+ (Just RegularSiteVisible)+ (regularSiteDisposition label regular)+ assertEqual+ ("two regular neighbours for " <> label)+ 2+ (Set.size (regularNeighbours label regular)))+ sites++testRegularBoundedDualSegment :: IO ()+testRegularBoundedDualSegment = do+ zero <- admittedWeight 0+ sites <-+ traverse+ (\(label, point) -> admittedSite label point zero)+ ( ("south-west", Point 0 0)+ :| [ ("south-east", Point 4 0)+ , ("north-east", Point 3 3)+ , ("north-west", Point 0 4)+ ]+ )+ (regular, receipt) <-+ requireRight "four-site regular topology" (regularTriangulation sites)+ assertEqual+ "regular planar Euler equation"+ 1+ ( regularTriangulationVisibleSites receipt+ - regularTriangulationEdges receipt+ + regularTriangulationFaces receipt+ )+ let faceDuals = Set.fromList (fmap regularFaceDualPoint (regularFaces regular))+ bounded =+ [ segment+ | edge <- regularEdges regular+ , BoundedPowerDual segment <- [regularEdgeDual edge]+ ]+ case bounded of+ [segment] ->+ let (firstEndpoint, secondEndpoint) = exactSegmentEndpoints segment+ in unless+ (Set.member firstEndpoint faceDuals && Set.member secondEndpoint faceDuals)+ (fail "bounded regular dual does not join its two incident face duals")+ segments ->+ fail ("four-site topology expected one bounded dual, got " <> show (length segments))++testRegularCoplanarUpperFacet :: IO ()+testRegularCoplanarUpperFacet = do+ sites <-+ traverse+ prepareOracleSite+ ( ("a-bottom", Point 0.25 0.25, -0.875)+ :| [ ("b-south-west", Point 0 0, 0)+ , ("c-south-east", Point 1 0, 1)+ , ("d-center", Point 0.5 0.5, 0.5)+ , ("e-north-east", Point 1 1, 2)+ , ("f-north-west", Point 0 1, 1)+ ]+ )+ (regular, receipt) <-+ requireRight "coplanar upper regular facet" (regularTriangulation sites)+ traverse_+ (\label ->+ assertEqual+ ("upper-facet vertex remains visible: " <> label)+ (Just RegularSiteVisible)+ (regularSiteDisposition label regular))+ ["b-south-west", "c-south-east", "e-north-east", "f-north-west"]+ assertEqual+ "upper-facet interior generator remains lower-dimensional"+ (Just RegularSiteLowerDimensional)+ (regularSiteDisposition "d-center" regular)+ assertEqual+ "strictly lower lifted generator remains hidden"+ (Just RegularSiteHidden)+ (regularSiteDisposition "a-bottom" regular)+ assertEqual "coplanar upper facet has four visible vertices" 4 (regularTriangulationVisibleSites receipt)+ assertEqual "coplanar upper facet receives one deterministic diagonal" 2 (regularTriangulationFaces receipt)++testRegularLowerDimensionalAndHiddenSites :: IO ()+testRegularLowerDimensionalAndHiddenSites = do+ domain <- squareDomain+ zero <- admittedWeight 0+ lowerWeight <- admittedWeight (-1.5)+ hiddenWeight <- admittedWeight (-2)+ firstSite <- admittedSite "first" (Point 0 0) zero+ secondSite <- admittedSite "second" (Point 2 0) zero+ thirdSite <- admittedSite "third" (Point 0 2) zero+ lowerSite <- admittedSite "center" (Point 0.5 0.5) lowerWeight+ hiddenSite <- admittedSite "center" (Point 0.5 0.5) hiddenWeight+ let lowerSites = firstSite :| [secondSite, thirdSite, lowerSite]+ hiddenSites = firstSite :| [secondSite, thirdSite, hiddenSite]+ (lowerRegular, _) <-+ requireRight "coplanar regular topology" (regularTriangulation lowerSites)+ assertEqual+ "coplanar interior generator remains lower-dimensional"+ (Just RegularSiteLowerDimensional)+ (regularSiteDisposition "center" lowerRegular)+ (lowerDiagram, lowerReceipt) <-+ requireRight "bounded lower-dimensional power cell" (boundedPowerDiagram domain lowerSites)+ case powerCellDisposition "center" lowerDiagram of+ Just (LowerDimensionalPowerCell _) -> pure ()+ other -> fail ("expected lower-dimensional center cell, got " <> dispositionTag other)+ assertEqual "one lower-dimensional oracle cell" 1 (powerDiagramOracleCells lowerReceipt)+ (hiddenRegular, _) <-+ requireRight "hidden regular topology" (regularTriangulation hiddenSites)+ assertEqual+ "strictly interior lifted generator is hidden"+ (Just RegularSiteHidden)+ (regularSiteDisposition "center" hiddenRegular)+ (hiddenDiagram, hiddenReceipt) <-+ requireRight "bounded hidden power cell" (boundedPowerDiagram domain hiddenSites)+ assertEqual "hidden generator has empty cell" (Just EmptyPowerCell) (powerCellDisposition "center" hiddenDiagram)+ assertEqual "hidden generator needs no HPI oracle" 0 (powerDiagramOracleCells hiddenReceipt)+ unless+ ( powerDiagramMaximumCellConstraints hiddenReceipt <= powerDiagramRegularEdges hiddenReceipt+ )+ (fail "power construction retained more axes than the regular graph")++testRegularCollinearClassification :: IO ()+testRegularCollinearClassification = do+ sites <- traverse prepareCollinearSite (0 :| [1 .. 8])+ (regular, _) <-+ requireRight "collinear regular topology" (regularTriangulation sites)+ traverse_+ (\index ->+ assertEqual+ ("collinear disposition for " <> show index)+ (Just (if even index then RegularSiteVisible else RegularSiteHidden))+ (regularSiteDisposition (show index) regular))+ ([0 .. 8] :: [Int])+ where+ prepareCollinearSite :: Int -> IO (PowerSite String)+ prepareCollinearSite index = do+ weight <- admittedWeight (if odd index then -(2 / 256) else 0)+ admittedSite (show index) (Point (fromIntegral index / 16) 0) weight++testSparsePowerMatchesCompleteOracle :: IO ()+testSparsePowerMatchesCompleteOracle = do+ domain <- squareDomain+ sites <-+ traverse+ prepareOracleSite+ ( ("a", Point 1.0 1.0, 0.25)+ :| [ ("b", Point 3.0 0.8, -0.125)+ , ("c", Point 5.2 1.4, 0.375)+ , ("d", Point 8.5 1.0, 0.0)+ , ("e", Point 1.7 3.8, -0.25)+ , ("f", Point 4.1 4.4, 0.125)+ , ("g", Point 7.6 3.5, -0.375)+ , ("h", Point 9.1 5.4, 0.25)+ , ("i", Point 1.0 7.8, 0.0)+ , ("j", Point 3.6 8.9, 0.5)+ , ("k", Point 6.4 7.5, -0.125)+ , ("l", Point 8.8 9.0, 0.375)+ ]+ )+ (diagram, receipt) <-+ requireRight "sparse regular-neighbour power diagram" (boundedPowerDiagram domain sites)+ traverse_+ (\site -> do+ oracle <- completeOraclePowerCell domain sites site+ assertEqual+ ("regular-neighbour cell equals complete HPI oracle for " <> powerSiteLabel site)+ (Just oracle)+ (powerCellDisposition (powerSiteLabel site) diagram))+ sites+ let siteCount = NonEmpty.length sites+ completeConstraintCount = siteCount * (siteCount - 1)+ unless (powerDiagramSubmittedSiteConstraints receipt < completeConstraintCount) $+ fail "regular-neighbour construction did not eliminate the complete pairwise cell schedule"+ unless (powerDiagramMaximumCellConstraints receipt <= powerDiagramRegularEdges receipt) $+ fail "regular-neighbour construction retained a non-topological radical axis"++prepareOracleSite+ :: (String, Point, Double)+ -> IO (PowerSite String)+prepareOracleSite (label, point, weightValue) = do+ weight <- admittedWeight weightValue+ admittedSite label point weight++data OracleScore = OracleScore+ { oracleXCoefficient :: !ExactRational+ , oracleYCoefficient :: !ExactRational+ , oracleConstant :: !ExactRational+ }++oracleScore :: PowerSite label -> IO OracleScore+oracleScore site = do+ point <- requireRight "oracle source point" (exactPointFromPoint (powerSitePosition site))+ let (coordinateX, coordinateY) = exactPointCoordinates point+ pure+ OracleScore+ { oracleXCoefficient = 2 * coordinateX+ , oracleYCoefficient = 2 * coordinateY+ , oracleConstant =+ powerWeightExact (powerSiteWeight site)+ - coordinateX * coordinateX+ - coordinateY * coordinateY+ }++completeOraclePowerCell+ :: ConvexPolygon+ -> NonEmpty (PowerSite String)+ -> PowerSite String+ -> IO (PowerCellDisposition String)+completeOraclePowerCell domain sites owner = do+ retainedDomain <-+ requireRight "oracle retained domain" (exactRetainedPolygon (convexPolygonPoints domain))+ ownerScore <- oracleScore owner+ halfPlanes <-+ traverse+ (oracleWinningHalfPlane ownerScore)+ ( filter+ ((/= powerSiteLabel owner) . powerSiteLabel)+ (NonEmpty.toList sites)+ )+ (disposition, _) <-+ requireRight+ ("complete HPI oracle for " <> powerSiteLabel owner)+ (exactClipRetainedPolygon retainedDomain halfPlanes)+ case disposition of+ ExactClipFullDimensional retained ->+ PublishedPowerCell+ <$> requireRight+ "oracle convex publication"+ (convexPolygon (exactRetainedPolygonPoints retained))+ ExactClipLowerDimensional points ->+ pure (LowerDimensionalPowerCell points)+ ExactClipEmpty -> pure EmptyPowerCell++oracleWinningHalfPlane+ :: OracleScore+ -> PowerSite label+ -> IO ExactClosedHalfPlane+oracleWinningHalfPlane owner competitor = do+ competitorScore <- oracleScore competitor+ line <-+ requireRight+ "oracle radical axis"+ ( exactAffineLine+ (oracleXCoefficient owner - oracleXCoefficient competitorScore)+ (oracleYCoefficient owner - oracleYCoefficient competitorScore)+ (oracleConstant owner - oracleConstant competitorScore)+ )+ pure (exactClosedHalfPlane line)++assertBoundaryDualRay+ :: NonEmpty (PowerSite String)+ -> ExactPoint+ -> RegularEdge String+ -> IO ()+assertBoundaryDualRay sites expectedOrigin edge =+ case regularEdgeDual edge of+ UnboundedPowerDual ray -> do+ assertEqual "regular ray starts at incident face dual" expectedOrigin (exactRayOrigin ray)+ let ExactVector directionX directionY = exactRayDirection ray+ unless (directionX /= 0 || directionY /= 0) $+ fail ("regular edge has a zero dual-ray direction: " <> show (regularEdgeLabels edge))+ let sample = translateExactPoint expectedOrigin (exactRayDirection ray)+ (firstLabel, secondLabel) = regularEdgeLabels edge+ firstSite <- requireSite firstLabel sites+ secondSite <- requireSite secondLabel sites+ firstDistance <- powerDistance sample firstSite+ secondDistance <- powerDistance sample secondSite+ assertEqual "regular ray remains on its radical axis" firstDistance secondDistance+ traverse_+ (\competitor -> do+ competitorDistance <- powerDistance sample competitor+ unless (firstDistance <= competitorDistance) $+ fail ("regular ray points outside the common winning cone: " <> show (firstLabel, secondLabel)))+ sites+ other -> fail ("regular triangle boundary expected a ray, got " <> show other)++requireSite :: Eq label => label -> NonEmpty (PowerSite label) -> IO (PowerSite label)+requireSite label sites =+ case List.find ((== label) . powerSiteLabel) (NonEmpty.toList sites) of+ Nothing -> fail "regular topology references a missing source site"+ Just site -> pure site++testEqualWeightsAndCommonShift :: IO ()+testEqualWeightsAndCommonShift = do+ domain <- squareDomain+ zero <- admittedWeight 0+ shifted <- admittedWeight 7+ zeroSites <-+ ( :| )+ <$> admittedSite "left" (Point 2 5) zero+ <*> traverse (uncurry3 admittedSite) [("right", Point 8 5, zero)]+ shiftedSites <-+ ( :| )+ <$> admittedSite "left" (Point 2 5) shifted+ <*> traverse (uncurry3 admittedSite) [("right", Point 8 5, shifted)]+ (diagram, receipt) <- requireRight "equal-weight power diagram" (boundedPowerDiagram domain zeroSites)+ (shiftedDiagram, _) <- requireRight "common-shift power diagram" (boundedPowerDiagram domain shiftedSites)+ assertEqual "common additive power shift" (powerCellDispositions diagram) (powerCellDispositions shiftedDiagram)+ assertPermutationInvariant "equal-weight power diagram" domain zeroSites diagram+ assertPermutationInvariant "common-shift power diagram" domain shiftedSites shiftedDiagram+ expectedLeft <-+ requireRight+ "expected left power cell"+ (convexPolygon (integerPoint 0 0 :| [integerPoint 5 0, integerPoint 5 10, integerPoint 0 10]))+ expectedRight <-+ requireRight+ "expected right power cell"+ (convexPolygon (integerPoint 5 0 :| [integerPoint 10 0, integerPoint 10 10, integerPoint 5 10]))+ assertEqual "equal-weight left bisector cell" (Just (PublishedPowerCell expectedLeft)) (powerCellDisposition "left" diagram)+ assertEqual "equal-weight right bisector cell" (Just (PublishedPowerCell expectedRight)) (powerCellDisposition "right" diagram)+ assertEqual "receipt input sites" 2 (powerDiagramInputSites receipt)+ assertEqual+ "one submitted site constraint per cell"+ 2+ (powerDiagramSubmittedSiteConstraints receipt)+ assertEqual+ "parallel domain and site boundaries coalesce"+ 8+ (powerDiagramActiveBoundaries receipt)+ assertEqual "receipt published cells" 2 (powerDiagramPublishedCells receipt)+ assertEqual "receipt empty cells" 0 (powerDiagramEmptyCells receipt)+ traverse_ (assertPublishedVerticesWin zeroSites) (powerCellDispositions diagram)+ layer <- requireRight "power diagram planar layer" (powerDiagramPlanarLayer "outside" diagram)+ assertEqual "derived layer contains both cells" ["left", "right"] (Map.keys (planarLayerRegions layer))+ assertOutsideCollision diagram++testThreeSiteExactPartitionCoverage :: IO ()+testThreeSiteExactPartitionCoverage = do+ domain <- squareDomain+ weight <- admittedWeight 0+ left <- admittedSite "left" (Point 2 5) weight+ middle <- admittedSite "middle" (Point 5 5) weight+ right <- admittedSite "right" (Point 8 5) weight+ let sites = left :| [middle, right]+ (diagram, receipt) <-+ requireRight "three-site exact partition" (boundedPowerDiagram domain sites)+ assertEqual "three-site partition publishes every cell" 3 (powerDiagramPublishedCells receipt)+ assertEqual+ "three-site exact cell areas cover the square"+ 200+ (publishedTwiceAreaSum diagram)+ traverse_+ (\permutation -> do+ (permuted, _) <-+ requireRight+ "permuted three-site exact partition"+ (boundedPowerDiagram domain permutation)+ assertEqual+ "three-site partition is permutation invariant"+ (powerCellDispositions diagram)+ (powerCellDispositions permuted)+ assertEqual+ "permuted exact cell areas cover the square"+ 200+ (publishedTwiceAreaSum permuted))+ (nonEmptyPermutations sites)++nonEmptyPermutations :: NonEmpty value -> [NonEmpty value]+nonEmptyPermutations =+ foldMap (maybe [] pure . NonEmpty.nonEmpty)+ . List.permutations+ . NonEmpty.toList++assertPermutationInvariant+ :: String+ -> ConvexPolygon+ -> NonEmpty (PowerSite String)+ -> BoundedPowerDiagram String+ -> IO ()+assertPermutationInvariant label domain sites expected =+ traverse_+ (\permutation -> do+ (permuted, _) <-+ requireRight+ (label <> " permutation")+ (boundedPowerDiagram domain permutation)+ assertEqual+ (label <> " is permutation invariant")+ (powerCellDispositions expected)+ (powerCellDispositions permuted))+ (nonEmptyPermutations sites)++publishedTwiceAreaSum :: BoundedPowerDiagram label -> ExactRational+publishedTwiceAreaSum =+ List.foldl'+ (\total (_, disposition) -> case disposition of+ PublishedPowerCell polygon -> total + exactPolygonTwiceArea (convexPolygonPoints polygon)+ LowerDimensionalPowerCell _ -> total+ EmptyPowerCell -> total+ CoincidentEquivalentTo _ -> total+ CoincidentDominatedBy _ -> total)+ 0+ . powerCellDispositions++exactPolygonTwiceArea :: NonEmpty ExactPoint -> ExactRational+exactPolygonTwiceArea points =+ abs+ ( List.foldl'+ (\twiceArea (firstPoint, secondPoint) ->+ let (firstX, firstY) = exactPointCoordinates firstPoint+ (secondX, secondY) = exactPointCoordinates secondPoint+ in twiceArea + firstX * secondY - firstY * secondX)+ 0+ (cyclicPairs points)+ )++cyclicPairs :: NonEmpty value -> [(value, value)]+cyclicPairs (firstValue :| remainingValues) =+ zip+ (firstValue : remainingValues)+ (remainingValues <> [firstValue])++testEqualCoincidentResolution :: IO ()+testEqualCoincidentResolution = do+ domain <- squareDomain+ weight <- admittedWeight 3+ a <- admittedSite "a" (Point 4 4) weight+ b <- admittedSite "b" (Point 4 4) weight+ (diagram, receipt) <- requireRight "equivalent coincident power sites" (boundedPowerDiagram domain (b :| [a]))+ assertPublished "coincident canonical representative" (powerCellDisposition "a" diagram)+ assertEqual "equal-function coincident alias" (Just (CoincidentEquivalentTo "a")) (powerCellDisposition "b" diagram)+ assertEqual "equivalent coincidence receipt" 1 (powerDiagramCoincidentEquivalentCells receipt)+ assertEqual "equivalent coincidence is not dominance" 0 (powerDiagramCoincidentDominatedCells receipt)+ (permuted, _) <- requireRight "permuted equivalent sites" (boundedPowerDiagram domain (a :| [b]))+ assertEqual "equivalent resolution permutation invariance" (powerCellDispositions diagram) (powerCellDispositions permuted)++testDominantCoincidentResolution :: IO ()+testDominantCoincidentResolution = do+ domain <- squareDomain+ high <- admittedWeight 3+ low <- admittedWeight 2+ winner <- admittedSite "winner" (Point 4 4) high+ dominated <- admittedSite "dominated" (Point 4 4) low+ (diagram, receipt) <- requireRight "dominant coincident power sites" (boundedPowerDiagram domain (dominated :| [winner]))+ assertPublished "dominant coincident representative" (powerCellDisposition "winner" diagram)+ assertEqual "lower-weight coincident site" (Just (CoincidentDominatedBy "winner")) (powerCellDisposition "dominated" diagram)+ assertEqual "dominant coincidence receipt" 1 (powerDiagramCoincidentDominatedCells receipt)+ assertEqual "dominance is not equivalence" 0 (powerDiagramCoincidentEquivalentCells receipt)+ assertPermutationInvariant "dominant coincident power sites" domain (dominated :| [winner]) diagram++testLowerDimensionalCell :: IO ()+testLowerDimensionalCell = do+ domain <- squareDomain+ zero <- admittedWeight 0+ suppressed <- admittedWeight (-1)+ left <- admittedSite "left" (Point 0 5) zero+ right <- admittedSite "right" (Point 2 5) zero+ middle <- admittedSite "middle" (Point 1 5) suppressed+ let sites = left :| [right, middle]+ (diagram, receipt) <- requireRight "one-dimensional bounded power cell" (boundedPowerDiagram domain sites)+ case powerCellDisposition "middle" diagram of+ Just (LowerDimensionalPowerCell points) ->+ assertEqual+ "middle cell is the exact x=1 segment"+ (List.sort [integerPoint 1 0, integerPoint 1 10])+ (List.sort (NonEmpty.toList points))+ other -> fail ("one-dimensional power cell: expected retained segment, got " <> dispositionTag other)+ assertEqual "lower-dimensional receipt" 1 (powerDiagramLowerDimensionalCells receipt)+ assertEqual "lower-dimensional cell is not empty" 0 (powerDiagramEmptyCells receipt)+ assertEqual "two full-dimensional neighbours" 2 (powerDiagramPublishedCells receipt)+ traverse_ (assertPublishedVerticesWin sites) (powerCellDispositions diagram)+ layer <- requireRight "lower-dimensional derived layer" (powerDiagramPlanarLayer "outside" diagram)+ assertEqual "derived layer omits the one-dimensional cell" ["left", "right"] (Map.keys (planarLayerRegions layer))+ assertPermutationInvariant "one-dimensional bounded power cell" domain sites diagram++testDistinctEmptyCell :: IO ()+testDistinctEmptyCell = do+ domain <- squareDomain+ ordinary <- admittedWeight 0+ suppressed <- admittedWeight (-1000)+ winner <- admittedSite "winner" (Point 0 0) ordinary+ hidden <- admittedSite "hidden" (Point 5 5) suppressed+ (diagram, receipt) <- requireRight "distinct empty power cell" (boundedPowerDiagram domain (winner :| [hidden]))+ assertPublished "dominant distinct site" (powerCellDisposition "winner" diagram)+ assertEqual "distinct site may have empty bounded cell" (Just EmptyPowerCell) (powerCellDisposition "hidden" diagram)+ assertEqual "empty disposition receipt" 1 (powerDiagramEmptyCells receipt)+ assertPermutationInvariant "distinct empty power cell" domain (winner :| [hidden]) diagram++testDyadicNearParallelBisectors :: IO ()+testDyadicNearParallelBisectors = do+ domain <- squareDomain+ weight <- admittedWeight 0+ let epsilon = 2 ** (-20) :: Double+ origin <- admittedSite "origin" (Point 0 0) weight+ horizontal <- admittedSite "horizontal" (Point 2 0) weight+ tilted <- admittedSite "tilted" (Point 2 epsilon) weight+ let sites = origin :| [horizontal, tilted]+ (diagram, receipt) <-+ requireRight+ "dyadic near-parallel power bisectors"+ (boundedPowerDiagram domain sites)+ assertEqual "near-parallel cells retain full dimension" 3 (powerDiagramPublishedCells receipt)+ traverse_ (assertPublishedVerticesWin sites) (powerCellDispositions diagram)+ assertPermutationInvariant "dyadic near-parallel power bisectors" domain sites diagram++testBinary64PrecisionSites :: IO ()+testBinary64PrecisionSites = do+ domain <- squareDomain+ firstWeight <- admittedWeight 0.2+ secondWeight <- admittedWeight (-0.3)+ thirdWeight <- admittedWeight 0.7+ firstSite <- admittedSite "first" (Point 0.1 0.3) firstWeight+ secondSite <- admittedSite "second" (Point 9.7 0.2) secondWeight+ thirdSite <- admittedSite "third" (Point 4.9 9.6) thirdWeight+ let sites = firstSite :| [secondSite, thirdSite]+ (diagram, receipt) <-+ requireRight+ "exact binary64 power sites"+ (boundedPowerDiagram domain sites)+ assertEqual "binary64 fixture publishes every cell" 3 (powerDiagramPublishedCells receipt)+ traverse_ (assertPublishedVerticesWin sites) (powerCellDispositions diagram)+ assertPermutationInvariant "exact binary64 power sites" domain sites diagram++testDuplicateLabels :: IO ()+testDuplicateLabels = do+ domain <- squareDomain+ weight <- admittedWeight 0+ firstSite <- admittedSite "duplicate" (Point 1 1) weight+ secondSite <- admittedSite "duplicate" (Point 9 9) weight+ case boundedPowerDiagram domain (firstSite :| [secondSite]) of+ Left (DuplicatePowerSiteLabel "duplicate") -> pure ()+ other -> fail ("duplicate power-site label: expected typed refusal, got " <> show other)++testNonFiniteWeight :: IO ()+testNonFiniteWeight =+ case powerWeight (0 / 0) of+ Left (PowerWeightNonFinite _) -> pure ()+ other -> fail ("non-finite power weight: expected typed refusal, got " <> show other)++squareDomain :: IO ConvexPolygon+squareDomain =+ requireRight+ "square clipping domain"+ (convexPolygon (integerPoint 0 0 :| [integerPoint 10 0, integerPoint 10 10, integerPoint 0 10]))++admittedWeight :: Double -> IO PowerWeight+admittedWeight value =+ requireRight+ "finite power weight"+ (powerWeight value)++admittedSite :: String -> Point -> PowerWeight -> IO (PowerSite String)+admittedSite label point weight = requireRight "admitted power site" (powerSite label point weight)++uncurry3 :: (a -> b -> c -> result) -> (a, b, c) -> result+uncurry3 function (firstValue, secondValue, thirdValue) = function firstValue secondValue thirdValue++assertPublished :: String -> Maybe (PowerCellDisposition label) -> IO ()+assertPublished _ (Just (PublishedPowerCell _)) = pure ()+assertPublished label other = fail (label <> ": expected published cell, got " <> dispositionTag other)++dispositionTag :: Maybe (PowerCellDisposition label) -> String+dispositionTag Nothing = "missing"+dispositionTag (Just (PublishedPowerCell _)) = "published"+dispositionTag (Just (LowerDimensionalPowerCell _)) = "lower-dimensional"+dispositionTag (Just EmptyPowerCell) = "empty"+dispositionTag (Just (CoincidentEquivalentTo _)) = "coincident-equivalent"+dispositionTag (Just (CoincidentDominatedBy _)) = "coincident-dominated"++assertOutsideCollision :: BoundedPowerDiagram String -> IO ()+assertOutsideCollision diagram =+ case powerDiagramPlanarLayer "left" diagram of+ Left (PowerDiagramOutsideLabelCollides "left") -> pure ()+ other -> fail ("power layer outside-label collision: expected refusal, got " <> show other)++assertPublishedVerticesWin+ :: NonEmpty (PowerSite String)+ -> (String, PowerCellDisposition String)+ -> IO ()+assertPublishedVerticesWin sites (ownerLabel, disposition) =+ case disposition of+ PublishedPowerCell polygon ->+ assertCellPointsWin ownerLabel sites (convexPolygonPoints polygon)+ LowerDimensionalPowerCell points ->+ assertCellPointsWin ownerLabel sites points+ EmptyPowerCell -> pure ()+ CoincidentEquivalentTo _ -> pure ()+ CoincidentDominatedBy _ -> pure ()++assertCellPointsWin+ :: String+ -> NonEmpty (PowerSite String)+ -> NonEmpty ExactPoint+ -> IO ()+assertCellPointsWin ownerLabel sites points =+ case lookupOwner ownerLabel (NonEmpty.toList sites) of+ Nothing -> fail ("power-cell owner missing: " <> ownerLabel)+ Just owner ->+ traverse_+ (\point -> traverse_ (assertOwnerWinsAt point owner) sites)+ points++lookupOwner :: Eq label => label -> [PowerSite label] -> Maybe (PowerSite label)+lookupOwner label = List.find ((== label) . powerSiteLabel)++assertOwnerWinsAt+ :: ExactPoint+ -> PowerSite String+ -> PowerSite String+ -> IO ()+assertOwnerWinsAt point owner competitor = do+ ownerValue <- powerDistance point owner+ competitorValue <- powerDistance point competitor+ unless (ownerValue <= competitorValue) $+ fail+ ( "power-cell vertex violates source inequality: "+ <> show (powerSiteLabel owner, powerSiteLabel competitor, ownerValue, competitorValue)+ )++powerDistance :: ExactPoint -> PowerSite label -> IO ExactRational+powerDistance point site = do+ exactSite <- requireRight "site exact position" (exactPointFromPoint (powerSitePosition site))+ let (x, y) = exactPointCoordinates point+ (siteX, siteY) = exactPointCoordinates exactSite+ deltaX = x - siteX+ deltaY = y - siteY+ pure (deltaX * deltaX + deltaY * deltaY - powerWeightExact (powerSiteWeight site))
test/coherence/Main.hs view
@@ -1,12 +1,15 @@ -- | Compile every test slice against the union of their dependencies. Empty -- imports make module and instance collisions observable without executing the--- four focused behavioral suites twice.+-- behavioral suites twice. module Main (main) where import Moonlight.Triangulation.AlgebraSpec ()+import Moonlight.Triangulation.ExactClipRetentionSpec ()+import Moonlight.Triangulation.LayerOperationsSpec () import Moonlight.Triangulation.MinkowskiSpec () import Moonlight.Triangulation.NativeSpec () import Moonlight.Triangulation.ParallelSpec ()+import Moonlight.Triangulation.PowerDiagramSpec () import Moonlight.Triangulation.RegionAlgebraSpec () import Moonlight.Triangulation.ScheduleAgreementSpec () import Moonlight.Triangulation.SerializationSpec ()
+ test/exact-planar/Main.hs view
@@ -0,0 +1,15 @@+module Main (main) where++import qualified Moonlight.Triangulation.ExactClipRetentionSpec as ExactClipRetentionSpec+import qualified Moonlight.Triangulation.LayerOperationsSpec as LayerOperationsSpec+import qualified Moonlight.Triangulation.MinkowskiSpec as MinkowskiSpec+import qualified Moonlight.Triangulation.PowerDiagramSpec as PowerDiagramSpec++main :: IO ()+main =+ sequence_+ [ ExactClipRetentionSpec.tests+ , MinkowskiSpec.tests+ , PowerDiagramSpec.tests+ , LayerOperationsSpec.tests+ ]
test/native/Moonlight/Triangulation/ExactEmbeddingSpec.hs view
@@ -36,7 +36,7 @@ , exactRationalNumerator , exactSignum )-import Moonlight.Triangulation.Internal.Overlay.Embedding+import Moonlight.Triangulation.Embedding ( DraftIncidence (..) , DraftId (..) , DraftNeighborhood (..)@@ -44,7 +44,12 @@ , DraftVertexId , EmbeddingObligation (..) , ExactArrangementDraft (..)- , LocalEmbeddingCertificate (..)+ , certificateIncidenceRelationPreservationCount+ , certificateNeighborhoodRotationPreservationCount+ , certificateResidual+ , certificateRoundedVertexDistinctnessCount+ , certificateRoundedVertices+ , certificateSplitOrderPreservationCount , OverlayEmbeddingObstruction (..) , certifyLocalEmbedding , residualUndischargedObligations
test/public-components/Main.hs view
@@ -11,6 +11,11 @@ import Moonlight.Triangulation.Handles.Iterators.DynamicIterators ( vertexHandles )+import Moonlight.Triangulation.Handles.Scoped+ ( scopedVertexPoint+ , scopedVertices+ , withScopedTriangulation+ ) import Moonlight.Triangulation.PointLocation (locatePoint) import Moonlight.Triangulation.Session ( insertVertexAt@@ -37,6 +42,7 @@ data PublicComponentSummary = PublicComponentSummary { residentVertices :: !Int , owningVertexHandles :: !Int+ , scopedVertexPoints :: !Int , rectangleVertices :: !Int , locatedResidentVertex :: !Bool , sessionInsertedVertex :: !Bool@@ -80,6 +86,9 @@ PublicComponentSummary { residentVertices = numVertices restored , owningVertexHandles = length (vertexHandles restored)+ , scopedVertexPoints =+ withScopedTriangulation restored $ \scoped ->+ length (fmap (scopedVertexPoint scoped) (scopedVertices scoped)) , rectangleVertices = length inside , locatedResidentVertex = case locatePoint restored query of OnVertex _ -> True@@ -104,6 +113,7 @@ PublicComponentSummary { residentVertices = 4 , owningVertexHandles = 4+ , scopedVertexPoints = 4 , rectangleVertices = 4 , locatedResidentVertex = True , sessionInsertedVertex = True