1use std::collections::HashSet;
11
12use super::GlyphDelta;
13use crate::{round::OtRound, util::WrappingGet};
14
15use kurbo::{Point, Vec2};
16
17const NUM_PHANTOM_POINTS: usize = 4;
18
19pub fn iup_delta_optimize(
29 deltas: Vec<Vec2>,
30 coords: Vec<Point>,
31 tolerance: f64,
32 contour_ends: &[usize],
33) -> Result<Vec<GlyphDelta>, IupError> {
34 let num_coords = coords.len();
35 if num_coords < NUM_PHANTOM_POINTS {
36 return Err(IupError::NotEnoughCoords(num_coords));
37 }
38 if deltas.len() != coords.len() {
39 return Err(IupError::DeltaCoordLengthMismatch {
40 num_deltas: deltas.len(),
41 num_coords: coords.len(),
42 });
43 }
44
45 let mut contour_ends = contour_ends.to_vec();
46 contour_ends.sort();
47
48 let expected_num_coords = contour_ends
49 .last()
50 .copied()
51 .map(|v| v + 1)
52 .unwrap_or_default()
53 + NUM_PHANTOM_POINTS;
54 if num_coords != expected_num_coords {
55 return Err(IupError::CoordEndsMismatch {
56 num_coords,
57 expected_num_coords,
58 });
59 }
60
61 for offset in (1..=4).rev() {
62 contour_ends.push(num_coords.saturating_sub(offset));
63 }
64
65 let mut result = Vec::with_capacity(num_coords);
66 let mut start = 0;
67 let mut deltas = deltas;
68 let mut coords = coords;
69 for end in contour_ends {
70 let contour = iup_contour_optimize(
71 &mut deltas[start..=end],
72 &mut coords[start..=end],
73 tolerance,
74 )?;
75 result.extend_from_slice(&contour);
76 assert_eq!(contour.len() + start, end + 1);
77 start = end + 1;
78 }
79 Ok(result)
80}
81
82#[derive(Clone, Debug)]
83pub enum IupError {
84 DeltaCoordLengthMismatch {
85 num_deltas: usize,
86 num_coords: usize,
87 },
88 NotEnoughCoords(usize),
89 CoordEndsMismatch {
90 num_coords: usize,
91 expected_num_coords: usize,
92 },
93 AchievedInvalidState(String),
94}
95
96fn must_encode_at(deltas: &[Vec2], coords: &[Point], tolerance: f64, at: usize) -> bool {
100 let ld = *deltas.wrapping_prev(at);
101 let d = deltas[at];
102 let nd = *deltas.wrapping_next(at);
103 let lc = *coords.wrapping_prev(at);
104 let c = coords[at];
105 let nc = *coords.wrapping_next(at);
106
107 for axis in [Axis2D::X, Axis2D::Y] {
108 let (ldj, lcj) = (ld.get(axis), lc.get(axis));
109 let (dj, cj) = (d.get(axis), c.get(axis));
110 let (ndj, ncj) = (nd.get(axis), nc.get(axis));
111 let (c1, c2, d1, d2) = if lcj <= ncj {
112 (lcj, ncj, ldj, ndj)
113 } else {
114 (ncj, lcj, ndj, ldj)
115 };
116 match (c1, c2) {
120 _ if c1 == c2 => {
121 if (d1 - d2).abs() > tolerance && dj.abs() > tolerance {
122 return true;
123 }
124 }
125 _ if c1 <= cj && cj <= c2 => {
126 if !(d1.min(d2) - tolerance <= dj && dj <= d1.max(d2) + tolerance) {
132 return true;
133 }
134 }
135 _ => {
136 if d1 != d2 && dj.abs() > tolerance {
140 if cj < c1 {
141 if ((dj - d1).abs() > tolerance) && (dj - tolerance < d1) != (d1 < d2) {
142 return true;
143 }
144 } else if ((dj - d2).abs() > tolerance) && (d2 < dj + tolerance) != (d1 < d2) {
145 return true;
146 }
147 }
148 }
149 }
150 }
151 false
152}
153
154fn iup_must_encode(
161 deltas: &[Vec2],
162 coords: &[Point],
163 tolerance: f64,
164) -> Result<HashSet<usize>, IupError> {
165 Ok((0..deltas.len())
166 .rev()
167 .filter(|i| must_encode_at(deltas, coords, tolerance, *i))
168 .collect())
169}
170
171#[derive(Copy, Clone, Debug)]
173enum Axis2D {
174 X,
175 Y,
176}
177
178trait Coord {
182 fn get(&self, axis: Axis2D) -> f64;
183 fn set(&mut self, coord: Axis2D, value: f64);
184}
185
186impl Coord for Point {
187 fn get(&self, axis: Axis2D) -> f64 {
188 match axis {
189 Axis2D::X => self.x,
190 Axis2D::Y => self.y,
191 }
192 }
193
194 fn set(&mut self, axis: Axis2D, value: f64) {
195 match axis {
196 Axis2D::X => self.x = value,
197 Axis2D::Y => self.y = value,
198 }
199 }
200}
201
202impl Coord for Vec2 {
203 fn get(&self, axis: Axis2D) -> f64 {
204 match axis {
205 Axis2D::X => self.x,
206 Axis2D::Y => self.y,
207 }
208 }
209
210 fn set(&mut self, axis: Axis2D, value: f64) {
211 match axis {
212 Axis2D::X => self.x = value,
213 Axis2D::Y => self.y = value,
214 }
215 }
216}
217
218fn iup_segment(coords: &[Point], rc1: Point, rd1: Vec2, rc2: Point, rd2: Vec2) -> Vec<Vec2> {
224 let n = coords.len();
228 let mut result = vec![Vec2::default(); n];
229 for axis in [Axis2D::X, Axis2D::Y] {
230 let c1 = rc1.get(axis);
231 let c2 = rc2.get(axis);
232 let d1 = rd1.get(axis);
233 let d2 = rd2.get(axis);
234
235 if c1 == c2 {
236 let value = if d1 == d2 { d1 } else { 0.0 };
237 for r in result.iter_mut() {
238 r.set(axis, value);
239 }
240 continue;
241 }
242
243 let (c1, c2, d1, d2) = if c1 > c2 {
244 (c2, c1, d2, d1) } else {
246 (c1, c2, d1, d2) };
248
249 let scale = (d2 - d1) / (c2 - c1);
251 for (idx, point) in coords.iter().enumerate() {
252 let c = point.get(axis);
253 let d = if c <= c1 {
254 d1
255 } else if c >= c2 {
256 d2
257 } else {
258 d1 + (c - c1) * scale
260 };
261 result[idx].set(axis, d);
262 }
263 }
264 result
265}
266
267fn can_iup_in_between(
275 deltas: &[Vec2],
276 coords: &[Point],
277 tolerance: f64,
278 from: isize,
279 to: isize,
280) -> Result<bool, IupError> {
281 if from < -1 || to <= from || to - from < 2 {
282 return Err(IupError::AchievedInvalidState(format!(
283 "bad from/to: {from}..{to}"
284 )));
285 }
286 let to = to as usize;
290 let (rc1, rd1) = if from < 0 {
291 (*coords.last().unwrap(), *deltas.last().unwrap())
292 } else {
293 (coords[from as usize], deltas[from as usize])
294 };
295 let iup_values = iup_segment(
296 &coords[(from + 1) as usize..to],
297 rc1,
298 rd1,
299 coords[to],
300 deltas[to],
301 );
302
303 let real_values = &deltas[(from + 1) as usize..to];
304
305 let tolerance_sq = tolerance.powi(2);
307 Ok(real_values
308 .iter()
309 .zip(iup_values)
310 .all(|(d, i)| (*d - i).hypot2() <= tolerance_sq))
311}
312
313fn iup_initial_lookback(deltas: &[Vec2]) -> usize {
315 std::cmp::min(deltas.len(), 8usize) }
317
318#[derive(Debug, PartialEq)]
319struct OptimizeDpResult {
320 costs: Vec<i32>,
321 chain: Vec<Option<usize>>,
322}
323
324fn iup_contour_optimize_dp(
334 deltas: &[Vec2],
335 coords: &[Point],
336 tolerance: f64,
337 must_encode: &HashSet<usize>,
338 lookback: usize,
339) -> Result<OptimizeDpResult, IupError> {
340 let n = deltas.len();
341 let lookback = lookback as isize;
342 let mut costs = Vec::with_capacity(n);
343 let mut chain: Vec<_> = (0..n)
344 .map(|i| if i > 0 { Some(i - 1) } else { None })
345 .collect();
346
347 if n < 2 {
349 return Ok(OptimizeDpResult { costs, chain });
350 }
351
352 for i in 0..n {
353 let mut best_cost = if i > 0 { costs[i - 1] } else { 0 } + 1;
354
355 costs.push(best_cost);
356 if i > 0 && must_encode.contains(&(i - 1)) {
357 continue;
358 }
359
360 let j_min = std::cmp::max(i as isize - lookback, -2);
386 let j_max = i as isize - 2;
387 for j in (j_min + 1..j_max + 1).rev() {
388 let (cost, must_encode) = if j >= 0 {
389 (costs[j as usize] + 1, must_encode.contains(&(j as usize)))
390 } else {
391 (1, false)
392 };
393 if cost < best_cost && can_iup_in_between(deltas, coords, tolerance, j, i as isize)? {
394 best_cost = cost;
395 costs[i] = best_cost;
396 chain[i] = if j >= 0 { Some(j as usize) } else { None };
397 }
398 if must_encode {
399 break;
400 }
401 }
402 }
403
404 Ok(OptimizeDpResult { costs, chain })
405}
406
407fn iup_contour_optimize(
415 deltas: &mut [Vec2],
416 coords: &mut [Point],
417 tolerance: f64,
418) -> Result<Vec<GlyphDelta>, IupError> {
419 if deltas.len() != coords.len() {
420 return Err(IupError::DeltaCoordLengthMismatch {
421 num_deltas: deltas.len(),
422 num_coords: coords.len(),
423 });
424 }
425
426 let n = deltas.len();
427
428 let Some(first_delta) = deltas.first() else {
432 return Ok(Vec::new());
433 };
434 if deltas.iter().all(|d| d == first_delta) {
435 if *first_delta == Vec2::ZERO {
436 return Ok(vec![GlyphDelta::optional(0, 0); n]);
437 }
438
439 let (x, y) = first_delta.to_point().ot_round();
440 return Ok(std::iter::once(GlyphDelta::required(x, y))
443 .chain(std::iter::repeat(GlyphDelta::optional(x, y)))
444 .take(n)
445 .collect());
446 }
447
448 let must_encode = iup_must_encode(deltas, coords, tolerance)?;
450 let encode = if !must_encode.is_empty() {
459 let mid = n - 1 - must_encode.iter().max().unwrap();
463 deltas.rotate_right(mid);
464 coords.rotate_right(mid);
465 let must_encode: HashSet<usize> = must_encode.iter().map(|idx| (idx + mid) % n).collect();
466 let dp_result = iup_contour_optimize_dp(
467 deltas,
468 coords,
469 tolerance,
470 &must_encode,
471 iup_initial_lookback(deltas),
472 )?;
473
474 let mut encode = HashSet::new();
476
477 let mut i = n - 1;
478 loop {
479 encode.insert(i);
480 i = match dp_result.chain[i] {
481 Some(v) => v,
482 None => break,
483 };
484 }
485
486 if !encode.is_superset(&must_encode) {
487 return Err(IupError::AchievedInvalidState(format!(
488 "{encode:?} should contain {must_encode:?}"
489 )));
490 }
491
492 deltas.rotate_left(mid);
493 encode.iter().map(|idx| (idx + n - mid) % n).collect()
499 } else {
500 let mut deltas_twice = Vec::with_capacity(2 * n);
504 deltas_twice.extend_from_slice(deltas);
505 deltas_twice.extend_from_slice(deltas);
506 let mut coords_twice = Vec::with_capacity(2 * n);
507 coords_twice.extend_from_slice(coords);
508 coords_twice.extend_from_slice(coords);
509
510 let dp_result = iup_contour_optimize_dp(
511 &deltas_twice,
512 &coords_twice,
513 tolerance,
514 &must_encode,
515 iup_initial_lookback(deltas),
516 )?;
517
518 let mut best_sol = None;
519 let mut best_cost = (n + 1) as i32;
520
521 for start in n - 1..dp_result.costs.len() - 1 {
522 let mut solution = HashSet::new();
524 let mut i = Some(start);
525
526 while i > start.checked_sub(n) {
528 let idx = i.unwrap();
529 solution.insert(idx % n);
530 i = dp_result.chain[idx];
531 }
532
533 if i == start.checked_sub(n) {
535 let cost = dp_result.costs[start]
537 - if n < start {
538 dp_result.costs[start - n]
539 } else {
540 0
541 };
542 if cost <= best_cost {
543 best_sol = Some(solution);
544 best_cost = cost;
545 }
546 }
547 }
548
549 let encode = best_sol.ok_or(IupError::AchievedInvalidState(
550 "No best solution identified".to_string(),
551 ))?;
552
553 if !encode.is_superset(&must_encode) {
554 return Err(IupError::AchievedInvalidState(format!(
555 "{encode:?} should contain {must_encode:?}"
556 )));
557 }
558
559 encode
560 };
561
562 Ok(deltas
563 .iter()
564 .enumerate()
565 .map(|(i, delta)| {
566 let (x, y) = delta.to_point().ot_round();
567 if encode.contains(&i) {
568 GlyphDelta::required(x, y)
569 } else {
570 GlyphDelta::optional(x, y)
571 }
572 })
573 .collect())
574}
575
576#[cfg(test)]
577mod tests {
578 use super::*;
579 use pretty_assertions::assert_eq;
580
581 struct IupScenario {
582 deltas: Vec<Vec2>,
583 coords: Vec<Point>,
584 expected_must_encode: HashSet<usize>,
585 }
586
587 impl IupScenario {
588 fn assert_must_encode(&self) {
590 assert_eq!(
591 self.expected_must_encode,
592 iup_must_encode(&self.deltas, &self.coords, f64::EPSILON).unwrap()
593 );
594 }
595
596 fn assert_optimize_dp(&self) {
598 let must_encode = iup_must_encode(&self.deltas, &self.coords, f64::EPSILON).unwrap();
599 let lookback = iup_initial_lookback(&self.deltas);
600 let r1 = iup_contour_optimize_dp(
601 &self.deltas,
602 &self.coords,
603 f64::EPSILON,
604 &must_encode,
605 lookback,
606 )
607 .unwrap();
608 let must_encode = HashSet::new();
609 let r2 = iup_contour_optimize_dp(
610 &self.deltas,
611 &self.coords,
612 f64::EPSILON,
613 &must_encode,
614 lookback,
615 )
616 .unwrap();
617
618 assert_eq!(r1, r2);
619 }
620
621 fn assert_optimize_contour(&self) {
623 let mut deltas = self.deltas.clone();
624 let mut coords = self.coords.clone();
625 iup_contour_optimize(&mut deltas, &mut coords, f64::EPSILON).unwrap();
626 }
627 }
628
629 fn iup_scenario1() -> IupScenario {
631 IupScenario {
632 deltas: vec![(0.0, 0.0).into()],
633 coords: vec![(1.0, 2.0).into()],
634 expected_must_encode: HashSet::new(),
635 }
636 }
637
638 fn iup_scenario2() -> IupScenario {
640 IupScenario {
641 deltas: vec![(0.0, 0.0).into(), (0.0, 0.0).into(), (0.0, 0.0).into()],
642 coords: vec![(1.0, 2.0).into(), (3.0, 2.0).into(), (2.0, 3.0).into()],
643 expected_must_encode: HashSet::new(),
644 }
645 }
646
647 fn iup_scenario3() -> IupScenario {
649 IupScenario {
650 deltas: vec![
651 (1.0, 1.0).into(),
652 (-1.0, 1.0).into(),
653 (-1.0, -1.0).into(),
654 (1.0, -1.0).into(),
655 ],
656 coords: vec![
657 (0.0, 0.0).into(),
658 (2.0, 0.0).into(),
659 (2.0, 2.0).into(),
660 (0.0, 2.0).into(),
661 ],
662 expected_must_encode: HashSet::new(),
663 }
664 }
665
666 fn iup_scenario4() -> IupScenario {
668 IupScenario {
669 deltas: vec![
670 (-1.0, 0.0).into(),
671 (-1.0, 0.0).into(),
672 (-1.0, 0.0).into(),
673 (-1.0, 0.0).into(),
674 (-1.0, 0.0).into(),
675 (0.0, 0.0).into(),
676 (0.0, 0.0).into(),
677 (0.0, 0.0).into(),
678 (0.0, 0.0).into(),
679 (0.0, 0.0).into(),
680 (0.0, 0.0).into(),
681 (-1.0, 0.0).into(),
682 ],
683 coords: vec![
684 (-35.0, -152.0).into(),
685 (-86.0, -101.0).into(),
686 (-50.0, -65.0).into(),
687 (0.0, -116.0).into(),
688 (51.0, -65.0).into(),
689 (86.0, -99.0).into(),
690 (35.0, -151.0).into(),
691 (87.0, -202.0).into(),
692 (51.0, -238.0).into(),
693 (-1.0, -187.0).into(),
694 (-53.0, -239.0).into(),
695 (-88.0, -205.0).into(),
696 ],
697 expected_must_encode: HashSet::from([11]),
698 }
699 }
700
701 fn iup_scenario5() -> IupScenario {
703 IupScenario {
704 deltas: vec![
705 (0.0, 0.0).into(),
706 (1.0, 0.0).into(),
707 (2.0, 0.0).into(),
708 (2.0, 0.0).into(),
709 (0.0, 0.0).into(),
710 (1.0, 0.0).into(),
711 (3.0, 0.0).into(),
712 (3.0, 0.0).into(),
713 (2.0, 0.0).into(),
714 (2.0, 0.0).into(),
715 (0.0, 0.0).into(),
716 (0.0, 0.0).into(),
717 (-1.0, 0.0).into(),
718 (-1.0, 0.0).into(),
719 (-1.0, 0.0).into(),
720 (-3.0, 0.0).into(),
721 (-1.0, 0.0).into(),
722 (0.0, 0.0).into(),
723 (0.0, 0.0).into(),
724 (-2.0, 0.0).into(),
725 (-2.0, 0.0).into(),
726 (-1.0, 0.0).into(),
727 (-1.0, 0.0).into(),
728 (-1.0, 0.0).into(),
729 (-4.0, 0.0).into(),
730 ],
731 coords: vec![
732 (330.0, 65.0).into(),
733 (401.0, 65.0).into(),
734 (499.0, 117.0).into(),
735 (549.0, 225.0).into(),
736 (549.0, 308.0).into(),
737 (549.0, 422.0).into(),
738 (549.0, 500.0).into(),
739 (497.0, 600.0).into(),
740 (397.0, 648.0).into(),
741 (324.0, 648.0).into(),
742 (271.0, 648.0).into(),
743 (200.0, 620.0).into(),
744 (165.0, 570.0).into(),
745 (165.0, 536.0).into(),
746 (165.0, 473.0).into(),
747 (252.0, 407.0).into(),
748 (355.0, 407.0).into(),
749 (396.0, 407.0).into(),
750 (396.0, 333.0).into(),
751 (354.0, 333.0).into(),
752 (249.0, 333.0).into(),
753 (141.0, 268.0).into(),
754 (141.0, 203.0).into(),
755 (141.0, 131.0).into(),
756 (247.0, 65.0).into(),
757 ],
758 expected_must_encode: HashSet::from([5, 15, 24]),
759 }
760 }
761
762 fn iup_scenario6() -> IupScenario {
766 IupScenario {
767 deltas: vec![
768 (0.0, 0.0).into(),
769 (1.0, 1.0).into(),
770 (2.0, 2.0).into(),
771 (3.0, 3.0).into(),
772 (4.0, 4.0).into(),
773 (5.0, 5.0).into(),
774 (6.0, 6.0).into(),
775 (7.0, 7.0).into(),
776 ],
777 coords: vec![
778 (0.0, 0.0).into(),
779 (10.0, 10.0).into(),
780 (20.0, 20.0).into(),
781 (30.0, 30.0).into(),
782 (40.0, 40.0).into(),
783 (50.0, 50.0).into(),
784 (60.0, 60.0).into(),
785 (70.0, 70.0).into(),
786 ],
787 expected_must_encode: HashSet::from([]),
788 }
789 }
790
791 fn iup_scenario7() -> IupScenario {
794 IupScenario {
795 coords: vec![
796 (242.0, 111.0),
797 (314.0, 111.0),
798 (314.0, 317.0),
799 (513.0, 317.0),
800 (513.0, 388.0),
801 (314.0, 388.0),
802 (314.0, 595.0),
803 (242.0, 595.0),
804 (242.0, 388.0),
805 (43.0, 388.0),
806 (43.0, 317.0),
807 (242.0, 317.0),
808 (0.0, 0.0),
809 (557.0, 0.0),
810 (0.0, 0.0),
811 (0.0, 0.0),
812 ]
813 .into_iter()
814 .map(|c| c.into())
815 .collect(),
816 deltas: vec![
817 (-10.0, 0.0),
818 (25.0, 0.0),
819 (25.0, -18.0),
820 (15.0, -18.0),
821 (15.0, 18.0),
822 (25.0, 18.0),
823 (25.0, 1.0),
824 (-10.0, 1.0),
825 (-10.0, 18.0),
826 (0.0, 18.0),
827 (0.0, -18.0),
828 (-10.0, -18.0),
829 (0.0, 0.0),
830 (15.0, 0.0),
831 (0.0, 0.0),
832 (0.0, 0.0),
833 ]
834 .into_iter()
835 .map(|c| c.into())
836 .collect(),
837 expected_must_encode: HashSet::from([0]),
838 }
839 }
840
841 fn iup_scenario8() -> IupScenario {
843 IupScenario {
844 coords: vec![
845 (131.0, 430.0),
846 (131.0, 350.0),
847 (470.0, 350.0),
848 (470.0, 430.0),
849 (131.0, 330.0),
850 ]
851 .into_iter()
852 .map(|c| c.into())
853 .collect(),
854 deltas: vec![
855 (-15.0, 115.0),
856 (-15.0, 30.0),
857 (124.0, 30.0),
858 (124.0, 115.0),
859 (-39.0, 26.0),
860 ]
861 .into_iter()
862 .map(|c| c.into())
863 .collect(),
864 expected_must_encode: HashSet::from([0, 4]),
865 }
866 }
867
868 #[test]
869 fn iup_test_scenario01_must_encode() {
870 iup_scenario1().assert_must_encode();
871 }
872
873 #[test]
874 fn iup_test_scenario02_must_encode() {
875 iup_scenario2().assert_must_encode();
876 }
877
878 #[test]
879 fn iup_test_scenario03_must_encode() {
880 iup_scenario3().assert_must_encode();
881 }
882
883 #[test]
884 fn iup_test_scenario04_must_encode() {
885 iup_scenario4().assert_must_encode();
886 }
887
888 #[test]
889 fn iup_test_scenario05_must_encode() {
890 iup_scenario5().assert_must_encode();
891 }
892
893 #[test]
894 fn iup_test_scenario06_must_encode() {
895 iup_scenario6().assert_must_encode();
896 }
897
898 #[test]
899 fn iup_test_scenario07_must_encode() {
900 iup_scenario7().assert_must_encode();
901 }
902
903 #[test]
904 fn iup_test_scenario08_must_encode() {
905 iup_scenario8().assert_must_encode();
906 }
907
908 #[test]
909 fn iup_test_scenario01_optimize() {
910 iup_scenario1().assert_optimize_dp();
911 }
912
913 #[test]
914 fn iup_test_scenario02_optimize() {
915 iup_scenario2().assert_optimize_dp();
916 }
917
918 #[test]
919 fn iup_test_scenario03_optimize() {
920 iup_scenario3().assert_optimize_dp();
921 }
922
923 #[test]
924 fn iup_test_scenario04_optimize() {
925 iup_scenario4().assert_optimize_dp();
926 }
927
928 #[test]
929 fn iup_test_scenario05_optimize() {
930 iup_scenario5().assert_optimize_dp();
931 }
932
933 #[test]
934 fn iup_test_scenario06_optimize() {
935 iup_scenario6().assert_optimize_dp();
936 }
937
938 #[test]
939 fn iup_test_scenario07_optimize() {
940 iup_scenario7().assert_optimize_dp();
941 }
942
943 #[test]
944 fn iup_test_scenario08_optimize() {
945 iup_scenario8().assert_optimize_dp();
946 }
947
948 #[test]
949 fn iup_test_scenario01_optimize_contour() {
950 iup_scenario1().assert_optimize_contour();
951 }
952
953 #[test]
954 fn iup_test_scenario02_optimize_contour() {
955 iup_scenario2().assert_optimize_contour();
956 }
957
958 #[test]
959 fn iup_test_scenario03_optimize_contour() {
960 iup_scenario3().assert_optimize_contour();
961 }
962
963 #[test]
964 fn iup_test_scenario04_optimize_contour() {
965 iup_scenario4().assert_optimize_contour();
966 }
967
968 #[test]
969 fn iup_test_scenario05_optimize_contour() {
970 iup_scenario5().assert_optimize_contour();
971 }
972
973 #[test]
974 fn iup_test_scenario06_optimize_contour() {
975 iup_scenario6().assert_optimize_contour();
976 }
977
978 #[test]
979 fn iup_test_scenario07_optimize_contour() {
980 iup_scenario7().assert_optimize_contour();
981 }
982
983 #[test]
984 fn iup_test_scenario08_optimize_contour() {
985 iup_scenario8().assert_optimize_contour();
986 }
987
988 fn make_vec_of_options(deltas: &[GlyphDelta]) -> Vec<(usize, Option<Vec2>)> {
990 deltas
991 .iter()
992 .enumerate()
993 .map(|(i, delta)| {
994 (
995 i,
996 delta
997 .required
998 .then_some(Vec2::new(delta.x as _, delta.y as _)),
999 )
1000 })
1001 .collect()
1002 }
1003
1004 #[test]
1005 fn iup_delta_optimize_oswald_glyph_two() {
1006 let deltas: Vec<_> = vec![
1008 (0.0, 0.0),
1009 (41.0, 0.0),
1010 (41.0, 41.0),
1011 (60.0, 41.0),
1012 (22.0, -22.0),
1013 (27.0, -15.0),
1014 (38.0, -4.0),
1015 (44.0, 2.0),
1016 (44.0, -1.0),
1017 (44.0, 2.0),
1018 (29.0, 4.0),
1019 (18.0, 4.0),
1020 (9.0, 4.0),
1021 (-4.0, -4.0),
1022 (-11.0, -12.0),
1023 (-11.0, -10.0),
1024 (-11.0, -25.0),
1025 (44.0, -25.0),
1026 (44.0, -12.0),
1027 (44.0, -20.0),
1028 (39.0, -38.0),
1029 (26.0, -50.0),
1030 (16.0, -50.0),
1031 (-5.0, -50.0),
1032 (-13.0, -21.0),
1033 (-13.0, 1.0),
1034 (-13.0, 11.0),
1035 (-13.0, 16.0),
1036 (-13.0, 16.0),
1037 (-12.0, 19.0),
1038 (0.0, 42.0),
1039 (0.0, 0.0),
1040 (36.0, 0.0),
1041 (0.0, 0.0),
1042 (0.0, 0.0),
1043 ]
1044 .into_iter()
1045 .map(|c| c.into())
1046 .collect();
1047 let coords: Vec<_> = vec![
1048 (41.0, 0.0),
1049 (423.0, 0.0),
1050 (423.0, 90.0),
1051 (167.0, 90.0),
1052 (353.0, 374.0),
1053 (377.0, 410.0),
1054 (417.0, 478.0),
1055 (442.0, 556.0),
1056 (442.0, 608.0),
1057 (442.0, 706.0),
1058 (346.0, 817.0),
1059 (248.0, 817.0),
1060 (176.0, 817.0),
1061 (89.0, 759.0),
1062 (50.0, 654.0),
1063 (50.0, 581.0),
1064 (50.0, 553.0),
1065 (157.0, 553.0),
1066 (157.0, 580.0),
1067 (157.0, 619.0),
1068 (173.0, 687.0),
1069 (215.0, 729.0),
1070 (253.0, 729.0),
1071 (298.0, 729.0),
1072 (334.0, 665.0),
1073 (334.0, 609.0),
1074 (334.0, 564.0),
1075 (309.0, 495.0),
1076 (270.0, 433.0),
1077 (247.0, 397.0),
1078 (41.0, 76.0),
1079 (0.0, 0.0),
1080 (478.0, 0.0),
1081 (0.0, 0.0),
1082 (0.0, 0.0),
1083 ]
1084 .into_iter()
1085 .map(|c| c.into())
1086 .collect();
1087
1088 let tolerance = 0.5;
1090 let contour_ends = vec![coords.len() - 1 - 4];
1092
1093 let result = iup_delta_optimize(deltas.clone(), coords, tolerance, &contour_ends).unwrap();
1094 assert_eq!(result.len(), deltas.len());
1095 let result = make_vec_of_options(&result);
1096
1097 assert_eq!(
1098 result,
1099 vec![
1101 (0, None),
1102 (1, Some(Vec2 { x: 41.0, y: 0.0 })),
1103 (2, None),
1104 (3, Some(Vec2 { x: 60.0, y: 41.0 })),
1105 (4, Some(Vec2 { x: 22.0, y: -22.0 })),
1106 (5, Some(Vec2 { x: 27.0, y: -15.0 })),
1107 (6, Some(Vec2 { x: 38.0, y: -4.0 })),
1108 (7, Some(Vec2 { x: 44.0, y: 2.0 })),
1109 (8, Some(Vec2 { x: 44.0, y: -1.0 })),
1110 (9, Some(Vec2 { x: 44.0, y: 2.0 })),
1111 (10, Some(Vec2 { x: 29.0, y: 4.0 })),
1112 (11, Some(Vec2 { x: 18.0, y: 4.0 })),
1113 (12, Some(Vec2 { x: 9.0, y: 4.0 })),
1114 (13, Some(Vec2 { x: -4.0, y: -4.0 })),
1115 (14, Some(Vec2 { x: -11.0, y: -12.0 })),
1116 (15, Some(Vec2 { x: -11.0, y: -10.0 })),
1117 (16, None),
1118 (17, Some(Vec2 { x: 44.0, y: -25.0 })),
1119 (18, Some(Vec2 { x: 44.0, y: -12.0 })),
1120 (19, Some(Vec2 { x: 44.0, y: -20.0 })),
1121 (20, Some(Vec2 { x: 39.0, y: -38.0 })),
1122 (21, Some(Vec2 { x: 26.0, y: -50.0 })),
1123 (22, Some(Vec2 { x: 16.0, y: -50.0 })),
1124 (23, Some(Vec2 { x: -5.0, y: -50.0 })),
1125 (24, Some(Vec2 { x: -13.0, y: -21.0 })),
1126 (25, Some(Vec2 { x: -13.0, y: 1.0 })),
1127 (26, Some(Vec2 { x: -13.0, y: 11.0 })),
1128 (27, Some(Vec2 { x: -13.0, y: 16.0 })),
1129 (28, Some(Vec2 { x: -13.0, y: 16.0 })),
1130 (29, None),
1131 (30, Some(Vec2 { x: 0.0, y: 42.0 })),
1132 (31, None),
1133 (32, Some(Vec2 { x: 36.0, y: 0.0 })),
1134 (33, None),
1135 (34, None),
1136 ]
1137 )
1138 }
1139
1140 #[test]
1141 fn iup_delta_optimize_gs_glyph_uppercase_c() {
1142 let deltas: Vec<_> = vec![
1144 (2.0, 0.0),
1145 (4.0, 0.0),
1146 (8.0, -1.0),
1147 (10.0, -1.0),
1148 (10.0, 0.0),
1149 (-14.0, 25.0),
1150 (-8.0, 34.0),
1151 (-3.0, 38.0),
1152 (-5.0, 35.0),
1153 (-7.0, 35.0),
1154 (6.0, 35.0),
1155 (22.0, 27.0),
1156 (29.0, 11.0),
1157 (29.0, -1.0),
1158 (29.0, -13.0),
1159 (22.0, -29.0),
1160 (8.0, -37.0),
1161 (-3.0, -37.0),
1162 (0.0, -37.0),
1163 (1.0, -43.0),
1164 (-7.0, -41.0),
1165 (-19.0, -28.0),
1166 (8.0, 0.0),
1167 (8.0, 0.0),
1168 (6.0, 0.0),
1169 (4.0, 0.0),
1170 (2.0, 0.0),
1171 (0.0, 0.0),
1172 (-5.0, 0.0),
1173 (-8.0, 0.0),
1174 (-10.0, 0.0),
1175 (-10.0, 0.0),
1176 (-8.0, 0.0),
1177 (-5.0, 0.0),
1178 (-1.0, 0.0),
1179 (0.0, 0.0),
1180 (0.0, 0.0),
1181 (0.0, 0.0),
1182 (0.0, 0.0),
1183 ]
1184 .into_iter()
1185 .map(|c| c.into())
1186 .collect();
1187
1188 let coords: Vec<_> = vec![
1189 (416.0, -16.0),
1190 (476.0, -16.0),
1191 (581.0, 17.0),
1192 (668.0, 75.0),
1193 (699.0, 112.0),
1194 (637.0, 172.0),
1195 (609.0, 139.0),
1196 (542.0, 91.0),
1197 (463.0, 65.0),
1198 (416.0, 65.0),
1199 (339.0, 65.0),
1200 (209.0, 137.0),
1201 (131.0, 269.0),
1202 (131.0, 358.0),
1203 (131.0, 448.0),
1204 (209.0, 579.0),
1205 (339.0, 651.0),
1206 (416.0, 651.0),
1207 (458.0, 651.0),
1208 (529.0, 631.0),
1209 (590.0, 589.0),
1210 (617.0, 556.0),
1211 (678.0, 615.0),
1212 (646.0, 652.0),
1213 (566.0, 704.0),
1214 (471.0, 732.0),
1215 (416.0, 732.0),
1216 (337.0, 732.0),
1217 (202.0, 675.0),
1218 (101.0, 574.0),
1219 (45.0, 438.0),
1220 (45.0, 279.0),
1221 (101.0, 142.0),
1222 (202.0, 41.0),
1223 (337.0, -16.0),
1224 (0.0, 0.0),
1225 (741.0, 0.0),
1226 (0.0, 0.0),
1227 (0.0, 0.0),
1228 ]
1229 .into_iter()
1230 .map(|c| c.into())
1231 .collect();
1232
1233 let tolerance = 0.5;
1235 let contour_ends = vec![coords.len() - 1 - 4];
1237
1238 let result = iup_delta_optimize(deltas.clone(), coords, tolerance, &contour_ends).unwrap();
1239 let result = make_vec_of_options(&result);
1240
1241 assert_eq!(
1242 result,
1243 vec![
1244 (0, None),
1245 (1, Some(Vec2 { x: 4.0, y: 0.0 })),
1246 (2, Some(Vec2 { x: 8.0, y: -1.0 })),
1247 (3, Some(Vec2 { x: 10.0, y: -1.0 })),
1248 (4, Some(Vec2 { x: 10.0, y: 0.0 })),
1249 (5, Some(Vec2 { x: -14.0, y: 25.0 })),
1250 (6, Some(Vec2 { x: -8.0, y: 34.0 })),
1251 (7, Some(Vec2 { x: -3.0, y: 38.0 })),
1252 (8, Some(Vec2 { x: -5.0, y: 35.0 })),
1253 (9, Some(Vec2 { x: -7.0, y: 35.0 })),
1254 (10, Some(Vec2 { x: 6.0, y: 35.0 })),
1255 (11, Some(Vec2 { x: 22.0, y: 27.0 })),
1256 (12, Some(Vec2 { x: 29.0, y: 11.0 })),
1257 (13, None),
1258 (14, Some(Vec2 { x: 29.0, y: -13.0 })),
1259 (15, Some(Vec2 { x: 22.0, y: -29.0 })),
1260 (16, Some(Vec2 { x: 8.0, y: -37.0 })),
1261 (17, Some(Vec2 { x: -3.0, y: -37.0 })),
1262 (18, Some(Vec2 { x: 0.0, y: -37.0 })),
1263 (19, Some(Vec2 { x: 1.0, y: -43.0 })),
1264 (20, Some(Vec2 { x: -7.0, y: -41.0 })),
1265 (21, Some(Vec2 { x: -19.0, y: -28.0 })),
1266 (22, Some(Vec2 { x: 8.0, y: 0.0 })),
1267 (23, Some(Vec2 { x: 8.0, y: 0.0 })),
1268 (24, None),
1269 (25, Some(Vec2 { x: 4.0, y: 0.0 })),
1270 (26, None),
1271 (27, None),
1272 (28, None),
1273 (29, None),
1274 (30, None),
1275 (31, Some(Vec2 { x: -10.0, y: 0.0 })),
1276 (32, None),
1277 (33, None),
1278 (34, None),
1279 (35, None),
1280 (36, None),
1281 (37, None),
1282 (38, None),
1283 ]
1284 )
1285 }
1286
1287 #[test]
1291 fn bug_662() {
1292 let deltas = vec![
1293 Vec2 { x: 73.0, y: 0.0 },
1294 Vec2 { x: 158.0, y: 448.0 },
1295 Vec2 { x: 154.0, y: 448.0 },
1296 Vec2 { x: 0.0, y: 0.0 },
1297 Vec2 { x: 765.0, y: 0.0 },
1298 Vec2 { x: 0.0, y: 0.0 },
1299 Vec2 { x: 0.0, y: 0.0 },
1300 ];
1301
1302 let coords = [
1303 (73.0, 0.0),
1304 (158.0, 448.0),
1305 (154.0, 448.0),
1306 (0.0, 0.0),
1307 (765.0, 0.0),
1308 (0.0, 0.0),
1309 (0.0, 0.0),
1310 ]
1311 .into_iter()
1312 .map(|(x, y)| Point::new(x, y))
1313 .collect::<Vec<_>>();
1314
1315 let contour_ends = vec![2];
1316
1317 iup_delta_optimize(deltas, coords, 0.5, &contour_ends).unwrap();
1318 }
1319
1320 #[test]
1325 fn bug_fontc_1116() {
1326 let mut deltas = vec![
1327 Vec2 { x: 0.0, y: 0.0 },
1328 Vec2 { x: 0.0, y: 3.0 },
1329 Vec2 { x: 0.0, y: 0.0 },
1330 Vec2 { x: 0.0, y: 0.0 },
1331 Vec2 { x: 2.0, y: 3.0 },
1332 Vec2 { x: 2.0, y: 0.0 },
1333 Vec2 { x: 1.0, y: 3.0 },
1334 Vec2 { x: 1.0, y: 0.0 },
1335 Vec2 { x: 0.0, y: 0.0 },
1336 Vec2 { x: 0.0, y: 1.0 },
1337 Vec2 { x: 1.0, y: 0.0 },
1338 Vec2 { x: 1.0, y: 0.0 },
1339 ];
1340 let mut coords = [
1341 (0.0, 0.0),
1342 (0.0, 3.0),
1343 (0.0, 0.0),
1344 (0.0, 0.0),
1345 (2.0, 3.0),
1346 (2.0, 0.0),
1347 (1.0, 3.0),
1348 (1.0, 0.0),
1349 (0.0, 0.0),
1350 (0.0, 1.0),
1351 (1.0, 0.0),
1352 (1.0, 0.0),
1353 ]
1354 .into_iter()
1355 .map(|(x, y)| Point::new(x, y))
1356 .collect::<Vec<_>>();
1357
1358 iup_contour_optimize(&mut deltas, &mut coords, 0.5).unwrap();
1359 }
1360}