brep_kernel/props/mass_properties/
measures.rs1use super::*;
2
3pub fn face_area(face: &FaceRecord) -> Result<f64, String> {
4 if is_affine(&face.surface)? {
5 return Ok(parameter_space_area(face)?.abs() * planar_metric(face)?);
6 }
7 if let Some(values) = biperiodic_band_integral(face, &[Integrand::Area])? {
8 return Ok(values[0]);
9 }
10 if is_untrimmed(face)? {
11 integrate_untrimmed(face, Integrand::Area)
12 } else {
13 integrate_trimmed(face, Integrand::Area)
14 }
15}
16
17pub fn curve_arc_length(curve: &NurbsCurve, t0: f64, t1: f64) -> Result<f64, String> {
24 let (lo, hi) = (t0.min(t1), t0.max(t1));
25 if !(hi > lo) || !hi.is_finite() || !lo.is_finite() {
26 return Ok(0.0);
27 }
28 let mut breaks: Vec<f64> = vec![lo, hi];
31 for &knot in &curve.knots {
32 if knot > lo + 1e-12 && knot < hi - 1e-12 {
33 breaks.push(knot);
34 }
35 }
36 breaks.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
37 breaks.dedup_by(|a, b| (*a - *b).abs() <= 1e-12);
38
39 let mut length = 0.0;
40 for window in breaks.windows(2) {
41 let (a, b) = (window[0], window[1]);
42 if !(b > a) {
43 continue;
44 }
45 for sub in 0..2 {
47 let sa = a + (b - a) * sub as f64 / 2.0;
48 let sb = a + (b - a) * (sub + 1) as f64 / 2.0;
49 let middle = 0.5 * (sa + sb);
50 let half = 0.5 * (sb - sa);
51 for index in 0..GAUSS_X.len() {
52 let t = middle + half * GAUSS_X[index];
53 let speed = curve.derivatives(t, 1)?[1].length();
54 length += GAUSS_W[index] * half * speed;
55 }
56 }
57 }
58 Ok(length)
59}
60
61pub fn edge_arc_length(edge: &EdgeRecord) -> Result<f64, String> {
64 if edge.degenerate {
65 return Ok(0.0);
66 }
67 curve_arc_length(&edge.curve, edge.t0, edge.t1)
68}
69
70pub fn solid_edge_length_total(solid: &BrepSolid) -> Result<f64, String> {
74 let mut total = 0.0;
75 for edge in &solid.edges {
76 total += edge_arc_length(edge)?;
77 }
78 Ok(total)
79}
80
81pub fn face_boundary_length(solid: &BrepSolid, face: &FaceRecord) -> Result<f64, String> {
86 let mut edge_ids: Vec<u64> = Vec::new();
87 for loop_record in &face.loops {
88 for coedge in &loop_record.coedges {
89 if !edge_ids.contains(&coedge.edge_id) {
90 edge_ids.push(coedge.edge_id);
91 }
92 }
93 }
94 let mut total = 0.0;
95 for id in edge_ids {
96 if let Some(edge) = solid.edges.iter().find(|edge| edge.id == id) {
97 total += edge_arc_length(edge)?;
98 }
99 }
100 Ok(total)
101}
102
103pub fn face_volume_contribution(face: &FaceRecord) -> Result<f64, String> {
104 if is_affine(&face.surface)? {
105 let signed_area = parameter_space_area(face)? * planar_metric(face)?;
106 let ku = crate::KnotVector::new(face.surface.knots_u.clone(), face.surface.degree_u)?;
107 let kv = crate::KnotVector::new(face.surface.knots_v.clone(), face.surface.degree_v)?;
108 let u = ku.domain()[0];
109 let v = kv.domain()[0];
110 let point = face.surface.evaluate(u, v)?;
111 let normal = face.surface.normal(u, v)?;
112 return Ok(point.dot(normal) * signed_area / 3.0);
113 }
114 if let Some(values) = biperiodic_band_integral(face, &[Integrand::Volume])? {
115 return Ok(values[0] / 3.0);
116 }
117 let integral = if is_untrimmed(face)? {
118 integrate_untrimmed(face, Integrand::Volume)?
119 } else {
120 integrate_trimmed(face, Integrand::Volume)?
121 };
122 Ok(integral / 3.0)
123}
124
125pub(super) fn face_volume_contribution_about(face: &FaceRecord, reference: Vec3) -> Result<f64, String> {
126 if is_affine(&face.surface)? {
127 let signed_area = parameter_space_area(face)? * planar_metric(face)?;
128 let ku = crate::KnotVector::new(face.surface.knots_u.clone(), face.surface.degree_u)?;
129 let kv = crate::KnotVector::new(face.surface.knots_v.clone(), face.surface.degree_v)?;
130 let u = ku.domain()[0];
131 let v = kv.domain()[0];
132 let point = face.surface.evaluate(u, v)?;
133 let normal = face.surface.normal(u, v)?;
134 return Ok(point.sub(reference).dot(normal) * signed_area / 3.0);
135 }
136 let kind = Integrand::VolumeAbout(reference);
137 if let Some(values) = biperiodic_band_integral(face, &[kind])? {
138 return Ok(values[0] / 3.0);
139 }
140 let integral = if is_untrimmed(face)? {
141 integrate_untrimmed(face, kind)?
142 } else {
143 integrate_trimmed(face, kind)?
144 };
145 Ok(integral / 3.0)
146}