Base the core arc primitive on a unit chord and transform.
This lets us handle ellipses better.
This commit is contained in:
parent
607a518544
commit
9756aa89f9
|
@ -311,7 +311,9 @@ 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) {
|
||||||
self.current_contour.push_arc(center, radius, start_angle, end_angle);
|
let mut transform = Transform2DF::from_scale(Point2DF::splat(radius));
|
||||||
|
transform = transform.post_mul(&Transform2DF::from_translation(center));
|
||||||
|
self.current_contour.push_arc(&transform, start_angle, end_angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rect(&mut self, rect: RectF) {
|
pub fn rect(&mut self, rect: RectF) {
|
||||||
|
|
|
@ -368,34 +368,20 @@ 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, center: Point2DF, radius: f32, start_angle: f32, end_angle: f32) {
|
pub fn push_arc(&mut self, transform: &Transform2DF, start_angle: f32, end_angle: f32) {
|
||||||
if end_angle - start_angle >= PI * 2.0 {
|
if end_angle - start_angle >= PI * 2.0 {
|
||||||
return self.push_circle(center, radius);
|
self.push_ellipse(transform);
|
||||||
|
} else {
|
||||||
|
let start = Point2DF::new(f32::cos(start_angle), f32::sin(start_angle));
|
||||||
|
let end = Point2DF::new(f32::cos(end_angle), f32::sin(end_angle));
|
||||||
|
self.push_arc_from_unit_chord(transform, LineSegmentF::new(start, end));
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
self.push_arc_from_chord(radius, chord);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
pub fn push_arc_from_unit_chord(&mut self, transform: &Transform2DF, chord: LineSegmentF) {
|
||||||
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));
|
|
||||||
|
|
||||||
chord = chord.translate(-center).scale(radius.recip());
|
|
||||||
let (mut vector, end_vector) = (UnitVector(chord.from()), UnitVector(chord.to()));
|
let (mut vector, end_vector) = (UnitVector(chord.from()), UnitVector(chord.to()));
|
||||||
|
|
||||||
let scale = Transform2DF::from_scale(Point2DF::splat(radius));
|
|
||||||
let translation = Transform2DF::from_translation(center);
|
|
||||||
|
|
||||||
let mut first_segment = true;
|
let mut first_segment = true;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -407,7 +393,7 @@ impl Contour {
|
||||||
let mut segment = Segment::arc_from_cos(sweep_vector.0.x());
|
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(&scale.post_mul(&rotation).post_mul(&translation));
|
segment = segment.transform(&rotation.post_mul(&transform));
|
||||||
|
|
||||||
if first_segment {
|
if first_segment {
|
||||||
self.push_full_segment(&segment, true);
|
self.push_full_segment(&segment, true);
|
||||||
|
@ -426,10 +412,7 @@ impl Contour {
|
||||||
const EPSILON: f32 = 0.001;
|
const EPSILON: f32 = 0.001;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_circle(&mut self, center: Point2DF, radius: f32) {
|
pub fn push_ellipse(&mut self, transform: &Transform2DF) {
|
||||||
let scale = Transform2DF::from_scale(Point2DF::splat(radius));
|
|
||||||
let translation = Transform2DF::from_translation(center);
|
|
||||||
|
|
||||||
let rotations = [
|
let rotations = [
|
||||||
Transform2DF::default(),
|
Transform2DF::default(),
|
||||||
Transform2DF::from_rotation_vector(UnitVector(Point2DF::new( 0.0, 1.0))),
|
Transform2DF::from_rotation_vector(UnitVector(Point2DF::new( 0.0, 1.0))),
|
||||||
|
@ -440,7 +423,7 @@ impl Contour {
|
||||||
let base_segment = Segment::arc_from_cos(0.0);
|
let base_segment = Segment::arc_from_cos(0.0);
|
||||||
|
|
||||||
for (rotation_index, rotation) in rotations.iter().enumerate() {
|
for (rotation_index, rotation) in rotations.iter().enumerate() {
|
||||||
let segment = base_segment.transform(&scale.post_mul(rotation).post_mul(&translation));
|
let segment = base_segment.transform(&rotation.post_mul(&transform));
|
||||||
if rotation_index == 0 {
|
if rotation_index == 0 {
|
||||||
self.push_full_segment(&segment, true);
|
self.push_full_segment(&segment, true);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
use crate::basic::line_segment::LineSegmentF;
|
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::outline::{Contour, Outline};
|
use crate::outline::{Contour, Outline};
|
||||||
use crate::segment::Segment;
|
use crate::segment::Segment;
|
||||||
use std::f32;
|
use std::f32;
|
||||||
|
@ -133,9 +134,12 @@ impl<'a> OutlineStrokeToFill<'a> {
|
||||||
contour.push_endpoint(p4);
|
contour.push_endpoint(p4);
|
||||||
}
|
}
|
||||||
LineCap::Round => {
|
LineCap::Round => {
|
||||||
let offset = gradient.yx().scale_xy(Point2DF::new(-width, width));
|
let scale = Point2DF::splat(width * 0.5);
|
||||||
let chord = LineSegmentF::new(p1, p1 + offset);
|
let offset = gradient.yx().scale_xy(Point2DF::new(-1.0, 1.0));
|
||||||
contour.push_arc_from_chord(width * 0.5, chord);
|
let mut transform = Transform2DF::from_scale(scale);
|
||||||
|
let translation = p1 + offset.scale(width * 0.5);
|
||||||
|
transform = transform.post_mul(&Transform2DF::from_translation(translation));
|
||||||
|
contour.push_arc_from_unit_chord(&transform, LineSegmentF::new(-offset, offset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -365,8 +369,12 @@ impl Contour {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LineJoin::Round => {
|
LineJoin::Round => {
|
||||||
self.push_arc_from_chord(distance.abs(),
|
let scale = Point2DF::splat(distance.abs());
|
||||||
LineSegmentF::new(prev_tangent.to(), next_tangent.to()));
|
let mut transform = Transform2DF::from_scale(scale);
|
||||||
|
transform = transform.post_mul(&Transform2DF::from_translation(join_point));
|
||||||
|
let chord_from = (prev_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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue