use core::fmt;
use std::{
cmp::Ordering,
hash::{Hash, Hasher},
sync::Arc,
};
#[cfg(feature = "rkyv")]
use rkyv::{Archive, Deserialize as rDeser, Serialize as rSer};
use super::PathItem;
#[derive(Default, Clone, Copy)]
#[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))]
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
pub struct Scalar(pub f32);
impl Scalar {
fn is_zero(&self) -> bool {
self.0 == 0.0
}
}
impl From<f32> for Scalar {
fn from(float: f32) -> Self {
Self(float)
}
}
impl From<Scalar> for f32 {
fn from(scalar: Scalar) -> Self {
scalar.0
}
}
impl fmt::Debug for Scalar {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl std::ops::Add for Scalar {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self(self.0 + rhs.0)
}
}
impl std::ops::Sub for Scalar {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self(self.0 - rhs.0)
}
}
impl std::ops::Mul for Scalar {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self(self.0 * rhs.0)
}
}
impl std::ops::Div for Scalar {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
Self(self.0 / rhs.0)
}
}
impl Eq for Scalar {}
impl PartialEq for Scalar {
fn eq(&self, other: &Self) -> bool {
assert!(!self.0.is_nan() && !other.0.is_nan(), "float is NaN");
self.0 == other.0
}
}
impl PartialEq<f32> for Scalar {
fn eq(&self, other: &f32) -> bool {
self == &Self(*other)
}
}
impl Ord for Scalar {
fn cmp(&self, other: &Self) -> Ordering {
self.0.partial_cmp(&other.0).expect("float is NaN")
}
}
impl PartialOrd for Scalar {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
fn lt(&self, other: &Self) -> bool {
self.0 < other.0
}
fn le(&self, other: &Self) -> bool {
self.0 <= other.0
}
fn gt(&self, other: &Self) -> bool {
self.0 > other.0
}
fn ge(&self, other: &Self) -> bool {
self.0 >= other.0
}
}
impl std::ops::Neg for Scalar {
type Output = Self;
fn neg(self) -> Self::Output {
Self(-self.0)
}
}
impl Hash for Scalar {
fn hash<H: Hasher>(&self, state: &mut H) {
debug_assert!(!self.0.is_nan(), "float is NaN");
self.0.to_le_bytes().hash(state);
}
}
pub type Abs = Scalar;
pub type Size = Axes<Abs>;
pub type Point = Axes<Scalar>;
pub type Ratio = Scalar;
pub type Angle = Scalar;
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))]
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
pub struct Axes<T> {
pub x: T,
pub y: T,
}
impl<T> Axes<T> {
pub fn new(x: T, y: T) -> Self {
Self { x, y }
}
}
impl<T> std::ops::Add for Axes<T>
where
T: std::ops::Add<Output = T>,
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
impl From<tiny_skia_path::Point> for Point {
fn from(typst_axes: tiny_skia_path::Point) -> Self {
Self {
x: typst_axes.x.into(),
y: typst_axes.y.into(),
}
}
}
impl From<Point> for tiny_skia_path::Point {
fn from(axes: Point) -> Self {
Self {
x: axes.x.into(),
y: axes.y.into(),
}
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))]
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
pub struct Transform {
pub sx: Ratio,
pub ky: Ratio,
pub kx: Ratio,
pub sy: Ratio,
pub tx: Abs,
pub ty: Abs,
}
impl Transform {
pub fn from_scale(sx: Ratio, sy: Ratio) -> Self {
Self {
sx,
ky: 0.0.into(),
kx: 0.0.into(),
sy,
tx: 0.0.into(),
ty: 0.0.into(),
}
}
pub fn from_translate(tx: Abs, ty: Abs) -> Self {
Self {
sx: 1.0.into(),
ky: 0.0.into(),
kx: 0.0.into(),
sy: 1.0.into(),
tx,
ty,
}
}
pub fn from_skew(kx: Ratio, ky: Ratio) -> Self {
Self {
sx: 1.0.into(),
ky,
kx,
sy: 1.0.into(),
tx: 0.0.into(),
ty: 0.0.into(),
}
}
pub fn identity() -> Self {
Self::from_scale(1.0.into(), 1.0.into())
}
#[inline]
pub fn pre_concat(self, other: Self) -> Self {
let ts: tiny_skia_path::Transform = self.into();
let other: tiny_skia_path::Transform = other.into();
let ts = ts.pre_concat(other);
ts.into()
}
#[inline]
pub fn post_concat(self, other: Self) -> Self {
other.pre_concat(self)
}
#[inline]
pub fn pre_translate(self, tx: f32, ty: f32) -> Self {
let ts: tiny_skia_path::Transform = self.into();
let ts = ts.pre_translate(tx, ty);
ts.into()
}
pub fn is_identity(self) -> bool {
self == Self::identity()
}
pub fn invert(self) -> Option<Self> {
if self.is_identity() {
return Some(self);
}
if self.kx.is_zero() && self.ky.is_zero() {
if self.sx.is_zero() || self.sy.is_zero() {
return Some(Self::from_translate(-self.tx, -self.ty));
}
let inv_x = 1.0 / self.sx.0;
let inv_y = 1.0 / self.sy.0;
return Some(Self {
sx: Scalar(inv_x),
ky: Scalar(0.),
kx: Scalar(0.),
sy: Scalar(inv_y),
tx: Scalar(-self.tx.0 * inv_x),
ty: Scalar(-self.ty.0 * inv_y),
});
}
let det = self.sx.0 * self.sy.0 - self.kx.0 * self.ky.0;
if det.abs() < 1e-12 {
return None;
}
let inv_det = 1.0 / det;
Some(Self {
sx: Scalar(self.sy.0 * inv_det),
ky: Scalar(-self.ky.0 * inv_det),
kx: Scalar(-self.kx.0 * inv_det),
sy: Scalar(self.sx.0 * inv_det),
tx: Scalar((self.kx.0 * self.ty.0 - self.sy.0 * self.tx.0) * inv_det),
ty: Scalar((self.ky.0 * self.tx.0 - self.sx.0 * self.ty.0) * inv_det),
})
}
}
impl From<tiny_skia_path::Transform> for Transform {
fn from(skia_transform: tiny_skia_path::Transform) -> Self {
Self {
sx: skia_transform.sx.into(),
ky: skia_transform.ky.into(),
kx: skia_transform.kx.into(),
sy: skia_transform.sy.into(),
tx: skia_transform.tx.into(),
ty: skia_transform.ty.into(),
}
}
}
impl From<Transform> for tiny_skia_path::Transform {
fn from(ir_transform: Transform) -> Self {
Self {
sx: ir_transform.sx.into(),
ky: ir_transform.ky.into(),
kx: ir_transform.kx.into(),
sy: ir_transform.sy.into(),
tx: ir_transform.tx.into(),
ty: ir_transform.ty.into(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Hash, Default)]
pub struct Rect {
pub lo: Point,
pub hi: Point,
}
impl Rect {
pub fn empty() -> Self {
Self {
lo: Point::new(Scalar(0.), Scalar(0.)),
hi: Point::new(Scalar(0.), Scalar(0.)),
}
}
pub fn cano(&self) -> Self {
let Rect { lo, hi } = self;
Self {
lo: Point::new(lo.x.min(hi.x), lo.y.min(hi.y)),
hi: Point::new(lo.x.max(hi.x), lo.y.max(hi.y)),
}
}
pub fn is_empty(&self) -> bool {
self.lo.x >= self.hi.x || self.lo.y >= self.hi.y
}
pub fn is_intersected(&self) -> bool {
self.lo.x <= self.hi.x || self.lo.y <= self.hi.y
}
pub fn intersect(&self, other: &Self) -> Self {
Self {
lo: self.lo.max(&other.lo),
hi: self.hi.min(&other.hi),
}
}
pub fn union(&self, other: &Self) -> Self {
if self.is_empty() {
return *other;
}
Self {
lo: self.lo.min(&other.lo),
hi: self.hi.max(&other.hi),
}
}
pub fn translate(&self, dp: Point) -> Self {
Self {
lo: self.lo + dp,
hi: self.hi + dp,
}
}
pub fn width(&self) -> Scalar {
self.hi.x - self.lo.x
}
pub fn height(&self) -> Scalar {
self.hi.y - self.lo.y
}
pub fn left(&self) -> Scalar {
self.lo.x
}
pub fn right(&self) -> Scalar {
self.hi.x
}
pub fn top(&self) -> Scalar {
self.lo.y
}
pub fn bottom(&self) -> Scalar {
self.hi.y
}
}
impl From<tiny_skia_path::Rect> for Rect {
fn from(rect: tiny_skia_path::Rect) -> Self {
Self {
lo: Point::new(Scalar(rect.left()), Scalar(rect.top())),
hi: Point::new(Scalar(rect.right()), Scalar(rect.bottom())),
}
}
}
impl TryFrom<Rect> for tiny_skia_path::Rect {
type Error = ();
fn try_from(rect: Rect) -> Result<Self, Self::Error> {
Self::from_ltrb(rect.lo.x.0, rect.lo.y.0, rect.hi.x.0, rect.hi.y.0).ok_or(())
}
}
pub trait EuclidMinMax {
fn min(&self, other: &Self) -> Self;
fn max(&self, other: &Self) -> Self;
}
impl EuclidMinMax for Scalar {
fn min(&self, other: &Self) -> Self {
Self(self.0.min(other.0))
}
fn max(&self, other: &Self) -> Self {
Self(self.0.max(other.0))
}
}
impl EuclidMinMax for Point {
fn min(&self, other: &Self) -> Self {
Self {
x: self.x.min(other.x),
y: self.y.min(other.y),
}
}
fn max(&self, other: &Self) -> Self {
Self {
x: self.x.max(other.x),
y: self.y.max(other.y),
}
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
#[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))]
#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
pub enum TransformItem {
Matrix(Arc<Transform>),
Translate(Arc<Axes<Abs>>),
Scale(Arc<(Ratio, Ratio)>),
Rotate(Arc<Scalar>),
Skew(Arc<(Ratio, Ratio)>),
Clip(Arc<PathItem>),
}
impl From<TransformItem> for Transform {
fn from(value: TransformItem) -> Self {
match value {
TransformItem::Matrix(m) => *m,
TransformItem::Scale(m) => Transform::from_scale(m.0, m.1),
TransformItem::Translate(m) => Transform::from_translate(m.x, m.y),
TransformItem::Rotate(_m) => todo!(),
TransformItem::Skew(m) => Transform::from_skew(m.0, m.1),
TransformItem::Clip(_m) => Transform::identity(),
}
}
}