zng_unit/
px_density.rs

1use std::fmt;
2
3/// Measurement of pixels in a screen or points in print.
4///
5/// Internally the density is stored as pixels per centimeter.
6#[derive(Clone, Copy, serde::Serialize, serde::Deserialize)]
7#[serde(transparent)]
8pub struct PxDensity(f32);
9impl fmt::Debug for PxDensity {
10    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
11        f.debug_struct("PxDensity")
12            .field(".ppi()", &self.ppi())
13            .field(".ppcm()", &self.ppcm())
14            .finish()
15    }
16}
17/// `"{}"` writes `{:.0}ppi`, `"{:#}` writes `{:.0}ppcm`.
18impl fmt::Display for PxDensity {
19    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20        if f.alternate() {
21            write!(f, "{:.0}ppcm", self.ppcm())
22        } else {
23            write!(f, "{:.0}ppi", self.ppi())
24        }
25    }
26}
27impl Default for PxDensity {
28    /// 96ppi.
29    fn default() -> Self {
30        PxDensity::new_ppi(96.0)
31    }
32}
33impl PxDensity {
34    /// Centimeters per inch.
35    pub const CM_TO_INCH: f32 = 2.54;
36
37    /// new from pixels per inch (PPI/DPI).
38    pub fn new_ppi(pixels_per_inch: f32) -> Self {
39        Self::new_ppcm(pixels_per_inch / Self::CM_TO_INCH)
40    }
41
42    /// New from pixels per meter (PPM/DPM).
43    pub fn new_ppm(pixels_per_meter: f32) -> Self {
44        Self::new_ppcm(pixels_per_meter / 100.0)
45    }
46
47    /// New from pixels per centimeter (PPCM/DPCM).
48    pub fn new_ppcm(pixels_per_centimeter: f32) -> Self {
49        Self(pixels_per_centimeter)
50    }
51
52    /// Density in pixels per inch (PPI/DPI).
53    pub fn ppi(self) -> f32 {
54        self.0 * Self::CM_TO_INCH
55    }
56
57    /// Density in pixels per meter (PPM/DPM).
58    pub fn ppm(self) -> f32 {
59        self.0 * 100.0
60    }
61
62    /// Density in pixels per centimeter (PPCM/DPCM).
63    pub fn ppcm(self) -> f32 {
64        self.0
65    }
66}
67impl PartialEq for PxDensity {
68    fn eq(&self, other: &Self) -> bool {
69        super::about_eq(self.0, other.0, 0.001)
70    }
71}
72impl Eq for PxDensity {}
73impl std::hash::Hash for PxDensity {
74    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
75        super::about_eq_hash(self.0, 0.001, state)
76    }
77}
78impl Ord for PxDensity {
79    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
80        super::about_eq_ord(self.0, other.0, 0.001)
81    }
82}
83impl PartialOrd for PxDensity {
84    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
85        Some(self.cmp(other))
86    }
87}
88/// Parses `"##"`, `"##unit"` and `"##.unit()"` where `##` is a `f32` and `unit` is `ppi`, `dpi` or `ppcm`. When
89/// no unit is given assumes DPI.
90impl std::str::FromStr for PxDensity {
91    type Err = std::num::ParseFloatError;
92
93    fn from_str(s: &str) -> Result<Self, Self::Err> {
94        for suffix in ["ppi", "dpi", ".ppi()", ".dpi()"] {
95            if let Some(s) = s.strip_suffix(suffix) {
96                return Ok(PxDensity::new_ppi(s.trim_end().parse()?));
97            }
98        }
99        for suffix in ["ppcm", ".ppcm()"] {
100            if let Some(s) = s.strip_suffix(suffix) {
101                return Ok(PxDensity::new_ppcm(s.parse()?));
102            }
103        }
104
105        Ok(PxDensity::new_ppi(s.parse()?))
106    }
107}
108
109/// Extension methods for initializing pixel density units.
110///
111/// # Examples
112///
113/// ```
114/// # use zng_unit::*;
115/// #
116/// let p: PxDensity = 96.ppi();
117///
118/// println!("ppi: {p}");
119/// println!("ppcm: {p:#}");
120/// ```
121pub trait PxDensityUnits {
122    /// Pixels-per-inch.
123    fn ppi(self) -> PxDensity;
124    /// Same as [`ppi`].
125    ///
126    /// [`ppi`]: PxDensityUnits::ppi
127    fn dpi(self) -> PxDensity
128    where
129        Self: Sized,
130    {
131        self.ppi()
132    }
133
134    /// Pixels-per-centimeter.
135    fn ppcm(self) -> PxDensity;
136}
137impl PxDensityUnits for u32 {
138    fn ppi(self) -> PxDensity {
139        PxDensity::new_ppi(self as f32)
140    }
141
142    fn ppcm(self) -> PxDensity {
143        PxDensity::new_ppcm(self as f32)
144    }
145}
146impl PxDensityUnits for f32 {
147    fn ppi(self) -> PxDensity {
148        PxDensity::new_ppi(self)
149    }
150
151    fn ppcm(self) -> PxDensity {
152        PxDensity::new_ppcm(self)
153    }
154}
155
156/// Pixel density value that can differ between dimensions.
157pub type PxDensity2d = euclid::Size2D<PxDensity, PxDensity>;