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