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",
|
||||
"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)",
|
||||
"smallvec 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -175,38 +175,27 @@ bitflags! {
|
|||
pub struct CubicSegment<'s>(&'s Segment);
|
||||
|
||||
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]
|
||||
pub fn flatten_once(self, tolerance: f32) -> Option<Segment> {
|
||||
let (baseline, ctrl) = (self.0.baseline.0, self.0.ctrl.0);
|
||||
let from_from = baseline.xyxy();
|
||||
let v0102 = ctrl - from_from;
|
||||
|
||||
// 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;
|
||||
if self.is_flat(tolerance) {
|
||||
None
|
||||
} else {
|
||||
Some(self.split_after(0.5))
|
||||
}
|
||||
|
||||
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]
|
||||
pub fn split(self, t: f32) -> (Segment, Segment) {
|
||||
|
|
|
@ -10,6 +10,7 @@ euclid = "0.19"
|
|||
fixedbitset = "0.1"
|
||||
hashbrown = "0.1"
|
||||
rayon = "1.0"
|
||||
smallvec = "0.6"
|
||||
|
||||
[dependencies.pathfinder_geometry]
|
||||
path = "../geometry"
|
||||
|
|
|
@ -18,6 +18,7 @@ use pathfinder_geometry::outline::{Contour, Outline, PointIndex};
|
|||
use pathfinder_geometry::point::Point2DF32;
|
||||
use pathfinder_geometry::segment::Segment;
|
||||
use pathfinder_geometry::util;
|
||||
use smallvec::SmallVec;
|
||||
use std::cmp::Ordering;
|
||||
use std::mem;
|
||||
|
||||
|
@ -405,40 +406,46 @@ impl ActiveEdge {
|
|||
}
|
||||
}
|
||||
|
||||
let mut oriented_segment = segment.orient(winding);
|
||||
loop {
|
||||
let rest_segment = match segment
|
||||
.orient(winding)
|
||||
.as_cubic_segment()
|
||||
.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),
|
||||
};
|
||||
let mut split_t = 1.0;
|
||||
let mut before_segment = oriented_segment;
|
||||
let mut after_segment = None;
|
||||
|
||||
debug_assert!(segment.baseline.min_y() <= tile_bottom);
|
||||
|
||||
let line_segment = LineSegmentF32::new(
|
||||
&segment.baseline.upper_point(),
|
||||
&rest_segment.baseline.upper_point(),
|
||||
)
|
||||
.orient(winding);
|
||||
if self
|
||||
.process_line_segment(&line_segment, built_object, tile_y)
|
||||
.is_some()
|
||||
{
|
||||
self.segment = rest_segment;
|
||||
return;
|
||||
while !before_segment.as_cubic_segment().is_flat(FLATTENING_TOLERANCE) {
|
||||
let next_t = 0.5 * split_t;
|
||||
let (before, after) = oriented_segment.as_cubic_segment().split(next_t);
|
||||
before_segment = before;
|
||||
after_segment = Some(after);
|
||||
split_t = next_t;
|
||||
}
|
||||
|
||||
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