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 euclid::{Point2D, Size2D, Transform2D};
|
||||||
use pathfinder_font_renderer::{FontContext, FontInstanceKey, FontKey, GlyphKey};
|
use pathfinder_font_renderer::{FontContext, FontInstanceKey, FontKey, GlyphKey};
|
||||||
use pathfinder_partitioner::partitioner::Partitioner;
|
use pathfinder_partitioner::partitioner::Partitioner;
|
||||||
|
use pathfinder_path_utils::monotonic::MonotonicPathSegmentStream;
|
||||||
use pathfinder_path_utils::stroke;
|
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::http::{ContentType, Status};
|
||||||
use rocket::request::Request;
|
use rocket::request::Request;
|
||||||
use rocket::response::{NamedFile, Redirect, Responder, Response};
|
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.
|
// This might fail; if so, just leave it blank.
|
||||||
if let Ok(glyph_outline) = font_context.glyph_outline(&font_instance_key, &glyph_key) {
|
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();
|
let last_subpath_index = path_buffer.subpaths.len();
|
||||||
|
@ -497,9 +500,15 @@ fn partition_svg_paths(request: Json<PartitionSvgPathsRequest>)
|
||||||
}
|
}
|
||||||
|
|
||||||
match path.kind {
|
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) => {
|
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::Point2D;
|
||||||
use euclid::approxeq::ApproxEq;
|
use euclid::approxeq::ApproxEq;
|
||||||
|
use pathfinder_path_utils::curve::Curve;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
pub(crate) trait ApproxOrdered {
|
pub(crate) trait ApproxOrdered {
|
||||||
|
@ -85,11 +86,6 @@ pub fn quadratic_bezier_quadratic_bezier_crossing_point(_a_p0: &Point2D<f32>,
|
||||||
None
|
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 {
|
pub fn solve_line_t_for_x(x: f32, a: &Point2D<f32>, b: &Point2D<f32>) -> f32 {
|
||||||
if b.x == a.x {
|
if b.x == a.x {
|
||||||
0.0
|
0.0
|
||||||
|
@ -121,7 +117,7 @@ pub fn solve_quadratic_bezier_y_for_x(x: f32,
|
||||||
p1: &Point2D<f32>,
|
p1: &Point2D<f32>,
|
||||||
p2: &Point2D<f32>)
|
p2: &Point2D<f32>)
|
||||||
-> 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> {
|
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 geometry::{self, SubdividedQuadraticBezier};
|
||||||
use log::LogLevel;
|
use log::LogLevel;
|
||||||
use pathfinder_path_utils::PathBuffer;
|
use pathfinder_path_utils::PathBuffer;
|
||||||
|
use pathfinder_path_utils::curve::Curve;
|
||||||
use std::collections::BinaryHeap;
|
use std::collections::BinaryHeap;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::f32;
|
use std::f32;
|
||||||
|
@ -712,10 +713,7 @@ impl<'a> Partitioner<'a> {
|
||||||
}
|
}
|
||||||
control_point_vertex_index => {
|
control_point_vertex_index => {
|
||||||
let control_point = &self.b_vertex_positions[control_point_vertex_index as usize];
|
let control_point = &self.b_vertex_positions[control_point_vertex_index as usize];
|
||||||
geometry::sample_quadratic_bezier(t,
|
Curve::new(left_vertex_position, control_point, right_endpoint_position).sample(t)
|
||||||
left_vertex_position,
|
|
||||||
control_point,
|
|
||||||
right_endpoint_position)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub struct Endpoint {
|
pub struct Endpoint {
|
||||||
|
|
Loading…
Reference in New Issue