Initial commit of Pathfinder 2, still incomplete

This commit is contained in:
Patrick Walton 2017-06-28 14:14:40 -07:00
parent 93c928bf5c
commit 2fde50113c
7 changed files with 968 additions and 0 deletions

2
partitionfinder/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
target
Cargo.lock

View File

@ -0,0 +1,8 @@
[package]
name = "partitionfinder"
version = "0.1.0"
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
[dependencies]
bit-vec = "0.4"
euclid = "0.15"

View File

@ -0,0 +1,48 @@
// partitionfinder/capi.rs
use partitioner::Partitioner;
use std::mem;
use std::slice;
use {Bezieroid, ControlPoints, Endpoint, Path, Subpath};
#[no_mangle]
pub unsafe extern fn pf_partitioner_new(endpoints: *const Endpoint,
endpoint_count: u32,
control_points: *const ControlPoints,
control_points_count: u32,
subpaths: *const Subpath,
subpath_count: u32,
paths: *const Path,
path_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)));
let partitioner_ptr: *mut Partitioner<'static> = &mut *partitioner;
mem::forget(partitioner);
partitioner_ptr
}
#[no_mangle]
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_partition<'a>(partitioner: *mut Partitioner<'a>) {
(*partitioner).partition()
}
#[no_mangle]
pub unsafe extern fn pf_partitioner_bezieroids<'a>(partitioner: *mut Partitioner<'a>,
out_bezieroid_count: *mut u32)
-> *const Bezieroid {
let bezieroids = (*partitioner).bezieroids();
if !out_bezieroid_count.is_null() {
*out_bezieroid_count = bezieroids.len() as u32
}
bezieroids.as_ptr()
}

View File

@ -0,0 +1,89 @@
// partitionfinder/geometry.rs
use euclid::Point2D;
use euclid::approxeq::ApproxEq;
// https://stackoverflow.com/a/565282
pub fn line_line_crossing_point(a_p0: &Point2D<f32>,
a_p1: &Point2D<f32>,
b_p0: &Point2D<f32>,
b_p1: &Point2D<f32>)
-> Option<Point2D<f32>> {
let (p, r) = (*a_p0, *a_p1 - *a_p0);
let (q, s) = (*b_p0, *b_p1 - *b_p0);
let rs = r.cross(s);
if rs.approx_eq(&0.0) {
return None
}
let t = (q - p).cross(s) / rs;
if t < f32::approx_epsilon() || t > 1.0f32 - f32::approx_epsilon() {
return None
}
let u = (q - p).cross(r) / rs;
if u < f32::approx_epsilon() || u > 1.0f32 - f32::approx_epsilon() {
return None
}
return Some(p + r * t)
}
// TODO(pcwalton): Implement this.
pub fn line_cubic_bezier_crossing_point(_a_p0: &Point2D<f32>,
_a_p1: &Point2D<f32>,
_b_p0: &Point2D<f32>,
_b_p1: &Point2D<f32>,
_b_p2: &Point2D<f32>,
_b_p3: &Point2D<f32>)
-> Option<Point2D<f32>> {
None
}
// TODO(pcwalton): Implement this.
pub fn cubic_bezier_cubic_bezier_crossing_point(_a_p0: &Point2D<f32>,
_a_p1: &Point2D<f32>,
_a_p2: &Point2D<f32>,
_a_p3: &Point2D<f32>,
_b_p0: &Point2D<f32>,
_b_p1: &Point2D<f32>,
_b_p2: &Point2D<f32>,
_b_p3: &Point2D<f32>)
-> Option<Point2D<f32>> {
None
}
fn sample_cubic_bezier(t: f32,
p0: &Point2D<f32>,
p1: &Point2D<f32>,
p2: &Point2D<f32>,
p3: &Point2D<f32>)
-> Point2D<f32> {
// TODO(pcwalton)
Point2D::new(0.0, 0.0)
}
pub fn solve_line_y_for_x(x: f32, a: &Point2D<f32>, b: &Point2D<f32>) -> f32 {
// TODO(pcwalton)
0.0
}
pub fn solve_cubic_bezier_t_for_x(x: f32,
p0: &Point2D<f32>,
p1: &Point2D<f32>,
p2: &Point2D<f32>,
p3: &Point2D<f32>)
-> f32 {
// TODO(pcwalton)
0.0
}
pub fn solve_cubic_bezier_y_for_x(x: f32,
p0: &Point2D<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
}

View File

@ -0,0 +1,63 @@
// partitionfinder/lib.rs
extern crate bit_vec;
extern crate euclid;
use euclid::Point2D;
use std::u32;
pub mod capi;
pub mod geometry;
pub mod partitioner;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct Bezieroid {
pub upper_prev_endpoint: u32,
pub upper_next_endpoint: u32,
pub lower_prev_endpoint: u32,
pub lower_next_endpoint: u32,
pub upper_left_time: f32,
pub upper_right_time: f32,
pub lower_left_time: f32,
pub lower_right_time: f32,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct Endpoint {
pub position: Point2D<f32>,
/// `u32::MAX` if not present.
pub control_points_index: u32,
pub subpath_index: u32,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct ControlPoints {
pub point1: Point2D<f32>,
pub point2: Point2D<f32>,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
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

@ -0,0 +1,674 @@
// partitionfinder/partitioner.rs
use bit_vec::BitVec;
use euclid::Point2D;
use geometry;
use std::collections::BinaryHeap;
use std::cmp::{self, Ordering};
use std::u32;
use {Bezieroid, ControlPoints, Endpoint, Path, Subpath};
pub struct Partitioner<'a> {
endpoints: &'a [Endpoint],
control_points: &'a [ControlPoints],
subpaths: &'a [Subpath],
paths: &'a [Path],
bezieroids: Vec<Bezieroid>,
heap: BinaryHeap<Point>,
visited_points: BitVec,
active_edges: Vec<ActiveEdge>,
}
impl<'a> Partitioner<'a> {
#[inline]
pub fn new<'b>(endpoints: &'b [Endpoint],
control_points: &'b [ControlPoints],
subpaths: &'b [Subpath],
paths: &'b [Path])
-> Partitioner<'b> {
Partitioner {
endpoints: endpoints,
control_points: control_points,
subpaths: subpaths,
paths: paths,
bezieroids: vec![],
heap: BinaryHeap::new(),
visited_points: BitVec::new(),
active_edges: vec![],
}
}
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() {}
}
}
#[inline]
pub fn bezieroids(&self) -> &[Bezieroid] {
&self.bezieroids
}
fn process_next_point(&mut self) -> bool {
let point = match self.heap.peek() {
Some(point) => *point,
None => return false,
};
if self.already_visited_point(&point) {
self.heap.pop();
return true
}
self.mark_point_as_visited(&point);
let matching_active_edges = self.find_right_point_in_active_edge_list(point.endpoint_index);
match point.point_type {
PointType::Endpoint => {
match matching_active_edges.count {
0 => self.process_min_endpoint(point.endpoint_index),
1 => {
self.process_regular_endpoint(point.endpoint_index,
matching_active_edges.indices[0])
}
2 => {
self.process_max_endpoint(point.endpoint_index,
matching_active_edges.indices)
}
_ => debug_assert!(false),
}
}
PointType::CrossingBelow => {
debug_assert!(matching_active_edges.count > 0);
self.process_crossing_point(point.position.x, matching_active_edges.indices[0])
}
}
true
}
fn process_min_endpoint(&mut self, endpoint_index: u32) {
let next_active_edge_index = self.find_point_between_active_edges(endpoint_index);
let endpoint = &self.endpoints[endpoint_index as usize];
if self.should_fill_above_active_edge(next_active_edge_index) {
self.emit_bezieroid_above(next_active_edge_index, endpoint.position.x)
}
self.add_new_edges_for_min_point(endpoint_index, next_active_edge_index);
let prev_endpoint_index = self.prev_endpoint_of(endpoint_index);
let next_endpoint_index = self.next_endpoint_of(endpoint_index);
let new_point = self.create_point_from_endpoint(next_endpoint_index);
*self.heap.peek_mut().unwrap() = new_point;
if next_endpoint_index != prev_endpoint_index {
let new_point = self.create_point_from_endpoint(prev_endpoint_index);
self.heap.push(new_point)
}
self.add_crossings_to_heap_if_necessary(next_active_edge_index + 0,
next_active_edge_index + 2)
}
fn process_regular_endpoint(&mut self, endpoint_index: u32, active_edge_index: u32) {
let endpoint = &self.endpoints[endpoint_index as usize];
if self.should_fill_below_active_edge(active_edge_index) {
self.emit_bezieroid_below(active_edge_index, endpoint.position.x)
}
if self.should_fill_above_active_edge(active_edge_index) {
self.emit_bezieroid_above(active_edge_index, endpoint.position.x)
}
let prev_endpoint_index = self.prev_endpoint_of(endpoint_index);
let next_endpoint_index = self.next_endpoint_of(endpoint_index);
{
let active_edge = &mut self.active_edges[active_edge_index as usize];
active_edge.left_endpoint_index = active_edge.right_endpoint_index;
if active_edge.left_to_right {
active_edge.right_endpoint_index = next_endpoint_index;
active_edge.time = 0.0
} else {
active_edge.right_endpoint_index = prev_endpoint_index;
active_edge.time = 1.0
}
}
let right_endpoint_index = self.active_edges[active_edge_index as usize]
.right_endpoint_index;
let new_point = self.create_point_from_endpoint(right_endpoint_index);
*self.heap.peek_mut().unwrap() = new_point;
self.add_crossings_to_heap_if_necessary(active_edge_index + 0, active_edge_index + 2)
}
fn process_max_endpoint(&mut self, endpoint_index: u32, active_edge_indices: [u32; 2]) {
debug_assert!(active_edge_indices[0] < active_edge_indices[1],
"Matching active edge indices in wrong order when processing MAX point");
let endpoint = &self.endpoints[endpoint_index as usize];
if self.should_fill_above_active_edge(active_edge_indices[0]) {
self.emit_bezieroid_above(active_edge_indices[0], endpoint.position.x)
}
if self.should_fill_above_active_edge(active_edge_indices[1]) {
self.emit_bezieroid_above(active_edge_indices[1], endpoint.position.x)
}
if self.should_fill_below_active_edge(active_edge_indices[1]) {
self.emit_bezieroid_below(active_edge_indices[1], endpoint.position.x)
}
self.heap.pop();
// FIXME(pcwalton): This is twice as slow as it needs to be.
self.active_edges.remove(active_edge_indices[1] as usize);
self.active_edges.remove(active_edge_indices[0] as usize);
self.add_crossings_to_heap_if_necessary(active_edge_indices[0], active_edge_indices[0] + 2)
}
fn process_crossing_point(&mut self, x: f32, upper_active_edge_index: u32) {
if self.should_fill_above_active_edge(upper_active_edge_index) {
self.emit_bezieroid_above(upper_active_edge_index, x)
}
if self.should_fill_below_active_edge(upper_active_edge_index) {
self.emit_bezieroid_below(upper_active_edge_index, x)
}
// Swap the two edges.
let lower_active_edge_index = upper_active_edge_index + 1;
self.active_edges.swap(upper_active_edge_index as usize, lower_active_edge_index as usize)
}
fn add_new_edges_for_min_point(&mut self, endpoint_index: u32, next_active_edge_index: u32) {
// FIXME(pcwalton): This is twice as slow as it needs to be.
self.active_edges.insert(next_active_edge_index as usize, ActiveEdge::default());
self.active_edges.insert(next_active_edge_index as usize, ActiveEdge::default());
let prev_endpoint_index = self.prev_endpoint_of(endpoint_index);
let next_endpoint_index = self.next_endpoint_of(endpoint_index);
let new_active_edges = &mut self.active_edges[next_active_edge_index as usize..
next_active_edge_index as usize + 2];
let endpoint = &self.endpoints[endpoint_index as usize];
let prev_endpoint = &self.endpoints[prev_endpoint_index as usize];
let next_endpoint = &self.endpoints[next_endpoint_index as usize];
// TODO(pcwalton): There's a faster way to do this with no divisions, almost certainly.
let prev_vector = (prev_endpoint.position - endpoint.position).normalize();
let next_vector = (next_endpoint.position - endpoint.position).normalize();
if prev_vector.y <= next_vector.y {
new_active_edges[0].right_endpoint_index = prev_endpoint_index;
new_active_edges[1].right_endpoint_index = next_endpoint_index;
new_active_edges[0].left_to_right = false;
new_active_edges[1].left_to_right = true;
new_active_edges[0].time = 1.0;
new_active_edges[1].time = 0.0;
} else {
new_active_edges[0].right_endpoint_index = next_endpoint_index;
new_active_edges[1].right_endpoint_index = prev_endpoint_index;
new_active_edges[0].left_to_right = true;
new_active_edges[1].left_to_right = false;
new_active_edges[0].time = 0.0;
new_active_edges[1].time = 1.0;
}
}
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 should_fill_below_active_edge(&self, active_edge_index: u32) -> bool {
// TODO(pcwalton): Support the winding fill rule.
active_edge_index % 2 == 0
}
fn should_fill_above_active_edge(&self, active_edge_index: u32) -> bool {
// TODO(pcwalton): Support the winding fill rule.
active_edge_index % 2 == 1
}
fn emit_bezieroid_below(&mut self, upper_active_edge_index: u32, right_x: f32) {
self.emit_bezieroid_above(upper_active_edge_index + 1, right_x)
}
fn emit_bezieroid_above(&mut self, lower_active_edge_index: u32, right_x: f32) {
// TODO(pcwalton): Assert that the green X position is the same on both edges.
debug_assert!(lower_active_edge_index > 0,
"Can't emit bezieroids above the top active edge");
let upper_active_edge_index = lower_active_edge_index - 1;
let new_bezieroid;
{
let lower_active_edge = &self.active_edges[lower_active_edge_index as usize];
let upper_active_edge = &self.active_edges[upper_active_edge_index as usize];
new_bezieroid = Bezieroid {
upper_prev_endpoint: upper_active_edge.prev_endpoint_index(),
upper_next_endpoint: upper_active_edge.next_endpoint_index(),
lower_prev_endpoint: lower_active_edge.prev_endpoint_index(),
lower_next_endpoint: lower_active_edge.next_endpoint_index(),
upper_left_time: upper_active_edge.time,
upper_right_time: self.solve_t_for_active_edge(upper_active_edge_index, right_x),
lower_left_time: lower_active_edge.time,
lower_right_time: self.solve_t_for_active_edge(lower_active_edge_index, right_x),
};
self.bezieroids.push(new_bezieroid);
}
self.active_edges[upper_active_edge_index as usize].time = new_bezieroid.upper_right_time;
self.active_edges[lower_active_edge_index as usize].time = new_bezieroid.lower_right_time;
}
fn already_visited_point(&self, point: &Point) -> bool {
// FIXME(pcwalton): This makes the visited vector too big.
let index = point.endpoint_index as usize * 2 + point.point_type as usize;
match self.visited_points.get(index) {
None => false,
Some(visited) => visited,
}
}
fn mark_point_as_visited(&mut self, point: &Point) {
// FIXME(pcwalton): This makes the visited vector too big.
self.visited_points.set(point.endpoint_index as usize * 2 + point.point_type as usize,
true)
}
fn find_right_point_in_active_edge_list(&self, endpoint_index: u32) -> MatchingActiveEdges {
let mut matching_active_edges = MatchingActiveEdges {
indices: [0, 0],
count: 0,
};
for (active_edge_index, active_edge) in self.active_edges.iter().enumerate() {
if active_edge.right_endpoint_index == endpoint_index {
matching_active_edges.indices[matching_active_edges.count as usize] =
active_edge_index as u32;
matching_active_edges.count += 1;
if matching_active_edges.count == 2 {
break
}
}
}
matching_active_edges
}
fn classify_endpoint(&self, endpoint_index: u32) -> EndpointClass {
// Create temporary points just for the comparison.
let point = self.create_point_from_endpoint(endpoint_index);
let prev_point = self.create_point_from_endpoint(self.prev_endpoint_of(endpoint_index));
let next_point = self.create_point_from_endpoint(self.next_endpoint_of(endpoint_index));
match (prev_point.cmp(&point), next_point.cmp(&point)) {
(Ordering::Less, Ordering::Less) => EndpointClass::Max,
(Ordering::Less, _) | (_, Ordering::Less) => EndpointClass::Regular,
(_, _) => EndpointClass::Min,
}
}
fn solve_t_for_active_edge(&self, active_edge_index: u32, x: f32) -> f32 {
let active_edge = &self.active_edges[active_edge_index as usize];
let prev_endpoint_index = active_edge.prev_endpoint_index();
let next_endpoint_index = active_edge.next_endpoint_index();
let prev_endpoint = &self.endpoints[prev_endpoint_index as usize];
let next_endpoint = &self.endpoints[next_endpoint_index as usize];
match self.control_points_index(next_endpoint_index) {
None => {
let x_vector = next_endpoint.position.x - prev_endpoint.position.x;
(x - prev_endpoint.position.x) / x_vector
}
Some(control_points_index) => {
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)
}
}
}
fn find_point_between_active_edges(&self, endpoint_index: u32) -> u32 {
let endpoint = &self.endpoints[endpoint_index as usize];
match self.active_edges.iter().position(|active_edge| {
self.solve_active_edge_y_for_x(endpoint.position.x, active_edge) > endpoint.position.y
}) {
Some(active_edge_index) => active_edge_index as u32,
None => self.active_edges.len() as u32,
}
}
fn solve_active_edge_y_for_x(&self, x: f32, active_edge: &ActiveEdge) -> f32 {
let prev_endpoint_index = active_edge.prev_endpoint_index();
let next_endpoint_index = active_edge.next_endpoint_index();
if self.control_points_index(next_endpoint_index).is_none() {
self.solve_line_y_for_x(x, prev_endpoint_index, next_endpoint_index)
} else {
self.solve_cubic_bezier_y_for_x(x, prev_endpoint_index, next_endpoint_index)
}
}
fn solve_line_y_for_x(&self, x: f32, prev_endpoint_index: u32, next_endpoint_index: u32)
-> f32 {
geometry::solve_line_y_for_x(x,
&self.endpoints[prev_endpoint_index as usize].position,
&self.endpoints[next_endpoint_index as usize].position)
}
fn solve_cubic_bezier_y_for_x(&self, x: f32, prev_endpoint_index: u32, next_endpoint_index: u32)
-> f32 {
let prev_endpoint = &self.endpoints[prev_endpoint_index as usize];
let next_endpoint = &self.endpoints[next_endpoint_index as usize];
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];
geometry::solve_cubic_bezier_y_for_x(x,
&prev_endpoint.position,
&control_points.point1,
&control_points.point2,
&next_endpoint.position)
}
fn control_points_index(&self, next_endpoint_index: u32) -> Option<u32> {
match self.endpoints[next_endpoint_index as usize].control_points_index {
u32::MAX => None,
control_points_index => Some(control_points_index),
}
}
fn add_crossings_to_heap_if_necessary(&mut self,
mut first_active_edge_index: u32,
mut last_active_edge_index: u32) {
if self.active_edges.is_empty() {
return
}
first_active_edge_index = first_active_edge_index.checked_sub(1)
.unwrap_or(first_active_edge_index);
last_active_edge_index = cmp::min(last_active_edge_index + 1,
self.active_edges.len() as u32);
for (upper_active_edge_index, upper_active_edge) in
self.active_edges[(first_active_edge_index as usize)..
(last_active_edge_index as usize - 1)]
.iter()
.enumerate() {
let crossing_position =
match self.crossing_point_for_active_edge(upper_active_edge_index as u32) {
None => continue,
Some(crossing_point) => crossing_point,
};
let new_point = Point {
position: crossing_position,
endpoint_index: upper_active_edge.right_endpoint_index,
point_type: PointType::CrossingBelow,
};
self.heap.push(new_point);
}
}
fn crossing_point_for_active_edge(&self, upper_active_edge_index: u32)
-> Option<Point2D<f32>> {
let lower_active_edge_index = upper_active_edge_index + 1;
let upper_active_edge = &self.active_edges[upper_active_edge_index as usize];
let lower_active_edge = &self.active_edges[lower_active_edge_index as usize];
if upper_active_edge.left_endpoint_index == lower_active_edge.left_endpoint_index ||
upper_active_edge.right_endpoint_index == lower_active_edge.right_endpoint_index {
return None
}
let prev_upper_endpoint_index = upper_active_edge.prev_endpoint_index();
let next_upper_endpoint_index = upper_active_edge.next_endpoint_index();
let prev_lower_endpoint_index = lower_active_edge.prev_endpoint_index();
let next_lower_endpoint_index = lower_active_edge.next_endpoint_index();
let upper_control_points_index = self.endpoints[next_upper_endpoint_index as usize]
.control_points_index;
let lower_control_points_index = self.endpoints[next_lower_endpoint_index as usize]
.control_points_index;
match (lower_control_points_index, upper_control_points_index) {
(u32::MAX, u32::MAX) => {
self.line_line_crossing_point(prev_upper_endpoint_index,
next_upper_endpoint_index,
prev_lower_endpoint_index,
next_lower_endpoint_index)
}
(u32::MAX, _) => {
self.line_cubic_bezier_crossing_point(prev_upper_endpoint_index,
next_upper_endpoint_index,
next_lower_endpoint_index,
next_lower_endpoint_index)
}
(_, u32::MAX) => {
self.line_cubic_bezier_crossing_point(prev_lower_endpoint_index,
next_lower_endpoint_index,
next_upper_endpoint_index,
next_upper_endpoint_index)
}
(_, _) => {
self.cubic_bezier_cubic_bezier_crossing_point(prev_upper_endpoint_index,
next_upper_endpoint_index,
prev_lower_endpoint_index,
next_lower_endpoint_index)
}
}
}
fn line_line_crossing_point(&self,
prev_upper_endpoint_index: u32,
next_upper_endpoint_index: u32,
prev_lower_endpoint_index: u32,
next_lower_endpoint_index: u32)
-> Option<Point2D<f32>> {
let endpoints = &self.endpoints;
geometry::line_line_crossing_point(&endpoints[prev_upper_endpoint_index as usize].position,
&endpoints[next_upper_endpoint_index as usize].position,
&endpoints[prev_lower_endpoint_index as usize].position,
&endpoints[next_lower_endpoint_index as usize].position)
}
fn line_cubic_bezier_crossing_point(&self,
prev_line_endpoint_index: u32,
next_line_endpoint_index: u32,
prev_bezier_endpoint_index: u32,
next_bezier_endpoint_index: u32)
-> Option<Point2D<f32>> {
let control_points_index = self.control_points_index(next_bezier_endpoint_index)
.expect("Edge not a cubic Bezier!");
let control_points = &self.control_points[control_points_index as usize];
geometry::line_cubic_bezier_crossing_point(
&self.endpoints[prev_line_endpoint_index as usize].position,
&self.endpoints[next_line_endpoint_index as usize].position,
&self.endpoints[prev_bezier_endpoint_index as usize].position,
&control_points.point1,
&control_points.point2,
&self.endpoints[next_bezier_endpoint_index as usize].position)
}
fn cubic_bezier_cubic_bezier_crossing_point(&self,
prev_upper_endpoint_index: u32,
next_upper_endpoint_index: u32,
prev_lower_endpoint_index: u32,
next_lower_endpoint_index: u32)
-> Option<Point2D<f32>> {
let upper_control_points_index = self.control_points_index(next_upper_endpoint_index)
.expect("Upper edge not a cubic Bezier!");
let upper_control_points = &self.control_points[upper_control_points_index as usize];
let lower_control_points_index = self.control_points_index(next_lower_endpoint_index)
.expect("Lower edge not a cubic Bezier!");
let lower_control_points = &self.control_points[lower_control_points_index as usize];
geometry::cubic_bezier_cubic_bezier_crossing_point(
&self.endpoints[prev_upper_endpoint_index as usize].position,
&upper_control_points.point1,
&upper_control_points.point2,
&self.endpoints[next_upper_endpoint_index as usize].position,
&self.endpoints[prev_lower_endpoint_index as usize].position,
&lower_control_points.point1,
&lower_control_points.point2,
&self.endpoints[next_lower_endpoint_index as usize].position)
}
fn prev_endpoint_of(&self, endpoint_index: u32) -> u32 {
let endpoint = &self.endpoints[endpoint_index as usize];
let first_endpoint_index_of_subpath = self.subpaths[endpoint.subpath_index as usize]
.first_endpoint_index;
if endpoint_index > first_endpoint_index_of_subpath {
endpoint_index - 1
} else {
self.last_endpoint_index_of_subpath(endpoint.subpath_index) - 1
}
}
fn next_endpoint_of(&self, endpoint_index: u32) -> u32 {
let endpoint = &self.endpoints[endpoint_index as usize];
let last_endpoint_index_of_subpath =
self.last_endpoint_index_of_subpath(endpoint.subpath_index);
if endpoint_index + 1 < last_endpoint_index_of_subpath {
endpoint_index + 1
} else {
self.subpaths[endpoint.subpath_index as usize].first_endpoint_index
}
}
fn create_point_from_endpoint(&self, endpoint_index: u32) -> Point {
Point {
position: self.endpoints[endpoint_index as usize].position,
endpoint_index: endpoint_index,
point_type: PointType::Endpoint,
}
}
fn last_endpoint_index_of_subpath(&self, subpath_index: u32) -> u32 {
match self.subpaths.get(subpath_index as usize + 1) {
Some(subpath) => subpath.first_endpoint_index,
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)]
struct Point {
position: Point2D<f32>,
endpoint_index: u32,
point_type: PointType,
}
impl PartialEq for Point {
#[inline]
fn eq(&self, other: &Point) -> bool {
self.position == other.position && self.endpoint_index == other.endpoint_index
}
#[inline]
fn ne(&self, other: &Point) -> bool {
self.position != other.position || self.endpoint_index != other.endpoint_index
}
}
impl Eq for Point {}
impl PartialOrd for Point {
#[inline]
fn partial_cmp(&self, other: &Point) -> Option<Ordering> {
match self.position.x.partial_cmp(&other.position.x) {
None | Some(Ordering::Equal) => {}
Some(ordering) => return Some(ordering),
}
match self.position.y.partial_cmp(&other.position.y) {
None | Some(Ordering::Equal) => {}
Some(ordering) => return Some(ordering),
}
self.endpoint_index.partial_cmp(&other.endpoint_index)
}
}
impl Ord for Point {
#[inline]
fn cmp(&self, other: &Point) -> Ordering {
self.partial_cmp(other).unwrap_or(Ordering::Equal)
}
}
#[derive(Debug, Clone, Copy, Default)]
struct ActiveEdge {
left_endpoint_index: u32,
right_endpoint_index: u32,
time: f32,
left_to_right: bool,
}
impl ActiveEdge {
fn prev_endpoint_index(&self) -> u32 {
if self.left_to_right {
self.left_endpoint_index
} else {
self.right_endpoint_index
}
}
fn next_endpoint_index(&self) -> u32 {
if self.left_to_right {
self.right_endpoint_index
} else {
self.left_endpoint_index
}
}
}
#[derive(Debug, Clone, Copy)]
enum PointType {
Endpoint = 0,
CrossingBelow = 1,
}
#[derive(Debug, Clone, Copy)]
enum EndpointClass {
Min,
Regular,
Max,
}
#[derive(Debug, Clone, Copy)]
struct MatchingActiveEdges {
indices: [u32; 2],
count: u8,
}

View File

@ -0,0 +1,84 @@
// partitionfinder/partitionfinder.h
#ifndef PARTITIONFINDER_H
#define PARTITIONFINDER_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
struct pf_point2d_f32 {
float x, y;
};
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;
uint32_t upper_left_time, upper_right_time;
uint32_t lower_left_time, lower_right_time;
};
typedef struct pf_bezieroid pf_bezieroid_t;
struct pf_endpoint {
pf_point2d_f32_t position;
uint32_t control_points_index;
uint32_t subpath_index;
};
typedef struct pf_endpoint pf_endpoint_t;
struct pf_control_points {
pf_point2d_f32_t point1, point2;
};
typedef struct pf_control_points pf_control_points_t;
struct pf_subpath {
uint32_t first_endpoint_index, path_index;
};
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;
pf_partitioner_t *pf_partitioner_new(const pf_endpoint_t *endpoints,
uint32_t endpoint_count,
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);
void pf_partitioner_destroy(pf_partitioner_t *partitioner);
void pf_partitioner_partition(pf_partitioner_t *partitioner);
const pf_bezieroid_t *pf_partitioner_bezieroids(pf_partitioner_t *partitioner,
uint32_t *out_bezieroid_count);
#ifdef __cplusplus
}
#endif
#endif