From 8d59242f3f0256494620f2f9b6949de42aafdc74 Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Sun, 21 Apr 2024 19:51:47 +0200 Subject: [PATCH] overlay,egui: impl. incomplete ratsnest generation and drawing For now only doing Delaunay triangulation, then applying minimum spanning tree (without properly working edge ordering yet). --- src/bin/topola-egui/app.rs | 60 +++++++++++++++---------- src/bin/topola-egui/painter.rs | 10 ++++- src/bin/topola-sdl2-demo/main.rs | 24 +++++----- src/overlay/mod.rs | 1 + src/overlay/overlay.rs | 22 +++++---- src/overlay/ratsnest.rs | 76 ++++++++++++++++++++++++++++++++ src/router/navmesh.rs | 20 ++++----- src/router/router.rs | 8 ++-- src/triangulation.rs | 53 +++++++++++++--------- 9 files changed, 193 insertions(+), 81 deletions(-) create mode 100644 src/overlay/ratsnest.rs diff --git a/src/bin/topola-egui/app.rs b/src/bin/topola-egui/app.rs index 1c6762d..1af0d65 100644 --- a/src/bin/topola-egui/app.rs +++ b/src/bin/topola-egui/app.rs @@ -1,5 +1,6 @@ use futures::executor; use geo::point; +use petgraph::visit::{EdgeRef, IntoEdgeReferences}; use std::{ future::Future, sync::mpsc::{channel, Receiver, Sender}, @@ -10,7 +11,7 @@ use topola::{ dsn::{design::DsnDesign, rules::DsnRules}, geometry::{ compound::CompoundManagerTrait, - primitive::{BendShape, DotShape, PrimitiveShape, SegShape}, + primitive::{BendShape, DotShape, PrimitiveShape, PrimitiveShapeTrait, SegShape}, GenericNode, }, layout::{zone::MakePolyShape, Layout}, @@ -25,13 +26,13 @@ use crate::painter::Painter; #[serde(default)] pub struct App { #[serde(skip)] - overlay: Overlay, + maybe_overlay: Option, #[serde(skip)] text_channel: (Sender, Receiver), #[serde(skip)] - layout: Option>, + maybe_layout: Option>, #[serde(skip)] from_rect: egui::emath::Rect, @@ -40,9 +41,9 @@ pub struct App { impl Default for App { fn default() -> Self { Self { - overlay: Overlay::new(), + maybe_overlay: None, text_channel: channel(), - layout: None, + maybe_layout: None, from_rect: egui::Rect::from_x_y_ranges(0.0..=1000000.0, 0.0..=500000.0), } } @@ -71,12 +72,16 @@ impl eframe::App for App { if cfg!(target_arch = "wasm32") { if let Ok(file_contents) = self.text_channel.1.try_recv() { let design = DsnDesign::load_from_string(file_contents).unwrap(); - self.layout = Some(design.make_layout()); + let layout = design.make_layout(); + self.maybe_overlay = Some(Overlay::new(&layout).unwrap()); + self.maybe_layout = Some(layout); } } else { if let Ok(path) = self.text_channel.1.try_recv() { let design = DsnDesign::load_from_file(&path).unwrap(); - self.layout = Some(design.make_layout()); + let layout = design.make_layout(); + self.maybe_overlay = Some(Overlay::new(&layout).unwrap()); + self.maybe_layout = Some(layout); } } @@ -144,9 +149,10 @@ impl eframe::App for App { let transform = egui::emath::RectTransform::from_to(self.from_rect, viewport_rect); let mut painter = Painter::new(ui, transform); - if let Some(layout) = &self.layout { + if let (Some(layout), Some(overlay)) = (&self.maybe_layout, &mut self.maybe_overlay) + { if ctx.input(|i| i.pointer.any_click()) { - self.overlay.click( + overlay.click( layout, point! {x: latest_pos.x as f64, y: -latest_pos.y as f64}, ); @@ -155,8 +161,7 @@ impl eframe::App for App { for primitive in layout.drawing().layer_primitive_nodes(1) { let shape = primitive.primitive(layout.drawing()).shape(); - let color = if self - .overlay + let color = if overlay .selection() .contains(&GenericNode::Primitive(primitive)) { @@ -168,11 +173,7 @@ impl eframe::App for App { } for zone in layout.layer_zones(1) { - let color = if self - .overlay - .selection() - .contains(&GenericNode::Compound(zone)) - { + let color = if overlay.selection().contains(&GenericNode::Compound(zone)) { egui::Color32::from_rgb(100, 100, 255) } else { egui::Color32::from_rgb(52, 52, 200) @@ -189,8 +190,7 @@ impl eframe::App for App { for primitive in layout.drawing().layer_primitive_nodes(0) { let shape = primitive.primitive(layout.drawing()).shape(); - let color = if self - .overlay + let color = if overlay .selection() .contains(&GenericNode::Primitive(primitive)) { @@ -202,11 +202,7 @@ impl eframe::App for App { } for zone in layout.layer_zones(0) { - let color = if self - .overlay - .selection() - .contains(&GenericNode::Compound(zone)) - { + let color = if overlay.selection().contains(&GenericNode::Compound(zone)) { egui::Color32::from_rgb(255, 100, 100) } else { egui::Color32::from_rgb(200, 52, 52) @@ -219,6 +215,24 @@ impl eframe::App for App { color, ) } + + 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::Color32::from_rgb(90, 90, 200)); + } + //unreachable!(); } }) }); diff --git a/src/bin/topola-egui/painter.rs b/src/bin/topola-egui/painter.rs index 88e3b2b..5b7c6d1 100644 --- a/src/bin/topola-egui/painter.rs +++ b/src/bin/topola-egui/painter.rs @@ -70,6 +70,14 @@ impl<'a> Painter<'a> { } pub fn paint_edge(&mut self, from: Point, to: Point, color: Color32) { - // + self.ui.painter().add(epaint::Shape::line_segment( + [ + self.transform + .transform_pos([from.x() as f32, -from.y() as f32].into()), + self.transform + .transform_pos([to.x() as f32, -to.y() as f32].into()), + ], + Stroke::new(1.0, color), + )); } } diff --git a/src/bin/topola-sdl2-demo/main.rs b/src/bin/topola-sdl2-demo/main.rs index c11c100..2c8e897 100644 --- a/src/bin/topola-sdl2-demo/main.rs +++ b/src/bin/topola-sdl2-demo/main.rs @@ -24,7 +24,7 @@ use topola::dsn::design::DsnDesign; use topola::geometry::primitive::{PrimitiveShape, PrimitiveShapeTrait}; use topola::layout::connectivity::BandIndex; use topola::layout::Layout; -use topola::mesh::{Mesh, MeshEdgeReference, VertexIndex}; +use topola::navmesh::{Navmesh, NavmeshEdgeReference, VertexIndex}; use topola::router::RouterObserverTrait; use sdl2::event::Event; @@ -87,12 +87,12 @@ struct EmptyRouterObserver; impl RouterObserverTrait for EmptyRouterObserver { fn on_rework(&mut self, _tracer: &Tracer, _trace: &Trace) {} - fn before_probe(&mut self, _tracer: &Tracer, _trace: &Trace, _edge: MeshEdgeReference) {} + fn before_probe(&mut self, _tracer: &Tracer, _trace: &Trace, _edge: NavmeshEdgeReference) {} fn on_probe( &mut self, _tracer: &Tracer, _trace: &Trace, - _edge: MeshEdgeReference, + _edge: NavmeshEdgeReference, _result: Result<(), DrawException>, ) { } @@ -135,7 +135,7 @@ impl<'a, R: RulesTrait> RouterObserverTrait for DebugRouterObserver<'a> { self.view, RouterOrLayout::Layout(tracer.layout.drawing()), None, - Some(tracer.mesh.clone()), + Some(tracer.navmesh.clone()), &trace.path, &[], &[], @@ -143,7 +143,7 @@ impl<'a, R: RulesTrait> RouterObserverTrait for DebugRouterObserver<'a> { ); } - fn before_probe(&mut self, tracer: &Tracer, trace: &Trace, edge: MeshEdgeReference) { + fn before_probe(&mut self, tracer: &Tracer, trace: &Trace, edge: NavmeshEdgeReference) { let mut path = trace.path.clone(); path.push(edge.target()); render_times( @@ -154,7 +154,7 @@ impl<'a, R: RulesTrait> RouterObserverTrait for DebugRouterObserver<'a> { self.view, RouterOrLayout::Layout(tracer.layout.drawing()), None, - Some(tracer.mesh.clone()), + Some(tracer.navmesh.clone()), &path, &[], &[], @@ -166,7 +166,7 @@ impl<'a, R: RulesTrait> RouterObserverTrait for DebugRouterObserver<'a> { &mut self, tracer: &Tracer, trace: &Trace, - _edge: MeshEdgeReference, + _edge: NavmeshEdgeReference, result: Result<(), DrawException>, ) { let (ghosts, highlighteds, delay) = match result { @@ -186,7 +186,7 @@ impl<'a, R: RulesTrait> RouterObserverTrait for DebugRouterObserver<'a> { self.view, RouterOrLayout::Layout(tracer.layout.drawing()), None, - Some(tracer.mesh.clone()), + Some(tracer.navmesh.clone()), &trace.path, &ghosts, &highlighteds, @@ -324,7 +324,7 @@ fn render_times( view: &mut View, mut router_or_layout: RouterOrLayout, maybe_band: Option, - mut maybe_mesh: Option, + mut maybe_navmesh: Option, path: &[VertexIndex], ghosts: &[PrimitiveShape], highlighteds: &[PrimitiveIndex], @@ -389,7 +389,7 @@ fn render_times( ), ) .ok(); - maybe_mesh = None; + maybe_navmesh = None; } router.layout.drawing() @@ -440,8 +440,8 @@ fn render_times( painter.paint_shape(&ghost, ColorU::new(75, 75, 150, 255), view.zoom); } - if let Some(ref mesh) = maybe_mesh { - for edge in mesh.edge_references() { + if let Some(ref navmesh) = maybe_navmesh { + for edge in navmesh.edge_references() { let to = edge.source().primitive(drawing).shape().center(); let from = edge.target().primitive(drawing).shape().center(); diff --git a/src/overlay/mod.rs b/src/overlay/mod.rs index d67f202..b8a8c17 100644 --- a/src/overlay/mod.rs +++ b/src/overlay/mod.rs @@ -1,3 +1,4 @@ mod overlay; +pub mod ratsnest; pub use overlay::*; diff --git a/src/overlay/overlay.rs b/src/overlay/overlay.rs index c7bbb2b..4e72b75 100644 --- a/src/overlay/overlay.rs +++ b/src/overlay/overlay.rs @@ -2,10 +2,11 @@ use std::collections::HashSet; use geo::Point; use rstar::AABB; +use spade::InsertionError; use crate::{ drawing::{ - graph::{GetLayer, MakePrimitive, PrimitiveIndex}, + graph::{GetLayer, MakePrimitive}, primitive::MakePrimitiveShape, rules::RulesTrait, }, @@ -13,24 +14,23 @@ use crate::{ compound::CompoundManagerTrait, shape::{Shape, ShapeTrait}, }, - graph::GenericIndex, - layout::{ - zone::{MakePolyShape, ZoneWeight}, - Layout, NodeIndex, - }, + layout::{zone::MakePolyShape, Layout, NodeIndex}, + overlay::ratsnest::Ratsnest, }; pub struct Overlay { + ratsnest: Ratsnest, selection: HashSet, active_layer: u64, } impl Overlay { - pub fn new() -> Self { - Self { + pub fn new(layout: &Layout) -> Result { + Ok(Self { + ratsnest: Ratsnest::new(layout)?, selection: HashSet::new(), active_layer: 0, - } + }) } pub fn click(&mut self, layout: &Layout, at: Point) { @@ -88,6 +88,10 @@ impl Overlay { } } + pub fn ratsnest(&self) -> &Ratsnest { + &self.ratsnest + } + pub fn selection(&self) -> &HashSet { &self.selection } diff --git a/src/overlay/ratsnest.rs b/src/overlay/ratsnest.rs new file mode 100644 index 0000000..d3f5041 --- /dev/null +++ b/src/overlay/ratsnest.rs @@ -0,0 +1,76 @@ +use geo::Point; +use petgraph::{ + data::FromElements, + stable_graph::StableUnGraph, + visit::{self, EdgeRef, NodeIndexable}, +}; +use spade::{HasPosition, InsertionError, Point2}; + +use crate::{ + drawing::{ + dot::FixedDotIndex, + graph::{MakePrimitive, PrimitiveIndex}, + primitive::MakePrimitiveShape, + rules::RulesTrait, + }, + geometry::primitive::PrimitiveShapeTrait, + layout::Layout, + triangulation::{GetVertexIndex, Triangulation}, +}; + +#[derive(Debug, Clone, Copy)] +pub struct TriangulationWeight { + vertex: FixedDotIndex, + pub pos: Point, +} + +impl GetVertexIndex for TriangulationWeight { + fn vertex_index(&self) -> FixedDotIndex { + self.vertex + } +} + +impl HasPosition for TriangulationWeight { + type Scalar = f64; + fn position(&self) -> Point2 { + Point2::new(self.pos.x(), self.pos.y()) + } +} + +pub struct Ratsnest { + graph: StableUnGraph, +} + +impl Ratsnest { + pub fn new(layout: &Layout) -> Result { + let mut this = Self { + graph: StableUnGraph::default(), + }; + + let mut triangulation = + Triangulation::new(layout.drawing().geometry().graph().node_bound()); + + for node in layout.drawing().primitive_nodes() { + let center = node.primitive(layout.drawing()).shape().center(); + + match node { + PrimitiveIndex::FixedDot(dot) => { + triangulation.add_vertex(TriangulationWeight { + vertex: dot, + pos: center, + })?; + } + _ => (), + } + } + + this.graph = + StableUnGraph::from_elements(petgraph::algo::min_spanning_tree(&triangulation)); + + Ok(this) + } + + pub fn graph(&self) -> &StableUnGraph { + &self.graph + } +} diff --git a/src/router/navmesh.rs b/src/router/navmesh.rs index e4bb404..d118cf7 100644 --- a/src/router/navmesh.rs +++ b/src/router/navmesh.rs @@ -64,7 +64,7 @@ struct TriangulationWeight { } impl GetVertexIndex for TriangulationWeight { - fn vertex(&self) -> TriangulationVertexIndex { + fn vertex_index(&self) -> TriangulationVertexIndex { self.vertex } } @@ -153,12 +153,12 @@ impl visit::Data for Navmesh { } #[derive(Debug, Clone, Copy)] -pub struct MeshEdgeReference { +pub struct NavmeshEdgeReference { from: VertexIndex, to: VertexIndex, } -impl visit::EdgeRef for MeshEdgeReference { +impl visit::EdgeRef for NavmeshEdgeReference { type NodeId = VertexIndex; type EdgeId = (VertexIndex, VertexIndex); type Weight = (); @@ -203,7 +203,7 @@ impl<'a> visit::IntoNeighbors for &'a Navmesh { fn edge_with_near_edges( triangulation: &Triangulation, edge: TriangulationEdgeReference, -) -> impl Iterator { +) -> impl Iterator { let mut from_vertices = vec![edge.source().into()]; // Append rails to the source. @@ -230,15 +230,15 @@ fn edge_with_near_edges( from_vertices .into_iter() .cartesian_product(to_vertices.into_iter()) - .map(|pair| MeshEdgeReference { + .map(|pair| NavmeshEdgeReference { from: pair.0, to: pair.1.into(), }) } impl<'a> visit::IntoEdgeReferences for &'a Navmesh { - type EdgeRef = MeshEdgeReference; - type EdgeReferences = Box + 'a>; + type EdgeRef = NavmeshEdgeReference; + type EdgeReferences = Box + 'a>; fn edge_references(self) -> Self::EdgeReferences { Box::new( @@ -253,7 +253,7 @@ fn vertex_edges( triangulation: &Triangulation, from: VertexIndex, to: TriangulationVertexIndex, -) -> impl Iterator { +) -> impl Iterator { let from_vertices = vec![from]; let mut to_vertices = vec![to.into()]; @@ -270,14 +270,14 @@ fn vertex_edges( from_vertices .into_iter() .cartesian_product(to_vertices.into_iter()) - .map(|pair| MeshEdgeReference { + .map(|pair| NavmeshEdgeReference { from: pair.0, to: pair.1, }) } impl<'a> visit::IntoEdges for &'a Navmesh { - type Edges = Box + 'a>; + type Edges = Box + 'a>; fn edges(self, vertex: Self::NodeId) -> Self::Edges { Box::new( diff --git a/src/router/router.rs b/src/router/router.rs index 96b5ef7..e343b93 100644 --- a/src/router/router.rs +++ b/src/router/router.rs @@ -17,7 +17,7 @@ use crate::layout::Layout; use crate::router::{ astar::{astar, AstarStrategy, PathTracker}, draw::DrawException, - navmesh::{MeshEdgeReference, Navmesh, VertexIndex}, + navmesh::{Navmesh, NavmeshEdgeReference, VertexIndex}, tracer::{Trace, Tracer}, }; @@ -41,12 +41,12 @@ pub enum RoutingErrorKind { pub trait RouterObserverTrait { fn on_rework(&mut self, tracer: &Tracer, trace: &Trace); - fn before_probe(&mut self, tracer: &Tracer, trace: &Trace, edge: MeshEdgeReference); + fn before_probe(&mut self, tracer: &Tracer, trace: &Trace, edge: NavmeshEdgeReference); fn on_probe( &mut self, tracer: &Tracer, trace: &Trace, - edge: MeshEdgeReference, + edge: NavmeshEdgeReference, result: Result<(), DrawException>, ); fn on_estimate(&mut self, tracer: &Tracer, vertex: VertexIndex); @@ -94,7 +94,7 @@ impl<'a, RO: RouterObserverTrait, R: RulesTrait> AstarStrategy<&Navmesh, f64> self.tracer.finish(&mut self.trace, self.to, width).is_ok() } - fn edge_cost(&mut self, edge: MeshEdgeReference) -> Option { + fn edge_cost(&mut self, edge: NavmeshEdgeReference) -> Option { self.observer.before_probe(&self.tracer, &self.trace, edge); if edge.target() == self.to.into() { return None; diff --git a/src/triangulation.rs b/src/triangulation.rs index 0677631..f9ef0d7 100644 --- a/src/triangulation.rs +++ b/src/triangulation.rs @@ -7,7 +7,7 @@ use spade::{handles::FixedVertexHandle, DelaunayTriangulation, HasPosition, Inse use crate::graph::GetNodeIndex; pub trait GetVertexIndex { - fn vertex(&self) -> I; + fn vertex_index(&self) -> I; } #[derive(Debug, Clone)] @@ -31,7 +31,7 @@ impl + HasPosition Result<(), InsertionError> { - let index = weight.vertex().node_index().index(); + let index = weight.vertex_index().node_index().index(); self.vertex_to_handle[index] = Some(spade::Triangulation::insert( &mut self.triangulation, weight, @@ -60,7 +60,7 @@ impl + HasPosition I { spade::Triangulation::vertex(&self.triangulation, handle) .as_ref() - .vertex() + .vertex_index() } fn handle(&self, vertex: I) -> FixedVertexHandle { @@ -78,7 +78,7 @@ impl + HasPosition + HasPosition> visit::Data for Triangulation { - type NodeWeight = (); + type NodeWeight = W; type EdgeWeight = (); } @@ -169,53 +169,62 @@ impl<'a, I: Copy + PartialEq + GetNodeIndex, W: GetVertexIndex + HasPosition< spade::Triangulation::fixed_vertices(&self.triangulation).map(|vertex| { spade::Triangulation::s(&self.triangulation) .vertex_data(vertex) - .vertex() + .vertex_index() }), ) } } -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct TriangulationVertexReference { +/*#[derive(Debug, Clone, Copy, PartialEq)] +pub struct TriangulationVertexReference { index: I, + weight: &'a W, } -impl visit::NodeRef for TriangulationVertexReference { +impl visit::NodeRef for TriangulationVertexReference { type NodeId = I; - type Weight = (); + type Weight = W; fn id(&self) -> Self::NodeId { self.index } fn weight(&self) -> &Self::Weight { - &() + self.weight } -} +}*/ impl<'a, I: Copy + PartialEq + GetNodeIndex, W: GetVertexIndex + HasPosition> visit::IntoNodeReferences for &'a Triangulation { - type NodeRef = TriangulationVertexReference; - type NodeReferences = Box> + 'a>; + /*type NodeRef = TriangulationVertexReference; + type NodeReferences = Box> + 'a>;*/ + type NodeRef = (I, &'a W); + type NodeReferences = Box + 'a>; + fn node_references(self) -> Self::NodeReferences { Box::new( spade::Triangulation::fixed_vertices(&self.triangulation).map(|vertex| { - TriangulationVertexReference { - index: spade::Triangulation::s(&self.triangulation) - .vertex_data(vertex) - .vertex(), - } + let weight = spade::Triangulation::s(&self.triangulation).vertex_data(vertex); + /*TriangulationVertexReference { + index: weight.vertex_index(), + weight, + }*/ + (weight.vertex_index(), weight) }), ) } } -impl<'a, I: Copy + PartialEq + GetNodeIndex, W: GetVertexIndex + HasPosition> - visit::NodeIndexable for &'a Triangulation +impl< + 'a, + I: Copy + PartialEq + GetNodeIndex + std::fmt::Debug, + W: GetVertexIndex + HasPosition, + > visit::NodeIndexable for &'a Triangulation { fn node_bound(&self) -> usize { - spade::Triangulation::num_vertices(&self.triangulation) + //spade::Triangulation::num_vertices(&self.triangulation) + self.vertex_to_handle.len() } fn to_index(&self, node: I) -> usize { @@ -225,6 +234,6 @@ impl<'a, I: Copy + PartialEq + GetNodeIndex, W: GetVertexIndex + HasPosition< fn from_index(&self, index: usize) -> I { spade::Triangulation::s(&self.triangulation) .vertex_data(self.vertex_to_handle[index].unwrap()) - .vertex() + .vertex_index() } }