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::surface2::*;
use crate::vector2::Vector2D;
use crate::transform2::Transform2;
use crate::bounding_box2::BoundingBox2D;
use crate::ray2::Ray2D;
use crate::plane2::Plane2;
use std::sync::{RwLock, Arc};

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

    /// data from surface2
    pub surface_data: Surface2Data,
}

impl Box2 {
    /// Constructs (0, 0) x (1, 1) box.
    pub fn new_default(transform: Option<Transform2>,
                       is_normal_flipped: Option<bool>) -> Box2 {
        return Box2 {
            bound: BoundingBox2D::new(Vector2D::new_default(), Vector2D::new(1.0, 1.0)),
            surface_data: Surface2Data::new(transform, is_normal_flipped),
        };
    }

    /// Constructs a box with given \p lower_corner and \p upper_corner.
    pub fn new(lower_corner: Vector2D,
               upper_corner: Vector2D,
               transform: Option<Transform2>,
               is_normal_flipped: Option<bool>) -> Box2 {
        return Box2 {
            bound: BoundingBox2D::new(lower_corner, upper_corner),
            surface_data: Surface2Data::new(transform, is_normal_flipped),
        };
    }
    /// Constructs a box with BoundingBox2D instance.
    pub fn new_aabb(bounding_box: BoundingBox2D,
                    transform: Option<Transform2>,
                    is_normal_flipped: Option<bool>) -> Box2 {
        return Box2 {
            bound: bounding_box,
            surface_data: Surface2Data::new(transform, is_normal_flipped),
        };
    }

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

impl Surface2 for Box2 {
    fn closest_point_local(&self, other_point: &Vector2D) -> Vector2D {
        return if self.bound.contains(&other_point) {
            let planes = [Plane2::new(Vector2D::new(1.0, 0.0), self.bound.upper_corner, None, None),
                Plane2::new(Vector2D::new(0.0, 1.0), self.bound.upper_corner, None, None),
                Plane2::new(Vector2D::new(-1.0, 0.0), self.bound.lower_corner, None, None),
                Plane2::new(Vector2D::new(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..4 {
                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::vector2::clamp(&other_point, &self.bound.lower_corner, &self.bound.upper_corner)
        };
    }

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

    fn closest_intersection_local(&self, ray: &Ray2D) -> SurfaceRayIntersection2 {
        let mut intersection = SurfaceRayIntersection2::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: &Vector2D) -> Vector2D {
        let planes = [Plane2::new(Vector2D::new(1.0, 0.0), self.bound.upper_corner, None, None),
            Plane2::new(Vector2D::new(0.0, 1.0), self.bound.upper_corner, None, None),
            Plane2::new(Vector2D::new(-1.0, 0.0), self.bound.lower_corner, None, None),
            Plane2::new(Vector2D::new(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..4 {
                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::vector2::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..4 {
                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: &Ray2D) -> bool {
        return self.bound.intersects(ray_local);
    }

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

/// Shared pointer type for the Box2.
pub type Box2Ptr = Arc<RwLock<Box2>>;

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

    _surface_data: Surface2Data,
}

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

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

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

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

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

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

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