From 8c8a961f10926a06ae3caccd7b76222f2fca4930 Mon Sep 17 00:00:00 2001 From: jebba Date: Sat, 15 Jan 2022 11:39:40 -0700 Subject: [PATCH] Add upstream Go files to convert .dsn to .pcb --- dsn2pcb/dsn2pcb.go | 557 +++++++++++++++++++++++++ dsn2pcb/layer/layer.go | 275 +++++++++++++ dsn2pcb/mymath/mymath.go | 711 ++++++++++++++++++++++++++++++++ dsn2pcb/router/router.go | 858 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 2401 insertions(+) create mode 100644 dsn2pcb/dsn2pcb.go create mode 100644 dsn2pcb/layer/layer.go create mode 100644 dsn2pcb/mymath/mymath.go create mode 100644 dsn2pcb/router/router.go diff --git a/dsn2pcb/dsn2pcb.go b/dsn2pcb/dsn2pcb.go new file mode 100644 index 0000000..3d85dcb --- /dev/null +++ b/dsn2pcb/dsn2pcb.go @@ -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("]]") + } +} diff --git a/dsn2pcb/layer/layer.go b/dsn2pcb/layer/layer.go new file mode 100644 index 0000000..5fbd6c3 --- /dev/null +++ b/dsn2pcb/layer/layer.go @@ -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 +} diff --git a/dsn2pcb/mymath/mymath.go b/dsn2pcb/mymath/mymath.go new file mode 100644 index 0000000..457dfe0 --- /dev/null +++ b/dsn2pcb/mymath/mymath.go @@ -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 +} diff --git a/dsn2pcb/router/router.go b/dsn2pcb/router/router.go new file mode 100644 index 0000000..96464f1 --- /dev/null +++ b/dsn2pcb/router/router.go @@ -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 +}