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 (|| self.new_object_key (object.kind()).unwrap());
139 match object {
140 object::Variant::Static (object) => {
141 self.create_object_static (object, key, &mut output);
142 }
143 object::Variant::Dynamic (object) => {
144 self.create_object_dynamic (object, key, &mut output);
145 }
146 object::Variant::Nodetect (object) => {
147 self.create_object_nodetect (object, key);
148 }
149 }
150 }
151
152 event::Input::ModifyObject (object_modify_event) => {
156 match object_modify_event {
157 event::ObjectModify::Dynamic (key, event) => {
158 self.modify_object_dynamic (key, event);
159 }
160 event::ObjectModify::Static (key, event) => {
161 self.modify_object_static (key, event);
162 }
163 }
164 }
165
166 event::Input::DestroyObject (object::Id { kind, key }) => {
170 match kind {
171 object::Kind::Static => {
172 self.collision.remove_object_static (key);
173 self.constraint_manager.remove_object (InternalId::new_static (key));
174 assert!(self.objects_static.remove (key.index()).is_some());
175 }
176 object::Kind::Dynamic => {
177 self.collision.remove_object_dynamic (key);
178 self.constraint_manager.remove_object (InternalId::new_dynamic (key));
179 assert!(self.objects_dynamic.remove (key.index()).is_some());
180 }
181 object::Kind::Nodetect =>
182 assert!(self.objects_nodetect.remove (key.index()).is_some())
183 }
184 }
185
186 event::Input::SetGravity (gravity) => {
190 assert!(self.gravity.is_none());
191 self.gravity = Some (gravity);
192 }
193
194 event::Input::ClearGravity => {
198 assert!(self.gravity.is_some());
199 self.gravity = None;
200 }
201
202 event::Input::Step => {
206 #[cfg(feature = "debug_dump")]
207 let s = self.clone();
208
209 self.derivative_evaluation();
212 self.integrate_velocity();
214 self.constrain_velocity();
216 self.integrate_position();
218 self.constrain_position();
220 self.collision (&mut output);
222
223 #[cfg(feature = "debug_dump")]
224 if collision::DEBUG_DUMP.swap (false, std::sync::atomic::Ordering::SeqCst)
225 && std::env::var ("LINEAR_SIM_DEBUG_DUMP").is_ok()
226 {
227 let p = format!("linear-sim-{}.dump", s.step);
228 log::info!(filepath=p.as_str(); "dumping system");
229 let mut f = std::fs::File::create (&p).unwrap();
230 bincode::serde::encode_into_std_write (&s, &mut f, bincode::config::standard())
231 .unwrap();
232 }
233
234 self.step += 1;
235 }
236
237 }
238 log::debug!(
239 step=input_step,
240 output:?=output.iter().map (ToString::to_string).collect::<Vec<_>>();
241 "output events");
242 output
243 }
244
245 pub fn new_object_key (&self, kind : object::Kind) -> Option <object::Key> {
247 fn next_available_key <T> (map : &VecMap <T>) -> Option <object::Key> {
248 for i in 0..object::KEY_MAX as usize {
249 if !map.contains_key (i) {
250 return Some (i.into())
251 }
252 }
253 None
254 }
255 match kind {
256 object::Kind::Static => next_available_key (&self.objects_static),
257 object::Kind::Dynamic => next_available_key (&self.objects_dynamic),
258 object::Kind::Nodetect => next_available_key (&self.objects_nodetect)
259 }
260 }
261
262 pub fn constraints (&self)
264 -> Vec <Vec <(object::Id, object::Id, constraint::Variant)>>
265 {
266 let mut islands = vec![];
267 for (_, island) in self.constraint_manager.islands.as_ref().unwrap().iter() {
268 let mut v = vec![];
269 for (object_pair, constraint) in island.constraints.iter().cloned() {
270 let (object_id_a, object_id_b) = object_pair.into();
271 v.push ((object_id_a.into(), object_id_b.into(), constraint));
272 }
273 islands.push (v);
274 }
275 islands
276 }
277
278 pub fn add_constraint (&mut self,
279 object_id_a : object::Id, object_id_b : object::Id, constraint : constraint::Variant
280 ) {
281 self.constraint_manager
282 .add_constraint ((object_id_a, object_id_b).try_into().unwrap(), constraint)
283 }
284
285 fn create_object_static (&mut self,
290 object : object::Static,
291 key : object::Key,
292 output : &mut Vec <event::Output>
293 ) {
294 let object_id = object::Id { kind: object::Kind::Static, key };
295 match self.collision.try_add_object_static (
297 &self.objects_dynamic, &object, key
298 ) {
299 Ok (()) => {
300 assert!(self.objects_static.insert (key.index(), object).is_none());
302 output.push (event::CreateObjectResult::Created (object_id).into());
303 }
304 Err (intersections) => {
305 debug_assert!(!intersections.is_empty());
306 output.push (
307 event::CreateObjectResult::Intersection (intersections).into());
308 }
309 }
310 }
311
312 fn create_object_dynamic (&mut self,
313 object : object::Dynamic,
314 key : object::Key,
315 output : &mut Vec <event::Output>
316 ) {
317 let object_id = object::Id { kind: object::Kind::Dynamic, key };
318 match self.collision.try_add_object_dynamic (
320 &mut self.constraint_manager.pseudo_velocities,
321 &self.objects_static, &self.objects_dynamic, &object, key
322 ) {
323 Ok (()) => {
324 assert!(self.objects_dynamic.insert (key.index(), object).is_none());
326 output.push (event::CreateObjectResult::Created (object_id).into());
327 }
328 Err (intersections) => {
329 debug_assert!(!intersections.is_empty());
330 output.push (
331 event::CreateObjectResult::Intersection (intersections).into());
332 }
333 }
334 }
335
336 fn create_object_nodetect (&mut self,
337 object : object::Nodetect,
338 key : object::Key,
339 ) {
340 assert!(self.objects_nodetect.insert (key.index(), object).is_none());
341 }
342
343 fn modify_object_dynamic (&mut self,
345 key : object::Key,
346 event : event::ObjectModifyDynamic
347 ) {
348 let object = &mut self.objects_dynamic[key.index()];
349 match event {
350 event::ObjectModifyDynamic::ApplyImpulse (impulse) =>
351 object.derivatives.velocity += impulse,
352 event::ObjectModifyDynamic::SetForceFlags (force_flags) =>
353 object.derivatives.force_flags = force_flags,
354 event::ObjectModifyDynamic::SetDrag (drag) => object.drag.0 = drag,
355 event::ObjectModifyDynamic::SetPosition (position) => {
356 object.position = position;
357 self.collision.update_object_dynamic (object, key);
358 log::warn!("TODO: dynamic object set position results");
359 }
360 }
361 }
362
363 fn modify_object_static (&mut self,
365 key : object::Key,
366 event : event::ObjectModifyStatic
367 ) {
368 let object = &mut self.objects_static[key.index()];
369 match event {
370 event::ObjectModifyStatic::Move (move_vector) => {
372 object.position.0 += move_vector;
373 self.collision.update_object_static (object, key);
374 unimplemented!("TODO: move static object results");
375 }
376 }
377 }
378
379 fn derivative_evaluation (&mut self) {
381 use crate::Force;
382 use object::Inertial;
383 for object in self.objects_dynamic.values_mut() {
385 object.derivatives.force = [0.0, 0.0, 0.0].into();
386 }
387 for object in self.objects_nodetect.values_mut() {
388 object.derivatives.force = [0.0, 0.0, 0.0].into();
389 }
390
391 if let Some (gravity) = &self.gravity {
393 macro accumulate_forces ($object:ident) {
394 if $object.derivatives.force_flags.contains (force::Flag::Gravity) {
395 let impulse = gravity.impulse ($object, self.step, 1.0);
396 $object.derivatives.force += impulse;
397 }
398 }
399 for object in self.objects_dynamic.values_mut() {
400 accumulate_forces!(object);
401 }
402 for object in self.objects_nodetect.values_mut() {
403 accumulate_forces!(object);
404 }
405 }
406
407 for object in self.objects_dynamic.values_mut() {
409 object.compute_acceleration_inplace();
410 }
411 for object in self.objects_nodetect.values_mut() {
412 object.compute_acceleration_inplace();
413 }
414 }
415
416 fn integrate_velocity (&mut self) {
418 macro integrate_velocity ($integrator:ident, $object:ident) {
419 $integrator::integrate_velocity ($object);
420 $object.derivatives.velocity *= 1.0 - $object.drag.0;
425 }
426 for object in self.objects_dynamic.values_mut() {
427 integrate_velocity!(INTG, object);
428 }
429 for object in self.objects_nodetect.values_mut() {
430 integrate_velocity!(INTG, object);
431 }
432 }
433
434 #[inline]
436 fn constrain_velocity (&mut self) {
437 self.constraint_manager
438 .constrain_velocities (&self.objects_static, &mut self.objects_dynamic);
439 }
440
441 fn integrate_position (&mut self) {
443 for object in self.objects_dynamic.values_mut() {
444 INTG::integrate_position (object);
445 }
446 for object in self.objects_nodetect.values_mut() {
447 INTG::integrate_position (object);
448 }
449 }
450
451 #[inline]
453 fn constrain_position (&mut self) {
454 self.constraint_manager
455 .constrain_positions (&self.objects_static, &mut self.objects_dynamic);
456 }
457
458 fn collision (&mut self, output : &mut Vec <event::Output>) {
460 self.collision.detect_resolve_loop (
461 &mut self.constraint_manager, &self.objects_static, &mut self.objects_dynamic,
462 self.step, output);
463 }
464
465}
466
467pub fn report_sizes() {
469 use std::mem::size_of;
470 println!("system report sizes...");
471
472 println!(" size of System <integrator::SemiImplicitEuler>: {}",
473 size_of::<System <integrator::SemiImplicitEuler>>());
474
475 println!("...system report sizes");
476}
477
478#[cfg(test)]
479mod tests {
480 use super::*;
481 use crate::component;
482 use crate::geometry::{self, shape};
483 #[test]
484 fn segment_query() {
485 let segment = geometry::Segment3::noisy (
486 [-1.0, 0.0, 0.5].into(),
487 [ 2.0, 0.0, 0.5].into());
488 let mut system = System::<integrator::SemiImplicitEuler>::new();
489 let sphere1 = {
490 let position = component::Position ([0.5, 0.0, 0.5].into());
491 let mass = component::Mass::new (20.0);
492 let derivatives = component::Derivatives::zero();
493 let drag = component::Drag::zero();
494 let bound = component::Bound::from (shape::Sphere::noisy (0.5));
495 let material = component::MATERIAL_STONE;
496 let collidable = true;
497 object::Dynamic {
498 position, mass, derivatives, drag, bound, material, collidable
499 }.into()
500 };
501 let mut result = system.handle_event (event::Input::CreateObject (sphere1, None));
502 let event::Output::CreateObjectResult (
503 event::CreateObjectResult::Created (sphere1_id)
504 ) = result.pop().unwrap() else { unreachable!() };
505 assert!(result.pop().is_none());
506 let block1 = {
507 let position = component::Position ([1.5, 0.0, 0.5].into());
508 let bound = component::Bound (
509 shape::Bounded::from (shape::Cuboid::noisy ([0.5, 0.5, 0.5])).into());
510 let material = component::MATERIAL_STONE;
511 let collidable = true;
512 object::Static { position, bound, material, collidable }.into()
513 };
514 let mut result = system.handle_event (event::Input::CreateObject (block1, None));
515 let event::Output::CreateObjectResult (
516 event::CreateObjectResult::Created (block1_id)
517 ) = result.pop().unwrap() else { unreachable!() };
518 assert!(result.pop().is_none());
519 let result = system.segment_query (segment, &[]);
520 let (frac, point, id) = result.first().unwrap();
521 assert_eq!(**frac, 1.0/3.0);
522 assert_eq!(*point, [0.0, 0.0, 0.5].into());
523 assert_eq!(*id, sphere1_id);
524 let (frac, point, id) = result.get (1).unwrap();
525 assert_eq!(**frac, 2.0/3.0);
526 assert_eq!(*point, [1.0, 0.0, 0.5].into());
527 assert_eq!(*id, block1_id);
528 }
529}