packages feed

moonlight-triangulation-0.1.0.0: src-dcel/Moonlight/Triangulation/Internal/DcelOperations/FlipRewrite.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

-- | The flip rewrite itself, on a quadrilateral.
module Moonlight.Triangulation.Internal.DcelOperations.FlipRewrite
  ( flipEdge
  , applyFlip
  ) where

import Control.Monad (unless)
import Control.Monad.ST (ST)
import Moonlight.Triangulation.Handles.HandleDefs (UndirectedEdgeId (..))
import Moonlight.Triangulation.Internal.DcelOperations.Twin (reverseIndex)
import Moonlight.Triangulation.Internal.Mutable
  ( MutableDcel
  , payloadsPristine
  , readConstraint
  , readFace
  , readNext
  , readOrigin
  , readPrevious
  , resetEdgeData
  , resetFaceData
  , setCycle3
  , writeOrigin
  , writeVertexOut
  )
import Moonlight.Triangulation.Internal.Types (BuildError (ConstrainedEdgeFlipRefused))

flipEdge :: MutableDcel s vertex directed undirected face -> Int -> ST s (Either BuildError ())
flipEdge mutable edge = do
  protected <- readConstraint mutable edge
  if protected
    then pure (Left (ConstrainedEdgeFlipRefused (UndirectedEdgeId (fromIntegral (edge `quot` 2)))))
    else do
      let !twin = reverseIndex edge
      edgeNext <- readNext mutable edge
      edgePrevious <- readPrevious mutable edge
      twinNext <- readNext mutable twin
      twinPrevious <- readPrevious mutable twin
      leftFace <- readFace mutable edge
      rightFace <- readFace mutable twin
      a <- readOrigin mutable edge
      b <- readOrigin mutable twin
      c <- readOrigin mutable edgePrevious
      d <- readOrigin mutable twinPrevious
      applyFlip mutable edge twin edgeNext edgePrevious twinNext twinPrevious leftFace rightFace a b c d
      pure (Right ())

-- | The rewrite itself, from a quadrilateral the caller already holds. The
-- decision that licenses a flip reads the same two half-edge records the
-- rewrite consumes, so the drain hands its neighbourhood straight here rather
-- than making 'flipEdge' fetch it a second time; 'flipEdge' is that fetch, for
-- callers arriving with nothing but an index.
applyFlip
  :: MutableDcel s vertex directed undirected face
  -> Int
  -> Int
  -> Int
  -> Int
  -> Int
  -> Int
  -> Int
  -> Int
  -> Int
  -> Int
  -> Int
  -> Int
  -> ST s ()
applyFlip mutable edge twin edgeNext edgePrevious twinNext twinPrevious leftFace rightFace a b c d = do
  writeOrigin mutable edge c
  writeOrigin mutable twin d
  setCycle3 mutable leftFace edge twinPrevious edgeNext
  setCycle3 mutable rightFace twin edgePrevious twinNext
  -- The diagonal AB is gone and CD stands in its slot; both triangles have
  -- swapped a corner. Three elements changed what they are, so three labels go.
  -- Each reset carries the same test; a site doing several states it once.
  unless (payloadsPristine mutable) $ do
    resetEdgeData mutable (edge `quot` 2)
    resetFaceData mutable leftFace
    resetFaceData mutable rightFace
  writeVertexOut mutable a twinNext
  writeVertexOut mutable b edgeNext
  writeVertexOut mutable c edge
  writeVertexOut mutable d twin
{-# INLINE applyFlip #-}