Fix curve/edge intersection logic in clipping
This commit is contained in:
parent
dd480feb52
commit
4801503dd8
|
@ -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 {
|
||||
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);
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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,6 +199,20 @@ impl<'s> CubicSegment<'s> {
|
|||
|
||||
#[inline]
|
||||
pub fn split(self, t: f32) -> (Segment, Segment) {
|
||||
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);
|
||||
|
@ -216,24 +230,30 @@ impl<'s> CubicSegment<'s> {
|
|||
// 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
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue