packages feed

Box2D-0.1.0.0: README.md

# Box2D

Low-level Haskell bindings to [Box2D](https://box2d.org/), Erin Catto's 2D
rigid-body physics engine.

The engine's C sources (v3.2.0, MIT licensed) are bundled and compiled with the
package. You need a C compiler, but no system library.

The bindings stay close to the C API: `b2World_Step` is `Box2D.World.step`, and
each wrapper's documentation names the function it binds. Most modules are
generated from the engine headers by `boxnd-gen`, in the
[source repository](https://gitlab.com/dpwiz/box-nd).

## Example

```haskell
{-# LANGUAGE PatternSynonyms #-}

module Main (main) where

import Control.Monad (replicateM_)

import Box2D.Body (BodyDef (..), defaultBodyDef, pattern DynamicBody)
import Box2D.Body qualified as Body
import Box2D.Geometry (Circle (..))
import Box2D.MathTypes (Pos (..), Vec2 (..), vec2Zero)
import Box2D.Shape (defaultShapeDef)
import Box2D.Shape qualified as Shape
import Box2D.World (WorldDef (..), defaultWorldDef)
import Box2D.World qualified as World

main :: IO ()
main = do
  worldDef <- defaultWorldDef
  world <- World.create worldDef{gravity = Vec2 0 (-10)}

  bodyDef <- defaultBodyDef
  ball <- Body.create world bodyDef{type_ = DynamicBody, position = Pos 0 10}
  shapeDef <- defaultShapeDef
  _ <- Shape.createCircle ball shapeDef (Circle vec2Zero 0.5)

  replicateM_ 60 (World.step world (1 / 60) 4)
  Body.getPosition ball >>= print

  World.destroy world
```

## Modules

The modules are designed for qualified import.

| Area | Modules |
| --- | --- |
| Simulation | `World`, `Body`, `Shape`, `Chain`, `Contact`, `Events` |
| Joints | `Joint`, `DistanceJoint`, `FilterJoint`, `MotorJoint`, `MoverJoint`, `PogoJoint`, `PrismaticJoint`, `RevoluteJoint`, `WeldJoint`, `WheelJoint` |
| Geometry and queries | `Geometry`, `Collision`, `Distance`, `DynamicTree`, `Mover` |
| Callbacks and drawing | `Callbacks`, `DebugDraw` |
| Recording | `Recording`, `Replay` |
| Foundation | `Id`, `MathTypes`, `MathFunctions`, `Base`, `UserData`, `Extra`, `Tags` |

`Box2D.Internal` and its submodule are exposed, but may change in any release.

## Things to know

- **Start from the defaults.** Create definitions with `default*Def` and update
  the fields you need, rather than building them from scratch. They carry an
  initialization marker, which the engine checks when `assertions` is on.
- **World positions are `Pos`.** The engine takes and returns `Pos` for places
  in the world and `WorldTransform` for a full pose, while directions, velocities
  and shape geometry stay `Vec2`. `MathTypes.toPos`, `toVec2`, `subPos` and
  `offsetPos` convert between them.
- **Enums are open.** They are newtypes over `CInt` with pattern synonyms, and the
  engine can hand back a value with no name here, so include a wildcard when you
  `case` on one.
- **Worlds are not thread-safe.** Create, step and destroy them from one thread at
  a time, and never create or destroy two worlds at once: the world table is
  global. `Callbacks.withThreadPoolTaskSystem` runs the engine's internal work in
  parallel.
- **Callback lifetimes.** The `with*` functions in `Box2D.Callbacks` free their
  callback when the action ends, so anything holding it, a world included, must
  be finished by then. Callbacks may run on the engine's worker threads.
- **Exceptions in callbacks.** `Draw` callbacks are trapped and rethrown by
  `Callbacks.draw`. An exception escaping any other callback stops the process,
  because it cannot unwind through C frames.
- **Events are copied.** `Box2D.Events` copies each event buffer into a vector
  that is safe to keep. The raw buffers from `Box2D.World` are only valid until
  the next step.

## Build flags

`assertions` (off by default) keeps the engine's internal assertions. They catch
invalid arguments and corrupted state, stopping the process with a trap (`SIGILL`
on x86-64), and only with this flag does `Base.setAssertCallback` ever fire. Turn
it on while developing, for example with `stack build --flag Box2D:assertions`.

`double-precision` (off by default) builds the engine's large world mode, which
stores world positions as doubles so they stay accurate far from the origin.
`Pos` then holds `Double` instead of `Float`, and everything else stays `Float`.
Code that builds positions from literals and the `MathTypes` helpers compiles
either way. Every package in a build shares this setting.

Run `stack clean Box2D` before changing either flag on an already-built package.
A flag change does not rebuild the C objects or preprocessed modules, so without
a clean the old setting silently stays in effect.

## Limits

- x86-64 and AArch64 only: ids cross the FFI as machine words, which relies on
  those calling conventions.
- Built and tested on Linux x86-64 with GHC 9.10.3.