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

/// # Custom 2-D implicit surface using arbitrary function.
pub struct CustomImplicitSurface2 {
    _func: fn(&Vector2D) -> f64,
    _domain: BoundingBox2D,
    _resolution: f64,
    _ray_marching_resolution: f64,
    _max_num_of_iterations: usize,

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

impl CustomImplicitSurface2 {
    ///
    /// Constructs an implicit surface using the given signed-distance function.
    ///
    /// - parameter: func Custom SDF function object.
    /// - parameter: domain Bounding box of the SDF if exists.
    /// - parameter: resolution Finite differencing resolution for derivatives.
    /// - parameter: ray_marching_resolution Ray marching resolution for ray tests.
    /// - parameter: maxNumOfIterations Number of iterations for closest point search.
    /// - parameter: transform Local-to-world transform.
    /// - parameter: is_normal_flipped True if normal is flipped.
    ///
    pub fn new(func: fn(&Vector2D) -> f64,
               domain: Option<BoundingBox2D>,
               resolution: Option<f64>,
               ray_marching_resolution: Option<f64>,
               number_of_iterations: Option<usize>,
               transform: Option<Transform2>,
               is_normal_flipped: Option<bool>) -> CustomImplicitSurface2 {
        return CustomImplicitSurface2 {
            _func: func,
            _domain: domain.unwrap_or(BoundingBox2D::new_default()),
            _resolution: resolution.unwrap_or(1.0e-3),
            _ray_marching_resolution: ray_marching_resolution.unwrap_or(1.0e-6),
            _max_num_of_iterations: number_of_iterations.unwrap_or(5),
            surface_data: Surface2Data::new(transform, is_normal_flipped),
        };
    }

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

    pub fn gradient_local(&self, x: &Vector2D) -> Vector2D {
        let left = (self._func)(&(*x - Vector2D::new(0.5 * self._resolution, 0.0)));
        let right = (self._func)(&(*x + Vector2D::new(0.5 * self._resolution, 0.0)));
        let bottom = (self._func)(&(*x - Vector2D::new(0.0, 0.5 * self._resolution)));
        let top = (self._func)(&(*x + Vector2D::new(0.0, 0.5 * self._resolution)));

        return Vector2D::new((right - left) / self._resolution, (top - bottom) / self._resolution);
    }
}

impl Surface2 for CustomImplicitSurface2 {
    fn closest_point_local(&self, other_point: &Vector2D) -> Vector2D {
        let mut pt = crate::vector2::clamp(other_point, &self._domain.lower_corner, &self._domain.upper_corner);
        for _ in 0..self._max_num_of_iterations {
            let sdf = self.signed_distance_local(&pt);
            if f64::abs(sdf) < f64::EPSILON {
                break;
            }
            let g = self.gradient_local(&pt);
            pt = pt - g * sdf;
        }
        return pt;
    }
    fn bounding_box_local(&self) -> BoundingBox2D {
        return self._domain.clone();
    }

    fn closest_intersection_local(&self, ray: &Ray2D) -> SurfaceRayIntersection2 {
        let mut result = SurfaceRayIntersection2::new();

        let intersection = self._domain.closest_intersection(ray);

        if intersection.is_intersecting {
            let t_start;
            let t_end;
            if intersection.t_far == f64::MAX {
                t_start = 0.0;
                t_end = intersection.t_near;
            } else {
                t_start = intersection.t_near;
                t_end = intersection.t_far;
            }

            let mut t = t_start;
            let mut t_prev = t;
            let mut pt = ray.point_at(t);
            let mut prev_phi = (self._func)(&pt);

            while t <= t_end {
                pt = ray.point_at(t);
                let new_phi = (self._func)(&pt);
                let new_phi_abs = f64::abs(new_phi);

                if new_phi * prev_phi < 0.0 {
                    let frac = prev_phi / (prev_phi - new_phi);
                    let t_sub = t_prev + self._ray_marching_resolution * frac;

                    result.is_intersecting = true;
                    result.distance = t_sub;
                    result.point = ray.point_at(t_sub);
                    result.normal = self.gradient_local(&result.point);
                    if result.normal.length() > 0.0 {
                        result.normal.normalize();
                    }

                    return result;
                }

                t_prev = t;
                t += f64::max(new_phi_abs, self._ray_marching_resolution);
                prev_phi = new_phi;
            }
        }

        return result;
    }

    fn closest_normal_local(&self, other_point: &Vector2D) -> Vector2D {
        let pt = self.closest_point_local(other_point);
        let g = self.gradient_local(&pt);
        return if g.length_squared() > 0.0 {
            g.normalized()
        } else {
            g
        };
    }

    fn intersects_local(&self, ray: &Ray2D) -> bool {
        let intersection = self._domain.closest_intersection(ray);

        if intersection.is_intersecting {
            let t_start;
            let t_end;
            if intersection.t_far == f64::MAX {
                t_start = 0.0;
                t_end = intersection.t_near;
            } else {
                t_start = intersection.t_near;
                t_end = intersection.t_far;
            }

            let mut t = t_start;
            let mut pt = ray.point_at(t);
            let mut prev_phi = (self._func)(&pt);
            while t <= t_end {
                pt = ray.point_at(t);
                let new_phi = (self._func)(&pt);
                let new_phi_abs = f64::abs(new_phi);

                if new_phi * prev_phi < 0.0 {
                    return true;
                }

                t += f64::max(new_phi_abs, self._ray_marching_resolution);
                prev_phi = new_phi;
            }
        }

        return false;
    }

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

impl ImplicitSurface2 for CustomImplicitSurface2 {
    fn signed_distance_local(&self, other_point: &Vector2D) -> f64 {
        return (self._func)(other_point);
    }
}

/// Shared pointer type for the CustomImplicitSurface2.
pub type CustomImplicitSurface2Ptr = Arc<RwLock<CustomImplicitSurface2>>;

///
/// # Front-end to create CustomImplicitSurface2 objects step by step.
///
pub struct Builder {
    _func: Option<fn(&Vector2D) -> f64>,
    _domain: BoundingBox2D,
    _resolution: f64,
    _ray_marching_resolution: f64,
    _max_num_of_iterations: usize,

    _surface_data: Surface2Data,
}

impl Builder {
    /// Returns builder with custom signed-distance function
    pub fn with_signed_distance_function(&mut self, func: fn(&Vector2D) -> f64) -> &mut Self {
        self._func = Some(func);
        return self;
    }

    /// Returns builder with domain.
    pub fn with_domain(&mut self, domain: BoundingBox2D) -> &mut Self {
        self._domain = domain;
        return self;
    }

    /// Returns builder with finite differencing resolution.
    pub fn with_resolution(&mut self, resolution: f64) -> &mut Self {
        self._resolution = resolution;
        return self;
    }

    /// Returns builder with ray marching resolution which determines the ray
    /// intersection quality.
    pub fn with_ray_marching_resolution(&mut self, ray_marching_resolution: f64) -> &mut Self {
        self._ray_marching_resolution = ray_marching_resolution;
        return self;
    }

    /// Returns builder with number of iterations for closest point/normal
    /// searches.
    pub fn with_max_number_of_iterations(&mut self, num_iter: usize) -> &mut Self {
        self._max_num_of_iterations = num_iter;
        return self;
    }

    /// Builds CustomImplicitSurface2.
    pub fn build(&mut self) -> CustomImplicitSurface2 {
        return CustomImplicitSurface2::new(self._func.unwrap(),
                                           Some(self._domain.clone()),
                                           Some(self._resolution),
                                           Some(self._ray_marching_resolution),
                                           Some(self._max_num_of_iterations),
                                           Some(self._surface_data.transform.clone()),
                                           Some(self._surface_data.is_normal_flipped));
    }

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

    /// constructor
    pub fn new() -> Builder {
        return Builder {
            _func: None,
            _domain: BoundingBox2D::new_default(),
            _resolution: 1.0e-3,
            _ray_marching_resolution: 1.0e-6,
            _max_num_of_iterations: 5,
            _surface_data: Surface2Data::new(None, None),
        };
    }
}

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