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
//! Example: Gradually decay (shrink) older parts of a trace
//!
//! What it demonstrates
//! - Streaming a single sine trace into the UI.
//! - Applying a repeated multiplicative transform to an X-range using `apply_y_fn_in_x_range`.
//! - Use of `f64::NAN` as a shorthand for the start/end of the trace buffer (see docs).
//!
//! How to run
//! ```bash
//! cargo run --example decay_old
//! ```
//! The example repeatedly multiplies all samples older than 3 seconds by 0.9, so older
//! regions of the trace visually shrink over time.
use liveplot::{channel_plot, run_liveplot, LivePlotConfig, PlotPoint};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
fn main() -> eframe::Result<()> {
// Create multi-trace plot channel (single trace labeled "signal")
let (sink, rx) = channel_plot();
let trace = sink.create_trace("signal", Some("Sine with decaying history"));
// Producer: 500 Hz sample rate, 2 Hz sine
let sink_clone = sink.clone();
let trace_clone = trace.clone();
std::thread::spawn(move || {
const FS_HZ: f64 = 500.0;
const F_HZ: f64 = 2.0;
let dt = Duration::from_millis((1000.0 / FS_HZ) as u64);
let mut n: u64 = 0;
loop {
let t_s = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs_f64())
.unwrap_or(0.0);
let phase_t = n as f64 / FS_HZ;
let val = (2.0 * std::f64::consts::PI * F_HZ * phase_t).sin();
let _ = sink_clone.send_point(&trace_clone, PlotPoint { x: t_s, y: val });
n = n.wrapping_add(1);
std::thread::sleep(dt);
}
});
// Periodically shrink the amplitude of older parts of the trace.
// Every 300ms, apply a 0.9x multiplier to all samples older than 3 seconds.
let sink_decay = sink.clone();
let trace_decay = trace.clone();
std::thread::spawn(move || {
let mut last_tick = SystemTime::now();
loop {
std::thread::sleep(Duration::from_millis(300));
let now_s = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs_f64())
.unwrap_or(0.0);
// Define the range: everything strictly older than 3 seconds
let x_max = now_s - 3.0;
if x_max.is_finite() {
// Apply multiplicative decay to older samples; repeated application makes older segments fade
// Use NaN as the lower bound to mean "start of data" (see docs)
let _ = sink_decay.apply_y_fn_in_x_range(
&trace_decay,
f64::NAN,
x_max,
Box::new(|y| y * 0.9),
);
}
// Prevent too frequent updates if system clock jumps
let _ = &mut last_tick;
}
});
// Run the UI until closed
run_liveplot(rx, LivePlotConfig::default())
}