diff --git a/geometry/src/outline.rs b/geometry/src/outline.rs index f46b2be4..2cb65a13 100644 --- a/geometry/src/outline.rs +++ b/geometry/src/outline.rs @@ -385,12 +385,15 @@ impl Contour { loop { let mut sweep_vector = end_vector.rev_rotate_by(vector); let last = sweep_vector.0.x() >= -EPSILON && sweep_vector.0.y() >= -EPSILON; + + let mut segment; if !last { sweep_vector = UnitVector(Point2DF::new(0.0, 1.0)); + segment = Segment::quarter_circle_arc(); + } else { + segment = Segment::arc_from_cos(sweep_vector.0.x()); } - // TODO(pcwalton): Cache 90 degree arc segments so we aren't calling this all the time. - let mut segment = Segment::arc_from_cos(sweep_vector.0.x()); let rotation = Transform2DF::from_rotation_vector(sweep_vector.halve_angle().rotate_by(vector)); segment = segment.transform(&rotation.post_mul(&transform)); @@ -413,23 +416,15 @@ impl Contour { } pub fn push_ellipse(&mut self, transform: &Transform2DF) { - let rotations = [ - Transform2DF::default(), - Transform2DF::from_rotation_vector(UnitVector(Point2DF::new( 0.0, 1.0))), - Transform2DF::from_rotation_vector(UnitVector(Point2DF::new(-1.0, 0.0))), - Transform2DF::from_rotation_vector(UnitVector(Point2DF::new( 0.0, -1.0))), - ]; - - let base_segment = Segment::arc_from_cos(0.0); - - for (rotation_index, rotation) in rotations.iter().enumerate() { - let segment = base_segment.transform(&rotation.post_mul(&transform)); - if rotation_index == 0 { - self.push_full_segment(&segment, true); - } else { - self.push_segment(segment, true); - } - } + let segment = Segment::quarter_circle_arc(); + let mut rotation; + self.push_full_segment(&segment.transform(transform), true); + rotation = Transform2DF::from_rotation_vector(UnitVector(Point2DF::new( 0.0, 1.0))); + self.push_segment(segment.transform(&rotation.post_mul(&transform)), true); + rotation = Transform2DF::from_rotation_vector(UnitVector(Point2DF::new(-1.0, 0.0))); + self.push_segment(segment.transform(&rotation.post_mul(&transform)), true); + rotation = Transform2DF::from_rotation_vector(UnitVector(Point2DF::new( 0.0, -1.0))); + self.push_segment(segment.transform(&rotation.post_mul(&transform)), true); } #[inline] diff --git a/geometry/src/segment.rs b/geometry/src/segment.rs index 4ef5620e..d71ee473 100644 --- a/geometry/src/segment.rs +++ b/geometry/src/segment.rs @@ -94,6 +94,15 @@ impl Segment { const K: f32 = 4.0 / 3.0 * (SQRT_2 - 1.0); } + #[inline] + pub fn quarter_circle_arc() -> Segment { + let p0 = Point2DF::splat(SQRT_2 * 0.5); + let p1 = Point2DF::new(-SQRT_2 / 6.0 + 4.0 / 3.0, 7.0 * SQRT_2 / 6.0 - 4.0 / 3.0); + let flip = Point2DF::new(1.0, -1.0); + let (p2, p3) = (p1.scale_xy(flip), p0.scale_xy(flip)); + Segment::cubic(&LineSegmentF::new(p3, p0), &LineSegmentF::new(p2, p1)) + } + #[inline] pub fn as_line_segment(&self) -> LineSegmentF { debug_assert!(self.is_line()); diff --git a/geometry/src/stroke.rs b/geometry/src/stroke.rs index fe70d67d..a3acee91 100644 --- a/geometry/src/stroke.rs +++ b/geometry/src/stroke.rs @@ -122,6 +122,7 @@ impl<'a> OutlineStrokeToFill<'a> { match self.style.line_cap { LineCap::Butt => unreachable!(), + LineCap::Square => { let offset = gradient.scale(width * 0.5); @@ -133,6 +134,7 @@ impl<'a> OutlineStrokeToFill<'a> { contour.push_endpoint(p3); contour.push_endpoint(p4); } + LineCap::Round => { let scale = Point2DF::splat(width * 0.5); let offset = gradient.yx().scale_xy(Point2DF::new(-1.0, 1.0));