packages feed

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

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

-- | Local direction tracing inside the thawed mesh: where a directed line
-- leaves the vertex or edge it currently stands on.
module Moonlight.Triangulation.Internal.Cdt.Corridor.Trace
  ( MutableVertexOut (..)
  , MutableVertexTrace (..)
  , MutableEdgeOut (..)
  , nextMutableIntersection
  , traceMutableDirectionOutOfVertex
  , advanceMutableVertexTrace
  , traceMutableDirectionOutOfEdge
  , mutableEdgeIntersectsNonCollinear
  , mutableVertexTraceIsSearching
  ) where

import Control.Monad.ST (ST)
import Data.Bits (xor)
import Moonlight.Triangulation.Handles.HandleDefs
import Moonlight.Triangulation.IntersectionIterator
import Moonlight.Triangulation.Internal.Cdt.Combinators
  ( directedInt
  , foldWhileM
  , vertexInt
  )
import Moonlight.Triangulation.Internal.Mutable
import Moonlight.Triangulation.Internal.Types
import Moonlight.Triangulation.Math
import Moonlight.Triangulation.Scalar (orient2dCoordinates)

data MutableVertexOut
  = MutableVertexOutHull
  | MutableVertexOutOverlap {-# UNPACK #-} !Int
  | MutableVertexOutEdge {-# UNPACK #-} !Int

data MutableVertexTrace
  = MutableVertexTraceSearching {-# UNPACK #-} !Int !Ordering
  | MutableVertexTraceComplete !MutableVertexOut

data MutableEdgeOut
  = MutableEdgeOutHull
  | MutableEdgeOutVertex {-# UNPACK #-} !Int
  | MutableEdgeOutEdge {-# UNPACK #-} !Int
  | MutableEdgeOutNone

nextMutableIntersection
  :: MutableDcel s vertex directed undirected face
  -> Point
  -> Point
  -> Intersection
  -> ST s (Maybe Intersection)
nextMutableIntersection mutable lineFrom lineTo current =
  case current of
    EdgeIntersection directed -> do
      edgeOut <- traceMutableDirectionOutOfEdge mutable (directedInt directed) lineFrom lineTo
      pure $ case edgeOut of
        MutableEdgeOutHull -> Nothing
        MutableEdgeOutVertex vertex ->
          Just (VertexIntersection (VertexId (fromIntegral vertex)))
        MutableEdgeOutEdge edge ->
          Just (EdgeIntersection (DirectedEdgeId (fromIntegral edge)))
        MutableEdgeOutNone -> Nothing
    VertexIntersection vertex -> do
      currentPoint <- pointAt mutable (vertexInt vertex)
      if currentPoint == lineTo
        then pure Nothing
        else do
          vertexOut <- traceMutableDirectionOutOfVertex mutable (vertexInt vertex) lineTo
          case vertexOut of
            MutableVertexOutHull -> pure Nothing
            MutableVertexOutOverlap edge ->
              pure (Just (EdgeOverlap (DirectedEdgeId (fromIntegral edge))))
            MutableVertexOutEdge edge -> do
              edgeFrom <- edgeOriginPoint mutable edge
              edgeTo <- edgeOriginPoint mutable (edge `xor` 1)
              pure
                ( if orient2d edgeFrom edgeTo lineTo == LT
                    then Nothing
                    else Just (EdgeIntersection (DirectedEdgeId (fromIntegral edge)))
                )
    EdgeOverlap directed
      | lineFrom == lineTo -> pure Nothing
      | otherwise -> do
          destination <- readOrigin mutable (directedInt directed `xor` 1)
          destinationPoint <- pointAt mutable destination
          pure
            ( if onClosedSegment lineFrom lineTo destinationPoint
                then Just (VertexIntersection (VertexId (fromIntegral destination)))
                else Nothing
            )

traceMutableDirectionOutOfVertex
  :: MutableDcel s vertex directed undirected face
  -> Int
  -> Point
  -> ST s MutableVertexOut
traceMutableDirectionOutOfVertex mutable vertex target = do
  start <- readVertexOut mutable vertex
  if start < 0
    then pure MutableVertexOutHull
    else do
      currentPoint <- pointAt mutable vertex
      startTarget <- edgeOriginPoint mutable (start `xor` 1)
      halfEdges <- directedEdgeCount mutable
      let startSide = orient2d currentPoint startTarget target
          rotateCounterClockwise = startSide == GT
      traced <-
        foldWhileM
          mutableVertexTraceIsSearching
          (advanceMutableVertexTrace mutable currentPoint target rotateCounterClockwise)
          (MutableVertexTraceSearching start startSide)
          [0 .. halfEdges]
      pure $ case traced of
        MutableVertexTraceComplete result -> result
        MutableVertexTraceSearching _ _ -> MutableVertexOutHull

advanceMutableVertexTrace
  :: MutableDcel s vertex directed undirected face
  -> Point
  -> Point
  -> Bool
  -> MutableVertexTrace
  -> Int
  -> ST s MutableVertexTrace
advanceMutableVertexTrace _ _ _ _ complete@(MutableVertexTraceComplete _) _ = pure complete
advanceMutableVertexTrace mutable currentPoint target rotateCounterClockwise (MutableVertexTraceSearching current currentSide) _ = do
  currentTarget <- edgeOriginPoint mutable (current `xor` 1)
  if currentSide == EQ && projectionFactor currentPoint currentTarget target >= 0
    then pure (MutableVertexTraceComplete (MutableVertexOutOverlap current))
    else do
      following <-
        if rotateCounterClockwise
          then (`xor` 1) <$> readPrevious mutable current
          else readNext mutable (current `xor` 1)
      followingTarget <- edgeOriginPoint mutable (following `xor` 1)
      let followingSide = orient2d currentPoint followingTarget target
      if followingSide == EQ && projectionFactor currentPoint followingTarget target >= 0
        then pure (MutableVertexTraceComplete (MutableVertexOutOverlap following))
        else do
          faceBetween <-
            readFace mutable (if rotateCounterClockwise then current else following)
          if faceBetween == 0
            then pure (MutableVertexTraceComplete MutableVertexOutHull)
            else
              if rotateCounterClockwise == (followingSide == LT)
                then do
                  segment <-
                    if rotateCounterClockwise
                      then readNext mutable current
                      else readPrevious mutable (current `xor` 1)
                  pure
                    ( MutableVertexTraceComplete
                        (MutableVertexOutEdge (segment `xor` 1))
                    )
                else pure (MutableVertexTraceSearching following followingSide)

traceMutableDirectionOutOfEdge
  :: MutableDcel s vertex directed undirected face
  -> Int
  -> Point
  -> Point
  -> ST s MutableEdgeOut
traceMutableDirectionOutOfEdge mutable edge lineFrom lineTo = do
  incident <- readFace mutable edge
  if incident == 0
    then pure MutableEdgeOutHull
    else do
      edgePrevious <- readPrevious mutable edge
      edgeNext <- readNext mutable edge
      edgeOrigin <- edgeOriginPoint mutable edge
      oppositeVertex <- edgeOriginPoint mutable edgePrevious
      let !originSide = orient2d lineFrom lineTo edgeOrigin
          !oppositeSide = orient2d lineFrom lineTo oppositeVertex
      if originSide == EQ || oppositeSide == EQ
        then classifyDegenerate edgePrevious edgeNext
        else
          if originSide == oppositeSide
            then do
              nextOrigin <- edgeOriginPoint mutable edgeNext
              let !outgoing = edgeNext `xor` 1
                  !targetSide = orient2d oppositeVertex nextOrigin lineTo
              pure
                ( if targetSide == LT
                    then MutableEdgeOutNone
                    else MutableEdgeOutEdge outgoing
                )
            else do
              let !outgoing = edgePrevious `xor` 1
                  !targetSide = orient2d edgeOrigin oppositeVertex lineTo
              pure
                ( if targetSide == LT
                    then MutableEdgeOutNone
                    else MutableEdgeOutEdge outgoing
                )
 where
  classifyDegenerate edgePrevious edgeNext = do
    previousIntersects <- mutableEdgeIntersectsNonCollinear mutable lineFrom lineTo edgePrevious
    nextIntersects <- mutableEdgeIntersectsNonCollinear mutable lineFrom lineTo edgeNext
    case (previousIntersects, nextIntersects) of
      (True, False) -> pure (MutableEdgeOutEdge (edgePrevious `xor` 1))
      (False, True) -> pure (MutableEdgeOutEdge (edgeNext `xor` 1))
      (True, True) -> MutableEdgeOutVertex <$> readOrigin mutable edgePrevious
      (False, False) -> pure MutableEdgeOutNone

mutableEdgeIntersectsNonCollinear
  :: MutableDcel s vertex directed undirected face
  -> Point
  -> Point
  -> Int
  -> ST s Bool
mutableEdgeIntersectsNonCollinear
  mutable
  (Point lineFromX lineFromY)
  (Point lineToX lineToY)
  edge = do
    edgeFromVertex <- readOrigin mutable edge
    edgeToVertex <- readOrigin mutable (edge `xor` 1)
    Point edgeFromX edgeFromY <- pointAt mutable edgeFromVertex
    Point edgeToX edgeToY <- pointAt mutable edgeToVertex
    let !lineFromSide =
          orient2dCoordinates
            edgeFromX edgeFromY edgeToX edgeToY lineFromX lineFromY
        !lineToSide =
          orient2dCoordinates
            edgeFromX edgeFromY edgeToX edgeToY lineToX lineToY
        !edgeFromSide =
          orient2dCoordinates
            lineFromX lineFromY lineToX lineToY edgeFromX edgeFromY
        !edgeToSide =
          orient2dCoordinates
            lineFromX lineFromY lineToX lineToY edgeToX edgeToY
    pure
      ( lineFromSide /= lineToSide
          && edgeFromSide /= edgeToSide
      )

mutableVertexTraceIsSearching :: MutableVertexTrace -> Bool
mutableVertexTraceIsSearching (MutableVertexTraceSearching _ _) = True
mutableVertexTraceIsSearching _ = False