""" 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()