\section{Specific and Interpolated Curves}
\begin{code}
module RSAGL.Math.CurveExtras
(sphericalCoordinates,
cylindricalCoordinates,
toroidalCoordinates,
circularCoordinates,
polarCoordinates,
transformUnitSquareToUnitCircle,
transformUnitCubeToUnitSphere,
circleXY,
regularPolygon,
linearInterpolation,
loopedLinearInterpolation,
smoothCurve,
loopCurve)
where
import RSAGL.Math.Curve
import RSAGL.Math.Interpolation
import RSAGL.Math.Vector
import RSAGL.Math.Angle
import RSAGL.Math.AbstractVector
import RSAGL.Math.Affine
import RSAGL.Math.ListUtils
import Control.Arrow
import RSAGL.Math.Types
\end{code}
\subsection{Alternate Coordinate Systems for Models}
\begin{code}
sphericalCoordinates :: ((Angle,Angle) -> a) -> Surface a
sphericalCoordinates f = transformSurface2 id (clampCurve (0,1)) $ surface $ curry (f . (\(u,v) -> (fromRadians $ u*2*pi,fromRadians $ ((pi/2) v*pi))))
cylindricalCoordinates :: ((Angle,RSdouble) -> a) -> Surface a
cylindricalCoordinates f = transformSurface2 id (clampCurve (0,1)) $ surface $ curry (f . (\(u,v) -> (fromRadians $ u*2*pi,v)))
toroidalCoordinates :: ((Angle,Angle) -> a) -> Surface a
toroidalCoordinates f = surface $ curry (f . (\(u,v) -> (fromRadians $ u*2*pi,fromRadians $ negate $ v*2*pi)))
circularCoordinates :: ((RSdouble,RSdouble) -> a) -> Surface a
circularCoordinates f = surface $ curry $ (f . second negate . transformUnitSquareToUnitCircle)
polarCoordinates :: ((Angle,RSdouble) -> a) -> Surface a
polarCoordinates f = circularCoordinates (f . cartesianToPolar)
\end{code}
\subsection{Transformations Between Unit Volumes}
\begin{code}
transformUnitSquareToUnitCircle :: (RSdouble,RSdouble) -> (RSdouble,RSdouble)
transformUnitSquareToUnitCircle (u,v) = (x,z)
where (Point3D x _ z) = transformUnitCubeToUnitSphere (Point3D u 0.5 v)
transformUnitCubeToUnitSphere :: Point3D -> Point3D
transformUnitCubeToUnitSphere p =
let p_centered@(Point3D x y z) = scale' 2.0 $ translate (Vector3D (0.5) (0.5) (0.5)) p
p_projected = scale' (minimum [recip $ abs x,recip $ abs y,recip $ abs z]) p_centered
k = recip $ distanceBetween origin_point_3d p_projected
w = maximum $ [abs x, abs y, abs z]
in if p_centered == origin_point_3d then origin_point_3d else lerp w (p_centered,scale' k p_centered)
\end{code}
\subsection{Circles}
\begin{code}
circleXY :: Curve Point3D
circleXY = curve $ \u_ -> let u = fromRotations u_ in Point3D (cosine u) (sine u) 0
\end{code}
\subsection{Regular Polygons}
A regular polygon, centered at the origin, in the XY plane.
\begin{code}
regularPolygon :: Integer -> Curve Point3D
regularPolygon n = loopedLinearInterpolation $ map (flip rotateZ (Point3D 0 1 0) . fromRotations) $ zeroToOne n
\end{code}
\subsection{Piecewise Length Normalization}
\begin{code}
normalizePolyline :: (AbstractSubtract p v,AbstractMagnitude v) => [p] -> [(RSdouble,p)]
normalizePolyline pts = zip (map (/ total_dist) accumulated_dists) pts
where dists = map (uncurry abstractDistance) $ doubles pts
total_dist = last accumulated_dists
accumulated_dists = scanl (+) 0 dists
\end{code}
\subsection{Interpolated Curves}
\begin{code}
linearInterpolation :: (AbstractSubtract p v,AbstractAdd p v,AbstractMagnitude v,AbstractScale v) => [p] -> Curve p
linearInterpolation = curve . lerpMap . normalizePolyline
loopedLinearInterpolation :: (AbstractSubtract p v,AbstractAdd p v,AbstractMagnitude v,AbstractScale v) => [p] -> Curve p
loopedLinearInterpolation = loopCurve (0,1) . linearInterpolation . (\a -> last a:a)
\end{code}
\subsection{Smoothing Curves}
\texttt{smoothCurve i h} takes i samples of an h-long piece of a 'Curve' at each point to smooth it. This is not an interpolation function and will tend to shrink shapes
toward their center of gravity.
\begin{code}
smoothCurve :: (AbstractAdd p v,AbstractSubtract p v,AbstractVector v,AbstractZero p) => Integer -> RSdouble -> Curve p -> Curve p
smoothCurve i h c = curve $ \u -> abstractAverage $ iterateCurve i $ controlCurve (uh/2,u+h/2) (0,1) c
\end{code}