From db6986ca1f1c3b0cf7d2ea4fe68e3faccd7974c1 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 7 Mar 2018 11:21:08 -0800 Subject: [PATCH] Take the orientation of a path into account when computing normals --- demo/client/html/mesh-debugger.html.hbs | 1 + demo/server/src/main.rs | 2 +- partitioner/src/mesh_library.rs | 4 +- path-utils/src/lib.rs | 1 + path-utils/src/normals.rs | 45 ++++++++++++------ path-utils/src/orientation.rs | 62 +++++++++++++++++++++++++ shaders/gles2/common.inc.glsl | 2 +- 7 files changed, 99 insertions(+), 18 deletions(-) create mode 100644 path-utils/src/orientation.rs diff --git a/demo/client/html/mesh-debugger.html.hbs b/demo/client/html/mesh-debugger.html.hbs index d9298748..cc32bd5d 100644 --- a/demo/client/html/mesh-debugger.html.hbs +++ b/demo/client/html/mesh-debugger.html.hbs @@ -41,6 +41,7 @@ + diff --git a/demo/server/src/main.rs b/demo/server/src/main.rs index 445fd33e..d6fe66eb 100644 --- a/demo/server/src/main.rs +++ b/demo/server/src/main.rs @@ -77,7 +77,7 @@ const SUGGESTED_JSON_SIZE_LIMIT: u64 = 32 * 1024 * 1024; 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; diff --git a/partitioner/src/mesh_library.rs b/partitioner/src/mesh_library.rs index 92125dab..11fee383 100644 --- a/partitioner/src/mesh_library.rs +++ b/partitioner/src/mesh_library.rs @@ -176,10 +176,10 @@ impl MeshLibrary { let mut b_quad_vertex_positions = BQuadVertexPositions { upper_left_vertex_position: ul, - upper_control_point_position: ul, + upper_control_point_position: ul.lerp(ur, 0.5), upper_right_vertex_position: ur, lower_left_vertex_position: ll, - lower_control_point_position: lr, + lower_control_point_position: ll.lerp(lr, 0.5), lower_right_vertex_position: lr, }; diff --git a/path-utils/src/lib.rs b/path-utils/src/lib.rs index d0a90dd6..4e6d847a 100644 --- a/path-utils/src/lib.rs +++ b/path-utils/src/lib.rs @@ -19,6 +19,7 @@ extern crate lyon_path; pub mod cubic_to_quadratic; pub mod normals; +pub mod orientation; pub mod segments; pub mod stroke; pub mod transform; diff --git a/path-utils/src/normals.rs b/path-utils/src/normals.rs index ef739388..0f8f99f9 100644 --- a/path-utils/src/normals.rs +++ b/path-utils/src/normals.rs @@ -10,6 +10,7 @@ use euclid::{Point2D, Vector2D}; use lyon_path::PathEvent; +use orientation::Orientation; #[derive(Clone, Copy, Debug)] pub struct SegmentNormals { @@ -40,8 +41,12 @@ impl PathNormals { self.normals.clear() } - pub fn add_path(&mut self, mut stream: I) where I: Iterator { + pub fn add_path(&mut self, stream: I) where I: Iterator { + let events: Vec<_> = stream.collect(); + let orientation = Orientation::from_path(events.iter().cloned()); + let (mut path_ops, mut path_points) = (vec![], vec![]); + let mut stream = events.iter().cloned(); while let Some(event) = stream.next() { path_ops.push(PathOp::from_path_event(&event)); match event { @@ -54,40 +59,49 @@ impl PathNormals { PathEvent::Arc(..) => { 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(&mut self, path_stream: I, path_points: &mut Vec>) + fn flush(&mut self, + orientation: Orientation, + path_stream: I, + path_points: &mut Vec>) where I: Iterator { match path_points.len() { 0 | 1 => path_points.clear(), 2 => { + let orientation = -(orientation as i32 as f32); self.normals.push(SegmentNormals { - from: path_points[1] - path_points[0], + from: (path_points[1] - path_points[0]) * orientation, ctrl: Vector2D::zero(), - to: path_points[0] - path_points[1], + to: (path_points[0] - path_points[1]) * orientation, }); path_points.clear(); } - _ => self.flush_slow(path_stream, path_points), + _ => self.flush_slow(orientation, path_stream, path_points), } } - fn flush_slow(&mut self, path_stream: I, path_points: &mut Vec>) + fn flush_slow(&mut self, + orientation: Orientation, + path_stream: I, + path_points: &mut Vec>) where I: Iterator { 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[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[1]); 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(); @@ -125,10 +139,13 @@ impl PathNormals { } } -fn compute_normal(prev: &Point2D, current: &Point2D, next: &Point2D) - -> Vector2D { +fn compute_normal(orientation: Orientation, + prev: &Point2D, + current: &Point2D, + next: &Point2D) + -> Vector2D { 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)] diff --git a/path-utils/src/orientation.rs b/path-utils/src/orientation.rs new file mode 100644 index 00000000..e004048b --- /dev/null +++ b/path-utils/src/orientation.rs @@ -0,0 +1,62 @@ +// pathfinder/path-utils/src/orientation.rs +// +// Copyright © 2018 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , 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(stream: I) -> Orientation where I: Iterator { + 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, b: &Point2D) -> f32 { + a.x * b.y - a.y * b.x +} diff --git a/shaders/gles2/common.inc.glsl b/shaders/gles2/common.inc.glsl index 723c4045..983ea7d9 100644 --- a/shaders/gles2/common.inc.glsl +++ b/shaders/gles2/common.inc.glsl @@ -101,7 +101,7 @@ vec2 hintPosition(vec2 position, vec4 pathHints) { } 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