Take the orientation of a path into account when computing normals
This commit is contained in:
parent
aa3ecf28a5
commit
db6986ca1f
|
@ -41,6 +41,7 @@
|
||||||
<option value="font-open-sans">Font: Open Sans</option>
|
<option value="font-open-sans">Font: Open Sans</option>
|
||||||
<option value="font-nimbus-sans">Font: Nimbus Sans</option>
|
<option value="font-nimbus-sans">Font: Nimbus Sans</option>
|
||||||
<option value="font-inter-ui">Font: Inter UI</option>
|
<option value="font-inter-ui">Font: Inter UI</option>
|
||||||
|
<option value="font-custom">Font: Custom…</option>
|
||||||
<option value="svg-tiger">SVG: Ghostscript Tiger</option>
|
<option value="svg-tiger">SVG: Ghostscript Tiger</option>
|
||||||
<option value="svg-logo">SVG: Pathfinder Logo</option>
|
<option value="svg-logo">SVG: Pathfinder Logo</option>
|
||||||
<option value="svg-custom">SVG: Custom…</option>
|
<option value="svg-custom">SVG: Custom…</option>
|
||||||
|
|
|
@ -77,7 +77,7 @@ const SUGGESTED_JSON_SIZE_LIMIT: u64 = 32 * 1024 * 1024;
|
||||||
|
|
||||||
const MESH_LIBRARY_CACHE_SIZE: usize = 16;
|
const MESH_LIBRARY_CACHE_SIZE: usize = 16;
|
||||||
|
|
||||||
const CUBIC_TO_QUADRATIC_APPROX_TOLERANCE: f32 = 1.0;
|
const CUBIC_TO_QUADRATIC_APPROX_TOLERANCE: f32 = 5.0;
|
||||||
|
|
||||||
static NEXT_FONT_KEY: AtomicUsize = ATOMIC_USIZE_INIT;
|
static NEXT_FONT_KEY: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||||
|
|
||||||
|
|
|
@ -176,10 +176,10 @@ impl MeshLibrary {
|
||||||
|
|
||||||
let mut b_quad_vertex_positions = BQuadVertexPositions {
|
let mut b_quad_vertex_positions = BQuadVertexPositions {
|
||||||
upper_left_vertex_position: ul,
|
upper_left_vertex_position: ul,
|
||||||
upper_control_point_position: ul,
|
upper_control_point_position: ul.lerp(ur, 0.5),
|
||||||
upper_right_vertex_position: ur,
|
upper_right_vertex_position: ur,
|
||||||
lower_left_vertex_position: ll,
|
lower_left_vertex_position: ll,
|
||||||
lower_control_point_position: lr,
|
lower_control_point_position: ll.lerp(lr, 0.5),
|
||||||
lower_right_vertex_position: lr,
|
lower_right_vertex_position: lr,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ extern crate lyon_path;
|
||||||
|
|
||||||
pub mod cubic_to_quadratic;
|
pub mod cubic_to_quadratic;
|
||||||
pub mod normals;
|
pub mod normals;
|
||||||
|
pub mod orientation;
|
||||||
pub mod segments;
|
pub mod segments;
|
||||||
pub mod stroke;
|
pub mod stroke;
|
||||||
pub mod transform;
|
pub mod transform;
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
use euclid::{Point2D, Vector2D};
|
use euclid::{Point2D, Vector2D};
|
||||||
use lyon_path::PathEvent;
|
use lyon_path::PathEvent;
|
||||||
|
use orientation::Orientation;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct SegmentNormals {
|
pub struct SegmentNormals {
|
||||||
|
@ -40,8 +41,12 @@ impl PathNormals {
|
||||||
self.normals.clear()
|
self.normals.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_path<I>(&mut self, mut stream: I) where I: Iterator<Item = PathEvent> {
|
pub fn add_path<I>(&mut self, stream: I) where I: Iterator<Item = PathEvent> {
|
||||||
|
let events: Vec<_> = stream.collect();
|
||||||
|
let orientation = Orientation::from_path(events.iter().cloned());
|
||||||
|
|
||||||
let (mut path_ops, mut path_points) = (vec![], vec![]);
|
let (mut path_ops, mut path_points) = (vec![], vec![]);
|
||||||
|
let mut stream = events.iter().cloned();
|
||||||
while let Some(event) = stream.next() {
|
while let Some(event) = stream.next() {
|
||||||
path_ops.push(PathOp::from_path_event(&event));
|
path_ops.push(PathOp::from_path_event(&event));
|
||||||
match event {
|
match event {
|
||||||
|
@ -54,40 +59,49 @@ impl PathNormals {
|
||||||
PathEvent::Arc(..) => {
|
PathEvent::Arc(..) => {
|
||||||
panic!("PathNormals::add_path(): Convert arcs to quadratics first!")
|
panic!("PathNormals::add_path(): Convert arcs to quadratics first!")
|
||||||
}
|
}
|
||||||
PathEvent::Close => self.flush(path_ops.drain(..), &mut path_points),
|
PathEvent::Close => self.flush(orientation, path_ops.drain(..), &mut path_points),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.flush(path_ops.into_iter(), &mut path_points);
|
self.flush(orientation, path_ops.into_iter(), &mut path_points);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush<I>(&mut self, path_stream: I, path_points: &mut Vec<Point2D<f32>>)
|
fn flush<I>(&mut self,
|
||||||
|
orientation: Orientation,
|
||||||
|
path_stream: I,
|
||||||
|
path_points: &mut Vec<Point2D<f32>>)
|
||||||
where I: Iterator<Item = PathOp> {
|
where I: Iterator<Item = PathOp> {
|
||||||
match path_points.len() {
|
match path_points.len() {
|
||||||
0 | 1 => path_points.clear(),
|
0 | 1 => path_points.clear(),
|
||||||
2 => {
|
2 => {
|
||||||
|
let orientation = -(orientation as i32 as f32);
|
||||||
self.normals.push(SegmentNormals {
|
self.normals.push(SegmentNormals {
|
||||||
from: path_points[1] - path_points[0],
|
from: (path_points[1] - path_points[0]) * orientation,
|
||||||
ctrl: Vector2D::zero(),
|
ctrl: Vector2D::zero(),
|
||||||
to: path_points[0] - path_points[1],
|
to: (path_points[0] - path_points[1]) * orientation,
|
||||||
});
|
});
|
||||||
path_points.clear();
|
path_points.clear();
|
||||||
}
|
}
|
||||||
_ => self.flush_slow(path_stream, path_points),
|
_ => self.flush_slow(orientation, path_stream, path_points),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush_slow<I>(&mut self, path_stream: I, path_points: &mut Vec<Point2D<f32>>)
|
fn flush_slow<I>(&mut self,
|
||||||
|
orientation: Orientation,
|
||||||
|
path_stream: I,
|
||||||
|
path_points: &mut Vec<Point2D<f32>>)
|
||||||
where I: Iterator<Item = PathOp> {
|
where I: Iterator<Item = PathOp> {
|
||||||
let mut normals = vec![Vector2D::zero(); path_points.len()];
|
let mut normals = vec![Vector2D::zero(); path_points.len()];
|
||||||
*normals.last_mut().unwrap() = compute_normal(&path_points[path_points.len() - 2],
|
*normals.last_mut().unwrap() = compute_normal(orientation,
|
||||||
|
&path_points[path_points.len() - 2],
|
||||||
&path_points[path_points.len() - 1],
|
&path_points[path_points.len() - 1],
|
||||||
&path_points[0]);
|
&path_points[0]);
|
||||||
normals[0] = compute_normal(&path_points[path_points.len() - 1],
|
normals[0] = compute_normal(orientation,
|
||||||
|
&path_points[path_points.len() - 1],
|
||||||
&path_points[0],
|
&path_points[0],
|
||||||
&path_points[1]);
|
&path_points[1]);
|
||||||
for (index, window) in path_points.windows(3).enumerate() {
|
for (index, window) in path_points.windows(3).enumerate() {
|
||||||
normals[index + 1] = compute_normal(&window[0], &window[1], &window[2]);
|
normals[index + 1] = compute_normal(orientation, &window[0], &window[1], &window[2])
|
||||||
}
|
}
|
||||||
|
|
||||||
path_points.clear();
|
path_points.clear();
|
||||||
|
@ -125,10 +139,13 @@ impl PathNormals {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_normal(prev: &Point2D<f32>, current: &Point2D<f32>, next: &Point2D<f32>)
|
fn compute_normal(orientation: Orientation,
|
||||||
-> Vector2D<f32> {
|
prev: &Point2D<f32>,
|
||||||
|
current: &Point2D<f32>,
|
||||||
|
next: &Point2D<f32>)
|
||||||
|
-> Vector2D<f32> {
|
||||||
let vector = ((*current - *prev) + (*next - *current)).normalize();
|
let vector = ((*current - *prev) + (*next - *current)).normalize();
|
||||||
Vector2D::new(vector.y, -vector.x)
|
Vector2D::new(vector.y, -vector.x) * -(orientation as i32 as f32)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
// pathfinder/path-utils/src/orientation.rs
|
||||||
|
//
|
||||||
|
// Copyright © 2018 The Pathfinder Project Developers.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
use euclid::Point2D;
|
||||||
|
use lyon_path::PathEvent;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum Orientation {
|
||||||
|
Ccw = -1,
|
||||||
|
Cw = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Orientation {
|
||||||
|
/// This follows the FreeType algorithm.
|
||||||
|
pub fn from_path<I>(stream: I) -> Orientation where I: Iterator<Item = PathEvent> {
|
||||||
|
let (mut from, mut subpath_start) = (Point2D::zero(), Point2D::zero());
|
||||||
|
let mut area = 0.0;
|
||||||
|
for event in stream {
|
||||||
|
match event {
|
||||||
|
PathEvent::MoveTo(to) => {
|
||||||
|
from = to;
|
||||||
|
subpath_start = to;
|
||||||
|
}
|
||||||
|
PathEvent::LineTo(to) => {
|
||||||
|
area += det(&from, &to);
|
||||||
|
from = to;
|
||||||
|
}
|
||||||
|
PathEvent::QuadraticTo(ctrl, to) => {
|
||||||
|
area += det(&from, &ctrl) + det(&ctrl, &to);
|
||||||
|
from = to;
|
||||||
|
}
|
||||||
|
PathEvent::CubicTo(ctrl0, ctrl1, to) => {
|
||||||
|
area += det(&from, &ctrl0) + det(&ctrl0, &ctrl1) + det(&ctrl1, &to);
|
||||||
|
from = to;
|
||||||
|
}
|
||||||
|
PathEvent::Arc(..) => {
|
||||||
|
// TODO(pcwalton)
|
||||||
|
}
|
||||||
|
PathEvent::Close => {
|
||||||
|
area += det(&from, &subpath_start);
|
||||||
|
from = subpath_start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if area <= 0.0 {
|
||||||
|
Orientation::Ccw
|
||||||
|
} else {
|
||||||
|
Orientation::Cw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn det(a: &Point2D<f32>, b: &Point2D<f32>) -> f32 {
|
||||||
|
a.x * b.y - a.y * b.x
|
||||||
|
}
|
|
@ -101,7 +101,7 @@ vec2 hintPosition(vec2 position, vec4 pathHints) {
|
||||||
}
|
}
|
||||||
|
|
||||||
vec2 quantize(vec2 position) {
|
vec2 quantize(vec2 position) {
|
||||||
return (floor(position * 10000.0 + 0.5) - 0.5) / 10000.0;
|
return (floor(position * 20000.0 + 0.5) - 0.5) / 20000.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts the given 2D position in clip space to device pixel space (with origin in the lower
|
/// Converts the given 2D position in clip space to device pixel space (with origin in the lower
|
||||||
|
|
Loading…
Reference in New Issue