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};
24#[cfg(feature = "malloc_size_of")]
25use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
26use num_traits::NumCast;
27#[cfg(feature = "serde")]
28use serde::{Deserialize, Serialize};
29
30#[repr(C)]
50#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
51#[cfg_attr(
52 feature = "serde",
53 serde(bound(
54 serialize = "T: serde::Serialize",
55 deserialize = "T: serde::Deserialize<'de>"
56 ))
57)]
58pub struct Scale<T, Src, Dst>(pub T, #[doc(hidden)] pub PhantomData<(Src, Dst)>);
59
60impl<T, Src, Dst> Scale<T, Src, Dst> {
61 #[inline]
62 pub const fn new(x: T) -> Self {
63 Scale(x, PhantomData)
64 }
65
66 #[inline]
68 pub fn identity() -> Self
69 where
70 T: One,
71 {
72 Scale::new(T::one())
73 }
74
75 #[inline]
89 pub fn transform_point(self, point: Point2D<T, Src>) -> Point2D<T::Output, Dst>
90 where
91 T: Copy + Mul,
92 {
93 Point2D::new(point.x * self.0, point.y * self.0)
94 }
95
96 #[inline]
98 pub fn transform_point3d(self, point: Point3D<T, Src>) -> Point3D<T::Output, Dst>
99 where
100 T: Copy + Mul,
101 {
102 Point3D::new(point.x * self.0, point.y * self.0, point.z * self.0)
103 }
104
105 #[inline]
119 pub fn transform_vector(self, vec: Vector2D<T, Src>) -> Vector2D<T::Output, Dst>
120 where
121 T: Copy + Mul,
122 {
123 Vector2D::new(vec.x * self.0, vec.y * self.0)
124 }
125
126 #[inline]
140 pub fn transform_size(self, size: Size2D<T, Src>) -> Size2D<T::Output, Dst>
141 where
142 T: Copy + Mul,
143 {
144 Size2D::new(size.width * self.0, size.height * self.0)
145 }
146
147 #[inline]
161 pub fn transform_rect(self, rect: &Rect<T, Src>) -> Rect<T::Output, Dst>
162 where
163 T: Copy + Mul,
164 {
165 Rect::new(
166 self.transform_point(rect.origin),
167 self.transform_size(rect.size),
168 )
169 }
170
171 #[inline]
173 pub fn transform_box2d(self, b: &Box2D<T, Src>) -> Box2D<T::Output, Dst>
174 where
175 T: Copy + Mul,
176 {
177 Box2D {
178 min: self.transform_point(b.min),
179 max: self.transform_point(b.max),
180 }
181 }
182
183 #[inline]
185 pub fn transform_box3d(self, b: &Box3D<T, Src>) -> Box3D<T::Output, Dst>
186 where
187 T: Copy + Mul,
188 {
189 Box3D {
190 min: self.transform_point3d(b.min),
191 max: self.transform_point3d(b.max),
192 }
193 }
194
195 #[inline]
213 pub fn is_identity(self) -> bool
214 where
215 T: PartialEq + One,
216 {
217 self.0 == T::one()
218 }
219
220 #[inline]
222 pub fn get(self) -> T {
223 self.0
224 }
225
226 pub fn inverse(self) -> Scale<T::Output, Dst, Src>
240 where
241 T: One + Div,
242 {
243 let one: T = One::one();
244 Scale::new(one / self.0)
245 }
246
247 #[inline]
249 pub fn with_source<NewSrc>(self) -> Scale<T, NewSrc, Dst> {
250 Scale::new(self.0)
251 }
252
253 #[inline]
255 pub fn with_destination<NewDst>(self) -> Scale<T, Src, NewDst> {
256 Scale::new(self.0)
257 }
258}
259
260impl<T: PartialOrd, Src, Dst> Scale<T, Src, Dst> {
261 #[inline]
262 pub fn min(self, other: Self) -> Self {
263 Self::new(min(self.0, other.0))
264 }
265
266 #[inline]
267 pub fn max(self, other: Self) -> Self {
268 Self::new(max(self.0, other.0))
269 }
270
271 #[inline]
276 pub fn clamp(self, start: Self, end: Self) -> Self
277 where
278 T: Copy,
279 {
280 self.max(start).min(end)
281 }
282}
283
284impl<T: NumCast, Src, Dst> Scale<T, Src, Dst> {
285 #[inline]
313 pub fn cast<NewT: NumCast>(self) -> Scale<NewT, Src, Dst> {
314 self.try_cast().unwrap()
315 }
316
317 pub fn try_cast<NewT: NumCast>(self) -> Option<Scale<NewT, Src, Dst>> {
337 NumCast::from(self.0).map(Scale::new)
338 }
339}
340
341#[cfg(feature = "arbitrary")]
342impl<'a, T, Src, Dst> arbitrary::Arbitrary<'a> for Scale<T, Src, Dst>
343where
344 T: arbitrary::Arbitrary<'a>,
345{
346 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
347 Ok(Scale::new(arbitrary::Arbitrary::arbitrary(u)?))
348 }
349}
350
351#[cfg(feature = "bytemuck")]
352unsafe impl<T: Zeroable, Src, Dst> Zeroable for Scale<T, Src, Dst> {}
353
354#[cfg(feature = "bytemuck")]
355unsafe impl<T: Pod, Src: 'static, Dst: 'static> Pod for Scale<T, Src, Dst> {}
356
357#[cfg(feature = "malloc_size_of")]
358impl<T: MallocSizeOf, Src, Dst> MallocSizeOf for Scale<T, Src, Dst> {
359 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
360 self.0.size_of(ops)
361 }
362}
363
364impl<T: Mul, A, B, C> Mul<Scale<T, B, C>> for Scale<T, A, B> {
367 type Output = Scale<T::Output, A, C>;
368
369 #[inline]
370 fn mul(self, other: Scale<T, B, C>) -> Self::Output {
371 Scale::new(self.0 * other.0)
372 }
373}
374
375impl<T: Add, Src, Dst> Add for Scale<T, Src, Dst> {
377 type Output = Scale<T::Output, Src, Dst>;
378
379 #[inline]
380 fn add(self, other: Scale<T, Src, Dst>) -> Self::Output {
381 Scale::new(self.0 + other.0)
382 }
383}
384
385impl<T: Sub, Src, Dst> Sub for Scale<T, Src, Dst> {
387 type Output = Scale<T::Output, Src, Dst>;
388
389 #[inline]
390 fn sub(self, other: Scale<T, Src, Dst>) -> Self::Output {
391 Scale::new(self.0 - other.0)
392 }
393}
394
395impl<T: PartialEq, Src, Dst> PartialEq for Scale<T, Src, Dst> {
399 fn eq(&self, other: &Scale<T, Src, Dst>) -> bool {
400 self.0 == other.0
401 }
402}
403
404impl<T: Eq, Src, Dst> Eq for Scale<T, Src, Dst> {}
405
406impl<T: PartialOrd, Src, Dst> PartialOrd for Scale<T, Src, Dst> {
407 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
408 self.0.partial_cmp(&other.0)
409 }
410}
411
412impl<T: Ord, Src, Dst> Ord for Scale<T, Src, Dst> {
413 fn cmp(&self, other: &Self) -> Ordering {
414 self.0.cmp(&other.0)
415 }
416}
417
418impl<T: Clone, Src, Dst> Clone for Scale<T, Src, Dst> {
419 fn clone(&self) -> Scale<T, Src, Dst> {
420 Scale::new(self.0.clone())
421 }
422}
423
424impl<T: Copy, Src, Dst> Copy for Scale<T, Src, Dst> {}
425
426impl<T: fmt::Debug, Src, Dst> fmt::Debug for Scale<T, Src, Dst> {
427 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
428 self.0.fmt(f)
429 }
430}
431
432impl<T: Default, Src, Dst> Default for Scale<T, Src, Dst> {
433 fn default() -> Self {
434 Self::new(T::default())
435 }
436}
437
438impl<T: Hash, Src, Dst> Hash for Scale<T, Src, Dst> {
439 fn hash<H: Hasher>(&self, state: &mut H) {
440 self.0.hash(state);
441 }
442}
443
444impl<T: One, Src, Dst> One for Scale<T, Src, Dst> {
445 #[inline]
446 fn one() -> Self {
447 Scale::new(T::one())
448 }
449}
450
451#[cfg(test)]
452mod tests {
453 use super::Scale;
454
455 enum Inch {}
456 enum Cm {}
457 enum Mm {}
458
459 #[test]
460 fn test_scale() {
461 let mm_per_inch: Scale<f32, Inch, Mm> = Scale::new(25.4);
462 let cm_per_mm: Scale<f32, Mm, Cm> = Scale::new(0.1);
463
464 let mm_per_cm: Scale<f32, Cm, Mm> = cm_per_mm.inverse();
465 assert_eq!(mm_per_cm.get(), 10.0);
466
467 let one: Scale<f32, Mm, Mm> = cm_per_mm * mm_per_cm;
468 assert_eq!(one.get(), 1.0);
469
470 let one: Scale<f32, Cm, Cm> = mm_per_cm * cm_per_mm;
471 assert_eq!(one.get(), 1.0);
472
473 let cm_per_inch: Scale<f32, Inch, Cm> = mm_per_inch * cm_per_mm;
474 assert_eq!(cm_per_inch, Scale::new(2.54));
478
479 let a: Scale<isize, Inch, Inch> = Scale::new(2);
480 let b: Scale<isize, Inch, Inch> = Scale::new(3);
481 assert_ne!(a, b);
482 assert_eq!(a, a.clone());
483 assert_eq!(a.clone() + b.clone(), Scale::new(5));
484 assert_eq!(a - b, Scale::new(-1));
485
486 assert_eq!(Scale::identity().clamp(a, b), a);
488 assert_eq!(Scale::new(5).clamp(a, b), b);
489 let a = Scale::<f32, Inch, Inch>::new(2.0);
490 let b = Scale::<f32, Inch, Inch>::new(3.0);
491 let c = Scale::<f32, Inch, Inch>::new(2.5);
492 assert_eq!(c.clamp(a, b), c);
493 }
494}