1use core::fmt;
2use std::{
3 cmp::Ordering,
4 hash::{Hash, Hasher},
5 sync::Arc,
6};
7
8#[cfg(feature = "rkyv")]
9use rkyv::{Archive, Deserialize as rDeser, Serialize as rSer};
10
11use super::PathItem;
12
13#[derive(Default, Clone, Copy)]
16#[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))]
17#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
18pub struct Scalar(pub f32);
19
20impl Scalar {
21 fn is_zero(&self) -> bool {
22 self.0 == 0.0
23 }
24}
25
26impl From<f32> for Scalar {
27 fn from(float: f32) -> Self {
28 Self(float)
29 }
30}
31
32impl From<Scalar> for f32 {
33 fn from(scalar: Scalar) -> Self {
34 scalar.0
35 }
36}
37
38impl fmt::Debug for Scalar {
39 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40 self.0.fmt(f)
41 }
42}
43
44impl std::ops::Add for Scalar {
45 type Output = Self;
46
47 fn add(self, rhs: Self) -> Self::Output {
48 Self(self.0 + rhs.0)
49 }
50}
51
52impl std::ops::Sub for Scalar {
53 type Output = Self;
54
55 fn sub(self, rhs: Self) -> Self::Output {
56 Self(self.0 - rhs.0)
57 }
58}
59
60impl std::ops::Mul for Scalar {
61 type Output = Self;
62
63 fn mul(self, rhs: Self) -> Self::Output {
64 Self(self.0 * rhs.0)
65 }
66}
67
68impl std::ops::Div for Scalar {
69 type Output = Self;
70
71 fn div(self, rhs: Self) -> Self::Output {
72 Self(self.0 / rhs.0)
73 }
74}
75
76impl Eq for Scalar {}
77
78impl PartialEq for Scalar {
79 fn eq(&self, other: &Self) -> bool {
80 assert!(!self.0.is_nan() && !other.0.is_nan(), "float is NaN");
81 self.0 == other.0
82 }
83}
84
85impl PartialEq<f32> for Scalar {
86 fn eq(&self, other: &f32) -> bool {
87 self == &Self(*other)
88 }
89}
90
91impl Ord for Scalar {
92 fn cmp(&self, other: &Self) -> Ordering {
93 self.0.partial_cmp(&other.0).expect("float is NaN")
94 }
95}
96
97impl PartialOrd for Scalar {
98 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
99 Some(self.cmp(other))
100 }
101
102 fn lt(&self, other: &Self) -> bool {
103 self.0 < other.0
104 }
105
106 fn le(&self, other: &Self) -> bool {
107 self.0 <= other.0
108 }
109
110 fn gt(&self, other: &Self) -> bool {
111 self.0 > other.0
112 }
113
114 fn ge(&self, other: &Self) -> bool {
115 self.0 >= other.0
116 }
117}
118
119impl std::ops::Neg for Scalar {
120 type Output = Self;
121
122 fn neg(self) -> Self::Output {
123 Self(-self.0)
124 }
125}
126
127impl Hash for Scalar {
128 fn hash<H: Hasher>(&self, state: &mut H) {
129 debug_assert!(!self.0.is_nan(), "float is NaN");
130 self.0.to_le_bytes().hash(state);
132 }
133}
134
135pub type Abs = Scalar;
137pub type Size = Axes<Abs>;
139pub type Point = Axes<Scalar>;
141pub type Ratio = Scalar;
143pub type Angle = Scalar;
145
146#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash)]
148#[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))]
149#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
150pub struct Axes<T> {
151 pub x: T,
153 pub y: T,
155}
156
157impl<T> Axes<T> {
158 pub const fn new(x: T, y: T) -> Self {
159 Self { x, y }
160 }
161}
162
163impl<T> std::ops::Add for Axes<T>
165where
166 T: std::ops::Add<Output = T>,
167{
168 type Output = Self;
169
170 fn add(self, rhs: Self) -> Self::Output {
171 Self {
172 x: self.x + rhs.x,
173 y: self.y + rhs.y,
174 }
175 }
176}
177
178impl From<tiny_skia_path::Point> for Point {
179 fn from(typst_axes: tiny_skia_path::Point) -> Self {
180 Self {
181 x: typst_axes.x.into(),
182 y: typst_axes.y.into(),
183 }
184 }
185}
186
187impl From<Point> for tiny_skia_path::Point {
188 fn from(axes: Point) -> Self {
189 Self {
190 x: axes.x.into(),
191 y: axes.y.into(),
192 }
193 }
194}
195
196#[repr(C)]
198#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
199#[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))]
200#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
201pub struct Transform {
202 pub sx: Ratio,
203 pub ky: Ratio,
204 pub kx: Ratio,
205 pub sy: Ratio,
206 pub tx: Abs,
207 pub ty: Abs,
208}
209
210impl Transform {
211 pub fn from_scale(sx: Ratio, sy: Ratio) -> Self {
212 Self {
213 sx,
214 ky: 0.0.into(),
215 kx: 0.0.into(),
216 sy,
217 tx: 0.0.into(),
218 ty: 0.0.into(),
219 }
220 }
221
222 pub fn from_translate(tx: Abs, ty: Abs) -> Self {
223 Self {
224 sx: 1.0.into(),
225 ky: 0.0.into(),
226 kx: 0.0.into(),
227 sy: 1.0.into(),
228 tx,
229 ty,
230 }
231 }
232
233 pub fn from_skew(kx: Ratio, ky: Ratio) -> Self {
234 Self {
235 sx: 1.0.into(),
236 ky,
237 kx,
238 sy: 1.0.into(),
239 tx: 0.0.into(),
240 ty: 0.0.into(),
241 }
242 }
243
244 pub fn identity() -> Self {
245 Self::from_scale(1.0.into(), 1.0.into())
246 }
247
248 #[inline]
249 pub fn pre_concat(self, other: Self) -> Self {
250 let ts: tiny_skia_path::Transform = self.into();
251 let other: tiny_skia_path::Transform = other.into();
252 let ts = ts.pre_concat(other);
253 ts.into()
254 }
255
256 #[inline]
257 pub fn post_concat(self, other: Self) -> Self {
258 other.pre_concat(self)
259 }
260
261 #[inline]
262 pub fn pre_translate(self, tx: f32, ty: f32) -> Self {
263 let ts: tiny_skia_path::Transform = self.into();
264 let ts = ts.pre_translate(tx, ty);
265 ts.into()
266 }
267
268 pub fn is_identity(self) -> bool {
270 self == Self::identity()
271 }
272
273 pub fn invert(self) -> Option<Self> {
277 if self.is_identity() {
279 return Some(self);
280 }
281
282 if self.kx.is_zero() && self.ky.is_zero() {
284 if self.sx.is_zero() || self.sy.is_zero() {
285 return Some(Self::from_translate(-self.tx, -self.ty));
286 }
287
288 let inv_x = 1.0 / self.sx.0;
289 let inv_y = 1.0 / self.sy.0;
290 return Some(Self {
291 sx: Scalar(inv_x),
292 ky: Scalar(0.),
293 kx: Scalar(0.),
294 sy: Scalar(inv_y),
295 tx: Scalar(-self.tx.0 * inv_x),
296 ty: Scalar(-self.ty.0 * inv_y),
297 });
298 }
299
300 let det = self.sx.0 * self.sy.0 - self.kx.0 * self.ky.0;
301 if det.abs() < 1e-12 {
302 return None;
303 }
304
305 let inv_det = 1.0 / det;
306 Some(Self {
307 sx: Scalar(self.sy.0 * inv_det),
308 ky: Scalar(-self.ky.0 * inv_det),
309 kx: Scalar(-self.kx.0 * inv_det),
310 sy: Scalar(self.sx.0 * inv_det),
311 tx: Scalar((self.kx.0 * self.ty.0 - self.sy.0 * self.tx.0) * inv_det),
312 ty: Scalar((self.ky.0 * self.tx.0 - self.sx.0 * self.ty.0) * inv_det),
313 })
314 }
315}
316
317impl From<tiny_skia_path::Transform> for Transform {
318 fn from(skia_transform: tiny_skia_path::Transform) -> Self {
319 Self {
320 sx: skia_transform.sx.into(),
321 ky: skia_transform.ky.into(),
322 kx: skia_transform.kx.into(),
323 sy: skia_transform.sy.into(),
324 tx: skia_transform.tx.into(),
325 ty: skia_transform.ty.into(),
326 }
327 }
328}
329
330impl From<Transform> for tiny_skia_path::Transform {
331 fn from(ir_transform: Transform) -> Self {
332 Self {
333 sx: ir_transform.sx.into(),
334 ky: ir_transform.ky.into(),
335 kx: ir_transform.kx.into(),
336 sy: ir_transform.sy.into(),
337 tx: ir_transform.tx.into(),
338 ty: ir_transform.ty.into(),
339 }
340 }
341}
342
343#[derive(Debug, Clone, Copy, PartialEq, Hash, Default)]
344pub struct Rect {
345 pub lo: Point,
346 pub hi: Point,
347}
348
349impl Rect {
350 pub fn empty() -> Self {
351 Self {
352 lo: Point::new(Scalar(0.), Scalar(0.)),
353 hi: Point::new(Scalar(0.), Scalar(0.)),
354 }
355 }
356
357 pub fn cano(&self) -> Self {
358 let Rect { lo, hi } = self;
359 Self {
360 lo: Point::new(lo.x.min(hi.x), lo.y.min(hi.y)),
361 hi: Point::new(lo.x.max(hi.x), lo.y.max(hi.y)),
362 }
363 }
364
365 pub fn is_empty(&self) -> bool {
367 self.lo.x >= self.hi.x || self.lo.y >= self.hi.y
368 }
369
370 pub fn is_intersected(&self) -> bool {
374 self.lo.x <= self.hi.x || self.lo.y <= self.hi.y
375 }
376
377 pub fn intersect(&self, other: &Self) -> Self {
378 Self {
379 lo: self.lo.max(&other.lo),
380 hi: self.hi.min(&other.hi),
381 }
382 }
383
384 pub fn union(&self, other: &Self) -> Self {
385 if self.is_empty() {
386 return *other;
387 }
388
389 Self {
390 lo: self.lo.min(&other.lo),
391 hi: self.hi.max(&other.hi),
392 }
393 }
394
395 pub fn translate(&self, dp: Point) -> Self {
396 Self {
397 lo: self.lo + dp,
398 hi: self.hi + dp,
399 }
400 }
401
402 pub fn width(&self) -> Scalar {
403 self.hi.x - self.lo.x
404 }
405
406 pub fn height(&self) -> Scalar {
407 self.hi.y - self.lo.y
408 }
409
410 pub fn left(&self) -> Scalar {
411 self.lo.x
412 }
413
414 pub fn right(&self) -> Scalar {
415 self.hi.x
416 }
417
418 pub fn top(&self) -> Scalar {
419 self.lo.y
420 }
421
422 pub fn bottom(&self) -> Scalar {
423 self.hi.y
424 }
425}
426
427impl From<tiny_skia_path::Rect> for Rect {
428 fn from(rect: tiny_skia_path::Rect) -> Self {
429 Self {
430 lo: Point::new(Scalar(rect.left()), Scalar(rect.top())),
431 hi: Point::new(Scalar(rect.right()), Scalar(rect.bottom())),
432 }
433 }
434}
435
436impl TryFrom<Rect> for tiny_skia_path::Rect {
437 type Error = ();
438
439 fn try_from(rect: Rect) -> Result<Self, Self::Error> {
440 Self::from_ltrb(rect.lo.x.0, rect.lo.y.0, rect.hi.x.0, rect.hi.y.0).ok_or(())
441 }
442}
443
444pub trait EuclidMinMax {
445 fn min(&self, other: &Self) -> Self;
446 fn max(&self, other: &Self) -> Self;
447}
448
449impl EuclidMinMax for Scalar {
450 fn min(&self, other: &Self) -> Self {
451 Self(self.0.min(other.0))
452 }
453
454 fn max(&self, other: &Self) -> Self {
455 Self(self.0.max(other.0))
456 }
457}
458
459impl EuclidMinMax for Point {
460 fn min(&self, other: &Self) -> Self {
461 Self {
462 x: self.x.min(other.x),
463 y: self.y.min(other.y),
464 }
465 }
466
467 fn max(&self, other: &Self) -> Self {
468 Self {
469 x: self.x.max(other.x),
470 y: self.y.max(other.y),
471 }
472 }
473}
474
475#[derive(Debug, Clone, Hash, PartialEq, Eq)]
478#[cfg_attr(feature = "rkyv", derive(Archive, rDeser, rSer))]
479#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))]
480pub enum TransformItem {
481 Matrix(Arc<Transform>),
483 Translate(Arc<Axes<Abs>>),
485 Scale(Arc<(Ratio, Ratio)>),
487 Rotate(Arc<Scalar>),
489 Skew(Arc<(Ratio, Ratio)>),
491
492 Clip(Arc<PathItem>),
494}
495
496impl From<TransformItem> for Transform {
498 fn from(value: TransformItem) -> Self {
499 match value {
500 TransformItem::Matrix(m) => *m,
501 TransformItem::Scale(m) => Transform::from_scale(m.0, m.1),
502 TransformItem::Translate(m) => Transform::from_translate(m.x, m.y),
503 TransformItem::Rotate(_m) => todo!(),
504 TransformItem::Skew(m) => Transform::from_skew(m.0, m.1),
505 TransformItem::Clip(_m) => Transform::identity(),
506 }
507 }
508}