Add a quick check to skip clipping when an outline is entirely inside or

outside the clip area
This commit is contained in:
Patrick Walton 2019-02-09 19:24:30 -08:00
parent 92421943fc
commit f04c000cae
3 changed files with 91 additions and 6 deletions

View File

@ -541,3 +541,67 @@ impl Edge3D {
prev.lerp(next, alpha as f32)
}
}
/// Coarse collision detection
// Separating axis theorem. Requires that the polygon be convex.
pub(crate) fn rect_is_outside_polygon(rect: RectF32, polygon_points: &[Point2DF32]) -> bool {
let mut outcode = Outcode::all();
for point in polygon_points {
if point.x() > rect.min_x() { outcode.remove(Outcode::LEFT); }
if point.x() < rect.max_x() { outcode.remove(Outcode::RIGHT); }
if point.y() > rect.min_y() { outcode.remove(Outcode::TOP); }
if point.y() < rect.max_y() { outcode.remove(Outcode::BOTTOM); }
}
if !outcode.is_empty() {
return true
}
// FIXME(pcwalton): Check winding!
let rect_points = [rect.origin(), rect.upper_right(), rect.lower_left(), rect.lower_right()];
for (next_point_index, &next) in polygon_points.iter().enumerate() {
let prev_point_index = if next_point_index == 0 {
polygon_points.len() - 1
} else {
next_point_index - 1
};
let prev = polygon_points[prev_point_index];
let polygon_edge_vector = next - prev;
if rect_points.iter().all(|&rect_point| polygon_edge_vector.det(rect_point - prev) < 0.0) {
return true
}
}
false
}
// Edge equation method. Requires that the polygon be convex.
pub(crate) fn rect_is_inside_polygon(rect: RectF32, polygon_points: &[Point2DF32]) -> bool {
// FIXME(pcwalton): Check winding!
let rect_points = [rect.origin(), rect.upper_right(), rect.lower_left(), rect.lower_right()];
for (next_point_index, &next) in polygon_points.iter().enumerate() {
let prev_point_index = if next_point_index == 0 {
polygon_points.len() - 1
} else {
next_point_index - 1
};
let prev = polygon_points[prev_point_index];
let polygon_edge_vector = next - prev;
for &rect_point in &rect_points {
if polygon_edge_vector.det(rect_point - prev) < 0.0 {
return false;
}
}
}
true
}
bitflags! {
struct Outcode: u8 {
const LEFT = 0x01;
const RIGHT = 0x02;
const TOP = 0x04;
const BOTTOM = 0x08;
}
}

View File

@ -15,7 +15,7 @@ use crate::basic::point::Point2DF32;
use crate::basic::rect::RectF32;
use crate::basic::transform2d::Transform2DF32;
use crate::basic::transform3d::Perspective;
use crate::clip::{ContourPolygonClipper, ContourRectClipper};
use crate::clip::{self, ContourPolygonClipper, ContourRectClipper};
use crate::dilation::ContourDilator;
use crate::orientation::Orientation;
use crate::segment::{Segment, SegmentFlags, SegmentKind};
@ -138,7 +138,20 @@ impl Outline {
self.bounds = self.bounds.intersection(view_box).unwrap_or_else(|| RectF32::default());
}
pub fn is_outside_polygon(&self, clip_polygon: &[Point2DF32]) -> bool {
clip::rect_is_outside_polygon(self.bounds, clip_polygon)
}
fn is_inside_polygon(&self, clip_polygon: &[Point2DF32]) -> bool {
clip::rect_is_inside_polygon(self.bounds, clip_polygon)
}
pub fn clip_against_polygon(&mut self, clip_polygon: &[Point2DF32]) {
// Quick check.
if self.is_inside_polygon(clip_polygon) {
return;
}
let mut new_bounds = None;
for contour in mem::replace(&mut self.contours, vec![]) {
let contour = ContourPolygonClipper::new(clip_polygon, contour).clip();

View File

@ -104,19 +104,27 @@ impl Scene {
.collect()
}
fn apply_render_options(&self, outline: &Outline, options: &PreparedRenderOptions) -> Outline {
// FIXME(pcwalton): Don't clone?
let mut outline = (*outline).clone();
fn apply_render_options(&self, original_outline: &Outline, options: &PreparedRenderOptions)
-> Outline {
let mut outline;
match options.transform {
PreparedRenderTransform::Perspective { ref perspective, ref clip_polygon } => {
outline.clip_against_polygon(clip_polygon);
outline.apply_perspective(perspective);
if original_outline.is_outside_polygon(clip_polygon) {
outline = Outline::new();
} else {
outline = (*original_outline).clone();
outline.clip_against_polygon(clip_polygon);
outline.apply_perspective(perspective);
}
}
PreparedRenderTransform::Transform2D(ref transform) => {
// TODO(pcwalton): Short circuit.
outline = (*original_outline).clone();
outline.transform(transform);
outline.clip_against_rect(self.view_box);
}
PreparedRenderTransform::None => {
outline = (*original_outline).clone();
outline.clip_against_rect(self.view_box);
}
}