1use crate::geom::{CubicBezierSegment, LineSegment, QuadraticBezierSegment, Segment};
4use crate::math::*;
5use crate::path::{
6 builder::PathBuilder, AttributeStore, Attributes, EndpointId, IdEvent, Path, PathSlice,
7 PositionStore,
8};
9use core::ops::Range;
10
11use alloc::vec::Vec;
12
13#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
15pub enum SampleType {
16 Distance,
17 Normalized,
18}
19
20type EndpointPair = (EndpointId, EndpointId);
21
22enum SegmentWrapper {
23 Empty,
24 Line(LineSegment<f32>, EndpointPair),
25 Quadratic(QuadraticBezierSegment<f32>, EndpointPair),
26 Cubic(CubicBezierSegment<f32>, EndpointPair),
27}
28
29impl SegmentWrapper {
30 fn split(&self, range: Range<f32>) -> Self {
31 match self {
32 Self::Empty => Self::Empty,
33 Self::Line(segment, pair) => Self::Line(segment.split_range(range), *pair),
34 Self::Quadratic(segment, pair) => Self::Quadratic(segment.split_range(range), *pair),
35 Self::Cubic(segment, pair) => Self::Cubic(segment.split_range(range), *pair),
36 }
37 }
38}
39
40struct Edge {
41 distance: f32,
43 index: usize,
45 t: f32,
47}
48
49#[derive(PartialEq, Debug)]
51pub struct PathSample<'l> {
52 position: Point,
53 tangent: Vector,
54 attributes: Attributes<'l>,
55}
56
57impl<'l> PathSample<'l> {
58 #[inline]
59 pub fn position(&self) -> Point {
60 self.position
61 }
62
63 #[inline]
64 pub fn tangent(&self) -> Vector {
65 self.tangent
66 }
67
68 #[inline]
71 pub fn attributes(&mut self) -> Attributes<'l> {
72 self.attributes
73 }
74}
75
76pub struct PathMeasurements {
128 events: Vec<IdEvent>,
129 edges: Vec<Edge>,
130}
131
132impl PathMeasurements {
133 pub fn empty() -> Self {
137 PathMeasurements {
138 events: Vec::new(),
139 edges: Vec::new(),
140 }
141 }
142
143 pub fn from_path(path: &Path, tolerance: f32) -> Self {
145 let mut m = Self::empty();
146 m.initialize(path.id_iter(), path, tolerance);
147
148 m
149 }
150
151 pub fn from_path_slice(path: &PathSlice, tolerance: f32) -> Self {
153 let mut m = Self::empty();
154 m.initialize(path.id_iter(), path, tolerance);
155
156 m
157 }
158
159 pub fn from_iter<Iter, PS>(path: Iter, positions: &PS, tolerance: f32) -> Self
161 where
162 Iter: IntoIterator<Item = IdEvent>,
163 PS: PositionStore,
164 {
165 let mut m = Self::empty();
166 m.initialize(path, positions, tolerance);
167
168 m
169 }
170
171 pub fn initialize<Iter, PS>(&mut self, path: Iter, position_store: &PS, tolerance: f32)
173 where
174 Iter: IntoIterator<Item = IdEvent>,
175 PS: PositionStore,
176 {
177 let tolerance = tolerance.max(1e-4);
178 let mut events = core::mem::take(&mut self.events);
179 events.clear();
180 events.extend(path.into_iter());
181 let mut edges = core::mem::take(&mut self.edges);
182 edges.clear();
183
184 let mut distance = 0.0;
185 for (index, event) in events.iter().cloned().enumerate() {
186 match event {
187 IdEvent::Begin { .. } => {
188 edges.push(Edge {
189 distance,
190 index,
191 t: 1.0,
192 });
193 }
194 IdEvent::Line { from, to } => {
195 let from = position_store.get_endpoint(from);
196 let to = position_store.get_endpoint(to);
197 distance += (from - to).length();
198 edges.push(Edge {
199 distance,
200 index,
201 t: 1.0,
202 })
203 }
204 IdEvent::Quadratic { from, ctrl, to } => {
205 let from = position_store.get_endpoint(from);
206 let to = position_store.get_endpoint(to);
207 let ctrl = position_store.get_control_point(ctrl);
208 let segment = QuadraticBezierSegment { from, ctrl, to };
209 segment.for_each_flattened_with_t(tolerance, &mut |line, t| {
210 distance += line.length();
211 edges.push(Edge {
212 distance,
213 index,
214 t: t.end,
215 });
216 });
217 }
218 IdEvent::Cubic {
219 from,
220 ctrl1,
221 ctrl2,
222 to,
223 } => {
224 let from = position_store.get_endpoint(from);
225 let to = position_store.get_endpoint(to);
226 let ctrl1 = position_store.get_control_point(ctrl1);
227 let ctrl2 = position_store.get_control_point(ctrl2);
228 let segment = CubicBezierSegment {
229 from,
230 ctrl1,
231 ctrl2,
232 to,
233 };
234 segment.for_each_flattened_with_t(tolerance, &mut |line, t| {
235 distance += line.length();
236 edges.push(Edge {
237 distance,
238 index,
239 t: t.end,
240 });
241 });
242 }
243 IdEvent::End {
244 last,
245 first,
246 close: true,
247 } => {
248 let last = position_store.get_endpoint(last);
249 let first = position_store.get_endpoint(first);
250 distance += (last - first).length();
251 edges.push(Edge {
252 distance,
253 index,
254 t: 1.0,
255 })
256 }
257 _ => {}
258 }
259 }
260
261 if !edges.is_empty() {
262 debug_assert_eq!(edges.first().unwrap().distance, 0.0);
263 }
264
265 self.events = events;
266 self.edges = edges;
267 }
268
269 pub fn initialize_with_path(&mut self, path: &Path, tolerance: f32) {
271 self.initialize_with_path_slice(path.as_slice(), tolerance)
272 }
273
274 pub fn initialize_with_path_slice(&mut self, path: PathSlice, tolerance: f32) {
276 self.initialize(path.id_iter(), &path, tolerance)
277 }
278
279 pub fn length(&self) -> f32 {
281 if self.edges.is_empty() {
282 0.0
283 } else {
284 self.edges.last().unwrap().distance
285 }
286 }
287
288 pub fn create_sampler<'l, PS: PositionStore>(
292 &'l self,
293 positions: &'l PS,
294 ty: SampleType,
295 ) -> PathSampler<'l, PS, ()> {
296 let attr: &'static () = &();
297 PathSampler::new(self, positions, attr, ty)
298 }
299
300 pub fn create_sampler_with_attributes<'l, PS, AS>(
304 &'l self,
305 positions: &'l PS,
306 attributes: &'l AS,
307 ty: SampleType,
308 ) -> PathSampler<'l, PS, AS>
309 where
310 PS: PositionStore,
311 AS: AttributeStore,
312 {
313 PathSampler::new(self, positions, attributes, ty)
314 }
315}
316
317pub struct PathSampler<'l, PS, AS> {
325 events: &'l [IdEvent],
326 edges: &'l [Edge],
327 positions: &'l PS,
328 attributes: &'l AS,
329 attribute_buffer: Vec<f32>,
330 cursor: usize,
331 sample_type: SampleType,
332}
333
334impl<'l, PS: PositionStore, AS: AttributeStore> PathSampler<'l, PS, AS> {
335 pub fn new(
339 measurements: &'l PathMeasurements,
340 positions: &'l PS,
341 attributes: &'l AS,
342 sample_type: SampleType,
343 ) -> Self {
344 PathSampler {
345 events: &measurements.events,
346 edges: &measurements.edges,
347 positions,
348 attributes,
349 attribute_buffer: alloc::vec![0.0; attributes.num_attributes()],
350 cursor: 0,
351 sample_type,
352 }
353 }
354
355 pub fn sample(&mut self, dist: f32) -> PathSample {
359 self.sample_impl(dist, self.sample_type)
360 }
361
362 pub fn split_range(&mut self, mut range: Range<f32>, output: &mut dyn PathBuilder) {
368 let length = self.length();
369 if self.sample_type == SampleType::Normalized {
370 range.start *= length;
371 range.end *= length;
372 }
373 range.start = range.start.max(0.0);
374 range.end = range.end.max(range.start);
375 range.start = range.start.min(length);
376 range.end = range.end.min(length);
377
378 if range.is_empty() {
379 return;
380 }
381
382 let result = self.sample_impl(range.start, SampleType::Distance);
383 output.begin(result.position, result.attributes);
384 let (ptr1, seg1) = (self.cursor, self.edges[self.cursor].index);
385 self.move_cursor(range.end);
386 let (ptr2, seg2) = (self.cursor, self.edges[self.cursor].index);
387
388 let mut is_in_subpath = true;
389 if seg1 == seg2 {
390 self.cursor = ptr1;
391 let t_begin = self.t(range.start);
392 self.cursor = ptr2;
393 let t_end = self.t(range.end);
394 self.add_segment(seg1, Some(t_begin..t_end), output, &mut is_in_subpath);
395 } else {
396 self.cursor = ptr1;
397 self.add_segment(
398 seg1,
399 Some(self.t(range.start)..1.0),
400 output,
401 &mut is_in_subpath,
402 );
403 for seg in (seg1 + 1)..seg2 {
404 self.add_segment(seg, None, output, &mut is_in_subpath);
405 }
406 self.cursor = ptr2;
407 self.add_segment(
408 seg2,
409 Some(0.0..self.t(range.end)),
410 output,
411 &mut is_in_subpath,
412 );
413 }
414
415 output.end(false);
416 }
417
418 pub fn length(&self) -> f32 {
420 if self.edges.is_empty() {
421 0.0
422 } else {
423 self.edges.last().unwrap().distance
424 }
425 }
426
427 fn to_segment(&self, event: IdEvent) -> SegmentWrapper {
428 match event {
429 IdEvent::Line { from, to } => SegmentWrapper::Line(
430 LineSegment {
431 from: self.positions.get_endpoint(from),
432 to: self.positions.get_endpoint(to),
433 },
434 (from, to),
435 ),
436 IdEvent::Quadratic { from, ctrl, to } => SegmentWrapper::Quadratic(
437 QuadraticBezierSegment {
438 from: self.positions.get_endpoint(from),
439 to: self.positions.get_endpoint(to),
440 ctrl: self.positions.get_control_point(ctrl),
441 },
442 (from, to),
443 ),
444 IdEvent::Cubic {
445 from,
446 ctrl1,
447 ctrl2,
448 to,
449 } => SegmentWrapper::Cubic(
450 CubicBezierSegment {
451 from: self.positions.get_endpoint(from),
452 to: self.positions.get_endpoint(to),
453 ctrl1: self.positions.get_control_point(ctrl1),
454 ctrl2: self.positions.get_control_point(ctrl2),
455 },
456 (from, to),
457 ),
458 IdEvent::End {
459 last,
460 first,
461 close: true,
462 } => SegmentWrapper::Line(
463 LineSegment {
464 from: self.positions.get_endpoint(last),
465 to: self.positions.get_endpoint(first),
466 },
467 (last, first),
468 ),
469 _ => SegmentWrapper::Empty,
470 }
471 }
472
473 fn in_bounds(&self, dist: f32) -> bool {
474 self.cursor != 0
475 && self.edges[self.cursor - 1].distance <= dist
476 && dist <= self.edges[self.cursor].distance
477 }
478
479 fn move_cursor(&mut self, dist: f32) {
481 if dist == 0.0 {
482 self.cursor = 1;
483 return;
484 }
485 if self.in_bounds(dist) {
486 return;
488 }
489
490 fn partition_point(first: usize, last: usize, pred: impl Fn(usize) -> bool) -> usize {
495 let mut l = first;
496 let mut r = last;
497 while l < r {
498 let mid = (l + r) / 2;
499 if pred(mid) {
500 l = mid + 1;
501 } else {
502 r = mid;
503 }
504 }
505 debug_assert_eq!(l, r);
506 debug_assert_ne!(l, last);
507 l
508 }
509
510 fn floor_log2(num: usize) -> u32 {
511 core::mem::size_of::<usize>() as u32 * 8 - num.leading_zeros() - 1
512 }
513
514 let start = self.edges[self.cursor].distance;
524 if start < dist {
525 let (len, num) = (self.length() - start, self.edges.len() - self.cursor - 1);
526 debug_assert_ne!(num, 0);
527 if (dist - start) / len * (num as f32) < floor_log2(num) as f32 {
528 loop {
529 self.cursor += 1;
530 if dist <= self.edges[self.cursor].distance {
531 break;
532 }
533 }
534 } else {
535 self.cursor = partition_point(self.cursor + 1, self.edges.len(), |p| {
536 self.edges[p].distance < dist
537 });
538 }
539 } else {
540 let (len, num) = (start, self.cursor + 1);
541 debug_assert_ne!(num, 0);
542 if (start - dist) / len * (num as f32) < floor_log2(num) as f32 {
543 loop {
544 self.cursor -= 1;
545 if self.cursor == 0 || self.edges[self.cursor - 1].distance < dist {
546 break;
547 }
548 }
549 } else {
550 self.cursor = partition_point(0, self.cursor, |p| self.edges[p].distance < dist);
551 }
552 }
553
554 debug_assert!(self.in_bounds(dist));
555 }
556
557 fn interpolate_attributes(&mut self, from: EndpointId, to: EndpointId, t: f32) {
559 let from = self.attributes.get(from);
560 let to = self.attributes.get(to);
561 for i in 0..self.attribute_buffer.len() {
562 self.attribute_buffer[i] = from[i] * (1.0 - t) + to[i] * t;
563 }
564 }
565
566 fn t(&self, dist: f32) -> f32 {
568 debug_assert!(self.in_bounds(dist));
569 let prev = &self.edges[self.cursor - 1];
570 let cur = &self.edges[self.cursor];
571 let t_begin = if prev.index == cur.index { prev.t } else { 0.0 };
572 let t_end = cur.t;
573 t_begin + (t_end - t_begin) * ((dist - prev.distance) / (cur.distance - prev.distance))
574 }
575
576 fn sample_impl(&mut self, mut dist: f32, sample_type: SampleType) -> PathSample {
577 let length = self.length();
578 if length == 0.0 {
579 return self.sample_zero_length();
580 }
581 if sample_type == SampleType::Normalized {
582 dist *= length;
583 }
584 dist = dist.max(0.0);
585 dist = dist.min(length);
586
587 self.move_cursor(dist);
588 let t = self.t(dist);
589 macro_rules! dispatched_call {
590 ([$v:expr] ($seg:ident, $pair:ident) => $code:block) => {
591 #[allow(unused_parens)]
592 match $v {
593 SegmentWrapper::Line($seg, $pair) => $code,
594 SegmentWrapper::Quadratic($seg, $pair) => $code,
595 SegmentWrapper::Cubic($seg, $pair) => $code,
596 _ => {}
597 }
598 };
599 }
600
601 dispatched_call!([self.to_segment(self.events[self.edges[self.cursor].index])] (segment, pair) => {
602 self.interpolate_attributes(pair.0, pair.1, t);
603 return PathSample {
604 position: segment.sample(t),
605 tangent: segment.derivative(t).normalize(),
606 attributes: &self.attribute_buffer,
607 }
608 });
609
610 unreachable!();
611 }
612
613 #[cold]
614 fn sample_zero_length(&mut self) -> PathSample {
615 if let Some(IdEvent::Begin { at }) = self.events.first() {
616 return PathSample {
617 position: self.positions.get_endpoint(*at),
618 tangent: vector(0.0, 0.0),
619 attributes: self.attributes.get(*at),
620 };
621 }
622
623 for value in &mut self.attribute_buffer {
624 *value = f32::NAN;
625 }
626
627 PathSample {
628 position: point(f32::NAN, f32::NAN),
629 tangent: vector(f32::NAN, f32::NAN),
630 attributes: &self.attribute_buffer,
631 }
632 }
633
634 fn add_segment(
637 &mut self,
638 ptr: usize,
639 range: Option<Range<f32>>,
640 dest: &mut dyn PathBuilder,
641 is_in_subpath: &mut bool,
642 ) {
643 let segment = self.to_segment(self.events[ptr]);
644 let segment = match range.clone() {
645 Some(range) => segment.split(range),
646 None => segment,
647 };
648 macro_rules! obtain_attrs {
649 ($p:ident, $index:tt) => {
650 match range.clone() {
651 Some(range) => {
652 if range.end == 1.0 {
653 self.attributes.get($p.$index)
654 } else {
655 self.interpolate_attributes($p.0, $p.1, range.end);
656 &mut self.attribute_buffer
657 }
658 }
659 None => self.attributes.get($p.$index),
660 }
661 };
662 }
663
664 match segment {
665 SegmentWrapper::Line(LineSegment { from, to }, pair) => {
666 if !*is_in_subpath {
667 dest.end(false);
668 dest.begin(from, obtain_attrs!(pair, 0));
669 }
670 dest.line_to(to, obtain_attrs!(pair, 1));
671 }
672 SegmentWrapper::Quadratic(QuadraticBezierSegment { from, ctrl, to }, pair) => {
673 if !*is_in_subpath {
674 dest.end(false);
675 dest.begin(from, obtain_attrs!(pair, 0));
676 }
677 dest.quadratic_bezier_to(ctrl, to, obtain_attrs!(pair, 1));
678 }
679 SegmentWrapper::Cubic(
680 CubicBezierSegment {
681 from,
682 ctrl1,
683 ctrl2,
684 to,
685 },
686 pair,
687 ) => {
688 if !*is_in_subpath {
689 dest.end(false);
690 dest.begin(from, obtain_attrs!(pair, 0));
691 }
692 dest.cubic_bezier_to(ctrl1, ctrl2, to, obtain_attrs!(pair, 1));
693 }
694 _ => {}
695 }
696
697 *is_in_subpath = !matches!(
698 self.events[ptr],
699 IdEvent::End { .. } | IdEvent::Begin { .. }
700 );
701 }
702}
703
704#[cfg(test)]
705fn slice(a: &[f32]) -> &[f32] {
706 a
707}
708
709#[test]
710fn measure_line() {
711 let mut path = Path::builder();
712 path.begin(point(1.0, 1.0));
713 path.line_to(point(0.0, 0.0));
714 path.end(false);
715 let path = path.build();
716 let measure = PathMeasurements::from_path(&path, 0.01);
717 let mut sampler = measure.create_sampler(&path, SampleType::Normalized);
718 for t in [0.0, 0.2, 0.3, 0.5, 1.0] {
719 let result = sampler.sample(t);
720 assert!((result.position - point(1.0 - t, 1.0 - t)).length() < 1e-5);
721 assert_eq!(result.tangent, vector(-1.0, -1.0).normalize());
722 }
723}
724
725#[test]
726fn measure_square() {
727 let mut path = Path::builder();
728 path.begin(point(0.0, 0.0));
729 path.line_to(point(1.0, 0.0));
730 path.line_to(point(1.0, 1.0));
731 path.line_to(point(0.0, 1.0));
732 path.close();
733 let path = path.build();
734 let measure = PathMeasurements::from_path(&path, 0.01);
735 let mut sampler = measure.create_sampler(&path, SampleType::Normalized);
736 for (t, position, tangent) in [
737 (0.125, point(0.5, 0.0), vector(1.0, 0.0)),
738 (0.375, point(1.0, 0.5), vector(0.0, 1.0)),
739 (0.625, point(0.5, 1.0), vector(-1.0, 0.0)),
740 (0.875, point(0.0, 0.5), vector(0.0, -1.0)),
741 ] {
742 let result = sampler.sample(t);
743 assert!((result.position - position).length() < 1e-5);
744 assert_eq!(result.tangent, tangent);
745 }
746}
747
748#[test]
749fn measure_attributes() {
750 let mut path = Path::builder_with_attributes(2);
751 path.begin(point(0.0, 0.0), &[1.0, 2.0]);
752 path.line_to(point(1.0, 0.0), &[2.0, 3.0]);
753 path.line_to(point(1.0, 1.0), &[0.0, 0.0]);
754 path.end(false);
755 let path = path.build();
756 let measure = PathMeasurements::from_path(&path, 0.01);
757 let mut sampler = measure.create_sampler_with_attributes(&path, &path, SampleType::Normalized);
758
759 for (t, position, attrs) in [
760 (0.25, point(0.5, 0.0), &[1.5, 2.5]),
761 (0.5, point(1.0, 0.0), &[2.0, 3.0]),
762 (0.75, point(1.0, 0.5), &[1.0, 1.5]),
763 ] {
764 let result = sampler.sample(t);
765 assert!((result.position - position).length() < 1e-5);
766 for i in 0..2 {
767 assert!((result.attributes[i] - attrs[i]).abs() < 1e-5);
768 }
769 }
770}
771
772#[test]
773fn measure_bezier_curve() {
774 let mut path = Path::builder();
775 path.begin(point(0.0, 0.0));
776 path.quadratic_bezier_to(point(0.5, 0.7), point(1.0, 0.0));
777 path.quadratic_bezier_to(point(1.5, -0.7), point(2.0, 0.0));
778 path.end(false);
779 let path = path.build();
780
781 let measure = PathMeasurements::from_path(&path, 0.01);
782 let mut sampler = measure.create_sampler(&path, SampleType::Normalized);
783
784 for t in [0.25, 0.75] {
785 let result = sampler.sample(t);
786 assert_eq!(result.tangent, vector(1.0, 0.0));
787 }
788 for (t, position) in [
789 (0.0, point(0.0, 0.0)),
790 (0.5, point(1.0, 0.0)),
791 (1.0, point(2.0, 0.0)),
792 ] {
793 let result = sampler.sample(t);
794 assert_eq!(result.position, position);
795 }
796}
797
798#[test]
799fn split_square() {
800 use crate::path::Event;
801
802 let mut path = Path::builder();
803 path.begin(point(0.0, 0.0));
804 path.line_to(point(1.0, 0.0));
805 path.line_to(point(1.0, 1.0));
806 path.line_to(point(0.0, 1.0));
807 path.close();
808 let path = path.build();
809 let measure = PathMeasurements::from_path(&path, 0.01);
810 let mut sampler = measure.create_sampler(&path, SampleType::Normalized);
811 let mut path2 = Path::builder();
812 sampler.split_range(0.125..0.625, &mut path2);
813 let path2 = path2.build();
814 assert_eq!(
815 path2.iter().collect::<Vec<_>>(),
816 alloc::vec![
817 Event::Begin {
818 at: point(0.5, 0.0)
819 },
820 Event::Line {
821 from: point(0.5, 0.0),
822 to: point(1.0, 0.0)
823 },
824 Event::Line {
825 from: point(1.0, 0.0),
826 to: point(1.0, 1.0)
827 },
828 Event::Line {
829 from: point(1.0, 1.0),
830 to: point(0.5, 1.0)
831 },
832 Event::End {
833 last: point(0.5, 1.0),
834 first: point(0.5, 0.0),
835 close: false
836 },
837 ]
838 );
839}
840
841#[test]
842fn split_bezier_curve() {
843 use crate::path::Event;
844
845 let mut path = Path::builder();
846 path.begin(point(0.0, 0.0));
847 path.quadratic_bezier_to(point(1.0, 1.0), point(2.0, 0.0));
848 path.end(false);
849 let path = path.build();
850 let measure = PathMeasurements::from_path(&path, 0.01);
851 let mut sampler = measure.create_sampler(&path, SampleType::Normalized);
852
853 let mut path2 = Path::builder();
854 sampler.split_range(0.5..1.0, &mut path2);
855 let path2 = path2.build();
856
857 assert_eq!(
858 path2.iter().collect::<Vec<_>>(),
859 alloc::vec![
860 Event::Begin {
861 at: point(1.0, 0.5)
862 },
863 Event::Quadratic {
864 from: point(1.0, 0.5),
865 ctrl: point(1.5, 0.5),
866 to: point(2.0, 0.0),
867 },
868 Event::End {
869 last: point(2.0, 0.0),
870 first: point(1.0, 0.5),
871 close: false
872 }
873 ]
874 );
875}
876
877#[test]
878fn split_attributes() {
879 use crate::path::Event;
880
881 let mut path = Path::builder_with_attributes(2);
882 path.begin(point(0.0, 0.0), &[1.0, 2.0]);
883 path.line_to(point(1.0, 0.0), &[2.0, 3.0]);
884 path.line_to(point(1.0, 1.0), &[0.0, 0.0]);
885 path.end(false);
886 let path = path.build();
887 let measure = PathMeasurements::from_path(&path, 0.01);
888 let mut sampler = measure.create_sampler_with_attributes(&path, &path, SampleType::Normalized);
889
890 let mut path2 = Path::builder_with_attributes(2);
891 sampler.split_range(0.0..1.0, &mut path2);
892 let path2 = path2.build();
893
894 assert_eq!(
895 path2.iter_with_attributes().collect::<Vec<_>>(),
896 path.iter_with_attributes().collect::<Vec<_>>()
897 );
898
899 let mut path3 = Path::builder_with_attributes(2);
900 sampler.split_range(0.25..0.75, &mut path3);
901 let path3 = path3.build();
902
903 assert_eq!(
904 path3.iter_with_attributes().collect::<Vec<_>>(),
905 alloc::vec![
906 Event::Begin {
907 at: (point(0.5, 0.0), slice(&[1.5, 2.5]))
908 },
909 Event::Line {
910 from: (point(0.5, 0.0), slice(&[1.5, 2.5])),
911 to: (point(1.0, 0.0), slice(&[2.0, 3.0])),
912 },
913 Event::Line {
914 from: (point(1.0, 0.0), slice(&[2.0, 3.0])),
915 to: (point(1.0, 0.5), slice(&[1.0, 1.5])),
916 },
917 Event::End {
918 last: (point(1.0, 0.5), slice(&[1.0, 1.5])),
919 first: (point(0.5, 0.0), slice(&[1.5, 2.5])),
920 close: false
921 }
922 ]
923 );
924}
925
926#[test]
927fn zero_length() {
928 fn expect_nans(sample: PathSample, num_attribs: usize) {
929 assert!(sample.position.x.is_nan());
930 assert!(sample.position.y.is_nan());
931 assert!(sample.tangent.x.is_nan());
932 assert!(sample.tangent.y.is_nan());
933 for attr in sample.attributes {
934 assert!(attr.is_nan());
935 }
936 assert_eq!(sample.attributes.len(), num_attribs);
937 }
938
939 let mut path = Path::builder_with_attributes(2);
940 path.begin(point(1.0, 2.0), &[3.0, 4.0]);
941 path.end(false);
942 let path = path.build();
943 let measure = PathMeasurements::from_path(&path, 0.01);
944 let mut sampler = measure.create_sampler_with_attributes(&path, &path, SampleType::Normalized);
945 let expected = PathSample {
946 position: point(1.0, 2.0),
947 tangent: vector(0.0, 0.0),
948 attributes: &[3.0, 4.0],
949 };
950 assert_eq!(sampler.sample(0.0), expected);
951 assert_eq!(sampler.sample(0.5), expected);
952 assert_eq!(sampler.sample(1.0), expected);
953
954 let mut path = Path::builder_with_attributes(2);
955 path.begin(point(1.0, 2.0), &[3.0, 4.0]);
956 path.end(false);
957 let path = path.build();
958 let measure = PathMeasurements::from_path(&path, 0.01);
959 let mut sampler = measure.create_sampler_with_attributes(&path, &path, SampleType::Distance);
960 let expected = PathSample {
961 position: point(1.0, 2.0),
962 tangent: vector(0.0, 0.0),
963 attributes: &[3.0, 4.0],
964 };
965 assert_eq!(sampler.sample(0.0), expected);
966 assert_eq!(sampler.sample(0.5), expected);
967 assert_eq!(sampler.sample(1.0), expected);
968
969 let path = Path::builder_with_attributes(2).build();
970 let measure = PathMeasurements::from_path(&path, 0.01);
971 let mut sampler = measure.create_sampler_with_attributes(&path, &path, SampleType::Normalized);
972 expect_nans(sampler.sample(0.0), 2);
973 expect_nans(sampler.sample(0.5), 2);
974 expect_nans(sampler.sample(1.0), 2);
975
976 let path = Path::builder_with_attributes(2).build();
977 let measure = PathMeasurements::from_path(&path, 0.01);
978 let mut sampler = measure.create_sampler_with_attributes(&path, &path, SampleType::Distance);
979 expect_nans(sampler.sample(0.0), 2);
980 expect_nans(sampler.sample(0.5), 2);
981 expect_nans(sampler.sample(1.0), 2);
982}
983
984
985#[test]
986fn multiple_sub_paths() {
987 let mut path = Path::builder();
988
989 path.begin(point(0.0, 0.0));
990 path.line_to(point(10.0, 0.0));
991 path.end(false);
992
993 path.begin(point(10.0, 10.0));
994 path.line_to(point(20.0, 10.0));
995 path.end(false);
996
997 let path = path.build();
998 let measure = PathMeasurements::from_path(&path, 0.01);
999 let mut sampler = measure.create_sampler(&path, SampleType::Normalized);
1000
1001 let mut dashes = Path::builder();
1002 sampler.split_range(0.0 .. 0.25, &mut dashes);
1003 sampler.split_range(0.25 .. 0.5, &mut dashes);
1004 sampler.split_range(0.6 .. 0.75, &mut dashes);
1006 sampler.split_range(0.75 .. 1.0, &mut dashes);
1007 let dashes = dashes.build();
1008
1009 let mut iter = dashes.iter();
1010
1011 use crate::path::geom::euclid::approxeq::ApproxEq;
1012 fn expect_begin(event: Option<path::PathEvent>, pos: Point) {
1013 std::eprintln!("- {:?}", event);
1014 if let Some(path::PathEvent::Begin { at }) = event {
1015 assert!(at.approx_eq(&pos), "Expected Begin {:?}, got {:?}", pos, at);
1016 } else {
1017 panic!("Expected begin, got {:?}", event);
1018 }
1019 }
1020
1021 fn expect_end(event: Option<path::PathEvent>, pos: Point) {
1022 std::eprintln!("- {:?}", event);
1023 if let Some(path::PathEvent::End { last, .. }) = event {
1024 assert!(last.approx_eq(&pos), "Expected End {:?}, got {:?}", pos, last);
1025 } else {
1026 panic!("Expected end, got {:?}", event);
1027 }
1028 }
1029 fn expect_line(event: Option<path::PathEvent>, expect_from: Point, expect_to: Point) {
1030 std::eprintln!("- {:?}", event);
1031 if let Some(path::PathEvent::Line { from, to }) = event {
1032 assert!(from.approx_eq(&expect_from), "Expected line {:?} {:?}, got {:?} {:?}", expect_from, expect_to, from, to);
1033 assert!(to.approx_eq(&expect_to), "Expected line {:?} {:?}, got {:?} {:?}", expect_from, expect_to, from, to);
1034 } else {
1035 panic!("Expected a line {:?} {:?}, got {:?}", expect_from, expect_to, event);
1036 }
1037 }
1038
1039 expect_begin(iter.next(), point(0.0, 0.0));
1040 expect_line(iter.next(), point(0.0, 0.0), point(5.0, 0.0));
1041 expect_end(iter.next(), point(5.0, 0.0));
1042
1043 expect_begin(iter.next(), point(5.0, 0.0));
1044 expect_line(iter.next(), point(5.0, 0.0), point(10.0, 0.0));
1045 expect_end(iter.next(), point(10.0, 0.0));
1046
1047 expect_begin(iter.next(), point(12.0, 10.0));
1048 expect_line(iter.next(), point(12.0, 10.0), point(15.0, 10.0));
1049 expect_end(iter.next(), point(15.0, 10.0));
1050
1051 expect_begin(iter.next(), point(15.0, 10.0));
1052 expect_line(iter.next(), point(15.0, 10.0), point(20.0, 10.0));
1053 expect_end(iter.next(), point(20.0, 10.0));
1054}