tiramisu/spatial
Spatial partitioning data structures for efficient spatial queries.
Provides octree and Collider (Box and Sphere) for:
- Finding objects within a region (10-100x faster than linear search)
- Finding nearby objects
- Frustum culling optimization
- Broad-phase collision detection
Quick Example
import tiramisu/spatial
// Create octree for world bounds
let world_bounds = spatial.box(
min: vec3.Vec3(-100.0, -100.0, -100.0),
max: vec3.Vec3(100.0, 100.0, 100.0),
)
let tree = spatial.octree_new(bounds: world_bounds, capacity: 8)
// Insert enemies
let tree = spatial.octree_insert(tree, enemy_pos, enemy_id)
// Find enemies near player
let nearby = spatial.octree_query_radius(tree, player_pos, radius: 10.0)
Types
Collision volume for spatial queries and collision detection.
pub opaque type Collider
Values
pub fn collider_box(
min min: vec3.Vec3(Float),
max max: vec3.Vec3(Float),
) -> Collider
Create a box collider from min and max points.
Example
let bounds = spatial.collider_box(
min: vec3.Vec3(-1.0, -1.0, -1.0),
max: vec3.Vec3(1.0, 1.0, 1.0),
)
pub fn collider_box_from_center(
center: vec3.Vec3(Float),
half_extents: vec3.Vec3(Float),
) -> Collider
Create a box collider from center and half-extents.
Example
let bounds = spatial.box_from_center(
center: vec3.Vec3(0.0, 5.0, 0.0),
half_extents: vec3.Vec3(2.0, 1.0, 2.0),
)
pub fn collider_contains_point(
collider: Collider,
point: vec3.Vec3(Float),
) -> Bool
Check if a point is inside a collider.
Works for both Box and Sphere colliders.
pub fn collider_from_transform(
collider: Collider,
transform: transform.Transform,
) -> Collider
Create a new collider from a local-space collider and a transform.
For Box: Computes a new axis-aligned bounding box that encompasses all 8 corners after rotation, translation, and scaling.
For Sphere: Transforms the center and scales the radius by the maximum scale component (since spheres remain spherical under uniform scaling).
Example
// Local space box
let local_box = spatial.box(
min: vec3.Vec3(-1.0, -1.0, -1.0),
max: vec3.Vec3(1.0, 1.0, 1.0),
)
// Rotated 45 degrees around Y axis
let transform = transform.identity
|> transform.with_position(vec3.Vec3(5.0, 0.0, 0.0))
|> transform.with_euler_rotation(vec3.Vec3(0.0, 0.785, 0.0))
// Get world-space collider
let world_box = spatial.collider_from_transform(local_box, transform)
pub fn collider_intersects(a: Collider, b: Collider) -> Bool
Check if two colliders intersect.
Handles Box-Box, Sphere-Sphere, and Box-Sphere collisions.
pub fn collider_size(collider: Collider) -> vec3.Vec3(Float)
Get the size (dimensions) of a box collider.
For spheres, returns the diameter as a uniform Vec3.
pub fn collider_sphere(
center center: vec3.Vec3(Float),
radius radius: Float,
) -> Collider
Create a sphere collider from center and radius.
Example
let bounds = spatial.sphere(
center: vec3.Vec3(0.0, 0.0, 0.0),
radius: 2.5,
)
pub fn octree_insert(
tree: Octree(a),
position: vec3.Vec3(Float),
item: a,
) -> Octree(a)
Insert an item at a position into the octree
pub fn octree_new(bounds: Collider, capacity: Int) -> Octree(a)
Create a new empty octree.
Note: Octrees require Box colliders (not Sphere).
Parameters
bounds: The spatial region this octree covers (must be a Box)capacity: Maximum items per node before subdividing (typically 8-16)
Example
let bounds = spatial.box(
min: vec3.Vec3(-100.0, -100.0, -100.0),
max: vec3.Vec3(100.0, 100.0, 100.0),
)
let tree = spatial.octree_new(bounds, capacity: 8)
pub fn octree_query(
tree: Octree(a),
query_bounds: Collider,
) -> List(#(vec3.Vec3(Float), a))
Query all items within a collider region
pub fn octree_query_all(
tree: Octree(a),
) -> List(#(vec3.Vec3(Float), a))
Query all items in the octree (useful for iteration)