content: {-|
  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
  ( pointedLoftWithPrecision
  , pointedLoft
  , loft
  ) where
  
  import Linear (V3 (..))
  import Waterfall.Internal.Path (Path, rawPath)
  import Waterfall.Internal.Solid (Solid (..), solidFromAcquire)
  import Waterfall.Internal.ToOpenCascade (v3ToVertex)
  import Waterfall.Internal.Path.Common (rawPathWire)
  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_, (<=<))
  
  -- | like `pointedLoft`, but allows the user to set the precision used by the underlying algorithm
  pointedLoftWithPrecision :: 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
  pointedLoftWithPrecision precision start paths end = 
      solidFromAcquire $ do
          thruSections <- ThruSections.new True False precision
          forM_ start ((liftIO . ThruSections.addVertex thruSections) <=< v3ToVertex)
          forM_ paths (traverse (liftIO . ThruSections.addWire thruSections) . rawPathWire . rawPath)
          forM_ end ((liftIO . ThruSections.addVertex thruSections) <=< v3ToVertex)
          MakeShape.shape (upcast thruSections)
  
  -- | 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 :: 
      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 = pointedLoftWithPrecision 1e-6
  
  -- | Form a loft between a series of cross-sections.
  loft :: 
      [Path] -- ^ Series of cross-sections that the loft will pass through
      -> Solid
  loft paths = pointedLoft Nothing paths Nothing