\section{Curves} A curve is a one-dimensional figure in an arbitrary space. The Curve typeclass allows an arbitrary curve to be sampled so that it can be rendered iteratively in OpenGL. The Differentiable typeclass allows a Curve to be transformed into its derivative. \begin{code} {-# LANGUAGE GeneralizedNewtypeDeriving #-} module RSAGL.Curve (Curve, zipCurve, iterateCurve, transposeCurve, curve, Surface, surface, wrapSurface, unwrapSurface, pretransformCurve, pretransformCurve2, transposeSurface, zipSurface, iterateSurface, halfIterateSurface, pretransformSurface, flipTransposeSurface, uv_identity, surfaceDerivative, curveDerivative, surfaceNormals3D) where import Control.Arrow hiding (pure) import RSAGL.Vector import RSAGL.Auxiliary import RSAGL.Affine import Data.List import Control.Parallel.Strategies import Control.Applicative import RSAGL.AbstractVector \end{code} \subsection{The Curve} A \texttt{Curve} is either a simple parametric curve, or a derivative of a parametric curve. To approximate the derivative of a curve, we require a subtraction function that yields the derivative's type and a scalar multiplication function that operates on the base type. We also allow two curves to be zipped together. We can take the derivative of a curve an arbitrary number of times, but this will run up against the precision of the underlying data types, including Double. \begin{code} type CurveF a = (Double,Double) -> a type SurfaceF a = CurveF (CurveF a) newtype Curve a = Curve { fromCurve :: CurveF a } instance Functor Curve where fmap g (Curve f) = Curve $ g . f instance Applicative Curve where pure a = Curve $ const a f <*> a = zipCurve ($) f a instance (AffineTransformable a) => AffineTransformable (Curve a) where scale v = fmap (scale v) translate v = fmap (translate v) rotate vector angle = fmap (rotate vector angle) transform m = fmap (transform m) instance NFData (Curve a) where rnf (Curve f) = seq f () sampleCurve :: Curve a -> Double -> Double -> a sampleCurve (Curve f) = curry f iterateCurve :: Integer -> Curve x -> [x] iterateCurve n c = map f $ zeroToOne n where f = sampleCurve c (0.25/fromInteger n) zipCurve :: (x -> y -> z) -> Curve x -> Curve y -> Curve z zipCurve f (Curve x) (Curve y) = Curve $ \hu -> f (x hu) (y hu) mapCurve :: (CurveF a -> CurveF a) -> Curve a -> Curve a mapCurve f = Curve . f . fromCurve mapCurve2 :: (SurfaceF a -> SurfaceF a) -> Curve (Curve a) -> Curve (Curve a) mapCurve2 f = Curve . (Curve .) . f . (fromCurve .) . fromCurve pretransformCurve :: (Double -> Double) -> Curve a -> Curve a pretransformCurve g = mapCurve (\f (h,u) -> f (h,g u)) pretransformCurve2 :: (Double -> Double) -> (Double -> Double) -> Curve (Curve a) -> Curve (Curve a) pretransformCurve2 fx fy = mapCurve2 $ (\f x y -> f (second fx x) (second fy y)) transposeCurve :: Curve (Curve a) -> Curve (Curve a) transposeCurve = mapCurve2 flip curve :: (Double -> a) -> Curve a curve = Curve . uncurry . const \end{code} \subsection{Surfaces} \begin{code} newtype Surface a = Surface (Curve (Curve a)) deriving (NFData,AffineTransformable) surface :: (Double -> Double -> a) -> Surface a surface f = Surface $ curve (\x -> curve $ flip f x) wrapSurface :: Curve (Curve a) -> Surface a wrapSurface = Surface unwrapSurface :: Surface a -> Curve (Curve a) unwrapSurface (Surface s) = s transposeSurface :: Surface a -> Surface a transposeSurface (Surface s) = Surface $ transposeCurve s iterateSurface :: (Integer,Integer) -> Surface a -> [[a]] iterateSurface (u,v) (Surface s) = map (iterateCurve u) $ iterateCurve v s halfIterateSurface :: Integer -> Surface a -> [Curve a] halfIterateSurface u = iterateCurve u . unwrapSurface instance Functor Surface where fmap f (Surface x) = Surface $ fmap (fmap f) x instance Applicative Surface where pure a = surface (const $ const a) f <*> a = zipSurface ($) f a zipSurface :: (x -> y -> z) -> Surface x -> Surface y -> Surface z zipSurface f (Surface x) (Surface y) = Surface $ zipCurve (zipCurve f) x y pretransformSurface :: (Double -> Double) -> (Double -> Double) -> Surface a -> Surface a pretransformSurface fx fy = Surface . pretransformCurve2 fx fy . unwrapSurface flipTransposeSurface :: Surface a -> Surface a flipTransposeSurface = pretransformSurface id (1-) . transposeSurface uv_identity :: Surface (Double,Double) uv_identity = surface (curry id) \end{code} \subsection{Taking the Derivative of a Curve} \begin{code} curveDerivative :: (AbstractSubtract p v,AbstractScale v) => Curve p -> Curve v curveDerivative (Curve f) = Curve $ \(h,u) -> scalarMultiply (recip $ 2 * h) $ f (h/2,u+h) `sub` f (h/2,u-h) surfaceDerivative :: (AbstractSubtract p v,AbstractScale v) => Surface p -> Surface (v,v) surfaceDerivative s = zipSurface (,) (curvewiseDerivative s) (transposeSurface $ curvewiseDerivative $ transposeSurface s) where curvewiseDerivative (Surface t) = Surface $ fmap curveDerivative t surfaceNormals3D :: Surface Point3D -> Surface SurfaceVertex3D surfaceNormals3D s = SurfaceVertex3D <$> s <*> fmap (vectorNormalize . uncurry crossProduct) (surfaceDerivative s) \end{code}