moonlight-triangulation-0.1.0.0: src-build/Moonlight/Triangulation/Internal/Cdt/Corridor.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
-- | The corridor walk: one traversal of the requested segment that yields
-- either the first oriented blocking witness or a complete recovery program.
module Moonlight.Triangulation.Internal.Cdt.Corridor
( constraintWorkspaceFor
, scanMutableConstraint
, advanceMutablePlan
, continueMutablePlan
, beginMutableRecover
, finishMutableRecover
, settleMutableProgram
, mutablePlanIsActive
, writeConstraintProgram
, readConstraintProgram
, existingProgramTag
, recoverProgramTag
) where
import Control.Monad.ST (ST)
import Data.Bits (xor)
import Moonlight.Triangulation.Handles.HandleDefs
import Moonlight.Triangulation.IntersectionIterator (Intersection (..))
import Moonlight.Triangulation.Internal.Cdt.Combinators
( directedInt
, foldWhileM
, vertexInt
)
import Moonlight.Triangulation.Internal.Cdt.Corridor.Trace (nextMutableIntersection)
import Moonlight.Triangulation.Internal.Cdt.Types
import Moonlight.Triangulation.Internal.Growable
( GrowableWord32
, clearGrowable
, readGrowable
, writeGrowable
)
import Moonlight.Triangulation.Internal.Mutable
import Moonlight.Triangulation.Internal.Types (Point)
-- | The walk budget bounds a corridor through the mesh as it stands, so it is
-- taken per request rather than per transaction: a verb that inserts before it
-- recovers has changed the mesh the budget describes. Recovery itself adds
-- neither vertex nor edge, so a batch reads the same number every time.
constraintWorkspaceFor
:: GrowableWord32 s
-> MutableDcel s vertex directed undirected face
-> ST s (ConstraintWorkspace s)
constraintWorkspaceFor constraintProgramWords mutable = do
halfEdges <- directedEdgeCount mutable
vertices <- pointCount mutable
pure
ConstraintWorkspace
{ constraintProgramWords
, constraintWalkBudget = 2 * halfEdges + vertices + 8
}
writeConstraintProgram :: ConstraintWorkspace s -> Int -> Int -> ST s ()
writeConstraintProgram ConstraintWorkspace{constraintProgramWords} index value =
writeGrowable constraintProgramWords index (fromIntegral value)
{-# INLINE writeConstraintProgram #-}
readConstraintProgram :: ConstraintWorkspace s -> Int -> ST s Int
readConstraintProgram ConstraintWorkspace{constraintProgramWords} index =
fromIntegral <$> readGrowable constraintProgramWords index
{-# INLINE readConstraintProgram #-}
-- | Walk one corridor once, gluing the local observations into either the
-- first oriented blocking witness or a complete recovery program. The former
-- admission/planning pair traversed every admitted corridor twice and could
-- disagree if either walk evolved independently; this scanner is their single
-- semantic owner.
scanMutableConstraint
:: ConstraintWorkspace s
-> MutableDcel s vertex directed undirected face
-> VertexId
-> VertexId
-> 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)
initialCursor <- beginMutableRecover workspace 0 from 0 False
walked <-
foldWhileM
mutablePlanIsActive
(advanceMutablePlan workspace mutable lineFrom lineTo to)
(MutablePlanActive (VertexIntersection from) initialCursor)
[1 .. constraintWalkBudget]
pure $ case walked of
MutablePlanComplete plan -> Right (MutableConstraintScanAdmitted plan)
MutablePlanBlocked blocking -> Right (MutableConstraintScanBlocked blocking)
MutablePlanFailed obstruction -> Left (ConstraintCorridorObstructed obstruction)
MutablePlanActive _ _ ->
Left
( ConstraintCorridorObstructed
(CorridorWalkDidNotTerminate constraintWalkBudget)
)
advanceMutablePlan
:: ConstraintWorkspace s
-> MutableDcel s vertex directed undirected face
-> Point
-> Point
-> VertexId
-> MutablePlanWalk
-> Int
-> ST s MutablePlanWalk
advanceMutablePlan _ _ _ _ _ complete@(MutablePlanComplete _) _ = pure complete
advanceMutablePlan _ _ _ _ _ blocked@(MutablePlanBlocked _) _ = pure blocked
advanceMutablePlan _ _ _ _ _ failed@(MutablePlanFailed _) _ = pure failed
advanceMutablePlan workspace mutable lineFrom lineTo target (MutablePlanActive event cursor) _ =
case event of
EdgeIntersection directed -> do
constrained <- readConstraint mutable (directedInt directed)
if constrained
then pure (MutablePlanBlocked directed)
else do
writeConstraintProgram workspace (mutableCursorWriteAt cursor) (directedInt directed)
continueMutablePlan
workspace
mutable
lineFrom
lineTo
target
event
cursor
{ mutableCursorWriteAt = mutableCursorWriteAt cursor + 1
, mutableCursorConflictCount = mutableCursorConflictCount cursor + 1
, mutableCursorAfterOverlap = False
}
VertexIntersection vertex
| mutableCursorAfterOverlap cursor ->
continueMutablePlan
workspace
mutable
lineFrom
lineTo
target
event
cursor{mutableCursorAfterOverlap = False}
| vertex == mutableCursorAt cursor ->
continueMutablePlan
workspace
mutable
lineFrom
lineTo
target
event
cursor{mutableCursorAfterOverlap = False}
| otherwise -> do
finished <- finishMutableRecover workspace vertex cursor
nextCursor <-
beginMutableRecover
workspace
(mutableCursorWriteAt finished)
vertex
(mutableCursorPieceCount finished)
False
continueMutablePlan workspace mutable lineFrom lineTo target event nextCursor
EdgeOverlap rawDirected -> do
let rawEdge = directedInt rawDirected
rawOrigin <- readOrigin mutable rawEdge
rawDestination <- readOrigin mutable (rawEdge `xor` 1)
let current = vertexInt (mutableCursorAt cursor)
oriented
| rawOrigin == current = Just rawEdge
| rawDestination == current = Just (rawEdge `xor` 1)
| otherwise = Nothing
case oriented of
Nothing ->
pure
( MutablePlanFailed
( CorridorBoundaryMissing
(mutableCursorAt cursor)
(VertexId (fromIntegral rawOrigin))
)
)
Just edge -> do
prefix <-
if mutableCursorConflictCount cursor == 0
then pure cursor
else finishMutableRecover workspace (mutableCursorAt cursor) cursor
let !existingAt =
if mutableCursorConflictCount cursor == 0
then mutableCursorHeader cursor
else mutableCursorWriteAt prefix
!edgeDestination =
VertexId
( fromIntegral
(if edge == rawEdge then rawDestination else rawOrigin)
)
!pieceCount = mutableCursorPieceCount prefix + 1
!nextHeader = existingAt + 2
writeConstraintProgram workspace existingAt existingProgramTag
writeConstraintProgram workspace (existingAt + 1) edge
nextCursor <-
beginMutableRecover
workspace
nextHeader
edgeDestination
pieceCount
True
continueMutablePlan workspace mutable lineFrom lineTo target event nextCursor
continueMutablePlan
:: ConstraintWorkspace s
-> MutableDcel s vertex directed undirected face
-> Point
-> Point
-> VertexId
-> Intersection
-> MutableProgramCursor
-> ST s MutablePlanWalk
continueMutablePlan workspace mutable lineFrom lineTo target event cursor = do
following <- nextMutableIntersection mutable lineFrom lineTo event
case following of
Just nextEvent -> pure (MutablePlanActive nextEvent cursor)
Nothing -> MutablePlanComplete <$> settleMutableProgram workspace target cursor
beginMutableRecover
:: ConstraintWorkspace s
-> Int
-> VertexId
-> Int
-> Bool
-> ST s MutableProgramCursor
beginMutableRecover workspace header from pieceCount afterOverlap = do
writeConstraintProgram workspace header recoverProgramTag
writeConstraintProgram workspace (header + 1) (vertexInt from)
writeConstraintProgram workspace (header + 2) 0
writeConstraintProgram workspace (header + 3) 0
pure
MutableProgramCursor
{ mutableCursorAt = from
, mutableCursorHeader = header
, mutableCursorWriteAt = header + 4
, mutableCursorConflictCount = 0
, mutableCursorPieceCount = pieceCount
, mutableCursorAfterOverlap = afterOverlap
}
finishMutableRecover
:: ConstraintWorkspace s
-> VertexId
-> MutableProgramCursor
-> ST s MutableProgramCursor
finishMutableRecover workspace to cursor = do
writeConstraintProgram workspace (mutableCursorHeader cursor + 2) (vertexInt to)
writeConstraintProgram workspace (mutableCursorHeader cursor + 3) (mutableCursorConflictCount cursor)
pure
cursor
{ mutableCursorAt = to
, mutableCursorPieceCount = mutableCursorPieceCount cursor + 1
}
settleMutableProgram
:: ConstraintWorkspace s
-> VertexId
-> MutableProgramCursor
-> ST s MutableConstraintProgram
settleMutableProgram workspace target cursor
| mutableCursorAt cursor == target =
pure
MutableConstraintProgram
{ mutableProgramWordCount = mutableCursorHeader cursor
, mutableProgramPieceCount = mutableCursorPieceCount cursor
}
| otherwise = do
finished <- finishMutableRecover workspace target cursor
pure
MutableConstraintProgram
{ mutableProgramWordCount = mutableCursorWriteAt finished
, mutableProgramPieceCount = mutableCursorPieceCount finished
}
existingProgramTag :: Int
existingProgramTag = 0
recoverProgramTag :: Int
recoverProgramTag = 1
mutablePlanIsActive :: MutablePlanWalk -> Bool
mutablePlanIsActive (MutablePlanActive _ _) = True
mutablePlanIsActive _ = False