From 805f0c9fa75ea527b7bdab6fff618bd176c604de Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 1 Feb 2019 17:27:23 -0800 Subject: [PATCH] Trait-ify the 2D clipper --- geometry/src/clip.rs | 243 ++++++++++++++++++++++------------------ geometry/src/outline.rs | 6 +- 2 files changed, 136 insertions(+), 113 deletions(-) diff --git a/geometry/src/clip.rs b/geometry/src/clip.rs index 3ec5fe1f..d4ef6174 100644 --- a/geometry/src/clip.rs +++ b/geometry/src/clip.rs @@ -105,31 +105,7 @@ impl<'a> RectClipper<'a> { #[derive(Clone, Copy, Debug)] 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()))) - } - +impl TEdge for Edge { #[inline] fn point_is_inside(&self, point: &Point2DF32) -> bool { let area = (self.0.to() - self.0.from()).det(*point - self.0.from()); @@ -137,25 +113,6 @@ impl Edge { area >= 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; - } - if !segment.is_line() { - if from_inside != self.point_is_inside(&segment.ctrl.from()) { - return EdgeRelativeLocation::Intersecting; - } - if !segment.is_quadratic() { - if from_inside != self.point_is_inside(&segment.ctrl.to()) { - return EdgeRelativeLocation::Intersecting; - } - } - } - if from_inside { EdgeRelativeLocation::Inside } else { EdgeRelativeLocation::Outside } - } - fn intersect_segment(&self, segment: &Segment) -> ArrayVec<[f32; 3]> { if segment.is_line() { return self.intersect_line_segment(&segment.baseline); @@ -183,6 +140,32 @@ impl Edge { const EPSILON: f32 = 0.0001; } +} + +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()))) + } fn intersect_line_segment(&self, segment: &LineSegmentF32) -> ArrayVec<[f32; 3]> { let mut results = ArrayVec::new(); @@ -231,20 +214,120 @@ impl Edge { } } -pub(crate) struct ContourClipper { +trait TEdge { + fn point_is_inside(&self, point: &Point2DF32) -> bool; + fn intersect_segment(&self, segment: &Segment) -> ArrayVec<[f32; 3]>; + + 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; + } + if !segment.is_line() { + if from_inside != self.point_is_inside(&segment.ctrl.from()) { + return EdgeRelativeLocation::Intersecting; + } + if !segment.is_quadratic() { + if from_inside != self.point_is_inside(&segment.ctrl.to()) { + return EdgeRelativeLocation::Intersecting; + } + } + } + if from_inside { EdgeRelativeLocation::Inside } else { EdgeRelativeLocation::Outside } + } + +} + +trait ContourClipper where Self::Edge: TEdge { + type Edge; + + fn contour_mut(&mut self) -> &mut Contour; + + fn clip_against(&mut self, edge: Self::Edge) { + let input = mem::replace(self.contour_mut(), Contour::new()); + for mut segment in input.iter() { + // Easy cases. + match edge.trivially_test_segment(&segment) { + EdgeRelativeLocation::Outside => continue, + EdgeRelativeLocation::Inside => { + //println!("trivial test inside, pushing segment"); + push_segment(self.contour_mut(), &segment); + continue; + } + EdgeRelativeLocation::Intersecting => {} + } + + // We have a potential intersection. + //println!("potential intersection: {:?} edge: {:?}", segment, edge); + let mut starts_inside = edge.point_is_inside(&segment.baseline.from()); + 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={:?} t={:?} starts_inside={:?}", + edge.0, + before_split, + t, + starts_inside);*/ + if starts_inside { + //println!("... split segment case, pushing segment"); + push_segment(self.contour_mut(), &before_split); + } + + // We've now transitioned from inside to outside or vice versa. + starts_inside = !starts_inside; + last_t = t; + segment = after_split; + } + + // No more intersections. Push the last segment if applicable. + if starts_inside { + //println!("... last segment case, pushing segment"); + push_segment(self.contour_mut(), &segment); + } + } + + fn push_segment(contour: &mut Contour, segment: &Segment) { + //println!("... push_segment({:?}, edge={:?}", segment, edge); + if let Some(last_position) = contour.last_position() { + if last_position != segment.baseline.from() { + // Add a line to join up segments. + contour.push_point(segment.baseline.from(), PointFlags::empty()); + } + } + + contour.push_segment(*segment); + } + } +} + +pub(crate) struct ContourPolygonClipper { clip_polygon: SmallVec<[Point2DF32; 4]>, contour: Contour, } -impl ContourClipper { +impl ContourClipper for ContourPolygonClipper { + type Edge = Edge; + #[inline] - pub(crate) fn new(clip_polygon: &[Point2DF32], contour: Contour) -> ContourClipper { - ContourClipper { clip_polygon: SmallVec::from_slice(clip_polygon), contour } + fn contour_mut(&mut self) -> &mut Contour { + &mut self.contour + } +} + +impl ContourPolygonClipper { + #[inline] + pub(crate) fn new(clip_polygon: &[Point2DF32], contour: Contour) -> ContourPolygonClipper { + ContourPolygonClipper { clip_polygon: SmallVec::from_slice(clip_polygon), contour } } #[inline] - pub(crate) fn from_rect(clip_rect: &Rect, contour: Contour) -> ContourClipper { - ContourClipper::new(&[ + pub(crate) fn from_rect(clip_rect: &Rect, contour: Contour) -> ContourPolygonClipper { + ContourPolygonClipper::new(&[ Point2DF32::from_euclid(clip_rect.origin), Point2DF32::from_euclid(clip_rect.top_right()), Point2DF32::from_euclid(clip_rect.bottom_right()), @@ -270,66 +353,6 @@ impl ContourClipper { self.contour } - - fn clip_against(&mut self, edge: Edge) { - let input = mem::replace(&mut self.contour, Contour::new()); - for mut segment in input.iter() { - // Easy cases. - match edge.trivially_test_segment(&segment) { - EdgeRelativeLocation::Outside => continue, - EdgeRelativeLocation::Inside => { - //println!("trivial test inside, pushing segment"); - push_segment(&mut self.contour, &segment, edge); - continue; - } - EdgeRelativeLocation::Intersecting => {} - } - - // We have a potential intersection. - //println!("potential intersection: {:?} edge: {:?}", segment, edge); - let mut starts_inside = edge.point_is_inside(&segment.baseline.from()); - 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={:?} t={:?} starts_inside={:?}", - edge.0, - before_split, - t, - starts_inside);*/ - if starts_inside { - //println!("... split segment case, pushing segment"); - push_segment(&mut self.contour, &before_split, edge); - } - - // We've now transitioned from inside to outside or vice versa. - starts_inside = !starts_inside; - last_t = t; - segment = after_split; - } - - // No more intersections. Push the last segment if applicable. - if starts_inside { - //println!("... last segment case, pushing segment"); - push_segment(&mut self.contour, &segment, edge); - } - } - - fn push_segment(contour: &mut Contour, segment: &Segment, edge: Edge) { - //println!("... push_segment({:?}, edge={:?}", segment, edge); - if let Some(last_position) = contour.last_position() { - if last_position != segment.baseline.from() { - // Add a line to join up segments. - contour.push_point(segment.baseline.from(), PointFlags::empty()); - } - } - - contour.push_segment(*segment); - } - } } enum EdgeRelativeLocation { diff --git a/geometry/src/outline.rs b/geometry/src/outline.rs index 1d13246e..aa681bb4 100644 --- a/geometry/src/outline.rs +++ b/geometry/src/outline.rs @@ -10,7 +10,7 @@ //! A compressed in-memory representation of paths. -use crate::clip::ContourClipper; +use crate::clip::ContourPolygonClipper; use crate::line_segment::LineSegmentF32; use crate::monotonic::MonotonicConversionIter; use crate::point::Point2DF32; @@ -130,7 +130,7 @@ impl Outline { pub fn clip_against_polygon(&mut self, clip_polygon: &[Point2DF32]) { for contour in mem::replace(&mut self.contours, vec![]) { - let contour = ContourClipper::new(clip_polygon, contour).clip(); + let contour = ContourPolygonClipper::new(clip_polygon, contour).clip(); if !contour.is_empty() { self.push_contour(contour); } @@ -139,7 +139,7 @@ impl Outline { pub fn clip_against_rect(&mut self, clip_rect: &Rect) { for contour in mem::replace(&mut self.contours, vec![]) { - let contour = ContourClipper::from_rect(clip_rect, contour).clip(); + let contour = ContourPolygonClipper::from_rect(clip_rect, contour).clip(); if !contour.is_empty() { self.push_contour(contour); }