Hipmunk-0.1: A Haskell binding for Chipmunk.Source codeContentsIndex
Physics.Hipmunk.Space
Portabilityportable (needs FFI)
Stabilitybeta
Maintainerfelipe.lessa@gmail.com
Contents
Callbacks problem
Creating spaces and adding entities
Properties
Iterations
Elastic iterations
Gravity
Damping
Time stamp
Spatial hashes
Point query
Stepping
Collision pair functions
Contacts
Description
The space, where the simulation happens and the various entities interact.
Synopsis
data Space
newSpace :: IO Space
freeSpace :: Space -> IO ()
class Entity a where
spaceAdd :: Space -> a -> IO ()
spaceRemove :: Space -> a -> IO ()
newtype StaticShape = Static {
unStatic :: Shape
}
type Iterations = Int32
getIterations :: Space -> IO Iterations
setIterations :: Space -> Iterations -> IO ()
type ElasticIterations = Int32
getElasticIterations :: Space -> IO ElasticIterations
setElasticIterations :: Space -> ElasticIterations -> IO ()
type Gravity = Vector
getGravity :: Space -> IO Gravity
setGravity :: Space -> Gravity -> IO ()
type Damping = CpFloat
getDamping :: Space -> IO Damping
setDamping :: Space -> Damping -> IO ()
type TimeStamp = Int32
getTimeStamp :: Space -> IO TimeStamp
resizeStaticHash :: Space -> CpFloat -> Int32 -> IO ()
resizeActiveHash :: Space -> CpFloat -> Int32 -> IO ()
rehashStatic :: Space -> IO ()
data QueryType
= ActiveHash
| StaticHash
| Both
spaceQuery :: Space -> QueryType -> Position -> (Shape -> IO ()) -> IO ()
spaceQueryList :: Space -> QueryType -> Position -> IO [Shape]
step :: Space -> Time -> IO ()
data Callback
= Full (Shape -> Shape -> StorableArray Int Contact -> CpFloat -> IO Bool)
| Basic (Shape -> Shape -> IO Bool)
| Constant !Bool
setDefaultCallback :: Space -> Callback -> IO ()
addCallback :: Space -> (CollisionType, CollisionType) -> Callback -> IO ()
removeCallback :: Space -> (CollisionType, CollisionType) -> IO ()
data Contact = Contact {
ctPos :: Position
ctNormal :: Vector
ctDist :: CpFloat
ctJnAcc :: CpFloat
ctJtAcc :: CpFloat
}
sumImpulses :: StorableArray Int Contact -> IO Vector
sumImpulsesWithFriction :: StorableArray Int Contact -> IO Vector
Callbacks problem

We have a huge problem for callbacks: we *have* to call freeHaskellFunPtr to every Haskell function that was passed via FFI to C code after we don't need them. However, the ForeignPtr that the Space has can portably have finalizers only in the FFI, never in the Haskell land, so we can't run the Haskell function freeHaskellFunPtr from a ForeignPtr finalizer.

There are two options:

1. Use Foreign.Concurrent to add a Haskell finalizer. Under GHC this is great and adds no overhead (maybe there's even less overhead than calling a C function). However Foreign.Concurrent is not portable and works only under GHC.

2. Require that users of the library (you) call a finalizer function when they plan to stop using the space. This adds some burden to the programmer and somehow defeats the purpose of the GC, however it works everywhere.

As this is a library that intends to be as portable as possible (like Chipmunk itself), of course I chose to follow the second path. This means that your code will run unchanged on every Haskell environment supporting FFI with C99, but also that you have to take care to avoid memory leaks. You've been warned! :)

Creating spaces and adding entities
data Space Source
A space is where the simulation really occurs. You add bodies, shapes and joints to a space and then step it to update it as whole.
show/hide Instances
newSpace :: IO SpaceSource
Creates a new, empty space. Some of the memory resources associated with the space must be manually freed through freeSpace when the Space is no longer necessary.
freeSpace :: Space -> IO ()Source
freeSpace sp frees some memory resources that can't be automatically deallocated in a portable way. The space sp then becomes invalid and should not be used (passing sp to any other function, including freeSpace, results in undefined behavior).
class Entity a whereSource
Type class implemented by entities that can be added to a space.
Methods
spaceAdd :: Space -> a -> IO ()Source
Add an entity to a Space. Don't add the same entity twice to a space.
spaceRemove :: Space -> a -> IO ()Source
Remove an entity from a Space. Don't remove an entity that wasn't add.
show/hide Instances
newtype StaticShape Source

A StaticShape is a Shape container that, when added to a space via spaceAdd, is added to the static list of shapes.

A static shape is one assumed not to move. If you move a static shape after adding it, then you need to rehashStatic.

You should not add the same shape as active and static, nor should you add as active and try to remove as static or vice versa.

Constructors
Static
unStatic :: Shape
show/hide Instances
Properties
Iterations
type Iterations = Int32Source
The number of iterations to use when solving constraints. (default is 10).
getIterations :: Space -> IO IterationsSource
setIterations :: Space -> Iterations -> IO ()Source
Elastic iterations
type ElasticIterations = Int32Source
The number of elastic iterations to use when solving constraints. (default is 0).
getElasticIterations :: Space -> IO ElasticIterationsSource
setElasticIterations :: Space -> ElasticIterations -> IO ()Source
Gravity
type Gravity = VectorSource
The gravity applied to the system. (default is 0)
getGravity :: Space -> IO GravitySource
setGravity :: Space -> Gravity -> IO ()Source
Damping
type Damping = CpFloatSource
The amount of viscous damping applied to the system. (default is 1)
getDamping :: Space -> IO DampingSource
setDamping :: Space -> Damping -> IO ()Source
Time stamp
type TimeStamp = Int32Source
The time stamp of the simulation, increased in 1 every time step is called.
getTimeStamp :: Space -> IO TimeStampSource
Spatial hashes

resizeStaticHash sp dim count resizes the static hash of space sp to have hash cells of size dim and suggested minimum number of cells count. resizeActiveHash sp dim count works the same way but modifying the active hash of the space.

Chipmunk's performance is highly sensitive to both parameters, which should be hand-tuned to maximize performance. It is in general recommended to set dim as the average object size and count around 10 times the number of objects in the hash. Usually bigger numbers are better to count, but only to point. By default dim is 100.0 and count is 1000.

Note that in the case of the static hash you may try larger numbers as the static hash is only rehashed when requested by rehashStatic, however that will use more memory.

resizeStaticHash :: Space -> CpFloat -> Int32 -> IO ()Source
resizeActiveHash :: Space -> CpFloat -> Int32 -> IO ()Source
rehashStatic :: Space -> IO ()Source
Rehashes the shapes in the static spatial hash. You only need to call this if you move one of the static shapes.
Point query
Point querying uses the spatial hashes to find out in what shapes a point is contained. It is useful, for example, to know if a shape was clicked by the user.
data QueryType Source
You may query the static hash, the active hash or both.
Constructors
ActiveHash
StaticHash
Both
spaceQuery :: Space -> QueryType -> Position -> (Shape -> IO ()) -> IO ()Source

spaceQuery sp query pos cb will call cb for every shape that

  • Contains point pos (in world's coordinates).
  • Is in the hash selected by query (see QueryType).

The order in which the callback is called is unspecified. However it is guaranteed that it will be called once, and only once, for each of the shapes described above (and never for those who aren't).

spaceQueryList :: Space -> QueryType -> Position -> IO [Shape]Source
spaceQueryList sp query pos acts like spaceQuery but returns a list of Shapes instead of calling a callback. This is just a convenience function.
Stepping
step :: Space -> Time -> IO ()Source

step sp dt will update the space sp for a dt time step.

It is highly recommended to use a fixed dt to increase the efficiency of contact persistence. Some tips may be found in http://www.gaffer.org/game-physics/fix-your-timestep.

Collision pair functions

A collision pair function is a callback triggered by step in response to certain collision events. Its return value will determine whether or not the collision will be processed. If False, then the collision will be ignored.

The callbacks themselves may execute arbitrary operations with a simple exception: /callbacks cannot add or remove entities from the space/. You can of course create a queue of add/remove actions and then process it after step returns.

As for the events that trigger collision pair functions, the rule is simple. All shapes have a CollisionType. When shapes a and b collide, if there was a callback associated with a's and b's collision types, then it is called. Otherwise the default callback is called. By default, the default callback always returns True (i.e. all collisions are treated).

data Callback Source

A Callback function can be of three types:

  • A Full callback has access to all parameters passed by Chipmunk, but it is common not to need all of them. The two colliding Shapes are passed as arguments with a Contact array and a normal coefficient (this coefficient should be multiplied to the contacts' normals as Chipmunk may have reversed the argument order). See Contact for more information.
  • A Basic callback can't access the Contact information, but incurs a lower overhead per call.
  • A Constant callback always accepts or reject the collision. For example, a Constant False will never accept any collision.

Although Basic and Constant can be implemented in terms of Full, they're optimized to incur less overhead. So try to use the simplest callback type (e.g. Constant False instead of Basic (_ _ -> return False)).

Constructors
Full (Shape -> Shape -> StorableArray Int Contact -> CpFloat -> IO Bool)
Basic (Shape -> Shape -> IO Bool)
Constant !Bool
setDefaultCallback :: Space -> Callback -> IO ()Source
Defines a new default collision pair function. This callback is called whenever two shapes a and b collide such that no other collision pair function was defined to a's and b's collision types. The default is Constant True.
addCallback :: Space -> (CollisionType, CollisionType) -> Callback -> IO ()Source

addCallback sp (cta,ctb) f defines f as the callback to be called whenever a collision occurs between a shape of collision type cta and another of collision type ctb (and vice versa). Any other callback already registered to handle (cta,ctb) will be removed.

Note that you should not add callbacks to both combinations of (cta,ctb) and (ctb,cta). A good rule of thumb is to always use cta <= ctb, although this is not necessary.

removeCallback :: Space -> (CollisionType, CollisionType) -> IO ()Source

removeCallback sp (cta,ctb) removes any callbacks that were registered to handle (cta,ctb) (see addCallback). Any collisions that would be handled by the removed callback will be handled by the default one (see setDefaultCallback).

Note that you should always use the same order that was passed to addCallback. In other words, after addCallback sp (cta,ctb) f you should use removeCallback sp (cta,ctb), and never removeCallback sp (ctb,cta).

Although pointless, it is harmless to remove a callback that was not added.

Contacts
data Contact Source

A Contact contains information about a collision. It is passed to Physics.Hipmunk.Space.Full.

The fields ctJnAcc and ctJtAcc do not have any meaningfull value until Physics.Hipmunk.Space.step has returned (i.e. during a call to a callback this information contains garbage), and by extension you can only know the impulse sum after step returns as well.

IMPORTANT: You may maintain a reference to an array of Contacts that was passed to a callback to do any other processing later. However, a new call to step/ will invalidate any of those arrays/! Be careful.

Constructors
Contact
ctPos :: PositionPosition of the collision in world's coordinates.
ctNormal :: VectorNormal of the collision.
ctDist :: CpFloatPenetration distance of the collision.
ctJnAcc :: CpFloatNormal component of final impulse applied. (Valid only after step finishes.)
ctJtAcc :: CpFloatTangential component of final impulse applied. (Valid only after step finishes.)
show/hide Instances
sumImpulses :: StorableArray Int Contact -> IO VectorSource
Sums the impulses applied to the given contact points. sumImpulses sums only the normal components. This function should be called only after step returns.
sumImpulsesWithFriction :: StorableArray Int Contact -> IO VectorSource
Sums the impulses applied to the given contact points. This function sums both the normal and tangential components and should be called only after step returns.
Produced by Haddock version 2.3.0