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::plane3::Plane3;
use std::sync::{RwLock, Arc};

///
/// # 3-D box geometry.
///
/// This class represents 3-D box geometry which extends Surface3 by overriding
/// surface-related queries. This box implementation is an axis-aligned box
/// that wraps lower-level primitive type, BoundingBox3D.
///
pub struct Box3 {
    /// Bounding box of this box.
    bound: BoundingBox3D,

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

impl Box3 {
    /// Constructs (0, 0) x (1, 1) box.
    pub fn new_default(transform: Option<Transform3>,
                       is_normal_flipped: Option<bool>) -> Box3 {
        return Box3 {
            bound: BoundingBox3D::new(Vector3D::new_default(), Vector3D::new(1.0, 1.0, 1.0)),
            surface_data: Surface3Data::new(transform, is_normal_flipped),
        };
    }

    /// Constructs a box with given \p lower_corner and \p upper_corner.
    pub fn new(lower_corner: Vector3D,
               upper_corner: Vector3D,
               transform: Option<Transform3>,
               is_normal_flipped: Option<bool>) -> Box3 {
        return Box3 {
            bound: BoundingBox3D::new(lower_corner, upper_corner),
            surface_data: Surface3Data::new(transform, is_normal_flipped),
        };
    }
    /// Constructs a box with BoundingBox3D instance.
    pub fn new_aabb(bounding_box: BoundingBox3D,
                    transform: Option<Transform3>,
                    is_normal_flipped: Option<bool>) -> Box3 {
        return Box3 {
            bound: bounding_box,
            surface_data: Surface3Data::new(transform, is_normal_flipped),
        };
    }

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

impl Surface3 for Box3 {
    fn closest_point_local(&self, other_point: &Vector3D) -> Vector3D {
        return if self.bound.contains(other_point) {
            let planes = [Plane3::new(Vector3D::new(1.0, 0.0, 0.0), self.bound.upper_corner, None, None),
                Plane3::new(Vector3D::new(0.0, 1.0, 0.0), self.bound.upper_corner, None, None),
                Plane3::new(Vector3D::new(0.0, 0.0, 1.0), self.bound.upper_corner, None, None),
                Plane3::new(Vector3D::new(-1.0, 0.0, 0.0), self.bound.lower_corner, None, None),
                Plane3::new(Vector3D::new(0.0, -1.0, 0.0), self.bound.lower_corner, None, None),
                Plane3::new(Vector3D::new(0.0, 0.0, -1.0), self.bound.lower_corner, None, None)];

            let mut result = planes[0].closest_point(other_point);
            let mut distance_squared = result.distance_squared_to(*other_point);

            for i in 1..6 {
                let local_result = planes[i].closest_point(other_point);
                let local_distance_squared =
                    local_result.distance_squared_to(*other_point);

                if local_distance_squared < distance_squared {
                    result = local_result;
                    distance_squared = local_distance_squared;
                }
            }

            result
        } else {
            crate::vector3::clamp(&other_point, &self.bound.lower_corner, &self.bound.upper_corner)
        };
    }

    fn bounding_box_local(&self) -> BoundingBox3D {
        return self.bound.clone();
    }

    fn closest_intersection_local(&self, ray: &Ray3D) -> SurfaceRayIntersection3 {
        let mut intersection = SurfaceRayIntersection3::new();
        let bb_ray_intersection = self.bound.closest_intersection(&ray);
        intersection.is_intersecting = bb_ray_intersection.is_intersecting;
        if intersection.is_intersecting {
            intersection.distance = bb_ray_intersection.t_near;
            intersection.point = ray.point_at(bb_ray_intersection.t_near);
            intersection.normal = self.closest_normal_local(&intersection.point);
        }

        return intersection;
    }

    fn closest_normal_local(&self, other_point: &Vector3D) -> Vector3D {
        let planes = [Plane3::new(Vector3D::new(1.0, 0.0, 0.0), self.bound.upper_corner, None, None),
            Plane3::new(Vector3D::new(0.0, 1.0, 0.0), self.bound.upper_corner, None, None),
            Plane3::new(Vector3D::new(0.0, 0.0, 1.0), self.bound.upper_corner, None, None),
            Plane3::new(Vector3D::new(-1.0, 0.0, 0.0), self.bound.lower_corner, None, None),
            Plane3::new(Vector3D::new(0.0, -1.0, 0.0), self.bound.lower_corner, None, None),
            Plane3::new(Vector3D::new(0.0, 0.0, -1.0), self.bound.lower_corner, None, None)];

        return if self.bound.contains(other_point) {
            let mut closest_normal = planes[0].normal;
            let closest_point = planes[0].closest_point(other_point);
            let mut min_distance_squared = (closest_point - *other_point).length_squared();

            for i in 1..6 {
                let local_closest_point = planes[i].closest_point(other_point);
                let local_distance_squared =
                    (local_closest_point - *other_point).length_squared();

                if local_distance_squared < min_distance_squared {
                    closest_normal = planes[i].normal;
                    min_distance_squared = local_distance_squared;
                }
            }

            closest_normal
        } else {
            let closest_point =
                crate::vector3::clamp(&other_point, &self.bound.lower_corner, &self.bound.upper_corner);
            let closest_point_to_input_point = *other_point - closest_point;
            let mut closest_normal = planes[0].normal;
            let mut max_cosine_angle = closest_normal.dot(&closest_point_to_input_point);

            for i in 1..6 {
                let cosine_angle = planes[i].normal.dot(&closest_point_to_input_point);

                if cosine_angle > max_cosine_angle {
                    closest_normal = planes[i].normal;
                    max_cosine_angle = cosine_angle;
                }
            }

            closest_normal
        };
    }

    fn intersects_local(&self, ray_local: &Ray3D) -> bool {
        return self.bound.intersects(ray_local);
    }

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

/// Shared pointer type for the Box3.
pub type Box3Ptr = Arc<RwLock<Box3>>;

///
/// # Front-end to create Box3 objects step by step.
///
pub struct Builder {
    _lower_corner: Vector3D,
    _upper_corner: Vector3D,

    _surface_data: Surface3Data,
}

impl Builder {
    /// Returns builder with lower corner set.
    pub fn with_lower_corner(&mut self, pt: Vector3D) -> &mut Self {
        self._lower_corner = pt;
        return self;
    }

    /// Returns builder with upper corner set.
    pub fn with_upper_corner(&mut self, pt: Vector3D) -> &mut Self {
        self._upper_corner = pt;
        return self;
    }

    /// Returns builder with bounding box.
    pub fn with_bounding_box(&mut self, bbox: BoundingBox3D) -> &mut Self {
        self._lower_corner = bbox.lower_corner;
        self._upper_corner = bbox.upper_corner;
        return self;
    }

    /// Builds Box3.
    pub fn build(&mut self) -> Box3 {
        return Box3::new_aabb(
            BoundingBox3D::new(self._lower_corner, self._upper_corner),
            Some(self._surface_data.transform.clone()),
            Some(self._surface_data.is_normal_flipped),
        );
    }

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

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

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