1#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))]
63#![forbid(unsafe_code)]
64
65#[cfg(feature = "serde")]
66use serde::{Deserialize, Serialize};
67
68pub trait Pixel: Copy + Into<f64> {
69 fn from_f64(f: f64) -> Self;
70 fn cast<P: Pixel>(self) -> P {
71 P::from_f64(self.into())
72 }
73}
74
75impl Pixel for u8 {
76 fn from_f64(f: f64) -> Self {
77 f.round() as u8
78 }
79}
80impl Pixel for u16 {
81 fn from_f64(f: f64) -> Self {
82 f.round() as u16
83 }
84}
85impl Pixel for u32 {
86 fn from_f64(f: f64) -> Self {
87 f.round() as u32
88 }
89}
90impl Pixel for i8 {
91 fn from_f64(f: f64) -> Self {
92 f.round() as i8
93 }
94}
95impl Pixel for i16 {
96 fn from_f64(f: f64) -> Self {
97 f.round() as i16
98 }
99}
100impl Pixel for i32 {
101 fn from_f64(f: f64) -> Self {
102 f.round() as i32
103 }
104}
105impl Pixel for f32 {
106 fn from_f64(f: f64) -> Self {
107 f as f32
108 }
109}
110impl Pixel for f64 {
111 fn from_f64(f: f64) -> Self {
112 f
113 }
114}
115
116#[inline]
122pub fn validate_scale_factor(scale_factor: f64) -> bool {
123 scale_factor.is_sign_positive() && scale_factor.is_normal()
124}
125
126#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
128#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
129pub struct LogicalUnit<P>(pub P);
130
131impl<P> LogicalUnit<P> {
132 pub const MAX: LogicalUnit<f64> = LogicalUnit::new(f64::MAX);
134 pub const MIN: LogicalUnit<f64> = LogicalUnit::new(f64::MIN);
136 pub const ZERO: LogicalUnit<f64> = LogicalUnit::new(0.0);
138
139 #[inline]
140 pub const fn new(v: P) -> Self {
141 LogicalUnit(v)
142 }
143}
144
145impl<P: Pixel> LogicalUnit<P> {
146 #[inline]
147 pub fn from_physical<T: Into<PhysicalUnit<X>>, X: Pixel>(
148 physical: T,
149 scale_factor: f64,
150 ) -> Self {
151 physical.into().to_logical(scale_factor)
152 }
153
154 #[inline]
155 pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalUnit<X> {
156 assert!(validate_scale_factor(scale_factor));
157 PhysicalUnit::new(self.0.into() * scale_factor).cast()
158 }
159
160 #[inline]
161 pub fn cast<X: Pixel>(&self) -> LogicalUnit<X> {
162 LogicalUnit(self.0.cast())
163 }
164}
165
166impl<P: Pixel, X: Pixel> From<X> for LogicalUnit<P> {
167 fn from(v: X) -> LogicalUnit<P> {
168 LogicalUnit::new(v.cast())
169 }
170}
171
172impl<P: Pixel> From<LogicalUnit<P>> for u8 {
173 fn from(v: LogicalUnit<P>) -> u8 {
174 v.0.cast()
175 }
176}
177
178impl<P: Pixel> From<LogicalUnit<P>> for u16 {
179 fn from(v: LogicalUnit<P>) -> u16 {
180 v.0.cast()
181 }
182}
183
184impl<P: Pixel> From<LogicalUnit<P>> for u32 {
185 fn from(v: LogicalUnit<P>) -> u32 {
186 v.0.cast()
187 }
188}
189
190impl<P: Pixel> From<LogicalUnit<P>> for i8 {
191 fn from(v: LogicalUnit<P>) -> i8 {
192 v.0.cast()
193 }
194}
195
196impl<P: Pixel> From<LogicalUnit<P>> for i16 {
197 fn from(v: LogicalUnit<P>) -> i16 {
198 v.0.cast()
199 }
200}
201
202impl<P: Pixel> From<LogicalUnit<P>> for i32 {
203 fn from(v: LogicalUnit<P>) -> i32 {
204 v.0.cast()
205 }
206}
207
208impl<P: Pixel> From<LogicalUnit<P>> for f32 {
209 fn from(v: LogicalUnit<P>) -> f32 {
210 v.0.cast()
211 }
212}
213
214impl<P: Pixel> From<LogicalUnit<P>> for f64 {
215 fn from(v: LogicalUnit<P>) -> f64 {
216 v.0.cast()
217 }
218}
219
220#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
222#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
223pub struct PhysicalUnit<P>(pub P);
224
225impl<P> PhysicalUnit<P> {
226 pub const MAX: LogicalUnit<f64> = LogicalUnit::new(f64::MAX);
228 pub const MIN: LogicalUnit<f64> = LogicalUnit::new(f64::MIN);
230 pub const ZERO: LogicalUnit<f64> = LogicalUnit::new(0.0);
232
233 #[inline]
234 pub const fn new(v: P) -> Self {
235 PhysicalUnit(v)
236 }
237}
238
239impl<P: Pixel> PhysicalUnit<P> {
240 #[inline]
241 pub fn from_logical<T: Into<LogicalUnit<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self {
242 logical.into().to_physical(scale_factor)
243 }
244
245 #[inline]
246 pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalUnit<X> {
247 assert!(validate_scale_factor(scale_factor));
248 LogicalUnit::new(self.0.into() / scale_factor).cast()
249 }
250
251 #[inline]
252 pub fn cast<X: Pixel>(&self) -> PhysicalUnit<X> {
253 PhysicalUnit(self.0.cast())
254 }
255}
256
257impl<P: Pixel, X: Pixel> From<X> for PhysicalUnit<P> {
258 fn from(v: X) -> PhysicalUnit<P> {
259 PhysicalUnit::new(v.cast())
260 }
261}
262
263impl<P: Pixel> From<PhysicalUnit<P>> for u8 {
264 fn from(v: PhysicalUnit<P>) -> u8 {
265 v.0.cast()
266 }
267}
268
269impl<P: Pixel> From<PhysicalUnit<P>> for u16 {
270 fn from(v: PhysicalUnit<P>) -> u16 {
271 v.0.cast()
272 }
273}
274
275impl<P: Pixel> From<PhysicalUnit<P>> for u32 {
276 fn from(v: PhysicalUnit<P>) -> u32 {
277 v.0.cast()
278 }
279}
280
281impl<P: Pixel> From<PhysicalUnit<P>> for i8 {
282 fn from(v: PhysicalUnit<P>) -> i8 {
283 v.0.cast()
284 }
285}
286
287impl<P: Pixel> From<PhysicalUnit<P>> for i16 {
288 fn from(v: PhysicalUnit<P>) -> i16 {
289 v.0.cast()
290 }
291}
292
293impl<P: Pixel> From<PhysicalUnit<P>> for i32 {
294 fn from(v: PhysicalUnit<P>) -> i32 {
295 v.0.cast()
296 }
297}
298
299impl<P: Pixel> From<PhysicalUnit<P>> for f32 {
300 fn from(v: PhysicalUnit<P>) -> f32 {
301 v.0.cast()
302 }
303}
304
305impl<P: Pixel> From<PhysicalUnit<P>> for f64 {
306 fn from(v: PhysicalUnit<P>) -> f64 {
307 v.0.cast()
308 }
309}
310
311#[derive(Debug, Copy, Clone, PartialEq)]
313#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
314pub enum PixelUnit {
315 Physical(PhysicalUnit<i32>),
316 Logical(LogicalUnit<f64>),
317}
318
319impl PixelUnit {
320 pub const MAX: PixelUnit = PixelUnit::Logical(LogicalUnit::new(f64::MAX));
322 pub const MIN: PixelUnit = PixelUnit::Logical(LogicalUnit::new(f64::MIN));
324 pub const ZERO: PixelUnit = PixelUnit::Logical(LogicalUnit::new(0.0));
326
327 pub fn new<S: Into<PixelUnit>>(unit: S) -> PixelUnit {
328 unit.into()
329 }
330
331 pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalUnit<P> {
332 match *self {
333 PixelUnit::Physical(unit) => unit.to_logical(scale_factor),
334 PixelUnit::Logical(unit) => unit.cast(),
335 }
336 }
337
338 pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalUnit<P> {
339 match *self {
340 PixelUnit::Physical(unit) => unit.cast(),
341 PixelUnit::Logical(unit) => unit.to_physical(scale_factor),
342 }
343 }
344}
345
346impl<P: Pixel> From<PhysicalUnit<P>> for PixelUnit {
347 #[inline]
348 fn from(unit: PhysicalUnit<P>) -> PixelUnit {
349 PixelUnit::Physical(unit.cast())
350 }
351}
352
353impl<P: Pixel> From<LogicalUnit<P>> for PixelUnit {
354 #[inline]
355 fn from(unit: LogicalUnit<P>) -> PixelUnit {
356 PixelUnit::Logical(unit.cast())
357 }
358}
359
360#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
366#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
367pub struct LogicalPosition<P> {
368 pub x: P,
369 pub y: P,
370}
371
372impl<P> LogicalPosition<P> {
373 #[inline]
374 pub const fn new(x: P, y: P) -> Self {
375 LogicalPosition { x, y }
376 }
377}
378
379impl<P: Pixel> LogicalPosition<P> {
380 #[inline]
381 pub fn from_physical<T: Into<PhysicalPosition<X>>, X: Pixel>(
382 physical: T,
383 scale_factor: f64,
384 ) -> Self {
385 physical.into().to_logical(scale_factor)
386 }
387
388 #[inline]
389 pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalPosition<X> {
390 assert!(validate_scale_factor(scale_factor));
391 let x = self.x.into() * scale_factor;
392 let y = self.y.into() * scale_factor;
393 PhysicalPosition::new(x, y).cast()
394 }
395
396 #[inline]
397 pub fn cast<X: Pixel>(&self) -> LogicalPosition<X> {
398 LogicalPosition { x: self.x.cast(), y: self.y.cast() }
399 }
400}
401
402impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalPosition<P> {
403 fn from((x, y): (X, X)) -> LogicalPosition<P> {
404 LogicalPosition::new(x.cast(), y.cast())
405 }
406}
407
408impl<P: Pixel, X: Pixel> From<LogicalPosition<P>> for (X, X) {
409 fn from(p: LogicalPosition<P>) -> (X, X) {
410 (p.x.cast(), p.y.cast())
411 }
412}
413
414impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalPosition<P> {
415 fn from([x, y]: [X; 2]) -> LogicalPosition<P> {
416 LogicalPosition::new(x.cast(), y.cast())
417 }
418}
419
420impl<P: Pixel, X: Pixel> From<LogicalPosition<P>> for [X; 2] {
421 fn from(p: LogicalPosition<P>) -> [X; 2] {
422 [p.x.cast(), p.y.cast()]
423 }
424}
425
426#[cfg(feature = "mint")]
427impl<P: Pixel> From<mint::Point2<P>> for LogicalPosition<P> {
428 fn from(p: mint::Point2<P>) -> Self {
429 Self::new(p.x, p.y)
430 }
431}
432
433#[cfg(feature = "mint")]
434impl<P: Pixel> From<LogicalPosition<P>> for mint::Point2<P> {
435 fn from(p: LogicalPosition<P>) -> Self {
436 mint::Point2 { x: p.x, y: p.y }
437 }
438}
439
440#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
442#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
443pub struct PhysicalPosition<P> {
444 pub x: P,
445 pub y: P,
446}
447
448impl<P> PhysicalPosition<P> {
449 #[inline]
450 pub const fn new(x: P, y: P) -> Self {
451 PhysicalPosition { x, y }
452 }
453}
454
455impl<P: Pixel> PhysicalPosition<P> {
456 #[inline]
457 pub fn from_logical<T: Into<LogicalPosition<X>>, X: Pixel>(
458 logical: T,
459 scale_factor: f64,
460 ) -> Self {
461 logical.into().to_physical(scale_factor)
462 }
463
464 #[inline]
465 pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalPosition<X> {
466 assert!(validate_scale_factor(scale_factor));
467 let x = self.x.into() / scale_factor;
468 let y = self.y.into() / scale_factor;
469 LogicalPosition::new(x, y).cast()
470 }
471
472 #[inline]
473 pub fn cast<X: Pixel>(&self) -> PhysicalPosition<X> {
474 PhysicalPosition { x: self.x.cast(), y: self.y.cast() }
475 }
476}
477
478impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalPosition<P> {
479 fn from((x, y): (X, X)) -> PhysicalPosition<P> {
480 PhysicalPosition::new(x.cast(), y.cast())
481 }
482}
483
484impl<P: Pixel, X: Pixel> From<PhysicalPosition<P>> for (X, X) {
485 fn from(p: PhysicalPosition<P>) -> (X, X) {
486 (p.x.cast(), p.y.cast())
487 }
488}
489
490impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalPosition<P> {
491 fn from([x, y]: [X; 2]) -> PhysicalPosition<P> {
492 PhysicalPosition::new(x.cast(), y.cast())
493 }
494}
495
496impl<P: Pixel, X: Pixel> From<PhysicalPosition<P>> for [X; 2] {
497 fn from(p: PhysicalPosition<P>) -> [X; 2] {
498 [p.x.cast(), p.y.cast()]
499 }
500}
501
502#[cfg(feature = "mint")]
503impl<P: Pixel> From<mint::Point2<P>> for PhysicalPosition<P> {
504 fn from(p: mint::Point2<P>) -> Self {
505 Self::new(p.x, p.y)
506 }
507}
508
509#[cfg(feature = "mint")]
510impl<P: Pixel> From<PhysicalPosition<P>> for mint::Point2<P> {
511 fn from(p: PhysicalPosition<P>) -> Self {
512 mint::Point2 { x: p.x, y: p.y }
513 }
514}
515
516#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
518#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
519pub struct LogicalSize<P> {
520 pub width: P,
521 pub height: P,
522}
523
524impl<P> LogicalSize<P> {
525 #[inline]
526 pub const fn new(width: P, height: P) -> Self {
527 LogicalSize { width, height }
528 }
529}
530
531impl<P: Pixel> LogicalSize<P> {
532 #[inline]
533 pub fn from_physical<T: Into<PhysicalSize<X>>, X: Pixel>(
534 physical: T,
535 scale_factor: f64,
536 ) -> Self {
537 physical.into().to_logical(scale_factor)
538 }
539
540 #[inline]
541 pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalSize<X> {
542 assert!(validate_scale_factor(scale_factor));
543 let width = self.width.into() * scale_factor;
544 let height = self.height.into() * scale_factor;
545 PhysicalSize::new(width, height).cast()
546 }
547
548 #[inline]
549 pub fn cast<X: Pixel>(&self) -> LogicalSize<X> {
550 LogicalSize { width: self.width.cast(), height: self.height.cast() }
551 }
552}
553
554impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalSize<P> {
555 fn from((x, y): (X, X)) -> LogicalSize<P> {
556 LogicalSize::new(x.cast(), y.cast())
557 }
558}
559
560impl<P: Pixel, X: Pixel> From<LogicalSize<P>> for (X, X) {
561 fn from(s: LogicalSize<P>) -> (X, X) {
562 (s.width.cast(), s.height.cast())
563 }
564}
565
566impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalSize<P> {
567 fn from([x, y]: [X; 2]) -> LogicalSize<P> {
568 LogicalSize::new(x.cast(), y.cast())
569 }
570}
571
572impl<P: Pixel, X: Pixel> From<LogicalSize<P>> for [X; 2] {
573 fn from(s: LogicalSize<P>) -> [X; 2] {
574 [s.width.cast(), s.height.cast()]
575 }
576}
577
578#[cfg(feature = "mint")]
579impl<P: Pixel> From<mint::Vector2<P>> for LogicalSize<P> {
580 fn from(v: mint::Vector2<P>) -> Self {
581 Self::new(v.x, v.y)
582 }
583}
584
585#[cfg(feature = "mint")]
586impl<P: Pixel> From<LogicalSize<P>> for mint::Vector2<P> {
587 fn from(s: LogicalSize<P>) -> Self {
588 mint::Vector2 { x: s.width, y: s.height }
589 }
590}
591
592#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
594#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
595pub struct PhysicalSize<P> {
596 pub width: P,
597 pub height: P,
598}
599
600impl<P> PhysicalSize<P> {
601 #[inline]
602 pub const fn new(width: P, height: P) -> Self {
603 PhysicalSize { width, height }
604 }
605}
606
607impl<P: Pixel> PhysicalSize<P> {
608 #[inline]
609 pub fn from_logical<T: Into<LogicalSize<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self {
610 logical.into().to_physical(scale_factor)
611 }
612
613 #[inline]
614 pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalSize<X> {
615 assert!(validate_scale_factor(scale_factor));
616 let width = self.width.into() / scale_factor;
617 let height = self.height.into() / scale_factor;
618 LogicalSize::new(width, height).cast()
619 }
620
621 #[inline]
622 pub fn cast<X: Pixel>(&self) -> PhysicalSize<X> {
623 PhysicalSize { width: self.width.cast(), height: self.height.cast() }
624 }
625}
626
627impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalSize<P> {
628 fn from((x, y): (X, X)) -> PhysicalSize<P> {
629 PhysicalSize::new(x.cast(), y.cast())
630 }
631}
632
633impl<P: Pixel, X: Pixel> From<PhysicalSize<P>> for (X, X) {
634 fn from(s: PhysicalSize<P>) -> (X, X) {
635 (s.width.cast(), s.height.cast())
636 }
637}
638
639impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalSize<P> {
640 fn from([x, y]: [X; 2]) -> PhysicalSize<P> {
641 PhysicalSize::new(x.cast(), y.cast())
642 }
643}
644
645impl<P: Pixel, X: Pixel> From<PhysicalSize<P>> for [X; 2] {
646 fn from(s: PhysicalSize<P>) -> [X; 2] {
647 [s.width.cast(), s.height.cast()]
648 }
649}
650
651#[cfg(feature = "mint")]
652impl<P: Pixel> From<mint::Vector2<P>> for PhysicalSize<P> {
653 fn from(v: mint::Vector2<P>) -> Self {
654 Self::new(v.x, v.y)
655 }
656}
657
658#[cfg(feature = "mint")]
659impl<P: Pixel> From<PhysicalSize<P>> for mint::Vector2<P> {
660 fn from(s: PhysicalSize<P>) -> Self {
661 mint::Vector2 { x: s.width, y: s.height }
662 }
663}
664
665#[derive(Debug, Copy, Clone, PartialEq)]
667#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
668pub enum Size {
669 Physical(PhysicalSize<u32>),
670 Logical(LogicalSize<f64>),
671}
672
673impl Size {
674 pub fn new<S: Into<Size>>(size: S) -> Size {
675 size.into()
676 }
677
678 pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalSize<P> {
679 match *self {
680 Size::Physical(size) => size.to_logical(scale_factor),
681 Size::Logical(size) => size.cast(),
682 }
683 }
684
685 pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalSize<P> {
686 match *self {
687 Size::Physical(size) => size.cast(),
688 Size::Logical(size) => size.to_physical(scale_factor),
689 }
690 }
691
692 pub fn clamp<S: Into<Size>>(input: S, min: S, max: S, scale_factor: f64) -> Size {
693 let (input, min, max) = (
694 input.into().to_physical::<f64>(scale_factor),
695 min.into().to_physical::<f64>(scale_factor),
696 max.into().to_physical::<f64>(scale_factor),
697 );
698
699 let width = input.width.clamp(min.width, max.width);
700 let height = input.height.clamp(min.height, max.height);
701
702 PhysicalSize::new(width, height).into()
703 }
704}
705
706impl<P: Pixel> From<PhysicalSize<P>> for Size {
707 #[inline]
708 fn from(size: PhysicalSize<P>) -> Size {
709 Size::Physical(size.cast())
710 }
711}
712
713impl<P: Pixel> From<LogicalSize<P>> for Size {
714 #[inline]
715 fn from(size: LogicalSize<P>) -> Size {
716 Size::Logical(size.cast())
717 }
718}
719
720#[derive(Debug, Copy, Clone, PartialEq)]
722#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
723pub enum Position {
724 Physical(PhysicalPosition<i32>),
725 Logical(LogicalPosition<f64>),
726}
727
728impl Position {
729 pub fn new<S: Into<Position>>(position: S) -> Position {
730 position.into()
731 }
732
733 pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalPosition<P> {
734 match *self {
735 Position::Physical(position) => position.to_logical(scale_factor),
736 Position::Logical(position) => position.cast(),
737 }
738 }
739
740 pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalPosition<P> {
741 match *self {
742 Position::Physical(position) => position.cast(),
743 Position::Logical(position) => position.to_physical(scale_factor),
744 }
745 }
746}
747
748impl<P: Pixel> From<PhysicalPosition<P>> for Position {
749 #[inline]
750 fn from(position: PhysicalPosition<P>) -> Position {
751 Position::Physical(position.cast())
752 }
753}
754
755impl<P: Pixel> From<LogicalPosition<P>> for Position {
756 #[inline]
757 fn from(position: LogicalPosition<P>) -> Position {
758 Position::Logical(position.cast())
759 }
760}
761
762#[cfg(test)]
763mod tests {
764 use std::collections::HashSet;
765
766 use super::*;
767
768 macro_rules! test_pixel_int_impl {
769 ($($name:ident => $ty:ty),*) => {$(
770 #[test]
771 fn $name() {
772 assert_eq!(
773 <$ty as Pixel>::from_f64(37.0),
774 37,
775 );
776 assert_eq!(
777 <$ty as Pixel>::from_f64(37.4),
778 37,
779 );
780 assert_eq!(
781 <$ty as Pixel>::from_f64(37.5),
782 38,
783 );
784 assert_eq!(
785 <$ty as Pixel>::from_f64(37.9),
786 38,
787 );
788
789 assert_eq!(
790 <$ty as Pixel>::cast::<u8>(37),
791 37,
792 );
793 assert_eq!(
794 <$ty as Pixel>::cast::<u16>(37),
795 37,
796 );
797 assert_eq!(
798 <$ty as Pixel>::cast::<u32>(37),
799 37,
800 );
801 assert_eq!(
802 <$ty as Pixel>::cast::<i8>(37),
803 37,
804 );
805 assert_eq!(
806 <$ty as Pixel>::cast::<i16>(37),
807 37,
808 );
809 assert_eq!(
810 <$ty as Pixel>::cast::<i32>(37),
811 37,
812 );
813 }
814 )*};
815 }
816
817 test_pixel_int_impl! {
818 test_pixel_int_u8 => u8,
819 test_pixel_int_u16 => u16,
820 test_pixel_int_u32 => u32,
821 test_pixel_int_i8 => i8,
822 test_pixel_int_i16 => i16
823 }
824
825 macro_rules! assert_approx_eq {
826 ($a:expr, $b:expr $(,)?) => {
827 assert!(($a - $b).abs() < 0.001, "{} is not approximately equal to {}", $a, $b);
828 };
829 }
830
831 macro_rules! test_pixel_float_impl {
832 ($($name:ident => $ty:ty),*) => {$(
833 #[test]
834 fn $name() {
835 assert_approx_eq!(
836 <$ty as Pixel>::from_f64(37.0),
837 37.0,
838 );
839 assert_approx_eq!(
840 <$ty as Pixel>::from_f64(37.4),
841 37.4,
842 );
843 assert_approx_eq!(
844 <$ty as Pixel>::from_f64(37.5),
845 37.5,
846 );
847 assert_approx_eq!(
848 <$ty as Pixel>::from_f64(37.9),
849 37.9,
850 );
851
852 assert_eq!(
853 <$ty as Pixel>::cast::<u8>(37.0),
854 37,
855 );
856 assert_eq!(
857 <$ty as Pixel>::cast::<u8>(37.4),
858 37,
859 );
860 assert_eq!(
861 <$ty as Pixel>::cast::<u8>(37.5),
862 38,
863 );
864
865 assert_eq!(
866 <$ty as Pixel>::cast::<u16>(37.0),
867 37,
868 );
869 assert_eq!(
870 <$ty as Pixel>::cast::<u16>(37.4),
871 37,
872 );
873 assert_eq!(
874 <$ty as Pixel>::cast::<u16>(37.5),
875 38,
876 );
877
878 assert_eq!(
879 <$ty as Pixel>::cast::<u32>(37.0),
880 37,
881 );
882 assert_eq!(
883 <$ty as Pixel>::cast::<u32>(37.4),
884 37,
885 );
886 assert_eq!(
887 <$ty as Pixel>::cast::<u32>(37.5),
888 38,
889 );
890
891 assert_eq!(
892 <$ty as Pixel>::cast::<i8>(37.0),
893 37,
894 );
895 assert_eq!(
896 <$ty as Pixel>::cast::<i8>(37.4),
897 37,
898 );
899 assert_eq!(
900 <$ty as Pixel>::cast::<i8>(37.5),
901 38,
902 );
903
904 assert_eq!(
905 <$ty as Pixel>::cast::<i16>(37.0),
906 37,
907 );
908 assert_eq!(
909 <$ty as Pixel>::cast::<i16>(37.4),
910 37,
911 );
912 assert_eq!(
913 <$ty as Pixel>::cast::<i16>(37.5),
914 38,
915 );
916 }
917 )*};
918}
919
920 test_pixel_float_impl! {
921 test_pixel_float_f32 => f32,
922 test_pixel_float_f64 => f64
923 }
924
925 #[test]
926 fn test_validate_scale_factor() {
927 assert!(validate_scale_factor(1.0));
928 assert!(validate_scale_factor(2.0));
929 assert!(validate_scale_factor(3.0));
930 assert!(validate_scale_factor(1.5));
931 assert!(validate_scale_factor(0.5));
932
933 assert!(!validate_scale_factor(0.0));
934 assert!(!validate_scale_factor(-1.0));
935 assert!(!validate_scale_factor(f64::INFINITY));
936 assert!(!validate_scale_factor(f64::NAN));
937 assert!(!validate_scale_factor(f64::NEG_INFINITY));
938 }
939
940 #[test]
941 fn test_logical_unity() {
942 let log_unit = LogicalUnit::new(1.0);
943 assert_eq!(log_unit.to_physical::<u32>(1.0), PhysicalUnit::new(1));
944 assert_eq!(log_unit.to_physical::<u32>(2.0), PhysicalUnit::new(2));
945 assert_eq!(log_unit.cast::<u32>(), LogicalUnit::new(1));
946 assert_eq!(log_unit, LogicalUnit::from_physical(PhysicalUnit::new(1.0), 1.0));
947 assert_eq!(log_unit, LogicalUnit::from_physical(PhysicalUnit::new(2.0), 2.0));
948 assert_eq!(LogicalUnit::from(2.0), LogicalUnit::new(2.0));
949
950 let x: f64 = log_unit.into();
951 assert_eq!(x, 1.0);
952 }
953
954 #[test]
955 fn test_physical_unit() {
956 assert_eq!(PhysicalUnit::from_logical(LogicalUnit::new(1.0), 1.0), PhysicalUnit::new(1));
957 assert_eq!(PhysicalUnit::from_logical(LogicalUnit::new(2.0), 0.5), PhysicalUnit::new(1));
958 assert_eq!(PhysicalUnit::from(2.0), PhysicalUnit::new(2.0,));
959 assert_eq!(PhysicalUnit::from(2.0), PhysicalUnit::new(2.0));
960
961 let x: f64 = PhysicalUnit::new(1).into();
962 assert_eq!(x, 1.0);
963 }
964
965 #[test]
966 fn test_logical_position() {
967 let log_pos = LogicalPosition::new(1.0, 2.0);
968 assert_eq!(log_pos.to_physical::<u32>(1.0), PhysicalPosition::new(1, 2));
969 assert_eq!(log_pos.to_physical::<u32>(2.0), PhysicalPosition::new(2, 4));
970 assert_eq!(log_pos.cast::<u32>(), LogicalPosition::new(1, 2));
971 assert_eq!(log_pos, LogicalPosition::from_physical(PhysicalPosition::new(1.0, 2.0), 1.0));
972 assert_eq!(log_pos, LogicalPosition::from_physical(PhysicalPosition::new(2.0, 4.0), 2.0));
973 assert_eq!(LogicalPosition::from((2.0, 2.0)), LogicalPosition::new(2.0, 2.0));
974 assert_eq!(LogicalPosition::from([2.0, 3.0]), LogicalPosition::new(2.0, 3.0));
975
976 let x: (f64, f64) = log_pos.into();
977 assert_eq!(x, (1.0, 2.0));
978 let x: [f64; 2] = log_pos.into();
979 assert_eq!(x, [1.0, 2.0]);
980 }
981
982 #[test]
983 fn test_physical_position() {
984 assert_eq!(
985 PhysicalPosition::from_logical(LogicalPosition::new(1.0, 2.0), 1.0),
986 PhysicalPosition::new(1, 2)
987 );
988 assert_eq!(
989 PhysicalPosition::from_logical(LogicalPosition::new(2.0, 4.0), 0.5),
990 PhysicalPosition::new(1, 2)
991 );
992 assert_eq!(PhysicalPosition::from((2.0, 2.0)), PhysicalPosition::new(2.0, 2.0));
993 assert_eq!(PhysicalPosition::from([2.0, 3.0]), PhysicalPosition::new(2.0, 3.0));
994
995 let x: (f64, f64) = PhysicalPosition::new(1, 2).into();
996 assert_eq!(x, (1.0, 2.0));
997 let x: [f64; 2] = PhysicalPosition::new(1, 2).into();
998 assert_eq!(x, [1.0, 2.0]);
999 }
1000
1001 #[test]
1002 fn test_logical_size() {
1003 let log_size = LogicalSize::new(1.0, 2.0);
1004 assert_eq!(log_size.to_physical::<u32>(1.0), PhysicalSize::new(1, 2));
1005 assert_eq!(log_size.to_physical::<u32>(2.0), PhysicalSize::new(2, 4));
1006 assert_eq!(log_size.cast::<u32>(), LogicalSize::new(1, 2));
1007 assert_eq!(log_size, LogicalSize::from_physical(PhysicalSize::new(1.0, 2.0), 1.0));
1008 assert_eq!(log_size, LogicalSize::from_physical(PhysicalSize::new(2.0, 4.0), 2.0));
1009 assert_eq!(LogicalSize::from((2.0, 2.0)), LogicalSize::new(2.0, 2.0));
1010 assert_eq!(LogicalSize::from([2.0, 3.0]), LogicalSize::new(2.0, 3.0));
1011
1012 let x: (f64, f64) = log_size.into();
1013 assert_eq!(x, (1.0, 2.0));
1014 let x: [f64; 2] = log_size.into();
1015 assert_eq!(x, [1.0, 2.0]);
1016 }
1017
1018 #[test]
1019 fn test_physical_size() {
1020 assert_eq!(
1021 PhysicalSize::from_logical(LogicalSize::new(1.0, 2.0), 1.0),
1022 PhysicalSize::new(1, 2)
1023 );
1024 assert_eq!(
1025 PhysicalSize::from_logical(LogicalSize::new(2.0, 4.0), 0.5),
1026 PhysicalSize::new(1, 2)
1027 );
1028 assert_eq!(PhysicalSize::from((2.0, 2.0)), PhysicalSize::new(2.0, 2.0));
1029 assert_eq!(PhysicalSize::from([2.0, 3.0]), PhysicalSize::new(2.0, 3.0));
1030
1031 let x: (f64, f64) = PhysicalSize::new(1, 2).into();
1032 assert_eq!(x, (1.0, 2.0));
1033 let x: [f64; 2] = PhysicalSize::new(1, 2).into();
1034 assert_eq!(x, [1.0, 2.0]);
1035 }
1036
1037 #[test]
1038 fn test_size() {
1039 assert_eq!(Size::new(PhysicalSize::new(1, 2)), Size::Physical(PhysicalSize::new(1, 2)));
1040 assert_eq!(
1041 Size::new(LogicalSize::new(1.0, 2.0)),
1042 Size::Logical(LogicalSize::new(1.0, 2.0))
1043 );
1044
1045 assert_eq!(
1046 Size::new(PhysicalSize::new(1, 2)).to_logical::<f64>(1.0),
1047 LogicalSize::new(1.0, 2.0)
1048 );
1049 assert_eq!(
1050 Size::new(PhysicalSize::new(1, 2)).to_logical::<f64>(2.0),
1051 LogicalSize::new(0.5, 1.0)
1052 );
1053 assert_eq!(
1054 Size::new(LogicalSize::new(1.0, 2.0)).to_logical::<f64>(1.0),
1055 LogicalSize::new(1.0, 2.0)
1056 );
1057
1058 assert_eq!(
1059 Size::new(PhysicalSize::new(1, 2)).to_physical::<u32>(1.0),
1060 PhysicalSize::new(1, 2)
1061 );
1062 assert_eq!(
1063 Size::new(PhysicalSize::new(1, 2)).to_physical::<u32>(2.0),
1064 PhysicalSize::new(1, 2)
1065 );
1066 assert_eq!(
1067 Size::new(LogicalSize::new(1.0, 2.0)).to_physical::<u32>(1.0),
1068 PhysicalSize::new(1, 2)
1069 );
1070 assert_eq!(
1071 Size::new(LogicalSize::new(1.0, 2.0)).to_physical::<u32>(2.0),
1072 PhysicalSize::new(2, 4)
1073 );
1074
1075 let small = Size::Physical((1, 2).into());
1076 let medium = Size::Logical((3, 4).into());
1077 let medium_physical = Size::new(medium.to_physical::<u32>(1.0));
1078 let large = Size::Physical((5, 6).into());
1079 assert_eq!(Size::clamp(medium, small, large, 1.0), medium_physical);
1080 assert_eq!(Size::clamp(small, medium, large, 1.0), medium_physical);
1081 assert_eq!(Size::clamp(large, small, medium, 1.0), medium_physical);
1082 }
1083
1084 #[test]
1085 fn test_position() {
1086 assert_eq!(
1087 Position::new(PhysicalPosition::new(1, 2)),
1088 Position::Physical(PhysicalPosition::new(1, 2))
1089 );
1090 assert_eq!(
1091 Position::new(LogicalPosition::new(1.0, 2.0)),
1092 Position::Logical(LogicalPosition::new(1.0, 2.0))
1093 );
1094
1095 assert_eq!(
1096 Position::new(PhysicalPosition::new(1, 2)).to_logical::<f64>(1.0),
1097 LogicalPosition::new(1.0, 2.0)
1098 );
1099 assert_eq!(
1100 Position::new(PhysicalPosition::new(1, 2)).to_logical::<f64>(2.0),
1101 LogicalPosition::new(0.5, 1.0)
1102 );
1103 assert_eq!(
1104 Position::new(LogicalPosition::new(1.0, 2.0)).to_logical::<f64>(1.0),
1105 LogicalPosition::new(1.0, 2.0)
1106 );
1107
1108 assert_eq!(
1109 Position::new(PhysicalPosition::new(1, 2)).to_physical::<u32>(1.0),
1110 PhysicalPosition::new(1, 2)
1111 );
1112 assert_eq!(
1113 Position::new(PhysicalPosition::new(1, 2)).to_physical::<u32>(2.0),
1114 PhysicalPosition::new(1, 2)
1115 );
1116 assert_eq!(
1117 Position::new(LogicalPosition::new(1.0, 2.0)).to_physical::<u32>(1.0),
1118 PhysicalPosition::new(1, 2)
1119 );
1120 assert_eq!(
1121 Position::new(LogicalPosition::new(1.0, 2.0)).to_physical::<u32>(2.0),
1122 PhysicalPosition::new(2, 4)
1123 );
1124 }
1125
1126 #[test]
1128 fn ensure_attrs_do_not_panic() {
1129 let _ = format!("{:?}", LogicalPosition::<u32>::default().clone());
1130 HashSet::new().insert(LogicalPosition::<u32>::default());
1131
1132 let _ = format!("{:?}", PhysicalPosition::<u32>::default().clone());
1133 HashSet::new().insert(PhysicalPosition::<u32>::default());
1134
1135 let _ = format!("{:?}", LogicalSize::<u32>::default().clone());
1136 HashSet::new().insert(LogicalSize::<u32>::default());
1137
1138 let _ = format!("{:?}", PhysicalSize::<u32>::default().clone());
1139 HashSet::new().insert(PhysicalSize::<u32>::default());
1140
1141 let _ = format!("{:?}", Size::Physical((1, 2).into()).clone());
1142 let _ = format!("{:?}", Position::Physical((1, 2).into()).clone());
1143 }
1144
1145 #[test]
1146 fn ensure_copy_trait() {
1147 fn is_copy<T: Copy>() {}
1148
1149 is_copy::<LogicalUnit<i32>>();
1150 is_copy::<PhysicalUnit<f64>>();
1151 is_copy::<PixelUnit>();
1152
1153 is_copy::<LogicalSize<i32>>();
1154 is_copy::<PhysicalSize<f64>>();
1155 is_copy::<Size>();
1156
1157 is_copy::<LogicalPosition<i32>>();
1158 is_copy::<PhysicalPosition<f64>>();
1159 is_copy::<Position>();
1160 }
1161
1162 #[test]
1163 fn ensure_partial_eq_trait() {
1164 fn is_partial_eq<T: PartialEq>() {}
1165
1166 is_partial_eq::<LogicalUnit<i32>>();
1167 is_partial_eq::<PhysicalUnit<f64>>();
1168 is_partial_eq::<PixelUnit>();
1169
1170 is_partial_eq::<LogicalSize<i32>>();
1171 is_partial_eq::<PhysicalSize<f64>>();
1172 is_partial_eq::<Size>();
1173
1174 is_partial_eq::<LogicalPosition<i32>>();
1175 is_partial_eq::<PhysicalPosition<f64>>();
1176 is_partial_eq::<Position>();
1177 }
1178}