diff --git a/topola-egui/src/display.rs b/topola-egui/src/display.rs index a023aaf..d54d8bb 100644 --- a/topola-egui/src/display.rs +++ b/topola-egui/src/display.rs @@ -3,10 +3,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::{viewport::Viewport, workspace::Workspace}; -use topola::{ - Joint, JointId, PinSelection, Polygon, PolygonId, PrimitiveId, Ratline, Segment, SegmentId, - Vector2, -}; +use topola::{Joint, JointId, Polygon, PolygonId, Segment, SegmentId, Vector2}; pub struct Display {} @@ -40,6 +37,7 @@ impl Display { ui.painter().line( workspace .autorouter + .navmesher_board() .board() .layout() .boundary() @@ -52,7 +50,14 @@ impl Display { egui::Stroke::new(5.0 / viewport.scale_factor(), egui::Color32::WHITE), ); - for (joint_index, joint) in workspace.autorouter.board().layout().joints().collection() { + for (joint_index, joint) in workspace + .autorouter + .navmesher_board() + .board() + .layout() + .joints() + .collection() + { if workspace.appearance_panel.visible[joint.layer] { self.paint_joint( ctx, @@ -61,11 +66,19 @@ impl Display { joint, workspace.appearance_panel.layer_color( ctx, - workspace.autorouter.board().layer_name(joint.layer), - workspace.autorouter.board().pin_selection_contains_joint( - &workspace.pin_selection, - JointId::new(joint_index), - ), + workspace + .autorouter + .navmesher_board() + .board() + .layer_name(joint.layer), + workspace + .autorouter + .navmesher_board() + .board() + .pin_selection_contains_joint( + &workspace.pin_selection, + JointId::new(joint_index), + ), ), ); } @@ -73,6 +86,7 @@ impl Display { for (segment_index, segment) in workspace .autorouter + .navmesher_board() .board() .layout() .segments() @@ -86,16 +100,25 @@ impl Display { segment, workspace .autorouter + .navmesher_board() .board() .layout() .segment_endpoints(SegmentId::new(segment_index)), workspace.appearance_panel.layer_color( ctx, - workspace.autorouter.board().layer_name(segment.layer), - workspace.autorouter.board().pin_selection_contains_segment( - &workspace.pin_selection, - SegmentId::new(segment_index), - ), + workspace + .autorouter + .navmesher_board() + .board() + .layer_name(segment.layer), + workspace + .autorouter + .navmesher_board() + .board() + .pin_selection_contains_segment( + &workspace.pin_selection, + SegmentId::new(segment_index), + ), ), ); } @@ -105,6 +128,7 @@ impl Display { for (polygon_index, polygon) in workspace .autorouter + .navmesher_board() .board() .layout() .polygons() @@ -118,11 +142,19 @@ impl Display { polygon, workspace.appearance_panel.layer_color( ctx, - workspace.autorouter.board().layer_name(polygon.layer), - workspace.autorouter.board().pin_selection_contains_polygon( - &workspace.pin_selection, - PolygonId::new(polygon_index), - ), + workspace + .autorouter + .navmesher_board() + .board() + .layer_name(polygon.layer), + workspace + .autorouter + .navmesher_board() + .board() + .pin_selection_contains_polygon( + &workspace.pin_selection, + PolygonId::new(polygon_index), + ), ), ); } @@ -190,7 +222,14 @@ impl Display { viewport: &Viewport, workspace: &Workspace, ) { - for (_, joint) in workspace.autorouter.board().layout().joints().collection() { + for (_, joint) in workspace + .autorouter + .navmesher_board() + .board() + .layout() + .joints() + .collection() + { if workspace.appearance_panel.visible[joint.layer] { ui.painter().rect_stroke( egui::Rect { @@ -212,6 +251,7 @@ impl Display { for (i, segment) in workspace .autorouter + .navmesher_board() .board() .layout() .segments() @@ -220,6 +260,7 @@ impl Display { if workspace.appearance_panel.visible[segment.layer] { let endpoints = workspace .autorouter + .navmesher_board() .board() .layout() .segment_endpoints(SegmentId::new(i)); @@ -240,6 +281,7 @@ impl Display { for (i, polygon) in workspace .autorouter + .navmesher_board() .board() .layout() .polygons() @@ -268,10 +310,20 @@ impl Display { viewport: &Viewport, workspace: &Workspace, ) { - for layer in 0..*workspace.autorouter.board().layout().layer_count() { + for layer in 0..*workspace + .autorouter + .navmesher_board() + .board() + .layout() + .layer_count() + { if workspace.appearance_panel.visible[layer] { - for navmesh in - workspace.autorouter.navmesher().layer_navmeshers()[layer].navmeshes() + for navmesh in workspace + .autorouter + .navmesher_board() + .navmesher() + .layer_navmeshers()[layer] + .navmeshes() { for edge_geom in navmesh .triangulation() @@ -309,7 +361,7 @@ impl Display { .appearance_panel .colors(ctx) .layers - .color(workspace.navmesher_board.board().layer_name(layer)) + .color(workspace.autorouter.navmesher_board().board().layer_name(layer)) .normal,*/ ), ); diff --git a/topola-egui/src/viewport.rs b/topola-egui/src/viewport.rs index 50fafca..36f9dad 100644 --- a/topola-egui/src/viewport.rs +++ b/topola-egui/src/viewport.rs @@ -53,8 +53,11 @@ impl Viewport { let pointer_scene_pos = scene_to_viewport.inverse() * pointer_viewport_pos; if response.clicked() { - if let Some(pin_selector) = - workspace.autorouter.board().point_pin_selector( + if let Some(pin_selector) = workspace + .autorouter + .navmesher_board() + .board() + .point_pin_selector( 0, Vector2::new( pointer_scene_pos.x as i64, @@ -113,14 +116,26 @@ impl Viewport { } fn boundary_bounding_box(workspace: &Workspace) -> egui::Rect { - let first = workspace.autorouter.board().layout().boundary()[0]; + let first = workspace + .autorouter + .navmesher_board() + .board() + .layout() + .boundary()[0]; let mut min_x = first[0]; let mut max_x = first[0]; let mut min_y = first[1]; let mut max_y = first[1]; - for point in workspace.autorouter.board().layout().boundary()[1..].iter() { + for point in workspace + .autorouter + .navmesher_board() + .board() + .layout() + .boundary()[1..] + .iter() + { if point[0] < min_x { min_x = point[0]; } diff --git a/topola-egui/src/workspace.rs b/topola-egui/src/workspace.rs index 80ec206..38941f1 100644 --- a/topola-egui/src/workspace.rs +++ b/topola-egui/src/workspace.rs @@ -17,13 +17,14 @@ impl Workspace { let appearance_panel = AppearancePanel::new(&board); Self { - autorouter: Autorouter::with_board(board), + autorouter: Autorouter::new(board), appearance_panel, pin_selection: PinSelection::new(), } } pub fn update_appearance_panel(&mut self, ctx: &egui::Context) { - self.appearance_panel.update(ctx, &self.autorouter.board()); + self.appearance_panel + .update(ctx, &self.autorouter.navmesher_board().board()); } } diff --git a/topola/src/autorouter.rs b/topola/src/autorouter.rs index 495373c..7a7697b 100644 --- a/topola/src/autorouter.rs +++ b/topola/src/autorouter.rs @@ -3,141 +3,22 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use derive_getters::Getters; -use undoredo::Recorder; -use crate::{ - Board, Joint, JointId, Polygon, PolygonId, Ratsnest, Segment, SegmentId, Vector2, Via, ViaId, - navmesher::{MultiObstacleId, Navmesher}, -}; +use crate::{Board, Ratsnest, navmesher::NavmesherBoard}; #[derive(Clone, Debug, Getters)] pub struct Autorouter { - navmesher: Navmesher, + navmesher_board: NavmesherBoard, ratsnest: Ratsnest, - board: Board, - - joint_multiobstacles: Recorder>, - segment_multiobstacles: Recorder>, - polygon_multiobstacles: Recorder>, } impl Autorouter { - pub fn with_board(board: Board) -> Self { - let mut this = Self { - navmesher: Navmesher::new( - board - .layout() - .boundary() - .iter() - .map(|p| Vector2::new(p[0], p[1])), - *board.layout().layer_count(), - ), - ratsnest: Ratsnest::new(&board), - board, + pub fn new(board: Board) -> Self { + let ratsnest = Ratsnest::new(&board); - joint_multiobstacles: Recorder::new(Vec::new()), - segment_multiobstacles: Recorder::new(Vec::new()), - polygon_multiobstacles: Recorder::new(Vec::new()), - }; - - for (i, joint) in this.board.layout().joints().collection() { - this.joint_multiobstacles.insert( - i, - this.navmesher - .insert_multiobstacle(joint.layer, Self::joint_bounding_octagon(*joint)), - ); + Self { + navmesher_board: NavmesherBoard::new(board), + ratsnest, } - - for (i, segment) in this.board.layout().segments().collection() { - this.segment_multiobstacles.insert( - i, - this.navmesher.insert_multiobstacle( - segment.layer, - this.segment_bounding_rectangle(SegmentId::new(i), *segment), - ), - ); - } - - for (i, polygon) in this.board.layout().polygons().collection() { - this.polygon_multiobstacles.insert( - i, - this.navmesher - .insert_multiobstacle(polygon.layer, polygon.vertices.clone()), - ); - } - - this - } - - pub fn insert_joint(&mut self, joint: Joint) -> JointId { - let joint_id = self.board.add_joint(joint); - self.joint_multiobstacles.insert( - joint_id.index(), - self.navmesher - .insert_multiobstacle(joint.layer, Self::joint_bounding_octagon(joint)), - ); - - joint_id - } - - fn joint_bounding_octagon(joint: Joint) -> [Vector2; 8] { - let cx = joint.position.x; - let cy = joint.position.y; - let r = joint.radius as i64; - - [ - Vector2::new(cx + r, cy + r / 2), - Vector2::new(cx + r / 2, cy + r), - Vector2::new(cx - r / 2, cy + r), - Vector2::new(cx - r, cy + r / 2), - Vector2::new(cx - r, cy - r / 2), - Vector2::new(cx - r / 2, cy - r), - Vector2::new(cx + r / 2, cy - r), - Vector2::new(cx + r, cy - r / 2), - ] - } - - pub fn insert_segment(&mut self, segment: Segment) -> SegmentId { - let segment_id = self.board.add_segment(segment); - self.segment_multiobstacles.insert( - segment_id.index(), - self.navmesher.insert_multiobstacle( - segment.layer, - self.segment_bounding_rectangle(segment_id, segment), - ), - ); - - segment_id - } - - fn segment_bounding_rectangle( - &self, - segment_id: SegmentId, - segment: Segment, - ) -> [Vector2; 4] { - let endpoints = self.board.layout().segment_endpoints(segment_id); - crate::math::inflated_segment( - endpoints[0].x, - endpoints[0].y, - endpoints[1].x, - endpoints[1].y, - segment.half_width, - ) - } - - pub fn insert_via(&mut self, via: Via) -> ViaId { - // TODO: Insert into navmesh. - self.board.add_via(via) - } - - pub fn insert_polygon(&mut self, polygon: Polygon) -> PolygonId { - let polygon_id = self.board.add_polygon(polygon.clone()); - self.polygon_multiobstacles.insert( - polygon_id.index(), - self.navmesher - .insert_multiobstacle(polygon.layer, polygon.vertices), - ); - - polygon_id } } diff --git a/topola/src/math.rs b/topola/src/math.rs index d86a457..44d4f84 100644 --- a/topola/src/math.rs +++ b/topola/src/math.rs @@ -5,6 +5,7 @@ use derive_more::{ Add, AddAssign, Constructor, Div, DivAssign, From, Into, Mul, MulAssign, Sub, SubAssign, }; +use polygon_unionfind::UnionFind; use serde::{Deserialize, Serialize}; #[derive( @@ -177,8 +178,7 @@ pub fn kruskal_mst( let mut sorted_edges = edges.to_vec(); sorted_edges.sort_by_key(|(w, _)| *w); - let mut unionfind: polygon_unionfind::UnionFind, Vec> = - polygon_unionfind::UnionFind::with_len(vertex_count); + let mut unionfind: UnionFind = UnionFind::with_len(vertex_count); let mut min_spanning_tree = Vec::new(); for (_, uv) in sorted_edges { diff --git a/topola/src/navmesher.rs b/topola/src/navmesher.rs index 1e39769..68f661c 100644 --- a/topola/src/navmesher.rs +++ b/topola/src/navmesher.rs @@ -6,8 +6,9 @@ use dearcut::{RecordingTriangulator, VertexId}; use derive_getters::Getters; use derive_more::Constructor; use serde::{Deserialize, Serialize}; +use undoredo::Recorder; -use crate::Vector2; +use crate::{Board, Joint, JointId, Polygon, PolygonId, Segment, SegmentId, Vector2}; #[derive( Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize, @@ -169,3 +170,128 @@ impl Navmesher { } } } + +#[derive(Clone, Debug, Getters)] +pub struct NavmesherBoard { + navmesher: Navmesher, + board: Board, + + joint_multiobstacles: Recorder>, + segment_multiobstacles: Recorder>, + polygon_multiobstacles: Recorder>, +} + +impl NavmesherBoard { + pub fn new(board: Board) -> Self { + let mut this = Self { + navmesher: Navmesher::new( + board + .layout() + .boundary() + .iter() + .map(|p| Vector2::new(p[0], p[1])), + *board.layout().layer_count(), + ), + board, + + joint_multiobstacles: Recorder::new(Vec::new()), + segment_multiobstacles: Recorder::new(Vec::new()), + polygon_multiobstacles: Recorder::new(Vec::new()), + }; + + for (i, joint) in this.board.layout().joints().collection() { + this.joint_multiobstacles.insert( + i, + this.navmesher + .insert_multiobstacle(joint.layer, Self::joint_bounding_octagon(*joint)), + ); + } + + for (i, segment) in this.board.layout().segments().collection() { + this.segment_multiobstacles.insert( + i, + this.navmesher.insert_multiobstacle( + segment.layer, + this.segment_bounding_rectangle(SegmentId::new(i), *segment), + ), + ); + } + + for (i, polygon) in this.board.layout().polygons().collection() { + this.polygon_multiobstacles.insert( + i, + this.navmesher + .insert_multiobstacle(polygon.layer, polygon.vertices.clone()), + ); + } + + this + } + + pub fn insert_joint(&mut self, joint: Joint) -> JointId { + let joint_id = self.board.add_joint(joint); + self.joint_multiobstacles.insert( + joint_id.index(), + self.navmesher + .insert_multiobstacle(joint.layer, Self::joint_bounding_octagon(joint)), + ); + + joint_id + } + + fn joint_bounding_octagon(joint: Joint) -> [Vector2; 8] { + let cx = joint.position.x; + let cy = joint.position.y; + let r = joint.radius as i64; + + [ + Vector2::new(cx + r, cy + r / 2), + Vector2::new(cx + r / 2, cy + r), + Vector2::new(cx - r / 2, cy + r), + Vector2::new(cx - r, cy + r / 2), + Vector2::new(cx - r, cy - r / 2), + Vector2::new(cx - r / 2, cy - r), + Vector2::new(cx + r / 2, cy - r), + Vector2::new(cx + r, cy - r / 2), + ] + } + + pub fn insert_segment(&mut self, segment: Segment) -> SegmentId { + let segment_id = self.board.add_segment(segment); + self.segment_multiobstacles.insert( + segment_id.index(), + self.navmesher.insert_multiobstacle( + segment.layer, + self.segment_bounding_rectangle(segment_id, segment), + ), + ); + + segment_id + } + + fn segment_bounding_rectangle( + &self, + segment_id: SegmentId, + segment: Segment, + ) -> [Vector2; 4] { + let endpoints = self.board.layout().segment_endpoints(segment_id); + crate::math::inflated_segment( + endpoints[0].x, + endpoints[0].y, + endpoints[1].x, + endpoints[1].y, + segment.half_width, + ) + } + + pub fn insert_polygon(&mut self, polygon: Polygon) -> PolygonId { + let polygon_id = self.board.add_polygon(polygon.clone()); + self.polygon_multiobstacles.insert( + polygon_id.index(), + self.navmesher + .insert_multiobstacle(polygon.layer, polygon.vertices), + ); + + polygon_id + } +}