diff --git a/src/ui/panes/pid_drawing_tool.rs b/src/ui/panes/pid_drawing_tool.rs index 0b68be7608c1178dec86908d3a729a420c7870c9..35a766e5d90c57ee8de6f4e1de77a9c1a02990ff 100644 --- a/src/ui/panes/pid_drawing_tool.rs +++ b/src/ui/panes/pid_drawing_tool.rs @@ -13,7 +13,7 @@ use grid::GridInfo; use serde::{Deserialize, Serialize}; use std::f32::consts::PI; use strum::IntoEnumIterator; -use symbols::Symbol; +use symbols::{icons::Icon, Symbol}; use crate::ui::{composable_view::PaneResponse, utils::egui_to_glam}; @@ -213,16 +213,7 @@ impl PidPane { self.action = Some(Action::Connect(elem_idx)); ui.close_menu(); } - if ui.button("Rotate 90° ⟲").clicked() { - self.elements[elem_idx].rotate(-PI / 2.0); - self.action.take(); - ui.close_menu(); - } - if ui.button("Rotate 90° ⟳").clicked() { - self.elements[elem_idx].rotate(PI / 2.0); - self.action.take(); - ui.close_menu(); - } + self.elements[elem_idx].context_menu(ui); if ui.button("Delete").clicked() { self.delete_element(elem_idx); self.action.take(); @@ -251,7 +242,20 @@ impl PidPane { } else { ui.menu_button("Symbols", |ui| { for symbol in Symbol::iter() { - if ui.button(symbol.to_string()).clicked() { + if let Symbol::Icon(_) = symbol { + ui.menu_button("Icons", |ui| { + for icon in Icon::iter() { + if ui.button(icon.to_string()).clicked() { + self.elements.push(Element::new( + self.grid.screen_to_grid(pointer_pos).round(), + Symbol::Icon(icon), + )); + self.action.take(); + ui.close_menu(); + } + } + }); + } else if ui.button(symbol.to_string()).clicked() { self.elements.push(Element::new( self.grid.screen_to_grid(pointer_pos).round(), symbol, @@ -322,8 +326,8 @@ impl PidPane { self.grid.apply_scroll_delta(scroll_delta, pointer_pos); // Invalidate the cache to redraw the images - for symbol in Symbol::iter() { - let img: egui::ImageSource = symbol.get_image(theme); + for icon in Icon::iter() { + let img: egui::ImageSource = icon.get_image(theme); ui.ctx().forget_image(img.uri().unwrap()); } } diff --git a/src/ui/panes/pid_drawing_tool/elements.rs b/src/ui/panes/pid_drawing_tool/elements.rs index db0c3154333913f315f573f46f4bc5d08b03161c..638f5ca4eee6781f0c58a56f13245b8e39249158 100644 --- a/src/ui/panes/pid_drawing_tool/elements.rs +++ b/src/ui/panes/pid_drawing_tool/elements.rs @@ -1,14 +1,14 @@ -use crate::{msg_broker, ui::utils::glam_to_egui}; +use std::f32::consts::PI; -use super::grid::GridInfo; -use super::symbols::Symbol; -use crate::error::ErrInstrument; -use egui::{Rect, Theme, Ui}; +use super::{ + grid::GridInfo, + symbols::{Symbol, SymbolBehavior}, +}; +use egui::{Theme, Ui}; use glam::{Mat2, Vec2}; use serde::{Deserialize, Serialize}; #[derive(Clone, Serialize, Deserialize, Debug)] -#[serde(from = "SerialElement")] pub struct Element { /// Anchor postion in grid coordinates, top-left corner position: glam::Vec2, @@ -18,13 +18,6 @@ pub struct Element { /// Rotation in radiants rotation: f32, - - /// Anchor point in grid coordinates relative to the element's center - /// - /// These vectors include the current rotation of the element. - /// They are cached to avoid recomputing the rotation. - #[serde(skip)] - anchor_points: Vec<Vec2>, } impl PartialEq for Element { @@ -40,7 +33,6 @@ impl Element { Self { position: center - symbol.size() / 2.0, rotation: 0.0, - anchor_points: symbol.anchor_points(), symbol, } } @@ -72,9 +64,28 @@ impl Element { pub fn change_symbol(&mut self, symbol: Symbol) { self.symbol = symbol; + } + + pub fn get_symbol(&self) -> Symbol { + self.symbol.clone() + } - // Anchor points can be different between symbols, realod the cache - self.reload_anchor_points(); + pub fn context_menu(&mut self, ui: &mut Ui) { + match &mut self.symbol { + Symbol::Icon(_) => { + if ui.button("Rotate 90° ⟲").clicked() { + self.rotate(-PI / 2.0); + ui.close_menu(); + } + if ui.button("Rotate 90° ⟳").clicked() { + self.rotate(PI / 2.0); + ui.close_menu(); + } + } + Symbol::Label(label) => { + label.context_menu(ui); + } + } } /// Rotate the element by its center @@ -87,31 +98,23 @@ impl Element { // Update absolute rotation self.rotation += rotation; - - // Recompute anchor points cache - self.reload_anchor_points(); - } - - fn reload_anchor_points(&mut self) { - // Rotation matrix from element's frame to grid's frame - let rotm_e_to_g = Mat2::from_angle(self.rotation); - - // Then rotate the anchor points - self.anchor_points = self - .symbol - .anchor_points() - .iter() - .map(|&p| rotm_e_to_g * p) - .collect(); } /// Returns the position of one anchor point in grid coordinates pub fn anchor_point(&self, idx: usize) -> Vec2 { - self.anchor_points[idx] + self.position + if let Some(anchor_points) = self.symbol.anchor_points() { + // Rotation matrix from element's frame to grid's frame + let rotm_e_to_g = Mat2::from_angle(self.rotation); + + // Then rotate and translate the anchor points + rotm_e_to_g * anchor_points[idx] + self.position + } else { + Vec2::ZERO + } } pub fn anchor_points_len(&self) -> usize { - self.anchor_points.len() + self.symbol.anchor_points().map_or(0, |v| v.len()) } /// Size in grid units @@ -125,35 +128,8 @@ impl Element { } pub fn draw(&mut self, grid: &GridInfo, ui: &Ui, theme: Theme) { - let center = glam_to_egui(grid.grid_to_screen(self.position)).to_pos2(); - let image_rect = Rect::from_min_size(center, glam_to_egui(self.size() * grid.size())); - - egui::Image::new(self.symbol.get_image(theme)) - .rotate(self.rotation, egui::Vec2::splat(0.0)) - .paint_at(ui, image_rect); - - if let Symbol::MotorValve(motor_valve) = &mut self.symbol { - msg_broker!().refresh_view(motor_valve).log_expect("bruh"); - } - } -} - -#[derive(Deserialize)] -pub struct SerialElement { - position: glam::Vec2, - symbol: Symbol, - rotation: f32, -} - -impl From<SerialElement> for Element { - fn from(value: SerialElement) -> Self { - let mut value = Self { - position: value.position, - symbol: value.symbol, - rotation: value.rotation, - anchor_points: Vec::new(), - }; - value.reload_anchor_points(); - value + let pos = grid.grid_to_screen(self.position); + let size = grid.size(); + self.symbol.paint(ui, theme, pos, size, self.rotation); } } diff --git a/src/ui/panes/pid_drawing_tool/symbols.rs b/src/ui/panes/pid_drawing_tool/symbols.rs index d4b92f3ed0f2f811c2b7f6d6b31cf2f1d6d32454..4f8f4a8b26befc953a20d79578743b82893eb20e 100644 --- a/src/ui/panes/pid_drawing_tool/symbols.rs +++ b/src/ui/panes/pid_drawing_tool/symbols.rs @@ -1,169 +1,64 @@ -mod motor_valve; +pub mod icons; +mod labels; -use egui::{ImageSource, Theme}; +use crate::mavlink::ViewId; +use egui::{Theme, Ui}; +use enum_dispatch::enum_dispatch; use glam::Vec2; -use motor_valve::MotorValve; +use icons::Icon; +use labels::Label; use serde::{Deserialize, Serialize}; use strum_macros::{Display, EnumIter}; -use crate::mavlink::ViewId; - #[derive(Clone, Serialize, Deserialize, PartialEq, EnumIter, Display, Debug)] +#[enum_dispatch] pub enum Symbol { - Arrow, - BurstDisk, - CheckValve, - FlexibleConnection, - ManualValve, - MotorValve(MotorValve), - PressureGauge, - PressureRegulator, - PressureTransducer, - QuickConnector, - ReliefValve, - ThreeWayValve, - Vessel, + Icon(Icon), + Label(Label), } -impl Symbol { - pub fn get_image(&self, theme: Theme) -> ImageSource { - match (&self, theme) { - (Symbol::Arrow, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/arrow.svg") - } - (Symbol::Arrow, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/arrow.svg") - } - (Symbol::BurstDisk, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/burst_disk.svg") - } - (Symbol::BurstDisk, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/burst_disk.svg") - } - (Symbol::ManualValve, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/manual_valve.svg") - } - (Symbol::ManualValve, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/manual_valve.svg") - } - (Symbol::CheckValve, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/check_valve.svg") - } - (Symbol::CheckValve, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/check_valve.svg") - } - (Symbol::ReliefValve, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/relief_valve.svg") - } - (Symbol::ReliefValve, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/relief_valve.svg") - } - (Symbol::MotorValve(state), Theme::Light) => match state.last_value { - None => egui::include_image!("../../../../icons/pid_symbols/light/motor_valve.svg"), - Some(true) => { - egui::include_image!( - "../../../../icons/pid_symbols/light/motor_valve_green.svg" - ) - } - Some(false) => { - egui::include_image!("../../../../icons/pid_symbols/light/motor_valve_red.svg") - } - }, - (Symbol::MotorValve(state), Theme::Dark) => match state.last_value { - None => egui::include_image!("../../../../icons/pid_symbols/dark/motor_valve.svg"), - Some(true) => { - egui::include_image!("../../../../icons/pid_symbols/dark/motor_valve_green.svg") - } - Some(false) => { - egui::include_image!("../../../../icons/pid_symbols/dark/motor_valve_red.svg") - } - }, - (Symbol::ThreeWayValve, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/three_way_valve.svg") - } - (Symbol::ThreeWayValve, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/three_way_valve.svg") - } - (Symbol::PressureRegulator, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/pressure_regulator.svg") - } - (Symbol::PressureRegulator, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/pressure_regulator.svg") - } - (Symbol::QuickConnector, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/quick_connector.svg") - } - (Symbol::QuickConnector, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/quick_connector.svg") - } - (Symbol::PressureTransducer, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/pressure_transducer.svg") - } - (Symbol::PressureTransducer, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/pressure_transducer.svg") - } - (Symbol::PressureGauge, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/pressure_gauge.svg") - } - (Symbol::PressureGauge, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/pressure_gauge.svg") - } - (Symbol::FlexibleConnection, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/flexible_connection.svg") - } - (Symbol::FlexibleConnection, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/flexible_connection.svg") - } - (Symbol::Vessel, Theme::Light) => { - egui::include_image!("../../../../icons/pid_symbols/light/vessel.svg") - } - (Symbol::Vessel, Theme::Dark) => { - egui::include_image!("../../../../icons/pid_symbols/dark/vessel.svg") - } - } +impl Default for Symbol { + fn default() -> Self { + Symbol::Icon(Icon::default()) } +} + +#[enum_dispatch(Symbol)] +pub trait SymbolBehavior { + fn paint(&mut self, ui: &Ui, theme: Theme, pos: Vec2, size: f32, rotation: f32); + + /// Anchor point in grid coordinates relative to the element's center + /// + /// These vectors include the current rotation of the element. + /// They are cached to avoid recomputing the rotation. + fn anchor_points(&self) -> Option<Vec<Vec2>>; /// Symbol size in grid coordinates - pub fn size(&self) -> Vec2 { - match self { - Symbol::Arrow => (4.0, 4.0), - Symbol::BurstDisk => (4.0, 6.0), - Symbol::CheckValve => (10.0, 5.0), - Symbol::FlexibleConnection => (10.0, 6.0), - Symbol::ManualValve => (10.0, 5.0), - Symbol::MotorValve(_) => (10.0, 8.0), - Symbol::PressureGauge => (7.0, 7.0), - Symbol::PressureRegulator => (10.0, 10.0), - Symbol::PressureTransducer => (7.0, 7.0), - Symbol::QuickConnector => (6.0, 5.0), - Symbol::ReliefValve => (6.0, 10.0), - Symbol::ThreeWayValve => (10.0, 8.0), - Symbol::Vessel => (8.2, 15.2), - } - .into() - } + fn size(&self) -> Vec2; - /// Anchor point position relative to top right corner in grid units - pub fn anchor_points(&self) -> Vec<Vec2> { - match self { - Symbol::Arrow => vec![(0.0, 2.0), (4.0, 2.0)], - Symbol::BurstDisk => vec![(0.0, 3.0), (4.0, 3.0)], - Symbol::CheckValve => vec![(0.0, 2.5), (10.0, 2.5)], - Symbol::FlexibleConnection => vec![(0.0, 3.0), (10.0, 3.0)], - Symbol::ManualValve => vec![(0.0, 2.5), (10.0, 2.5)], - Symbol::MotorValve(_) => vec![(0.0, 5.0), (10.0, 5.0)], - Symbol::PressureGauge => vec![(3.5, 7.0)], - Symbol::PressureRegulator => vec![(0.0, 7.0), (10.0, 7.0)], - Symbol::PressureTransducer => vec![(3.5, 7.0)], - Symbol::QuickConnector => vec![(0.0, 2.5), (6.0, 2.5)], - Symbol::ReliefValve => vec![(3.0, 10.0)], - Symbol::ThreeWayValve => vec![(0.0, 3.0), (10.0, 3.0), (5.0, 8.0)], - Symbol::Vessel => vec![(0.0, 7.6), (8.2, 7.6), (4.1, 0.0), (4.1, 15.1)], - } - .iter() - .map(|&p| p.into()) - .collect() - } + // /// Anchor point position relative to top right corner in grid units + // pub fn anchor_points(&self) -> Vec<Vec2> { + // match self { + // Symbol::Arrow => vec![(0.0, 2.0), (4.0, 2.0)], + // Symbol::BurstDisk => vec![(0.0, 3.0), (4.0, 3.0)], + // Symbol::CheckValve => vec![(0.0, 2.5), (10.0, 2.5)], + // Symbol::FlexibleConnection => vec![(0.0, 3.0), (10.0, 3.0)], + // Symbol::ManualValve => vec![(0.0, 2.5), (10.0, 2.5)], + // Symbol::MotorValve(_) => vec![(0.0, 5.0), (10.0, 5.0)], + // Symbol::PressureGauge => vec![(3.5, 7.0)], + // Symbol::PressureRegulator => vec![(0.0, 7.0), (10.0, 7.0)], + // Symbol::PressureTransducer => vec![(3.5, 7.0)], + // Symbol::QuickConnector => vec![(0.0, 2.5), (6.0, 2.5)], + // Symbol::ReliefValve => vec![(3.0, 10.0)], + // Symbol::ThreeWayValve => vec![(0.0, 3.0), (10.0, 3.0), (5.0, 8.0)], + // Symbol::Vessel => vec![(0.0, 7.6), (8.2, 7.6), (4.1, 0.0), (4.1, 15.1)], + // } + // .iter() + // .map(|&p| p.into()) + // .collect() + // } + + fn context_menu(&mut self, ui: &mut Ui) {} } /// Single MavLink value source info diff --git a/src/ui/panes/pid_drawing_tool/symbols/icons.rs b/src/ui/panes/pid_drawing_tool/symbols/icons.rs index 66192cc3f40199b4f83ecec56c3592415186713c..3ba10fe7b80793e1d20f5a42e3a09d49b342da4f 100644 --- a/src/ui/panes/pid_drawing_tool/symbols/icons.rs +++ b/src/ui/panes/pid_drawing_tool/symbols/icons.rs @@ -6,7 +6,7 @@ use motor_valve::MotorValve; use serde::{Deserialize, Serialize}; use strum_macros::{Display, EnumIter}; -use crate::ui::utils::glam_to_egui; +use crate::{mavlink::ViewId, ui::utils::glam_to_egui}; use super::SymbolBehavior; @@ -214,7 +214,7 @@ struct MavlinkValue { field: String, #[serde(skip)] - view_id: egui::Id, + view_id: ViewId, } #[derive(Deserialize)] @@ -228,7 +228,7 @@ impl From<SerialMavlinkValue> for MavlinkValue { Self { msg_id: value.msg_id, field: value.field, - view_id: egui::Id::new(""), + view_id: ViewId::new(), } } } diff --git a/src/ui/panes/pid_drawing_tool/symbols/icons/motor_valve.rs b/src/ui/panes/pid_drawing_tool/symbols/icons/motor_valve.rs index af4f38f55f56eef934762ba41ab9218be32d8111..09819c6f1e26a087185386955b340e0e51338457 100644 --- a/src/ui/panes/pid_drawing_tool/symbols/icons/motor_valve.rs +++ b/src/ui/panes/pid_drawing_tool/symbols/icons/motor_valve.rs @@ -1,4 +1,4 @@ -use crate::mavlink::{extract_from_message, MavlinkResult, MessageView, TimedMessage}; +use crate::mavlink::{extract_from_message, MavlinkResult, MessageView, TimedMessage, ViewId}; use super::MavlinkValue; @@ -19,7 +19,7 @@ impl Default for MotorValve { source: MavlinkValue { msg_id: orion::GSE_TM_DATA::ID, field: "n2o_filling_valve_state".to_string(), - view_id: egui::Id::new(""), + view_id: ViewId::new(), }, last_value: None, } @@ -27,8 +27,8 @@ impl Default for MotorValve { } impl MessageView for MotorValve { - fn widget_id(&self) -> &egui::Id { - &self.source.view_id + fn view_id(&self) -> crate::mavlink::ViewId { + self.source.view_id } fn id_of_interest(&self) -> u32 {