pictorus_blocks/core_blocks/
trianglewave_block.rs1use crate::traits::Float;
2use pictorus_block_data::BlockData;
3use pictorus_traits::GeneratorBlock;
4
5#[derive(Debug, Clone)]
6pub struct TrianglewaveBlock<T>
8where
9 T: Float,
10 f64: From<T>,
11{
12 phantom: core::marker::PhantomData<T>,
13 pub data: BlockData,
14}
15
16impl<T> Default for TrianglewaveBlock<T>
17where
18 T: Float,
19 f64: From<T>,
20{
21 fn default() -> Self {
22 Self {
23 phantom: core::marker::PhantomData,
24 data: BlockData::from_scalar(f64::from(T::zero())),
25 }
26 }
27}
28
29impl<T> GeneratorBlock for TrianglewaveBlock<T>
30where
31 T: Float,
32 f64: From<T>,
33{
34 type Parameters = Parameters<T>;
35 type Output = T;
36
37 fn generate(
38 &mut self,
39 parameters: &Self::Parameters,
40 context: &dyn pictorus_traits::Context,
41 ) -> pictorus_traits::PassBy<Self::Output> {
42 let two: T = T::one() + T::one();
44 let four: T = two + two;
45 let t =
46 (parameters.frequency * T::from_duration(context.time()) + parameters.phase) / (T::TAU);
47 let t = num_traits::Float::fract(t);
48 let y = if t < T::one() / two { t } else { T::one() - t };
49 let val = (four * y - T::one()) * parameters.amplitude + parameters.bias;
52 self.data = BlockData::from_scalar(val.into());
53 val
54 }
55}
56
57pub struct Parameters<T: Float> {
58 pub amplitude: T,
59 pub frequency: T,
60 pub phase: T,
61 pub bias: T,
62}
63
64impl<T: Float> Parameters<T> {
65 pub fn new(amplitude: T, frequency: T, phase: T, bias: T) -> Parameters<T> {
66 Parameters {
67 amplitude,
68 frequency,
69 phase,
70 bias,
71 }
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 use crate::testing::{StubContext, StubRuntime};
79 use approx::assert_relative_eq;
80 use core::time::Duration;
81
82 const PI: f64 = core::f64::consts::PI;
83
84 #[test]
85 fn test_trianglewave_block_simple() {
86 let context = StubContext::new(
87 Duration::from_secs(0),
88 None,
89 Duration::from_secs_f64(PI / 2.0),
90 );
91 let mut runtime = StubRuntime::new(context);
92
93 let amplitude = 1.0;
94 let frequency = 1.0;
95 let phase = 0.0;
96 let bias = 0.0;
97 let params = Parameters::new(amplitude, frequency, phase, bias);
98
99 let mut block = TrianglewaveBlock::default();
100
101 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), -1.0, epsilon = 1e-6);
103
104 runtime.tick();
105 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), 0.0, epsilon = 1e-6);
107
108 runtime.tick();
109 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), 1.0, epsilon = 1e-6);
111
112 runtime.tick();
113 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), 0.0, epsilon = 1e-6);
115
116 runtime.tick();
117 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), -1.0, epsilon = 1e-6);
119 }
120
121 #[test]
122 fn test_trianglewave_block_phase() {
123 let context = StubContext::new(
124 Duration::from_secs(0),
125 None,
126 Duration::from_secs_f64(PI / 2.0),
127 );
128 let mut runtime = StubRuntime::new(context);
129
130 let amplitude = 1.0;
131 let frequency = 1.0;
132 let phase = 0.5 * PI;
133 let bias = 0.0;
134 let params = Parameters::new(amplitude, frequency, phase, bias);
135
136 let mut block = TrianglewaveBlock::default();
137
138 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), 0.0, epsilon = 1e-6);
140
141 runtime.tick();
142 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), 1.0, epsilon = 1e-6);
144
145 runtime.tick();
146 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), 0.0, epsilon = 1e-6);
148
149 runtime.tick();
150 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), -1.0, epsilon = 1e-6);
152
153 runtime.tick();
154 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), 0.0, epsilon = 1e-6);
156 }
157
158 #[test]
159 fn test_trianglewave_block_bias() {
160 let context = StubContext::new(
161 Duration::from_secs(0),
162 None,
163 Duration::from_secs_f64(PI / 2.0),
164 );
165 let mut runtime = StubRuntime::new(context);
166
167 let amplitude = 1.0;
168 let frequency = 1.0;
169 let phase = 0.0;
170 let bias = 1.0;
171 let params = Parameters::new(amplitude, frequency, phase, bias);
172
173 let mut block = TrianglewaveBlock::default();
174
175 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), 0.0, epsilon = 1e-6);
177
178 runtime.tick();
179 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), 1.0, epsilon = 1e-6);
181
182 runtime.tick();
183 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), 2.0, epsilon = 1e-6);
185
186 runtime.tick();
187 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), 1.0, epsilon = 1e-6);
189
190 runtime.tick();
191 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), 0.0, epsilon = 1e-6);
193 }
194
195 #[test]
196 fn test_trianglewave_block_amplitude() {
197 let context = StubContext::new(
198 Duration::from_secs(0),
199 None,
200 Duration::from_secs_f64(PI / 2.0),
201 );
202 let mut runtime = StubRuntime::new(context);
203
204 let amplitude = 2.0;
205 let frequency = 1.0;
206 let phase = 0.0;
207 let bias = 0.0;
208 let params = Parameters::new(amplitude, frequency, phase, bias);
209
210 let mut block = TrianglewaveBlock::default();
211
212 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), -2.0, epsilon = 1e-6);
214
215 runtime.tick();
216 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), 0.0, epsilon = 1e-6);
218
219 runtime.tick();
220 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), 2.0, epsilon = 1e-6);
222
223 runtime.tick();
224 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), 0.0, epsilon = 1e-6);
226
227 runtime.tick();
228 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), -2.0, epsilon = 1e-6);
230 }
231
232 #[test]
233 fn test_trianglewave_block_high_time() {
234 let context = StubContext::new(
235 Duration::from_secs(0),
236 None,
237 Duration::from_secs_f64(PI / 2.0),
238 );
239 let mut runtime = StubRuntime::new(context);
240
241 let amplitude = 1.0;
242 let frequency = 2.0;
243 let phase = 0.0;
244 let bias = 0.0;
245
246 let params = Parameters::new(amplitude, frequency, phase, bias);
247 let mut block = TrianglewaveBlock::default();
248
249 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), -1.0, epsilon = 1e-6);
251
252 runtime.set_time(Duration::from_secs_f64(400.0 * PI));
253 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), -1.0, epsilon = 1e-6);
255 }
256
257 #[test]
258 fn test_trianglewave_block_frequency() {
259 let context = StubContext::new(
260 Duration::from_secs(0),
261 None,
262 Duration::from_secs_f64(PI / 4.0),
263 );
264 let mut runtime = StubRuntime::new(context);
265
266 let amplitude = 1.0;
267 let frequency = 2.0;
268 let phase = 0.0;
269 let bias = 0.0;
270
271 let params = Parameters::new(amplitude, frequency, phase, bias);
272 let mut block = TrianglewaveBlock::default();
273
274 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), -1.0, epsilon = 1e-6);
276
277 runtime.tick();
278 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), 0.0, epsilon = 1e-6);
280
281 runtime.tick();
282 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), 1.0, epsilon = 1e-6);
284
285 runtime.tick();
286 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), 0.0, epsilon = 1e-6);
288
289 runtime.tick();
290 block.generate(¶ms, &runtime.context()); assert_relative_eq!(block.data.scalar(), -1.0, epsilon = 1e-6);
292 }
293}