From ca34e19f99793a4922b61bd354c24b23b98c62d4 Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler Date: Wed, 12 May 2021 22:53:38 +0100 Subject: [PATCH 01/10] More extensive Contour::to_kurbo --- src/glyph/mod.rs | 170 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 127 insertions(+), 43 deletions(-) diff --git a/src/glyph/mod.rs b/src/glyph/mod.rs index a018c3d4..90a97a96 100644 --- a/src/glyph/mod.rs +++ b/src/glyph/mod.rs @@ -315,55 +315,139 @@ impl Contour { self.points.first().map_or(true, |v| v.typ != PointType::Move) } - /// Converts the `Contour` to a [`kurbo::BezPath`]. + /// Converts the `Contour` to a Vec of [`kurbo::PathEl`]. #[cfg(feature = "kurbo")] - pub fn to_kurbo(&self) -> Result { - let mut path = kurbo::BezPath::new(); - let mut offs = std::collections::VecDeque::new(); - let mut points = if self.is_closed() { - // Add end-of-contour offcurves to queue - let rotate = self - .points - .iter() - .rev() - .position(|pt| pt.typ != PointType::OffCurve) - .map(|idx| self.points.len() - 1 - idx); - self.points.iter().cycle().skip(rotate.unwrap_or(0)).take(self.points.len() + 1) - } else { - self.points.iter().cycle().skip(0).take(self.points.len()) - }; - if let Some(start) = points.next() { - path.move_to(start.to_kurbo()); - } - for pt in points { - let kurbo_point = pt.to_kurbo(); - match pt.typ { - PointType::Move => path.move_to(kurbo_point), - PointType::Line => path.line_to(kurbo_point), - PointType::OffCurve => offs.push_back(kurbo_point), - PointType::Curve => { - match offs.make_contiguous() { - [] => return Err(ConvertContourError::new(ErrorKind::BadPoint)), - [p1] => path.quad_to(*p1, kurbo_point), - [p1, p2] => path.curve_to(*p1, *p2, kurbo_point), - _ => return Err(ConvertContourError::new(ErrorKind::TooManyOffCurves)), - }; - offs.clear(); + pub fn to_kurbo(&self) -> Result, ConvertContourError> { + use kurbo::{PathEl, Point}; + + let mut points: Vec<&ContourPoint> = self.points.iter().collect(); + let mut segments = Vec::new(); + + let closed; + let start: &ContourPoint; + let implied_oncurve: ContourPoint; + + // Phase 1: Preparation + match points.len() { + // Empty contours cannot be represented by segments. + 0 => return Ok(segments), + // Single points are converted to open MoveTos because closed single points of any + // PointType make no sense. + 1 => { + segments.push(PathEl::MoveTo(Point::new(points[0].x as f64, points[0].y as f64))); + return Ok(segments); + } + // Contours with two or more points come in three flavors...: + _ => { + // 1. ... Open contours begin with a Move. Start the segment on the first point + // and don't close it. Note: Trailing off-curves are an error. + if let PointType::Move = points[0].typ { + closed = false; + // Pop off the Move here so the segmentation loop below can just error out on + // encountering any other Move. + start = points.remove(0); + } else { + closed = true; + // 2. ... Closed contours begin with anything else. Locate the first on-curve + // point and rotate the point list so that it _ends_ with that point. The first + // point could be a curve with its off-curves at the end; moving the point + // makes always makes all associated off-curves reachable in a single pass + // without wrapping around. Start the segment on the last point. + if let Some(first_oncurve) = + points.iter().position(|e| e.typ != PointType::OffCurve) + { + points.rotate_left(first_oncurve + 1); + start = points.last().unwrap(); + // 3. ... Closed all-offcurve quadratic contours: Rare special case of + // TrueType's “implied on-curve points” principle. Compute the last implied + // on-curve point and append it, so we can handle this normally in the loop + // below. Start the segment on the last, computed point. + } else { + let first = points.first().unwrap(); + let last = points.last().unwrap(); + implied_oncurve = ContourPoint::new( + 0.5 * (last.x + first.x), + 0.5 * (last.y + first.y), + PointType::QCurve, + false, + None, + None, + None, + ); + points.push(&implied_oncurve); + start = &implied_oncurve; + } } - PointType::QCurve => { - while let Some(pt) = offs.pop_front() { - if let Some(next) = offs.front() { - let implied_point = pt.midpoint(*next); - path.quad_to(pt, implied_point); - } else { - path.quad_to(pt, kurbo_point); + } + } + + // Phase 1.5: Always need a MoveTo as the first element. + segments.push(PathEl::MoveTo(Point::new(start.x as f64, start.y as f64))); + + // Phase 2: Conversion + let mut controls: Vec = Vec::new(); + for point in points { + let p = Point::new(point.x as f64, point.y as f64); + match point.typ { + PointType::OffCurve => controls.push(p), + // The first Move is removed from the points above, any other Move we encounter is illegal. + PointType::Move => return Err(ConvertContourError::new(ErrorKind::UnexpectedMove)), + // A line must have 0 off-curves preceeding it. + PointType::Line => match controls.len() { + 0 => segments.push(PathEl::LineTo(p)), + _ => { + return Err(ConvertContourError::new( + ErrorKind::UnexpectedPointAfterOffCurve, + )) + } + }, + // A quadratic curve can have any number of off-curves preceeding it. Zero means it's + // a line, numbers > 1 mean we must expand “implied on-curve points”. + PointType::QCurve => match controls.len() { + 0 => segments.push(PathEl::LineTo(p)), + 1 => { + segments.push(PathEl::QuadTo(controls[0], p)); + controls.clear() + } + _ => { + // TODO: make iterator? controls.iter().zip(controls.iter().cycle().skip(1)) + for i in 0..=controls.len() - 2 { + let c = controls[i]; + let cn = controls[i + 1]; + let pi = Point::new(0.5 * (c.x + cn.x), 0.5 * (c.y + cn.y)); + segments.push(PathEl::QuadTo(c, pi)); } + segments.push(PathEl::QuadTo(controls[controls.len() - 1], p)); + controls.clear() } - offs.clear(); - } + }, + // A curve can have 0, 1 or 2 off-curves preceeding it according to the UFO specification. + // Zero means it's a line, one means it's a quadratic curve, two means it's a cubic curve. + PointType::Curve => match controls.len() { + 0 => segments.push(PathEl::LineTo(p)), + 1 => { + segments.push(PathEl::QuadTo(controls[0], p)); + controls.clear() + } + 2 => { + segments.push(PathEl::CurveTo(controls[0], controls[1], p)); + controls.clear() + } + _ => return Err(ConvertContourError::new(ErrorKind::TooManyOffCurves)), + }, } } - Ok(path) + // If we have control points left at this point, we are an open contour, which must end on + // an on-curve point. + if !controls.is_empty() { + debug_assert!(!closed); + return Err(ConvertContourError::new(ErrorKind::TrailingOffCurves)); + } + if closed { + segments.push(PathEl::ClosePath); + } + + Ok(segments) } } From 10a907730b81d5972e8cb2d7962ba36f7324c97a Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler Date: Thu, 22 Sep 2022 16:34:11 +0800 Subject: [PATCH 02/10] Use to_kurbo method for new Points --- src/glyph/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/glyph/mod.rs b/src/glyph/mod.rs index 90a97a96..5556a522 100644 --- a/src/glyph/mod.rs +++ b/src/glyph/mod.rs @@ -334,7 +334,7 @@ impl Contour { // Single points are converted to open MoveTos because closed single points of any // PointType make no sense. 1 => { - segments.push(PathEl::MoveTo(Point::new(points[0].x as f64, points[0].y as f64))); + segments.push(PathEl::MoveTo(points[0].to_kurbo())); return Ok(segments); } // Contours with two or more points come in three flavors...: @@ -382,12 +382,12 @@ impl Contour { } // Phase 1.5: Always need a MoveTo as the first element. - segments.push(PathEl::MoveTo(Point::new(start.x as f64, start.y as f64))); + segments.push(PathEl::MoveTo(start.to_kurbo())); // Phase 2: Conversion let mut controls: Vec = Vec::new(); for point in points { - let p = Point::new(point.x as f64, point.y as f64); + let p = point.to_kurbo(); match point.typ { PointType::OffCurve => controls.push(p), // The first Move is removed from the points above, any other Move we encounter is illegal. From 0bbfb3844884f89ccd4ffa081e3b1e244984e69a Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler Date: Thu, 22 Sep 2022 16:44:44 +0800 Subject: [PATCH 03/10] Use slice matching --- src/glyph/mod.rs | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/glyph/mod.rs b/src/glyph/mod.rs index 5556a522..5795ab15 100644 --- a/src/glyph/mod.rs +++ b/src/glyph/mod.rs @@ -328,20 +328,20 @@ impl Contour { let implied_oncurve: ContourPoint; // Phase 1: Preparation - match points.len() { + match points.as_slice() { // Empty contours cannot be represented by segments. - 0 => return Ok(segments), + &[] => return Ok(segments), // Single points are converted to open MoveTos because closed single points of any // PointType make no sense. - 1 => { - segments.push(PathEl::MoveTo(points[0].to_kurbo())); + &[p1] => { + segments.push(PathEl::MoveTo(p1.to_kurbo())); return Ok(segments); } // Contours with two or more points come in three flavors...: - _ => { + &[first, .., last] => { // 1. ... Open contours begin with a Move. Start the segment on the first point // and don't close it. Note: Trailing off-curves are an error. - if let PointType::Move = points[0].typ { + if let PointType::Move = first.typ { closed = false; // Pop off the Move here so the segmentation loop below can just error out on // encountering any other Move. @@ -357,14 +357,12 @@ impl Contour { points.iter().position(|e| e.typ != PointType::OffCurve) { points.rotate_left(first_oncurve + 1); - start = points.last().unwrap(); + start = points.last().unwrap(); // Not `last` because we rotate. // 3. ... Closed all-offcurve quadratic contours: Rare special case of // TrueType's “implied on-curve points” principle. Compute the last implied // on-curve point and append it, so we can handle this normally in the loop // below. Start the segment on the last, computed point. } else { - let first = points.first().unwrap(); - let last = points.last().unwrap(); implied_oncurve = ContourPoint::new( 0.5 * (last.x + first.x), 0.5 * (last.y + first.y), @@ -393,8 +391,8 @@ impl Contour { // The first Move is removed from the points above, any other Move we encounter is illegal. PointType::Move => return Err(ConvertContourError::new(ErrorKind::UnexpectedMove)), // A line must have 0 off-curves preceeding it. - PointType::Line => match controls.len() { - 0 => segments.push(PathEl::LineTo(p)), + PointType::Line => match controls.as_slice() { + &[] => segments.push(PathEl::LineTo(p)), _ => { return Err(ConvertContourError::new( ErrorKind::UnexpectedPointAfterOffCurve, @@ -403,10 +401,10 @@ impl Contour { }, // A quadratic curve can have any number of off-curves preceeding it. Zero means it's // a line, numbers > 1 mean we must expand “implied on-curve points”. - PointType::QCurve => match controls.len() { - 0 => segments.push(PathEl::LineTo(p)), - 1 => { - segments.push(PathEl::QuadTo(controls[0], p)); + PointType::QCurve => match controls.as_slice() { + &[] => segments.push(PathEl::LineTo(p)), + &[c1] => { + segments.push(PathEl::QuadTo(c1, p)); controls.clear() } _ => { @@ -423,14 +421,14 @@ impl Contour { }, // A curve can have 0, 1 or 2 off-curves preceeding it according to the UFO specification. // Zero means it's a line, one means it's a quadratic curve, two means it's a cubic curve. - PointType::Curve => match controls.len() { - 0 => segments.push(PathEl::LineTo(p)), - 1 => { - segments.push(PathEl::QuadTo(controls[0], p)); + PointType::Curve => match controls.as_slice() { + &[] => segments.push(PathEl::LineTo(p)), + &[c1] => { + segments.push(PathEl::QuadTo(c1, p)); controls.clear() } - 2 => { - segments.push(PathEl::CurveTo(controls[0], controls[1], p)); + &[c1, c2] => { + segments.push(PathEl::CurveTo(c1, c2, p)); controls.clear() } _ => return Err(ConvertContourError::new(ErrorKind::TooManyOffCurves)), From 52809683bdfcbec6559608e6cdf4b10e0e5feb38 Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler Date: Thu, 22 Sep 2022 18:11:11 +0800 Subject: [PATCH 04/10] Use zip for computing implied on-curves --- src/glyph/mod.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/glyph/mod.rs b/src/glyph/mod.rs index 5795ab15..4837dda6 100644 --- a/src/glyph/mod.rs +++ b/src/glyph/mod.rs @@ -407,15 +407,13 @@ impl Contour { segments.push(PathEl::QuadTo(c1, p)); controls.clear() } - _ => { - // TODO: make iterator? controls.iter().zip(controls.iter().cycle().skip(1)) - for i in 0..=controls.len() - 2 { - let c = controls[i]; - let cn = controls[i + 1]; - let pi = Point::new(0.5 * (c.x + cn.x), 0.5 * (c.y + cn.y)); - segments.push(PathEl::QuadTo(c, pi)); + cn @ [.., last] => { + // Insert a computed on-curve point between each control point. + for (c1, c2) in cn.iter().zip(cn.iter().skip(1)) { + let p1 = Point::new(0.5 * (c1.x + c2.x), 0.5 * (c1.y + c2.y)); + segments.push(PathEl::QuadTo(*c1, p1)); } - segments.push(PathEl::QuadTo(controls[controls.len() - 1], p)); + segments.push(PathEl::QuadTo(*last, p)); controls.clear() } }, From 7068f9f567b240fefbd165d2304209fd0c9256dd Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler Date: Thu, 22 Sep 2022 18:15:54 +0800 Subject: [PATCH 05/10] Change for autoformatting --- src/glyph/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/glyph/mod.rs b/src/glyph/mod.rs index 4837dda6..007ebb93 100644 --- a/src/glyph/mod.rs +++ b/src/glyph/mod.rs @@ -357,7 +357,8 @@ impl Contour { points.iter().position(|e| e.typ != PointType::OffCurve) { points.rotate_left(first_oncurve + 1); - start = points.last().unwrap(); // Not `last` because we rotate. + // Recompute `last` after rotation: + start = points.last().unwrap(); // 3. ... Closed all-offcurve quadratic contours: Rare special case of // TrueType's “implied on-curve points” principle. Compute the last implied // on-curve point and append it, so we can handle this normally in the loop From da3c9642a24aba52d9de12713e087be31ac82cd2 Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler Date: Thu, 22 Sep 2022 18:35:39 +0800 Subject: [PATCH 06/10] Fix lints --- src/glyph/mod.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/glyph/mod.rs b/src/glyph/mod.rs index 007ebb93..676172da 100644 --- a/src/glyph/mod.rs +++ b/src/glyph/mod.rs @@ -328,17 +328,17 @@ impl Contour { let implied_oncurve: ContourPoint; // Phase 1: Preparation - match points.as_slice() { + match *points.as_slice() { // Empty contours cannot be represented by segments. - &[] => return Ok(segments), + [] => return Ok(segments), // Single points are converted to open MoveTos because closed single points of any // PointType make no sense. - &[p1] => { + [p1] => { segments.push(PathEl::MoveTo(p1.to_kurbo())); return Ok(segments); } // Contours with two or more points come in three flavors...: - &[first, .., last] => { + [first, .., last] => { // 1. ... Open contours begin with a Move. Start the segment on the first point // and don't close it. Note: Trailing off-curves are an error. if let PointType::Move = first.typ { @@ -392,8 +392,8 @@ impl Contour { // The first Move is removed from the points above, any other Move we encounter is illegal. PointType::Move => return Err(ConvertContourError::new(ErrorKind::UnexpectedMove)), // A line must have 0 off-curves preceeding it. - PointType::Line => match controls.as_slice() { - &[] => segments.push(PathEl::LineTo(p)), + PointType::Line => match *controls.as_slice() { + [] => segments.push(PathEl::LineTo(p)), _ => { return Err(ConvertContourError::new( ErrorKind::UnexpectedPointAfterOffCurve, @@ -402,31 +402,31 @@ impl Contour { }, // A quadratic curve can have any number of off-curves preceeding it. Zero means it's // a line, numbers > 1 mean we must expand “implied on-curve points”. - PointType::QCurve => match controls.as_slice() { - &[] => segments.push(PathEl::LineTo(p)), - &[c1] => { + PointType::QCurve => match *controls.as_slice() { + [] => segments.push(PathEl::LineTo(p)), + [c1] => { segments.push(PathEl::QuadTo(c1, p)); controls.clear() } - cn @ [.., last] => { + [.., cn] => { // Insert a computed on-curve point between each control point. - for (c1, c2) in cn.iter().zip(cn.iter().skip(1)) { + for (c1, c2) in controls.iter().zip(controls.iter().skip(1)) { let p1 = Point::new(0.5 * (c1.x + c2.x), 0.5 * (c1.y + c2.y)); segments.push(PathEl::QuadTo(*c1, p1)); } - segments.push(PathEl::QuadTo(*last, p)); + segments.push(PathEl::QuadTo(cn, p)); controls.clear() } }, // A curve can have 0, 1 or 2 off-curves preceeding it according to the UFO specification. // Zero means it's a line, one means it's a quadratic curve, two means it's a cubic curve. - PointType::Curve => match controls.as_slice() { - &[] => segments.push(PathEl::LineTo(p)), - &[c1] => { + PointType::Curve => match *controls.as_slice() { + [] => segments.push(PathEl::LineTo(p)), + [c1] => { segments.push(PathEl::QuadTo(c1, p)); controls.clear() } - &[c1, c2] => { + [c1, c2] => { segments.push(PathEl::CurveTo(c1, c2, p)); controls.clear() } From 2a5e4ced02d81ca6cedeafb30cdd8e8f140ccc80 Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler Date: Thu, 22 Sep 2022 19:02:44 +0800 Subject: [PATCH 07/10] Rename match vars --- src/glyph/mod.rs | 98 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 15 deletions(-) diff --git a/src/glyph/mod.rs b/src/glyph/mod.rs index 676172da..c4ee11c8 100644 --- a/src/glyph/mod.rs +++ b/src/glyph/mod.rs @@ -333,15 +333,15 @@ impl Contour { [] => return Ok(segments), // Single points are converted to open MoveTos because closed single points of any // PointType make no sense. - [p1] => { - segments.push(PathEl::MoveTo(p1.to_kurbo())); + [p0] => { + segments.push(PathEl::MoveTo(p0.to_kurbo())); return Ok(segments); } // Contours with two or more points come in three flavors...: - [first, .., last] => { + [p0, .., pn] => { // 1. ... Open contours begin with a Move. Start the segment on the first point // and don't close it. Note: Trailing off-curves are an error. - if let PointType::Move = first.typ { + if let PointType::Move = p0.typ { closed = false; // Pop off the Move here so the segmentation loop below can just error out on // encountering any other Move. @@ -365,8 +365,8 @@ impl Contour { // below. Start the segment on the last, computed point. } else { implied_oncurve = ContourPoint::new( - 0.5 * (last.x + first.x), - 0.5 * (last.y + first.y), + 0.5 * (pn.x + p0.x), + 0.5 * (pn.y + p0.y), PointType::QCurve, false, None, @@ -404,15 +404,15 @@ impl Contour { // a line, numbers > 1 mean we must expand “implied on-curve points”. PointType::QCurve => match *controls.as_slice() { [] => segments.push(PathEl::LineTo(p)), - [c1] => { - segments.push(PathEl::QuadTo(c1, p)); + [c0] => { + segments.push(PathEl::QuadTo(c0, p)); controls.clear() } [.., cn] => { // Insert a computed on-curve point between each control point. - for (c1, c2) in controls.iter().zip(controls.iter().skip(1)) { - let p1 = Point::new(0.5 * (c1.x + c2.x), 0.5 * (c1.y + c2.y)); - segments.push(PathEl::QuadTo(*c1, p1)); + for (c0, c1) in controls.iter().zip(controls.iter().skip(1)) { + let p1 = Point::new(0.5 * (c0.x + c1.x), 0.5 * (c0.y + c1.y)); + segments.push(PathEl::QuadTo(*c0, p1)); } segments.push(PathEl::QuadTo(cn, p)); controls.clear() @@ -422,12 +422,12 @@ impl Contour { // Zero means it's a line, one means it's a quadratic curve, two means it's a cubic curve. PointType::Curve => match *controls.as_slice() { [] => segments.push(PathEl::LineTo(p)), - [c1] => { - segments.push(PathEl::QuadTo(c1, p)); + [c0] => { + segments.push(PathEl::QuadTo(c0, p)); controls.clear() } - [c1, c2] => { - segments.push(PathEl::CurveTo(c1, c2, p)); + [c0, c1] => { + segments.push(PathEl::CurveTo(c0, c1, p)); controls.clear() } _ => return Err(ConvertContourError::new(ErrorKind::TooManyOffCurves)), @@ -896,3 +896,71 @@ impl From for druid::piet::Color { druid::piet::Color::rgba(red, green, blue, alpha) } } + +#[cfg(test)] +mod tests2 { + use super::*; + + #[test] + fn many_control_quads() { + let c1 = Contour::new( + vec![ + ContourPoint::new(0.0, 0.0, PointType::OffCurve, false, None, None, None), + ContourPoint::new(2.0, 2.0, PointType::OffCurve, false, None, None, None), + ContourPoint::new(4.0, 4.0, PointType::OffCurve, false, None, None, None), + ContourPoint::new(100.0, 100.0, PointType::QCurve, false, None, None, None), + ], + None, + None, + ); + + assert_eq!( + c1.to_kurbo().unwrap(), + vec![ + kurbo::PathEl::MoveTo((100.0, 100.0).into()), + kurbo::PathEl::QuadTo((0.0, 0.0).into(), (1.0, 1.0).into(),), + kurbo::PathEl::QuadTo((2.0, 2.0).into(), (3.0, 3.0).into(),), + kurbo::PathEl::QuadTo((4.0, 4.0).into(), (100.0, 100.0).into(),), + kurbo::PathEl::ClosePath, + ] + ); + + let c2 = Contour::new( + vec![ + ContourPoint::new(0.0, 0.0, PointType::OffCurve, false, None, None, None), + ContourPoint::new(2.0, 2.0, PointType::OffCurve, false, None, None, None), + ContourPoint::new(100.0, 100.0, PointType::QCurve, false, None, None, None), + ], + None, + None, + ); + + assert_eq!( + c2.to_kurbo().unwrap(), + vec![ + kurbo::PathEl::MoveTo((100.0, 100.0).into()), + kurbo::PathEl::QuadTo((0.0, 0.0).into(), (1.0, 1.0).into(),), + kurbo::PathEl::QuadTo((2.0, 2.0).into(), (100.0, 100.0).into(),), + kurbo::PathEl::ClosePath, + ] + ); + + let c3 = Contour::new( + vec![ + ContourPoint::new(0.0, 0.0, PointType::OffCurve, false, None, None, None), + ContourPoint::new(100.0, 100.0, PointType::QCurve, false, None, None, None), + ], + None, + None, + ); + + assert_eq!( + c3.to_kurbo().unwrap(), + vec![ + kurbo::PathEl::MoveTo((100.0, 100.0).into()), + kurbo::PathEl::QuadTo((0.0, 0.0).into(), (100.0, 100.0).into(),), + kurbo::PathEl::ClosePath, + ] + ); + } +} From c72e3eea76278bbe7f180f76afe0694cf4b59d30 Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler Date: Thu, 22 Sep 2022 19:10:42 +0800 Subject: [PATCH 08/10] Use kurbo's midpoint() --- src/glyph/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/glyph/mod.rs b/src/glyph/mod.rs index c4ee11c8..8069efc4 100644 --- a/src/glyph/mod.rs +++ b/src/glyph/mod.rs @@ -411,8 +411,7 @@ impl Contour { [.., cn] => { // Insert a computed on-curve point between each control point. for (c0, c1) in controls.iter().zip(controls.iter().skip(1)) { - let p1 = Point::new(0.5 * (c0.x + c1.x), 0.5 * (c0.y + c1.y)); - segments.push(PathEl::QuadTo(*c0, p1)); + segments.push(PathEl::QuadTo(*c0, c0.midpoint(*c1))); } segments.push(PathEl::QuadTo(cn, p)); controls.clear() From b135fba0365ddf0e0ac11ed342b9ed3e12b77776 Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler Date: Thu, 22 Sep 2022 23:16:18 +0800 Subject: [PATCH 09/10] Guard test behind cfg --- src/glyph/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/glyph/mod.rs b/src/glyph/mod.rs index 8069efc4..c120f4a8 100644 --- a/src/glyph/mod.rs +++ b/src/glyph/mod.rs @@ -900,6 +900,7 @@ impl From for druid::piet::Color { mod tests2 { use super::*; + #[cfg(feature = "kurbo")] #[test] fn many_control_quads() { let c1 = Contour::new( From c15fd4e5ed7647d7cdb1d91db56e37e3e2eb87a3 Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler Date: Fri, 23 Sep 2022 10:57:08 +0800 Subject: [PATCH 10/10] Fix lint --- src/glyph/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/glyph/mod.rs b/src/glyph/mod.rs index c120f4a8..01ad841e 100644 --- a/src/glyph/mod.rs +++ b/src/glyph/mod.rs @@ -896,11 +896,10 @@ impl From for druid::piet::Color { } } -#[cfg(test)] -mod tests2 { +#[cfg(all(test, feature = "kurbo"))] +mod kurbo_tests { use super::*; - #[cfg(feature = "kurbo")] #[test] fn many_control_quads() { let c1 = Contour::new(