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::vector3::Vector3D;
use crate::transform3::Transform3;
use crate::surface3::*;
use crate::bounding_box3::BoundingBox3D;
use crate::ray3::Ray3D;
use std::sync::{RwLock, Arc};

///
/// # 3-D triangle geometry.
///
/// This class represents 3-D triangle geometry which extends Surface3 by
/// overriding surface-related queries.
///
pub struct Triangle3 {
    /// Three points.
    pub points: [Vector3D; 3],

    /// Three normals.
    pub normals: [Vector3D; 3],

    /// Three UV coordinates.
    pub uvs: [Vector2D; 3],

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

impl Triangle3 {
    /// Constructs an empty triangle.
    pub fn new_default(transform: Option<Transform3>,
                       is_normal_flipped: Option<bool>) -> Triangle3 {
        return Triangle3 {
            points: [Vector3D::new_default(); 3],
            normals: [Vector3D::new_default(); 3],
            uvs: [Vector2D::new_default(); 3],
            surface_data: Surface3Data::new(transform, is_normal_flipped),
        };
    }

    /// Constructs a triangle with given \p points, \p normals, and \p uvs.
    pub fn new(points: [Vector3D; 3],
               normals: [Vector3D; 3],
               uvs: [Vector2D; 3],
               transform: Option<Transform3>,
               is_normal_flipped: Option<bool>) -> Triangle3 {
        return Triangle3 {
            points,
            normals,
            uvs,
            surface_data: Surface3Data::new(transform, is_normal_flipped),
        };
    }

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

    /// Returns the area of this triangle.
    pub fn area(&self) -> f64 {
        return 0.5 * (self.points[1] - self.points[0]).cross(&(self.points[2] - self.points[0])).length();
    }

    /// Returns barycentric coordinates for the given point \p pt.
    pub fn get_barycentric_coords(&self, pt: &Vector3D,
                                  b0: &mut f64,
                                  b1: &mut f64,
                                  b2: &mut f64) {
        let q01 = (self.points[1] - self.points[0]).cross(&(*pt - self.points[0]));
        let q12 = (self.points[2] - self.points[1]).cross(&(*pt - self.points[1]));
        let q02 = (self.points[0] - self.points[2]).cross(&(*pt - self.points[2]));

        let a = self.area();
        *b0 = 0.5 * q12.length() / a;
        *b1 = 0.5 * q02.length() / a;
        *b2 = 0.5 * q01.length() / a;
    }

    /// Returns the face normal of the triangle.
    pub fn face_normal(&self) -> Vector3D {
        let ret = (self.points[1] - self.points[0]).cross(&(self.points[2] - self.points[0]));
        return ret.normalized();
    }

    /// Set Triangle3::normals to the face normal.
    pub fn set_normals_to_face_normal(&mut self) {
        self.normals[0] = self.face_normal();
        self.normals[1] = self.face_normal();
        self.normals[2] = self.face_normal();
    }
}

fn closest_point_on_line(v0: &Vector3D, v1: &Vector3D,
                         pt: &Vector3D) -> Vector3D {
    let len_squared = (*v1 - *v0).length_squared();
    if len_squared < f64::EPSILON {
        return *v0;
    }

    let t = (*pt - *v0).dot(&(*v1 - *v0)) / len_squared;
    if t < 0.0 {
        return *v0;
    } else if t > 1.0 {
        return *v1;
    }

    return *v0 + (*v1 - *v0) * t;
}

fn closest_normal_on_line(v0: &Vector3D, v1: &Vector3D,
                          n0: &Vector3D, n1: &Vector3D,
                          pt: &Vector3D) -> Vector3D {
    let len_squared = (*v1 - *v0).length_squared();
    if len_squared < f64::EPSILON {
        return *n0;
    }

    let t = (*pt - *v0).dot(&(*v1 - *v0)) / len_squared;
    if t < 0.0 {
        return *n0;
    } else if t > 1.0 {
        return *n1;
    }

    return (*n0 + (*n1 - *n0) * t).normalized();
}

impl Surface3 for Triangle3 {
    fn closest_point_local(&self, other_point: &Vector3D) -> Vector3D {
        let n = self.face_normal();
        let nd = n.dot(&n);
        let d = n.dot(&self.points[0]);
        let t = (d - n.dot(&other_point)) / nd;

        let q = n * t + *other_point;

        let q01 = (self.points[1] - self.points[0]).cross(&(q - self.points[0]));
        if n.dot(&q01) < 0.0 {
            return closest_point_on_line(&self.points[0], &self.points[1], &q);
        }

        let q12 = (self.points[2] - self.points[1]).cross(&(q - self.points[1]));
        if n.dot(&q12) < 0.0 {
            return closest_point_on_line(&self.points[1], &self.points[2], &q);
        }

        let q02 = (self.points[0] - self.points[2]).cross(&(q - self.points[2]));
        if n.dot(&q02) < 0.0 {
            return closest_point_on_line(&self.points[0], &self.points[2], &q);
        }

        let a = self.area();
        let b0 = 0.5 * q12.length() / a;
        let b1 = 0.5 * q02.length() / a;
        let b2 = 0.5 * q01.length() / a;

        return self.points[0] * b0 + self.points[1] * b1 + self.points[2] * b2;
    }

    fn bounding_box_local(&self) -> BoundingBox3D {
        let mut aabb = BoundingBox3D::new(self.points[0], self.points[1]);
        aabb.merge_vec(&self.points[2]);
        return aabb;
    }

    fn closest_intersection_local(&self, ray: &Ray3D) -> SurfaceRayIntersection3 {
        let mut intersection = SurfaceRayIntersection3::new();
        let n = self.face_normal();
        let nd = n.dot(&ray.direction);

        if nd < f64::EPSILON {
            intersection.is_intersecting = false;
            return intersection;
        }

        let d = n.dot(&self.points[0]);
        let t = (d - n.dot(&ray.origin)) / nd;

        if t < 0.0 {
            intersection.is_intersecting = false;
            return intersection;
        }

        let q = ray.point_at(t);

        let q01 = (self.points[1] - self.points[0]).cross(&(q - self.points[0]));
        if n.dot(&q01) <= 0.0 {
            intersection.is_intersecting = false;
            return intersection;
        }

        let q12 = (self.points[2] - self.points[1]).cross(&(q - self.points[1]));
        if n.dot(&q12) <= 0.0 {
            intersection.is_intersecting = false;
            return intersection;
        }

        let q02 = (self.points[0] - self.points[2]).cross(&(q - self.points[2]));
        if n.dot(&q02) <= 0.0 {
            intersection.is_intersecting = false;
            return intersection;
        }

        let a = self.area();
        let b0 = 0.5 * q12.length() / a;
        let b1 = 0.5 * q02.length() / a;
        let b2 = 0.5 * q01.length() / a;

        let normal = self.normals[0] * b0 + self.normals[1] * b1 + self.normals[2] * b2;

        intersection.is_intersecting = true;
        intersection.distance = t;
        intersection.point = q;
        intersection.normal = normal.normalized();

        return intersection;
    }

    fn closest_normal_local(&self, other_point: &Vector3D) -> Vector3D {
        let n = self.face_normal();
        let nd = n.dot(&n);
        let d = n.dot(&self.points[0]);
        let t = (d - n.dot(&other_point)) / nd;

        let q = n * t + *other_point;

        let q01 = (self.points[1] - self.points[0]).cross(&(q - self.points[0]));
        if n.dot(&q01) < 0.0 {
            return closest_normal_on_line(&self.points[0], &self.points[1], &self.normals[0], &self.normals[1],
                                          &q);
        }

        let q12 = (self.points[2] - self.points[1]).cross(&(q - self.points[1]));
        if n.dot(&q12) < 0.0 {
            return closest_normal_on_line(&self.points[1], &self.points[2], &self.normals[1], &self.normals[2],
                                          &q);
        }

        let q02 = (self.points[0] - self.points[2]).cross(&(q - self.points[2]));
        if n.dot(&q02) < 0.0 {
            return closest_normal_on_line(&self.points[0], &self.points[2], &self.normals[0], &self.normals[2],
                                          &q);
        }

        let a = self.area();
        let b0 = 0.5 * q12.length() / a;
        let b1 = 0.5 * q02.length() / a;
        let b2 = 0.5 * q01.length() / a;

        return (self.normals[0] * b0 + self.normals[1] * b1 + self.normals[2] * b2).normalized();
    }

    fn intersects_local(&self, ray: &Ray3D) -> bool {
        let n = self.face_normal();
        let nd = n.dot(&ray.direction);

        if nd < f64::EPSILON {
            return false;
        }

        let d = n.dot(&self.points[0]);
        let t = (d - n.dot(&ray.origin)) / nd;

        if t < 0.0 {
            return false;
        }

        let q = ray.point_at(t);

        let q01 = (self.points[1] - self.points[0]).cross(&(q - self.points[0]));
        if n.dot(&q01) <= 0.0 {
            return false;
        }

        let q12 = (self.points[2] - self.points[1]).cross(&(q - self.points[1]));
        if n.dot(&q12) <= 0.0 {
            return false;
        }

        let q02 = (self.points[0] - self.points[2]).cross(&(q - self.points[2]));
        if n.dot(&q02) <= 0.0 {
            return false;
        }

        return true;
    }

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

/// Shared pointer for the Triangle3 type.
pub type Triangle3Ptr = Arc<RwLock<Triangle3>>;

///
/// # Front-end to create Triangle3 objects step by step.
///
pub struct Builder {
    _points: [Vector3D; 3],
    _normals: [Vector3D; 3],
    _uvs: [Vector2D; 3],

    _surface_data: Surface3Data,
}

impl Builder {
    /// Returns builder with points.
    pub fn with_points(&mut self, points: [Vector3D; 3]) -> &mut Self {
        self._points = points;
        return self;
    }

    /// Returns builder with normals.
    pub fn with_normals(&mut self, normals: [Vector3D; 3]) -> &mut Self {
        self._normals = normals;
        return self;
    }

    /// Returns builder with uvs.
    pub fn with_uvs(&mut self, uvs: [Vector2D; 3]) -> &mut Self {
        self._uvs = uvs;
        return self;
    }

    /// Builds Triangle3.
    pub fn build(&mut self) -> Triangle3 {
        return Triangle3::new(
            self._points,
            self._normals,
            self._uvs,
            Some(self._surface_data.transform.clone()),
            Some(self._surface_data.is_normal_flipped),
        );
    }

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

    /// constructor
    pub fn new() -> Builder {
        return Builder {
            _points: [Vector3D::new_default(); 3],
            _normals: [Vector3D::new_default(); 3],
            _uvs: [Vector2D::new_default(); 3],
            _surface_data: Surface3Data::new(None, None),
        };
    }
}

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