Cauldron2D.Body (Cauldron2D v0.1.3)

Copy Markdown View Source

A point mass with a heading: position, velocity, and one of headings directions.

Positions and velocities are in tiles and tiles per second. Heading 0 points right, and headings increase counter-clockwise on screen, so a quarter of them points up.

body =
  Cauldron2D.Body.new(pos: {4.0, 4.0}, headings: 64)
  |> Cauldron2D.Body.turn(16)
  |> Cauldron2D.Body.thrust(20.0, dt)
  |> Cauldron2D.Body.gravitate(fields, dt)
  |> Cauldron2D.Body.drag(0.1, dt)
  |> Cauldron2D.Body.integrate(dt)

Gravity fields

gravitate/3 takes a list of maps:

  • %{pos: {x, y}, strength: s, kind: :attract | :repel} — pulls toward or pushes from pos, falling off with the square of the distance and capped within one tile

  • %{pos: {x, y}, strength: s, kind: :clockwise | :anticlockwise} — pushes across the line to pos, same falloff

  • %{strength: s, kind: {:uniform, {dx, dy}}} — a constant pull in a direction
  • %{strength: s, kind: {:toward, {x, y}}} — a constant pull toward a point from anywhere, away from it when s is negative

Summary

Functions

Add {ax, ay} tiles per second squared for dt seconds.

The heading as radians, counter-clockwise from right.

Scale the velocity down to limit if it is faster; slower bodies are untouched.

The unit vector the body faces, in screen coordinates (y grows downward).

Slow the body by coefficient of its velocity per second, for dt seconds.

Apply every field in fields for dt seconds.

Move by the velocity for dt seconds.

A body at rest.

The velocity's length in tiles per second.

Accelerate along the heading by accel tiles per second squared for dt seconds.

Turn by steps headings; negative turns clockwise. Wraps around the circle.

Bring a body that has left a {width, height} world back in on the opposite side.

Types

field()

@type field() :: %{
  optional(:pos) => point(),
  strength: number(),
  kind:
    :attract
    | :repel
    | :clockwise
    | :anticlockwise
    | {:uniform, point()}
    | {:toward, point()}
}

point()

@type point() :: {float(), float()}

t()

@type t() :: %Cauldron2D.Body{
  heading: non_neg_integer(),
  headings: pos_integer(),
  mass: float(),
  pos: point(),
  radius: float(),
  vel: point()
}

Functions

accelerate(body, arg, dt)

@spec accelerate(t(), point(), number()) :: t()

Add {ax, ay} tiles per second squared for dt seconds.

angle(body)

@spec angle(t()) :: float()

The heading as radians, counter-clockwise from right.

cap_speed(body, limit)

@spec cap_speed(t(), number()) :: t()

Scale the velocity down to limit if it is faster; slower bodies are untouched.

direction(body)

@spec direction(t()) :: point()

The unit vector the body faces, in screen coordinates (y grows downward).

drag(body, coefficient, dt)

@spec drag(t(), number(), number()) :: t()

Slow the body by coefficient of its velocity per second, for dt seconds.

The velocity is scaled by exp(-coefficient * dt), so it approaches zero without ever reversing.

gravitate(body, fields, dt)

@spec gravitate(t(), [field()], number()) :: t()

Apply every field in fields for dt seconds.

integrate(body, dt)

@spec integrate(t(), number()) :: t()

Move by the velocity for dt seconds.

new(opts \\ [])

@spec new(keyword()) :: t()

A body at rest.

Options

  • :pos{x, y} in tiles. Default {0.0, 0.0}
  • :vel{vx, vy} in tiles per second. Default {0.0, 0.0}
  • :heading — index into the headings. Default 0
  • :headings — how many directions the body can face. Default 64
  • :mass — used by callers that trade momentum. Default 1.0
  • :radius — in tiles, for collision. Default 0.5

speed(body)

@spec speed(t()) :: float()

The velocity's length in tiles per second.

thrust(body, accel, dt)

@spec thrust(t(), number(), number()) :: t()

Accelerate along the heading by accel tiles per second squared for dt seconds.

turn(body, steps)

@spec turn(t(), integer()) :: t()

Turn by steps headings; negative turns clockwise. Wraps around the circle.

wrap(body, arg)

@spec wrap(t(), {number(), number()}) :: t()

Bring a body that has left a {width, height} world back in on the opposite side.