Many fixes to tile clipping

This commit is contained in:
Patrick Walton 2018-12-19 18:20:22 -08:00
parent 4a00468595
commit bfbe48959a
1 changed files with 90 additions and 69 deletions

View File

@ -94,6 +94,11 @@ fn main() {
for (index, tile) in built_scene.solid_tiles.iter().enumerate() { for (index, tile) in built_scene.solid_tiles.iter().enumerate() {
println!("... {}: {:?}", index, tile); println!("... {}: {:?}", index, tile);
} }
println!("fills:");
for (index, fill) in built_scene.fills.iter().enumerate() {
println!("... {}: {:?}", index, fill);
}
*/ */
if let Some(output_path) = output_path { if let Some(output_path) = output_path {
@ -758,41 +763,46 @@ impl Segment {
} }
fn split_y(&self, y: f32) -> (Option<Segment>, Option<Segment>) { fn split_y(&self, y: f32) -> (Option<Segment>, Option<Segment>) {
//println!("split_y({:?}, {:?})", self, y);
// Trivial cases. // Trivial cases.
if self.from.y <= y && self.to.y <= y { if self.from.y <= y && self.to.y <= y {
return if self.from.y < self.to.y { return (Some(*self), None)
(Some(*self), None)
} else {
(None, Some(*self))
}
} }
if self.from.y >= y && self.to.y >= y { if self.from.y >= y && self.to.y >= y {
return if self.from.y < self.to.y { return (None, Some(*self))
(None, Some(*self))
} else {
(Some(*self), None)
}
} }
// TODO(pcwalton): Reduce code duplication? // TODO(pcwalton): Reduce code duplication?
if let Some(mut line_segment) = self.as_line_segment() { let (prev, next) = match self.as_line_segment() {
let t = LineAxis::from_y(&line_segment).solve_for_t(y).unwrap(); Some(line_segment) => {
let (prev, next) = line_segment.split(t); let t = LineAxis::from_y(&line_segment).solve_for_t(y).unwrap();
return (Some(Segment::from_line(&prev)), Some(Segment::from_line(&next))); let (prev, next) = line_segment.split(t);
} //println!("... split line at {}: {:?}, {:?}", t, prev, next);
(Segment::from_line(&prev), Segment::from_line(&next))
}
None => {
// TODO(pcwalton): Don't degree elevate!
let mut cubic_segment = self.as_cubic_segment().unwrap();
let t = CubicAxis::from_y(&cubic_segment).solve_for_t(y);
let t = t.expect("Failed to solve cubic for Y!");
let (prev, next) = cubic_segment.split(t);
(Segment::from_cubic(&prev), Segment::from_cubic(&next))
}
};
// TODO(pcwalton): Don't degree elevate! if self.from.y < self.to.y {
let mut cubic_segment = self.as_cubic_segment().unwrap(); (Some(prev), Some(next))
let t = CubicAxis::from_y(&cubic_segment).solve_for_t(y); } else {
let t = t.expect("Failed to solve cubic for Y!"); (Some(next), Some(prev))
let (prev, next) = cubic_segment.split(t); }
(Some(Segment::from_cubic(&prev)), Some(Segment::from_cubic(&next)))
} }
fn generate_fill_primitives(&self, fn generate_fill_primitives(&self,
strip_origin: &Point2D<f32>, strip_origin: &Point2D<f32>,
primitives: &mut Vec<FillPrimitive>) { primitives: &mut Vec<FillPrimitive>) {
if let Some(ref line_segment) = self.as_line_segment() { if let Some(ref line_segment) = self.as_line_segment() {
//println!("generate_fill_primitives({:?}, {:?})", strip_origin, line_segment);
generate_fill_primitives_for_line(line_segment, strip_origin, primitives); generate_fill_primitives_for_line(line_segment, strip_origin, primitives);
return; return;
} }
@ -823,6 +833,10 @@ impl Segment {
f32::max(0.0, f32::floor((segment.to.x - strip_origin.x) / TILE_WIDTH)) as u32; f32::max(0.0, f32::floor((segment.to.x - strip_origin.x) / TILE_WIDTH)) as u32;
if from_tile_index == to_tile_index { if from_tile_index == to_tile_index {
/*println!("... ... pushing LAST fill primitive {}: {:?} @ {:?}",
primitives.len(),
segment,
tile_offset);*/
primitives.push(FillPrimitive { primitives.push(FillPrimitive {
from: segment.from - tile_offset, from: segment.from - tile_offset,
to: segment.to - tile_offset, to: segment.to - tile_offset,
@ -832,13 +846,16 @@ impl Segment {
} }
// Split line at tile boundary. // Split line at tile boundary.
let next_tile_index = if segment.from.x < segment.to.x { let (next_tile_index, split_x) = if segment.from.x < segment.to.x {
from_tile_index + 1 (from_tile_index + 1, tile_offset.x + TILE_WIDTH)
} else { } else {
from_tile_index - 1 (from_tile_index - 1, tile_offset.x)
}; };
let next_tile_x = (next_tile_index as f32) * TILE_WIDTH + strip_origin.x; let (prev_segment, next_segment) = segment.split_at_x(split_x);
let (prev_segment, next_segment) = segment.split_at_x(next_tile_x); /*println!("... ... pushing fill primitive {}: {:?} @ {:?}",
primitives.len(),
prev_segment,
tile_offset);*/
primitives.push(FillPrimitive { primitives.push(FillPrimitive {
from: prev_segment.from - tile_offset, from: prev_segment.from - tile_offset,
to: prev_segment.to - tile_offset, to: prev_segment.to - tile_offset,
@ -911,6 +928,7 @@ impl<'o, 'p> Tiler<'o, 'p> {
fn generate_tiles(&mut self) { fn generate_tiles(&mut self) {
// Sort all edge indices. // Sort all edge indices.
// TODO(pcwalton): Only find MIN points.
self.sorted_edge_indices.clear(); self.sorted_edge_indices.clear();
for contour_index in 0..self.outline.contours.len() { for contour_index in 0..self.outline.contours.len() {
let contour = &self.outline.contours[contour_index]; let contour = &self.outline.contours[contour_index];
@ -983,6 +1001,7 @@ impl<'o, 'p> Tiler<'o, 'p> {
} }
// Populate tile strip with active intervals. // Populate tile strip with active intervals.
// TODO(pcwalton): Use only the active edge list!
let mut strip_tile_index = 0; let mut strip_tile_index = 0;
for interval in &self.active_intervals.ranges { for interval in &self.active_intervals.ranges {
if interval.winding == 0.0 { if interval.winding == 0.0 {
@ -1004,8 +1023,8 @@ impl<'o, 'p> Tiler<'o, 'p> {
if tile_interval == (tile_left..tile_right) { if tile_interval == (tile_left..tile_right) {
strip_tiles[strip_tile_index].backdrop = interval.winding strip_tiles[strip_tile_index].backdrop = interval.winding
} else { } else {
let left = Point2D::new(interval.start, strip_origin.y); let left = Point2D::new(interval.start - tile_left, strip_origin.y);
let right = Point2D::new(interval.end, strip_origin.y); let right = Point2D::new(interval.end - tile_left, strip_origin.y);
strip_fills.push(FillPrimitive { strip_fills.push(FillPrimitive {
from: if interval.winding < 0.0 { left } else { right }, from: if interval.winding < 0.0 { left } else { right },
to: if interval.winding < 0.0 { right } else { left }, to: if interval.winding < 0.0 { right } else { left },
@ -1020,6 +1039,7 @@ impl<'o, 'p> Tiler<'o, 'p> {
// Process old active edges. // Process old active edges.
for active_edge in &mut self.active_edges { for active_edge in &mut self.active_edges {
let fills = if above_view_box { None } else { Some(&mut strip_fills) }; let fills = if above_view_box { None } else { Some(&mut strip_fills) };
//println!("processing old active edge: {:?}", active_edge);
process_active_edge(active_edge, process_active_edge(active_edge,
&strip_bounds, &strip_bounds,
fills, fills,
@ -1037,8 +1057,11 @@ impl<'o, 'p> Tiler<'o, 'p> {
break break
} }
//println!("processing new active edge: {:?}", segment);
if !segment.is_degenerate() { if !segment.is_degenerate() {
if let Some(mut segment) = segment.clip_x(strip_range) { //println!("... is not degenerate ...");
if let Some(mut segment) = segment.clip_x(strip_range.clone()) {
//println!("... clipped to {:?}: {:?}", strip_range, segment);
let fills = if above_view_box { None } else { Some(&mut strip_fills) }; let fills = if above_view_box { None } else { Some(&mut strip_fills) };
process_active_edge(&mut segment, process_active_edge(&mut segment,
&strip_bounds, &strip_bounds,
@ -1096,49 +1119,47 @@ fn process_active_edge(active_edge: &mut Segment,
used_tiles: &mut FixedBitSet) { used_tiles: &mut FixedBitSet) {
let strip_extent = strip_bounds.bottom_right(); let strip_extent = strip_bounds.bottom_right();
let (prev_segment, next_segment) = active_edge.split_y(strip_extent.y); // TODO(pcwalton): Maybe these shouldn't be Options?
let (upper_segment, lower_segment) = active_edge.split_y(strip_extent.y);
*active_edge = Segment::new(); *active_edge = Segment::new();
for &segment in [&prev_segment, &next_segment].into_iter() { if let Some(segment) = upper_segment {
let segment = match *segment { if let Some(ref mut fills) = fills {
None => continue, //println!("process_active_edge: generating fill primitives for {:?}", segment);
Some(ref segment) => segment, segment.generate_fill_primitives(&strip_bounds.origin, *fills);
};
if segment.from.y < strip_extent.y || segment.to.y < strip_extent.y {
if let Some(ref mut fills) = fills {
active_edge.generate_fill_primitives(&strip_bounds.origin, *fills);
}
// FIXME(pcwalton): Assumes x-monotonicity!
let mut from_x = clamp(segment.from.x, 0.0, active_intervals.extent());
let mut to_x = clamp(segment.to.x, 0.0, active_intervals.extent());
from_x = clamp(from_x, 0.0, strip_extent.x);
to_x = clamp(to_x, 0.0, strip_extent.x);
if from_x < to_x {
active_intervals.add(IntervalRange::new(from_x, to_x, -1.0))
} else {
active_intervals.add(IntervalRange::new(to_x, from_x, 1.0))
}
// FIXME(pcwalton): Assumes x-monotonicity!
// FIXME(pcwalton): Don't hardcode a view box left of 0!
let mut min_x = f32::min(segment.from.x, segment.to.x);
let mut max_x = f32::max(segment.from.x, segment.to.x);
min_x = clamp(min_x, 0.0, strip_extent.x);
max_x = clamp(max_x, 0.0, strip_extent.x);
let tile_left = f32::floor(min_x / TILE_WIDTH) * TILE_WIDTH;
let tile_right = f32::ceil(max_x / TILE_WIDTH) * TILE_WIDTH;
let left_tile_index = (tile_left - strip_bounds.origin.x) as u32 / TILE_WIDTH as u32;
let right_tile_index = (tile_right - strip_bounds.origin.x) as u32 / TILE_WIDTH as u32;
// Set used bits.
for tile_index in left_tile_index..right_tile_index {
used_tiles.insert(tile_index as usize);
}
} else {
*active_edge = *segment;
} }
// FIXME(pcwalton): Assumes x-monotonicity!
let mut from_x = clamp(segment.from.x, 0.0, active_intervals.extent());
let mut to_x = clamp(segment.to.x, 0.0, active_intervals.extent());
from_x = clamp(from_x, 0.0, strip_extent.x);
to_x = clamp(to_x, 0.0, strip_extent.x);
if from_x < to_x {
active_intervals.add(IntervalRange::new(from_x, to_x, -1.0))
} else {
active_intervals.add(IntervalRange::new(to_x, from_x, 1.0))
}
// FIXME(pcwalton): Assumes x-monotonicity!
// FIXME(pcwalton): Don't hardcode a view box left of 0!
let mut min_x = f32::min(segment.from.x, segment.to.x);
let mut max_x = f32::max(segment.from.x, segment.to.x);
min_x = clamp(min_x, 0.0, strip_extent.x);
max_x = clamp(max_x, 0.0, strip_extent.x);
let tile_left = f32::floor(min_x / TILE_WIDTH) * TILE_WIDTH;
let tile_right = f32::ceil(max_x / TILE_WIDTH) * TILE_WIDTH;
let left_tile_index = (tile_left - strip_bounds.origin.x) as u32 / TILE_WIDTH as u32;
let right_tile_index = (tile_right - strip_bounds.origin.x) as u32 / TILE_WIDTH as u32;
// Set used bits.
for tile_index in left_tile_index..right_tile_index {
used_tiles.insert(tile_index as usize);
}
}
match lower_segment {
Some(segment) => *active_edge = segment,
None => *active_edge = Segment::new(),
} }
} }