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}
176