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 crate::matrix2x2::*;
use crate::vector2::Vector2D;
use crate::particle_emitter2::*;
use std::sync::{RwLock, Arc};
use rand::prelude::*;
use rand_pcg::{Pcg64, Lcg128Xsl64};

///
/// # Callback function type for update calls.
///
/// This type of callback function will take the emitter pointer, current
/// time, and time interval in seconds.
///
pub type OnBeginUpdateCallback = fn(&mut PointParticleEmitter2, f64, f64);

///
/// # 2-D point particle emitter.
///
/// This class emits particles from a single point in given direction, speed,
/// and spreading angle.
///
pub struct PointParticleEmitter2 {
    _rng: Lcg128Xsl64,
    _first_frame_time_in_seconds: f64,
    _number_of_emitted_particles: usize,

    _max_number_of_new_particles_per_second: usize,
    _max_number_of_particles: usize,

    _origin: Vector2D,
    _direction: Vector2D,
    _speed: f64,
    _spread_angle_in_radians: f64,

    _emitter_data: ParticleEmitter2Data,
    _on_begin_update_callback: Option<OnBeginUpdateCallback>,
}

impl PointParticleEmitter2 {
    ///
    /// Constructs an emitter that spawns particles from given origin,
    /// direction, speed, spread angle, max number of new particles per second,
    /// max total number of particles to be emitted, and random seed.
    ///
    /// - parameter:   origin                            The origin.
    /// - parameter:   direction                         The direction.
    /// - parameter:   speed                             The speed.
    /// - parameter:   spread_angle_in_degrees           The spread angle in degrees.
    /// - parameter:   max_num_of_new_particles_per_sec  The max number of new particles per second.
    /// - parameter:   max_num_of_particles              The max number of particles to be emitted.
    /// - parameter:   seed                              The random seed.
    ///
    pub fn new(origin: &Vector2D,
               direction: &Vector2D,
               speed: f64,
               spread_angle_in_degrees: f64,
               max_num_of_new_particles_per_sec: Option<usize>,
               max_num_of_particles: Option<usize>,
               seed: Option<u64>) -> PointParticleEmitter2 {
        return PointParticleEmitter2 {
            _rng: Pcg64::seed_from_u64(seed.unwrap_or(0)),
            _first_frame_time_in_seconds: 0.0,
            _number_of_emitted_particles: 0,
            _max_number_of_new_particles_per_second: max_num_of_new_particles_per_sec.unwrap_or(1),
            _max_number_of_particles: max_num_of_particles.unwrap_or(usize::MAX),
            _origin: *origin,
            _direction: *direction,
            _speed: speed,
            _spread_angle_in_radians: crate::math_utils::degrees_to_radians(spread_angle_in_degrees),
            _emitter_data: ParticleEmitter2Data::new(),
            _on_begin_update_callback: None,
        };
    }

    /// Returns builder fox PointParticleEmitter2.
    pub fn builder() -> Builder {
        return Builder::new();
    }

    /// Returns max number of new particles per second.
    pub fn max_number_of_new_particles_per_second(&self) -> usize {
        return self._max_number_of_new_particles_per_second;
    }

    /// Sets max number of new particles per second.
    pub fn set_max_number_of_new_particles_per_second(&mut self, rate: usize) {
        self._max_number_of_new_particles_per_second = rate;
    }

    /// Returns max number of particles to be emitted.
    pub fn max_number_of_particles(&self) -> usize {
        return self._max_number_of_particles;
    }

    /// Sets max number of particles to be emitted.
    pub fn set_max_number_of_particles(&mut self, max_number_of_particles: usize) {
        self._max_number_of_particles = max_number_of_particles;
    }

    fn emit(&mut self, new_positions: &mut Vec<Vector2D>,
            new_velocities: &mut Vec<Vector2D>,
            max_new_number_of_particles: usize) {
        for _ in 0..max_new_number_of_particles {
            let new_angle_in_radian = (self.random() - 0.5) * self._spread_angle_in_radians;
            let rotation_matrix = Matrix2x2D::make_rotation_matrix(new_angle_in_radian);

            new_positions.push(self._origin);
            new_velocities.push((rotation_matrix * self._direction) * self._speed);
        }
    }

    fn random(&mut self) -> f64 {
        return self._rng.gen_range(0.0..1.0);
    }

    ///
    /// \brief      Sets the callback function to be called when
    ///             ParticleEmitter2::update function is invoked.
    ///
    /// The callback function takes current simulation time in seconds unit. Use
    /// this callback to track any motion or state changes related to this
    /// emitter.
    ///
    /// - parameter:   callback The callback function.
    ///
    pub fn set_on_begin_update_callback(&mut self, callback: OnBeginUpdateCallback) {
        self._on_begin_update_callback = Some(callback);
    }
}

impl ParticleEmitter2 for PointParticleEmitter2 {
    fn update(&mut self, current_time_in_seconds: f64, time_interval_in_seconds: f64) where Self: Sized {
        match self._on_begin_update_callback {
            None => {}
            Some(callback) => {
                callback(self, current_time_in_seconds,
                         time_interval_in_seconds);
            }
        }

        self.on_update(current_time_in_seconds, time_interval_in_seconds);
    }

    fn on_update(&mut self, current_time_in_seconds: f64, time_interval_in_seconds: f64) {
        let particles;
        match self.target() {
            None => {
                return;
            }
            Some(target) => {
                particles = target.clone();
            }
        }

        if self._number_of_emitted_particles == 0 {
            self._first_frame_time_in_seconds = current_time_in_seconds;
        }

        let elapsed_time_in_seconds = current_time_in_seconds - self._first_frame_time_in_seconds;

        let mut new_max_total_number_of_emitted_particles = (
            f64::ceil((elapsed_time_in_seconds + time_interval_in_seconds)
                * self._max_number_of_new_particles_per_second as f64)) as usize;
        new_max_total_number_of_emitted_particles = usize::min(new_max_total_number_of_emitted_particles,
                                                               self._max_number_of_particles);
        let max_number_of_new_particles
            = new_max_total_number_of_emitted_particles - self._number_of_emitted_particles;

        if max_number_of_new_particles > 0 {
            let mut candidate_positions: Vec<Vector2D> = Vec::new();
            let mut candidate_velocities: Vec<Vector2D> = Vec::new();
            let mut new_positions: Vec<Vector2D> = Vec::new();
            let mut new_velocities: Vec<Vector2D> = Vec::new();

            self.emit(
                &mut candidate_positions,
                &mut candidate_velocities,
                max_number_of_new_particles);

            new_positions.append(&mut candidate_positions);
            new_velocities.append(&mut candidate_velocities);

            particles.write().unwrap().add_particles(&new_positions, Some(&new_velocities), None);

            self._number_of_emitted_particles += new_positions.len();
        }
    }

    fn view(&self) -> &ParticleEmitter2Data {
        return &self._emitter_data;
    }

    fn view_mut(&mut self) -> &mut ParticleEmitter2Data {
        return &mut self._emitter_data;
    }
}

/// Shared pointer for the PointParticleEmitter2 type.
pub type PointParticleEmitter2Ptr = Arc<RwLock<PointParticleEmitter2>>;

///
/// # Front-end to create PointParticleEmitter2 objects step by step.
///
pub struct Builder {
    _max_number_of_new_particles_per_second: usize,
    _max_number_of_particles: usize,
    _origin: Vector2D,
    _direction: Vector2D,
    _speed: f64,
    _spread_angle_in_degrees: f64,
    _seed: u64,
}

impl Builder {
    /// Returns builder with origin.
    pub fn with_origin(&mut self, origin: &Vector2D) -> &mut Self {
        self._origin = *origin;
        return self;
    }

    /// Returns builder with direction.
    pub fn with_direction(&mut self, direction: &Vector2D) -> &mut Self {
        self._direction = *direction;
        return self;
    }

    /// Returns builder with speed.
    pub fn with_speed(&mut self, speed: f64) -> &mut Self {
        self._speed = speed;
        return self;
    }

    /// Returns builder with spread angle in degrees.
    pub fn with_spread_angle_in_degrees(&mut self, spread_angle_in_degrees: f64) -> &mut Self {
        self._spread_angle_in_degrees = spread_angle_in_degrees;
        return self;
    }

    pub fn with_max_number_of_new_particles_per_second(&mut self,
                                                       max_num_of_new_particles_per_sec: usize) -> &mut Self {
        self._max_number_of_new_particles_per_second = max_num_of_new_particles_per_sec;
        return self;
    }

    /// Returns builder with max number of particles.
    pub fn with_max_number_of_particles(&mut self, max_number_of_particles: usize) -> &mut Self {
        self._max_number_of_particles = max_number_of_particles;
        return self;
    }

    /// Returns builder with random seed.
    pub fn with_random_seed(&mut self, seed: u64) -> &mut Self {
        self._seed = seed;
        return self;
    }

    /// Builds PointParticleEmitter2.
    pub fn build(&mut self) -> PointParticleEmitter2 {
        return PointParticleEmitter2::new(&self._origin,
                                          &self._direction,
                                          self._speed,
                                          self._spread_angle_in_degrees,
                                          Some(self._max_number_of_new_particles_per_second),
                                          Some(self._max_number_of_particles),
                                          Some(self._seed));
    }

    /// Builds shared pointer of PointParticleEmitter2 instance.
    pub fn make_shared(&mut self) -> PointParticleEmitter2Ptr {
        return PointParticleEmitter2Ptr::new(RwLock::new(self.build()));
    }

    /// constructor
    pub fn new() -> Builder {
        return Builder {
            _max_number_of_new_particles_per_second: 1,
            _max_number_of_particles: usize::MAX,
            _origin: Vector2D::new_default(),
            _direction: Vector2D::new(0.0, 1.0),
            _speed: 1.0,
            _spread_angle_in_degrees: 90.0,
            _seed: 0,
        };
    }
}