fj_core/geometry/
surface.rs

1//! The geometry that defines a surface
2
3use fj_math::{Line, Plane, Point, Transform, Vector};
4
5use super::GlobalPath;
6
7/// The geometry that defines a surface
8#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
9pub struct SurfaceGeometry {
10    /// The u-axis of the surface
11    pub u: GlobalPath,
12
13    /// The v-axis of the surface
14    pub v: Vector<3>,
15}
16
17impl SurfaceGeometry {
18    /// Convert a point in surface coordinates to model coordinates
19    pub fn point_from_surface_coords(
20        &self,
21        point: impl Into<Point<2>>,
22    ) -> Point<3> {
23        let point = point.into();
24        self.u.point_from_path_coords([point.u])
25            + self.path_to_line().vector_from_line_coords([point.v])
26    }
27
28    /// Convert a vector in surface coordinates to model coordinates
29    pub fn vector_from_surface_coords(
30        &self,
31        vector: impl Into<Vector<2>>,
32    ) -> Vector<3> {
33        let vector = vector.into();
34        self.u.vector_from_path_coords([vector.u])
35            + self.path_to_line().vector_from_line_coords([vector.v])
36    }
37
38    fn path_to_line(&self) -> Line<3> {
39        Line::from_origin_and_direction(self.u.origin(), self.v)
40    }
41
42    /// Project the global point into the surface
43    pub fn project_global_point(&self, point: impl Into<Point<3>>) -> Point<2> {
44        let GlobalPath::Line(line) = self.u else {
45            todo!("Projecting point into non-plane surface is not supported")
46        };
47
48        let plane =
49            Plane::from_parametric(line.origin(), line.direction(), self.v);
50        plane.project_point(point)
51    }
52
53    /// Transform the surface geometry
54    #[must_use]
55    pub fn transform(self, transform: &Transform) -> Self {
56        let u = self.u.transform(transform);
57        let v = transform.transform_vector(&self.v);
58        Self { u, v }
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use fj_math::{Line, Point, Vector};
65    use pretty_assertions::assert_eq;
66
67    use crate::geometry::{GlobalPath, SurfaceGeometry};
68
69    #[test]
70    fn point_from_surface_coords() {
71        let surface = SurfaceGeometry {
72            u: GlobalPath::Line(Line::from_origin_and_direction(
73                Point::from([1., 1., 1.]),
74                Vector::from([0., 2., 0.]),
75            )),
76            v: Vector::from([0., 0., 2.]),
77        };
78
79        assert_eq!(
80            surface.point_from_surface_coords([2., 4.]),
81            Point::from([1., 5., 9.]),
82        );
83    }
84
85    #[test]
86    fn vector_from_surface_coords() {
87        let surface = SurfaceGeometry {
88            u: GlobalPath::Line(Line::from_origin_and_direction(
89                Point::from([1., 0., 0.]),
90                Vector::from([0., 2., 0.]),
91            )),
92            v: Vector::from([0., 0., 2.]),
93        };
94
95        assert_eq!(
96            surface.vector_from_surface_coords([2., 4.]),
97            Vector::from([0., 4., 8.]),
98        );
99    }
100}