1use crate::num::One;
12
13use crate::approxord::{max, min};
14use crate::{Box2D, Box3D, Point2D, Point3D, Rect, Size2D, Vector2D};
15
16use core::cmp::Ordering;
17use core::fmt;
18use core::hash::{Hash, Hasher};
19use core::marker::PhantomData;
20use core::ops::{Add, Div, Mul, Sub};
21
22#[cfg(feature = "bytemuck")]
23use bytemuck::{Pod, Zeroable};
24use num_traits::NumCast;
25#[cfg(feature = "serde")]
26use serde::{Deserialize, Serialize};
27
28#[repr(C)]
48#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
49#[cfg_attr(
50 feature = "serde",
51 serde(bound(
52 serialize = "T: serde::Serialize",
53 deserialize = "T: serde::Deserialize<'de>"
54 ))
55)]
56pub struct Scale<T, Src, Dst>(pub T, #[doc(hidden)] pub PhantomData<(Src, Dst)>);
57
58impl<T, Src, Dst> Scale<T, Src, Dst> {
59 #[inline]
60 pub const fn new(x: T) -> Self {
61 Scale(x, PhantomData)
62 }
63
64 #[inline]
66 pub fn identity() -> Self
67 where
68 T: One,
69 {
70 Scale::new(T::one())
71 }
72
73 #[inline]
87 pub fn transform_point(self, point: Point2D<T, Src>) -> Point2D<T::Output, Dst>
88 where
89 T: Copy + Mul,
90 {
91 Point2D::new(point.x * self.0, point.y * self.0)
92 }
93
94 #[inline]
96 pub fn transform_point3d(self, point: Point3D<T, Src>) -> Point3D<T::Output, Dst>
97 where
98 T: Copy + Mul,
99 {
100 Point3D::new(point.x * self.0, point.y * self.0, point.z * self.0)
101 }
102
103 #[inline]
117 pub fn transform_vector(self, vec: Vector2D<T, Src>) -> Vector2D<T::Output, Dst>
118 where
119 T: Copy + Mul,
120 {
121 Vector2D::new(vec.x * self.0, vec.y * self.0)
122 }
123
124 #[inline]
138 pub fn transform_size(self, size: Size2D<T, Src>) -> Size2D<T::Output, Dst>
139 where
140 T: Copy + Mul,
141 {
142 Size2D::new(size.width * self.0, size.height * self.0)
143 }
144
145 #[inline]
159 pub fn transform_rect(self, rect: &Rect<T, Src>) -> Rect<T::Output, Dst>
160 where
161 T: Copy + Mul,
162 {
163 Rect::new(
164 self.transform_point(rect.origin),
165 self.transform_size(rect.size),
166 )
167 }
168
169 #[inline]
171 pub fn transform_box2d(self, b: &Box2D<T, Src>) -> Box2D<T::Output, Dst>
172 where
173 T: Copy + Mul,
174 {
175 Box2D {
176 min: self.transform_point(b.min),
177 max: self.transform_point(b.max),
178 }
179 }
180
181 #[inline]
183 pub fn transform_box3d(self, b: &Box3D<T, Src>) -> Box3D<T::Output, Dst>
184 where
185 T: Copy + Mul,
186 {
187 Box3D {
188 min: self.transform_point3d(b.min),
189 max: self.transform_point3d(b.max),
190 }
191 }
192
193 #[inline]
211 pub fn is_identity(self) -> bool
212 where
213 T: PartialEq + One,
214 {
215 self.0 == T::one()
216 }
217
218 #[inline]
220 pub fn get(self) -> T {
221 self.0
222 }
223
224 pub fn inverse(self) -> Scale<T::Output, Dst, Src>
238 where
239 T: One + Div,
240 {
241 let one: T = One::one();
242 Scale::new(one / self.0)
243 }
244}
245
246impl<T: PartialOrd, Src, Dst> Scale<T, Src, Dst> {
247 #[inline]
248 pub fn min(self, other: Self) -> Self {
249 Self::new(min(self.0, other.0))
250 }
251
252 #[inline]
253 pub fn max(self, other: Self) -> Self {
254 Self::new(max(self.0, other.0))
255 }
256
257 #[inline]
262 pub fn clamp(self, start: Self, end: Self) -> Self
263 where
264 T: Copy,
265 {
266 self.max(start).min(end)
267 }
268}
269
270impl<T: NumCast, Src, Dst> Scale<T, Src, Dst> {
271 #[inline]
299 pub fn cast<NewT: NumCast>(self) -> Scale<NewT, Src, Dst> {
300 self.try_cast().unwrap()
301 }
302
303 pub fn try_cast<NewT: NumCast>(self) -> Option<Scale<NewT, Src, Dst>> {
323 NumCast::from(self.0).map(Scale::new)
324 }
325}
326
327#[cfg(feature = "arbitrary")]
328impl<'a, T, Src, Dst> arbitrary::Arbitrary<'a> for Scale<T, Src, Dst>
329where
330 T: arbitrary::Arbitrary<'a>,
331{
332 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
333 Ok(Scale::new(arbitrary::Arbitrary::arbitrary(u)?))
334 }
335}
336
337#[cfg(feature = "bytemuck")]
338unsafe impl<T: Zeroable, Src, Dst> Zeroable for Scale<T, Src, Dst> {}
339
340#[cfg(feature = "bytemuck")]
341unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for Scale<T, Src, Dst> {}
342
343impl<T: Mul, A, B, C> Mul<Scale<T, B, C>> for Scale<T, A, B> {
346 type Output = Scale<T::Output, A, C>;
347
348 #[inline]
349 fn mul(self, other: Scale<T, B, C>) -> Self::Output {
350 Scale::new(self.0 * other.0)
351 }
352}
353
354impl<T: Add, Src, Dst> Add for Scale<T, Src, Dst> {
356 type Output = Scale<T::Output, Src, Dst>;
357
358 #[inline]
359 fn add(self, other: Scale<T, Src, Dst>) -> Self::Output {
360 Scale::new(self.0 + other.0)
361 }
362}
363
364impl<T: Sub, Src, Dst> Sub for Scale<T, Src, Dst> {
366 type Output = Scale<T::Output, Src, Dst>;
367
368 #[inline]
369 fn sub(self, other: Scale<T, Src, Dst>) -> Self::Output {
370 Scale::new(self.0 - other.0)
371 }
372}
373
374impl<T: PartialEq, Src, Dst> PartialEq for Scale<T, Src, Dst> {
378 fn eq(&self, other: &Scale<T, Src, Dst>) -> bool {
379 self.0 == other.0
380 }
381}
382
383impl<T: Eq, Src, Dst> Eq for Scale<T, Src, Dst> {}
384
385impl<T: PartialOrd, Src, Dst> PartialOrd for Scale<T, Src, Dst> {
386 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
387 self.0.partial_cmp(&other.0)
388 }
389}
390
391impl<T: Ord, Src, Dst> Ord for Scale<T, Src, Dst> {
392 fn cmp(&self, other: &Self) -> Ordering {
393 self.0.cmp(&other.0)
394 }
395}
396
397impl<T: Clone, Src, Dst> Clone for Scale<T, Src, Dst> {
398 fn clone(&self) -> Scale<T, Src, Dst> {
399 Scale::new(self.0.clone())
400 }
401}
402
403impl<T: Copy, Src, Dst> Copy for Scale<T, Src, Dst> {}
404
405impl<T: fmt::Debug, Src, Dst> fmt::Debug for Scale<T, Src, Dst> {
406 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
407 self.0.fmt(f)
408 }
409}
410
411impl<T: Default, Src, Dst> Default for Scale<T, Src, Dst> {
412 fn default() -> Self {
413 Self::new(T::default())
414 }
415}
416
417impl<T: Hash, Src, Dst> Hash for Scale<T, Src, Dst> {
418 fn hash<H: Hasher>(&self, state: &mut H) {
419 self.0.hash(state)
420 }
421}
422
423impl<T: One, Src, Dst> One for Scale<T, Src, Dst> {
424 #[inline]
425 fn one() -> Self {
426 Scale::new(T::one())
427 }
428}
429
430#[cfg(test)]
431mod tests {
432 use super::Scale;
433
434 enum Inch {}
435 enum Cm {}
436 enum Mm {}
437
438 #[test]
439 fn test_scale() {
440 let mm_per_inch: Scale<f32, Inch, Mm> = Scale::new(25.4);
441 let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
442
443 let mm_per_cm: Scale<f32, Cm, Mm> = cm_per_mm.inverse();
444 assert_eq!(mm_per_cm.get(), 10.0);
445
446 let one: Scale<f32, Mm, Mm> = cm_per_mm * mm_per_cm;
447 assert_eq!(one.get(), 1.0);
448
449 let one: Scale<f32, Cm, Cm> = mm_per_cm * cm_per_mm;
450 assert_eq!(one.get(), 1.0);
451
452 let cm_per_inch: Scale<f32, Inch, Cm> = mm_per_inch * cm_per_mm;
453 assert_eq!(cm_per_inch, Scale::new(2.54));
457
458 let a: Scale<isize, Inch, Inch> = Scale::new(2);
459 let b: Scale<isize, Inch, Inch> = Scale::new(3);
460 assert_ne!(a, b);
461 assert_eq!(a, a.clone());
462 assert_eq!(a.clone() + b.clone(), Scale::new(5));
463 assert_eq!(a - b, Scale::new(-1));
464
465 assert_eq!(Scale::identity().clamp(a, b), a);
467 assert_eq!(Scale::new(5).clamp(a, b), b);
468 let a = Scale::<f32, Inch, Inch>::new(2.0);
469 let b = Scale::<f32, Inch, Inch>::new(3.0);
470 let c = Scale::<f32, Inch, Inch>::new(2.5);
471 assert_eq!(c.clamp(a, b), c);
472 }
473}