diff --git a/gui/lib/core/src/animation.rs b/gui/lib/core/src/animation.rs deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gui/lib/core/src/animation/animator.rs b/gui/lib/core/src/animation/animator.rs new file mode 100644 index 00000000000..af1ce18ada7 --- /dev/null +++ b/gui/lib/core/src/animation/animator.rs @@ -0,0 +1,47 @@ +use super::ContinuousTimeAnimator; +use super::FnAnimation; + + + +// ==================== +// === AnimatorData === +// ==================== + +struct AnimatorData { + closure : Box, + previous_ms : Option +} + +impl AnimatorData { + pub fn new(f:F) -> Self { + let closure = Box::new(f); + let previous_ms = None; + Self { closure, previous_ms } + } +} + + + +// ================ +// === Animator === +// ================ + +/// This structure runs an animation every frame with the time difference from the last frame as +/// its input. +pub struct Animator { + _continuous_animator: ContinuousTimeAnimator +} + +impl Animator { + pub fn new(f:F) -> Self { + let mut data = AnimatorData::new(f); + let _continuous_animator = ContinuousTimeAnimator::new(move |current_ms| { + if let Some(previous_ms) = data.previous_ms { + let delta_ms = current_ms - previous_ms; + (data.closure)(delta_ms); + } + data.previous_ms = Some(current_ms); + }); + Self { _continuous_animator } + } +} diff --git a/gui/lib/core/src/animation/continuous_time_animator.rs b/gui/lib/core/src/animation/continuous_time_animator.rs new file mode 100644 index 00000000000..5d7f44a77c2 --- /dev/null +++ b/gui/lib/core/src/animation/continuous_time_animator.rs @@ -0,0 +1,64 @@ +use crate::system::web::get_performance; +use crate::system::web::animation_frame_loop::AnimationFrameLoop; +use super::FnAnimation; + +use std::rc::Rc; +use std::cell::RefCell; + + +// ================================== +// === ContinuousTimeAnimatorData === +// ================================== + +struct ContinuousTimeAnimatorData { + closure : Box, + start_time : f32, + current_time : f32 +} + +impl ContinuousTimeAnimatorData { + pub fn new(f:F) -> Self { + let closure = Box::new(f); + let start_time = get_performance().expect("Couldn't get performance timer").now() as f32; + let current_time = start_time; + Self { closure,start_time,current_time } + } + + pub fn set_time(&mut self, time:f32) { + self.start_time = self.current_time + time; + } +} + + + +// ============================== +// === ContinuousTimeAnimator === +// ============================== + +/// This structure runs an animation with continuous time as its input. +pub struct ContinuousTimeAnimator { + _animation_loop : AnimationFrameLoop, + data : Rc> +} + +impl ContinuousTimeAnimator { + pub fn new(f:F) -> Self { + let data = Rc::new(RefCell::new(ContinuousTimeAnimatorData::new(f))); + let data_clone = data.clone(); + let _animation_loop = AnimationFrameLoop::new(move |current_time| { + let mut data : &mut ContinuousTimeAnimatorData = &mut data_clone.borrow_mut(); + (data.closure)(current_time - data.start_time); + data.current_time = current_time; + }); + Self { _animation_loop, data } + } +} + + +// === Setters === + +impl ContinuousTimeAnimator { + pub fn set_time(&mut self, time:f32) { + self.data.borrow_mut().set_time(time); + } +} \ No newline at end of file diff --git a/gui/lib/core/src/animation/fixed_step_animator.rs b/gui/lib/core/src/animation/fixed_step_animator.rs new file mode 100644 index 00000000000..a50c573ae54 --- /dev/null +++ b/gui/lib/core/src/animation/fixed_step_animator.rs @@ -0,0 +1,79 @@ +use super::Animator; +use super::FnAnimation; + +use nalgebra::zero; + + + +// ======================= +// === IntervalCounter === +// ======================= + +/// This struct counts the intervals in a time period. +pub struct IntervalCounter { + pub interval_duration : f32, + pub accumulated_time : f32 +} + +impl IntervalCounter { + pub fn new(interval_duration:f32) -> Self { + let accumulated_time = zero(); + Self { interval_duration, accumulated_time } + } + + /// Adds time to the counter and returns the number of intervals it reached. + pub fn add_time(&mut self, time:f32) -> u32 { + self.accumulated_time += time; + let count = (self.accumulated_time / self.interval_duration) as u32; + self.accumulated_time -= count as f32 * self.interval_duration; + count + } +} + +// ============================= +// === FixedStepAnimatorData === +// ============================= + +struct FixedStepAnimatorData { + closure : Box, + counter : IntervalCounter +} + +impl FixedStepAnimatorData { + pub fn new(steps_per_second:f32, f:F) -> Self { + let closure = Box::new(f); + let step_duration = 1000.0 / steps_per_second; + let counter = IntervalCounter::new(step_duration); + Self { closure,counter } + } +} + + + +// ================ +// === Animator === +// ================ + +/// This structure attempts to run a closure at a fixed time rate. +/// +/// # Internals +/// If, for instance, we want to run FnAnimation once per second, it's delta_time +/// (FnAnimation(delta_time)) will be 1 second. But keep in mind that if the actual frame takes +/// longer, say 2 seconds, FnAnimation will be called twice in the same moment, but its delta_time +/// parameter will always be fixed to 1 second. +pub struct FixedStepAnimator { + _animator: Animator +} + +impl FixedStepAnimator { + pub fn new(steps_per_second:f32, f:F) -> Self { + let mut data = FixedStepAnimatorData::new(steps_per_second, f); + let _animator = Animator::new(move |delta_ms| { + let intervals = data.counter.add_time(delta_ms); + for _ in 0..intervals { + (data.closure)(data.counter.interval_duration); + } + }); + Self { _animator } + } +} diff --git a/gui/lib/core/src/animation/mod.rs b/gui/lib/core/src/animation/mod.rs new file mode 100644 index 00000000000..313d24411d9 --- /dev/null +++ b/gui/lib/core/src/animation/mod.rs @@ -0,0 +1,35 @@ +// FIXME: Animators structs should get EventLoop as parameter. The whole application should have +// only one RequestAnimationFrame loop going on to avoid its overhead. + +pub mod physics; +mod continuous_time_animator; +mod animator; +mod fixed_step_animator; + +pub use continuous_time_animator::ContinuousTimeAnimator; +pub use animator::Animator; +pub use fixed_step_animator::FixedStepAnimator; +pub use fixed_step_animator::IntervalCounter; + +// =================== +// === FnAnimation === +// =================== + +pub trait FnAnimation = FnMut(f32) + 'static; + + + +// FIXME: The objects in this section needs a better place. +// ============= +// === Utils === +// ============= + +use nalgebra::clamp; +use std::ops::Mul; +use std::ops::Add; + +pub fn linear_interpolation(a:T, b:T, t:f32) -> T +where T : Mul + Add { + let t = clamp(t, 0.0, 1.0); + a * (1.0 - t) + b * t +} diff --git a/gui/lib/core/src/animation/physics.rs b/gui/lib/core/src/animation/physics.rs new file mode 100644 index 00000000000..f16ae534274 --- /dev/null +++ b/gui/lib/core/src/animation/physics.rs @@ -0,0 +1,320 @@ +use crate::prelude::*; + +use crate::animation::{Animator, IntervalCounter, linear_interpolation}; +use crate::traits::HasPosition; + +use nalgebra::Vector3; +use nalgebra::zero; + +// ==================== +// === PhysicsForce === +// ==================== + +pub trait PhysicsForce { + fn force(&self, kinematics:&KinematicsProperties) -> Vector3; +} + +// ====================== +// === DragProperties === +// ====================== + +/// This structure contains air dragging properties. +#[derive(Default, Clone, Copy)] +pub struct DragProperties { + coefficient: f32 +} + +impl DragProperties { + pub fn new(amount:f32) -> Self { + Self { coefficient: amount } + } +} + +impl PhysicsForce for DragProperties { + fn force(&self, kinematics:&KinematicsProperties) -> Vector3 { + let velocity = kinematics.velocity; + let speed = velocity.norm(); + if speed > 0.0 { + velocity.normalize() * speed * speed * -0.5 * self.coefficient + } else { + zero() + } + } +} + +// === Getters === + +impl DragProperties { + pub fn coefficient(self) -> f32 { + self.coefficient + } +} + + +// === Setters === + +impl DragProperties { + pub fn set_coefficient(&mut self, coefficient:f32) { + self.coefficient = coefficient + } +} + + + +// ======================== +// === SpringProperties === +// ======================== + +/// This structure contains spring physics properties. +#[derive(Debug, Clone, Copy)] +pub struct SpringProperties { + coefficient : f32, + fixed_point : Vector3 +} + +impl Default for SpringProperties { + fn default() -> Self { + Self::new(zero(),zero()) + } +} + +impl SpringProperties { + pub fn new(coefficient:f32, fixed_point:Vector3) -> Self { + Self { coefficient,fixed_point } + } +} + + +// === Getters === + +impl SpringProperties { + pub fn coefficient(&self) -> f32 { self.coefficient } + pub fn fixed_point(&self) -> Vector3 { self.fixed_point } +} + +impl PhysicsForce for SpringProperties { + fn force(&self, kinematics:&KinematicsProperties) -> Vector3 { + let delta = self.fixed_point() - kinematics.position(); + let delta_len = delta.magnitude(); + if delta_len > 0.0 { + let force_val = delta_len * self.coefficient(); + delta.normalize() * force_val + } else { + zero() + } + } +} + +// === Setters === + +impl SpringProperties { + pub fn set_coefficient(&mut self, coefficient:f32) { self.coefficient = coefficient } + pub fn set_fixed_point(&mut self, fixed_point:Vector3) { self.fixed_point = fixed_point } +} + + + +// ============================ +// === KinematicProperties === +// ============================ + +/// This structure contains kinematics properties. +#[derive(Debug, Clone, Copy)] +pub struct KinematicsProperties { + position : Vector3, + velocity : Vector3, + acceleration : Vector3, + mass : f32 +} + +impl Default for KinematicsProperties { + fn default() -> Self { + Self::new(zero(),zero(),zero(),zero()) + } +} + +impl KinematicsProperties { + pub fn new + (position:Vector3, velocity:Vector3, acceleration:Vector3, mass:f32) -> Self { + Self { position,velocity,acceleration,mass } + } +} + + +// === Getters === + +impl KinematicsProperties { + pub fn velocity (&self) -> Vector3 { self.velocity } + pub fn acceleration(&self) -> Vector3 { self.acceleration } + pub fn mass (&self) -> f32 { self.mass } +} + + +// === Setters === + +impl KinematicsProperties { + pub fn set_velocity(&mut self, velocity:Vector3) { + self.velocity = velocity + } + + pub fn set_acceleration(&mut self, acceleration:Vector3) { + self.acceleration = acceleration + } + + pub fn set_mass(&mut self, mass:f32) { + self.mass = mass + } +} + +impl HasPosition for KinematicsProperties { + fn position (&self) -> Vector3 { self.position } + fn set_position(&mut self, position:Vector3) { self.position = position } +} + + + +// ============================= +// === PhysicsPropertiesData === +// ============================= + +struct PhysicsPropertiesData { + kinematics : KinematicsProperties, + spring : SpringProperties, + drag : DragProperties +} + +impl PhysicsPropertiesData { + pub fn new + (kinematics: KinematicsProperties, spring:SpringProperties, drag:DragProperties) -> Self { + Self { kinematics,spring,drag } + } +} + + + +// ========================= +// === PhysicsProperties === +// ========================= + +/// A structure including kinematics, drag and spring properties. +#[derive(Clone)] +pub struct PhysicsProperties { + data : Rc> +} + +impl PhysicsProperties { + pub fn new + (kinematics: KinematicsProperties, spring:SpringProperties, drag:DragProperties) -> Self { + let data = Rc::new(RefCell::new(PhysicsPropertiesData::new(kinematics, spring, drag))); + Self { data } + } +} + + +// === Getters === + +impl PhysicsProperties { + pub fn kinematics(&self) -> KinematicsProperties { self.data.borrow().kinematics } + pub fn spring (&self) -> SpringProperties { self.data.borrow().spring } + pub fn drag (&self) -> DragProperties { self.data.borrow().drag } +} + + +// === Setters === + +impl PhysicsProperties { + pub fn mod_kinematics(&mut self, f:F) { + let mut kinematics = self.kinematics(); + f(&mut kinematics); + self.set_kinematics(kinematics); + } + + pub fn set_kinematics(&mut self, kinematics:KinematicsProperties) { + self.data.borrow_mut().kinematics = kinematics; + } + + pub fn mod_spring(&mut self, f:F) { + let mut spring = self.spring(); + f(&mut spring); + self.set_spring(spring); + } + + pub fn set_spring(&mut self, spring:SpringProperties) { + self.data.borrow_mut().spring = spring; + } + + pub fn mod_drag(&mut self, f:F) { + let mut drag = self.drag(); + f(&mut drag); + self.set_drag(drag); + } + + pub fn set_drag(&mut self, drag:DragProperties) { + self.data.borrow_mut().drag = drag; + } +} + + + +// ======================== +// === SimulationObject === +// ======================== + +pub trait SimulationObject = HasPosition + 'static; + + + +// ======================== +// === PhysicsSimulator === +// ======================== + +/// A 60 steps per second physics simulator used to simulate `Properties`. +pub struct PhysicsSimulator { + _animator : Animator +} + +impl PhysicsSimulator { + /// Simulates `Properties` on `object`. + pub fn new(mut object:T, mut properties:PhysicsProperties) -> Self { + properties.mod_kinematics(|kinematics| { kinematics.set_position(object.position()); }); + let steps_per_second = 60.0; + let step_ms = 1000.0 / steps_per_second; + let mut current_position = object.position(); + let mut next_position = simulate(&mut properties, step_ms); + let mut interval_counter = IntervalCounter::new(step_ms); + let _animator = Animator::new(move |delta_ms| { + let intervals = interval_counter.add_time(delta_ms); + for _ in 0..intervals { + current_position = next_position; + next_position = simulate(&mut properties, step_ms); + } + + let transition = interval_counter.accumulated_time / interval_counter.interval_duration; + let position = linear_interpolation(current_position, next_position, transition); + object.set_position(position); + }); + + Self { _animator } + } +} + +/// Simulate the `KinematicProperties`. +fn simulate_kinematics(kinematics:&mut KinematicsProperties, force:&Vector3, dt:f32) { + kinematics.set_acceleration(force / kinematics.mass); + kinematics.set_velocity(kinematics.velocity() + kinematics.acceleration() * dt); + kinematics.set_position(kinematics.position() + kinematics.velocity() * dt); +} + +/// Runs a simulation step. +fn simulate(properties:&mut PhysicsProperties, delta_ms:f32) -> Vector3 { + let spring = properties.spring(); + let drag = properties.drag(); + let mut net_force = zero(); + properties.mod_kinematics(|mut kinematics| { + net_force += spring.force(&kinematics); + net_force += drag.force(&kinematics); + let delta_seconds = delta_ms / 1000.0; + simulate_kinematics(&mut kinematics, &net_force, delta_seconds); + }); + properties.kinematics().position() +} diff --git a/gui/lib/core/src/control/event_loop.rs b/gui/lib/core/src/control/event_loop.rs index 201b95c22ba..28680ec053c 100644 --- a/gui/lib/core/src/control/event_loop.rs +++ b/gui/lib/core/src/control/event_loop.rs @@ -35,7 +35,7 @@ impl EventLoop { /// Init the event loop. fn init(self) -> Self { let data = Rc::downgrade(&self.rc); - let main = move || { data.upgrade().map(|t| t.borrow_mut().run()); }; + let main = move |_| { data.upgrade().map(|t| t.borrow_mut().run()); }; with(self.rc.borrow_mut(), |mut data| { data.main = Some(Closure::new(main)); data.run(); @@ -60,7 +60,7 @@ impl EventLoop { #[derive(Derivative)] #[derivative(Debug, Default)] pub struct EventLoopData { - main : Option>, + main : Option>, callbacks : CallbackRegistry, main_id : i32, } diff --git a/gui/lib/core/src/display/render/css3d/html/html_object.rs b/gui/lib/core/src/display/render/css3d/html/html_object.rs index 0800bd9a8b2..84231dbab07 100644 --- a/gui/lib/core/src/display/render/css3d/html/html_object.rs +++ b/gui/lib/core/src/display/render/css3d/html/html_object.rs @@ -6,16 +6,19 @@ use crate::system::web::dyn_into; use crate::system::web::Result; use crate::system::web::Error; use crate::system::web::StyleSetter; +use crate::traits::HasPosition; -use nalgebra::Vector2; +use nalgebra::{Vector2, Vector3}; use web_sys::HtmlElement; + + // ================== // === HTMLObject === // ================== /// A structure for representing a 3D HTMLElement in a `HTMLScene`. -#[derive(Shrinkwrap, Debug)] +#[derive(Shrinkwrap, Debug, Clone)] #[shrinkwrap(mutable)] pub struct HTMLObject { #[shrinkwrap(main_field)] @@ -24,6 +27,16 @@ pub struct HTMLObject { dimensions : Vector2, } +impl HasPosition for HTMLObject { + fn position(&self) -> Vector3 { + self.object.position() + } + + fn set_position(&mut self, position:Vector3) { + self.object.set_position(position) + } +} + impl HTMLObject { /// Creates a HTMLObject from element name. pub fn new(dom_name: &str) -> Result { diff --git a/gui/lib/core/src/display/render/css3d/html/html_renderer.rs b/gui/lib/core/src/display/render/css3d/html/html_renderer.rs index 7bc8d2fa7df..c9e35546834 100644 --- a/gui/lib/core/src/display/render/css3d/html/html_renderer.rs +++ b/gui/lib/core/src/display/render/css3d/html/html_renderer.rs @@ -18,6 +18,12 @@ use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsValue; use web_sys::HtmlElement; + + +// =================== +// === Js Bindings === +// =================== + mod js { use super::*; #[wasm_bindgen(module = "/src/display/render/css3d/html/snippets.js")] @@ -74,6 +80,8 @@ fn setup_camera_transform } } + + // ======================== // === HTMLRendererData === // ======================== @@ -145,7 +153,7 @@ impl HTMLRenderer { /// Renders the `Scene` from `Camera`'s point of view. pub fn render(&self, camera: &mut Camera, scene: &Scene) { - let trans_cam = camera.transform.to_homogeneous().try_inverse(); + let trans_cam = camera.transform().to_homogeneous().try_inverse(); let trans_cam = trans_cam.expect("Camera's matrix is not invertible."); let trans_cam = trans_cam.map(eps); let trans_cam = invert_y(trans_cam); @@ -166,7 +174,7 @@ impl HTMLRenderer { let scene : &Scene = &scene; for object in &mut scene.into_iter() { - let mut transform = object.transform.to_homogeneous(); + let mut transform = object.transform().to_homogeneous(); transform.iter_mut().for_each(|a| *a = eps(*a)); let parent_node = object.dom.parent_node(); diff --git a/gui/lib/core/src/display/render/css3d/object.rs b/gui/lib/core/src/display/render/css3d/object.rs index 19ec58e24d6..dfd9fb48e5f 100644 --- a/gui/lib/core/src/display/render/css3d/object.rs +++ b/gui/lib/core/src/display/render/css3d/object.rs @@ -5,67 +5,77 @@ use crate::display::render::css3d::Transform; use nalgebra::UnitQuaternion; use nalgebra::Vector3; +use std::rc::Rc; +use std::cell::RefCell; +use crate::traits::HasPosition; + + + // ============== // === Object === // ============== /// Base structure for representing a 3D object in a `Scene`. -#[derive(Default, Debug)] +#[derive(Default, Debug, Clone)] pub struct Object { - pub transform : Transform + transform : Rc> } impl Object { /// Creates a Default Object. pub fn new() -> Object { default() } - /// Sets the object's position. - pub fn set_position(&mut self, x:f32, y:f32, z:f32) { - self.transform.set_translation(x, y, z) - } - - /// Gets the object's position. - pub fn position(&self) -> &Vector3 { - self.transform.translation() - } + /// Gets object's transform. + pub fn transform(&self) -> Transform { *self.transform.borrow() } /// Sets the object's rotation in YXZ (yaw -> roll -> pitch) order. pub fn set_rotation(&mut self, roll:f32, pitch:f32, yaw:f32) { - self.transform.set_rotation(roll, pitch, yaw) + self.transform.borrow_mut().set_rotation(roll, pitch, yaw) } /// Gets the object's rotation UnitQuaternion. - pub fn rotation(&self) -> &UnitQuaternion { - self.transform.rotation() + pub fn rotation(&self) -> UnitQuaternion { + *self.transform.borrow().rotation() } /// Sets the object's scale. pub fn set_scale(&mut self, x: f32, y: f32, z: f32) { - self.transform.set_scale(x, y, z); + self.transform.borrow_mut().set_scale(x, y, z); } /// Gets the object's scale. - pub fn scale(&self) -> &Vector3 { - self.transform.scale() + pub fn scale(&self) -> Vector3 { + *self.transform.borrow().scale() + } +} + +impl HasPosition for Object { + fn position(&self) -> Vector3 { + *self.transform.borrow().translation() + } + + fn set_position(&mut self, position:Vector3) { + self.transform.borrow_mut().set_translation(position) } } #[cfg(test)] mod test { + use super::Object; + use crate::traits::HasPosition; + use nalgebra::Vector3; + use nalgebra::Quaternion; + use std::f32::consts::PI; + #[test] fn set_transform() { - use super::Object; - use nalgebra::Vector3; - use nalgebra::Quaternion; - use std::f32::consts::PI; - let mut object = Object::new(); - object.set_position(1.0, 2.0, 3.0); + object.set_position(Vector3::new(1.0, 2.0, 3.0)); object.set_scale(3.0, 2.0, 1.0); object.set_rotation(PI * 2.0, PI, PI / 2.0); - assert_eq!(*object.position(), Vector3::new(1.0, 2.0, 3.0)); - assert_eq!(*object.scale(), Vector3::new(3.0, 2.0, 1.0)); + assert_eq!(object.position(), Vector3::new(1.0, 2.0, 3.0)); + assert_eq!(object.scale(), Vector3::new(3.0, 2.0, 1.0)); let expected = Quaternion::new ( 0.00000009272586 diff --git a/gui/lib/core/src/display/render/css3d/scene.rs b/gui/lib/core/src/display/render/css3d/scene.rs index ffe2e7819d7..20b59226d18 100644 --- a/gui/lib/core/src/display/render/css3d/scene.rs +++ b/gui/lib/core/src/display/render/css3d/scene.rs @@ -3,6 +3,8 @@ use crate::data::opt_vec::*; type Index = usize; + + // ============= // === Scene === // ============= @@ -38,20 +40,24 @@ impl Scene { pub fn is_empty(&self) -> bool { self.objects.is_empty() } + + /// Gets mutable iterator. + pub fn iter_mut(&mut self) -> IterMut<'_, T> { self.objects.iter_mut() } + + /// Gets iterator. + pub fn iter(&self) -> Iter<'_, T> { self.objects.iter() } } impl<'a, T> IntoIterator for &'a Scene { type Item = &'a T; type IntoIter = Iter<'a, T>; fn into_iter(self) -> Self::IntoIter { - self.objects.into_iter() + self.iter() } } impl<'a, T> IntoIterator for &'a mut Scene { type Item = &'a mut T; - type IntoIter = IterMut<'a, T>; - fn into_iter(self) -> Self::IntoIter { - (&mut self.objects).into_iter() - } + type IntoIter = IterMut<'a, T> ; + fn into_iter(self) -> Self::IntoIter { self.iter_mut() } } diff --git a/gui/lib/core/src/display/render/css3d/transform.rs b/gui/lib/core/src/display/render/css3d/transform.rs index bf96bd44ce8..f7ffe00d035 100644 --- a/gui/lib/core/src/display/render/css3d/transform.rs +++ b/gui/lib/core/src/display/render/css3d/transform.rs @@ -5,6 +5,8 @@ use nalgebra::Quaternion; use nalgebra::UnitQuaternion; use nalgebra::Vector3; + + // ============= // === Utils === // ============= @@ -22,12 +24,14 @@ fn from_euler_angles_pry(roll:f32, pitch:f32, yaw:f32) -> UnitQuaternion { )) } + + // ================= // === Transform === // ================= /// A structure representing 3D Position, Rotation and Scale. -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct Transform { pub translation : Vector3, pub rotation : UnitQuaternion, @@ -48,8 +52,8 @@ impl Transform { pub fn identity() -> Self { default() } /// Sets Transform's translation. - pub fn set_translation(&mut self, x:f32, y:f32, z:f32) { - self.translation = Vector3::new(x, y, z); + pub fn set_translation(&mut self, translation:Vector3) { + self.translation = translation; } /// Gets Transform's translation. @@ -121,6 +125,8 @@ impl Transform { } } + + // ============= // === Tests === // ============= @@ -147,7 +153,7 @@ mod test { use std::f32::consts::PI; let mut transform = Transform::identity(); - transform.set_translation(1.0, 2.0, 3.0); + transform.set_translation(Vector3::new(1.0, 2.0, 3.0)); transform.set_scale(3.0, 2.0, 1.0); transform.set_rotation(PI * 2.0, PI, PI / 2.0); diff --git a/gui/lib/core/src/lib.rs b/gui/lib/core/src/lib.rs index af51f5b4037..b3b54a73e32 100644 --- a/gui/lib/core/src/lib.rs +++ b/gui/lib/core/src/lib.rs @@ -26,6 +26,7 @@ pub mod control; pub mod data; pub mod debug; pub mod display; +pub mod traits; pub use basegl_prelude as prelude; pub mod system { diff --git a/gui/lib/core/src/traits.rs b/gui/lib/core/src/traits.rs new file mode 100644 index 00000000000..d5b1fdef71b --- /dev/null +++ b/gui/lib/core/src/traits.rs @@ -0,0 +1,8 @@ +use nalgebra::Vector3; + +pub trait HasPosition { + /// Gets self's position. + fn position(&self) -> Vector3; + /// Sets self's position. + fn set_position(&mut self, position:Vector3); +} diff --git a/gui/lib/core/tests/html_renderer.rs b/gui/lib/core/tests/html_renderer.rs index 90c67e35ffb..f4c93a4db63 100644 --- a/gui/lib/core/tests/html_renderer.rs +++ b/gui/lib/core/tests/html_renderer.rs @@ -24,8 +24,10 @@ mod tests { use basegl::display::render::css3d::html::HTMLRenderer; use basegl::system::web::StyleSetter; use basegl::system::web::get_performance; + use basegl::traits::HasPosition; use web_test::*; use web_sys::Performance; + use nalgebra::Vector3; #[web_test(no_container)] fn invalid_container() { @@ -44,7 +46,7 @@ mod tests { assert_eq!((view_dim.x, view_dim.y), (320.0, 240.0)); let mut object = HTMLObject::new("div").unwrap(); - object.set_position(0.0, 0.0, 0.0); + object.set_position(Vector3::new(0.0, 0.0, 0.0)); object.dom.set_property_or_panic("background-color", "black"); object.set_dimensions(100.0, 100.0); scene.add(object); @@ -52,7 +54,7 @@ mod tests { let aspect_ratio = view_dim.x / view_dim.y; let mut camera = Camera::perspective(45.0, aspect_ratio, 1.0, 1000.0); // We move the Camera behind the object so we don't see it. - camera.set_position(0.0, 0.0, -100.0); + camera.set_position(Vector3::new(0.0, 0.0, -100.0)); renderer.render(&mut camera, &scene); } @@ -76,7 +78,7 @@ mod tests { let x = (i * axis.0) as f32; let y = (i * axis.1) as f32; let z = (i * axis.2) as f32; - object.set_position(x, y, z); + object.set_position(Vector3::new(x, y, z)); // Creates a gradient color based on the axis. let r = (x * 25.5) as u8; @@ -105,7 +107,7 @@ mod tests { let mut camera = Camera::perspective(45.0, aspect_ratio, 1.0, 1000.0); // We move the Camera 29 units away from the center. - camera.set_position(0.0, 0.0, 29.0); + camera.set_position(Vector3::new(0.0, 0.0, 29.0)); renderer.render(&mut camera, &scene); } @@ -125,7 +127,7 @@ mod tests { let mut camera = Camera::perspective(45.0, aspect_ratio, 1.0, 1000.0); // We move the Camera -29 units away from the center. - camera.set_position(0.0, 0.0, -29.0); + camera.set_position(Vector3::new(0.0, 0.0, -29.0)); // We rotate it 180 degrees so we can see the center of the scene // from behind. camera.set_rotation(0.0, PI, 0.0); @@ -150,7 +152,7 @@ mod tests { b.iter(move || { let t = (performance.now() / 1000.0) as f32; // We move the Camera 29 units away from the center. - camera.set_position(t.sin() * 5.0, t.cos() * 5.0, 29.0); + camera.set_position(Vector3::new(t.sin() * 5.0, t.cos() * 5.0, 29.0)); renderer.render(&mut camera, &scene); }) @@ -174,7 +176,7 @@ mod tests { x += (y * 1.25 + t * 2.50).cos() * 0.5; y += (z * 1.25 + t * 2.00).cos() * 0.5; z += (x * 1.25 + t * 3.25).cos() * 0.5; - object.set_position(x * 5.0, y * 5.0, z * 5.0); + object.set_position(Vector3::new(x * 5.0, y * 5.0, z * 5.0)); let faster_t = t * 100.0; let r = (i + 0.0 + faster_t) as u8 % 255; @@ -208,7 +210,7 @@ mod tests { .expect("Couldn't get performance obj"); // We move the Camera 29 units away from the center. - camera.set_position(0.0, 0.0, 29.0); + camera.set_position(Vector3::new(0.0, 0.0, 29.0)); make_sphere(&mut scene, &performance); @@ -241,7 +243,7 @@ mod tests { .expect("Couldn't get performance obj"); // We move the Camera 29 units away from the center. - camera.set_position(0.0, 0.0, 29.0); + camera.set_position(Vector3::new(0.0, 0.0, 29.0)); b.iter(move || { make_sphere(&mut scene, &performance); diff --git a/gui/lib/core/tests/physics_simulator.rs b/gui/lib/core/tests/physics_simulator.rs new file mode 100644 index 00000000000..f7770c9f612 --- /dev/null +++ b/gui/lib/core/tests/physics_simulator.rs @@ -0,0 +1,75 @@ +//! Test suite for the Web and headless browsers. +#![cfg(target_arch = "wasm32")] + +use web_test::web_configure; +web_configure!(run_in_browser); + +#[cfg(test)] +mod tests { + use basegl::system::web::StyleSetter; + use basegl::animation::physics::DragProperties; + use basegl::animation::physics::SpringProperties; + use basegl::animation::physics::KinematicsProperties; + use basegl::animation::FixedStepAnimator; + use basegl::animation::physics::PhysicsSimulator; + use basegl::animation::physics::PhysicsProperties; + use basegl::display::render::css3d::html::HTMLRenderer; + use basegl::display::render::css3d::html::HTMLObject; + use basegl::display::render::css3d::Scene; + use basegl::display::render::css3d::Camera; + use basegl::traits::HasPosition; + use web_test::*; + use nalgebra::{zero, Vector3}; + use js_sys::Math::random; + + #[web_bench] + fn simulator(b : &mut Bencher) { + let renderer = HTMLRenderer::new("simulator").expect("Renderer couldn't be created"); + renderer.container.dom.set_property_or_panic("background-color", "black"); + + let mut scene : Scene = Scene::new(); + + let mut target = HTMLObject::new("div").unwrap(); + target.set_dimensions(1.0, 1.0); + target.dom.set_property_or_panic("background-color", "green"); + scene.add(target.clone()); + + let mut object = HTMLObject::new("div").unwrap(); + object.set_dimensions(1.0, 1.0); + object.dom.set_property_or_panic("background-color", "red"); + scene.add(object.clone()); + + let view_dim = renderer.dimensions(); + assert_eq!((view_dim.x, view_dim.y), (320.0, 240.0)); + + let aspect_ratio = view_dim.x / view_dim.y; + let mut camera = Camera::perspective(45.0, aspect_ratio, 1.0, 1000.0); + camera.set_position(Vector3::new(0.0, 0.0, 29.0)); + + let mass = 2.0; + let kinematics = KinematicsProperties::new(zero(), zero(), zero(), mass); + let coefficient = 10.0; + let fixed_point = zero(); + let spring = SpringProperties::new(coefficient, fixed_point); + let drag = DragProperties::new(0.8); + let mut properties = PhysicsProperties::new(kinematics, spring, drag); + let simulator = PhysicsSimulator::new(object, properties.clone()); + + // Updates spring's fixed point every two seconds. + let every = 2.0; + let animator = FixedStepAnimator::new(1.0 / every, move |_| { + let x = 32.0 * (random() - 0.5) as f32; + let y = 24.0 * (random() - 0.5) as f32; + let z = 0.0; + let position = Vector3::new(x, y, z); + properties.mod_spring(|spring| spring.set_fixed_point(position)); + target.set_position(position); + }); + + b.iter(move || { + let _keep_alive = &simulator; + let _keep_alive = &animator; + renderer.render(&mut camera, &scene); + }); + } +} diff --git a/gui/lib/system/web/src/animationframeloop.rs b/gui/lib/system/web/src/animation_frame_loop.rs similarity index 76% rename from gui/lib/system/web/src/animationframeloop.rs rename to gui/lib/system/web/src/animation_frame_loop.rs index d43d7414701..36a422e7fd2 100644 --- a/gui/lib/system/web/src/animationframeloop.rs +++ b/gui/lib/system/web/src/animation_frame_loop.rs @@ -5,6 +5,14 @@ use std::cell::RefCell; use wasm_bindgen::prelude::Closure; +// ======================= +// === FnAnimationLoop === +// ======================= + +pub trait FnAnimationLoop = FnMut(f32) + 'static; + + + // ========================== // === AnimationFrameData === // ========================== @@ -19,13 +27,14 @@ pub struct AnimationFrameLoop { } + // ========================== // === AnimationFrameLoop === // ========================== impl AnimationFrameLoop { - pub fn new(mut func:Box) -> Self { - let nop_func = Box::new(|| ()) as Box; + pub fn new(mut f:F) -> Self { + let nop_func = Box::new(|_| ()) as Box; let nop_closure = Closure::once(nop_func); let callback = Rc::new(RefCell::new(nop_closure)); let run = true; @@ -33,13 +42,13 @@ impl AnimationFrameLoop { let callback_clone = callback.clone(); let data_clone = data.clone(); - *callback.borrow_mut() = Closure::wrap(Box::new(move || { + *callback.borrow_mut() = Closure::wrap(Box::new(move |delta_time| { if data_clone.borrow().run { - func(); + f(delta_time); let clb = &callback_clone.borrow(); request_animation_frame(&clb).expect("Request Animation Frame"); } - }) as Box); + }) as Box); request_animation_frame(&callback.borrow()).unwrap(); let forget = false; diff --git a/gui/lib/system/web/src/lib.rs b/gui/lib/system/web/src/lib.rs index 6fa94238bf7..c2c074b0d4a 100644 --- a/gui/lib/system/web/src/lib.rs +++ b/gui/lib/system/web/src/lib.rs @@ -1,8 +1,7 @@ #![feature(trait_alias)] pub mod resize_observer; -mod animationframeloop; -pub use animationframeloop::AnimationFrameLoop; +pub mod animation_frame_loop; use basegl_prelude::*; @@ -113,24 +112,24 @@ impl Logger { #[cfg(target_arch = "wasm32")] impl Logger { - pub fn trace(&self, msg: M) { + pub fn trace(&self, _msg: M) { // console::debug_1(&self.format(msg)); } - pub fn info(&self, msg: M) { + pub fn info(&self, _msg: M) { // console::group_1(&self.format(msg)); // console::group_end(); } - pub fn warning(&self, msg: M) { + pub fn warning(&self, _msg: M) { // console::warn_1(&self.format(msg)); } - pub fn error(&self, msg: M) { + pub fn error(&self, _msg: M) { // console::error_1(&self.format(msg)); } - pub fn group_begin(&self, msg: M) { + pub fn group_begin(&self, _msg: M) { // console::group_1(&self.format(msg)); } @@ -232,7 +231,7 @@ pub fn get_webgl2_context context.dyn_into().map_err(|_| no_webgl()) } -pub fn request_animation_frame(f:&Closure) -> Result { +pub fn request_animation_frame(f:&Closure) -> Result { let req = window()?.request_animation_frame(f.as_ref().unchecked_ref()); req.map_err(|_| Error::missing("requestAnimationFrame")) } diff --git a/gui/lib/web-test/src/bencher.rs b/gui/lib/web-test/src/bencher.rs index 8d5720afb46..9a5c29fde46 100644 --- a/gui/lib/web-test/src/bencher.rs +++ b/gui/lib/web-test/src/bencher.rs @@ -2,7 +2,7 @@ use crate::prelude::*; use super::BenchContainer; pub use crate::system::web::get_performance; -pub use crate::system::web::AnimationFrameLoop; +pub use crate::system::web::animation_frame_loop::AnimationFrameLoop; use wasm_bindgen::prelude::Closure; use wasm_bindgen::JsCast; @@ -10,6 +10,7 @@ use std::rc::Rc; use std::cell::RefCell; + // =================== // === BencherCell === // =================== @@ -46,6 +47,7 @@ impl BencherCell { } + // =================== // === BencherData === // =================== @@ -66,7 +68,7 @@ impl BencherData { let data_clone = self.clone(); let performance = get_performance().expect("Performance object"); let mut t0 = performance.now(); - let anim_loop = AnimationFrameLoop::new(Box::new(move || { + let anim_loop = AnimationFrameLoop::new(Box::new(move |_| { let mut data = data_clone.borrow_mut(); (&mut data.func)(); @@ -91,6 +93,8 @@ impl BencherData { } } + + // =============== // === Bencher === // ===============