Add arc building methods and switch the Moiré demo to use them.
Also, stop taking points by reference in many methods, for consistency.
This commit is contained in:
parent
55be787ffd
commit
f24d93819b
|
@ -180,6 +180,7 @@ dependencies = [
|
||||||
"pathfinder_gl 0.1.0",
|
"pathfinder_gl 0.1.0",
|
||||||
"pathfinder_gpu 0.1.0",
|
"pathfinder_gpu 0.1.0",
|
||||||
"pathfinder_renderer 0.1.0",
|
"pathfinder_renderer 0.1.0",
|
||||||
|
"pretty_env_logger 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sdl2 0.32.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"sdl2 0.32.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sdl2-sys 0.32.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"sdl2-sys 0.32.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
|
@ -97,7 +97,7 @@ impl CanvasRenderingContext2D {
|
||||||
drop(self.scene.push_text(string,
|
drop(self.scene.push_text(string,
|
||||||
&TextStyle { size: self.current_state.font_size },
|
&TextStyle { size: self.current_state.font_size },
|
||||||
&self.current_state.font_collection,
|
&self.current_state.font_collection,
|
||||||
&Transform2DF32::from_translation(&position),
|
&Transform2DF32::from_translation(position),
|
||||||
TextRenderMode::Fill,
|
TextRenderMode::Fill,
|
||||||
HintingOptions::None,
|
HintingOptions::None,
|
||||||
paint_id));
|
paint_id));
|
||||||
|
@ -109,7 +109,7 @@ impl CanvasRenderingContext2D {
|
||||||
drop(self.scene.push_text(string,
|
drop(self.scene.push_text(string,
|
||||||
&TextStyle { size: self.current_state.font_size },
|
&TextStyle { size: self.current_state.font_size },
|
||||||
&self.current_state.font_collection,
|
&self.current_state.font_collection,
|
||||||
&Transform2DF32::from_translation(&position),
|
&Transform2DF32::from_translation(position),
|
||||||
TextRenderMode::Stroke(self.current_state.line_width),
|
TextRenderMode::Stroke(self.current_state.line_width),
|
||||||
HintingOptions::None,
|
HintingOptions::None,
|
||||||
paint_id));
|
paint_id));
|
||||||
|
@ -196,7 +196,7 @@ pub struct Path2D {
|
||||||
current_contour: Contour,
|
current_contour: Contour,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(pcwalton): `arc`, `ellipse`
|
// TODO(pcwalton): `ellipse`
|
||||||
impl Path2D {
|
impl Path2D {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new() -> Path2D {
|
pub fn new() -> Path2D {
|
||||||
|
@ -230,6 +230,11 @@ impl Path2D {
|
||||||
self.current_contour.push_cubic(ctrl0, ctrl1, to);
|
self.current_contour.push_cubic(ctrl0, ctrl1, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn arc(&mut self, center: Point2DF32, radius: f32, start_angle: f32, end_angle: f32) {
|
||||||
|
self.current_contour.push_arc(center, radius, start_angle, end_angle);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn rect(&mut self, rect: RectF32) {
|
pub fn rect(&mut self, rect: RectF32) {
|
||||||
self.flush_current_contour();
|
self.flush_current_contour();
|
||||||
self.current_contour.push_endpoint(rect.origin());
|
self.current_contour.push_endpoint(rect.origin());
|
||||||
|
|
|
@ -6,6 +6,7 @@ edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gl = "0.6"
|
gl = "0.6"
|
||||||
|
pretty_env_logger = "0.3"
|
||||||
sdl2 = "0.32"
|
sdl2 = "0.32"
|
||||||
sdl2-sys = "0.32"
|
sdl2-sys = "0.32"
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ use pathfinder_renderer::options::RenderOptions;
|
||||||
use sdl2::event::Event;
|
use sdl2::event::Event;
|
||||||
use sdl2::keyboard::Keycode;
|
use sdl2::keyboard::Keycode;
|
||||||
use sdl2::video::GLProfile;
|
use sdl2::video::GLProfile;
|
||||||
|
use std::f32::consts::PI;
|
||||||
use std::f32;
|
use std::f32;
|
||||||
|
|
||||||
const VELOCITY: f32 = 0.02;
|
const VELOCITY: f32 = 0.02;
|
||||||
|
@ -36,6 +37,8 @@ const CIRCLE_THICKNESS: f32 = 16.0;
|
||||||
const COLOR_CYCLE_SPEED: f32 = 0.0025;
|
const COLOR_CYCLE_SPEED: f32 = 0.0025;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
pretty_env_logger::init();
|
||||||
|
|
||||||
// Set up SDL2.
|
// Set up SDL2.
|
||||||
let sdl_context = sdl2::init().unwrap();
|
let sdl_context = sdl2::init().unwrap();
|
||||||
let video = sdl_context.video().unwrap();
|
let video = sdl_context.video().unwrap();
|
||||||
|
@ -150,27 +153,14 @@ impl MoireRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_circles(&self, canvas: &mut CanvasRenderingContext2D, center: Point2DF32) {
|
fn draw_circles(&self, canvas: &mut CanvasRenderingContext2D, center: Point2DF32) {
|
||||||
|
let center = center.scale(self.device_pixel_ratio);
|
||||||
for index in 0..CIRCLE_COUNT {
|
for index in 0..CIRCLE_COUNT {
|
||||||
|
let radius = (index + 1) as f32 * CIRCLE_SPACING * self.device_pixel_ratio;
|
||||||
let mut path = Path2D::new();
|
let mut path = Path2D::new();
|
||||||
self.add_circle_subpath(&mut path,
|
path.arc(center, radius, 0.0, PI * 2.0);
|
||||||
center.scale(self.device_pixel_ratio),
|
|
||||||
index as f32 * CIRCLE_SPACING * self.device_pixel_ratio);
|
|
||||||
canvas.stroke_path(path);
|
canvas.stroke_path(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_circle_subpath(&self, path: &mut Path2D, center: Point2DF32, radius: f32) {
|
|
||||||
path.move_to(center + Point2DF32::new(0.0, -radius));
|
|
||||||
path.quadratic_curve_to(center + Point2DF32::new(radius, -radius),
|
|
||||||
center + Point2DF32::new(radius, 0.0));
|
|
||||||
path.quadratic_curve_to(center + Point2DF32::new(radius, radius),
|
|
||||||
center + Point2DF32::new(0.0, radius));
|
|
||||||
path.quadratic_curve_to(center + Point2DF32::new(-radius, radius),
|
|
||||||
center + Point2DF32::new(-radius, 0.0));
|
|
||||||
path.quadratic_curve_to(center + Point2DF32::new(-radius, -radius),
|
|
||||||
center + Point2DF32::new(0.0, -radius));
|
|
||||||
path.close_path();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ColorGradient([ColorF; 5]);
|
struct ColorGradient([ColorF; 5]);
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub struct LineSegmentF32(pub F32x4);
|
||||||
|
|
||||||
impl LineSegmentF32 {
|
impl LineSegmentF32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(from: &Point2DF32, to: &Point2DF32) -> LineSegmentF32 {
|
pub fn new(from: Point2DF32, to: Point2DF32) -> LineSegmentF32 {
|
||||||
LineSegmentF32(from.0.concat_xy_xy(to.0))
|
LineSegmentF32(from.0.concat_xy_xy(to.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
//! 2D affine transforms.
|
//! 2D affine transforms.
|
||||||
|
|
||||||
|
use crate::basic::line_segment::LineSegmentF32;
|
||||||
use crate::basic::point::Point2DF32;
|
use crate::basic::point::Point2DF32;
|
||||||
use crate::basic::rect::RectF32;
|
use crate::basic::rect::RectF32;
|
||||||
use crate::basic::transform3d::Transform3DF32;
|
use crate::basic::transform3d::Transform3DF32;
|
||||||
|
@ -24,13 +25,13 @@ pub struct Matrix2x2F32(pub F32x4);
|
||||||
impl Default for Matrix2x2F32 {
|
impl Default for Matrix2x2F32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn default() -> Matrix2x2F32 {
|
fn default() -> Matrix2x2F32 {
|
||||||
Self::from_scale(&Point2DF32::splat(1.0))
|
Self::from_scale(Point2DF32::splat(1.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Matrix2x2F32 {
|
impl Matrix2x2F32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_scale(scale: &Point2DF32) -> Matrix2x2F32 {
|
pub fn from_scale(scale: Point2DF32) -> Matrix2x2F32 {
|
||||||
Matrix2x2F32(F32x4::new(scale.x(), 0.0, 0.0, scale.y()))
|
Matrix2x2F32(F32x4::new(scale.x(), 0.0, 0.0, scale.y()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +67,7 @@ impl Matrix2x2F32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn transform_point(&self, point: &Point2DF32) -> Point2DF32 {
|
pub fn transform_point(&self, point: Point2DF32) -> Point2DF32 {
|
||||||
let halves = self.0 * point.0.xxyy();
|
let halves = self.0 * point.0.xxyy();
|
||||||
Point2DF32(halves + halves.zwzw())
|
Point2DF32(halves + halves.zwzw())
|
||||||
}
|
}
|
||||||
|
@ -118,13 +119,13 @@ pub struct Transform2DF32 {
|
||||||
impl Default for Transform2DF32 {
|
impl Default for Transform2DF32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn default() -> Transform2DF32 {
|
fn default() -> Transform2DF32 {
|
||||||
Self::from_scale(&Point2DF32::splat(1.0))
|
Self::from_scale(Point2DF32::splat(1.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transform2DF32 {
|
impl Transform2DF32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_scale(scale: &Point2DF32) -> Transform2DF32 {
|
pub fn from_scale(scale: Point2DF32) -> Transform2DF32 {
|
||||||
Transform2DF32 {
|
Transform2DF32 {
|
||||||
matrix: Matrix2x2F32::from_scale(scale),
|
matrix: Matrix2x2F32::from_scale(scale),
|
||||||
vector: Point2DF32::default(),
|
vector: Point2DF32::default(),
|
||||||
|
@ -140,11 +141,8 @@ impl Transform2DF32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_translation(vector: &Point2DF32) -> Transform2DF32 {
|
pub fn from_translation(vector: Point2DF32) -> Transform2DF32 {
|
||||||
Transform2DF32 {
|
Transform2DF32 { matrix: Matrix2x2F32::default(), vector }
|
||||||
matrix: Matrix2x2F32::default(),
|
|
||||||
vector: *vector,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -154,10 +152,8 @@ impl Transform2DF32 {
|
||||||
translation: Point2DF32,
|
translation: Point2DF32,
|
||||||
) -> Transform2DF32 {
|
) -> Transform2DF32 {
|
||||||
let rotation = Transform2DF32::from_rotation(theta);
|
let rotation = Transform2DF32::from_rotation(theta);
|
||||||
let translation = Transform2DF32::from_translation(&translation);
|
let translation = Transform2DF32::from_translation(translation);
|
||||||
Transform2DF32::from_scale(&scale)
|
Transform2DF32::from_scale(scale).post_mul(&rotation).post_mul(&translation)
|
||||||
.post_mul(&rotation)
|
|
||||||
.post_mul(&translation)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -169,16 +165,22 @@ impl Transform2DF32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn transform_point(&self, point: &Point2DF32) -> Point2DF32 {
|
pub fn transform_point(&self, point: Point2DF32) -> Point2DF32 {
|
||||||
self.matrix.transform_point(point) + self.vector
|
self.matrix.transform_point(point) + self.vector
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn transform_line_segment(&self, line_segment: &LineSegmentF32) -> LineSegmentF32 {
|
||||||
|
LineSegmentF32::new(self.transform_point(line_segment.from()),
|
||||||
|
self.transform_point(line_segment.to()))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn transform_rect(&self, rect: &RectF32) -> RectF32 {
|
pub fn transform_rect(&self, rect: &RectF32) -> RectF32 {
|
||||||
let upper_left = self.transform_point(&rect.origin());
|
let upper_left = self.transform_point(rect.origin());
|
||||||
let upper_right = self.transform_point(&rect.upper_right());
|
let upper_right = self.transform_point(rect.upper_right());
|
||||||
let lower_left = self.transform_point(&rect.lower_left());
|
let lower_left = self.transform_point(rect.lower_left());
|
||||||
let lower_right = self.transform_point(&rect.lower_right());
|
let lower_right = self.transform_point(rect.lower_right());
|
||||||
let min_point = upper_left.min(upper_right).min(lower_left).min(lower_right);
|
let min_point = upper_left.min(upper_right).min(lower_left).min(lower_right);
|
||||||
let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right);
|
let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right);
|
||||||
RectF32::from_points(min_point, max_point)
|
RectF32::from_points(min_point, max_point)
|
||||||
|
@ -187,7 +189,7 @@ impl Transform2DF32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn post_mul(&self, other: &Transform2DF32) -> Transform2DF32 {
|
pub fn post_mul(&self, other: &Transform2DF32) -> Transform2DF32 {
|
||||||
let matrix = self.matrix.post_mul(&other.matrix);
|
let matrix = self.matrix.post_mul(&other.matrix);
|
||||||
let vector = other.transform_point(&self.vector);
|
let vector = other.transform_point(self.vector);
|
||||||
Transform2DF32 { matrix, vector }
|
Transform2DF32 { matrix, vector }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,7 +245,7 @@ impl Transform2DF32 {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn post_translate(&self, vector: Point2DF32) -> Transform2DF32 {
|
pub fn post_translate(&self, vector: Point2DF32) -> Transform2DF32 {
|
||||||
self.post_mul(&Transform2DF32::from_translation(&vector))
|
self.post_mul(&Transform2DF32::from_translation(vector))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -253,7 +255,7 @@ impl Transform2DF32 {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn post_scale(&self, scale: Point2DF32) -> Transform2DF32 {
|
pub fn post_scale(&self, scale: Point2DF32) -> Transform2DF32 {
|
||||||
self.post_mul(&Transform2DF32::from_scale(&scale))
|
self.post_mul(&Transform2DF32::from_scale(scale))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the translation part of this matrix.
|
/// Returns the translation part of this matrix.
|
||||||
|
@ -303,18 +305,18 @@ where
|
||||||
if !segment.is_none() {
|
if !segment.is_none() {
|
||||||
segment
|
segment
|
||||||
.baseline
|
.baseline
|
||||||
.set_from(&self.transform.transform_point(&segment.baseline.from()));
|
.set_from(&self.transform.transform_point(segment.baseline.from()));
|
||||||
segment
|
segment
|
||||||
.baseline
|
.baseline
|
||||||
.set_to(&self.transform.transform_point(&segment.baseline.to()));
|
.set_to(&self.transform.transform_point(segment.baseline.to()));
|
||||||
if !segment.is_line() {
|
if !segment.is_line() {
|
||||||
segment
|
segment
|
||||||
.ctrl
|
.ctrl
|
||||||
.set_from(&self.transform.transform_point(&segment.ctrl.from()));
|
.set_from(&self.transform.transform_point(segment.ctrl.from()));
|
||||||
if !segment.is_quadratic() {
|
if !segment.is_quadratic() {
|
||||||
segment
|
segment
|
||||||
.ctrl
|
.ctrl
|
||||||
.set_to(&self.transform.transform_point(&segment.ctrl.to()));
|
.set_to(&self.transform.transform_point(segment.ctrl.to()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -325,7 +325,7 @@ impl ContourPolygonClipper {
|
||||||
Some(prev) => *prev,
|
Some(prev) => *prev,
|
||||||
};
|
};
|
||||||
for &next in &clip_polygon {
|
for &next in &clip_polygon {
|
||||||
self.clip_against(Edge(LineSegmentF32::new(&prev, &next)));
|
self.clip_against(Edge(LineSegmentF32::new(prev, next)));
|
||||||
prev = next;
|
prev = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ use crate::clip::{self, ContourPolygonClipper, ContourRectClipper};
|
||||||
use crate::dilation::ContourDilator;
|
use crate::dilation::ContourDilator;
|
||||||
use crate::orientation::Orientation;
|
use crate::orientation::Orientation;
|
||||||
use crate::segment::{Segment, SegmentFlags, SegmentKind};
|
use crate::segment::{Segment, SegmentFlags, SegmentKind};
|
||||||
|
use std::f32::consts::FRAC_PI_2;
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
@ -292,7 +293,10 @@ impl Contour {
|
||||||
|
|
||||||
// TODO(pcwalton): SIMD.
|
// TODO(pcwalton): SIMD.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_point(&mut self, point: Point2DF32, flags: PointFlags, update_bounds: bool) {
|
pub(crate) fn push_point(&mut self,
|
||||||
|
point: Point2DF32,
|
||||||
|
flags: PointFlags,
|
||||||
|
update_bounds: bool) {
|
||||||
debug_assert!(!point.x().is_nan() && !point.y().is_nan());
|
debug_assert!(!point.x().is_nan() && !point.y().is_nan());
|
||||||
|
|
||||||
if update_bounds {
|
if update_bounds {
|
||||||
|
@ -354,6 +358,37 @@ 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: Point2DF32, radius: f32, start_angle: f32, end_angle: f32) {
|
||||||
|
let scale = Transform2DF32::from_scale(Point2DF32::splat(radius));
|
||||||
|
let translation = Transform2DF32::from_translation(center);
|
||||||
|
|
||||||
|
let (mut angle, mut first_segment) = (start_angle, true);
|
||||||
|
while angle < end_angle {
|
||||||
|
let sweep_angle = f32::min(FRAC_PI_2, end_angle - angle);
|
||||||
|
let mut segment = Segment::arc(sweep_angle);
|
||||||
|
let rotation = Transform2DF32::from_rotation(angle);
|
||||||
|
segment = segment.transform(&scale.post_mul(&rotation).post_mul(&translation));
|
||||||
|
|
||||||
|
/*
|
||||||
|
println!("angle={} start_angle={} end_angle={} sweep_angle={} segment={:?}",
|
||||||
|
angle,
|
||||||
|
start_angle,
|
||||||
|
end_angle,
|
||||||
|
sweep_angle,
|
||||||
|
segment);
|
||||||
|
*/
|
||||||
|
|
||||||
|
if first_segment {
|
||||||
|
self.push_full_segment(&segment, true);
|
||||||
|
first_segment = false;
|
||||||
|
} else {
|
||||||
|
self.push_segment(segment, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
angle += sweep_angle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn segment_after(&self, point_index: u32) -> Segment {
|
pub fn segment_after(&self, point_index: u32) -> Segment {
|
||||||
debug_assert!(self.point_is_endpoint(point_index));
|
debug_assert!(self.point_is_endpoint(point_index));
|
||||||
|
@ -388,8 +423,8 @@ impl Contour {
|
||||||
pub fn hull_segment_after(&self, prev_point_index: u32) -> LineSegmentF32 {
|
pub fn hull_segment_after(&self, prev_point_index: u32) -> LineSegmentF32 {
|
||||||
let next_point_index = self.next_point_index_of(prev_point_index);
|
let next_point_index = self.next_point_index_of(prev_point_index);
|
||||||
LineSegmentF32::new(
|
LineSegmentF32::new(
|
||||||
&self.points[prev_point_index as usize],
|
self.points[prev_point_index as usize],
|
||||||
&self.points[next_point_index as usize],
|
self.points[next_point_index as usize],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -455,7 +490,7 @@ impl Contour {
|
||||||
|
|
||||||
pub fn transform(&mut self, transform: &Transform2DF32) {
|
pub fn transform(&mut self, transform: &Transform2DF32) {
|
||||||
for (point_index, point) in self.points.iter_mut().enumerate() {
|
for (point_index, point) in self.points.iter_mut().enumerate() {
|
||||||
*point = transform.transform_point(point);
|
*point = transform.transform_point(*point);
|
||||||
union_rect(&mut self.bounds, *point, point_index == 0);
|
union_rect(&mut self.bounds, *point, point_index == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -522,8 +557,8 @@ impl Contour {
|
||||||
point_index
|
point_index
|
||||||
};
|
};
|
||||||
let baseline = LineSegmentF32::new(
|
let baseline = LineSegmentF32::new(
|
||||||
&contour.points[last_endpoint_index as usize],
|
contour.points[last_endpoint_index as usize],
|
||||||
&contour.points[position_index as usize],
|
contour.points[position_index as usize],
|
||||||
);
|
);
|
||||||
let point_count = point_index - last_endpoint_index + 1;
|
let point_count = point_index - last_endpoint_index + 1;
|
||||||
if point_count == 3 {
|
if point_count == 3 {
|
||||||
|
@ -531,13 +566,13 @@ impl Contour {
|
||||||
let ctrl_position = &contour.points[ctrl_point_index];
|
let ctrl_position = &contour.points[ctrl_point_index];
|
||||||
handle_cubic(
|
handle_cubic(
|
||||||
self,
|
self,
|
||||||
Segment::quadratic(&baseline, &ctrl_position).to_cubic(),
|
Segment::quadratic(&baseline, *ctrl_position).to_cubic(),
|
||||||
);
|
);
|
||||||
} else if point_count == 4 {
|
} else if point_count == 4 {
|
||||||
let first_ctrl_point_index = last_endpoint_index as usize + 1;
|
let first_ctrl_point_index = last_endpoint_index as usize + 1;
|
||||||
let ctrl_position_0 = &contour.points[first_ctrl_point_index + 0];
|
let ctrl_position_0 = &contour.points[first_ctrl_point_index + 0];
|
||||||
let ctrl_position_1 = &contour.points[first_ctrl_point_index + 1];
|
let ctrl_position_1 = &contour.points[first_ctrl_point_index + 1];
|
||||||
let ctrl = LineSegmentF32::new(&ctrl_position_0, &ctrl_position_1);
|
let ctrl = LineSegmentF32::new(*ctrl_position_0, *ctrl_position_1);
|
||||||
handle_cubic(self, Segment::cubic(&baseline, &ctrl));
|
handle_cubic(self, Segment::cubic(&baseline, &ctrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -723,24 +758,21 @@ impl<'a> Iterator for ContourIter<'a> {
|
||||||
if self.index == contour.len() {
|
if self.index == contour.len() {
|
||||||
let point1 = contour.position_of(0);
|
let point1 = contour.position_of(0);
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
return Some(Segment::line(&LineSegmentF32::new(&point0, &point1)));
|
return Some(Segment::line(&LineSegmentF32::new(point0, point1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let point1_index = self.index;
|
let point1_index = self.index;
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
let point1 = contour.position_of(point1_index);
|
let point1 = contour.position_of(point1_index);
|
||||||
if contour.point_is_endpoint(point1_index) {
|
if contour.point_is_endpoint(point1_index) {
|
||||||
return Some(Segment::line(&LineSegmentF32::new(&point0, &point1)));
|
return Some(Segment::line(&LineSegmentF32::new(point0, point1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let point2_index = self.index;
|
let point2_index = self.index;
|
||||||
let point2 = contour.position_of(point2_index);
|
let point2 = contour.position_of(point2_index);
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
if contour.point_is_endpoint(point2_index) {
|
if contour.point_is_endpoint(point2_index) {
|
||||||
return Some(Segment::quadratic(
|
return Some(Segment::quadratic(&LineSegmentF32::new(point0, point2), point1));
|
||||||
&LineSegmentF32::new(&point0, &point2),
|
|
||||||
&point1,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let point3_index = self.index;
|
let point3_index = self.index;
|
||||||
|
@ -748,8 +780,8 @@ impl<'a> Iterator for ContourIter<'a> {
|
||||||
self.index += 1;
|
self.index += 1;
|
||||||
debug_assert!(contour.point_is_endpoint(point3_index));
|
debug_assert!(contour.point_is_endpoint(point3_index));
|
||||||
return Some(Segment::cubic(
|
return Some(Segment::cubic(
|
||||||
&LineSegmentF32::new(&point0, &point3),
|
&LineSegmentF32::new(point0, point3),
|
||||||
&LineSegmentF32::new(&point1, &point2),
|
&LineSegmentF32::new(point1, point2),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,10 @@
|
||||||
|
|
||||||
use crate::basic::line_segment::LineSegmentF32;
|
use crate::basic::line_segment::LineSegmentF32;
|
||||||
use crate::basic::point::Point2DF32;
|
use crate::basic::point::Point2DF32;
|
||||||
|
use crate::basic::transform2d::Transform2DF32;
|
||||||
use crate::util::{self, EPSILON};
|
use crate::util::{self, EPSILON};
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
|
use std::f32::consts::SQRT_2;
|
||||||
|
|
||||||
const MAX_NEWTON_ITERATIONS: u32 = 32;
|
const MAX_NEWTON_ITERATIONS: u32 = 32;
|
||||||
|
|
||||||
|
@ -47,10 +49,10 @@ impl Segment {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn quadratic(baseline: &LineSegmentF32, ctrl: &Point2DF32) -> Segment {
|
pub fn quadratic(baseline: &LineSegmentF32, ctrl: Point2DF32) -> Segment {
|
||||||
Segment {
|
Segment {
|
||||||
baseline: *baseline,
|
baseline: *baseline,
|
||||||
ctrl: LineSegmentF32::new(ctrl, &Point2DF32::default()),
|
ctrl: LineSegmentF32::new(ctrl, Point2DF32::default()),
|
||||||
kind: SegmentKind::Quadratic,
|
kind: SegmentKind::Quadratic,
|
||||||
flags: SegmentFlags::empty(),
|
flags: SegmentFlags::empty(),
|
||||||
}
|
}
|
||||||
|
@ -66,6 +68,24 @@ impl Segment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Approximates an unit-length arc with a cubic Bézier curve.
|
||||||
|
///
|
||||||
|
/// The maximum supported `sweep_angle` is π/2 (i.e. 90°).
|
||||||
|
pub fn arc(sweep_angle: f32) -> Segment {
|
||||||
|
// Aleksas Riškus, "Approximation of a Cubic Bézier Curve by Circular Arcs and Vice Versa"
|
||||||
|
// 2006.
|
||||||
|
//
|
||||||
|
// https://pdfs.semanticscholar.org/1639/0db1a470bd13fe428e0896671a9a5745070a.pdf
|
||||||
|
let phi = 0.5 * sweep_angle;
|
||||||
|
let p0 = Point2DF32::new(f32::cos(phi), f32::sin(phi));
|
||||||
|
let p3 = p0.scale_xy(Point2DF32::new(1.0, -1.0));
|
||||||
|
let p1 = p0 - p3.yx().scale(K);
|
||||||
|
let p2 = p3 + p0.yx().scale(K);
|
||||||
|
return Segment::cubic(&LineSegmentF32::new(p3, p0), &LineSegmentF32::new(p2, p1));
|
||||||
|
|
||||||
|
const K: f32 = 4.0 / 3.0 * (SQRT_2 - 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_line_segment(&self) -> LineSegmentF32 {
|
pub fn as_line_segment(&self) -> LineSegmentF32 {
|
||||||
debug_assert!(self.is_line());
|
debug_assert!(self.is_line());
|
||||||
|
@ -109,7 +129,7 @@ impl Segment {
|
||||||
let mut new_segment = *self;
|
let mut new_segment = *self;
|
||||||
let p1_2 = self.ctrl.from() + self.ctrl.from();
|
let p1_2 = self.ctrl.from() + self.ctrl.from();
|
||||||
new_segment.ctrl =
|
new_segment.ctrl =
|
||||||
LineSegmentF32::new(&(self.baseline.from() + p1_2), &(p1_2 + self.baseline.to()))
|
LineSegmentF32::new(self.baseline.from() + p1_2, p1_2 + self.baseline.to())
|
||||||
.scale(1.0 / 3.0);
|
.scale(1.0 / 3.0);
|
||||||
new_segment.kind = SegmentKind::Cubic;
|
new_segment.kind = SegmentKind::Cubic;
|
||||||
new_segment
|
new_segment
|
||||||
|
@ -176,6 +196,16 @@ impl Segment {
|
||||||
self.to_cubic().as_cubic_segment().sample(t)
|
self.to_cubic().as_cubic_segment().sample(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn transform(self, transform: &Transform2DF32) -> Segment {
|
||||||
|
Segment {
|
||||||
|
baseline: transform.transform_line_segment(&self.baseline),
|
||||||
|
ctrl: transform.transform_line_segment(&self.ctrl),
|
||||||
|
kind: self.kind,
|
||||||
|
flags: self.flags,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
@ -215,16 +245,16 @@ impl<'s> CubicSegment<'s> {
|
||||||
let (baseline0, ctrl0, baseline1, ctrl1);
|
let (baseline0, ctrl0, baseline1, ctrl1);
|
||||||
if t <= 0.0 {
|
if t <= 0.0 {
|
||||||
let from = &self.0.baseline.from();
|
let from = &self.0.baseline.from();
|
||||||
baseline0 = LineSegmentF32::new(from, from);
|
baseline0 = LineSegmentF32::new(*from, *from);
|
||||||
ctrl0 = LineSegmentF32::new(from, from);
|
ctrl0 = LineSegmentF32::new(*from, *from);
|
||||||
baseline1 = self.0.baseline;
|
baseline1 = self.0.baseline;
|
||||||
ctrl1 = self.0.ctrl;
|
ctrl1 = self.0.ctrl;
|
||||||
} else if t >= 1.0 {
|
} else if t >= 1.0 {
|
||||||
let to = &self.0.baseline.to();
|
let to = &self.0.baseline.to();
|
||||||
baseline0 = self.0.baseline;
|
baseline0 = self.0.baseline;
|
||||||
ctrl0 = self.0.ctrl;
|
ctrl0 = self.0.ctrl;
|
||||||
baseline1 = LineSegmentF32::new(to, to);
|
baseline1 = LineSegmentF32::new(*to, *to);
|
||||||
ctrl1 = LineSegmentF32::new(to, to);
|
ctrl1 = LineSegmentF32::new(*to, *to);
|
||||||
} else {
|
} else {
|
||||||
let tttt = F32x4::splat(t);
|
let tttt = F32x4::splat(t);
|
||||||
|
|
||||||
|
|
|
@ -118,51 +118,51 @@ impl Offset for Segment {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.is_quadratic() {
|
if self.is_quadratic() {
|
||||||
let mut segment_0 = LineSegmentF32::new(&self.baseline.from(), &self.ctrl.from());
|
let mut segment_0 = LineSegmentF32::new(self.baseline.from(), self.ctrl.from());
|
||||||
let mut segment_1 = LineSegmentF32::new(&self.ctrl.from(), &self.baseline.to());
|
let mut segment_1 = LineSegmentF32::new(self.ctrl.from(), self.baseline.to());
|
||||||
segment_0 = segment_0.offset(distance);
|
segment_0 = segment_0.offset(distance);
|
||||||
segment_1 = segment_1.offset(distance);
|
segment_1 = segment_1.offset(distance);
|
||||||
let ctrl = match segment_0.intersection_t(&segment_1) {
|
let ctrl = match segment_0.intersection_t(&segment_1) {
|
||||||
Some(t) => segment_0.sample(t),
|
Some(t) => segment_0.sample(t),
|
||||||
None => segment_0.to().lerp(segment_1.from(), 0.5),
|
None => segment_0.to().lerp(segment_1.from(), 0.5),
|
||||||
};
|
};
|
||||||
let baseline = LineSegmentF32::new(&segment_0.from(), &segment_1.to());
|
let baseline = LineSegmentF32::new(segment_0.from(), segment_1.to());
|
||||||
return Segment::quadratic(&baseline, &ctrl);
|
return Segment::quadratic(&baseline, ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_assert!(self.is_cubic());
|
debug_assert!(self.is_cubic());
|
||||||
|
|
||||||
if self.baseline.from() == self.ctrl.from() {
|
if self.baseline.from() == self.ctrl.from() {
|
||||||
let mut segment_0 = LineSegmentF32::new(&self.baseline.from(), &self.ctrl.to());
|
let mut segment_0 = LineSegmentF32::new(self.baseline.from(), self.ctrl.to());
|
||||||
let mut segment_1 = LineSegmentF32::new(&self.ctrl.to(), &self.baseline.to());
|
let mut segment_1 = LineSegmentF32::new(self.ctrl.to(), self.baseline.to());
|
||||||
segment_0 = segment_0.offset(distance);
|
segment_0 = segment_0.offset(distance);
|
||||||
segment_1 = segment_1.offset(distance);
|
segment_1 = segment_1.offset(distance);
|
||||||
let ctrl = match segment_0.intersection_t(&segment_1) {
|
let ctrl = match segment_0.intersection_t(&segment_1) {
|
||||||
Some(t) => segment_0.sample(t),
|
Some(t) => segment_0.sample(t),
|
||||||
None => segment_0.to().lerp(segment_1.from(), 0.5),
|
None => segment_0.to().lerp(segment_1.from(), 0.5),
|
||||||
};
|
};
|
||||||
let baseline = LineSegmentF32::new(&segment_0.from(), &segment_1.to());
|
let baseline = LineSegmentF32::new(segment_0.from(), segment_1.to());
|
||||||
let ctrl = LineSegmentF32::new(&segment_0.from(), &ctrl);
|
let ctrl = LineSegmentF32::new(segment_0.from(), ctrl);
|
||||||
return Segment::cubic(&baseline, &ctrl);
|
return Segment::cubic(&baseline, &ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.ctrl.to() == self.baseline.to() {
|
if self.ctrl.to() == self.baseline.to() {
|
||||||
let mut segment_0 = LineSegmentF32::new(&self.baseline.from(), &self.ctrl.from());
|
let mut segment_0 = LineSegmentF32::new(self.baseline.from(), self.ctrl.from());
|
||||||
let mut segment_1 = LineSegmentF32::new(&self.ctrl.from(), &self.baseline.to());
|
let mut segment_1 = LineSegmentF32::new(self.ctrl.from(), self.baseline.to());
|
||||||
segment_0 = segment_0.offset(distance);
|
segment_0 = segment_0.offset(distance);
|
||||||
segment_1 = segment_1.offset(distance);
|
segment_1 = segment_1.offset(distance);
|
||||||
let ctrl = match segment_0.intersection_t(&segment_1) {
|
let ctrl = match segment_0.intersection_t(&segment_1) {
|
||||||
Some(t) => segment_0.sample(t),
|
Some(t) => segment_0.sample(t),
|
||||||
None => segment_0.to().lerp(segment_1.from(), 0.5),
|
None => segment_0.to().lerp(segment_1.from(), 0.5),
|
||||||
};
|
};
|
||||||
let baseline = LineSegmentF32::new(&segment_0.from(), &segment_1.to());
|
let baseline = LineSegmentF32::new(segment_0.from(), segment_1.to());
|
||||||
let ctrl = LineSegmentF32::new(&ctrl, &segment_1.to());
|
let ctrl = LineSegmentF32::new(ctrl, segment_1.to());
|
||||||
return Segment::cubic(&baseline, &ctrl);
|
return Segment::cubic(&baseline, &ctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut segment_0 = LineSegmentF32::new(&self.baseline.from(), &self.ctrl.from());
|
let mut segment_0 = LineSegmentF32::new(self.baseline.from(), self.ctrl.from());
|
||||||
let mut segment_1 = LineSegmentF32::new(&self.ctrl.from(), &self.ctrl.to());
|
let mut segment_1 = LineSegmentF32::new(self.ctrl.from(), self.ctrl.to());
|
||||||
let mut segment_2 = LineSegmentF32::new(&self.ctrl.to(), &self.baseline.to());
|
let mut segment_2 = LineSegmentF32::new(self.ctrl.to(), self.baseline.to());
|
||||||
segment_0 = segment_0.offset(distance);
|
segment_0 = segment_0.offset(distance);
|
||||||
segment_1 = segment_1.offset(distance);
|
segment_1 = segment_1.offset(distance);
|
||||||
segment_2 = segment_2.offset(distance);
|
segment_2 = segment_2.offset(distance);
|
||||||
|
@ -176,8 +176,8 @@ impl Offset for Segment {
|
||||||
segment_1.to().lerp(segment_2.from(), 0.5),
|
segment_1.to().lerp(segment_2.from(), 0.5),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
let baseline = LineSegmentF32::new(&segment_0.from(), &segment_2.to());
|
let baseline = LineSegmentF32::new(segment_0.from(), segment_2.to());
|
||||||
let ctrl = LineSegmentF32::new(&ctrl_0, &ctrl_1);
|
let ctrl = LineSegmentF32::new(ctrl_0, ctrl_1);
|
||||||
Segment::cubic(&baseline, &ctrl)
|
Segment::cubic(&baseline, &ctrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -236,9 +236,9 @@ impl BuiltObject {
|
||||||
let right = Point2DF32::new(right, tile_origin_y);
|
let right = Point2DF32::new(right, tile_origin_y);
|
||||||
|
|
||||||
let segment = if winding < 0 {
|
let segment = if winding < 0 {
|
||||||
LineSegmentF32::new(&left, &right)
|
LineSegmentF32::new(left, right)
|
||||||
} else {
|
} else {
|
||||||
LineSegmentF32::new(&right, &left)
|
LineSegmentF32::new(right, left)
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
|
@ -300,14 +300,14 @@ impl BuiltObject {
|
||||||
let point = Point2DF32::new(x, segment.solve_y_for_x(x));
|
let point = Point2DF32::new(x, segment.solve_y_for_x(x));
|
||||||
if !winding {
|
if !winding {
|
||||||
fill_to = point;
|
fill_to = point;
|
||||||
segment = LineSegmentF32::new(&point, &segment.to());
|
segment = LineSegmentF32::new(point, segment.to());
|
||||||
} else {
|
} else {
|
||||||
fill_from = point;
|
fill_from = point;
|
||||||
segment = LineSegmentF32::new(&segment.from(), &point);
|
segment = LineSegmentF32::new(segment.from(), point);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let fill_segment = LineSegmentF32::new(&fill_from, &fill_to);
|
let fill_segment = LineSegmentF32::new(fill_from, fill_to);
|
||||||
let fill_tile_coords = Point2DI32::new(subsegment_tile_x, tile_y);
|
let fill_tile_coords = Point2DI32::new(subsegment_tile_x, tile_y);
|
||||||
self.add_fill(builder, &fill_segment, fill_tile_coords);
|
self.add_fill(builder, &fill_segment, fill_tile_coords);
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ impl Scene {
|
||||||
};
|
};
|
||||||
if options.subpixel_aa_enabled {
|
if options.subpixel_aa_enabled {
|
||||||
transform = transform
|
transform = transform
|
||||||
.post_mul(&Transform2DF32::from_scale(&Point2DF32::new(3.0, 1.0)))
|
.post_mul(&Transform2DF32::from_scale(Point2DF32::new(3.0, 1.0)))
|
||||||
}
|
}
|
||||||
outline.transform(&transform);
|
outline.transform(&transform);
|
||||||
}
|
}
|
||||||
|
|
|
@ -434,8 +434,7 @@ impl ActiveEdge {
|
||||||
// If necessary, draw initial line.
|
// If necessary, draw initial line.
|
||||||
if self.crossing.y() < segment.baseline.min_y() {
|
if self.crossing.y() < segment.baseline.min_y() {
|
||||||
let first_line_segment =
|
let first_line_segment =
|
||||||
LineSegmentF32::new(&self.crossing, &segment.baseline.upper_point())
|
LineSegmentF32::new(self.crossing, segment.baseline.upper_point()).orient(winding);
|
||||||
.orient(winding);
|
|
||||||
if self
|
if self
|
||||||
.process_line_segment(&first_line_segment, builder, built_object, tile_y)
|
.process_line_segment(&first_line_segment, builder, built_object, tile_y)
|
||||||
.is_some()
|
.is_some()
|
||||||
|
|
|
@ -93,7 +93,7 @@ impl SceneExt for Scene {
|
||||||
let scale = style.size / (font.metrics().units_per_em as f32);
|
let scale = style.size / (font.metrics().units_per_em as f32);
|
||||||
let scale = Point2DF32::new(scale, -scale);
|
let scale = Point2DF32::new(scale, -scale);
|
||||||
let transform =
|
let transform =
|
||||||
Transform2DF32::from_scale(&scale).post_mul(transform).post_translate(offset);
|
Transform2DF32::from_scale(scale).post_mul(transform).post_translate(offset);
|
||||||
self.push_glyph(font,
|
self.push_glyph(font,
|
||||||
glyph.glyph_id,
|
glyph.glyph_id,
|
||||||
&transform,
|
&transform,
|
||||||
|
@ -147,7 +147,7 @@ impl OutlinePathBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_point(&self, point: Point2D<f32>) -> Point2DF32 {
|
fn convert_point(&self, point: Point2D<f32>) -> Point2DF32 {
|
||||||
self.transform.transform_point(&Point2DF32::new(point.x, point.y))
|
self.transform.transform_point(Point2DF32::new(point.x, point.y))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue