moonlight-triangulation-0.1.0.0: src-build/Moonlight/Triangulation/Internal/Cdt/Build.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
-- | Constrained bulk loading: an unconstrained load promoted to the
-- constrained layer, then driven through the batch corridor interpreter.
module Moonlight.Triangulation.Internal.Cdt.Build
( fromDelaunay
, constrainedDelaunay
, constrainedDelaunayMaximal
) where
import qualified Data.Vector as V
import Data.Primitive.PrimArray (PrimArray, indexPrimArray, sizeofPrimArray)
import Data.Word (Word32)
import Moonlight.Triangulation.BulkLoad (delaunay)
import Moonlight.Triangulation.Handles.HandleDefs
import Moonlight.Triangulation.Internal.Cdt.Batch (recoverConstraints)
import Moonlight.Triangulation.Internal.Cdt.Combinators (mapLeft)
import Moonlight.Triangulation.Internal.Cdt.Types
import Moonlight.Triangulation.Internal.Representation
import Moonlight.Triangulation.Internal.Types
-- | Promote an unconstrained mesh into the constrained layer with no marked edges.
fromDelaunay
:: Triangulation 'Unconstrained vertex directed undirected face
-> Triangulation 'Constrained vertex directed undirected face
fromDelaunay = promoteConstrained
-- | Build a constrained triangulation, refusing the complete request when any
-- input constraint cannot be admitted.
constrainedDelaunay
:: HasPosition vertex
=> ElementDefaults directed undirected face
-> V.Vector vertex
-> V.Vector (Int, Int)
-> Either (CdtError) (BuildResult 'Constrained vertex directed undirected face)
constrainedDelaunay defaults inputVertices constraints = do
result <- constrainedDelaunayMaximal defaults inputVertices constraints
if V.null (cdtRejectedConstraints result)
then
Right
BuildResult
{ buildTriangulation = cdtBuildTriangulation result
, buildInputVertices = cdtBuildInputVertices result
, buildStats = cdtBuildStats result
}
else Left (ConstraintInputConflicts (cdtRejectedConstraints result))
-- | Stable constrained bulk loading. Duplicate input vertices are rerouted to
-- the first surviving handle. Every proper constraint conflict is returned in
-- input order; accepted constraints remain in the result.
constrainedDelaunayMaximal
:: HasPosition vertex
=> ElementDefaults directed undirected face
-> V.Vector vertex
-> V.Vector (Int, Int)
-> Either (CdtError) (CdtBuildResult vertex directed undirected face)
constrainedDelaunayMaximal defaults inputVertices constraints = do
built <- mapLeft CdtBuildError (delaunay defaults inputVertices)
let !mapping = buildInputVertices built
!initial = fromDelaunay (buildTriangulation built)
requests <- V.mapM (mapConstraintRequest mapping) constraints
batch <- recoverConstraints initial requests
let rejected =
V.mapMaybe
(\(request, outcome) ->
case outcome of
ConstraintAccepted _ _ -> Nothing
ConstraintRejected _ -> Just request
)
(V.zip constraints (constraintBatchOutcomes batch))
pure
CdtBuildResult
{ cdtBuildTriangulation = constraintBatchTriangulation batch
, cdtBuildInputVertices = mapping
, cdtBuildStats = buildStats built
, cdtRejectedConstraints = rejected
}
where
mapConstraintRequest
:: PrimArray Word32
-> (Int, Int)
-> Either (CdtError) (VertexId, VertexId)
mapConstraintRequest mapping (fromIndex, toIndex) =
(,)
<$> mapConstraintEndpoint mapping fromIndex
<*> mapConstraintEndpoint mapping toIndex
mapConstraintEndpoint
:: PrimArray Word32
-> Int
-> Either (CdtError) VertexId
mapConstraintEndpoint mapping endpointIndex
| endpointIndex >= 0 && endpointIndex < sizeofPrimArray mapping =
Right (VertexId (indexPrimArray mapping endpointIndex))
| otherwise =
Left (ConstraintEndpointIndexOutOfRange endpointIndex (sizeofPrimArray mapping))