tiny_skia_path/
scalar.rs

1// Copyright 2006 The Android Open Source Project
2// Copyright 2020 Yevhenii Reizner
3//
4// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6
7use crate::floating_point::f32_as_2s_compliment;
8
9#[allow(missing_docs)]
10pub const SCALAR_MAX: f32 = 3.402823466e+38;
11#[allow(missing_docs)]
12pub const SCALAR_NEARLY_ZERO: f32 = 1.0 / (1 << 12) as f32;
13#[allow(missing_docs)]
14pub const SCALAR_ROOT_2_OVER_2: f32 = 0.707106781;
15
16/// Float number extension methods.
17///
18/// Mainly for internal use. Do not rely on it!
19#[allow(missing_docs)]
20pub trait Scalar {
21    fn half(self) -> Self;
22    fn ave(self, other: Self) -> Self;
23    fn sqr(self) -> Self;
24    fn invert(self) -> Self;
25    fn bound(self, min: Self, max: Self) -> Self;
26    fn is_nearly_equal(self, other: Self) -> bool;
27    fn is_nearly_zero(self) -> bool;
28    fn is_nearly_zero_within_tolerance(self, tolerance: Self) -> bool;
29    fn almost_dequal_ulps(self, other: Self) -> bool;
30}
31
32impl Scalar for f32 {
33    fn half(self) -> f32 {
34        self * 0.5
35    }
36
37    fn ave(self, other: Self) -> f32 {
38        (self + other) * 0.5
39    }
40
41    fn sqr(self) -> f32 {
42        self * self
43    }
44
45    fn invert(self) -> f32 {
46        1.0 / self
47    }
48
49    // Works just like SkTPin, returning `max` for NaN/inf
50    /// A non-panicking clamp.
51    fn bound(self, min: Self, max: Self) -> Self {
52        max.min(self).max(min)
53    }
54
55    fn is_nearly_equal(self, other: Self) -> bool {
56        (self - other).abs() <= SCALAR_NEARLY_ZERO
57    }
58
59    fn is_nearly_zero(self) -> bool {
60        self.is_nearly_zero_within_tolerance(SCALAR_NEARLY_ZERO)
61    }
62
63    fn is_nearly_zero_within_tolerance(self, tolerance: Self) -> bool {
64        debug_assert!(tolerance >= 0.0);
65        self.abs() <= tolerance
66    }
67
68    // From SkPathOpsTypes.
69    fn almost_dequal_ulps(self, other: Self) -> bool {
70        const ULPS_EPSILON: i32 = 16;
71        let a_bits = f32_as_2s_compliment(self);
72        let b_bits = f32_as_2s_compliment(other);
73        // Find the difference in ULPs.
74        a_bits < b_bits + ULPS_EPSILON && b_bits < a_bits + ULPS_EPSILON
75    }
76}
77
78#[allow(missing_docs)]
79#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
80pub trait NoStdFloat {
81    fn trunc(self) -> Self;
82    fn sqrt(self) -> Self;
83    fn abs(self) -> Self;
84    fn sin(self) -> Self;
85    fn cos(self) -> Self;
86    fn ceil(self) -> Self;
87    fn floor(self) -> Self;
88    fn round(self) -> Self;
89    fn powf(self, y: Self) -> Self;
90    fn acos(self) -> Self;
91}
92
93#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
94impl NoStdFloat for f32 {
95    fn trunc(self) -> Self {
96        libm::truncf(self)
97    }
98    fn sqrt(self) -> Self {
99        libm::sqrtf(self)
100    }
101    fn abs(self) -> Self {
102        libm::fabsf(self)
103    }
104    fn sin(self) -> Self {
105        libm::sinf(self)
106    }
107    fn cos(self) -> Self {
108        libm::cosf(self)
109    }
110    fn ceil(self) -> Self {
111        libm::ceilf(self)
112    }
113    fn floor(self) -> Self {
114        libm::floorf(self)
115    }
116    fn round(self) -> Self {
117        libm::roundf(self)
118    }
119    fn powf(self, y: Self) -> Self {
120        libm::powf(self, y)
121    }
122    fn acos(self) -> Self {
123        libm::acosf(self)
124    }
125}
126
127#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
128impl NoStdFloat for f64 {
129    fn trunc(self) -> Self {
130        libm::trunc(self)
131    }
132    fn sqrt(self) -> Self {
133        libm::sqrt(self)
134    }
135    fn abs(self) -> Self {
136        libm::fabs(self)
137    }
138    fn sin(self) -> Self {
139        libm::sin(self)
140    }
141    fn cos(self) -> Self {
142        libm::cos(self)
143    }
144    fn ceil(self) -> Self {
145        libm::ceil(self)
146    }
147    fn floor(self) -> Self {
148        libm::floor(self)
149    }
150    fn round(self) -> Self {
151        libm::round(self)
152    }
153    fn powf(self, y: Self) -> Self {
154        libm::pow(self, y)
155    }
156    fn acos(self) -> Self {
157        libm::acos(self)
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    #[test]
166    fn bound() {
167        assert_eq!(f32::NAN.bound(0.0, 1.0), 1.0);
168        assert_eq!(f32::INFINITY.bound(0.0, 1.0), 1.0);
169        assert_eq!(f32::NEG_INFINITY.bound(0.0, 1.0), 0.0);
170        assert_eq!(f32::EPSILON.bound(0.0, 1.0), f32::EPSILON);
171        assert_eq!(0.5.bound(0.0, 1.0), 0.5);
172        assert_eq!((-1.0).bound(0.0, 1.0), 0.0);
173        assert_eq!(2.0.bound(0.0, 1.0), 1.0);
174    }
175}