packages feed

waterfall-cad-0.4.0.0: src/Waterfall/Internal/Path.hs

{-# LANGUAGE  InstanceSigs#-}
{-# OPTIONS_HADDOCK not-home #-}
module Waterfall.Internal.Path
( Path (..)
, joinPaths
, allPathEndpoints
) where

import Data.List.NonEmpty (NonEmpty ())
import Data.Foldable (toList)
import Waterfall.Internal.Finalizers (toAcquire, unsafeFromAcquire) 
import Control.Monad.IO.Class (liftIO)
import qualified OpenCascade.TopoDS as TopoDS
import Foreign.Ptr
import Linear (V3 (..))
import Data.Semigroup (sconcat)
import Waterfall.Internal.Edges (allWireEndpoints, intersperseLines, joinWires)
-- | A Path in 3D Space 
--
-- Under the hood, this is represented by an OpenCascade `TopoDS.Wire`.
newtype Path = Path { rawPath :: Ptr TopoDS.Wire }

-- | Exposing this because I found it useful for debugging
allPathEndpoints :: Path -> [(V3 Double, V3 Double)]
allPathEndpoints (Path raw) = unsafeFromAcquire $ do
    wire <- toAcquire raw
    liftIO $ allWireEndpoints wire
    
joinPaths :: [Path] -> Path
joinPaths paths = Path . unsafeFromAcquire $ do
    wires <- traverse (toAcquire . rawPath) paths
    joinWires =<< intersperseLines wires

-- | Joins `Path`s, @ a <> b @ connects the end point of @ b @ to the start of @ b @, if these points are not coincident, a line is created between them.
-- 
-- Attempts to combine paths in ways that generate a non manifold path will produce an error case that is not currently handled gracefully.
instance Semigroup Path where
    sconcat :: NonEmpty Path -> Path
    sconcat = joinPaths . toList
    (<>) :: Path -> Path -> Path
    a <> b = joinPaths [a, b]
    
instance Monoid Path where
    mempty :: Path
    mempty = joinPaths []
    mconcat :: [Path] -> Path
    mconcat = joinPaths