Use a fast path for axis aligned clipping
This commit is contained in:
parent
805f0c9fa7
commit
4c5e351829
|
@ -113,32 +113,13 @@ impl TEdge for Edge {
|
||||||
area >= 0.0
|
area >= 0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn intersect_segment(&self, segment: &Segment) -> ArrayVec<[f32; 3]> {
|
fn intersect_line_segment(&self, segment: &LineSegmentF32) -> ArrayVec<[f32; 3]> {
|
||||||
if segment.is_line() {
|
|
||||||
return self.intersect_line_segment(&segment.baseline);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut segment = *segment;
|
|
||||||
if segment.is_quadratic() {
|
|
||||||
segment = segment.to_cubic();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut results = ArrayVec::new();
|
let mut results = ArrayVec::new();
|
||||||
let mut prev_t = 0.0;
|
let t = segment.intersection_t(&self.0);
|
||||||
while !results.is_full() {
|
if t >= 0.0 && t <= 1.0 {
|
||||||
if prev_t >= 1.0 {
|
results.push(t);
|
||||||
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;
|
results
|
||||||
|
|
||||||
const EPSILON: f32 = 0.0001;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,15 +147,90 @@ impl Edge {
|
||||||
Edge(LineSegmentF32::new(&Point2DF32::from_euclid(rect.bottom_right()),
|
Edge(LineSegmentF32::new(&Point2DF32::from_euclid(rect.bottom_right()),
|
||||||
&Point2DF32::from_euclid(rect.bottom_left())))
|
&Point2DF32::from_euclid(rect.bottom_left())))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
enum AxisAlignedEdge {
|
||||||
|
Left(f32),
|
||||||
|
Top(f32),
|
||||||
|
Right(f32),
|
||||||
|
Bottom(f32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TEdge for AxisAlignedEdge {
|
||||||
|
#[inline]
|
||||||
|
fn point_is_inside(&self, point: &Point2DF32) -> bool {
|
||||||
|
match *self {
|
||||||
|
AxisAlignedEdge::Left(x) => point.x() >= x,
|
||||||
|
AxisAlignedEdge::Top(y) => point.y() >= y,
|
||||||
|
AxisAlignedEdge::Right(x) => point.x() <= x,
|
||||||
|
AxisAlignedEdge::Bottom(y) => point.y() <= y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn intersect_line_segment(&self, segment: &LineSegmentF32) -> ArrayVec<[f32; 3]> {
|
fn intersect_line_segment(&self, segment: &LineSegmentF32) -> ArrayVec<[f32; 3]> {
|
||||||
let mut results = ArrayVec::new();
|
let mut results = ArrayVec::new();
|
||||||
let t = segment.intersection_t(&self.0);
|
let t = match *self {
|
||||||
|
AxisAlignedEdge::Left(x) | AxisAlignedEdge::Right(x) => segment.solve_t_for_x(x),
|
||||||
|
AxisAlignedEdge::Top(y) | AxisAlignedEdge::Bottom(y) => segment.solve_t_for_y(y),
|
||||||
|
};
|
||||||
if t >= 0.0 && t <= 1.0 {
|
if t >= 0.0 && t <= 1.0 {
|
||||||
results.push(t);
|
results.push(t);
|
||||||
}
|
}
|
||||||
results
|
results
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait TEdge {
|
||||||
|
fn point_is_inside(&self, point: &Point2DF32) -> bool;
|
||||||
|
fn intersect_line_segment(&self, segment: &LineSegmentF32) -> 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 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intersect_segment(&self, segment: &Segment) -> ArrayVec<[f32; 3]> {
|
||||||
|
if segment.is_line() {
|
||||||
|
return self.intersect_line_segment(&segment.baseline);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut segment = *segment;
|
||||||
|
if segment.is_quadratic() {
|
||||||
|
segment = segment.to_cubic();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 intersect_cubic_segment(&self, segment: &Segment, mut t_min: f32, mut t_max: f32)
|
fn intersect_cubic_segment(&self, segment: &Segment, mut t_min: f32, mut t_max: f32)
|
||||||
-> Option<f32> {
|
-> Option<f32> {
|
||||||
|
@ -214,31 +270,6 @@ impl Edge {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
trait ContourClipper where Self::Edge: TEdge {
|
||||||
type Edge;
|
type Edge;
|
||||||
|
|
||||||
|
@ -305,6 +336,8 @@ trait ContourClipper where Self::Edge: TEdge {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// General convex polygon clipping in 2D
|
||||||
|
|
||||||
pub(crate) struct ContourPolygonClipper {
|
pub(crate) struct ContourPolygonClipper {
|
||||||
clip_polygon: SmallVec<[Point2DF32; 4]>,
|
clip_polygon: SmallVec<[Point2DF32; 4]>,
|
||||||
contour: Contour,
|
contour: Contour,
|
||||||
|
@ -325,21 +358,8 @@ impl ContourPolygonClipper {
|
||||||
ContourPolygonClipper { clip_polygon: SmallVec::from_slice(clip_polygon), contour }
|
ContourPolygonClipper { clip_polygon: SmallVec::from_slice(clip_polygon), contour }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn from_rect(clip_rect: &Rect<f32>, 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()),
|
|
||||||
Point2DF32::from_euclid(clip_rect.bottom_left()),
|
|
||||||
], contour)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn clip(mut self) -> Contour {
|
pub(crate) fn clip(mut self) -> Contour {
|
||||||
// TODO(pcwalton): Reenable this optimization.
|
// TODO(pcwalton): Maybe have a coarse circumscribed rect and use that for clipping?
|
||||||
/*if self.clip_rect.contains_rect(&self.contour.bounds()) {
|
|
||||||
return self.contour
|
|
||||||
}*/
|
|
||||||
|
|
||||||
let clip_polygon = mem::replace(&mut self.clip_polygon, SmallVec::default());
|
let clip_polygon = mem::replace(&mut self.clip_polygon, SmallVec::default());
|
||||||
let mut prev = match clip_polygon.last() {
|
let mut prev = match clip_polygon.last() {
|
||||||
|
@ -361,6 +381,43 @@ enum EdgeRelativeLocation {
|
||||||
Outside,
|
Outside,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fast axis-aligned box 2D clipping
|
||||||
|
|
||||||
|
pub(crate) struct ContourRectClipper {
|
||||||
|
clip_rect: Rect<f32>,
|
||||||
|
contour: Contour,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContourClipper for ContourRectClipper {
|
||||||
|
type Edge = AxisAlignedEdge;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn contour_mut(&mut self) -> &mut Contour {
|
||||||
|
&mut self.contour
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContourRectClipper {
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn new(clip_rect: &Rect<f32>, contour: Contour) -> ContourRectClipper {
|
||||||
|
ContourRectClipper { clip_rect: *clip_rect, contour }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn clip(mut self) -> Contour {
|
||||||
|
// TODO(pcwalton): Reenable this optimization.
|
||||||
|
/*if self.clip_rect.contains_rect(&self.contour.bounds()) {
|
||||||
|
return self.contour
|
||||||
|
}*/
|
||||||
|
|
||||||
|
self.clip_against(AxisAlignedEdge::Left(self.clip_rect.origin.x));
|
||||||
|
self.clip_against(AxisAlignedEdge::Top(self.clip_rect.origin.y));
|
||||||
|
self.clip_against(AxisAlignedEdge::Right(self.clip_rect.max_x()));
|
||||||
|
self.clip_against(AxisAlignedEdge::Bottom(self.clip_rect.max_y()));
|
||||||
|
|
||||||
|
self.contour
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 3D quad clipping
|
// 3D quad clipping
|
||||||
|
|
||||||
pub struct PolygonClipper3D {
|
pub struct PolygonClipper3D {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
//! A compressed in-memory representation of paths.
|
//! A compressed in-memory representation of paths.
|
||||||
|
|
||||||
use crate::clip::ContourPolygonClipper;
|
use crate::clip::{ContourPolygonClipper, ContourRectClipper};
|
||||||
use crate::line_segment::LineSegmentF32;
|
use crate::line_segment::LineSegmentF32;
|
||||||
use crate::monotonic::MonotonicConversionIter;
|
use crate::monotonic::MonotonicConversionIter;
|
||||||
use crate::point::Point2DF32;
|
use crate::point::Point2DF32;
|
||||||
|
@ -139,7 +139,7 @@ impl Outline {
|
||||||
|
|
||||||
pub fn clip_against_rect(&mut self, clip_rect: &Rect<f32>) {
|
pub fn clip_against_rect(&mut self, clip_rect: &Rect<f32>) {
|
||||||
for contour in mem::replace(&mut self.contours, vec![]) {
|
for contour in mem::replace(&mut self.contours, vec![]) {
|
||||||
let contour = ContourPolygonClipper::from_rect(clip_rect, contour).clip();
|
let contour = ContourRectClipper::new(clip_rect, contour).clip();
|
||||||
if !contour.is_empty() {
|
if !contour.is_empty() {
|
||||||
self.push_contour(contour);
|
self.push_contour(contour);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue