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::keyboard::Keycode;
|
||||||
use sdl2::video::GLProfile;
|
use sdl2::video::GLProfile;
|
||||||
use std::f32::consts::FRAC_PI_4;
|
use std::f32::consts::FRAC_PI_4;
|
||||||
use std::time::Instant;
|
use std::panic;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::process;
|
||||||
|
use std::time::Instant;
|
||||||
use usvg::{Options as UsvgOptions, Tree};
|
use usvg::{Options as UsvgOptions, Tree};
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
|
@ -259,9 +261,20 @@ fn load_scene(options: &Options, window_size: &Size2D<u32>) -> Scene {
|
||||||
fn build_scene(scene: &Scene, options: &Options) -> BuiltScene {
|
fn build_scene(scene: &Scene, options: &Options) -> BuiltScene {
|
||||||
let z_buffer = ZBuffer::new(&scene.view_box);
|
let z_buffer = ZBuffer::new(&scene.view_box);
|
||||||
|
|
||||||
let built_objects = match options.jobs {
|
let built_objects = panic::catch_unwind(|| {
|
||||||
Some(1) => scene.build_objects_sequentially(&z_buffer),
|
match options.jobs {
|
||||||
_ => scene.build_objects(&z_buffer),
|
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);
|
let mut built_scene = BuiltScene::new(&scene.view_box);
|
||||||
|
|
|
@ -11,8 +11,7 @@
|
||||||
use crate::line_segment::LineSegmentF32;
|
use crate::line_segment::LineSegmentF32;
|
||||||
use crate::outline::{Contour, PointFlags};
|
use crate::outline::{Contour, PointFlags};
|
||||||
use crate::point::{Point2DF32, Point3DF32};
|
use crate::point::{Point2DF32, Point3DF32};
|
||||||
use crate::segment::Segment;
|
use crate::segment::{CubicSegment, Segment};
|
||||||
use crate::simd::F32x4;
|
|
||||||
use crate::util::lerp;
|
use crate::util::lerp;
|
||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
use euclid::Rect;
|
use euclid::Rect;
|
||||||
|
@ -198,72 +197,37 @@ impl Edge {
|
||||||
-> Option<f32> {
|
-> Option<f32> {
|
||||||
/*println!("... intersect_cubic_segment({:?}, {:?}, t=({}, {}))",
|
/*println!("... intersect_cubic_segment({:?}, {:?}, t=({}, {}))",
|
||||||
self, segment, t_min, t_max);*/
|
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 {
|
loop {
|
||||||
let t_mid = lerp(t_min, t_max, 0.5);
|
let t_mid = lerp(t_min, t_max, 0.5);
|
||||||
if t_max - t_min < 0.00001 {
|
if t_max - t_min < 0.00001 {
|
||||||
return Some(t_mid);
|
return Some(t_mid);
|
||||||
}
|
}
|
||||||
|
|
||||||
let min_sign = self.point_is_inside(&cubic_segment.sample(t_min));
|
let (prev_segment, next_segment) = segment.as_cubic_segment().split(0.5);
|
||||||
let mid_sign = self.point_is_inside(&cubic_segment.sample(t_mid));
|
if self.intersects_cubic_segment_hull(prev_segment.as_cubic_segment()) {
|
||||||
let max_sign = self.point_is_inside(&cubic_segment.sample(t_max));
|
t_max = t_mid;
|
||||||
/*println!("... ... ({}, {}, {}) ({}, {}, {})",
|
segment = prev_segment;
|
||||||
t_min, t_mid, t_max,
|
} else if self.intersects_cubic_segment_hull(next_segment.as_cubic_segment()) {
|
||||||
min_sign, mid_sign, max_sign);*/
|
t_min = t_mid;
|
||||||
|
segment = next_segment;
|
||||||
match (min_sign == mid_sign, max_sign == mid_sign) {
|
} else {
|
||||||
(true, false) => t_min = t_mid,
|
return None;
|
||||||
(false, true) => t_max = t_mid,
|
|
||||||
_ => return None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fixup_clipped_segments(&self, segment: &(Segment, Segment)) -> Option<(Segment, Segment)> {
|
fn intersects_cubic_segment_hull(&self, cubic_segment: CubicSegment) -> bool {
|
||||||
let (mut prev, mut next) = *segment;
|
let inside = self.point_is_inside(&cubic_segment.0.baseline.from());
|
||||||
|
inside != self.point_is_inside(&cubic_segment.0.ctrl.from()) ||
|
||||||
let point = prev.baseline.to();
|
inside != self.point_is_inside(&cubic_segment.0.ctrl.to()) ||
|
||||||
|
inside != self.point_is_inside(&cubic_segment.0.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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -320,9 +320,6 @@ impl Contour {
|
||||||
*point = transform.transform_point(point);
|
*point = transform.transform_point(point);
|
||||||
union_rect(&mut self.bounds, *point, point_index == 0);
|
union_rect(&mut self.bounds, *point, point_index == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(pcwalton): Skip this step if the transform is rectilinear.
|
|
||||||
self.make_monotonic();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -331,8 +328,6 @@ impl Contour {
|
||||||
*point = perspective.transform_point_2d(point);
|
*point = perspective.transform_point_2d(point);
|
||||||
union_rect(&mut self.bounds, *point, point_index == 0);
|
union_rect(&mut self.bounds, *point, point_index == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.make_monotonic();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -172,7 +172,7 @@ bitflags! {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct CubicSegment<'s>(&'s Segment);
|
pub struct CubicSegment<'s>(pub &'s Segment);
|
||||||
|
|
||||||
impl<'s> CubicSegment<'s> {
|
impl<'s> CubicSegment<'s> {
|
||||||
// See Kaspar Fischer, "Piecewise Linear Approximation of Bézier Curves", 2000.
|
// See Kaspar Fischer, "Piecewise Linear Approximation of Bézier Curves", 2000.
|
||||||
|
@ -199,41 +199,61 @@ impl<'s> CubicSegment<'s> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn split(self, t: f32) -> (Segment, Segment) {
|
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 (p0p3, p1p2) = (self.0.baseline.0, self.0.ctrl.0);
|
||||||
let p0p1 = p0p3.combine_axaybxby(p1p2);
|
let p0p1 = p0p3.combine_axaybxby(p1p2);
|
||||||
|
|
||||||
// p01 = lerp(p0, p1, t), p12 = lerp(p1, p2, t), p23 = lerp(p2, p3, t)
|
// p01 = lerp(p0, p1, t), p12 = lerp(p1, p2, t), p23 = lerp(p2, p3, t)
|
||||||
let p01p12 = p0p1 + tttt * (p1p2 - p0p1);
|
let p01p12 = p0p1 + tttt * (p1p2 - p0p1);
|
||||||
let pxxp23 = p1p2 + tttt * (p0p3 - p1p2);
|
let pxxp23 = p1p2 + tttt * (p0p3 - p1p2);
|
||||||
let p12p23 = p01p12.combine_azawbzbw(pxxp23);
|
let p12p23 = p01p12.combine_azawbzbw(pxxp23);
|
||||||
|
|
||||||
// p012 = lerp(p01, p12, t), p123 = lerp(p12, p23, t)
|
// p012 = lerp(p01, p12, t), p123 = lerp(p12, p23, t)
|
||||||
let p012p123 = p01p12 + tttt * (p12p23 - p01p12);
|
let p012p123 = p01p12 + tttt * (p12p23 - p01p12);
|
||||||
let p123 = p012p123.zwzw();
|
let p123 = p012p123.zwzw();
|
||||||
|
|
||||||
// p0123 = lerp(p012, p123, t)
|
// p0123 = lerp(p012, p123, t)
|
||||||
let p0123 = p012p123 + tttt * (p123 - p012p123);
|
let p0123 = p012p123 + tttt * (p123 - p012p123);
|
||||||
|
|
||||||
let baseline0 = p0p3.combine_axaybxby(p0123);
|
baseline0 = LineSegmentF32(p0p3.combine_axaybxby(p0123));
|
||||||
let ctrl0 = p01p12.combine_axaybxby(p012p123);
|
ctrl0 = LineSegmentF32(p01p12.combine_axaybxby(p012p123));
|
||||||
let baseline1 = p0123.combine_axaybzbw(p0p3);
|
baseline1 = LineSegmentF32(p0123.combine_axaybzbw(p0p3));
|
||||||
let ctrl1 = p012p123.combine_azawbzbw(p12p23);
|
ctrl1 = LineSegmentF32(p012p123.combine_azawbzbw(p12p23));
|
||||||
|
}
|
||||||
|
|
||||||
(Segment {
|
(Segment {
|
||||||
baseline: LineSegmentF32(baseline0),
|
baseline: baseline0,
|
||||||
ctrl: LineSegmentF32(ctrl0),
|
ctrl: ctrl0,
|
||||||
kind: SegmentKind::Cubic,
|
kind: SegmentKind::Cubic,
|
||||||
flags: self.0.flags & SegmentFlags::FIRST_IN_SUBPATH,
|
flags: self.0.flags & SegmentFlags::FIRST_IN_SUBPATH,
|
||||||
}, Segment {
|
}, Segment {
|
||||||
baseline: LineSegmentF32(baseline1),
|
baseline: baseline1,
|
||||||
ctrl: LineSegmentF32(ctrl1),
|
ctrl: ctrl1,
|
||||||
kind: SegmentKind::Cubic,
|
kind: SegmentKind::Cubic,
|
||||||
flags: self.0.flags & SegmentFlags::CLOSES_SUBPATH,
|
flags: self.0.flags & SegmentFlags::CLOSES_SUBPATH,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn split_before(self, t: f32) -> Segment {
|
||||||
|
self.split(t).0
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn split_after(self, t: f32) -> Segment {
|
pub fn split_after(self, t: f32) -> Segment {
|
||||||
self.split(t).1
|
self.split(t).1
|
||||||
|
|
|
@ -118,6 +118,7 @@ impl Scene {
|
||||||
|
|
||||||
pub fn prepare(&mut self) {
|
pub fn prepare(&mut self) {
|
||||||
for object in &mut self.objects {
|
for object in &mut self.objects {
|
||||||
|
object.outline.clip_against_rect(&self.view_box);
|
||||||
object.outline.make_monotonic();
|
object.outline.make_monotonic();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,6 +127,7 @@ impl Scene {
|
||||||
for object in &mut self.objects {
|
for object in &mut self.objects {
|
||||||
object.outline.transform(transform);
|
object.outline.transform(transform);
|
||||||
object.outline.clip_against_rect(&self.view_box);
|
object.outline.clip_against_rect(&self.view_box);
|
||||||
|
object.outline.make_monotonic();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update_bounds();
|
self.update_bounds();
|
||||||
|
@ -146,6 +148,7 @@ impl Scene {
|
||||||
object.outline.clip_against_polygon(&quad);
|
object.outline.clip_against_polygon(&quad);
|
||||||
object.outline.apply_perspective(perspective);
|
object.outline.apply_perspective(perspective);
|
||||||
object.outline.clip_against_rect(&self.view_box);
|
object.outline.clip_against_rect(&self.view_box);
|
||||||
|
object.outline.make_monotonic();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.update_bounds();
|
self.update_bounds();
|
||||||
|
@ -159,6 +162,7 @@ impl Scene {
|
||||||
object.outline.clip_against_polygon(&quad);
|
object.outline.clip_against_polygon(&quad);
|
||||||
object.outline.apply_perspective(perspective);
|
object.outline.apply_perspective(perspective);
|
||||||
object.outline.clip_against_rect(&view_box);
|
object.outline.clip_against_rect(&view_box);
|
||||||
|
object.outline.make_monotonic();
|
||||||
});
|
});
|
||||||
|
|
||||||
self.update_bounds();
|
self.update_bounds();
|
||||||
|
|
Loading…
Reference in New Issue