From 67f3426586e8d59baf9512ae30e2867afa43140e Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Sun, 17 May 2026 02:18:42 +0200 Subject: [PATCH] Split `Segment` into full `Segment` and input `SegmentSpec` --- topola-egui/src/display.rs | 12 +++--- topola/src/board.rs | 15 +++++--- topola/src/layout.rs | 79 +++++++++++++------------------------- topola/src/math.rs | 4 +- topola/src/navmesher.rs | 32 ++++----------- topola/src/primitives.rs | 52 +++++++++++++++++++++++-- topola/src/ratsnest.rs | 2 +- topola/src/specctra.rs | 15 +++++--- 8 files changed, 109 insertions(+), 102 deletions(-) diff --git a/topola-egui/src/display.rs b/topola-egui/src/display.rs index d34b3f9..b217182 100644 --- a/topola-egui/src/display.rs +++ b/topola-egui/src/display.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::{viewport::Viewport, workspace::Workspace}; -use topola::{Joint, Polygon, Segment, Vector2}; +use topola::{Joint, Polygon, Segment}; pub struct Display {} @@ -65,7 +65,6 @@ impl Display { ui, viewport, segment, - layout.segment_endpoints(segment_id), workspace.appearance_panel.layer_color( ctx, board.layer_name(segment.layer), @@ -126,15 +125,14 @@ impl Display { ui: &egui::Ui, viewport: &Viewport, segment: &Segment, - endpoints: [Vector2; 2], color: egui::Color32, ) { ui.painter().line_segment( [ - egui::pos2(endpoints[0].x as f32, endpoints[0].y as f32), - egui::pos2(endpoints[1].x as f32, endpoints[1].y as f32), + egui::pos2(segment.endpoints[0].x as f32, segment.endpoints[0].y as f32), + egui::pos2(segment.endpoints[1].x as f32, segment.endpoints[1].y as f32), ], - egui::Stroke::new(segment.half_width as f32 * 2.0, color), + egui::Stroke::new(segment.spec.half_width as f32 * 2.0, color), ); } @@ -194,7 +192,7 @@ impl Display { } for segment_id in layout.layer_segments(layer) { - let endpoints = layout.segment_endpoints(segment_id); + let endpoints = layout.segment(segment_id).endpoints; ui.painter().rect_stroke( egui::Rect::from_two_pos( diff --git a/topola/src/board.rs b/topola/src/board.rs index b222214..0c5a92b 100644 --- a/topola/src/board.rs +++ b/topola/src/board.rs @@ -9,9 +9,8 @@ use undoredo::{ApplyDelta, Delta, FlushDelta}; use crate::{ layout::{Layout, LayoutHalfDelta, NetId, PinId}, math::Vector2, - primitives::{Joint, JointId, Polygon, PolygonId, Segment, SegmentId, Via, ViaId}, - selection::PinSelection, - selection::PinSelector, + primitives::{Joint, JointId, Polygon, PolygonId, Segment, SegmentId, SegmentSpec, Via, ViaId}, + selection::{PinSelection, PinSelector}, }; #[derive(Clone, Debug, Getters)] @@ -64,8 +63,12 @@ impl Board { self.layout.add_joint(joint) } - pub fn add_segment(&mut self, segment: Segment) -> SegmentId { - self.layout.add_segment(segment) + pub fn add_segment(&mut self, spec: SegmentSpec) -> SegmentId { + self.layout.add_segment(spec) + } + + pub fn add_segment_raw(&mut self, segment: Segment) -> SegmentId { + self.layout.add_segment_raw(segment) } pub fn add_via(&mut self, via: Via) -> ViaId { @@ -89,7 +92,7 @@ impl Board { let segment = self.layout.segment(segment_id); Some(PinSelector { - pin: self.pin_name(segment.pin?)?.to_string(), + pin: self.pin_name(segment.spec.pin?)?.to_string(), layer: self.layer_name(segment.layer)?.to_string(), }) } diff --git a/topola/src/layout.rs b/topola/src/layout.rs index d237b40..6a0d7ce 100644 --- a/topola/src/layout.rs +++ b/topola/src/layout.rs @@ -14,7 +14,10 @@ use serde::{Deserialize, Serialize}; use stable_vec::StableVec; use undoredo::{ApplyDelta, Delta, FlushDelta, Recorder}; -use crate::{Joint, JointId, Polygon, PolygonId, Segment, SegmentId, Vector2, Via, ViaId}; +use crate::{ + Joint, JointId, Polygon, PolygonId, Segment, SegmentId, Vector2, Via, ViaId, + primitives::SegmentSpec, +}; #[derive( Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize, @@ -24,7 +27,7 @@ pub struct PinId(usize); impl PinId { /// Returns the underlying index. #[inline] - pub fn id(self) -> usize { + pub fn index(self) -> usize { self.0 } } @@ -56,7 +59,7 @@ pub struct NetId(usize); impl NetId { /// Returns the underlying index. #[inline] - pub fn id(self) -> usize { + pub fn index(self) -> usize { self.0 } } @@ -114,22 +117,34 @@ impl Layout { .insert(GeomWithData::new(bbox, joint_id), ()); if let Some(pin_id) = pin_id { - self.pins[pin_id.id()].joints.push(joint_id); + self.pins[pin_id.index()].joints.push(joint_id); } joint_id } - pub fn add_segment(&mut self, segment: Segment) -> SegmentId { - let pin_id = segment.pin; + pub fn add_segment(&mut self, segment: SegmentSpec) -> SegmentId { + self.add_segment_raw(Segment { + spec: segment, + endpoints: [ + self.joint(segment.endjoints[0]).position, + self.joint(segment.endjoints[1]).position, + ], + layer: self.joint(segment.endjoints[0]).layer, + net: self.joint(segment.endjoints[0]).net, + }) + } + + pub fn add_segment_raw(&mut self, segment: Segment) -> SegmentId { + let pin_id = segment.spec.pin; + let bbox = segment.rtree_bbox(); let segment_id = SegmentId::new(self.segments.push(segment)); - let bbox = self.segment_bbox(segment_id); self.segments_rtree .insert(GeomWithData::new(bbox, segment_id), ()); if let Some(pin_id) = pin_id { - self.pins[pin_id.id()].segments.push(segment_id); + self.pins[pin_id.index()].segments.push(segment_id); } segment_id @@ -143,7 +158,7 @@ impl Layout { //self.vias_rtree.insert(GeomWithData::new(bbox, via_id), ()); if let Some(pin_id) = pin_id { - self.pins[pin_id.id()].vias.push(via_id); + self.pins[pin_id.index()].vias.push(via_id); } via_id @@ -158,52 +173,12 @@ impl Layout { .insert(GeomWithData::new(bbox, polygon_id), ()); if let Some(pin_id) = pin_id { - self.pins[pin_id.id()].polygons.push(polygon_id); + self.pins[pin_id.index()].polygons.push(polygon_id); } polygon_id } - pub fn segment_center(&self, segment_id: SegmentId) -> Vector2 { - let endpoints = self.segment_endpoints(segment_id); - - (endpoints[0] + endpoints[1]) / 2 - } - - pub fn segment_endpoints(&self, segment_id: SegmentId) -> [Vector2; 2] { - let endjoints = self.segments.get(&segment_id.index()).unwrap().endjoints; - [ - self.joints.get(&endjoints[0].index()).unwrap().position, - self.joints.get(&endjoints[1].index()).unwrap().position, - ] - } - - pub fn segment_contains_point(&self, segment_id: SegmentId, point: Vector2) -> bool { - let endpoints = self.segment_endpoints(segment_id); - let segment = self.segments.get(&segment_id.index()).unwrap(); - let vertices = crate::math::inflated_segment( - endpoints[0].x, - endpoints[0].y, - endpoints[1].x, - endpoints[1].y, - segment.half_width, - ); - point.inside_polygon(&vertices) - } - - pub fn segment_bbox(&self, segment_id: SegmentId) -> Rectangle<[i64; 3]> { - let endpoints = self.segment_endpoints(segment_id); - let layer = self.segments.get(&segment_id.index()).unwrap().layer as i64; - let half_width = self.segments.get(&segment_id.index()).unwrap().half_width as i64; - - let min_x = std::cmp::min(endpoints[0].x, endpoints[1].x) - half_width; - let min_y = std::cmp::min(endpoints[0].y, endpoints[1].y) - half_width; - let max_x = std::cmp::max(endpoints[0].x, endpoints[1].x) + half_width; - let max_y = std::cmp::max(endpoints[0].y, endpoints[1].y) + half_width; - - Rectangle::from_corners([min_x, min_y, layer], [max_x, max_y, layer]) - } - pub fn locate_joints_at_point( &self, layer: usize, @@ -230,7 +205,7 @@ impl Layout { .as_ref() .locate_all_at_point(&[point.x, point.y, layer as i64]) .map(|geom_with_data| geom_with_data.data) - .filter(move |segment_id| self.segment_contains_point(*segment_id, point)) + .filter(move |&segment_id| self.segment(segment_id).contains_point(point)) } // TODO: vias. @@ -299,7 +274,7 @@ impl Layout { } pub fn pin(&self, pin_id: PinId) -> &Pin { - &self.pins[pin_id.id()] + &self.pins[pin_id.index()] } } diff --git a/topola/src/math.rs b/topola/src/math.rs index e6aff9d..c6bc9d8 100644 --- a/topola/src/math.rs +++ b/topola/src/math.rs @@ -116,8 +116,8 @@ impl_inside_polygon!(f64); impl_inside_polygon!(i32); impl_inside_polygon!(i64); -/// Returns the four vertices of a segment inflated by `half_width`, forming a convex -/// quadrilateral. The segment goes from (x1, y1) to (x2, y2). +/// Returns the four vertices of a segment inflated by `half_width`, forming a +/// convex quadrilateral. The segment goes from (x1, y1) to (x2, y2). pub fn inflated_segment(x1: i64, y1: i64, x2: i64, y2: i64, half_width: u64) -> [Vector2; 4] { let dx = x2 - x1; let dy = y2 - y1; diff --git a/topola/src/navmesher.rs b/topola/src/navmesher.rs index 68f661c..3400769 100644 --- a/topola/src/navmesher.rs +++ b/topola/src/navmesher.rs @@ -210,10 +210,8 @@ impl NavmesherBoard { 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), - ), + this.navmesher + .insert_multiobstacle(segment.layer, segment.bbox()), ); } @@ -256,34 +254,18 @@ impl NavmesherBoard { ] } - pub fn insert_segment(&mut self, segment: Segment) -> SegmentId { - let segment_id = self.board.add_segment(segment); + pub fn insert_segment_with_cache(&mut self, segment: Segment) -> SegmentId { + let layer = segment.layer; + let obstacle = segment.bbox(); + let segment_id = self.board.add_segment_raw(segment); self.segment_multiobstacles.insert( segment_id.index(), - self.navmesher.insert_multiobstacle( - segment.layer, - self.segment_bounding_rectangle(segment_id, segment), - ), + self.navmesher.insert_multiobstacle(layer, obstacle), ); 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( diff --git a/topola/src/primitives.rs b/topola/src/primitives.rs index c09ab12..87387a5 100644 --- a/topola/src/primitives.rs +++ b/topola/src/primitives.rs @@ -80,14 +80,60 @@ impl SegmentId { } #[derive(Clone, Copy, Debug)] -pub struct Segment { +pub struct SegmentSpec { pub endjoints: [JointId; 2], - pub layer: usize, pub half_width: u64, - pub net: NetId, pub pin: Option, } +#[derive(Clone, Copy, Debug)] +pub struct Segment { + pub spec: SegmentSpec, + pub endpoints: [Vector2; 2], + pub layer: usize, + pub net: NetId, +} + +impl Segment { + pub fn center(&self) -> Vector2 { + (self.endpoints[0] + self.endpoints[1]) / 2 + } + + pub fn contains_point(&self, point: Vector2) -> bool { + let vertices = crate::math::inflated_segment( + self.endpoints[0].x, + self.endpoints[0].y, + self.endpoints[1].x, + self.endpoints[1].y, + self.spec.half_width, + ); + point.inside_polygon(&vertices) + } + + pub fn bbox(&self) -> [Vector2; 4] { + crate::math::inflated_segment( + self.endpoints[0].x, + self.endpoints[0].y, + self.endpoints[1].x, + self.endpoints[1].y, + self.spec.half_width, + ) + } + + pub fn rtree_bbox(&self) -> Rectangle<[i64; 3]> { + let endpoints = self.endpoints; + let layer = self.layer as i64; + let half_width = self.spec.half_width as i64; + + let min_x = std::cmp::min(endpoints[0].x, endpoints[1].x) - half_width; + let min_y = std::cmp::min(endpoints[0].y, endpoints[1].y) - half_width; + let max_x = std::cmp::max(endpoints[0].x, endpoints[1].x) + half_width; + let max_y = std::cmp::max(endpoints[0].y, endpoints[1].y) + half_width; + + Rectangle::from_corners([min_x, min_y, layer], [max_x, max_y, layer]) + } +} + #[derive( Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize, )] diff --git a/topola/src/ratsnest.rs b/topola/src/ratsnest.rs index 05229b6..a842549 100644 --- a/topola/src/ratsnest.rs +++ b/topola/src/ratsnest.rs @@ -59,7 +59,7 @@ impl Ratsnest { } for (i, segment) in board.layout().segments().collection() { - let segment_center = board.layout().segment_center(SegmentId::new(i)); + let segment_center = segment.center(); let _ = triangulations .entry((segment.net, segment.layer)) .or_insert_with(DelaunayTriangulation::new) diff --git a/topola/src/specctra.rs b/topola/src/specctra.rs index 7e6701e..9dc32ac 100644 --- a/topola/src/specctra.rs +++ b/topola/src/specctra.rs @@ -14,7 +14,7 @@ use crate::{ Segment, Vector2, board::Board, layout::{NetId, PinId}, - primitives::{Joint, Polygon}, + primitives::{Joint, Polygon, SegmentSpec}, }; impl Board { @@ -351,12 +351,15 @@ impl Board { }); // Add a seg between the current and previous coords. - let _ = board.add_segment(Segment { - endjoints: [prev_joint, joint], + let _ = board.add_segment_raw(Segment { + spec: SegmentSpec { + endjoints: [prev_joint, joint], + half_width: (width / 2.0) as u64, + pin, + }, + endpoints: [prev_pos, pos], layer, - half_width: (width / 2.0) as u64, net, - pin, }); prev_pos = pos; @@ -369,7 +372,7 @@ impl Board { place: PointWithRotation, pin_pos: PointWithRotation, coords: &[Point], - width: f64, + _width: f64, layer: usize, net: NetId, pin: Option,