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);
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
572 let length = cur.distance - prev.distance;
573 if length == 0.0 {
574 return 0.0;
575 }
576
577 let t_begin = if prev.index == cur.index { prev.t } else { 0.0 };
578 let t_end = cur.t;
579 t_begin + (t_end - t_begin) * ((dist - prev.distance) / length)
580 }
581
582 fn sample_impl(&mut self, mut dist: f32, sample_type: SampleType) -> PathSample {
583 let length = self.length();
584 if length == 0.0 {
585 return self.sample_zero_length();
586 }
587 if sample_type == SampleType::Normalized {
588 dist *= length;
589 }
590 dist = dist.max(0.0);
591 dist = dist.min(length);
592
593 self.move_cursor(dist);
594 let t = self.t(dist);
595 macro_rules! dispatched_call {
596 ([$v:expr] ($seg:ident, $pair:ident) => $code:block) => {
597 #[allow(unused_parens)]
598 match $v {
599 SegmentWrapper::Line($seg, $pair) => $code,
600 SegmentWrapper::Quadratic($seg, $pair) => $code,
601 SegmentWrapper::Cubic($seg, $pair) => $code,
602 _ => {}
603 }
604 };
605 }
606
607 dispatched_call!([self.to_segment(self.events[self.edges[self.cursor].index])] (segment, pair) => {
608 self.interpolate_attributes(pair.0, pair.1, t);
609 return PathSample {
610 position: segment.sample(t),
611 tangent: segment.derivative(t).normalize(),
612 attributes: &self.attribute_buffer,
613 }
614 });
615
616 unreachable!();
617 }
618
619 #[cold]
620 fn sample_zero_length(&mut self) -> PathSample {
621 if let Some(IdEvent::Begin { at }) = self.events.first() {
622 return PathSample {
623 position: self.positions.get_endpoint(*at),
624 tangent: vector(0.0, 0.0),
625 attributes: self.attributes.get(*at),
626 };
627 }
628
629 for value in &mut self.attribute_buffer {
630 *value = f32::NAN;
631 }
632
633 PathSample {
634 position: point(f32::NAN, f32::NAN),
635 tangent: vector(f32::NAN, f32::NAN),
636 attributes: &self.attribute_buffer,
637 }
638 }
639
640 fn add_segment(
643 &mut self,
644 ptr: usize,
645 range: Option<Range<f32>>,
646 dest: &mut dyn PathBuilder,
647 is_in_subpath: &mut bool,
648 ) {
649 let segment = self.to_segment(self.events[ptr]);
650 let segment = match range.clone() {
651 Some(range) => segment.split(range),
652 None => segment,
653 };
654 macro_rules! obtain_attrs {
655 ($p:ident, $index:tt) => {
656 match range.clone() {
657 Some(range) => {
658 if range.end == 1.0 {
659 self.attributes.get($p.$index)
660 } else {
661 self.interpolate_attributes($p.0, $p.1, range.end);
662 &mut self.attribute_buffer
663 }
664 }
665 None => self.attributes.get($p.$index),
666 }
667 };
668 }
669
670 match segment {
671 SegmentWrapper::Line(LineSegment { from, to }, pair) => {
672 if !*is_in_subpath {
673 dest.end(false);
674 dest.begin(from, obtain_attrs!(pair, 0));
675 }
676 dest.line_to(to, obtain_attrs!(pair, 1));
677 }
678 SegmentWrapper::Quadratic(QuadraticBezierSegment { from, ctrl, to }, pair) => {
679 if !*is_in_subpath {
680 dest.end(false);
681 dest.begin(from, obtain_attrs!(pair, 0));
682 }
683 dest.quadratic_bezier_to(ctrl, to, obtain_attrs!(pair, 1));
684 }
685 SegmentWrapper::Cubic(
686 CubicBezierSegment {
687 from,
688 ctrl1,
689 ctrl2,
690 to,
691 },
692 pair,
693 ) => {
694 if !*is_in_subpath {
695 dest.end(false);
696 dest.begin(from, obtain_attrs!(pair, 0));
697 }
698 dest.cubic_bezier_to(ctrl1, ctrl2, to, obtain_attrs!(pair, 1));
699 }
700 _ => {}
701 }
702
703 *is_in_subpath = !matches!(
704 self.events[ptr],
705 IdEvent::End { .. } | IdEvent::Begin { .. }
706 );
707 }
708}
709
710#[cfg(test)]
711use path::geom::euclid::approxeq::ApproxEq;
712
713#[cfg(test)]
714fn slice(a: &[f32]) -> &[f32] {
715 a
716}
717
718#[test]
719fn measure_line() {
720 let mut path = Path::builder();
721 path.begin(point(1.0, 1.0));
722 path.line_to(point(0.0, 0.0));
723 path.end(false);
724 let path = path.build();
725 let measure = PathMeasurements::from_path(&path, 0.01);
726 let mut sampler = measure.create_sampler(&path, SampleType::Normalized);
727 for t in [0.0, 0.2, 0.3, 0.5, 1.0] {
728 let result = sampler.sample(t);
729 assert!((result.position - point(1.0 - t, 1.0 - t)).length() < 1e-5);
730 assert_eq!(result.tangent, vector(-1.0, -1.0).normalize());
731 }
732}
733
734#[test]
735fn measure_square() {
736 let mut path = Path::builder();
737 path.begin(point(0.0, 0.0));
738 path.line_to(point(1.0, 0.0));
739 path.line_to(point(1.0, 1.0));
740 path.line_to(point(0.0, 1.0));
741 path.close();
742 let path = path.build();
743 let measure = PathMeasurements::from_path(&path, 0.01);
744 let mut sampler = measure.create_sampler(&path, SampleType::Normalized);
745 for (t, position, tangent) in [
746 (0.125, point(0.5, 0.0), vector(1.0, 0.0)),
747 (0.375, point(1.0, 0.5), vector(0.0, 1.0)),
748 (0.625, point(0.5, 1.0), vector(-1.0, 0.0)),
749 (0.875, point(0.0, 0.5), vector(0.0, -1.0)),
750 ] {
751 let result = sampler.sample(t);
752 assert!((result.position - position).length() < 1e-5);
753 assert_eq!(result.tangent, tangent);
754 }
755}
756
757#[test]
758fn measure_attributes() {
759 let mut path = Path::builder_with_attributes(2);
760 path.begin(point(0.0, 0.0), &[1.0, 2.0]);
761 path.line_to(point(1.0, 0.0), &[2.0, 3.0]);
762 path.line_to(point(1.0, 1.0), &[0.0, 0.0]);
763 path.end(false);
764 let path = path.build();
765 let measure = PathMeasurements::from_path(&path, 0.01);
766 let mut sampler = measure.create_sampler_with_attributes(&path, &path, SampleType::Normalized);
767
768 for (t, position, attrs) in [
769 (0.25, point(0.5, 0.0), &[1.5, 2.5]),
770 (0.5, point(1.0, 0.0), &[2.0, 3.0]),
771 (0.75, point(1.0, 0.5), &[1.0, 1.5]),
772 ] {
773 let result = sampler.sample(t);
774 assert!((result.position - position).length() < 1e-5);
775 for i in 0..2 {
776 assert!((result.attributes[i] - attrs[i]).abs() < 1e-5);
777 }
778 }
779}
780
781#[test]
782fn measure_bezier_curve() {
783 let mut path = Path::builder();
784 path.begin(point(0.0, 0.0));
785 path.quadratic_bezier_to(point(0.5, 0.7), point(1.0, 0.0));
786 path.quadratic_bezier_to(point(1.5, -0.7), point(2.0, 0.0));
787 path.end(false);
788 let path = path.build();
789
790 let measure = PathMeasurements::from_path(&path, 0.01);
791 let mut sampler = measure.create_sampler(&path, SampleType::Normalized);
792
793 for t in [0.25, 0.75] {
794 let result = sampler.sample(t);
795 assert!(result.tangent.approx_eq(&vector(1.0, 0.0)));
796 }
797 for (t, position) in [
798 (0.0, point(0.0, 0.0)),
799 (0.5, point(1.0, 0.0)),
800 (1.0, point(2.0, 0.0)),
801 ] {
802 let result = sampler.sample(t);
803 assert!(result.position.approx_eq(&position));
804 }
805}
806
807#[test]
808fn split_square() {
809 use crate::path::Event;
810
811 let mut path = Path::builder();
812 path.begin(point(0.0, 0.0));
813 path.line_to(point(1.0, 0.0));
814 path.line_to(point(1.0, 1.0));
815 path.line_to(point(0.0, 1.0));
816 path.close();
817 let path = path.build();
818 let measure = PathMeasurements::from_path(&path, 0.01);
819 let mut sampler = measure.create_sampler(&path, SampleType::Normalized);
820 let mut path2 = Path::builder();
821 sampler.split_range(0.125..0.625, &mut path2);
822 let path2 = path2.build();
823 assert_eq!(
824 path2.iter().collect::<Vec<_>>(),
825 alloc::vec![
826 Event::Begin {
827 at: point(0.5, 0.0)
828 },
829 Event::Line {
830 from: point(0.5, 0.0),
831 to: point(1.0, 0.0)
832 },
833 Event::Line {
834 from: point(1.0, 0.0),
835 to: point(1.0, 1.0)
836 },
837 Event::Line {
838 from: point(1.0, 1.0),
839 to: point(0.5, 1.0)
840 },
841 Event::End {
842 last: point(0.5, 1.0),
843 first: point(0.5, 0.0),
844 close: false
845 },
846 ]
847 );
848}
849
850#[test]
851fn split_bezier_curve() {
852 use crate::path::Event;
853
854 let mut path = Path::builder();
855 path.begin(point(0.0, 0.0));
856 path.quadratic_bezier_to(point(1.0, 1.0), point(2.0, 0.0));
857 path.end(false);
858 let path = path.build();
859 let measure = PathMeasurements::from_path(&path, 0.01);
860 let mut sampler = measure.create_sampler(&path, SampleType::Normalized);
861
862 let mut path2 = Path::builder();
863 sampler.split_range(0.5..1.0, &mut path2);
864 let path2 = path2.build();
865
866 assert_eq!(
867 path2.iter().collect::<Vec<_>>(),
868 alloc::vec![
869 Event::Begin {
870 at: point(1.0, 0.5)
871 },
872 Event::Quadratic {
873 from: point(1.0, 0.5),
874 ctrl: point(1.5, 0.5),
875 to: point(2.0, 0.0),
876 },
877 Event::End {
878 last: point(2.0, 0.0),
879 first: point(1.0, 0.5),
880 close: false
881 }
882 ]
883 );
884}
885
886#[test]
887fn split_attributes() {
888 use crate::path::Event;
889
890 let mut path = Path::builder_with_attributes(2);
891 path.begin(point(0.0, 0.0), &[1.0, 2.0]);
892 path.line_to(point(1.0, 0.0), &[2.0, 3.0]);
893 path.line_to(point(1.0, 1.0), &[0.0, 0.0]);
894 path.end(false);
895 let path = path.build();
896 let measure = PathMeasurements::from_path(&path, 0.01);
897 let mut sampler = measure.create_sampler_with_attributes(&path, &path, SampleType::Normalized);
898
899 let mut path2 = Path::builder_with_attributes(2);
900 sampler.split_range(0.0..1.0, &mut path2);
901 let path2 = path2.build();
902
903 assert_eq!(
904 path2.iter_with_attributes().collect::<Vec<_>>(),
905 path.iter_with_attributes().collect::<Vec<_>>()
906 );
907
908 let mut path3 = Path::builder_with_attributes(2);
909 sampler.split_range(0.25..0.75, &mut path3);
910 let path3 = path3.build();
911
912 assert_eq!(
913 path3.iter_with_attributes().collect::<Vec<_>>(),
914 alloc::vec![
915 Event::Begin {
916 at: (point(0.5, 0.0), slice(&[1.5, 2.5]))
917 },
918 Event::Line {
919 from: (point(0.5, 0.0), slice(&[1.5, 2.5])),
920 to: (point(1.0, 0.0), slice(&[2.0, 3.0])),
921 },
922 Event::Line {
923 from: (point(1.0, 0.0), slice(&[2.0, 3.0])),
924 to: (point(1.0, 0.5), slice(&[1.0, 1.5])),
925 },
926 Event::End {
927 last: (point(1.0, 0.5), slice(&[1.0, 1.5])),
928 first: (point(0.5, 0.0), slice(&[1.5, 2.5])),
929 close: false
930 }
931 ]
932 );
933}
934
935#[test]
936fn zero_length() {
937 fn expect_nans(sample: PathSample, num_attribs: usize) {
938 assert!(sample.position.x.is_nan());
939 assert!(sample.position.y.is_nan());
940 assert!(sample.tangent.x.is_nan());
941 assert!(sample.tangent.y.is_nan());
942 for attr in sample.attributes {
943 assert!(attr.is_nan());
944 }
945 assert_eq!(sample.attributes.len(), num_attribs);
946 }
947
948 let mut path = Path::builder_with_attributes(2);
949 path.begin(point(1.0, 2.0), &[3.0, 4.0]);
950 path.end(false);
951 let path = path.build();
952 let measure = PathMeasurements::from_path(&path, 0.01);
953 let mut sampler = measure.create_sampler_with_attributes(&path, &path, SampleType::Normalized);
954 let expected = PathSample {
955 position: point(1.0, 2.0),
956 tangent: vector(0.0, 0.0),
957 attributes: &[3.0, 4.0],
958 };
959 assert_eq!(sampler.sample(0.0), expected);
960 assert_eq!(sampler.sample(0.5), expected);
961 assert_eq!(sampler.sample(1.0), expected);
962
963 let mut path = Path::builder_with_attributes(2);
964 path.begin(point(1.0, 2.0), &[3.0, 4.0]);
965 path.end(false);
966 let path = path.build();
967 let measure = PathMeasurements::from_path(&path, 0.01);
968 let mut sampler = measure.create_sampler_with_attributes(&path, &path, SampleType::Distance);
969 let expected = PathSample {
970 position: point(1.0, 2.0),
971 tangent: vector(0.0, 0.0),
972 attributes: &[3.0, 4.0],
973 };
974 assert_eq!(sampler.sample(0.0), expected);
975 assert_eq!(sampler.sample(0.5), expected);
976 assert_eq!(sampler.sample(1.0), expected);
977
978 let path = Path::builder_with_attributes(2).build();
979 let measure = PathMeasurements::from_path(&path, 0.01);
980 let mut sampler = measure.create_sampler_with_attributes(&path, &path, SampleType::Normalized);
981 expect_nans(sampler.sample(0.0), 2);
982 expect_nans(sampler.sample(0.5), 2);
983 expect_nans(sampler.sample(1.0), 2);
984
985 let path = Path::builder_with_attributes(2).build();
986 let measure = PathMeasurements::from_path(&path, 0.01);
987 let mut sampler = measure.create_sampler_with_attributes(&path, &path, SampleType::Distance);
988 expect_nans(sampler.sample(0.0), 2);
989 expect_nans(sampler.sample(0.5), 2);
990 expect_nans(sampler.sample(1.0), 2);
991}
992
993#[test]
994fn multiple_sub_paths() {
995 let mut path = Path::builder();
996
997 path.begin(point(0.0, 0.0));
998 path.line_to(point(10.0, 0.0));
999 path.end(false);
1000
1001 path.begin(point(10.0, 10.0));
1002 path.line_to(point(20.0, 10.0));
1003 path.end(false);
1004
1005 let path = path.build();
1006 let measure = PathMeasurements::from_path(&path, 0.01);
1007 let mut sampler = measure.create_sampler(&path, SampleType::Normalized);
1008
1009 let mut dashes = Path::builder();
1010 sampler.split_range(0.0..0.25, &mut dashes);
1011 sampler.split_range(0.25..0.5, &mut dashes);
1012 sampler.split_range(0.6..0.75, &mut dashes);
1014 sampler.split_range(0.75..1.0, &mut dashes);
1015 let dashes = dashes.build();
1016
1017 let mut iter = dashes.iter();
1018
1019 use crate::path::geom::euclid::approxeq::ApproxEq;
1020 fn expect_begin(event: Option<path::PathEvent>, pos: Point) {
1021 std::eprintln!("- {:?}", event);
1022 if let Some(path::PathEvent::Begin { at }) = event {
1023 assert!(at.approx_eq(&pos), "Expected Begin {:?}, got {:?}", pos, at);
1024 } else {
1025 panic!("Expected begin, got {:?}", event);
1026 }
1027 }
1028
1029 fn expect_end(event: Option<path::PathEvent>, pos: Point) {
1030 std::eprintln!("- {:?}", event);
1031 if let Some(path::PathEvent::End { last, .. }) = event {
1032 assert!(
1033 last.approx_eq(&pos),
1034 "Expected End {:?}, got {:?}",
1035 pos,
1036 last
1037 );
1038 } else {
1039 panic!("Expected end, got {:?}", event);
1040 }
1041 }
1042 fn expect_line(event: Option<path::PathEvent>, expect_from: Point, expect_to: Point) {
1043 std::eprintln!("- {:?}", event);
1044 if let Some(path::PathEvent::Line { from, to }) = event {
1045 assert!(
1046 from.approx_eq(&expect_from),
1047 "Expected line {:?} {:?}, got {:?} {:?}",
1048 expect_from,
1049 expect_to,
1050 from,
1051 to
1052 );
1053 assert!(
1054 to.approx_eq(&expect_to),
1055 "Expected line {:?} {:?}, got {:?} {:?}",
1056 expect_from,
1057 expect_to,
1058 from,
1059 to
1060 );
1061 } else {
1062 panic!(
1063 "Expected a line {:?} {:?}, got {:?}",
1064 expect_from, expect_to, event
1065 );
1066 }
1067 }
1068
1069 expect_begin(iter.next(), point(0.0, 0.0));
1070 expect_line(iter.next(), point(0.0, 0.0), point(5.0, 0.0));
1071 expect_end(iter.next(), point(5.0, 0.0));
1072
1073 expect_begin(iter.next(), point(5.0, 0.0));
1074 expect_line(iter.next(), point(5.0, 0.0), point(10.0, 0.0));
1075 expect_end(iter.next(), point(10.0, 0.0));
1076
1077 expect_begin(iter.next(), point(12.0, 10.0));
1078 expect_line(iter.next(), point(12.0, 10.0), point(15.0, 10.0));
1079 expect_end(iter.next(), point(15.0, 10.0));
1080
1081 expect_begin(iter.next(), point(15.0, 10.0));
1082 expect_line(iter.next(), point(15.0, 10.0), point(20.0, 10.0));
1083 expect_end(iter.next(), point(20.0, 10.0));
1084}
1085
1086#[test]
1087fn zero_length_when_first_2_points_are_same() {
1088 use crate::path::Event;
1089
1090 let mut path = Path::builder();
1091
1092 path.begin(point(0.0, 0.0));
1093 path.line_to(point(0.0, 0.0));
1094 path.line_to(point(1.0, 0.0));
1095 path.end(false);
1096
1097 let path = path.build();
1098 let measure = PathMeasurements::from_path(&path, 0.01);
1099 let mut sampler = measure.create_sampler(&path, SampleType::Normalized);
1100
1101 let mut dashes = Path::builder();
1102
1103 sampler.split_range(0.0..1.0, &mut dashes);
1104 let dashes = dashes.build();
1105
1106 assert_eq!(
1107 dashes.iter_with_attributes().collect::<Vec<_>>(),
1108 alloc::vec![
1109 Event::Begin {
1110 at: (point(0.0, 0.0), slice(&[]))
1111 },
1112 Event::Line {
1113 from: (point(0.0, 0.0), slice(&[])),
1114 to: (point(0.0, 0.0), slice(&[])),
1115 },
1116 Event::Line {
1117 from: (point(0.0, 0.0), slice(&[])),
1118 to: (point(1.0, 0.0), slice(&[])),
1119 },
1120 Event::End {
1121 last: (point(1.0, 0.0), slice(&[])),
1122 first: (point(0.0, 0.0), slice(&[])),
1123 close: false
1124 }
1125 ]
1126 );
1127}