1use crate::{
4 from_obj::{FromObjRef, FromTableRef, ToOwnedTable},
5 util::{self, MultiZip, WrappingGet},
6 FontWrite, OtRound,
7};
8
9use kurbo::BezPath;
10use read_fonts::{tables::glyf::SimpleGlyphFlags, FontRead, ReadArgs};
11
12pub use read_fonts::tables::glyf::CurvePoint;
13
14use super::Bbox;
15
16#[derive(Clone, Debug, Default, PartialEq, Eq)]
18pub struct SimpleGlyph {
19 pub bbox: Bbox,
20 pub contours: Vec<Contour>,
21 pub instructions: Vec<u8>,
22 pub overlaps: bool,
26}
27
28#[derive(Clone, Debug, Default, PartialEq, Eq)]
30pub struct Contour(Vec<CurvePoint>);
31
32#[derive(Clone, Debug)]
34#[non_exhaustive]
35pub enum MalformedPath {
36 HasCubic,
37 TooSmall,
38 MissingMove,
39 UnequalNumberOfElements(Vec<usize>),
40 InconsistentPathElements(usize, Vec<&'static str>),
41}
42
43impl SimpleGlyph {
44 pub fn from_bezpath(path: &BezPath) -> Result<Self, MalformedPath> {
64 Self::interpolatable_glyphs_from_bezpaths(std::slice::from_ref(path))
65 .map(|mut x| x.pop().unwrap())
66 }
67
68 pub fn interpolatable_glyphs_from_bezpaths(
81 paths: &[BezPath],
82 ) -> Result<Vec<Self>, MalformedPath> {
83 simple_glyphs_from_kurbo(paths)
84 }
85
86 fn compute_point_deltas(
94 &self,
95 ) -> impl Iterator<Item = (SimpleGlyphFlags, CoordDelta, CoordDelta)> + '_ {
96 fn flag_and_delta(
98 value: i16,
99 short_flag: SimpleGlyphFlags,
100 same_or_pos: SimpleGlyphFlags,
101 ) -> (SimpleGlyphFlags, CoordDelta) {
102 const SHORT_MAX: i16 = u8::MAX as i16;
103 const SHORT_MIN: i16 = -SHORT_MAX;
104 match value {
105 0 => (same_or_pos, CoordDelta::Skip),
106 SHORT_MIN..=-1 => (short_flag, CoordDelta::Short(value.unsigned_abs() as u8)),
107 1..=SHORT_MAX => (short_flag | same_or_pos, CoordDelta::Short(value as _)),
108 _other => (SimpleGlyphFlags::empty(), CoordDelta::Long(value)),
109 }
110 }
111
112 let (mut last_x, mut last_y) = (0, 0);
113 let mut iter = self.contours.iter().flat_map(|c| c.iter());
114 std::iter::from_fn(move || {
115 let point = iter.next()?;
116 let mut flag = SimpleGlyphFlags::empty();
117 let d_x = point.x - last_x;
118 let d_y = point.y - last_y;
119 last_x = point.x;
120 last_y = point.y;
121
122 if point.on_curve {
123 flag |= SimpleGlyphFlags::ON_CURVE_POINT;
124 }
125 let (x_flag, x_data) = flag_and_delta(
126 d_x,
127 SimpleGlyphFlags::X_SHORT_VECTOR,
128 SimpleGlyphFlags::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
129 );
130 let (y_flag, y_data) = flag_and_delta(
131 d_y,
132 SimpleGlyphFlags::Y_SHORT_VECTOR,
133 SimpleGlyphFlags::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
134 );
135
136 flag |= x_flag | y_flag;
137 Some((flag, x_data, y_data))
138 })
139 }
140
141 pub fn recompute_bounding_box(&mut self) {
143 let mut points = self
144 .contours
145 .iter()
146 .flat_map(|c| c.iter())
147 .map(|p| (p.x, p.y));
148
149 if let Some((mut x_min, mut y_min)) = points.next() {
150 let mut x_max = x_min;
151 let mut y_max = y_min;
152 for (x, y) in points {
153 x_min = x_min.min(x);
154 y_min = y_min.min(y);
155 x_max = x_max.max(x);
156 y_max = y_max.max(y);
157 }
158 self.bbox = Bbox {
159 x_min,
160 y_min,
161 x_max,
162 y_max,
163 };
164 }
165 }
166}
167
168impl Contour {
169 pub fn len(&self) -> usize {
171 self.0.len()
172 }
173
174 pub fn is_empty(&self) -> bool {
176 self.0.is_empty()
177 }
178
179 pub fn iter(&self) -> impl Iterator<Item = &CurvePoint> {
180 self.0.iter()
181 }
182}
183
184impl From<Vec<CurvePoint>> for Contour {
185 fn from(points: Vec<CurvePoint>) -> Self {
186 Self(points)
187 }
188}
189
190impl From<Contour> for Vec<CurvePoint> {
191 fn from(contour: Contour) -> Self {
192 contour.0
193 }
194}
195
196impl MalformedPath {
197 fn inconsistent_path_els(idx: usize, elements: &[kurbo::PathEl]) -> Self {
198 fn el_types(elements: &[kurbo::PathEl]) -> Vec<&'static str> {
199 elements
200 .iter()
201 .map(|el| match el {
202 kurbo::PathEl::MoveTo(_) => "M",
203 kurbo::PathEl::LineTo(_) => "L",
204 kurbo::PathEl::QuadTo(_, _) => "Q",
205 kurbo::PathEl::CurveTo(_, _, _) => "C",
206 kurbo::PathEl::ClosePath => "Z",
207 })
208 .collect()
209 }
210
211 MalformedPath::InconsistentPathElements(idx, el_types(elements))
212 }
213}
214
215#[derive(Clone, Copy, Debug)]
217enum CoordDelta {
218 Skip,
220 Short(u8),
221 Long(i16),
222}
223
224impl FontWrite for CoordDelta {
225 fn write_into(&self, writer: &mut crate::TableWriter) {
226 match self {
227 CoordDelta::Skip => (),
228 CoordDelta::Short(val) => val.write_into(writer),
229 CoordDelta::Long(val) => val.write_into(writer),
230 }
231 }
232}
233
234impl FromObjRef<read_fonts::tables::glyf::SimpleGlyph<'_>> for SimpleGlyph {
235 fn from_obj_ref(
236 from: &read_fonts::tables::glyf::SimpleGlyph,
237 _data: read_fonts::FontData,
238 ) -> Self {
239 let bbox = Bbox {
240 x_min: from.x_min(),
241 y_min: from.y_min(),
242 x_max: from.x_max(),
243 y_max: from.y_max(),
244 };
245 let mut points = from.points();
246 let mut last_end = 0;
247 let mut contours = vec![];
248 for end_pt in from.end_pts_of_contours() {
249 let end = end_pt.get() as usize + 1;
250 let count = end - last_end;
251 last_end = end;
252 contours.push(Contour(points.by_ref().take(count).collect()));
253 }
254 Self {
255 bbox,
256 contours,
257 instructions: from.instructions().to_owned(),
258 overlaps: from.has_overlapping_contours(),
259 }
260 }
261}
262
263impl FromTableRef<read_fonts::tables::glyf::SimpleGlyph<'_>> for SimpleGlyph {}
264
265impl ReadArgs for SimpleGlyph {
266 type Args = ();
267}
268
269impl<'a> FontRead<'a> for SimpleGlyph {
270 fn read_with_args(
271 data: read_fonts::FontData<'a>,
272 _: (),
273 ) -> Result<Self, read_fonts::ReadError> {
274 read_fonts::tables::glyf::SimpleGlyph::read(data).map(|g| g.to_owned_table())
275 }
276}
277
278impl FontWrite for SimpleGlyph {
279 fn write_into(&self, writer: &mut crate::TableWriter) {
280 assert!(self.contours.len() < i16::MAX as usize);
281 assert!(self.instructions.len() < u16::MAX as usize);
282 let n_contours = self.contours.len() as i16;
283 if n_contours == 0 {
284 return;
286 }
287 n_contours.write_into(writer);
288 self.bbox.write_into(writer);
289 let mut cur = 0;
291 for contour in &self.contours {
292 cur += contour.len();
293 (cur as u16 - 1).write_into(writer);
294 }
295 (self.instructions.len() as u16).write_into(writer);
296 self.instructions.write_into(writer);
297
298 let mut deltas = self.compute_point_deltas().collect::<Vec<_>>();
299 if self.overlaps {
300 if let Some((flags, _, _)) = deltas.first_mut() {
301 flags.insert(SimpleGlyphFlags::OVERLAP_SIMPLE);
302 }
303 }
304 RepeatableFlag::iter_from_flags(deltas.iter().map(|(flag, _, _)| *flag))
305 .for_each(|flag| flag.write_into(writer));
306 deltas.iter().for_each(|(_, x, _)| x.write_into(writer));
307 deltas.iter().for_each(|(_, _, y)| y.write_into(writer));
308 writer.pad_to_2byte_aligned();
309 }
310}
311
312#[derive(Clone, Copy, Debug, PartialEq, Eq)]
314struct RepeatableFlag {
315 flag: SimpleGlyphFlags,
316 repeat: u8,
317}
318
319impl FontWrite for RepeatableFlag {
320 fn write_into(&self, writer: &mut crate::TableWriter) {
321 debug_assert_eq!(
322 self.flag.contains(SimpleGlyphFlags::REPEAT_FLAG),
323 self.repeat > 0
324 );
325
326 self.flag.bits().write_into(writer);
327 if self.flag.contains(SimpleGlyphFlags::REPEAT_FLAG) {
328 self.repeat.write_into(writer);
329 }
330 }
331}
332
333impl RepeatableFlag {
334 fn iter_from_flags(
338 flags: impl IntoIterator<Item = SimpleGlyphFlags>,
339 ) -> impl Iterator<Item = RepeatableFlag> {
340 let mut iter = flags.into_iter();
341 let mut prev = None;
342 let mut decompose_single_repeat = None;
350
351 std::iter::from_fn(move || loop {
352 if let Some(repeat) = decompose_single_repeat.take() {
353 return Some(repeat);
354 }
355
356 match (iter.next(), prev.take()) {
357 (None, Some(RepeatableFlag { flag, repeat: 1 })) => {
358 let flag = flag & !SimpleGlyphFlags::REPEAT_FLAG;
359 decompose_single_repeat = Some(RepeatableFlag { flag, repeat: 0 });
360 return decompose_single_repeat;
361 }
362 (None, prev) => return prev,
363 (Some(flag), None) => prev = Some(RepeatableFlag { flag, repeat: 0 }),
364 (Some(flag), Some(mut last)) => {
365 if (last.flag & !SimpleGlyphFlags::REPEAT_FLAG) == flag && last.repeat < u8::MAX
366 {
367 last.repeat += 1;
368 last.flag |= SimpleGlyphFlags::REPEAT_FLAG;
369 prev = Some(last);
370 } else {
371 if last.repeat == 1 {
373 last.flag &= !SimpleGlyphFlags::REPEAT_FLAG;
374 last.repeat = 0;
375 decompose_single_repeat = Some(last);
378 }
379 prev = Some(RepeatableFlag { flag, repeat: 0 });
380 return Some(last);
381 }
382 }
383 }
384 })
385 }
386}
387
388impl crate::validate::Validate for SimpleGlyph {
389 fn validate_impl(&self, ctx: &mut crate::codegen_prelude::ValidationCtx) {
390 if self.instructions.len() > u16::MAX as usize {
391 ctx.report("instructions len overflows");
392 }
393 }
394}
395
396#[derive(Clone, Copy, Debug, PartialEq)]
401struct ContourPoint {
402 point: kurbo::Point,
403 on_curve: bool,
404}
405
406impl ContourPoint {
407 fn new(point: kurbo::Point, on_curve: bool) -> Self {
408 Self { point, on_curve }
409 }
410
411 fn on_curve(point: kurbo::Point) -> Self {
412 Self::new(point, true)
413 }
414
415 fn off_curve(point: kurbo::Point) -> Self {
416 Self::new(point, false)
417 }
418}
419
420impl From<ContourPoint> for CurvePoint {
421 fn from(pt: ContourPoint) -> Self {
422 let (x, y) = pt.point.ot_round();
423 CurvePoint::new(x, y, pt.on_curve)
424 }
425}
426#[derive(Clone, Debug, PartialEq)]
430struct InterpolatableContourBuilder(Vec<Vec<ContourPoint>>);
431
432impl InterpolatableContourBuilder {
433 fn new(move_pts: &[kurbo::Point]) -> Self {
435 assert!(!move_pts.is_empty());
436 Self(
437 move_pts
438 .iter()
439 .map(|pt| vec![ContourPoint::on_curve(*pt)])
440 .collect(),
441 )
442 }
443
444 fn len(&self) -> usize {
446 self.0.len()
447 }
448
449 fn line_to(&mut self, pts: &[kurbo::Point]) {
451 assert_eq!(pts.len(), self.len());
452 for (i, pt) in pts.iter().enumerate() {
453 self.0[i].push(ContourPoint::on_curve(*pt));
454 }
455 }
456
457 fn quad_to(&mut self, pts: &[(kurbo::Point, kurbo::Point)]) {
459 for (i, (p0, p1)) in pts.iter().enumerate() {
460 self.0[i].push(ContourPoint::off_curve(*p0));
461 self.0[i].push(ContourPoint::on_curve(*p1));
462 }
463 }
464
465 fn num_points(&self) -> usize {
467 let n = self.0[0].len();
468 assert!(self.0.iter().all(|c| c.len() == n));
469 n
470 }
471
472 fn first(&self) -> impl Iterator<Item = &ContourPoint> {
474 self.0.iter().map(|v| v.first().unwrap())
475 }
476
477 fn last(&self) -> impl Iterator<Item = &ContourPoint> {
479 self.0.iter().map(|v| v.last().unwrap())
480 }
481
482 fn remove_last(&mut self) {
484 self.0.iter_mut().for_each(|c| {
485 c.pop().unwrap();
486 });
487 }
488
489 fn is_implicit_on_curve(&self, idx: usize) -> bool {
490 self.0
491 .iter()
492 .all(|points| is_implicit_on_curve(points, idx))
493 }
494
495 fn build(self) -> Vec<Contour> {
497 let num_contours = self.len();
498 let num_points = self.num_points();
499 let mut contours = vec![Contour::default(); num_contours];
500 contours.iter_mut().for_each(|c| c.0.reserve(num_points));
501 for point_idx in (0..num_points).filter(|point_idx| !self.is_implicit_on_curve(*point_idx))
502 {
503 for (contour_idx, contour) in contours.iter_mut().enumerate() {
504 contour
505 .0
506 .push(CurvePoint::from(self.0[contour_idx][point_idx]));
507 }
508 }
509 contours
510 }
511}
512
513#[inline]
518fn is_mid_point(p0: kurbo::Point, p1: kurbo::Point, p2: kurbo::Point) -> bool {
519 let mid = p0.midpoint(p2);
520 (util::isclose(mid.x, p1.x) && util::isclose(mid.y, p1.y))
521 || p0.to_vec2().ot_round() + p2.to_vec2().ot_round() == p1.to_vec2().ot_round() * 2.0
522}
523
524fn is_implicit_on_curve(points: &[ContourPoint], idx: usize) -> bool {
525 let p1 = &points[idx]; if !p1.on_curve {
527 return false;
528 }
529 let p0 = points.wrapping_prev(idx);
530 let p2 = points.wrapping_next(idx);
531 if p0.on_curve || p0.on_curve != p2.on_curve {
532 return false;
533 }
534 is_mid_point(p0.point, p1.point, p2.point)
536}
537
538fn simple_glyphs_from_kurbo(paths: &[BezPath]) -> Result<Vec<SimpleGlyph>, MalformedPath> {
540 let num_elements: Vec<usize> = paths.iter().map(|path| path.elements().len()).collect();
542 if num_elements.iter().any(|n| *n != num_elements[0]) {
543 return Err(MalformedPath::UnequalNumberOfElements(num_elements));
544 }
545 let path_iters = MultiZip::new(paths.iter().map(|path| path.iter()).collect());
546 let mut contours: Vec<InterpolatableContourBuilder> = Vec::new();
547 let mut current: Option<InterpolatableContourBuilder> = None;
548 let num_glyphs = paths.len();
549 let mut pts = Vec::with_capacity(num_glyphs);
550 let mut quad_pts = Vec::with_capacity(num_glyphs);
551 for (i, elements) in path_iters.enumerate() {
552 let first_el = elements.first().unwrap();
555 match first_el {
556 kurbo::PathEl::MoveTo(_) => {
557 if let Some(prev) = current.take() {
559 contours.push(prev);
560 }
561 pts.clear();
562 for el in &elements {
563 match el {
564 &kurbo::PathEl::MoveTo(pt) => {
565 pts.push(pt);
566 }
567 _ => return Err(MalformedPath::inconsistent_path_els(i, &elements)),
568 }
569 }
570 current = Some(InterpolatableContourBuilder::new(&pts));
571 }
572 kurbo::PathEl::LineTo(_) => {
573 pts.clear();
574 for el in &elements {
575 match el {
576 &kurbo::PathEl::LineTo(pt) => {
577 pts.push(pt);
578 }
579 _ => return Err(MalformedPath::inconsistent_path_els(i, &elements)),
580 }
581 }
582 current
583 .as_mut()
584 .ok_or(MalformedPath::MissingMove)?
585 .line_to(&pts)
586 }
587 kurbo::PathEl::QuadTo(_, _) => {
588 quad_pts.clear();
589 for el in &elements {
590 match el {
591 &kurbo::PathEl::QuadTo(p0, p1) => {
592 quad_pts.push((p0, p1));
593 }
594 _ => return Err(MalformedPath::inconsistent_path_els(i, &elements)),
595 }
596 }
597 current
598 .as_mut()
599 .ok_or(MalformedPath::MissingMove)?
600 .quad_to(&quad_pts)
601 }
602 kurbo::PathEl::CurveTo(_, _, _) => return Err(MalformedPath::HasCubic),
603 kurbo::PathEl::ClosePath => {
604 let contour = current.as_mut().ok_or(MalformedPath::MissingMove)?;
605 if contour.num_points() > 1 && contour.last().eq(contour.first()) {
609 contour.remove_last();
610 }
611 }
612 }
613 }
614 contours.extend(current);
615
616 let mut glyph_contours = vec![Vec::new(); num_glyphs];
617 for builder in contours {
618 assert_eq!(builder.len(), num_glyphs);
619 for (i, contour) in builder.build().into_iter().enumerate() {
620 glyph_contours[i].push(contour);
621 }
622 }
623
624 let mut glyphs = Vec::new();
625 for (contours, path) in glyph_contours.into_iter().zip(paths.iter()) {
626 glyphs.push(SimpleGlyph {
629 bbox: path.control_box().into(),
630 contours,
631 instructions: Default::default(),
632 overlaps: false,
633 })
634 }
635
636 Ok(glyphs)
637}
638
639#[cfg(test)]
640mod tests {
641 use font_types::GlyphId;
642 use kurbo::Affine;
643 use read_fonts::{tables::glyf as read_glyf, FontRef, TableProvider};
644
645 use super::*;
646
647 fn pad_for_loca_format(loca: &read_fonts::tables::loca::Loca, mut bytes: Vec<u8>) -> Vec<u8> {
650 if matches!(loca, read_fonts::tables::loca::Loca::Short(_)) && bytes.len() & 1 != 0 {
651 bytes.push(0);
652 }
653 bytes
654 }
655
656 #[test]
657 fn bad_path_input() {
658 let mut path = BezPath::new();
659 path.move_to((0., 0.));
660 path.curve_to((10., 10.), (20., 20.), (30., 30.));
661 path.line_to((50., 50.));
662 path.line_to((10., 10.));
663 let err = SimpleGlyph::from_bezpath(&path).unwrap_err();
664 assert!(matches!(err, MalformedPath::HasCubic));
665 }
666
667 #[test]
668 fn read_write_simple() {
669 let font = FontRef::new(font_test_data::SIMPLE_GLYF).unwrap();
670 let loca = font.loca(None).unwrap();
671 let glyf = font.glyf().unwrap();
672 let read_glyf::Glyph::Simple(orig) = loca
673 .get(GlyphId::new(0), &glyf)
674 .and_then(|g| g.into_glyph())
675 .unwrap()
676 else {
677 panic!("not a simple glyph")
678 };
679 let orig_bytes = orig.offset_data();
680
681 let ours = SimpleGlyph::from_table_ref(&orig);
682 let bytes = pad_for_loca_format(&loca, crate::dump_table(&ours).unwrap());
683 let ours = read_glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
684
685 let our_points = ours.points().collect::<Vec<_>>();
686 let their_points = orig.points().collect::<Vec<_>>();
687 assert_eq!(our_points, their_points);
688 assert_eq!(orig_bytes.as_ref(), bytes);
689 assert_eq!(orig.glyph_data(), ours.glyph_data());
690 assert_eq!(orig_bytes.len(), bytes.len());
691 }
692
693 #[test]
694 fn round_trip_simple() {
695 let font = FontRef::new(font_test_data::SIMPLE_GLYF).unwrap();
696 let loca = font.loca(None).unwrap();
697 let glyf = font.glyf().unwrap();
698 let read_glyf::Glyph::Simple(orig) = loca
699 .get(GlyphId::new(2), &glyf)
700 .and_then(|g| g.into_glyph())
701 .unwrap()
702 else {
703 panic!("not a simple glyph")
704 };
705 let orig_bytes = orig.offset_data();
706
707 let bezpath = BezPath::from_svg("M278,710 L278,470 L998,470 L998,710 Z").unwrap();
708
709 let ours = SimpleGlyph::from_bezpath(&bezpath).unwrap();
710 let bytes = pad_for_loca_format(&loca, crate::dump_table(&ours).unwrap());
711 let ours = read_glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
712
713 let our_points = ours.points().collect::<Vec<_>>();
714 let their_points = orig.points().collect::<Vec<_>>();
715 assert_eq!(our_points, their_points);
716 assert_eq!(orig_bytes.as_ref(), bytes);
717 assert_eq!(orig.glyph_data(), ours.glyph_data());
718 assert_eq!(orig_bytes.len(), bytes.len());
719 }
720
721 #[test]
722 fn round_trip_multi_contour() {
723 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
724 let loca = font.loca(None).unwrap();
725 let glyf = font.glyf().unwrap();
726 let read_glyf::Glyph::Simple(orig) = loca
727 .get(GlyphId::new(1), &glyf)
728 .and_then(|g| g.into_glyph())
729 .unwrap()
730 else {
731 panic!("not a simple glyph")
732 };
733 let orig_bytes = orig.offset_data();
734
735 let bezpath = BezPath::from_svg("M708,1327 L226,0 L29,0 L584,1456 L711,1456 Z M1112,0 L629,1327 L626,1456 L753,1456 L1310,0 Z M1087,539 L1087,381 L269,381 L269,539 Z").unwrap();
736
737 let ours = SimpleGlyph::from_bezpath(&bezpath).unwrap();
738 let bytes = pad_for_loca_format(&loca, crate::dump_table(&ours).unwrap());
739 let ours = read_glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
740
741 let our_points = ours.points().collect::<Vec<_>>();
742 let their_points = orig.points().collect::<Vec<_>>();
743 dbg!(
744 SimpleGlyphFlags::from_bits(1),
745 SimpleGlyphFlags::from_bits(9)
746 );
747 assert_eq!(our_points, their_points);
748 assert_eq!(orig.glyph_data(), ours.glyph_data());
749 assert_eq!(orig_bytes.len(), bytes.len());
750 assert_eq!(orig_bytes.as_ref(), bytes);
751 }
752
753 #[test]
754 fn simple_glyph_open_path() {
755 let mut path = BezPath::new();
756 path.move_to((20., -100.));
757 path.quad_to((1337., 1338.), (-50., -69.0));
758 path.quad_to((13., 255.), (-255., 256.));
759 path.line_to((20., -100.));
762
763 let glyph = SimpleGlyph::from_bezpath(&path).unwrap();
764 let bytes = crate::dump_table(&glyph).unwrap();
765 let read = read_fonts::tables::glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
766 assert_eq!(read.number_of_contours(), 1);
767 assert_eq!(read.num_points(), 6);
768 assert_eq!(read.end_pts_of_contours(), &[5]);
769 let points = read.points().collect::<Vec<_>>();
770 assert_eq!(points[0].x, 20);
771 assert_eq!(points[0].y, -100);
772 assert!(points[0].on_curve);
773 assert_eq!(points[1].x, 1337);
774 assert_eq!(points[1].y, 1338);
775 assert!(!points[1].on_curve);
776 assert_eq!(points[4].x, -255);
777 assert_eq!(points[4].y, 256);
778 assert!(points[4].on_curve);
779 assert_eq!(points[5].x, 20);
780 assert_eq!(points[5].y, -100);
781 assert!(points[5].on_curve);
782 }
783
784 #[test]
785 fn simple_glyph_closed_path_implicit_vs_explicit_closing_line() {
786 let mut path1 = BezPath::new();
787 path1.move_to((20., -100.));
788 path1.quad_to((1337., 1338.), (-50., -69.0));
789 path1.quad_to((13., 255.), (-255., 256.));
790 path1.close_path();
791
792 let mut path2 = BezPath::new();
793 path2.move_to((20., -100.));
794 path2.quad_to((1337., 1338.), (-50., -69.0));
795 path2.quad_to((13., 255.), (-255., 256.));
796 path2.line_to((20., -100.));
799 path2.close_path();
800
801 for path in &[path1, path2] {
802 let glyph = SimpleGlyph::from_bezpath(path).unwrap();
803 let bytes = crate::dump_table(&glyph).unwrap();
804 let read =
805 read_fonts::tables::glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
806 assert_eq!(read.number_of_contours(), 1);
807 assert_eq!(read.num_points(), 5);
808 assert_eq!(read.end_pts_of_contours(), &[4]);
809 let points = read.points().collect::<Vec<_>>();
810 assert_eq!(points[0].x, 20);
811 assert_eq!(points[0].y, -100);
812 assert!(points[0].on_curve);
813 assert_eq!(points[1].x, 1337);
814 assert_eq!(points[1].y, 1338);
815 assert!(!points[1].on_curve);
816 assert_eq!(points[4].x, -255);
817 assert_eq!(points[4].y, 256);
818 assert!(points[4].on_curve);
819 }
820 }
821
822 #[test]
823 fn keep_single_point_contours() {
824 let mut path = BezPath::new();
826 path.move_to((0.0, 0.0));
827 path.move_to((1.0, 2.0));
829 path.close_path();
830
831 let glyph = SimpleGlyph::from_bezpath(&path).unwrap();
832 let bytes = crate::dump_table(&glyph).unwrap();
833 let read = read_fonts::tables::glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
834 assert_eq!(read.number_of_contours(), 2);
835 assert_eq!(read.num_points(), 2);
836 assert_eq!(read.end_pts_of_contours(), &[0, 1]);
837 let points = read.points().collect::<Vec<_>>();
838 assert_eq!(points[0].x, 0);
839 assert_eq!(points[0].y, 0);
840 assert!(points[0].on_curve);
841 assert_eq!(points[1].x, 1);
842 assert_eq!(points[1].y, 2);
843 assert!(points[0].on_curve);
844 }
845
846 #[test]
847 fn compile_repeatable_flags() {
848 let mut path = BezPath::new();
849 path.move_to((20., -100.));
850 path.line_to((25., -90.));
851 path.line_to((50., -69.));
852 path.line_to((80., -20.));
853
854 let glyph = SimpleGlyph::from_bezpath(&path).unwrap();
855 let flags = glyph
856 .compute_point_deltas()
857 .map(|x| x.0)
858 .collect::<Vec<_>>();
859 let r_flags = RepeatableFlag::iter_from_flags(flags.iter().copied()).collect::<Vec<_>>();
860
861 assert_eq!(r_flags.len(), 2, "{r_flags:?}");
862 let bytes = crate::dump_table(&glyph).unwrap();
863 let read = read_fonts::tables::glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
864 assert_eq!(read.number_of_contours(), 1);
865 assert_eq!(read.num_points(), 4);
866 assert_eq!(read.end_pts_of_contours(), &[3]);
867 let points = read.points().collect::<Vec<_>>();
868 assert_eq!(points[0].x, 20);
869 assert_eq!(points[0].y, -100);
870 assert_eq!(points[1].x, 25);
871 assert_eq!(points[1].y, -90);
872 assert_eq!(points[2].x, 50);
873 assert_eq!(points[2].y, -69);
874 assert_eq!(points[3].x, 80);
875 assert_eq!(points[3].y, -20);
876 }
877
878 #[test]
879 fn simple_glyphs_from_kurbo_unequal_number_of_elements() {
880 let mut path1 = BezPath::new();
881 path1.move_to((0., 0.));
882 path1.line_to((1., 1.));
883 path1.line_to((2., 2.));
884 path1.line_to((0., 0.));
885 path1.close_path();
886 assert_eq!(path1.elements().len(), 5);
887
888 let mut path2 = BezPath::new();
889 path2.move_to((3., 3.));
890 path2.line_to((4., 4.));
891 path2.line_to((5., 5.));
892 path2.line_to((6., 6.));
893 path2.line_to((3., 3.));
894 path2.close_path();
895 assert_eq!(path2.elements().len(), 6);
896
897 let err = simple_glyphs_from_kurbo(&[path1, path2]).unwrap_err();
898 assert!(matches!(err, MalformedPath::UnequalNumberOfElements(_)));
899 assert_eq!(format!("{:?}", err), "UnequalNumberOfElements([5, 6])");
900 }
901
902 #[test]
903 fn simple_glyphs_from_kurbo_inconsistent_path_elements() {
904 let mut path1 = BezPath::new();
905 path1.move_to((0., 0.));
906 path1.line_to((1., 1.));
907 path1.quad_to((2., 2.), (0., 0.));
908 path1.close_path();
909 let mut path2 = BezPath::new();
910 path2.move_to((3., 3.));
911 path2.quad_to((4., 4.), (5., 5.)); path2.line_to((3., 3.));
913 path2.close_path();
914
915 let err = simple_glyphs_from_kurbo(&[path1, path2]).unwrap_err();
916 assert!(matches!(err, MalformedPath::InconsistentPathElements(1, _)));
917 assert_eq!(
918 format!("{:?}", err),
919 "InconsistentPathElements(1, [\"L\", \"Q\"])"
920 );
921 }
922
923 fn make_interpolatable_paths(
944 num_paths: usize,
945 el_types: &str,
946 last_pt_equal_move: bool,
947 ) -> Vec<BezPath> {
948 let mut paths = Vec::new();
949 let mut start = 0.0;
952 let mut points = std::iter::from_fn(move || {
953 let value = start;
954 start += 1.0;
955 Some((value, value))
956 });
957 let el_types = el_types.chars().collect::<Vec<_>>();
958 assert!(!el_types.is_empty());
959 for _ in 0..num_paths {
960 let mut path = BezPath::new();
961 let mut start_pt = None;
962 let mut el_types_iter = el_types.iter().peekable();
964 while let Some(&el_type) = el_types_iter.next() {
965 let next_el_type = el_types_iter.peek().map(|x| **x).unwrap_or('M');
966 match el_type {
967 'M' => {
968 start_pt = points.next();
969 path.move_to(start_pt.unwrap());
970 }
971 'L' => {
972 if matches!(next_el_type, 'Z' | 'M') && last_pt_equal_move {
973 path.line_to(start_pt.unwrap());
974 } else {
975 path.line_to(points.next().unwrap());
976 }
977 }
978 'Q' => {
979 let p1 = points.next().unwrap();
980 let p2 = if matches!(next_el_type, 'Z' | 'M') && last_pt_equal_move {
981 start_pt.unwrap()
982 } else {
983 points.next().unwrap()
984 };
985 path.quad_to(p1, p2);
986 }
987 'Z' => {
988 path.close_path();
989 start_pt = None;
990 }
991 _ => panic!("Unsupported element type {:?}", el_type),
992 }
993 }
994 paths.push(path);
995 }
996 assert_eq!(paths.len(), num_paths);
997 paths
998 }
999
1000 fn assert_contour_points(glyph: &SimpleGlyph, all_points: Vec<Vec<CurvePoint>>) {
1001 let expected_num_contours = all_points.len();
1002 assert_eq!(glyph.contours.len(), expected_num_contours);
1003 for (contour, expected_points) in glyph.contours.iter().zip(all_points.iter()) {
1004 let points = contour.iter().copied().collect::<Vec<_>>();
1005 assert_eq!(points, *expected_points);
1006 }
1007 }
1008
1009 #[test]
1010 fn simple_glyphs_from_kurbo_3_lines_closed() {
1011 let paths = make_interpolatable_paths(2, "MLLLZ", true);
1013 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1014
1015 assert_contour_points(
1016 &glyphs[0],
1017 vec![vec![
1018 CurvePoint::on_curve(0, 0),
1019 CurvePoint::on_curve(1, 1),
1020 CurvePoint::on_curve(2, 2),
1021 ]],
1022 );
1023 assert_contour_points(
1024 &glyphs[1],
1025 vec![vec![
1026 CurvePoint::on_curve(3, 3),
1027 CurvePoint::on_curve(4, 4),
1028 CurvePoint::on_curve(5, 5),
1029 ]],
1030 );
1031 }
1032
1033 #[test]
1034 fn simple_glyphs_from_kurbo_3_lines_implicitly_closed() {
1035 let paths = make_interpolatable_paths(2, "MLLZ", false);
1037 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1038
1039 assert_contour_points(
1040 &glyphs[0],
1041 vec![vec![
1042 CurvePoint::on_curve(0, 0),
1043 CurvePoint::on_curve(1, 1),
1044 CurvePoint::on_curve(2, 2),
1045 ]],
1046 );
1047 assert_contour_points(
1048 &glyphs[1],
1049 vec![vec![
1050 CurvePoint::on_curve(3, 3),
1051 CurvePoint::on_curve(4, 4),
1052 CurvePoint::on_curve(5, 5),
1053 ]],
1054 );
1055 }
1056
1057 #[test]
1058 fn simple_glyphs_from_kurbo_2_quads_closed() {
1059 let paths = make_interpolatable_paths(2, "MQQZ", true);
1064 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1065
1066 assert_contour_points(
1067 &glyphs[0],
1068 vec![vec![
1069 CurvePoint::on_curve(0, 0),
1070 CurvePoint::off_curve(1, 1),
1071 CurvePoint::off_curve(3, 3),
1073 ]],
1074 );
1075 assert_contour_points(
1076 &glyphs[1],
1077 vec![vec![
1078 CurvePoint::on_curve(4, 4),
1079 CurvePoint::off_curve(5, 5),
1080 CurvePoint::off_curve(7, 7),
1082 ]],
1083 );
1084 }
1085
1086 #[test]
1087 fn simple_glyphs_from_kurbo_2_quads_1_line_implicitly_closed() {
1088 let paths = make_interpolatable_paths(2, "MQQZ", false);
1092 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1093
1094 assert_contour_points(
1095 &glyphs[0],
1096 vec![vec![
1097 CurvePoint::on_curve(0, 0),
1098 CurvePoint::off_curve(1, 1),
1099 CurvePoint::off_curve(3, 3),
1101 CurvePoint::on_curve(4, 4),
1102 ]],
1103 );
1104 assert_contour_points(
1105 &glyphs[1],
1106 vec![vec![
1107 CurvePoint::on_curve(5, 5),
1108 CurvePoint::off_curve(6, 6),
1109 CurvePoint::off_curve(8, 8),
1111 CurvePoint::on_curve(9, 9),
1112 ]],
1113 );
1114 }
1115
1116 #[test]
1117 fn simple_glyphs_from_kurbo_multiple_contours_mixed_segments() {
1118 let paths = make_interpolatable_paths(4, "MLQQZMQLQLZ", true);
1120 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1121
1122 assert_contour_points(
1123 &glyphs[0],
1124 vec![
1125 vec![
1126 CurvePoint::on_curve(0, 0),
1127 CurvePoint::on_curve(1, 1),
1128 CurvePoint::off_curve(2, 2),
1129 CurvePoint::off_curve(4, 4),
1131 ],
1132 vec![
1133 CurvePoint::on_curve(5, 5),
1134 CurvePoint::off_curve(6, 6),
1135 CurvePoint::on_curve(7, 7),
1136 CurvePoint::on_curve(8, 8),
1137 CurvePoint::off_curve(9, 9),
1138 CurvePoint::on_curve(10, 10),
1139 ],
1140 ],
1141 );
1142 }
1143
1144 #[test]
1145 fn simple_glyphs_from_kurbo_all_quad_off_curves() {
1146 let mut path1 = BezPath::new();
1149 path1.move_to((0.0, 1.0));
1150 path1.quad_to((1.0, 1.0), (1.0, 0.0));
1151 path1.quad_to((1.0, -1.0), (0.0, -1.0));
1152 path1.quad_to((-1.0, -1.0), (-1.0, 0.0));
1153 path1.quad_to((-1.0, 1.0), (0.0, 1.0));
1154 path1.close_path();
1155
1156 let mut path2 = path1.clone();
1157 path2.apply_affine(Affine::scale(2.0));
1158
1159 let glyphs = simple_glyphs_from_kurbo(&[path1, path2]).unwrap();
1160
1161 assert_contour_points(
1162 &glyphs[0],
1163 vec![vec![
1164 CurvePoint::off_curve(1, 1),
1165 CurvePoint::off_curve(1, -1),
1166 CurvePoint::off_curve(-1, -1),
1167 CurvePoint::off_curve(-1, 1),
1168 ]],
1169 );
1170 assert_contour_points(
1171 &glyphs[1],
1172 vec![vec![
1173 CurvePoint::off_curve(2, 2),
1174 CurvePoint::off_curve(2, -2),
1175 CurvePoint::off_curve(-2, -2),
1176 CurvePoint::off_curve(-2, 2),
1177 ]],
1178 );
1179 }
1180
1181 #[test]
1182 fn simple_glyphs_from_kurbo_keep_on_curve_unless_impliable_for_all() {
1183 let mut path1 = BezPath::new();
1184 path1.move_to((0.0, 0.0));
1185 path1.quad_to((0.0, 1.0), (1.0, 1.0)); path1.quad_to((2.0, 1.0), (2.0, 0.0));
1187 path1.line_to((0.0, 0.0));
1188 path1.close_path();
1189
1190 assert_contour_points(
1193 &SimpleGlyph::from_bezpath(&path1).unwrap(),
1194 vec![vec![
1195 CurvePoint::on_curve(0, 0),
1196 CurvePoint::off_curve(0, 1),
1197 CurvePoint::off_curve(2, 1),
1199 CurvePoint::on_curve(2, 0),
1200 ]],
1201 );
1202
1203 let mut path2 = BezPath::new();
1204 path2.move_to((0.0, 0.0));
1205 path2.quad_to((0.0, 2.0), (2.0, 2.0)); path2.quad_to((3.0, 2.0), (3.0, 0.0));
1207 path2.line_to((0.0, 0.0));
1208 path2.close_path();
1209
1210 let glyphs = simple_glyphs_from_kurbo(&[path1, path2]).unwrap();
1211
1212 assert_contour_points(
1215 &glyphs[0],
1216 vec![vec![
1217 CurvePoint::on_curve(0, 0),
1218 CurvePoint::off_curve(0, 1),
1219 CurvePoint::on_curve(1, 1), CurvePoint::off_curve(2, 1),
1221 CurvePoint::on_curve(2, 0),
1222 ]],
1223 );
1224 assert_contour_points(
1225 &glyphs[1],
1226 vec![vec![
1227 CurvePoint::on_curve(0, 0),
1228 CurvePoint::off_curve(0, 2),
1229 CurvePoint::on_curve(2, 2), CurvePoint::off_curve(3, 2),
1231 CurvePoint::on_curve(3, 0),
1232 ]],
1233 );
1234 }
1235
1236 #[test]
1237 fn simple_glyphs_from_kurbo_2_lines_open() {
1238 let paths = make_interpolatable_paths(2, "MLL", false);
1241 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1242
1243 assert_contour_points(
1244 &glyphs[0],
1245 vec![vec![
1246 CurvePoint::on_curve(0, 0),
1247 CurvePoint::on_curve(1, 1),
1248 CurvePoint::on_curve(2, 2),
1249 ]],
1250 );
1251 assert_contour_points(
1252 &glyphs[1],
1253 vec![vec![
1254 CurvePoint::on_curve(3, 3),
1255 CurvePoint::on_curve(4, 4),
1256 CurvePoint::on_curve(5, 5),
1257 ]],
1258 );
1259 }
1260
1261 #[test]
1262 fn simple_glyphs_from_kurbo_3_lines_open_duplicate_last_pt() {
1263 let paths = make_interpolatable_paths(2, "MLLL", true);
1268 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1269
1270 assert_contour_points(
1271 &glyphs[0],
1272 vec![vec![
1273 CurvePoint::on_curve(0, 0),
1274 CurvePoint::on_curve(1, 1),
1275 CurvePoint::on_curve(2, 2),
1276 CurvePoint::on_curve(0, 0),
1277 ]],
1278 );
1279 assert_contour_points(
1280 &glyphs[1],
1281 vec![vec![
1282 CurvePoint::on_curve(3, 3),
1283 CurvePoint::on_curve(4, 4),
1284 CurvePoint::on_curve(5, 5),
1285 CurvePoint::on_curve(3, 3),
1286 ]],
1287 );
1288 }
1289
1290 #[test]
1291 fn simple_glyphs_from_kurbo_4_lines_closed_duplicate_last_pt() {
1292 for implicit_closing_line in &[true, false] {
1293 let mut path1 = BezPath::new();
1297 path1.move_to((0.0, 0.0));
1298 path1.line_to((0.0, 1.0));
1299 path1.line_to((1.0, 1.0));
1300 path1.line_to((0.0, 0.0));
1301 if !*implicit_closing_line {
1302 path1.line_to((0.0, 0.0));
1303 }
1304 path1.close_path();
1305
1306 let mut path2 = BezPath::new();
1307 path2.move_to((0.0, 0.0));
1308 path2.line_to((0.0, 2.0));
1309 path2.line_to((2.0, 2.0));
1310 path2.line_to((2.0, 0.0));
1311 if !*implicit_closing_line {
1312 path2.line_to((0.0, 0.0));
1313 }
1314 path2.close_path();
1315
1316 let glyphs = simple_glyphs_from_kurbo(&[path1, path2]).unwrap();
1317
1318 assert_contour_points(
1319 &glyphs[0],
1320 vec![vec![
1321 CurvePoint::on_curve(0, 0),
1322 CurvePoint::on_curve(0, 1),
1323 CurvePoint::on_curve(1, 1),
1324 CurvePoint::on_curve(0, 0), ]],
1326 );
1327 assert_contour_points(
1328 &glyphs[1],
1329 vec![vec![
1330 CurvePoint::on_curve(0, 0),
1331 CurvePoint::on_curve(0, 2),
1332 CurvePoint::on_curve(2, 2),
1333 CurvePoint::on_curve(2, 0),
1334 ]],
1335 );
1336 }
1337 }
1338
1339 #[test]
1340 fn simple_glyphs_from_kurbo_2_quads_1_line_closed_duplicate_last_pt() {
1341 for implicit_closing_line in &[true, false] {
1342 let mut path1 = BezPath::new();
1345 path1.move_to((0.0, 0.0));
1346 path1.quad_to((0.0, 1.0), (1.0, 1.0));
1347 path1.quad_to((1.0, 0.0), (0.0, 0.0));
1348 if !*implicit_closing_line {
1349 path1.line_to((0.0, 0.0));
1350 }
1351 path1.close_path();
1352
1353 let mut path2 = BezPath::new();
1354 path2.move_to((0.0, 0.0));
1355 path2.quad_to((0.0, 2.0), (2.0, 2.0));
1356 path2.quad_to((2.0, 1.0), (1.0, 0.0));
1357 if !*implicit_closing_line {
1358 path2.line_to((0.0, 0.0));
1359 }
1360 path2.close_path();
1361
1362 let glyphs = simple_glyphs_from_kurbo(&[path1, path2]).unwrap();
1363
1364 assert_contour_points(
1365 &glyphs[0],
1366 vec![vec![
1367 CurvePoint::on_curve(0, 0),
1368 CurvePoint::off_curve(0, 1),
1369 CurvePoint::on_curve(1, 1),
1370 CurvePoint::off_curve(1, 0),
1371 CurvePoint::on_curve(0, 0), ]],
1373 );
1374 assert_contour_points(
1375 &glyphs[1],
1376 vec![vec![
1377 CurvePoint::on_curve(0, 0),
1378 CurvePoint::off_curve(0, 2),
1379 CurvePoint::on_curve(2, 2),
1380 CurvePoint::off_curve(2, 1),
1381 CurvePoint::on_curve(1, 0),
1382 ]],
1383 );
1384 }
1385 }
1386
1387 #[test]
1388 fn simple_glyph_from_kurbo_equidistant_but_not_collinear_points() {
1389 let mut path = BezPath::new();
1390 path.move_to((0.0, 0.0));
1391 path.quad_to((2.0, 2.0), (4.0, 3.0));
1392 path.quad_to((6.0, 2.0), (8.0, 0.0));
1393 path.close_path();
1394
1395 let glyph = SimpleGlyph::from_bezpath(&path).unwrap();
1396
1397 assert_contour_points(
1398 &glyph,
1399 vec![vec![
1400 CurvePoint::on_curve(0, 0),
1401 CurvePoint::off_curve(2, 2),
1402 CurvePoint::on_curve(4, 3),
1406 CurvePoint::off_curve(6, 2),
1407 CurvePoint::on_curve(8, 0),
1408 ]],
1409 );
1410 }
1411
1412 #[test]
1413 fn repeatable_flags_basic() {
1414 let flags = [
1415 SimpleGlyphFlags::ON_CURVE_POINT,
1416 SimpleGlyphFlags::X_SHORT_VECTOR,
1417 SimpleGlyphFlags::X_SHORT_VECTOR,
1418 ];
1419 let repeatable = RepeatableFlag::iter_from_flags(flags).collect::<Vec<_>>();
1420 let expected = flags
1421 .into_iter()
1422 .map(|flag| RepeatableFlag { flag, repeat: 0 })
1423 .collect::<Vec<_>>();
1424
1425 assert_eq!(repeatable, expected);
1428 }
1429
1430 #[test]
1431 fn repeatable_flags_repeats() {
1432 let some_dupes = std::iter::repeat_n(SimpleGlyphFlags::ON_CURVE_POINT, 4);
1433 let many_dupes = std::iter::repeat_n(SimpleGlyphFlags::Y_SHORT_VECTOR, 257);
1434 let repeatable =
1435 RepeatableFlag::iter_from_flags(some_dupes.chain(many_dupes)).collect::<Vec<_>>();
1436 assert_eq!(repeatable.len(), 3);
1437 assert_eq!(
1438 repeatable[0],
1439 RepeatableFlag {
1440 flag: SimpleGlyphFlags::ON_CURVE_POINT | SimpleGlyphFlags::REPEAT_FLAG,
1441 repeat: 3
1442 }
1443 );
1444 assert_eq!(
1445 repeatable[1],
1446 RepeatableFlag {
1447 flag: SimpleGlyphFlags::Y_SHORT_VECTOR | SimpleGlyphFlags::REPEAT_FLAG,
1448 repeat: u8::MAX,
1449 }
1450 );
1451
1452 assert_eq!(
1453 repeatable[2],
1454 RepeatableFlag {
1455 flag: SimpleGlyphFlags::Y_SHORT_VECTOR,
1456 repeat: 0,
1457 }
1458 )
1459 }
1460
1461 #[test]
1462 fn mid_points() {
1463 assert!(is_mid_point(
1465 kurbo::Point::new(0.0, 0.0),
1466 kurbo::Point::new(1.0, 1.0),
1467 kurbo::Point::new(2.0, 2.0)
1468 ));
1469 assert!(is_mid_point(
1471 kurbo::Point::new(0.5, 0.5),
1472 kurbo::Point::new(3.0, 3.0),
1473 kurbo::Point::new(5.5, 5.5)
1474 ));
1475 assert!(is_mid_point(
1477 kurbo::Point::new(0.0, 0.0),
1478 kurbo::Point::new(1.00001, 0.99999),
1479 kurbo::Point::new(2.0, 2.0)
1480 ));
1481 assert!(is_mid_point(
1483 kurbo::Point::new(0.0, 0.0),
1484 kurbo::Point::new(-1.499999, 0.500001),
1485 kurbo::Point::new(-2.0, 2.0)
1486 ));
1487 assert!(!is_mid_point(
1489 kurbo::Point::new(0.0, 0.0),
1490 kurbo::Point::new(1.0, 1.5),
1491 kurbo::Point::new(2.0, 2.0)
1492 ));
1493 }
1494}