Precompute the curve for a quarter-circle arc

This commit is contained in:
Patrick Walton 2019-05-30 21:20:32 -07:00
parent 48f825077c
commit 712c69faf1
3 changed files with 25 additions and 19 deletions

View File

@ -385,12 +385,15 @@ impl Contour {
loop { loop {
let mut sweep_vector = end_vector.rev_rotate_by(vector); let mut sweep_vector = end_vector.rev_rotate_by(vector);
let last = sweep_vector.0.x() >= -EPSILON && sweep_vector.0.y() >= -EPSILON; let last = sweep_vector.0.x() >= -EPSILON && sweep_vector.0.y() >= -EPSILON;
let mut segment;
if !last { if !last {
sweep_vector = UnitVector(Point2DF::new(0.0, 1.0)); 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 = let rotation =
Transform2DF::from_rotation_vector(sweep_vector.halve_angle().rotate_by(vector)); Transform2DF::from_rotation_vector(sweep_vector.halve_angle().rotate_by(vector));
segment = segment.transform(&rotation.post_mul(&transform)); segment = segment.transform(&rotation.post_mul(&transform));
@ -413,23 +416,15 @@ impl Contour {
} }
pub fn push_ellipse(&mut self, transform: &Transform2DF) { pub fn push_ellipse(&mut self, transform: &Transform2DF) {
let rotations = [ let segment = Segment::quarter_circle_arc();
Transform2DF::default(), let mut rotation;
Transform2DF::from_rotation_vector(UnitVector(Point2DF::new( 0.0, 1.0))), self.push_full_segment(&segment.transform(transform), true);
Transform2DF::from_rotation_vector(UnitVector(Point2DF::new(-1.0, 0.0))), rotation = Transform2DF::from_rotation_vector(UnitVector(Point2DF::new( 0.0, 1.0)));
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);
let base_segment = Segment::arc_from_cos(0.0); rotation = Transform2DF::from_rotation_vector(UnitVector(Point2DF::new( 0.0, -1.0)));
self.push_segment(segment.transform(&rotation.post_mul(&transform)), true);
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);
}
}
} }
#[inline] #[inline]

View File

@ -94,6 +94,15 @@ impl Segment {
const K: f32 = 4.0 / 3.0 * (SQRT_2 - 1.0); 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] #[inline]
pub fn as_line_segment(&self) -> LineSegmentF { pub fn as_line_segment(&self) -> LineSegmentF {
debug_assert!(self.is_line()); debug_assert!(self.is_line());

View File

@ -122,6 +122,7 @@ impl<'a> OutlineStrokeToFill<'a> {
match self.style.line_cap { match self.style.line_cap {
LineCap::Butt => unreachable!(), LineCap::Butt => unreachable!(),
LineCap::Square => { LineCap::Square => {
let offset = gradient.scale(width * 0.5); let offset = gradient.scale(width * 0.5);
@ -133,6 +134,7 @@ impl<'a> OutlineStrokeToFill<'a> {
contour.push_endpoint(p3); contour.push_endpoint(p3);
contour.push_endpoint(p4); contour.push_endpoint(p4);
} }
LineCap::Round => { LineCap::Round => {
let scale = Point2DF::splat(width * 0.5); let scale = Point2DF::splat(width * 0.5);
let offset = gradient.yx().scale_xy(Point2DF::new(-1.0, 1.0)); let offset = gradient.yx().scale_xy(Point2DF::new(-1.0, 1.0));