1use super::polygon::{
19 ANGLE_EPSILON, Cubic, DISTANCE_EPSILON, Pt, RoundedPolygon, atan2, interpolate,
20 positive_modulo, pt,
21};
22
23fn progress_in_range(progress: f32, from: f32, to: f32) -> bool {
25 if to >= from {
26 (from..=to).contains(&progress)
27 } else {
28 progress >= from || progress <= to
29 }
30}
31
32fn progress_distance(a: f32, b: f32) -> f32 {
34 let distance = (a - b).abs();
35 distance.min(1. - distance)
36}
37
38fn linear_map(xs: &[f32], ys: &[f32], x: f32) -> f32 {
39 let n = xs.len();
40 let start = (0..n)
41 .find(|&i| progress_in_range(x, xs[i], xs[(i + 1) % n]))
42 .unwrap_or(0);
43 let end = (start + 1) % n;
44 let size_x = positive_modulo(xs[end] - xs[start], 1.);
45 let size_y = positive_modulo(ys[end] - ys[start], 1.);
46 let position = if size_x < 0.001 {
47 0.5
48 } else {
49 positive_modulo(x - xs[start], 1.) / size_x
50 };
51 positive_modulo(ys[start] + size_y * position, 1.)
52}
53
54struct DoubleMapper {
56 source: Vec<f32>,
57 target: Vec<f32>,
58}
59
60impl DoubleMapper {
61 fn new(mappings: &[(f32, f32)]) -> Self {
63 let source: Vec<f32> = mappings.iter().map(|m| m.0).collect();
64 let target: Vec<f32> = mappings.iter().map(|m| m.1).collect();
65 if valid_progress(&source) && valid_progress(&target) {
66 Self { source, target }
67 } else {
68 Self {
69 source: vec![0., 0.5],
70 target: vec![0., 0.5],
71 }
72 }
73 }
74 fn map(&self, x: f32) -> f32 {
75 linear_map(&self.source, &self.target, x)
76 }
77 fn map_back(&self, x: f32) -> f32 {
78 linear_map(&self.target, &self.source, x)
79 }
80}
81
82fn valid_progress(values: &[f32]) -> bool {
84 let Some(&last) = values.last() else {
85 return false;
86 };
87 let mut previous = last;
88 let mut wraps = 0;
89 for ¤t in values {
90 if !(0. ..1.).contains(¤t) || progress_distance(current, previous) <= DISTANCE_EPSILON
91 {
92 return false;
93 }
94 if current < previous {
95 wraps += 1;
96 if wraps > 1 {
97 return false;
98 }
99 }
100 previous = current;
101 }
102 true
103}
104
105#[derive(Clone, Copy)]
107struct MeasuredCorner {
108 progress: f32,
109 representative: Pt,
110 convex: bool,
111}
112
113fn feature_mapper(corners1: &[MeasuredCorner], corners2: &[MeasuredCorner]) -> DoubleMapper {
115 let mut pairs: Vec<(f32, usize, usize)> = Vec::new();
116 for (i, a) in corners1.iter().enumerate() {
117 for (j, b) in corners2.iter().enumerate() {
118 if a.convex == b.convex {
120 let d = (a.representative - b.representative).distance_squared();
121 if d != f32::MAX {
122 pairs.push((d, i, j));
123 }
124 }
125 }
126 }
127 pairs.sort_by(|a, b| a.0.total_cmp(&b.0));
128 match pairs.as_slice() {
129 [] => DoubleMapper::new(&[(0., 0.), (0.5, 0.5)]),
130 [(_, i, j)] => {
131 let (f1, f2) = (corners1[*i].progress, corners2[*j].progress);
132 DoubleMapper::new(&[(f1, f2), ((f1 + 0.5) % 1., (f2 + 0.5) % 1.)])
133 }
134 _ => {
135 let mut mapping: Vec<(f32, f32)> = Vec::new();
136 let mut used1 = vec![false; corners1.len()];
137 let mut used2 = vec![false; corners2.len()];
138 for &(_, i, j) in &pairs {
139 if used1[i] || used2[j] {
140 continue;
141 }
142 let (f1, f2) = (corners1[i].progress, corners2[j].progress);
143 let Err(at) = mapping.binary_search_by(|m| m.0.total_cmp(&f1)) else {
144 continue;
145 };
146 let n = mapping.len();
147 if n >= 1 {
148 let (before1, before2) = mapping[(at + n - 1) % n];
149 let (after1, after2) = mapping[at % n];
150 if progress_distance(f1, before1) < DISTANCE_EPSILON
151 || progress_distance(f1, after1) < DISTANCE_EPSILON
152 || progress_distance(f2, before2) < DISTANCE_EPSILON
153 || progress_distance(f2, after2) < DISTANCE_EPSILON
154 {
155 continue;
156 }
157 if n > 1 && !progress_in_range(f2, before2, after2) {
158 continue;
159 }
160 }
161 mapping.insert(at, (f1, f2));
162 used1[i] = true;
163 used2[j] = true;
164 }
165 DoubleMapper::new(&mapping)
166 }
167 }
168}
169
170fn greedy_picks(f1: &[MeasuredCorner], f2: &[MeasuredCorner]) -> Vec<MeasuredCorner> {
173 let distance = |a: &MeasuredCorner, b: &MeasuredCorner| {
174 if a.convex == b.convex {
175 (a.representative - b.representative).distance_squared()
176 } else {
177 f32::MAX
178 }
179 };
180 let nearest = |corner: &MeasuredCorner, from: usize, to: usize| {
181 let mut best = from;
182 let mut best_distance = distance(corner, &f2[from % f2.len()]);
183 for i in from + 1..=to {
184 let d = distance(corner, &f2[i % f2.len()]);
185 if d < best_distance {
186 best = i;
187 best_distance = d;
188 }
189 }
190 best
191 };
192 let (m, n) = (f1.len(), f2.len());
193 let first = nearest(&f1[0], 0, n - 1);
194 let mut picks = vec![f2[first]];
195 let mut last_picked = first;
196 for (i, corner) in f1.iter().enumerate().skip(1) {
197 let mut last = first as isize - (m - i) as isize;
198 if last <= last_picked as isize {
199 last += n as isize;
200 }
201 let best = nearest(corner, last_picked + 1, last.max(0) as usize);
202 picks.push(f2[best % n]);
203 last_picked = best;
204 }
205 picks
206}
207
208fn greedy_mapper(corners1: &[MeasuredCorner], corners2: &[MeasuredCorner]) -> DoubleMapper {
211 if corners1.is_empty() || corners2.is_empty() {
212 return DoubleMapper::new(&[(0., 0.), (0.5, 0.5)]);
213 }
214 let pairs: Vec<(f32, f32)> = if corners1.len() > corners2.len() {
215 greedy_picks(corners2, corners1)
216 .iter()
217 .zip(corners2)
218 .map(|(a, b)| (a.progress, b.progress))
219 .collect()
220 } else {
221 corners1
222 .iter()
223 .zip(greedy_picks(corners1, corners2))
224 .map(|(a, b)| (a.progress, b.progress))
225 .collect()
226 };
227 DoubleMapper::new(&pairs)
228}
229
230const MEASURE_SEGMENTS: u32 = 3;
231
232#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
234pub enum MorphMatching {
235 #[default]
239 Current,
240 Compose1_5,
244}
245
246#[derive(Clone, Copy)]
248enum Measurer {
249 Length,
250 Angle(Pt),
251}
252
253impl Measurer {
254 fn of(matching: MorphMatching, polygon: &RoundedPolygon) -> Self {
255 match matching {
256 MorphMatching::Current => Self::Length,
257 MorphMatching::Compose1_5 => {
258 let (x, y) = polygon.center();
259 Self::Angle(pt(x, y))
260 }
261 }
262 }
263
264 fn measure(self, cubic: &Cubic) -> f32 {
265 match self {
266 Self::Length => closest_progress_to(cubic, f32::INFINITY).1,
267 Self::Angle(center) => {
268 let sweep = positive_modulo(
269 angle_around(center, cubic.anchor1()) - angle_around(center, cubic.anchor0()),
270 TWO_PI,
271 );
272 if sweep > TWO_PI - DISTANCE_EPSILON {
274 0.
275 } else {
276 sweep
277 }
278 }
279 }
280 }
281
282 fn cut_point(self, cubic: &Cubic, measure: f32) -> f32 {
283 match self {
284 Self::Length => closest_progress_to(cubic, measure).0,
285 Self::Angle(center) => {
286 let start = angle_around(center, cubic.anchor0());
287 find_minimum(|t| {
288 let at = cubic.point_at(t);
289 (positive_modulo(angle_around(center, at) - start, TWO_PI) - measure).abs()
290 })
291 }
292 }
293 }
294}
295
296const TWO_PI: f32 = 2. * std::f32::consts::PI;
297
298fn angle_around(center: Pt, point: (f32, f32)) -> f32 {
299 (atan2(point.1 - center.y, point.0 - center.x) + TWO_PI) % TWO_PI
300}
301
302fn find_minimum(f: impl Fn(f32) -> f32) -> f32 {
304 let (mut a, mut b) = (0_f32, 1_f32);
305 while b - a > 1e-5 {
306 let c1 = (2. * a + b) / 3.;
307 let c2 = (2. * b + a) / 3.;
308 if f(c1) < f(c2) {
309 b = c2;
310 } else {
311 a = c1;
312 }
313 }
314 (a + b) / 2.
315}
316
317fn closest_progress_to(cubic: &Cubic, threshold: f32) -> (f32, f32) {
320 let mut total = 0_f32;
321 let mut remainder = threshold;
322 let mut previous = pt(cubic.points[0], cubic.points[1]);
323 for i in 1..=MEASURE_SEGMENTS {
324 let progress = i as f32 / MEASURE_SEGMENTS as f32;
325 let point = cubic.point_on_curve(progress);
326 let segment = (point - previous).distance();
327 if segment >= remainder {
328 return (
329 progress - (1. - remainder / segment) / MEASURE_SEGMENTS as f32,
330 threshold,
331 );
332 }
333 remainder -= segment;
334 total += segment;
335 previous = point;
336 }
337 (1., total)
338}
339
340#[derive(Clone, Copy)]
341struct MeasuredCubic {
342 cubic: Cubic,
343 start: f32,
344 end: f32,
345 size: f32,
346 measurer: Measurer,
347}
348
349impl MeasuredCubic {
350 fn new(measurer: Measurer, cubic: Cubic, start: f32, end: f32) -> Self {
351 Self {
352 cubic,
353 start,
354 end,
355 size: measurer.measure(&cubic),
356 measurer,
357 }
358 }
359
360 fn cut_at(&self, progress: f32) -> (Self, Self) {
361 let bounded = if progress < self.start {
363 self.start
364 } else if progress > self.end {
365 self.end
366 } else {
367 progress
368 };
369 let relative = (bounded - self.start) / (self.end - self.start);
370 let t = self.measurer.cut_point(&self.cubic, relative * self.size);
371 let t = if t.is_nan() { 0. } else { t.clamp(0., 1.) };
373 let (c1, c2) = self.cubic.split(t);
374 (
375 Self::new(self.measurer, c1, self.start, bounded),
376 Self::new(self.measurer, c2, bounded, self.end),
377 )
378 }
379}
380
381fn measured_cubics(
382 measurer: Measurer,
383 cubics: &[Cubic],
384 outline_progress: &[f32],
385) -> Vec<MeasuredCubic> {
386 let mut measured: Vec<MeasuredCubic> = Vec::with_capacity(cubics.len());
387 let mut start = 0.;
388 for (i, cubic) in cubics.iter().enumerate() {
389 if outline_progress[i + 1] - outline_progress[i] > DISTANCE_EPSILON {
390 measured.push(MeasuredCubic::new(
391 measurer,
392 *cubic,
393 start,
394 outline_progress[i + 1],
395 ));
396 start = outline_progress[i + 1];
397 }
398 }
399 match measured.last_mut() {
400 Some(last) => last.end = 1.,
401 None => measured.push(MeasuredCubic::new(
403 measurer,
404 cubics.first().copied().unwrap_or_default(),
405 0.,
406 1.,
407 )),
408 }
409 measured
410}
411
412fn measure_polygon(
413 measurer: Measurer,
414 polygon: &RoundedPolygon,
415) -> (Vec<MeasuredCubic>, Vec<MeasuredCorner>) {
416 let mut cubics = Vec::new();
417 let mut corner_cubics: Vec<(usize, Pt, bool)> = Vec::new();
418 for feature in &polygon.features {
419 for (index, cubic) in feature.cubics.iter().enumerate() {
420 if let Some(convex) = feature.corner
421 && index == feature.cubics.len() / 2
422 {
423 let first = feature.cubics.first().copied().unwrap_or_default();
424 let last = feature.cubics.last().copied().unwrap_or_default();
425 let representative = pt(
426 (first.points[0] + last.points[6]) / 2.,
427 (first.points[1] + last.points[7]) / 2.,
428 );
429 corner_cubics.push((cubics.len(), representative, convex));
430 }
431 cubics.push(*cubic);
432 }
433 }
434 let mut measures = Vec::with_capacity(cubics.len() + 1);
435 measures.push(0_f32);
436 let mut sum = 0_f32;
437 for cubic in &cubics {
438 sum += measurer.measure(cubic);
439 measures.push(sum);
440 }
441 let outline_progress: Vec<f32> = measures.iter().map(|m| m / sum).collect();
442 let corners = corner_cubics
443 .into_iter()
444 .map(|(i, representative, convex)| MeasuredCorner {
445 progress: {
446 let middle = (outline_progress[i] + outline_progress[i + 1]) / 2.;
447 match measurer {
449 Measurer::Length => positive_modulo(middle, 1.),
450 Measurer::Angle(_) => middle,
451 }
452 },
453 representative,
454 convex,
455 })
456 .collect();
457 (
458 measured_cubics(measurer, &cubics, &outline_progress),
459 corners,
460 )
461}
462
463fn cut_and_shift(cubics: Vec<MeasuredCubic>, cut: f32) -> Vec<MeasuredCubic> {
465 if cut < DISTANCE_EPSILON {
466 return cubics;
467 }
468 let target_index = cubics
469 .iter()
470 .position(|c| (c.start..=c.end).contains(&cut))
471 .unwrap_or(0);
472 let measurer = cubics[target_index].measurer;
473 let (b1, b2) = cubics[target_index].cut_at(cut);
474 let n = cubics.len();
475 let mut shifted = Vec::with_capacity(n + 1);
476 shifted.push(b2.cubic);
477 for i in 1..n {
478 shifted.push(cubics[(i + target_index) % n].cubic);
479 }
480 shifted.push(b1.cubic);
481 let mut progress = Vec::with_capacity(n + 2);
482 for index in 0..n + 2 {
483 progress.push(if index == 0 {
484 0.
485 } else if index == n + 1 {
486 1.
487 } else {
488 positive_modulo(cubics[(target_index + index - 1) % n].end - cut, 1.)
489 });
490 }
491 measured_cubics(measurer, &shifted, &progress)
492}
493
494fn match_cubics(
495 p1: &RoundedPolygon,
496 p2: &RoundedPolygon,
497 matching: MorphMatching,
498) -> Vec<(Cubic, Cubic)> {
499 let (bs1, corners1) = measure_polygon(Measurer::of(matching, p1), p1);
500 let (measured2, corners2) = measure_polygon(Measurer::of(matching, p2), p2);
501 let mapper = match matching {
502 MorphMatching::Current => feature_mapper(&corners1, &corners2),
503 MorphMatching::Compose1_5 => greedy_mapper(&corners1, &corners2),
504 };
505 let cut = mapper.map(0.);
506 let bs2 = cut_and_shift(measured2, cut);
507
508 let mut matched = Vec::with_capacity(bs1.len().max(bs2.len()) * 2);
509 let (mut i1, mut i2) = (1, 1);
510 let mut b1 = bs1.first().copied();
511 let mut b2 = bs2.first().copied();
512 while let (Some(c1), Some(c2)) = (b1, b2) {
513 let b1a = if i1 == bs1.len() { 1. } else { c1.end };
514 let b2a = if i2 == bs2.len() {
515 1.
516 } else {
517 mapper.map_back(positive_modulo(c2.end + cut, 1.))
518 };
519 let min = b1a.min(b2a);
520 let (segment1, next1) = if b1a > min + ANGLE_EPSILON {
521 let (a, b) = c1.cut_at(min);
522 (a, Some(b))
523 } else {
524 let next = bs1.get(i1).copied();
525 i1 += 1;
526 (c1, next)
527 };
528 let (segment2, next2) = if b2a > min + ANGLE_EPSILON {
529 let (a, b) = c2.cut_at(positive_modulo(mapper.map(min) - cut, 1.));
530 (a, Some(b))
531 } else {
532 let next = bs2.get(i2).copied();
533 i2 += 1;
534 (c2, next)
535 };
536 matched.push((segment1.cubic, segment2.cubic));
537 b1 = next1;
538 b2 = next2;
539 }
540 matched
541}
542
543#[derive(Clone, Debug)]
546pub struct Morph {
547 start: RoundedPolygon,
548 end: RoundedPolygon,
549 matched: Vec<(Cubic, Cubic)>,
550}
551
552impl Morph {
553 pub fn new(start: &RoundedPolygon, end: &RoundedPolygon) -> Self {
557 Self::with_matching(start, end, MorphMatching::default())
558 }
559
560 pub fn with_matching(
562 start: &RoundedPolygon,
563 end: &RoundedPolygon,
564 matching: MorphMatching,
565 ) -> Self {
566 Self {
567 start: start.clone(),
568 end: end.clone(),
569 matched: match_cubics(start, end, matching),
570 }
571 }
572
573 pub fn len(&self) -> usize {
575 self.matched.len()
576 }
577
578 pub fn is_empty(&self) -> bool {
580 self.matched.is_empty()
581 }
582
583 pub fn for_each_cubic(&self, progress: f32, mut each: impl FnMut(&Cubic)) {
587 for (a, b) in &self.matched {
588 let mut cubic = Cubic::default();
589 for (value, (from, to)) in cubic.points.iter_mut().zip(a.points.iter().zip(&b.points)) {
590 *value = interpolate(*from, *to, progress);
591 }
592 each(&cubic);
593 }
594 }
595
596 pub fn as_cubics(&self, progress: f32) -> Vec<Cubic> {
598 let mut cubics = Vec::with_capacity(self.matched.len());
599 self.for_each_cubic(progress, |cubic| cubics.push(*cubic));
600 if let [first, .., last] = cubics.as_mut_slice() {
601 last.points[6] = first.points[0];
602 last.points[7] = first.points[1];
603 }
604 cubics
605 }
606
607 pub fn bounds(&self, approximate: bool) -> [f32; 4] {
609 union(self.start.bounds(approximate), self.end.bounds(approximate))
610 }
611
612 pub fn max_bounds(&self) -> [f32; 4] {
614 union(self.start.max_bounds(), self.end.max_bounds())
615 }
616}
617
618fn union(a: [f32; 4], b: [f32; 4]) -> [f32; 4] {
619 [
620 a[0].min(b[0]),
621 a[1].min(b[1]),
622 a[2].max(b[2]),
623 a[3].max(b[3]),
624 ]
625}
626
627#[cfg(test)]
628mod tests {
629 use super::*;
630 use crate::shapes::polygon::CornerRounding;
631 use core::prelude::v1::test;
632
633 #[test]
634 fn degenerate_and_mismatched_polygons_morph_without_panicking() {
635 let point =
636 RoundedPolygon::from_vertices(&[(1., 1.); 3], CornerRounding::UNROUNDED, None, None)
637 .expect("finite");
638 let star = RoundedPolygon::star(5, 1., 0.4, CornerRounding::new(0.1), None, (0., 0.))
639 .expect("valid");
640 let triangle = RoundedPolygon::regular(3, 1., (0., 0.), CornerRounding::UNROUNDED, None)
641 .expect("valid");
642 for (a, b) in [
643 (&point, &star),
644 (&star, &point),
645 (&point, &point),
646 (&star, &triangle),
647 (&triangle, &star),
648 ] {
649 let morph = Morph::new(a, b);
650 assert!(!morph.is_empty());
651 for progress in [0., 0.5, 1., 1.2] {
652 assert!(
653 morph
654 .as_cubics(progress)
655 .iter()
656 .all(|c| c.points.iter().all(|v| v.is_finite())),
657 "{progress}"
658 );
659 }
660 }
661 }
662
663 #[test]
664 fn the_mapper_rejects_repeats_and_double_wraps() {
665 assert!(valid_progress(&[0.3, 0.6, 0.]));
666 assert!(!valid_progress(&[0.5, 0., 0.7]));
667 assert!(!valid_progress(&[0.5]));
668 assert!(!valid_progress(&[]));
669 assert!(!valid_progress(&[0.2, 1.2]));
670 let identity = DoubleMapper::new(&[(0.5, 0.5)]);
671 assert_eq!(identity.map(0.25), 0.25);
672 }
673}