module Waterfall.TwoD.Internal.Path2D
( Path2D (..)
, joinPaths
) where

import Data.Foldable (toList)
import Waterfall.Internal.Finalizers (toAcquire, unsafeFromAcquire)
import qualified OpenCascade.TopoDS as TopoDS
import Foreign.Ptr
import Data.Semigroup (sconcat)
import Waterfall.Internal.Edges (intersperseLines, joinWires)

-- | A Path in 2D Space 
--
-- Under the hood, this is represented by an OpenCascade `TopoDS.Wire`, constrained to the plane \(z=0\).
--
-- Please feel free to report a bug if you're able to construct a `Path2D`
-- which does not lie on this plane (without using Internal functions).
newtype Path2D = Path2D { Path2D -> Ptr Wire
rawPath :: Ptr TopoDS.Wire }

joinPaths :: [Path2D] -> Path2D
joinPaths :: [Path2D] -> Path2D
joinPaths [Path2D]
paths = Ptr Wire -> Path2D
Path2D (Ptr Wire -> Path2D)
-> (Acquire (Ptr Wire) -> Ptr Wire) -> Acquire (Ptr Wire) -> Path2D
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Acquire (Ptr Wire) -> Ptr Wire
forall a. Acquire a -> a
unsafeFromAcquire (Acquire (Ptr Wire) -> Path2D) -> Acquire (Ptr Wire) -> Path2D
forall a b. (a -> b) -> a -> b
$ do
    [Ptr Wire]
wires <- (Path2D -> Acquire (Ptr Wire)) -> [Path2D] -> Acquire [Ptr Wire]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse (Ptr Wire -> Acquire (Ptr Wire)
forall a. a -> Acquire a
toAcquire (Ptr Wire -> Acquire (Ptr Wire))
-> (Path2D -> Ptr Wire) -> Path2D -> Acquire (Ptr Wire)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Path2D -> Ptr Wire
rawPath) [Path2D]
paths
    [Ptr Wire] -> Acquire (Ptr Wire)
joinWires ([Ptr Wire] -> Acquire (Ptr Wire))
-> Acquire [Ptr Wire] -> Acquire (Ptr Wire)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< [Ptr Wire] -> Acquire [Ptr Wire]
intersperseLines [Ptr Wire]
wires

-- | Joins `Path2D`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 Path2D where
    sconcat :: NonEmpty Path2D -> Path2D
sconcat = [Path2D] -> Path2D
joinPaths ([Path2D] -> Path2D)
-> (NonEmpty Path2D -> [Path2D]) -> NonEmpty Path2D -> Path2D
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NonEmpty Path2D -> [Path2D]
forall a. NonEmpty a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList
    Path2D
a <> :: Path2D -> Path2D -> Path2D
<> Path2D
b = [Path2D] -> Path2D
joinPaths [Path2D
a, Path2D
b] 
    
instance Monoid Path2D where
    mempty :: Path2D
mempty = [Path2D] -> Path2D
joinPaths []
    mconcat :: [Path2D] -> Path2D
mconcat = [Path2D] -> Path2D
joinPaths