{-# OPTIONS_GHC -fglasgow-exts #-}

module RSAGL.Modeling.Deformation
    (Deformation,DeformationClass(..),constrain)
    where

import RSAGL.Math.Vector
import RSAGL.Math.Matrix
import RSAGL.Math.Affine
import RSAGL.Auxiliary.Auxiliary
import RSAGL.Scene.CoordinateSystems

-- | A deformation of a surface.  These come in two types: a deformation that
-- modifies only the shape of a surface, leaving the normal vectors to
-- be recalculated automatically, or a deformation that modified both
-- the shape and the normal vectors explicitly.
--
-- For example, the deformation function of type @(Point3D -> Ponit3D)@
-- depends on automatic differentiation to determine the new surface normals.
--
-- On the other hand, an affine transformation applied as a deformation
-- can quickly and correctly compute the new surface normals.
--
-- Finally, one might want to construct a deformation that modifies
-- the surface normals while leaving the shape intact -- this is
-- perfectly legal.
--
type Deformation = Either (SurfaceVertex3D -> Point3D) (SurfaceVertex3D -> SurfaceVertex3D)

-- | A convenienve class to convert common descriptions of deformations
-- into the canonical representation.
class DeformationClass a where
    deformation :: a -> Deformation

instance (DeformationClass a,DeformationClass b) =>
         DeformationClass (Either a b) where
    deformation = either deformation deformation

instance DeformationClass Matrix where
    deformation m = Right (\s -> transform m s)

instance DeformationClass (Point3D -> Point3D) where
    deformation f = Left (\(SurfaceVertex3D p _) -> f p)

instance DeformationClass (Point3D -> SurfaceVertex3D) where
    deformation f = Right (\(SurfaceVertex3D p _) -> f p)

instance DeformationClass (SurfaceVertex3D -> Point3D) where
    deformation f = Left f

instance DeformationClass (SurfaceVertex3D -> SurfaceVertex3D) where
    deformation f = Right f

instance DeformationClass (Point3D -> Affine) where
    deformation f = Left (\(SurfaceVertex3D p _) ->
                        affine_transformation (f p) p)

instance DeformationClass (SurfaceVertex3D -> Affine) where
    deformation f = Left (\s -> affine_transformation (f s) $ sv3d_position s)