mirror of https://codeberg.org/topola/topola.git
[WIP]
This commit is contained in:
parent
9c007a8ccb
commit
7b16328a6c
|
|
@ -54,7 +54,7 @@ impl From<RatvertexIndex> for crate::layout::NodeIndex {
|
|||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RatvertexWeight {
|
||||
vertex: RatvertexIndex,
|
||||
pub(crate) vertex: RatvertexIndex,
|
||||
pub pos: Point,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use petgraph::stable_graph::NodeIndex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{drawing::Drawing, graph::GetPetgraphIndex};
|
||||
|
||||
|
|
@ -89,7 +90,7 @@ macro_rules! impl_loose_weight {
|
|||
// TODO: This enum shouldn't exist: we shouldn't be carrying the tag around like this. Instead we
|
||||
// should be getting it from the graph when it's needed.
|
||||
#[enum_dispatch(GetPetgraphIndex, MakePrimitive)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
pub enum PrimitiveIndex {
|
||||
FixedDot(FixedDotIndex),
|
||||
LooseDot(LooseDotIndex),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
// SPDX-FileCopyrightText: 2024 Topola contributors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
//! A work-in-progress brute-force routing implementation
|
||||
//!
|
||||
//! This should always (except in pathological cases like a non-planar ratsnest)
|
||||
//! be able to find an optimal solution, but potentially require extremely long
|
||||
//! run/walltime to do so. It is intended as a benchmark for heuristic approximations
|
||||
//! of such optimal routing (to compare to potentially suboptimal routing).
|
||||
|
||||
pub mod topo_navmesh;
|
||||
|
|
@ -0,0 +1,361 @@
|
|||
// SPDX-FileCopyrightText: 2024 Topola contributors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
//! A topological navmesh implmentation
|
||||
// idea: see issue #132
|
||||
|
||||
use bimap::BiMap;
|
||||
use geo::Point;
|
||||
use petgraph::graph::{NodeIndex, UnGraph};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use spade::HasPosition;
|
||||
|
||||
use crate::{autorouter::ratsnest::Ratsnest, layout::NodeIndex as LayoutNodeIndex};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub struct NodeWeight {
|
||||
/// Position of a node. Warning: multiple nodes might have the same position
|
||||
pub pos: Point,
|
||||
|
||||
/// can we pass through this point when constructing a route?
|
||||
///
|
||||
/// e.g. primal nodes are not passable, nodes used by etched paths are not passable,
|
||||
/// the remaining dual nodes are passable.
|
||||
pub is_passable: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct EdgeWeight<P> {
|
||||
/// indicates if this is part of an etched path (`P` is the type of the path index)
|
||||
pub path: Option<P>,
|
||||
}
|
||||
|
||||
/// A topological navmesh,
|
||||
/// built upon the merging of the primary and dual graph,
|
||||
/// and with an operation to create barriers.
|
||||
///
|
||||
/// This is basically a planar graph embedding, and is used for enumeration of such embeddings.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Navmesh<P> {
|
||||
// a merging of the primary and dual graph
|
||||
graph: UnGraph<NodeWeight, EdgeWeight<P>>,
|
||||
|
||||
// association between primary graph and this one
|
||||
primals: BiMap<LayoutNodeIndex, NodeIndex>,
|
||||
}
|
||||
|
||||
/// Trianvertices are the vertices of the triangulation before it is converted
|
||||
/// to the navmesh.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
struct Trianvertex<T: geo::CoordNum> {
|
||||
pub idx: LayoutNodeIndex,
|
||||
pub pos: Point<T>,
|
||||
}
|
||||
|
||||
impl<T: geo::CoordNum + spade::SpadeNum> HasPosition for Trianvertex<T> {
|
||||
type Scalar = T;
|
||||
|
||||
#[inline]
|
||||
fn position(&self) -> spade::Point2<T> {
|
||||
let pos = self.pos.0.clone();
|
||||
spade::Point2 { x: pos.x, y: pos.y }
|
||||
}
|
||||
}
|
||||
|
||||
fn voronoi_vertex_get_weight<V, DE, UE, F>(
|
||||
vertex: &spade::handles::VoronoiVertex<'_, V, DE, UE, F>,
|
||||
) -> NodeWeight
|
||||
where
|
||||
V: HasPosition<Scalar = f64>,
|
||||
{
|
||||
fn outer_halfspace_get_weight<V: HasPosition<Scalar = f64>, DE, UE, F>(
|
||||
face: &spade::handles::DirectedVoronoiEdge<'_, V, DE, UE, F>,
|
||||
) -> NodeWeight {
|
||||
let delauney = face.as_delaunay_edge();
|
||||
let from = delauney.from().position();
|
||||
let from = geo::point! { x: from.x, y: from.y };
|
||||
let to = delauney.to().position();
|
||||
let to = geo::point! { x: to.x, y: to.y };
|
||||
let diff = to - from;
|
||||
let orth = geo::point! {
|
||||
x: -diff.y(),
|
||||
y: diff.x()
|
||||
};
|
||||
|
||||
NodeWeight {
|
||||
pos: (from + to + orth) / 2.0,
|
||||
is_passable: true,
|
||||
}
|
||||
}
|
||||
|
||||
use spade::handles::VoronoiVertex as VV;
|
||||
match vertex {
|
||||
VV::Inner(face) => {
|
||||
let cc = face.circumcenter();
|
||||
NodeWeight {
|
||||
pos: geo::point! { x: cc.x, y: cc.y },
|
||||
is_passable: true,
|
||||
}
|
||||
}
|
||||
VV::Outer(out) => outer_halfspace_get_weight(out),
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Navmesh<P> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
graph: UnGraph::new_undirected(),
|
||||
primals: BiMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a navmesh based upon a ratsnest
|
||||
pub fn from_ratsnest(ratsnest: &Ratsnest) -> Result<Self, spade::InsertionError> {
|
||||
use spade::Triangulation;
|
||||
let mut triangulation = spade::DelaunayTriangulation::<Trianvertex<f64>, ()>::new();
|
||||
|
||||
let rng = ratsnest.graph();
|
||||
for node in rng.node_indices() {
|
||||
let weight = &rng[node];
|
||||
triangulation.insert(Trianvertex {
|
||||
idx: LayoutNodeIndex::from(weight.vertex),
|
||||
pos: weight.pos,
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(Self::from_triangulation(&triangulation))
|
||||
}
|
||||
|
||||
/// Create a navmesh based upon a triangulation
|
||||
pub fn from_triangulation<T>(triangulation: &T) -> Self
|
||||
where
|
||||
T: spade::Triangulation<Vertex = Trianvertex<f64>>,
|
||||
{
|
||||
let mut this = UnGraph::new_undirected();
|
||||
// `primals` maps between layout node indices and indices in this graph
|
||||
let mut primals = BiMap::with_capacity(triangulation.num_vertices());
|
||||
// `voronoi_verts` maps between dual node indices
|
||||
// (computed from face indices) and our navmesh graph indices
|
||||
let mut voronoi_verts = std::collections::HashMap::<usize, NodeIndex>::with_capacity(
|
||||
triangulation.num_all_faces(),
|
||||
);
|
||||
|
||||
let mut handle_voronoi_vertex = |this: &mut UnGraph<NodeWeight, EdgeWeight<P>>,
|
||||
vertex: &spade::handles::VoronoiVertex<
|
||||
'_,
|
||||
Trianvertex<f64>,
|
||||
T::DirectedEdge,
|
||||
T::UndirectedEdge,
|
||||
T::Face,
|
||||
>| {
|
||||
use spade::handles::VoronoiVertex as VV;
|
||||
let index = match vertex {
|
||||
VV::Inner(face) => face.index() << 1,
|
||||
VV::Outer(face) => (face.index() << 1) + 1,
|
||||
};
|
||||
*voronoi_verts
|
||||
.entry(index)
|
||||
.or_insert_with(|| this.add_node(voronoi_vertex_get_weight(vertex)))
|
||||
};
|
||||
|
||||
for node in triangulation.vertices() {
|
||||
let graph_idx = this.add_node(NodeWeight {
|
||||
pos: node.data().pos,
|
||||
is_passable: false,
|
||||
});
|
||||
|
||||
if primals
|
||||
.insert_no_overwrite(node.data().idx, graph_idx)
|
||||
.is_err()
|
||||
{
|
||||
panic!("duplicate layout node index");
|
||||
}
|
||||
|
||||
for edge in node.as_voronoi_face().adjacent_edges() {
|
||||
// to convert dual edges around a node into dual nodes around a node,
|
||||
// we use the dual nodes that the edges point to.
|
||||
let graph_idx2 = handle_voronoi_vertex(&mut this, &edge.to());
|
||||
this.update_edge(graph_idx, graph_idx2, EdgeWeight { path: None });
|
||||
}
|
||||
}
|
||||
|
||||
for edge in triangulation.undirected_voronoi_edges() {
|
||||
let [a, b] = edge.vertices();
|
||||
let gridx_a = handle_voronoi_vertex(&mut this, &a);
|
||||
let gridx_b = handle_voronoi_vertex(&mut this, &b);
|
||||
this.update_edge(gridx_a, gridx_b, EdgeWeight { path: None });
|
||||
}
|
||||
|
||||
Self {
|
||||
graph: this,
|
||||
primals,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn graph(&self) -> &UnGraph<NodeWeight, EdgeWeight<P>> {
|
||||
&self.graph
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn primals(&self) -> &BiMap<LayoutNodeIndex, NodeIndex> {
|
||||
&self.primals
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn neighbors(&self, idx: NodeIndex) -> petgraph::graph::Neighbors<'_, EdgeWeight<P>, u32> {
|
||||
self.graph.neighbors(idx)
|
||||
}
|
||||
|
||||
/// Advance "etching" by a single step.
|
||||
///
|
||||
/// * `prev` is the previous node in the path (if there is any), and possibly
|
||||
/// the companions from the previous step (edges to which should form a grid)
|
||||
/// * `current` is the currently selected node
|
||||
/// * `dest` is the next node in the path (edges to there should be duplicated)
|
||||
fn etch_path_make_companions(
|
||||
&mut self,
|
||||
prev: Option<(NodeIndex, Option<(NodeIndex, NodeIndex)>)>,
|
||||
current: NodeIndex,
|
||||
next: Option<NodeIndex>,
|
||||
) -> Option<(NodeIndex, NodeIndex)> {
|
||||
let node_cur = self.graph.node_weight(current)?.clone();
|
||||
if prev.is_none() && next.is_none() || node_cur.is_passable != true {
|
||||
return None;
|
||||
}
|
||||
use std::collections::BTreeSet;
|
||||
let cur_pos = node_cur.pos;
|
||||
let mut link_angles: Vec<_> = self
|
||||
.graph
|
||||
.neighbors(current)
|
||||
.map(|i| {
|
||||
(
|
||||
i,
|
||||
self.graph
|
||||
.node_weight(i)
|
||||
.expect("encountered invalid edge endpoint")
|
||||
.pos
|
||||
- cur_pos,
|
||||
)
|
||||
})
|
||||
.map(|(i, oth_pos_rel)| (i, oth_pos_rel.y().atan2(oth_pos_rel.x())))
|
||||
.collect();
|
||||
link_angles.sort_unstable_by(|a, b| f64::total_cmp(&a.1, &b.1));
|
||||
|
||||
// positions in the list of angles
|
||||
// make `prev` the first item in the link_angles
|
||||
if let Some(xpos) = prev
|
||||
.as_ref()
|
||||
.and_then(|(i, _)| link_angles.iter().position(|(j, _)| i == j))
|
||||
{
|
||||
let mut tmp: Vec<_> = link_angles.drain(..xpos).collect();
|
||||
link_angles.append(&mut tmp);
|
||||
let prev_idx = prev.unwrap().0;
|
||||
// the prev isn't duplicated
|
||||
assert_eq!(link_angles[0].0, prev_idx);
|
||||
assert_eq!(
|
||||
link_angles[1..].iter().position(|(j, _)| &prev_idx == j),
|
||||
None
|
||||
);
|
||||
|
||||
// subtract prev angle from all angles, and get them into `[0, 2π)`
|
||||
let prev_angle = link_angles[0].1;
|
||||
link_angles.iter_mut().for_each(|(_, i)| {
|
||||
*i -= prev_angle;
|
||||
use core::f64::consts::PI;
|
||||
if *i < 0.0 {
|
||||
*i += 2.0 * PI;
|
||||
} else if *i >= 2.0 * PI {
|
||||
*i -= 2.0 * PI;
|
||||
}
|
||||
});
|
||||
assert!(link_angles.is_sorted_by(|a, b| &a.1 <= &b.1));
|
||||
}
|
||||
|
||||
// split the list at the next, categorize links into left vs right
|
||||
if let Some(xpos) = next
|
||||
.as_ref()
|
||||
.and_then(|i| link_angles.iter().position(|(j, _)| i == j))
|
||||
{
|
||||
let (lhs, rhs) = link_angles.split_at(xpos);
|
||||
// don't put xpos into either
|
||||
let rhs = &rhs[1..];
|
||||
} else {
|
||||
// split at π
|
||||
let split_point = match link_angles
|
||||
.binary_search_by(|x| f64::total_cmp(&x.1, &core::f64::consts::PI))
|
||||
{
|
||||
Ok(x) => x,
|
||||
Err(x) => x,
|
||||
};
|
||||
let (lhs, rhs) = link_angles.split_at(split_point);
|
||||
};
|
||||
|
||||
let mut link_lhs = BTreeSet::new();
|
||||
let mut link_rhs = BTreeSet::new();
|
||||
let mut link_middle = BTreeSet::new();
|
||||
|
||||
if let Some((prev, prev_links)) = prev {
|
||||
match prev_links {
|
||||
None => {
|
||||
//assert!(link_middle_pre.contains(&prev));
|
||||
link_lhs.insert(prev);
|
||||
link_rhs.insert(prev);
|
||||
link_middle.insert(prev);
|
||||
}
|
||||
Some((old_lhs, old_rhs)) => {
|
||||
//assert!(link_middle_pre.contains(&prev));
|
||||
link_lhs.insert(old_lhs);
|
||||
link_rhs.insert(old_rhs);
|
||||
link_middle.insert(prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
link_lhs.insert(current);
|
||||
link_rhs.insert(current);
|
||||
|
||||
if let Some(next) = next {
|
||||
link_lhs.insert(next);
|
||||
link_rhs.insert(next);
|
||||
link_middle.insert(next);
|
||||
}
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// "etch" a taken route into the navmesh (marking it such that it doesn't get crossed later)
|
||||
/// basically marking the path itself, and also creating new dual paths to the left and right of it
|
||||
///
|
||||
/// @returns `false` if invalid edges or nodes were encountered
|
||||
pub fn etch_path(&mut self, path: &[NodeIndex]) -> bool {
|
||||
if path.len() < 2 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut it = path.windows(2).map(|i| (i[0], i[1]));
|
||||
let mut prev = None;
|
||||
|
||||
for (i0, i1) in it {
|
||||
prev = Some((i0, self.etch_path_make_companions(prev, i0, Some(i1))));
|
||||
}
|
||||
|
||||
self.etch_path_make_companions(prev, *path.last().unwrap(), None);
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn first_node() {
|
||||
let fi = NodeIndex::FIRST;
|
||||
assert!(fi.ty < NodeTy::Compound);
|
||||
assert!(fi.ty < NodeTy::DualInner);
|
||||
assert!(fi.ty < NodeTy::DualOuter);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pub mod astar;
|
||||
pub mod brute_force;
|
||||
pub mod draw;
|
||||
pub mod navcord;
|
||||
pub mod navcorder;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,138 @@
|
|||
(pcb test.dsn
|
||||
(parser
|
||||
(string_quote ")
|
||||
(space_in_quoted_tokens on)
|
||||
(host_cad "KiCad's Pcbnew")
|
||||
(host_version "7.0.9")
|
||||
)
|
||||
(resolution um 10)
|
||||
(unit um)
|
||||
(structure
|
||||
(layer F.Cu
|
||||
(type signal)
|
||||
(property
|
||||
(index 0)
|
||||
)
|
||||
)
|
||||
(layer B.Cu
|
||||
(type signal)
|
||||
(property
|
||||
(index 1)
|
||||
)
|
||||
)
|
||||
(boundary
|
||||
(path pcb 0 61846 -50400 2162.05 -50400 2162.05 -14754.8 61846 -14754.8
|
||||
61846 -50400)
|
||||
)
|
||||
(via "Via[0-1]_800:400_um")
|
||||
(rule
|
||||
(width 600)
|
||||
(clearance 200.1)
|
||||
(clearance 200.1 (type default_smd))
|
||||
(clearance 50 (type smd_smd))
|
||||
)
|
||||
)
|
||||
(placement
|
||||
(component Connector_Pin:Pin_D1.0mm_L10.0mm
|
||||
(place REF21 16510.000000 -46990.000000 front 0.000000 (PN Pin_D1.0mm_L10.0mm))
|
||||
(place REF11 53340.000000 -17653.000000 front 0.000000 (PN Pin_D1.0mm_L10.0mm))
|
||||
(place REF31 12700.000000 -40640.000000 front 0.000000 (PN Pin_D1.0mm_L10.0mm))
|
||||
(place REF22 45720.000000 -35560.000000 front 0.000000 (PN Pin_D1.0mm_L10.0mm))
|
||||
(place REF12 10668.000000 -32258.000000 front 0.000000 (PN Pin_D1.0mm_L10.0mm))
|
||||
(place REF32 35560.000000 -24130.000000 front 0.000000 (PN Pin_D1.0mm_L10.0mm))
|
||||
)
|
||||
)
|
||||
(library
|
||||
(image Connector_Pin:Pin_D1.0mm_L10.0mm
|
||||
(outline (path signal 120 1251 0 1231.99 -217.234 1175.56 -427.867 1083.4 -625.5
|
||||
958.322 -804.127 804.127 -958.322 625.5 -1083.4 427.867 -1175.56
|
||||
217.234 -1231.99 0 -1251 -217.234 -1231.99 -427.867 -1175.56
|
||||
-625.5 -1083.4 -804.127 -958.322 -958.322 -804.127 -1083.4 -625.5
|
||||
-1175.56 -427.867 -1231.99 -217.234 -1251 0 -1231.99 217.234
|
||||
-1175.56 427.867 -1083.4 625.5 -958.322 804.127 -804.127 958.322
|
||||
-625.5 1083.4 -427.867 1175.56 -217.234 1231.99 0 1251 217.234 1231.99
|
||||
427.867 1175.56 625.5 1083.4 804.127 958.322 958.322 804.127
|
||||
1083.4 625.5 1175.56 427.867 1231.99 217.234 1251 0))
|
||||
(outline (path signal 50 1500 0 1480.58 -240.617 1422.81 -475.002 1328.18 -697.085
|
||||
1199.16 -901.113 1039.09 -1081.8 852.097 -1234.48 643.039 -1355.18
|
||||
417.326 -1440.78 180.805 -1489.06 -60.399 -1498.78 -300.039 -1469.69
|
||||
-531.907 -1402.52 -750 -1299.04 -948.668 -1161.91 -1122.77 -994.684
|
||||
-1267.79 -801.699 -1379.97 -587.95 -1456.41 -358.973 -1495.14 -120.7
|
||||
-1495.14 120.7 -1456.41 358.973 -1379.97 587.95 -1267.79 801.699
|
||||
-1122.77 994.684 -948.668 1161.91 -750 1299.04 -531.907 1402.52
|
||||
-300.039 1469.69 -60.399 1498.78 180.805 1489.06 417.326 1440.78
|
||||
643.039 1355.18 852.097 1234.48 1039.09 1081.8 1199.16 901.113
|
||||
1328.18 697.085 1422.81 475.002 1480.58 240.617 1500 0))
|
||||
(outline (path signal 120 500 0 481.459 -134.898 427.21 -259.792 341.277 -365.418
|
||||
230.033 -443.943 101.728 -489.542 -34.121 -498.834 -167.44 -471.13
|
||||
-288.34 -408.485 -387.856 -315.544 -458.606 -199.201 -495.343 -68.083
|
||||
-495.343 68.083 -458.606 199.201 -387.856 315.544 -288.34 408.485
|
||||
-167.44 471.13 -34.121 498.834 101.728 489.542 230.033 443.943
|
||||
341.277 365.418 427.21 259.792 481.459 134.898 500 0))
|
||||
(outline (path signal 120 1000 0 980.785 -195.09 923.88 -382.683 831.47 -555.57
|
||||
707.107 -707.107 555.57 -831.47 382.683 -923.88 195.09 -980.785
|
||||
0 -1000 -195.09 -980.785 -382.683 -923.88 -555.57 -831.47
|
||||
-707.107 -707.107 -831.47 -555.57 -923.88 -382.683 -980.785 -195.09
|
||||
-1000 0 -980.785 195.09 -923.88 382.683 -831.47 555.57 -707.107 707.107
|
||||
-555.57 831.47 -382.683 923.88 -195.09 980.785 0 1000 195.09 980.785
|
||||
382.683 923.88 555.57 831.47 707.107 707.107 831.47 555.57
|
||||
923.88 382.683 980.785 195.09 1000 0))
|
||||
(pin Round[A]Pad_2000_um 1 0 0)
|
||||
)
|
||||
(padstack Round[A]Pad_2000_um
|
||||
(shape (circle F.Cu 2000))
|
||||
(shape (circle B.Cu 2000))
|
||||
(attach off)
|
||||
)
|
||||
(padstack "Via[0-1]_800:400_um"
|
||||
(shape (circle F.Cu 800))
|
||||
(shape (circle B.Cu 800))
|
||||
(attach off)
|
||||
)
|
||||
)
|
||||
(network
|
||||
(net 1
|
||||
(pins REF11-1 REF12-1)
|
||||
)
|
||||
(net 2
|
||||
(pins REF21-1 REF22-1)
|
||||
)
|
||||
(net 3
|
||||
(pins REF31-1 REF32-1)
|
||||
)
|
||||
(class kicad_default "" 1 2 3
|
||||
(circuit
|
||||
(use_via Via[0-1]_800:400_um)
|
||||
)
|
||||
(rule
|
||||
(width 600)
|
||||
(clearance 200.1)
|
||||
)
|
||||
)
|
||||
(class test unconnected
|
||||
(circuit
|
||||
(use_via Via[0-1]_800:400_um)
|
||||
)
|
||||
(rule
|
||||
(width 300)
|
||||
(clearance 500.1)
|
||||
)
|
||||
)
|
||||
)
|
||||
(wiring
|
||||
(wire (path F.Cu 600 20000 -50000 60000 -50000)(net unconnected)(type route))
|
||||
(wire (path F.Cu 600 60000 -20000 40000 -20000 40000 -40000)(net unconnected)(type route))
|
||||
(wire (path F.Cu 600 50000 -40000 50000 -30000)(net unconnected)(type route))
|
||||
(wire (path F.Cu 600 60000 -50000 60000 -20000)(net unconnected)(type route))
|
||||
(wire (path F.Cu 600 20000 -20000 20000 -50000)(net unconnected)(type route))
|
||||
(wire (path F.Cu 600 40000 -40000 50000 -40000)(net unconnected)(type route))
|
||||
(via "Via[0-1]_800:400_um" 20000 -50000 (net unconnected)(type route))
|
||||
(via "Via[0-1]_800:400_um" 60000 -50000 (net unconnected)(type route))
|
||||
(via "Via[0-1]_800:400_um" 50000 -40000 (net unconnected)(type route))
|
||||
(via "Via[0-1]_800:400_um" 40000 -20000 (net unconnected)(type route))
|
||||
(via "Via[0-1]_800:400_um" 20000 -20000 (net unconnected)(type route))
|
||||
(via "Via[0-1]_800:400_um" 40000 -40000 (net unconnected)(type route))
|
||||
(via "Via[0-1]_800:400_um" 60000 -20000 (net unconnected)(type route))
|
||||
(via "Via[0-1]_800:400_um" 50000 -30000 (net unconnected)(type route))
|
||||
)
|
||||
)
|
||||
Loading…
Reference in New Issue