Files
context-continuity/venv.old-py38/lib/python3.9/site-packages/wavedrom/waveform.py
T

926 lines
40 KiB
Python
Raw Normal View History

# 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