Skip to main content

World

Struct World 

Source
pub struct World {
Show 63 fields pub broad_phase: BroadPhase, pub constraint_graph: ConstraintGraph, pub body_id_pool: IdPool, pub bodies: Vec<Body>, pub solver_set_id_pool: IdPool, pub solver_sets: Vec<SolverSet>, pub joint_id_pool: IdPool, pub joints: Vec<Joint>, pub contact_id_pool: IdPool, pub contacts: Vec<Contact>, pub island_id_pool: IdPool, pub islands: Vec<Island>, pub shape_id_pool: IdPool, pub chain_id_pool: IdPool, pub shapes: Vec<Shape>, pub chain_shapes: Vec<ChainShape>, pub sensors: Vec<Sensor>, pub task_contexts: Vec<TaskContext>, pub sensor_task_contexts: Vec<SensorTaskContext>, pub body_move_events: Vec<BodyMoveEvent>, pub sensor_begin_events: Vec<SensorBeginTouchEvent>, pub contact_begin_events: Vec<ContactBeginTouchEvent>, pub sensor_end_events: [Vec<SensorEndTouchEvent>; 2], pub contact_end_events: [Vec<ContactEndTouchEvent>; 2], pub end_event_array_index: i32, pub contact_hit_events: Vec<ContactHitEvent>, pub joint_events: Vec<JointEvent>, pub debug_body_set: BitSet, pub debug_joint_set: BitSet, pub debug_contact_set: BitSet, pub debug_island_set: BitSet, pub step_index: u64, pub split_island_id: i32, pub gravity: Vec2, pub hit_event_threshold: f32, pub restitution_threshold: f32, pub max_linear_speed: f32, pub contact_speed: f32, pub contact_hertz: f32, pub contact_damping_ratio: f32, pub contact_recycle_distance: f32, pub friction_callback: Option<FrictionCallback>, pub restitution_callback: Option<RestitutionCallback>, pub generation: u16, pub profile: Profile, pub max_capacity: Capacity, pub pre_solve_fcn: Option<PreSolveFcn>, pub pre_solve_context: u64, pub custom_filter_fcn: Option<CustomFilterFcn>, pub custom_filter_context: u64, pub worker_count: i32, pub user_data: u64, pub inv_h: f32, pub inv_dt: f32, pub world_id: u16, pub enable_sleep: bool, pub locked: bool, pub enable_warm_starting: bool, pub enable_contact_softening: bool, pub enable_continuous: bool, pub enable_speculative: bool, pub in_use: bool, pub recording: Option<Recording>,
}
Expand description

The world struct manages all physics entities, dynamic simulation, and asynchronous queries. (b2World)

Fields§

§broad_phase: BroadPhase§constraint_graph: ConstraintGraph§body_id_pool: IdPool

The body id pool allocates and recycles body ids. Body ids provide a stable identifier for users. Aligns with bodies.

§bodies: Vec<Body>

Sparse array mapping body ids to the body data stored in solver sets.

§solver_set_id_pool: IdPool

Provides free list for solver sets.

§solver_sets: Vec<SolverSet>

Solver sets store sims in contiguous arrays. Set 0 is static, set 1 is disabled, set 2 is awake; the rest are sleeping islands.

§joint_id_pool: IdPool

Used to create stable ids for joints.

§joints: Vec<Joint>

Sparse array mapping joint ids to joints in the constraint graph or solver sets.

§contact_id_pool: IdPool

Used to create stable ids for contacts.

§contacts: Vec<Contact>

Sparse array mapping contact ids to contacts in the constraint graph or solver sets.

§island_id_pool: IdPool

Used to create stable ids for islands.

§islands: Vec<Island>

Persistent islands.

§shape_id_pool: IdPool§chain_id_pool: IdPool§shapes: Vec<Shape>

Sparse arrays that point into the pools above.

§chain_shapes: Vec<ChainShape>§sensors: Vec<Sensor>

Dense array of sensor data.

§task_contexts: Vec<TaskContext>

Per thread storage (one entry in the single-threaded port).

§sensor_task_contexts: Vec<SensorTaskContext>§body_move_events: Vec<BodyMoveEvent>§sensor_begin_events: Vec<SensorBeginTouchEvent>§contact_begin_events: Vec<ContactBeginTouchEvent>§sensor_end_events: [Vec<SensorEndTouchEvent>; 2]

End events are double buffered so that the user doesn’t need to flush events.

§contact_end_events: [Vec<ContactEndTouchEvent>; 2]§end_event_array_index: i32§contact_hit_events: Vec<ContactHitEvent>§joint_events: Vec<JointEvent>§debug_body_set: BitSet

Used to track debug draw.

§debug_joint_set: BitSet§debug_contact_set: BitSet§debug_island_set: BitSet§step_index: u64

Id that is incremented every time step.

§split_island_id: i32

Identify islands for splitting.

§gravity: Vec2§hit_event_threshold: f32§restitution_threshold: f32§max_linear_speed: f32§contact_speed: f32§contact_hertz: f32§contact_damping_ratio: f32§contact_recycle_distance: f32§friction_callback: Option<FrictionCallback>§restitution_callback: Option<RestitutionCallback>§generation: u16§profile: Profile§max_capacity: Capacity§pre_solve_fcn: Option<PreSolveFcn>§pre_solve_context: u64§custom_filter_fcn: Option<CustomFilterFcn>§custom_filter_context: u64§worker_count: i32§user_data: u64§inv_h: f32

Inverse sub-step, remembered for reporting forces and torques.

§inv_dt: f32

Inverse full-step.

§world_id: u16§enable_sleep: bool§locked: bool§enable_warm_starting: bool§enable_contact_softening: bool§enable_continuous: bool§enable_speculative: bool§in_use: bool§recording: Option<Recording>

Active recording session; owned by the world between world_start_recording and world_stop_recording. (C: b2Recording*)

Implementations§

Source§

impl World

Source

pub fn validate_connectivity(&self)

(b2ValidateConnectivity — C compiles the body under B2_ENABLE_VALIDATION; here the whole check runs in debug builds only)

Source§

impl World

Source

pub fn validate_contacts(&self)

(b2ValidateContacts — C compiles the body under B2_ENABLE_VALIDATION; here the whole check runs in debug builds only)

Source§

impl World

Source

pub fn new(def: &WorldDef) -> World

Create a world. (b2CreateWorld)

Differences from C, all documented in the module header: there is no global world registry (the returned World is owned; world_id stays 0 unless the embedder assigns one), no arena stack, and the serial task path is always used (worker_count = 1 with one task context), which is the C fallback when no task system is supplied.

Examples found in repository?
examples/demo_scenes.rs (line 54)
50fn main() {
51    // === Bodies demo scene ===
52    let mut wd = default_world_def();
53    wd.gravity = m::Vec2 { x: 0.0, y: -10.0 };
54    let mut world = World::new(&wd);
55
56    add_static_box(&mut world, 0.0, -0.5, 13.0, 0.5);
57    add_static_box(&mut world, -12.2, 2.0, 0.3, 2.0);
58    add_static_box(&mut world, 12.2, 2.0, 0.3, 2.0);
59
60    let mut tracked = Vec::new();
61    for i in 0..24usize {
62        let x = -6.0 + (i % 8) as f32 * 1.7 + 0.13 * (i % 3) as f32;
63        let y = 5.0 + (i / 8) as f32 * 1.6;
64        if i % 2 == 0 {
65            let hx = 0.25 + 0.2 * ((i * 7) % 3) as f32 * 0.5;
66            tracked.push(add_box(&mut world, x, y, hx, hx));
67        } else {
68            let r = 0.22 + 0.16 * ((i * 5) % 3) as f32 * 0.5;
69            tracked.push(add_circle(&mut world, x, y, r, 1.0));
70        }
71    }
72
73    for step in 0..600 {
74        world_step(&mut world, 1.0 / 60.0, 4);
75        if step % 100 == 0 {
76            let t = get_body_transform(&world, tracked[0]);
77            println!(
78                "bodies step {step}: body0 = ({:.3}, {:.3}), contacts = {}",
79                t.p.x,
80                t.p.y,
81                world.contact_id_pool.id_count()
82            );
83        }
84    }
85    println!("BODIES SCENE OK");
86
87    // === Stacking demo scene ===
88    let mut world = World::new(&wd);
89    add_static_box(&mut world, 0.0, -0.5, 11.0, 0.5);
90    let h = 0.4f32;
91    let base = 9i32;
92    for row in 0..base {
93        let count = base - row;
94        let y = h + row as f32 * 2.0 * h;
95        for i in 0..count {
96            let x = (i as f32 - (count - 1) as f32 / 2.0) * 2.05 * h;
97            add_box(&mut world, x, y, h, h);
98        }
99    }
100    for _ in 0..600 {
101        world_step(&mut world, 1.0 / 60.0, 4);
102    }
103    println!(
104        "stacking settled: awake = {}",
105        world.solver_sets[box2d_rust::solver_set::AWAKE_SET as usize]
106            .body_sims
107            .len()
108    );
109    // Drop the heavy ball
110    add_circle(&mut world, 0.3, 9.0, 0.5, 4.0);
111    for _ in 0..600 {
112        world_step(&mut world, 1.0 / 60.0, 4);
113    }
114    println!("STACKING SCENE OK");
115}
More examples
Hide additional examples
examples/benchmark/main.rs (line 232)
82fn main() {
83    let benchmarks: [Benchmark; 10] = [
84        Benchmark {
85            name: "compounds",
86            create_fcn: scenes::create_compounds,
87            step_fcn: None,
88            total_step_count: 500,
89        },
90        Benchmark {
91            name: "joint_grid",
92            create_fcn: scenes::create_joint_grid,
93            step_fcn: None,
94            total_step_count: 500,
95        },
96        Benchmark {
97            name: "junkyard",
98            create_fcn: scenes::create_junkyard,
99            step_fcn: Some(scenes::step_junkyard),
100            total_step_count: 800,
101        },
102        Benchmark {
103            name: "large_pyramid",
104            create_fcn: scenes::create_large_pyramid,
105            step_fcn: None,
106            total_step_count: 500,
107        },
108        Benchmark {
109            name: "many_pyramids",
110            create_fcn: scenes::create_many_pyramids,
111            step_fcn: None,
112            total_step_count: 200,
113        },
114        Benchmark {
115            name: "rain",
116            create_fcn: scenes::create_rain,
117            step_fcn: Some(scenes::step_rain),
118            total_step_count: 1000,
119        },
120        Benchmark {
121            name: "smash",
122            create_fcn: scenes::create_smash,
123            step_fcn: None,
124            total_step_count: 300,
125        },
126        Benchmark {
127            name: "spinner",
128            create_fcn: scenes::create_spinner,
129            step_fcn: Some(scenes::step_spinner),
130            total_step_count: 500,
131        },
132        Benchmark {
133            name: "tumbler",
134            create_fcn: scenes::create_tumbler,
135            step_fcn: None,
136            total_step_count: 750,
137        },
138        Benchmark {
139            name: "washer",
140            create_fcn: scenes::create_washer,
141            step_fcn: None,
142            total_step_count: 500,
143        },
144    ];
145
146    let benchmark_count = benchmarks.len() as i32;
147
148    let mut max_steps = benchmarks[0].total_step_count;
149    for b in benchmarks.iter().skip(1) {
150        max_steps = max_steps.max(b.total_step_count);
151    }
152
153    // Profiles persist across all benchmarks, exactly like the C array that is
154    // allocated once before the benchmark loop and never reset.
155    let mut profiles: Vec<Profile> = vec![max_profile(); max_steps as usize];
156    let mut step_results: Vec<f32> = vec![0.0; max_steps as usize];
157
158    let mut run_count = 4;
159    let mut single_benchmark = -1;
160    let mut enable_continuous = true;
161    let mut record_step_times = false;
162
163    for arg in std::env::args().skip(1) {
164        if let Some(value) = arg.strip_prefix("-t=") {
165            // Serial port: the worker count is fixed at 1.
166            let _ = value.parse::<i32>().unwrap_or(0);
167            println!("Note: '-t' ignored; the Rust port runs a single-threaded solver");
168        } else if let Some(value) = arg.strip_prefix("-b=") {
169            single_benchmark = value.parse::<i32>().unwrap_or(0);
170            single_benchmark = clamp_int(single_benchmark, 0, benchmark_count - 1);
171        } else if let Some(value) = arg.strip_prefix("-w=") {
172            // Serial port: a single worker count is the only option.
173            let _ = value.parse::<i32>().unwrap_or(0);
174            println!("Note: '-w' ignored; the Rust port runs a single-threaded solver");
175        } else if let Some(value) = arg.strip_prefix("-r=") {
176            run_count = clamp_int(value.parse::<i32>().unwrap_or(0), 1, 1000);
177        } else if arg.starts_with("-nc") {
178            enable_continuous = false;
179            println!("Continuous disabled");
180        } else if arg.starts_with("-s") {
181            record_step_times = true;
182        } else if arg == "-h" {
183            println!(
184                "Usage\n\
185                 -t=<integer>: the maximum number of threads to use (ignored, serial port)\n\
186                 -b=<integer>: run a single benchmark\n\
187                 -w=<integer>: run a single worker count (ignored, serial port)\n\
188                 -r=<integer>: number of repeats (default is 4)\n\
189                 -nc: disable continuous collision\n\
190                 -s: record step times"
191            );
192            std::process::exit(0);
193        }
194    }
195
196    println!("Starting Box2D benchmarks");
197    println!("======================================");
198
199    // mirrors C's indexed loop
200    #[allow(clippy::needless_range_loop)]
201    for benchmark_index in 0..benchmark_count as usize {
202        if single_benchmark != -1 && benchmark_index as i32 != single_benchmark {
203            continue;
204        }
205
206        let benchmark = &benchmarks[benchmark_index];
207
208        // NDEBUG uses the full step count; debug builds cap at 10 like the C.
209        let step_count = if cfg!(debug_assertions) {
210            10
211        } else {
212            benchmark.total_step_count
213        };
214
215        let mut counters = box2d_rust::types::Counters::default();
216        let mut counters_acquired = false;
217
218        println!("benchmark: {}, steps = {}", benchmark.name, step_count);
219
220        // Single thread only in the serial port.
221        let mut min_time = 0.0f32;
222
223        println!("thread count: 1");
224
225        for run_index in 0..run_count {
226            let world_def = {
227                let mut wd = default_world_def();
228                wd.enable_continuous = enable_continuous;
229                wd.worker_count = 1;
230                wd
231            };
232            let mut world = World::new(&world_def);
233
234            (benchmark.create_fcn)(&mut world);
235
236            let time_step = 1.0 / 60.0;
237            let sub_step_count = 4;
238
239            // Initial step can be expensive and skew benchmark.
240            if let Some(step_fcn) = benchmark.step_fcn {
241                step_results[0] = step_fcn(&mut world, 0);
242            }
243
244            debug_assert!(step_count <= max_steps);
245
246            world_step(&mut world, time_step, sub_step_count);
247
248            let profile = world_get_profile(&world);
249            min_profile(&mut profiles[0], &profile);
250
251            let ticks = get_ticks();
252
253            for step_index in 1..step_count {
254                if let Some(step_fcn) = benchmark.step_fcn {
255                    step_results[step_index as usize] = step_fcn(&mut world, step_index);
256                }
257
258                world_step(&mut world, time_step, sub_step_count);
259                let profile = world_get_profile(&world);
260                min_profile(&mut profiles[step_index as usize], &profile);
261            }
262
263            let ms = get_milliseconds(ticks);
264            println!("run {} : {} (ms)", run_index, ms);
265
266            if run_index == 0 {
267                min_time = ms;
268            } else {
269                min_time = min_float(min_time, ms);
270            }
271
272            if !counters_acquired {
273                counters = world_get_counters(&world);
274                counters_acquired = true;
275            }
276
277            // b2DestroyWorld: dropping the world frees it.
278            drop(world);
279        }
280
281        if record_step_times {
282            let file_name = format!("{}_t1.dat", benchmark.name);
283            if let Ok(mut file) = File::create(&file_name) {
284                // mirrors C's indexed loop
285                #[allow(clippy::needless_range_loop)]
286                for step_index in 0..step_count as usize {
287                    let p = profiles[step_index];
288                    let _ = writeln!(
289                        file,
290                        "{} {} {} {} {} {} {}",
291                        p.step,
292                        p.pairs,
293                        p.collide,
294                        p.constraints,
295                        p.transforms,
296                        p.refit,
297                        p.sleep_islands
298                    );
299                }
300            }
301        }
302
303        println!(
304            "body {} / shape {} / contact {} / joint {} / stack {}",
305            counters.body_count,
306            counters.shape_count,
307            counters.contact_count,
308            counters.joint_count,
309            counters.stack_used
310        );
311        print!("color counts:");
312        for c in counters.color_counts.iter() {
313            print!(" {}", c);
314        }
315        println!("\n");
316
317        let file_name = format!("{}.csv", benchmark.name);
318        if let Ok(mut file) = File::create(&file_name) {
319            let _ = writeln!(file, "threads,ms");
320            let _ = writeln!(file, "1,{}", min_time);
321        }
322    }
323
324    println!("======================================");
325    println!("All Box2D benchmarks complete!");
326}
Source

pub fn validate_solver_sets(&self)

Validate the solver-set bookkeeping. (b2ValidateSolverSets)

bring-up: the C version (physics_world.c, compiled only with B2_ENABLE_VALIDATION) also cross-checks contacts, joints, and graph colors; those checks are added as their slices land. This subset validates the body <-> sim <-> set <-> island mapping.

Trait Implementations§

Source§

impl Debug for World

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for World

§

impl RefUnwindSafe for World

§

impl Send for World

§

impl Sync for World

§

impl Unpin for World

§

impl UnsafeUnpin for World

§

impl UnwindSafe for World

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.