Generalize clipping to non-axis-aligned lines
This commit is contained in:
parent
3ce60afb44
commit
7da8bdef89
|
@ -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<PathEvent> {
|
||||
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<f32>) -> Edge {
|
||||
Edge(LineSegmentF32::new(&Point2DF32::from_euclid(rect.bottom_left()),
|
||||
&Point2DF32::from_euclid(rect.origin)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn top(rect: &Rect<f32>) -> Edge {
|
||||
Edge(LineSegmentF32::new(&Point2DF32::from_euclid(rect.origin),
|
||||
&Point2DF32::from_euclid(rect.top_right())))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn right(rect: &Rect<f32>) -> Edge {
|
||||
Edge(LineSegmentF32::new(&Point2DF32::from_euclid(rect.top_right()),
|
||||
&Point2DF32::from_euclid(rect.bottom_right())))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn bottom(rect: &Rect<f32>) -> 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<Point2DF32> {
|
||||
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<f32> {
|
||||
/*
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Point2DF32> for LineSegmentF32 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<usize> 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 {
|
||||
|
|
Loading…
Reference in New Issue