moonlight-triangulation-0.1.0.0: src-dcel/Moonlight/Triangulation/Internal/DcelOperations/Legalize.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
-- | The seeding entry points that drive one legalization epoch.
module Moonlight.Triangulation.Internal.DcelOperations.Legalize
( legalizeScratch
, legalizeEdges
, legalizeCavityFanScratch
) where
import Control.Monad (when)
import Control.Monad.ST (ST)
import Data.Foldable (traverse_)
import qualified Data.Vector.Unboxed.Mutable as MUV
import Moonlight.Triangulation.Internal.DcelOperations.CandidateArena
( genericCandidate
, growLegalizationArena
, noStarVertex
, seedGenericEdges
, seedStarScratch
)
import Moonlight.Triangulation.Internal.DcelOperations.FlipRule (LegalizationLaw (..))
import Moonlight.Triangulation.Internal.DcelOperations.Normalize (drainLegalization)
import Moonlight.Triangulation.Internal.Mutable (MutableDcel)
import Moonlight.Triangulation.Internal.OperationState
( Counter (..)
, OperationState
, addCounter
, legalizationArena
, maxCounter
, readScratch
, storeLegalizationArena
)
import Moonlight.Triangulation.Internal.PackedIndex (packIndex)
import Moonlight.Triangulation.Internal.Probe (KnownProbe, Probe (..))
legalizeEdges :: MutableDcel s vertex directed undirected face -> OperationState s -> [Int] -> ST s ()
legalizeEdges mutable operation initial = do
top <- seedGenericEdges operation 0 initial
(flips, maxDepth) <- drainLegalization @'ProbeOff mutable operation top noStarVertex ValidMesh
addCounter operation CounterEdgeFlips flips
maxCounter operation CounterLegalizationMaxStack maxDepth
-- | Repair the fan that fills a removed vertex's hole, draining cavity
-- candidates already written into the operation-owned scratch section. The
-- fan is a valid combinatorial filling but not yet a triangulation — a link
-- polygon that is non-convex at the fan origin yields an inverted triangle —
-- so this drain carries 'CavityRepair' rather than the insertion law: it
-- flips on the incircle determinant alone, and only the fan's own edges may
-- flip. Removal discovers and constructs the cavity inside that same
-- transaction; materializing a list merely to seed the legalization arena
-- would duplicate the local program.
legalizeCavityFanScratch
:: MutableDcel s vertex directed undirected face
-> OperationState s
-> Int
-> Int
-> Int
-> ST s ()
legalizeCavityFanScratch mutable operation cavityFloor scratchOffset candidateCount = do
initialArena <- legalizationArena operation
arena <- growLegalizationArena initialArena candidateCount
when (MUV.length arena /= MUV.length initialArena) (storeLegalizationArena operation arena)
traverse_
(\index -> do
edge <- readScratch operation (scratchOffset + index)
MUV.unsafeWrite arena index (packIndex (genericCandidate edge))
)
[0 .. candidateCount - 1]
(flips, maxDepth) <-
drainLegalization
@'ProbeOff
mutable
operation
candidateCount
noStarVertex
(CavityRepair cavityFloor)
addCounter operation CounterEdgeFlips flips
maxCounter operation CounterLegalizationMaxStack maxDepth
legalizeScratch :: forall p s vertex directed undirected face. KnownProbe p => MutableDcel s vertex directed undirected face -> OperationState s -> Int -> Int -> ST s ()
legalizeScratch mutable operation vertex candidateCount = do
top <- seedStarScratch operation 0 candidateCount
(flips, maxDepth) <- drainLegalization @p mutable operation top vertex ValidMesh
addCounter operation CounterEdgeFlips flips
maxCounter operation CounterLegalizationMaxStack maxDepth