manifold_rust/
smoothing.rs1use std::collections::HashMap;
18
19use crate::impl_mesh::ManifoldImpl;
20use crate::linalg::{cross, dot, length, Mat3, Vec3, Vec4};
21use crate::math;
22use crate::types::{K_PI, K_TWO_PI, Smoothness, TriRef};
23
24const K_MIN_SHARP_ANGLE: f64 = 1e-4;
28
29#[inline]
30pub(super) fn vec3_from_vec4(v: Vec4) -> Vec3 {
31 Vec3::new(v.x, v.y, v.z)
32}
33
34#[inline]
35pub(super) fn safe_normalize(v: Vec3) -> Vec3 {
36 let len2 = dot(v, v);
37 if len2 <= 0.0 || !len2.is_finite() {
38 Vec3::new(0.0, 0.0, 0.0)
39 } else {
40 v / len2.sqrt()
41 }
42}
43
44pub(super) fn wrap(radians: f64) -> f64 {
45 if radians < -K_PI {
46 radians + K_TWO_PI
47 } else if radians > K_PI {
48 radians - K_TWO_PI
49 } else {
50 radians
51 }
52}
53
54pub(super) fn angle_between(a: Vec3, b: Vec3) -> f64 {
55 let d = dot(a, b);
56 if d >= 1.0 {
57 0.0
58 } else if d <= -1.0 {
59 K_PI
60 } else {
61 math::acos(d)
62 }
63}
64
65pub(super) fn equal_normals(a: Vec3, b: Vec3) -> bool {
69 dot(safe_normalize(a), safe_normalize(b)) > 0.9999
70}
71
72pub fn circular_tangent(tangent: Vec3, edge_vec: Vec3) -> Vec4 {
73 let dir = safe_normalize(tangent);
74 let weight = 0.5f64.max(dot(dir, safe_normalize(edge_vec)));
75 let bz2 = Vec4::new(
76 dir.x * 0.5 * length(edge_vec),
77 dir.y * 0.5 * length(edge_vec),
78 dir.z * 0.5 * length(edge_vec),
79 weight,
80 );
81 let bz3 = Vec4::new(
82 bz2.x * (2.0 / 3.0),
83 bz2.y * (2.0 / 3.0),
84 bz2.z * (2.0 / 3.0),
85 1.0 + (bz2.w - 1.0) * (2.0 / 3.0),
86 );
87 Vec4::new(bz3.x / bz3.w, bz3.y / bz3.w, bz3.z / bz3.w, bz3.w)
88}
89
90pub(super) fn collect_vertex_cycle(mesh: &ManifoldImpl, start: usize) -> Vec<usize> {
91 let mut cycle = Vec::new();
92 let mut current = start;
93 loop {
94 cycle.push(current);
95 let paired = mesh.halfedge[current].paired_halfedge;
96 if paired < 0 {
97 break;
98 }
99 current = crate::impl_mesh::next_halfedge(paired) as usize;
100 if current == start {
101 break;
102 }
103 }
104 cycle
105}
106
107impl ManifoldImpl {
108 pub fn get_normal(&self, halfedge: usize, normal_idx: usize) -> Vec3 {
109 let prop = self.halfedge[halfedge].prop_vert as usize;
110 let base = prop * self.num_prop + normal_idx;
111 let normal = Vec3::new(
112 self.properties[base],
113 self.properties[base + 1],
114 self.properties[base + 2],
115 );
116 let mesh_id = self.mesh_relation.tri_ref[halfedge / 3].mesh_id;
122 match self.mesh_relation.mesh_id_transform.get(&mesh_id) {
123 Some(rel) if !rel.has_normals => rel.get_normal_transform() * normal,
124 _ => normal,
125 }
126 }
127
128 pub fn tangent_from_normal(&self, normal: Vec3, halfedge: usize) -> Vec4 {
129 let edge = self.halfedge[halfedge];
130 let edge_vec = self.vert_pos[edge.end_vert as usize] - self.vert_pos[edge.start_vert as usize];
131 let edge_normal =
132 self.face_normal[halfedge / 3] + self.face_normal[edge.paired_halfedge as usize / 3];
133 let bi_tangent = if dot(normal, edge_normal) < 0.0 {
138 cross(edge_normal, edge_vec)
139 } else {
140 cross(normal, edge_vec)
141 };
142 circular_tangent(cross(bi_tangent, normal), edge_vec)
143 }
144
145 pub fn is_inside_quad(&self, halfedge: usize) -> bool {
146 if !self.halfedge_tangent.is_empty() {
147 return self.halfedge_tangent[halfedge].w < 0.0;
148 }
149 let tri = halfedge / 3;
150 let ref_tri = self.mesh_relation.tri_ref[tri];
151 let pair = self.halfedge[halfedge].paired_halfedge as usize;
152 let pair_tri = pair / 3;
153 let pair_ref = self.mesh_relation.tri_ref[pair_tri];
154 if !ref_tri.same_face(&pair_ref) {
155 return false;
156 }
157
158 let same_face = |edge_idx: usize, reference: TriRef| -> bool {
159 let pair = self.halfedge[edge_idx].paired_halfedge as usize / 3;
160 reference.same_face(&self.mesh_relation.tri_ref[pair])
161 };
162
163 let mut neighbor = crate::impl_mesh::next_halfedge(halfedge as i32) as usize;
164 if same_face(neighbor, ref_tri) {
165 return false;
166 }
167 neighbor = crate::impl_mesh::next_halfedge(neighbor as i32) as usize;
168 if same_face(neighbor, ref_tri) {
169 return false;
170 }
171 neighbor = crate::impl_mesh::next_halfedge(pair as i32) as usize;
172 if same_face(neighbor, pair_ref) {
173 return false;
174 }
175 neighbor = crate::impl_mesh::next_halfedge(neighbor as i32) as usize;
176 if same_face(neighbor, pair_ref) {
177 return false;
178 }
179 true
180 }
181
182 pub fn is_marked_inside_quad(&self, halfedge: usize) -> bool {
183 !self.halfedge_tangent.is_empty() && self.halfedge_tangent[halfedge].w == -1.0
185 }
186
187 pub fn update_sharpened_edges(&self, sharpened_edges: &[Smoothness]) -> Vec<Smoothness> {
188 let mut old_halfedge_to_new = HashMap::new();
189 for tri in 0..self.num_tri() {
190 let old_tri = self.mesh_relation.tri_ref[tri].face_id;
191 for i in 0..3 {
192 old_halfedge_to_new.insert((3 * old_tri + i as i32) as usize, 3 * tri + i);
193 }
194 }
195 let mut out = sharpened_edges.to_vec();
196 for edge in &mut out {
197 if let Some(&new_edge) = old_halfedge_to_new.get(&edge.halfedge) {
198 edge.halfedge = new_edge;
199 }
200 }
201 out
202 }
203
204 pub fn flat_faces(&self) -> Vec<bool> {
205 let num_tri = self.num_tri();
206 let mut tri_is_flat_face = vec![false; num_tri];
207 for tri in 0..num_tri {
208 let reference = self.mesh_relation.tri_ref[tri];
209 let mut face_neighbors = 0;
210 let mut face_tris = [-1; 3];
211 for j in 0..3 {
212 let neighbor_tri = self.halfedge[3 * tri + j].paired_halfedge as usize / 3;
213 let j_ref = self.mesh_relation.tri_ref[neighbor_tri];
214 if j_ref.same_face(&reference) {
215 face_neighbors += 1;
216 face_tris[j] = neighbor_tri as i32;
217 }
218 }
219 if face_neighbors > 1 {
220 tri_is_flat_face[tri] = true;
221 for j in 0..3 {
222 if face_tris[j] >= 0 {
223 tri_is_flat_face[face_tris[j] as usize] = true;
224 }
225 }
226 }
227 }
228 tri_is_flat_face
229 }
230
231 pub fn vert_flat_face(&self, flat_faces: &[bool]) -> Vec<i32> {
232 let mut vert_flat_face = vec![-1; self.num_vert()];
233 let mut vert_ref = vec![TriRef::default(); self.num_vert()];
234 for tri in 0..self.num_tri() {
235 if flat_faces[tri] {
236 for j in 0..3 {
237 let vert = self.halfedge[3 * tri + j].start_vert as usize;
238 if vert_ref[vert].same_face(&self.mesh_relation.tri_ref[tri]) {
239 continue;
240 }
241 vert_ref[vert] = self.mesh_relation.tri_ref[tri];
242 vert_flat_face[vert] = if vert_flat_face[vert] == -1 { tri as i32 } else { -2 };
243 }
244 }
245 }
246 vert_flat_face
247 }
248
249 pub fn set_normals(&mut self, normal_idx: i32, min_sharp_angle: f64) {
253 if self.is_empty() || normal_idx < 0 {
254 return;
255 }
256 let min_sharp_angle = min_sharp_angle.max(K_MIN_SHARP_ANGLE);
259 let normal_idx = normal_idx as usize;
260
261 let old_num_prop = self.num_prop;
262
263 let mut vert_num_sharp = vec![0i32; self.num_vert()];
268 for e in 0..self.halfedge.len() {
269 if !self.halfedge[e].is_forward() {
270 continue;
271 }
272 let pair = self.halfedge[e].paired_halfedge as usize;
273 let tri1 = e / 3;
274 let tri2 = pair / 3;
275 let dihedral =
276 angle_between(self.face_normal[tri1], self.face_normal[tri2]).to_degrees();
277 if dihedral > min_sharp_angle {
278 vert_num_sharp[self.halfedge[e].start_vert as usize] += 1;
279 vert_num_sharp[self.halfedge[e].end_vert as usize] += 1;
280 }
281 }
282
283 let num_prop = old_num_prop.max(normal_idx + 3);
287 let num_prop_vert = self.num_prop_vert();
288 let orig_props = std::mem::take(&mut self.properties); let mut new_properties = vec![0.0f64; num_prop * num_prop_vert];
290
291 self.num_prop = num_prop;
292
293 let old_halfedge_prop: Vec<i32> = self.halfedge.iter().map(|h| h.prop_vert).collect();
295 for h in self.halfedge.iter_mut() {
296 h.prop_vert = -1;
297 }
298
299 let mut mesh_id_to_inv_transform: HashMap<i32, Mat3> = HashMap::new();
304
305 let num_edge = self.halfedge.len();
306 for start_edge in 0..num_edge {
307 if self.halfedge[start_edge].prop_vert >= 0 {
308 continue;
309 }
310 let vert = self.halfedge[start_edge].start_vert as usize;
311
312 let mesh_id = self.mesh_relation.tri_ref[start_edge / 3].mesh_id;
314 let inv_transform = *mesh_id_to_inv_transform.entry(mesh_id).or_insert_with(|| {
315 self.mesh_relation
316 .mesh_id_transform
317 .get(&mesh_id)
318 .map(|rel| rel.get_inverse_normal_transform())
319 .unwrap_or_else(Mat3::identity)
320 });
321
322 if vert_num_sharp[vert] < 2 {
323 let world_normal = self.vert_normal[vert];
326 let normal = if normal_idx == 0 {
331 world_normal
332 } else {
333 inv_transform * world_normal
334 };
335
336 let mut last_prop: i32 = -1;
337 let mut current = start_edge;
339 loop {
340 let prop = old_halfedge_prop[current];
341 self.halfedge[current].prop_vert = prop;
342 if prop != last_prop {
343 last_prop = prop;
344 let dst_start = (prop as usize) * num_prop;
346 let src_start = (prop as usize) * old_num_prop;
347 for p in 0..old_num_prop.min(num_prop) {
348 if src_start + p < orig_props.len() {
349 new_properties[dst_start + p] = orig_props[src_start + p];
350 }
351 }
352 new_properties[prop as usize * num_prop + normal_idx] = normal.x;
354 new_properties[prop as usize * num_prop + normal_idx + 1] = normal.y;
355 new_properties[prop as usize * num_prop + normal_idx + 2] = normal.z;
356 }
357 current = crate::types::next_halfedge(
358 self.halfedge[current].paired_halfedge,
359 ) as usize;
360 if current == start_edge {
361 break;
362 }
363 }
364 } else {
365 let center_pos = self.vert_pos[vert];
367 let mut group: Vec<usize> = Vec::new();
368 let mut normals: Vec<Vec3> = Vec::new();
369
370 let mut current = start_edge;
372 let mut prev_face = current / 3;
373 loop {
374 let next = crate::types::next_halfedge(
375 self.halfedge[current].paired_halfedge,
376 ) as usize;
377 let face = next / 3;
378 let dihedral =
379 angle_between(self.face_normal[face], self.face_normal[prev_face])
380 .to_degrees();
381 if dihedral > min_sharp_angle {
382 break;
383 }
384 current = next;
385 prev_face = face;
386 if current == start_edge {
387 break;
388 }
389 }
390
391 let end_edge = current;
392
393 let get_edge_vec = |he: usize| -> Vec3 {
404 let end_v = self.halfedge[he].end_vert as usize;
405 safe_normalize(self.vert_pos[end_v] - center_pos)
406 };
407
408 let mut here_face = end_edge / 3;
409 let mut here_ev = get_edge_vec(end_edge);
410 current = end_edge;
411 loop {
412 let next_he = crate::types::next_halfedge(
413 self.halfedge[current].paired_halfedge,
414 ) as usize;
415 let next_face = next_he / 3;
416 let mut next_ev = get_edge_vec(next_he);
417
418 let dihedral =
420 angle_between(self.face_normal[here_face], self.face_normal[next_face])
421 .to_degrees();
422 if dihedral > min_sharp_angle {
423 normals.push(Vec3::splat(0.0));
424 }
425 group.push(normals.len() - 1);
426
427 if next_ev.x.is_finite() {
429 let c = cross(next_ev, here_ev);
430 let angle = angle_between(here_ev, next_ev);
431 *normals.last_mut().unwrap() =
432 *normals.last().unwrap() + safe_normalize(c) * angle;
433 } else {
434 next_ev = here_ev;
435 }
436
437 here_face = next_face;
438 here_ev = next_ev;
439 current = next_he;
440 if current == end_edge {
441 break;
442 }
443 }
444
445 for n in normals.iter_mut() {
449 *n = if normal_idx == 0 {
450 safe_normalize(*n)
451 } else {
452 safe_normalize(inv_transform * *n)
453 };
454 }
455
456 let mut last_group: usize = 0;
461 let mut last_prop: i32 = -1;
462 let mut new_prop: i32 = -1;
463 let mut idx = 0usize;
464 current = crate::types::next_halfedge(
465 self.halfedge[end_edge].paired_halfedge,
466 ) as usize;
467 loop {
468 let prop = old_halfedge_prop[current];
469 let g = if idx < group.len() { group[idx] } else { 0 };
470
471 if g != last_group && g != 0 && prop == last_prop {
472 last_group = g;
474 new_prop = (new_properties.len() / num_prop) as i32;
475 new_properties.resize(new_properties.len() + num_prop, 0.0);
476 let src_start = (prop as usize) * old_num_prop;
477 for p in 0..old_num_prop.min(num_prop) {
478 if src_start + p < orig_props.len() {
479 new_properties[new_prop as usize * num_prop + p] = orig_props[src_start + p];
480 }
481 }
482 if g < normals.len() {
483 new_properties[new_prop as usize * num_prop + normal_idx] = normals[g].x;
484 new_properties[new_prop as usize * num_prop + normal_idx + 1] = normals[g].y;
485 new_properties[new_prop as usize * num_prop + normal_idx + 2] = normals[g].z;
486 }
487 } else if prop != last_prop {
488 last_prop = prop;
490 new_prop = prop;
491 let dst_start = (prop as usize) * num_prop;
492 let src_start = (prop as usize) * old_num_prop;
493 for p in 0..old_num_prop.min(num_prop) {
494 if src_start + p < orig_props.len() {
495 new_properties[dst_start + p] = orig_props[src_start + p];
496 }
497 }
498 if g < normals.len() {
499 new_properties[prop as usize * num_prop + normal_idx] = normals[g].x;
500 new_properties[prop as usize * num_prop + normal_idx + 1] = normals[g].y;
501 new_properties[prop as usize * num_prop + normal_idx + 2] = normals[g].z;
502 }
503 }
504
505 self.halfedge[current].prop_vert = new_prop;
506 idx += 1;
507
508 let next_current = crate::types::next_halfedge(
509 self.halfedge[current].paired_halfedge,
510 ) as usize;
511 if current == end_edge {
513 break;
514 }
515 current = next_current;
516 }
517 }
518 }
519
520 self.properties = new_properties;
521 }
522}
523
524#[path = "smoothing_tangents.rs"]
528mod smoothing_tangents;
529
530#[cfg(test)]
531#[path = "smoothing_tests.rs"]
532mod tests;