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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
//! Collection of math Traits and functions for manipulating integers. //! //! Some including functions: //! - Scaling Functions (from one integer to another) //! - In place and batch scaling (`nscale16x3` for example). //! - Dimming and Brightening Functions //! - Fast u8 and u16 trigonometric functions //! - Other useful operations, such as blending integers. //! //! This module offers a couple different ways to access the m //! These are the raw functions for both `u8` and `u16`. Most of these methods //! are implemented through the [`Scaling`] trait interface, see that for better //! documentation of these functions. //! //! If `const` functions are desired, use the re-exported functions rather than //! the trait impls. //! //! [`Scaling`]: ./trait.ScalingInt.html // Credit for most of these functions goes to the authoers of the FastLED library. #![allow(clippy::cast_lossless)] pub(crate) mod lerp; pub mod trig; pub use math_u8_impls::scale as scale8; pub use math_u8_impls::scale_video as scale8_video; pub use math_u8_impls::dim_raw as dim8_raw; pub use math_u8_impls::dim_video as dim8_video; pub use math_u8_impls::dim_lin as dim8_lin; pub use math_u8_impls::brighten_raw as brighten8_raw; pub use math_u8_impls::brighten_video as brighten8_video; pub use math_u8_impls::brighten_lin as brighten8_lin; pub use math_u8_impls::nscale as nscale8; pub use math_u8_impls::nscale_x2 as nscale8x2; pub use math_u8_impls::nscale_x3 as nscale8x3; pub use math_u8_impls::nscale_x4 as nscale8x4; pub use math_u8_impls::blend as blend8; pub use math_u16_impls::scale as scale16; pub use math_u16_impls::scale_video as scale16_video; pub use math_u16_impls::dim_raw as dim16_raw; pub use math_u16_impls::dim_video as dim16_video; pub use math_u16_impls::dim_lin as dim16_lin; pub use math_u16_impls::brighten_raw as brighten16_raw; pub use math_u16_impls::brighten_video as brighten16_video; pub use math_u16_impls::brighten_lin as brighten16_lin; pub use math_u16_impls::nscale as nscale16; pub use math_u16_impls::nscale_x2 as nscale16x2; pub use math_u16_impls::nscale_x3 as nscale16x3; pub use math_u16_impls::nscale_x4 as nscale16x4; pub use math_u16_impls::blend as blend16; /// Basic trigonometric functions for integers. pub trait Trig<Signed> { fn sin(self) -> Signed; fn cos(self) -> Signed; } /// Scaling, Dimming, Brightening, and other misc functions functions for integers /// representing scalar components. /// /// These functions are extremely useful for operating on integer color components, /// such as the red/blue/green values seen in `RGB` color encoding. /// /// # Notes on Fractional Components /// /// These methods are used primarily for representing integers as fractions, rather than /// whole numbers. They can also be treated as fractions, percents, or some otherwise /// range-bounded scalar to a dimension. A more accurate way to represent this information /// would be to use a `f32`/`f64` and clamping the result to a pre-defined range. /// Integers are used as the math is significantly faster to compute, and floating values /// aren't always available on all target platforms. /// /// For example, a `u8` takes on the range `[0:255]`. The maximum value /// of 255 doesn't represent the existence of 255 items, but rather being the maximum /// possible scalar for a dimension. Respectively, the value of 0 is the minimum value /// for a dimension. /// /// As a by-product, these functions are saturating, hitting a ceiling at the maximum /// possible value, and hitting a floor at the minimum possible value (usually 0, /// except for `_video` functions). /// /// # Terminology /// /// - `_video`: The output is guaranteed to only be zero if at least one of the /// inputs is zero. /// - `_lin`: Used only in brightening and dimming functions. If the input is below /// half of the maximum value, the value is brightened / dimmed linearly instead of /// scaled. /// pub trait ScalingInt { /// Scales self by a second one (`scale`), which is treated as the numerator /// of a fraction whose denominator is `Self::MAX`. /// /// In other words, it computes `i * (scale / Self::MAX)` /// /// # Example /// /// ``` /// use cichlid::ScalingInt; /// /// assert_eq!(100u8.scale(255), 100); // 100 * 1.0 /// assert_eq!(100u8.scale(0), 0); // 100 * 0.0 /// assert_eq!(100u8.scale(255 / 2), 50); // 100 * 0.5 /// ``` fn scale(self, other: Self) -> Self; /// The "video" version of scale. /// /// This version guarantees that the output will be only be zero if one /// or both of the inputs are zero. If both inputs are non-zero, the output is guaranteed /// to be non-zero. /// /// This makes for better 'video'/LED dimming, at the cost of several additional cycles. /// /// # Example /// /// ``` /// use cichlid::ScalingInt; /// /// assert_eq!(100u8.scale_video(255), 100u8.scale(255)); // same as scale8... /// assert_ne!(1u8.scale_video(1), 1u8.scale(1)); // Except scale8() == 0 /// ``` fn scale_video(self, other: Self) -> Self; /// Dims an integer. /// /// The eye does not respond in a linear way to light. High speed PWM'd LEDs at 50% duty cycle /// appear far brighter then the 'half as bright' you might expect. /// /// If you want your midpoint brightness level (for `u8`, that'd be 128) to appear half as /// bright as 'full' brightness (255 for `u8`), you have to apply a dimming function. /// /// # Example /// /// ``` /// use cichlid::ScalingInt; /// /// let full_brightness: u8 = 255; /// assert_eq!(255, full_brightness.dim_raw()); /// /// let half_brightness: u8 = full_brightness / 2; /// assert_eq!(63, half_brightness.dim_raw()); /// ``` fn dim_raw(self) -> Self; /// Dims in video mode. /// /// This is the same as `dim_raw`, but the output of this function will only be zero if the /// input is zero. /// /// # Example /// /// ``` /// use cichlid::ScalingInt; /// /// assert_eq!(255u8.dim_raw(), 255u8.dim_video()); /// assert_ne!(30u8.dim_raw(), 30u8.dim_video()); /// ``` fn dim_video(self) -> Self; /// Dims an integer linearly. /// /// This is the same as `dim_raw`, but when `x < (Self::MAX / 2)`, the value is simply halved. /// The output will only be zero if the input is zero. fn dim_lin(self) -> Self; /// Inverse of the `dim_raw` function, brightens a value. fn brighten_raw(self) -> Self; /// Inverse of the `dim_video` function, brightens a value. fn brighten_video(self) -> Self; /// Linear version of the `brighten8_raw`, that halves for values < `Self::MAX / 2`. /// /// Notably, this is the relative inverse of `dim_lin`. fn brighten_lin(self) -> Self; /// Blends self with another integer by the fraction `amount_of_b`. fn blend(self, b: Self, amount_of_b: Self) -> Self; } macro_rules! doc_comment { ($x:expr, $($tt:tt)*) => { #[doc = $x] $($tt)* }; } // nscaling macro macro_rules! impl_nscale_ops { ($t:tt, $up:tt, $shift:expr, $mscaler:expr, $($element:tt),*) => { let scaler: $up = 1 as $up + $up::from($mscaler); $( *$element = (((*$element as $up) * scaler) >> $shift) as $t; )* }; } macro_rules! impl_scale_ops { ($t:tt, $up:tt, $shift:expr, $max:expr) => ( doc_comment!{concat!( "Scale a `", stringify!($t), "` by another."), #[inline(always)] pub const fn scale(i: $t, scale: $t) -> $t { (((i as $up) * (1 as $up + scale as $up)) >> $shift) as $t } } doc_comment!{concat!( "Scale a `", stringify!($t), "` by another, but in video mode.", "\n\n", "Video scaling guarantees the output of this function will only be zero", "if-and-only-if at least one of the inputs are zero."), #[inline] pub const fn scale_video(i: $t, scale: $t) -> $t { let x: $t = (((i as $up) * (scale as $up)) >> $shift) as $t; let correction_int: $t = (i != 0) as $t; let correction_scale: $t = (scale != 0) as $t; let correction: $t = correction_int & correction_scale; x + correction as $t }} doc_comment!{concat!("Dims a `", stringify!($t), "`."), #[inline(always)] pub const fn dim_raw(x: $t) -> $t { scale(x, x) }} doc_comment!{concat!( "Dims a `", stringify!($t), "` in video mode.", "\n\n", "Similar to `scale_video`, the output will only be zero if the input", "is also zero."), #[inline(always)] pub const fn dim_video(x: $t) -> $t { scale_video(x, x) }} doc_comment!{concat!( "Dims a `", stringify!($t), "` similar to `dim_raw`, but linearly below a threshold.", "\n\n", "When the input is less than equal to`", stringify!($max / 2), "`, the output is dimmed ", "by halving."), #[inline] pub const fn dim_lin(x: $t) -> $t { const UPPER_BITS: $t = (1 << ($shift - 1)); let use_lin = (x & UPPER_BITS) != 0; let scale_x_reg = (use_lin as $t) * scale(x, x); let scale_x_lin = (!use_lin as $t) * (x.wrapping_add(1) / 2); // This is just a hack to be able to use const fns. scale_x_reg.wrapping_add(scale_x_lin) }} doc_comment!{concat!( "Brightens a `", stringify!($t), "`.", "\n\n", "This is the inverse of `dim_raw`."), #[inline] pub const fn brighten_raw(x: $t) -> $t { let ix = $max - x; $max - dim_raw(ix) }} doc_comment!{concat!( "Brightens a `", stringify!($t), "` but in video mode.", "\n\n", "This is the inverse of `dim_video`."), #[inline] pub const fn brighten_video(x: $t) -> $t { let ix = $max - x; $max - dim_video(ix) }} doc_comment!{concat!( "Brightens a `", stringify!($t), "`, but linearly below a threshold.", "\n\n", "This is the inverse of `dim_lin`."), #[inline] pub const fn brighten_lin(x: $t) -> $t { let ix = $max - x; $max - dim_lin(ix) }} doc_comment!{concat!( "Scales a single `", stringify!($t), "` in place."), #[inline(always)] pub fn nscale(int: &mut $t, scaler: $t) { *int = scale(*int, scaler); }} doc_comment!{concat!( "Inplace scaling for two `", stringify!($t), "`'s by the same value."), #[inline(always)] pub fn nscale_x2(int_1: &mut $t, int_2: &mut $t, scaler: $t) { impl_nscale_ops!($t, $up, $shift, scaler, int_1, int_2); }} doc_comment!{concat!( "Inplace scaling for three `", stringify!($t), "`'s by the same value."), #[inline] pub fn nscale_x3(int_1: &mut $t, int_2: &mut $t, int_3: &mut $t, scaler: $t) { impl_nscale_ops!($t, $up, $shift, scaler, int_1, int_2, int_3); }} doc_comment!{concat!( "Inplace scaling for four `", stringify!($t), "`'s by the same value."), #[inline] pub fn nscale_x4(int_1: &mut $t, int_2: &mut $t, int_3: &mut $t, int_4: &mut $t, scaler: $t) { impl_nscale_ops!($t, $up, $shift, scaler, int_1, int_2, int_3, int_4); }} doc_comment!{concat!( "Blends a `", stringify!($t), "`another integer by the fraction `amount_of_b`."), #[inline] pub const fn blend(a: $t, b: $t, amount_of_b: $t) -> $t { let amount_of_a: $up = ($max - amount_of_b) as $up; let mut partial: $up = 0; partial += a as $up * amount_of_a as $up; partial += a as $up; partial += b as $up * amount_of_b as $up; partial += b as $up; (partial >> $shift) as $t }} ) } // Re exports a function name to be used through another. // // Great for creating shimmy traits with already made functions underneath. macro_rules! impl_scaling_trait_rename { ($t:tt, $fname:ident) => ( #[inline(always)] fn $fname(self) -> $t { $fname(self) } ); ($t:tt, $param:ident, $fname:ident) => ( #[inline(always)] fn $fname(self, $param: $t) -> $t { $fname(self, $param) } ); ($t:tt, $param_1:ident, $param_2:ident, $fname:ident) => ( #[inline(always)] fn $fname(self, $param_1: $t, $param_2: $t) -> $t { $fname(self, $param_1, $param_2) } ); } macro_rules! impl_scaling_trait { ($t:tt) => ( impl crate::math::ScalingInt for $t { impl_scaling_trait_rename!($t, other, scale); impl_scaling_trait_rename!($t, other, scale_video); impl_scaling_trait_rename!($t, dim_raw); impl_scaling_trait_rename!($t, dim_video); impl_scaling_trait_rename!($t, dim_lin); impl_scaling_trait_rename!($t, brighten_raw); impl_scaling_trait_rename!($t, brighten_video); impl_scaling_trait_rename!($t, brighten_lin); impl_scaling_trait_rename!($t, b, amount_of_b, blend); } ) } pub mod math_u8_impls { //! Math functions for `u8`s. Includes scaling, dimming, brightening. //! //! Better documentation for these functions can be found under [`ScalingInt`]. //! //! [`ScalingInt`]: ../trait.ScalingInt.html impl_scale_ops!(u8, u16, 8, 255); impl_scaling_trait!(u8); } pub mod math_u16_impls { //! Math functions for `u16`s. Includes scaling, dimming, brightening. //! //! Better documentation for these functions can be found under [`ScalingInt`]. //! //! [`ScalingInt`]: ../trait.ScalingInt.html impl_scale_ops!(u16, u32, 16, 65535); impl_scaling_trait!(u16); }