WIP before moving to a segment basis
This commit is contained in:
parent
c7a1d9e960
commit
d75ed71af4
|
@ -13,7 +13,7 @@
|
||||||
use crate::basic::point::Point2DF32;
|
use crate::basic::point::Point2DF32;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
use std::ops::Sub;
|
use std::ops::{Add, Sub};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Default)]
|
#[derive(Clone, Copy, Debug, PartialEq, Default)]
|
||||||
pub struct LineSegmentF32(pub F32x4);
|
pub struct LineSegmentF32(pub F32x4);
|
||||||
|
@ -224,20 +224,42 @@ impl LineSegmentF32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// http://www.cs.swan.ac.uk/~cssimon/line_intersection.html
|
// 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 d0d1 = self.vector().0.concat_xy_xy(other.vector().0);
|
||||||
let offset = other.from() - self.from();
|
let offset = other.from() - self.from();
|
||||||
let factors = d0d1.concat_wz_yx(offset.0);
|
let factors = d0d1.concat_wz_yx(offset.0);
|
||||||
let terms = d0d1 * factors;
|
let terms = d0d1 * factors;
|
||||||
let t = (terms[3] - terms[2]) / (terms[0] - terms[1]);
|
let denom = terms[0] - terms[1];
|
||||||
//println!("intersection_t({:?}, {:?})={} (d0d1={:?}, factors={:?})", self, other, t, d0d1, factors);
|
if f32::abs(denom) < EPSILON {
|
||||||
t
|
return None;
|
||||||
|
}
|
||||||
|
return Some((terms[3] - terms[2]) / denom);
|
||||||
|
|
||||||
|
const EPSILON: f32 = 0.0001;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn sample(&self, t: f32) -> Point2DF32 {
|
pub fn sample(&self, t: f32) -> Point2DF32 {
|
||||||
self.from() + self.vector().scale(t)
|
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 {
|
impl Sub<Point2DF32> for LineSegmentF32 {
|
||||||
|
|
|
@ -112,9 +112,10 @@ impl TEdge for Edge {
|
||||||
|
|
||||||
fn intersect_line_segment(&self, segment: &LineSegmentF32) -> ArrayVec<[f32; 3]> {
|
fn intersect_line_segment(&self, segment: &LineSegmentF32) -> ArrayVec<[f32; 3]> {
|
||||||
let mut results = ArrayVec::new();
|
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 {
|
if t >= 0.0 && t <= 1.0 {
|
||||||
results.push(t);
|
results.push(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
results
|
results
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ use std::mem;
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Outline {
|
pub struct Outline {
|
||||||
pub contours: Vec<Contour>,
|
pub contours: Vec<Contour>,
|
||||||
bounds: RectF32,
|
pub(crate) bounds: RectF32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -304,10 +304,17 @@ impl Contour {
|
||||||
segment
|
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]
|
#[inline]
|
||||||
pub fn point_is_endpoint(&self, point_index: u32) -> bool {
|
pub fn point_is_endpoint(&self, point_index: u32) -> bool {
|
||||||
!self.flags[point_index as usize]
|
!self.flags[point_index as usize]
|
||||||
.intersects(PointFlags::CONTROL_POINT_0 | PointFlags::CONTROL_POINT_1)
|
.intersects(PointFlags::CONTROL_POINT_0 | PointFlags::CONTROL_POINT_1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -510,7 +517,7 @@ impl Contour {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_bounds(&self, bounds: &mut Option<RectF32>) {
|
pub(crate) fn update_bounds(&self, bounds: &mut Option<RectF32>) {
|
||||||
*bounds = Some(match *bounds {
|
*bounds = Some(match *bounds {
|
||||||
None => self.bounds,
|
None => self.bounds,
|
||||||
Some(bounds) => bounds.union_rect(self.bounds),
|
Some(bounds) => bounds.union_rect(self.bounds),
|
||||||
|
|
|
@ -10,9 +10,13 @@
|
||||||
|
|
||||||
//! Utilities for converting path strokes to fills.
|
//! 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 crate::segments::{Segment, SegmentIter};
|
||||||
use lyon_path::PathEvent;
|
use lyon_path::PathEvent;
|
||||||
use lyon_path::iterator::PathIterator;
|
use lyon_path::iterator::PathIterator;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct StrokeStyle {
|
pub struct StrokeStyle {
|
||||||
|
@ -144,3 +148,112 @@ enum StrokeToFillState {
|
||||||
Forward,
|
Forward,
|
||||||
Backward,
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ use pathfinder_geometry::basic::transform2d::{Transform2DF32, Transform2DF32Path
|
||||||
use pathfinder_geometry::outline::Outline;
|
use pathfinder_geometry::outline::Outline;
|
||||||
use pathfinder_geometry::segment::{PathEventsToSegments, Segment};
|
use pathfinder_geometry::segment::{PathEventsToSegments, Segment};
|
||||||
use pathfinder_geometry::segment::{SegmentFlags, SegmentsToPathEvents};
|
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::paint::{ColorU, Paint};
|
||||||
use pathfinder_renderer::scene::{PathObject, PathObjectKind, Scene};
|
use pathfinder_renderer::scene::{PathObject, PathObjectKind, Scene};
|
||||||
use std::mem;
|
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);
|
f32::max(stroke.width.value() as f32, HAIRLINE_STROKE_WIDTH);
|
||||||
|
|
||||||
let path = UsvgPathToSegments::new(path.segments.iter().cloned());
|
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 = PathIter::new(path);
|
||||||
let path = StrokeToFillIter::new(path, StrokeStyle::new(stroke_width));
|
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 path = Transform2DF32PathIter::new(path, &transform);
|
||||||
let outline = Outline::from_segments(path);
|
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.bounds = scene.bounds.union_rect(outline.bounds());
|
||||||
scene.objects.push(PathObject::new(
|
scene.objects.push(PathObject::new(
|
||||||
outline,
|
outline,
|
||||||
|
|
Loading…
Reference in New Issue