1use std::ops::{Add, Rem};
4
5use vecmath::{self, traits::Float};
6pub use vecmath::{
7 mat2x3_inv as invert, row_mat2x3_mul as multiply, row_mat2x3_transform_pos2 as transform_pos,
8 row_mat2x3_transform_vec2 as transform_vec, vec2_add as add, vec2_cast as cast,
9 vec2_cross as cross, vec2_dot as dot, vec2_mul as mul, vec2_scale as mul_scalar,
10 vec2_square_len as square_len, vec2_sub as sub,
11};
12
13use crate::{
14 modular_index::previous,
15 types::{Area, Color, Line, Polygon, Ray, Rectangle, SourceRectangle, Triangle},
16};
17
18pub type Scalar = f64;
20
21pub type Matrix2d<T = Scalar> = vecmath::Matrix2x3<T>;
23
24pub type Vec2d<T = Scalar> = vecmath::Vector2<T>;
26
27pub type Vec3d<T = Scalar> = vecmath::Vector3<T>;
29
30#[inline(always)]
32pub fn perp<T>(v: [T; 2]) -> [T; 2]
33where
34 T: Float,
35{
36 [-v[1], v[0]]
37}
38
39#[allow(clippy::just_underscores_and_digits)] #[inline(always)]
46pub fn abs_transform<T>(w: T, h: T) -> Matrix2d<T>
47where
48 T: Float,
49{
50 use vecmath::traits::{FromPrimitive, One, Zero};
51
52 let _0: T = Zero::zero();
53 let _1: T = One::one();
54 let _2: T = FromPrimitive::from_f64(2.0);
55 let sx = _2 / w;
56 let sy = -_2 / h;
57 [[sx, _0, -_1], [_0, sy, _1]]
58}
59
60#[allow(clippy::just_underscores_and_digits)] #[inline(always)]
63pub fn translate<T>(v: Vec2d<T>) -> Matrix2d<T>
64where
65 T: Float,
66{
67 use vecmath::traits::{One, Zero};
68
69 let _0: T = Zero::zero();
70 let _1: T = One::one();
71 [[_1, _0, v[0]], [_0, _1, v[1]]]
72}
73
74#[allow(clippy::just_underscores_and_digits)] #[inline(always)]
77pub fn rotate_radians<T>(angle: T) -> Matrix2d<T>
78where
79 T: Float,
80{
81 use vecmath::traits::Zero;
82
83 let _0 = Zero::zero();
84 let c = angle.cos();
85 let s = angle.sin();
86 [[c, -s, _0], [s, c, _0]]
87}
88
89#[allow(clippy::just_underscores_and_digits)] #[inline(always)]
95pub fn orient<T>(x: T, y: T) -> Matrix2d<T>
96where
97 T: Float,
98{
99 use vecmath::traits::Zero;
100
101 let _0: T = Zero::zero();
102 let len = x * x + y * y;
103 if len == _0 {
104 return identity();
105 }
106
107 let len = len.sqrt();
108 let c = x / len;
109 let s = y / len;
110 [[c, -s, _0], [s, c, _0]]
111}
112
113#[allow(clippy::just_underscores_and_digits)] #[inline(always)]
116pub fn scale<T>(sx: T, sy: T) -> Matrix2d<T>
117where
118 T: Float,
119{
120 use vecmath::traits::Zero;
121
122 let _0: T = Zero::zero();
123 [[sx, _0, _0], [_0, sy, _0]]
124}
125
126#[allow(clippy::just_underscores_and_digits)] #[inline(always)]
129pub fn shear<T>(v: Vec2d<T>) -> Matrix2d<T>
130where
131 T: Float,
132{
133 use vecmath::traits::{One, Zero};
134
135 let _0 = Zero::zero();
136 let _1 = One::one();
137 [[_1, v[0], _0], [v[1], _1, _0]]
138}
139
140#[allow(clippy::just_underscores_and_digits)] #[inline(always)]
143pub fn identity<T>() -> Matrix2d<T>
144where
145 T: Float,
146{
147 use vecmath::traits::{One, Zero};
148
149 let _0: T = Zero::zero();
150 let _1: T = One::one();
151 [[_1, _0, _0], [_0, _1, _0]]
152}
153
154#[inline(always)]
156pub fn get_scale<T>(m: Matrix2d<T>) -> Vec2d<T>
157where
158 T: Float,
159{
160 [
161 (m[0][0] * m[0][0] + m[1][0] * m[1][0]).sqrt(),
162 (m[0][1] * m[0][1] + m[1][1] * m[1][1]).sqrt(),
163 ]
164}
165
166#[inline(always)]
169pub fn separation<T>(ray: Ray<T>, v: Vec2d<T>) -> Vec2d<T>
170where
171 T: Float,
172{
173 let (dir_x, dir_y) = (ray[2], ray[3]);
175 let (dx, dy) = (ray[0] - v[0], ray[1] - v[1]);
177 let dot = dir_x * v[0] + dir_y * v[1];
179 [dx - dot * dir_x, dy - dot * dir_y]
185}
186
187#[inline(always)]
192pub fn least_separation_4<T>(
193 sep1: Vec2d<T>,
194 sep2: Vec2d<T>,
195 sep3: Vec2d<T>,
196 sep4: Vec2d<T>,
197) -> Vec2d<T>
198where
199 T: Float,
200{
201 let dot1 = sep1[0] * sep1[0] + sep1[1] * sep1[1];
202 let dot2 = sep2[0] * sep2[0] + sep2[1] * sep2[1];
203 let dot3 = sep3[0] * sep3[0] + sep3[1] * sep3[1];
204 let dot4 = sep4[0] * sep4[0] + sep4[1] * sep4[1];
205 if dot1 < dot2 {
207 if dot3 < dot4 {
208 if dot1 < dot3 {
209 sep1
210 } else {
211 sep3
212 }
213 } else if dot1 < dot4 {
214 sep1
215 } else {
216 sep4
217 }
218 } else if dot3 < dot4 {
219 if dot2 < dot3 {
220 sep2
221 } else {
222 sep3
223 }
224 } else if dot2 < dot4 {
225 sep2
226 } else {
227 sep4
228 }
229}
230
231#[allow(clippy::just_underscores_and_digits)] #[inline(always)]
234pub fn margin_rectangle<T>(rect: Rectangle<T>, m: T) -> Rectangle<T>
235where
236 T: Float,
237{
238 use vecmath::traits::{FromPrimitive, Zero};
239
240 let _0: T = Zero::zero();
241 let _05: T = FromPrimitive::from_f64(0.5);
242 let _2: T = FromPrimitive::from_f64(2.0);
243 let w = rect[2] - _2 * m;
244 let h = rect[3] - _2 * m;
245 let (x, w) = if w < _0 {
246 (rect[0] + _05 * rect[2], _0)
247 } else {
248 (rect[0] + m, w)
249 };
250 let (y, h) = if h < _0 {
251 (rect[1] + _05 * rect[3], _0)
252 } else {
253 (rect[1] + m, h)
254 };
255 [x, y, w, h]
256}
257
258#[inline(always)]
260pub fn relative_rectangle<T>(rect: Rectangle<T>, v: Vec2d<T>) -> Rectangle<T>
261where
262 T: Float,
263{
264 [
265 rect[0] + v[0] * rect[2],
266 rect[1] + v[1] * rect[3],
267 rect[2],
268 rect[3],
269 ]
270}
271
272#[inline(always)]
276pub fn overlap_rectangle<T>(a: Rectangle<T>, b: Rectangle<T>) -> Option<Rectangle<T>>
277where
278 T: Float,
279{
280 #[inline(always)]
281 fn min<T: Float>(a: T, b: T) -> T {
282 if a < b {
283 a
284 } else {
285 b
286 }
287 }
288
289 #[inline(always)]
290 fn max<T: Float>(a: T, b: T) -> T {
291 if a > b {
292 a
293 } else {
294 b
295 }
296 }
297
298 if a[0] < b[0] + b[2] && a[1] < b[1] + b[3] && b[0] < a[0] + a[2] && b[1] < a[1] + a[3] {
299 let x = max(a[0], b[0]);
300 let y = max(a[1], b[1]);
301 let w = min(a[0] + a[2], b[0] + b[2]) - x;
302 let h = min(a[1] + a[3], b[1] + b[3]) - y;
303 Some([x, y, w, h])
304 } else {
305 None
306 }
307}
308
309#[cfg(test)]
310mod test_overlap {
311 use super::overlap_rectangle;
312
313 #[test]
314 fn overlap() {
315 let a = [0.0, 1.0, 100.0, 101.0];
316 let b = [51.0, 52.0, 102.0, 103.0];
317 let c = overlap_rectangle(a, b).unwrap();
318 assert_eq!(c, [51.0, 52.0, 49.0, 50.0]);
319 let d = overlap_rectangle(a, c).unwrap();
320 assert_eq!(d, c);
321 let e = overlap_rectangle(b, c).unwrap();
322 assert_eq!(e, c);
323 }
324
325 #[test]
326 fn edge() {
327 let a = [0.0, 0.0, 100.0, 100.0];
328 let b = [100.0, 0.0, 100.0, 100.0];
329 let c = overlap_rectangle(a, b);
330 assert_eq!(c, None);
331 }
332}
333
334#[inline(always)]
337pub fn relative_source_rectangle<T>(rect: SourceRectangle<T>, x: T, y: T) -> SourceRectangle<T>
338where
339 T: Float,
340{
341 let (rx, ry, rw, rh) = (rect[0], rect[1], rect[2], rect[3]);
342 let (x, y) = (rx + x * rw, ry + y * rh);
343 [x, y, rw, rh]
344}
345
346#[inline(always)]
348pub fn modular_offset<T: Add<Output = T> + Rem<Output = T> + Copy>(n: &T, i: &T, off: &T) -> T {
349 (*i + (*off % *n + *n)) % *n
350}
351
352#[cfg(test)]
353mod test_modular_offset {
354 use super::*;
355
356 #[test]
357 fn test_modular_offset() {
358 assert_eq!(modular_offset(&3.0_f64, &0.0_f64, &-1.0_f64), 2.0_f64);
359 assert_eq!(modular_offset(&3.0_f64, &1.0_f64, &-1.0_f64), 0.0_f64);
360 assert_eq!(modular_offset(&3.0_f64, &2.0_f64, &-1.0_f64), 1.0_f64);
361 assert_eq!(modular_offset(&3.0_f64, &3.0_f64, &-1.0_f64), 2.0_f64);
362
363 assert_eq!(modular_offset(&3.0_f64, &0.0_f64, &1.0_f64), 1.0_f64);
364 assert_eq!(modular_offset(&3.0_f64, &1.0_f64, &1.0_f64), 2.0_f64);
365 assert_eq!(modular_offset(&3.0_f64, &2.0_f64, &1.0_f64), 0.0_f64);
366 assert_eq!(modular_offset(&3.0_f64, &3.0_f64, &1.0_f64), 1.0_f64);
367 }
368}
369
370#[allow(clippy::just_underscores_and_digits)] pub fn area_centroid<T>(polygon: Polygon<'_, T>) -> (Area<T>, Vec2d<T>)
376where
377 T: Float,
378{
379 use vecmath::traits::{FromPrimitive, Zero};
380
381 let _0: T = Zero::zero();
382 let _05: T = FromPrimitive::from_f64(0.5);
383 let _3: T = FromPrimitive::from_f64(3.0);
384 let n = polygon.len();
385 let mut sum = _0;
386 let (mut cx, mut cy) = (_0, _0);
387 for i in 0..n {
388 let qx = polygon[i][0];
389 let qy = polygon[i][1];
390 let p_i = previous(n, i);
391 let px = polygon[p_i][0];
392 let py = polygon[p_i][1];
393 let cross = px * qy - qx * py;
394 cx += (px + qx) * cross;
395 cy += (py + qy) * cross;
396 sum += cross;
397 }
398
399 let area = _05 * sum;
400 let centroid = [cx / (_3 * sum), cy / (_3 * sum)];
402 (area, centroid)
403}
404
405#[inline(always)]
409pub fn area<T>(polygon: Polygon<'_, T>) -> T
410where
411 T: Float,
412{
413 let (res, _) = area_centroid(polygon);
414 res
415}
416
417#[inline(always)]
421pub fn centroid<T>(polygon: Polygon<'_, T>) -> Vec2d<T>
422where
423 T: Float,
424{
425 let (_, res) = area_centroid(polygon);
426 res
427}
428
429#[inline(always)]
435pub fn line_side<T>(line: Line<T>, v: Vec2d<T>) -> T
436where
437 T: Float,
438{
439 let (ax, ay) = (line[0], line[1]);
440 let (bx, by) = (line[2], line[3]);
441 (bx - ax) * (v[1] - ay) - (by - ay) * (v[0] - ax)
442}
443
444#[allow(clippy::just_underscores_and_digits)] pub fn inside_triangle<T>(triangle: Triangle<T>, v: Vec2d<T>) -> bool
451where
452 T: Float,
453{
454 use vecmath::traits::Zero;
455
456 let _0: T = Zero::zero();
457
458 let ax = triangle[0][0];
459 let ay = triangle[0][1];
460 let bx = triangle[1][0];
461 let by = triangle[1][1];
462 let cx = triangle[2][0];
463 let cy = triangle[2][1];
464
465 let ab_side = line_side([ax, ay, bx, by], v);
466 let bc_side = line_side([bx, by, cx, cy], v);
467 let ca_side = line_side([cx, cy, ax, ay], v);
468
469 let ab_positive = ab_side >= _0;
470 let bc_positive = bc_side >= _0;
471 let ca_positive = ca_side >= _0;
472
473 ab_positive == bc_positive && bc_positive == ca_positive
474}
475
476#[allow(clippy::just_underscores_and_digits)] #[inline(always)]
485pub fn triangle_face<T>(triangle: Triangle<T>) -> bool
486where
487 T: Float,
488{
489 use vecmath::traits::Zero;
490
491 let _0 = Zero::zero();
492
493 let ax = triangle[0][0];
494 let ay = triangle[0][1];
495 let bx = triangle[1][0];
496 let by = triangle[1][1];
497 let cx = triangle[2][0];
498 let cy = triangle[2][1];
499
500 let ab_side = line_side([ax, ay, bx, by], [cx, cy]);
501
502 ab_side <= _0
503}
504
505#[cfg(test)]
506mod test_triangle {
507 use super::*;
508
509 #[test]
510 fn test_triangle() {
511 let tri_1 = [[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]];
513 let tri_2 = [[0.0, 0.0], [1.0, 1.0], [1.0, 0.0]];
515 let (x, y) = (0.5, 0.25);
516 assert!(inside_triangle(tri_1, [x, y]));
517 assert!(inside_triangle(tri_2, [x, y]));
518 assert_eq!(triangle_face(tri_1), false);
519 assert!(triangle_face(tri_2));
520 }
521}
522
523#[allow(clippy::just_underscores_and_digits)] #[inline(always)]
526pub fn to_barycentric<T>(triangle: Triangle<T>, pos: Vec2d<T>) -> Vec3d<T>
527where
528 T: Float,
529{
530 use vecmath::traits::One;
531
532 let _1: T = One::one();
533 let x = pos[0];
534 let y = pos[1];
535 let x1 = triangle[0][0];
536 let y1 = triangle[0][1];
537 let x2 = triangle[1][0];
538 let y2 = triangle[1][1];
539 let x3 = triangle[2][0];
540 let y3 = triangle[2][1];
541 let lambda1 = ((y2 - y3) * (x - x3) + (x3 - x2) * (y - y3))
542 / ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3));
543 let lambda2 = ((y3 - y1) * (x - x3) + (x1 - x3) * (y - y3))
544 / ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3));
545 let lambda3 = _1 - lambda1 - lambda2;
546 [lambda1, lambda2, lambda3]
547}
548
549#[inline(always)]
551pub fn from_barycentric<T>(triangle: Triangle<T>, lambda: Vec3d<T>) -> Vec2d<T>
552where
553 T: Float,
554{
555 let x1 = triangle[0][0];
556 let y1 = triangle[0][1];
557 let x2 = triangle[1][0];
558 let y2 = triangle[1][1];
559 let x3 = triangle[2][0];
560 let y3 = triangle[2][1];
561 [
562 lambda[0] * x1 + lambda[1] * x2 + lambda[2] * x3,
563 lambda[0] * y1 + lambda[1] * y2 + lambda[2] * y3,
564 ]
565}
566
567#[cfg(test)]
568mod test_barycentric {
569 use super::*;
570
571 #[test]
572 fn test_barycentric() {
573 let triangle = [[0.0, 0.0], [100.0, 0.0], [0.0, 50.0]];
574 let old_pos = [10.0, 20.0];
575 let b = to_barycentric(triangle, old_pos);
576 let new_pos: Vec2d = from_barycentric(triangle, b);
577 let eps = 0.00001;
578 assert!((new_pos[0] - old_pos[0]).abs() < eps);
579 assert!((new_pos[1] - old_pos[1]).abs() < eps);
580 }
581}
582
583#[inline(always)]
587pub fn hsv(color: Color, h_rad: f32, s: f32, v: f32) -> Color {
588 let vsu = v * s * h_rad.cos();
589 let vsw = v * s * h_rad.sin();
590 [
591 (0.299 * v + 0.701 * vsu + 0.168 * vsw) * color[0]
592 + (0.587 * v - 0.587 * vsu + 0.330 * vsw) * color[1]
593 + (0.114 * v - 0.114 * vsu - 0.497 * vsw) * color[2],
594 (0.299 * v - 0.299 * vsu - 0.328 * vsw) * color[0]
595 + (0.587 * v + 0.413 * vsu + 0.035 * vsw) * color[1]
596 + (0.114 * v - 0.114 * vsu + 0.292 * vsw) * color[2],
597 (0.299 * v - 0.3 * vsu + 1.25 * vsw) * color[0]
598 + (0.587 * v - 0.588 * vsu - 1.05 * vsw) * color[1]
599 + (0.114 * v + 0.886 * vsu - 0.203 * vsw) * color[2],
600 color[3],
601 ]
602}