1use crate::core::{
17 check_precision_range, constants, cross_product_three_points, distance_sqr, is_collinear,
18 perpendic_dist_from_line_sqrd, point_in_polygon, scale_path, scale_paths, scale_rect, sqr,
19 FromF64, Path, Path64, PathD, Paths, Paths64, PathsD, Point, Point64, PointInPolygonResult,
20 Rect64, RectD, ToF64,
21};
22use crate::engine::ClipType;
23use crate::engine_public::{Clipper64, ClipperD, PolyTree64, PolyTreeD};
24use crate::offset::{ClipperOffset, EndType, JoinType};
25use crate::rectclip::{RectClip64, RectClipLines64};
26use crate::FillRule;
27use num_traits::Num;
28
29pub fn boolean_op_64(
36 clip_type: ClipType,
37 fill_rule: FillRule,
38 subjects: &Paths64,
39 clips: &Paths64,
40) -> Paths64 {
41 let mut result = Paths64::new();
42 let mut clipper = Clipper64::new();
43 clipper.add_subject(subjects);
44 clipper.add_clip(clips);
45 clipper.execute(clip_type, fill_rule, &mut result, None);
46 result
47}
48
49pub fn boolean_op_tree_64(
52 clip_type: ClipType,
53 fill_rule: FillRule,
54 subjects: &Paths64,
55 clips: &Paths64,
56 solution: &mut PolyTree64,
57) {
58 let mut sol_open = Paths64::new();
59 let mut clipper = Clipper64::new();
60 clipper.add_subject(subjects);
61 clipper.add_clip(clips);
62 clipper.execute_tree(clip_type, fill_rule, solution, &mut sol_open);
63}
64
65pub fn boolean_op_d(
68 clip_type: ClipType,
69 fill_rule: FillRule,
70 subjects: &PathsD,
71 clips: &PathsD,
72 precision: i32,
73) -> PathsD {
74 let mut error_code = 0;
75 let mut prec = precision;
76 check_precision_range(&mut prec, &mut error_code);
77 let mut result = PathsD::new();
78 if error_code != 0 {
79 return result;
80 }
81 let mut clipper = ClipperD::new(precision);
82 clipper.add_subject(subjects);
83 clipper.add_clip(clips);
84 clipper.execute(clip_type, fill_rule, &mut result, None);
85 result
86}
87
88pub fn boolean_op_tree_d(
91 clip_type: ClipType,
92 fill_rule: FillRule,
93 subjects: &PathsD,
94 clips: &PathsD,
95 polytree: &mut PolyTreeD,
96 precision: i32,
97) {
98 polytree.clear();
99 let mut error_code = 0;
100 let mut prec = precision;
101 check_precision_range(&mut prec, &mut error_code);
102 if error_code != 0 {
103 return;
104 }
105 let mut clipper = ClipperD::new(precision);
106 clipper.add_subject(subjects);
107 clipper.add_clip(clips);
108 let mut open_paths = PathsD::new();
109 clipper.execute_tree(clip_type, fill_rule, polytree, &mut open_paths);
110}
111
112pub fn intersect_64(subjects: &Paths64, clips: &Paths64, fill_rule: FillRule) -> Paths64 {
119 boolean_op_64(ClipType::Intersection, fill_rule, subjects, clips)
120}
121
122pub fn intersect_d(
125 subjects: &PathsD,
126 clips: &PathsD,
127 fill_rule: FillRule,
128 precision: i32,
129) -> PathsD {
130 boolean_op_d(
131 ClipType::Intersection,
132 fill_rule,
133 subjects,
134 clips,
135 precision,
136 )
137}
138
139pub fn union_64(subjects: &Paths64, clips: &Paths64, fill_rule: FillRule) -> Paths64 {
146 boolean_op_64(ClipType::Union, fill_rule, subjects, clips)
147}
148
149pub fn union_d(subjects: &PathsD, clips: &PathsD, fill_rule: FillRule, precision: i32) -> PathsD {
152 boolean_op_d(ClipType::Union, fill_rule, subjects, clips, precision)
153}
154
155pub fn union_subjects_64(subjects: &Paths64, fill_rule: FillRule) -> Paths64 {
158 let mut result = Paths64::new();
159 let mut clipper = Clipper64::new();
160 clipper.add_subject(subjects);
161 clipper.execute(ClipType::Union, fill_rule, &mut result, None);
162 result
163}
164
165pub fn union_subjects_d(subjects: &PathsD, fill_rule: FillRule, precision: i32) -> PathsD {
168 let mut result = PathsD::new();
169 let mut error_code = 0;
170 let mut prec = precision;
171 check_precision_range(&mut prec, &mut error_code);
172 if error_code != 0 {
173 return result;
174 }
175 let mut clipper = ClipperD::new(precision);
176 clipper.add_subject(subjects);
177 clipper.execute(ClipType::Union, fill_rule, &mut result, None);
178 result
179}
180
181pub fn difference_64(subjects: &Paths64, clips: &Paths64, fill_rule: FillRule) -> Paths64 {
188 boolean_op_64(ClipType::Difference, fill_rule, subjects, clips)
189}
190
191pub fn difference_d(
194 subjects: &PathsD,
195 clips: &PathsD,
196 fill_rule: FillRule,
197 precision: i32,
198) -> PathsD {
199 boolean_op_d(ClipType::Difference, fill_rule, subjects, clips, precision)
200}
201
202pub fn xor_64(subjects: &Paths64, clips: &Paths64, fill_rule: FillRule) -> Paths64 {
209 boolean_op_64(ClipType::Xor, fill_rule, subjects, clips)
210}
211
212pub fn xor_d(subjects: &PathsD, clips: &PathsD, fill_rule: FillRule, precision: i32) -> PathsD {
215 boolean_op_d(ClipType::Xor, fill_rule, subjects, clips, precision)
216}
217
218pub fn inflate_paths_64(
225 paths: &Paths64,
226 delta: f64,
227 jt: JoinType,
228 et: EndType,
229 miter_limit: f64,
230 arc_tolerance: f64,
231) -> Paths64 {
232 if delta == 0.0 {
233 return paths.clone();
234 }
235 let mut clip_offset = ClipperOffset::new(miter_limit, arc_tolerance, false, false);
236 clip_offset.add_paths(paths, jt, et);
237 let mut solution = Paths64::new();
238 clip_offset.execute(delta, &mut solution);
239 solution
240}
241
242pub fn inflate_paths_d(
245 paths: &PathsD,
246 delta: f64,
247 jt: JoinType,
248 et: EndType,
249 miter_limit: f64,
250 precision: i32,
251 arc_tolerance: f64,
252) -> PathsD {
253 let mut error_code = 0;
254 let mut prec = precision;
255 check_precision_range(&mut prec, &mut error_code);
256 if delta == 0.0 {
257 return paths.clone();
258 }
259 if error_code != 0 {
260 return PathsD::new();
261 }
262 let scale = 10f64.powi(precision);
263 let mut clip_offset = ClipperOffset::new(miter_limit, arc_tolerance * scale, false, false);
264 let scaled_paths: Paths64 = scale_paths(paths, scale, scale, &mut error_code);
265 if error_code != 0 {
266 return PathsD::new();
267 }
268 clip_offset.add_paths(&scaled_paths, jt, et);
269 let mut solution = Paths64::new();
270 clip_offset.execute(delta * scale, &mut solution);
271 scale_paths(&solution, 1.0 / scale, 1.0 / scale, &mut error_code)
272}
273
274pub fn translate_path<T>(path: &Path<T>, dx: T, dy: T) -> Path<T>
281where
282 T: Copy + std::ops::Add<Output = T>,
283{
284 let mut result = Vec::with_capacity(path.len());
285 for pt in path {
286 result.push(Point {
287 x: pt.x + dx,
288 y: pt.y + dy,
289 });
290 }
291 result
292}
293
294pub fn translate_paths<T>(paths: &Paths<T>, dx: T, dy: T) -> Paths<T>
297where
298 T: Copy + std::ops::Add<Output = T>,
299{
300 let mut result = Vec::with_capacity(paths.len());
301 for path in paths {
302 result.push(translate_path(path, dx, dy));
303 }
304 result
305}
306
307pub fn rect_clip_64(rect: &Rect64, paths: &Paths64) -> Paths64 {
314 if rect.is_empty() || paths.is_empty() {
315 return Paths64::new();
316 }
317 let mut rc = RectClip64::new(*rect);
318 rc.execute(paths)
319}
320
321pub fn rect_clip_path_64(rect: &Rect64, path: &Path64) -> Paths64 {
324 if rect.is_empty() || path.is_empty() {
325 return Paths64::new();
326 }
327 let mut rc = RectClip64::new(*rect);
328 rc.execute(&vec![path.clone()])
329}
330
331pub fn rect_clip_d(rect: &RectD, paths: &PathsD, precision: i32) -> PathsD {
334 if rect.is_empty() || paths.is_empty() {
335 return PathsD::new();
336 }
337 let mut error_code = 0;
338 let mut prec = precision;
339 check_precision_range(&mut prec, &mut error_code);
340 if error_code != 0 {
341 return PathsD::new();
342 }
343 let scale = 10f64.powi(precision);
344 let r: Rect64 = scale_rect(rect, scale);
345 let mut rc = RectClip64::new(r);
346 let pp: Paths64 = scale_paths(paths, scale, scale, &mut error_code);
347 if error_code != 0 {
348 return PathsD::new();
349 }
350 let result = rc.execute(&pp);
351 scale_paths(&result, 1.0 / scale, 1.0 / scale, &mut error_code)
352}
353
354pub fn rect_clip_path_d(rect: &RectD, path: &PathD, precision: i32) -> PathsD {
357 rect_clip_d(rect, &vec![path.clone()], precision)
358}
359
360pub fn rect_clip_lines_64(rect: &Rect64, lines: &Paths64) -> Paths64 {
367 if rect.is_empty() || lines.is_empty() {
368 return Paths64::new();
369 }
370 let mut rcl = RectClipLines64::new(*rect);
371 rcl.execute(lines)
372}
373
374pub fn rect_clip_line_64(rect: &Rect64, line: &Path64) -> Paths64 {
377 rect_clip_lines_64(rect, &vec![line.clone()])
378}
379
380pub fn rect_clip_lines_d(rect: &RectD, lines: &PathsD, precision: i32) -> PathsD {
383 if rect.is_empty() || lines.is_empty() {
384 return PathsD::new();
385 }
386 let mut error_code = 0;
387 let mut prec = precision;
388 check_precision_range(&mut prec, &mut error_code);
389 if error_code != 0 {
390 return PathsD::new();
391 }
392 let scale = 10f64.powi(precision);
393 let r: Rect64 = scale_rect(rect, scale);
394 let mut rcl = RectClipLines64::new(r);
395 let p: Paths64 = scale_paths(lines, scale, scale, &mut error_code);
396 if error_code != 0 {
397 return PathsD::new();
398 }
399 let result = rcl.execute(&p);
400 scale_paths(&result, 1.0 / scale, 1.0 / scale, &mut error_code)
401}
402
403pub fn rect_clip_line_d(rect: &RectD, line: &PathD, precision: i32) -> PathsD {
406 rect_clip_lines_d(rect, &vec![line.clone()], precision)
407}
408
409fn poly_path_to_paths64(tree: &PolyTree64, node_idx: usize, paths: &mut Paths64) {
415 let polygon = tree.nodes[node_idx].polygon().clone();
416 if !polygon.is_empty() {
417 paths.push(polygon);
418 }
419 for &child_idx in tree.nodes[node_idx].children() {
420 poly_path_to_paths64(tree, child_idx, paths);
421 }
422}
423
424fn poly_path_to_paths_d(tree: &PolyTreeD, node_idx: usize, paths: &mut PathsD) {
426 let polygon = tree.nodes[node_idx].polygon().clone();
427 if !polygon.is_empty() {
428 paths.push(polygon);
429 }
430 for &child_idx in tree.nodes[node_idx].children() {
431 poly_path_to_paths_d(tree, child_idx, paths);
432 }
433}
434
435pub fn poly_tree_to_paths64(polytree: &PolyTree64) -> Paths64 {
438 let mut result = Paths64::new();
439 let root = &polytree.nodes[0];
440 for &child_idx in root.children() {
441 poly_path_to_paths64(polytree, child_idx, &mut result);
442 }
443 result
444}
445
446pub fn poly_tree_to_paths_d(polytree: &PolyTreeD) -> PathsD {
449 let mut result = PathsD::new();
450 let root = &polytree.nodes[0];
451 for &child_idx in root.children() {
452 poly_path_to_paths_d(polytree, child_idx, &mut result);
453 }
454 result
455}
456
457pub fn check_polytree_fully_contains_children(polytree: &PolyTree64) -> bool {
460 let root = &polytree.nodes[0];
461 for &child_idx in root.children() {
462 if polytree.nodes[child_idx].count() > 0
463 && !poly_path64_contains_children(polytree, child_idx)
464 {
465 return false;
466 }
467 }
468 true
469}
470
471fn poly_path64_contains_children(tree: &PolyTree64, node_idx: usize) -> bool {
474 let parent_polygon = tree.nodes[node_idx].polygon();
475 for &child_idx in tree.nodes[node_idx].children() {
476 let child_polygon = tree.nodes[child_idx].polygon();
477 let mut outside_cnt: i32 = 0;
482 for pt in child_polygon {
483 let result = point_in_polygon(*pt, parent_polygon);
484 if result == PointInPolygonResult::IsInside {
485 outside_cnt -= 1;
486 } else if result == PointInPolygonResult::IsOutside {
487 outside_cnt += 1;
488 }
489 if outside_cnt > 1 {
490 return false;
491 } else if outside_cnt < -1 {
492 break;
493 }
494 }
495
496 if tree.nodes[child_idx].count() > 0 && !poly_path64_contains_children(tree, child_idx) {
498 return false;
499 }
500 }
501 true
502}
503
504pub fn make_path64(coords: &[i64]) -> Path64 {
511 let size = coords.len() - coords.len() % 2;
512 let mut result = Path64::with_capacity(size / 2);
513 let mut i = 0;
514 while i < size {
515 result.push(Point64::new(coords[i], coords[i + 1]));
516 i += 2;
517 }
518 result
519}
520
521pub fn make_path_d(coords: &[f64]) -> PathD {
524 let size = coords.len() - coords.len() % 2;
525 let mut result = PathD::with_capacity(size / 2);
526 let mut i = 0;
527 while i < size {
528 result.push(Point::<f64>::new(coords[i], coords[i + 1]));
529 i += 2;
530 }
531 result
532}
533
534pub fn trim_collinear_64(p: &Path64, is_open_path: bool) -> Path64 {
541 let len = p.len();
542 if len < 3 {
543 if !is_open_path || len < 2 || p[0] == p[1] {
544 return Path64::new();
545 } else {
546 return p.clone();
547 }
548 }
549
550 let mut dst = Path64::with_capacity(len);
551 let mut src_idx: usize = 0;
552 let mut stop = len - 1;
553
554 if !is_open_path {
555 while src_idx != stop && is_collinear(p[stop], p[src_idx], p[src_idx + 1]) {
556 src_idx += 1;
557 }
558 while src_idx != stop && is_collinear(p[stop - 1], p[stop], p[src_idx]) {
559 stop -= 1;
560 }
561 if src_idx == stop {
562 return Path64::new();
563 }
564 }
565
566 let mut prev_idx = src_idx;
567 dst.push(p[prev_idx]);
568 src_idx += 1;
569
570 while src_idx < stop {
571 if !is_collinear(p[prev_idx], p[src_idx], p[src_idx + 1]) {
572 prev_idx = src_idx;
573 dst.push(p[prev_idx]);
574 }
575 src_idx += 1;
576 }
577
578 if is_open_path || !is_collinear(p[prev_idx], p[stop], dst[0]) {
579 dst.push(p[stop]);
580 } else {
581 while dst.len() > 2 && is_collinear(dst[dst.len() - 1], dst[dst.len() - 2], dst[0]) {
582 dst.pop();
583 }
584 if dst.len() < 3 {
585 return Path64::new();
586 }
587 }
588 dst
589}
590
591pub fn trim_collinear_d(path: &PathD, precision: i32, is_open_path: bool) -> PathD {
594 let mut error_code = 0;
595 let mut prec = precision;
596 check_precision_range(&mut prec, &mut error_code);
597 if error_code != 0 {
598 return PathD::new();
599 }
600 let scale = 10f64.powi(precision);
601 let p: Path64 = scale_path(path, scale, scale, &mut error_code);
602 if error_code != 0 {
603 return PathD::new();
604 }
605 let p = trim_collinear_64(&p, is_open_path);
606 scale_path(&p, 1.0 / scale, 1.0 / scale, &mut error_code)
607}
608
609pub fn distance<T>(pt1: Point<T>, pt2: Point<T>) -> f64
616where
617 T: Copy + ToF64,
618{
619 distance_sqr(pt1, pt2).sqrt()
620}
621
622pub fn path_length<T>(path: &Path<T>, is_closed_path: bool) -> f64
625where
626 T: Copy + ToF64,
627{
628 let mut result = 0.0;
629 if path.len() < 2 {
630 return result;
631 }
632 for i in 0..path.len() - 1 {
633 result += distance(path[i], path[i + 1]);
634 }
635 if is_closed_path {
636 result += distance(path[path.len() - 1], path[0]);
637 }
638 result
639}
640
641pub fn near_collinear<T>(
648 pt1: Point<T>,
649 pt2: Point<T>,
650 pt3: Point<T>,
651 sin_sqrd_min_angle_rads: f64,
652) -> bool
653where
654 T: Copy + ToF64,
655{
656 let cp = cross_product_three_points(pt1, pt2, pt3).abs();
657 (cp * cp) / (distance_sqr(pt1, pt2) * distance_sqr(pt2, pt3)) < sin_sqrd_min_angle_rads
658}
659
660fn get_next(current: usize, high: usize, flags: &[bool]) -> usize {
666 let mut c = current + 1;
667 while c <= high && flags[c] {
668 c += 1;
669 }
670 if c <= high {
671 return c;
672 }
673 c = 0;
674 while flags[c] {
675 c += 1;
676 }
677 c
678}
679
680fn get_prior(current: usize, high: usize, flags: &[bool]) -> usize {
682 let mut c = if current == 0 { high } else { current - 1 };
683 while c > 0 && flags[c] {
684 c -= 1;
685 }
686 if !flags[c] {
687 return c;
688 }
689 c = high;
690 while flags[c] {
691 c -= 1;
692 }
693 c
694}
695
696pub fn simplify_path<T>(path: &Path<T>, epsilon: f64, is_closed_path: bool) -> Path<T>
700where
701 T: Copy + ToF64 + FromF64 + Num + PartialEq,
702{
703 let len = path.len();
704 if len < 4 {
705 return path.clone();
706 }
707 let high = len - 1;
708 let eps_sqr = sqr(epsilon);
709
710 let mut flags = vec![false; len];
711 let mut dist_sqr = vec![0.0f64; len];
712
713 if is_closed_path {
714 dist_sqr[0] = perpendic_dist_from_line_sqrd(path[0], path[high], path[1]);
715 dist_sqr[high] = perpendic_dist_from_line_sqrd(path[high], path[0], path[high - 1]);
716 } else {
717 dist_sqr[0] = constants::MAX_DBL;
718 dist_sqr[high] = constants::MAX_DBL;
719 }
720 for i in 1..high {
721 dist_sqr[i] = perpendic_dist_from_line_sqrd(path[i], path[i - 1], path[i + 1]);
722 }
723
724 let mut curr = 0usize;
725 loop {
726 if dist_sqr[curr] > eps_sqr {
727 let start = curr;
728 loop {
729 curr = get_next(curr, high, &flags);
730 if curr == start || dist_sqr[curr] <= eps_sqr {
731 break;
732 }
733 }
734 if curr == start {
735 break;
736 }
737 }
738
739 let prior = get_prior(curr, high, &flags);
740 let mut next = get_next(curr, high, &flags);
741 if next == prior {
742 break;
743 }
744
745 let prior2;
746 let prior_for_update;
747 if dist_sqr[next] < dist_sqr[curr] {
748 prior_for_update = curr;
749 curr = next;
750 next = get_next(next, high, &flags);
751 prior2 = get_prior(prior_for_update, high, &flags);
752 } else {
753 prior_for_update = prior;
754 prior2 = get_prior(prior, high, &flags);
755 }
756
757 flags[curr] = true;
758 curr = next;
759 next = get_next(next, high, &flags);
760
761 if is_closed_path || (curr != high && curr != 0) {
762 dist_sqr[curr] =
763 perpendic_dist_from_line_sqrd(path[curr], path[prior_for_update], path[next]);
764 }
765 if is_closed_path || (prior_for_update != 0 && prior_for_update != high) {
766 dist_sqr[prior_for_update] =
767 perpendic_dist_from_line_sqrd(path[prior_for_update], path[prior2], path[curr]);
768 }
769 }
770
771 let mut result = Vec::with_capacity(len);
772 for i in 0..len {
773 if !flags[i] {
774 result.push(path[i]);
775 }
776 }
777 result
778}
779
780pub fn simplify_paths<T>(paths: &Paths<T>, epsilon: f64, is_closed_path: bool) -> Paths<T>
783where
784 T: Copy + ToF64 + FromF64 + Num + PartialEq,
785{
786 let mut result = Vec::with_capacity(paths.len());
787 for path in paths {
788 result.push(simplify_path(path, epsilon, is_closed_path));
789 }
790 result
791}
792
793fn rdp<T>(path: &Path<T>, begin: usize, end: usize, eps_sqrd: f64, flags: &mut Vec<bool>)
803where
804 T: Copy + ToF64 + PartialEq,
805{
806 let mut idx = 0;
807 let mut max_d = 0.0;
808 let mut actual_end = end;
809
810 while actual_end > begin && path[begin] == path[actual_end] {
812 flags[actual_end] = false;
813 actual_end -= 1;
814 }
815
816 for i in (begin + 1)..actual_end {
817 let d = perpendic_dist_from_line_sqrd(path[i], path[begin], path[actual_end]);
818 if d <= max_d {
819 continue;
820 }
821 max_d = d;
822 idx = i;
823 }
824
825 if max_d <= eps_sqrd {
826 return;
827 }
828
829 flags[idx] = true;
830 if idx > begin + 1 {
831 rdp(path, begin, idx, eps_sqrd, flags);
832 }
833 if idx < actual_end - 1 {
834 rdp(path, idx, actual_end, eps_sqrd, flags);
835 }
836}
837
838pub fn ramer_douglas_peucker<T>(path: &Path<T>, epsilon: f64) -> Path<T>
841where
842 T: Copy + ToF64 + PartialEq,
843{
844 let len = path.len();
845 if len < 5 {
846 return path.clone();
847 }
848 let mut flags = vec![false; len];
849 flags[0] = true;
850 flags[len - 1] = true;
851 rdp(path, 0, len - 1, sqr(epsilon), &mut flags);
852 let mut result = Vec::with_capacity(len);
853 for i in 0..len {
854 if flags[i] {
855 result.push(path[i]);
856 }
857 }
858 result
859}
860
861pub fn ramer_douglas_peucker_paths<T>(paths: &Paths<T>, epsilon: f64) -> Paths<T>
864where
865 T: Copy + ToF64 + PartialEq,
866{
867 let mut result = Vec::with_capacity(paths.len());
868 for path in paths {
869 result.push(ramer_douglas_peucker(path, epsilon));
870 }
871 result
872}
873
874#[cfg(test)]
879#[path = "clipper_tests.rs"]
880mod tests;