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::outline::{Contour, PointFlags};
|
||||||
use crate::point::Point2DF32;
|
use crate::point::Point2DF32;
|
||||||
use crate::segment::Segment;
|
use crate::segment::Segment;
|
||||||
|
use crate::simd::F32x4;
|
||||||
use crate::util::lerp;
|
use crate::util::lerp;
|
||||||
use arrayvec::ArrayVec;
|
use euclid::Rect;
|
||||||
use euclid::{Point2D, Rect, Vector3D};
|
|
||||||
use lyon_path::PathEvent;
|
use lyon_path::PathEvent;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
@ -33,10 +33,10 @@ impl<'a> RectClipper<'a> {
|
||||||
|
|
||||||
pub fn clip(&self) -> Vec<PathEvent> {
|
pub fn clip(&self) -> Vec<PathEvent> {
|
||||||
let mut output = self.subject.to_vec();
|
let mut output = self.subject.to_vec();
|
||||||
self.clip_against(Edge::Left(self.clip_rect.origin.x), &mut output);
|
self.clip_against(Edge::left(&self.clip_rect), &mut output);
|
||||||
self.clip_against(Edge::Top(self.clip_rect.origin.y), &mut output);
|
self.clip_against(Edge::top(&self.clip_rect), &mut output);
|
||||||
self.clip_against(Edge::Right(self.clip_rect.max_x()), &mut output);
|
self.clip_against(Edge::right(&self.clip_rect), &mut output);
|
||||||
self.clip_against(Edge::Bottom(self.clip_rect.max_y()), &mut output);
|
self.clip_against(Edge::bottom(&self.clip_rect), &mut output);
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,13 +66,17 @@ impl<'a> RectClipper<'a> {
|
||||||
|
|
||||||
if edge.point_is_inside(&to) {
|
if edge.point_is_inside(&to) {
|
||||||
if !edge.point_is_inside(&from) {
|
if !edge.point_is_inside(&from) {
|
||||||
let intersection = edge.line_intersection(&LineSegmentF32::new(&from, &to));
|
let line_segment = LineSegmentF32::new(&from, &to);
|
||||||
add_line(&intersection, output, &mut first_point);
|
if let Some(intersection) = edge.line_intersection(&line_segment) {
|
||||||
|
add_line(&intersection, output, &mut first_point);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
add_line(&to, output, &mut first_point);
|
add_line(&to, output, &mut first_point);
|
||||||
} else if edge.point_is_inside(&from) {
|
} else if edge.point_is_inside(&from) {
|
||||||
let intersection = edge.line_intersection(&LineSegmentF32::new(&from, &to));
|
let line_segment = LineSegmentF32::new(&from, &to);
|
||||||
add_line(&intersection, output, &mut first_point);
|
if let Some(intersection) = edge.line_intersection(&line_segment) {
|
||||||
|
add_line(&intersection, output, &mut first_point);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
from = to;
|
from = to;
|
||||||
|
@ -96,25 +100,41 @@ impl<'a> RectClipper<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
enum Edge {
|
struct Edge(LineSegmentF32);
|
||||||
Left(f32),
|
|
||||||
Top(f32),
|
|
||||||
Right(f32),
|
|
||||||
Bottom(f32),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Edge {
|
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 {
|
fn point_is_inside(&self, point: &Point2DF32) -> bool {
|
||||||
match *self {
|
(self.0.to() - self.0.from()).det(*point - self.0.from()) >= 0.0
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trivially_test_segment(&self, segment: &Segment) -> EdgeRelativeLocation {
|
fn trivially_test_segment(&self, segment: &Segment) -> EdgeRelativeLocation {
|
||||||
let from_inside = self.point_is_inside(&segment.baseline.from());
|
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()) {
|
if from_inside != self.point_is_inside(&segment.baseline.to()) {
|
||||||
return EdgeRelativeLocation::Intersecting;
|
return EdgeRelativeLocation::Intersecting;
|
||||||
}
|
}
|
||||||
|
@ -131,14 +151,18 @@ impl Edge {
|
||||||
if from_inside { EdgeRelativeLocation::Inside } else { EdgeRelativeLocation::Outside }
|
if from_inside { EdgeRelativeLocation::Inside } else { EdgeRelativeLocation::Outside }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn line_intersection(&self, line_segment: &LineSegmentF32) -> Point2DF32 {
|
fn line_intersection(&self, other: &LineSegmentF32) -> Option<Point2DF32> {
|
||||||
match *self {
|
let (this_line, other_line) = (self.0.line_coords(), other.line_coords());
|
||||||
Edge::Left(x_edge) | Edge::Right(x_edge) => {
|
let result = this_line.cross(other_line);
|
||||||
Point2DF32::new(x_edge, line_segment.solve_y_for_x(x_edge))
|
let z = result[2];
|
||||||
}
|
if z == 0.0 {
|
||||||
Edge::Top(y_edge) | Edge::Bottom(y_edge) => {
|
return None;
|
||||||
Point2DF32::new(line_segment.solve_x_for_y(y_edge), y_edge)
|
}
|
||||||
}
|
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)> {
|
fn split_line_segment(&self, segment: &Segment) -> Option<(Segment, Segment)> {
|
||||||
let intersection;
|
let intersection = self.line_intersection(&segment.baseline)?;
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Some((Segment::line(&LineSegmentF32::new(&segment.baseline.from(), &intersection)),
|
Some((Segment::line(&LineSegmentF32::new(&segment.baseline.from(), &intersection)),
|
||||||
Segment::line(&LineSegmentF32::new(&intersection, &segment.baseline.to()))))
|
Segment::line(&LineSegmentF32::new(&intersection, &segment.baseline.to()))))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn intersect_cubic_segment(&self, segment: &Segment, t_min: f32, t_max: f32) -> Option<f32> {
|
fn intersect_cubic_segment(&self, segment: &Segment, t_min: f32, t_max: f32) -> Option<f32> {
|
||||||
/*
|
/*println!("... intersect_cubic_segment({:?}, {:?}, t=({}, {}))",
|
||||||
println!("... intersect_cubic_segment({:?}, {:?}, t=({}, {}))",
|
self, segment, t_min, t_max);*/
|
||||||
self, segment, t_min, t_max);
|
|
||||||
*/
|
|
||||||
let t_mid = lerp(t_min, t_max, 0.5);
|
let t_mid = lerp(t_min, t_max, 0.5);
|
||||||
if t_max - t_min < 0.001 {
|
if t_max - t_min < 0.001 {
|
||||||
return Some(t_mid);
|
return Some(t_mid);
|
||||||
|
@ -196,30 +200,12 @@ impl Edge {
|
||||||
let prev_cubic_segment = prev_segment.as_cubic_segment();
|
let prev_cubic_segment = prev_segment.as_cubic_segment();
|
||||||
let next_cubic_segment = next_segment.as_cubic_segment();
|
let next_cubic_segment = next_segment.as_cubic_segment();
|
||||||
|
|
||||||
let (prev_min, prev_max, next_min, next_max, edge);
|
if self.line_intersection(&prev_segment.baseline).is_some() {
|
||||||
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 {
|
|
||||||
self.intersect_cubic_segment(segment, t_min, t_mid)
|
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)
|
self.intersect_cubic_segment(segment, t_mid, t_max)
|
||||||
} else if (prev_max == edge && next_min == edge) ||
|
} else if prev_segment.baseline.to() == self.0.from() ||
|
||||||
(prev_min == edge && next_max == edge) {
|
prev_segment.baseline.to() == self.0.to() {
|
||||||
Some(t_mid)
|
Some(t_mid)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -227,8 +213,20 @@ impl Edge {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fixup_clipped_segments(&self, segment: &(Segment, Segment)) -> (Segment, Segment) {
|
fn fixup_clipped_segments(&self, segment: &(Segment, Segment)) -> (Segment, Segment) {
|
||||||
let (mut before, mut after) = *segment;
|
let (mut prev, mut next) = *segment;
|
||||||
match *self {
|
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) => {
|
Edge::Left(x) | Edge::Right(x) => {
|
||||||
before.baseline.set_to_x(x);
|
before.baseline.set_to_x(x);
|
||||||
after.baseline.set_from_x(x);
|
after.baseline.set_from_x(x);
|
||||||
|
@ -237,8 +235,9 @@ impl Edge {
|
||||||
before.baseline.set_to_y(y);
|
before.baseline.set_to_y(y);
|
||||||
after.baseline.set_from_y(y);
|
after.baseline.set_from_y(y);
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
(before, after)
|
|
||||||
|
(prev, next)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,10 +257,23 @@ impl ContourRectClipper {
|
||||||
return self.contour
|
return self.contour
|
||||||
}
|
}
|
||||||
|
|
||||||
self.clip_against(Edge::Left(self.clip_rect.origin.x));
|
self.clip_against(Edge::left(&self.clip_rect));
|
||||||
self.clip_against(Edge::Top(self.clip_rect.origin.y));
|
self.clip_against(Edge::top(&self.clip_rect));
|
||||||
self.clip_against(Edge::Right(self.clip_rect.max_x()));
|
self.clip_against(Edge::right(&self.clip_rect));
|
||||||
self.clip_against(Edge::Bottom(self.clip_rect.max_y()));
|
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
|
self.contour
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -203,6 +203,17 @@ impl LineSegmentF32 {
|
||||||
let (dx, dy) = (self.to_x() - self.from_x(), self.to_y() - self.from_y());
|
let (dx, dy) = (self.to_x() - self.from_x(), self.to_y() - self.from_y());
|
||||||
dx * dx + dy * dy
|
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 {
|
impl Sub<Point2DF32> for LineSegmentF32 {
|
||||||
|
|
|
@ -65,6 +65,12 @@ impl Point2DF32 {
|
||||||
pub fn max(&self, other: Point2DF32) -> Point2DF32 {
|
pub fn max(&self, other: Point2DF32) -> Point2DF32 {
|
||||||
Point2DF32(self.0.max(other.0))
|
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 {
|
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) {
|
pub fn transpose4(a: &mut F32x4, b: &mut F32x4, c: &mut F32x4, d: &mut F32x4) {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn cross(&self, other: F32x4) -> F32x4 {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<usize> for F32x4 {
|
impl Index<usize> for F32x4 {
|
||||||
|
@ -428,11 +433,21 @@ mod x86 {
|
||||||
unsafe { F32x4(x86_64::_mm_shuffle_ps(self.0, self.0, 0b1011_0001)) }
|
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]
|
#[inline]
|
||||||
pub fn ywyw(self) -> F32x4 {
|
pub fn ywyw(self) -> F32x4 {
|
||||||
unsafe { F32x4(x86_64::_mm_shuffle_ps(self.0, self.0, 0b1101_1101)) }
|
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]
|
#[inline]
|
||||||
pub fn zyzy(self) -> F32x4 {
|
pub fn zyzy(self) -> F32x4 {
|
||||||
unsafe { F32x4(x86_64::_mm_shuffle_ps(self.0, self.0, 0b0110_0110)) }
|
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)
|
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 {
|
impl Default for F32x4 {
|
||||||
|
|
Loading…
Reference in New Issue