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}