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