1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
/*
 * // 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::vector3::Vector3D;
use crate::bounding_box3::BoundingBox3D;
use crate::ray3::Ray3D;
use crate::transform3::Transform3;
use crate::quaternion::QuaternionD;
use std::sync::{RwLock, Arc};

/// Struct that represents ray-surface intersection point.
pub struct SurfaceRayIntersection3 {
    pub is_intersecting: bool,
    pub distance: f64,
    pub point: Vector3D,
    pub normal: Vector3D,
}

impl SurfaceRayIntersection3 {
    pub fn new() -> SurfaceRayIntersection3 {
        return SurfaceRayIntersection3 {
            is_intersecting: false,
            distance: f64::MAX,
            point: Vector3D::new_default(),
            normal: Vector3D::new_default(),
        };
    }
}

pub struct Surface3Data {
    /// Local-to-world transform.
    pub transform: Transform3,

    /// Flips normal.
    pub is_normal_flipped: bool,
}

impl Surface3Data {
    pub fn new(transform: Option<Transform3>,
               is_normal_flipped: Option<bool>) -> Surface3Data {
        return Surface3Data {
            transform: transform.unwrap_or(Transform3::new_default()),
            is_normal_flipped: is_normal_flipped.unwrap_or(false),
        };
    }
}

/// Abstract base class for 3-D surface.
pub trait Surface3 {
    /// Returns the closest point from the given point \p other_point to the
    /// surface in local frame.
    fn closest_point_local(&self, other_point: &Vector3D) -> Vector3D;

    /// Returns the bounding box of this surface object in local frame.
    fn bounding_box_local(&self) -> BoundingBox3D;

    /// Returns the closest intersection point for given \p ray in local frame.
    fn closest_intersection_local(&self, ray: &Ray3D) -> SurfaceRayIntersection3;

    /// Returns the normal to the closest point on the surface from the given
    /// point \p other_point in local frame.
    fn closest_normal_local(&self, other_point: &Vector3D) -> Vector3D;

    /// Returns true if the given \p ray intersects with this surface object
    /// in local frame.
    fn intersects_local(&self, ray_local: &Ray3D) -> bool {
        let result = self.closest_intersection_local(ray_local);
        return result.is_intersecting;
    }

    /// Returns the closest distance from the given point \p otherPoint to the
    /// point on the surface in local frame.
    fn closest_distance_local(&self, other_point_local: &Vector3D) -> f64 {
        return other_point_local.distance_to(self.closest_point_local(other_point_local));
    }

    /// Returns true if \p otherPoint is inside by given \p depth the volume
    /// defined by the surface in local frame.
    fn is_inside_local(&self, other_point_local: &Vector3D) -> bool {
        let cp_local = self.closest_point_local(other_point_local);
        let normal_local = self.closest_normal_local(other_point_local);
        return (*other_point_local - cp_local).dot(&normal_local) < 0.0;
    }

    //----------------------------------------------------------------------------------------------
    /// Returns the closest point from the given point \p other_point to the surface.
    fn closest_point(&self, other_point: &Vector3D) -> Vector3D {
        return self.view().transform.to_world_vec(&self.closest_point_local(
            &self.view().transform.to_local_vec(&other_point)));
    }

    /// Returns the bounding box of this surface object.
    fn bounding_box(&self) -> BoundingBox3D {
        return self.view().transform.to_world_aabb(&self.bounding_box_local());
    }

    /// Returns true if the given \p ray intersects with this surface object.
    fn intersects(&self, ray: &Ray3D) -> bool {
        return self.intersects_local(&self.view().transform.to_local_ray(&ray));
    }

    /// Returns the closest distance from the given point \p other_point to the
    /// point on the surface.
    fn closest_distance(&self, other_point: &Vector3D) -> f64 {
        return self.closest_distance_local(&self.view().transform.to_local_vec(&other_point));
    }

    /// Returns the closest intersection point for given \p ray.
    fn closest_intersection(&self, ray: &Ray3D) -> SurfaceRayIntersection3 {
        let mut result = self.closest_intersection_local(
            &self.view().transform.to_local_ray(&ray));
        result.point = self.view().transform.to_world_vec(&result.point);
        result.normal = self.view().transform.to_world_direction(&result.normal);
        match self.view().is_normal_flipped {
            true => result.normal *= -1.0,
            _ => {}
        }
        return result;
    }

    /// Returns the normal to the closest point on the surface from the given
    /// point \p other_point.
    fn closest_normal(&self, other_point: &Vector3D) -> Vector3D {
        let mut result = self.view().transform.to_world_direction(
            &self.closest_normal_local(
                &self.view().transform.to_local_vec(&other_point)));
        match self.view().is_normal_flipped {
            true => result *= -1.0,
            _ => {}
        }
        return result;
    }

    /// Updates internal spatial query engine.
    fn update_query_engine(&self) {}

    /// Returns true if bounding box can be defined.
    fn is_bounded(&self) -> bool {
        return true;
    }

    /// Returns true if the surface is a valid geometry.
    fn is_valid_geometry(&self) -> bool {
        return true;
    }

    /// Returns true if \p other_point is inside the volume defined by the surface.
    fn is_inside(&self, other_point: &Vector3D) -> bool {
        return self.view().is_normal_flipped == !self.is_inside_local(
            &self.view().transform.to_local_vec(&other_point));
    }

    //----------------------------------------------------------------------------------------------
    fn view(&self) -> &Surface3Data;
}

/// Shared pointer for the Surface3 type.
pub type Surface3Ptr = Arc<RwLock<dyn Surface3>>;

///
/// # Base class for 3-D surface builder.
///
pub trait SurfaceBuilderBase3 {
    /// Returns builder with flipped normal flag.
    fn with_is_normal_flipped(&mut self, is_normal_flipped: bool) -> &mut Self {
        self.view().is_normal_flipped = is_normal_flipped;
        return self;
    }

    /// Returns builder with translation.
    fn with_translation(&mut self, translation: Vector3D) -> &mut Self {
        self.view().transform.set_translation(translation);
        return self;
    }

    /// Returns builder with orientation.
    fn with_orientation(&mut self, orientation: QuaternionD) -> &mut Self {
        self.view().transform.set_orientation(orientation);
        return self;
    }

    /// Returns builder with transform.
    fn with_transform(&mut self, transform: Transform3) -> &mut Self {
        self.view().transform = transform;
        return self;
    }

    //----------------------------------------------------------------------------------------------
    fn view(&mut self) -> &mut Surface3Data;
}