123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- """
- 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 <https://graphviz.org/>`_ 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=<<B>{n} ({t})</B>>;
- 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()
|