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);
}