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) =
673 loca.get_glyf(GlyphId::new(0), &glyf).unwrap().unwrap()
674 else {
675 panic!("not a simple glyph")
676 };
677 let orig_bytes = orig.offset_data();
678
679 let ours = SimpleGlyph::from_table_ref(&orig);
680 let bytes = pad_for_loca_format(&loca, crate::dump_table(&ours).unwrap());
681 let ours = read_glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
682
683 let our_points = ours.points().collect::<Vec<_>>();
684 let their_points = orig.points().collect::<Vec<_>>();
685 assert_eq!(our_points, their_points);
686 assert_eq!(orig_bytes.as_ref(), bytes);
687 assert_eq!(orig.glyph_data(), ours.glyph_data());
688 assert_eq!(orig_bytes.len(), bytes.len());
689 }
690
691 #[test]
692 fn round_trip_simple() {
693 let font = FontRef::new(font_test_data::SIMPLE_GLYF).unwrap();
694 let loca = font.loca(None).unwrap();
695 let glyf = font.glyf().unwrap();
696 let read_glyf::Glyph::Simple(orig) =
697 loca.get_glyf(GlyphId::new(2), &glyf).unwrap().unwrap()
698 else {
699 panic!("not a simple glyph")
700 };
701 let orig_bytes = orig.offset_data();
702
703 let bezpath = BezPath::from_svg("M278,710 L278,470 L998,470 L998,710 Z").unwrap();
704
705 let ours = SimpleGlyph::from_bezpath(&bezpath).unwrap();
706 let bytes = pad_for_loca_format(&loca, crate::dump_table(&ours).unwrap());
707 let ours = read_glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
708
709 let our_points = ours.points().collect::<Vec<_>>();
710 let their_points = orig.points().collect::<Vec<_>>();
711 assert_eq!(our_points, their_points);
712 assert_eq!(orig_bytes.as_ref(), bytes);
713 assert_eq!(orig.glyph_data(), ours.glyph_data());
714 assert_eq!(orig_bytes.len(), bytes.len());
715 }
716
717 #[test]
718 fn round_trip_multi_contour() {
719 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
720 let loca = font.loca(None).unwrap();
721 let glyf = font.glyf().unwrap();
722 let read_glyf::Glyph::Simple(orig) =
723 loca.get_glyf(GlyphId::new(1), &glyf).unwrap().unwrap()
724 else {
725 panic!("not a simple glyph")
726 };
727 let orig_bytes = orig.offset_data();
728
729 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();
730
731 let ours = SimpleGlyph::from_bezpath(&bezpath).unwrap();
732 let bytes = pad_for_loca_format(&loca, crate::dump_table(&ours).unwrap());
733 let ours = read_glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
734
735 let our_points = ours.points().collect::<Vec<_>>();
736 let their_points = orig.points().collect::<Vec<_>>();
737 dbg!(
738 SimpleGlyphFlags::from_bits(1),
739 SimpleGlyphFlags::from_bits(9)
740 );
741 assert_eq!(our_points, their_points);
742 assert_eq!(orig.glyph_data(), ours.glyph_data());
743 assert_eq!(orig_bytes.len(), bytes.len());
744 assert_eq!(orig_bytes.as_ref(), bytes);
745 }
746
747 #[test]
748 fn simple_glyph_open_path() {
749 let mut path = BezPath::new();
750 path.move_to((20., -100.));
751 path.quad_to((1337., 1338.), (-50., -69.0));
752 path.quad_to((13., 255.), (-255., 256.));
753 path.line_to((20., -100.));
756
757 let glyph = SimpleGlyph::from_bezpath(&path).unwrap();
758 let bytes = crate::dump_table(&glyph).unwrap();
759 let read = read_fonts::tables::glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
760 assert_eq!(read.number_of_contours(), 1);
761 assert_eq!(read.num_points(), 6);
762 assert_eq!(read.end_pts_of_contours(), &[5]);
763 let points = read.points().collect::<Vec<_>>();
764 assert_eq!(points[0].x, 20);
765 assert_eq!(points[0].y, -100);
766 assert!(points[0].on_curve);
767 assert_eq!(points[1].x, 1337);
768 assert_eq!(points[1].y, 1338);
769 assert!(!points[1].on_curve);
770 assert_eq!(points[4].x, -255);
771 assert_eq!(points[4].y, 256);
772 assert!(points[4].on_curve);
773 assert_eq!(points[5].x, 20);
774 assert_eq!(points[5].y, -100);
775 assert!(points[5].on_curve);
776 }
777
778 #[test]
779 fn simple_glyph_closed_path_implicit_vs_explicit_closing_line() {
780 let mut path1 = BezPath::new();
781 path1.move_to((20., -100.));
782 path1.quad_to((1337., 1338.), (-50., -69.0));
783 path1.quad_to((13., 255.), (-255., 256.));
784 path1.close_path();
785
786 let mut path2 = BezPath::new();
787 path2.move_to((20., -100.));
788 path2.quad_to((1337., 1338.), (-50., -69.0));
789 path2.quad_to((13., 255.), (-255., 256.));
790 path2.line_to((20., -100.));
793 path2.close_path();
794
795 for path in &[path1, path2] {
796 let glyph = SimpleGlyph::from_bezpath(path).unwrap();
797 let bytes = crate::dump_table(&glyph).unwrap();
798 let read =
799 read_fonts::tables::glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
800 assert_eq!(read.number_of_contours(), 1);
801 assert_eq!(read.num_points(), 5);
802 assert_eq!(read.end_pts_of_contours(), &[4]);
803 let points = read.points().collect::<Vec<_>>();
804 assert_eq!(points[0].x, 20);
805 assert_eq!(points[0].y, -100);
806 assert!(points[0].on_curve);
807 assert_eq!(points[1].x, 1337);
808 assert_eq!(points[1].y, 1338);
809 assert!(!points[1].on_curve);
810 assert_eq!(points[4].x, -255);
811 assert_eq!(points[4].y, 256);
812 assert!(points[4].on_curve);
813 }
814 }
815
816 #[test]
817 fn keep_single_point_contours() {
818 let mut path = BezPath::new();
820 path.move_to((0.0, 0.0));
821 path.move_to((1.0, 2.0));
823 path.close_path();
824
825 let glyph = SimpleGlyph::from_bezpath(&path).unwrap();
826 let bytes = crate::dump_table(&glyph).unwrap();
827 let read = read_fonts::tables::glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
828 assert_eq!(read.number_of_contours(), 2);
829 assert_eq!(read.num_points(), 2);
830 assert_eq!(read.end_pts_of_contours(), &[0, 1]);
831 let points = read.points().collect::<Vec<_>>();
832 assert_eq!(points[0].x, 0);
833 assert_eq!(points[0].y, 0);
834 assert!(points[0].on_curve);
835 assert_eq!(points[1].x, 1);
836 assert_eq!(points[1].y, 2);
837 assert!(points[0].on_curve);
838 }
839
840 #[test]
841 fn compile_repeatable_flags() {
842 let mut path = BezPath::new();
843 path.move_to((20., -100.));
844 path.line_to((25., -90.));
845 path.line_to((50., -69.));
846 path.line_to((80., -20.));
847
848 let glyph = SimpleGlyph::from_bezpath(&path).unwrap();
849 let flags = glyph
850 .compute_point_deltas()
851 .map(|x| x.0)
852 .collect::<Vec<_>>();
853 let r_flags = RepeatableFlag::iter_from_flags(flags.iter().copied()).collect::<Vec<_>>();
854
855 assert_eq!(r_flags.len(), 2, "{r_flags:?}");
856 let bytes = crate::dump_table(&glyph).unwrap();
857 let read = read_fonts::tables::glyf::SimpleGlyph::read(bytes.as_slice().into()).unwrap();
858 assert_eq!(read.number_of_contours(), 1);
859 assert_eq!(read.num_points(), 4);
860 assert_eq!(read.end_pts_of_contours(), &[3]);
861 let points = read.points().collect::<Vec<_>>();
862 assert_eq!(points[0].x, 20);
863 assert_eq!(points[0].y, -100);
864 assert_eq!(points[1].x, 25);
865 assert_eq!(points[1].y, -90);
866 assert_eq!(points[2].x, 50);
867 assert_eq!(points[2].y, -69);
868 assert_eq!(points[3].x, 80);
869 assert_eq!(points[3].y, -20);
870 }
871
872 #[test]
873 fn simple_glyphs_from_kurbo_unequal_number_of_elements() {
874 let mut path1 = BezPath::new();
875 path1.move_to((0., 0.));
876 path1.line_to((1., 1.));
877 path1.line_to((2., 2.));
878 path1.line_to((0., 0.));
879 path1.close_path();
880 assert_eq!(path1.elements().len(), 5);
881
882 let mut path2 = BezPath::new();
883 path2.move_to((3., 3.));
884 path2.line_to((4., 4.));
885 path2.line_to((5., 5.));
886 path2.line_to((6., 6.));
887 path2.line_to((3., 3.));
888 path2.close_path();
889 assert_eq!(path2.elements().len(), 6);
890
891 let err = simple_glyphs_from_kurbo(&[path1, path2]).unwrap_err();
892 assert!(matches!(err, MalformedPath::UnequalNumberOfElements(_)));
893 assert_eq!(format!("{:?}", err), "UnequalNumberOfElements([5, 6])");
894 }
895
896 #[test]
897 fn simple_glyphs_from_kurbo_inconsistent_path_elements() {
898 let mut path1 = BezPath::new();
899 path1.move_to((0., 0.));
900 path1.line_to((1., 1.));
901 path1.quad_to((2., 2.), (0., 0.));
902 path1.close_path();
903 let mut path2 = BezPath::new();
904 path2.move_to((3., 3.));
905 path2.quad_to((4., 4.), (5., 5.)); path2.line_to((3., 3.));
907 path2.close_path();
908
909 let err = simple_glyphs_from_kurbo(&[path1, path2]).unwrap_err();
910 assert!(matches!(err, MalformedPath::InconsistentPathElements(1, _)));
911 assert_eq!(
912 format!("{:?}", err),
913 "InconsistentPathElements(1, [\"L\", \"Q\"])"
914 );
915 }
916
917 fn make_interpolatable_paths(
938 num_paths: usize,
939 el_types: &str,
940 last_pt_equal_move: bool,
941 ) -> Vec<BezPath> {
942 let mut paths = Vec::new();
943 let mut start = 0.0;
946 let mut points = std::iter::from_fn(move || {
947 let value = start;
948 start += 1.0;
949 Some((value, value))
950 });
951 let el_types = el_types.chars().collect::<Vec<_>>();
952 assert!(!el_types.is_empty());
953 for _ in 0..num_paths {
954 let mut path = BezPath::new();
955 let mut start_pt = None;
956 let mut el_types_iter = el_types.iter().peekable();
958 while let Some(&el_type) = el_types_iter.next() {
959 let next_el_type = el_types_iter.peek().map(|x| **x).unwrap_or('M');
960 match el_type {
961 'M' => {
962 start_pt = points.next();
963 path.move_to(start_pt.unwrap());
964 }
965 'L' => {
966 if matches!(next_el_type, 'Z' | 'M') && last_pt_equal_move {
967 path.line_to(start_pt.unwrap());
968 } else {
969 path.line_to(points.next().unwrap());
970 }
971 }
972 'Q' => {
973 let p1 = points.next().unwrap();
974 let p2 = if matches!(next_el_type, 'Z' | 'M') && last_pt_equal_move {
975 start_pt.unwrap()
976 } else {
977 points.next().unwrap()
978 };
979 path.quad_to(p1, p2);
980 }
981 'Z' => {
982 path.close_path();
983 start_pt = None;
984 }
985 _ => panic!("Unsupported element type {:?}", el_type),
986 }
987 }
988 paths.push(path);
989 }
990 assert_eq!(paths.len(), num_paths);
991 paths
992 }
993
994 fn assert_contour_points(glyph: &SimpleGlyph, all_points: Vec<Vec<CurvePoint>>) {
995 let expected_num_contours = all_points.len();
996 assert_eq!(glyph.contours.len(), expected_num_contours);
997 for (contour, expected_points) in glyph.contours.iter().zip(all_points.iter()) {
998 let points = contour.iter().copied().collect::<Vec<_>>();
999 assert_eq!(points, *expected_points);
1000 }
1001 }
1002
1003 #[test]
1004 fn simple_glyphs_from_kurbo_3_lines_closed() {
1005 let paths = make_interpolatable_paths(2, "MLLLZ", true);
1007 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1008
1009 assert_contour_points(
1010 &glyphs[0],
1011 vec![vec![
1012 CurvePoint::on_curve(0, 0),
1013 CurvePoint::on_curve(1, 1),
1014 CurvePoint::on_curve(2, 2),
1015 ]],
1016 );
1017 assert_contour_points(
1018 &glyphs[1],
1019 vec![vec![
1020 CurvePoint::on_curve(3, 3),
1021 CurvePoint::on_curve(4, 4),
1022 CurvePoint::on_curve(5, 5),
1023 ]],
1024 );
1025 }
1026
1027 #[test]
1028 fn simple_glyphs_from_kurbo_3_lines_implicitly_closed() {
1029 let paths = make_interpolatable_paths(2, "MLLZ", false);
1031 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1032
1033 assert_contour_points(
1034 &glyphs[0],
1035 vec![vec![
1036 CurvePoint::on_curve(0, 0),
1037 CurvePoint::on_curve(1, 1),
1038 CurvePoint::on_curve(2, 2),
1039 ]],
1040 );
1041 assert_contour_points(
1042 &glyphs[1],
1043 vec![vec![
1044 CurvePoint::on_curve(3, 3),
1045 CurvePoint::on_curve(4, 4),
1046 CurvePoint::on_curve(5, 5),
1047 ]],
1048 );
1049 }
1050
1051 #[test]
1052 fn simple_glyphs_from_kurbo_2_quads_closed() {
1053 let paths = make_interpolatable_paths(2, "MQQZ", true);
1058 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1059
1060 assert_contour_points(
1061 &glyphs[0],
1062 vec![vec![
1063 CurvePoint::on_curve(0, 0),
1064 CurvePoint::off_curve(1, 1),
1065 CurvePoint::off_curve(3, 3),
1067 ]],
1068 );
1069 assert_contour_points(
1070 &glyphs[1],
1071 vec![vec![
1072 CurvePoint::on_curve(4, 4),
1073 CurvePoint::off_curve(5, 5),
1074 CurvePoint::off_curve(7, 7),
1076 ]],
1077 );
1078 }
1079
1080 #[test]
1081 fn simple_glyphs_from_kurbo_2_quads_1_line_implicitly_closed() {
1082 let paths = make_interpolatable_paths(2, "MQQZ", false);
1086 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1087
1088 assert_contour_points(
1089 &glyphs[0],
1090 vec![vec![
1091 CurvePoint::on_curve(0, 0),
1092 CurvePoint::off_curve(1, 1),
1093 CurvePoint::off_curve(3, 3),
1095 CurvePoint::on_curve(4, 4),
1096 ]],
1097 );
1098 assert_contour_points(
1099 &glyphs[1],
1100 vec![vec![
1101 CurvePoint::on_curve(5, 5),
1102 CurvePoint::off_curve(6, 6),
1103 CurvePoint::off_curve(8, 8),
1105 CurvePoint::on_curve(9, 9),
1106 ]],
1107 );
1108 }
1109
1110 #[test]
1111 fn simple_glyphs_from_kurbo_multiple_contours_mixed_segments() {
1112 let paths = make_interpolatable_paths(4, "MLQQZMQLQLZ", true);
1114 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1115
1116 assert_contour_points(
1117 &glyphs[0],
1118 vec![
1119 vec![
1120 CurvePoint::on_curve(0, 0),
1121 CurvePoint::on_curve(1, 1),
1122 CurvePoint::off_curve(2, 2),
1123 CurvePoint::off_curve(4, 4),
1125 ],
1126 vec![
1127 CurvePoint::on_curve(5, 5),
1128 CurvePoint::off_curve(6, 6),
1129 CurvePoint::on_curve(7, 7),
1130 CurvePoint::on_curve(8, 8),
1131 CurvePoint::off_curve(9, 9),
1132 CurvePoint::on_curve(10, 10),
1133 ],
1134 ],
1135 );
1136 }
1137
1138 #[test]
1139 fn simple_glyphs_from_kurbo_all_quad_off_curves() {
1140 let mut path1 = BezPath::new();
1143 path1.move_to((0.0, 1.0));
1144 path1.quad_to((1.0, 1.0), (1.0, 0.0));
1145 path1.quad_to((1.0, -1.0), (0.0, -1.0));
1146 path1.quad_to((-1.0, -1.0), (-1.0, 0.0));
1147 path1.quad_to((-1.0, 1.0), (0.0, 1.0));
1148 path1.close_path();
1149
1150 let mut path2 = path1.clone();
1151 path2.apply_affine(Affine::scale(2.0));
1152
1153 let glyphs = simple_glyphs_from_kurbo(&[path1, path2]).unwrap();
1154
1155 assert_contour_points(
1156 &glyphs[0],
1157 vec![vec![
1158 CurvePoint::off_curve(1, 1),
1159 CurvePoint::off_curve(1, -1),
1160 CurvePoint::off_curve(-1, -1),
1161 CurvePoint::off_curve(-1, 1),
1162 ]],
1163 );
1164 assert_contour_points(
1165 &glyphs[1],
1166 vec![vec![
1167 CurvePoint::off_curve(2, 2),
1168 CurvePoint::off_curve(2, -2),
1169 CurvePoint::off_curve(-2, -2),
1170 CurvePoint::off_curve(-2, 2),
1171 ]],
1172 );
1173 }
1174
1175 #[test]
1176 fn simple_glyphs_from_kurbo_keep_on_curve_unless_impliable_for_all() {
1177 let mut path1 = BezPath::new();
1178 path1.move_to((0.0, 0.0));
1179 path1.quad_to((0.0, 1.0), (1.0, 1.0)); path1.quad_to((2.0, 1.0), (2.0, 0.0));
1181 path1.line_to((0.0, 0.0));
1182 path1.close_path();
1183
1184 assert_contour_points(
1187 &SimpleGlyph::from_bezpath(&path1).unwrap(),
1188 vec![vec![
1189 CurvePoint::on_curve(0, 0),
1190 CurvePoint::off_curve(0, 1),
1191 CurvePoint::off_curve(2, 1),
1193 CurvePoint::on_curve(2, 0),
1194 ]],
1195 );
1196
1197 let mut path2 = BezPath::new();
1198 path2.move_to((0.0, 0.0));
1199 path2.quad_to((0.0, 2.0), (2.0, 2.0)); path2.quad_to((3.0, 2.0), (3.0, 0.0));
1201 path2.line_to((0.0, 0.0));
1202 path2.close_path();
1203
1204 let glyphs = simple_glyphs_from_kurbo(&[path1, path2]).unwrap();
1205
1206 assert_contour_points(
1209 &glyphs[0],
1210 vec![vec![
1211 CurvePoint::on_curve(0, 0),
1212 CurvePoint::off_curve(0, 1),
1213 CurvePoint::on_curve(1, 1), CurvePoint::off_curve(2, 1),
1215 CurvePoint::on_curve(2, 0),
1216 ]],
1217 );
1218 assert_contour_points(
1219 &glyphs[1],
1220 vec![vec![
1221 CurvePoint::on_curve(0, 0),
1222 CurvePoint::off_curve(0, 2),
1223 CurvePoint::on_curve(2, 2), CurvePoint::off_curve(3, 2),
1225 CurvePoint::on_curve(3, 0),
1226 ]],
1227 );
1228 }
1229
1230 #[test]
1231 fn simple_glyphs_from_kurbo_2_lines_open() {
1232 let paths = make_interpolatable_paths(2, "MLL", false);
1235 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1236
1237 assert_contour_points(
1238 &glyphs[0],
1239 vec![vec![
1240 CurvePoint::on_curve(0, 0),
1241 CurvePoint::on_curve(1, 1),
1242 CurvePoint::on_curve(2, 2),
1243 ]],
1244 );
1245 assert_contour_points(
1246 &glyphs[1],
1247 vec![vec![
1248 CurvePoint::on_curve(3, 3),
1249 CurvePoint::on_curve(4, 4),
1250 CurvePoint::on_curve(5, 5),
1251 ]],
1252 );
1253 }
1254
1255 #[test]
1256 fn simple_glyphs_from_kurbo_3_lines_open_duplicate_last_pt() {
1257 let paths = make_interpolatable_paths(2, "MLLL", true);
1262 let glyphs = simple_glyphs_from_kurbo(&paths).unwrap();
1263
1264 assert_contour_points(
1265 &glyphs[0],
1266 vec![vec![
1267 CurvePoint::on_curve(0, 0),
1268 CurvePoint::on_curve(1, 1),
1269 CurvePoint::on_curve(2, 2),
1270 CurvePoint::on_curve(0, 0),
1271 ]],
1272 );
1273 assert_contour_points(
1274 &glyphs[1],
1275 vec![vec![
1276 CurvePoint::on_curve(3, 3),
1277 CurvePoint::on_curve(4, 4),
1278 CurvePoint::on_curve(5, 5),
1279 CurvePoint::on_curve(3, 3),
1280 ]],
1281 );
1282 }
1283
1284 #[test]
1285 fn simple_glyphs_from_kurbo_4_lines_closed_duplicate_last_pt() {
1286 for implicit_closing_line in &[true, false] {
1287 let mut path1 = BezPath::new();
1291 path1.move_to((0.0, 0.0));
1292 path1.line_to((0.0, 1.0));
1293 path1.line_to((1.0, 1.0));
1294 path1.line_to((0.0, 0.0));
1295 if !*implicit_closing_line {
1296 path1.line_to((0.0, 0.0));
1297 }
1298 path1.close_path();
1299
1300 let mut path2 = BezPath::new();
1301 path2.move_to((0.0, 0.0));
1302 path2.line_to((0.0, 2.0));
1303 path2.line_to((2.0, 2.0));
1304 path2.line_to((2.0, 0.0));
1305 if !*implicit_closing_line {
1306 path2.line_to((0.0, 0.0));
1307 }
1308 path2.close_path();
1309
1310 let glyphs = simple_glyphs_from_kurbo(&[path1, path2]).unwrap();
1311
1312 assert_contour_points(
1313 &glyphs[0],
1314 vec![vec![
1315 CurvePoint::on_curve(0, 0),
1316 CurvePoint::on_curve(0, 1),
1317 CurvePoint::on_curve(1, 1),
1318 CurvePoint::on_curve(0, 0), ]],
1320 );
1321 assert_contour_points(
1322 &glyphs[1],
1323 vec![vec![
1324 CurvePoint::on_curve(0, 0),
1325 CurvePoint::on_curve(0, 2),
1326 CurvePoint::on_curve(2, 2),
1327 CurvePoint::on_curve(2, 0),
1328 ]],
1329 );
1330 }
1331 }
1332
1333 #[test]
1334 fn simple_glyphs_from_kurbo_2_quads_1_line_closed_duplicate_last_pt() {
1335 for implicit_closing_line in &[true, false] {
1336 let mut path1 = BezPath::new();
1339 path1.move_to((0.0, 0.0));
1340 path1.quad_to((0.0, 1.0), (1.0, 1.0));
1341 path1.quad_to((1.0, 0.0), (0.0, 0.0));
1342 if !*implicit_closing_line {
1343 path1.line_to((0.0, 0.0));
1344 }
1345 path1.close_path();
1346
1347 let mut path2 = BezPath::new();
1348 path2.move_to((0.0, 0.0));
1349 path2.quad_to((0.0, 2.0), (2.0, 2.0));
1350 path2.quad_to((2.0, 1.0), (1.0, 0.0));
1351 if !*implicit_closing_line {
1352 path2.line_to((0.0, 0.0));
1353 }
1354 path2.close_path();
1355
1356 let glyphs = simple_glyphs_from_kurbo(&[path1, path2]).unwrap();
1357
1358 assert_contour_points(
1359 &glyphs[0],
1360 vec![vec![
1361 CurvePoint::on_curve(0, 0),
1362 CurvePoint::off_curve(0, 1),
1363 CurvePoint::on_curve(1, 1),
1364 CurvePoint::off_curve(1, 0),
1365 CurvePoint::on_curve(0, 0), ]],
1367 );
1368 assert_contour_points(
1369 &glyphs[1],
1370 vec![vec![
1371 CurvePoint::on_curve(0, 0),
1372 CurvePoint::off_curve(0, 2),
1373 CurvePoint::on_curve(2, 2),
1374 CurvePoint::off_curve(2, 1),
1375 CurvePoint::on_curve(1, 0),
1376 ]],
1377 );
1378 }
1379 }
1380
1381 #[test]
1382 fn simple_glyph_from_kurbo_equidistant_but_not_collinear_points() {
1383 let mut path = BezPath::new();
1384 path.move_to((0.0, 0.0));
1385 path.quad_to((2.0, 2.0), (4.0, 3.0));
1386 path.quad_to((6.0, 2.0), (8.0, 0.0));
1387 path.close_path();
1388
1389 let glyph = SimpleGlyph::from_bezpath(&path).unwrap();
1390
1391 assert_contour_points(
1392 &glyph,
1393 vec![vec![
1394 CurvePoint::on_curve(0, 0),
1395 CurvePoint::off_curve(2, 2),
1396 CurvePoint::on_curve(4, 3),
1400 CurvePoint::off_curve(6, 2),
1401 CurvePoint::on_curve(8, 0),
1402 ]],
1403 );
1404 }
1405
1406 #[test]
1407 fn repeatable_flags_basic() {
1408 let flags = [
1409 SimpleGlyphFlags::ON_CURVE_POINT,
1410 SimpleGlyphFlags::X_SHORT_VECTOR,
1411 SimpleGlyphFlags::X_SHORT_VECTOR,
1412 ];
1413 let repeatable = RepeatableFlag::iter_from_flags(flags).collect::<Vec<_>>();
1414 let expected = flags
1415 .into_iter()
1416 .map(|flag| RepeatableFlag { flag, repeat: 0 })
1417 .collect::<Vec<_>>();
1418
1419 assert_eq!(repeatable, expected);
1422 }
1423
1424 #[test]
1425 fn repeatable_flags_repeats() {
1426 let some_dupes = std::iter::repeat_n(SimpleGlyphFlags::ON_CURVE_POINT, 4);
1427 let many_dupes = std::iter::repeat_n(SimpleGlyphFlags::Y_SHORT_VECTOR, 257);
1428 let repeatable =
1429 RepeatableFlag::iter_from_flags(some_dupes.chain(many_dupes)).collect::<Vec<_>>();
1430 assert_eq!(repeatable.len(), 3);
1431 assert_eq!(
1432 repeatable[0],
1433 RepeatableFlag {
1434 flag: SimpleGlyphFlags::ON_CURVE_POINT | SimpleGlyphFlags::REPEAT_FLAG,
1435 repeat: 3
1436 }
1437 );
1438 assert_eq!(
1439 repeatable[1],
1440 RepeatableFlag {
1441 flag: SimpleGlyphFlags::Y_SHORT_VECTOR | SimpleGlyphFlags::REPEAT_FLAG,
1442 repeat: u8::MAX,
1443 }
1444 );
1445
1446 assert_eq!(
1447 repeatable[2],
1448 RepeatableFlag {
1449 flag: SimpleGlyphFlags::Y_SHORT_VECTOR,
1450 repeat: 0,
1451 }
1452 )
1453 }
1454
1455 #[test]
1456 fn mid_points() {
1457 assert!(is_mid_point(
1459 kurbo::Point::new(0.0, 0.0),
1460 kurbo::Point::new(1.0, 1.0),
1461 kurbo::Point::new(2.0, 2.0)
1462 ));
1463 assert!(is_mid_point(
1465 kurbo::Point::new(0.5, 0.5),
1466 kurbo::Point::new(3.0, 3.0),
1467 kurbo::Point::new(5.5, 5.5)
1468 ));
1469 assert!(is_mid_point(
1471 kurbo::Point::new(0.0, 0.0),
1472 kurbo::Point::new(1.00001, 0.99999),
1473 kurbo::Point::new(2.0, 2.0)
1474 ));
1475 assert!(is_mid_point(
1477 kurbo::Point::new(0.0, 0.0),
1478 kurbo::Point::new(-1.499999, 0.500001),
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.0, 1.5),
1485 kurbo::Point::new(2.0, 2.0)
1486 ));
1487 }
1488}