use std;
use log;
use vec_map::VecMap;
use rs_utils::for_sequence;
#[cfg(feature = "derive_serdes")]
use serde::{Deserialize, Serialize};
use crate::{collision, event, force, geometry, integrator, math, object,
Collision, Integrator};
#[cfg_attr(feature = "derive_serdes", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, Default)]
pub struct System <INTG : Integrator> {
step : u64,
objects_static : VecMap <object::Static>,
objects_dynamic : VecMap <object::Dynamic>,
objects_nodetect : VecMap <object::Nodetect>,
gravity : Option <force::Gravity>,
collision : Collision,
#[cfg_attr(feature = "derive_serdes", serde(skip))]
_phantom_data : std::marker::PhantomData <INTG>
}
impl <INTG : Integrator> System <INTG> {
#[inline]
pub fn new() -> Self where INTG : Default {
std::default::Default::default()
}
#[inline]
pub fn step (&self) -> u64 {
self.step
}
#[inline]
pub fn objects_dynamic (&self) -> &VecMap <object::Dynamic> {
&self.objects_dynamic
}
#[inline]
pub fn objects_nodetect (&self) -> &VecMap <object::Nodetect> {
&self.objects_nodetect
}
#[inline]
pub fn objects_static (&self) -> &VecMap <object::Static> {
&self.objects_static
}
#[allow(mismatched_lifetime_syntaxes)]
pub fn get_object (&self, id : object::Id) -> object::VariantRef {
match id.kind {
object::Kind::Static => (&self.objects_static[id.key.index()]).into(),
object::Kind::Dynamic => (&self.objects_dynamic[id.key.index()]).into(),
object::Kind::Nodetect => (&self.objects_nodetect[id.key.index()]).into()
}
}
pub fn segment_query (&self,
segment : geometry::Segment3 <f64>,
ignore_list : &[object::Id]
) -> Vec <(f64, math::Point3 <f64>, object::Id)> {
use object::Bounded;
let mut out : Vec <(f64, math::Point3 <f64>, object::Id)> = vec![];
let aabb = segment.aabb3();
let mut overlaps : Vec <object::Id> = self.collision.broad()
.overlaps_discrete (aabb).into_iter().map (|id| id.into()).collect();
overlaps.retain (|id| !ignore_list.contains (id));
for object_id in overlaps {
if let Some ((frac, point)) = match object_id.kind {
object::Kind::Static =>
self.objects_static[object_id.key.index()].hit_test (segment),
object::Kind::Dynamic =>
self.objects_dynamic[object_id.key.index()].hit_test (segment),
_ => unreachable!()
} {
out.insert (
out.binary_search_by (|(f, _, _)| f.partial_cmp (&frac).unwrap())
.unwrap_or_else (|i| i),
(frac, point, object_id)
);
}
}
out
}
#[allow(clippy::doc_overindented_list_items)]
pub fn handle_event (&mut self, input : event::Input) -> Vec <event::Output> {
let input_step = self.step;
log::debug!(step=input_step, input=input.to_string().as_str();
"input event");
let mut output = Vec::new();
match input {
event::Input::CreateObject (object, key) => {
let key = key.unwrap_or_else (
|| self.new_object_key (object.kind()).unwrap());
match object {
object::Variant::Static (object) => {
self.create_object_static (object, key, &mut output);
}
object::Variant::Dynamic (object) => {
self.create_object_dynamic (object, key, &mut output);
}
object::Variant::Nodetect (object) => {
self.create_object_nodetect (object, key);
}
}
}
event::Input::ModifyObject (object_modify_event) => {
match object_modify_event {
event::ObjectModify::Dynamic (key, event) => {
self.modify_object_dynamic (key, event);
}
event::ObjectModify::Static (key, event) => {
self.modify_object_static (key, event);
}
}
}
event::Input::DestroyObject (object::Id { kind, key }) => {
match kind {
object::Kind::Static => {
self.collision.remove_object_static (key);
assert!(self.objects_static.remove (key.index()).is_some());
}
object::Kind::Dynamic => {
self.collision.remove_object_dynamic (key);
assert!(self.objects_dynamic.remove (key.index()).is_some());
}
object::Kind::Nodetect =>
assert!(self.objects_nodetect.remove (key.index()).is_some())
}
}
event::Input::SetGravity (gravity) => {
assert!(self.gravity.is_none());
self.gravity = Some (gravity);
}
event::Input::ClearGravity => {
assert!(self.gravity.is_some());
self.gravity = None;
}
event::Input::Step => {
#[cfg(feature = "debug_dump")]
let s = self.clone();
self.derivative_evaluation();
self.integrate_velocity();
self.constrain_velocity();
self.integrate_position();
self.constrain_position();
self.collision (&mut output);
#[cfg(feature = "debug_dump")]
if collision::DEBUG_DUMP.swap (false, std::sync::atomic::Ordering::SeqCst)
&& std::env::var ("LINEAR_SIM_DEBUG_DUMP").is_ok()
{
let p = format!("linear-sim-{}.dump", s.step);
log::info!(filepath=p.as_str(); "dumping system");
let mut f = std::fs::File::create (&p).unwrap();
bincode::serde::encode_into_std_write (&s, &mut f, bincode::config::standard())
.unwrap();
}
self.step += 1;
}
}
log::debug!(
step=input_step,
output:?=output.iter().map (ToString::to_string).collect::<Vec<_>>();
"output events");
output
}
pub fn new_object_key (&self, kind : object::Kind) -> Option <object::Key> {
fn next_available_key <T> (map : &VecMap <T>) -> Option <object::Key> {
for i in 0..object::KEY_MAX as usize {
if !map.contains_key (i) {
return Some (i.into())
}
}
None
}
match kind {
object::Kind::Static => next_available_key (&self.objects_static),
object::Kind::Dynamic => next_available_key (&self.objects_dynamic),
object::Kind::Nodetect => next_available_key (&self.objects_nodetect)
}
}
pub fn contacts (&self)
-> Vec <Vec <(object::Id, object::Id, collision::Contact)>>
{
let mut groups = vec![];
for (_, group) in self.collision.contact_groups().iter() {
let mut v = vec![];
for (object_pair, contact) in group.contacts.iter().cloned() {
let (object_id_a, object_id_b) = object_pair.into();
v.push ((object_id_a.into(), object_id_b.into(), contact));
}
groups.push (v);
}
groups
}
fn create_object_static (&mut self,
object : object::Static,
key : object::Key,
output : &mut Vec <event::Output>
) {
let object_id = object::Id { kind: object::Kind::Static, key };
match self.collision.try_add_object_static (
&self.objects_dynamic, &object, key
) {
Ok (()) => {
assert!(self.objects_static.insert (key.index(), object).is_none());
output.push (event::CreateObjectResult::Created (object_id).into());
}
Err (intersections) => {
debug_assert!(!intersections.is_empty());
output.push (
event::CreateObjectResult::Intersection (intersections).into());
}
}
}
fn create_object_dynamic (&mut self,
object : object::Dynamic,
key : object::Key,
output : &mut Vec <event::Output>
) {
let object_id = object::Id { kind: object::Kind::Dynamic, key };
match self.collision.try_add_object_dynamic (
&self.objects_static, &self.objects_dynamic, &object, key
) {
Ok (()) => {
assert!(self.objects_dynamic.insert (key.index(), object).is_none());
output.push (event::CreateObjectResult::Created (object_id).into());
}
Err (intersections) => {
debug_assert!(!intersections.is_empty());
output.push (
event::CreateObjectResult::Intersection (intersections).into());
}
}
}
fn create_object_nodetect (&mut self,
object : object::Nodetect,
key : object::Key,
) {
assert!(self.objects_nodetect.insert (key.index(), object).is_none());
}
fn modify_object_dynamic (&mut self,
key : object::Key,
event : event::ObjectModifyDynamic
) {
let object = &mut self.objects_dynamic[key.index()];
match event {
event::ObjectModifyDynamic::ApplyImpulse (impulse) =>
object.derivatives.velocity += impulse,
event::ObjectModifyDynamic::SetForceFlags (force_flags) =>
object.derivatives.force_flags = force_flags,
event::ObjectModifyDynamic::SetDrag (drag) => object.drag.0 = drag,
event::ObjectModifyDynamic::SetPosition (position) => {
object.position = position;
self.collision.update_object_dynamic (object, key);
log::warn!("TODO: dynamic object set position results");
}
}
}
fn modify_object_static (&mut self,
key : object::Key,
event : event::ObjectModifyStatic
) {
let object = &mut self.objects_static[key.index()];
match event {
event::ObjectModifyStatic::Move (move_vector) => {
object.position.0 += move_vector;
self.collision.update_object_static (object, key);
unimplemented!("TODO: move static object results");
}
}
}
fn derivative_evaluation (&mut self) {
for_sequence!{
(_, object) in (
self.objects_dynamic.iter_mut(), self.objects_nodetect.iter_mut()
) {
object.derivatives.force = [0.0, 0.0, 0.0].into();
}
}
if let Some (gravity) = &self.gravity {
for_sequence!{
(_, object) in (
self.objects_dynamic.iter_mut(), self.objects_nodetect.iter_mut()
) {
if object.derivatives.force_flags.contains (force::Flag::Gravity) {
use crate::Force;
let impulse = gravity.impulse (object, self.step, 1.0);
object.derivatives.force += impulse;
}
}
}
}
for_sequence!{
(_, object) in (
self.objects_dynamic.iter_mut(), self.objects_nodetect.iter_mut()
) {
use object::Inertial;
object.compute_acceleration_inplace();
}
}
}
fn integrate_velocity (&mut self) {
for_sequence!{
(_, object) in (
self.objects_dynamic.iter_mut(), self.objects_nodetect.iter_mut()
) {
INTG::integrate_velocity (object);
object.derivatives.velocity *= 1.0 - object.drag.0;
}
}
}
#[inline]
fn constrain_velocity (&mut self) {
self.collision
.constrain_contact_velocities (&self.objects_static, &mut self.objects_dynamic);
}
fn integrate_position (&mut self) {
for_sequence!{
(_, object) in (
self.objects_dynamic.iter_mut(), self.objects_nodetect.iter_mut()
) {
INTG::integrate_position (object);
}
}
}
#[inline]
fn constrain_position (&mut self) {
self.collision
.constrain_contact_positions (&self.objects_static, &mut self.objects_dynamic);
}
fn collision (&mut self, output : &mut Vec <event::Output>) {
self.collision.detect_resolve_loop (
&mut self.objects_static, &mut self.objects_dynamic, self.step, output);
}
}
pub fn report_sizes() {
use std::mem::size_of;
println!("system report sizes...");
println!(" size of System <integrator::SemiImplicitEuler>: {}",
size_of::<System <integrator::SemiImplicitEuler>>());
println!("...system report sizes");
}
#[cfg(test)]
mod tests {
use super::*;
use crate::component;
use crate::geometry::{self, shape};
#[test]
fn segment_query() {
let segment = geometry::Segment3::new (
[-1.0, 0.0, 0.5].into(),
[ 2.0, 0.0, 0.5].into());
let mut system = System::<integrator::SemiImplicitEuler>::new();
let sphere1 = {
let position = component::Position ([0.5, 0.0, 0.5].into());
let mass = component::Mass::new (20.0);
let derivatives = component::Derivatives::zero();
let drag = component::Drag::zero();
let bound = component::Bound (shape::Bounded::from (
shape::Sphere::noisy (0.5)).into());
let material = component::MATERIAL_STONE;
let collidable = true;
object::Dynamic {
position, mass, derivatives, drag, bound, material, collidable
}.into()
};
let mut result = system
.handle_event (event::Input::CreateObject (sphere1, None));
let sphere1_id = match result.pop().unwrap() {
event::Output::CreateObjectResult (event::CreateObjectResult::Created (id))
=> id,
_ => unreachable!()
};
assert!(result.pop().is_none());
let block1 = {
let position = component::Position ([1.5, 0.0, 0.5].into());
let bound = component::Bound (shape::Bounded::from (
shape::Cuboid::noisy ([0.5, 0.5, 0.5].into())).into());
let material = component::MATERIAL_STONE;
let collidable = true;
object::Static { position, bound, material, collidable }.into()
};
let mut result = system
.handle_event (event::Input::CreateObject (block1, None));
let block1_id = match result.pop().unwrap() {
event::Output::CreateObjectResult (event::CreateObjectResult::Created (id))
=> id,
_ => unreachable!()
};
assert!(result.pop().is_none());
let result = system.segment_query (segment, &[]);
let (frac, point, id) = result.get (0).unwrap();
assert_eq!(*frac, 1.0/3.0);
assert_eq!(*point, [0.0, 0.0, 0.5].into());
assert_eq!(*id, sphere1_id);
let (frac, point, id) = result.get (1).unwrap();
assert_eq!(*frac, 2.0/3.0);
assert_eq!(*point, [1.0, 0.0, 0.5].into());
assert_eq!(*id, block1_id);
}
}