packages feed

moonlight-triangulation-1.0.0.0: src-dcel/Moonlight/Triangulation/Handles/Dynamic.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RoleAnnotations #-}

-- | Owning handles that prevent identifiers from crossing mesh boundaries.
module Moonlight.Triangulation.Handles.Dynamic
  ( InnerTag
  , PossiblyOuterTag
  , FixedFaceHandle
  , asPossiblyOuter
  , fixedFaceId
  , VertexHandle
  , DirectedEdgeHandle
  , UndirectedEdgeHandle
  , FaceHandle
  , vertexHandle
  , directedEdgeHandle
  , undirectedEdgeHandle
  , faceHandle
  , innerFaceHandle
  , outerFaceHandle
  , fixVertex
  , fixDirectedEdge
  , fixUndirectedEdge
  , fixFace
  , vertexHandleData
  , vertexHandlePosition
  , vertexHandleOutEdge
  , vertexHandleOutEdges
  , directedEdgeDataH
  , directedEdgeFrom
  , directedEdgeTo
  , directedEdgeVertices
  , directedEdgePositions
  , directedEdgeReverse
  , directedEdgeNext
  , directedEdgePrevious
  , directedEdgeClockwise
  , directedEdgeCounterClockwise
  , directedEdgeFace
  , directedEdgeAsUndirected
  , directedEdgeIsOuter
  , directedEdgeSideQuery
  , directedEdgeOppositeVertex
  , directedEdgeOppositePosition
  , directedEdgeProjectionFactor
  , directedEdgeNearestPoint
  , undirectedEdgeDataH
  , undirectedEdgeAsDirected
  , undirectedEdgeVertices
  , undirectedEdgeIsConstraint
  , undirectedEdgeIsBoundary
  , faceDataH
  , faceIsOuter
  , faceAsInner
  , faceAdjacentEdge
  , faceAdjacentEdges
  , innerFaceVertices
  , innerFaceCircumcenter
  , innerFacePositions
  , innerFaceBarycentric
  ) where

import Moonlight.Triangulation.Dcel qualified as Dcel
import Moonlight.Triangulation.Handles.HandleDefs
import Moonlight.Triangulation.LineSideInfo (LineSideInfo)
import Moonlight.Triangulation.Math qualified as Math
import Moonlight.Triangulation.Types

-- | A face handle that is statically known not to denote the outer face.
data InnerTag

-- | A face handle that may denote the unique outer face.
data PossiblyOuterTag

type role FixedFaceHandle nominal
-- | Fixed face identifier refined by whether it may denote the outer face.
newtype FixedFaceHandle tag = FixedFaceHandle { unFixedFaceHandle :: FaceId }
  deriving stock (Show)
  deriving newtype (Eq, Ord)

-- | Forget the proof that a fixed face is bounded.
asPossiblyOuter :: FixedFaceHandle InnerTag -> FixedFaceHandle PossiblyOuterTag
asPossiblyOuter (FixedFaceHandle face) = FixedFaceHandle face
{-# INLINE asPossiblyOuter #-}

-- | Recover the unrefined face identifier.
fixedFaceId :: FixedFaceHandle tag -> FaceId
fixedFaceId (FixedFaceHandle face) = face
{-# INLINE fixedFaceId #-}

-- | Admitted vertex paired with its owning triangulation.
data VertexHandle mode vertex directed undirected face = VertexHandle
  !(Triangulation mode vertex directed undirected face)
  !VertexId

-- | Admitted directed edge paired with its owning triangulation.
data DirectedEdgeHandle mode vertex directed undirected face = DirectedEdgeHandle
  !(Triangulation mode vertex directed undirected face)
  !DirectedEdgeId

-- | Admitted undirected edge paired with its owning triangulation.
data UndirectedEdgeHandle mode vertex directed undirected face = UndirectedEdgeHandle
  !(Triangulation mode vertex directed undirected face)
  !UndirectedEdgeId

-- | Admitted face paired with its owning triangulation and outer-face proof.
data FaceHandle tag mode vertex directed undirected face = FaceHandle
  !(Triangulation mode vertex directed undirected face)
  !(FixedFaceHandle tag)

instance Show (VertexHandle mode vertex directed undirected face) where
  showsPrec precedence = showsPrec precedence . fixVertex

instance Show (DirectedEdgeHandle mode vertex directed undirected face) where
  showsPrec precedence = showsPrec precedence . fixDirectedEdge

instance Show (UndirectedEdgeHandle mode vertex directed undirected face) where
  showsPrec precedence = showsPrec precedence . fixUndirectedEdge

instance Show (FaceHandle tag mode vertex directed undirected face) where
  showsPrec precedence = showsPrec precedence . fixFace

-- | Admit a vertex identifier into a triangulation.
vertexHandle
  :: Triangulation mode vertex directed undirected face
  -> VertexId
  -> Maybe (VertexHandle mode vertex directed undirected face)
vertexHandle triangulation vertex@(VertexId raw)
  | fromIntegral raw < Dcel.numVertices triangulation = Just (VertexHandle triangulation vertex)
  | otherwise = Nothing

-- | Admit a directed-edge identifier into a triangulation.
directedEdgeHandle
  :: Triangulation mode vertex directed undirected face
  -> DirectedEdgeId
  -> Maybe (DirectedEdgeHandle mode vertex directed undirected face)
directedEdgeHandle triangulation edge@(DirectedEdgeId raw)
  | fromIntegral raw < Dcel.numDirectedEdges triangulation = Just (DirectedEdgeHandle triangulation edge)
  | otherwise = Nothing

-- | Admit an undirected-edge identifier into a triangulation.
undirectedEdgeHandle
  :: Triangulation mode vertex directed undirected face
  -> UndirectedEdgeId
  -> Maybe (UndirectedEdgeHandle mode vertex directed undirected face)
undirectedEdgeHandle triangulation edge@(UndirectedEdgeId raw)
  | fromIntegral raw < Dcel.numUndirectedEdges triangulation = Just (UndirectedEdgeHandle triangulation edge)
  | otherwise = Nothing

-- | Admit a face identifier that may denote the outer face.
faceHandle
  :: Triangulation mode vertex directed undirected face
  -> FaceId
  -> Maybe (FaceHandle PossiblyOuterTag mode vertex directed undirected face)
faceHandle triangulation face@(FaceId raw)
  | fromIntegral raw < Dcel.numFaces triangulation = Just (FaceHandle triangulation (FixedFaceHandle face))
  | otherwise = Nothing

-- | Admit a face identifier while proving that it is bounded.
innerFaceHandle
  :: Triangulation mode vertex directed undirected face
  -> FaceId
  -> Maybe (FaceHandle InnerTag mode vertex directed undirected face)
innerFaceHandle triangulation face
  | face == Dcel.outerFace = Nothing
  | otherwise = do
      FaceHandle _ (FixedFaceHandle valid) <- faceHandle triangulation face
      pure (FaceHandle triangulation (FixedFaceHandle valid))

-- | Owning handle to the unique unbounded face.
outerFaceHandle
  :: Triangulation mode vertex directed undirected face
  -> FaceHandle PossiblyOuterTag mode vertex directed undirected face
outerFaceHandle triangulation = FaceHandle triangulation (FixedFaceHandle Dcel.outerFace)

-- | Forget ownership and recover the vertex identifier.
fixVertex :: VertexHandle mode vertex directed undirected face -> VertexId
fixVertex (VertexHandle _ vertex) = vertex
{-# INLINE fixVertex #-}

-- | Forget ownership and recover the directed-edge identifier.
fixDirectedEdge :: DirectedEdgeHandle mode vertex directed undirected face -> DirectedEdgeId
fixDirectedEdge (DirectedEdgeHandle _ edge) = edge
{-# INLINE fixDirectedEdge #-}

-- | Forget ownership and recover the undirected-edge identifier.
fixUndirectedEdge :: UndirectedEdgeHandle mode vertex directed undirected face -> UndirectedEdgeId
fixUndirectedEdge (UndirectedEdgeHandle _ edge) = edge
{-# INLINE fixUndirectedEdge #-}

-- | Forget ownership while retaining the outer-face refinement.
fixFace :: FaceHandle tag mode vertex directed undirected face -> FixedFaceHandle tag
fixFace (FaceHandle _ face) = face
{-# INLINE fixFace #-}

-- | Vertex annotation through an owning handle.
vertexHandleData :: VertexHandle mode vertex directed undirected face -> vertex
vertexHandleData (VertexHandle triangulation vertex) = Dcel.vertexData triangulation vertex
{-# INLINE vertexHandleData #-}

-- | Authoritative vertex position through an owning handle.
vertexHandlePosition
  :: VertexHandle mode vertex directed undirected face
  -> Point
vertexHandlePosition (VertexHandle triangulation vertex) = (Dcel.vertexPoint triangulation vertex)
{-# INLINE vertexHandlePosition #-}

-- | One outgoing edge of a connected vertex.
vertexHandleOutEdge
  :: VertexHandle mode vertex directed undirected face
  -> Maybe (DirectedEdgeHandle mode vertex directed undirected face)
vertexHandleOutEdge (VertexHandle triangulation vertex) = DirectedEdgeHandle triangulation <$> Dcel.vertexOutEdge triangulation vertex

-- | Directed edges leaving a vertex in ring order.
vertexHandleOutEdges
  :: VertexHandle mode vertex directed undirected face
  -> [DirectedEdgeHandle mode vertex directed undirected face]
vertexHandleOutEdges (VertexHandle triangulation vertex) = map (DirectedEdgeHandle triangulation) (Dcel.vertexOutgoingEdges triangulation vertex)

-- | Directed-edge annotation through an owning handle.
directedEdgeDataH :: DirectedEdgeHandle mode vertex directed undirected face -> directed
directedEdgeDataH (DirectedEdgeHandle triangulation edge) = Dcel.directedEdgeData triangulation edge
{-# INLINE directedEdgeDataH #-}

-- | Origin vertex of an owning directed edge.
directedEdgeFrom :: DirectedEdgeHandle mode vertex directed undirected face -> VertexHandle mode vertex directed undirected face
directedEdgeFrom (DirectedEdgeHandle triangulation edge) = VertexHandle triangulation (Dcel.origin triangulation edge)
{-# INLINE directedEdgeFrom #-}

-- | Destination vertex of an owning directed edge.
directedEdgeTo :: DirectedEdgeHandle mode vertex directed undirected face -> VertexHandle mode vertex directed undirected face
directedEdgeTo (DirectedEdgeHandle triangulation edge) = VertexHandle triangulation (Dcel.destination triangulation edge)
{-# INLINE directedEdgeTo #-}

-- | Origin and destination of an owning directed edge.
directedEdgeVertices
  :: DirectedEdgeHandle mode vertex directed undirected face
  -> (VertexHandle mode vertex directed undirected face, VertexHandle mode vertex directed undirected face)
directedEdgeVertices edge = (directedEdgeFrom edge, directedEdgeTo edge)

-- | Origin and destination positions of an owning directed edge.
directedEdgePositions
  :: DirectedEdgeHandle mode vertex directed undirected face
  -> (Point, Point)
directedEdgePositions edge = (vertexHandlePosition (directedEdgeFrom edge), vertexHandlePosition (directedEdgeTo edge))
{-# INLINE directedEdgePositions #-}

-- | Reverse an owning directed edge.
directedEdgeReverse :: DirectedEdgeHandle mode vertex directed undirected face -> DirectedEdgeHandle mode vertex directed undirected face
directedEdgeReverse (DirectedEdgeHandle triangulation edge) = DirectedEdgeHandle triangulation (reverseEdge edge)
{-# INLINE directedEdgeReverse #-}

-- | Next owning edge around the incident face.
directedEdgeNext :: DirectedEdgeHandle mode vertex directed undirected face -> DirectedEdgeHandle mode vertex directed undirected face
directedEdgeNext (DirectedEdgeHandle triangulation edge) = DirectedEdgeHandle triangulation (Dcel.next triangulation edge)
{-# INLINE directedEdgeNext #-}

-- | Previous owning edge around the incident face.
directedEdgePrevious :: DirectedEdgeHandle mode vertex directed undirected face -> DirectedEdgeHandle mode vertex directed undirected face
directedEdgePrevious (DirectedEdgeHandle triangulation edge) = DirectedEdgeHandle triangulation (Dcel.previous triangulation edge)
{-# INLINE directedEdgePrevious #-}

-- | Previous owning edge around its origin vertex.
directedEdgeClockwise :: DirectedEdgeHandle mode vertex directed undirected face -> DirectedEdgeHandle mode vertex directed undirected face
directedEdgeClockwise (DirectedEdgeHandle triangulation edge) = DirectedEdgeHandle triangulation (Dcel.clockwise triangulation edge)
{-# INLINE directedEdgeClockwise #-}

-- | Next owning edge around its origin vertex.
directedEdgeCounterClockwise :: DirectedEdgeHandle mode vertex directed undirected face -> DirectedEdgeHandle mode vertex directed undirected face
directedEdgeCounterClockwise (DirectedEdgeHandle triangulation edge) = DirectedEdgeHandle triangulation (Dcel.counterClockwise triangulation edge)
{-# INLINE directedEdgeCounterClockwise #-}

-- | Owning handle to the incident face.
directedEdgeFace
  :: DirectedEdgeHandle mode vertex directed undirected face
  -> FaceHandle PossiblyOuterTag mode vertex directed undirected face
directedEdgeFace (DirectedEdgeHandle triangulation edge) = FaceHandle triangulation (FixedFaceHandle (Dcel.incidentFace triangulation edge))

-- | Forget the orientation of an owning edge.
directedEdgeAsUndirected
  :: DirectedEdgeHandle mode vertex directed undirected face
  -> UndirectedEdgeHandle mode vertex directed undirected face
directedEdgeAsUndirected (DirectedEdgeHandle triangulation edge) = UndirectedEdgeHandle triangulation (asUndirected edge)

-- | Whether the owning edge is incident to the outer face.
directedEdgeIsOuter :: DirectedEdgeHandle mode vertex directed undirected face -> Bool
directedEdgeIsOuter = faceIsOuter . directedEdgeFace

-- | Exact side of the owning edge's oriented line.
directedEdgeSideQuery
  :: DirectedEdgeHandle mode vertex directed undirected face
  -> Point
  -> LineSideInfo
directedEdgeSideQuery edge query =
  let (from, to) = directedEdgePositions edge
   in Math.sideQuery from to query

-- | Undirected-edge annotation through an owning handle.
undirectedEdgeDataH :: UndirectedEdgeHandle mode vertex directed undirected face -> undirected
undirectedEdgeDataH (UndirectedEdgeHandle triangulation edge) = Dcel.undirectedEdgeData triangulation edge
{-# INLINE undirectedEdgeDataH #-}

-- | Normalized directed orientation of an owning undirected edge.
undirectedEdgeAsDirected
  :: UndirectedEdgeHandle mode vertex directed undirected face
  -> DirectedEdgeHandle mode vertex directed undirected face
undirectedEdgeAsDirected (UndirectedEdgeHandle triangulation edge) = DirectedEdgeHandle triangulation (normalizedDirected edge)

-- | Endpoints of an owning undirected edge.
undirectedEdgeVertices
  :: UndirectedEdgeHandle mode vertex directed undirected face
  -> (VertexHandle mode vertex directed undirected face, VertexHandle mode vertex directed undirected face)
undirectedEdgeVertices = directedEdgeVertices . undirectedEdgeAsDirected

-- | Face annotation through an owning handle.
faceDataH :: FaceHandle tag mode vertex directed undirected face -> face
faceDataH (FaceHandle triangulation (FixedFaceHandle face)) = Dcel.faceData triangulation face
{-# INLINE faceDataH #-}

-- | Whether the owning face is the unique unbounded face.
faceIsOuter :: FaceHandle tag mode vertex directed undirected face -> Bool
faceIsOuter (FaceHandle _ (FixedFaceHandle face)) = face == Dcel.outerFace
{-# INLINE faceIsOuter #-}

-- | Refine an owning face handle by excluding the outer face.
faceAsInner
  :: FaceHandle PossiblyOuterTag mode vertex directed undirected face
  -> Maybe (FaceHandle InnerTag mode vertex directed undirected face)
faceAsInner handle@(FaceHandle triangulation (FixedFaceHandle face))
  | faceIsOuter handle = Nothing
  | otherwise = Just (FaceHandle triangulation (FixedFaceHandle face))

-- | One owning edge on the face boundary.
faceAdjacentEdge
  :: FaceHandle tag mode vertex directed undirected face
  -> Maybe (DirectedEdgeHandle mode vertex directed undirected face)
faceAdjacentEdge (FaceHandle triangulation (FixedFaceHandle face)) = DirectedEdgeHandle triangulation <$> Dcel.adjacentEdge triangulation face

-- | Owning directed boundary of a face.
faceAdjacentEdges
  :: FaceHandle tag mode vertex directed undirected face
  -> [DirectedEdgeHandle mode vertex directed undirected face]
faceAdjacentEdges (FaceHandle triangulation (FixedFaceHandle face)) = map (DirectedEdgeHandle triangulation) (Dcel.faceDirectedEdges triangulation face)

-- | Three owning vertices of a bounded face.
innerFaceVertices
  :: FaceHandle InnerTag mode vertex directed undirected face
  -> Maybe
       ( VertexHandle mode vertex directed undirected face
       , VertexHandle mode vertex directed undirected face
       , VertexHandle mode vertex directed undirected face
       )
innerFaceVertices (FaceHandle triangulation (FixedFaceHandle face)) =
  (\(a, b, c) -> (VertexHandle triangulation a, VertexHandle triangulation b, VertexHandle triangulation c))
    <$> Dcel.innerFaceVertices triangulation face

-- | Circumcenter of an owning bounded face.
innerFaceCircumcenter
  :: FaceHandle InnerTag mode vertex directed undirected face
  -> Maybe (Point)
innerFaceCircumcenter face = do
  (a, b, c) <- innerFaceVertices face
  Math.circumcenter (vertexHandlePosition a) (vertexHandlePosition b) (vertexHandlePosition c)

-- | Vertex opposite an owning directed edge in its bounded incident face.
directedEdgeOppositeVertex
  :: DirectedEdgeHandle mode vertex directed undirected face
  -> Maybe (VertexHandle mode vertex directed undirected face)
directedEdgeOppositeVertex (DirectedEdgeHandle triangulation edge)
  | Dcel.incidentFace triangulation edge == Dcel.outerFace = Nothing
  | otherwise =
      Just (VertexHandle triangulation (Dcel.destination triangulation (Dcel.next triangulation edge)))

-- | Position opposite an owning directed edge in its bounded incident face.
directedEdgeOppositePosition
  :: DirectedEdgeHandle mode vertex directed undirected face
  -> Maybe (Point)
directedEdgeOppositePosition = fmap vertexHandlePosition . directedEdgeOppositeVertex
{-# INLINE directedEdgeOppositePosition #-}

-- | Projection parameter of a point onto an owning directed edge's line.
directedEdgeProjectionFactor
  :: DirectedEdgeHandle mode vertex directed undirected face
  -> Point
  -> Double
directedEdgeProjectionFactor edge query =
  let (from, to) = directedEdgePositions edge
   in Math.projectionFactor from to query

-- | Nearest point on the closed owning directed edge.
directedEdgeNearestPoint
  :: DirectedEdgeHandle mode vertex directed undirected face
  -> Point
  -> Point
directedEdgeNearestPoint edge query =
  let (from@(Point ax ay), to@(Point bx by)) = directedEdgePositions edge
      factor = max 0 (min 1 (Math.projectionFactor from to query))
   in Point (ax + factor * (bx - ax)) (ay + factor * (by - ay))

-- | Whether an owning undirected edge is constrained.
undirectedEdgeIsConstraint
  :: UndirectedEdgeHandle mode vertex directed undirected face
  -> Bool
undirectedEdgeIsConstraint (UndirectedEdgeHandle triangulation edge) =
  Dcel.isConstraintEdge triangulation edge

-- | Whether an owning undirected edge touches the outer face.
undirectedEdgeIsBoundary
  :: UndirectedEdgeHandle mode vertex directed undirected face
  -> Bool
undirectedEdgeIsBoundary (UndirectedEdgeHandle triangulation edge) =
  Dcel.isBoundaryEdge triangulation edge

-- | Positions of the three vertices of an owning bounded face.
innerFacePositions
  :: FaceHandle InnerTag mode vertex directed undirected face
  -> Maybe (Point, Point, Point)
innerFacePositions face = do
  (a, b, c) <- innerFaceVertices face
  pure (vertexHandlePosition a, vertexHandlePosition b, vertexHandlePosition c)
{-# INLINE innerFacePositions #-}

-- | Barycentric coordinates in an owning bounded face.
innerFaceBarycentric
  :: FaceHandle InnerTag mode vertex directed undirected face
  -> Point
  -> Maybe (Double, Double, Double)
innerFaceBarycentric face query = do
  (a, b, c) <- innerFacePositions face
  Math.barycentricCoordinates a b c query