packages feed

moonlight-triangulation 1.4.0.5 → 1.5.0.0

raw patch · 84 files changed

+7861/−2818 lines, 84 filesdep ~basedep ~containersdep ~moonlight-triangulationPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base, containers, moonlight-triangulation

API changes (from Hackage documentation)

- Moonlight.Triangulation: PowerDiagramOutsideLabelCollides :: label -> PowerDiagramError label
- Moonlight.Triangulation: [regularTriangulationPeakHullFaces] :: RegularTriangulationReceipt -> Int
- 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.PowerDiagram: PowerDiagramOutsideLabelCollides :: label -> PowerDiagramError label
- Moonlight.Triangulation.PowerDiagram: [regularTriangulationPeakHullFaces] :: RegularTriangulationReceipt -> Int
- 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: PowerRegularDispositionMissing :: label -> PowerDiagramError label
+ Moonlight.Triangulation: PowerRegularGeneratorMissing :: label -> PowerDiagramError label
+ Moonlight.Triangulation: RegularAlphaComplexInvalid :: PlanarComplexError label -> RegularAlphaError label
+ Moonlight.Triangulation: RegularAlphaDualMismatch :: PlanarSimplex label -> ExactRational -> ExactRational -> RegularAlphaError label
+ Moonlight.Triangulation: RegularAlphaProjectionFailed :: PlanarSimplex label -> ExactArithmeticError -> RegularAlphaError label
+ Moonlight.Triangulation: RegularAlphaSimplexInvalid :: PlanarSimplexError label -> RegularAlphaError label
+ Moonlight.Triangulation: RegularAlphaSiteMissing :: label -> RegularAlphaError label
+ Moonlight.Triangulation: RegularAlphaSublevelInvalid :: PlanarComplexError label -> RegularAlphaError label
+ Moonlight.Triangulation: RegularAlphaVertexBirthMissing :: label -> RegularAlphaError label
+ Moonlight.Triangulation: RegularEditResult :: RegularTriangulation label -> Set label -> Vector (RegularSiteTransition label) -> RegularEditResult label
+ Moonlight.Triangulation: RegularEditSiteConflict :: label -> PowerSite label -> PowerSite label -> RegularEditError label
+ Moonlight.Triangulation: RegularEditTopologyFailed :: RegularTopologyError label -> RegularEditError label
+ Moonlight.Triangulation: RegularEditUnknownSites :: NonEmpty label -> RegularEditError label
+ Moonlight.Triangulation: RegularSiteAppeared :: label -> RegularSiteDisposition label -> RegularSiteTransition label
+ Moonlight.Triangulation: RegularSiteDisappeared :: label -> RegularSiteDisposition label -> RegularSiteTransition label
+ Moonlight.Triangulation: RegularSiteTransitioned :: label -> RegularSiteDisposition label -> RegularSiteDisposition label -> RegularSiteTransition label
+ Moonlight.Triangulation: [regularEditChangedSites] :: RegularEditResult label -> Set label
+ Moonlight.Triangulation: [regularEditTransitions] :: RegularEditResult label -> Vector (RegularSiteTransition label)
+ Moonlight.Triangulation: [regularEditTriangulation] :: RegularEditResult label -> RegularTriangulation label
+ Moonlight.Triangulation: boundedPowerDiagramFromRegular :: Ord label => ConvexPolygon -> RegularTriangulation label -> Either (PowerDiagramError label) (BoundedPowerDiagram label, PowerDiagramReceipt)
+ Moonlight.Triangulation: data ExactPlanarMoments
+ Moonlight.Triangulation: data PowerAlphaBirth
+ Moonlight.Triangulation: data RegularAlphaError label
+ Moonlight.Triangulation: data RegularAlphaFiltration label
+ Moonlight.Triangulation: data RegularEditError label
+ Moonlight.Triangulation: data RegularEditResult label
+ Moonlight.Triangulation: data RegularSiteTransition label
+ Moonlight.Triangulation: emptyRegularTriangulation :: RegularTriangulation label
+ Moonlight.Triangulation: exactPlanarFirstX :: ExactPlanarMoments -> ExactRational
+ Moonlight.Triangulation: exactPlanarFirstY :: ExactPlanarMoments -> ExactRational
+ Moonlight.Triangulation: exactPlanarMeasure :: ExactPlanarMoments -> ExactRational
+ Moonlight.Triangulation: exactPlanarSecondXX :: ExactPlanarMoments -> ExactRational
+ Moonlight.Triangulation: exactPlanarSecondXY :: ExactPlanarMoments -> ExactRational
+ Moonlight.Triangulation: exactPlanarSecondYY :: ExactPlanarMoments -> ExactRational
+ Moonlight.Triangulation: insertRegularSite :: Ord label => PowerSite label -> RegularTriangulation label -> Either (RegularEditError label) (RegularEditResult label)
+ Moonlight.Triangulation: insertVertexAtNearVertex :: VertexId -> Point -> vertex -> Session s vertex directed undirected face (VertexId, InsertionDisposition)
+ Moonlight.Triangulation: polygonComponentMoments :: PolygonComponent -> ExactPlanarMoments
+ Moonlight.Triangulation: powerAlphaBirthDenominator :: PowerAlphaBirth -> Integer
+ Moonlight.Triangulation: powerAlphaBirthExact :: PowerAlphaBirth -> ExactRational
+ Moonlight.Triangulation: powerAlphaBirthNumerator :: PowerAlphaBirth -> Integer
+ Moonlight.Triangulation: powerSiteExactPosition :: PowerSite label -> ExactPoint
+ Moonlight.Triangulation: powerWeightFromExact :: ExactRational -> PowerWeight
+ Moonlight.Triangulation: regularAlphaBirths :: RegularAlphaFiltration label -> Map (PlanarSimplex label) PowerAlphaBirth
+ Moonlight.Triangulation: regularAlphaComplex :: RegularAlphaFiltration label -> PlanarComplex label
+ Moonlight.Triangulation: regularAlphaComplexAtBirth :: Ord label => PowerAlphaBirth -> RegularAlphaFiltration label -> Either (RegularAlphaError label) (PlanarComplex label)
+ Moonlight.Triangulation: regularAlphaCriticalBirths :: RegularAlphaFiltration label -> [PowerAlphaBirth]
+ Moonlight.Triangulation: regularAlphaFiltration :: Ord label => RegularTriangulation label -> Either (RegularAlphaError label) (RegularAlphaFiltration label)
+ Moonlight.Triangulation: regularAlphaSimplexBirth :: Ord label => PlanarSimplex label -> RegularAlphaFiltration label -> Maybe PowerAlphaBirth
+ Moonlight.Triangulation: regularSite :: Ord label => label -> RegularTriangulation label -> Maybe (PowerSite label)
+ Moonlight.Triangulation: regularSiteCount :: RegularTriangulation label -> Int
+ Moonlight.Triangulation: regularSites :: RegularTriangulation label -> [PowerSite label]
+ Moonlight.Triangulation: regularTriangulationReceipt :: RegularTriangulation label -> RegularTriangulationReceipt
+ Moonlight.Triangulation: removeRegularSite :: Ord label => label -> RegularTriangulation label -> Either (RegularEditError label) (RegularEditResult label)
+ Moonlight.Triangulation: reweightRegularSites :: Ord label => Map label PowerWeight -> RegularTriangulation label -> Either (RegularEditError label) (RegularEditResult label)
+ Moonlight.Triangulation: scaleExactPlanarMoments :: ExactRational -> ExactPlanarMoments -> ExactPlanarMoments
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: RegularReweightDescent :: Map label (CoincidentGeneratorDisposition label) -> Map label (ExactPowerGenerator label) -> Map label (RegularSiteDisposition label) -> [RegularSiteTransition label] -> RegularReweightDescent label
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: [descendedCoincidentDispositions] :: RegularReweightDescent label -> Map label (CoincidentGeneratorDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: [descendedGenerators] :: RegularReweightDescent label -> Map label (ExactPowerGenerator label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: [descendedRegularDispositions] :: RegularReweightDescent label -> Map label (RegularSiteDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: [descendedTransitions] :: RegularReweightDescent label -> [RegularSiteTransition label]
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: advanceRegularConflict :: Ord label => RegularSection label -> ExactPowerGenerator label -> RegularConflictDescent label -> RegularConflictDescent label
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: applyRegularFacePatch :: Ord label => Map label (ExactPowerGenerator label) -> Set (RegularFaceKey label) -> [RegularFace label] -> RegularSection label -> Either (LocalRegularEditObstruction label) (RegularFacePatch label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: becameRegularHidden :: Ord label => Map label (RegularSiteDisposition label) -> Map label (RegularSiteDisposition label) -> label -> Bool
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: becameRegularVisible :: Ord label => Map label (RegularSiteDisposition label) -> Map label (RegularSiteDisposition label) -> label -> Bool
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: certifyRegularEdgeConvexity :: Ord label => Map label (ExactPowerGenerator label) -> Map (RegularFaceKey label) (RegularFace label) -> (RegularEdgeKey label, RegularEdgeSection label) -> Either (LocalRegularEditObstruction label) ()
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: certifyRegularFacePatch :: Ord label => Map label (ExactPowerGenerator label) -> RegularFacePatch label -> Either (LocalRegularEditObstruction label) ()
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: changedDispositionTransition :: Eq label => label -> RegularSiteDisposition label -> RegularSiteDisposition label -> Maybe (RegularSiteTransition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: classifyCoincidentSubordinate :: Ord label => PowerSite label -> PowerSite label -> Maybe (CoincidentGeneratorDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: classifyConflictEdge :: Ord label => RegularSection label -> Set (RegularFaceKey label) -> (label, label) -> Either (LocalRegularEditObstruction label) [(RegularEdgeKey label, (label, label))]
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: coincidentRepresentativeLabel :: CoincidentGeneratorDisposition label -> label
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: commonRegularWeightShift :: Ord label => Map label PowerWeight -> Map label (PowerSite label) -> Maybe ExactRational
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: data RegularReweightDescent label
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: descendTopologyPreservingReweight :: Ord label => Set label -> Map label (PowerSite label) -> Set label -> RegularTriangulation label -> RegularReweightDescent label -> PowerSite label -> Maybe (RegularReweightDescent label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: dispositionTransitions :: Ord label => Map label (RegularSiteDisposition label) -> Map label (RegularSiteDisposition label) -> Vector (RegularSiteTransition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: dispositionTransitionsOn :: Ord label => Set label -> Map label (RegularSiteDisposition label) -> Map label (RegularSiteDisposition label) -> Vector (RegularSiteTransition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: hasCoincidentAliases :: Eq label => label -> RegularSection label -> Bool
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: insertCoincidentRegularSite :: Ord label => PowerSite label -> ExactPowerGenerator label -> label -> RegularSection label -> RegularTriangulation label -> Either (LocalRegularEditObstruction label) (RegularTriangulation label, Set label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: insertDistinctRegularSite :: Ord label => PowerSite label -> ExactPowerGenerator label -> RegularSection label -> RegularTriangulation label -> Either (LocalRegularEditObstruction label) (RegularTriangulation label, Set label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: insertHiddenRegularSite :: Ord label => PowerSite label -> ExactPowerGenerator label -> RegularFaceKey label -> RegularLocality label -> RegularSection label -> RegularTriangulation label -> Either (LocalRegularEditObstruction label) (RegularTriangulation label, Set label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: insertRegularSite :: Ord label => PowerSite label -> RegularTriangulation label -> Either (RegularEditError label) (RegularEditResult label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: insertRegularSiteLocally :: Ord label => PowerSite label -> RegularTriangulation label -> Either (LocalRegularEditObstruction label) (RegularTriangulation label, Set label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: insertVisibleRegularSite :: Ord label => PowerSite label -> ExactPowerGenerator label -> RegularFaceKey label -> RegularLocality label -> RegularSection label -> RegularTriangulation label -> Either (LocalRegularEditObstruction label) (RegularTriangulation label, Set label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: normalizeRegularEdit :: Ord label => RegularTriangulation label -> Map label (PowerSite label) -> Set label -> Either (RegularEditError label) (RegularEditResult label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: patchRegularEdge :: Ord label => Map label (ExactPowerGenerator label) -> Map (RegularFaceKey label) (RegularFace label) -> Set (RegularFaceKey label) -> Map (RegularEdgeKey label) (Set (RegularFaceKey label)) -> Map (RegularEdgeKey label) (RegularEdgeSection label) -> RegularEdgeKey label -> Either (LocalRegularEditObstruction label) (Map (RegularEdgeKey label) (RegularEdgeSection label))
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: patchRegularStars :: Ord label => Set label -> Set (RegularFaceKey label) -> Map (RegularFaceKey label) (RegularFace label) -> Set (RegularEdgeKey label) -> Map (RegularEdgeKey label) (RegularEdgeSection label) -> RegularSection label -> Map label (RegularSiteStar label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: powerSiteHasWeightShift :: ExactRational -> PowerWeight -> PowerSite label -> Bool
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: publishLocalRegularEdit :: Ord label => RegularTriangulation label -> Set label -> (RegularTriangulation label, Set label) -> RegularEditResult label
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: publishPatchedRegularDisposition :: Ord label => Map label (RegularSiteStar label) -> Map label (RegularSiteDisposition label) -> label -> Map label (RegularSiteDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: publishRegularEdgeSection :: Ord label => Map label (ExactPowerGenerator label) -> Map (RegularFaceKey label) (RegularFace label) -> RegularEdgeKey label -> Set (RegularFaceKey label) -> Either (LocalRegularEditObstruction label) (RegularEdgeSection label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: publishRemovalCandidateDisposition :: Ord label => Map label (RegularSiteStar label) -> Map label (RegularSiteDisposition label) -> label -> Map label (RegularSiteDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: regularCoincidentRepresentative :: RegularSiteDisposition label -> Maybe label
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: regularConflictCavity :: Ord label => RegularSection label -> ExactPowerGenerator label -> RegularFaceKey label -> Either (LocalRegularEditObstruction label) (Set (RegularFaceKey label))
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: regularConflictFaceHorizon :: Ord label => RegularSection label -> Set (RegularFaceKey label) -> RegularFaceKey label -> Either (LocalRegularEditObstruction label) (Map (RegularEdgeKey label) (label, label))
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: regularConflictHorizon :: Ord label => RegularSection label -> Set (RegularFaceKey label) -> Either (LocalRegularEditObstruction label) (Map (RegularEdgeKey label) (label, label))
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: regularFaceCentroidInside :: Ord label => Map label (ExactPowerGenerator label) -> [RegularFace label] -> RegularFace label -> Bool
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: regularFaceGenerators :: Ord label => Map label (ExactPowerGenerator label) -> RegularFace label -> Either (LocalRegularEditObstruction label) (ExactPowerGenerator label, ExactPowerGenerator label, ExactPowerGenerator label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: regularFaceSectionBoundary :: Ord label => [RegularFace label] -> Set (RegularEdgeKey label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: regularFaceSlopeSum :: Ord label => Map label (ExactPowerGenerator label) -> RegularFace label -> Either (LocalRegularEditObstruction label) (ExactRational, ExactRational)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: regularFaceVolume :: Ord label => RegularSection label -> RegularFaceKey label -> ExactPowerGenerator label -> Either (LocalRegularEditObstruction label) ExactRational
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: regularHorizonFace :: Ord label => RegularSection label -> ExactPowerGenerator label -> (label, label) -> Either (LocalRegularEditObstruction label) (RegularFace label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: regularSlopeSumInsideFace :: Ord label => Map label (ExactPowerGenerator label) -> (ExactRational, ExactRational) -> RegularFace label -> Bool
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: removeAnyRegularSiteLocally :: Ord label => label -> RegularTriangulation label -> Either (LocalRegularEditObstruction label) (RegularTriangulation label, Set label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: removeRegularSite :: Ord label => label -> RegularTriangulation label -> Either (RegularEditError label) (RegularEditResult label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: removeRegularSiteLocally :: Ord label => label -> RegularTriangulation label -> Either (LocalRegularEditObstruction label) (RegularTriangulation label, Set label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: removeTopologyPreservingSite :: Ord label => label -> RegularTriangulation label -> Maybe (RegularTriangulation label, Vector (RegularSiteTransition label))
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: requireInteriorRemovalEdge :: Ord label => RegularSection label -> label -> label -> Either (LocalRegularEditObstruction label) ()
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: requireRegularEdgeIncidence :: Ord label => RegularSection label -> RegularEdgeKey label -> Either (LocalRegularEditObstruction label) (Set (RegularFaceKey label))
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: reweightRegularSiteLocally :: Ord label => PowerSite label -> RegularTriangulation label -> Either (LocalRegularEditObstruction label) (RegularTriangulation label, Set label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: reweightRegularSites :: Ord label => Map label PowerWeight -> RegularTriangulation label -> Either (RegularEditError label) (RegularEditResult label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: reweightTopologyPreservingSites :: Ord label => Set label -> Map label (PowerSite label) -> Map label (PowerSite label) -> RegularTriangulation label -> Maybe (RegularTriangulation label, Vector (RegularSiteTransition label))
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: translateExactPowerGenerator :: ExactRational -> ExactPowerGenerator label -> ExactPowerGenerator label
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: translateRegularWeights :: ExactRational -> Map label (PowerSite label) -> Set label -> RegularTriangulation label -> RegularEditResult label
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: unchangedRegularEdit :: RegularTriangulation label -> RegularEditResult label
+ Moonlight.Triangulation.Internal.PowerDiagram.Edit: withRegularEditFallback :: Ord label => RegularTriangulation label -> Set label -> Either (RegularEditError label) (RegularEditResult label) -> Either obstruction (RegularTriangulation label, Set label) -> Either (RegularEditError label) (RegularEditResult label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: DistinctSlopeGenerators :: NonEmpty (ExactPowerGenerator label) -> DistinctSlopeGenerators label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: ExactPowerGenerator :: label -> ExactRational -> ExactRational -> ExactRational -> ExactPowerGenerator label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: GeneratorDualCollapsed :: ExactPoint -> GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: GeneratorDualLine :: ExactAffineLine -> GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: GeneratorDualRay :: ExactRay -> GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: GeneratorDualSegment :: ExactSegment -> GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: GeneratorRegularEdge :: ExactPowerGenerator label -> ExactPowerGenerator label -> GeneratorDualGeometry -> GeneratorRegularEdge label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: GeneratorRegularFace :: label -> label -> label -> ExactPoint -> GeneratorRegularFace label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: GeneratorRegularReceipt :: Int -> Int -> Int -> Int -> Int -> Int -> GeneratorRegularReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: GeneratorRegularTopology :: NonEmpty (ExactPowerGenerator label, RegularGeneratorDisposition) -> [GeneratorRegularFace label] -> [GeneratorRegularEdge label] -> GeneratorRegularReceipt -> GeneratorRegularTopology label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: RegularGeneratorHidden :: RegularGeneratorDisposition
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: RegularGeneratorLowerDimensional :: RegularGeneratorDisposition
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: RegularGeneratorVisible :: RegularGeneratorDisposition
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: RegularTopologyAxisInvalid :: label -> label -> ExactHalfPlaneError -> RegularTopologyError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: RegularTopologyDualRayInvalid :: label -> label -> ExactGeometryError -> RegularTopologyError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: RegularTopologyDualSegmentInvalid :: label -> label -> ExactGeometryError -> RegularTopologyError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: RegularTopologyDualVertexInvalid :: label -> label -> label -> ExactIntersectionError -> RegularTopologyError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: RegularTopologyFlatBoundary :: label -> label -> label -> RegularTopologyError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: RegularTopologyNonManifoldEdge :: label -> label -> Int -> RegularTopologyError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: RegularTopologySlopeHullInvalid :: MinkowskiError -> RegularTopologyError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: RegularTopologySlopeVertexMissing :: ExactPoint -> RegularTopologyError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [exactPowerGeneratorConstant] :: ExactPowerGenerator label -> ExactRational
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [exactPowerGeneratorLabel] :: ExactPowerGenerator label -> label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [exactPowerGeneratorXCoefficient] :: ExactPowerGenerator label -> ExactRational
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [exactPowerGeneratorYCoefficient] :: ExactPowerGenerator label -> ExactRational
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [generatorRegularDispositions] :: GeneratorRegularTopology label -> NonEmpty (ExactPowerGenerator label, RegularGeneratorDisposition)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [generatorRegularEdgeCount] :: GeneratorRegularReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [generatorRegularEdgeDual] :: GeneratorRegularEdge label -> GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [generatorRegularEdgeFirst] :: GeneratorRegularEdge label -> ExactPowerGenerator label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [generatorRegularEdgeSecond] :: GeneratorRegularEdge label -> ExactPowerGenerator label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [generatorRegularEdges] :: GeneratorRegularTopology label -> [GeneratorRegularEdge label]
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [generatorRegularFaceCount] :: GeneratorRegularReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [generatorRegularFaceDualPoint] :: GeneratorRegularFace label -> ExactPoint
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [generatorRegularFaceFirst] :: GeneratorRegularFace label -> label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [generatorRegularFaceSecond] :: GeneratorRegularFace label -> label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [generatorRegularFaceThird] :: GeneratorRegularFace label -> label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [generatorRegularFaces] :: GeneratorRegularTopology label -> [GeneratorRegularFace label]
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [generatorRegularHiddenSites] :: GeneratorRegularReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [generatorRegularInputSites] :: GeneratorRegularReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [generatorRegularLowerDimensionalSites] :: GeneratorRegularReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [generatorRegularReceipt] :: GeneratorRegularTopology label -> GeneratorRegularReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: [generatorRegularVisibleSites] :: GeneratorRegularReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: data ExactPowerGenerator label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: data GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: data GeneratorRegularEdge label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: data GeneratorRegularFace label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: data GeneratorRegularReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: data GeneratorRegularTopology label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: data RegularGeneratorDisposition
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: data RegularTopologyError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: exactGeneratorAxis :: ExactPowerGenerator label -> ExactPowerGenerator label -> Either ExactHalfPlaneError ExactAffineLine
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: exactGeneratorBoundaryDualRay :: ExactPoint -> ExactPowerGenerator label -> ExactPowerGenerator label -> ExactPowerGenerator label -> Either (RegularTopologyError label) ExactRay
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: exactGeneratorDualBetween :: label -> label -> ExactPoint -> ExactPoint -> Either (RegularTopologyError label) GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: exactGeneratorFaceDual :: ExactPowerGenerator label -> ExactPowerGenerator label -> ExactPowerGenerator label -> Either (RegularTopologyError label) ExactPoint
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: exactGeneratorLiftedVolume :: ExactPowerGenerator label -> ExactPowerGenerator label -> ExactPowerGenerator label -> ExactPowerGenerator label -> ExactRational
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.PowerDiagram.Generator.RegularGeneratorDisposition
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Generator.ExactPowerGenerator label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularEdge label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularFace label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularTopology label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Generator.RegularTopologyError label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.PowerDiagram.Generator.RegularGeneratorDisposition
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Generator.ExactPowerGenerator label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularEdge label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularFace label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularTopology label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Generator.RegularTopologyError label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.PowerDiagram.Generator.RegularGeneratorDisposition
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.PowerDiagram.Generator.ExactPowerGenerator label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularEdge label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularFace label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Generator.ExactPowerGenerator label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularEdge label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularFace label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularTopology label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Generator.RegularTopologyError label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.PowerDiagram.Generator.RegularGeneratorDisposition
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorDualGeometry
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.PowerDiagram.Generator.RegularGeneratorDisposition
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Generator.ExactPowerGenerator label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularEdge label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularFace label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Generator.GeneratorRegularTopology label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Generator.RegularTopologyError label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Generator: newtype DistinctSlopeGenerators label
+ Moonlight.Triangulation.Internal.PowerDiagram.Hull: instance GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Hull.LiftedPoint label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Hull: instance GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.PowerDiagram.Hull.LiftedPoint label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Hull: regularGeneratorTopology :: Ord label => DistinctSlopeGenerators label -> Either (RegularTopologyError label) (GeneratorRegularTopology label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Locality: attachHiddenGenerator :: Ord label => RegularSection label -> RegularLocality label -> label -> Maybe (RegularLocality label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Locality: attachRegularHidden :: Ord label => label -> RegularFaceKey label -> RegularLocality label -> RegularLocality label
+ Moonlight.Triangulation.Internal.PowerDiagram.Locality: buildRegularLocality :: Ord label => RegularSection label -> Maybe (RegularLocality label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Locality: descendRegularSlope :: Ord label => ExactPoint -> RegularSection label -> Int -> RegularFaceKey label -> Maybe (RegularSlopeLocation label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Locality: detachRegularHidden :: Ord label => label -> RegularLocality label -> RegularLocality label
+ Moonlight.Triangulation.Internal.PowerDiagram.Locality: faceEdgeExcludes :: Ord label => ExactPoint -> RegularSection label -> RegularEdgeKey label -> Bool
+ Moonlight.Triangulation.Internal.PowerDiagram.Locality: firstExteriorFaceEdge :: Ord label => ExactPoint -> RegularSection label -> RegularFace label -> Maybe (RegularEdgeKey label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Locality: generatorEdgeExcludes :: Ord label => ExactPoint -> Map label (ExactPowerGenerator label) -> RegularEdgeKey label -> Bool
+ Moonlight.Triangulation.Internal.PowerDiagram.Locality: generatorEdgeExcludesScaled :: Ord label => ExactRational -> (ExactRational, ExactRational) -> Map label (ExactPowerGenerator label) -> RegularEdgeKey label -> Bool
+ Moonlight.Triangulation.Internal.PowerDiagram.Locality: locateHiddenSupport :: Ord label => RegularFaceKey label -> RegularSection label -> label -> Either (LocalRegularEditObstruction label) (label, RegularFaceKey label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Locality: locateRegularSlope :: Ord label => RegularFaceKey label -> RegularSection label -> ExactPowerGenerator label -> Maybe (RegularSlopeLocation label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Locality: patchRegularLocality :: Ord label => RegularLocality label -> Set (RegularFaceKey label) -> Set label -> RegularSection label -> Either (LocalRegularEditObstruction label) (RegularLocality label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: AffineForm :: ExactRational -> ExactRational -> ExactRational -> AffineForm
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: BoundedPowerDiagram :: Map label (PowerCellDisposition label) -> BoundedPowerDiagram label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: BoundedPowerDual :: ExactSegment -> PowerDualEdge
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: CoincidentDominatedBy :: label -> PowerCellDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: CoincidentEquivalentTo :: label -> PowerCellDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: CoincidentGeneratorDominatedBy :: label -> CoincidentGeneratorDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: CoincidentGeneratorEquivalentTo :: label -> CoincidentGeneratorDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: CollapsedPowerDual :: ExactPoint -> PowerDualEdge
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: DuplicatePowerSiteLabel :: label -> PowerDiagramError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: EmptyPowerCell :: PowerCellDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: FullLinePowerDual :: ExactAffineLine -> PowerDualEdge
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: LocalRegularBoundaryRemoval :: label -> LocalRegularEditObstruction label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: LocalRegularCandidateSectionEmpty :: LocalRegularEditObstruction label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: LocalRegularCavityBoundaryMismatch :: LocalRegularEditObstruction label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: LocalRegularDegenerateFace :: RegularFaceKey label -> LocalRegularEditObstruction label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: LocalRegularEmptyHorizon :: LocalRegularEditObstruction label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: LocalRegularFaceMissing :: RegularFaceKey label -> LocalRegularEditObstruction label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: LocalRegularGeneratorMissing :: label -> LocalRegularEditObstruction label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: LocalRegularLocalityUnavailable :: LocalRegularEditObstruction label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: LocalRegularNonConvexEdge :: RegularEdgeKey label -> LocalRegularEditObstruction label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: LocalRegularNonManifoldEdge :: RegularEdgeKey label -> Int -> LocalRegularEditObstruction label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: LocalRegularSectionUnavailable :: LocalRegularEditObstruction label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: LocalRegularSlopeCoincidence :: label -> LocalRegularEditObstruction label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: LocalRegularSlopeLocationFailed :: LocalRegularEditObstruction label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: LocalRegularSlopeOutside :: RegularEdgeKey label -> LocalRegularEditObstruction label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: LocalRegularTopologyObstruction :: RegularTopologyError label -> LocalRegularEditObstruction label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: LowerDimensionalPowerCell :: NonEmpty ExactPoint -> PowerCellDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: PowerBisectorInvalid :: label -> label -> ExactHalfPlaneError -> PowerDiagramError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: PowerCellClipFailed :: label -> ExactClipError -> PowerDiagramError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: PowerDiagramReceipt :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> PowerDiagramReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: PowerDomainInvalid :: ExactHalfPlaneError -> PowerDiagramError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: PowerRegularDispositionMissing :: label -> PowerDiagramError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: PowerRegularGeneratorMissing :: label -> PowerDiagramError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: PowerRegularTopologyFailed :: RegularTopologyError label -> PowerDiagramError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: PowerSite :: label -> QueryPoint -> PowerWeight -> PowerSite label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: PowerSitePositionInvalid :: label -> PointValidationError -> PowerDiagramError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: PowerWeight :: ExactRational -> PowerWeight
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: PowerWeightNonFinite :: NonFiniteValue -> PowerWeightError
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: PublishedPowerCell :: ConvexPolygon -> PowerCellDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularConflictDescent :: Set (RegularFaceKey label) -> Set (RegularFaceKey label) -> Set (RegularFaceKey label) -> Maybe (LocalRegularEditObstruction label) -> RegularConflictDescent label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularEdge :: label -> label -> PowerDualEdge -> RegularEdge label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularEdgeSection :: RegularEdge label -> Set (RegularFaceKey label) -> RegularEdgeSection label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularEditResult :: RegularTriangulation label -> Set label -> Vector (RegularSiteTransition label) -> RegularEditResult label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularEditSiteConflict :: label -> PowerSite label -> PowerSite label -> RegularEditError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularEditTopologyFailed :: RegularTopologyError label -> RegularEditError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularEditUnknownSites :: NonEmpty label -> RegularEditError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularFace :: label -> label -> label -> ExactPoint -> RegularFace label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularFacePatch :: Map (RegularFaceKey label) (RegularFace label) -> Map (RegularEdgeKey label) (RegularEdgeSection label) -> Map label (RegularSiteStar label) -> Set label -> Set (RegularEdgeKey label) -> RegularFacePatch label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularLocality :: RegularFaceKey label -> Map label (RegularFaceKey label) -> Map (RegularFaceKey label) (Set label) -> RegularLocality label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularSection :: Map label (ExactPowerGenerator label) -> Map (ExactRational, ExactRational) label -> Map label (CoincidentGeneratorDisposition label) -> Map label (RegularSiteDisposition label) -> Map (RegularFaceKey label) (RegularFace label) -> Map (RegularEdgeKey label) (RegularEdgeSection label) -> Map label (RegularSiteStar label) -> GeneratorRegularReceipt -> Maybe (RegularLocality label) -> RegularSection label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularSiteAppeared :: label -> RegularSiteDisposition label -> RegularSiteTransition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularSiteCoincidentDominatedBy :: label -> RegularSiteDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularSiteCoincidentEquivalentTo :: label -> RegularSiteDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularSiteDisappeared :: label -> RegularSiteDisposition label -> RegularSiteTransition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularSiteHidden :: RegularSiteDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularSiteLowerDimensional :: RegularSiteDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularSiteStar :: Set (RegularFaceKey label) -> Set label -> RegularSiteStar label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularSiteTransitioned :: label -> RegularSiteDisposition label -> RegularSiteDisposition label -> RegularSiteTransition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularSiteVisible :: RegularSiteDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularSlopeInside :: RegularFaceKey label -> RegularSlopeLocation label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularSlopeOutside :: RegularEdgeKey label -> RegularSlopeLocation label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularTriangulation :: Map label (PowerSite label) -> Maybe (RegularSection label) -> RegularTriangulation label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: RegularTriangulationReceipt :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> RegularTriangulationReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: ResolvedGeneratorSection :: Map label (CoincidentGeneratorDisposition label) -> GeneratorRegularTopology label -> ResolvedGeneratorSection label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: UnboundedPowerDual :: ExactRay -> PowerDualEdge
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: UpperEnvelopeEmptyForms :: UpperEnvelopeError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: UpperEnvelopePowerConstructionFailed :: PowerDiagramError label -> UpperEnvelopeError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: UpperEnvelopeWindowHullFailed :: MinkowskiError -> UpperEnvelopeError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: UpperEnvelopeWindowOverlayFailed :: OverlayError (Maybe label) Bool -> UpperEnvelopeError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [affineFormConstant] :: AffineForm -> ExactRational
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [affineFormXCoefficient] :: AffineForm -> ExactRational
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [affineFormYCoefficient] :: AffineForm -> ExactRational
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [conflictObstruction] :: RegularConflictDescent label -> Maybe (LocalRegularEditObstruction label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [conflictPendingFaces] :: RegularConflictDescent label -> Set (RegularFaceKey label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [conflictVisibleFaces] :: RegularConflictDescent label -> Set (RegularFaceKey label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [conflictVisitedFaces] :: RegularConflictDescent label -> Set (RegularFaceKey label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [localityFaceSeed] :: RegularLocality label -> RegularFaceKey label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [localityHiddenSupport] :: RegularLocality label -> Map label (RegularFaceKey label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [localitySupportHidden] :: RegularLocality label -> Map (RegularFaceKey label) (Set label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [patchedRegularEdges] :: RegularFacePatch label -> Map (RegularEdgeKey label) (RegularEdgeSection label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [patchedRegularFaces] :: RegularFacePatch label -> Map (RegularFaceKey label) (RegularFace label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [patchedRegularLabels] :: RegularFacePatch label -> Set label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [patchedRegularStars] :: RegularFacePatch label -> Map label (RegularSiteStar label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [patchedRegularTouchedEdges] :: RegularFacePatch label -> Set (RegularEdgeKey label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramActiveBoundaries] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramBoundaryCompatibilityChecks] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramCoincidentDominatedCells] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramCoincidentEquivalentCells] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramDomainVertices] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramEmptyCells] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramExactIntersections] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramFinalCoordinateBits] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramFinalDenominatorBits] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramLowerDimensionalCells] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramMaximumAffineCoefficientBits] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramMaximumCellConstraints] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramMaximumInputBits] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramOracleCells] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramPeakIntermediateCoordinateBits] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramPublishedCells] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramRegularEdges] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramRegularFaces] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerDiagramSubmittedSiteConstraints] :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerSiteLabel] :: PowerSite label -> label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerSiteQueryPoint] :: PowerSite label -> QueryPoint
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [powerSiteWeight] :: PowerSite label -> PowerWeight
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [regularEditChangedSites] :: RegularEditResult label -> Set label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [regularEditTransitions] :: RegularEditResult label -> Vector (RegularSiteTransition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [regularEditTriangulation] :: RegularEditResult label -> RegularTriangulation label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [regularTriangulationCoincidentSites] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [regularTriangulationEdges] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [regularTriangulationFaces] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [regularTriangulationHiddenSites] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [regularTriangulationInputSites] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [regularTriangulationLowerDimensionalSites] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [regularTriangulationRepresentativeSites] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [regularTriangulationVisibleSites] :: RegularTriangulationReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [resolvedCoincidentDispositions] :: ResolvedGeneratorSection label -> Map label (CoincidentGeneratorDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [resolvedRegularTopology] :: ResolvedGeneratorSection label -> GeneratorRegularTopology label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [sectionCoincidentDispositions] :: RegularSection label -> Map label (CoincidentGeneratorDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [sectionGenerators] :: RegularSection label -> Map label (ExactPowerGenerator label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [sectionIncidentFaces] :: RegularSiteStar label -> Set (RegularFaceKey label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [sectionRegularDispositions] :: RegularSection label -> Map label (RegularSiteDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [sectionRegularEdgeFaces] :: RegularEdgeSection label -> Set (RegularFaceKey label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [sectionRegularEdge] :: RegularEdgeSection label -> RegularEdge label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [sectionRegularEdges] :: RegularSection label -> Map (RegularEdgeKey label) (RegularEdgeSection label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [sectionRegularFaces] :: RegularSection label -> Map (RegularFaceKey label) (RegularFace label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [sectionRegularLocality] :: RegularSection label -> Maybe (RegularLocality label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [sectionRegularReceipt] :: RegularSection label -> GeneratorRegularReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [sectionRegularStars] :: RegularSection label -> Map label (RegularSiteStar label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [sectionSiteNeighbours] :: RegularSiteStar label -> Set label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [sectionSlopeRepresentatives] :: RegularSection label -> Map (ExactRational, ExactRational) label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [storedRegularSection] :: RegularTriangulation label -> Maybe (RegularSection label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: [storedRegularSites] :: RegularTriangulation label -> Map label (PowerSite label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data AffineForm
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data CoincidentGeneratorDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data LocalRegularEditObstruction label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data PowerCellDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data PowerDiagramError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data PowerDiagramReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data PowerDualEdge
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data PowerSite label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data PowerWeightError
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data RegularConflictDescent label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data RegularEdge label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data RegularEdgeSection label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data RegularEditError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data RegularEditResult label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data RegularFace label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data RegularFacePatch label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data RegularLocality label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data RegularSection label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data RegularSiteDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data RegularSiteStar label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data RegularSiteTransition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data RegularSlopeLocation label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data RegularTriangulation label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data RegularTriangulationReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data ResolvedGeneratorSection label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: data UpperEnvelopeError label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: emptyRegularSiteStar :: RegularSiteStar label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: exactGeneratorSlope :: ExactPowerGenerator label -> (ExactRational, ExactRational)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: foldRegularFaceBoundary :: (label -> label -> label -> result -> result) -> result -> RegularFace label -> result
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.PowerDiagram.Model.AffineForm
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerDiagramReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerDualEdge
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerWeight
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerWeightError
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularTriangulationReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Model.BoundedPowerDiagram label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Model.CoincidentGeneratorDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerCellDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerDiagramError label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerSite label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularEdge label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularEdgeSection label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularEditError label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularEditResult label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularFace label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularLocality label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSection label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSiteDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSiteStar label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSiteTransition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularTriangulation label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Model.ResolvedGeneratorSection label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.Internal.PowerDiagram.Model.UpperEnvelopeError label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.PowerDiagram.Model.AffineForm
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerDiagramReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerDualEdge
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerWeight
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerWeightError
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularTriangulationReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Model.BoundedPowerDiagram label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Model.CoincidentGeneratorDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerCellDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerDiagramError label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerSite label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularEdge label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularEdgeSection label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularEditError label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularEditResult label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularFace label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularLocality label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSection label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSiteDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSiteStar label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSiteTransition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularTriangulation label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Model.ResolvedGeneratorSection label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.Internal.PowerDiagram.Model.UpperEnvelopeError label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.PowerDiagram.Model.AffineForm
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerDiagramReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerDualEdge
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerWeight
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerWeightError
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularTriangulationReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.PowerDiagram.Model.CoincidentGeneratorDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerCellDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerSite label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularEdge label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularEdgeSection label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularFace label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSiteDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSiteStar label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Classes.Ord label => GHC.Internal.Classes.Ord (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSiteTransition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Model.BoundedPowerDiagram label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Model.CoincidentGeneratorDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerCellDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerDiagramError label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerSite label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularEdge label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularEdgeSection label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularEditError label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularEditResult label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularFace label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularLocality label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSection label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSiteDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSiteStar label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSiteTransition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularTriangulation label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Model.ResolvedGeneratorSection label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.Internal.PowerDiagram.Model.UpperEnvelopeError label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.PowerDiagram.Model.AffineForm
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerDiagramReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerDualEdge
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerWeight
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerWeightError
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularTriangulationReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.PowerDiagram.Model.AffineForm
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerDiagramReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerDualEdge
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerWeight
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerWeightError
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularTriangulationReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Model.BoundedPowerDiagram label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Model.CoincidentGeneratorDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerCellDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerDiagramError label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Model.PowerSite label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularEdge label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularEdgeSection label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularEditError label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularEditResult label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularFace label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularLocality label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSection label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSiteDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSiteStar label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularSiteTransition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Model.RegularTriangulation label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Model.ResolvedGeneratorSection label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.Internal.PowerDiagram.Model.UpperEnvelopeError label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: newtype BoundedPowerDiagram label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: newtype PowerWeight
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: powerDiagramFinalCoordinateBitGrowth :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: powerDiagramInputSites :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: powerDiagramPeakIntermediateBitGrowth :: PowerDiagramReceipt -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: powerSite :: label -> Point -> PowerWeight -> Either (PowerDiagramError label) (PowerSite label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: powerSiteExactPosition :: PowerSite label -> ExactPoint
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: powerSitePosition :: PowerSite label -> Point
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: powerWeight :: Double -> Either PowerWeightError PowerWeight
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: powerWeightExact :: PowerWeight -> ExactRational
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: powerWeightFromExact :: ExactRational -> PowerWeight
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: regularDispositionSection :: RegularTriangulation label -> Map label (RegularSiteDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: regularEdgeDual :: RegularEdge label -> PowerDualEdge
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: regularEdgeKey :: Ord label => RegularEdge label -> RegularEdgeKey label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: regularEdgeLabels :: RegularEdge label -> (label, label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: regularFaceCanonicalEdgeKeys :: Ord label => RegularFace label -> [RegularEdgeKey label]
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: regularFaceDirectedEdgeKeys :: RegularFace label -> [RegularEdgeKey label]
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: regularFaceDualPoint :: RegularFace label -> ExactPoint
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: regularFaceEdgeIncidence :: Ord label => RegularFace label -> Map (RegularEdgeKey label) (Set (RegularFaceKey label))
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: regularFaceKey :: Ord label => RegularFace label -> RegularFaceKey label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: regularFaceLabelSet :: Ord label => RegularFace label -> Set label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: regularFaceLabels :: RegularFace label -> (label, label, label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: regularFaceNeighbours :: Ord label => RegularSection label -> RegularFaceKey label -> Either (LocalRegularEditObstruction label) (Set (RegularFaceKey label))
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: replaceRegularSiteSection :: Map label (PowerSite label) -> RegularSection label -> RegularTriangulation label -> RegularTriangulation label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: requireFaceEdgeWitness :: Ord label => RegularEdgeKey label -> RegularFace label -> Either (LocalRegularEditObstruction label) (label, label, label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: requireFaceFromMap :: Ord label => Map (RegularFaceKey label) (RegularFace label) -> RegularFaceKey label -> Either (LocalRegularEditObstruction label) (RegularFace label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: requireGeneratorFromMap :: Ord label => Map label (ExactPowerGenerator label) -> label -> Either (LocalRegularEditObstruction label) (ExactPowerGenerator label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: sortedRegularFaceLabels :: Ord label => label -> label -> label -> RegularFaceKey label
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: type RegularEdgeKey label = (label, label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Model: type RegularFaceKey label = (label, label, label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Projection: boundedPowerDiagram :: Ord label => ConvexPolygon -> NonEmpty (PowerSite label) -> Either (PowerDiagramError label) (BoundedPowerDiagram label, PowerDiagramReceipt)
+ Moonlight.Triangulation.Internal.PowerDiagram.Projection: boundedPowerDiagramFromRegular :: Ord label => ConvexPolygon -> RegularTriangulation label -> Either (PowerDiagramError label) (BoundedPowerDiagram label, PowerDiagramReceipt)
+ Moonlight.Triangulation.Internal.PowerDiagram.Projection: powerCellDisposition :: Ord label => label -> BoundedPowerDiagram label -> Maybe (PowerCellDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Projection: powerCellDispositions :: BoundedPowerDiagram label -> [(label, PowerCellDisposition label)]
+ Moonlight.Triangulation.Internal.PowerDiagram.Projection: powerDiagramPlanarLayer :: Ord label => BoundedPowerDiagram label -> PlanarLayer (Maybe label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Projection: upperEnvelope :: Ord label => PolygonComponent -> Map label AffineForm -> Either (UpperEnvelopeError label) (PlanarLayer (Maybe label))
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: addIncidentFace :: Ord label => RegularFaceKey label -> RegularSiteStar label -> RegularSiteStar label
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: addNeighbour :: Ord label => label -> RegularSiteStar label -> RegularSiteStar label
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: canonicalRegularFace :: Ord label => label -> label -> label -> ExactPoint -> RegularFace label
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: chooseCoincidentRepresentative :: Ord label => ExactPowerGenerator label -> ExactPowerGenerator label -> ExactPowerGenerator label
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: classifyCoincidentGenerator :: ExactPowerGenerator label -> ExactPowerGenerator label -> CoincidentGeneratorDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: classifyCoincidentValue :: Eq value => label -> value -> value -> CoincidentGeneratorDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: coincidentPowerDisposition :: CoincidentGeneratorDisposition label -> PowerCellDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: coincidentRegularDisposition :: CoincidentGeneratorDisposition label -> RegularSiteDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: compareCoincidentPriority :: (Ord label, Ord value) => (value, label) -> (value, label) -> Ordering
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: constructRegularTriangulation :: Ord label => Map label (PowerSite label) -> Either (RegularTopologyError label) (RegularTriangulation label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: emptyGeneratorRegularReceipt :: GeneratorRegularReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: emptyRegularStars :: Map label disposition -> Map label (RegularSiteStar label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: emptyRegularTriangulation :: RegularTriangulation label
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: emptyRegularTriangulationReceipt :: RegularTriangulationReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: groupGeneratorsBySlope :: NonEmpty (ExactPowerGenerator label) -> NonEmpty (NonEmpty (ExactPowerGenerator label))
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: insertRegularEdgeStar :: Ord label => Map label (RegularSiteStar label) -> RegularEdgeSection label -> Map label (RegularSiteStar label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: insertRegularFaceStar :: Ord label => Map label (RegularSiteStar label) -> RegularFaceKey label -> RegularFace label -> Map label (RegularSiteStar label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: maximumPowerSiteInputBits :: Map label (PowerSite label) -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: powerSiteInputBitWidth :: PowerSite label -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: prepareExactPowerGenerator :: PowerSite label -> (ExactPowerGenerator label, Int)
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: publishGeneratorDisposition :: RegularGeneratorDisposition -> RegularSiteDisposition label
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: publishGeneratorDualGeometry :: GeneratorDualGeometry -> PowerDualEdge
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: publishGeneratorEdge :: GeneratorRegularEdge label -> RegularEdge label
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: publishGeneratorFace :: Ord label => GeneratorRegularFace label -> RegularFace label
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: publishRegularSection :: Ord label => Map label (PowerSite label) -> ResolvedGeneratorSection label -> RegularTriangulation label
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: publishRepresentativeDispositions :: Ord label => GeneratorRegularTopology label -> Map label (RegularSiteDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: regularEdges :: RegularTriangulation label -> [RegularEdge label]
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: regularFaces :: RegularTriangulation label -> [RegularFace label]
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: regularNeighbours :: Ord label => label -> RegularTriangulation label -> Set label
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: regularReceiptFromSection :: Int -> RegularSection label -> RegularTriangulationReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: regularSite :: Ord label => label -> RegularTriangulation label -> Maybe (PowerSite label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: regularSiteCount :: RegularTriangulation label -> Int
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: regularSiteDisposition :: Ord label => label -> RegularTriangulation label -> Maybe (RegularSiteDisposition label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: regularSiteStars :: Ord label => Map label disposition -> Map (RegularFaceKey label) (RegularFace label) -> Map (RegularEdgeKey label) (RegularEdgeSection label) -> Map label (RegularSiteStar label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: regularSites :: RegularTriangulation label -> [PowerSite label]
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: regularTriangulation :: Ord label => NonEmpty (PowerSite label) -> Either (PowerDiagramError label) (RegularTriangulation label, RegularTriangulationReceipt)
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: regularTriangulationReceipt :: RegularTriangulation label -> RegularTriangulationReceipt
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: resolveCoincidentGeneratorGroup :: Ord label => NonEmpty (ExactPowerGenerator label) -> (ExactPowerGenerator label, [(label, CoincidentGeneratorDisposition label)])
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: resolvedGeneratorSection :: Ord label => NonEmpty (ExactPowerGenerator label) -> Either (RegularTopologyError label) (ResolvedGeneratorSection label)
+ Moonlight.Triangulation.Internal.PowerDiagram.Section: validateAndSortSites :: Ord label => NonEmpty (PowerSite label) -> Either (PowerDiagramError label) (NonEmpty (PowerSite label))
+ Moonlight.Triangulation.Overlay: foldBoundedOverlayCells :: (accumulator -> (leftLabel, rightLabel) -> PolygonComponent -> accumulator) -> accumulator -> OverlayResult leftLabel rightLabel -> accumulator
+ Moonlight.Triangulation.PowerDiagram: PowerRegularDispositionMissing :: label -> PowerDiagramError label
+ Moonlight.Triangulation.PowerDiagram: PowerRegularGeneratorMissing :: label -> PowerDiagramError label
+ Moonlight.Triangulation.PowerDiagram: RegularEditResult :: RegularTriangulation label -> Set label -> Vector (RegularSiteTransition label) -> RegularEditResult label
+ Moonlight.Triangulation.PowerDiagram: RegularEditSiteConflict :: label -> PowerSite label -> PowerSite label -> RegularEditError label
+ Moonlight.Triangulation.PowerDiagram: RegularEditTopologyFailed :: RegularTopologyError label -> RegularEditError label
+ Moonlight.Triangulation.PowerDiagram: RegularEditUnknownSites :: NonEmpty label -> RegularEditError label
+ Moonlight.Triangulation.PowerDiagram: RegularSiteAppeared :: label -> RegularSiteDisposition label -> RegularSiteTransition label
+ Moonlight.Triangulation.PowerDiagram: RegularSiteDisappeared :: label -> RegularSiteDisposition label -> RegularSiteTransition label
+ Moonlight.Triangulation.PowerDiagram: RegularSiteTransitioned :: label -> RegularSiteDisposition label -> RegularSiteDisposition label -> RegularSiteTransition label
+ Moonlight.Triangulation.PowerDiagram: [regularEditChangedSites] :: RegularEditResult label -> Set label
+ Moonlight.Triangulation.PowerDiagram: [regularEditTransitions] :: RegularEditResult label -> Vector (RegularSiteTransition label)
+ Moonlight.Triangulation.PowerDiagram: [regularEditTriangulation] :: RegularEditResult label -> RegularTriangulation label
+ Moonlight.Triangulation.PowerDiagram: boundedPowerDiagramFromRegular :: Ord label => ConvexPolygon -> RegularTriangulation label -> Either (PowerDiagramError label) (BoundedPowerDiagram label, PowerDiagramReceipt)
+ Moonlight.Triangulation.PowerDiagram: data RegularEditError label
+ Moonlight.Triangulation.PowerDiagram: data RegularEditResult label
+ Moonlight.Triangulation.PowerDiagram: data RegularSiteTransition label
+ Moonlight.Triangulation.PowerDiagram: emptyRegularTriangulation :: RegularTriangulation label
+ Moonlight.Triangulation.PowerDiagram: insertRegularSite :: Ord label => PowerSite label -> RegularTriangulation label -> Either (RegularEditError label) (RegularEditResult label)
+ Moonlight.Triangulation.PowerDiagram: powerSiteExactPosition :: PowerSite label -> ExactPoint
+ Moonlight.Triangulation.PowerDiagram: powerWeightFromExact :: ExactRational -> PowerWeight
+ Moonlight.Triangulation.PowerDiagram: regularSite :: Ord label => label -> RegularTriangulation label -> Maybe (PowerSite label)
+ Moonlight.Triangulation.PowerDiagram: regularSiteCount :: RegularTriangulation label -> Int
+ Moonlight.Triangulation.PowerDiagram: regularSites :: RegularTriangulation label -> [PowerSite label]
+ Moonlight.Triangulation.PowerDiagram: regularTriangulationReceipt :: RegularTriangulation label -> RegularTriangulationReceipt
+ Moonlight.Triangulation.PowerDiagram: removeRegularSite :: Ord label => label -> RegularTriangulation label -> Either (RegularEditError label) (RegularEditResult label)
+ Moonlight.Triangulation.PowerDiagram: reweightRegularSites :: Ord label => Map label PowerWeight -> RegularTriangulation label -> Either (RegularEditError label) (RegularEditResult label)
+ Moonlight.Triangulation.RegularAlpha: RegularAlphaComplexInvalid :: PlanarComplexError label -> RegularAlphaError label
+ Moonlight.Triangulation.RegularAlpha: RegularAlphaDualMismatch :: PlanarSimplex label -> ExactRational -> ExactRational -> RegularAlphaError label
+ Moonlight.Triangulation.RegularAlpha: RegularAlphaProjectionFailed :: PlanarSimplex label -> ExactArithmeticError -> RegularAlphaError label
+ Moonlight.Triangulation.RegularAlpha: RegularAlphaSimplexInvalid :: PlanarSimplexError label -> RegularAlphaError label
+ Moonlight.Triangulation.RegularAlpha: RegularAlphaSiteMissing :: label -> RegularAlphaError label
+ Moonlight.Triangulation.RegularAlpha: RegularAlphaSublevelInvalid :: PlanarComplexError label -> RegularAlphaError label
+ Moonlight.Triangulation.RegularAlpha: RegularAlphaVertexBirthMissing :: label -> RegularAlphaError label
+ Moonlight.Triangulation.RegularAlpha: data PowerAlphaBirth
+ Moonlight.Triangulation.RegularAlpha: data RegularAlphaError label
+ Moonlight.Triangulation.RegularAlpha: data RegularAlphaFiltration label
+ Moonlight.Triangulation.RegularAlpha: instance Control.DeepSeq.NFData Moonlight.Triangulation.RegularAlpha.PowerAlphaBirth
+ Moonlight.Triangulation.RegularAlpha: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.RegularAlpha.RegularAlphaError label)
+ Moonlight.Triangulation.RegularAlpha: instance Control.DeepSeq.NFData label => Control.DeepSeq.NFData (Moonlight.Triangulation.RegularAlpha.RegularAlphaFiltration label)
+ Moonlight.Triangulation.RegularAlpha: instance GHC.Internal.Classes.Eq Moonlight.Triangulation.RegularAlpha.PowerAlphaBirth
+ Moonlight.Triangulation.RegularAlpha: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.RegularAlpha.RegularAlphaError label)
+ Moonlight.Triangulation.RegularAlpha: instance GHC.Internal.Classes.Eq label => GHC.Internal.Classes.Eq (Moonlight.Triangulation.RegularAlpha.RegularAlphaFiltration label)
+ Moonlight.Triangulation.RegularAlpha: instance GHC.Internal.Classes.Ord Moonlight.Triangulation.RegularAlpha.PowerAlphaBirth
+ Moonlight.Triangulation.RegularAlpha: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.RegularAlpha.RegularAlphaError label)
+ Moonlight.Triangulation.RegularAlpha: instance GHC.Internal.Generics.Generic (Moonlight.Triangulation.RegularAlpha.RegularAlphaFiltration label)
+ Moonlight.Triangulation.RegularAlpha: instance GHC.Internal.Generics.Generic Moonlight.Triangulation.RegularAlpha.PowerAlphaBirth
+ Moonlight.Triangulation.RegularAlpha: instance GHC.Internal.Show.Show Moonlight.Triangulation.RegularAlpha.PowerAlphaBirth
+ Moonlight.Triangulation.RegularAlpha: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.RegularAlpha.RegularAlphaError label)
+ Moonlight.Triangulation.RegularAlpha: instance GHC.Internal.Show.Show label => GHC.Internal.Show.Show (Moonlight.Triangulation.RegularAlpha.RegularAlphaFiltration label)
+ Moonlight.Triangulation.RegularAlpha: powerAlphaBirthDenominator :: PowerAlphaBirth -> Integer
+ Moonlight.Triangulation.RegularAlpha: powerAlphaBirthExact :: PowerAlphaBirth -> ExactRational
+ Moonlight.Triangulation.RegularAlpha: powerAlphaBirthNumerator :: PowerAlphaBirth -> Integer
+ Moonlight.Triangulation.RegularAlpha: regularAlphaBirths :: RegularAlphaFiltration label -> Map (PlanarSimplex label) PowerAlphaBirth
+ Moonlight.Triangulation.RegularAlpha: regularAlphaComplex :: RegularAlphaFiltration label -> PlanarComplex label
+ Moonlight.Triangulation.RegularAlpha: regularAlphaComplexAtBirth :: Ord label => PowerAlphaBirth -> RegularAlphaFiltration label -> Either (RegularAlphaError label) (PlanarComplex label)
+ Moonlight.Triangulation.RegularAlpha: regularAlphaCriticalBirths :: RegularAlphaFiltration label -> [PowerAlphaBirth]
+ Moonlight.Triangulation.RegularAlpha: regularAlphaFiltration :: Ord label => RegularTriangulation label -> Either (RegularAlphaError label) (RegularAlphaFiltration label)
+ Moonlight.Triangulation.RegularAlpha: regularAlphaSimplexBirth :: Ord label => PlanarSimplex label -> RegularAlphaFiltration label -> Maybe PowerAlphaBirth
- Moonlight.Triangulation: RegularTriangulationReceipt :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> RegularTriangulationReceipt
+ Moonlight.Triangulation: RegularTriangulationReceipt :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> RegularTriangulationReceipt
- Moonlight.Triangulation: inCircle :: Point -> Point -> Point -> Point -> Ordering
+ Moonlight.Triangulation: inCircle :: QueryPoint -> QueryPoint -> QueryPoint -> QueryPoint -> Ordering
- Moonlight.Triangulation: orient2d :: Point -> Point -> Point -> Ordering
+ Moonlight.Triangulation: orient2d :: QueryPoint -> QueryPoint -> QueryPoint -> Ordering
- Moonlight.Triangulation: powerDiagramPlanarLayer :: Ord label => label -> BoundedPowerDiagram label -> Either (PowerDiagramError label) (PlanarLayer label)
+ Moonlight.Triangulation: powerDiagramPlanarLayer :: Ord label => BoundedPowerDiagram label -> PlanarLayer (Maybe label)
- Moonlight.Triangulation: segmentRelation :: Point -> Point -> Point -> Point -> SegmentRelation
+ Moonlight.Triangulation: segmentRelation :: QueryPoint -> QueryPoint -> QueryPoint -> QueryPoint -> SegmentRelation
- Moonlight.Triangulation: segmentsIntersect :: Point -> Point -> Point -> Point -> Bool
+ Moonlight.Triangulation: segmentsIntersect :: QueryPoint -> QueryPoint -> QueryPoint -> QueryPoint -> Bool
- Moonlight.Triangulation: segmentsProperlyCross :: Point -> Point -> Point -> Point -> Bool
+ Moonlight.Triangulation: segmentsProperlyCross :: QueryPoint -> QueryPoint -> QueryPoint -> QueryPoint -> Bool
- Moonlight.Triangulation: sideQuery :: Point -> Point -> Point -> LineSideInfo
+ Moonlight.Triangulation: sideQuery :: QueryPoint -> QueryPoint -> QueryPoint -> LineSideInfo
- Moonlight.Triangulation.PowerDiagram: RegularTriangulationReceipt :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> RegularTriangulationReceipt
+ Moonlight.Triangulation.PowerDiagram: RegularTriangulationReceipt :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> Int -> RegularTriangulationReceipt
- Moonlight.Triangulation.PowerDiagram: powerDiagramPlanarLayer :: Ord label => label -> BoundedPowerDiagram label -> Either (PowerDiagramError label) (PlanarLayer label)
+ Moonlight.Triangulation.PowerDiagram: powerDiagramPlanarLayer :: Ord label => BoundedPowerDiagram label -> PlanarLayer (Maybe label)

Files

CHANGELOG.md view
@@ -10,6 +10,34 @@ The serialization format carries its own version tag, independent of the package version; any change to it is recorded here explicitly. +## Unreleased++## 1.5.0.0 - 2026-08-30++* Make the complete stable-labelled `PowerSite` section authoritative in+  `RegularTriangulation`. Add pure insertion, removal, and batch reweighting+  with exhaustive typed disposition transitions, plus bounded power-cell+  construction that reuses an already normalized regular value.+* Collapse edit publication to changed-site support plus exact disposition+  transitions. A normalized face/edge/star section supports exact slope walks,+  insertion conflict cavities, interior removal cavities, and atomic reweight+  composition. Local publication certifies incidence, cavity boundary, and+  lifted convexity; unsupported degeneracies reconstruct once through the exact+  batch owner. On the retained GHC 9.14.1 `-O1` 512-site fixture, full-result+  insertion falls from 250.7 ms / 304.7 MB to 0.53 ms / 0.61 MB, removal from+  246.4 ms / 303.5 MB to 0.72 ms / 0.87 MB, and a general one-site reweight+  from 99.4 ms / 119.2 MB to 22.8 ms / 34.2 MB. A common shift of all 512+  weights takes 0.22 ms / 0.27 MB without changing incidence. Remove the+  transient peak-hull counter+  from the persistent topology receipt: unlike its remaining fields, it was a+  construction-history measurement rather than a property of the value.+* Make batch-reweight validation proportional to the patch rather than the+  resident site set, and descend changed sites without an association-list+  copy.+* Add signed exact weighted-alpha filtration over resident regular topology, a+  generic labelled planar-complex lowering into Homology, and focused weighted+  preparation and persistence benchmark lanes.+ ## 1.4.0.5 - 2026-08-29  * Add the GHC-9.14 `zigzag` sublibrary. Stable labelled activation points become
README.md view
@@ -62,10 +62,14 @@ | `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` |+| `insertRegularSite` / `removeRegularSite` | A persistent regular value gains or loses one stable-labelled site | `PowerSite` / label and `RegularTriangulation` | New regular value, changed-site support, and exact disposition transitions / `RegularEditError` |+| `reweightRegularSites` | Stable sites receive one optimizer or simulation weight update | `Map label PowerWeight`, `RegularTriangulation` | New regular value, changed-site support, and exact disposition transitions / `RegularEditError` | | `boundedPowerDiagram` | Labelled weighted sites need exact cells inside one finite convex domain | `ConvexPolygon`, nonempty `PowerSite` family | Total labelled dispositions plus rational-width receipt / `PowerDiagramError` |+| `boundedPowerDiagramFromRegular` | An admitted regular value already owns the weighted-site topology | `ConvexPolygon`, `RegularTriangulation` | Exact bounded cells without rebuilding the lifted hull / `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` |+| `regularAlphaFiltration` | Weighted regular topology needs signed exact births | `RegularTriangulation` | `RegularAlphaFiltration` / `RegularAlphaError` | | `fromExactCellSet` | Exact cells need a generic complex view | `ExactCellSet` | `DCELComplex` | | `filteredAlphaComplex` | Alpha births must enter persistence | `AlphaFiltration` | Filtered chain complex / `DCELError` | | `activationAlphaZigzag` | Non-nested labelled depths need checked correspondence | `NonEmpty (ActivationSlice depth label)` | Admitted complexes, adjacent-union witnesses, and checked zigzag / `ActivationZigzagError` |@@ -75,23 +79,33 @@ [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) · [Track topology across non-nested activation depths.](docs/activation-zigzag.md)+· [Compute weighted alpha persistence.](docs/weighted-alpha.md)  ## 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.+every admitted labelled site as its semantic value; visibility, regular faces,+and weighted-dual segments, rays, full lines, or collapsed degenerate edges are+sealed derived projections. Pure insertion, removal, and batch reweighting+return exhaustive typed disposition transitions. Exact conflict-cavity and+face-star descent update only affected topology; an internal typed refusal+falls back once to the canonical batch constructor. `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.+When a regular value is already available, `boundedPowerDiagramFromRegular`+reuses its normalized lifted hull.  `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.++[Edit and reweight the complete regular-site section.](docs/regular-site-algebra.md)+· [Lower weighted topology into exact persistence.](docs/weighted-alpha.md)  ## Foreign bindings 
bench/alpha/Main.hs view
@@ -10,6 +10,8 @@   , timedValue   ) import Data.Map.Strict qualified as Map+import Data.List.NonEmpty qualified as NonEmpty+import Data.Foldable (traverse_) import Data.Vector qualified as Vector import Moonlight.Homology.Boundary   ( degreeCardinality@@ -35,12 +37,29 @@   ) import Moonlight.Triangulation.BulkLoad (delaunayGeometry) import Moonlight.Triangulation.CellComplex (filteredAlphaComplex)-import Moonlight.Triangulation.Types (DelaunayTriangulation)+import Moonlight.Triangulation.LabelledComplex (filteredPlanarComplex)+import Moonlight.Triangulation.PowerDiagram+  ( PowerSite+  , RegularTriangulation+  , powerSite+  , powerWeight+  , regularTriangulation+  , regularTriangulationReceipt+  )+import Moonlight.Triangulation.RegularAlpha+  ( PowerAlphaBirth+  , regularAlphaBirths+  , regularAlphaComplex+  , regularAlphaFiltration+  )+import Moonlight.Triangulation.Types (DelaunayTriangulation, Point)  type PreparedAlpha = FilteredFiniteChainComplex AlphaBirth Int  main :: IO ()-main = benchmarkAlphaFiltration 64+main = do+  benchmarkAlphaFiltration 64+  traverse_ benchmarkWeightedAlphaFiltration [64, 512, 2048]  benchmarkAlphaFiltration :: Int -> IO () benchmarkAlphaFiltration siteCount = do@@ -90,7 +109,9 @@   filtration <- requireRight (alphaFiltration triangulation)   requireRight (filteredAlphaComplex filtration) -preparationReceipt :: PreparedAlpha -> (Int, Int, [Int])+preparationReceipt+  :: FilteredFiniteChainComplex filtration coefficient+  -> (Int, Int, [Int]) preparationReceipt filtered =   let finite = filteredBaseComplex filtered       HomologicalDegree maximumDegree = maxHomologicalDegree finite@@ -126,3 +147,57 @@     <> replicate       (max 0 (maximumDegree + 1 - length bettiVector))       0++type PreparedWeightedAlpha = FilteredFiniteChainComplex PowerAlphaBirth Int++benchmarkWeightedAlphaFiltration :: Int -> IO ()+benchmarkWeightedAlphaFiltration siteCount = do+  sites <-+    traverse prepareWeightedSite+      (zip [0 ..] (randomPoints 0x517cc1b727220a95 siteCount))+  submitted <-+    maybe (fail "weighted alpha benchmark requires at least one site") pure+      (NonEmpty.nonEmpty sites)+  regular <-+    timedProjection+      (benchmarkLabel siteCount "regular-topology")+      regularTriangulationReceipt+      (fst <$> requireRight (regularTriangulation submitted))+  filtered <-+    timedProjection+      (benchmarkLabel siteCount "weighted-filtered-complex")+      preparationReceipt+      (prepareWeightedAlpha regular)+  profiles <-+    timedValue+      (benchmarkLabel siteCount "weighted-persistence-critical-profile")+      ( do+          (_, bettiTable) <-+            requireRight (mod2PersistentPairsWithCriticalBettiTable filtered)+          pure (criticalBettiVectors bettiTable)+      )+  putStrLn+    ( benchmarkLabel siteCount "weighted-profile"+        <> ": critical-births="+        <> show (length (filteredCriticalValues filtered))+        <> " profiles="+        <> show (length profiles)+        <> " prepared-cells="+        <> show (preparationReceipt filtered)+    )++prepareWeightedSite :: (Int, Point) -> IO (PowerSite Int)+prepareWeightedSite (label, point) = do+  weight <- requireRight (powerWeight (fromIntegral (label `mod` 11 - 5) / 256))+  requireRight (powerSite label point weight)++prepareWeightedAlpha+  :: RegularTriangulation Int+  -> IO PreparedWeightedAlpha+prepareWeightedAlpha regular = do+  filtration <- requireRight (regularAlphaFiltration regular)+  requireRight+    ( filteredPlanarComplex+        (regularAlphaComplex filtration)+        (regularAlphaBirths filtration)+    )
bench/power/Moonlight/Triangulation/PowerBench.hs view
@@ -14,8 +14,12 @@ import Control.DeepSeq (force) import Control.Exception (evaluate) 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 qualified Data.Vector as Vector import GHC.Conc.Sync (getAllocationCounter, setAllocationCounter) import Moonlight.Triangulation   ( BoundedPowerDiagram@@ -24,9 +28,13 @@   , PowerDiagramError   , PowerDiagramReceipt   , PowerSite+  , PowerWeight+  , RegularEditError+  , RegularEditResult   , RegularTriangulation   , RegularTriangulationReceipt   , boundedPowerDiagram+  , boundedPowerDiagramFromRegular   , convexPolygon   , exactPoint   , powerDiagramActiveBoundaries@@ -52,8 +60,22 @@   , powerDiagramRegularFaces   , powerDiagramSubmittedSiteConstraints   , powerSite+  , powerSiteLabel+  , powerSitePosition+  , powerSiteWeight   , powerWeight+  , powerWeightExact+  , powerWeightFromExact+  , insertRegularSite+  , removeRegularSite+  , reweightRegularSites   , regularTriangulation+  , regularEditChangedSites+  , regularEditTriangulation+  , regularEditTransitions+  , regularSiteDisposition+  , regularSites+  , RegularSiteDisposition (RegularSiteHidden)   ) import System.Mem (performGC) import Test.Tasty.Bench (Benchmark, bench, bgroup, defaultMain, nf)@@ -68,8 +90,22 @@   { powerBenchLabel :: !String   , powerBenchDomain :: !ConvexPolygon   , powerBenchSites :: !(NonEmpty (PowerSite Int))+  , powerBenchRegular :: !(RegularTriangulation Int)+  , powerBenchInsertionBase :: !(RegularTriangulation Int)+  , powerBenchInsertedSite :: !(PowerSite Int)+  , powerBenchReweight :: !PowerWeight+  , powerBenchCommonShift :: !(Map.Map Int PowerWeight)+  , powerBenchCoincidentLabel :: !Int+  , powerBenchCoincidentRegular :: !(RegularTriangulation Int)+  , powerBenchCoincidentReweight :: !PowerWeight+  , powerBenchHiddenEdit :: !(Maybe HiddenEditFixture)   } +data HiddenEditFixture = HiddenEditFixture+  { hiddenEditLabel :: !Int+  , hiddenEditLowerWeight :: !PowerWeight+  }+ type PowerResult =   Either (PowerDiagramError Int) (BoundedPowerDiagram Int, PowerDiagramReceipt) @@ -81,8 +117,14 @@     (PowerDiagramError Int)     (RegularTriangulation Int, RegularTriangulationReceipt) +type RegularEditBenchmarkResult =+  Either (RegularEditError Int) (RegularEditResult Int)++type RegularEditObservation =+  Either (RegularEditError Int) (Int, Int, Maybe (RegularSiteDisposition Int))+ siteCounts :: [Int]-siteCounts = [16, 64, 128, 169, 256, 512]+siteCounts = [16, 64, 128, 169, 256, 512, 2048]  families :: [PowerBenchFamily] families = [WellConditioned, NearParallel, CollinearSlopes]@@ -107,12 +149,57 @@     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 ->+    Just nonEmptySites -> do+      regular <- requireRight (regularTriangulation nonEmptySites)+      insertionBaseSites <-+        case NonEmpty.nonEmpty (NonEmpty.init nonEmptySites) of+          Nothing -> fail "power edit benchmark requires at least two sites"+          Just baseSites -> pure baseSites+      insertionBase <- requireRight (regularTriangulation insertionBaseSites)+      replacementWeight <- requireRight (powerWeight 17)+      subordinateWeight <- requireRight (powerWeight (-1048576))+      subordinateReweight <- requireRight (powerWeight (-2097152))+      let representativeSite = NonEmpty.head nonEmptySites+          commonShift =+            Map.fromList+              [ ( powerSiteLabel site+                , powerWeightFromExact (powerWeightExact (powerSiteWeight site) + 1)+                )+              | site <- NonEmpty.toList nonEmptySites+              ]+      subordinateSite <-+        requireRight+          (powerSite siteCount (powerSitePosition representativeSite) subordinateWeight)+      subordinateInsertion <-+        requireRight (insertRegularSite subordinateSite (fst regular))+      let preparedRegular = fst regular+          hiddenEdit =+            fmap+              (\site ->+                 HiddenEditFixture+                   { hiddenEditLabel = powerSiteLabel site+                   , hiddenEditLowerWeight = subordinateWeight+                   })+              ( List.find+                  (\site ->+                     regularSiteDisposition (powerSiteLabel site) preparedRegular+                       == Just RegularSiteHidden)+                  (regularSites preparedRegular)+              )       pure         PowerBenchCase           { powerBenchLabel = familyLabel family <> "/n=" <> show siteCount           , powerBenchDomain = domain           , powerBenchSites = nonEmptySites+          , powerBenchRegular = preparedRegular+          , powerBenchInsertionBase = fst insertionBase+          , powerBenchInsertedSite = NonEmpty.last nonEmptySites+          , powerBenchReweight = replacementWeight+          , powerBenchCommonShift = commonShift+          , powerBenchCoincidentLabel = powerSiteLabel subordinateSite+          , powerBenchCoincidentRegular = regularEditTriangulation subordinateInsertion+          , powerBenchCoincidentReweight = subordinateReweight+          , powerBenchHiddenEdit = hiddenEdit           }  fixturePoints :: PowerBenchFamily -> Int -> [Point]@@ -200,8 +287,53 @@   , bench       (powerBenchLabel benchmarkCase <> "/construct-diagram-only")       (nf constructDiagramOnly benchmarkCase)+  , bench+      (powerBenchLabel benchmarkCase <> "/clip-prepared-regular")+      (nf constructFromRegular benchmarkCase)+  , bench+      (powerBenchLabel benchmarkCase <> "/insert-local-observation")+      (nf observeInsertion benchmarkCase)+  , bench+      (powerBenchLabel benchmarkCase <> "/insert-and-normalize")+      (nf insertAndNormalize benchmarkCase)+  , bench+      (powerBenchLabel benchmarkCase <> "/remove-local-observation")+      (nf observeRemoval benchmarkCase)+  , bench+      (powerBenchLabel benchmarkCase <> "/remove-and-normalize")+      (nf removeAndNormalize benchmarkCase)+  , bench+      (powerBenchLabel benchmarkCase <> "/reweight-local-observation")+      (nf observeReweight benchmarkCase)+  , bench+      (powerBenchLabel benchmarkCase <> "/reweight-and-normalize")+      (nf reweightAndNormalize benchmarkCase)+  , bench+      (powerBenchLabel benchmarkCase <> "/common-weight-shift")+      (nf shiftEveryWeight benchmarkCase)   ]+    <> topologyPreservingBenchmarks benchmarkCase +topologyPreservingBenchmarks :: PowerBenchCase -> [Benchmark]+topologyPreservingBenchmarks benchmarkCase =+  [ bench+      (powerBenchLabel benchmarkCase <> "/remove-coincident-subordinate")+      (nf removeCoincidentSubordinate benchmarkCase)+  , bench+      (powerBenchLabel benchmarkCase <> "/reweight-coincident-subordinate")+      (nf reweightCoincidentSubordinate benchmarkCase)+  ]+    <> maybe [] hiddenBenchmarks (powerBenchHiddenEdit benchmarkCase)+ where+  hiddenBenchmarks hidden =+    [ bench+        (powerBenchLabel benchmarkCase <> "/remove-hidden")+        (nf (removeHidden benchmarkCase) hidden)+    , bench+        (powerBenchLabel benchmarkCase <> "/reweight-hidden-downward")+        (nf (reweightHiddenDownward benchmarkCase) hidden)+    ]+ construct :: PowerBenchCase -> PowerResult construct benchmarkCase =   boundedPowerDiagram@@ -213,6 +345,120 @@  constructRegularTopology :: PowerBenchCase -> RegularResult constructRegularTopology = regularTriangulation . powerBenchSites++constructFromRegular :: PowerBenchCase -> PowerResult+constructFromRegular benchmarkCase =+  boundedPowerDiagramFromRegular+    (powerBenchDomain benchmarkCase)+    (powerBenchRegular benchmarkCase)++insertAndNormalize+  :: PowerBenchCase+  -> RegularEditBenchmarkResult+insertAndNormalize benchmarkCase =+  insertRegularSite+    (powerBenchInsertedSite benchmarkCase)+    (powerBenchInsertionBase benchmarkCase)++observeInsertion+  :: PowerBenchCase+  -> RegularEditObservation+observeInsertion benchmarkCase =+  observeRegularEdit+    (powerSiteLabel (powerBenchInsertedSite benchmarkCase))+    (insertAndNormalize benchmarkCase)++removeAndNormalize+  :: PowerBenchCase+  -> RegularEditBenchmarkResult+removeAndNormalize benchmarkCase =+  removeRegularSite+    (powerSiteLabel (powerBenchInsertedSite benchmarkCase))+    (powerBenchRegular benchmarkCase)++observeRemoval+  :: PowerBenchCase+  -> RegularEditObservation+observeRemoval benchmarkCase =+  observeRegularEdit+    (powerSiteLabel (powerBenchInsertedSite benchmarkCase))+    (removeAndNormalize benchmarkCase)++reweightAndNormalize+  :: PowerBenchCase+  -> RegularEditBenchmarkResult+reweightAndNormalize benchmarkCase =+  reweightRegularSites+    ( Map.singleton+        (powerSiteLabel (powerBenchInsertedSite benchmarkCase))+        (powerBenchReweight benchmarkCase)+    )+    (powerBenchRegular benchmarkCase)++observeReweight+  :: PowerBenchCase+  -> RegularEditObservation+observeReweight benchmarkCase =+  observeRegularEdit+    (powerSiteLabel (powerBenchInsertedSite benchmarkCase))+    (reweightAndNormalize benchmarkCase)++observeRegularEdit+  :: Int+  -> RegularEditBenchmarkResult+  -> RegularEditObservation+observeRegularEdit focus =+  fmap+    (\editResult ->+       ( Set.size (regularEditChangedSites editResult)+       , Vector.length (regularEditTransitions editResult)+       , regularSiteDisposition+           focus+           (regularEditTriangulation editResult)+       ))++shiftEveryWeight+  :: PowerBenchCase+  -> RegularEditBenchmarkResult+shiftEveryWeight benchmarkCase =+  reweightRegularSites+    (powerBenchCommonShift benchmarkCase)+    (powerBenchRegular benchmarkCase)++removeCoincidentSubordinate+  :: PowerBenchCase+  -> RegularEditBenchmarkResult+removeCoincidentSubordinate benchmarkCase =+  removeRegularSite+    (powerBenchCoincidentLabel benchmarkCase)+    (powerBenchCoincidentRegular benchmarkCase)++reweightCoincidentSubordinate+  :: PowerBenchCase+  -> RegularEditBenchmarkResult+reweightCoincidentSubordinate benchmarkCase =+  reweightRegularSites+    ( Map.singleton+        (powerBenchCoincidentLabel benchmarkCase)+        (powerBenchCoincidentReweight benchmarkCase)+    )+    (powerBenchCoincidentRegular benchmarkCase)++removeHidden+  :: PowerBenchCase+  -> HiddenEditFixture+  -> RegularEditBenchmarkResult+removeHidden benchmarkCase fixture =+  removeRegularSite (hiddenEditLabel fixture) (powerBenchRegular benchmarkCase)++reweightHiddenDownward+  :: PowerBenchCase+  -> HiddenEditFixture+  -> RegularEditBenchmarkResult+reweightHiddenDownward benchmarkCase fixture =+  reweightRegularSites+    (Map.singleton (hiddenEditLabel fixture) (hiddenEditLowerWeight fixture))+    (powerBenchRegular benchmarkCase)  data SharedPowerAllocation = SharedPowerAllocation   { sharedFullAllocation :: !Integer
bench/spade-compare/hs/Main.hs view
@@ -104,13 +104,9 @@   SweepAngleCollapseLane -> do     points <- evaluate (force (V.fromList (nearCollinearPoints 0x9e3779b97f4a7c15 first)))     measure (buildTriangulation <$> require (delaunay unitElementDefaults points))-  -- Exactly collinear input, which never builds a face, so both loaders fall-  -- out of their sweep and degrade to plain incremental insertion for the-  -- whole load — and neither has a sub-linear answer for locating against a-  -- face-less mesh. This side scans every vertex and then every half-edge per-  -- insertion; spade collects and sorts all vertices per insertion. Both are-  -- quadratic in the load, so the ratio reports which quadratic costs more-  -- rather than whether one exists, and spade's carries the extra log factor.+  -- Exactly collinear input never builds a face. Moonlight recognizes that+  -- section after radial admission and glues its complete DCEL chain once;+  -- Spade still locates against the growing face-less mesh per insertion.   DegenerateLineLane -> do     points <- evaluate (force (V.fromList (exactlyCollinearPoints first)))     measure (buildTriangulation <$> require (delaunay unitElementDefaults points))
bench/spade-compare/hs/Moonlight/Triangulation/Bench/SpadeCompare/Gates.hs view
@@ -224,10 +224,8 @@   -- Spade timing rows. Their canonical egress is retained for the hard gate.   writePersistentGate directory 1000   writePersistentRemovalGate directory 10000 2500-  -- The remaining cliff lanes. Each times an entry point of this side that is-  -- expected to lose badly, which is worth nothing unless the two are first-  -- shown to compute the same thing — a quadratic path and a linear one that-  -- disagree are not a comparison.+  -- The remaining diagnostic gates. Timing is meaningful only after both+  -- sides have been shown to compute the same canonical result.   writeConstraintIncrementalGate directory 8000 800   writeConstraintSplitGate directory 1000   writeSweepAngleGate directory 2000
bench/spade-compare/hs/Moonlight/Triangulation/Bench/SpadeCompare/Hierarchy.hs view
@@ -8,6 +8,7 @@ import Moonlight.Triangulation import Moonlight.Triangulation.BulkLoad import Moonlight.Triangulation.HintGenerator+import Moonlight.Triangulation.Math (validatePoint) import Moonlight.Triangulation.Session import Moonlight.Triangulation.Bench.SpadeCompare.Support import Moonlight.Triangulation.Bench.SpadeCompare.Input (randomPoints)@@ -27,7 +28,11 @@   -> Point   -> Session s Point () () () HierarchyHint hierarchyStep hierarchy point = do-  (vertex, disposition) <- insertVertexAt point point+  query <- either refuse pure (validatePoint Nothing point)+  (vertex, disposition) <-+    case hierarchyHint hierarchy query of+      Just (VertexHint seed) -> insertVertexAtNearVertex seed point point+      _ -> insertVertexAt point point   either refuse pure (updateHierarchyAfterInsertion hierarchy point vertex disposition)  -- | Arrival-order insertion with the hierarchy maintained alongside it.
bench/spade-compare/hs/Moonlight/Triangulation/Bench/SpadeCompare/Interpolation.hs view
@@ -16,7 +16,8 @@ import Moonlight.Triangulation import Moonlight.Triangulation.Dcel import Moonlight.Triangulation.Interpolation-import Moonlight.Triangulation.Math+import Moonlight.Triangulation.Internal.Predicates qualified as Predicates+import Moonlight.Triangulation.Math (isFinite) import Moonlight.Triangulation.PointLocation import Moonlight.Triangulation.Types import Moonlight.Triangulation.Bench.SpadeCompare.Support@@ -93,7 +94,7 @@               let !pa = vertexPoint triangulation a                   !pb = vertexPoint triangulation b                   !pc = vertexPoint triangulation c-                  !verdict = inCircle pa pb pc query+                  !verdict = Predicates.inCircle pa pb pc query                   !margins' = if insideMargin pa pb pc query then margins + 1 else margins                in if verdict == GT                     then go (adjacent triangulation face ++ rest) (face : seen) (tests + 1) margins'@@ -266,7 +267,7 @@       case innerFaceVertices triangulation face of         Nothing -> False         Just (a, b, c) ->-          inCircle+          Predicates.inCircle             (vertexPoint triangulation a)             (vertexPoint triangulation b)             (vertexPoint triangulation c)
bench/spade-compare/hs/Moonlight/Triangulation/Bench/SpadeCompare/Intersection.hs view
@@ -15,16 +15,14 @@ -- | Query endpoints pulled inward. The generator fills [-1, 1]^2, so a point -- with both coordinates inside [-0.5, 0.5] is strictly inside the hull of a -- few thousand of them and locating it lands on a face. A chord that starts--- OUTSIDE the hull takes a different route on each side — spade walks the hull--- from the located hull edge while this side falls back to a scan over every--- element — so those chords are the "intersection-outside" cliff lane and are--- kept out of this one deliberately.+-- OUTSIDE the hull makes endpoint choice and local hull entry observable, so+-- those chords remain the separate "intersection-outside" cliff lane. interiorQueryPoints :: Word64 -> Int -> [Point] interiorQueryPoints seed count =   [Point (0.5 * x) (0.5 * y) | Point x y <- randomPoints seed count] --- | Query origins pushed well clear of the hull, so locating one is certain to--- report the outside of the convex hull rather than a face.+-- | Query origins pushed away from the dense centre so the corpus repeatedly+-- exercises outside-hull entry rather than only face-to-face corridors. exteriorQueryPoints :: Word64 -> Int -> [Point] exteriorQueryPoints seed count =   [Point (4 * x) (4 * y) | Point x y <- randomPoints seed count]
bench/spade-compare/hs/Moonlight/Triangulation/Bench/SpadeCompare/Lane.hs view
@@ -270,6 +270,7 @@   , parity VoronoiSweepLane 1000 0 "Voronoi dual sweep, 1k sites"   , parity DcelWalkLane 2000 0 "DCEL traversal, 2k points"   , parity IntersectionLane 10000 500 "Line intersection, 500 chords / 10k"+  , parity DegenerateLineLane 2000 0 "Exactly collinear bulk chain, 2k"   , cliff ConstraintIncrementalLane 8000 800 "Constraint one at a time, 800 / 8k"   , cliff ConstraintSplitLane 1000 0 "Constraint split, 1k crossings"   -- This deliberately stays at 1k while its parity twin also reaches 10k.@@ -277,7 +278,6 @@   , cliff HierarchyDuplicateLane 10000 500 "Hierarchy duplicate insert, 500 / 10k"   , cliff HierarchyRemovalLane 10000 250 "Hierarchy removal, 250 / 10k"   , cliff SweepAngleCollapseLane 2000 0 "Sweep hull index at one angle, 2k"-  , cliff DegenerateLineLane 2000 0 "Exactly collinear load, face-less locate, 2k"   , cliff IntersectionOutsideLane 2000 100 "Line intersection from outside the hull, 100 / 2k"   ]  where
bench/spade-compare/hs/Moonlight/Triangulation/Bench/SpadeCompare/Removal.hs view
@@ -13,7 +13,7 @@ import Moonlight.Triangulation import Moonlight.Triangulation.Dcel import Moonlight.Triangulation.Internal.HandleDefs-import Moonlight.Triangulation.Math+import Moonlight.Triangulation.Internal.Predicates qualified as Predicates import Moonlight.Triangulation.PointLocation import Moonlight.Triangulation.Removal import Moonlight.Triangulation.Session@@ -221,7 +221,7 @@                 else do                   let fanOrientations =                         zipWith3-                          (\index p q -> (index, p, q, orient2d fanOriginPoint p q))+                          (\index p q -> (index, p, q, Predicates.orient2d fanOriginPoint p q))                           [(1 :: Int) ..]                           ringTail                           (drop 1 ringTail)@@ -230,9 +230,9 @@                           ( \(index, priorPoint) current following ->                               ( index                               , current-                              , orient2d priorPoint following fanOriginPoint == GT-                                  && orient2d following priorPoint current == GT-                              , inCircle current fanOriginPoint priorPoint following+                              , Predicates.orient2d priorPoint following fanOriginPoint == GT+                                  && Predicates.orient2d following priorPoint current == GT+                              , Predicates.inCircle current fanOriginPoint priorPoint following                               )                           )                           (zip [(2 :: Int) ..] ringTail)@@ -356,7 +356,7 @@     [ (vertexPoint triangulation a, vertexPoint triangulation b, vertexPoint triangulation c)     | face <- innerFaces triangulation     , Just (a, b, c) <- [innerFaceVertices triangulation face]-    , orient2d+    , Predicates.orient2d         (vertexPoint triangulation a)         (vertexPoint triangulation b)         (vertexPoint triangulation c)@@ -388,7 +388,7 @@   isCounterClockwise face = case innerFaceVertices triangulation face of     Nothing -> True     Just (a, b, c) ->-      orient2d+      Predicates.orient2d         (vertexPoint triangulation a)         (vertexPoint triangulation b)         (vertexPoint triangulation c)@@ -397,7 +397,7 @@   edgeIsIllegal edge =     let twin = reverseEdge edge      in incidentFace triangulation twin /= outerFace-          && inCircle+          && Predicates.inCircle             (vertexPoint triangulation (origin triangulation edge))             (vertexPoint triangulation (destination triangulation edge))             (vertexPoint triangulation (destination triangulation (next triangulation edge)))
docs/README.md view
@@ -9,6 +9,8 @@ | 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) |+| How do weighted sites survive insertion, removal, and reweighting? | [Persistent regular-site algebra](./regular-site-algebra.md) |+| How do weighted regular cells become an exact alpha filtration? | [Exact weighted alpha filtration](./weighted-alpha.md) | | 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) |
+ docs/regular-site-algebra.md view
@@ -0,0 +1,86 @@+# Persistent regular-site algebra++`Moonlight.Triangulation.PowerDiagram` owns the complete weighted-site value.+Use this surface when site identity must survive visibility changes or repeated+weight updates.++## Construct and edit++A `PowerSite label` combines a stable identity, an admitted planar position,+and an exact signed `PowerWeight`. Construction retains every site, including+hidden and coincident sites:++```haskell+regularTriangulation sites++editRegular newSite oldLabel weightPatch regular = do+  inserted <- insertRegularSite newSite regular+  removed <-+    removeRegularSite oldLabel (regularEditTriangulation inserted)+  reweightRegularSites+    weightPatch+    (regularEditTriangulation removed)+```++Each edit returns `RegularEditResult`:++- `regularEditTriangulation` is the new authoritative value.+- `regularEditChangedSites` is the exact support changed by the edit; it is+  empty for idempotent insertion, removal, or reweighting.+- `regularEditTransitions` carries every resulting disposition transition.++Read structural counts from the returned value with+`regularTriangulationReceipt`; the edit result does not duplicate them.++`RegularSiteTransition` distinguishes appearance, disappearance, and a change+between any two `RegularSiteDisposition` values. Thus removing or down-weighting+a visible site can report a previously hidden site becoming visible without a+caller-maintained hidden-site table.++## Identity and obstructions++The label is site identity. Re-inserting the same labelled position and weight+is idempotent. Reusing a label for different geometry returns+`RegularEditSiteConflict`; changing only weights belongs in+`reweightRegularSites`. A batch weight patch is applied once and returns+`RegularEditUnknownSites` before construction if any label is absent.++Distinct labels may occupy the same position. Their exact affine forms remain+separate submissions, and the result explicitly classifies them as visible,+coincident-equivalent, or coincident-dominated rather than silently deleting an+identity.++`emptyRegularTriangulation` is the insertion identity and the lawful result of+removing the final site. `regularSite`, `regularSites`, and `regularSiteCount`+read the canonical site section; faces, edges, neighbours, and visibility are+derived from it.++## Reuse the normalized topology++```haskell+(diagram, receipt) <- boundedPowerDiagramFromRegular domain regular+```++This clips regular-neighbour inequalities without reconstructing the lifted+hull. `boundedPowerDiagram domain sites` remains the one-shot entrance when the+editable regular value is not otherwise needed.++Edits descend through a persistent exact index. Coincident subordinates and+hidden sites update directly. A distinct insertion walks the slope+triangulation, discovers its lifted conflict cavity, glues the exact horizon,+and reattaches only hidden generators supported by removed faces. Interior+removal retriangulates the removed face star from its boundary and attached+hidden candidates. A single general reweight composes removal with insertion+atomically; a uniform shift of every weight translates the resident affine+section without changing topology. Multi-site patches use the direct+topology-preserving cases when exact, and otherwise normalize once through the+batch owner rather than accumulating local edits.++Every local publication checks manifold incidence, exact cavity boundary, and+strict lifted convexity. Degenerate, boundary-changing, or unsupported cases+decline to the exact batch constructor used by `regularTriangulation`; no+uncertified local result is published. On the retained 512-site fixture this+collapses insertion and removal from whole-hull reconstruction to+sub-millisecond face-star work.++[Compute signed weighted alpha persistence from the same value.](./weighted-alpha.md)
+ docs/weighted-alpha.md view
@@ -0,0 +1,32 @@+# Exact weighted alpha filtration++`Moonlight.Triangulation.RegularAlpha` derives a signed exact alpha filtration+from the canonical `RegularTriangulation`. It reuses resident regular faces,+edges, weighted-dual points, rays, and lines; no second weighted geometry is+constructed.++```haskell+filtration <- regularAlphaFiltration regular+filtered <-+  filteredPlanarComplex+    (regularAlphaComplex filtration)+    (regularAlphaBirths filtration)+pairs <- mod2PersistentPairs filtered+```++`PowerAlphaBirth` is an opaque exact rational. Negative births are lawful: a+positive site weight can make a simplex present before power radius zero.+Hidden and coincident-subordinate sites remain in the editable regular value but+contribute no simplex. `filteredPlanarComplex` is the only lowering boundary;+Homology owns chain and filtration validation.++Use ordinary `alphaFiltration` for an unweighted Delaunay DCEL. Use+`regularAlphaFiltration` when weights, exact dual rays, or repeated regular-site+edits are semantic inputs.++```console+cabal bench moonlight-triangulation-alpha-bench+```++[Return to the operation index.](../README.md#operations)+· [Edit the regular site section.](./regular-site-algebra.md)
moonlight-triangulation.cabal view
@@ -1,6 +1,6 @@ cabal-version:       3.4 name:                moonlight-triangulation-version:             1.4.0.5+version:             1.5.0.0 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@@ -45,6 +45,8 @@   docs/README.md   docs/development.md   docs/activation-zigzag.md+  docs/regular-site-algebra.md+  docs/weighted-alpha.md   docs/examples/README.md   docs/mesh-publication.md   docs/category-observatory/README.md@@ -108,7 +110,7 @@ source-repository this   type:     git   location: https://github.com/PaleRoses/moonlight.git-  tag:      moonlight-triangulation-1.4.0.5+  tag:      moonlight-triangulation-1.5.0.0   subdir:   moonlight-triangulation  flag warnings-as-errors@@ -139,11 +141,13 @@     ghc-options: -optlo-enable-new-pm=0  -- ── the tower ────────────────────────────────────────────────────────────────--- The implementation tower is core <- dcel-internal <- build-internal. Public--- dcel and build components are restrictions of those owners: they reexport--- lawful modules without copying implementations or publishing trusted--- constructors. Dual branches directly from the DCEL owner; the apex owns only--- the genuine overlaps. The graph is acyclic and uses no @.hs-boot@ files.+-- The implementation tower separates the public exact scalar core from the+-- package-private paged-storage kernel. Both feed dcel-internal, followed by+-- build-internal. Public dcel and build components are restrictions of those+-- owners: they reexport lawful modules without copying implementations or+-- publishing trusted constructors. Dual branches directly from the DCEL owner;+-- the apex owns only the genuine overlaps. The graph is acyclic and uses no+-- @.hs-boot@ files.  library core   import: shared-properties@@ -154,6 +158,19 @@     Moonlight.Triangulation.LineSideInfo     Moonlight.Triangulation.Internal.Dyadic     Moonlight.Triangulation.Internal.ExactRational+  build-depends:+    base >= 4.19 && < 5+    , deepseq >= 1.5 && < 1.6+  ghc-options: -fexpose-all-unfoldings++-- Trusted paged storage is shared by the immutable DCEL and construction+-- owners. It is private because raw page indices and unsafe vector operations+-- are valid only after those owners have admitted array bounds and page order.+library storage-internal+  import: shared-properties+  visibility: private+  hs-source-dirs: src-core+  exposed-modules:     Moonlight.Triangulation.Internal.PageDirectory     Moonlight.Triangulation.Internal.Paged     Moonlight.Triangulation.Internal.BoxedPaged@@ -176,6 +193,7 @@     Moonlight.Triangulation.Types     Moonlight.Triangulation.Math     Moonlight.Triangulation.Exact+    Moonlight.Triangulation.Simplex     Moonlight.Triangulation.CellSet     Moonlight.Triangulation.Region     Moonlight.Triangulation.Valuation@@ -204,6 +222,7 @@     Moonlight.Triangulation.Internal.ExactSegmentEvents     Moonlight.Triangulation.Internal.Region.Publication     Moonlight.Triangulation.Internal.Region.Types+    Moonlight.Triangulation.Internal.Predicates     Moonlight.Triangulation.Internal.SegmentRelation     Moonlight.Triangulation.Internal.Types     Moonlight.Triangulation.Internal.Representation@@ -232,6 +251,7 @@     , vector >= 0.13 && < 0.14     , vector-algorithms >= 0.9 && < 0.10     , moonlight-triangulation:core+    , moonlight-triangulation:storage-internal   ghc-options: -fexpose-all-unfoldings  -- Public immutable-DCEL and exact-geometry surface. Every module is the module@@ -245,6 +265,7 @@     , Moonlight.Triangulation.Types     , Moonlight.Triangulation.Math     , Moonlight.Triangulation.Exact+    , Moonlight.Triangulation.Simplex     , Moonlight.Triangulation.CellSet     , Moonlight.Triangulation.Region     , Moonlight.Triangulation.Valuation@@ -279,6 +300,7 @@     TypeFamilies   exposed-modules:     Moonlight.Triangulation.CellComplex+    Moonlight.Triangulation.LabelledComplex   if impl(ghc < 9.14)     buildable: False   build-depends:@@ -359,6 +381,7 @@     , vector-algorithms >= 0.9 && < 0.10     , moonlight-triangulation:core     , moonlight-triangulation:dcel-internal+    , moonlight-triangulation:storage-internal   ghc-options: -fexpose-all-unfoldings  -- Public construction surface over the private kernel. Trusted admitted-input@@ -411,6 +434,7 @@     , vector >= 0.13 && < 0.14     , moonlight-triangulation:core     , moonlight-triangulation:dcel-internal+    , moonlight-triangulation:storage-internal   -- The @Binary@ instances for the identifier and point types are orphans by   -- construction: the types belong to @dcel@ and the class to @binary@, and the   -- whole purpose of this component is that neither one has to know about the@@ -456,6 +480,7 @@     , moonlight-triangulation:core     , moonlight-triangulation:dcel-internal     , moonlight-triangulation:build-internal+    , moonlight-triangulation:storage-internal  library   import: shared-properties@@ -467,6 +492,7 @@     Moonlight.Triangulation.Minkowski     Moonlight.Triangulation.Overlay     Moonlight.Triangulation.PowerDiagram+    Moonlight.Triangulation.RegularAlpha   -- 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.@@ -506,7 +532,13 @@   other-modules:     Moonlight.Triangulation.Internal.Minkowski.Convex     Moonlight.Triangulation.Internal.Minkowski.Types-    Moonlight.Triangulation.Internal.RegularTriangulation+    Moonlight.Triangulation.Internal.PowerDiagram.Edit+    Moonlight.Triangulation.Internal.PowerDiagram.Generator+    Moonlight.Triangulation.Internal.PowerDiagram.Hull+    Moonlight.Triangulation.Internal.PowerDiagram.Locality+    Moonlight.Triangulation.Internal.PowerDiagram.Model+    Moonlight.Triangulation.Internal.PowerDiagram.Projection+    Moonlight.Triangulation.Internal.PowerDiagram.Section   build-depends:     base >= 4.19 && < 5     , containers >= 0.8 && < 0.9@@ -517,6 +549,7 @@     , moonlight-triangulation:build-internal     , moonlight-triangulation:dual     , moonlight-triangulation:overlay-internal+    , moonlight-triangulation:storage-internal   ghc-options: -fexpose-all-unfoldings  library ffi@@ -553,7 +586,7 @@     moonlight_triangulation.h   build-depends:     base >= 4.19 && < 5-    , moonlight-triangulation:ffi >= 1.4 && < 1.5+    , moonlight-triangulation:ffi >= 1.5 && < 1.6   ghc-options: -threaded   if os(windows)     options: standalone@@ -571,7 +604,7 @@     base >= 4.19 && < 5     , directory >= 1.3 && < 1.4     , filepath >= 1.4 && < 1.6-    , moonlight-triangulation:ffi >= 1.4 && < 1.5+    , moonlight-triangulation:ffi >= 1.5 && < 1.6  -- ── test slices ────────────────────────────────────────────────────────────── @@ -616,6 +649,7 @@     , moonlight-triangulation:build-internal     , moonlight-triangulation:dual     , moonlight-triangulation:overlay-internal+    , moonlight-triangulation:storage-internal  common triangulation-serialization-test-slice   other-modules: Moonlight.Triangulation.SerializationSpec@@ -963,6 +997,7 @@     , moonlight-triangulation:core     , moonlight-triangulation:dcel-internal     , moonlight-triangulation:build-internal+    , moonlight-triangulation:storage-internal  common triangulation-region-benchmark-slice   other-modules: Moonlight.Triangulation.RegionBench@@ -1180,6 +1215,7 @@     , containers >= 0.6 && < 0.9     , deepseq >= 1.5 && < 1.6     , moonlight-homology >= 0.1.0.2 && < 0.2+    , moonlight-triangulation     , moonlight-triangulation:build-internal     , moonlight-triangulation:cell-complex     , moonlight-triangulation:dcel-internal@@ -1200,7 +1236,8 @@   other-modules:     Moonlight.Triangulation.PowerBench   build-depends:-    tasty-bench >= 0.3 && < 0.6+    containers >= 0.6 && < 0.9+    , 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
src-build/Moonlight/Triangulation/Insertion.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-}@@ -35,7 +34,8 @@   , addCounter   ) import Moonlight.Triangulation.Internal.Probe (KnownProbe, Probe (..))-import Moonlight.Triangulation.Math+import Moonlight.Triangulation.Internal.Predicates+import Moonlight.Triangulation.Math (validatePoint) import Moonlight.Triangulation.Types  -- | Admit a raw site, then delegate to the shared admitted-site interpreter.
src-build/Moonlight/Triangulation/Internal/BulkLoad.hs view
@@ -35,7 +35,12 @@ import qualified Data.Vector.Unboxed.Mutable as MUV import Data.Word (Word32) import Moonlight.Triangulation.Dcel (numInnerFaces, numVertices)-import Moonlight.Triangulation.Internal.HandleDefs (DirectedEdgeId (..), FaceId (..), VertexId (..))+import Moonlight.Triangulation.Internal.HandleDefs+  ( VertexId (..)+  , directedEdgeIdIndex+  , faceIdIndex+  , vertexIdIndex+  ) import Moonlight.Triangulation.Internal.BoxedPaged (boxedFromVector, boxedUpdate, emptyBoxedPaged) import Moonlight.Triangulation.Internal.Capacity (ensureCapacity) import Moonlight.Triangulation.Insertion (insertExistingVertexAtLocation, insertVertexAtPoint)@@ -66,7 +71,7 @@ import Moonlight.Triangulation.PointLocation (locatePointWithHint) import Moonlight.Triangulation.Internal.Probe (Probe (..)) import Moonlight.Triangulation.Internal.Representation (Triangulation (..))-import Moonlight.Triangulation.Internal.PackedIndex (noIndex)+import Moonlight.Triangulation.Internal.PackedIndex (noIndex, unpackIndex) import Moonlight.Triangulation.Internal.Paged (TransactionShape (DenseTransaction, LocalTransaction), emptyPaged, fromVector) import Moonlight.Triangulation.Internal.Transaction (runTransaction) import Moonlight.Triangulation.Types@@ -279,8 +284,8 @@   sumGeometryVertices dense vertices =     MUV.foldM'       (\(!sumX, !sumY) raw -> do-         x <- denseReadPointX dense (fromIntegral raw)-         y <- denseReadPointY dense (fromIntegral raw)+         x <- denseReadPointX dense (unpackIndex raw)+         y <- denseReadPointY dense (unpackIndex raw)          pure (sumX + x, sumY + y)       )       (0, 0)@@ -701,13 +706,13 @@                        let thawedLocation =                              case located of                                EmptyTriangulation -> MutableEmpty-                               OnEdge (DirectedEdgeId raw) -> MutableOnEdge (fromIntegral raw)-                               InFace (FaceId raw) -> MutableInFace (fromIntegral raw)+                               OnEdge edge -> MutableOnEdge (directedEdgeIdIndex edge)+                               InFace face -> MutableInFace (faceIdIndex face)                                -- The frozen locator emits no edge only for a                                -- singleton mesh. Its mutable interpreter ignores                                -- this sentinel while constructing the second vertex.                                OutsideConvexHull Nothing -> MutableOutsideHull 0-                               OutsideConvexHull (Just (DirectedEdgeId raw)) -> MutableOutsideHull (fromIntegral raw)+                               OutsideConvexHull (Just edge) -> MutableOutsideHull (directedEdgeIdIndex edge)                        ((vertex, Inserted) <$) <$> insertExistingVertexAtLocation @'ProbeOff mutable operation vertex thawedLocation              case inserted of                Left failure -> pure (Left failure)@@ -746,12 +751,12 @@   -> LocationStats   -> vertex   -> InsertionResult mode vertex directed undirected face-replaceResidentPayload triangulation resident@(VertexId raw) walked vertexData =+replaceResidentPayload triangulation resident walked vertexData =   InsertionResult     { insertionTriangulation =         triangulation           { triVertexData =-              boxedUpdate (fromIntegral raw) vertexData (triVertexData triangulation)+              boxedUpdate (vertexIdIndex resident) vertexData (triVertexData triangulation)           }     , insertionVertex = resident     , insertionDisposition = AlreadyPresent@@ -884,8 +889,8 @@   arena <- MUV.new count   forM_ [0 .. count - 1] $ \index -> do     raw <- lookupId index-    x <- readPointX mutable (fromIntegral raw)-    y <- readPointY mutable (fromIntegral raw)+    x <- readPointX mutable (unpackIndex raw)+    y <- readPointY mutable (unpackIndex raw)     let !wideX = x         !wideY = y         !deltaX = centerX - wideX
src-build/Moonlight/Triangulation/Internal/Cdt/Admission.hs view
@@ -12,9 +12,9 @@ import qualified Data.IntSet as IntSet import qualified Moonlight.Triangulation.Dcel as Dcel import Moonlight.Triangulation.Internal.HandleDefs+import Moonlight.Triangulation.Internal.Predicates (segmentsProperlyCross) import Moonlight.Triangulation.Internal.Representation import Moonlight.Triangulation.Internal.Types-import Moonlight.Triangulation.Math  -- | Immutable admission descends through the sparse constrained-edge section, -- not through every ordinary edge in the requested corridor. Constraint
src-build/Moonlight/Triangulation/Internal/Cdt/Combinators.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-}  -- | Bounded monadic folding, refusable transaction sequencing, and the handle@@ -7,14 +6,8 @@   ( foldWhileM   , bindMutable   , asConstraintStep-  , vertexInt-  , directedInt   ) where -import Moonlight.Triangulation.Internal.HandleDefs-  ( DirectedEdgeId (..)-  , VertexId (..)-  ) import Moonlight.Triangulation.Internal.Cdt.Types (CdtError (..)) import Moonlight.Triangulation.Internal.Types (BuildError) @@ -58,9 +51,3 @@ asConstraintStep :: Functor f => f (Either BuildError a) -> f (Either (CdtError) a) asConstraintStep = fmap (either (Left . CdtBuildError) Right) {-# INLINE asConstraintStep #-}--vertexInt :: VertexId -> Int-vertexInt (VertexId value) = fromIntegral value--directedInt :: DirectedEdgeId -> Int-directedInt (DirectedEdgeId value) = fromIntegral value
src-build/Moonlight/Triangulation/Internal/Cdt/Corridor.hs view
@@ -22,11 +22,7 @@ import Data.Bits (xor) import Moonlight.Triangulation.Internal.HandleDefs import Moonlight.Triangulation.IntersectionIterator (Intersection (..))-import Moonlight.Triangulation.Internal.Cdt.Combinators-  ( directedInt-  , foldWhileM-  , vertexInt-  )+import Moonlight.Triangulation.Internal.Cdt.Combinators (foldWhileM) import Moonlight.Triangulation.Internal.Cdt.Corridor.Trace (nextMutableIntersection) import Moonlight.Triangulation.Internal.Cdt.Types import Moonlight.Triangulation.Internal.Growable@@ -78,8 +74,8 @@   -> ST s (Either (CdtError) MutableConstraintScan) scanMutableConstraint workspace@ConstraintWorkspace{constraintProgramWords, constraintWalkBudget} mutable from to = do   clearGrowable constraintProgramWords-  lineFrom <- pointAt mutable (vertexInt from)-  lineTo <- pointAt mutable (vertexInt to)+  lineFrom <- pointAt mutable (vertexIdIndex from)+  lineTo <- pointAt mutable (vertexIdIndex to)   initialCursor <- beginMutableRecover workspace 0 from 0 False   walked <-     foldWhileM@@ -112,11 +108,11 @@ advanceMutablePlan workspace mutable lineFrom lineTo target (MutablePlanActive event cursor) _ =   case event of     EdgeIntersection directed -> do-      constrained <- readConstraint mutable (directedInt directed)+      constrained <- readConstraint mutable (directedEdgeIdIndex directed)       if constrained         then pure (MutablePlanBlocked directed)         else do-          writeConstraintProgram workspace (mutableCursorWriteAt cursor) (directedInt directed)+          writeConstraintProgram workspace (mutableCursorWriteAt cursor) (directedEdgeIdIndex directed)           continueMutablePlan             workspace             mutable@@ -159,10 +155,10 @@               False           continueMutablePlan workspace mutable lineFrom lineTo target event nextCursor     EdgeOverlap rawDirected -> do-      let rawEdge = directedInt rawDirected+      let rawEdge = directedEdgeIdIndex rawDirected       rawOrigin <- readOrigin mutable rawEdge       rawDestination <- readOrigin mutable (rawEdge `xor` 1)-      let current = vertexInt (mutableCursorAt cursor)+      let current = vertexIdIndex (mutableCursorAt cursor)           oriented             | rawOrigin == current = Just rawEdge             | rawDestination == current = Just (rawEdge `xor` 1)@@ -227,7 +223,7 @@   -> ST s MutableProgramCursor beginMutableRecover workspace header from pieceCount afterOverlap = do   writeConstraintProgram workspace header recoverProgramTag-  writeConstraintProgram workspace (header + 1) (vertexInt from)+  writeConstraintProgram workspace (header + 1) (vertexIdIndex from)   writeConstraintProgram workspace (header + 2) 0   writeConstraintProgram workspace (header + 3) 0   pure@@ -246,7 +242,7 @@   -> MutableProgramCursor   -> ST s MutableProgramCursor finishMutableRecover workspace to cursor = do-  writeConstraintProgram workspace (mutableCursorHeader cursor + 2) (vertexInt to)+  writeConstraintProgram workspace (mutableCursorHeader cursor + 2) (vertexIdIndex to)   writeConstraintProgram workspace (mutableCursorHeader cursor + 3) (mutableCursorConflictCount cursor)   pure     cursor
src-build/Moonlight/Triangulation/Internal/Cdt/Corridor/Trace.hs view
@@ -19,15 +19,15 @@ import Data.Bits (xor) import Moonlight.Triangulation.Internal.HandleDefs import Moonlight.Triangulation.IntersectionIterator-import Moonlight.Triangulation.Internal.Cdt.Combinators-  ( directedInt-  , foldWhileM-  , vertexInt-  )+import Moonlight.Triangulation.Internal.Cdt.Combinators (foldWhileM) import Moonlight.Triangulation.Internal.Mutable+import Moonlight.Triangulation.Internal.Predicates+  ( onClosedSegment+  , orient2d+  , orient2dCoordinates+  ) import Moonlight.Triangulation.Internal.Types-import Moonlight.Triangulation.Math-import Moonlight.Triangulation.Scalar (orient2dCoordinates)+import Moonlight.Triangulation.Math (projectionFactor)  data MutableVertexOut   = MutableVertexOutHull@@ -53,7 +53,7 @@ nextMutableIntersection mutable lineFrom lineTo current =   case current of     EdgeIntersection directed -> do-      edgeOut <- traceMutableDirectionOutOfEdge mutable (directedInt directed) lineFrom lineTo+      edgeOut <- traceMutableDirectionOutOfEdge mutable (directedEdgeIdIndex directed) lineFrom lineTo       pure $ case edgeOut of         MutableEdgeOutHull -> Nothing         MutableEdgeOutVertex vertex ->@@ -62,11 +62,11 @@           Just (EdgeIntersection (DirectedEdgeId (fromIntegral edge)))         MutableEdgeOutNone -> Nothing     VertexIntersection vertex -> do-      currentPoint <- pointAt mutable (vertexInt vertex)+      currentPoint <- pointAt mutable (vertexIdIndex vertex)       if currentPoint == lineTo         then pure Nothing         else do-          vertexOut <- traceMutableDirectionOutOfVertex mutable (vertexInt vertex) lineTo+          vertexOut <- traceMutableDirectionOutOfVertex mutable (vertexIdIndex vertex) lineTo           case vertexOut of             MutableVertexOutHull -> pure Nothing             MutableVertexOutOverlap edge ->@@ -82,7 +82,7 @@     EdgeOverlap directed       | lineFrom == lineTo -> pure Nothing       | otherwise -> do-          destination <- readOrigin mutable (directedInt directed `xor` 1)+          destination <- readOrigin mutable (directedEdgeIdIndex directed `xor` 1)           destinationPoint <- pointAt mutable destination           pure             ( if onClosedSegment lineFrom lineTo destinationPoint
src-build/Moonlight/Triangulation/Internal/Cdt/Recovery.hs view
@@ -21,7 +21,6 @@ import Moonlight.Triangulation.Internal.HandleDefs import Moonlight.Triangulation.Internal.Cdt.Combinators   ( foldWhileM-  , vertexInt   ) import Moonlight.Triangulation.Internal.Cdt.Corridor   ( constraintWorkspaceFor@@ -45,7 +44,7 @@   , writeScratch   ) import Moonlight.Triangulation.Internal.Types-import Moonlight.Triangulation.Scalar (orient2dCoordinates)+import Moonlight.Triangulation.Internal.Predicates (orient2dCoordinates)  data ConflictRecovery = ConflictRecovery   { recoveredConstraintEdge :: {-# UNPACK #-} !Int@@ -238,8 +237,8 @@   -> Int   -> ST s (Either (CdtError) ConflictRecovery) resolveConflictStrip workspace mutable operation from to conflictStart stripLength = do-  fromPoint <- pointAt mutable (vertexInt from)-  toPoint <- pointAt mutable (vertexInt to)+  fromPoint <- pointAt mutable (vertexIdIndex from)+  toPoint <- pointAt mutable (vertexIdIndex to)   traverse_     (\index -> do       edge <- readConstraintProgram workspace (conflictStart + index)@@ -290,7 +289,7 @@               )           )     | pendingCount <= 0 = do-        direct <- findMutableEdge mutable (vertexInt from) (vertexInt to)+        direct <- findMutableEdge mutable (vertexIdIndex from) (vertexIdIndex to)         case direct of           Just edge -> pure (Right (edge, flipped))           Nothing ->
src-build/Moonlight/Triangulation/Internal/Cdt/Region.hs view
@@ -34,8 +34,7 @@ boundedRegionFaces triangulation =   [ face   | face <- innerFaces triangulation-  , let FaceId raw = face-  , not (IntSet.member (fromIntegral raw) outside)+  , not (IntSet.member (faceIdIndex face) outside)   ]  where-  outside = IntSet.fromList [fromIntegral raw | FaceId raw <- outerRegionFaces triangulation]+  outside = IntSet.fromList (fmap faceIdIndex (outerRegionFaces triangulation))
src-build/Moonlight/Triangulation/Internal/Cdt/Segment.hs view
@@ -106,8 +106,8 @@     -> ST s (Either BuildError VertexId)   placeOrReuseConstraintEndpoint mutable operation existing point payload =     case existing of-      Just vertex@(VertexId rawVertex) -> do-        writeVertexData mutable (fromIntegral rawVertex) payload+      Just vertex -> do+        writeVertexData mutable (vertexIdIndex vertex) payload         pure (Right vertex)       Nothing -> placeConstraintEndpoint mutable operation Nothing point payload @@ -122,13 +122,13 @@   -> VertexId   -> vertex   -> Either (CdtError) (ConstraintResult vertex directed undirected face)-applyConstraintToExistingEndpoints triangulation from@(VertexId rawFrom) fromPayload to@(VertexId rawTo) toPayload =+applyConstraintToExistingEndpoints triangulation from fromPayload to toPayload =   do     (request, frozen) <-       runUnmeasuredTransaction CdtBuildError LocalTransaction triangulation 0 $ \mutable operation -> do         programWords <- newGrowableWord32 256-        writeVertexData mutable (fromIntegral rawFrom) fromPayload-        writeVertexData mutable (fromIntegral rawTo) toPayload+        writeVertexData mutable (vertexIdIndex from) fromPayload+        writeVertexData mutable (vertexIdIndex to) toPayload         recoverConstraintRequest programWords mutable operation from to     pure (publishConstraintResult frozen request) @@ -207,8 +207,8 @@   :: Triangulation 'Constrained vertex directed undirected face   -> UndirectedEdgeId   -> Either (CdtError) (Triangulation 'Constrained vertex directed undirected face)-removeConstraintEdge triangulation edge@(UndirectedEdgeId raw)-  | fromIntegral raw >= edgeCount =+removeConstraintEdge triangulation edge+  | undirectedEdgeIdIndex edge >= edgeCount =       Left (ConstraintEdgeIndexOutOfRange edge edgeCount)   -- An edge carrying no constraint has nothing to retire, and answering that   -- without thawing is the difference between O(1) and a republished mesh.@@ -228,9 +228,9 @@   -> OperationState s   -> UndirectedEdgeId   -> ST s (Either (CdtError) ())-retireConstraintEdge mutable operation edge@(UndirectedEdgeId raw) = do+retireConstraintEdge mutable operation edge = do   halfEdges <- directedEdgeCount mutable-  let !directed = fromIntegral raw * 2+  let !directed = directedEdgeIdIndex (normalizedDirected edge)   if directed >= halfEdges     then pure (Left (ConstraintEdgeIndexOutOfRange edge (halfEdges `quot` 2)))     else do
src-build/Moonlight/Triangulation/Internal/Cdt/Split.hs view
@@ -18,8 +18,6 @@ import Moonlight.Triangulation.Internal.Cdt.Combinators   ( asConstraintStep   , bindMutable-  , directedInt-  , vertexInt   ) import Moonlight.Triangulation.Internal.Cdt.Corridor   ( constraintWorkspaceFor@@ -336,10 +334,10 @@       else divideWithin cursor crossing    divideWithin cursor crossing = do-    let !crossingEdge = directedInt crossing+    let !crossingEdge = directedEdgeIdIndex crossing         !oldConstraint = asUndirected crossing-    segmentFrom <- pointAt mutable (vertexInt (splitCursorAt cursor))-    segmentTo <- pointAt mutable (vertexInt target)+    segmentFrom <- pointAt mutable (vertexIdIndex (splitCursorAt cursor))+    segmentTo <- pointAt mutable (vertexIdIndex target)     oldFrom <- readOrigin mutable crossingEdge     oldTo <- readOrigin mutable (crossingEdge `xor` 1)     edgeFrom <- pointAt mutable oldFrom@@ -405,9 +403,9 @@   -> (VertexId, VertexId)   -> VertexId   -> ST s Bool-mutableEdgeWasSplit mutable (UndirectedEdgeId raw) (from, to) splitVertex = do+mutableEdgeWasSplit mutable edge (from, to) splitVertex = do   halfEdges <- directedEdgeCount mutable-  let !directed = fromIntegral raw * 2+  let !directed = directedEdgeIdIndex (normalizedDirected edge)   stillConstrained <-     if directed >= halfEdges       then pure False@@ -426,7 +424,7 @@   -> VertexId   -> ST s Bool mutableConstraintBetween mutable from to = do-  found <- findMutableEdge mutable (vertexInt from) (vertexInt to)+  found <- findMutableEdge mutable (vertexIdIndex from) (vertexIdIndex to)   case found of     Nothing -> pure False     Just edge -> readConstraint mutable edge
src-build/Moonlight/Triangulation/Internal/Cdt/Union.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE ScopedTypeVariables #-}@@ -63,12 +62,15 @@   ) import Moonlight.Triangulation.Internal.BoxedPaged (boxedFill) import Moonlight.Triangulation.Internal.Paged (TransactionShape (DenseTransaction, LocalTransaction))+import Moonlight.Triangulation.Internal.Predicates+  ( onClosedSegment+  , segmentsProperlyCross+  ) import Moonlight.Triangulation.Internal.Representation import Moonlight.Triangulation.Internal.Mutable (MutableDcel) import Moonlight.Triangulation.Internal.OperationState (OperationState) import Moonlight.Triangulation.Internal.Transaction (runTransaction) import Moonlight.Triangulation.Internal.Types-import Moonlight.Triangulation.Math  canonicalSegment :: Point -> Point -> CanonicalSegment canonicalSegment from to
src-build/Moonlight/Triangulation/Internal/CircleSweep.hs view
@@ -36,6 +36,10 @@ import Moonlight.Triangulation.Internal.DcelOperations.CandidateArena   ( seedGenericPairInArena   )+import Moonlight.Triangulation.Internal.DcelOperations.Chain+  ( connectAscendingLineArena+  , prepareAscendingLineArena+  ) import Moonlight.Triangulation.Internal.DcelOperations.Normalize   ( LegalizationDrain (..)   , drainDenseUnconstrainedGenericLegalization@@ -43,6 +47,7 @@ import Moonlight.Triangulation.Internal.Mutable   ( DenseMutableDcel   , MutableDcel+  , connectedCount   , denseMutableDcel   , denseMutableOwner   , denseFaceEdges@@ -68,8 +73,9 @@   , storeLegalizationArena   , writeScratch   )+import Moonlight.Triangulation.Internal.PackedIndex (noIndex, unpackIndex) import Moonlight.Triangulation.Internal.Probe (Probe (..))-import Moonlight.Triangulation.Scalar (orient2dCoordinates)+import Moonlight.Triangulation.Internal.Predicates (orient2dCoordinates) import Moonlight.Triangulation.Types (BuildError (..))  -- | The hull is an angular index over the DCEL outer-face cycle. The cycle@@ -258,9 +264,6 @@       (MUV.unsafeSlice start (end - start) vertices) {-# INLINE sortGeometryPrefixFibre #-} -noOuterEdge :: Word32-noOuterEdge = maxBound- -- | Circle sweep over one mutable DCEL, consuming one radial vertex order. -- Coordinates are read from the authoritative dense DCEL. Insertions -- initially close only acute hull turns. One terminal Graham pass restores@@ -286,6 +289,25 @@ circleSweepInsertDense dense operation arena   | MUV.length arena == 0 = pure (Right 0)   | otherwise = do+      residentCount <- connectedCount mutable+      ascendingLine <-+        if residentCount == 0+          then prepareAscendingLineArena dense arena+          else pure Nothing+      case ascendingLine of+        Just line -> connectAscendingLineArena dense operation line+        Nothing -> circleSweepInsertArea dense operation arena+ where+  !mutable = denseMutableOwner dense+{-# INLINE circleSweepInsertDense #-}++circleSweepInsertArea+  :: forall s vertex directed undirected face+   . DenseMutableDcel s vertex directed undirected face+  -> OperationState s+  -> MUV.MVector s Word32+  -> ST s (Either BuildError Int)+circleSweepInsertArea dense operation arena = do       let !ordered = MUV.length arena       seed <- insertSeed 0       case seed of@@ -363,7 +385,7 @@     | index >= MUV.length arena = pure (Right index)     | otherwise = do         raw <- MUV.unsafeRead arena index-        result <- insertExistingVertex @'ProbeOff mutable operation (fromIntegral raw)+        result <- insertExistingVertex @'ProbeOff mutable operation (unpackIndex raw)         case result of           Left failure -> pure (Left failure)           Right () -> do@@ -389,7 +411,7 @@     | index >= ordered = pure (Right (skippedCount, fastCount, flips, maxDepth, cursor, candidateArena))     | otherwise = do         raw <- MUV.unsafeRead arena index-        let !vertex = fromIntegral raw+        let !vertex = unpackIndex raw         queryXWide <- denseReadPointX dense vertex         queryYWide <- denseReadPointY dense vertex         let !queryAngle =@@ -554,7 +576,7 @@ rebuildBuckets :: DenseMutableDcel s vertex directed undirected face -> Hull s -> Int -> Int -> ST s (HullIndex s) rebuildBuckets dense hull active requested = do   let !count = max 1 (min (hullBucketCapacity hull) requested)-  buckets <- MUV.replicate count noOuterEdge+  buckets <- MUV.replicate count noIndex   start <- denseReadFaceEdge dense 0   let go !remaining !edge         | remaining <= 0 = pure ()@@ -673,9 +695,9 @@   let !count = MUV.length buckets       !bucket = bucketFor count queryAngle   raw <- MUV.unsafeRead buckets bucket-  if raw == noOuterEdge+  if raw == noIndex     then denseReadFaceEdge dense 0-    else adjustFromBoundary bucket (fromIntegral raw)+    else adjustFromBoundary bucket (unpackIndex raw)  where   adjustFromBoundary !bucket !initial = do     initialAngle <- readAngle hull initial
src-build/Moonlight/Triangulation/Internal/Excision.hs view
@@ -26,7 +26,7 @@   , writeScratch   ) import Moonlight.Triangulation.Internal.Types-import Moonlight.Triangulation.Math (orient2d)+import Moonlight.Triangulation.Internal.Predicates (orient2d)  -- | The proved outgoing section of one ordinary removal. Its edges live in the -- operation scratch arena in counter-clockwise order; the record carries only
src-build/Moonlight/Triangulation/Internal/Join/Seam.hs view
@@ -96,7 +96,10 @@   , RefinementParameters (..)   , unitElementDefaults   )-import Moonlight.Triangulation.Scalar (inCircleCoordinates, orient2dCoordinates)+import Moonlight.Triangulation.Internal.Predicates+  ( inCircleCoordinates+  , orient2dCoordinates+  ) import Moonlight.Triangulation.Math (distance, midpoint, validatePoint) import Moonlight.Triangulation.Internal.Transaction (runTransactionWithPublication) 
src-build/Moonlight/Triangulation/Internal/Location.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE BangPatterns #-}-{-# LANGUAGE NamedFieldPuns #-}  module Moonlight.Triangulation.Internal.Location   ( MutableLocation (..)@@ -26,7 +25,11 @@   ( FaceId (..)   , UndirectedEdgeId (..)   )+import Moonlight.Triangulation.Internal.Predicates import Moonlight.Triangulation.Math+  ( canonicalPoint+  , squaredDistanceWide+  ) import Moonlight.Triangulation.Types  data MutableLocation@@ -89,7 +92,7 @@  -- | Greedy first-improvement descent through vertex neighbours: hop to the -- first neighbour strictly closer to the query until none improves. Distance--- is a heuristic only — exact containment belongs to 'walk' — so the widened+-- is a heuristic only — exact containment belongs to 'walkWithBarrier' — so the widened -- comparison can lengthen the path but never move the located answer. -- -- Strictly decreasing distance bounds the hops and nothing else: no vertex is@@ -99,7 +102,7 @@ -- step per outgoing half-edge, so a budget past 'directedEdgeCount' cannot be -- exhausted while the links are well formed. Exhausting it means they are not, -- and yields the vertex in hand rather than an error — a truncated descent can--- only lengthen 'walk', never move what it finds.+-- only lengthen 'walkWithBarrier', never move what it finds. descendToNearest   :: MutableDcel s vertex directed undirected face   -> OperationState s@@ -229,15 +232,6 @@                 _ -> go (edge + 1) (Just (terminal, candidate))    complementOne = -2--walk-  :: MutableDcel s vertex directed undirected face-  -> OperationState s-  -> Point-  -> Int-  -> ST s (Either BuildError MutableLocation)-walk mutable operation query initialFace = do-  walkWithBarrier Nothing mutable operation query initialFace  walkWithBarrier   :: Maybe IntSet.IntSet
src-build/Moonlight/Triangulation/Internal/Refinement.hs view
@@ -85,16 +85,18 @@   , readScratch   , writeScratch   )-import Moonlight.Triangulation.Internal.PackedIndex (noIndex, packIndex)+import Moonlight.Triangulation.Internal.PackedIndex (noIndex, packIndex, unpackIndex)+import Moonlight.Triangulation.Internal.Predicates+  ( inCircle+  , inDiametralCircle+  , orient2d+  ) import Moonlight.Triangulation.Internal.Probe (Probe (..)) import Moonlight.Triangulation.Math   ( canonicalPoint   , circumcenter-  , inCircle-  , inDiametralCircle   , isFinite   , midpoint-  , orient2d   , squaredDistance   , squaredDistanceWide   , triangleArea@@ -950,7 +952,7 @@     count <- growableLength faces     traverse_       (\index -> do-         face <- fromIntegral <$> readGrowable faces index+         face <- unpackIndex <$> readGrowable faces index          retirePermittedFace workspace face       )       [0 .. count - 1]@@ -960,7 +962,7 @@     count <- growableLength faces     traverse       (\index -> do-         face <- fromIntegral <$> readGrowable faces index+         face <- unpackIndex <$> readGrowable faces index          signature <- mutableFaceSignature face          pure (face, signature)       )@@ -1239,7 +1241,7 @@   chainBoundary Workspace{wsCavityEpoch, wsCavityFaceMarks, wsCavityVertexMarks, wsCavityBoundary, wsCavityChain} count = do     epoch <- readSTRef wsCavityEpoch     walkBudget <- directedEdgeCount mutable-    start <- fromIntegral <$> readGrowable wsCavityBoundary 0+    start <- unpackIndex <$> readGrowable wsCavityBoundary 0     clearGrowable wsCavityChain     pushGrowable wsCavityChain (packIndex start)     walk epoch (walkBudget + 2) start start (count - 1)@@ -1300,7 +1302,7 @@           spoke !index !pair             | index >= boundaryCount = pure ()             | otherwise = do-                boundaryEdge <- fromIntegral <$> readGrowable wsCavityChain index+                boundaryEdge <- unpackIndex <$> readGrowable wsCavityChain index                 face <- fanFace faceBase index                 nextPair <-                   if index + 1 >= boundaryCount@@ -1329,7 +1331,7 @@       -- full shouldFlip pass would ask again what it already answered.       cocircular <- growableLength wsCavityCocircular       when (cocircular > 0) $ do-        ties <- traverse (\index -> fromIntegral <$> readGrowable wsCavityCocircular index) [0 .. cocircular - 1]+        ties <- traverse (\index -> unpackIndex <$> readGrowable wsCavityCocircular index) [0 .. cocircular - 1]         legalizeEdges mutable operation ties       admitStar workspace vertex       let offer :: Int -> ST s ()@@ -1345,12 +1347,12 @@      fanFace :: Int -> Int -> ST s Int     fanFace faceBase index-      | index < cavityFaceCount = fromIntegral <$> readGrowable wsCavityFaces index+      | index < cavityFaceCount = unpackIndex <$> readGrowable wsCavityFaces index       | otherwise = pure (faceBase + (index - cavityFaceCount))      fanPair :: Int -> Int -> ST s Int     fanPair edgeBase index-      | index < internalCount = fromIntegral <$> readGrowable wsCavityInternal index+      | index < internalCount = unpackIndex <$> readGrowable wsCavityInternal index       | otherwise = pure ((edgeBase + 2 * (index - internalCount)) `quot` 2)    -- A candidate outside the hull (only reachable when the hull is not
src-build/Moonlight/Triangulation/Internal/Session.hs view
@@ -12,6 +12,7 @@   , insertVertex   , insertVertexAt   , insertVertexAtNear+  , insertVertexAtNearVertex   , insertAdmittedVertexAtCombining   , removeAt   , removeAtNear@@ -123,10 +124,7 @@   :: Point   -> vertex   -> Session s vertex directed undirected face (VertexId, InsertionDisposition)-insertVertexAt point payload = Session $ \mesh operation ->-  fmap-    (fmap (\(vertex, disposition) -> (VertexId (fromIntegral vertex), disposition)))-    (insertPointCombining (\_ replacement -> replacement) Nothing mesh operation point payload)+insertVertexAt = insertVertexAtFrom (const (pure Nothing))  -- | 'insertVertexAt' with the walk seeded at a face the caller vouches for -- -- typically the face a locate on the just-published value settled on, which@@ -138,17 +136,39 @@   -> Point   -> vertex   -> Session s vertex directed undirected face (VertexId, InsertionDisposition)-insertVertexAtNear (FaceId rawSeed) point payload = Session $ \mesh operation ->+insertVertexAtNear seed =+  insertVertexAtFrom (const (pure (Just (faceIdIndex seed))))++-- | 'insertVertexAt' with the exact walk restricted first to an inner face in+-- the supplied vertex's star. This is the native boundary for hierarchy and+-- nearest-neighbour hints, which answer vertices rather than faces. The seed+-- remains non-authoritative: an invalid handle or a vertex with no incident+-- inner face degrades to the ordinary unhinted descent.+insertVertexAtNearVertex+  :: VertexId+  -> Point+  -> vertex+  -> Session s vertex directed undirected face (VertexId, InsertionDisposition)+insertVertexAtNearVertex seed = insertVertexAtFrom (`interiorFaceNearVertex` seed)++insertVertexAtFrom+  :: (MutableDcel s vertex directed undirected face -> ST s (Maybe Int))+  -> Point+  -> vertex+  -> Session s vertex directed undirected face (VertexId, InsertionDisposition)+insertVertexAtFrom seedFor point payload = Session $ \mesh operation -> do+  seed <- seedFor mesh   fmap     (fmap (\(vertex, disposition) -> (VertexId (fromIntegral vertex), disposition)))     (insertPointCombining        (\_ replacement -> replacement)-       (Just (fromIntegral rawSeed))+       seed        mesh        operation        point        payload     )+{-# INLINE insertVertexAtFrom #-}  -- | Insert an admitted site while combining an occupied annotation. This is -- package-internal descent from a boundary that already owns coordinate@@ -205,9 +225,9 @@   :: VertexId   -> Point   -> Session s vertex directed undirected face (Maybe (RemovalOutcome vertex))-removeAtNear (VertexId rawGuess) point = Session $ \mesh operation -> do+removeAtNear guessVertex point = Session $ \mesh operation -> do   indexed <- identityIndexActive mesh-  let !guess = fromIntegral rawGuess+  let !guess = vertexIdIndex guessVertex       !query = canonicalPoint point   if indexed     then removeIndexed mesh operation query@@ -220,16 +240,35 @@           if stored == query             then fmap (fmap Just) (removeMutableOutcome mesh operation guess)             else do-              outgoing <- readVertexOut mesh guess-              let interiorFace edge fallback = do-                    face <- readFace mesh edge-                    if face > 0 then pure (Just face) else fallback-              hint <--                if outgoing < 0-                  then pure Nothing-                  else interiorFace outgoing (interiorFace (outgoing `xor` 1) (pure Nothing))+              hint <- interiorFaceAtVertex mesh guess               walkAndRemove mesh operation hint query +interiorFaceNearVertex+  :: MutableDcel s vertex directed undirected face+  -> VertexId+  -> ST s (Maybe Int)+interiorFaceNearVertex mesh vertex@(VertexId rawVertex) = do+  vertices <- pointCount mesh+  if toInteger rawVertex >= toInteger vertices+    then pure Nothing+    else interiorFaceAtVertex mesh (vertexIdIndex vertex)++interiorFaceAtVertex+  :: MutableDcel s vertex directed undirected face+  -> Int+  -> ST s (Maybe Int)+interiorFaceAtVertex mesh vertex = do+  outgoing <- readVertexOut mesh vertex+  if outgoing < 0+    then pure Nothing+    else do+      primary <- innerFaceOf outgoing+      maybe (innerFaceOf (outgoing `xor` 1)) (pure . Just) primary+ where+  innerFaceOf edge = do+    face <- readFace mesh edge+    pure (if face > 0 then Just face else Nothing)+ walkAndRemove   :: MutableDcel s vertex directed undirected face   -> OperationState s@@ -314,7 +353,7 @@     then pure (Left (RemovalVertexOutOfRange requested vertices))     else do       activatePointIndex mesh-      removeMutableOutcome mesh operation (fromIntegral raw)+      removeMutableOutcome mesh operation (vertexIdIndex requested)  removeMutableOutcome   :: MutableDcel s vertex directed undirected face
src-build/Moonlight/Triangulation/Refinement.hs view
@@ -37,7 +37,9 @@   , UndirectedEdgeId (..)   , asUndirected   , directedPair+  , faceIdIndex   , reverseEdge+  , undirectedEdgeIdIndex   ) import Moonlight.Triangulation.Internal.Mutable import Moonlight.Triangulation.Internal.Paged@@ -120,15 +122,12 @@       [ pair       | rawFace <- IntSet.toAscList permitted       , edge <- faceDirectedEdges triangulation (FaceId (fromIntegral rawFace))-      , let pair = fromIntegral (unUndirected (asUndirected edge))+      , let pair = undirectedEdgeIdIndex (asUndirected edge)       , let adjacent = incidentFace triangulation (reverseEdge edge)       , adjacent /= FaceId 0-      , IntSet.notMember (fromIntegral (unFace adjacent)) permitted+      , IntSet.notMember (faceIdIndex adjacent) permitted       ] -  unUndirected (UndirectedEdgeId raw) = raw-  unFace (FaceId raw) = raw- -- | Refine exactly one checked local section. Interface edges are installed as -- transaction-local legalization barriers and removed before publication; -- the receipt and collar proof inspect only the admitted local closure.@@ -223,8 +222,8 @@         if domain == Nothing && refineExcludeOuterFaces parameters           then             IntSet.fromList-              [ fromIntegral raw-              | FaceId raw <-+              [ faceIdIndex face+              | face <-                   facesAtEvenBarrierDepth                     triangulation                     (isConstraintEdge triangulation)@@ -373,12 +372,10 @@   -> IntSet.IntSet localDomainEdges triangulation faces =   IntSet.fromList-    [ fromIntegral (unUndirected (asUndirected edge))+    [ undirectedEdgeIdIndex (asUndirected edge)     | rawFace <- IntSet.toAscList faces     , edge <- faceDirectedEdges triangulation (FaceId (fromIntegral rawFace))     ]- where-  unUndirected (UndirectedEdgeId raw) = raw  localEdgeSignature   :: Triangulation mode vertex directed undirected face@@ -436,8 +433,8 @@       , let edge = UndirectedEdgeId (fromIntegral rawPair)       , let (forward, backward) = directedPair edge       , rawFace <--          [ fromIntegral (unFace (incidentFace before forward))-          , fromIntegral (unFace (incidentFace before backward))+          [ faceIdIndex (incidentFace before forward)+          , faceIdIndex (incidentFace before backward)           ]       , rawFace > 0       , IntSet.notMember rawFace permitted@@ -464,7 +461,7 @@         | protectedFace `elem` afterFaces ->             case filter (/= protectedFace) afterFaces of               [oppositeFace]-                | IntSet.member (fromIntegral (unFace oppositeFace)) finalPermitted ->+                | IntSet.member (faceIdIndex oppositeFace) finalPermitted ->                     Right (edge, protectedFace, oppositeFace)                 | otherwise ->                     Left@@ -476,11 +473,9 @@       _ -> Left RefinementDomainTopologyChanged    protected face =-    let raw = fromIntegral (unFace face)+    let raw = faceIdIndex face      in raw > 0 && IntSet.notMember raw permitted -  unFace (FaceId raw) = raw- -- | Refuse outer or absent faces rather than silently treating an invalid -- topology witness as an empty repair. Face zero is the outer face and has no -- active refinement equation.@@ -494,10 +489,10 @@   totalFaces = numFaces triangulation    validateFace :: FaceId -> Either BuildError Int-  validateFace face@(FaceId raw)-    | raw == 0 || toInteger raw >= toInteger totalFaces =+  validateFace face+    | face == FaceId 0 || toInteger (unFaceId face) >= toInteger totalFaces =         Left (RefinementSeedFaceNotActive face totalFaces)-    | otherwise = Right (fromIntegral raw)+    | otherwise = Right (faceIdIndex face)  -- | Refuse parameters no mesh can satisfy. validateRefinementParameters :: RefinementParameters -> Either BuildError ()
src-build/Moonlight/Triangulation/Session.hs view
@@ -6,6 +6,7 @@   , insertVertex   , insertVertexAt   , insertVertexAtNear+  , insertVertexAtNearVertex   , removeAt   , removeAtNear   , removeManyAt
+ src-cell-complex/Moonlight/Triangulation/LabelledComplex.hs view
@@ -0,0 +1,131 @@+-- | Homology interpretations of canonical labelled planar complexes.+--+-- 'PlanarComplex' owns simplex identity and downward closure. This module+-- materializes that admitted section once; Homology remains the sole owner of+-- chain-law and filtration-law validation.+module Moonlight.Triangulation.LabelledComplex+  ( PlanarComplexInterpretationError (..)+  , finitePlanarComplex+  , filteredPlanarComplex+  )+where++import Data.Bifunctor (first)+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Vector qualified as Vector+import Moonlight.Homology.Boundary+  ( BoundaryIncidence+  , FiniteChainComplex+  , emptyBoundaryIncidence+  , emptyBoundaryIncidenceOf+  , materializeIncidenceBoundary+  , mkFiniteChainComplexChecked+  )+import Moonlight.Homology.Chain+  ( HomologicalDegree (..)+  , HomologyFailure+  )+import Moonlight.Homology.Persistence+  ( FilteredFiniteChainComplex+  , mkFilteredFiniteChainComplex+  )+import Moonlight.Homology.Topology (BasisCellRef (..))+import Moonlight.Triangulation.Simplex+  ( PlanarComplex+  , PlanarSimplex+  , SimplexDimension (..)+  , planarComplexBasisAt+  , planarSimplexBoundary+  )++-- | Typed obstructions at the labelled-complex/Homology boundary.+data PlanarComplexInterpretationError label+  = PlanarComplexChainInvalid !HomologyFailure+  | PlanarComplexBirthUnexpected !(PlanarSimplex label)+  | PlanarComplexFiltrationInvalid !HomologyFailure+  deriving stock (Eq, Show)++-- | Canonical integral chains in ascending labelled-simplex order.+finitePlanarComplex+  :: Ord label+  => PlanarComplex label+  -> Either (PlanarComplexInterpretationError label) (FiniteChainComplex Int)+finitePlanarComplex complexValue = do+  degreeOneBoundary <-+    first PlanarComplexChainInvalid+      ( materializeIncidenceBoundary+          planarSimplexBoundary+          (basisList SimplexDimension1 complexValue)+          (basisList SimplexDimension0 complexValue)+      )+  degreeTwoBoundary <-+    first PlanarComplexChainInvalid+      ( materializeIncidenceBoundary+          planarSimplexBoundary+          (basisList SimplexDimension2 complexValue)+          (basisList SimplexDimension1 complexValue)+      )+  let degreeZeroBoundary :: BoundaryIncidence Int+      degreeZeroBoundary =+        emptyBoundaryIncidenceOf+          (fromIntegral (Vector.length (planarComplexBasisAt SimplexDimension0 complexValue)))+          0+      boundaryAt (HomologicalDegree degreeIndex) =+        case degreeIndex of+          0 -> degreeZeroBoundary+          1 -> degreeOneBoundary+          2 -> degreeTwoBoundary+          _ -> emptyBoundaryIncidence+  first PlanarComplexChainInvalid+    (mkFiniteChainComplexChecked (HomologicalDegree 2) boundaryAt)++-- | Add exact births to the admitted complex. Homology validates uniqueness,+-- total coverage, and every boundary inequality; this interpreter deliberately+-- does not duplicate those laws.+filteredPlanarComplex+  :: (Ord label, Ord filtration)+  => PlanarComplex label+  -> Map (PlanarSimplex label) filtration+  -> Either+      (PlanarComplexInterpretationError label)+      (FilteredFiniteChainComplex filtration Int)+filteredPlanarComplex complexValue births = do+  finite <- finitePlanarComplex complexValue+  assignments <- traverse birthAssignment (Map.toAscList births)+  first PlanarComplexFiltrationInvalid+    (mkFilteredFiniteChainComplex finite assignments)+ where+  basisReferences = planarBasisReferences complexValue+  birthAssignment (simplex, birthValue) =+    maybe+      (Left (PlanarComplexBirthUnexpected simplex))+      (Right . (,birthValue))+      (Map.lookup simplex basisReferences)++planarBasisReferences+  :: Ord label+  => PlanarComplex label+  -> Map (PlanarSimplex label) BasisCellRef+planarBasisReferences complexValue =+  Map.unions+    [ referencesAt 0 SimplexDimension0+    , referencesAt 1 SimplexDimension1+    , referencesAt 2 SimplexDimension2+    ]+ where+  referencesAt degreeValue dimension =+    Map.fromDistinctAscList+      ( zipWith+          (\basisIndex simplex ->+              (simplex, BasisCellRef (HomologicalDegree degreeValue) basisIndex)+          )+          [0 ..]+          (basisList dimension complexValue)+      )++basisList+  :: SimplexDimension+  -> PlanarComplex label+  -> [PlanarSimplex label]+basisList dimension = Vector.toList . planarComplexBasisAt dimension
src-core/Moonlight/Triangulation/Internal/ExactRational.hs view
@@ -13,6 +13,7 @@   , exactRationalFromFiniteDouble   , exactRationalFromDyadic   , exactRationalFromDyadicRatio+  , exactRationalFromNormalizedRatio   , exactRationalNumerator   , exactRationalDenominator   , exactRationalBitWidth@@ -114,6 +115,13 @@         then exactRational (oddNumerator `shiftL` residualPower) oddDenominator         else exactRational oddNumerator (oddDenominator `shiftL` negate residualPower) {-# INLINE exactRationalFromDyadicRatio #-}++-- | Internal bridge from the normalized carrier owned by @Data.Ratio@. This+-- exists for statically nonzero rational constants in exact kernels; public+-- callers continue through 'exactRational'.+exactRationalFromNormalizedRatio :: Ratio Integer -> ExactRational+exactRationalFromNormalizedRatio = ExactRational+{-# INLINE exactRationalFromNormalizedRatio #-}  -- | Read the reduced numerator. exactRationalNumerator :: ExactRational -> Integer
src-core/Moonlight/Triangulation/Scalar.hs view
@@ -9,8 +9,6 @@   , scalarUnitRoundoff   , scalarCcwErrorBound   , scalarInCircleErrorBound-  , orient2dCoordinates-  , inCircleCoordinates   , circumradiusSquaredWithinCoordinates   , BinaryFormat   , formatRadix@@ -24,8 +22,6 @@  import Moonlight.Triangulation.Internal.Dyadic   ( exactCircumradiusSquaredWithin-  , exactInCircleDet-  , exactOrientSignDouble   )  -- | The coordinate component of canonical point identity. IEEE signed zeros@@ -81,21 +77,6 @@ scalarInCircleErrorBound :: Double scalarInCircleErrorBound = 1.1102230246251577e-15 --- | Exact orientation ordering of three binary64 coordinate pairs.-orient2dCoordinates-  :: Double -> Double -> Double -> Double -> Double -> Double-  -> Ordering-orient2dCoordinates = filteredOrient2dDouble-{-# INLINE orient2dCoordinates #-}---- | Exact in-circle ordering of four binary64 coordinate pairs.-inCircleCoordinates-  :: Double -> Double -> Double -> Double-  -> Double -> Double -> Double -> Double-  -> Ordering-inCircleCoordinates = filteredInCircle scalarInCircleErrorBound-{-# INLINE inCircleCoordinates #-}- -- | Closed exact circumradius membership at a finite, non-negative binary64 -- threshold. Invalid thresholds and collinear triples are outside. circumradiusSquaredWithinCoordinates@@ -133,51 +114,6 @@ isFinite :: Double -> Bool isFinite value = value - value == 0 {-# INLINE isFinite #-}---- The binary64 kernel pairs the approximation test with the--- fixed-precision exact sign, which answers the dyadic determinant's sign in--- machine words rather than allocated Integers whenever the exponent spread--- allows, and defers to the dyadic determinant when it does not.-filteredOrient2dDouble-  :: Double -> Double -> Double -> Double -> Double -> Double -> Ordering-filteredOrient2dDouble ax ay bx by cx cy-  | abs determinant > errorBound * determinantSum = compare determinant 0-  | otherwise = exactOrientSignDouble ax ay bx by cx cy- where-  errorBound = 3.3306690738754716e-16-  !left = (ax - cx) * (by - cy)-  !right = (ay - cy) * (bx - cx)-  !determinant = left - right-  !determinantSum = abs left + abs right-{-# INLINE filteredOrient2dDouble #-}--filteredInCircle-  :: Double-  -> Double -> Double -> Double -> Double-  -> Double -> Double -> Double -> Double-  -> Ordering-filteredInCircle errorBound ax ay bx by cx cy dx dy-  | abs determinant > errorBound * permanent = compare determinant 0-  | otherwise = compare (exactInCircleDet ax ay bx by cx cy dx dy) 0- where-  !adx = ax - dx-  !ady = ay - dy-  !bdx = bx - dx-  !bdy = by - dy-  !cdx = cx - dx-  !cdy = cy - dy-  !abdet = adx * bdy - bdx * ady-  !bcdet = bdx * cdy - cdx * bdy-  !cadet = cdx * ady - adx * cdy-  !alift = adx * adx + ady * ady-  !blift = bdx * bdx + bdy * bdy-  !clift = cdx * cdx + cdy * cdy-  !determinant = alift * bcdet + blift * cadet + clift * abdet-  !permanent =-    (abs (bdx * cdy) + abs (cdx * bdy)) * alift-      + (abs (cdx * ady) + abs (adx * cdy)) * blift-      + (abs (adx * bdy) + abs (bdx * ady)) * clift-{-# INLINE filteredInCircle #-}  -- | The smallest coordinate magnitude the exact predicates accept. minimumAllowedCoordinate :: Double
src-dcel/Moonlight/Triangulation/Alpha.hs view
@@ -44,6 +44,9 @@   , UndirectedEdgeId (..)   , VertexId (..)   , directedPair+  , faceIdIndex+  , undirectedEdgeIdIndex+  , vertexIdIndex   ) import Moonlight.Triangulation.Internal.CellSet   ( ExactCellSet (..)@@ -66,7 +69,7 @@   ( DelaunayTriangulation   , Triangulation   )-import Moonlight.Triangulation.Math (inDiametralCircle)+import Moonlight.Triangulation.Internal.Predicates (inDiametralCircle) import Moonlight.Triangulation.Scalar (circumradiusSquaredWithinCoordinates) import Moonlight.Triangulation.Types   ( ConstraintMode (Unconstrained)@@ -188,14 +191,14 @@ -- | Exact birth of a resident vertex, when the handle belongs to this -- filtration. alphaVertexBirth :: AlphaFiltration -> VertexId -> Maybe AlphaBirth-alphaVertexBirth filtration (VertexId rawVertex) =-  alphaVertexBirths filtration Vector.!? fromIntegral rawVertex+alphaVertexBirth filtration vertex =+  alphaVertexBirths filtration Vector.!? vertexIdIndex vertex  -- | Exact birth of a resident undirected edge, when the handle belongs to this -- filtration. alphaEdgeBirth :: AlphaFiltration -> UndirectedEdgeId -> Maybe AlphaBirth-alphaEdgeBirth filtration (UndirectedEdgeId rawEdge) =-  alphaEdgeBirths filtration Vector.!? fromIntegral rawEdge+alphaEdgeBirth filtration edge =+  alphaEdgeBirths filtration Vector.!? undirectedEdgeIdIndex edge  -- | Exact birth of a resident bounded face, when the handle belongs to this -- filtration.@@ -374,14 +377,14 @@     Just nonEmptyBirths -> Right (minimum nonEmptyBirths)  faceSectionAt :: Vector FaceAlphaSection -> FaceId -> Maybe FaceAlphaSection-faceSectionAt faceSections (FaceId rawFace)-  | rawFace == 0 = Nothing-  | otherwise = faceSections Vector.!? (fromIntegral rawFace - 1)+faceSectionAt faceSections face+  | face == Dcel.outerFace = Nothing+  | otherwise = faceSections Vector.!? (faceIdIndex face - 1)  faceBirthAt :: Vector AlphaBirth -> FaceId -> Maybe AlphaBirth-faceBirthAt faceBirths (FaceId rawFace)-  | rawFace == 0 = Nothing-  | otherwise = faceBirths Vector.!? (fromIntegral rawFace - 1)+faceBirthAt faceBirths face+  | face == Dcel.outerFace = Nothing+  | otherwise = faceBirths Vector.!? (faceIdIndex face - 1)  triangleVertices :: (VertexId, VertexId, VertexId) -> [VertexId] triangleVertices (firstVertex, secondVertex, thirdVertex) =
src-dcel/Moonlight/Triangulation/Dcel.hs view
@@ -97,8 +97,8 @@  -- | Authoritative geometric position of an admitted vertex handle. vertexPoint :: Triangulation mode vertex directed undirected face -> VertexId -> Point-vertexPoint triangulation (VertexId vertex) =-  let !index = fromIntegral vertex+vertexPoint triangulation vertex =+  let !index = vertexIdIndex vertex    in Point (pagedUnsafeIndex (triPointX triangulation) index) (pagedUnsafeIndex (triPointY triangulation) index) {-# INLINE vertexPoint #-} @@ -110,26 +110,26 @@  -- | Annotation carried by an admitted vertex handle. vertexData :: Triangulation mode vertex directed undirected face -> VertexId -> vertex-vertexData triangulation (VertexId vertex) =-  boxedUnsafeIndex (triVertexData triangulation) (fromIntegral vertex)+vertexData triangulation vertex =+  boxedUnsafeIndex (triVertexData triangulation) (vertexIdIndex vertex) {-# INLINE vertexData #-}  -- | Annotation carried by an admitted directed edge. directedEdgeData :: Triangulation mode vertex directed undirected face -> DirectedEdgeId -> directed-directedEdgeData triangulation (DirectedEdgeId edge) =-  boxedUnsafeIndex (triDirectedData triangulation) (fromIntegral edge)+directedEdgeData triangulation edge =+  boxedUnsafeIndex (triDirectedData triangulation) (directedEdgeIdIndex edge) {-# INLINE directedEdgeData #-}  -- | Annotation carried by an admitted undirected edge. undirectedEdgeData :: Triangulation mode vertex directed undirected face -> UndirectedEdgeId -> undirected-undirectedEdgeData triangulation (UndirectedEdgeId edge) =-  boxedUnsafeIndex (triUndirectedData triangulation) (fromIntegral edge)+undirectedEdgeData triangulation edge =+  boxedUnsafeIndex (triUndirectedData triangulation) (undirectedEdgeIdIndex edge) {-# INLINE undirectedEdgeData #-}  -- | Annotation carried by an admitted face. faceData :: Triangulation mode vertex directed undirected face -> FaceId -> face-faceData triangulation (FaceId face) =-  boxedUnsafeIndex (triFaceData triangulation) (fromIntegral face)+faceData triangulation face =+  boxedUnsafeIndex (triFaceData triangulation) (faceIdIndex face) {-# INLINE faceData #-}  -- | Replace one vertex annotation without changing geometry or topology.@@ -138,8 +138,8 @@   -> VertexId   -> vertex   -> Triangulation mode vertex directed undirected face-setVertexData triangulation (VertexId vertex) payload =-  triangulation{triVertexData = boxedUpdate (fromIntegral vertex) payload (triVertexData triangulation)}+setVertexData triangulation vertex payload =+  triangulation{triVertexData = boxedUpdate (vertexIdIndex vertex) payload (triVertexData triangulation)}  -- | Replace one directed-edge annotation without changing geometry or topology. setDirectedEdgeData@@ -147,8 +147,8 @@   -> DirectedEdgeId   -> directed   -> Triangulation mode vertex directed undirected face-setDirectedEdgeData triangulation (DirectedEdgeId edge) payload =-  triangulation{triDirectedData = boxedUpdate (fromIntegral edge) payload (triDirectedData triangulation)}+setDirectedEdgeData triangulation edge payload =+  triangulation{triDirectedData = boxedUpdate (directedEdgeIdIndex edge) payload (triDirectedData triangulation)}  -- | Replace one undirected-edge annotation without changing geometry or topology. setUndirectedEdgeData@@ -156,8 +156,8 @@   -> UndirectedEdgeId   -> undirected   -> Triangulation mode vertex directed undirected face-setUndirectedEdgeData triangulation (UndirectedEdgeId edge) payload =-  triangulation{triUndirectedData = boxedUpdate (fromIntegral edge) payload (triUndirectedData triangulation)}+setUndirectedEdgeData triangulation edge payload =+  triangulation{triUndirectedData = boxedUpdate (undirectedEdgeIdIndex edge) payload (triUndirectedData triangulation)}  -- | Replace one face annotation without changing geometry or topology. setFaceData@@ -165,25 +165,25 @@   -> FaceId   -> face   -> Triangulation mode vertex directed undirected face-setFaceData triangulation (FaceId face) payload =-  triangulation{triFaceData = boxedUpdate (fromIntegral face) payload (triFaceData triangulation)}+setFaceData triangulation face payload =+  triangulation{triFaceData = boxedUpdate (faceIdIndex face) payload (triFaceData triangulation)}  -- | One outgoing directed edge, if the vertex is connected. vertexOutEdge :: Triangulation mode vertex directed undirected face -> VertexId -> Maybe DirectedEdgeId-vertexOutEdge triangulation (VertexId vertex) =-  DirectedEdgeId . fromIntegral <$> unpackOptionalIndex (pagedUnsafeIndex (triVertexOut triangulation) (fromIntegral vertex))+vertexOutEdge triangulation vertex =+  DirectedEdgeId . fromIntegral <$> unpackOptionalIndex (pagedUnsafeIndex (triVertexOut triangulation) (vertexIdIndex vertex)) {-# INLINE vertexOutEdge #-}  -- | One boundary edge of a face, if the face has a boundary. adjacentEdge :: Triangulation mode vertex directed undirected face -> FaceId -> Maybe DirectedEdgeId-adjacentEdge triangulation (FaceId face) =-  DirectedEdgeId . fromIntegral <$> unpackOptionalIndex (pagedUnsafeIndex (triFaceEdge triangulation) (fromIntegral face))+adjacentEdge triangulation face =+  DirectedEdgeId . fromIntegral <$> unpackOptionalIndex (pagedUnsafeIndex (triFaceEdge triangulation) (faceIdIndex face)) {-# INLINE adjacentEdge #-}  -- | Origin vertex of an admitted directed edge. origin :: Triangulation mode vertex directed undirected face -> DirectedEdgeId -> VertexId-origin triangulation (DirectedEdgeId edge) =-  VertexId (pagedUnsafeIndex (triHalfTopology triangulation) (4 * fromIntegral edge))+origin triangulation edge =+  VertexId (pagedUnsafeIndex (triHalfTopology triangulation) (4 * directedEdgeIdIndex edge)) {-# INLINE origin #-}  -- | Destination vertex of an admitted directed edge.@@ -193,20 +193,20 @@  -- | Next directed edge around the incident face. next :: Triangulation mode vertex directed undirected face -> DirectedEdgeId -> DirectedEdgeId-next triangulation (DirectedEdgeId edge) =-  DirectedEdgeId (pagedUnsafeIndex (triHalfTopology triangulation) (4 * fromIntegral edge + 1))+next triangulation edge =+  DirectedEdgeId (pagedUnsafeIndex (triHalfTopology triangulation) (4 * directedEdgeIdIndex edge + 1)) {-# INLINE next #-}  -- | Previous directed edge around the incident face. previous :: Triangulation mode vertex directed undirected face -> DirectedEdgeId -> DirectedEdgeId-previous triangulation (DirectedEdgeId edge) =-  DirectedEdgeId (pagedUnsafeIndex (triHalfTopology triangulation) (4 * fromIntegral edge + 2))+previous triangulation edge =+  DirectedEdgeId (pagedUnsafeIndex (triHalfTopology triangulation) (4 * directedEdgeIdIndex edge + 2)) {-# INLINE previous #-}  -- | Face on the left of an admitted directed edge. incidentFace :: Triangulation mode vertex directed undirected face -> DirectedEdgeId -> FaceId-incidentFace triangulation (DirectedEdgeId edge) =-  FaceId (pagedUnsafeIndex (triHalfTopology triangulation) (4 * fromIntegral edge + 3))+incidentFace triangulation edge =+  FaceId (pagedUnsafeIndex (triHalfTopology triangulation) (4 * directedEdgeIdIndex edge + 3)) {-# INLINE incidentFace #-}  -- | Whether the directed edge is incident to the unbounded face.@@ -223,8 +223,8 @@  -- | Whether the edge belongs to the constrained-edge section. isConstraintEdge :: Triangulation mode vertex directed undirected face -> UndirectedEdgeId -> Bool-isConstraintEdge triangulation (UndirectedEdgeId edge) =-  pagedUnsafeIndex (triConstraint triangulation) (fromIntegral edge) /= (0 :: Word8)+isConstraintEdge triangulation edge =+  pagedUnsafeIndex (triConstraint triangulation) (undirectedEdgeIdIndex edge) /= (0 :: Word8) {-# INLINE isConstraintEdge #-}  -- | Number of constrained undirected edges.@@ -254,8 +254,8 @@  -- | The three directed edges of a bounded triangular face. innerFaceDirectedEdges :: Triangulation mode vertex directed undirected face -> FaceId -> Maybe (DirectedEdgeId, DirectedEdgeId, DirectedEdgeId)-innerFaceDirectedEdges triangulation face@(FaceId rawFace)-  | face == outerFace || fromIntegral rawFace >= numFaces triangulation = Nothing+innerFaceDirectedEdges triangulation face+  | face == outerFace || faceIdIndex face >= numFaces triangulation = Nothing   | otherwise = do       e0 <- adjacentEdge triangulation face       let !e1 = next triangulation e0
src-dcel/Moonlight/Triangulation/Exact.hs view
@@ -4,7 +4,14 @@  -- | Exact rational planar geometry over admitted binary64 points. module Moonlight.Triangulation.Exact-  ( ExactPoint+  ( ExactRational+  , ExactArithmeticError (..)+  , exactRational+  , exactRationalNumerator+  , exactRationalDenominator+  , exactRationalBitWidth+  , exactDivide+  , ExactPoint   , exactPoint   , exactPointCoordinates   , exactPointCross
src-dcel/Moonlight/Triangulation/Handles/Dynamic.hs view
@@ -2,7 +2,6 @@ {-# LANGUAGE EmptyDataDecls #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE KindSignatures #-}-{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RoleAnnotations #-}  -- | Owning handles that prevent identifiers from crossing mesh boundaries.@@ -66,6 +65,7 @@  import Moonlight.Triangulation.Dcel qualified as Dcel import Moonlight.Triangulation.Internal.HandleDefs+import Moonlight.Triangulation.Internal.Predicates qualified as Admitted import Moonlight.Triangulation.LineSideInfo (LineSideInfo) import Moonlight.Triangulation.Math qualified as Math import Moonlight.Triangulation.Types@@ -129,8 +129,8 @@   :: Triangulation mode vertex directed undirected face   -> VertexId   -> Maybe (VertexHandle mode vertex directed undirected face)-vertexHandle triangulation vertex@(VertexId raw)-  | fromIntegral raw < Dcel.numVertices triangulation = Just (VertexHandle triangulation vertex)+vertexHandle triangulation vertex+  | vertexIdIndex vertex < Dcel.numVertices triangulation = Just (VertexHandle triangulation vertex)   | otherwise = Nothing  -- | Admit a directed-edge identifier into a triangulation.@@ -138,8 +138,8 @@   :: Triangulation mode vertex directed undirected face   -> DirectedEdgeId   -> Maybe (DirectedEdgeHandle mode vertex directed undirected face)-directedEdgeHandle triangulation edge@(DirectedEdgeId raw)-  | fromIntegral raw < Dcel.numDirectedEdges triangulation = Just (DirectedEdgeHandle triangulation edge)+directedEdgeHandle triangulation edge+  | directedEdgeIdIndex edge < Dcel.numDirectedEdges triangulation = Just (DirectedEdgeHandle triangulation edge)   | otherwise = Nothing  -- | Admit an undirected-edge identifier into a triangulation.@@ -147,8 +147,8 @@   :: Triangulation mode vertex directed undirected face   -> UndirectedEdgeId   -> Maybe (UndirectedEdgeHandle mode vertex directed undirected face)-undirectedEdgeHandle triangulation edge@(UndirectedEdgeId raw)-  | fromIntegral raw < Dcel.numUndirectedEdges triangulation = Just (UndirectedEdgeHandle triangulation edge)+undirectedEdgeHandle triangulation edge+  | undirectedEdgeIdIndex edge < Dcel.numUndirectedEdges triangulation = Just (UndirectedEdgeHandle triangulation edge)   | otherwise = Nothing  -- | Admit a face identifier that may denote the outer face.@@ -156,8 +156,8 @@   :: Triangulation mode vertex directed undirected face   -> FaceId   -> Maybe (FaceHandle PossiblyOuterTag mode vertex directed undirected face)-faceHandle triangulation face@(FaceId raw)-  | fromIntegral raw < Dcel.numFaces triangulation = Just (FaceHandle triangulation (FixedFaceHandle face))+faceHandle triangulation face+  | faceIdIndex face < Dcel.numFaces triangulation = Just (FaceHandle triangulation (FixedFaceHandle face))   | otherwise = Nothing  -- | Admit a face identifier while proving that it is bounded.@@ -293,11 +293,11 @@ -- | Exact side of the owning edge's oriented line. directedEdgeSideQuery   :: DirectedEdgeHandle mode vertex directed undirected face-  -> Point+  -> QueryPoint   -> LineSideInfo directedEdgeSideQuery edge query =   let (from, to) = directedEdgePositions edge-   in Math.sideQuery from to query+   in Admitted.sideQuery from to (queryPointValue query)  -- | Undirected-edge annotation through an owning handle. undirectedEdgeDataH :: UndirectedEdgeHandle mode vertex directed undirected face -> undirected
src-dcel/Moonlight/Triangulation/Internal/CellSet.hs view
@@ -42,6 +42,9 @@   , UndirectedEdgeId (..)   , VertexId (..)   , asUndirected+  , faceIdIndex+  , undirectedEdgeIdIndex+  , vertexIdIndex   ) import Moonlight.Triangulation.Internal.Representation (Triangulation) import Moonlight.Triangulation.Internal.Types (PointValidationError)@@ -107,9 +110,9 @@   exactPoints <-     IntMap.fromDistinctAscList       <$> traverse-        ( \vertexIndex -> do-            point <- ordinaryExactPoint triangulation (VertexId (fromIntegral vertexIndex))-            pure (vertexIndex, point)+        ( \residentIndex -> do+            point <- ordinaryExactPoint triangulation (VertexId (fromIntegral residentIndex))+            pure (residentIndex, point)         )         vertexIndices   pure@@ -226,15 +229,15 @@   traverse_ validateEdge selectedEdges   traverse_ validateFace selectedFaces  where-  validateVertex vertex@(VertexId raw)-    | fromIntegral raw < numVertices triangulation = Right ()+  validateVertex vertex+    | vertexIdIndex vertex < numVertices triangulation = Right ()     | otherwise = Left (CellVertexOutOfRange vertex (numVertices triangulation))-  validateEdge edge@(UndirectedEdgeId raw)-    | fromIntegral raw < numUndirectedEdges triangulation = Right ()+  validateEdge edge+    | undirectedEdgeIdIndex edge < numUndirectedEdges triangulation = Right ()     | otherwise = Left (CellEdgeOutOfRange edge (numUndirectedEdges triangulation))-  validateFace face@(FaceId raw)-    | raw == 0 = Left CellOuterFaceSelected-    | fromIntegral raw < numFaces triangulation = Right ()+  validateFace face+    | faceIdIndex face == 0 = Left CellOuterFaceSelected+    | faceIdIndex face < numFaces triangulation = Right ()     | otherwise = Left (CellFaceOutOfRange face (numFaces triangulation))  validateEdgeClosure@@ -246,8 +249,8 @@   traverse_ requireVertex [from, to]  where   (from, to) = undirectedEndpoints triangulation edge-  requireVertex vertex@(VertexId raw)-    | IntSet.member (fromIntegral raw) selectedVertices = Right ()+  requireVertex vertex+    | IntSet.member (vertexIdIndex vertex) selectedVertices = Right ()     | otherwise = Left (CellEdgeBoundaryMissing edge vertex)  validateFaceClosure@@ -258,18 +261,18 @@ validateFaceClosure triangulation selectedEdges face =   traverse_ (requireEdge . asUndirected) (faceDirectedEdges triangulation face)  where-  requireEdge edge@(UndirectedEdgeId raw)-    | IntSet.member (fromIntegral raw) selectedEdges = Right ()+  requireEdge edge+    | IntSet.member (undirectedEdgeIdIndex edge) selectedEdges = Right ()     | otherwise = Left (CellFaceEdgeMissing face edge)  vertexSet :: [VertexId] -> IntSet.IntSet-vertexSet = IntSet.fromList . map (\(VertexId raw) -> fromIntegral raw)+vertexSet = IntSet.fromList . map vertexIdIndex  edgeSet :: [UndirectedEdgeId] -> IntSet.IntSet-edgeSet = IntSet.fromList . map (\(UndirectedEdgeId raw) -> fromIntegral raw)+edgeSet = IntSet.fromList . map undirectedEdgeIdIndex  faceSet :: [FaceId] -> IntSet.IntSet-faceSet = IntSet.fromList . map (\(FaceId raw) -> fromIntegral raw)+faceSet = IntSet.fromList . map faceIdIndex  exactCellSetVertexCount :: ExactCellSet -> Int exactCellSetVertexCount (ExactCellSet _ selected _ _) = IntMap.size selected
src-dcel/Moonlight/Triangulation/Internal/DcelOperations/Chain.hs view
@@ -11,6 +11,8 @@   , setupSecondVertex   , splitLineEdge   , extendLine+  , prepareAscendingLineArena+  , connectAscendingLineArena   , collectLineChain   , lineToArea   ) where@@ -18,14 +20,28 @@ import Control.Monad (forM_, when) import Control.Monad.ST (ST) import Data.STRef (writeSTRef)+import qualified Data.Vector.Unboxed.Mutable as MUV+import Data.Word (Word32) import Moonlight.Triangulation.Internal.HandleDefs (VertexId (..)) import Moonlight.Triangulation.Internal.DcelOperations.Legalize (legalizeScratch) import Moonlight.Triangulation.Internal.DcelOperations.Twin (reverseIndex)+import Moonlight.Triangulation.Internal.PackedIndex (unpackIndex) import Moonlight.Triangulation.Internal.Mutable   ( MutableDcel (..)+  , DenseMutableDcel   , addEdge   , addEdgeBlock   , addFaceBlock+  , denseAddEdgeBlock+  , denseCommitFreshConnections+  , denseMarkFreshConnected+  , denseMutableOwner+  , denseReadPointX+  , denseReadPointY+  , denseWriteFace+  , denseWriteFaceEdge+  , denseLinkEdges+  , denseWriteOrigin   , directedEdgeCount   , edgeOriginPoint   , ensureCellCapacity@@ -56,7 +72,7 @@   ) import Moonlight.Triangulation.Internal.Probe (KnownProbe) import Moonlight.Triangulation.Internal.Types (BuildError (..))-import Moonlight.Triangulation.Math (orient2d)+import Moonlight.Triangulation.Internal.Predicates (orient2d, orient2dCoordinates)  setupFirstVertex :: MutableDcel s vertex directed undirected face -> Int -> ST s () setupFirstVertex mutable vertex = markConnected mutable vertex (-1)@@ -146,6 +162,145 @@           markConnected mutable vertex newTwin           addCounter operation CounterLineExtensions 1           pure (Right ())++-- | A consumed radial arena whose sites have been proved collinear and placed+-- in ascending coordinate order. The constructor stays here with the chain+-- topology it licenses; the sweep can only obtain the witness through+-- 'prepareAscendingLineArena'.+newtype AscendingLineArena s = AscendingLineArena (MUV.MVector s Word32)++-- | Recognize a one-dimensional fresh section and derive its chain order from+-- the radial order already paid for by bulk admission. The nearest radial site+-- is a lawful pivot: distances increase monotonically on each side of it, so+-- reversing the lower fibre and retaining the upper fibre yields total line+-- order without another sort.+--+-- The third point is inspected before the remaining fold. General-position+-- input therefore pays one exact orientation, not a speculative linear scan.+prepareAscendingLineArena+  :: forall s vertex directed undirected face+   . DenseMutableDcel s vertex directed undirected face+  -> MUV.MVector s Word32+  -> ST s (Maybe (AscendingLineArena s))+prepareAscendingLineArena dense radial+  | MUV.length radial < 3 = pure Nothing+  | otherwise = do+      first <- MUV.unsafeRead radial 0+      second <- MUV.unsafeRead radial 1+      third <- MUV.unsafeRead radial 2+      firstX <- coordinateX first+      firstY <- coordinateY first+      secondX <- coordinateX second+      secondY <- coordinateY second+      thirdX <- coordinateX third+      thirdY <- coordinateY third+      let !useX = firstX /= secondX+          pointCoordinate :: Double -> Double -> Double+          pointCoordinate x y = if useX then x else y+          coordinateAt :: Word32 -> ST s Double+          coordinateAt = if useX then coordinateX else coordinateY+          !pivotCoordinate = pointCoordinate firstX firstY+          !initialLowerCount =+            fromEnum (pointCoordinate secondX secondY < pivotCoordinate)+              + fromEnum (pointCoordinate thirdX thirdY < pivotCoordinate)+      if orient2dCoordinates firstX firstY secondX secondY thirdX thirdY /= EQ+        then pure Nothing+        else do+          (!collinear, !lowerCount) <-+            MUV.foldM'+              (\(!admitted, !count) raw ->+                 if admitted+                   then do+                     x <- coordinateX raw+                     y <- coordinateY raw+                     pure+                       ( orient2dCoordinates firstX firstY secondX secondY x y == EQ+                       , count + fromEnum (pointCoordinate x y < pivotCoordinate)+                       )+                   else pure (False, count)+              )+              (True, initialLowerCount)+              (MUV.unsafeSlice 3 (MUV.length radial - 3) radial)+          if collinear+            then Just <$> orderAlongLine coordinateAt pivotCoordinate lowerCount+            else pure Nothing+ where+  coordinateX :: Word32 -> ST s Double+  coordinateX = denseReadPointX dense . fromIntegral+  coordinateY :: Word32 -> ST s Double+  coordinateY = denseReadPointY dense . fromIntegral++  orderAlongLine+    :: (Word32 -> ST s Double)+    -> Double+    -> Int+    -> ST s (AscendingLineArena s)+  orderAlongLine coordinateAt pivotCoordinate lowerCount = do+    ascending <- MUV.new (MUV.length radial)+    _ <-+      MUV.foldM'+        (\(!lowerSeen, !upperSeen) raw -> do+           coordinate <- coordinateAt raw+           if coordinate < pivotCoordinate+             then do+               MUV.unsafeWrite ascending (lowerCount - lowerSeen - 1) raw+               pure (lowerSeen + 1, upperSeen)+             else do+               MUV.unsafeWrite ascending (lowerCount + upperSeen) raw+               pure (lowerSeen, upperSeen + 1)+        )+        (0, 0)+        radial+    pure (AscendingLineArena ascending)++-- | Glue a proved ascending line as one outer-face cycle. Every site and cell+-- is fresh, so the dense section initializes each topology slot exactly once+-- and commits connectivity once after the local pieces agree on their shared+-- endpoints.+connectAscendingLineArena+  :: DenseMutableDcel s vertex directed undirected face+  -> OperationState s+  -> AscendingLineArena s+  -> ST s (Either BuildError Int)+connectAscendingLineArena dense operation (AscendingLineArena vertices) = do+  let !vertexCount = MUV.length vertices+      !segmentCount = vertexCount - 1+      mutable = denseMutableOwner dense+  capacity <- ensureCellCapacity mutable segmentCount 0+  case capacity of+    Left obstruction -> pure (Left obstruction)+    Right () -> do+      edgeBase <- denseAddEdgeBlock dense segmentCount+      MUV.imapM_+        (\index fromRaw -> do+           toRaw <- MUV.unsafeRead vertices (index + 1)+           let !forward = edgeBase + 2 * index+               !backward = forward + 1+               !nextForward = if index == segmentCount - 1 then backward else forward + 2+               !nextBackward = if index == 0 then edgeBase else backward - 2+           denseWriteOrigin dense forward (unpackIndex fromRaw)+           denseWriteOrigin dense backward (unpackIndex toRaw)+           denseLinkEdges dense forward nextForward+           denseLinkEdges dense backward nextBackward+           denseWriteFace dense forward 0+           denseWriteFace dense backward 0+        )+        (MUV.unsafeSlice 0 segmentCount vertices)+      MUV.imapM_+        (\index raw ->+           denseMarkFreshConnected+             dense+             (unpackIndex raw)+             ( if index < segmentCount+                 then edgeBase + 2 * index+                 else edgeBase + 2 * segmentCount - 1+             )+        )+        vertices+      denseCommitFreshConnections dense vertexCount+      denseWriteFaceEdge dense 0 edgeBase+      addCounter operation CounterLineExtensions (max 0 (vertexCount - 2))+      pure (Right vertexCount)  collectLineChain :: MutableDcel s vertex directed undirected face -> OperationState s -> ST s (Either BuildError Int) collectLineChain mutable operation = do
src-dcel/Moonlight/Triangulation/Internal/DcelOperations/FlipRule.hs view
@@ -25,7 +25,10 @@   , readPointY   , readPrevious   )-import Moonlight.Triangulation.Scalar (inCircleCoordinates, orient2dCoordinates)+import Moonlight.Triangulation.Internal.Predicates+  ( inCircleCoordinates+  , orient2dCoordinates+  )  -- | Whether the flip rule may fire on a quadrilateral @a b c d@ under a law, -- where @ab@ is the diagonal and @c@, @d@ the apexes opposite it. The
src-dcel/Moonlight/Triangulation/Internal/DcelOperations/Hull.hs view
@@ -92,7 +92,7 @@   ) import Moonlight.Triangulation.Internal.Probe (KnownProbe) import Moonlight.Triangulation.Types (BuildError (..), Point (..))-import Moonlight.Triangulation.Scalar (orient2dCoordinates)+import Moonlight.Triangulation.Internal.Predicates (orient2dCoordinates)  -- | Proof that the remaining circle-sweep program fits the particular mutable -- arena carried here. The constructor is private: only 'reserveSweepCells' can
src-dcel/Moonlight/Triangulation/Internal/DcelOperations/Normalize.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-}-{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -O3 -fllvm -optlo-O3 -optlc-O3 #-}
src-dcel/Moonlight/Triangulation/Internal/FaceProbe.hs view
@@ -1,11 +1,9 @@-{-# LANGUAGE BangPatterns #-}- module Moonlight.Triangulation.Internal.FaceProbe   ( BoundaryProbe (..)   , probeBoundary   ) where -import Moonlight.Triangulation.Math (onClosedSegment, orient2d)+import Moonlight.Triangulation.Internal.Predicates (onClosedSegment, orient2d) import Moonlight.Triangulation.Types (Point)  -- | Classification of one oriented boundary of a triangular face. A
src-dcel/Moonlight/Triangulation/Internal/FloodFillIterator.hs view
@@ -61,7 +61,12 @@   ( simplifyBoundaryCycle   , traceOrientedBoundaryCircuits   )+import Moonlight.Triangulation.Internal.Predicates import Moonlight.Triangulation.Math+  ( mkQueryPoint+  , segmentDistanceSquaredWide+  , squaredDistanceWide+  ) import Moonlight.Triangulation.PointLocation import Moonlight.Triangulation.Types @@ -233,8 +238,8 @@   edges = edgesInShape triangulation metric   set = List.foldl' addEndpoints IntSet.empty edges   addEndpoints acc edge =-    let (VertexId from, VertexId to) = undirectedEndpoints triangulation edge-     in IntSet.insert (fromIntegral from) (IntSet.insert (fromIntegral to) acc)+    let (from, to) = undirectedEndpoints triangulation edge+     in IntSet.insert (vertexIdIndex from) (IntSet.insert (vertexIdIndex to) acc)   candidateVertices     | numVertices triangulation == 1 = [VertexId 0]     | otherwise = map (VertexId . fromIntegral) (IntSet.toAscList set)@@ -267,7 +272,7 @@   let (faces, accepted, rejected) = go initialStack initialVisited IntSet.empty IntSet.empty []    in (reverse faces, accepted, rejected)  where-  valid face@(FaceId value) = face /= outerFace && fromIntegral value < numFaces triangulation+  valid face = face /= outerFace && faceIdIndex face < numFaces triangulation   (initialStack, initialVisited) = List.foldl' enqueueStart ([], IntSet.empty) starts    enqueueStart state face@@ -285,8 +290,8 @@      in go stack' visited' accepted' rejected' (face : result)    expand (stack, visited, accepted, rejected) edge =-    let undirected@(UndirectedEdgeId raw) = asUndirected edge-        edgeIndex = fromIntegral raw+    let undirected = asUndirected edge+        edgeIndex = undirectedEdgeIdIndex undirected         adjacent = incidentFace triangulation (reverseEdge edge)         edgeAdmission           | IntSet.member edgeIndex accepted = (True, accepted, rejected)@@ -300,11 +305,11 @@             else (stack, visited)      in (stack', visited', accepted', rejected') -  enqueue face@(FaceId value) (stack, visited)+  enqueue face (stack, visited)     | IntSet.member index visited = (stack, visited)     | otherwise = (face : stack, IntSet.insert index visited)    where-    index = fromIntegral value+    index = faceIdIndex face  -- | Connected components of equally labelled bounded faces. Labels are -- evaluated once; the component carrier is the same 'IntSet' used by descent.@@ -394,9 +399,8 @@         )       else (queued, unvisited)    where-    adjacent@(FaceId adjacentRaw) =-      incidentFace triangulation (reverseEdge edge)-    adjacentIndex = fromIntegral adjacentRaw+    adjacent = incidentFace triangulation (reverseEdge edge)+    adjacentIndex = faceIdIndex adjacent {-# INLINE faceComponentsFromLabels #-}  -- | Descend every equally labelled bounded-face component through the one@@ -477,8 +481,7 @@             )         Nothing -> Right (FaceComponent indices)  where-  indices = IntSet.fromList (fmap faceIndex (Set.toAscList selectedFaces))-  faceIndex (FaceId raw) = fromIntegral raw+  indices = IntSet.fromList (fmap faceIdIndex (Set.toAscList selectedFaces))  componentBoundaryGraph   :: Triangulation mode vertex directed undirected face@@ -518,11 +521,11 @@               firstEdge               secondEdge           SimpleBoundaryGraph edges outgoing ->-            case IntMap.lookup vertexIndex outgoing of+            case IntMap.lookup residentIndex outgoing of               Nothing ->                 SimpleBoundaryGraph                   (insertEdge edges)-                  (IntMap.insert vertexIndex edge outgoing)+                  (IntMap.insert residentIndex edge outgoing)               Just previousEdge ->                 PinchedBoundaryGraph                   (insertEdge edges)@@ -530,12 +533,10 @@                   previousEdge                   edge    where-    FaceId adjacent = incidentFace triangulation (reverseEdge edge)-    adjacentIndex = fromIntegral adjacent-    vertex@(VertexId rawVertex) = origin triangulation edge-    vertexIndex = fromIntegral rawVertex-    DirectedEdgeId rawEdge = edge-    insertEdge = IntSet.insert (fromIntegral rawEdge)+    adjacentIndex = faceIdIndex (incidentFace triangulation (reverseEdge edge))+    vertex = origin triangulation edge+    residentIndex = vertexIdIndex vertex+    insertEdge = IntSet.insert (directedEdgeIdIndex edge)  regionBoundaryFromLoops   :: [BoundaryLoop]@@ -600,15 +601,13 @@         descend (loop : loops) remaining    traceCycle start current unvisited reversedVertices =-    let DirectedEdgeId rawCurrent = current-        remaining = IntSet.delete (fromIntegral rawCurrent) unvisited+    let remaining = IntSet.delete (directedEdgeIdIndex current) unvisited         accumulated = origin triangulation current : reversedVertices-        VertexId rawTarget = destination triangulation current-     in case IntMap.lookup (fromIntegral rawTarget) outgoingByVertex of+        target = destination triangulation current+     in case IntMap.lookup (vertexIdIndex target) outgoingByVertex of           Just successor             | successor == start -> Right (reverse accumulated, remaining)-            | let DirectedEdgeId rawSuccessor = successor-            , IntSet.member (fromIntegral rawSuccessor) remaining ->+            | IntSet.member (directedEdgeIdIndex successor) remaining ->                 traceCycle start successor remaining accumulated             | otherwise -> Left (BoundaryCycleDidNotClose start successor)           Nothing -> Left (BoundaryCycleDidNotClose start current)@@ -666,9 +665,7 @@ barrierDepthLayers triangulation isBarrier =   map (filter (/= outerFace)) (layers IntSet.empty [outerFace])  where-  known (FaceId value) = fromIntegral value < numFaces triangulation-  key :: FaceId -> Int-  key (FaceId value) = fromIntegral value+  known face = faceIdIndex face < numFaces triangulation    layers visited frontier = case flood visited [] frontier of     ([], _) -> []@@ -676,10 +673,10 @@    flood visited acc [] = (reverse acc, visited)   flood visited acc (face : rest)-    | not (known face) || IntSet.member (key face) visited = flood visited acc rest+    | not (known face) || IntSet.member (faceIdIndex face) visited = flood visited acc rest     | otherwise =         flood-          (IntSet.insert (key face) visited)+          (IntSet.insert (faceIdIndex face) visited)           (face : acc)           (neighbours (not . isBarrier) face <> rest) @@ -710,7 +707,14 @@     OnEdge edge -> filter (/= outerFace) [incidentFace triangulation edge, incidentFace triangulation (reverseEdge edge)]     OnVertex vertex ->       intSetToFaces-        (List.foldl' (\set edge -> let FaceId value = incidentFace triangulation edge in if value == 0 then set else IntSet.insert (fromIntegral value) set) IntSet.empty (vertexOutgoingEdges triangulation vertex))+        ( List.foldl'+            (\set edge ->+              let face = incidentFace triangulation edge+               in if face == outerFace then set else IntSet.insert (faceIdIndex face) set+            )+            IntSet.empty+            (vertexOutgoingEdges triangulation vertex)+        )     OutsideConvexHull _ ->       [ incidentFace triangulation (reverseEdge edge)       | edge <- faceDirectedEdges triangulation outerFace
src-dcel/Moonlight/Triangulation/Internal/HandleDefs.hs view
@@ -4,9 +4,13 @@ -- its index complement, so orientation is a bit rather than a lookup. module Moonlight.Triangulation.Internal.HandleDefs   ( VertexId (..)+  , vertexIdIndex   , FaceId (..)+  , faceIdIndex   , DirectedEdgeId (..)+  , directedEdgeIdIndex   , UndirectedEdgeId (..)+  , undirectedEdgeIdIndex   , reverseEdge   , asUndirected   , normalizedDirected@@ -18,6 +22,7 @@ import Control.DeepSeq (NFData) import Data.Bits (shiftL, shiftR, xor, (.&.)) import Data.Word (Word32)+import Moonlight.Triangulation.Internal.PackedIndex (unpackIndex)  -- | Index of a vertex in the immutable DCEL. newtype VertexId = VertexId { unVertexId :: Word32 }@@ -38,6 +43,20 @@ newtype UndirectedEdgeId = UndirectedEdgeId { unUndirectedEdgeId :: Word32 }   deriving stock (Show)   deriving newtype (Eq, Ord, NFData)++-- | Project compact identifiers into resident vector indices.+vertexIdIndex :: VertexId -> Int+vertexIdIndex = unpackIndex . unVertexId+{-# INLINE vertexIdIndex #-}+faceIdIndex :: FaceId -> Int+faceIdIndex = unpackIndex . unFaceId+{-# INLINE faceIdIndex #-}+directedEdgeIdIndex :: DirectedEdgeId -> Int+directedEdgeIdIndex = unpackIndex . unDirectedEdgeId+{-# INLINE directedEdgeIdIndex #-}+undirectedEdgeIdIndex :: UndirectedEdgeId -> Int+undirectedEdgeIdIndex = unpackIndex . unUndirectedEdgeId+{-# INLINE undirectedEdgeIdIndex #-}  -- | Select the opposite orientation of the same undirected edge. reverseEdge :: DirectedEdgeId -> DirectedEdgeId
src-dcel/Moonlight/Triangulation/Internal/Mutable.hs view
@@ -128,7 +128,7 @@   , VertexId (..)   ) import Moonlight.Triangulation.Internal.BoxedPaged-import Moonlight.Triangulation.Internal.PackedIndex (noIndex, packIndex)+import Moonlight.Triangulation.Internal.PackedIndex (noIndex, packIndex, unpackIndex) import Moonlight.Triangulation.Internal.Paged import Moonlight.Triangulation.Internal.PointIndex   ( MutablePointIndex@@ -1427,13 +1427,13 @@ {-# INLINE denseCommitFreshDefaultPoints #-}  denseReadOrigin :: DenseMutableDcel s vertex directed undirected face -> Int -> ST s Int-denseReadOrigin DenseMutableDcel{dmdHalfTopology} index = fromIntegral <$> readFlatMutable dmdHalfTopology (4 * index)+denseReadOrigin DenseMutableDcel{dmdHalfTopology} index = unpackIndex <$> readFlatMutable dmdHalfTopology (4 * index) denseReadNext :: DenseMutableDcel s vertex directed undirected face -> Int -> ST s Int-denseReadNext DenseMutableDcel{dmdHalfTopology} index = fromIntegral <$> readFlatMutable dmdHalfTopology (4 * index + 1)+denseReadNext DenseMutableDcel{dmdHalfTopology} index = unpackIndex <$> readFlatMutable dmdHalfTopology (4 * index + 1) denseReadPrevious :: DenseMutableDcel s vertex directed undirected face -> Int -> ST s Int-denseReadPrevious DenseMutableDcel{dmdHalfTopology} index = fromIntegral <$> readFlatMutable dmdHalfTopology (4 * index + 2)+denseReadPrevious DenseMutableDcel{dmdHalfTopology} index = unpackIndex <$> readFlatMutable dmdHalfTopology (4 * index + 2) denseReadFace :: DenseMutableDcel s vertex directed undirected face -> Int -> ST s Int-denseReadFace DenseMutableDcel{dmdHalfTopology} index = fromIntegral <$> readFlatMutable dmdHalfTopology (4 * index + 3)+denseReadFace DenseMutableDcel{dmdHalfTopology} index = unpackIndex <$> readFlatMutable dmdHalfTopology (4 * index + 3) {-# INLINE denseReadOrigin #-} {-# INLINE denseReadNext #-} {-# INLINE denseReadPrevious #-}@@ -1482,7 +1482,7 @@ denseReadFaceEdge :: DenseMutableDcel s vertex directed undirected face -> Int -> ST s Int denseReadFaceEdge DenseMutableDcel{dmdFaceEdge} index = do   value <- readFlatMutable dmdFaceEdge index-  pure (if value == noIndex then -1 else fromIntegral value)+  pure (if value == noIndex then -1 else unpackIndex value) {-# INLINE denseReadFaceEdge #-}  denseWriteFaceEdge :: DenseMutableDcel s vertex directed undirected face -> Int -> Int -> ST s ()@@ -1496,13 +1496,13 @@ {-# INLINE denseReadConstraint #-}  readOrigin :: MutableDcel s vertex directed undirected face -> Int -> ST s Int-readOrigin MutableDcel{mdHalfTopology} index = fromIntegral <$> readPaged mdHalfTopology (4 * index)+readOrigin MutableDcel{mdHalfTopology} index = unpackIndex <$> readPaged mdHalfTopology (4 * index) readNext :: MutableDcel s vertex directed undirected face -> Int -> ST s Int-readNext MutableDcel{mdHalfTopology} index = fromIntegral <$> readPaged mdHalfTopology (4 * index + 1)+readNext MutableDcel{mdHalfTopology} index = unpackIndex <$> readPaged mdHalfTopology (4 * index + 1) readPrevious :: MutableDcel s vertex directed undirected face -> Int -> ST s Int-readPrevious MutableDcel{mdHalfTopology} index = fromIntegral <$> readPaged mdHalfTopology (4 * index + 2)+readPrevious MutableDcel{mdHalfTopology} index = unpackIndex <$> readPaged mdHalfTopology (4 * index + 2) readFace :: MutableDcel s vertex directed undirected face -> Int -> ST s Int-readFace MutableDcel{mdHalfTopology} index = fromIntegral <$> readPaged mdHalfTopology (4 * index + 3)+readFace MutableDcel{mdHalfTopology} index = unpackIndex <$> readPaged mdHalfTopology (4 * index + 3) {-# INLINE readOrigin #-} {-# INLINE readNext #-} {-# INLINE readPrevious #-}@@ -1524,7 +1524,7 @@ readVertexOut :: MutableDcel s vertex directed undirected face -> Int -> ST s Int readVertexOut MutableDcel{mdVertexOut} index = do   value <- readPaged mdVertexOut index-  pure (if value == noIndex then -1 else fromIntegral value)+  pure (if value == noIndex then -1 else unpackIndex value) {-# INLINE readVertexOut #-}  writeVertexOut :: MutableDcel s vertex directed undirected face -> Int -> Int -> ST s ()@@ -1535,7 +1535,7 @@ readFaceEdge :: MutableDcel s vertex directed undirected face -> Int -> ST s Int readFaceEdge MutableDcel{mdFaceEdge} index = do   value <- readPaged mdFaceEdge index-  pure (if value == noIndex then -1 else fromIntegral value)+  pure (if value == noIndex then -1 else unpackIndex value) {-# INLINE readFaceEdge #-}  writeFaceEdge :: MutableDcel s vertex directed undirected face -> Int -> Int -> ST s ()
src-dcel/Moonlight/Triangulation/Internal/OperationState.hs view
@@ -37,7 +37,7 @@   , readGrowable   , writeGrowable   )-import Moonlight.Triangulation.Internal.PackedIndex (packIndex)+import Moonlight.Triangulation.Internal.PackedIndex (packIndex, unpackIndex) import Moonlight.Triangulation.Internal.Types (BuildStats (..))  -- | The typed candidate section owned by one operation. The newtype prevents@@ -146,7 +146,7 @@ {-# INLINE writeScratch #-}  readScratch :: OperationState s -> Int -> ST s Int-readScratch OperationState{osScratchArena} index = fromIntegral <$> readGrowable osScratchArena index+readScratch OperationState{osScratchArena} index = unpackIndex <$> readGrowable osScratchArena index {-# INLINE readScratch #-}  addCounter :: OperationState s -> Counter -> Int -> ST s ()
+ src-dcel/Moonlight/Triangulation/Internal/Predicates.hs view
@@ -0,0 +1,143 @@+{-# LANGUAGE BangPatterns #-}+{-# OPTIONS_GHC -O3 -fllvm -optlo-O3 -optlc-O3 #-}++-- | Exact predicates over points already admitted by a construction or query+-- boundary.  This module is package-private: public callers must carry+-- 'QueryPoint' evidence through "Moonlight.Triangulation.Math" instead of+-- presenting arbitrary IEEE-754 payloads to an exact classifier.+module Moonlight.Triangulation.Internal.Predicates+  ( orient2dCoordinates+  , inCircleCoordinates+  , orient2d+  , sideQuery+  , inCircle+  , onClosedSegment+  , segmentRelation+  , segmentsProperlyCross+  , segmentsIntersect+  , inDiametralCircle+  ) where++import Moonlight.Triangulation.Internal.Dyadic+  ( exactDiametralDot+  , exactInCircleDet+  , exactOrientSignDouble+  )+import Moonlight.Triangulation.Internal.SegmentRelation+  ( SegmentRelation (..)+  , segmentRelationWith+  )+import Moonlight.Triangulation.LineSideInfo (LineSideInfo, fromOrdering)+import Moonlight.Triangulation.Scalar+  ( isFinite+  , scalarCcwErrorBound+  , scalarInCircleErrorBound+  )+import Moonlight.Triangulation.Internal.Types (Point (..))++-- The coordinate kernels are package-private because their raw 'Double'+-- arguments carry no finite-range evidence. Every public exact classifier+-- descends through 'QueryPoint'; internal hot paths call these only after their+-- construction boundary has admitted the stored coordinates.+orient2dCoordinates+  :: Double -> Double -> Double -> Double -> Double -> Double+  -> Ordering+orient2dCoordinates ax ay bx by cx cy+  | abs determinant > scalarCcwErrorBound * determinantSum = compare determinant 0+  | otherwise = exactOrientSignDouble ax ay bx by cx cy+ where+  !left = (ax - cx) * (by - cy)+  !right = (ay - cy) * (bx - cx)+  !determinant = left - right+  !determinantSum = abs left + abs right+{-# INLINE orient2dCoordinates #-}++inCircleCoordinates+  :: Double -> Double -> Double -> Double+  -> Double -> Double -> Double -> Double+  -> Ordering+inCircleCoordinates ax ay bx by cx cy dx dy+  | abs determinant > scalarInCircleErrorBound * permanent = compare determinant 0+  | otherwise = compare (exactInCircleDet ax ay bx by cx cy dx dy) 0+ where+  !adx = ax - dx+  !ady = ay - dy+  !bdx = bx - dx+  !bdy = by - dy+  !cdx = cx - dx+  !cdy = cy - dy+  !abdet = adx * bdy - bdx * ady+  !bcdet = bdx * cdy - cdx * bdy+  !cadet = cdx * ady - adx * cdy+  !alift = adx * adx + ady * ady+  !blift = bdx * bdx + bdy * bdy+  !clift = cdx * cdx + cdy * cdy+  !determinant = alift * bcdet + blift * cadet + clift * abdet+  !permanent =+    (abs (bdx * cdy) + abs (cdx * bdy)) * alift+      + (abs (cdx * ady) + abs (adx * cdy)) * blift+      + (abs (adx * bdy) + abs (bdx * ady)) * clift+{-# INLINE inCircleCoordinates #-}++-- | Exact orientation of points whose coordinates have already been admitted.+orient2d :: Point -> Point -> Point -> Ordering+orient2d (Point ax ay) (Point bx by) (Point cx cy) =+  orient2dCoordinates ax ay bx by cx cy+{-# INLINE orient2d #-}++-- | Exact side of an admitted oriented line.+sideQuery :: Point -> Point -> Point -> LineSideInfo+sideQuery from to query = fromOrdering (orient2d from to query)+{-# INLINE sideQuery #-}++-- | Ordering of the admitted oriented incircle determinant.+inCircle :: Point -> Point -> Point -> Point -> Ordering+inCircle+  (Point ax ay)+  (Point bx by)+  (Point cx cy)+  (Point dx dy) =+    inCircleCoordinates ax ay bx by cx cy dx dy+{-# INLINE inCircle #-}++-- | Whether an admitted point lies on an admitted closed segment.+onClosedSegment :: Point -> Point -> Point -> Bool+onClosedSegment from@(Point fromX fromY) to@(Point toX toY) query@(Point queryX queryY) =+  orient2d from to query == EQ+    && queryX >= min fromX toX+    && queryX <= max fromX toX+    && queryY >= min fromY toY+    && queryY <= max fromY toY+{-# INLINE onClosedSegment #-}++-- | Exact relation between admitted closed segments.+segmentRelation :: Point -> Point -> Point -> Point -> SegmentRelation+segmentRelation = segmentRelationWith (==) compare orient2d onClosedSegment++-- | Whether two admitted closed segments share any point.+segmentsIntersect :: Point -> Point -> Point -> Point -> Bool+segmentsIntersect firstFrom firstTo secondFrom secondTo =+  segmentRelation firstFrom firstTo secondFrom secondTo /= SegmentsDisjoint++-- | Whether two admitted segments cross away from their endpoints.+segmentsProperlyCross :: Point -> Point -> Point -> Point -> Bool+segmentsProperlyCross firstFrom firstTo secondFrom secondTo =+  opposite (orient2d firstFrom firstTo secondFrom) (orient2d firstFrom firstTo secondTo)+    && opposite (orient2d secondFrom secondTo firstFrom) (orient2d secondFrom secondTo firstTo)+ where+  opposite LT GT = True+  opposite GT LT = True+  opposite _ _ = False++-- | Whether an admitted point lies in the closed diametral disk of an admitted+-- segment.+inDiametralCircle :: Point -> Point -> Point -> Bool+inDiametralCircle (Point ax ay) (Point bx by) (Point px py)+  | isFinite dot && abs dot > scalarCcwErrorBound * dotSum = dot < 0+  | otherwise = exactDiametralDot ax ay bx by px py <= 0+ where+  !left = (ax - px) * (bx - px)+  !right = (ay - py) * (by - py)+  !dot = left + right+  !dotSum = abs left + abs right+{-# INLINE inDiametralCircle #-}
src-dcel/Moonlight/Triangulation/Internal/Representation.hs view
@@ -54,6 +54,7 @@   , UndirectedEdgeId (..)   , VertexId (..)   )+import Moonlight.Triangulation.Internal.PackedIndex (unpackIndex, unpackOptionalIndex) import Moonlight.Triangulation.Internal.BoxedPaged (BoxedPaged, boxedDefaulted, boxedFromVector, boxedToVector) import Moonlight.Triangulation.Internal.Paged   ( Paged@@ -161,7 +162,7 @@       closesAtStart = do         lastEdge <- terminal         pure-          ( fromIntegral+          ( unpackIndex               (pagedUnsafeIndex (triHalfTopology triangulation) (4 * lastEdge + 1))               == start           )@@ -178,10 +179,7 @@     :: Triangulation mode vertex directed undirected face     -> Maybe Int   frontierStart mesh =-    let raw = pagedUnsafeIndex (triFaceEdge mesh) 0-     in if raw == (maxBound :: Word32)-          then Nothing-          else Just (fromIntegral raw)+    unpackOptionalIndex (pagedUnsafeIndex (triFaceEdge mesh) 0)    frontierStep     :: Int@@ -233,7 +231,7 @@     -> (Double, Double)   frontierPoint mesh rawEdge =     let rawVertex = pagedUnsafeIndex (triHalfTopology mesh) (4 * rawEdge)-        vertex = fromIntegral rawVertex+        vertex = unpackIndex rawVertex      in ( pagedUnsafeIndex (triPointX mesh) vertex         , pagedUnsafeIndex (triPointY mesh) vertex         )
src-dcel/Moonlight/Triangulation/Internal/Validation.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE BangPatterns #-}- -- | Discharge: the invariants the constructors guarantee, checkable on a value -- built by any route. module Moonlight.Triangulation.Internal.Validation@@ -22,8 +20,12 @@ import Moonlight.Triangulation.Dcel import Moonlight.Triangulation.Internal.HandleDefs import Moonlight.Triangulation.Handles.Iterators.FixedIterators (allFaces, directedEdges, undirectedEdges, vertices)-import Moonlight.Triangulation.Internal.PackedIndex (noIndex)+import Moonlight.Triangulation.Internal.PackedIndex (noIndex, unpackIndex)+import Moonlight.Triangulation.Internal.Predicates import Moonlight.Triangulation.Math+  ( squaredDistance+  , triangleArea+  ) import Moonlight.Triangulation.Internal.Representation import Moonlight.Triangulation.Internal.Types @@ -109,42 +111,42 @@     [ EdgeOriginOutOfRange (DirectedEdgeId (fromIntegral index)) (VertexId value) verticesCount     | index <- [0 .. halfCount - 1]     , let value = pagedUnsafeIndex (triHalfTopology triangulation) (4 * index)-    , fromIntegral value >= verticesCount+    , unpackIndex value >= verticesCount     ]       ++ [ EdgeNextOutOfRange (DirectedEdgeId (fromIntegral index)) (DirectedEdgeId value) halfCount          | index <- [0 .. halfCount - 1]          , let value = pagedUnsafeIndex (triHalfTopology triangulation) (4 * index + 1)-         , fromIntegral value >= halfCount+         , unpackIndex value >= halfCount          ]       ++ [ EdgePreviousOutOfRange (DirectedEdgeId (fromIntegral index)) (DirectedEdgeId value) halfCount          | index <- [0 .. halfCount - 1]          , let value = pagedUnsafeIndex (triHalfTopology triangulation) (4 * index + 2)-         , fromIntegral value >= halfCount+         , unpackIndex value >= halfCount          ]       ++ [ EdgeFaceOutOfRange (DirectedEdgeId (fromIntegral index)) (FaceId value) facesCount          | index <- [0 .. halfCount - 1]          , let value = pagedUnsafeIndex (triHalfTopology triangulation) (4 * index + 3)-         , fromIntegral value >= facesCount+         , unpackIndex value >= facesCount          ]       ++ [ VertexOutgoingOutOfRange (VertexId (fromIntegral index)) (DirectedEdgeId value) halfCount          | index <- [0 .. pagedLength (triVertexOut triangulation) - 1]          , let value = pagedUnsafeIndex (triVertexOut triangulation) index          , value /= noIndex-         , fromIntegral value >= halfCount+         , unpackIndex value >= halfCount          ]       ++ [ FaceAdjacentOutOfRange (FaceId (fromIntegral index)) (DirectedEdgeId value) halfCount          | index <- [0 .. facesCount - 1]          , let value = pagedUnsafeIndex (triFaceEdge triangulation) index          , value /= noIndex-         , fromIntegral value >= halfCount+         , unpackIndex value >= halfCount          ]    edgeViolations     | not halfArraysEqual || odd halfCount || not (null rangeViolations) = []     | otherwise = concatMap validateEdge (directedEdges triangulation) -  validateEdge edge@(DirectedEdgeId raw) =-    let index = fromIntegral raw+  validateEdge edge =+    let index = directedEdgeIdIndex edge         nextEdge = next triangulation edge         previousEdge = previous triangulation edge         twinEdge = reverseEdge edge@@ -167,7 +169,7 @@             else []      in if index < halfCount then local ++ innerCycle else [] -  validEdge (DirectedEdgeId value) = fromIntegral value < halfCount+  validEdge edge = directedEdgeIdIndex edge < halfCount    faceViolations     | not (null rangeViolations) = []@@ -257,7 +259,7 @@       , rawPair < numUndirectedEdges triangulation       , let edge = UndirectedEdgeId (fromIntegral rawPair)       , let (forward, backward) = directedPair edge-      , rawFace <- fmap faceRaw [incidentFace triangulation forward, incidentFace triangulation backward]+      , rawFace <- fmap faceIdIndex [incidentFace triangulation forward, incidentFace triangulation backward]       , rawFace > 0       ]   selectedFaces = IntSet.union admittedFaces collarFaces@@ -267,7 +269,7 @@       [ rawEdge       | rawFace <- IntSet.toAscList selectedFaces       , edge <- faceEdgesBounded (FaceId (fromIntegral rawFace))-      , rawEdge <- [edgeRaw edge, edgeRaw (reverseEdge edge)]+      , rawEdge <- [directedEdgeIdIndex edge, directedEdgeIdIndex (reverseEdge edge)]       ]   selectedInterfaceEdges =     IntSet.fromList@@ -284,10 +286,10 @@       | rawEdge <- IntSet.toAscList selectedEdges       , rawEdge >= 0       , rawEdge < halfCount-      , edgeRaw (reverseEdge (DirectedEdgeId (fromIntegral rawEdge))) < halfCount+      , directedEdgeIdIndex (reverseEdge (DirectedEdgeId (fromIntegral rawEdge))) < halfCount       , rawVertex <--          [ vertexRaw (origin triangulation (DirectedEdgeId (fromIntegral rawEdge)))-          , vertexRaw (destination triangulation (DirectedEdgeId (fromIntegral rawEdge)))+          [ vertexIdIndex (origin triangulation (DirectedEdgeId (fromIntegral rawEdge)))+          , vertexIdIndex (destination triangulation (DirectedEdgeId (fromIntegral rawEdge)))           ]       , rawVertex < verticesCount       ]@@ -299,7 +301,7 @@     , let edge = DirectedEdgeId (fromIntegral rawEdge)     , rawEdge < halfCount     , let vertex = origin triangulation edge-    , vertexRaw vertex >= verticesCount+    , vertexIdIndex vertex >= verticesCount     ]       ++ [ EdgeNextOutOfRange edge nextEdge halfCount          | rawEdge <- IntSet.toAscList selectedEdges@@ -307,7 +309,7 @@          , rawEdge < halfCount          , let edge = DirectedEdgeId (fromIntegral rawEdge)          , let nextEdge = next triangulation edge-         , edgeRaw nextEdge >= halfCount+         , directedEdgeIdIndex nextEdge >= halfCount          ]       ++ [ EdgePreviousOutOfRange edge previousEdge halfCount          | rawEdge <- IntSet.toAscList selectedEdges@@ -315,7 +317,7 @@          , rawEdge < halfCount          , let edge = DirectedEdgeId (fromIntegral rawEdge)          , let previousEdge = previous triangulation edge-         , edgeRaw previousEdge >= halfCount+         , directedEdgeIdIndex previousEdge >= halfCount          ]       ++ [ EdgeFaceOutOfRange edge face facesCount          | rawEdge <- IntSet.toAscList selectedEdges@@ -323,7 +325,7 @@          , rawEdge < halfCount          , let edge = DirectedEdgeId (fromIntegral rawEdge)          , let face = incidentFace triangulation edge-         , faceRaw face >= facesCount+         , faceIdIndex face >= facesCount          ]       ++ [ FaceAdjacentOutOfRange face edge halfCount          | rawFace <- IntSet.toAscList selectedFaces@@ -331,7 +333,7 @@          , rawFace < facesCount          , let face = FaceId (fromIntegral rawFace)          , Just edge <- [adjacentEdge triangulation face]-         , edgeRaw edge >= halfCount+         , directedEdgeIdIndex edge >= halfCount          ]    edgeViolations =@@ -344,9 +346,9 @@             nextEdge = next triangulation edge             previousEdge = previous triangulation edge             twinEdge = reverseEdge edge-            nextValid = edgeRaw nextEdge < halfCount-            previousValid = edgeRaw previousEdge < halfCount-            nextNextValid = nextValid && edgeRaw (next triangulation nextEdge) < halfCount+            nextValid = directedEdgeIdIndex nextEdge < halfCount+            previousValid = directedEdgeIdIndex previousEdge < halfCount+            nextNextValid = nextValid && directedEdgeIdIndex (next triangulation nextEdge) < halfCount             innerCycle =               if incidentFace triangulation edge /= outerFace && nextValid && previousValid && nextNextValid                 then [InnerFaceNotTriangularAtEdge edge | next triangulation (next triangulation nextEdge) /= edge]@@ -371,7 +373,7 @@        in case adjacentEdge triangulation face of           Nothing -> [FaceMissingAdjacentEdge face]           Just edge-            | edgeRaw edge >= halfCount -> []+            | directedEdgeIdIndex edge >= halfCount -> []             | otherwise ->                 let representedFace = incidentFace triangulation edge                     (faceEdges, faceVertices') = triangleEdgesAndVertices face@@ -387,7 +389,7 @@    triangleEdgesAndVertices face =     let edges = faceEdgesBounded face-        vertices' = fmap (vertexRaw . origin triangulation) edges+        vertices' = fmap (vertexIdIndex . origin triangulation) edges      in (edges, vertices')    triangleClosed edges =@@ -404,7 +406,7 @@             | verticesCount > 1 -> [ConnectedVertexMissingOutgoing vertex]             | otherwise -> []           Just edge-            | edgeRaw edge >= halfCount ->+            | directedEdgeIdIndex edge >= halfCount ->                 [VertexOutgoingOutOfRange vertex edge halfCount]             | otherwise ->                 [ VertexOutgoingOriginMismatch vertex edge actualOrigin@@ -451,20 +453,16 @@     adjacentEdges = case adjacentEdge triangulation face of       Nothing -> []       Just start-        | edgeRaw start >= halfCount -> []+        | directedEdgeIdIndex start >= halfCount -> []         | otherwise ->             let second = next triangulation start-             in if edgeRaw second >= halfCount+             in if directedEdgeIdIndex second >= halfCount                   then [start]                   else                     let third = next triangulation second-                     in if edgeRaw third >= halfCount+                     in if directedEdgeIdIndex third >= halfCount                           then [start, second]                           else [start, second, third]--  edgeRaw (DirectedEdgeId raw) = fromIntegral raw-  vertexRaw (VertexId raw) = fromIntegral raw-  faceRaw (FaceId raw) = fromIntegral raw  -- | Every edge whose circumcircle is not empty. validateDelaunay :: Triangulation mode vertex directed undirected face -> [InvariantViolation]
src-dcel/Moonlight/Triangulation/IntersectionIterator.hs view
@@ -17,8 +17,12 @@ import Data.Void (Void, absurd) import Moonlight.Triangulation.Dcel import Moonlight.Triangulation.Internal.HandleDefs+import Moonlight.Triangulation.Internal.Predicates import Moonlight.Triangulation.Handles.Iterators.FixedIterators (undirectedEdges, vertices) import Moonlight.Triangulation.Math+  ( projectionFactor+  , squaredDistanceWide+  ) import Moonlight.Triangulation.PointLocation import Moonlight.Triangulation.Types @@ -38,16 +42,20 @@ lineIntersections triangulation queryFrom queryTo =   let !from = queryPointValue queryFrom       !to = queryPointValue queryTo-   in case firstIntersection triangulation queryFrom queryTo of-        Nothing -> []-        Just first -> walkIntersections triangulation from to first+   in if endpointNearerToWalkSeed triangulation to from+        then case firstIntersection triangulation queryTo queryFrom of+          Nothing -> []+          Just first -> walkIntersections AgainstQuery triangulation to from first+        else case firstIntersection triangulation queryFrom queryTo of+          Nothing -> []+          Just first -> walkIntersections AlongQuery triangulation from to first  -- | 'lineIntersections' between two existing vertices. lineIntersectionsBetweenVertices :: Triangulation mode vertex directed undirected face -> VertexId -> VertexId -> [Intersection] lineIntersectionsBetweenVertices triangulation fromVertex toVertex =   let from = vertexPoint triangulation fromVertex       to = vertexPoint triangulation toVertex-   in walkIntersections triangulation from to (VertexIntersection fromVertex)+   in walkIntersections AlongQuery triangulation from to (VertexIntersection fromVertex)  -- | The directed edges a crossing list cuts. conflictingEdges :: [Intersection] -> [DirectedEdgeId]@@ -127,16 +135,51 @@ -- where the answer is 'Void', the left branch is erased outright. {-# INLINE foldCorridor #-} -walkIntersections :: Triangulation mode vertex directed undirected face -> Point -> Point -> Intersection -> [Intersection]-walkIntersections triangulation from to first =+data QueryDirection = AlongQuery | AgainstQuery++walkIntersections+  :: QueryDirection+  -> Triangulation mode vertex directed undirected face+  -> Point+  -> Point+  -> Intersection+  -> [Intersection]+walkIntersections direction triangulation from to first =   case foldCorridor triangulation from to first collect [] of-    Nothing -> exactIntersectionScan triangulation from to+    Nothing -> orientExact (exactIntersectionScan triangulation from to)     Just (Left impossible) -> absurd impossible-    Just (Right events) -> reverse events+    Just (Right events) -> orientCollected events  where   collect :: [Intersection] -> Intersection -> Either Void [Intersection]   collect accumulated event = Right (event : accumulated)+  orientExact = case direction of+    AlongQuery -> id+    AgainstQuery -> map reverseIntersection . reverse+  orientCollected = case direction of+    AlongQuery -> reverse+    AgainstQuery -> map reverseIntersection +reverseIntersection :: Intersection -> Intersection+reverseIntersection event = case event of+  VertexIntersection vertex -> VertexIntersection vertex+  EdgeIntersection edge -> EdgeIntersection (reverseEdge edge)+  EdgeOverlap edge -> EdgeOverlap (reverseEdge edge)++-- The unhinted point locator starts at face one. Choosing the segment endpoint+-- decisively nearer one of that face's vertices shortens the same exact walk+-- without making ordinary interior chords pay to reverse similar paths.+endpointNearerToWalkSeed+  :: Triangulation mode vertex directed undirected face+  -> Point+  -> Point+  -> Bool+endpointNearerToWalkSeed triangulation candidate incumbent =+  case innerFaceVertices triangulation (FaceId 1) of+    Just (anchor, _, _) ->+      let !seed = vertexPoint triangulation anchor+       in 4 * squaredDistanceWide seed candidate < squaredDistanceWide seed incumbent+    Nothing -> False+ nextIntersection :: Triangulation mode vertex directed undirected face -> Point -> Point -> Intersection -> Maybe Intersection nextIntersection triangulation lineFrom lineTo current = case current of   EdgeIntersection edge -> case traceDirectionOutOfEdge triangulation edge lineFrom lineTo of@@ -203,11 +246,10 @@   -- event. A ring that outran its budget, an absent locator edge, and   -- endpoints that leave the parameter comparison without a total order all   -- keep the exact scan.-  firstFromOutside (Just edge)-    | finiteEndpoint lineFrom && finiteEndpoint lineTo =-        case ringEntryEvent triangulation lineFrom lineTo edge of-          Just entry -> eventValue <$> entry-          Nothing -> firstFromScan+  firstFromOutside (Just edge) =+    case hullEntryIntersection triangulation lineFrom lineTo edge of+      Just entry -> entry+      Nothing -> firstFromScan   firstFromOutside _ = firstFromScan    firstFromScan = case exactIntersectionScan triangulation lineFrom lineTo of@@ -370,35 +412,50 @@   high = min 1 (max projectedA projectedB)   oriented = if orient2d a b lineTo == LT then reverseEdge directed else directed --- | The earliest event carried by the outer-face ring the given edge sits on,--- walked from that edge. 'Nothing' reports a ring that outran its budget.-ringEntryEvent+-- | Restrict an outside segment to the hull edge supplied by point location,+-- then follow only the side on which its entry can lie. The outer 'Maybe'+-- witnesses a hull walk that closed within its topology budget; the inner one+-- is the geometric answer. An exhausted or impossible local section descends+-- to the exact scan owned by 'firstIntersection'.+hullEntryIntersection   :: Triangulation mode vertex directed undirected face   -> Point   -> Point   -> DirectedEdgeId-  -> Maybe (Maybe (Event))-ringEntryEvent triangulation lineFrom lineTo entry =-  go (numDirectedEdges triangulation + 1) entry False Nothing+  -> Maybe (Maybe Intersection)+hullEntryIntersection triangulation lineFrom lineTo entry+  | orient2d entryFrom entryTo lineFrom == EQ =+      Just+        ( Just+            ( VertexIntersection+                ( if squaredDistanceWide entryTo lineFrom < squaredDistanceWide entryFrom lineFrom+                    then destination triangulation entry+                    else origin triangulation entry+                )+            )+        )+  | otherwise = go (numDirectedEdges triangulation + 1) entry  where-  go !remaining !edge !departed !earliest+  entryFrom = vertexPoint triangulation (origin triangulation entry)+  entryTo = vertexPoint triangulation (destination triangulation entry)++  go !remaining !edge     | remaining <= 0 = Nothing-    | departed && edge == entry = Just earliest     | otherwise =-        let !stepped =-              keepEarliest (edgeEvent triangulation lineFrom lineTo (asUndirected edge)) $-                keepEarliest (vertexEvent triangulation lineFrom lineTo (origin triangulation edge)) earliest-         in go (remaining - 1) (next triangulation edge) True stepped--keepEarliest :: Maybe (Event) -> Maybe (Event) -> Maybe (Event)-keepEarliest Nothing held = held-keepEarliest candidate Nothing = candidate-keepEarliest candidate@(Just proposed) held@(Just incumbent)-  | compareEvent proposed incumbent == LT = candidate-  | otherwise = held--finiteEndpoint :: Point -> Bool-finiteEndpoint point = isFinite (pointX point) && isFinite (pointY point)+        let !edgeFrom = vertexPoint triangulation (origin triangulation edge)+            !edgeTo = vertexPoint triangulation (destination triangulation edge)+            !fromSide = orient2d lineFrom lineTo edgeFrom+            !toSide = orient2d lineFrom lineTo edgeTo+         in if orient2d edgeFrom edgeTo lineTo == GT+              then Just Nothing+              else case (fromSide == GT, toSide /= LT) of+                (True, True) -> go (remaining - 1) (previous triangulation edge)+                (False, False) -> go (remaining - 1) (next triangulation edge)+                (False, True)+                  | toSide == EQ -> Just (Just (VertexIntersection (destination triangulation edge)))+                  | fromSide == EQ -> Just (Just (VertexIntersection (origin triangulation edge)))+                  | otherwise -> Just (Just (EdgeIntersection (reverseEdge edge)))+                (True, False) -> Nothing  segmentIntersectionParameter :: Point -> Point -> Point -> Point -> Double segmentIntersectionParameter (Point ax ay) (Point bx by) (Point cx cy) (Point dx dy)
src-dcel/Moonlight/Triangulation/Math.hs view
@@ -18,6 +18,8 @@   , segmentDistanceSquared   , segmentDistanceSquaredWide   , distance+  , negateVector+  , normalizeVector   , midpoint   , centroid   , triangleArea@@ -39,22 +41,19 @@  import Moonlight.Triangulation.Internal.Dyadic   ( exactBarycentricDeterminants-  , exactDiametralDot   , integerRatioToDouble   )+import Moonlight.Triangulation.Internal.Predicates qualified as Admitted import Moonlight.Triangulation.Internal.SegmentRelation   ( SegmentRelation (..)   , allSegmentRelations-  , segmentRelationWith   )-import Moonlight.Triangulation.LineSideInfo (LineSideInfo, fromOrdering)+import Moonlight.Triangulation.LineSideInfo (LineSideInfo) import Moonlight.Triangulation.Scalar   ( canonicalScalarZero-  , inCircleCoordinates   , isFinite   , maximumAllowedCoordinate   , minimumAllowedCoordinate-  , orient2dCoordinates   , scalarCcwErrorBound   ) import Moonlight.Triangulation.Internal.Types@@ -65,42 +64,50 @@   , QueryPoint (..)   ) --- | Exact relation between two closed segments.+-- | Exact relation between two admitted closed segments. segmentRelation-  :: Point-  -> Point-  -> Point-  -> Point+  :: QueryPoint+  -> QueryPoint+  -> QueryPoint+  -> QueryPoint   -> SegmentRelation-segmentRelation a b c d =-  segmentRelationWith (==) compare orient2d onClosedSegment a b c d+segmentRelation firstFrom firstTo secondFrom secondTo =+  Admitted.segmentRelation+    (queryPointValue firstFrom)+    (queryPointValue firstTo)+    (queryPointValue secondFrom)+    (queryPointValue secondTo) --- | Whether two closed segments share any point.+-- | Whether two admitted closed segments share any point. segmentsIntersect-  :: Point-  -> Point-  -> Point-  -> Point+  :: QueryPoint+  -> QueryPoint+  -> QueryPoint+  -> QueryPoint   -> Bool-segmentsIntersect a b c d = segmentRelation a b c d /= SegmentsDisjoint+segmentsIntersect firstFrom firstTo secondFrom secondTo =+  Admitted.segmentsIntersect+    (queryPointValue firstFrom)+    (queryPointValue firstTo)+    (queryPointValue secondFrom)+    (queryPointValue secondTo)  -- | The proper-crossing section of 'segmentRelation'. Consumers which reject -- only that constructor need not compute the collinear and endpoint-touch -- distinctions required by the complete ADT after either side already proves -- separation. segmentsProperlyCross-  :: Point-  -> Point-  -> Point-  -> Point+  :: QueryPoint+  -> QueryPoint+  -> QueryPoint+  -> QueryPoint   -> Bool-segmentsProperlyCross a b c d =-  opposite (orient2d a b c) (orient2d a b d)-    && opposite (orient2d c d a) (orient2d c d b)- where-  opposite LT GT = True-  opposite GT LT = True-  opposite _ _ = False+segmentsProperlyCross firstFrom firstTo secondFrom secondTo =+  Admitted.segmentsProperlyCross+    (queryPointValue firstFrom)+    (queryPointValue firstTo)+    (queryPointValue secondFrom)+    (queryPointValue secondTo)  -- | Classify a coordinate outside the exact-predicate input domain. validateCoordinate :: Double -> Maybe CoordinateError@@ -156,15 +163,22 @@   (ax - cx) * (by - cy) - (ay - cy) * (bx - cx) {-# INLINE orientDetApprox #-} --- | Exact orientation ordering of three points.-orient2d :: Point -> Point -> Point -> Ordering-orient2d (Point ax ay) (Point bx by) (Point cx cy) =-  orient2dCoordinates ax ay bx by cx cy+-- | Exact orientation ordering of three admitted points.+orient2d :: QueryPoint -> QueryPoint -> QueryPoint -> Ordering+orient2d first second third =+  Admitted.orient2d+    (queryPointValue first)+    (queryPointValue second)+    (queryPointValue third) {-# INLINE orient2d #-} --- | Exact side of an oriented line.-sideQuery :: Point -> Point -> Point -> LineSideInfo-sideQuery a b point = fromOrdering (orient2d a b point)+-- | Exact side of an admitted oriented line.+sideQuery :: QueryPoint -> QueryPoint -> QueryPoint -> LineSideInfo+sideQuery from to query =+  Admitted.sideQuery+    (queryPointValue from)+    (queryPointValue to)+    (queryPointValue query) {-# INLINE sideQuery #-}  -- | Fast approximate oriented in-circle determinant.@@ -191,26 +205,26 @@   !clift = cdx * cdx + cdy * cdy {-# INLINE inCircleDetApprox #-} --- | Ordering of the oriented incircle determinant. For a counter-clockwise--- triangle, 'GT' means the fourth point lies strictly inside its circumcircle.-inCircle-  :: Point -> Point -> Point -> Point -> Ordering+-- | Ordering of the admitted oriented incircle determinant. For a+-- counter-clockwise triangle, 'GT' means the fourth point lies strictly inside+-- its circumcircle. inCircle-  (Point ax ay)-  (Point bx by)-  (Point cx cy)-  (Point dx dy) =-    inCircleCoordinates ax ay bx by cx cy dx dy+  :: QueryPoint -> QueryPoint -> QueryPoint -> QueryPoint -> Ordering+inCircle first second third query =+  Admitted.inCircle+    (queryPointValue first)+    (queryPointValue second)+    (queryPointValue third)+    (queryPointValue query) {-# INLINE inCircle #-} --- | Whether a point lies on a closed segment.-onClosedSegment :: Point -> Point -> Point -> Bool-onClosedSegment a@(Point ax ay) b@(Point bx by) query@(Point qx qy) =-  orient2d a b query == EQ-    && qx >= min ax bx-    && qx <= max ax bx-    && qy >= min ay by-    && qy <= max ay by+-- | Whether an admitted point lies on an admitted closed segment.+onClosedSegment :: QueryPoint -> QueryPoint -> QueryPoint -> Bool+onClosedSegment from to query =+  Admitted.onClosedSegment+    (queryPointValue from)+    (queryPointValue to)+    (queryPointValue query) {-# INLINE onClosedSegment #-}  -- | Squared Euclidean distance.@@ -253,6 +267,24 @@ distance left right = sqrt (squaredDistance left right) {-# INLINE distance #-} +-- | Negate a point interpreted as a vector.+negateVector :: Point -> Point+negateVector (Point x y) = Point (-x) (-y)+{-# INLINE negateVector #-}++-- | Normalize a point interpreted as a vector without overflowing its length.+normalizeVector :: Point -> Point+normalizeVector (Point x y)+  | scale == 0 = Point 0 0+  | otherwise =+      let !scaledX = x / scale+          !scaledY = y / scale+          !length' = sqrt (scaledX * scaledX + scaledY * scaledY)+       in Point (scaledX / length') (scaledY / length')+ where+  !scale = max (abs x) (abs y)+{-# INLINE normalizeVector #-}+ -- | Midpoint of two points. midpoint :: Point -> Point -> Point midpoint (Point ax ay) (Point bx by) = Point (0.5 * ax + 0.5 * bx) (0.5 * ay + 0.5 * by)@@ -385,8 +417,8 @@         !right = (py - sy) * (rx - sx)      in (left - right, abs left + abs right) --- | Whether a point lies in the closed diametral disk of a segment, decided by--- the sign of @(a-p)·(b-p)@.+-- | Whether an admitted point lies in the closed diametral disk of an admitted+-- segment, decided by the sign of @(a-p)·(b-p)@. -- -- The approximation is two coordinate differences and one product per term -- combined additively, which is the arithmetic shape 'orient2d' is analysed@@ -394,15 +426,12 @@ -- addition does not depend on the sign of that combination. The orientation -- coefficient therefore transfers unchanged, and the exact dot product remains -- the oracle for the uncertain band.-inDiametralCircle :: Point -> Point -> Point -> Bool-inDiametralCircle (Point ax ay) (Point bx by) (Point px py)-  | isFinite dot && abs dot > scalarCcwErrorBound * dotSum = dot < 0-  | otherwise = exactDiametralDot ax ay bx by px py <= 0- where-  !left = (ax - px) * (bx - px)-  !right = (ay - py) * (by - py)-  !dot = left + right-  !dotSum = abs left + abs right+inDiametralCircle :: QueryPoint -> QueryPoint -> QueryPoint -> Bool+inDiametralCircle from to query =+  Admitted.inDiametralCircle+    (queryPointValue from)+    (queryPointValue to)+    (queryPointValue query) {-# INLINE inDiametralCircle #-}  -- | Projection parameter of a point onto an oriented segment line.
src-dcel/Moonlight/Triangulation/PointLocation.hs view
@@ -12,7 +12,7 @@ import Moonlight.Triangulation.Internal.HandleDefs import Moonlight.Triangulation.Handles.Iterators.FixedIterators import Moonlight.Triangulation.Internal.FaceProbe-import Moonlight.Triangulation.Math+import Moonlight.Triangulation.Internal.Predicates import Moonlight.Triangulation.Types  -- | Locate an admitted point without an initial topology hint.@@ -118,8 +118,8 @@ chooseStart :: Triangulation mode vertex directed undirected face -> Maybe LocationHint -> FaceId chooseStart triangulation hint =   case hint of-    Just (FaceHint face@(FaceId index))-      | index > 0 && fromIntegral index < numFaces triangulation -> face+    Just (FaceHint face)+      | face /= outerFace && faceIdIndex face < numFaces triangulation -> face     Just (VertexHint vertex@(VertexId index))       | toInteger index < toInteger (numVertices triangulation) ->           case [face | edge <- vertexOutgoingEdges triangulation vertex, let face = incidentFace triangulation edge, face /= outerFace] of
+ src-dcel/Moonlight/Triangulation/Simplex.hs view
@@ -0,0 +1,226 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE TypeApplications #-}++-- | Canonical labelled simplices and downward-closed planar complexes.+-- Labels, rather than resident DCEL handles, own identity across independently+-- constructed meshes.+module Moonlight.Triangulation.Simplex+  ( SimplexDimension (..)+  , PlanarSimplex+  , PlanarSimplexError (..)+  , planarVertex+  , planarEdge+  , planarFace+  , planarSimplexDimension+  , planarSimplexVertices+  , planarSimplexBoundary+  , PlanarComplex+  , PlanarComplexError (..)+  , planarComplex+  , planarComplexCells+  , planarComplexBasisAt+  , unionPlanarComplex+  , intersectPlanarComplex+  )+where++import Control.DeepSeq (NFData)+import Data.List qualified as List+import Data.List.NonEmpty (NonEmpty (..))+import Data.Set (Set)+import Data.Set qualified as Set+import Data.Vector (Vector)+import Data.Vector qualified as Vector+import GHC.Generics (Generic)++-- | The closed dimension vocabulary for planar simplicial complexes.+data SimplexDimension+  = SimplexDimension0+  | SimplexDimension1+  | SimplexDimension2+  deriving stock (Eq, Ord, Show, Enum, Bounded, Generic)+  deriving anyclass (NFData)++-- | One canonical simplex. Edge and face labels are strictly ascending.+data PlanarSimplex label+  = PlanarVertex !label+  | PlanarEdge !label !label+  | PlanarFace !label !label !label+  deriving stock (Eq, Ord, Show, Generic)+  deriving anyclass (NFData)++-- | A repeated vertex would lower the requested simplex's dimension.+data PlanarSimplexError label+  = PlanarSimplexRepeatedVertex !label+  deriving stock (Eq, Ord, Show, Generic)+  deriving anyclass (NFData)++planarVertex :: label -> PlanarSimplex label+planarVertex = PlanarVertex++planarEdge+  :: Ord label+  => label+  -> label+  -> Either (PlanarSimplexError label) (PlanarSimplex label)+planarEdge firstLabel secondLabel =+  case compare firstLabel secondLabel of+    LT -> Right (PlanarEdge firstLabel secondLabel)+    EQ -> Left (PlanarSimplexRepeatedVertex firstLabel)+    GT -> Right (PlanarEdge secondLabel firstLabel)++planarFace+  :: Ord label+  => label+  -> label+  -> label+  -> Either (PlanarSimplexError label) (PlanarSimplex label)+planarFace firstLabel secondLabel thirdLabel =+  let (firstLow, firstHigh) = ascendingPair firstLabel secondLabel+      (secondLow, finalHigh) = ascendingPair firstHigh thirdLabel+      (finalLow, finalMiddle) = ascendingPair firstLow secondLow+   in if finalLow == finalMiddle+        then Left (PlanarSimplexRepeatedVertex finalLow)+        else+          if finalMiddle == finalHigh+            then Left (PlanarSimplexRepeatedVertex finalMiddle)+            else Right (PlanarFace finalLow finalMiddle finalHigh)++planarSimplexDimension :: PlanarSimplex label -> SimplexDimension+planarSimplexDimension simplex =+  case simplex of+    PlanarVertex _ -> SimplexDimension0+    PlanarEdge _ _ -> SimplexDimension1+    PlanarFace _ _ _ -> SimplexDimension2++planarSimplexVertices :: PlanarSimplex label -> NonEmpty label+planarSimplexVertices simplex =+  case simplex of+    PlanarVertex label -> label :| []+    PlanarEdge firstLabel secondLabel -> firstLabel :| [secondLabel]+    PlanarFace firstLabel secondLabel thirdLabel ->+      firstLabel :| [secondLabel, thirdLabel]++-- | The canonical oriented simplicial boundary.+planarSimplexBoundary+  :: Num coefficient+  => PlanarSimplex label+  -> [(coefficient, PlanarSimplex label)]+planarSimplexBoundary simplex =+  case simplex of+    PlanarVertex _ -> []+    PlanarEdge firstLabel secondLabel ->+      [ (-1, PlanarVertex firstLabel)+      , (1, PlanarVertex secondLabel)+      ]+    PlanarFace firstLabel secondLabel thirdLabel ->+      [ (1, PlanarEdge secondLabel thirdLabel)+      , (-1, PlanarEdge firstLabel thirdLabel)+      , (1, PlanarEdge firstLabel secondLabel)+      ]++-- | A requested cell whose boundary is absent from the same complex.+data PlanarComplexError label+  = PlanarComplexBoundaryMissing+      !(PlanarSimplex label)+      !(PlanarSimplex label)+  deriving stock (Eq, Ord, Show, Generic)+  deriving anyclass (NFData)++-- | An admitted downward-closed complex and its canonical dense bases.+data PlanarComplex label = PlanarComplex+  { storedPlanarComplexCells :: !(Set (PlanarSimplex label))+  , storedPlanarVertexBasis :: !(Vector (PlanarSimplex label))+  , storedPlanarEdgeBasis :: !(Vector (PlanarSimplex label))+  , storedPlanarFaceBasis :: !(Vector (PlanarSimplex label))+  }+  deriving stock (Show, Generic)+  deriving anyclass (NFData)++instance Eq label => Eq (PlanarComplex label) where+  left == right =+    storedPlanarComplexCells left == storedPlanarComplexCells right++planarComplex+  :: Ord label+  => Set (PlanarSimplex label)+  -> Either (PlanarComplexError label) (PlanarComplex label)+planarComplex cells =+  case List.find (\(_, boundary) -> Set.notMember boundary cells) boundaryPairs of+    Just (simplex, boundary) ->+      Left (PlanarComplexBoundaryMissing simplex boundary)+    Nothing -> Right (admittedPlanarComplex cells)+ where+  boundaryPairs =+    [ (simplex, boundary)+    | simplex <- Set.toAscList cells+    , (_, boundary) <- planarSimplexBoundary @Int simplex+    ]++planarComplexCells :: PlanarComplex label -> Set (PlanarSimplex label)+planarComplexCells = storedPlanarComplexCells++planarComplexBasisAt+  :: SimplexDimension+  -> PlanarComplex label+  -> Vector (PlanarSimplex label)+planarComplexBasisAt dimension complexValue =+  case dimension of+    SimplexDimension0 -> storedPlanarVertexBasis complexValue+    SimplexDimension1 -> storedPlanarEdgeBasis complexValue+    SimplexDimension2 -> storedPlanarFaceBasis complexValue++-- | Union preserves downward closure, so no second validation is needed.+unionPlanarComplex+  :: Ord label+  => PlanarComplex label+  -> PlanarComplex label+  -> PlanarComplex label+unionPlanarComplex left right =+  admittedPlanarComplex+    (Set.union (planarComplexCells left) (planarComplexCells right))++-- | Intersection also preserves downward closure.+intersectPlanarComplex+  :: Ord label+  => PlanarComplex label+  -> PlanarComplex label+  -> PlanarComplex label+intersectPlanarComplex left right =+  admittedPlanarComplex+    (Set.intersection (planarComplexCells left) (planarComplexCells right))++admittedPlanarComplex :: Set (PlanarSimplex label) -> PlanarComplex label+admittedPlanarComplex cells =+  let (vertices, edges, faces) =+        foldr partitionSimplex ([], [], []) (Set.toAscList cells)+   in PlanarComplex+        { storedPlanarComplexCells = cells+        , storedPlanarVertexBasis = Vector.fromList vertices+        , storedPlanarEdgeBasis = Vector.fromList edges+        , storedPlanarFaceBasis = Vector.fromList faces+        }++partitionSimplex+  :: PlanarSimplex label+  -> ( [PlanarSimplex label]+     , [PlanarSimplex label]+     , [PlanarSimplex label]+     )+  -> ( [PlanarSimplex label]+     , [PlanarSimplex label]+     , [PlanarSimplex label]+     )+partitionSimplex simplex (vertices, edges, faces) =+  case simplex of+    PlanarVertex _ -> (simplex : vertices, edges, faces)+    PlanarEdge _ _ -> (vertices, simplex : edges, faces)+    PlanarFace _ _ _ -> (vertices, edges, simplex : faces)++ascendingPair :: Ord value => value -> value -> (value, value)+ascendingPair firstValue secondValue =+  if firstValue <= secondValue+    then (firstValue, secondValue)+    else (secondValue, firstValue)
src-dcel/Moonlight/Triangulation/Valuation.hs view
@@ -11,6 +11,15 @@   , eulerCharacteristicValue   , ExactArea   , exactAreaValue+  , ExactPlanarMoments+  , exactPlanarMeasure+  , exactPlanarFirstX+  , exactPlanarFirstY+  , exactPlanarSecondXX+  , exactPlanarSecondXY+  , exactPlanarSecondYY+  , scaleExactPlanarMoments+  , polygonComponentMoments   , polygonComponentArea   , ExactLengthTerm   , lengthCoefficient@@ -68,6 +77,8 @@   , UndirectedEdgeId (..)   , VertexId (..)   , directedPair+  , faceIdIndex+  , vertexIdIndex   ) import Moonlight.Triangulation.Internal.CellSet   ( ExactCellSet (..)@@ -84,6 +95,7 @@   , exactRationalDenominator   , exactRationalFromDyadic   , exactRationalFromFiniteDouble+  , exactRationalFromNormalizedRatio   , exactRationalIsZero   , exactRationalNumerator   , exactSignum@@ -128,12 +140,126 @@ exactAreaValue :: ExactArea -> ExactRational exactAreaValue (ExactArea value) = value +-- | Exact moments of a bounded planar measure through total degree two.+data ExactPlanarMoments = ExactPlanarMoments+  { exactPlanarMeasure :: !ExactRational+    -- ^ Total signed measure.+  , exactPlanarFirstX :: !ExactRational+    -- ^ Raw first moment integral of @x@.+  , exactPlanarFirstY :: !ExactRational+    -- ^ Raw first moment integral of @y@.+  , exactPlanarSecondXX :: !ExactRational+    -- ^ Raw second moment integral of @x^2@.+  , exactPlanarSecondXY :: !ExactRational+    -- ^ Raw mixed moment integral of @x*y@.+  , exactPlanarSecondYY :: !ExactRational+    -- ^ Raw second moment integral of @y^2@.+  }+  deriving stock (Eq, Ord, Show, Generic)+  deriving anyclass (NFData)++instance Semigroup ExactPlanarMoments where+  left <> right =+    ExactPlanarMoments+      { exactPlanarMeasure = exactPlanarMeasure left + exactPlanarMeasure right+      , exactPlanarFirstX = exactPlanarFirstX left + exactPlanarFirstX right+      , exactPlanarFirstY = exactPlanarFirstY left + exactPlanarFirstY right+      , exactPlanarSecondXX = exactPlanarSecondXX left + exactPlanarSecondXX right+      , exactPlanarSecondXY = exactPlanarSecondXY left + exactPlanarSecondXY right+      , exactPlanarSecondYY = exactPlanarSecondYY left + exactPlanarSecondYY right+      }++instance Monoid ExactPlanarMoments where+  mempty = ExactPlanarMoments 0 0 0 0 0 0++-- | Scale every moment by one exact coefficient.+scaleExactPlanarMoments+  :: ExactRational+  -> ExactPlanarMoments+  -> ExactPlanarMoments+scaleExactPlanarMoments scalar moments =+  ExactPlanarMoments+    { exactPlanarMeasure = scalar * exactPlanarMeasure moments+    , exactPlanarFirstX = scalar * exactPlanarFirstX moments+    , exactPlanarFirstY = scalar * exactPlanarFirstY moments+    , exactPlanarSecondXX = scalar * exactPlanarSecondXX moments+    , exactPlanarSecondXY = scalar * exactPlanarSecondXY moments+    , exactPlanarSecondYY = scalar * exactPlanarSecondYY moments+    }++-- | Exact area and raw moments of one admitted component. The six unscaled+-- boundary sums are accumulated together and normalized once.+polygonComponentMoments :: PolygonComponent -> ExactPlanarMoments+polygonComponentMoments component =+  let raw =+        foldMap+          loopMomentSums+          (polygonOuterLoop component : polygonHoleLoops component)+   in ExactPlanarMoments+        { exactPlanarMeasure = oneHalf * rawDoubleArea raw+        , exactPlanarFirstX = oneSixth * rawFirstX raw+        , exactPlanarFirstY = oneSixth * rawFirstY raw+        , exactPlanarSecondXX = oneTwelfth * rawSecondXX raw+        , exactPlanarSecondXY = oneTwentyFourth * rawSecondXY raw+        , exactPlanarSecondYY = oneTwelfth * rawSecondYY raw+        }+ -- | 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 RawPlanarMomentSums = RawPlanarMomentSums+  { rawDoubleArea :: !ExactRational+  , rawFirstX :: !ExactRational+  , rawFirstY :: !ExactRational+  , rawSecondXX :: !ExactRational+  , rawSecondXY :: !ExactRational+  , rawSecondYY :: !ExactRational+  }++instance Semigroup RawPlanarMomentSums where+  left <> right =+    RawPlanarMomentSums+      { rawDoubleArea = rawDoubleArea left + rawDoubleArea right+      , rawFirstX = rawFirstX left + rawFirstX right+      , rawFirstY = rawFirstY left + rawFirstY right+      , rawSecondXX = rawSecondXX left + rawSecondXX right+      , rawSecondXY = rawSecondXY left + rawSecondXY right+      , rawSecondYY = rawSecondYY left + rawSecondYY right+      }++instance Monoid RawPlanarMomentSums where+  mempty = RawPlanarMomentSums 0 0 0 0 0 0++loopMomentSums :: ExactLoop -> RawPlanarMomentSums+loopMomentSums (ExactLoop points) =+  List.foldl' accumulateMomentEdge mempty (cyclePairs points)++accumulateMomentEdge+  :: RawPlanarMomentSums+  -> (ExactPoint, ExactPoint)+  -> RawPlanarMomentSums+accumulateMomentEdge accumulated (from, to) =+  let (fromX, fromY) = exactPointCoordinates from+      (toX, toY) = exactPointCoordinates to+      cross = exactPointCross from to+   in RawPlanarMomentSums+        { rawDoubleArea = rawDoubleArea accumulated + cross+        , rawFirstX = rawFirstX accumulated + (fromX + toX) * cross+        , rawFirstY = rawFirstY accumulated + (fromY + toY) * cross+        , rawSecondXX =+            rawSecondXX accumulated+              + (fromX * fromX + fromX * toX + toX * toX) * cross+        , rawSecondXY =+            rawSecondXY accumulated+              + (2 * fromX * fromY + fromX * toY + toX * fromY + 2 * toX * toY) * cross+        , rawSecondYY =+            rawSecondYY accumulated+              + (fromY * fromY + fromY * toY + toY * toY) * cross+        }+ data ExactLengthTerm = ExactLengthTerm   { lengthCoefficient :: !ExactRational   , squaredLength :: !ExactRational@@ -280,9 +406,7 @@ cellEdgeLengthContribution triangulation points selectedFaces edge = do   let (fromVertex, toVertex) = undirectedEndpoints triangulation edge       (forward, backward) = directedPair edge-      selected face =-        let FaceId raw = face-         in IntSet.member (fromIntegral raw) selectedFaces+      selected face = IntSet.member (faceIdIndex face) selectedFaces       coefficient = case (selected (incidentFace triangulation forward), selected (incidentFace triangulation backward)) of         (False, False) -> 1         (True, True) -> 0@@ -295,11 +419,11 @@   :: IntMap.IntMap ExactPoint   -> VertexId   -> Either ValuationError ExactPoint-cellPoint points vertex@(VertexId raw) =+cellPoint points vertex =   maybe     (Left (ValuationCoordinateMissing vertex))     Right-    (IntMap.lookup (fromIntegral raw) points)+    (IntMap.lookup (vertexIdIndex vertex) points)  triangleDoubleArea :: ExactPoint -> ExactPoint -> ExactPoint -> ExactRational triangleDoubleArea firstPoint secondPoint thirdPoint =@@ -660,3 +784,12 @@  oneHalf :: ExactRational oneHalf = exactRationalFromDyadic 1 (-1)++oneSixth :: ExactRational+oneSixth = exactRationalFromNormalizedRatio (1 Ratio.% 6)++oneTwelfth :: ExactRational+oneTwelfth = exactRationalFromNormalizedRatio (1 Ratio.% 12)++oneTwentyFourth :: ExactRational+oneTwentyFourth = exactRationalFromNormalizedRatio (1 Ratio.% 24)
src-dual/Moonlight/Triangulation/Interpolation.hs view
@@ -3,7 +3,6 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-}-{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} @@ -54,14 +53,16 @@   ( DirectedEdgeId (..)   , asUndirected   , FaceId (..)+  , faceIdIndex   , VertexId (..)   , reverseEdge+  , vertexIdIndex   ) import Moonlight.Triangulation.Internal.InterpolationWorkspace+import Moonlight.Triangulation.Internal.Predicates (inCircle) import Moonlight.Triangulation.Math   ( barycentricCoordinates   , circumcenter-  , inCircle   , isFinite   , projectionFactor   , squaredDistanceWide@@ -135,8 +136,8 @@   !startDistance = squaredDistanceWide query (vertexPoint triangulation start)   !bound = numDirectedEdges triangulation + numVertices triangulation + 1 -  validateHint (Just vertex@(VertexId index))-    | fromIntegral index < numVertices triangulation = vertex+  validateHint (Just vertex)+    | vertexIdIndex vertex < numVertices triangulation = vertex   validateHint _ = VertexId 0    -- A vertex's Voronoi cell is the intersection of the half-planes its@@ -335,8 +336,8 @@   -> Point   -> FaceId   -> ST s (Int, Int)-discoverCavity workspace generation query (FaceId rawStart) = do-  MUV.unsafeWrite (nnFaceSeenMarks workspace) (fromIntegral rawStart) generation+discoverCavity workspace generation query startFace@(FaceId rawStart) = do+  MUV.unsafeWrite (nnFaceSeenMarks workspace) (faceIdIndex startFace) generation   MUV.unsafeWrite (nnFaceQueue workspace) 0 rawStart   go 1 0 0  where@@ -352,7 +353,7 @@         if not inside           then go slot cavitySize tests'           else do-            MUV.unsafeWrite (nnFaceMarks workspace) (fromIntegral rawFace) generation+            MUV.unsafeWrite (nnFaceMarks workspace) (faceIdIndex face) generation             MUV.unsafeWrite (nnCavityFaces workspace) cavitySize rawFace             nextQueue <- pushNeighbors triangulation workspace generation slot face             go nextQueue (cavitySize + 1) tests'@@ -395,7 +396,7 @@  where   pushOne !size edge =     let !adjacent@(FaceId raw) = incidentFace triangulation (reverseEdge edge)-        !index = fromIntegral raw+        !index = faceIdIndex adjacent      in if adjacent == outerFace           then pure size           else do@@ -427,10 +428,10 @@             appendIfBoundary size2 e2         goFaces (index + 1) nextSize   appendIfBoundary !size edge = do-    let adjacent@(FaceId rawAdjacent) = incidentFace triangulation (reverseEdge edge)+    let adjacent = incidentFace triangulation (reverseEdge edge)     outside <- if adjacent == outerFace       then pure True-      else (/= generation) <$> MUV.unsafeRead (nnFaceMarks workspace) (fromIntegral rawAdjacent)+      else (/= generation) <$> MUV.unsafeRead (nnFaceMarks workspace) (faceIdIndex adjacent)     if outside       then case edge of         DirectedEdgeId raw -> MUV.unsafeWrite (nnBoundaryEdges workspace) size raw >> pure (size + 1)@@ -452,7 +453,7 @@     | index >= count = pure ()     | otherwise = do         rawEdge <- MUV.unsafeRead (nnBoundaryEdges workspace) index-        let !from = vertexIndex (origin triangulation (DirectedEdgeId rawEdge))+        let !from = vertexIdIndex (origin triangulation (DirectedEdgeId rawEdge))         MUV.unsafeWrite (nnOriginMarks workspace) from generation         MUV.unsafeWrite (nnOriginEdge workspace) from rawEdge         install generation (index + 1)@@ -461,7 +462,7 @@         if currentRaw == firstRaw then pure count else pure 0     | otherwise = do         MUV.unsafeWrite (nnOrderedEdges workspace) index currentRaw-        let !to = vertexIndex (destination triangulation (DirectedEdgeId currentRaw))+        let !to = vertexIdIndex (destination triangulation (DirectedEdgeId currentRaw))         marked <- MUV.unsafeRead (nnOriginMarks workspace) to         if marked /= generation           then pure 0@@ -591,7 +592,7 @@   -> Point   -> FaceId   -> ST s (Maybe (Point))-cachedFaceCircumcenter workspace generation query face@(FaceId rawFace) = do+cachedFaceCircumcenter workspace generation query face = do   stamp <- MUV.unsafeRead (nnCircumcenterMarks workspace) slot   if stamp == generation     then do@@ -606,7 +607,7 @@         MUV.unsafeWrite (nnCircumcenterMarks workspace) slot generation         pure (Just center)  where-  !slot = fromIntegral rawFace+  !slot = faceIdIndex face  faceCircumcenterRelative   :: Triangulation mode vertex directed undirected face@@ -884,10 +885,6 @@         , alpha + c1Weight * radiusSquared         , beta + weight * radiusSquared         )--vertexIndex :: VertexId -> Int-vertexIndex (VertexId raw) = fromIntegral raw-{-# INLINE vertexIndex #-}  subtractPoint :: Point -> Point -> Point subtractPoint (Point ax ay) (Point bx by) = Point (ax - bx) (ay - by)
src-dual/Moonlight/Triangulation/Voronoi.hs view
@@ -260,34 +260,17 @@       VoronoiSegment <$> faceCircumcenter triangulation fromFace <*> faceCircumcenter triangulation toFace   | innerFrom = do       start <- faceCircumcenter triangulation fromFace-      pure (VoronoiRay start (normalize (voronoiDirectionVector triangulation edge)))+      pure (VoronoiRay start (normalizeVector (voronoiDirectionVector triangulation edge)))   | innerTo = do       end <- faceCircumcenter triangulation toFace-      pure (VoronoiRay end (normalize (negatePoint (voronoiDirectionVector triangulation edge))))+      pure (VoronoiRay end (normalizeVector (negateVector (voronoiDirectionVector triangulation edge))))   | otherwise =       let !center = midpoint             (vertexPoint triangulation (origin triangulation primal))             (vertexPoint triangulation (destination triangulation primal))-       in Just (VoronoiLine center (normalize (voronoiDirectionVector triangulation edge)))+       in Just (VoronoiLine center (normalizeVector (voronoiDirectionVector triangulation edge)))  where   !fromFace = incidentFace triangulation primal   !toFace = incidentFace triangulation (reverseEdge primal)   !innerFrom = fromFace /= outerFace   !innerTo = toFace /= outerFace--normalize :: Point -> Point-normalize (Point x y)-  | scale == 0 = Point 0 0-  | otherwise =-      let !scaledX = x / scale-          !scaledY = y / scale-          !length' = sqrt (scaledX * scaledX + scaledY * scaledY)-       in Point (scaledX / length') (scaledY / length')- where-  !scale = max (abs x) (abs y)--negatePoint :: Point -> Point-negatePoint (Point x y) = Point (-x) (-y)--{-# INLINE normalize #-}-{-# INLINE negatePoint #-}
src-dual/Moonlight/Triangulation/Voronoi/Handles.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-}  -- | Zero-copy Voronoi views that retain the owning triangulation.@@ -37,7 +36,7 @@   ) where  import Moonlight.Triangulation.Handles.Dynamic-import Moonlight.Triangulation.Math (midpoint)+import Moonlight.Triangulation.Math (midpoint, negateVector, normalizeVector) import Moonlight.Triangulation.Types import Moonlight.Triangulation.Voronoi @@ -269,27 +268,13 @@       VoronoiSegment <$> innerFaceCircumcenter fromFace <*> innerFaceCircumcenter toFace     (InnerVoronoiVertexHandle fromFace, OuterVoronoiVertexHandle _) -> do       start <- innerFaceCircumcenter fromFace-      pure (VoronoiRay start (normalizePoint (voronoiEdgeDirectionH edge)))+      pure (VoronoiRay start (normalizeVector (voronoiEdgeDirectionH edge)))     (OuterVoronoiVertexHandle _, InnerVoronoiVertexHandle toFace) -> do       end <- innerFaceCircumcenter toFace-      pure (VoronoiRay end (normalizePoint (negatePoint (voronoiEdgeDirectionH edge))))+      pure (VoronoiRay end (normalizeVector (negateVector (voronoiEdgeDirectionH edge))))     (OuterVoronoiVertexHandle _, OuterVoronoiVertexHandle _) ->       let (from, to) = directedEdgePositions primal-       in Just (VoronoiLine (midpoint from to) (normalizePoint (voronoiEdgeDirectionH edge)))--normalizePoint :: Point -> Point-normalizePoint (Point x y)-  | scale == 0 = Point 0 0-  | otherwise =-      let !scaledX = x / scale-          !scaledY = y / scale-          !length' = sqrt (scaledX * scaledX + scaledY * scaledY)-       in Point (scaledX / length') (scaledY / length')- where-  !scale = max (abs x) (abs y)--negatePoint :: Point -> Point-negatePoint (Point x y) = Point (-x) (-y)+       in Just (VoronoiLine (midpoint from to) (normalizeVector (voronoiEdgeDirectionH edge)))  -- The handle verbs cross the same component boundary as the fixed-index ones -- and carry their unfoldings for the same reason. 'voronoiVertexPositionH'
src-overlay/Moonlight/Triangulation/Embedding.hs view
@@ -47,7 +47,7 @@   , unorderedPairs   ) import Moonlight.Triangulation.Internal.ExactRational (ExactRational)-import qualified Moonlight.Triangulation.Math as Math+import qualified Moonlight.Triangulation.Internal.Predicates as Admitted   ( orient2d   , segmentRelation   )@@ -459,7 +459,7 @@   exactRelation =     relationFor exactSegmentRelation projectedExactPoint firstSegment secondSegment   roundedRelation =-    relationFor Math.segmentRelation roundedProjectedPoint firstSegment secondSegment+    relationFor Admitted.segmentRelation roundedProjectedPoint firstSegment secondSegment  relationFor   :: (point -> point -> point -> point -> SegmentRelation)@@ -507,7 +507,7 @@   -> Ordering compareRoundedAround center left right =   compareRoundedAroundWith-    ( Math.orient2d+    ( Admitted.orient2d         (roundedVertexPoint center)         (roundedVertexPoint left)         (roundedVertexPoint right)
src-public/Moonlight/Triangulation.hs view
@@ -119,6 +119,7 @@   , insertVertex   , insertVertexAt   , insertVertexAtNear+  , insertVertexAtNearVertex   , removeAt   , removeAtNear   , removeManyAt@@ -493,7 +494,16 @@   , EulerCharacteristic   , eulerCharacteristicValue   , ExactArea+  , ExactPlanarMoments   , exactAreaValue+  , exactPlanarMeasure+  , exactPlanarFirstX+  , exactPlanarFirstY+  , exactPlanarSecondXX+  , exactPlanarSecondXY+  , exactPlanarSecondYY+  , scaleExactPlanarMoments+  , polygonComponentMoments   , polygonComponentArea   , ExactLengthTerm   , lengthCoefficient@@ -533,11 +543,13 @@   , PowerWeight   , PowerWeightError (..)   , powerWeight+  , powerWeightFromExact   , powerWeightExact   , PowerSite   , powerSite   , powerSiteLabel   , powerSitePosition+  , powerSiteExactPosition   , powerSiteWeight   , PowerCellDisposition (..)   , RegularSiteDisposition (..)@@ -549,14 +561,26 @@   , regularEdgeLabels   , regularEdgeDual   , RegularTriangulation+  , emptyRegularTriangulation   , regularTriangulation+  , regularSite+  , regularSites+  , regularSiteCount   , regularSiteDisposition   , regularFaces   , regularEdges   , regularNeighbours+  , regularTriangulationReceipt   , RegularTriangulationReceipt (..)+  , RegularSiteTransition (..)+  , RegularEditError (..)+  , RegularEditResult (..)+  , insertRegularSite+  , removeRegularSite+  , reweightRegularSites   , BoundedPowerDiagram   , boundedPowerDiagram+  , boundedPowerDiagramFromRegular   , powerCellDisposition   , powerCellDispositions   , powerDiagramPlanarLayer@@ -569,6 +593,18 @@   , AffineForm (..)   , UpperEnvelopeError (..)   , upperEnvelope+  , PowerAlphaBirth+  , powerAlphaBirthExact+  , powerAlphaBirthNumerator+  , powerAlphaBirthDenominator+  , RegularAlphaFiltration+  , RegularAlphaError (..)+  , regularAlphaFiltration+  , regularAlphaComplex+  , regularAlphaBirths+  , regularAlphaSimplexBirth+  , regularAlphaCriticalBirths+  , regularAlphaComplexAtBirth      -- * Discharge — the invariants the constructors guarantee, checkable on a     -- value built by any route; every violation is a value carrying its witness@@ -1023,13 +1059,19 @@   , PowerWeight   , PowerWeightError (..)   , RegularEdge+  , RegularEditError (..)+  , RegularEditResult (..)   , RegularFace   , RegularSiteDisposition (..)+  , RegularSiteTransition (..)   , RegularTriangulation   , RegularTriangulationReceipt (..)   , RegularTopologyError (..)   , UpperEnvelopeError (..)   , boundedPowerDiagram+  , boundedPowerDiagramFromRegular+  , emptyRegularTriangulation+  , insertRegularSite   , powerCellDisposition   , powerCellDispositions   , powerDiagramPlanarLayer@@ -1039,8 +1081,10 @@   , powerSite   , powerSiteLabel   , powerSitePosition+  , powerSiteExactPosition   , powerSiteWeight   , powerWeight+  , powerWeightFromExact   , powerWeightExact   , regularEdgeDual   , regularEdgeLabels@@ -1049,10 +1093,30 @@   , regularFaceLabels   , regularFaces   , regularNeighbours+  , regularSite+  , regularSiteCount   , regularSiteDisposition+  , regularSites   , regularTriangulation+  , regularTriangulationReceipt+  , removeRegularSite+  , reweightRegularSites   , upperEnvelope   )+import Moonlight.Triangulation.RegularAlpha+  ( PowerAlphaBirth+  , RegularAlphaError (..)+  , RegularAlphaFiltration+  , powerAlphaBirthDenominator+  , powerAlphaBirthExact+  , powerAlphaBirthNumerator+  , regularAlphaBirths+  , regularAlphaComplex+  , regularAlphaComplexAtBirth+  , regularAlphaCriticalBirths+  , regularAlphaFiltration+  , regularAlphaSimplexBirth+  ) import Moonlight.Triangulation.Payload   ( PayloadTraversal   , directedPayloads@@ -1122,6 +1186,7 @@   , insertVertex   , insertVertexAt   , insertVertexAtNear+  , insertVertexAtNearVertex   , refuse   , removeAt   , removeAtNear@@ -1141,6 +1206,7 @@   ( CertifiedInterval (..)   , EulerCharacteristic   , ExactArea+  , ExactPlanarMoments   , ExactLengthExpression   , ExactLengthMeasurement   , ExactLengthTerm@@ -1150,7 +1216,14 @@   , cellValuations   , eulerCharacteristicValue   , exactAreaValue+  , exactPlanarFirstX+  , exactPlanarFirstY+  , exactPlanarMeasure+  , exactPlanarSecondXX+  , exactPlanarSecondXY+  , exactPlanarSecondYY   , polygonComponentArea+  , polygonComponentMoments   , exactLengthBounds   , exactLengthExpression   , exactLengthTerms@@ -1158,6 +1231,7 @@   , planarValuationsPerimeter   , regionPerimeter   , regionValuations+  , scaleExactPlanarMoments   , squaredLength   , valuationArea   , valuationEuler
src-public/Moonlight/Triangulation/HintGenerator.hs view
@@ -32,7 +32,11 @@ import Data.Word (Word32) import Moonlight.Triangulation.BulkLoad (delaunay, insert) import Moonlight.Triangulation.Dcel (numUndirectedEdges, numVertices, undirectedEndpoints, vertexPoint)-import Moonlight.Triangulation.Internal.HandleDefs (UndirectedEdgeId (..), VertexId (..))+import Moonlight.Triangulation.Internal.HandleDefs+  ( UndirectedEdgeId (..)+  , VertexId (..)+  , vertexIdIndex+  ) import Moonlight.Triangulation.Interpolation (nearestNeighbor) import Moonlight.Triangulation.Math (canonicalPoint, validatePoint) import Moonlight.Triangulation.Removal (RemovalOutcome, removalTriangulation, removeVertex)@@ -239,7 +243,7 @@   case disposition of     AlreadyPresent -> Right hierarchy     Inserted-      | vertexIndex vertex /= baseIndex ->+      | vertexIdIndex vertex /= baseIndex ->           Left             ( HierarchyInsertionHandleMismatch                 (VertexId (fromIntegral baseIndex))@@ -277,7 +281,7 @@           )     | otherwise = do         inserted <- insert level point-        if vertexIndex (insertionVertex inserted) /= numVertices level+        if vertexIdIndex (insertionVertex inserted) /= numVertices level           then             Left               ( HierarchyInsertionHandleMismatch@@ -348,7 +352,7 @@   -- The freed slot is the removed vertex's own index, and it is where the   -- former last vertex now stands. A removal that took the last vertex frees   -- no slot and reports none, and its removed index is that last index.-  !freedSlot = maybe surviving (vertexIndex . fst) swap+  !freedSlot = maybe surviving (vertexIdIndex . fst) swap   !vacatedIndex = surviving    -- The nesting law is stated here as an equation for the same reason the@@ -473,9 +477,6 @@ samplePopulation count divisor   | count <= 0 = 0   | otherwise = 1 + (count - 1) `quot` divisor--vertexIndex :: VertexId -> Int-vertexIndex (VertexId value) = fromIntegral value  -- The hierarchy is consulted once per query from another package, so its -- polymorphic entries expose their unfoldings for the same reason the search
+ src-public/Moonlight/Triangulation/Internal/PowerDiagram/Edit.hs view
@@ -0,0 +1,1376 @@+-- | Pure topology-changing regular edits by exact conflict-cavity descent and gluing.+module Moonlight.Triangulation.Internal.PowerDiagram.Edit where++import Control.Monad (when)+import Data.Bifunctor (first)+import qualified Data.Foldable as Foldable+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 (isJust)+import qualified Data.Set as Set+import Data.Set (Set)+import qualified Data.Vector as Vector+import Moonlight.Triangulation.Exact+  ( exactOrient2d+  , exactPoint+  )+import Moonlight.Triangulation.Internal.BoundaryCycle (orderedPair)+import Moonlight.Triangulation.Internal.ExactRational (ExactRational)+import Moonlight.Triangulation.Internal.PowerDiagram.Generator+import Moonlight.Triangulation.Internal.PowerDiagram.Hull (regularGeneratorTopology)+import Moonlight.Triangulation.Internal.PowerDiagram.Locality+import Moonlight.Triangulation.Internal.PowerDiagram.Model+import Moonlight.Triangulation.Internal.PowerDiagram.Section++-- | Insert one stable-labelled site. Repeating the identical site is+-- idempotent; reusing its label for different geometry is a typed conflict.+insertRegularSite+  :: Ord label+  => PowerSite label+  -> RegularTriangulation label+  -> Either+      (RegularEditError label)+      (RegularEditResult label)+insertRegularSite site triangulation =+  let label = powerSiteLabel site+   in case Map.lookup label (storedRegularSites triangulation) of+        Nothing ->+          withRegularEditFallback+            triangulation+            (Set.singleton label)+            ( normalizeRegularEdit+                triangulation+                (Map.insert label site (storedRegularSites triangulation))+                (Set.singleton label)+            )+            (insertRegularSiteLocally site triangulation)+        Just resident+          | resident == site ->+              Right (unchangedRegularEdit triangulation)+          | otherwise -> Left (RegularEditSiteConflict label resident site)++-- | Remove one labelled site. Absence is an idempotent successful result.+removeRegularSite+  :: Ord label+  => label+  -> RegularTriangulation label+  -> Either+      (RegularEditError label)+      (RegularEditResult label)+removeRegularSite label triangulation =+  case Map.lookup label (storedRegularSites triangulation) of+    Nothing ->+      Right (unchangedRegularEdit triangulation)+    Just _ ->+      case removeTopologyPreservingSite label triangulation of+        Just (edited, transitions) ->+          Right+            ( RegularEditResult+                edited+                (Set.singleton label)+                transitions+            )+        Nothing ->+          withRegularEditFallback+            triangulation+            (Set.singleton label)+            ( normalizeRegularEdit+                triangulation+                (Map.delete label (storedRegularSites triangulation))+                (Set.singleton label)+            )+            (removeRegularSiteLocally label triangulation)++-- | Replace many weights in one normalization. Every label must already+-- belong to the triangulation; an empty or unchanged patch is idempotent.+reweightRegularSites+  :: Ord label+  => Map label PowerWeight+  -> RegularTriangulation label+  -> Either+      (RegularEditError label)+      (RegularEditResult label)+reweightRegularSites requested triangulation =+  case NonEmpty.nonEmpty unknownLabels of+    Just unknown -> Left (RegularEditUnknownSites unknown)+    Nothing+      | Map.null changedSites ->+          Right (unchangedRegularEdit triangulation)+      | otherwise ->+          case reweightTopologyPreservingSites changedLabels changedSites reweightedSites triangulation of+            Just (edited, transitions) ->+              Right+                ( RegularEditResult+                    edited+                    changedLabels+                    transitions+                )+            Nothing ->+              case commonRegularWeightShift requested sites of+                Just shift ->+                  Right+                    ( translateRegularWeights+                        shift+                        reweightedSites+                        changedLabels+                        triangulation+                    )+                Nothing ->+                  case Map.elems changedSites of+                    [revised] ->+                      withRegularEditFallback+                        triangulation+                        changedLabels+                        normalizeGlobally+                        (reweightRegularSiteLocally revised triangulation)+                    _ -> normalizeGlobally+ where+  sites = storedRegularSites triangulation+  unknownLabels = Map.keys (Map.difference requested sites)+  changedSites =+    Map.mapMaybeWithKey+      (\label weight ->+          case Map.lookup label sites of+            Just site+              | powerSiteWeight site /= weight ->+                  Just site{powerSiteWeight = weight}+            _ -> Nothing+      )+      requested+  reweightedSites = Map.union changedSites sites+  changedLabels = Map.keysSet changedSites+  normalizeGlobally =+    normalizeRegularEdit triangulation reweightedSites changedLabels++withRegularEditFallback+  :: Ord label+  => RegularTriangulation label+  -> Set label+  -> Either (RegularEditError label) (RegularEditResult label)+  -> Either obstruction (RegularTriangulation label, Set label)+  -> Either (RegularEditError label) (RegularEditResult label)+withRegularEditFallback before changed fallback =+  either (const fallback) (Right . publishLocalRegularEdit before changed)+{-# INLINE withRegularEditFallback #-}++publishLocalRegularEdit+  :: Ord label+  => RegularTriangulation label+  -> Set label+  -> (RegularTriangulation label, Set label)+  -> RegularEditResult label+publishLocalRegularEdit before changed (edited, transitionSupport) =+  RegularEditResult+    edited+    changed+    ( dispositionTransitionsOn+        transitionSupport+        (regularDispositionSection before)+        (regularDispositionSection edited)+    )+{-# INLINE publishLocalRegularEdit #-}++insertRegularSiteLocally+  :: Ord label+  => PowerSite label+  -> RegularTriangulation label+  -> Either+      (LocalRegularEditObstruction label)+      (RegularTriangulation label, Set label)+insertRegularSiteLocally site triangulation = do+  section <-+    maybe (Left LocalRegularSectionUnavailable) Right+      (storedRegularSection triangulation)+  let (generator, _) = prepareExactPowerGenerator site+      slope = exactGeneratorSlope generator+  case Map.lookup slope (sectionSlopeRepresentatives section) of+    Just representative ->+      insertCoincidentRegularSite site generator representative section triangulation+    Nothing ->+      insertDistinctRegularSite site generator section triangulation++insertCoincidentRegularSite+  :: Ord label+  => PowerSite label+  -> ExactPowerGenerator label+  -> label+  -> RegularSection label+  -> RegularTriangulation label+  -> Either+      (LocalRegularEditObstruction label)+      (RegularTriangulation label, Set label)+insertCoincidentRegularSite site generator representativeLabel section triangulation = do+  representative <-+    requireGeneratorFromMap (sectionGenerators section) representativeLabel+  let label = powerSiteLabel site+  case+    compareCoincidentPriority+      (exactPowerGeneratorConstant generator, label)+      (exactPowerGeneratorConstant representative, representativeLabel) of+    LT ->+      let coincident = classifyCoincidentGenerator representative generator+          disposition = coincidentRegularDisposition coincident+          editedSection =+            section+              { sectionCoincidentDispositions =+                  Map.insert label coincident (sectionCoincidentDispositions section)+              , sectionRegularDispositions =+                  Map.insert label disposition (sectionRegularDispositions section)+              , sectionRegularStars =+                  Map.insert label emptyRegularSiteStar (sectionRegularStars section)+              }+       in Right+            ( replaceRegularSiteSection+                (Map.insert label site (storedRegularSites triangulation))+                editedSection+                triangulation+            , Set.singleton label+            )+    _ -> Left (LocalRegularSlopeCoincidence representativeLabel)++insertDistinctRegularSite+  :: Ord label+  => PowerSite label+  -> ExactPowerGenerator label+  -> RegularSection label+  -> RegularTriangulation label+  -> Either+      (LocalRegularEditObstruction label)+      (RegularTriangulation label, Set label)+insertDistinctRegularSite site generator section triangulation = do+  locality <-+    maybe (Left LocalRegularLocalityUnavailable) Right+      (sectionRegularLocality section)+  location <-+    maybe (Left LocalRegularSlopeLocationFailed) Right+      (locateRegularSlope (localityFaceSeed locality) section generator)+  case location of+    RegularSlopeOutside edgeKey -> Left (LocalRegularSlopeOutside edgeKey)+    RegularSlopeInside faceKey -> do+      volume <- regularFaceVolume section faceKey generator+      case compare volume 0 of+        LT -> insertHiddenRegularSite site generator faceKey locality section triangulation+        EQ -> Left (LocalRegularDegenerateFace faceKey)+        GT -> insertVisibleRegularSite site generator faceKey locality section triangulation++insertHiddenRegularSite+  :: Ord label+  => PowerSite label+  -> ExactPowerGenerator label+  -> RegularFaceKey label+  -> RegularLocality label+  -> RegularSection label+  -> RegularTriangulation label+  -> Either+      (LocalRegularEditObstruction label)+      (RegularTriangulation label, Set label)+insertHiddenRegularSite site generator supportFace locality section triangulation =+  let label = powerSiteLabel site+      receipt = sectionRegularReceipt section+      editedLocality = attachRegularHidden label supportFace locality+      editedSection =+        section+          { sectionGenerators = Map.insert label generator (sectionGenerators section)+          , sectionSlopeRepresentatives =+              Map.insert (exactGeneratorSlope generator) label (sectionSlopeRepresentatives section)+          , sectionRegularDispositions =+              Map.insert label RegularSiteHidden (sectionRegularDispositions section)+          , sectionRegularStars =+              Map.insert label emptyRegularSiteStar (sectionRegularStars section)+          , sectionRegularReceipt =+              receipt+                { generatorRegularInputSites = generatorRegularInputSites receipt + 1+                , generatorRegularHiddenSites = generatorRegularHiddenSites receipt + 1+                }+          , sectionRegularLocality = Just editedLocality+          }+   in Right+        ( replaceRegularSiteSection+            (Map.insert label site (storedRegularSites triangulation))+            editedSection+            triangulation+        , Set.singleton label+        )++insertVisibleRegularSite+  :: Ord label+  => PowerSite label+  -> ExactPowerGenerator label+  -> RegularFaceKey label+  -> RegularLocality label+  -> RegularSection label+  -> RegularTriangulation label+  -> Either+      (LocalRegularEditObstruction label)+      (RegularTriangulation label, Set label)+insertVisibleRegularSite site generator seedFace locality section triangulation = do+  conflictFaces <- regularConflictCavity section generator seedFace+  horizon <- regularConflictHorizon section conflictFaces+  when (Map.null horizon) (Left LocalRegularEmptyHorizon)+  newFaces <- traverse (regularHorizonFace section generator) (Map.elems horizon)+  let label = powerSiteLabel site+      generators = Map.insert label generator (sectionGenerators section)+  facePatch <-+    applyRegularFacePatch+      generators+      conflictFaces+      newFaces+      section+  certifyRegularFacePatch generators facePatch+  let changedTopologyLabels = Set.insert label (patchedRegularLabels facePatch)+      provisionalDispositions =+        Set.foldl'+          (publishPatchedRegularDisposition (patchedRegularStars facePatch))+          (Map.insert label RegularSiteVisible (sectionRegularDispositions section))+          changedTopologyLabels+      newlyHidden =+        Set.filter+          (becameRegularHidden (sectionRegularDispositions section) provisionalDispositions)+          changedTopologyLabels+      hiddenOnConflict =+        Set.unions+          [ Map.findWithDefault Set.empty faceKey (localitySupportHidden locality)+          | faceKey <- Set.toAscList conflictFaces+          ]+      hiddenToAttach = Set.union newlyHidden hiddenOnConflict+      receipt = sectionRegularReceipt section+      provisionalSection =+        section+          { sectionGenerators = generators+          , sectionSlopeRepresentatives =+              Map.insert (exactGeneratorSlope generator) label (sectionSlopeRepresentatives section)+          , sectionRegularDispositions = provisionalDispositions+          , sectionRegularFaces = patchedRegularFaces facePatch+          , sectionRegularEdges = patchedRegularEdges facePatch+          , sectionRegularStars = patchedRegularStars facePatch+          , sectionRegularReceipt =+              receipt+                { generatorRegularInputSites = generatorRegularInputSites receipt + 1+                , generatorRegularVisibleSites =+                    generatorRegularVisibleSites receipt + 1 - Set.size newlyHidden+                , generatorRegularHiddenSites =+                    generatorRegularHiddenSites receipt + Set.size newlyHidden+                , generatorRegularFaceCount = Map.size (patchedRegularFaces facePatch)+                , generatorRegularEdgeCount = Map.size (patchedRegularEdges facePatch)+                }+          , sectionRegularLocality = Nothing+          }+  editedLocality <-+    patchRegularLocality locality conflictFaces hiddenToAttach provisionalSection+  let editedSection = provisionalSection{sectionRegularLocality = Just editedLocality}+  pure+    ( replaceRegularSiteSection+        (Map.insert label site (storedRegularSites triangulation))+        editedSection+        triangulation+    , Set.insert label newlyHidden+    )+++regularFaceVolume+  :: Ord label+  => RegularSection label+  -> RegularFaceKey label+  -> ExactPowerGenerator label+  -> Either (LocalRegularEditObstruction label) ExactRational+regularFaceVolume section faceKey candidate = do+  face <- requireFaceFromMap (sectionRegularFaces section) faceKey+  (firstGenerator, secondGenerator, thirdGenerator) <-+    regularFaceGenerators (sectionGenerators section) face+  pure+    ( exactGeneratorLiftedVolume+        firstGenerator+        secondGenerator+        thirdGenerator+        candidate+    )++regularConflictCavity+  :: Ord label+  => RegularSection label+  -> ExactPowerGenerator label+  -> RegularFaceKey label+  -> Either+      (LocalRegularEditObstruction label)+      (Set (RegularFaceKey label))+regularConflictCavity section candidate seed =+  let initial =+        RegularConflictDescent+          { conflictPendingFaces = Set.singleton seed+          , conflictVisitedFaces = Set.empty+          , conflictVisibleFaces = Set.empty+          , conflictObstruction = Nothing+          }+      descended =+        Map.foldl'+          (\current _ -> advanceRegularConflict section candidate current)+          initial+          (sectionRegularFaces section)+   in case conflictObstruction descended of+        Just obstruction -> Left obstruction+        Nothing+          | not (Set.null (conflictPendingFaces descended)) ->+              Left LocalRegularSlopeLocationFailed+          | Set.null (conflictVisibleFaces descended) ->+              Left (LocalRegularDegenerateFace seed)+          | otherwise -> Right (conflictVisibleFaces descended)++advanceRegularConflict+  :: Ord label+  => RegularSection label+  -> ExactPowerGenerator label+  -> RegularConflictDescent label+  -> RegularConflictDescent label+advanceRegularConflict _ _ descent+  | Set.null (conflictPendingFaces descent)+      || isJust (conflictObstruction descent) = descent+advanceRegularConflict section candidate descent =+  case Set.minView (conflictPendingFaces descent) of+    Nothing -> descent+    Just (faceKey, remaining) ->+      case regularFaceVolume section faceKey candidate of+        Left obstruction ->+          descent+            { conflictPendingFaces = remaining+            , conflictObstruction = Just obstruction+            }+        Right volume ->+          let visited = Set.insert faceKey (conflictVisitedFaces descent)+           in case compare volume 0 of+                LT ->+                  descent+                    { conflictPendingFaces = remaining+                    , conflictVisitedFaces = visited+                    }+                EQ ->+                  descent+                    { conflictPendingFaces = remaining+                    , conflictVisitedFaces = visited+                    , conflictObstruction = Just (LocalRegularDegenerateFace faceKey)+                    }+                GT ->+                  case regularFaceNeighbours section faceKey of+                    Left obstruction ->+                      descent+                        { conflictPendingFaces = remaining+                        , conflictVisitedFaces = visited+                        , conflictObstruction = Just obstruction+                        }+                    Right neighbours ->+                      descent+                        { conflictPendingFaces =+                            Set.union remaining (Set.difference neighbours visited)+                        , conflictVisitedFaces = visited+                        , conflictVisibleFaces =+                            Set.insert faceKey (conflictVisibleFaces descent)+                        }+++regularConflictHorizon+  :: Ord label+  => RegularSection label+  -> Set (RegularFaceKey label)+  -> Either+      (LocalRegularEditObstruction label)+      (Map (RegularEdgeKey label) (label, label))+regularConflictHorizon section conflictFaces = do+  boundarySections <-+    traverse+      (regularConflictFaceHorizon section conflictFaces)+      (Set.toAscList conflictFaces)+  pure (Map.unions boundarySections)++regularConflictFaceHorizon+  :: Ord label+  => RegularSection label+  -> Set (RegularFaceKey label)+  -> RegularFaceKey label+  -> Either+      (LocalRegularEditObstruction label)+      (Map (RegularEdgeKey label) (label, label))+regularConflictFaceHorizon section conflictFaces faceKey = do+  face <- requireFaceFromMap (sectionRegularFaces section) faceKey+  associations <-+    traverse+      (classifyConflictEdge section conflictFaces)+      (regularFaceDirectedEdgeKeys face)+  pure (Map.fromList (concat associations))++classifyConflictEdge+  :: Ord label+  => RegularSection label+  -> Set (RegularFaceKey label)+  -> (label, label)+  -> Either+      (LocalRegularEditObstruction label)+      [(RegularEdgeKey label, (label, label))]+classifyConflictEdge section conflictFaces directed@(fromLabel, toLabel) = do+  let edgeKey = orderedPair fromLabel toLabel+  incident <- requireRegularEdgeIncidence section edgeKey+  if Set.size incident > 2+    then Left (LocalRegularNonManifoldEdge edgeKey (Set.size incident))+    else+      if Set.size (Set.intersection incident conflictFaces) == 1+        then Right [(edgeKey, directed)]+        else Right []++regularHorizonFace+  :: Ord label+  => RegularSection label+  -> ExactPowerGenerator label+  -> (label, label)+  -> Either (LocalRegularEditObstruction label) (RegularFace label)+regularHorizonFace section candidate (fromLabel, toLabel) = do+  fromGenerator <- requireGeneratorFromMap (sectionGenerators section) fromLabel+  toGenerator <- requireGeneratorFromMap (sectionGenerators section) toLabel+  let candidateLabel = exactPowerGeneratorLabel candidate+      faceKey = sortedRegularFaceLabels fromLabel toLabel candidateLabel+      fromPoint = uncurry exactPoint (exactGeneratorSlope fromGenerator)+      toPoint = uncurry exactPoint (exactGeneratorSlope toGenerator)+      candidatePoint = uncurry exactPoint (exactGeneratorSlope candidate)+  case exactOrient2d fromPoint toPoint candidatePoint of+    GT -> do+      dual <-+        first LocalRegularTopologyObstruction+          (exactGeneratorFaceDual fromGenerator toGenerator candidate)+      pure (canonicalRegularFace fromLabel toLabel candidateLabel dual)+    _ -> Left (LocalRegularDegenerateFace faceKey)++applyRegularFacePatch+  :: Ord label+  => Map label (ExactPowerGenerator label)+  -> Set (RegularFaceKey label)+  -> [RegularFace label]+  -> RegularSection label+  -> Either (LocalRegularEditObstruction label) (RegularFacePatch label)+applyRegularFacePatch generators removedFaceKeys newFaces section = do+  removedFaces <-+    traverse+      (requireFaceFromMap (sectionRegularFaces section))+      (Set.toAscList removedFaceKeys)+  let newFaceSection =+        Map.fromList [(regularFaceKey face, face) | face <- newFaces]+      faces =+        Map.union newFaceSection+          (Map.withoutKeys (sectionRegularFaces section) removedFaceKeys)+      touchedEdges =+        Set.unions+          [ Set.unions (fmap (Set.fromList . regularFaceCanonicalEdgeKeys) removedFaces)+          , Set.unions (fmap (Set.fromList . regularFaceCanonicalEdgeKeys) newFaces)+          ]+      newIncidence =+        List.foldl'+          (Map.unionWith Set.union)+          Map.empty+          (fmap regularFaceEdgeIncidence newFaces)+      affectedLabels =+        Set.unions+          [ Set.unions (fmap regularFaceLabelSet removedFaces)+          , Set.unions (fmap regularFaceLabelSet newFaces)+          ]+  edges <-+    Foldable.foldlM+      (patchRegularEdge generators faces removedFaceKeys newIncidence)+      (sectionRegularEdges section)+      touchedEdges+  let stars =+        patchRegularStars+          affectedLabels+          removedFaceKeys+          newFaceSection+          touchedEdges+          edges+          section+  pure+    RegularFacePatch+      { patchedRegularFaces = faces+      , patchedRegularEdges = edges+      , patchedRegularStars = stars+      , patchedRegularLabels = affectedLabels+      , patchedRegularTouchedEdges = touchedEdges+      }++patchRegularEdge+  :: Ord label+  => Map label (ExactPowerGenerator label)+  -> Map (RegularFaceKey label) (RegularFace label)+  -> Set (RegularFaceKey label)+  -> Map (RegularEdgeKey label) (Set (RegularFaceKey label))+  -> Map (RegularEdgeKey label) (RegularEdgeSection label)+  -> RegularEdgeKey label+  -> Either+      (LocalRegularEditObstruction label)+      (Map (RegularEdgeKey label) (RegularEdgeSection label))+patchRegularEdge generators faces removedFaceKeys newIncidence edges edgeKey =+  let oldIncidence =+        maybe Set.empty sectionRegularEdgeFaces (Map.lookup edgeKey edges)+      incidence =+        Set.union+          (Set.difference oldIncidence removedFaceKeys)+          (Map.findWithDefault Set.empty edgeKey newIncidence)+   in if Set.null incidence+        then Right (Map.delete edgeKey edges)+        else do+          edgeSection <- publishRegularEdgeSection generators faces edgeKey incidence+          pure (Map.insert edgeKey edgeSection edges)++publishRegularEdgeSection+  :: Ord label+  => Map label (ExactPowerGenerator label)+  -> Map (RegularFaceKey label) (RegularFace label)+  -> RegularEdgeKey label+  -> Set (RegularFaceKey label)+  -> Either (LocalRegularEditObstruction label) (RegularEdgeSection label)+publishRegularEdgeSection generators faces edgeKey@(firstLabel, secondLabel) incidence =+  case Set.toAscList incidence of+    [faceKey] -> do+      face <- requireFaceFromMap faces faceKey+      (fromLabel, toLabel, oppositeLabel) <-+        requireFaceEdgeWitness edgeKey face+      fromGenerator <- requireGeneratorFromMap generators fromLabel+      toGenerator <- requireGeneratorFromMap generators toLabel+      oppositeGenerator <- requireGeneratorFromMap generators oppositeLabel+      ray <-+        first LocalRegularTopologyObstruction+          ( exactGeneratorBoundaryDualRay+              (regularFaceDualPoint face)+              fromGenerator+              toGenerator+              oppositeGenerator+          )+      pure+        ( RegularEdgeSection+            (RegularEdge firstLabel secondLabel (UnboundedPowerDual ray))+            incidence+        )+    [firstFaceKey, secondFaceKey] -> do+      firstFace <- requireFaceFromMap faces firstFaceKey+      secondFace <- requireFaceFromMap faces secondFaceKey+      let firstDual = regularFaceDualPoint firstFace+          secondDual = regularFaceDualPoint secondFace+      dual <-+        fmap publishGeneratorDualGeometry+          ( first LocalRegularTopologyObstruction+              ( exactGeneratorDualBetween+                  firstLabel+                  secondLabel+                  firstDual+                  secondDual+              )+          )+      pure+        ( RegularEdgeSection+            (RegularEdge firstLabel secondLabel dual)+            incidence+        )+    incidentFaces ->+      Left (LocalRegularNonManifoldEdge edgeKey (length incidentFaces))++patchRegularStars+  :: Ord label+  => Set label+  -> Set (RegularFaceKey label)+  -> Map (RegularFaceKey label) (RegularFace label)+  -> Set (RegularEdgeKey label)+  -> Map (RegularEdgeKey label) (RegularEdgeSection label)+  -> RegularSection label+  -> Map label (RegularSiteStar label)+patchRegularStars affectedLabels removedFaceKeys newFaceSection touchedEdges edges section =+  Set.foldl' patchStar (sectionRegularStars section) affectedLabels+ where+  affectedSupport = Map.fromSet (const ()) affectedLabels+  touchedStars =+    regularSiteStars+      affectedSupport+      Map.empty+      ( Map.restrictKeys+          (Map.union edges (sectionRegularEdges section))+          touchedEdges+      )+  addedStars =+    regularSiteStars+      affectedSupport+      newFaceSection+      (Map.restrictKeys edges touchedEdges)+  patchStar stars label =+    let oldStar = Map.findWithDefault emptyRegularSiteStar label stars+        touchedStar = Map.findWithDefault emptyRegularSiteStar label touchedStars+        addedStar = Map.findWithDefault emptyRegularSiteStar label addedStars+        incidentFaces =+          Set.union+            (Set.difference (sectionIncidentFaces oldStar) removedFaceKeys)+            (sectionIncidentFaces addedStar)+        retainedNeighbours =+          Set.difference+            (sectionSiteNeighbours oldStar)+            (sectionSiteNeighbours touchedStar)+     in Map.insert+          label+          ( RegularSiteStar+              incidentFaces+              (Set.union retainedNeighbours (sectionSiteNeighbours addedStar))+          )+          stars++certifyRegularFacePatch+  :: Ord label+  => Map label (ExactPowerGenerator label)+  -> RegularFacePatch label+  -> Either (LocalRegularEditObstruction label) ()+certifyRegularFacePatch generators patch =+  Foldable.traverse_+    (\edgeKey ->+       case Map.lookup edgeKey (patchedRegularEdges patch) of+         Nothing -> Right ()+         Just edgeSection ->+           certifyRegularEdgeConvexity+             generators+             (patchedRegularFaces patch)+             (edgeKey, edgeSection))+    (patchedRegularTouchedEdges patch)++certifyRegularEdgeConvexity+  :: Ord label+  => Map label (ExactPowerGenerator label)+  -> Map (RegularFaceKey label) (RegularFace label)+  -> (RegularEdgeKey label, RegularEdgeSection label)+  -> Either (LocalRegularEditObstruction label) ()+certifyRegularEdgeConvexity generators faces (edgeKey, edgeSection) =+  case Set.toAscList (sectionRegularEdgeFaces edgeSection) of+    [_] -> Right ()+    [firstFaceKey, secondFaceKey] -> do+      firstFace <- requireFaceFromMap faces firstFaceKey+      secondFace <- requireFaceFromMap faces secondFaceKey+      (_, _, secondOpposite) <- requireFaceEdgeWitness edgeKey secondFace+      secondOppositeGenerator <- requireGeneratorFromMap generators secondOpposite+      (firstGenerator, secondGenerator, thirdGenerator) <-+        regularFaceGenerators generators firstFace+      if exactGeneratorLiftedVolume+          firstGenerator+          secondGenerator+          thirdGenerator+          secondOppositeGenerator+          < 0+        then Right ()+        else Left (LocalRegularNonConvexEdge edgeKey)+    incident -> Left (LocalRegularNonManifoldEdge edgeKey (length incident))++publishPatchedRegularDisposition+  :: Ord label+  => Map label (RegularSiteStar label)+  -> Map label (RegularSiteDisposition label)+  -> label+  -> Map label (RegularSiteDisposition label)+publishPatchedRegularDisposition stars dispositions label =+  case Map.lookup label dispositions of+    Just RegularSiteVisible+      | maybe True (Set.null . sectionIncidentFaces) (Map.lookup label stars) ->+          Map.insert label RegularSiteHidden dispositions+    _ -> dispositions++becameRegularHidden+  :: Ord label+  => Map label (RegularSiteDisposition label)+  -> Map label (RegularSiteDisposition label)+  -> label+  -> Bool+becameRegularHidden before after label =+  Map.lookup label before == Just RegularSiteVisible+    && Map.lookup label after == Just RegularSiteHidden++requireRegularEdgeIncidence+  :: Ord label+  => RegularSection label+  -> RegularEdgeKey label+  -> Either+      (LocalRegularEditObstruction label)+      (Set (RegularFaceKey label))+requireRegularEdgeIncidence section edgeKey =+  maybe+    (Left (LocalRegularNonManifoldEdge edgeKey 0))+    (Right . sectionRegularEdgeFaces)+    (Map.lookup edgeKey (sectionRegularEdges section))++regularFaceGenerators+  :: Ord label+  => Map label (ExactPowerGenerator label)+  -> RegularFace label+  -> Either+      (LocalRegularEditObstruction label)+      ( ExactPowerGenerator label+      , ExactPowerGenerator label+      , ExactPowerGenerator label+      )+regularFaceGenerators generators face = do+  let (firstLabel, secondLabel, thirdLabel) = regularFaceLabels face+  (,,)+    <$> requireGeneratorFromMap generators firstLabel+    <*> requireGeneratorFromMap generators secondLabel+    <*> requireGeneratorFromMap generators thirdLabel++removeRegularSiteLocally+  :: Ord label+  => label+  -> RegularTriangulation label+  -> Either+      (LocalRegularEditObstruction label)+      (RegularTriangulation label, Set label)+removeRegularSiteLocally label triangulation = do+  section <-+    maybe (Left LocalRegularSectionUnavailable) Right+      (storedRegularSection triangulation)+  locality <-+    maybe (Left LocalRegularLocalityUnavailable) Right+      (sectionRegularLocality section)+  disposition <-+    maybe (Left (LocalRegularGeneratorMissing label)) Right+      (Map.lookup label (sectionRegularDispositions section))+  when+    (disposition /= RegularSiteVisible || hasCoincidentAliases label section)+    (Left (LocalRegularBoundaryRemoval label))+  star <-+    maybe (Left (LocalRegularGeneratorMissing label)) Right+      (Map.lookup label (sectionRegularStars section))+  let removedFaces = sectionIncidentFaces star+  when (Set.null removedFaces) (Left (LocalRegularBoundaryRemoval label))+  Foldable.traverse_+    (requireInteriorRemovalEdge section label)+    (sectionSiteNeighbours star)+  horizon <- regularConflictHorizon section removedFaces+  when (Map.null horizon) (Left LocalRegularEmptyHorizon)+  let hiddenCandidates =+        Set.unions+          [ Map.findWithDefault Set.empty faceKey (localitySupportHidden locality)+          | faceKey <- Set.toAscList removedFaces+          ]+      horizonLabels =+        Set.fromList+          [ endpoint+          | (firstLabel, secondLabel) <- Map.keys horizon+          , endpoint <- [firstLabel, secondLabel]+          ]+      candidateLabels = Set.union horizonLabels hiddenCandidates+      generators = Map.delete label (sectionGenerators section)+  candidateGenerators <-+    traverse+      (requireGeneratorFromMap generators)+      (Set.toAscList candidateLabels)+  nonEmptyCandidates <-+    maybe (Left LocalRegularCandidateSectionEmpty) Right+      (NonEmpty.nonEmpty candidateGenerators)+  candidateTopology <-+    first LocalRegularTopologyObstruction+      (regularGeneratorTopology (DistinctSlopeGenerators nonEmptyCandidates))+  when+    ( any+        ((== RegularGeneratorLowerDimensional) . snd)+        (NonEmpty.toList (generatorRegularDispositions candidateTopology))+    )+    (Left LocalRegularCavityBoundaryMismatch)+  removedFaceValues <-+    traverse+      (requireFaceFromMap (sectionRegularFaces section))+      (Set.toAscList removedFaces)+  let proposedFaces = fmap publishGeneratorFace (generatorRegularFaces candidateTopology)+      newFaces =+        filter+          (regularFaceCentroidInside generators removedFaceValues)+          proposedFaces+      proposedBoundary = regularFaceSectionBoundary newFaces+  when+    (proposedBoundary /= Map.keysSet horizon)+    (Left LocalRegularCavityBoundaryMismatch)+  facePatch <- applyRegularFacePatch generators removedFaces newFaces section+  certifyRegularFacePatch generators facePatch+  let candidateDispositions =+        Set.foldl'+          (publishRemovalCandidateDisposition (patchedRegularStars facePatch))+          (Map.delete label (sectionRegularDispositions section))+          candidateLabels+      exposed =+        Set.filter+          (becameRegularVisible (sectionRegularDispositions section) candidateDispositions)+          candidateLabels+      stillHidden =+        Set.filter+          (\candidate -> Map.lookup candidate candidateDispositions == Just RegularSiteHidden)+          hiddenCandidates+      receipt = sectionRegularReceipt section+      provisionalSection =+        section+          { sectionGenerators = generators+          , sectionSlopeRepresentatives =+              Map.filter (/= label) (sectionSlopeRepresentatives section)+          , sectionRegularDispositions = candidateDispositions+          , sectionRegularFaces = patchedRegularFaces facePatch+          , sectionRegularEdges = patchedRegularEdges facePatch+          , sectionRegularStars = Map.delete label (patchedRegularStars facePatch)+          , sectionRegularReceipt =+              receipt+                { generatorRegularInputSites = generatorRegularInputSites receipt - 1+                , generatorRegularVisibleSites =+                    generatorRegularVisibleSites receipt - 1 + Set.size exposed+                , generatorRegularHiddenSites =+                    generatorRegularHiddenSites receipt - Set.size exposed+                , generatorRegularFaceCount = Map.size (patchedRegularFaces facePatch)+                , generatorRegularEdgeCount = Map.size (patchedRegularEdges facePatch)+                }+          , sectionRegularLocality = Nothing+          }+  editedLocality <-+    patchRegularLocality locality removedFaces stillHidden provisionalSection+  let editedSection = provisionalSection{sectionRegularLocality = Just editedLocality}+  pure+    ( replaceRegularSiteSection+        (Map.delete label (storedRegularSites triangulation))+        editedSection+        triangulation+    , Set.insert label exposed+    )++requireInteriorRemovalEdge+  :: Ord label+  => RegularSection label+  -> label+  -> label+  -> Either (LocalRegularEditObstruction label) ()+requireInteriorRemovalEdge section label neighbour = do+  let edgeKey = orderedPair label neighbour+  incidence <- requireRegularEdgeIncidence section edgeKey+  if Set.size incidence == 2+    then Right ()+    else Left (LocalRegularBoundaryRemoval label)++regularFaceCentroidInside+  :: Ord label+  => Map label (ExactPowerGenerator label)+  -> [RegularFace label]+  -> RegularFace label+  -> Bool+regularFaceCentroidInside generators domainFaces candidate =+  case regularFaceSlopeSum generators candidate of+    Left _ -> False+    Right slopeSum -> any (regularSlopeSumInsideFace generators slopeSum) domainFaces++regularFaceSlopeSum+  :: Ord label+  => Map label (ExactPowerGenerator label)+  -> RegularFace label+  -> Either+      (LocalRegularEditObstruction label)+      (ExactRational, ExactRational)+regularFaceSlopeSum generators face = do+  (firstGenerator, secondGenerator, thirdGenerator) <-+    regularFaceGenerators generators face+  let (firstX, firstY) = exactGeneratorSlope firstGenerator+      (secondX, secondY) = exactGeneratorSlope secondGenerator+      (thirdX, thirdY) = exactGeneratorSlope thirdGenerator+  pure (firstX + secondX + thirdX, firstY + secondY + thirdY)++regularSlopeSumInsideFace+  :: Ord label+  => Map label (ExactPowerGenerator label)+  -> (ExactRational, ExactRational)+  -> RegularFace label+  -> Bool+regularSlopeSumInsideFace generators slopeSum =+  not+    . any (generatorEdgeExcludesScaled 3 slopeSum generators)+    . regularFaceDirectedEdgeKeys++regularFaceSectionBoundary+  :: Ord label+  => [RegularFace label]+  -> Set (RegularEdgeKey label)+regularFaceSectionBoundary =+  Map.keysSet+    . Map.filter ((== 1) . Set.size)+    . List.foldl'+        (Map.unionWith Set.union)+        Map.empty+    . fmap regularFaceEdgeIncidence++publishRemovalCandidateDisposition+  :: Ord label+  => Map label (RegularSiteStar label)+  -> Map label (RegularSiteDisposition label)+  -> label+  -> Map label (RegularSiteDisposition label)+publishRemovalCandidateDisposition stars dispositions label =+  Map.insert+    label+    ( if maybe True (Set.null . sectionIncidentFaces) (Map.lookup label stars)+        then RegularSiteHidden+        else RegularSiteVisible+    )+    dispositions++becameRegularVisible+  :: Ord label+  => Map label (RegularSiteDisposition label)+  -> Map label (RegularSiteDisposition label)+  -> label+  -> Bool+becameRegularVisible before after label =+  Map.lookup label before == Just RegularSiteHidden+    && Map.lookup label after == Just RegularSiteVisible++reweightRegularSiteLocally+  :: Ord label+  => PowerSite label+  -> RegularTriangulation label+  -> Either+      (LocalRegularEditObstruction label)+      (RegularTriangulation label, Set label)+reweightRegularSiteLocally revised triangulation = do+  let label = powerSiteLabel revised+  (removed, removalSupport) <- removeAnyRegularSiteLocally label triangulation+  (inserted, insertionSupport) <- insertRegularSiteLocally revised removed+  pure (inserted, Set.union removalSupport insertionSupport)++commonRegularWeightShift+  :: Ord label+  => Map label PowerWeight+  -> Map label (PowerSite label)+  -> Maybe ExactRational+commonRegularWeightShift requested sites+  | Map.size requested /= Map.size sites = Nothing+  | otherwise = do+      (firstLabel, firstWeight) <- Map.lookupMin requested+      firstSite <- Map.lookup firstLabel sites+      let shift =+            powerWeightExact firstWeight+              - powerWeightExact (powerSiteWeight firstSite)+      if Map.isSubmapOfBy (powerSiteHasWeightShift shift) requested sites+        then Just shift+        else Nothing++powerSiteHasWeightShift+  :: ExactRational+  -> PowerWeight+  -> PowerSite label+  -> Bool+powerSiteHasWeightShift shift weight site =+  powerWeightExact weight+    - powerWeightExact (powerSiteWeight site)+    == shift++translateRegularWeights+  :: ExactRational+  -> Map label (PowerSite label)+  -> Set label+  -> RegularTriangulation label+  -> RegularEditResult label+translateRegularWeights shift sites changedLabels triangulation =+  let shiftedSection =+        fmap+          (\section ->+             section+               { sectionGenerators =+                   fmap+                     (translateExactPowerGenerator shift)+                     (sectionGenerators section)+               })+          (storedRegularSection triangulation)+   in RegularEditResult+        { regularEditTriangulation =+            triangulation+              { storedRegularSites = sites+              , storedRegularSection = shiftedSection+              }+        , regularEditChangedSites = changedLabels+        , regularEditTransitions = Vector.empty+        }++translateExactPowerGenerator+  :: ExactRational+  -> ExactPowerGenerator label+  -> ExactPowerGenerator label+translateExactPowerGenerator shift generator =+  generator+    { exactPowerGeneratorConstant =+        exactPowerGeneratorConstant generator + shift+    }++removeAnyRegularSiteLocally+  :: Ord label+  => label+  -> RegularTriangulation label+  -> Either+      (LocalRegularEditObstruction label)+      (RegularTriangulation label, Set label)+removeAnyRegularSiteLocally label triangulation =+  case removeTopologyPreservingSite label triangulation of+    Just (edited, _) -> Right (edited, Set.singleton label)+    Nothing -> removeRegularSiteLocally label triangulation++-- Exact topology-preserving edits descend through the existing regular+-- section. A failed classification is not an error; it selects the canonical+-- global normalization below.++classifyCoincidentSubordinate+  :: Ord label+  => PowerSite label+  -> PowerSite label+  -> Maybe (CoincidentGeneratorDisposition label)+classifyCoincidentSubordinate representative candidate =+  if powerSiteQueryPoint candidate /= powerSiteQueryPoint representative+    then Nothing+    else+      case+        compareCoincidentPriority+          (powerSiteWeight candidate, powerSiteLabel candidate)+          (powerSiteWeight representative, powerSiteLabel representative) of+        LT ->+          Just+            ( classifyCoincidentValue+                (powerSiteLabel representative)+                (powerSiteWeight representative)+                (powerSiteWeight candidate)+            )+        _ -> Nothing++removeTopologyPreservingSite+  :: Ord label+  => label+  -> RegularTriangulation label+  -> Maybe+      ( RegularTriangulation label+      , Vector.Vector (RegularSiteTransition label)+      )+removeTopologyPreservingSite label triangulation = do+  section <- storedRegularSection triangulation+  disposition <- Map.lookup label (sectionRegularDispositions section)+  editedSection <-+    case disposition of+      RegularSiteCoincidentEquivalentTo _ -> removeCoincident section+      RegularSiteCoincidentDominatedBy _ -> removeCoincident section+      RegularSiteHidden+        | not (hasCoincidentAliases label section) -> do+            generator <- Map.lookup label (sectionGenerators section)+            let receipt = sectionRegularReceipt section+            pure+              section+                { sectionGenerators = Map.delete label (sectionGenerators section)+                , sectionSlopeRepresentatives =+                    Map.delete (exactGeneratorSlope generator) (sectionSlopeRepresentatives section)+                , sectionRegularDispositions =+                    Map.delete label (sectionRegularDispositions section)+                , sectionRegularStars = Map.delete label (sectionRegularStars section)+                , sectionRegularReceipt =+                    receipt+                      { generatorRegularInputSites = generatorRegularInputSites receipt - 1+                      , generatorRegularHiddenSites = generatorRegularHiddenSites receipt - 1+                      }+                , sectionRegularLocality = fmap (detachRegularHidden label) (sectionRegularLocality section)+                }+      _ -> Nothing+  let edited =+        replaceRegularSiteSection+          (Map.delete label (storedRegularSites triangulation))+          editedSection+          triangulation+  pure+    ( edited+    , Vector.singleton (RegularSiteDisappeared label disposition)+    )+ where+  removeCoincident section = do+    _ <- Map.lookup label (sectionCoincidentDispositions section)+    pure+      section+        { sectionCoincidentDispositions =+            Map.delete label (sectionCoincidentDispositions section)+        , sectionRegularDispositions =+            Map.delete label (sectionRegularDispositions section)+        , sectionRegularStars = Map.delete label (sectionRegularStars section)+        }++data RegularReweightDescent label = RegularReweightDescent+  { descendedCoincidentDispositions :: !(Map label (CoincidentGeneratorDisposition label))+  , descendedGenerators :: !(Map label (ExactPowerGenerator label))+  , descendedRegularDispositions :: !(Map label (RegularSiteDisposition label))+  , descendedTransitions :: ![RegularSiteTransition label]+  }++reweightTopologyPreservingSites+  :: Ord label+  => Set label+  -> Map label (PowerSite label)+  -> Map label (PowerSite label)+  -> RegularTriangulation label+  -> Maybe+      ( RegularTriangulation label+      , Vector.Vector (RegularSiteTransition label)+      )+reweightTopologyPreservingSites changedLabels changedSites reweightedSites triangulation = do+  section <- storedRegularSection triangulation+  let coincidentRepresentatives =+        Map.foldl'+          (flip (Set.insert . coincidentRepresentativeLabel))+          Set.empty+          (sectionCoincidentDispositions section)+  descent <-+    Foldable.foldlM+      ( descendTopologyPreservingReweight+          changedLabels+          reweightedSites+          coincidentRepresentatives+          triangulation+      )+      RegularReweightDescent+        { descendedCoincidentDispositions = sectionCoincidentDispositions section+        , descendedGenerators = sectionGenerators section+        , descendedRegularDispositions = sectionRegularDispositions section+        , descendedTransitions = []+        }+      changedSites+  let editedSection =+        section+          { sectionCoincidentDispositions = descendedCoincidentDispositions descent+          , sectionGenerators = descendedGenerators descent+          , sectionRegularDispositions = descendedRegularDispositions descent+          }+      edited =+        replaceRegularSiteSection+          reweightedSites+          editedSection+          triangulation+  pure (edited, Vector.fromList (reverse (descendedTransitions descent)))++descendTopologyPreservingReweight+  :: Ord label+  => Set label+  -> Map label (PowerSite label)+  -> Set label+  -> RegularTriangulation label+  -> RegularReweightDescent label+  -> PowerSite label+  -> Maybe (RegularReweightDescent label)+descendTopologyPreservingReweight changedLabels reweightedSites coincidentRepresentatives triangulation descent revised = do+  resident <- Map.lookup label (storedRegularSites triangulation)+  disposition <-+    storedRegularSection triangulation+      >>= Map.lookup label . sectionRegularDispositions+  case regularCoincidentRepresentative disposition of+    Just representative+      | Set.notMember representative changedLabels -> do+          representativeSite <- Map.lookup representative reweightedSites+          coincident <- classifyCoincidentSubordinate representativeSite revised+          let after = coincidentRegularDisposition coincident+          pure+            descent+              { descendedCoincidentDispositions =+                  Map.insert label coincident (descendedCoincidentDispositions descent)+              , descendedRegularDispositions =+                  Map.insert label after (descendedRegularDispositions descent)+              , descendedTransitions =+                  if disposition == after+                    then descendedTransitions descent+                    else RegularSiteTransitioned label disposition after : descendedTransitions descent+              }+    _+      | disposition == RegularSiteHidden+          && powerSiteWeight revised < powerSiteWeight resident+          && Set.notMember label coincidentRepresentatives ->+          let (generator, _) = prepareExactPowerGenerator revised+           in Just+                descent+                  { descendedGenerators =+                      Map.insert label generator (descendedGenerators descent)+                  }+      | otherwise -> Nothing+ where+  label = powerSiteLabel revised++hasCoincidentAliases+  :: Eq label+  => label+  -> RegularSection label+  -> Bool+hasCoincidentAliases label =+  any ((== label) . coincidentRepresentativeLabel)+    . Map.elems+    . sectionCoincidentDispositions++regularCoincidentRepresentative :: RegularSiteDisposition label -> Maybe label+regularCoincidentRepresentative disposition =+  case disposition of+    RegularSiteCoincidentEquivalentTo label -> Just label+    RegularSiteCoincidentDominatedBy label -> Just label+    _ -> Nothing++coincidentRepresentativeLabel :: CoincidentGeneratorDisposition label -> label+coincidentRepresentativeLabel disposition =+  case disposition of+    CoincidentGeneratorEquivalentTo label -> label+    CoincidentGeneratorDominatedBy label -> label++normalizeRegularEdit+  :: Ord label+  => RegularTriangulation label+  -> Map label (PowerSite label)+  -> Set label+  -> Either+      (RegularEditError label)+      (RegularEditResult label)+normalizeRegularEdit previous sites changedSites = do+  triangulation <-+    first RegularEditTopologyFailed+      (constructRegularTriangulation sites)+  pure+    ( RegularEditResult+        triangulation+        changedSites+        ( dispositionTransitions+            (regularDispositionSection previous)+            (regularDispositionSection triangulation)+        )+    )++unchangedRegularEdit+  :: RegularTriangulation label+  -> RegularEditResult label+unchangedRegularEdit triangulation =+  RegularEditResult triangulation Set.empty Vector.empty+++dispositionTransitions+  :: Ord label+  => Map label (RegularSiteDisposition label)+  -> Map label (RegularSiteDisposition label)+  -> Vector.Vector (RegularSiteTransition label)+dispositionTransitions previous current =+  Vector.fromList+    ( Map.elems+        ( Map.mergeWithKey+            changedDispositionTransition+            (Map.mapWithKey RegularSiteDisappeared)+            (Map.mapWithKey RegularSiteAppeared)+            previous+            current+        )+    )++dispositionTransitionsOn+  :: Ord label+  => Set label+  -> Map label (RegularSiteDisposition label)+  -> Map label (RegularSiteDisposition label)+  -> Vector.Vector (RegularSiteTransition label)+dispositionTransitionsOn support previous current =+  dispositionTransitions+    (Map.restrictKeys previous support)+    (Map.restrictKeys current support)++changedDispositionTransition+  :: Eq label+  => label+  -> RegularSiteDisposition label+  -> RegularSiteDisposition label+  -> Maybe (RegularSiteTransition label)+changedDispositionTransition label before after+  | before == after = Nothing+  | otherwise = Just (RegularSiteTransitioned label before after)
+ src-public/Moonlight/Triangulation/Internal/PowerDiagram/Generator.hs view
@@ -0,0 +1,264 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}++-- | Exact affine generators and their dual geometry. This module owns the+-- algebra shared by batch hull construction, local edits, and bounded+-- projection; it does not choose a construction schedule.+module Moonlight.Triangulation.Internal.PowerDiagram.Generator+  ( ExactPowerGenerator (..)+  , DistinctSlopeGenerators (..)+  , RegularGeneratorDisposition (..)+  , GeneratorRegularFace (..)+  , GeneratorDualGeometry (..)+  , GeneratorRegularEdge (..)+  , GeneratorRegularTopology (..)+  , GeneratorRegularReceipt (..)+  , RegularTopologyError (..)+  , exactGeneratorAxis+  , exactGeneratorLiftedVolume+  , exactGeneratorFaceDual+  , exactGeneratorBoundaryDualRay+  , exactGeneratorDualBetween+  ) where++import Control.DeepSeq (NFData)+import Data.Bifunctor (first)+import Data.List.NonEmpty (NonEmpty)+import GHC.Generics (Generic)+import Moonlight.Triangulation.Exact+  ( ExactAffineLine+  , ExactGeometryError+  , ExactHalfPlaneError+  , ExactIntersectionError+  , ExactPoint+  , ExactRay+  , ExactSegment+  , ExactVector (..)+  , exactAffineLine+  , exactAffineLineCoefficients+  , exactAffineLineIntersection+  , exactRay+  , exactSegment+  )+import Moonlight.Triangulation.Internal.ExactRational (ExactRational)+import Moonlight.Triangulation.Minkowski (MinkowskiError)+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+  }+  deriving stock (Eq, Ord, Show, Generic)+  deriving anyclass (NFData)++-- | Exact slope-hull, incidence, and dual-construction obstructions.+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)++exactGeneratorAxis+  :: ExactPowerGenerator label+  -> ExactPowerGenerator label+  -> Either ExactHalfPlaneError ExactAffineLine+exactGeneratorAxis firstGenerator secondGenerator =+  exactAffineLine+    ( exactPowerGeneratorXCoefficient firstGenerator+        - exactPowerGeneratorXCoefficient secondGenerator+    )+    ( exactPowerGeneratorYCoefficient firstGenerator+        - exactPowerGeneratorYCoefficient secondGenerator+    )+    ( exactPowerGeneratorConstant firstGenerator+        - exactPowerGeneratorConstant secondGenerator+    )++-- | Signed exact lifted volume of four affine generators. For a+-- counter-clockwise slope face, a positive result lies above its supporting+-- plane and therefore belongs to the insertion conflict cavity.+exactGeneratorLiftedVolume+  :: ExactPowerGenerator label+  -> ExactPowerGenerator label+  -> ExactPowerGenerator label+  -> ExactPowerGenerator label+  -> ExactRational+exactGeneratorLiftedVolume firstGenerator secondGenerator thirdGenerator fourthGenerator =+  let (firstX, firstY, firstZ) = generatorCoordinates firstGenerator+      (secondX, secondY, secondZ) = generatorCoordinates secondGenerator+      (thirdX, thirdY, thirdZ) = generatorCoordinates thirdGenerator+      (fourthX, fourthY, fourthZ) = generatorCoordinates fourthGenerator+      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')++-- | Exact dual vertex shared by three affine generators.+exactGeneratorFaceDual+  :: ExactPowerGenerator label+  -> ExactPowerGenerator label+  -> ExactPowerGenerator label+  -> Either (RegularTopologyError label) ExactPoint+exactGeneratorFaceDual firstGenerator secondGenerator thirdGenerator = do+  firstAxis <-+    first+      (RegularTopologyAxisInvalid (exactPowerGeneratorLabel firstGenerator) (exactPowerGeneratorLabel secondGenerator))+      (exactGeneratorAxis firstGenerator secondGenerator)+  secondAxis <-+    first+      (RegularTopologyAxisInvalid (exactPowerGeneratorLabel firstGenerator) (exactPowerGeneratorLabel thirdGenerator))+      (exactGeneratorAxis firstGenerator thirdGenerator)+  first+    ( RegularTopologyDualVertexInvalid+        (exactPowerGeneratorLabel firstGenerator)+        (exactPowerGeneratorLabel secondGenerator)+        (exactPowerGeneratorLabel thirdGenerator)+    )+    (exactAffineLineIntersection firstAxis secondAxis)++-- | Exact outward dual ray of an oriented boundary edge and its incident+-- counter-clockwise face.+exactGeneratorBoundaryDualRay+  :: ExactPoint+  -> ExactPowerGenerator label+  -> ExactPowerGenerator label+  -> ExactPowerGenerator label+  -> Either (RegularTopologyError label) ExactRay+exactGeneratorBoundaryDualRay originPoint firstGenerator secondGenerator thirdGenerator = do+  axis <-+    first+      ( RegularTopologyAxisInvalid+          (exactPowerGeneratorLabel firstGenerator)+          (exactPowerGeneratorLabel secondGenerator)+      )+      (exactGeneratorAxis firstGenerator secondGenerator)+  let (axisX, axisY, _) = exactAffineLineCoefficients axis+      candidate = ExactVector axisY (negate axisX)+      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+            (exactPowerGeneratorLabel firstGenerator)+            (exactPowerGeneratorLabel secondGenerator)+            (exactPowerGeneratorLabel thirdGenerator)+        )+    else+      first+        ( RegularTopologyDualRayInvalid+            (exactPowerGeneratorLabel firstGenerator)+            (exactPowerGeneratorLabel secondGenerator)+        )+        (exactRay originPoint direction)++-- | Canonical undirected dual geometry between two incident face vertices.+exactGeneratorDualBetween+  :: label+  -> label+  -> ExactPoint+  -> ExactPoint+  -> Either (RegularTopologyError label) GeneratorDualGeometry+exactGeneratorDualBetween firstLabel secondLabel firstPoint secondPoint+  | firstPoint == secondPoint = Right (GeneratorDualCollapsed firstPoint)+  | otherwise =+      fmap GeneratorDualSegment+        ( first+            (RegularTopologyDualSegmentInvalid firstLabel secondLabel)+            ( uncurry exactSegment+                ( if firstPoint <= secondPoint+                    then (firstPoint, secondPoint)+                    else (secondPoint, firstPoint)+                )+            )+        )++generatorCoordinates+  :: ExactPowerGenerator label+  -> (ExactRational, ExactRational, ExactRational)+generatorCoordinates generator =+  ( exactPowerGeneratorXCoefficient generator+  , exactPowerGeneratorYCoefficient generator+  , exactPowerGeneratorConstant generator+  )
+ src-public/Moonlight/Triangulation/Internal/PowerDiagram/Hull.hs view
@@ -0,0 +1,925 @@+-- | 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.PowerDiagram.Hull+  ( regularGeneratorTopology+  ) where++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 Moonlight.Triangulation.Exact+  ( ExactAffineLine+  , ExactPoint+  , ExactRay+  , exactPoint+  , exactPointCoordinates+  )+import Moonlight.Triangulation.Internal.BoundaryCycle (consecutivePairs)+import Moonlight.Triangulation.Internal.ExactRational (ExactRational)+import Moonlight.Triangulation.Internal.Minkowski.Convex (convexHullPolygon)+import Moonlight.Triangulation.Internal.PowerDiagram.Generator+import Moonlight.Triangulation.Minkowski (convexPolygonPoints)++-- | 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 [] [])+        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++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 =+  exactGeneratorLiftedVolume+    (liftedPointGenerator firstPoint)+    (liftedPointGenerator secondPoint)+    (liftedPointGenerator thirdPoint)+    (liftedPointGenerator fourthPoint)++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+  }++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)+          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)++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)+    }++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)++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)++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+  dual <-+    exactGeneratorFaceDual+      (liftedPointGenerator firstPoint)+      (liftedPointGenerator secondPoint)+      (liftedPointGenerator thirdPoint)+  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+        dual <-+          exactGeneratorDualBetween+            (pointLabel firstPoint)+            (pointLabel secondPoint)+            firstDual+            secondDual+        pure (regularEdge firstPoint secondPoint dual)+    (_, 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+  exactGeneratorBoundaryDualRay+    originPoint+    (liftedPointGenerator firstPoint)+    (liftedPointGenerator secondPoint)+    (liftedPointGenerator thirdPoint)++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]+  -> GeneratorRegularTopology label+topologyFromParts points visible lowerDimensional faces edges =+  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+              }+        }
+ src-public/Moonlight/Triangulation/Internal/PowerDiagram/Locality.hs view
@@ -0,0 +1,235 @@+{-# LANGUAGE BangPatterns #-}++-- | The discardable hidden-support index and exact face walk for one regular section.+module Moonlight.Triangulation.Internal.PowerDiagram.Locality where++import qualified Data.Foldable as Foldable+import qualified Data.List as List+import qualified Data.Map.Strict as Map+import Data.Map.Strict (Map)+import qualified Data.Set as Set+import Data.Set (Set)+import Moonlight.Triangulation.Exact+  ( ExactPoint+  , exactOrient2d+  , exactPoint+  )+import Moonlight.Triangulation.Internal.BoundaryCycle (orderedPair)+import Moonlight.Triangulation.Internal.ExactRational (ExactRational)+import Moonlight.Triangulation.Internal.PowerDiagram.Generator+  ( ExactPowerGenerator+  )+import Moonlight.Triangulation.Internal.PowerDiagram.Model++patchRegularLocality+  :: Ord label+  => RegularLocality label+  -> Set (RegularFaceKey label)+  -> Set label+  -> RegularSection label+  -> Either (LocalRegularEditObstruction label) (RegularLocality label)+patchRegularLocality locality removedFaces hiddenToAttach section = do+  seed <-+    case Map.lookupMin (sectionRegularFaces section) of+      Nothing -> Left LocalRegularLocalityUnavailable+      Just (minimumFace, _) ->+        Right+          ( if Map.member (localityFaceSeed locality) (sectionRegularFaces section)+              then localityFaceSeed locality+              else minimumFace+          )+  let detachedForFaces =+        Set.unions+          [ Map.findWithDefault Set.empty faceKey (localitySupportHidden locality)+          | faceKey <- Set.toAscList removedFaces+          ]+      detachedLabels = Set.union detachedForFaces hiddenToAttach+      detached = Set.foldl' (flip detachRegularHidden) locality detachedLabels+  attachments <-+    traverse+      (locateHiddenSupport seed section)+      (Set.toAscList hiddenToAttach)+  pure+    ( Foldable.foldl'+        (\current (label, faceKey) -> attachRegularHidden label faceKey current)+        detached{localityFaceSeed = seed}+        attachments+    )++locateHiddenSupport+  :: Ord label+  => RegularFaceKey label+  -> RegularSection label+  -> label+  -> Either (LocalRegularEditObstruction label) (label, RegularFaceKey label)+locateHiddenSupport seed section label = do+  generator <- requireGeneratorFromMap (sectionGenerators section) label+  location <-+    maybe (Left LocalRegularSlopeLocationFailed) Right+      (locateRegularSlope seed section generator)+  case location of+    RegularSlopeInside faceKey -> Right (label, faceKey)+    RegularSlopeOutside edgeKey -> Left (LocalRegularSlopeOutside edgeKey)++attachRegularHidden+  :: Ord label+  => label+  -> RegularFaceKey label+  -> RegularLocality label+  -> RegularLocality label+attachRegularHidden label faceKey locality =+  locality+    { localityHiddenSupport = Map.insert label faceKey (localityHiddenSupport locality)+    , localitySupportHidden =+        Map.insertWith Set.union faceKey (Set.singleton label) (localitySupportHidden locality)+    }++detachRegularHidden+  :: Ord label+  => label+  -> RegularLocality label+  -> RegularLocality label+detachRegularHidden label locality =+  case Map.lookup label (localityHiddenSupport locality) of+    Nothing -> locality+    Just faceKey ->+      locality+        { localityHiddenSupport = Map.delete label (localityHiddenSupport locality)+        , localitySupportHidden =+            Map.update+              (\labels ->+                 let retained = Set.delete label labels+                  in if Set.null retained then Nothing else Just retained)+              faceKey+              (localitySupportHidden locality)+        }++buildRegularLocality+  :: Ord label+  => RegularSection label+  -> Maybe (RegularLocality label)+buildRegularLocality section = do+  (seed, _) <- Map.lookupMin (sectionRegularFaces section)+  Foldable.foldlM+    (attachHiddenGenerator section)+    RegularLocality+      { localityFaceSeed = seed+      , localityHiddenSupport = Map.empty+      , localitySupportHidden = Map.empty+      }+    [ label+    | (_, label) <- Map.toAscList (sectionSlopeRepresentatives section)+    , Map.lookup label (sectionRegularDispositions section)+        == Just RegularSiteHidden+    ]++attachHiddenGenerator+  :: Ord label+  => RegularSection label+  -> RegularLocality label+  -> label+  -> Maybe (RegularLocality label)+attachHiddenGenerator section locality label = do+  generator <- Map.lookup label (sectionGenerators section)+  location <- locateRegularSlope (localityFaceSeed locality) section generator+  case location of+    RegularSlopeInside faceKey ->+      Just+        (attachRegularHidden label faceKey locality)+          { localityFaceSeed = faceKey+          }+    RegularSlopeOutside _ -> Nothing++locateRegularSlope+  :: Ord label+  => RegularFaceKey label+  -> RegularSection label+  -> ExactPowerGenerator label+  -> Maybe (RegularSlopeLocation label)+locateRegularSlope seed section generator =+  descendRegularSlope+    (exactPoint queryX queryY)+    section+    (Map.size (sectionRegularFaces section) + 1)+    seed+ where+  (queryX, queryY) = exactGeneratorSlope generator++descendRegularSlope+  :: Ord label+  => ExactPoint+  -> RegularSection label+  -> Int+  -> RegularFaceKey label+  -> Maybe (RegularSlopeLocation label)+descendRegularSlope query section !remaining faceKey+  | remaining <= 0 = Nothing+  | otherwise = do+      face <- Map.lookup faceKey (sectionRegularFaces section)+      case firstExteriorFaceEdge query section face of+        Nothing -> Just (RegularSlopeInside faceKey)+        Just edgeKey -> do+          edgeSection <- Map.lookup edgeKey (sectionRegularEdges section)+          case Set.lookupMin (Set.delete faceKey (sectionRegularEdgeFaces edgeSection)) of+            Just adjacent ->+              descendRegularSlope query section (remaining - 1) adjacent+            Nothing -> Just (RegularSlopeOutside edgeKey)++firstExteriorFaceEdge+  :: Ord label+  => ExactPoint+  -> RegularSection label+  -> RegularFace label+  -> Maybe (RegularEdgeKey label)+firstExteriorFaceEdge query section face =+  fmap+    (uncurry orderedPair)+    ( List.find+        (faceEdgeExcludes query section)+        (regularFaceDirectedEdgeKeys face)+    )++faceEdgeExcludes+  :: Ord label+  => ExactPoint+  -> RegularSection label+  -> RegularEdgeKey label+  -> Bool+faceEdgeExcludes query section (fromLabel, toLabel) =+  generatorEdgeExcludes+    query+    (sectionGenerators section)+    (fromLabel, toLabel)++generatorEdgeExcludes+  :: Ord label+  => ExactPoint+  -> Map label (ExactPowerGenerator label)+  -> RegularEdgeKey label+  -> Bool+generatorEdgeExcludes query generators (fromLabel, toLabel) =+  case (Map.lookup fromLabel generators, Map.lookup toLabel generators) of+    (Just fromGenerator, Just toGenerator) ->+      exactOrient2d+        (uncurry exactPoint (exactGeneratorSlope fromGenerator))+        (uncurry exactPoint (exactGeneratorSlope toGenerator))+        query+        == LT+    _ -> False++generatorEdgeExcludesScaled+  :: Ord label+  => ExactRational+  -> (ExactRational, ExactRational)+  -> Map label (ExactPowerGenerator label)+  -> RegularEdgeKey label+  -> Bool+generatorEdgeExcludesScaled scale (queryX, queryY) generators (fromLabel, toLabel) =+  case (Map.lookup fromLabel generators, Map.lookup toLabel generators) of+    (Just fromGenerator, Just toGenerator) ->+      let (fromX, fromY) = exactGeneratorSlope fromGenerator+          (toX, toY) = exactGeneratorSlope toGenerator+       in (toX - fromX) * (queryY - scale * fromY)+            - (toY - fromY) * (queryX - scale * fromX)+            < 0+    _ -> False
+ src-public/Moonlight/Triangulation/Internal/PowerDiagram/Model.hs view
@@ -0,0 +1,558 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}++-- | Shared opaque carriers for exact power topology. This module owns+-- representation only; construction and interpretation live in sibling modules.+module Moonlight.Triangulation.Internal.PowerDiagram.Model where++import Control.DeepSeq (NFData)+import Data.Bifunctor (first)+import Data.List.NonEmpty (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 Vector+import GHC.Generics (Generic)+import Moonlight.Triangulation.Exact+  ( ExactAffineLine+  , ExactClipError+  , ExactHalfPlaneError+  , ExactPoint+  , ExactRay+  , ExactSegment+  , exactPointFromQueryPoint+  )+import Moonlight.Triangulation.Internal.ExactRational+  ( ExactRational+  , exactRationalFromFiniteDouble+  )+import Moonlight.Triangulation.Internal.BoundaryCycle (orderedPair)+import Moonlight.Triangulation.Internal.PowerDiagram.Generator+  ( ExactPowerGenerator (..)+  , GeneratorRegularReceipt+  , GeneratorRegularTopology+  , RegularTopologyError+  )+import Moonlight.Triangulation.Math (mkQueryPoint)+import Moonlight.Triangulation.Minkowski (ConvexPolygon, MinkowskiError)+import Moonlight.Triangulation.Internal.Overlay.Types (OverlayError)+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))++-- | Admit an already-exact signed power offset without a binary64 round trip.+powerWeightFromExact :: ExactRational -> PowerWeight+powerWeightFromExact = PowerWeight++-- | 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+    -- ^ Stable identity used by edits and canonical topology.+  , powerSiteQueryPoint :: !QueryPoint+  , powerSiteWeight :: !PowerWeight+    -- ^ Signed additive power offset.+  }+  deriving stock (Eq, Ord, Show, Generic)+  deriving anyclass (NFData)++-- | Admit one stable-labelled site at a finite position.+powerSite+  :: label+  -> Point+  -> PowerWeight+  -> Either (PowerDiagramError label) (PowerSite label)+powerSite label point weight =+  PowerSite label <$> first (PowerSitePositionInvalid label) (mkQueryPoint point) <*> pure weight++-- | Binary64 position originally admitted for the site.+powerSitePosition :: PowerSite label -> Point+powerSitePosition = queryPointValue . powerSiteQueryPoint++-- | Exact coordinates retained by an admitted site.+powerSiteExactPosition :: PowerSite label -> ExactPoint+powerSiteExactPosition = exactPointFromQueryPoint . 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)++-- | Canonically oriented labels of the face.+regularFaceLabels :: RegularFace label -> (label, label, label)+regularFaceLabels (RegularFace firstLabel secondLabel thirdLabel _) =+  (firstLabel, secondLabel, thirdLabel)++-- | Exact power vertex dual to the face.+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)++-- | Ascending endpoint labels of the unordered edge.+regularEdgeLabels :: RegularEdge label -> (label, label)+regularEdgeLabels (RegularEdge firstLabel secondLabel _) =+  (firstLabel, secondLabel)++-- | Exact power-dual carrier of the edge.+regularEdgeDual :: RegularEdge label -> PowerDualEdge+regularEdgeDual (RegularEdge _ _ dual) = dual++type RegularFaceKey label = (label, label, label)+type RegularEdgeKey label = (label, label)++data RegularEdgeSection label = RegularEdgeSection+  { sectionRegularEdge :: !(RegularEdge label)+  , sectionRegularEdgeFaces :: !(Set (RegularFaceKey label))+  }+  deriving stock (Eq, Ord, Show, Generic)+  deriving anyclass (NFData)++data RegularSiteStar label = RegularSiteStar+  { sectionIncidentFaces :: !(Set (RegularFaceKey label))+  , sectionSiteNeighbours :: !(Set label)+  }+  deriving stock (Eq, Ord, Show, Generic)+  deriving anyclass (NFData)++data RegularLocality label = RegularLocality+  { localityFaceSeed :: !(RegularFaceKey label)+  , localityHiddenSupport :: !(Map label (RegularFaceKey label))+  , localitySupportHidden :: !(Map (RegularFaceKey label) (Set label))+  }+  deriving stock (Eq, Show, Generic)+  deriving anyclass (NFData)++data RegularSlopeLocation label+  = RegularSlopeInside !(RegularFaceKey label)+  | RegularSlopeOutside !(RegularEdgeKey label)++data LocalRegularEditObstruction label+  = LocalRegularSectionUnavailable+  | LocalRegularLocalityUnavailable+  | LocalRegularSlopeCoincidence !label+  | LocalRegularSlopeOutside !(RegularEdgeKey label)+  | LocalRegularSlopeLocationFailed+  | LocalRegularDegenerateFace !(RegularFaceKey label)+  | LocalRegularFaceMissing !(RegularFaceKey label)+  | LocalRegularGeneratorMissing !label+  | LocalRegularNonManifoldEdge !(RegularEdgeKey label) !Int+  | LocalRegularNonConvexEdge !(RegularEdgeKey label)+  | LocalRegularEmptyHorizon+  | LocalRegularBoundaryRemoval !label+  | LocalRegularCandidateSectionEmpty+  | LocalRegularCavityBoundaryMismatch+  | LocalRegularTopologyObstruction !(RegularTopologyError label)++data RegularConflictDescent label = RegularConflictDescent+  { conflictPendingFaces :: !(Set (RegularFaceKey label))+  , conflictVisitedFaces :: !(Set (RegularFaceKey label))+  , conflictVisibleFaces :: !(Set (RegularFaceKey label))+  , conflictObstruction :: !(Maybe (LocalRegularEditObstruction label))+  }++data RegularFacePatch label = RegularFacePatch+  { patchedRegularFaces :: !(Map (RegularFaceKey label) (RegularFace label))+  , patchedRegularEdges :: !(Map (RegularEdgeKey label) (RegularEdgeSection label))+  , patchedRegularStars :: !(Map label (RegularSiteStar label))+  , patchedRegularLabels :: !(Set label)+  , patchedRegularTouchedEdges :: !(Set (RegularEdgeKey label))+  }++data RegularSection label = RegularSection+  { sectionGenerators :: !(Map label (ExactPowerGenerator label))+  , sectionSlopeRepresentatives :: !(Map (ExactRational, ExactRational) label)+  , sectionCoincidentDispositions :: !(Map label (CoincidentGeneratorDisposition label))+  , sectionRegularDispositions :: !(Map label (RegularSiteDisposition label))+  , sectionRegularFaces :: !(Map (RegularFaceKey label) (RegularFace label))+  , sectionRegularEdges :: !(Map (RegularEdgeKey label) (RegularEdgeSection label))+  , sectionRegularStars :: !(Map label (RegularSiteStar label))+  , sectionRegularReceipt :: !GeneratorRegularReceipt+  , sectionRegularLocality :: !(Maybe (RegularLocality label))+  }+  deriving stock (Eq, Show, Generic)+  deriving anyclass (NFData)++-- | Exact regular subdivision. Every weighted site remains resident in this+-- opaque value; one normalized section owns all derived topology and incidence.+data RegularTriangulation label = RegularTriangulation+  { storedRegularSites :: !(Map label (PowerSite label))+  , storedRegularSection :: !(Maybe (RegularSection label))+  }+  deriving stock (Generic)+  deriving anyclass (NFData)++-- | Semantic equality is equality of the complete weighted-site section.+-- Topology and visibility are sealed derived caches of that section.+instance Eq label => Eq (RegularTriangulation label) where+  left == right = storedRegularSites left == storedRegularSites right++instance Show label => Show (RegularTriangulation label) where+  showsPrec precedence triangulation =+    showParen (precedence > 10)+      ( showString "RegularTriangulation "+          . shows (storedRegularSites triangulation)+      )++-- | Strict cardinality receipt for one normalized regular section.+data RegularTriangulationReceipt = RegularTriangulationReceipt+  { regularTriangulationInputSites :: !Int+  , regularTriangulationRepresentativeSites :: !Int+  , regularTriangulationVisibleSites :: !Int+  , regularTriangulationLowerDimensionalSites :: !Int+  , regularTriangulationHiddenSites :: !Int+  , regularTriangulationCoincidentSites :: !Int+  , regularTriangulationFaces :: !Int+  , regularTriangulationEdges :: !Int+  }+  deriving stock (Eq, Ord, Show, Generic)+  deriving anyclass (NFData)++-- | One exact change in the exhaustive visibility section. Appearance and+-- disappearance are explicit rather than encoded with sentinel dispositions.+data RegularSiteTransition label+  = RegularSiteAppeared !label !(RegularSiteDisposition label)+  | RegularSiteDisappeared !label !(RegularSiteDisposition label)+  | RegularSiteTransitioned+      !label+      !(RegularSiteDisposition label)+      !(RegularSiteDisposition label)+  deriving stock (Eq, Ord, Show, Generic)+  deriving anyclass (NFData)++-- | A regular edit either conflicts with an existing stable identity, names+-- absent patch identities, or carries the exact normalization obstruction.+data RegularEditError label+  = RegularEditSiteConflict+      !label+      !(PowerSite label)+      !(PowerSite label)+  | RegularEditUnknownSites !(NonEmpty label)+  | RegularEditTopologyFailed !(RegularTopologyError label)+  deriving stock (Eq, Show, Generic)+  deriving anyclass (NFData)++-- | One common publication for every pure regular-site edit. The changed-site+-- support is empty precisely for an idempotent edit.+data RegularEditResult label = RegularEditResult+  { regularEditTriangulation :: !(RegularTriangulation label)+  , regularEditChangedSites :: !(Set label)+  , regularEditTransitions :: !(Vector.Vector (RegularSiteTransition label))+  }+  deriving stock (Eq, 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)++-- | Typed admission, topology, and clipping obstructions.+data PowerDiagramError label+  = PowerSitePositionInvalid !label !PointValidationError+  | DuplicatePowerSiteLabel !label+  | PowerDomainInvalid !ExactHalfPlaneError+  | PowerBisectorInvalid !label !label !ExactHalfPlaneError+  | PowerRegularTopologyFailed !(RegularTopologyError label)+  | PowerRegularDispositionMissing !label+  | PowerRegularGeneratorMissing !label+  | PowerCellClipFailed !label !ExactClipError+  deriving stock (Eq, Show, Generic)+  deriving anyclass (NFData)++-- | Exact construction counts and rational-width evidence for a bounded diagram.+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)+++data CoincidentGeneratorDisposition label+  = CoincidentGeneratorEquivalentTo !label+  | CoincidentGeneratorDominatedBy !label+  deriving stock (Eq, Ord, Show, Generic)+  deriving anyclass (NFData)++data ResolvedGeneratorSection label = ResolvedGeneratorSection+  { resolvedCoincidentDispositions :: !(Map label (CoincidentGeneratorDisposition label))+  , resolvedRegularTopology :: !(GeneratorRegularTopology label)+  }+  deriving stock (Eq, Show, Generic)+  deriving anyclass (NFData)++foldRegularFaceBoundary+  :: (label -> label -> label -> result -> result)+  -> result+  -> RegularFace label+  -> result+foldRegularFaceBoundary step initial face =+  let (firstLabel, secondLabel, thirdLabel) = regularFaceLabels face+   in step firstLabel secondLabel thirdLabel+        ( step secondLabel thirdLabel firstLabel+            (step thirdLabel firstLabel secondLabel initial)+        )+{-# INLINE foldRegularFaceBoundary #-}++regularFaceDirectedEdgeKeys :: RegularFace label -> [RegularEdgeKey label]+regularFaceDirectedEdgeKeys =+  foldRegularFaceBoundary+    (\fromLabel toLabel _ -> ((fromLabel, toLabel) :))+    []++regularFaceCanonicalEdgeKeys+  :: Ord label+  => RegularFace label+  -> [RegularEdgeKey label]+regularFaceCanonicalEdgeKeys =+  foldRegularFaceBoundary+    (\fromLabel toLabel _ -> (orderedPair fromLabel toLabel :))+    []++regularFaceLabelSet :: Ord label => RegularFace label -> Set label+regularFaceLabelSet =+  foldRegularFaceBoundary (\label _ _ -> Set.insert label) Set.empty++requireFaceEdgeWitness+  :: Ord label+  => RegularEdgeKey label+  -> RegularFace label+  -> Either (LocalRegularEditObstruction label) (label, label, label)+requireFaceEdgeWitness edgeKey face =+  maybe+    (Left (LocalRegularFaceMissing (regularFaceKey face)))+    Right+    ( foldRegularFaceBoundary+        (\fromLabel toLabel oppositeLabel later ->+            if orderedPair fromLabel toLabel == edgeKey+              then Just (fromLabel, toLabel, oppositeLabel)+              else later)+        Nothing+        face+    )++regularFaceKey :: Ord label => RegularFace label -> RegularFaceKey label+regularFaceKey face =+  let (firstLabel, secondLabel, thirdLabel) = regularFaceLabels face+   in sortedRegularFaceLabels firstLabel secondLabel thirdLabel++sortedRegularFaceLabels+  :: Ord label+  => label+  -> label+  -> label+  -> RegularFaceKey label+sortedRegularFaceLabels firstLabel secondLabel thirdLabel =+  let (firstLow, firstHigh) = orderedPair firstLabel secondLabel+      (secondLow, finalHigh) = orderedPair firstHigh thirdLabel+      (finalLow, finalMiddle) = orderedPair firstLow secondLow+   in (finalLow, finalMiddle, finalHigh)++regularEdgeKey :: Ord label => RegularEdge label -> RegularEdgeKey label+regularEdgeKey = uncurry orderedPair . regularEdgeLabels++regularFaceEdgeIncidence+  :: Ord label+  => RegularFace label+  -> Map (RegularEdgeKey label) (Set (RegularFaceKey label))+regularFaceEdgeIncidence face =+  let incidentFace = Set.singleton (regularFaceKey face)+   in foldRegularFaceBoundary+        (\fromLabel toLabel _ ->+            Map.insert (orderedPair fromLabel toLabel) incidentFace)+        Map.empty+        face++emptyRegularSiteStar :: RegularSiteStar label+emptyRegularSiteStar = RegularSiteStar Set.empty Set.empty++requireFaceFromMap+  :: Ord label+  => Map (RegularFaceKey label) (RegularFace label)+  -> RegularFaceKey label+  -> Either (LocalRegularEditObstruction label) (RegularFace label)+requireFaceFromMap faces faceKey =+  maybe (Left (LocalRegularFaceMissing faceKey)) Right (Map.lookup faceKey faces)++requireGeneratorFromMap+  :: Ord label+  => Map label (ExactPowerGenerator label)+  -> label+  -> Either (LocalRegularEditObstruction label) (ExactPowerGenerator label)+requireGeneratorFromMap generators label =+  maybe (Left (LocalRegularGeneratorMissing label)) Right (Map.lookup label generators)++regularFaceNeighbours+  :: Ord label+  => RegularSection label+  -> RegularFaceKey label+  -> Either+      (LocalRegularEditObstruction label)+      (Set (RegularFaceKey label))+regularFaceNeighbours section faceKey = do+  face <- requireFaceFromMap (sectionRegularFaces section) faceKey+  incident <- traverse requireIncidence (regularFaceCanonicalEdgeKeys face)+  pure (Set.delete faceKey (Set.unions incident))+ where+  requireIncidence edgeKey =+    maybe+      (Left (LocalRegularNonManifoldEdge edgeKey 0))+      (Right . sectionRegularEdgeFaces)+      (Map.lookup edgeKey (sectionRegularEdges section))++replaceRegularSiteSection+  :: Map label (PowerSite label)+  -> RegularSection label+  -> RegularTriangulation label+  -> RegularTriangulation label+replaceRegularSiteSection sites section triangulation =+  triangulation+    { storedRegularSites = sites+    , storedRegularSection = Just section+    }++regularDispositionSection+  :: RegularTriangulation label+  -> Map label (RegularSiteDisposition label)+regularDispositionSection =+  maybe Map.empty sectionRegularDispositions . storedRegularSection++exactGeneratorSlope+  :: ExactPowerGenerator label+  -> (ExactRational, ExactRational)+exactGeneratorSlope generator =+  ( exactPowerGeneratorXCoefficient generator+  , exactPowerGeneratorYCoefficient generator+  )
+ src-public/Moonlight/Triangulation/Internal/PowerDiagram/Projection.hs view
@@ -0,0 +1,601 @@+{-# LANGUAGE BangPatterns #-}+-- | Bounded clipping and planar publication of exact regular sections.+module Moonlight.Triangulation.Internal.PowerDiagram.Projection+  ( boundedPowerDiagram+  , boundedPowerDiagramFromRegular+  , powerCellDisposition+  , powerCellDispositions+  , powerDiagramPlanarLayer+  , upperEnvelope+  ) where++import Data.Bifunctor (first)+import qualified Data.Foldable as Foldable+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.Vector as Vector+import Moonlight.Triangulation.Exact+  ( ExactClipDisposition (..)+  , ExactClipReceipt (..)+  , ExactAffineLine+  , ExactClosedHalfPlane+  , ExactRetainedPolygon+  , exactClipRetainedPolygon+  , exactClosedHalfPlane+  , exactRetainedPolygon+  , oppositeExactAffineLine+  )+import Moonlight.Triangulation.Internal.PowerDiagram.Generator+  ( ExactPowerGenerator (..)+  , GeneratorRegularEdge (..)+  , GeneratorRegularReceipt (..)+  , GeneratorRegularTopology (..)+  , RegularGeneratorDisposition (..)+  , exactGeneratorAxis+  )+import Moonlight.Triangulation.Internal.PowerDiagram.Model+import Moonlight.Triangulation.Internal.PowerDiagram.Section+import Moonlight.Triangulation.Internal.Minkowski.Convex+  ( convexHullPolygon+  , convexPolygonComponent+  , convexPolygonFromRetained+  )+import Moonlight.Triangulation.Internal.Overlay.Types+  ( OverlayCell (..)+  , OverlayCellGeometry (..)+  , OverlayResult (..)+  )+import Moonlight.Triangulation.Internal.Region.Publication+  ( planarLayerFromAdmittedComponents+  )+import Moonlight.Triangulation.Minkowski+  ( ConvexPolygon+  , convexPolygonPoints+  )+import Moonlight.Triangulation.Overlay+  ( overlayLayers )+import Moonlight.Triangulation.Region+  ( PlanarLayer+  , PolygonComponent+  , exactLoopPoints+  , polygonOuterLoop+  )++-- | Construct and clip a normalized regular subdivision to one convex domain.+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)++-- | Clip an already normalized regular value without rebuilding its lifted+-- hull. The empty regular value yields an empty labelled diagram.+boundedPowerDiagramFromRegular+  :: Ord label+  => ConvexPolygon+  -> RegularTriangulation label+  -> Either (PowerDiagramError label) (BoundedPowerDiagram label, PowerDiagramReceipt)+boundedPowerDiagramFromRegular domain triangulation =+  case storedRegularSection triangulation of+    Nothing ->+      Right+        ( BoundedPowerDiagram Map.empty+        , aggregateReceipt+            domain+            Map.empty+            0+            mempty+            emptyGeneratorRegularReceipt+            0+        )+    Just section -> do+      (dispositions, clipReceipt, generatorReceipt, maximumCellConstraints) <-+        exactRegularSectionDispositionsWith (<>) mempty domain section+      let receipt =+            aggregateReceipt+              domain+              dispositions+              (maximumPowerSiteInputBits (storedRegularSites triangulation))+              clipReceipt+              generatorReceipt+              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++-- | Look up the exhaustive bounded-cell disposition for one submitted label.+powerCellDisposition+  :: Ord label+  => label+  -> BoundedPowerDiagram label+  -> Maybe (PowerCellDisposition label)+powerCellDisposition label (BoundedPowerDiagram dispositions) =+  Map.lookup label dispositions++-- | Enumerate every submitted label and bounded-cell disposition in order.+powerCellDispositions+  :: BoundedPowerDiagram label+  -> [(label, PowerCellDisposition label)]+powerCellDispositions (BoundedPowerDiagram dispositions) = Map.toAscList dispositions++-- | Publish the full-dimensional cell layer with a canonical exterior.+powerDiagramPlanarLayer+  :: Ord label+  => BoundedPowerDiagram label+  -> PlanarLayer (Maybe label)+powerDiagramPlanarLayer (BoundedPowerDiagram dispositions) =+  affineDispositionLayer 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]+        ]+    )+++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+  section <- first PowerRegularTopologyFailed (resolvedGeneratorSection generators)+  exactResolvedGeneratorDispositionsWith+    summarizeReceipt+    initialSummary+    domain+    section++exactResolvedGeneratorDispositionsWith+  :: Ord label+  => (summary -> ExactClipReceipt -> summary)+  -> summary+  -> ConvexPolygon+  -> ResolvedGeneratorSection label+  -> Either+      (PowerDiagramError label)+      ( Map label (PowerCellDisposition label)+      , summary+      , GeneratorRegularReceipt+      , Int+      )+exactResolvedGeneratorDispositionsWith summarizeReceipt initialSummary domain section = do+  retainedDomain <-+    first PowerDomainInvalid (exactRetainedPolygon (convexPolygonPoints domain))+  let topology = resolvedRegularTopology section+  constraints <- prepareRegularConstraintSection topology+  clipped <-+    traverse+      (clipPowerCell retainedDomain section constraints)+      (generatorRegularDispositions topology)+  let (dispositions, summary, maximumCellConstraints) =+        summarizeClippedPowerCells+          summarizeReceipt+          initialSummary+          (resolvedCoincidentDispositions section)+          (NonEmpty.toList clipped)+  pure+    ( dispositions+    , summary+    , generatorRegularReceipt topology+    , maximumCellConstraints+    )++exactRegularSectionDispositionsWith+  :: Ord label+  => (summary -> ExactClipReceipt -> summary)+  -> summary+  -> ConvexPolygon+  -> RegularSection label+  -> Either+      (PowerDiagramError label)+      ( Map label (PowerCellDisposition label)+      , summary+      , GeneratorRegularReceipt+      , Int+      )+exactRegularSectionDispositionsWith summarizeReceipt initialSummary domain section = do+  retainedDomain <-+    first PowerDomainInvalid (exactRetainedPolygon (convexPolygonPoints domain))+  constraints <- preparePublishedRegularConstraints section+  representativeDispositions <-+    traverse+      (\(label, generator) -> do+         disposition <-+           maybe+             (Left (PowerRegularDispositionMissing label))+             Right+             (Map.lookup label (sectionRegularDispositions section))+         clipPublishedPowerCell+           retainedDomain+           section+           constraints+           generator+           disposition)+      (Map.toAscList (sectionGenerators section))+  let (dispositions, summary, maximumCellConstraints) =+        summarizeClippedPowerCells+          summarizeReceipt+          initialSummary+          (sectionCoincidentDispositions section)+          representativeDispositions+  pure+    ( dispositions+    , summary+    , sectionRegularReceipt section+    , maximumCellConstraints+    )++summarizeClippedPowerCells+  :: Ord label+  => (summary -> ExactClipReceipt -> summary)+  -> summary+  -> Map label (CoincidentGeneratorDisposition label)+  -> [(label, PowerCellDisposition label, ExactClipReceipt, Int)]+  -> (Map label (PowerCellDisposition label), summary, Int)+summarizeClippedPowerCells summarizeReceipt initialSummary coincident clipped =+  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+   in ( fmap coincidentPowerDisposition coincident+          <> Map.fromList publishedAssociations+      , summary+      , maximumCellConstraints+      )++preparePublishedRegularConstraints+  :: Ord label+  => RegularSection label+  -> Either (PowerDiagramError label) (Map label [ExactClosedHalfPlane])+preparePublishedRegularConstraints section =+  Foldable.foldlM prepare Map.empty (Map.elems (sectionRegularEdges section))+ where+  prepare constraints edgeSection = do+    let edge = sectionRegularEdge edgeSection+        (firstLabel, secondLabel) = regularEdgeLabels edge+    firstGenerator <- requireSectionGenerator section firstLabel+    secondGenerator <- requireSectionGenerator section secondLabel+    insertRegularConstraintEdge constraints firstGenerator secondGenerator++clipPublishedPowerCell+  :: Ord label+  => ExactRetainedPolygon+  -> RegularSection label+  -> Map label [ExactClosedHalfPlane]+  -> ExactPowerGenerator label+  -> RegularSiteDisposition label+  -> Either+      (PowerDiagramError label)+      (label, PowerCellDisposition label, ExactClipReceipt, Int)+clipPublishedPowerCell retainedDomain section constraints ownerGenerator disposition =+  let ownerLabel = exactPowerGeneratorLabel ownerGenerator+      competitors =+        filter+          ((/= ownerLabel) . exactPowerGeneratorLabel)+          (Map.elems (sectionGenerators section))+      clipAs =+        clipRegularGeneratorCell+          retainedDomain+          ownerGenerator+          (Map.findWithDefault [] ownerLabel constraints)+          competitors+   in case disposition of+        RegularSiteHidden -> clipAs RegularGeneratorHidden+        RegularSiteVisible -> clipAs RegularGeneratorVisible+        RegularSiteLowerDimensional -> clipAs RegularGeneratorLowerDimensional+        RegularSiteCoincidentEquivalentTo _ ->+          Left (PowerRegularDispositionMissing ownerLabel)+        RegularSiteCoincidentDominatedBy _ ->+          Left (PowerRegularDispositionMissing ownerLabel)++requireSectionGenerator+  :: Ord label+  => RegularSection label+  -> label+  -> Either (PowerDiagramError label) (ExactPowerGenerator label)+requireSectionGenerator section label =+  maybe+    (Left (PowerRegularGeneratorMissing label))+    Right+    (Map.lookup label (sectionGenerators section))++discardClipReceipt :: () -> ExactClipReceipt -> ()+discardClipReceipt _ _ = ()++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+  insertRegularConstraintEdge constraints firstGenerator secondGenerator++insertRegularConstraintEdge+  :: Ord label+  => Map label [ExactClosedHalfPlane]+  -> ExactPowerGenerator label+  -> ExactPowerGenerator label+  -> Either (PowerDiagramError label) (Map label [ExactClosedHalfPlane])+insertRegularConstraintEdge constraints firstGenerator secondGenerator = do+  let 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+      competitors =+        [ generator+        | (generator, _) <-+            NonEmpty.toList (generatorRegularDispositions topology)+        , exactPowerGeneratorLabel generator /= ownerLabel+        ]+   in clipRegularGeneratorCell+        retainedDomain+        ownerGenerator+        (Map.findWithDefault [] ownerLabel constraints)+        competitors+        disposition++clipRegularGeneratorCell+  :: ExactRetainedPolygon+  -> ExactPowerGenerator label+  -> [ExactClosedHalfPlane]+  -> [ExactPowerGenerator label]+  -> RegularGeneratorDisposition+  -> Either+      (PowerDiagramError label)+      (label, PowerCellDisposition label, ExactClipReceipt, Int)+clipRegularGeneratorCell retainedDomain ownerGenerator activeConstraints competitors disposition =+  case disposition of+    RegularGeneratorHidden ->+      pure (exactPowerGeneratorLabel ownerGenerator, EmptyPowerCell, mempty, 0)+    RegularGeneratorVisible ->+      finishPowerCell retainedDomain ownerGenerator activeConstraints+    RegularGeneratorLowerDimensional -> do+      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+      }
+ src-public/Moonlight/Triangulation/Internal/PowerDiagram/Section.hs view
@@ -0,0 +1,489 @@+-- | Batch normalization and publication of one exact regular section.+module Moonlight.Triangulation.Internal.PowerDiagram.Section where++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 qualified Data.Set as Set+import Data.Set (Set)+import Moonlight.Triangulation.Exact+  ( ExactPoint+  , exactPointBitWidth+  , exactPointCoordinates+  , exactPointFromQueryPoint+  )+import Moonlight.Triangulation.Internal.BoundaryCycle (consecutivePairs)+import Moonlight.Triangulation.Internal.ExactRational (exactRationalBitWidth)+import Moonlight.Triangulation.Internal.PowerDiagram.Generator+import Moonlight.Triangulation.Internal.PowerDiagram.Hull (regularGeneratorTopology)+import Moonlight.Triangulation.Internal.PowerDiagram.Locality (buildRegularLocality)+import Moonlight.Triangulation.Internal.PowerDiagram.Model++regularTriangulation+  :: Ord label+  => NonEmpty (PowerSite label)+  -> Either+      (PowerDiagramError label)+      (RegularTriangulation label, RegularTriangulationReceipt)+regularTriangulation submitted = do+  sortedSites <- validateAndSortSites submitted+  triangulation <-+    first PowerRegularTopologyFailed+      ( constructRegularTriangulation+          (Map.fromDistinctAscList (fmap (\site -> (powerSiteLabel site, site)) (NonEmpty.toList sortedSites)))+      )+  pure (triangulation, regularTriangulationReceipt triangulation)++-- | The empty all-site section. It is the identity for insertion and the+-- result of removing the final site.+emptyRegularTriangulation :: RegularTriangulation label+emptyRegularTriangulation =+  RegularTriangulation+    { storedRegularSites = Map.empty+    , storedRegularSection = Nothing+    }++-- | Look up one canonical site by its stable label.+regularSite+  :: Ord label+  => label+  -> RegularTriangulation label+  -> Maybe (PowerSite label)+regularSite label = Map.lookup label . storedRegularSites++-- | Enumerate every canonical site in ascending label order, including hidden+-- and coincident sites.+regularSites :: RegularTriangulation label -> [PowerSite label]+regularSites = Map.elems . storedRegularSites++-- | Number of canonical sites, independent of their visibility.+regularSiteCount :: RegularTriangulation label -> Int+regularSiteCount = Map.size . storedRegularSites++-- | Observe one site's exhaustive derived visibility disposition.+regularSiteDisposition+  :: Ord label+  => label+  -> RegularTriangulation label+  -> Maybe (RegularSiteDisposition label)+regularSiteDisposition label triangulation =+  storedRegularSection triangulation+    >>= Map.lookup label . sectionRegularDispositions++-- | Enumerate canonical visible faces in ascending key order.+regularFaces :: RegularTriangulation label -> [RegularFace label]+regularFaces =+  maybe [] (Map.elems . sectionRegularFaces) . storedRegularSection++-- | Enumerate canonical regular edges in ascending endpoint order.+regularEdges :: RegularTriangulation label -> [RegularEdge label]+regularEdges =+  maybe [] (fmap sectionRegularEdge . Map.elems . sectionRegularEdges)+    . storedRegularSection++-- | Exact neighbours of one visible site; nonvisible and absent sites have none.+regularNeighbours+  :: Ord label+  => label+  -> RegularTriangulation label+  -> Set label+regularNeighbours label =+  maybe+    Set.empty+    ( maybe Set.empty sectionSiteNeighbours+        . Map.lookup label+        . sectionRegularStars+    )+    . storedRegularSection++-- | Construction statistics derived from the sealed normalized section.+regularTriangulationReceipt+  :: RegularTriangulation label+  -> RegularTriangulationReceipt+regularTriangulationReceipt triangulation =+  case storedRegularSection triangulation of+    Nothing -> emptyRegularTriangulationReceipt+    Just section ->+      regularReceiptFromSection (regularSiteCount triangulation) section++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++maximumPowerSiteInputBits :: Map label (PowerSite label) -> Int+maximumPowerSiteInputBits =+  Map.foldl' (\bits site -> max bits (powerSiteInputBitWidth site)) 0++constructRegularTriangulation+  :: Ord label+  => Map label (PowerSite label)+  -> Either (RegularTopologyError label) (RegularTriangulation label)+constructRegularTriangulation sites =+  case NonEmpty.nonEmpty (Map.elems sites) of+    Nothing -> Right emptyRegularTriangulation+    Just sortedSites -> do+      let generators = fmap (fst . prepareExactPowerGenerator) sortedSites+      section <- resolvedGeneratorSection generators+      pure (publishRegularSection sites section)++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)+      )++powerSiteInputBitWidth :: PowerSite label -> Int+powerSiteInputBitWidth site =+  max+    (exactPointBitWidth (exactPointFromQueryPoint (powerSiteQueryPoint site)))+    (exactRationalBitWidth (powerWeightExact (powerSiteWeight site)))++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++resolveCoincidentGeneratorGroup+  :: Ord label+  => NonEmpty (ExactPowerGenerator label)+  -> (ExactPowerGenerator label, [(label, CoincidentGeneratorDisposition label)])+resolveCoincidentGeneratorGroup generators@(initial :| remaining) =+  let representative = List.foldl' chooseCoincidentRepresentative initial remaining+      representativeLabel = exactPowerGeneratorLabel representative+   in ( representative+      , [ ( exactPowerGeneratorLabel generator+          , classifyCoincidentGenerator representative generator+          )+        | generator <- NonEmpty.toList generators+        , exactPowerGeneratorLabel generator /= representativeLabel+        ]+      )++chooseCoincidentRepresentative+  :: Ord label+  => ExactPowerGenerator label+  -> ExactPowerGenerator label+  -> ExactPowerGenerator label+chooseCoincidentRepresentative selected candidate =+  case+    compareCoincidentPriority+      (exactPowerGeneratorConstant candidate, exactPowerGeneratorLabel candidate)+      (exactPowerGeneratorConstant selected, exactPowerGeneratorLabel selected) of+    GT -> candidate+    _ -> selected++compareCoincidentPriority+  :: (Ord label, Ord value)+  => (value, label)+  -> (value, label)+  -> Ordering+compareCoincidentPriority (candidateValue, candidateLabel) (selectedValue, selectedLabel) =+  compare candidateValue selectedValue <> compare selectedLabel candidateLabel++classifyCoincidentGenerator+  :: ExactPowerGenerator label+  -> ExactPowerGenerator label+  -> CoincidentGeneratorDisposition label+classifyCoincidentGenerator representative candidate =+  classifyCoincidentValue+    (exactPowerGeneratorLabel representative)+    (exactPowerGeneratorConstant representative)+    (exactPowerGeneratorConstant candidate)++classifyCoincidentValue+  :: Eq value+  => label+  -> value+  -> value+  -> CoincidentGeneratorDisposition label+classifyCoincidentValue representativeLabel representativeValue candidateValue+  | candidateValue == representativeValue =+      CoincidentGeneratorEquivalentTo representativeLabel+  | otherwise = CoincidentGeneratorDominatedBy representativeLabel+++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+++resolvedGeneratorSection+  :: Ord label+  => NonEmpty (ExactPowerGenerator label)+  -> Either (RegularTopologyError 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 <- regularGeneratorTopology (DistinctSlopeGenerators representatives)+  pure+    ResolvedGeneratorSection+      { resolvedCoincidentDispositions = coincidentDispositions+      , resolvedRegularTopology = topology+      }++publishRegularSection+  :: Ord label+  => Map label (PowerSite label)+  -> ResolvedGeneratorSection label+  -> RegularTriangulation label+publishRegularSection sites resolvedSection =+  let topology = resolvedRegularTopology resolvedSection+      coincident = resolvedCoincidentDispositions resolvedSection+      representativeDispositions = publishRepresentativeDispositions topology+      dispositions =+        representativeDispositions <> fmap coincidentRegularDisposition coincident+      generators =+        Map.fromList+          [ (exactPowerGeneratorLabel generator, generator)+          | (generator, _) <- NonEmpty.toList (generatorRegularDispositions topology)+          ]+      faces = fmap publishGeneratorFace (generatorRegularFaces topology)+      faceSection = Map.fromList (fmap (\face -> (regularFaceKey face, face)) faces)+      edgeFaceIncidence =+        List.foldl'+          (Map.unionWith Set.union)+          Map.empty+          (fmap regularFaceEdgeIncidence faces)+      edges = fmap publishGeneratorEdge (generatorRegularEdges topology)+      edgeSection =+        Map.fromList+          [ ( key+            , RegularEdgeSection+                edge+                (Map.findWithDefault Set.empty key edgeFaceIncidence)+            )+          | edge <- edges+          , let key = regularEdgeKey edge+          ]+      stars = regularSiteStars dispositions faceSection edgeSection+      baseSection =+        RegularSection+          { sectionGenerators = generators+          , sectionSlopeRepresentatives =+              Map.fromList+                [ (exactGeneratorSlope generator, label)+                | (label, generator) <- Map.toAscList generators+                ]+          , sectionCoincidentDispositions = coincident+          , sectionRegularDispositions = dispositions+          , sectionRegularFaces = faceSection+          , sectionRegularEdges = edgeSection+          , sectionRegularStars = stars+          , sectionRegularReceipt = generatorRegularReceipt topology+          , sectionRegularLocality = Nothing+          }+      section =+        baseSection+          { sectionRegularLocality = buildRegularLocality baseSection+          }+   in RegularTriangulation+        { storedRegularSites = sites+        , storedRegularSection = Just section+        }++regularReceiptFromSection+  :: Int+  -> RegularSection label+  -> RegularTriangulationReceipt+regularReceiptFromSection inputSites section =+  let generatorReceipt = sectionRegularReceipt section+      coincidentCount = Map.size (sectionCoincidentDispositions section)+   in RegularTriangulationReceipt+        { regularTriangulationInputSites = inputSites+        , regularTriangulationRepresentativeSites =+            generatorRegularInputSites generatorReceipt+        , regularTriangulationVisibleSites =+            generatorRegularVisibleSites generatorReceipt+        , regularTriangulationLowerDimensionalSites =+            generatorRegularLowerDimensionalSites generatorReceipt+        , regularTriangulationHiddenSites =+            generatorRegularHiddenSites generatorReceipt+        , regularTriangulationCoincidentSites = coincidentCount+        , regularTriangulationFaces = generatorRegularFaceCount generatorReceipt+        , regularTriangulationEdges = generatorRegularEdgeCount generatorReceipt+        }++emptyRegularTriangulationReceipt :: RegularTriangulationReceipt+emptyRegularTriangulationReceipt =+  RegularTriangulationReceipt+    { regularTriangulationInputSites = 0+    , regularTriangulationRepresentativeSites = 0+    , regularTriangulationVisibleSites = 0+    , regularTriangulationLowerDimensionalSites = 0+    , regularTriangulationHiddenSites = 0+    , regularTriangulationCoincidentSites = 0+    , regularTriangulationFaces = 0+    , regularTriangulationEdges = 0+    }++emptyGeneratorRegularReceipt :: GeneratorRegularReceipt+emptyGeneratorRegularReceipt =+  GeneratorRegularReceipt+    { generatorRegularInputSites = 0+    , generatorRegularVisibleSites = 0+    , generatorRegularLowerDimensionalSites = 0+    , generatorRegularHiddenSites = 0+    , generatorRegularFaceCount = 0+    , generatorRegularEdgeCount = 0+    }++publishGeneratorDisposition+  :: RegularGeneratorDisposition+  -> RegularSiteDisposition label+publishGeneratorDisposition disposition =+  case disposition of+    RegularGeneratorVisible -> RegularSiteVisible+    RegularGeneratorLowerDimensional -> RegularSiteLowerDimensional+    RegularGeneratorHidden -> RegularSiteHidden++publishGeneratorFace+  :: Ord label+  => GeneratorRegularFace label+  -> RegularFace label+publishGeneratorFace face =+  canonicalRegularFace+    (generatorRegularFaceFirst face)+    (generatorRegularFaceSecond face)+    (generatorRegularFaceThird face)+    (generatorRegularFaceDualPoint face)++canonicalRegularFace+  :: Ord label+  => label+  -> label+  -> label+  -> ExactPoint+  -> RegularFace label+canonicalRegularFace firstLabel secondLabel thirdLabel dualPoint+  | firstLabel <= secondLabel && firstLabel <= thirdLabel =+      RegularFace firstLabel secondLabel thirdLabel dualPoint+  | secondLabel <= thirdLabel =+      RegularFace secondLabel thirdLabel firstLabel dualPoint+  | otherwise =+      RegularFace thirdLabel firstLabel secondLabel dualPoint++publishGeneratorEdge :: GeneratorRegularEdge label -> RegularEdge label+publishGeneratorEdge edge =+  RegularEdge+    (exactPowerGeneratorLabel (generatorRegularEdgeFirst edge))+    (exactPowerGeneratorLabel (generatorRegularEdgeSecond edge))+    (publishGeneratorDualGeometry (generatorRegularEdgeDual edge))++publishGeneratorDualGeometry :: GeneratorDualGeometry -> PowerDualEdge+publishGeneratorDualGeometry dual =+  case dual of+    GeneratorDualSegment segment -> BoundedPowerDual segment+    GeneratorDualRay ray -> UnboundedPowerDual ray+    GeneratorDualLine line -> FullLinePowerDual line+    GeneratorDualCollapsed point -> CollapsedPowerDual point++regularSiteStars+  :: Ord label+  => Map label disposition+  -> Map (RegularFaceKey label) (RegularFace label)+  -> Map (RegularEdgeKey label) (RegularEdgeSection label)+  -> Map label (RegularSiteStar label)+regularSiteStars dispositions faces =+  Map.foldl'+    insertRegularEdgeStar+    (Map.foldlWithKey' insertRegularFaceStar (emptyRegularStars dispositions) faces)++emptyRegularStars+  :: Map label disposition+  -> Map label (RegularSiteStar label)+emptyRegularStars = Map.map (const emptyRegularSiteStar)++publishRepresentativeDispositions+  :: Ord label+  => GeneratorRegularTopology label+  -> Map label (RegularSiteDisposition label)+publishRepresentativeDispositions topology =+  Map.fromList+    [ ( exactPowerGeneratorLabel generator+      , publishGeneratorDisposition disposition+      )+    | (generator, disposition) <-+        NonEmpty.toList (generatorRegularDispositions topology)+    ]++insertRegularFaceStar+  :: Ord label+  => Map label (RegularSiteStar label)+  -> RegularFaceKey label+  -> RegularFace label+  -> Map label (RegularSiteStar label)+insertRegularFaceStar stars faceKey face =+  let (firstLabel, secondLabel, thirdLabel) = regularFaceLabels face+   in Foldable.foldl'+        (\current label -> Map.adjust (addIncidentFace faceKey) label current)+        stars+        [firstLabel, secondLabel, thirdLabel]++insertRegularEdgeStar+  :: Ord label+  => Map label (RegularSiteStar label)+  -> RegularEdgeSection label+  -> Map label (RegularSiteStar label)+insertRegularEdgeStar stars edgeSection =+  let (firstLabel, secondLabel) = regularEdgeLabels (sectionRegularEdge edgeSection)+   in Map.adjust (addNeighbour secondLabel) firstLabel+        (Map.adjust (addNeighbour firstLabel) secondLabel stars)++addIncidentFace+  :: Ord label+  => RegularFaceKey label+  -> RegularSiteStar label+  -> RegularSiteStar label+addIncidentFace faceKey star =+  star{sectionIncidentFaces = Set.insert faceKey (sectionIncidentFaces star)}++addNeighbour :: Ord label => label -> RegularSiteStar label -> RegularSiteStar label+addNeighbour label star =+  star{sectionSiteNeighbours = Set.insert label (sectionSiteNeighbours star)}
− src-public/Moonlight/Triangulation/Internal/RegularTriangulation.hs
@@ -1,1111 +0,0 @@-{-# 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/Overlay.hs view
@@ -48,6 +48,7 @@   , overlayEmbeddedTriangulation   , overlayReceipt   , overlayCells+  , foldBoundedOverlayCells   , overlayArrangementVertices   , overlayArrangementEdges   , overlayPlanarLayer
src-public/Moonlight/Triangulation/PowerDiagram.hs view
@@ -1,19 +1,17 @@-{-# 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.+-- | Exact regular topology and power cells. The complete labelled weighted-site+-- section is authoritative; visibility, incidence, and bounded planar layers+-- are derived views. module Moonlight.Triangulation.PowerDiagram   ( PowerWeight   , PowerWeightError (..)   , powerWeight+  , powerWeightFromExact   , powerWeightExact   , PowerSite   , powerSite   , powerSiteLabel   , powerSitePosition+  , powerSiteExactPosition   , powerSiteWeight   , PowerCellDisposition (..)   , RegularSiteDisposition (..)@@ -25,14 +23,26 @@   , regularEdgeLabels   , regularEdgeDual   , RegularTriangulation+  , emptyRegularTriangulation   , regularTriangulation+  , regularSite+  , regularSites+  , regularSiteCount   , regularSiteDisposition   , regularFaces   , regularEdges   , regularNeighbours+  , regularTriangulationReceipt   , RegularTriangulationReceipt (..)+  , RegularSiteTransition (..)+  , RegularEditError (..)+  , RegularEditResult (..)+  , insertRegularSite+  , removeRegularSite+  , reweightRegularSites   , BoundedPowerDiagram   , boundedPowerDiagram+  , boundedPowerDiagramFromRegular   , powerCellDisposition   , powerCellDispositions   , powerDiagramPlanarLayer@@ -47,909 +57,8 @@   , 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-      }+import Moonlight.Triangulation.Internal.PowerDiagram.Edit+import Moonlight.Triangulation.Internal.PowerDiagram.Generator (RegularTopologyError (..))+import Moonlight.Triangulation.Internal.PowerDiagram.Model+import Moonlight.Triangulation.Internal.PowerDiagram.Projection+import Moonlight.Triangulation.Internal.PowerDiagram.Section
+ src-public/Moonlight/Triangulation/RegularAlpha.hs view
@@ -0,0 +1,393 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}++-- | Exact weighted alpha filtration of a full-dimensional regular+-- subdivision. Births are signed power radii; Homology lowering remains in+-- @moonlight-triangulation:cell-complex@.+module Moonlight.Triangulation.RegularAlpha+  ( PowerAlphaBirth+  , powerAlphaBirthExact+  , powerAlphaBirthNumerator+  , powerAlphaBirthDenominator+  , RegularAlphaFiltration+  , RegularAlphaError (..)+  , regularAlphaFiltration+  , regularAlphaComplex+  , regularAlphaBirths+  , regularAlphaSimplexBirth+  , regularAlphaCriticalBirths+  , regularAlphaComplexAtBirth+  )+where++import Control.DeepSeq (NFData)+import Data.Bifunctor (first)+import Data.List.NonEmpty qualified as NonEmpty+import Data.Map.Strict (Map)+import Data.Map.Strict qualified as Map+import Data.Set (Set)+import Data.Set qualified as Set+import GHC.Generics (Generic)+import Moonlight.Triangulation.Exact+  ( ExactAffineLine+  , ExactArithmeticError+  , ExactPoint+  , ExactRational+  , ExactVector (..)+  , exactAffineLineCoefficients+  , exactDivide+  , exactPoint+  , exactPointCoordinates+  , exactRayDirection+  , exactRayOrigin+  , exactRationalDenominator+  , exactRationalNumerator+  , exactSegmentEndpoints+  )+import Moonlight.Triangulation.PowerDiagram+  ( PowerDualEdge (..)+  , PowerSite+  , RegularEdge+  , RegularFace+  , RegularSiteDisposition (..)+  , RegularTriangulation+  , powerSiteExactPosition+  , powerSiteLabel+  , powerSiteWeight+  , powerWeightExact+  , regularEdgeDual+  , regularEdgeLabels+  , regularEdges+  , regularFaceDualPoint+  , regularFaceLabels+  , regularFaces+  , regularNeighbours+  , regularSite+  , regularSiteDisposition+  , regularSites+  )+import Moonlight.Triangulation.Simplex+  ( PlanarComplex+  , PlanarComplexError+  , PlanarSimplex+  , PlanarSimplexError+  , planarComplex+  , planarEdge+  , planarFace+  , planarSimplexVertices+  , planarVertex+  )++-- | Signed exact power radius. Unlike ordinary alpha birth, negative values+-- are lawful when a positive site weight already contains the simplex at+-- negative power level.+newtype PowerAlphaBirth = PowerAlphaBirth ExactRational+  deriving stock (Eq, Ord, Show, Generic)+  deriving anyclass (NFData)++-- | Recover the reduced exact power radius.+powerAlphaBirthExact :: PowerAlphaBirth -> ExactRational+powerAlphaBirthExact (PowerAlphaBirth value) = value++-- | Numerator of the reduced exact power radius.+powerAlphaBirthNumerator :: PowerAlphaBirth -> Integer+powerAlphaBirthNumerator = exactRationalNumerator . powerAlphaBirthExact++-- | Positive denominator of the reduced exact power radius.+powerAlphaBirthDenominator :: PowerAlphaBirth -> Integer+powerAlphaBirthDenominator = exactRationalDenominator . powerAlphaBirthExact++-- | One admitted labelled regular complex and its total exact birth section.+data RegularAlphaFiltration label = RegularAlphaFiltration+  { regularAlphaComplex :: !(PlanarComplex label)+    -- ^ Canonical full-dimensional regular complex.+  , regularAlphaBirths :: !(Map (PlanarSimplex label) PowerAlphaBirth)+    -- ^ Total exact birth section over that complex.+  }+  deriving stock (Eq, Show, Generic)+  deriving anyclass (NFData)++-- | Typed topology, dual-consistency, projection, and sublevel obstructions.+data RegularAlphaError label+  = RegularAlphaSiteMissing !label+  | RegularAlphaSimplexInvalid !(PlanarSimplexError label)+  | RegularAlphaComplexInvalid !(PlanarComplexError label)+  | RegularAlphaDualMismatch+      !(PlanarSimplex label)+      !ExactRational+      !ExactRational+  | RegularAlphaProjectionFailed+      !(PlanarSimplex label)+      !ExactArithmeticError+  | RegularAlphaVertexBirthMissing !label+  | RegularAlphaSublevelInvalid !(PlanarComplexError label)+  deriving stock (Eq, Show, Generic)+  deriving anyclass (NFData)++-- | Derive one exact weighted-alpha birth section from the resident regular dual.+regularAlphaFiltration+  :: Ord label+  => RegularTriangulation label+  -> Either (RegularAlphaError label) (RegularAlphaFiltration label)+regularAlphaFiltration triangulation = do+  let visibleLabels =+        Set.fromAscList+          [ powerSiteLabel site+          | site <- regularSites triangulation+          , regularSiteDisposition (powerSiteLabel site) triangulation+              == Just RegularSiteVisible+          ]+      visibleEdges = filter (edgeIsVisible visibleLabels) (regularEdges triangulation)+      visibleFaces = filter (faceIsVisible visibleLabels) (regularFaces triangulation)+  edgeSimplices <- traverse regularEdgeSimplex visibleEdges+  faceSimplices <- traverse regularFaceSimplex visibleFaces+  complexValue <-+    first RegularAlphaComplexInvalid+      ( planarComplex+          ( Set.unions+              [ Set.map planarVertex visibleLabels+              , Set.fromList edgeSimplices+              , Set.fromList faceSimplices+              ]+          )+      )+  faceBirthSection <-+    Map.fromList <$> traverse (regularFaceBirth triangulation) visibleFaces+  edgeBirthEntries <- traverse (regularEdgeBirth triangulation) visibleEdges+  let edgeBirthSection = Map.fromList edgeBirthEntries+      incidentEdgeBirths =+        Map.fromListWith min (edgeBirthEntries >>= incidentEdgeBirthsFor)+  vertexBirthSection <-+    Map.fromList+      <$> traverse+        (regularVertexBirth triangulation incidentEdgeBirths)+        (Set.toAscList visibleLabels)+  pure+    RegularAlphaFiltration+      { regularAlphaComplex = complexValue+      , regularAlphaBirths =+          vertexBirthSection <> edgeBirthSection <> faceBirthSection+      }++incidentEdgeBirthsFor+  :: (PlanarSimplex label, PowerAlphaBirth)+  -> [(label, PowerAlphaBirth)]+incidentEdgeBirthsFor (simplex, birthValue) =+  case NonEmpty.toList (planarSimplexVertices simplex) of+    [firstLabel, secondLabel] ->+      [ (firstLabel, birthValue)+      , (secondLabel, birthValue)+        ]+    _ -> []++-- | Look up the exact birth of one admitted simplex.+regularAlphaSimplexBirth+  :: Ord label+  => PlanarSimplex label+  -> RegularAlphaFiltration label+  -> Maybe PowerAlphaBirth+regularAlphaSimplexBirth simplex = Map.lookup simplex . regularAlphaBirths++-- | Distinct births in ascending exact order.+regularAlphaCriticalBirths+  :: RegularAlphaFiltration label+  -> [PowerAlphaBirth]+regularAlphaCriticalBirths =+  Set.toAscList . Set.fromList . Map.elems . regularAlphaBirths++-- | Reconstruct the closed subcomplex born no later than the threshold.+regularAlphaComplexAtBirth+  :: Ord label+  => PowerAlphaBirth+  -> RegularAlphaFiltration label+  -> Either (RegularAlphaError label) (PlanarComplex label)+regularAlphaComplexAtBirth threshold filtration =+  first RegularAlphaSublevelInvalid+    ( planarComplex+        ( Map.keysSet+            (Map.filter (<= threshold) (regularAlphaBirths filtration))+        )+    )++edgeIsVisible :: Ord label => Set label -> RegularEdge label -> Bool+edgeIsVisible visible edge =+  let (firstLabel, secondLabel) = regularEdgeLabels edge+   in Set.member firstLabel visible && Set.member secondLabel visible++faceIsVisible :: Ord label => Set label -> RegularFace label -> Bool+faceIsVisible visible face =+  let (firstLabel, secondLabel, thirdLabel) = regularFaceLabels face+   in all (`Set.member` visible) [firstLabel, secondLabel, thirdLabel]++regularEdgeSimplex+  :: Ord label+  => RegularEdge label+  -> Either (RegularAlphaError label) (PlanarSimplex label)+regularEdgeSimplex edge =+  let (firstLabel, secondLabel) = regularEdgeLabels edge+   in first RegularAlphaSimplexInvalid (planarEdge firstLabel secondLabel)++regularFaceSimplex+  :: Ord label+  => RegularFace label+  -> Either (RegularAlphaError label) (PlanarSimplex label)+regularFaceSimplex face =+  let (firstLabel, secondLabel, thirdLabel) = regularFaceLabels face+   in first RegularAlphaSimplexInvalid+        (planarFace firstLabel secondLabel thirdLabel)++regularFaceBirth+  :: Ord label+  => RegularTriangulation label+  -> RegularFace label+  -> Either (RegularAlphaError label) (PlanarSimplex label, PowerAlphaBirth)+regularFaceBirth triangulation face = do+  simplex <- regularFaceSimplex face+  let (firstLabel, secondLabel, thirdLabel) = regularFaceLabels face+      dualPoint = regularFaceDualPoint face+  firstSite <- requireRegularSite triangulation firstLabel+  secondSite <- requireRegularSite triangulation secondLabel+  thirdSite <- requireRegularSite triangulation thirdLabel+  let firstBirth = powerDistance firstSite dualPoint+      secondBirth = powerDistance secondSite dualPoint+      thirdBirth = powerDistance thirdSite dualPoint+  requireEqualDual simplex firstBirth secondBirth+  requireEqualDual simplex firstBirth thirdBirth+  pure (simplex, PowerAlphaBirth firstBirth)++regularEdgeBirth+  :: Ord label+  => RegularTriangulation label+  -> RegularEdge label+  -> Either (RegularAlphaError label) (PlanarSimplex label, PowerAlphaBirth)+regularEdgeBirth triangulation edge = do+  simplex <- regularEdgeSimplex edge+  let (firstLabel, secondLabel) = regularEdgeLabels edge+  firstSite <- requireRegularSite triangulation firstLabel+  secondSite <- requireRegularSite triangulation secondLabel+  minimizingPoint <- minimizePowerOnDual simplex firstSite (regularEdgeDual edge)+  let firstBirth = powerDistance firstSite minimizingPoint+      secondBirth = powerDistance secondSite minimizingPoint+  requireEqualDual simplex firstBirth secondBirth+  pure (simplex, PowerAlphaBirth firstBirth)++regularVertexBirth+  :: Ord label+  => RegularTriangulation label+  -> Map label PowerAlphaBirth+  -> label+  -> Either (RegularAlphaError label) (PlanarSimplex label, PowerAlphaBirth)+regularVertexBirth triangulation incidentBirths label = do+  owner <- requireRegularSite triangulation label+  competitors <-+    traverse (requireRegularSite triangulation) (Set.toAscList (regularNeighbours label triangulation))+  let ownerPoint = powerSiteExactPosition owner+      ownerBirth = powerDistance owner ownerPoint+  if all ((ownerBirth <=) . (`powerDistance` ownerPoint)) competitors+    then Right (planarVertex label, PowerAlphaBirth ownerBirth)+    else+      case Map.lookup label incidentBirths of+        Just birthValue -> Right (planarVertex label, birthValue)+        Nothing -> Left (RegularAlphaVertexBirthMissing label)++minimizePowerOnDual+  :: PlanarSimplex label+  -> PowerSite label+  -> PowerDualEdge+  -> Either (RegularAlphaError label) ExactPoint+minimizePowerOnDual simplex site dual =+  case dual of+    BoundedPowerDual segment -> do+      let (origin, terminal) = exactSegmentEndpoints segment+      projected <- projectAlong simplex (powerSiteExactPosition site) origin (differenceVector origin terminal)+      pure (pointAtClampedParameter 0 1 origin (differenceVector origin terminal) projected)+    UnboundedPowerDual ray -> do+      let origin = exactRayOrigin ray+          direction = exactRayDirection ray+      projected <- projectAlong simplex (powerSiteExactPosition site) origin direction+      pure (pointAtParameter origin direction (max 0 projected))+    FullLinePowerDual line -> projectOntoLine simplex (powerSiteExactPosition site) line+    CollapsedPowerDual point -> Right point++projectAlong+  :: PlanarSimplex label+  -> ExactPoint+  -> ExactPoint+  -> ExactVector+  -> Either (RegularAlphaError label) ExactRational+projectAlong simplex query origin direction =+  let (queryX, queryY) = exactPointCoordinates query+      (originX, originY) = exactPointCoordinates origin+      ExactVector directionX directionY = direction+      numerator = (queryX - originX) * directionX + (queryY - originY) * directionY+      denominator = directionX * directionX + directionY * directionY+   in first (RegularAlphaProjectionFailed simplex)+        (exactDivide numerator denominator)++pointAtClampedParameter+  :: ExactRational+  -> ExactRational+  -> ExactPoint+  -> ExactVector+  -> ExactRational+  -> ExactPoint+pointAtClampedParameter lower upper origin direction parameter =+  pointAtParameter origin direction (max lower (min upper parameter))++pointAtParameter :: ExactPoint -> ExactVector -> ExactRational -> ExactPoint+pointAtParameter origin (ExactVector directionX directionY) parameter =+  let (originX, originY) = exactPointCoordinates origin+   in exactPoint+        (originX + parameter * directionX)+        (originY + parameter * directionY)++projectOntoLine+  :: PlanarSimplex label+  -> ExactPoint+  -> ExactAffineLine+  -> Either (RegularAlphaError label) ExactPoint+projectOntoLine simplex query line = do+  let (normalX, normalY, constant) = exactAffineLineCoefficients line+      (queryX, queryY) = exactPointCoordinates query+      lineValue = normalX * queryX + normalY * queryY + constant+      squaredNormal = normalX * normalX + normalY * normalY+  displacement <-+    first (RegularAlphaProjectionFailed simplex)+      (exactDivide lineValue squaredNormal)+  pure+    ( exactPoint+        (queryX - displacement * normalX)+        (queryY - displacement * normalY)+    )++powerDistance :: PowerSite label -> ExactPoint -> ExactRational+powerDistance site point =+  let (pointX, pointY) = exactPointCoordinates point+      (siteX, siteY) = exactPointCoordinates (powerSiteExactPosition site)+      deltaX = pointX - siteX+      deltaY = pointY - siteY+   in deltaX * deltaX + deltaY * deltaY - powerWeightExact (powerSiteWeight site)++differenceVector :: ExactPoint -> ExactPoint -> ExactVector+differenceVector from to =+  let (fromX, fromY) = exactPointCoordinates from+      (toX, toY) = exactPointCoordinates to+   in ExactVector (toX - fromX) (toY - fromY)++requireRegularSite+  :: Ord label+  => RegularTriangulation label+  -> label+  -> Either (RegularAlphaError label) (PowerSite label)+requireRegularSite triangulation label =+  maybe (Left (RegularAlphaSiteMissing label)) Right (regularSite label triangulation)++requireEqualDual+  :: PlanarSimplex label+  -> ExactRational+  -> ExactRational+  -> Either (RegularAlphaError label) ()+requireEqualDual simplex expected actual+  | expected == actual = Right ()+  | otherwise = Left (RegularAlphaDualMismatch simplex expected actual)
src-zigzag/Moonlight/Triangulation/Zigzag.hs view
@@ -8,7 +8,6 @@ module Moonlight.Triangulation.Zigzag   ( ActivationPoint (..),     ActivationSlice (..),-    ActivationCell (..),     ActivationStage (..),     ActivationZigzagError (..),     ActivationComplex,@@ -46,15 +45,10 @@ import Moonlight.Homology.Boundary   ( BoundaryIncidence,     FiniteChainComplex,-    emptyBoundaryIncidence,-    emptyBoundaryIncidenceOf,-    materializeIncidenceBoundary,-    mkFiniteChainComplexChecked,     overlapBoundaryIncidence,   ) import Moonlight.Homology.Chain   ( HomologicalDegree (..),-    HomologyFailure,   ) import Moonlight.Homology.Persistence   ( FiniteChainMap,@@ -81,6 +75,25 @@   ( DCELComplex,     fromExactCellSet,   )+import Moonlight.Triangulation.LabelledComplex+  ( PlanarComplexInterpretationError,+    finitePlanarComplex,+  )+import Moonlight.Triangulation.Simplex+  ( PlanarComplex,+    PlanarComplexError,+    PlanarSimplex,+    PlanarSimplexError,+    SimplexDimension (..),+    intersectPlanarComplex,+    planarComplex,+    planarComplexBasisAt,+    planarComplexCells,+    planarEdge,+    planarFace,+    planarVertex,+    unionPlanarComplex,+  ) import Moonlight.Triangulation.Dcel qualified as Dcel import Moonlight.Triangulation.Handles.HandleDefs   ( FaceId,@@ -113,14 +126,6 @@   }   deriving stock (Eq, Show) --- | Canonical abstract simplex identity induced by stable vertex labels.--- Edge and face labels are stored in ascending order.-data ActivationCell label-  = ActivationVertex !label-  | ActivationEdge !label !label-  | ActivationFace !label !label !label-  deriving stock (Eq, Ord, Show)- -- | Vertices of the expanded zigzag alternate between observed depths and -- their adjacent common ambient unions. data ActivationStage depth@@ -136,39 +141,44 @@   | ActivationFiltrationFailed !depth !AlphaFiltrationError   | ActivationSelectionFailed !depth !CellSelectionError   | ActivationInnerFaceInvalid !(ActivationStage depth) !FaceId-  | ActivationChainComplexFailed !(ActivationStage depth) !HomologyFailure+  | ActivationSimplexInvalid !(ActivationStage depth) !(PlanarSimplexError label)+  | ActivationComplexInvalid !(ActivationStage depth) !(PlanarComplexError label)+  | ActivationChainComplexFailed+      !(ActivationStage depth)+      !(PlanarComplexInterpretationError label)   | ActivationZigzagFailed !ZigzagFailure   | ActivationStageIndexMissing !Int   deriving stock (Eq, Show) -data ActivationBasis label = ActivationBasis-  { activationVertexBasis :: ![ActivationCell label],-    activationEdgeBasis :: ![ActivationCell label],-    activationFaceBasis :: ![ActivationCell label]-  }- -- | A checked, canonically labelled simplicial interpretation of one observed -- alpha complex or adjacent union. data ActivationComplex depth label = ActivationComplex   { activationComplexStage :: !(ActivationStage depth),-    activationComplexCells :: !(Set (ActivationCell label)),-    activationComplexBasis :: !(ActivationBasis label),+    storedActivationPlanarComplex :: !(PlanarComplex label),     activationComplexChainComplex :: !(FiniteChainComplex Int)   } +activationComplexCells+  :: ActivationComplex depth label+  -> Set (PlanarSimplex label)+activationComplexCells = planarComplexCells . storedActivationPlanarComplex+ -- | The canonical labelled basis corresponding to the numeric basis of -- 'activationComplexChainComplex' at one degree. activationComplexBasisAt ::   ActivationComplex depth label ->   HomologicalDegree ->-  [ActivationCell label]+  [PlanarSimplex label] activationComplexBasisAt complexValue degreeValue =-  activationBasisAt degreeValue (activationComplexBasis complexValue)+  maybe+    []+    (Vector.toList . (`planarComplexBasisAt` storedActivationPlanarComplex complexValue))+    (simplexDimensionAt degreeValue)  -- | The two checked inclusion legs proving how adjacent observations meet in -- their union.  The common-cell set is a derived inspection surface. data AdjacentUnionWitness depth label = AdjacentUnionWitness-  { adjacentCommonCells :: !(Set (ActivationCell label)),+  { adjacentCommonCells :: !(Set (PlanarSimplex label)),     adjacentUnionComplex :: !(ActivationComplex depth label),     adjacentLeftInclusion :: !(FiniteChainMap Int),     adjacentRightInclusion :: !(FiniteChainMap Int)@@ -274,137 +284,71 @@   ActivationStage depth ->   DelaunayTriangulation label ->   DCELComplex ->-  Either (ActivationZigzagError depth label) (Set (ActivationCell label))+  Either (ActivationZigzagError depth label) (PlanarComplex label) activationCellsFromDcel stage triangulation complexValue = do   let vertexCells =         fmap-          (ActivationVertex . Dcel.vertexData triangulation)+          (planarVertex . Dcel.vertexData triangulation)           (CellComplex.vertices complexValue)-      edgeCells =-        fmap-          (activationEdgeFromDcel triangulation complexValue)-          (CellComplex.edges complexValue)+  edgeCells <-+    traverse+      (activationEdgeFromDcel stage triangulation complexValue)+      (CellComplex.edges complexValue)   faceCells <-     traverse       (activationFaceFromDcel stage triangulation)       (CellComplex.faces complexValue)-  pure (Set.fromList (vertexCells <> edgeCells <> faceCells))+  first (ActivationComplexInvalid stage)+    (planarComplex (Set.fromList (vertexCells <> edgeCells <> faceCells)))  activationEdgeFromDcel ::   Ord label =>+  ActivationStage depth ->   DelaunayTriangulation label ->   DCELComplex ->   UndirectedEdgeId ->-  ActivationCell label-activationEdgeFromDcel triangulation complexValue edgeValue =+  Either (ActivationZigzagError depth label) (PlanarSimplex label)+activationEdgeFromDcel stage triangulation complexValue edgeValue =   let (firstVertex, secondVertex) = CellComplex.edgeBoundary complexValue edgeValue-   in orderedActivationEdge-        (Dcel.vertexData triangulation firstVertex)-        (Dcel.vertexData triangulation secondVertex)+   in first (ActivationSimplexInvalid stage)+        ( planarEdge+            (Dcel.vertexData triangulation firstVertex)+            (Dcel.vertexData triangulation secondVertex)+        )  activationFaceFromDcel ::   Ord label =>   ActivationStage depth ->   DelaunayTriangulation label ->   FaceId ->-  Either (ActivationZigzagError depth label) (ActivationCell label)+  Either (ActivationZigzagError depth label) (PlanarSimplex label) activationFaceFromDcel stage triangulation faceValue =   case Dcel.innerFaceVertices triangulation faceValue of     Nothing -> Left (ActivationInnerFaceInvalid stage faceValue)     Just (firstVertex, secondVertex, thirdVertex) ->-      Right-        ( orderedActivationFace+      first (ActivationSimplexInvalid stage)+        ( planarFace             (Dcel.vertexData triangulation firstVertex)             (Dcel.vertexData triangulation secondVertex)             (Dcel.vertexData triangulation thirdVertex)         ) -orderedActivationEdge :: Ord label => label -> label -> ActivationCell label-orderedActivationEdge firstLabel secondLabel =-  uncurry ActivationEdge (ascendingPair firstLabel secondLabel)--orderedActivationFace :: Ord label => label -> label -> label -> ActivationCell label-orderedActivationFace firstLabel secondLabel thirdLabel =-  let (firstLow, firstHigh) = ascendingPair firstLabel secondLabel-      (secondLow, finalHigh) = ascendingPair firstHigh thirdLabel-      (finalLow, finalMiddle) = ascendingPair firstLow secondLow-   in ActivationFace finalLow finalMiddle finalHigh--ascendingPair :: Ord value => value -> value -> (value, value)-ascendingPair firstValue secondValue =-  if firstValue <= secondValue-    then (firstValue, secondValue)-    else (secondValue, firstValue)- compileActivationComplex ::   Ord label =>   ActivationStage depth ->-  Set (ActivationCell label) ->+  PlanarComplex label ->   Either (ActivationZigzagError depth label) (ActivationComplex depth label)-compileActivationComplex stage cells = do-  let basis = activationBasisFromCells cells-  degreeOneBoundary <--    first (ActivationChainComplexFailed stage)-      ( materializeIncidenceBoundary-          activationCellBoundary-          (activationEdgeBasis basis)-          (activationVertexBasis basis)-      )-  degreeTwoBoundary <--    first (ActivationChainComplexFailed stage)-      ( materializeIncidenceBoundary-          activationCellBoundary-          (activationFaceBasis basis)-          (activationEdgeBasis basis)-      )-  let degreeZeroBoundary :: BoundaryIncidence Int-      degreeZeroBoundary =-        emptyBoundaryIncidenceOf-          (fromIntegral (length (activationVertexBasis basis)))-          0-      boundaryAt :: HomologicalDegree -> BoundaryIncidence Int-      boundaryAt (HomologicalDegree degreeIndex) =-        case degreeIndex of-          0 -> degreeZeroBoundary-          1 -> degreeOneBoundary-          2 -> degreeTwoBoundary-          _ -> emptyBoundaryIncidence+compileActivationComplex stage complexValue = do   chainComplex <-     first (ActivationChainComplexFailed stage)-      (mkFiniteChainComplexChecked (HomologicalDegree 2) boundaryAt)+      (finitePlanarComplex complexValue)   pure     ActivationComplex       { activationComplexStage = stage,-        activationComplexCells = cells,-        activationComplexBasis = basis,+        storedActivationPlanarComplex = complexValue,         activationComplexChainComplex = chainComplex       } -activationBasisFromCells :: Set (ActivationCell label) -> ActivationBasis label-activationBasisFromCells cells =-  foldr insertCell (ActivationBasis [] [] []) (Set.toAscList cells)- where-  insertCell :: ActivationCell label -> ActivationBasis label -> ActivationBasis label-  insertCell cellValue basis =-    case cellValue of-      ActivationVertex _ -> basis{activationVertexBasis = cellValue : activationVertexBasis basis}-      ActivationEdge _ _ -> basis{activationEdgeBasis = cellValue : activationEdgeBasis basis}-      ActivationFace _ _ _ -> basis{activationFaceBasis = cellValue : activationFaceBasis basis}--activationCellBoundary :: Num coefficient => ActivationCell label -> [(coefficient, ActivationCell label)]-activationCellBoundary cellValue =-  case cellValue of-    ActivationVertex _ -> []-    ActivationEdge firstLabel secondLabel ->-      [ (-1, ActivationVertex firstLabel),-        (1, ActivationVertex secondLabel)-      ]-    ActivationFace firstLabel secondLabel thirdLabel ->-      [ (1, ActivationEdge secondLabel thirdLabel),-        (-1, ActivationEdge firstLabel thirdLabel),-        (1, ActivationEdge firstLabel secondLabel)-      ]- buildActivationZigzag ::   Ord label =>   NonEmpty (depth, ActivationComplex depth label) ->@@ -446,19 +390,21 @@ adjacentUnionWitness leftDepth rightDepth leftComplex rightComplex = do   let unionStage =         AdjacentDepthUnion leftDepth rightDepth-      unionCells =-        Set.union-          (activationComplexCells leftComplex)-          (activationComplexCells rightComplex)-  unionComplex <- compileActivationComplex unionStage unionCells+      unionPlanar =+        unionPlanarComplex+          (storedActivationPlanarComplex leftComplex)+          (storedActivationPlanarComplex rightComplex)+  unionComplex <- compileActivationComplex unionStage unionPlanar   leftInclusion <- activationInclusion leftComplex unionComplex   rightInclusion <- activationInclusion rightComplex unionComplex   pure     AdjacentUnionWitness       { adjacentCommonCells =-          Set.intersection-            (activationComplexCells leftComplex)-            (activationComplexCells rightComplex),+          planarComplexCells+            ( intersectPlanarComplex+                (storedActivationPlanarComplex leftComplex)+                (storedActivationPlanarComplex rightComplex)+            ),         adjacentUnionComplex = unionComplex,         adjacentLeftInclusion = leftInclusion,         adjacentRightInclusion = rightInclusion@@ -489,13 +435,13 @@     (activationComplexBasisAt sourceComplex degreeValue)     (activationComplexBasisAt targetComplex degreeValue) -activationBasisAt :: HomologicalDegree -> ActivationBasis label -> [ActivationCell label]-activationBasisAt (HomologicalDegree degreeIndex) basis =+simplexDimensionAt :: HomologicalDegree -> Maybe SimplexDimension+simplexDimensionAt (HomologicalDegree degreeIndex) =   case degreeIndex of-    0 -> activationVertexBasis basis-    1 -> activationEdgeBasis basis-    2 -> activationFaceBasis basis-    _ -> []+    0 -> Just SimplexDimension0+    1 -> Just SimplexDimension1+    2 -> Just SimplexDimension2+    _ -> Nothing  requireStage ::   Vector (ActivationStage depth) ->
test/algebra/Moonlight/Triangulation/PowerDiagramSpec.hs view
@@ -8,6 +8,7 @@ import qualified Data.List.NonEmpty as NonEmpty import qualified Data.Map.Strict as Map import qualified Data.Set as Set+import qualified Data.Vector as Vector import Moonlight.Triangulation   ( ExactPoint   , ExactRational@@ -21,9 +22,15 @@   , PowerSite   , PowerWeight   , PowerWeightError (..)+  , powerAlphaBirthExact+  , RegularEditError (..)+  , RegularEditResult (..)   , RegularSiteDisposition (..)+  , RegularSiteTransition (..)   , RegularEdge+  , RegularTriangulation   , boundedPowerDiagram+  , boundedPowerDiagramFromRegular   , convexPolygon   , convexPolygonPoints   , exactPointCoordinates@@ -52,17 +59,31 @@   , powerSiteWeight   , powerWeight   , powerWeightExact+  , regularAlphaBirths+  , regularAlphaComplex+  , regularAlphaComplexAtBirth+  , regularAlphaFiltration+  , emptyRegularTriangulation+  , insertRegularSite   , regularEdgeDual   , regularEdgeLabels   , regularEdges   , regularFaceDualPoint+  , regularFaceLabels   , regularFaces   , regularNeighbours+  , regularSite+  , regularSiteCount   , regularSiteDisposition+  , regularSites   , regularTriangulation+  , regularTriangulationReceipt   , regularTriangulationEdges   , regularTriangulationFaces+  , regularTriangulationInputSites   , regularTriangulationVisibleSites+  , removeRegularSite+  , reweightRegularSites   , translateExactPoint   ) import Moonlight.Triangulation.Exact@@ -74,6 +95,12 @@   , exactRetainedPolygon   , exactRetainedPolygonPoints   )+import Moonlight.Triangulation.Simplex+  ( planarComplexCells+  , planarEdge+  , planarFace+  , planarVertex+  ) import Support (assertEqual, integerPoint, requireRight)  tests :: IO ()@@ -81,10 +108,20 @@   testEqualWeightsAndCommonShift   testThreeSiteExactPartitionCoverage   testRegularTriangleDualRays+  testRegularAlphaBirths+  testRegularAlphaCommonShift   testRegularBoundedDualSegment   testRegularCoplanarUpperFacet   testRegularCollinearClassification   testRegularLowerDimensionalAndHiddenSites+  testRegularSiteOwnership+  testHiddenInsertionAndExposure+  testRegularInsertionAndRemovalTransitions+  testRegularReweightTransitions+  testTopologyPreservingEdits+  testRegularEditDifferentialLaws+  testRegularEditObstructionsAndIdempotence+  testBoundedPowerFromRegular   testSparsePowerMatchesCompleteOracle   testEqualCoincidentResolution   testDominantCoincidentResolution@@ -96,6 +133,82 @@   testNonFiniteWeight   putStrLn "power diagram: ok" +testRegularAlphaBirths :: IO ()+testRegularAlphaBirths = do+  positive <- admittedWeight 3+  singleton <- admittedSite "only" (Point 0 0) positive+  (singleRegular, _) <-+    requireRight "singleton regular topology" (regularTriangulation (singleton :| []))+  singleAlpha <- requireRight "singleton weighted alpha" (regularAlphaFiltration singleRegular)+  assertEqual+    "positive weight gives signed vertex birth"+    (Just (-3))+    (fmap powerAlphaBirthExact (Map.lookup (planarVertex "only") (regularAlphaBirths singleAlpha)))++  zero <- admittedWeight 0+  firstSite <- admittedSite "first" (Point 0 0) zero+  secondSite <- admittedSite "second" (Point 2 0) zero+  thirdSite <- admittedSite "third" (Point 0 2) zero+  dominated <- admittedSite "dominated" (Point 0 0) =<< admittedWeight (-1)+  (regular, _) <-+    requireRight+      "triangle regular topology for weighted alpha"+      (regularTriangulation (firstSite :| [secondSite, thirdSite, dominated]))+  filtration <- requireRight "triangle weighted alpha" (regularAlphaFiltration regular)+  firstSecond <- requireRight "first-second simplex" (planarEdge "first" "second")+  firstThird <- requireRight "first-third simplex" (planarEdge "first" "third")+  secondThird <- requireRight "second-third simplex" (planarEdge "second" "third")+  face <- requireRight "triangle simplex" (planarFace "first" "second" "third")+  let birthAt simplex = fmap powerAlphaBirthExact (Map.lookup simplex (regularAlphaBirths filtration))+  assertEqual "first leg birth" (Just 1) (birthAt firstSecond)+  assertEqual "second leg birth" (Just 1) (birthAt firstThird)+  assertEqual "hypotenuse birth" (Just 2) (birthAt secondThird)+  assertEqual "face birth" (Just 2) (birthAt face)+  assertEqual+    "coincident subordinate has no alpha simplex"+    False+    (Set.member (planarVertex "dominated") (planarComplexCells (regularAlphaComplex filtration)))+  threshold <- requireSome "edge threshold" (Map.lookup firstSecond (regularAlphaBirths filtration))+  _ <- requireRight "weighted alpha sublevel is closed" (regularAlphaComplexAtBirth threshold filtration)+  pure ()++testRegularAlphaCommonShift :: IO ()+testRegularAlphaCommonShift = do+  zero <- admittedWeight 0+  shifted <- admittedWeight 3+  let labelledPoints =+        ("south-west", Point 0 0)+          :| [ ("south-east", Point 4 0)+             , ("north-east", Point 3 3)+             , ("north-west", Point 0 4)+             ]+  baseSites <- traverse (\(label, point) -> admittedSite label point zero) labelledPoints+  shiftedSites <- traverse (\(label, point) -> admittedSite label point shifted) labelledPoints+  (baseRegular, _) <- requireRight "base regular alpha topology" (regularTriangulation baseSites)+  (shiftedRegular, _) <- requireRight "shifted regular alpha topology" (regularTriangulation shiftedSites)+  shiftedEdit <-+    requireRight+      "common regular weight shift"+      ( reweightRegularSites+          (Map.fromList [(label, shifted) | (label, _) <- NonEmpty.toList labelledPoints])+          baseRegular+      )+  assertEqual+    "common shift reuses the regular topology"+    shiftedRegular+    (regularEditTriangulation shiftedEdit)+  assertEqual "common shift changes no disposition" [] (editTransitions shiftedEdit)+  baseAlpha <- requireRight "base regular alpha" (regularAlphaFiltration baseRegular)+  shiftedAlpha <- requireRight "shifted regular alpha" (regularAlphaFiltration shiftedRegular)+  assertEqual+    "common weight shift preserves the complex"+    (regularAlphaComplex baseAlpha)+    (regularAlphaComplex shiftedAlpha)+  assertEqual+    "common weight shift subtracts from every birth"+    (Map.map ((\birth -> birth - 3) . powerAlphaBirthExact) (regularAlphaBirths baseAlpha))+    (Map.map powerAlphaBirthExact (regularAlphaBirths shiftedAlpha))+ testRegularTriangleDualRays :: IO () testRegularTriangleDualRays = do   zero <- admittedWeight 0@@ -236,6 +349,436 @@     )     (fail "power construction retained more axes than the regular graph") +testRegularSiteOwnership :: IO ()+testRegularSiteOwnership = do+  zero <- admittedWeight 0+  sites <-+    traverse+      (\(label, point) -> admittedSite label point zero)+      ( ("c", Point 0 2)+          :| [("a", Point 0 0), ("b", Point 2 0)]+      )+  (regular, receipt) <-+    requireRight "site-owning regular topology" (regularTriangulation sites)+  assertEqual "regular site count" 3 (regularSiteCount regular)+  assertEqual+    "regular sites are the canonical ascending section"+    ["a", "b", "c"]+    (fmap powerSiteLabel (regularSites regular))+  traverse_+    (\site ->+       assertEqual+         ("regular site lookup for " <> powerSiteLabel site)+         (Just site)+         (regularSite (powerSiteLabel site) regular))+    sites+  case NonEmpty.nonEmpty (regularSites regular) of+    Nothing -> fail "nonempty regular topology lost its canonical site section"+    Just retainedSites -> do+      (reconstructed, _) <-+        requireRight "regular reconstruction from owned sites" (regularTriangulation retainedSites)+      assertEqual "owned sites reconstruct the same semantic value" regular reconstructed+  assertEqual "receipt counts every owned site" 3 (regularTriangulationInputSites receipt)+  assertEqual "empty regular site count" 0 (regularSiteCount emptyRegularTriangulation)+  assertEqual+    "empty regular site section"+    ([] :: [PowerSite String])+    (regularSites emptyRegularTriangulation)++testHiddenInsertionAndExposure :: IO ()+testHiddenInsertionAndExposure = do+  zero <- admittedWeight 0+  hiddenWeight <- admittedWeight (-2)+  first <- admittedSite "first" (Point 0 0) zero+  second <- admittedSite "second" (Point 2 0) zero+  third <- admittedSite "third" (Point 0 2) zero+  hidden <- admittedSite "hidden" (Point 0.5 0.5) hiddenWeight+  (initial, _) <-+    requireRight+      "regular hidden-insertion base"+      (regularTriangulation (first :| [second, third]))+  inserted <-+    requireRight "insert hidden regular site" (insertRegularSite hidden initial)+  assertEqual+    "hidden insertion is successful resident publication"+    (Just hidden)+    (regularSite "hidden" (regularEditTriangulation inserted))+  assertEqual+    "hidden insertion disposition"+    [RegularSiteAppeared "hidden" RegularSiteHidden]+    (editTransitions inserted)+  removed <-+    requireRight+      "remove face-defining site"+      (removeRegularSite "first" (regularEditTriangulation inserted))+  assertEqual+    "removal re-exposes a hidden resident"+    (Just RegularSiteVisible)+    (regularSiteDisposition "hidden" (regularEditTriangulation removed))+  assertEqual+    "hidden exposure receipt"+    [ RegularSiteDisappeared "first" RegularSiteVisible+    , RegularSiteTransitioned "hidden" RegularSiteHidden RegularSiteVisible+    ]+    (editTransitions removed)++testRegularInsertionAndRemovalTransitions :: IO ()+testRegularInsertionAndRemovalTransitions = do+  zero <- admittedWeight 0+  dominantWeight <- admittedWeight 1+  resident <- admittedSite "a-resident" (Point 0 0) zero+  dominant <- admittedSite "z-dominant" (Point 0 0) dominantWeight+  (initial, _) <-+    requireRight "regular insertion base" (regularTriangulation (resident :| []))+  inserted <-+    requireRight "dominant regular insertion" (insertRegularSite dominant initial)+  assertEqual+    "insert changed-site support"+    (Set.singleton "z-dominant")+    (regularEditChangedSites inserted)+  assertEqual+    "dominant insertion transitions"+    [ RegularSiteTransitioned+        "a-resident"+        RegularSiteVisible+        (RegularSiteCoincidentDominatedBy "z-dominant")+    , RegularSiteAppeared "z-dominant" RegularSiteVisible+    ]+    (editTransitions inserted)+  removed <-+    requireRight+      "dominant regular removal"+      (removeRegularSite "z-dominant" (regularEditTriangulation inserted))+  assertEqual+    "remove changed-site support"+    (Set.singleton "z-dominant")+    (regularEditChangedSites removed)+  assertEqual+    "dominant removal re-exposes resident"+    [ RegularSiteTransitioned+        "a-resident"+        (RegularSiteCoincidentDominatedBy "z-dominant")+        RegularSiteVisible+    , RegularSiteDisappeared "z-dominant" RegularSiteVisible+    ]+    (editTransitions removed)+  assertEqual+    "insert then remove returns the canonical site value"+    initial+    (regularEditTriangulation removed)++testRegularReweightTransitions :: IO ()+testRegularReweightTransitions = do+  dominantWeight <- admittedWeight 1+  subordinateWeight <- admittedWeight 0+  promotedWeight <- admittedWeight 2+  first <- admittedSite "a" (Point 0 0) dominantWeight+  second <- admittedSite "b" (Point 0 0) subordinateWeight+  (initial, _) <-+    requireRight "regular reweight base" (regularTriangulation (first :| [second]))+  reweighted <-+    requireRight+      "regular batch reweight"+      (reweightRegularSites (Map.singleton "b" promotedWeight) initial)+  assertEqual+    "reweight changed-site support"+    (Set.singleton "b")+    (regularEditChangedSites reweighted)+  assertEqual+    "reweight transitions preserve identities"+    [ RegularSiteTransitioned+        "a"+        RegularSiteVisible+        (RegularSiteCoincidentDominatedBy "b")+    , RegularSiteTransitioned+        "b"+        (RegularSiteCoincidentDominatedBy "a")+        RegularSiteVisible+    ]+    (editTransitions reweighted)+  let revised = regularEditTriangulation reweighted+  assertEqual+    "reweight preserves the site position"+    (Just (Point 0 0))+    (powerSitePosition <$> regularSite "b" revised)+  assertEqual+    "reweight replaces only the requested weight"+    (Just promotedWeight)+    (powerSiteWeight <$> regularSite "b" revised)++testTopologyPreservingEdits :: IO ()+testTopologyPreservingEdits = do+  domain <- squareDomain+  zero <- admittedWeight 0+  one <- admittedWeight 1+  hiddenWeight <- admittedWeight (-2)+  lowerHiddenWeight <- admittedWeight (-3)++  representative <- admittedSite "a" (Point 0 0) one+  subordinate <- admittedSite "b" (Point 0 0) zero+  (coincidentBase, _) <-+    requireRight "coincident edit base" (regularTriangulation (representative :| []))+  inserted <-+    requireRight+      "canonical coincident insertion"+      (insertRegularSite subordinate coincidentBase)+  assertEqual+    "coincident subordinate insertion transition"+    [RegularSiteAppeared "b" (RegularSiteCoincidentDominatedBy "a")]+    (editTransitions inserted)+  assertRegularReconstructs domain "canonical coincident insertion" (regularEditTriangulation inserted)++  reweighted <-+    requireRight+      "topology-preserving coincident reweight"+      (reweightRegularSites (Map.singleton "b" one) (regularEditTriangulation inserted))+  assertEqual+    "coincident subordinate reweight transition"+    [ RegularSiteTransitioned+        "b"+        (RegularSiteCoincidentDominatedBy "a")+        (RegularSiteCoincidentEquivalentTo "a")+    ]+    (editTransitions reweighted)+  assertRegularReconstructs domain "coincident reweight" (regularEditTriangulation reweighted)++  removedSubordinate <-+    requireRight+      "topology-preserving coincident removal"+      (removeRegularSite "b" (regularEditTriangulation reweighted))+  assertEqual+    "coincident subordinate removal transition"+    [RegularSiteDisappeared "b" (RegularSiteCoincidentEquivalentTo "a")]+    (editTransitions removedSubordinate)+  assertRegularReconstructs domain "coincident removal" (regularEditTriangulation removedSubordinate)++  first <- admittedSite "first" (Point 0 0) zero+  second <- admittedSite "second" (Point 2 0) zero+  third <- admittedSite "third" (Point 0 2) zero+  hidden <- admittedSite "hidden" (Point 0.5 0.5) hiddenWeight+  secondHidden <- admittedSite "hidden-second" (Point 0.75 0.5) hiddenWeight+  (hiddenBase, _) <-+    requireRight+      "hidden edit base"+      (regularTriangulation (first :| [second, third, hidden, secondHidden]))+  lowered <-+    requireRight+      "topology-preserving hidden reweight"+      ( reweightRegularSites+          (Map.fromList [("hidden", lowerHiddenWeight), ("hidden-second", lowerHiddenWeight)])+          hiddenBase+      )+  assertEqual+    "hidden batch changed-site support"+    (Set.fromList ["hidden", "hidden-second"])+    (regularEditChangedSites lowered)+  assertEqual "hidden downward reweight has no visibility transition" [] (editTransitions lowered)+  assertRegularReconstructs domain "hidden downward reweight" (regularEditTriangulation lowered)++  removedHidden <-+    requireRight+      "topology-preserving hidden removal"+      (removeRegularSite "hidden" (regularEditTriangulation lowered))+  assertEqual+    "hidden removal transition"+    [RegularSiteDisappeared "hidden" RegularSiteHidden]+    (editTransitions removedHidden)+  assertRegularReconstructs domain "hidden removal" (regularEditTriangulation removedHidden)++assertRegularReconstructs+  :: (Ord label, Show label)+  => ConvexPolygon+  -> String+  -> RegularTriangulation label+  -> IO ()+assertRegularReconstructs domain name edited =+  case NonEmpty.nonEmpty (regularSites edited) of+    Nothing -> assertEqual (name <> " remains empty") 0 (regularSiteCount edited)+    Just sites -> do+      (rebuilt, _) <-+        requireRight (name <> " reconstruction") (regularTriangulation sites)+      let labels = fmap powerSiteLabel (NonEmpty.toList sites)+      assertEqual+        (name <> " receipt")+        (regularTriangulationReceipt rebuilt)+        (regularTriangulationReceipt edited)+      assertEqual+        (name <> " face labels")+        (fmap regularFaceLabels (regularFaces rebuilt))+        (fmap regularFaceLabels (regularFaces edited))+      assertEqual (name <> " faces") (regularFaces rebuilt) (regularFaces edited)+      assertEqual+        (name <> " edge labels")+        (fmap regularEdgeLabels (regularEdges rebuilt))+        (fmap regularEdgeLabels (regularEdges edited))+      assertEqual (name <> " edges") (regularEdges rebuilt) (regularEdges edited)+      traverse_+        (\label -> do+           assertEqual+             (name <> " disposition " <> show label)+             (regularSiteDisposition label rebuilt)+             (regularSiteDisposition label edited)+           assertEqual+             (name <> " neighbours " <> show label)+             (regularNeighbours label rebuilt)+             (regularNeighbours label edited))+        labels+      rebuiltDiagram <-+        requireRight (name <> " rebuilt clipping") (boundedPowerDiagramFromRegular domain rebuilt)+      editedDiagram <-+        requireRight (name <> " edited clipping") (boundedPowerDiagramFromRegular domain edited)+      assertEqual (name <> " bounded cells") rebuiltDiagram editedDiagram++testRegularEditDifferentialLaws :: IO ()+testRegularEditDifferentialLaws = do+  domain <- squareDomain+  sites <- traverse prepareDifferentialSite [0 .. 47]+  nonEmptySites <-+    maybe (fail "differential regular fixture is empty") pure (NonEmpty.nonEmpty sites)+  baseSites <-+    maybe (fail "differential insertion fixture is empty") pure+      (NonEmpty.nonEmpty (NonEmpty.init nonEmptySites))+  (base, _) <- requireRight "differential insertion base" (regularTriangulation baseSites)+  inserted <-+    requireRight+      "differential local insertion"+      (insertRegularSite (NonEmpty.last nonEmptySites) base)+  assertRegularAlphaClosed+    "differential weighted alpha"+    (regularEditTriangulation inserted)+  assertRegularReconstructs+    domain+    "differential insertion"+    (regularEditTriangulation inserted)+  removed <-+    requireRight+      "differential local removal"+      (removeRegularSite "s23" (regularEditTriangulation inserted))+  assertRegularReconstructs+    domain+    "differential removal"+    (regularEditTriangulation removed)+  raised <- admittedWeight 2+  reweighted <-+    requireRight+      "differential local reweight"+      (reweightRegularSites (Map.singleton "s17" raised) (regularEditTriangulation removed))+  assertRegularReconstructs+    domain+    "differential reweight"+    (regularEditTriangulation reweighted)++assertRegularAlphaClosed+  :: (Ord label, Show label)+  => String+  -> RegularTriangulation label+  -> IO ()+assertRegularAlphaClosed name regular = do+  filtration <- requireRight name (regularAlphaFiltration regular)+  traverse_+    (requireRight (name <> " sublevel") . (`regularAlphaComplexAtBirth` filtration))+    (Map.elems (regularAlphaBirths filtration))++prepareDifferentialSite :: Int -> IO (PowerSite String)+prepareDifferentialSite label = do+  let coordinateX = fromIntegral ((label * 37 + 11) `mod` 97) / 10+      coordinateY = fromIntegral ((label * 61 + 7) `mod` 89) / 10+      weightValue = fromIntegral ((label * 17) `mod` 13 - 6) / 64+  weight <- admittedWeight weightValue+  admittedSite ("s" <> show label) (Point coordinateX coordinateY) weight++testRegularEditObstructionsAndIdempotence :: IO ()+testRegularEditObstructionsAndIdempotence = do+  zero <- admittedWeight 0+  one <- admittedWeight 1+  site <- admittedSite "site" (Point 0 0) zero+  conflicting <- admittedSite "site" (Point 0 0) one+  inserted <-+    requireRight+      "insert into empty regular topology"+      (insertRegularSite site emptyRegularTriangulation)+  repeated <-+    requireRight+      "repeat identical regular insertion"+      (insertRegularSite site (regularEditTriangulation inserted))+  assertEqual+    "identical insertion changes no site"+    Set.empty+    (regularEditChangedSites repeated)+  assertEqual "identical insertion has no transitions" [] (editTransitions repeated)+  case insertRegularSite conflicting (regularEditTriangulation inserted) of+    Left (RegularEditSiteConflict "site" resident requested) -> do+      assertEqual "conflict retains resident" site resident+      assertEqual "conflict retains requested site" conflicting requested+    other -> fail ("expected regular edit site conflict, got " <> show other)+  case reweightRegularSites (Map.singleton "missing" zero) (regularEditTriangulation inserted) of+    Left (RegularEditUnknownSites ("missing" :| [])) -> pure ()+    other -> fail ("expected unknown reweight site, got " <> show other)+  absent <-+    requireRight+      "remove absent regular site"+      (removeRegularSite "missing" (regularEditTriangulation inserted))+  assertEqual+    "absent removal changes no site"+    Set.empty+    (regularEditChangedSites absent)+  assertEqual "absent removal has no transitions" [] (editTransitions absent)+  removed <-+    requireRight+      "remove final regular site"+      (removeRegularSite "site" (regularEditTriangulation inserted))+  assertEqual "final removal returns empty" 0 (regularSiteCount (regularEditTriangulation removed))+  assertEqual+    "final removal transition"+    [RegularSiteDisappeared "site" RegularSiteVisible]+    (editTransitions removed)++testBoundedPowerFromRegular :: IO ()+testBoundedPowerFromRegular = do+  domain <- squareDomain+  fixtures <-+    traverse+      (\(name, submitted) -> (,) name <$> traverse prepareOracleSite submitted)+      [ ( "ordinary"+        , ("a", Point 1 1, 0.25)+            :| [("b", Point 3 1, -0.125), ("c", Point 2 3, 0.5)]+        )+      , ( "lower-dimensional"+        , ("a", Point 0 0, 0)+            :| [("b", Point 2 0, 0), ("c", Point 0 2, 0), ("center", Point 0.5 0.5, -1.5)]+        )+      , ( "hidden"+        , ("a", Point 0 0, 0)+            :| [("b", Point 2 0, 0), ("c", Point 0 2, 0), ("center", Point 0.5 0.5, -2)]+        )+      , ( "coincident"+        , ("a", Point 1 1, 0)+            :| [("b", Point 1 1, 0), ("c", Point 3 1, 0)]+        )+      ]+  traverse_ (assertPreparedPowerMatches domain) fixtures+  (emptyDiagram, emptyReceipt) <-+    requireRight+      "bounded power diagram from empty regular source"+      (boundedPowerDiagramFromRegular domain (emptyRegularTriangulation :: RegularTriangulation String))+  assertEqual "empty regular source has no cell dispositions" [] (powerCellDispositions emptyDiagram)+  assertEqual "empty regular source has no input sites" 0 (powerDiagramInputSites emptyReceipt)++assertPreparedPowerMatches+  :: ConvexPolygon+  -> (String, NonEmpty (PowerSite String))+  -> IO ()+assertPreparedPowerMatches domain (name, sites) = do+  (regular, _) <-+    requireRight (name <> " regular source") (regularTriangulation sites)+  oneShot <-+    requireRight (name <> " one-shot power diagram") (boundedPowerDiagram domain sites)+  fromRegular <-+    requireRight+      (name <> " prepared power diagram")+      (boundedPowerDiagramFromRegular domain regular)+  assertEqual (name <> " prepared clipping") oneShot fromRegular+ testRegularCollinearClassification :: IO () testRegularCollinearClassification = do   sites <- traverse prepareCollinearSite (0 :| [1 .. 8])@@ -437,9 +980,8 @@   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+  let layer = powerDiagramPlanarLayer diagram+  assertEqual "derived layer contains both cells" [Just "left", Just "right"] (Map.keys (planarLayerRegions layer))  testThreeSiteExactPartitionCoverage :: IO () testThreeSiteExactPartitionCoverage = do@@ -576,8 +1118,8 @@   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))+  let layer = powerDiagramPlanarLayer diagram+  assertEqual "derived layer omits the one-dimensional cell" [Just "left", Just "right"] (Map.keys (planarLayerRegions layer))   assertPermutationInvariant "one-dimensional bounded power cell" domain sites diagram  testDistinctEmptyCell :: IO ()@@ -644,6 +1186,12 @@     Left (PowerWeightNonFinite _) -> pure ()     other -> fail ("non-finite power weight: expected typed refusal, got " <> show other) +editTransitions+  :: RegularEditResult label+  -> [RegularSiteTransition label]+editTransitions =+  Vector.toList . regularEditTransitions+ squareDomain :: IO ConvexPolygon squareDomain =   requireRight@@ -674,11 +1222,11 @@ 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)+requireSome :: String -> Maybe value -> IO value+requireSome label value =+  case value of+    Just present -> pure present+    Nothing -> fail (label <> ": missing value")  assertPublishedVerticesWin   :: NonEmpty (PowerSite String)
test/algebra/Moonlight/Triangulation/ValuationSpec.hs view
@@ -9,7 +9,9 @@   , rectangleRegion   ) import Moonlight.Triangulation.Internal.ExactRational-  ( ExactRational )+  ( ExactRational+  , exactRational+  ) import Moonlight.Triangulation.Overlay   ( overlayClosedIntersection   , overlayClosedUnion@@ -20,9 +22,11 @@   ( PlanarRegion   , emptyPlanarRegion   , planarRegion+  , planarRegionComponents   ) import Moonlight.Triangulation.Valuation   ( CertifiedInterval (..)+  , ExactPlanarMoments   , ExactLengthTerm   , PlanarValuations   , ValuationError (ValuationCellSetNotPureRegion)@@ -32,10 +36,19 @@   , exactLengthBounds   , exactLengthExpression   , exactLengthTerms+  , exactPlanarFirstX+  , exactPlanarFirstY+  , exactPlanarMeasure+  , exactPlanarSecondXX+  , exactPlanarSecondXY+  , exactPlanarSecondYY   , eulerCharacteristicValue   , lengthCoefficient+  , polygonComponentArea+  , polygonComponentMoments   , regionPerimeter   , regionValuations+  , scaleExactPlanarMoments   , squaredLength   , valuationArea   , valuationEuler@@ -46,10 +59,61 @@ tests :: IO () tests = do   testRegionGoldenValues+  testExactPlanarMoments   testClosedCellInclusionExclusion   testDimensionalCellFixtures   testMetricInvariance   putStrLn "valuation: ok"++testExactPlanarMoments :: IO ()+testExactPlanarMoments = do+  unit <- rectangleComponent 0 0 1 1+  oneHalfValue <- exactValue 1 2+  oneThird <- exactValue 1 3+  oneQuarter <- exactValue 1 4+  let unitMoments = polygonComponentMoments unit+  assertEqual+    "area is the measure projection"+    (exactPlanarMeasure unitMoments)+    (exactAreaValue (polygonComponentArea unit))+  assertEqual+    "unit-square moments"+    (1, oneHalfValue, oneHalfValue, oneThird, oneQuarter, oneThird)+    (momentDigest unitMoments)++  translated <- rectangleComponent 2 (-1) 4 2+  assertEqual+    "translated-rectangle moments"+    (6, 18, 3, 56, 9, 6)+    (momentDigest (polygonComponentMoments translated))++  annulus <- annulusRegion (0, 0, 3, 3) (1, 1, 2, 2)+  seventyFourThirds <- exactValue 74 3+  let annulusMoments = foldMap polygonComponentMoments (planarRegionComponents annulus)+  assertEqual+    "annulus moments respect clockwise holes"+    (8, 12, 12, seventyFourThirds, 18, seventyFourThirds)+    (momentDigest annulusMoments)+  assertEqual+    "moment monoid and scalar action"+    (momentDigest (unitMoments <> unitMoments))+    (momentDigest (scaleExactPlanarMoments 2 unitMoments))++momentDigest+  :: ExactPlanarMoments+  -> (ExactRational, ExactRational, ExactRational, ExactRational, ExactRational, ExactRational)+momentDigest moments =+  ( exactPlanarMeasure moments+  , exactPlanarFirstX moments+  , exactPlanarFirstY moments+  , exactPlanarSecondXX moments+  , exactPlanarSecondXY moments+  , exactPlanarSecondYY moments+  )++exactValue :: Integer -> Integer -> IO ExactRational+exactValue numerator denominator =+  requireRight "exact valuation fixture" (exactRational numerator denominator)  testRegionGoldenValues :: IO () testRegionGoldenValues = do
test/cell-complex/Main.hs view
@@ -7,6 +7,7 @@ import Data.Map.Strict qualified as Map import Data.Maybe (mapMaybe) import Data.Ratio ((%))+import Data.Set qualified as Set import Data.Vector qualified as Vector import Moonlight.Homology.Boundary   ( BoundaryEntry@@ -81,7 +82,14 @@   , exactRationalFromDyadic   , exactRationalFromDyadicRatio   )-import Moonlight.Triangulation.Math (inDiametralCircle)+import Moonlight.Triangulation.Internal.Predicates (inDiametralCircle)+import Moonlight.Triangulation.LabelledComplex (filteredPlanarComplex)+import Moonlight.Triangulation.Simplex+  ( planarComplex+  , planarEdge+  , planarFace+  , planarVertex+  ) import Moonlight.Triangulation.Types   ( DelaunayTriangulation   , Point (..)@@ -106,6 +114,7 @@       testCase "agrees with the exact diametral predicate" diametralPredicateMatchesExactSign,       testCase "keeps every boundary cell no later than its coface" alphaBirthsAreFaceMonotone,       testCase "finds the square alpha hole from radius one to radius two" squareAlphaPersistence,+      testCase "lowers a labelled planar filtration through the same Homology owner" labelledPlanarPersistence,       testCase "keeps point and collinear alpha filtrations total" degenerateSupportFiltrations     ] @@ -289,6 +298,52 @@           & filter ((== HomologicalDegree 1) . persistenceDegree)           & mapMaybe positiveBirthAndDeath   positiveDegreeOnePairs @?= [((1, 1), (2, 1))]++labelledPlanarPersistence :: Assertion+labelledPlanarPersistence = do+  edgeAB <- requireRight "labelled edge ab" (planarEdge 'a' 'b')+  edgeAC <- requireRight "labelled edge ac" (planarEdge 'a' 'c')+  edgeBC <- requireRight "labelled edge bc" (planarEdge 'b' 'c')+  faceABC <- requireRight "labelled face abc" (planarFace 'a' 'b' 'c')+  complexValue <-+    requireRight+      "labelled triangle complex"+      ( planarComplex+          ( Set.fromList+              [ planarVertex 'a'+              , planarVertex 'b'+              , planarVertex 'c'+              , edgeAB+              , edgeAC+              , edgeBC+              , faceABC+              ]+          )+      )+  filtered <-+    requireRight+      "labelled triangle filtration"+      ( filteredPlanarComplex+          complexValue+          ( Map.fromList+              [ (planarVertex 'a', 0 :: Int)+              , (planarVertex 'b', 0)+              , (planarVertex 'c', 0)+              , (edgeAB, 1)+              , (edgeAC, 1)+              , (edgeBC, 1)+              , (faceABC, 2)+              ]+          )+      )+  pairs <- requireRight "labelled triangle persistence" (mod2PersistentPairs filtered)+  let oneCycles =+        [ pair+        | pair <- pairs+        , persistenceDegree pair == HomologicalDegree 1+        ]+  fmap (\pair -> (persistenceBirth pair, persistenceDeath pair)) oneCycles+    @?= [(1, Just 2)]  degenerateSupportFiltrations :: Assertion degenerateSupportFiltrations =
test/native/Moonlight/Triangulation/ExactEmbeddingSpec.hs view
@@ -54,10 +54,10 @@   , certifyLocalEmbedding   , residualUndischargedObligations   )+import Moonlight.Triangulation.Internal.Predicates (segmentRelation) import Moonlight.Triangulation.Math   ( SegmentRelation (..)   , allSegmentRelations-  , segmentRelation   ) import Moonlight.Triangulation.Types   ( CoordinateError (..)
test/native/Moonlight/Triangulation/FilteredPredicateOptimizationSpec.hs view
@@ -18,6 +18,7 @@   , orient2d   , orientDetApprox   )+import Moonlight.Triangulation.Internal.Types (QueryPoint (..)) import Moonlight.Triangulation.Types (Point (..)) import System.Mem (performGC) import qualified Data.Vector as V@@ -38,13 +39,13 @@       orientCertified =         V.generate           count-          (\index -> Point (fromIntegral index * bigScale) (if even index then 0 else smallScale))+          (\index -> QueryPoint (Point (fromIntegral index * bigScale) (if even index then 0 else smallScale)))       orientDegenerate =         V.generate           count-          (\index -> Point (fromIntegral index * bigScale) (fromIntegral index * smallScale))-      zigzag = V.generate count (\index -> Point (fromIntegral index) (if even index then 0 else 1))-      collinear = V.generate count (\index -> Point (fromIntegral index) (fromIntegral index))+          (\index -> QueryPoint (Point (fromIntegral index * bigScale) (fromIntegral index * smallScale)))+      zigzag = V.generate count (\index -> QueryPoint (Point (fromIntegral index) (if even index then 0 else 1)))+      collinear = V.generate count (\index -> QueryPoint (Point (fromIntegral index) (fromIntegral index)))       orientationIndices = U.enumFromN 0 (max 0 (count - 2))       circleIndices = U.enumFromN 0 (max 0 (count - 3))   _ <- evaluate (force orientCertified)@@ -107,9 +108,9 @@ allocationBeyondApproximation measured support = measured - min measured support  sumTripleRelations-  :: (Point -> Point -> Point -> Ordering)+  :: (QueryPoint -> QueryPoint -> QueryPoint -> Ordering)   -> U.Vector Int-  -> V.Vector (Point)+  -> V.Vector QueryPoint   -> Int sumTripleRelations relation indices points =   U.foldl'@@ -127,9 +128,9 @@ {-# INLINE sumTripleRelations #-}  sumQuadRelations-  :: (Point -> Point -> Point -> Point -> Ordering)+  :: (QueryPoint -> QueryPoint -> QueryPoint -> QueryPoint -> Ordering)   -> U.Vector Int-  -> V.Vector (Point)+  -> V.Vector QueryPoint   -> Int sumQuadRelations relation indices points =   U.foldl'@@ -147,10 +148,12 @@     indices {-# INLINE sumQuadRelations #-} -approximateOrientation :: Point -> Point -> Point -> Ordering-approximateOrientation a b c = compare (orientDetApprox a b c) 0+approximateOrientation :: QueryPoint -> QueryPoint -> QueryPoint -> Ordering+approximateOrientation (QueryPoint a) (QueryPoint b) (QueryPoint c) =+  compare (orientDetApprox a b c) 0 {-# INLINE approximateOrientation #-} -approximateInCircle :: Point -> Point -> Point -> Point -> Ordering-approximateInCircle a b c d = compare (inCircleDetApprox a b c d) 0+approximateInCircle :: QueryPoint -> QueryPoint -> QueryPoint -> QueryPoint -> Ordering+approximateInCircle (QueryPoint a) (QueryPoint b) (QueryPoint c) (QueryPoint d) =+  compare (inCircleDetApprox a b c d) 0 {-# INLINE approximateInCircle #-}
test/native/Moonlight/Triangulation/NativeSpec.hs view
@@ -68,6 +68,7 @@   , fromVector   , toVector   )+import Moonlight.Triangulation.Internal.Predicates qualified as Admitted import Moonlight.Triangulation.Internal.PointIndex   ( MutablePointIndexUpdate (..)   , lookupMutablePoint@@ -93,6 +94,7 @@   testWideDistanceNearestNeighbor   testIncrementalLocationDescent   testPersistentInsertionReusesFrozenLocation+  testVertexSeededSessionInsertion   testCircleSweepBulkLoad   testMixedEditSession   testBulkRemovalAgreement@@ -286,14 +288,11 @@           unless (not (null (validateTopologyClosure admitted IntSet.empty corrupted))) $             fail "selected validation missed a locally corrupt face closure" --- Twin half-edges are arithmetic complements, so a crossing's undirected--- identity is its handle halved. Comparing cardinality alone would admit a--- traversal that returned the right number of the wrong crossings.-crossingIdentity :: Intersection -> Either Int Int-crossingIdentity = \case-  EdgeIntersection edge -> Right (fromIntegral (unDirectedEdgeId edge) `quot` 2)-  EdgeOverlap edge -> Right (fromIntegral (unDirectedEdgeId edge) `quot` 2)-  VertexIntersection vertex -> Left (fromIntegral (unVertexId vertex))+reverseTraversalEvent :: Intersection -> Intersection+reverseTraversalEvent = \case+  EdgeIntersection edge -> EdgeIntersection (reverseEdge edge)+  EdgeOverlap edge -> EdgeOverlap (reverseEdge edge)+  VertexIntersection vertex -> VertexIntersection vertex  -- | What @refinementComplete@ claims is that the quality worklist drained. The -- assertable content of that claim is a fixpoint: a second pass under the same@@ -526,6 +525,80 @@   assertValid (label <> " result") (insertionTriangulation result)   pure located +-- A vertex hint restricts to one incident face and then relinquishes+-- authority to the exact mutable walk. Valid, stale, occupied, and face-less+-- local sections must therefore glue to the same published result as the+-- unhinted entrance.+testVertexSeededSessionInsertion :: IO ()+testVertexSeededSessionInsertion = do+  area <- sampleMesh "vertex-seeded area" [Point 0 0, Point 4 0, Point 0 4]+  seed <- vertexAt "vertex-seeded area seed" area (Point 0 0)+  assertVertexSeededInsertionAgreement+    "vertex-seeded interior insertion"+    area+    seed+    (SampleVertex (Point 1 1) 40)+  assertVertexSeededInsertionAgreement+    "vertex-seeded invalid hint"+    area+    (VertexId maxBound)+    (SampleVertex (Point 3 3) 41)+  assertVertexSeededInsertionAgreement+    "vertex-seeded duplicate"+    area+    seed+    (SampleVertex (Point 4 0) 42)++  line <- sampleMesh "vertex-seeded line" [Point 0 0, Point 2 0, Point 4 0]+  lineSeed <- vertexAt "vertex-seeded line seed" line (Point 2 0)+  assertVertexSeededInsertionAgreement+    "vertex-seeded face-less fallback"+    line+    lineSeed+    (SampleVertex (Point 3 0) 43)+ where+  sampleMesh+    :: String+    -> [Point]+    -> IO (Triangulation 'Unconstrained SampleVertex () () ())+  sampleMesh label points = do+    built <-+      requireRight label $+        delaunay+          unitElementDefaults+          (V.imap (\index point -> SampleVertex point index) (V.fromList points))+    pure (buildTriangulation built)++  vertexAt+    :: String+    -> Triangulation 'Unconstrained SampleVertex () () ()+    -> Point+    -> IO VertexId+  vertexAt label triangulation point = do+    query <- requireQueryPoint label point+    case locatePoint triangulation query of+      OnVertex vertex -> pure vertex+      location -> fail (label <> " did not locate a vertex: " <> show location)++assertVertexSeededInsertionAgreement+  :: String+  -> Triangulation 'Unconstrained SampleVertex () () ()+  -> VertexId+  -> SampleVertex+  -> IO ()+assertVertexSeededInsertionAgreement label source seed payload = do+  ((referenceVertex, referenceDisposition), reference, _) <-+    requireRight (label <> " unhinted") $+      withSession source 1 (insertVertexAt (samplePosition payload) payload)+  ((hintedVertex, hintedDisposition), hinted, _) <-+    requireRight (label <> " hinted") $+      withSession source 1 (insertVertexAtNearVertex seed (samplePosition payload) payload)+  assertEqual (label <> " vertex") referenceVertex hintedVertex+  assertEqual (label <> " disposition") referenceDisposition hintedDisposition+  assertEqual (label <> " topology") (canonicalEdges reference) (canonicalEdges hinted)+  assertEqual (label <> " payload") payload (vertexData hinted hintedVertex)+  assertValid label hinted+ -- Circle sweep must be a construction schedule, not a second topology. It is -- compared against the arrival-order session kernel on the same exact inputs. -- | One session, both verbs. The reason the two published sessions became one:@@ -776,32 +849,39 @@  testPredicates :: IO () testPredicates = do-  let a, b, c :: Point-      a = Point 0 0-      b = Point 1 0-      c = Point 0 1+  let rawA, rawB, rawC :: Point+      rawA = Point 0 0+      rawB = Point 1 0+      rawC = Point 0 1+  a <- requireRight "predicate point a" (mkQueryPoint rawA)+  b <- requireRight "predicate point b" (mkQueryPoint rawB)+  c <- requireRight "predicate point c" (mkQueryPoint rawC)+  midpointQuery <- requireRight "predicate midpoint" (mkQueryPoint (Point 0.5 0))+  inside <- requireRight "incircle inside point" (mkQueryPoint (Point 0.25 0.25))+  boundary <- requireRight "incircle boundary point" (mkQueryPoint (Point 1 1))+  outside <- requireRight "incircle outside point" (mkQueryPoint (Point 2 2))   assertEqual "orientation left" GT (orient2d a b c)   assertEqual "orientation right" LT (orient2d b a c)-  assertEqual "orientation collinear" EQ (orient2d a b (Point 0.5 0))-  assertEqual "incircle inside" GT (inCircle a b c (Point 0.25 0.25))-  assertEqual "incircle boundary" EQ (inCircle a b c (Point 1 1))-  assertEqual "incircle outside" LT (inCircle a b c (Point 2 2))+  assertEqual "orientation collinear" EQ (orient2d a b midpointQuery)+  assertEqual "incircle inside" GT (inCircle a b c inside)+  assertEqual "incircle boundary" EQ (inCircle a b c boundary)+  assertEqual "incircle outside" LT (inCircle a b c outside)   assertEqual "underflow mitigation" (Point 0 1) (mitigateUnderflow (Point 1.0e-44 1 :: Point))   _ <- requireRight "point validation" (validatePoint Nothing (Point 0 1 :: Point))   let large = encodeFloat 1 180 :: Double       ulp = encodeFloat 1 128 :: Double+  largeA <- requireRight "large predicate point a" (mkQueryPoint (Point large large))+  largeB <- requireRight "large predicate point b" (mkQueryPoint (Point (large + ulp) large))+  largeC <- requireRight "large predicate point c" (mkQueryPoint (Point large (large + ulp)))+  largeD <- requireRight "large predicate point d" (mkQueryPoint (Point (large + ulp) (large + ulp)))   assertEqual     "large exact orientation"     GT-    (orient2d (Point large large) (Point (large + ulp) large) (Point large (large + ulp)))+    (orient2d largeA largeB largeC)   assertEqual     "large exact cocircularity"     EQ-    (inCircle-      (Point large large)-      (Point (large + ulp) large)-      (Point large (large + ulp))-      (Point (large + ulp) (large + ulp)))+    (inCircle largeA largeB largeC largeD)  testHandlesAndFiniteDcel :: IO () testHandlesAndFiniteDcel = do@@ -893,11 +973,44 @@   assertEqual "insertion stores a canonical zero"     (0, 0) (bits (vertexPoint (insertionTriangulation incremental) (VertexId 0))) -  collinear <- requirePointBuild "collinear" [Point 0 4, Point 0 0, Point 0 3, Point 0 2, Point 0 1]-  let line = buildTriangulation collinear-  assertEqual "line edge count" 4 (numUndirectedEdges line)-  assertEqual "line face count" 0 (numInnerFaces line)-  assertValid "collinear terminal splits" line+  traverse_+    assertBulkLine+    [ ("vertical", [Point 0 4, Point 0 0, Point 0 3, Point 0 2, Point 0 1])+    , ("horizontal reversed", reverse [Point x 7 | x <- [0 .. 8]])+    , ("oblique scrambled", [Point 3 8, Point (-2) (-2), Point 1 4, Point (-1) 0, Point 2 6, Point 0 2])+    , ("duplicate-bearing", [Point 2 5, Point 0 1, Point 1 3, Point 2 5, Point (-0.0) 1, Point 3 7])+    ]+  areaBase <- requirePointBuild "resident area before collinear batch" [Point 0 0, Point 4 0, Point 0 4]+  areaExtended <-+    requireRight+      "collinear batch into resident area"+      (insertMany (buildTriangulation areaBase) (V.fromList [Point 1 1, Point 2 2, Point 3 3]))+  assertEqual "resident area collinear batch count" 6 (numVertices (buildTriangulation areaExtended))+  assertValid "resident area collinear batch" (buildTriangulation areaExtended)+ where+  assertBulkLine (label, points) = do+    annotated <- requirePointBuild (label <> " annotated line") points+    geometry <- requireRight (label <> " geometry line") (delaunayGeometry (V.fromList points))+    batched <-+      requireRight+        (label <> " empty-base batch line")+        (insertMany (empty unitElementDefaults) (V.fromList points))+    let line = buildTriangulation annotated+        batchLine = buildTriangulation batched+        ascending = Set.toAscList (Set.fromList points)+        expectedEdges = Set.fromList (zip ascending (drop 1 ascending))+        uniqueCount = length ascending+        stats = buildStats annotated+    assertEqual (label <> " annotated edge set") expectedEdges (canonicalEdges line)+    assertEqual (label <> " geometry edge set") expectedEdges (canonicalEdges geometry)+    assertEqual (label <> " empty-base batch edge set") expectedEdges (canonicalEdges batchLine)+    assertEqual (label <> " edge count") (max 0 (uniqueCount - 1)) (numUndirectedEdges line)+    assertEqual (label <> " face count") 0 (numInnerFaces line)+    assertEqual (label <> " spatial seed count") uniqueCount (statSpatialSeedPoints stats)+    assertEqual (label <> " aggregate extension count") (max 0 (uniqueCount - 2)) (statLineExtensions stats)+    assertValid (label <> " annotated line") line+    assertValid (label <> " geometry line") geometry+    assertValid (label <> " empty-base batch line") batchLine  testPersistentLocalUpdates :: IO () testPersistentLocalUpdates = do@@ -1117,7 +1230,6 @@  where   sumPayloadPages (vertexPages, directedPages, undirectedPages, facePages) =     vertexPages + directedPages + undirectedPages + facePages- testPayloadTraversals :: IO () testPayloadTraversals = do   let defaults = ElementDefaults (7 :: Int) ("new-undirected" :: String) ("new-face" :: String)@@ -1228,7 +1340,7 @@       predicted a b c d = compare (liftedOrientation (lifted a) (lifted b) (lifted c) (lifted d)) 0       measured :: Point -> Point -> Point -> Point -> Ordering       measured (Point ax ay) (Point bx by) (Point cx cy) (Point dx dy) =-        inCircleCoordinates ax ay bx by cx cy dx dy+        Admitted.inCircleCoordinates ax ay bx by cx cy dx dy       quadruples :: [Point] -> [(Point, Point, Point, Point)]       quadruples (a : b : c : d : rest) = (a, b, c, d) : quadruples rest       quadruples _ = []@@ -1507,7 +1619,9 @@       (voronoiFaceSite (voronoiIncidentFace triangulation edge))     case voronoiEdgeGeometry triangulation edge of       Nothing -> fail "valid dual edge has no geometry"-      Just _ -> pure ()+      fixedGeometry@(Just _) -> do+        owning <- requireJust "dynamic Voronoi edge" (VoronoiDynamic.directedVoronoiEdgeHandle triangulation edge)+        assertEqual "fixed/owning dual geometry" fixedGeometry (VoronoiDynamic.voronoiEdgeGeometryH owning)   case directedVoronoiEdges triangulation of     [] -> fail "Voronoi test produced no directed dual edge"     first : _ -> do@@ -2884,7 +2998,7 @@                           case filter (/= fromVertex) (filter (/= toVertex) verticesInFace) of                             [vertex] -> Just (vertexPoint source vertex)                             _ -> Nothing-         in maybe False (inDiametralCircle fromPoint toPoint) oppositePoint+         in maybe False (Admitted.inDiametralCircle fromPoint toPoint) oppositePoint   unless (any encroachedHull hullEdges) $     fail "encroached hull fixture did not produce an encroached hull pair"   refined <-@@ -3966,9 +4080,23 @@       backward = lineIntersections triangulation forwardEnd forwardStart   when (null forward) $ fail "ordered line traversal crossed nothing"   assertEqual-    "reversing the segment reverses the crossings"-    (map crossingIdentity forward)-    (reverse (map crossingIdentity backward))+    "reversing the segment reverses crossing order and orientation"+    (map reverseTraversalEvent forward)+    (reverse backward)+  interiorEnd <- requireQueryPoint "outside traversal interior end" (Point 0.25 0.5)+  let outsideToInterior = lineIntersections triangulation forwardStart interiorEnd+      interiorToOutside = lineIntersections triangulation interiorEnd forwardStart+  when (null outsideToInterior) $ fail "outside-to-interior traversal crossed nothing"+  assertEqual+    "endpoint-directed outside traversal preserves order and orientation"+    (map reverseTraversalEvent outsideToInterior)+    (reverse interiorToOutside)+  missFrom <- requireQueryPoint "outside traversal miss from" (Point (-4) 4)+  missTo <- requireQueryPoint "outside traversal miss to" (Point 4 4)+  assertEqual+    "outside segment missing the hull reports no intersections"+    []+    (lineIntersections triangulation missFrom missTo)   circleEdges <- Set.fromList <$> requireRight "circle edge query" (edgesInCircle triangulation (Point 0 0) 4)   let bruteCircle = Set.fromList         [ edge
test/public-components/Main.hs view
@@ -18,7 +18,7 @@   ) import Moonlight.Triangulation.PointLocation (locatePoint) import Moonlight.Triangulation.Session-  ( insertVertexAt+  ( insertVertexAtNearVertex   , removeAt   , withSession   )@@ -29,7 +29,7 @@   , Point (..)   , PointValidationError   )-import Moonlight.Triangulation.Math (mkQueryPoint)+import Moonlight.Triangulation.Math (mkQueryPoint, orient2d) import Moonlight.Triangulation.Validation (validateTriangulation) import System.Exit (die) @@ -37,6 +37,7 @@   = PublicBuildFailure !BuildError   | PublicQueryFailure !PointValidationError   | PublicRectangleFailure !RectangleMetricError+  | PublicSeedVertexMissing   deriving stock (Show)  data PublicComponentSummary = PublicComponentSummary@@ -45,6 +46,7 @@   , scopedVertexPoints :: !Int   , rectangleVertices :: !Int   , locatedResidentVertex :: !Bool+  , admittedPredicateCorrect :: !Bool   , sessionInsertedVertex :: !Bool   , sessionRemovedVertex :: !Bool   , finalMeshValid :: !Bool@@ -71,14 +73,19 @@               ]           )       )+  query <- first PublicQueryFailure (mkQueryPoint (Point (-1) (-1)))+  seed <- case locatePoint square query of+    OnVertex vertex -> Right vertex+    _ -> Left PublicSeedVertexMissing   ((disposition, removed), restored, _) <-     first PublicBuildFailure       ( withSession square 1 $ do-          (_, inserted) <- insertVertexAt (Point 0 0) ()+          (_, inserted) <- insertVertexAtNearVertex seed (Point 0 0) ()           removal <- removeAt (Point 0 0)           pure (inserted, removal)       )-  query <- first PublicQueryFailure (mkQueryPoint (Point (-1) (-1)))+  queryRight <- first PublicQueryFailure (mkQueryPoint (Point 1 (-1)))+  queryTop <- first PublicQueryFailure (mkQueryPoint (Point (-1) 1))   inside <-     first PublicRectangleFailure       (verticesInRectangle restored (Point (-2) (-2)) (Point 2 2))@@ -93,6 +100,7 @@       , locatedResidentVertex = case locatePoint restored query of           OnVertex _ -> True           _ -> False+      , admittedPredicateCorrect = orient2d query queryRight queryTop == GT       , sessionInsertedVertex = disposition == Inserted       , sessionRemovedVertex = maybe False (const True) removed       , finalMeshValid = null (validateTriangulation restored)@@ -116,6 +124,7 @@       , scopedVertexPoints = 4       , rectangleVertices = 4       , locatedResidentVertex = True+      , admittedPredicateCorrect = True       , sessionInsertedVertex = True       , sessionRemovedVertex = True       , finalMeshValid = True