1use std;
4
5use log;
6use vec_map::VecMap;
7#[cfg(feature = "derive_serdes")]
8use serde::{Deserialize, Serialize};
9
10use crate::{
11 constraint, event, force, geometry, integrator, math, object, Collision, Integrator
12};
13#[cfg(feature = "debug_dump")]
14use crate::collision;
15use crate::object::InternalId;
16
17#[cfg_attr(feature = "derive_serdes", derive(Deserialize, Serialize))]
25#[derive(Clone, Debug, Default)]
26pub struct System <INTG : Integrator> {
27 step : u64,
29 objects_static : VecMap <object::Static>,
31 objects_dynamic : VecMap <object::Dynamic>,
33 objects_nodetect : VecMap <object::Nodetect>,
35 gravity : Option <force::Gravity>,
37 collision : Collision,
39 constraint_manager : constraint::Manager,
41 #[cfg_attr(feature = "derive_serdes", serde(skip))]
42 _phantom_data : std::marker::PhantomData <INTG>
43}
44
45impl <INTG : Integrator> System <INTG> {
46 #[inline]
47 pub fn new() -> Self where INTG : Default {
48 Default::default()
49 }
50 #[inline]
51 pub const fn step (&self) -> u64 {
52 self.step
53 }
54 #[inline]
55 pub const fn objects_dynamic (&self) -> &VecMap <object::Dynamic> {
56 &self.objects_dynamic
57 }
58 #[inline]
59 pub const fn objects_nodetect (&self) -> &VecMap <object::Nodetect> {
60 &self.objects_nodetect
61 }
62 #[inline]
63 pub const fn objects_static (&self) -> &VecMap <object::Static> {
64 &self.objects_static
65 }
66
67 #[expect(mismatched_lifetime_syntaxes)]
68 pub fn get_object (&self, id : object::Id) -> object::VariantRef {
69 match id.kind {
70 object::Kind::Static => (&self.objects_static[id.key.index()]).into(),
71 object::Kind::Dynamic => (&self.objects_dynamic[id.key.index()]).into(),
72 object::Kind::Nodetect => (&self.objects_nodetect[id.key.index()]).into()
73 }
74 }
75
76 pub fn segment_query (&self,
77 segment : geometry::Segment3 <f64>,
78 ignore_list : &[object::Id]
79 ) -> Vec <(math::Normalized <f64>, math::Point3 <f64>, object::Id)> {
80 use object::Bounded;
81 let mut out = Vec::<(math::Normalized <f64>, math::Point3 <f64>, object::Id)>::new();
82 let aabb = segment.aabb3();
83 let mut overlaps : Vec <object::Id> = self.collision.broad()
84 .overlaps_discrete (aabb).into_iter().map (object::Id::from).collect();
85 overlaps.retain (|id| !ignore_list.contains (id));
86 for object_id in overlaps {
87 if let Some ((frac, point)) = match object_id.kind {
88 object::Kind::Static =>
89 self.objects_static[object_id.key.index()].hit_test (segment),
90 object::Kind::Dynamic =>
91 self.objects_dynamic[object_id.key.index()].hit_test (segment),
92 object::Kind::Nodetect => unreachable!()
93 } {
94 out.insert (
95 out.binary_search_by (|(f, _, _)| f.partial_cmp (&frac).unwrap())
96 .unwrap_or_else (|i| i),
97 (frac, point, object_id)
98 );
99 }
100 }
101 out
102 }
103
104 #[expect(clippy::doc_overindented_list_items)]
105 pub fn handle_event (&mut self, input : event::Input) -> Vec <event::Output> {
128 let input_step = self.step;
129 log::debug!(step=input_step, input=input.to_string().as_str();
130 "input event");
131 let mut output = Vec::new();
132 match input {
133
134 event::Input::CreateObject (object, key) => {
138 let key = key.unwrap_or_else (
139 || self.new_object_key (object.kind()).unwrap());
140 match object {
141 object::Variant::Static (object) => {
142 self.create_object_static (object, key, &mut output);
143 }
144 object::Variant::Dynamic (object) => {
145 self.create_object_dynamic (object, key, &mut output);
146 }
147 object::Variant::Nodetect (object) => {
148 self.create_object_nodetect (object, key);
149 }
150 }
151 }
152
153 event::Input::ModifyObject (object_modify_event) => {
157 match object_modify_event {
158 event::ObjectModify::Dynamic (key, event) => {
159 self.modify_object_dynamic (key, event);
160 }
161 event::ObjectModify::Static (key, event) => {
162 self.modify_object_static (key, event);
163 }
164 }
165 }
166
167 event::Input::DestroyObject (object::Id { kind, key }) => {
171 match kind {
172 object::Kind::Static => {
173 self.collision.remove_object_static (key);
174 self.constraint_manager.remove_object (InternalId::new_static (key));
175 assert!(self.objects_static.remove (key.index()).is_some());
176 }
177 object::Kind::Dynamic => {
178 self.collision.remove_object_dynamic (key);
179 self.constraint_manager.remove_object (InternalId::new_dynamic (key));
180 assert!(self.objects_dynamic.remove (key.index()).is_some());
181 }
182 object::Kind::Nodetect =>
183 assert!(self.objects_nodetect.remove (key.index()).is_some())
184 }
185 }
186
187 event::Input::SetGravity (gravity) => {
191 assert!(self.gravity.is_none());
192 self.gravity = Some (gravity);
193 }
194
195 event::Input::ClearGravity => {
199 assert!(self.gravity.is_some());
200 self.gravity = None;
201 }
202
203 event::Input::Step => {
207 #[cfg(feature = "debug_dump")]
208 let s = self.clone();
209
210 self.derivative_evaluation();
213 self.integrate_velocity();
215 self.constrain_velocity();
217 self.integrate_position();
219 self.constrain_position();
221 self.collision (&mut output);
223
224 #[cfg(feature = "debug_dump")]
225 if collision::DEBUG_DUMP.swap (false, std::sync::atomic::Ordering::SeqCst)
226 && std::env::var ("LINEAR_SIM_DEBUG_DUMP").is_ok()
227 {
228 let p = format!("linear-sim-{}.dump", s.step);
229 log::info!(filepath=p.as_str(); "dumping system");
230 let mut f = std::fs::File::create (&p).unwrap();
231 bincode::serde::encode_into_std_write (&s, &mut f, bincode::config::standard())
232 .unwrap();
233 }
234
235 self.step += 1;
236 }
237
238 }
239 log::debug!(
240 step=input_step,
241 output:?=output.iter().map (ToString::to_string).collect::<Vec<_>>();
242 "output events");
243 output
244 }
245
246 pub fn new_object_key (&self, kind : object::Kind) -> Option <object::Key> {
248 fn next_available_key <T> (map : &VecMap <T>) -> Option <object::Key> {
249 for i in 0..object::KEY_MAX as usize {
250 if !map.contains_key (i) {
251 return Some (i.into())
252 }
253 }
254 None
255 }
256 match kind {
257 object::Kind::Static => next_available_key (&self.objects_static),
258 object::Kind::Dynamic => next_available_key (&self.objects_dynamic),
259 object::Kind::Nodetect => next_available_key (&self.objects_nodetect)
260 }
261 }
262
263 pub fn constraints (&self)
265 -> Vec <Vec <(object::Id, object::Id, constraint::Variant)>>
266 {
267 let mut islands = vec![];
268 for (_, island) in self.constraint_manager.islands.as_ref().unwrap().iter() {
269 let mut v = vec![];
270 for (object_pair, constraint) in island.constraints.iter().cloned() {
271 let (object_id_a, object_id_b) = object_pair.into();
272 v.push ((object_id_a.into(), object_id_b.into(), constraint));
273 }
274 islands.push (v);
275 }
276 islands
277 }
278
279 pub fn add_constraint (&mut self,
280 object_id_a : object::Id, object_id_b : object::Id, constraint : constraint::Variant
281 ) {
282 self.constraint_manager
283 .add_constraint ((object_id_a, object_id_b).try_into().unwrap(), constraint)
284 }
285
286 fn create_object_static (&mut self,
291 object : object::Static,
292 key : object::Key,
293 output : &mut Vec <event::Output>
294 ) {
295 let object_id = object::Id { kind: object::Kind::Static, key };
296 match self.collision.try_add_object_static (
298 &self.objects_dynamic, &object, key
299 ) {
300 Ok (()) => {
301 assert!(self.objects_static.insert (key.index(), object).is_none());
303 output.push (event::CreateObjectResult::Created (object_id).into());
304 }
305 Err (intersections) => {
306 debug_assert!(!intersections.is_empty());
307 output.push (
308 event::CreateObjectResult::Intersection (intersections).into());
309 }
310 }
311 }
312
313 fn create_object_dynamic (&mut self,
314 object : object::Dynamic,
315 key : object::Key,
316 output : &mut Vec <event::Output>
317 ) {
318 let object_id = object::Id { kind: object::Kind::Dynamic, key };
319 match self.collision.try_add_object_dynamic (
321 &mut self.constraint_manager.pseudo_velocities,
322 &self.objects_static, &self.objects_dynamic, &object, key
323 ) {
324 Ok (()) => {
325 assert!(self.objects_dynamic.insert (key.index(), object).is_none());
327 output.push (event::CreateObjectResult::Created (object_id).into());
328 }
329 Err (intersections) => {
330 debug_assert!(!intersections.is_empty());
331 output.push (
332 event::CreateObjectResult::Intersection (intersections).into());
333 }
334 }
335 }
336
337 fn create_object_nodetect (&mut self,
338 object : object::Nodetect,
339 key : object::Key,
340 ) {
341 assert!(self.objects_nodetect.insert (key.index(), object).is_none());
342 }
343
344 fn modify_object_dynamic (&mut self,
346 key : object::Key,
347 event : event::ObjectModifyDynamic
348 ) {
349 let object = &mut self.objects_dynamic[key.index()];
350 match event {
351 event::ObjectModifyDynamic::ApplyImpulse (impulse) =>
352 object.derivatives.velocity += impulse,
353 event::ObjectModifyDynamic::SetForceFlags (force_flags) =>
354 object.derivatives.force_flags = force_flags,
355 event::ObjectModifyDynamic::SetDrag (drag) => object.drag.0 = drag,
356 event::ObjectModifyDynamic::SetPosition (position) => {
357 object.position = position;
358 self.collision.update_object_dynamic (object, key);
359 log::warn!("TODO: dynamic object set position results");
360 }
361 }
362 }
363
364 fn modify_object_static (&mut self,
366 key : object::Key,
367 event : event::ObjectModifyStatic
368 ) {
369 let object = &mut self.objects_static[key.index()];
370 match event {
371 event::ObjectModifyStatic::Move (move_vector) => {
373 object.position.0 += move_vector;
374 self.collision.update_object_static (object, key);
375 unimplemented!("TODO: move static object results");
376 }
377 }
378 }
379
380 fn derivative_evaluation (&mut self) {
382 use crate::Force;
383 use object::Inertial;
384 for object in self.objects_dynamic.values_mut() {
386 object.derivatives.force = [0.0, 0.0, 0.0].into();
387 }
388 for object in self.objects_nodetect.values_mut() {
389 object.derivatives.force = [0.0, 0.0, 0.0].into();
390 }
391
392 if let Some (gravity) = &self.gravity {
394 macro accumulate_forces ($object:ident) {
395 if $object.derivatives.force_flags.contains (force::Flag::Gravity) {
396 let impulse = gravity.impulse ($object, self.step, 1.0);
397 $object.derivatives.force += impulse;
398 }
399 }
400 for object in self.objects_dynamic.values_mut() {
401 accumulate_forces!(object);
402 }
403 for object in self.objects_nodetect.values_mut() {
404 accumulate_forces!(object);
405 }
406 }
407
408 for object in self.objects_dynamic.values_mut() {
410 object.compute_acceleration_inplace();
411 }
412 for object in self.objects_nodetect.values_mut() {
413 object.compute_acceleration_inplace();
414 }
415 }
416
417 fn integrate_velocity (&mut self) {
419 macro integrate_velocity ($integrator:ident, $object:ident) {
420 $integrator::integrate_velocity ($object);
421 $object.derivatives.velocity *= 1.0 - $object.drag.0;
426 }
427 for object in self.objects_dynamic.values_mut() {
428 integrate_velocity!(INTG, object);
429 }
430 for object in self.objects_nodetect.values_mut() {
431 integrate_velocity!(INTG, object);
432 }
433 }
434
435 #[inline]
437 fn constrain_velocity (&mut self) {
438 self.constraint_manager
439 .constrain_velocities (&self.objects_static, &mut self.objects_dynamic);
440 }
441
442 fn integrate_position (&mut self) {
444 for object in self.objects_dynamic.values_mut() {
445 INTG::integrate_position (object);
446 }
447 for object in self.objects_nodetect.values_mut() {
448 INTG::integrate_position (object);
449 }
450 }
451
452 #[inline]
454 fn constrain_position (&mut self) {
455 self.constraint_manager
456 .constrain_positions (&self.objects_static, &mut self.objects_dynamic);
457 }
458
459 fn collision (&mut self, output : &mut Vec <event::Output>) {
461 self.collision.detect_resolve_loop (
462 &mut self.constraint_manager, &self.objects_static, &mut self.objects_dynamic,
463 self.step, output);
464 }
465
466}
467
468pub fn report_sizes() {
470 use std::mem::size_of;
471 println!("system report sizes...");
472
473 println!(" size of System <integrator::SemiImplicitEuler>: {}",
474 size_of::<System <integrator::SemiImplicitEuler>>());
475
476 println!("...system report sizes");
477}
478
479#[cfg(test)]
480mod tests {
481 use super::*;
482 use crate::component;
483 use crate::geometry::{self, shape};
484 #[test]
485 fn segment_query() {
486 let segment = geometry::Segment3::noisy (
487 [-1.0, 0.0, 0.5].into(),
488 [ 2.0, 0.0, 0.5].into());
489 let mut system = System::<integrator::SemiImplicitEuler>::new();
490 let sphere1 = {
491 let position = component::Position ([0.5, 0.0, 0.5].into());
492 let mass = component::Mass::new (20.0);
493 let derivatives = component::Derivatives::zero();
494 let drag = component::Drag::zero();
495 let bound = component::Bound::from (shape::Sphere::noisy (0.5));
496 let material = component::MATERIAL_STONE;
497 let collidable = true;
498 object::Dynamic {
499 position, mass, derivatives, drag, bound, material, collidable
500 }.into()
501 };
502 let mut result = system.handle_event (event::Input::CreateObject (sphere1, None));
503 let event::Output::CreateObjectResult (
504 event::CreateObjectResult::Created (sphere1_id)
505 ) = result.pop().unwrap() else { unreachable!() };
506 assert!(result.pop().is_none());
507 let block1 = {
508 let position = component::Position ([1.5, 0.0, 0.5].into());
509 let bound = component::Bound (
510 shape::Bounded::from (shape::Cuboid::noisy ([0.5, 0.5, 0.5])).into());
511 let material = component::MATERIAL_STONE;
512 let collidable = true;
513 object::Static { position, bound, material, collidable }.into()
514 };
515 let mut result = system.handle_event (event::Input::CreateObject (block1, None));
516 let event::Output::CreateObjectResult (
517 event::CreateObjectResult::Created (block1_id)
518 ) = result.pop().unwrap() else { unreachable!() };
519 assert!(result.pop().is_none());
520 let result = system.segment_query (segment, &[]);
521 let (frac, point, id) = result.first().unwrap();
522 assert_eq!(**frac, 1.0/3.0);
523 assert_eq!(*point, [0.0, 0.0, 0.5].into());
524 assert_eq!(*id, sphere1_id);
525 let (frac, point, id) = result.get (1).unwrap();
526 assert_eq!(**frac, 2.0/3.0);
527 assert_eq!(*point, [1.0, 0.0, 0.5].into());
528 assert_eq!(*id, block1_id);
529 }
530}