axiolid_nurbs/
surface_analysis.rs1use axiolid_contracts::{GeomError, GeomResult};
4use axiolid_core::{Point3, Scalar, Tolerance, Vec3};
5use axiolid_evaluate::surface::jet;
6use axiolid_surface::Surface;
7
8#[derive(Debug, Clone, Copy, PartialEq)]
10pub struct FundamentalForm {
11 pub e: Scalar,
13 pub f: Scalar,
15 pub g: Scalar,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq)]
21pub struct SurfaceDifferential {
22 pub point: Point3,
24 pub unit_normal: Vec3,
26 pub first: FundamentalForm,
28 pub second: FundamentalForm,
30 pub gaussian_curvature: Scalar,
32 pub mean_curvature: Scalar,
34 pub principal_curvatures: [Scalar; 2],
36}
37
38pub fn analyze_surface(
40 surface: &Surface,
41 u: Scalar,
42 v: Scalar,
43 tolerance: Tolerance,
44) -> GeomResult<SurfaceDifferential> {
45 let j = jet(surface, u, v)?;
46 let cross = j.du.cross(j.dv);
47 let area = cross.length();
48 let area_floor = tolerance.linear() * tolerance.linear();
49 if !area.is_finite() || area <= area_floor {
50 return Err(GeomError::Degenerate(format!(
51 "surface differential area {area} does not exceed the tolerance area"
52 )));
53 }
54 let n = cross / area;
55 let first = FundamentalForm {
56 e: j.du.dot(j.du),
57 f: j.du.dot(j.dv),
58 g: j.dv.dot(j.dv),
59 };
60 let second = FundamentalForm {
61 e: n.dot(j.duu),
62 f: n.dot(j.duv),
63 g: n.dot(j.dvv),
64 };
65 let determinant = first.e * first.g - first.f * first.f;
66 if !determinant.is_finite() || determinant <= area_floor * area_floor {
67 return Err(GeomError::Degenerate(
68 "surface first fundamental form is singular".to_owned(),
69 ));
70 }
71 let gaussian = (second.e * second.g - second.f * second.f) / determinant;
72 let mean =
73 (second.e * first.g - 2.0 * second.f * first.f + second.g * first.e) / (2.0 * determinant);
74 let raw_discriminant = mean * mean - gaussian;
75 let scale = mean.abs().max(gaussian.abs().sqrt()).max(1.0);
76 let floor = -64.0 * Scalar::EPSILON * scale * scale;
77 if raw_discriminant < floor || !raw_discriminant.is_finite() {
78 return Err(GeomError::Degenerate(
79 "surface principal-curvature discriminant is invalid".to_owned(),
80 ));
81 }
82 let root = raw_discriminant.max(0.0).sqrt();
83 let result = SurfaceDifferential {
84 point: j.point,
85 unit_normal: n,
86 first,
87 second,
88 gaussian_curvature: gaussian,
89 mean_curvature: mean,
90 principal_curvatures: [mean - root, mean + root],
91 };
92 if [
93 gaussian,
94 mean,
95 result.principal_curvatures[0],
96 result.principal_curvatures[1],
97 ]
98 .iter()
99 .all(|x| x.is_finite())
100 {
101 Ok(result)
102 } else {
103 Err(GeomError::Degenerate(
104 "surface curvature is non-finite".to_owned(),
105 ))
106 }
107}