From 965080b8dba0d3f752f17bcaae4ea0cc1547082e Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Fri, 13 Mar 2026 22:56:46 +0100 Subject: [PATCH] Keep track of primitive id associated with navpolygon --- topola-egui/src/displayer.rs | 4 + topola/Cargo.toml | 2 +- topola/src/lib.rs | 1 + topola/src/navmesh.rs | 74 ++++++++++++++ topola/src/navmesher.rs | 180 ++++++++++++++--------------------- 5 files changed, 154 insertions(+), 107 deletions(-) create mode 100644 topola/src/navmesh.rs diff --git a/topola-egui/src/displayer.rs b/topola-egui/src/displayer.rs index de8d437..e4d8fa0 100644 --- a/topola-egui/src/displayer.rs +++ b/topola-egui/src/displayer.rs @@ -187,6 +187,7 @@ impl Displayer { if workspace.appearance_panel.visible[layer] { for navmesh in workspace.navmesher_board.navmesher().layers()[layer].navmeshes() { for edge_geom in navmesh + .triangulator() .triangulation() .rtreed_dcel() .edges_rtree() @@ -194,17 +195,20 @@ impl Displayer { .iter() { let (from_vertex, to_vertex) = navmesh + .triangulator() .triangulation() .rtreed_dcel() .dcel() .edge_endpoints(edge_geom.data); let from = navmesh + .triangulator() .triangulation() .rtreed_dcel() .dcel() .vertex_weight(from_vertex) .position(); let to = navmesh + .triangulator() .triangulation() .rtreed_dcel() .dcel() diff --git a/topola/Cargo.toml b/topola/Cargo.toml index d330eb8..dac59b6 100644 --- a/topola/Cargo.toml +++ b/topola/Cargo.toml @@ -10,7 +10,7 @@ edition = "2024" [dependencies] bimap = "0.6" -dearcut = { version = "0.1", features = ["undoredo"] } +dearcut = { version = "0.2", features = ["undoredo"] } derive-getters.workspace = true derive_more.workspace = true serde.workspace = true diff --git a/topola/src/lib.rs b/topola/src/lib.rs index f4a5caf..f9b3c38 100644 --- a/topola/src/lib.rs +++ b/topola/src/lib.rs @@ -5,6 +5,7 @@ mod board; mod layout; mod math; +mod navmesh; mod navmesher; mod primitives; mod selection; diff --git a/topola/src/navmesh.rs b/topola/src/navmesh.rs new file mode 100644 index 0000000..c18945c --- /dev/null +++ b/topola/src/navmesh.rs @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2026 Topola contributors +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use dearcut::RecordingTriangulator; +use derive_getters::Getters; + +use crate::primitives::PrimitiveId; + +#[derive(Clone, Debug, Getters)] +pub struct Navmesh { + boundary: Vec<[i64; 2]>, + triangulator: RecordingTriangulator, + navpolygon_primitives: Vec, + inflation_factor: f64, +} + +impl Navmesh { + pub fn new(boundary: impl IntoIterator) -> Self { + Self { + boundary: boundary.into_iter().collect(), + triangulator: RecordingTriangulator::new(), + navpolygon_primitives: Vec::new(), + inflation_factor: 0.0, + } + } + + pub fn insert_polygon( + &mut self, + primitive_id: PrimitiveId, + polygon: impl IntoIterator, + ) { + let navpolygon_index = self.triangulator.insert_polygon_and_rebuild( + Self::inflate_polygon(polygon, self.inflation_factor), + self.boundary.clone(), + ); + + self.navpolygon_primitives + .resize(navpolygon_index + 1, primitive_id); + self.navpolygon_primitives[navpolygon_index] = primitive_id; + } + + fn inflate_polygon( + polygon: impl IntoIterator, + inflation_factor: f64, + ) -> impl IntoIterator { + let polygon: Vec<[i64; 2]> = polygon.into_iter().collect(); + + // Centroid. + let cx = polygon.iter().map(|p| p[0] as f64).sum::() / polygon.len() as f64; + let cy = polygon.iter().map(|p| p[1] as f64).sum::() / polygon.len() as f64; + + polygon.into_iter().map(move |[px, py]| { + // Delta. + let dx = px as f64 - cx; + let dy = py as f64 - cy; + let d = (dx * dx + dy * dy).sqrt(); + + // Normalize delta. + let nx = dx / d; + let ny = dy / d; + + // Shift away from centroid. + let fx = px as f64 + nx * inflation_factor; + let fy = py as f64 + ny * inflation_factor; + + // Round away from centroid. + let rx = if fx >= cx { fx.ceil() } else { fx.floor() }; + let ry = if fy >= cy { fy.ceil() } else { fy.floor() }; + + [rx as i64, ry as i64] + }) + } +} diff --git a/topola/src/navmesher.rs b/topola/src/navmesher.rs index eb7ba32..795fc96 100644 --- a/topola/src/navmesher.rs +++ b/topola/src/navmesher.rs @@ -2,72 +2,41 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 -use dearcut::RecordingTriangulator; use derive_getters::Getters; use crate::{ Board, - primitives::{Joint, JointId, Polygon, PolygonId, Segment, SegmentId, Via, ViaId}, + navmesh::Navmesh, + primitives::{Joint, JointId, Polygon, PolygonId, PrimitiveId, Segment, SegmentId, Via, ViaId}, }; #[derive(Clone, Debug, Getters)] pub struct LayerNavmesher { boundary: Vec<[i64; 2]>, - navmeshes: Vec>, - inflation_factors: Vec, + navmeshes: Vec, } impl LayerNavmesher { - pub fn new(boundary: impl IntoIterator) -> Self { + fn new(boundary: impl IntoIterator) -> Self { + let boundary: Vec<[i64; 2]> = boundary.into_iter().collect(); + Self { - boundary: boundary.into_iter().collect(), - navmeshes: vec![RecordingTriangulator::new()], - inflation_factors: vec![0.0], + boundary: boundary.clone(), + navmeshes: vec![Navmesh::new(boundary)], } } - pub fn insert_polygon(&mut self, polygon: impl IntoIterator) { + fn insert_primitive_in_polygon( + &mut self, + primitive_id: PrimitiveId, + polygon: impl IntoIterator, + ) { let polygon: Vec<[i64; 2]> = polygon.into_iter().collect(); for i in 0..self.navmeshes.len() { - self.navmeshes[i].insert_polygon_and_rebuild( - Self::inflate_polygon(polygon.clone(), self.inflation_factors[i]), - self.boundary.clone(), - ); + self.navmeshes[i].insert_polygon(primitive_id, polygon.clone()); } } - - fn inflate_polygon( - polygon: impl IntoIterator, - inflation_factor: f64, - ) -> impl IntoIterator { - let polygon: Vec<[i64; 2]> = polygon.into_iter().collect(); - - // Centroid. - let cx = polygon.iter().map(|p| p[0] as f64).sum::() / polygon.len() as f64; - let cy = polygon.iter().map(|p| p[1] as f64).sum::() / polygon.len() as f64; - - polygon.into_iter().map(move |[px, py]| { - // Delta. - let dx = px as f64 - cx; - let dy = py as f64 - cy; - let d = (dx * dx + dy * dy).sqrt(); - - // Normalize delta. - let nx = dx / d; - let ny = dy / d; - - // Shift away from centroid. - let fx = px as f64 + nx * inflation_factor; - let fy = py as f64 + ny * inflation_factor; - - // Round away from centroid. - let rx = if fx >= cx { fx.ceil() } else { fx.floor() }; - let ry = if fy >= cy { fy.ceil() } else { fy.floor() }; - - [rx as i64, ry as i64] - }) - } } #[derive(Clone, Debug, Getters)] @@ -76,7 +45,7 @@ pub struct Navmesher { } impl Navmesher { - pub fn new(boundary: impl IntoIterator, layer_count: usize) -> Self { + fn new(boundary: impl IntoIterator, layer_count: usize) -> Self { let boundary: Vec<[i64; 2]> = boundary.into_iter().collect(); Self { @@ -86,46 +55,9 @@ impl Navmesher { } } - pub fn insert_polygon(&mut self, layer: usize, polygon: impl IntoIterator) { - self.layers[layer].insert_polygon(polygon); - } -} - -#[derive(Clone, Debug, Getters)] -pub struct NavmesherBoard { - navmesher: Navmesher, - board: Board, -} - -impl NavmesherBoard { - pub fn with_board(board: Board) -> Self { - let mut navmesher = Navmesher::new( - board.layout().boundary().clone(), - *board.layout().layer_count(), - ); - - for (_, joint) in board.layout().joints().collection() { - Self::insert_joint_in_navmesher(&mut navmesher, *joint); - } - - for (i, segment) in board.layout().segments().collection() { - Self::insert_segment_in_navmesher(&mut navmesher, &board, SegmentId::new(i), *segment); - } - - for (_, polygon) in board.layout().polygons().collection() { - Self::insert_polygon_in_navmesher(&mut navmesher, polygon.clone()); - } - - Self { navmesher, board } - } - - pub fn insert_joint(&mut self, joint: Joint) -> JointId { - Self::insert_joint_in_navmesher(&mut self.navmesher, joint); - self.board.add_joint(joint) - } - - fn insert_joint_in_navmesher(navmesher: &mut Navmesher, joint: Joint) { - navmesher.insert_polygon(joint.layer, Self::joint_circumscribed_octagon(joint)); + fn insert_joint(&mut self, joint_id: JointId, joint: Joint) { + self.layers[joint.layer] + .insert_primitive_in_polygon(joint_id.into(), Self::joint_circumscribed_octagon(joint)); } fn joint_circumscribed_octagon(joint: Joint) -> [[i64; 2]; 8] { @@ -145,23 +77,11 @@ impl NavmesherBoard { ] } - pub fn insert_segment(&mut self, segment: Segment) -> SegmentId { - let segment_id = self.board.add_segment(segment); - Self::insert_segment_in_navmesher(&mut self.navmesher, &self.board, segment_id, segment); - - segment_id - } - - fn insert_segment_in_navmesher( - navmesher: &mut Navmesher, - board: &Board, - segment_id: SegmentId, - segment: Segment, - ) { + fn insert_segment(&mut self, board: &Board, segment_id: SegmentId, segment: Segment) { let endpoints = board.layout().segment_endpoints(segment_id); - navmesher.insert_polygon( - segment.layer, + self.layers[segment.layer].insert_primitive_in_polygon( + segment_id.into(), Self::inflated_segment( endpoints[0][0], endpoints[0][1], @@ -191,17 +111,65 @@ impl NavmesherBoard { ] } + fn insert_polygon(&mut self, polygon_id: PolygonId, polygon: Polygon) { + self.layers[polygon.layer].insert_primitive_in_polygon(polygon_id.into(), polygon.vertices); + } +} + +#[derive(Clone, Debug, Getters)] +pub struct NavmesherBoard { + navmesher: Navmesher, + board: Board, +} + +impl NavmesherBoard { + pub fn with_board(board: Board) -> Self { + let mut navmesher = Navmesher::new( + board.layout().boundary().clone(), + *board.layout().layer_count(), + ); + + for (i, joint) in board.layout().joints().collection() { + navmesher.insert_joint(JointId::new(i).into(), *joint); + } + + for (i, segment) in board.layout().segments().collection() { + navmesher.insert_segment(&board, SegmentId::new(i), *segment); + } + + // TODO: Vias. + + for (i, polygon) in board.layout().polygons().collection() { + navmesher.insert_polygon(PolygonId::new(i), polygon.clone()); + } + + Self { navmesher, board } + } + + pub fn insert_joint(&mut self, joint: Joint) -> JointId { + let joint_id = self.board.add_joint(joint); + self.navmesher.insert_joint(joint_id, joint); + + joint_id + } + + pub fn insert_segment(&mut self, segment: Segment) -> SegmentId { + let segment_id = self.board.add_segment(segment); + self.navmesher + .insert_segment(&self.board, segment_id, segment); + + segment_id + } + 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 { - Self::insert_polygon_in_navmesher(&mut self.navmesher, polygon.clone()); - self.board.add_polygon(polygon) - } + let polygon_id = self.board.add_polygon(polygon.clone()); + self.navmesher.insert_polygon(polygon_id, polygon); - fn insert_polygon_in_navmesher(navmesher: &mut Navmesher, polygon: Polygon) { - navmesher.insert_polygon(polygon.layer, polygon.vertices); + polygon_id } }