packages feed

moonlight-triangulation-0.1.0.0: src-build/Moonlight/Triangulation/Internal/Cdt/Admission.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}

-- | Immutable admission of one requested segment against the sparse
-- constrained-edge section, decided without thawing topology.
module Moonlight.Triangulation.Internal.Cdt.Admission
  ( ConstraintAdmission (..)
  , constraintAdmission
  , segmentBoxesAreDisjoint
  ) where

import qualified Data.IntSet as IntSet
import qualified Moonlight.Triangulation.Dcel as Dcel
import Moonlight.Triangulation.Handles.HandleDefs
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
-- cardinality is the lawful index here: rejection depends only on a proper
-- crossing with an existing protected segment, while shared endpoints,
-- duplicate segments, and collinear overlap remain recoverable by the mutable
-- corridor algebra. Accepted singleton requests descend once more inside their
-- sealed transaction; the overwhelmingly common rejected request stops here
-- without thawing topology.
data ConstraintAdmission
  = ConstraintBlocked !UndirectedEdgeId
  | ConstraintAdmitted

constraintAdmission
  :: Triangulation 'Constrained vertex directed undirected face
  -> VertexId
  -> VertexId
  -> ConstraintAdmission
constraintAdmission triangulation from to =
  IntSet.foldr firstBlocking ConstraintAdmitted (triConstraintEdges triangulation)
 where
  !requestFrom = Dcel.vertexPoint triangulation from
  !requestTo = Dcel.vertexPoint triangulation to

  firstBlocking raw later =
    let !edge = UndirectedEdgeId (fromIntegral raw)
        (!edgeFromId, !edgeToId) = Dcel.undirectedEndpoints triangulation edge
     in if
          from == edgeFromId
            || from == edgeToId
            || to == edgeFromId
            || to == edgeToId
          then later
          else
            let !edgeFrom = Dcel.vertexPoint triangulation edgeFromId
                !edgeTo = Dcel.vertexPoint triangulation edgeToId
             in if segmentBoxesAreDisjoint requestFrom requestTo edgeFrom edgeTo
                  then later
                  else
                    if segmentsProperlyCross requestFrom requestTo edgeFrom edgeTo
                      then ConstraintBlocked edge
                      else later

segmentBoxesAreDisjoint
  :: Point
  -> Point
  -> Point
  -> Point
  -> Bool
segmentBoxesAreDisjoint (Point ax ay) (Point bx by) (Point cx cy) (Point dx dy) =
  max ax bx < min cx dx
    || max cx dx < min ax bx
    || max ay by < min cy dy
    || max cy dy < min ay by
{-# INLINE segmentBoxesAreDisjoint #-}