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::vector2::Vector2D;
use crate::surface2::*;
use std::sync::{RwLock, Arc};

/// Internal query result structure.
pub struct ColliderQueryResult {
    distance: f64,
    point: Vector2D,
    normal: Vector2D,
    velocity: Vector2D,
}

impl ColliderQueryResult {
    pub fn new() -> ColliderQueryResult {
        return ColliderQueryResult {
            distance: 0.0,
            point: Vector2D::new_default(),
            normal: Vector2D::new_default(),
            velocity: Vector2D::new_default(),
        };
    }
}

pub struct Collider2Data {
    _surface: Option<Surface2Ptr>,
    _friction_coefficient: f64,
}

impl Collider2Data {
    pub fn new(surface: Option<Surface2Ptr>) -> Collider2Data {
        return Collider2Data {
            _surface: surface,
            _friction_coefficient: 0.0,
        };
    }
}

///
/// # Abstract base class for generic collider object.
///
/// This class contains basic interfaces for colliders. Most of the
/// functionalities are implemented within this class, except the member
/// function Collider2::velocity_at. This class also let the subclasses to
/// provide a Surface2 instance to define collider surface using
/// Collider2::set_surface function.
///
pub trait Collider2 {
    /// Returns the velocity of the collider at given \p point.
    fn velocity_at(&self, point: &Vector2D) -> Vector2D;

    /// Resolves collision for given point.
    /// - Parameters:
    ///   - radius: Radius of the colliding point.
    ///   - restitution_coefficient:  Defines the restitution effect.
    ///   - position: Input and output position of the point.
    ///   - velocity: Input and output velocity of the point.
    fn resolve_collision(&self, radius: f64,
                         restitution_coefficient: f64,
                         new_position: &mut Vector2D,
                         new_velocity: &mut Vector2D) {
        if !self.surface().read().unwrap().is_valid_geometry() {
            return;
        }

        let mut collider_point = ColliderQueryResult::new();

        self.get_closest_point(self.surface(), new_position, &mut collider_point);

        // Check if the new position is penetrating the surface
        if self.is_penetrating(&collider_point, new_position, radius) {
            // Target point is the closest non-penetrating position from the
            // new position.
            let target_normal = collider_point.normal;
            let target_point = collider_point.point + target_normal * radius;
            let collider_vel_at_target_point = collider_point.velocity;

            // Get new candidate relative velocity from the target point.
            let relative_vel = *new_velocity - collider_vel_at_target_point;
            let normal_dot_relative_vel = target_normal.dot(&relative_vel);
            let mut relative_vel_n = target_normal * normal_dot_relative_vel;
            let mut relative_vel_t = relative_vel - relative_vel_n;

            // Check if the velocity is facing opposite direction of the surface
            // normal
            if normal_dot_relative_vel < 0.0 {
                // Apply restitution coefficient to the surface normal component of
                // the velocity
                let delta_relative_vel_n = relative_vel_n * (-restitution_coefficient - 1.0);
                relative_vel_n *= -restitution_coefficient;

                // Apply friction to the tangential component of the velocity
                // From Bridson et al., Robust Treatment of Collisions, Contact and
                // Friction for Cloth Animation, 2002
                // http://graphics.stanford.edu/papers/cloth-sig02/cloth.pdf
                if relative_vel_t.length_squared() > 0.0 {
                    let friction_scale = f64::max(
                        1.0 - self.friction_coefficient() * delta_relative_vel_n.length() /
                            relative_vel_t.length(),
                        0.0);
                    relative_vel_t *= friction_scale;
                }

                // Reassemble the components
                *new_velocity = relative_vel_n + relative_vel_t + collider_vel_at_target_point;
            }

            // Geometric fix
            *new_position = target_point;
        }
    }

    //----------------------------------------------------------------------------------------------
    /// Returns friction coefficient.
    fn friction_coefficient(&self) -> f64 {
        return self.view()._friction_coefficient;
    }

    /// Sets the friction coefficient.
    ///
    /// This function assigns the friction coefficient to the collider. Any
    /// negative inputs will be clamped to zero.
    fn set_friction_coefficient(&mut self, new_friction_coefficient: f64) {
        self.view_mut()._friction_coefficient = new_friction_coefficient;
    }

    /// Returns the surface instance.
    fn surface(&self) -> Surface2Ptr {
        return self.view()._surface.as_ref().unwrap().clone();
    }

    /// Updates the collider state.
    fn update(&mut self, current_time_in_seconds: f64,
              time_interval_in_seconds: f64);

    /// Assigns the surface instance from the subclass.
    fn set_surface(&mut self, new_surface: Surface2Ptr) {
        self.view_mut()._surface = Some(new_surface);
    }

    /// Outputs closest point's information.
    fn get_closest_point(&self, surface: Surface2Ptr,
                         query_point: &Vector2D,
                         result: &mut ColliderQueryResult) {
        result.distance = surface.read().unwrap().closest_distance(query_point);
        result.point = surface.read().unwrap().closest_point(query_point);
        result.normal = surface.read().unwrap().closest_normal(query_point);
        result.velocity = self.velocity_at(query_point);
    }

    /// Returns true if given point is in the opposite side of the surface.
    fn is_penetrating(&self, collider_point: &ColliderQueryResult,
                      position: &Vector2D,
                      radius: f64) -> bool {
        // If the new candidate position of the particle is inside
        // the volume defined by the surface OR the new distance to the surface is
        // less than the particle's radius, this particle is in colliding state.
        return self.surface().read().unwrap().is_inside(position) || collider_point.distance < radius;
    }

    //----------------------------------------------------------------------------------------------
    fn view(&self) -> &Collider2Data;

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

/// Shared pointer type for the Collider2.
pub type Collider2Ptr = Arc<RwLock<dyn Collider2 + Send + Sync>>;