1use std::collections::{BTreeMap, HashMap, HashSet};
7
8use anyhow::{anyhow, bail, Result};
9
10use super::brep::{
11 candidates, cross, dot, eval, grow, norm, sub, Brep, Conv, Curve, Kind, Surface, P3,
12};
13use crate::mesh::{Mesh, MAX_TRIANGLES};
14
15const MAX_LOOP: usize = 100_000;
17const WELD_REL: f64 = 1e-6;
19const SNAP_REL: f64 = 1e-3;
22const H: f64 = 1e-4;
24
25fn is_constant(vals: impl Iterator<Item = f64>) -> bool {
26 let mut first = None;
27 for v in vals {
28 match first {
29 None => first = Some(v),
30 Some(f) if (f - v).abs() > 1e-9 => return false,
31 _ => {}
32 }
33 }
34 first.is_some()
35}
36
37pub fn add_twins(brep: &mut Brep) {
41 let ids: HashSet<i64> = brep.surfaces.iter().map(|s| s.id).collect();
42 let mut missing: Vec<i64> = brep
43 .curves
44 .iter()
45 .flat_map(|c| c.faces)
46 .filter(|g| !ids.contains(g))
47 .collect::<HashSet<_>>()
48 .into_iter()
49 .collect();
50 missing.sort_unstable();
51 let by_id: HashMap<i64, usize> = brep
52 .surfaces
53 .iter()
54 .enumerate()
55 .map(|(i, s)| (s.id, i))
56 .collect();
57 let mut twins = Vec::new();
58 for g in missing {
59 let mut partner: Option<usize> = None;
60 for c in &brep.curves {
61 if !c.faces.contains(&g) || !(c.ty == 1 || c.ty == 8) {
62 continue;
63 }
64 let side = usize::from(c.faces[0] != g);
65 let other = c.face(1 - side);
66 let Some(&oi) = by_id.get(&other) else {
67 continue;
68 };
69 if brep.surfaces.get(oi).is_some_and(|s| {
70 matches!(s.kind, Kind::Plane | Kind::Other) || s.kind.is_freeform()
71 }) {
72 continue;
73 }
74 if is_constant(c.pts.iter().map(|r| if side == 0 { r[0] } else { r[2] })) {
75 partner = Some(oi);
76 break;
77 }
78 }
79 let Some(pi) = partner else { continue };
80 let Some(p) = brep.surfaces.get(pi) else {
81 continue;
82 };
83 let mut tw = p.clone();
84 tw.id = g;
85 tw.twin_of = Some(p.id);
86 tw.contours = twin_contours(brep, g);
87 twins.push(tw);
88 }
89 brep.surfaces.extend(twins);
90}
91
92fn twin_contours(brep: &Brep, g: i64) -> Vec<(i64, i64)> {
94 let on_g: HashMap<i64, (usize, usize)> = brep
95 .curves
96 .iter()
97 .enumerate()
98 .filter_map(|(ci, c)| {
99 let side = if c.faces[0] == g {
100 0
101 } else if c.faces[1] == g {
102 1
103 } else {
104 return None;
105 };
106 Some((c.id, (ci, side)))
107 })
108 .collect();
109 let mut starts = Vec::new();
110 let mut seen = HashSet::new();
111 let mut ids_sorted: Vec<i64> = on_g.keys().copied().collect();
112 ids_sorted.sort_unstable();
113 for cid0 in ids_sorted {
114 if seen.contains(&cid0) {
115 continue;
116 }
117 let mut cid = cid0;
118 for _ in 0..MAX_LOOP {
119 let Some(&(ci, side)) = on_g.get(&cid) else {
120 break;
121 };
122 if !seen.insert(cid) {
123 break;
124 }
125 cid = brep.curves.get(ci).map_or(cid, |c| c.next_on(side));
126 }
127 starts.push(cid0);
128 }
129 starts
130 .iter()
131 .enumerate()
132 .map(|(k, &s)| (s, if k == 0 { 1 } else { -1 }))
133 .collect()
134}
135
136pub fn repair_twins(brep: &mut Brep, fixed: &HashMap<i64, Conv>) -> usize {
140 let donors: Vec<usize> = brep
141 .surfaces
142 .iter()
143 .enumerate()
144 .filter(|(_, s)| {
145 s.twin_of.is_none() && matches!(s.kind, Kind::Cylinder | Kind::Cone | Kind::Torus)
146 })
147 .map(|(i, _)| i)
148 .collect();
149 let by_id: HashMap<i64, usize> = brep
150 .surfaces
151 .iter()
152 .enumerate()
153 .map(|(i, s)| (s.id, i))
154 .collect();
155 let mut changes: Vec<(usize, usize)> = Vec::new();
156 for (ti, tw) in brep.surfaces.iter().enumerate() {
157 if tw.twin_of.is_none() || fixed.contains_key(&tw.id) {
158 continue;
159 }
160 let g = tw.id;
161 let mut samples: Vec<([f64; 2], P3)> = Vec::new();
162 for c in &brep.curves {
163 for sd in 0..2 {
164 if c.face(sd) != g {
165 continue;
166 }
167 let other = c.face(1 - sd);
168 let (Some(oconv), Some(&oi)) = (fixed.get(&other), by_id.get(&other)) else {
169 continue;
170 };
171 let Some(os) = brep.surfaces.get(oi) else {
172 continue;
173 };
174 for r in &c.pts {
175 if r.iter().all(|x| x.is_finite()) {
176 let (um, vm, uo, vo) = if sd == 0 {
177 (r[0], r[1], r[2], r[3])
178 } else {
179 (r[2], r[3], r[0], r[1])
180 };
181 let p = eval(os, oconv, uo, vo);
182 if p.iter().all(|x| x.is_finite()) {
183 samples.push(([um, vm], p));
184 }
185 }
186 }
187 }
188 }
189 if samples.is_empty() {
190 continue;
191 }
192 let mut best: Option<(f64, usize)> = None;
193 for &di in &donors {
194 let Some(d) = brep.surfaces.get(di) else {
195 continue;
196 };
197 for cand in candidates(d.kind) {
198 let mut errs: Vec<f64> = samples
199 .iter()
200 .map(|(uv, p)| norm(sub(eval(d, &cand, uv[0], uv[1]), *p)))
201 .filter(|e| e.is_finite())
202 .collect();
203 if errs.is_empty() {
204 continue;
205 }
206 errs.sort_by(|a, b| a.total_cmp(b));
207 let med = errs.get(errs.len() / 2).copied().unwrap_or(f64::INFINITY);
208 if best.is_none_or(|(e, _)| med < e) {
209 best = Some((med, di));
210 }
211 }
212 }
213 if let Some((_, di)) = best {
214 if brep.surfaces.get(di).map(|d| d.id) != tw.twin_of {
215 changes.push((ti, di));
216 }
217 }
218 }
219 for &(ti, di) in &changes {
220 let donor = brep.surfaces.get(di).cloned();
221 if let (Some(d), Some(tw)) = (donor, brep.surfaces.get_mut(ti)) {
222 tw.kind = d.kind;
223 tw.kind_name = d.kind_name.clone();
224 tw.orient = d.orient;
225 tw.frame = d.frame;
226 tw.params = d.params;
227 tw.freeform = d.freeform.clone();
228 tw.twin_of = Some(d.id);
229 }
230 }
231 changes.len()
232}
233
234#[derive(Clone, Copy)]
237struct LoopPt {
238 uv: [f64; 2],
239 xyz: P3,
240}
241
242fn edge_points(
246 brep: &Brep,
247 fixed: &HashMap<i64, Conv>,
248 by_id: &HashMap<i64, usize>,
249 agree: f64,
250) -> HashMap<i64, Vec<P3>> {
251 let mut out: HashMap<i64, Vec<P3>> = HashMap::new();
252 let mut rank: HashMap<i64, u8> = HashMap::new();
253 for c in &brep.curves {
254 let sides: Vec<(usize, &Surface)> = (0..2)
255 .filter_map(|sd| {
256 by_id
257 .get(&c.face(sd))
258 .and_then(|&i| brep.surfaces.get(i))
259 .map(|s| (sd, s))
260 })
261 .collect();
262 let Some(&(sd, s)) = sides.iter().min_by_key(|(sd, s)| (s.kind.rank(), *sd)) else {
263 continue;
264 };
265 let Some(conv) = fixed.get(&s.id) else {
266 continue;
267 };
268 let pts: Vec<P3> = c
269 .pts
270 .iter()
271 .map(|r| {
272 if sd == 0 {
273 eval(s, conv, r[0], r[1])
274 } else {
275 eval(s, conv, r[2], r[3])
276 }
277 })
278 .collect();
279 if sides.len() == 2 {
280 let Some(&(osd, os)) = sides.get(1 - sd) else {
281 continue;
282 };
283 if let Some(oconv) = fixed.get(&os.id) {
284 let mut disagree = false;
285 for (r, p) in c.pts.iter().zip(&pts) {
286 let q = if osd == 0 {
287 eval(os, oconv, r[0], r[1])
288 } else {
289 eval(os, oconv, r[2], r[3])
290 };
291 let d = norm(sub(*p, q));
292 if d.is_nan() || d > agree {
293 disagree = true;
294 break;
295 }
296 }
297 if disagree {
298 continue;
299 }
300 }
301 }
302 rank.insert(c.id, s.kind.rank());
303 out.insert(c.id, pts);
304 }
305 let cmap: HashMap<i64, &Curve> = brep.curves.iter().map(|c| (c.id, c)).collect();
307 let mut parent: HashMap<(i64, bool), (i64, bool)> = HashMap::new();
308 fn find(parent: &mut HashMap<(i64, bool), (i64, bool)>, mut x: (i64, bool)) -> (i64, bool) {
309 let mut path = Vec::new();
310 while let Some(&p) = parent.get(&x) {
311 if p == x {
312 break;
313 }
314 path.push(x);
315 x = p;
316 }
317 for q in path {
318 parent.insert(q, x);
319 }
320 x
321 }
322 for s in &brep.surfaces {
323 for &(start, _) in &s.contours {
324 let mut cid = start;
325 let mut prev: Option<(i64, bool)> = None;
326 let mut first: Option<(i64, bool)> = None;
327 for _ in 0..MAX_LOOP {
328 let Some(c) = cmap.get(&cid) else { break };
329 let side = if c.faces[0] == s.id {
330 0
331 } else if c.faces[1] == s.id {
332 1
333 } else {
334 break;
335 };
336 if !out.contains_key(&cid) {
337 break;
338 }
339 let (head, tail) = if c.dir_on(side) == -1 {
341 ((cid, true), (cid, false))
342 } else {
343 ((cid, false), (cid, true))
344 };
345 parent.entry(head).or_insert(head);
346 parent.entry(tail).or_insert(tail);
347 if let Some(p) = prev {
348 let (ra, rb) = (find(&mut parent, p), find(&mut parent, head));
349 parent.insert(ra, rb);
350 }
351 first.get_or_insert(head);
352 prev = Some(tail);
353 cid = c.next_on(side);
354 if cid == start {
355 if let (Some(p), Some(f)) = (prev, first) {
356 let (ra, rb) = (find(&mut parent, p), find(&mut parent, f));
357 parent.insert(ra, rb);
358 }
359 break;
360 }
361 }
362 }
363 }
364 let keys: Vec<(i64, bool)> = parent.keys().copied().collect();
365 let mut groups: HashMap<(i64, bool), Vec<(i64, bool)>> = HashMap::new();
366 for k in keys {
367 let r = find(&mut parent, k);
368 groups.entry(r).or_default().push(k);
369 }
370 for members in groups.values() {
371 let best = members
372 .iter()
373 .filter_map(|&(cid, end)| {
374 let pts = out.get(&cid)?;
375 let p = if end { *pts.last()? } else { *pts.first()? };
376 p.iter().all(|x| x.is_finite()).then_some((
377 rank.get(&cid).copied().unwrap_or(9),
378 cid,
379 p,
380 ))
381 })
382 .min_by(|a, b| (a.0, a.1).cmp(&(b.0, b.1)));
383 let Some((_, _, p)) = best else { continue };
384 for &(cid, end) in members {
385 if let Some(pts) = out.get_mut(&cid) {
386 if end {
387 if let Some(l) = pts.last_mut() {
388 *l = p;
389 }
390 } else if let Some(f) = pts.first_mut() {
391 *f = p;
392 }
393 }
394 }
395 }
396 out
397}
398
399fn loops(
401 s: &Surface,
402 curves: &HashMap<i64, &Curve>,
403 e3: &HashMap<i64, Vec<P3>>,
404) -> Vec<(Vec<LoopPt>, i64)> {
405 let mut out = Vec::new();
406 for &(start, trv) in &s.contours {
407 let mut pts: Vec<LoopPt> = Vec::new();
408 let mut cid = start;
409 let mut ok = true;
410 for _ in 0..MAX_LOOP {
411 let Some(c) = curves.get(&cid) else {
412 ok = false;
413 break;
414 };
415 let side = if c.faces[0] == s.id {
416 0
417 } else if c.faces[1] == s.id {
418 1
419 } else {
420 ok = false;
421 break;
422 };
423 let snapped = e3.get(&cid);
424 let mut seg: Vec<LoopPt> = c
425 .pts
426 .iter()
427 .enumerate()
428 .map(|(k, r)| LoopPt {
429 uv: if side == 0 {
430 [r[0], r[1]]
431 } else {
432 [r[2], r[3]]
433 },
434 xyz: snapped
435 .and_then(|v| v.get(k))
436 .copied()
437 .unwrap_or([f64::NAN; 3]),
438 })
439 .filter(|p| p.uv[0].is_finite() && p.uv[1].is_finite())
440 .collect();
441 if c.dir_on(side) == -1 {
442 seg.reverse();
443 }
444 pts.extend(seg);
445 cid = c.next_on(side);
446 if cid == start {
447 break;
448 }
449 }
450 if !ok || pts.len() < 3 {
451 continue;
452 }
453 let mut dedup: Vec<LoopPt> = Vec::with_capacity(pts.len());
454 for p in pts {
455 if dedup
456 .last()
457 .is_none_or(|q| (q.uv[0] - p.uv[0]).hypot(q.uv[1] - p.uv[1]) > 1e-9)
458 {
459 dedup.push(p);
460 }
461 }
462 if dedup.len() > 1 {
463 let closed = match (dedup.first(), dedup.last()) {
464 (Some(f), Some(l)) => (f.uv[0] - l.uv[0]).hypot(f.uv[1] - l.uv[1]) < 1e-9,
465 _ => false,
466 };
467 if closed {
468 dedup.pop();
469 }
470 }
471 if dedup.len() >= 3 {
472 out.push((dedup, trv));
473 }
474 }
475 out
476}
477
478fn area2(poly: &[[f64; 2]], scale: [f64; 2]) -> f64 {
479 let n = poly.len();
480 let mut s = 0.0;
481 for i in 0..n {
482 let a = poly.get(i).copied().unwrap_or_default();
483 let b = poly.get((i + 1) % n).copied().unwrap_or_default();
484 s += a[0] * scale[0] * b[1] * scale[1] - b[0] * scale[0] * a[1] * scale[1];
485 }
486 0.5 * s
487}
488
489fn point_in_poly(p: [f64; 2], poly: &[[f64; 2]]) -> bool {
490 let n = poly.len();
491 let mut inside = false;
492 for i in 0..n {
493 let a = poly.get(i).copied().unwrap_or_default();
494 let b = poly.get((i + 1) % n).copied().unwrap_or_default();
495 if (a[1] > p[1]) != (b[1] > p[1]) {
496 let x = a[0] + (p[1] - a[1]) * (b[0] - a[0]) / (b[1] - a[1]);
497 if x > p[0] {
498 inside = !inside;
499 }
500 }
501 }
502 inside
503}
504
505pub struct Coarse {
508 pub points: Vec<P3>,
509 pub normals: Vec<P3>,
510 pub triangles: Vec<[u32; 3]>,
511}
512
513pub fn tessellate(brep: &Brep, fixed: &HashMap<i64, Conv>) -> Result<Coarse> {
515 let curves: HashMap<i64, &Curve> = brep.curves.iter().map(|c| (c.id, c)).collect();
516 let by_id: HashMap<i64, usize> = brep
517 .surfaces
518 .iter()
519 .enumerate()
520 .map(|(i, s)| (s.id, i))
521 .collect();
522 let rough = edge_points(brep, fixed, &by_id, f64::INFINITY);
524 let mut lo = [f64::INFINITY; 3];
525 let mut hi = [f64::NEG_INFINITY; 3];
526 for p in rough.values().flatten() {
527 if p.iter().all(|x| x.is_finite()) {
528 grow(&mut lo, &mut hi, *p);
529 }
530 }
531 let size = if lo[0].is_finite() {
532 norm(sub(hi, lo)).max(1e-9)
533 } else {
534 1.0
535 };
536 let e3 = edge_points(brep, fixed, &by_id, SNAP_REL * size);
537 let mut out = Coarse {
538 points: Vec::new(),
539 normals: Vec::new(),
540 triangles: Vec::new(),
541 };
542 for s in &brep.surfaces {
543 if s.kind == Kind::Other {
544 continue;
545 }
546 let conv = fixed
547 .get(&s.id)
548 .copied()
549 .or_else(|| candidates(s.kind).first().copied())
550 .unwrap_or(Conv::IDENTITY);
551 let polys = loops(s, &curves, &e3);
552 if polys.is_empty() {
553 continue;
554 }
555 let scale = match s.kind {
557 Kind::Cylinder => [s.params[0].abs().max(1e-9), 1.0],
558 Kind::Torus => [
559 (s.params[0].abs() + s.params[1].abs()).max(1e-9),
560 s.params[1].abs().max(1e-9),
561 ],
562 Kind::Cone => [(s.params[0].tan().abs() * 0.5).max(1e-9), 1.0],
563 _ => [1.0, 1.0],
564 };
565 let uv_of = |p: &Vec<LoopPt>| -> Vec<[f64; 2]> { p.iter().map(|q| q.uv).collect() };
566 let mut outers: Vec<&Vec<LoopPt>> = polys
567 .iter()
568 .filter(|(_, t)| *t == 1)
569 .map(|(p, _)| p)
570 .collect();
571 let mut holes: Vec<&Vec<LoopPt>> = polys
572 .iter()
573 .filter(|(_, t)| *t != 1)
574 .map(|(p, _)| p)
575 .collect();
576 if outers.is_empty() {
577 let Some((k, _)) = polys.iter().enumerate().max_by(|a, b| {
578 area2(&uv_of(&a.1 .0), scale)
579 .abs()
580 .total_cmp(&area2(&uv_of(&b.1 .0), scale).abs())
581 }) else {
582 continue;
583 };
584 outers = polys.get(k).map(|(p, _)| vec![p]).unwrap_or_default();
585 holes = polys
586 .iter()
587 .enumerate()
588 .filter(|(i, _)| *i != k)
589 .map(|(_, (p, _))| p)
590 .collect();
591 }
592 let flip = s.orient == -1;
593 for outer in outers {
594 let mut ring: Vec<LoopPt> = outer.clone();
595 if area2(&uv_of(&ring), scale) < 0.0 {
596 ring.reverse();
597 }
598 let ring_uv = uv_of(&ring);
599 let mut coords: Vec<f64> = Vec::new();
600 let mut pts: Vec<LoopPt> = Vec::new();
601 let mut hole_starts: Vec<usize> = Vec::new();
602 for p in &ring {
603 coords.extend([p.uv[0] * scale[0], p.uv[1] * scale[1]]);
604 pts.push(*p);
605 }
606 for h in &holes {
607 let Some(first) = h.first() else { continue };
608 if !point_in_poly(first.uv, &ring_uv) {
609 continue;
610 }
611 let mut hr: Vec<LoopPt> = (*h).clone();
612 if area2(&uv_of(&hr), scale) > 0.0 {
613 hr.reverse();
614 }
615 hole_starts.push(pts.len());
616 for p in &hr {
617 coords.extend([p.uv[0] * scale[0], p.uv[1] * scale[1]]);
618 pts.push(*p);
619 }
620 }
621 let pairs: Vec<[f64; 2]> = coords
622 .chunks_exact(2)
623 .filter_map(|c| <[f64; 2]>::try_from(c).ok())
624 .collect();
625 let tris: Vec<usize> = crate::mesh::earcut::earcut(&pairs, &hole_starts)
626 .into_iter()
627 .flatten()
628 .collect();
629 if tris.len() < 3 {
630 continue;
631 }
632 let tris = delaunay_flips(tris, &coords);
633 let signed: f64 = tris
634 .chunks(3)
635 .map(|t| {
636 let g = |k: usize| {
637 pts.get(t.get(k).copied().unwrap_or(usize::MAX))
638 .map(|p| p.uv)
639 .unwrap_or_default()
640 };
641 area2(&[g(0), g(1), g(2)], scale)
642 })
643 .sum();
644 let ccw = signed >= 0.0;
645 let base = u32::try_from(out.points.len()).map_err(|_| anyhow!("too many vertices"))?;
646 for p in &pts {
647 let [u, v] = p.uv;
648 let xyz = if p.xyz.iter().all(|x| x.is_finite()) {
649 p.xyz
650 } else {
651 eval(s, &conv, u, v)
652 };
653 let du = sub(eval(s, &conv, u + H, v), eval(s, &conv, u - H, v));
654 let dv = sub(eval(s, &conv, u, v + H), eval(s, &conv, u, v - H));
655 let mut n = cross(du, dv);
656 let l = norm(n);
657 n = if l > 1e-18 && n.iter().all(|x| x.is_finite()) {
658 n.map(|x| x / l)
659 } else {
660 [0.0; 3]
661 };
662 if flip {
663 n = n.map(|x| -x);
664 }
665 if !xyz.iter().all(|x| x.is_finite()) {
666 bail!("face {} evaluates to NaN", s.id);
667 }
668 out.points.push(xyz);
669 out.normals.push(n);
670 }
671 for t in tris.chunks(3) {
672 let (Some(&a), Some(&b), Some(&c)) = (t.first(), t.get(1), t.get(2)) else {
673 continue;
674 };
675 let (a, b, c) = (u32::try_from(a)?, u32::try_from(b)?, u32::try_from(c)?);
676 let tri = if ccw != flip {
677 [base + a, base + b, base + c]
678 } else {
679 [base + a, base + c, base + b]
680 };
681 out.triangles.push(tri);
682 if out.triangles.len() > MAX_TRIANGLES {
683 bail!("The model has more than {MAX_TRIANGLES} triangles – more than the preview shows.");
684 }
685 }
686 }
687 }
688 if out.triangles.is_empty() {
689 bail!("The Creo part contains no faces that could be tessellated.");
690 }
691 Ok(out)
692}
693
694fn delaunay_flips(tris: Vec<usize>, coords: &[f64]) -> Vec<usize> {
698 let n = tris.len() / 3;
699 let mut t: Vec<[usize; 3]> = tris
700 .chunks_exact(3)
701 .filter_map(|c| <[usize; 3]>::try_from(c).ok())
702 .collect();
703 let pt = |i: usize| -> [f64; 2] {
704 [
705 coords.get(2 * i).copied().unwrap_or(0.0),
706 coords.get(2 * i + 1).copied().unwrap_or(0.0),
707 ]
708 };
709 let in_circle = |a: [f64; 2], b: [f64; 2], c: [f64; 2], d: [f64; 2]| -> bool {
710 let (ax, ay) = (a[0] - d[0], a[1] - d[1]);
711 let (bx, by) = (b[0] - d[0], b[1] - d[1]);
712 let (cx, cy) = (c[0] - d[0], c[1] - d[1]);
713 let det = (ax * ax + ay * ay) * (bx * cy - cx * by)
714 - (bx * bx + by * by) * (ax * cy - cx * ay)
715 + (cx * cx + cy * cy) * (ax * by - bx * ay);
716 let orient = (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
717 if orient > 0.0 {
718 det > 1e-12
719 } else {
720 det < -1e-12
721 }
722 };
723 for _ in 0..n.clamp(1, 64) {
726 let mut edges: BTreeMap<(usize, usize), Vec<usize>> = BTreeMap::new();
727 for (ti, &[x, y, z]) in t.iter().enumerate() {
728 for (a, b) in [(x, y), (y, z), (z, x)] {
729 edges.entry((a.min(b), a.max(b))).or_default().push(ti);
730 }
731 }
732 let mut flipped = false;
733 let mut done: HashSet<usize> = HashSet::new();
734 for (&(a, b), owners) in &edges {
735 let [ti, tj] = owners.as_slice() else {
736 continue;
737 };
738 if done.contains(ti) || done.contains(tj) {
739 continue;
740 }
741 let (Some(&x), Some(&y)) = (t.get(*ti), t.get(*tj)) else {
742 continue;
743 };
744 let Some(&c) = x.iter().find(|&&v| v != a && v != b) else {
745 continue;
746 };
747 let Some(&d) = y.iter().find(|&&v| v != a && v != b) else {
748 continue;
749 };
750 if !in_circle(pt(a), pt(b), pt(c), pt(d)) {
751 continue;
752 }
753 let orient = |p: [f64; 2], q: [f64; 2], r: [f64; 2]| {
755 (q[0] - p[0]) * (r[1] - p[1]) - (q[1] - p[1]) * (r[0] - p[0])
756 };
757 let o_old = orient(pt(x[0]), pt(x[1]), pt(x[2]));
758 let new1 = [c, d, b];
759 let new2 = [d, c, a];
760 let (n1, n2) = (orient(pt(c), pt(d), pt(b)), orient(pt(d), pt(c), pt(a)));
761 if n1 * o_old <= 0.0 || n2 * o_old <= 0.0 {
762 continue;
763 }
764 if let Some(v) = t.get_mut(*ti) {
765 *v = new1;
766 }
767 if let Some(v) = t.get_mut(*tj) {
768 *v = new2;
769 }
770 done.insert(*ti);
771 done.insert(*tj);
772 flipped = true;
773 }
774 if !flipped {
775 break;
776 }
777 }
778 t.into_iter().flatten().collect()
779}
780
781pub fn build(coarse: &Coarse) -> Result<Mesh> {
784 let mut lo = [f64::INFINITY; 3];
785 let mut hi = [f64::NEG_INFINITY; 3];
786 for p in &coarse.points {
787 grow(&mut lo, &mut hi, *p);
788 }
789 let size = norm(sub(hi, lo)).max(1e-9);
790 let weld = WELD_REL * size;
791 let mut points: Vec<P3> = Vec::new();
792 let mut welded: HashMap<[i64; 3], u32> = HashMap::new();
793 let mut index: Vec<u32> = Vec::with_capacity(coarse.points.len());
794 for p in &coarse.points {
795 let grid = p.map(|c| (c / weld).round() as i64);
796 let next = u32::try_from(points.len()).map_err(|_| anyhow!("too many vertices"))?;
797 index.push(*welded.entry(grid).or_insert_with(|| {
798 points.push(*p);
799 next
800 }));
801 }
802 let mut triangles: Vec<[u32; 3]> = Vec::with_capacity(coarse.triangles.len());
803 let mut normal_triangles: Vec<[u32; 3]> = Vec::with_capacity(coarse.triangles.len());
804 for t in &coarse.triangles {
805 let w = t.map(|k| index.get(k as usize).copied().unwrap_or(u32::MAX));
806 if w[0] == w[1] || w[1] == w[2] || w[0] == w[2] || w.contains(&u32::MAX) {
807 continue;
808 }
809 triangles.push(w);
810 normal_triangles.push(*t);
811 }
812 let volume: f64 = triangles
813 .iter()
814 .map(|t| {
815 let g = |k: u32| points.get(k as usize).copied().unwrap_or_default();
816 dot(g(t[0]), cross(g(t[1]), g(t[2])))
817 })
818 .sum::<f64>()
819 / 6.0;
820 let mut normals = coarse.normals.clone();
821 if volume < 0.0 {
822 for t in &mut triangles {
823 t.swap(1, 2);
824 }
825 for t in &mut normal_triangles {
826 t.swap(1, 2);
827 }
828 for n in &mut normals {
829 *n = n.map(|x| -x);
830 }
831 }
832 let level = crate::mesh::pn::refine_level(triangles.len());
833 let (points, triangles) = if level > 1 {
834 crate::mesh::pn::refine(&points, &triangles, &normals, &normal_triangles, level)
835 } else {
836 (points, triangles)
837 };
838 if triangles.len() > MAX_TRIANGLES {
839 bail!("The model has more than {MAX_TRIANGLES} triangles – more than the preview shows.");
840 }
841 let mut mesh = Mesh {
842 parts: 1,
843 ..Mesh::default()
844 };
845 for p in &points {
846 mesh.vertex(*p);
847 }
848 mesh.indices.extend(triangles.iter().flatten());
849 Ok(mesh)
850}