Add upstream Go files to convert .dsn to .pcb

main
jebba 2022-01-15 11:39:40 -07:00
parent e7d7434186
commit 8c8a961f10
4 changed files with 2401 additions and 0 deletions

557
dsn2pcb/dsn2pcb.go 100644
View File

@ -0,0 +1,557 @@
///opt/local/bin/go run
// Copyright (C) 2014 Chris Hinsley.
//package name
package main
//package imports
import (
"./router"
"bufio"
"flag"
"fmt"
"math"
"os"
"strconv"
"strings"
)
type tree struct {
value *string
branches []*tree
}
type point struct {
x float64
y float64
}
type pin struct {
form *string
angle float64
name *string
x float64
y float64
}
type component struct {
name *string
pin_map map[string]*pin
}
type instance struct {
name *string
comp *string
x float64
y float64
side *string
angle float64
}
type rule struct {
radius float64
gap float64
shape *[]point
}
type circuit struct {
via *string
rule *rule
}
func shape_to_cords(shape *[]point, a1, a2 float64) *router.Cords {
cords := router.Cords{}
rads := math.Mod(a1+a2, 2*math.Pi)
s := math.Sin(rads)
c := math.Cos(rads)
for _, p := range *shape {
px := (c*p.x - s*p.y)
py := (s*p.x + c*p.y)
cords = append(cords, &router.Cord{float32(px), float32(py)})
}
return &cords
}
func cords_equal(pc1, pc2 *router.Cords) bool {
if pc1 == pc2 {
return true
}
c1 := *pc1
c2 := *pc2
if len(c1) != len(c2) {
return false
}
for i := 0; i < len(c1); i++ {
if c1[i].X != c2[i].X {
return false
}
if c1[i].Y != c2[i].Y {
return false
}
}
return true
}
func term_equal(pt1, pt2 *router.Terminal) bool {
if pt1 == pt2 {
return true
}
if !cords_equal(&pt1.Shape, &pt2.Shape) {
return false
}
if pt1.Radius != pt2.Radius {
return false
}
if pt1.Gap != pt2.Gap {
return false
}
if pt1.Term != pt2.Term {
return false
}
return true
}
func peek_char(r *bufio.Reader) (byte, bool) {
bytes, err := r.Peek(1)
if err != nil {
//eof
return ' ', true
}
return bytes[0], false
}
func read_char(r *bufio.Reader) (byte, bool) {
b, err := r.ReadByte()
if err != nil {
//eof
return ' ', true
}
return b, false
}
func read_whitespace(r *bufio.Reader) {
for {
b, eof := peek_char(r)
if eof {
return
}
if b != ' ' && b != '\t' && b != '\r' && b != '\n' {
return
}
read_char(r)
}
}
func peek_until(r *bufio.Reader, c byte) {
for {
b, eof := peek_char(r)
if eof {
return
}
if b == c {
return
}
read_char(r)
}
}
func read_until(r *bufio.Reader, c byte) {
for {
b, eof := read_char(r)
if eof {
return
}
if b == c {
return
}
}
}
func read_node_name(r *bufio.Reader) *string {
s := ""
for {
b, eof := peek_char(r)
if eof || b == '\t' || b == '\n' || b == '\r' || b == ' ' || b == ')' {
break
}
s += string(b)
read_char(r)
}
return &s
}
func read_string(r *bufio.Reader) *tree {
t := tree{nil, nil}
s := ""
for {
b, eof := peek_char(r)
if eof || b == '\t' || b == '\n' || b == '\r' || b == ' ' || b == ')' {
break
}
s += string(b)
read_char(r)
}
t.value = &s
return &t
}
func read_quoted_string(r *bufio.Reader) *tree {
t := tree{nil, nil}
s := ""
for {
b, eof := peek_char(r)
if eof || b == '"' {
break
}
s += string(b)
read_char(r)
}
t.value = &s
return &t
}
func read_tree(r *bufio.Reader) *tree {
read_until(r, '(')
read_whitespace(r)
t := tree{read_node_name(r), nil}
for {
read_whitespace(r)
b, eof := peek_char(r)
if eof {
break
}
if b == ')' {
read_char(r)
break
}
if b == '(' {
t.branches = append(t.branches, read_tree(r))
continue
}
if b == '"' {
read_char(r)
t.branches = append(t.branches, read_quoted_string(r))
read_char(r)
continue
}
t.branches = append(t.branches, read_string(r))
}
return &t
}
func search_tree(t *tree, s *string) *tree {
if t.value != nil {
if *t.value == *s {
return t
}
}
for _, ct := range t.branches {
st := search_tree(ct, s)
if st != nil {
return st
}
}
return nil
}
func print_tree(t *tree, indent int) {
if t.value != nil {
for i := 0; i < indent; i++ {
fmt.Print(" ")
}
fmt.Print(*t.value, "\n")
}
for _, ct := range t.branches {
print_tree(ct, indent+1)
}
}
func main() {
//command line flags and defaults etc
arg_infile := os.Stdin
var arg_b int
flag.IntVar(&arg_b, "b", 0, "border gap, default 0")
flag.Parse()
//input reader from default stdin or given file
if flag.NArg() > 0 {
//read access
file, err := os.Open(flag.Args()[0])
if err != nil {
os.Exit(1)
}
arg_infile = file
}
reader := bufio.NewReader(arg_infile)
//create tree from input
tree := read_tree(reader)
ss := "structure"
structure_root := search_tree(tree, &ss)
num_layers := 0
minx := 1000000.0
miny := 1000000.0
maxx := -1000000.0
maxy := -1000000.0
for _, structure_node := range structure_root.branches {
if *structure_node.value == "layer" {
num_layers++
}
if *structure_node.value == "boundary" {
for _, boundary_node := range structure_node.branches {
if *boundary_node.value == "path" {
cords := boundary_node.branches[2:]
for i := 0; i < len(cords); i += 2 {
px, _ := strconv.ParseFloat(*cords[i].value, 32)
py, _ := strconv.ParseFloat(*cords[i+1].value, 32)
px /= 1000.0
py /= -1000.0
if px < minx {
minx = px
}
if px > maxx {
maxx = px
}
if py < miny {
miny = py
}
if py > maxy {
maxy = py
}
}
}
}
}
}
ss = "library"
library_root := search_tree(tree, &ss)
component_map := map[string]*component{}
rule_map := map[string]*rule{}
for _, library_node := range library_root.branches {
if *library_node.value == "image" {
component_name := library_node.branches[0].value
component := component{component_name, map[string]*pin{}}
for _, image_node := range library_node.branches[1:] {
if *image_node.value == "pin" {
pin := pin{}
pin.form = image_node.branches[0].value
if *image_node.branches[1].value == "rotate" {
pin.angle, _ = strconv.ParseFloat(*image_node.branches[1].branches[0].value, 32)
pin.name = image_node.branches[2].value
pin.x, _ = strconv.ParseFloat(*image_node.branches[3].value, 32)
pin.y, _ = strconv.ParseFloat(*image_node.branches[4].value, 32)
pin.angle = pin.angle * (math.Pi / 180.0)
} else {
pin.angle = 0.0
pin.name = image_node.branches[1].value
pin.x, _ = strconv.ParseFloat(*image_node.branches[2].value, 32)
pin.y, _ = strconv.ParseFloat(*image_node.branches[3].value, 32)
}
pin.x /= 1000.0
pin.y /= -1000.0
component.pin_map[*pin.name] = &pin
}
}
component_map[*component_name] = &component
}
if *library_node.value == "padstack" {
for _, padstack_node := range library_node.branches[1:] {
if *padstack_node.value == "shape" {
points := []point{}
rule := rule{0.5, 0.125, nil}
if *padstack_node.branches[0].value == "circle" {
rule.radius, _ = strconv.ParseFloat(*padstack_node.branches[0].branches[1].value, 32)
rule.radius /= 2000.0
}
if *padstack_node.branches[0].value == "path" {
rule.radius, _ = strconv.ParseFloat(*padstack_node.branches[0].branches[1].value, 32)
rule.radius /= 2000.0
x1, _ := strconv.ParseFloat(*padstack_node.branches[0].branches[2].value, 32)
y1, _ := strconv.ParseFloat(*padstack_node.branches[0].branches[3].value, 32)
x2, _ := strconv.ParseFloat(*padstack_node.branches[0].branches[4].value, 32)
y2, _ := strconv.ParseFloat(*padstack_node.branches[0].branches[5].value, 32)
x1 /= 1000.0
y1 /= -1000.0
x2 /= 1000.0
y2 /= -1000.0
points = append(points, point{x1, y1})
points = append(points, point{x2, y2})
}
if *library_node.branches[1].branches[0].value == "rect" {
rule.radius = 0.0
x1, _ := strconv.ParseFloat(*padstack_node.branches[0].branches[1].value, 32)
y1, _ := strconv.ParseFloat(*padstack_node.branches[0].branches[2].value, 32)
x2, _ := strconv.ParseFloat(*padstack_node.branches[0].branches[3].value, 32)
y2, _ := strconv.ParseFloat(*padstack_node.branches[0].branches[4].value, 32)
x1 /= 1000.0
y1 /= -1000.0
x2 /= 1000.0
y2 /= -1000.0
points = append(points, point{x1, y1})
points = append(points, point{x2, y1})
points = append(points, point{x2, y2})
points = append(points, point{x1, y2})
points = append(points, point{x1, y1})
}
rule.shape = &points
rule_map[*library_node.branches[0].value] = &rule
}
}
}
}
ss = "placement"
placement_root := search_tree(tree, &ss)
instance_map := map[string]*instance{}
for _, placement_node := range placement_root.branches {
if *placement_node.value == "component" {
component_name := placement_node.branches[0].value
for _, component_node := range placement_node.branches[1:] {
if *component_node.value == "place" {
instance := instance{}
instance_name := component_node.branches[0].value
instance.name = instance_name
instance.comp = component_name
instance.x, _ = strconv.ParseFloat(*component_node.branches[1].value, 32)
instance.y, _ = strconv.ParseFloat(*component_node.branches[2].value, 32)
instance.side = component_node.branches[3].value
instance.angle, _ = strconv.ParseFloat(*component_node.branches[4].value, 32)
instance.angle = instance.angle * (math.Pi / 180.0)
instance.x /= 1000.0
instance.y /= -1000.0
instance_map[*instance_name] = &instance
}
}
}
}
all_terminals := router.Terminals{}
for _, instance := range instance_map {
component := component_map[*instance.comp]
for _, pin := range component.pin_map {
x := pin.x
y := pin.y
if *instance.side != "front" {
x = -x
}
s := math.Sin(instance.angle)
c := math.Cos(instance.angle)
px := (c*x - s*y) + instance.x
py := (s*x + c*y) + instance.y
pin_rule := rule_map[*pin.form]
tp := router.Tpoint{float32(px), float32(py), 0.0}
cords := shape_to_cords(pin_rule.shape, pin.angle, instance.angle)
all_terminals = append(all_terminals, &router.Terminal{float32(pin_rule.radius), float32(pin_rule.gap), tp, *cords})
if px < minx {
minx = px
}
if px > maxx {
maxx = px
}
if py < miny {
miny = py
}
if py > maxy {
maxy = py
}
}
}
ss = "network"
network_root := search_tree(tree, &ss)
circuit_map := map[string]*circuit{}
for _, network_node := range network_root.branches {
if *network_node.value == "class" {
net_rule := rule{0.125, 0.125, nil}
circuit := circuit{nil, &net_rule}
for _, class_node := range network_node.branches {
if *class_node.value == "rule" {
for _, dims := range class_node.branches {
if *dims.value == "width" {
net_rule.radius, _ = strconv.ParseFloat(*dims.branches[0].value, 32)
net_rule.radius /= 2000.0
}
if *dims.value == "clearance" {
net_rule.gap, _ = strconv.ParseFloat(*dims.branches[0].value, 32)
net_rule.gap /= 2000.0
}
}
}
if *class_node.value == "circuit" {
for _, circuit_node := range class_node.branches {
if *circuit_node.value == "use_via" {
circuit.via = circuit_node.branches[0].value
}
}
}
}
for _, netname := range network_node.branches {
if netname.branches == nil {
circuit_map[*netname.value] = &circuit
}
}
}
}
tracks := make([]router.Track, 0)
for _, network_node := range network_root.branches {
if *network_node.value == "net" {
terminals := router.Terminals{}
for _, pin := range network_node.branches[1].branches {
pin_info := strings.Split(*pin.value, "-")
instance_name := pin_info[0]
pin_name := pin_info[1]
instance := instance_map[instance_name]
component := component_map[*instance.comp]
pin := component.pin_map[pin_name]
x := pin.x
y := pin.y
if *instance.side != "front" {
x = -x
}
s := math.Sin(instance.angle)
c := math.Cos(instance.angle)
px := (c*x - s*y) + instance.x
py := (s*x + c*y) + instance.y
pin_rule := rule_map[*pin.form]
tp := router.Tpoint{float32(px), float32(py), 0.0}
cords := shape_to_cords(pin_rule.shape, pin.angle, instance.angle)
term := router.Terminal{float32(pin_rule.radius), float32(pin_rule.gap), tp, *cords}
terminals = append(terminals, &term)
for i, t := range all_terminals {
if term_equal(t, &term) {
all_terminals = append(all_terminals[:i], all_terminals[i+1:]...)
break
}
}
}
circuit := circuit_map[*network_node.branches[0].value]
net_rule := circuit.rule
via_rule := rule_map[*circuit.via]
tracks = append(tracks, router.Track{float32(net_rule.radius), float32(via_rule.radius), float32(net_rule.gap), terminals})
}
}
tracks = append(tracks, router.Track{0.0, 0.0, 0.0, all_terminals})
border := float64(arg_b)
fmt.Print("[", int(maxx-minx+(border*2)+0.5), ",", int(maxy-miny+(border*2)+0.5), ",", num_layers, "]\n")
for _, track := range tracks {
fmt.Print("[", track.Radius, ",", track.Via, ",", track.Gap, ",[")
for i, term := range track.Terms {
fmt.Print("(", term.Radius, ",", term.Gap, ",(", term.Term.X-float32(minx-border), ",",
term.Term.Y-float32(miny-border), ",", term.Term.Z, "),[")
for j, cord := range term.Shape {
fmt.Print("(", cord.X, ",", cord.Y, ")")
if j != (len(term.Shape) - 1) {
fmt.Print(",")
}
}
fmt.Print("])")
if i != (len(track.Terms) - 1) {
fmt.Print(",")
}
}
fmt.Println("]]")
}
}

View File

@ -0,0 +1,275 @@
//package name
package layer
//package imports
import (
"../mymath"
"math"
)
/////////////////////////
//private structure/types
/////////////////////////
type dims struct {
width int
height int
}
type point struct {
x float32
y float32
}
type line struct {
p1 point
p2 point
radius float32
gap float32
}
type record struct {
id int
line line
}
type aabb struct {
minx int
miny int
maxx int
maxy int
}
type bucket []*record
type buckets []bucket
//layer object
type layer struct {
width int
height int
scale float32
buckets buckets
test int
}
//layer methods
/////////////////
//private methods
/////////////////
func newlayer(dims dims, s float32) *layer {
l := layer{}
l.init(dims, s)
return &l
}
func (self *layer) init(dims dims, s float32) {
self.width = dims.width
self.height = dims.height
self.scale = s
self.buckets = make(buckets, (self.width * self.height), (self.width * self.height))
for i := 0; i < (self.width * self.height); i++ {
self.buckets[i] = bucket{}
}
self.test = 0
return
}
func (self *layer) aabb(l *line) *aabb {
x1, y1, x2, y2 := l.p1.x, l.p1.y, l.p2.x, l.p2.y
if x1 > x2 {
x1, x2 = x2, x1
}
if y1 > y2 {
y1, y2 = y2, y1
}
r := l.radius + l.gap
minx := int(math.Floor(float64((x1 - r) * self.scale)))
miny := int(math.Floor(float64((y1 - r) * self.scale)))
maxx := int(math.Ceil(float64((x2 + r) * self.scale)))
maxy := int(math.Ceil(float64((y2 + r) * self.scale)))
if minx < 0 {
minx = 0
}
if miny < 0 {
miny = 0
}
if maxx > self.width {
maxx = self.width
}
if maxy > self.height {
maxy = self.height
}
return &aabb{minx, miny, maxx, maxy}
}
func lines_equal(l1, l2 *line) bool {
if l1.p1.x != l2.p1.x {
return false
}
if l1.p1.y != l2.p1.y {
return false
}
if l1.p2.x != l2.p2.x {
return false
}
if l1.p2.y != l2.p2.y {
return false
}
if l1.radius != l2.radius {
return false
}
if l1.gap != l2.gap {
return false
}
return true
}
func (self *layer) add_line(l *line) {
new_record := record{0, *l}
bb := self.aabb(l)
for y := bb.miny; y < bb.maxy; y++ {
for x := bb.minx; x < bb.maxx; x++ {
b := y*self.width + x
found := false
for _, record := range self.buckets[b] {
if lines_equal(&record.line, l) {
found = true
break
}
}
if !found {
self.buckets[b] = append(self.buckets[b], &new_record)
}
}
}
}
func (self *layer) sub_line(l *line) {
bb := self.aabb(l)
for y := bb.miny; y < bb.maxy; y++ {
for x := bb.minx; x < bb.maxx; x++ {
b := y*self.width + x
for i := len(self.buckets[b]) - 1; i >= 0; i-- {
if lines_equal(&self.buckets[b][i].line, l) {
self.buckets[b] = append(self.buckets[b][:i], self.buckets[b][i+1:]...)
}
}
}
}
}
func (self *layer) hit_line(l *line) bool {
self.test += 1
bb := self.aabb(l)
l1_p1 := mymath.Point{l.p1.x, l.p1.y}
l1_p2 := mymath.Point{l.p2.x, l.p2.y}
l2_p1 := mymath.Point{0.0, 0.0}
l2_p2 := mymath.Point{0.0, 0.0}
for y := bb.miny; y < bb.maxy; y++ {
for x := bb.minx; x < bb.maxx; x++ {
for _, record := range self.buckets[y*self.width+x] {
if record.id != self.test {
record.id = self.test
r := l.radius + record.line.radius
if l.gap >= record.line.gap {
r += l.gap
} else {
r += record.line.gap
}
l2_p1[0], l2_p1[1] = record.line.p1.x, record.line.p1.y
l2_p2[0], l2_p2[1] = record.line.p2.x, record.line.p2.y
if mymath.Collide_thick_lines_2d(&l1_p1, &l1_p2, &l2_p1, &l2_p2, r) {
return true
}
}
}
}
}
return false
}
/////////////////////////
//public structures/types
/////////////////////////
type Dims struct {
Width int
Height int
Depth int
}
//layers object
type Layers struct {
depth int
layers []*layer
}
//layers methods
////////////////
//public methods
////////////////
func NewLayers(dims Dims, s float32) *Layers {
l := Layers{}
l.Init(dims, s)
return &l
}
func (self *Layers) Init(dm Dims, s float32) *Layers {
width := dm.Width
height := dm.Height
self.depth = dm.Depth
self.layers = make([]*layer, self.depth, self.depth)
for z := 0; z < self.depth; z++ {
self.layers[z] = newlayer(dims{width, height}, s)
}
return self
}
func (self *Layers) Add_line(pp1, pp2 *mymath.Point, r, g float32) {
p1 := *pp1
p2 := *pp2
z1 := int(p1[2])
z2 := int(p2[2])
if z1 > z2 {
z1, z2 = z2, z1
}
line := line{point{p1[0], p1[1]}, point{p2[0], p2[1]}, r, g}
for z := z1; z <= z2; z++ {
self.layers[z].add_line(&line)
}
}
func (self *Layers) Sub_line(pp1, pp2 *mymath.Point, r, g float32) {
p1 := *pp1
p2 := *pp2
z1 := int(p1[2])
z2 := int(p2[2])
if z1 > z2 {
z1, z2 = z2, z1
}
line := line{point{p1[0], p1[1]}, point{p2[0], p2[1]}, r, g}
for z := z1; z <= z2; z++ {
self.layers[z].sub_line(&line)
}
}
func (self *Layers) Hit_line(pp1, pp2 *mymath.Point, r, g float32) bool {
p1 := *pp1
p2 := *pp2
z1 := int(p1[2])
z2 := int(p2[2])
if z1 > z2 {
z1, z2 = z2, z1
}
line := line{point{p1[0], p1[1]}, point{p2[0], p2[1]}, r, g}
for z := z1; z <= z2; z++ {
if self.layers[z].hit_line(&line) {
return true
}
}
return false
}

View File

@ -0,0 +1,711 @@
// Copyright (C) 2014 Chris Hinsley.
//package name
package mymath
//package imports
import (
"math"
"math/rand"
)
/////////////////////////
//public structures/types
/////////////////////////
type Point []float32
type Points []*Point
//////////////////
//public functions
//////////////////
///////////////////////////////
//generic distance metric stuff
///////////////////////////////
func Manhattan_distance(pp1, pp2 *Point) float32 {
p1 := *pp1
p2 := *pp2
d := float32(0.0)
for i := 0; i < len(p1); i++ {
d += float32(math.Abs(float64(p1[i] - p2[i])))
}
return d
}
func Euclidean_distance(pp1, pp2 *Point) float32 {
p1 := *pp1
p2 := *pp2
d := float32(0.0)
for i := 0; i < len(p1); i++ {
ps := p1[i] - p2[i]
d += ps * ps
}
return float32(math.Sqrt(float64(d)))
}
func Squared_euclidean_distance(pp1, pp2 *Point) float32 {
p1 := *pp1
p2 := *pp2
d := float32(0.0)
for i := 0; i < len(p1); i++ {
ps := p1[i] - p2[i]
d += ps * ps
}
return d
}
func Chebyshev_distance(pp1, pp2 *Point) float32 {
p1 := *pp1
p2 := *pp2
d := float32(0.0)
for i := 0; i < len(p1); i++ {
d1 := float32(math.Abs(float64(p1[i] - p2[i])))
if d1 > d {
d = d1
}
}
return d
}
func Reciprical_distance(pp1, pp2 *Point) float32 {
d := Manhattan_distance(pp1, pp2)
if d == 0.0 {
return 1.0
}
return 1.0 / d
}
func Random_distance(pp1, pp2 *Point) float32 {
return float32(rand.Float32())
}
///////////////////////
//generic vector stuff
///////////////////////
func Sign(x int) int {
if x == 0 {
return 0
}
if x < 0 {
return -1
}
return 1
}
func Equal(pp1, pp2 *Point) bool {
return Manhattan_distance(pp1, pp2) == 0.0
}
func Add(pp1, pp2 *Point) *Point {
p1 := *pp1
p2 := *pp2
p := make(Point, len(p1), len(p1))
for i := 0; i < len(p1); i++ {
p[i] = p1[i] + p2[i]
}
return &p
}
func Sub(pp1, pp2 *Point) *Point {
p1 := *pp1
p2 := *pp2
p := make(Point, len(p1), len(p1))
for i := 0; i < len(p1); i++ {
p[i] = p1[i] - p2[i]
}
return &p
}
func Scale(pp *Point, s float32) *Point {
p := *pp
sp := make(Point, len(p), len(p))
for i := 0; i < len(p); i++ {
sp[i] = p[i] * s
}
return &sp
}
func Dot(pp1, pp2 *Point) float32 {
p1 := *pp1
p2 := *pp2
d := float32(0.0)
for i := 0; i < len(p1); i++ {
d += p1[i] * p2[i]
}
return d
}
func Length(pp *Point) float32 {
return float32(math.Sqrt(float64(Dot(pp, pp))))
}
func Distance(pp1, pp2 *Point) float32 {
return Length(Sub(pp2, pp1))
}
func Distance_squared(pp1, pp2 *Point) float32 {
pp := Sub(pp2, pp1)
return Dot(pp, pp)
}
func Norm(pp *Point) *Point {
l := Length(pp)
if l == 0.0 {
return Scale(pp, 0.0)
}
return Scale(pp, 1.0/l)
}
func Distance_to_line(pp, pp1, pp2 *Point) float32 {
lv := Sub(pp2, pp1)
pv := Sub(pp, pp1)
c1 := Dot(pv, lv)
if c1 <= 0 {
return Distance(pp, pp1)
}
c2 := Dot(lv, lv)
if c2 <= c1 {
return Distance(pp, pp2)
}
return Distance(pp, Add(pp1, Scale(lv, c1/c2)))
}
func Distance_squared_to_line(pp, pp1, pp2 *Point) float32 {
lv := Sub(pp2, pp1)
pv := Sub(pp, pp1)
c1 := Dot(pv, lv)
if c1 <= 0 {
return Distance_squared(pp, pp1)
}
c2 := Dot(lv, lv)
if c2 <= c1 {
return Distance_squared(pp, pp2)
}
return Distance_squared(pp, Add(pp1, Scale(lv, c1/c2)))
}
///////////////////////
//specific vector stuff
///////////////////////
func Equal_2d(pp1, pp2 *Point) bool {
p1 := *pp1
p2 := *pp2
if p1[0] != p2[0] {
return false
}
if p1[1] != p2[1] {
return false
}
return true
}
func Equal_3d(pp1, pp2 *Point) bool {
p1 := *pp1
p2 := *pp2
if p1[0] != p2[0] {
return false
}
if p1[1] != p2[1] {
return false
}
if p1[2] != p2[2] {
return false
}
return true
}
func Add_2d(pp1, pp2 *Point) *Point {
p1 := *pp1
p2 := *pp2
return &Point{p1[0] + p2[0], p1[1] + p2[1]}
}
func Add_3d(pp1, pp2 *Point) *Point {
p1 := *pp1
p2 := *pp2
return &Point{p1[0] + p2[0], p1[1] + p2[1], p1[2] + p2[2]}
}
func Sub_2d(pp1, pp2 *Point) *Point {
p1 := *pp1
p2 := *pp2
return &Point{p1[0] - p2[0], p1[1] - p2[1]}
}
func Sub_3d(pp1, pp2 *Point) *Point {
p1 := *pp1
p2 := *pp2
return &Point{p1[0] - p2[0], p1[1] - p2[1], p1[2] - p2[2]}
}
func Scale_2d(pp *Point, s float32) *Point {
p := *pp
return &Point{p[0] * s, p[1] * s}
}
func Scale_3d(pp *Point, s float32) *Point {
p := *pp
return &Point{p[0] * s, p[1] * s, p[2] * s}
}
func Perp_2d(pp *Point) *Point {
p := *pp
return &Point{p[1], -p[0]}
}
func Cross_3d(pp1, pp2 *Point) *Point {
p1 := *pp1
p2 := *pp2
return &Point{p1[1]*p2[2] - p1[2]*p2[1], p1[2]*p2[0] - p1[0]*p2[2], p1[0]*p2[1] - p1[1]*p2[0]}
}
func Dot_2d(pp1, pp2 *Point) float32 {
p1 := *pp1
p2 := *pp2
return p1[0]*p2[0] + p1[1]*p2[1]
}
func Dot_3d(pp1, pp2 *Point) float32 {
p1 := *pp1
p2 := *pp2
return p1[0]*p2[0] + p1[1]*p2[1] + p1[2]*p2[2]
}
func Length_2d(pp *Point) float32 {
return float32(math.Sqrt(float64(Dot_2d(pp, pp))))
}
func Length_3d(pp *Point) float32 {
return float32(math.Sqrt(float64(Dot_3d(pp, pp))))
}
func Norm_2d(pp *Point) *Point {
l := Length_2d(pp)
if l == 0 {
return &Point{0, 0}
}
p := *pp
return &Point{p[0] / l, p[1] / l}
}
func Norm_3d(pp *Point) *Point {
l := Length_3d(pp)
if l == 0 {
return &Point{0, 0, 0}
}
p := *pp
return &Point{p[0] / l, p[1] / l, p[2] / l}
}
func Distance_2d(pp1, pp2 *Point) float32 {
return Length_2d(Sub_2d(pp2, pp1))
}
func Distance_3d(pp1, pp2 *Point) float32 {
return Length_3d(Sub_3d(pp2, pp1))
}
func Distance_squared_2d(pp1, pp2 *Point) float32 {
pp := Sub_2d(pp2, pp1)
return Dot_2d(pp, pp)
}
func Distance_squared_3d(pp1, pp2 *Point) float32 {
pp := Sub_3d(pp2, pp1)
return Dot_3d(pp, pp)
}
func Distance_to_line_2d(pp, pp1, pp2 *Point) float32 {
lv := Sub_2d(pp2, pp1)
pv := Sub_2d(pp, pp1)
c1 := Dot_2d(pv, lv)
if c1 <= 0 {
return Distance_2d(pp, pp1)
}
c2 := Dot_2d(lv, lv)
if c2 <= c1 {
return Distance_2d(pp, pp2)
}
return Distance_2d(pp, Add_2d(pp1, Scale_2d(lv, c1/c2)))
}
func Distance_to_line_3d(pp, pp1, pp2 *Point) float32 {
lv := Sub_3d(pp2, pp1)
pv := Sub_3d(pp, pp1)
c1 := Dot_3d(pv, lv)
if c1 <= 0 {
return Distance_3d(pp, pp1)
}
c2 := Dot_3d(lv, lv)
if c2 <= c1 {
return Distance_3d(pp, pp2)
}
return Distance_3d(pp, Add_3d(pp1, Scale_3d(lv, c1/c2)))
}
func Distance_squared_to_line_2d(pp, pp1, pp2 *Point) float32 {
lv := Sub_2d(pp2, pp1)
pv := Sub_2d(pp, pp1)
c1 := Dot_2d(pv, lv)
if c1 <= 0 {
return Distance_squared_2d(pp, pp1)
}
c2 := Dot_2d(lv, lv)
if c2 <= c1 {
return Distance_squared_2d(pp, pp2)
}
return Distance_squared_2d(pp, Add_2d(pp1, Scale_2d(lv, c1/c2)))
}
func Distance_squared_to_line_3d(pp, pp1, pp2 *Point) float32 {
lv := Sub_3d(pp2, pp1)
pv := Sub_3d(pp, pp1)
c1 := Dot_3d(pv, lv)
if c1 <= 0 {
return Distance_squared_3d(pp, pp1)
}
c2 := Dot_3d(lv, lv)
if c2 <= c1 {
return Distance_squared_3d(pp, pp2)
}
return Distance_squared_3d(pp, Add_3d(pp1, Scale_3d(lv, c1/c2)))
}
func Collide_lines_2d(pl1_p1, pl1_p2, pl2_p1, pl2_p2 *Point) bool {
l1_p1 := *pl1_p1
l1_p2 := *pl1_p2
l2_p1 := *pl2_p1
l2_p2 := *pl2_p2
l1_x1, l1_y1 := l1_p1[0], l1_p1[1]
l1_x2, l1_y2 := l1_p2[0], l1_p2[1]
l2_x1, l2_y1 := l2_p1[0], l2_p1[1]
l2_x2, l2_y2 := l2_p2[0], l2_p2[1]
ax := l1_x2 - l1_x1
ay := l1_y2 - l1_y1
bx := l2_x1 - l2_x2
by := l2_y1 - l2_y2
cx := l1_x1 - l2_x1
cy := l1_y1 - l2_y1
an := by*cx - bx*cy
ad := ay*bx - ax*by
bn := ax*cy - ay*cx
bd := ay*bx - ax*by
if (ad == 0) || (bd == 0) {
return false
} else {
if ad > 0 {
if (an < 0) || (an > ad) {
return false
}
} else {
if (an > 0) || (an < ad) {
return false
}
}
if bd > 0 {
if (bn < 0) || (bn > bd) {
return false
}
} else {
if (bn > 0) || (bn < bd) {
return false
}
}
}
return true
}
func Collide_thick_lines_2d(tl1_p1, tl1_p2, tl2_p1, tl2_p2 *Point, r float32) bool {
if Collide_lines_2d(tl1_p1, tl1_p2, tl2_p1, tl2_p2) {
return true
}
r *= r
if Distance_squared_to_line_2d(tl2_p1, tl1_p1, tl1_p2) <= r {
return true
}
if Distance_squared_to_line_2d(tl2_p2, tl1_p1, tl1_p2) <= r {
return true
}
if Distance_squared_to_line_2d(tl1_p1, tl2_p1, tl2_p2) <= r {
return true
}
if Distance_squared_to_line_2d(tl1_p2, tl2_p1, tl2_p2) <= r {
return true
}
return false
}
////////////////////
//generic path stuff
////////////////////
func Circle_as_lines(p *Point, radius float32, resolution int) *Points {
out_points := make(Points, resolution+1, resolution+1)
rvx, rvy := float32(0.0), radius
for i := 0; i <= resolution; i++ {
angle := float64((float32(i) * math.Pi * 2.0) / float32(resolution))
s := float32(math.Sin(angle))
c := float32(math.Cos(angle))
rv := &Point{rvx*c - rvy*s, rvx*s + rvy*c}
out_points[i] = Sub_2d(p, rv)
}
out_points[resolution] = out_points[0]
return &out_points
}
func Circle_as_tristrip(p *Point, radius1, radius2 float32, resolution int) *Points {
out_points := make(Points, resolution*2+2, resolution*2+2)
rvx1, rvy1 := float32(0.0), radius1
rvx2, rvy2 := float32(0.0), radius2
for i := 0; i <= resolution; i++ {
angle := float64((float32(i) * math.Pi * 2.0) / float32(resolution))
s := float32(math.Sin(angle))
c := float32(math.Cos(angle))
rv1 := &Point{rvx1*c - rvy1*s, rvx1*s + rvy1*c}
rv2 := &Point{rvx2*c - rvy2*s, rvx2*s + rvy2*c}
out_points[i*2] = Sub_2d(p, rv1)
out_points[i*2+1] = Sub_2d(p, rv2)
}
out_points[resolution*2] = out_points[0]
out_points[resolution*2+1] = out_points[1]
return &out_points
}
func Thicken_path_as_lines(pathp *Points, radius float32, capstyle, joinstyle, resolution int) *Points {
if radius == 0.0 {
radius = 0.00000001
}
path := *pathp
index := 0
step := 1
out_points := Points{}
for {
p1 := path[index]
index += step
p2 := path[index]
index += step
l2_v := Sub_2d(p2, p1)
l2_pv := Perp_2d(l2_v)
l2_npv := Norm_2d(l2_pv)
rv := Scale_2d(l2_npv, radius)
switch {
case capstyle == 0:
//butt cap
out_points = append(out_points, Sub_2d(p1, rv))
out_points = append(out_points, Add_2d(p1, rv))
case capstyle == 1:
//square cap
p0 := Add_2d(p1, Perp_2d(rv))
out_points = append(out_points, Sub_2d(p0, rv))
out_points = append(out_points, Add_2d(p0, rv))
case capstyle == 2:
//triangle cap
out_points = append(out_points, Sub_2d(p1, rv))
out_points = append(out_points, Add_2d(p1, Perp_2d(rv)))
out_points = append(out_points, Add_2d(p1, rv))
default:
//round cap
rvd := *rv
rvx, rvy := rvd[0], rvd[1]
for i := 0; i <= resolution; i++ {
angle := float64((float32(i) * math.Pi) / float32(resolution))
s := float32(math.Sin(angle))
c := float32(math.Cos(angle))
rv := &Point{rvx*c - rvy*s, rvx*s + rvy*c}
out_points = append(out_points, Sub_2d(p1, rv))
}
}
for (index != -1) && (index != len(path)) {
p1, l1_v, l1_npv := p2, l2_v, l2_npv
p2 = path[index]
index += step
l2_v = Sub_2d(p2, p1)
l2_pv = Perp_2d(l2_v)
l2_npv = Norm_2d(l2_pv)
nbv := Norm_2d(Scale_2d(Add_2d(l1_npv, l2_npv), 0.5))
c := Dot_2d(nbv, Norm_2d(l1_v))
switch {
case (c <= 0) || (joinstyle == 0):
//mitre join
s := float32(math.Sin(math.Acos(float64(c))))
bv := Scale_2d(nbv, radius/s)
out_points = append(out_points, Add_2d(p1, bv))
case joinstyle == 1:
//bevel join
out_points = append(out_points, Add_2d(p1, Scale_2d(l1_npv, radius)))
out_points = append(out_points, Add_2d(p1, Scale_2d(l2_npv, radius)))
default:
//round join
rv := Scale_2d(l1_npv, radius)
rvd := *rv
rvx, rvy := rvd[0], rvd[1]
theta := float32(math.Acos(float64(Dot_2d(l1_npv, l2_npv))))
segs := int((theta/float32(math.Pi))*float32(resolution)) + 1
for i := 0; i <= segs; i++ {
angle := float64((float32(i) * theta) / float32(segs))
s := float32(math.Sin(angle))
c := float32(math.Cos(angle))
rv := &Point{rvx*c - rvy*s, rvx*s + rvy*c}
out_points = append(out_points, Add_2d(p1, rv))
}
}
}
if step < 0 {
break
}
step = -step
index += step
}
out_points = append(out_points, out_points[0])
return &out_points
}
func Thicken_path_as_tristrip(pathp *Points, radius float32, capstyle, joinstyle, resolution int) *Points {
if radius == 0.0 {
radius = 0.00000001
}
path := *pathp
index := 0
step := 1
out_points := Points{}
for {
p1 := path[index]
index += step
p2 := path[index]
index += step
l2_v := Sub_2d(p2, p1)
l2_pv := Perp_2d(l2_v)
l2_npv := Norm_2d(l2_pv)
rv := Scale_2d(l2_npv, radius)
switch {
case capstyle == 0:
//butt cap
out_points = append(out_points, p1)
out_points = append(out_points, Sub_2d(p1, rv))
out_points = append(out_points, p1)
out_points = append(out_points, Add_2d(p1, rv))
case capstyle == 1:
//square cap
p0 := Add_2d(p1, Perp_2d(rv))
out_points = append(out_points, p0)
out_points = append(out_points, Sub_2d(p0, rv))
out_points = append(out_points, p0)
out_points = append(out_points, Add_2d(p0, rv))
case capstyle == 2:
//triangle cap
out_points = append(out_points, p1)
out_points = append(out_points, Sub_2d(p1, rv))
out_points = append(out_points, p1)
out_points = append(out_points, Add_2d(p1, Perp_2d(rv)))
out_points = append(out_points, p1)
out_points = append(out_points, Add_2d(p1, rv))
default:
//round cap
rvd := *rv
rvx, rvy := rvd[0], rvd[1]
for i := 0; i <= resolution; i++ {
angle := float64((float32(i) * math.Pi) / float32(resolution))
s := float32(math.Sin(angle))
c := float32(math.Cos(angle))
rv := &Point{rvx*c - rvy*s, rvx*s + rvy*c}
out_points = append(out_points, p1)
out_points = append(out_points, Sub_2d(p1, rv))
}
}
for (index != -1) && (index != len(path)) {
p1, l1_v, l1_npv := p2, l2_v, l2_npv
p2 = path[index]
index += step
l2_v = Sub_2d(p2, p1)
l2_pv = Perp_2d(l2_v)
l2_npv = Norm_2d(l2_pv)
nbv := Norm_2d(Scale_2d(Add_2d(l1_npv, l2_npv), 0.5))
c := Dot_2d(nbv, Norm_2d(l1_v))
switch {
case (c <= 0) || (joinstyle == 0):
//mitre join
s := float32(math.Sin(math.Acos(float64(c))))
bv := Scale_2d(nbv, radius/s)
out_points = append(out_points, p1)
out_points = append(out_points, Add_2d(p1, bv))
case joinstyle == 1:
//bevel join
out_points = append(out_points, p1)
out_points = append(out_points, Add_2d(p1, Scale_2d(l1_npv, radius)))
out_points = append(out_points, p1)
out_points = append(out_points, Add_2d(p1, Scale_2d(l2_npv, radius)))
default:
//round join
rv := Scale_2d(l1_npv, radius)
rvd := *rv
rvx, rvy := rvd[0], rvd[1]
theta := float32(math.Acos(float64(Dot_2d(l1_npv, l2_npv))))
segs := int((theta/float32(math.Pi))*float32(resolution)) + 1
for i := 0; i <= segs; i++ {
angle := float64((float32(i) * theta) / float32(segs))
s := float32(math.Sin(angle))
c := float32(math.Cos(angle))
rv := &Point{rvx*c - rvy*s, rvx*s + rvy*c}
out_points = append(out_points, p1)
out_points = append(out_points, Add_2d(p1, rv))
}
}
}
if step < 0 {
break
}
step = -step
index += step
}
out_points = append(out_points, out_points[0])
out_points = append(out_points, out_points[1])
return &out_points
}
func recursive_bezier(x1, y1, x2, y2, x3, y3, x4, y4 float32, pointsp *Points, distance_tolerance float32) *Points {
//calculate all the mid-points of the line segments
x12 := (x1 + x2) * 0.5
y12 := (y1 + y2) * 0.5
x23 := (x2 + x3) * 0.5
y23 := (y2 + y3) * 0.5
x34 := (x3 + x4) * 0.5
y34 := (y3 + y4) * 0.5
x123 := (x12 + x23) * 0.5
y123 := (y12 + y23) * 0.5
x234 := (x23 + x34) * 0.5
y234 := (y23 + y34) * 0.5
x1234 := (x123 + x234) * 0.5
y1234 := (y123 + y234) * 0.5
//try to approximate the full cubic curve by a single straight line
dx := x4 - x1
dy := y4 - y1
d2 := float32(math.Abs(float64(((x2-x4)*dy - (y2-y4)*dx))))
d3 := float32(math.Abs(float64(((x3-x4)*dy - (y3-y4)*dx))))
points := *pointsp
if (d2+d3)*(d2+d3) < distance_tolerance*(dx*dx+dy*dy) {
points = append(points, &Point{x1234, y1234})
return &points
}
//continue subdivision
points = *recursive_bezier(x1, y1, x12, y12, x123, y123, x1234, y1234, &points, distance_tolerance)
points = *recursive_bezier(x1234, y1234, x234, y234, x34, y34, x4, y4, &points, distance_tolerance)
return &points
}
//create bezier path
func Bezier_path_as_lines(pp1, pp2, pp3, pp4 *Point, distance_tolerance float32) *Points {
p1, p2, p3, p4 := *pp1, *pp2, *pp3, *pp4
points := Points{}
points = append(points, &Point{p1[0], p1[1]})
points = *recursive_bezier(p1[0], p1[1], p2[0], p2[1], p3[0], p3[1], p4[0], p4[1], &points, distance_tolerance)
points = append(points, &Point{p4[0], p4[1]})
return &points
}

View File

@ -0,0 +1,858 @@
// 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
}