blob: ec801ebed3d5969cce0415ca684929abe21e15c7 [file] [log] [blame] [edit]
// Copyright 2023 the Vello Authors
// SPDX-License-Identifier: Apache-2.0 OR MIT OR Unlicense
//! Utility types
use std::ops::Mul;
use vello_encoding::ConfigUniform;
#[derive(Clone, Copy, Default, Debug, PartialEq)]
#[repr(C)]
pub struct Vec2 {
pub x: f32,
pub y: f32,
}
impl std::ops::Add for Vec2 {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
impl std::ops::Sub for Vec2 {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
Self {
x: self.x - rhs.x,
y: self.y - rhs.y,
}
}
}
impl Mul<f32> for Vec2 {
type Output = Self;
fn mul(self, rhs: f32) -> Self {
Self {
x: self.x * rhs,
y: self.y * rhs,
}
}
}
impl std::ops::Div<f32> for Vec2 {
type Output = Self;
fn div(self, rhs: f32) -> Self {
Self {
x: self.x / rhs,
y: self.y / rhs,
}
}
}
impl Mul<Vec2> for f32 {
type Output = Vec2;
fn mul(self, rhs: Vec2) -> Self::Output {
rhs * self
}
}
impl std::ops::Neg for Vec2 {
type Output = Self;
fn neg(self) -> Self::Output {
Self::new(-self.x, -self.y)
}
}
impl Vec2 {
pub fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
pub fn dot(self, other: Self) -> f32 {
self.x * other.x + self.y * other.y
}
pub fn cross(self, other: Self) -> f32 {
(self.x * other.y) - (self.y * other.x)
}
pub fn length(self) -> f32 {
self.x.hypot(self.y)
}
pub fn length_squared(self) -> f32 {
self.dot(self)
}
pub fn distance(self, other: Self) -> f32 {
(self - other).length()
}
pub fn to_array(self) -> [f32; 2] {
[self.x, self.y]
}
pub fn from_array(a: [f32; 2]) -> Self {
Self { x: a[0], y: a[1] }
}
pub fn mix(self, other: Self, t: f32) -> Self {
let x = self.x + (other.x - self.x) * t;
let y = self.y + (other.y - self.y) * t;
Self { x, y }
}
pub fn normalize(self) -> Self {
self / self.length()
}
pub fn atan2(self) -> f32 {
self.y.atan2(self.x)
}
pub fn is_nan(self) -> bool {
self.x.is_nan() || self.y.is_nan()
}
pub fn min(self, other: Self) -> Self {
Self::new(self.x.min(other.x), self.y.min(other.y))
}
pub fn max(self, other: Self) -> Self {
Self::new(self.x.max(other.x), self.y.max(other.y))
}
}
#[derive(Clone)]
pub(crate) struct Transform(pub(crate) [f32; 6]);
impl Transform {
pub(crate) fn identity() -> Self {
Self([1., 0., 0., 1., 0., 0.])
}
pub(crate) fn apply(&self, p: Vec2) -> Vec2 {
let z = self.0;
let x = z[0] * p.x + z[2] * p.y + z[4];
let y = z[1] * p.x + z[3] * p.y + z[5];
Vec2 { x, y }
}
pub(crate) fn inverse(&self) -> Self {
let z = self.0;
let inv_det = (z[0] * z[3] - z[1] * z[2]).recip();
let inv_mat = [
z[3] * inv_det,
-z[1] * inv_det,
-z[2] * inv_det,
z[0] * inv_det,
];
Self([
inv_mat[0],
inv_mat[1],
inv_mat[2],
inv_mat[3],
-(inv_mat[0] * z[4] + inv_mat[2] * z[5]),
-(inv_mat[1] * z[4] + inv_mat[3] * z[5]),
])
}
pub(crate) fn read(transform_base: u32, ix: u32, data: &[u32]) -> Self {
let mut z = [0.0; 6];
let base = (transform_base + ix * 6) as usize;
for i in 0..6 {
z[i] = f32::from_bits(data[base + i]);
}
Self(z)
}
}
impl Mul for Transform {
type Output = Self;
#[inline]
fn mul(self, other: Self) -> Self {
Self([
self.0[0] * other.0[0] + self.0[2] * other.0[1],
self.0[1] * other.0[0] + self.0[3] * other.0[1],
self.0[0] * other.0[2] + self.0[2] * other.0[3],
self.0[1] * other.0[2] + self.0[3] * other.0[3],
self.0[0] * other.0[4] + self.0[2] * other.0[5] + self.0[4],
self.0[1] * other.0[4] + self.0[3] * other.0[5] + self.0[5],
])
}
}
pub(crate) fn span(a: f32, b: f32) -> u32 {
(a.max(b).ceil() - a.min(b).floor()).max(1.0) as u32
}
const DRAWTAG_NOP: u32 = 0;
/// Read draw tag, guarded by number of draw objects.
///
/// The `ix` argument is allowed to exceed the number of draw objects,
/// in which case a NOP is returned.
pub(crate) fn read_draw_tag_from_scene(config: &ConfigUniform, scene: &[u32], ix: u32) -> u32 {
if ix < config.layout.n_draw_objects {
let tag_ix = config.layout.draw_tag_base + ix;
scene[tag_ix as usize]
} else {
DRAWTAG_NOP
}
}
/// The largest floating point value strictly less than 1.
///
/// This value is used to limit the value of b so that its floor is strictly less
/// than 1. That guarantees that floor(a * i + b) == 0 for i == 0, which lands on
/// the correct first tile.
pub(crate) const ONE_MINUS_ULP: f32 = 0.99999994;
/// An epsilon to be applied in path numerical robustness.
///
/// When floor(a * (n - 1) + b) does not match the expected value (the width in
/// grid cells minus one), this delta is applied to a to push it in the correct
/// direction. The theory is that a is not off by more than a few ulp, and it's
/// always in the range of 0..1.
pub(crate) const ROBUST_EPSILON: f32 = 2e-7;