```module RSAGL.Modeling.Extrusion
(extrude,
extrudeTube,
extrudePrism)
where

import RSAGL.Math.Curve
import RSAGL.Math.CurveExtras
import RSAGL.Math.Vector
import RSAGL.Math.Affine
import Control.Applicative
import RSAGL.Scene.CoordinateSystems
import RSAGL.Math.Orthogonal
import Data.Maybe
import RSAGL.Math.Types

-- | The most general form of an extrusion.  Requires first a
-- control curve for orientation, for example the center of
-- a torus or a vector that simply never runs parallel
-- to the spine.  Second, the spine of the extrusion,
-- and third the shape to be extruded.
--
-- The +Y axis of the shape will be oriented toward
-- the control curve.
extrude :: Curve (Either Point3D Vector3D) ->
Curve Point3D ->
Curve (Curve Point3D) ->
Surface Point3D
extrude upish spine loop = wrapSurface \$
transformation <\$>
(modelLookAt <\$> spine <*>
(forward <\$> Right <\$> spine') <*>
(up <\$> upish)) <*>
loop
where spine' = curveDerivative spine

-- | A tubular extrusion using taking a radius and a spine.
extrudeTube :: Curve RSdouble -> Curve Point3D -> Surface Point3D
extrude upish
spine
(scale' <\$> radius <*> pure circleXY)
where upish = pure \$ Right \$ fromMaybe
(let [a,b] = iterateCurve 2 spine
in fst \$ orthos \$ vectorToFrom a b) \$
newellCurve spine

-- | An extrusion whose spine is a straight line, with radii specified
-- at both ends.  In this case only the extruded shape needs to be
-- specified as a curve.
extrudePrism :: Vector3D -> (Point3D,RSdouble) -> (Point3D,RSdouble) -> Curve Point3D -> Surface Point3D
extrudePrism upish (a,ra) (b,rb) c =
extrude (pure \$ Right \$ upish)
(linearInterpolation [a,b])
(flip scale' c <\$> linearInterpolation [ra,rb])

```