packages feed

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

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

-- | Read-only interrogation of the constrained layer, over both the published
-- mesh and the thawed one, plus the endpoint validity the verbs share.
module Moonlight.Triangulation.Internal.Cdt.Query
  ( constraintEdges
  , constraintStorageBytes
  , existsConstraint
  , canAddConstraint
  , intersectsConstraint
  , getConflictingEdgesBetweenPoints
  , getConflictingEdgesBetweenVertices
  , findDirectedEdge
  , findMutableEdge
  , validateEndpoints
  , validVertex
  ) where

import Control.Monad.ST (ST)
import Data.Bits (xor)
import qualified Data.IntSet as IntSet
import qualified Moonlight.Triangulation.Dcel as Dcel
import Moonlight.Triangulation.Handles.HandleDefs
import Moonlight.Triangulation.IntersectionIterator
import Moonlight.Triangulation.Internal.Cdt.Admission
  ( ConstraintAdmission (..)
  , constraintAdmission
  )
import Moonlight.Triangulation.Internal.Cdt.Types (CdtError (..))
import Moonlight.Triangulation.Internal.Mutable
import Moonlight.Triangulation.Internal.Paged (pagedLength)
import Moonlight.Triangulation.Internal.Representation
import Moonlight.Triangulation.Internal.Types

-- | Marked undirected edges in ascending identifier order.
constraintEdges
  :: Triangulation 'Constrained vertex directed undirected face
  -> [UndirectedEdgeId]
constraintEdges =
  fmap (UndirectedEdgeId . fromIntegral)
    . IntSet.toAscList
    . triConstraintEdges
{-# INLINE constraintEdges #-}

constraintStorageBytes
  :: Triangulation 'Constrained vertex directed undirected face
  -> Integer
constraintStorageBytes = toInteger . pagedLength . triConstraint

existsConstraint
  :: Triangulation 'Constrained vertex directed undirected face
  -> VertexId
  -> VertexId
  -> Bool
existsConstraint triangulation from to =
  case findDirectedEdge triangulation from to of
    Just edge -> Dcel.isConstraintEdge triangulation (asUndirected edge)
    Nothing -> False

canAddConstraint
  :: Triangulation 'Constrained vertex directed undirected face
  -> VertexId
  -> VertexId
  -> Bool
canAddConstraint triangulation from to =
  from /= to
    && validVertex triangulation from
    && validVertex triangulation to
    && case constraintAdmission triangulation from to of
      ConstraintBlocked _ -> False
      ConstraintAdmitted -> True

intersectsConstraint
  :: Triangulation 'Constrained vertex directed undirected face
  -> QueryPoint
  -> QueryPoint
  -> Bool
intersectsConstraint triangulation from to =
  case foldCorridorBetweenPoints triangulation from to firstBlocking () of
    Just (Left ()) -> True
    Just (Right ()) -> False
    Nothing -> not (null (getConflictingEdgesBetweenPoints triangulation from to))
 where
  firstBlocking :: () -> Intersection -> Either () ()
  firstBlocking _ (EdgeIntersection edge)
    | Dcel.isConstraintEdge triangulation (asUndirected edge) = Left ()
  firstBlocking _ _ = Right ()

getConflictingEdgesBetweenPoints
  :: Triangulation 'Constrained vertex directed undirected face
  -> QueryPoint
  -> QueryPoint
  -> [DirectedEdgeId]
getConflictingEdgesBetweenPoints triangulation from to =
  [ edge
  | EdgeIntersection edge <- lineIntersections triangulation from to
  , Dcel.isConstraintEdge triangulation (asUndirected edge)
  ]

getConflictingEdgesBetweenVertices
  :: Triangulation 'Constrained vertex directed undirected face
  -> VertexId
  -> VertexId
  -> [DirectedEdgeId]
getConflictingEdgesBetweenVertices triangulation from to
  | not (validVertex triangulation from && validVertex triangulation to) = []
  | otherwise =
      [ edge
      | EdgeIntersection edge <- lineIntersectionsBetweenVertices triangulation from to
      , Dcel.isConstraintEdge triangulation (asUndirected edge)
      ]

findDirectedEdge :: Triangulation 'Constrained vertex directed undirected face -> VertexId -> VertexId -> Maybe DirectedEdgeId
findDirectedEdge triangulation from to =
  case filter ((== to) . Dcel.destination triangulation) (Dcel.vertexOutgoingEdges triangulation from) of
    edge : _ -> Just edge
    [] -> Nothing

findMutableEdge :: MutableDcel s vertex directed undirected face -> Int -> Int -> ST s (Maybe Int)
findMutableEdge mutable from to = do
  start <- readVertexOut mutable from
  halfEdges <- directedEdgeCount mutable
  if start < 0
    then pure Nothing
    else go (halfEdges + 1) start start False
 where
  go !remaining !start !edge !visited
    | remaining <= 0 = pure Nothing
    | visited && edge == start = pure Nothing
    | otherwise = do
        destination <- readOrigin mutable (edge `xor` 1)
        if destination == to
          then pure (Just edge)
          else do
            previousEdge <- readPrevious mutable edge
            go (remaining - 1) start (previousEdge `xor` 1) True

validateEndpoints :: Triangulation 'Constrained vertex directed undirected face -> VertexId -> VertexId -> Either (CdtError) ()
validateEndpoints triangulation from to
  | not (validVertex triangulation from) = Left (InvalidConstraintVertex from)
  | not (validVertex triangulation to) = Left (InvalidConstraintVertex to)
  | otherwise = Right ()

validVertex :: Triangulation 'Constrained vertex directed undirected face -> VertexId -> Bool
validVertex triangulation (VertexId vertex) = fromIntegral vertex < Dcel.numVertices triangulation