Keep track of primitive id associated with navpolygon

This commit is contained in:
Mikolaj Wielgus 2026-03-13 22:56:46 +01:00
parent 4afd0d7e3d
commit 965080b8db
5 changed files with 154 additions and 107 deletions

View File

@ -187,6 +187,7 @@ impl Displayer {
if workspace.appearance_panel.visible[layer] { if workspace.appearance_panel.visible[layer] {
for navmesh in workspace.navmesher_board.navmesher().layers()[layer].navmeshes() { for navmesh in workspace.navmesher_board.navmesher().layers()[layer].navmeshes() {
for edge_geom in navmesh for edge_geom in navmesh
.triangulator()
.triangulation() .triangulation()
.rtreed_dcel() .rtreed_dcel()
.edges_rtree() .edges_rtree()
@ -194,17 +195,20 @@ impl Displayer {
.iter() .iter()
{ {
let (from_vertex, to_vertex) = navmesh let (from_vertex, to_vertex) = navmesh
.triangulator()
.triangulation() .triangulation()
.rtreed_dcel() .rtreed_dcel()
.dcel() .dcel()
.edge_endpoints(edge_geom.data); .edge_endpoints(edge_geom.data);
let from = navmesh let from = navmesh
.triangulator()
.triangulation() .triangulation()
.rtreed_dcel() .rtreed_dcel()
.dcel() .dcel()
.vertex_weight(from_vertex) .vertex_weight(from_vertex)
.position(); .position();
let to = navmesh let to = navmesh
.triangulator()
.triangulation() .triangulation()
.rtreed_dcel() .rtreed_dcel()
.dcel() .dcel()

View File

@ -10,7 +10,7 @@ edition = "2024"
[dependencies] [dependencies]
bimap = "0.6" bimap = "0.6"
dearcut = { version = "0.1", features = ["undoredo"] } dearcut = { version = "0.2", features = ["undoredo"] }
derive-getters.workspace = true derive-getters.workspace = true
derive_more.workspace = true derive_more.workspace = true
serde.workspace = true serde.workspace = true

View File

@ -5,6 +5,7 @@
mod board; mod board;
mod layout; mod layout;
mod math; mod math;
mod navmesh;
mod navmesher; mod navmesher;
mod primitives; mod primitives;
mod selection; mod selection;

74
topola/src/navmesh.rs Normal file
View File

@ -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<i64>,
navpolygon_primitives: Vec<PrimitiveId>,
inflation_factor: f64,
}
impl Navmesh {
pub fn new(boundary: impl IntoIterator<Item = [i64; 2]>) -> 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<Item = [i64; 2]>,
) {
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<Item = [i64; 2]>,
inflation_factor: f64,
) -> impl IntoIterator<Item = [i64; 2]> {
let polygon: Vec<[i64; 2]> = polygon.into_iter().collect();
// Centroid.
let cx = polygon.iter().map(|p| p[0] as f64).sum::<f64>() / polygon.len() as f64;
let cy = polygon.iter().map(|p| p[1] as f64).sum::<f64>() / 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]
})
}
}

View File

@ -2,72 +2,41 @@
// //
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
use dearcut::RecordingTriangulator;
use derive_getters::Getters; use derive_getters::Getters;
use crate::{ use crate::{
Board, 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)] #[derive(Clone, Debug, Getters)]
pub struct LayerNavmesher { pub struct LayerNavmesher {
boundary: Vec<[i64; 2]>, boundary: Vec<[i64; 2]>,
navmeshes: Vec<RecordingTriangulator<i64>>, navmeshes: Vec<Navmesh>,
inflation_factors: Vec<f64>,
} }
impl LayerNavmesher { impl LayerNavmesher {
pub fn new(boundary: impl IntoIterator<Item = [i64; 2]>) -> Self { fn new(boundary: impl IntoIterator<Item = [i64; 2]>) -> Self {
let boundary: Vec<[i64; 2]> = boundary.into_iter().collect();
Self { Self {
boundary: boundary.into_iter().collect(), boundary: boundary.clone(),
navmeshes: vec![RecordingTriangulator::new()], navmeshes: vec![Navmesh::new(boundary)],
inflation_factors: vec![0.0],
} }
} }
pub fn insert_polygon(&mut self, polygon: impl IntoIterator<Item = [i64; 2]>) { fn insert_primitive_in_polygon(
&mut self,
primitive_id: PrimitiveId,
polygon: impl IntoIterator<Item = [i64; 2]>,
) {
let polygon: Vec<[i64; 2]> = polygon.into_iter().collect(); let polygon: Vec<[i64; 2]> = polygon.into_iter().collect();
for i in 0..self.navmeshes.len() { for i in 0..self.navmeshes.len() {
self.navmeshes[i].insert_polygon_and_rebuild( self.navmeshes[i].insert_polygon(primitive_id, polygon.clone());
Self::inflate_polygon(polygon.clone(), self.inflation_factors[i]),
self.boundary.clone(),
);
} }
} }
fn inflate_polygon(
polygon: impl IntoIterator<Item = [i64; 2]>,
inflation_factor: f64,
) -> impl IntoIterator<Item = [i64; 2]> {
let polygon: Vec<[i64; 2]> = polygon.into_iter().collect();
// Centroid.
let cx = polygon.iter().map(|p| p[0] as f64).sum::<f64>() / polygon.len() as f64;
let cy = polygon.iter().map(|p| p[1] as f64).sum::<f64>() / 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)] #[derive(Clone, Debug, Getters)]
@ -76,7 +45,7 @@ pub struct Navmesher {
} }
impl Navmesher { impl Navmesher {
pub fn new(boundary: impl IntoIterator<Item = [i64; 2]>, layer_count: usize) -> Self { fn new(boundary: impl IntoIterator<Item = [i64; 2]>, layer_count: usize) -> Self {
let boundary: Vec<[i64; 2]> = boundary.into_iter().collect(); let boundary: Vec<[i64; 2]> = boundary.into_iter().collect();
Self { Self {
@ -86,46 +55,9 @@ impl Navmesher {
} }
} }
pub fn insert_polygon(&mut self, layer: usize, polygon: impl IntoIterator<Item = [i64; 2]>) { fn insert_joint(&mut self, joint_id: JointId, joint: Joint) {
self.layers[layer].insert_polygon(polygon); self.layers[joint.layer]
} .insert_primitive_in_polygon(joint_id.into(), Self::joint_circumscribed_octagon(joint));
}
#[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 joint_circumscribed_octagon(joint: Joint) -> [[i64; 2]; 8] { fn joint_circumscribed_octagon(joint: Joint) -> [[i64; 2]; 8] {
@ -145,23 +77,11 @@ impl NavmesherBoard {
] ]
} }
pub fn insert_segment(&mut self, segment: Segment) -> SegmentId { fn insert_segment(&mut self, board: &Board, segment_id: SegmentId, segment: Segment) {
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,
) {
let endpoints = board.layout().segment_endpoints(segment_id); let endpoints = board.layout().segment_endpoints(segment_id);
navmesher.insert_polygon( self.layers[segment.layer].insert_primitive_in_polygon(
segment.layer, segment_id.into(),
Self::inflated_segment( Self::inflated_segment(
endpoints[0][0], endpoints[0][0],
endpoints[0][1], 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 { pub fn insert_via(&mut self, via: Via) -> ViaId {
// TODO: Insert into navmesh. // TODO: Insert into navmesh.
self.board.add_via(via) self.board.add_via(via)
} }
pub fn insert_polygon(&mut self, polygon: Polygon) -> PolygonId { pub fn insert_polygon(&mut self, polygon: Polygon) -> PolygonId {
Self::insert_polygon_in_navmesher(&mut self.navmesher, polygon.clone()); let polygon_id = self.board.add_polygon(polygon.clone());
self.board.add_polygon(polygon) self.navmesher.insert_polygon(polygon_id, polygon);
}
fn insert_polygon_in_navmesher(navmesher: &mut Navmesher, polygon: Polygon) { polygon_id
navmesher.insert_polygon(polygon.layer, polygon.vertices);
} }
} }