Add support for counterclockwise winding to arcs

This commit is contained in:
Patrick Walton 2019-06-01 12:11:47 -07:00
parent 99c1cca05c
commit 678b6f12c7
4 changed files with 49 additions and 15 deletions

View File

@ -19,7 +19,7 @@ use pathfinder_geometry::basic::point::Point2DF;
use pathfinder_geometry::basic::rect::RectF; use pathfinder_geometry::basic::rect::RectF;
use pathfinder_geometry::basic::transform2d::Transform2DF; use pathfinder_geometry::basic::transform2d::Transform2DF;
use pathfinder_geometry::color::ColorU; use pathfinder_geometry::color::ColorU;
use pathfinder_geometry::outline::{Contour, Outline}; use pathfinder_geometry::outline::{ArcDirection, Contour, Outline};
use pathfinder_geometry::stroke::{LineCap, LineJoin as StrokeLineJoin}; use pathfinder_geometry::stroke::{LineCap, LineJoin as StrokeLineJoin};
use pathfinder_geometry::stroke::{OutlineStrokeToFill, StrokeStyle}; use pathfinder_geometry::stroke::{OutlineStrokeToFill, StrokeStyle};
use pathfinder_renderer::paint::{Paint, PaintId}; use pathfinder_renderer::paint::{Paint, PaintId};
@ -246,7 +246,7 @@ impl CanvasRenderingContext2D {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct State { struct State {
transform: Transform2DF, transform: Transform2DF,
font_collection: Arc<FontCollection>, font_collection: Arc<FontCollection>,
font_size: f32, font_size: f32,
@ -336,10 +336,15 @@ impl Path2D {
} }
#[inline] #[inline]
pub fn arc(&mut self, center: Point2DF, radius: f32, start_angle: f32, end_angle: f32) { pub fn arc(&mut self,
center: Point2DF,
radius: f32,
start_angle: f32,
end_angle: f32,
direction: ArcDirection) {
let mut transform = Transform2DF::from_scale(Point2DF::splat(radius)); let mut transform = Transform2DF::from_scale(Point2DF::splat(radius));
transform = transform.post_mul(&Transform2DF::from_translation(center)); transform = transform.post_mul(&Transform2DF::from_translation(center));
self.current_contour.push_arc(&transform, start_angle, end_angle); self.current_contour.push_arc(&transform, start_angle, end_angle, direction);
} }
#[inline] #[inline]
@ -357,7 +362,9 @@ impl Path2D {
let chord = LineSegmentF::new(vu0.yx().scale_xy(Point2DF::new(-1.0, 1.0)), let chord = LineSegmentF::new(vu0.yx().scale_xy(Point2DF::new(-1.0, 1.0)),
vu1.yx().scale_xy(Point2DF::new(1.0, -1.0))); vu1.yx().scale_xy(Point2DF::new(1.0, -1.0)));
self.current_contour.push_arc_from_unit_chord(&transform, chord);
// FIXME(pcwalton): Is clockwise direction correct?
self.current_contour.push_arc_from_unit_chord(&transform, chord, ArcDirection::CW);
} }
pub fn rect(&mut self, rect: RectF) { pub fn rect(&mut self, rect: RectF) {
@ -380,7 +387,7 @@ impl Path2D {
let mut transform = Transform2DF::from_rotation(rotation); let mut transform = Transform2DF::from_rotation(rotation);
transform = transform.post_mul(&Transform2DF::from_scale(axes)); transform = transform.post_mul(&Transform2DF::from_scale(axes));
transform = transform.post_mul(&Transform2DF::from_translation(center)); transform = transform.post_mul(&Transform2DF::from_translation(center));
self.current_contour.push_arc(&transform, start_angle, end_angle); self.current_contour.push_arc(&transform, start_angle, end_angle, ArcDirection::CW);
if end_angle - start_angle >= 2.0 * PI { if end_angle - start_angle >= 2.0 * PI {
self.current_contour.close(); self.current_contour.close();

View File

@ -97,6 +97,11 @@ impl LineSegmentF {
LineSegmentF(self.0 * F32x4::splat(factor)) LineSegmentF(self.0 * F32x4::splat(factor))
} }
#[inline]
pub fn scale_xy(&self, factors: Point2DF) -> LineSegmentF {
LineSegmentF(self.0 * factors.0.xyxy())
}
#[inline] #[inline]
pub fn split(&self, t: f32) -> (LineSegmentF, LineSegmentF) { pub fn split(&self, t: f32) -> (LineSegmentF, LineSegmentF) {
debug_assert!(t >= 0.0 && t <= 1.0); debug_assert!(t >= 0.0 && t <= 1.0);

View File

@ -352,17 +352,30 @@ impl Contour {
self.push_point(segment.baseline.to(), PointFlags::empty(), update_bounds); self.push_point(segment.baseline.to(), PointFlags::empty(), update_bounds);
} }
pub fn push_arc(&mut self, transform: &Transform2DF, start_angle: f32, end_angle: f32) { pub fn push_arc(&mut self,
transform: &Transform2DF,
start_angle: f32,
end_angle: f32,
direction: ArcDirection) {
if end_angle - start_angle >= PI * 2.0 { if end_angle - start_angle >= PI * 2.0 {
self.push_ellipse(transform); self.push_ellipse(transform);
} else { } else {
let start = Point2DF::new(f32::cos(start_angle), f32::sin(start_angle)); 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 end = Point2DF::new(f32::cos(end_angle), f32::sin(end_angle));
self.push_arc_from_unit_chord(transform, LineSegmentF::new(start, end)); self.push_arc_from_unit_chord(transform, LineSegmentF::new(start, end), direction);
} }
} }
pub fn push_arc_from_unit_chord(&mut self, transform: &Transform2DF, chord: LineSegmentF) { pub fn push_arc_from_unit_chord(&mut self,
transform: &Transform2DF,
mut chord: LineSegmentF,
direction: ArcDirection) {
let mut direction_transform = Transform2DF::default();
if direction == ArcDirection::CCW {
chord = chord.scale_xy(Point2DF::new(-1.0, 1.0));
direction_transform = Transform2DF::from_scale(Point2DF::new(-1.0, 1.0));
}
let (mut vector, end_vector) = (UnitVector(chord.from()), UnitVector(chord.to())); let (mut vector, end_vector) = (UnitVector(chord.from()), UnitVector(chord.to()));
let mut first_segment = true; let mut first_segment = true;
@ -378,9 +391,10 @@ impl Contour {
segment = Segment::arc_from_cos(sweep_vector.0.x()); segment = Segment::arc_from_cos(sweep_vector.0.x());
} }
let rotation = let half_sweep_vector = sweep_vector.halve_angle();
Transform2DF::from_rotation_vector(sweep_vector.halve_angle().rotate_by(vector)); let rotation = Transform2DF::from_rotation_vector(half_sweep_vector.rotate_by(vector));
segment = segment.transform(&rotation.post_mul(&transform)); segment = segment.transform(&direction_transform.post_mul(&rotation)
.post_mul(&transform));
let mut push_segment_flags = PushSegmentFlags::UPDATE_BOUNDS; let mut push_segment_flags = PushSegmentFlags::UPDATE_BOUNDS;
if first_segment { if first_segment {
@ -816,6 +830,12 @@ impl<'a> Iterator for ContourIter<'a> {
} }
} }
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ArcDirection {
CW,
CCW,
}
#[inline] #[inline]
pub(crate) fn union_rect(bounds: &mut RectF, new_point: Point2DF, first: bool) { pub(crate) fn union_rect(bounds: &mut RectF, new_point: Point2DF, first: bool) {
if first { if first {

View File

@ -14,7 +14,7 @@ use crate::basic::line_segment::LineSegmentF;
use crate::basic::point::Point2DF; use crate::basic::point::Point2DF;
use crate::basic::rect::RectF; use crate::basic::rect::RectF;
use crate::basic::transform2d::Transform2DF; use crate::basic::transform2d::Transform2DF;
use crate::outline::{Contour, Outline, PushSegmentFlags}; use crate::outline::{ArcDirection, Contour, Outline, PushSegmentFlags};
use crate::segment::Segment; use crate::segment::Segment;
use std::f32; use std::f32;
@ -141,7 +141,8 @@ impl<'a> OutlineStrokeToFill<'a> {
let mut transform = Transform2DF::from_scale(scale); let mut transform = Transform2DF::from_scale(scale);
let translation = p1 + offset.scale(width * 0.5); let translation = p1 + offset.scale(width * 0.5);
transform = transform.post_mul(&Transform2DF::from_translation(translation)); transform = transform.post_mul(&Transform2DF::from_translation(translation));
contour.push_arc_from_unit_chord(&transform, LineSegmentF::new(-offset, offset)); let chord = LineSegmentF::new(-offset, offset);
contour.push_arc_from_unit_chord(&transform, chord, ArcDirection::CW);
} }
} }
} }
@ -377,7 +378,8 @@ impl Contour {
transform = transform.post_mul(&Transform2DF::from_translation(join_point)); transform = transform.post_mul(&Transform2DF::from_translation(join_point));
let chord_from = (prev_tangent.to() - join_point).normalize(); let chord_from = (prev_tangent.to() - join_point).normalize();
let chord_to = (next_tangent.to() - join_point).normalize(); let chord_to = (next_tangent.to() - join_point).normalize();
self.push_arc_from_unit_chord(&transform, LineSegmentF::new(chord_from, chord_to)); let chord = LineSegmentF::new(chord_from, chord_to);
self.push_arc_from_unit_chord(&transform, chord, ArcDirection::CW);
} }
} }
} }