mirror of https://codeberg.org/topola/topola.git
feat(router/prenavmesh): Have fillets as prenavnodes instead of long vertex chains
This commit is contained in:
parent
521bb0598a
commit
3e466960fa
|
|
@ -158,10 +158,11 @@ impl<M: AccessMesadata> Board<M> {
|
|||
weight: PolyWeight,
|
||||
maybe_pin: Option<String>,
|
||||
nodes: &[PrimitiveIndex],
|
||||
fillets: &[FixedDotIndex],
|
||||
) -> GenericIndex<PolyWeight> {
|
||||
let (poly, apex) =
|
||||
self.layout
|
||||
.add_poly_with_nodes(&mut recorder.layout_edit, weight, nodes);
|
||||
.add_poly_with_nodes(&mut recorder.layout_edit, weight, nodes, fillets);
|
||||
|
||||
if let Some(pin) = maybe_pin {
|
||||
for i in nodes {
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ impl<CW, Cel, R> GetOuterGears for FixedDot<'_, CW, Cel, R> {
|
|||
impl<CW: Clone, Cel: Copy, R: AccessRules> GetPrevNextInChain for FixedDot<'_, CW, Cel, R> {
|
||||
fn next_in_chain(&self, maybe_prev: Option<GearIndex>) -> Option<GearIndex> {
|
||||
self.drawing
|
||||
.clearance_intersectors(self.index.into())
|
||||
.overlapees(self.index.into())
|
||||
.find_map(|infringement| {
|
||||
let PrimitiveIndex::FixedDot(intersectee) = infringement.1 else {
|
||||
return None;
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ impl<CW: Clone, Cel: Copy, R: AccessRules> Drawing<CW, Cel, R> {
|
|||
infringer: PrimitiveIndex,
|
||||
it: impl Iterator<Item = PrimitiveIndex> + 'a,
|
||||
) -> impl Iterator<Item = Infringement> + 'a {
|
||||
self.clearance_intersectors_among(infringer, it)
|
||||
self.overlapees_among(infringer, it)
|
||||
.filter(move |infringement| {
|
||||
// Infringement with loose dots resulted in false positives for
|
||||
// line-of-sight paths.
|
||||
|
|
@ -161,24 +161,25 @@ impl<CW: Clone, Cel: Copy, R: AccessRules> Drawing<CW, Cel, R> {
|
|||
.filter(move |infringement| !self.are_connectable(infringer, infringement.1))
|
||||
}
|
||||
|
||||
pub fn clearance_intersectors<'a>(
|
||||
pub fn overlapees<'a>(
|
||||
&'a self,
|
||||
intersector: PrimitiveIndex,
|
||||
overlapper: PrimitiveIndex,
|
||||
) -> impl Iterator<Item = Infringement> + 'a {
|
||||
self.clearance_intersectors_among(
|
||||
intersector,
|
||||
self.locate_possible_infringees(intersector)
|
||||
self.overlapees_among(
|
||||
overlapper,
|
||||
self.locate_possible_infringees(overlapper)
|
||||
.filter_map(move |infringee_node| {
|
||||
if let GenericNode::Primitive(primitive_node) = infringee_node {
|
||||
Some(primitive_node)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
})
|
||||
.filter(move |&overlapee| overlapper != overlapee),
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn clearance_intersectors_among<'a>(
|
||||
pub(super) fn overlapees_among<'a>(
|
||||
&'a self,
|
||||
intersector: PrimitiveIndex,
|
||||
it: impl Iterator<Item = PrimitiveIndex> + 'a,
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ pub enum CompoundWeight {
|
|||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum CompoundEntryLabel {
|
||||
Normal,
|
||||
NotInConvexHull,
|
||||
Apex,
|
||||
Fillet,
|
||||
}
|
||||
|
||||
/// The alias to differ node types
|
||||
|
|
@ -254,13 +255,14 @@ impl<R: AccessRules> Layout<R> {
|
|||
recorder: &mut LayoutEdit,
|
||||
weight: PolyWeight,
|
||||
nodes: &[PrimitiveIndex],
|
||||
fillets: &[FixedDotIndex],
|
||||
) -> (GenericIndex<PolyWeight>, FixedDotIndex) {
|
||||
let layer = weight.layer();
|
||||
let maybe_net = weight.maybe_net();
|
||||
let poly = self.add_poly(recorder, weight);
|
||||
(
|
||||
poly,
|
||||
add_poly_with_nodes_intern(self, recorder, poly, nodes, layer, maybe_net),
|
||||
add_poly_with_nodes_intern(self, recorder, poly, nodes, fillets, layer, maybe_net),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -361,6 +363,7 @@ impl<R: AccessRules> Layout<R> {
|
|||
if self
|
||||
.drawing()
|
||||
.geometry()
|
||||
// TODO: Add `.compounds()` method working on `PrimitiveIndex`.
|
||||
.compounds(GenericIndex::<()>::new(primitive.petgraph_index()))
|
||||
.next()
|
||||
.is_some()
|
||||
|
|
@ -380,7 +383,7 @@ impl<R: AccessRules> Layout<R> {
|
|||
let apex = loop {
|
||||
// this returns None if the via is not present on this layer
|
||||
let (entry_label, dot) = dots.next()?;
|
||||
if entry_label == CompoundEntryLabel::NotInConvexHull {
|
||||
if entry_label == CompoundEntryLabel::Apex {
|
||||
if let Some((dot, weight)) = handle_fixed_dot(&self.drawing, dot) {
|
||||
if weight.layer() == active_layer {
|
||||
break dot;
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ pub(super) fn add_poly_with_nodes_intern<R: AccessRules>(
|
|||
recorder: &mut LayoutEdit,
|
||||
poly: GenericIndex<PolyWeight>,
|
||||
nodes: &[PrimitiveIndex],
|
||||
fillets: &[FixedDotIndex],
|
||||
layer: usize,
|
||||
maybe_net: Option<usize>,
|
||||
) -> FixedDotIndex {
|
||||
|
|
@ -123,19 +124,22 @@ pub(super) fn add_poly_with_nodes_intern<R: AccessRules>(
|
|||
layout.drawing.add_to_compound(
|
||||
recorder,
|
||||
GenericIndex::<()>::new(idx.petgraph_index()),
|
||||
CompoundEntryLabel::NotInConvexHull,
|
||||
CompoundEntryLabel::Apex,
|
||||
poly_compound,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for fillet in fillets {
|
||||
layout
|
||||
.drawing
|
||||
.add_to_compound(recorder, *fillet, CompoundEntryLabel::Fillet, poly_compound)
|
||||
}
|
||||
|
||||
// maybe this should be a different edge label
|
||||
layout.drawing.add_to_compound(
|
||||
recorder,
|
||||
apex,
|
||||
CompoundEntryLabel::NotInConvexHull,
|
||||
poly_compound,
|
||||
);
|
||||
layout
|
||||
.drawing
|
||||
.add_to_compound(recorder, apex, CompoundEntryLabel::Apex, poly_compound);
|
||||
|
||||
assert!(is_apex(&layout.drawing, apex));
|
||||
apex
|
||||
|
|
@ -166,7 +170,7 @@ impl<'a, R> PolyRef<'a, R> {
|
|||
.geometry()
|
||||
.compound_members(self.index.into())
|
||||
.find_map(|(label, primitive_node)| {
|
||||
if label == CompoundEntryLabel::NotInConvexHull {
|
||||
if label == CompoundEntryLabel::Apex {
|
||||
if let PrimitiveIndex::FixedDot(dot) = primitive_node {
|
||||
if is_apex(self.drawing, dot) {
|
||||
return Some(dot);
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ impl Navmesh {
|
|||
|
||||
// The existence of a constraint edge does not (!) guarantee that this
|
||||
// edge exactly will be present in the triangulation. It appears that
|
||||
// Spade splits a constraint edge into two if an endpoint of another
|
||||
// Spade splits a constraint edge in two if an endpoint of another
|
||||
// constraint lies on it.
|
||||
//
|
||||
// So now we go over all the constraints and make sure that
|
||||
|
|
@ -324,7 +324,7 @@ impl Navmesh {
|
|||
overlapping_prenavnodes_unions: &mut UnionFind<NodeIndex<usize>>,
|
||||
prenavnode: PrenavmeshNodeIndex,
|
||||
) {
|
||||
for overlap in layout.drawing().clearance_intersectors(prenavnode.into()) {
|
||||
for overlap in layout.drawing().overlapees(prenavnode.into()) {
|
||||
let PrimitiveIndex::FixedDot(overlapee) = overlap.1 else {
|
||||
continue;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ use crate::{
|
|||
Drawing,
|
||||
},
|
||||
geometry::{shape::AccessShape, GetLayer},
|
||||
graph::GetPetgraphIndex,
|
||||
layout::Layout,
|
||||
graph::{GenericIndex, GetPetgraphIndex},
|
||||
layout::{CompoundEntryLabel, Layout},
|
||||
triangulation::{GetTrianvertexNodeIndex, Triangulation},
|
||||
};
|
||||
|
||||
|
|
@ -153,13 +153,33 @@ impl Prenavmesh {
|
|||
for node in layout.drawing().layer_primitive_nodes(layer) {
|
||||
let primitive = node.primitive(layout.drawing());
|
||||
|
||||
if let Some(primitive_net) = primitive.maybe_net() {
|
||||
let Some(primitive_net) = primitive.maybe_net() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if node == origin.into()
|
||||
|| node == destination.into()
|
||||
|| Some(primitive_net) != maybe_net
|
||||
{
|
||||
match node {
|
||||
PrimitiveIndex::FixedDot(dot) => {
|
||||
layout
|
||||
.drawing()
|
||||
// TODO: Add `.compounds()` method working on `PrimitiveIndex`.
|
||||
.compounds(GenericIndex::<()>::new(dot.petgraph_index()))
|
||||
.find(|(label, _)| *label == CompoundEntryLabel::Fillet)
|
||||
.is_some();
|
||||
|
||||
// Do not add prenavnodes for primitives that have been filleted.
|
||||
// For now, we do this by detecting if the primitive overlaps
|
||||
// a fillet.
|
||||
// TODO: This method is simplistic and will obviously result in
|
||||
// false positives in some cases, so in the future, instead of this,
|
||||
// create a fillet compound type and check for compound membership.
|
||||
if Self::is_fixed_dot_filleted(layout, dot) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.triangulation
|
||||
.add_vertex(PrenavmeshWeight::new_from_fixed_dot(layout, dot))?;
|
||||
}
|
||||
|
|
@ -181,12 +201,14 @@ impl Prenavmesh {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for node in layout.drawing().layer_primitive_nodes(layer) {
|
||||
let primitive = node.primitive(layout.drawing());
|
||||
|
||||
if let Some(primitive_net) = primitive.maybe_net() {
|
||||
let Some(primitive_net) = primitive.maybe_net() else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if node == origin.into()
|
||||
|| node == destination.into()
|
||||
|| Some(primitive_net) != maybe_net
|
||||
|
|
@ -205,6 +227,14 @@ impl Prenavmesh {
|
|||
// fixed segs that do not cause an intersection.
|
||||
match node {
|
||||
PrimitiveIndex::FixedSeg(seg) => {
|
||||
let (from_dot, to_dot) = layout.drawing().primitive(seg).joints();
|
||||
|
||||
if Self::is_fixed_dot_filleted(layout, from_dot)
|
||||
&& Self::is_fixed_dot_filleted(layout, to_dot)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let constraint = PrenavmeshConstraint::new_from_fixed_seg(layout, seg);
|
||||
|
||||
if !this
|
||||
|
|
@ -218,11 +248,35 @@ impl Prenavmesh {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
fn is_fixed_dot_filleted(layout: &Layout<impl AccessRules>, dot: FixedDotIndex) -> bool {
|
||||
layout
|
||||
.drawing()
|
||||
.compounds(GenericIndex::<()>::new(dot.petgraph_index()))
|
||||
.find(|(label, _)|
|
||||
// Fillets fail this test for some reason that I did not investigate, so
|
||||
// I added this condition.
|
||||
*label == CompoundEntryLabel::Fillet
|
||||
// Exclude apices because they may overlap fillets.
|
||||
|| *label == CompoundEntryLabel::Apex)
|
||||
.is_none()
|
||||
&& layout
|
||||
.drawing()
|
||||
.overlapees(dot.into())
|
||||
.find(|overlapee| {
|
||||
layout
|
||||
.drawing()
|
||||
// TODO: Add `.compounds()` method working on `PrimitiveIndex`.
|
||||
.compounds(GenericIndex::<()>::new(overlapee.1.petgraph_index()))
|
||||
.find(|(label, _)| *label == CompoundEntryLabel::Fillet)
|
||||
.is_some()
|
||||
})
|
||||
.is_some()
|
||||
}
|
||||
|
||||
fn add_constraint(&mut self, constraint: PrenavmeshConstraint) -> Result<(), InsertionError> {
|
||||
self.triangulation
|
||||
.add_constraint_edge(constraint.0, constraint.1)?;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use specctra_core::math::PointWithRotation;
|
|||
use crate::{
|
||||
board::{edit::BoardEdit, AccessMesadata, Board},
|
||||
drawing::{
|
||||
dot::{FixedDotWeight, GeneralDotWeight},
|
||||
dot::{FixedDotIndex, FixedDotWeight, GeneralDotWeight},
|
||||
graph::{GetMaybeNet, MakePrimitive},
|
||||
primitive::MakePrimitiveShape,
|
||||
seg::{FixedSegWeight, GeneralSegWeight},
|
||||
|
|
@ -584,6 +584,7 @@ impl SpecctraDesign {
|
|||
seg3.into(),
|
||||
seg4.into(),
|
||||
],
|
||||
&[],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -728,15 +729,16 @@ impl SpecctraDesign {
|
|||
.into(),
|
||||
);
|
||||
|
||||
let fillets = Self::add_polygon_fillet_circles(
|
||||
recorder, board, place, pin, coords, width, layer, maybe_net, None, flip,
|
||||
);
|
||||
|
||||
board.add_poly_with_nodes(
|
||||
recorder,
|
||||
SolidPolyWeight { layer, maybe_net }.into(),
|
||||
maybe_pin,
|
||||
&nodes[..],
|
||||
);
|
||||
|
||||
Self::add_polygon_fillet_circles(
|
||||
recorder, board, place, pin, coords, width, layer, maybe_net, None, flip,
|
||||
&fillets[..],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -751,9 +753,10 @@ impl SpecctraDesign {
|
|||
maybe_net: Option<usize>,
|
||||
_maybe_pin: Option<String>,
|
||||
flip: bool,
|
||||
) {
|
||||
) -> Vec<FixedDotIndex> {
|
||||
let MIN_FIRST_CHAIN_ELEMENT_LENGTH = 100.0;
|
||||
let mut maybe_first_chain_segment = None;
|
||||
let mut fillets = vec![];
|
||||
|
||||
let first_pos = Self::pos(place, pin, coords[0].x, coords[0].y, flip);
|
||||
let last_pos = Self::pos(
|
||||
|
|
@ -790,22 +793,23 @@ impl SpecctraDesign {
|
|||
if index - first_chain_index >= 3 {
|
||||
let circle = math::fillet_circle(&first_chain_segment, &curr_segment12);
|
||||
|
||||
board.add_fixed_dot_infringably(
|
||||
fillets.push(board.add_fixed_dot_infringably(
|
||||
recorder,
|
||||
FixedDotWeight(GeneralDotWeight {
|
||||
circle,
|
||||
layer,
|
||||
maybe_net: None, // TODO.
|
||||
//maybe_net,
|
||||
maybe_net,
|
||||
}),
|
||||
None,
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
maybe_first_chain_segment = Some((Line::new(curr_pos1, curr_pos2), index));
|
||||
}
|
||||
}
|
||||
|
||||
fillets
|
||||
}
|
||||
|
||||
fn pos(place: PointWithRotation, pin: PointWithRotation, x: f64, y: f64, flip: bool) -> Point {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ fn test_tht_de9_to_tht_de9() {
|
|||
#[test]
|
||||
fn test_0603_breakout() {
|
||||
let mut autorouter = common::load_design("tests/single_layer/0603_breakout/0603_breakout.dsn");
|
||||
common::assert_navnode_count(&mut autorouter, "R1-2", "J1-2", 54);
|
||||
common::assert_navnode_count(&mut autorouter, "R1-2", "J1-2", 22);
|
||||
let mut invoker = common::create_invoker_and_assert(autorouter);
|
||||
common::replay_and_assert(
|
||||
&mut invoker,
|
||||
|
|
@ -93,7 +93,7 @@ fn test_4x_3rd_order_smd_lc_filters() {
|
|||
let mut autorouter = common::load_design(
|
||||
"tests/single_layer/4x_3rd_order_smd_lc_filters/4x_3rd_order_smd_lc_filters.dsn",
|
||||
);
|
||||
common::assert_navnode_count(&mut autorouter, "J1-1", "L1-1", 2062);
|
||||
common::assert_navnode_count(&mut autorouter, "J1-1", "L1-1", 558);
|
||||
let mut invoker = common::create_invoker_and_assert(autorouter);
|
||||
common::replay_and_assert(
|
||||
&mut invoker,
|
||||
|
|
@ -130,7 +130,7 @@ fn test_tht_3pin_xlr_to_tht_3pin_xlr() {
|
|||
fn test_vga_dac_breakout() {
|
||||
let mut autorouter =
|
||||
common::load_design("tests/single_layer/vga_dac_breakout/vga_dac_breakout.dsn");
|
||||
common::assert_navnode_count(&mut autorouter, "J1-2", "R4-1", 944);
|
||||
common::assert_navnode_count(&mut autorouter, "J1-2", "R4-1", 272);
|
||||
let mut invoker = common::create_invoker_and_assert(autorouter);
|
||||
common::replay_and_assert(
|
||||
&mut invoker,
|
||||
|
|
|
|||
Loading…
Reference in New Issue