mirror of https://codeberg.org/topola/topola.git
Compare commits
5 Commits
fc3857072c
...
7f67a24808
| Author | SHA1 | Date |
|---|---|---|
|
|
7f67a24808 | |
|
|
272bdb326d | |
|
|
258b43267d | |
|
|
67f3426586 | |
|
|
36004b155b |
|
|
@ -12,7 +12,7 @@ derive-getters = "0.5"
|
|||
derive_more = { version = "2.1", features = ["full"] }
|
||||
serde = { version = "1", features = ["derive", "rc"] }
|
||||
thiserror = "2.0"
|
||||
undoredo = { version = "0.8", features = ["stable-vec"] }
|
||||
undoredo = { version = "0.10", features = ["derive", "stable-vec", "rstar"] }
|
||||
|
||||
[profile.release]
|
||||
opt-level = 2 # Fast and small WASM.
|
||||
|
|
|
|||
|
|
@ -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<i64>; 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(
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ dearcut = { version = "0.3", features = ["serde", "undoredo"] }
|
|||
derive-getters.workspace = true
|
||||
derive_more.workspace = true
|
||||
i_triangle = "0.40"
|
||||
polygon_unionfind = "0.5"
|
||||
polygon_unionfind = "0.7"
|
||||
rstar = "0.12"
|
||||
serde.workspace = true
|
||||
spade = "2.15"
|
||||
|
|
|
|||
|
|
@ -9,9 +9,10 @@ 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, ViaSpec,
|
||||
},
|
||||
selection::{PinSelection, PinSelector},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Getters)]
|
||||
|
|
@ -64,12 +65,20 @@ 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_via(&mut self, via: Via) -> ViaId {
|
||||
self.layout.add_via(via)
|
||||
pub fn add_segment_raw(&mut self, segment: Segment) -> SegmentId {
|
||||
self.layout.add_segment_raw(segment)
|
||||
}
|
||||
|
||||
pub fn add_via(&mut self, spec: ViaSpec) -> ViaId {
|
||||
self.layout.add_via(spec)
|
||||
}
|
||||
|
||||
pub fn add_via_raw(&mut self, via: Via) -> ViaId {
|
||||
self.layout.add_via_raw(via)
|
||||
}
|
||||
|
||||
pub fn add_polygon(&mut self, polygon: Polygon) -> PolygonId {
|
||||
|
|
@ -89,7 +98,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(),
|
||||
})
|
||||
}
|
||||
|
|
@ -192,11 +201,11 @@ pub struct BoardHalfDelta {
|
|||
}
|
||||
|
||||
impl ApplyDelta<BoardHalfDelta> for Board {
|
||||
fn apply_delta(&mut self, delta: &Delta<BoardHalfDelta>) {
|
||||
let (removed, inserted) = delta.clone().dissolve();
|
||||
fn apply_delta(&mut self, delta: Delta<BoardHalfDelta>) {
|
||||
let (removed, inserted) = delta.dissolve();
|
||||
|
||||
let layout_delta = Delta::with_removed_inserted(removed.layout, inserted.layout);
|
||||
self.layout.apply_delta(&layout_delta);
|
||||
self.layout.apply_delta(layout_delta);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@ impl Connectivity {
|
|||
pub fn new(board: &Board) -> Self {
|
||||
let mut this = Connectivity {
|
||||
joints_unionfind: UnionFind::with_len(
|
||||
board.layout().joints().collection().num_elements(),
|
||||
board.layout().joints().container().num_elements(),
|
||||
),
|
||||
segments_unionfind: UnionFind::with_len(
|
||||
board.layout().segments().collection().num_elements(),
|
||||
board.layout().segments().container().num_elements(),
|
||||
),
|
||||
polygons_unionfind: UnionFind::with_len(
|
||||
board.layout().polygons().collection().num_elements(),
|
||||
board.layout().polygons().container().num_elements(),
|
||||
),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use derive_getters::{Dissolve, Getters};
|
||||
use derive_getters::Getters;
|
||||
use derive_more::Constructor;
|
||||
use rstar::{
|
||||
AABB, RTree,
|
||||
|
|
@ -12,9 +10,13 @@ use rstar::{
|
|||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use stable_vec::StableVec;
|
||||
use undoredo::{ApplyDelta, Delta, FlushDelta, Recorder};
|
||||
use undoredo::aliases::RTreeHalfDelta;
|
||||
use undoredo::{Delta, 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, ViaSpec},
|
||||
};
|
||||
|
||||
#[derive(
|
||||
Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
|
||||
|
|
@ -24,7 +26,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,17 +58,21 @@ pub struct NetId(usize);
|
|||
impl NetId {
|
||||
/// Returns the underlying index.
|
||||
#[inline]
|
||||
pub fn id(self) -> usize {
|
||||
pub fn index(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Getters)]
|
||||
#[derive(Delta, Clone, Debug, Getters)]
|
||||
pub struct Layout {
|
||||
#[undoredo(skip)]
|
||||
boundary: Vec<[i64; 2]>,
|
||||
#[undoredo(skip)]
|
||||
place_boundary: Vec<[i64; 2]>,
|
||||
#[undoredo(skip)]
|
||||
layer_count: usize,
|
||||
|
||||
#[undoredo(skip)]
|
||||
pins: StableVec<Pin>,
|
||||
|
||||
joints: Recorder<StableVec<Joint>>,
|
||||
|
|
@ -74,10 +80,22 @@ pub struct Layout {
|
|||
vias: Recorder<StableVec<Via>>,
|
||||
polygons: Recorder<StableVec<Polygon>>,
|
||||
|
||||
joints_rtree: Recorder<RTree<GeomWithData<Rectangle<[i64; 3]>, JointId>>>,
|
||||
segments_rtree: Recorder<RTree<GeomWithData<Rectangle<[i64; 3]>, SegmentId>>>,
|
||||
vias_rtree: Recorder<RTree<GeomWithData<Rectangle<[i64; 3]>, ViaId>>>,
|
||||
polygons_rtree: Recorder<RTree<GeomWithData<Rectangle<[i64; 3]>, PolygonId>>>,
|
||||
joints_rtree: Recorder<
|
||||
RTree<GeomWithData<Rectangle<[i64; 3]>, JointId>>,
|
||||
RTreeHalfDelta<GeomWithData<Rectangle<[i64; 3]>, JointId>>,
|
||||
>,
|
||||
segments_rtree: Recorder<
|
||||
RTree<GeomWithData<Rectangle<[i64; 3]>, SegmentId>>,
|
||||
RTreeHalfDelta<GeomWithData<Rectangle<[i64; 3]>, SegmentId>>,
|
||||
>,
|
||||
vias_rtree: Recorder<
|
||||
RTree<GeomWithData<Rectangle<[i64; 3]>, ViaId>>,
|
||||
RTreeHalfDelta<GeomWithData<Rectangle<[i64; 3]>, ViaId>>,
|
||||
>,
|
||||
polygons_rtree: Recorder<
|
||||
RTree<GeomWithData<Rectangle<[i64; 3]>, PolygonId>>,
|
||||
RTreeHalfDelta<GeomWithData<Rectangle<[i64; 3]>, PolygonId>>,
|
||||
>,
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
|
|
@ -114,36 +132,61 @@ 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, spec: SegmentSpec) -> SegmentId {
|
||||
self.add_segment_raw(Segment {
|
||||
spec,
|
||||
endpoints: [
|
||||
self.joint(spec.endjoints[0]).position,
|
||||
self.joint(spec.endjoints[1]).position,
|
||||
],
|
||||
layer: self.joint(spec.endjoints[0]).layer,
|
||||
net: self.joint(spec.endjoints[0]).net,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_segment_raw(&mut self, segment: Segment) -> SegmentId {
|
||||
let pin_id = segment.spec.pin;
|
||||
let bbox = segment.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
|
||||
}
|
||||
|
||||
pub fn add_via(&mut self, via: Via) -> ViaId {
|
||||
//let bbox = via.bbox();
|
||||
let pin_id = via.pin;
|
||||
pub fn add_via(&mut self, spec: ViaSpec) -> ViaId {
|
||||
let joint0 = self.joint(spec.endjoints[0]);
|
||||
let joint1 = self.joint(spec.endjoints[1]);
|
||||
|
||||
self.add_via_raw(Via {
|
||||
spec,
|
||||
min_layer: std::cmp::min(joint0.layer, joint1.layer),
|
||||
max_layer: std::cmp::max(joint0.layer, joint1.layer),
|
||||
net: joint0.net,
|
||||
position: (joint0.position + joint1.position) / 2,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_via_raw(&mut self, via: Via) -> ViaId {
|
||||
let bbox = via.bbox();
|
||||
let pin_id = via.spec.pin;
|
||||
let via_id = ViaId::new(self.vias.push(via));
|
||||
|
||||
//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 {
|
||||
self.pins[pin_id.id()].vias.push(via_id);
|
||||
self.pins[pin_id.index()].vias.push(via_id);
|
||||
}
|
||||
|
||||
via_id
|
||||
|
|
@ -158,52 +201,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<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(
|
||||
&self,
|
||||
layer: usize,
|
||||
|
|
@ -213,7 +216,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 |joint_id| {
|
||||
.filter(move |&joint_id| {
|
||||
self.joints
|
||||
.get(&joint_id.index())
|
||||
.unwrap()
|
||||
|
|
@ -230,7 +233,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.
|
||||
|
|
@ -244,7 +247,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 |polygon_id| {
|
||||
.filter(move |&polygon_id| {
|
||||
self.polygons
|
||||
.get(&polygon_id.index())
|
||||
.unwrap()
|
||||
|
|
@ -299,60 +302,6 @@ impl Layout {
|
|||
}
|
||||
|
||||
pub fn pin(&self, pin_id: PinId) -> &Pin {
|
||||
&self.pins[pin_id.id()]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Dissolve)]
|
||||
pub struct LayoutHalfDelta {
|
||||
joints: BTreeMap<usize, Joint>,
|
||||
segments: BTreeMap<usize, Segment>,
|
||||
vias: BTreeMap<usize, Via>,
|
||||
polygons: BTreeMap<usize, Polygon>,
|
||||
}
|
||||
|
||||
impl ApplyDelta<LayoutHalfDelta> for Layout {
|
||||
fn apply_delta(&mut self, delta: &Delta<LayoutHalfDelta>) {
|
||||
let (removed, inserted) = delta.clone().dissolve();
|
||||
|
||||
let joints_delta = Delta::with_removed_inserted(removed.joints, inserted.joints);
|
||||
self.joints.apply_delta(&joints_delta);
|
||||
|
||||
let segments_delta = Delta::with_removed_inserted(removed.segments, inserted.segments);
|
||||
self.segments.apply_delta(&segments_delta);
|
||||
|
||||
let vias_delta = Delta::with_removed_inserted(removed.vias, inserted.vias);
|
||||
self.vias.apply_delta(&vias_delta);
|
||||
|
||||
let polygons_delta = Delta::with_removed_inserted(removed.polygons, inserted.polygons);
|
||||
self.polygons.apply_delta(&polygons_delta);
|
||||
|
||||
// TODO R-trees.
|
||||
}
|
||||
}
|
||||
|
||||
impl FlushDelta<LayoutHalfDelta> for Layout {
|
||||
fn flush_delta(&mut self) -> Delta<LayoutHalfDelta> {
|
||||
let (removed_joints, inserted_joints) = self.joints.flush_delta().dissolve();
|
||||
let (removed_segments, inserted_segments) = self.segments.flush_delta().dissolve();
|
||||
let (removed_vias, inserted_vias) = self.vias.flush_delta().dissolve();
|
||||
let (removed_polygons, inserted_polygons) = self.polygons.flush_delta().dissolve();
|
||||
|
||||
// TODO R-trees.
|
||||
|
||||
Delta::with_removed_inserted(
|
||||
LayoutHalfDelta {
|
||||
joints: removed_joints,
|
||||
segments: removed_segments,
|
||||
vias: removed_vias,
|
||||
polygons: removed_polygons,
|
||||
},
|
||||
LayoutHalfDelta {
|
||||
joints: inserted_joints,
|
||||
segments: inserted_segments,
|
||||
vias: inserted_vias,
|
||||
polygons: inserted_polygons,
|
||||
},
|
||||
)
|
||||
&self.pins[pin_id.index()]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,35 +53,55 @@ impl<T: Copy> From<Vector2<T>> for [T; 2] {
|
|||
macro_rules! impl_inside_polygon {
|
||||
($type:ty) => {
|
||||
impl Vector2<$type> {
|
||||
// Checks if the point (px, py) is inside a polygon using the ray-casting
|
||||
// algorithm. Division is not used to avoid integer truncation errors.
|
||||
// Checks if the point is inside a polygon by casting a ray to the
|
||||
// right. Division is not used to avoid integer truncation errors.
|
||||
pub fn inside_polygon(&self, polygon: &[Vector2<$type>]) -> bool {
|
||||
let mut inside = false;
|
||||
let n = polygon.len();
|
||||
let px = self.x;
|
||||
let py = self.y;
|
||||
|
||||
// `self` is `v0`.
|
||||
|
||||
// `v1` is the previous vertex.
|
||||
let mut v1 = &polygon[n - 1];
|
||||
|
||||
// `v2` is the current vertex.
|
||||
for v2 in polygon.iter() {
|
||||
let dy = v2.y - v1.y;
|
||||
let zero = 0 as $type;
|
||||
let dx12 = v2.x - v1.x;
|
||||
let dy12 = v2.y - v1.y;
|
||||
|
||||
if dy != zero && (py > v1.y) != (py > v2.y) {
|
||||
let dx = v2.x - v1.x;
|
||||
let t = py - v1.y;
|
||||
let s = px - v1.x;
|
||||
// First, check if the line of the horizontal rightward ray
|
||||
// cast to actually crosses the vertical span of the current
|
||||
// `(v1, v2)` edge.
|
||||
if dy12 != (0 as $type) && (self.y > v1.y) != (self.y > v2.y) {
|
||||
let dx01 = self.x - v1.x;
|
||||
let dy01 = self.y - v1.y;
|
||||
|
||||
let crosses = if dy > zero {
|
||||
s * dy < dx * t
|
||||
// Now check if the (v1, v2) edge is actually on the
|
||||
// right side of the ray and not on the left.
|
||||
//
|
||||
// This just compares the X coordinate of `self` (`v0`)
|
||||
// to the X coordinate of the intersection between the
|
||||
// horizontal rightward ray and the current `(v1, v2)` edge:
|
||||
//
|
||||
// `self.x < v1.x + (self.y - v1.y) * (dx12 / dy12)`
|
||||
//
|
||||
// but is algebraically simplified and rewritten to not
|
||||
// use division.
|
||||
let crosses = if dy12 > (0 as $type) {
|
||||
dx01 * dy12 < dx12 * dy01
|
||||
} else {
|
||||
s * dy > dx * t
|
||||
dx01 * dy12 > dx12 * dy01
|
||||
};
|
||||
|
||||
// Even-odd rule: flip whether the point is inside or
|
||||
// outside upon each detected crossing.
|
||||
if crosses {
|
||||
inside = !inside;
|
||||
}
|
||||
}
|
||||
|
||||
// Make the current vertex previous for the next loop
|
||||
// iteration.
|
||||
v1 = v2;
|
||||
}
|
||||
|
||||
|
|
@ -96,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<i64>; 4] {
|
||||
let dx = x2 - x1;
|
||||
let dy = y2 - y1;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use dearcut::{RecordingTriangulator, VertexId};
|
|||
use derive_getters::Getters;
|
||||
use derive_more::Constructor;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use stable_vec::StableVec;
|
||||
use undoredo::Recorder;
|
||||
|
||||
use crate::{Board, Joint, JointId, Polygon, PolygonId, Segment, SegmentId, Vector2};
|
||||
|
|
@ -176,9 +177,9 @@ pub struct NavmesherBoard {
|
|||
navmesher: Navmesher,
|
||||
board: Board,
|
||||
|
||||
joint_multiobstacles: Recorder<Vec<MultiObstacleId>>,
|
||||
segment_multiobstacles: Recorder<Vec<MultiObstacleId>>,
|
||||
polygon_multiobstacles: Recorder<Vec<MultiObstacleId>>,
|
||||
joint_multiobstacles: Recorder<StableVec<MultiObstacleId>>,
|
||||
segment_multiobstacles: Recorder<StableVec<MultiObstacleId>>,
|
||||
polygon_multiobstacles: Recorder<StableVec<MultiObstacleId>>,
|
||||
}
|
||||
|
||||
impl NavmesherBoard {
|
||||
|
|
@ -194,12 +195,12 @@ impl NavmesherBoard {
|
|||
),
|
||||
board,
|
||||
|
||||
joint_multiobstacles: Recorder::new(Vec::new()),
|
||||
segment_multiobstacles: Recorder::new(Vec::new()),
|
||||
polygon_multiobstacles: Recorder::new(Vec::new()),
|
||||
joint_multiobstacles: Recorder::new(StableVec::new()),
|
||||
segment_multiobstacles: Recorder::new(StableVec::new()),
|
||||
polygon_multiobstacles: Recorder::new(StableVec::new()),
|
||||
};
|
||||
|
||||
for (i, joint) in this.board.layout().joints().collection() {
|
||||
for (i, joint) in this.board.layout().joints().container().iter() {
|
||||
this.joint_multiobstacles.insert(
|
||||
i,
|
||||
this.navmesher
|
||||
|
|
@ -207,17 +208,15 @@ impl NavmesherBoard {
|
|||
);
|
||||
}
|
||||
|
||||
for (i, segment) in this.board.layout().segments().collection() {
|
||||
for (i, segment) in this.board.layout().segments().container().iter() {
|
||||
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.bounding_rectangle()),
|
||||
);
|
||||
}
|
||||
|
||||
for (i, polygon) in this.board.layout().polygons().collection() {
|
||||
for (i, polygon) in this.board.layout().polygons().container().iter() {
|
||||
this.polygon_multiobstacles.insert(
|
||||
i,
|
||||
this.navmesher
|
||||
|
|
@ -256,34 +255,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.bounding_rectangle();
|
||||
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<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 {
|
||||
let polygon_id = self.board.add_polygon(polygon.clone());
|
||||
self.polygon_multiobstacles.insert(
|
||||
|
|
|
|||
|
|
@ -1,159 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2026 Topola contributors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use derive_more::Constructor;
|
||||
use rstar::{AABB, Envelope, primitives::Rectangle};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
Vector2,
|
||||
layout::{NetId, PinId},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||
pub enum PrimitiveId {
|
||||
Joint(JointId),
|
||||
Segment(SegmentId),
|
||||
Polygon(PolygonId),
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
|
||||
)]
|
||||
pub struct JointId(usize);
|
||||
|
||||
impl JointId {
|
||||
/// Returns the underlying index.
|
||||
#[inline]
|
||||
pub fn index(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Joint {
|
||||
pub position: Vector2<i64>,
|
||||
pub layer: usize,
|
||||
pub radius: u64,
|
||||
pub net: NetId,
|
||||
pub pin: Option<PinId>,
|
||||
}
|
||||
|
||||
impl Joint {
|
||||
pub fn bbox(&self) -> Rectangle<[i64; 3]> {
|
||||
Rectangle::from_aabb(AABB::from_corners(
|
||||
[
|
||||
self.position.x - self.radius as i64,
|
||||
self.position.y - self.radius as i64,
|
||||
self.layer as i64,
|
||||
],
|
||||
[
|
||||
self.position.x + self.radius as i64,
|
||||
self.position.y + self.radius as i64,
|
||||
self.layer as i64,
|
||||
],
|
||||
))
|
||||
}
|
||||
|
||||
pub fn center(&self) -> Vector2<i64> {
|
||||
self.position
|
||||
}
|
||||
|
||||
pub fn contains_point(&self, point: Vector2<i64>) -> bool {
|
||||
(point.x - self.position.x).pow(2) as u64 + (point.y - self.position.y).pow(2) as u64
|
||||
<= self.radius.pow(2)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
|
||||
)]
|
||||
pub struct SegmentId(usize);
|
||||
|
||||
impl SegmentId {
|
||||
/// Returns the underlying index.
|
||||
#[inline]
|
||||
pub fn index(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Segment {
|
||||
pub endjoints: [JointId; 2],
|
||||
pub layer: usize,
|
||||
pub half_width: u64,
|
||||
pub net: NetId,
|
||||
pub pin: Option<PinId>,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
|
||||
)]
|
||||
pub struct ViaId(usize);
|
||||
|
||||
impl ViaId {
|
||||
/// Returns the underlying index.
|
||||
#[inline]
|
||||
pub fn index(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Via {
|
||||
pub endjoints: [JointId; 2],
|
||||
pub layer: usize, // ??? This should be a range.
|
||||
pub radius: u64,
|
||||
pub net: NetId,
|
||||
pub pin: Option<PinId>,
|
||||
}
|
||||
|
||||
impl Via {
|
||||
/*pub fn bbox(&self) -> Rectangle<[i64; 3]> {
|
||||
//
|
||||
}*/
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
|
||||
)]
|
||||
pub struct PolygonId(usize);
|
||||
|
||||
impl PolygonId {
|
||||
/// Returns the underlying index.
|
||||
#[inline]
|
||||
pub fn index(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Polygon {
|
||||
pub vertices: Vec<Vector2<i64>>,
|
||||
pub layer: usize,
|
||||
pub net: NetId,
|
||||
pub pin: Option<PinId>,
|
||||
}
|
||||
|
||||
impl Polygon {
|
||||
pub fn bbox(&self) -> Rectangle<[i64; 3]> {
|
||||
Rectangle::from_aabb(
|
||||
self.vertices
|
||||
.clone()
|
||||
.into_iter()
|
||||
.fold(AABB::new_empty(), |aabb, vertex| {
|
||||
aabb.merged(&AABB::from_point([vertex.x, vertex.y, self.layer as i64]))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn center(&self) -> Vector2<i64> {
|
||||
Vector2::<i64>::polygon_centroid(&self.vertices)
|
||||
}
|
||||
|
||||
pub fn contains_point(&self, point: Vector2<i64>) -> bool {
|
||||
point.inside_polygon(&self.vertices)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
// SPDX-FileCopyrightText: 2026 Topola contributors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use derive_more::Constructor;
|
||||
use rstar::{AABB, primitives::Rectangle};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::layout::{NetId, PinId};
|
||||
use crate::math::Vector2;
|
||||
|
||||
#[derive(
|
||||
Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
|
||||
)]
|
||||
pub struct JointId(usize);
|
||||
|
||||
impl JointId {
|
||||
/// Returns the underlying index.
|
||||
#[inline]
|
||||
pub fn index(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Joint {
|
||||
pub position: Vector2<i64>,
|
||||
pub layer: usize,
|
||||
pub radius: u64,
|
||||
pub net: NetId,
|
||||
pub pin: Option<PinId>,
|
||||
}
|
||||
|
||||
impl Joint {
|
||||
pub fn bbox(&self) -> Rectangle<[i64; 3]> {
|
||||
Rectangle::from_aabb(AABB::from_corners(
|
||||
[
|
||||
self.position.x - self.radius as i64,
|
||||
self.position.y - self.radius as i64,
|
||||
self.layer as i64,
|
||||
],
|
||||
[
|
||||
self.position.x + self.radius as i64,
|
||||
self.position.y + self.radius as i64,
|
||||
self.layer as i64,
|
||||
],
|
||||
))
|
||||
}
|
||||
|
||||
pub fn center(&self) -> Vector2<i64> {
|
||||
self.position
|
||||
}
|
||||
|
||||
pub fn contains_point(&self, point: Vector2<i64>) -> bool {
|
||||
(point.x - self.position.x).pow(2) as u64 + (point.y - self.position.y).pow(2) as u64
|
||||
<= self.radius.pow(2)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// SPDX-FileCopyrightText: 2026 Topola contributors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
mod joint;
|
||||
mod polygon;
|
||||
mod segment;
|
||||
mod via;
|
||||
|
||||
pub use joint::{Joint, JointId};
|
||||
pub use polygon::{Polygon, PolygonId};
|
||||
pub use segment::{Segment, SegmentId, SegmentSpec};
|
||||
pub use via::{Via, ViaId, ViaSpec};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||
pub enum PrimitiveId {
|
||||
Joint(JointId),
|
||||
Segment(SegmentId),
|
||||
Polygon(PolygonId),
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// SPDX-FileCopyrightText: 2026 Topola contributors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use derive_more::Constructor;
|
||||
use rstar::{AABB, Envelope, primitives::Rectangle};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::layout::{NetId, PinId};
|
||||
use crate::math::Vector2;
|
||||
|
||||
#[derive(
|
||||
Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
|
||||
)]
|
||||
pub struct PolygonId(usize);
|
||||
|
||||
impl PolygonId {
|
||||
/// Returns the underlying index.
|
||||
#[inline]
|
||||
pub fn index(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Polygon {
|
||||
pub vertices: Vec<Vector2<i64>>,
|
||||
pub layer: usize,
|
||||
pub net: NetId,
|
||||
pub pin: Option<PinId>,
|
||||
}
|
||||
|
||||
impl Polygon {
|
||||
pub fn bbox(&self) -> Rectangle<[i64; 3]> {
|
||||
Rectangle::from_aabb(
|
||||
self.vertices
|
||||
.clone()
|
||||
.into_iter()
|
||||
.fold(AABB::new_empty(), |aabb, vertex| {
|
||||
aabb.merged(&AABB::from_point([vertex.x, vertex.y, self.layer as i64]))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn center(&self) -> Vector2<i64> {
|
||||
Vector2::<i64>::polygon_centroid(&self.vertices)
|
||||
}
|
||||
|
||||
pub fn contains_point(&self, point: Vector2<i64>) -> bool {
|
||||
point.inside_polygon(&self.vertices)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
// SPDX-FileCopyrightText: 2026 Topola contributors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use derive_more::Constructor;
|
||||
use rstar::primitives::Rectangle;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::layout::{NetId, PinId};
|
||||
use crate::math::Vector2;
|
||||
use crate::primitives::JointId;
|
||||
|
||||
#[derive(
|
||||
Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
|
||||
)]
|
||||
pub struct SegmentId(usize);
|
||||
|
||||
impl SegmentId {
|
||||
/// Returns the underlying index.
|
||||
#[inline]
|
||||
pub fn index(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct SegmentSpec {
|
||||
pub endjoints: [JointId; 2],
|
||||
pub half_width: u64,
|
||||
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)
|
||||
}
|
||||
|
||||
/// NOTE: This is not the bounding box. The output rectangle is in general
|
||||
/// not axis-aligned.
|
||||
pub fn bounding_rectangle(&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 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])
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// SPDX-FileCopyrightText: 2026 Topola contributors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use derive_more::Constructor;
|
||||
use rstar::{AABB, primitives::Rectangle};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::layout::{NetId, PinId};
|
||||
use crate::math::Vector2;
|
||||
|
||||
use super::joint::JointId;
|
||||
|
||||
#[derive(
|
||||
Clone, Constructor, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize,
|
||||
)]
|
||||
pub struct ViaId(usize);
|
||||
|
||||
impl ViaId {
|
||||
/// Returns the underlying index.
|
||||
#[inline]
|
||||
pub fn index(self) -> usize {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ViaSpec {
|
||||
pub endjoints: [JointId; 2],
|
||||
pub radius: u64,
|
||||
pub pin: Option<PinId>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Via {
|
||||
pub spec: ViaSpec,
|
||||
pub position: Vector2<i64>,
|
||||
pub min_layer: usize,
|
||||
pub max_layer: usize,
|
||||
pub net: NetId,
|
||||
}
|
||||
|
||||
impl Via {
|
||||
pub fn bbox(&self) -> Rectangle<[i64; 3]> {
|
||||
let radius = self.spec.radius as i64;
|
||||
|
||||
Rectangle::from_aabb(AABB::from_corners(
|
||||
[
|
||||
self.position.x - radius,
|
||||
self.position.y - radius,
|
||||
self.min_layer as i64,
|
||||
],
|
||||
[
|
||||
self.position.x + radius,
|
||||
self.position.y + radius,
|
||||
self.max_layer as i64,
|
||||
],
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -46,7 +46,7 @@ impl Ratsnest {
|
|||
let mut triangulations: BTreeMap<(NetId, usize), DelaunayTriangulation<DelaunayVertex>> =
|
||||
BTreeMap::new();
|
||||
|
||||
for (i, joint) in board.layout().joints().collection() {
|
||||
for (i, joint) in board.layout().joints().container().iter() {
|
||||
let _ = triangulations
|
||||
.entry((joint.net, joint.layer))
|
||||
.or_insert_with(DelaunayTriangulation::new)
|
||||
|
|
@ -58,8 +58,8 @@ impl Ratsnest {
|
|||
});
|
||||
}
|
||||
|
||||
for (i, segment) in board.layout().segments().collection() {
|
||||
let segment_center = board.layout().segment_center(SegmentId::new(i));
|
||||
for (i, segment) in board.layout().segments().container().iter() {
|
||||
let segment_center = segment.center();
|
||||
let _ = triangulations
|
||||
.entry((segment.net, segment.layer))
|
||||
.or_insert_with(DelaunayTriangulation::new)
|
||||
|
|
@ -71,7 +71,7 @@ impl Ratsnest {
|
|||
});
|
||||
}
|
||||
|
||||
for (i, polygon) in board.layout().polygons().collection() {
|
||||
for (i, polygon) in board.layout().polygons().container().iter() {
|
||||
let _ = triangulations
|
||||
.entry((polygon.net, polygon.layer))
|
||||
.or_insert_with(DelaunayTriangulation::new)
|
||||
|
|
|
|||
|
|
@ -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<PinId>,
|
||||
|
|
|
|||
Loading…
Reference in New Issue