vox_geometry_rust 0.1.2

Geometry Tools for Rust
Documentation
/*
 * // Copyright (c) 2021 Feng Yang
 * //
 * // I am making my contributions/submissions to this project solely in my
 * // personal capacity and am not conveying any rights to any intellectual
 * // property of any third parties.
 */

use std::sync::{RwLock, Arc};
use log::info;
use crate::animation::*;
use std::time::SystemTime;

pub struct PhysicsAnimationData {
    _current_frame: Frame,
    _is_using_fixed_sub_time_steps: bool,
    _number_of_fixed_sub_time_steps: usize,
    _current_time: f64,
}

impl PhysicsAnimationData {
    pub fn new() -> PhysicsAnimationData {
        return PhysicsAnimationData {
            _current_frame: Frame::new(-1, 1.0 / 60.0),
            _is_using_fixed_sub_time_steps: true,
            _number_of_fixed_sub_time_steps: 1,
            _current_time: 0.0,
        };
    }
}

///
/// # Abstract base class for physics-based animation.
///
/// This class represents physics-based animation by adding time-integration
/// specific functions to Animation class.
///
pub trait PhysicsAnimation: Animation {
    ///
    /// ## Returns true if fixed sub-time stepping is used.
    ///
    /// When performing a time-integration, it is often required to take
    /// sub-time stepping for better results. The sub-stepping can be either
    /// fixed rate or adaptive, and this function returns which feature is
    /// currently selected.
    ///
    /// \return     True if using fixed sub time steps, false otherwise.
    ///
    fn is_using_fixed_sub_time_steps(&self) -> bool {
        return self.view()._is_using_fixed_sub_time_steps;
    }

    ///
    /// ## Sets true if fixed sub-time stepping is used.
    ///
    /// When performing a time-integration, it is often required to take
    /// sub-time stepping for better results. The sub-stepping can be either
    /// fixed rate or adaptive, and this function sets which feature should be
    /// selected.
    ///
    /// - parameter:   isUsing True to enable fixed sub-stepping.
    ///
    fn set_is_using_fixed_sub_time_steps(&mut self, is_using: bool) {
        self.view_mut()._is_using_fixed_sub_time_steps = is_using;
    }

    ///
    /// ## Returns the number of fixed sub-time steps.
    ///
    /// When performing a time-integration, it is often required to take
    /// sub-time stepping for better results. The sub-stepping can be either
    /// fixed rate or adaptive, and this function returns the number of fixed
    /// sub-steps.
    ///
    /// \return     The number of fixed sub-time steps.
    ///
    fn number_of_fixed_sub_time_steps(&self) -> usize {
        return self.view()._number_of_fixed_sub_time_steps;
    }

    ///
    /// ## Sets the number of fixed sub-time steps.
    ///
    /// When performing a time-integration, it is often required to take
    /// sub-time stepping for better results. The sub-stepping can be either
    /// fixed rate or adaptive, and this function sets the number of fixed
    /// sub-steps.
    ///
    /// - parameter:  numberOfSteps The number of fixed sub-time steps.
    ///
    fn set_number_of_fixed_sub_time_steps(&mut self, number_of_steps: usize) {
        self.view_mut()._number_of_fixed_sub_time_steps = number_of_steps;
    }

    /// Advances a single frame.
    fn advance_single_frame(&mut self) {
        let mut f = self.view_mut()._current_frame.clone();
        f.advance_single();
        self.update(&f);
    }

    ///
    /// ## Returns current frame.
    ///
    fn current_frame(&self) -> Frame {
        return self.view()._current_frame.clone();
    }

    ///
    /// ## Sets current frame cursor (but do not invoke update()).
    ///
    fn set_current_frame(&mut self, frame: &Frame) {
        self.view_mut()._current_frame = frame.clone();
    }

    ///
    /// ## Returns current time in seconds.
    ///
    /// This function returns the current time which is calculated by adding
    /// current frame + sub-time steps it passed.
    ///
    fn current_time_in_seconds(&self) -> f64 {
        return self.view()._current_time;
    }

    ///
    /// ## Called when a single time-step should be advanced.
    ///
    /// When Animation::update function is called, this class will internally
    /// subdivide a frame into sub-steps if needed. Each sub-step, or time-step,
    /// is then taken to move forward in time. This function is called for each
    /// time-step, and a subclass that inherits PhysicsAnimation class should
    /// implement this function for its own physics model.
    ///
    /// - parameter:  timeIntervalInSeconds The time interval in seconds
    ///
    fn on_advance_time_step(&mut self, time_interval_in_seconds: f64);

    ///
    /// ## Returns the required number of sub-time steps for given time interval.
    ///
    /// The required number of sub-time step can be different depending on the
    /// physics model behind the implementation. Override this function to
    /// implement own logic for model specific sub-time stepping for given
    /// time interval.
    ///
    /// - parameter:  timeIntervalInSeconds The time interval in seconds.
    ///
    /// \return     The required number of sub-time steps.
    ///
    fn number_of_sub_time_steps(&self, _: f64) -> usize {
        // Returns number of fixed sub-time steps by default
        return self.view()._number_of_fixed_sub_time_steps;
    }

    ///
    /// ## Called at frame 0 to initialize the physics state.
    ///
    /// Inheriting classes can override this function to setup initial condition
    /// for the simulation.
    ///
    fn on_initialize(&mut self) {}

    fn on_update(&mut self, frame: &Frame) {
        if frame.index > self.view()._current_frame.index {
            if self.view()._current_frame.index < 0 {
                self.initialize();
            }

            let number_of_frames = frame.index - self.view()._current_frame.index;

            for _ in 0..number_of_frames {
                self.advance_time_step(frame.time_interval_in_seconds);
            }
            self.view_mut()._current_frame = frame.clone();
        }
    }

    fn advance_time_step(&mut self, time_interval_in_seconds: f64) {
        self.view_mut()._current_time = self.view()._current_frame.time_in_seconds();

        if self.is_using_fixed_sub_time_steps() {
            info!("Using fixed sub-timesteps: {}", self.view()._number_of_fixed_sub_time_steps);

            // Perform fixed time-stepping
            let actual_time_interval = time_interval_in_seconds / self.view()._number_of_fixed_sub_time_steps as f64;

            for _ in 0..self.view()._number_of_fixed_sub_time_steps {
                info!("Begin onAdvanceTimeStep: {} (1/{}) seconds", actual_time_interval, 1.0 / actual_time_interval);

                let timer = SystemTime::now();
                self.on_advance_time_step(actual_time_interval);

                info!("End onAdvanceTimeStep (took {} seconds)", timer.elapsed().unwrap().as_secs_f64());

                self.view_mut()._current_time += actual_time_interval;
            }
        } else {
            info!("Using adaptive sub-timesteps");

            // Perform adaptive time-stepping
            let mut remaining_time = time_interval_in_seconds;
            while remaining_time > f64::EPSILON {
                let num_steps = self.number_of_sub_time_steps(remaining_time);
                let actual_time_interval = remaining_time / num_steps as f64;

                info!("Number of remaining sub-timesteps: {}", num_steps);

                info!("Begin onAdvanceTimeStep: {} (1/{}) seconds", actual_time_interval, 1.0 / actual_time_interval);

                let timer = SystemTime::now();
                self.on_advance_time_step(actual_time_interval);

                info!("End onAdvanceTimeStep (took {} seconds)", timer.elapsed().unwrap().as_secs_f64());

                remaining_time -= actual_time_interval;
                self.view_mut()._current_time += actual_time_interval;
            }
        }
    }

    fn initialize(&mut self) {
        self.on_initialize();
    }

    fn view(&self) -> &PhysicsAnimationData;

    fn view_mut(&mut self) -> &mut PhysicsAnimationData;
}

pub type PhysicsAnimationPtr = Arc<RwLock<dyn PhysicsAnimation>>;