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 assert!(self.objects_dynamic.remove (key.index()).is_some());
182 }
183 object::Kind::Nodetect =>
184 assert!(self.objects_nodetect.remove (key.index()).is_some())
185 }
186 }
187
188 event::Input::SetGravity (gravity) => {
192 assert!(self.gravity.is_none());
193 self.gravity = Some (gravity);
194 }
195
196 event::Input::ClearGravity => {
200 assert!(self.gravity.is_some());
201 self.gravity = None;
202 }
203
204 event::Input::Step => {
208 #[cfg(feature = "debug_dump")]
209 let s = self.clone();
210
211 self.derivative_evaluation();
214 self.integrate_velocity();
216 self.constrain_velocity();
218 self.integrate_position();
220 self.constrain_position();
222 self.collision (&mut output);
224
225 #[cfg(feature = "debug_dump")]
226 if collision::DEBUG_DUMP.swap (false, std::sync::atomic::Ordering::SeqCst)
227 && std::env::var ("LINEAR_SIM_DEBUG_DUMP").is_ok()
228 {
229 let p = format!("linear-sim-{}.dump", s.step);
230 log::info!(filepath=p.as_str(); "dumping system");
231 let mut f = std::fs::File::create (&p).unwrap();
232 bincode::serde::encode_into_std_write (&s, &mut f, bincode::config::standard())
233 .unwrap();
234 }
235
236 self.step += 1;
237 }
238
239 }
240 log::debug!(
241 step=input_step,
242 output:?=output.iter().map (ToString::to_string).collect::<Vec<_>>();
243 "output events");
244 output
245 }
246
247 pub fn new_object_key (&self, kind : object::Kind) -> Option <object::Key> {
249 fn next_available_key <T> (map : &VecMap <T>) -> Option <object::Key> {
250 for i in 0..object::KEY_MAX as usize {
251 if !map.contains_key (i) {
252 return Some (i.into())
253 }
254 }
255 None
256 }
257 match kind {
258 object::Kind::Static => next_available_key (&self.objects_static),
259 object::Kind::Dynamic => next_available_key (&self.objects_dynamic),
260 object::Kind::Nodetect => next_available_key (&self.objects_nodetect)
261 }
262 }
263
264 pub fn constraints (&self)
266 -> Vec <Vec <(object::Id, object::Id, constraint::Variant)>>
267 {
268 let mut islands = vec![];
269 for (_, island) in self.constraint_manager.islands.as_ref().unwrap().iter() {
270 let mut v = vec![];
271 for (object_pair, constraint) in island.constraints.iter().cloned() {
272 let (object_id_a, object_id_b) = object_pair.into();
273 v.push ((object_id_a.into(), object_id_b.into(), constraint));
274 }
275 islands.push (v);
276 }
277 islands
278 }
279
280 pub fn add_constraint (&mut self,
281 object_id_a : object::Id, object_id_b : object::Id, constraint : constraint::Variant
282 ) {
283 self.constraint_manager
284 .add_constraint ((object_id_a, object_id_b).try_into().unwrap(), constraint)
285 }
286
287 fn create_object_static (&mut self,
292 object : object::Static,
293 key : object::Key,
294 output : &mut Vec <event::Output>
295 ) {
296 let object_id = object::Id { kind: object::Kind::Static, key };
297 match self.collision.try_add_object_static (
299 &self.objects_dynamic, &object, key
300 ) {
301 Ok (()) => {
302 assert!(self.objects_static.insert (key.index(), object).is_none());
304 output.push (event::CreateObjectResult::Created (object_id).into());
305 }
306 Err (intersections) => {
307 debug_assert!(!intersections.is_empty());
308 output.push (
309 event::CreateObjectResult::Intersection (intersections).into());
310 }
311 }
312 }
313
314 fn create_object_dynamic (&mut self,
315 object : object::Dynamic,
316 key : object::Key,
317 output : &mut Vec <event::Output>
318 ) {
319 let object_id = object::Id { kind: object::Kind::Dynamic, key };
320 match self.collision.try_add_object_dynamic (
322 &mut self.constraint_manager.pseudo_velocities,
323 &self.objects_static, &self.objects_dynamic, &object, key
324 ) {
325 Ok (()) => {
326 assert!(self.objects_dynamic.insert (key.index(), object).is_none());
328 output.push (event::CreateObjectResult::Created (object_id).into());
329 }
330 Err (intersections) => {
331 debug_assert!(!intersections.is_empty());
332 output.push (
333 event::CreateObjectResult::Intersection (intersections).into());
334 }
335 }
336 }
337
338 fn create_object_nodetect (&mut self,
339 object : object::Nodetect,
340 key : object::Key,
341 ) {
342 assert!(self.objects_nodetect.insert (key.index(), object).is_none());
343 }
344
345 fn modify_object_dynamic (&mut self,
347 key : object::Key,
348 event : event::ObjectModifyDynamic
349 ) {
350 let object = &mut self.objects_dynamic[key.index()];
351 match event {
352 event::ObjectModifyDynamic::ApplyImpulse (impulse) =>
353 object.derivatives.velocity += impulse,
354 event::ObjectModifyDynamic::SetForceFlags (force_flags) =>
355 object.derivatives.force_flags = force_flags,
356 event::ObjectModifyDynamic::SetDrag (drag) => object.drag.0 = drag,
357 event::ObjectModifyDynamic::SetPosition (position) => {
358 object.position = position;
359 self.collision.update_object_dynamic (object, key);
360 log::warn!("TODO: dynamic object set position results");
361 }
362 }
363 }
364
365 fn modify_object_static (&mut self,
367 key : object::Key,
368 event : event::ObjectModifyStatic
369 ) {
370 let object = &mut self.objects_static[key.index()];
371 match event {
372 event::ObjectModifyStatic::Move (move_vector) => {
374 object.position.0 += move_vector;
375 self.collision.update_object_static (object, key);
376 unimplemented!("TODO: move static object results");
377 }
378 }
379 }
380
381 fn derivative_evaluation (&mut self) {
383 for_sequence!{
385 (_, object) in (
386 self.objects_dynamic.iter_mut(), self.objects_nodetect.iter_mut()
387 ) {
388 object.derivatives.force = [0.0, 0.0, 0.0].into();
389 }
390 }
391
392 if let Some (gravity) = &self.gravity {
394 for_sequence!{
395 (_, object) in (
396 self.objects_dynamic.iter_mut(), self.objects_nodetect.iter_mut()
397 ) {
398 if object.derivatives.force_flags.contains (force::Flag::Gravity) {
399 use crate::Force;
400 let impulse = gravity.impulse (object, self.step, 1.0);
401 object.derivatives.force += impulse;
402 }
403 }
404 }
405 }
406
407 for_sequence!{
409 (_, object) in (
410 self.objects_dynamic.iter_mut(), self.objects_nodetect.iter_mut()
411 ) {
412 use object::Inertial;
413 object.compute_acceleration_inplace();
414 }
415 }
416 }
417
418 fn integrate_velocity (&mut self) {
420 for_sequence!{
421 (_, object) in (
422 self.objects_dynamic.iter_mut(), self.objects_nodetect.iter_mut()
423 ) {
424 INTG::integrate_velocity (object);
425 object.derivatives.velocity *= 1.0 - object.drag.0;
430 }
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_sequence!{
444 (_, object) in (
445 self.objects_dynamic.iter_mut(), self.objects_nodetect.iter_mut()
446 ) {
447 INTG::integrate_position (object);
448 }
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}