1use std::collections::BTreeMap;
6
7use crate::impl_mesh::ManifoldImpl;
8use crate::linalg::{cross, dot, normalize, Vec3, Vec4};
9use crate::math;
10use crate::types::{next_halfedge, prev_halfedge, radians, K_PI, K_TWO_PI, Smoothness};
11
12use super::{vec3_from_vec4, safe_normalize, angle_between, circular_tangent, equal_normals, wrap, collect_vertex_cycle};
13
14impl ManifoldImpl {
15 pub fn vert_halfedge(&self) -> Vec<i32> {
16 let mut vert_halfedge = vec![-1; self.num_vert()];
17 for (idx, edge) in self.halfedge.iter().enumerate() {
18 let start = edge.start_vert as usize;
19 if vert_halfedge[start] < 0 {
20 vert_halfedge[start] = idx as i32;
21 }
22 }
23 vert_halfedge
24 }
25
26 pub fn sharpen_edges(&self, min_sharp_angle: f64, min_smoothness: f64) -> Vec<Smoothness> {
27 let mut out = Vec::new();
28 let min_radians = radians(min_sharp_angle.max(1e-4));
30 for e in 0..self.halfedge.len() {
31 if !self.halfedge[e].is_forward() {
32 continue;
33 }
34 let pair = self.halfedge[e].paired_halfedge as usize;
35 let d = dot(self.face_normal[e / 3], self.face_normal[pair / 3]).clamp(-1.0, 1.0);
36 let dihedral = math::acos(d);
37 if dihedral > min_radians {
38 out.push(Smoothness { halfedge: e, smoothness: min_smoothness });
39 out.push(Smoothness { halfedge: pair, smoothness: min_smoothness });
40 }
41 }
42 out
43 }
44
45 pub fn sharpen_tangent(&mut self, halfedge: usize, smoothness: f64) {
46 self.halfedge_tangent[halfedge] = Vec4::new(
47 smoothness * self.halfedge_tangent[halfedge].x,
48 smoothness * self.halfedge_tangent[halfedge].y,
49 smoothness * self.halfedge_tangent[halfedge].z,
50 if smoothness == 0.0 { 0.0 } else { self.halfedge_tangent[halfedge].w },
51 );
52 }
53
54 pub fn linearize_flat_tangents(&mut self) {
55 for halfedge in 0..self.halfedge_tangent.len() {
56 if !self.halfedge[halfedge].is_forward() {
57 continue;
58 }
59 let pair = self.halfedge[halfedge].paired_halfedge as usize;
60 let tangent = self.halfedge_tangent[halfedge];
61 let other = self.halfedge_tangent[pair];
62 let flat = [tangent.w == 0.0, other.w == 0.0];
63 if !flat[0] && !flat[1] {
64 continue;
65 }
66 let edge_vec = self.vert_pos[self.halfedge[halfedge].end_vert as usize]
67 - self.vert_pos[self.halfedge[halfedge].start_vert as usize];
68
69 if flat[0] && flat[1] {
70 self.halfedge_tangent[halfedge] = Vec4::new(edge_vec.x / 3.0, edge_vec.y / 3.0, edge_vec.z / 3.0, 1.0);
71 self.halfedge_tangent[pair] = Vec4::new(-edge_vec.x / 3.0, -edge_vec.y / 3.0, -edge_vec.z / 3.0, 1.0);
72 } else if flat[0] {
73 let other_v = vec3_from_vec4(other);
74 let v = (edge_vec + other_v) / 2.0;
75 self.halfedge_tangent[halfedge] = Vec4::new(v.x, v.y, v.z, 1.0);
76 } else {
77 let tan_v = vec3_from_vec4(tangent);
78 let v = (-edge_vec + tan_v) / 2.0;
79 self.halfedge_tangent[pair] = Vec4::new(v.x, v.y, v.z, 1.0);
80 }
81 }
82 }
83
84 pub fn distribute_tangents(&mut self, fixed_halfedges: &[bool]) {
85 for halfedge in 0..fixed_halfedges.len() {
86 if !fixed_halfedges[halfedge] || self.is_marked_inside_quad(halfedge) {
89 continue;
90 }
91
92 let start = halfedge;
93
94 let mut normal = Vec3::new(0.0, 0.0, 0.0);
95 let mut current_angle = Vec::new();
96 let mut desired_angle = Vec::new();
97
98 let approx_normal = self.vert_normal[self.halfedge[start].start_vert as usize];
99 let center = self.vert_pos[self.halfedge[start].start_vert as usize];
100 let mut last_edge_vec =
101 safe_normalize(self.vert_pos[self.halfedge[start].end_vert as usize] - center);
102 let first_tangent = safe_normalize(vec3_from_vec4(self.halfedge_tangent[start]));
103 let mut last_tangent = first_tangent;
104 let mut current = start;
105 let mut guard = 0usize;
106
107 loop {
108 guard += 1;
109 if guard > self.halfedge.len() + 1 {
110 break;
111 }
112 current = crate::impl_mesh::next_halfedge(self.halfedge[current].paired_halfedge) as usize;
113 if self.is_marked_inside_quad(current) {
114 if current == start {
115 break;
116 }
117 continue;
118 }
119 let this_edge_vec =
120 safe_normalize(self.vert_pos[self.halfedge[current].end_vert as usize] - center);
121 let this_tangent = safe_normalize(vec3_from_vec4(self.halfedge_tangent[current]));
122 normal = normal + cross(this_tangent, last_tangent);
123
124 let cumulative = angle_between(this_edge_vec, last_edge_vec)
125 + desired_angle.last().copied().unwrap_or(0.0);
126 desired_angle.push(cumulative);
127
128 if current == start {
129 current_angle.push(K_TWO_PI);
130 } else {
131 let mut angle = angle_between(this_tangent, first_tangent);
132 if dot(approx_normal, cross(this_tangent, first_tangent)) < 0.0 {
133 angle = K_TWO_PI - angle;
134 }
135 current_angle.push(angle);
136 }
137
138 last_edge_vec = this_edge_vec;
139 last_tangent = this_tangent;
140 if fixed_halfedges[current] {
141 break;
142 }
143 }
144
145 if current_angle.len() == 1 || dot(normal, normal) == 0.0 {
146 continue;
147 }
148
149 let scale = current_angle.last().copied().unwrap_or(K_TWO_PI)
150 / desired_angle.last().copied().unwrap_or(K_TWO_PI);
151 let mut offset = 0.0;
152 if current == start {
153 for i in 0..current_angle.len() {
154 offset += wrap(current_angle[i] - scale * desired_angle[i]);
155 }
156 offset /= current_angle.len() as f64;
157 }
158
159 current = start;
160 let axis = safe_normalize(normal);
161 let mut i = 0usize;
162 let mut guard = 0usize;
163 loop {
164 guard += 1;
165 if guard > self.halfedge.len() + 1 {
166 break;
167 }
168 current = crate::impl_mesh::next_halfedge(self.halfedge[current].paired_halfedge) as usize;
169 if current != start && fixed_halfedges[current] {
172 break;
173 }
174 if self.is_marked_inside_quad(current) {
175 if current == start {
176 break;
177 }
178 continue;
179 }
180 desired_angle[i] *= scale;
181 let last_angle = if i > 0 { desired_angle[i - 1] } else { 0.0 };
182 if desired_angle[i] - last_angle > K_PI {
183 desired_angle[i] = last_angle + K_PI;
184 } else if i + 1 < desired_angle.len() && scale * desired_angle[i + 1] - desired_angle[i] > K_PI {
185 desired_angle[i] = scale * desired_angle[i + 1] - K_PI;
186 }
187
188 let angle = current_angle[i] - desired_angle[i] - offset;
189 let tangent = vec3_from_vec4(self.halfedge_tangent[current]);
190 let q = crate::linalg::rotation_quat_axis_angle(axis, angle);
191 let rotated = crate::linalg::qrot(q, tangent);
192 self.halfedge_tangent[current] = Vec4::new(
193 rotated.x,
194 rotated.y,
195 rotated.z,
196 self.halfedge_tangent[current].w,
197 );
198 i += 1;
199 if fixed_halfedges[current] {
200 break;
201 }
202 }
203 }
204 }
205
206 pub fn create_tangents_from_normals(&mut self, normal_idx: usize) {
207 if self.is_empty() {
208 return;
209 }
210 const K_INSIDE_QUAD: f64 = -1.0;
212 const K_MISSING_NORMAL: f64 = -3.0;
213
214 let num_vert = self.num_vert();
215 let num_halfedge = self.halfedge.len();
216 let mut tangent = vec![Vec4::new(0.0, 0.0, 0.0, 0.0); num_halfedge];
217 let mut fixed_halfedge = vec![false; num_halfedge];
218 let vert_halfedge = self.vert_halfedge();
219
220 for &e in vert_halfedge.iter().take(num_vert) {
221 if e < 0 {
222 continue;
223 }
224 let e = e as usize;
225 let cycle = collect_vertex_cycle(self, e);
226 let mut face_edges = [-1isize, -1isize];
227 let mut start_halfedge: isize = -1;
228 let mut last_normal = Vec3::new(0.0, 0.0, 0.0);
229
230 for idx in 0..cycle.len() {
231 let halfedge = cycle[idx];
232 let next_he = cycle[(idx + 1) % cycle.len()];
233 let here_normal = self.get_normal(halfedge, normal_idx);
234 let next_normal = self.get_normal(next_he, normal_idx);
235 let here_is_flat = equal_normals(here_normal, self.face_normal[halfedge / 3]);
238 let next_is_flat = equal_normals(next_normal, self.face_normal[next_he / 3]);
239
240 tangent[halfedge].w = 1.0;
242
243 if here_is_flat != next_is_flat {
244 if face_edges[0] == -1 {
246 face_edges[0] = halfedge as isize;
247 } else if face_edges[1] == -1 {
248 face_edges[1] = halfedge as isize;
249 } else {
250 face_edges[0] = -2;
251 }
252 }
253
254 let here_zero = here_normal == Vec3::new(0.0, 0.0, 0.0);
255 let next_zero = next_normal == Vec3::new(0.0, 0.0, 0.0);
256
257 if here_zero || next_zero {
258 if !here_zero {
259 last_normal = here_normal;
261 } else if !next_zero {
262 if start_halfedge < 0 {
264 start_halfedge = halfedge as isize;
265 }
266 } else {
267 if start_halfedge < 0 {
269 start_halfedge = -2;
270 }
271 }
272 tangent[halfedge] = Vec4::new(last_normal.x, last_normal.y, last_normal.z, K_MISSING_NORMAL);
273 }
274
275 if self.is_inside_quad(halfedge) {
276 tangent[halfedge] = Vec4::new(last_normal.x, last_normal.y, last_normal.z, K_INSIDE_QUAD);
277 }
278
279 if tangent[halfedge].w < 0.0 {
280 continue;
281 }
282
283 let different_normals = !equal_normals(next_normal, here_normal);
284 if different_normals {
285 fixed_halfedge[halfedge] = true;
286 face_edges[0] = -2; }
288
289 tangent[halfedge] = if different_normals {
290 let edge_vec = self.vert_pos[self.halfedge[halfedge].end_vert as usize]
291 - self.vert_pos[self.halfedge[halfedge].start_vert as usize];
292 let dir = cross(here_normal, next_normal);
293 let signed_dir = if dot(dir, edge_vec) < 0.0 { -dir } else { dir };
294 circular_tangent(signed_dir, edge_vec)
295 } else {
296 self.tangent_from_normal(here_normal, halfedge)
297 };
298 }
299
300 let last_zero = last_normal == Vec3::new(0.0, 0.0, 0.0);
302 if start_halfedge != -1 && last_zero {
303 let vert = self.halfedge[e].start_vert as usize;
304 let normal = self.vert_normal[vert];
305 for &halfedge in &cycle {
306 if tangent[halfedge].w != K_INSIDE_QUAD {
307 tangent[halfedge] = self.tangent_from_normal(normal, halfedge);
308 }
309 }
310 continue;
311 }
312
313 if start_halfedge >= 0 {
315 let start = start_halfedge as usize;
316 let paired_start = self.halfedge[start].paired_halfedge as usize;
318 let next_of_paired = next_halfedge(paired_start as i32) as usize;
319 let mut prev_norm = self.get_normal(next_of_paired, normal_idx);
320
321 let mut current = start;
322 loop {
323 if tangent[current].w == K_MISSING_NORMAL {
324 let stored = Vec3::new(tangent[current].x, tangent[current].y, tangent[current].z);
325 let next_norm = if stored == Vec3::new(0.0, 0.0, 0.0) {
326 last_normal
327 } else {
328 stored
329 };
330
331 tangent[current] = if equal_normals(prev_norm, next_norm) {
332 self.tangent_from_normal(prev_norm, current)
333 } else {
334 let dir = cross(prev_norm, next_norm);
335 let edge_vec = self.vert_pos[self.halfedge[current].end_vert as usize]
336 - self.vert_pos[self.halfedge[current].start_vert as usize];
337 let signed_dir = if dot(dir, edge_vec) < 0.0 { -dir } else { dir };
338 circular_tangent(signed_dir, edge_vec)
339 };
340 }
341
342 let current_normal = self.get_normal(current, normal_idx);
343 if current_normal != Vec3::new(0.0, 0.0, 0.0) {
344 prev_norm = current_normal;
345 }
346 let prev_he = prev_halfedge(current as i32) as usize;
348 current = self.halfedge[prev_he].paired_halfedge as usize;
349 if current == start {
350 break;
351 }
352 }
353 }
354
355 if face_edges[0] >= 0 && face_edges[1] >= 0 {
356 let f0 = face_edges[0] as usize;
357 let f1 = face_edges[1] as usize;
358 let edge0 = self.vert_pos[self.halfedge[f0].end_vert as usize]
359 - self.vert_pos[self.halfedge[f0].start_vert as usize];
360 let edge1 = self.vert_pos[self.halfedge[f1].end_vert as usize]
361 - self.vert_pos[self.halfedge[f1].start_vert as usize];
362 let new_tangent = normalize(edge0) - normalize(edge1);
363 tangent[f0] = circular_tangent(new_tangent, edge0);
364 tangent[f1] = circular_tangent(-new_tangent, edge1);
365 fixed_halfedge[f0] = true;
367 fixed_halfedge[f1] = true;
368 }
369 }
370
371 self.halfedge_tangent = tangent;
372 self.distribute_tangents(&fixed_halfedge);
373 }
374
375 pub fn valid_tangents(&self) -> bool {
379 if self.halfedge_tangent.len() != self.halfedge.len() {
380 return true; }
382 let num_halfedge = self.halfedge.len();
383 for edge_idx in 0..num_halfedge {
384 let in_quad = self.is_marked_inside_quad(edge_idx);
385 let pair = self.halfedge[edge_idx].paired_halfedge as usize;
386 if in_quad != self.is_marked_inside_quad(pair) {
387 return false;
388 }
389 if !in_quad {
390 continue;
391 }
392 let next_e = next_halfedge(edge_idx as i32) as usize;
394 let prev_e = prev_halfedge(edge_idx as i32) as usize;
395 let pair_next = next_halfedge(pair as i32) as usize;
396 let pair_prev = prev_halfedge(pair as i32) as usize;
397 if self.is_marked_inside_quad(next_e)
398 || self.is_marked_inside_quad(prev_e)
399 || self.is_marked_inside_quad(pair_next)
400 || self.is_marked_inside_quad(pair_prev)
401 {
402 return false;
403 }
404 }
405 true
406 }
407
408 pub fn create_tangents(&mut self, mut sharpened_edges: Vec<Smoothness>) {
409 if self.is_empty() {
410 return;
411 }
412 let num_halfedge = self.halfedge.len();
413 let vert_halfedge = self.vert_halfedge();
414 let tri_is_flat_face = self.flat_faces();
415 let vert_flat_face = self.vert_flat_face(&tri_is_flat_face);
416 let mut vert_normal = self.vert_normal.clone();
417 for v in 0..self.num_vert() {
418 if vert_flat_face[v] >= 0 {
419 vert_normal[v] = self.face_normal[vert_flat_face[v] as usize];
420 }
421 }
422
423 let mut tangent = vec![Vec4::new(0.0, 0.0, 0.0, 0.0); num_halfedge];
424 let mut fixed_halfedge = vec![false; num_halfedge];
425 for (edge_idx, tan) in tangent.iter_mut().enumerate() {
426 *tan = if self.is_inside_quad(edge_idx) {
427 Vec4::new(0.0, 0.0, 0.0, -1.0)
428 } else {
429 self.tangent_from_normal(vert_normal[self.halfedge[edge_idx].start_vert as usize], edge_idx)
430 };
431 }
432 self.halfedge_tangent = tangent;
433
434 for tri in 0..self.num_tri() {
435 if !tri_is_flat_face[tri] {
436 continue;
437 }
438 for j in 0..3 {
439 let tri2 = self.halfedge[3 * tri + j].paired_halfedge as usize / 3;
440 if !tri_is_flat_face[tri2]
441 || !self.mesh_relation.tri_ref[tri].same_face(&self.mesh_relation.tri_ref[tri2])
442 {
443 sharpened_edges.push(Smoothness { halfedge: 3 * tri + j, smoothness: 0.0 });
444 }
445 }
446 }
447
448 type Pair = (Smoothness, Smoothness);
449 let mut edges: BTreeMap<usize, Pair> = BTreeMap::new();
450 for edge in sharpened_edges {
451 if edge.smoothness >= 1.0 {
452 continue;
453 }
454 let forward = self.halfedge[edge.halfedge].is_forward();
455 let pair = self.halfedge[edge.halfedge].paired_halfedge as usize;
456 let idx = if forward { edge.halfedge } else { pair };
457 edges.entry(idx)
458 .and_modify(|existing| {
459 let e = if forward { &mut existing.0 } else { &mut existing.1 };
460 e.smoothness = e.smoothness.min(edge.smoothness);
461 })
462 .or_insert_with(|| {
463 let mut pair_entry = (edge, Smoothness { halfedge: pair, smoothness: 1.0 });
464 if !forward {
465 pair_entry = (pair_entry.1, pair_entry.0);
466 }
467 pair_entry
468 });
469 }
470
471 let mut vert_tangents: BTreeMap<usize, Vec<Pair>> = BTreeMap::new();
472 for edge in edges.values() {
473 vert_tangents
474 .entry(self.halfedge[edge.0.halfedge].start_vert as usize)
475 .or_default()
476 .push(*edge);
477 vert_tangents
478 .entry(self.halfedge[edge.1.halfedge].start_vert as usize)
479 .or_default()
480 .push((edge.1, edge.0));
481 }
482
483 for v in 0..self.num_vert() {
484 let Some(vert) = vert_tangents.get(&v) else {
485 if vert_halfedge[v] >= 0 {
486 fixed_halfedge[vert_halfedge[v] as usize] = true;
487 }
488 continue;
489 };
490
491 if vert.len() == 1 {
492 continue;
493 }
494 if vert.len() == 2 {
495 let first = vert[0].0.halfedge;
496 let second = vert[1].0.halfedge;
497 fixed_halfedge[first] = true;
498 fixed_halfedge[second] = true;
499 let new_tangent =
500 normalize(vec3_from_vec4(self.halfedge_tangent[first]) - vec3_from_vec4(self.halfedge_tangent[second]));
501 let pos = self.vert_pos[self.halfedge[first].start_vert as usize];
502 self.halfedge_tangent[first] =
503 circular_tangent(new_tangent, self.vert_pos[self.halfedge[first].end_vert as usize] - pos);
504 self.halfedge_tangent[second] =
505 circular_tangent(-new_tangent, self.vert_pos[self.halfedge[second].end_vert as usize] - pos);
506
507 let mut smoothness = (vert[0].1.smoothness + vert[1].0.smoothness) / 2.0;
508 for current in collect_vertex_cycle(self, first) {
509 if current == second {
510 smoothness = (vert[1].1.smoothness + vert[0].0.smoothness) / 2.0;
511 } else if current != first && !self.is_marked_inside_quad(current) {
512 self.sharpen_tangent(current, smoothness);
513 }
514 }
515 } else {
516 let mut smoothness = 0.0;
517 let mut denom = 0.0;
518 for pair in vert {
519 smoothness += pair.0.smoothness + pair.1.smoothness;
520 denom += if pair.0.smoothness == 0.0 { 0.0 } else { 1.0 };
521 denom += if pair.1.smoothness == 0.0 { 0.0 } else { 1.0 };
522 }
523 if denom > 0.0 {
524 smoothness /= denom;
525 }
526
527 for current in collect_vertex_cycle(self, vert[0].0.halfedge) {
528 if !self.is_marked_inside_quad(current) {
529 let pair = self.halfedge[current].paired_halfedge as usize;
530 let s = if tri_is_flat_face[current / 3] || tri_is_flat_face[pair / 3] {
531 0.0
532 } else {
533 smoothness
534 };
535 self.sharpen_tangent(current, s);
536 }
537 }
538 }
539 }
540
541 self.linearize_flat_tangents();
542 self.distribute_tangents(&fixed_halfedge);
543 }
544}