Add upstream Go files to convert .dsn to .pcb
parent
e7d7434186
commit
8c8a961f10
|
@ -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("]]")
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue