Clipping mostly works now

This commit is contained in:
Patrick Walton 2019-01-28 14:58:57 -08:00
parent 7e3365c481
commit a9c1760de5
5 changed files with 89 additions and 53 deletions

View File

@ -14,6 +14,7 @@ use crate::point::{Point2DF32, Point3DF32};
use crate::segment::Segment;
use crate::simd::F32x4;
use crate::util::lerp;
use arrayvec::ArrayVec;
use euclid::Rect;
use lyon_path::PathEvent;
use smallvec::SmallVec;
@ -68,14 +69,16 @@ impl<'a> RectClipper<'a> {
if edge.point_is_inside(&to) {
if !edge.point_is_inside(&from) {
let line_segment = LineSegmentF32::new(&from, &to);
if let Some(intersection) = edge.line_intersection(&line_segment) {
for t in edge.intersect_line_segment(&line_segment) {
let intersection = line_segment.sample(t);
add_line(&intersection, output, &mut first_point);
}
}
add_line(&to, output, &mut first_point);
} else if edge.point_is_inside(&from) {
let line_segment = LineSegmentF32::new(&from, &to);
if let Some(intersection) = edge.line_intersection(&line_segment) {
for t in edge.intersect_line_segment(&line_segment) {
let intersection = line_segment.sample(t);
add_line(&intersection, output, &mut first_point);
}
}
@ -154,21 +157,9 @@ impl Edge {
if from_inside { EdgeRelativeLocation::Inside } else { EdgeRelativeLocation::Outside }
}
fn line_intersection(&self, other: &LineSegmentF32) -> Option<Point2DF32> {
let t = other.intersection_t(&self.0);
//println!("line_intersection({:?}, {:?}) t={:?}", self, other, t);
if t < 0.0 || t > 1.0 {
None
} else {
// FIXME(pcwalton)
//Some(other.sample(t))
Some(self.0.sample(self.0.intersection_t(&other)))
}
}
fn split_segment(&self, segment: &Segment) -> Option<(Segment, Segment)> {
fn intersect_segment(&self, segment: &Segment) -> ArrayVec<[f32; 3]> {
if segment.is_line() {
return self.split_line_segment(segment);
return self.intersect_line_segment(&segment.baseline);
}
let mut segment = *segment;
@ -176,32 +167,56 @@ impl Edge {
segment = segment.to_cubic();
}
self.intersect_cubic_segment(&segment, 0.0, 1.0).and_then(|t| {
self.fixup_clipped_segments(&segment.as_cubic_segment().split(t))
})
let mut results = ArrayVec::new();
let mut prev_t = 0.0;
while !results.is_full() {
if prev_t >= 1.0 {
break
}
let next_t = match self.intersect_cubic_segment(&segment, prev_t, 1.0) {
None => break,
Some(next_t) => next_t,
};
results.push(next_t);
prev_t = next_t + EPSILON;
}
return results;
const EPSILON: f32 = 0.0001;
}
fn split_line_segment(&self, segment: &Segment) -> Option<(Segment, Segment)> {
let intersection = self.line_intersection(&segment.baseline)?;
Some((Segment::line(&LineSegmentF32::new(&segment.baseline.from(), &intersection)),
Segment::line(&LineSegmentF32::new(&intersection, &segment.baseline.to()))))
fn intersect_line_segment(&self, segment: &LineSegmentF32) -> ArrayVec<[f32; 3]> {
let mut results = ArrayVec::new();
let t = segment.intersection_t(&self.0);
if t >= 0.0 && t <= 1.0 {
results.push(t);
}
results
}
fn intersect_cubic_segment(&self, segment: &Segment, t_min: f32, t_max: f32) -> Option<f32> {
fn intersect_cubic_segment(&self, segment: &Segment, mut t_min: f32, mut t_max: f32)
-> Option<f32> {
/*println!("... intersect_cubic_segment({:?}, {:?}, t=({}, {}))",
self, segment, t_min, t_max);*/
let t_mid = lerp(t_min, t_max, 0.5);
if t_max - t_min < 0.001 {
return Some(t_mid);
}
let cubic_segment = segment.as_cubic_segment();
loop {
let t_mid = lerp(t_min, t_max, 0.5);
if t_max - t_min < 0.00001 {
return Some(t_mid);
}
let (prev_segment, next_segment) = segment.as_cubic_segment().split(t_mid);
if self.line_intersection(&prev_segment.baseline).is_some() {
self.intersect_cubic_segment(segment, t_min, t_mid)
} else if self.line_intersection(&next_segment.baseline).is_some() {
self.intersect_cubic_segment(segment, t_mid, t_max)
} else {
None
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,
}
}
}
@ -319,20 +334,20 @@ impl ContourClipper {
}
// We have a potential intersection.
println!("potential intersection: {:?} edge: {:?}", segment, edge);
//println!("potential intersection: {:?} edge: {:?}", segment, edge);
let mut starts_inside = edge.point_is_inside(&segment.baseline.from());
for _ in 0..3 {
let (before_split, after_split) = match edge.split_segment(&segment) {
None => break,
Some((before_split, after_split)) => (before_split, after_split),
};
let intersection_ts = edge.intersect_segment(&segment);
let mut last_t = 0.0;
//println!("... intersections: {:?}", intersection_ts);
for t in intersection_ts {
let (before_split, after_split) = segment.split((t - last_t) / (1.0 - last_t));
// Push the split segment if appropriate.
println!("... ... edge={:?} before_split={:?} after_split={:?} starts_inside={:?}",
/*println!("... ... edge={:?} before_split={:?} t={:?} starts_inside={:?}",
edge.0,
before_split,
after_split,
starts_inside);
t,
starts_inside);*/
if starts_inside {
//println!("... split segment case, pushing segment");
push_segment(&mut self.contour, &before_split, edge);
@ -340,6 +355,7 @@ impl ContourClipper {
// We've now transitioned from inside to outside or vice versa.
starts_inside = !starts_inside;
last_t = t;
segment = after_split;
}

View File

@ -224,8 +224,11 @@ impl LineSegmentF32 {
pub fn intersection_t(&self, other: &LineSegmentF32) -> f32 {
let d0d1 = self.vector().0.combine_axaybxby(other.vector().0);
let offset = other.from() - self.from();
let terms = d0d1 * d0d1.combine_awazbybx(offset.0);
(terms[3] - terms[2]) / (terms[0] - terms[1])
let factors = d0d1.combine_awazbybx(offset.0);
let terms = d0d1 * factors;
let t = (terms[3] - terms[2]) / (terms[0] - terms[1]);
//println!("intersection_t({:?}, {:?})={} (d0d1={:?}, factors={:?})", self, other, t, d0d1, factors);
t
}
#[inline]

View File

@ -142,6 +142,17 @@ impl Segment {
const EPSILON: f32 = 0.0001;
self.baseline.square_length() < EPSILON
}
#[inline]
pub fn split(&self, t: f32) -> (Segment, Segment) {
// FIXME(pcwalton): Don't degree elevate!
if self.is_line() {
let (before, after) = self.as_line_segment().split(t);
(Segment::line(&before), Segment::line(&after))
} else {
self.to_cubic().as_cubic_segment().split(t)
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
@ -239,6 +250,12 @@ impl<'s> CubicSegment<'s> {
self.split(t).1
}
// FIXME(pcwalton): Use Horner's method!
#[inline]
pub fn sample(self, t: f32) -> Point2DF32 {
self.split(t).0.baseline.to()
}
#[inline]
pub fn y_extrema(self) -> (Option<f32>, Option<f32>) {
let p0p1p2p3 = F32x4::new(self.0.baseline.from_y(),

View File

@ -553,7 +553,7 @@ mod x86 {
#[inline]
pub fn combine_awazbybx(self, other: F32x4) -> F32x4 {
unsafe { F32x4(x86_64::_mm_shuffle_ps(self.0, other.0, 0b1110_0100)) }
unsafe { F32x4(x86_64::_mm_shuffle_ps(self.0, other.0, 0b0001_1011)) }
}
#[inline]

View File

@ -120,16 +120,16 @@ impl Scene {
pub fn apply_perspective(&mut self, perspective: &Perspective) {
let quad = self.clip_bounding_quad_with_perspective(perspective);
println!("bounds={:?} PRE-transform quad={:?}", self.bounds, quad);
//println!("bounds={:?} PRE-transform quad={:?}", self.bounds, quad);
let inverse_transform = perspective.transform.inverse();
let quad: Vec<_> = quad.into_iter().map(|point| {
inverse_transform.transform_point_3d(point).to_2d()
}).collect();
println!("bounds={:?} POST-transform quad={:?}", self.bounds, quad);
//println!("bounds={:?} POST-transform quad={:?}", self.bounds, quad);
let mut bounds = Rect::zero();
for (object_index, object) in self.objects.iter_mut().enumerate() {
//object.outline.clip_against_polygon(&quad);
object.outline.clip_against_polygon(&quad);
object.outline.apply_perspective(perspective);
object.outline.clip_against_rect(&self.view_box);
@ -151,12 +151,12 @@ impl Scene {
Point3DF32::from_euclid_2d(&self.bounds.bottom_right()),
Point3DF32::from_euclid_2d(&self.bounds.bottom_left()),
];
println!("-----");
println!("bounds={:?} ORIGINAL quad={:?}", self.bounds, points);
//println!("-----");
//println!("bounds={:?} ORIGINAL quad={:?}", self.bounds, points);
for point in &mut points {
*point = perspective.transform.transform_point_3d(*point);
}
println!("PERSPECTIVE quad={:?}", points);
//println!("PERSPECTIVE quad={:?}", points);
//points
PolygonClipper3D::new(points).clip()
}