diff --git a/demo/server/src/main.rs b/demo/server/src/main.rs index aa5271c3..7ab42868 100644 --- a/demo/server/src/main.rs +++ b/demo/server/src/main.rs @@ -26,8 +26,9 @@ use bincode::Infinite; use euclid::{Point2D, Size2D, Transform2D}; use pathfinder_font_renderer::{FontContext, FontInstanceKey, FontKey, GlyphKey}; use pathfinder_partitioner::partitioner::Partitioner; +use pathfinder_path_utils::monotonic::MonotonicPathSegmentStream; use pathfinder_path_utils::stroke; -use pathfinder_path_utils::{PathBuffer, PathSegment, Transform2DPathStream}; +use pathfinder_path_utils::{PathBuffer, PathBufferStream, PathSegment, Transform2DPathStream}; use rocket::http::{ContentType, Status}; use rocket::request::Request; use rocket::response::{NamedFile, Redirect, Responder, Response}; @@ -410,7 +411,9 @@ fn partition_font(request: Json) // This might fail; if so, just leave it blank. if let Ok(glyph_outline) = font_context.glyph_outline(&font_instance_key, &glyph_key) { - path_buffer.add_stream(Transform2DPathStream::new(glyph_outline, &glyph.transform)) + let stream = Transform2DPathStream::new(glyph_outline, &glyph.transform); + let stream = MonotonicPathSegmentStream::new(stream); + path_buffer.add_stream(stream) } let last_subpath_index = path_buffer.subpaths.len(); @@ -497,9 +500,15 @@ fn partition_svg_paths(request: Json) } match path.kind { - PartitionSvgPathKind::Fill => path_buffer.add_stream(stream.into_iter()), + PartitionSvgPathKind::Fill => { + path_buffer.add_stream(MonotonicPathSegmentStream::new(stream.into_iter())) + } PartitionSvgPathKind::Stroke(stroke_width) => { - stroke::stroke(&mut path_buffer, stream.into_iter(), stroke_width) + let mut temp_path_buffer = PathBuffer::new(); + stroke::stroke(&mut temp_path_buffer, stream.into_iter(), stroke_width); + let stream = PathBufferStream::new(&temp_path_buffer); + let stream = MonotonicPathSegmentStream::new(stream); + path_buffer.add_stream(stream) } } diff --git a/partitioner/src/geometry.rs b/partitioner/src/geometry.rs index 759937f1..472b4e3e 100644 --- a/partitioner/src/geometry.rs +++ b/partitioner/src/geometry.rs @@ -10,6 +10,7 @@ use euclid::Point2D; use euclid::approxeq::ApproxEq; +use pathfinder_path_utils::curve::Curve; use std::cmp::Ordering; pub(crate) trait ApproxOrdered { @@ -85,11 +86,6 @@ pub fn quadratic_bezier_quadratic_bezier_crossing_point(_a_p0: &Point2D, None } -pub fn sample_quadratic_bezier(t: f32, p0: &Point2D, p1: &Point2D, p2: &Point2D) - -> Point2D { - p0.lerp(*p1, t).lerp(p1.lerp(*p2, t), t) -} - pub fn solve_line_t_for_x(x: f32, a: &Point2D, b: &Point2D) -> f32 { if b.x == a.x { 0.0 @@ -121,7 +117,7 @@ pub fn solve_quadratic_bezier_y_for_x(x: f32, p1: &Point2D, p2: &Point2D) -> f32 { - sample_quadratic_bezier(solve_quadratic_bezier_t_for_x(x, p0, p1, p2), p0, p1, p2).y + Curve::new(p0, p1, p2).sample(solve_quadratic_bezier_t_for_x(x, p0, p1, p2)).y } fn quadratic_bezier_axis_inflection_point(p0: f32, p1: f32, p2: f32) -> Option { diff --git a/partitioner/src/partitioner.rs b/partitioner/src/partitioner.rs index 1e09a89e..299e29ac 100644 --- a/partitioner/src/partitioner.rs +++ b/partitioner/src/partitioner.rs @@ -13,6 +13,7 @@ use euclid::Point2D; use geometry::{self, SubdividedQuadraticBezier}; use log::LogLevel; use pathfinder_path_utils::PathBuffer; +use pathfinder_path_utils::curve::Curve; use std::collections::BinaryHeap; use std::cmp::Ordering; use std::f32; @@ -712,10 +713,7 @@ impl<'a> Partitioner<'a> { } control_point_vertex_index => { let control_point = &self.b_vertex_positions[control_point_vertex_index as usize]; - geometry::sample_quadratic_bezier(t, - left_vertex_position, - control_point, - right_endpoint_position) + Curve::new(left_vertex_position, control_point, right_endpoint_position).sample(t) } } } diff --git a/path-utils/src/lib.rs b/path-utils/src/lib.rs index a3c7d77e..d7a6d449 100644 --- a/path-utils/src/lib.rs +++ b/path-utils/src/lib.rs @@ -98,6 +98,53 @@ impl PathBuffer { } } +pub struct PathBufferStream<'a> { + path_buffer: &'a PathBuffer, + endpoint_index: u32, + subpath_index: u32, +} + +impl<'a> PathBufferStream<'a> { + pub fn new<'b>(path_buffer: &'b PathBuffer) -> PathBufferStream<'b> { + PathBufferStream { + path_buffer: path_buffer, + endpoint_index: 0, + subpath_index: 0, + } + } +} + +impl<'a> Iterator for PathBufferStream<'a> { + type Item = PathSegment; + + fn next(&mut self) -> Option { + if self.subpath_index as usize == self.path_buffer.subpaths.len() { + return None + } + + let subpath = &self.path_buffer.subpaths[self.subpath_index as usize]; + if self.endpoint_index == subpath.last_endpoint_index { + self.subpath_index += 1; + return Some(PathSegment::ClosePath) + } + + let endpoint = &self.path_buffer.endpoints[self.endpoint_index as usize]; + self.endpoint_index += 1; + + if self.endpoint_index == subpath.first_endpoint_index { + return Some(PathSegment::MoveTo(endpoint.position)) + } + + if endpoint.control_point_index == u32::MAX { + return Some(PathSegment::LineTo(endpoint.position)) + } + + let control_point = &self.path_buffer + .control_points[endpoint.control_point_index as usize]; + Some(PathSegment::CurveTo(*control_point, endpoint.position)) + } +} + #[repr(C)] #[derive(Debug, Clone, Copy, Serialize, Deserialize)] pub struct Endpoint {