Initial commit of Pathfinder 2, still incomplete
This commit is contained in:
parent
93c928bf5c
commit
2fde50113c
|
@ -0,0 +1,2 @@
|
||||||
|
target
|
||||||
|
Cargo.lock
|
|
@ -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"
|
|
@ -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()
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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,
|
||||||
|
}
|
|
@ -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,
|
||||||
|
}
|
|
@ -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
|
Loading…
Reference in New Issue