1use num_traits::{Float, Num, Zero};
7use std::fmt::{Debug, Display};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
12#[repr(C)]
13pub enum FillRule {
14 #[default]
16 EvenOdd,
17 NonZero,
19 Positive,
21 Negative,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28#[repr(C)]
29pub enum Location {
30 Left,
31 Top,
32 Right,
33 Bottom,
34 Inside,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct Clipper2Exception {
41 description: String,
42}
43
44impl Clipper2Exception {
45 pub fn new(description: &str) -> Self {
46 Self {
47 description: description.to_string(),
48 }
49 }
50
51 pub fn description(&self) -> &str {
52 &self.description
53 }
54}
55
56impl Display for Clipper2Exception {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 write!(f, "Clipper2Exception: {}", self.description)
59 }
60}
61
62impl std::error::Error for Clipper2Exception {}
63
64pub fn do_error(error_code: i32) -> Result<(), Clipper2Exception> {
67 use errors::*;
68
69 match error_code {
70 PRECISION_ERROR_I => Err(Clipper2Exception::new(PRECISION_ERROR)),
71 SCALE_ERROR_I => Err(Clipper2Exception::new(SCALE_ERROR)),
72 NON_PAIR_ERROR_I => Err(Clipper2Exception::new(NON_PAIR_ERROR)),
73 UNDEFINED_ERROR_I => Err(Clipper2Exception::new(UNDEFINED_ERROR)),
74 RANGE_ERROR_I => Err(Clipper2Exception::new(RANGE_ERROR)),
75 _ => Err(Clipper2Exception::new("Unknown error")),
76 }
77}
78
79pub mod constants {
82 pub const PI: f64 = std::f64::consts::PI;
84
85 pub const CLIPPER2_MAX_DEC_PRECISION: i32 = 8;
87
88 pub const MAX_COORD: i64 = i64::MAX >> 2;
90 pub const MIN_COORD: i64 = -MAX_COORD;
92 pub const INVALID: i64 = i64::MAX;
94 pub const MAX_COORD_D: f64 = MAX_COORD as f64;
96 pub const MIN_COORD_D: f64 = MIN_COORD as f64;
98 pub const MAX_DBL: f64 = f64::MAX;
100}
101
102pub mod errors {
104 pub const PRECISION_ERROR: &str = "Precision exceeds the permitted range";
106 pub const RANGE_ERROR: &str = "Values exceed permitted range";
108 pub const SCALE_ERROR: &str = "Invalid scale (either 0 or too large)";
110 pub const NON_PAIR_ERROR: &str = "There must be 2 values for each coordinate";
112 pub const UNDEFINED_ERROR: &str = "There is an undefined error in Clipper2";
114
115 pub const PRECISION_ERROR_I: i32 = 1;
117 pub const SCALE_ERROR_I: i32 = 2;
119 pub const NON_PAIR_ERROR_I: i32 = 4;
121 pub const UNDEFINED_ERROR_I: i32 = 32;
123 pub const RANGE_ERROR_I: i32 = 64;
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Default)]
130#[repr(C)]
131pub struct Point<T> {
132 pub x: T,
133 pub y: T,
134}
135
136impl<T> Point<T>
137where
138 T: Num + Copy,
139{
140 pub fn new(x: T, y: T) -> Self {
142 Self { x, y }
143 }
144
145 pub fn zero() -> Self
147 where
148 T: Zero,
149 {
150 Self {
151 x: T::zero(),
152 y: T::zero(),
153 }
154 }
155}
156
157impl<T> Point<T>
158where
159 T: Num + Copy,
160{
161 pub fn add_point(self, other: Self) -> Self {
163 Self {
164 x: self.x + other.x,
165 y: self.y + other.y,
166 }
167 }
168
169 pub fn sub_point(self, other: Self) -> Self {
171 Self {
172 x: self.x - other.x,
173 y: self.y - other.y,
174 }
175 }
176
177 pub fn negate(self) -> Self {
179 Self {
180 x: T::zero() - self.x,
181 y: T::zero() - self.y,
182 }
183 }
184}
185
186impl<T> Point<T>
187where
188 T: Num + Copy + PartialOrd,
189{
190 pub fn scale<F>(self, scale: F) -> Point<F>
192 where
193 F: Float,
194 T: Into<F>,
195 {
196 Point {
197 x: self.x.into() * scale,
198 y: self.y.into() * scale,
199 }
200 }
201}
202
203impl<T> std::ops::Add for Point<T>
205where
206 T: Num + Copy,
207{
208 type Output = Self;
209
210 fn add(self, rhs: Self) -> Self::Output {
211 self.add_point(rhs)
212 }
213}
214
215impl<T> std::ops::Sub for Point<T>
216where
217 T: Num + Copy,
218{
219 type Output = Self;
220
221 fn sub(self, rhs: Self) -> Self::Output {
222 self.sub_point(rhs)
223 }
224}
225
226impl<T> std::ops::Neg for Point<T>
227where
228 T: Num + Copy,
229{
230 type Output = Self;
231
232 fn neg(self) -> Self::Output {
233 self.negate()
234 }
235}
236
237#[derive(Debug, Clone, Copy, Default)]
240#[repr(C)]
241pub struct Rect<T> {
242 pub left: T,
243 pub top: T,
244 pub right: T,
245 pub bottom: T,
246}
247
248impl<T> Rect<T>
249where
250 T: Num + Copy + PartialOrd,
251{
252 pub fn new(left: T, top: T, right: T, bottom: T) -> Self {
254 Self {
255 left,
256 top,
257 right,
258 bottom,
259 }
260 }
261
262 pub fn new_with_validity(is_valid: bool) -> Self
265 where
266 T: num_traits::Bounded,
267 {
268 if is_valid {
269 Self {
270 left: T::zero(),
271 top: T::zero(),
272 right: T::zero(),
273 bottom: T::zero(),
274 }
275 } else {
276 Self {
277 left: T::max_value(),
278 top: T::max_value(),
279 right: T::min_value(),
280 bottom: T::min_value(),
281 }
282 }
283 }
284
285 pub fn invalid() -> Self
288 where
289 T: num_traits::Bounded,
290 {
291 Self {
292 left: T::max_value(),
293 top: T::max_value(),
294 right: T::min_value(),
295 bottom: T::min_value(),
296 }
297 }
298
299 pub fn mid_point(&self) -> Point<T> {
302 Point {
303 x: (self.left + self.right) / (T::one() + T::one()),
304 y: (self.top + self.bottom) / (T::one() + T::one()),
305 }
306 }
307
308 pub fn as_path(&self) -> Path<T> {
311 vec![
312 Point::new(self.left, self.top),
313 Point::new(self.right, self.top),
314 Point::new(self.right, self.bottom),
315 Point::new(self.left, self.bottom),
316 ]
317 }
318
319 pub fn contains_point(&self, pt: &Point<T>) -> bool {
322 pt.x > self.left && pt.x < self.right && pt.y > self.top && pt.y < self.bottom
323 }
324
325 pub fn contains_rect(&self, rec: &Rect<T>) -> bool {
328 rec.left >= self.left
329 && rec.right <= self.right
330 && rec.top >= self.top
331 && rec.bottom <= self.bottom
332 }
333
334 pub fn intersects(&self, rec: &Rect<T>) -> bool {
337 let max_left = if self.left > rec.left {
338 self.left
339 } else {
340 rec.left
341 };
342 let min_right = if self.right < rec.right {
343 self.right
344 } else {
345 rec.right
346 };
347 let max_top = if self.top > rec.top {
348 self.top
349 } else {
350 rec.top
351 };
352 let min_bottom = if self.bottom < rec.bottom {
353 self.bottom
354 } else {
355 rec.bottom
356 };
357
358 max_left <= min_right && max_top <= min_bottom
359 }
360
361 pub fn is_valid(&self) -> bool
364 where
365 T: num_traits::Bounded + PartialEq,
366 {
367 self.left != T::max_value()
368 }
369
370 pub fn width(&self) -> T {
372 self.right - self.left
373 }
374
375 pub fn height(&self) -> T {
377 self.bottom - self.top
378 }
379
380 pub fn set_width(&mut self, width: T) {
382 self.right = self.left + width;
383 }
384
385 pub fn set_height(&mut self, height: T) {
387 self.bottom = self.top + height;
388 }
389
390 pub fn is_empty(&self) -> bool {
392 self.left >= self.right || self.top >= self.bottom
393 }
394}
395
396impl<T> Rect<T>
397where
398 T: Float + Copy,
399{
400 pub fn scale(&mut self, scale: T) {
402 self.left = self.left * scale;
403 self.top = self.top * scale;
404 self.right = self.right * scale;
405 self.bottom = self.bottom * scale;
406 }
407}
408
409impl<T> PartialEq for Rect<T>
412where
413 T: PartialEq,
414{
415 fn eq(&self, other: &Self) -> bool {
416 self.left == other.left
417 && self.right == other.right
418 && self.top == other.top
419 && self.bottom == other.bottom
420 }
421}
422
423impl<T> std::ops::AddAssign for Rect<T>
426where
427 T: Num + Copy + PartialOrd,
428{
429 fn add_assign(&mut self, other: Self) {
430 self.left = if self.left < other.left {
431 self.left
432 } else {
433 other.left
434 };
435 self.top = if self.top < other.top {
436 self.top
437 } else {
438 other.top
439 };
440 self.right = if self.right > other.right {
441 self.right
442 } else {
443 other.right
444 };
445 self.bottom = if self.bottom > other.bottom {
446 self.bottom
447 } else {
448 other.bottom
449 };
450 }
451}
452
453impl<T> std::ops::Add for Rect<T>
456where
457 T: Num + Copy + PartialOrd,
458{
459 type Output = Self;
460
461 fn add(self, other: Self) -> Self {
462 let mut result = self;
463 result += other;
464 result
465 }
466}
467
468pub type Point64 = Point<i64>;
470pub type PointD = Point<f64>;
471pub type Rect64 = Rect<i64>;
472pub type RectD = Rect<f64>;
473
474pub type Path<T> = Vec<Point<T>>;
476pub type Path64 = Path<i64>;
477pub type PathD = Path<f64>;
478
479pub type Paths<T> = Vec<Path<T>>;
481pub type Paths64 = Paths<i64>;
482pub type PathsD = Paths<f64>;
483
484pub const INVALID_POINT64: Point64 = Point64 {
486 x: i64::MAX,
487 y: i64::MAX,
488};
489
490pub const INVALID_POINTD: PointD = PointD {
491 x: f64::MAX,
492 y: f64::MAX,
493};
494
495#[inline]
498pub fn mid_point<T>(p1: Point<T>, p2: Point<T>) -> Point<T>
499where
500 T: Num + Copy,
501{
502 Point {
503 x: (p1.x + p2.x) / (T::one() + T::one()),
504 y: (p1.y + p2.y) / (T::one() + T::one()),
505 }
506}
507
508pub trait ToF64 {
510 fn to_f64(self) -> f64;
511}
512
513impl ToF64 for i64 {
514 fn to_f64(self) -> f64 {
515 self as f64
516 }
517}
518
519impl ToF64 for i32 {
520 fn to_f64(self) -> f64 {
521 self as f64
522 }
523}
524
525impl ToF64 for f64 {
526 fn to_f64(self) -> f64 {
527 self
528 }
529}
530
531impl ToF64 for f32 {
532 fn to_f64(self) -> f64 {
533 self as f64
534 }
535}
536
537#[inline]
540pub fn cross_product_three_points<T>(pt1: Point<T>, pt2: Point<T>, pt3: Point<T>) -> f64
541where
542 T: Copy + ToF64,
543{
544 let pt1_x = pt1.x.to_f64();
545 let pt1_y = pt1.y.to_f64();
546 let pt2_x = pt2.x.to_f64();
547 let pt2_y = pt2.y.to_f64();
548 let pt3_x = pt3.x.to_f64();
549 let pt3_y = pt3.y.to_f64();
550
551 (pt2_x - pt1_x) * (pt3_y - pt2_y) - (pt2_y - pt1_y) * (pt3_x - pt2_x)
552}
553
554#[inline]
557pub fn cross_product_two_vectors<T>(vec1: Point<T>, vec2: Point<T>) -> f64
558where
559 T: Copy + ToF64,
560{
561 let vec1_x = vec1.x.to_f64();
562 let vec1_y = vec1.y.to_f64();
563 let vec2_x = vec2.x.to_f64();
564 let vec2_y = vec2.y.to_f64();
565
566 vec1_y * vec2_x - vec2_y * vec1_x
567}
568
569#[inline]
572pub fn dot_product_three_points<T>(pt1: Point<T>, pt2: Point<T>, pt3: Point<T>) -> f64
573where
574 T: Copy + ToF64,
575{
576 let pt1_x = pt1.x.to_f64();
577 let pt1_y = pt1.y.to_f64();
578 let pt2_x = pt2.x.to_f64();
579 let pt2_y = pt2.y.to_f64();
580 let pt3_x = pt3.x.to_f64();
581 let pt3_y = pt3.y.to_f64();
582
583 (pt2_x - pt1_x) * (pt3_x - pt2_x) + (pt2_y - pt1_y) * (pt3_y - pt2_y)
584}
585
586#[inline]
589pub fn dot_product_two_vectors<T>(vec1: Point<T>, vec2: Point<T>) -> f64
590where
591 T: Copy + ToF64,
592{
593 let vec1_x = vec1.x.to_f64();
594 let vec1_y = vec1.y.to_f64();
595 let vec2_x = vec2.x.to_f64();
596 let vec2_y = vec2.y.to_f64();
597
598 vec1_x * vec2_x + vec1_y * vec2_y
599}
600
601#[inline]
604pub fn tri_sign(x: i64) -> i32 {
605 if x > 0 {
606 1
607 } else if x < 0 {
608 -1
609 } else {
610 0
611 }
612}
613
614#[derive(Debug, Clone, Copy, PartialEq)]
617pub struct UInt128Struct {
618 pub lo: u64,
619 pub hi: u64,
620}
621
622#[inline]
625pub fn multiply_u64(a: u64, b: u64) -> UInt128Struct {
626 let lo = |x: u64| -> u64 { x & 0xFFFFFFFF };
628 let hi = |x: u64| -> u64 { x >> 32 };
629
630 let x1 = lo(a) * lo(b);
631 let x2 = hi(a) * lo(b) + hi(x1);
632 let x3 = lo(a) * hi(b) + lo(x2);
633 let lobits = lo(x3) << 32 | lo(x1);
634 let hibits = hi(a) * hi(b) + hi(x2) + hi(x3);
635
636 UInt128Struct {
637 lo: lobits,
638 hi: hibits,
639 }
640}
641
642#[inline]
645pub fn products_are_equal(a: i64, b: i64, c: i64, d: i64) -> bool {
646 #[cfg(target_pointer_width = "64")]
648 {
649 let ab = a as i128 * b as i128;
650 let cd = c as i128 * d as i128;
651 ab == cd
652 }
653
654 #[cfg(not(target_pointer_width = "64"))]
656 {
657 let abs_a = a.unsigned_abs();
659 let abs_b = b.unsigned_abs();
660 let abs_c = c.unsigned_abs();
661 let abs_d = d.unsigned_abs();
662
663 let ab = multiply_u64(abs_a, abs_b);
664 let cd = multiply_u64(abs_c, abs_d);
665
666 let sign_ab = tri_sign(a) * tri_sign(b);
668 let sign_cd = tri_sign(c) * tri_sign(d);
669
670 ab == cd && sign_ab == sign_cd
671 }
672}
673
674#[inline]
677pub fn strip_duplicates_path<T>(path: &mut Path<T>, is_closed_path: bool)
678where
679 T: PartialEq + Clone,
680{
681 path.dedup();
683
684 if is_closed_path {
686 while path.len() > 1 && path.last() == path.first() {
687 path.pop();
688 }
689 }
690}
691
692#[inline]
695pub fn strip_duplicates_paths<T>(paths: &mut Paths<T>, is_closed_path: bool)
696where
697 T: PartialEq + Clone,
698{
699 for path in paths.iter_mut() {
700 strip_duplicates_path(path, is_closed_path);
701 }
702}
703
704#[inline]
707pub fn check_precision_range(precision: &mut i32, error_code: &mut i32) {
708 use constants::CLIPPER2_MAX_DEC_PRECISION;
709 use errors::PRECISION_ERROR_I;
710
711 if *precision >= -CLIPPER2_MAX_DEC_PRECISION && *precision <= CLIPPER2_MAX_DEC_PRECISION {
712 return;
713 }
714
715 *error_code |= PRECISION_ERROR_I; *precision = if *precision > 0 {
721 CLIPPER2_MAX_DEC_PRECISION
722 } else {
723 -CLIPPER2_MAX_DEC_PRECISION
724 };
725}
726
727#[inline]
730pub fn check_precision_range_simple(precision: &mut i32) {
731 let mut error_code = 0;
732 check_precision_range(precision, &mut error_code);
733}
734
735#[inline]
738pub fn get_bounds_path<T>(path: &Path<T>) -> Rect<T>
739where
740 T: Copy + PartialOrd + num_traits::Bounded + num_traits::Num,
741{
742 let mut xmin = T::max_value();
743 let mut ymin = T::max_value();
744 let mut xmax = T::min_value();
745 let mut ymax = T::min_value();
746
747 for p in path {
748 if p.x < xmin {
749 xmin = p.x;
750 }
751 if p.x > xmax {
752 xmax = p.x;
753 }
754 if p.y < ymin {
755 ymin = p.y;
756 }
757 if p.y > ymax {
758 ymax = p.y;
759 }
760 }
761
762 Rect::new(xmin, ymin, xmax, ymax)
763}
764
765#[inline]
768pub fn get_bounds_paths<T>(paths: &Paths<T>) -> Rect<T>
769where
770 T: Copy + PartialOrd + num_traits::Bounded + num_traits::Num,
771{
772 let mut xmin = T::max_value();
773 let mut ymin = T::max_value();
774 let mut xmax = T::min_value();
775 let mut ymax = T::min_value();
776
777 for path in paths {
778 for p in path {
779 if p.x < xmin {
780 xmin = p.x;
781 }
782 if p.x > xmax {
783 xmax = p.x;
784 }
785 if p.y < ymin {
786 ymin = p.y;
787 }
788 if p.y > ymax {
789 ymax = p.y;
790 }
791 }
792 }
793
794 Rect::new(xmin, ymin, xmax, ymax)
795}
796
797#[inline]
800pub fn get_bounds_path_convert<T, T2>(path: &Path<T2>) -> Rect<T>
801where
802 T: Copy + PartialOrd + num_traits::Bounded + num_traits::Num,
803 T2: Copy + Into<T>,
804{
805 let mut xmin = T::max_value();
806 let mut ymin = T::max_value();
807 let mut xmax = T::min_value();
808 let mut ymax = T::min_value();
809
810 for p in path {
811 let x: T = p.x.into();
812 let y: T = p.y.into();
813 if x < xmin {
814 xmin = x;
815 }
816 if x > xmax {
817 xmax = x;
818 }
819 if y < ymin {
820 ymin = y;
821 }
822 if y > ymax {
823 ymax = y;
824 }
825 }
826
827 Rect::new(xmin, ymin, xmax, ymax)
828}
829
830#[inline]
833pub fn get_bounds_paths_convert<T, T2>(paths: &Paths<T2>) -> Rect<T>
834where
835 T: Copy + PartialOrd + num_traits::Bounded + num_traits::Num,
836 T2: Copy + Into<T>,
837{
838 let mut xmin = T::max_value();
839 let mut ymin = T::max_value();
840 let mut xmax = T::min_value();
841 let mut ymax = T::min_value();
842
843 for path in paths {
844 for p in path {
845 let x: T = p.x.into();
846 let y: T = p.y.into();
847 if x < xmin {
848 xmin = x;
849 }
850 if x > xmax {
851 xmax = x;
852 }
853 if y < ymin {
854 ymin = y;
855 }
856 if y > ymax {
857 ymax = y;
858 }
859 }
860 }
861
862 Rect::new(xmin, ymin, xmax, ymax)
863}
864
865#[inline]
868pub fn sqr<T>(val: T) -> f64
869where
870 T: ToF64,
871{
872 let d = val.to_f64();
873 d * d
874}
875
876#[inline]
879pub fn distance_sqr<T>(pt1: Point<T>, pt2: Point<T>) -> f64
880where
881 T: Copy + ToF64,
882{
883 sqr(pt1.x.to_f64() - pt2.x.to_f64()) + sqr(pt1.y.to_f64() - pt2.y.to_f64())
884}
885
886#[inline]
889pub fn perpendicular_distance_from_line_sqr<T>(
890 pt: Point<T>,
891 line1: Point<T>,
892 line2: Point<T>,
893) -> f64
894where
895 T: Copy + ToF64,
896{
897 let a = pt.x.to_f64() - line1.x.to_f64();
898 let b = pt.y.to_f64() - line1.y.to_f64();
899 let c = line2.x.to_f64() - line1.x.to_f64();
900 let d = line2.y.to_f64() - line1.y.to_f64();
901
902 if c == 0.0 && d == 0.0 {
903 return 0.0;
904 }
905
906 sqr(a * d - c * b) / (c * c + d * d)
907}
908
909#[inline]
912pub fn area<T>(path: &Path<T>) -> f64
913where
914 T: Copy + ToF64,
915{
916 let cnt = path.len();
917 if cnt < 3 {
918 return 0.0;
919 }
920
921 let mut area = 0.0;
923 for i in 0..cnt {
924 let j = (i + 1) % cnt;
925 let xi = path[i].x.to_f64();
926 let yi = path[i].y.to_f64();
927 let xj = path[j].x.to_f64();
928 let yj = path[j].y.to_f64();
929
930 area += xi * yj - xj * yi;
931 }
932
933 area * 0.5
934}
935
936#[inline]
939pub fn area_paths<T>(paths: &Paths<T>) -> f64
940where
941 T: Copy + ToF64,
942{
943 let mut total_area = 0.0;
944 for path in paths {
945 total_area += area(path);
946 }
947 total_area
948}
949
950#[inline]
953pub fn is_positive<T>(poly: &Path<T>) -> bool
954where
955 T: Copy + ToF64,
956{
957 area(poly) >= 0.0
958}
959
960#[inline]
964pub fn get_location(rec: &Rect64, pt: &Point64, loc: &mut Location) -> bool {
965 if pt.x == rec.left && pt.y >= rec.top && pt.y <= rec.bottom {
966 *loc = Location::Left;
967 return false;
968 } else if pt.x == rec.right && pt.y >= rec.top && pt.y <= rec.bottom {
969 *loc = Location::Right;
970 return false;
971 } else if pt.y == rec.top && pt.x >= rec.left && pt.x <= rec.right {
972 *loc = Location::Top;
973 return false;
974 } else if pt.y == rec.bottom && pt.x >= rec.left && pt.x <= rec.right {
975 *loc = Location::Bottom;
976 return false;
977 } else if pt.x < rec.left {
978 *loc = Location::Left;
979 } else if pt.x > rec.right {
980 *loc = Location::Right;
981 } else if pt.y < rec.top {
982 *loc = Location::Top;
983 } else if pt.y > rec.bottom {
984 *loc = Location::Bottom;
985 } else {
986 *loc = Location::Inside;
987 }
988 true
989}
990
991#[inline]
995pub fn is_horizontal(pt1: &Point64, pt2: &Point64) -> bool {
996 pt1.y == pt2.y
997}
998
999#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1002#[repr(C)]
1003pub enum PointInPolygonResult {
1004 IsOn,
1005 IsInside,
1006 IsOutside,
1007}
1008
1009#[inline]
1012pub fn get_segment_intersect_pt(
1013 ln1a: Point64,
1014 ln1b: Point64,
1015 ln2a: Point64,
1016 ln2b: Point64,
1017 ip: &mut Point64,
1018) -> bool {
1019 let dx1 = ln1b.x as f64 - ln1a.x as f64;
1021 let dy1 = ln1b.y as f64 - ln1a.y as f64;
1022 let dx2 = ln2b.x as f64 - ln2a.x as f64;
1023 let dy2 = ln2b.y as f64 - ln2a.y as f64;
1024
1025 let det = dy1 * dx2 - dy2 * dx1;
1026 if det == 0.0 {
1027 return false;
1028 }
1029
1030 let t = ((ln1a.x as f64 - ln2a.x as f64) * dy2 - (ln1a.y as f64 - ln2a.y as f64) * dx2) / det;
1031
1032 if t <= 0.0 {
1033 *ip = ln1a;
1034 } else if t >= 1.0 {
1035 *ip = ln1b;
1036 } else {
1037 ip.x = (ln1a.x as f64 + t * dx1) as i64;
1039 ip.y = (ln1a.y as f64 + t * dy1) as i64;
1040 }
1041
1042 true
1043}
1044
1045#[inline]
1048pub fn get_segment_intersect_pt_d(
1049 ln1a: PointD,
1050 ln1b: PointD,
1051 ln2a: PointD,
1052 ln2b: PointD,
1053 ip: &mut PointD,
1054) -> bool {
1055 let dx1 = ln1b.x - ln1a.x;
1057 let dy1 = ln1b.y - ln1a.y;
1058 let dx2 = ln2b.x - ln2a.x;
1059 let dy2 = ln2b.y - ln2a.y;
1060
1061 let det = dy1 * dx2 - dy2 * dx1;
1062 if det == 0.0 {
1063 return false;
1064 }
1065 let t = ((ln1a.x - ln2a.x) * dy2 - (ln1a.y - ln2a.y) * dx2) / det;
1066 if t <= 0.0 {
1067 *ip = ln1a;
1068 } else if t >= 1.0 {
1069 *ip = ln1b;
1070 } else {
1071 ip.x = ln1a.x + t * dx1;
1072 ip.y = ln1a.y + t * dy1;
1073 }
1074 true
1075}
1076
1077#[inline]
1080pub fn is_collinear(pt1: Point64, shared_pt: Point64, pt2: Point64) -> bool {
1081 let a = shared_pt.x - pt1.x;
1082 let b = pt2.y - shared_pt.y;
1083 let c = shared_pt.y - pt1.y;
1084 let d = pt2.x - shared_pt.x;
1085
1086 products_are_equal(a, b, c, d)
1089}
1090
1091#[inline]
1095pub fn point_in_polygon(pt: Point64, polygon: &Path64) -> PointInPolygonResult {
1096 if polygon.len() < 3 {
1097 return PointInPolygonResult::IsOutside;
1098 }
1099
1100 let mut val = 0i32;
1101 let mut first_idx = 0;
1102
1103 while first_idx < polygon.len() && polygon[first_idx].y == pt.y {
1105 first_idx += 1;
1106 }
1107
1108 if first_idx == polygon.len() {
1109 return PointInPolygonResult::IsOutside;
1111 }
1112
1113 let mut is_above = polygon[first_idx].y < pt.y;
1114 let starting_above = is_above;
1115 let mut curr_idx = (first_idx + 1) % polygon.len();
1116 let mut wrap_to_start = false;
1117
1118 loop {
1119 if curr_idx == polygon.len() {
1121 if wrap_to_start || first_idx == 0 {
1122 break;
1123 }
1124 wrap_to_start = true;
1125 curr_idx = 0;
1126 }
1127
1128 if is_above {
1130 while curr_idx != polygon.len()
1131 && (!wrap_to_start || curr_idx < first_idx)
1132 && polygon[curr_idx].y < pt.y
1133 {
1134 curr_idx += 1;
1135 }
1136 if curr_idx == polygon.len() || (wrap_to_start && curr_idx >= first_idx) {
1137 continue;
1138 }
1139 } else {
1140 while curr_idx != polygon.len()
1141 && (!wrap_to_start || curr_idx < first_idx)
1142 && polygon[curr_idx].y > pt.y
1143 {
1144 curr_idx += 1;
1145 }
1146 if curr_idx == polygon.len() || (wrap_to_start && curr_idx >= first_idx) {
1147 continue;
1148 }
1149 }
1150
1151 let curr = &polygon[curr_idx];
1152 let prev_idx = if curr_idx == 0 {
1153 polygon.len() - 1
1154 } else {
1155 curr_idx - 1
1156 };
1157 let prev = &polygon[prev_idx];
1158
1159 if curr.y == pt.y {
1160 if curr.x == pt.x || (curr.y == prev.y && ((pt.x < prev.x) != (pt.x < curr.x))) {
1161 return PointInPolygonResult::IsOn;
1162 }
1163 curr_idx += 1;
1164 if curr_idx == first_idx {
1165 break;
1166 }
1167 continue;
1168 }
1169
1170 if pt.x < curr.x && pt.x < prev.x {
1171 } else if pt.x > prev.x && pt.x > curr.x {
1173 val = 1 - val; } else {
1175 let d = cross_product_three_points(*prev, *curr, pt);
1176 if d == 0.0 {
1177 return PointInPolygonResult::IsOn;
1178 }
1179 if (d < 0.0) == is_above {
1180 val = 1 - val;
1181 }
1182 }
1183
1184 is_above = !is_above;
1185 curr_idx += 1;
1186 }
1187
1188 if is_above != starting_above {
1190 let curr_idx = if curr_idx >= polygon.len() {
1191 0
1192 } else {
1193 curr_idx
1194 };
1195 let prev_idx = if curr_idx == 0 {
1196 polygon.len() - 1
1197 } else {
1198 curr_idx - 1
1199 };
1200 let curr = &polygon[curr_idx];
1201 let prev = &polygon[prev_idx];
1202 let d = cross_product_three_points(*prev, *curr, pt);
1203 if d == 0.0 {
1204 return PointInPolygonResult::IsOn;
1205 }
1206 if (d < 0.0) == is_above {
1207 val = 1 - val;
1208 }
1209 }
1210
1211 if val == 0 {
1212 PointInPolygonResult::IsOutside
1213 } else {
1214 PointInPolygonResult::IsInside
1215 }
1216}
1217
1218pub use get_bounds_path as get_bounds;
1221
1222pub trait FromF64: Copy {
1225 fn from_f64(val: f64) -> Self;
1226 fn is_integral() -> bool;
1227}
1228
1229impl FromF64 for i64 {
1230 #[inline]
1231 fn from_f64(val: f64) -> Self {
1232 val.round() as i64
1233 }
1234 #[inline]
1235 fn is_integral() -> bool {
1236 true
1237 }
1238}
1239
1240impl FromF64 for f64 {
1241 #[inline]
1242 fn from_f64(val: f64) -> Self {
1243 val
1244 }
1245 #[inline]
1246 fn is_integral() -> bool {
1247 false
1248 }
1249}
1250
1251#[inline]
1254pub fn scale_rect<T1, T2>(rect: &Rect<T2>, scale: f64) -> Rect<T1>
1255where
1256 T1: Num + Copy + PartialOrd + FromF64,
1257 T2: Copy + ToF64,
1258{
1259 Rect {
1260 left: T1::from_f64(rect.left.to_f64() * scale),
1261 top: T1::from_f64(rect.top.to_f64() * scale),
1262 right: T1::from_f64(rect.right.to_f64() * scale),
1263 bottom: T1::from_f64(rect.bottom.to_f64() * scale),
1264 }
1265}
1266
1267#[inline]
1270pub fn scale_path<T1, T2>(
1271 path: &Path<T2>,
1272 scale_x: f64,
1273 scale_y: f64,
1274 error_code: &mut i32,
1275) -> Path<T1>
1276where
1277 T1: Num + Copy + PartialOrd + FromF64,
1278 T2: Copy + ToF64,
1279{
1280 let mut sx = scale_x;
1281 let mut sy = scale_y;
1282
1283 if sx == 0.0 || sy == 0.0 {
1284 *error_code |= errors::SCALE_ERROR_I;
1285 if sx == 0.0 {
1287 sx = 1.0;
1288 }
1289 if sy == 0.0 {
1290 sy = 1.0;
1291 }
1292 }
1293
1294 let mut result = Vec::with_capacity(path.len());
1295 for pt in path {
1296 result.push(Point {
1297 x: T1::from_f64(pt.x.to_f64() * sx),
1298 y: T1::from_f64(pt.y.to_f64() * sy),
1299 });
1300 }
1301 result
1302}
1303
1304#[inline]
1307pub fn scale_path_uniform<T1, T2>(path: &Path<T2>, scale: f64, error_code: &mut i32) -> Path<T1>
1308where
1309 T1: Num + Copy + PartialOrd + FromF64,
1310 T2: Copy + ToF64,
1311{
1312 scale_path(path, scale, scale, error_code)
1313}
1314
1315#[inline]
1319pub fn scale_paths<T1, T2>(
1320 paths: &Paths<T2>,
1321 scale_x: f64,
1322 scale_y: f64,
1323 error_code: &mut i32,
1324) -> Paths<T1>
1325where
1326 T1: Num + Copy + PartialOrd + FromF64,
1327 T2: Copy + ToF64,
1328{
1329 if T1::is_integral() {
1331 let mut xmin = f64::MAX;
1333 let mut ymin = f64::MAX;
1334 let mut xmax = f64::MIN;
1335 let mut ymax = f64::MIN;
1336 for path in paths {
1337 for p in path {
1338 let x = p.x.to_f64();
1339 let y = p.y.to_f64();
1340 if x < xmin {
1341 xmin = x;
1342 }
1343 if x > xmax {
1344 xmax = x;
1345 }
1346 if y < ymin {
1347 ymin = y;
1348 }
1349 if y > ymax {
1350 ymax = y;
1351 }
1352 }
1353 }
1354 if (xmin * scale_x) < constants::MIN_COORD_D
1355 || (xmax * scale_x) > constants::MAX_COORD_D
1356 || (ymin * scale_y) < constants::MIN_COORD_D
1357 || (ymax * scale_y) > constants::MAX_COORD_D
1358 {
1359 *error_code |= errors::RANGE_ERROR_I;
1360 return Paths::new(); }
1362 }
1363
1364 let mut result = Vec::with_capacity(paths.len());
1365 for path in paths {
1366 result.push(scale_path(path, scale_x, scale_y, error_code));
1367 }
1368 result
1369}
1370
1371#[inline]
1374pub fn scale_paths_uniform<T1, T2>(paths: &Paths<T2>, scale: f64, error_code: &mut i32) -> Paths<T1>
1375where
1376 T1: Num + Copy + PartialOrd + FromF64,
1377 T2: Copy + ToF64,
1378{
1379 scale_paths(paths, scale, scale, error_code)
1380}
1381
1382#[inline]
1385pub fn transform_path<T1, T2>(path: &Path<T2>) -> Path<T1>
1386where
1387 T1: Num + Copy + FromF64,
1388 T2: Copy + ToF64,
1389{
1390 let mut result = Vec::with_capacity(path.len());
1391 for pt in path {
1392 result.push(Point {
1393 x: T1::from_f64(pt.x.to_f64()),
1394 y: T1::from_f64(pt.y.to_f64()),
1395 });
1396 }
1397 result
1398}
1399
1400#[inline]
1403pub fn transform_paths<T1, T2>(paths: &Paths<T2>) -> Paths<T1>
1404where
1405 T1: Num + Copy + FromF64,
1406 T2: Copy + ToF64,
1407{
1408 let mut result = Vec::with_capacity(paths.len());
1409 for path in paths {
1410 result.push(transform_path(path));
1411 }
1412 result
1413}
1414
1415#[inline]
1418pub fn near_equal<T>(p1: &Point<T>, p2: &Point<T>, max_dist_sqrd: f64) -> bool
1419where
1420 T: Copy + ToF64,
1421{
1422 sqr(p1.x.to_f64() - p2.x.to_f64()) + sqr(p1.y.to_f64() - p2.y.to_f64()) < max_dist_sqrd
1423}
1424
1425#[inline]
1428pub fn strip_near_equal<T>(path: &Path<T>, max_dist_sqrd: f64, is_closed_path: bool) -> Path<T>
1429where
1430 T: Copy + ToF64 + PartialEq + Num,
1431{
1432 if path.is_empty() {
1433 return Path::new();
1434 }
1435
1436 let mut result = Vec::with_capacity(path.len());
1437 let first_pt = path[0];
1438 let mut last_pt = first_pt;
1439 result.push(first_pt);
1440
1441 for pt in path.iter().skip(1) {
1442 if !near_equal(pt, &last_pt, max_dist_sqrd) {
1443 last_pt = *pt;
1444 result.push(last_pt);
1445 }
1446 }
1447
1448 if !is_closed_path {
1449 return result;
1450 }
1451
1452 while result.len() > 1 && near_equal(result.last().unwrap(), &first_pt, max_dist_sqrd) {
1453 result.pop();
1454 }
1455
1456 result
1457}
1458
1459#[inline]
1462pub fn strip_near_equal_paths<T>(
1463 paths: &Paths<T>,
1464 max_dist_sqrd: f64,
1465 is_closed_path: bool,
1466) -> Paths<T>
1467where
1468 T: Copy + ToF64 + PartialEq + Num,
1469{
1470 let mut result = Vec::with_capacity(paths.len());
1471 for path in paths {
1472 result.push(strip_near_equal(path, max_dist_sqrd, is_closed_path));
1473 }
1474 result
1475}
1476
1477#[inline]
1480pub fn translate_point<T>(pt: &Point<T>, dx: f64, dy: f64) -> Point<T>
1481where
1482 T: Copy + ToF64 + FromF64 + Num,
1483{
1484 Point {
1485 x: T::from_f64(pt.x.to_f64() + dx),
1486 y: T::from_f64(pt.y.to_f64() + dy),
1487 }
1488}
1489
1490#[inline]
1493pub fn reflect_point<T>(pt: &Point<T>, pivot: &Point<T>) -> Point<T>
1494where
1495 T: Num + Copy,
1496{
1497 Point {
1498 x: pivot.x + (pivot.x - pt.x),
1499 y: pivot.y + (pivot.y - pt.y),
1500 }
1501}
1502
1503#[inline]
1506pub fn get_sign<T>(val: &T) -> i32
1507where
1508 T: Num + PartialOrd,
1509{
1510 if val.is_zero() {
1511 0
1512 } else if *val > T::zero() {
1513 1
1514 } else {
1515 -1
1516 }
1517}
1518
1519#[inline]
1523pub fn cross_product_sign(pt1: Point64, pt2: Point64, pt3: Point64) -> i32 {
1524 let a = pt2.x as i128 - pt1.x as i128;
1526 let b = pt3.y as i128 - pt2.y as i128;
1527 let c = pt2.y as i128 - pt1.y as i128;
1528 let d = pt3.x as i128 - pt2.x as i128;
1529
1530 let ab = a * b;
1531 let cd = c * d;
1532
1533 if ab > cd {
1534 1
1535 } else if ab < cd {
1536 -1
1537 } else {
1538 0
1539 }
1540}
1541
1542#[inline]
1545pub fn segments_intersect(
1546 seg1a: Point64,
1547 seg1b: Point64,
1548 seg2a: Point64,
1549 seg2b: Point64,
1550 inclusive: bool,
1551) -> bool {
1552 if inclusive {
1553 let res1 = cross_product_three_points(seg1a, seg2a, seg2b);
1554 let res2 = cross_product_three_points(seg1b, seg2a, seg2b);
1555 if res1 * res2 > 0.0 {
1556 return false;
1557 }
1558 let res3 = cross_product_three_points(seg2a, seg1a, seg1b);
1559 let res4 = cross_product_three_points(seg2b, seg1a, seg1b);
1560 if res3 * res4 > 0.0 {
1561 return false;
1562 }
1563 res1 != 0.0 || res2 != 0.0 || res3 != 0.0 || res4 != 0.0
1565 } else {
1566 (get_sign(&cross_product_three_points(seg1a, seg2a, seg2b))
1567 * get_sign(&cross_product_three_points(seg1b, seg2a, seg2b))
1568 < 0)
1569 && (get_sign(&cross_product_three_points(seg2a, seg1a, seg1b))
1570 * get_sign(&cross_product_three_points(seg2b, seg1a, seg1b))
1571 < 0)
1572 }
1573}
1574
1575#[inline]
1578pub fn get_closest_point_on_segment<T>(off_pt: Point<T>, seg1: Point<T>, seg2: Point<T>) -> Point<T>
1579where
1580 T: Copy + ToF64 + FromF64 + Num + PartialEq,
1581{
1582 if seg1.x == seg2.x && seg1.y == seg2.y {
1583 return seg1;
1584 }
1585 let dx = seg2.x.to_f64() - seg1.x.to_f64();
1586 let dy = seg2.y.to_f64() - seg1.y.to_f64();
1587 let mut q =
1588 (off_pt.x.to_f64() - seg1.x.to_f64()) * dx + (off_pt.y.to_f64() - seg1.y.to_f64()) * dy;
1589 q /= sqr(dx) + sqr(dy);
1590 q = q.clamp(0.0, 1.0);
1591 Point {
1592 x: T::from_f64(seg1.x.to_f64() + q * dx),
1593 y: T::from_f64(seg1.y.to_f64() + q * dy),
1594 }
1595}
1596
1597#[inline]
1602pub fn perpendic_dist_from_line_sqrd<T>(pt: Point<T>, line1: Point<T>, line2: Point<T>) -> f64
1603where
1604 T: Copy + ToF64,
1605{
1606 let a = pt.x.to_f64() - line1.x.to_f64();
1607 let b = pt.y.to_f64() - line1.y.to_f64();
1608 let c = line2.x.to_f64() - line1.x.to_f64();
1609 let d = line2.y.to_f64() - line1.y.to_f64();
1610 if c == 0.0 && d == 0.0 {
1611 return 0.0;
1612 }
1613 sqr(a * d - c * b) / (c * c + d * d)
1614}
1615
1616pub fn ellipse_point64(center: Point64, radius_x: f64, radius_y: f64, steps: usize) -> Path64 {
1619 if radius_x <= 0.0 {
1620 return Path64::new();
1621 }
1622 let radius_y = if radius_y <= 0.0 { radius_x } else { radius_y };
1623 let steps = if steps <= 2 {
1624 (constants::PI * ((radius_x + radius_y) / 2.0).sqrt()) as usize
1625 } else {
1626 steps
1627 };
1628 if steps == 0 {
1629 return Path64::new();
1630 }
1631
1632 let si = (2.0 * constants::PI / steps as f64).sin();
1633 let co = (2.0 * constants::PI / steps as f64).cos();
1634 let mut dx = co;
1635 let mut dy = si;
1636 let mut result = Path64::with_capacity(steps);
1637 result.push(Point64::new(
1638 (center.x as f64 + radius_x).round() as i64,
1639 center.y,
1640 ));
1641 for _ in 1..steps {
1642 result.push(Point64::new(
1643 (center.x as f64 + radius_x * dx).round() as i64,
1644 (center.y as f64 + radius_y * dy).round() as i64,
1645 ));
1646 let x = dx * co - dy * si;
1647 dy = dy * co + dx * si;
1648 dx = x;
1649 }
1650 result
1651}
1652
1653pub fn ellipse_point_d(center: PointD, radius_x: f64, radius_y: f64, steps: usize) -> PathD {
1656 if radius_x <= 0.0 {
1657 return PathD::new();
1658 }
1659 let radius_y = if radius_y <= 0.0 { radius_x } else { radius_y };
1660 let steps = if steps <= 2 {
1661 (constants::PI * ((radius_x + radius_y) / 2.0).sqrt()) as usize
1662 } else {
1663 steps
1664 };
1665 if steps == 0 {
1666 return PathD::new();
1667 }
1668
1669 let si = (2.0 * constants::PI / steps as f64).sin();
1670 let co = (2.0 * constants::PI / steps as f64).cos();
1671 let mut dx = co;
1672 let mut dy = si;
1673 let mut result = PathD::with_capacity(steps);
1674 result.push(PointD::new(center.x + radius_x, center.y));
1675 for _ in 1..steps {
1676 result.push(PointD::new(
1677 center.x + radius_x * dx,
1678 center.y + radius_y * dy,
1679 ));
1680 let x = dx * co - dy * si;
1681 dy = dy * co + dx * si;
1682 dx = x;
1683 }
1684 result
1685}
1686
1687#[cfg(test)]
1689#[path = "core_tests.rs"]
1690mod tests;