use std::io;
use std::ops::{Deref, DerefMut};
use std::sync::{Arc, Mutex, mpsc};
use std::time::Duration;
pub use lasy::InterpolationConfig;
use crate::stream;
use crate::stream::raw::{self, Buffer, StreamError};
use crate::{Point, RawPoint};
pub trait RenderFn<M>: Fn(&mut M, &mut Frame) {}
impl<M, F> RenderFn<M> for F where F: Fn(&mut M, &mut Frame) {}
pub struct Stream<M> {
raw: raw::Stream<M>,
state_update_tx: mpsc::Sender<StateUpdate>,
}
#[derive(Clone)]
struct State {
frame_hz: u32,
interpolation_conf: lasy::InterpolationConfig,
enable_optimisations: bool,
enable_draw_reorder: bool,
}
type StateUpdate = Box<dyn FnMut(&mut State) + 'static + Send>;
pub struct Frame {
frame_hz: u32,
point_hz: u32,
latency_points: u32,
points: Vec<Point>,
}
struct Requester {
last_frame_point: Option<RawPoint>,
raw_points: Vec<RawPoint>,
blank_points: Vec<RawPoint>,
}
type DefaultProcessRawFn<M> = fn(&mut M, &mut Buffer);
pub struct Builder<M, F, R = DefaultProcessRawFn<M>, E = raw::DefaultStreamErrorFn<M>> {
pub(crate) api_inner: Arc<crate::Inner>,
pub builder: stream::Builder,
pub model: M,
pub render: F,
pub process_raw: R,
pub stream_error: E,
pub frame_hz: Option<u32>,
pub interpolation_conf: lasy::InterpolationConfig,
pub enable_optimisations: bool,
pub enable_draw_reorder: bool,
}
impl<M> Stream<M> {
pub fn set_distance_per_point(&self, d: f32) -> Result<(), mpsc::SendError<()>> {
self.send_frame_state_update(move |state| state.interpolation_conf.distance_per_point = d)
.map_err(|_| mpsc::SendError(()))
}
pub fn set_blank_delay_points(&self, ps: u32) -> Result<(), mpsc::SendError<()>> {
self.send_frame_state_update(move |state| state.interpolation_conf.blank_delay_points = ps)
.map_err(|_| mpsc::SendError(()))
}
pub fn set_radians_per_point(&self, rad: f32) -> Result<(), mpsc::SendError<()>> {
self.send_frame_state_update(move |state| state.interpolation_conf.radians_per_point = rad)
.map_err(|_| mpsc::SendError(()))
}
pub fn set_frame_hz(&self, fps: u32) -> Result<(), mpsc::SendError<()>> {
self.send_frame_state_update(move |state| state.frame_hz = fps)
.map_err(|_| mpsc::SendError(()))
}
pub fn enable_optimisations(&self, enabled: bool) -> Result<(), mpsc::SendError<()>> {
self.send_frame_state_update(move |state| state.enable_optimisations = enabled)
.map_err(|_| mpsc::SendError(()))
}
pub fn enable_draw_reorder(&self, enabled: bool) -> Result<(), mpsc::SendError<()>> {
self.send_frame_state_update(move |state| state.enable_draw_reorder = enabled)
.map_err(|_| mpsc::SendError(()))
}
pub fn close(self) -> Option<std::thread::Result<Result<(), StreamError>>> {
let Stream { raw, .. } = self;
raw.close()
}
fn send_frame_state_update<F>(&self, update: F) -> Result<(), mpsc::SendError<StateUpdate>>
where
F: FnOnce(&mut State) + Send + 'static,
{
let mut update_opt = Some(update);
let update_fn = move |state: &mut State| {
if let Some(update) = update_opt.take() {
update(state);
}
};
self.state_update_tx.send(Box::new(update_fn))
}
}
impl<M, F, R, E> Builder<M, F, R, E> {
pub fn detected_dac(mut self, dac: crate::DetectedDac) -> Self {
self.builder.dac = Some(dac);
self
}
pub fn tcp_timeout(mut self, tcp_timeout: Option<Duration>) -> Self {
self.builder.tcp_timeout = tcp_timeout;
self
}
pub fn point_hz(mut self, point_hz: u32) -> Self {
self.builder.point_hz = Some(point_hz);
self
}
pub fn frame_hz(mut self, frame_hz: u32) -> Self {
self.frame_hz = Some(frame_hz);
self
}
pub fn latency_points(mut self, points: u32) -> Self {
self.builder.latency_points = Some(points);
self
}
pub fn distance_per_point(mut self, dpp: f32) -> Self {
self.interpolation_conf.distance_per_point = dpp;
self
}
pub fn blank_delay_points(mut self, points: u32) -> Self {
self.interpolation_conf.blank_delay_points = points;
self
}
pub fn radians_per_point(mut self, radians: f32) -> Self {
self.interpolation_conf.radians_per_point = radians;
self
}
pub fn enable_optimisations(mut self, enable: bool) -> Self {
self.enable_optimisations = enable;
self
}
pub fn enable_draw_reorder(mut self, enable: bool) -> Self {
self.enable_draw_reorder = enable;
self
}
pub fn process_raw<R2>(self, process_raw: R2) -> Builder<M, F, R2, E> {
let Builder {
api_inner,
builder,
model,
render,
stream_error,
frame_hz,
interpolation_conf,
enable_optimisations,
enable_draw_reorder,
..
} = self;
Builder {
api_inner,
builder,
model,
render,
process_raw,
stream_error,
frame_hz,
interpolation_conf,
enable_optimisations,
enable_draw_reorder,
}
}
pub fn stream_error<E2>(self, stream_error: E2) -> Builder<M, F, R, E2> {
let Builder {
api_inner,
builder,
model,
render,
process_raw,
frame_hz,
interpolation_conf,
enable_optimisations,
enable_draw_reorder,
..
} = self;
Builder {
api_inner,
builder,
model,
render,
process_raw,
stream_error,
frame_hz,
interpolation_conf,
enable_optimisations,
enable_draw_reorder,
}
}
pub fn build(self) -> io::Result<Stream<M>>
where
M: 'static + Send,
F: 'static + RenderFn<M> + Send,
R: 'static + raw::RenderFn<M> + Send,
E: 'static + raw::StreamErrorFn<M> + Send,
{
let Builder {
api_inner,
builder,
model,
render,
process_raw,
stream_error,
frame_hz,
interpolation_conf,
enable_optimisations,
enable_draw_reorder,
} = self;
let frame_hz = frame_hz.unwrap_or(stream::DEFAULT_FRAME_HZ);
let requester = Requester {
last_frame_point: None,
raw_points: vec![],
blank_points: vec![],
};
let requester = Arc::new(Mutex::new(requester));
let (state_update_tx, state_update_rx) = mpsc::channel();
let state_update_tx: mpsc::Sender<StateUpdate> = state_update_tx;
let state = Arc::new(Mutex::new(State {
frame_hz,
interpolation_conf,
enable_optimisations,
enable_draw_reorder,
}));
let raw_render = move |model: &mut M, buffer: &mut Buffer| {
let state = {
let mut state = state.lock().expect("failed to lock");
for mut state_update in state_update_rx.try_iter() {
(*state_update)(&mut state);
}
state.clone()
};
let mut guard = requester.lock().expect("failed to lock frame requester");
guard.fill_buffer(model, &render, buffer, &state);
process_raw(model, buffer);
};
let raw_builder = raw::Builder {
api_inner,
builder,
model,
render: raw_render,
stream_error,
};
let raw_stream = raw_builder.build()?;
let stream = Stream {
raw: raw_stream,
state_update_tx,
};
Ok(stream)
}
}
impl Frame {
pub fn frame_hz(&self) -> u32 {
self.frame_hz
}
pub fn point_hz(&self) -> u32 {
self.point_hz
}
pub fn latency_points(&self) -> u32 {
self.latency_points
}
pub fn points_per_frame(&self) -> u32 {
self.point_hz / self.frame_hz
}
pub fn add_points<I>(&mut self, points: I)
where
I: IntoIterator,
I::Item: AsRef<Point>,
{
for p in points {
let p = *p.as_ref();
self.add_lines([p, p].iter().cloned());
}
}
pub fn add_lines<I>(&mut self, points: I)
where
I: IntoIterator,
I::Item: AsRef<Point>,
{
let mut points = points.into_iter();
if let Some(&last) = self.points.last() {
if let Some(next) = points.next() {
let next = next.as_ref();
self.points.push(last.blanked());
self.points.push(next.blanked());
self.points.push(*next);
}
}
self.points.extend(points.map(|p| *p.as_ref()));
}
}
impl Requester {
fn fill_buffer<M, F>(&mut self, model: &mut M, render: F, buffer: &mut Buffer, state: &State)
where
F: RenderFn<M>,
{
if state.frame_hz == 0 {
return;
}
if buffer.is_empty() {
return;
}
let point_hz = buffer.point_hz();
let latency_points = buffer.latency_points();
let mut start = 0;
if !self.raw_points.is_empty() {
if self.raw_points.len() < buffer.len() {
start = self.raw_points.len();
buffer[..start].copy_from_slice(&self.raw_points);
self.raw_points.clear();
} else if self.raw_points.len() == buffer.len() {
buffer.copy_from_slice(&self.raw_points);
self.raw_points.clear();
return;
} else {
let end = buffer.len();
buffer.copy_from_slice(&self.raw_points[..end]);
self.raw_points.drain(0..end);
return;
}
}
let points_per_frame = point_hz / state.frame_hz;
loop {
let num_points_remaining = buffer.len() - start;
let num_points_to_fill = std::cmp::min(points_per_frame as usize, num_points_remaining);
let mut frame = Frame {
point_hz,
latency_points,
frame_hz: state.frame_hz,
points: vec![], };
render(model, &mut frame);
if state.enable_optimisations {
if frame.points.is_empty() {
let blank_point = self
.last_frame_point
.map(|p| p.blanked())
.unwrap_or_else(RawPoint::centered_blank);
self.raw_points
.extend((0..points_per_frame).map(|_| blank_point));
} else {
let segs: Vec<lasy::Segment> = if state.enable_draw_reorder {
let segs = lasy::points_to_segments(frame.iter().cloned());
let pg = lasy::segments_to_point_graph(&frame, segs);
let eg = lasy::point_graph_to_euler_graph(&pg);
let ec = lasy::euler_graph_to_euler_circuit(&frame, &eg);
lasy::euler_circuit_to_segments(&ec, &eg).collect()
} else {
lasy::points_to_segments(frame.iter().cloned()).collect()
};
let last_frame_point = self.last_frame_point.take();
let next_frame_first = segs.first().map(|seg| frame[seg.start as usize]);
inter_frame_blank_points(
last_frame_point,
next_frame_first,
state.interpolation_conf.blank_delay_points,
&mut self.blank_points,
);
let inter_frame_point_count = self.blank_points.len() as u32;
let target_points = if points_per_frame > inter_frame_point_count {
points_per_frame - inter_frame_point_count
} else {
0
};
let interp_conf = &state.interpolation_conf;
let mut interpolated = vec![];
lasy::interpolate_path(
&frame,
segs,
target_points,
interp_conf,
&mut interpolated,
);
if interpolated.is_empty() {
let blank_point = self
.blank_points
.last()
.copied()
.or_else(|| last_frame_point.map(|p| p.blanked()))
.unwrap_or_else(RawPoint::centered_blank);
interpolated.extend((0..target_points).map(|_| blank_point));
}
self.raw_points.append(&mut self.blank_points);
self.raw_points.extend(interpolated);
}
} else {
let last_frame_point = self.last_frame_point.take();
let next_frame_first = frame.iter().cloned().next();
inter_frame_blank_points(
last_frame_point,
next_frame_first,
state.interpolation_conf.blank_delay_points,
&mut self.blank_points,
);
let frame_points = frame
.iter()
.flat_map(|pt| Some(pt.to_raw()).into_iter().chain(pt.to_raw_weighted()));
self.raw_points.append(&mut self.blank_points);
self.raw_points.extend(frame_points);
}
self.last_frame_point = self.raw_points.last().copied();
let end = start + std::cmp::min(num_points_to_fill, self.raw_points.len());
let range = start..end;
buffer[range.clone()].copy_from_slice(&self.raw_points[..range.len()]);
self.raw_points.drain(..range.len());
if end == buffer.len() {
break;
}
start = end;
}
}
}
impl Deref for Frame {
type Target = Vec<Point>;
fn deref(&self) -> &Self::Target {
&self.points
}
}
impl DerefMut for Frame {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.points
}
}
impl<M> Deref for Stream<M> {
type Target = raw::Stream<M>;
fn deref(&self) -> &Self::Target {
&self.raw
}
}
fn inter_frame_blank_points(
last: Option<RawPoint>,
next: Option<Point>,
blank_delay_points: u32,
points: &mut Vec<RawPoint>,
) {
points.clear();
let (last, next) = match (last, next) {
(Some(l), Some(n)) => (l, n),
_ => return,
};
if last.position == next.position {
return;
}
let a = last.blanked().with_weight(0);
let b = next.to_raw().blanked();
points.extend(lasy::blank_segment_points(a, b, blank_delay_points));
}
pub(crate) fn default_process_raw_fn<M>(_model: &mut M, _buffer: &mut Buffer) {}