1use core::ops::{Add, Sub};
7
8#[inline]
15pub fn iround(v: f64) -> i32 {
16 if v < 0.0 {
17 (v - 0.5) as i32
18 } else {
19 (v + 0.5) as i32
20 }
21}
22
23#[inline]
26pub fn uround(v: f64) -> u32 {
27 (v + 0.5) as u32
28}
29
30#[inline]
33pub fn ifloor(v: f64) -> i32 {
34 let i = v as i32;
35 i - (i as f64 > v) as i32
36}
37
38#[inline]
41pub fn ufloor(v: f64) -> u32 {
42 v as u32
43}
44
45#[inline]
48pub fn iceil(v: f64) -> i32 {
49 v.ceil() as i32
50}
51
52#[inline]
55pub fn uceil(v: f64) -> u32 {
56 v.ceil() as u32
57}
58
59#[inline]
66pub fn saturation_iround(limit: i32, v: f64) -> i32 {
67 if v < -(limit as f64) {
68 return -limit;
69 }
70 if v > limit as f64 {
71 return limit;
72 }
73 iround(v)
74}
75
76#[inline]
79pub fn mul_one(a: u32, b: u32, shift: u32) -> u32 {
80 let q = a * b + (1 << (shift - 1));
81 (q + (q >> shift)) >> shift
82}
83
84pub type CoverType = u8;
90
91pub const COVER_SHIFT: u32 = 8;
92pub const COVER_SIZE: u32 = 1 << COVER_SHIFT;
93pub const COVER_MASK: u32 = COVER_SIZE - 1;
94pub const COVER_NONE: CoverType = 0;
95pub const COVER_FULL: CoverType = COVER_MASK as CoverType;
96
97pub const POLY_SUBPIXEL_SHIFT: u32 = 8;
104pub const POLY_SUBPIXEL_SCALE: u32 = 1 << POLY_SUBPIXEL_SHIFT;
105pub const POLY_SUBPIXEL_MASK: u32 = POLY_SUBPIXEL_SCALE - 1;
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
113pub enum FillingRule {
114 NonZero,
115 EvenOdd,
116}
117
118pub const PI: f64 = std::f64::consts::PI;
123
124#[inline]
126pub fn deg2rad(deg: f64) -> f64 {
127 deg * PI / 180.0
128}
129
130#[inline]
132pub fn rad2deg(rad: f64) -> f64 {
133 rad * 180.0 / PI
134}
135
136#[derive(Debug, Clone, Copy, PartialEq)]
143pub struct Rect<T: Copy> {
144 pub x1: T,
145 pub y1: T,
146 pub x2: T,
147 pub y2: T,
148}
149
150impl<T: Copy + PartialOrd> Rect<T> {
151 pub fn new(x1: T, y1: T, x2: T, y2: T) -> Self {
152 Self { x1, y1, x2, y2 }
153 }
154
155 pub fn init(&mut self, x1: T, y1: T, x2: T, y2: T) {
156 self.x1 = x1;
157 self.y1 = y1;
158 self.x2 = x2;
159 self.y2 = y2;
160 }
161
162 pub fn normalize(&mut self) -> &Self {
164 if self.x1 > self.x2 {
165 core::mem::swap(&mut self.x1, &mut self.x2);
166 }
167 if self.y1 > self.y2 {
168 core::mem::swap(&mut self.y1, &mut self.y2);
169 }
170 self
171 }
172
173 pub fn clip(&mut self, r: &Self) -> bool {
176 if self.x2 > r.x2 {
177 self.x2 = r.x2;
178 }
179 if self.y2 > r.y2 {
180 self.y2 = r.y2;
181 }
182 if self.x1 < r.x1 {
183 self.x1 = r.x1;
184 }
185 if self.y1 < r.y1 {
186 self.y1 = r.y1;
187 }
188 self.x1 <= self.x2 && self.y1 <= self.y2
189 }
190
191 pub fn is_valid(&self) -> bool {
193 self.x1 <= self.x2 && self.y1 <= self.y2
194 }
195
196 pub fn hit_test(&self, x: T, y: T) -> bool {
198 x >= self.x1 && x <= self.x2 && y >= self.y1 && y <= self.y2
199 }
200
201 pub fn overlaps(&self, r: &Self) -> bool {
203 !(r.x1 > self.x2 || r.x2 < self.x1 || r.y1 > self.y2 || r.y2 < self.y1)
204 }
205}
206
207pub fn intersect_rectangles<T: Copy + PartialOrd>(r1: &Rect<T>, r2: &Rect<T>) -> Rect<T> {
209 let mut r = *r1;
210 if r.x2 > r2.x2 {
212 r.x2 = r2.x2;
213 }
214 if r.y2 > r2.y2 {
215 r.y2 = r2.y2;
216 }
217 if r.x1 < r2.x1 {
218 r.x1 = r2.x1;
219 }
220 if r.y1 < r2.y1 {
221 r.y1 = r2.y1;
222 }
223 r
224}
225
226pub fn unite_rectangles<T: Copy + PartialOrd>(r1: &Rect<T>, r2: &Rect<T>) -> Rect<T> {
228 let mut r = *r1;
229 if r.x2 < r2.x2 {
230 r.x2 = r2.x2;
231 }
232 if r.y2 < r2.y2 {
233 r.y2 = r2.y2;
234 }
235 if r.x1 > r2.x1 {
236 r.x1 = r2.x1;
237 }
238 if r.y1 > r2.y1 {
239 r.y1 = r2.y1;
240 }
241 r
242}
243
244pub type RectI = Rect<i32>;
246pub type RectF = Rect<f32>;
248pub type RectD = Rect<f64>;
250
251pub const PATH_CMD_STOP: u32 = 0;
256pub const PATH_CMD_MOVE_TO: u32 = 1;
257pub const PATH_CMD_LINE_TO: u32 = 2;
258pub const PATH_CMD_CURVE3: u32 = 3;
259pub const PATH_CMD_CURVE4: u32 = 4;
260pub const PATH_CMD_CURVE_N: u32 = 5;
261pub const PATH_CMD_CATROM: u32 = 6;
262pub const PATH_CMD_UBSPLINE: u32 = 7;
263pub const PATH_CMD_END_POLY: u32 = 0x0F;
264pub const PATH_CMD_MASK: u32 = 0x0F;
265
266pub const PATH_FLAGS_NONE: u32 = 0;
271pub const PATH_FLAGS_CCW: u32 = 0x10;
272pub const PATH_FLAGS_CW: u32 = 0x20;
273pub const PATH_FLAGS_CLOSE: u32 = 0x40;
274pub const PATH_FLAGS_MASK: u32 = 0xF0;
275
276#[inline]
282pub fn is_vertex(c: u32) -> bool {
283 (PATH_CMD_MOVE_TO..PATH_CMD_END_POLY).contains(&c)
284}
285
286#[inline]
288pub fn is_drawing(c: u32) -> bool {
289 (PATH_CMD_LINE_TO..PATH_CMD_END_POLY).contains(&c)
290}
291
292#[inline]
294pub fn is_stop(c: u32) -> bool {
295 c == PATH_CMD_STOP
296}
297
298#[inline]
300pub fn is_move_to(c: u32) -> bool {
301 c == PATH_CMD_MOVE_TO
302}
303
304#[inline]
306pub fn is_line_to(c: u32) -> bool {
307 c == PATH_CMD_LINE_TO
308}
309
310#[inline]
312pub fn is_curve(c: u32) -> bool {
313 c == PATH_CMD_CURVE3 || c == PATH_CMD_CURVE4
314}
315
316#[inline]
318pub fn is_curve3(c: u32) -> bool {
319 c == PATH_CMD_CURVE3
320}
321
322#[inline]
324pub fn is_curve4(c: u32) -> bool {
325 c == PATH_CMD_CURVE4
326}
327
328#[inline]
330pub fn is_end_poly(c: u32) -> bool {
331 (c & PATH_CMD_MASK) == PATH_CMD_END_POLY
332}
333
334#[inline]
336pub fn is_close(c: u32) -> bool {
337 (c & !(PATH_FLAGS_CW | PATH_FLAGS_CCW)) == (PATH_CMD_END_POLY | PATH_FLAGS_CLOSE)
338}
339
340#[inline]
342pub fn is_next_poly(c: u32) -> bool {
343 is_stop(c) || is_move_to(c) || is_end_poly(c)
344}
345
346#[inline]
348pub fn is_cw(c: u32) -> bool {
349 (c & PATH_FLAGS_CW) != 0
350}
351
352#[inline]
354pub fn is_ccw(c: u32) -> bool {
355 (c & PATH_FLAGS_CCW) != 0
356}
357
358#[inline]
360pub fn is_oriented(c: u32) -> bool {
361 (c & (PATH_FLAGS_CW | PATH_FLAGS_CCW)) != 0
362}
363
364#[inline]
366pub fn is_closed(c: u32) -> bool {
367 (c & PATH_FLAGS_CLOSE) != 0
368}
369
370#[inline]
372pub fn get_close_flag(c: u32) -> u32 {
373 c & PATH_FLAGS_CLOSE
374}
375
376#[inline]
378pub fn clear_orientation(c: u32) -> u32 {
379 c & !(PATH_FLAGS_CW | PATH_FLAGS_CCW)
380}
381
382#[inline]
384pub fn get_orientation(c: u32) -> u32 {
385 c & (PATH_FLAGS_CW | PATH_FLAGS_CCW)
386}
387
388#[inline]
390pub fn set_orientation(c: u32, o: u32) -> u32 {
391 clear_orientation(c) | o
392}
393
394#[derive(Debug, Clone, Copy, PartialEq, Default)]
401pub struct PointBase<T: Copy> {
402 pub x: T,
403 pub y: T,
404}
405
406impl<T: Copy> PointBase<T> {
407 pub fn new(x: T, y: T) -> Self {
408 Self { x, y }
409 }
410}
411
412pub type PointI = PointBase<i32>;
413pub type PointF = PointBase<f32>;
414pub type PointD = PointBase<f64>;
415
416#[derive(Debug, Clone, Copy, PartialEq)]
423pub struct VertexBase<T: Copy> {
424 pub x: T,
425 pub y: T,
426 pub cmd: u32,
427}
428
429impl<T: Copy> VertexBase<T> {
430 pub fn new(x: T, y: T, cmd: u32) -> Self {
431 Self { x, y, cmd }
432 }
433}
434
435pub type VertexI = VertexBase<i32>;
436pub type VertexF = VertexBase<f32>;
437pub type VertexD = VertexBase<f64>;
438
439#[derive(Debug, Clone, Copy)]
446pub struct RowInfo<T> {
447 pub x1: i32,
448 pub x2: i32,
449 pub ptr: *mut T,
450}
451
452impl<T> RowInfo<T> {
453 pub fn new(x1: i32, x2: i32, ptr: *mut T) -> Self {
454 Self { x1, x2, ptr }
455 }
456}
457
458#[derive(Debug, Clone, Copy)]
460pub struct ConstRowInfo<T> {
461 pub x1: i32,
462 pub x2: i32,
463 pub ptr: *const T,
464}
465
466impl<T> ConstRowInfo<T> {
467 pub fn new(x1: i32, x2: i32, ptr: *const T) -> Self {
468 Self { x1, x2, ptr }
469 }
470}
471
472pub fn is_equal_eps<T>(v1: T, v2: T, epsilon: T) -> bool
480where
481 T: Copy + PartialOrd + Sub<Output = T> + Add<Output = T> + Into<f64> + From<f64>,
482{
483 let v1_f: f64 = v1.into();
484 let v2_f: f64 = v2.into();
485 let eps_f: f64 = epsilon.into();
486
487 let neg1 = v1_f < 0.0;
488 let neg2 = v2_f < 0.0;
489
490 if neg1 != neg2 {
491 return v1_f.abs() < eps_f && v2_f.abs() < eps_f;
492 }
493
494 let (_, exp1) = frexp(v1_f);
495 let (_, exp2) = frexp(v2_f);
496 let min_exp = exp1.min(exp2);
497
498 let scaled1 = ldexp(v1_f, -min_exp);
499 let scaled2 = ldexp(v2_f, -min_exp);
500
501 (scaled1 - scaled2).abs() < eps_f
502}
503
504#[inline]
507fn frexp(x: f64) -> (f64, i32) {
508 if x == 0.0 {
509 return (0.0, 0);
510 }
511 let bits = x.to_bits();
512 let exp = ((bits >> 52) & 0x7FF) as i32 - 1022;
513 let mantissa = f64::from_bits((bits & 0x800F_FFFF_FFFF_FFFF) | 0x3FE0_0000_0000_0000);
514 (mantissa, exp)
515}
516
517#[inline]
519fn ldexp(x: f64, exp: i32) -> f64 {
520 x * (2.0_f64).powi(exp)
521}
522
523pub trait VertexSource {
533 fn rewind(&mut self, path_id: u32);
536
537 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32;
540}
541
542impl<T: VertexSource> VertexSource for &mut T {
545 fn rewind(&mut self, path_id: u32) {
546 (*self).rewind(path_id);
547 }
548
549 fn vertex(&mut self, x: &mut f64, y: &mut f64) -> u32 {
550 (*self).vertex(x, y)
551 }
552}
553
554#[cfg(test)]
559mod tests {
560 use super::*;
561
562 #[test]
563 fn test_iround() {
564 assert_eq!(iround(0.5), 1);
565 assert_eq!(iround(0.49), 0);
566 assert_eq!(iround(-0.5), -1);
567 assert_eq!(iround(-0.49), 0);
568 assert_eq!(iround(1.5), 2);
569 assert_eq!(iround(-1.5), -2);
570 assert_eq!(iround(0.0), 0);
571 }
572
573 #[test]
574 fn test_uround() {
575 assert_eq!(uround(0.5), 1);
576 assert_eq!(uround(0.49), 0);
577 assert_eq!(uround(1.5), 2);
578 assert_eq!(uround(0.0), 0);
579 }
580
581 #[test]
582 fn test_ifloor() {
583 assert_eq!(ifloor(1.7), 1);
584 assert_eq!(ifloor(1.0), 1);
585 assert_eq!(ifloor(-1.7), -2);
586 assert_eq!(ifloor(-1.0), -1);
587 assert_eq!(ifloor(0.0), 0);
588 }
589
590 #[test]
591 fn test_ufloor() {
592 assert_eq!(ufloor(1.7), 1);
593 assert_eq!(ufloor(1.0), 1);
594 assert_eq!(ufloor(0.0), 0);
595 }
596
597 #[test]
598 fn test_iceil() {
599 assert_eq!(iceil(1.1), 2);
600 assert_eq!(iceil(1.0), 1);
601 assert_eq!(iceil(-1.1), -1);
602 assert_eq!(iceil(0.0), 0);
603 }
604
605 #[test]
606 fn test_uceil() {
607 assert_eq!(uceil(1.1), 2);
608 assert_eq!(uceil(1.0), 1);
609 assert_eq!(uceil(0.0), 0);
610 }
611
612 #[test]
613 fn test_saturation_iround() {
614 assert_eq!(saturation_iround(128, 200.0), 128);
615 assert_eq!(saturation_iround(128, -200.0), -128);
616 assert_eq!(saturation_iround(128, 50.7), 51);
617 }
618
619 #[test]
620 fn test_mul_one_shift8() {
621 assert_eq!(mul_one(255, 255, 8), 255);
624 assert_eq!(mul_one(128, 255, 8), 128);
626 assert_eq!(mul_one(0, 255, 8), 0);
628 }
629
630 #[test]
631 fn test_cover_constants() {
632 assert_eq!(COVER_SHIFT, 8);
633 assert_eq!(COVER_SIZE, 256);
634 assert_eq!(COVER_MASK, 255);
635 assert_eq!(COVER_NONE, 0);
636 assert_eq!(COVER_FULL, 255);
637 }
638
639 #[test]
640 fn test_poly_subpixel_constants() {
641 assert_eq!(POLY_SUBPIXEL_SHIFT, 8);
642 assert_eq!(POLY_SUBPIXEL_SCALE, 256);
643 assert_eq!(POLY_SUBPIXEL_MASK, 255);
644 }
645
646 #[test]
647 fn test_deg2rad_rad2deg() {
648 let epsilon = 1e-10;
649 assert!((deg2rad(180.0) - PI).abs() < epsilon);
650 assert!((rad2deg(PI) - 180.0).abs() < epsilon);
651 assert!((deg2rad(90.0) - PI / 2.0).abs() < epsilon);
652 assert!((deg2rad(0.0)).abs() < epsilon);
653 }
654
655 #[test]
656 fn test_rect_new_and_is_valid() {
657 let r = RectI::new(10, 20, 30, 40);
658 assert!(r.is_valid());
659 assert_eq!(r.x1, 10);
660 assert_eq!(r.y1, 20);
661 assert_eq!(r.x2, 30);
662 assert_eq!(r.y2, 40);
663
664 let r_invalid = RectI::new(30, 40, 10, 20);
665 assert!(!r_invalid.is_valid());
666 }
667
668 #[test]
669 fn test_rect_normalize() {
670 let mut r = RectI::new(30, 40, 10, 20);
671 r.normalize();
672 assert_eq!(r.x1, 10);
673 assert_eq!(r.y1, 20);
674 assert_eq!(r.x2, 30);
675 assert_eq!(r.y2, 40);
676 }
677
678 #[test]
679 fn test_rect_clip() {
680 let mut r = RectI::new(10, 20, 100, 200);
681 let clip = RectI::new(50, 50, 80, 80);
682 let valid = r.clip(&clip);
683 assert!(valid);
684 assert_eq!(r.x1, 50);
685 assert_eq!(r.y1, 50);
686 assert_eq!(r.x2, 80);
687 assert_eq!(r.y2, 80);
688 }
689
690 #[test]
691 fn test_rect_hit_test() {
692 let r = RectI::new(10, 20, 30, 40);
693 assert!(r.hit_test(15, 25));
694 assert!(r.hit_test(10, 20));
695 assert!(r.hit_test(30, 40));
696 assert!(!r.hit_test(5, 25));
697 assert!(!r.hit_test(15, 45));
698 }
699
700 #[test]
701 fn test_rect_overlaps() {
702 let r1 = RectI::new(10, 20, 30, 40);
703 let r2 = RectI::new(25, 35, 50, 60);
704 assert!(r1.overlaps(&r2));
705 assert!(r2.overlaps(&r1));
706
707 let r3 = RectI::new(31, 41, 50, 60);
708 assert!(!r1.overlaps(&r3));
709 }
710
711 #[test]
712 fn test_intersect_rectangles() {
713 let r1 = RectI::new(10, 20, 100, 200);
714 let r2 = RectI::new(50, 50, 80, 80);
715 let r = intersect_rectangles(&r1, &r2);
716 assert_eq!(r.x1, 50);
717 assert_eq!(r.y1, 50);
718 assert_eq!(r.x2, 80);
719 assert_eq!(r.y2, 80);
720 }
721
722 #[test]
723 fn test_unite_rectangles() {
724 let r1 = RectI::new(10, 20, 30, 40);
725 let r2 = RectI::new(50, 60, 70, 80);
726 let r = unite_rectangles(&r1, &r2);
727 assert_eq!(r.x1, 10);
728 assert_eq!(r.y1, 20);
729 assert_eq!(r.x2, 70);
730 assert_eq!(r.y2, 80);
731 }
732
733 #[test]
734 fn test_path_command_classification() {
735 assert!(is_stop(PATH_CMD_STOP));
736 assert!(!is_stop(PATH_CMD_MOVE_TO));
737
738 assert!(is_move_to(PATH_CMD_MOVE_TO));
739 assert!(is_line_to(PATH_CMD_LINE_TO));
740
741 assert!(is_vertex(PATH_CMD_MOVE_TO));
742 assert!(is_vertex(PATH_CMD_LINE_TO));
743 assert!(is_vertex(PATH_CMD_CURVE3));
744 assert!(is_vertex(PATH_CMD_CURVE4));
745 assert!(!is_vertex(PATH_CMD_STOP));
746 assert!(!is_vertex(PATH_CMD_END_POLY));
747
748 assert!(is_drawing(PATH_CMD_LINE_TO));
749 assert!(is_drawing(PATH_CMD_CURVE3));
750 assert!(!is_drawing(PATH_CMD_MOVE_TO));
751 assert!(!is_drawing(PATH_CMD_END_POLY));
752
753 assert!(is_curve(PATH_CMD_CURVE3));
754 assert!(is_curve(PATH_CMD_CURVE4));
755 assert!(!is_curve(PATH_CMD_LINE_TO));
756
757 assert!(is_curve3(PATH_CMD_CURVE3));
758 assert!(!is_curve3(PATH_CMD_CURVE4));
759
760 assert!(is_curve4(PATH_CMD_CURVE4));
761 assert!(!is_curve4(PATH_CMD_CURVE3));
762 }
763
764 #[test]
765 fn test_path_end_poly_and_close() {
766 assert!(is_end_poly(PATH_CMD_END_POLY));
767 assert!(is_end_poly(PATH_CMD_END_POLY | PATH_FLAGS_CLOSE));
768 assert!(is_end_poly(PATH_CMD_END_POLY | PATH_FLAGS_CW));
769 assert!(!is_end_poly(PATH_CMD_LINE_TO));
770
771 assert!(is_close(PATH_CMD_END_POLY | PATH_FLAGS_CLOSE));
772 assert!(!is_close(PATH_CMD_END_POLY));
773 assert!(is_close(
775 PATH_CMD_END_POLY | PATH_FLAGS_CLOSE | PATH_FLAGS_CW
776 ));
777 }
778
779 #[test]
780 fn test_path_flags() {
781 let cmd = PATH_CMD_END_POLY | PATH_FLAGS_CLOSE | PATH_FLAGS_CW;
782 assert!(is_cw(cmd));
783 assert!(!is_ccw(cmd));
784 assert!(is_oriented(cmd));
785 assert!(is_closed(cmd));
786
787 assert_eq!(get_close_flag(cmd), PATH_FLAGS_CLOSE);
788 assert_eq!(get_orientation(cmd), PATH_FLAGS_CW);
789 assert_eq!(clear_orientation(cmd), PATH_CMD_END_POLY | PATH_FLAGS_CLOSE);
790 assert_eq!(
791 set_orientation(cmd, PATH_FLAGS_CCW),
792 PATH_CMD_END_POLY | PATH_FLAGS_CLOSE | PATH_FLAGS_CCW
793 );
794 }
795
796 #[test]
797 fn test_is_next_poly() {
798 assert!(is_next_poly(PATH_CMD_STOP));
799 assert!(is_next_poly(PATH_CMD_MOVE_TO));
800 assert!(is_next_poly(PATH_CMD_END_POLY));
801 assert!(!is_next_poly(PATH_CMD_LINE_TO));
802 assert!(!is_next_poly(PATH_CMD_CURVE3));
803 }
804
805 #[test]
806 fn test_point() {
807 let p = PointD::new(1.5, 2.5);
808 assert_eq!(p.x, 1.5);
809 assert_eq!(p.y, 2.5);
810 }
811
812 #[test]
813 fn test_vertex() {
814 let v = VertexD::new(1.0, 2.0, PATH_CMD_LINE_TO);
815 assert_eq!(v.x, 1.0);
816 assert_eq!(v.y, 2.0);
817 assert_eq!(v.cmd, PATH_CMD_LINE_TO);
818 }
819
820 #[test]
821 fn test_is_equal_eps() {
822 assert!(is_equal_eps(1.0, 1.0, 1e-10));
823 assert!(is_equal_eps(1.0, 1.0 + 1e-11, 1e-10));
824 assert!(!is_equal_eps(1.0, 2.0, 1e-10));
825 assert!(is_equal_eps(0.0, 0.0, 1e-10));
826 assert!(is_equal_eps(1e-12, -1e-12, 1e-10));
828 assert!(!is_equal_eps(0.1, -0.1, 1e-10));
830 }
831}