diff --git a/geometry/src/clip.rs b/geometry/src/clip.rs index 5e511ce6..3736215f 100644 --- a/geometry/src/clip.rs +++ b/geometry/src/clip.rs @@ -12,9 +12,9 @@ use crate::line_segment::LineSegmentF32; use crate::outline::{Contour, PointFlags}; use crate::point::Point2DF32; use crate::segment::Segment; +use crate::simd::F32x4; use crate::util::lerp; -use arrayvec::ArrayVec; -use euclid::{Point2D, Rect, Vector3D}; +use euclid::Rect; use lyon_path::PathEvent; use std::mem; @@ -33,10 +33,10 @@ impl<'a> RectClipper<'a> { pub fn clip(&self) -> Vec { let mut output = self.subject.to_vec(); - self.clip_against(Edge::Left(self.clip_rect.origin.x), &mut output); - self.clip_against(Edge::Top(self.clip_rect.origin.y), &mut output); - self.clip_against(Edge::Right(self.clip_rect.max_x()), &mut output); - self.clip_against(Edge::Bottom(self.clip_rect.max_y()), &mut output); + self.clip_against(Edge::left(&self.clip_rect), &mut output); + self.clip_against(Edge::top(&self.clip_rect), &mut output); + self.clip_against(Edge::right(&self.clip_rect), &mut output); + self.clip_against(Edge::bottom(&self.clip_rect), &mut output); output } @@ -66,13 +66,17 @@ impl<'a> RectClipper<'a> { if edge.point_is_inside(&to) { if !edge.point_is_inside(&from) { - let intersection = edge.line_intersection(&LineSegmentF32::new(&from, &to)); - add_line(&intersection, output, &mut first_point); + let line_segment = LineSegmentF32::new(&from, &to); + if let Some(intersection) = edge.line_intersection(&line_segment) { + add_line(&intersection, output, &mut first_point); + } } add_line(&to, output, &mut first_point); } else if edge.point_is_inside(&from) { - let intersection = edge.line_intersection(&LineSegmentF32::new(&from, &to)); - add_line(&intersection, output, &mut first_point); + let line_segment = LineSegmentF32::new(&from, &to); + if let Some(intersection) = edge.line_intersection(&line_segment) { + add_line(&intersection, output, &mut first_point); + } } from = to; @@ -96,25 +100,41 @@ impl<'a> RectClipper<'a> { } #[derive(Clone, Copy, Debug)] -enum Edge { - Left(f32), - Top(f32), - Right(f32), - Bottom(f32), -} +struct Edge(LineSegmentF32); impl Edge { + #[inline] + fn left(rect: &Rect) -> Edge { + Edge(LineSegmentF32::new(&Point2DF32::from_euclid(rect.bottom_left()), + &Point2DF32::from_euclid(rect.origin))) + } + + #[inline] + fn top(rect: &Rect) -> Edge { + Edge(LineSegmentF32::new(&Point2DF32::from_euclid(rect.origin), + &Point2DF32::from_euclid(rect.top_right()))) + } + + #[inline] + fn right(rect: &Rect) -> Edge { + Edge(LineSegmentF32::new(&Point2DF32::from_euclid(rect.top_right()), + &Point2DF32::from_euclid(rect.bottom_right()))) + } + + #[inline] + fn bottom(rect: &Rect) -> Edge { + Edge(LineSegmentF32::new(&Point2DF32::from_euclid(rect.bottom_right()), + &Point2DF32::from_euclid(rect.bottom_left()))) + } + + #[inline] fn point_is_inside(&self, point: &Point2DF32) -> bool { - match *self { - Edge::Left(x_edge) => point.x() > x_edge, - Edge::Top(y_edge) => point.y() > y_edge, - Edge::Right(x_edge) => point.x() < x_edge, - Edge::Bottom(y_edge) => point.y() < y_edge, - } + (self.0.to() - self.0.from()).det(*point - self.0.from()) >= 0.0 } fn trivially_test_segment(&self, segment: &Segment) -> EdgeRelativeLocation { let from_inside = self.point_is_inside(&segment.baseline.from()); + //println!("point {:?} inside {:?}: {:?}", segment.baseline.from(), self, from_inside); if from_inside != self.point_is_inside(&segment.baseline.to()) { return EdgeRelativeLocation::Intersecting; } @@ -131,14 +151,18 @@ impl Edge { if from_inside { EdgeRelativeLocation::Inside } else { EdgeRelativeLocation::Outside } } - fn line_intersection(&self, line_segment: &LineSegmentF32) -> Point2DF32 { - match *self { - Edge::Left(x_edge) | Edge::Right(x_edge) => { - Point2DF32::new(x_edge, line_segment.solve_y_for_x(x_edge)) - } - Edge::Top(y_edge) | Edge::Bottom(y_edge) => { - Point2DF32::new(line_segment.solve_x_for_y(y_edge), y_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() { + None + } else { + Some(result) } } @@ -158,34 +182,14 @@ impl Edge { } fn split_line_segment(&self, segment: &Segment) -> Option<(Segment, Segment)> { - let intersection; - match *self { - Edge::Left(x_edge) | Edge::Right(x_edge) => { - if (segment.baseline.from_x() <= x_edge && segment.baseline.to_x() <= x_edge) || - (segment.baseline.from_x() >= x_edge && - segment.baseline.to_x() >= x_edge) { - return None - } - intersection = Point2DF32::new(x_edge, segment.baseline.solve_y_for_x(x_edge)); - } - Edge::Top(y_edge) | Edge::Bottom(y_edge) => { - if (segment.baseline.from_y() <= y_edge && segment.baseline.to_y() <= y_edge) || - (segment.baseline.from_y() >= y_edge && - segment.baseline.to_y() >= y_edge) { - return None - } - intersection = Point2DF32::new(segment.baseline.solve_x_for_y(y_edge), y_edge); - } - }; + 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_cubic_segment(&self, segment: &Segment, t_min: f32, t_max: f32) -> Option { - /* - println!("... intersect_cubic_segment({:?}, {:?}, t=({}, {}))", - self, segment, t_min, t_max); - */ + /*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); @@ -196,30 +200,12 @@ impl Edge { let prev_cubic_segment = prev_segment.as_cubic_segment(); let next_cubic_segment = next_segment.as_cubic_segment(); - let (prev_min, prev_max, next_min, next_max, edge); - match *self { - Edge::Left(x) | Edge::Right(x) => { - prev_min = prev_cubic_segment.min_x(); - prev_max = prev_cubic_segment.max_x(); - next_min = next_cubic_segment.min_x(); - next_max = next_cubic_segment.max_x(); - edge = x; - } - Edge::Top(y) | Edge::Bottom(y) => { - prev_min = prev_cubic_segment.min_y(); - prev_max = prev_cubic_segment.max_y(); - next_min = next_cubic_segment.min_y(); - next_max = next_cubic_segment.max_y(); - edge = y; - } - } - - if prev_min < edge && edge < prev_max { + if self.line_intersection(&prev_segment.baseline).is_some() { self.intersect_cubic_segment(segment, t_min, t_mid) - } else if next_min < edge && edge < next_max { + } else if self.line_intersection(&next_segment.baseline).is_some() { self.intersect_cubic_segment(segment, t_mid, t_max) - } else if (prev_max == edge && next_min == edge) || - (prev_min == edge && next_max == edge) { + } else if prev_segment.baseline.to() == self.0.from() || + prev_segment.baseline.to() == self.0.to() { Some(t_mid) } else { None @@ -227,8 +213,20 @@ impl Edge { } fn fixup_clipped_segments(&self, segment: &(Segment, Segment)) -> (Segment, Segment) { - let (mut before, mut after) = *segment; - match *self { + 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); + + /*match *self { Edge::Left(x) | Edge::Right(x) => { before.baseline.set_to_x(x); after.baseline.set_from_x(x); @@ -237,8 +235,9 @@ impl Edge { before.baseline.set_to_y(y); after.baseline.set_from_y(y); } - } - (before, after) + }*/ + + (prev, next) } } @@ -258,10 +257,23 @@ impl ContourRectClipper { return self.contour } - self.clip_against(Edge::Left(self.clip_rect.origin.x)); - self.clip_against(Edge::Top(self.clip_rect.origin.y)); - self.clip_against(Edge::Right(self.clip_rect.max_x())); - self.clip_against(Edge::Bottom(self.clip_rect.max_y())); + self.clip_against(Edge::left(&self.clip_rect)); + self.clip_against(Edge::top(&self.clip_rect)); + self.clip_against(Edge::right(&self.clip_rect)); + self.clip_against(Edge::bottom(&self.clip_rect)); + + /* + let top = Point2DF32::new(lerp(self.clip_rect.origin.x, self.clip_rect.max_x(), 0.5), + self.clip_rect.origin.y); + self.clip_against(Edge(LineSegmentF32::new(&Point2DF32::from_euclid(self.clip_rect + .bottom_left()), + &top))); + self.clip_against(Edge(LineSegmentF32::new(&top, + &Point2DF32::from_euclid(self.clip_rect + .bottom_right())))); + self.clip_against(Edge::bottom(&self.clip_rect)); + */ + self.contour } diff --git a/geometry/src/line_segment.rs b/geometry/src/line_segment.rs index cec50b4f..0e9ed1ed 100644 --- a/geometry/src/line_segment.rs +++ b/geometry/src/line_segment.rs @@ -203,6 +203,17 @@ impl LineSegmentF32 { let (dx, dy) = (self.to_x() - self.from_x(), self.to_y() - self.from_y()); dx * dx + dy * dy } + + // Given a line equation of the form `ax + by + c = 0`, returns a vector of the form + // `[a, b, c, 0]`. + // + // TODO(pcwalton): Optimize. + #[inline] + pub fn line_coords(&self) -> F32x4 { + let from = F32x4::new(self.0[0], self.0[1], 1.0, 0.0); + let to = F32x4::new(self.0[2], self.0[3], 1.0, 0.0); + from.cross(to) + } } impl Sub for LineSegmentF32 { diff --git a/geometry/src/point.rs b/geometry/src/point.rs index 65eaf51b..8c2429cb 100644 --- a/geometry/src/point.rs +++ b/geometry/src/point.rs @@ -65,6 +65,12 @@ impl Point2DF32 { pub fn max(&self, other: Point2DF32) -> 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() + } } impl PartialEq for Point2DF32 { diff --git a/geometry/src/simd.rs b/geometry/src/simd.rs index d167028a..85122aed 100644 --- a/geometry/src/simd.rs +++ b/geometry/src/simd.rs @@ -156,6 +156,11 @@ mod scalar { pub fn transpose4(a: &mut F32x4, b: &mut F32x4, c: &mut F32x4, d: &mut F32x4) { unimplemented!() } + + #[inline] + pub fn cross(&self, other: F32x4) -> F32x4 { + unimplemented!() + } } impl Index for F32x4 { @@ -428,11 +433,21 @@ mod x86 { unsafe { F32x4(x86_64::_mm_shuffle_ps(self.0, self.0, 0b1011_0001)) } } + #[inline] + pub fn yzxw(self) -> F32x4 { + unsafe { F32x4(x86_64::_mm_shuffle_ps(self.0, self.0, 0b1100_1001)) } + } + #[inline] pub fn ywyw(self) -> F32x4 { unsafe { F32x4(x86_64::_mm_shuffle_ps(self.0, self.0, 0b1101_1101)) } } + #[inline] + pub fn zxyw(self) -> F32x4 { + unsafe { F32x4(x86_64::_mm_shuffle_ps(self.0, self.0, 0b1101_0010)) } + } + #[inline] pub fn zyzy(self) -> F32x4 { unsafe { F32x4(x86_64::_mm_shuffle_ps(self.0, self.0, 0b0110_0110)) } @@ -474,6 +489,11 @@ mod x86 { x86_64::_MM_TRANSPOSE4_PS(&mut a.0, &mut b.0, &mut c.0, &mut d.0) } } + + #[inline] + pub fn cross(&self, other: F32x4) -> F32x4 { + self.yzxw() * other.zxyw() - self.zxyw() * other.yzxw() + } } impl Default for F32x4 {