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
11pub type Scalar = f64;
13pub const EPSILON: f64 = f64::EPSILON;
15pub const EPSILON_SQRT: f64 = 1.490_116_119_384_765_6e-8;
17pub 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, 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 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#[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 #[inline]
133 pub const fn x(self) -> Scalar {
134 self.0[0]
135 }
136
137 #[inline]
139 pub const fn y(self) -> Scalar {
140 self.0[1]
141 }
142
143 pub fn length(self) -> Scalar {
145 let Self([x, y]) = self;
146 x.hypot(y)
147 }
148
149 pub fn dist(self, other: Self) -> Scalar {
151 (self - other).length()
152 }
153
154 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 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 pub fn normal(self) -> Point {
170 let Self([x, y]) = self;
171 Self([y, -x])
172 }
173
174 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 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 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 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 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#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
298pub enum Align {
299 Min,
301 Mid,
303 Max,
305}
306
307#[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 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 pub fn invert(&self) -> Option<Self> {
371 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 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 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 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 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 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 pub fn pre_concat(&self, other: Self) -> Self {
433 *self * other
434 }
435
436 pub fn post_concat(&self, other: Self) -> Self {
438 other * *self
439 }
440
441 pub fn line_to_line(src: Line, dst: Line) -> Option<Self> {
443 fn unit_y_to_line(line: Line) -> Transform {
445 let Line([p0, p1]) = line;
446 #[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 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 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 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 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 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#[derive(Clone, Copy, PartialEq, Hash)]
543pub struct BBox {
544 min: Point,
546 max: Point,
548}
549
550impl BBox {
551 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 #[inline]
565 pub fn min(&self) -> Point {
566 self.min
567 }
568
569 #[inline]
571 pub fn max(&self) -> Point {
572 self.max
573 }
574
575 #[inline]
577 pub fn x(&self) -> Scalar {
578 self.min.x()
579 }
580
581 #[inline]
583 pub fn y(&self) -> Scalar {
584 self.min.y()
585 }
586
587 #[inline]
589 pub fn width(&self) -> Scalar {
590 self.max.x() - self.min.x()
591 }
592
593 #[inline]
595 pub fn height(&self) -> Scalar {
596 self.max.y() - self.min.y()
597 }
598
599 pub fn diag(&self) -> Line {
601 Line::new(self.min, self.max)
602 }
603
604 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 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 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 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 pub fn unit_transform(&self) -> Transform {
663 Transform::new_translate(self.x(), self.y()).pre_scale(self.width(), self.height())
664 }
665
666 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
676fn 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 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 assert_approx_eq!(o0.direction().dot(o1.direction()), 0.0);
794 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}