Render received tracks to a laser
This commit is contained in:
parent
e979602156
commit
32e1a98608
6 changed files with 2209 additions and 192 deletions
1765
Cargo.lock
generated
1765
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -7,12 +7,17 @@ edition = "2024"
|
|||
bevy = "0.15.3"
|
||||
bevy_nannou = { git = "https://github.com/nannou-org/nannou", branch = "bevy-refactor", version = "0.1.0", features = ["wayland"] }
|
||||
iyes_perf_ui = "0.4.0"
|
||||
|
||||
nannou_laser = { git = "https://github.com/seem-less/nannou", branch = "helios_laser_DAC" }
|
||||
serde = "1.0.219"
|
||||
serde_json = "1.0.140"
|
||||
zmq = "0.10.0"
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
nannou = "0.19.0"
|
||||
nannou_egui = { version = "0.19.0", features = ["wayland"] }
|
||||
|
||||
|
||||
# Enable max optimizations for dependencies, but not for our code:
|
||||
# (tip from bevy examples for fast compilation/performance trade-off)
|
||||
|
@ -21,4 +26,4 @@ opt-level = 3
|
|||
|
||||
[[bin]]
|
||||
name="renderer"
|
||||
path="src/main.rs"
|
||||
path="src/main.rs"
|
||||
|
|
581
examples/laser_frame_stream_gui.rs
Normal file
581
examples/laser_frame_stream_gui.rs
Normal file
|
@ -0,0 +1,581 @@
|
|||
//! From https://github.com/seem-less/nannou/blob/helios_laser_DAC/examples/laser/laser_frame_stream_gui.rs
|
||||
//! A clone of the `laser_frame_stream.rs` example that allows for configuring laser settings via a
|
||||
//! UI.
|
||||
|
||||
use nannou::geom::Rect;
|
||||
use nannou::prelude::*;
|
||||
use nannou_egui::{self, egui, Egui};
|
||||
use nannou_laser as laser;
|
||||
use std::sync::{mpsc, Arc};
|
||||
|
||||
fn main() {
|
||||
nannou::app(model).update(update).run();
|
||||
}
|
||||
|
||||
struct Model {
|
||||
// A handle to the laser API used for spawning streams and detecting DACs.
|
||||
laser_api: Arc<laser::Api>,
|
||||
// All of the live stream handles.
|
||||
laser_streams: Vec<laser::FrameStream<Laser>>,
|
||||
// A copy of the state that will live on the laser thread so we can present a GUI.
|
||||
laser_model: Laser,
|
||||
// A copy of the laser settings so that we can control them with the GUI.
|
||||
laser_settings: LaserSettings,
|
||||
// For receiving newly detected DACs.
|
||||
dac_rx: mpsc::Receiver<laser::DetectedDac>,
|
||||
// The UI for control over laser parameters and settings.
|
||||
egui: Egui,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Laser {
|
||||
draw_mode: DrawMode,
|
||||
scale: f32,
|
||||
color_profile: RgbProfile,
|
||||
point_weight: u32,
|
||||
test_pattern: TestPattern,
|
||||
}
|
||||
|
||||
struct LaserSettings {
|
||||
point_hz: u32,
|
||||
latency_points: u32,
|
||||
frame_hz: u32,
|
||||
enable_optimisations: bool,
|
||||
distance_per_point: f32,
|
||||
blank_delay_points: u32,
|
||||
radians_per_point: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct RgbProfile {
|
||||
rgb: [f32; 3],
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum DrawMode {
|
||||
Lines,
|
||||
Points,
|
||||
}
|
||||
|
||||
// A collection of laser test patterns. We'll toggle between these with the numeric keys.
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum TestPattern {
|
||||
// A rectangle that outlines the laser's entire field of projection.
|
||||
Rectangle,
|
||||
// A triangle in the centre of the projection field.
|
||||
Triangle,
|
||||
// A crosshair in the centre of the projection field that reaches the edges.
|
||||
Crosshair,
|
||||
// Three vertical lines. One to the far left, one in the centre and one on the right.
|
||||
ThreeVerticalLines,
|
||||
// A circle whose diameter reaches the edges of the projection field.
|
||||
Circle,
|
||||
// A spiral that starts from the centre and revolves out towards the edge of the field.
|
||||
Spiral,
|
||||
}
|
||||
|
||||
impl Default for Laser {
|
||||
fn default() -> Self {
|
||||
Laser {
|
||||
draw_mode: DrawMode::Lines,
|
||||
scale: 1.0,
|
||||
point_weight: laser::Point::DEFAULT_LINE_POINT_WEIGHT,
|
||||
test_pattern: TestPattern::Rectangle,
|
||||
color_profile: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LaserSettings {
|
||||
fn default() -> Self {
|
||||
use laser::stream;
|
||||
use laser::stream::frame::InterpolationConfig;
|
||||
LaserSettings {
|
||||
point_hz: stream::DEFAULT_POINT_HZ,
|
||||
latency_points: stream::points_per_frame(
|
||||
stream::DEFAULT_POINT_HZ,
|
||||
stream::DEFAULT_FRAME_HZ,
|
||||
),
|
||||
frame_hz: stream::DEFAULT_FRAME_HZ,
|
||||
enable_optimisations: true,
|
||||
distance_per_point: InterpolationConfig::DEFAULT_DISTANCE_PER_POINT,
|
||||
blank_delay_points: InterpolationConfig::DEFAULT_BLANK_DELAY_POINTS,
|
||||
radians_per_point: InterpolationConfig::DEFAULT_RADIANS_PER_POINT,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RgbProfile {
|
||||
fn default() -> Self {
|
||||
RgbProfile { rgb: [1.0; 3] }
|
||||
}
|
||||
}
|
||||
|
||||
fn model(app: &App) -> Model {
|
||||
// Create a window to receive keyboard events.
|
||||
let w_id = app
|
||||
.new_window()
|
||||
.size(312, 530)
|
||||
.key_pressed(key_pressed)
|
||||
.raw_event(raw_window_event)
|
||||
.view(view)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// Initialise the state that we want to live on the laser thread and spawn the stream.
|
||||
let laser_settings = LaserSettings::default();
|
||||
let laser_model = Laser::default();
|
||||
|
||||
// TODO Implement `Clone` for `Api` so that we don't have to `Arc` it.
|
||||
let laser_api = Arc::new(laser::Api::new());
|
||||
|
||||
// A channel for receiving newly detected DACs.
|
||||
let (dac_tx, dac_rx) = mpsc::channel();
|
||||
|
||||
// Spawn a thread for detecting the DACs.
|
||||
let laser_api2 = laser_api.clone();
|
||||
std::thread::spawn(move || {
|
||||
let mut detected = std::collections::HashSet::new();
|
||||
|
||||
// detect Helios DACs first since they can't be detected while simultaneously sending data to them
|
||||
for res in laser_api2.detect_dacs(laser::DacVariant::DacVariantHelios) {
|
||||
if let laser::DetectDacs::Helios { previous_dac } = res {
|
||||
if !detected.insert(laser::DetectedDac::from(previous_dac).id()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for detected_helios in &detected {
|
||||
if let laser::dac_manager::Id::Helios { id } = *detected_helios {
|
||||
let dac: laser::helios_dac::NativeHeliosDacParams = id.into();
|
||||
println!("{:#?}", dac);
|
||||
if dac_tx.send(dac.into()).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for Etherdream DAC
|
||||
for res in laser_api2
|
||||
.detect_dacs(laser::DacVariant::DacVariantEtherdream)
|
||||
.expect("failed to start detecting Etherdream DACs")
|
||||
{
|
||||
let dac = res.expect("error occurred during DAC detection");
|
||||
if detected.insert(dac.id()) {
|
||||
println!("{:#?}", dac);
|
||||
if dac_tx.send(dac).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// We'll use a `Vec` to collect laser streams as they appear.
|
||||
let laser_streams = vec![];
|
||||
|
||||
// A user-interface to tweak the settings.
|
||||
let window = app.window(w_id).unwrap();
|
||||
let egui = Egui::from_window(&window);
|
||||
// egui.ctx().set_fonts(fonts());
|
||||
egui.ctx().set_style(style());
|
||||
|
||||
Model {
|
||||
laser_api,
|
||||
laser_settings,
|
||||
laser_model,
|
||||
laser_streams,
|
||||
dac_rx,
|
||||
egui,
|
||||
}
|
||||
}
|
||||
|
||||
// Draw lines or points based on the `DrawMode`.
|
||||
fn add_points<I>(points: I, mode: DrawMode, scale: f32, frame: &mut laser::Frame)
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: AsRef<laser::Point>,
|
||||
{
|
||||
let points = points.into_iter().map(|p| {
|
||||
let mut p = p.as_ref().clone();
|
||||
p.position[0] *= scale;
|
||||
p.position[1] *= scale;
|
||||
p
|
||||
});
|
||||
match mode {
|
||||
DrawMode::Lines => frame.add_lines(points),
|
||||
DrawMode::Points => frame.add_points(points),
|
||||
}
|
||||
}
|
||||
|
||||
fn laser(laser: &mut Laser, frame: &mut laser::Frame) {
|
||||
// Simple constructor for a lit point.
|
||||
let color = laser.color_profile.rgb;
|
||||
let weight = laser.point_weight;
|
||||
let lit_p = |position| laser::Point {
|
||||
position,
|
||||
color,
|
||||
weight,
|
||||
};
|
||||
|
||||
// Retrieve some points to draw based on the pattern.
|
||||
match laser.test_pattern {
|
||||
TestPattern::Rectangle => {
|
||||
let tl = [-1.0, 1.0];
|
||||
let tr = [1.0, 1.0];
|
||||
let br = [1.0, -1.0];
|
||||
let bl = [-1.0, -1.0];
|
||||
let positions = [tl, tr, br, bl, tl];
|
||||
let points = positions.iter().cloned().map(lit_p);
|
||||
add_points(points, laser.draw_mode, laser.scale, frame);
|
||||
}
|
||||
|
||||
TestPattern::Triangle => {
|
||||
let a = [-0.75, -0.75];
|
||||
let b = [0.0, 0.75];
|
||||
let c = [0.75, -0.75];
|
||||
let positions = [a, b, c, a];
|
||||
let points = positions.iter().cloned().map(lit_p);
|
||||
add_points(points, laser.draw_mode, laser.scale, frame);
|
||||
}
|
||||
|
||||
TestPattern::Crosshair => {
|
||||
let xa = [-1.0, 0.0];
|
||||
let xb = [1.0, 0.0];
|
||||
let ya = [0.0, -1.0];
|
||||
let yb = [0.0, 1.0];
|
||||
let x = [lit_p(xa), lit_p(xb)];
|
||||
let y = [lit_p(ya), lit_p(yb)];
|
||||
add_points(&x, laser.draw_mode, laser.scale, frame);
|
||||
add_points(&y, laser.draw_mode, laser.scale, frame);
|
||||
}
|
||||
|
||||
TestPattern::ThreeVerticalLines => {
|
||||
let la = [-1.0, -0.5];
|
||||
let lb = [-1.0, 0.5];
|
||||
let ma = [0.0, 0.5];
|
||||
let mb = [0.0, -0.5];
|
||||
let ra = [1.0, -0.5];
|
||||
let rb = [1.0, 0.5];
|
||||
let l = [lit_p(la), lit_p(lb)];
|
||||
let m = [lit_p(ma), lit_p(mb)];
|
||||
let r = [lit_p(ra), lit_p(rb)];
|
||||
add_points(&l, laser.draw_mode, laser.scale, frame);
|
||||
add_points(&m, laser.draw_mode, laser.scale, frame);
|
||||
add_points(&r, laser.draw_mode, laser.scale, frame);
|
||||
}
|
||||
|
||||
TestPattern::Circle => {
|
||||
let n_points = frame.points_per_frame() as usize / 4;
|
||||
let rect = Rect::from_w_h(2.0, 2.0);
|
||||
let ellipse: Vec<_> = geom::ellipse::Circumference::new(rect, n_points as f32)
|
||||
.map(|[x, y]| lit_p([x, y]))
|
||||
.collect();
|
||||
add_points(&ellipse, laser.draw_mode, laser.scale, frame);
|
||||
}
|
||||
|
||||
TestPattern::Spiral => {
|
||||
let n_points = frame.points_per_frame() as usize / 2;
|
||||
let radius = 1.0;
|
||||
let rings = 5.0;
|
||||
let points = (0..n_points)
|
||||
.map(|i| {
|
||||
let fract = i as f32 / n_points as f32;
|
||||
let mag = fract * radius;
|
||||
let phase = rings * fract * 2.0 * std::f32::consts::PI;
|
||||
let y = mag * -phase.sin();
|
||||
let x = mag * phase.cos();
|
||||
[x, y]
|
||||
})
|
||||
.map(lit_p)
|
||||
.collect::<Vec<_>>();
|
||||
add_points(&points, laser.draw_mode, laser.scale, frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn raw_window_event(_app: &App, model: &mut Model, event: &nannou::winit::event::WindowEvent) {
|
||||
model.egui.handle_raw_event(event);
|
||||
}
|
||||
|
||||
fn update(_app: &App, model: &mut Model, update: Update) {
|
||||
// First, check for new laser DACs.
|
||||
for dac in model.dac_rx.try_recv() {
|
||||
println!("Detected DAC {:?}!", dac.id());
|
||||
let stream = model
|
||||
.laser_api
|
||||
.new_frame_stream(model.laser_model.clone(), laser)
|
||||
.detected_dac(dac)
|
||||
.build()
|
||||
.expect("failed to establish stream with newly detected DAC");
|
||||
model.laser_streams.push(stream);
|
||||
}
|
||||
|
||||
// Check if any streams have dropped out (e.g network issues, DAC turned off) and attempt to
|
||||
// start them again.
|
||||
let mut dropped = vec![];
|
||||
for (i, stream) in model.laser_streams.iter().enumerate() {
|
||||
if stream.is_closed() {
|
||||
dropped.push(i);
|
||||
}
|
||||
}
|
||||
for i in dropped.into_iter().rev() {
|
||||
let stream = model.laser_streams.remove(i);
|
||||
let dac = stream
|
||||
.dac()
|
||||
.expect("`dac` returned `None` even though one was specified during stream creation");
|
||||
let res = stream
|
||||
.close()
|
||||
.expect("stream was unexpectedly already closed from another stream handle")
|
||||
.expect("failed to join stream thread");
|
||||
if let Err(err) = res {
|
||||
eprintln!("Stream closed due to an error: {}", err);
|
||||
}
|
||||
println!("attempting to restart stream with DAC {:?}", dac.id());
|
||||
match model
|
||||
.laser_api
|
||||
.new_frame_stream(model.laser_model.clone(), laser)
|
||||
.detected_dac(dac)
|
||||
.build()
|
||||
{
|
||||
Err(err) => eprintln!("failed to restart stream: {}", err),
|
||||
Ok(stream) => model.laser_streams.push(stream),
|
||||
}
|
||||
}
|
||||
|
||||
// Update the GUI.
|
||||
let Model {
|
||||
ref mut egui,
|
||||
ref laser_streams,
|
||||
ref mut laser_model,
|
||||
ref mut laser_settings,
|
||||
..
|
||||
} = *model;
|
||||
|
||||
egui.set_elapsed_time(update.since_start);
|
||||
let ctx = egui.begin_frame();
|
||||
|
||||
// The timeline area.
|
||||
egui::containers::CentralPanel::default().show(&ctx, |ui| {
|
||||
fn grid_min_col_width(ui: &egui::Ui, n_options: usize) -> f32 {
|
||||
let gap_space = ui.spacing().item_spacing.x * (n_options as f32 - 1.0);
|
||||
let grid_w = ui.available_width();
|
||||
(grid_w - gap_space) / n_options as f32
|
||||
}
|
||||
|
||||
ui.heading("Laser Points");
|
||||
|
||||
let col_w = grid_min_col_width(ui, 2);
|
||||
egui::Grid::new("Mode")
|
||||
.min_col_width(col_w)
|
||||
.max_col_width(col_w)
|
||||
.show(ui, |ui| {
|
||||
use DrawMode::{Lines, Points};
|
||||
let mut changed = false;
|
||||
ui.vertical_centered_justified(|ui| {
|
||||
changed |= ui
|
||||
.selectable_value(&mut laser_model.draw_mode, Lines, "LINES")
|
||||
.changed();
|
||||
});
|
||||
ui.vertical_centered_justified(|ui| {
|
||||
changed |= ui
|
||||
.selectable_value(&mut laser_model.draw_mode, Points, "POINTS")
|
||||
.changed();
|
||||
});
|
||||
if changed {
|
||||
let mode = laser_model.draw_mode;
|
||||
for stream in laser_streams {
|
||||
stream.send(move |laser| laser.draw_mode = mode).ok();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if ui
|
||||
.add(egui::Slider::new(&mut laser_model.scale, 0.0..=1.0).text("Scale"))
|
||||
.changed()
|
||||
{
|
||||
let scale = laser_model.scale;
|
||||
for stream in laser_streams {
|
||||
stream.send(move |laser| laser.scale = scale).ok();
|
||||
}
|
||||
}
|
||||
if ui
|
||||
.add(egui::Slider::new(&mut laser_model.point_weight, 0..=128).text("Point Weight"))
|
||||
.changed()
|
||||
{
|
||||
let scale = laser_model.scale;
|
||||
for stream in laser_streams {
|
||||
stream.send(move |laser| laser.scale = scale).ok();
|
||||
}
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.heading("Laser Settings");
|
||||
|
||||
if ui
|
||||
.add(egui::Slider::new(&mut laser_settings.point_hz, 1_000..=10_000).text("DAC PPS"))
|
||||
.changed()
|
||||
{
|
||||
let hz = laser_settings.point_hz;
|
||||
for stream in laser_streams {
|
||||
stream.set_point_hz(hz).ok();
|
||||
}
|
||||
}
|
||||
if ui
|
||||
.add(egui::Slider::new(&mut laser_settings.latency_points, 10..=1_500).text("Latency"))
|
||||
.changed()
|
||||
{
|
||||
let latency = laser_settings.latency_points;
|
||||
for stream in laser_streams {
|
||||
stream.set_latency_points(latency).ok();
|
||||
}
|
||||
}
|
||||
if ui
|
||||
.add(egui::Slider::new(&mut laser_settings.frame_hz, 1..=120).text("Target FPS"))
|
||||
.changed()
|
||||
{
|
||||
let hz = laser_settings.frame_hz;
|
||||
for stream in laser_streams {
|
||||
stream.set_frame_hz(hz).ok();
|
||||
}
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.heading("Laser Path Interpolation");
|
||||
|
||||
if ui
|
||||
.checkbox(&mut laser_settings.enable_optimisations, "Optimize Path")
|
||||
.changed()
|
||||
{
|
||||
for stream in laser_streams {
|
||||
stream
|
||||
.enable_optimisations(laser_settings.enable_optimisations)
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
if ui
|
||||
.add(
|
||||
egui::Slider::new(&mut laser_settings.distance_per_point, 0.01..=1.0)
|
||||
.text("Distance Per Point"),
|
||||
)
|
||||
.changed()
|
||||
{
|
||||
let distance = laser_settings.distance_per_point;
|
||||
for stream in laser_streams {
|
||||
stream.set_distance_per_point(distance).ok();
|
||||
}
|
||||
}
|
||||
if ui
|
||||
.add(
|
||||
egui::Slider::new(&mut laser_settings.blank_delay_points, 0..=32)
|
||||
.text("Blank Delay (Points)"),
|
||||
)
|
||||
.changed()
|
||||
{
|
||||
let delay = laser_settings.blank_delay_points;
|
||||
for stream in laser_streams {
|
||||
stream.set_blank_delay_points(delay).ok();
|
||||
}
|
||||
}
|
||||
let mut degrees = rad_to_deg(laser_settings.radians_per_point);
|
||||
if ui
|
||||
.add(egui::Slider::new(&mut degrees, 1.0..=180.0).text("Degrees Per Point"))
|
||||
.changed()
|
||||
{
|
||||
let radians = deg_to_rad(degrees);
|
||||
laser_settings.radians_per_point = radians;
|
||||
for stream in laser_streams {
|
||||
stream.set_radians_per_point(radians).ok();
|
||||
}
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.heading("Color Profile");
|
||||
|
||||
if ui
|
||||
.color_edit_button_rgb(&mut laser_model.color_profile.rgb)
|
||||
.changed()
|
||||
{
|
||||
let rgb = laser_model.color_profile.rgb;
|
||||
for stream in laser_streams {
|
||||
stream.send(move |model| model.color_profile.rgb = rgb).ok();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn key_pressed(_app: &App, model: &mut Model, key: Key) {
|
||||
// Send a new pattern to the laser on keys 1, 2, 3 and 4.
|
||||
let new_pattern = match key {
|
||||
Key::Key1 => TestPattern::Rectangle,
|
||||
Key::Key2 => TestPattern::Triangle,
|
||||
Key::Key3 => TestPattern::Crosshair,
|
||||
Key::Key4 => TestPattern::ThreeVerticalLines,
|
||||
Key::Key5 => TestPattern::Circle,
|
||||
Key::Key6 => TestPattern::Spiral,
|
||||
_ => return,
|
||||
};
|
||||
for stream in &model.laser_streams {
|
||||
stream
|
||||
.send(move |laser| laser.test_pattern = new_pattern)
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
fn view(_app: &App, model: &Model, frame: Frame) {
|
||||
model.egui.draw_to_frame(&frame).unwrap();
|
||||
}
|
||||
|
||||
// The following functions are some custom styling preferences in an attempt to improve on the
|
||||
// default egui theming.
|
||||
|
||||
// fn fonts() -> egui::FontDefinitions {
|
||||
// let mut fonts = egui::FontDefinitions::default();
|
||||
// let entries = [
|
||||
// (
|
||||
// egui::TextStyle::Small,
|
||||
// (egui::FontFamily::Proportional, 13.0),
|
||||
// ),
|
||||
// (
|
||||
// egui::TextStyle::Body,
|
||||
// (egui::FontFamily::Proportional, 16.0),
|
||||
// ),
|
||||
// (
|
||||
// egui::TextStyle::Button,
|
||||
// (egui::FontFamily::Proportional, 16.0),
|
||||
// ),
|
||||
// (
|
||||
// egui::TextStyle::Heading,
|
||||
// (egui::FontFamily::Proportional, 20.0),
|
||||
// ),
|
||||
// (
|
||||
// egui::TextStyle::Monospace,
|
||||
// (egui::FontFamily::Monospace, 14.0),
|
||||
// ),
|
||||
// ];
|
||||
// fonts.families.extend(entries.iter().cloned());
|
||||
// fonts
|
||||
// }
|
||||
|
||||
fn style() -> egui::Style {
|
||||
let mut style = egui::Style::default();
|
||||
style.spacing = egui::style::Spacing {
|
||||
item_spacing: egui::Vec2::splat(8.0),
|
||||
// window_margin: egui::Vec2::new(6.0, 6.0),
|
||||
button_padding: egui::Vec2::new(4.0, 2.0),
|
||||
interact_size: egui::Vec2::new(56.0, 24.0),
|
||||
indent: 10.0,
|
||||
icon_width: 20.0,
|
||||
icon_spacing: 1.0,
|
||||
..style.spacing
|
||||
};
|
||||
style.visuals.widgets.inactive.fg_stroke.color = egui::Color32::WHITE;
|
||||
style.visuals.extreme_bg_color = egui::Color32::from_gray(12);
|
||||
style.visuals.faint_bg_color = egui::Color32::from_gray(24);
|
||||
style.visuals.widgets.noninteractive.bg_fill = egui::Color32::from_gray(36);
|
||||
style.visuals.widgets.noninteractive.bg_stroke.color = egui::Color32::BLACK;
|
||||
style.visuals.widgets.noninteractive.fg_stroke.color = egui::Color32::WHITE;
|
||||
style
|
||||
}
|
42
src/main.rs
42
src/main.rs
|
@ -7,9 +7,12 @@ use bevy_nannou::prelude::*;
|
|||
use bevy_nannou::NannouPlugin;
|
||||
use iyes_perf_ui::prelude::*;
|
||||
use nannou_laser::point::Rgb;
|
||||
use nannou_laser::DacVariant;
|
||||
use nannou_laser::DetectedDacError;
|
||||
use trap::laser::apply_homography_matrix;
|
||||
use trap::laser::python_cv_h_into_mat3;
|
||||
use trap::laser::TMP_PYTHON_LASER_H;
|
||||
use trap::laser::TMP_PYTHON_LASER_H_FOR_NANNOU;
|
||||
use trap::shapes::PositionAndIntensity;
|
||||
use trap::tracks::LaserPoints;
|
||||
use trap::tracks::RenderableLines;
|
||||
|
@ -84,9 +87,17 @@ fn setup_laser(mut commands: Commands) {
|
|||
// Initialise the state that we want to live on the laser thread and spawn the stream.
|
||||
let laser_model = LaserModel::new();
|
||||
let _laser_api = laser::Api::new();
|
||||
// dacs = _laser_api.detect_dacs()
|
||||
// let detected_dacs = _laser_api.detect_dacs(DacVariant::DacVariantHelios);
|
||||
|
||||
// while let Ok(res) = detected_dacs {
|
||||
// if let laser::DetectDacs::Helios { previous_dac } = res {
|
||||
// info!("DACS: {:?}", previous_dac);
|
||||
// }
|
||||
// }
|
||||
|
||||
let laser_stream = _laser_api
|
||||
.new_frame_stream(laser_model, laser_frame_producer)
|
||||
// .detected_dac(dac)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
|
@ -134,6 +145,7 @@ fn text2points(position_and_intensity: PositionAndIntensity) -> laser::Point{
|
|||
// }
|
||||
|
||||
const LASER_H: Mat3 = python_cv_h_into_mat3(TMP_PYTHON_LASER_H);
|
||||
// const LASER_H: Mat3 = python_cv_h_into_mat3(TMP_PYTHON_LASER_H_FOR_NANNOU);
|
||||
|
||||
fn laser_frame_producer(model: &mut LaserModel, frame: &mut laser::Frame){
|
||||
|
||||
|
@ -145,21 +157,14 @@ fn laser_frame_producer(model: &mut LaserModel, frame: &mut laser::Frame){
|
|||
// false => trap::shapes::ARE_YOU_SURE,
|
||||
// };
|
||||
|
||||
// let points = positions.iter().cloned().map(text2points);
|
||||
let points = model.current_points.clone();
|
||||
|
||||
// let laser_cv_h: [[f32;3]; 3] = [[ 2.47442963e+02, -7.01714050e+01, -9.71749119e+01],
|
||||
// [ 1.02328119e+01, 1.47185254e+02, 1.96295638e+02],
|
||||
// [-1.20921986e-03, -3.32735973e-02, 1.00000000e+00]];
|
||||
|
||||
|
||||
let mut new_points = Vec::new();
|
||||
for point in points.into_iter() {
|
||||
let p = point.position;
|
||||
let new_position = apply_homography_matrix(LASER_H, &p);
|
||||
// let new_position = laser_h.mul_vec3(v);
|
||||
// TODO: current matrix is shifted by 0xFFF/2 and scaled by 0xFFF/2
|
||||
let s = 0xFFF as f32;
|
||||
// let s = 1.; // when using TMP_PYTHON_LASER_H_FOR_NANNOU -- doesn't work?
|
||||
let s = 0xFFF as f32; // when using TMP_PYTHON_LASER_H
|
||||
|
||||
let new_point = laser::Point {
|
||||
position: [new_position[0]/s, new_position[1]/s],
|
||||
|
@ -168,11 +173,8 @@ fn laser_frame_producer(model: &mut LaserModel, frame: &mut laser::Frame){
|
|||
new_points.push(new_point);
|
||||
}
|
||||
|
||||
// let mut src_points: Vector<Point2f> = Vector::new();
|
||||
// let mut dst_points: Vector<Point2f> = Vector::new();
|
||||
// let homography_transform_matrix = Mat::new();
|
||||
println!("Points {}", new_points.len());
|
||||
println!("{:?}", new_points);
|
||||
info!("Points {}", new_points.len());
|
||||
// println!("{:?}", new_points);
|
||||
frame.add_lines(new_points);
|
||||
}
|
||||
|
||||
|
@ -197,7 +199,7 @@ struct LaserTimer {
|
|||
|
||||
fn exit_system(keys: Res<ButtonInput<KeyCode>>, mut exit: EventWriter<AppExit>) {
|
||||
if keys.just_pressed(KeyCode::KeyQ) {
|
||||
println!("Sending exit command");
|
||||
info!("Sending exit command");
|
||||
exit.send(AppExit::Success);
|
||||
}
|
||||
}
|
||||
|
@ -210,10 +212,9 @@ fn update(
|
|||
tracks: Query<(&Track, &SpawnedTime)>,
|
||||
time: Res<Time>,
|
||||
) {
|
||||
println!("Start update");
|
||||
let mut lines = RenderableLines::new();
|
||||
for (track, created_at) in tracks.iter() {
|
||||
println!("{} {}, history: {}", track.track_id.to_string(), created_at.instant.elapsed().as_millis(), track.history.len());
|
||||
// println!("{} {}, history: {}", track.track_id.to_string(), created_at.instant.elapsed().as_millis(), track.history.len());
|
||||
let rl = RenderableLines::from(track);
|
||||
lines.lines.extend(rl.lines);
|
||||
}
|
||||
|
@ -223,7 +224,7 @@ fn update(
|
|||
laser_timer.timer.tick(time.delta());
|
||||
|
||||
let version = laser_timer.timer.elapsed().as_millis() > 500;
|
||||
println!("{} {}", version, laser_timer.timer.elapsed().as_millis());
|
||||
debug!("{} {}", version, laser_timer.timer.elapsed().as_millis());
|
||||
// let lines = get_laser_lines(version);
|
||||
let points: LaserPoints = (&lines).into();
|
||||
laser_api.laser_stream.send(|laser| {
|
||||
|
@ -231,7 +232,6 @@ fn update(
|
|||
// TODO: homography
|
||||
laser.current_points = laserpoints;
|
||||
}).unwrap();
|
||||
println!(" - sent");
|
||||
}
|
||||
|
||||
for draw in draws.iter() {
|
||||
|
@ -241,6 +241,4 @@ fn update(
|
|||
|
||||
}
|
||||
|
||||
println!("End update");
|
||||
|
||||
}
|
|
@ -4,6 +4,10 @@ pub const TMP_PYTHON_LASER_H: [[f32;3];3] = [[ 2.47442963e+02, -7.01714050e+01,
|
|||
[ 1.02328119e+01, 1.47185254e+02, 1.96295638e+02],
|
||||
[-1.20921986e-03, -3.32735973e-02, 1.00000000e+00]];
|
||||
|
||||
pub const TMP_PYTHON_LASER_H_FOR_NANNOU: [[f32;3]; 3] = [[ 2.47442963e+02/0xFFF as f32, -7.01714050e+01, -9.71749119e+01],
|
||||
[ 1.02328119e+01, 1.47185254e+02/0xFFF as f32, 1.96295638e+02],
|
||||
[-1.20921986e-03, -3.32735973e-02, 1.00000000e+00]];
|
||||
|
||||
pub const fn python_cv_h_into_mat3(m: [[f32;3]; 3]) -> Mat3{
|
||||
Mat3::from_cols(
|
||||
Vec3::new(m[0][0], m[1][0], m[2][0]),
|
||||
|
|
|
@ -30,7 +30,7 @@ fn receive_zmq_messsages(mut commands: Commands, subscriber: NonSend<Socket>, mu
|
|||
subscriber.as_poll_item(zmq::POLLIN)
|
||||
];
|
||||
let _nr = zmq::poll(&mut items, 0).unwrap();
|
||||
// println!("receive {_nr}");
|
||||
|
||||
if items[0].is_readable() {
|
||||
let json = subscriber.recv_string(0).unwrap().unwrap();
|
||||
// dbg!(&json[4..]);
|
||||
|
|
Loading…
Reference in a new issue