Output to a window for projectors
This commit is contained in:
parent
3b5e023da3
commit
9602b46839
1 changed files with 142 additions and 50 deletions
178
src/main.rs
178
src/main.rs
|
|
@ -11,7 +11,7 @@ use bevy::prelude::Mat3; // for glam::f32::Mat3, which is distinct from nannou::
|
||||||
use nannou_egui::{self, egui, Egui};
|
use nannou_egui::{self, egui, Egui};
|
||||||
use nannou_laser::DacId;
|
use nannou_laser::DacId;
|
||||||
use nannou_laser::{self as laser};
|
use nannou_laser::{self as laser};
|
||||||
use serde_json::Result;
|
// use serde_json::Result;
|
||||||
use laserspace::trap::filters::{MappedPoint, PointFilters};
|
use laserspace::trap::filters::{MappedPoint, PointFilters};
|
||||||
use laserspace::trap::laser::{shape_rect, LaserPoints, LaserSpace, StreamSource, STREAM_SOURCES, TMP_DESK_CLUBMAX, Corner};
|
use laserspace::trap::laser::{shape_rect, LaserPoints, LaserSpace, StreamSource, STREAM_SOURCES, TMP_DESK_CLUBMAX, Corner};
|
||||||
use laserspace::trap::tracks::{CoordinateSpace, RenderableLayers, renderable};
|
use laserspace::trap::tracks::{CoordinateSpace, RenderableLayers, renderable};
|
||||||
|
|
@ -66,7 +66,7 @@ struct GuiModel {
|
||||||
laser_model: LaserModel,
|
laser_model: LaserModel,
|
||||||
// A copy of the laser settings so that we can control them with the GUI.
|
// A copy of the laser settings so that we can control them with the GUI.
|
||||||
laser_settings: LaserSettings,
|
laser_settings: LaserSettings,
|
||||||
per_laser_config: DacConfigMap,
|
per_laser_config: OutputConfigMap,
|
||||||
// For receiving newly detected DACs.
|
// For receiving newly detected DACs.
|
||||||
dac_rx: mpsc::Receiver<laser::DetectedDac>,
|
dac_rx: mpsc::Receiver<laser::DetectedDac>,
|
||||||
// The UI for control over laser parameters and settings.
|
// The UI for control over laser parameters and settings.
|
||||||
|
|
@ -78,7 +78,7 @@ struct GuiModel {
|
||||||
// dimming_factor: f32,
|
// dimming_factor: f32,
|
||||||
lost_alpha: f32,
|
lost_alpha: f32,
|
||||||
connected: bool,
|
connected: bool,
|
||||||
selected_stream: Option<DacId>,
|
selected_stream: Option<OutputId>,
|
||||||
canvas_scale: f32,
|
canvas_scale: f32,
|
||||||
canvas_translate: Vec2,
|
canvas_translate: Vec2,
|
||||||
canvas_dragging_corner: Option<Corner>,
|
canvas_dragging_corner: Option<Corner>,
|
||||||
|
|
@ -235,32 +235,62 @@ fn zmq_receive(model: &mut GuiModel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type DacConfigMap = HashMap<DacId, DacConfig>;
|
type OutputConfigMap = HashMap<OutputId, DacConfig>;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||||
|
pub enum OutputId{
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
#[serde(remote = "DacId")]
|
|
||||||
pub enum DacIdSerializable {
|
|
||||||
EtherDream { mac_address: [u8; 6] },
|
EtherDream { mac_address: [u8; 6] },
|
||||||
Helios { id: u32 },
|
Helios { id: u32 },
|
||||||
|
Display { monitor: u32, enabled: bool }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
impl From<DacId> for OutputId {
|
||||||
|
fn from(dac_id: DacId) -> OutputId {
|
||||||
|
match dac_id {
|
||||||
|
DacId::EtherDream { mac_address } => OutputId::EtherDream { mac_address },
|
||||||
|
DacId::Helios {id } => OutputId::Helios { id },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryInto<DacId> for OutputId {
|
||||||
|
type Error = OutputId;
|
||||||
|
|
||||||
|
fn try_into(self) -> Result<DacId, Self::Error> {
|
||||||
|
match self {
|
||||||
|
OutputId::EtherDream { mac_address } => Ok(DacId::EtherDream { mac_address }),
|
||||||
|
OutputId::Helios { id } => Ok(DacId::Helios { id }),
|
||||||
|
// Return the original OutputId in the Err variant
|
||||||
|
// if it doesn't match any DacId variant
|
||||||
|
other => Err(other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[derive(Debug, Serialize, Deserialize)]
|
||||||
|
// #[serde(remote = "DacId")]
|
||||||
|
// pub enum DacIdSerializable {
|
||||||
|
// EtherDream { mac_address: [u8; 6] },
|
||||||
|
// Helios { id: u32 },
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct SavedDacConfig{
|
pub struct SavedOutputConfig{
|
||||||
#[serde(with = "DacIdSerializable")]
|
// #[serde(with = "DacIdSerializable")]
|
||||||
dac_id: DacId,
|
dac_id: OutputId,
|
||||||
config: DacConfig,
|
config: DacConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct SavedConfig {
|
pub struct SavedConfig {
|
||||||
dacs: Vec<SavedDacConfig>
|
dacs: Vec<SavedOutputConfig>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<DacConfigMap> for SavedConfig {
|
impl Into<OutputConfigMap> for SavedConfig {
|
||||||
fn into(self) -> DacConfigMap {
|
fn into(self) -> OutputConfigMap {
|
||||||
let mut configs = HashMap::new();
|
let mut configs = HashMap::new();
|
||||||
for dac in self.dacs {
|
for dac in self.dacs {
|
||||||
configs.insert(dac.dac_id, dac.config);
|
configs.insert(dac.dac_id, dac.config);
|
||||||
|
|
@ -269,11 +299,11 @@ impl Into<DacConfigMap> for SavedConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&mut DacConfigMap> for SavedConfig {
|
impl From<&mut OutputConfigMap> for SavedConfig {
|
||||||
fn from(per_laser_config: &mut DacConfigMap) -> SavedConfig {
|
fn from(per_output_config: &mut OutputConfigMap) -> SavedConfig {
|
||||||
let mut dacs = Vec::new();
|
let mut dacs = Vec::new();
|
||||||
for (dac_id, config) in per_laser_config.into_iter() {
|
for (dac_id, config) in per_output_config.into_iter() {
|
||||||
dacs.push(SavedDacConfig{
|
dacs.push(SavedOutputConfig{
|
||||||
dac_id: dac_id.clone(), config: config.clone()
|
dac_id: dac_id.clone(), config: config.clone()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -305,7 +335,7 @@ fn save_config_file<P: AsRef<Path>>(path: P, config: SavedConfig) -> std::result
|
||||||
|
|
||||||
|
|
||||||
// Some hardcoded config. Not spending time on reading/writing config atm.
|
// Some hardcoded config. Not spending time on reading/writing config atm.
|
||||||
fn get_dac_configs(config_path: &PathBuf) -> DacConfigMap{
|
fn get_dac_configs(config_path: &PathBuf) -> OutputConfigMap{
|
||||||
match read_config_from_file(config_path) {
|
match read_config_from_file(config_path) {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("Could not load config {}", err);
|
eprintln!("Could not load config {}", err);
|
||||||
|
|
@ -501,16 +531,17 @@ fn double_send_settings_at_connect(laser_settings: &LaserSettings, stream: &lase
|
||||||
stream.set_radians_per_point(laser_settings.radians_per_point).ok();
|
stream.set_radians_per_point(laser_settings.radians_per_point).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(_app: &App, model: &mut GuiModel, update: Update) {
|
fn update(app: &App, model: &mut GuiModel, update: Update) {
|
||||||
// First, check for new laser DACs.
|
// First, check for new laser DACs.
|
||||||
for dac in model.dac_rx.try_recv() {
|
for dac in model.dac_rx.try_recv() {
|
||||||
println!("Detected DAC {:?}!", dac.id());
|
println!("Detected DAC {:?}!", dac.id());
|
||||||
if !model.per_laser_config.contains_key(&dac.id()) {
|
let output_id: OutputId = dac.id().into();
|
||||||
|
if !model.per_laser_config.contains_key(&output_id) {
|
||||||
println!("Found unknown DAC, register with defaults");
|
println!("Found unknown DAC, register with defaults");
|
||||||
model.per_laser_config.insert(dac.id(), DacConfig::default());
|
model.per_laser_config.insert(output_id.clone(), DacConfig::default());
|
||||||
|
|
||||||
}
|
}
|
||||||
let config = &model.per_laser_config[&dac.id()];
|
let config = &model.per_laser_config[&output_id];
|
||||||
|
|
||||||
let stream = model
|
let stream = model
|
||||||
.laser_api
|
.laser_api
|
||||||
|
|
@ -557,9 +588,10 @@ fn update(_app: &App, model: &mut GuiModel, update: Update) {
|
||||||
eprintln!("Stream closed due to an error: {}", err);
|
eprintln!("Stream closed due to an error: {}", err);
|
||||||
}
|
}
|
||||||
// TODO: keeps looping on disconnect.
|
// TODO: keeps looping on disconnect.
|
||||||
println!("attempting to restart stream with DAC {:?}", dac.id());
|
|
||||||
let dac_id = dac.id();
|
let dac_id = dac.id();
|
||||||
let config = &model.per_laser_config[&dac.id()];
|
println!("attempting to restart stream with DAC {:?}", &dac_id);
|
||||||
|
let output_id: OutputId = dac_id.clone().into();
|
||||||
|
let config = &model.per_laser_config[&output_id];
|
||||||
match model
|
match model
|
||||||
.laser_api
|
.laser_api
|
||||||
.new_frame_stream(model.laser_model.with_config(config), laser_frame_producer)
|
.new_frame_stream(model.laser_model.with_config(config), laser_frame_producer)
|
||||||
|
|
@ -728,49 +760,93 @@ fn update(_app: &App, model: &mut GuiModel, update: Update) {
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.heading("Laser specific settings");
|
ui.heading("Laser specific settings");
|
||||||
|
|
||||||
if per_laser_config.is_empty() {
|
|
||||||
ui.label("No dacs available");
|
|
||||||
} else {
|
|
||||||
ui.horizontal_wrapped(|ui| {
|
ui.horizontal_wrapped(|ui| {
|
||||||
ui.selectable_value(
|
ui.selectable_value(
|
||||||
selected_stream,
|
selected_stream,
|
||||||
None,
|
None,
|
||||||
"⊗"
|
"⊗"
|
||||||
);
|
);
|
||||||
|
if per_laser_config.is_empty() {
|
||||||
|
ui.label("No dacs available");
|
||||||
|
} else {
|
||||||
|
|
||||||
|
for (output_id, _config) in per_laser_config.iter() {
|
||||||
|
let is_available = match &output_id.clone().try_into() {
|
||||||
|
Ok(dac_id) => laser_streams.contains_key(dac_id),
|
||||||
|
Err(_output_id) => {
|
||||||
|
if let OutputId::Display { enabled, .. } = output_id {
|
||||||
|
*enabled
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
for (dac_id, _config) in per_laser_config.iter() {
|
|
||||||
let is_available = laser_streams.contains_key(&dac_id);
|
|
||||||
ui.style_mut().visuals.override_text_color = if is_available {Some(egui::Color32::GREEN)} else {None};
|
ui.style_mut().visuals.override_text_color = if is_available {Some(egui::Color32::GREEN)} else {None};
|
||||||
ui.style_mut().visuals.widgets.inactive.bg_stroke = if is_available {egui::Stroke::new(2.0, egui::Color32::GREEN)} else {egui::Stroke::NONE};
|
ui.style_mut().visuals.widgets.inactive.bg_stroke = if is_available {egui::Stroke::new(2.0, egui::Color32::GREEN)} else {egui::Stroke::NONE};
|
||||||
let name = if let Some(config) = per_laser_config.get(&dac_id) { config.name.clone() } else { "DAC".into() };
|
let name = if let Some(config) = per_laser_config.get(&output_id) { config.name.clone() } else { "DAC".into() };
|
||||||
let indicator = if is_available{" 🔌"}else{""};
|
let indicator = if is_available{" 🔌"}else{""};
|
||||||
// egui::widgets::SelectableLabel cannot have border unless hovered/highlighted
|
// egui::widgets::SelectableLabel cannot have border unless hovered/highlighted
|
||||||
// TODO: alternatively underscore with ui.painter().rect/hline(ui.selectable_value().rect.max/min, ....)
|
// TODO: alternatively underscore with ui.painter().rect/hline(ui.selectable_value().rect.max/min, ....)
|
||||||
ui.selectable_value(
|
ui.selectable_value(
|
||||||
selected_stream,
|
selected_stream,
|
||||||
Some(dac_id.clone()),
|
Some(output_id.clone()),
|
||||||
format!("{name}{indicator}")
|
format!("{name}{indicator}")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// reset
|
// reset
|
||||||
ui.style_mut().visuals.override_text_color = None;
|
ui.style_mut().visuals.override_text_color = None;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(selected_stream_value) = selected_stream {
|
if ui.button("+").clicked() {
|
||||||
|
let output_id = OutputId::Display { monitor: 0, enabled: false };
|
||||||
|
per_laser_config.insert(output_id, DacConfig::default());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(selected_output) = selected_stream {
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.add(egui::Label::new(format!("{:?}", selected_stream_value)));
|
ui.add(egui::Label::new(format!("{:?}", selected_output)));
|
||||||
|
|
||||||
let selected_config: &mut DacConfig = per_laser_config.get_mut(&selected_stream_value).unwrap();
|
let selected_config: &mut DacConfig = per_laser_config.get_mut(&selected_output).unwrap();
|
||||||
|
|
||||||
ui.add(egui::TextEdit::singleline(&mut selected_config.name));
|
ui.add(egui::TextEdit::singleline(&mut selected_config.name));
|
||||||
|
|
||||||
let selected_laser_stream = laser_streams.get(&selected_stream_value);
|
let selected_laser_stream = match selected_output.clone().try_into() {
|
||||||
|
Ok(dac_id) => laser_streams.get(&dac_id),
|
||||||
|
Err(_output_id) => None,
|
||||||
|
} ;
|
||||||
|
|
||||||
// let stream_config: &mut StreamConfig = laser_streams.get_mut(&selected_stream_value).expect("Selected stream not found in configs");
|
// let stream_config: &mut StreamConfig = laser_streams.get_mut(&selected_stream_value).expect("Selected stream not found in configs");
|
||||||
|
|
||||||
|
|
||||||
|
if let OutputId::Display { monitor, enabled: fullscreen } = selected_output {
|
||||||
|
let monitors = app.available_monitors();
|
||||||
|
// monitors.get(0).unwrap().name()
|
||||||
|
|
||||||
|
// fullscreen = FullScreen::Borderless(Some(monitor_handle));
|
||||||
|
// app.new_window().fullscreen_with(fullscreen)
|
||||||
|
|
||||||
|
let mut selected_monitor_name = String::from("haha");
|
||||||
|
|
||||||
|
egui::ComboBox::from_label("Monitor")
|
||||||
|
.selected_text(format!("MONITOR -- {selected_monitor_name:?}"))
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
for monitor_option in monitors {
|
||||||
|
if let Some(monitor_name) = monitor_option.name() {
|
||||||
|
|
||||||
|
if ui.selectable_value(&mut selected_monitor_name, monitor_name.clone(), format!("{:?}", &monitor_name)).clicked() {
|
||||||
|
if let Some(stream) = selected_laser_stream {
|
||||||
|
// TODO: make this for monitor: switch it
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let source = &mut selected_config.source;
|
let source = &mut selected_config.source;
|
||||||
|
|
||||||
egui::ComboBox::from_label("Source")
|
egui::ComboBox::from_label("Source")
|
||||||
|
|
@ -791,7 +867,7 @@ fn update(_app: &App, model: &mut GuiModel, update: Update) {
|
||||||
|
|
||||||
|
|
||||||
ui.horizontal_wrapped(|ui| {
|
ui.horizontal_wrapped(|ui| {
|
||||||
for layer_nr in 1..=8 {
|
for layer_nr in 0..8 {
|
||||||
let mask = 1 << layer_nr;
|
let mask = 1 << layer_nr;
|
||||||
let mut enabled_bool = mask & selected_config.layers_enabled != 0;
|
let mut enabled_bool = mask & selected_config.layers_enabled != 0;
|
||||||
|
|
||||||
|
|
@ -1284,7 +1360,11 @@ fn laser_mouse_moved(app: &App, model: &mut GuiModel, pos: Point2) {
|
||||||
// 3. update config in laser stream threat
|
// 3. update config in laser stream threat
|
||||||
let homography = config.filters.homography.clone();
|
let homography = config.filters.homography.clone();
|
||||||
|
|
||||||
let selected_laser_stream = model.laser_streams.get(&dac_id);
|
let selected_laser_stream = match dac_id.clone().try_into() {
|
||||||
|
Ok(dac_id) => model.laser_streams.get(&dac_id),
|
||||||
|
Err(_output_id) => None,
|
||||||
|
} ;
|
||||||
|
|
||||||
if let Some(stream) = selected_laser_stream {
|
if let Some(stream) = selected_laser_stream {
|
||||||
stream.send(move |laser_model: &mut LaserModel| {
|
stream.send(move |laser_model: &mut LaserModel| {
|
||||||
laser_model.config.filters.homography = homography
|
laser_model.config.filters.homography = homography
|
||||||
|
|
@ -1305,7 +1385,11 @@ fn laser_mouse_moved(app: &App, model: &mut GuiModel, pos: Point2) {
|
||||||
// 3. update config in laser stream threat
|
// 3. update config in laser stream threat
|
||||||
let mask = config.filters.clip.mask.clone();
|
let mask = config.filters.clip.mask.clone();
|
||||||
|
|
||||||
let selected_laser_stream = model.laser_streams.get(&dac_id);
|
let selected_laser_stream = match dac_id.clone().try_into() {
|
||||||
|
Ok(dac_id) => model.laser_streams.get(&dac_id),
|
||||||
|
Err(_output_id) => None,
|
||||||
|
} ;
|
||||||
|
|
||||||
if let Some(stream) = selected_laser_stream {
|
if let Some(stream) = selected_laser_stream {
|
||||||
stream.send(move |laser_model: &mut LaserModel| {
|
stream.send(move |laser_model: &mut LaserModel| {
|
||||||
laser_model.config.filters.clip.mask = mask;
|
laser_model.config.filters.clip.mask = mask;
|
||||||
|
|
@ -1431,7 +1515,11 @@ fn map_mouse_moved(_app: &App, model: &mut GuiModel, pos: Point2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. propagate to laser stream threat
|
// 3. propagate to laser stream threat
|
||||||
let selected_laser_stream = model.laser_streams.get(&dac_id);
|
let selected_laser_stream = match dac_id.clone().try_into() {
|
||||||
|
Ok(dac_id) => model.laser_streams.get(&dac_id),
|
||||||
|
Err(_output_id) => None,
|
||||||
|
} ;
|
||||||
|
|
||||||
let homography = config.filters.homography.clone();
|
let homography = config.filters.homography.clone();
|
||||||
|
|
||||||
if let Some(stream) = selected_laser_stream {
|
if let Some(stream) = selected_laser_stream {
|
||||||
|
|
@ -1540,7 +1628,11 @@ fn map_mouse_pressed(app: &App, model: &mut GuiModel, button: MouseButton) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// propagate to laser dac streamer
|
// propagate to laser dac streamer
|
||||||
let selected_laser_stream = model.laser_streams.get(&dac_id);
|
let selected_laser_stream = match dac_id.clone().try_into() {
|
||||||
|
Ok(dac_id) => model.laser_streams.get(&dac_id),
|
||||||
|
Err(_output_id) => None,
|
||||||
|
} ;
|
||||||
|
|
||||||
let homography = config.filters.homography.clone();
|
let homography = config.filters.homography.clone();
|
||||||
|
|
||||||
if let Some(stream) = selected_laser_stream {
|
if let Some(stream) = selected_laser_stream {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue