BB.Collision.Primitives (bb v0.15.0)

View Source

Collision detection algorithms for primitive geometry pairs.

All functions take world-space geometry (position + orientation applied) and return either {:collision, penetration_depth} or :no_collision.

Penetration depth is the estimated overlap distance - how far the geometries would need to be separated to no longer collide.

Supported Geometry Types

  • Sphere: {:sphere, centre :: Vec3.t(), radius :: float()}
  • Capsule: {:capsule, point_a :: Vec3.t(), point_b :: Vec3.t(), radius :: float()}
  • Box (OBB): {:box, centre :: Vec3.t(), half_extents :: Vec3.t(), axes :: {Vec3.t(), Vec3.t(), Vec3.t()}}

Cylinders are converted to capsules internally for simpler, more conservative collision detection.

Summary

Functions

Test collision between two oriented bounding boxes using the Separating Axis Theorem.

Test collision between a capsule and an oriented bounding box.

Test collision between two capsules.

Find the closest point on an OBB to a given point.

Find the closest point on a line segment to a given point.

Find the closest points between two line segments.

Test collision between a sphere and an oriented bounding box.

Test collision between a sphere and a capsule.

Test collision between two spheres.

Test two geometries for collision.

Test two geometries with an additional margin/padding.

Types

box()

@type box() ::
  {:box, centre :: BB.Math.Vec3.t(), half_extents :: BB.Math.Vec3.t(),
   axes :: {BB.Math.Vec3.t(), BB.Math.Vec3.t(), BB.Math.Vec3.t()}}

capsule()

@type capsule() ::
  {:capsule, point_a :: BB.Math.Vec3.t(), point_b :: BB.Math.Vec3.t(),
   radius :: float()}

collision_result()

@type collision_result() :: {:collision, penetration_depth :: float()} | :no_collision

geometry()

@type geometry() :: sphere() | capsule() | box()

sphere()

@type sphere() :: {:sphere, centre :: BB.Math.Vec3.t(), radius :: float()}

Functions

box_box(arg1, arg2)

@spec box_box(box(), box()) :: collision_result()

Test collision between two oriented bounding boxes using the Separating Axis Theorem.

Two convex shapes are separated if there exists an axis along which their projections don't overlap. For two OBBs, we need to test 15 potential separating axes:

  • 3 face normals from box A
  • 3 face normals from box B
  • 9 cross products of edges from A and B

capsule_box(arg1, arg2)

@spec capsule_box(capsule(), box()) :: collision_result()

Test collision between a capsule and an oriented bounding box.

Finds the closest distance between the capsule's line segment and the box, then checks if it's less than the capsule's radius.

capsule_capsule(arg1, arg2)

@spec capsule_capsule(capsule(), capsule()) :: collision_result()

Test collision between two capsules.

Two capsules collide if the closest distance between their line segments is less than the sum of their radii.

closest_point_on_box(point, box_centre, half_extents, arg)

Find the closest point on an OBB to a given point.

Returns {closest_point, distance}.

closest_point_on_segment(point, seg_a, seg_b)

@spec closest_point_on_segment(BB.Math.Vec3.t(), BB.Math.Vec3.t(), BB.Math.Vec3.t()) ::
  {BB.Math.Vec3.t(), float()}

Find the closest point on a line segment to a given point.

Returns {closest_point, distance}.

closest_points_segments(a1, b1, a2, b2)

@spec closest_points_segments(
  BB.Math.Vec3.t(),
  BB.Math.Vec3.t(),
  BB.Math.Vec3.t(),
  BB.Math.Vec3.t()
) ::
  {BB.Math.Vec3.t(), BB.Math.Vec3.t(), float()}

Find the closest points between two line segments.

Returns {closest_on_seg1, closest_on_seg2, distance}.

Uses the algorithm from "Real-Time Collision Detection" by Christer Ericson.

sphere_box(arg1, arg2)

@spec sphere_box(sphere(), box()) :: collision_result()

Test collision between a sphere and an oriented bounding box.

The sphere collides with the box if the closest point on the box to the sphere's centre is within the sphere's radius.

sphere_capsule(arg1, arg2)

@spec sphere_capsule(sphere(), capsule()) :: collision_result()

Test collision between a sphere and a capsule.

A sphere and capsule collide if the closest distance from the sphere's centre to the capsule's line segment is less than the sum of their radii.

sphere_sphere(arg1, arg2)

@spec sphere_sphere(sphere(), sphere()) :: collision_result()

Test collision between two spheres.

Two spheres collide if the distance between their centres is less than the sum of their radii.

test(a, b)

@spec test(geometry(), geometry()) :: collision_result()

Test two geometries for collision.

Dispatches to the appropriate collision test based on geometry types. Order of arguments doesn't matter - the function handles symmetry internally.

Examples

iex> sphere1 = {:sphere, Vec3.new(0, 0, 0), 1.0}
iex> sphere2 = {:sphere, Vec3.new(1.5, 0, 0), 1.0}
iex> BB.Collision.Primitives.test(sphere1, sphere2)
{:collision, 0.5}

iex> sphere1 = {:sphere, Vec3.new(0, 0, 0), 1.0}
iex> sphere2 = {:sphere, Vec3.new(3.0, 0, 0), 1.0}
iex> BB.Collision.Primitives.test(sphere1, sphere2)
:no_collision

test_with_margin(a, b, margin)

@spec test_with_margin(geometry(), geometry(), margin :: float()) ::
  collision_result()

Test two geometries with an additional margin/padding.

The margin is added to both geometries, effectively expanding them. Useful for detecting "near misses" or adding safety buffers.