// -*- coding: utf-8 -*- // // Copyright 2021 Michael Büsch // // Licensed under the Apache License version 2.0 // or the MIT license, at your option. // SPDX-License-Identifier: Apache-2.0 OR MIT // pub mod aviation; pub mod walker; use crate::base::{all_axes, Axis, PhyType}; use crate::point::{Point, PointLim}; use crate::rotation::{rotmove::RotMove, Rotation}; use crate::vector::{Vector, VectorLim}; use crate::world::WorldContext; pub trait PhyObject { fn get_common(&self) -> Option<&CommonObject> { None } fn get_common_mut(&mut self) -> Option<&mut CommonObject> { None } fn is_enabled(&self) -> bool { if let Some(common) = self.get_common() { common.is_enabled() } else { Default::default() } } fn set_enabled(&mut self, enabled: bool) { if let Some(common) = self.get_common_mut() { common.set_enabled(enabled); } } fn get_pos(&self) -> Point { if let Some(common) = self.get_common() { common.get_pos() } else { Default::default() } } fn set_pos(&mut self, pos: Point) { if let Some(common) = self.get_common_mut() { common.set_pos(pos); } } fn set_pos_limits(&mut self, axis: Axis, limits: (PhyType, PhyType)) { if let Some(common) = self.get_common_mut() { common.set_pos_limits(axis, limits) } } fn get_rot_move(&self) -> &RotMove { if let Some(common) = self.get_common() { common.get_rot_move() } else { panic!("PhyObject/get_rot_move: No common.") } } fn get_rot_move_mut(&mut self) -> &mut RotMove { if let Some(common) = self.get_common_mut() { common.get_rot_move_mut() } else { panic!("PhyObject/get_rot_move_mut: No common.") } } fn get_rot(&self) -> Rotation { self.get_rot_move().get_rot() } fn set_rot(&mut self, rot: &Rotation) { self.get_rot_move_mut().set_rot(rot); } fn get_speed(&self) -> Vector { if let Some(common) = self.get_common() { common.get_speed() } else { Default::default() } } fn set_speed(&mut self, speed: Vector) { if let Some(common) = self.get_common_mut() { common.set_speed(speed); } } fn set_speed_limits(&mut self, limits: (PhyType, PhyType)) { if let Some(common) = self.get_common_mut() { common.set_speed_limits(limits); } } fn calc(&mut self, context: &WorldContext); } #[derive(Clone, Copy, PartialEq, Debug)] pub enum CollHyst { Hyst(PhyType), None, } #[derive(Clone, Copy, PartialEq, Debug)] pub enum CollDir { Neg, Pos, Omni, } #[derive(Clone, Debug)] pub struct CommonObject { enabled: bool, pos: PointLim, rot_move: RotMove, acc: Vector, speed: VectorLim, } impl CommonObject { fn new() -> CommonObject { CommonObject { enabled: true, pos: PointLim::new(Point::new_zero()), rot_move: RotMove::new_abs(), acc: Vector::new_zero(), speed: VectorLim::new(Vector::new_zero()), } } pub fn get_acc(&self) -> Vector { self.acc } pub fn set_acc(&mut self, acc: Vector) { self.acc = acc; } fn get_dist_to_position_limit(&self, axis: Axis, dir: CollDir) -> PhyType { let lim = self.pos.get_limits(axis); match dir { CollDir::Pos => { if lim.1.is_unlimited() { PhyType::INFINITE } else { lim.1 - self.pos.get(axis) } } CollDir::Neg => { if lim.0.is_unlimited() { PhyType::INFINITE } else { self.pos.get(axis) - lim.0 } } CollDir::Omni => panic!("get_dist_to_position_limit: Invalid direction."), } } pub fn has_collision_axis( &self, axis: Axis, hyst: CollHyst, dir: CollDir, move_vect: Option, ) -> bool { let hyst = match hyst { CollHyst::None => PhyType::EPSILON, CollHyst::Hyst(h) => h, }; match dir { CollDir::Neg | CollDir::Omni if self.get_dist_to_position_limit(axis, CollDir::Neg) < hyst => { if let Some(move_vect) = move_vect { // Moving towards collision or not moving? move_vect.get_axis(axis) <= 0.into() } else { true } } CollDir::Pos | CollDir::Omni if self.get_dist_to_position_limit(axis, CollDir::Pos) < hyst => { if let Some(move_vect) = move_vect { // Moving towards collision or not moving? move_vect.get_axis(axis) >= 0.into() } else { true } } CollDir::Neg | CollDir::Pos | CollDir::Omni => false, } } pub fn has_collision(&self, hyst: CollHyst, move_vect: Option) -> bool { self.has_collision_axis(Axis::X, hyst, CollDir::Omni, move_vect) || self.has_collision_axis(Axis::Y, hyst, CollDir::Omni, move_vect) || self.has_collision_axis(Axis::Z, hyst, CollDir::Omni, move_vect) } pub fn calc_acc(&mut self, context: &WorldContext) { // Add the gravitational acceleration. self.acc += context.gravity(); } pub fn calc_speed(&mut self, context: &WorldContext) { // Calculate the new speed. *self.speed += self.acc * context.period_sec(); self.speed.apply_limits(); } pub fn calc_speed_clipping(&mut self, _context: &WorldContext) { for axis in &all_axes() { let neg_coll = self.has_collision_axis(*axis, CollHyst::None, CollDir::Neg, None); let pos_coll = self.has_collision_axis(*axis, CollHyst::None, CollDir::Pos, None); self.speed.set_clipping( *axis, if neg_coll { 0.into() } else { PhyType::UNLIMITED }, if pos_coll { 0.into() } else { PhyType::UNLIMITED }, ); } self.speed.apply_limits(); } pub fn calc_pos(&mut self, context: &WorldContext) { // Calculate the new position. *self.pos = self.pos.add_vector(*self.speed * context.period_sec()); self.pos.apply_limits(); if false { println!(); // println!("acc = {}", self.acc); println!("speed = {}", self.speed); // println!("pos = {}", self.pos); } } pub fn calc_finalize(&mut self, _context: &WorldContext) { // Clear it for the next calc. self.acc = Vector::new_zero(); } } impl PhyObject for CommonObject { fn is_enabled(&self) -> bool { self.enabled } fn set_enabled(&mut self, enabled: bool) { self.enabled = enabled; } fn get_pos(&self) -> Point { *self.pos } fn set_pos(&mut self, pos: Point) { *self.pos = pos; } fn set_pos_limits(&mut self, axis: Axis, limits: (PhyType, PhyType)) { self.pos.set_limits(axis, limits); } fn get_rot_move(&self) -> &RotMove { &self.rot_move } fn get_rot_move_mut(&mut self) -> &mut RotMove { &mut self.rot_move } fn get_speed(&self) -> Vector { *self.speed } fn set_speed(&mut self, speed: Vector) { *self.speed = speed; } fn set_speed_limits(&mut self, limits: (PhyType, PhyType)) { self.speed.set_limits(limits.0, limits.1); } fn calc(&mut self, _context: &WorldContext) { std::unimplemented!("CommonObject has no calc() method."); } } #[cfg(test)] mod tests { use super::*; #[test] fn test_pos() { let mut a = CommonObject::new(); assert_eq!(a.get_pos().x(), 0.into()); assert_eq!(a.get_pos().y(), 0.into()); assert_eq!(a.get_pos().z(), 0.into()); a.set_pos(Point::new(1.into(), 2.into(), 3.into())); assert_eq!(a.get_pos().x(), 1.into()); assert_eq!(a.get_pos().y(), 2.into()); assert_eq!(a.get_pos().z(), 3.into()); } #[test] fn test_acc() { let mut a = CommonObject::new(); assert_eq!(a.get_acc().x(), 0.into()); assert_eq!(a.get_acc().y(), 0.into()); assert_eq!(a.get_acc().z(), 0.into()); a.set_acc(Vector::new(1.into(), 2.into(), 3.into())); assert_eq!(a.get_acc().x(), 1.into()); assert_eq!(a.get_acc().y(), 2.into()); assert_eq!(a.get_acc().z(), 3.into()); } #[test] fn test_speed() { let mut a = CommonObject::new(); assert_eq!(a.get_speed().x(), 0.into()); assert_eq!(a.get_speed().y(), 0.into()); assert_eq!(a.get_speed().z(), 0.into()); a.set_speed(Vector::new(1.into(), 2.into(), 3.into())); assert_eq!(a.get_speed().x(), 1.into()); assert_eq!(a.get_speed().y(), 2.into()); assert_eq!(a.get_speed().z(), 3.into()); } } // vim: ts=4 sw=4 expandtab