Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
437
third-party/vendor/tiny-skia/src/path64/cubic64.rs
vendored
Normal file
437
third-party/vendor/tiny-skia/src/path64/cubic64.rs
vendored
Normal file
|
|
@ -0,0 +1,437 @@
|
|||
// Copyright 2012 Google Inc.
|
||||
// Copyright 2020 Yevhenii Reizner
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
use super::point64::{Point64, SearchAxis};
|
||||
use super::quad64;
|
||||
use super::Scalar64;
|
||||
|
||||
#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
|
||||
use tiny_skia_path::NoStdFloat;
|
||||
|
||||
pub const POINT_COUNT: usize = 4;
|
||||
const PI: f64 = 3.141592653589793;
|
||||
|
||||
pub struct Cubic64Pair {
|
||||
pub points: [Point64; 7],
|
||||
}
|
||||
|
||||
pub struct Cubic64 {
|
||||
pub points: [Point64; POINT_COUNT],
|
||||
}
|
||||
|
||||
impl Cubic64 {
|
||||
pub fn new(points: [Point64; POINT_COUNT]) -> Self {
|
||||
Cubic64 { points }
|
||||
}
|
||||
|
||||
pub fn as_f64_slice(&self) -> [f64; POINT_COUNT * 2] {
|
||||
[
|
||||
self.points[0].x,
|
||||
self.points[0].y,
|
||||
self.points[1].x,
|
||||
self.points[1].y,
|
||||
self.points[2].x,
|
||||
self.points[2].y,
|
||||
self.points[3].x,
|
||||
self.points[3].y,
|
||||
]
|
||||
}
|
||||
|
||||
pub fn point_at_t(&self, t: f64) -> Point64 {
|
||||
if t == 0.0 {
|
||||
return self.points[0];
|
||||
}
|
||||
|
||||
if t == 1.0 {
|
||||
return self.points[3];
|
||||
}
|
||||
|
||||
let one_t = 1.0 - t;
|
||||
let one_t2 = one_t * one_t;
|
||||
let a = one_t2 * one_t;
|
||||
let b = 3.0 * one_t2 * t;
|
||||
let t2 = t * t;
|
||||
let c = 3.0 * one_t * t2;
|
||||
let d = t2 * t;
|
||||
Point64::from_xy(
|
||||
a * self.points[0].x
|
||||
+ b * self.points[1].x
|
||||
+ c * self.points[2].x
|
||||
+ d * self.points[3].x,
|
||||
a * self.points[0].y
|
||||
+ b * self.points[1].y
|
||||
+ c * self.points[2].y
|
||||
+ d * self.points[3].y,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn search_roots(
|
||||
&self,
|
||||
mut extrema: usize,
|
||||
axis_intercept: f64,
|
||||
x_axis: SearchAxis,
|
||||
extreme_ts: &mut [f64; 6],
|
||||
valid_roots: &mut [f64],
|
||||
) -> usize {
|
||||
extrema += self.find_inflections(&mut extreme_ts[extrema..]);
|
||||
extreme_ts[extrema] = 0.0;
|
||||
extrema += 1;
|
||||
extreme_ts[extrema] = 1.0;
|
||||
debug_assert!(extrema < 6);
|
||||
extreme_ts[0..extrema].sort_by(cmp_f64);
|
||||
let mut valid_count = 0;
|
||||
let mut index = 0;
|
||||
while index < extrema {
|
||||
let min = extreme_ts[index];
|
||||
index += 1;
|
||||
let max = extreme_ts[index];
|
||||
if min == max {
|
||||
continue;
|
||||
}
|
||||
|
||||
let new_t = self.binary_search(min, max, axis_intercept, x_axis);
|
||||
if new_t >= 0.0 {
|
||||
if valid_count >= 3 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
valid_roots[valid_count] = new_t;
|
||||
valid_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
valid_count
|
||||
}
|
||||
|
||||
fn find_inflections(&self, t_values: &mut [f64]) -> usize {
|
||||
let ax = self.points[1].x - self.points[0].x;
|
||||
let ay = self.points[1].y - self.points[0].y;
|
||||
let bx = self.points[2].x - 2.0 * self.points[1].x + self.points[0].x;
|
||||
let by = self.points[2].y - 2.0 * self.points[1].y + self.points[0].y;
|
||||
let cx = self.points[3].x + 3.0 * (self.points[1].x - self.points[2].x) - self.points[0].x;
|
||||
let cy = self.points[3].y + 3.0 * (self.points[1].y - self.points[2].y) - self.points[0].y;
|
||||
quad64::roots_valid_t(
|
||||
bx * cy - by * cx,
|
||||
ax * cy - ay * cx,
|
||||
ax * by - ay * bx,
|
||||
t_values,
|
||||
)
|
||||
}
|
||||
|
||||
// give up when changing t no longer moves point
|
||||
// also, copy point rather than recompute it when it does change
|
||||
fn binary_search(&self, min: f64, max: f64, axis_intercept: f64, x_axis: SearchAxis) -> f64 {
|
||||
let mut t = (min + max) / 2.0;
|
||||
let mut step = (t - min) / 2.0;
|
||||
let mut cubic_at_t = self.point_at_t(t);
|
||||
let mut calc_pos = cubic_at_t.axis_coord(x_axis);
|
||||
let mut calc_dist = calc_pos - axis_intercept;
|
||||
loop {
|
||||
let prior_t = min.max(t - step);
|
||||
let less_pt = self.point_at_t(prior_t);
|
||||
if less_pt.x.approximately_equal_half(cubic_at_t.x)
|
||||
&& less_pt.y.approximately_equal_half(cubic_at_t.y)
|
||||
{
|
||||
return -1.0; // binary search found no point at this axis intercept
|
||||
}
|
||||
|
||||
let less_dist = less_pt.axis_coord(x_axis) - axis_intercept;
|
||||
let last_step = step;
|
||||
step /= 2.0;
|
||||
let ok = if calc_dist > 0.0 {
|
||||
calc_dist > less_dist
|
||||
} else {
|
||||
calc_dist < less_dist
|
||||
};
|
||||
if ok {
|
||||
t = prior_t;
|
||||
} else {
|
||||
let next_t = t + last_step;
|
||||
if next_t > max {
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
let more_pt = self.point_at_t(next_t);
|
||||
if more_pt.x.approximately_equal_half(cubic_at_t.x)
|
||||
&& more_pt.y.approximately_equal_half(cubic_at_t.y)
|
||||
{
|
||||
return -1.0; // binary search found no point at this axis intercept
|
||||
}
|
||||
|
||||
let more_dist = more_pt.axis_coord(x_axis) - axis_intercept;
|
||||
let ok = if calc_dist > 0.0 {
|
||||
calc_dist <= more_dist
|
||||
} else {
|
||||
calc_dist >= more_dist
|
||||
};
|
||||
if ok {
|
||||
continue;
|
||||
}
|
||||
|
||||
t = next_t;
|
||||
}
|
||||
|
||||
let test_at_t = self.point_at_t(t);
|
||||
cubic_at_t = test_at_t;
|
||||
calc_pos = cubic_at_t.axis_coord(x_axis);
|
||||
calc_dist = calc_pos - axis_intercept;
|
||||
|
||||
if calc_pos.approximately_equal(axis_intercept) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
t
|
||||
}
|
||||
|
||||
pub fn chop_at(&self, t: f64) -> Cubic64Pair {
|
||||
let mut dst = [Point64::zero(); 7];
|
||||
if t == 0.5 {
|
||||
dst[0] = self.points[0];
|
||||
dst[1].x = (self.points[0].x + self.points[1].x) / 2.0;
|
||||
dst[1].y = (self.points[0].y + self.points[1].y) / 2.0;
|
||||
dst[2].x = (self.points[0].x + 2.0 * self.points[1].x + self.points[2].x) / 4.0;
|
||||
dst[2].y = (self.points[0].y + 2.0 * self.points[1].y + self.points[2].y) / 4.0;
|
||||
dst[3].x =
|
||||
(self.points[0].x + 3.0 * (self.points[1].x + self.points[2].x) + self.points[3].x)
|
||||
/ 8.0;
|
||||
dst[3].y =
|
||||
(self.points[0].y + 3.0 * (self.points[1].y + self.points[2].y) + self.points[3].y)
|
||||
/ 8.0;
|
||||
dst[4].x = (self.points[1].x + 2.0 * self.points[2].x + self.points[3].x) / 4.0;
|
||||
dst[4].y = (self.points[1].y + 2.0 * self.points[2].y + self.points[3].y) / 4.0;
|
||||
dst[5].x = (self.points[2].x + self.points[3].x) / 2.0;
|
||||
dst[5].y = (self.points[2].y + self.points[3].y) / 2.0;
|
||||
dst[6] = self.points[3];
|
||||
|
||||
Cubic64Pair { points: dst }
|
||||
} else {
|
||||
interp_cubic_coords_x(&self.points, t, &mut dst);
|
||||
interp_cubic_coords_y(&self.points, t, &mut dst);
|
||||
Cubic64Pair { points: dst }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn coefficients(src: &[f64]) -> (f64, f64, f64, f64) {
|
||||
let mut a = src[6]; // d
|
||||
let mut b = src[4] * 3.0; // 3*c
|
||||
let mut c = src[2] * 3.0; // 3*b
|
||||
let d = src[0]; // a
|
||||
a -= d - c + b; // A = -a + 3*b - 3*c + d
|
||||
b += 3.0 * d - 2.0 * c; // B = 3*a - 6*b + 3*c
|
||||
c -= 3.0 * d; // C = -3*a + 3*b
|
||||
|
||||
(a, b, c, d)
|
||||
}
|
||||
|
||||
// from SkGeometry.cpp (and Numeric Solutions, 5.6)
|
||||
pub fn roots_valid_t(a: f64, b: f64, c: f64, d: f64, t: &mut [f64; 3]) -> usize {
|
||||
let mut s = [0.0; 3];
|
||||
let real_roots = roots_real(a, b, c, d, &mut s);
|
||||
let mut found_roots = quad64::push_valid_ts(&s, real_roots, t);
|
||||
'outer: for index in 0..real_roots {
|
||||
let t_value = s[index];
|
||||
if !t_value.approximately_one_or_less() && t_value.between(1.0, 1.00005) {
|
||||
for idx2 in 0..found_roots {
|
||||
if t[idx2].approximately_equal(1.0) {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
|
||||
debug_assert!(found_roots < 3);
|
||||
t[found_roots] = 1.0;
|
||||
found_roots += 1;
|
||||
} else if !t_value.approximately_zero_or_more() && t_value.between(-0.00005, 0.0) {
|
||||
for idx2 in 0..found_roots {
|
||||
if t[idx2].approximately_equal(0.0) {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
|
||||
debug_assert!(found_roots < 3);
|
||||
t[found_roots] = 0.0;
|
||||
found_roots += 1;
|
||||
}
|
||||
}
|
||||
|
||||
found_roots
|
||||
}
|
||||
|
||||
fn roots_real(a: f64, b: f64, c: f64, d: f64, s: &mut [f64; 3]) -> usize {
|
||||
if a.approximately_zero()
|
||||
&& a.approximately_zero_when_compared_to(b)
|
||||
&& a.approximately_zero_when_compared_to(c)
|
||||
&& a.approximately_zero_when_compared_to(d)
|
||||
{
|
||||
// we're just a quadratic
|
||||
return quad64::roots_real(b, c, d, s);
|
||||
}
|
||||
|
||||
if d.approximately_zero_when_compared_to(a)
|
||||
&& d.approximately_zero_when_compared_to(b)
|
||||
&& d.approximately_zero_when_compared_to(c)
|
||||
{
|
||||
// 0 is one root
|
||||
let mut num = quad64::roots_real(a, b, c, s);
|
||||
for i in 0..num {
|
||||
if s[i].approximately_zero() {
|
||||
return num;
|
||||
}
|
||||
}
|
||||
|
||||
s[num] = 0.0;
|
||||
num += 1;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
if (a + b + c + d).approximately_zero() {
|
||||
// 1 is one root
|
||||
let mut num = quad64::roots_real(a, a + b, -d, s);
|
||||
for i in 0..num {
|
||||
if s[i].almost_dequal_ulps(1.0) {
|
||||
return num;
|
||||
}
|
||||
}
|
||||
s[num] = 1.0;
|
||||
num += 1;
|
||||
return num;
|
||||
}
|
||||
|
||||
let (a, b, c) = {
|
||||
let inv_a = 1.0 / a;
|
||||
let a = b * inv_a;
|
||||
let b = c * inv_a;
|
||||
let c = d * inv_a;
|
||||
(a, b, c)
|
||||
};
|
||||
|
||||
let a2 = a * a;
|
||||
let q = (a2 - b * 3.0) / 9.0;
|
||||
let r = (2.0 * a2 * a - 9.0 * a * b + 27.0 * c) / 54.0;
|
||||
let r2 = r * r;
|
||||
let q3 = q * q * q;
|
||||
let r2_minus_q3 = r2 - q3;
|
||||
let adiv3 = a / 3.0;
|
||||
let mut offset = 0;
|
||||
if r2_minus_q3 < 0.0 {
|
||||
// we have 3 real roots
|
||||
|
||||
// the divide/root can, due to finite precisions, be slightly outside of -1...1
|
||||
let theta = (r / q3.sqrt()).bound(-1.0, 1.0).acos();
|
||||
let neg2_root_q = -2.0 * q.sqrt();
|
||||
|
||||
let mut rr = neg2_root_q * (theta / 3.0).cos() - adiv3;
|
||||
s[offset] = rr;
|
||||
offset += 1;
|
||||
|
||||
rr = neg2_root_q * ((theta + 2.0 * PI) / 3.0).cos() - adiv3;
|
||||
if !s[0].almost_dequal_ulps(rr) {
|
||||
s[offset] = rr;
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
rr = neg2_root_q * ((theta - 2.0 * PI) / 3.0).cos() - adiv3;
|
||||
if !s[0].almost_dequal_ulps(rr) && (offset == 1 || !s[1].almost_dequal_ulps(rr)) {
|
||||
s[offset] = rr;
|
||||
offset += 1;
|
||||
}
|
||||
} else {
|
||||
// we have 1 real root
|
||||
let sqrt_r2_minus_q3 = r2_minus_q3.sqrt();
|
||||
let mut a = r.abs() + sqrt_r2_minus_q3;
|
||||
a = super::cube_root(a);
|
||||
if r > 0.0 {
|
||||
a = -a;
|
||||
}
|
||||
|
||||
if a != 0.0 {
|
||||
a += q / a;
|
||||
}
|
||||
|
||||
let mut r2 = a - adiv3;
|
||||
s[offset] = r2;
|
||||
offset += 1;
|
||||
if r2.almost_dequal_ulps(q3) {
|
||||
r2 = -a / 2.0 - adiv3;
|
||||
if !s[0].almost_dequal_ulps(r2) {
|
||||
s[offset] = r2;
|
||||
offset += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
offset
|
||||
}
|
||||
|
||||
// Cubic64'(t) = At^2 + Bt + C, where
|
||||
// A = 3(-a + 3(b - c) + d)
|
||||
// B = 6(a - 2b + c)
|
||||
// C = 3(b - a)
|
||||
// Solve for t, keeping only those that fit between 0 < t < 1
|
||||
pub fn find_extrema(src: &[f64], t_values: &mut [f64]) -> usize {
|
||||
// we divide A,B,C by 3 to simplify
|
||||
let a = src[0];
|
||||
let b = src[2];
|
||||
let c = src[4];
|
||||
let d = src[6];
|
||||
let a2 = d - a + 3.0 * (b - c);
|
||||
let b2 = 2.0 * (a - b - b + c);
|
||||
let c2 = b - a;
|
||||
|
||||
quad64::roots_valid_t(a2, b2, c2, t_values)
|
||||
}
|
||||
|
||||
// Skia doesn't seems to care about NaN/inf during sorting, so we don't too.
|
||||
fn cmp_f64(a: &f64, b: &f64) -> core::cmp::Ordering {
|
||||
if a < b {
|
||||
core::cmp::Ordering::Less
|
||||
} else if a > b {
|
||||
core::cmp::Ordering::Greater
|
||||
} else {
|
||||
core::cmp::Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
// classic one t subdivision
|
||||
fn interp_cubic_coords_x(src: &[Point64; 4], t: f64, dst: &mut [Point64; 7]) {
|
||||
use super::interp;
|
||||
|
||||
let ab = interp(src[0].x, src[1].x, t);
|
||||
let bc = interp(src[1].x, src[2].x, t);
|
||||
let cd = interp(src[2].x, src[3].x, t);
|
||||
let abc = interp(ab, bc, t);
|
||||
let bcd = interp(bc, cd, t);
|
||||
let abcd = interp(abc, bcd, t);
|
||||
|
||||
dst[0].x = src[0].x;
|
||||
dst[1].x = ab;
|
||||
dst[2].x = abc;
|
||||
dst[3].x = abcd;
|
||||
dst[4].x = bcd;
|
||||
dst[5].x = cd;
|
||||
dst[6].x = src[3].x;
|
||||
}
|
||||
|
||||
fn interp_cubic_coords_y(src: &[Point64; 4], t: f64, dst: &mut [Point64; 7]) {
|
||||
use super::interp;
|
||||
|
||||
let ab = interp(src[0].y, src[1].y, t);
|
||||
let bc = interp(src[1].y, src[2].y, t);
|
||||
let cd = interp(src[2].y, src[3].y, t);
|
||||
let abc = interp(ab, bc, t);
|
||||
let bcd = interp(bc, cd, t);
|
||||
let abcd = interp(abc, bcd, t);
|
||||
|
||||
dst[0].y = src[0].y;
|
||||
dst[1].y = ab;
|
||||
dst[2].y = abc;
|
||||
dst[3].y = abcd;
|
||||
dst[4].y = bcd;
|
||||
dst[5].y = cd;
|
||||
dst[6].y = src[3].y;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue