1#![allow(clippy::many_single_char_names)]
23
24pub mod aabb;
25pub mod curve;
26pub mod frustum;
27pub mod octree;
28pub mod plane;
29pub mod ray;
30pub mod segment;
31pub mod triangulator;
32
33use crate::ray::IntersectionResult;
34use bytemuck::{Pod, Zeroable};
35use nalgebra::{
36 Matrix3, Matrix4, RealField, Scalar, SimdRealField, UnitQuaternion, Vector2, Vector3,
37};
38use std::{
39 fmt::Debug,
40 hash::{Hash, Hasher},
41 ops::{Index, IndexMut},
42};
43
44pub use rectutils::*;
45
46#[derive(Copy, Clone)]
47pub enum PlaneClass {
48 XY,
49 YZ,
50 XZ,
51}
52
53#[inline]
54#[allow(clippy::useless_let_if_seq)]
55pub fn classify_plane(normal: Vector3<f32>) -> PlaneClass {
56 let mut longest = 0.0f32;
57 let mut class = PlaneClass::XY;
58
59 if normal.x.abs() > longest {
60 longest = normal.x.abs();
61 class = PlaneClass::YZ;
62 }
63
64 if normal.y.abs() > longest {
65 longest = normal.y.abs();
66 class = PlaneClass::XZ;
67 }
68
69 if normal.z.abs() > longest {
70 class = PlaneClass::XY;
71 }
72
73 class
74}
75
76#[inline]
77pub fn get_polygon_normal(polygon: &[Vector3<f32>]) -> Result<Vector3<f32>, &'static str> {
78 let mut normal = Vector3::default();
79
80 for (i, current) in polygon.iter().enumerate() {
81 let next = polygon[(i + 1) % polygon.len()];
82 normal.x += (current.y - next.y) * (current.z + next.z);
83 normal.y += (current.z - next.z) * (current.x + next.x);
84 normal.z += (current.x - next.x) * (current.y + next.y);
85 }
86
87 normal
88 .try_normalize(f32::EPSILON)
89 .ok_or("Unable to get normal of degenerated polygon!")
90}
91
92#[inline]
93pub fn get_signed_triangle_area(v1: Vector2<f32>, v2: Vector2<f32>, v3: Vector2<f32>) -> f32 {
94 0.5 * (v1.x * (v3.y - v2.y) + v2.x * (v1.y - v3.y) + v3.x * (v2.y - v1.y))
95}
96
97#[inline]
98pub fn vec3_to_vec2_by_plane(
99 plane_class: PlaneClass,
100 normal: Vector3<f32>,
101 point: Vector3<f32>,
102) -> Vector2<f32> {
103 match plane_class {
104 PlaneClass::XY => {
105 if normal.z < 0.0 {
106 Vector2::new(point.y, point.x)
107 } else {
108 Vector2::new(point.x, point.y)
109 }
110 }
111 PlaneClass::XZ => {
112 if normal.y < 0.0 {
113 Vector2::new(point.x, point.z)
114 } else {
115 Vector2::new(point.z, point.x)
116 }
117 }
118 PlaneClass::YZ => {
119 if normal.x < 0.0 {
120 Vector2::new(point.z, point.y)
121 } else {
122 Vector2::new(point.y, point.z)
123 }
124 }
125 }
126}
127
128#[inline]
129pub fn is_point_inside_2d_triangle(
130 point: Vector2<f32>,
131 pt_a: Vector2<f32>,
132 pt_b: Vector2<f32>,
133 pt_c: Vector2<f32>,
134) -> bool {
135 let ba = pt_b - pt_a;
136 let ca = pt_c - pt_a;
137
138 let vp = point - pt_a;
139
140 let ba_dot_ba = ba.dot(&ba);
141 let ca_dot_ba = ca.dot(&ba);
142 let ca_dot_ca = ca.dot(&ca);
143
144 let dot_02 = ca.dot(&vp);
145 let dot_12 = ba.dot(&vp);
146
147 let inv_denom = 1.0 / (ca_dot_ca * ba_dot_ba - ca_dot_ba.powi(2));
148
149 let u = (ba_dot_ba * dot_02 - ca_dot_ba * dot_12) * inv_denom;
151 let v = (ca_dot_ca * dot_12 - ca_dot_ba * dot_02) * inv_denom;
152
153 (u >= 0.0) && (v >= 0.0) && (u + v < 1.0)
154}
155
156#[inline]
157pub fn wrap_angle(angle: f32) -> f32 {
158 let two_pi = 2.0 * std::f32::consts::PI;
159
160 if angle > 0.0 {
161 angle % two_pi
162 } else {
163 (angle + two_pi) % two_pi
164 }
165}
166
167#[inline]
169pub fn ieee_remainder(x: f32, y: f32) -> f32 {
170 x - (x / y).round() * y
171}
172
173#[inline]
174pub fn round_to_step(x: f32, step: f32) -> f32 {
175 x - ieee_remainder(x, step)
176}
177
178#[inline]
179pub fn wrapf(mut n: f32, mut min_limit: f32, mut max_limit: f32) -> f32 {
180 if n >= min_limit && n <= max_limit {
181 return n;
182 }
183
184 if max_limit == 0.0 && min_limit == 0.0 {
185 return 0.0;
186 }
187
188 max_limit -= min_limit;
189
190 let offset = min_limit;
191 min_limit = 0.0;
192 n -= offset;
193
194 let num_of_max = (n / max_limit).abs().floor();
195
196 if n >= max_limit {
197 n -= num_of_max * max_limit;
198 } else if n < min_limit {
199 n += (num_of_max + 1.0) * max_limit;
200 }
201
202 n + offset
203}
204
205#[inline(always)]
206pub fn lerpf(a: f32, b: f32, t: f32) -> f32 {
207 a + (b - a) * t
208}
209
210#[inline]
212pub fn cubicf(p0: f32, p1: f32, t: f32, m0: f32, m1: f32) -> f32 {
213 let t2 = t * t;
214 let t3 = t2 * t;
215 let scale = (p1 - p0).abs();
216
217 (2.0 * t3 - 3.0 * t2 + 1.0) * p0
218 + (t3 - 2.0 * t2 + t) * m0 * scale
219 + (-2.0 * t3 + 3.0 * t2) * p1
220 + (t3 - t2) * m1 * scale
221}
222
223#[inline]
224pub fn cubicf_derivative(p0: f32, p1: f32, t: f32, m0: f32, m1: f32) -> f32 {
225 let t2 = t * t;
226 let scale = (p1 - p0).abs();
227
228 (6.0 * t2 - 6.0 * t) * p0
229 + (3.0 * t2 - 4.0 * t + 1.0) * m0 * scale
230 + (6.0 * t - 6.0 * t2) * p1
231 + (3.0 * t2 - 2.0 * t) * m1 * scale
232}
233
234#[inline]
235pub fn inf_sup_cubicf(p0: f32, p1: f32, m0: f32, m1: f32) -> (f32, f32) {
236 let d = -(9.0 * p0 * p0 + 6.0 * p0 * (-3.0 * p1 + m1 + m0) + 9.0 * p1 * p1
239 - 6.0 * p1 * (m1 + m0)
240 + m1 * m1
241 + m1 * m0
242 + m0 * m0)
243 .sqrt();
244 let k = 3.0 * (2.0 * p0 - 2.0 * p1 + m1 + m0);
245 let v = 3.0 * p0 - 3.0 * p1 + m1 + 2.0 * m0;
246 let t0 = (-d + v) / k;
247 let t1 = (d + v) / k;
248 (cubicf(p0, p1, t0, m0, m1), cubicf(p0, p1, t1, m0, m1))
249}
250
251#[inline]
252pub fn get_farthest_point(points: &[Vector3<f32>], dir: Vector3<f32>) -> Vector3<f32> {
253 let mut n_farthest = 0;
254 let mut max_dot = -f32::MAX;
255 for (i, point) in points.iter().enumerate() {
256 let dot = dir.dot(point);
257 if dot > max_dot {
258 n_farthest = i;
259 max_dot = dot
260 }
261 }
262 points[n_farthest]
263}
264
265#[inline]
266pub fn get_barycentric_coords(
267 p: &Vector3<f32>,
268 a: &Vector3<f32>,
269 b: &Vector3<f32>,
270 c: &Vector3<f32>,
271) -> (f32, f32, f32) {
272 let v0 = *b - *a;
273 let v1 = *c - *a;
274 let v2 = *p - *a;
275
276 let d00 = v0.dot(&v0);
277 let d01 = v0.dot(&v1);
278 let d11 = v1.dot(&v1);
279 let d20 = v2.dot(&v0);
280 let d21 = v2.dot(&v1);
281 let denom = d00 * d11 - d01.powi(2);
282
283 let v = (d11 * d20 - d01 * d21) / denom;
284 let w = (d00 * d21 - d01 * d20) / denom;
285 let u = 1.0 - v - w;
286
287 (u, v, w)
288}
289
290#[inline]
291pub fn get_barycentric_coords_2d(
292 p: Vector2<f32>,
293 a: Vector2<f32>,
294 b: Vector2<f32>,
295 c: Vector2<f32>,
296) -> (f32, f32, f32) {
297 let v0 = b - a;
298 let v1 = c - a;
299 let v2 = p - a;
300
301 let d00 = v0.dot(&v0);
302 let d01 = v0.dot(&v1);
303 let d11 = v1.dot(&v1);
304 let d20 = v2.dot(&v0);
305 let d21 = v2.dot(&v1);
306 let inv_denom = 1.0 / (d00 * d11 - d01.powi(2));
307
308 let v = (d11 * d20 - d01 * d21) * inv_denom;
309 let w = (d00 * d21 - d01 * d20) * inv_denom;
310 let u = 1.0 - v - w;
311
312 (u, v, w)
313}
314
315#[inline]
316pub fn barycentric_to_world(
317 bary: (f32, f32, f32),
318 pa: Vector3<f32>,
319 pb: Vector3<f32>,
320 pc: Vector3<f32>,
321) -> Vector3<f32> {
322 pa.scale(bary.0) + pb.scale(bary.1) + pc.scale(bary.2)
323}
324
325#[inline]
326pub fn barycentric_is_inside(bary: (f32, f32, f32)) -> bool {
327 (bary.0 >= 0.0) && (bary.1 >= 0.0) && (bary.0 + bary.1 < 1.0)
328}
329
330#[inline]
331pub fn is_point_inside_triangle(p: &Vector3<f32>, vertices: &[Vector3<f32>; 3]) -> bool {
332 let ba = vertices[1] - vertices[0];
333 let ca = vertices[2] - vertices[0];
334 let vp = *p - vertices[0];
335
336 let ba_dot_ba = ba.dot(&ba);
337 let ca_dot_ba = ca.dot(&ba);
338 let ca_dot_ca = ca.dot(&ca);
339
340 let dot02 = ca.dot(&vp);
341 let dot12 = ba.dot(&vp);
342
343 let inv_denom = 1.0 / (ca_dot_ca * ba_dot_ba - ca_dot_ba.powi(2));
344
345 let u = (ba_dot_ba * dot02 - ca_dot_ba * dot12) * inv_denom;
347 let v = (ca_dot_ca * dot12 - ca_dot_ba * dot02) * inv_denom;
348
349 (u >= 0.0) && (v >= 0.0) && (u + v < 1.0)
350}
351
352#[inline]
353pub fn triangle_area(a: Vector3<f32>, b: Vector3<f32>, c: Vector3<f32>) -> f32 {
354 (b - a).cross(&(c - a)).norm() * 0.5
355}
356
357#[inline]
358pub fn solve_quadratic(a: f32, b: f32, c: f32) -> Option<[f32; 2]> {
359 let discriminant = b * b - 4.0 * a * c;
360 if discriminant < 0.0 {
361 None
363 } else {
364 let _2a = 2.0 * a;
367 let discr_root = discriminant.sqrt();
368 let r1 = (-b + discr_root) / _2a;
369 let r2 = (-b - discr_root) / _2a;
370 Some([r1, r2])
371 }
372}
373
374#[inline]
375pub fn spherical_to_cartesian(azimuth: f32, elevation: f32, radius: f32) -> Vector3<f32> {
376 let x = radius * elevation.sin() * azimuth.sin();
377 let y = radius * elevation.cos();
378 let z = -radius * elevation.sin() * azimuth.cos();
379 Vector3::new(x, y, z)
380}
381
382#[inline]
383pub fn ray_rect_intersection(
384 rect: Rect<f32>,
385 origin: Vector2<f32>,
386 dir: Vector2<f32>,
387) -> Option<IntersectionResult> {
388 let min = rect.left_top_corner();
389 let max = rect.right_bottom_corner();
390
391 let (mut tmin, mut tmax) = if dir.x >= 0.0 {
392 ((min.x - origin.x) / dir.x, (max.x - origin.x) / dir.x)
393 } else {
394 ((max.x - origin.x) / dir.x, (min.x - origin.x) / dir.x)
395 };
396
397 let (tymin, tymax) = if dir.y >= 0.0 {
398 ((min.y - origin.y) / dir.y, (max.y - origin.y) / dir.y)
399 } else {
400 ((max.y - origin.y) / dir.y, (min.y - origin.y) / dir.y)
401 };
402
403 if tmin > tymax || tymin > tmax {
404 return None;
405 }
406 if tymin > tmin {
407 tmin = tymin;
408 }
409 if tymax < tmax {
410 tmax = tymax;
411 }
412 if tmin <= 1.0 && tmax >= 0.0 {
413 Some(IntersectionResult {
414 min: tmin,
415 max: tmax,
416 })
417 } else {
418 None
419 }
420}
421
422#[derive(Clone, Copy, Debug, Default)]
423#[repr(C)]
424pub struct TriangleEdge {
425 pub a: u32,
426 pub b: u32,
427}
428
429impl PartialEq for TriangleEdge {
430 fn eq(&self, other: &Self) -> bool {
431 self.a == other.a && self.b == other.b || self.a == other.b && self.b == other.a
432 }
433}
434
435impl Eq for TriangleEdge {}
436
437impl Hash for TriangleEdge {
438 fn hash<H: Hasher>(&self, state: &mut H) {
439 (self.a as u64 + self.b as u64).hash(state)
441 }
442}
443
444#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Hash, Pod, Zeroable)]
445#[repr(C)]
446pub struct TriangleDefinition(pub [u32; 3]);
447
448impl TriangleDefinition {
449 #[inline]
450 pub fn indices(&self) -> &[u32] {
451 self.as_ref()
452 }
453
454 #[inline]
455 pub fn indices_mut(&mut self) -> &mut [u32] {
456 self.as_mut()
457 }
458
459 #[inline]
460 pub fn edges(&self) -> [TriangleEdge; 3] {
461 [
462 TriangleEdge {
463 a: self.0[0],
464 b: self.0[1],
465 },
466 TriangleEdge {
467 a: self.0[1],
468 b: self.0[2],
469 },
470 TriangleEdge {
471 a: self.0[2],
472 b: self.0[0],
473 },
474 ]
475 }
476
477 #[inline]
478 pub fn add(&self, i: u32) -> Self {
479 Self([self.0[0] + i, self.0[1] + i, self.0[2] + i])
480 }
481}
482
483impl Index<usize> for TriangleDefinition {
484 type Output = u32;
485
486 #[inline]
487 fn index(&self, index: usize) -> &Self::Output {
488 &self.0[index]
489 }
490}
491
492impl IndexMut<usize> for TriangleDefinition {
493 #[inline]
494 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
495 &mut self.0[index]
496 }
497}
498
499pub trait PositionProvider: Sized {
500 fn position(&self) -> Vector3<f32>;
501}
502
503impl PositionProvider for Vector3<f32> {
504 #[inline]
505 fn position(&self) -> Vector3<f32> {
506 *self
507 }
508}
509
510impl AsRef<[u32]> for TriangleDefinition {
511 #[inline]
512 fn as_ref(&self) -> &[u32] {
513 &self.0
514 }
515}
516
517impl AsMut<[u32]> for TriangleDefinition {
518 #[inline]
519 fn as_mut(&mut self) -> &mut [u32] {
520 &mut self.0
521 }
522}
523
524#[inline]
530pub fn get_closest_point<P: PositionProvider>(points: &[P], point: Vector3<f32>) -> Option<usize> {
531 let mut closest_sqr_distance = f32::MAX;
532 let mut closest_index = None;
533 for (i, vertex) in points.iter().enumerate() {
534 let sqr_distance = (vertex.position() - point).norm_squared();
535 if sqr_distance < closest_sqr_distance {
536 closest_sqr_distance = sqr_distance;
537 closest_index = Some(i);
538 }
539 }
540 closest_index
541}
542
543#[inline]
545pub fn get_closest_point_triangles<P>(
546 points: &[P],
547 triangles: &[TriangleDefinition],
548 triangle_indices: impl Iterator<Item = usize>,
549 point: Vector3<f32>,
550) -> Option<(usize, usize)>
551where
552 P: PositionProvider,
553{
554 let mut closest_sqr_distance = f32::MAX;
555 let mut closest_index = None;
556 for triangle_index in triangle_indices {
557 let triangle = triangles.get(triangle_index).unwrap();
558 for point_index in triangle.0.iter() {
559 let vertex = points.get(*point_index as usize).unwrap();
560 let sqr_distance = (vertex.position() - point).norm_squared();
561 if sqr_distance < closest_sqr_distance {
562 closest_sqr_distance = sqr_distance;
563 closest_index = Some((*point_index as usize, triangle_index));
564 }
565 }
566 }
567 closest_index
568}
569
570#[inline]
571pub fn get_arbitrary_line_perpendicular(
572 begin: Vector3<f32>,
573 end: Vector3<f32>,
574) -> Option<Vector3<f32>> {
575 let dir = (end - begin).try_normalize(f32::EPSILON)?;
576 for axis in [Vector3::z(), Vector3::y(), Vector3::x()] {
577 let perp = dir.cross(&axis);
578 if perp.norm_squared().ne(&0.0) {
579 return Some(perp);
580 }
581 }
582 None
583}
584
585#[inline]
587pub fn get_closest_point_triangle_set<P>(
588 points: &[P],
589 triangles: &[TriangleDefinition],
590 point: Vector3<f32>,
591) -> Option<(usize, usize)>
592where
593 P: PositionProvider,
594{
595 let mut closest_sqr_distance = f32::MAX;
596 let mut closest_index = None;
597 for (triangle_index, triangle) in triangles.iter().enumerate() {
598 for point_index in triangle.0.iter() {
599 let vertex = points.get(*point_index as usize).unwrap();
600 let sqr_distance = (vertex.position() - point).norm_squared();
601 if sqr_distance < closest_sqr_distance {
602 closest_sqr_distance = sqr_distance;
603 closest_index = Some((*point_index as usize, triangle_index));
604 }
605 }
606 }
607 closest_index
608}
609
610#[derive(Debug, PartialEq, Clone)]
611pub struct SmoothAngle {
612 pub angle: f32,
614
615 pub target: f32,
617
618 pub speed: f32,
620}
621
622impl SmoothAngle {
623 #[inline]
624 pub fn set_target(&mut self, angle: f32) -> &mut Self {
625 self.target = angle;
626 self
627 }
628
629 #[inline]
630 pub fn update(&mut self, dt: f32) -> &mut Self {
631 self.target = wrap_angle(self.target);
632 self.angle = wrap_angle(self.angle);
633 if !self.at_target() {
634 let delta = self.speed * dt;
635 if self.distance().abs() > delta {
636 self.angle += self.turn_direction() * delta;
637 } else {
638 self.angle = self.target;
639 }
640 }
641 self
642 }
643
644 #[inline]
645 pub fn set_speed(&mut self, speed: f32) -> &mut Self {
646 self.speed = speed;
647 self
648 }
649
650 #[inline]
651 pub fn set_angle(&mut self, angle: f32) -> &mut Self {
652 self.angle = angle;
653 self
654 }
655
656 #[inline]
657 pub fn angle(&self) -> f32 {
658 self.angle
659 }
660
661 #[inline]
662 pub fn at_target(&self) -> bool {
663 (self.target - self.angle).abs() <= f32::EPSILON
664 }
665
666 #[inline]
667 pub fn distance(&self) -> f32 {
668 let diff = (self.target - self.angle + std::f32::consts::PI) % std::f32::consts::TAU
669 - std::f32::consts::PI;
670 if diff < -std::f32::consts::PI {
671 diff + std::f32::consts::TAU
672 } else {
673 diff
674 }
675 }
676
677 #[inline]
678 fn turn_direction(&self) -> f32 {
679 let distance = self.distance();
680
681 if distance < 0.0 {
682 if distance < -std::f32::consts::PI {
683 1.0
684 } else {
685 -1.0
686 }
687 } else if distance > std::f32::consts::PI {
688 -1.0
689 } else {
690 1.0
691 }
692 }
693}
694
695impl Default for SmoothAngle {
696 fn default() -> Self {
697 Self {
698 angle: 0.0,
699 target: 0.0,
700 speed: 1.0,
701 }
702 }
703}
704
705#[derive(Copy, Clone, Hash, PartialOrd, PartialEq, Ord, Eq)]
706pub enum RotationOrder {
707 XYZ,
708 XZY,
709 YZX,
710 YXZ,
711 ZXY,
712 ZYX,
713}
714
715#[inline]
716pub fn quat_from_euler<T: SimdRealField + RealField + Copy + Clone>(
717 euler_radians: Vector3<T>,
718 order: RotationOrder,
719) -> UnitQuaternion<T> {
720 let qx = UnitQuaternion::from_axis_angle(&Vector3::x_axis(), euler_radians.x);
721 let qy = UnitQuaternion::from_axis_angle(&Vector3::y_axis(), euler_radians.y);
722 let qz = UnitQuaternion::from_axis_angle(&Vector3::z_axis(), euler_radians.z);
723 match order {
724 RotationOrder::XYZ => qz * qy * qx,
725 RotationOrder::XZY => qy * qz * qx,
726 RotationOrder::YZX => qx * qz * qy,
727 RotationOrder::YXZ => qz * qx * qy,
728 RotationOrder::ZXY => qy * qx * qz,
729 RotationOrder::ZYX => qx * qy * qz,
730 }
731}
732
733pub trait Matrix4Ext<T: Scalar> {
734 fn side(&self) -> Vector3<T>;
735 fn up(&self) -> Vector3<T>;
736 fn look(&self) -> Vector3<T>;
737 fn position(&self) -> Vector3<T>;
738 fn basis(&self) -> Matrix3<T>;
739}
740
741impl<T: Scalar + Default + Copy + Clone> Matrix4Ext<T> for Matrix4<T> {
742 #[inline]
743 fn side(&self) -> Vector3<T> {
744 Vector3::new(self[0], self[1], self[2])
745 }
746
747 #[inline]
748 fn up(&self) -> Vector3<T> {
749 Vector3::new(self[4], self[5], self[6])
750 }
751
752 #[inline]
753 fn look(&self) -> Vector3<T> {
754 Vector3::new(self[8], self[9], self[10])
755 }
756
757 #[inline]
758 fn position(&self) -> Vector3<T> {
759 Vector3::new(self[12], self[13], self[14])
760 }
761
762 #[inline]
763 fn basis(&self) -> Matrix3<T> {
764 self.fixed_resize::<3, 3>(T::default())
765 }
766}
767
768pub trait Matrix3Ext<T: Scalar> {
769 fn side(&self) -> Vector3<T>;
770 fn up(&self) -> Vector3<T>;
771 fn look(&self) -> Vector3<T>;
772}
773
774impl<T: Scalar + Copy + Clone> Matrix3Ext<T> for Matrix3<T> {
775 #[inline]
776 fn side(&self) -> Vector3<T> {
777 Vector3::new(self[0], self[1], self[2])
778 }
779
780 #[inline]
781 fn up(&self) -> Vector3<T> {
782 Vector3::new(self[3], self[4], self[5])
783 }
784
785 #[inline]
786 fn look(&self) -> Vector3<T> {
787 Vector3::new(self[6], self[7], self[8])
788 }
789}
790
791pub trait Vector3Ext {
792 fn follow(&mut self, other: &Self, fraction: f32);
793
794 fn sqr_distance(&self, other: &Self) -> f32;
795
796 fn non_uniform_scale(&self, other: &Self) -> Self;
797}
798
799impl Vector3Ext for Vector3<f32> {
800 #[inline]
801 fn follow(&mut self, other: &Self, fraction: f32) {
802 self.x += (other.x - self.x) * fraction;
803 self.y += (other.y - self.y) * fraction;
804 self.z += (other.z - self.z) * fraction;
805 }
806
807 #[inline]
808 fn sqr_distance(&self, other: &Self) -> f32 {
809 (self - other).norm_squared()
810 }
811
812 #[inline]
813 fn non_uniform_scale(&self, other: &Self) -> Self {
814 Self::new(self.x * other.x, self.y * other.y, self.z * other.z)
815 }
816}
817
818pub trait Vector2Ext {
819 fn follow(&mut self, other: &Self, fraction: f32);
820
821 fn per_component_min(&self, other: &Self) -> Self;
822 fn per_component_max(&self, other: &Self) -> Self;
823}
824
825impl Vector2Ext for Vector2<f32> {
826 #[inline]
827 fn follow(&mut self, other: &Self, fraction: f32) {
828 self.x += (other.x - self.x) * fraction;
829 self.y += (other.y - self.y) * fraction;
830 }
831
832 #[inline]
833 fn per_component_min(&self, other: &Self) -> Self {
834 Self::new(self.x.min(other.x), self.y.min(other.y))
835 }
836
837 #[inline]
838 fn per_component_max(&self, other: &Self) -> Self {
839 Self::new(self.x.max(other.x), self.y.max(other.y))
840 }
841}
842
843#[inline]
846pub fn vector_to_quat(vec: Vector3<f32>) -> UnitQuaternion<f32> {
847 if vec.norm() == 0.0 {
848 return Default::default();
849 }
850
851 let dot = vec.normalize().dot(&Vector3::y());
852
853 if dot.abs() > 1.0 - 10.0 * f32::EPSILON {
854 UnitQuaternion::from_axis_angle(&Vector3::x_axis(), -dot.signum() * 90.0f32.to_radians())
856 } else {
857 UnitQuaternion::face_towards(&vec, &Vector3::y())
858 }
859}
860
861#[inline]
862pub fn m4x4_approx_eq(a: &Matrix4<f32>, b: &Matrix4<f32>) -> bool {
863 a.iter()
864 .zip(b.iter())
865 .all(|(a, b)| (*a - *b).abs() <= 0.001)
866}
867
868#[cfg(test)]
869mod test {
870 use nalgebra::{Matrix3, Matrix4, UnitQuaternion, Vector3};
871 use num_traits::Zero;
872
873 use super::{
874 barycentric_is_inside, barycentric_to_world, cubicf_derivative, get_barycentric_coords,
875 get_barycentric_coords_2d, get_closest_point, get_closest_point_triangle_set,
876 get_closest_point_triangles, get_farthest_point, get_signed_triangle_area, ieee_remainder,
877 inf_sup_cubicf, quat_from_euler, round_to_step, spherical_to_cartesian, triangle_area,
878 wrap_angle, wrapf, Matrix3Ext, Matrix4Ext, PositionProvider, Rect, RotationOrder,
879 SmoothAngle, TriangleDefinition, TriangleEdge, Vector2Ext, Vector3Ext,
880 };
881 use nalgebra::Vector2;
882
883 #[test]
884 fn ray_rect_intersection() {
885 let rect = Rect::new(0.0, 0.0, 10.0, 10.0);
886
887 assert!(super::ray_rect_intersection(
889 rect,
890 Vector2::new(-1.0, 5.0),
891 Vector2::new(1.0, 0.0)
892 )
893 .is_some());
894
895 assert!(super::ray_rect_intersection(
897 rect,
898 Vector2::new(5.0, -1.0),
899 Vector2::new(0.0, 1.0)
900 )
901 .is_some());
902 }
903
904 #[test]
905 fn smooth_angle() {
906 let mut angle = SmoothAngle {
907 angle: 290.0f32.to_radians(),
908 target: 90.0f32.to_radians(),
909 speed: 100.0f32.to_radians(),
910 };
911
912 while !angle.at_target() {
913 println!("{}", angle.update(1.0).angle().to_degrees());
914 }
915 }
916
917 #[test]
918 fn default_for_rect() {
919 assert_eq!(
920 Rect::<f32>::default(),
921 Rect {
922 position: Vector2::new(Zero::zero(), Zero::zero()),
923 size: Vector2::new(Zero::zero(), Zero::zero()),
924 }
925 );
926 }
927
928 #[test]
929 fn rect_with_position() {
930 let rect = Rect::new(0, 0, 1, 1);
931
932 assert_eq!(
933 rect.with_position(Vector2::new(1, 1)),
934 Rect::new(1, 1, 1, 1)
935 );
936 }
937
938 #[test]
939 fn rect_with_size() {
940 let rect = Rect::new(0, 0, 1, 1);
941
942 assert_eq!(
943 rect.with_size(Vector2::new(10, 10)),
944 Rect::new(0, 0, 10, 10)
945 );
946 }
947
948 #[test]
949 fn rect_inflate() {
950 let rect = Rect::new(0, 0, 1, 1);
951
952 assert_eq!(rect.inflate(5, 5), Rect::new(-5, -5, 11, 11));
953 }
954
955 #[test]
956 fn rect_deflate() {
957 let rect = Rect::new(-5, -5, 11, 11);
958
959 assert_eq!(rect.deflate(5, 5), Rect::new(0, 0, 1, 1));
960 }
961
962 #[test]
963 fn rect_contains() {
964 let rect = Rect::new(0, 0, 10, 10);
965
966 assert!(rect.contains(Vector2::new(0, 0)));
967 assert!(rect.contains(Vector2::new(0, 10)));
968 assert!(rect.contains(Vector2::new(10, 0)));
969 assert!(rect.contains(Vector2::new(10, 10)));
970 assert!(rect.contains(Vector2::new(5, 5)));
971
972 assert!(!rect.contains(Vector2::new(0, 20)));
973 }
974
975 #[test]
976 fn rect_center() {
977 let rect = Rect::new(0, 0, 10, 10);
978
979 assert_eq!(rect.center(), Vector2::new(5, 5));
980 }
981
982 #[test]
983 fn rect_push() {
984 let mut rect = Rect::new(10, 10, 11, 11);
985
986 rect.push(Vector2::new(0, 0));
987 assert_eq!(rect, Rect::new(0, 0, 21, 21));
988
989 rect.push(Vector2::new(0, 20));
990 assert_eq!(rect, Rect::new(0, 0, 21, 21));
991
992 rect.push(Vector2::new(20, 20));
993 assert_eq!(rect, Rect::new(0, 0, 21, 21));
994
995 rect.push(Vector2::new(30, 30));
996 assert_eq!(rect, Rect::new(0, 0, 30, 30));
997 }
998
999 #[test]
1000 fn rect_getters() {
1001 let rect = Rect::new(0, 0, 1, 1);
1002
1003 assert_eq!(rect.left_top_corner(), Vector2::new(0, 0));
1004 assert_eq!(rect.left_bottom_corner(), Vector2::new(0, 1));
1005 assert_eq!(rect.right_top_corner(), Vector2::new(1, 0));
1006 assert_eq!(rect.right_bottom_corner(), Vector2::new(1, 1));
1007
1008 assert_eq!(rect.x(), 0);
1009 assert_eq!(rect.y(), 0);
1010 assert_eq!(rect.w(), 1);
1011 assert_eq!(rect.h(), 1);
1012 }
1013
1014 #[test]
1015 fn rect_clip_by() {
1016 let rect = Rect::new(0, 0, 10, 10);
1017
1018 assert_eq!(
1019 rect.clip_by(Rect::new(2, 2, 1, 1)).unwrap(),
1020 Rect::new(2, 2, 1, 1)
1021 );
1022 assert_eq!(
1023 rect.clip_by(Rect::new(0, 0, 15, 15)).unwrap(),
1024 Rect::new(0, 0, 10, 10)
1025 );
1026
1027 assert!(rect.clip_by(Rect::new(-2, 1, 1, 1)).is_none());
1029 assert!(rect.clip_by(Rect::new(11, 1, 1, 1)).is_none());
1030 assert!(rect.clip_by(Rect::new(1, -2, 1, 1)).is_none());
1031 assert!(rect.clip_by(Rect::new(1, 11, 1, 1)).is_none());
1032 }
1033
1034 #[test]
1035 fn rect_translate() {
1036 let rect = Rect::new(0, 0, 10, 10);
1037
1038 assert_eq!(rect.translate(Vector2::new(5, 5)), Rect::new(5, 5, 10, 10));
1039 }
1040
1041 #[test]
1042 fn rect_intersects_circle() {
1043 let rect = Rect::new(0.0, 0.0, 1.0, 1.0);
1044
1045 assert!(!rect.intersects_circle(Vector2::new(5.0, 5.0), 1.0));
1046 assert!(rect.intersects_circle(Vector2::new(0.0, 0.0), 1.0));
1047 assert!(rect.intersects_circle(Vector2::new(-0.5, -0.5), 1.0));
1048 }
1049
1050 #[test]
1051 fn rect_extend_to_contain() {
1052 let mut rect = Rect::new(0.0, 0.0, 1.0, 1.0);
1053
1054 rect.extend_to_contain(Rect::new(1.0, 1.0, 1.0, 1.0));
1055 assert_eq!(rect, Rect::new(0.0, 0.0, 2.0, 2.0));
1056
1057 rect.extend_to_contain(Rect::new(-1.0, -1.0, 1.0, 1.0));
1058 assert_eq!(rect, Rect::new(-1.0, -1.0, 3.0, 3.0));
1059 }
1060
1061 #[test]
1062 fn rect_transform() {
1063 let rect = Rect::new(0.0, 0.0, 1.0, 1.0);
1064
1065 assert_eq!(
1066 rect.transform(&Matrix3::new(
1067 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0,
1070 )),
1071 rect,
1072 );
1073
1074 assert_eq!(
1075 rect.transform(&Matrix3::new(
1076 2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 2.0,
1079 )),
1080 Rect::new(0.0, 0.0, 2.0, 2.0),
1081 );
1082 }
1083
1084 #[test]
1085 fn test_get_signed_triangle_area() {
1086 assert_eq!(
1087 get_signed_triangle_area(
1088 Vector2::new(0.0, 0.0),
1089 Vector2::new(0.0, 1.0),
1090 Vector2::new(1.0, 0.0)
1091 ),
1092 0.5
1093 );
1094 assert_eq!(
1095 get_signed_triangle_area(
1096 Vector2::new(1.0, 1.0),
1097 Vector2::new(0.0, 1.0),
1098 Vector2::new(1.0, 0.0)
1099 ),
1100 -0.5
1101 );
1102 }
1103
1104 #[test]
1105 fn test_wrap_angle() {
1106 let angle = 0.5 * std::f32::consts::PI;
1107 assert_eq!(wrap_angle(angle), angle);
1108 assert_eq!(wrap_angle(-angle), 3.0 * angle);
1109 }
1110
1111 #[test]
1112 fn test_ieee_remainder() {
1113 assert_eq!(ieee_remainder(1.0, 2.0), -1.0);
1114 assert_eq!(ieee_remainder(3.0, 2.0), -1.0);
1115
1116 assert_eq!(ieee_remainder(1.0, 3.0), 1.0);
1117 assert_eq!(ieee_remainder(4.0, 3.0), 1.0);
1118
1119 assert_eq!(ieee_remainder(-1.0, 2.0), 1.0);
1120 assert_eq!(ieee_remainder(-3.0, 2.0), 1.0);
1121 }
1122
1123 #[test]
1124 fn test_round_to_step() {
1125 assert_eq!(round_to_step(1.0, 2.0), 2.0);
1126 assert_eq!(round_to_step(3.0, 2.0), 4.0);
1127
1128 assert_eq!(round_to_step(-1.0, 2.0), -2.0);
1129 assert_eq!(round_to_step(-3.0, 2.0), -4.0);
1130 }
1131
1132 #[test]
1133 fn test_wrapf() {
1134 assert_eq!(wrapf(5.0, 0.0, 10.0), 5.0);
1135 assert_eq!(wrapf(5.0, 0.0, 0.0), 0.0);
1136 assert_eq!(wrapf(2.0, 5.0, 10.0), 7.0);
1137 assert_eq!(wrapf(12.0, 5.0, 10.0), 7.0);
1138 }
1139
1140 #[test]
1141 fn test_cubicf_derivative() {
1142 assert_eq!(cubicf_derivative(1.0, 1.0, 1.0, 1.0, 1.0), 0.0);
1143 assert_eq!(cubicf_derivative(2.0, 1.0, 1.0, 1.0, 1.0), 1.0);
1144 }
1145
1146 #[test]
1147 fn test_inf_sup_cubicf() {
1148 assert_eq!(inf_sup_cubicf(1.0, 1.0, 1.0, 1.0), (1.0, 1.0));
1149 assert_eq!(inf_sup_cubicf(2.0, 2.0, 1.0, 1.0), (2.0, 2.0));
1150 }
1151
1152 #[test]
1153 fn test_get_farthest_point() {
1154 let points = [
1155 Vector3::new(0.0, 0.0, 0.0),
1156 Vector3::new(1.0, 0.0, 0.0),
1157 Vector3::new(0.0, 1.0, 0.0),
1158 Vector3::new(0.0, 0.0, 1.0),
1159 Vector3::new(1.0, 1.0, 1.0),
1160 ];
1161
1162 assert_eq!(
1163 get_farthest_point(&points, Vector3::new(1.0, 0.0, 0.0)),
1164 Vector3::new(1.0, 0.0, 0.0)
1165 );
1166 assert_eq!(
1167 get_farthest_point(&points, Vector3::new(10.0, 0.0, 0.0)),
1168 Vector3::new(1.0, 0.0, 0.0)
1169 );
1170 assert_eq!(
1171 get_farthest_point(&points, Vector3::new(1.0, 1.0, 0.0)),
1172 Vector3::new(1.0, 1.0, 1.0)
1173 );
1174 }
1175
1176 #[test]
1177 fn test_get_barycentric_coords() {
1178 assert_eq!(
1179 get_barycentric_coords(
1180 &Vector3::new(0.0, 0.0, 0.0),
1181 &Vector3::new(1.0, 0.0, 0.0),
1182 &Vector3::new(0.0, 1.0, 0.0),
1183 &Vector3::new(0.0, 0.0, 1.0),
1184 ),
1185 (0.33333328, 0.33333334, 0.33333334)
1186 );
1187 }
1188
1189 #[test]
1190 fn test_get_barycentric_coords_2d() {
1191 assert_eq!(
1192 get_barycentric_coords_2d(
1193 Vector2::new(0.0, 0.0),
1194 Vector2::new(1.0, 0.0),
1195 Vector2::new(0.0, 1.0),
1196 Vector2::new(0.0, 0.0),
1197 ),
1198 (0.0, 0.0, 1.0)
1199 );
1200 }
1201
1202 #[test]
1203 fn test_barycentric_to_world() {
1204 assert_eq!(
1205 barycentric_to_world(
1206 (2.0, 2.0, 2.0),
1207 Vector3::new(1.0, 0.0, 0.0),
1208 Vector3::new(0.0, 1.0, 0.0),
1209 Vector3::new(0.0, 0.0, 1.0),
1210 ),
1211 Vector3::new(2.0, 2.0, 2.0)
1212 );
1213 }
1214
1215 #[test]
1216 fn test_barycentric_is_inside() {
1217 assert!(barycentric_is_inside((0.0, 0.0, 0.0)));
1218 assert!(barycentric_is_inside((0.5, 0.49, 0.0)));
1219
1220 assert!(!barycentric_is_inside((0.5, 0.5, 0.0)));
1221 assert!(!barycentric_is_inside((-0.5, 0.49, 0.0)));
1222 assert!(!barycentric_is_inside((0.5, -0.49, 0.0)));
1223 assert!(!barycentric_is_inside((-0.5, -0.49, 0.0)));
1224 }
1225
1226 #[test]
1227 fn test_triangle_area() {
1228 assert_eq!(
1229 triangle_area(
1230 Vector3::new(0.0, 0.0, 0.0),
1231 Vector3::new(0.0, 1.0, 0.0),
1232 Vector3::new(0.0, 0.0, 1.0),
1233 ),
1234 0.5
1235 );
1236 }
1237
1238 #[test]
1239 fn test_spherical_to_cartesian() {
1240 assert_eq!(
1241 spherical_to_cartesian(0.0, 0.0, 1.0),
1242 Vector3::new(0.0, 1.0, 0.0)
1243 );
1244 }
1245
1246 #[test]
1247 fn partial_eq_for_triangle_edge() {
1248 let te = TriangleEdge { a: 2, b: 5 };
1249 let te2 = TriangleEdge { a: 2, b: 5 };
1250 let te3 = TriangleEdge { a: 5, b: 2 };
1251
1252 assert_eq!(te, te2);
1253 assert_eq!(te, te3);
1254 }
1255
1256 #[test]
1257 fn triangle_definition_indices() {
1258 assert_eq!(TriangleDefinition([0, 0, 0]).indices(), &[0, 0, 0]);
1259 }
1260
1261 #[test]
1262 fn triangle_definition_indices_mut() {
1263 assert_eq!(TriangleDefinition([0, 0, 0]).indices_mut(), &mut [0, 0, 0]);
1264 }
1265
1266 #[test]
1267 fn triangle_definition_edges() {
1268 let t = TriangleDefinition([0, 1, 2]);
1269 assert_eq!(
1270 t.edges(),
1271 [
1272 TriangleEdge { a: 0, b: 1 },
1273 TriangleEdge { a: 1, b: 2 },
1274 TriangleEdge { a: 2, b: 0 }
1275 ]
1276 );
1277 }
1278
1279 #[test]
1280 fn index_for_triangle_definition() {
1281 let t = TriangleDefinition([0, 1, 2]);
1282
1283 assert_eq!(t[0], 0);
1284 assert_eq!(t[1], 1);
1285 assert_eq!(t[2], 2);
1286 }
1287
1288 #[test]
1289 fn index_mut_for_triangle_definition() {
1290 let mut t = TriangleDefinition([5, 5, 5]);
1291 t[0] = 0;
1292 t[1] = 1;
1293 t[2] = 2;
1294
1295 assert_eq!(t[0], 0);
1296 assert_eq!(t[1], 1);
1297 assert_eq!(t[2], 2);
1298 }
1299
1300 #[test]
1301 fn position_provider_for_vector3() {
1302 let v = Vector3::new(0.0, 1.0, 2.0);
1303
1304 assert_eq!(v.position(), v);
1305 }
1306
1307 #[test]
1308 fn as_ref_for_triangle_definition() {
1309 let t = TriangleDefinition([0, 1, 2]);
1310
1311 assert_eq!(t.as_ref(), &[0, 1, 2]);
1312 }
1313
1314 #[test]
1315 fn as_mut_for_triangle_definition() {
1316 let mut t = TriangleDefinition([0, 1, 2]);
1317
1318 assert_eq!(t.as_mut(), &mut [0, 1, 2]);
1319 }
1320
1321 #[test]
1322 fn test_get_closest_point() {
1323 let points = [
1324 Vector3::new(1.0, 0.0, 0.0),
1325 Vector3::new(0.0, 1.0, 0.0),
1326 Vector3::new(0.0, 0.0, 1.0),
1327 ];
1328
1329 assert_eq!(
1330 get_closest_point(&points, Vector3::new(0.0, 0.0, 0.0)),
1331 Some(0),
1332 );
1333 assert_eq!(
1334 get_closest_point(&points, Vector3::new(0.0, 1.0, 1.0)),
1335 Some(1),
1336 );
1337 assert_eq!(
1338 get_closest_point(&points, Vector3::new(0.0, 0.0, 10.0)),
1339 Some(2),
1340 );
1341 }
1342
1343 #[test]
1344 fn test_get_closest_point_triangles() {
1345 let points = [
1346 Vector3::new(0.0, 0.0, 0.0),
1347 Vector3::new(1.0, 0.0, 0.0),
1348 Vector3::new(0.0, 1.0, 0.0),
1349 Vector3::new(0.0, 0.0, 1.0),
1350 ];
1351 let triangles = [TriangleDefinition([0, 1, 2]), TriangleDefinition([1, 2, 3])];
1352
1353 assert_eq!(
1354 get_closest_point_triangles(
1355 &points,
1356 &triangles,
1357 [0, 1].into_iter(),
1358 Vector3::new(1.0, 1.0, 1.0)
1359 ),
1360 Some((1, 0))
1361 );
1362 }
1363
1364 #[test]
1365 fn test_get_closest_point_triangle_set() {
1366 let points = [
1367 Vector3::new(0.0, 0.0, 0.0),
1368 Vector3::new(1.0, 0.0, 0.0),
1369 Vector3::new(0.0, 1.0, 0.0),
1370 Vector3::new(0.0, 0.0, 1.0),
1371 ];
1372 let triangles = [TriangleDefinition([0, 1, 2]), TriangleDefinition([1, 2, 3])];
1373
1374 assert_eq!(
1375 get_closest_point_triangle_set(&points, &triangles, Vector3::new(1.0, 1.0, 1.0)),
1376 Some((1, 0))
1377 );
1378 }
1379
1380 #[test]
1381 fn smooth_angle_setters() {
1382 let mut sa = SmoothAngle {
1383 angle: 0.0,
1384 speed: 0.0,
1385 target: 0.0,
1386 };
1387
1388 assert_eq!(sa.angle(), 0.0);
1389
1390 sa.set_angle(std::f32::consts::PI);
1391 assert_eq!(sa.angle(), std::f32::consts::PI);
1392
1393 sa.set_target(std::f32::consts::PI);
1394 assert_eq!(sa.target, std::f32::consts::PI);
1395
1396 sa.set_speed(1.0);
1397 assert_eq!(sa.speed, 1.0);
1398 }
1399
1400 #[test]
1401 fn smooth_angle_turn_direction() {
1402 assert_eq!(
1403 SmoothAngle {
1404 angle: 0.0,
1405 speed: 0.0,
1406 target: std::f32::consts::PI * 1.1,
1407 }
1408 .turn_direction(),
1409 -1.0
1410 );
1411
1412 assert_eq!(
1413 SmoothAngle {
1414 angle: 0.0,
1415 speed: 0.0,
1416 target: -std::f32::consts::PI * 1.1,
1417 }
1418 .turn_direction(),
1419 1.0
1420 );
1421
1422 assert_eq!(
1423 SmoothAngle {
1424 angle: 0.0,
1425 speed: 0.0,
1426 target: -std::f32::consts::PI * 0.9,
1427 }
1428 .turn_direction(),
1429 -1.0
1430 );
1431
1432 assert_eq!(
1433 SmoothAngle {
1434 angle: 0.0,
1435 speed: 0.0,
1436 target: std::f32::consts::PI * 0.9,
1437 }
1438 .turn_direction(),
1439 1.0
1440 );
1441 }
1442
1443 #[test]
1444 fn default_for_smooth_angle() {
1445 let sa = SmoothAngle::default();
1446
1447 assert_eq!(sa.angle, 0.0);
1448 assert_eq!(sa.target, 0.0);
1449 assert_eq!(sa.speed, 1.0);
1450 }
1451
1452 #[test]
1453 fn test_quat_from_euler() {
1454 assert_eq!(
1455 quat_from_euler(
1456 Vector3::new(
1457 std::f32::consts::PI,
1458 std::f32::consts::PI,
1459 std::f32::consts::PI
1460 ),
1461 RotationOrder::XYZ
1462 ),
1463 UnitQuaternion::from_euler_angles(
1464 std::f32::consts::PI,
1465 std::f32::consts::PI,
1466 std::f32::consts::PI
1467 )
1468 );
1469 }
1470
1471 #[test]
1472 fn matrix4_ext_for_matrix4() {
1473 let m = Matrix4::new(
1474 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
1478 );
1479
1480 assert_eq!(m.side(), Vector3::new(1.0, 0.0, 0.0));
1481 assert_eq!(m.up(), Vector3::new(0.0, 1.0, 0.0));
1482 assert_eq!(m.look(), Vector3::new(0.0, 0.0, 1.0));
1483 assert_eq!(m.position(), Vector3::new(0.0, 0.0, 0.0));
1484 assert_eq!(
1485 m.basis(),
1486 Matrix3::new(
1487 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0,
1490 )
1491 );
1492 }
1493
1494 #[test]
1495 fn matrix3_ext_for_matrix3() {
1496 let m = Matrix3::new(
1497 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0,
1500 );
1501
1502 assert_eq!(m.side(), Vector3::new(1.0, 0.0, 0.0));
1503 assert_eq!(m.up(), Vector3::new(0.0, 1.0, 0.0));
1504 assert_eq!(m.look(), Vector3::new(0.0, 0.0, 1.0));
1505 }
1506
1507 #[test]
1508 fn vector3_ext_for_vector3() {
1509 let mut v = Vector3::new(2.0, 2.0, 2.0);
1510
1511 assert_eq!(v.sqr_distance(&Vector3::new(0.0, 1.0, 1.0)), 6.0);
1512
1513 assert_eq!(
1514 v.non_uniform_scale(&Vector3::new(3.0, 3.0, 3.0)),
1515 Vector3::new(6.0, 6.0, 6.0)
1516 );
1517
1518 v.follow(&Vector3::new(0.5, 0.5, 0.5), 2.0);
1519 assert_eq!(v, Vector3::new(-1.0, -1.0, -1.0));
1520 }
1521
1522 #[test]
1523 fn vector2_ext_for_vector2() {
1524 let mut v = Vector2::new(2.0, 2.0);
1525
1526 assert_eq!(
1527 v.per_component_min(&Vector2::new(0.0, 4.0)),
1528 Vector2::new(0.0, 2.0)
1529 );
1530
1531 assert_eq!(
1532 v.per_component_max(&Vector2::new(0.0, 4.0)),
1533 Vector2::new(2.0, 4.0)
1534 );
1535
1536 v.follow(&Vector2::new(0.5, 0.5), 2.0);
1537 assert_eq!(v, Vector2::new(-1.0, -1.0));
1538 }
1539
1540 #[test]
1541 fn test_m4x4_approx_eq() {
1542 assert!(crate::m4x4_approx_eq(
1543 &Matrix4::new(
1544 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
1548 ),
1549 &Matrix4::new(
1550 1.0001, 0.0001, 0.0001, 0.0001, 0.0001, 1.0001, 0.0001, 0.0001, 0.0001, 0.0001, 1.0001, 0.0001, 0.0001, 0.0001, 0.0001, 1.0001,
1554 )
1555 ),);
1556 }
1557}