1use std::f64;
5
6#[derive(Clone, Copy, Debug, PartialEq)]
12pub struct Point2 {
13 pub x: f64,
14 pub y: f64,
15}
16
17impl Point2 {
18 pub fn new(x: f64, y: f64) -> Self { Point2 { x, y } }
19 pub fn zero() -> Self { Point2 { x: 0.0, y: 0.0 } }
20 pub fn dot(self, other: Self) -> f64 { self.x * other.x + self.y * other.y }
21 pub fn cross(self, other: Self) -> f64 { self.x * other.y - self.y * other.x }
22 pub fn len_sq(self) -> f64 { self.x * self.x + self.y * self.y }
23 pub fn len(self) -> f64 { self.len_sq().sqrt() }
24 pub fn normalize(self) -> Self {
25 let l = self.len();
26 if l < 1e-300 { return Self::zero(); }
27 Point2 { x: self.x / l, y: self.y / l }
28 }
29 pub fn dist(self, other: Self) -> f64 { (self - other).len() }
30 pub fn perp(self) -> Self { Point2 { x: -self.y, y: self.x } }
31}
32
33impl std::ops::Add for Point2 {
34 type Output = Self;
35 fn add(self, o: Self) -> Self { Point2 { x: self.x + o.x, y: self.y + o.y } }
36}
37impl std::ops::Sub for Point2 {
38 type Output = Self;
39 fn sub(self, o: Self) -> Self { Point2 { x: self.x - o.x, y: self.y - o.y } }
40}
41impl std::ops::Mul<f64> for Point2 {
42 type Output = Self;
43 fn mul(self, s: f64) -> Self { Point2 { x: self.x * s, y: self.y * s } }
44}
45impl std::ops::Neg for Point2 {
46 type Output = Self;
47 fn neg(self) -> Self { Point2 { x: -self.x, y: -self.y } }
48}
49
50#[derive(Clone, Copy, Debug, PartialEq)]
52pub struct Point3 {
53 pub x: f64,
54 pub y: f64,
55 pub z: f64,
56}
57
58impl Point3 {
59 pub fn new(x: f64, y: f64, z: f64) -> Self { Point3 { x, y, z } }
60 pub fn zero() -> Self { Point3 { x: 0.0, y: 0.0, z: 0.0 } }
61 pub fn dot(self, other: Self) -> f64 { self.x * other.x + self.y * other.y + self.z * other.z }
62 pub fn cross(self, other: Self) -> Self {
63 Point3 {
64 x: self.y * other.z - self.z * other.y,
65 y: self.z * other.x - self.x * other.z,
66 z: self.x * other.y - self.y * other.x,
67 }
68 }
69 pub fn len_sq(self) -> f64 { self.x * self.x + self.y * self.y + self.z * self.z }
70 pub fn len(self) -> f64 { self.len_sq().sqrt() }
71 pub fn normalize(self) -> Self {
72 let l = self.len();
73 if l < 1e-300 { return Self::zero(); }
74 Point3 { x: self.x / l, y: self.y / l, z: self.z / l }
75 }
76 pub fn dist(self, other: Self) -> f64 { (self - other).len() }
77 pub fn lerp(self, other: Self, t: f64) -> Self {
78 self + (other - self) * t
79 }
80 pub fn abs(self) -> Self { Point3 { x: self.x.abs(), y: self.y.abs(), z: self.z.abs() } }
81 pub fn component_min(self, other: Self) -> Self {
82 Point3 { x: self.x.min(other.x), y: self.y.min(other.y), z: self.z.min(other.z) }
83 }
84 pub fn component_max(self, other: Self) -> Self {
85 Point3 { x: self.x.max(other.x), y: self.y.max(other.y), z: self.z.max(other.z) }
86 }
87}
88
89impl std::ops::Add for Point3 {
90 type Output = Self;
91 fn add(self, o: Self) -> Self { Point3 { x: self.x + o.x, y: self.y + o.y, z: self.z + o.z } }
92}
93impl std::ops::Sub for Point3 {
94 type Output = Self;
95 fn sub(self, o: Self) -> Self { Point3 { x: self.x - o.x, y: self.y - o.y, z: self.z - o.z } }
96}
97impl std::ops::Mul<f64> for Point3 {
98 type Output = Self;
99 fn mul(self, s: f64) -> Self { Point3 { x: self.x * s, y: self.y * s, z: self.z * s } }
100}
101impl std::ops::Div<f64> for Point3 {
102 type Output = Self;
103 fn div(self, s: f64) -> Self { Point3 { x: self.x / s, y: self.y / s, z: self.z / s } }
104}
105impl std::ops::Neg for Point3 {
106 type Output = Self;
107 fn neg(self) -> Self { Point3 { x: -self.x, y: -self.y, z: -self.z } }
108}
109impl std::ops::AddAssign for Point3 {
110 fn add_assign(&mut self, o: Self) { self.x += o.x; self.y += o.y; self.z += o.z; }
111}
112
113#[derive(Clone, Copy, Debug)]
115pub struct Segment2 {
116 pub a: Point2,
117 pub b: Point2,
118}
119
120#[derive(Clone, Copy, Debug)]
122pub struct Triangle2 {
123 pub a: Point2,
124 pub b: Point2,
125 pub c: Point2,
126}
127
128#[derive(Clone, Copy, Debug)]
130pub struct Triangle3 {
131 pub a: Point3,
132 pub b: Point3,
133 pub c: Point3,
134}
135
136#[derive(Clone, Copy, Debug)]
138pub struct Aabb2 {
139 pub min: Point2,
140 pub max: Point2,
141}
142
143impl Aabb2 {
144 pub fn new(min: Point2, max: Point2) -> Self { Aabb2 { min, max } }
145 pub fn contains(&self, p: Point2) -> bool {
146 p.x >= self.min.x && p.x <= self.max.x && p.y >= self.min.y && p.y <= self.max.y
147 }
148 pub fn center(&self) -> Point2 {
149 Point2 { x: (self.min.x + self.max.x) * 0.5, y: (self.min.y + self.max.y) * 0.5 }
150 }
151}
152
153#[derive(Clone, Copy, Debug)]
155pub struct Aabb3 {
156 pub min: Point3,
157 pub max: Point3,
158}
159
160impl Aabb3 {
161 pub fn new(min: Point3, max: Point3) -> Self { Aabb3 { min, max } }
162 pub fn center(&self) -> Point3 { (self.min + self.max) * 0.5 }
163 pub fn half_extents(&self) -> Point3 { (self.max - self.min) * 0.5 }
164 pub fn expand(&self, amount: f64) -> Self {
165 let a = Point3::new(amount, amount, amount);
166 Aabb3 { min: self.min - a, max: self.max + a }
167 }
168}
169
170#[derive(Clone, Copy, Debug)]
172pub struct Circle {
173 pub center: Point2,
174 pub radius: f64,
175}
176
177#[derive(Clone, Copy, Debug)]
179pub struct Sphere {
180 pub center: Point3,
181 pub radius: f64,
182}
183
184#[derive(Clone, Copy, Debug)]
186pub struct Plane {
187 pub normal: Point3,
188 pub d: f64,
189}
190
191impl Plane {
192 pub fn from_point_normal(p: Point3, n: Point3) -> Self {
193 let n = n.normalize();
194 Plane { normal: n, d: -n.dot(p) }
195 }
196 pub fn from_triangle(a: Point3, b: Point3, c: Point3) -> Self {
197 let n = (b - a).cross(c - a).normalize();
198 Self::from_point_normal(a, n)
199 }
200 pub fn signed_dist(&self, p: Point3) -> f64 {
201 self.normal.dot(p) + self.d
202 }
203}
204
205#[derive(Clone, Copy, Debug)]
207pub struct Ray2 {
208 pub origin: Point2,
209 pub dir: Point2,
210}
211
212#[derive(Clone, Copy, Debug)]
214pub struct Ray3 {
215 pub origin: Point3,
216 pub dir: Point3,
217}
218
219impl Ray3 {
220 pub fn at(&self, t: f64) -> Point3 { self.origin + self.dir * t }
221}
222
223#[derive(Clone, Copy, Debug)]
225pub struct Capsule {
226 pub a: Point3,
227 pub b: Point3,
228 pub radius: f64,
229}
230
231#[derive(Clone, Copy, Debug)]
233pub struct Obb3 {
234 pub center: Point3,
235 pub axes: [Point3; 3], pub half_extents: Point3, }
238
239#[derive(Clone, Copy, Debug, PartialEq)]
241pub enum Halfspace {
242 Front,
243 Back,
244 Intersect,
245}
246
247pub fn ray_aabb(ray: &Ray3, aabb: &Aabb3) -> Option<f64> {
253 let inv_dir = Point3::new(1.0 / ray.dir.x, 1.0 / ray.dir.y, 1.0 / ray.dir.z);
254 let t1 = (aabb.min.x - ray.origin.x) * inv_dir.x;
255 let t2 = (aabb.max.x - ray.origin.x) * inv_dir.x;
256 let t3 = (aabb.min.y - ray.origin.y) * inv_dir.y;
257 let t4 = (aabb.max.y - ray.origin.y) * inv_dir.y;
258 let t5 = (aabb.min.z - ray.origin.z) * inv_dir.z;
259 let t6 = (aabb.max.z - ray.origin.z) * inv_dir.z;
260 let tmin = t1.min(t2).max(t3.min(t4)).max(t5.min(t6));
261 let tmax = t1.max(t2).min(t3.max(t4)).min(t5.max(t6));
262 if tmax < 0.0 || tmin > tmax { None } else { Some(tmin.max(0.0)) }
263}
264
265pub fn ray_sphere(ray: &Ray3, sphere: &Sphere) -> Option<f64> {
267 let oc = ray.origin - sphere.center;
268 let a = ray.dir.dot(ray.dir);
269 let b = 2.0 * oc.dot(ray.dir);
270 let c = oc.dot(oc) - sphere.radius * sphere.radius;
271 let disc = b * b - 4.0 * a * c;
272 if disc < 0.0 { return None; }
273 let sqrt_d = disc.sqrt();
274 let t1 = (-b - sqrt_d) / (2.0 * a);
275 let t2 = (-b + sqrt_d) / (2.0 * a);
276 if t1 >= 0.0 { Some(t1) } else if t2 >= 0.0 { Some(t2) } else { None }
277}
278
279pub fn ray_triangle(ray: &Ray3, tri: &Triangle3) -> Option<(f64, f64, f64)> {
282 const EPS: f64 = 1e-10;
283 let edge1 = tri.b - tri.a;
284 let edge2 = tri.c - tri.a;
285 let h = ray.dir.cross(edge2);
286 let det = edge1.dot(h);
287 if det.abs() < EPS { return None; }
288 let inv_det = 1.0 / det;
289 let s = ray.origin - tri.a;
290 let u = inv_det * s.dot(h);
291 if !(0.0..=1.0).contains(&u) { return None; }
292 let q = s.cross(edge1);
293 let v = inv_det * ray.dir.dot(q);
294 if v < 0.0 || u + v > 1.0 { return None; }
295 let t = inv_det * edge2.dot(q);
296 if t < EPS { return None; }
297 Some((t, u, v))
298}
299
300pub fn ray_plane(ray: &Ray3, plane: &Plane) -> Option<f64> {
302 let denom = plane.normal.dot(ray.dir);
303 if denom.abs() < 1e-12 { return None; }
304 let t = -(plane.normal.dot(ray.origin) + plane.d) / denom;
305 if t < 0.0 { None } else { Some(t) }
306}
307
308pub fn ray_capsule(ray: &Ray3, cap: &Capsule) -> Option<f64> {
310 let ab = cap.b - cap.a;
311 let ao = ray.origin - cap.a;
312 let ab_len_sq = ab.dot(ab);
313 let ab_dot_d = ab.dot(ray.dir);
314 let ab_dot_ao = ab.dot(ao);
315 let a = ab_len_sq * ray.dir.dot(ray.dir) - ab_dot_d * ab_dot_d;
316 let bv = ab_len_sq * ray.dir.dot(ao) - ab_dot_d * ab_dot_ao;
317 let c = ab_len_sq * (ao.dot(ao) - cap.radius * cap.radius) - ab_dot_ao * ab_dot_ao;
318 let mut t_hit = f64::MAX;
319 if a.abs() > 1e-10 {
320 let disc = bv * bv - a * c;
321 if disc >= 0.0 {
322 let t = (-bv - disc.sqrt()) / a;
323 let m = ab_dot_d * t + ab_dot_ao;
324 if m >= 0.0 && m <= ab_len_sq {
325 t_hit = t_hit.min(t);
326 }
327 }
328 }
329 for cap_center in [cap.a, cap.b] {
331 let sphere = Sphere { center: cap_center, radius: cap.radius };
332 if let Some(t) = ray_sphere(ray, &sphere) {
333 t_hit = t_hit.min(t);
334 }
335 }
336 if t_hit < f64::MAX && t_hit >= 0.0 { Some(t_hit) } else { None }
337}
338
339pub fn ray_obb(ray: &Ray3, obb: &Obb3) -> Option<f64> {
341 let d = ray.origin - obb.center;
343 let mut t_min = f64::NEG_INFINITY;
344 let mut t_max = f64::INFINITY;
345 let half = [obb.half_extents.x, obb.half_extents.y, obb.half_extents.z];
346 for (i, axis) in obb.axes.iter().enumerate() {
347 let e = axis.dot(d);
348 let f = axis.dot(ray.dir);
349 if f.abs() > 1e-12 {
350 let t1 = (-e - half[i]) / f;
351 let t2 = (-e + half[i]) / f;
352 let (t1, t2) = if t1 > t2 { (t2, t1) } else { (t1, t2) };
353 t_min = t_min.max(t1);
354 t_max = t_max.min(t2);
355 if t_min > t_max { return None; }
356 } else if e.abs() > half[i] {
357 return None;
358 }
359 }
360 if t_max < 0.0 { return None; }
361 let t = if t_min >= 0.0 { t_min } else { t_max };
362 Some(t)
363}
364
365pub fn aabb_aabb(a: &Aabb3, b: &Aabb3) -> bool {
367 a.min.x <= b.max.x && a.max.x >= b.min.x &&
368 a.min.y <= b.max.y && a.max.y >= b.min.y &&
369 a.min.z <= b.max.z && a.max.z >= b.min.z
370}
371
372pub fn sphere_sphere(a: &Sphere, b: &Sphere) -> bool {
374 let d = a.center.dist(b.center);
375 d <= a.radius + b.radius
376}
377
378pub fn sphere_aabb(s: &Sphere, b: &Aabb3) -> bool {
380 let closest = Point3::new(
381 s.center.x.clamp(b.min.x, b.max.x),
382 s.center.y.clamp(b.min.y, b.max.y),
383 s.center.z.clamp(b.min.z, b.max.z),
384 );
385 s.center.dist(closest) <= s.radius
386}
387
388pub fn aabb_plane(b: &Aabb3, p: &Plane) -> Halfspace {
390 let center = b.center();
391 let half = b.half_extents();
392 let r = half.x * p.normal.x.abs()
393 + half.y * p.normal.y.abs()
394 + half.z * p.normal.z.abs();
395 let dist = p.normal.dot(center) + p.d;
396 if dist > r { Halfspace::Front }
397 else if dist < -r { Halfspace::Back }
398 else { Halfspace::Intersect }
399}
400
401pub fn obb_obb(a: &Obb3, b: &Obb3) -> bool {
403 let t = b.center - a.center;
404 let half_a = [a.half_extents.x, a.half_extents.y, a.half_extents.z];
405 let half_b = [b.half_extents.x, b.half_extents.y, b.half_extents.z];
406
407 let axes_a = a.axes;
409 let axes_b = b.axes;
410
411 let project_half = |axes: &[Point3; 3], halves: &[f64; 3], axis: Point3| -> f64 {
413 halves[0] * axes[0].dot(axis).abs()
414 + halves[1] * axes[1].dot(axis).abs()
415 + halves[2] * axes[2].dot(axis).abs()
416 };
417
418 let test_axis = |axis: Point3| -> bool {
419 let len = axis.len_sq();
420 if len < 1e-10 { return true; } let axis = axis * (1.0 / len.sqrt());
422 let dist = t.dot(axis).abs();
423 let ra = project_half(&axes_a, &half_a, axis);
424 let rb = project_half(&axes_b, &half_b, axis);
425 dist <= ra + rb
426 };
427
428 for i in 0..3 {
430 if !test_axis(axes_a[i]) { return false; }
431 }
432 for i in 0..3 {
434 if !test_axis(axes_b[i]) { return false; }
435 }
436 for i in 0..3 {
438 for j in 0..3 {
439 let axis = axes_a[i].cross(axes_b[j]);
440 if !test_axis(axis) { return false; }
441 }
442 }
443 true
444}
445
446pub fn capsule_capsule(a: &Capsule, b: &Capsule) -> bool {
448 let dist = segment_segment_dist_3d(a.a, a.b, b.a, b.b);
449 dist <= a.radius + b.radius
450}
451
452pub fn point_in_triangle(p: Point2, tri: &Triangle2) -> bool {
454 let d1 = (p - tri.a).cross(tri.b - tri.a);
455 let d2 = (p - tri.b).cross(tri.c - tri.b);
456 let d3 = (p - tri.c).cross(tri.a - tri.c);
457 let has_neg = d1 < 0.0 || d2 < 0.0 || d3 < 0.0;
458 let has_pos = d1 > 0.0 || d2 > 0.0 || d3 > 0.0;
459 !(has_neg && has_pos)
460}
461
462pub fn point_in_aabb(p: Point3, b: &Aabb3) -> bool {
464 p.x >= b.min.x && p.x <= b.max.x &&
465 p.y >= b.min.y && p.y <= b.max.y &&
466 p.z >= b.min.z && p.z <= b.max.z
467}
468
469pub fn point_in_obb(p: Point3, obb: &Obb3) -> bool {
471 let d = p - obb.center;
472 let hx = d.dot(obb.axes[0]).abs();
473 let hy = d.dot(obb.axes[1]).abs();
474 let hz = d.dot(obb.axes[2]).abs();
475 hx <= obb.half_extents.x && hy <= obb.half_extents.y && hz <= obb.half_extents.z
476}
477
478pub fn segment_segment_2d(a: &Segment2, b: &Segment2) -> Option<Point2> {
480 let r = a.b - a.a;
481 let s = b.b - b.a;
482 let denom = r.cross(s);
483 if denom.abs() < 1e-12 { return None; }
484 let diff = b.a - a.a;
485 let t = diff.cross(s) / denom;
486 let u = diff.cross(r) / denom;
487 if (0.0..=1.0).contains(&t) && (0.0..=1.0).contains(&u) {
488 Some(a.a + r * t)
489 } else {
490 None
491 }
492}
493
494fn clamp01(x: f64) -> f64 { x.clamp(0.0, 1.0) }
499
500pub fn point_to_segment(p: Point3, seg_a: Point3, seg_b: Point3) -> f64 {
502 let ab = seg_b - seg_a;
503 let t = clamp01((p - seg_a).dot(ab) / ab.dot(ab).max(1e-300));
504 (seg_a + ab * t).dist(p)
505}
506
507fn segment_segment_dist_3d(a0: Point3, a1: Point3, b0: Point3, b1: Point3) -> f64 {
509 let d1 = a1 - a0;
510 let d2 = b1 - b0;
511 let r = a0 - b0;
512 let a = d1.dot(d1);
513 let e = d2.dot(d2);
514 let f = d2.dot(r);
515 let (s, t) = if a < 1e-10 {
516 (0.0, clamp01(f / e.max(1e-10)))
517 } else {
518 let c = d1.dot(r);
519 if e < 1e-10 {
520 (clamp01(-c / a), 0.0)
521 } else {
522 let b = d1.dot(d2);
523 let denom = a * e - b * b;
524 let s = if denom.abs() > 1e-10 { clamp01((b * f - c * e) / denom) } else { 0.0 };
525 let t = (b * s + f) / e;
526 if t < 0.0 {
527 (clamp01(-c / a), 0.0)
528 } else if t > 1.0 {
529 (clamp01((b - c) / a), 1.0)
530 } else {
531 (s, t)
532 }
533 }
534 };
535 let p1 = a0 + d1 * s;
536 let p2 = b0 + d2 * t;
537 p1.dist(p2)
538}
539
540pub fn segment_to_segment(a: &Segment2, b: &Segment2) -> f64 {
542 let a0 = Point3::new(a.a.x, a.a.y, 0.0);
543 let a1 = Point3::new(a.b.x, a.b.y, 0.0);
544 let b0 = Point3::new(b.a.x, b.a.y, 0.0);
545 let b1 = Point3::new(b.b.x, b.b.y, 0.0);
546 segment_segment_dist_3d(a0, a1, b0, b1)
547}
548
549pub fn point_to_plane(p: Point3, plane: &Plane) -> f64 {
551 plane.normal.dot(p) + plane.d
552}
553
554pub fn point_to_aabb(p: Point3, b: &Aabb3) -> f64 {
556 let dx = (b.min.x - p.x).max(0.0).max(p.x - b.max.x);
557 let dy = (b.min.y - p.y).max(0.0).max(p.y - b.max.y);
558 let dz = (b.min.z - p.z).max(0.0).max(p.z - b.max.z);
559 (dx * dx + dy * dy + dz * dz).sqrt()
560}
561
562pub fn point_to_triangle(p: Point3, tri: &Triangle3) -> f64 {
564 let ab = tri.b - tri.a;
565 let ac = tri.c - tri.a;
566 let ap = p - tri.a;
567 let d1 = ab.dot(ap);
568 let d2 = ac.dot(ap);
569 if d1 <= 0.0 && d2 <= 0.0 { return p.dist(tri.a); }
570 let bp = p - tri.b;
571 let d3 = ab.dot(bp);
572 let d4 = ac.dot(bp);
573 if d3 >= 0.0 && d4 <= d3 { return p.dist(tri.b); }
574 let vc = d1 * d4 - d3 * d2;
575 if vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0 {
576 let v = d1 / (d1 - d3);
577 return p.dist(tri.a + ab * v);
578 }
579 let cp = p - tri.c;
580 let d5 = ab.dot(cp);
581 let d6 = ac.dot(cp);
582 if d6 >= 0.0 && d5 <= d6 { return p.dist(tri.c); }
583 let vb = d5 * d2 - d1 * d6;
584 if vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0 {
585 let w = d2 / (d2 - d6);
586 return p.dist(tri.a + ac * w);
587 }
588 let va = d3 * d6 - d5 * d4;
589 if va <= 0.0 && d4 - d3 >= 0.0 && d5 - d6 >= 0.0 {
590 let w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
591 return p.dist(tri.b + (tri.c - tri.b) * w);
592 }
593 let denom = 1.0 / (va + vb + vc);
594 let v = vb * denom;
595 let w = vc * denom;
596 let closest = tri.a + ab * v + ac * w;
597 p.dist(closest)
598}
599
600pub fn sphere_to_aabb(s: &Sphere, b: &Aabb3) -> f64 {
602 point_to_aabb(s.center, b) - s.radius
603}
604
605pub fn convex_hull_2d(points: &[Point2]) -> Vec<Point2> {
611 let n = points.len();
612 if n < 3 { return points.to_vec(); }
613 let start = points
615 .iter()
616 .enumerate()
617 .min_by(|(_, a), (_, b)| a.x.partial_cmp(&b.x).unwrap().then(a.y.partial_cmp(&b.y).unwrap()))
618 .unwrap()
619 .0;
620 let mut hull = Vec::new();
621 let mut current = start;
622 loop {
623 hull.push(points[current]);
624 let mut next = (current + 1) % n;
625 for i in 0..n {
626 let cross = (points[next] - points[current]).cross(points[i] - points[current]);
627 if cross < 0.0 { next = i; }
628 }
629 current = next;
630 if current == start { break; }
631 if hull.len() > n { break; } }
633 hull
634}
635
636pub fn convex_hull_3d(points: &[Point3]) -> Vec<[usize; 3]> {
638 let n = points.len();
639 if n < 4 { return vec![]; }
640
641 let mut extreme = [0usize; 6];
643 for (i, p) in points.iter().enumerate() {
644 if p.x < points[extreme[0]].x { extreme[0] = i; }
645 if p.x > points[extreme[1]].x { extreme[1] = i; }
646 if p.y < points[extreme[2]].y { extreme[2] = i; }
647 if p.y > points[extreme[3]].y { extreme[3] = i; }
648 if p.z < points[extreme[4]].z { extreme[4] = i; }
649 if p.z > points[extreme[5]].z { extreme[5] = i; }
650 }
651
652 let mut simplex = vec![extreme[0], extreme[1]];
654 for &e in &extreme[2..] {
655 if !simplex.contains(&e) { simplex.push(e); }
656 if simplex.len() == 4 { break; }
657 }
658 for i in 0..n {
660 if simplex.len() >= 4 { break; }
661 if !simplex.contains(&i) { simplex.push(i); }
662 }
663 if simplex.len() < 4 { return vec![]; }
664
665 let (i0, i1, i2, i3) = (simplex[0], simplex[1], simplex[2], simplex[3]);
667 let mut faces: Vec<[usize; 3]> = vec![
668 [i0, i1, i2], [i0, i2, i3], [i0, i3, i1], [i1, i3, i2],
669 ];
670
671 let centroid = points.iter().fold(Point3::zero(), |acc, p| acc + *p) * (1.0 / n as f64);
673 for face in &mut faces {
674 let n = (points[face[1]] - points[face[0]]).cross(points[face[2]] - points[face[0]]);
675 if n.dot(centroid - points[face[0]]) > 0.0 {
676 face.swap(0, 1);
677 }
678 }
679
680 for p_idx in 0..n {
682 if simplex.contains(&p_idx) { continue; }
683 let p = points[p_idx];
684
685 let visible: Vec<bool> = faces.iter().map(|face| {
687 let normal = (points[face[1]] - points[face[0]]).cross(points[face[2]] - points[face[0]]);
688 normal.dot(p - points[face[0]]) > 1e-10
689 }).collect();
690
691 if !visible.iter().any(|&v| v) { continue; }
692
693 let mut horizon: Vec<(usize, usize)> = Vec::new();
695 for (fi, face) in faces.iter().enumerate() {
696 if !visible[fi] { continue; }
697 let edges = [(face[0], face[1]), (face[1], face[2]), (face[2], face[0])];
698 for &(ea, eb) in &edges {
699 let shared = faces.iter().enumerate().any(|(fj, f2)| {
701 !visible[fj] && (
702 (f2[0] == eb && f2[1] == ea) ||
703 (f2[1] == eb && f2[2] == ea) ||
704 (f2[2] == eb && f2[0] == ea)
705 )
706 });
707 if shared { horizon.push((ea, eb)); }
708 }
709 }
710
711 let kept: Vec<[usize; 3]> = faces.iter().enumerate()
713 .filter(|(i, _)| !visible[*i])
714 .map(|(_, f)| *f)
715 .collect();
716 faces = kept;
717 for (ea, eb) in horizon {
718 let normal = (points[eb] - points[ea]).cross(points[p_idx] - points[ea]);
719 let centroid_side = normal.dot(centroid - points[ea]);
720 if centroid_side > 0.0 {
721 faces.push([ea, p_idx, eb]);
722 } else {
723 faces.push([ea, eb, p_idx]);
724 }
725 }
726 }
727 faces
728}
729
730pub fn point_in_convex_polygon(p: Point2, hull: &[Point2]) -> bool {
732 let n = hull.len();
733 if n < 3 { return false; }
734 for i in 0..n {
735 let a = hull[i];
736 let b = hull[(i + 1) % n];
737 if (b - a).cross(p - a) < 0.0 { return false; }
738 }
739 true
740}
741
742pub fn convex_polygon_area(hull: &[Point2]) -> f64 {
744 polygon_area(hull).abs()
745}
746
747pub fn ear_clipping(polygon: &[Point2]) -> Vec<[usize; 3]> {
754 let n = polygon.len();
755 if n < 3 { return vec![]; }
756 let mut result = Vec::new();
757 let mut indices: Vec<usize> = (0..n).collect();
758
759 while indices.len() > 3 {
760 let len = indices.len();
761 let mut found_ear = false;
762 for i in 0..len {
763 let prev = (i + len - 1) % len;
764 let next = (i + 1) % len;
765 let a = polygon[indices[prev]];
766 let b = polygon[indices[i]];
767 let c = polygon[indices[next]];
768 let cross = (b - a).cross(c - b);
770 if cross <= 0.0 { continue; }
771 let tri = Triangle2 { a, b, c };
773 let ear_valid = !indices.iter().enumerate().any(|(j, &vi)| {
774 j != prev && j != i && j != next
775 && point_in_triangle(polygon[vi], &tri)
776 });
777 if ear_valid {
778 result.push([indices[prev], indices[i], indices[next]]);
779 indices.remove(i);
780 found_ear = true;
781 break;
782 }
783 }
784 if !found_ear { break; }
785 }
786 if indices.len() == 3 {
787 result.push([indices[0], indices[1], indices[2]]);
788 }
789 result
790}
791
792pub fn delaunay_2d(points: &[Point2]) -> Vec<[usize; 3]> {
795 let n = points.len();
796 if n < 3 { return vec![]; }
797
798 let mut pts = points.to_vec();
800 let min_x = pts.iter().map(|p| p.x).fold(f64::INFINITY, f64::min);
801 let max_x = pts.iter().map(|p| p.x).fold(f64::NEG_INFINITY, f64::max);
802 let min_y = pts.iter().map(|p| p.y).fold(f64::INFINITY, f64::min);
803 let max_y = pts.iter().map(|p| p.y).fold(f64::NEG_INFINITY, f64::max);
804 let dx = max_x - min_x;
805 let dy = max_y - min_y;
806 let delta_max = dx.max(dy);
807 let mid_x = (min_x + max_x) * 0.5;
808 let mid_y = (min_y + max_y) * 0.5;
809
810 let st0 = Point2::new(mid_x - 20.0 * delta_max, mid_y - delta_max);
811 let st1 = Point2::new(mid_x, mid_y + 20.0 * delta_max);
812 let st2 = Point2::new(mid_x + 20.0 * delta_max, mid_y - delta_max);
813 let si0 = n;
814 let si1 = n + 1;
815 let si2 = n + 2;
816 pts.push(st0); pts.push(st1); pts.push(st2);
817
818 let mut triangles: Vec<[usize; 3]> = vec![[si0, si1, si2]];
819
820 for i in 0..n {
821 let p = pts[i];
822 let mut bad_triangles: Vec<[usize; 3]> = Vec::new();
823 let mut good_triangles: Vec<[usize; 3]> = Vec::new();
824
825 for &tri in &triangles {
826 if circumcircle_contains(&pts[tri[0]], &pts[tri[1]], &pts[tri[2]], &p) {
827 bad_triangles.push(tri);
828 } else {
829 good_triangles.push(tri);
830 }
831 }
832
833 let mut boundary: Vec<(usize, usize)> = Vec::new();
835 for &tri in &bad_triangles {
836 let edges = [(tri[0], tri[1]), (tri[1], tri[2]), (tri[2], tri[0])];
837 for &(ea, eb) in &edges {
838 let shared = bad_triangles.iter().any(|&t2| {
839 t2 != tri && (
840 (t2[0] == eb && t2[1] == ea)
841 || (t2[1] == eb && t2[2] == ea)
842 || (t2[2] == eb && t2[0] == ea)
843 || (t2[0] == ea && t2[1] == eb)
844 || (t2[1] == ea && t2[2] == eb)
845 || (t2[2] == ea && t2[0] == eb)
846 )
847 });
848 if !shared { boundary.push((ea, eb)); }
849 }
850 }
851
852 triangles = good_triangles;
853 for (ea, eb) in boundary {
854 triangles.push([ea, eb, i]);
855 }
856 }
857
858 triangles.retain(|t| t[0] < n && t[1] < n && t[2] < n);
860 triangles
861}
862
863fn circumcircle_contains(a: &Point2, b: &Point2, c: &Point2, p: &Point2) -> bool {
864 let ax = a.x - p.x; let ay = a.y - p.y;
865 let bx = b.x - p.x; let by = b.y - p.y;
866 let cx = c.x - p.x; let cy = c.y - p.y;
867 let det = ax * (by * (cx * cx + cy * cy) - cy * (bx * bx + by * by))
868 - ay * (bx * (cx * cx + cy * cy) - cx * (bx * bx + by * by))
869 + (ax * ax + ay * ay) * (bx * cy - by * cx);
870 det > 0.0
871}
872
873pub fn voronoi_2d(points: &[Point2], bounds: Aabb2) -> Vec<Vec<Point2>> {
876 let tris = delaunay_2d(points);
877 let n = points.len();
878 let mut cells: Vec<Vec<Point2>> = vec![Vec::new(); n];
879
880 let circumcenter = |a: &Point2, b: &Point2, c: &Point2| -> Point2 {
882 let ax = a.x; let ay = a.y;
883 let bx = b.x; let by = b.y;
884 let cx = c.x; let cy = c.y;
885 let d = 2.0 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by));
886 if d.abs() < 1e-12 { return Point2::new((ax + bx + cx) / 3.0, (ay + by + cy) / 3.0); }
887 let ux = ((ax * ax + ay * ay) * (by - cy)
888 + (bx * bx + by * by) * (cy - ay)
889 + (cx * cx + cy * cy) * (ay - by)) / d;
890 let uy = ((ax * ax + ay * ay) * (cx - bx)
891 + (bx * bx + by * by) * (ax - cx)
892 + (cx * cx + cy * cy) * (bx - ax)) / d;
893 Point2::new(ux, uy)
894 };
895
896 for tri in &tris {
898 let cc = circumcenter(&points[tri[0]], &points[tri[1]], &points[tri[2]]);
899 let cc = Point2::new(
901 cc.x.clamp(bounds.min.x, bounds.max.x),
902 cc.y.clamp(bounds.min.y, bounds.max.y),
903 );
904 for &vi in tri {
905 cells[vi].push(cc);
906 }
907 }
908
909 for (i, cell) in cells.iter_mut().enumerate() {
911 let center = points[i];
912 cell.sort_by(|a, b| {
913 let angle_a = (a.y - center.y).atan2(a.x - center.x);
914 let angle_b = (b.y - center.y).atan2(b.x - center.x);
915 angle_a.partial_cmp(&angle_b).unwrap()
916 });
917 cell.dedup_by(|a, b| (a.x - b.x).abs() < 1e-10 && (a.y - b.y).abs() < 1e-10);
918 }
919 cells
920}
921
922pub fn polygon_area(pts: &[Point2]) -> f64 {
928 let n = pts.len();
929 if n < 3 { return 0.0; }
930 let mut area = 0.0;
931 for i in 0..n {
932 let j = (i + 1) % n;
933 area += pts[i].x * pts[j].y;
934 area -= pts[j].x * pts[i].y;
935 }
936 area * 0.5
937}
938
939pub fn polygon_centroid(pts: &[Point2]) -> Point2 {
941 let n = pts.len();
942 if n == 0 { return Point2::zero(); }
943 let area = polygon_area(pts);
944 if area.abs() < 1e-12 {
945 let x = pts.iter().map(|p| p.x).sum::<f64>() / n as f64;
946 let y = pts.iter().map(|p| p.y).sum::<f64>() / n as f64;
947 return Point2::new(x, y);
948 }
949 let mut cx = 0.0;
950 let mut cy = 0.0;
951 for i in 0..n {
952 let j = (i + 1) % n;
953 let cross = pts[i].x * pts[j].y - pts[j].x * pts[i].y;
954 cx += (pts[i].x + pts[j].x) * cross;
955 cy += (pts[i].y + pts[j].y) * cross;
956 }
957 let factor = 1.0 / (6.0 * area);
958 Point2::new(cx * factor, cy * factor)
959}
960
961pub fn polygon_is_convex(pts: &[Point2]) -> bool {
963 let n = pts.len();
964 if n < 3 { return true; }
965 let mut sign = 0.0f64;
966 for i in 0..n {
967 let a = pts[i];
968 let b = pts[(i + 1) % n];
969 let c = pts[(i + 2) % n];
970 let cross = (b - a).cross(c - b);
971 if cross != 0.0 {
972 if sign == 0.0 { sign = cross.signum(); }
973 else if cross.signum() != sign { return false; }
974 }
975 }
976 true
977}
978
979pub fn polygon_winding(pts: &[Point2]) -> f64 {
981 polygon_area(pts).signum()
982}
983
984pub fn sutherland_hodgman(subject: &[Point2], clip: &[Point2]) -> Vec<Point2> {
986 if subject.is_empty() || clip.is_empty() { return vec![]; }
987 let mut output = subject.to_vec();
988 let m = clip.len();
989 for i in 0..m {
990 if output.is_empty() { return vec![]; }
991 let input = output.clone();
992 output.clear();
993 let edge_start = clip[i];
994 let edge_end = clip[(i + 1) % m];
995 let inside = |p: Point2| -> bool {
996 (edge_end - edge_start).cross(p - edge_start) >= 0.0
997 };
998 let intersect = |a: Point2, b: Point2| -> Point2 {
999 let ab = b - a;
1000 let es = edge_end - edge_start;
1001 let num = (edge_start - a).cross(es);
1002 let den = ab.cross(es);
1003 if den.abs() < 1e-12 { return a; }
1004 a + ab * (num / den)
1005 };
1006 for j in 0..input.len() {
1007 let current = input[j];
1008 let previous = input[(j + input.len() - 1) % input.len()];
1009 if inside(current) {
1010 if !inside(previous) { output.push(intersect(previous, current)); }
1011 output.push(current);
1012 } else if inside(previous) {
1013 output.push(intersect(previous, current));
1014 }
1015 }
1016 }
1017 output
1018}
1019
1020pub fn minkowski_sum(a: &[Point2], b: &[Point2]) -> Vec<Point2> {
1022 if a.is_empty() || b.is_empty() { return vec![]; }
1023 let start_of = |pts: &[Point2]| -> usize {
1025 pts.iter().enumerate()
1026 .min_by(|(_, p), (_, q)| {
1027 p.y.partial_cmp(&q.y).unwrap().then(p.x.partial_cmp(&q.x).unwrap())
1028 })
1029 .unwrap()
1030 .0
1031 };
1032 let ia = start_of(a);
1033 let ib = start_of(b);
1034 let na = a.len();
1035 let nb = b.len();
1036 let mut result = Vec::new();
1037 let mut i = 0;
1038 let mut j = 0;
1039 while i < na || j < nb {
1040 result.push(a[(ia + i) % na] + b[(ib + j) % nb]);
1041 let ea = a[(ia + i + 1) % na] - a[(ia + i) % na];
1042 let eb = b[(ib + j + 1) % nb] - b[(ib + j) % nb];
1043 let cross = ea.cross(eb);
1044 if i >= na { j += 1; }
1045 else if j >= nb { i += 1; }
1046 else if cross > 0.0 { i += 1; }
1047 else if cross < 0.0 { j += 1; }
1048 else { i += 1; j += 1; }
1049 }
1050 result
1051}
1052
1053fn support_3d(shape: &[Point3], dir: Point3) -> Point3 {
1058 shape.iter().copied()
1059 .max_by(|a, b| a.dot(dir).partial_cmp(&b.dot(dir)).unwrap())
1060 .unwrap_or(Point3::zero())
1061}
1062
1063fn support_minkowski_diff(a: &[Point3], b: &[Point3], dir: Point3) -> Point3 {
1064 support_3d(a, dir) - support_3d(b, -dir)
1065}
1066
1067fn triple_product(a: Point3, b: Point3, c: Point3) -> Point3 {
1068 b * a.dot(c) - c * a.dot(b)
1069}
1070
1071pub fn gjk(shape_a: &[Point3], shape_b: &[Point3]) -> bool {
1073 let mut dir = support_minkowski_diff(shape_a, shape_b, Point3::new(1.0, 0.0, 0.0));
1074 if dir.len_sq() < 1e-20 {
1075 dir = Point3::new(0.0, 1.0, 0.0);
1076 }
1077 let mut simplex: Vec<Point3> = Vec::new();
1078 simplex.push(dir);
1079 let mut search_dir = -dir;
1080 for _ in 0..64 {
1081 let a = support_minkowski_diff(shape_a, shape_b, search_dir);
1082 if a.dot(search_dir) < 0.0 { return false; }
1083 simplex.push(a);
1084 if do_simplex(&mut simplex, &mut search_dir) { return true; }
1085 }
1086 false
1087}
1088
1089fn do_simplex(simplex: &mut Vec<Point3>, dir: &mut Point3) -> bool {
1090 match simplex.len() {
1091 2 => {
1092 let b = simplex[0];
1093 let a = simplex[1];
1094 let ab = b - a;
1095 let ao = -a;
1096 if ab.dot(ao) > 0.0 {
1097 *dir = triple_product(ab, ao, ab);
1098 } else {
1099 *simplex = vec![a];
1100 *dir = ao;
1101 }
1102 false
1103 }
1104 3 => {
1105 let c = simplex[0]; let b = simplex[1]; let a = simplex[2];
1106 let ab = b - a; let ac = c - a; let ao = -a;
1107 let abc = ab.cross(ac);
1108 if abc.cross(ac).dot(ao) > 0.0 {
1109 if ac.dot(ao) > 0.0 {
1110 *simplex = vec![c, a]; *dir = triple_product(ac, ao, ac);
1111 } else {
1112 *simplex = vec![b, a]; return do_simplex(simplex, dir);
1113 }
1114 } else if ab.cross(abc).dot(ao) > 0.0 {
1115 *simplex = vec![b, a]; return do_simplex(simplex, dir);
1116 } else if abc.dot(ao) > 0.0 {
1117 *dir = abc;
1118 } else {
1119 simplex.swap(0, 1); *dir = -abc;
1120 }
1121 false
1122 }
1123 4 => {
1124 let d = simplex[0]; let c = simplex[1];
1125 let b = simplex[2]; let a = simplex[3];
1126 let ab = b - a; let ac = c - a;
1127 let ad = d - a; let ao = -a;
1128 let abc = ab.cross(ac);
1129 let acd = ac.cross(ad);
1130 let adb = ad.cross(ab);
1131 if abc.dot(ao) > 0.0 {
1132 *simplex = vec![c, b, a];
1133 return do_simplex(simplex, dir);
1134 }
1135 if acd.dot(ao) > 0.0 {
1136 *simplex = vec![d, c, a];
1137 return do_simplex(simplex, dir);
1138 }
1139 if adb.dot(ao) > 0.0 {
1140 *simplex = vec![b, d, a];
1141 return do_simplex(simplex, dir);
1142 }
1143 true }
1145 _ => { *dir = Point3::new(1.0, 0.0, 0.0); false }
1146 }
1147}
1148
1149pub fn gjk_closest(shape_a: &[Point3], shape_b: &[Point3]) -> Option<(Point3, Point3, f64)> {
1151 if gjk(shape_a, shape_b) { return None; }
1152 let mut dir = shape_a[0] - shape_b[0];
1154 if dir.len_sq() < 1e-20 { return None; }
1155 let mut best_dist = f64::INFINITY;
1156 let mut best_pa = Point3::zero();
1157 let mut best_pb = Point3::zero();
1158 for _ in 0..64 {
1159 dir = dir.normalize();
1160 let pa = support_3d(shape_a, dir);
1161 let pb = support_3d(shape_b, -dir);
1162 let d = (pa - pb).len();
1163 if d < best_dist {
1164 best_dist = d;
1165 best_pa = pa;
1166 best_pb = pb;
1167 }
1168 dir = pb - pa;
1169 if dir.len_sq() < 1e-20 { break; }
1170 }
1171 Some((best_pa, best_pb, best_dist))
1172}
1173
1174pub fn epa(shape_a: &[Point3], shape_b: &[Point3]) -> Option<(Point3, f64)> {
1176 if !gjk(shape_a, shape_b) { return None; }
1177 let dirs = [
1179 Point3::new(1.0, 0.0, 0.0), Point3::new(-1.0, 0.0, 0.0),
1180 Point3::new(0.0, 1.0, 0.0), Point3::new(0.0, -1.0, 0.0),
1181 Point3::new(0.0, 0.0, 1.0), Point3::new(0.0, 0.0, -1.0),
1182 ];
1183 let mut polytope: Vec<Point3> = dirs.iter()
1184 .map(|&d| support_minkowski_diff(shape_a, shape_b, d))
1185 .collect();
1186 let mut faces: Vec<[usize; 3]> = vec![
1187 [0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 1],
1188 [5, 2, 1], [5, 3, 2], [5, 4, 3], [5, 1, 4],
1189 ];
1190
1191 for _ in 0..64 {
1192 let (min_dist, min_face_idx, min_normal) = faces.iter().enumerate()
1194 .map(|(fi, face)| {
1195 let a = polytope[face[0]]; let b = polytope[face[1]]; let c = polytope[face[2]];
1196 let n = (b - a).cross(c - a);
1197 let len = n.len();
1198 if len < 1e-12 { return (f64::INFINITY, fi, Point3::new(0.0, 0.0, 1.0)); }
1199 let n = n * (1.0 / len);
1200 let d = n.dot(a);
1201 (d.abs(), fi, if d >= 0.0 { n } else { -n })
1202 })
1203 .min_by(|a, b| a.0.partial_cmp(&b.0).unwrap())
1204 .unwrap();
1205
1206 let _ = min_face_idx;
1207 let support = support_minkowski_diff(shape_a, shape_b, min_normal);
1208 let new_dist = min_normal.dot(support);
1209
1210 if (new_dist - min_dist).abs() < 1e-6 {
1211 return Some((min_normal, min_dist));
1212 }
1213
1214 let si = polytope.len();
1216 polytope.push(support);
1217 let mut edge_set: Vec<(usize, usize)> = Vec::new();
1218 faces.retain(|face| {
1219 let a = polytope[face[0]]; let b = polytope[face[1]]; let c = polytope[face[2]];
1220 let n = (b - a).cross(c - a);
1221 if n.dot(support - a) > 0.0 {
1222 for &(ea, eb) in &[(face[0], face[1]), (face[1], face[2]), (face[2], face[0])] {
1223 let rev_pos = edge_set.iter().position(|&(x, y)| x == eb && y == ea);
1224 if let Some(pos) = rev_pos { edge_set.remove(pos); }
1225 else { edge_set.push((ea, eb)); }
1226 }
1227 false
1228 } else { true }
1229 });
1230 for (ea, eb) in edge_set {
1231 faces.push([ea, eb, si]);
1232 }
1233 }
1234 None
1235}
1236
1237#[cfg(test)]
1248mod tests {
1249 use super::*;
1250
1251 #[test]
1252 fn test_ray_aabb_hit() {
1253 let ray = Ray3 { origin: Point3::new(0.0, 0.0, 0.0), dir: Point3::new(1.0, 0.0, 0.0) };
1254 let aabb = Aabb3 { min: Point3::new(2.0, -1.0, -1.0), max: Point3::new(4.0, 1.0, 1.0) };
1255 let t = ray_aabb(&ray, &aabb).unwrap();
1256 assert!((t - 2.0).abs() < 1e-10);
1257 }
1258
1259 #[test]
1260 fn test_ray_aabb_miss() {
1261 let ray = Ray3 { origin: Point3::new(0.0, 0.0, 0.0), dir: Point3::new(0.0, 0.0, 1.0) };
1262 let aabb = Aabb3 { min: Point3::new(2.0, -1.0, -1.0), max: Point3::new(4.0, 1.0, 1.0) };
1263 assert!(ray_aabb(&ray, &aabb).is_none());
1264 }
1265
1266 #[test]
1267 fn test_ray_sphere() {
1268 let ray = Ray3 { origin: Point3::new(-5.0, 0.0, 0.0), dir: Point3::new(1.0, 0.0, 0.0) };
1269 let sphere = Sphere { center: Point3::new(0.0, 0.0, 0.0), radius: 1.0 };
1270 let t = ray_sphere(&ray, &sphere).unwrap();
1271 assert!((t - 4.0).abs() < 1e-10);
1272 }
1273
1274 #[test]
1275 fn test_ray_triangle_moller_trumbore() {
1276 let ray = Ray3 { origin: Point3::new(0.0, 0.0, -1.0), dir: Point3::new(0.0, 0.0, 1.0) };
1277 let tri = Triangle3 {
1278 a: Point3::new(-1.0, -1.0, 0.0),
1279 b: Point3::new(1.0, -1.0, 0.0),
1280 c: Point3::new(0.0, 1.0, 0.0),
1281 };
1282 let (t, u, v) = ray_triangle(&ray, &tri).unwrap();
1283 assert!((t - 1.0).abs() < 1e-10);
1284 let _ = (u, v);
1285 }
1286
1287 #[test]
1288 fn test_aabb_aabb_overlap() {
1289 let a = Aabb3 { min: Point3::new(0.0, 0.0, 0.0), max: Point3::new(2.0, 2.0, 2.0) };
1290 let b = Aabb3 { min: Point3::new(1.0, 1.0, 1.0), max: Point3::new(3.0, 3.0, 3.0) };
1291 assert!(aabb_aabb(&a, &b));
1292 }
1293
1294 #[test]
1295 fn test_aabb_aabb_no_overlap() {
1296 let a = Aabb3 { min: Point3::new(0.0, 0.0, 0.0), max: Point3::new(1.0, 1.0, 1.0) };
1297 let b = Aabb3 { min: Point3::new(2.0, 2.0, 2.0), max: Point3::new(3.0, 3.0, 3.0) };
1298 assert!(!aabb_aabb(&a, &b));
1299 }
1300
1301 #[test]
1302 fn test_sphere_sphere() {
1303 let a = Sphere { center: Point3::new(0.0, 0.0, 0.0), radius: 1.0 };
1304 let b = Sphere { center: Point3::new(1.5, 0.0, 0.0), radius: 1.0 };
1305 assert!(sphere_sphere(&a, &b));
1306 }
1307
1308 #[test]
1309 fn test_point_in_triangle() {
1310 let tri = Triangle2 {
1311 a: Point2::new(0.0, 0.0),
1312 b: Point2::new(1.0, 0.0),
1313 c: Point2::new(0.5, 1.0),
1314 };
1315 assert!(point_in_triangle(Point2::new(0.5, 0.4), &tri));
1316 assert!(!point_in_triangle(Point2::new(2.0, 0.0), &tri));
1317 }
1318
1319 #[test]
1320 fn test_segment_segment_2d() {
1321 let a = Segment2 { a: Point2::new(0.0, 0.0), b: Point2::new(2.0, 2.0) };
1322 let b = Segment2 { a: Point2::new(0.0, 2.0), b: Point2::new(2.0, 0.0) };
1323 let p = segment_segment_2d(&a, &b).unwrap();
1324 assert!((p.x - 1.0).abs() < 1e-10);
1325 assert!((p.y - 1.0).abs() < 1e-10);
1326 }
1327
1328 #[test]
1329 fn test_polygon_area() {
1330 let square = vec![
1331 Point2::new(0.0, 0.0), Point2::new(1.0, 0.0),
1332 Point2::new(1.0, 1.0), Point2::new(0.0, 1.0),
1333 ];
1334 assert!((polygon_area(&square) - 1.0).abs() < 1e-10);
1335 }
1336
1337 #[test]
1338 fn test_polygon_centroid() {
1339 let square = vec![
1340 Point2::new(0.0, 0.0), Point2::new(2.0, 0.0),
1341 Point2::new(2.0, 2.0), Point2::new(0.0, 2.0),
1342 ];
1343 let c = polygon_centroid(&square);
1344 assert!((c.x - 1.0).abs() < 1e-10);
1345 assert!((c.y - 1.0).abs() < 1e-10);
1346 }
1347
1348 #[test]
1349 fn test_convex_hull_2d() {
1350 let pts = vec![
1351 Point2::new(0.0, 0.0), Point2::new(1.0, 0.0),
1352 Point2::new(1.0, 1.0), Point2::new(0.0, 1.0),
1353 Point2::new(0.5, 0.5), ];
1355 let hull = convex_hull_2d(&pts);
1356 assert_eq!(hull.len(), 4);
1357 }
1358
1359 #[test]
1360 fn test_point_in_convex_polygon() {
1361 let square = vec![
1362 Point2::new(0.0, 0.0), Point2::new(1.0, 0.0),
1363 Point2::new(1.0, 1.0), Point2::new(0.0, 1.0),
1364 ];
1365 assert!(point_in_convex_polygon(Point2::new(0.5, 0.5), &square));
1366 assert!(!point_in_convex_polygon(Point2::new(2.0, 0.5), &square));
1367 }
1368
1369 #[test]
1370 fn test_ear_clipping() {
1371 let quad = vec![
1372 Point2::new(0.0, 0.0), Point2::new(1.0, 0.0),
1373 Point2::new(1.0, 1.0), Point2::new(0.0, 1.0),
1374 ];
1375 let tris = ear_clipping(&quad);
1376 assert_eq!(tris.len(), 2);
1377 }
1378
1379 #[test]
1380 fn test_delaunay_2d() {
1381 let pts = vec![
1382 Point2::new(0.0, 0.0), Point2::new(1.0, 0.0),
1383 Point2::new(0.5, 1.0), Point2::new(0.5, 0.3),
1384 ];
1385 let tris = delaunay_2d(&pts);
1386 assert!(!tris.is_empty());
1387 }
1388
1389 #[test]
1390 fn test_sutherland_hodgman() {
1391 let subject = vec![
1392 Point2::new(0.0, 0.0), Point2::new(2.0, 0.0),
1393 Point2::new(2.0, 2.0), Point2::new(0.0, 2.0),
1394 ];
1395 let clip = vec![
1396 Point2::new(1.0, 1.0), Point2::new(3.0, 1.0),
1397 Point2::new(3.0, 3.0), Point2::new(1.0, 3.0),
1398 ];
1399 let result = sutherland_hodgman(&subject, &clip);
1400 assert!(!result.is_empty());
1401 }
1402
1403 #[test]
1404 fn test_point_to_plane() {
1405 let plane = Plane { normal: Point3::new(0.0, 1.0, 0.0), d: -2.0 };
1406 let p = Point3::new(0.0, 5.0, 0.0);
1407 let d = point_to_plane(p, &plane);
1408 assert!((d - 3.0).abs() < 1e-10);
1409 }
1410
1411 #[test]
1412 fn test_point_to_aabb() {
1413 let aabb = Aabb3 { min: Point3::new(0.0, 0.0, 0.0), max: Point3::new(1.0, 1.0, 1.0) };
1414 assert!(point_to_aabb(Point3::new(0.5, 0.5, 0.5), &aabb).abs() < 1e-10);
1415 assert!((point_to_aabb(Point3::new(3.0, 0.5, 0.5), &aabb) - 2.0).abs() < 1e-10);
1416 }
1417
1418 #[test]
1419 fn test_obb_obb_overlap() {
1420 let center = Point3::new(0.0, 0.0, 0.0);
1421 let axes = [
1422 Point3::new(1.0, 0.0, 0.0),
1423 Point3::new(0.0, 1.0, 0.0),
1424 Point3::new(0.0, 0.0, 1.0),
1425 ];
1426 let a = Obb3 { center, axes, half_extents: Point3::new(1.0, 1.0, 1.0) };
1427 let b = Obb3 {
1428 center: Point3::new(1.5, 0.0, 0.0),
1429 axes,
1430 half_extents: Point3::new(1.0, 1.0, 1.0),
1431 };
1432 assert!(obb_obb(&a, &b));
1433 }
1434
1435 #[test]
1436 fn test_gjk_overlapping() {
1437 let a: Vec<Point3> = vec![
1438 Point3::new(-1.0, -1.0, -1.0), Point3::new(1.0, -1.0, -1.0),
1439 Point3::new(0.0, 1.0, -1.0), Point3::new(0.0, 0.0, 1.0),
1440 ];
1441 let b: Vec<Point3> = vec![
1442 Point3::new(-0.5, -0.5, -0.5), Point3::new(0.5, -0.5, -0.5),
1443 Point3::new(0.0, 0.5, -0.5), Point3::new(0.0, 0.0, 0.5),
1444 ];
1445 assert!(gjk(&a, &b));
1446 }
1447
1448 #[test]
1449 fn test_polygon_is_convex() {
1450 let convex = vec![
1451 Point2::new(0.0, 0.0), Point2::new(1.0, 0.0),
1452 Point2::new(1.0, 1.0), Point2::new(0.0, 1.0),
1453 ];
1454 assert!(polygon_is_convex(&convex));
1455 }
1456
1457 #[test]
1458 fn test_ray_plane() {
1459 let plane = Plane { normal: Point3::new(0.0, 1.0, 0.0), d: -5.0 };
1460 let ray = Ray3 { origin: Point3::new(0.0, 0.0, 0.0), dir: Point3::new(0.0, 1.0, 0.0) };
1461 let t = ray_plane(&ray, &plane).unwrap();
1462 assert!((t - 5.0).abs() < 1e-10);
1463 }
1464
1465 #[test]
1466 fn test_point_in_obb() {
1467 let obb = Obb3 {
1468 center: Point3::new(0.0, 0.0, 0.0),
1469 axes: [
1470 Point3::new(1.0, 0.0, 0.0),
1471 Point3::new(0.0, 1.0, 0.0),
1472 Point3::new(0.0, 0.0, 1.0),
1473 ],
1474 half_extents: Point3::new(2.0, 2.0, 2.0),
1475 };
1476 assert!(point_in_obb(Point3::new(1.0, 1.0, 1.0), &obb));
1477 assert!(!point_in_obb(Point3::new(3.0, 0.0, 0.0), &obb));
1478 }
1479
1480 #[test]
1481 fn test_sphere_aabb() {
1482 let s = Sphere { center: Point3::new(2.0, 0.0, 0.0), radius: 1.5 };
1483 let b = Aabb3 { min: Point3::new(0.0, 0.0, 0.0), max: Point3::new(1.0, 1.0, 1.0) };
1484 assert!(sphere_aabb(&s, &b));
1485 }
1486
1487 #[test]
1488 fn test_aabb_plane() {
1489 let aabb = Aabb3 { min: Point3::new(0.0, 0.0, 0.0), max: Point3::new(1.0, 1.0, 1.0) };
1490 let plane = Plane { normal: Point3::new(0.0, 1.0, 0.0), d: -10.0 };
1491 assert_eq!(aabb_plane(&aabb, &plane), Halfspace::Back);
1492 }
1493
1494 #[test]
1495 fn test_point3_ops() {
1496 let a = Point3::new(1.0, 2.0, 3.0);
1497 let b = Point3::new(4.0, 5.0, 6.0);
1498 let c = a + b;
1499 assert_eq!(c, Point3::new(5.0, 7.0, 9.0));
1500 let d = b - a;
1501 assert_eq!(d, Point3::new(3.0, 3.0, 3.0));
1502 let e = a.cross(b);
1503 let dot = a.dot(b);
1504 assert!((dot - 32.0).abs() < 1e-10);
1505 let _ = e;
1506 }
1507}