diff --git a/Cargo.toml b/Cargo.toml index ac691b5..52f59d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,13 +67,13 @@ version = "0.9.4" [dependencies.eframe] optional = true -version = "0.26.0" +version = "0.28.1" default-features = false features = ["accesskit", "default_fonts", "glow", "persistence"] [dependencies.egui] optional = true -version = "0.26.0" +version = "0.28.1" [dependencies.rfd] optional = true diff --git a/index.html b/index.html index 184e7a2..c9b35e3 100644 --- a/index.html +++ b/index.html @@ -3,19 +3,30 @@ - + - Topola + Topola PCB router + - - + - - + + + + + + + + + + + + + + + - - --> + + diff --git a/locales/en-US/main.ftl b/locales/en-US/main.ftl index 319d288..56d79f6 100644 --- a/locales/en-US/main.ftl +++ b/locales/en-US/main.ftl @@ -1,11 +1,17 @@ menu-file = File +menu-edit = Edit +menu-view = View +menu-place = Place +menu-route = Route +menu-inspect = Inspect + menu-options = Options menu-debug = Debug action-open-dsn = Open -action-export-ses = Export session file -action-import-cmd = Import history -action-export-cmd = Export history +action-export-ses = Export Session File +action-import-cmd = Import History +action-export-cmd = Export History action-quit = Quit action-autoroute = Autoroute @@ -17,13 +23,17 @@ action-measure-length = Measure Length action-undo = Undo action-redo = Redo -presort-by-pairwise-detours = Presort by pairwise detours -squeeze-under-bands = Squeeze under bands -wrap-around-bands = Wrap around bands +presort-by-pairwise-detours = Presort by Pairwise Detours +squeeze-under-bands = Squeeze under Bands +wrap-around-bands = Wrap around Bands + +zoom-to-fit = Zoom to Fit show-ratsnest = Show Ratsnest show-navmesh = Show Navmesh show-bboxes = Show BBoxes show-origin-destination = Show Origin–Destination +show-layer-manager = Show Layer Manager + specctra-session-file = Specctra session file diff --git a/locales/nl/main.ftl b/locales/nl/main.ftl index e69de29..ba555ee 100644 --- a/locales/nl/main.ftl +++ b/locales/nl/main.ftl @@ -0,0 +1,19 @@ +menu-file = Bestand +menu-debug = Debug +action-export-ses = Sessiebestand exporteren +action-export-cmd = Geschiedenis exporteren +action-quit = Stop +action-place-via = Plaats Via +action-remove-bands = Banden verwijderen +action-compare-detours = Vergelijk Omwegen +action-undo = Ongedaan maken +action-redo = Opnieuw doen +show-navmesh = Toon Navmesh +show-bboxes = Toon BBoxen +action-open-dsn = Open +action-autoroute = Automatische route +action-import-cmd = Geschiedenis importeren +show-ratsnest = Toon Rattennest +show-origin-destination = Toon Herkomst-Bestemming +specctra-session-file = Specctra-sessiebestand +action-measure-length = Lengte meten diff --git a/locales/pl/main.ftl b/locales/pl/main.ftl index e69de29..a9cbfd0 100644 --- a/locales/pl/main.ftl +++ b/locales/pl/main.ftl @@ -0,0 +1,22 @@ +action-place-via = Wstaw przelotkę +menu-debug = Debuguj +action-open-dsn = Otwórz +action-export-cmd = Eksportuj historię +action-autoroute = Autotrasuj +action-remove-bands = Usuń taśmy +action-measure-length = Zmierz długość +action-undo = Cofnij +show-ratsnest = Pokaż szczurze gniazdo +specctra-session-file = Plik sesji Specctra +menu-file = Plik +action-redo = Ponów +action-export-ses = Eksportuj plik sesji +action-import-cmd = Importuj historię +action-quit = Wyjdź +action-compare-detours = Porównaj objazdy +show-navmesh = Pokaż siatkę nawigacyjną +show-bboxes = Pokaż obwiednie +show-origin-destination = Pokaż pochodzenie i destynację +menu-options = Opcje +wrap-around-bands = Owiń wokół taśm +squeeze-under-bands = Ściśnij pod taśmami diff --git a/src/bin/topola-egui/app.rs b/src/bin/topola-egui/app.rs index 700e82d..ea89b97 100644 --- a/src/bin/topola-egui/app.rs +++ b/src/bin/topola-egui/app.rs @@ -142,7 +142,8 @@ impl App { self.maybe_design = Some(design); self.arc_mutex_maybe_invoker = Arc::new(Mutex::new(Some(Invoker::new( Autorouter::new(board).unwrap(), - )))) + )))); + self.viewport.scheduled_zoom_to_fit = true; } if let Some(invoker) = self.arc_mutex_maybe_invoker.lock().unwrap().as_mut() { @@ -177,15 +178,21 @@ impl eframe::App for App { self.history_channel.0.clone(), self.arc_mutex_maybe_invoker.clone(), &mut self.maybe_execute, + &mut self.viewport, &mut self.maybe_overlay, &self.maybe_design, ); self.update_state(ctx.input(|i| i.stable_dt)); - if let Some(ref mut layers) = self.maybe_layers { - if let Some(invoker) = self.arc_mutex_maybe_invoker.lock().unwrap().as_ref() { - layers.update(ctx, invoker.autorouter().board()); + self.bottom + .update(ctx, &self.translator, &self.viewport, &self.maybe_execute); + + if self.top.show_layer_manager { + if let Some(ref mut layers) = self.maybe_layers { + if let Some(invoker) = self.arc_mutex_maybe_invoker.lock().unwrap().as_ref() { + layers.update(ctx, invoker.autorouter().board()); + } } } @@ -198,14 +205,6 @@ impl eframe::App for App { &self.maybe_layers, ); - self.bottom.update( - ctx, - &self.translator, - &self.viewport, - viewport_rect, - &self.maybe_execute, - ); - if ctx.input(|i| i.key_pressed(egui::Key::Escape)) { ctx.send_viewport_cmd(egui::ViewportCommand::Close); } diff --git a/src/bin/topola-egui/bottom.rs b/src/bin/topola-egui/bottom.rs index 9a1e705..a93fe31 100644 --- a/src/bin/topola-egui/bottom.rs +++ b/src/bin/topola-egui/bottom.rs @@ -14,14 +14,11 @@ impl Bottom { ctx: &egui::Context, tr: &Translator, viewport: &Viewport, - viewport_rect: egui::Rect, maybe_execute: &Option, ) { egui::TopBottomPanel::bottom("bottom_panel").show(ctx, |ui| { - let transform = egui::emath::RectTransform::from_to(viewport.from_rect, viewport_rect); - let latest_pos = transform - .inverse() - .transform_pos(ctx.input(|i| i.pointer.latest_pos().unwrap_or_default())); + let latest_pos = viewport.transform.inverse() + * ctx.input(|i| i.pointer.latest_pos().unwrap_or_default()); let mut message = String::from(""); diff --git a/src/bin/topola-egui/file_receiver.rs b/src/bin/topola-egui/file_receiver.rs index 2a892d3..4e2e00a 100644 --- a/src/bin/topola-egui/file_receiver.rs +++ b/src/bin/topola-egui/file_receiver.rs @@ -19,6 +19,6 @@ impl<'a> FileReceiver<'a> { #[cfg(target_arch = "wasm32")] pub fn try_recv(&mut self) -> Result>, TryRecvError> { - Ok(Cursor::new(self.receiver.try_recv().unwrap().into())) + Ok(Cursor::new(self.receiver.try_recv()?.into())) } } diff --git a/src/bin/topola-egui/main.rs b/src/bin/topola-egui/main.rs index 7d51971..446daf0 100644 --- a/src/bin/topola-egui/main.rs +++ b/src/bin/topola-egui/main.rs @@ -21,6 +21,7 @@ use unic_langid::{langid, LanguageIdentifier}; #[cfg(not(target_arch = "wasm32"))] fn main() -> eframe::Result<()> { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). + let langid = if let Some(langname) = get_locale() { langname.parse().unwrap_or(langid!("en-US")) } else { @@ -36,7 +37,7 @@ fn main() -> eframe::Result<()> { eframe::run_native( "topola-egui", native_options, - Box::new(|cc| Box::new(App::new(cc, langid))), + Box::new(|cc| Ok(Box::new(App::new(cc, langid)))), ) } @@ -47,16 +48,32 @@ fn main() { eframe::WebLogger::init(log::LevelFilter::Debug).ok(); let web_options = eframe::WebOptions::default(); - let langid: LanguageIdentifier = "en-US".parse(); wasm_bindgen_futures::spawn_local(async { - eframe::WebRunner::new() + let start_result = eframe::WebRunner::new() .start( "topola-egui", web_options, - Box::new(|cc| Box::new(App::new(cc, langid))), + Box::new(|cc| Ok(Box::new(App::new(cc, langid!("en-US"))))), ) - .await - .expect("failed to start eframe"); + .await; + + // Remove the loading text and spinner: + let loading_text = eframe::web_sys::window() + .and_then(|w| w.document()) + .and_then(|d| d.get_element_by_id("loading_text")); + if let Some(loading_text) = loading_text { + match start_result { + Ok(_) => { + loading_text.remove(); + } + Err(e) => { + loading_text.set_inner_html( + "

The app has crashed. See the developer console for details.

", + ); + panic!("Failed to start eframe: {e:?}"); + } + } + } }); } diff --git a/src/bin/topola-egui/painter.rs b/src/bin/topola-egui/painter.rs index fa400b6..30ec427 100644 --- a/src/bin/topola-egui/painter.rs +++ b/src/bin/topola-egui/painter.rs @@ -1,4 +1,5 @@ use geo::{CoordsIter, Point, Polygon}; +use rstar::AABB; use topola::{ geometry::primitive::{AccessPrimitiveShape, PrimitiveShape}, math::{self, Circle}, @@ -6,14 +7,14 @@ use topola::{ pub struct Painter<'a> { ui: &'a mut egui::Ui, - transform: egui::emath::RectTransform, + transform: egui::emath::TSTransform, paint_bboxes: bool, } impl<'a> Painter<'a> { pub fn new( ui: &'a mut egui::Ui, - transform: egui::emath::RectTransform, + transform: egui::emath::TSTransform, paint_bboxes: bool, ) -> Self { Self { @@ -29,11 +30,11 @@ impl<'a> Painter<'a> { PrimitiveShape::Seg(seg) => egui::Shape::line_segment( [ self.transform - .transform_pos([seg.from.x() as f32, -seg.from.y() as f32].into()), + .mul_pos([seg.from.x() as f32, -seg.from.y() as f32].into()), self.transform - .transform_pos([seg.to.x() as f32, -seg.to.y() as f32].into()), + .mul_pos([seg.to.x() as f32, -seg.to.y() as f32].into()), ], - egui::Stroke::new(seg.width as f32 * self.transform.scale().x, color), + egui::Stroke::new(seg.width as f32 * self.transform.scaling, color), ), PrimitiveShape::Bend(bend) => { let circle = bend.circle(); @@ -46,12 +47,12 @@ impl<'a> Painter<'a> { for i in 0..=100 { let x = circle.pos.x() + circle.r * (angle_from + i as f64 * angle_step).cos(); let y = circle.pos.y() + circle.r * (angle_from + i as f64 * angle_step).sin(); - points.push(self.transform.transform_pos([x as f32, -y as f32].into())); + points.push(self.transform.mul_pos([x as f32, -y as f32].into())); } egui::Shape::line( points, - egui::Stroke::new(bend.width as f32 * self.transform.scale().x, color), + egui::Stroke::new(bend.width as f32 * self.transform.scaling, color), ) } }; @@ -59,19 +60,22 @@ impl<'a> Painter<'a> { self.ui.painter().add(epaint_shape); if self.paint_bboxes { - let bbox = AccessPrimitiveShape::bbox(shape, 0.0); - let rect = egui::epaint::Rect { - min: [bbox.lower()[0] as f32, -bbox.upper()[1] as f32].into(), - max: [bbox.upper()[0] as f32, -bbox.lower()[1] as f32].into(), - }; - self.ui.painter().add(egui::Shape::rect_stroke( - self.transform.transform_rect(rect), - egui::Rounding::ZERO, - egui::Stroke::new(1.0, egui::Color32::GRAY), - )); + self.paint_bbox(AccessPrimitiveShape::bbox(shape, 0.0)); } } + pub fn paint_bbox(&mut self, bbox: AABB<[f64; 2]>) { + let rect = egui::epaint::Rect { + min: [bbox.lower()[0] as f32, -bbox.upper()[1] as f32].into(), + max: [bbox.upper()[0] as f32, -bbox.lower()[1] as f32].into(), + }; + self.ui.painter().add(egui::Shape::rect_stroke( + self.transform * rect, + egui::Rounding::ZERO, + egui::Stroke::new(1.0, egui::Color32::GRAY), + )); + } + pub fn paint_dot(&mut self, circle: Circle, color: egui::epaint::Color32) { let shape = self.dot_shape(circle, color); self.ui.painter().add(shape); @@ -80,8 +84,8 @@ impl<'a> Painter<'a> { fn dot_shape(&mut self, circle: Circle, color: egui::epaint::Color32) -> egui::Shape { egui::Shape::circle_filled( self.transform - .transform_pos([circle.pos.x() as f32, -circle.pos.y() as f32].into()), - circle.r as f32 * self.transform.scale().x, + .mul_pos([circle.pos.x() as f32, -circle.pos.y() as f32].into()), + circle.r as f32 * self.transform.scaling, color, ) } @@ -92,7 +96,7 @@ impl<'a> Painter<'a> { .exterior_coords_iter() .map(|coords| { self.transform - .transform_pos([coords.x as f32, -coords.y as f32].into()) + .mul_pos([coords.x as f32, -coords.y as f32].into()) }) .collect(), color, @@ -104,9 +108,9 @@ impl<'a> Painter<'a> { self.ui.painter().add(egui::Shape::line_segment( [ self.transform - .transform_pos([from.x() as f32, -from.y() as f32].into()), + .mul_pos([from.x() as f32, -from.y() as f32].into()), self.transform - .transform_pos([to.x() as f32, -to.y() as f32].into()), + .mul_pos([to.x() as f32, -to.y() as f32].into()), ], stroke, )); diff --git a/src/bin/topola-egui/top.rs b/src/bin/topola-egui/top.rs index e48ff14..ee2151f 100644 --- a/src/bin/topola-egui/top.rs +++ b/src/bin/topola-egui/top.rs @@ -19,6 +19,7 @@ use crate::{ file_sender::FileSender, overlay::Overlay, translator::Translator, + viewport::Viewport, }; pub struct Top { @@ -28,6 +29,7 @@ pub struct Top { pub show_navmesh: bool, pub show_bboxes: bool, pub show_origin_destination: bool, + pub show_layer_manager: bool, } impl Top { @@ -45,6 +47,7 @@ impl Top { show_navmesh: false, show_bboxes: false, show_origin_destination: false, + show_layer_manager: true, } } @@ -56,6 +59,7 @@ impl Top { history_sender: Sender, arc_mutex_maybe_invoker: Arc>>>, maybe_execute: &mut Option, + viewport: &mut Viewport, maybe_overlay: &mut Option, maybe_design: &Option, ) -> Result<(), InvokerError> { @@ -140,39 +144,23 @@ impl Top { } }); - ui.separator(); + ui.menu_button(tr.text("menu-edit"), |ui| { + undo.button(ctx, ui); + redo.button(ctx, ui); - autoroute.button(ctx, ui); + ui.separator(); - ui.menu_button(tr.text("menu-options"), |ui| { - ui.checkbox( - &mut self.autorouter_options.presort_by_pairwise_detours, - tr.text("presort-by-pairwise-detours"), - ); - ui.checkbox( - &mut self.autorouter_options.router_options.squeeze_under_bands, - tr.text("squeeze-under-bands"), - ); - ui.checkbox( - &mut self.autorouter_options.router_options.wrap_around_bands, - tr.text("wrap-around-bands"), - ); + remove_bands.button(ctx, ui); }); - ui.separator(); + ui.menu_button(tr.text("menu-view"), |ui| { + ui.toggle_value( + &mut viewport.scheduled_zoom_to_fit, + tr.text("zoom-to-fit"), + ); - place_via.toggle_widget(ctx, ui, &mut self.is_placing_via); + ui.separator(); - remove_bands.button(ctx, ui); - - ui.separator(); - - undo.button(ctx, ui); - redo.button(ctx, ui); - - ui.separator(); - - ui.menu_button(tr.text("menu-debug"), |ui| { ui.checkbox(&mut self.show_ratsnest, tr.text("show-ratsnest")); ui.checkbox(&mut self.show_navmesh, tr.text("show-navmesh")); ui.checkbox(&mut self.show_bboxes, tr.text("show-bboxes")); @@ -182,6 +170,35 @@ impl Top { ); ui.separator(); + + ui.checkbox(&mut self.show_layer_manager, tr.text("show-layer-manager")); + }); + + ui.menu_button(tr.text("menu-place"), |ui| { + place_via.toggle_widget(ctx, ui, &mut self.is_placing_via); + }); + + ui.menu_button(tr.text("menu-route"), |ui| { + autoroute.button(ctx, ui); + ui.separator(); + + ui.menu_button(tr.text("menu-options"), |ui| { + ui.checkbox( + &mut self.autorouter_options.presort_by_pairwise_detours, + tr.text("presort-by-pairwise-detours"), + ); + ui.checkbox( + &mut self.autorouter_options.router_options.squeeze_under_bands, + tr.text("squeeze-under-bands"), + ); + ui.checkbox( + &mut self.autorouter_options.router_options.wrap_around_bands, + tr.text("wrap-around-bands"), + ); + }); + }); + + ui.menu_button(tr.text("menu-inspect"), |ui| { compare_detours.button(ctx, ui); measure_length.button(ctx, ui); }); diff --git a/src/bin/topola-egui/viewport.rs b/src/bin/topola-egui/viewport.rs index 2966751..36c8a73 100644 --- a/src/bin/topola-egui/viewport.rs +++ b/src/bin/topola-egui/viewport.rs @@ -3,6 +3,7 @@ use petgraph::{ data::DataMap, visit::{EdgeRef, IntoEdgeReferences}, }; +use rstar::{Envelope, AABB}; use topola::{ autorouter::invoker::{ Command, ExecuteWithStatus, GetGhosts, GetMaybeNavmesh, GetMaybeTrace, GetObstacles, @@ -22,13 +23,15 @@ use topola::{ use crate::{app::execute, layers::Layers, overlay::Overlay, painter::Painter, top::Top}; pub struct Viewport { - pub from_rect: egui::emath::Rect, + pub transform: egui::emath::TSTransform, + pub scheduled_zoom_to_fit: bool, } impl Viewport { pub fn new() -> Self { Self { - from_rect: egui::Rect::from_x_y_ranges(0.0..=1000000.0, 0.0..=500000.0), + transform: egui::emath::TSTransform::new([0.0, 0.0].into(), 0.01), + scheduled_zoom_to_fit: false, } } @@ -40,37 +43,46 @@ impl Viewport { maybe_execute: &mut Option, maybe_overlay: &mut Option, maybe_layers: &Option, + ) -> egui::Rect { + let viewport_rect = self.paint( + ctx, + top, + maybe_invoker, + maybe_execute, + maybe_overlay, + maybe_layers, + ); + + if self.scheduled_zoom_to_fit { + self.zoom_to_fit(maybe_invoker, &viewport_rect); + } + + viewport_rect + } + + pub fn paint( + &mut self, + ctx: &egui::Context, + top: &Top, + maybe_invoker: &mut Option>, + maybe_execute: &mut Option, + maybe_overlay: &mut Option, + maybe_layers: &Option, ) -> egui::Rect { 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 (_id, viewport_rect) = ui.allocate_space(ui.available_size()); + let latest_pos = self.transform.inverse() * (ctx.input(|i| i.pointer.latest_pos().unwrap_or_default())); - 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_scaling = self.transform.scaling; + self.transform.scaling *= ctx.input(|i| i.zoom_delta()); - let old_scale = old_transform.scale().x; - self.from_rect = self.from_rect / ctx.input(|i| i.zoom_delta()); + self.transform.translation += latest_pos.to_vec2() * (old_scaling - self.transform.scaling); + self.transform.translation += ctx.input(|i| i.smooth_scroll_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, top.show_bboxes); + let mut painter = Painter::new(ui, self.transform, top.show_bboxes); if let Some(ref mut invoker) = maybe_invoker { if ctx.input(|i| i.pointer.any_click()) { @@ -224,6 +236,13 @@ impl Viewport { } } + if top.show_bboxes { + let root_bbox3d = board.layout().drawing().rtree().root().envelope(); + + let root_bbox = AABB::<[f64; 2]>::from_corners([root_bbox3d.lower()[0], root_bbox3d.lower()[1]].into(), [root_bbox3d.upper()[0], root_bbox3d.upper()[1]].into()); + painter.paint_bbox(root_bbox); + } + if let Some(execute) = maybe_execute { for ghost in execute.ghosts().iter() { painter.paint_primitive(&ghost, egui::Color32::from_rgb(75, 75, 150)); @@ -257,4 +276,43 @@ impl Viewport { }) }).inner.inner } + + fn zoom_to_fit( + &mut self, + maybe_invoker: &mut Option>, + viewport_rect: &egui::Rect, + ) { + if self.scheduled_zoom_to_fit { + if let Some(invoker) = maybe_invoker { + let root_bbox = invoker + .autorouter() + .board() + .layout() + .drawing() + .rtree() + .root() + .envelope(); + + let root_bbox_width = root_bbox.upper()[0] - root_bbox.lower()[0]; + let root_bbox_height = root_bbox.upper()[1] - root_bbox.lower()[1]; + + if root_bbox_width / root_bbox_height + >= (viewport_rect.width() as f64) / (viewport_rect.height() as f64) + { + self.transform.scaling = 0.8 * viewport_rect.width() / root_bbox_width as f32; + } else { + self.transform.scaling = 0.8 * viewport_rect.height() / root_bbox_height as f32; + } + + self.transform.translation = egui::Vec2::new( + viewport_rect.center()[0] as f32, + viewport_rect.center()[1] as f32, + ) - (self.transform.scaling + * egui::Pos2::new(root_bbox.center()[0] as f32, -root_bbox.center()[1] as f32)) + .to_vec2(); + } + } + + self.scheduled_zoom_to_fit = false; + } } diff --git a/src/bin/topola/main.rs b/src/bin/topola/main.rs index 64f5382..ddf674a 100644 --- a/src/bin/topola/main.rs +++ b/src/bin/topola/main.rs @@ -6,6 +6,8 @@ use topola::autorouter::invoker::Command; use topola::autorouter::invoker::Invoker; use topola::autorouter::selection::PinSelection; use topola::autorouter::Autorouter; +use topola::autorouter::AutorouterOptions; +use topola::router::RouterOptions; use topola::specctra::design::SpecctraDesign; pub mod cli; @@ -25,9 +27,16 @@ fn main() -> Result<(), std::io::Error> { serde_json::from_reader(commands_bufread)? } else { let mut history = History::new(); - history.do_(Command::Autoroute(PinSelection::new_select_layer( - &board, 0, - ))); + history.do_(Command::Autoroute( + PinSelection::new_select_layer(&board, 0), + AutorouterOptions { + presort_by_pairwise_detours: false, + router_options: RouterOptions { + wrap_around_bands: true, + squeeze_under_bands: false, + }, + }, + )); history }; diff --git a/src/router/router.rs b/src/router/router.rs index f8fef1e..c232f76 100644 --- a/src/router/router.rs +++ b/src/router/router.rs @@ -1,5 +1,3 @@ -use std::convert::Infallible; - use geo::EuclideanDistance; use petgraph::{data::DataMap, visit::EdgeRef}; use serde::{Deserialize, Serialize}; @@ -16,7 +14,7 @@ use crate::{ Collision, DrawingException, Infringement, }, geometry::{ - primitive::{AccessPrimitiveShape, PrimitiveShape}, + primitive::PrimitiveShape, shape::{AccessShape, MeasureLength}, }, graph::{GetPetgraphIndex, MakeRef},