\section{Pre-specified Colors, Models, Materials and Deformations} \begin{code}

{-# OPTIONS_GHC -fno-warn-missing-signatures #-}

module RSAGL.Modeling.ModelingExtras
    (smoothbox,
     regularPrism,
     heightField,
     heightDisc,
     rotationGroup,
     glass,
     plastic,
     metallic,
     pattern,
     cloudy,
     blinkBoxes,
     spherical,
     directional,
     gradient,
     bumps,
     waves,
     heightMap,
     disregardSurfaceNormals,
     ColorFunction,
     Pattern,
     dropRandomElements,
     module RSAGL.Modeling.Material,
     module RSAGL.Auxiliary.ApplicativeWrapper,
     module Control.Applicative)
    where

import RSAGL.Modeling.Noise
import RSAGL.Color
import RSAGL.Color.RSAGLColors
import Control.Applicative
import RSAGL.Auxiliary.ApplicativeWrapper
import RSAGL.Math.Vector
import RSAGL.Modeling.Material
import RSAGL.Math.Affine
import RSAGL.Modeling.Model
import System.Random
import RSAGL.Math.Interpolation
import Data.Monoid
import RSAGL.Auxiliary.Auxiliary
import RSAGL.Math.Angle
import RSAGL.Math.Ray
import RSAGL.Math.AbstractVector
import RSAGL.Math.Types
\end{code} \subsection{Colors} \texttt{RSAGL.ModellingSupport} exports the contents of \texttt{rsagl-colors.txt}. This file is translated to HTML and Haskell source by the script in \texttt{ProcessColors.hs}. Color samples can be viewed by opening the file \texttt{rsagl-colors.html} in a web browser. With the exception of \texttt{blackbody}, all of the colors contain non-zero values for the red, green, and blue components, so that extremely bright lights will (realistically) wash those colors out to white. \subsection{Models} \texttt{smoothbox} is a box that takes an extra smoothing parameter between 0 and 1. This box doesn't have perfectly flat normals, and may therefore be a little easier on the eye. \begin{code}
smoothbox :: (Monoid attr) => RSdouble -> Point3D -> Point3D -> Modeling attr
smoothbox u p q = model $
    do box p q
       deform $ \(SurfaceVertex3D point vector) -> SurfaceVertex3D point $ vectorNormalize $ lerp u (vector,vectorNormalize $ vectorToFrom point midpoint)
        where midpoint = lerp 0.5 (p,q)
\end{code} \texttt{regularPrism} constructs a regular n-sided prism or pyramid. \begin{code}
regularPrism ::(Monoid attr) => (Point3D,RSdouble) -> (Point3D,RSdouble) -> Integer -> Modeling attr
regularPrism (a,ra) (b,rb) n = 
    model $ translate (vectorToFrom a origin_point_3d) $ 
        rotateToFrom (Vector3D 0 1 0) (vectorToFrom b a) $ sequence_ $ rotationGroup (Vector3D 0 1 0) n $ quad
  where a1 = Point3D 0 0 ra
        a2 = rotateY (fromRotations $ recip $ fromInteger n) a1
        b1 = Point3D 0 (distanceBetween a b) rb
        b2 = rotateY (fromRotations $ recip $ fromInteger n) b1
        quad = quadralateral a1 a2 b2 b1
\end{code} \begin{code}
-- | A rectangular height field rising off of the x-z plane.
heightField :: (Monoid attr) => (RSdouble,RSdouble) -> (RSdouble,RSdouble) -> ((RSdouble,RSdouble) -> RSdouble) -> Modeling attr
heightField (x1,z1) (x2,z2) f = model $
    do quadralateral (Point3D x1 0 z1) (Point3D x1 0 z2) (Point3D x2 0 z2) (Point3D x2 0 z1)
       heightMap f
       
\end{code} \begin{code}
-- | A circular height field rising off of the x-z plane.
heightDisc :: (Monoid attr) => (RSdouble,RSdouble) -> RSdouble -> ((RSdouble,RSdouble) -> RSdouble) -> Modeling attr
heightDisc (x,y) r f = model $
    do closedDisc (Point3D x 0 y) (Vector3D 0 1 0) r
       heightMap f
\end{code} \texttt{rotationGroup} rotates a model repeatedly. \begin{code}
rotationGroup :: (AffineTransformable a) => Vector3D -> Integer -> a -> [a]
rotationGroup v n m = map (flip (rotate v) m . fromRotations) $ tail $ zeroToOne (n+1)
\end{code} \subsection{Patterns} \texttt{cloudy} is a pattern made using perlin noise. \texttt{spherical} is a pattern that ranges from the center of a sphere to its radius, where the center maps to zero and the radius maps to one. \texttt{directional} is a pattern based on the directional (infinite) light source. An object rendered with an emissive layer defined by a directional light source will seem to be lit from that direction. \begin{code}
type ColorFunction a = ApplicativeWrapper ((->) SurfaceVertex3D) a

type Pattern = SurfaceVertex3D -> RSdouble

pattern :: (AbstractVector a) => Pattern -> [(RSfloat,ColorFunction a)] -> ColorFunction a
pattern _ [(_,constant_pattern)] = constant_pattern
pattern f color_map = wrapApplicative (\sv3d -> toApplicative (lerpMap color_map $ f2f $ f sv3d) $ sv3d)

cloudy :: Int -> RSdouble -> Pattern
cloudy seed wave_length (SurfaceVertex3D p _) = perlinNoise (translate offset $ scale' frequency p) + 0.5
    where frequency = recip wave_length
          offset = vectorNormalize $ fst $ randomXYZ (-1000.0*wave_length,1000.0*wave_length) (mkStdGen seed)

blinkBoxes :: Int -> RSdouble -> RSdouble -> RSdouble -> Pattern
blinkBoxes seed box_size chaos threshold = thresholdF . cloudy seed (recip chaos) . toLatticeCoordinates
    where thresholdF u = if u > threshold then 1.0 else 0.0
          toLatticeCoordinates (SurfaceVertex3D (Point3D x y z) v) = 
              SurfaceVertex3D (Point3D (to1LatticeCoordinate x) (to1LatticeCoordinate y) (to1LatticeCoordinate z)) v
          to1LatticeCoordinate u = fromInteger $ round $ u/box_size

spherical :: Point3D -> RSdouble -> Pattern
spherical center radius (SurfaceVertex3D p _) = distanceBetween center p / radius

directional :: Vector3D -> Pattern
directional vector (SurfaceVertex3D _ v) = dotProduct (vectorNormalize v) normalized_vector
    where normalized_vector = vectorNormalize vector

gradient :: Point3D -> Vector3D -> Pattern
gradient center vector (SurfaceVertex3D p _) = distanceAlong (Ray3D center vector) p
\end{code} \subsection{Materials} \begin{code}
glass :: RGBFunction -> MaterialM attr ()
glass rgbf =
    do transparent $ (alpha 0.05 . transformColor) <$> rgbf
       specular 100 $ (\rgb_color ->
           curry (lerp $ linear_value $ viewChannel channel_luminance rgb_color)
                      rgb_color white) <$> rgbf

plastic :: RGBFunction -> MaterialM attr ()
plastic rgbf =
    do pigment rgbf
       specular 50 (pure white)

metallic :: RGBFunction -> MaterialM attr ()
metallic rgbf =
    do pigment rgbf
       specular 75 rgbf
\end{code} \subsection{Deformations} \texttt{bumps} can be used to describe any deformation in which vertices are purturbed in the direction of their normal vectors. \texttt{waves} is a deformation that makes little waves in a surface. \begin{code}
bumps :: Pattern -> Modeling attr
bumps f = deform $ \(sv3d@(SurfaceVertex3D p v)) -> translate (vectorScale (f sv3d) v) p

waves :: RSdouble -> RSdouble -> Pattern
waves wave_length amplitude (SurfaceVertex3D (Point3D x y z) _) = (wave_f x + wave_f y + wave_f z) * amplitude / 3
    where wave_f u = sin (u / wave_length * 2*pi)

-- | Raises or lowers each point in a model along the y-axis according to its (x,z) coordinate.
-- Typically this is used to construct height fields.
--
heightMap :: ((RSdouble,RSdouble) -> RSdouble) -> Modeling attr
heightMap f = deform $ \(Point3D x y z) -> Point3D x (y + f (x,z)) z

-- | For models where we are certain surface normals don't matter, then don't calculate them.
disregardSurfaceNormals :: Modeling attr
disregardSurfaceNormals = deform $ \(SurfaceVertex3D p _) -> SurfaceVertex3D p (Vector3D 0 1 0)
\end{code}