use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::mpsc::{Receiver, Sender};
pub type TraceId = u32;
#[derive(Debug, Clone, Copy)]
pub struct PlotPoint {
pub x: f64,
pub y: f64,
}
#[derive(Debug, Clone)]
pub struct Trace {
pub id: TraceId,
pub name: String,
pub info: Option<String>,
}
pub enum PlotCommand {
RegisterTrace {
id: TraceId,
name: String,
info: Option<String>,
},
Point { trace_id: TraceId, point: PlotPoint },
Points {
trace_id: TraceId,
points: Vec<PlotPoint>,
},
SetPointsY {
trace_id: TraceId,
xs: Vec<f64>,
y: f64,
},
DeletePointsX { trace_id: TraceId, xs: Vec<f64> },
DeleteXRange {
trace_id: TraceId,
x_min: f64,
x_max: f64,
},
ApplyYFnAtX {
trace_id: TraceId,
xs: Vec<f64>,
f: YTransform,
},
ApplyYFnInXRange {
trace_id: TraceId,
x_min: f64,
x_max: f64,
f: YTransform,
},
ClearData { trace_id: TraceId },
SetData { trace_id: TraceId, points: Vec<PlotPoint> },
}
#[derive(Clone)]
pub struct PlotSink {
tx: Sender<PlotCommand>,
}
pub type YTransform = Box<dyn Fn(f64) -> f64 + Send + 'static>;
impl PlotSink {
pub fn create_trace<S: Into<String>>(&self, name: S, info: Option<S>) -> Trace {
static NEXT_ID: AtomicU32 = AtomicU32::new(1);
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
let name = name.into();
let info_str = info.map(|s| s.into());
let _ = self.tx.send(PlotCommand::RegisterTrace {
id,
name: name.clone(),
info: info_str.clone(),
});
Trace {
id,
name,
info: info_str,
}
}
pub fn send_point(
&self,
trace: &Trace,
point: PlotPoint,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>> {
self.tx.send(PlotCommand::Point {
trace_id: trace.id,
point,
})
}
pub fn send_point_by_id(
&self,
trace_id: TraceId,
point: PlotPoint,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>> {
self.tx.send(PlotCommand::Point { trace_id, point })
}
pub fn send_points<I>(
&self,
trace: &Trace,
points: I,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>>
where
I: Into<Vec<PlotPoint>>,
{
self.tx.send(PlotCommand::Points {
trace_id: trace.id,
points: points.into(),
})
}
pub fn send_points_by_id<I>(
&self,
trace_id: TraceId,
points: I,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>>
where
I: Into<Vec<PlotPoint>>,
{
self.tx.send(PlotCommand::Points {
trace_id,
points: points.into(),
})
}
#[inline]
pub fn set_point_y(
&self,
trace: &Trace,
x: f64,
y: f64,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>> {
self.tx.send(PlotCommand::SetPointsY {
trace_id: trace.id,
xs: vec![x],
y,
})
}
#[inline]
pub fn set_point_y_by_id(
&self,
trace_id: TraceId,
x: f64,
y: f64,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>> {
self.tx.send(PlotCommand::SetPointsY {
trace_id,
xs: vec![x],
y,
})
}
#[inline]
pub fn set_points_y<I>(
&self,
trace: &Trace,
xs: I,
y: f64,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>>
where
I: Into<Vec<f64>>,
{
self.tx.send(PlotCommand::SetPointsY {
trace_id: trace.id,
xs: xs.into(),
y,
})
}
#[inline]
pub fn set_points_y_by_id<I>(
&self,
trace_id: TraceId,
xs: I,
y: f64,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>>
where
I: Into<Vec<f64>>,
{
self.tx.send(PlotCommand::SetPointsY {
trace_id,
xs: xs.into(),
y,
})
}
#[inline]
pub fn delete_point_x(
&self,
trace: &Trace,
x: f64,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>> {
self.tx.send(PlotCommand::DeletePointsX {
trace_id: trace.id,
xs: vec![x],
})
}
#[inline]
pub fn delete_point_x_by_id(
&self,
trace_id: TraceId,
x: f64,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>> {
self.tx.send(PlotCommand::DeletePointsX {
trace_id,
xs: vec![x],
})
}
#[inline]
pub fn delete_points_x<I>(
&self,
trace: &Trace,
xs: I,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>>
where
I: Into<Vec<f64>>,
{
self.tx.send(PlotCommand::DeletePointsX {
trace_id: trace.id,
xs: xs.into(),
})
}
#[inline]
pub fn delete_points_x_by_id<I>(
&self,
trace_id: TraceId,
xs: I,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>>
where
I: Into<Vec<f64>>,
{
self.tx.send(PlotCommand::DeletePointsX {
trace_id,
xs: xs.into(),
})
}
#[inline]
pub fn delete_x_range(
&self,
trace: &Trace,
x_min: f64,
x_max: f64,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>> {
self.tx.send(PlotCommand::DeleteXRange {
trace_id: trace.id,
x_min,
x_max,
})
}
#[inline]
pub fn delete_x_range_by_id(
&self,
trace_id: TraceId,
x_min: f64,
x_max: f64,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>> {
self.tx.send(PlotCommand::DeleteXRange {
trace_id,
x_min,
x_max,
})
}
#[inline]
pub fn apply_y_fn_at_x(
&self,
trace: &Trace,
x: f64,
f: YTransform,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>> {
self.tx.send(PlotCommand::ApplyYFnAtX {
trace_id: trace.id,
xs: vec![x],
f,
})
}
#[inline]
pub fn apply_y_fn_at_x_by_id(
&self,
trace_id: TraceId,
x: f64,
f: YTransform,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>> {
self.tx.send(PlotCommand::ApplyYFnAtX {
trace_id,
xs: vec![x],
f,
})
}
#[inline]
pub fn apply_y_fn_at_xs<I>(
&self,
trace: &Trace,
xs: I,
f: YTransform,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>>
where
I: Into<Vec<f64>>,
{
self.tx.send(PlotCommand::ApplyYFnAtX {
trace_id: trace.id,
xs: xs.into(),
f,
})
}
#[inline]
pub fn apply_y_fn_at_xs_by_id<I>(
&self,
trace_id: TraceId,
xs: I,
f: YTransform,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>>
where
I: Into<Vec<f64>>,
{
self.tx.send(PlotCommand::ApplyYFnAtX {
trace_id,
xs: xs.into(),
f,
})
}
#[inline]
pub fn apply_y_fn_in_x_range(
&self,
trace: &Trace,
x_min: f64,
x_max: f64,
f: YTransform,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>> {
self.tx.send(PlotCommand::ApplyYFnInXRange {
trace_id: trace.id,
x_min,
x_max,
f,
})
}
#[inline]
pub fn apply_y_fn_in_x_range_by_id(
&self,
trace_id: TraceId,
x_min: f64,
x_max: f64,
f: YTransform,
) -> Result<(), std::sync::mpsc::SendError<PlotCommand>> {
self.tx.send(PlotCommand::ApplyYFnInXRange {
trace_id,
x_min,
x_max,
f,
})
}
#[inline]
pub fn clear_data(&self, trace: &Trace) -> Result<(), std::sync::mpsc::SendError<PlotCommand>> {
self.tx.send(PlotCommand::ClearData { trace_id: trace.id })
}
#[inline]
pub fn clear_data_by_id(&self, trace_id: TraceId) -> Result<(), std::sync::mpsc::SendError<PlotCommand>> {
self.tx.send(PlotCommand::ClearData { trace_id })
}
pub fn set_data<I>(&self, trace: &Trace, points: I) -> Result<(), std::sync::mpsc::SendError<PlotCommand>>
where
I: Into<Vec<PlotPoint>>,
{
self.tx.send(PlotCommand::SetData {
trace_id: trace.id,
points: points.into(),
})
}
pub fn set_data_by_id<I>(&self, trace_id: TraceId, points: I) -> Result<(), std::sync::mpsc::SendError<PlotCommand>>
where
I: Into<Vec<PlotPoint>>,
{
self.tx.send(PlotCommand::SetData {
trace_id,
points: points.into(),
})
}
}
pub fn channel_plot() -> (PlotSink, Receiver<PlotCommand>) {
let (tx, rx) = std::sync::mpsc::channel();
(PlotSink { tx }, rx)
}