1use crate::{Path, Point, Transform};
10
11use crate::dash::StrokeDash;
12use crate::floating_point::{NonZeroPositiveF32, NormalizedF32, NormalizedF32Exclusive};
13use crate::path::{PathSegment, PathSegmentsIter};
14use crate::path_builder::{PathBuilder, PathDirection};
15use crate::path_geometry;
16use crate::scalar::{Scalar, SCALAR_NEARLY_ZERO, SCALAR_ROOT_2_OVER_2};
17
18#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
19use crate::NoStdFloat;
20
21struct SwappableBuilders<'a> {
22 inner: &'a mut PathBuilder,
23 outer: &'a mut PathBuilder,
24}
25
26impl SwappableBuilders<'_> {
27 fn swap(&mut self) {
28 core::mem::swap(&mut self.inner, &mut self.outer);
34 }
35}
36
37#[derive(Clone, PartialEq, Debug)]
39pub struct Stroke {
40 pub width: f32,
48
49 pub miter_limit: f32,
53
54 pub line_cap: LineCap,
58
59 pub line_join: LineJoin,
63
64 pub dash: Option<StrokeDash>,
68}
69
70impl Default for Stroke {
71 fn default() -> Self {
72 Stroke {
73 width: 1.0,
74 miter_limit: 4.0,
75 line_cap: LineCap::default(),
76 line_join: LineJoin::default(),
77 dash: None,
78 }
79 }
80}
81
82#[derive(Copy, Clone, Default, PartialEq, Debug)]
84pub enum LineCap {
85 #[default]
87 Butt,
88 Round,
90 Square,
92}
93
94#[derive(Copy, Clone, Default, PartialEq, Debug)]
107pub enum LineJoin {
108 #[default]
110 Miter,
111 MiterClip,
113 Round,
115 Bevel,
117}
118
119const QUAD_RECURSIVE_LIMIT: usize = 3;
120
121const RECURSIVE_LIMITS: [i32; 4] = [5 * 3, 26 * 3, 11 * 3, 11 * 3]; type CapProc = fn(
127 pivot: Point,
128 normal: Point,
129 stop: Point,
130 other_path: Option<&PathBuilder>,
131 path: &mut PathBuilder,
132);
133
134type JoinProc = fn(
135 before_unit_normal: Point,
136 pivot: Point,
137 after_unit_normal: Point,
138 radius: f32,
139 inv_miter_limit: f32,
140 prev_is_line: bool,
141 curr_is_line: bool,
142 builders: SwappableBuilders,
143);
144
145#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
146enum ReductionType {
147 Point, Line, Quad, Degenerate, Degenerate2, Degenerate3, }
154
155#[derive(Copy, Clone, PartialEq, Debug)]
156enum StrokeType {
157 Outer = 1, Inner = -1,
159}
160
161#[derive(Copy, Clone, PartialEq, Debug)]
162enum ResultType {
163 Split, Degenerate, Quad, }
167
168#[derive(Copy, Clone, PartialEq, Debug)]
169enum IntersectRayType {
170 CtrlPt,
171 ResultType,
172}
173
174impl Path {
175 pub fn stroke(&self, stroke: &Stroke, resolution_scale: f32) -> Option<Path> {
184 PathStroker::new().stroke(self, stroke, resolution_scale)
185 }
186}
187
188#[allow(missing_debug_implementations)]
190#[derive(Clone)]
191pub struct PathStroker {
192 radius: f32,
193 inv_miter_limit: f32,
194 res_scale: f32,
195 inv_res_scale: f32,
196 inv_res_scale_squared: f32,
197
198 first_normal: Point,
199 prev_normal: Point,
200 first_unit_normal: Point,
201 prev_unit_normal: Point,
202
203 first_pt: Point,
205 prev_pt: Point,
206
207 first_outer_pt: Point,
208 first_outer_pt_index_in_contour: usize,
209 segment_count: i32,
210 prev_is_line: bool,
211
212 capper: CapProc,
213 joiner: JoinProc,
214
215 inner: PathBuilder,
217 outer: PathBuilder,
218 cusper: PathBuilder,
219
220 stroke_type: StrokeType,
221
222 recursion_depth: i32, found_tangents: bool, join_completed: bool, }
226
227impl Default for PathStroker {
228 fn default() -> Self {
229 PathStroker::new()
230 }
231}
232
233impl PathStroker {
234 pub fn new() -> Self {
236 PathStroker {
237 radius: 0.0,
238 inv_miter_limit: 0.0,
239 res_scale: 1.0,
240 inv_res_scale: 1.0,
241 inv_res_scale_squared: 1.0,
242
243 first_normal: Point::zero(),
244 prev_normal: Point::zero(),
245 first_unit_normal: Point::zero(),
246 prev_unit_normal: Point::zero(),
247
248 first_pt: Point::zero(),
249 prev_pt: Point::zero(),
250
251 first_outer_pt: Point::zero(),
252 first_outer_pt_index_in_contour: 0,
253 segment_count: -1,
254 prev_is_line: false,
255
256 capper: butt_capper,
257 joiner: miter_joiner,
258
259 inner: PathBuilder::new(),
260 outer: PathBuilder::new(),
261 cusper: PathBuilder::new(),
262
263 stroke_type: StrokeType::Outer,
264
265 recursion_depth: 0,
266 found_tangents: false,
267 join_completed: false,
268 }
269 }
270
271 pub fn compute_resolution_scale(ts: &Transform) -> f32 {
281 let sx = Point::from_xy(ts.sx, ts.kx).length();
282 let sy = Point::from_xy(ts.ky, ts.sy).length();
283 if sx.is_finite() && sy.is_finite() {
284 let scale = sx.max(sy);
285 if scale > 0.0 {
286 return scale;
287 }
288 }
289
290 1.0
291 }
292
293 pub fn stroke(&mut self, path: &Path, stroke: &Stroke, resolution_scale: f32) -> Option<Path> {
300 let width = NonZeroPositiveF32::new(stroke.width)?;
301 self.stroke_inner(
302 path,
303 width,
304 stroke.miter_limit,
305 stroke.line_cap,
306 stroke.line_join,
307 resolution_scale,
308 )
309 }
310
311 fn stroke_inner(
312 &mut self,
313 path: &Path,
314 width: NonZeroPositiveF32,
315 miter_limit: f32,
316 line_cap: LineCap,
317 mut line_join: LineJoin,
318 res_scale: f32,
319 ) -> Option<Path> {
320 let mut inv_miter_limit = 0.0;
323
324 if line_join == LineJoin::Miter {
325 if miter_limit <= 1.0 {
326 line_join = LineJoin::Bevel;
327 } else {
328 inv_miter_limit = miter_limit.invert();
329 }
330 }
331
332 if line_join == LineJoin::MiterClip {
333 inv_miter_limit = miter_limit.invert();
334 }
335
336 self.res_scale = res_scale;
337 self.inv_res_scale = (res_scale * 4.0).invert();
339 self.inv_res_scale_squared = self.inv_res_scale.sqr();
340
341 self.radius = width.get().half();
342 self.inv_miter_limit = inv_miter_limit;
343
344 self.first_normal = Point::zero();
345 self.prev_normal = Point::zero();
346 self.first_unit_normal = Point::zero();
347 self.prev_unit_normal = Point::zero();
348
349 self.first_pt = Point::zero();
350 self.prev_pt = Point::zero();
351
352 self.first_outer_pt = Point::zero();
353 self.first_outer_pt_index_in_contour = 0;
354 self.segment_count = -1;
355 self.prev_is_line = false;
356
357 self.capper = cap_factory(line_cap);
358 self.joiner = join_factory(line_join);
359
360 self.inner.clear();
366 self.inner.reserve(path.verbs.len(), path.points.len());
367
368 self.outer.clear();
370 self.outer
371 .reserve(path.verbs.len() * 3, path.points.len() * 3);
372
373 self.cusper.clear();
374
375 self.stroke_type = StrokeType::Outer;
376
377 self.recursion_depth = 0;
378 self.found_tangents = false;
379 self.join_completed = false;
380
381 let mut last_segment_is_line = false;
382 let mut iter = path.segments();
383 iter.set_auto_close(true);
384 while let Some(segment) = iter.next() {
385 match segment {
386 PathSegment::MoveTo(p) => {
387 self.move_to(p);
388 }
389 PathSegment::LineTo(p) => {
390 self.line_to(p, Some(&iter));
391 last_segment_is_line = true;
392 }
393 PathSegment::QuadTo(p1, p2) => {
394 self.quad_to(p1, p2);
395 last_segment_is_line = false;
396 }
397 PathSegment::CubicTo(p1, p2, p3) => {
398 self.cubic_to(p1, p2, p3);
399 last_segment_is_line = false;
400 }
401 PathSegment::Close => {
402 if line_cap != LineCap::Butt {
403 if self.has_only_move_to() {
407 self.line_to(self.move_to_pt(), None);
408 last_segment_is_line = true;
409 continue;
410 }
411
412 if self.is_current_contour_empty() {
416 last_segment_is_line = true;
417 continue;
418 }
419 }
420
421 self.close(last_segment_is_line);
422 }
423 }
424 }
425
426 self.finish(last_segment_is_line)
427 }
428
429 fn builders(&mut self) -> SwappableBuilders<'_> {
430 SwappableBuilders {
431 inner: &mut self.inner,
432 outer: &mut self.outer,
433 }
434 }
435
436 fn move_to_pt(&self) -> Point {
437 self.first_pt
438 }
439
440 fn move_to(&mut self, p: Point) {
441 if self.segment_count > 0 {
442 self.finish_contour(false, false);
443 }
444
445 self.segment_count = 0;
446 self.first_pt = p;
447 self.prev_pt = p;
448 self.join_completed = false;
449 }
450
451 fn line_to(&mut self, p: Point, iter: Option<&PathSegmentsIter>) {
452 let teeny_line = self
453 .prev_pt
454 .equals_within_tolerance(p, SCALAR_NEARLY_ZERO * self.inv_res_scale);
455 if fn_ptr_eq(self.capper, butt_capper) && teeny_line {
456 return;
457 }
458
459 if teeny_line && (self.join_completed || iter.map(|i| i.has_valid_tangent()) == Some(true))
460 {
461 return;
462 }
463
464 let mut normal = Point::zero();
465 let mut unit_normal = Point::zero();
466 if !self.pre_join_to(p, true, &mut normal, &mut unit_normal) {
467 return;
468 }
469
470 self.outer.line_to(p.x + normal.x, p.y + normal.y);
471 self.inner.line_to(p.x - normal.x, p.y - normal.y);
472
473 self.post_join_to(p, normal, unit_normal);
474 }
475
476 fn quad_to(&mut self, p1: Point, p2: Point) {
477 let quad = [self.prev_pt, p1, p2];
478 let (reduction, reduction_type) = check_quad_linear(&quad);
479 if reduction_type == ReductionType::Point {
480 self.line_to(p2, None);
484 return;
485 }
486
487 if reduction_type == ReductionType::Line {
488 self.line_to(p2, None);
489 return;
490 }
491
492 if reduction_type == ReductionType::Degenerate {
493 self.line_to(reduction, None);
494 let save_joiner = self.joiner;
495 self.joiner = round_joiner;
496 self.line_to(p2, None);
497 self.joiner = save_joiner;
498 return;
499 }
500
501 debug_assert_eq!(reduction_type, ReductionType::Quad);
502
503 let mut normal_ab = Point::zero();
504 let mut unit_ab = Point::zero();
505 let mut normal_bc = Point::zero();
506 let mut unit_bc = Point::zero();
507 if !self.pre_join_to(p1, false, &mut normal_ab, &mut unit_ab) {
508 self.line_to(p2, None);
509 return;
510 }
511
512 let mut quad_points = QuadConstruct::default();
513 self.init_quad(
514 StrokeType::Outer,
515 NormalizedF32::ZERO,
516 NormalizedF32::ONE,
517 &mut quad_points,
518 );
519 self.quad_stroke(&quad, &mut quad_points);
520 self.init_quad(
521 StrokeType::Inner,
522 NormalizedF32::ZERO,
523 NormalizedF32::ONE,
524 &mut quad_points,
525 );
526 self.quad_stroke(&quad, &mut quad_points);
527
528 let ok = set_normal_unit_normal(
529 quad[1],
530 quad[2],
531 self.res_scale,
532 self.radius,
533 &mut normal_bc,
534 &mut unit_bc,
535 );
536 if !ok {
537 normal_bc = normal_ab;
538 unit_bc = unit_ab;
539 }
540
541 self.post_join_to(p2, normal_bc, unit_bc);
542 }
543
544 fn cubic_to(&mut self, pt1: Point, pt2: Point, pt3: Point) {
545 let cubic = [self.prev_pt, pt1, pt2, pt3];
546 let mut reduction = [Point::zero(); 3];
547 let mut tangent_pt = Point::zero();
548 let reduction_type = check_cubic_linear(&cubic, &mut reduction, Some(&mut tangent_pt));
549 if reduction_type == ReductionType::Point {
550 self.line_to(pt3, None);
554 return;
555 }
556
557 if reduction_type == ReductionType::Line {
558 self.line_to(pt3, None);
559 return;
560 }
561
562 if ReductionType::Degenerate <= reduction_type
563 && ReductionType::Degenerate3 >= reduction_type
564 {
565 self.line_to(reduction[0], None);
566 let save_joiner = self.joiner;
567 self.joiner = round_joiner;
568 if ReductionType::Degenerate2 <= reduction_type {
569 self.line_to(reduction[1], None);
570 }
571
572 if ReductionType::Degenerate3 == reduction_type {
573 self.line_to(reduction[2], None);
574 }
575
576 self.line_to(pt3, None);
577 self.joiner = save_joiner;
578 return;
579 }
580
581 debug_assert_eq!(reduction_type, ReductionType::Quad);
582 let mut normal_ab = Point::zero();
583 let mut unit_ab = Point::zero();
584 let mut normal_cd = Point::zero();
585 let mut unit_cd = Point::zero();
586 if !self.pre_join_to(tangent_pt, false, &mut normal_ab, &mut unit_ab) {
587 self.line_to(pt3, None);
588 return;
589 }
590
591 let mut t_values = path_geometry::new_t_values();
592 let t_values = path_geometry::find_cubic_inflections(&cubic, &mut t_values);
593 let mut last_t = NormalizedF32::ZERO;
594 for index in 0..=t_values.len() {
595 let next_t = t_values
596 .get(index)
597 .cloned()
598 .map(|n| n.to_normalized())
599 .unwrap_or(NormalizedF32::ONE);
600
601 let mut quad_points = QuadConstruct::default();
602 self.init_quad(StrokeType::Outer, last_t, next_t, &mut quad_points);
603 self.cubic_stroke(&cubic, &mut quad_points);
604 self.init_quad(StrokeType::Inner, last_t, next_t, &mut quad_points);
605 self.cubic_stroke(&cubic, &mut quad_points);
606 last_t = next_t;
607 }
608
609 if let Some(cusp) = path_geometry::find_cubic_cusp(&cubic) {
610 let cusp_loc = path_geometry::eval_cubic_pos_at(&cubic, cusp.to_normalized());
611 self.cusper.push_circle(cusp_loc.x, cusp_loc.y, self.radius);
612 }
613
614 self.set_cubic_end_normal(&cubic, normal_ab, unit_ab, &mut normal_cd, &mut unit_cd);
617
618 self.post_join_to(pt3, normal_cd, unit_cd);
619 }
620
621 fn cubic_stroke(&mut self, cubic: &[Point; 4], quad_points: &mut QuadConstruct) -> bool {
622 if !self.found_tangents {
623 let result_type = self.tangents_meet(cubic, quad_points);
624 if result_type != ResultType::Quad {
625 let ok = points_within_dist(
626 quad_points.quad[0],
627 quad_points.quad[2],
628 self.inv_res_scale,
629 );
630 if (result_type == ResultType::Degenerate || ok)
631 && self.cubic_mid_on_line(cubic, quad_points)
632 {
633 self.add_degenerate_line(quad_points);
634 return true;
635 }
636 } else {
637 self.found_tangents = true;
638 }
639 }
640
641 if self.found_tangents {
642 let result_type = self.compare_quad_cubic(cubic, quad_points);
643 if result_type == ResultType::Quad {
644 let stroke = &quad_points.quad;
645 if self.stroke_type == StrokeType::Outer {
646 self.outer
647 .quad_to(stroke[1].x, stroke[1].y, stroke[2].x, stroke[2].y);
648 } else {
649 self.inner
650 .quad_to(stroke[1].x, stroke[1].y, stroke[2].x, stroke[2].y);
651 }
652
653 return true;
654 }
655
656 if result_type == ResultType::Degenerate {
657 if !quad_points.opposite_tangents {
658 self.add_degenerate_line(quad_points);
659 return true;
660 }
661 }
662 }
663
664 if !quad_points.quad[2].x.is_finite() || !quad_points.quad[2].x.is_finite() {
665 return false; }
667
668 self.recursion_depth += 1;
669 if self.recursion_depth > RECURSIVE_LIMITS[self.found_tangents as usize] {
670 return false; }
672
673 let mut half = QuadConstruct::default();
674 if !half.init_with_start(quad_points) {
675 self.add_degenerate_line(quad_points);
676 self.recursion_depth -= 1;
677 return true;
678 }
679
680 if !self.cubic_stroke(cubic, &mut half) {
681 return false;
682 }
683
684 if !half.init_with_end(quad_points) {
685 self.add_degenerate_line(quad_points);
686 self.recursion_depth -= 1;
687 return true;
688 }
689
690 if !self.cubic_stroke(cubic, &mut half) {
691 return false;
692 }
693
694 self.recursion_depth -= 1;
695 true
696 }
697
698 fn cubic_mid_on_line(&self, cubic: &[Point; 4], quad_points: &mut QuadConstruct) -> bool {
699 let mut stroke_mid = Point::zero();
700 self.cubic_quad_mid(cubic, quad_points, &mut stroke_mid);
701 let dist = pt_to_line(stroke_mid, quad_points.quad[0], quad_points.quad[2]);
702 dist < self.inv_res_scale_squared
703 }
704
705 fn cubic_quad_mid(&self, cubic: &[Point; 4], quad_points: &mut QuadConstruct, mid: &mut Point) {
706 let mut cubic_mid_pt = Point::zero();
707 self.cubic_perp_ray(cubic, quad_points.mid_t, &mut cubic_mid_pt, mid, None);
708 }
709
710 fn cubic_perp_ray(
713 &self,
714 cubic: &[Point; 4],
715 t: NormalizedF32,
716 t_pt: &mut Point,
717 on_pt: &mut Point,
718 tangent: Option<&mut Point>,
719 ) {
720 *t_pt = path_geometry::eval_cubic_pos_at(cubic, t);
721 let mut dxy = path_geometry::eval_cubic_tangent_at(cubic, t);
722
723 let mut chopped = [Point::zero(); 7];
724 if dxy.x == 0.0 && dxy.y == 0.0 {
725 let mut c_points: &[Point] = cubic;
726 if t.get().is_nearly_zero() {
727 dxy = cubic[2] - cubic[0];
728 } else if (1.0 - t.get()).is_nearly_zero() {
729 dxy = cubic[3] - cubic[1];
730 } else {
731 let t = NormalizedF32Exclusive::new(t.get()).unwrap();
736 path_geometry::chop_cubic_at2(cubic, t, &mut chopped);
737 dxy = chopped[3] - chopped[2];
738 if dxy.x == 0.0 && dxy.y == 0.0 {
739 dxy = chopped[3] - chopped[1];
740 c_points = &chopped;
741 }
742 }
743
744 if dxy.x == 0.0 && dxy.y == 0.0 {
745 dxy = c_points[3] - c_points[0];
746 }
747 }
748
749 self.set_ray_points(*t_pt, &mut dxy, on_pt, tangent);
750 }
751
752 fn set_cubic_end_normal(
753 &mut self,
754 cubic: &[Point; 4],
755 normal_ab: Point,
756 unit_normal_ab: Point,
757 normal_cd: &mut Point,
758 unit_normal_cd: &mut Point,
759 ) {
760 let mut ab = cubic[1] - cubic[0];
761 let mut cd = cubic[3] - cubic[2];
762
763 let mut degenerate_ab = degenerate_vector(ab);
764 let mut degenerate_cb = degenerate_vector(cd);
765
766 if degenerate_ab && degenerate_cb {
767 *normal_cd = normal_ab;
768 *unit_normal_cd = unit_normal_ab;
769 return;
770 }
771
772 if degenerate_ab {
773 ab = cubic[2] - cubic[0];
774 degenerate_ab = degenerate_vector(ab);
775 }
776
777 if degenerate_cb {
778 cd = cubic[3] - cubic[1];
779 degenerate_cb = degenerate_vector(cd);
780 }
781
782 if degenerate_ab || degenerate_cb {
783 *normal_cd = normal_ab;
784 *unit_normal_cd = unit_normal_ab;
785 return;
786 }
787
788 let res = set_normal_unit_normal2(cd, self.radius, normal_cd, unit_normal_cd);
789 debug_assert!(res);
790 }
791
792 fn compare_quad_cubic(
793 &self,
794 cubic: &[Point; 4],
795 quad_points: &mut QuadConstruct,
796 ) -> ResultType {
797 self.cubic_quad_ends(cubic, quad_points);
799 let result_type = self.intersect_ray(IntersectRayType::CtrlPt, quad_points);
800 if result_type != ResultType::Quad {
801 return result_type;
802 }
803
804 let mut ray0 = Point::zero();
807 let mut ray1 = Point::zero();
808 self.cubic_perp_ray(cubic, quad_points.mid_t, &mut ray1, &mut ray0, None);
809 self.stroke_close_enough(&quad_points.quad.clone(), &[ray0, ray1], quad_points)
810 }
811
812 fn cubic_quad_ends(&self, cubic: &[Point; 4], quad_points: &mut QuadConstruct) {
814 if !quad_points.start_set {
815 let mut cubic_start_pt = Point::zero();
816 self.cubic_perp_ray(
817 cubic,
818 quad_points.start_t,
819 &mut cubic_start_pt,
820 &mut quad_points.quad[0],
821 Some(&mut quad_points.tangent_start),
822 );
823 quad_points.start_set = true;
824 }
825
826 if !quad_points.end_set {
827 let mut cubic_end_pt = Point::zero();
828 self.cubic_perp_ray(
829 cubic,
830 quad_points.end_t,
831 &mut cubic_end_pt,
832 &mut quad_points.quad[2],
833 Some(&mut quad_points.tangent_end),
834 );
835 quad_points.end_set = true;
836 }
837 }
838
839 fn close(&mut self, is_line: bool) {
840 self.finish_contour(true, is_line);
841 }
842
843 fn finish_contour(&mut self, close: bool, curr_is_line: bool) {
844 if self.segment_count > 0 {
845 if close {
846 (self.joiner)(
847 self.prev_unit_normal,
848 self.prev_pt,
849 self.first_unit_normal,
850 self.radius,
851 self.inv_miter_limit,
852 self.prev_is_line,
853 curr_is_line,
854 self.builders(),
855 );
856 self.outer.close();
857
858 let pt = self.inner.last_point().unwrap_or_default();
860 self.outer.move_to(pt.x, pt.y);
861 self.outer.reverse_path_to(&self.inner);
862 self.outer.close();
863 } else {
864 let pt = self.inner.last_point().unwrap_or_default();
868 let other_path = if curr_is_line {
869 Some(&self.inner)
870 } else {
871 None
872 };
873 (self.capper)(
874 self.prev_pt,
875 self.prev_normal,
876 pt,
877 other_path,
878 &mut self.outer,
879 );
880 self.outer.reverse_path_to(&self.inner);
881
882 let other_path = if self.prev_is_line {
884 Some(&self.inner)
885 } else {
886 None
887 };
888 (self.capper)(
889 self.first_pt,
890 -self.first_normal,
891 self.first_outer_pt,
892 other_path,
893 &mut self.outer,
894 );
895 self.outer.close();
896 }
897
898 if !self.cusper.is_empty() {
899 self.outer.push_path_builder(&self.cusper);
900 self.cusper.clear();
901 }
902 }
903
904 self.inner.clear();
907 self.segment_count = -1;
908 self.first_outer_pt_index_in_contour = self.outer.points.len();
909 }
910
911 fn pre_join_to(
912 &mut self,
913 p: Point,
914 curr_is_line: bool,
915 normal: &mut Point,
916 unit_normal: &mut Point,
917 ) -> bool {
918 debug_assert!(self.segment_count >= 0);
919
920 let prev_x = self.prev_pt.x;
921 let prev_y = self.prev_pt.y;
922
923 let normal_set = set_normal_unit_normal(
924 self.prev_pt,
925 p,
926 self.res_scale,
927 self.radius,
928 normal,
929 unit_normal,
930 );
931 if !normal_set {
932 if fn_ptr_eq(self.capper, butt_capper) {
933 return false;
934 }
935
936 *normal = Point::from_xy(self.radius, 0.0);
940 *unit_normal = Point::from_xy(1.0, 0.0);
941 }
942
943 if self.segment_count == 0 {
944 self.first_normal = *normal;
945 self.first_unit_normal = *unit_normal;
946 self.first_outer_pt = Point::from_xy(prev_x + normal.x, prev_y + normal.y);
947
948 self.outer
949 .move_to(self.first_outer_pt.x, self.first_outer_pt.y);
950 self.inner.move_to(prev_x - normal.x, prev_y - normal.y);
951 } else {
952 (self.joiner)(
954 self.prev_unit_normal,
955 self.prev_pt,
956 *unit_normal,
957 self.radius,
958 self.inv_miter_limit,
959 self.prev_is_line,
960 curr_is_line,
961 self.builders(),
962 );
963 }
964 self.prev_is_line = curr_is_line;
965 true
966 }
967
968 fn post_join_to(&mut self, p: Point, normal: Point, unit_normal: Point) {
969 self.join_completed = true;
970 self.prev_pt = p;
971 self.prev_unit_normal = unit_normal;
972 self.prev_normal = normal;
973 self.segment_count += 1;
974 }
975
976 fn init_quad(
977 &mut self,
978 stroke_type: StrokeType,
979 start: NormalizedF32,
980 end: NormalizedF32,
981 quad_points: &mut QuadConstruct,
982 ) {
983 self.stroke_type = stroke_type;
984 self.found_tangents = false;
985 quad_points.init(start, end);
986 }
987
988 fn quad_stroke(&mut self, quad: &[Point; 3], quad_points: &mut QuadConstruct) -> bool {
989 let result_type = self.compare_quad_quad(quad, quad_points);
990 if result_type == ResultType::Quad {
991 let path = if self.stroke_type == StrokeType::Outer {
992 &mut self.outer
993 } else {
994 &mut self.inner
995 };
996
997 path.quad_to(
998 quad_points.quad[1].x,
999 quad_points.quad[1].y,
1000 quad_points.quad[2].x,
1001 quad_points.quad[2].y,
1002 );
1003
1004 return true;
1005 }
1006
1007 if result_type == ResultType::Degenerate {
1008 self.add_degenerate_line(quad_points);
1009 return true;
1010 }
1011
1012 self.recursion_depth += 1;
1013 if self.recursion_depth > RECURSIVE_LIMITS[QUAD_RECURSIVE_LIMIT] {
1014 return false; }
1016
1017 let mut half = QuadConstruct::default();
1018 half.init_with_start(quad_points);
1019 if !self.quad_stroke(quad, &mut half) {
1020 return false;
1021 }
1022
1023 half.init_with_end(quad_points);
1024 if !self.quad_stroke(quad, &mut half) {
1025 return false;
1026 }
1027
1028 self.recursion_depth -= 1;
1029 true
1030 }
1031
1032 fn compare_quad_quad(
1033 &mut self,
1034 quad: &[Point; 3],
1035 quad_points: &mut QuadConstruct,
1036 ) -> ResultType {
1037 if !quad_points.start_set {
1039 let mut quad_start_pt = Point::zero();
1040 self.quad_perp_ray(
1041 quad,
1042 quad_points.start_t,
1043 &mut quad_start_pt,
1044 &mut quad_points.quad[0],
1045 Some(&mut quad_points.tangent_start),
1046 );
1047 quad_points.start_set = true;
1048 }
1049
1050 if !quad_points.end_set {
1051 let mut quad_end_pt = Point::zero();
1052 self.quad_perp_ray(
1053 quad,
1054 quad_points.end_t,
1055 &mut quad_end_pt,
1056 &mut quad_points.quad[2],
1057 Some(&mut quad_points.tangent_end),
1058 );
1059 quad_points.end_set = true;
1060 }
1061
1062 let result_type = self.intersect_ray(IntersectRayType::CtrlPt, quad_points);
1063 if result_type != ResultType::Quad {
1064 return result_type;
1065 }
1066
1067 let mut ray0 = Point::zero();
1069 let mut ray1 = Point::zero();
1070 self.quad_perp_ray(quad, quad_points.mid_t, &mut ray1, &mut ray0, None);
1071 self.stroke_close_enough(&quad_points.quad.clone(), &[ray0, ray1], quad_points)
1072 }
1073
1074 fn set_ray_points(
1077 &self,
1078 tp: Point,
1079 dxy: &mut Point,
1080 on_p: &mut Point,
1081 mut tangent: Option<&mut Point>,
1082 ) {
1083 if !dxy.set_length(self.radius) {
1084 *dxy = Point::from_xy(self.radius, 0.0);
1085 }
1086
1087 let axis_flip = self.stroke_type as i32 as f32; on_p.x = tp.x + axis_flip * dxy.y;
1089 on_p.y = tp.y - axis_flip * dxy.x;
1090
1091 if let Some(ref mut tangent) = tangent {
1092 tangent.x = on_p.x + dxy.x;
1093 tangent.y = on_p.y + dxy.y;
1094 }
1095 }
1096
1097 fn quad_perp_ray(
1100 &self,
1101 quad: &[Point; 3],
1102 t: NormalizedF32,
1103 tp: &mut Point,
1104 on_p: &mut Point,
1105 tangent: Option<&mut Point>,
1106 ) {
1107 *tp = path_geometry::eval_quad_at(quad, t);
1108 let mut dxy = path_geometry::eval_quad_tangent_at(quad, t);
1109
1110 if dxy.is_zero() {
1111 dxy = quad[2] - quad[0];
1112 }
1113
1114 self.set_ray_points(*tp, &mut dxy, on_p, tangent);
1115 }
1116
1117 fn add_degenerate_line(&mut self, quad_points: &QuadConstruct) {
1118 if self.stroke_type == StrokeType::Outer {
1119 self.outer
1120 .line_to(quad_points.quad[2].x, quad_points.quad[2].y);
1121 } else {
1122 self.inner
1123 .line_to(quad_points.quad[2].x, quad_points.quad[2].y);
1124 }
1125 }
1126
1127 fn stroke_close_enough(
1128 &self,
1129 stroke: &[Point; 3],
1130 ray: &[Point; 2],
1131 quad_points: &mut QuadConstruct,
1132 ) -> ResultType {
1133 let half = NormalizedF32::new_clamped(0.5);
1134 let stroke_mid = path_geometry::eval_quad_at(stroke, half);
1135 if points_within_dist(ray[0], stroke_mid, self.inv_res_scale) {
1137 if sharp_angle(&quad_points.quad) {
1139 return ResultType::Split;
1140 }
1141
1142 return ResultType::Quad;
1143 }
1144
1145 if !pt_in_quad_bounds(stroke, ray[0], self.inv_res_scale) {
1148 return ResultType::Split;
1150 }
1151
1152 let mut roots = path_geometry::new_t_values();
1154 let roots = intersect_quad_ray(ray, stroke, &mut roots);
1155 if roots.len() != 1 {
1156 return ResultType::Split;
1157 }
1158
1159 let quad_pt = path_geometry::eval_quad_at(stroke, roots[0].to_normalized());
1160 let error = self.inv_res_scale * (1.0 - (roots[0].get() - 0.5).abs() * 2.0);
1161 if points_within_dist(ray[0], quad_pt, error) {
1162 if sharp_angle(&quad_points.quad) {
1164 return ResultType::Split;
1165 }
1166
1167 return ResultType::Quad;
1168 }
1169
1170 ResultType::Split
1172 }
1173
1174 fn intersect_ray(
1178 &self,
1179 intersect_ray_type: IntersectRayType,
1180 quad_points: &mut QuadConstruct,
1181 ) -> ResultType {
1182 let start = quad_points.quad[0];
1183 let end = quad_points.quad[2];
1184 let a_len = quad_points.tangent_start - start;
1185 let b_len = quad_points.tangent_end - end;
1186
1187 let denom = a_len.cross(b_len);
1193 if denom == 0.0 || !denom.is_finite() {
1194 quad_points.opposite_tangents = a_len.dot(b_len) < 0.0;
1195 return ResultType::Degenerate;
1196 }
1197
1198 quad_points.opposite_tangents = false;
1199 let ab0 = start - end;
1200 let mut numer_a = b_len.cross(ab0);
1201 let numer_b = a_len.cross(ab0);
1202 if (numer_a >= 0.0) == (numer_b >= 0.0) {
1203 let dist1 = pt_to_line(start, end, quad_points.tangent_end);
1208 let dist2 = pt_to_line(end, start, quad_points.tangent_start);
1209 if dist1.max(dist2) <= self.inv_res_scale_squared {
1210 return ResultType::Degenerate;
1211 }
1212
1213 return ResultType::Split;
1214 }
1215
1216 numer_a /= denom;
1219 let valid_divide = numer_a > numer_a - 1.0;
1220 if valid_divide {
1221 if intersect_ray_type == IntersectRayType::CtrlPt {
1222 quad_points.quad[1].x =
1225 start.x * (1.0 - numer_a) + quad_points.tangent_start.x * numer_a;
1226 quad_points.quad[1].y =
1227 start.y * (1.0 - numer_a) + quad_points.tangent_start.y * numer_a;
1228 }
1229
1230 return ResultType::Quad;
1231 }
1232
1233 quad_points.opposite_tangents = a_len.dot(b_len) < 0.0;
1234
1235 ResultType::Degenerate
1237 }
1238
1239 fn tangents_meet(&self, cubic: &[Point; 4], quad_points: &mut QuadConstruct) -> ResultType {
1241 self.cubic_quad_ends(cubic, quad_points);
1242 self.intersect_ray(IntersectRayType::ResultType, quad_points)
1243 }
1244
1245 fn finish(&mut self, is_line: bool) -> Option<Path> {
1246 self.finish_contour(false, is_line);
1247
1248 let mut buf = PathBuilder::new();
1250 core::mem::swap(&mut self.outer, &mut buf);
1251
1252 buf.finish()
1253 }
1254
1255 fn has_only_move_to(&self) -> bool {
1256 self.segment_count == 0
1257 }
1258
1259 fn is_current_contour_empty(&self) -> bool {
1260 self.inner.is_zero_length_since_point(0)
1261 && self
1262 .outer
1263 .is_zero_length_since_point(self.first_outer_pt_index_in_contour)
1264 }
1265}
1266
1267fn cap_factory(cap: LineCap) -> CapProc {
1268 match cap {
1269 LineCap::Butt => butt_capper,
1270 LineCap::Round => round_capper,
1271 LineCap::Square => square_capper,
1272 }
1273}
1274
1275fn butt_capper(_: Point, _: Point, stop: Point, _: Option<&PathBuilder>, path: &mut PathBuilder) {
1276 path.line_to(stop.x, stop.y);
1277}
1278
1279fn round_capper(
1280 pivot: Point,
1281 normal: Point,
1282 stop: Point,
1283 _: Option<&PathBuilder>,
1284 path: &mut PathBuilder,
1285) {
1286 let mut parallel = normal;
1287 parallel.rotate_cw();
1288
1289 let projected_center = pivot + parallel;
1290
1291 path.conic_points_to(
1292 projected_center + normal,
1293 projected_center,
1294 SCALAR_ROOT_2_OVER_2,
1295 );
1296 path.conic_points_to(projected_center - normal, stop, SCALAR_ROOT_2_OVER_2);
1297}
1298
1299fn square_capper(
1300 pivot: Point,
1301 normal: Point,
1302 stop: Point,
1303 other_path: Option<&PathBuilder>,
1304 path: &mut PathBuilder,
1305) {
1306 let mut parallel = normal;
1307 parallel.rotate_cw();
1308
1309 if other_path.is_some() {
1310 path.set_last_point(Point::from_xy(
1311 pivot.x + normal.x + parallel.x,
1312 pivot.y + normal.y + parallel.y,
1313 ));
1314 path.line_to(
1315 pivot.x - normal.x + parallel.x,
1316 pivot.y - normal.y + parallel.y,
1317 );
1318 } else {
1319 path.line_to(
1320 pivot.x + normal.x + parallel.x,
1321 pivot.y + normal.y + parallel.y,
1322 );
1323 path.line_to(
1324 pivot.x - normal.x + parallel.x,
1325 pivot.y - normal.y + parallel.y,
1326 );
1327 path.line_to(stop.x, stop.y);
1328 }
1329}
1330
1331fn join_factory(join: LineJoin) -> JoinProc {
1332 match join {
1333 LineJoin::Miter => miter_joiner,
1334 LineJoin::MiterClip => miter_clip_joiner,
1335 LineJoin::Round => round_joiner,
1336 LineJoin::Bevel => bevel_joiner,
1337 }
1338}
1339
1340fn is_clockwise(before: Point, after: Point) -> bool {
1341 before.x * after.y > before.y * after.x
1342}
1343
1344#[derive(Copy, Clone, PartialEq, Debug)]
1345enum AngleType {
1346 Nearly180,
1347 Sharp,
1348 Shallow,
1349 NearlyLine,
1350}
1351
1352fn dot_to_angle_type(dot: f32) -> AngleType {
1353 if dot >= 0.0 {
1354 if (1.0 - dot).is_nearly_zero() {
1356 AngleType::NearlyLine
1357 } else {
1358 AngleType::Shallow
1359 }
1360 } else {
1361 if (1.0 + dot).is_nearly_zero() {
1363 AngleType::Nearly180
1364 } else {
1365 AngleType::Sharp
1366 }
1367 }
1368}
1369
1370fn handle_inner_join(pivot: Point, after: Point, inner: &mut PathBuilder) {
1371 inner.line_to(pivot.x, pivot.y);
1377
1378 inner.line_to(pivot.x - after.x, pivot.y - after.y);
1379}
1380
1381fn bevel_joiner(
1382 before_unit_normal: Point,
1383 pivot: Point,
1384 after_unit_normal: Point,
1385 radius: f32,
1386 _: f32,
1387 _: bool,
1388 _: bool,
1389 mut builders: SwappableBuilders,
1390) {
1391 let mut after = after_unit_normal.scaled(radius);
1392
1393 if !is_clockwise(before_unit_normal, after_unit_normal) {
1394 builders.swap();
1395 after = -after;
1396 }
1397
1398 builders.outer.line_to(pivot.x + after.x, pivot.y + after.y);
1399 handle_inner_join(pivot, after, builders.inner);
1400}
1401
1402fn round_joiner(
1403 before_unit_normal: Point,
1404 pivot: Point,
1405 after_unit_normal: Point,
1406 radius: f32,
1407 _: f32,
1408 _: bool,
1409 _: bool,
1410 mut builders: SwappableBuilders,
1411) {
1412 let dot_prod = before_unit_normal.dot(after_unit_normal);
1413 let angle_type = dot_to_angle_type(dot_prod);
1414
1415 if angle_type == AngleType::NearlyLine {
1416 return;
1417 }
1418
1419 let mut before = before_unit_normal;
1420 let mut after = after_unit_normal;
1421 let mut dir = PathDirection::CW;
1422
1423 if !is_clockwise(before, after) {
1424 builders.swap();
1425 before = -before;
1426 after = -after;
1427 dir = PathDirection::CCW;
1428 }
1429
1430 let ts = Transform::from_row(radius, 0.0, 0.0, radius, pivot.x, pivot.y);
1431
1432 let mut conics = [path_geometry::Conic::default(); 5];
1433 let conics = path_geometry::Conic::build_unit_arc(before, after, dir, ts, &mut conics);
1434 if let Some(conics) = conics {
1435 for conic in conics {
1436 builders
1437 .outer
1438 .conic_points_to(conic.points[1], conic.points[2], conic.weight);
1439 }
1440
1441 after.scale(radius);
1442 handle_inner_join(pivot, after, builders.inner);
1443 }
1444}
1445
1446#[inline]
1447fn miter_joiner(
1448 before_unit_normal: Point,
1449 pivot: Point,
1450 after_unit_normal: Point,
1451 radius: f32,
1452 inv_miter_limit: f32,
1453 prev_is_line: bool,
1454 curr_is_line: bool,
1455 builders: SwappableBuilders,
1456) {
1457 miter_joiner_inner(
1458 before_unit_normal,
1459 pivot,
1460 after_unit_normal,
1461 radius,
1462 inv_miter_limit,
1463 false,
1464 prev_is_line,
1465 curr_is_line,
1466 builders,
1467 );
1468}
1469
1470#[inline]
1471fn miter_clip_joiner(
1472 before_unit_normal: Point,
1473 pivot: Point,
1474 after_unit_normal: Point,
1475 radius: f32,
1476 inv_miter_limit: f32,
1477 prev_is_line: bool,
1478 curr_is_line: bool,
1479 builders: SwappableBuilders,
1480) {
1481 miter_joiner_inner(
1482 before_unit_normal,
1483 pivot,
1484 after_unit_normal,
1485 radius,
1486 inv_miter_limit,
1487 true,
1488 prev_is_line,
1489 curr_is_line,
1490 builders,
1491 );
1492}
1493
1494fn miter_joiner_inner(
1495 before_unit_normal: Point,
1496 pivot: Point,
1497 after_unit_normal: Point,
1498 radius: f32,
1499 inv_miter_limit: f32,
1500 miter_clip: bool,
1501 prev_is_line: bool,
1502 mut curr_is_line: bool,
1503 mut builders: SwappableBuilders,
1504) {
1505 fn do_blunt_or_clipped(
1506 builders: SwappableBuilders,
1507 pivot: Point,
1508 radius: f32,
1509 prev_is_line: bool,
1510 curr_is_line: bool,
1511 mut before: Point,
1512 mut mid: Point,
1513 mut after: Point,
1514 inv_miter_limit: f32,
1515 miter_clip: bool,
1516 ) {
1517 after.scale(radius);
1518
1519 if miter_clip {
1520 mid.normalize();
1521
1522 let cos_beta = before.dot(mid);
1523 let sin_beta = before.cross(mid);
1524
1525 let x = if sin_beta.abs() <= SCALAR_NEARLY_ZERO {
1526 1.0 / inv_miter_limit
1527 } else {
1528 ((1.0 / inv_miter_limit) - cos_beta) / sin_beta
1529 };
1530
1531 before.scale(radius);
1532
1533 let mut before_tangent = before;
1534 before_tangent.rotate_cw();
1535
1536 let mut after_tangent = after;
1537 after_tangent.rotate_ccw();
1538
1539 let c1 = pivot + before + before_tangent.scaled(x);
1540 let c2 = pivot + after + after_tangent.scaled(x);
1541
1542 if prev_is_line {
1543 builders.outer.set_last_point(c1);
1544 } else {
1545 builders.outer.line_to(c1.x, c1.y);
1546 }
1547
1548 builders.outer.line_to(c2.x, c2.y);
1549 }
1550
1551 if !curr_is_line {
1552 builders.outer.line_to(pivot.x + after.x, pivot.y + after.y);
1553 }
1554
1555 handle_inner_join(pivot, after, builders.inner);
1556 }
1557
1558 fn do_miter(
1559 builders: SwappableBuilders,
1560 pivot: Point,
1561 radius: f32,
1562 prev_is_line: bool,
1563 curr_is_line: bool,
1564 mid: Point,
1565 mut after: Point,
1566 ) {
1567 after.scale(radius);
1568
1569 if prev_is_line {
1570 builders
1571 .outer
1572 .set_last_point(Point::from_xy(pivot.x + mid.x, pivot.y + mid.y));
1573 } else {
1574 builders.outer.line_to(pivot.x + mid.x, pivot.y + mid.y);
1575 }
1576
1577 if !curr_is_line {
1578 builders.outer.line_to(pivot.x + after.x, pivot.y + after.y);
1579 }
1580
1581 handle_inner_join(pivot, after, builders.inner);
1582 }
1583
1584 let dot_prod = before_unit_normal.dot(after_unit_normal);
1586 let angle_type = dot_to_angle_type(dot_prod);
1587 let mut before = before_unit_normal;
1588 let mut after = after_unit_normal;
1589 let mut mid;
1590
1591 if angle_type == AngleType::NearlyLine {
1592 return;
1593 }
1594
1595 if angle_type == AngleType::Nearly180 {
1596 curr_is_line = false;
1597 mid = (after - before).scaled(radius / 2.0);
1598 do_blunt_or_clipped(
1599 builders,
1600 pivot,
1601 radius,
1602 prev_is_line,
1603 curr_is_line,
1604 before,
1605 mid,
1606 after,
1607 inv_miter_limit,
1608 miter_clip,
1609 );
1610 return;
1611 }
1612
1613 let ccw = !is_clockwise(before, after);
1614 if ccw {
1615 builders.swap();
1616 before = -before;
1617 after = -after;
1618 }
1619
1620 if dot_prod == 0.0 && inv_miter_limit <= SCALAR_ROOT_2_OVER_2 {
1626 mid = (before + after).scaled(radius);
1627 do_miter(
1628 builders,
1629 pivot,
1630 radius,
1631 prev_is_line,
1632 curr_is_line,
1633 mid,
1634 after,
1635 );
1636 return;
1637 }
1638
1639 if angle_type == AngleType::Sharp {
1641 mid = Point::from_xy(after.y - before.y, before.x - after.x);
1642 if ccw {
1643 mid = -mid;
1644 }
1645 } else {
1646 mid = Point::from_xy(before.x + after.x, before.y + after.y);
1647 }
1648
1649 let sin_half_angle = (1.0 + dot_prod).half().sqrt();
1657 if sin_half_angle < inv_miter_limit {
1658 curr_is_line = false;
1659 do_blunt_or_clipped(
1660 builders,
1661 pivot,
1662 radius,
1663 prev_is_line,
1664 curr_is_line,
1665 before,
1666 mid,
1667 after,
1668 inv_miter_limit,
1669 miter_clip,
1670 );
1671 return;
1672 }
1673
1674 mid.set_length(radius / sin_half_angle);
1675 do_miter(
1676 builders,
1677 pivot,
1678 radius,
1679 prev_is_line,
1680 curr_is_line,
1681 mid,
1682 after,
1683 );
1684}
1685
1686fn set_normal_unit_normal(
1687 before: Point,
1688 after: Point,
1689 scale: f32,
1690 radius: f32,
1691 normal: &mut Point,
1692 unit_normal: &mut Point,
1693) -> bool {
1694 if !unit_normal.set_normalize((after.x - before.x) * scale, (after.y - before.y) * scale) {
1695 return false;
1696 }
1697
1698 unit_normal.rotate_ccw();
1699 *normal = unit_normal.scaled(radius);
1700 true
1701}
1702
1703fn set_normal_unit_normal2(
1704 vec: Point,
1705 radius: f32,
1706 normal: &mut Point,
1707 unit_normal: &mut Point,
1708) -> bool {
1709 if !unit_normal.set_normalize(vec.x, vec.y) {
1710 return false;
1711 }
1712
1713 unit_normal.rotate_ccw();
1714 *normal = unit_normal.scaled(radius);
1715 true
1716}
1717
1718fn fn_ptr_eq(f1: CapProc, f2: CapProc) -> bool {
1719 core::ptr::eq(f1 as *const (), f2 as *const ())
1720}
1721
1722#[derive(Debug)]
1723struct QuadConstruct {
1724 quad: [Point; 3], tangent_start: Point, tangent_end: Point, start_t: NormalizedF32, mid_t: NormalizedF32,
1730 end_t: NormalizedF32,
1731 start_set: bool, end_set: bool,
1733 opposite_tangents: bool, }
1735
1736impl Default for QuadConstruct {
1737 fn default() -> Self {
1738 Self {
1739 quad: Default::default(),
1740 tangent_start: Point::default(),
1741 tangent_end: Point::default(),
1742 start_t: NormalizedF32::ZERO,
1743 mid_t: NormalizedF32::ZERO,
1744 end_t: NormalizedF32::ZERO,
1745 start_set: false,
1746 end_set: false,
1747 opposite_tangents: false,
1748 }
1749 }
1750}
1751
1752impl QuadConstruct {
1753 fn init(&mut self, start: NormalizedF32, end: NormalizedF32) -> bool {
1755 self.start_t = start;
1756 self.mid_t = NormalizedF32::new_clamped((start.get() + end.get()).half());
1757 self.end_t = end;
1758 self.start_set = false;
1759 self.end_set = false;
1760 self.start_t < self.mid_t && self.mid_t < self.end_t
1761 }
1762
1763 fn init_with_start(&mut self, parent: &Self) -> bool {
1764 if !self.init(parent.start_t, parent.mid_t) {
1765 return false;
1766 }
1767
1768 self.quad[0] = parent.quad[0];
1769 self.tangent_start = parent.tangent_start;
1770 self.start_set = true;
1771 true
1772 }
1773
1774 fn init_with_end(&mut self, parent: &Self) -> bool {
1775 if !self.init(parent.mid_t, parent.end_t) {
1776 return false;
1777 }
1778
1779 self.quad[2] = parent.quad[2];
1780 self.tangent_end = parent.tangent_end;
1781 self.end_set = true;
1782 true
1783 }
1784}
1785
1786fn check_quad_linear(quad: &[Point; 3]) -> (Point, ReductionType) {
1787 let degenerate_ab = degenerate_vector(quad[1] - quad[0]);
1788 let degenerate_bc = degenerate_vector(quad[2] - quad[1]);
1789 if degenerate_ab & degenerate_bc {
1790 return (Point::zero(), ReductionType::Point);
1791 }
1792
1793 if degenerate_ab | degenerate_bc {
1794 return (Point::zero(), ReductionType::Line);
1795 }
1796
1797 if !quad_in_line(quad) {
1798 return (Point::zero(), ReductionType::Quad);
1799 }
1800
1801 let t = path_geometry::find_quad_max_curvature(quad);
1802 if t == NormalizedF32::ZERO || t == NormalizedF32::ONE {
1803 return (Point::zero(), ReductionType::Line);
1804 }
1805
1806 (
1807 path_geometry::eval_quad_at(quad, t),
1808 ReductionType::Degenerate,
1809 )
1810}
1811
1812fn degenerate_vector(v: Point) -> bool {
1813 !v.can_normalize()
1814}
1815
1816fn quad_in_line(quad: &[Point; 3]) -> bool {
1823 let mut pt_max = -1.0;
1824 let mut outer1 = 0;
1825 let mut outer2 = 0;
1826 for index in 0..2 {
1827 for inner in index + 1..3 {
1828 let test_diff = quad[inner] - quad[index];
1829 let test_max = test_diff.x.abs().max(test_diff.y.abs());
1830 if pt_max < test_max {
1831 outer1 = index;
1832 outer2 = inner;
1833 pt_max = test_max;
1834 }
1835 }
1836 }
1837
1838 debug_assert!(outer1 <= 1);
1839 debug_assert!(outer2 >= 1 && outer2 <= 2);
1840 debug_assert!(outer1 < outer2);
1841
1842 let mid = outer1 ^ outer2 ^ 3;
1843 const CURVATURE_SLOP: f32 = 0.000005; let line_slop = pt_max * pt_max * CURVATURE_SLOP;
1845 pt_to_line(quad[mid], quad[outer1], quad[outer2]) <= line_slop
1846}
1847
1848fn pt_to_line(pt: Point, line_start: Point, line_end: Point) -> f32 {
1850 let dxy = line_end - line_start;
1851 let ab0 = pt - line_start;
1852 let numer = dxy.dot(ab0);
1853 let denom = dxy.dot(dxy);
1854 let t = numer / denom;
1855 if t >= 0.0 && t <= 1.0 {
1856 let hit = Point::from_xy(
1857 line_start.x * (1.0 - t) + line_end.x * t,
1858 line_start.y * (1.0 - t) + line_end.y * t,
1859 );
1860 hit.distance_to_sqd(pt)
1861 } else {
1862 pt.distance_to_sqd(line_start)
1863 }
1864}
1865
1866fn intersect_quad_ray<'a>(
1868 line: &[Point; 2],
1869 quad: &[Point; 3],
1870 roots: &'a mut [NormalizedF32Exclusive; 3],
1871) -> &'a [NormalizedF32Exclusive] {
1872 let vec = line[1] - line[0];
1873 let mut r = [0.0; 3];
1874 for n in 0..3 {
1875 r[n] = (quad[n].y - line[0].y) * vec.x - (quad[n].x - line[0].x) * vec.y;
1876 }
1877 let mut a = r[2];
1878 let mut b = r[1];
1879 let c = r[0];
1880 a += c - 2.0 * b; b -= c; let len = path_geometry::find_unit_quad_roots(a, 2.0 * b, c, roots);
1884 &roots[0..len]
1885}
1886
1887fn points_within_dist(near_pt: Point, far_pt: Point, limit: f32) -> bool {
1888 near_pt.distance_to_sqd(far_pt) <= limit * limit
1889}
1890
1891fn sharp_angle(quad: &[Point; 3]) -> bool {
1892 let mut smaller = quad[1] - quad[0];
1893 let mut larger = quad[1] - quad[2];
1894 let smaller_len = smaller.length_sqd();
1895 let mut larger_len = larger.length_sqd();
1896 if smaller_len > larger_len {
1897 core::mem::swap(&mut smaller, &mut larger);
1898 larger_len = smaller_len;
1899 }
1900
1901 if !smaller.set_length(larger_len) {
1902 return false;
1903 }
1904
1905 let dot = smaller.dot(larger);
1906 dot > 0.0
1907}
1908
1909fn pt_in_quad_bounds(quad: &[Point; 3], pt: Point, inv_res_scale: f32) -> bool {
1911 let x_min = quad[0].x.min(quad[1].x).min(quad[2].x);
1912 if pt.x + inv_res_scale < x_min {
1913 return false;
1914 }
1915
1916 let x_max = quad[0].x.max(quad[1].x).max(quad[2].x);
1917 if pt.x - inv_res_scale > x_max {
1918 return false;
1919 }
1920
1921 let y_min = quad[0].y.min(quad[1].y).min(quad[2].y);
1922 if pt.y + inv_res_scale < y_min {
1923 return false;
1924 }
1925
1926 let y_max = quad[0].y.max(quad[1].y).max(quad[2].y);
1927 if pt.y - inv_res_scale > y_max {
1928 return false;
1929 }
1930
1931 true
1932}
1933
1934fn check_cubic_linear(
1935 cubic: &[Point; 4],
1936 reduction: &mut [Point; 3],
1937 tangent_pt: Option<&mut Point>,
1938) -> ReductionType {
1939 let degenerate_ab = degenerate_vector(cubic[1] - cubic[0]);
1940 let degenerate_bc = degenerate_vector(cubic[2] - cubic[1]);
1941 let degenerate_cd = degenerate_vector(cubic[3] - cubic[2]);
1942 if degenerate_ab & degenerate_bc & degenerate_cd {
1943 return ReductionType::Point;
1944 }
1945
1946 if degenerate_ab as i32 + degenerate_bc as i32 + degenerate_cd as i32 == 2 {
1947 return ReductionType::Line;
1948 }
1949
1950 if !cubic_in_line(cubic) {
1951 if let Some(tangent_pt) = tangent_pt {
1952 *tangent_pt = if degenerate_ab { cubic[2] } else { cubic[1] };
1953 }
1954
1955 return ReductionType::Quad;
1956 }
1957
1958 let mut t_values = [NormalizedF32::ZERO; 3];
1959 let t_values = path_geometry::find_cubic_max_curvature(cubic, &mut t_values);
1960 let mut r_count = 0;
1961 for t in t_values {
1963 if 0.0 >= t.get() || t.get() >= 1.0 {
1964 continue;
1965 }
1966
1967 reduction[r_count] = path_geometry::eval_cubic_pos_at(cubic, *t);
1968 if reduction[r_count] != cubic[0] && reduction[r_count] != cubic[3] {
1969 r_count += 1;
1970 }
1971 }
1972
1973 match r_count {
1974 0 => ReductionType::Line,
1975 1 => ReductionType::Degenerate,
1976 2 => ReductionType::Degenerate2,
1977 3 => ReductionType::Degenerate3,
1978 _ => unreachable!(),
1979 }
1980}
1981
1982fn cubic_in_line(cubic: &[Point; 4]) -> bool {
2010 let mut pt_max = -1.0;
2011 let mut outer1 = 0;
2012 let mut outer2 = 0;
2013 for index in 0..3 {
2014 for inner in index + 1..4 {
2015 let test_diff = cubic[inner] - cubic[index];
2016 let test_max = test_diff.x.abs().max(test_diff.y.abs());
2017 if pt_max < test_max {
2018 outer1 = index;
2019 outer2 = inner;
2020 pt_max = test_max;
2021 }
2022 }
2023 }
2024 debug_assert!(outer1 <= 2);
2025 debug_assert!(outer2 >= 1 && outer2 <= 3);
2026 debug_assert!(outer1 < outer2);
2027 let mid1 = (1 + (2 >> outer2)) >> outer1;
2028 debug_assert!(mid1 <= 2);
2029 debug_assert!(outer1 != mid1 && outer2 != mid1);
2030 let mid2 = outer1 ^ outer2 ^ mid1;
2031 debug_assert!(mid2 >= 1 && mid2 <= 3);
2032 debug_assert!(mid2 != outer1 && mid2 != outer2 && mid2 != mid1);
2033 debug_assert!(((1 << outer1) | (1 << outer2) | (1 << mid1) | (1 << mid2)) == 0x0f);
2034 let line_slop = pt_max * pt_max * 0.00001; pt_to_line(cubic[mid1], cubic[outer1], cubic[outer2]) <= line_slop
2037 && pt_to_line(cubic[mid2], cubic[outer1], cubic[outer2]) <= line_slop
2038}
2039
2040#[rustfmt::skip]
2041#[cfg(test)]
2042mod tests {
2043 use super::*;
2044
2045 impl PathSegment {
2046 fn new_move_to(x: f32, y: f32) -> Self {
2047 PathSegment::MoveTo(Point::from_xy(x, y))
2048 }
2049
2050 fn new_line_to(x: f32, y: f32) -> Self {
2051 PathSegment::LineTo(Point::from_xy(x, y))
2052 }
2053
2054 fn new_close() -> Self {
2063 PathSegment::Close
2064 }
2065 }
2066
2067 #[test]
2069 fn auto_close() {
2070 let mut pb = PathBuilder::new();
2072 pb.move_to(10.0, 10.0);
2073 pb.line_to(20.0, 50.0);
2074 pb.line_to(30.0, 10.0);
2075 pb.close();
2076 let path = pb.finish().unwrap();
2077
2078 let stroke = Stroke::default();
2079 let stroke_path = PathStroker::new().stroke(&path, &stroke, 1.0).unwrap();
2080
2081 let mut iter = stroke_path.segments();
2082 iter.set_auto_close(true);
2083
2084 assert_eq!(iter.next().unwrap(), PathSegment::new_move_to(10.485071, 9.878732));
2085 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(20.485071, 49.878731));
2086 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(20.0, 50.0));
2087 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(19.514929, 49.878731));
2088 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(29.514929, 9.878732));
2089 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(30.0, 10.0));
2090 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(30.0, 10.5));
2091 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(10.0, 10.5));
2092 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(10.0, 10.0));
2093 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(10.485071, 9.878732));
2094 assert_eq!(iter.next().unwrap(), PathSegment::new_close());
2095 assert_eq!(iter.next().unwrap(), PathSegment::new_move_to(9.3596115, 9.5));
2096 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(30.640388, 9.5));
2097 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(20.485071, 50.121269));
2098 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(19.514929, 50.121269));
2099 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(9.514929, 10.121268));
2100 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(9.3596115, 9.5));
2101 assert_eq!(iter.next().unwrap(), PathSegment::new_close());
2102 }
2103
2104 #[test]
2106 fn cubic_1() {
2107 let mut pb = PathBuilder::new();
2108 pb.move_to(51.0161362, 1511.52478);
2109 pb.cubic_to(
2110 51.0161362, 1511.52478,
2111 51.0161362, 1511.52478,
2112 51.0161362, 1511.52478,
2113 );
2114 let path = pb.finish().unwrap();
2115
2116 let mut stroke = Stroke::default();
2117 stroke.width = 0.394537568;
2118
2119 assert!(PathStroker::new().stroke(&path, &stroke, 1.0).is_none());
2120 }
2121
2122 #[test]
2124 fn cubic_2() {
2125 let mut pb = PathBuilder::new();
2126 pb.move_to(f32::from_bits(0x424c1086), f32::from_bits(0x44bcf0cb)); pb.cubic_to(
2128 f32::from_bits(0x424c107c), f32::from_bits(0x44bcf0cb), f32::from_bits(0x424c10c2), f32::from_bits(0x44bcf0cb), f32::from_bits(0x424c1119), f32::from_bits(0x44bcf0ca), );
2132 let path = pb.finish().unwrap();
2133
2134 let mut stroke = Stroke::default();
2135 stroke.width = 0.394537568;
2136
2137 assert!(PathStroker::new().stroke(&path, &stroke, 1.0).is_some());
2138 }
2139
2140 #[test]
2143 fn big() {
2144 let mut pb = PathBuilder::new();
2147 pb.move_to(f32::from_bits(0x46380000), f32::from_bits(0xc6380000)); pb.line_to(f32::from_bits(0x46a00000), f32::from_bits(0xc6a00000)); pb.line_to(f32::from_bits(0x468c0000), f32::from_bits(0xc68c0000)); pb.line_to(f32::from_bits(0x46100000), f32::from_bits(0xc6100000)); pb.line_to(f32::from_bits(0x46380000), f32::from_bits(0xc6380000)); pb.close();
2153 let path = pb.finish().unwrap();
2154
2155 let mut stroke = Stroke::default();
2156 stroke.width = 1.49679073e+10;
2157
2158 assert!(PathStroker::new().stroke(&path, &stroke, 1.0).is_some());
2159 }
2160
2161 #[test]
2163 fn quad_stroker_one_off() {
2164 let mut pb = PathBuilder::new();
2165 pb.move_to(f32::from_bits(0x43c99223), f32::from_bits(0x42b7417e));
2166 pb.quad_to(
2167 f32::from_bits(0x4285d839), f32::from_bits(0x43ed6645),
2168 f32::from_bits(0x43c941c8), f32::from_bits(0x42b3ace3),
2169 );
2170 let path = pb.finish().unwrap();
2171
2172 let mut stroke = Stroke::default();
2173 stroke.width = 164.683548;
2174
2175 assert!(PathStroker::new().stroke(&path, &stroke, 1.0).is_some());
2176 }
2177
2178 #[test]
2180 fn cubic_stroker_one_off() {
2181 let mut pb = PathBuilder::new();
2182 pb.move_to(f32::from_bits(0x433f5370), f32::from_bits(0x43d1f4b3));
2183 pb.cubic_to(
2184 f32::from_bits(0x4331cb76), f32::from_bits(0x43ea3340),
2185 f32::from_bits(0x4388f498), f32::from_bits(0x42f7f08d),
2186 f32::from_bits(0x43f1cd32), f32::from_bits(0x42802ec1),
2187 );
2188 let path = pb.finish().unwrap();
2189
2190 let mut stroke = Stroke::default();
2191 stroke.width = 42.835968;
2192
2193 assert!(PathStroker::new().stroke(&path, &stroke, 1.0).is_some());
2194 }
2195}