From ce127caf76ff9e8b3289257eff74292fa357870a Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 30 May 2019 10:56:19 -0700 Subject: [PATCH] Fix drawing of full circles --- geometry/src/outline.rs | 46 +++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/geometry/src/outline.rs b/geometry/src/outline.rs index 3b598e7c..27540cd2 100644 --- a/geometry/src/outline.rs +++ b/geometry/src/outline.rs @@ -369,28 +369,25 @@ impl Contour { } pub fn push_arc(&mut self, center: Point2DF, radius: f32, start_angle: f32, end_angle: f32) { + if end_angle - start_angle >= PI * 2.0 { + return self.push_circle(center, radius); + } + let start = Point2DF::new(f32::cos(start_angle), f32::sin(start_angle)); let end = Point2DF::new(f32::cos(end_angle), f32::sin(end_angle)); let chord = LineSegmentF::new(start, end).scale(radius).translate(center); - let full_circle = end_angle - start_angle >= PI * 2.0; - self.push_arc_from_chord_full(radius, chord, full_circle); + self.push_arc_from_chord(radius, chord); } #[inline] - pub fn push_arc_from_chord(&mut self, radius: f32, chord: LineSegmentF) { - self.push_arc_from_chord_full(radius, chord, false); - } - - fn push_arc_from_chord_full(&mut self, - radius: f32, - mut chord: LineSegmentF, - full_circle: bool) { + pub fn push_arc_from_chord(&mut self, radius: f32, mut chord: LineSegmentF) { let chord_length = chord.vector().length(); let radius_minus_sagitta_sq = radius * radius - 0.25 * chord_length * chord_length; let radius_minus_sagitta = f32::sqrt(f32::max(0.0, radius_minus_sagitta_sq)); + let scale_factor = radius_minus_sagitta / chord_length; let center = chord.midpoint() + chord.vector().yx().scale_xy(Point2DF::new(-scale_factor, - scale_factor)); + scale_factor)); chord = chord.translate(-center).scale(radius.recip()); let (mut vector, end_vector) = (UnitVector(chord.from()), UnitVector(chord.to())); @@ -401,12 +398,12 @@ impl Contour { let mut first_segment = true; loop { let mut sweep_vector = end_vector.rev_rotate_by(vector); - let last = !(full_circle && first_segment) && - sweep_vector.0.x() >= -EPSILON && sweep_vector.0.y() >= -EPSILON; + let last = sweep_vector.0.x() >= -EPSILON && sweep_vector.0.y() >= -EPSILON; if !last { sweep_vector = UnitVector(Point2DF::new(0.0, 1.0)); } + // 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)); @@ -429,6 +426,29 @@ impl Contour { const EPSILON: f32 = 0.001; } + fn push_circle(&mut self, center: Point2DF, radius: f32) { + let scale = Transform2DF::from_scale(Point2DF::splat(radius)); + let translation = Transform2DF::from_translation(center); + + 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(&scale.post_mul(rotation).post_mul(&translation)); + if rotation_index == 0 { + self.push_full_segment(&segment, true); + } else { + self.push_segment(segment, true); + } + } + } + #[inline] pub fn segment_after(&self, point_index: u32) -> Segment { debug_assert!(self.point_is_endpoint(point_index));