Files
pdfcraft/vendor/vello_common/src/math.rs
T
572870eca8 M2: clip intersection starts its row walk at the first shared row (#2026)
* M2: clip intersection starts its row walk at the first shared row

clip::intersect in vello_common, the step every clipped path passes
through, began its walk over strip rows at the earlier of the two paths'
first rows. Each path therefore visited every row above it, although rows
where one path has no strips produce no output. Every page is clipped (the
crop box is a clip), so this ran for every stroke of every render: on the
A1 engineering sample it was the hottest function in a full-page render.

Start at the later first row, as vello_common 0.1 does. The strips and the
pixels are unchanged; a test counts the rows the walk visits and fails
without the change.

vello_common 0.0.8 is vendored with that one-line PdfCraft patch (marked
in the source), its unit test, the vendor/README.md row and an
ATTRIBUTION.toml entry for its reference image. Cargo.lock records it as a
path crate.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>

* Refresh catalog hashes after merging main

---------

Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-15 02:38:54 -04:00

61 lines
2.2 KiB
Rust

// Copyright 2025 the Vello Authors
// SPDX-License-Identifier: Apache-2.0 OR MIT
//! Mathematical helper functions.
use core::ops::Sub;
#[cfg(not(feature = "std"))]
use peniko::kurbo::common::FloatFuncs as _;
// See https://raphlinus.github.io/audio/2018/09/05/sigmoid.html for a little
// explanation of this approximation to the erf function.
/// Approximate the erf function.
pub fn compute_erf7(x: f32) -> f32 {
// Clamp `x`, because for large `x` the terms here become `inf`, causing the result to be 0 or
// `NaN`. This clamping doesn't lose any information, because `erf(±10) ≈ 1` well within `f64`
// machine precision, let alone `f32`.
let x = x.clamp(-10., 10.);
let x = x * core::f32::consts::FRAC_2_SQRT_PI;
let xx = x * x;
let x = x + (0.24295 + (0.03395 + 0.0104 * xx) * xx) * (x * xx);
x / (1.0 + x * x).sqrt()
}
// From <https://github.com/linebender/tiny-skia/blob/68b198a7210a6bbf752b43d6bc4db62445730313/path/src/scalar.rs#L12>
// Note: If this value changes, also update NEARLY_ZERO_TOLERANCE in render_strips.wgsl
// @see {@link https://github.com/linebender/vello/blob/58b80d660e2fc5aef3bd32b24af3f95e973aab95/sparse_strips/vello_sparse_shaders/shaders/render_strips.wgsl#L63}
const SCALAR_NEARLY_ZERO: f32 = 1.0 / (1 << 12) as f32;
/// A number of useful methods for floating-point numbers.
pub trait FloatExt: Sized + Sub<Self, Output = Self> {
/// Whether the number is approximately 0.
fn is_nearly_zero(&self) -> bool;
/// Whether the number is approximately 0, with a given tolerance.
fn is_nearly_zero_within_tolerance(&self, tolerance: f32) -> bool;
}
impl FloatExt for f32 {
fn is_nearly_zero(&self) -> bool {
self.is_nearly_zero_within_tolerance(SCALAR_NEARLY_ZERO)
}
fn is_nearly_zero_within_tolerance(&self, tolerance: f32) -> bool {
debug_assert!(tolerance >= 0.0, "tolerance must be positive");
self.abs() <= tolerance
}
}
impl FloatExt for f64 {
fn is_nearly_zero(&self) -> bool {
self.is_nearly_zero_within_tolerance(SCALAR_NEARLY_ZERO)
}
fn is_nearly_zero_within_tolerance(&self, tolerance: f32) -> bool {
debug_assert!(tolerance >= 0.0, "tolerance must be positive");
self.abs() <= tolerance as Self
}
}