1use axiolid_core::{Point2, Point3, Polygon2, Scalar, Tolerance, Triangle2, Triangle3};
26
27#[derive(Debug, Clone, Copy, PartialEq)]
29#[non_exhaustive]
30pub enum BarycentricError {
31 NonFinite,
33 Degenerate {
37 thickness: Scalar,
39 },
40 TooFewVertices {
42 count: usize,
44 },
45 ShortEdge {
48 index: usize,
50 },
51 SelfIntersecting {
54 first: usize,
56 second: usize,
58 },
59 Undefined,
62 NotConvex {
65 index: usize,
67 },
68 VertexIndex {
71 face: usize,
73 index: usize,
75 len: usize,
77 },
78}
79
80impl core::fmt::Display for BarycentricError {
81 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
82 match self {
83 Self::NonFinite => f.write_str("a corner or the query point is not finite"),
84 Self::Degenerate { thickness } => write!(
85 f,
86 "shape is {thickness} thick, not thicker than the linear tolerance"
87 ),
88 Self::TooFewVertices { count } => {
89 write!(f, "polygon has {count} vertices, need at least 3")
90 }
91 Self::ShortEdge { index } => {
92 write!(f, "polygon edge {index} is not longer than the tolerance")
93 }
94 Self::SelfIntersecting { first, second } => {
95 write!(f, "polygon edges {first} and {second} meet")
96 }
97 Self::Undefined => {
98 f.write_str("mean-value weights cancel here (outside a non-convex polygon)")
99 }
100 Self::NotConvex { index } => {
101 write!(f, "polygon is not convex at vertex {index}")
102 }
103 Self::VertexIndex { face, index, len } => {
104 write!(f, "face {face} names vertex {index}, but only {len} exist")
105 }
106 }
107 }
108}
109
110impl core::error::Error for BarycentricError {}
111
112fn area2(x: Point2, y: Point2, z: Point2) -> Scalar {
114 (y - x).perp_dot(z - x)
115}
116
117fn volume6(x: Point3, y: Point3, z: Point3, w: Point3) -> Scalar {
119 (y - x).dot((z - x).cross(w - x))
120}
121
122pub fn triangle_barycentric2(
134 triangle: &Triangle2,
135 point: Point2,
136 tolerance: Tolerance,
137) -> Result<[Scalar; 3], BarycentricError> {
138 let Triangle2 { a, b, c } = *triangle;
139 if ![a, b, c, point].iter().all(|p| p.is_finite()) {
140 return Err(BarycentricError::NonFinite);
141 }
142 let total = area2(a, b, c);
143 let longest = (b - a).length().max((c - b).length()).max((a - c).length());
144 thick_enough(total.abs(), longest, tolerance)?;
145 Ok([
146 area2(point, b, c) / total,
147 area2(a, point, c) / total,
148 area2(a, b, point) / total,
149 ])
150}
151
152pub fn triangle_barycentric3(
163 triangle: &Triangle3,
164 point: Point3,
165 tolerance: Tolerance,
166) -> Result<[Scalar; 3], BarycentricError> {
167 let Triangle3 { a, b, c } = *triangle;
168 if ![a, b, c, point].iter().all(|p| p.is_finite()) {
169 return Err(BarycentricError::NonFinite);
170 }
171 let normal = (b - a).cross(c - a);
172 let longest = (b - a).length().max((c - b).length()).max((a - c).length());
173 thick_enough(normal.length(), longest, tolerance)?;
174 let total = normal.dot(normal);
175 Ok([
176 normal.dot((b - point).cross(c - point)) / total,
177 normal.dot((c - point).cross(a - point)) / total,
178 normal.dot((a - point).cross(b - point)) / total,
179 ])
180}
181
182pub fn tetrahedron_barycentric(
194 corners: [Point3; 4],
195 point: Point3,
196 tolerance: Tolerance,
197) -> Result<[Scalar; 4], BarycentricError> {
198 let [a, b, c, d] = corners;
199 if ![a, b, c, d, point].iter().all(|p| p.is_finite()) {
200 return Err(BarycentricError::NonFinite);
201 }
202 let total = volume6(a, b, c, d);
203 let largest_face = [(b, c, d), (a, c, d), (a, b, d), (a, b, c)]
205 .iter()
206 .map(|&(x, y, z)| (y - x).cross(z - x).length())
207 .fold(0.0, Scalar::max);
208 thick_enough(total.abs(), largest_face, tolerance)?;
209 Ok([
210 volume6(point, b, c, d) / total,
211 volume6(a, point, c, d) / total,
212 volume6(a, b, point, d) / total,
213 volume6(a, b, c, point) / total,
214 ])
215}
216
217fn thick_enough(
220 measure: Scalar,
221 base: Scalar,
222 tolerance: Tolerance,
223) -> Result<(), BarycentricError> {
224 let thickness = if base > 0.0 { measure / base } else { 0.0 };
225 if thickness > tolerance.linear() {
226 Ok(())
227 } else {
228 Err(BarycentricError::Degenerate { thickness })
229 }
230}
231
232pub fn mean_value_coordinates2(
253 polygon: &Polygon2,
254 point: Point2,
255 tolerance: Tolerance,
256) -> Result<Vec<Scalar>, BarycentricError> {
257 let v = &polygon.vertices;
258 let n = v.len();
259 if !point.is_finite() || !v.iter().all(|p| p.is_finite()) {
260 return Err(BarycentricError::NonFinite);
261 }
262 if n < 3 {
263 return Err(BarycentricError::TooFewVertices { count: n });
264 }
265 check_simple(v, tolerance)?;
266 let perimeter: Scalar = (0..n).map(|i| (v[(i + 1) % n] - v[i]).length()).sum();
267 thick_enough(2.0 * polygon.signed_area().abs(), perimeter, tolerance)?;
268
269 let linear = tolerance.linear();
270 let mut weights = vec![0.0; n];
271 let s: Vec<Point2> = v.iter().map(|&q| q - point).collect();
273 let r: Vec<Scalar> = s.iter().map(|q| q.length()).collect();
274 if let Some(i) = (0..n)
275 .filter(|&i| r[i] <= linear)
276 .min_by(|&i, &j| r[i].total_cmp(&r[j]))
277 {
278 weights[i] = 1.0;
279 return Ok(weights);
280 }
281 for i in 0..n {
283 let j = (i + 1) % n;
284 let edge = v[j] - v[i];
285 let length = edge.length();
286 let t = (point - v[i]).dot(edge) / (length * length);
287 if (0.0..=1.0).contains(&t) && s[i].perp_dot(s[j]).abs() / length <= linear {
288 weights[i] = 1.0 - t;
289 weights[j] = t;
290 return Ok(weights);
291 }
292 }
293 let half_tangent: Vec<Scalar> = (0..n)
297 .map(|i| {
298 let j = (i + 1) % n;
299 s[i].perp_dot(s[j]) / (r[i] * r[j] + s[i].dot(s[j]))
300 })
301 .collect();
302 let mut sum = 0.0;
303 let mut magnitude = 0.0;
304 for i in 0..n {
305 let w = (half_tangent[(i + n - 1) % n] + half_tangent[i]) / r[i];
306 weights[i] = w;
307 sum += w;
308 magnitude += w.abs();
309 }
310 if !sum.is_finite() || sum.abs() <= Scalar::EPSILON * n as Scalar * magnitude {
311 return Err(BarycentricError::Undefined);
312 }
313 for w in &mut weights {
314 *w /= sum;
315 }
316 Ok(weights)
317}
318
319fn check_simple(v: &[Point2], tolerance: Tolerance) -> Result<(), BarycentricError> {
328 let n = v.len();
329 let linear = tolerance.linear();
330 let edge = |i: usize| (v[i], v[(i + 1) % n]);
331 for i in 0..n {
332 let (p, q) = edge(i);
333 if (q - p).length() <= linear {
334 return Err(BarycentricError::ShortEdge { index: i });
335 }
336 }
337 for i in 0..n {
338 let last = if i == 0 { n - 1 } else { n };
341 for j in i + 2..last {
342 let (p, q) = edge(i);
343 let (r, s) = edge(j);
344 if segment_distance(p, q, r, s) <= linear {
345 return Err(BarycentricError::SelfIntersecting {
346 first: i,
347 second: j,
348 });
349 }
350 }
351 }
352 Ok(())
353}
354
355fn segment_distance(p: Point2, q: Point2, r: Point2, s: Point2) -> Scalar {
357 let o1 = area2(p, q, r);
358 let o2 = area2(p, q, s);
359 let o3 = area2(r, s, p);
360 let o4 = area2(r, s, q);
361 if o1 * o2 < 0.0 && o3 * o4 < 0.0 {
362 return 0.0;
363 }
364 point_segment(r, p, q)
365 .min(point_segment(s, p, q))
366 .min(point_segment(p, r, s))
367 .min(point_segment(q, r, s))
368}
369
370fn point_segment(x: Point2, p: Point2, q: Point2) -> Scalar {
371 let d = q - p;
372 let t = ((x - p).dot(d) / d.dot(d)).clamp(0.0, 1.0);
373 (p + d * t - x).length()
374}
375
376fn vertex_or_edge_weights(
382 v: &[Point2],
383 point: Point2,
384 tolerance: Tolerance,
385) -> Option<Vec<Scalar>> {
386 let n = v.len();
387 let linear = tolerance.linear();
388 let mut weights = vec![0.0; n];
389 let r: Vec<Scalar> = v.iter().map(|&q| (q - point).length()).collect();
390 if let Some(i) = (0..n)
391 .filter(|&i| r[i] <= linear)
392 .min_by(|&i, &j| r[i].total_cmp(&r[j]))
393 {
394 weights[i] = 1.0;
395 return Some(weights);
396 }
397 for i in 0..n {
398 let j = (i + 1) % n;
399 let edge = v[j] - v[i];
400 let length = edge.length();
401 let t = (point - v[i]).dot(edge) / (length * length);
402 let s_i = v[i] - point;
403 let s_j = v[j] - point;
404 if (0.0..=1.0).contains(&t) && s_i.perp_dot(s_j).abs() / length <= linear {
405 weights[i] = 1.0 - t;
406 weights[j] = t;
407 return Some(weights);
408 }
409 }
410 None
411}
412
413fn check_convex(v: &[Point2], tolerance: Tolerance) -> Result<(), BarycentricError> {
422 let n = v.len();
423 let signed_area: Scalar = (0..n)
424 .map(|i| v[i].perp_dot(v[(i + 1) % n]))
425 .sum::<Scalar>()
426 * 0.5;
427 let orientation = if signed_area >= 0.0 { 1.0 } else { -1.0 };
428 let perimeter: Scalar = (0..n).map(|i| (v[(i + 1) % n] - v[i]).length()).sum();
429 let threshold = tolerance.linear() * perimeter;
430 for i in 0..n {
431 let prev = v[(i + n - 1) % n];
432 let next = v[(i + 1) % n];
433 let turn = area2(prev, v[i], next);
434 if turn * orientation < -threshold {
435 return Err(BarycentricError::NotConvex { index: i });
436 }
437 }
438 Ok(())
439}
440
441pub fn wachspress_coordinates2(
463 polygon: &Polygon2,
464 point: Point2,
465 tolerance: Tolerance,
466) -> Result<Vec<Scalar>, BarycentricError> {
467 let v = &polygon.vertices;
468 let n = v.len();
469 if !point.is_finite() || !v.iter().all(|p| p.is_finite()) {
470 return Err(BarycentricError::NonFinite);
471 }
472 if n < 3 {
473 return Err(BarycentricError::TooFewVertices { count: n });
474 }
475 check_simple(v, tolerance)?;
476 let perimeter: Scalar = (0..n).map(|i| (v[(i + 1) % n] - v[i]).length()).sum();
477 thick_enough(2.0 * polygon.signed_area().abs(), perimeter, tolerance)?;
478 check_convex(v, tolerance)?;
479 if let Some(w) = vertex_or_edge_weights(v, point, tolerance) {
480 return Ok(w);
481 }
482
483 let a: Vec<Scalar> = (0..n).map(|i| area2(v[i], v[(i + 1) % n], point)).collect();
484 let mut weights = vec![0.0; n];
485 let mut sum = 0.0;
486 let mut magnitude = 0.0;
487 for i in 0..n {
488 let prev = (i + n - 1) % n;
489 let c = area2(v[prev], v[i], v[(i + 1) % n]);
490 let w = c / (a[prev] * a[i]);
491 weights[i] = w;
492 sum += w;
493 magnitude += w.abs();
494 }
495 if !sum.is_finite() || sum.abs() <= Scalar::EPSILON * n as Scalar * magnitude {
496 return Err(BarycentricError::Undefined);
497 }
498 for w in &mut weights {
499 *w /= sum;
500 }
501 Ok(weights)
502}
503
504pub fn discrete_harmonic_coordinates2(
526 polygon: &Polygon2,
527 point: Point2,
528 tolerance: Tolerance,
529) -> Result<Vec<Scalar>, BarycentricError> {
530 let v = &polygon.vertices;
531 let n = v.len();
532 if !point.is_finite() || !v.iter().all(|p| p.is_finite()) {
533 return Err(BarycentricError::NonFinite);
534 }
535 if n < 3 {
536 return Err(BarycentricError::TooFewVertices { count: n });
537 }
538 check_simple(v, tolerance)?;
539 let perimeter: Scalar = (0..n).map(|i| (v[(i + 1) % n] - v[i]).length()).sum();
540 thick_enough(2.0 * polygon.signed_area().abs(), perimeter, tolerance)?;
541 check_convex(v, tolerance)?;
542 if let Some(w) = vertex_or_edge_weights(v, point, tolerance) {
543 return Ok(w);
544 }
545
546 let mut weights = vec![0.0; n];
547 let mut sum = 0.0;
548 let mut magnitude = 0.0;
549 for i in 0..n {
550 let u = point - v[i];
551 let prev = v[(i + n - 1) % n] - v[i];
552 let next = v[(i + 1) % n] - v[i];
553 let cot_prev = u.dot(prev) / u.perp_dot(prev).abs();
554 let cot_next = u.dot(next) / u.perp_dot(next).abs();
555 let r2 = u.length_squared();
556 let w = (cot_prev + cot_next) / r2;
557 weights[i] = w;
558 sum += w;
559 magnitude += w.abs();
560 }
561 if !sum.is_finite() || sum.abs() <= Scalar::EPSILON * n as Scalar * magnitude {
562 return Err(BarycentricError::Undefined);
563 }
564 for w in &mut weights {
565 *w /= sum;
566 }
567 Ok(weights)
568}
569
570pub fn mean_value_coordinates3(
594 vertices: &[Point3],
595 faces: &[[usize; 3]],
596 point: Point3,
597 tolerance: Tolerance,
598) -> Result<Vec<Scalar>, BarycentricError> {
599 let n = vertices.len();
600 if !point.is_finite() || !vertices.iter().all(|p| p.is_finite()) {
601 return Err(BarycentricError::NonFinite);
602 }
603 if n < 3 || faces.is_empty() {
604 return Err(BarycentricError::TooFewVertices { count: n });
605 }
606 for (f, face) in faces.iter().enumerate() {
607 for &index in face {
608 if index >= n {
609 return Err(BarycentricError::VertexIndex {
610 face: f,
611 index,
612 len: n,
613 });
614 }
615 }
616 }
617
618 let linear = tolerance.linear();
619 let d: Vec<Scalar> = vertices.iter().map(|&q| (q - point).length()).collect();
620 if let Some(i) = (0..n)
621 .filter(|&i| d[i] <= linear)
622 .min_by(|&i, &j| d[i].total_cmp(&d[j]))
623 {
624 let mut weights = vec![0.0; n];
625 weights[i] = 1.0;
626 return Ok(weights);
627 }
628 let u: Vec<Point3> = vertices
629 .iter()
630 .zip(&d)
631 .map(|(&q, &r)| (q - point) / r)
632 .collect();
633
634 let mut weights = vec![0.0; n];
635 for &[fi, fj, fk] in faces {
636 let (ui, uj, uk) = (u[fi], u[fj], u[fk]);
637 let l = [(uj - uk).length(), (uk - ui).length(), (ui - uj).length()];
638 let theta = [
639 2.0 * (l[0] / 2.0).clamp(-1.0, 1.0).asin(),
640 2.0 * (l[1] / 2.0).clamp(-1.0, 1.0).asin(),
641 2.0 * (l[2] / 2.0).clamp(-1.0, 1.0).asin(),
642 ];
643 let h = (theta[0] + theta[1] + theta[2]) / 2.0;
644 if core::f64::consts::PI - h < linear {
645 let tri = Triangle3 {
648 a: vertices[fi],
649 b: vertices[fj],
650 c: vertices[fk],
651 };
652 let w = triangle_barycentric3(&tri, point, tolerance)?;
653 let mut weights = vec![0.0; n];
654 weights[fi] = w[0];
655 weights[fj] = w[1];
656 weights[fk] = w[2];
657 return Ok(weights);
658 }
659 let sin_theta = [theta[0].sin(), theta[1].sin(), theta[2].sin()];
660 if sin_theta.iter().any(|s| s.abs() <= Scalar::EPSILON) {
661 continue;
664 }
665 let c = [
666 2.0 * h.sin() * (h - theta[0]).sin() / (sin_theta[1] * sin_theta[2]) - 1.0,
667 2.0 * h.sin() * (h - theta[1]).sin() / (sin_theta[2] * sin_theta[0]) - 1.0,
668 2.0 * h.sin() * (h - theta[2]).sin() / (sin_theta[0] * sin_theta[1]) - 1.0,
669 ];
670 let sign = ui.dot(uj.cross(uk)).signum();
671 let mut degenerate = false;
672 let s: Vec<Scalar> = c
673 .iter()
674 .map(|&ci| {
675 let value = sign * (1.0 - ci * ci).max(0.0).sqrt();
676 if value.abs() <= Scalar::EPSILON {
677 degenerate = true;
678 }
679 value
680 })
681 .collect();
682 if degenerate {
683 continue;
686 }
687 weights[fi] +=
688 (theta[0] - c[1] * theta[2] - c[2] * theta[1]) / (d[fi] * sin_theta[1] * s[2]);
689 weights[fj] +=
690 (theta[1] - c[2] * theta[0] - c[0] * theta[2]) / (d[fj] * sin_theta[2] * s[0]);
691 weights[fk] +=
692 (theta[2] - c[0] * theta[1] - c[1] * theta[0]) / (d[fk] * sin_theta[0] * s[1]);
693 }
694 let sum: Scalar = weights.iter().sum();
695 let magnitude: Scalar = weights.iter().map(|w| w.abs()).sum();
696 if !sum.is_finite() || sum.abs() <= Scalar::EPSILON * n as Scalar * magnitude {
697 return Err(BarycentricError::Undefined);
698 }
699 for w in &mut weights {
700 *w /= sum;
701 }
702 Ok(weights)
703}