Another project
1use bone_types::{
2 Aabb3, AngleTolerance, ChordHeightTolerance, Length, Parameter, Plane3, Point3, Tolerance,
3 UnitVec3, Vec3,
4};
5use uom::si::length::millimeter;
6
7use crate::KernelError;
8use crate::circular3;
9use crate::mesh::{MeshVertex, TriMesh};
10use crate::surface3::Surface3;
11
12#[derive(Copy, Clone, Debug, PartialEq)]
13pub struct PlaneSurface {
14 plane: Plane3,
15 u_extent: Length,
16 v_extent: Length,
17}
18
19impl PlaneSurface {
20 pub fn new(
21 plane: Plane3,
22 u_extent: Length,
23 v_extent: Length,
24 tolerance: Tolerance,
25 ) -> Result<Self, KernelError> {
26 let u = u_extent.get::<millimeter>();
27 let v = v_extent.get::<millimeter>();
28 if !u.is_finite() || !v.is_finite() || u < tolerance.value() || v < tolerance.value() {
29 return Err(KernelError::DegeneratePlane);
30 }
31 Ok(Self {
32 plane,
33 u_extent,
34 v_extent,
35 })
36 }
37
38 #[must_use]
39 pub const fn plane(self) -> Plane3 {
40 self.plane
41 }
42
43 #[must_use]
44 pub const fn u_extent(self) -> Length {
45 self.u_extent
46 }
47
48 #[must_use]
49 pub const fn v_extent(self) -> Length {
50 self.v_extent
51 }
52
53 #[must_use]
54 fn point_at(self, u: f64, v: f64) -> Point3 {
55 let (ox, oy, oz) = self.plane.origin().coords_mm();
56 let (xx, xy, xz) = self.plane.x_axis().components();
57 let (yx, yy, yz) = self.plane.y_axis().components();
58 let su = u * self.u_extent.get::<millimeter>();
59 let sv = v * self.v_extent.get::<millimeter>();
60 Point3::from_mm(
61 ox + su * xx + sv * yx,
62 oy + su * xy + sv * yy,
63 oz + su * xz + sv * yz,
64 )
65 }
66}
67
68impl Surface3 for PlaneSurface {
69 fn evaluate(&self, u: Parameter, v: Parameter) -> Point3 {
70 self.point_at(u.value(), v.value())
71 }
72
73 fn partials(&self, _u: Parameter, _v: Parameter) -> (Vec3, Vec3) {
74 (
75 self.plane.x_axis().into_vec(self.u_extent),
76 self.plane.y_axis().into_vec(self.v_extent),
77 )
78 }
79
80 fn normal(&self, _u: Parameter, _v: Parameter) -> UnitVec3 {
81 self.plane.normal()
82 }
83
84 fn bounding_box(&self) -> Aabb3 {
85 Aabb3::from_corners(self.point_at(0.0, 0.0), self.point_at(1.0, 1.0)).union(
86 Aabb3::from_corners(self.point_at(1.0, 0.0), self.point_at(0.0, 1.0)),
87 )
88 }
89
90 fn contains_point(&self, p: Point3, tolerance: Tolerance) -> bool {
91 let (a, b, off_plane) = circular3::local_coords(self.plane, p);
92 if off_plane.abs() > tolerance.value() {
93 return false;
94 }
95 let tol = tolerance.value();
96 (-tol..=self.u_extent.get::<millimeter>() + tol).contains(&a)
97 && (-tol..=self.v_extent.get::<millimeter>() + tol).contains(&b)
98 }
99
100 fn tessellate(&self, _chord: ChordHeightTolerance, _angle: AngleTolerance) -> TriMesh {
101 let normal = self.plane.normal();
102 TriMesh::from_grid(2, 2, |i, j| {
103 MeshVertex::new(self.point_at(f64::from(i), f64::from(j)), normal)
104 })
105 }
106}
107
108impl core::fmt::Display for PlaneSurface {
109 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
110 write!(
111 f,
112 "plane_surface{{ o={}, normal={}, u={} mm, v={} mm }}",
113 self.plane.origin(),
114 self.plane.normal(),
115 self.u_extent.get::<millimeter>(),
116 self.v_extent.get::<millimeter>(),
117 )
118 }
119}