OpenRAM/compiler/datasheet/wavedrom.py

828 lines
31 KiB
Python

#!/usr/bin/python
# The MIT License (MIT)
#
# Copyright (c) 2011-2016 Aliaksei Chapyzhenka
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# Translated to Python from original file:
# https://github.com/drom/wavedrom/blob/master/src/WaveDrom.js
#
import sys
import json
import math
import waveskin
font_width = 7
lane = {
"xs" : 20, # tmpgraphlane0.width
"ys" : 20, # tmpgraphlane0.height
"xg" : 120, # tmpgraphlane0.x
"yg" : 0, # head gap
"yh0" : 0, # head gap title
"yh1" : 0, # head gap
"yf0" : 0, # foot gap
"yf1" : 0, # foot gap
"y0" : 5, # tmpgraphlane0.y
"yo" : 30, # tmpgraphlane1.y - y0
"tgo" : -10, # tmptextlane0.x - xg
"ym" : 15, # tmptextlane0.y - y0
"xlabel" : 6, # tmptextlabel.x - xg
"xmax" : 1,
"scale" : 1,
"head" : {},
"foot" : {}
}
def genBrick (texts, extra, times) :
R = []
if len( texts ) == 4 :
for j in range( times ):
R.append(texts[0])
for i in range ( extra ):
R.append(texts[1])
R.append(texts[2])
for i in range ( extra ):
R.append(texts[3])
return R
if len( texts ) == 1 :
texts.append(texts[0])
R.append(texts[0])
for i in range (times * (2 * (extra + 1)) - 1) :
R.append(texts[1])
return R
def genFirstWaveBrick (text, extra, times) :
pattern = {
'p': ['pclk', '111', 'nclk', '000'],
'n': ['nclk', '000', 'pclk', '111'],
'P': ['Pclk', '111', 'nclk', '000'],
'N': ['Nclk', '000', 'pclk', '111'],
'l': ['000'],
'L': ['000'],
'0': ['000'],
'h': ['111'],
'H': ['111'],
'1': ['111'],
'=': ['vvv-2'],
'2': ['vvv-2'],
'3': ['vvv-3'],
'4': ['vvv-4'],
'5': ['vvv-5'],
'd': ['ddd'],
'u': ['uuu'],
'z': ['zzz']
}
return genBrick( pattern.get( text, ['xxx'] ) , extra, times );
def genWaveBrick (text, extra, times) :
x1 = {'p':'pclk', 'n':'nclk', 'P':'Pclk', 'N':'Nclk', 'h':'pclk', 'l':'nclk', 'H':'Pclk', 'L':'Nclk'}
x2 = {'0':'0', '1':'1', 'x':'x', 'd':'d', 'u':'u', 'z':'z', '=':'v', '2':'v', '3':'v', '4':'v', '5':'v' }
x3 = {'0': '', '1': '', 'x': '', 'd': '', 'u': '', 'z': '', '=':'-2', '2':'-2', '3':'-3', '4':'-4', '5':'-5'}
y1 = {
'p':'0', 'n':'1',
'P':'0', 'N':'1',
'h':'1', 'l':'0',
'H':'1', 'L':'0',
'0':'0', '1':'1', 'x':'x', 'd':'d', 'u':'u', 'z':'z', '=':'v', '2':'v', '3':'v', '4':'v', '5':'v'}
y2 = {
'p': '', 'n': '',
'P': '', 'N': '',
'h': '', 'l': '',
'H': '', 'L': '',
'0': '', '1': '', 'x': '', 'd': '', 'u': '', 'z': '', '=':'-2', '2':'-2', '3':'-3', '4':'-4', '5':'-5'}
x4 = {
'p': '111', 'n': '000',
'P': '111', 'N': '000',
'h': '111', 'l': '000',
'H': '111', 'L': '000',
'0': '000', '1': '111', 'x': 'xxx', 'd': 'ddd', 'u': 'uuu', 'z': 'zzz',
'=': 'vvv-2', '2': 'vvv-2', '3': 'vvv-3', '4': 'vvv-4', '5': 'vvv-5'}
x5 = {'p':'nclk', 'n':'pclk', 'P':'nclk', 'N':'pclk'}
x6 = {'p': '000', 'n': '111', 'P': '000', 'N': '111'}
xclude = {'hp':'111', 'Hp':'111', 'ln': '000', 'Ln': '000', 'nh':'111', 'Nh':'111', 'pl': '000', 'Pl':'000'}
#atext = text.split()
atext = text
tmp0 = x4.get(atext[1])
tmp1 = x1.get(atext[1])
if tmp1 == None :
tmp2 = x2.get(atext[1])
if tmp2 == None :
# unknown
return genBrick(['xxx'], extra, times)
else :
tmp3 = y1.get(atext[0])
if tmp3 == None :
# unknown
return genBrick(['xxx'], extra, times)
# soft curves
return genBrick([tmp3 + 'm' + tmp2 + y2[atext[0]] + x3[atext[1]], tmp0], extra, times)
else :
tmp4 = xclude.get(text)
if tmp4 != None :
tmp1 = tmp4
# sharp curves
tmp2 = x5.get(atext[1])
if tmp2 == None :
# hlHL
return genBrick([tmp1, tmp0], extra, times)
else :
# pnPN
return genBrick([tmp1, tmp0, tmp2, x6[atext[1]]], extra, times)
def parseWaveLane (text, extra) :
R = []
Stack = text
Next = Stack[0]
Stack = Stack[1:]
Repeats = 1
while len(Stack) and ( Stack[0] == '.' or Stack[0] == '|' ): # repeaters parser
Stack=Stack[1:]
Repeats += 1
R.extend(genFirstWaveBrick(Next, extra, Repeats))
while len(Stack) :
Top = Next
Next = Stack[0]
Stack = Stack[1:]
Repeats = 1
while len(Stack) and ( Stack[0] == '.' or Stack[0] == '|' ) : # repeaters parser
Stack=Stack[1:]
Repeats += 1
R.extend(genWaveBrick((Top + Next), extra, Repeats))
for i in range( lane['phase'] ):
R = R[1:]
return R
def parseWaveLanes (sig) :
def data_extract (e) :
tmp = e.get('data')
if tmp == None : return None
if is_type_str (tmp) : tmp=tmp.split()
return tmp
content = []
for sigx in sig :
lane['period'] = sigx.get('period',1)
lane['phase'] = int( sigx.get('phase',0 ) * 2 )
sub_content=[]
sub_content.append( [sigx.get('name',' '), sigx.get('phase',0 ) ] )
sub_content.append( parseWaveLane( sigx['wave'], int(lane['period'] * lane['hscale'] - 1 ) ) if sigx.get('wave') else None )
sub_content.append( data_extract(sigx) )
content.append(sub_content)
return content
def findLaneMarkers (lanetext) :
lcount = 0
gcount = 0
ret = []
for i in range( len( lanetext ) ) :
if lanetext[i] == 'vvv-2' or lanetext[i] == 'vvv-3' or lanetext[i] == 'vvv-4' or lanetext[i] == 'vvv-5' :
lcount += 1
else :
if lcount !=0 :
ret.append(gcount - ((lcount + 1) / 2))
lcount = 0
gcount += 1
if lcount != 0 :
ret.append(gcount - ((lcount + 1) / 2))
return ret
def renderWaveLane (root, content, index) :
xmax = 0
xgmax = 0
glengths = []
svgns = 'http://www.w3.org/2000/svg'
xlinkns = 'http://www.w3.org/1999/xlink'
xmlns = 'http://www.w3.org/XML/1998/namespace'
for j in range( len(content) ):
name = content[j][0][0]
if name : # check name
g = [
'g',
{
'id': 'wavelane_' + str(j) + '_' + str(index),
'transform': 'translate(0,' + str(lane['y0'] + j * lane['yo']) + ')'
}
]
root.append(g)
title = [
'text',
{
'x': lane['tgo'],
'y': lane['ym'],
'class': 'info',
'text-anchor': 'end',
'xml:space': 'preserve'
},
['tspan', name]
]
g.append(title)
glengths.append( len(name) * font_width + font_width )
xoffset = content[j][0][1]
xoffset = math.ceil(2 * xoffset) - 2 * xoffset if xoffset > 0 else -2 * xoffset
gg = [
'g',
{
'id': 'wavelane_draw_' + str(j) + '_' + str(index),
'transform': 'translate(' + str( xoffset * lane['xs'] ) + ', 0)'
}
]
g.append(gg)
if content[j][1] :
for i in range( len(content[j][1]) ) :
b = [
'use',
{
#'id': 'use_' + str(i) + '_' + str(j) + '_' + str(index),
'xmlns:xlink':xlinkns,
'xlink:href': '#' + str( content[j][1][i] ),
'transform': 'translate(' + str(i * lane['xs']) + ')'
}
]
gg.append(b)
if content[j][2] and len(content[j][2]) :
labels = findLaneMarkers(content[j][1])
if len(labels) != 0 :
for k in range( len(labels) ) :
if content[j][2] and k < len(content[j][2]) :
title = [
'text',
{
'x': int(labels[k]) * lane['xs'] + lane['xlabel'],
'y': lane['ym'],
'text-anchor': 'middle',
'xml:space': 'preserve'
},
['tspan',content[j][2][k]]
]
gg.append(title)
if len(content[j][1]) > xmax :
xmax = len(content[j][1])
lane['xmax'] = xmax
lane['xg'] = xgmax + 20
return glengths
def renderMarks (root, content, index) :
def captext ( g, cxt, anchor, y ) :
if cxt.get(anchor) and cxt[anchor].get('text') :
tmark = [
'text',
{
'x': float( cxt['xmax'] ) * float( cxt['xs'] ) / 2,
'y': y,
'text-anchor': 'middle',
'fill': '#000',
'xml:space': 'preserve'
}, cxt[anchor]['text']
]
g.append(tmark)
def ticktock ( g, cxt, ref1, ref2, x, dx, y, length ) :
L = []
if cxt.get(ref1) == None or cxt[ref1].get(ref2) == None :
return
val = cxt[ref1][ref2]
if is_type_str( val ) :
val = val.split()
elif type( val ) is int :
offset = val
val = []
for i in range ( length ) :
val.append(i + offset)
if type( val ) is list :
if len( val ) == 0 :
return
elif len( val ) == 1 :
offset = val[0]
if is_type_str(offset) :
L = val
else :
for i in range ( length ) :
L[i] = i + offset
elif len( val ) == 2:
offset = int(val[0])
step = int(val[1])
tmp = val[1].split('.')
if len( tmp ) == 2 :
dp = len( tmp[1] )
if is_type_str(offset) or is_type_str(step) :
L = val
else :
offset = step * offset
for i in range( length ) :
L[i] = "{0:.",dp,"f}".format(step * i + offset)
else :
L = val
else :
return
for i in range( length ) :
tmp = L[i]
tmark = [
'text',
{
'x': i * dx + x,
'y': y,
'text-anchor': 'middle',
'class': 'muted',
'xml:space': 'preserve'
}, str(tmp)
]
g.append(tmark)
mstep = 2 * int(lane['hscale'])
mmstep = mstep * lane['xs']
marks = int( lane['xmax'] / mstep )
gy = len( content ) * int(lane['yo'])
g = ['g', {'id': 'gmarks_' + str(index)}]
root.insert(0,g)
for i in range( marks + 1):
gg = [
'path',
{
'id': 'gmark_' + str(i) + '_' + str(index),
'd': 'm ' + str(i * mmstep) + ',' + '0' + ' 0,' + str(gy),
'style': 'stroke:#888;stroke-width:0.5;stroke-dasharray:1,3'
}
]
g.append( gg )
captext(g, lane, 'head', -33 if lane['yh0'] else -13 )
captext(g, lane, 'foot', gy + ( 45 if lane['yf0'] else 25 ) )
ticktock( g, lane, 'head', 'tick', 0, mmstep, -5, marks + 1)
ticktock( g, lane, 'head', 'tock', mmstep / 2, mmstep, -5, marks)
ticktock( g, lane, 'foot', 'tick', 0, mmstep, gy + 15, marks + 1)
ticktock( g, lane, 'foot', 'tock', mmstep / 2, mmstep, gy + 15, marks)
def renderArcs (root, source, index, top) :
Stack = []
Edge = {'words': [], 'frm': 0, 'shape': '', 'to': 0, 'label': ''}
Events = {}
svgns = 'http://www.w3.org/2000/svg'
xmlns = 'http://www.w3.org/XML/1998/namespace'
if source :
for i in range (len (source) ) :
lane['period'] = source[i].get('period',1)
lane['phase'] = int( source[i].get('phase',0 ) * 2 )
text = source[i].get('node')
if text:
Stack = text
pos = 0
while len( Stack ) :
eventname = Stack[0]
Stack=Stack[1:]
if eventname != '.' :
Events[eventname] = {
'x' : str( int( float( lane['xs'] ) * (2 * pos * lane['period'] * lane['hscale'] - lane['phase'] ) + float( lane['xlabel'] ) ) ),
'y' : str( int( i * lane['yo'] + lane['y0'] + float( lane['ys'] ) * 0.5 ) )
}
pos += 1
gg = [ 'g', { 'id' : 'wavearcs_' + str( index ) } ]
root.append(gg)
if top.get('edge') :
for i in range( len ( top['edge'] ) ) :
Edge['words'] = top['edge'][i].split()
Edge['label'] = top['edge'][i][len(Edge['words'][0]):]
Edge['label'] = Edge['label'][1:]
Edge['frm'] = Edge['words'][0][0]
Edge['to'] = Edge['words'][0][-1]
Edge['shape'] = Edge['words'][0][1:-1]
frm = Events[Edge['frm']]
to = Events[Edge['to']]
gmark = [
'path',
{
'id': 'gmark_' + Edge['frm'] + '_' + Edge['to'],
'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + to['x'] + ',' + to['y'],
'style': 'fill:none;stroke:#00F;stroke-width:1'
}
]
gg.append(gmark)
dx = float( to['x'] ) - float( frm['x'] )
dy = float( to['y'] ) - float( frm['y'] )
lx = (float(frm['x']) + float(to['x'])) / 2
ly = (float(frm['y']) + float(to['y'])) / 2
pattern = {
'~' : {'d': 'M ' + frm['x'] + ',' + frm['y'] + ' c ' + str(0.7 * dx) + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy) },
'-~' : {'d': 'M ' + frm['x'] + ',' + frm['y'] + ' c ' + str(0.7 * dx) + ', 0 ' + str(dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy) },
'~-' : {'d': 'M ' + frm['x'] + ',' + frm['y'] + ' c ' + '0' + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy) },
'-|' : {'d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx) + ',0 0,' + str(dy)},
'|-' : {'d': 'm ' + frm['x'] + ',' + frm['y'] + ' 0,' + str(dy) + ' ' + str(dx) + ',0'},
'-|-' : {'d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx / 2) + ',0 0,' + str(dy) + ' ' + str(dx / 2) + ',0'},
'->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none'},
'~>' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)},
'-~>' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)},
'~->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + '0' + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)},
'-|>' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx) + ',0 0,' + str(dy)},
'|->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'm ' + frm['x'] + ',' + frm['y'] + ' 0,' + str(dy) + ' ' + str(dx) + ',0'},
'-|->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx / 2) + ',0 0,' + str(dy) + ' ' + str(dx / 2) + ',0'},
'<->' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none'},
'<~>' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)},
'<-~>' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)},
'<-|>' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx) + ',0 0,' + str(dy)},
'<-|->': {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx / 2) + ',0 0,' + str(dy) + ' ' + str(dx / 2) + ',0'}
}
gmark[1].update( pattern.get( Edge['shape'], { 'style': 'fill:none;stroke:#00F;stroke-width:1' } ) )
if Edge['label']:
if Edge['shape'] == '-~' :
lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.75
if Edge['shape'] == '~-' :
lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.25
if Edge['shape'] == '-|' :
lx = float(to['x'])
if Edge['shape'] == '|-' :
lx = float(frm['x'])
if Edge['shape'] == '-~>':
lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.75
if Edge['shape'] == '~->':
lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.25
if Edge['shape'] == '-|>' :
lx = float(to['x'])
if Edge['shape'] == '|->' :
lx = float(frm['x'])
if Edge['shape'] == '<-~>':
lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.75
if Edge['shape'] =='<-|>' :
lx = float(to['x'])
lwidth = len( Edge['label'] ) * font_width
label = [
'text',
{
'style': 'font-size:10px;',
'text-anchor': 'middle',
'xml:space': 'preserve',
'x': int( lx ),
'y': int( ly + 3 )
},
[ 'tspan', Edge['label'] ]
]
underlabel = [
'rect',
{
'height': 9,
'style': 'fill:#FFF;',
'width': lwidth,
'x': int( lx - lwidth / 2 ),
'y': int( ly - 5 )
}
]
gg.append(underlabel)
gg.append(label)
for k in Events:
if k.islower() :
if int( Events[k]['x'] ) > 0 :
lwidth = len( k ) * font_width
underlabel = [
'rect',
{
'x': float( Events[k]['x'] ) - float(lwidth) / 2,
'y': int( Events[k]['y'] ) - 4,
'height': 8,
'width': lwidth,
'style': 'fill:#FFF;'
}
]
gg.append(underlabel)
label = [
'text',
{
'style': 'font-size:8px;',
'x': int( Events[k]['x'] ),
'y': int( Events[k]['y'] ) + 2,
'width': lwidth,
'text-anchor': 'middle'
},
k
]
gg.append(label)
def parseConfig (source) :
lane['hscale'] = 1
if lane.get('hscale0') :
lane['hscale'] = lane['hscale0']
if source and source.get('config') and source.get('config').get('hscale'):
hscale = round(source.get('config').get('hscale'))
if hscale > 0 :
if hscale > 100 : hscale = 100
lane['hscale'] = hscale
lane['yh0'] = 0
lane['yh1'] = 0
if source and source.get('head') :
lane['head'] = source['head']
if source.get('head').get('tick',0) == 0 : lane['yh0'] = 20
if source.get('head').get('tock',0) == 0 : lane['yh0'] = 20
if source.get('head').get('text') : lane['yh1'] = 46; lane['head']['text'] = source['head']['text']
lane['yf0'] = 0
lane['yf1'] = 0
if source and source.get('foot') :
lane['foot'] = source['foot']
if source.get('foot').get('tick',0) == 0 : lane['yf0'] = 20
if source.get('foot').get('tock',0) == 0 : lane['yf0'] = 20
if source.get('foot').get('text') : lane['yf1'] = 46; lane['foot']['text'] = source['foot']['text']
def rec (tmp, state) :
name = str( tmp[0] )
delta_x = 25
state['x'] += delta_x
for i in range( len( tmp ) ) :
if type( tmp[i] ) is list :
old_y = state['y']
rec( tmp[i], state )
state['groups'].append( {'x':state['xx'], 'y':old_y, 'height':state['y'] - old_y, 'name': state['name'] } )
elif type( tmp[i] ) is dict :
state['lanes'].append(tmp[i])
state['width'].append(state['x'])
state['y'] += 1
state['xx'] = state['x']
state['x'] -= delta_x
state['name'] = name
def insertSVGTemplate (index, parent, source) :
e = waveskin.WaveSkin['default']
if source.get('config') and source.get('config').get('skin') :
if waveskin.WaveSkin.get( source.get('config').get('skin') ) :
e = waveskin.WaveSkin[ source.get('config').get('skin') ]
if index == 0 :
lane['xs'] = int( e[3][1][2][1]['width'] )
lane['ys'] = int( e[3][1][2][1]['height'] )
lane['xlabel'] = int( e[3][1][2][1]['x'] )
lane['ym'] = int( e[3][1][2][1]['y'] )
else :
e = ['svg', {'id': 'svg', 'xmlns': 'http://www.w3.org/2000/svg', 'xmlns:xlink': 'http://www.w3.org/1999/xlink', 'height': '0'},
['g', {'id': 'waves'},
['g', {'id': 'lanes'}],
['g', {'id': 'groups'}]
]
]
e[-1][1]['id'] = 'waves_' + str(index)
e[-1][2][1]['id'] = 'lanes_' + str(index)
e[-1][3][1]['id'] = 'groups_' + str(index)
e[1]['id'] = 'svgcontent_' + str(index)
e[1]['height'] = 0
parent.extend(e)
def renderWaveForm (index, source, output) :
xmax = 0
root = []
groups = []
if source.get('signal'):
insertSVGTemplate(index, output, source)
parseConfig( source )
ret = {'x':0, 'y':0, 'xmax':0, 'width':[], 'lanes':[], 'groups':[] }
rec( source['signal'], ret )
content = parseWaveLanes(ret['lanes'])
glengths = renderWaveLane(root, content, index)
for i in range( len( glengths ) ):
xmax = max( xmax, ( glengths[i] + ret['width'][i] ) )
renderMarks(root, content, index)
renderArcs(root, ret['lanes'], index, source)
renderGaps(root, ret['lanes'], index)
renderGroups(groups, ret['groups'], index)
lane['xg'] = int( math.ceil( float( xmax - lane['tgo'] ) / float(lane['xs'] ) ) ) * lane['xs']
width = (lane['xg'] + lane['xs'] * (lane['xmax'] + 1) )
height = len(content) * lane['yo'] + lane['yh0'] + lane['yh1'] + lane['yf0'] + lane['yf1']
output[1]={
'id' :'svgcontent_' + str(index),
'xmlns' :"http://www.w3.org/2000/svg",
'xmlns:xlink':"http://www.w3.org/1999/xlink",
'width' :str(width),
'height' :str(height),
'viewBox' :'0 0 ' + str(width) + ' ' + str(height),
'overflow' :"hidden"
}
output[-1][2][1]['transform']='translate(' + str(lane['xg'] + 0.5) + ', ' + str((float(lane['yh0']) + float(lane['yh1'])) + 0.5) + ')'
output[-1][2].extend(root)
output[-1][3].extend(groups)
def renderGroups (root, groups, index) :
svgns = 'http://www.w3.org/2000/svg',
xmlns = 'http://www.w3.org/XML/1998/namespace'
for i in range( len( groups ) ) :
group = [
'path',
{
'id': 'group_' + str(i) + '_' + str(index),
'd': 'm ' + str( groups[i]['x'] + 0.5 ) + ',' + str( groups[i]['y']* lane['yo'] + 3.5 + lane['yh0'] + lane['yh1'] ) + ' c -3,0 -5,2 -5,5 l 0,' + str( int( groups[i]['height'] * lane['yo'] - 16 ) ) + ' c 0,3 2,5 5,5',
'style': 'stroke:#0041c4;stroke-width:1;fill:none'
}
]
root.append(group)
name = groups[i]['name']
x = str( int( groups[i]['x'] - 10 ) )
y = str( int( lane['yo'] * (groups[i]['y'] + (float(groups[i]['height']) / 2)) + lane['yh0'] + lane['yh1'] ) )
label = [
['g',
{'transform': 'translate(' + x + ',' + y + ')'},
['g', {'transform': 'rotate(270)'},
'text',
{
'text-anchor': 'middle',
'class': 'info',
'xml:space' : 'preserve'
},
['tspan',name]
]
]
]
root.append(label)
def renderGaps (root, source, index) :
Stack = []
svgns = 'http://www.w3.org/2000/svg',
xlinkns = 'http://www.w3.org/1999/xlink'
if source:
gg = [
'g',
{ 'id': 'wavegaps_' + str(index) }
]
for i in range( len( source )):
lane['period'] = source[i].get('period',1)
lane['phase'] = int( source[i].get('phase',0 ) * 2 )
g = [
'g',
{
'id': 'wavegap_' + str(i) + '_' + str(index),
'transform': 'translate(0,' + str(lane['y0'] + i * lane['yo']) + ')'
}
]
gg.append(g)
if source[i].get('wave'):
text = source[i]['wave']
Stack = text
pos = 0
while len( Stack ) :
c = Stack [0]
Stack = Stack[1:]
if c == '|' :
b = [
'use',
{
'xmlns:xlink':xlinkns,
'xlink:href':'#gap',
'transform': 'translate(' + str(int(float(lane['xs']) * ((2 * pos + 1) * float(lane['period']) * float(lane['hscale']) - float(lane['phase'])))) + ')'
}
]
g.append(b)
pos += 1
root.append( gg )
def is_type_str( var ) :
if sys.version_info[0] < 3:
return type( var ) is str or type( var ) is unicode
else:
return type( var ) is str
def convert_to_svg( root ) :
svg_output = ''
if type( root ) is list:
if len(root) >= 2 and type( root[1] ) is dict:
if len( root ) == 2 :
svg_output += '<' + root[0] + convert_to_svg( root[1] ) + '/>\n'
elif len( root ) >= 3 :
svg_output += '<' + root[0] + convert_to_svg( root[1] ) + '>\n'
if len( root ) == 3:
svg_output += convert_to_svg( root[2] )
else:
svg_output += convert_to_svg( root[2:] )
svg_output += '</' + root[0] + '>\n'
elif type( root[0] ) is list:
for eleml in root:
svg_output += convert_to_svg( eleml )
else:
svg_output += '<' + root[0] + '>\n'
for eleml in root[1:]:
svg_output += convert_to_svg( eleml )
svg_output += '</' + root[0] + '>\n'
elif type( root ) is dict:
for elemd in root :
svg_output += ' ' + elemd + '="' + str(root[elemd]) + '"'
else:
svg_output += root
return svg_output
if __name__ == '__main__':
if len( sys.argv ) != 5:
print ( 'Usage : ' + sys.argv[0] + ' source <input.json> svg <output.svg>' )
exit(1)
if sys.argv[3] != 'svg' :
print ( 'Error: only SVG format supported.' )
exit(1)
output=[]
inputfile = sys.argv[2]
outputfile = sys.argv[4]
with open(inputfile,'r') as f:
jinput = json.load(f)
renderWaveForm(0,jinput,output)
svg_output = convert_to_svg(output)
with open(outputfile,'w') as f:
f.write( svg_output )