Implement some missing geometry methods, and remove the `Path` type.

With the removal of the `Path` type, partitioning multiple paths becomes the responsibility of the user of the library. The reason is that users of the partitioner will frequently want to parallelize over paths via e.g. rayon or Grand Central Dispatch, and we shouldn't mandate a particular parallelism technique.
This commit is contained in:
Patrick Walton 2017-07-02 15:48:57 -04:00
parent 2fde50113c
commit d695a3d11f
5 changed files with 93 additions and 85 deletions

View File

@ -3,7 +3,7 @@
use partitioner::Partitioner;
use std::mem;
use std::slice;
use {Bezieroid, ControlPoints, Endpoint, Path, Subpath};
use {Bezieroid, ControlPoints, Endpoint, Subpath};
#[no_mangle]
pub unsafe extern fn pf_partitioner_new(endpoints: *const Endpoint,
@ -11,16 +11,13 @@ pub unsafe extern fn pf_partitioner_new(endpoints: *const Endpoint,
control_points: *const ControlPoints,
control_points_count: u32,
subpaths: *const Subpath,
subpath_count: u32,
paths: *const Path,
path_count: u32)
subpath_count: u32)
-> *mut Partitioner<'static> {
let mut partitioner =
Box::new(Partitioner::new(slice::from_raw_parts(endpoints, endpoint_count as usize),
slice::from_raw_parts(control_points,
control_points_count as usize),
slice::from_raw_parts(subpaths, subpath_count as usize),
slice::from_raw_parts(paths, path_count as usize)));
slice::from_raw_parts(subpaths, subpath_count as usize)));
let partitioner_ptr: *mut Partitioner<'static> = &mut *partitioner;
mem::forget(partitioner);
partitioner_ptr
@ -31,6 +28,20 @@ pub unsafe extern fn pf_partitioner_destroy<'a>(partitioner: *mut Partitioner<'a
drop(mem::transmute::<*mut Partitioner<'a>, Box<Partitioner>>(partitioner))
}
#[no_mangle]
pub unsafe extern fn pf_partitioner_reset<'a>(partitioner: *mut Partitioner<'a>,
new_endpoints: *const Endpoint,
new_endpoint_count: u32,
new_control_points: *const ControlPoints,
new_control_points_count: u32,
new_subpaths: *const Subpath,
new_subpath_count: u32) {
(*partitioner).reset(slice::from_raw_parts(new_endpoints, new_endpoint_count as usize),
slice::from_raw_parts(new_control_points,
new_control_points_count as usize),
slice::from_raw_parts(new_subpaths, new_subpath_count as usize))
}
#[no_mangle]
pub unsafe extern fn pf_partitioner_partition<'a>(partitioner: *mut Partitioner<'a>) {
(*partitioner).partition()

View File

@ -1,7 +1,9 @@
// partitionfinder/geometry.rs
use euclid::Point2D;
use euclid::approxeq::ApproxEq;
use euclid::{Point2D, Vector2D};
const NEWTON_RAPHSON_ITERATIONS: u8 = 8;
// https://stackoverflow.com/a/565282
pub fn line_line_crossing_point(a_p0: &Point2D<f32>,
@ -27,7 +29,7 @@ pub fn line_line_crossing_point(a_p0: &Point2D<f32>,
return None
}
return Some(p + r * t)
Some(p + r * t)
}
// TODO(pcwalton): Implement this.
@ -60,13 +62,38 @@ fn sample_cubic_bezier(t: f32,
p2: &Point2D<f32>,
p3: &Point2D<f32>)
-> Point2D<f32> {
// TODO(pcwalton)
Point2D::new(0.0, 0.0)
let (p0p1, p1p2, p2p3) = (p0.lerp(*p1, t), p1.lerp(*p2, t), p2.lerp(*p3, t));
let (p0p1p2, p1p2p3) = (p0p1.lerp(p1p2, t), p1p2.lerp(p2p3, t));
p0p1p2.lerp(p1p2p3, t)
}
fn sample_cubic_bezier_deriv(t: f32,
p0: &Point2D<f32>,
p1: &Point2D<f32>,
p2: &Point2D<f32>,
p3: &Point2D<f32>)
-> Vector2D<f32> {
// https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B.C3.A9zier_curves
// FIXME(pcwalton): Can this be made faster?
let tt = 1.0 - t;
return (*p1 - *p0) * 3.0 * tt * tt + (*p2 - *p1) * 6.0 * tt * t + (*p3 - *p2) * 3.0 * t * t
}
pub fn solve_line_y_for_x(x: f32, a: &Point2D<f32>, b: &Point2D<f32>) -> f32 {
// TODO(pcwalton)
0.0
a.lerp(*b, (x - a.x) / (b.x - a.x)).y
}
fn newton_raphson<F, DFDX>(f: F, dfdx: DFDX, mut x_guess: f32) -> Option<f32>
where F: Fn(f32) -> f32, DFDX: Fn(f32) -> f32 {
for _ in 0..NEWTON_RAPHSON_ITERATIONS {
let y = f(x_guess);
if y.approx_eq(&0.0) {
return Some(x_guess)
}
let yy = dfdx(x_guess);
x_guess -= y / yy
}
None
}
pub fn solve_cubic_bezier_t_for_x(x: f32,
@ -74,9 +101,10 @@ pub fn solve_cubic_bezier_t_for_x(x: f32,
p1: &Point2D<f32>,
p2: &Point2D<f32>,
p3: &Point2D<f32>)
-> f32 {
// TODO(pcwalton)
0.0
-> Option<f32> {
newton_raphson(|t| sample_cubic_bezier(t, p0, p1, p2, p3).x - x,
|t| sample_cubic_bezier_deriv(t, p0, p1, p2, p3).x,
0.5)
}
pub fn solve_cubic_bezier_y_for_x(x: f32,
@ -84,6 +112,6 @@ pub fn solve_cubic_bezier_y_for_x(x: f32,
p1: &Point2D<f32>,
p2: &Point2D<f32>,
p3: &Point2D<f32>)
-> f32 {
sample_cubic_bezier(solve_cubic_bezier_t_for_x(x, p0, p1, p2, p3), p0, p1, p2, p3).y
-> Option<f32> {
solve_cubic_bezier_t_for_x(x, p0, p1, p2, p3).map(|t| sample_cubic_bezier(t, p0, p1, p2, p3).y)
}

View File

@ -45,19 +45,3 @@ pub struct Subpath {
pub first_endpoint_index: u32,
pub path_index: u32,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct Path {
pub first_subpath_index: u32,
pub fill_color: ColorU8,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct ColorU8 {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}

View File

@ -6,13 +6,12 @@ use geometry;
use std::collections::BinaryHeap;
use std::cmp::{self, Ordering};
use std::u32;
use {Bezieroid, ControlPoints, Endpoint, Path, Subpath};
use {Bezieroid, ControlPoints, Endpoint, Subpath};
pub struct Partitioner<'a> {
endpoints: &'a [Endpoint],
control_points: &'a [ControlPoints],
subpaths: &'a [Subpath],
paths: &'a [Path],
bezieroids: Vec<Bezieroid>,
@ -25,14 +24,12 @@ impl<'a> Partitioner<'a> {
#[inline]
pub fn new<'b>(endpoints: &'b [Endpoint],
control_points: &'b [ControlPoints],
subpaths: &'b [Subpath],
paths: &'b [Path])
subpaths: &'b [Subpath])
-> Partitioner<'b> {
Partitioner {
endpoints: endpoints,
control_points: control_points,
subpaths: subpaths,
paths: paths,
bezieroids: vec![],
@ -42,11 +39,23 @@ impl<'a> Partitioner<'a> {
}
}
pub fn reset(&mut self,
new_endpoints: &'a [Endpoint],
new_control_points: &'a [ControlPoints],
new_subpaths: &'a [Subpath]) {
self.endpoints = new_endpoints;
self.control_points = new_control_points;
self.subpaths = new_subpaths;
self.bezieroids.clear();
self.heap.clear();
self.visited_points.clear();
self.active_edges.clear();
}
pub fn partition(&mut self) {
for path_index in (0..self.paths.len() as u32).rev() {
self.init_heap_for_path(path_index);
while self.process_next_point() {}
}
self.init_heap();
while self.process_next_point() {}
}
#[inline]
@ -221,21 +230,14 @@ impl<'a> Partitioner<'a> {
}
}
fn init_heap_for_path(&mut self, path_index: u32) {
let path = &self.paths[path_index as usize];
let first_subpath_index = path.first_subpath_index;
let last_subpath_index = self.last_subpath_index_of_path(path_index);
for subpath_index in first_subpath_index..last_subpath_index {
let first_endpoint_index = self.subpaths[subpath_index as usize].first_endpoint_index;
let last_endpoint_index = self.last_endpoint_index_of_path(path_index);
for endpoint_index in first_endpoint_index..last_endpoint_index {
match self.classify_endpoint(endpoint_index) {
EndpointClass::Min => {
let new_point = self.create_point_from_endpoint(endpoint_index);
self.heap.push(new_point)
}
EndpointClass::Regular | EndpointClass::Max => {}
fn init_heap(&mut self) {
for endpoint_index in 0..(self.endpoints.len() as u32) {
match self.classify_endpoint(endpoint_index) {
EndpointClass::Min => {
let new_point = self.create_point_from_endpoint(endpoint_index);
self.heap.push(new_point)
}
EndpointClass::Regular | EndpointClass::Max => {}
}
}
}
@ -344,12 +346,13 @@ impl<'a> Partitioner<'a> {
(x - prev_endpoint.position.x) / x_vector
}
Some(control_points_index) => {
// FIXME(pcwalton): Is `unwrap_or(0.0)` sensible?
let control_points = &self.control_points[control_points_index as usize];
geometry::solve_cubic_bezier_t_for_x(x,
&prev_endpoint.position,
&control_points.point1,
&control_points.point2,
&next_endpoint.position)
&next_endpoint.position).unwrap_or(0.0)
}
}
}
@ -388,11 +391,12 @@ impl<'a> Partitioner<'a> {
let control_points_index = self.control_points_index(next_endpoint_index)
.expect("Edge not a cubic bezier!");
let control_points = &self.control_points[control_points_index as usize];
// FIXME(pcwalton): Is `.unwrap_or(0.0)` sensible?
geometry::solve_cubic_bezier_y_for_x(x,
&prev_endpoint.position,
&control_points.point1,
&control_points.point2,
&next_endpoint.position)
&next_endpoint.position).unwrap_or(0.0)
}
fn control_points_index(&self, next_endpoint_index: u32) -> Option<u32> {
@ -573,17 +577,6 @@ impl<'a> Partitioner<'a> {
None => self.endpoints.len() as u32,
}
}
fn last_subpath_index_of_path(&self, path_index: u32) -> u32 {
match self.paths.get(path_index as usize + 1) {
Some(path) => path.first_subpath_index,
None => self.subpaths.len() as u32,
}
}
fn last_endpoint_index_of_path(&self, path_index: u32) -> u32 {
self.last_endpoint_index_of_subpath(self.last_subpath_index_of_path(path_index))
}
}
#[derive(Debug, Clone, Copy)]

View File

@ -15,12 +15,6 @@ struct pf_point2d_f32 {
typedef struct pf_point2d_f32 pf_point2d_f32_t;
struct pf_color_u8 {
uint8_t r, g, b, a;
};
typedef struct pf_color_u8 pf_color_u8_t;
struct pf_bezieroid {
uint32_t upper_prev_endpoint, upper_next_endpoint;
uint32_t lower_prev_endpoint, lower_next_endpoint;
@ -50,13 +44,6 @@ struct pf_subpath {
typedef struct pf_subpath pf_subpath_t;
struct pf_path {
uint32_t first_subpath_index;
pf_color_u8_t fill_color;
};
typedef struct pf_path pf_path_t;
struct pf_partitioner;
typedef struct pf_partitioner pf_partitioner_t;
@ -66,12 +53,17 @@ pf_partitioner_t *pf_partitioner_new(const pf_endpoint_t *endpoints,
const pf_control_points_t *control_points,
uint32_t control_points_count,
const pf_subpath_t *subpaths,
uint32_t subpath_count,
const pf_path_t *paths,
uint32_t path_count);
uint32_t subpath_count);
void pf_partitioner_destroy(pf_partitioner_t *partitioner);
void pf_partitioner_reset(const pf_endpoint_t *new_endpoints,
uint32_t new_endpoint_count,
const pf_control_points_t *new_control_points,
uint32_t new_control_points_count,
const pf_subpath_t *new_subpaths,
uint32_t new_subpath_count);
void pf_partitioner_partition(pf_partitioner_t *partitioner);
const pf_bezieroid_t *pf_partitioner_bezieroids(pf_partitioner_t *partitioner,