mingrammer / diagrams

:art: Diagram as Code for prototyping cloud system architectures
https://diagrams.mingrammer.com
MIT License
37.35k stars 2.43k forks source link

Generic shapes question #290

Open piotr-sokolowski opened 4 years ago

piotr-sokolowski commented 4 years ago

Do you plan to support more generic node types like for example simple shapes ? This would allow to use diagrams when none of existing specific nodes really match like when creating process or algorithm block diagram

craighurley commented 4 years ago

I would like to create a simple diagram to describe relationships between AWS accounts and it appears there is no generic box.

steschuser commented 4 years ago

How would a generic box look like? If you have icons I'll try to include it.

wolfspyre commented 4 years ago

i’d imagine (LMK if i’m misinterpreting) the desired behavior would be that one could create a cluster-like visual bounding container to represent that some amalgam of resources are physically or logically connected..

the use cases for such a primitive are myriad. the datacenter, subnet, and rack node types are examples that come immediately to my mind

steschuser commented 3 years ago

well, if you can link me some nice looking shapes that are free to use Id be happy to include them..

ludwigm commented 3 years ago

I am also in need for exactly this.

The question for me would be if it really needs to be a png shape? It would be awesome to use all what diagrams offers with the additional shape options of graphviz. Happy to have that only limited to a rectangle with text to start with: https://graphviz.org/doc/info/shapes.html

Is there anything speaking against this? :)

Thanks.

clayms commented 3 years ago

If all you want is a box with text, you can just use the Custom node class and pass in an empty string as your node image.

from diagrams import Diagram
from diagrams.custom import Custom

with Diagram("\nCustom", show=False) as diag:
    Custom_1 = Custom("My\nCustom\ntext #1\n\n\nHELLO", "")
    Custom_2 = Custom("My 2nd\nCustom\nstring\n\n\nWORLD!", "")

    Custom_1 >> Edge(label="WEEE!!!") >> Custom_2

diag

image

clayms commented 3 years ago

As a longer term solution, I would suggest adding a Polygon node class, that behaves similar to the Custom node class, with the additional option to pass in the following keyword arguments:

  1. sides,
  2. distortion,
  3. orientation,
  4. skew,
  5. color

Have a look at this Graphviz example: https://www.graphviz.org/Gallery/directed/crazy.html

clayms commented 3 years ago

@steschuser @ludwigm @wolfspyre @craighurley

You can use all of the Graphviz shapes with the Node class. See code and output below. https://graphviz.gitlab.io/doc/info/shapes.html

Additionally, all of the Graphviz Node attributes should also be available to you. https://graphviz.gitlab.io/doc/info/attrs.html

from diagrams import Diagram, Node
from diagrams.custom import Custom

shapes = [
    "box","polygon","ellipse","oval","circle",
    "point","egg","triangle","plaintext","plain",
    "diamond","trapezium","parallelogram","house","pentagon",
    "hexagon","septagon","octagon","doublecircle","doubleoctagon",
    "Mdiamond","Msquare","Mcircle",
    "rect","rectangle","square","star","none","underline","cylinder",
    "tripleoctagon","invtriangle","invtrapezium","invhouse",
    "note","tab","folder","box3d","component","promoter",
    "cds","terminator","utr","primersite","restrictionsite",
    "fivepoverhang","threepoverhang","noverhang","assembly",
    "signature","insulator","ribosite","rnastab","proteasesite",
    "proteinstab","rpromoter","rarrow","larrow","lpromoter", ]

num_shapes = len(shapes)
shapes_per_row = 5
num_of_rows = int(num_shapes / shapes_per_row) + (num_shapes % shapes_per_row > 0)

with Diagram("\n\nUsing Graphviz Shapes", show=False) as diag:
    for row in range(num_of_rows)[::-1]:
        items_in_row = shapes_per_row - (row+1) * shapes_per_row // num_shapes
        shapes_i = row * shapes_per_row
        node_list = [
               'Node('
                f'shape="{shapes[shapes_i+item_num]}", '
                f'label="\\n"+"{shapes[shapes_i+item_num]}", '
                'labelloc="t", '
                'style="solid") - Edge(penwidth="0.0")'
            for item_num in range(items_in_row)[:-1]
        ] + ['Node('
                f'shape="{shapes[shapes_i+items_in_row-1]}", '
                f'label="\\n"+"{shapes[shapes_i+items_in_row-1]}", '
                'labelloc="t", '
                'style="solid")']

        node_row = "-".join(node_list)
        eval(node_row)

diag

using_graphviz_shapes

wolfspyre commented 3 years ago

I for one, really appreciate all your efforts to help out here, (and all the other places where you’ve helped others, with example code even!!!) thank you, @clayms the world needs more contributors like you :)

@mingrammer - assuming you like the implementation as is, the above would be super useful as an example, and perhaps even a test to help ensure no future change unintentionally alters behavior.

ludwigm commented 3 years ago

@clayms This is fantastic. I was not aware that this is possible. I totally will give this a try :)

clayms commented 3 years ago

Here is how you can add "record" shaped Nodes and have the Edges point to specific fields of each "record" Node.

See: https://graphviz.gitlab.io/doc/info/shapes.html#record

with Diagram("\nRecord-based Nodes", show=False, direction="TB") as diag:
    TB_fields  = Node(shape="record", label="{ <t> top |<m> middle |<b> bottom }")
    TB_fields2 = Node(shape="record", label="{ <t> top |<m> middle |<b> bottom }")
    LR_fields  = Node(shape="record", label=" <f0> left|<f1> middle|<f2> right" )
    LR_fields2 = Node(shape="record", label=" <f0> left|<f1> middle|<f2> right")

    TB_fields  >> Edge(tailport="t", headport="b") >> TB_fields2

    LR_fields >> Edge(tailport="f2", headport="f0") >> LR_fields2

diag

image

clayms commented 3 years ago

@mingrammer If some form of the examples above were to be added to the documentation, which document do you prefer:

  1. examples.md
  2. node.md

I can see a case for having certain versions in both.

Regarding the Edge specific items tailport and headport, those may also be called out in edge.md.

With the ability to use all of the Graphviz node shapes, it is also possible to replicate some of the graphs from the Graphviz Gallery.

Let me know any particular preference you have and I can add them and create a pull request.

Thanks,