rasterize/
geometry.rs

1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3
4use crate::{Line, Size, SvgParserError, utils::clamp};
5use std::{
6    fmt,
7    ops::{Add, Div, Mul, Sub},
8    str::FromStr,
9};
10
11/// Scalar type
12pub type Scalar = f64;
13/// Epsilon value
14pub const EPSILON: f64 = f64::EPSILON;
15/// Square root of the epsilon value
16pub const EPSILON_SQRT: f64 = 1.490_116_119_384_765_6e-8;
17/// Mathematical pi constant
18pub const PI: f64 = std::f64::consts::PI;
19
20const SCALAR_PRECISION: usize = 4;
21const SCALAR_FORMAT: u128 = lexical_core::NumberFormatBuilder::new()
22    .required_integer_digits(false)
23    .build_strict();
24const SCALAR_FORMAT_OPTIONS: lexical_core::WriteFloatOptions =
25    lexical_core::WriteFloatOptions::builder()
26        .max_significant_digits(std::num::NonZero::new(SCALAR_PRECISION))
27        .trim_floats(true)
28        .build_unchecked();
29
30pub struct ScalarFormatter {
31    precision: usize,
32    round: bool, // whether to preround (correctly removes)
33    options: lexical_core::WriteFloatOptions,
34    buffer: [u8; lexical_core::BUFFER_SIZE],
35}
36impl ScalarFormatter {
37    pub fn new(precision: Option<usize>, round: bool) -> Self {
38        let options = precision
39            .and_then(|precision| {
40                lexical_core::WriteFloatOptionsBuilder::new()
41                    .max_significant_digits(std::num::NonZero::new(precision))
42                    .trim_floats(true)
43                    .build()
44                    .ok()
45            })
46            .unwrap_or(SCALAR_FORMAT_OPTIONS);
47        Self {
48            precision: precision.unwrap_or(SCALAR_PRECISION),
49            options,
50            round,
51            buffer: [0u8; lexical_core::BUFFER_SIZE],
52        }
53    }
54
55    pub fn new_fmt(fmt: &fmt::Formatter<'_>) -> Self {
56        Self::new(fmt.precision(), fmt.alternate())
57    }
58
59    pub fn format(&mut self, mut value: Scalar) -> &[u8] {
60        if self.round {
61            value = Self::round_significant(value, self.precision);
62        }
63        lexical_core::write_with_options::<_, SCALAR_FORMAT>(value, &mut self.buffer, &self.options)
64    }
65
66    pub fn format_str(&mut self, value: Scalar) -> &str {
67        unsafe {
68            // SAFETY: trust lexical to produce valid utf-8 string
69            std::str::from_utf8_unchecked(self.format(value))
70        }
71    }
72
73    pub fn round_significant(value: f64, precision: usize) -> f64 {
74        if value.abs() < EPSILON {
75            0.0
76        } else {
77            let shift = precision as i32 - value.abs().log10().ceil() as i32;
78            let shift_factor = 10_f64.powi(shift);
79            (value * shift_factor).round() / shift_factor
80        }
81    }
82}
83
84pub struct ScalarFormat(pub Scalar);
85
86impl fmt::Debug for ScalarFormat {
87    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
88        let mut formatter = ScalarFormatter::new_fmt(fmt);
89        fmt.write_str(formatter.format_str(self.0))
90    }
91}
92
93impl fmt::Display for ScalarFormat {
94    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
95        let mut formatter = ScalarFormatter::new_fmt(fmt);
96        fmt.write_str(formatter.format_str(self.0))
97    }
98}
99
100/// Value representing a 2D point or vector.
101#[derive(Clone, Copy, PartialEq)]
102#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
103pub struct Point(pub [Scalar; 2]);
104
105#[allow(clippy::derived_hash_with_manual_eq)]
106impl std::hash::Hash for Point {
107    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
108        let Point([x, y]) = self;
109        x.to_be_bytes().hash(state);
110        y.to_be_bytes().hash(state);
111    }
112}
113
114impl fmt::Debug for Point {
115    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
116        let Point([x, y]) = self;
117        let mut formatter = ScalarFormatter::new_fmt(fmt);
118        fmt.write_str(formatter.format_str(*x))?;
119        fmt.write_str(",")?;
120        fmt.write_str(formatter.format_str(*y))?;
121        Ok(())
122    }
123}
124
125impl Point {
126    #[inline]
127    pub const fn new(x: Scalar, y: Scalar) -> Self {
128        Self([x, y])
129    }
130
131    /// Get `x` component of the point
132    #[inline]
133    pub const fn x(self) -> Scalar {
134        self.0[0]
135    }
136
137    /// Get `y` component of the point
138    #[inline]
139    pub const fn y(self) -> Scalar {
140        self.0[1]
141    }
142
143    /// Get length of the vector (distance from the origin)
144    pub fn length(self) -> Scalar {
145        let Self([x, y]) = self;
146        x.hypot(y)
147    }
148
149    /// Distance between two points
150    pub fn dist(self, other: Self) -> Scalar {
151        (self - other).length()
152    }
153
154    /// Dot product between two vectors
155    pub fn dot(self, other: Self) -> Scalar {
156        let Self([x0, y0]) = self;
157        let Self([x1, y1]) = other;
158        x0 * x1 + y0 * y1
159    }
160
161    /// Cross product between two vectors
162    pub fn cross(self, other: Self) -> Scalar {
163        let Self([x0, y0]) = self;
164        let Self([x1, y1]) = other;
165        x0 * y1 - y0 * x1
166    }
167
168    /// Get vector normal (not a unit sized)
169    pub fn normal(self) -> Point {
170        let Self([x, y]) = self;
171        Self([y, -x])
172    }
173
174    /// Convert vector to a unit size vector, if length is not zero
175    pub fn normalize(self) -> Option<Point> {
176        let Self([x, y]) = self;
177        let length = self.length();
178        if length < EPSILON {
179            None
180        } else {
181            Some(Self([x / length, y / length]))
182        }
183    }
184
185    /// Calculate angle (from self to the other) between two vectors
186    pub fn angle_between(self, other: Self) -> Option<Scalar> {
187        let angle = clamp(self.cos_between(other)?, -1.0, 1.0).acos();
188        if self.cross(other) < 0.0 {
189            Some(-angle)
190        } else {
191            Some(angle)
192        }
193    }
194
195    /// Cosine of the angle (from self to the other) between to vectors
196    pub fn cos_between(self, other: Self) -> Option<Scalar> {
197        let lengths = self.length() * other.length();
198        if lengths < EPSILON {
199            None
200        } else {
201            Some(self.dot(other) / lengths)
202        }
203    }
204
205    /// Sine of the angle (from self to the other) between to vectors
206    pub fn sin_between(self, other: Self) -> Option<Scalar> {
207        let cos = self.cos_between(other)?;
208        Some((1.0 - cos * cos).sqrt())
209    }
210
211    /// Determine if self is close to the other within the margin of error (EPSILON)
212    pub fn is_close_to(self, other: Point) -> bool {
213        let Self([x0, y0]) = self;
214        let Self([x1, y1]) = other;
215        (x0 - x1).abs() < EPSILON && (y0 - y1).abs() < EPSILON
216    }
217}
218
219impl From<(Scalar, Scalar)> for Point {
220    #[inline]
221    fn from(xy: (Scalar, Scalar)) -> Self {
222        Self([xy.0, xy.1])
223    }
224}
225
226impl<'a> From<&'a Point> for Point {
227    fn from(point: &'a Point) -> Self {
228        let Self([x, y]) = point;
229        Self([*x, *y])
230    }
231}
232
233impl Mul<&Point> for Scalar {
234    type Output = Point;
235
236    #[inline]
237    fn mul(self, other: &Point) -> Self::Output {
238        let Point([x, y]) = other;
239        Point([self * x, self * y])
240    }
241}
242
243impl Mul<Point> for Scalar {
244    type Output = Point;
245
246    #[inline]
247    fn mul(self, other: Point) -> Self::Output {
248        let Point([x, y]) = other;
249        Point([self * x, self * y])
250    }
251}
252
253impl Div<Scalar> for Point {
254    type Output = Point;
255
256    #[inline]
257    fn div(self, rhs: Scalar) -> Self::Output {
258        let Point([x, y]) = self;
259        Point([x / rhs, y / rhs])
260    }
261}
262
263impl Add for Point {
264    type Output = Point;
265
266    #[inline]
267    fn add(self, other: Point) -> Self::Output {
268        let Point([x0, y0]) = self;
269        let Point([x1, y1]) = other;
270        Point([x0 + x1, y0 + y1])
271    }
272}
273
274impl Sub for Point {
275    type Output = Point;
276
277    #[inline]
278    fn sub(self, other: Point) -> Self::Output {
279        let Point([x0, y0]) = self;
280        let Point([x1, y1]) = other;
281        Point([x0 - x1, y0 - y1])
282    }
283}
284
285impl Mul for Point {
286    type Output = Point;
287
288    #[inline]
289    fn mul(self, other: Point) -> Self::Output {
290        let Point([x0, y0]) = self;
291        let Point([x1, y1]) = other;
292        Point([x0 * x1, y0 * y1])
293    }
294}
295
296/// Alignment options
297#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
298pub enum Align {
299    /// Align by minimal value
300    Min,
301    /// Align by center value
302    Mid,
303    /// Align by maximum value
304    Max,
305}
306
307/// 2D affine transformation
308///
309/// Stored as an array [m00, m01, m02, m10, m11, m12], semantically corresponds to
310/// a matrix:
311/// ┌             ┐
312/// │ m00 m01 m02 │
313/// │ m11 m11 m12 │
314/// │   0   0   1 │
315/// └             ┘
316#[derive(Clone, Copy, PartialEq)]
317pub struct Transform([Scalar; 6]);
318
319impl Default for Transform {
320    fn default() -> Self {
321        Self::identity()
322    }
323}
324
325impl fmt::Debug for Transform {
326    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
327        fmt::Display::fmt(self, f)
328    }
329}
330
331impl fmt::Display for Transform {
332    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
333        let mut formatter = ScalarFormatter::new(fmt.precision().or(Some(6)), fmt.alternate());
334        let Self([m00, m01, m02, m10, m11, m12]) = self;
335        fmt.write_str("matrix(")?;
336        for val in [m00, m10, m01, m11, m02] {
337            fmt.write_str(formatter.format_str(*val))?;
338            fmt.write_str(" ")?;
339        }
340        fmt.write_str(formatter.format_str(*m12))?;
341        fmt.write_str(")")?;
342        Ok(())
343    }
344}
345
346impl Transform {
347    pub fn new(
348        m00: Scalar,
349        m01: Scalar,
350        m02: Scalar,
351        m10: Scalar,
352        m11: Scalar,
353        m12: Scalar,
354    ) -> Self {
355        Self([m00, m01, m02, m10, m11, m12])
356    }
357
358    pub fn identity() -> Self {
359        Self([1.0, 0.0, 0.0, 0.0, 1.0, 0.0])
360    }
361
362    /// Apply this transformation to a point
363    pub fn apply(&self, point: Point) -> Point {
364        let Self([m00, m01, m02, m10, m11, m12]) = self;
365        let Point([x, y]) = point;
366        Point([x * m00 + y * m01 + m02, x * m10 + y * m11 + m12])
367    }
368
369    /// Find the inverse transformation
370    pub fn invert(&self) -> Option<Self> {
371        // inv([[M, v], [0, 1]]) = [[inv(M), - inv(M) * v], [0, 1]]
372        let Self([m00, m01, m02, m10, m11, m12]) = self;
373        let det = m00 * m11 - m10 * m01;
374        if det.abs() <= EPSILON {
375            return None;
376        }
377        let o00 = m11 / det;
378        let o01 = -m01 / det;
379        let o10 = -m10 / det;
380        let o11 = m00 / det;
381        let o02 = -o00 * m02 - o01 * m12;
382        let o12 = -o10 * m02 - o11 * m12;
383        Some(Self([o00, o01, o02, o10, o11, o12]))
384    }
385
386    /// Apply translation by `[tx, ty]` before self
387    pub fn pre_translate(&self, tx: Scalar, ty: Scalar) -> Self {
388        self.pre_concat(Self::new_translate(tx, ty))
389    }
390
391    pub fn new_translate(tx: Scalar, ty: Scalar) -> Self {
392        Self([1.0, 0.0, tx, 0.0, 1.0, ty])
393    }
394
395    /// Apply scale transformation by `[sx, sy]` before self
396    pub fn pre_scale(&self, sx: Scalar, sy: Scalar) -> Self {
397        self.pre_concat(Self::new_scale(sx, sy))
398    }
399
400    pub fn new_scale(sx: Scalar, sy: Scalar) -> Self {
401        Self([sx, 0.0, 0.0, 0.0, sy, 0.0])
402    }
403
404    /// Apply rotation by `a` angle around the origin before self
405    pub fn pre_rotate(&self, a: Scalar) -> Self {
406        self.pre_concat(Self::new_rotate(a))
407    }
408
409    pub fn new_rotate(a: Scalar) -> Self {
410        let (sin, cos) = a.sin_cos();
411        Self([cos, -sin, 0.0, sin, cos, 0.0])
412    }
413
414    /// Apply rotation around point `p` by angle `a` before self
415    pub fn pre_rotate_around(&self, a: Scalar, p: impl Into<Point>) -> Self {
416        let p = p.into();
417        self.pre_translate(p.x(), p.y())
418            .pre_rotate(a)
419            .pre_translate(-p.x(), -p.y())
420    }
421
422    /// Apply skew transformation by `[ax, ay]` before self
423    pub fn pre_skew(&self, ax: Scalar, ay: Scalar) -> Self {
424        self.pre_concat(Self::new_skew(ax, ay))
425    }
426
427    pub fn new_skew(ax: Scalar, ay: Scalar) -> Self {
428        Self([1.0, ax.tan(), 0.0, ay.tan(), 1.0, 0.0])
429    }
430
431    /// Apply other transformation before the current one
432    pub fn pre_concat(&self, other: Self) -> Self {
433        *self * other
434    }
435
436    /// Apply other transformation after the current one
437    pub fn post_concat(&self, other: Self) -> Self {
438        other * *self
439    }
440
441    /// Create transformation which converts `src` line segment to `dst` line segment
442    pub fn line_to_line(src: Line, dst: Line) -> Option<Self> {
443        // Find transformation which converts (0, 0) to p0 and (0, 1) to p1
444        fn unit_y_to_line(line: Line) -> Transform {
445            let Line([p0, p1]) = line;
446            // rotation + scale
447            #[rustfmt::skip]
448            let tr = Transform::new(
449                p1.y() - p0.y(), p1.x() - p0.x(), p0.x(),
450                p0.x() - p1.x(), p1.y() - p0.y(), p0.y(),
451            );
452            tr
453        }
454        Some(unit_y_to_line(dst) * unit_y_to_line(src).invert()?)
455    }
456
457    /// Create transformation which makes line horizontal with origin at (0, 0).
458    pub fn make_horizontal(line: Line) -> Transform {
459        let [p0, p1] = line.points();
460        let cos_sin = match (p1 - p0).normalize() {
461            None => return Transform::identity(),
462            Some(cos_sin) => cos_sin,
463        };
464        let cos = cos_sin.x();
465        let sin = cos_sin.y();
466        Transform([cos, sin, 0.0, -sin, cos, 0.0]).pre_translate(-p0.x(), -p0.y())
467    }
468
469    /// Create transformation that is required to fit `src` box into `dst`.
470    pub fn fit_bbox(src: BBox, dst: BBox, align: Align) -> Transform {
471        let scale = (dst.height() / src.height()).min(dst.width() / src.width());
472        let base = Transform::new_translate(dst.x(), dst.y())
473            .pre_scale(scale, scale)
474            .pre_translate(-src.x(), -src.y());
475        let align = match align {
476            Align::Min => Transform::identity(),
477            Align::Mid => Transform::new_translate(
478                (dst.width() - src.width() * scale) / 2.0,
479                (dst.height() - src.height() * scale) / 2.0,
480            ),
481            Align::Max => Transform::new_translate(
482                dst.width() - src.width() * scale,
483                dst.height() - src.height() * scale,
484            ),
485        };
486        align * base
487    }
488
489    /// Create transformation needed to fit source bounding box to provided size image
490    pub fn fit_size(src: BBox, size: Size, align: Align) -> (Size, Transform) {
491        let src = {
492            let min = Point::new(src.min().x().floor(), src.min().y().floor());
493            let max = Point::new(src.max().x().ceil(), src.max().y().ceil());
494            BBox::new(min, max)
495        };
496        let (height, width) = match (size.height, size.width) {
497            (0, 0) => (src.height(), src.width()),
498            (height, 0) => {
499                let height = height as Scalar;
500                (height, (src.width() * height / src.height()).ceil())
501            }
502            (0, width) => {
503                let width = width as Scalar;
504                ((src.height() * width / src.width()).ceil(), width)
505            }
506            (height, width) => (height as Scalar, width as Scalar),
507        };
508        let dst = BBox::new((0.0, 0.0), (width, height));
509        (
510            Size {
511                height: height as usize,
512                width: width as usize,
513            },
514            Transform::fit_bbox(src, dst, align),
515        )
516    }
517}
518
519impl Mul<Transform> for Transform {
520    type Output = Transform;
521
522    /// Multiply matrices representing transformations
523    fn mul(self, other: Transform) -> Self::Output {
524        let Self([s00, s01, s02, s10, s11, s12]) = self;
525        let Self([o00, o01, o02, o10, o11, o12]) = other;
526
527        // s00, s01, s02 | o00, o01, o02
528        // s10, s11, s12 | o10, o11, o12
529        // 0  , 0  , 1   | 0  , 0  , 1
530        Self([
531            s00 * o00 + s01 * o10,
532            s00 * o01 + s01 * o11,
533            s00 * o02 + s01 * o12 + s02,
534            s10 * o00 + s11 * o10,
535            s10 * o01 + s11 * o11,
536            s10 * o02 + s11 * o12 + s12,
537        ])
538    }
539}
540
541/// Bounding box with sides directed along the axes
542#[derive(Clone, Copy, PartialEq, Hash)]
543pub struct BBox {
544    /// Point with minimal x and y values
545    min: Point,
546    /// Point with maximum x and y values
547    max: Point,
548}
549
550impl BBox {
551    /// Construct bounding box which includes points `p0` and `p1`
552    pub fn new(p0: impl Into<Point>, p1: impl Into<Point>) -> Self {
553        let Point([x0, y0]) = p0.into();
554        let Point([x1, y1]) = p1.into();
555        let (x0, x1) = if x0 <= x1 { (x0, x1) } else { (x1, x0) };
556        let (y0, y1) = if y0 <= y1 { (y0, y1) } else { (y1, y0) };
557        Self {
558            min: Point([x0, y0]),
559            max: Point([x1, y1]),
560        }
561    }
562
563    /// Point with minimum values of x and y coordinates
564    #[inline]
565    pub fn min(&self) -> Point {
566        self.min
567    }
568
569    /// Point with minimum values of x and y coordinates
570    #[inline]
571    pub fn max(&self) -> Point {
572        self.max
573    }
574
575    /// `x` coordinate of the point with the minimal value
576    #[inline]
577    pub fn x(&self) -> Scalar {
578        self.min.x()
579    }
580
581    /// `y` coordinate of the point with the minimal value
582    #[inline]
583    pub fn y(&self) -> Scalar {
584        self.min.y()
585    }
586
587    /// Width of the bounding box
588    #[inline]
589    pub fn width(&self) -> Scalar {
590        self.max.x() - self.min.x()
591    }
592
593    /// Hight of the bounding box
594    #[inline]
595    pub fn height(&self) -> Scalar {
596        self.max.y() - self.min.y()
597    }
598
599    /// Diagonal line from `min` to `max` of the bounding box
600    pub fn diag(&self) -> Line {
601        Line::new(self.min, self.max)
602    }
603
604    /// Determine if the point is inside of the bounding box
605    pub fn contains(&self, point: Point) -> bool {
606        let Point([x, y]) = point;
607        self.min.x() <= x && x <= self.max.x() && self.min.y() <= y && y <= self.max.y()
608    }
609
610    /// Extend bounding box so it would contains provided point
611    pub fn extend(&self, point: Point) -> Self {
612        let Point([x, y]) = point;
613        let Point([x0, y0]) = self.min;
614        let Point([x1, y1]) = self.max;
615        let (x0, x1) = if x < x0 {
616            (x, x1)
617        } else if x > x1 {
618            (x0, x)
619        } else {
620            (x0, x1)
621        };
622        let (y0, y1) = if y < y0 {
623            (y, y1)
624        } else if y > y1 {
625            (y0, y)
626        } else {
627            (y0, y1)
628        };
629        Self {
630            min: Point([x0, y0]),
631            max: Point([x1, y1]),
632        }
633    }
634
635    /// Create union bounding box of two bounding boxes
636    pub fn union(&self, other: BBox) -> Self {
637        self.extend(other.min).extend(other.max)
638    }
639
640    pub fn union_opt(&self, other: Option<BBox>) -> Self {
641        match other {
642            Some(other) => self.union(other),
643            None => *self,
644        }
645    }
646
647    /// Find bounding box of the intersection of two bounding boxes
648    pub fn intersect(&self, other: BBox) -> Option<BBox> {
649        let (x_min, x_max) =
650            range_intersect(self.min.x(), self.max.x(), other.min.x(), other.max.x())?;
651        let (y_min, y_max) =
652            range_intersect(self.min.y(), self.max.y(), other.min.y(), other.max.y())?;
653        Some(BBox::new(
654            Point::new(x_min, y_min),
655            Point::new(x_max, y_max),
656        ))
657    }
658
659    /// Transform that makes bounding box a unit-sized square
660    ///
661    /// This is used by clip|mask|gradient units
662    pub fn unit_transform(&self) -> Transform {
663        Transform::new_translate(self.x(), self.y()).pre_scale(self.width(), self.height())
664    }
665
666    /// Compute new bounding box such that it will include original bounding box after transformation
667    pub fn transform(&self, tr: Transform) -> BBox {
668        let p00 = tr.apply(self.min);
669        let p01 = tr.apply(Point::new(self.min.x(), self.max.y()));
670        let p10 = tr.apply(Point::new(self.max.x(), self.min.y()));
671        let p11 = tr.apply(self.max);
672        BBox::new(p00, p11).extend(p10).extend(p01)
673    }
674}
675
676/// Find intersection of two ranges
677fn range_intersect(
678    r0_min: Scalar,
679    r0_max: Scalar,
680    r1_min: Scalar,
681    r1_max: Scalar,
682) -> Option<(Scalar, Scalar)> {
683    if r0_min > r1_max || r1_min > r0_max {
684        None
685    } else {
686        Some((r0_min.max(r1_min), r0_max.min(r1_max)))
687    }
688}
689
690impl FromStr for BBox {
691    type Err = SvgParserError;
692
693    fn from_str(string: &str) -> Result<Self, Self::Err> {
694        let mut values = string.split([' ', ',']).map(|s| s.trim().parse().ok());
695        let minx: Scalar = values.next().flatten().ok_or(SvgParserError::InvalidBBox)?;
696        let miny: Scalar = values.next().flatten().ok_or(SvgParserError::InvalidBBox)?;
697        let width: Scalar = values.next().flatten().ok_or(SvgParserError::InvalidBBox)?;
698        let height: Scalar = values.next().flatten().ok_or(SvgParserError::InvalidBBox)?;
699        Ok(BBox::new((minx, miny), (minx + width, miny + height)))
700    }
701}
702
703impl fmt::Display for BBox {
704    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
705        let mut formatter = ScalarFormatter::new_fmt(fmt);
706        fmt.write_str(formatter.format_str(self.x()))?;
707        fmt.write_str(" ")?;
708        fmt.write_str(formatter.format_str(self.y()))?;
709        fmt.write_str(" ")?;
710        fmt.write_str(formatter.format_str(self.width()))?;
711        fmt.write_str(" ")?;
712        fmt.write_str(formatter.format_str(self.height()))
713    }
714}
715
716impl fmt::Debug for BBox {
717    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
718        let mut formatter = ScalarFormatter::new_fmt(fmt);
719        fmt.debug_struct("BBox")
720            .field("x", &formatter.format_str(self.x()))
721            .field("y", &formatter.format_str(self.y()))
722            .field("w", &formatter.format_str(self.width()))
723            .field("h", &formatter.format_str(self.height()))
724            .finish()
725    }
726}
727
728#[cfg(feature = "serde")]
729impl Serialize for BBox {
730    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
731    where
732        S: serde::Serializer,
733    {
734        (self.x(), self.y(), self.width(), self.height()).serialize(serializer)
735    }
736}
737
738#[cfg(feature = "serde")]
739impl<'de> Deserialize<'de> for BBox {
740    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
741    where
742        D: serde::Deserializer<'de>,
743    {
744        let (minx, miny, width, height): (Scalar, Scalar, Scalar, Scalar) =
745            Deserialize::deserialize(deserializer)?;
746        Ok(BBox::new((minx, miny), (minx + width, miny + height)))
747    }
748}
749
750#[cfg(test)]
751mod tests {
752    use super::*;
753    use crate::{Curve, assert_approx_eq};
754    type Error = Box<dyn std::error::Error>;
755
756    #[test]
757    fn test_trasform() -> Result<(), Error> {
758        let tr = Transform::identity()
759            .pre_translate(1.0, 2.0)
760            .pre_rotate(PI / 3.0)
761            .pre_skew(2.0, 3.0)
762            .pre_scale(3.0, 2.0);
763        let inv = tr.invert().unwrap();
764        let p0 = Point::new(1.0, 1.0);
765
766        let p1 = tr.apply(p0);
767        assert_approx_eq!(p1.x(), -1.04674389, 1e-6);
768        assert_approx_eq!(p1.y(), 1.59965634, 1e-6);
769
770        let p2 = inv.apply(p1);
771        assert_approx_eq!(p2.x(), 1.0, 1e-6);
772        assert_approx_eq!(p2.y(), 1.0, 1e-6);
773
774        let l0 = Line::new((1.0, 0.0), (-3.0, 3.0));
775        let l1 = l0.transform(Transform::make_horizontal(l0));
776        assert_eq!(l1.start(), Point::new(0.0, 0.0));
777        assert_approx_eq!(l1.end().x(), 5.0);
778        assert_approx_eq!(l1.end().y(), 0.0, 1e-6);
779
780        let s0 = Line::new((2.0, 1.0), (1.0, 4.0));
781        // unit vector perpendicular to s0
782        let s1 = Line::new(
783            s0.start(),
784            s0.start() + s0.direction().normal().normalize().unwrap(),
785        );
786        let d0 = Line::new((3.0, 1.0), (4.0, 2.0));
787        let tr = Transform::line_to_line(s0, d0).unwrap();
788        let o0 = s0.transform(tr);
789        let o1 = s1.transform(tr);
790        assert_approx_eq!((o0.start() - d0.start()).length(), 0.0);
791        assert_approx_eq!((o0.end() - d0.end()).length(), 0.0);
792        // no skew introduced
793        assert_approx_eq!(o0.direction().dot(o1.direction()), 0.0);
794        // uniform scale
795        assert_approx_eq!(o1.length(), d0.length() / s0.length());
796
797        Ok(())
798    }
799
800    #[test]
801    fn test_transform_fit() -> Result<(), Error> {
802        let s0 = BBox::new(Point::new(1.0, 1.0), Point::new(2.0, 2.0));
803        let s1 = BBox::new(Point::new(1.0, 1.0), Point::new(1.5, 2.0));
804        let s2 = BBox::new(Point::new(1.0, 1.0), Point::new(2.0, 1.5));
805        let d = BBox::new(Point::new(3.0, 5.0), Point::new(13.0, 15.0));
806
807        let tr0 = Transform::fit_bbox(s0, d, Align::Mid);
808        assert!(tr0.apply(s0.min).is_close_to(d.min));
809        assert!(tr0.apply(s0.max).is_close_to(d.max));
810
811        let tr1 = Transform::fit_bbox(s1, d, Align::Min);
812        assert!(tr1.apply(s1.min).is_close_to(d.min));
813        assert!(tr1.apply(s1.max).is_close_to(Point::new(8.0, 15.0)));
814
815        let tr2 = Transform::fit_bbox(s2, d, Align::Max);
816        assert!(tr2.apply(s2.max).is_close_to(d.max));
817        assert!(tr2.apply(s2.min).is_close_to(Point::new(3.0, 10.0)));
818
819        let tr3 = Transform::fit_bbox(s1, d, Align::Mid);
820        assert!(
821            tr3.apply((s1.min + s1.max) / 2.0)
822                .is_close_to((d.min + d.max) / 2.0)
823        );
824        assert!(tr3.apply(s1.min).is_close_to(Point::new(5.5, 5.0)));
825        assert!(tr3.apply(s1.max).is_close_to(Point::new(10.5, 15.0)));
826
827        let tr4 = Transform::fit_bbox(s2, d, Align::Mid);
828        assert!(
829            tr4.apply((s2.min + s2.max) / 2.0)
830                .is_close_to((d.min + d.max) / 2.0)
831        );
832        assert!(tr4.apply(s2.min).is_close_to(Point::new(3.0, 7.5)));
833        assert!(tr4.apply(s2.max).is_close_to(Point::new(13.0, 12.5)));
834
835        Ok(())
836    }
837
838    #[cfg(feature = "serde")]
839    #[test]
840    fn test_bbox_serde() -> Result<(), Error> {
841        let expected = BBox::new((1.0, 2.0), (4.0, 6.0));
842        let result: BBox = serde_json::from_str(&serde_json::to_string(&expected)?)?;
843        assert_eq!(result, expected);
844        let result: BBox = serde_json::from_str("[1, 2, 3, 4]")?;
845        assert_eq!(result, expected);
846
847        assert_eq!(expected, expected.to_string().parse()?);
848        Ok(())
849    }
850
851    #[test]
852    fn test_scalar_format() -> Result<(), Error> {
853        let value: Scalar = 0.1234567;
854        assert_eq!(format!("{}", ScalarFormat(value)), "0.1235".to_owned());
855        assert_eq!(format!("{:#}", ScalarFormat(value)), "0.1235".to_owned());
856        assert_eq!(format!("{:.3}", ScalarFormat(value)), "0.123".to_owned());
857
858        let value: Scalar = 12.3001;
859        assert_eq!(format!("{}", ScalarFormat(value)), "12.30".to_owned());
860        assert_eq!(format!("{:#}", ScalarFormat(value)), "12.3".to_owned());
861        assert_eq!(format!("{:.3}", ScalarFormat(value)), "12.3".to_owned());
862
863        let value: Scalar = 12300.0;
864        assert_eq!(format!("{}", ScalarFormat(value)), "12300".to_owned());
865        assert_eq!(format!("{:#}", ScalarFormat(value)), "12300".to_owned());
866        assert_eq!(format!("{:.3}", ScalarFormat(value)), "12300".to_owned());
867
868        let value: Scalar = 0.0;
869        assert_eq!(format!("{}", ScalarFormat(value)), "0".to_owned());
870        assert_eq!(format!("{:#}", ScalarFormat(value)), "0".to_owned());
871        assert_eq!(format!("{:.3}", ScalarFormat(value)), "0".to_owned());
872
873        Ok(())
874    }
875}