Output to a window for projectors
This commit is contained in:
parent
3b5e023da3
commit
9602b46839
1 changed files with 142 additions and 50 deletions
192
src/main.rs
192
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_laser::DacId;
|
||||
use nannou_laser::{self as laser};
|
||||
use serde_json::Result;
|
||||
// use serde_json::Result;
|
||||
use laserspace::trap::filters::{MappedPoint, PointFilters};
|
||||
use laserspace::trap::laser::{shape_rect, LaserPoints, LaserSpace, StreamSource, STREAM_SOURCES, TMP_DESK_CLUBMAX, Corner};
|
||||
use laserspace::trap::tracks::{CoordinateSpace, RenderableLayers, renderable};
|
||||
|
|
@ -66,7 +66,7 @@ struct GuiModel {
|
|||
laser_model: LaserModel,
|
||||
// A copy of the laser settings so that we can control them with the GUI.
|
||||
laser_settings: LaserSettings,
|
||||
per_laser_config: DacConfigMap,
|
||||
per_laser_config: OutputConfigMap,
|
||||
// For receiving newly detected DACs.
|
||||
dac_rx: mpsc::Receiver<laser::DetectedDac>,
|
||||
// The UI for control over laser parameters and settings.
|
||||
|
|
@ -78,7 +78,7 @@ struct GuiModel {
|
|||
// dimming_factor: f32,
|
||||
lost_alpha: f32,
|
||||
connected: bool,
|
||||
selected_stream: Option<DacId>,
|
||||
selected_stream: Option<OutputId>,
|
||||
canvas_scale: f32,
|
||||
canvas_translate: Vec2,
|
||||
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(Debug, Serialize, Deserialize)]
|
||||
#[serde(remote = "DacId")]
|
||||
pub enum DacIdSerializable {
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
pub enum OutputId{
|
||||
EtherDream { mac_address: [u8; 6] },
|
||||
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)]
|
||||
pub struct SavedDacConfig{
|
||||
#[serde(with = "DacIdSerializable")]
|
||||
dac_id: DacId,
|
||||
pub struct SavedOutputConfig{
|
||||
// #[serde(with = "DacIdSerializable")]
|
||||
dac_id: OutputId,
|
||||
config: DacConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct SavedConfig {
|
||||
dacs: Vec<SavedDacConfig>
|
||||
dacs: Vec<SavedOutputConfig>
|
||||
}
|
||||
|
||||
impl Into<DacConfigMap> for SavedConfig {
|
||||
fn into(self) -> DacConfigMap {
|
||||
impl Into<OutputConfigMap> for SavedConfig {
|
||||
fn into(self) -> OutputConfigMap {
|
||||
let mut configs = HashMap::new();
|
||||
for dac in self.dacs {
|
||||
configs.insert(dac.dac_id, dac.config);
|
||||
|
|
@ -269,11 +299,11 @@ impl Into<DacConfigMap> for SavedConfig {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&mut DacConfigMap> for SavedConfig {
|
||||
fn from(per_laser_config: &mut DacConfigMap) -> SavedConfig {
|
||||
impl From<&mut OutputConfigMap> for SavedConfig {
|
||||
fn from(per_output_config: &mut OutputConfigMap) -> SavedConfig {
|
||||
let mut dacs = Vec::new();
|
||||
for (dac_id, config) in per_laser_config.into_iter() {
|
||||
dacs.push(SavedDacConfig{
|
||||
for (dac_id, config) in per_output_config.into_iter() {
|
||||
dacs.push(SavedOutputConfig{
|
||||
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.
|
||||
fn get_dac_configs(config_path: &PathBuf) -> DacConfigMap{
|
||||
fn get_dac_configs(config_path: &PathBuf) -> OutputConfigMap{
|
||||
match read_config_from_file(config_path) {
|
||||
Err(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();
|
||||
}
|
||||
|
||||
fn update(_app: &App, model: &mut GuiModel, update: Update) {
|
||||
fn update(app: &App, model: &mut GuiModel, update: Update) {
|
||||
// First, check for new laser DACs.
|
||||
for dac in model.dac_rx.try_recv() {
|
||||
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");
|
||||
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
|
||||
.laser_api
|
||||
|
|
@ -557,9 +588,10 @@ fn update(_app: &App, model: &mut GuiModel, update: Update) {
|
|||
eprintln!("Stream closed due to an error: {}", err);
|
||||
}
|
||||
// TODO: keeps looping on disconnect.
|
||||
println!("attempting to restart stream with DAC {:?}", 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
|
||||
.laser_api
|
||||
.new_frame_stream(model.laser_model.with_config(config), laser_frame_producer)
|
||||
|
|
@ -728,48 +760,92 @@ fn update(_app: &App, model: &mut GuiModel, update: Update) {
|
|||
ui.separator();
|
||||
ui.heading("Laser specific settings");
|
||||
|
||||
if per_laser_config.is_empty() {
|
||||
ui.label("No dacs available");
|
||||
} else {
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.selectable_value(
|
||||
selected_stream,
|
||||
None,
|
||||
"⊗"
|
||||
);
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.selectable_value(
|
||||
selected_stream,
|
||||
None,
|
||||
"⊗"
|
||||
);
|
||||
if per_laser_config.is_empty() {
|
||||
ui.label("No dacs available");
|
||||
} else {
|
||||
|
||||
for (dac_id, _config) in per_laser_config.iter() {
|
||||
let is_available = laser_streams.contains_key(&dac_id);
|
||||
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
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
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};
|
||||
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{""};
|
||||
// egui::widgets::SelectableLabel cannot have border unless hovered/highlighted
|
||||
// TODO: alternatively underscore with ui.painter().rect/hline(ui.selectable_value().rect.max/min, ....)
|
||||
ui.selectable_value(
|
||||
selected_stream,
|
||||
Some(dac_id.clone()),
|
||||
Some(output_id.clone()),
|
||||
format!("{name}{indicator}")
|
||||
);
|
||||
}
|
||||
// reset
|
||||
ui.style_mut().visuals.override_text_color = None;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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_stream_value) = selected_stream {
|
||||
if let Some(selected_output) = selected_stream {
|
||||
|
||||
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));
|
||||
|
||||
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");
|
||||
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -791,7 +867,7 @@ fn update(_app: &App, model: &mut GuiModel, update: Update) {
|
|||
|
||||
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
for layer_nr in 1..=8 {
|
||||
for layer_nr in 0..8 {
|
||||
let mask = 1 << layer_nr;
|
||||
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
|
||||
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 {
|
||||
stream.send(move |laser_model: &mut LaserModel| {
|
||||
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
|
||||
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 {
|
||||
stream.send(move |laser_model: &mut LaserModel| {
|
||||
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
|
||||
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();
|
||||
|
||||
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
|
||||
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();
|
||||
|
||||
if let Some(stream) = selected_laser_stream {
|
||||
|
|
|
|||
Loading…
Reference in a new issue