chiphunk-0.1.2.0: src/Chiphunk/Low/Types.chs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
-- | Description: Basic Chipmunk data types.
-- Module provides basic Chipmunk data types.
module Chiphunk.Low.Types
( Vect (..)
, VectPtr
, BB (..)
, BBPtr
, DataPtr
, Body (..)
, BodyType (..)
, Space (..)
, Shape (..)
, Constraint (..)
, Arbiter (..)
, Transform (..)
, TransformPtr
, CollisionType
, CPBool
, mkStateVar
, Polyline(..)
, PolylinePtr
, PolylineSet(..)
, PolylineSetPtr
, withPolylinePtr
, peekPolylineSet
) where
import Data.Cross
import Data.Hashable
import Data.StateVar
import Data.VectorSpace
import Foreign
import GHC.Generics (Generic)
#include <chipmunk/chipmunk.h>
-- | 2D vector packed into a struct.
data Vect = Vect
{ vX :: !Double, vY :: !Double
} deriving (Eq, Show, Ord, Generic)
instance Hashable Vect
instance AdditiveGroup Vect where
zeroV = Vect 0 0
negateV (Vect x y) = Vect (-x) (-y)
Vect x1 y1 ^+^ Vect x2 y2 = Vect (x1 + x2) (y1 + y2)
Vect x1 y1 ^-^ Vect x2 y2 = Vect (x1 - x2) (y1 - y2)
instance VectorSpace Vect where
type Scalar Vect = Double
f *^ Vect x y = Vect (f * x) (f * y)
instance InnerSpace Vect where
Vect x1 y1 <.> Vect x2 y2 = x1 * x2 + y1 * y2
instance HasCross2 Vect where
cross2 (Vect x y) = Vect (-y) x
instance Storable Vect where
sizeOf _ = {# sizeof cpVect #}
alignment _ = {# alignof cpVect #}
poke p (Vect x y) = do
{# set cpVect->x #} p $ realToFrac x
{# set cpVect->y #} p $ realToFrac y
peek p = Vect <$> (realToFrac <$> {# get cpVect->x #} p)
<*> (realToFrac <$> {# get cpVect->y #} p)
-- | Pointer to vector.
{# pointer *cpVect as VectPtr -> Vect #}
-- | Simple bounding box struct. Stored as left, bottom, right, top values.
data BB = BB
{ bbL :: !Double, bbB :: !Double, bbR :: !Double, bbT :: !Double
} deriving (Show, Eq, Ord, Generic)
instance Hashable BB
instance Storable BB where
sizeOf _ = {# sizeof cpBB #}
alignment _ = {# alignof cpBB #}
poke p (BB l b r t) = do
{# set cpBB->l #} p $ realToFrac l
{# set cpBB->b #} p $ realToFrac b
{# set cpBB->r #} p $ realToFrac r
{# set cpBB->t #} p $ realToFrac t
peek p = BB <$> (realToFrac <$> {# get cpBB->l #} p)
<*> (realToFrac <$> {# get cpBB->b #} p)
<*> (realToFrac <$> {# get cpBB->r #} p)
<*> (realToFrac <$> {# get cpBB->t #} p)
-- | Pointer to bounding box.
{# pointer *cpBB as BBPtr -> BB #}
-- | Pointer to user data.
{# pointer cpDataPointer as DataPtr #}
-- | Rigid body somewhere in C code.
{# pointer *cpBody as Body newtype #}
deriving (Eq, Ord, Generic)
instance Hashable Body
instance Storable Body where
sizeOf (Body p) = sizeOf p
alignment (Body p) = alignment p
poke p (Body b) = poke (castPtr p) b
peek p = Body <$> peek (castPtr p)
-- | Chipmunk supports three different types of bodies with unique behavioral and performance characteristics.
data BodyType =
BodyTypeDynamic
-- ^ Dynamic bodies are the default body type.
-- They react to collisions, are affected by forces and gravity, and have a finite amount of mass.
-- These are the type of bodies that you want the physics engine to simulate for you.
-- Dynamic bodies interact with all types of bodies and can generate collision callbacks.
| BodyTypeKimenatic
-- ^ Kinematic bodies are bodies that are controlled from your code instead of inside the physics engine.
-- They arent affected by gravity
-- and they have an infinite amount of mass so they don’t react to collisions or forces with other bodies.
-- Kinematic bodies are controlled by setting their velocity, which will cause them to move.
-- Good examples of kinematic bodies might include things like moving platforms.
-- Objects that are touching or jointed to a kinematic body are never allowed to fall asleep.
| BodyTypeStatic
-- ^ Static bodies are bodies that never (or rarely) move.
-- Using static bodies for things like terrain offers a big performance boost over other body types —
-- because Chipmunk doesn’t need to check for collisions between static objects
-- and it never needs to update their collision information.
-- Additionally, because static bodies don’t move,
-- Chipmunk knows it’s safe to let objects that are touching or jointed to them fall asleep.
-- Generally all of your level geometry will be attached to a static body
-- except for things like moving platforms or doors.
-- Every space provide a built-in static body for your convenience.
-- Static bodies can be moved, but there is a performance penalty as the collision information is recalculated.
-- There is no penalty for having multiple static bodies, and it can be useful for simplifying your code
-- by allowing different parts of your static geometry to be initialized or moved separately.
{# enum cpBodyType as BodyType nocode
{ CP_BODY_TYPE_DYNAMIC as BodyTypeDynamic
, CP_BODY_TYPE_KINEMATIC as BodyTypeKimenatic
, CP_BODY_TYPE_STATIC as BodyTypeStatic
} #}
deriving instance Show BodyType
-- | Spaces in Chipmunk are the basic unit of simulation. You add rigid bodies, shapes, and constraints to the space
-- and then step them all forward through time together.
{# pointer *cpSpace as Space newtype #}
deriving (Eq, Ord, Generic)
instance Hashable Space
instance Storable Space where
sizeOf (Space p) = sizeOf p
alignment (Space p) = alignment p
poke p (Space b) = poke (castPtr p) b
peek p = Space <$> peek (castPtr p)
-- | There are currently 3 collision shape types:
--
-- * __Circles__: Fastest and simplest collision shape.
--
-- * __Line segments__: Meant mainly as a static shape. Can be beveled in order to give them a thickness.
--
-- * __Convex polygons__: Slowest, but most flexible collision shape.
--
-- You can add as many shapes to a body as you wish. That is why the two types are separate.
--
-- Combining multiple shapes gives you the flexibility to make any object you want
-- as well as providing different areas of the same object with different friction, elasticity or callback values.
{# pointer *cpShape as Shape newtype #}
deriving (Eq, Ord, Generic)
instance Hashable Shape
instance Storable Shape where
sizeOf (Shape p) = sizeOf p
alignment (Shape p) = alignment p
poke p (Shape b) = poke (castPtr p) b
peek p = Shape <$> peek (castPtr p)
-- | A constraint is something that describes how two bodies interact with each other. (how they constrain each other)
-- Constraints can be simple joints that allow bodies to pivot around each other like the bones in your body,
-- or they can be more abstract like the gear joint or motors.
{# pointer *cpConstraint as Constraint newtype #}
deriving (Eq, Ord, Generic)
instance Hashable Constraint
instance Storable Constraint where
sizeOf (Constraint p) = sizeOf p
alignment (Constraint p) = alignment p
poke p (Constraint b) = poke (castPtr p) b
peek p = Constraint <$> peek (castPtr p)
-- | Chipmunk’s 'Arbiter' struct encapsulates a pair of colliding shapes and all of the data about their collision.
-- 'Arbiter' is created when a collision starts, and persist until those shapes are no longer colliding.
--
-- Why are they called arbiters? The short answer is that I kept using the word “arbitrates”
-- to describe the way that collisions were resolved and then I saw that Box2D actually called them arbiters
-- way back in 2006 when I was looking at its solver.
-- An arbiter is like a judge, a person that has authority to settle disputes between two people.
-- It was a fun, fitting name and was shorter to type than CollisionPair which I had been using.
-- It was originally meant to be a private internal structure only, but evolved to be useful from callbacks.
{# pointer *cpArbiter as Arbiter newtype #}
deriving (Eq, Ord, Generic)
instance Hashable Arbiter
instance Storable Arbiter where
sizeOf (Arbiter p) = sizeOf p
alignment (Arbiter p) = alignment p
poke p (Arbiter b) = poke (castPtr p) b
peek p = Arbiter <$> peek (castPtr p)
-- | Type used for 2×3 affine transforms in Chipmunk.
data Transform = Transform
{ tA :: !Double, tB :: !Double, tC :: !Double, tD :: !Double, tTx :: !Double, tTy :: !Double
} deriving (Show, Eq)
instance Storable Transform where
sizeOf _ = {# sizeof cpTransform #}
alignment _ = {# alignof cpTransform #}
poke p (Transform a b c d tx ty) = do
{# set cpTransform->a #} p $ realToFrac a
{# set cpTransform->b #} p $ realToFrac b
{# set cpTransform->c #} p $ realToFrac c
{# set cpTransform->d #} p $ realToFrac d
{# set cpTransform->tx #} p $ realToFrac tx
{# set cpTransform->ty #} p $ realToFrac ty
peek p = Transform <$> (realToFrac <$> {# get cpTransform->a #} p)
<*> (realToFrac <$> {# get cpTransform->b #} p)
<*> (realToFrac <$> {# get cpTransform->c #} p)
<*> (realToFrac <$> {# get cpTransform->d #} p)
<*> (realToFrac <$> {# get cpTransform->tx #} p)
<*> (realToFrac <$> {# get cpTransform->ty #} p)
-- | Pointer to 'Transform'
{# pointer *cpTransform as TransformPtr -> Transform #}
-- | Collision type
type CollisionType = WordPtr
type CPBool = {# type cpBool #}
-- | 'makeStateVar' lifted to reader monad
mkStateVar :: (a -> IO b) -> (a -> b -> IO ()) -> a -> StateVar b
mkStateVar g s i = makeStateVar (g i) (s i)
{# pointer *cpPolyline as PolylinePtr -> Polyline #}
newtype Polyline = Polyline { unPolyline :: [Vect] }
foreign import ccall w_cpPolylineVerts :: Ptr Polyline -> Ptr Vect
withPolylinePtr :: Polyline -> (Ptr Polyline -> IO a) -> IO a
withPolylinePtr (Polyline verts) fn = do
allocaBytes (sizeOf (undefined :: Vect) * (count+10)) $ \p -> do
{# set cpPolyline->count #} p $ fromIntegral count
{# set cpPolyline->capacity #} p $ fromIntegral count
let vp = plusPtr p {# offsetof cpPolyline->verts #}
pokeArray vp verts
fn p
where
count = length verts
peekPolyline :: Ptr Polyline -> IO Polyline
peekPolyline p = do
count <- fromIntegral <$> {# get cpPolyline->count #} p
let vp = w_cpPolylineVerts p
Polyline <$> peekArray count vp
{# pointer *cpPolylineSet as PolylineSetPtr -> PolylineSet #}
data PolylineSet = PolylineSet { unPolylineSet :: [Polyline] }
peekPolylineSet :: Ptr PolylineSet -> IO PolylineSet
peekPolylineSet p = do
count <- fromIntegral <$> {# get cpPolyline->count #} p
lp <- {# get cpPolylineSet->lines #} p
PolylineSet <$> (mapM peekPolyline =<< peekArray count lp)