1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/// A size of something in either scene units or ui points.
///
/// Implementation:
/// * If positive, this is in scene units.
/// * If negative, this is in ui points.
///
/// Resolved on-the-fly in shader code. See shader/utils/size.wgsl
#[repr(C)]
#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Size(pub f32);

impl Size {
    /// Zero radius.
    pub const ZERO: Self = Self(0.0);

    /// Radius of length 1 in ui points.
    pub const ONE_UI_POINT: Self = Self(-1.0);

    /// Creates a new size in scene units.
    ///
    /// Values passed must be finite positive.
    #[inline]
    pub fn new_scene_units(size: f32) -> Self {
        debug_assert!(0.0 <= size, "Bad size: {size}");
        Self(size)
    }

    /// Creates a new size in ui point units.
    ///
    /// Values passed must be finite positive.
    #[inline]
    pub fn new_ui_points(size: f32) -> Self {
        debug_assert!(0.0 <= size, "Bad size: {size}");
        Self(-size)
    }

    /// Get the scene-size of this, if stored as a scene size.
    #[inline]
    pub fn scene_units(&self) -> Option<f32> {
        // Ensure negative zero is treated as a point size.
        self.0.is_sign_positive().then_some(self.0)
    }

    /// Get the point size of this, if stored as a point size.
    #[inline]
    pub fn ui_points(&self) -> Option<f32> {
        // Ensure negative zero is treated as a point size.
        self.0.is_sign_negative().then_some(-self.0)
    }
}

impl PartialEq for Size {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.0.is_nan() && other.0.is_nan() || self.0 == other.0
    }
}

impl std::ops::Mul<f32> for Size {
    type Output = Self;

    #[inline]
    fn mul(self, rhs: f32) -> Self::Output {
        debug_assert!(rhs.is_finite() && rhs >= 0.0);
        debug_assert!((self.0 * rhs).is_finite());
        Self(self.0 * rhs)
    }
}

impl std::ops::MulAssign<f32> for Size {
    #[inline]
    fn mul_assign(&mut self, rhs: f32) {
        debug_assert!(rhs.is_finite() && rhs >= 0.0);
        debug_assert!((self.0 * rhs).is_finite());
        self.0 *= rhs;
    }
}

/// Same as [`Size`] but stored with a f16 float.
#[repr(C)]
#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
pub struct SizeHalf(half::f16);

impl From<Size> for SizeHalf {
    #[inline]
    fn from(size: Size) -> Self {
        Self(half::f16::from_f32(size.0))
    }
}

#[cfg(test)]
mod tests {
    use crate::Size;

    #[test]
    fn scene_point_distinction() {
        let size = Size(1.0);
        assert_eq!(size.scene_units(), Some(1.0));
        assert_eq!(size.ui_points(), None);

        let size = Size(-1.0);
        assert_eq!(size.scene_units(), None);
        assert_eq!(size.ui_points(), Some(1.0));

        let size = Size(f32::INFINITY);
        assert_eq!(size.scene_units(), Some(f32::INFINITY));
        assert_eq!(size.ui_points(), None);

        let size = Size(f32::NEG_INFINITY);
        assert_eq!(size.scene_units(), None);
        assert_eq!(size.ui_points(), Some(f32::INFINITY));

        let size = Size(0.0);
        assert_eq!(size.scene_units(), Some(0.0));
        assert_eq!(size.ui_points(), None);

        let size = Size(-0.0);
        assert_eq!(size.scene_units(), None);
        assert_eq!(size.ui_points(), Some(0.0));
    }
}