From 4a9322d69454607085c0f419db3f99f48c5a7715 Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Tue, 16 Apr 2024 00:21:18 +0000 Subject: [PATCH] overlay: new module, impl. basic mouse selection of primitives Only check for intersection of the AABB as a starter. --- src/bin/topola-egui/app.rs | 39 ++++++++++++++++++++++------ src/bin/topola-sdl2-demo/main.rs | 18 ++++++------- src/drawing/drawing.rs | 12 ++++----- src/drawing/graph.rs | 2 +- src/drawing/primitive.rs | 14 +++------- src/geometry/geometry.rs | 33 ++++++++++-------------- src/geometry/with_rtree.rs | 37 +++++++++++++-------------- src/layout/layout.rs | 8 +++--- src/lib.rs | 1 + src/overlay/mod.rs | 3 +++ src/overlay/overlay.rs | 44 ++++++++++++++++++++++++++++++++ 11 files changed, 135 insertions(+), 76 deletions(-) create mode 100644 src/overlay/mod.rs create mode 100644 src/overlay/overlay.rs diff --git a/src/bin/topola-egui/app.rs b/src/bin/topola-egui/app.rs index 8c1aaf8..0867c6b 100644 --- a/src/bin/topola-egui/app.rs +++ b/src/bin/topola-egui/app.rs @@ -1,4 +1,5 @@ use futures::executor; +use geo::point; use std::{ future::Future, sync::mpsc::{channel, Receiver, Sender}, @@ -7,9 +8,13 @@ use std::{ use topola::{ drawing::{graph::MakePrimitive, primitive::MakeShape, Drawing}, dsn::{design::DsnDesign, rules::DsnRules}, - geometry::primitive::{BendShape, DotShape, PrimitiveShape, SegShape}, + geometry::{ + primitive::{BendShape, DotShape, PrimitiveShape, SegShape}, + Node, + }, layout::{zone::MakePolygon, Layout}, math::Circle, + overlay::Overlay, }; use crate::painter::Painter; @@ -18,10 +23,10 @@ use crate::painter::Painter; #[derive(serde::Deserialize, serde::Serialize)] #[serde(default)] pub struct App { - // Example stuff: - label: String, + #[serde(skip)] + overlay: Overlay, - #[serde(skip)] // Don't serialize this field. + #[serde(skip)] text_channel: (Sender, Receiver), #[serde(skip)] @@ -34,8 +39,7 @@ pub struct App { impl Default for App { fn default() -> Self { Self { - // Example stuff: - label: "Hello World!".to_owned(), + overlay: Overlay::new(), text_channel: channel(), layout: None, from_rect: egui::Rect::from_x_y_ranges(0.0..=1000000.0, 0.0..=500000.0), @@ -140,9 +144,22 @@ impl eframe::App for App { let mut painter = Painter::new(ui, transform); if let Some(layout) = &self.layout { + if ctx.input(|i| i.pointer.any_click()) { + self.overlay.click( + layout, + point! {x: latest_pos.x as f64, y: -latest_pos.y as f64}, + ); + } + for node in layout.drawing().layer_primitive_nodes(1) { let shape = node.primitive(layout.drawing()).shape(); - painter.paint_shape(&shape, egui::Color32::from_rgb(52, 52, 200)); + + let color = if self.overlay.selection().contains(&Node::Primitive(node)) { + egui::Color32::from_rgb(100, 100, 255) + } else { + egui::Color32::from_rgb(52, 52, 200) + }; + painter.paint_shape(&shape, color); } for zone in layout.layer_zones(1) { @@ -154,7 +171,13 @@ impl eframe::App for App { for node in layout.drawing().layer_primitive_nodes(0) { let shape = node.primitive(layout.drawing()).shape(); - painter.paint_shape(&shape, egui::Color32::from_rgb(200, 52, 52)); + + let color = if self.overlay.selection().contains(&Node::Primitive(node)) { + egui::Color32::from_rgb(255, 100, 100) + } else { + egui::Color32::from_rgb(200, 52, 52) + }; + painter.paint_shape(&shape, color); } for zone in layout.layer_zones(0) { diff --git a/src/bin/topola-sdl2-demo/main.rs b/src/bin/topola-sdl2-demo/main.rs index 27c9027..3368e2e 100644 --- a/src/bin/topola-sdl2-demo/main.rs +++ b/src/bin/topola-sdl2-demo/main.rs @@ -78,7 +78,7 @@ impl RulesTrait for SimpleRules { } // Clunky enum to work around borrow checker. -enum RouterOrDrawing<'a, R: RulesTrait> { +enum RouterOrLayout<'a, R: RulesTrait> { Router(&'a mut Router), Layout(&'a Layout), } @@ -133,7 +133,7 @@ impl<'a, R: RulesTrait> RouterObserverTrait for DebugRouterObserver<'a> { self.renderer, self.font_context, self.view, - RouterOrDrawing::Layout(tracer.layout.drawing()), + RouterOrLayout::Layout(tracer.layout.drawing()), None, Some(tracer.mesh.clone()), &trace.path, @@ -152,7 +152,7 @@ impl<'a, R: RulesTrait> RouterObserverTrait for DebugRouterObserver<'a> { self.renderer, self.font_context, self.view, - RouterOrDrawing::Layout(tracer.layout.drawing()), + RouterOrLayout::Layout(tracer.layout.drawing()), None, Some(tracer.mesh.clone()), &path, @@ -184,7 +184,7 @@ impl<'a, R: RulesTrait> RouterObserverTrait for DebugRouterObserver<'a> { self.renderer, self.font_context, self.view, - RouterOrDrawing::Layout(tracer.layout.drawing()), + RouterOrLayout::Layout(tracer.layout.drawing()), None, Some(tracer.mesh.clone()), &trace.path, @@ -275,7 +275,7 @@ fn main() -> Result<(), anyhow::Error> { &mut renderer, &font_context, &mut view, - RouterOrDrawing::Layout(router.layout.drawing()), + RouterOrLayout::Layout(router.layout.drawing()), None, None, &[], @@ -299,7 +299,7 @@ fn main() -> Result<(), anyhow::Error> { &mut renderer, &font_context, &mut view, - RouterOrDrawing::Layout(router.layout.drawing()), + RouterOrLayout::Layout(router.layout.drawing()), None, None, &[], @@ -322,7 +322,7 @@ fn render_times( renderer: &mut Renderer, font_context: &CanvasFontContext, view: &mut View, - mut router_or_layout: RouterOrDrawing, + mut router_or_layout: RouterOrLayout, maybe_band: Option, mut maybe_mesh: Option, path: &[VertexIndex], @@ -371,7 +371,7 @@ fn render_times( let mut painter = Painter::new(&mut canvas); let drawing = match router_or_layout { - RouterOrDrawing::Router(ref mut router) => { + RouterOrLayout::Router(ref mut router) => { let state = event_pump.mouse_state(); if let Some(band) = maybe_band { @@ -394,7 +394,7 @@ fn render_times( router.layout.drawing() } - RouterOrDrawing::Layout(layout) => layout, + RouterOrLayout::Layout(layout) => layout, }; //let result = panic::catch_unwind(|| { diff --git a/src/drawing/drawing.rs b/src/drawing/drawing.rs index ba1a533..1820a6e 100644 --- a/src/drawing/drawing.rs +++ b/src/drawing/drawing.rs @@ -29,7 +29,7 @@ use crate::drawing::{ }; use crate::geometry::compound::CompoundManagerTrait; use crate::geometry::with_rtree::BboxedIndex; -use crate::geometry::NodeWeight; +use crate::geometry::Node; use crate::geometry::{ primitive::{PrimitiveShape, PrimitiveShapeTrait}, with_rtree::GeometryWithRtree, @@ -639,7 +639,7 @@ impl Drawing { .rtree() .iter() .filter_map(|wrapper| { - if let NodeWeight::Primitive(primitive_node) = wrapper.data { + if let Node::Primitive(primitive_node) = wrapper.data { Some(primitive_node) } else { None @@ -655,7 +655,7 @@ impl Drawing { [f64::INFINITY, f64::INFINITY, layer as f64], )) .filter_map(|wrapper| { - if let NodeWeight::Primitive(primitive_node) = wrapper.data { + if let Node::Primitive(primitive_node) = wrapper.data { Some(primitive_node) } else { None @@ -750,7 +750,7 @@ impl Drawing { .rtree() .locate_in_envelope_intersecting(&limiting_shape.full_height_envelope_3d(0.0, 2)) .filter_map(|wrapper| { - if let NodeWeight::Primitive(primitive_node) = wrapper.data { + if let Node::Primitive(primitive_node) = wrapper.data { Some(primitive_node) } else { None @@ -785,7 +785,7 @@ impl Drawing { .rtree() .locate_in_envelope_intersecting(&shape.full_height_envelope_3d(0.0, 2)) .filter_map(|wrapper| { - if let NodeWeight::Primitive(primitive_node) = wrapper.data { + if let Node::Primitive(primitive_node) = wrapper.data { Some(primitive_node) } else { None @@ -833,7 +833,7 @@ impl Drawing { #[debug_ensures(self.geometry_with_rtree.graph().node_count() == old(self.geometry_with_rtree.graph().node_count()))] #[debug_ensures(self.geometry_with_rtree.graph().edge_count() == old(self.geometry_with_rtree.graph().edge_count()))] - pub fn rtree(&self) -> &RTree>>> { + pub fn rtree(&self) -> &RTree>>> { self.geometry_with_rtree.rtree() } diff --git a/src/drawing/graph.rs b/src/drawing/graph.rs index 4232bfe..e8e7e99 100644 --- a/src/drawing/graph.rs +++ b/src/drawing/graph.rs @@ -84,7 +84,7 @@ macro_rules! impl_loose_weight { } #[enum_dispatch(GetNodeIndex, MakePrimitive)] -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)] pub enum PrimitiveIndex { FixedDot(FixedDotIndex), LooseDot(LooseDotIndex), diff --git a/src/drawing/primitive.rs b/src/drawing/primitive.rs index d0e4e5b..94bc31e 100644 --- a/src/drawing/primitive.rs +++ b/src/drawing/primitive.rs @@ -20,7 +20,7 @@ use crate::{ }, Drawing, }, - geometry::NodeWeight, + geometry::Node, }; #[enum_dispatch] @@ -186,7 +186,7 @@ impl<'a, W, CW: Copy, R: RulesTrait> GenericPrimitive<'a, W, CW, R> { } fn tagged_weight(&self) -> PrimitiveWeight { - if let NodeWeight::Primitive(weight) = *self + if let Node::Primitive(weight) = *self .drawing .geometry() .graph() @@ -262,15 +262,9 @@ impl<'a, CW: Copy, R: RulesTrait> FixedDot<'a, CW, R> { .graph() .node_weight(ni.node_index()) .unwrap(); - if matches!( - weight, - NodeWeight::Primitive(PrimitiveWeight::LoneLooseSeg(..)) - ) { + if matches!(weight, Node::Primitive(PrimitiveWeight::LoneLooseSeg(..))) { Some(LoneLooseSegIndex::new(ni.node_index()).into()) - } else if matches!( - weight, - NodeWeight::Primitive(PrimitiveWeight::SeqLooseSeg(..)) - ) { + } else if matches!(weight, Node::Primitive(PrimitiveWeight::SeqLooseSeg(..))) { Some(SeqLooseSegIndex::new(ni.node_index()).into()) } else { None diff --git a/src/geometry/geometry.rs b/src/geometry/geometry.rs index 94feaf4..f294e1e 100644 --- a/src/geometry/geometry.rs +++ b/src/geometry/geometry.rs @@ -58,10 +58,10 @@ pub enum GeometryLabel { Compound, } -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum NodeWeight { - Primitive(PW), - Compound(CW), +#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)] +pub enum Node { + Primitive(P), + Compound(C), } pub trait DotWeightTrait: GetPos + SetPos + GetWidth + Into + Copy {} @@ -80,7 +80,7 @@ pub struct Geometry< SI: GetNodeIndex + Into + Copy, BI: GetNodeIndex + Into + Copy, > { - graph: StableDiGraph, GeometryLabel, usize>, + graph: StableDiGraph, GeometryLabel, usize>, weight_marker: PhantomData, dot_weight_marker: PhantomData, seg_weight_marker: PhantomData, @@ -120,7 +120,7 @@ impl< } pub fn add_dot>(&mut self, weight: W) -> GenericIndex { - GenericIndex::::new(self.graph.add_node(NodeWeight::Primitive(weight.into()))) + GenericIndex::::new(self.graph.add_node(Node::Primitive(weight.into()))) } pub fn add_seg>( @@ -129,7 +129,7 @@ impl< to: DI, weight: W, ) -> GenericIndex { - let seg = GenericIndex::::new(self.graph.add_node(NodeWeight::Primitive(weight.into()))); + let seg = GenericIndex::::new(self.graph.add_node(Node::Primitive(weight.into()))); self.graph .update_edge(from.node_index(), seg.node_index(), GeometryLabel::Joined); @@ -146,8 +146,7 @@ impl< core: DI, weight: W, ) -> GenericIndex { - let bend = - GenericIndex::::new(self.graph.add_node(NodeWeight::Primitive(weight.into()))); + let bend = GenericIndex::::new(self.graph.add_node(Node::Primitive(weight.into()))); self.graph .update_edge(from.node_index(), bend.node_index(), GeometryLabel::Joined); @@ -166,15 +165,13 @@ impl< pub fn move_dot(&mut self, dot: DI, to: Point) { let mut weight = self.dot_weight(dot); weight.set_pos(to); - *self.graph.node_weight_mut(dot.node_index()).unwrap() = - NodeWeight::Primitive(weight.into()); + *self.graph.node_weight_mut(dot.node_index()).unwrap() = Node::Primitive(weight.into()); } pub fn shift_bend(&mut self, bend: BI, offset: f64) { let mut weight = self.bend_weight(bend); weight.set_offset(offset); - *self.graph.node_weight_mut(bend.node_index()).unwrap() = - NodeWeight::Primitive(weight.into()); + *self.graph.node_weight_mut(bend.node_index()).unwrap() = Node::Primitive(weight.into()); } pub fn flip_bend(&mut self, bend: BI) { @@ -270,7 +267,7 @@ impl< } fn primitive_weight(&self, index: NodeIndex) -> PW { - if let NodeWeight::Primitive(weight) = *self.graph.node_weight(index).unwrap() { + if let Node::Primitive(weight) = *self.graph.node_weight(index).unwrap() { weight } else { unreachable!() @@ -296,9 +293,7 @@ impl< } pub fn compound_weight(&self, compound: GenericIndex) -> CW { - if let NodeWeight::Compound(weight) = - *self.graph.node_weight(compound.node_index()).unwrap() - { + if let Node::Compound(weight) = *self.graph.node_weight(compound.node_index()).unwrap() { weight } else { unreachable!() @@ -473,7 +468,7 @@ impl< }) } - pub fn graph(&self) -> &StableDiGraph, GeometryLabel, usize> { + pub fn graph(&self) -> &StableDiGraph, GeometryLabel, usize> { &self.graph } } @@ -491,7 +486,7 @@ impl< > CompoundManagerTrait> for Geometry { fn add_compound(&mut self, weight: CW) -> GenericIndex { - GenericIndex::::new(self.graph.add_node(NodeWeight::Compound(weight))) + GenericIndex::::new(self.graph.add_node(Node::Compound(weight))) } fn remove_compound(&mut self, compound: GenericIndex) { diff --git a/src/geometry/with_rtree.rs b/src/geometry/with_rtree.rs index e6bc9ab..9a208dc 100644 --- a/src/geometry/with_rtree.rs +++ b/src/geometry/with_rtree.rs @@ -10,8 +10,7 @@ use crate::{ geometry::{ compound::CompoundManagerTrait, primitive::{PrimitiveShape, PrimitiveShapeTrait}, - BendWeightTrait, DotWeightTrait, Geometry, GeometryLabel, GetWidth, NodeWeight, - SegWeightTrait, + BendWeightTrait, DotWeightTrait, Geometry, GeometryLabel, GetWidth, Node, SegWeightTrait, }, graph::{GenericIndex, GetNodeIndex}, }; @@ -49,7 +48,7 @@ pub struct GeometryWithRtree< BI: GetNodeIndex + Into + Copy, > { geometry: Geometry, - rtree: RTree>>>, + rtree: RTree>>>, layer_count: u64, weight_marker: PhantomData, dot_weight_marker: PhantomData, @@ -102,7 +101,7 @@ impl< .dot_shape(dot.into().try_into().unwrap_or_else(|_| unreachable!())) .envelope_3d(0.0, weight.layer()), ), - NodeWeight::Primitive(dot.into()), + Node::Primitive(dot.into()), )); dot } @@ -123,7 +122,7 @@ impl< .seg_shape(seg.into().try_into().unwrap_or_else(|_| unreachable!())) .envelope_3d(0.0, weight.layer()), ), - NodeWeight::Primitive(seg.into()), + Node::Primitive(seg.into()), )); seg } @@ -145,7 +144,7 @@ impl< .bend_shape(bend.into().try_into().unwrap_or_else(|_| unreachable!())) .envelope_3d(0.0, weight.layer()), ), - NodeWeight::Primitive(bend.into()), + Node::Primitive(bend.into()), )); bend } @@ -265,7 +264,7 @@ impl< BI: GetNodeIndex + Into + Copy, > GeometryWithRtree { - fn make_bbox(&self, primitive: PI) -> BboxedIndex>> { + fn make_bbox(&self, primitive: PI) -> BboxedIndex>> { if let Ok(dot) = >::try_into(primitive) { self.make_dot_bbox(dot) } else if let Ok(seg) = >::try_into(primitive) { @@ -277,50 +276,50 @@ impl< } } - fn make_dot_bbox(&self, dot: DI) -> BboxedIndex>> { + fn make_dot_bbox(&self, dot: DI) -> BboxedIndex>> { BboxedIndex::new( Bbox::new( self.geometry .dot_shape(dot) .envelope_3d(0.0, self.layer(dot.into())), ), - NodeWeight::Primitive(dot.into()), + Node::Primitive(dot.into()), ) } - fn make_seg_bbox(&self, seg: SI) -> BboxedIndex>> { + fn make_seg_bbox(&self, seg: SI) -> BboxedIndex>> { BboxedIndex::new( Bbox::new( self.geometry .seg_shape(seg) .envelope_3d(0.0, self.layer(seg.into())), ), - NodeWeight::Primitive(seg.into()), + Node::Primitive(seg.into()), ) } - fn make_bend_bbox(&self, bend: BI) -> BboxedIndex>> { + fn make_bend_bbox(&self, bend: BI) -> BboxedIndex>> { BboxedIndex::new( Bbox::new( self.geometry .bend_shape(bend) .envelope_3d(0.0, self.layer(bend.into())), ), - NodeWeight::Primitive(bend.into()), + Node::Primitive(bend.into()), ) } fn make_compound_bbox( &self, compound: GenericIndex, - ) -> BboxedIndex>> { + ) -> BboxedIndex>> { let mut aabb = AABB::<[f64; 3]>::new_empty(); for member in self.geometry.compound_members(compound) { aabb.merge(&self.make_bbox(member).geom().aabb); } - BboxedIndex::new(Bbox::new(aabb), NodeWeight::Compound(compound)) + BboxedIndex::new(Bbox::new(aabb), Node::Compound(compound)) } fn shape(&self, primitive: PI) -> PrimitiveShape { @@ -352,25 +351,25 @@ impl< } // XXX: The type appears wrong? I don't think it should contain CW? - pub fn rtree(&self) -> &RTree>>> { + pub fn rtree(&self) -> &RTree>>> { &self.rtree } - pub fn graph(&self) -> &StableDiGraph, GeometryLabel, usize> { + pub fn graph(&self) -> &StableDiGraph, GeometryLabel, usize> { self.geometry.graph() } fn test_envelopes(&self) -> bool { !self.rtree.iter().any(|wrapper| { // TODO: Test envelopes of compounds too. - let NodeWeight::Primitive(primitive_node) = wrapper.data else { + let Node::Primitive(primitive_node) = wrapper.data else { return false; }; let shape = self.shape(primitive_node); let layer = self.layer(primitive_node); let wrapper = BboxedIndex::new( Bbox::new(shape.envelope_3d(0.0, layer)), - NodeWeight::Primitive(primitive_node), + Node::Primitive(primitive_node), ); !self .rtree diff --git a/src/layout/layout.rs b/src/layout/layout.rs index d6d960f..63e4c7d 100644 --- a/src/layout/layout.rs +++ b/src/layout/layout.rs @@ -18,7 +18,7 @@ use crate::{ }, geometry::{ compound::CompoundManagerTrait, BendWeightTrait, DotWeightTrait, Geometry, GeometryLabel, - GetWidth, NodeWeight, SegWeightTrait, + GetWidth, Node, SegWeightTrait, }, graph::{GenericIndex, GetNodeIndex}, layout::{ @@ -170,7 +170,7 @@ impl Layout { pub fn zones(&self) -> impl Iterator + '_ { self.drawing.rtree().iter().filter_map(|wrapper| { - if let NodeWeight::Compound(zone) = wrapper.data { + if let Node::Compound(zone) = wrapper.data { Some(match self.drawing.geometry().compound_weight(zone) { ZoneWeight::Solid(..) => { ZoneIndex::Solid(SolidZoneIndex::new(zone.node_index())) @@ -191,7 +191,7 @@ impl Layout { [f64::INFINITY, f64::INFINITY, layer as f64], )) .filter_map(|wrapper| { - if let NodeWeight::Compound(zone) = wrapper.data { + if let Node::Compound(zone) = wrapper.data { Some(match self.drawing.geometry().compound_weight(zone) { ZoneWeight::Solid(..) => { ZoneIndex::Solid(SolidZoneIndex::new(zone.node_index())) @@ -212,7 +212,7 @@ impl Layout { .compound_members(GenericIndex::new(zone.node_index())) } - pub fn drawing(&self) -> &Drawing { + pub fn drawing(&self) -> &Drawing { &self.drawing } } diff --git a/src/lib.rs b/src/lib.rs index bcde884..ff28b00 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,4 +7,5 @@ pub mod dsn; pub mod geometry; pub mod layout; pub mod math; +pub mod overlay; pub mod router; diff --git a/src/overlay/mod.rs b/src/overlay/mod.rs new file mode 100644 index 0000000..d67f202 --- /dev/null +++ b/src/overlay/mod.rs @@ -0,0 +1,3 @@ +mod overlay; + +pub use overlay::*; diff --git a/src/overlay/overlay.rs b/src/overlay/overlay.rs new file mode 100644 index 0000000..ac20d77 --- /dev/null +++ b/src/overlay/overlay.rs @@ -0,0 +1,44 @@ +use std::collections::HashSet; + +use geo::Point; +use rstar::AABB; + +use crate::{ + drawing::{graph::PrimitiveIndex, rules::RulesTrait}, + geometry::Node, + graph::GenericIndex, + layout::{zone::ZoneWeight, Layout}, +}; + +pub struct Overlay { + selection: HashSet>>, +} + +impl Overlay { + pub fn new() -> Self { + Self { + selection: HashSet::new(), + } + } + + pub fn click(&mut self, layout: &Layout, at: Point) { + for geom in layout.drawing().rtree().locate_in_envelope_intersecting( + &AABB::<[f64; 3]>::from_corners( + [at.x(), at.y(), -f64::INFINITY], + [at.x(), at.y(), f64::INFINITY], + ), + ) { + self.toggle_selection(geom.data); + } + } + + fn toggle_selection(&mut self, node: Node>) { + if !self.selection.insert(node) { + self.selection.remove(&node); + } + } + + pub fn selection(&self) -> &HashSet>> { + &self.selection + } +}