Skip to content

Commit

Permalink
split and drawing are good, but splines are still TODO
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonathan Zheng authored and Jonathan Zheng committed May 12, 2019
1 parent 4435653 commit 644001d
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 61 deletions.
248 changes: 195 additions & 53 deletions pconfluent.py
@@ -1,24 +1,18 @@
# import power as cpp
import pgd as cpp
import numpy as np

class routing_node:
def __init__(self, idx):
self.idx = idx
self.incoming = []
self.outgoing = []
self.power = []

def add_child(self, child):
self.outgoing.append(child)
child.incoming.append(self)

def add_poweredge(self, neighbour):
self.power.append(neighbour)
neighbour.power.append(self)
self.parent = None
self.children = []
self.pout = []
self.pin = []
self.split = False

def init_routing_nodes(Ir, Jr, Ip, Jp):
def reconstruct_routing(Ir, Jr, Ip, Jp, nodesplit=False):
"""reconstructs the hierarchy of routing nodes from the edges."""
rnodes = {}
# reconstruct hierarchy
for ij in range(len(Ir)):
i = Ir[ij]
j = Jr[ij]
Expand All @@ -27,77 +21,225 @@ def init_routing_nodes(Ir, Jr, Ip, Jp):
if j not in rnodes.keys():
rnodes[j] = routing_node(j)

rnodes[i].add_child(rnodes[j])
rnodes[i].children.append(rnodes[j])
# TODO: this may not be necessary when generalising to strict-confluent
if rnodes[j].parent is not None:
raise "node has more than one parent"
rnodes[j].parent = rnodes[i]

# add power edges
for ij in range(len(Ip)):
i = Ip[ij]
j = Jp[ij]
rnodes[i].add_poweredge(rnodes[j])
if i not in rnodes.keys():
rnodes[i] = routing_node(i)
if j not in rnodes.keys():
rnodes[j] = routing_node(j)

rnodes[i].pout.append(rnodes[j])
rnodes[j].pin.append(rnodes[i])

# split crossing-artifact nodes
#for node in routing_nodes:
# TODO: make the split edge shorter?
if nodesplit:
new_idx = len(rnodes)
splitnodes = []
for node in rnodes.values():
if len(node.children)>=2 and ((1 if node.parent is not None else 0)+len(node.pout)+len(node.pin))>=2:
splitnode = routing_node(new_idx)
splitnode.split = True
splitnode.parent = node
splitnode.children = node.children
for child in splitnode.children:
child.parent = splitnode

node.children = [splitnode]
splitnodes.append(splitnode)
new_idx += 1

for splitnode in splitnodes:
rnodes[splitnode.idx] = splitnode

return rnodes


# post-order traversal to init paths to one end
def init_paths_from_leaves(node, stack, paths):
def get_routing_adjacency(rnodes):
I = []
J = []
# V = []
for node in rnodes.values():
print(node.idx)
for child in node.children:
I.append(node.idx)
J.append(child.idx)
# if child.split:
# V.append(.5)
# else:
# V.append(1)
for pout in node.pout:
I.append(node.idx)
J.append(pout.idx)
# V.append(1)

return I,J#,V

# DFS to init paths to one end
def init_paths_to_leaves(node, stack, paths):
stack.append(node.idx)
if len(node.outgoing) == 0:
if len(node.children) == 0:
# if leaf, create path
paths.append([i for i in reversed(stack)])
paths.append([i for i in stack])
else:
for child in node.outgoing:
init_paths_from_leaves(child, stack, paths)
for child in node.children:
init_paths_to_leaves(child, stack, paths)
stack.pop()

# pre-order traversal to finish paths from other end
def finish_paths_to_leaves(node, stack, paths_from, all_paths):
# DFS to finish paths from other end
def finish_paths_from_leaves(node, stack, paths_to, all_paths):
stack.append(node.idx)
if len(node.outgoing) == 0:
if len(node.children) == 0:
# if leaf, finish path
path_to = [i for i in stack]
for path_from in paths_from:
path_from = [i for i in reversed(stack)]
for path_to in paths_to:
all_paths.append(path_from + path_to)
else:
for child in node.outgoing:
finish_paths_to_leaves(child, stack, paths_from, all_paths)
for child in node.children:
finish_paths_from_leaves(child, stack, paths_to, all_paths)
stack.pop()

def find_spline_paths(rnodes, Ip, Jp):
def find_spline_paths(rnodes):
all_paths = []
for ij in range(len(Ip)):
i = Ip[ij]
j = Jp[ij]
paths_from = []
init_paths_from_leaves(rnodes[i], [], paths_from)
finish_paths_to_leaves(rnodes[j], [], paths_from, all_paths)
for node in rnodes.values():
for adjacent in node.pout:
paths_to = []
init_paths_to_leaves(node, [], paths_to)
finish_paths_from_leaves(adjacent, [], paths_to, all_paths)

return all_paths

M_bspline = 1/6 * np.array([[-1, 3,-3, 1],
[ 3,-6, 3, 0],
[-3, 0, 3, 0],
[ 1, 4, 1, 0]])

def bspline(routing_nodes, path, nseg=50):
return
def draw_bspline_quad(layout, path):
m = len(path)
if m < 2:
raise "path is less than 2 points long"
if m == 2:
p0 = layout[path[0]]
p1 = layout[path[1]]
print('<path d="M {} {} L {} {}"/>'.format(p0[0],p0[1],p1[0],p1[1]))
elif m == 3:
p00 = layout[path[0]]
p01 = layout[path[1]]
p11 = layout[path[2]]
print('<path d="M {} {} Q {} {} {} {}"/>'.format(p00[0],p00[1],p01[0],p01[1],p11[0],p11[1]))
else:
nseg = m - 2
p00 = layout[path[0]]
p01 = layout[path[1]]
print('<path d="M {} {} Q {} {}'.format(p00[0],p00[1],p01[0],p01[1]), end='')

for i in range(1, nseg):
p11 = .5*layout[path[i]] + .5*layout[path[i+1]]
print(' {} {} T'.format(p11[0], p11[1]), end='')

p22 = layout[path[-1]]
print(' {} {}"/>'.format(p22[0],p22[1]))

def draw_bspline_cubic(layout, path):
m = len(path)
if m < 2:
raise "path is less than 2 points long"
if m == 2:
p0 = layout[path[0]]
p1 = layout[path[1]]
print('<path d="M {} {} L {} {}"/>'.format(p0[0],p0[1],p1[0],p1[1]))
elif m == 3:
p000 = layout[path[0]]
# p001 = 1/3*layout[path[0]] + 2/3*layout[path[1]]
# p011 = 2/3*layout[path[1]] + 1/3*layout[path[2]]
p001 = 1/2*layout[path[0]] + 1/2*layout[path[1]]
p011 = 1/2*layout[path[1]] + 1/2*layout[path[2]]
p111 = layout[path[2]]
print('<path d="M {} {} C {} {} {} {} {} {}"/>'.format(p000[0],p000[1],p001[0],p001[1],p011[0],p011[1],p111[0],p111[1]))
elif m == 4:
p000 = layout[path[0]]
p001 = layout[path[1]]
p011 = layout[path[2]]
p111 = layout[path[3]]
print('<path d="M {} {} C {} {} {} {} {} {}"/>'.format(p000[0],p000[1],p001[0],p001[1],p011[0],p011[1],p111[0],p111[1]))
else:
nseg = m - 3
p000 = layout[path[0]]
p001 = layout[path[1]]
p011 = .5*layout[path[1]] + .5*layout[path[2]]
print('<path d="M {} {} C {} {} {} {}'.format(p000[0],p000[1],p001[0],p001[1],p011[0],p011[1]), end='')

for i in range(2, nseg):
p112 = .75*layout[path[i]] + .25*layout[path[i+1]] # symmetric about the knot
p111 = .5*p011 + .5*p112 # knot
p122 = .25*layout[path[i]] + .75*layout[path[i+1]]
print(' {} {} S {} {}'.format(p111[0],p111[1],p122[0],p122[1]), end='')
p011 = p122

p112 = .5*layout[path[-3]] + .5*layout[path[-2]]
p111 = .5*p011 + .5*p112
p122 = layout[path[-2]]
p222 = layout[path[-1]]
print(' {} {} S {} {} {} {}"/>'.format(p111[0],p111[1],p122[0],p122[1],p222[0],p222[1]))

# TODO: change number of significant figures for coordinates
def draw_svg(rnodes, paths, layout, noderadius=.2, linkwidth=.05, width=750, border=50, linkopacity=1):
X = layout
n = len(X)
X_min = [min(X[i,0] for i in range(n)), min(X[i,1] for i in range(n))]
X_max = [max(X[i,0] for i in range(n)), max(X[i,1] for i in range(n))]

range_max = max(X_max[0]-X_min[0], X_max[1]-X_min[1]) # taller or wider
range_max += 2*noderadius # guarantee no nodes are cut off at the edges
scale = (width-2*border) / range_max

X_svg = np.empty((n,2))
for i in range(n):
X_svg[i] = (X[i] - X_min) * scale
X_svg[i] += [border + scale*noderadius, border + scale*noderadius]

print('<svg width="{}" height="{}" xmlns="http://www.w3.org/2000/svg">'.format(width, width))
print('<style type="text/css">')
print('path{{stroke:black;stroke-width:{};stroke-opacity:{};stroke-linecap:round;fill:transparent}}'.format(scale*linkwidth,linkopacity))
print('circle{{r:{}}}'.format(scale*noderadius))
print('</style>');

# draw splines
for path in paths:
# draw_bspline_quad(X_svg, path)
draw_bspline_cubic(X_svg, path)
# draw nodes
for node in rnodes.values():
if len(node.children) == 0:
print('<circle cx="{}" cy="{}"/>'.format(X_svg[node.idx][0],X_svg[node.idx][1]))
else:
print('<circle cx="{}" cy="{}" fill="red" fill-opacity=".5"/>'.format(X_svg[node.idx][0],X_svg[node.idx][1]))

print('</svg>')



# I = [0,0,0, 1,1,1, 2,2,2]
# J = [3,4,5, 3,4,5, 3,4,5]
# Ir, Jr, Ip, Jp = cpp.routing(6, I, J)

Ir = [6,6,6, 7,7,7]
Jr = [0,1,2, 3,4,5]
Ip = [6]
Jp = [7]
Ir = [6,6,6, 7,7, 8,8]
Jr = [0,1,2, 3,8, 4,5]
Ip = [6,9]
Jp = [7,8]

# Ir = []
# Jr = []
# Ip = [0,0,0, 1,1,1, 2,2,2, 6,6]
# Jp = [3,4,5, 3,4,5, 3,4,5, 3,4]

rnodes = init_routing_nodes(Ir, Jr, Ip, Jp)
rnodes = reconstruct_routing(Ir, Jr, Ip, Jp, nodesplit=False)

paths = find_spline_paths(rnodes, Ip, Jp)
print("paths:")
print(paths)
import s_gd2
I,J = get_routing_adjacency(rnodes)
layout = s_gd2.layout(len(rnodes), I, J)

paths = find_spline_paths(rnodes)
draw_svg(rnodes, paths, layout)
Binary file added sederberg.pdf
Binary file not shown.
20 changes: 12 additions & 8 deletions swig/pgd.i
Expand Up @@ -14,10 +14,14 @@
(int* J, int len_J)}

// output edge indices
%apply (int* ARGOUT_ARRAY1, int DIM1){(int* Ir, int len_Ir),
(int* Jr, int len_Jr),
(int* Ip, int len_Ip),
(int* Jp, int len_Jp)}
// %apply (int* ARGOUT_ARRAY1, int DIM1){(int* Ir, int len_Ir),
// (int* Jr, int len_Jr),
// (int* Ip, int len_Ip),
// (int* Jp, int len_Jp)}
%apply (int* ARGOUT_ARRAY1[ANY]){(int* Ir),
(int* Jr),
(int* Ip),
(int* Jp)}

extern void routing(int n, int m, int* I, int* J, int* Ir, int* Jr, int* Ip, int* Jp);

Expand All @@ -31,10 +35,10 @@ extern void routing(int n, int m, int* I, int* J, int* Ir, int* Jr, int* Ip, int
void np_routing(int n,
int* I, int len_I,
int* J, int len_J,
int* Ir, int len_Ir,
int* Jr, int len_Jr,
int* Ip, int len_Ip,
int* Jp, int len_Jp) {
int* Ir,
int* Jr,
int* Ip,
int* Jp) {

if (len_I != len_J) {
PyErr_Format(PyExc_ValueError, "arrays of indices do not have same length");
Expand Down

0 comments on commit 644001d

Please sign in to comment.