Skip to main content

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_equal_within_tolerance(self, other: Self, tolerance: Self) -> bool;
28    fn is_nearly_zero(self) -> bool;
29    fn is_nearly_zero_within_tolerance(self, tolerance: Self) -> bool;
30    fn almost_dequal_ulps(self, other: Self) -> bool;
31}
32
33impl Scalar for f32 {
34    fn half(self) -> f32 {
35        self * 0.5
36    }
37
38    fn ave(self, other: Self) -> f32 {
39        (self + other) * 0.5
40    }
41
42    fn sqr(self) -> f32 {
43        self * self
44    }
45
46    fn invert(self) -> f32 {
47        1.0 / self
48    }
49
50    // Works just like SkTPin, returning `max` for NaN/inf
51    /// A non-panicking clamp.
52    fn bound(self, min: Self, max: Self) -> Self {
53        max.min(self).max(min)
54    }
55
56    fn is_nearly_equal(self, other: Self) -> bool {
57        (self - other).abs() <= SCALAR_NEARLY_ZERO
58    }
59
60    fn is_nearly_equal_within_tolerance(self, other: Self, tolerance: Self) -> bool {
61        (self - other).abs() <= tolerance
62    }
63
64    fn is_nearly_zero(self) -> bool {
65        self.is_nearly_zero_within_tolerance(SCALAR_NEARLY_ZERO)
66    }
67
68    fn is_nearly_zero_within_tolerance(self, tolerance: Self) -> bool {
69        debug_assert!(tolerance >= 0.0);
70        self.abs() <= tolerance
71    }
72
73    // From SkPathOpsTypes.
74    fn almost_dequal_ulps(self, other: Self) -> bool {
75        const ULPS_EPSILON: i32 = 16;
76        let a_bits = f32_as_2s_compliment(self);
77        let b_bits = f32_as_2s_compliment(other);
78        // Find the difference in ULPs.
79        a_bits < b_bits + ULPS_EPSILON && b_bits < a_bits + ULPS_EPSILON
80    }
81}
82
83#[allow(missing_docs)]
84#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
85pub trait NoStdFloat {
86    fn trunc(self) -> Self;
87    fn sqrt(self) -> Self;
88    fn abs(self) -> Self;
89    fn sin(self) -> Self;
90    fn cos(self) -> Self;
91    fn ceil(self) -> Self;
92    fn floor(self) -> Self;
93    fn round(self) -> Self;
94    fn powf(self, y: Self) -> Self;
95    fn acos(self) -> Self;
96}
97
98#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
99impl NoStdFloat for f32 {
100    fn trunc(self) -> Self {
101        libm::truncf(self)
102    }
103    fn sqrt(self) -> Self {
104        libm::sqrtf(self)
105    }
106    fn abs(self) -> Self {
107        libm::fabsf(self)
108    }
109    fn sin(self) -> Self {
110        libm::sinf(self)
111    }
112    fn cos(self) -> Self {
113        libm::cosf(self)
114    }
115    fn ceil(self) -> Self {
116        libm::ceilf(self)
117    }
118    fn floor(self) -> Self {
119        libm::floorf(self)
120    }
121    fn round(self) -> Self {
122        libm::roundf(self)
123    }
124    fn powf(self, y: Self) -> Self {
125        libm::powf(self, y)
126    }
127    fn acos(self) -> Self {
128        libm::acosf(self)
129    }
130}
131
132#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
133impl NoStdFloat for f64 {
134    fn trunc(self) -> Self {
135        libm::trunc(self)
136    }
137    fn sqrt(self) -> Self {
138        libm::sqrt(self)
139    }
140    fn abs(self) -> Self {
141        libm::fabs(self)
142    }
143    fn sin(self) -> Self {
144        libm::sin(self)
145    }
146    fn cos(self) -> Self {
147        libm::cos(self)
148    }
149    fn ceil(self) -> Self {
150        libm::ceil(self)
151    }
152    fn floor(self) -> Self {
153        libm::floor(self)
154    }
155    fn round(self) -> Self {
156        libm::round(self)
157    }
158    fn powf(self, y: Self) -> Self {
159        libm::pow(self, y)
160    }
161    fn acos(self) -> Self {
162        libm::acos(self)
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169
170    #[test]
171    fn bound() {
172        assert_eq!(f32::NAN.bound(0.0, 1.0), 1.0);
173        assert_eq!(f32::INFINITY.bound(0.0, 1.0), 1.0);
174        assert_eq!(f32::NEG_INFINITY.bound(0.0, 1.0), 0.0);
175        assert_eq!(f32::EPSILON.bound(0.0, 1.0), f32::EPSILON);
176        assert_eq!(0.5.bound(0.0, 1.0), 0.5);
177        assert_eq!((-1.0).bound(0.0, 1.0), 0.0);
178        assert_eq!(2.0.bound(0.0, 1.0), 1.0);
179    }
180}