1#![allow(clippy::needless_range_loop)]
3
4use crate::geom::arrayvec::ArrayVec;
5use crate::geom::utils::tangent;
6use crate::geom::{CubicBezierSegment, Line, LineSegment, QuadraticBezierSegment};
7use crate::math::*;
8use crate::math_utils::compute_normal;
9use crate::path::builder::{Build, NoAttributes, PathBuilder};
10use crate::path::polygon::Polygon;
11use crate::path::private::DebugValidator;
12use crate::path::{
13 AttributeStore, Attributes, EndpointId, IdEvent, PathEvent, PathSlice, PositionStore, Winding,
14};
15use crate::{
16 LineCap, LineJoin, Side, SimpleAttributeStore, StrokeGeometryBuilder, StrokeOptions,
17 TessellationError, TessellationResult, VertexId, VertexSource,
18};
19
20use core::f32::consts::PI;
21use alloc::vec::Vec;
22
23#[cfg(not(feature = "std"))]
24use num_traits::Float;
25
26const SIDE_POSITIVE: usize = 0;
27const SIDE_NEGATIVE: usize = 1;
28
29macro_rules! nan_check {
30 ($($v:expr),+) => { $(debug_assert!(!$v.is_nan());)+ };
31}
32
33#[derive(Default)]
108pub struct StrokeTessellator {
109 attrib_buffer: Vec<f32>,
110 builder_attrib_store: SimpleAttributeStore,
111}
112
113impl StrokeTessellator {
114 pub fn new() -> Self {
115 StrokeTessellator {
116 attrib_buffer: Vec::new(),
117 builder_attrib_store: SimpleAttributeStore::new(0),
118 }
119 }
120
121 pub fn tessellate(
123 &mut self,
124 input: impl IntoIterator<Item = PathEvent>,
125 options: &StrokeOptions,
126 builder: &mut dyn StrokeGeometryBuilder,
127 ) -> TessellationResult {
128 debug_assert!(
129 options.variable_line_width.is_none(),
130 "Variable line width requires custom attributes. Try tessellate_with_ids or tessellate_path",
131 );
132
133 let mut buffer = Vec::new();
134 let builder = StrokeBuilderImpl::new(options, &mut buffer, builder);
135
136 builder.tessellate_fw(input)
137 }
138
139 pub fn tessellate_with_ids(
141 &mut self,
142 path: impl IntoIterator<Item = IdEvent>,
143 positions: &impl PositionStore,
144 custom_attributes: Option<&dyn AttributeStore>,
145 options: &StrokeOptions,
146 output: &mut dyn StrokeGeometryBuilder,
147 ) -> TessellationResult {
148 let custom_attributes = custom_attributes.unwrap_or(&());
149
150 self.attrib_buffer.clear();
151 for _ in 0..custom_attributes.num_attributes() {
152 self.attrib_buffer.push(0.0);
153 }
154
155 let builder = StrokeBuilderImpl::new(options, &mut self.attrib_buffer, output);
156
157 builder.tessellate_with_ids(path, positions, custom_attributes)
158 }
159
160 pub fn tessellate_path<'l>(
165 &'l mut self,
166 path: impl Into<PathSlice<'l>>,
167 options: &'l StrokeOptions,
168 builder: &'l mut dyn StrokeGeometryBuilder,
169 ) -> TessellationResult {
170 let path = path.into();
171
172 if path.num_attributes() > 0 {
173 self.tessellate_with_ids(path.id_iter(), &path, Some(&path), options, builder)
174 } else {
175 self.tessellate(path.iter(), options, builder)
176 }
177 }
178
179 pub fn builder<'l>(
216 &'l mut self,
217 options: &'l StrokeOptions,
218 output: &'l mut dyn StrokeGeometryBuilder,
219 ) -> NoAttributes<StrokeBuilder<'l>> {
220 self.builder_attrib_store.reset(0);
221 self.attrib_buffer.clear();
222 NoAttributes::wrap(StrokeBuilder::new(
223 options,
224 &mut self.attrib_buffer,
225 &mut self.builder_attrib_store,
226 output,
227 ))
228 }
229
230 pub fn builder_with_attributes<'l>(
235 &'l mut self,
236 num_attributes: usize,
237 options: &'l StrokeOptions,
238 output: &'l mut dyn StrokeGeometryBuilder,
239 ) -> StrokeBuilder<'l> {
240 self.builder_attrib_store.reset(num_attributes);
241 self.attrib_buffer.clear();
242 for _ in 0..num_attributes {
243 self.attrib_buffer.push(0.0);
244 }
245
246 StrokeBuilder::new(
247 options,
248 &mut self.attrib_buffer,
249 &mut self.builder_attrib_store,
250 output,
251 )
252 }
253
254 pub fn tessellate_polygon(
256 &mut self,
257 polygon: Polygon<Point>,
258 options: &StrokeOptions,
259 output: &mut dyn StrokeGeometryBuilder,
260 ) -> TessellationResult {
261 self.tessellate(polygon.path_events(), options, output)
262 }
263
264 pub fn tessellate_rectangle(
266 &mut self,
267 rect: &Box2D,
268 options: &StrokeOptions,
269 output: &mut dyn StrokeGeometryBuilder,
270 ) -> TessellationResult {
271 assert!(options.variable_line_width.is_none());
272
273 let mut builder = self.builder(options, output);
274 builder.add_rectangle(rect, Winding::Positive);
275
276 builder.build()
277 }
278
279 pub fn tessellate_circle(
281 &mut self,
282 center: Point,
283 radius: f32,
284 options: &StrokeOptions,
285 output: &mut dyn StrokeGeometryBuilder,
286 ) -> TessellationResult {
287 let mut builder = self.builder(options, output);
288 builder.add_circle(center, radius, Winding::Positive);
289
290 builder.build()
291 }
292
293 pub fn tessellate_ellipse(
295 &mut self,
296 center: Point,
297 radii: Vector,
298 x_rotation: Angle,
299 winding: Winding,
300 options: &StrokeOptions,
301 output: &mut dyn StrokeGeometryBuilder,
302 ) -> TessellationResult {
303 let mut builder = self.builder(options, output);
304 builder.add_ellipse(center, radii, x_rotation, winding);
305
306 builder.build()
307 }
308}
309
310#[derive(Copy, Clone, Debug)]
311pub(crate) struct SidePoints {
312 prev: Point,
313 next: Point,
314 single_vertex: Option<Point>,
315 prev_vertex: VertexId,
316 next_vertex: VertexId,
317}
318
319#[derive(Copy, Clone, Debug)]
320pub(crate) struct EndpointData {
321 pub position: Point,
322 pub half_width: f32,
323 pub advancement: f32,
324 pub line_join: LineJoin,
325 pub src: VertexSource,
326 pub side_points: [SidePoints; 2],
327 pub fold: [bool; 2],
328 pub is_flattening_step: bool,
329}
330
331impl Default for EndpointData {
332 fn default() -> Self {
333 EndpointData {
334 position: Point::zero(),
335 half_width: f32::NAN,
336 advancement: f32::NAN,
337 line_join: LineJoin::Miter,
338 src: VertexSource::Endpoint {
339 id: EndpointId::INVALID,
340 },
341 side_points: [SidePoints {
342 prev: point(f32::NAN, f32::NAN),
343 prev_vertex: VertexId(u32::MAX),
344 next: point(f32::NAN, f32::NAN),
345 next_vertex: VertexId(u32::MAX),
346 single_vertex: None,
347 }; 2],
348 fold: [false, false],
349 is_flattening_step: false,
350 }
351 }
352}
353
354pub struct StrokeBuilder<'l> {
359 builder: StrokeBuilderImpl<'l>,
360 attrib_store: &'l mut SimpleAttributeStore,
361 validator: DebugValidator,
362 prev: (Point, EndpointId, f32),
363}
364
365impl<'l> StrokeBuilder<'l> {
366 pub(crate) fn new(
367 options: &StrokeOptions,
368 attrib_buffer: &'l mut Vec<f32>,
369 attrib_store: &'l mut SimpleAttributeStore,
370 output: &'l mut dyn StrokeGeometryBuilder,
371 ) -> Self {
372 StrokeBuilder {
373 builder: StrokeBuilderImpl::new(options, attrib_buffer, output),
374 attrib_store,
375 validator: DebugValidator::new(),
376 prev: (Point::zero(), EndpointId::INVALID, 0.0),
377 }
378 }
379
380 #[inline]
381 pub fn set_line_join(&mut self, join: LineJoin) {
382 self.builder.options.line_join = join;
383 }
384
385 #[inline]
386 pub fn set_start_cap(&mut self, cap: LineCap) {
387 self.builder.options.start_cap = cap;
388 }
389
390 #[inline]
391 pub fn set_end_cap(&mut self, cap: LineCap) {
392 self.builder.options.end_cap = cap;
393 }
394
395 #[inline]
396 pub fn set_miter_limit(&mut self, limit: f32) {
397 self.builder.options.miter_limit = limit;
398 }
399
400 fn get_width(&self, attributes: Attributes) -> f32 {
401 if let Some(idx) = self.builder.options.variable_line_width {
402 self.builder.options.line_width * attributes[idx]
403 } else {
404 self.builder.options.line_width
405 }
406 }
407}
408
409impl<'l> PathBuilder for StrokeBuilder<'l> {
410 fn num_attributes(&self) -> usize {
411 self.attrib_store.num_attributes()
412 }
413
414 fn begin(&mut self, to: Point, attributes: Attributes) -> EndpointId {
415 self.validator.begin();
416 let id = self.attrib_store.add(attributes);
417 if let Some(attrib_index) = self.builder.options.variable_line_width {
418 let width = self.builder.options.line_width * attributes[attrib_index];
419 self.builder.begin(to, id, width, self.attrib_store);
420 self.prev = (to, id, width);
421 } else {
422 self.builder.begin_fw(to, id, self.attrib_store);
423 self.prev = (to, id, self.builder.options.line_width);
424 }
425
426 id
427 }
428
429 fn end(&mut self, close: bool) {
430 self.validator.end();
431 self.builder.end(close, self.attrib_store);
432 }
433
434 fn line_to(&mut self, to: Point, attributes: Attributes) -> EndpointId {
435 let id = self.attrib_store.add(attributes);
436 self.validator.edge();
437 if let Some(attrib_index) = self.builder.options.variable_line_width {
438 let width = self.builder.options.line_width * attributes[attrib_index];
439 self.builder.line_to(to, id, width, self.attrib_store);
440 self.prev = (to, id, width);
441 } else {
442 self.builder.line_to_fw(to, id, self.attrib_store);
443 self.prev = (to, id, self.builder.options.line_width);
444 }
445
446 id
447 }
448
449 fn quadratic_bezier_to(
450 &mut self,
451 ctrl: Point,
452 to: Point,
453 attributes: Attributes,
454 ) -> EndpointId {
455 self.validator.edge();
456 let (from, from_id, start_width) = self.prev;
457 let to_id = self.attrib_store.add(attributes);
458
459 let curve = QuadraticBezierSegment { from, ctrl, to };
460
461 if let Some(attrib_index) = self.builder.options.variable_line_width {
462 let end_width = self.builder.options.line_width * attributes[attrib_index];
463 self.builder.quadratic_bezier_to(
464 &curve,
465 from_id,
466 to_id,
467 start_width,
468 end_width,
469 self.attrib_store,
470 );
471
472 self.prev = (to, to_id, end_width);
473 } else {
474 self.builder
475 .quadratic_bezier_to_fw(&curve, from_id, to_id, self.attrib_store);
476
477 self.prev = (to, to_id, self.builder.options.line_width);
478 }
479
480 to_id
481 }
482
483 fn cubic_bezier_to(
484 &mut self,
485 ctrl1: Point,
486 ctrl2: Point,
487 to: Point,
488 attributes: Attributes,
489 ) -> EndpointId {
490 self.validator.edge();
491 let (from, from_id, start_width) = self.prev;
492 let to_id = self.attrib_store.add(attributes);
493
494 let curve = CubicBezierSegment {
495 from,
496 ctrl1,
497 ctrl2,
498 to,
499 };
500
501 if let Some(attrib_index) = self.builder.options.variable_line_width {
502 let end_width = self.builder.options.line_width * attributes[attrib_index];
503 self.builder.cubic_bezier_to(
504 &curve,
505 from_id,
506 to_id,
507 start_width,
508 end_width,
509 self.attrib_store,
510 );
511
512 self.prev = (to, to_id, end_width);
513 } else {
514 self.builder
515 .cubic_bezier_to_fw(&curve, from_id, to_id, self.attrib_store);
516
517 self.prev = (to, to_id, self.builder.options.line_width);
518 }
519
520 to_id
521 }
522
523 fn add_rectangle(&mut self, rect: &Box2D, winding: Winding, attributes: Attributes) {
524 let threshold = match self.builder.options.line_join {
528 LineJoin::Miter => 1.0,
529 _ => 0.05,
530 } * self.builder.options.line_width;
531
532 if self.builder.options.variable_line_width.is_none()
533 && (rect.width().abs() < threshold || rect.height().abs() < threshold)
534 {
535 approximate_thin_rectangle(self, rect, attributes);
536 return;
537 }
538
539 match winding {
540 Winding::Positive => self.add_polygon(
541 Polygon {
542 points: &[
543 rect.min,
544 point(rect.max.x, rect.min.y),
545 rect.max,
546 point(rect.min.x, rect.max.y),
547 ],
548 closed: true,
549 },
550 attributes,
551 ),
552 Winding::Negative => self.add_polygon(
553 Polygon {
554 points: &[
555 rect.min,
556 point(rect.min.x, rect.max.y),
557 rect.max,
558 point(rect.max.x, rect.min.y),
559 ],
560 closed: true,
561 },
562 attributes,
563 ),
564 };
565 }
566}
567
568impl<'l> Build for StrokeBuilder<'l> {
569 type PathType = TessellationResult;
570
571 fn build(self) -> TessellationResult {
572 self.builder.build()
573 }
574}
575
576pub(crate) struct StrokeBuilderImpl<'l> {
578 options: StrokeOptions,
579 pub(crate) error: Option<TessellationError>,
580 pub(crate) output: &'l mut dyn StrokeGeometryBuilder,
581 vertex: StrokeVertexData<'l>,
582 point_buffer: PointBuffer,
583 firsts: ArrayVec<EndpointData, 2>,
584 previous: Option<EndpointData>,
585 sub_path_start_advancement: f32,
586 square_merge_threshold: f32,
587 may_need_empty_cap: bool,
588}
589
590impl<'l> StrokeBuilderImpl<'l> {
591 pub(crate) fn new(
592 options: &StrokeOptions,
593 attrib_buffer: &'l mut Vec<f32>,
594 output: &'l mut dyn StrokeGeometryBuilder,
595 ) -> Self {
596 output.begin_geometry();
597
598 let square_merge_threshold = (options.tolerance * options.tolerance * 0.5)
604 .min(options.line_width * options.line_width * 0.05)
605 .max(1e-8);
606
607 let zero = Point::new(0.0, 0.0);
608 StrokeBuilderImpl {
609 options: *options,
610 error: None,
611 output,
612 vertex: StrokeVertexData {
613 position_on_path: zero,
614 normal: vector(0.0, 0.0),
615 half_width: options.line_width * 0.5,
616 advancement: 0.0,
617 buffer: attrib_buffer,
618 side: Side::Negative,
619 src: VertexSource::Endpoint {
620 id: EndpointId::INVALID,
621 },
622 buffer_is_valid: false,
623 },
624 point_buffer: PointBuffer::new(),
625 firsts: ArrayVec::new(),
626 previous: None,
627 sub_path_start_advancement: 0.0,
628 square_merge_threshold,
629 may_need_empty_cap: false,
630 }
631 }
632
633 #[cold]
634 pub(crate) fn error<E: Into<TessellationError>>(&mut self, e: E) {
635 if self.error.is_none() {
636 self.error = Some(e.into());
637 }
638 }
639
640 pub(crate) fn tessellate_with_ids(
641 self,
642 path: impl IntoIterator<Item = IdEvent>,
643 positions: &impl PositionStore,
644 attributes: &dyn AttributeStore,
645 ) -> TessellationResult {
646 if self.options.variable_line_width.is_some() {
647 self.tessellate_with_ids_vw(path, positions, attributes)
648 } else {
649 self.tessellate_with_ids_fw(path, positions, attributes)
650 }
651 }
652
653 fn tessellate_with_ids_vw(
654 mut self,
655 path: impl IntoIterator<Item = IdEvent>,
656 positions: &impl PositionStore,
657 attributes: &dyn AttributeStore,
658 ) -> TessellationResult {
659 let base_width = self.options.line_width;
660 let attrib_index = self.options.variable_line_width.unwrap();
661
662 let mut validator = DebugValidator::new();
663
664 let mut current_endpoint = EndpointId(u32::MAX);
665 let mut current_position = point(f32::NAN, f32::NAN);
666
667 for evt in path.into_iter() {
668 match evt {
669 IdEvent::Begin { at } => {
670 validator.begin();
671 let half_width = base_width * attributes.get(at)[attrib_index] * 0.5;
672 current_endpoint = at;
673 current_position = positions.get_endpoint(at);
674 self.may_need_empty_cap = false;
675 self.step(
676 EndpointData {
677 position: current_position,
678 half_width,
679 advancement: self.sub_path_start_advancement,
680 line_join: self.options.line_join,
681 src: VertexSource::Endpoint { id: at },
682 ..Default::default()
683 },
684 attributes,
685 );
686 }
687 IdEvent::Line { to, .. } => {
688 validator.edge();
689 let half_width = base_width * attributes.get(to)[attrib_index] * 0.5;
690 current_endpoint = to;
691 current_position = positions.get_endpoint(to);
692 self.step(
693 EndpointData {
694 position: current_position,
695 half_width,
696 line_join: self.options.line_join,
697 src: VertexSource::Endpoint { id: to },
698 ..Default::default()
699 },
700 attributes,
701 );
702 }
703 IdEvent::Quadratic { ctrl, to, .. } => {
704 validator.edge();
705 let start_width = base_width * attributes.get(current_endpoint)[attrib_index];
706 let end_width = base_width * attributes.get(to)[attrib_index];
707
708 let from = current_endpoint;
709 let from_pos = current_position;
710 current_endpoint = to;
711 current_position = positions.get_endpoint(to);
712
713 self.quadratic_bezier_to(
714 &QuadraticBezierSegment {
715 from: from_pos,
716 ctrl: positions.get_control_point(ctrl),
717 to: current_position,
718 },
719 from,
720 to,
721 start_width,
722 end_width,
723 attributes,
724 );
725 }
726 IdEvent::Cubic {
727 ctrl1, ctrl2, to, ..
728 } => {
729 validator.edge();
730
731 let start_width = base_width * attributes.get(current_endpoint)[attrib_index];
732 let end_width = base_width * attributes.get(to)[attrib_index];
733
734 let from = current_endpoint;
735 let from_pos = current_position;
736 current_endpoint = to;
737 current_position = positions.get_endpoint(to);
738
739 self.cubic_bezier_to(
740 &CubicBezierSegment {
741 from: from_pos,
742 ctrl1: positions.get_control_point(ctrl1),
743 ctrl2: positions.get_control_point(ctrl2),
744 to: current_position,
745 },
746 from,
747 to,
748 start_width,
749 end_width,
750 attributes,
751 );
752 }
753 IdEvent::End { close, .. } => {
754 validator.end();
755 self.end(close, attributes);
756 }
757 }
758
759 if let Some(err) = self.error {
760 self.output.abort_geometry();
761 return Err(err);
762 }
763 }
764
765 validator.build();
766 self.build()
767 }
768
769 fn tessellate_with_ids_fw(
770 mut self,
771 path: impl IntoIterator<Item = IdEvent>,
772 positions: &impl PositionStore,
773 attributes: &dyn AttributeStore,
774 ) -> TessellationResult {
775 let mut validator = DebugValidator::new();
776
777 let mut current_endpoint = EndpointId(u32::MAX);
778 let mut current_position = point(f32::NAN, f32::NAN);
779
780 let half_width = self.options.line_width * 0.5;
781
782 for evt in path.into_iter() {
783 match evt {
784 IdEvent::Begin { at } => {
785 validator.begin();
786 current_endpoint = at;
787 current_position = positions.get_endpoint(at);
788 self.may_need_empty_cap = false;
789 self.fixed_width_step(
790 EndpointData {
791 position: current_position,
792 half_width,
793 advancement: self.sub_path_start_advancement,
794 line_join: self.options.line_join,
795 src: VertexSource::Endpoint { id: at },
796 ..Default::default()
797 },
798 attributes,
799 );
800 }
801 IdEvent::Line { to, .. } => {
802 validator.edge();
803 current_endpoint = to;
804 current_position = positions.get_endpoint(to);
805 self.fixed_width_step(
806 EndpointData {
807 position: current_position,
808 half_width,
809 line_join: self.options.line_join,
810 src: VertexSource::Endpoint { id: to },
811 ..Default::default()
812 },
813 attributes,
814 );
815 }
816 IdEvent::Quadratic { ctrl, to, .. } => {
817 validator.edge();
818 let from = current_endpoint;
819 let from_pos = current_position;
820 current_endpoint = to;
821 current_position = positions.get_endpoint(to);
822
823 self.quadratic_bezier_to_fw(
824 &QuadraticBezierSegment {
825 from: from_pos,
826 ctrl: positions.get_control_point(ctrl),
827 to: current_position,
828 },
829 from,
830 to,
831 attributes,
832 );
833 }
834 IdEvent::Cubic {
835 ctrl1, ctrl2, to, ..
836 } => {
837 validator.edge();
838 let from = current_endpoint;
839 let from_pos = current_position;
840 current_endpoint = to;
841 current_position = positions.get_endpoint(to);
842
843 self.cubic_bezier_to_fw(
844 &CubicBezierSegment {
845 from: from_pos,
846 ctrl1: positions.get_control_point(ctrl1),
847 ctrl2: positions.get_control_point(ctrl2),
848 to: current_position,
849 },
850 from,
851 to,
852 attributes,
853 );
854 }
855 IdEvent::End { close, .. } => {
856 validator.end();
857 self.end(close, attributes);
858 }
859 }
860
861 if let Some(err) = self.error {
862 self.output.abort_geometry();
863 return Err(err);
864 }
865 }
866
867 validator.build();
868 self.build()
869 }
870
871 pub(crate) fn tessellate_fw(
873 mut self,
874 input: impl IntoIterator<Item = PathEvent>,
875 ) -> TessellationResult {
876 self.options.variable_line_width = None;
879
880 let mut validator = DebugValidator::new();
881
882 let mut id = EndpointId(0);
883 let mut current_position = point(f32::NAN, f32::NAN);
884
885 for evt in input {
886 match evt {
887 PathEvent::Begin { at } => {
888 validator.begin();
889 current_position = at;
890 self.begin_fw(at, id, &());
891 id.0 += 1;
892 }
893 PathEvent::Line { to, .. } => {
894 validator.edge();
895 current_position = to;
896 self.line_to_fw(to, id, &());
897 id.0 += 1;
898 }
899 PathEvent::Quadratic { ctrl, to, .. } => {
900 validator.edge();
901
902 let from = current_position;
903 current_position = to;
904 let prev_id = EndpointId(id.0 - 1);
905
906 self.quadratic_bezier_to_fw(
907 &QuadraticBezierSegment { from, ctrl, to },
908 prev_id,
909 id,
910 &(),
911 );
912
913 id.0 += 1;
914 }
915 PathEvent::Cubic {
916 ctrl1, ctrl2, to, ..
917 } => {
918 validator.edge();
919 let prev_id = EndpointId(id.0 - 1);
920
921 let from = current_position;
922 current_position = to;
923
924 self.cubic_bezier_to_fw(
925 &CubicBezierSegment {
926 from,
927 ctrl1,
928 ctrl2,
929 to,
930 },
931 prev_id,
932 id,
933 &(),
934 );
935
936 id.0 += 1;
937 }
938 PathEvent::End { close, .. } => {
939 validator.end();
940 self.end(close, &());
941 }
942 }
943
944 if let Some(err) = self.error {
945 self.output.abort_geometry();
946 return Err(err);
947 }
948 }
949
950 validator.build();
951 self.build()
952 }
953
954 pub(crate) fn begin(
955 &mut self,
956 position: Point,
957 endpoint: EndpointId,
958 width: f32,
959 attributes: &dyn AttributeStore,
960 ) {
961 self.may_need_empty_cap = false;
962 let half_width = width * 0.5;
963 self.step(
964 EndpointData {
965 position,
966 half_width,
967 advancement: self.sub_path_start_advancement,
968 line_join: self.options.line_join,
969 src: VertexSource::Endpoint { id: endpoint },
970 ..Default::default()
971 },
972 attributes,
973 );
974 }
975
976 pub(crate) fn line_to(
977 &mut self,
978 to: Point,
979 endpoint: EndpointId,
980 width: f32,
981 attributes: &dyn AttributeStore,
982 ) {
983 let half_width = width * 0.5;
984 self.step(
985 EndpointData {
986 position: to,
987 half_width,
988 line_join: self.options.line_join,
989 src: VertexSource::Endpoint { id: endpoint },
990 ..Default::default()
991 },
992 attributes,
993 );
994 }
995
996 pub(crate) fn quadratic_bezier_to(
997 &mut self,
998 curve: &QuadraticBezierSegment<f32>,
999 from_id: EndpointId,
1000 to_id: EndpointId,
1001 start_width: f32,
1002 end_width: f32,
1003 attributes: &dyn AttributeStore,
1004 ) {
1005 flatten_quad(
1006 curve,
1007 self.options.tolerance,
1008 &mut |position, t, is_flattening_step| {
1009 let src = if t == 1.0 {
1010 VertexSource::Endpoint { id: to_id }
1011 } else {
1012 VertexSource::Edge {
1013 from: from_id,
1014 to: to_id,
1015 t,
1016 }
1017 };
1018
1019 self.step(
1020 EndpointData {
1021 position,
1022 half_width: (start_width * (1.0 - t) + end_width * t) * 0.5,
1023 line_join: self.options.line_join,
1024 src,
1025 is_flattening_step,
1026 ..Default::default()
1027 },
1028 attributes,
1029 );
1030 },
1031 );
1032 }
1033
1034 pub(crate) fn cubic_bezier_to(
1035 &mut self,
1036 curve: &CubicBezierSegment<f32>,
1037 from_id: EndpointId,
1038 to_id: EndpointId,
1039 start_width: f32,
1040 end_width: f32,
1041 attributes: &dyn AttributeStore,
1042 ) {
1043 curve.for_each_flattened_with_t(self.options.tolerance, &mut |line, t| {
1044 let is_flattening_step = t.end != 1.0;
1045 let src = if is_flattening_step {
1046 VertexSource::Edge {
1047 from: from_id,
1048 to: to_id,
1049 t: t.end,
1050 }
1051 } else {
1052 VertexSource::Endpoint { id: to_id }
1053 };
1054
1055 self.step(
1056 EndpointData {
1057 position: line.to,
1058 half_width: (start_width * (1.0 - t.end) + end_width * t.end) * 0.5,
1059 line_join: self.options.line_join,
1060 src,
1061 is_flattening_step,
1062 ..Default::default()
1063 },
1064 attributes,
1065 );
1066 });
1067 }
1068
1069 pub(crate) fn begin_fw(
1070 &mut self,
1071 position: Point,
1072 endpoint: EndpointId,
1073 attributes: &dyn AttributeStore,
1074 ) {
1075 self.may_need_empty_cap = false;
1076 self.fixed_width_step(
1077 EndpointData {
1078 position,
1079 half_width: self.options.line_width * 0.5,
1080 advancement: self.sub_path_start_advancement,
1081 line_join: self.options.line_join,
1082 src: VertexSource::Endpoint { id: endpoint },
1083 ..Default::default()
1084 },
1085 attributes,
1086 );
1087 }
1088
1089 pub(crate) fn line_to_fw(
1090 &mut self,
1091 to: Point,
1092 endpoint: EndpointId,
1093 attributes: &dyn AttributeStore,
1094 ) {
1095 let half_width = self.options.line_width * 0.5;
1096 self.fixed_width_step(
1097 EndpointData {
1098 position: to,
1099 half_width,
1100 line_join: self.options.line_join,
1101 src: VertexSource::Endpoint { id: endpoint },
1102 ..Default::default()
1103 },
1104 attributes,
1105 );
1106 }
1107
1108 pub(crate) fn quadratic_bezier_to_fw(
1109 &mut self,
1110 curve: &QuadraticBezierSegment<f32>,
1111 from_id: EndpointId,
1112 to_id: EndpointId,
1113 attributes: &dyn AttributeStore,
1114 ) {
1115 let half_width = self.options.line_width * 0.5;
1116 flatten_quad(
1117 curve,
1118 self.options.tolerance,
1119 &mut |position, t, is_flattening_step| {
1120 let src = if t == 1.0 {
1121 VertexSource::Endpoint { id: to_id }
1122 } else {
1123 VertexSource::Edge {
1124 from: from_id,
1125 to: to_id,
1126 t,
1127 }
1128 };
1129
1130 self.fixed_width_step(
1131 EndpointData {
1132 position,
1133 half_width,
1134 line_join: self.options.line_join,
1135 src,
1136 is_flattening_step,
1137 ..Default::default()
1138 },
1139 attributes,
1140 );
1141 },
1142 );
1143 }
1144
1145 pub(crate) fn cubic_bezier_to_fw(
1146 &mut self,
1147 curve: &CubicBezierSegment<f32>,
1148 from_id: EndpointId,
1149 to_id: EndpointId,
1150 attributes: &dyn AttributeStore,
1151 ) {
1152 let half_width = self.options.line_width * 0.5;
1153 curve.for_each_flattened_with_t(self.options.tolerance, &mut |line, t| {
1154 let is_flattening_step = t.end != 1.0;
1155 let src = if is_flattening_step {
1156 VertexSource::Edge {
1157 from: from_id,
1158 to: to_id,
1159 t: t.end,
1160 }
1161 } else {
1162 VertexSource::Endpoint { id: to_id }
1163 };
1164
1165 self.fixed_width_step(
1166 EndpointData {
1167 position: line.to,
1168 half_width,
1169 line_join: self.options.line_join,
1170 src,
1171 is_flattening_step,
1172 ..Default::default()
1173 },
1174 attributes,
1175 );
1176 });
1177 }
1178
1179 pub(crate) fn end(&mut self, close: bool, attributes: &dyn AttributeStore) {
1180 self.may_need_empty_cap |= close && self.point_buffer.count() == 1;
1181 let e = if close && self.point_buffer.count() > 2 {
1182 self.close(attributes)
1183 } else {
1184 self.end_with_caps(attributes)
1185 };
1186
1187 if let Err(e) = e {
1188 self.error(e);
1189 }
1190
1191 self.point_buffer.clear();
1192 self.firsts.clear();
1193 }
1194
1195 pub(crate) fn build(self) -> TessellationResult {
1196 if let Some(err) = self.error {
1197 self.output.abort_geometry();
1198 return Err(err);
1199 }
1200
1201 self.output.end_geometry();
1202
1203 Ok(())
1204 }
1205
1206 fn close(&mut self, attributes: &dyn AttributeStore) -> Result<(), TessellationError> {
1207 if self.point_buffer.count() == 1 {
1208 self.tessellate_empty_cap(attributes)?;
1209 }
1210
1211 if self.point_buffer.count() <= 2 {
1212 return Ok(());
1213 }
1214
1215 assert!(!self.firsts.is_empty());
1216
1217 let mut p = self.firsts[0];
1218 let advancement = p.advancement;
1221 p.advancement = f32::NAN;
1222 let segment_added = if self.options.variable_line_width.is_some() {
1223 self.step_impl(p, attributes)?
1224 } else {
1225 self.fixed_width_step_impl(p, attributes)?
1226 };
1227
1228 if !segment_added {
1229 self.point_buffer.last_mut().position = p.position;
1238 }
1239
1240 if self.firsts.len() >= 2 {
1241 let p2 = self.firsts[1];
1242 if self.options.variable_line_width.is_some() {
1243 self.step_impl(p2, attributes)?;
1244 } else {
1245 self.fixed_width_step_impl(p2, attributes)?;
1246 }
1247
1248 let (p0, p1) = self.point_buffer.last_two_mut();
1249 self.vertex.src = p0.src;
1253 self.vertex.position_on_path = p0.position;
1254 self.vertex.half_width = p0.half_width;
1255 self.vertex.advancement = advancement;
1256 self.vertex.buffer_is_valid = false;
1257 for side in 0..2 {
1258 self.vertex.side = if side == SIDE_POSITIVE {
1259 Side::Positive
1260 } else {
1261 Side::Negative
1262 };
1263 self.vertex.normal = if let Some(pos) = p0.side_points[side].single_vertex {
1264 (pos - p0.position) / p0.half_width
1265 } else {
1266 (p0.side_points[side].next - p0.position) / p0.half_width
1267 };
1268
1269 let vertex = self
1270 .output
1271 .add_stroke_vertex(StrokeVertex(&mut self.vertex, attributes))?;
1272 p0.side_points[side].next_vertex = vertex;
1273 }
1274
1275 add_edge_triangles(p0, p1, self.output);
1276 }
1277
1278 Ok(())
1279 }
1280
1281 #[cold]
1282 fn tessellate_empty_cap(
1283 &mut self,
1284 attributes: &dyn AttributeStore,
1285 ) -> Result<(), TessellationError> {
1286 let point = self.point_buffer.get(0);
1287 self.vertex.advancement = point.advancement;
1288 self.vertex.src = point.src;
1289 self.vertex.half_width = point.half_width;
1290
1291 match self.options.start_cap {
1292 LineCap::Square => {
1293 crate::stroke::tessellate_empty_square_cap(
1296 point.position,
1297 &mut self.vertex,
1298 attributes,
1299 self.output,
1300 )?;
1301 }
1302 LineCap::Round => {
1303 crate::stroke::tessellate_empty_round_cap(
1305 point.position,
1306 &self.options,
1307 &mut self.vertex,
1308 attributes,
1309 self.output,
1310 )?;
1311 }
1312 _ => {}
1313 }
1314
1315 Ok(())
1316 }
1317
1318 fn points_are_too_close(&self, p0: Point, p1: Point) -> bool {
1319 (p0 - p1).square_length() < self.square_merge_threshold
1320 }
1321
1322 fn end_with_caps(&mut self, attributes: &dyn AttributeStore) -> Result<(), TessellationError> {
1323 let count = self.point_buffer.count();
1324
1325 if self.may_need_empty_cap && count == 1 {
1326 return self.tessellate_empty_cap(attributes);
1327 }
1328
1329 if count >= 2 {
1330 let (p0, p1) = self.point_buffer.last_two_mut();
1335 let mut p0 = *p0;
1336 let mut p1 = *p1;
1337
1338 if self.options.variable_line_width.is_none() {
1339 let tangent = (p1.position - p0.position).normalize();
1343 let n = vector(-tangent.y, tangent.x) * p1.half_width;
1344 p1.side_points[SIDE_POSITIVE].prev = p1.position + n;
1345 p1.side_points[SIDE_NEGATIVE].prev = p1.position - n;
1346 }
1347
1348 let is_first = count == 2;
1349 tessellate_last_edge(
1350 &p0,
1351 &mut p1,
1352 is_first,
1353 &self.options,
1354 &mut self.vertex,
1355 attributes,
1356 self.output,
1357 )?;
1358
1359 self.sub_path_start_advancement = p1.advancement;
1360
1361 if count > 2 {
1362 p0 = self.firsts[0];
1363 p1 = self.firsts[1];
1364 }
1365
1366 tessellate_first_edge(
1368 &mut p0,
1369 &p1,
1370 &self.options,
1371 &mut self.vertex,
1372 attributes,
1373 self.output,
1374 )?;
1375 }
1376
1377 Ok(())
1378 }
1379
1380 pub(crate) fn step_impl(
1381 &mut self,
1382 mut next: EndpointData,
1383 attributes: &dyn AttributeStore,
1384 ) -> Result<bool, TessellationError> {
1385 let count = self.point_buffer.count();
1386
1387 debug_assert!(self.options.variable_line_width.is_some());
1388
1389 if count > 0 && self.points_are_too_close(self.point_buffer.last().position, next.position)
1390 {
1391 if count == 1 {
1392 self.may_need_empty_cap = true;
1395 }
1396 return Ok(false);
1403 }
1404
1405 if count > 0 {
1406 let join = self.point_buffer.last_mut();
1407 if !join.is_flattening_step || !next.is_flattening_step {
1410 compute_edge_attachment_positions(join, &mut next);
1411 }
1412 }
1413
1414 let mut skip = false;
1415 if count > 1 {
1416 let (prev, join) = self.point_buffer.last_two_mut();
1417 nan_check!(join.advancement);
1418 nan_check!(prev.advancement);
1419
1420 self.vertex.src = join.src;
1421 self.vertex.position_on_path = join.position;
1422 self.vertex.half_width = join.half_width;
1423 self.vertex.advancement = join.advancement;
1424 self.vertex.buffer_is_valid = false;
1425 let fast_path = if join.is_flattening_step {
1428 let v0 = join.position - prev.position;
1429 let v1 = next.position - join.position;
1430 v0.dot(v1) > 0.0
1431 } else {
1432 false
1433 };
1434 if fast_path {
1435 join.line_join = LineJoin::Miter;
1436 skip = flattened_step(
1438 prev,
1439 join,
1440 &mut next,
1441 &mut self.vertex,
1442 attributes,
1443 self.output,
1444 )?;
1445 } else {
1446 compute_join_side_positions(
1447 prev,
1448 join,
1449 &next,
1450 self.options.miter_limit,
1451 SIDE_POSITIVE,
1452 );
1453 compute_join_side_positions(
1454 prev,
1455 join,
1456 &next,
1457 self.options.miter_limit,
1458 SIDE_NEGATIVE,
1459 );
1460
1461 if join.side_points[SIDE_POSITIVE].single_vertex.is_some() {
1463 join.fold[SIDE_NEGATIVE] = false;
1464 }
1465 if join.side_points[SIDE_NEGATIVE].single_vertex.is_some() {
1466 join.fold[SIDE_POSITIVE] = false;
1467 }
1468
1469 add_join_base_vertices(
1470 join,
1471 &mut self.vertex,
1472 attributes,
1473 self.output,
1474 Side::Negative,
1475 )?;
1476 add_join_base_vertices(
1477 join,
1478 &mut self.vertex,
1479 attributes,
1480 self.output,
1481 Side::Positive,
1482 )?;
1483 }
1484
1485 if !skip {
1486 if count > 2 {
1487 add_edge_triangles(prev, join, self.output);
1488 }
1489
1490 tessellate_join(
1491 join,
1492 &self.options,
1493 &mut self.vertex,
1494 attributes,
1495 self.output,
1496 )?;
1497
1498 if count == 2 {
1499 self.firsts.push(*prev);
1500 self.firsts.push(*join);
1501 }
1502 }
1503 }
1504
1505 if skip {
1506 self.point_buffer.replace_last(next);
1507 } else {
1508 self.point_buffer.push(next);
1509 }
1510
1511 Ok(true)
1512 }
1513
1514 #[cfg_attr(feature = "profiling", inline(never))]
1515 pub(crate) fn step(&mut self, next: EndpointData, attributes: &dyn AttributeStore) {
1516 if let Err(e) = self.step_impl(next, attributes) {
1517 self.error(e);
1518 }
1519 }
1520
1521 pub(crate) fn fixed_width_step_impl(
1522 &mut self,
1523 mut next: EndpointData,
1524 attributes: &dyn AttributeStore,
1525 ) -> Result<bool, TessellationError> {
1526 let count = self.point_buffer.count();
1527
1528 debug_assert!(self.options.variable_line_width.is_none());
1529
1530 if count > 0 {
1531 if self.points_are_too_close(self.point_buffer.last().position, next.position) {
1532 if count == 1 {
1533 self.may_need_empty_cap = true;
1534 }
1535 return Ok(false);
1536 }
1537
1538 if count == 1 {
1539 let first = self.point_buffer.last_mut();
1543 let edge = next.position - first.position;
1544 let length = edge.length();
1545
1546 if next.advancement.is_nan() {
1547 nan_check!(first.advancement);
1548 nan_check!(length);
1549 next.advancement = first.advancement + length;
1550 }
1551
1552 let tangent = edge / length;
1553 let n = vector(-tangent.y, tangent.x) * next.half_width;
1554 first.side_points[SIDE_POSITIVE].next = first.position + n;
1555 first.side_points[SIDE_NEGATIVE].next = first.position - n;
1556 }
1557 }
1558
1559 if count > 1 {
1560 let (prev, join) = self.point_buffer.last_two_mut();
1561
1562 self.vertex.src = join.src;
1563 self.vertex.position_on_path = join.position;
1564 self.vertex.half_width = join.half_width;
1565 self.vertex.buffer_is_valid = false;
1566 let fast_path = if join.is_flattening_step {
1569 let v0 = join.position - prev.position;
1570 let v1 = next.position - join.position;
1571 v0.dot(v1) > 0.0
1572 } else {
1573 false
1574 };
1575 if fast_path {
1576 join.line_join = LineJoin::Miter;
1577 flattened_step(
1579 prev,
1580 join,
1581 &mut next,
1582 &mut self.vertex,
1583 attributes,
1584 self.output,
1585 )?;
1586 } else {
1587 compute_join_side_positions_fixed_width(
1588 prev,
1589 join,
1590 &next,
1591 self.options.miter_limit,
1592 &mut self.vertex,
1593 )?;
1594
1595 add_join_base_vertices(
1596 join,
1597 &mut self.vertex,
1598 attributes,
1599 self.output,
1600 Side::Negative,
1601 )?;
1602 add_join_base_vertices(
1603 join,
1604 &mut self.vertex,
1605 attributes,
1606 self.output,
1607 Side::Positive,
1608 )?;
1609 }
1610
1611 if count > 2 {
1612 add_edge_triangles(prev, join, self.output);
1613 }
1614
1615 tessellate_join(
1616 join,
1617 &self.options,
1618 &mut self.vertex,
1619 attributes,
1620 self.output,
1621 )?;
1622
1623 if count == 2 {
1624 self.firsts.push(*prev);
1625 self.firsts.push(*join);
1626 }
1627 }
1628
1629 self.point_buffer.push(next);
1630
1631 Ok(true)
1632 }
1633
1634 #[cfg_attr(feature = "profiling", inline(never))]
1635 pub(crate) fn fixed_width_step(&mut self, next: EndpointData, attributes: &dyn AttributeStore) {
1636 if let Err(e) = self.fixed_width_step_impl(next, attributes) {
1637 self.error(e);
1638 }
1639 }
1640}
1641
1642#[cfg_attr(feature = "profiling", inline(never))]
1643fn compute_join_side_positions_fixed_width(
1644 prev: &EndpointData,
1645 join: &mut EndpointData,
1646 next: &EndpointData,
1647 miter_limit: f32,
1648 vertex: &mut StrokeVertexData,
1649) -> Result<(), TessellationError> {
1650 let prev_tangent = join.position - prev.position;
1651 let next_tangent = next.position - join.position;
1652 let prev_length = prev_tangent.length();
1653 let next_length = next_tangent.length();
1654 let prev_tangent = prev_tangent / prev_length;
1655 let next_tangent = next_tangent / next_length;
1656
1657 if join.advancement.is_nan() {
1658 nan_check!(prev.advancement);
1659 nan_check!(prev_length);
1660 join.advancement = prev.advancement + prev_length;
1661 }
1662 vertex.advancement = join.advancement;
1663
1664 let normal = compute_normal(prev_tangent, next_tangent);
1665 let (front_side, front_normal) = if prev_tangent.cross(next_tangent) >= 0.0 {
1666 (SIDE_NEGATIVE, -normal)
1667 } else {
1668 (SIDE_POSITIVE, normal)
1669 };
1670
1671 let back_side = 1 - front_side;
1672
1673 let extruded_normal = front_normal * vertex.half_width;
1686 let unclipped_miter = (join.line_join == LineJoin::Miter
1687 || join.line_join == LineJoin::MiterClip)
1688 && !miter_limit_is_exceeded(front_normal, miter_limit);
1689
1690 let mut fold = false;
1691 let angle_is_sharp = next_tangent.dot(prev_tangent) < 0.0;
1692 if !unclipped_miter && angle_is_sharp {
1693 let d_next = extruded_normal.dot(-next_tangent) - next_length;
1696 let d_prev = extruded_normal.dot(prev_tangent) - prev_length;
1697 if d_next.min(d_prev) > 0.0 || normal.square_length() < 1e-5 {
1698 join.fold[front_side] = true;
1702 fold = true;
1703 }
1704 }
1705
1706 let n0 = vector(-prev_tangent.y, prev_tangent.x) * vertex.half_width;
1707 let n1 = vector(-next_tangent.y, next_tangent.x) * vertex.half_width;
1708 join.side_points[SIDE_POSITIVE].prev = join.position + n0;
1709 join.side_points[SIDE_POSITIVE].next = join.position + n1;
1710 join.side_points[SIDE_NEGATIVE].prev = join.position - n0;
1711 join.side_points[SIDE_NEGATIVE].next = join.position - n1;
1712
1713 if !fold {
1714 let miter_pos = [
1715 join.position + normal * vertex.half_width,
1716 join.position - normal * vertex.half_width,
1717 ];
1718
1719 join.side_points[back_side].single_vertex = Some(miter_pos[back_side]);
1720 if unclipped_miter {
1721 join.side_points[front_side].single_vertex = Some(miter_pos[front_side]);
1722 } else if join.line_join == LineJoin::MiterClip {
1723 let n0 = join.side_points[front_side].prev - join.position;
1724 let n1 = join.side_points[front_side].next - join.position;
1725 let (prev_normal, next_normal) =
1726 get_clip_intersections(n0, n1, front_normal, miter_limit * 0.5 * vertex.half_width);
1727 join.side_points[front_side].prev = join.position + prev_normal;
1728 join.side_points[front_side].next = join.position + next_normal;
1729 }
1730 }
1731
1732 Ok(())
1733}
1734
1735#[cfg_attr(feature = "profiling", inline(never))]
1743fn flattened_step(
1744 prev: &mut EndpointData,
1745 join: &mut EndpointData,
1746 next: &mut EndpointData,
1747 vertex: &mut StrokeVertexData,
1748 attributes: &dyn AttributeStore,
1749 output: &mut dyn StrokeGeometryBuilder,
1750) -> Result<bool, TessellationError> {
1751 let prev_edge = join.position - prev.position;
1752 let prev_length = prev_edge.length();
1753 let prev_tangent = prev_edge / prev_length;
1754 let next_edge = next.position - join.position;
1755 let next_length = next_edge.length();
1756 let next_tangent = next_edge / next_length;
1757 let normal = compute_normal(prev_tangent, next_tangent);
1758
1759 if join.advancement.is_nan() {
1760 nan_check!(prev.advancement);
1761 nan_check!(prev_length);
1762 join.advancement = prev.advancement + prev_length;
1763 }
1764
1765 if next.advancement.is_nan() {
1766 nan_check!(join.advancement);
1767 nan_check!(next_length);
1768 next.advancement = join.advancement + next_length;
1769 }
1770
1771 vertex.advancement = join.advancement;
1772
1773 let p0 = join.position + normal * vertex.half_width;
1774 nan_check!(p0);
1775 join.side_points[SIDE_POSITIVE].prev = p0;
1776 join.side_points[SIDE_POSITIVE].next = p0;
1777 join.side_points[SIDE_POSITIVE].single_vertex = Some(p0);
1778
1779 let p1 = join.position - normal * vertex.half_width;
1780 nan_check!(p1);
1781 join.side_points[SIDE_NEGATIVE].prev = p1;
1782 join.side_points[SIDE_NEGATIVE].next = p1;
1783 join.side_points[SIDE_NEGATIVE].single_vertex = Some(p1);
1784
1785 let v0 = p0 - prev.side_points[SIDE_POSITIVE].next;
1786 let v1 = p1 - prev.side_points[SIDE_NEGATIVE].next;
1787 if prev_edge.dot(v0) < 0.0 && prev_edge.dot(v1) < 0.0 {
1788 return Ok(true);
1789 }
1790
1791 vertex.normal = normal;
1792 vertex.side = Side::Positive;
1793 let pos_vertex = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
1794
1795 vertex.normal = -normal;
1796 vertex.side = Side::Negative;
1797 let neg_vertex = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
1798
1799 join.side_points[SIDE_POSITIVE].prev_vertex = pos_vertex;
1800 join.side_points[SIDE_POSITIVE].next_vertex = pos_vertex;
1801
1802 join.side_points[SIDE_NEGATIVE].prev_vertex = neg_vertex;
1803 join.side_points[SIDE_NEGATIVE].next_vertex = neg_vertex;
1804
1805 Ok(false)
1806}
1807
1808#[cfg_attr(feature = "profiling", inline(never))]
1809fn compute_edge_attachment_positions(p0: &mut EndpointData, p1: &mut EndpointData) {
1810 let edge = p1.position - p0.position;
1811 let d = edge.length();
1812 let edge_angle = edge.angle_from_x_axis().radians;
1813
1814 let sin_vwidth_angle = (p1.half_width - p0.half_width) / d;
1817 let mut vwidth_angle = sin_vwidth_angle.asin();
1818 if vwidth_angle.is_nan() {
1824 vwidth_angle = 0.0;
1825 }
1826
1827 nan_check!(d, p0.half_width, p1.half_width, vwidth_angle);
1828
1829 compute_side_attachment_positions(p0, p1, edge_angle, vwidth_angle, SIDE_POSITIVE);
1830 compute_side_attachment_positions(p0, p1, edge_angle, vwidth_angle, SIDE_NEGATIVE);
1831
1832 if p1.advancement.is_nan() {
1833 nan_check!(p0.advancement, d);
1834 p1.advancement = p0.advancement + d;
1835 }
1836}
1837
1838fn compute_side_attachment_positions(
1839 p0: &mut EndpointData,
1840 p1: &mut EndpointData,
1841 edge_angle: f32,
1842 vwidth_angle: f32,
1843 side: usize,
1844) {
1845 nan_check!(
1846 edge_angle,
1847 vwidth_angle,
1848 p0.position,
1849 p1.position,
1850 p0.half_width,
1851 p1.half_width
1852 );
1853
1854 let nl = side_sign(side);
1855
1856 let normal_angle = edge_angle + nl * (PI * 0.5 + vwidth_angle);
1857 let normal = vector(normal_angle.cos(), normal_angle.sin());
1858
1859 nan_check!(normal);
1860
1861 p0.side_points[side].next = p0.position + normal * p0.half_width;
1862 p1.side_points[side].prev = p1.position + normal * p1.half_width;
1863
1864 nan_check!(p0.side_points[side].next);
1865 nan_check!(p1.side_points[side].prev);
1866}
1867
1868#[cfg_attr(feature = "profiling", inline(never))]
1869fn add_edge_triangles(
1870 p0: &EndpointData,
1871 p1: &EndpointData,
1872 output: &mut dyn StrokeGeometryBuilder,
1873) {
1874 let mut p0_neg = p0.side_points[SIDE_NEGATIVE].next_vertex;
1875 let mut p0_pos = p0.side_points[SIDE_POSITIVE].next_vertex;
1876 let mut p1_neg = p1.side_points[SIDE_NEGATIVE].prev_vertex;
1877 let mut p1_pos = p1.side_points[SIDE_POSITIVE].prev_vertex;
1878
1879 if p0.fold[SIDE_POSITIVE] {
1880 p0_neg = p0.side_points[SIDE_POSITIVE].prev_vertex;
1881 }
1882 if p0.fold[SIDE_NEGATIVE] {
1883 p0_pos = p0.side_points[SIDE_NEGATIVE].prev_vertex;
1884 }
1885 if p1.fold[SIDE_POSITIVE] {
1886 p1_neg = p1.side_points[SIDE_POSITIVE].next_vertex;
1887 }
1888 if p1.fold[SIDE_NEGATIVE] {
1889 p1_pos = p1.side_points[SIDE_NEGATIVE].next_vertex;
1890 }
1891
1892 if p0_neg == p1_pos {
1894 return;
1895 }
1896
1897 if p0_neg != p0_pos && p0_pos != p1_pos {
1898 output.add_triangle(p0_neg, p0_pos, p1_pos);
1899 }
1900
1901 if p0_neg != p1_neg && p1_pos != p1_neg {
1902 output.add_triangle(p0_neg, p1_pos, p1_neg);
1903 }
1904}
1905
1906#[cfg_attr(feature = "profiling", inline(never))]
1907fn tessellate_join(
1908 join: &mut EndpointData,
1909 options: &StrokeOptions,
1910 vertex: &mut StrokeVertexData,
1911 attributes: &dyn AttributeStore,
1912 output: &mut dyn StrokeGeometryBuilder,
1913) -> Result<(), TessellationError> {
1914 let side_needs_join = [
1915 join.side_points[SIDE_POSITIVE].single_vertex.is_none() && !join.fold[SIDE_NEGATIVE],
1916 join.side_points[SIDE_NEGATIVE].single_vertex.is_none() && !join.fold[SIDE_POSITIVE],
1917 ];
1918
1919 if !join.fold[SIDE_POSITIVE] && !join.fold[SIDE_NEGATIVE] {
1920 match side_needs_join {
1922 [true, true] => {
1923 output.add_triangle(
1924 join.side_points[SIDE_POSITIVE].prev_vertex,
1925 join.side_points[SIDE_POSITIVE].next_vertex,
1926 join.side_points[SIDE_NEGATIVE].next_vertex,
1927 );
1928
1929 output.add_triangle(
1930 join.side_points[SIDE_POSITIVE].prev_vertex,
1931 join.side_points[SIDE_NEGATIVE].next_vertex,
1932 join.side_points[SIDE_NEGATIVE].prev_vertex,
1933 );
1934 }
1935 [false, true] => {
1936 output.add_triangle(
1937 join.side_points[SIDE_NEGATIVE].prev_vertex,
1938 join.side_points[SIDE_POSITIVE].prev_vertex,
1939 join.side_points[SIDE_NEGATIVE].next_vertex,
1940 );
1941 }
1942 [true, false] => {
1943 output.add_triangle(
1944 join.side_points[SIDE_NEGATIVE].prev_vertex,
1945 join.side_points[SIDE_POSITIVE].prev_vertex,
1946 join.side_points[SIDE_POSITIVE].next_vertex,
1947 );
1948 }
1949 [false, false] => {}
1950 }
1951 }
1952
1953 for side in 0..2 {
1955 if !side_needs_join[side] {
1956 continue;
1957 }
1958
1959 if join.line_join == LineJoin::Round {
1960 tessellate_round_join(join, side, options, vertex, attributes, output)?;
1961 }
1962 }
1963
1964 Ok(())
1965}
1966
1967#[cfg_attr(feature = "profiling", inline(never))]
1968fn tessellate_round_join(
1969 join: &mut EndpointData,
1970 side: usize,
1971 options: &StrokeOptions,
1972 vertex: &mut StrokeVertexData,
1973 attributes: &dyn AttributeStore,
1974 output: &mut dyn StrokeGeometryBuilder,
1975) -> Result<(), TessellationError> {
1976 let center = join.position;
1977 let radius = join.half_width;
1978 let start_normal = join.side_points[side].prev - center;
1979 let end_normal = join.side_points[side].next - center;
1980
1981 let mut start_vertex = join.side_points[side].prev_vertex;
1982 let mut end_vertex = join.side_points[side].next_vertex;
1983
1984 let angle_sign = if side == SIDE_NEGATIVE { 1.0 } else { -1.0 };
1985
1986 let mut start_angle = start_normal.angle_from_x_axis();
1987 let mut diff = start_angle.angle_to(end_normal.angle_from_x_axis());
1988
1989 if diff.radians * angle_sign < 0.0 {
1991 diff.radians = angle_sign * (2.0 * PI - diff.radians.abs());
1992 }
1993 let mut end_angle = start_angle + diff;
1994
1995 if side == SIDE_NEGATIVE {
1996 core::mem::swap(&mut start_angle, &mut end_angle);
1998 core::mem::swap(&mut start_vertex, &mut end_vertex);
1999 }
2000
2001 let step = circle_flattening_step(radius, options.tolerance);
2003 let num_segments = (diff.radians.abs() / step).ceil();
2004 let num_subdivisions = num_segments.log2().round() as u32;
2005
2006 vertex.side = if side == SIDE_POSITIVE {
2007 Side::Positive
2008 } else {
2009 Side::Negative
2010 };
2011
2012 crate::stroke::tessellate_arc(
2013 (start_angle.radians, end_angle.radians),
2014 start_vertex,
2015 end_vertex,
2016 num_subdivisions,
2017 vertex,
2018 attributes,
2019 output,
2020 )
2021}
2022
2023#[cfg_attr(feature = "profiling", inline(never))]
2024fn add_join_base_vertices(
2025 join: &mut EndpointData,
2026 vertex: &mut StrokeVertexData,
2027 attributes: &dyn AttributeStore,
2028 output: &mut dyn StrokeGeometryBuilder,
2029 side: Side,
2030) -> Result<(), TessellationError> {
2031 vertex.side = side;
2032
2033 let side = match side {
2034 Side::Positive => SIDE_POSITIVE,
2035 Side::Negative => SIDE_NEGATIVE,
2036 };
2037
2038 if let Some(pos) = join.side_points[side].single_vertex {
2039 vertex.normal = (pos - join.position) / join.half_width;
2040 let vertex = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2041 join.side_points[side].prev_vertex = vertex;
2042 join.side_points[side].next_vertex = vertex;
2043 } else {
2044 vertex.normal = (join.side_points[side].prev - join.position) / join.half_width;
2045 let prev_vertex = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2046
2047 vertex.normal = (join.side_points[side].next - join.position) / join.half_width;
2048 let next_vertex = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2049
2050 join.side_points[side].prev_vertex = prev_vertex;
2051 join.side_points[side].next_vertex = next_vertex;
2052 }
2053
2054 Ok(())
2055}
2056
2057#[cfg_attr(feature = "profiling", inline(never))]
2060fn compute_join_side_positions(
2061 prev: &EndpointData,
2062 join: &mut EndpointData,
2063 next: &EndpointData,
2064 miter_limit: f32,
2065 side: usize,
2066) {
2067 nan_check!(join.position);
2068 nan_check!(prev.side_points[side].next);
2069 nan_check!(join.side_points[side].next);
2070
2071 let sign = side_sign(side);
2072 let v0 = (join.side_points[side].prev - prev.side_points[side].next).normalize();
2073 let v1 = (next.side_points[side].prev - join.side_points[side].next).normalize();
2074 let inward = v0.cross(v1) * sign > 0.0;
2075 let forward = v0.dot(v1) > 0.0;
2076
2077 let normal = compute_normal(v0, v1) * sign;
2078 let path_v0 = (join.position - prev.position).normalize();
2079 let path_v1 = (next.position - join.position).normalize();
2080
2081 nan_check!(v0, v1);
2082
2083 let normal_same_side = (v0 + v1).dot(path_v0 + path_v1) >= 0.0;
2084
2085 let angle_is_sharp = inward && !forward && normal_same_side;
2088 if angle_is_sharp {
2089 let extruded_normal = normal * join.half_width;
2092 let prev_length = join.advancement - prev.advancement;
2093 let next_length = next.advancement - join.advancement;
2094 let d_next = extruded_normal.dot(v1) - next_length;
2095 let d_prev = extruded_normal.dot(-v0) - prev_length;
2096
2097 if d_next.min(d_prev) >= 0.0 || normal.square_length() < 1e-5 {
2098 join.fold[side] = true;
2102 }
2103 }
2104
2105 let concave = inward && normal_same_side && !join.fold[side];
2107
2108 if concave
2109 || ((join.line_join == LineJoin::Miter || join.line_join == LineJoin::MiterClip)
2110 && !miter_limit_is_exceeded(normal, miter_limit))
2111 {
2112 let p = join.position + normal * join.half_width;
2113 join.side_points[side].single_vertex = Some(p);
2114 } else if join.line_join == LineJoin::MiterClip {
2115 let n0 = join.side_points[side].prev - join.position;
2119 let n1 = join.side_points[side].next - join.position;
2120 let (prev_normal, next_normal) =
2121 get_clip_intersections(n0, n1, normal, miter_limit * 0.5 * join.half_width);
2122 join.side_points[side].prev = join.position + prev_normal;
2123 join.side_points[side].next = join.position + next_normal;
2124 nan_check!(n0, n1, prev_normal, next_normal);
2125 nan_check!(join.side_points[side].prev);
2126 nan_check!(join.side_points[side].next);
2127 }
2128}
2129
2130fn tessellate_last_edge(
2131 p0: &EndpointData,
2132 p1: &mut EndpointData,
2133 is_first_edge: bool,
2134 options: &StrokeOptions,
2135 vertex: &mut StrokeVertexData,
2136 attributes: &dyn AttributeStore,
2137 output: &mut dyn StrokeGeometryBuilder,
2138) -> Result<(), TessellationError> {
2139 let v = p1.position - p0.position;
2140 p1.advancement = p0.advancement + v.length();
2141
2142 vertex.src = p1.src;
2143 vertex.position_on_path = p1.position;
2144 vertex.advancement = p1.advancement;
2145 vertex.half_width = p1.half_width;
2146 vertex.buffer_is_valid = false;
2147
2148 let sides = [Side::Positive, Side::Negative];
2149
2150 for side in 0..2 {
2151 let side_position = p1.side_points[side].prev;
2152 let clip = match options.end_cap {
2153 LineCap::Square => Some(p1.half_width),
2154 LineCap::Butt => Some(0.0),
2155 _ => None,
2156 };
2157
2158 if let Some(clip) = clip {
2159 let normal = (p1.position - p0.position).normalize();
2160 let clip_line = Line {
2161 point: p1.position + normal * clip,
2162 vector: tangent(normal),
2163 };
2164 let side_line = Line {
2165 point: side_position,
2166 vector: side_position - p0.side_points[side].next,
2167 };
2168
2169 let intersection = clip_line
2170 .to_f64()
2171 .intersection(&side_line.to_f64())
2172 .map(|p| p.to_f32())
2173 .unwrap_or(p1.side_points[side].prev);
2174
2175 p1.side_points[side].prev = intersection;
2176 }
2177
2178 vertex.side = sides[side];
2179 vertex.normal = (p1.side_points[side].prev - p1.position) / p1.half_width;
2180 let prev_vertex = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2181 p1.side_points[side].prev_vertex = prev_vertex;
2182 }
2183
2184 if !is_first_edge {
2186 add_edge_triangles(p0, p1, output);
2187 }
2188
2189 if options.end_cap == LineCap::Round {
2190 crate::stroke::tessellate_round_cap(
2191 p1.position,
2192 p1.half_width,
2193 p1.side_points[SIDE_POSITIVE].prev - p1.position,
2194 p1.side_points[SIDE_POSITIVE].prev_vertex,
2195 p1.side_points[SIDE_NEGATIVE].prev_vertex,
2196 v,
2197 options,
2198 false,
2199 vertex,
2200 attributes,
2201 output,
2202 )?;
2203 }
2204
2205 Ok(())
2206}
2207
2208fn tessellate_first_edge(
2209 first: &mut EndpointData,
2210 second: &EndpointData,
2211 options: &StrokeOptions,
2212 vertex: &mut StrokeVertexData,
2213 attributes: &dyn AttributeStore,
2214 output: &mut dyn StrokeGeometryBuilder,
2215) -> Result<(), TessellationError> {
2216 vertex.src = first.src;
2217 vertex.position_on_path = first.position;
2218 vertex.advancement = first.advancement;
2219 vertex.half_width = first.half_width;
2220 vertex.buffer_is_valid = false;
2221
2222 let sides = [Side::Positive, Side::Negative];
2223
2224 for side in 0..2 {
2225 let mut side_position = first.side_points[side].next;
2226 let clip = match options.start_cap {
2227 LineCap::Square => Some(first.half_width),
2228 LineCap::Butt => Some(0.0),
2229 _ => None,
2230 };
2231
2232 if let Some(clip) = clip {
2233 let normal = (first.position - second.position).normalize();
2234 let clip_line = Line {
2235 point: first.position + normal * clip,
2236 vector: tangent(normal),
2237 };
2238 let side_line = Line {
2239 point: side_position,
2240 vector: side_position - second.side_points[side].prev,
2241 };
2242
2243 let intersection = clip_line
2244 .to_f64()
2245 .intersection(&side_line.to_f64())
2246 .map(|p| p.to_f32())
2247 .unwrap_or(first.side_points[side].next);
2248 side_position = intersection;
2249 }
2250
2251 vertex.side = sides[side];
2252 vertex.normal = (side_position - first.position) / first.half_width;
2253 first.side_points[side].next_vertex =
2254 output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2255 }
2256
2257 add_edge_triangles(first, second, output);
2259
2260 match options.start_cap {
2261 LineCap::Round => crate::stroke::tessellate_round_cap(
2262 first.position,
2263 first.half_width,
2264 first.side_points[SIDE_NEGATIVE].next - first.position,
2265 first.side_points[SIDE_NEGATIVE].next_vertex,
2266 first.side_points[SIDE_POSITIVE].next_vertex,
2267 first.position - second.position,
2268 options,
2269 true,
2270 vertex,
2271 attributes,
2272 output,
2273 ),
2274 _ => Ok(()),
2275 }
2276}
2277
2278#[cfg_attr(feature = "profiling", inline(never))]
2279fn get_clip_intersections(
2280 previous_normal: Vector,
2281 next_normal: Vector,
2282 normal: Vector,
2283 clip_distance: f32,
2284) -> (Vector, Vector) {
2285 let clip_line = Line {
2286 point: normal.normalize().to_point() * clip_distance,
2287 vector: tangent(normal),
2288 }
2289 .to_f64();
2290
2291 let prev_line = Line {
2292 point: previous_normal.to_point(),
2293 vector: tangent(previous_normal),
2294 }
2295 .to_f64();
2296
2297 let next_line = Line {
2298 point: next_normal.to_point(),
2299 vector: tangent(next_normal),
2300 }
2301 .to_f64();
2302
2303 let i1 = clip_line
2304 .intersection(&prev_line)
2305 .map(|p| p.to_f32())
2306 .unwrap_or_else(|| normal.to_point())
2307 .to_vector();
2308 let i2 = clip_line
2309 .intersection(&next_line)
2310 .map(|p| p.to_f32())
2311 .unwrap_or_else(|| normal.to_point())
2312 .to_vector();
2313
2314 (i1, i2)
2315}
2316
2317fn miter_limit_is_exceeded(normal: Vector, miter_limit: f32) -> bool {
2321 normal.square_length() > miter_limit * miter_limit * 4.0
2322}
2323
2324fn side_sign(side: usize) -> f32 {
2325 if side == SIDE_NEGATIVE {
2326 -1.0
2327 } else {
2328 1.0
2329 }
2330}
2331
2332#[inline(never)]
2336fn approximate_thin_rectangle(builder: &mut StrokeBuilder, rect: &Box2D, attributes: Attributes) {
2337 let (from, to, d) = if rect.width() > rect.height() {
2338 let d = rect.height() * 0.5;
2339 let min_x = rect.min.x + d;
2340 let max_x = rect.max.x - d;
2341 let y = (rect.min.y + rect.max.y) * 0.5;
2342
2343 (point(min_x, y), point(max_x, y), d)
2344 } else {
2345 let d = rect.width() * 0.5;
2346 let min_y = rect.min.y + d;
2347 let max_y = rect.max.y - d;
2348 let x = (rect.min.x + rect.max.x) * 0.5;
2349
2350 (point(x, min_y), point(x, max_y), d)
2351 };
2352
2353 let options = builder.builder.options;
2355
2356 let cap = match options.line_join {
2357 LineJoin::Round => LineCap::Round,
2358 _ => LineCap::Square,
2359 };
2360
2361 builder.builder.options.line_width += d;
2362 builder.builder.options.start_cap = cap;
2363 builder.builder.options.end_cap = cap;
2364
2365 builder.add_line_segment(&LineSegment { from, to }, attributes);
2366
2367 builder.builder.options = options;
2369}
2370
2371struct PointBuffer {
2372 points: [EndpointData; 3],
2373 start: usize,
2374 count: usize,
2375}
2376
2377impl PointBuffer {
2378 fn new() -> Self {
2379 PointBuffer {
2380 points: [EndpointData::default(); 3],
2381 start: 0,
2382 count: 0,
2383 }
2384 }
2385
2386 fn push(&mut self, point: EndpointData) {
2387 if self.count < 3 {
2388 self.points[self.count] = point;
2389 self.count += 1;
2390 return;
2391 }
2392
2393 self.points[self.start] = point;
2394 self.start += 1;
2395 if self.start == 3 {
2396 self.start = 0;
2397 }
2398 }
2399
2400 fn replace_last(&mut self, point: EndpointData) {
2401 let mut idx = self.start;
2402 if idx == 0 {
2403 idx = self.count;
2404 }
2405 self.points[idx - 1] = point;
2406 }
2407
2408 fn clear(&mut self) {
2409 self.count = 0;
2410 self.start = 0;
2411 }
2412
2413 fn count(&self) -> usize {
2414 self.count
2415 }
2416
2417 fn get(&self, idx: usize) -> &EndpointData {
2418 assert!(idx < self.count);
2419 let idx = (idx + self.start) % 3;
2420
2421 &self.points[idx]
2422 }
2423
2424 fn get_reverse(&self, idx: usize) -> &EndpointData {
2425 assert!(idx < self.count);
2426 self.get(self.count - 1 - idx)
2427 }
2428
2429 fn get_mut(&mut self, idx: usize) -> &mut EndpointData {
2430 assert!(idx < self.count);
2431 let idx = (idx + self.start) % 3;
2432
2433 &mut self.points[idx]
2434 }
2435
2436 fn last(&self) -> &EndpointData {
2437 assert!(self.count > 0);
2438 self.get(self.count - 1)
2439 }
2440
2441 fn last_mut(&mut self) -> &mut EndpointData {
2442 assert!(self.count > 0);
2443 self.get_mut(self.count - 1)
2444 }
2445
2446 fn last_two_mut(&mut self) -> (&mut EndpointData, &mut EndpointData) {
2447 assert!(self.count >= 2);
2448 let i0 = (self.start + self.count - 1) % 3;
2449 let i1 = (self.start + self.count - 2) % 3;
2450 unsafe {
2451 (
2452 &mut *(self.points.get_unchecked_mut(i1) as *mut _),
2453 &mut *(self.points.get_unchecked_mut(i0) as *mut _),
2454 )
2455 }
2456 }
2457}
2458
2459pub(crate) fn tessellate_round_cap(
2460 center: Point,
2461 radius: f32,
2462 start_normal: Vector,
2463 start_vertex: VertexId,
2464 end_vertex: VertexId,
2465 edge_normal: Vector,
2466 options: &StrokeOptions,
2467 is_start: bool,
2468 vertex: &mut StrokeVertexData,
2469 attributes: &dyn AttributeStore,
2470 output: &mut dyn StrokeGeometryBuilder,
2471) -> Result<(), TessellationError> {
2472 if radius < options.tolerance {
2473 return Ok(());
2474 }
2475
2476 let first_side = if is_start ^ (edge_normal.cross(start_normal) >= 0.0) {
2477 Side::Positive
2478 } else {
2479 Side::Negative
2480 };
2481
2482 let start_angle = start_normal.angle_from_x_axis();
2483 let diff = start_angle.angle_to(edge_normal.angle_from_x_axis());
2484 let mid_angle = start_angle + diff;
2485 let end_angle = mid_angle + diff;
2486
2487 let step = circle_flattening_step(radius, options.tolerance);
2489 let num_segments = (diff.radians.abs() / step).ceil();
2490 let num_subdivisions = num_segments.log2().round() as u32;
2491
2492 vertex.position_on_path = center;
2493 vertex.half_width = radius;
2494 vertex.side = first_side;
2495
2496 vertex.normal = edge_normal.normalize();
2497 let mid_vertex = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2498
2499 output.add_triangle(start_vertex, mid_vertex, end_vertex);
2500
2501 tessellate_arc(
2502 (start_angle.radians, mid_angle.radians),
2503 start_vertex,
2504 mid_vertex,
2505 num_subdivisions,
2506 vertex,
2507 attributes,
2508 output,
2509 )?;
2510
2511 vertex.side = first_side.opposite();
2512
2513 tessellate_arc(
2514 (mid_angle.radians, end_angle.radians),
2515 mid_vertex,
2516 end_vertex,
2517 num_subdivisions,
2518 vertex,
2519 attributes,
2520 output,
2521 )?;
2522
2523 Ok(())
2524}
2525
2526pub(crate) fn tessellate_empty_square_cap(
2527 position: Point,
2528 vertex: &mut StrokeVertexData,
2529 attributes: &dyn AttributeStore,
2530 output: &mut dyn StrokeGeometryBuilder,
2531) -> Result<(), TessellationError> {
2532 vertex.position_on_path = position;
2533
2534 vertex.normal = vector(1.0, 1.0);
2535 vertex.side = Side::Negative;
2536
2537 let a = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2538
2539 vertex.normal = vector(1.0, -1.0);
2540 vertex.side = Side::Positive;
2541
2542 let b = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2543
2544 vertex.normal = vector(-1.0, -1.0);
2545 vertex.side = Side::Positive;
2546
2547 let c = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2548
2549 vertex.normal = vector(-1.0, 1.0);
2550 vertex.side = Side::Negative;
2551
2552 let d = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2553
2554 output.add_triangle(a, b, c);
2555 output.add_triangle(a, c, d);
2556
2557 Ok(())
2558}
2559
2560pub(crate) fn tessellate_empty_round_cap(
2561 center: Point,
2562 options: &StrokeOptions,
2563 vertex: &mut StrokeVertexData,
2564 attribute_store: &dyn AttributeStore,
2565 output: &mut dyn StrokeGeometryBuilder,
2566) -> Result<(), TessellationError> {
2567 let radius = vertex.half_width;
2568
2569 vertex.position_on_path = center;
2570 vertex.normal = vector(-1.0, 0.0);
2571 vertex.side = Side::Positive;
2572
2573 let left_id = output.add_stroke_vertex(StrokeVertex(vertex, attribute_store))?;
2574
2575 vertex.normal = vector(1.0, 0.0);
2576 vertex.side = Side::Negative;
2577
2578 let right_id = output.add_stroke_vertex(StrokeVertex(vertex, attribute_store))?;
2579
2580 tessellate_round_cap(
2581 center,
2582 radius,
2583 vector(-1.0, 0.0),
2584 left_id,
2585 right_id,
2586 vector(0.0, 1.0),
2587 options,
2588 true,
2589 vertex,
2590 attribute_store,
2591 output,
2592 )?;
2593
2594 tessellate_round_cap(
2595 center,
2596 radius,
2597 vector(1.0, 0.0),
2598 right_id,
2599 left_id,
2600 vector(0.0, -1.0),
2601 options,
2602 false,
2603 vertex,
2604 attribute_store,
2605 output,
2606 )?;
2607
2608 Ok(())
2609}
2610
2611#[allow(clippy::too_many_arguments)]
2612pub(crate) fn tessellate_arc(
2613 angle: (f32, f32),
2614 va: VertexId,
2615 vb: VertexId,
2616 num_recursions: u32,
2617 vertex: &mut StrokeVertexData,
2618 attributes: &dyn AttributeStore,
2619 output: &mut dyn StrokeGeometryBuilder,
2620) -> Result<(), TessellationError> {
2621 if num_recursions == 0 {
2622 return Ok(());
2623 }
2624
2625 let mid_angle = (angle.0 + angle.1) * 0.5;
2626
2627 let normal = vector(mid_angle.cos(), mid_angle.sin());
2628
2629 vertex.normal = normal;
2630
2631 let vertex_id = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2632
2633 output.add_triangle(va, vertex_id, vb);
2634
2635 tessellate_arc(
2636 (angle.0, mid_angle),
2637 va,
2638 vertex_id,
2639 num_recursions - 1,
2640 vertex,
2641 attributes,
2642 output,
2643 )?;
2644 tessellate_arc(
2645 (mid_angle, angle.1),
2646 vertex_id,
2647 vb,
2648 num_recursions - 1,
2649 vertex,
2650 attributes,
2651 output,
2652 )
2653}
2654
2655pub(crate) struct StrokeVertexData<'l> {
2657 pub(crate) position_on_path: Point,
2658 pub(crate) half_width: f32,
2659 pub(crate) normal: Vector,
2660 pub(crate) advancement: f32,
2661 pub(crate) side: Side,
2662 pub(crate) src: VertexSource,
2663 pub(crate) buffer: &'l mut [f32],
2664 pub(crate) buffer_is_valid: bool,
2665}
2666
2667pub struct StrokeVertex<'a, 'b>(
2669 pub(crate) &'b mut StrokeVertexData<'a>,
2670 pub(crate) &'b dyn AttributeStore,
2671);
2672
2673impl<'a, 'b> StrokeVertex<'a, 'b> {
2674 #[inline]
2676 pub fn position(&self) -> Point {
2677 self.0.position_on_path + self.0.normal * self.0.half_width
2678 }
2679
2680 #[inline]
2685 pub fn normal(&self) -> Vector {
2686 self.0.normal
2687 }
2688
2689 #[inline]
2691 pub fn position_on_path(&self) -> Point {
2692 self.0.position_on_path
2693 }
2694
2695 #[inline]
2701 pub fn line_width(&self) -> f32 {
2702 self.0.half_width * 2.0
2703 }
2704
2705 #[inline]
2707 pub fn advancement(&self) -> f32 {
2708 self.0.advancement
2709 }
2710
2711 #[inline]
2713 pub fn side(&self) -> Side {
2714 self.0.side
2715 }
2716
2717 #[inline]
2719 pub fn source(&self) -> VertexSource {
2720 self.0.src
2721 }
2722
2723 #[inline]
2728 pub fn interpolated_attributes(&mut self) -> Attributes {
2729 if self.0.buffer_is_valid {
2730 return self.0.buffer;
2731 }
2732
2733 match self.0.src {
2734 VertexSource::Endpoint { id } => self.1.get(id),
2735 VertexSource::Edge { from, to, t } => {
2736 let a = self.1.get(from);
2737 let b = self.1.get(to);
2738 for i in 0..self.0.buffer.len() {
2739 self.0.buffer[i] = a[i] * (1.0 - t) + b[i] * t;
2740 }
2741 self.0.buffer_is_valid = true;
2742
2743 self.0.buffer
2744 }
2745 }
2746 }
2747}
2748
2749pub(crate) fn circle_flattening_step(radius: f32, mut tolerance: f32) -> f32 {
2750 tolerance = f32::min(tolerance, radius);
2752 2.0 * ((radius - tolerance) / radius).acos()
2753}
2754
2755fn flatten_quad<F>(curve: &QuadraticBezierSegment<f32>, tolerance: f32, cb: &mut F)
2756where
2757 F: FnMut(Point, f32, bool),
2758{
2759 if let Some(t_split) = find_sharp_turn(curve) {
2760 let (before, after) = curve.split(t_split);
2761
2762 before.for_each_flattened_with_t(tolerance, &mut |line, t| {
2763 let is_flattening_step = t.end != 1.0;
2764 let t = t.end * t_split;
2765 cb(line.to, t, is_flattening_step);
2766 });
2767
2768 after.for_each_flattened_with_t(tolerance, &mut |line, t| {
2769 let is_flattening_step = t.end != 1.0;
2770 let t = t_split + t.end * (1.0 - t_split);
2771 cb(line.to, t, is_flattening_step);
2772 });
2773 } else {
2774 curve.for_each_flattened_with_t(tolerance, &mut |line, t| {
2775 let is_flattening_step = t.end != 1.0;
2776 cb(line.to, t.end, is_flattening_step);
2777 });
2778 }
2779}
2780
2781#[cfg(test)]
2782use crate::geometry_builder::*;
2783#[cfg(test)]
2784use crate::path::Path;
2785
2786#[cfg(test)]
2787fn test_path(path: PathSlice, options: &StrokeOptions, expected_triangle_count: Option<u32>) {
2788 struct TestBuilder<'l> {
2789 builder: SimpleBuffersBuilder<'l>,
2790 }
2791
2792 impl<'l> GeometryBuilder for TestBuilder<'l> {
2793 fn begin_geometry(&mut self) {
2794 self.builder.begin_geometry();
2795 }
2796 fn end_geometry(&mut self) {
2797 self.builder.end_geometry();
2798 }
2799 fn add_triangle(&mut self, a: VertexId, b: VertexId, c: VertexId) {
2800 assert_ne!(a, b);
2801 assert_ne!(a, c);
2802 assert_ne!(b, c);
2803 let pa = self.builder.buffers().vertices[a.0 as usize];
2804 let pb = self.builder.buffers().vertices[b.0 as usize];
2805 let pc = self.builder.buffers().vertices[c.0 as usize];
2806 let threshold = -0.035; assert!((pa - pb).cross(pc - pb) >= threshold);
2808 self.builder.add_triangle(a, b, c);
2809 }
2810 fn abort_geometry(&mut self) {
2811 panic!();
2812 }
2813 }
2814
2815 impl<'l> StrokeGeometryBuilder for TestBuilder<'l> {
2816 fn add_stroke_vertex(
2817 &mut self,
2818 attributes: StrokeVertex,
2819 ) -> Result<VertexId, GeometryBuilderError> {
2820 assert!(!attributes.position().x.is_nan());
2821 assert!(!attributes.position().y.is_nan());
2822 assert!(!attributes.normal().x.is_nan());
2823 assert!(!attributes.normal().y.is_nan());
2824 assert_ne!(attributes.normal().square_length(), 0.0);
2825 assert!(!attributes.advancement().is_nan());
2826 self.builder.add_stroke_vertex(attributes)
2827 }
2828 }
2829
2830 let mut buffers: VertexBuffers<Point, u16> = VertexBuffers::new();
2831
2832 let mut tess = StrokeTessellator::new();
2833 tess.tessellate_path(
2834 path,
2835 options,
2836 &mut TestBuilder {
2837 builder: simple_builder(&mut buffers),
2838 },
2839 )
2840 .unwrap();
2841
2842 if let Some(triangles) = expected_triangle_count {
2843 assert_eq!(
2844 triangles,
2845 buffers.indices.len() as u32 / 3,
2846 "Unexpected number of triangles"
2847 );
2848 }
2849}
2850
2851#[test]
2852fn test_square() {
2853 let mut builder = Path::builder_with_attributes(1);
2854
2855 builder.begin(point(-1.0, 1.0), &[0.3]);
2856 builder.line_to(point(1.0, 1.0), &[0.3]);
2857 builder.line_to(point(1.0, -1.0), &[0.3]);
2858 builder.line_to(point(-1.0, -1.0), &[0.3]);
2859 builder.end(false);
2860
2861 builder.begin(point(-1.0, -1.0), &[0.3]);
2862 builder.line_to(point(1.0, -1.0), &[0.3]);
2863 builder.line_to(point(1.0, 1.0), &[0.3]);
2864 builder.line_to(point(-1.0, 1.0), &[0.3]);
2865 builder.end(false);
2866
2867 let path = builder.build();
2868
2869 let options = [
2871 StrokeOptions::default().with_variable_line_width(0),
2872 StrokeOptions::default(),
2873 ];
2874
2875 for options in options {
2876 test_path(
2877 path.as_slice(),
2878 &options
2879 .with_line_join(LineJoin::Miter)
2880 .with_line_cap(LineCap::Butt),
2881 Some(12),
2882 );
2883
2884 test_path(
2885 path.as_slice(),
2886 &options
2887 .with_line_join(LineJoin::Bevel)
2888 .with_line_cap(LineCap::Square),
2889 Some(16),
2890 );
2891
2892 test_path(
2893 path.as_slice(),
2894 &options
2895 .with_line_join(LineJoin::MiterClip)
2896 .with_miter_limit(1.0)
2897 .with_line_cap(LineCap::Round),
2898 None,
2899 );
2900
2901 test_path(
2902 path.as_slice(),
2903 &options
2904 .with_tolerance(0.001)
2905 .with_line_join(LineJoin::Round)
2906 .with_line_cap(LineCap::Round),
2907 None,
2908 );
2909 }
2910}
2911
2912#[test]
2913fn test_empty_path() {
2914 let path = Path::builder().build();
2915 test_path(path.as_slice(), &StrokeOptions::default(), Some(0));
2916
2917 let path = Path::builder_with_attributes(1).build();
2918 test_path(path.as_slice(), &StrokeOptions::default(), Some(0));
2919}
2920
2921#[test]
2922fn test_empty_caps() {
2923 let mut builder = Path::builder_with_attributes(1);
2924
2925 builder.begin(point(1.0, 0.0), &[1.0]);
2927 builder.end(true);
2928
2929 builder.begin(point(2.0, 0.0), &[1.0]);
2931 builder.line_to(point(2.0, 0.0), &[1.0]);
2932 builder.end(false);
2933
2934 builder.begin(point(3.0, 0.0), &[1.0]);
2936 builder.line_to(point(3.0, 0.0), &[1.0]);
2937 builder.end(true);
2938
2939 builder.begin(point(3.0, 0.0), &[1.0]);
2941 builder.line_to(point(3.0, 0.0), &[1.0]);
2942 builder.line_to(point(3.0, 0.0), &[1.0]);
2943 builder.line_to(point(3.0, 0.0), &[1.0]);
2944 builder.end(true);
2945
2946 builder.begin(point(4.0, 0.0), &[1.0]);
2948 builder.end(false);
2949
2950 let path = builder.build();
2951
2952 let options = [
2953 StrokeOptions::default().with_variable_line_width(0),
2954 StrokeOptions::default(),
2955 ];
2956
2957 for options in options {
2958 test_path(
2959 path.as_slice(),
2960 &options.with_line_cap(LineCap::Butt),
2961 Some(0),
2962 );
2963 test_path(
2964 path.as_slice(),
2965 &options.with_line_cap(LineCap::Square),
2966 Some(8),
2967 );
2968 test_path(
2969 path.as_slice(),
2970 &options.with_line_cap(LineCap::Round),
2971 None,
2972 );
2973 }
2974}
2975
2976#[test]
2977fn test_too_many_vertices() {
2978 use crate::extra::rust_logo::build_logo_path;
2981 use crate::GeometryBuilder;
2982
2983 struct Builder {
2984 max_vertices: u32,
2985 }
2986
2987 impl GeometryBuilder for Builder {
2988 fn begin_geometry(&mut self) {}
2989 fn end_geometry(&mut self) {
2990 panic!();
2992 }
2993 fn add_triangle(&mut self, a: VertexId, b: VertexId, c: VertexId) {
2994 assert_ne!(a, b);
2995 assert_ne!(a, c);
2996 assert_ne!(b, c);
2997 }
2998 fn abort_geometry(&mut self) {}
2999 }
3000
3001 impl StrokeGeometryBuilder for Builder {
3002 fn add_stroke_vertex(&mut self, _: StrokeVertex) -> Result<VertexId, GeometryBuilderError> {
3003 if self.max_vertices == 0 {
3004 return Err(GeometryBuilderError::TooManyVertices);
3005 }
3006 self.max_vertices -= 1;
3007 Ok(VertexId(self.max_vertices))
3008 }
3009 }
3010
3011 let mut path = Path::builder().with_svg();
3012 build_logo_path(&mut path);
3013 let path = path.build();
3014
3015 let mut tess = StrokeTessellator::new();
3016 let options = StrokeOptions::tolerance(0.05);
3017
3018 assert_eq!(
3019 tess.tessellate(&path, &options, &mut Builder { max_vertices: 0 }),
3020 Err(TessellationError::GeometryBuilder(
3021 GeometryBuilderError::TooManyVertices
3022 )),
3023 );
3024 assert_eq!(
3025 tess.tessellate(&path, &options, &mut Builder { max_vertices: 10 }),
3026 Err(TessellationError::GeometryBuilder(
3027 GeometryBuilderError::TooManyVertices
3028 )),
3029 );
3030
3031 assert_eq!(
3032 tess.tessellate(&path, &options, &mut Builder { max_vertices: 100 }),
3033 Err(TessellationError::GeometryBuilder(
3034 GeometryBuilderError::TooManyVertices
3035 )),
3036 );
3037}
3038
3039#[test]
3040fn stroke_vertex_source_01() {
3041 let mut path = crate::path::Path::builder_with_attributes(1);
3042 let a = path.begin(point(0.0, 0.0), &[1.0]);
3043 let b = path.line_to(point(10.0, 10.0), &[2.0]);
3044 let c = path.quadratic_bezier_to(point(10.0, 20.0), point(0.0, 20.0), &[3.0]);
3045 path.end(true);
3046
3047 let path = path.build();
3048
3049 let mut tess = StrokeTessellator::new();
3050 tess.tessellate_with_ids(
3051 &mut path.id_iter(),
3052 &path,
3053 Some(&path),
3054 &StrokeOptions::default().with_variable_line_width(0),
3055 &mut CheckVertexSources {
3056 next_vertex: 0,
3057 a,
3058 b,
3059 c,
3060 },
3061 )
3062 .unwrap();
3063
3064 struct CheckVertexSources {
3065 next_vertex: u32,
3066 a: EndpointId,
3067 b: EndpointId,
3068 c: EndpointId,
3069 }
3070
3071 impl GeometryBuilder for CheckVertexSources {
3072 fn add_triangle(&mut self, _: VertexId, _: VertexId, _: VertexId) {}
3073 fn abort_geometry(&mut self) {}
3074 }
3075
3076 fn eq(a: Point, b: Point) -> bool {
3077 (a.x - b.x).abs() < 0.00001 && (a.y - b.y).abs() < 0.00001
3078 }
3079
3080 impl StrokeGeometryBuilder for CheckVertexSources {
3081 fn add_stroke_vertex(
3082 &mut self,
3083 mut attr: StrokeVertex,
3084 ) -> Result<VertexId, GeometryBuilderError> {
3085 let pos = attr.position_on_path();
3086 let src = attr.source();
3087 if eq(pos, point(0.0, 0.0)) {
3088 assert_eq!(src, VertexSource::Endpoint { id: self.a })
3089 } else if eq(pos, point(10.0, 10.0)) {
3090 assert_eq!(src, VertexSource::Endpoint { id: self.b })
3091 } else if eq(pos, point(0.0, 20.0)) {
3092 assert_eq!(src, VertexSource::Endpoint { id: self.c })
3093 } else {
3094 match src {
3095 VertexSource::Edge { from, to, t } => {
3096 assert_eq!(from, self.b);
3097 assert_eq!(to, self.c);
3098 assert!(t < 1.0);
3099 assert!(t > 0.0);
3100 }
3101 _ => panic!("{:?} at {:?}", src, pos),
3102 }
3103 }
3104
3105 let vertex = attr.interpolated_attributes();
3106 if eq(pos, point(0.0, 0.0)) {
3107 assert_eq!(vertex, &[1.0])
3108 } else if eq(pos, point(10.0, 10.0)) {
3109 assert_eq!(vertex, &[2.0])
3110 } else if eq(pos, point(0.0, 20.0)) {
3111 assert_eq!(vertex, &[3.0])
3112 } else {
3113 assert_eq!(vertex.len(), 1);
3114 assert!(vertex[0] > 2.0);
3115 assert!(vertex[0] < 3.0);
3116 }
3117
3118 let id = self.next_vertex;
3119 self.next_vertex += 1;
3120
3121 Ok(VertexId(id))
3122 }
3123 }
3124}
3125
3126#[test]
3127fn test_line_width() {
3128 use crate::geom::euclid::approxeq::ApproxEq;
3129 use crate::math::{point, Point};
3130 let mut builder = crate::path::Path::builder();
3131 builder.begin(point(0.0, 1.0));
3132 builder.line_to(point(2.0, 1.0));
3133 builder.end(false);
3134 let path = builder.build();
3135
3136 let options = StrokeOptions::DEFAULT.with_line_width(2.0);
3137 let mut geometry: VertexBuffers<Point, u16> = VertexBuffers::new();
3138 StrokeTessellator::new()
3139 .tessellate(
3140 path.iter(),
3141 &options,
3142 &mut crate::geometry_builder::simple_builder(&mut geometry),
3143 )
3144 .unwrap();
3145
3146 for p in &geometry.vertices {
3147 assert!(
3148 p.approx_eq(&point(0.0, 0.0))
3149 || p.approx_eq(&point(0.0, 2.0))
3150 || p.approx_eq(&point(2.0, 0.0))
3151 || p.approx_eq(&point(2.0, 2.0))
3152 );
3153 }
3154}
3155
3156trait IsNan {
3157 fn is_nan(&self) -> bool;
3158}
3159
3160impl IsNan for f32 {
3161 fn is_nan(&self) -> bool {
3162 f32::is_nan(*self)
3163 }
3164}
3165
3166impl IsNan for Point {
3167 fn is_nan(&self) -> bool {
3168 self.x.is_nan() || self.y.is_nan()
3169 }
3170}
3171
3172impl IsNan for Vector {
3173 fn is_nan(&self) -> bool {
3174 self.x.is_nan() || self.y.is_nan()
3175 }
3176}
3177
3178fn find_sharp_turn(curve: &QuadraticBezierSegment<f32>) -> Option<f32> {
3179 let baseline = curve.to - curve.from;
3182 let v = curve.ctrl - curve.from;
3183 let n = vector(-baseline.y, baseline.x);
3184 let v_dot_b = v.dot(baseline);
3185 let v_dot_n = v.dot(n);
3186
3187 let long_axis = if (v_dot_b >= 0.0 && v_dot_b <= baseline.dot(baseline))
3190 || v_dot_n.abs() * 2.0 >= v_dot_b.abs()
3191 {
3192 if baseline.square_length() * 30.0 > v.square_length() {
3194 return None;
3195 }
3196
3197 v
3198 } else {
3199 baseline
3200 };
3201
3202 let rot = crate::geom::euclid::Rotation2D::new(-long_axis.angle_from_x_axis());
3205 let rotated = QuadraticBezierSegment {
3206 from: point(0.0, 0.0),
3207 ctrl: rot.transform_vector(v).to_point(),
3208 to: rot.transform_vector(baseline).to_point(),
3209 };
3210
3211 rotated.local_x_extremum_t()
3212}
3213
3214#[test]
3215fn test_triangle_winding() {
3216 use crate::math::{point, Point};
3217 use crate::GeometryBuilder;
3218
3219 struct Builder {
3220 vertices: Vec<Point>,
3221 }
3222
3223 impl GeometryBuilder for Builder {
3224 fn add_triangle(&mut self, a: VertexId, b: VertexId, c: VertexId) {
3225 let a = self.vertices[a.to_usize()];
3226 let b = self.vertices[b.to_usize()];
3227 let c = self.vertices[c.to_usize()];
3228 assert!((b - a).cross(c - b) <= 0.0);
3229 }
3230 }
3231
3232 impl StrokeGeometryBuilder for Builder {
3233 fn add_stroke_vertex(&mut self, v: StrokeVertex) -> Result<VertexId, GeometryBuilderError> {
3234 let id = VertexId(self.vertices.len() as u32);
3235 self.vertices.push(v.position());
3236
3237 Ok(id)
3238 }
3239 }
3240
3241 let mut path = Path::builder().with_svg();
3242 path.move_to(point(0.0, 0.0));
3243 path.quadratic_bezier_to(point(100.0, 0.0), point(100.0, 100.0));
3244 let path = path.build();
3245
3246 let mut tess = StrokeTessellator::new();
3247 let options = StrokeOptions::tolerance(0.05);
3248
3249 tess.tessellate(
3250 &path,
3251 &options,
3252 &mut Builder {
3253 vertices: Vec::new(),
3254 },
3255 )
3256 .unwrap();
3257}
3258
3259#[test]
3260fn single_segment_closed() {
3261 let mut path = Path::builder().with_svg();
3262 path.move_to(point(0.0, 0.0));
3263 path.line_to(point(100.0, 0.0));
3264 let path = path.build();
3265
3266 let mut tess = StrokeTessellator::new();
3267 let options = StrokeOptions::tolerance(0.05);
3268 let mut output: VertexBuffers<Point, u16> = VertexBuffers::new();
3269 tess.tessellate(&path, &options, &mut simple_builder(&mut output))
3270 .unwrap();
3271
3272 assert!(!output.indices.is_empty());
3273
3274 let mut path = Path::builder_with_attributes(1);
3275 path.begin(point(0.0, 0.0), &[1.0]);
3276 path.line_to(point(100.0, 0.0), &[1.0]);
3277 path.end(false);
3278 let path = path.build();
3279
3280 let mut tess = StrokeTessellator::new();
3281 let options = StrokeOptions::tolerance(0.05);
3282 let mut output: VertexBuffers<Point, u16> = VertexBuffers::new();
3283 tess.tessellate(&path, &options, &mut simple_builder(&mut output))
3284 .unwrap();
3285
3286 assert!(!output.indices.is_empty());
3287}
3288
3289#[test]
3290fn issue_819() {
3291 let mut path = Path::builder();
3297 path.begin(point(650.539978027344, 173.559997558594));
3298 path.line_to(point(650.609985351562, 173.529998779297));
3299 path.line_to(point(650.729980468750, 173.630004882812));
3300 path.line_to(point(650.559997558594, 173.570007324219));
3301 path.end(true);
3302
3303 let mut tess = StrokeTessellator::new();
3304 let options = StrokeOptions::tolerance(0.1);
3305 let mut output: VertexBuffers<Point, u16> = VertexBuffers::new();
3306 tess.tessellate(&path.build(), &options, &mut simple_builder(&mut output))
3307 .unwrap();
3308
3309 let mut path = Path::builder_with_attributes(1);
3310 path.begin(point(650.539978027344, 173.559997558594), &[0.0]);
3311 path.line_to(point(650.609985351562, 173.529998779297), &[0.0]);
3312 path.line_to(point(650.729980468750, 173.630004882812), &[0.0]);
3313 path.line_to(point(650.559997558594, 173.570007324219), &[0.0]);
3314 path.end(true);
3315
3316 let mut tess = StrokeTessellator::new();
3317 let options = StrokeOptions::tolerance(0.1);
3318 let mut output: VertexBuffers<Point, u16> = VertexBuffers::new();
3319 tess.tessellate(&path.build(), &options, &mut simple_builder(&mut output))
3320 .unwrap();
3321}
3322
3323#[test]
3324fn issue_821() {
3325 let mut tessellator = StrokeTessellator::new();
3326
3327 let options = StrokeOptions::default()
3328 .with_tolerance(0.001)
3329 .with_line_cap(LineCap::Round)
3330 .with_variable_line_width(0);
3331
3332 let mut path = Path::builder_with_attributes(1);
3333 path.begin(point(-45.192276, -69.800575), &[1.0]);
3334 path.line_to(point(-45.164116, -69.84931), &[1.0]);
3335 path.line_to(point(-45.135952, -69.90564), &[1.0]);
3336 path.line_to(point(-45.10779, -69.93248), &[1.0]);
3337 path.line_to(point(-45.052788, -69.96064), &[1.0]);
3338 path.line_to(point(-45.026604, -69.898155), &[1.0]);
3339
3340 path.end(false);
3341
3342 let path = path.build();
3343 let mut mesh = VertexBuffers::new();
3344 let mut builder = simple_builder(&mut mesh);
3345 tessellator
3346 .tessellate_path(&path, &options, &mut builder)
3347 .unwrap();
3348}
3349
3350#[test]
3351fn issue_894() {
3352 struct VariableWidthStrokeCtor;
3353 impl StrokeVertexConstructor<[f32; 2]> for VariableWidthStrokeCtor {
3354 fn new_vertex(&mut self, vertex: StrokeVertex) -> [f32; 2] {
3355 vertex.position().to_array()
3356 }
3357 }
3358
3359 const STROKE_WIDTH: lyon_path::AttributeIndex = 0;
3360
3361 let mut builder = Path::builder_with_attributes(1);
3362 builder.begin(point(435.72, 368.42), &[38.82]);
3363 builder.line_to(point(433.53, 366.06), &[38.82]);
3364 builder.quadratic_bezier_to(point(431.35, 363.70), point(430.22, 362.52), &[39.59]);
3365 builder.quadratic_bezier_to(point(429.09, 361.34), point(429.05, 362.14), &[41.62]);
3366 builder.line_to(point(429.00, 362.95), &[41.63]);
3367 builder.end(false);
3368 let path = builder.build();
3369 let mut stroke_tessellator = StrokeTessellator::new();
3370 let mut geometry: crate::VertexBuffers<[f32; 2], u16> = crate::VertexBuffers::new();
3371 _ = stroke_tessellator.tessellate_path(
3372 &path,
3373 &StrokeOptions::tolerance(0.01)
3374 .with_line_cap(LineCap::Round)
3375 .with_variable_line_width(STROKE_WIDTH),
3376 &mut BuffersBuilder::new(&mut geometry, VariableWidthStrokeCtor),
3377 );
3378}