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