-- Implicit CAD. Copyright (C) 2011, Christopher Olah (chris@colah.ca)
-- Released under the GNU GPL, see LICENSE

{-# LANGUAGE FlexibleInstances, FlexibleContexts #-}

module Graphics.Implicit.Operations (
	translate, 
	scale,
	complement,
	union,  intersect,  difference,
	unionR, intersectR, differenceR,
	shell,
	slice,
	bubble,
	extrude,
	extrudeR,
	extrudeOnEdgeOf
) where

import Prelude hiding ((+),(-),(*),(/))
import Graphics.Implicit.Definitions
import Graphics.Implicit.MathUtil
import Graphics.Implicit.SaneOperators


-- | Translate an object by a vector of appropriate dimension. 
translate :: 
	(Additive a a a, AdditiveInvertable a)
	=> a             -- ^ Vector to translate by (Also: a is a vector, blah, blah)
	-> (a -> )   -- ^ Object to translate
	-> (a -> )   -- ^ Resulting object
translate p obj = \q -> obj (q-p)

-- | Scale an object
scale :: (Multiplicative a  a) => 
	            -- ^ Amount to scale by
	-> (a -> )  -- ^ Object to scale
	-> (a -> )  -- ^ Resulting scaled object
scale s obj = \p -> s * obj (p/s)

complement :: 
	(a -> )     -- ^ Object to complement
	-> (a -> )  -- ^ Result
complement obj = \p -> - obj p

shell :: 
	             -- ^ width of shell
	-> (a -> )   -- ^ object to take shell of
	-> (a -> )   -- ^ resulting shell
shell w a = \p -> abs (a p) - w/(2.0::)

-- | Rounded union
unionR :: 
	           -- ^ The radius of rounding
	-> [a -> ] -- ^ objects to union
	-> (a -> ) -- ^ Resulting object
unionR r objs = \p -> rminimum r $ map ($p) objs

-- | Rounded minimum
intersectR :: 
	           -- ^ The radius of rounding
	-> [a -> ] -- ^ Objects to intersect
	-> (a -> ) -- ^ Resulting object
intersectR r objs = \p -> rmaximum r $ map ($p) objs

-- | Rounded difference
differenceR :: 
	           -- ^ The radius of rounding
	-> [a -> ] -- ^ Objects to difference 
	-> (a -> ) -- ^ Resulting object
differenceR r (x:xs) = \p -> rmaximum r $ (x p) :(map (negate . ($p)) xs)


-- | Union a list of objects
union :: 
	[a -> ] -- ^ List of objects to union
	-> (a -> ) -- ^ The object resulting from the union
union objs = \p -> minimum $ map ($p) objs

-- | Intersect a list of objects
intersect :: 
	[a -> ] -- ^ List of objects to intersect
	-> (a -> ) -- ^ The object resulting from the intersection
intersect objs = \p -> maximum $ map ($p) objs

-- | Difference a list of objects
difference :: 
	[a -> ] -- ^ List of objects to difference
	-> (a -> ) -- ^ The object resulting from the difference
difference (obj:objs) = \p -> maximum $ map ($p) $ obj:(map complement objs)

-- | Slice a 3D objects at a given z value to make a 2D object.
slice :: 
	         -- ^ z-level to cut at
	-> Obj3   -- ^ 3D object to slice from
	-> Obj2   -- ^ Resulting 2D object
slice z obj = \(a,b) -> obj (a,b,z)

-- | Bubble out a 2D object into a 3D one.
bubble ::  -> Obj2 -> Obj3
bubble s obj = 
	let
		spsqrt n = signum n * sqrt (abs n)
		spsq n = signum n * n ** 2
	in
		\(x,y,z) -> spsqrt ( z ** 2 + s * obj (x,y) )

-- | Extrude a 2D object. (The extrusion goes into the z-plane)
extrude :: 
	          -- ^ Length to extrude
	-> Obj2    -- ^ 2D object to extrude
	-> Obj3    -- ^ Resulting 3D object
extrude h obj = \(x,y,z) -> max (obj (x,y)) (abs (z + h/(2.0 ::  )) - h)

-- | Rounded extrude. Instead of the extrude having a flat top or bottom, it is bevelled.
extrudeR ::
	          -- ^ Radius of rounding
	->        -- ^ Length to extrude
	-> Obj2    -- ^ 2D object to extrude
	-> Obj3    -- ^ Resulting 3D object
extrudeR r h obj = \(x,y,z) -> rmax r (obj (x,y)) (abs (z + h/(2.0 :: )) - h)

-- | Create a 3D object by extruding a 2D object along the edge of another 2D object.
-- For example, extruding a circle on the edge of another circle would make a torus.
extrudeOnEdgeOf :: 
	Obj2     -- ^ Object to extrude
	-> Obj2  -- ^ Object to extrude along the edge of
	-> Obj3  -- ^ Resulting 3D object
extrudeOnEdgeOf a b = \(x,y,z) -> a (b (x,y), z)