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 let orient = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
873 if orient > 0.0 { det > 0.0 } else { det < 0.0 }
874}
875
876pub fn voronoi_2d(points: &[Point2], bounds: Aabb2) -> Vec<Vec<Point2>> {
879 let tris = delaunay_2d(points);
880 let n = points.len();
881 let mut cells: Vec<Vec<Point2>> = vec![Vec::new(); n];
882
883 let circumcenter = |a: &Point2, b: &Point2, c: &Point2| -> Point2 {
885 let ax = a.x; let ay = a.y;
886 let bx = b.x; let by = b.y;
887 let cx = c.x; let cy = c.y;
888 let d = 2.0 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by));
889 if d.abs() < 1e-12 { return Point2::new((ax + bx + cx) / 3.0, (ay + by + cy) / 3.0); }
890 let ux = ((ax * ax + ay * ay) * (by - cy)
891 + (bx * bx + by * by) * (cy - ay)
892 + (cx * cx + cy * cy) * (ay - by)) / d;
893 let uy = ((ax * ax + ay * ay) * (cx - bx)
894 + (bx * bx + by * by) * (ax - cx)
895 + (cx * cx + cy * cy) * (bx - ax)) / d;
896 Point2::new(ux, uy)
897 };
898
899 for tri in &tris {
901 let cc = circumcenter(&points[tri[0]], &points[tri[1]], &points[tri[2]]);
902 let cc = Point2::new(
904 cc.x.clamp(bounds.min.x, bounds.max.x),
905 cc.y.clamp(bounds.min.y, bounds.max.y),
906 );
907 for &vi in tri {
908 cells[vi].push(cc);
909 }
910 }
911
912 for (i, cell) in cells.iter_mut().enumerate() {
914 let center = points[i];
915 cell.sort_by(|a, b| {
916 let angle_a = (a.y - center.y).atan2(a.x - center.x);
917 let angle_b = (b.y - center.y).atan2(b.x - center.x);
918 angle_a.partial_cmp(&angle_b).unwrap()
919 });
920 cell.dedup_by(|a, b| (a.x - b.x).abs() < 1e-10 && (a.y - b.y).abs() < 1e-10);
921 }
922 cells
923}
924
925pub fn polygon_area(pts: &[Point2]) -> f64 {
931 let n = pts.len();
932 if n < 3 { return 0.0; }
933 let mut area = 0.0;
934 for i in 0..n {
935 let j = (i + 1) % n;
936 area += pts[i].x * pts[j].y;
937 area -= pts[j].x * pts[i].y;
938 }
939 area * 0.5
940}
941
942pub fn polygon_centroid(pts: &[Point2]) -> Point2 {
944 let n = pts.len();
945 if n == 0 { return Point2::zero(); }
946 let area = polygon_area(pts);
947 if area.abs() < 1e-12 {
948 let x = pts.iter().map(|p| p.x).sum::<f64>() / n as f64;
949 let y = pts.iter().map(|p| p.y).sum::<f64>() / n as f64;
950 return Point2::new(x, y);
951 }
952 let mut cx = 0.0;
953 let mut cy = 0.0;
954 for i in 0..n {
955 let j = (i + 1) % n;
956 let cross = pts[i].x * pts[j].y - pts[j].x * pts[i].y;
957 cx += (pts[i].x + pts[j].x) * cross;
958 cy += (pts[i].y + pts[j].y) * cross;
959 }
960 let factor = 1.0 / (6.0 * area);
961 Point2::new(cx * factor, cy * factor)
962}
963
964pub fn polygon_is_convex(pts: &[Point2]) -> bool {
966 let n = pts.len();
967 if n < 3 { return true; }
968 let mut sign = 0.0f64;
969 for i in 0..n {
970 let a = pts[i];
971 let b = pts[(i + 1) % n];
972 let c = pts[(i + 2) % n];
973 let cross = (b - a).cross(c - b);
974 if cross != 0.0 {
975 if sign == 0.0 { sign = cross.signum(); }
976 else if cross.signum() != sign { return false; }
977 }
978 }
979 true
980}
981
982pub fn polygon_winding(pts: &[Point2]) -> f64 {
984 polygon_area(pts).signum()
985}
986
987pub fn sutherland_hodgman(subject: &[Point2], clip: &[Point2]) -> Vec<Point2> {
989 if subject.is_empty() || clip.is_empty() { return vec![]; }
990 let mut output = subject.to_vec();
991 let m = clip.len();
992 for i in 0..m {
993 if output.is_empty() { return vec![]; }
994 let input = output.clone();
995 output.clear();
996 let edge_start = clip[i];
997 let edge_end = clip[(i + 1) % m];
998 let inside = |p: Point2| -> bool {
999 (edge_end - edge_start).cross(p - edge_start) >= 0.0
1000 };
1001 let intersect = |a: Point2, b: Point2| -> Point2 {
1002 let ab = b - a;
1003 let es = edge_end - edge_start;
1004 let num = (edge_start - a).cross(es);
1005 let den = ab.cross(es);
1006 if den.abs() < 1e-12 { return a; }
1007 a + ab * (num / den)
1008 };
1009 for j in 0..input.len() {
1010 let current = input[j];
1011 let previous = input[(j + input.len() - 1) % input.len()];
1012 if inside(current) {
1013 if !inside(previous) { output.push(intersect(previous, current)); }
1014 output.push(current);
1015 } else if inside(previous) {
1016 output.push(intersect(previous, current));
1017 }
1018 }
1019 }
1020 output
1021}
1022
1023pub fn minkowski_sum(a: &[Point2], b: &[Point2]) -> Vec<Point2> {
1025 if a.is_empty() || b.is_empty() { return vec![]; }
1026 let start_of = |pts: &[Point2]| -> usize {
1028 pts.iter().enumerate()
1029 .min_by(|(_, p), (_, q)| {
1030 p.y.partial_cmp(&q.y).unwrap().then(p.x.partial_cmp(&q.x).unwrap())
1031 })
1032 .unwrap()
1033 .0
1034 };
1035 let ia = start_of(a);
1036 let ib = start_of(b);
1037 let na = a.len();
1038 let nb = b.len();
1039 let mut result = Vec::new();
1040 let mut i = 0;
1041 let mut j = 0;
1042 while i < na || j < nb {
1043 result.push(a[(ia + i) % na] + b[(ib + j) % nb]);
1044 let ea = a[(ia + i + 1) % na] - a[(ia + i) % na];
1045 let eb = b[(ib + j + 1) % nb] - b[(ib + j) % nb];
1046 let cross = ea.cross(eb);
1047 if i >= na { j += 1; }
1048 else if j >= nb { i += 1; }
1049 else if cross > 0.0 { i += 1; }
1050 else if cross < 0.0 { j += 1; }
1051 else { i += 1; j += 1; }
1052 }
1053 result
1054}
1055
1056fn support_3d(shape: &[Point3], dir: Point3) -> Point3 {
1061 shape.iter().copied()
1062 .max_by(|a, b| a.dot(dir).partial_cmp(&b.dot(dir)).unwrap())
1063 .unwrap_or(Point3::zero())
1064}
1065
1066fn support_minkowski_diff(a: &[Point3], b: &[Point3], dir: Point3) -> Point3 {
1067 support_3d(a, dir) - support_3d(b, -dir)
1068}
1069
1070fn triple_product(a: Point3, b: Point3, c: Point3) -> Point3 {
1071 b * a.dot(c) - c * a.dot(b)
1072}
1073
1074pub fn gjk(shape_a: &[Point3], shape_b: &[Point3]) -> bool {
1076 let mut dir = support_minkowski_diff(shape_a, shape_b, Point3::new(1.0, 0.0, 0.0));
1077 if dir.len_sq() < 1e-20 {
1078 dir = Point3::new(0.0, 1.0, 0.0);
1079 }
1080 let mut simplex: Vec<Point3> = Vec::new();
1081 simplex.push(dir);
1082 let mut search_dir = -dir;
1083 for _ in 0..64 {
1084 let a = support_minkowski_diff(shape_a, shape_b, search_dir);
1085 if a.dot(search_dir) < 0.0 { return false; }
1086 simplex.push(a);
1087 if do_simplex(&mut simplex, &mut search_dir) { return true; }
1088 }
1089 false
1090}
1091
1092fn do_simplex(simplex: &mut Vec<Point3>, dir: &mut Point3) -> bool {
1093 match simplex.len() {
1094 2 => {
1095 let b = simplex[0];
1096 let a = simplex[1];
1097 let ab = b - a;
1098 let ao = -a;
1099 if ab.dot(ao) > 0.0 {
1100 *dir = triple_product(ab, ao, ab);
1101 } else {
1102 *simplex = vec![a];
1103 *dir = ao;
1104 }
1105 false
1106 }
1107 3 => {
1108 let c = simplex[0]; let b = simplex[1]; let a = simplex[2];
1109 let ab = b - a; let ac = c - a; let ao = -a;
1110 let abc = ab.cross(ac);
1111 if abc.cross(ac).dot(ao) > 0.0 {
1112 if ac.dot(ao) > 0.0 {
1113 *simplex = vec![c, a]; *dir = triple_product(ac, ao, ac);
1114 } else {
1115 *simplex = vec![b, a]; return do_simplex(simplex, dir);
1116 }
1117 } else if ab.cross(abc).dot(ao) > 0.0 {
1118 *simplex = vec![b, a]; return do_simplex(simplex, dir);
1119 } else if abc.dot(ao) > 0.0 {
1120 *dir = abc;
1121 } else {
1122 simplex.swap(0, 1); *dir = -abc;
1123 }
1124 false
1125 }
1126 4 => {
1127 let d = simplex[0]; let c = simplex[1];
1128 let b = simplex[2]; let a = simplex[3];
1129 let ab = b - a; let ac = c - a;
1130 let ad = d - a; let ao = -a;
1131 let abc = ab.cross(ac);
1132 let acd = ac.cross(ad);
1133 let adb = ad.cross(ab);
1134 if abc.dot(ao) > 0.0 {
1135 *simplex = vec![c, b, a];
1136 return do_simplex(simplex, dir);
1137 }
1138 if acd.dot(ao) > 0.0 {
1139 *simplex = vec![d, c, a];
1140 return do_simplex(simplex, dir);
1141 }
1142 if adb.dot(ao) > 0.0 {
1143 *simplex = vec![b, d, a];
1144 return do_simplex(simplex, dir);
1145 }
1146 true }
1148 _ => { *dir = Point3::new(1.0, 0.0, 0.0); false }
1149 }
1150}
1151
1152pub fn gjk_closest(shape_a: &[Point3], shape_b: &[Point3]) -> Option<(Point3, Point3, f64)> {
1154 if gjk(shape_a, shape_b) { return None; }
1155 let mut dir = shape_a[0] - shape_b[0];
1157 if dir.len_sq() < 1e-20 { return None; }
1158 let mut best_dist = f64::INFINITY;
1159 let mut best_pa = Point3::zero();
1160 let mut best_pb = Point3::zero();
1161 for _ in 0..64 {
1162 dir = dir.normalize();
1163 let pa = support_3d(shape_a, dir);
1164 let pb = support_3d(shape_b, -dir);
1165 let d = (pa - pb).len();
1166 if d < best_dist {
1167 best_dist = d;
1168 best_pa = pa;
1169 best_pb = pb;
1170 }
1171 dir = pb - pa;
1172 if dir.len_sq() < 1e-20 { break; }
1173 }
1174 Some((best_pa, best_pb, best_dist))
1175}
1176
1177pub fn epa(shape_a: &[Point3], shape_b: &[Point3]) -> Option<(Point3, f64)> {
1179 if !gjk(shape_a, shape_b) { return None; }
1180 let dirs = [
1182 Point3::new(1.0, 0.0, 0.0), Point3::new(-1.0, 0.0, 0.0),
1183 Point3::new(0.0, 1.0, 0.0), Point3::new(0.0, -1.0, 0.0),
1184 Point3::new(0.0, 0.0, 1.0), Point3::new(0.0, 0.0, -1.0),
1185 ];
1186 let mut polytope: Vec<Point3> = dirs.iter()
1187 .map(|&d| support_minkowski_diff(shape_a, shape_b, d))
1188 .collect();
1189 let mut faces: Vec<[usize; 3]> = vec![
1190 [0, 1, 2], [0, 2, 3], [0, 3, 4], [0, 4, 1],
1191 [5, 2, 1], [5, 3, 2], [5, 4, 3], [5, 1, 4],
1192 ];
1193
1194 for _ in 0..64 {
1195 let (min_dist, min_face_idx, min_normal) = faces.iter().enumerate()
1197 .map(|(fi, face)| {
1198 let a = polytope[face[0]]; let b = polytope[face[1]]; let c = polytope[face[2]];
1199 let n = (b - a).cross(c - a);
1200 let len = n.len();
1201 if len < 1e-12 { return (f64::INFINITY, fi, Point3::new(0.0, 0.0, 1.0)); }
1202 let n = n * (1.0 / len);
1203 let d = n.dot(a);
1204 (d.abs(), fi, if d >= 0.0 { n } else { -n })
1205 })
1206 .min_by(|a, b| a.0.partial_cmp(&b.0).unwrap())
1207 .unwrap();
1208
1209 let _ = min_face_idx;
1210 let support = support_minkowski_diff(shape_a, shape_b, min_normal);
1211 let new_dist = min_normal.dot(support);
1212
1213 if (new_dist - min_dist).abs() < 1e-6 {
1214 return Some((min_normal, min_dist));
1215 }
1216
1217 let si = polytope.len();
1219 polytope.push(support);
1220 let mut edge_set: Vec<(usize, usize)> = Vec::new();
1221 faces.retain(|face| {
1222 let a = polytope[face[0]]; let b = polytope[face[1]]; let c = polytope[face[2]];
1223 let n = (b - a).cross(c - a);
1224 if n.dot(support - a) > 0.0 {
1225 for &(ea, eb) in &[(face[0], face[1]), (face[1], face[2]), (face[2], face[0])] {
1226 let rev_pos = edge_set.iter().position(|&(x, y)| x == eb && y == ea);
1227 if let Some(pos) = rev_pos { edge_set.remove(pos); }
1228 else { edge_set.push((ea, eb)); }
1229 }
1230 false
1231 } else { true }
1232 });
1233 for (ea, eb) in edge_set {
1234 faces.push([ea, eb, si]);
1235 }
1236 }
1237 None
1238}
1239
1240#[cfg(test)]
1251mod tests {
1252 use super::*;
1253
1254 #[test]
1255 fn test_ray_aabb_hit() {
1256 let ray = Ray3 { origin: Point3::new(0.0, 0.0, 0.0), dir: Point3::new(1.0, 0.0, 0.0) };
1257 let aabb = Aabb3 { min: Point3::new(2.0, -1.0, -1.0), max: Point3::new(4.0, 1.0, 1.0) };
1258 let t = ray_aabb(&ray, &aabb).unwrap();
1259 assert!((t - 2.0).abs() < 1e-10);
1260 }
1261
1262 #[test]
1263 fn test_ray_aabb_miss() {
1264 let ray = Ray3 { origin: Point3::new(0.0, 0.0, 0.0), dir: Point3::new(0.0, 0.0, 1.0) };
1265 let aabb = Aabb3 { min: Point3::new(2.0, -1.0, -1.0), max: Point3::new(4.0, 1.0, 1.0) };
1266 assert!(ray_aabb(&ray, &aabb).is_none());
1267 }
1268
1269 #[test]
1270 fn test_ray_sphere() {
1271 let ray = Ray3 { origin: Point3::new(-5.0, 0.0, 0.0), dir: Point3::new(1.0, 0.0, 0.0) };
1272 let sphere = Sphere { center: Point3::new(0.0, 0.0, 0.0), radius: 1.0 };
1273 let t = ray_sphere(&ray, &sphere).unwrap();
1274 assert!((t - 4.0).abs() < 1e-10);
1275 }
1276
1277 #[test]
1278 fn test_ray_triangle_moller_trumbore() {
1279 let ray = Ray3 { origin: Point3::new(0.0, 0.0, -1.0), dir: Point3::new(0.0, 0.0, 1.0) };
1280 let tri = Triangle3 {
1281 a: Point3::new(-1.0, -1.0, 0.0),
1282 b: Point3::new(1.0, -1.0, 0.0),
1283 c: Point3::new(0.0, 1.0, 0.0),
1284 };
1285 let (t, u, v) = ray_triangle(&ray, &tri).unwrap();
1286 assert!((t - 1.0).abs() < 1e-10);
1287 let _ = (u, v);
1288 }
1289
1290 #[test]
1291 fn test_aabb_aabb_overlap() {
1292 let a = Aabb3 { min: Point3::new(0.0, 0.0, 0.0), max: Point3::new(2.0, 2.0, 2.0) };
1293 let b = Aabb3 { min: Point3::new(1.0, 1.0, 1.0), max: Point3::new(3.0, 3.0, 3.0) };
1294 assert!(aabb_aabb(&a, &b));
1295 }
1296
1297 #[test]
1298 fn test_aabb_aabb_no_overlap() {
1299 let a = Aabb3 { min: Point3::new(0.0, 0.0, 0.0), max: Point3::new(1.0, 1.0, 1.0) };
1300 let b = Aabb3 { min: Point3::new(2.0, 2.0, 2.0), max: Point3::new(3.0, 3.0, 3.0) };
1301 assert!(!aabb_aabb(&a, &b));
1302 }
1303
1304 #[test]
1305 fn test_sphere_sphere() {
1306 let a = Sphere { center: Point3::new(0.0, 0.0, 0.0), radius: 1.0 };
1307 let b = Sphere { center: Point3::new(1.5, 0.0, 0.0), radius: 1.0 };
1308 assert!(sphere_sphere(&a, &b));
1309 }
1310
1311 #[test]
1312 fn test_point_in_triangle() {
1313 let tri = Triangle2 {
1314 a: Point2::new(0.0, 0.0),
1315 b: Point2::new(1.0, 0.0),
1316 c: Point2::new(0.5, 1.0),
1317 };
1318 assert!(point_in_triangle(Point2::new(0.5, 0.4), &tri));
1319 assert!(!point_in_triangle(Point2::new(2.0, 0.0), &tri));
1320 }
1321
1322 #[test]
1323 fn test_segment_segment_2d() {
1324 let a = Segment2 { a: Point2::new(0.0, 0.0), b: Point2::new(2.0, 2.0) };
1325 let b = Segment2 { a: Point2::new(0.0, 2.0), b: Point2::new(2.0, 0.0) };
1326 let p = segment_segment_2d(&a, &b).unwrap();
1327 assert!((p.x - 1.0).abs() < 1e-10);
1328 assert!((p.y - 1.0).abs() < 1e-10);
1329 }
1330
1331 #[test]
1332 fn test_polygon_area() {
1333 let square = vec![
1334 Point2::new(0.0, 0.0), Point2::new(1.0, 0.0),
1335 Point2::new(1.0, 1.0), Point2::new(0.0, 1.0),
1336 ];
1337 assert!((polygon_area(&square) - 1.0).abs() < 1e-10);
1338 }
1339
1340 #[test]
1341 fn test_polygon_centroid() {
1342 let square = vec![
1343 Point2::new(0.0, 0.0), Point2::new(2.0, 0.0),
1344 Point2::new(2.0, 2.0), Point2::new(0.0, 2.0),
1345 ];
1346 let c = polygon_centroid(&square);
1347 assert!((c.x - 1.0).abs() < 1e-10);
1348 assert!((c.y - 1.0).abs() < 1e-10);
1349 }
1350
1351 #[test]
1352 fn test_convex_hull_2d() {
1353 let pts = vec![
1354 Point2::new(0.0, 0.0), Point2::new(1.0, 0.0),
1355 Point2::new(1.0, 1.0), Point2::new(0.0, 1.0),
1356 Point2::new(0.5, 0.5), ];
1358 let hull = convex_hull_2d(&pts);
1359 assert_eq!(hull.len(), 4);
1360 }
1361
1362 #[test]
1363 fn test_point_in_convex_polygon() {
1364 let square = vec![
1365 Point2::new(0.0, 0.0), Point2::new(1.0, 0.0),
1366 Point2::new(1.0, 1.0), Point2::new(0.0, 1.0),
1367 ];
1368 assert!(point_in_convex_polygon(Point2::new(0.5, 0.5), &square));
1369 assert!(!point_in_convex_polygon(Point2::new(2.0, 0.5), &square));
1370 }
1371
1372 #[test]
1373 fn test_ear_clipping() {
1374 let quad = vec![
1375 Point2::new(0.0, 0.0), Point2::new(1.0, 0.0),
1376 Point2::new(1.0, 1.0), Point2::new(0.0, 1.0),
1377 ];
1378 let tris = ear_clipping(&quad);
1379 assert_eq!(tris.len(), 2);
1380 }
1381
1382 #[test]
1383 fn test_delaunay_2d() {
1384 let pts = vec![
1385 Point2::new(0.0, 0.0), Point2::new(1.0, 0.0),
1386 Point2::new(0.5, 1.0), Point2::new(0.5, 0.3),
1387 ];
1388 let tris = delaunay_2d(&pts);
1389 assert!(!tris.is_empty());
1390 }
1391
1392 #[test]
1393 fn test_sutherland_hodgman() {
1394 let subject = vec![
1395 Point2::new(0.0, 0.0), Point2::new(2.0, 0.0),
1396 Point2::new(2.0, 2.0), Point2::new(0.0, 2.0),
1397 ];
1398 let clip = vec![
1399 Point2::new(1.0, 1.0), Point2::new(3.0, 1.0),
1400 Point2::new(3.0, 3.0), Point2::new(1.0, 3.0),
1401 ];
1402 let result = sutherland_hodgman(&subject, &clip);
1403 assert!(!result.is_empty());
1404 }
1405
1406 #[test]
1407 fn test_point_to_plane() {
1408 let plane = Plane { normal: Point3::new(0.0, 1.0, 0.0), d: -2.0 };
1409 let p = Point3::new(0.0, 5.0, 0.0);
1410 let d = point_to_plane(p, &plane);
1411 assert!((d - 3.0).abs() < 1e-10);
1412 }
1413
1414 #[test]
1415 fn test_point_to_aabb() {
1416 let aabb = Aabb3 { min: Point3::new(0.0, 0.0, 0.0), max: Point3::new(1.0, 1.0, 1.0) };
1417 assert!(point_to_aabb(Point3::new(0.5, 0.5, 0.5), &aabb).abs() < 1e-10);
1418 assert!((point_to_aabb(Point3::new(3.0, 0.5, 0.5), &aabb) - 2.0).abs() < 1e-10);
1419 }
1420
1421 #[test]
1422 fn test_obb_obb_overlap() {
1423 let center = Point3::new(0.0, 0.0, 0.0);
1424 let axes = [
1425 Point3::new(1.0, 0.0, 0.0),
1426 Point3::new(0.0, 1.0, 0.0),
1427 Point3::new(0.0, 0.0, 1.0),
1428 ];
1429 let a = Obb3 { center, axes, half_extents: Point3::new(1.0, 1.0, 1.0) };
1430 let b = Obb3 {
1431 center: Point3::new(1.5, 0.0, 0.0),
1432 axes,
1433 half_extents: Point3::new(1.0, 1.0, 1.0),
1434 };
1435 assert!(obb_obb(&a, &b));
1436 }
1437
1438 #[test]
1439 fn test_gjk_overlapping() {
1440 let a: Vec<Point3> = vec![
1441 Point3::new(-1.0, -1.0, -1.0), Point3::new(1.0, -1.0, -1.0),
1442 Point3::new(0.0, 1.0, -1.0), Point3::new(0.0, 0.0, 1.0),
1443 ];
1444 let b: Vec<Point3> = vec![
1445 Point3::new(-0.5, -0.5, -0.5), Point3::new(0.5, -0.5, -0.5),
1446 Point3::new(0.0, 0.5, -0.5), Point3::new(0.0, 0.0, 0.5),
1447 ];
1448 assert!(gjk(&a, &b));
1449 }
1450
1451 #[test]
1452 fn test_polygon_is_convex() {
1453 let convex = vec![
1454 Point2::new(0.0, 0.0), Point2::new(1.0, 0.0),
1455 Point2::new(1.0, 1.0), Point2::new(0.0, 1.0),
1456 ];
1457 assert!(polygon_is_convex(&convex));
1458 }
1459
1460 #[test]
1461 fn test_ray_plane() {
1462 let plane = Plane { normal: Point3::new(0.0, 1.0, 0.0), d: -5.0 };
1463 let ray = Ray3 { origin: Point3::new(0.0, 0.0, 0.0), dir: Point3::new(0.0, 1.0, 0.0) };
1464 let t = ray_plane(&ray, &plane).unwrap();
1465 assert!((t - 5.0).abs() < 1e-10);
1466 }
1467
1468 #[test]
1469 fn test_point_in_obb() {
1470 let obb = Obb3 {
1471 center: Point3::new(0.0, 0.0, 0.0),
1472 axes: [
1473 Point3::new(1.0, 0.0, 0.0),
1474 Point3::new(0.0, 1.0, 0.0),
1475 Point3::new(0.0, 0.0, 1.0),
1476 ],
1477 half_extents: Point3::new(2.0, 2.0, 2.0),
1478 };
1479 assert!(point_in_obb(Point3::new(1.0, 1.0, 1.0), &obb));
1480 assert!(!point_in_obb(Point3::new(3.0, 0.0, 0.0), &obb));
1481 }
1482
1483 #[test]
1484 fn test_sphere_aabb() {
1485 let s = Sphere { center: Point3::new(2.0, 0.0, 0.0), radius: 1.5 };
1486 let b = Aabb3 { min: Point3::new(0.0, 0.0, 0.0), max: Point3::new(1.0, 1.0, 1.0) };
1487 assert!(sphere_aabb(&s, &b));
1488 }
1489
1490 #[test]
1491 fn test_aabb_plane() {
1492 let aabb = Aabb3 { min: Point3::new(0.0, 0.0, 0.0), max: Point3::new(1.0, 1.0, 1.0) };
1493 let plane = Plane { normal: Point3::new(0.0, 1.0, 0.0), d: -10.0 };
1494 assert_eq!(aabb_plane(&aabb, &plane), Halfspace::Back);
1495 }
1496
1497 #[test]
1498 fn test_point3_ops() {
1499 let a = Point3::new(1.0, 2.0, 3.0);
1500 let b = Point3::new(4.0, 5.0, 6.0);
1501 let c = a + b;
1502 assert_eq!(c, Point3::new(5.0, 7.0, 9.0));
1503 let d = b - a;
1504 assert_eq!(d, Point3::new(3.0, 3.0, 3.0));
1505 let e = a.cross(b);
1506 let dot = a.dot(b);
1507 assert!((dot - 32.0).abs() < 1e-10);
1508 let _ = e;
1509 }
1510}