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