WIP before moving to a segment basis

This commit is contained in:
Patrick Walton 2019-02-20 12:41:43 -08:00
parent c7a1d9e960
commit d75ed71af4
5 changed files with 161 additions and 14 deletions

View File

@ -13,7 +13,7 @@
use crate::basic::point::Point2DF32;
use crate::util;
use pathfinder_simd::default::F32x4;
use std::ops::Sub;
use std::ops::{Add, Sub};
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub struct LineSegmentF32(pub F32x4);
@ -224,20 +224,42 @@ impl LineSegmentF32 {
}
// http://www.cs.swan.ac.uk/~cssimon/line_intersection.html
pub fn intersection_t(&self, other: &LineSegmentF32) -> f32 {
pub fn intersection_t(&self, other: &LineSegmentF32) -> Option<f32> {
let d0d1 = self.vector().0.concat_xy_xy(other.vector().0);
let offset = other.from() - self.from();
let factors = d0d1.concat_wz_yx(offset.0);
let terms = d0d1 * factors;
let t = (terms[3] - terms[2]) / (terms[0] - terms[1]);
//println!("intersection_t({:?}, {:?})={} (d0d1={:?}, factors={:?})", self, other, t, d0d1, factors);
t
let denom = terms[0] - terms[1];
if f32::abs(denom) < EPSILON {
return None;
}
return Some((terms[3] - terms[2]) / denom);
const EPSILON: f32 = 0.0001;
}
#[inline]
pub fn sample(&self, t: f32) -> Point2DF32 {
self.from() + self.vector().scale(t)
}
#[inline]
pub fn offset(&self, amount: f32) -> LineSegmentF32 {
*self + self.vector().yx().normalize().scale_xy(Point2DF32::new(-amount, amount))
}
#[inline]
pub fn is_zero_length(&self) -> bool {
self.vector().is_zero()
}
}
impl Add<Point2DF32> for LineSegmentF32 {
type Output = LineSegmentF32;
#[inline]
fn add(self, point: Point2DF32) -> LineSegmentF32 {
LineSegmentF32(self.0 + point.0.xyxy())
}
}
impl Sub<Point2DF32> for LineSegmentF32 {

View File

@ -112,10 +112,11 @@ impl TEdge for Edge {
fn intersect_line_segment(&self, segment: &LineSegmentF32) -> ArrayVec<[f32; 3]> {
let mut results = ArrayVec::new();
let t = segment.intersection_t(&self.0);
if let Some(t) = segment.intersection_t(&self.0) {
if t >= 0.0 && t <= 1.0 {
results.push(t);
}
}
results
}
}

View File

@ -25,7 +25,7 @@ use std::mem;
#[derive(Clone)]
pub struct Outline {
pub contours: Vec<Contour>,
bounds: RectF32,
pub(crate) bounds: RectF32,
}
#[derive(Clone)]
@ -304,6 +304,13 @@ impl Contour {
segment
}
#[inline]
pub fn hull_segment_after(&self, prev_point_index: u32) -> LineSegmentF32 {
let next_point_index = self.next_point_index_of(prev_point_index);
LineSegmentF32::new(&self.points[prev_point_index as usize],
&self.points[next_point_index as usize])
}
#[inline]
pub fn point_is_endpoint(&self, point_index: u32) -> bool {
!self.flags[point_index as usize]
@ -510,7 +517,7 @@ impl Contour {
true
}
fn update_bounds(&self, bounds: &mut Option<RectF32>) {
pub(crate) fn update_bounds(&self, bounds: &mut Option<RectF32>) {
*bounds = Some(match *bounds {
None => self.bounds,
Some(bounds) => bounds.union_rect(self.bounds),

View File

@ -10,9 +10,13 @@
//! Utilities for converting path strokes to fills.
use crate::basic::line_segment::LineSegmentF32;
use crate::basic::rect::RectF32;
use crate::outline::{Contour, Outline};
use crate::segments::{Segment, SegmentIter};
use lyon_path::PathEvent;
use lyon_path::iterator::PathIterator;
use std::mem;
#[derive(Clone, Copy, Debug)]
pub struct StrokeStyle {
@ -144,3 +148,112 @@ enum StrokeToFillState {
Forward,
Backward,
}
// Pathfinder 3
pub struct OutlineStrokeToFill {
pub outline: Outline,
pub radius: f32,
}
impl OutlineStrokeToFill {
#[inline]
pub fn new(outline: Outline, radius: f32) -> OutlineStrokeToFill {
OutlineStrokeToFill { outline, radius }
}
#[inline]
pub fn offset(&mut self) {
let mut new_bounds = None;
for contour in &mut self.outline.contours {
let input = mem::replace(contour, Contour::new());
let mut contour_stroke_to_fill =
ContourStrokeToFill::new(input, Contour::new(), self.radius);
contour_stroke_to_fill.offset_forward();
contour_stroke_to_fill.offset_backward();
*contour = contour_stroke_to_fill.output;
contour.update_bounds(&mut new_bounds);
}
self.outline.bounds = new_bounds.unwrap_or_else(|| RectF32::default());
}
}
struct ContourStrokeToFill {
input: Contour,
output: Contour,
radius: f32,
}
impl ContourStrokeToFill {
#[inline]
fn new(input: Contour, output: Contour, radius: f32) -> ContourStrokeToFill {
ContourStrokeToFill { input, output, radius }
}
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);
}
}
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);
}
}
}

View File

@ -18,7 +18,7 @@ use pathfinder_geometry::basic::transform2d::{Transform2DF32, Transform2DF32Path
use pathfinder_geometry::outline::Outline;
use pathfinder_geometry::segment::{PathEventsToSegments, Segment};
use pathfinder_geometry::segment::{SegmentFlags, SegmentsToPathEvents};
use pathfinder_geometry::stroke::{StrokeStyle, StrokeToFillIter};
use pathfinder_geometry::stroke::OutlineStrokeToFill;
use pathfinder_renderer::paint::{ColorU, Paint};
use pathfinder_renderer::scene::{PathObject, PathObjectKind, Scene};
use std::mem;
@ -91,13 +91,17 @@ fn process_node(scene: &mut Scene, node: &Node, transform: &Transform2DF32) {
f32::max(stroke.width.value() as f32, HAIRLINE_STROKE_WIDTH);
let path = UsvgPathToSegments::new(path.segments.iter().cloned());
let path = SegmentsToPathEvents::new(path);
/*let path = SegmentsToPathEvents::new(path);
let path = PathIter::new(path);
let path = StrokeToFillIter::new(path, StrokeStyle::new(stroke_width));
let path = PathEventsToSegments::new(path);
let path = PathEventsToSegments::new(path);*/
let path = Transform2DF32PathIter::new(path, &transform);
let outline = Outline::from_segments(path);
let mut stroke_to_fill = OutlineStrokeToFill::new(outline, stroke_width);
stroke_to_fill.offset();
let outline = stroke_to_fill.outline;
scene.bounds = scene.bounds.union_rect(outline.bounds());
scene.objects.push(PathObject::new(
outline,