nidi3 / graphviz-java

Use graphviz with pure java
Apache License 2.0
937 stars 107 forks source link

FDP subgraphs nodes link to external node #195

Closed MadaManu closed 3 years ago

MadaManu commented 3 years ago

Example:

HOST_NETWORK_NODE = node("HOST")
                .with(Color.PERU, Size.mode(Size.Mode.FIXED).size(10, 1))
                .with(Shape.RECTANGLE)
        Graph g = graph("all")
                .graphAttr().with(splines(GraphAttr.SplineMode.POLYLINE))
                .with(
                        graph()
                                .directed()
                                .cluster().named("hostname")
                                .graphAttr().with(Label.of("hostname"))
                                .with(HOST_NETWORK_NODE)
                                .with(graph("subGraph1")
                                        .cluster().named("subGraph1")
                                        .graphAttr().with(Rank.dir(LEFT_TO_RIGHT))
                                        .graphAttr().with(Style.FILLED, Color.LIGHTGREY, Label.of("subGraph1"))
                                        .with(node("a")
                                                .link(to(HOST_NETWORK_NODE).with(Label.of("link1"))))
                                        .with(node("b"))
                                )
                                .with(graph("subGraph2")
                                        .cluster().named("subGraph2")
                                        .graphAttr().with(Rank.dir(LEFT_TO_RIGHT))
                                        .graphAttr().with(Style.FILLED, Color.LIGHTGREY, Label.of("subGraph2"))
                                        .with(node("c")
                                                .link(to(HOST_NETWORK_NODE).with(Label.of("link2"))))
                                        .with(node("d"))
                                )
                )

        Graphviz.fromGraph(g).engine(Engine.FDP).totalMemory(100_000_000).render(Format.DOT).toFile(new File("out/FDP.txt"))

        Graphviz.fromGraph(g).engine(Engine.FDP).totalMemory(100_000_000).render(Format.PNG).toFile(new File("out/image.png"))

ERROR in rendering PNG:

Exception in thread "main" guru.nidi.graphviz.engine.GraphvizException: Error: node "HOST" is contained in two non-comparable clusters "cluster_subGraph2" and "cluster_subGraph1"

Text file generated:

digraph "all" {
edge ["dir"="none"]
graph ["splines"="polyline"]
subgraph "cluster_hostname" {
edge ["dir"="forward"]
graph ["label"="hostname"]
"HOST" ["color"="peru","fixedsize"="true","width"="10.0","height"="1.0","shape"="rectangle"]
subgraph "cluster_subGraph1" {
edge ["dir"="none"]
graph ["rankdir"="LR","style"="filled","color"="lightgrey","label"="subGraph1"]
"HOST" ["color"="peru","fixedsize"="true","width"="10.0","height"="1.0","shape"="rectangle"]
"b"
"a" -> "HOST" ["label"="link1"]
}
subgraph "cluster_subGraph2" {
edge ["dir"="none"]
graph ["rankdir"="LR","style"="filled","color"="lightgrey","label"="subGraph2"]
"HOST" ["color"="peru","fixedsize"="true","width"="10.0","height"="1.0","shape"="rectangle"]
"d"
"c" -> "HOST" ["label"="link2"]
}
}
}

However, the expected generation for the expected outcome I am looking for would be:

Note the HOST node definition only once, compared to generated one where this node is defined in each subgraph. Also, note the links in the generated file are defined in each subgraph where the expected would be defined outside.

digraph "all" {
edge ["dir"="none"]
graph ["splines"="polyline"]
subgraph "cluster_hostname" {
edge ["dir"="forward"]
graph ["label"="hostname"]
"HOST" ["color"="peru","fixedsize"="true","width"="10.0","height"="1.0","shape"="rectangle"]
subgraph "cluster_subGraph1" {
edge ["dir"="none"]
graph ["rankdir"="LR","style"="filled","color"="lightgrey","label"="subGraph1"]
"b"
"a"
}
subgraph "cluster_subGraph2" {
edge ["dir"="none"]
graph ["rankdir"="LR","style"="filled","color"="lightgrey","label"="subGraph2"]
"d"
"c"
}
"a" -> "HOST" ["label"="link1"]
"c" -> "HOST" ["label"="link2"]
}
}

GraphvizOnline of expected

Image of expected (note the link between inner nodes of a subgraph to the HOST node.

Screenshot 2021-03-03 at 22 09 57

Same result using both 0.18.0 and 0.18.1.

Is there something that I'm missing in my code? How can I achieve the described expected outcome?

nidi3 commented 3 years ago

You should add the a->HOST and c->HOST links outside of the clusers, the same as in DOT:

        Graph g = graph("all")
                .graphAttr().with(splines(GraphAttr.SplineMode.POLYLINE))
                .with(
                        graph()
                                .directed()
                                .cluster().named("hostname")
                                .graphAttr().with(Label.of("hostname"))
                                .with(HOST_NETWORK_NODE)
                                .with(graph("subGraph1")
                                        .cluster().named("subGraph1")
                                        .graphAttr().with(Rank.dir(LEFT_TO_RIGHT))
                                        .graphAttr().with(Style.FILLED, Color.LIGHTGREY, Label.of("subGraph1"))
                                        .with(node("b"))
                                        .with(node("a"))
                                )
                                .with(graph("subGraph2")
                                        .cluster().named("subGraph2")
                                        .graphAttr().with(Rank.dir(LEFT_TO_RIGHT))
                                        .graphAttr().with(Style.FILLED, Color.LIGHTGREY, Label.of("subGraph2"))
                                        .with(node("c"))
                                        .with(node("d"))
                                )
                                .with(node("a")
                                        .link(to(HOST_NETWORK_NODE).with(Label.of("link1"))))
                                .with(node("c")
                                        .link(to(HOST_NETWORK_NODE).with(Label.of("link2"))))
                );
nidi3 commented 3 years ago

Unfortunately, with the JS engines, the resulting layout is not the same as with native dot. I don't know why.

MadaManu commented 3 years ago

That indeed solved my issue. Thank you @nidi3

nidi3 commented 3 years ago

Good to hear. @MadaManu So you use native dot engine and don't have an issue with the layout?

MadaManu commented 3 years ago

No issue with the layout. Works fine using both DOT and FDP engines.