Use a much better technique for curve flattening
This commit is contained in:
parent
a9c1760de5
commit
d1ca5fe757
|
@ -534,6 +534,7 @@ dependencies = [
|
||||||
"pathfinder_geometry 0.3.0",
|
"pathfinder_geometry 0.3.0",
|
||||||
"quickcheck 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"quickcheck 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -175,38 +175,27 @@ bitflags! {
|
||||||
pub struct CubicSegment<'s>(&'s Segment);
|
pub struct CubicSegment<'s>(&'s Segment);
|
||||||
|
|
||||||
impl<'s> CubicSegment<'s> {
|
impl<'s> CubicSegment<'s> {
|
||||||
|
// See Kaspar Fischer, "Piecewise Linear Approximation of Bézier Curves", 2000.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_flat(self, tolerance: f32) -> bool {
|
||||||
|
let mut uv = F32x4::splat(3.0) * self.0.ctrl.0 -
|
||||||
|
self.0.baseline.0 - self.0.baseline.0 -
|
||||||
|
self.0.baseline.reversed().0;
|
||||||
|
uv = uv * uv;
|
||||||
|
uv = uv.max(uv.zwxy());
|
||||||
|
uv[0] + uv[1] <= 16.0 * tolerance * tolerance
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn flatten_once(self, tolerance: f32) -> Option<Segment> {
|
pub fn flatten_once(self, tolerance: f32) -> Option<Segment> {
|
||||||
let (baseline, ctrl) = (self.0.baseline.0, self.0.ctrl.0);
|
if self.is_flat(tolerance) {
|
||||||
let from_from = baseline.xyxy();
|
None
|
||||||
let v0102 = ctrl - from_from;
|
} else {
|
||||||
|
Some(self.split_after(0.5))
|
||||||
// v01.x v01.y v02.x v02.y
|
|
||||||
// * v01.x v01.y v01.y v01.x
|
|
||||||
// -------------------------
|
|
||||||
// v01.x^2 v01.y^2 ad bc
|
|
||||||
// | | | |
|
|
||||||
// +-------+ +-----+
|
|
||||||
// + -
|
|
||||||
// v01 len^2 determinant
|
|
||||||
let products = v0102 * v0102.xyyx();
|
|
||||||
|
|
||||||
let det = products[2] - products[3];
|
|
||||||
if det == 0.0 {
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let s2inv = (products[0] + products[1]).sqrt() / det;
|
|
||||||
|
|
||||||
let t = 2.0 * ((tolerance / 3.0) * s2inv.abs()).sqrt();
|
|
||||||
if t >= 1.0 - EPSILON || t == 0.0 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Some(self.split_after(t));
|
|
||||||
|
|
||||||
const EPSILON: f32 = 0.005;
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn split(self, t: f32) -> (Segment, Segment) {
|
pub fn split(self, t: f32) -> (Segment, Segment) {
|
||||||
|
|
|
@ -10,6 +10,7 @@ euclid = "0.19"
|
||||||
fixedbitset = "0.1"
|
fixedbitset = "0.1"
|
||||||
hashbrown = "0.1"
|
hashbrown = "0.1"
|
||||||
rayon = "1.0"
|
rayon = "1.0"
|
||||||
|
smallvec = "0.6"
|
||||||
|
|
||||||
[dependencies.pathfinder_geometry]
|
[dependencies.pathfinder_geometry]
|
||||||
path = "../geometry"
|
path = "../geometry"
|
||||||
|
|
|
@ -18,6 +18,7 @@ use pathfinder_geometry::outline::{Contour, Outline, PointIndex};
|
||||||
use pathfinder_geometry::point::Point2DF32;
|
use pathfinder_geometry::point::Point2DF32;
|
||||||
use pathfinder_geometry::segment::Segment;
|
use pathfinder_geometry::segment::Segment;
|
||||||
use pathfinder_geometry::util;
|
use pathfinder_geometry::util;
|
||||||
|
use smallvec::SmallVec;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
@ -405,40 +406,46 @@ impl ActiveEdge {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut oriented_segment = segment.orient(winding);
|
||||||
loop {
|
loop {
|
||||||
let rest_segment = match segment
|
let mut split_t = 1.0;
|
||||||
.orient(winding)
|
let mut before_segment = oriented_segment;
|
||||||
.as_cubic_segment()
|
let mut after_segment = None;
|
||||||
.flatten_once(FLATTENING_TOLERANCE)
|
|
||||||
{
|
|
||||||
None => {
|
|
||||||
let line_segment = segment.baseline;
|
|
||||||
self.segment =
|
|
||||||
match self.process_line_segment(&line_segment, built_object, tile_y) {
|
|
||||||
Some(ref lower_part) => Segment::line(lower_part),
|
|
||||||
None => Segment::none(),
|
|
||||||
};
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Some(rest_segment) => rest_segment.orient(winding),
|
|
||||||
};
|
|
||||||
|
|
||||||
debug_assert!(segment.baseline.min_y() <= tile_bottom);
|
while !before_segment.as_cubic_segment().is_flat(FLATTENING_TOLERANCE) {
|
||||||
|
let next_t = 0.5 * split_t;
|
||||||
let line_segment = LineSegmentF32::new(
|
let (before, after) = oriented_segment.as_cubic_segment().split(next_t);
|
||||||
&segment.baseline.upper_point(),
|
before_segment = before;
|
||||||
&rest_segment.baseline.upper_point(),
|
after_segment = Some(after);
|
||||||
)
|
split_t = next_t;
|
||||||
.orient(winding);
|
|
||||||
if self
|
|
||||||
.process_line_segment(&line_segment, built_object, tile_y)
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
self.segment = rest_segment;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
segment = rest_segment;
|
/*
|
||||||
|
println!("... tile_y={} winding={} segment={:?} t={} before_segment={:?} after_segment={:?}",
|
||||||
|
tile_y,
|
||||||
|
winding,
|
||||||
|
segment,
|
||||||
|
split_t,
|
||||||
|
before_segment,
|
||||||
|
after_segment);
|
||||||
|
*/
|
||||||
|
|
||||||
|
let line = before_segment.baseline.orient(winding);
|
||||||
|
match self.process_line_segment(&line, built_object, tile_y) {
|
||||||
|
Some(ref lower_part) if split_t == 1.0 => {
|
||||||
|
self.segment = Segment::line(&lower_part);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
None if split_t == 1.0 => {
|
||||||
|
self.segment = Segment::none();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Some(_) => {
|
||||||
|
self.segment = after_segment.unwrap().orient(winding);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
None => oriented_segment = after_segment.unwrap(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue