{-|
Module: Waterfall.Loft

[Loft](https://en.wikipedia.org/wiki/Loft_\(3D\)) is a method to create smooth 3D shapes. 

Analagous to the [lofting](https://en.wikipedia.org/wiki/Lofting) process in boat building. 
A loft is defined by planar cross-sections of the desired shape at chosen locations. 
These cross-sections are then interpolated to form a smooth 3d shape.
-}
module Waterfall.Loft
( pointedLoft
, loft
) where

import Linear (V3 (..))
import Waterfall.Internal.Path (Path, rawPath)
import Waterfall.Internal.Solid (Solid (..), solidFromAcquire)
import Waterfall.Internal.ToOpenCascade (v3ToVertex)
import qualified OpenCascade.BRepOffsetAPI.ThruSections as ThruSections
import qualified OpenCascade.BRepBuilderAPI.MakeShape as MakeShape
import OpenCascade.Inheritance (upcast)
import Control.Monad.IO.Class (liftIO)
import Control.Monad (forM_, (<=<))

-- | Form a Loft which may terminate at defined points.
--
-- If the start or end points are set to `Nothing` then one end of the loft will be the terminal cross-section.
-- Otherwise, the loft will interpolate to that point.
pointedLoft :: Double -- ^ The loft precision, this should be a small value, e.g. @ 1e-6 @ 
    -> Maybe (V3 Double) -- ^ Optional start point for the loft
    -> [Path] -- ^ Series of cross-sections that the loft will pass through
    -> Maybe (V3 Double) -- ^ Optional end point for the loft
    -> Solid
pointedLoft :: Double -> Maybe (V3 Double) -> [Path] -> Maybe (V3 Double) -> Solid
pointedLoft Double
precision Maybe (V3 Double)
start [Path]
paths Maybe (V3 Double)
end = 
    Acquire (Ptr Shape) -> Solid
solidFromAcquire (Acquire (Ptr Shape) -> Solid) -> Acquire (Ptr Shape) -> Solid
forall a b. (a -> b) -> a -> b
$ do
        Ptr ThruSections
thruSections <- Bool -> Bool -> Double -> Acquire (Ptr ThruSections)
ThruSections.new Bool
True Bool
False Double
precision
        Maybe (V3 Double) -> (V3 Double -> Acquire ()) -> Acquire ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ Maybe (V3 Double)
start ((IO () -> Acquire ()
forall a. IO a -> Acquire a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> Acquire ())
-> (Ptr Vertex -> IO ()) -> Ptr Vertex -> Acquire ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Ptr ThruSections -> Ptr Vertex -> IO ()
ThruSections.addVertex Ptr ThruSections
thruSections) (Ptr Vertex -> Acquire ())
-> (V3 Double -> Acquire (Ptr Vertex)) -> V3 Double -> Acquire ()
forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< V3 Double -> Acquire (Ptr Vertex)
v3ToVertex)
        [Path] -> (Path -> Acquire ()) -> Acquire ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [Path]
paths (IO () -> Acquire ()
forall a. IO a -> Acquire a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> Acquire ()) -> (Path -> IO ()) -> Path -> Acquire ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Ptr ThruSections -> Ptr Wire -> IO ()
ThruSections.addWire Ptr ThruSections
thruSections (Ptr Wire -> IO ()) -> (Path -> Ptr Wire) -> Path -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Path -> Ptr Wire
rawPath)
        Maybe (V3 Double) -> (V3 Double -> Acquire ()) -> Acquire ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ Maybe (V3 Double)
end ((IO () -> Acquire ()
forall a. IO a -> Acquire a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> Acquire ())
-> (Ptr Vertex -> IO ()) -> Ptr Vertex -> Acquire ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Ptr ThruSections -> Ptr Vertex -> IO ()
ThruSections.addVertex Ptr ThruSections
thruSections) (Ptr Vertex -> Acquire ())
-> (V3 Double -> Acquire (Ptr Vertex)) -> V3 Double -> Acquire ()
forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< V3 Double -> Acquire (Ptr Vertex)
v3ToVertex)
        Ptr MakeShape -> Acquire (Ptr Shape)
MakeShape.shape (Ptr ThruSections -> Ptr MakeShape
forall a b. SubTypeOf a b => Ptr b -> Ptr a
upcast Ptr ThruSections
thruSections)

-- | Form a loft between a series of cross-sections.
loft :: Double  -- ^ The loft precision, this should be a small value, e.g @ 1e-6 @
    -> [Path] -- ^ Series of cross-sections that the loft will pass through
    -> Solid
loft :: Double -> [Path] -> Solid
loft Double
precision [Path]
paths = Double -> Maybe (V3 Double) -> [Path] -> Maybe (V3 Double) -> Solid
pointedLoft Double
precision Maybe (V3 Double)
forall a. Maybe a
Nothing [Path]
paths Maybe (V3 Double)
forall a. Maybe a
Nothing