\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] -- 'w' could be 1, but this gives a smoother tesselation 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 (u-h/2,u+h/2) (0,1) c \end{code}