Reimplement segment offsetting

This commit is contained in:
Patrick Walton 2019-02-20 16:21:58 -08:00
parent d75ed71af4
commit b9e3952246
4 changed files with 112 additions and 60 deletions

View File

@ -244,8 +244,12 @@ impl LineSegmentF32 {
}
#[inline]
pub fn offset(&self, amount: f32) -> LineSegmentF32 {
*self + self.vector().yx().normalize().scale_xy(Point2DF32::new(-amount, amount))
pub fn offset(&self, distance: f32) -> LineSegmentF32 {
if self.is_zero_length() {
*self
} else {
*self + self.vector().yx().normalize().scale_xy(Point2DF32::new(-distance, distance))
}
}
#[inline]

View File

@ -133,6 +133,11 @@ impl Point2DF32 {
pub fn is_zero(&self) -> bool {
*self == Point2DF32::default()
}
#[inline]
pub fn lerp(&self, other: Point2DF32, t: f32) -> Point2DF32 {
*self + (other - *self).scale(t)
}
}
impl PartialEq for Point2DF32 {

View File

@ -245,6 +245,8 @@ impl Contour {
point: Point2DF32,
flags: PointFlags,
update_bounds: bool) {
debug_assert!(!point.x().is_nan() && !point.y().is_nan());
if update_bounds {
let first = self.is_empty();
union_rect(&mut self.bounds, point, first);
@ -274,6 +276,20 @@ impl Contour {
self.push_point(segment.baseline.to(), PointFlags::empty(), update_bounds);
}
#[inline]
pub(crate) fn push_full_segment(&mut self, segment: &Segment, update_bounds: bool) {
self.push_point(segment.baseline.from(), PointFlags::empty(), update_bounds);
if !segment.is_line() {
self.push_point(segment.ctrl.from(), PointFlags::CONTROL_POINT_0, update_bounds);
if !segment.is_quadratic() {
self.push_point(segment.ctrl.to(), PointFlags::CONTROL_POINT_1, update_bounds);
}
}
self.push_point(segment.baseline.to(), PointFlags::empty(), update_bounds);
}
#[inline]
pub fn segment_after(&self, point_index: u32) -> Segment {
debug_assert!(self.point_is_endpoint(point_index));

View File

@ -13,6 +13,7 @@
use crate::basic::line_segment::LineSegmentF32;
use crate::basic::rect::RectF32;
use crate::outline::{Contour, Outline};
use crate::segment::Segment as SegmentPF3;
use crate::segments::{Segment, SegmentIter};
use lyon_path::PathEvent;
use lyon_path::iterator::PathIterator;
@ -192,68 +193,94 @@ impl ContourStrokeToFill {
}
fn offset_forward(&mut self) {
for point_index in 0..(self.input.points.len() as u32) {
let mut prev_point_index = self.input.prev_point_index_of(point_index);
while prev_point_index != point_index &&
self.input.position_of(prev_point_index) ==
self.input.position_of(point_index) {
prev_point_index = self.input.prev_point_index_of(prev_point_index);
}
let mut next_point_index = self.input.next_point_index_of(point_index);
while next_point_index != point_index &&
self.input.position_of(next_point_index) ==
self.input.position_of(point_index) {
next_point_index = self.input.next_point_index_of(next_point_index);
}
let prev_line_segment = LineSegmentF32::new(&self.input.position_of(prev_point_index),
&self.input.position_of(point_index));
let next_line_segment = LineSegmentF32::new(&self.input.position_of(point_index),
&self.input.position_of(next_point_index));
let prev_offset_line_segment = prev_line_segment.offset(self.radius);
let next_offset_line_segment = next_line_segment.offset(self.radius);
let new_position;
match prev_offset_line_segment.intersection_t(&next_offset_line_segment) {
None => new_position = self.input.position_of(point_index),
Some(t) => new_position = prev_offset_line_segment.sample(t),
}
self.output.push_point(new_position, self.input.flags[point_index as usize], true);
for segment in self.input.iter() {
segment.offset(self.radius, &mut self.output);
}
}
fn offset_backward(&mut self) {
for point_index in (0..(self.input.points.len() as u32)).rev() {
let mut prev_point_index = self.input.prev_point_index_of(point_index);
while prev_point_index != point_index &&
self.input.position_of(prev_point_index) ==
self.input.position_of(point_index) {
prev_point_index = self.input.prev_point_index_of(prev_point_index);
}
let mut next_point_index = self.input.next_point_index_of(point_index);
while next_point_index != point_index &&
self.input.position_of(next_point_index) ==
self.input.position_of(point_index) {
next_point_index = self.input.next_point_index_of(next_point_index);
}
let prev_line_segment = LineSegmentF32::new(&self.input.position_of(prev_point_index),
&self.input.position_of(point_index));
let next_line_segment = LineSegmentF32::new(&self.input.position_of(point_index),
&self.input.position_of(next_point_index));
let prev_offset_line_segment = prev_line_segment.offset(-self.radius);
let next_offset_line_segment = next_line_segment.offset(-self.radius);
let new_position;
match prev_offset_line_segment.intersection_t(&next_offset_line_segment) {
None => new_position = self.input.position_of(point_index),
Some(t) => new_position = prev_offset_line_segment.sample(t),
}
self.output.push_point(new_position, self.input.flags[point_index as usize], true);
// FIXME(pcwalton)
let mut segments: Vec<_> = self.input.iter().map(|segment| segment.reversed()).collect();
segments.reverse();
for segment in &segments {
segment.offset(self.radius, &mut self.output);
}
}
}
trait Offset {
fn offset(&self, distance: f32, contour: &mut Contour);
}
impl Offset for SegmentPF3 {
fn offset(&self, distance: f32, contour: &mut Contour) {
if self.is_line() {
contour.push_full_segment(&SegmentPF3::line(&self.baseline.offset(distance)), true);
return;
}
if self.is_quadratic() {
let mut segment_0 = LineSegmentF32::new(&self.baseline.from(), &self.ctrl.from());
let mut segment_1 = LineSegmentF32::new(&self.ctrl.from(), &self.baseline.to());
segment_0 = segment_0.offset(distance);
segment_1 = segment_1.offset(distance);
let ctrl = match segment_0.intersection_t(&segment_1) {
Some(t) => segment_0.sample(t),
None => segment_0.to().lerp(segment_1.from(), 0.5),
};
let baseline = LineSegmentF32::new(&segment_0.from(), &segment_1.to());
contour.push_full_segment(&SegmentPF3::quadratic(&baseline, &ctrl), true);
return;
}
debug_assert!(self.is_cubic());
if self.baseline.from() == self.ctrl.from() {
let mut segment_0 = LineSegmentF32::new(&self.baseline.from(), &self.ctrl.to());
let mut segment_1 = LineSegmentF32::new(&self.ctrl.to(), &self.baseline.to());
segment_0 = segment_0.offset(distance);
segment_1 = segment_1.offset(distance);
let ctrl = match segment_0.intersection_t(&segment_1) {
Some(t) => segment_0.sample(t),
None => segment_0.to().lerp(segment_1.from(), 0.5),
};
let baseline = LineSegmentF32::new(&segment_0.from(), &segment_1.to());
let ctrl = LineSegmentF32::new(&segment_0.from(), &ctrl);
contour.push_full_segment(&SegmentPF3::cubic(&baseline, &ctrl), true);
return;
}
if self.ctrl.to() == self.baseline.to() {
let mut segment_0 = LineSegmentF32::new(&self.baseline.from(), &self.ctrl.from());
let mut segment_1 = LineSegmentF32::new(&self.ctrl.from(), &self.baseline.to());
segment_0 = segment_0.offset(distance);
segment_1 = segment_1.offset(distance);
let ctrl = match segment_0.intersection_t(&segment_1) {
Some(t) => segment_0.sample(t),
None => segment_0.to().lerp(segment_1.from(), 0.5),
};
let baseline = LineSegmentF32::new(&segment_0.from(), &segment_1.to());
let ctrl = LineSegmentF32::new(&ctrl, &segment_1.to());
contour.push_full_segment(&SegmentPF3::cubic(&baseline, &ctrl), true);
return;
}
let mut segment_0 = LineSegmentF32::new(&self.baseline.from(), &self.ctrl.from());
let mut segment_1 = LineSegmentF32::new(&self.ctrl.from(), &self.ctrl.to());
let mut segment_2 = LineSegmentF32::new(&self.ctrl.to(), &self.baseline.to());
segment_0 = segment_0.offset(distance);
segment_1 = segment_1.offset(distance);
segment_2 = segment_2.offset(distance);
let (ctrl_0, ctrl_1) = match (segment_0.intersection_t(&segment_1),
segment_1.intersection_t(&segment_2)) {
(Some(t0), Some(t1)) => (segment_0.sample(t0), segment_1.sample(t1)),
_ => {
(segment_0.to().lerp(segment_1.from(), 0.5),
segment_1.to().lerp(segment_2.from(), 0.5))
}
};
let baseline = LineSegmentF32::new(&segment_0.from(), &segment_2.to());
let ctrl = LineSegmentF32::new(&ctrl_0, &ctrl_1);
contour.push_full_segment(&SegmentPF3::cubic(&baseline, &ctrl), true);
}
}