trig.rs

9.7 kB · rust · 375 lines

1#![allow(clippy::excessive_precision)]23/// The number of samples in one full turn.4pub const N: usize = 256;56/// One full turn of sine, sampled N times.7pub const SINE: [f32; 256] = [8    0.0f32,9    0.024541229009628296f32,10    0.049067676067352295f32,11    0.0735645666718483f32,12    0.0980171412229538f32,13    0.12241067737340927f32,14    0.1467304676771164f32,15    0.1709618866443634f32,16    0.19509032368659973f32,17    0.21910123527050018f32,18    0.24298018217086792f32,19    0.2667127549648285f32,20    0.290284663438797f32,21    0.3136817514896393f32,22    0.3368898630142212f32,23    0.3598950505256653f32,24    0.3826834261417389f32,25    0.40524131059646606f32,26    0.4275550842285156f32,27    0.4496113359928131f32,28    0.4713967442512512f32,29    0.49289819598197937f32,30    0.5141027569770813f32,31    0.5349976420402527f32,32    0.5555702447891235f32,33    0.5758081674575806f32,34    0.5956993103027344f32,35    0.6152315735816956f32,36    0.6343932747840881f32,37    0.6531728506088257f32,38    0.6715589761734009f32,39    0.6895405650138855f32,40    0.7071067690849304f32,41    0.7242470979690552f32,42    0.7409511208534241f32,43    0.7572088241577148f32,44    0.7730104327201843f32,45    0.7883464097976685f32,46    0.803207516670227f32,47    0.8175848126411438f32,48    0.8314695954322815f32,49    0.8448535799980164f32,50    0.8577286005020142f32,51    0.8700869679450989f32,52    0.8819212913513184f32,53    0.89322429895401f32,54    0.903989315032959f32,55    0.91420978307724f32,56    0.9238795042037964f32,57    0.9329928159713745f32,58    0.9415440559387207f32,59    0.949528157711029f32,60    0.9569403529167175f32,61    0.9637760519981384f32,62    0.9700312614440918f32,63    0.9757021069526672f32,64    0.9807852506637573f32,65    0.9852776527404785f32,66    0.9891765117645264f32,67    0.9924795627593994f32,68    0.9951847195625305f32,69    0.9972904324531555f32,70    0.9987954497337341f32,71    0.99969881772995f32,72    1.0f32,73    0.99969881772995f32,74    0.9987954497337341f32,75    0.9972904324531555f32,76    0.9951847195625305f32,77    0.9924795627593994f32,78    0.9891765117645264f32,79    0.9852776527404785f32,80    0.9807852506637573f32,81    0.9757021069526672f32,82    0.9700312614440918f32,83    0.9637760519981384f32,84    0.9569403529167175f32,85    0.949528157711029f32,86    0.9415440559387207f32,87    0.9329928159713745f32,88    0.9238795042037964f32,89    0.91420978307724f32,90    0.903989315032959f32,91    0.89322429895401f32,92    0.8819212913513184f32,93    0.8700869679450989f32,94    0.8577286005020142f32,95    0.8448535799980164f32,96    0.8314695954322815f32,97    0.8175848126411438f32,98    0.803207516670227f32,99    0.7883464097976685f32,100    0.7730104327201843f32,101    0.7572088241577148f32,102    0.7409511208534241f32,103    0.7242470979690552f32,104    0.7071067690849304f32,105    0.6895405650138855f32,106    0.6715589761734009f32,107    0.6531728506088257f32,108    0.6343932747840881f32,109    0.6152315735816956f32,110    0.5956993103027344f32,111    0.5758081674575806f32,112    0.5555702447891235f32,113    0.5349976420402527f32,114    0.5141027569770813f32,115    0.49289819598197937f32,116    0.4713967442512512f32,117    0.4496113359928131f32,118    0.4275550842285156f32,119    0.40524131059646606f32,120    0.3826834261417389f32,121    0.3598950505256653f32,122    0.3368898630142212f32,123    0.3136817514896393f32,124    0.290284663438797f32,125    0.2667127549648285f32,126    0.24298018217086792f32,127    0.21910123527050018f32,128    0.19509032368659973f32,129    0.1709618866443634f32,130    0.1467304676771164f32,131    0.12241067737340927f32,132    0.0980171412229538f32,133    0.0735645666718483f32,134    0.049067676067352295f32,135    0.024541229009628296f32,136    1.2246468525851679e-16f32,137    -0.024541229009628296f32,138    -0.049067676067352295f32,139    -0.0735645666718483f32,140    -0.0980171412229538f32,141    -0.12241067737340927f32,142    -0.1467304676771164f32,143    -0.1709618866443634f32,144    -0.19509032368659973f32,145    -0.21910123527050018f32,146    -0.24298018217086792f32,147    -0.2667127549648285f32,148    -0.290284663438797f32,149    -0.3136817514896393f32,150    -0.3368898630142212f32,151    -0.3598950505256653f32,152    -0.3826834261417389f32,153    -0.40524131059646606f32,154    -0.4275550842285156f32,155    -0.4496113359928131f32,156    -0.4713967442512512f32,157    -0.49289819598197937f32,158    -0.5141027569770813f32,159    -0.5349976420402527f32,160    -0.5555702447891235f32,161    -0.5758081674575806f32,162    -0.5956993103027344f32,163    -0.6152315735816956f32,164    -0.6343932747840881f32,165    -0.6531728506088257f32,166    -0.6715589761734009f32,167    -0.6895405650138855f32,168    -0.7071067690849304f32,169    -0.7242470979690552f32,170    -0.7409511208534241f32,171    -0.7572088241577148f32,172    -0.7730104327201843f32,173    -0.7883464097976685f32,174    -0.803207516670227f32,175    -0.8175848126411438f32,176    -0.8314695954322815f32,177    -0.8448535799980164f32,178    -0.8577286005020142f32,179    -0.8700869679450989f32,180    -0.8819212913513184f32,181    -0.89322429895401f32,182    -0.903989315032959f32,183    -0.91420978307724f32,184    -0.9238795042037964f32,185    -0.9329928159713745f32,186    -0.9415440559387207f32,187    -0.949528157711029f32,188    -0.9569403529167175f32,189    -0.9637760519981384f32,190    -0.9700312614440918f32,191    -0.9757021069526672f32,192    -0.9807852506637573f32,193    -0.9852776527404785f32,194    -0.9891765117645264f32,195    -0.9924795627593994f32,196    -0.9951847195625305f32,197    -0.9972904324531555f32,198    -0.9987954497337341f32,199    -0.99969881772995f32,200    -1.0f32,201    -0.99969881772995f32,202    -0.9987954497337341f32,203    -0.9972904324531555f32,204    -0.9951847195625305f32,205    -0.9924795627593994f32,206    -0.9891765117645264f32,207    -0.9852776527404785f32,208    -0.9807852506637573f32,209    -0.9757021069526672f32,210    -0.9700312614440918f32,211    -0.9637760519981384f32,212    -0.9569403529167175f32,213    -0.949528157711029f32,214    -0.9415440559387207f32,215    -0.9329928159713745f32,216    -0.9238795042037964f32,217    -0.91420978307724f32,218    -0.903989315032959f32,219    -0.89322429895401f32,220    -0.8819212913513184f32,221    -0.8700869679450989f32,222    -0.8577286005020142f32,223    -0.8448535799980164f32,224    -0.8314695954322815f32,225    -0.8175848126411438f32,226    -0.803207516670227f32,227    -0.7883464097976685f32,228    -0.7730104327201843f32,229    -0.7572088241577148f32,230    -0.7409511208534241f32,231    -0.7242470979690552f32,232    -0.7071067690849304f32,233    -0.6895405650138855f32,234    -0.6715589761734009f32,235    -0.6531728506088257f32,236    -0.6343932747840881f32,237    -0.6152315735816956f32,238    -0.5956993103027344f32,239    -0.5758081674575806f32,240    -0.5555702447891235f32,241    -0.5349976420402527f32,242    -0.5141027569770813f32,243    -0.49289819598197937f32,244    -0.4713967442512512f32,245    -0.4496113359928131f32,246    -0.4275550842285156f32,247    -0.40524131059646606f32,248    -0.3826834261417389f32,249    -0.3598950505256653f32,250    -0.3368898630142212f32,251    -0.3136817514896393f32,252    -0.290284663438797f32,253    -0.2667127549648285f32,254    -0.24298018217086792f32,255    -0.21910123527050018f32,256    -0.19509032368659973f32,257    -0.1709618866443634f32,258    -0.1467304676771164f32,259    -0.12241067737340927f32,260    -0.0980171412229538f32,261    -0.0735645666718483f32,262    -0.049067676067352295f32,263    -0.024541229009628296f32,264];265266/// Returns the sine at a sample index, wrapping past a full turn.267#[inline]268pub fn sin_idx(i: usize) -> f32 {269    SINE[i % N]270}271272/// Returns the cosine at a sample index by a quarter-turn shift of the sine table.273#[inline]274pub fn cos_idx(i: usize) -> f32 {275    SINE[(i + N / 4) % N]276}277278/// Returns the unit vector of cosine and sine at a sample index.279#[inline]280pub fn unit(i: usize) -> (f32, f32) {281    (cos_idx(i), sin_idx(i))282}283284/// A fractional sample index that wraps around the turn.285#[derive(Clone, Copy, Debug, PartialEq)]286pub struct FracIndex {287    /// The fractional index within one turn.288    pub value: f32,289}290291impl FracIndex {292    /// Builds a fractional index wrapped into the turn.293    #[inline]294    pub fn new(value: f32) -> FracIndex {295        FracIndex { value }.wrapped()296    }297298    /// Advances the index by a delta and wraps it back into the turn.299    #[inline]300    pub fn advance(&mut self, delta: f32) {301        self.value += delta;302        *self = self.wrapped();303    }304305    /// Rounds to the nearest whole sample index.306    #[inline]307    pub fn index(&self) -> usize {308        let r = (self.value + 0.5).floor();309        (r as usize) % N310    }311312    /// Returns the unit vector at the rounded index.313    #[inline]314    pub fn unit(&self) -> (f32, f32) {315        unit(self.index())316    }317318    #[inline]319    fn wrapped(self) -> FracIndex {320        let n = N as f32;321        let v = self.value - (self.value / n).floor() * n;322        FracIndex { value: v }323    }324}325326/// Scales a count of turns into a fractional sample index.327#[inline]328pub fn index_from_turns(turns: f32) -> f32 {329    turns * N as f32330}331332#[cfg(test)]333mod tests {334    use super::*;335336    #[test]337    fn quarter_turns_are_exact() {338        assert_eq!(unit(0), (1.0, 0.0));339        assert_eq!(sin_idx(N / 4), 1.0);340        assert!(cos_idx(N / 4).abs() < 1e-6);341        assert!(sin_idx(N / 2).abs() < 1e-6);342        assert_eq!(sin_idx(3 * N / 4), -1.0);343    }344345    #[test]346    fn unit_vectors_are_unit_length() {347        for i in 0..N {348            let (c, s) = unit(i);349            let mag = c * c + s * s;350            assert!((mag - 1.0).abs() < 1e-5, "index {i} mag {mag}");351        }352    }353354    #[test]355    fn stride_divides_every_count() {356        for &count in &[1usize, 2, 4, 8, 16, 32, 64, 128] {357            assert_eq!(N % count, 0, "N must divide count {count}");358        }359    }360361    #[test]362    fn frac_index_wraps_with_floor_only() {363        let mut f = FracIndex::new(0.0);364        f.advance(N as f32 + 3.0);365        assert!(f.value >= 0.0 && f.value < N as f32);366        assert_eq!(f.index(), 3);367    }368369    #[test]370    fn frac_index_negative_wraps() {371        let f = FracIndex::new(-1.0);372        assert!(f.value >= 0.0 && f.value < N as f32);373        assert_eq!(f.index(), N - 1);374    }375}