This commit is contained in:
Patrick Walton 2018-12-19 10:04:58 -08:00
parent 1e2d4ab8fe
commit a282f562ad
1 changed files with 81 additions and 90 deletions

View File

@ -49,9 +49,6 @@ const SCALE_FACTOR: f32 = 1.0;
// TODO(pcwalton): Make this configurable. // TODO(pcwalton): Make this configurable.
const FLATTENING_TOLERANCE: f32 = 3.0; const FLATTENING_TOLERANCE: f32 = 3.0;
// TODO(pcwalton): Make this configurable.
const RAYCASTING_TOLERANCE: f32 = 0.001;
fn main() { fn main() {
let matches = let matches =
App::new("tile-svg").arg(Arg::with_name("runs").short("r") App::new("tile-svg").arg(Arg::with_name("runs").short("r")
@ -741,6 +738,39 @@ impl Segment {
return Some(clipped); return Some(clipped);
} }
fn split_y(&self, y: f32) -> (Option<Segment>, Option<Segment>) {
// Trivial cases.
if self.from.y <= y && self.to.y <= y {
return if self.from.y < self.to.y {
(Some(*self), None)
} else {
(None, Some(*self))
}
}
if self.from.y >= y && self.to.y >= y {
return if self.from.y < self.to.y {
(None, Some(*self))
} else {
(Some(*self), None)
}
}
// TODO(pcwalton): Reduce code duplication?
if let Some(mut line_segment) = self.as_line_segment() {
let t = LineAxis::from_y(&line_segment).solve_for_t(y).unwrap();
let (prev, next) = line_segment.split(t);
return (Some(Segment::from_line(&prev)), Some(Segment::from_line(&next)));
}
// TODO(pcwalton): Don't degree elevate!
let mut cubic_segment = self.as_cubic_segment().unwrap();
println!("split_y(): y={} cubic_segment={:?}", y, cubic_segment);
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);
(Some(Segment::from_cubic(&prev)), Some(Segment::from_cubic(&next)))
}
fn generate_fill_primitives(&self, fn generate_fill_primitives(&self,
first_tile_index: u32, first_tile_index: u32,
strip_origin: &Point2D<f32>, strip_origin: &Point2D<f32>,
@ -825,49 +855,6 @@ impl Segment {
fn min_y(&self) -> f32 { fn min_y(&self) -> f32 {
f32::min(self.from.y, self.to.y) f32::min(self.from.y, self.to.y)
} }
fn clip_y(&self, y: f32) -> ClippedSegments {
if self.from.y < y && self.to.y < y {
return ClippedSegments { min: Some(*self), max: None }
}
if self.from.y > y && self.to.y > y {
return ClippedSegments { min: None, max: Some(*self) }
}
let (prev, next) = if self.flags.contains(SegmentFlags::HAS_CONTROL_POINT_1) {
let curve = CubicBezierSegment {
from: self.from,
ctrl1: self.ctrl0,
ctrl2: self.ctrl1,
to: self.to,
};
let swapped_curve = CubicBezierSegment {
from: curve.from.yx(),
ctrl1: curve.ctrl1.yx(),
ctrl2: curve.ctrl2.yx(),
to: curve.to.yx(),
};
let (prev, next) = curve.split(
swapped_curve.assume_monotonic().solve_t_for_x(y, 0.0..1.0, TOLERANCE));
(Segment::from_cubic(&prev), Segment::from_cubic(&next))
} else if self.flags.contains(SegmentFlags::HAS_CONTROL_POINT_0) {
let curve = QuadraticBezierSegment { from: self.from, ctrl: self.ctrl0, to: self.to };
let (prev, next) = curve.split(curve.assume_monotonic().solve_t_for_y(y));
(Segment::from_quadratic(&prev), Segment::from_quadratic(&next))
} else {
let line = LineSegment { from: self.from, to: self.to };
let (prev, next) = line.split(line.solve_t_for_y(y));
(Segment::from_line(&prev), Segment::from_line(&next))
};
if self.from.y <= self.to.y {
return ClippedSegments { min: Some(prev), max: Some(next) };
} else {
return ClippedSegments { min: Some(next), max: Some(prev) };
}
const TOLERANCE: f32 = 0.01;
}
} }
struct ClippedSegments { struct ClippedSegments {
@ -1097,57 +1084,60 @@ impl<'o, 'p> Tiler<'o, 'p> {
fn process_active_edge(active_edge: &mut Segment, fn process_active_edge(active_edge: &mut Segment,
strip_bounds: &Rect<f32>, strip_bounds: &Rect<f32>,
first_tile_index: u32, first_tile_index: u32,
fills: Option<&mut Vec<FillPrimitive>>, mut fills: Option<&mut Vec<FillPrimitive>>,
active_intervals: &mut Intervals, active_intervals: &mut Intervals,
used_tiles: &mut FixedBitSet) { used_tiles: &mut FixedBitSet) {
let strip_extent = strip_bounds.bottom_right(); let strip_extent = strip_bounds.bottom_right();
//println!("... clipping edge: {:?}", active_edge); //println!("... clipping edge: {:?}", active_edge);
let clipped = active_edge.clip_y(strip_extent.y); let (prev_segment, next_segment) = active_edge.split_y(strip_extent.y);
*active_edge = Segment::new();
if let Some(upper_segment) = clipped.min { for &segment in [&prev_segment, &next_segment].into_iter() {
//println!("... ... UPPER: {:?}", upper_segment); let segment = match *segment {
None => continue,
Some(ref segment) => segment,
};
if let Some(fills) = fills { if segment.from.y < strip_extent.y || segment.to.y < strip_extent.y {
active_edge.generate_fill_primitives(first_tile_index, //println!("... ... UPPER: {:?}", upper_segment);
&strip_bounds.origin,
fills);
}
// FIXME(pcwalton): Assumes x-monotonicity! if let Some(ref mut fills) = fills {
let mut from_x = clamp(upper_segment.from.x, 0.0, active_intervals.extent()); active_edge.generate_fill_primitives(first_tile_index,
let mut to_x = clamp(upper_segment.to.x, 0.0, active_intervals.extent()); &strip_bounds.origin,
from_x = clamp(from_x, 0.0, strip_extent.x); *fills);
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)) // 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 { } 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(upper_segment.from.x, upper_segment.to.x);
let mut max_x = f32::max(upper_segment.from.x, upper_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 clipped.max {
Some(lower_segment) => {
//println!("... ... LOWER: {:?}", lower_segment); //println!("... ... LOWER: {:?}", lower_segment);
*active_edge = lower_segment; *active_edge = *segment;
} }
None => *active_edge = Segment::new(),
} }
} }
@ -1578,13 +1568,14 @@ trait SolveT {
fn sample_deriv(&self, t: f32) -> f32; fn sample_deriv(&self, t: f32) -> f32;
fn solve_for_t(&self, x: f32) -> Option<f32> { fn solve_for_t(&self, x: f32) -> Option<f32> {
const MAX_NEWTON_ITERATIONS: u32 = 16; const MAX_NEWTON_ITERATIONS: u32 = 64;
const EPSILON: f32 = 0.001; const EPSILON: f32 = 0.001;
let mut t = 0.5; let mut t = 0.25;
for _ in 0..MAX_NEWTON_ITERATIONS { for iteration in 0..MAX_NEWTON_ITERATIONS {
let old_t = t; let old_t = t;
t -= self.sample(t) / self.sample_deriv(t); t -= (self.sample(t) - x) / self.sample_deriv(t);
println!("iteration {}: t={}", iteration, t);
if f32::abs(old_t - t) < EPSILON { if f32::abs(old_t - t) < EPSILON {
break break
} }