kararrr/go-dsn2pcb/router/router.go

859 lines
22 KiB
Go

// Copyright (C) 2014 Chris Hinsley.
//package name
package router
//package imports
import (
"../layer"
"../mymath"
"fmt"
"math/rand"
"sort"
"time"
)
/////////////////////////
//public structures/types
/////////////////////////
//dimensions of pcb board in grid points/layers
type Dims struct {
Width int
Height int
Depth int
}
//grid point and collections
type Point struct {
X int
Y int
Z int
}
type Vectors []*Point
type Vectorss []Vectors
//netlist structures
type Tpoint struct {
X float32
Y float32
Z float32
}
type Path []*Tpoint
type Paths []Path
type Cord struct {
X float32
Y float32
}
type Cords []*Cord
type Terminal struct {
Radius float32
Gap float32
Term Tpoint
Shape Cords
}
type Terminals []*Terminal
type Track struct {
Radius float32
Via float32
Gap float32
Terms Terminals
}
type Output struct {
Radius float32
Via float32
Gap float32
Terms Terminals
Paths Paths
}
//////////////////////////
//private structures/types
//////////////////////////
//sortable point
type sort_point struct {
mark float32
node *Point
}
type sort_points []*sort_point
type nets []*net
type aabb struct {
minx int
miny int
maxx int
maxy int
}
//for sorting nets
type by_group nets
func (s by_group) Len() int {
return len(s)
}
func (s by_group) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s by_group) Less(i, j int) bool {
if s[i].area == s[j].area {
return s[i].radius > s[j].radius
}
return s[i].area < s[j].area
}
///////////////////////////
//private utility functions
///////////////////////////
//insert sort_point in ascending order
func insert_sort_point(nodes *sort_points, node *Point, mark float32) *sort_points {
n := *nodes
mn := sort_point{mark, node}
for i := 0; i < len(n); i++ {
if n[i].mark >= mark {
n = append(n, nil)
copy(n[i+1:], n[i:])
n[i] = &mn
return &n
}
}
n = append(n, &mn)
return &n
}
//pcb object
type Pcb struct {
width int
height int
depth int
stride int
routing_flood_vectors *Vectorss
routing_path_vectors *Vectorss
dfunc func(*mymath.Point, *mymath.Point) float32
resolution int
verbosity int
quantization int
viascost int
layers *layer.Layers
netlist nets
nodes []int
deform map[Point]*mymath.Point
}
//pcb methods
////////////////
//public methods
////////////////
func NewPcb(dims *Dims, rfvs, rpvs *Vectorss, dfunc func(*mymath.Point, *mymath.Point) float32,
res, verb, quant, viascost int) *Pcb {
p := Pcb{}
p.Init(dims, rfvs, rpvs, dfunc, res, verb, quant, viascost)
return &p
}
func (self *Pcb) Init(dims *Dims, rfvs, rpvs *Vectorss,
dfunc func(*mymath.Point, *mymath.Point) float32, res, verb, quant, viascost int) {
self.width = dims.Width
self.height = dims.Height
self.depth = dims.Depth
self.routing_flood_vectors = rfvs
self.routing_path_vectors = rpvs
self.dfunc = dfunc
self.resolution = res
self.verbosity = verb
self.quantization = quant * res
self.viascost = viascost
self.layers = layer.NewLayers(layer.Dims{self.width * 3, self.height * 3, self.depth}, 3.0/float32(res))
self.netlist = nil
self.width *= res
self.height *= res
self.stride = self.width * self.height
self.nodes = make([]int, self.stride*self.depth, self.stride*self.depth)
self.deform = map[Point]*mymath.Point{}
}
func (self *Pcb) Copy() *Pcb {
new_pcb := Pcb{}
new_pcb.width = self.width
new_pcb.height = self.height
new_pcb.depth = self.depth
new_pcb.stride = self.stride
new_pcb.routing_flood_vectors = self.routing_flood_vectors
new_pcb.routing_path_vectors = self.routing_path_vectors
new_pcb.dfunc = self.dfunc
new_pcb.resolution = self.resolution
new_pcb.verbosity = self.verbosity
new_pcb.quantization = self.quantization
new_pcb.viascost = self.viascost
new_pcb.layers = layer.NewLayers(layer.Dims{self.width * 3, self.height * 3, self.depth}, 3.0/float32(self.resolution))
new_pcb.netlist = nil
for _, net := range self.netlist {
new_pcb.netlist = append(new_pcb.netlist, net.copy())
}
new_pcb.nodes = make([]int, self.stride*self.depth, self.stride*self.depth)
new_pcb.deform = self.deform
return &new_pcb
}
//add net
func (self *Pcb) Add_track(t *Track) {
self.netlist = append(self.netlist, newnet(t.Terms, t.Radius, t.Via, t.Gap, *self))
}
//attempt to route board within time
func (self *Pcb) Route(timeout float64) bool {
self.remove_netlist()
self.unmark_distances()
self.reset_areas()
self.shuffle_netlist()
sort.Sort(by_group(self.netlist))
hoisted_nets := map[*net]bool{}
index := 0
start_time := time.Now()
for index < len(self.netlist) {
if self.netlist[index].route() {
index += 1
} else {
if index == 0 {
self.reset_areas()
self.shuffle_netlist()
sort.Sort(by_group(self.netlist))
hoisted_nets = map[*net]bool{}
} else {
pos := 0
self.netlist, pos = hoist_net(self.netlist, index)
if (pos == index) || hoisted_nets[self.netlist[pos]] {
if pos != 0 {
self.netlist[pos].area = self.netlist[pos-1].area
self.netlist, pos = hoist_net(self.netlist, pos)
}
delete(hoisted_nets, self.netlist[pos])
} else {
hoisted_nets[self.netlist[pos]] = true
}
for index > pos {
self.netlist[index].remove()
self.netlist[index].shuffle_topology()
index -= 1
}
}
}
if time.Since(start_time).Seconds() > timeout {
return false
}
if self.verbosity >= 1 {
self.Print_netlist()
}
}
return true
}
//cost of board in complexity terms
func (self *Pcb) Cost() int {
sum := 0
for _, net := range self.netlist {
for _, path := range net.paths {
sum += len(path)
}
}
return sum
}
//increase area quantization
func (self *Pcb) Increase_quantization() {
self.quantization++
}
//output dimensions of board for viewer app
func (self *Pcb) Print_pcb() {
scale := 1.0 / float32(self.resolution)
fmt.Print("[")
fmt.Print(float32(self.width)*scale, ",")
fmt.Print(float32(self.height)*scale, ",")
fmt.Print(self.depth)
fmt.Println("]")
}
//output netlist and paths of board for viewer app
func (self *Pcb) Print_netlist() {
for _, net := range self.netlist {
net.print_net()
}
fmt.Println("[]")
}
//output stats to screen
func (self *Pcb) Print_stats() {
num_vias := 0
num_terminals := 0
num_nets := len(self.netlist)
for _, net := range self.netlist {
num_terminals += len(net.terminals)
for _, path := range net.paths {
p1 := *path[0]
for _, node := range path[1:] {
p0 := p1
p1 = *node
if p0.Z != p1.Z {
num_vias++
}
}
}
}
println("Number of Terminals:", num_terminals)
println("Number of Nets:", num_nets)
println("Number of Vias:", num_vias)
}
/////////////////
//private methods
/////////////////
//convert grid point to space point
func (self *Pcb) grid_to_space_point(p *Point) *mymath.Point {
sp := self.deform[*p]
if sp != nil {
return sp
}
return &mymath.Point{float32(p.X), float32(p.Y), float32(p.Z)}
}
//set grid point to value
func (self *Pcb) set_node(node *Point, value int) {
self.nodes[(self.stride*node.Z)+(node.Y*self.width)+node.X] = value
}
//get grid point value
func (self *Pcb) get_node(node *Point) int {
return self.nodes[(self.stride*node.Z)+(node.Y*self.width)+node.X]
}
//generate all grid points surrounding point, that are not value 0
func (self *Pcb) all_marked(vectors *Vectorss, node *Point) *sort_points {
vec := *vectors
x, y, z := node.X, node.Y, node.Z
yield := make(sort_points, 0, len(vec[z%2]))
for _, v := range vec[z%2] {
nx := x + v.X
ny := y + v.Y
nz := z + v.Z
if (0 <= nx) && (nx < self.width) && (0 <= ny) && (ny < self.height) && (0 <= nz) && (nz < self.depth) {
n := Point{nx, ny, nz}
mark := self.get_node(&n)
if mark != 0 {
yield = append(yield, &sort_point{float32(mark), &n})
}
}
}
return &yield
}
//generate all grid points surrounding point, that are value 0
func (self *Pcb) all_not_marked(vectors *Vectorss, node *Point) *Vectors {
vec := *vectors
x, y, z := node.X, node.Y, node.Z
yield := make(Vectors, 0, len(vec[z%2]))
for _, v := range vec[z%2] {
nx := x + v.X
ny := y + v.Y
nz := z + v.Z
if (0 <= nx) && (nx < self.width) && (0 <= ny) && (ny < self.height) && (0 <= nz) && (nz < self.depth) {
n := Point{nx, ny, nz}
if self.get_node(&n) == 0 {
yield = append(yield, &n)
}
}
}
return &yield
}
//generate all grid points surrounding point, that are nearer the goal point, sorted
func (self *Pcb) all_nearer_sorted(vectors *Vectorss, node, goal *Point,
dfunc func(*mymath.Point, *mymath.Point) float32) *Vectors {
gp := self.grid_to_space_point(goal)
distance := float32(self.get_node(node))
nodes := &sort_points{}
for _, mn := range *self.all_marked(vectors, node) {
if (distance - mn.mark) > 0 {
mnp := self.grid_to_space_point(mn.node)
nodes = insert_sort_point(nodes, mn.node, dfunc(mnp, gp))
}
}
yield := make(Vectors, len(*nodes), len(*nodes))
for i, node := range *nodes {
yield[i] = node.node
}
return &yield
}
//generate all grid points surrounding point that are not shorting with an existing track
func (self *Pcb) all_not_shorting(gather *Vectors, node *Point, radius, gap float32) *Vectors {
yield := make(Vectors, 0, 16)
np := self.grid_to_space_point(node)
for _, new_node := range *gather {
nnp := self.grid_to_space_point(new_node)
if !self.layers.Hit_line(np, nnp, radius, gap) {
yield = append(yield, new_node)
}
}
return &yield
}
//flood fill distances from starts till ends covered
func (self *Pcb) mark_distances(vectors *Vectorss, radius, via, gap float32, starts *map[Point]bool, ends *Vectors) {
via_vectors := &Vectorss{
Vectors{&Point{0, 0, -1}, &Point{0, 0, 1}},
Vectors{&Point{0, 0, -1}, &Point{0, 0, 1}}}
distance := 1
nodes := *starts
vias_nodes := map[int]*map[Point]bool{}
for (len(nodes) > 0) || (len(vias_nodes) > 0) {
for node := range nodes {
self.set_node(&node, distance)
}
flag := true
for _, node := range *ends {
if self.get_node(node) == 0 {
flag = false
break
}
}
if flag {
break
}
new_nodes := map[Point]bool{}
for node := range nodes {
for _, new_node := range *self.all_not_shorting(self.all_not_marked(vectors, &node), &node, radius, gap) {
new_nodes[*new_node] = true
}
}
new_vias_nodes := map[Point]bool{}
for node := range nodes {
for _, new_vias_node := range *self.all_not_shorting(self.all_not_marked(via_vectors, &node), &node, via, gap) {
new_vias_nodes[*new_vias_node] = true
}
}
if len(new_vias_nodes) > 0 {
vias_nodes[distance+self.viascost] = &new_vias_nodes
}
delay_nodes := vias_nodes[distance]
if delay_nodes != nil {
for vias_node := range *delay_nodes {
if self.get_node(&vias_node) == 0 {
new_nodes[vias_node] = true
}
}
delete(vias_nodes, distance)
}
nodes = new_nodes
distance++
}
}
//set all grid values back to 0
func (self *Pcb) unmark_distances() {
for i := 0; i < len(self.nodes); i++ {
self.nodes[i] = 0
}
}
//reset areas
func (self *Pcb) reset_areas() {
for _, net := range self.netlist {
net.area, net.bbox = aabb_terminals(net.terminals, self.quantization)
}
}
//shuffle order of netlist
func (self *Pcb) shuffle_netlist() {
for _, net := range self.netlist {
net.shuffle_topology()
}
new_nets := make(nets, len(self.netlist), len(self.netlist))
for i, r := range rand.Perm(len(self.netlist)) {
new_nets[i] = self.netlist[r]
}
self.netlist = new_nets
}
//move net to top of area group
func hoist_net(ns nets, n int) (nets, int) {
i := 0
if n != 0 {
for i = n; i >= 0; i-- {
if ns[i].area < ns[n].area {
break
}
}
i++
if n != i {
net := ns[n]
copy(ns[i+1:], ns[i:n])
ns[i] = net
}
}
return ns, i
}
func (self *Pcb) remove_netlist() {
for _, net := range self.netlist {
net.remove()
}
}
//net object
type net struct {
pcb Pcb
terminals Terminals
radius float32
via float32
gap float32
area int
bbox aabb
paths Vectorss
}
//net methods
/////////////////
//private methods
/////////////////
func newnet(terms Terminals, radius, via, gap float32, pcb Pcb) *net {
n := net{}
n.init(terms, radius, via, gap, pcb)
return &n
}
//scale terminals for resolution of grid
func scale_terminals(terms Terminals, res int) Terminals {
for i := 0; i < len(terms); i++ {
terms[i].Radius *= float32(res)
terms[i].Gap *= float32(res)
terms[i].Term.X *= float32(res)
terms[i].Term.Y *= float32(res)
terms[i].Term.Z *= float32(res)
for _, cord := range terms[i].Shape {
cord.X *= float32(res)
cord.Y *= float32(res)
}
}
return terms
}
//aabb of terminals
func aabb_terminals(terms Terminals, quantization int) (int, aabb) {
minx := (int(terms[0].Term.X) / quantization) * quantization
miny := (int(terms[0].Term.Y) / quantization) * quantization
maxx := ((int(terms[0].Term.X) + (quantization - 1)) / quantization) * quantization
maxy := ((int(terms[0].Term.Y) + (quantization - 1)) / quantization) * quantization
for i := 1; i < len(terms); i++ {
tminx := (int(terms[i].Term.X) / quantization) * quantization
tminy := (int(terms[i].Term.Y) / quantization) * quantization
tmaxx := ((int(terms[i].Term.X) + (quantization - 1)) / quantization) * quantization
tmaxy := ((int(terms[i].Term.Y) + (quantization - 1)) / quantization) * quantization
if tminx < minx {
minx = tminx
}
if tminy < miny {
miny = tminy
}
if tmaxx > maxx {
maxx = tmaxx
}
if tmaxy > maxy {
maxy = tmaxy
}
}
rec := aabb{minx, miny, maxx, maxy}
return (maxx - minx) * (maxy - miny), rec
}
func (self *net) init(terms Terminals, radius, via, gap float32, pcb Pcb) {
self.pcb = pcb
self.radius = radius * float32(pcb.resolution)
self.via = via * float32(pcb.resolution)
self.gap = gap * float32(pcb.resolution)
self.paths = make(Vectorss, 0)
self.terminals = scale_terminals(terms, pcb.resolution)
self.area, self.bbox = aabb_terminals(terms, pcb.quantization)
self.remove()
for _, term := range self.terminals {
for z := 0; z < pcb.depth; z++ {
p := Point{int(term.Term.X + 0.5), int(term.Term.Y + 0.5), z}
sp := mymath.Point{term.Term.X, term.Term.Y, float32(z)}
pcb.deform[p] = &sp
}
}
}
//copy terminals
func copy_terminals(terms Terminals) Terminals {
new_terms := make(Terminals, len(terms), cap(terms))
copy(new_terms, terms)
return new_terms
}
func (self *net) copy() *net {
new_net := net{}
new_net.pcb = self.pcb
new_net.radius = self.radius
new_net.via = self.via
new_net.gap = self.gap
new_net.area = self.area
new_net.terminals = copy_terminals(self.terminals)
new_net.paths = self.optimise_paths(self.paths[:])
return &new_net
}
//randomize order of terminals
func shuffle_terminals(terms Terminals) Terminals {
new_terms := make(Terminals, len(terms), len(terms))
for i, r := range rand.Perm(len(terms)) {
new_terms[i] = terms[r]
}
return new_terms
}
func (self *net) shuffle_topology() {
self.terminals = shuffle_terminals(self.terminals)
}
//add terminal entries to spacial cache
func (self *net) add_terminal_collision_lines() {
for _, node := range self.terminals {
r, g, x, y, shape := node.Radius, node.Gap, node.Term.X, node.Term.Y, node.Shape
if len(shape) == 0 {
self.pcb.layers.Add_line(&mymath.Point{x, y, 0}, &mymath.Point{x, y, float32(self.pcb.depth - 1)}, r, g)
} else {
for z := 0; z < self.pcb.depth; z++ {
p1 := mymath.Point{x + shape[0].X, y + shape[0].Y, float32(z)}
for i := 1; i < len(shape); i++ {
p0 := p1
p1 = mymath.Point{x + shape[i].X, y + shape[i].Y, float32(z)}
self.pcb.layers.Add_line(&p0, &p1, r, g)
}
}
}
}
}
//remove terminal entries from spacial cache
func (self *net) sub_terminal_collision_lines() {
for _, node := range self.terminals {
r, g, x, y, shape := node.Radius, node.Gap, node.Term.X, node.Term.Y, node.Shape
if len(shape) == 0 {
self.pcb.layers.Sub_line(&mymath.Point{x, y, 0}, &mymath.Point{x, y, float32(self.pcb.depth - 1)}, r, g)
} else {
for z := 0; z < self.pcb.depth; z++ {
p1 := mymath.Point{x + shape[0].X, y + shape[0].Y, float32(z)}
for i := 1; i < len(shape); i++ {
p0 := p1
p1 = mymath.Point{x + shape[i].X, y + shape[i].Y, float32(z)}
self.pcb.layers.Sub_line(&p0, &p1, r, g)
}
}
}
}
}
//add paths entries to spacial cache
func (self *net) add_paths_collision_lines() {
for _, path := range self.paths {
p1 := self.pcb.grid_to_space_point(path[0])
for i := 1; i < len(path); i++ {
p0 := p1
p1 = self.pcb.grid_to_space_point(path[i])
if path[i-1].Z != path[i].Z {
//via direction
self.pcb.layers.Add_line(p0, p1, self.via, self.gap)
} else {
//not via direction
self.pcb.layers.Add_line(p0, p1, self.radius, self.gap)
}
}
}
}
//remove paths entries from spacial cache
func (self *net) sub_paths_collision_lines() {
for _, path := range self.paths {
p1 := self.pcb.grid_to_space_point(path[0])
for i := 1; i < len(path); i++ {
p0 := p1
p1 = self.pcb.grid_to_space_point(path[i])
if path[i-1].Z != path[i].Z {
//via direction
self.pcb.layers.Sub_line(p0, p1, self.via, self.gap)
} else {
//not via direction
self.pcb.layers.Sub_line(p0, p1, self.radius, self.gap)
}
}
}
}
//remove net entries from spacial grid
func (self *net) remove() {
self.sub_paths_collision_lines()
self.sub_terminal_collision_lines()
self.paths = nil
self.add_terminal_collision_lines()
}
//remove redundant points from paths
func (self *net) optimise_paths(paths Vectorss) Vectorss {
opt_paths := make(Vectorss, 0)
for _, path := range paths {
opt_path := make(Vectors, 0)
d := &mymath.Point{0, 0, 0}
p1 := self.pcb.grid_to_space_point(path[0])
for i := 1; i < len(path); i++ {
p0 := p1
p1 = self.pcb.grid_to_space_point(path[i])
d1 := mymath.Norm_3d(mymath.Sub_3d(p1, p0))
if !mymath.Equal_3d(d1, d) {
opt_path = append(opt_path, path[i-1])
d = d1
}
}
opt_path = append(opt_path, path[len(path)-1])
opt_paths = append(opt_paths, opt_path)
}
return opt_paths
}
//backtrack path from ends to starts
func (self *net) backtrack_path(vis *map[Point]bool, end *Point, radius, via, gap float32) (Vectors, bool) {
via_vectors := &Vectorss{
Vectors{&Point{0, 0, -1}, &Point{0, 0, 1}},
Vectors{&Point{0, 0, -1}, &Point{0, 0, 1}}}
visited := *vis
path := Vectors{end}
dv := &mymath.Point{0, 0, 0}
for {
path_node := path[len(path)-1]
if visited[*path_node] {
//found existing track
return path, true
}
nearer_nodes := make(Vectors, 0)
for _, node := range *self.pcb.all_not_shorting(
self.pcb.all_nearer_sorted(self.pcb.routing_path_vectors, path_node, end, self.pcb.dfunc),
path_node, radius, gap) {
nearer_nodes = append(nearer_nodes, node)
}
for _, node := range *self.pcb.all_not_shorting(
self.pcb.all_nearer_sorted(via_vectors, path_node, end, self.pcb.dfunc),
path_node, via, gap) {
nearer_nodes = append(nearer_nodes, node)
}
if len(nearer_nodes) == 0 {
//no nearer nodes
return path, false
}
next_node := nearer_nodes[0]
dv2 := mymath.Norm_3d(self.pcb.grid_to_space_point(path_node))
if !visited[*next_node] {
for i := 1; i < len(nearer_nodes); i++ {
node := nearer_nodes[i]
dv1 := mymath.Norm_3d(self.pcb.grid_to_space_point(node))
if mymath.Equal_3d(dv, mymath.Sub_3d(dv1, dv2)) {
next_node = node
}
}
}
dv1 := mymath.Norm_3d(self.pcb.grid_to_space_point(next_node))
dv = mymath.Norm_3d(mymath.Sub_3d(dv1, dv2))
path = append(path, next_node)
}
}
//attempt to route this net on the current boards state
func (self *net) route() bool {
if self.radius == 0.0 {
//unused terminals track !
return true
}
self.paths = make(Vectorss, 0)
self.sub_terminal_collision_lines()
visited := map[Point]bool{}
for index := 1; index < len(self.terminals); index++ {
for z := 0; z < self.pcb.depth; z++ {
x, y := int(self.terminals[index-1].Term.X+0.5), int(self.terminals[index-1].Term.Y+0.5)
visited[Point{x, y, z}] = true
}
ends := make(Vectors, self.pcb.depth, self.pcb.depth)
for z := 0; z < self.pcb.depth; z++ {
x, y := int(self.terminals[index].Term.X+0.5), int(self.terminals[index].Term.Y+0.5)
ends[z] = &Point{x, y, z}
}
self.pcb.mark_distances(self.pcb.routing_flood_vectors, self.radius, self.via, self.gap, &visited, &ends)
e := make(sort_points, 0, len(ends))
end_nodes := &e
for _, node := range ends {
end_nodes = insert_sort_point(end_nodes, node, float32(self.pcb.get_node(node)))
}
e = *end_nodes
path, success := self.backtrack_path(&visited, e[0].node, self.radius, self.via, self.gap)
self.pcb.unmark_distances()
if !success {
self.remove()
return false
}
for _, node := range path {
visited[*node] = true
}
self.paths = append(self.paths, path)
}
self.paths = self.optimise_paths(self.paths[:])
self.add_paths_collision_lines()
self.add_terminal_collision_lines()
return true
}
//output net, terminals and paths, for viewer app
func (self *net) print_net() {
scale := 1.0 / float32(self.pcb.resolution)
fmt.Print("[", self.radius*scale, ",", self.via*scale, ",", self.gap*scale, ",[")
for i, t := range self.terminals {
fmt.Print("(", t.Radius*scale, ",", t.Gap*scale, ",(", t.Term.X*scale, ",", t.Term.Y*scale, ",", t.Term.Z, "),[")
for j, c := range t.Shape {
fmt.Print("(", c.X*scale, ",", c.Y*scale, ")")
if j != (len(t.Shape) - 1) {
fmt.Print(",")
}
}
fmt.Print("])")
if i != (len(self.terminals) - 1) {
fmt.Print(",")
}
}
fmt.Print("],[")
for i, path := range self.paths {
fmt.Print("[")
for j, p := range path {
psp := self.pcb.grid_to_space_point(p)
sp := *psp
fmt.Print("(", sp[0]*scale, ",", sp[1]*scale, ",", sp[2], ")")
if j != (len(path) - 1) {
fmt.Print(",")
}
}
fmt.Print("]")
if i != (len(self.paths) - 1) {
fmt.Print(",")
}
}
fmt.Println("]]")
return
}