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<'a> SwappableBuilders<'a> {
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, PartialEq, Debug)]
84pub enum LineCap {
85 Butt,
87 Round,
89 Square,
91}
92
93impl Default for LineCap {
94 fn default() -> Self {
95 LineCap::Butt
96 }
97}
98
99#[derive(Copy, Clone, PartialEq, Debug)]
112pub enum LineJoin {
113 Miter,
115 Round,
117 Bevel,
119}
120
121impl Default for LineJoin {
122 fn default() -> Self {
123 LineJoin::Miter
124 }
125}
126
127const QUAD_RECURSIVE_LIMIT: usize = 3;
128
129const RECURSIVE_LIMITS: [i32; 4] = [5 * 3, 26 * 3, 11 * 3, 11 * 3]; type CapProc = fn(
135 pivot: Point,
136 normal: Point,
137 stop: Point,
138 other_path: Option<&PathBuilder>,
139 path: &mut PathBuilder,
140);
141
142type JoinProc = fn(
143 before_unit_normal: Point,
144 pivot: Point,
145 after_unit_normal: Point,
146 radius: f32,
147 inv_miter_limit: f32,
148 prev_is_line: bool,
149 curr_is_line: bool,
150 builders: SwappableBuilders,
151);
152
153#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
154enum ReductionType {
155 Point, Line, Quad, Degenerate, Degenerate2, Degenerate3, }
162
163#[derive(Copy, Clone, PartialEq, Debug)]
164enum StrokeType {
165 Outer = 1, Inner = -1,
167}
168
169#[derive(Copy, Clone, PartialEq, Debug)]
170enum ResultType {
171 Split, Degenerate, Quad, }
175
176#[derive(Copy, Clone, PartialEq, Debug)]
177enum IntersectRayType {
178 CtrlPt,
179 ResultType,
180}
181
182impl Path {
183 pub fn stroke(&self, stroke: &Stroke, resolution_scale: f32) -> Option<Path> {
192 PathStroker::new().stroke(self, stroke, resolution_scale)
193 }
194}
195
196#[allow(missing_debug_implementations)]
198#[derive(Clone)]
199pub struct PathStroker {
200 radius: f32,
201 inv_miter_limit: f32,
202 res_scale: f32,
203 inv_res_scale: f32,
204 inv_res_scale_squared: f32,
205
206 first_normal: Point,
207 prev_normal: Point,
208 first_unit_normal: Point,
209 prev_unit_normal: Point,
210
211 first_pt: Point,
213 prev_pt: Point,
214
215 first_outer_pt: Point,
216 first_outer_pt_index_in_contour: usize,
217 segment_count: i32,
218 prev_is_line: bool,
219
220 capper: CapProc,
221 joiner: JoinProc,
222
223 inner: PathBuilder,
225 outer: PathBuilder,
226 cusper: PathBuilder,
227
228 stroke_type: StrokeType,
229
230 recursion_depth: i32, found_tangents: bool, join_completed: bool, }
234
235impl Default for PathStroker {
236 fn default() -> Self {
237 PathStroker::new()
238 }
239}
240
241impl PathStroker {
242 pub fn new() -> Self {
244 PathStroker {
245 radius: 0.0,
246 inv_miter_limit: 0.0,
247 res_scale: 1.0,
248 inv_res_scale: 1.0,
249 inv_res_scale_squared: 1.0,
250
251 first_normal: Point::zero(),
252 prev_normal: Point::zero(),
253 first_unit_normal: Point::zero(),
254 prev_unit_normal: Point::zero(),
255
256 first_pt: Point::zero(),
257 prev_pt: Point::zero(),
258
259 first_outer_pt: Point::zero(),
260 first_outer_pt_index_in_contour: 0,
261 segment_count: -1,
262 prev_is_line: false,
263
264 capper: butt_capper,
265 joiner: miter_joiner,
266
267 inner: PathBuilder::new(),
268 outer: PathBuilder::new(),
269 cusper: PathBuilder::new(),
270
271 stroke_type: StrokeType::Outer,
272
273 recursion_depth: 0,
274 found_tangents: false,
275 join_completed: false,
276 }
277 }
278
279 pub fn compute_resolution_scale(ts: &Transform) -> f32 {
289 let sx = Point::from_xy(ts.sx, ts.kx).length();
290 let sy = Point::from_xy(ts.ky, ts.sy).length();
291 if sx.is_finite() && sy.is_finite() {
292 let scale = sx.max(sy);
293 if scale > 0.0 {
294 return scale;
295 }
296 }
297
298 1.0
299 }
300
301 pub fn stroke(&mut self, path: &Path, stroke: &Stroke, resolution_scale: f32) -> Option<Path> {
308 let width = NonZeroPositiveF32::new(stroke.width)?;
309 self.stroke_inner(
310 path,
311 width,
312 stroke.miter_limit,
313 stroke.line_cap,
314 stroke.line_join,
315 resolution_scale,
316 )
317 }
318
319 fn stroke_inner(
320 &mut self,
321 path: &Path,
322 width: NonZeroPositiveF32,
323 miter_limit: f32,
324 line_cap: LineCap,
325 mut line_join: LineJoin,
326 res_scale: f32,
327 ) -> Option<Path> {
328 let mut inv_miter_limit = 0.0;
331
332 if line_join == LineJoin::Miter {
333 if miter_limit <= 1.0 {
334 line_join = LineJoin::Bevel;
335 } else {
336 inv_miter_limit = miter_limit.invert();
337 }
338 }
339
340 self.res_scale = res_scale;
341 self.inv_res_scale = (res_scale * 4.0).invert();
343 self.inv_res_scale_squared = self.inv_res_scale.sqr();
344
345 self.radius = width.get().half();
346 self.inv_miter_limit = inv_miter_limit;
347
348 self.first_normal = Point::zero();
349 self.prev_normal = Point::zero();
350 self.first_unit_normal = Point::zero();
351 self.prev_unit_normal = Point::zero();
352
353 self.first_pt = Point::zero();
354 self.prev_pt = Point::zero();
355
356 self.first_outer_pt = Point::zero();
357 self.first_outer_pt_index_in_contour = 0;
358 self.segment_count = -1;
359 self.prev_is_line = false;
360
361 self.capper = cap_factory(line_cap);
362 self.joiner = join_factory(line_join);
363
364 self.inner.clear();
370 self.inner.reserve(path.verbs.len(), path.points.len());
371
372 self.outer.clear();
374 self.outer
375 .reserve(path.verbs.len() * 3, path.points.len() * 3);
376
377 self.cusper.clear();
378
379 self.stroke_type = StrokeType::Outer;
380
381 self.recursion_depth = 0;
382 self.found_tangents = false;
383 self.join_completed = false;
384
385 let mut last_segment_is_line = false;
386 let mut iter = path.segments();
387 iter.set_auto_close(true);
388 while let Some(segment) = iter.next() {
389 match segment {
390 PathSegment::MoveTo(p) => {
391 self.move_to(p);
392 }
393 PathSegment::LineTo(p) => {
394 self.line_to(p, Some(&iter));
395 last_segment_is_line = true;
396 }
397 PathSegment::QuadTo(p1, p2) => {
398 self.quad_to(p1, p2);
399 last_segment_is_line = false;
400 }
401 PathSegment::CubicTo(p1, p2, p3) => {
402 self.cubic_to(p1, p2, p3);
403 last_segment_is_line = false;
404 }
405 PathSegment::Close => {
406 if line_cap != LineCap::Butt {
407 if self.has_only_move_to() {
411 self.line_to(self.move_to_pt(), None);
412 last_segment_is_line = true;
413 continue;
414 }
415
416 if self.is_current_contour_empty() {
420 last_segment_is_line = true;
421 continue;
422 }
423 }
424
425 self.close(last_segment_is_line);
426 }
427 }
428 }
429
430 self.finish(last_segment_is_line)
431 }
432
433 fn builders(&mut self) -> SwappableBuilders {
434 SwappableBuilders {
435 inner: &mut self.inner,
436 outer: &mut self.outer,
437 }
438 }
439
440 fn move_to_pt(&self) -> Point {
441 self.first_pt
442 }
443
444 fn move_to(&mut self, p: Point) {
445 if self.segment_count > 0 {
446 self.finish_contour(false, false);
447 }
448
449 self.segment_count = 0;
450 self.first_pt = p;
451 self.prev_pt = p;
452 self.join_completed = false;
453 }
454
455 fn line_to(&mut self, p: Point, iter: Option<&PathSegmentsIter>) {
456 let teeny_line = self
457 .prev_pt
458 .equals_within_tolerance(p, SCALAR_NEARLY_ZERO * self.inv_res_scale);
459 if fn_ptr_eq(self.capper, butt_capper) && teeny_line {
460 return;
461 }
462
463 if teeny_line && (self.join_completed || iter.map(|i| i.has_valid_tangent()) == Some(true))
464 {
465 return;
466 }
467
468 let mut normal = Point::zero();
469 let mut unit_normal = Point::zero();
470 if !self.pre_join_to(p, true, &mut normal, &mut unit_normal) {
471 return;
472 }
473
474 self.outer.line_to(p.x + normal.x, p.y + normal.y);
475 self.inner.line_to(p.x - normal.x, p.y - normal.y);
476
477 self.post_join_to(p, normal, unit_normal);
478 }
479
480 fn quad_to(&mut self, p1: Point, p2: Point) {
481 let quad = [self.prev_pt, p1, p2];
482 let (reduction, reduction_type) = check_quad_linear(&quad);
483 if reduction_type == ReductionType::Point {
484 self.line_to(p2, None);
488 return;
489 }
490
491 if reduction_type == ReductionType::Line {
492 self.line_to(p2, None);
493 return;
494 }
495
496 if reduction_type == ReductionType::Degenerate {
497 self.line_to(reduction, None);
498 let save_joiner = self.joiner;
499 self.joiner = round_joiner;
500 self.line_to(p2, None);
501 self.joiner = save_joiner;
502 return;
503 }
504
505 debug_assert_eq!(reduction_type, ReductionType::Quad);
506
507 let mut normal_ab = Point::zero();
508 let mut unit_ab = Point::zero();
509 let mut normal_bc = Point::zero();
510 let mut unit_bc = Point::zero();
511 if !self.pre_join_to(p1, false, &mut normal_ab, &mut unit_ab) {
512 self.line_to(p2, None);
513 return;
514 }
515
516 let mut quad_points = QuadConstruct::default();
517 self.init_quad(
518 StrokeType::Outer,
519 NormalizedF32::ZERO,
520 NormalizedF32::ONE,
521 &mut quad_points,
522 );
523 self.quad_stroke(&quad, &mut quad_points);
524 self.init_quad(
525 StrokeType::Inner,
526 NormalizedF32::ZERO,
527 NormalizedF32::ONE,
528 &mut quad_points,
529 );
530 self.quad_stroke(&quad, &mut quad_points);
531
532 let ok = set_normal_unit_normal(
533 quad[1],
534 quad[2],
535 self.res_scale,
536 self.radius,
537 &mut normal_bc,
538 &mut unit_bc,
539 );
540 if !ok {
541 normal_bc = normal_ab;
542 unit_bc = unit_ab;
543 }
544
545 self.post_join_to(p2, normal_bc, unit_bc);
546 }
547
548 fn cubic_to(&mut self, pt1: Point, pt2: Point, pt3: Point) {
549 let cubic = [self.prev_pt, pt1, pt2, pt3];
550 let mut reduction = [Point::zero(); 3];
551 let mut tangent_pt = Point::zero();
552 let reduction_type = check_cubic_linear(&cubic, &mut reduction, Some(&mut tangent_pt));
553 if reduction_type == ReductionType::Point {
554 self.line_to(pt3, None);
558 return;
559 }
560
561 if reduction_type == ReductionType::Line {
562 self.line_to(pt3, None);
563 return;
564 }
565
566 if ReductionType::Degenerate <= reduction_type
567 && ReductionType::Degenerate3 >= reduction_type
568 {
569 self.line_to(reduction[0], None);
570 let save_joiner = self.joiner;
571 self.joiner = round_joiner;
572 if ReductionType::Degenerate2 <= reduction_type {
573 self.line_to(reduction[1], None);
574 }
575
576 if ReductionType::Degenerate3 == reduction_type {
577 self.line_to(reduction[2], None);
578 }
579
580 self.line_to(pt3, None);
581 self.joiner = save_joiner;
582 return;
583 }
584
585 debug_assert_eq!(reduction_type, ReductionType::Quad);
586 let mut normal_ab = Point::zero();
587 let mut unit_ab = Point::zero();
588 let mut normal_cd = Point::zero();
589 let mut unit_cd = Point::zero();
590 if !self.pre_join_to(tangent_pt, false, &mut normal_ab, &mut unit_ab) {
591 self.line_to(pt3, None);
592 return;
593 }
594
595 let mut t_values = path_geometry::new_t_values();
596 let t_values = path_geometry::find_cubic_inflections(&cubic, &mut t_values);
597 let mut last_t = NormalizedF32::ZERO;
598 for index in 0..=t_values.len() {
599 let next_t = t_values
600 .get(index)
601 .cloned()
602 .map(|n| n.to_normalized())
603 .unwrap_or(NormalizedF32::ONE);
604
605 let mut quad_points = QuadConstruct::default();
606 self.init_quad(StrokeType::Outer, last_t, next_t, &mut quad_points);
607 self.cubic_stroke(&cubic, &mut quad_points);
608 self.init_quad(StrokeType::Inner, last_t, next_t, &mut quad_points);
609 self.cubic_stroke(&cubic, &mut quad_points);
610 last_t = next_t;
611 }
612
613 if let Some(cusp) = path_geometry::find_cubic_cusp(&cubic) {
614 let cusp_loc = path_geometry::eval_cubic_pos_at(&cubic, cusp.to_normalized());
615 self.cusper.push_circle(cusp_loc.x, cusp_loc.y, self.radius);
616 }
617
618 self.set_cubic_end_normal(&cubic, normal_ab, unit_ab, &mut normal_cd, &mut unit_cd);
621
622 self.post_join_to(pt3, normal_cd, unit_cd);
623 }
624
625 fn cubic_stroke(&mut self, cubic: &[Point; 4], quad_points: &mut QuadConstruct) -> bool {
626 if !self.found_tangents {
627 let result_type = self.tangents_meet(cubic, quad_points);
628 if result_type != ResultType::Quad {
629 let ok = points_within_dist(
630 quad_points.quad[0],
631 quad_points.quad[2],
632 self.inv_res_scale,
633 );
634 if (result_type == ResultType::Degenerate || ok)
635 && self.cubic_mid_on_line(cubic, quad_points)
636 {
637 self.add_degenerate_line(quad_points);
638 return true;
639 }
640 } else {
641 self.found_tangents = true;
642 }
643 }
644
645 if self.found_tangents {
646 let result_type = self.compare_quad_cubic(cubic, quad_points);
647 if result_type == ResultType::Quad {
648 let stroke = &quad_points.quad;
649 if self.stroke_type == StrokeType::Outer {
650 self.outer
651 .quad_to(stroke[1].x, stroke[1].y, stroke[2].x, stroke[2].y);
652 } else {
653 self.inner
654 .quad_to(stroke[1].x, stroke[1].y, stroke[2].x, stroke[2].y);
655 }
656
657 return true;
658 }
659
660 if result_type == ResultType::Degenerate {
661 if !quad_points.opposite_tangents {
662 self.add_degenerate_line(quad_points);
663 return true;
664 }
665 }
666 }
667
668 if !quad_points.quad[2].x.is_finite() || !quad_points.quad[2].x.is_finite() {
669 return false; }
671
672 self.recursion_depth += 1;
673 if self.recursion_depth > RECURSIVE_LIMITS[self.found_tangents as usize] {
674 return false; }
676
677 let mut half = QuadConstruct::default();
678 if !half.init_with_start(quad_points) {
679 self.add_degenerate_line(quad_points);
680 self.recursion_depth -= 1;
681 return true;
682 }
683
684 if !self.cubic_stroke(cubic, &mut half) {
685 return false;
686 }
687
688 if !half.init_with_end(quad_points) {
689 self.add_degenerate_line(quad_points);
690 self.recursion_depth -= 1;
691 return true;
692 }
693
694 if !self.cubic_stroke(cubic, &mut half) {
695 return false;
696 }
697
698 self.recursion_depth -= 1;
699 true
700 }
701
702 fn cubic_mid_on_line(&self, cubic: &[Point; 4], quad_points: &mut QuadConstruct) -> bool {
703 let mut stroke_mid = Point::zero();
704 self.cubic_quad_mid(cubic, quad_points, &mut stroke_mid);
705 let dist = pt_to_line(stroke_mid, quad_points.quad[0], quad_points.quad[2]);
706 dist < self.inv_res_scale_squared
707 }
708
709 fn cubic_quad_mid(&self, cubic: &[Point; 4], quad_points: &mut QuadConstruct, mid: &mut Point) {
710 let mut cubic_mid_pt = Point::zero();
711 self.cubic_perp_ray(cubic, quad_points.mid_t, &mut cubic_mid_pt, mid, None);
712 }
713
714 fn cubic_perp_ray(
717 &self,
718 cubic: &[Point; 4],
719 t: NormalizedF32,
720 t_pt: &mut Point,
721 on_pt: &mut Point,
722 tangent: Option<&mut Point>,
723 ) {
724 *t_pt = path_geometry::eval_cubic_pos_at(cubic, t);
725 let mut dxy = path_geometry::eval_cubic_tangent_at(cubic, t);
726
727 let mut chopped = [Point::zero(); 7];
728 if dxy.x == 0.0 && dxy.y == 0.0 {
729 let mut c_points: &[Point] = cubic;
730 if t.get().is_nearly_zero() {
731 dxy = cubic[2] - cubic[0];
732 } else if (1.0 - t.get()).is_nearly_zero() {
733 dxy = cubic[3] - cubic[1];
734 } else {
735 let t = NormalizedF32Exclusive::new(t.get()).unwrap();
740 path_geometry::chop_cubic_at2(cubic, t, &mut chopped);
741 dxy = chopped[3] - chopped[2];
742 if dxy.x == 0.0 && dxy.y == 0.0 {
743 dxy = chopped[3] - chopped[1];
744 c_points = &chopped;
745 }
746 }
747
748 if dxy.x == 0.0 && dxy.y == 0.0 {
749 dxy = c_points[3] - c_points[0];
750 }
751 }
752
753 self.set_ray_points(*t_pt, &mut dxy, on_pt, tangent);
754 }
755
756 fn set_cubic_end_normal(
757 &mut self,
758 cubic: &[Point; 4],
759 normal_ab: Point,
760 unit_normal_ab: Point,
761 normal_cd: &mut Point,
762 unit_normal_cd: &mut Point,
763 ) {
764 let mut ab = cubic[1] - cubic[0];
765 let mut cd = cubic[3] - cubic[2];
766
767 let mut degenerate_ab = degenerate_vector(ab);
768 let mut degenerate_cb = degenerate_vector(cd);
769
770 if degenerate_ab && degenerate_cb {
771 *normal_cd = normal_ab;
772 *unit_normal_cd = unit_normal_ab;
773 return;
774 }
775
776 if degenerate_ab {
777 ab = cubic[2] - cubic[0];
778 degenerate_ab = degenerate_vector(ab);
779 }
780
781 if degenerate_cb {
782 cd = cubic[3] - cubic[1];
783 degenerate_cb = degenerate_vector(cd);
784 }
785
786 if degenerate_ab || degenerate_cb {
787 *normal_cd = normal_ab;
788 *unit_normal_cd = unit_normal_ab;
789 return;
790 }
791
792 let res = set_normal_unit_normal2(cd, self.radius, normal_cd, unit_normal_cd);
793 debug_assert!(res);
794 }
795
796 fn compare_quad_cubic(
797 &self,
798 cubic: &[Point; 4],
799 quad_points: &mut QuadConstruct,
800 ) -> ResultType {
801 self.cubic_quad_ends(cubic, quad_points);
803 let result_type = self.intersect_ray(IntersectRayType::CtrlPt, quad_points);
804 if result_type != ResultType::Quad {
805 return result_type;
806 }
807
808 let mut ray0 = Point::zero();
811 let mut ray1 = Point::zero();
812 self.cubic_perp_ray(cubic, quad_points.mid_t, &mut ray1, &mut ray0, None);
813 self.stroke_close_enough(&quad_points.quad.clone(), &[ray0, ray1], quad_points)
814 }
815
816 fn cubic_quad_ends(&self, cubic: &[Point; 4], quad_points: &mut QuadConstruct) {
818 if !quad_points.start_set {
819 let mut cubic_start_pt = Point::zero();
820 self.cubic_perp_ray(
821 cubic,
822 quad_points.start_t,
823 &mut cubic_start_pt,
824 &mut quad_points.quad[0],
825 Some(&mut quad_points.tangent_start),
826 );
827 quad_points.start_set = true;
828 }
829
830 if !quad_points.end_set {
831 let mut cubic_end_pt = Point::zero();
832 self.cubic_perp_ray(
833 cubic,
834 quad_points.end_t,
835 &mut cubic_end_pt,
836 &mut quad_points.quad[2],
837 Some(&mut quad_points.tangent_end),
838 );
839 quad_points.end_set = true;
840 }
841 }
842
843 fn close(&mut self, is_line: bool) {
844 self.finish_contour(true, is_line);
845 }
846
847 fn finish_contour(&mut self, close: bool, curr_is_line: bool) {
848 if self.segment_count > 0 {
849 if close {
850 (self.joiner)(
851 self.prev_unit_normal,
852 self.prev_pt,
853 self.first_unit_normal,
854 self.radius,
855 self.inv_miter_limit,
856 self.prev_is_line,
857 curr_is_line,
858 self.builders(),
859 );
860 self.outer.close();
861
862 let pt = self.inner.last_point().unwrap_or_default();
864 self.outer.move_to(pt.x, pt.y);
865 self.outer.reverse_path_to(&self.inner);
866 self.outer.close();
867 } else {
868 let pt = self.inner.last_point().unwrap_or_default();
872 let other_path = if curr_is_line {
873 Some(&self.inner)
874 } else {
875 None
876 };
877 (self.capper)(
878 self.prev_pt,
879 self.prev_normal,
880 pt,
881 other_path,
882 &mut self.outer,
883 );
884 self.outer.reverse_path_to(&self.inner);
885
886 let other_path = if self.prev_is_line {
888 Some(&self.inner)
889 } else {
890 None
891 };
892 (self.capper)(
893 self.first_pt,
894 -self.first_normal,
895 self.first_outer_pt,
896 other_path,
897 &mut self.outer,
898 );
899 self.outer.close();
900 }
901
902 if !self.cusper.is_empty() {
903 self.outer.push_path(&self.cusper);
904 self.cusper.clear();
905 }
906 }
907
908 self.inner.clear();
911 self.segment_count = -1;
912 self.first_outer_pt_index_in_contour = self.outer.points.len();
913 }
914
915 fn pre_join_to(
916 &mut self,
917 p: Point,
918 curr_is_line: bool,
919 normal: &mut Point,
920 unit_normal: &mut Point,
921 ) -> bool {
922 debug_assert!(self.segment_count >= 0);
923
924 let prev_x = self.prev_pt.x;
925 let prev_y = self.prev_pt.y;
926
927 let normal_set = set_normal_unit_normal(
928 self.prev_pt,
929 p,
930 self.res_scale,
931 self.radius,
932 normal,
933 unit_normal,
934 );
935 if !normal_set {
936 if fn_ptr_eq(self.capper, butt_capper) {
937 return false;
938 }
939
940 *normal = Point::from_xy(self.radius, 0.0);
944 *unit_normal = Point::from_xy(1.0, 0.0);
945 }
946
947 if self.segment_count == 0 {
948 self.first_normal = *normal;
949 self.first_unit_normal = *unit_normal;
950 self.first_outer_pt = Point::from_xy(prev_x + normal.x, prev_y + normal.y);
951
952 self.outer
953 .move_to(self.first_outer_pt.x, self.first_outer_pt.y);
954 self.inner.move_to(prev_x - normal.x, prev_y - normal.y);
955 } else {
956 (self.joiner)(
958 self.prev_unit_normal,
959 self.prev_pt,
960 *unit_normal,
961 self.radius,
962 self.inv_miter_limit,
963 self.prev_is_line,
964 curr_is_line,
965 self.builders(),
966 );
967 }
968 self.prev_is_line = curr_is_line;
969 true
970 }
971
972 fn post_join_to(&mut self, p: Point, normal: Point, unit_normal: Point) {
973 self.join_completed = true;
974 self.prev_pt = p;
975 self.prev_unit_normal = unit_normal;
976 self.prev_normal = normal;
977 self.segment_count += 1;
978 }
979
980 fn init_quad(
981 &mut self,
982 stroke_type: StrokeType,
983 start: NormalizedF32,
984 end: NormalizedF32,
985 quad_points: &mut QuadConstruct,
986 ) {
987 self.stroke_type = stroke_type;
988 self.found_tangents = false;
989 quad_points.init(start, end);
990 }
991
992 fn quad_stroke(&mut self, quad: &[Point; 3], quad_points: &mut QuadConstruct) -> bool {
993 let result_type = self.compare_quad_quad(quad, quad_points);
994 if result_type == ResultType::Quad {
995 let path = if self.stroke_type == StrokeType::Outer {
996 &mut self.outer
997 } else {
998 &mut self.inner
999 };
1000
1001 path.quad_to(
1002 quad_points.quad[1].x,
1003 quad_points.quad[1].y,
1004 quad_points.quad[2].x,
1005 quad_points.quad[2].y,
1006 );
1007
1008 return true;
1009 }
1010
1011 if result_type == ResultType::Degenerate {
1012 self.add_degenerate_line(quad_points);
1013 return true;
1014 }
1015
1016 self.recursion_depth += 1;
1017 if self.recursion_depth > RECURSIVE_LIMITS[QUAD_RECURSIVE_LIMIT] {
1018 return false; }
1020
1021 let mut half = QuadConstruct::default();
1022 half.init_with_start(quad_points);
1023 if !self.quad_stroke(quad, &mut half) {
1024 return false;
1025 }
1026
1027 half.init_with_end(quad_points);
1028 if !self.quad_stroke(quad, &mut half) {
1029 return false;
1030 }
1031
1032 self.recursion_depth -= 1;
1033 true
1034 }
1035
1036 fn compare_quad_quad(
1037 &mut self,
1038 quad: &[Point; 3],
1039 quad_points: &mut QuadConstruct,
1040 ) -> ResultType {
1041 if !quad_points.start_set {
1043 let mut quad_start_pt = Point::zero();
1044 self.quad_perp_ray(
1045 quad,
1046 quad_points.start_t,
1047 &mut quad_start_pt,
1048 &mut quad_points.quad[0],
1049 Some(&mut quad_points.tangent_start),
1050 );
1051 quad_points.start_set = true;
1052 }
1053
1054 if !quad_points.end_set {
1055 let mut quad_end_pt = Point::zero();
1056 self.quad_perp_ray(
1057 quad,
1058 quad_points.end_t,
1059 &mut quad_end_pt,
1060 &mut quad_points.quad[2],
1061 Some(&mut quad_points.tangent_end),
1062 );
1063 quad_points.end_set = true;
1064 }
1065
1066 let result_type = self.intersect_ray(IntersectRayType::CtrlPt, quad_points);
1067 if result_type != ResultType::Quad {
1068 return result_type;
1069 }
1070
1071 let mut ray0 = Point::zero();
1073 let mut ray1 = Point::zero();
1074 self.quad_perp_ray(quad, quad_points.mid_t, &mut ray1, &mut ray0, None);
1075 self.stroke_close_enough(&quad_points.quad.clone(), &[ray0, ray1], quad_points)
1076 }
1077
1078 fn set_ray_points(
1081 &self,
1082 tp: Point,
1083 dxy: &mut Point,
1084 on_p: &mut Point,
1085 mut tangent: Option<&mut Point>,
1086 ) {
1087 if !dxy.set_length(self.radius) {
1088 *dxy = Point::from_xy(self.radius, 0.0);
1089 }
1090
1091 let axis_flip = self.stroke_type as i32 as f32; on_p.x = tp.x + axis_flip * dxy.y;
1093 on_p.y = tp.y - axis_flip * dxy.x;
1094
1095 if let Some(ref mut tangent) = tangent {
1096 tangent.x = on_p.x + dxy.x;
1097 tangent.y = on_p.y + dxy.y;
1098 }
1099 }
1100
1101 fn quad_perp_ray(
1104 &self,
1105 quad: &[Point; 3],
1106 t: NormalizedF32,
1107 tp: &mut Point,
1108 on_p: &mut Point,
1109 tangent: Option<&mut Point>,
1110 ) {
1111 *tp = path_geometry::eval_quad_at(quad, t);
1112 let mut dxy = path_geometry::eval_quad_tangent_at(quad, t);
1113
1114 if dxy.is_zero() {
1115 dxy = quad[2] - quad[0];
1116 }
1117
1118 self.set_ray_points(*tp, &mut dxy, on_p, tangent);
1119 }
1120
1121 fn add_degenerate_line(&mut self, quad_points: &QuadConstruct) {
1122 if self.stroke_type == StrokeType::Outer {
1123 self.outer
1124 .line_to(quad_points.quad[2].x, quad_points.quad[2].y);
1125 } else {
1126 self.inner
1127 .line_to(quad_points.quad[2].x, quad_points.quad[2].y);
1128 }
1129 }
1130
1131 fn stroke_close_enough(
1132 &self,
1133 stroke: &[Point; 3],
1134 ray: &[Point; 2],
1135 quad_points: &mut QuadConstruct,
1136 ) -> ResultType {
1137 let half = NormalizedF32::new_clamped(0.5);
1138 let stroke_mid = path_geometry::eval_quad_at(stroke, half);
1139 if points_within_dist(ray[0], stroke_mid, self.inv_res_scale) {
1141 if sharp_angle(&quad_points.quad) {
1143 return ResultType::Split;
1144 }
1145
1146 return ResultType::Quad;
1147 }
1148
1149 if !pt_in_quad_bounds(stroke, ray[0], self.inv_res_scale) {
1152 return ResultType::Split;
1154 }
1155
1156 let mut roots = path_geometry::new_t_values();
1158 let roots = intersect_quad_ray(ray, stroke, &mut roots);
1159 if roots.len() != 1 {
1160 return ResultType::Split;
1161 }
1162
1163 let quad_pt = path_geometry::eval_quad_at(stroke, roots[0].to_normalized());
1164 let error = self.inv_res_scale * (1.0 - (roots[0].get() - 0.5).abs() * 2.0);
1165 if points_within_dist(ray[0], quad_pt, error) {
1166 if sharp_angle(&quad_points.quad) {
1168 return ResultType::Split;
1169 }
1170
1171 return ResultType::Quad;
1172 }
1173
1174 ResultType::Split
1176 }
1177
1178 fn intersect_ray(
1182 &self,
1183 intersect_ray_type: IntersectRayType,
1184 quad_points: &mut QuadConstruct,
1185 ) -> ResultType {
1186 let start = quad_points.quad[0];
1187 let end = quad_points.quad[2];
1188 let a_len = quad_points.tangent_start - start;
1189 let b_len = quad_points.tangent_end - end;
1190
1191 let denom = a_len.cross(b_len);
1197 if denom == 0.0 || !denom.is_finite() {
1198 quad_points.opposite_tangents = a_len.dot(b_len) < 0.0;
1199 return ResultType::Degenerate;
1200 }
1201
1202 quad_points.opposite_tangents = false;
1203 let ab0 = start - end;
1204 let mut numer_a = b_len.cross(ab0);
1205 let numer_b = a_len.cross(ab0);
1206 if (numer_a >= 0.0) == (numer_b >= 0.0) {
1207 let dist1 = pt_to_line(start, end, quad_points.tangent_end);
1212 let dist2 = pt_to_line(end, start, quad_points.tangent_start);
1213 if dist1.max(dist2) <= self.inv_res_scale_squared {
1214 return ResultType::Degenerate;
1215 }
1216
1217 return ResultType::Split;
1218 }
1219
1220 numer_a /= denom;
1223 let valid_divide = numer_a > numer_a - 1.0;
1224 if valid_divide {
1225 if intersect_ray_type == IntersectRayType::CtrlPt {
1226 quad_points.quad[1].x =
1229 start.x * (1.0 - numer_a) + quad_points.tangent_start.x * numer_a;
1230 quad_points.quad[1].y =
1231 start.y * (1.0 - numer_a) + quad_points.tangent_start.y * numer_a;
1232 }
1233
1234 return ResultType::Quad;
1235 }
1236
1237 quad_points.opposite_tangents = a_len.dot(b_len) < 0.0;
1238
1239 ResultType::Degenerate
1241 }
1242
1243 fn tangents_meet(&self, cubic: &[Point; 4], quad_points: &mut QuadConstruct) -> ResultType {
1245 self.cubic_quad_ends(cubic, quad_points);
1246 self.intersect_ray(IntersectRayType::ResultType, quad_points)
1247 }
1248
1249 fn finish(&mut self, is_line: bool) -> Option<Path> {
1250 self.finish_contour(false, is_line);
1251
1252 let mut buf = PathBuilder::new();
1254 core::mem::swap(&mut self.outer, &mut buf);
1255
1256 buf.finish()
1257 }
1258
1259 fn has_only_move_to(&self) -> bool {
1260 self.segment_count == 0
1261 }
1262
1263 fn is_current_contour_empty(&self) -> bool {
1264 self.inner.is_zero_length_since_point(0)
1265 && self
1266 .outer
1267 .is_zero_length_since_point(self.first_outer_pt_index_in_contour)
1268 }
1269}
1270
1271fn cap_factory(cap: LineCap) -> CapProc {
1272 match cap {
1273 LineCap::Butt => butt_capper,
1274 LineCap::Round => round_capper,
1275 LineCap::Square => square_capper,
1276 }
1277}
1278
1279fn butt_capper(_: Point, _: Point, stop: Point, _: Option<&PathBuilder>, path: &mut PathBuilder) {
1280 path.line_to(stop.x, stop.y);
1281}
1282
1283fn round_capper(
1284 pivot: Point,
1285 normal: Point,
1286 stop: Point,
1287 _: Option<&PathBuilder>,
1288 path: &mut PathBuilder,
1289) {
1290 let mut parallel = normal;
1291 parallel.rotate_cw();
1292
1293 let projected_center = pivot + parallel;
1294
1295 path.conic_points_to(
1296 projected_center + normal,
1297 projected_center,
1298 SCALAR_ROOT_2_OVER_2,
1299 );
1300 path.conic_points_to(projected_center - normal, stop, SCALAR_ROOT_2_OVER_2);
1301}
1302
1303fn square_capper(
1304 pivot: Point,
1305 normal: Point,
1306 stop: Point,
1307 other_path: Option<&PathBuilder>,
1308 path: &mut PathBuilder,
1309) {
1310 let mut parallel = normal;
1311 parallel.rotate_cw();
1312
1313 if other_path.is_some() {
1314 path.set_last_point(Point::from_xy(
1315 pivot.x + normal.x + parallel.x,
1316 pivot.y + normal.y + parallel.y,
1317 ));
1318 path.line_to(
1319 pivot.x - normal.x + parallel.x,
1320 pivot.y - normal.y + parallel.y,
1321 );
1322 } else {
1323 path.line_to(
1324 pivot.x + normal.x + parallel.x,
1325 pivot.y + normal.y + parallel.y,
1326 );
1327 path.line_to(
1328 pivot.x - normal.x + parallel.x,
1329 pivot.y - normal.y + parallel.y,
1330 );
1331 path.line_to(stop.x, stop.y);
1332 }
1333}
1334
1335fn join_factory(join: LineJoin) -> JoinProc {
1336 match join {
1337 LineJoin::Miter => miter_joiner,
1338 LineJoin::Round => round_joiner,
1339 LineJoin::Bevel => bevel_joiner,
1340 }
1341}
1342
1343fn is_clockwise(before: Point, after: Point) -> bool {
1344 before.x * after.y > before.y * after.x
1345}
1346
1347#[derive(Copy, Clone, PartialEq, Debug)]
1348enum AngleType {
1349 Nearly180,
1350 Sharp,
1351 Shallow,
1352 NearlyLine,
1353}
1354
1355fn dot_to_angle_type(dot: f32) -> AngleType {
1356 if dot >= 0.0 {
1357 if (1.0 - dot).is_nearly_zero() {
1359 AngleType::NearlyLine
1360 } else {
1361 AngleType::Shallow
1362 }
1363 } else {
1364 if (1.0 + dot).is_nearly_zero() {
1366 AngleType::Nearly180
1367 } else {
1368 AngleType::Sharp
1369 }
1370 }
1371}
1372
1373fn handle_inner_join(pivot: Point, after: Point, inner: &mut PathBuilder) {
1374 inner.line_to(pivot.x, pivot.y);
1380
1381 inner.line_to(pivot.x - after.x, pivot.y - after.y);
1382}
1383
1384fn bevel_joiner(
1385 before_unit_normal: Point,
1386 pivot: Point,
1387 after_unit_normal: Point,
1388 radius: f32,
1389 _: f32,
1390 _: bool,
1391 _: bool,
1392 mut builders: SwappableBuilders,
1393) {
1394 let mut after = after_unit_normal.scaled(radius);
1395
1396 if !is_clockwise(before_unit_normal, after_unit_normal) {
1397 builders.swap();
1398 after = -after;
1399 }
1400
1401 builders.outer.line_to(pivot.x + after.x, pivot.y + after.y);
1402 handle_inner_join(pivot, after, builders.inner);
1403}
1404
1405fn round_joiner(
1406 before_unit_normal: Point,
1407 pivot: Point,
1408 after_unit_normal: Point,
1409 radius: f32,
1410 _: f32,
1411 _: bool,
1412 _: bool,
1413 mut builders: SwappableBuilders,
1414) {
1415 let dot_prod = before_unit_normal.dot(after_unit_normal);
1416 let angle_type = dot_to_angle_type(dot_prod);
1417
1418 if angle_type == AngleType::NearlyLine {
1419 return;
1420 }
1421
1422 let mut before = before_unit_normal;
1423 let mut after = after_unit_normal;
1424 let mut dir = PathDirection::CW;
1425
1426 if !is_clockwise(before, after) {
1427 builders.swap();
1428 before = -before;
1429 after = -after;
1430 dir = PathDirection::CCW;
1431 }
1432
1433 let ts = Transform::from_row(radius, 0.0, 0.0, radius, pivot.x, pivot.y);
1434
1435 let mut conics = [path_geometry::Conic::default(); 5];
1436 let conics = path_geometry::Conic::build_unit_arc(before, after, dir, ts, &mut conics);
1437 if let Some(conics) = conics {
1438 for conic in conics {
1439 builders
1440 .outer
1441 .conic_points_to(conic.points[1], conic.points[2], conic.weight);
1442 }
1443
1444 after.scale(radius);
1445 handle_inner_join(pivot, after, builders.inner);
1446 }
1447}
1448
1449fn miter_joiner(
1450 before_unit_normal: Point,
1451 pivot: Point,
1452 after_unit_normal: Point,
1453 radius: f32,
1454 inv_miter_limit: f32,
1455 prev_is_line: bool,
1456 mut curr_is_line: bool,
1457 mut builders: SwappableBuilders,
1458) {
1459 fn do_blunt(
1460 builders: SwappableBuilders,
1461 pivot: Point,
1462 radius: f32,
1463 curr_is_line: bool,
1464 mut after: Point,
1465 ) {
1466 after.scale(radius);
1467 if !curr_is_line {
1468 builders.outer.line_to(pivot.x + after.x, pivot.y + after.y);
1469 }
1470
1471 handle_inner_join(pivot, after, builders.inner);
1472 }
1473
1474 fn do_miter(
1475 builders: SwappableBuilders,
1476 pivot: Point,
1477 radius: f32,
1478 prev_is_line: bool,
1479 curr_is_line: bool,
1480 mid: Point,
1481 after: Point,
1482 ) {
1483 if prev_is_line {
1484 builders
1485 .outer
1486 .set_last_point(Point::from_xy(pivot.x + mid.x, pivot.y + mid.y));
1487 } else {
1488 builders.outer.line_to(pivot.x + mid.x, pivot.y + mid.y);
1489 }
1490
1491 do_blunt(builders, pivot, radius, curr_is_line, after);
1492 }
1493
1494 let dot_prod = before_unit_normal.dot(after_unit_normal);
1496 let angle_type = dot_to_angle_type(dot_prod);
1497 let mut before = before_unit_normal;
1498 let mut after = after_unit_normal;
1499 let mut mid;
1500
1501 if angle_type == AngleType::NearlyLine {
1502 return;
1503 }
1504
1505 if angle_type == AngleType::Nearly180 {
1506 curr_is_line = false;
1507 do_blunt(builders, pivot, radius, curr_is_line, after);
1508 return;
1509 }
1510
1511 let ccw = !is_clockwise(before, after);
1512 if ccw {
1513 builders.swap();
1514 before = -before;
1515 after = -after;
1516 }
1517
1518 if dot_prod == 0.0 && inv_miter_limit <= SCALAR_ROOT_2_OVER_2 {
1524 mid = (before + after).scaled(radius);
1525 do_miter(
1526 builders,
1527 pivot,
1528 radius,
1529 prev_is_line,
1530 curr_is_line,
1531 mid,
1532 after,
1533 );
1534 return;
1535 }
1536
1537 let sin_half_angle = (1.0 + dot_prod).half().sqrt();
1545 if sin_half_angle < inv_miter_limit {
1546 curr_is_line = false;
1547 do_blunt(builders, pivot, radius, curr_is_line, after);
1548 return;
1549 }
1550
1551 if angle_type == AngleType::Sharp {
1553 mid = Point::from_xy(after.y - before.y, before.x - after.x);
1554 if ccw {
1555 mid = -mid;
1556 }
1557 } else {
1558 mid = Point::from_xy(before.x + after.x, before.y + after.y);
1559 }
1560
1561 mid.set_length(radius / sin_half_angle);
1562 do_miter(
1563 builders,
1564 pivot,
1565 radius,
1566 prev_is_line,
1567 curr_is_line,
1568 mid,
1569 after,
1570 );
1571}
1572
1573fn set_normal_unit_normal(
1574 before: Point,
1575 after: Point,
1576 scale: f32,
1577 radius: f32,
1578 normal: &mut Point,
1579 unit_normal: &mut Point,
1580) -> bool {
1581 if !unit_normal.set_normalize((after.x - before.x) * scale, (after.y - before.y) * scale) {
1582 return false;
1583 }
1584
1585 unit_normal.rotate_ccw();
1586 *normal = unit_normal.scaled(radius);
1587 true
1588}
1589
1590fn set_normal_unit_normal2(
1591 vec: Point,
1592 radius: f32,
1593 normal: &mut Point,
1594 unit_normal: &mut Point,
1595) -> bool {
1596 if !unit_normal.set_normalize(vec.x, vec.y) {
1597 return false;
1598 }
1599
1600 unit_normal.rotate_ccw();
1601 *normal = unit_normal.scaled(radius);
1602 true
1603}
1604
1605fn fn_ptr_eq(f1: CapProc, f2: CapProc) -> bool {
1606 core::ptr::eq(f1 as *const (), f2 as *const ())
1607}
1608
1609#[derive(Debug)]
1610struct QuadConstruct {
1611 quad: [Point; 3], tangent_start: Point, tangent_end: Point, start_t: NormalizedF32, mid_t: NormalizedF32,
1617 end_t: NormalizedF32,
1618 start_set: bool, end_set: bool,
1620 opposite_tangents: bool, }
1622
1623impl Default for QuadConstruct {
1624 fn default() -> Self {
1625 Self {
1626 quad: Default::default(),
1627 tangent_start: Point::default(),
1628 tangent_end: Point::default(),
1629 start_t: NormalizedF32::ZERO,
1630 mid_t: NormalizedF32::ZERO,
1631 end_t: NormalizedF32::ZERO,
1632 start_set: false,
1633 end_set: false,
1634 opposite_tangents: false,
1635 }
1636 }
1637}
1638
1639impl QuadConstruct {
1640 fn init(&mut self, start: NormalizedF32, end: NormalizedF32) -> bool {
1642 self.start_t = start;
1643 self.mid_t = NormalizedF32::new_clamped((start.get() + end.get()).half());
1644 self.end_t = end;
1645 self.start_set = false;
1646 self.end_set = false;
1647 self.start_t < self.mid_t && self.mid_t < self.end_t
1648 }
1649
1650 fn init_with_start(&mut self, parent: &Self) -> bool {
1651 if !self.init(parent.start_t, parent.mid_t) {
1652 return false;
1653 }
1654
1655 self.quad[0] = parent.quad[0];
1656 self.tangent_start = parent.tangent_start;
1657 self.start_set = true;
1658 true
1659 }
1660
1661 fn init_with_end(&mut self, parent: &Self) -> bool {
1662 if !self.init(parent.mid_t, parent.end_t) {
1663 return false;
1664 }
1665
1666 self.quad[2] = parent.quad[2];
1667 self.tangent_end = parent.tangent_end;
1668 self.end_set = true;
1669 true
1670 }
1671}
1672
1673fn check_quad_linear(quad: &[Point; 3]) -> (Point, ReductionType) {
1674 let degenerate_ab = degenerate_vector(quad[1] - quad[0]);
1675 let degenerate_bc = degenerate_vector(quad[2] - quad[1]);
1676 if degenerate_ab & degenerate_bc {
1677 return (Point::zero(), ReductionType::Point);
1678 }
1679
1680 if degenerate_ab | degenerate_bc {
1681 return (Point::zero(), ReductionType::Line);
1682 }
1683
1684 if !quad_in_line(quad) {
1685 return (Point::zero(), ReductionType::Quad);
1686 }
1687
1688 let t = path_geometry::find_quad_max_curvature(quad);
1689 if t == NormalizedF32::ZERO || t == NormalizedF32::ONE {
1690 return (Point::zero(), ReductionType::Line);
1691 }
1692
1693 (
1694 path_geometry::eval_quad_at(quad, t),
1695 ReductionType::Degenerate,
1696 )
1697}
1698
1699fn degenerate_vector(v: Point) -> bool {
1700 !v.can_normalize()
1701}
1702
1703fn quad_in_line(quad: &[Point; 3]) -> bool {
1710 let mut pt_max = -1.0;
1711 let mut outer1 = 0;
1712 let mut outer2 = 0;
1713 for index in 0..2 {
1714 for inner in index + 1..3 {
1715 let test_diff = quad[inner] - quad[index];
1716 let test_max = test_diff.x.abs().max(test_diff.y.abs());
1717 if pt_max < test_max {
1718 outer1 = index;
1719 outer2 = inner;
1720 pt_max = test_max;
1721 }
1722 }
1723 }
1724
1725 debug_assert!(outer1 <= 1);
1726 debug_assert!(outer2 >= 1 && outer2 <= 2);
1727 debug_assert!(outer1 < outer2);
1728
1729 let mid = outer1 ^ outer2 ^ 3;
1730 const CURVATURE_SLOP: f32 = 0.000005; let line_slop = pt_max * pt_max * CURVATURE_SLOP;
1732 pt_to_line(quad[mid], quad[outer1], quad[outer2]) <= line_slop
1733}
1734
1735fn pt_to_line(pt: Point, line_start: Point, line_end: Point) -> f32 {
1737 let dxy = line_end - line_start;
1738 let ab0 = pt - line_start;
1739 let numer = dxy.dot(ab0);
1740 let denom = dxy.dot(dxy);
1741 let t = numer / denom;
1742 if t >= 0.0 && t <= 1.0 {
1743 let hit = Point::from_xy(
1744 line_start.x * (1.0 - t) + line_end.x * t,
1745 line_start.y * (1.0 - t) + line_end.y * t,
1746 );
1747 hit.distance_to_sqd(pt)
1748 } else {
1749 pt.distance_to_sqd(line_start)
1750 }
1751}
1752
1753fn intersect_quad_ray<'a>(
1755 line: &[Point; 2],
1756 quad: &[Point; 3],
1757 roots: &'a mut [NormalizedF32Exclusive; 3],
1758) -> &'a [NormalizedF32Exclusive] {
1759 let vec = line[1] - line[0];
1760 let mut r = [0.0; 3];
1761 for n in 0..3 {
1762 r[n] = (quad[n].y - line[0].y) * vec.x - (quad[n].x - line[0].x) * vec.y;
1763 }
1764 let mut a = r[2];
1765 let mut b = r[1];
1766 let c = r[0];
1767 a += c - 2.0 * b; b -= c; let len = path_geometry::find_unit_quad_roots(a, 2.0 * b, c, roots);
1771 &roots[0..len]
1772}
1773
1774fn points_within_dist(near_pt: Point, far_pt: Point, limit: f32) -> bool {
1775 near_pt.distance_to_sqd(far_pt) <= limit * limit
1776}
1777
1778fn sharp_angle(quad: &[Point; 3]) -> bool {
1779 let mut smaller = quad[1] - quad[0];
1780 let mut larger = quad[1] - quad[2];
1781 let smaller_len = smaller.length_sqd();
1782 let mut larger_len = larger.length_sqd();
1783 if smaller_len > larger_len {
1784 core::mem::swap(&mut smaller, &mut larger);
1785 larger_len = smaller_len;
1786 }
1787
1788 if !smaller.set_length(larger_len) {
1789 return false;
1790 }
1791
1792 let dot = smaller.dot(larger);
1793 dot > 0.0
1794}
1795
1796fn pt_in_quad_bounds(quad: &[Point; 3], pt: Point, inv_res_scale: f32) -> bool {
1798 let x_min = quad[0].x.min(quad[1].x).min(quad[2].x);
1799 if pt.x + inv_res_scale < x_min {
1800 return false;
1801 }
1802
1803 let x_max = quad[0].x.max(quad[1].x).max(quad[2].x);
1804 if pt.x - inv_res_scale > x_max {
1805 return false;
1806 }
1807
1808 let y_min = quad[0].y.min(quad[1].y).min(quad[2].y);
1809 if pt.y + inv_res_scale < y_min {
1810 return false;
1811 }
1812
1813 let y_max = quad[0].y.max(quad[1].y).max(quad[2].y);
1814 if pt.y - inv_res_scale > y_max {
1815 return false;
1816 }
1817
1818 true
1819}
1820
1821fn check_cubic_linear(
1822 cubic: &[Point; 4],
1823 reduction: &mut [Point; 3],
1824 tangent_pt: Option<&mut Point>,
1825) -> ReductionType {
1826 let degenerate_ab = degenerate_vector(cubic[1] - cubic[0]);
1827 let degenerate_bc = degenerate_vector(cubic[2] - cubic[1]);
1828 let degenerate_cd = degenerate_vector(cubic[3] - cubic[2]);
1829 if degenerate_ab & degenerate_bc & degenerate_cd {
1830 return ReductionType::Point;
1831 }
1832
1833 if degenerate_ab as i32 + degenerate_bc as i32 + degenerate_cd as i32 == 2 {
1834 return ReductionType::Line;
1835 }
1836
1837 if !cubic_in_line(cubic) {
1838 if let Some(tangent_pt) = tangent_pt {
1839 *tangent_pt = if degenerate_ab { cubic[2] } else { cubic[1] };
1840 }
1841
1842 return ReductionType::Quad;
1843 }
1844
1845 let mut t_values = [NormalizedF32::ZERO; 3];
1846 let t_values = path_geometry::find_cubic_max_curvature(cubic, &mut t_values);
1847 let mut r_count = 0;
1848 for t in t_values {
1850 if 0.0 >= t.get() || t.get() >= 1.0 {
1851 continue;
1852 }
1853
1854 reduction[r_count] = path_geometry::eval_cubic_pos_at(cubic, *t);
1855 if reduction[r_count] != cubic[0] && reduction[r_count] != cubic[3] {
1856 r_count += 1;
1857 }
1858 }
1859
1860 match r_count {
1861 0 => ReductionType::Line,
1862 1 => ReductionType::Degenerate,
1863 2 => ReductionType::Degenerate2,
1864 3 => ReductionType::Degenerate3,
1865 _ => unreachable!(),
1866 }
1867}
1868
1869fn cubic_in_line(cubic: &[Point; 4]) -> bool {
1897 let mut pt_max = -1.0;
1898 let mut outer1 = 0;
1899 let mut outer2 = 0;
1900 for index in 0..3 {
1901 for inner in index + 1..4 {
1902 let test_diff = cubic[inner] - cubic[index];
1903 let test_max = test_diff.x.abs().max(test_diff.y.abs());
1904 if pt_max < test_max {
1905 outer1 = index;
1906 outer2 = inner;
1907 pt_max = test_max;
1908 }
1909 }
1910 }
1911 debug_assert!(outer1 <= 2);
1912 debug_assert!(outer2 >= 1 && outer2 <= 3);
1913 debug_assert!(outer1 < outer2);
1914 let mid1 = (1 + (2 >> outer2)) >> outer1;
1915 debug_assert!(mid1 <= 2);
1916 debug_assert!(outer1 != mid1 && outer2 != mid1);
1917 let mid2 = outer1 ^ outer2 ^ mid1;
1918 debug_assert!(mid2 >= 1 && mid2 <= 3);
1919 debug_assert!(mid2 != outer1 && mid2 != outer2 && mid2 != mid1);
1920 debug_assert!(((1 << outer1) | (1 << outer2) | (1 << mid1) | (1 << mid2)) == 0x0f);
1921 let line_slop = pt_max * pt_max * 0.00001; pt_to_line(cubic[mid1], cubic[outer1], cubic[outer2]) <= line_slop
1924 && pt_to_line(cubic[mid2], cubic[outer1], cubic[outer2]) <= line_slop
1925}
1926
1927#[rustfmt::skip]
1928#[cfg(test)]
1929mod tests {
1930 use super::*;
1931
1932 impl PathSegment {
1933 fn new_move_to(x: f32, y: f32) -> Self {
1934 PathSegment::MoveTo(Point::from_xy(x, y))
1935 }
1936
1937 fn new_line_to(x: f32, y: f32) -> Self {
1938 PathSegment::LineTo(Point::from_xy(x, y))
1939 }
1940
1941 fn new_close() -> Self {
1950 PathSegment::Close
1951 }
1952 }
1953
1954 #[test]
1956 fn auto_close() {
1957 let mut pb = PathBuilder::new();
1959 pb.move_to(10.0, 10.0);
1960 pb.line_to(20.0, 50.0);
1961 pb.line_to(30.0, 10.0);
1962 pb.close();
1963 let path = pb.finish().unwrap();
1964
1965 let stroke = Stroke::default();
1966 let stroke_path = PathStroker::new().stroke(&path, &stroke, 1.0).unwrap();
1967
1968 let mut iter = stroke_path.segments();
1969 iter.set_auto_close(true);
1970
1971 assert_eq!(iter.next().unwrap(), PathSegment::new_move_to(10.485071, 9.878732));
1972 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(20.485071, 49.878731));
1973 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(20.0, 50.0));
1974 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(19.514929, 49.878731));
1975 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(29.514929, 9.878732));
1976 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(30.0, 10.0));
1977 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(30.0, 10.5));
1978 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(10.0, 10.5));
1979 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(10.0, 10.0));
1980 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(10.485071, 9.878732));
1981 assert_eq!(iter.next().unwrap(), PathSegment::new_close());
1982 assert_eq!(iter.next().unwrap(), PathSegment::new_move_to(9.3596115, 9.5));
1983 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(30.640388, 9.5));
1984 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(20.485071, 50.121269));
1985 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(19.514929, 50.121269));
1986 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(9.514929, 10.121268));
1987 assert_eq!(iter.next().unwrap(), PathSegment::new_line_to(9.3596115, 9.5));
1988 assert_eq!(iter.next().unwrap(), PathSegment::new_close());
1989 }
1990
1991 #[test]
1993 fn cubic_1() {
1994 let mut pb = PathBuilder::new();
1995 pb.move_to(51.0161362, 1511.52478);
1996 pb.cubic_to(
1997 51.0161362, 1511.52478,
1998 51.0161362, 1511.52478,
1999 51.0161362, 1511.52478,
2000 );
2001 let path = pb.finish().unwrap();
2002
2003 let mut stroke = Stroke::default();
2004 stroke.width = 0.394537568;
2005
2006 assert!(PathStroker::new().stroke(&path, &stroke, 1.0).is_none());
2007 }
2008
2009 #[test]
2011 fn cubic_2() {
2012 let mut pb = PathBuilder::new();
2013 pb.move_to(f32::from_bits(0x424c1086), f32::from_bits(0x44bcf0cb)); pb.cubic_to(
2015 f32::from_bits(0x424c107c), f32::from_bits(0x44bcf0cb), f32::from_bits(0x424c10c2), f32::from_bits(0x44bcf0cb), f32::from_bits(0x424c1119), f32::from_bits(0x44bcf0ca), );
2019 let path = pb.finish().unwrap();
2020
2021 let mut stroke = Stroke::default();
2022 stroke.width = 0.394537568;
2023
2024 assert!(PathStroker::new().stroke(&path, &stroke, 1.0).is_some());
2025 }
2026
2027 #[test]
2030 fn big() {
2031 let mut pb = PathBuilder::new();
2034 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();
2040 let path = pb.finish().unwrap();
2041
2042 let mut stroke = Stroke::default();
2043 stroke.width = 1.49679073e+10;
2044
2045 assert!(PathStroker::new().stroke(&path, &stroke, 1.0).is_some());
2046 }
2047
2048 #[test]
2050 fn quad_stroker_one_off() {
2051 let mut pb = PathBuilder::new();
2052 pb.move_to(f32::from_bits(0x43c99223), f32::from_bits(0x42b7417e));
2053 pb.quad_to(
2054 f32::from_bits(0x4285d839), f32::from_bits(0x43ed6645),
2055 f32::from_bits(0x43c941c8), f32::from_bits(0x42b3ace3),
2056 );
2057 let path = pb.finish().unwrap();
2058
2059 let mut stroke = Stroke::default();
2060 stroke.width = 164.683548;
2061
2062 assert!(PathStroker::new().stroke(&path, &stroke, 1.0).is_some());
2063 }
2064
2065 #[test]
2067 fn cubic_stroker_one_off() {
2068 let mut pb = PathBuilder::new();
2069 pb.move_to(f32::from_bits(0x433f5370), f32::from_bits(0x43d1f4b3));
2070 pb.cubic_to(
2071 f32::from_bits(0x4331cb76), f32::from_bits(0x43ea3340),
2072 f32::from_bits(0x4388f498), f32::from_bits(0x42f7f08d),
2073 f32::from_bits(0x43f1cd32), f32::from_bits(0x42802ec1),
2074 );
2075 let path = pb.finish().unwrap();
2076
2077 let mut stroke = Stroke::default();
2078 stroke.width = 42.835968;
2079
2080 assert!(PathStroker::new().stroke(&path, &stroke, 1.0).is_some());
2081 }
2082}