diff --git a/geometry/src/clip.rs b/geometry/src/clip.rs index 225409e3..aeb5b031 100644 --- a/geometry/src/clip.rs +++ b/geometry/src/clip.rs @@ -130,7 +130,9 @@ impl Edge { #[inline] fn point_is_inside(&self, point: &Point2DF32) -> bool { - (self.0.to() - self.0.from()).det(*point - self.0.from()) >= 0.0 + let area = (self.0.to() - self.0.from()).det(*point - self.0.from()); + //println!("point_is_inside({:?}, {:?}), area={}", self, point, area); + area >= 0.0 } fn trivially_test_segment(&self, segment: &Segment) -> EdgeRelativeLocation { @@ -153,17 +155,14 @@ impl Edge { } fn line_intersection(&self, other: &LineSegmentF32) -> Option { - let (this_line, other_line) = (self.0.line_coords(), other.line_coords()); - let result = this_line.cross(other_line); - let z = result[2]; - if z == 0.0 { - return None; - } - let result = Point2DF32((result * F32x4::splat(1.0 / z)).xyxy()); - if result.x() <= other.min_x() || result.x() >= other.max_x() { + let t = other.intersection_t(&self.0); + //println!("line_intersection({:?}, {:?}) t={:?}", self, other, t); + if t < 0.0 || t > 1.0 { None } else { - Some(result) + // FIXME(pcwalton) + //Some(other.sample(t)) + Some(self.0.sample(self.0.intersection_t(&other))) } } @@ -177,7 +176,7 @@ impl Edge { segment = segment.to_cubic(); } - self.intersect_cubic_segment(&segment, 0.0, 1.0).map(|t| { + self.intersect_cubic_segment(&segment, 0.0, 1.0).and_then(|t| { self.fixup_clipped_segments(&segment.as_cubic_segment().split(t)) }) } @@ -197,24 +196,18 @@ impl Edge { } let (prev_segment, next_segment) = segment.as_cubic_segment().split(t_mid); - - let prev_cubic_segment = prev_segment.as_cubic_segment(); - let next_cubic_segment = next_segment.as_cubic_segment(); - 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 if prev_segment.baseline.to() == self.0.from() || - prev_segment.baseline.to() == self.0.to() { - Some(t_mid) } else { None } } - fn fixup_clipped_segments(&self, segment: &(Segment, Segment)) -> (Segment, Segment) { + 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(); @@ -227,6 +220,23 @@ impl Edge { 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); @@ -238,7 +248,7 @@ impl Edge { } }*/ - (prev, next) + Some((prev, next)) } } @@ -250,7 +260,6 @@ pub(crate) struct ContourClipper { impl ContourClipper { #[inline] pub(crate) fn new(clip_polygon: &[Point2DF32], contour: Contour) -> ContourClipper { - debug_assert!(!clip_polygon.is_empty()); ContourClipper { clip_polygon: SmallVec::from_slice(clip_polygon), contour } } @@ -310,16 +319,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()); - while let Some((before_split, after_split)) = edge.split_segment(&segment) { + 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), + }; + // Push the split segment if appropriate. - /* - println!("... ... before_split={:?} after_split={:?} starts_inside={:?}", + println!("... ... edge={:?} before_split={:?} after_split={:?} starts_inside={:?}", + edge.0, before_split, after_split, starts_inside); - */ if starts_inside { //println!("... split segment case, pushing segment"); push_segment(&mut self.contour, &before_split, edge); diff --git a/geometry/src/line_segment.rs b/geometry/src/line_segment.rs index 47131cb5..effb8b67 100644 --- a/geometry/src/line_segment.rs +++ b/geometry/src/line_segment.rs @@ -214,6 +214,24 @@ impl LineSegmentF32 { let to = F32x4::new(self.0[2], self.0[3], 1.0, 0.0); from.cross(to) } + + #[inline] + pub fn vector(&self) -> Point2DF32 { + self.to() - self.from() + } + + // http://www.cs.swan.ac.uk/~cssimon/line_intersection.html + 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]) + } + + #[inline] + pub fn sample(&self, t: f32) -> Point2DF32 { + self.from() + self.vector().scale(t) + } } impl Sub for LineSegmentF32 { diff --git a/geometry/src/point.rs b/geometry/src/point.rs index 2cc21704..ae032fc4 100644 --- a/geometry/src/point.rs +++ b/geometry/src/point.rs @@ -56,6 +56,16 @@ impl Point2DF32 { self.0[1] } + #[inline] + pub fn set_x(&mut self, x: f32) { + self.0[0] = x; + } + + #[inline] + pub fn set_y(&mut self, y: f32) { + self.0[1] = y; + } + #[inline] pub fn min(&self, other: Point2DF32) -> Point2DF32 { Point2DF32(self.0.min(other.0)) @@ -66,11 +76,15 @@ impl Point2DF32 { Point2DF32(self.0.max(other.0)) } - // TODO(pcwalton): Optimize with SIMD. #[inline] pub fn det(&self, other: Point2DF32) -> f32 { self.x() * other.y() - self.y() * other.x() } + + #[inline] + pub fn scale(&self, x: f32) -> Point2DF32 { + Point2DF32(self.0 * F32x4::splat(x)) + } } impl PartialEq for Point2DF32 { diff --git a/geometry/src/segment.rs b/geometry/src/segment.rs index cff2d72c..1d713cdf 100644 --- a/geometry/src/segment.rs +++ b/geometry/src/segment.rs @@ -136,6 +136,12 @@ impl Segment { self.reversed() } } + + #[inline] + pub fn is_tiny(&self) -> bool { + const EPSILON: f32 = 0.0001; + self.baseline.square_length() < EPSILON + } } #[derive(Clone, Copy, Debug, PartialEq)] diff --git a/geometry/src/simd.rs b/geometry/src/simd.rs index 27095b9c..10ca057e 100644 --- a/geometry/src/simd.rs +++ b/geometry/src/simd.rs @@ -551,6 +551,11 @@ mod x86 { unsafe { F32x4(x86_64::_mm_unpackhi_ps(self.0, other.0)) } } + #[inline] + pub fn combine_awazbybx(self, other: F32x4) -> F32x4 { + unsafe { F32x4(x86_64::_mm_shuffle_ps(self.0, other.0, 0b1110_0100)) } + } + #[inline] pub fn transpose_4x4(a: &mut F32x4, b: &mut F32x4, c: &mut F32x4, d: &mut F32x4) { unsafe { diff --git a/renderer/src/scene.rs b/renderer/src/scene.rs index 4a7a0eb7..363defe9 100644 --- a/renderer/src/scene.rs +++ b/renderer/src/scene.rs @@ -129,7 +129,7 @@ impl Scene { 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);