packages feed

moonlight-triangulation-1.2.0.0: src-dcel/Moonlight/Triangulation/Internal/DcelOperations/Legalize.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -fllvm -optlo-O3 -optlc-O3 #-}

-- | The seeding entry points that drive one legalization epoch.
module Moonlight.Triangulation.Internal.DcelOperations.Legalize
  ( legalizeScratch
  , legalizeStarEdge
  , legalizeDenseStarEdgeInArena
  , 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
  ( CandidateDiscipline (..)
  , growLegalizationArena
  , seedGenericEdges
  , seedStarScratch
  )
import Moonlight.Triangulation.Internal.DcelOperations.FlipRule (LegalizationLaw (..))
import Moonlight.Triangulation.Internal.DcelOperations.Normalize
  ( LegalizationDrain
  , drainDenseUnconstrainedStarLegalization
  , drainLegalization
  )
import Moonlight.Triangulation.Internal.Mutable
  ( DenseMutableDcel
  , MutableDcel
  , MutableTopology
  )
import Moonlight.Triangulation.Internal.OperationState
  ( Counter (..)
  , LegalizationArena (..)
  , 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 GenericCandidates 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 (legalizationArenaLength arena /= legalizationArenaLength initialArena) (storeLegalizationArena operation arena)
  let LegalizationArena values = arena
  traverse_
    (\index -> do
       edge <- readScratch operation (scratchOffset + index)
       MUV.unsafeWrite values index (packIndex edge)
    )
    [0 .. candidateCount - 1]
  (flips, maxDepth) <-
    drainLegalization
      @'ProbeOff
      mutable
      operation
      candidateCount
      GenericCandidates
      (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 (StarCandidates vertex) ValidMesh
  addCounter operation CounterEdgeFlips flips
  maxCounter operation CounterLegalizationMaxStack maxDepth

-- | Normalize one newly covered outer edge against the inserted star vertex.
-- The circle sweep always covers exactly one edge; routing that singleton
-- through the shared scratch section merely materializes a one-element range
-- before immediately copying it into this arena. This is the same star-law
-- section written at its actual arity.
legalizeStarEdge
  :: forall p mutable s vertex directed undirected face
   . (KnownProbe p, MutableTopology mutable)
  => mutable s vertex directed undirected face
  -> OperationState s
  -> Int
  -> Int
  -> ST s (Int, Int)
legalizeStarEdge mutable operation vertex edge = do
  arena <- legalizationArena operation
  let LegalizationArena values = arena
  MUV.unsafeWrite values 0 (packIndex edge)
  drainLegalization @p mutable operation 1 (StarCandidates vertex) ValidMesh
{-# INLINE legalizeStarEdge #-}

-- | The monomorphic fresh-build interpreter for the singleton star epoch,
-- consuming and returning the sweep's borrowed candidate section.
legalizeDenseStarEdgeInArena
  :: DenseMutableDcel s vertex directed undirected face
  -> LegalizationArena s
  -> Int
  -> Int
  -> ST s (LegalizationDrain s ())
legalizeDenseStarEdgeInArena dense arena vertex edge = do
  let LegalizationArena values = arena
  MUV.unsafeWrite values 0 (packIndex edge)
  drainDenseUnconstrainedStarLegalization dense arena 1 vertex
{-# NOINLINE legalizeDenseStarEdgeInArena #-}

legalizationArenaLength :: LegalizationArena s -> Int
legalizationArenaLength (LegalizationArena values) = MUV.length values
{-# INLINE legalizationArenaLength #-}