packages feed

Michelangelo-0.2.2.0: src/Graphics/Michelangelo/Shapes.hs

-- |
-- Module      : Graphics.Michelangelo.Shapes
-- Description :
-- Copyright   : (c) Jonatan H Sundqvist, 2015
-- License     : MIT
-- Maintainer  : Jonatan H Sundqvist
-- Stability   : experimental|stable
-- Portability :
--

-- Created July 30 2015

-- TODO | - Generic container types (eg. Monoids or OverloadedLists?)
--        - Generic vertex types (?)
--        - Anchored shapes (?)
--        - Bounding boxes
--        - Indexed shapes
--        - Triangulation (or - more generally - tiling) of polygons
--        - QuickCheck
--        - Performance
--        - Types to represent surfaces, edges, etc.
--        - Different coordinate systems and orientations (eg. clockwise vs counter-clockwise)
--        - Generating colours (eg. monochrome) and creating data that is easily uploaded to the GPU

-- SPEC | -
--        -



module Graphics.Michelangelo.Shapes where



--------------------------------------------------------------------------------------------------------------------------------------------
-- We'll need these
--------------------------------------------------------------------------------------------------------------------------------------------
-- import Data.Monoid

import Graphics.Michelangelo.Types



--------------------------------------------------------------------------------------------------------------------------------------------
-- Types
--------------------------------------------------------------------------------------------------------------------------------------------

-- | A 'Face' is a list of vertices
-- TODO: Polymorphic container
-- TODO: Make sure all vertices lie in the same plane (using Triangles?)
data Face v = Face [v]


-- |
data Edge v = Edge v v



--------------------------------------------------------------------------------------------------------------------------------------------
-- Data
--------------------------------------------------------------------------------------------------------------------------------------------

-- | TODO: Factor out
π :: Floating f => f
π = pi



--------------------------------------------------------------------------------------------------------------------------------------------
-- Functions
--------------------------------------------------------------------------------------------------------------------------------------------

-- Vertex constructors ---------------------------------------------------------------------------------------------------------------------

-- | A simple list of vertices
vlist :: a -> a -> a -> [a]
vlist x y z = [x, y, z]

-- Tessellation ----------------------------------------------------------------------------------------------------------------------------

-- | Tessellate a polygon with triangles. Presently, only concave shapes will tessellate properly.
-- TODO: Make it work for any shape (convex and concave)
triangles :: [a] -> [[a]]
triangles (a:rest) = pairwise (\b c -> [a, b, c]) rest
  where
    -- | Combine every adjacent pair in the list with the given function
    pairwise :: (a -> a -> b) -> [a] -> [b]
    pairwise f xs = zipWith f xs (drop 1 xs)

-- Two-dimensional shapes ------------------------------------------------------------------------------------------------------------------

-- | Generate the vertices for a rectangular plane, centred at (0,0,0)
plane :: Fractional f => (f -> f -> f -> a) -> f -> f -> f -> [a]
plane f dx dy dz = [f (-dx/2) (dy/2) (dz/2), f (dx/2) (dy/2) (dz/2), f (dx/2) (-dy/2) (-dz/2), f (-dx/2) (-dy/2) (-dz/2)]


-- | Generate the vertices for a rectangular plane, parallel with the X and Y axes and centred at (0,0,0)
-- TODO: General 'plane' function
planeXY :: Fractional f => (f -> f -> f -> a) -> f -> f -> [a]
planeXY f dx dy = plane f dx dy 0
-- planeXY dx dy = [f (-dx/2) (dy/2) 0, f (dx/2) (dy/2) 0, f (dx/2) (-dy/2) 0, f (-dx/2) (-dy/2) 0]


-- | Generate the vertices for a rectangular plane, parallel with the X and Z axes and centred at (0,0,0)
-- TODO: General 'plane' function
planeXZ :: Fractional f => (f -> f -> f -> a) -> f -> f -> [a]
planeXZ f dx dz = plane f dx 0 dz


-- | Generate the vertices for a rectangular plane, parallel with the Y and Z axes and centred at (0,0,0)
-- TODO: General 'plane' function
planeYZ :: Fractional f => (f -> f -> f -> a) -> f -> f -> [a]
planeYZ f dy dz = plane f 0 dy dz


-- | Generate the vertices for a regular polygon, centred at (0,0,0)
polygon :: (Floating f, Integral i) => (f -> f -> a) -> i -> f -> [a]
polygon f sides radius = [ let θ = fromIntegral n * 2*π/fromIntegral sides in f (radius*cos θ) (radius*sin θ) | n <- [0..(sides-1)] ]

-- Three-dimensional shapes ----------------------------------------------------------------------------------------------------------------

-- | Generate the vertices of an axis-aligned cuboid centred at (0,0,0)
-- TODO: Use combinatorics to generate vertices (?)
cuboid :: Fractional f => (f -> f -> f -> a) -> f -> f -> f -> [a]
cuboid f dx dy dz = [f (-hdx)   hdy  hdz, f hdx   hdy  hdz, f hdx   hdy  (-hdz), f (-hdx)   hdy  (-hdz), --
                     f (-hdx) (-hdy) hdz, f hdx (-hdy) hdz, f hdx (-hdy) (-hdz), f (-hdx) (-hdy) (-hdz)] --
  where
    (hdx, hdy, hdz) = (dx/2, dy/2, dz/2)


-- | Generate the vertices of an axis-aligned cube centred at (0,0,0)
cube :: Fractional f => (f -> f -> f -> a) -> f -> [a]
cube f side = cuboid f side side side

-- Indices ---------------------------------------------------------------------------------------------------------------------------------

-- | Indices for a solid cuboid (with triangulated sides, cf. `cuboid`)
-- TODO: Use more sensible container types (three levels of lists seems a bit excessive...)
-- TODO: Allow any kind of tessellation (?)
cuboidIndices :: Integral i => [[[i]]]
cuboidIndices = map triangles [[0,1,2,3], [4,5,6,7], [3,2,6,7], [0,1,5,4], [0,3,7,4], [1,2,6,5]] -- Top, bottom, Front, Back, Left, Right