packages feed

moonlight-triangulation-1.2.0.0: src-dcel/Moonlight/Triangulation/Internal/Region/Publication.hs

-- | Trusted exact-coordinate publication from the one resident DCEL boundary
-- owner. The callback-bearing entrances are internal because only a sealed
-- downstream carrier may prove that its exact coordinate section belongs to
-- the supplied topology.
module Moonlight.Triangulation.Internal.Region.Publication
  ( labelledPlanarLayer
  , labelledPlanarLayerFromExactCoordinates
  , planarLayerFromAdmittedComponents
  , polygonComponentFromBoundaryCoordinates
  ) where

import Data.Bifunctor (first)
import Data.List (sort)
import Data.List.NonEmpty (NonEmpty)
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.Map.Strict as Map
import Moonlight.Triangulation.Dcel (vertexPoint)
import Moonlight.Triangulation.Exact
  ( ExactPoint
  , exactOnClosedSegment
  , exactOrient2d
  , exactPointFromPoint
  )
import Moonlight.Triangulation.FloodFillIterator
  ( BoundaryLoop
  , RegionBoundary
  , boundaryLoopVertices
  , componentBoundary
  , faceComponents
  , labelledRegionBoundaries
  , regionBoundaryHoleLoops
  , regionBoundaryOuterLoop
  )
import Moonlight.Triangulation.Handles.HandleDefs (FaceId, VertexId)
import Moonlight.Triangulation.Internal.BoundaryCycle
  ( rotateCycleLeast
  , simplifyBoundaryCycle
  )
import Moonlight.Triangulation.Internal.Region.Types
  ( ExactLoop (..)
  , PlanarLayer (..)
  , PlanarRegion (..)
  , PolygonComponent (..)
  , RegionPublicationError (..)
  , RegionValidationError (..)
  )
import Moonlight.Triangulation.Internal.Representation (Triangulation)

-- | Publish bounded resident face labels through the existing component and
-- boundary owners, then lift only traced boundary coordinates exactly.
labelledPlanarLayer
  :: Ord label
  => label
  -> Triangulation mode vertex directed undirected face
  -> (FaceId -> label)
  -> Either RegionPublicationError (PlanarLayer label)
labelledPlanarLayer outside triangulation labelFace = do
  labelledBoundaries <-
    first RegionBoundaryObstruction
      (labelledRegionBoundaries triangulation labelFace)
  labelledComponents <-
    traverse
      (\(label, boundary) ->
         (label,) <$> polygonComponentFromBoundaryCoordinates exactPointAt boundary)
      [ pair
      | pair@(label, _) <- labelledBoundaries
      , label /= outside
      ]
  pure (planarLayerFromAdmittedComponents outside labelledComponents)
 where
  exactPointAt vertex =
    first (RegionCoordinateObstruction vertex)
      (exactPointFromPoint (vertexPoint triangulation vertex))

-- | Publish bounded face labels using the exact-coordinate section belonging
-- to the resident carrier. Topology has already been proved by
-- 'labelledRegionBoundaries'; this path performs exact simplification but does
-- not send derived loops back through authoring event sweeps.
labelledPlanarLayerFromExactCoordinates
  :: Ord label
  => label
  -> Triangulation mode vertex directed undirected face
  -> (VertexId -> Either RegionPublicationError ExactPoint)
  -> (FaceId -> Either RegionPublicationError label)
  -> Either RegionPublicationError (PlanarLayer label)
labelledPlanarLayerFromExactCoordinates outside triangulation exactPointAt labelFace = do
  labelledComponents <-
    traverse
      (\(labelResult, component) -> do
         label <- labelResult
         boundary <-
           first RegionBoundaryObstruction
             (componentBoundary triangulation component)
         (label,) <$> polygonComponentFromBoundaryCoordinates exactPointAt boundary)
      [ pair
      | pair@(labelResult, _) <- faceComponents triangulation labelFace
      , labelResult /= Right outside
      ]
  pure (planarLayerFromAdmittedComponents outside labelledComponents)

-- | Glue already-admitted, pairwise interior-disjoint components by label.
-- Both DCEL publication and exact overlay cells reach this point only after
-- their topology owner has proved those obligations.
planarLayerFromAdmittedComponents
  :: Ord label
  => label
  -> [(label, PolygonComponent)]
  -> PlanarLayer label
planarLayerFromAdmittedComponents outside labelledComponents =
  PlanarLayer
    outside
    ( Map.map
        (PlanarRegion . sort)
        ( Map.fromListWith (<>)
            [(label, [component]) | (label, component) <- labelledComponents]
        )
    )

-- | Convert one already-traced resident component boundary against the exact
-- coordinate carrier admitted for that same resident topology.
polygonComponentFromBoundaryCoordinates
  :: (VertexId -> Either RegionPublicationError ExactPoint)
  -> RegionBoundary
  -> Either RegionPublicationError PolygonComponent
polygonComponentFromBoundaryCoordinates exactPointAt boundary = do
  outer <- convertLoop (regionBoundaryOuterLoop boundary)
  holes <- traverse convertLoop (regionBoundaryHoleLoops boundary)
  pure (PolygonComponent outer (sort holes))
 where
  convertLoop :: BoundaryLoop -> Either RegionPublicationError ExactLoop
  convertLoop loop = do
    points <- traverse exactPointAt (boundaryLoopVertices loop)
    admittedDerivedLoop points

-- | Boundary descent already proves simplicity, winding, and component
-- compatibility. Exact simplification remains necessary because the exact
-- carrier may expose a collinearity that the embedded boundary retained.
admittedDerivedLoop
  :: NonEmpty ExactPoint
  -> Either RegionPublicationError ExactLoop
admittedDerivedLoop points = do
  (_, simplified) <-
    first RegionValidationObstruction
      ( simplifyBoundaryCycle
          RegionLoopDegenerate
          (\previous current next ->
             exactOrient2d previous current next == EQ
               && exactOnClosedSegment previous next current)
          exactOrient2d
          id
          (NonEmpty.toList points)
      )
  pure (ExactLoop (rotateCycleLeast simplified))