926 lines
40 KiB
Python
926 lines
40 KiB
Python
|
|
# Copyright wavedrompy contributors.
|
||
|
|
# SPDX-License-Identifier: MIT
|
||
|
|
|
||
|
|
# Originally translated to Python from original file:
|
||
|
|
# https://github.com/drom/wavedrom/blob/master/src/WaveDrom.js
|
||
|
|
# Now many parts have been rewritten and diverged
|
||
|
|
|
||
|
|
import sys
|
||
|
|
import math
|
||
|
|
import re
|
||
|
|
from itertools import chain
|
||
|
|
import svgwrite
|
||
|
|
from .attrdict import AttrDict
|
||
|
|
from collections import deque
|
||
|
|
|
||
|
|
from six import string_types
|
||
|
|
|
||
|
|
from wavedrom.tspan import JsonMLElement
|
||
|
|
from . import waveskin, css
|
||
|
|
from .base import SVGBase
|
||
|
|
|
||
|
|
|
||
|
|
class WaveDrom(SVGBase):
|
||
|
|
def __init__(self):
|
||
|
|
self.font_width = 7
|
||
|
|
self.lane = AttrDict({
|
||
|
|
"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": {}
|
||
|
|
})
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
def stretch_bricks(wave, stretch):
|
||
|
|
stretcher = {
|
||
|
|
"Pclk": "111", "Nclk": "000",
|
||
|
|
"pclk": "111", "nclk": "000",
|
||
|
|
"0": "000", "1": "111", "x": "xxx", "d": "ddd", "u": "uuu", "z": "zzz",
|
||
|
|
"2": "vvv-2", "3": "vvv-3", "4": "vvv-4", "5": "vvv-5", "6": "vvv-6",
|
||
|
|
"7": "vvv-7", "8": "vvv-8", "9": "vvv-9"}
|
||
|
|
|
||
|
|
if stretch == -0.5:
|
||
|
|
# This is the only valid non-integer value, it essentially means halfing down. Further subsampling
|
||
|
|
# does not work I think..
|
||
|
|
return wave[0::2]
|
||
|
|
else:
|
||
|
|
stretch = int(stretch)
|
||
|
|
|
||
|
|
def getBrick(w):
|
||
|
|
if w in stretcher:
|
||
|
|
return stretcher[w]
|
||
|
|
elif w[2] in stretcher:
|
||
|
|
return stretcher[w[2]]
|
||
|
|
else:
|
||
|
|
return stretcher[w[-1]]
|
||
|
|
|
||
|
|
if stretch > 0:
|
||
|
|
return list(chain.from_iterable(([w] + [getBrick(w)]*stretch for w in wave)))
|
||
|
|
else:
|
||
|
|
return wave
|
||
|
|
|
||
|
|
def gen_wave_brick(self, prev=None, this=None, stretch=0, repeat=0, subcycle=False):
|
||
|
|
sharpedge_clk = { "p": "pclk", "n": "nclk", "P": "Pclk", "N": "Nclk" }
|
||
|
|
sharpedge_sig = { "h": "pclk", "l": "nclk", "H": "Pclk", "L": "Nclk" }
|
||
|
|
sharpedge = sharpedge_clk.copy()
|
||
|
|
sharpedge.update(sharpedge_sig)
|
||
|
|
|
||
|
|
# level: logical levels of symbols at wave
|
||
|
|
level = {"=": "v", "2": "v", "3": "v", "4": "v", "5": "v", "6": "v",
|
||
|
|
"7": "v", "8": "v", "9": "v", "h": "1", "H": "1", "l": "0", "L": "0"}
|
||
|
|
# translevel: Those are the levels at the end of a cycle (special for clocks)
|
||
|
|
translevel = level.copy()
|
||
|
|
translevel.update({"p": "0", "P": "0", "n": "1", "N": "1"})
|
||
|
|
# data: Modifiers of wavebricks that add data
|
||
|
|
data = {"=": "-2", "2": "-2", "3": "-3", "4": "-4", "5": "-5", "6":
|
||
|
|
"-6", "7": "-7", "8": "-8", "9": "-9"}
|
||
|
|
# clkinvert: The inverse brick to clock symbols
|
||
|
|
clkinvert = {"p": "nclk", "n": "pclk", "P": "nclk", "N": "pclk"}
|
||
|
|
# xclude: Those are actually identical levels, no transition
|
||
|
|
xclude = {"hp": "111", "Hp": "111", "ln": "000", "Ln": "000",
|
||
|
|
"nh": "111", "Nh": "111", "pl": "000", "Pl": "000"}
|
||
|
|
|
||
|
|
if this in sharpedge.keys():
|
||
|
|
if prev is None:
|
||
|
|
if this in sharpedge_clk.keys():
|
||
|
|
first = sharpedge[this]
|
||
|
|
else:
|
||
|
|
first = level.get(this, this)*3
|
||
|
|
else:
|
||
|
|
first = xclude.get(prev+this, sharpedge[this])
|
||
|
|
|
||
|
|
if this in sharpedge_clk.keys():
|
||
|
|
wave = [first, clkinvert[this]] * (1 + repeat)
|
||
|
|
else:
|
||
|
|
wave = [first] + [level.get(this, this)*3]*(2 * repeat + 1)
|
||
|
|
else:
|
||
|
|
if prev is None:
|
||
|
|
transition = level.get(this, this)*3 + data.get(this, "")
|
||
|
|
else:
|
||
|
|
transition = translevel.get(prev, prev) + 'm' + level.get(this, this) + data.get(prev, "") + data.get(this, "")
|
||
|
|
value = level.get(this, this)*3 + data.get(this, "")
|
||
|
|
wave = [transition, value] + [value, value] * repeat
|
||
|
|
|
||
|
|
if subcycle:
|
||
|
|
wave = wave[0:repeat+1]
|
||
|
|
|
||
|
|
if not (stretch == -0.5 and this in sharpedge_clk.keys()):
|
||
|
|
wave = self.stretch_bricks(wave, stretch)
|
||
|
|
|
||
|
|
return wave
|
||
|
|
|
||
|
|
def parse_wave_lane(self, text, stretch=0):
|
||
|
|
R = []
|
||
|
|
|
||
|
|
Stack = deque(text)
|
||
|
|
|
||
|
|
This = None
|
||
|
|
subCycle = False
|
||
|
|
|
||
|
|
while len(Stack) > 0:
|
||
|
|
Top = This
|
||
|
|
This = Stack.popleft()
|
||
|
|
repeat = 0
|
||
|
|
if This == '|':
|
||
|
|
This = 'x'
|
||
|
|
if This == '<':
|
||
|
|
subCycle = True
|
||
|
|
This = Top
|
||
|
|
Top = None
|
||
|
|
if Stack[0] in ['.', '|']:
|
||
|
|
Stack.popleft()
|
||
|
|
else:
|
||
|
|
continue
|
||
|
|
if This == '>':
|
||
|
|
subCycle = False
|
||
|
|
This = Top
|
||
|
|
Top = None
|
||
|
|
if Stack and Stack[0] in ['.', '|']:
|
||
|
|
Stack.popleft()
|
||
|
|
else:
|
||
|
|
continue
|
||
|
|
while Stack and Stack[0] in ['.', '|']:
|
||
|
|
Stack.popleft()
|
||
|
|
repeat += 1
|
||
|
|
R.extend(self.gen_wave_brick(Top, This, stretch, repeat, subCycle))
|
||
|
|
|
||
|
|
for i in range(int(math.ceil(self.lane.phase))):
|
||
|
|
R = R[1:]
|
||
|
|
|
||
|
|
return R
|
||
|
|
|
||
|
|
def parse_wave_lanes(self, sig=""):
|
||
|
|
def data_extract(e):
|
||
|
|
tmp = e.get("data")
|
||
|
|
if tmp is not None:
|
||
|
|
tmp = tmp.split() if isinstance(tmp, string_types) else tmp
|
||
|
|
return tmp
|
||
|
|
|
||
|
|
content = []
|
||
|
|
for sigx in sig:
|
||
|
|
self.lane.period = sigx.get("period", 1)
|
||
|
|
self.lane.phase = sigx.get("phase", 0) * 2
|
||
|
|
sub_content = []
|
||
|
|
sub_content.append([sigx.get("name", " "), sigx.get("phase", 0)])
|
||
|
|
if sigx.get("wave"):
|
||
|
|
sub_content.append(self.parse_wave_lane(sigx["wave"], self.lane.period * self.lane.hscale - 1))
|
||
|
|
else:
|
||
|
|
sub_content.append(None)
|
||
|
|
sub_content.append(data_extract(sigx))
|
||
|
|
content.append(sub_content)
|
||
|
|
|
||
|
|
return content
|
||
|
|
|
||
|
|
def find_lane_markers(self, lanetext=""):
|
||
|
|
|
||
|
|
lcount = 0
|
||
|
|
gcount = 0
|
||
|
|
ret = []
|
||
|
|
for idx, val in enumerate(lanetext):
|
||
|
|
if val in ["vvv-2", "vvv-3", "vvv-4", "vvv-5", "vvv-6", "vvv-7", "vvv-8", "vvv-9"]:
|
||
|
|
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 render_lane_uses(self, val, g):
|
||
|
|
if val[1]:
|
||
|
|
for i in range(len(val[1])):
|
||
|
|
b = self.container.use(href="#{}".format(val[1][i]))
|
||
|
|
b.translate(i * self.lane.xs)
|
||
|
|
g.add(b)
|
||
|
|
|
||
|
|
if val[2] and len(val[2]):
|
||
|
|
labels = self.find_lane_markers(val[1])
|
||
|
|
if len(labels) != 0:
|
||
|
|
for k in range(len(labels)):
|
||
|
|
if val[2] and k < len(val[2]):
|
||
|
|
tx = int(labels[k]) * self.lane.xs + self.lane.xlabel
|
||
|
|
title = self.element.text("", x=[tx], y=[self.lane.ym], text_anchor="middle")
|
||
|
|
title.add(self.element.tspan(val[2][k]))
|
||
|
|
title["xml:space"] = "preserve"
|
||
|
|
g.add(title)
|
||
|
|
|
||
|
|
def text_width(self, string, size=11):
|
||
|
|
chars = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,34,47,74,74,118,89,25,44,44,52,78,37,
|
||
|
|
44,37,37,74,74,74,74,74,74,74,74,74,74,37,37,78,78,78,74,135,89,89,96,96,89,81,103,96,37,67,89,74,109,
|
||
|
|
96,103,89,103,96,89,81,96,89,127,89,87,81,37,37,37,61,74,44,74,74,67,74,74,37,74,74,30,30,67,30,112,74,
|
||
|
|
74,74,74,44,67,37,74,67,95,66,65,67,44,34,44,78,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||
|
|
0,0,0,0,0,0,37,43,74,74,74,74,34,74,44,98,49,74,78,0,98,73,53,73,44,44,44,77,71,37,44,44,49,74,111,111,
|
||
|
|
111,81,89,89,89,89,89,89,133,96,89,89,89,89,37,37,37,37,96,96,103,103,103,103,103,78,103,96,96,96,96,
|
||
|
|
87,89,81,74,74,74,74,74,74,118,67,74,74,74,74,36,36,36,36,74,74,74,74,74,74,74,73,81,74,74,74,74,65,74,
|
||
|
|
65,89,74,89,74,89,74,96,67,96,67,96,67,96,67,96,82,96,74,89,74,89,74,89,74,89,74,89,74,103,74,103,74,
|
||
|
|
103,74,103,74,96,74,96,74,37,36,37,36,37,36,37,30,37,36,98,59,67,30,89,67,67,74,30,74,30,74,39,74,44,
|
||
|
|
74,30,96,74,96,74,96,74,80,96,74,103,74,103,74,103,74,133,126,96,44,96,44,96,44,89,67,89,67,89,67,89,
|
||
|
|
67,81,38,81,50,81,37,96,74,96,74,96,74,96,74,96,74,96,74,127,95,87,65,87,81,67,81,67,81,67,30,84,97,91,
|
||
|
|
84,91,84,94,92,73,104,109,91,84,81,84,100,82,76,74,103,91,131,47,40,99,77,37,79,130,100,84,104,114,87,
|
||
|
|
126,101,87,84,93,84,69,84,46,52,82,52,82,114,89,102,96,100,98,91,70,88,88,77,70,85,89,77,67,84,39,65,
|
||
|
|
61,39,189,173,153,111,105,61,123,123,106,89,74,37,30,103,74,96,74,96,74,96,74,96,74,96,74,81,91,81,91,
|
||
|
|
81,130,131,102,84,103,84,87,78,104,81,104,81,88,76,37,189,173,153,103,84,148,90,100,84,89,74,133,118,
|
||
|
|
103,81]
|
||
|
|
|
||
|
|
return sum([(chars[ord(c)] if ord(c) <= len(chars) else 114) for c in string])*size/100
|
||
|
|
|
||
|
|
def render_wave_lane(self, content="", index=0):
|
||
|
|
xmax = 0
|
||
|
|
xgmax = 0
|
||
|
|
glengths = []
|
||
|
|
groups = []
|
||
|
|
|
||
|
|
for j, val in enumerate(content):
|
||
|
|
name = val[0][0].strip()
|
||
|
|
if name is not None:
|
||
|
|
dy = self.lane.y0 + j * self.lane.yo
|
||
|
|
g = self.container.g(id="wavelane_{j}_{index}".format(j=j, index=index))
|
||
|
|
g.translate(0, dy)
|
||
|
|
title = self.element.text("", x=[self.lane.tgo], y=[self.lane.ym], text_anchor="end")
|
||
|
|
title.add(self.element.tspan(name))
|
||
|
|
title["xml:space"] = "preserve"
|
||
|
|
title["class"] = "info"
|
||
|
|
g.add(title)
|
||
|
|
|
||
|
|
glengths.append(self.text_width(name))
|
||
|
|
|
||
|
|
xoffset = val[0][1]
|
||
|
|
xoffset = math.ceil(2 * xoffset) - 2 * xoffset if xoffset > 0 else -2 * xoffset
|
||
|
|
gg = self.container.g(id="wavelane_draw_{j}_{index}".format(j=j, index=index))
|
||
|
|
gg.translate(xoffset * self.lane.xs, 0)
|
||
|
|
|
||
|
|
self.render_lane_uses(val, gg)
|
||
|
|
|
||
|
|
if val[1] and len(val[1]) > xmax:
|
||
|
|
xmax = len(val[1])
|
||
|
|
|
||
|
|
g.add(gg)
|
||
|
|
groups.append(g)
|
||
|
|
self.lane.xmax = xmax
|
||
|
|
self.lane.xg = xgmax + 20
|
||
|
|
return (glengths, groups)
|
||
|
|
|
||
|
|
def captext(self, g, cxt, anchor, y):
|
||
|
|
if cxt.get(anchor) and cxt[anchor].get("text"):
|
||
|
|
tmark = self.element.text("", x=[float(cxt.xmax)*float(cxt.xs)/2], y=[y], text_anchor="middle", fill="#000")
|
||
|
|
tmark["xml:space"] = "preserve"
|
||
|
|
if isinstance(cxt[anchor]["text"], string_types):
|
||
|
|
tmark.add(self.element.tspan(cxt[anchor]["text"]))
|
||
|
|
else:
|
||
|
|
tmark.add(JsonMLElement(cxt[anchor]["text"]))
|
||
|
|
g.add(tmark)
|
||
|
|
|
||
|
|
def ticktock(self, g, cxt, ref1, ref2, x, dx, y, length):
|
||
|
|
L = []
|
||
|
|
|
||
|
|
if cxt.get(ref1) is None or cxt[ref1].get(ref2) is None:
|
||
|
|
return
|
||
|
|
|
||
|
|
val = cxt[ref1][ref2]
|
||
|
|
|
||
|
|
if isinstance(val, string_types):
|
||
|
|
val = val.split()
|
||
|
|
elif isinstance(val, (int, float, bool)):
|
||
|
|
offset = int(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 isinstance(offset, string_types):
|
||
|
|
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 isinstance(offset, string_types) or isinstance(step, string_types):
|
||
|
|
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 = self.element.text(tmp, x=[i * dx + x], y=[y], text_anchor="middle")
|
||
|
|
tmark["class"] = "muted"
|
||
|
|
tmark["xml:space"] = "preserve"
|
||
|
|
g.add(tmark)
|
||
|
|
|
||
|
|
def render_marks(self, content="", index=0):
|
||
|
|
def get_elem(e):
|
||
|
|
if len(e) == 3:
|
||
|
|
ret = self.element[e[0]](e[2])
|
||
|
|
ret.attribs = e[1]
|
||
|
|
elif len(e) == 2:
|
||
|
|
ret = self.element[e[0]](e[1])
|
||
|
|
else:
|
||
|
|
ret = self.element.tspan(e)
|
||
|
|
return ret
|
||
|
|
|
||
|
|
mstep = 2 * int(self.lane.hscale)
|
||
|
|
mmstep = mstep * self.lane.xs
|
||
|
|
marks = int(self.lane.xmax / mstep)
|
||
|
|
gy = len(content) * int(self.lane.yo)
|
||
|
|
|
||
|
|
g = self.container.g(id="gmarks_{}".format(index))
|
||
|
|
|
||
|
|
for i in range(marks + 1):
|
||
|
|
gg = self.element.path(id="gmark_{i}_{index}".format(i=i, index=index),
|
||
|
|
d="m {dx},0 0,{gy}".format(dx=i * mmstep, gy=gy),
|
||
|
|
style="stroke:#888;stroke-width:0.5;stroke-dasharray:1,3")
|
||
|
|
g.add(gg)
|
||
|
|
|
||
|
|
self.captext(g, self.lane, "head", -33 if (self.lane.yh0 > 0) else -13)
|
||
|
|
self.captext(g, self.lane, "foot", gy + (45 if (self.lane.yf0 > 0) else 25))
|
||
|
|
self.ticktock(g, self.lane, "head", "tick", 0, mmstep, -5, marks + 1)
|
||
|
|
self.ticktock(g, self.lane, "head", "tock", mmstep / 2, mmstep, -5, marks)
|
||
|
|
self.ticktock(g, self.lane, "foot", "tick", 0, mmstep, gy + 15, marks + 1)
|
||
|
|
self.ticktock(g, self.lane, "foot", "tock", mmstep / 2, mmstep, gy + 15, marks)
|
||
|
|
|
||
|
|
return g
|
||
|
|
|
||
|
|
def render_labels(self, root, source, index):
|
||
|
|
if source:
|
||
|
|
gg = self.container.g(id="labels_{index}".format(index=index))
|
||
|
|
|
||
|
|
for idx, val in enumerate(source):
|
||
|
|
self.lane.period = val.get("period", 1)
|
||
|
|
self.lane.phase = val.get("phase", 0) * 2
|
||
|
|
|
||
|
|
dy = self.lane.y0 + idx * self.lane.yo
|
||
|
|
g = self.container.g(id="labels_{i}_{index}".format(i=idx, index=index))
|
||
|
|
g.translate(0, dy)
|
||
|
|
|
||
|
|
label = val.get("label")
|
||
|
|
if label:
|
||
|
|
pos = 0
|
||
|
|
for l in re.findall(r"([\.\w]|(?:\{\w+\}))(?:\((\d*\.?\d+)\))?", label):
|
||
|
|
if l[0] == ".":
|
||
|
|
pos += 1
|
||
|
|
continue
|
||
|
|
|
||
|
|
text = l[0]
|
||
|
|
try:
|
||
|
|
offset = float(l[1])
|
||
|
|
except ValueError:
|
||
|
|
offset = 0
|
||
|
|
|
||
|
|
m = re.match(r"\{(\w+)\}", l[0])
|
||
|
|
if m:
|
||
|
|
text = m.group(1)
|
||
|
|
x = int(float(self.lane.xs) * (2 * (pos + offset) * self.lane.period *
|
||
|
|
self.lane.hscale - self.lane.phase) + float(self.lane.xlabel))
|
||
|
|
y = int(idx * self.lane.yo + self.lane.y0 + float(self.lane.ys) * 0.5) - dy
|
||
|
|
|
||
|
|
lwidth = len(text) * self.font_width
|
||
|
|
lx = float(x) - float(lwidth) / 2
|
||
|
|
ly = int(y) - 5
|
||
|
|
underlabel = self.element.rect(insert=(lx, ly),
|
||
|
|
size=(lwidth, 8), style="fill:#FFF;")
|
||
|
|
g.add(underlabel)
|
||
|
|
lx = float(x)
|
||
|
|
ly = int(y) + 2
|
||
|
|
label = self.element.text(text, style="font-size:8px;", text_anchor="middle",
|
||
|
|
x=[lx], y=[ly])
|
||
|
|
g.add(label)
|
||
|
|
pos += 1
|
||
|
|
gg.add(g)
|
||
|
|
root.add(gg)
|
||
|
|
|
||
|
|
def arc_shape(self, Edge, frm, to):
|
||
|
|
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
|
||
|
|
|
||
|
|
const_style = AttrDict({
|
||
|
|
"a": "marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none",
|
||
|
|
"b": "marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none"
|
||
|
|
})
|
||
|
|
|
||
|
|
pattern = {
|
||
|
|
"-": { },
|
||
|
|
"~": {"d": "M {fx},{fy} c {dx},{dy} {dxx},{dyy} {dxxx},{dyyy}".format(fx=frm.x, fy=frm.y,
|
||
|
|
dx=(0.7 * dx), dy=0,
|
||
|
|
dxx=(0.3 * dx), dyy=dy,
|
||
|
|
dxxx=dx, dyyy=dy)},
|
||
|
|
"-~": {"d": "M {fx},{fy} c {dx},{dy} {dxx},{dyy} {dxxx},{dyyy}".format(fx=frm.x, fy=frm.y,
|
||
|
|
dx=(0.7 * dx), dy=0,
|
||
|
|
dxx=dx, dyy=dy,
|
||
|
|
dxxx=dx, dyyy=dy)},
|
||
|
|
"~-": {"d": "M {fx},{fy} c {dx},{dy} {dxx},{dyy} {dxxx},{dyyy}".format(fx=frm.x, fy=frm.y,
|
||
|
|
dx=0, dy=0,
|
||
|
|
dxx=(0.3 * dx), dyy=dy,
|
||
|
|
dxxx=dx, dyyy=dy)},
|
||
|
|
"-|": {"d": "m {fx},{fy} {dx},{dy} {dxx},{dyy}".format(fx=frm.x, fy=frm.y,
|
||
|
|
dx=dx, dy=0,
|
||
|
|
dxx=0, dyy=dy)},
|
||
|
|
"|-": {"d": "m {fx},{fy} {dx},{dy} {dxx},{dyy}".format(fx=frm.x, fy=frm.y,
|
||
|
|
dx=0, dy=dy,
|
||
|
|
dxx=dx, dyy=0)},
|
||
|
|
"-|-": {"d": "m {fx},{fy} {dx},{dy} {dxx},{dyy} {dxxx},{dyyy}".format(fx=frm.x, fy=frm.y,
|
||
|
|
dx=(dx / 2), dy=0,
|
||
|
|
dxx=0, dyy=dy,
|
||
|
|
dxxx=(dx / 2), dyyy=0)},
|
||
|
|
"->": {"style": const_style.a},
|
||
|
|
"~>": {"style": const_style.a,
|
||
|
|
"d": "M {fx},{fy} c {dx},{dy} {dxx},{dyy} {dxxx},{dyyy}".format(fx=frm.x, fy=frm.y,
|
||
|
|
dx=(0.7 * dx), dy=0,
|
||
|
|
dxx=(0.3 * dx), dyy=dy,
|
||
|
|
dxxx=dx, dyyy=dy)},
|
||
|
|
"-~>": {"style": const_style.a,
|
||
|
|
"d": "M {fx},{fy} c {dx},{dy} {dxx},{dyy} {dxxx},{dyyy}".format(fx=frm.x, fy=frm.y,
|
||
|
|
dx=(0.7 * dx), dy=0,
|
||
|
|
dxx=dx, dyy=dy,
|
||
|
|
dxxx=dx, dyyy=dy)},
|
||
|
|
"~->": {"style": const_style.a,
|
||
|
|
"d": "M {fx},{fy} c {dx},{dy} {dxx},{dyy} {dxxx},{dyyy}".format(fx=frm.x, fy=frm.y,
|
||
|
|
dx=0, dy=0,
|
||
|
|
dxx=(0.3 * dx), dyy=dy,
|
||
|
|
dxxx=dx, dyyy=dy)},
|
||
|
|
"-|>": {"style": const_style.a,
|
||
|
|
"d": "m {fx},{fy} {dx},{dy} {dxx},{dyy}".format(fx=frm.x, fy=frm.y,
|
||
|
|
dx=dx, dy=0,
|
||
|
|
dxx=0, dyy=dy)},
|
||
|
|
"|->": {"style": const_style.a,
|
||
|
|
"d": "m {fx},{fy} {dx},{dy} {dxx},{dyy}".format(fx=frm.x, fy=frm.y,
|
||
|
|
dx=0, dy=dy,
|
||
|
|
dxx=dx, dyy=0
|
||
|
|
)},
|
||
|
|
"-|->": {"style": const_style.a,
|
||
|
|
"d": "m {fx},{fy} {dx},{dy} {dxx},{dyy} {dxxx},{dyyy}".format(fx=frm.x, fy=frm.y,
|
||
|
|
dx=(dx / 2), dy=0,
|
||
|
|
dxx=0, dyy=dy,
|
||
|
|
dxxx=(dx / 2), dyyy=0
|
||
|
|
)},
|
||
|
|
"<->": {"style": const_style.b},
|
||
|
|
"<~>": {"style": const_style.b,
|
||
|
|
"d": "M {fx},{fy} c {dx},{dy} {dxx},{dyy} {dxxx},{dyyy}".format(fx=frm.x, fy=frm.y,
|
||
|
|
dx=(0.7 * dx), dy=0,
|
||
|
|
dxx=(0.3 * dx), dyy=dy,
|
||
|
|
dxxx=dx, dyyy=dy
|
||
|
|
)},
|
||
|
|
"<-~>": {"style": const_style.b,
|
||
|
|
"d": "M {fx},{fy} c {dx},{dy} {dxx},{dyy} {dxxx},{dyyy}".format(fx=frm.x, fy=frm.y,
|
||
|
|
dx=(0.7 * dx), dy=0,
|
||
|
|
dxx=dx, dyy=dy,
|
||
|
|
dxxx=dx, dyyy=dy
|
||
|
|
)},
|
||
|
|
"<-|>": {"style": const_style.b,
|
||
|
|
"d": "m {fx},{fy} {dx},{dy} {dxx},{dyy}".format(fx=frm.x, fy=frm.y,
|
||
|
|
dx=dx, dy=0,
|
||
|
|
dxx=0, dyy=dy
|
||
|
|
)},
|
||
|
|
"<-|->": {"style": const_style.b,
|
||
|
|
"d": "m {fx},{fy} {dx},{dy} {dxx},{dyy} {dxxx},{dyyy}".format(fx=frm.x, fy=frm.y,
|
||
|
|
dx=(dx / 2), dy=0,
|
||
|
|
dxx=0, dyy=dy,
|
||
|
|
dxxx=(dx / 2), dyyy=0,
|
||
|
|
)}
|
||
|
|
}
|
||
|
|
|
||
|
|
props = AttrDict({"lx": lx, "ly": ly, "style": "fill:none;stroke:#00F;stroke-width:1",
|
||
|
|
"d": "M {fx},{fy} {tx},{ty}".format(fx=frm.x, fy=frm.y, tx=to.x, ty=to.y)})
|
||
|
|
|
||
|
|
if Edge.shape in pattern:
|
||
|
|
props.d = pattern[Edge.shape].get("d", props.d)
|
||
|
|
props.style = pattern[Edge.shape].get("style", props.style)
|
||
|
|
|
||
|
|
if Edge.label:
|
||
|
|
if Edge.shape in ["-~", "-~>", "<-~>"]:
|
||
|
|
props.lx = float(frm.x) + (float(to.x) - float(frm.x)) * 0.75
|
||
|
|
elif Edge.shape in ["~-", "~->"]:
|
||
|
|
props.lx = float(frm.x) + (float(to.x) - float(frm.x)) * 0.25
|
||
|
|
elif Edge.shape in ["-|", "-|>", "<-|>"]:
|
||
|
|
props.lx = float(to.x)
|
||
|
|
elif Edge.shape in ["|-", "|->"]:
|
||
|
|
props.lx = float(frm.x)
|
||
|
|
|
||
|
|
return props
|
||
|
|
|
||
|
|
def render_arc(self, Edge, frm, to, shapeProps):
|
||
|
|
return self.element.path(id="gmark_{frm}_{to}".format(frm=Edge.frm, to=Edge.to),
|
||
|
|
d=shapeProps.d, style=shapeProps.style)
|
||
|
|
|
||
|
|
def render_label(self, p, text):
|
||
|
|
w = self.text_width(text,8) + 2
|
||
|
|
g = self.container.g(transform = "translate({},{})".format(p.x, p.y))
|
||
|
|
# todo: I don't think this is correct. reported:
|
||
|
|
# https://github.com/wavedrom/wavedrom/issues/252
|
||
|
|
rect = self.element.rect(insert=(int(0-w/2), -5), size=(w, 10), style="fill:#FFF;")
|
||
|
|
label = self.element.text("", style="font-size:8px;", text_anchor="middle", y=[3])
|
||
|
|
label.add(self.element.tspan(text))
|
||
|
|
g.add(rect)
|
||
|
|
g.add(label)
|
||
|
|
return g
|
||
|
|
|
||
|
|
def render_arcs(self, source, index, top):
|
||
|
|
Edge = AttrDict({"words": [], "frm": 0, "shape": "", "to": 0, "label": ""})
|
||
|
|
Events = AttrDict({})
|
||
|
|
|
||
|
|
if source:
|
||
|
|
for idx, val in enumerate(source):
|
||
|
|
self.lane.period = val.get("period", 1)
|
||
|
|
self.lane.phase = val.get("phase", 0) * 2
|
||
|
|
text = val.get("node")
|
||
|
|
if text:
|
||
|
|
Stack = list(text)
|
||
|
|
Stack.reverse()
|
||
|
|
pos = 0
|
||
|
|
step = 1
|
||
|
|
while len(Stack) > 0:
|
||
|
|
eventname = Stack.pop()
|
||
|
|
if eventname == "<":
|
||
|
|
step = 0.25
|
||
|
|
continue
|
||
|
|
elif eventname == ">":
|
||
|
|
step = 1
|
||
|
|
continue
|
||
|
|
x = int(float(self.lane.xs) * (2 * pos * self.lane.period *
|
||
|
|
self.lane.hscale - self.lane.phase) + float(self.lane.xlabel))
|
||
|
|
y = int(idx * self.lane.yo + self.lane.y0 + float(self.lane.ys) * 0.5)
|
||
|
|
if eventname != ".":
|
||
|
|
Events[eventname] = AttrDict({"x": str(x), "y": str(y)})
|
||
|
|
pos += step
|
||
|
|
|
||
|
|
gg = self.container.g(id="wavearcs_{index}".format(index=index))
|
||
|
|
|
||
|
|
if top.get("edge"):
|
||
|
|
for i, val in enumerate(top["edge"]):
|
||
|
|
Edge.words = val.split()
|
||
|
|
Edge.label = val[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 = AttrDict(Events[Edge.frm])
|
||
|
|
to = AttrDict(Events[Edge.to])
|
||
|
|
|
||
|
|
shapeProps = self.arc_shape(Edge, frm, to)
|
||
|
|
gg.add(self.render_arc(Edge, frm, to, shapeProps))
|
||
|
|
|
||
|
|
if Edge.label:
|
||
|
|
gg.add(self.render_label(AttrDict({"x": shapeProps.lx, "y": shapeProps.ly}), Edge.label))
|
||
|
|
|
||
|
|
|
||
|
|
for k in Events:
|
||
|
|
if k.islower() or k.isdigit():
|
||
|
|
if int(Events[k].x) > 0:
|
||
|
|
gg.add(self.render_label(AttrDict({"x": Events[k].x, "y": Events[k].y}), k))
|
||
|
|
|
||
|
|
return gg
|
||
|
|
|
||
|
|
def parse_config(self, source={}):
|
||
|
|
self.lane.hscale = 1
|
||
|
|
if self.lane.get("hscale0"):
|
||
|
|
self.lane.hscale = self.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
|
||
|
|
self.lane.hscale = hscale
|
||
|
|
|
||
|
|
self.lane.xmin_cfg = 0
|
||
|
|
self.lane.xmax_cfg = sys.maxsize
|
||
|
|
if source and "config" in source and "hbounds" in source["config"]:
|
||
|
|
if len(source["config"]["hbounds"]) == 2:
|
||
|
|
source["config"]["hbounds"][0] = math.floor(source["config"]["hbounds"][0])
|
||
|
|
source["config"]["hbounds"][1] = math.ceil(source["config"]["hbounds"][0])
|
||
|
|
if source["config"]["hbounds"][0] < source["config"]["hbounds"][1]:
|
||
|
|
self.lane.xmin_cfg = 2 * source["config"]["hbounds"][0]
|
||
|
|
self.lane.xmax_cfg = 2 * source["config"]["hbounds"][1]
|
||
|
|
|
||
|
|
self.lane.yh0 = 0
|
||
|
|
self.lane.yh1 = 0
|
||
|
|
if source and source.get("head"):
|
||
|
|
self.lane.head = source["head"]
|
||
|
|
if "tick" in source["head"] or "tock" in source["head"]:
|
||
|
|
self.lane.yh0 = 20
|
||
|
|
if "tick" in source["head"]:
|
||
|
|
source["head"]["tick"] += self.lane.xmin_cfg/2
|
||
|
|
if "tock" in source["head"]:
|
||
|
|
source["head"]["tock"] += self.lane.xmin_cfg/2
|
||
|
|
if source.get("head").get("text"):
|
||
|
|
self.lane.yh1 = 46
|
||
|
|
self.lane.head["text"] = source["head"]["text"]
|
||
|
|
|
||
|
|
self.lane.yf0 = 0
|
||
|
|
self.lane.yf1 = 0
|
||
|
|
if source and source.get("foot"):
|
||
|
|
self.lane.foot = source["foot"]
|
||
|
|
if "tick" in source["foot"] or "tock" in source["foot"]:
|
||
|
|
self.lane.yf0 = 20
|
||
|
|
if "tick" in source["foot"]:
|
||
|
|
source["foot"]["tick"] += self.lane.xmin_cfg/2
|
||
|
|
if "tock" in source["foot"]:
|
||
|
|
source["foot"]["tock"] += self.lane.xmin_cfg/2
|
||
|
|
if source.get("foot").get("text"):
|
||
|
|
self.lane.yf1 = 46
|
||
|
|
self.lane.foot["text"] = source["foot"]["text"]
|
||
|
|
|
||
|
|
def rec(self, tmp=[], state={}):
|
||
|
|
name = None
|
||
|
|
delta = AttrDict({"x": 10})
|
||
|
|
if isinstance(tmp[0], str) or isinstance(tmp[0], int):
|
||
|
|
name = str(tmp[0])
|
||
|
|
delta.x = 25
|
||
|
|
|
||
|
|
state.x += delta.x
|
||
|
|
for idx, val in enumerate(tmp):
|
||
|
|
if isinstance(val, list):
|
||
|
|
old_y = state.y
|
||
|
|
self.rec(val, state)
|
||
|
|
state["groups"].append({"x": state.xx,
|
||
|
|
"y": old_y,
|
||
|
|
"height": state.y - old_y,
|
||
|
|
"name": state.name})
|
||
|
|
elif isinstance(val, dict):
|
||
|
|
state["lanes"].append(val)
|
||
|
|
state["width"].append(state.x)
|
||
|
|
state.y += 1
|
||
|
|
|
||
|
|
state.xx = state.x
|
||
|
|
state.x -= delta.x
|
||
|
|
state.name = name
|
||
|
|
|
||
|
|
def another_template(self, index, source):
|
||
|
|
def get_container(elem):
|
||
|
|
ctype = elem[0]
|
||
|
|
ret = self.container[ctype]()
|
||
|
|
ret.attribs = elem[1]
|
||
|
|
|
||
|
|
def gen_elem(e):
|
||
|
|
if e[0] == "path":
|
||
|
|
attr = e[1]
|
||
|
|
elem = self.element.path(d=attr["d"])
|
||
|
|
elem.attribs = attr
|
||
|
|
elif e[0] == "rect":
|
||
|
|
attr = e[1]
|
||
|
|
x = attr["x"]
|
||
|
|
y = attr["y"]
|
||
|
|
w = attr["width"]
|
||
|
|
h = attr["height"]
|
||
|
|
elem = self.element.rect(insert=(x, y), size=(w, h))
|
||
|
|
elem.attribs = attr
|
||
|
|
|
||
|
|
return elem
|
||
|
|
[ret.add(gen_elem(e)) for e in elem[2:]]
|
||
|
|
|
||
|
|
return ret
|
||
|
|
|
||
|
|
skinname = source.get("config", {"skin" : "default"}).get("skin", "default")
|
||
|
|
skin = waveskin.WaveSkin.get(skinname, waveskin.WaveSkin["default"])
|
||
|
|
|
||
|
|
template = svgwrite.Drawing(id="svgcontent_{index}".format(index=index))
|
||
|
|
if index == 0:
|
||
|
|
template.add(template.style(skin[2][2]))
|
||
|
|
[template.defs.add(get_container(e)) for e in skin[3][1:]]
|
||
|
|
self.lane.xs = int(skin[3][1][2][1]["width"])
|
||
|
|
self.lane.ys = int(skin[3][1][2][1]["height"])
|
||
|
|
self.lane.xlabel = int(skin[3][1][2][1]["x"])
|
||
|
|
self.lane.ym = int(skin[3][1][2][1]["y"])
|
||
|
|
|
||
|
|
template["class"] = "WaveDrom"
|
||
|
|
template["overflow"] = "hidden"
|
||
|
|
|
||
|
|
return template
|
||
|
|
|
||
|
|
def insert_svg_template(self, index=0, 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:
|
||
|
|
self.lane.xs = int(e[3][1][2][1]["width"])
|
||
|
|
self.lane.ys = int(e[3][1][2][1]["height"])
|
||
|
|
self.lane.xlabel = int(e[3][1][2][1]["x"])
|
||
|
|
self.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"},
|
||
|
|
[ # e[-1]
|
||
|
|
"g", # e[-1][0]
|
||
|
|
{"id": "waves"}, # e[-1][1]
|
||
|
|
[ # e[-1][2]
|
||
|
|
"g", # e[-1][2][0]
|
||
|
|
{"id": "lanes"} # e[-1][2][1]
|
||
|
|
],
|
||
|
|
[ # e[-1][3]
|
||
|
|
"g", # e[-1][3][0]
|
||
|
|
{"id": "groups"} # e[-1][3][1]
|
||
|
|
]
|
||
|
|
]
|
||
|
|
]
|
||
|
|
|
||
|
|
e[-1][1]["id"] = "waves_{index}".format(index=index)
|
||
|
|
e[-1][2][1]["id"] = "lanes_{index}".format(index=index)
|
||
|
|
e[-1][3][1]["id"] = "groups_{index}".format(index=index)
|
||
|
|
e[1]["id"] = "svgcontent_{index}".format(index=index)
|
||
|
|
e[1]["height"] = 0
|
||
|
|
|
||
|
|
parent.extend(e)
|
||
|
|
|
||
|
|
def render_waveform(self, index=0, source={}, output=[], strict_js_features=False):
|
||
|
|
xmax = 0
|
||
|
|
|
||
|
|
if source.get("signal"):
|
||
|
|
template = self.another_template(index, source)
|
||
|
|
waves = template.g(id="waves_{index}".format(index=index))
|
||
|
|
lanes = template.g(id="lanes_{index}".format(index=index))
|
||
|
|
groups = template.g(id="groups_{index}".format(index=index))
|
||
|
|
self.parse_config(source)
|
||
|
|
ret = AttrDict({"x": 0, "y": 0, "xmax": 0, "width": [], "lanes": [], "groups": []})
|
||
|
|
self.rec(source["signal"], ret) # parse lanes
|
||
|
|
content = self.parse_wave_lanes(ret.lanes)
|
||
|
|
(glengths, lanegroups) = self.render_wave_lane(content, index)
|
||
|
|
for i, val in enumerate(glengths):
|
||
|
|
xmax = max(xmax, (val + ret.width[i]))
|
||
|
|
marks = self.render_marks(content, index)
|
||
|
|
gaps = self.render_gaps(ret.lanes, index)
|
||
|
|
if not strict_js_features:
|
||
|
|
self.render_labels(lanes, ret.lanes, index)
|
||
|
|
arcs = self.render_arcs(ret.lanes, index, source)
|
||
|
|
|
||
|
|
# Render
|
||
|
|
lanes.add(marks)
|
||
|
|
[lanes.add(l) for l in lanegroups]
|
||
|
|
lanes.add(arcs)
|
||
|
|
lanes.add(gaps)
|
||
|
|
|
||
|
|
self.render_groups(groups, ret.groups, index)
|
||
|
|
self.lane.xg = int(math.ceil(float(xmax - self.lane.tgo) / float(self.lane.xs))) * self.lane.xs
|
||
|
|
width = self.lane.xg + self.lane.xs * (self.lane.xmax + 1)
|
||
|
|
height = len(content) * self.lane.yo + self.lane.yh0 + self.lane.yh1 + self.lane.yf0 + self.lane.yf1
|
||
|
|
template["width"] = width
|
||
|
|
template["height"] = height
|
||
|
|
template.viewbox(0, 0, width, height)
|
||
|
|
dx = self.lane.xg + 0.5
|
||
|
|
dy = float(self.lane.yh0) + float(self.lane.yh1) + 0.5
|
||
|
|
lanes.translate(dx, dy)
|
||
|
|
|
||
|
|
waves.add(lanes)
|
||
|
|
waves.add(groups)
|
||
|
|
template.add(waves)
|
||
|
|
return template
|
||
|
|
|
||
|
|
def render_groups(self, root=[], groups=[], index=0):
|
||
|
|
for i, val in enumerate(groups):
|
||
|
|
dx = groups[i]["x"] + 0.5
|
||
|
|
dy = groups[i]["y"] * self.lane.yo + 3.5 + self.lane.yh0 + self.lane.yh1
|
||
|
|
h = int(groups[i]["height"] * self.lane.yo - 16)
|
||
|
|
group = self.element.path(id="group_{i}_{index}".format(i=i, index=index),
|
||
|
|
d="m {dx},{dy} c -3,0 -5,2 -5,5 l 0,{h} c 0,3 2,5 5,5".format(dx=dx, dy=dy, h=h),
|
||
|
|
style="stroke:#0041c4;stroke-width:1;fill:none")
|
||
|
|
|
||
|
|
root.add(group)
|
||
|
|
|
||
|
|
name = groups[i]["name"]
|
||
|
|
x = int(groups[i]["x"] - 10)
|
||
|
|
y = int(self.lane.yo * (groups[i]["y"] + (float(groups[i]["height"]) / 2)) +
|
||
|
|
self.lane.yh0 + self.lane.yh1)
|
||
|
|
label = self.container.g()
|
||
|
|
label.translate(x, y)
|
||
|
|
gg = self.container.g()
|
||
|
|
gg.rotate(270)
|
||
|
|
t = self.element.text("", text_anchor="middle")
|
||
|
|
t["class"] = "info"
|
||
|
|
t["xml:space"] = "preserve"
|
||
|
|
t.add(self.element.tspan(name))
|
||
|
|
gg.add(t)
|
||
|
|
label.add(gg)
|
||
|
|
root.add(label)
|
||
|
|
|
||
|
|
def render_gap_uses(self, wave, g):
|
||
|
|
subCycle = False
|
||
|
|
|
||
|
|
if wave:
|
||
|
|
Stack = deque(wave)
|
||
|
|
pos = 0
|
||
|
|
while len(Stack):
|
||
|
|
next = Stack.popleft()
|
||
|
|
if next == '<':
|
||
|
|
subCycle = True
|
||
|
|
continue
|
||
|
|
if next == '>':
|
||
|
|
subCycle = False
|
||
|
|
continue
|
||
|
|
if subCycle:
|
||
|
|
pos += self.lane.period
|
||
|
|
else:
|
||
|
|
pos += 2 * self.lane.period
|
||
|
|
if next == "|":
|
||
|
|
if subCycle:
|
||
|
|
dx = float(self.lane.xs) * (pos * float(self.lane.hscale) - float(self.lane.phase))
|
||
|
|
else:
|
||
|
|
dx = float(self.lane.xs) * ((pos - self.lane.period) * float(self.lane.hscale) - float(self.lane.phase))
|
||
|
|
b = self.container.use(href="#gap")
|
||
|
|
b.translate(dx)
|
||
|
|
g.add(b)
|
||
|
|
|
||
|
|
def render_gaps(self, source, index):
|
||
|
|
if source:
|
||
|
|
gg = self.container.g(id="wavegaps_{index}".format(index=index))
|
||
|
|
|
||
|
|
for idx, val in enumerate(source):
|
||
|
|
self.lane.period = val.get("period", 1)
|
||
|
|
self.lane.phase = int(val.get("phase", 0) * 2) + self.lane.xmin_cfg
|
||
|
|
|
||
|
|
dy = self.lane.y0 + idx * self.lane.yo
|
||
|
|
g = self.container.g(id="wavegap_{i}_{index}".format(i=idx, index=index))
|
||
|
|
g.translate(0, dy)
|
||
|
|
|
||
|
|
if "wave" in val:
|
||
|
|
self.render_gap_uses(val["wave"], g)
|
||
|
|
|
||
|
|
gg.add(g)
|
||
|
|
|
||
|
|
return gg
|
||
|
|
|
||
|
|
def convert_to_svg(self, root):
|
||
|
|
svg_output = ""
|
||
|
|
|
||
|
|
if type(root) is list:
|
||
|
|
if len(root) >= 2 and type(root[1]) is dict:
|
||
|
|
if len(root) == 2:
|
||
|
|
svg_output += "<{}{}/>\n".format(root[0], self.convert_to_svg(root[1]))
|
||
|
|
elif len(root) >= 3:
|
||
|
|
svg_output += "<{}{}/>\n".format(root[0], self.convert_to_svg(root[1]))
|
||
|
|
if len(root) == 3:
|
||
|
|
svg_output += self.convert_to_svg(root[2])
|
||
|
|
else:
|
||
|
|
svg_output += self.convert_to_svg(root[2:])
|
||
|
|
svg_output += "</{}>\n".format(root[0])
|
||
|
|
elif type(root[0]) is list:
|
||
|
|
for eleml in root:
|
||
|
|
svg_output += self.convert_to_svg(eleml)
|
||
|
|
else:
|
||
|
|
svg_output += "<{}>\n".format(root[0])
|
||
|
|
for eleml in root[1:]:
|
||
|
|
svg_output += self.convert_to_svg(eleml)
|
||
|
|
svg_output += "</{}>\n".format(root[0])
|
||
|
|
elif type(root) is dict:
|
||
|
|
for elemd in root:
|
||
|
|
svg_output += " {}=\"{}\"".format(elemd, root[elemd])
|
||
|
|
else:
|
||
|
|
svg_output += root
|
||
|
|
|
||
|
|
return svg_output
|
||
|
|
|
||
|
|
# Backward compatibility
|
||
|
|
genWaveBrick = gen_wave_brick
|
||
|
|
parseWaveLane = parse_wave_lane
|
||
|
|
parseWaveLanes = parse_wave_lanes
|
||
|
|
findLaneMarkers = find_lane_markers
|
||
|
|
renderWaveLane = render_wave_lane
|
||
|
|
renderMarks = render_marks
|
||
|
|
renderLabels = render_labels
|
||
|
|
renderArcs = render_arcs
|
||
|
|
parseConfig = parse_config
|
||
|
|
anotherTemplate = another_template
|
||
|
|
insertSVGTemplate = insert_svg_template
|
||
|
|
renderWaveForm = render_waveform
|
||
|
|
renderGroups = render_groups
|
||
|
|
renderGaps = render_gaps
|