diff --git a/partitionfinder/src/capi.rs b/partitionfinder/src/capi.rs index b6d0b27d..e5c17002 100644 --- a/partitionfinder/src/capi.rs +++ b/partitionfinder/src/capi.rs @@ -7,7 +7,8 @@ use partitioner::Partitioner; use tessellator::{QuadTessLevels, Tessellator}; use std::mem; use std::slice; -use {AntialiasingMode, BQuad, EdgeInstance, Endpoint, Subpath, Vertex}; +use {AntialiasingMode, BQuad, BVertex, CurveIndices, EdgeInstance, Endpoint, LineIndices}; +use {Subpath, Vertex}; #[derive(Clone, Copy)] #[repr(C)] @@ -34,6 +35,28 @@ pub struct Matrix2DF32 { pub m12: f32, } +#[derive(Clone, Copy)] +#[repr(C)] +pub struct CoverIndices { + pub interior_indices: *const u32, + pub interior_indices_len: u32, + pub curve_indices: *const u32, + pub curve_indices_len: u32, +} + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct EdgeIndices { + pub upper_line_indices: *const LineIndices, + pub upper_line_indices_len: u32, + pub upper_curve_indices: *const CurveIndices, + pub upper_curve_indices_len: u32, + pub lower_line_indices: *const LineIndices, + pub lower_line_indices_len: u32, + pub lower_curve_indices: *const CurveIndices, + pub lower_curve_indices_len: u32, +} + #[no_mangle] pub unsafe extern fn pf_legalizer_new() -> *mut Legalizer { let mut legalizer = Box::new(Legalizer::new()); @@ -145,15 +168,16 @@ pub unsafe extern fn pf_partitioner_init<'a>(partitioner: *mut Partitioner<'a>, #[no_mangle] pub unsafe extern fn pf_partitioner_partition<'a>(partitioner: *mut Partitioner<'a>, + path_id: u32, first_subpath_index: u32, last_subpath_index: u32) { - (*partitioner).partition(first_subpath_index, last_subpath_index) + (*partitioner).partition(path_id, first_subpath_index, last_subpath_index) } #[no_mangle] -pub unsafe extern fn pf_partitioner_b_quads<'a>(partitioner: *mut Partitioner<'a>, - out_b_quad_count: *mut u32) - -> *const BQuad { +pub unsafe extern fn pf_partitioner_b_quads<'a>(partitioner: *const Partitioner<'a>, + out_b_quad_count: *mut u32) + -> *const BQuad { let b_quads = (*partitioner).b_quads(); if !out_b_quad_count.is_null() { *out_b_quad_count = b_quads.len() as u32 @@ -162,21 +186,45 @@ pub unsafe extern fn pf_partitioner_b_quads<'a>(partitioner: *mut Partitioner<'a } #[no_mangle] -pub unsafe extern fn pf_tessellator_new(endpoints: *const Endpoint, - endpoint_count: u32, - control_points: *const Point2D, - control_point_count: u32, - b_quads: *const BQuad, - b_quad_count: u32, - antialiasing_mode: AntialiasingMode) - -> *mut Tessellator<'static> { +pub unsafe extern fn pf_partitioner_b_vertices<'a>(partitioner: *const Partitioner<'a>, + out_b_vertex_count: *mut u32) + -> *const BVertex { // FIXME(pcwalton): This is unsafe! `Point2D` and `Point2DF32` may have different layouts! - let mut tessellator = - Box::new(Tessellator::new(slice::from_raw_parts(endpoints, endpoint_count as usize), - slice::from_raw_parts(control_points, - control_point_count as usize), - slice::from_raw_parts(b_quads, b_quad_count as usize), - antialiasing_mode)); + let b_vertices = (*partitioner).b_vertices(); + if !out_b_vertex_count.is_null() { + *out_b_vertex_count = b_vertices.len() as u32 + } + b_vertices.as_ptr() as *const BVertex +} + +#[no_mangle] +pub unsafe extern fn pf_partitioner_cover_indices<'a>(partitioner: *const Partitioner<'a>, + out_cover_indices: *mut CoverIndices) { + let cover_indices = (*partitioner).cover_indices(); + (*out_cover_indices).interior_indices = cover_indices.interior_indices.as_ptr(); + (*out_cover_indices).interior_indices_len = cover_indices.interior_indices.len() as u32; + (*out_cover_indices).curve_indices = cover_indices.curve_indices.as_ptr(); + (*out_cover_indices).curve_indices_len = cover_indices.curve_indices.len() as u32; +} + +#[no_mangle] +pub unsafe extern fn pf_partitioner_edge_indices<'a>(partitioner: *const Partitioner<'a>, + out_edge_indices: *mut EdgeIndices) { + let edge_indices = (*partitioner).edge_indices(); + (*out_edge_indices).upper_line_indices = edge_indices.upper_line_indices.as_ptr(); + (*out_edge_indices).upper_line_indices_len = edge_indices.upper_line_indices.len() as u32; + (*out_edge_indices).upper_curve_indices = edge_indices.upper_curve_indices.as_ptr(); + (*out_edge_indices).upper_curve_indices_len = edge_indices.upper_curve_indices.len() as u32; + (*out_edge_indices).lower_line_indices = edge_indices.lower_line_indices.as_ptr(); + (*out_edge_indices).lower_line_indices_len = edge_indices.lower_line_indices.len() as u32; + (*out_edge_indices).lower_curve_indices = edge_indices.lower_curve_indices.as_ptr(); + (*out_edge_indices).lower_curve_indices_len = edge_indices.lower_curve_indices.len() as u32; +} + +#[no_mangle] +pub unsafe extern fn pf_tessellator_new(antialiasing_mode: AntialiasingMode) + -> *mut Tessellator<'static> { + let mut tessellator = Box::new(Tessellator::new(antialiasing_mode)); let tessellator_ptr: *mut Tessellator<'static> = &mut *tessellator; mem::forget(tessellator); tessellator_ptr @@ -187,6 +235,21 @@ pub unsafe extern fn pf_tessellator_destroy<'a>(tessellator: *mut Tessellator<'a drop(mem::transmute::<*mut Tessellator<'a>, Box>(tessellator)) } +#[no_mangle] +pub unsafe extern fn pf_tessellator_init<'a>(tessellator: *mut Tessellator<'a>, + b_quads: *const BQuad, + b_quad_count: u32, + b_vertices: *const BVertex, + b_vertex_count: u32, + b_indices: *const u32, + b_index_count: u32) { + // FIXME(pcwalton): This is unsafe! `Point2D` and `Point2DF32` may have different layouts! + (*tessellator).init(slice::from_raw_parts(b_quads, b_quad_count as usize), + slice::from_raw_parts(b_vertices as *const BVertex, + b_vertex_count as usize), + slice::from_raw_parts(b_indices as *const u32, b_index_count as usize)) +} + #[no_mangle] pub unsafe extern fn pf_tessellator_compute_hull<'a>(tessellator: *mut Tessellator<'a>, transform: *const Matrix2DF32) { diff --git a/partitionfinder/src/geometry.rs b/partitionfinder/src/geometry.rs index 7825e546..e10258fc 100644 --- a/partitionfinder/src/geometry.rs +++ b/partitionfinder/src/geometry.rs @@ -59,12 +59,12 @@ pub fn line_line_crossing_point(a_p0: &Point2D, } // TODO(pcwalton): Implement this. -pub fn line_quadratic_bezier_crossing_point(_a_p0: &Point2D, - _a_p1: &Point2D, - _b_p0: &Point2D, - _b_p1: &Point2D, - _b_p2: &Point2D) - -> Option> { +pub fn line_quadratic_bezier_crossing_point(a_p0: &Point2D, + a_p1: &Point2D, + b_p0: &Point2D, + b_p1: &Point2D, + b_p2: &Point2D) + -> Option> { None } @@ -79,8 +79,8 @@ pub fn quadratic_bezier_quadratic_bezier_crossing_point(_a_p0: &Point2D, None } -fn sample_quadratic_bezier(t: f32, p0: &Point2D, p1: &Point2D, p2: &Point2D) - -> Point2D { +pub fn sample_quadratic_bezier(t: f32, p0: &Point2D, p1: &Point2D, p2: &Point2D) + -> Point2D { p0.lerp(*p1, t).lerp(p1.lerp(*p2, t), t) } @@ -91,11 +91,22 @@ pub fn sample_quadratic_bezier_deriv(t: f32, -> Vector2D { // https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Quadratic_B.C3.A9zier_curves // FIXME(pcwalton): Can this be made faster? - return ((*p1 - *p0) * (1.0 - t) + (*p2 - *p1) * t) * 2.0 + ((*p1 - *p0) * (1.0 - t) + (*p2 - *p1) * t) * 2.0 } -pub fn solve_line_y_for_x(x: f32, a: &Point2D, b: &Point2D) -> f32 { - a.lerp(*b, (x - a.x) / (b.x - a.x)).y +pub fn sample_quadratic_bezier_deriv_deriv(p0: &Point2D, p1: &Point2D, p2: &Point2D) + -> Vector2D { + // https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Quadratic_B.C3.A9zier_curves + // FIXME(pcwalton): Can this be made faster? + (*p2 - *p1 * 2.0 + p0.to_vector()) * 2.0 +} + +pub fn solve_line_t_for_x(x: f32, a: &Point2D, b: &Point2D) -> f32 { + if b.x == a.x { + 0.0 + } else { + (x - a.x) / (b.x - a.x) + } } pub(crate) fn newton_raphson(f: F, dfdx: DFDX, mut x_guess: f32) -> f32 @@ -148,9 +159,16 @@ pub struct QuadraticBezierInflectionPoints { impl QuadraticBezierInflectionPoints { pub fn calculate(p0: &Point2D, p1: &Point2D, p2: &Point2D) -> QuadraticBezierInflectionPoints { - QuadraticBezierInflectionPoints { - xt: quadratic_bezier_axis_inflection_point(p0.x, p1.x, p2.x), - yt: quadratic_bezier_axis_inflection_point(p0.y, p1.y, p2.y), + if ((*p1 - *p0).length() + (*p2 - *p1).length()).abs() < f32::approx_epsilon() { + QuadraticBezierInflectionPoints { + xt: None, + yt: None, + } + } else { + QuadraticBezierInflectionPoints { + xt: quadratic_bezier_axis_inflection_point(p0.x, p1.x, p2.x), + yt: quadratic_bezier_axis_inflection_point(p0.y, p1.y, p2.y), + } } } } diff --git a/partitionfinder/src/lib.rs b/partitionfinder/src/lib.rs index 31b13d74..abadf7b7 100644 --- a/partitionfinder/src/lib.rs +++ b/partitionfinder/src/lib.rs @@ -2,7 +2,7 @@ #![feature(alloc_jemalloc)] -// Needed to work around a problem with `heapsize` +// needed to work around a problem with `heapsize` extern crate alloc_jemalloc; extern crate bit_vec; extern crate env_logger; @@ -23,14 +23,34 @@ pub mod tessellator; #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct BQuad { - 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, + pub upper_left_vertex_index: u32, + pub upper_control_point_vertex_index: u32, + pub upper_right_vertex_index: u32, + pub lower_left_vertex_index: u32, + pub lower_control_point_vertex_index: u32, + pub lower_right_vertex_index: u32, + pad: [u32; 2], +} + +impl BQuad { + #[inline] + pub fn new(upper_left_vertex_index: u32, + upper_control_point_vertex_index: u32, + upper_right_vertex_index: u32, + lower_left_vertex_index: u32, + lower_control_point_vertex_index: u32, + lower_right_vertex_index: u32) + -> BQuad { + BQuad { + upper_left_vertex_index: upper_left_vertex_index, + upper_control_point_vertex_index: upper_control_point_vertex_index, + upper_right_vertex_index: upper_right_vertex_index, + lower_left_vertex_index: lower_left_vertex_index, + lower_control_point_vertex_index: lower_control_point_vertex_index, + lower_right_vertex_index: lower_right_vertex_index, + pad: [0; 2], + } + } } #[repr(C)] @@ -53,26 +73,128 @@ pub struct Subpath { #[repr(u8)] pub enum AntialiasingMode { Msaa = 0, - Levien = 1, + Ecaa = 1, +} + +#[derive(Clone, Copy, PartialEq, Debug)] +#[repr(u8)] +pub enum BVertexKind { + Endpoint0 = 0, + Endpoint1 = 1, + ConvexControlPoint = 2, + ConcaveControlPoint = 3, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct BVertex { + pub position: Point2D, + pub path_id: u32, + pub tex_coord: [u8; 2], + pub kind: BVertexKind, + pad: u8, +} + +impl BVertex { + #[inline] + pub fn new(position: &Point2D, kind: BVertexKind, path_id: u32) -> BVertex { + let tex_coord = match kind { + BVertexKind::Endpoint0 => [0, 0], + BVertexKind::Endpoint1 => [2, 2], + BVertexKind::ConcaveControlPoint | BVertexKind::ConvexControlPoint => [1, 0], + }; + BVertex { + position: *position, + path_id: path_id, + tex_coord: tex_coord, + kind: kind, + pad: 0, + } + } + + pub(crate) fn control_point(left_endpoint_position: &Point2D, + control_point_position: &Point2D, + right_endpoint_position: &Point2D, + path_id: u32, + bottom: bool) + -> BVertex { + let control_point_vector = *control_point_position - *left_endpoint_position; + let right_vector = *right_endpoint_position - *left_endpoint_position; + let determinant = right_vector.cross(control_point_vector); + let endpoint_kind = if (determinant < 0.0) ^ bottom { + BVertexKind::ConvexControlPoint + } else { + BVertexKind::ConcaveControlPoint + }; + BVertex::new(control_point_position, endpoint_kind, path_id) + } } #[derive(Clone, Copy, Debug)] #[repr(C)] pub struct Vertex { - pub prev_endpoint_index: u32, - pub next_endpoint_index: u32, + pub left_b_vertex_index: u32, + pub control_point_b_vertex_index: u32, + pub right_b_vertex_index: u32, pub time: f32, - padding: u32, + pub path_id: u32, + pad: u32, } impl Vertex { #[inline] - pub fn new(prev_endpoint_index: u32, next_endpoint_index: u32, time: f32) -> Vertex { + pub fn new(path_id: u32, + left_b_vertex_index: u32, + control_point_b_vertex_index: u32, + right_b_vertex_index: u32, + time: f32) + -> Vertex { Vertex { - prev_endpoint_index: prev_endpoint_index, - next_endpoint_index: next_endpoint_index, + path_id: path_id, + left_b_vertex_index: left_b_vertex_index, + control_point_b_vertex_index: control_point_b_vertex_index, + right_b_vertex_index: right_b_vertex_index, time: time, - padding: 0, + pad: 0, + } + } +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct LineIndices { + pub left_vertex_index: u32, + pub right_vertex_index: u32, +} + +impl LineIndices { + #[inline] + pub fn new(left_vertex_index: u32, right_vertex_index: u32) -> LineIndices { + LineIndices { + left_vertex_index: left_vertex_index, + right_vertex_index: right_vertex_index, + } + } +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct CurveIndices { + pub left_vertex_index: u32, + pub right_vertex_index: u32, + pub control_point_vertex_index: u32, + pad: u32, +} + +impl CurveIndices { + #[inline] + pub fn new(left_vertex_index: u32, control_point_vertex_index: u32, right_vertex_index: u32) + -> CurveIndices { + CurveIndices { + left_vertex_index: left_vertex_index, + right_vertex_index: right_vertex_index, + control_point_vertex_index: control_point_vertex_index, + pad: 0, } } } @@ -80,21 +202,16 @@ impl Vertex { #[derive(Clone, Copy, Debug)] #[repr(C)] pub struct EdgeInstance { - pub prev_endpoint_index: u32, - pub next_endpoint_index: u32, - pub prev_time: f32, - pub next_time: f32, + pub left_vertex_index: u32, + pub right_vertex_index: u32, } impl EdgeInstance { #[inline] - pub fn new(prev_endpoint_index: u32, next_endpoint_index: u32, prev_time: f32, next_time: f32) - -> EdgeInstance { + pub fn new(left_vertex_index: u32, right_vertex_index: u32) -> EdgeInstance { EdgeInstance { - prev_endpoint_index: prev_endpoint_index, - next_endpoint_index: next_endpoint_index, - prev_time: prev_time, - next_time: next_time, + left_vertex_index: left_vertex_index, + right_vertex_index: right_vertex_index, } } } diff --git a/partitionfinder/src/partitioner.rs b/partitionfinder/src/partitioner.rs index a715f9d1..1e2ea832 100644 --- a/partitionfinder/src/partitioner.rs +++ b/partitionfinder/src/partitioner.rs @@ -2,12 +2,13 @@ use bit_vec::BitVec; use euclid::Point2D; -use geometry; +use geometry::{self, SubdividedQuadraticBezier}; use log::LogLevel; use std::collections::BinaryHeap; use std::cmp::{self, Ordering}; +use std::f32; use std::u32; -use {BQuad, Endpoint, Subpath}; +use {BQuad, BVertex, BVertexKind, CurveIndices, Endpoint, LineIndices, Subpath}; pub struct Partitioner<'a> { endpoints: &'a [Endpoint], @@ -15,10 +16,14 @@ pub struct Partitioner<'a> { subpaths: &'a [Subpath], b_quads: Vec, + b_vertices: Vec, + cover_indices: CoverIndicesBuffer, + edge_indices: EdgeIndicesBuffer, heap: BinaryHeap, visited_points: BitVec, active_edges: Vec, + path_id: u32, } impl<'a> Partitioner<'a> { @@ -30,10 +35,14 @@ impl<'a> Partitioner<'a> { subpaths: &[], b_quads: vec![], + b_vertices: vec![], + cover_indices: CoverIndicesBuffer::new(), + edge_indices: EdgeIndicesBuffer::new(), heap: BinaryHeap::new(), visited_points: BitVec::new(), active_edges: vec![], + path_id: 0, } } @@ -47,14 +56,19 @@ impl<'a> Partitioner<'a> { // FIXME(pcwalton): Move this initialization to `partition` below. Right now, this bit // vector uses too much memory. - self.visited_points = BitVec::from_elem(self.endpoints.len() * 2, false); + self.visited_points = BitVec::from_elem(self.endpoints.len(), false); } - pub fn partition(&mut self, first_subpath_index: u32, last_subpath_index: u32) { + pub fn partition(&mut self, path_id: u32, first_subpath_index: u32, last_subpath_index: u32) { self.b_quads.clear(); + self.b_vertices.clear(); + self.cover_indices.clear(); + self.edge_indices.clear(); self.heap.clear(); self.active_edges.clear(); + self.path_id = path_id; + self.init_heap(first_subpath_index, last_subpath_index); while self.process_next_point() {} @@ -65,6 +79,21 @@ impl<'a> Partitioner<'a> { &self.b_quads } + #[inline] + pub fn b_vertices(&self) -> &[BVertex] { + &self.b_vertices + } + + #[inline] + pub fn cover_indices(&self) -> CoverIndices { + self.cover_indices.as_ref() + } + + #[inline] + pub fn edge_indices(&self) -> EdgeIndices { + self.edge_indices.as_ref() + } + fn process_next_point(&mut self) -> bool { let point = match self.heap.peek() { Some(point) => *point, @@ -89,29 +118,17 @@ impl<'a> Partitioner<'a> { self.mark_point_as_visited(&point); + self.sort_active_edge_list(point.endpoint_index); + 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 => { - // FIXME(pcwalton): This condition should always pass, but it fails on the Dutch - // rail map. - if matching_active_edges.count > 0 { - self.process_crossing_point(point.position.x, matching_active_edges.indices[0]) - } + 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), } true @@ -137,19 +154,16 @@ impl<'a> Partitioner<'a> { 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) { debug!("... REGULAR point: active edge {}", active_edge_index); let endpoint = &self.endpoints[endpoint_index as usize]; - if self.should_fill_below_active_edge(active_edge_index) { + let bottom = self.should_fill_above_active_edge(active_edge_index); + if !bottom { self.emit_b_quad_below(active_edge_index, endpoint.position.x) - } - if self.should_fill_above_active_edge(active_edge_index) { + } else { self.emit_b_quad_above(active_edge_index, endpoint.position.x) } @@ -158,13 +172,20 @@ impl<'a> Partitioner<'a> { { let active_edge = &mut self.active_edges[active_edge_index as usize]; - active_edge.left_endpoint_index = active_edge.right_endpoint_index; + active_edge.left_vertex_index = self.b_vertices.len() as u32; + active_edge.control_point_vertex_index = active_edge.left_vertex_index + 1; + + let endpoint_position = self.endpoints[active_edge.right_endpoint_index as usize] + .position; + self.b_vertices + .push(BVertex::new(&endpoint_position, active_edge.endpoint_kind(), self.path_id)); + + active_edge.toggle_parity(); + 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 } } @@ -173,7 +194,32 @@ impl<'a> Partitioner<'a> { 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) + let control_point_index = if self.active_edges[active_edge_index as usize].left_to_right { + self.control_point_index_before_endpoint(next_endpoint_index) + } else { + self.control_point_index_after_endpoint(prev_endpoint_index) + }; + + match control_point_index { + u32::MAX => { + self.active_edges[active_edge_index as usize].control_point_vertex_index = u32::MAX + } + control_point_index => { + self.active_edges[active_edge_index as usize].control_point_vertex_index = + self.b_vertices.len() as u32; + + let left_vertex_index = self.active_edges[active_edge_index as usize] + .left_vertex_index; + let control_point_position = &self.control_points[control_point_index as usize]; + let control_point_b_vertex = + BVertex::control_point(&self.b_vertices[left_vertex_index as usize].position, + &control_point_position, + &new_point.position, + self.path_id, + bottom); + self.b_vertices.push(control_point_b_vertex) + } + } } fn process_max_endpoint(&mut self, endpoint_index: u32, active_edge_indices: [u32; 2]) { @@ -199,25 +245,18 @@ impl<'a> Partitioner<'a> { // 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_b_quad_above(upper_active_edge_index, x) - } - if self.should_fill_below_active_edge(upper_active_edge_index) { - self.emit_b_quad_below(upper_active_edge_index, x) - } - - // Swap the two edges. - // - // FIXME(pcwalton): This condition should always pass, but it fails on the Dutch rail map. - let lower_active_edge_index = upper_active_edge_index + 1; - if (lower_active_edge_index as usize) < self.active_edges.len() { - self.active_edges.swap(upper_active_edge_index as usize, - lower_active_edge_index as usize) + fn sort_active_edge_list(&mut self, endpoint_index: u32) { + for index in 1..self.active_edges.len() { + for sorted_index in (1..(index + 1)).rev() { + if self.active_edges_are_ordered((sorted_index - 1) as u32, + sorted_index as u32, + endpoint_index) { + break + } + self.active_edges.swap(sorted_index - 1, sorted_index) + } } } @@ -232,32 +271,112 @@ impl<'a> Partitioner<'a> { let new_active_edges = &mut self.active_edges[next_active_edge_index as usize.. next_active_edge_index as usize + 2]; - new_active_edges[0].left_endpoint_index = endpoint_index; - new_active_edges[1].left_endpoint_index = endpoint_index; + let left_vertex_index = self.b_vertices.len() as u32; + new_active_edges[0].left_vertex_index = left_vertex_index; + new_active_edges[1].left_vertex_index = left_vertex_index; + + let position = self.endpoints[endpoint_index as usize].position; + self.b_vertices.push(BVertex::new(&position, BVertexKind::Endpoint0, self.path_id)); + + new_active_edges[0].toggle_parity(); + new_active_edges[1].toggle_parity(); 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(); + let prev_vector = prev_endpoint.position - endpoint.position; + let next_vector = next_endpoint.position - endpoint.position; - if prev_vector.y <= next_vector.y { + let (upper_control_point_index, lower_control_point_index); + if prev_vector.cross(next_vector) >= 0.0 { 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; + + upper_control_point_index = self.endpoints[endpoint_index as usize].control_point_index; + lower_control_point_index = self.endpoints[next_endpoint_index as usize] + .control_point_index; } 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; + + upper_control_point_index = self.endpoints[next_endpoint_index as usize] + .control_point_index; + lower_control_point_index = self.endpoints[endpoint_index as usize].control_point_index; } + + match upper_control_point_index { + u32::MAX => new_active_edges[0].control_point_vertex_index = u32::MAX, + upper_control_point_index => { + new_active_edges[0].control_point_vertex_index = self.b_vertices.len() as u32; + + let control_point_position = + self.control_points[upper_control_point_index as usize]; + let right_vertex_position = + self.endpoints[new_active_edges[0].right_endpoint_index as usize].position; + let control_point_b_vertex = BVertex::control_point(&position, + &control_point_position, + &right_vertex_position, + self.path_id, + false); + self.b_vertices.push(control_point_b_vertex) + } + } + + match lower_control_point_index { + u32::MAX => new_active_edges[1].control_point_vertex_index = u32::MAX, + lower_control_point_index => { + new_active_edges[1].control_point_vertex_index = self.b_vertices.len() as u32; + + let control_point_position = + self.control_points[lower_control_point_index as usize]; + let right_vertex_position = + self.endpoints[new_active_edges[1].right_endpoint_index as usize].position; + let control_point_b_vertex = BVertex::control_point(&position, + &control_point_position, + &right_vertex_position, + self.path_id, + true); + self.b_vertices.push(control_point_b_vertex) + } + } + } + + fn active_edges_are_ordered(&mut self, + prev_active_edge_index: u32, + next_active_edge_index: u32, + reference_endpoint_index: u32) + -> bool { + let prev_active_edge = &self.active_edges[prev_active_edge_index as usize]; + let next_active_edge = &self.active_edges[next_active_edge_index as usize]; + if prev_active_edge.right_endpoint_index == next_active_edge.right_endpoint_index { + // Always ordered. + // FIXME(pcwalton): Is this true? + return true + } + + let prev_active_edge_right_endpoint = + &self.endpoints[prev_active_edge.right_endpoint_index as usize]; + let next_active_edge_right_endpoint = + &self.endpoints[next_active_edge.right_endpoint_index as usize]; + if prev_active_edge_right_endpoint.position.y <= + next_active_edge_right_endpoint.position.y { + // Guaranteed to be ordered. + // FIXME(pcwalton): Is this true? + return true + } + + // Slow path. + let reference_endpoint = &self.endpoints[reference_endpoint_index as usize]; + let prev_active_edge_y = self.solve_active_edge_y_for_x(reference_endpoint.position.x, + prev_active_edge); + let next_active_edge_y = self.solve_active_edge_y_for_x(reference_endpoint.position.x, + next_active_edge); + prev_active_edge_y <= next_active_edge_y } fn init_heap(&mut self, first_subpath_index: u32, last_subpath_index: u32) { @@ -295,33 +414,163 @@ impl<'a> Partitioner<'a> { "Can't emit b_quads above the top active edge"); let upper_active_edge_index = lower_active_edge_index - 1; - let new_b_quad; + let upper_curve = self.subdivide_active_edge_at(upper_active_edge_index, right_x); + let lower_curve = self.subdivide_active_edge_at(lower_active_edge_index, right_x); - { - 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]; + // NB: Order is important here—we depend on the provoking vertex! - new_b_quad = BQuad { - 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), - }; + let upper_shape = upper_curve.shape(&self.b_vertices); + let lower_shape = lower_curve.shape(&self.b_vertices); - self.b_quads.push(new_b_quad); + match upper_shape { + Shape::Flat => { + self.edge_indices + .upper_line_indices + .push(LineIndices::new(upper_curve.left_curve_left, upper_curve.middle_point)) + } + Shape::Convex | Shape::Concave => { + self.edge_indices + .upper_curve_indices + .push(CurveIndices::new(upper_curve.left_curve_left, + upper_curve.left_curve_control_point, + upper_curve.middle_point)) + } + } + match lower_shape { + Shape::Flat => { + self.edge_indices + .lower_line_indices + .push(LineIndices::new(lower_curve.left_curve_left, lower_curve.middle_point)) + } + Shape::Convex | Shape::Concave => { + self.edge_indices + .lower_curve_indices + .push(CurveIndices::new(lower_curve.left_curve_left, + lower_curve.left_curve_control_point, + lower_curve.middle_point)) + } } - self.active_edges[upper_active_edge_index as usize].time = new_b_quad.upper_right_time; - self.active_edges[lower_active_edge_index as usize].time = new_b_quad.lower_right_time; + match (upper_shape, lower_shape) { + (Shape::Flat, Shape::Flat) | + (Shape::Flat, Shape::Convex) | + (Shape::Convex, Shape::Flat) | + (Shape::Convex, Shape::Convex) => { + self.cover_indices.interior_indices.extend([ + upper_curve.left_curve_left, + upper_curve.middle_point, + lower_curve.left_curve_left, + lower_curve.middle_point, + lower_curve.left_curve_left, + upper_curve.middle_point, + ].into_iter()); + if upper_shape != Shape::Flat { + self.cover_indices.curve_indices.extend([ + upper_curve.left_curve_control_point, + upper_curve.middle_point, + upper_curve.left_curve_left, + ].into_iter()) + } + if lower_shape != Shape::Flat { + self.cover_indices.curve_indices.extend([ + lower_curve.left_curve_control_point, + lower_curve.left_curve_left, + lower_curve.middle_point, + ].into_iter()) + } + } + + (Shape::Concave, Shape::Flat) | + (Shape::Concave, Shape::Convex) => { + self.cover_indices.interior_indices.extend([ + upper_curve.left_curve_left, + upper_curve.left_curve_control_point, + lower_curve.left_curve_left, + upper_curve.middle_point, + lower_curve.middle_point, + upper_curve.left_curve_control_point, + lower_curve.middle_point, + lower_curve.left_curve_left, + upper_curve.left_curve_control_point, + ].into_iter()); + self.cover_indices.curve_indices.extend([ + upper_curve.left_curve_control_point, + upper_curve.left_curve_left, + upper_curve.middle_point, + ].into_iter()); + if lower_shape != Shape::Flat { + self.cover_indices.curve_indices.extend([ + lower_curve.left_curve_control_point, + lower_curve.left_curve_left, + lower_curve.middle_point, + ].into_iter()) + } + } + + (Shape::Flat, Shape::Concave) | + (Shape::Convex, Shape::Concave) => { + self.cover_indices.interior_indices.extend([ + upper_curve.left_curve_left, + upper_curve.middle_point, + lower_curve.left_curve_control_point, + upper_curve.middle_point, + lower_curve.middle_point, + lower_curve.left_curve_control_point, + upper_curve.left_curve_left, + lower_curve.left_curve_control_point, + lower_curve.left_curve_left, + ].into_iter()); + self.cover_indices.curve_indices.extend([ + lower_curve.left_curve_control_point, + lower_curve.middle_point, + lower_curve.left_curve_left, + ].into_iter()); + if upper_shape != Shape::Flat { + self.cover_indices.curve_indices.extend([ + upper_curve.left_curve_control_point, + upper_curve.middle_point, + upper_curve.left_curve_left, + ].into_iter()) + } + } + + (Shape::Concave, Shape::Concave) => { + self.cover_indices.interior_indices.extend([ + upper_curve.left_curve_left, + upper_curve.left_curve_control_point, + lower_curve.left_curve_left, + lower_curve.left_curve_left, + upper_curve.left_curve_control_point, + lower_curve.left_curve_control_point, + upper_curve.middle_point, + lower_curve.left_curve_control_point, + upper_curve.left_curve_control_point, + upper_curve.middle_point, + lower_curve.middle_point, + lower_curve.left_curve_control_point, + ].into_iter()); + self.cover_indices.curve_indices.extend([ + upper_curve.left_curve_control_point, + upper_curve.left_curve_left, + upper_curve.middle_point, + lower_curve.left_curve_control_point, + lower_curve.middle_point, + lower_curve.left_curve_left, + ].into_iter()); + } + } + + self.b_quads.push(BQuad::new(upper_curve.left_curve_left, + upper_curve.left_curve_control_point, + upper_curve.middle_point, + lower_curve.left_curve_left, + lower_curve.left_curve_control_point, + lower_curve.middle_point)) } 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; + let index = point.endpoint_index as usize; match self.visited_points.get(index) { None => false, Some(visited) => visited, @@ -330,8 +579,7 @@ impl<'a> Partitioner<'a> { 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) + self.visited_points.set(point.endpoint_index as usize, true) } fn find_right_point_in_active_edge_list(&self, endpoint_index: u32) -> MatchingActiveEdges { @@ -368,27 +616,6 @@ impl<'a> Partitioner<'a> { } } - 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_point_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_point_index) => { - let control_point = &self.control_points[control_point_index as usize]; - geometry::solve_quadratic_bezier_t_for_x(x, - &prev_endpoint.position, - control_point, - &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| { @@ -399,73 +626,47 @@ impl<'a> Partitioner<'a> { } } + fn solve_active_edge_t_for_x(&self, x: f32, active_edge: &ActiveEdge) -> f32 { + let left_vertex_position = &self.b_vertices[active_edge.left_vertex_index as usize] + .position; + let right_endpoint_position = &self.endpoints[active_edge.right_endpoint_index as usize] + .position; + match active_edge.control_point_vertex_index { + u32::MAX => { + geometry::solve_line_t_for_x(x, left_vertex_position, right_endpoint_position) + } + control_point_vertex_index => { + let control_point = &self.b_vertices[control_point_vertex_index as usize].position; + geometry::solve_quadratic_bezier_t_for_x(x, + left_vertex_position, + control_point, + right_endpoint_position) + } + } + } + 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_point_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) - } + self.sample_active_edge(self.solve_active_edge_t_for_x(x, active_edge), active_edge).y } - 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_point_index = self.control_point_index(next_endpoint_index) - .expect("Edge not a cubic bezier!"); - let control_point = &self.control_points[control_point_index as usize]; - geometry::solve_quadratic_bezier_y_for_x(x, - &prev_endpoint.position, - &control_point, - &next_endpoint.position) - } - - fn control_point_index(&self, next_endpoint_index: u32) -> Option { - match self.endpoints[next_endpoint_index as usize].control_point_index { - u32::MAX => None, - control_point_index => Some(control_point_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 sample_active_edge(&self, t: f32, active_edge: &ActiveEdge) -> Point2D { + let left_vertex_position = &self.b_vertices[active_edge.left_vertex_index as usize] + .position; + let right_endpoint_position = &self.endpoints[active_edge.right_endpoint_index as usize] + .position; + match active_edge.control_point_vertex_index { + u32::MAX => { + left_vertex_position.to_vector() + .lerp(right_endpoint_position.to_vector(), t) + .to_point() + } + control_point_vertex_index => { + let control_point = &self.b_vertices[control_point_vertex_index as usize].position; + geometry::sample_quadratic_bezier(t, + left_vertex_position, + control_point, + right_endpoint_position) + } } } @@ -475,97 +676,135 @@ impl<'a> Partitioner<'a> { 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 || + if upper_active_edge.left_vertex_index == lower_active_edge.left_vertex_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_point_index = self.endpoints[next_upper_endpoint_index as usize] - .control_point_index; - let lower_control_point_index = self.endpoints[next_lower_endpoint_index as usize] - .control_point_index; + let upper_left_vertex_position = + &self.b_vertices[upper_active_edge.left_vertex_index as usize].position; + let upper_right_endpoint_position = + &self.endpoints[upper_active_edge.right_endpoint_index as usize].position; + let lower_left_vertex_position = + &self.b_vertices[lower_active_edge.left_vertex_index as usize].position; + let lower_right_endpoint_position = + &self.endpoints[lower_active_edge.right_endpoint_index as usize].position; - match (upper_control_point_index, lower_control_point_index) { + match (upper_active_edge.control_point_vertex_index, + lower_active_edge.control_point_vertex_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) + geometry::line_line_crossing_point(upper_left_vertex_position, + upper_right_endpoint_position, + lower_left_vertex_position, + lower_right_endpoint_position) } - (u32::MAX, _) => { - self.line_quadratic_bezier_crossing_point(prev_upper_endpoint_index, - next_upper_endpoint_index, - next_lower_endpoint_index, - next_lower_endpoint_index) + (upper_control_point_vertex_index, u32::MAX) => { + let upper_control_point = + &self.b_vertices[upper_control_point_vertex_index as usize].position; + geometry::line_quadratic_bezier_crossing_point(lower_left_vertex_position, + lower_right_endpoint_position, + upper_left_vertex_position, + upper_control_point, + upper_right_endpoint_position) } - (_, u32::MAX) => { - self.line_quadratic_bezier_crossing_point(prev_lower_endpoint_index, - next_lower_endpoint_index, - next_upper_endpoint_index, - next_upper_endpoint_index) + (u32::MAX, lower_control_point_vertex_index) => { + let lower_control_point = + &self.b_vertices[lower_control_point_vertex_index as usize].position; + geometry::line_quadratic_bezier_crossing_point(upper_left_vertex_position, + upper_right_endpoint_position, + lower_left_vertex_position, + lower_control_point, + lower_right_endpoint_position) } - (_, _) => { - self.quadratic_bezier_quadratic_bezier_crossing_point(prev_upper_endpoint_index, - next_upper_endpoint_index, - prev_lower_endpoint_index, - next_lower_endpoint_index) + (upper_control_point_vertex_index, lower_control_point_vertex_index) => { + let upper_control_point = + &self.b_vertices[upper_control_point_vertex_index as usize].position; + let lower_control_point = + &self.b_vertices[lower_control_point_vertex_index as usize].position; + geometry::quadratic_bezier_quadratic_bezier_crossing_point( + upper_left_vertex_position, + upper_control_point, + upper_right_endpoint_position, + lower_left_vertex_position, + lower_control_point, + lower_right_endpoint_position) } } } - 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> { - 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 subdivide_active_edge_at(&mut self, active_edge_index: u32, x: f32) -> SubdividedActiveEdge { + let t = self.solve_active_edge_t_for_x(x, &self.active_edges[active_edge_index as usize]); - fn line_quadratic_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> { - let control_point_index = self.control_point_index(next_bezier_endpoint_index) - .expect("Edge not a quadratic Bezier!"); - let control_point = &self.control_points[control_point_index as usize]; - geometry::line_quadratic_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_point, - &self.endpoints[next_bezier_endpoint_index as usize].position) - } + let bottom = self.should_fill_above_active_edge(active_edge_index); - fn quadratic_bezier_quadratic_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> { - let upper_control_point_index = self.control_point_index(next_upper_endpoint_index) - .expect("Upper edge not a quadratic Bezier!"); - let upper_control_point = &self.control_points[upper_control_point_index as usize]; - let lower_control_point_index = self.control_point_index(next_lower_endpoint_index) - .expect("Lower edge not a quadratic Bezier!"); - let lower_control_point = &self.control_points[lower_control_point_index as usize]; - geometry::quadratic_bezier_quadratic_bezier_crossing_point( - &self.endpoints[prev_upper_endpoint_index as usize].position, - upper_control_point, - &self.endpoints[next_upper_endpoint_index as usize].position, - &self.endpoints[prev_lower_endpoint_index as usize].position, - lower_control_point, - &self.endpoints[next_lower_endpoint_index as usize].position) + let active_edge = &mut self.active_edges[active_edge_index as usize]; + let left_curve_left = active_edge.left_vertex_index; + + let left_curve_control_point_vertex_index; + match active_edge.control_point_vertex_index { + u32::MAX => { + let left_point = self.b_vertices[left_curve_left as usize]; + let right_point = self.endpoints[active_edge.right_endpoint_index as usize] + .position; + let middle_point = left_point.position.to_vector().lerp(right_point.to_vector(), t); + + active_edge.left_vertex_index = self.b_vertices.len() as u32; + self.b_vertices.push(BVertex::new(&middle_point.to_point(), + active_edge.endpoint_kind(), + left_point.path_id)); + + active_edge.toggle_parity(); + + left_curve_control_point_vertex_index = u32::MAX; + } + _ => { + let left_endpoint_position = self.b_vertices[active_edge.left_vertex_index as usize] + .position; + let right_endpoint_position = + self.endpoints[active_edge.right_endpoint_index as usize].position; + let subdivided_quadratic_bezier = SubdividedQuadraticBezier::new( + t, + &left_endpoint_position, + &self.b_vertices[active_edge.control_point_vertex_index as usize].position, + &right_endpoint_position); + + let control_point_b_vertex_a = + BVertex::control_point(&left_endpoint_position, + &subdivided_quadratic_bezier.ap1, + &subdivided_quadratic_bezier.ap2bp0, + self.path_id, + bottom); + + left_curve_control_point_vertex_index = self.b_vertices.len() as u32; + active_edge.left_vertex_index = left_curve_control_point_vertex_index + 1; + + self.b_vertices.extend([ + control_point_b_vertex_a, + BVertex::new(&subdivided_quadratic_bezier.ap2bp0, + active_edge.endpoint_kind(), + self.path_id) + ].into_iter()); + + let control_point_b_vertex_b = + BVertex::control_point(&subdivided_quadratic_bezier.ap2bp0, + &subdivided_quadratic_bezier.bp1, + &right_endpoint_position, + self.path_id, + bottom); + + active_edge.control_point_vertex_index = self.b_vertices.len() as u32; + self.b_vertices.push(control_point_b_vertex_b); + + active_edge.toggle_parity(); + } + } + + SubdividedActiveEdge { + left_curve_left: left_curve_left, + left_curve_control_point: left_curve_control_point_vertex_index, + middle_point: active_edge.left_vertex_index, + right_curve_control_point: active_edge.control_point_vertex_index, + } } fn prev_endpoint_of(&self, endpoint_index: u32) -> u32 { @@ -592,16 +831,98 @@ impl<'a> Partitioner<'a> { Point { position: self.endpoints[endpoint_index as usize].position, endpoint_index: endpoint_index, - point_type: PointType::Endpoint, } } + + fn control_point_index_before_endpoint(&self, endpoint_index: u32) -> u32 { + self.endpoints[endpoint_index as usize].control_point_index + } + + fn control_point_index_after_endpoint(&self, endpoint_index: u32) -> u32 { + self.control_point_index_before_endpoint(self.next_endpoint_of(endpoint_index)) + } +} + +#[derive(Debug, Clone)] +struct CoverIndicesBuffer { + interior_indices: Vec, + curve_indices: Vec, +} + +impl CoverIndicesBuffer { + fn new() -> CoverIndicesBuffer { + CoverIndicesBuffer { + interior_indices: vec![], + curve_indices: vec![], + } + } + + fn clear(&mut self) { + self.interior_indices.clear(); + self.curve_indices.clear(); + } + + fn as_ref(&self) -> CoverIndices { + CoverIndices { + interior_indices: &self.interior_indices, + curve_indices: &self.curve_indices, + } + } +} + +#[derive(Debug, Clone, Copy)] +pub struct CoverIndices<'a> { + pub interior_indices: &'a [u32], + pub curve_indices: &'a [u32], +} + +#[derive(Debug, Clone)] +struct EdgeIndicesBuffer { + upper_line_indices: Vec, + upper_curve_indices: Vec, + lower_line_indices: Vec, + lower_curve_indices: Vec, +} + +impl EdgeIndicesBuffer { + fn new() -> EdgeIndicesBuffer { + EdgeIndicesBuffer { + upper_line_indices: vec![], + upper_curve_indices: vec![], + lower_line_indices: vec![], + lower_curve_indices: vec![], + } + } + + fn clear(&mut self) { + self.upper_line_indices.clear(); + self.upper_curve_indices.clear(); + self.lower_line_indices.clear(); + self.lower_curve_indices.clear(); + } + + fn as_ref(&self) -> EdgeIndices { + EdgeIndices { + upper_line_indices: &self.upper_line_indices, + upper_curve_indices: &self.upper_curve_indices, + lower_line_indices: &self.lower_line_indices, + lower_curve_indices: &self.lower_curve_indices, + } + } +} + +#[derive(Debug, Clone, Copy)] +pub struct EdgeIndices<'a> { + pub upper_line_indices: &'a [LineIndices], + pub upper_curve_indices: &'a [CurveIndices], + pub lower_line_indices: &'a [LineIndices], + pub lower_curve_indices: &'a [CurveIndices], } #[derive(Debug, Clone, Copy)] struct Point { position: Point2D, endpoint_index: u32, - point_type: PointType, } impl PartialEq for Point { @@ -640,36 +961,59 @@ impl Ord for Point { } } -#[derive(Debug, Clone, Copy, Default)] +#[derive(Debug, Clone, Copy)] struct ActiveEdge { - left_endpoint_index: u32, + left_vertex_index: u32, + control_point_vertex_index: u32, right_endpoint_index: u32, - time: f32, left_to_right: bool, + parity: bool, +} + +impl Default for ActiveEdge { + fn default() -> ActiveEdge { + ActiveEdge { + left_vertex_index: 0, + control_point_vertex_index: u32::MAX, + right_endpoint_index: 0, + left_to_right: false, + parity: false, + } + } } impl ActiveEdge { - fn prev_endpoint_index(&self) -> u32 { - if self.left_to_right { - self.left_endpoint_index - } else { - self.right_endpoint_index - } + fn toggle_parity(&mut self) { + self.parity = !self.parity } - fn next_endpoint_index(&self) -> u32 { - if self.left_to_right { - self.right_endpoint_index + fn endpoint_kind(&self) -> BVertexKind { + if !self.parity { + BVertexKind::Endpoint0 } else { - self.left_endpoint_index + BVertexKind::Endpoint1 } } } #[derive(Debug, Clone, Copy)] -enum PointType { - Endpoint = 0, - CrossingBelow = 1, +struct SubdividedActiveEdge { + left_curve_left: u32, + left_curve_control_point: u32, + middle_point: u32, + right_curve_control_point: u32, +} + +impl SubdividedActiveEdge { + fn shape(&self, b_vertices: &[BVertex]) -> Shape { + if self.left_curve_control_point == u32::MAX { + return Shape::Flat + } + match b_vertices[self.left_curve_control_point as usize].kind { + BVertexKind::ConvexControlPoint => Shape::Convex, + _ => Shape::Concave, + } + } } #[derive(Debug, Clone, Copy)] @@ -684,3 +1028,10 @@ struct MatchingActiveEdges { indices: [u32; 2], count: u8, } + +#[derive(Debug, Clone, Copy, PartialEq)] +enum Shape { + Flat, + Convex, + Concave, +} diff --git a/partitionfinder/src/partitionfinder.h b/partitionfinder/src/partitionfinder.h index 097fc2d8..200944f1 100644 --- a/partitionfinder/src/partitionfinder.h +++ b/partitionfinder/src/partitionfinder.h @@ -3,10 +3,16 @@ #ifndef PARTITIONFINDER_H #define PARTITIONFINDER_H +#include #include #define PF_ANTIALIASING_MODE_MSAA 0 -#define PF_ANTIALIASING_MODE_LEVIEN 1 +#define PF_ANTIALIASING_MODE_ECAA 1 + +#define PF_B_VERTEX_KIND_ENDPOINT_0 0 +#define PF_B_VERTEX_KIND_ENDPOINT_1 1 +#define PF_B_VERTEX_KIND_CONVEX_CONTROL_POINT 2 +#define PF_B_VERTEX_KIND_CONCAVE_CONTROL_POINT 3 #ifdef __cplusplus extern "C" { @@ -16,6 +22,8 @@ typedef uint8_t pf_antialiasing_mode_t; typedef uint16_t pf_float16_t; +typedef uint8_t pf_b_vertex_kind_t; + struct pf_point2d_f32 { float x, y; }; @@ -29,20 +37,69 @@ struct pf_matrix2d_f32 { typedef struct pf_matrix2d_f32 pf_matrix2d_f32_t; +struct pf_b_vertex { + pf_point2d_f32_t position; + uint32_t path_id; + uint8_t tex_coord[2]; + pf_b_vertex_kind_t kind; + uint8_t pad; +}; + +typedef struct pf_b_vertex pf_b_vertex_t; + struct pf_vertex { - uint32_t prev_endpoint_index; - uint32_t next_endpoint_index; + uint32_t left_b_vertex_index; + uint32_t control_point_b_vertex_index; + uint32_t right_b_vertex_index; float time; - uint32_t padding; + uint32_t path_id; + uint8_t bottom; + uint8_t pad0, pad1, pad2; }; typedef struct pf_vertex pf_vertex_t; +struct pf_cover_indices { + const uint32_t *interior_indices; + uint32_t interior_indices_len; + const uint32_t *curve_indices; + uint32_t curve_indices_len; +}; + +typedef struct pf_cover_indices pf_cover_indices_t; + +struct pf_line_indices { + uint32_t left_vertex_index; + uint32_t right_vertex_index; +}; + +typedef struct pf_line_indices pf_line_indices_t; + +struct pf_curve_indices { + uint32_t left_vertex_index; + uint32_t right_vertex_index; + uint32_t control_point_vertex_index; + uint32_t pad; +}; + +typedef struct pf_curve_indices pf_curve_indices_t; + +struct pf_edge_indices { + const pf_line_indices_t *top_line_indices; + uint32_t top_line_indices_len; + const pf_curve_indices_t *top_curve_indices; + uint32_t top_curve_indices_len; + const pf_line_indices_t *bottom_line_indices; + uint32_t bottom_line_indices_len; + const pf_curve_indices_t *bottom_curve_indices; + uint32_t bottom_curve_indices_len; +}; + +typedef struct pf_edge_indices pf_edge_indices_t; + struct pf_edge_instance { - uint32_t prev_endpoint_index; - uint32_t next_endpoint_index; - float prev_time; - float next_time; + uint32_t left_vertex; + uint32_t right_vertex; }; typedef struct pf_edge_instance pf_edge_instance_t; @@ -55,10 +112,13 @@ struct pf_quad_tess_levels { typedef struct pf_quad_tess_levels pf_quad_tess_levels_t; struct pf_b_quad { - uint32_t upper_prev_endpoint, upper_next_endpoint; - uint32_t lower_prev_endpoint, lower_next_endpoint; - float upper_left_time, upper_right_time; - float lower_left_time, lower_right_time; + uint32_t upper_left_vertex_index; + uint32_t upper_control_point_vertex_index; + uint32_t upper_right_vertex_index; + uint32_t lower_left_vertex_index; + uint32_t lower_control_point_vertex_index; + uint32_t lower_right_vertex_index; + uint32_t pad[2]; }; typedef struct pf_b_quad pf_b_quad_t; @@ -131,22 +191,34 @@ void pf_partitioner_init(pf_partitioner_t *partitioner, uint32_t subpath_count); void pf_partitioner_partition(pf_partitioner_t *partitioner, + uint32_t path_id, uint32_t first_subpath_index, uint32_t last_subpath_index); -const pf_b_quad_t *pf_partitioner_b_quads(pf_partitioner_t *partitioner, - uint32_t *out_b_quad_count); +const pf_b_quad_t *pf_partitioner_b_quads(const pf_partitioner_t *partitioner, + uint32_t *out_b_quad_count); -pf_tessellator_t *pf_tessellator_new(const pf_endpoint_t *endpoints, - uint32_t endpoint_count, - const pf_point2d_f32_t *control_points, - uint32_t control_point_index, - const pf_b_quad_t *b_quads, - uint32_t b_quad_count, - pf_antialiasing_mode_t antialiasing_mode); +const pf_b_vertex_t *pf_partitioner_b_vertices(const pf_partitioner_t *partitioner, + uint32_t *out_b_vertex_count); + +const void pf_partitioner_cover_indices(const pf_partitioner_t *partitioner, + pf_cover_indices_t *out_cover_indices); + +const void pf_partitioner_edge_indices(const pf_partitioner_t *partitioner, + pf_edge_indices_t *out_edge_indices); + +pf_tessellator_t *pf_tessellator_new(pf_antialiasing_mode_t antialiasing_mode); void pf_tessellator_destroy(pf_tessellator_t *tessellator); +void pf_tessellator_init(pf_tessellator_t *tessellator, + const pf_b_quad_t *b_quads, + uint32_t b_quad_count, + const pf_point2d_f32_t *b_vertices, + uint32_t b_vertex_count, + const uint32_t *b_indices, + uint32_t b_index_count); + void pf_tessellator_compute_hull(pf_tessellator_t *tessellator, const pf_matrix2d_f32_t *transform); void pf_tessellator_compute_domain(pf_tessellator_t *tessellator); diff --git a/partitionfinder/src/tessellator.rs b/partitionfinder/src/tessellator.rs index 7d20a7f0..c98ed16b 100644 --- a/partitionfinder/src/tessellator.rs +++ b/partitionfinder/src/tessellator.rs @@ -2,18 +2,18 @@ #![allow(dead_code)] -use euclid::{Length, Point2D, Transform2D}; +use euclid::Transform2D; use half::{f16, self}; use std::cmp; use std::u32; -use {AntialiasingMode, BQuad, EdgeInstance, Endpoint, Vertex}; +use {AntialiasingMode, BQuad, BVertex, EdgeInstance, Vertex}; const TOLERANCE: f32 = 0.25; pub struct Tessellator<'a> { - endpoints: &'a [Endpoint], - control_points: &'a [Point2D], b_quads: &'a [BQuad], + b_vertices: &'a [BVertex], + b_indices: &'a [u32], antialiasing_mode: AntialiasingMode, tess_levels: Vec, @@ -41,42 +41,44 @@ impl QuadTessLevels { } impl<'a> Tessellator<'a> { - pub fn new<'b>(endpoints: &'b [Endpoint], - control_points: &'b [Point2D], - b_quads: &'b [BQuad], - antialiasing_mode: AntialiasingMode) - -> Tessellator<'b> { + pub fn new<'b>(antialiasing_mode: AntialiasingMode) -> Tessellator<'b> { Tessellator { - endpoints: endpoints, - control_points: control_points, - b_quads: b_quads, + b_quads: &[], + b_vertices: &[], + b_indices: &[], antialiasing_mode: antialiasing_mode, - tess_levels: vec![QuadTessLevels::new(); b_quads.len()], + tess_levels: vec![], vertices: vec![], msaa_indices: vec![], edge_instances: vec![], } } + pub fn init(&mut self, b_quads: &'a [BQuad], b_vertices: &'a [BVertex], b_indices: &'a [u32]) { + self.b_quads = b_quads; + self.b_vertices = b_vertices; + self.b_indices = b_indices; + self.tess_levels = vec![QuadTessLevels::new(); b_quads.len()]; + } + pub fn compute_hull(&mut self, transform: &Transform2D) { - for (tess_levels, bquad) in (self.tess_levels.iter_mut()).zip(self.b_quads.iter()) { - let upper_tess_level = tess_level_for_edge(bquad.upper_prev_endpoint, - bquad.upper_next_endpoint, - bquad.upper_left_time, - bquad.upper_right_time, + for b_quad_index in 0..self.tess_levels.len() { + let b_quad = &self.b_quads[b_quad_index as usize]; + + let upper_tess_level = tess_level_for_edge(b_quad.upper_left_vertex_index, + b_quad.upper_control_point_vertex_index, + b_quad.upper_right_vertex_index, transform, - self.endpoints, - self.control_points); - let lower_tess_level = tess_level_for_edge(bquad.lower_prev_endpoint, - bquad.lower_prev_endpoint, - bquad.lower_left_time, - bquad.lower_right_time, + self.b_vertices); + let lower_tess_level = tess_level_for_edge(b_quad.lower_left_vertex_index, + b_quad.lower_control_point_vertex_index, + b_quad.lower_right_vertex_index, transform, - self.endpoints, - self.control_points); + self.b_vertices); // TODO(pcwalton): Use fewer thin triangles. + let mut tess_levels = &mut self.tess_levels[b_quad_index as usize]; tess_levels.outer[0] = half::consts::ONE; tess_levels.outer[1] = f16::from_f32(upper_tess_level as f32); tess_levels.outer[2] = half::consts::ONE; @@ -89,27 +91,31 @@ impl<'a> Tessellator<'a> { // TODO(pcwalton): Do a better tessellation that doesn't make so many sliver triangles. pub fn compute_domain(&mut self) { - for (b_quad, tess_levels) in self.b_quads.iter().zip(self.tess_levels.iter()) { + for (b_quad_index, tess_levels) in self.tess_levels.iter().enumerate() { + let b_quad = &self.b_quads[b_quad_index as usize]; + let upper_tess_level = f32::from(tess_levels.outer[1]) as u32; let lower_tess_level = f32::from(tess_levels.outer[3]) as u32; let tess_level = cmp::max(upper_tess_level, lower_tess_level); + let path_id = self.b_vertices[b_quad.upper_left_vertex_index as usize].path_id; + let first_upper_vertex_index = self.vertices.len() as u32; self.vertices.extend((0..(tess_level + 1)).map(|index| { - let left_time: Length = Length::new(b_quad.upper_left_time); - let right_time: Length = Length::new(b_quad.upper_right_time); - Vertex::new(b_quad.upper_prev_endpoint, - b_quad.upper_next_endpoint, - left_time.lerp(right_time, index as f32 / tess_level as f32).get()) + Vertex::new(path_id, + b_quad.upper_left_vertex_index, + b_quad.upper_control_point_vertex_index, + b_quad.upper_right_vertex_index, + index as f32 / tess_level as f32) })); let first_lower_vertex_index = self.vertices.len() as u32; self.vertices.extend((0..(tess_level + 1)).map(|index| { - let left_time: Length = Length::new(b_quad.lower_left_time); - let right_time: Length = Length::new(b_quad.lower_right_time); - Vertex::new(b_quad.lower_prev_endpoint, - b_quad.lower_next_endpoint, - left_time.lerp(right_time, index as f32 / tess_level as f32).get()) + Vertex::new(path_id, + b_quad.lower_left_vertex_index, + b_quad.lower_control_point_vertex_index, + b_quad.lower_right_vertex_index, + index as f32 / tess_level as f32) })); // Emit a triangle strip. @@ -125,37 +131,14 @@ impl<'a> Tessellator<'a> { ].into_iter()) } - // If Levien-style antialiasing is in use, then emit edge instances. - if self.antialiasing_mode == AntialiasingMode::Levien { - let upper_left_endpoint_time: Length = Length::new(b_quad.upper_left_time); - let upper_right_endpoint_time: Length = - Length::new(b_quad.upper_right_time); - let lower_left_endpoint_time: Length = Length::new(b_quad.lower_left_time); - let lower_right_endpoint_time: Length = - Length::new(b_quad.lower_right_time); - + // If ECAA is in use, then emit edge instances. + if self.antialiasing_mode == AntialiasingMode::Ecaa { for index in 0..tess_level { - let left_tess_coord = index as f32 / tess_level as f32; - let right_tess_coord = (index + 1) as f32 / tess_level as f32; - - let upper_left_time = upper_left_endpoint_time.lerp(upper_right_endpoint_time, - left_tess_coord).get(); - let upper_right_time = upper_left_endpoint_time.lerp(upper_right_endpoint_time, - right_tess_coord).get(); - let lower_left_time = lower_left_endpoint_time.lerp(lower_right_endpoint_time, - left_tess_coord).get(); - let lower_right_time = lower_left_endpoint_time.lerp(lower_right_endpoint_time, - right_tess_coord).get(); - self.edge_instances.extend([ - EdgeInstance::new(b_quad.upper_prev_endpoint, - b_quad.upper_next_endpoint, - upper_left_time, - upper_right_time), - EdgeInstance::new(b_quad.lower_prev_endpoint, - b_quad.lower_next_endpoint, - lower_left_time, - lower_right_time), + EdgeInstance::new(first_upper_vertex_index + index + 0, + first_upper_vertex_index + index + 1), + EdgeInstance::new(first_lower_vertex_index + index + 0, + first_lower_vertex_index + index + 1) ].into_iter()) } } @@ -184,30 +167,25 @@ impl<'a> Tessellator<'a> { } // http://antigrain.com/research/adaptive_bezier/ -fn tess_level_for_edge(prev_endpoint_index: u32, - next_endpoint_index: u32, - left_time: f32, - right_time: f32, +fn tess_level_for_edge(left_endpoint_index: u32, + control_point_index: u32, + right_endpoint_index: u32, transform: &Transform2D, - endpoints: &[Endpoint], - control_points: &[Point2D]) + b_vertices: &[BVertex]) -> u32 { - let control_point_index = endpoints[next_endpoint_index as usize].control_point_index; if control_point_index == u32::MAX { return 1 } - let (prev_time, next_time) = (left_time.min(right_time), left_time.max(right_time)); + let left_endpoint = &b_vertices[left_endpoint_index as usize].position; + let right_endpoint = &b_vertices[right_endpoint_index as usize].position; + let control_point = &b_vertices[control_point_index as usize].position; - let prev_endpoint = &endpoints[prev_endpoint_index as usize]; - let next_endpoint = &endpoints[next_endpoint_index as usize]; - let control_point = &control_points[control_point_index as usize]; - - let p0 = transform.transform_point(&prev_endpoint.position); + let p0 = transform.transform_point(left_endpoint); let p1 = transform.transform_point(control_point); - let p2 = transform.transform_point(&next_endpoint.position); + let p2 = transform.transform_point(right_endpoint); // FIXME(pcwalton): Is this good for quadratics? let length = (p1 - p0).length() + (p2 - p1).length(); - 1 + (length * TOLERANCE * (next_time - prev_time)) as u32 + 1 + (length * TOLERANCE) as u32 }