Don't structure fills around the even-odd rule.

Improves self-intersecting paths under the winding rule.
This commit is contained in:
Patrick Walton 2017-09-21 14:54:04 -07:00
parent 4ac11e9010
commit a126e1248e
1 changed files with 111 additions and 95 deletions

View File

@ -188,9 +188,7 @@ impl<'a> Partitioner<'a> {
let next_active_edge_index = self.find_point_between_active_edges(endpoint_index); let next_active_edge_index = self.find_point_between_active_edges(endpoint_index);
let endpoint = &self.endpoints[endpoint_index as usize]; let endpoint = &self.endpoints[endpoint_index as usize];
if self.should_fill_above_active_edge(next_active_edge_index) { self.emit_b_quad(next_active_edge_index, endpoint.position.x);
self.emit_b_quad_above(next_active_edge_index, endpoint.position.x)
}
self.add_new_edges_for_min_point(endpoint_index, next_active_edge_index); self.add_new_edges_for_min_point(endpoint_index, next_active_edge_index);
@ -208,12 +206,8 @@ impl<'a> Partitioner<'a> {
debug!("... REGULAR point: active edge {}", active_edge_index); debug!("... REGULAR point: active edge {}", active_edge_index);
let endpoint = &self.endpoints[endpoint_index as usize]; let endpoint = &self.endpoints[endpoint_index as usize];
let bottom = self.should_fill_above_active_edge(active_edge_index); let bottom = self.emit_b_quad(active_edge_index, endpoint.position.x) ==
if !bottom { BQuadEmissionResult::BQuadEmittedAbove;
self.emit_b_quad_below(active_edge_index, endpoint.position.x)
} else {
self.emit_b_quad_above(active_edge_index, endpoint.position.x)
}
let prev_endpoint_index = self.prev_endpoint_of(endpoint_index); let prev_endpoint_index = self.prev_endpoint_of(endpoint_index);
let next_endpoint_index = self.next_endpoint_of(endpoint_index); let next_endpoint_index = self.next_endpoint_of(endpoint_index);
@ -281,15 +275,8 @@ impl<'a> Partitioner<'a> {
let endpoint = &self.endpoints[endpoint_index as usize]; let endpoint = &self.endpoints[endpoint_index as usize];
if self.should_fill_above_active_edge(active_edge_indices[0]) { self.emit_b_quad(active_edge_indices[0], endpoint.position.x);
self.emit_b_quad_above(active_edge_indices[0], endpoint.position.x) self.emit_b_quad(active_edge_indices[1], endpoint.position.x);
}
if self.should_fill_above_active_edge(active_edge_indices[1]) {
self.emit_b_quad_above(active_edge_indices[1], endpoint.position.x)
}
if self.should_fill_below_active_edge(active_edge_indices[1]) {
self.emit_b_quad_below(active_edge_indices[1], endpoint.position.x)
}
self.heap.pop(); self.heap.pop();
@ -312,15 +299,10 @@ impl<'a> Partitioner<'a> {
if let Some(crossing_point) = if let Some(crossing_point) =
self.crossing_point_for_active_edge(upper_active_edge_index) { self.crossing_point_for_active_edge(upper_active_edge_index) {
if self.should_fill_above_active_edge(upper_active_edge_index) { debug!("found SELF-INTERSECTION point for active edges {} & {}",
self.emit_b_quad_above(upper_active_edge_index, crossing_point.x) upper_active_edge_index,
} lower_active_edge_index);
if self.should_fill_above_active_edge(lower_active_edge_index) { self.emit_b_quad(lower_active_edge_index, crossing_point.x);
self.emit_b_quad_above(lower_active_edge_index, crossing_point.x)
}
if self.should_fill_above_active_edge(lower_active_edge_index + 1) {
self.emit_b_quad_below(lower_active_edge_index, crossing_point.x)
}
} }
self.active_edges.swap(upper_active_edge_index as usize, self.active_edges.swap(upper_active_edge_index as usize,
@ -476,83 +458,73 @@ impl<'a> Partitioner<'a> {
} }
} }
fn should_fill_below_active_edge(&self, active_edge_index: u32) -> bool { fn bounding_active_edges_for_fill(&self, active_edge_index: u32) -> (u32, u32) {
if (active_edge_index as usize) + 1 == self.active_edges.len() {
return false
}
match self.fill_rule { match self.fill_rule {
FillRule::EvenOdd => active_edge_index % 2 == 0, FillRule::EvenOdd if active_edge_index % 2 == 1 => {
FillRule::Winding => self.winding_number_below_active_edge(active_edge_index) != 0, (active_edge_index - 1, active_edge_index)
}
FillRule::EvenOdd if (active_edge_index as usize) + 1 == self.active_edges.len() => {
(active_edge_index, active_edge_index)
}
FillRule::EvenOdd => (active_edge_index, active_edge_index + 1),
FillRule::Winding => {
let (mut winding_number, mut upper_active_edge_index) = (0, 0);
for (active_edge_index, active_edge) in
self.active_edges[0..active_edge_index as usize].iter().enumerate() {
if winding_number == 0 {
upper_active_edge_index = active_edge_index as u32
}
winding_number += active_edge.winding_number()
}
if winding_number == 0 {
upper_active_edge_index = active_edge_index as u32
}
let mut lower_active_edge_index = active_edge_index;
for (active_edge_index, active_edge) in
self.active_edges.iter().enumerate().skip(active_edge_index as usize) {
winding_number += active_edge.winding_number();
if winding_number == 0 {
lower_active_edge_index = active_edge_index as u32;
break
} }
} }
fn should_fill_above_active_edge(&self, active_edge_index: u32) -> bool { (upper_active_edge_index, lower_active_edge_index)
active_edge_index > 0 && self.should_fill_below_active_edge(active_edge_index - 1)
}
fn winding_number_above_active_edge(&self, active_edge_index: u32) -> i32 {
if active_edge_index == 0 {
0
} else {
self.winding_number_below_active_edge(active_edge_index - 1)
}
}
fn winding_number_below_active_edge(&self, active_edge_index: u32) -> i32 {
let mut winding_number = 0;
for active_edge_index in 0..(active_edge_index as usize + 1) {
if self.active_edges[active_edge_index].left_to_right {
winding_number += 1
} else {
winding_number -= 1
}
}
winding_number
}
fn emit_b_quad_below(&mut self, upper_active_edge_index: u32, right_x: f32) {
let mut lower_active_edge_index = upper_active_edge_index + 1;
if self.fill_rule == FillRule::Winding {
let active_edge_count = self.active_edges.len() as u32;
let mut winding_number =
self.winding_number_below_active_edge(lower_active_edge_index);
while lower_active_edge_index + 1 < active_edge_count && winding_number != 0 {
lower_active_edge_index += 1;
if self.active_edges[lower_active_edge_index as usize].left_to_right {
winding_number += 1
} else {
winding_number -= 1
} }
} }
} }
self.emit_b_quad_above(lower_active_edge_index, right_x) fn emit_b_quad(&mut self, active_edge_index: u32, right_x: f32) -> BQuadEmissionResult {
if (active_edge_index as usize) >= self.active_edges.len() {
return BQuadEmissionResult::NoBQuadEmitted
} }
fn emit_b_quad_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. // TODO(pcwalton): Assert that the green X position is the same on both edges.
debug_assert!(lower_active_edge_index > 0, let (upper_active_edge_index, lower_active_edge_index) =
"Can't emit b_quads above the top active edge"); self.bounding_active_edges_for_fill(active_edge_index);
debug!("... bounding active edges for fill = [{},{}] around {}",
upper_active_edge_index,
lower_active_edge_index,
active_edge_index);
let mut upper_active_edge_index = lower_active_edge_index - 1; let emission_result = BQuadEmissionResult::new(active_edge_index,
upper_active_edge_index,
if self.fill_rule == FillRule::Winding { lower_active_edge_index);
let mut winding_number = if emission_result == BQuadEmissionResult::NoBQuadEmitted {
self.winding_number_above_active_edge(upper_active_edge_index); return emission_result
while upper_active_edge_index > 0 && winding_number != 0 {
upper_active_edge_index -= 1;
if self.active_edges[upper_active_edge_index as usize].left_to_right {
winding_number -= 1
} else {
winding_number += 1
}
}
} }
let upper_curve = self.subdivide_active_edge_at(upper_active_edge_index, right_x); let upper_curve = self.subdivide_active_edge_at(upper_active_edge_index,
let lower_curve = self.subdivide_active_edge_at(lower_active_edge_index, right_x); right_x,
SubdivisionType::Upper);
for active_edge_index in (upper_active_edge_index + 1)..lower_active_edge_index {
self.subdivide_active_edge_at(active_edge_index, right_x, SubdivisionType::Inside);
}
let lower_curve = self.subdivide_active_edge_at(lower_active_edge_index,
right_x,
SubdivisionType::Lower);
let upper_shape = upper_curve.shape(&self.b_vertex_loop_blinn_data); let upper_shape = upper_curve.shape(&self.b_vertex_loop_blinn_data);
let lower_shape = lower_curve.shape(&self.b_vertex_loop_blinn_data); let lower_shape = lower_curve.shape(&self.b_vertex_loop_blinn_data);
@ -706,7 +678,9 @@ impl<'a> Partitioner<'a> {
upper_curve.middle_point, upper_curve.middle_point,
lower_curve.left_curve_left, lower_curve.left_curve_left,
lower_curve.left_curve_control_point, lower_curve.left_curve_control_point,
lower_curve.middle_point)) lower_curve.middle_point));
emission_result
} }
fn already_visited_point(&self, point: &Point) -> bool { fn already_visited_point(&self, point: &Point) -> bool {
@ -875,11 +849,14 @@ impl<'a> Partitioner<'a> {
} }
} }
fn subdivide_active_edge_at(&mut self, active_edge_index: u32, x: f32) fn subdivide_active_edge_at(&mut self,
active_edge_index: u32,
x: f32,
subdivision_type: SubdivisionType)
-> SubdividedActiveEdge { -> SubdividedActiveEdge {
let t = self.solve_active_edge_t_for_x(x, &self.active_edges[active_edge_index as usize]); let t = self.solve_active_edge_t_for_x(x, &self.active_edges[active_edge_index as usize]);
let bottom = self.should_fill_above_active_edge(active_edge_index); let bottom = subdivision_type == SubdivisionType::Lower;
let active_edge = &mut self.active_edges[active_edge_index as usize]; let active_edge = &mut self.active_edges[active_edge_index as usize];
let left_curve_left = active_edge.left_vertex_index; let left_curve_left = active_edge.left_vertex_index;
@ -1135,6 +1112,15 @@ impl ActiveEdge {
BVertexKind::Endpoint1 BVertexKind::Endpoint1
} }
} }
#[inline]
fn winding_number(&self) -> i32 {
if self.left_to_right {
1
} else {
-1
}
}
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -1176,3 +1162,33 @@ enum Shape {
Convex, Convex,
Concave, Concave,
} }
#[derive(Debug, Clone, Copy, PartialEq)]
enum BQuadEmissionResult {
NoBQuadEmitted,
BQuadEmittedBelow,
BQuadEmittedAbove,
BQuadEmittedAround,
}
impl BQuadEmissionResult {
fn new(active_edge_index: u32, upper_active_edge_index: u32, lower_active_edge_index: u32)
-> BQuadEmissionResult {
if upper_active_edge_index == lower_active_edge_index {
BQuadEmissionResult::NoBQuadEmitted
} else if upper_active_edge_index == active_edge_index {
BQuadEmissionResult::BQuadEmittedBelow
} else if lower_active_edge_index == active_edge_index {
BQuadEmissionResult::BQuadEmittedAbove
} else {
BQuadEmissionResult::BQuadEmittedAround
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
enum SubdivisionType {
Upper,
Inside,
Lower,
}