1use super::types::{
6 ArtGalleryResult, ConvexFace3D, ConvexHull3D, DelaunayTri, Line2D, Point2, VoronoiCell2D,
7};
8use std::ops::{Add, Sub};
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 above_face(a: Point3, b: Point3, c: Point3, p: Point3) -> bool {
62 let normal = cross3(sub3(b, a), sub3(c, a));
63 dot3(normal, sub3(p, a)) > 1e-12
64}
65pub(super) fn initial_tetrahedron(pts: &[Point3]) -> Option<ConvexHull3D> {
67 if pts.len() < 4 {
68 return None;
69 }
70 let p0 = pts[0];
71 let p1_idx = pts
72 .iter()
73 .enumerate()
74 .skip(1)
75 .find(|(_, p)| mag3(sub3(**p, p0)) > 1e-10)
76 .map(|(i, _)| i)?;
77 let p1 = pts[p1_idx];
78 let p2_idx = pts
79 .iter()
80 .enumerate()
81 .skip(p1_idx + 1)
82 .find(|(_, p)| mag3(cross3(sub3(p1, p0), sub3(**p, p0))) > 1e-10)
83 .map(|(i, _)| i)?;
84 let p2 = pts[p2_idx];
85 let mut p3_idx = None;
86 let normal = cross3(sub3(p1, p0), sub3(p2, p0));
87 for (i, &pt) in pts.iter().enumerate().skip(p2_idx + 1) {
88 if dot3(normal, sub3(pt, p0)).abs() > 1e-10 {
89 p3_idx = Some(i);
90 break;
91 }
92 }
93 let p3_idx = p3_idx?;
94 let p3 = pts[p3_idx];
95 let verts = vec![p0, p1, p2, p3];
96 let cx = (p0[0] + p1[0] + p2[0] + p3[0]) / 4.0;
97 let cy = (p0[1] + p1[1] + p2[1] + p3[1]) / 4.0;
98 let cz = (p0[2] + p1[2] + p2[2] + p3[2]) / 4.0;
99 let center: Point3 = [cx, cy, cz];
100 let face_triples: [[usize; 3]; 4] = [[0, 1, 2], [0, 1, 3], [0, 2, 3], [1, 2, 3]];
101 let mut faces = Vec::new();
102 for tri in &face_triples {
103 let a = verts[tri[0]];
104 let b = verts[tri[1]];
105 let c = verts[tri[2]];
106 let mut normal = normalize3(cross3(sub3(b, a), sub3(c, a)));
107 if dot3(normal, sub3(a, center)) < 0.0 {
108 normal = scale3(normal, -1.0);
109 faces.push(ConvexFace3D {
110 verts: [tri[0], tri[2], tri[1]],
111 normal,
112 });
113 } else {
114 faces.push(ConvexFace3D {
115 verts: *tri,
116 normal,
117 });
118 }
119 }
120 Some(ConvexHull3D {
121 vertices: verts,
122 faces,
123 })
124}
125pub fn convex_hull_3d(points: &[Point3]) -> Option<ConvexHull3D> {
137 if points.len() < 4 {
138 return None;
139 }
140 let mut hull = initial_tetrahedron(points)?;
141 let skip_eps = 1e-10;
142 let mut remaining: Vec<Point3> = Vec::new();
143 'outer: for &p in points.iter() {
144 for v in &hull.vertices {
145 if mag3(sub3(p, *v)) < skip_eps {
146 continue 'outer;
147 }
148 }
149 remaining.push(p);
150 }
151 for &pt in &remaining {
152 let visible: Vec<bool> = hull
153 .faces
154 .iter()
155 .map(|f| {
156 let a = hull.vertices[f.verts[0]];
157 above_face(a, hull.vertices[f.verts[1]], hull.vertices[f.verts[2]], pt)
158 })
159 .collect();
160 let any_visible = visible.iter().any(|&v| v);
161 if !any_visible {
162 continue;
163 }
164 let mut horizon: Vec<(usize, usize)> = Vec::new();
165 for (fi, face) in hull.faces.iter().enumerate() {
166 if !visible[fi] {
167 continue;
168 }
169 let tri = face.verts;
170 let edges = [(tri[0], tri[1]), (tri[1], tri[2]), (tri[2], tri[0])];
171 for (ea, eb) in edges {
172 let on_horizon = hull.faces.iter().enumerate().any(|(fj, g)| {
173 if visible[fj] {
174 return false;
175 }
176 let t = g.verts;
177 [(t[0], t[1]), (t[1], t[2]), (t[2], t[0])]
178 .iter()
179 .any(|&(ga, gb)| ga == eb && gb == ea)
180 });
181 if on_horizon {
182 horizon.push((ea, eb));
183 }
184 }
185 }
186 let new_vid = hull.vertices.len();
187 hull.vertices.push(pt);
188 let n_v = hull.vertices.len() as f64;
189 let cx: f64 = hull.vertices.iter().map(|v| v[0]).sum::<f64>() / n_v;
190 let cy: f64 = hull.vertices.iter().map(|v| v[1]).sum::<f64>() / n_v;
191 let cz: f64 = hull.vertices.iter().map(|v| v[2]).sum::<f64>() / n_v;
192 let center = [cx, cy, cz];
193 let new_faces: Vec<ConvexFace3D> = hull
194 .faces
195 .iter()
196 .enumerate()
197 .filter(|(fi, _)| !visible[*fi])
198 .map(|(_, f)| f.clone())
199 .collect();
200 hull.faces = new_faces;
201 for (ha, hb) in horizon {
202 let a = hull.vertices[ha];
203 let b = hull.vertices[hb];
204 let mut normal = normalize3(cross3(sub3(b, a), sub3(pt, a)));
205 let verts = if dot3(normal, sub3(a, center)) < 0.0 {
206 normal = scale3(normal, -1.0);
207 [ha, new_vid, hb]
208 } else {
209 [ha, hb, new_vid]
210 };
211 hull.faces.push(ConvexFace3D { verts, normal });
212 }
213 }
214 Some(hull)
215}
216pub fn clip_polygon_by_edge(polygon: &[Point2], edge_a: Point2, edge_b: Point2) -> Vec<Point2> {
221 if polygon.is_empty() {
222 return vec![];
223 }
224 let n = polygon.len();
225 let mut output = Vec::with_capacity(n + 1);
226 for i in 0..n {
227 let cur = polygon[i];
228 let next = polygon[(i + 1) % n];
229 let d_cur = Point2::cross2(edge_a, edge_b, cur);
230 let d_next = Point2::cross2(edge_a, edge_b, next);
231 if d_cur >= 0.0 {
232 output.push(cur);
233 if d_next < 0.0 {
234 output.push(edge_intersect(cur, next, edge_a, edge_b));
235 }
236 } else {
237 if d_next >= 0.0 {
238 output.push(edge_intersect(cur, next, edge_a, edge_b));
239 }
240 }
241 }
242 output
243}
244pub(super) fn edge_intersect(p: Point2, q: Point2, a: Point2, b: Point2) -> Point2 {
246 let pq = q.sub(p);
247 let ab = b.sub(a);
248 let ap = a.sub(p);
249 let denom = pq.cross(ab);
250 if denom.abs() < 1e-15 {
251 return p;
252 }
253 let t = ap.cross(ab) / denom;
254 p.add(pq.scale(t))
255}
256pub fn sutherland_hodgman(subject: &[Point2], clip: &[Point2]) -> Vec<Point2> {
263 if subject.is_empty() || clip.is_empty() {
264 return vec![];
265 }
266 let mut output = subject.to_vec();
267 let n = clip.len();
268 for i in 0..n {
269 if output.is_empty() {
270 break;
271 }
272 let a = clip[i];
273 let b = clip[(i + 1) % n];
274 output = clip_polygon_by_edge(&output, a, b);
275 }
276 output
277}
278pub fn polygon_area(poly: &[Point2]) -> f64 {
280 let n = poly.len();
281 if n < 3 {
282 return 0.0;
283 }
284 let mut area = 0.0f64;
285 for i in 0..n {
286 let j = (i + 1) % n;
287 area += poly[i].x * poly[j].y;
288 area -= poly[j].x * poly[i].y;
289 }
290 area / 2.0
291}
292pub fn minkowski_sum(p: &[Point2], q: &[Point2]) -> Vec<Point2> {
303 if p.is_empty() || q.is_empty() {
304 return vec![];
305 }
306 let start_p = bottom_vertex(p);
307 let start_q = bottom_vertex(q);
308 let n = p.len();
309 let m = q.len();
310 let p_rot: Vec<Point2> = (0..n).map(|i| p[(start_p + i) % n]).collect();
311 let q_rot: Vec<Point2> = (0..m).map(|i| q[(start_q + i) % m]).collect();
312 let edges_p: Vec<Point2> = (0..n).map(|i| p_rot[(i + 1) % n].sub(p_rot[i])).collect();
313 let edges_q: Vec<Point2> = (0..m).map(|i| q_rot[(i + 1) % m].sub(q_rot[i])).collect();
314 let mut result = Vec::with_capacity(n + m);
315 let mut cur = p_rot[0].add(q_rot[0]);
316 result.push(cur);
317 let mut i = 0;
318 let mut j = 0;
319 while i < n || j < m {
320 let ep = if i < n {
321 edges_p[i]
322 } else {
323 Point2::new(1e18, 1e18)
324 };
325 let eq = if j < m {
326 edges_q[j]
327 } else {
328 Point2::new(1e18, 1e18)
329 };
330 let cross = ep.cross(eq);
331 let next_edge = if cross > 0.0 || j >= m {
332 i += 1;
333 ep
334 } else if cross < 0.0 || i >= n {
335 j += 1;
336 eq
337 } else {
338 i += 1;
339 j += 1;
340 ep.add(eq)
341 };
342 if i <= n || j <= m {
343 cur = cur.add(next_edge);
344 result.push(cur);
345 }
346 }
347 if result.len() > 1 {
348 let last = *result.last().expect("collection should not be empty");
349 let first = result[0];
350 if last.dist_sq(first) < 1e-20 {
351 result.pop();
352 }
353 }
354 result
355}
356pub(super) fn bottom_vertex(poly: &[Point2]) -> usize {
358 let mut best = 0;
359 for i in 1..poly.len() {
360 let b = poly[best];
361 let c = poly[i];
362 if c.y < b.y || (c.y == b.y && c.x < b.x) {
363 best = i;
364 }
365 }
366 best
367}
368pub fn line_intersect_2d(l1: &Line2D, l2: &Line2D) -> Option<Point2> {
370 let det = l1.a * l2.b - l2.a * l1.b;
371 if det.abs() < 1e-15 {
372 return None;
373 }
374 let x = (l1.c * l2.b - l2.c * l1.b) / det;
375 let y = (l1.a * l2.c - l2.a * l1.c) / det;
376 Some(Point2::new(x, y))
377}
378pub(super) fn segment_visible(p: Point2, q: Point2, blocking: &[(Point2, Point2)]) -> bool {
383 for &(a, b) in blocking {
384 if segments_properly_intersect(p, q, a, b) {
385 return false;
386 }
387 }
388 true
389}
390pub(super) fn segments_properly_intersect(p: Point2, q: Point2, a: Point2, b: Point2) -> bool {
392 let eps = 1e-12;
393 if p.dist_sq(a) < eps || p.dist_sq(b) < eps || q.dist_sq(a) < eps || q.dist_sq(b) < eps {
394 return false;
395 }
396 let d1 = Point2::cross2(a, b, p);
397 let d2 = Point2::cross2(a, b, q);
398 let d3 = Point2::cross2(p, q, a);
399 let d4 = Point2::cross2(p, q, b);
400 if d1 * d2 < 0.0 && d3 * d4 < 0.0 {
401 return true;
402 }
403 false
404}
405pub fn art_gallery_greedy(poly: &[Point2], max_guards: usize) -> ArtGalleryResult {
415 let n = poly.len();
416 if n == 0 {
417 return ArtGalleryResult {
418 guards: vec![],
419 covered: vec![],
420 };
421 }
422 let edges: Vec<(Point2, Point2)> = (0..n).map(|i| (poly[i], poly[(i + 1) % n])).collect();
423 let vis: Vec<Vec<bool>> = (0..n)
424 .map(|i| {
425 (0..n)
426 .map(|j| {
427 if i == j {
428 true
429 } else {
430 segment_visible(poly[i], poly[j], &edges)
431 }
432 })
433 .collect()
434 })
435 .collect();
436 let mut covered = vec![false; n];
437 let mut guards = Vec::new();
438 for _ in 0..max_guards {
439 if covered.iter().all(|&c| c) {
440 break;
441 }
442 let best = (0..n)
443 .max_by_key(|&i| (0..n).filter(|&j| !covered[j] && vis[i][j]).count())
444 .expect("operation should succeed");
445 guards.push(best);
446 for j in 0..n {
447 if vis[best][j] {
448 covered[j] = true;
449 }
450 }
451 }
452 ArtGalleryResult { guards, covered }
453}
454pub fn check_full_coverage(poly: &[Point2], guards: &[usize]) -> bool {
458 let n = poly.len();
459 let edges: Vec<(Point2, Point2)> = (0..n).map(|i| (poly[i], poly[(i + 1) % n])).collect();
460 for j in 0..n {
461 let seen = guards
462 .iter()
463 .any(|&g| g == j || segment_visible(poly[g], poly[j], &edges));
464 if !seen {
465 return false;
466 }
467 }
468 true
469}
470pub fn circumcircle_2d(pa: Point2, pb: Point2, pc: Point2) -> Option<(Point2, f64)> {
472 let ax = pb.x - pa.x;
473 let ay = pb.y - pa.y;
474 let bx = pc.x - pa.x;
475 let by = pc.y - pa.y;
476 let d = 2.0 * (ax * by - ay * bx);
477 if d.abs() < 1e-12 {
478 return None;
479 }
480 let ux = (by * (ax * ax + ay * ay) - ay * (bx * bx + by * by)) / d;
481 let uy = (ax * (bx * bx + by * by) - bx * (ax * ax + ay * ay)) / d;
482 let cx = pa.x + ux;
483 let cy = pa.y + uy;
484 let center = Point2::new(cx, cy);
485 let r2 = center.dist_sq(pa);
486 Some((center, r2))
487}
488pub fn in_circumcircle_2d(pa: Point2, pb: Point2, pc: Point2, p: Point2) -> bool {
490 match circumcircle_2d(pa, pb, pc) {
491 Some((center, r2)) => center.dist_sq(p) < r2 - 1e-12,
492 None => false,
493 }
494}
495pub fn delaunay_2d(points: &[Point2]) -> Vec<DelaunayTri> {
500 let n = points.len();
501 if n < 3 {
502 return vec![];
503 }
504 let min_x = points.iter().map(|p| p.x).fold(f64::INFINITY, f64::min);
505 let max_x = points.iter().map(|p| p.x).fold(f64::NEG_INFINITY, f64::max);
506 let min_y = points.iter().map(|p| p.y).fold(f64::INFINITY, f64::min);
507 let max_y = points.iter().map(|p| p.y).fold(f64::NEG_INFINITY, f64::max);
508 let dx = max_x - min_x;
509 let dy = max_y - min_y;
510 let delta = dx.max(dy) * 3.0 + 1.0;
511 let mx = (min_x + max_x) / 2.0;
512 let my = (min_y + max_y) / 2.0;
513 let mut all_pts = points.to_vec();
514 all_pts.push(Point2::new(mx - 20.0 * delta, my - delta));
515 all_pts.push(Point2::new(mx, my + 20.0 * delta));
516 all_pts.push(Point2::new(mx + 20.0 * delta, my - delta));
517 let s0 = n;
518 let s1 = n + 1;
519 let s2 = n + 2;
520 let mut triangles: Vec<DelaunayTri> = vec![DelaunayTri {
521 a: s0,
522 b: s1,
523 c: s2,
524 }];
525 for i in 0..n {
526 let pt = all_pts[i];
527 let (bad, good): (Vec<_>, Vec<_>) = triangles
528 .into_iter()
529 .partition(|t| in_circumcircle_2d(all_pts[t.a], all_pts[t.b], all_pts[t.c], pt));
530 let mut boundary: Vec<(usize, usize)> = Vec::new();
531 for t in &bad {
532 let edges = [(t.a, t.b), (t.b, t.c), (t.c, t.a)];
533 for (ea, eb) in edges {
534 let shared = bad.iter().any(|u| {
535 u != t
536 && ([(u.a, u.b), (u.b, u.c), (u.c, u.a)]
537 .iter()
538 .any(|&(ua, ub)| ua == eb && ub == ea))
539 });
540 if !shared {
541 boundary.push((ea, eb));
542 }
543 }
544 }
545 triangles = good;
546 for (ea, eb) in boundary {
547 triangles.push(DelaunayTri { a: ea, b: eb, c: i });
548 }
549 }
550 triangles.retain(|t| t.a < n && t.b < n && t.c < n);
551 triangles
552}
553pub fn voronoi_from_delaunay(points: &[Point2], tris: &[DelaunayTri]) -> Vec<VoronoiCell2D> {
558 let n = points.len();
559 let mut cells: Vec<VoronoiCell2D> = (0..n)
560 .map(|i| VoronoiCell2D {
561 site: i,
562 circumcenters: Vec::new(),
563 })
564 .collect();
565 for t in tris {
566 if let Some((center, _)) = circumcircle_2d(points[t.a], points[t.b], points[t.c]) {
567 cells[t.a].circumcenters.push(center);
568 cells[t.b].circumcenters.push(center);
569 cells[t.c].circumcenters.push(center);
570 }
571 }
572 for cell in &mut cells {
573 let site = points[cell.site];
574 cell.circumcenters.sort_by(|a, b| {
575 let ta = (a.y - site.y).atan2(a.x - site.x);
576 let tb = (b.y - site.y).atan2(b.x - site.x);
577 ta.partial_cmp(&tb).unwrap_or(std::cmp::Ordering::Equal)
578 });
579 }
580 cells
581}
582pub fn ccw(a: Point2, b: Point2, c: Point2) -> bool {
584 Point2::cross2(a, b, c) > 0.0
585}
586pub fn collinear(a: Point2, b: Point2, c: Point2) -> bool {
588 Point2::cross2(a, b, c).abs() < 1e-10
589}
590pub fn point_in_triangle(p: Point2, a: Point2, b: Point2, c: Point2) -> bool {
594 let d1 = Point2::cross2(p, a, b);
595 let d2 = Point2::cross2(p, b, c);
596 let d3 = Point2::cross2(p, c, a);
597 let has_neg = d1 < 0.0 || d2 < 0.0 || d3 < 0.0;
598 let has_pos = d1 > 0.0 || d2 > 0.0 || d3 > 0.0;
599 !(has_neg && has_pos)
600}
601pub fn point_in_convex_polygon(p: Point2, poly: &[Point2]) -> bool {
603 let n = poly.len();
604 if n < 3 {
605 return false;
606 }
607 for i in 0..n {
608 let a = poly[i];
609 let b = poly[(i + 1) % n];
610 if Point2::cross2(a, b, p) < 0.0 {
611 return false;
612 }
613 }
614 true
615}
616pub fn convex_hull_2d(points: &[Point2]) -> Vec<Point2> {
620 let mut pts = points.to_vec();
621 if pts.len() < 3 {
622 return pts;
623 }
624 pts.sort_by(|a, b| {
625 a.x.partial_cmp(&b.x)
626 .expect("operation should succeed")
627 .then(a.y.partial_cmp(&b.y).unwrap_or(std::cmp::Ordering::Equal))
628 });
629 pts.dedup_by(|a, b| a.dist_sq(*b) < 1e-20);
630 let n = pts.len();
631 let mut hull: Vec<Point2> = Vec::with_capacity(2 * n);
632 for &p in &pts {
633 while hull.len() >= 2 {
634 let a = hull[hull.len() - 2];
635 let b = hull[hull.len() - 1];
636 if Point2::cross2(a, b, p) <= 0.0 {
637 hull.pop();
638 } else {
639 break;
640 }
641 }
642 hull.push(p);
643 }
644 let lower_len = hull.len();
645 for &p in pts.iter().rev() {
646 while hull.len() > lower_len {
647 let a = hull[hull.len() - 2];
648 let b = hull[hull.len() - 1];
649 if Point2::cross2(a, b, p) <= 0.0 {
650 hull.pop();
651 } else {
652 break;
653 }
654 }
655 hull.push(p);
656 }
657 hull.pop();
658 hull
659}