1#![allow(clippy::needless_range_loop)]
6use super::types::{
7 ArtGalleryResult, ConvexFace3D, ConvexHull3D, DelaunayTri, Line2D, Point2, VoronoiCell2D,
8};
9
10pub type Point3 = [f64; 3];
12pub fn sub3(a: Point3, b: Point3) -> Point3 {
14 [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
15}
16pub fn add3(a: Point3, b: Point3) -> Point3 {
18 [a[0] + b[0], a[1] + b[1], a[2] + b[2]]
19}
20pub fn scale3(a: Point3, t: f64) -> Point3 {
22 [a[0] * t, a[1] * t, a[2] * t]
23}
24pub fn dot3(a: Point3, b: Point3) -> f64 {
26 a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
27}
28pub fn cross3(a: Point3, b: Point3) -> Point3 {
30 [
31 a[1] * b[2] - a[2] * b[1],
32 a[2] * b[0] - a[0] * b[2],
33 a[0] * b[1] - a[1] * b[0],
34 ]
35}
36pub fn mag3_sq(a: Point3) -> f64 {
38 dot3(a, a)
39}
40pub fn mag3(a: Point3) -> f64 {
42 mag3_sq(a).sqrt()
43}
44pub fn normalize3(a: Point3) -> Point3 {
46 let m = mag3(a);
47 if m < 1e-15 {
48 [0.0; 3]
49 } else {
50 scale3(a, 1.0 / m)
51 }
52}
53pub type HalfEdgeId = usize;
55pub type VertexId = usize;
57pub type FaceId = usize;
59pub(super) fn signed_tet_volume(a: Point3, b: Point3, c: Point3) -> f64 {
61 dot3(a, cross3(b, c)) / 6.0
62}
63pub(super) fn above_face(a: Point3, b: Point3, c: Point3, p: Point3) -> bool {
66 let normal = cross3(sub3(b, a), sub3(c, a));
67 dot3(normal, sub3(p, a)) > 1e-12
68}
69pub(super) fn initial_tetrahedron(pts: &[Point3]) -> Option<ConvexHull3D> {
71 if pts.len() < 4 {
72 return None;
73 }
74 let p0 = pts[0];
75 let mut p1_idx = None;
76 for i in 1..pts.len() {
77 if mag3(sub3(pts[i], p0)) > 1e-10 {
78 p1_idx = Some(i);
79 break;
80 }
81 }
82 let p1_idx = p1_idx?;
83 let p1 = pts[p1_idx];
84 let mut p2_idx = None;
85 for i in (p1_idx + 1)..pts.len() {
86 let v = cross3(sub3(p1, p0), sub3(pts[i], p0));
87 if mag3(v) > 1e-10 {
88 p2_idx = Some(i);
89 break;
90 }
91 }
92 let p2_idx = p2_idx?;
93 let p2 = pts[p2_idx];
94 let mut p3_idx = None;
95 let normal = cross3(sub3(p1, p0), sub3(p2, p0));
96 for i in (p2_idx + 1)..pts.len() {
97 if dot3(normal, sub3(pts[i], p0)).abs() > 1e-10 {
98 p3_idx = Some(i);
99 break;
100 }
101 }
102 let p3_idx = p3_idx?;
103 let p3 = pts[p3_idx];
104 let verts = vec![p0, p1, p2, p3];
105 let cx = (p0[0] + p1[0] + p2[0] + p3[0]) / 4.0;
106 let cy = (p0[1] + p1[1] + p2[1] + p3[1]) / 4.0;
107 let cz = (p0[2] + p1[2] + p2[2] + p3[2]) / 4.0;
108 let center: Point3 = [cx, cy, cz];
109 let face_triples: [[usize; 3]; 4] = [[0, 1, 2], [0, 1, 3], [0, 2, 3], [1, 2, 3]];
110 let mut faces = Vec::new();
111 for tri in &face_triples {
112 let a = verts[tri[0]];
113 let b = verts[tri[1]];
114 let c = verts[tri[2]];
115 let mut normal = normalize3(cross3(sub3(b, a), sub3(c, a)));
116 if dot3(normal, sub3(a, center)) < 0.0 {
117 normal = scale3(normal, -1.0);
118 faces.push(ConvexFace3D {
119 verts: [tri[0], tri[2], tri[1]],
120 normal,
121 });
122 } else {
123 faces.push(ConvexFace3D {
124 verts: *tri,
125 normal,
126 });
127 }
128 }
129 Some(ConvexHull3D {
130 vertices: verts,
131 faces,
132 })
133}
134pub fn convex_hull_3d(points: &[Point3]) -> Option<ConvexHull3D> {
146 if points.len() < 4 {
147 return None;
148 }
149 let mut hull = initial_tetrahedron(points)?;
150 let skip_eps = 1e-10;
151 let mut remaining: Vec<Point3> = Vec::new();
152 'outer: for &p in points.iter() {
153 for v in &hull.vertices {
154 if mag3(sub3(p, *v)) < skip_eps {
155 continue 'outer;
156 }
157 }
158 remaining.push(p);
159 }
160 for &pt in &remaining {
161 let visible: Vec<bool> = hull
162 .faces
163 .iter()
164 .map(|f| {
165 let a = hull.vertices[f.verts[0]];
166 above_face(a, hull.vertices[f.verts[1]], hull.vertices[f.verts[2]], pt)
167 })
168 .collect();
169 let any_visible = visible.iter().any(|&v| v);
170 if !any_visible {
171 continue;
172 }
173 let mut horizon: Vec<(usize, usize)> = Vec::new();
174 for (fi, face) in hull.faces.iter().enumerate() {
175 if !visible[fi] {
176 continue;
177 }
178 let tri = face.verts;
179 let edges = [(tri[0], tri[1]), (tri[1], tri[2]), (tri[2], tri[0])];
180 for (ea, eb) in edges {
181 let on_horizon = hull.faces.iter().enumerate().any(|(fj, g)| {
182 if visible[fj] {
183 return false;
184 }
185 let t = g.verts;
186 [(t[0], t[1]), (t[1], t[2]), (t[2], t[0])]
187 .iter()
188 .any(|&(ga, gb)| ga == eb && gb == ea)
189 });
190 if on_horizon {
191 horizon.push((ea, eb));
192 }
193 }
194 }
195 let new_vid = hull.vertices.len();
196 hull.vertices.push(pt);
197 let n_v = hull.vertices.len() as f64;
198 let cx: f64 = hull.vertices.iter().map(|v| v[0]).sum::<f64>() / n_v;
199 let cy: f64 = hull.vertices.iter().map(|v| v[1]).sum::<f64>() / n_v;
200 let cz: f64 = hull.vertices.iter().map(|v| v[2]).sum::<f64>() / n_v;
201 let center = [cx, cy, cz];
202 let new_faces: Vec<ConvexFace3D> = hull
203 .faces
204 .iter()
205 .enumerate()
206 .filter(|(fi, _)| !visible[*fi])
207 .map(|(_, f)| f.clone())
208 .collect();
209 hull.faces = new_faces;
210 for (ha, hb) in horizon {
211 let a = hull.vertices[ha];
212 let b = hull.vertices[hb];
213 let mut normal = normalize3(cross3(sub3(b, a), sub3(pt, a)));
214 let verts = if dot3(normal, sub3(a, center)) < 0.0 {
215 normal = scale3(normal, -1.0);
216 [ha, new_vid, hb]
217 } else {
218 [ha, hb, new_vid]
219 };
220 hull.faces.push(ConvexFace3D { verts, normal });
221 }
222 }
223 Some(hull)
224}
225pub fn clip_polygon_by_edge(polygon: &[Point2], edge_a: Point2, edge_b: Point2) -> Vec<Point2> {
230 if polygon.is_empty() {
231 return vec![];
232 }
233 let n = polygon.len();
234 let mut output = Vec::with_capacity(n + 1);
235 for i in 0..n {
236 let cur = polygon[i];
237 let next = polygon[(i + 1) % n];
238 let d_cur = Point2::cross2(edge_a, edge_b, cur);
239 let d_next = Point2::cross2(edge_a, edge_b, next);
240 if d_cur >= 0.0 {
241 output.push(cur);
242 if d_next < 0.0 {
243 output.push(edge_intersect(cur, next, edge_a, edge_b));
244 }
245 } else {
246 if d_next >= 0.0 {
247 output.push(edge_intersect(cur, next, edge_a, edge_b));
248 }
249 }
250 }
251 output
252}
253pub(super) fn edge_intersect(p: Point2, q: Point2, a: Point2, b: Point2) -> Point2 {
255 let pq = q.sub(p);
256 let ab = b.sub(a);
257 let ap = a.sub(p);
258 let denom = pq.cross(ab);
259 if denom.abs() < 1e-15 {
260 return p;
261 }
262 let t = ap.cross(ab) / denom;
263 p.add(pq.scale(t))
264}
265pub fn sutherland_hodgman(subject: &[Point2], clip: &[Point2]) -> Vec<Point2> {
272 if subject.is_empty() || clip.is_empty() {
273 return vec![];
274 }
275 let mut output = subject.to_vec();
276 let n = clip.len();
277 for i in 0..n {
278 if output.is_empty() {
279 break;
280 }
281 let a = clip[i];
282 let b = clip[(i + 1) % n];
283 output = clip_polygon_by_edge(&output, a, b);
284 }
285 output
286}
287pub fn polygon_area(poly: &[Point2]) -> f64 {
289 let n = poly.len();
290 if n < 3 {
291 return 0.0;
292 }
293 let mut area = 0.0f64;
294 for i in 0..n {
295 let j = (i + 1) % n;
296 area += poly[i].x * poly[j].y;
297 area -= poly[j].x * poly[i].y;
298 }
299 area / 2.0
300}
301pub fn minkowski_sum(p: &[Point2], q: &[Point2]) -> Vec<Point2> {
312 if p.is_empty() || q.is_empty() {
313 return vec![];
314 }
315 let start_p = bottom_vertex(p);
316 let start_q = bottom_vertex(q);
317 let n = p.len();
318 let m = q.len();
319 let p_rot: Vec<Point2> = (0..n).map(|i| p[(start_p + i) % n]).collect();
320 let q_rot: Vec<Point2> = (0..m).map(|i| q[(start_q + i) % m]).collect();
321 let edges_p: Vec<Point2> = (0..n).map(|i| p_rot[(i + 1) % n].sub(p_rot[i])).collect();
322 let edges_q: Vec<Point2> = (0..m).map(|i| q_rot[(i + 1) % m].sub(q_rot[i])).collect();
323 let mut result = Vec::with_capacity(n + m);
324 let mut cur = p_rot[0].add(q_rot[0]);
325 result.push(cur);
326 let mut i = 0;
327 let mut j = 0;
328 while i < n || j < m {
329 let ep = if i < n {
330 edges_p[i]
331 } else {
332 Point2::new(1e18, 1e18)
333 };
334 let eq = if j < m {
335 edges_q[j]
336 } else {
337 Point2::new(1e18, 1e18)
338 };
339 let cross = ep.cross(eq);
340 let next_edge = if cross > 0.0 || j >= m {
341 i += 1;
342 ep
343 } else if cross < 0.0 || i >= n {
344 j += 1;
345 eq
346 } else {
347 i += 1;
348 j += 1;
349 ep.add(eq)
350 };
351 if i <= n || j <= m {
352 cur = cur.add(next_edge);
353 result.push(cur);
354 }
355 }
356 if result.len() > 1 {
357 let last = *result.last().expect("collection should not be empty");
358 let first = result[0];
359 if last.dist_sq(first) < 1e-20 {
360 result.pop();
361 }
362 }
363 result
364}
365pub(super) fn bottom_vertex(poly: &[Point2]) -> usize {
367 let mut best = 0;
368 for i in 1..poly.len() {
369 let b = poly[best];
370 let c = poly[i];
371 if c.y < b.y || (c.y == b.y && c.x < b.x) {
372 best = i;
373 }
374 }
375 best
376}
377pub fn line_intersect_2d(l1: &Line2D, l2: &Line2D) -> Option<Point2> {
379 let det = l1.a * l2.b - l2.a * l1.b;
380 if det.abs() < 1e-15 {
381 return None;
382 }
383 let x = (l1.c * l2.b - l2.c * l1.b) / det;
384 let y = (l1.a * l2.c - l2.a * l1.c) / det;
385 Some(Point2::new(x, y))
386}
387pub(super) fn segment_visible(p: Point2, q: Point2, blocking: &[(Point2, Point2)]) -> bool {
392 for &(a, b) in blocking {
393 if segments_properly_intersect(p, q, a, b) {
394 return false;
395 }
396 }
397 true
398}
399pub(super) fn segments_properly_intersect(p: Point2, q: Point2, a: Point2, b: Point2) -> bool {
401 let eps = 1e-12;
402 if p.dist_sq(a) < eps || p.dist_sq(b) < eps || q.dist_sq(a) < eps || q.dist_sq(b) < eps {
403 return false;
404 }
405 let d1 = Point2::cross2(a, b, p);
406 let d2 = Point2::cross2(a, b, q);
407 let d3 = Point2::cross2(p, q, a);
408 let d4 = Point2::cross2(p, q, b);
409 if d1 * d2 < 0.0 && d3 * d4 < 0.0 {
410 return true;
411 }
412 false
413}
414pub fn art_gallery_greedy(poly: &[Point2], max_guards: usize) -> ArtGalleryResult {
424 let n = poly.len();
425 if n == 0 {
426 return ArtGalleryResult {
427 guards: vec![],
428 covered: vec![],
429 };
430 }
431 let edges: Vec<(Point2, Point2)> = (0..n).map(|i| (poly[i], poly[(i + 1) % n])).collect();
432 let vis: Vec<Vec<bool>> = (0..n)
433 .map(|i| {
434 (0..n)
435 .map(|j| {
436 if i == j {
437 true
438 } else {
439 segment_visible(poly[i], poly[j], &edges)
440 }
441 })
442 .collect()
443 })
444 .collect();
445 let mut covered = vec![false; n];
446 let mut guards = Vec::new();
447 for _ in 0..max_guards {
448 if covered.iter().all(|&c| c) {
449 break;
450 }
451 let best = (0..n)
452 .max_by_key(|&i| (0..n).filter(|&j| !covered[j] && vis[i][j]).count())
453 .expect("operation should succeed");
454 guards.push(best);
455 for j in 0..n {
456 if vis[best][j] {
457 covered[j] = true;
458 }
459 }
460 }
461 ArtGalleryResult { guards, covered }
462}
463pub fn check_full_coverage(poly: &[Point2], guards: &[usize]) -> bool {
467 let n = poly.len();
468 let edges: Vec<(Point2, Point2)> = (0..n).map(|i| (poly[i], poly[(i + 1) % n])).collect();
469 for j in 0..n {
470 let seen = guards
471 .iter()
472 .any(|&g| g == j || segment_visible(poly[g], poly[j], &edges));
473 if !seen {
474 return false;
475 }
476 }
477 true
478}
479pub fn circumcircle_2d(pa: Point2, pb: Point2, pc: Point2) -> Option<(Point2, f64)> {
481 let ax = pb.x - pa.x;
482 let ay = pb.y - pa.y;
483 let bx = pc.x - pa.x;
484 let by = pc.y - pa.y;
485 let d = 2.0 * (ax * by - ay * bx);
486 if d.abs() < 1e-12 {
487 return None;
488 }
489 let ux = (by * (ax * ax + ay * ay) - ay * (bx * bx + by * by)) / d;
490 let uy = (ax * (bx * bx + by * by) - bx * (ax * ax + ay * ay)) / d;
491 let cx = pa.x + ux;
492 let cy = pa.y + uy;
493 let center = Point2::new(cx, cy);
494 let r2 = center.dist_sq(pa);
495 Some((center, r2))
496}
497pub fn in_circumcircle_2d(pa: Point2, pb: Point2, pc: Point2, p: Point2) -> bool {
499 match circumcircle_2d(pa, pb, pc) {
500 Some((center, r2)) => center.dist_sq(p) < r2 - 1e-12,
501 None => false,
502 }
503}
504pub fn delaunay_2d(points: &[Point2]) -> Vec<DelaunayTri> {
509 let n = points.len();
510 if n < 3 {
511 return vec![];
512 }
513 let min_x = points.iter().map(|p| p.x).fold(f64::INFINITY, f64::min);
514 let max_x = points.iter().map(|p| p.x).fold(f64::NEG_INFINITY, f64::max);
515 let min_y = points.iter().map(|p| p.y).fold(f64::INFINITY, f64::min);
516 let max_y = points.iter().map(|p| p.y).fold(f64::NEG_INFINITY, f64::max);
517 let dx = max_x - min_x;
518 let dy = max_y - min_y;
519 let delta = dx.max(dy) * 3.0 + 1.0;
520 let mx = (min_x + max_x) / 2.0;
521 let my = (min_y + max_y) / 2.0;
522 let mut all_pts = points.to_vec();
523 all_pts.push(Point2::new(mx - 20.0 * delta, my - delta));
524 all_pts.push(Point2::new(mx, my + 20.0 * delta));
525 all_pts.push(Point2::new(mx + 20.0 * delta, my - delta));
526 let s0 = n;
527 let s1 = n + 1;
528 let s2 = n + 2;
529 let mut triangles: Vec<DelaunayTri> = vec![DelaunayTri {
530 a: s0,
531 b: s1,
532 c: s2,
533 }];
534 for i in 0..n {
535 let pt = all_pts[i];
536 let (bad, good): (Vec<_>, Vec<_>) = triangles
537 .into_iter()
538 .partition(|t| in_circumcircle_2d(all_pts[t.a], all_pts[t.b], all_pts[t.c], pt));
539 let mut boundary: Vec<(usize, usize)> = Vec::new();
540 for t in &bad {
541 let edges = [(t.a, t.b), (t.b, t.c), (t.c, t.a)];
542 for (ea, eb) in edges {
543 let shared = bad.iter().any(|u| {
544 u != t
545 && ([(u.a, u.b), (u.b, u.c), (u.c, u.a)]
546 .iter()
547 .any(|&(ua, ub)| ua == eb && ub == ea))
548 });
549 if !shared {
550 boundary.push((ea, eb));
551 }
552 }
553 }
554 triangles = good;
555 for (ea, eb) in boundary {
556 triangles.push(DelaunayTri { a: ea, b: eb, c: i });
557 }
558 }
559 triangles.retain(|t| t.a < n && t.b < n && t.c < n);
560 triangles
561}
562pub fn voronoi_from_delaunay(points: &[Point2], tris: &[DelaunayTri]) -> Vec<VoronoiCell2D> {
567 let n = points.len();
568 let mut cells: Vec<VoronoiCell2D> = (0..n)
569 .map(|i| VoronoiCell2D {
570 site: i,
571 circumcenters: Vec::new(),
572 })
573 .collect();
574 for t in tris {
575 if let Some((center, _)) = circumcircle_2d(points[t.a], points[t.b], points[t.c]) {
576 cells[t.a].circumcenters.push(center);
577 cells[t.b].circumcenters.push(center);
578 cells[t.c].circumcenters.push(center);
579 }
580 }
581 for cell in &mut cells {
582 let site = points[cell.site];
583 cell.circumcenters.sort_by(|a, b| {
584 let ta = (a.y - site.y).atan2(a.x - site.x);
585 let tb = (b.y - site.y).atan2(b.x - site.x);
586 ta.partial_cmp(&tb).unwrap_or(std::cmp::Ordering::Equal)
587 });
588 }
589 cells
590}
591pub fn ccw(a: Point2, b: Point2, c: Point2) -> bool {
593 Point2::cross2(a, b, c) > 0.0
594}
595pub fn collinear(a: Point2, b: Point2, c: Point2) -> bool {
597 Point2::cross2(a, b, c).abs() < 1e-10
598}
599pub fn point_in_triangle(p: Point2, a: Point2, b: Point2, c: Point2) -> bool {
603 let d1 = Point2::cross2(p, a, b);
604 let d2 = Point2::cross2(p, b, c);
605 let d3 = Point2::cross2(p, c, a);
606 let has_neg = d1 < 0.0 || d2 < 0.0 || d3 < 0.0;
607 let has_pos = d1 > 0.0 || d2 > 0.0 || d3 > 0.0;
608 !(has_neg && has_pos)
609}
610pub fn point_in_convex_polygon(p: Point2, poly: &[Point2]) -> bool {
612 let n = poly.len();
613 if n < 3 {
614 return false;
615 }
616 for i in 0..n {
617 let a = poly[i];
618 let b = poly[(i + 1) % n];
619 if Point2::cross2(a, b, p) < 0.0 {
620 return false;
621 }
622 }
623 true
624}
625pub fn convex_hull_2d(points: &[Point2]) -> Vec<Point2> {
629 let mut pts = points.to_vec();
630 if pts.len() < 3 {
631 return pts;
632 }
633 pts.sort_by(|a, b| {
634 a.x.partial_cmp(&b.x)
635 .expect("operation should succeed")
636 .then(a.y.partial_cmp(&b.y).unwrap_or(std::cmp::Ordering::Equal))
637 });
638 pts.dedup_by(|a, b| a.dist_sq(*b) < 1e-20);
639 let n = pts.len();
640 let mut hull: Vec<Point2> = Vec::with_capacity(2 * n);
641 for &p in &pts {
642 while hull.len() >= 2 {
643 let a = hull[hull.len() - 2];
644 let b = hull[hull.len() - 1];
645 if Point2::cross2(a, b, p) <= 0.0 {
646 hull.pop();
647 } else {
648 break;
649 }
650 }
651 hull.push(p);
652 }
653 let lower_len = hull.len();
654 for &p in pts.iter().rev() {
655 while hull.len() > lower_len {
656 let a = hull[hull.len() - 2];
657 let b = hull[hull.len() - 1];
658 if Point2::cross2(a, b, p) <= 0.0 {
659 hull.pop();
660 } else {
661 break;
662 }
663 }
664 hull.push(p);
665 }
666 hull.pop();
667 hull
668}