Split `Segment` into full `Segment` and input `SegmentSpec`

This commit is contained in:
Mikolaj Wielgus 2026-05-17 02:18:42 +02:00
parent 36004b155b
commit 67f3426586
8 changed files with 109 additions and 102 deletions

View File

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
use crate::{viewport::Viewport, workspace::Workspace}; use crate::{viewport::Viewport, workspace::Workspace};
use topola::{Joint, Polygon, Segment, Vector2}; use topola::{Joint, Polygon, Segment};
pub struct Display {} pub struct Display {}
@ -65,7 +65,6 @@ impl Display {
ui, ui,
viewport, viewport,
segment, segment,
layout.segment_endpoints(segment_id),
workspace.appearance_panel.layer_color( workspace.appearance_panel.layer_color(
ctx, ctx,
board.layer_name(segment.layer), board.layer_name(segment.layer),
@ -126,15 +125,14 @@ impl Display {
ui: &egui::Ui, ui: &egui::Ui,
viewport: &Viewport, viewport: &Viewport,
segment: &Segment, segment: &Segment,
endpoints: [Vector2<i64>; 2],
color: egui::Color32, color: egui::Color32,
) { ) {
ui.painter().line_segment( ui.painter().line_segment(
[ [
egui::pos2(endpoints[0].x as f32, endpoints[0].y as f32), egui::pos2(segment.endpoints[0].x as f32, segment.endpoints[0].y as f32),
egui::pos2(endpoints[1].x as f32, endpoints[1].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) { 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( ui.painter().rect_stroke(
egui::Rect::from_two_pos( egui::Rect::from_two_pos(

View File

@ -9,9 +9,8 @@ use undoredo::{ApplyDelta, Delta, FlushDelta};
use crate::{ use crate::{
layout::{Layout, LayoutHalfDelta, NetId, PinId}, layout::{Layout, LayoutHalfDelta, NetId, PinId},
math::Vector2, math::Vector2,
primitives::{Joint, JointId, Polygon, PolygonId, Segment, SegmentId, Via, ViaId}, primitives::{Joint, JointId, Polygon, PolygonId, Segment, SegmentId, SegmentSpec, Via, ViaId},
selection::PinSelection, selection::{PinSelection, PinSelector},
selection::PinSelector,
}; };
#[derive(Clone, Debug, Getters)] #[derive(Clone, Debug, Getters)]
@ -64,8 +63,12 @@ impl Board {
self.layout.add_joint(joint) self.layout.add_joint(joint)
} }
pub fn add_segment(&mut self, segment: Segment) -> SegmentId { pub fn add_segment(&mut self, spec: SegmentSpec) -> SegmentId {
self.layout.add_segment(segment) 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 { pub fn add_via(&mut self, via: Via) -> ViaId {
@ -89,7 +92,7 @@ impl Board {
let segment = self.layout.segment(segment_id); let segment = self.layout.segment(segment_id);
Some(PinSelector { 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(), layer: self.layer_name(segment.layer)?.to_string(),
}) })
} }

View File

@ -14,7 +14,10 @@ use serde::{Deserialize, Serialize};
use stable_vec::StableVec; use stable_vec::StableVec;
use undoredo::{ApplyDelta, Delta, FlushDelta, Recorder}; 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( #[derive(
Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize, Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
@ -24,7 +27,7 @@ pub struct PinId(usize);
impl PinId { impl PinId {
/// Returns the underlying index. /// Returns the underlying index.
#[inline] #[inline]
pub fn id(self) -> usize { pub fn index(self) -> usize {
self.0 self.0
} }
} }
@ -56,7 +59,7 @@ pub struct NetId(usize);
impl NetId { impl NetId {
/// Returns the underlying index. /// Returns the underlying index.
#[inline] #[inline]
pub fn id(self) -> usize { pub fn index(self) -> usize {
self.0 self.0
} }
} }
@ -114,22 +117,34 @@ impl Layout {
.insert(GeomWithData::new(bbox, joint_id), ()); .insert(GeomWithData::new(bbox, joint_id), ());
if let Some(pin_id) = pin_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 joint_id
} }
pub fn add_segment(&mut self, segment: Segment) -> SegmentId { pub fn add_segment(&mut self, segment: SegmentSpec) -> SegmentId {
let pin_id = segment.pin; 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 segment_id = SegmentId::new(self.segments.push(segment));
let bbox = self.segment_bbox(segment_id);
self.segments_rtree self.segments_rtree
.insert(GeomWithData::new(bbox, segment_id), ()); .insert(GeomWithData::new(bbox, segment_id), ());
if let Some(pin_id) = pin_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 segment_id
@ -143,7 +158,7 @@ impl Layout {
//self.vias_rtree.insert(GeomWithData::new(bbox, via_id), ()); //self.vias_rtree.insert(GeomWithData::new(bbox, via_id), ());
if let Some(pin_id) = pin_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 via_id
@ -158,52 +173,12 @@ impl Layout {
.insert(GeomWithData::new(bbox, polygon_id), ()); .insert(GeomWithData::new(bbox, polygon_id), ());
if let Some(pin_id) = pin_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 polygon_id
} }
pub fn segment_center(&self, segment_id: SegmentId) -> Vector2<i64> {
let endpoints = self.segment_endpoints(segment_id);
(endpoints[0] + endpoints[1]) / 2
}
pub fn segment_endpoints(&self, segment_id: SegmentId) -> [Vector2<i64>; 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<i64>) -> 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( pub fn locate_joints_at_point(
&self, &self,
layer: usize, layer: usize,
@ -230,7 +205,7 @@ impl Layout {
.as_ref() .as_ref()
.locate_all_at_point(&[point.x, point.y, layer as i64]) .locate_all_at_point(&[point.x, point.y, layer as i64])
.map(|geom_with_data| geom_with_data.data) .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. // TODO: vias.
@ -299,7 +274,7 @@ impl Layout {
} }
pub fn pin(&self, pin_id: PinId) -> &Pin { pub fn pin(&self, pin_id: PinId) -> &Pin {
&self.pins[pin_id.id()] &self.pins[pin_id.index()]
} }
} }

View File

@ -116,8 +116,8 @@ impl_inside_polygon!(f64);
impl_inside_polygon!(i32); impl_inside_polygon!(i32);
impl_inside_polygon!(i64); impl_inside_polygon!(i64);
/// Returns the four vertices of a segment inflated by `half_width`, forming a convex /// Returns the four vertices of a segment inflated by `half_width`, forming a
/// quadrilateral. The segment goes from (x1, y1) to (x2, y2). /// 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<i64>; 4] { pub fn inflated_segment(x1: i64, y1: i64, x2: i64, y2: i64, half_width: u64) -> [Vector2<i64>; 4] {
let dx = x2 - x1; let dx = x2 - x1;
let dy = y2 - y1; let dy = y2 - y1;

View File

@ -210,10 +210,8 @@ impl NavmesherBoard {
for (i, segment) in this.board.layout().segments().collection() { for (i, segment) in this.board.layout().segments().collection() {
this.segment_multiobstacles.insert( this.segment_multiobstacles.insert(
i, i,
this.navmesher.insert_multiobstacle( this.navmesher
segment.layer, .insert_multiobstacle(segment.layer, segment.bbox()),
this.segment_bounding_rectangle(SegmentId::new(i), *segment),
),
); );
} }
@ -256,34 +254,18 @@ impl NavmesherBoard {
] ]
} }
pub fn insert_segment(&mut self, segment: Segment) -> SegmentId { pub fn insert_segment_with_cache(&mut self, segment: Segment) -> SegmentId {
let segment_id = self.board.add_segment(segment); let layer = segment.layer;
let obstacle = segment.bbox();
let segment_id = self.board.add_segment_raw(segment);
self.segment_multiobstacles.insert( self.segment_multiobstacles.insert(
segment_id.index(), segment_id.index(),
self.navmesher.insert_multiobstacle( self.navmesher.insert_multiobstacle(layer, obstacle),
segment.layer,
self.segment_bounding_rectangle(segment_id, segment),
),
); );
segment_id segment_id
} }
fn segment_bounding_rectangle(
&self,
segment_id: SegmentId,
segment: Segment,
) -> [Vector2<i64>; 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 { pub fn insert_polygon(&mut self, polygon: Polygon) -> PolygonId {
let polygon_id = self.board.add_polygon(polygon.clone()); let polygon_id = self.board.add_polygon(polygon.clone());
self.polygon_multiobstacles.insert( self.polygon_multiobstacles.insert(

View File

@ -80,14 +80,60 @@ impl SegmentId {
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct Segment { pub struct SegmentSpec {
pub endjoints: [JointId; 2], pub endjoints: [JointId; 2],
pub layer: usize,
pub half_width: u64, pub half_width: u64,
pub net: NetId,
pub pin: Option<PinId>, pub pin: Option<PinId>,
} }
#[derive(Clone, Copy, Debug)]
pub struct Segment {
pub spec: SegmentSpec,
pub endpoints: [Vector2<i64>; 2],
pub layer: usize,
pub net: NetId,
}
impl Segment {
pub fn center(&self) -> Vector2<i64> {
(self.endpoints[0] + self.endpoints[1]) / 2
}
pub fn contains_point(&self, point: Vector2<i64>) -> 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<i64>; 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( #[derive(
Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize, Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
)] )]

View File

@ -59,7 +59,7 @@ impl Ratsnest {
} }
for (i, segment) in board.layout().segments().collection() { 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 let _ = triangulations
.entry((segment.net, segment.layer)) .entry((segment.net, segment.layer))
.or_insert_with(DelaunayTriangulation::new) .or_insert_with(DelaunayTriangulation::new)

View File

@ -14,7 +14,7 @@ use crate::{
Segment, Vector2, Segment, Vector2,
board::Board, board::Board,
layout::{NetId, PinId}, layout::{NetId, PinId},
primitives::{Joint, Polygon}, primitives::{Joint, Polygon, SegmentSpec},
}; };
impl Board { impl Board {
@ -351,12 +351,15 @@ impl Board {
}); });
// Add a seg between the current and previous coords. // Add a seg between the current and previous coords.
let _ = board.add_segment(Segment { let _ = board.add_segment_raw(Segment {
endjoints: [prev_joint, joint], spec: SegmentSpec {
endjoints: [prev_joint, joint],
half_width: (width / 2.0) as u64,
pin,
},
endpoints: [prev_pos, pos],
layer, layer,
half_width: (width / 2.0) as u64,
net, net,
pin,
}); });
prev_pos = pos; prev_pos = pos;
@ -369,7 +372,7 @@ impl Board {
place: PointWithRotation, place: PointWithRotation,
pin_pos: PointWithRotation, pin_pos: PointWithRotation,
coords: &[Point], coords: &[Point],
width: f64, _width: f64,
layer: usize, layer: usize,
net: NetId, net: NetId,
pin: Option<PinId>, pin: Option<PinId>,