box2d_rust/types/world.rs
1// World creation types and default from types.h / types.c.
2// SPDX-FileCopyrightText: 2023 Erin Catto
3// SPDX-License-Identifier: MIT
4
5use crate::core::{get_length_units_per_meter, SECRET_COOKIE};
6use crate::id::ShapeId;
7use crate::math_functions::{Pos, Vec2, POS_ZERO, VEC2_ZERO};
8
9/// Optional friction mixing callback. The default uses `sqrt(frictionA * frictionB)`.
10/// (b2FrictionCallback)
11///
12/// Args: `(friction_a, user_material_id_a, friction_b, user_material_id_b)`.
13pub type FrictionCallback = fn(f32, u64, f32, u64) -> f32;
14
15/// Optional restitution mixing callback. The default uses
16/// `max(restitutionA, restitutionB)`. (b2RestitutionCallback)
17pub type RestitutionCallback = fn(f32, u64, f32, u64) -> f32;
18
19/// Result from `b2World_RayCastClosest`. (b2RayResult)
20#[derive(Debug, Clone, Copy, PartialEq)]
21pub struct RayResult {
22 pub shape_id: ShapeId,
23 pub point: Pos,
24 pub normal: Vec2,
25 pub fraction: f32,
26 pub node_visits: i32,
27 pub leaf_visits: i32,
28 pub hit: bool,
29}
30
31impl Default for RayResult {
32 fn default() -> Self {
33 RayResult {
34 shape_id: ShapeId::default(),
35 point: POS_ZERO,
36 normal: VEC2_ZERO,
37 fraction: 0.0,
38 node_visits: 0,
39 leaf_visits: 0,
40 hit: false,
41 }
42 }
43}
44
45/// Optional world capacities that can be used to avoid run-time allocations.
46/// (b2Capacity)
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
48pub struct Capacity {
49 /// Number of expected static shapes.
50 pub static_shape_count: i32,
51 /// Number of expected dynamic and kinematic shapes.
52 pub dynamic_shape_count: i32,
53 /// Number of expected static bodies.
54 pub static_body_count: i32,
55 /// Number of expected dynamic and kinematic bodies.
56 pub dynamic_body_count: i32,
57 /// Number of expected contacts.
58 pub contact_count: i32,
59}
60
61/// Counters that give details of the simulation size. (b2Counters)
62///
63/// `byte_count`, `stack_used`, and `task_count` are always zero in this port:
64/// there is no global allocation tracker, no arena stack allocator, and no
65/// task system in the serial Rust implementation.
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
67pub struct Counters {
68 pub byte_count: i64,
69 pub body_count: i32,
70 pub shape_count: i32,
71 pub contact_count: i32,
72 pub joint_count: i32,
73 pub island_count: i32,
74 pub stack_used: i32,
75 pub static_tree_height: i32,
76 pub tree_height: i32,
77 pub task_count: i32,
78 pub color_counts: [i32; crate::constants::GRAPH_COLOR_COUNT as usize],
79
80 /// Number of contacts touched by the collide pass (graph contacts +
81 /// awake-set non-touching).
82 pub awake_contact_count: i32,
83
84 /// Number of contacts recycled in the most recent step.
85 pub recycled_contact_count: i32,
86}
87
88/// The explosion definition is used to configure options for explosions.
89/// Explosions consider shape geometry when computing the impulse.
90/// (b2ExplosionDef)
91#[derive(Debug, Clone, Copy, PartialEq)]
92pub struct ExplosionDef {
93 /// Mask bits to filter shapes
94 pub mask_bits: u64,
95
96 /// The center of the explosion in world space
97 pub position: Pos,
98
99 /// The radius of the explosion
100 pub radius: f32,
101
102 /// The falloff distance beyond the radius. Impulse is reduced to zero at
103 /// this distance.
104 pub falloff: f32,
105
106 /// Impulse per unit length. This applies an impulse according to the
107 /// shape perimeter that is facing the explosion. Explosions only apply to
108 /// circles, capsules, and polygons. This may be negative for implosions.
109 pub impulse_per_length: f32,
110}
111
112/// Use this to initialize your explosion definition. (b2DefaultExplosionDef)
113pub fn default_explosion_def() -> ExplosionDef {
114 ExplosionDef {
115 mask_bits: crate::dynamic_tree::DEFAULT_MASK_BITS,
116 position: POS_ZERO,
117 radius: 0.0,
118 falloff: 0.0,
119 impulse_per_length: 0.0,
120 }
121}
122
123impl Default for ExplosionDef {
124 fn default() -> Self {
125 default_explosion_def()
126 }
127}
128
129/// World definition used to create a simulation world. Must be initialized
130/// using [`default_world_def`]. (b2WorldDef)
131///
132/// `PartialEq` is intentionally not derived: it holds optional function-pointer
133/// callbacks, and function-pointer equality is not meaningful.
134#[derive(Debug, Clone, Copy)]
135pub struct WorldDef {
136 /// Gravity vector. Box2D has no up-vector defined.
137 pub gravity: Vec2,
138 /// Restitution speed threshold, usually in m/s. Collisions above this speed
139 /// have restitution applied (will bounce).
140 pub restitution_threshold: f32,
141 /// Threshold speed for hit events. Usually meters per second.
142 pub hit_event_threshold: f32,
143 /// Contact stiffness. Cycles per second.
144 pub contact_hertz: f32,
145 /// Contact bounciness. Non-dimensional.
146 pub contact_damping_ratio: f32,
147 /// Speed cap on overlap resolution, usually meters per second.
148 pub contact_speed: f32,
149 /// Maximum linear speed. Usually meters per second.
150 pub maximum_linear_speed: f32,
151 /// Optional mixing callback for friction.
152 pub friction_callback: Option<FrictionCallback>,
153 /// Optional mixing callback for restitution.
154 pub restitution_callback: Option<RestitutionCallback>,
155 /// Can bodies go to sleep to improve performance.
156 pub enable_sleep: bool,
157 /// Enable continuous collision.
158 pub enable_continuous: bool,
159 /// Contact softening when mass ratios are large. Experimental.
160 pub enable_contact_softening: bool,
161 /// Number of workers for multithreading, clamped to `[1, MAX_WORKERS]`.
162 pub worker_count: i32,
163 /// User data.
164 pub user_data: u64,
165 /// Optional initial capacities.
166 pub capacity: Capacity,
167 /// Used internally to detect a valid definition. DO NOT SET.
168 pub internal_value: i32,
169}
170
171/// Initialize a world definition with the default values. (b2DefaultWorldDef)
172pub fn default_world_def() -> WorldDef {
173 let length_units = get_length_units_per_meter();
174 WorldDef {
175 gravity: Vec2 { x: 0.0, y: -10.0 },
176 hit_event_threshold: 1.0 * length_units,
177 restitution_threshold: 1.0 * length_units,
178 contact_speed: 3.0 * length_units,
179 contact_hertz: 30.0,
180 contact_damping_ratio: 10.0,
181 // 400 meters per second, faster than the speed of sound
182 maximum_linear_speed: 400.0 * length_units,
183 friction_callback: None,
184 restitution_callback: None,
185 enable_sleep: true,
186 enable_continuous: true,
187 enable_contact_softening: false,
188 worker_count: 0,
189 user_data: 0,
190 capacity: Capacity::default(),
191 internal_value: SECRET_COOKIE,
192 }
193}
194
195impl Default for WorldDef {
196 fn default() -> Self {
197 default_world_def()
198 }
199}