1#![allow(clippy::unwrap_used, clippy::disallowed_methods)]
2use crate::{Point, Rect};
22
23#[derive(Debug, Clone, Copy, PartialEq)]
25#[repr(C)]
26pub struct Vec4 {
27 pub x: f32,
28 pub y: f32,
29 pub z: f32,
30 pub w: f32,
31}
32
33impl Vec4 {
34 #[inline]
36 #[must_use]
37 pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
38 Self { x, y, z, w }
39 }
40
41 #[inline]
43 #[must_use]
44 pub const fn zero() -> Self {
45 Self::new(0.0, 0.0, 0.0, 0.0)
46 }
47
48 #[inline]
50 #[must_use]
51 pub const fn from_point(p: Point) -> Self {
52 Self::new(p.x, p.y, 0.0, 1.0)
53 }
54
55 #[inline]
57 #[must_use]
58 pub const fn to_point(self) -> Point {
59 Point {
60 x: self.x,
61 y: self.y,
62 }
63 }
64
65 #[inline]
67 #[must_use]
68 pub fn dot(self, other: Self) -> f32 {
69 self.w.mul_add(
70 other.w,
71 self.z
72 .mul_add(other.z, self.x.mul_add(other.x, self.y * other.y)),
73 )
74 }
75
76 #[inline]
78 #[must_use]
79 pub fn add(self, other: Self) -> Self {
80 Self::new(
81 self.x + other.x,
82 self.y + other.y,
83 self.z + other.z,
84 self.w + other.w,
85 )
86 }
87
88 #[inline]
90 #[must_use]
91 pub fn sub(self, other: Self) -> Self {
92 Self::new(
93 self.x - other.x,
94 self.y - other.y,
95 self.z - other.z,
96 self.w - other.w,
97 )
98 }
99
100 #[inline]
102 #[must_use]
103 pub fn scale(self, s: f32) -> Self {
104 Self::new(self.x * s, self.y * s, self.z * s, self.w * s)
105 }
106
107 #[inline]
109 #[must_use]
110 pub fn mul(self, other: Self) -> Self {
111 Self::new(
112 self.x * other.x,
113 self.y * other.y,
114 self.z * other.z,
115 self.w * other.w,
116 )
117 }
118
119 #[inline]
121 #[must_use]
122 pub fn lerp(self, other: Self, t: f32) -> Self {
123 self.add(other.sub(self).scale(t))
124 }
125
126 #[inline]
128 #[must_use]
129 pub fn length(self) -> f32 {
130 self.dot(self).sqrt()
131 }
132
133 #[inline]
135 #[must_use]
136 pub fn normalize(self) -> Self {
137 let len = self.length();
138 if len > 0.0 {
139 self.scale(1.0 / len)
140 } else {
141 self
142 }
143 }
144}
145
146impl Default for Vec4 {
147 fn default() -> Self {
148 Self::zero()
149 }
150}
151
152impl From<Point> for Vec4 {
153 fn from(p: Point) -> Self {
154 Self::from_point(p)
155 }
156}
157
158impl From<Vec4> for Point {
159 fn from(v: Vec4) -> Self {
160 v.to_point()
161 }
162}
163
164#[derive(Debug, Clone, Copy, PartialEq)]
166#[repr(C)]
167pub struct Mat4 {
168 pub data: [[f32; 4]; 4],
170}
171
172impl Mat4 {
173 #[inline]
175 #[must_use]
176 pub const fn from_data(data: [[f32; 4]; 4]) -> Self {
177 Self { data }
178 }
179
180 #[inline]
182 #[must_use]
183 pub const fn identity() -> Self {
184 Self::from_data([
185 [1.0, 0.0, 0.0, 0.0],
186 [0.0, 1.0, 0.0, 0.0],
187 [0.0, 0.0, 1.0, 0.0],
188 [0.0, 0.0, 0.0, 1.0],
189 ])
190 }
191
192 #[inline]
194 #[must_use]
195 pub const fn zero() -> Self {
196 Self::from_data([
197 [0.0, 0.0, 0.0, 0.0],
198 [0.0, 0.0, 0.0, 0.0],
199 [0.0, 0.0, 0.0, 0.0],
200 [0.0, 0.0, 0.0, 0.0],
201 ])
202 }
203
204 #[inline]
206 #[must_use]
207 pub const fn translation(x: f32, y: f32, z: f32) -> Self {
208 Self::from_data([
209 [1.0, 0.0, 0.0, x],
210 [0.0, 1.0, 0.0, y],
211 [0.0, 0.0, 1.0, z],
212 [0.0, 0.0, 0.0, 1.0],
213 ])
214 }
215
216 #[inline]
218 #[must_use]
219 pub const fn translation_2d(x: f32, y: f32) -> Self {
220 Self::translation(x, y, 0.0)
221 }
222
223 #[inline]
225 #[must_use]
226 pub const fn scale(x: f32, y: f32, z: f32) -> Self {
227 Self::from_data([
228 [x, 0.0, 0.0, 0.0],
229 [0.0, y, 0.0, 0.0],
230 [0.0, 0.0, z, 0.0],
231 [0.0, 0.0, 0.0, 1.0],
232 ])
233 }
234
235 #[inline]
237 #[must_use]
238 pub const fn scale_2d(x: f32, y: f32) -> Self {
239 Self::scale(x, y, 1.0)
240 }
241
242 #[inline]
244 #[must_use]
245 pub const fn scale_uniform(s: f32) -> Self {
246 Self::scale(s, s, s)
247 }
248
249 #[inline]
251 #[must_use]
252 pub fn rotation_z(angle_rad: f32) -> Self {
253 let (sin, cos) = angle_rad.sin_cos();
254 Self::from_data([
255 [cos, -sin, 0.0, 0.0],
256 [sin, cos, 0.0, 0.0],
257 [0.0, 0.0, 1.0, 0.0],
258 [0.0, 0.0, 0.0, 1.0],
259 ])
260 }
261
262 #[inline]
264 #[must_use]
265 pub fn ortho(left: f32, right: f32, bottom: f32, top: f32, near: f32, far: f32) -> Self {
266 let width = right - left;
267 let height = top - bottom;
268 let depth = far - near;
269
270 Self::from_data([
271 [2.0 / width, 0.0, 0.0, -(right + left) / width],
272 [0.0, 2.0 / height, 0.0, -(top + bottom) / height],
273 [0.0, 0.0, -2.0 / depth, -(far + near) / depth],
274 [0.0, 0.0, 0.0, 1.0],
275 ])
276 }
277
278 #[inline]
280 #[must_use]
281 pub fn ortho_screen(width: f32, height: f32) -> Self {
282 Self::ortho(0.0, width, height, 0.0, -1.0, 1.0)
283 }
284
285 #[inline]
287 #[must_use]
288 pub fn mul(&self, other: &Self) -> Self {
289 let mut result = Self::zero();
290 for i in 0..4 {
291 for j in 0..4 {
292 for k in 0..4 {
293 result.data[i][j] += self.data[i][k] * other.data[k][j];
294 }
295 }
296 }
297 result
298 }
299
300 #[inline]
302 #[must_use]
303 pub fn transform_vec4(&self, v: Vec4) -> Vec4 {
304 Vec4::new(
305 self.data[0][3].mul_add(
306 v.w,
307 self.data[0][2].mul_add(v.z, self.data[0][0].mul_add(v.x, self.data[0][1] * v.y)),
308 ),
309 self.data[1][3].mul_add(
310 v.w,
311 self.data[1][2].mul_add(v.z, self.data[1][0].mul_add(v.x, self.data[1][1] * v.y)),
312 ),
313 self.data[2][3].mul_add(
314 v.w,
315 self.data[2][2].mul_add(v.z, self.data[2][0].mul_add(v.x, self.data[2][1] * v.y)),
316 ),
317 self.data[3][3].mul_add(
318 v.w,
319 self.data[3][2].mul_add(v.z, self.data[3][0].mul_add(v.x, self.data[3][1] * v.y)),
320 ),
321 )
322 }
323
324 #[inline]
326 #[must_use]
327 pub fn transform_point(&self, p: Point) -> Point {
328 let v = self.transform_vec4(Vec4::from_point(p));
329 Point::new(v.x, v.y)
330 }
331
332 #[inline]
334 #[must_use]
335 pub fn transform_rect(&self, rect: &Rect) -> Rect {
336 let corners = [
337 Point::new(rect.x, rect.y),
338 Point::new(rect.x + rect.width, rect.y),
339 Point::new(rect.x + rect.width, rect.y + rect.height),
340 Point::new(rect.x, rect.y + rect.height),
341 ];
342
343 let transformed: Vec<Point> = corners.iter().map(|&p| self.transform_point(p)).collect();
344
345 let min_x = transformed
346 .iter()
347 .map(|p| p.x)
348 .fold(f32::INFINITY, f32::min);
349 let max_x = transformed
350 .iter()
351 .map(|p| p.x)
352 .fold(f32::NEG_INFINITY, f32::max);
353 let min_y = transformed
354 .iter()
355 .map(|p| p.y)
356 .fold(f32::INFINITY, f32::min);
357 let max_y = transformed
358 .iter()
359 .map(|p| p.y)
360 .fold(f32::NEG_INFINITY, f32::max);
361
362 Rect::new(min_x, min_y, max_x - min_x, max_y - min_y)
363 }
364
365 #[inline]
367 #[must_use]
368 pub const fn column(&self, idx: usize) -> Vec4 {
369 Vec4::new(
370 self.data[0][idx],
371 self.data[1][idx],
372 self.data[2][idx],
373 self.data[3][idx],
374 )
375 }
376
377 #[inline]
379 #[must_use]
380 pub const fn row(&self, idx: usize) -> Vec4 {
381 Vec4::new(
382 self.data[idx][0],
383 self.data[idx][1],
384 self.data[idx][2],
385 self.data[idx][3],
386 )
387 }
388
389 #[inline]
391 #[must_use]
392 pub const fn transpose(&self) -> Self {
393 Self::from_data([
394 [
395 self.data[0][0],
396 self.data[1][0],
397 self.data[2][0],
398 self.data[3][0],
399 ],
400 [
401 self.data[0][1],
402 self.data[1][1],
403 self.data[2][1],
404 self.data[3][1],
405 ],
406 [
407 self.data[0][2],
408 self.data[1][2],
409 self.data[2][2],
410 self.data[3][2],
411 ],
412 [
413 self.data[0][3],
414 self.data[1][3],
415 self.data[2][3],
416 self.data[3][3],
417 ],
418 ])
419 }
420}
421
422impl Default for Mat4 {
423 fn default() -> Self {
424 Self::identity()
425 }
426}
427
428impl std::ops::Mul for Mat4 {
429 type Output = Self;
430 fn mul(self, rhs: Self) -> Self {
431 Self::mul(&self, &rhs)
432 }
433}
434
435impl std::ops::Mul<Vec4> for Mat4 {
436 type Output = Vec4;
437 fn mul(self, rhs: Vec4) -> Vec4 {
438 self.transform_vec4(rhs)
439 }
440}
441
442#[inline]
446#[must_use]
447pub fn batch_transform_points(points: &[Point], transform: &Mat4) -> Vec<Point> {
448 points
449 .iter()
450 .map(|&p| transform.transform_point(p))
451 .collect()
452}
453
454#[inline]
456#[must_use]
457pub fn batch_transform_vec4(vecs: &[Vec4], transform: &Mat4) -> Vec<Vec4> {
458 vecs.iter().map(|&v| transform.transform_vec4(v)).collect()
459}
460
461#[inline]
463#[must_use]
464pub fn batch_lerp_points(from: &[Point], to: &[Point], t: f32) -> Vec<Point> {
465 debug_assert_eq!(from.len(), to.len());
466 from.iter()
467 .zip(to.iter())
468 .map(|(a, b)| a.lerp(b, t))
469 .collect()
470}
471
472#[inline]
474#[must_use]
475pub fn bounding_box(points: &[Point]) -> Option<Rect> {
476 if points.is_empty() {
477 return None;
478 }
479
480 let mut min_x = f32::INFINITY;
481 let mut max_x = f32::NEG_INFINITY;
482 let mut min_y = f32::INFINITY;
483 let mut max_y = f32::NEG_INFINITY;
484
485 for p in points {
486 min_x = min_x.min(p.x);
487 max_x = max_x.max(p.x);
488 min_y = min_y.min(p.y);
489 max_y = max_y.max(p.y);
490 }
491
492 Some(Rect::new(min_x, min_y, max_x - min_x, max_y - min_y))
493}
494
495#[inline]
497#[must_use]
498pub fn centroid(points: &[Point]) -> Option<Point> {
499 if points.is_empty() {
500 return None;
501 }
502
503 let sum: (f32, f32) = points
504 .iter()
505 .fold((0.0, 0.0), |acc, p| (acc.0 + p.x, acc.1 + p.y));
506 let n = points.len() as f32;
507 Some(Point::new(sum.0 / n, sum.1 / n))
508}
509
510#[must_use]
512pub fn point_in_convex_polygon(point: Point, polygon: &[Point]) -> bool {
513 if polygon.len() < 3 {
514 return false;
515 }
516
517 let mut positive = false;
518 let mut negative = false;
519
520 for i in 0..polygon.len() {
521 let a = polygon[i];
522 let b = polygon[(i + 1) % polygon.len()];
523
524 let cross = (point.x - a.x).mul_add(b.y - a.y, -((point.y - a.y) * (b.x - a.x)));
525
526 if cross > 0.0 {
527 positive = true;
528 } else if cross < 0.0 {
529 negative = true;
530 }
531
532 if positive && negative {
533 return false;
534 }
535 }
536
537 true
538}
539
540#[must_use]
542pub fn polygon_area(polygon: &[Point]) -> f32 {
543 if polygon.len() < 3 {
544 return 0.0;
545 }
546
547 let mut area = 0.0;
548 for i in 0..polygon.len() {
549 let j = (i + 1) % polygon.len();
550 area += polygon[i].x * polygon[j].y;
551 area -= polygon[j].x * polygon[i].y;
552 }
553
554 (area / 2.0).abs()
555}
556
557#[cfg(feature = "simd")]
562mod simd_impl {
563 use super::Vec4;
564
565 pub type SimdVectorF32 = trueno::Vector<f32>;
567
568 #[inline]
570 #[must_use]
571 pub fn vec4_to_simd(v: Vec4) -> SimdVectorF32 {
572 SimdVectorF32::from_slice(&[v.x, v.y, v.z, v.w])
573 }
574
575 #[inline]
577 #[must_use]
578 pub fn simd_to_vec4(v: &SimdVectorF32) -> Vec4 {
579 let slice = v.as_slice();
580 Vec4::new(
581 slice.first().copied().unwrap_or(0.0),
582 slice.get(1).copied().unwrap_or(0.0),
583 slice.get(2).copied().unwrap_or(0.0),
584 slice.get(3).copied().unwrap_or(0.0),
585 )
586 }
587
588 pub fn batch_add_simd(a: &[f32], b: &[f32]) -> trueno::Result<Vec<f32>> {
590 let va = SimdVectorF32::from_slice(a);
591 let vb = SimdVectorF32::from_slice(b);
592 let result = va.add(&vb)?;
593 Ok(result.as_slice().to_vec())
594 }
595
596 pub fn dot_simd(a: &[f32], b: &[f32]) -> trueno::Result<f32> {
598 let va = SimdVectorF32::from_slice(a);
599 let vb = SimdVectorF32::from_slice(b);
600 va.dot(&vb)
601 }
602
603 pub fn scale_simd(a: &[f32], s: f32) -> trueno::Result<Vec<f32>> {
605 let va = SimdVectorF32::from_slice(a);
606 let result = va.scale(s)?;
607 Ok(result.as_slice().to_vec())
608 }
609
610 #[must_use]
612 pub fn best_backend() -> trueno::Backend {
613 trueno::Backend::select_best()
614 }
615
616 pub fn batch_dot_product(a: &[Vec4], b: &[Vec4]) -> Vec<f32> {
618 debug_assert_eq!(a.len(), b.len());
619 a.iter().zip(b.iter()).map(|(va, vb)| va.dot(*vb)).collect()
620 }
621}
622
623#[cfg(feature = "simd")]
624pub use simd_impl::*;
625
626#[cfg(test)]
627mod tests {
628 use super::*;
629
630 #[test]
635 fn test_vec4_new() {
636 let v = Vec4::new(1.0, 2.0, 3.0, 4.0);
637 assert_eq!(v.x, 1.0);
638 assert_eq!(v.y, 2.0);
639 assert_eq!(v.z, 3.0);
640 assert_eq!(v.w, 4.0);
641 }
642
643 #[test]
644 fn test_vec4_zero() {
645 let v = Vec4::zero();
646 assert_eq!(v, Vec4::new(0.0, 0.0, 0.0, 0.0));
647 }
648
649 #[test]
650 fn test_vec4_default() {
651 let v: Vec4 = Default::default();
652 assert_eq!(v, Vec4::zero());
653 }
654
655 #[test]
656 fn test_vec4_from_point() {
657 let p = Point::new(10.0, 20.0);
658 let v = Vec4::from_point(p);
659 assert_eq!(v, Vec4::new(10.0, 20.0, 0.0, 1.0));
660 }
661
662 #[test]
663 fn test_vec4_to_point() {
664 let v = Vec4::new(5.0, 15.0, 25.0, 35.0);
665 let p = v.to_point();
666 assert_eq!(p, Point::new(5.0, 15.0));
667 }
668
669 #[test]
670 fn test_vec4_dot() {
671 let a = Vec4::new(1.0, 2.0, 3.0, 4.0);
672 let b = Vec4::new(2.0, 3.0, 4.0, 5.0);
673 assert_eq!(a.dot(b), 1.0 * 2.0 + 2.0 * 3.0 + 3.0 * 4.0 + 4.0 * 5.0);
674 }
675
676 #[test]
677 fn test_vec4_add() {
678 let a = Vec4::new(1.0, 2.0, 3.0, 4.0);
679 let b = Vec4::new(5.0, 6.0, 7.0, 8.0);
680 let c = a.add(b);
681 assert_eq!(c, Vec4::new(6.0, 8.0, 10.0, 12.0));
682 }
683
684 #[test]
685 fn test_vec4_sub() {
686 let a = Vec4::new(5.0, 6.0, 7.0, 8.0);
687 let b = Vec4::new(1.0, 2.0, 3.0, 4.0);
688 let c = a.sub(b);
689 assert_eq!(c, Vec4::new(4.0, 4.0, 4.0, 4.0));
690 }
691
692 #[test]
693 fn test_vec4_scale() {
694 let v = Vec4::new(1.0, 2.0, 3.0, 4.0);
695 let s = v.scale(2.0);
696 assert_eq!(s, Vec4::new(2.0, 4.0, 6.0, 8.0));
697 }
698
699 #[test]
700 fn test_vec4_mul() {
701 let a = Vec4::new(1.0, 2.0, 3.0, 4.0);
702 let b = Vec4::new(2.0, 2.0, 2.0, 2.0);
703 let c = a.mul(b);
704 assert_eq!(c, Vec4::new(2.0, 4.0, 6.0, 8.0));
705 }
706
707 #[test]
708 fn test_vec4_lerp() {
709 let a = Vec4::new(0.0, 0.0, 0.0, 0.0);
710 let b = Vec4::new(10.0, 10.0, 10.0, 10.0);
711 let c = a.lerp(b, 0.5);
712 assert_eq!(c, Vec4::new(5.0, 5.0, 5.0, 5.0));
713 }
714
715 #[test]
716 fn test_vec4_length() {
717 let v = Vec4::new(3.0, 4.0, 0.0, 0.0);
718 assert!((v.length() - 5.0).abs() < 0.0001);
719 }
720
721 #[test]
722 fn test_vec4_normalize() {
723 let v = Vec4::new(3.0, 4.0, 0.0, 0.0);
724 let n = v.normalize();
725 assert!((n.length() - 1.0).abs() < 0.0001);
726 }
727
728 #[test]
729 fn test_vec4_from_impl() {
730 let p = Point::new(1.0, 2.0);
731 let v: Vec4 = p.into();
732 assert_eq!(v, Vec4::new(1.0, 2.0, 0.0, 1.0));
733 }
734
735 #[test]
740 fn test_mat4_identity() {
741 let m = Mat4::identity();
742 assert_eq!(m.data[0][0], 1.0);
743 assert_eq!(m.data[1][1], 1.0);
744 assert_eq!(m.data[2][2], 1.0);
745 assert_eq!(m.data[3][3], 1.0);
746 assert_eq!(m.data[0][1], 0.0);
747 }
748
749 #[test]
750 fn test_mat4_zero() {
751 let m = Mat4::zero();
752 for i in 0..4 {
753 for j in 0..4 {
754 assert_eq!(m.data[i][j], 0.0);
755 }
756 }
757 }
758
759 #[test]
760 fn test_mat4_default() {
761 let m: Mat4 = Default::default();
762 assert_eq!(m, Mat4::identity());
763 }
764
765 #[test]
766 fn test_mat4_translation() {
767 let m = Mat4::translation(10.0, 20.0, 30.0);
768 let p = Point::new(0.0, 0.0);
769 let t = m.transform_point(p);
770 assert_eq!(t, Point::new(10.0, 20.0));
771 }
772
773 #[test]
774 fn test_mat4_translation_2d() {
775 let m = Mat4::translation_2d(5.0, 15.0);
776 let p = Point::new(10.0, 10.0);
777 let t = m.transform_point(p);
778 assert_eq!(t, Point::new(15.0, 25.0));
779 }
780
781 #[test]
782 fn test_mat4_scale() {
783 let m = Mat4::scale(2.0, 3.0, 4.0);
784 let p = Point::new(10.0, 10.0);
785 let t = m.transform_point(p);
786 assert_eq!(t, Point::new(20.0, 30.0));
787 }
788
789 #[test]
790 fn test_mat4_scale_2d() {
791 let m = Mat4::scale_2d(0.5, 2.0);
792 let p = Point::new(10.0, 10.0);
793 let t = m.transform_point(p);
794 assert_eq!(t, Point::new(5.0, 20.0));
795 }
796
797 #[test]
798 fn test_mat4_scale_uniform() {
799 let m = Mat4::scale_uniform(2.0);
800 let p = Point::new(5.0, 5.0);
801 let t = m.transform_point(p);
802 assert_eq!(t, Point::new(10.0, 10.0));
803 }
804
805 #[test]
806 fn test_mat4_rotation_z() {
807 use std::f32::consts::PI;
808 let m = Mat4::rotation_z(PI / 2.0); let p = Point::new(1.0, 0.0);
810 let t = m.transform_point(p);
811 assert!((t.x - 0.0).abs() < 0.0001);
813 assert!((t.y - 1.0).abs() < 0.0001);
814 }
815
816 #[test]
817 fn test_mat4_mul_identity() {
818 let a = Mat4::identity();
819 let b = Mat4::identity();
820 let c = a.mul(&b);
821 assert_eq!(c, Mat4::identity());
822 }
823
824 #[test]
825 fn test_mat4_mul_combined_transform() {
826 let translate = Mat4::translation_2d(10.0, 0.0);
827 let scale = Mat4::scale_2d(2.0, 2.0);
828 let combined = translate.mul(&scale);
829
830 let p = Point::new(5.0, 5.0);
831 let t = combined.transform_point(p);
832 assert_eq!(t, Point::new(20.0, 10.0));
834 }
835
836 #[test]
837 fn test_mat4_transform_rect() {
838 let m = Mat4::scale_2d(2.0, 2.0);
839 let rect = Rect::new(10.0, 10.0, 20.0, 30.0);
840 let t = m.transform_rect(&rect);
841 assert_eq!(t.x, 20.0);
842 assert_eq!(t.y, 20.0);
843 assert_eq!(t.width, 40.0);
844 assert_eq!(t.height, 60.0);
845 }
846
847 #[test]
848 fn test_mat4_transpose() {
849 let m = Mat4::from_data([
850 [1.0, 2.0, 3.0, 4.0],
851 [5.0, 6.0, 7.0, 8.0],
852 [9.0, 10.0, 11.0, 12.0],
853 [13.0, 14.0, 15.0, 16.0],
854 ]);
855 let t = m.transpose();
856 assert_eq!(t.data[0][1], 5.0);
857 assert_eq!(t.data[1][0], 2.0);
858 }
859
860 #[test]
861 fn test_mat4_column() {
862 let m = Mat4::identity();
863 let col = m.column(0);
864 assert_eq!(col, Vec4::new(1.0, 0.0, 0.0, 0.0));
865 }
866
867 #[test]
868 fn test_mat4_row() {
869 let m = Mat4::identity();
870 let row = m.row(0);
871 assert_eq!(row, Vec4::new(1.0, 0.0, 0.0, 0.0));
872 }
873
874 #[test]
875 fn test_mat4_ortho_screen() {
876 let m = Mat4::ortho_screen(800.0, 600.0);
877 let p = m.transform_vec4(Vec4::new(0.0, 0.0, 0.0, 1.0));
879 assert!((p.x - (-1.0)).abs() < 0.001);
880 assert!((p.y - 1.0).abs() < 0.001);
881 }
882
883 #[test]
884 fn test_mat4_mul_operator() {
885 let a = Mat4::translation_2d(10.0, 20.0);
886 let b = Mat4::scale_2d(2.0, 2.0);
887 let c = a * b;
888 assert_eq!(c, a.mul(&b));
889 }
890
891 #[test]
892 fn test_mat4_mul_vec4_operator() {
893 let m = Mat4::translation_2d(10.0, 20.0);
894 let v = Vec4::new(0.0, 0.0, 0.0, 1.0);
895 let r = m * v;
896 assert_eq!(r, Vec4::new(10.0, 20.0, 0.0, 1.0));
897 }
898
899 #[test]
904 fn test_batch_transform_points() {
905 let m = Mat4::translation_2d(10.0, 10.0);
906 let points = vec![Point::new(0.0, 0.0), Point::new(5.0, 5.0)];
907 let result = batch_transform_points(&points, &m);
908 assert_eq!(result[0], Point::new(10.0, 10.0));
909 assert_eq!(result[1], Point::new(15.0, 15.0));
910 }
911
912 #[test]
913 fn test_batch_transform_vec4() {
914 let m = Mat4::scale_uniform(2.0);
915 let vecs = vec![Vec4::new(1.0, 1.0, 1.0, 0.0), Vec4::new(2.0, 2.0, 2.0, 0.0)];
916 let result = batch_transform_vec4(&vecs, &m);
917 assert_eq!(result[0], Vec4::new(2.0, 2.0, 2.0, 0.0));
918 assert_eq!(result[1], Vec4::new(4.0, 4.0, 4.0, 0.0));
919 }
920
921 #[test]
922 fn test_batch_lerp_points() {
923 let from = vec![Point::new(0.0, 0.0), Point::new(10.0, 10.0)];
924 let to = vec![Point::new(10.0, 10.0), Point::new(20.0, 20.0)];
925 let result = batch_lerp_points(&from, &to, 0.5);
926 assert_eq!(result[0], Point::new(5.0, 5.0));
927 assert_eq!(result[1], Point::new(15.0, 15.0));
928 }
929
930 #[test]
935 fn test_bounding_box() {
936 let points = vec![
937 Point::new(0.0, 0.0),
938 Point::new(10.0, 5.0),
939 Point::new(5.0, 15.0),
940 ];
941 let bbox = bounding_box(&points).unwrap();
942 assert_eq!(bbox.x, 0.0);
943 assert_eq!(bbox.y, 0.0);
944 assert_eq!(bbox.width, 10.0);
945 assert_eq!(bbox.height, 15.0);
946 }
947
948 #[test]
949 fn test_bounding_box_empty() {
950 let points: Vec<Point> = vec![];
951 assert!(bounding_box(&points).is_none());
952 }
953
954 #[test]
955 fn test_centroid() {
956 let points = vec![
957 Point::new(0.0, 0.0),
958 Point::new(10.0, 0.0),
959 Point::new(10.0, 10.0),
960 Point::new(0.0, 10.0),
961 ];
962 let c = centroid(&points).unwrap();
963 assert_eq!(c, Point::new(5.0, 5.0));
964 }
965
966 #[test]
967 fn test_centroid_empty() {
968 let points: Vec<Point> = vec![];
969 assert!(centroid(&points).is_none());
970 }
971
972 #[test]
973 fn test_point_in_convex_polygon() {
974 let square = vec![
975 Point::new(0.0, 0.0),
976 Point::new(10.0, 0.0),
977 Point::new(10.0, 10.0),
978 Point::new(0.0, 10.0),
979 ];
980
981 assert!(point_in_convex_polygon(Point::new(5.0, 5.0), &square));
982 assert!(!point_in_convex_polygon(Point::new(15.0, 5.0), &square));
983 }
984
985 #[test]
986 fn test_point_in_convex_polygon_edge() {
987 let triangle = vec![
988 Point::new(0.0, 0.0),
989 Point::new(10.0, 0.0),
990 Point::new(5.0, 10.0),
991 ];
992
993 assert!(point_in_convex_polygon(Point::new(5.0, 0.0), &triangle));
995 }
996
997 #[test]
998 fn test_polygon_area_square() {
999 let square = vec![
1000 Point::new(0.0, 0.0),
1001 Point::new(10.0, 0.0),
1002 Point::new(10.0, 10.0),
1003 Point::new(0.0, 10.0),
1004 ];
1005 let area = polygon_area(&square);
1006 assert!((area - 100.0).abs() < 0.0001);
1007 }
1008
1009 #[test]
1010 fn test_polygon_area_triangle() {
1011 let triangle = vec![
1012 Point::new(0.0, 0.0),
1013 Point::new(10.0, 0.0),
1014 Point::new(5.0, 10.0),
1015 ];
1016 let area = polygon_area(&triangle);
1017 assert!((area - 50.0).abs() < 0.0001);
1018 }
1019
1020 #[test]
1021 fn test_polygon_area_too_few_points() {
1022 assert_eq!(polygon_area(&[]), 0.0);
1023 assert_eq!(polygon_area(&[Point::new(0.0, 0.0)]), 0.0);
1024 assert_eq!(
1025 polygon_area(&[Point::new(0.0, 0.0), Point::new(1.0, 1.0)]),
1026 0.0
1027 );
1028 }
1029
1030 #[cfg(feature = "simd")]
1035 mod simd_tests {
1036 use super::*;
1037
1038 #[test]
1039 fn test_vec4_to_simd_roundtrip() {
1040 let v = Vec4::new(1.0, 2.0, 3.0, 4.0);
1041 let simd = vec4_to_simd(v);
1042 let back = simd_to_vec4(&simd);
1043 assert_eq!(v, back);
1044 }
1045
1046 #[test]
1047 fn test_batch_add_simd() {
1048 let a = vec![1.0, 2.0, 3.0, 4.0];
1049 let b = vec![5.0, 6.0, 7.0, 8.0];
1050 let result = batch_add_simd(&a, &b).unwrap();
1051 assert_eq!(result, vec![6.0, 8.0, 10.0, 12.0]);
1052 }
1053
1054 #[test]
1055 fn test_dot_simd() {
1056 let a = vec![1.0, 2.0, 3.0, 4.0];
1057 let b = vec![1.0, 1.0, 1.0, 1.0];
1058 let result = dot_simd(&a, &b).unwrap();
1059 assert_eq!(result, 10.0);
1060 }
1061
1062 #[test]
1063 fn test_scale_simd() {
1064 let a = vec![1.0, 2.0, 3.0, 4.0];
1065 let result = scale_simd(&a, 2.0).unwrap();
1066 assert_eq!(result, vec![2.0, 4.0, 6.0, 8.0]);
1067 }
1068
1069 #[test]
1070 fn test_best_backend() {
1071 let backend = best_backend();
1072 assert!(matches!(
1074 backend,
1075 trueno::Backend::Scalar
1076 | trueno::Backend::SSE2
1077 | trueno::Backend::AVX
1078 | trueno::Backend::AVX2
1079 | trueno::Backend::AVX512
1080 | trueno::Backend::NEON
1081 | trueno::Backend::WasmSIMD
1082 | trueno::Backend::GPU
1083 | trueno::Backend::Auto
1084 ));
1085 }
1086
1087 #[test]
1088 fn test_batch_dot_product() {
1089 let a = vec![Vec4::new(1.0, 0.0, 0.0, 0.0), Vec4::new(0.0, 1.0, 0.0, 0.0)];
1090 let b = vec![Vec4::new(1.0, 0.0, 0.0, 0.0), Vec4::new(0.0, 1.0, 0.0, 0.0)];
1091 let dots = batch_dot_product(&a, &b);
1092 assert_eq!(dots, vec![1.0, 1.0]);
1093 }
1094 }
1095}