Fix curve/edge intersection logic in clipping

This commit is contained in:
Patrick Walton 2019-01-31 10:12:37 -08:00
parent dd480feb52
commit 4801503dd8
5 changed files with 84 additions and 88 deletions

View File

@ -24,8 +24,10 @@ use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::video::GLProfile;
use std::f32::consts::FRAC_PI_4;
use std::time::Instant;
use std::panic;
use std::path::PathBuf;
use std::process;
use std::time::Instant;
use usvg::{Options as UsvgOptions, Tree};
#[global_allocator]
@ -259,9 +261,20 @@ fn load_scene(options: &Options, window_size: &Size2D<u32>) -> Scene {
fn build_scene(scene: &Scene, options: &Options) -> BuiltScene {
let z_buffer = ZBuffer::new(&scene.view_box);
let built_objects = match options.jobs {
Some(1) => scene.build_objects_sequentially(&z_buffer),
_ => scene.build_objects(&z_buffer),
let built_objects = panic::catch_unwind(|| {
match options.jobs {
Some(1) => scene.build_objects_sequentially(&z_buffer),
_ => scene.build_objects(&z_buffer),
}
});
let built_objects = match built_objects {
Ok(built_objects) => built_objects,
Err(_) => {
eprintln!("Scene building crashed! Dumping scene:");
println!("{:?}", scene);
process::exit(1);
}
};
let mut built_scene = BuiltScene::new(&scene.view_box);

View File

@ -11,8 +11,7 @@
use crate::line_segment::LineSegmentF32;
use crate::outline::{Contour, PointFlags};
use crate::point::{Point2DF32, Point3DF32};
use crate::segment::Segment;
use crate::simd::F32x4;
use crate::segment::{CubicSegment, Segment};
use crate::util::lerp;
use arrayvec::ArrayVec;
use euclid::Rect;
@ -198,72 +197,37 @@ impl Edge {
-> Option<f32> {
/*println!("... intersect_cubic_segment({:?}, {:?}, t=({}, {}))",
self, segment, t_min, t_max);*/
let cubic_segment = segment.as_cubic_segment();
let mut segment = segment.as_cubic_segment().split_after(t_min);
segment = segment.as_cubic_segment().split_before(t_max / (1.0 - t_min));
if !self.intersects_cubic_segment_hull(segment.as_cubic_segment()) {
return None
}
loop {
let t_mid = lerp(t_min, t_max, 0.5);
if t_max - t_min < 0.00001 {
return Some(t_mid);
}
let min_sign = self.point_is_inside(&cubic_segment.sample(t_min));
let mid_sign = self.point_is_inside(&cubic_segment.sample(t_mid));
let max_sign = self.point_is_inside(&cubic_segment.sample(t_max));
/*println!("... ... ({}, {}, {}) ({}, {}, {})",
t_min, t_mid, t_max,
min_sign, mid_sign, max_sign);*/
match (min_sign == mid_sign, max_sign == mid_sign) {
(true, false) => t_min = t_mid,
(false, true) => t_max = t_mid,
_ => return None,
let (prev_segment, next_segment) = segment.as_cubic_segment().split(0.5);
if self.intersects_cubic_segment_hull(prev_segment.as_cubic_segment()) {
t_max = t_mid;
segment = prev_segment;
} else if self.intersects_cubic_segment_hull(next_segment.as_cubic_segment()) {
t_min = t_mid;
segment = next_segment;
} else {
return None;
}
}
}
fn fixup_clipped_segments(&self, segment: &(Segment, Segment)) -> Option<(Segment, Segment)> {
let (mut prev, mut next) = *segment;
let point = prev.baseline.to();
let line_coords = self.0.line_coords();
let (a, b, c) = (line_coords[0], line_coords[1], line_coords[2]);
let denom = 1.0 / (a * a + b * b);
let factor = b * point.x() - a * point.y();
let snapped = Point2DF32::new(b * factor - a * c, a * -factor - b * c) *
Point2DF32::splat(denom);
prev.baseline.set_to(&snapped);
next.baseline.set_from(&snapped);
// FIXME(pcwalton): Do this more efficiently...
// FIXME(pcwalton): Remove duplication!
if self.0.from_x() == self.0.to_x() {
let x = self.0.from_x();
prev.baseline.set_to_x(x);
next.baseline.set_from_x(x);
}
if self.0.from_y() == self.0.to_y() {
let y = self.0.from_y();
prev.baseline.set_to_y(y);
next.baseline.set_from_y(y);
}
if prev.is_tiny() {
return None
}
/*match *self {
Edge::Left(x) | Edge::Right(x) => {
before.baseline.set_to_x(x);
after.baseline.set_from_x(x);
}
Edge::Top(y) | Edge::Bottom(y) => {
before.baseline.set_to_y(y);
after.baseline.set_from_y(y);
}
}*/
Some((prev, next))
fn intersects_cubic_segment_hull(&self, cubic_segment: CubicSegment) -> bool {
let inside = self.point_is_inside(&cubic_segment.0.baseline.from());
inside != self.point_is_inside(&cubic_segment.0.ctrl.from()) ||
inside != self.point_is_inside(&cubic_segment.0.ctrl.to()) ||
inside != self.point_is_inside(&cubic_segment.0.baseline.to())
}
}

View File

@ -320,9 +320,6 @@ impl Contour {
*point = transform.transform_point(point);
union_rect(&mut self.bounds, *point, point_index == 0);
}
// TODO(pcwalton): Skip this step if the transform is rectilinear.
self.make_monotonic();
}
#[inline]
@ -331,8 +328,6 @@ impl Contour {
*point = perspective.transform_point_2d(point);
union_rect(&mut self.bounds, *point, point_index == 0);
}
self.make_monotonic();
}
#[inline]

View File

@ -172,7 +172,7 @@ bitflags! {
}
#[derive(Clone, Copy, Debug)]
pub struct CubicSegment<'s>(&'s Segment);
pub struct CubicSegment<'s>(pub &'s Segment);
impl<'s> CubicSegment<'s> {
// See Kaspar Fischer, "Piecewise Linear Approximation of Bézier Curves", 2000.
@ -199,41 +199,61 @@ impl<'s> CubicSegment<'s> {
#[inline]
pub fn split(self, t: f32) -> (Segment, Segment) {
let tttt = F32x4::splat(t);
let (baseline0, ctrl0, baseline1, ctrl1);
if t <= 0.0 {
let from = &self.0.baseline.from();
baseline0 = LineSegmentF32::new(from, from);
ctrl0 = LineSegmentF32::new(from, from);
baseline1 = self.0.baseline;
ctrl1 = self.0.ctrl;
} else if t >= 1.0 {
let to = &self.0.baseline.to();
baseline0 = self.0.baseline;
ctrl0 = self.0.ctrl;
baseline1 = LineSegmentF32::new(to, to);
ctrl1 = LineSegmentF32::new(to, to);
} else {
let tttt = F32x4::splat(t);
let (p0p3, p1p2) = (self.0.baseline.0, self.0.ctrl.0);
let p0p1 = p0p3.combine_axaybxby(p1p2);
let (p0p3, p1p2) = (self.0.baseline.0, self.0.ctrl.0);
let p0p1 = p0p3.combine_axaybxby(p1p2);
// p01 = lerp(p0, p1, t), p12 = lerp(p1, p2, t), p23 = lerp(p2, p3, t)
let p01p12 = p0p1 + tttt * (p1p2 - p0p1);
let pxxp23 = p1p2 + tttt * (p0p3 - p1p2);
let p12p23 = p01p12.combine_azawbzbw(pxxp23);
// p01 = lerp(p0, p1, t), p12 = lerp(p1, p2, t), p23 = lerp(p2, p3, t)
let p01p12 = p0p1 + tttt * (p1p2 - p0p1);
let pxxp23 = p1p2 + tttt * (p0p3 - p1p2);
let p12p23 = p01p12.combine_azawbzbw(pxxp23);
// p012 = lerp(p01, p12, t), p123 = lerp(p12, p23, t)
let p012p123 = p01p12 + tttt * (p12p23 - p01p12);
let p123 = p012p123.zwzw();
// p012 = lerp(p01, p12, t), p123 = lerp(p12, p23, t)
let p012p123 = p01p12 + tttt * (p12p23 - p01p12);
let p123 = p012p123.zwzw();
// p0123 = lerp(p012, p123, t)
let p0123 = p012p123 + tttt * (p123 - p012p123);
// p0123 = lerp(p012, p123, t)
let p0123 = p012p123 + tttt * (p123 - p012p123);
let baseline0 = p0p3.combine_axaybxby(p0123);
let ctrl0 = p01p12.combine_axaybxby(p012p123);
let baseline1 = p0123.combine_axaybzbw(p0p3);
let ctrl1 = p012p123.combine_azawbzbw(p12p23);
baseline0 = LineSegmentF32(p0p3.combine_axaybxby(p0123));
ctrl0 = LineSegmentF32(p01p12.combine_axaybxby(p012p123));
baseline1 = LineSegmentF32(p0123.combine_axaybzbw(p0p3));
ctrl1 = LineSegmentF32(p012p123.combine_azawbzbw(p12p23));
}
(Segment {
baseline: LineSegmentF32(baseline0),
ctrl: LineSegmentF32(ctrl0),
baseline: baseline0,
ctrl: ctrl0,
kind: SegmentKind::Cubic,
flags: self.0.flags & SegmentFlags::FIRST_IN_SUBPATH,
}, Segment {
baseline: LineSegmentF32(baseline1),
ctrl: LineSegmentF32(ctrl1),
baseline: baseline1,
ctrl: ctrl1,
kind: SegmentKind::Cubic,
flags: self.0.flags & SegmentFlags::CLOSES_SUBPATH,
})
}
#[inline]
pub fn split_before(self, t: f32) -> Segment {
self.split(t).0
}
#[inline]
pub fn split_after(self, t: f32) -> Segment {
self.split(t).1

View File

@ -118,6 +118,7 @@ impl Scene {
pub fn prepare(&mut self) {
for object in &mut self.objects {
object.outline.clip_against_rect(&self.view_box);
object.outline.make_monotonic();
}
}
@ -126,6 +127,7 @@ impl Scene {
for object in &mut self.objects {
object.outline.transform(transform);
object.outline.clip_against_rect(&self.view_box);
object.outline.make_monotonic();
}
self.update_bounds();
@ -146,6 +148,7 @@ impl Scene {
object.outline.clip_against_polygon(&quad);
object.outline.apply_perspective(perspective);
object.outline.clip_against_rect(&self.view_box);
object.outline.make_monotonic();
}
self.update_bounds();
@ -159,6 +162,7 @@ impl Scene {
object.outline.clip_against_polygon(&quad);
object.outline.apply_perspective(perspective);
object.outline.clip_against_rect(&view_box);
object.outline.make_monotonic();
});
self.update_bounds();