{-# LANGUAGE Rank2Types, TypeOperators, FlexibleContexts, TypeFamilies , TypeSynonymInstances, FlexibleInstances, UndecidableInstances , MultiParamTypeClasses #-} {-# OPTIONS_GHC -Wall -fno-warn-orphans #-} ---------------------------------------------------------------------- -- | -- Module : Shady.ParamSurf -- Copyright : (c) Conal Elliott 2008, 2009 -- License : AGPLv3 -- -- Maintainer : conal@conal.net -- Stability : experimental -- -- Parametric surfaces with automatic normals ---------------------------------------------------------------------- -- This version uses Complex s instead of (s,s). Complex is consistent -- with Image but inconsistent with 1D and 3D. module Shady.ParamSurf where import Control.Applicative import Control.Arrow ((&&&)) import Data.NumInstances () import Data.VectorSpace import Data.Cross hiding (One,Two,Three) import Data.Derivative -- import Data.MemoTrie import Data.Basis import Shady.Language.Exp import Shady.Complex import Shady.ITransform (ITrans(..)) type HeightField s = Complex s -> s type Surf s = Complex s -> (s,s,s) type USurf = forall s. Floating s => Surf s type Curve2 s = s -> Complex s type Curve3 s = s -> (s,s,s) type Warp1 s = s -> s type Warp2 s = Complex s -> Complex s type Warp3 s = (s,s,s) -> (s,s,s) -- | Trig functions with unit period ([-1,1]) cosU, sinU :: Floating s => s -> s cosU = cos . (* pi) sinU = sin . (* pi) -- | Turn a height field into a surface hfSurf :: HeightField s -> Surf s hfSurf field = \ (u :+ v) -> (u, v, field (u :+ v)) -- | Like 'hfSurf' but for curve construction fcurve :: Warp1 s -> Curve2 s fcurve f = \ u -> u :+ f u -- | Unit circle. circle :: Floating s => Curve2 s circle = liftA2 (:+) cosU sinU -- | Half semi circle, with theta in [-pi/2,pi/2] semiCircle :: Floating s => Curve2 s semiCircle = circle . (/ 2) -- | Torus, given radius of sweep circle and cross section torus :: (Floating s, VectorSpace s, Scalar s ~ s) => s -> s -> Surf s -- torus sr cr = revolve (\ s -> (sr,0) ^+^ cr *^ circle s) torus sr cr = revolve (const (sr :+ 0) ^+^ const cr *^ circle) -- Surface of revolution, formed by rotation around Z axis. The curve is -- parameterized by u, and the rotation by v. In this generalized -- version, we have not a single curve, but a function from v to curves. revolveG :: Floating s => (s -> Curve2 s) -> Surf s revolveG curveF = \ (u :+ v) -> onXY (rotate (-pi*v)) (addY (curveF v) u) revolve :: Floating s => Curve2 s -> Surf s revolve curve = revolveG (const curve) -- A sphere is a revolved semi-circle sphere1 :: Floating s => Surf s sphere1 = revolve semiCircle -- | Profile product. profile :: Num s => Curve2 s -> Curve2 s -> Surf s profile curve prof (u :+ v) = (cx*px,cy*px,py) where cx :+ cy = curve u px :+ py = prof v -- More spheres sphere2,sphere3 :: Floating s => Surf s sphere2 = profile circle semiCircle sphere3 = profile semiCircle circle -- | Frustum, given base & cap radii and height. frustum :: (Floating s, VectorSpace s, Scalar s ~ s) => s -> s -> s -> Surf s frustum baseR topR h = profile circle rad where rad t = lerp baseR topR (t + 1/2) :+ h*t -- | Unit cylinder. Unit height and radii ucylinder :: (Floating s, VectorSpace s) => Surf s ucylinder = profile circle (const 1) -- | XY plane as a surface xyPlane :: Num s => Surf s xyPlane = hfSurf (const 0) -- | Given a combining op and two curves, make a surface. A sort of -- Cartesian product with combination. cartF :: (a -> b -> c) -> (s -> a) -> (s -> b) -> (Complex s -> c) cartF op f g = \ (u :+ v) -> f u `op` g v -- Sweep a basis curve by a sweep curve. Warning: does not reorient the -- basis curve as cross-section. TODO: Frenet frame. sweep :: VectorSpace s => Curve3 s -> Curve3 s -> Surf s sweep = cartF (^+^) -- | One period, unit height eggcrate eggcrateH :: Floating s => HeightField s eggcrateH = cartF (*) cosU sinU revolveH :: (Floating s, InnerSpace s, Scalar s ~ s) => Warp1 s -> HeightField s revolveH = (. magnitude) rippleH :: (Floating s, InnerSpace s, Scalar s ~ s) => HeightField s rippleH = revolveH sinU -- | Simple ripply pond shape ripple :: Floating s => Surf s ripple = -- onXY' (2 *^) $ revolve (const (0.5 :+ 0) - fcurve sinU) -- | Apply a displacement map at a value displaceV :: (InnerSpace v, s ~ Scalar v, Floating s, HasNormal v) => v -> Scalar v -> v displaceV v s = v ^+^ s *^ normal v -- | Apply a displacement map to a function (e.g., 'Curve2' or 'Surf') or -- other container. displace :: (InnerSpace v, Scalar v ~ s, Floating s, HasNormal v, Applicative f) => f v -> f (Scalar v) -> f v displace = liftA2 displaceV ---- Misc -- TODO: Reconcile this version with the one in Image rotate :: Floating s => s -> Warp2 s rotate theta = \ (x :+ y) -> (x * c - y * s) :+ (y * c + x * s) where c = cos theta s = sin theta addX, addY, addZ :: Num s => (a -> Complex s) -> (a -> (s,s,s)) addX = fmap (\ (y :+ z) -> (0,y,z)) addY = fmap (\ (x :+ z) -> (x,0,z)) addZ = fmap (\ (x :+ y) -> (x,y,0)) addYZ,addXZ,addXY :: Num s => (a -> s) -> (a -> (s,s,s)) addYZ = fmap (\ x -> (x,0,0)) addXZ = fmap (\ y -> (0,y,0)) addXY = fmap (\ z -> (0,0,z)) onX,onY,onZ :: Warp1 s -> Warp3 s onX f (x,y,z) = (f x, y, z) onY f (x,y,z) = (x, f y, z) onZ f (x,y,z) = (x, y, f z) onXY,onYZ,onXZ :: Warp2 s -> Warp3 s onXY f (x,y,z) = (x',y',z ) where x' :+ y' = f (x :+ y) onXZ f (x,y,z) = (x',y ,z') where x' :+ z' = f (x :+ z) onYZ f (x,y,z) = (x ,y',z') where y' :+ z' = f (y :+ z) onX',onY',onZ' :: Warp1 s -> (a -> (s,s,s)) -> (a -> (s,s,s)) onX' = fmap . onX onY' = fmap . onY onZ' = fmap . onZ onXY',onXZ',onYZ' :: Warp2 s -> (a -> (s,s,s)) -> (a -> (s,s,s)) onXY' = fmap . onXY onXZ' = fmap . onXZ onYZ' = fmap . onYZ {-------------------------------------------------------------------- Normals and tessellation --------------------------------------------------------------------} -- -- | Derivative tower of point on a surface -- type SurfPt = Exp R2 :> Exp R3 -- -- | Differentiable surface -- type SurfD = Surf (Exp R2 :> Exp R) -- -- | Vertex and normal -- data VN = VN (Exp R3) (Exp R3) -- -- No instances for (HasBasis (E V R2), -- -- HasTrie (Basis (E V R2)), -- -- HasNormal SurfPt) -- toVN :: SurfPt -> VN -- toVN v = VN (powVal v) (powVal (normal v)) -- TODO: move to Exp and remove -fno-warn-orphans type V2 a = (a,a) type V3 a = (a,a,a) type ER = FloatE type ER2 = V2 ER type ER3 = V3 ER instance HasBasis FloatE where type Basis FloatE = () basisValue () = 1 decompose s = [((),s)] decompose' s = const s instance HasBasis R2E where type Basis R2E = Basis ER2 basisValue b = vec2 x y where (x,y) = basisValue b decompose w = decompose (getX w, getX w) decompose' w = (w <.>) . basisValue -- TODO: are these instances used? -- TODO: move these two HasBasis orphans elsewhere. -- instance IsNat n => HasBasis (VecE n R) where -- type Basis (VecE n R) = n -- basisValue = ??? -- TODO: fill out this definition. How to enumerate a basis for Vec n R, -- for arbitrary IsNat n? type TR = ER :> ER -- tower type T = ER2 :> ER -- Standard do-nothing transformation instance ITrans (Complex T) T where (*:) = const id -- | Derivative towers of point on a surface type SurfPt = V3 T -- type SurfPt = ER2 :> ER3 -- | Differentiable surface type SurfD = Surf T -- -- | Vertex and normal -- data VN = VN ER3 ER3 -- powVal3 :: V3 (a :> b) -> V3 b -- powVal3 (q,r,s) = (powVal q, powVal r, powVal s) -- toVN :: SurfPt -> VN -- toVN v = VN (powVal3 v) (powVal3 (normal v)) -- -- type SurfV = ER2 :~> ER3 -- type SurfVN = ER2 -> VN -- -- or -- -- type SurfVN = Exp R2 -> (Exp R3, Exp R3) -- surfVN :: SurfD -> SurfVN -- surfVN f p = toVN (f (fstD p, sndD p)) -- | Vertex and normal type VN = (R3E, R3E) toVN :: SurfPt -> VN toVN = p3 &&& (p3 . normal) where p3 (q,r,s) = vec3 (powVal q) (powVal r) (powVal s) -- type SurfV = ER2 :~> ER3 type SurfVN = R2E -> VN surfVN :: SurfD -> SurfVN surfVN f p = toVN (f (fstD p' :+ sndD p')) where p' = (getX p, getY p)