"""
Useful drawing function to easily draw a CBD model in Graphviz.
"""
from CBD.Core import CBD
from CBD.lib.std import *
def gvDraw(cbd, filename, rankdir="LR", colors=None):
"""
Outputs a :class:`CBD` as a `GraphViz `_ script to filename.
For instance, drawing the CBD given in the :doc:`examples/EvenNumberGen` example, the following figure
can be obtained:
.. figure:: _figures/EvenNumberGV.svg
Note:
The resulting Graphviz file might look "clunky" and messy when rendering with
the standard :code:`dot` engine. The :code:`neato`, :code:`twopi` and :code:`circo`
engines might provide a cleaner and more readable result.
Args:
cbd (CBD): The :class:`CBD` to draw.
filename (str): The name of the dot-file.
rankdir (str): The GraphViz rankdir of the plot. Must be either :code:`TB`
or :code:`LR`.
colors (dict): An optional dictionary of :code:`blockname -> color`.
"""
# f = sys.stdout
f = open(filename, "w")
write = lambda s: f.write(s)
write("""// CBD model of the {n} block
// Created with CBD.converters.CBDDraw
digraph model {{
splines=ortho;
label=<{n} ({t})>;
labelloc=\"t\";
fontsize=20;
rankdir=\"{rd}\";
""".format(n=cbd.getPath(), t=cbd.getBlockType(), rd=rankdir))
if colors is None:
colors = {}
def writeBlock(block):
"""
Writes a block to graphviz.
Args:
block: The block to write.
"""
if isinstance(block, ConstantBlock):
label = " {}\\n({})\\n{}".format(block.getBlockType(), block.getBlockName(), block.getValue())
elif isinstance(block, GenericBlock):
label = " {}\\n({})\\n{}".format(block.getBlockType(), block.getBlockName(), block.getBlockOperator())
elif isinstance(block, ClampBlock) and block._use_const:
label = " {}\\n({})\\n[{}, {}]".format(block.getBlockType(), block.getBlockName(), block.min, block.max)
else:
label = block.getBlockType() + "\\n(" + block.getBlockName() + ")"
shape = "box"
if isinstance(block, CBD):
shape = "Msquare"
elif isinstance(block, ConstantBlock):
shape = "ellipse"
col = ""
if block.getBlockName() in colors:
col = ", color=\"{0}\", fontcolor=\"{0}\"".format(colors[block.getBlockName()])
write(" {b} [label=\"{lbl}\", shape={shape}{col}];\n".format(b=nodeName(block),
lbl=label,
shape=shape,
col=col))
def nodeName(block):
return "node_%d" % id(block)
for port in cbd.getInputPorts():
s = "%s_%s" % (nodeName(cbd), port.name)
write(" {s} [shape=point, width=0.01, height=0.01];\n".format(s=s))
i = "inter_%d_%s" % (id(port.block), port.name)
write(" {i} [shape=point, width=0.01, height=0.01];\n".format(i=i))
write(" {b} -> {i} [taillabel=\"{inp}\", arrowhead=\"none\", arrowtail=\"inv\", dir=both];\n".format(i=i, b=s, inp=port.name))
for block in cbd.getBlocks():
writeBlock(block)
for in_port in block.getInputPorts():
other = in_port.getIncoming().source
op = other.name
i = "inter_%d_%s" % (id(other.block), op)
write(" {i} -> {b} [headlabel=\"{inp}\", arrowhead=\"normal\", arrowtail=\"none\", dir=both];\n".format(i=i, b=nodeName(block), inp=in_port.name))
for op in block.getOutputPortNames():
i = "inter_%d_%s" % (id(block), op)
# if i not in conn: continue
write(" {i} [shape=point, width=0.01, height=0.01];\n".format(i=i))
write(" {a} -> {i} [taillabel=\"{out}\", arrowtail=\"oinv\", arrowhead=\"none\", dir=both];\n"\
.format(i=i, a=nodeName(block), out=op))
for port in cbd.getOutputPorts():
other = port.getIncoming().source
i = "inter_%d_%s" % (id(other.block), other.name)
t = "%s_%s" % (nodeName(cbd), port.name)
write(" {b} [shape=point, width=0.01, height=0.01];\n".format(b=t))
write(" {i} -> {b} [headlabel=\"{inp}\", arrowhead=\"onormal\", arrowtail=\"none\", dir=both];\n".format(i=i, b=t, inp=port.name))
write("\n}")
f.close()