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::surface3::*;
use crate::vector3::Vector3D;
use crate::transform3::Transform3;
use crate::bounding_box3::BoundingBox3D;
use crate::ray3::Ray3D;
use crate::vector2::Vector2D;
use crate::box2::Box2;
use crate::surface2::Surface2;
use crate::plane3::Plane3;
use std::sync::{RwLock, Arc};

///
/// # 3-D cylinder geometry.
///
/// This class represents 3-D cylinder geometry which extends Surface3 by
/// overriding surface-related queries. The cylinder is aligned with the y-axis.
///
pub struct Cylinder3 {
    /// Center of the cylinder.
    center: Vector3D,

    /// Radius of the cylinder.
    radius: f64,

    /// Height of the cylinder.
    height: f64,

    /// data from surface3
    pub surface_data: Surface3Data,
}

impl Cylinder3 {
    /// Constructs a cylinder with
    pub fn new_default(transform: Option<Transform3>,
                       is_normal_flipped: Option<bool>) -> Cylinder3 {
        return Cylinder3 {
            center: Vector3D::new_default(),
            radius: 1.0,
            height: 1.0,
            surface_data: Surface3Data::new(transform, is_normal_flipped),
        };
    }

    /// Constructs a cylinder with \p center, \p radius, and \p height.
    pub fn new(center: Vector3D,
               radius: f64,
               height: f64,
               transform: Option<Transform3>,
               is_normal_flipped: Option<bool>) -> Cylinder3 {
        return Cylinder3 {
            center,
            radius,
            height,
            surface_data: Surface3Data::new(transform, is_normal_flipped),
        };
    }

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

impl Surface3 for Cylinder3 {
    fn closest_point_local(&self, other_point: &Vector3D) -> Vector3D {
        let r = *other_point - self.center;
        let rr = Vector2D::new(f64::sqrt(r.x * r.x + r.z * r.z), r.y);
        let aabb = Box2::new(Vector2D::new(-self.radius, -0.5 * self.height),
                             Vector2D::new(self.radius, 0.5 * self.height), None, None);

        let cp = aabb.closest_point(&rr);
        let angle = f64::atan2(r.z, r.x);
        return Vector3D::new(cp.x * f64::cos(angle), cp.y, cp.x * f64::sin(angle)) + self.center;
    }

    fn bounding_box_local(&self) -> BoundingBox3D {
        return BoundingBox3D::new(self.center - Vector3D::new(self.radius, 0.5 * self.height, self.radius),
                                  self.center + Vector3D::new(self.radius, 0.5 * self.height, self.radius));
    }

    fn closest_intersection_local(&self, ray: &Ray3D) -> SurfaceRayIntersection3 {
        let mut intersection = SurfaceRayIntersection3::new();

        // Calculate intersection with infinite cylinder
        // (dx^2 + dz^2)t^2 + 2(ox.dx + oz.dz)t + ox^2 + oz^2 - r^2 = 0
        let mut d = ray.direction;
        d.y = 0.0;
        let mut o = ray.origin - self.center;
        o.y = 0.0;
        let aaa = d.length_squared();
        let bbb = d.dot(&o);
        let ccc = o.length_squared() - crate::math_utils::square(self.radius);

        let bbox = self.bounding_box_local();
        let upper_plane = Plane3::new(Vector3D::new(0.0, 1.0, 0.0),
                                      bbox.upper_corner, None, None);
        let lower_plane = Plane3::new(Vector3D::new(0.0, -1.0, 0.0),
                                      bbox.lower_corner, None, None);

        let mut upper_intersection = upper_plane.closest_intersection(&ray);

        let mut lower_intersection = lower_plane.closest_intersection(&ray);

        intersection.distance = f64::MAX;
        intersection.is_intersecting = false;

        // In case the ray does not intersect with infinite cylinder
        if aaa < f64::EPSILON || bbb * bbb - aaa * ccc < 0.0 {
            // Check if the ray is inside the infinite cylinder
            let r = ray.origin - self.center;
            let rr = Vector2D::new(r.x, r.z);
            if rr.length_squared() <= crate::math_utils::square(self.radius) {
                if upper_intersection.is_intersecting {
                    intersection = upper_intersection;
                }
                if lower_intersection.is_intersecting &&
                    lower_intersection.distance < intersection.distance {
                    intersection = lower_intersection;
                }
            }

            return intersection;
        }

        let t1 = (-bbb + f64::sqrt(bbb * bbb - aaa * ccc)) / aaa;
        let t2 = (-bbb - f64::sqrt(bbb * bbb - aaa * ccc)) / aaa;
        let mut t_cylinder = t2;

        if t2 < 0.0 {
            t_cylinder = t1;
        }

        let point_on_cylinder = ray.point_at(t_cylinder);

        if point_on_cylinder.y >= self.center.y - 0.5 * self.height &&
            point_on_cylinder.y <= self.center.y + 0.5 * self.height {
            intersection.is_intersecting = true;
            intersection.distance = t_cylinder;
            intersection.point = point_on_cylinder;
            intersection.normal = point_on_cylinder - self.center;
            intersection.normal.y = 0.0;
            intersection.normal.normalize();
        }

        if upper_intersection.is_intersecting {
            let mut r = upper_intersection.point - self.center;
            r.y = 0.0;
            if r.length_squared() > crate::math_utils::square(self.radius) {
                upper_intersection.is_intersecting = false;
            } else if upper_intersection.distance < intersection.distance {
                intersection = upper_intersection;
            }
        }

        if lower_intersection.is_intersecting {
            let mut r = lower_intersection.point - self.center;
            r.y = 0.0;
            if r.length_squared() > crate::math_utils::square(self.radius) {
                lower_intersection.is_intersecting = false;
            } else if lower_intersection.distance < intersection.distance {
                intersection = lower_intersection;
            }
        }

        return intersection;
    }

    fn closest_normal_local(&self, other_point: &Vector3D) -> Vector3D {
        let r = *other_point - self.center;
        let rr = Vector2D::new(f64::sqrt(r.x * r.x + r.z * r.z), r.y);
        let aabb = Box2::new(Vector2D::new(-self.radius, -0.5 * self.height),
                             Vector2D::new(self.radius, 0.5 * self.height), None, None);

        let cn = aabb.closest_normal(&rr);
        return if cn.y > 0.0 {
            Vector3D::new(0.0, 1.0, 0.0)
        } else if cn.y < 0.0 {
            Vector3D::new(0.0, -1.0, 0.0)
        } else {
            Vector3D::new(r.x, 0.0, r.z).normalized()
        };
    }

    fn intersects_local(&self, ray: &Ray3D) -> bool {
        // Calculate intersection with infinite cylinder
        // (dx^2 + dz^2)t^2 + 2(ox.dx + oz.dz)t + ox^2 + oz^2 - r^2 = 0
        let mut d = ray.direction;
        d.y = 0.0;
        let mut o = ray.origin - self.center;
        o.y = 0.0;
        let aaa = d.length_squared();
        let bbb = d.dot(&o);
        let ccc = o.length_squared() - crate::math_utils::square(self.radius);

        let bbox = self.bounding_box();
        let upper_plane = Plane3::new(Vector3D::new(0.0, 1.0, 0.0),
                                      bbox.upper_corner, None, None);
        let lower_plane = Plane3::new(Vector3D::new(0.0, -1.0, 0.0),
                                      bbox.lower_corner, None, None);

        let upper_intersection = upper_plane.closest_intersection(&ray);

        let lower_intersection = lower_plane.closest_intersection(&ray);

        // In case the ray does not intersect with infinite cylinder
        if aaa < f64::EPSILON || bbb * bbb - aaa * ccc < 0.0 {
            // Check if the ray is inside the infinite cylinder
            let r = ray.origin - self.center;
            let rr = Vector2D::new(r.x, r.z);
            if rr.length_squared() <= crate::math_utils::square(self.radius) {
                if upper_intersection.is_intersecting ||
                    lower_intersection.is_intersecting {
                    return true;
                }
            }

            return false;
        }

        let t1 = (-bbb + f64::sqrt(bbb * bbb - aaa * ccc)) / aaa;
        let t2 = (-bbb - f64::sqrt(bbb * bbb - aaa * ccc)) / aaa;
        let mut t_cylinder = t2;

        if t2 < 0.0 {
            t_cylinder = t1;
        }

        let point_on_cylinder = ray.point_at(t_cylinder);

        if point_on_cylinder.y >= self.center.y - 0.5 * self.height &&
            point_on_cylinder.y <= self.center.y + 0.5 * self.height {
            return true;
        }

        if upper_intersection.is_intersecting {
            let mut r = upper_intersection.point - self.center;
            r.y = 0.0;
            if r.length_squared() <= crate::math_utils::square(self.radius) {
                return true;
            }
        }

        if lower_intersection.is_intersecting {
            let mut r = lower_intersection.point - self.center;
            r.y = 0.0;
            if r.length_squared() <= crate::math_utils::square(self.radius) {
                return true;
            }
        }

        return false;
    }

    fn closest_distance_local(&self, other_point_local: &Vector3D) -> f64 {
        let r = *other_point_local - self.center;
        let rr = Vector2D::new(f64::sqrt(r.x * r.x + r.z * r.z), r.y);
        let aabb = Box2::new(Vector2D::new(-self.radius, -0.5 * self.height),
                             Vector2D::new(self.radius, 0.5 * self.height), None, None);

        return aabb.closest_distance(&rr);
    }

    fn view(&self) -> &Surface3Data {
        return &self.surface_data;
    }
}

/// Shared pointer type for the Cylinder3.
pub type Cylinder3Ptr = Arc<RwLock<Cylinder3>>;

///
/// # Front-end to create Cylinder3 objects step by step.
///
pub struct Builder {
    _center: Vector3D,
    _radius: f64,
    _height: f64,

    _surface_data: Surface3Data,
}

impl Builder {
    /// Returns builder with center.
    pub fn with_center(&mut self, center: Vector3D) -> &mut Self {
        self._center = center;
        return self;
    }

    /// Returns builder with radius.
    pub fn with_radius(&mut self, radius: f64) -> &mut Self {
        self._radius = radius;
        return self;
    }

    /// Returns builder with height.
    pub fn with_height(&mut self, height: f64) -> &mut Self {
        self._height = height;
        return self;
    }

    /// Builds Cylinder3.
    pub fn build(&mut self) -> Cylinder3 {
        return Cylinder3::new(
            self._center,
            self._radius,
            self._height,
            Some(self._surface_data.transform.clone()),
            Some(self._surface_data.is_normal_flipped),
        );
    }

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

    /// constructor
    pub fn new() -> Builder {
        return Builder {
            _center: Vector3D::new_default(),
            _radius: 1.0,
            _height: 1.0,
            _surface_data: Surface3Data::new(None, None),
        };
    }
}

impl SurfaceBuilderBase3 for Builder {
    fn view(&mut self) -> &mut Surface3Data {
        return &mut self._surface_data;
    }
}