{-# LANGUAGE ViewPatterns #-}
module Physics.Mesh where

import Control.Monad.State
import Data.Functor.Identity
import Data.Vec

newtype VertexIx = VertexIx Int
newtype EdgeIx = EdgeIx Int
newtype FaceIx = FaceIx Int

class TriangleMesh m where
    emptyMesh :: m

    insertVertex :: Vec3 Double -> m -> (VertexIx, m)
    insertFace :: VertexIx -> VertexIx -> VertexIx -> m -> (FaceIx, m)

    --edgesOfVertex :: VertexIx -> m -> [EdgeIx]
    facesOfVertex :: VertexIx -> m -> [FaceIx]
    --verticesOfEdge :: EdgeIx -> m -> (VertexIx, VertexIx)
    --facesOfEdge :: EdgeIx -> m -> (FaceIx, FaceIx)
    verticesOfFace :: FaceIx -> m -> (VertexIx, VertexIx, VertexIx)
    --edgesOfFace :: FaceIx -> m -> (EdgeIx, EdgeIx, EdgeIx)

    -- TODO: get normals

    vertexAt :: VertexIx -> m -> Vec3 Double

    mapVertices :: (Vec3 Double -> Vec3 Double) -> m -> m

    toTriangleList :: m -> [(Vec3 Double,Vec3 Double,Vec3 Double)]

-- | Return the normal of the triangle defined by three points, obeying the right-hand rule.
tnormal :: Vec3 Double -> Vec3 Double -> Vec3 Double -> Vec3 Double
tnormal a b c = normalize (cross (b-a) (c-a))

-- | Return the positive area of the triangle defined by three points.
tarea :: Vec3 Double -> Vec3 Double -> Vec3 Double -> Double 
tarea a b c = 0.5 * norm (cross (b-a) (c-a))

-- | A convenient 'State' monad for defining meshes.
type MeshBuilder m a = State m a

newVertex :: TriangleMesh m => Vec3 Double -> MeshBuilder m VertexIx 
newVertex v = StateT (Identity . insertVertex v)

newFace :: TriangleMesh m => VertexIx -> VertexIx -> VertexIx -> MeshBuilder m FaceIx
newFace a b c = StateT (Identity . insertFace a b c)

runMeshBuilder :: TriangleMesh m => MeshBuilder m a -> (a, m)
runMeshBuilder f = runState f emptyMesh

execMeshBuilder :: TriangleMesh m => MeshBuilder m a -> m
execMeshBuilder f = execState f emptyMesh

