From 6d66558a72366214e3e05b14c9de508ca9e2ab6b Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Thu, 13 Jun 2024 13:27:18 +0200 Subject: [PATCH] egui: move viewport to its own file --- src/bin/topola-egui/app.rs | 226 ++-------------------------- src/bin/topola-egui/main.rs | 1 + src/bin/topola-egui/viewport.rs | 254 ++++++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+), 215 deletions(-) create mode 100644 src/bin/topola-egui/viewport.rs diff --git a/src/bin/topola-egui/app.rs b/src/bin/topola-egui/app.rs index 5441e06..ea0b510 100644 --- a/src/bin/topola-egui/app.rs +++ b/src/bin/topola-egui/app.rs @@ -40,7 +40,7 @@ use topola::{ }, }; -use crate::{layers::Layers, overlay::Overlay, painter::Painter, top::Top}; +use crate::{layers::Layers, overlay::Overlay, painter::Painter, top::Top, viewport::Viewport}; #[derive(Debug, Default)] pub struct SharedData { @@ -69,7 +69,7 @@ pub struct App { text_channel: (Sender, Receiver), #[serde(skip)] - from_rect: egui::emath::Rect, + viewport: Viewport, #[serde(skip)] top: Top, @@ -85,7 +85,7 @@ impl Default for App { invoker: None, shared_data: Default::default(), text_channel: channel(), - from_rect: egui::Rect::from_x_y_ranges(0.0..=1000000.0, 0.0..=500000.0), + viewport: Viewport::new(), top: Top::new(), layers: None, } @@ -189,7 +189,7 @@ impl eframe::App for App { self.shared_data.clone(), self.text_channel.0.clone(), &self.invoker, - &self.overlay, + &mut self.overlay, ); if let Some(ref mut layers) = self.layers { @@ -198,217 +198,13 @@ impl eframe::App for App { } } - egui::CentralPanel::default().show(ctx, |ui| { - egui::Frame::canvas(ui.style()).show(ui, |ui| { - ui.ctx().request_repaint(); - - let desired_size = ui.available_width() * egui::vec2(1.0, 0.5); - let (_id, viewport_rect) = ui.allocate_space(desired_size); - - let old_transform = - egui::emath::RectTransform::from_to(self.from_rect, viewport_rect); - let latest_pos = old_transform - .inverse() - .transform_pos(ctx.input(|i| i.pointer.latest_pos().unwrap_or_default())); - - let old_scale = old_transform.scale().x; - self.from_rect = self.from_rect / ctx.input(|i| i.zoom_delta()); - - let new_scale = egui::emath::RectTransform::from_to(self.from_rect, viewport_rect) - .scale() - .x; - - self.from_rect = self.from_rect.translate( - ctx.input(|i| latest_pos.to_vec2() * (new_scale - old_scale) / new_scale), - ); - - self.from_rect = self - .from_rect - .translate(ctx.input(|i| -i.raw_scroll_delta / new_scale)); - - let transform = egui::emath::RectTransform::from_to(self.from_rect, viewport_rect); - let mut painter = Painter::new(ui, transform); - - if let Some(invoker_arc_mutex) = &self.invoker { - if ctx.input(|i| i.pointer.any_click()) { - if self.top.is_placing_via { - let invoker_arc_mutex = invoker_arc_mutex.clone(); - - execute(async move { - let mut invoker = invoker_arc_mutex.lock().unwrap(); - invoker.execute( - Command::PlaceVia(ViaWeight { - from_layer: 0, - to_layer: 0, - circle: Circle { - pos: point! {x: latest_pos.x as f64, y: -latest_pos.y as f64}, - r: 10000.0, - }, - maybe_net: Some(1234), - }), - &mut EmptyRouterObserver, - ); - }); - } else if let Some(overlay) = &mut self.overlay { - let invoker = invoker_arc_mutex.lock().unwrap(); - overlay.click( - invoker.autorouter().board(), - point! {x: latest_pos.x as f64, y: -latest_pos.y as f64}, - ); - } - } - - if let (invoker, shared_data, Some(overlay)) = ( - &invoker_arc_mutex.lock().unwrap(), - self.shared_data.lock().unwrap(), - &mut self.overlay, - ) { - let board = invoker.autorouter().board(); - - for primitive in board.layout().drawing().layer_primitive_nodes(1) { - let shape = primitive.primitive(board.layout().drawing()).shape(); - - let color = if shared_data.highlighteds.contains(&primitive) - || overlay - .selection() - .contains_node(board, GenericNode::Primitive(primitive)) - { - egui::Color32::from_rgb(100, 100, 255) - } else { - egui::Color32::from_rgb(52, 52, 200) - }; - painter.paint_primitive(&shape, color); - } - - for zone in board.layout().layer_zone_nodes(1) { - let color = if overlay - .selection() - .contains_node(board, GenericNode::Compound(zone.into())) - { - egui::Color32::from_rgb(100, 100, 255) - } else { - egui::Color32::from_rgb(52, 52, 200) - }; - painter.paint_polygon(&board.layout().zone(zone).shape().polygon, color) - } - - for primitive in board.layout().drawing().layer_primitive_nodes(0) { - let shape = primitive.primitive(board.layout().drawing()).shape(); - - let color = if shared_data.highlighteds.contains(&primitive) - || overlay - .selection() - .contains_node(board, GenericNode::Primitive(primitive)) - { - egui::Color32::from_rgb(255, 100, 100) - } else { - egui::Color32::from_rgb(200, 52, 52) - }; - painter.paint_primitive(&shape, color); - } - - for zone in board.layout().layer_zone_nodes(0) { - let color = if overlay - .selection() - .contains_node(board, GenericNode::Compound(zone.into())) - { - egui::Color32::from_rgb(255, 100, 100) - } else { - egui::Color32::from_rgb(200, 52, 52) - }; - painter.paint_polygon(&board.layout().zone(zone).shape().polygon, color) - } - - if self.top.show_ratsnest { - for edge in overlay.ratsnest().graph().edge_references() { - let from = overlay - .ratsnest() - .graph() - .node_weight(edge.source()) - .unwrap() - .pos; - let to = overlay - .ratsnest() - .graph() - .node_weight(edge.target()) - .unwrap() - .pos; - - painter.paint_edge( - from, - to, - egui::Stroke::new(1.0, egui::Color32::from_rgb(90, 90, 200)), - ); - } - } - - if let Some(navmesh) = &shared_data.navmesh { - for edge in navmesh.edge_references() { - let from = edge - .source() - .primitive(board.layout().drawing()) - .shape() - .center(); - let to = edge - .target() - .primitive(board.layout().drawing()) - .shape() - .center(); - - let stroke = 'blk: { - if let (Some(source_pos), Some(target_pos)) = ( - shared_data - .path - .iter() - .position(|node| *node == edge.source()), - shared_data - .path - .iter() - .position(|node| *node == edge.target()), - ) { - if target_pos == source_pos + 1 - || source_pos == target_pos + 1 - { - break 'blk egui::Stroke::new( - 5.0, - egui::Color32::from_rgb(250, 250, 0), - ); - } - } - - egui::Stroke::new(1.0, egui::Color32::from_rgb(125, 125, 125)) - }; - - painter.paint_edge(from, to, stroke); - } - } - - for ghost in shared_data.ghosts.iter() { - painter.paint_primitive(&ghost, egui::Color32::from_rgb(75, 75, 150)); - } - - if let (Some(from), Some(to)) = (shared_data.from, shared_data.to) { - painter.paint_dot( - Circle { - pos: board.layout().drawing().primitive(from).shape().center(), - r: 20.0, - }, - egui::Color32::from_rgb(255, 255, 100), - ); - painter.paint_dot( - Circle { - pos: board.layout().drawing().primitive(to).shape().center(), - r: 20.0, - }, - egui::Color32::from_rgb(255, 255, 100), - ); - } - - //unreachable!(); - } - } - }) - }); + self.viewport.update( + ctx, + &self.top, + self.shared_data.clone(), + &self.invoker, + &mut self.overlay, + ); if ctx.input(|i| i.key_pressed(egui::Key::Escape)) { ctx.send_viewport_cmd(egui::ViewportCommand::Close); diff --git a/src/bin/topola-egui/main.rs b/src/bin/topola-egui/main.rs index 929412a..3e1fb75 100644 --- a/src/bin/topola-egui/main.rs +++ b/src/bin/topola-egui/main.rs @@ -5,6 +5,7 @@ mod layers; mod overlay; mod painter; mod top; +mod viewport; use app::App; // Build to native. diff --git a/src/bin/topola-egui/viewport.rs b/src/bin/topola-egui/viewport.rs new file mode 100644 index 0000000..28b2cf0 --- /dev/null +++ b/src/bin/topola-egui/viewport.rs @@ -0,0 +1,254 @@ +use std::sync::{Arc, Mutex}; + +use geo::point; +use petgraph::visit::{EdgeRef, IntoEdgeReferences}; +use topola::{ + autorouter::invoker::{Command, Invoker}, + board::mesadata::MesadataTrait, + drawing::{graph::MakePrimitive, primitive::MakePrimitiveShape}, + dsn::mesadata::DsnMesadata, + geometry::{shape::ShapeTrait, GenericNode}, + layout::{via::ViaWeight, zone::MakePolyShape}, + math::Circle, + router::EmptyRouterObserver, +}; + +use crate::{ + app::{execute, SharedData}, + overlay::Overlay, + painter::Painter, + top::Top, +}; + +pub struct Viewport { + from_rect: egui::emath::Rect, +} + +impl Viewport { + pub fn new() -> Self { + Self { + from_rect: egui::Rect::from_x_y_ranges(0.0..=1000000.0, 0.0..=500000.0), + } + } + + pub fn update( + &mut self, + ctx: &egui::Context, + top: &Top, + shared_data: Arc>, + maybe_invoker: &Option>>>, + maybe_overlay: &mut Option, + ) { + egui::CentralPanel::default().show(ctx, |ui| { + egui::Frame::canvas(ui.style()).show(ui, |ui| { + ui.ctx().request_repaint(); + + let desired_size = ui.available_width() * egui::vec2(1.0, 0.5); + let (_id, viewport_rect) = ui.allocate_space(desired_size); + + let old_transform = + egui::emath::RectTransform::from_to(self.from_rect, viewport_rect); + let latest_pos = old_transform + .inverse() + .transform_pos(ctx.input(|i| i.pointer.latest_pos().unwrap_or_default())); + + let old_scale = old_transform.scale().x; + self.from_rect = self.from_rect / ctx.input(|i| i.zoom_delta()); + + let new_scale = egui::emath::RectTransform::from_to(self.from_rect, viewport_rect) + .scale() + .x; + + self.from_rect = self.from_rect.translate( + ctx.input(|i| latest_pos.to_vec2() * (new_scale - old_scale) / new_scale), + ); + + self.from_rect = self + .from_rect + .translate(ctx.input(|i| -i.raw_scroll_delta / new_scale)); + + let transform = egui::emath::RectTransform::from_to(self.from_rect, viewport_rect); + let mut painter = Painter::new(ui, transform); + + if let Some(invoker_arc_mutex) = maybe_invoker { + if ctx.input(|i| i.pointer.any_click()) { + if top.is_placing_via { + let invoker_arc_mutex = invoker_arc_mutex.clone(); + + execute(async move { + let mut invoker = invoker_arc_mutex.lock().unwrap(); + invoker.execute( + Command::PlaceVia(ViaWeight { + from_layer: 0, + to_layer: 0, + circle: Circle { + pos: point! {x: latest_pos.x as f64, y: -latest_pos.y as f64}, + r: 10000.0, + }, + maybe_net: Some(1234), + }), + &mut EmptyRouterObserver, + ); + }); + } else if let Some(overlay) = maybe_overlay { + let invoker = invoker_arc_mutex.lock().unwrap(); + overlay.click( + invoker.autorouter().board(), + point! {x: latest_pos.x as f64, y: -latest_pos.y as f64}, + ); + } + } + + if let (invoker, shared_data, Some(overlay)) = ( + &invoker_arc_mutex.lock().unwrap(), + shared_data.lock().unwrap(), + maybe_overlay, + ) { + let board = invoker.autorouter().board(); + + for primitive in board.layout().drawing().layer_primitive_nodes(1) { + let shape = primitive.primitive(board.layout().drawing()).shape(); + + let color = if shared_data.highlighteds.contains(&primitive) + || overlay + .selection() + .contains_node(board, GenericNode::Primitive(primitive)) + { + egui::Color32::from_rgb(100, 100, 255) + } else { + egui::Color32::from_rgb(52, 52, 200) + }; + painter.paint_primitive(&shape, color); + } + + for zone in board.layout().layer_zone_nodes(1) { + let color = if overlay + .selection() + .contains_node(board, GenericNode::Compound(zone.into())) + { + egui::Color32::from_rgb(100, 100, 255) + } else { + egui::Color32::from_rgb(52, 52, 200) + }; + painter.paint_polygon(&board.layout().zone(zone).shape().polygon, color) + } + + for primitive in board.layout().drawing().layer_primitive_nodes(0) { + let shape = primitive.primitive(board.layout().drawing()).shape(); + + let color = if shared_data.highlighteds.contains(&primitive) + || overlay + .selection() + .contains_node(board, GenericNode::Primitive(primitive)) + { + egui::Color32::from_rgb(255, 100, 100) + } else { + egui::Color32::from_rgb(200, 52, 52) + }; + painter.paint_primitive(&shape, color); + } + + for zone in board.layout().layer_zone_nodes(0) { + let color = if overlay + .selection() + .contains_node(board, GenericNode::Compound(zone.into())) + { + egui::Color32::from_rgb(255, 100, 100) + } else { + egui::Color32::from_rgb(200, 52, 52) + }; + painter.paint_polygon(&board.layout().zone(zone).shape().polygon, color) + } + + if top.show_ratsnest { + for edge in overlay.ratsnest().graph().edge_references() { + let from = overlay + .ratsnest() + .graph() + .node_weight(edge.source()) + .unwrap() + .pos; + let to = overlay + .ratsnest() + .graph() + .node_weight(edge.target()) + .unwrap() + .pos; + + painter.paint_edge( + from, + to, + egui::Stroke::new(1.0, egui::Color32::from_rgb(90, 90, 200)), + ); + } + } + + if let Some(navmesh) = &shared_data.navmesh { + for edge in navmesh.edge_references() { + let from = edge + .source() + .primitive(board.layout().drawing()) + .shape() + .center(); + let to = edge + .target() + .primitive(board.layout().drawing()) + .shape() + .center(); + + let stroke = 'blk: { + if let (Some(source_pos), Some(target_pos)) = ( + shared_data + .path + .iter() + .position(|node| *node == edge.source()), + shared_data + .path + .iter() + .position(|node| *node == edge.target()), + ) { + if target_pos == source_pos + 1 + || source_pos == target_pos + 1 + { + break 'blk egui::Stroke::new( + 5.0, + egui::Color32::from_rgb(250, 250, 0), + ); + } + } + + egui::Stroke::new(1.0, egui::Color32::from_rgb(125, 125, 125)) + }; + + painter.paint_edge(from, to, stroke); + } + } + + for ghost in shared_data.ghosts.iter() { + painter.paint_primitive(&ghost, egui::Color32::from_rgb(75, 75, 150)); + } + + if let (Some(from), Some(to)) = (shared_data.from, shared_data.to) { + painter.paint_dot( + Circle { + pos: board.layout().drawing().primitive(from).shape().center(), + r: 20.0, + }, + egui::Color32::from_rgb(255, 255, 100), + ); + painter.paint_dot( + Circle { + pos: board.layout().drawing().primitive(to).shape().center(), + r: 20.0, + }, + egui::Color32::from_rgb(255, 255, 100), + ); + } + + //unreachable!(); + } + } + }) + }); + } +}