kararrr/router.py

299 lines
12 KiB
Python
Raw Permalink Normal View History

2022-01-15 00:28:49 -07:00
# -*- coding: utf-8 -*-
2022-01-15 01:43:52 -07:00
# Copyright (C) 2014 Chris Hinsley All Rights Reserved
# Copyright (C) 2022 Jeff Moe
2022-01-15 00:28:49 -07:00
import sys, time
from array import array
2022-01-15 01:04:45 -07:00
from itertools import islice, groupby
2022-01-15 00:28:49 -07:00
from random import shuffle
from mymath import *
from layer import *
def shift(l, n):
2022-01-15 13:41:38 -07:00
n = n % len(l)
head = l[:n]
del l[:n]
l.extend(head)
return l
2022-01-15 00:28:49 -07:00
class Pcb():
2022-01-15 13:41:38 -07:00
def __init__(self, dimensions, routing_flood_vectors, routing_path_vectors, dfunc, resolution, verbosity, minz):
self.dfunc = dfunc
self.verbosity = verbosity
self.routing_flood_vectors = routing_flood_vectors
self.routing_path_vectors = routing_path_vectors
self.layers = Layers(dimensions, 1.0 / resolution)
self.width, self.height, self.depth = dimensions
self.resolution = resolution
self.minz = minz
self.width *= resolution
self.height *= resolution
self.stride = self.width * self.height
self.nodes = array('i', [0 for x in range(self.stride * self.depth)])
self.netlist = []
self.deform = {}
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def grid_to_space_point(self, node):
if node in self.deform:
return self.deform[node]
return (float(node[0]), float(node[1]), float(node[2]))
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def set_node(self, node, value):
self.nodes[(self.stride * node[2]) + (node[1] * self.width) + node[0]] = value
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def get_node(self, node):
return self.nodes[(self.stride * node[2]) + (node[1] * self.width) + node[0]]
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def all_marked(self, vectors, node):
x, y, z = node
for dx, dy, dz in vectors[z % 2]:
nx = x + dx; ny = y + dy; nz = z + dz
if (0 <= nx < self.width) and (0 <= ny < self.height) and (0 <= nz < self.depth):
mark = self.get_node((nx, ny, nz))
if mark != 0:
yield (mark, (nx, ny, nz))
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def all_not_marked(self, vectors, node):
x, y, z = node
for dx, dy, dz in vectors[z % 2]:
nx = x + dx; ny = y + dy; nz = z + dz
if (0 <= nx < self.width) and (0 <= ny < self.height) and (0 <= nz < self.depth):
if self.get_node((nx, ny, nz)) == 0:
yield (nx, ny, nz)
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def all_nearer_sorted(self, vectors, node, goal, func):
distance = self.get_node(node)
sgoal = self.grid_to_space_point(goal)
nodes = [(func(self.grid_to_space_point(marked[1]), sgoal), marked[1]) \
for marked in self.all_marked(vectors, node) \
if (distance - marked[0]) > 0]
nodes.sort()
for node in nodes:
yield node[1]
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def all_not_shorting(self, gather, params, node, radius, via, gap):
np = self.grid_to_space_point(node)
for new_node in gather(*params):
nnp = self.grid_to_space_point(new_node)
if node[2] != new_node[2]:
if not self.layers.hit_line(np, nnp, via, gap):
yield new_node
else:
if not self.layers.hit_line(np, nnp, radius, gap):
yield new_node
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def mark_distances(self, vectors, radius, via, gap, starts, ends = []):
distance = 1
nodes = list(starts)
for node in nodes:
self.set_node(node, distance)
while nodes:
distance += 1
new_nodes = []
for node in nodes:
for new_node in self.all_not_shorting(self.all_not_marked, (vectors, node), node, radius, via, gap):
self.set_node(new_node, distance)
new_nodes.append(new_node)
nodes = new_nodes
if 0 not in [self.get_node(node) for node in ends]:
break
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def unmark_distances(self):
self.nodes = array('i', [0 for x in range(self.stride * self.depth)])
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def add_track(self, track):
radius, via, gap, net = track
self.netlist.append(Net(net, radius, via, gap, self))
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def route(self, timeout):
self.remove_netlist()
self.unmark_distances()
now = time.time()
self.netlist.sort(key = lambda i: i.radius, reverse = True)
index = 0
while index < len(self.netlist):
if self.netlist[index].route(self.minz):
index += 1
else:
if index == 0:
self.shuffle_netlist()
else:
self.netlist.insert(0, self.netlist.pop(index))
while index != 0:
self.netlist[index].remove()
self.netlist[index].shuffle_topology()
index -= 1
if time.time() - now > timeout:
return False
if self.verbosity >= 1:
self.print_netlist()
return True
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def cost(self):
return sum(len(path) for net in self.netlist for path in net.paths)
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def shuffle_netlist(self):
for net in self.netlist:
net.remove()
net.shuffle_topology()
shuffle(self.netlist)
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def remove_netlist(self):
for net in self.netlist:
net.remove()
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def print_netlist(self):
for net in self.netlist:
net.print_net()
print([])
sys.stdout.flush()
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def print_pcb(self):
scale = 1.0 / self.resolution
print([self.width * scale, self.height * scale, self.depth])
sys.stdout.flush()
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def print_stats(self):
num_vias = 0
num_terminals = 0
num_nets = len(self.netlist)
for net in self.netlist:
num_terminals += len(net.terminals)
for path in net.paths:
for a, b in zip(path, islice(path, 1, None)):
if a[2] != b[2]:
num_vias += 1
print >> sys.stderr, "Number of Terminals:", num_terminals
print >> sys.stderr, "Number of Nets:", num_nets
print >> sys.stderr, "Number of Vias:", num_vias
2022-01-15 00:28:49 -07:00
class Net():
2022-01-15 13:41:38 -07:00
def __init__(self, terminals, radius, via, gap, pcb):
self.pcb = pcb
self.terminals = [(r * pcb.resolution, g * pcb.resolution, \
(x * pcb.resolution, y * pcb.resolution, z), \
[(cx * pcb.resolution, cy * pcb.resolution) for cx, cy in s]) \
for r, g, (x, y, z), s in terminals]
self.radius = radius * pcb.resolution
self.via = via * pcb.resolution
self.gap = gap * pcb.resolution
self.paths = []
self.remove()
for term in self.terminals:
for z in range(pcb.depth):
pcb.deform[(int(term[2][0] + 0.5), int(term[2][1] + 0.5), z)] = (term[2][0], term[2][1], float(z))
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def optimise_paths(self, paths):
opt_paths = []
for path in paths:
opt_path = []
d = (0, 0, 0)
for a, b in zip(path, islice(path, 1, None)):
p0 = self.pcb.grid_to_space_point(a)
p1 = self.pcb.grid_to_space_point(b)
d1 = norm_3d(sub_3d(p1, p0))
if d1 != d:
opt_path.append(a)
d = d1
opt_path.append(path[-1])
opt_paths.append(opt_path)
return opt_paths
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def shuffle_topology(self):
shuffle(self.terminals)
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def add_paths_collision_lines(self):
for path in self.paths:
for a, b in zip(path, islice(path, 1, None)):
p0 = self.pcb.grid_to_space_point(a)
p1 = self.pcb.grid_to_space_point(b)
if a[2] != b[2]:
self.pcb.layers.add_line(p0, p1, self.via, self.gap)
else:
self.pcb.layers.add_line(p0, p1, self.radius, self.gap)
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def sub_paths_collision_lines(self):
for path in self.paths:
for a, b in zip(path, islice(path, 1, None)):
p0 = self.pcb.grid_to_space_point(a)
p1 = self.pcb.grid_to_space_point(b)
if a[2] != b[2]:
self.pcb.layers.sub_line(p0, p1, self.via, self.gap)
else:
self.pcb.layers.sub_line(p0, p1, self.radius, self.gap)
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def add_terminal_collision_lines(self):
for node in self.terminals:
r, g, (x, y, _), s = node
if not s:
self.pcb.layers.add_line((x, y, 0), (x, y, self.pcb.depth - 1), r, g)
else:
for z in range(self.pcb.depth):
for a, b in zip(s, islice(s, 1, None)):
self.pcb.layers.add_line((x + a[0], y + a[1], z), (x + b[0], y + b[1], z), r, g)
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def sub_terminal_collision_lines(self):
for node in self.terminals:
r, g, (x, y, _), s = node
if not s:
self.pcb.layers.sub_line((x, y, 0), (x, y, self.pcb.depth - 1), r, g)
else:
for z in range(self.pcb.depth):
for a, b in zip(s, islice(s, 1, None)):
self.pcb.layers.sub_line((x + a[0], y + a[1], z), (x + b[0], y + b[1], z), r, g)
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def remove(self):
self.sub_paths_collision_lines()
self.sub_terminal_collision_lines()
self.paths = []
self.add_terminal_collision_lines()
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def route(self, minz):
try:
self.paths = []
self.sub_terminal_collision_lines()
visited = set()
for index in range(1, len(self.terminals)):
visited |= set([(int(self.terminals[index - 1][2][0]+0.5), int(self.terminals[index - 1][2][1]+0.5), z) for z in range(self.pcb.depth)])
ends = [(int(self.terminals[index][2][0]+0.5), int(self.terminals[index][2][1]+0.5), z) for z in range(self.pcb.depth)]
self.pcb.mark_distances(self.pcb.routing_flood_vectors, self.radius, self.via, self.gap, visited, ends)
ends = [(self.pcb.get_node(node), node) for node in ends]
ends.sort()
_, end = ends[0]
path = [end]
while path[-1] not in visited:
nearer_nodes = self.pcb.all_not_shorting(self.pcb.all_nearer_sorted, \
(self.pcb.routing_path_vectors, path[-1], end, self.pcb.dfunc), path[-1], self.radius, self.via, self.gap)
next_node = next(nearer_nodes)
if minz:
for node in nearer_nodes:
if node[2] == path[-1][2]:
next_node = node
break
path.append(next_node)
visited |= set(path)
self.paths.append(path)
self.pcb.unmark_distances()
self.paths = self.optimise_paths(self.paths)
self.add_paths_collision_lines()
self.add_terminal_collision_lines()
return True
except StopIteration:
self.pcb.unmark_distances()
self.remove()
return False
2022-01-15 00:28:49 -07:00
2022-01-15 13:41:38 -07:00
def print_net(self):
scale = 1.0 / self.pcb.resolution
spaths = []
for path in self.paths:
spath = []
for node in path:
spath += [self.pcb.grid_to_space_point(node)]
spaths += [spath]
print([self.radius * scale, self.via * scale, self.gap * scale, \
[(r * scale, g * scale, (x * scale, y * scale, z), \
[(cx * scale, cy * scale) for cx, cy in s]) for r, g, (x, y, z), s in self.terminals], \
[[(x * scale, y * scale, z) for x, y, z in spath] for spath in spaths]])
2022-01-15 01:18:14 -07:00