Split paths into monotonic segments before partitioning them in the demo
This commit is contained in:
parent
c3e02d6faa
commit
a7d75f913c
|
@ -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<PartitionFontRequest>)
|
|||
|
||||
// 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<PartitionSvgPathsRequest>)
|
|||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<f32>,
|
|||
None
|
||||
}
|
||||
|
||||
pub fn sample_quadratic_bezier(t: f32, p0: &Point2D<f32>, p1: &Point2D<f32>, p2: &Point2D<f32>)
|
||||
-> Point2D<f32> {
|
||||
p0.lerp(*p1, t).lerp(p1.lerp(*p2, t), t)
|
||||
}
|
||||
|
||||
pub fn solve_line_t_for_x(x: f32, a: &Point2D<f32>, b: &Point2D<f32>) -> f32 {
|
||||
if b.x == a.x {
|
||||
0.0
|
||||
|
@ -121,7 +117,7 @@ pub fn solve_quadratic_bezier_y_for_x(x: f32,
|
|||
p1: &Point2D<f32>,
|
||||
p2: &Point2D<f32>)
|
||||
-> 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<f32> {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<PathSegment> {
|
||||
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 {
|
||||
|
|
Loading…
Reference in New Issue