GraphViz - Jak podłączyć podgrafy?

166

W DOTjęzyku dla GraphVizstaram się przedstawić diagram zależności. Potrzebuję mieć węzły w kontenerze i móc uzależniać węzły i / lub kontenery od innych węzłów i / lub kontenerów.

Używam subgraphdo reprezentowania moich pojemników. Łączenie węzłów działa dobrze, ale nie mogę dowiedzieć się, jak połączyć podgrafy.

Biorąc pod uwagę poniższy program, muszę być w stanie połączyć się cluster_1i cluster_2za pomocą strzałki, ale wszystko, czego próbowałem, tworzy nowe węzły zamiast łączyć klastry:

digraph G {

    graph [fontsize=10 fontname="Verdana"];
    node [shape=record fontsize=10 fontname="Verdana"];

    subgraph cluster_0 {
        node [style=filled];
        "Item 1" "Item 2";
        label = "Container A";
        color=blue;
    }

    subgraph cluster_1 {
        node [style=filled];
        "Item 3" "Item 4";
        label = "Container B";
        color=blue;
    }

    subgraph cluster_2 {
        node [style=filled];
        "Item 5" "Item 6";
        label = "Container C";
        color=blue;
    }

    // Renders fine
    "Item 1" -> "Item 2";
    "Item 2" -> "Item 3";

    // Both of these create new nodes
    cluster_1 -> cluster_2;
    "Container A" -> "Container C";
}

wprowadź opis obrazu tutaj

Winston Smith
źródło
2
Mam ten sam problem, ale mają naturalny przykład, w którym podgrafy działają jak węzły, graphviz.org/content/fdpclust .
nlucaroni
1
@nlucaroni Zastanawiam się, czy ten problem został rozwiązany. ten przykład daje mi zły wykres: krawędzie łączą środki podgrafu. nie wiesz, jak sprawić, by działało jak na przykładzie?
k102
1
@ k102, wiem. Sprawdź tę stronę ponownie; mówi, że musisz użyć fdp. Połączony przykład, jak i ten powyżej, działają (ostatnia linia w tym przykładzie musi zawierać nazwy podgrafów, a nie etykietę i może być miło dołączyć długości linii dla wykresu); jest trochę napięty).
nlucaroni
1
@nlucaroni Używając wersji fdp2.28.0 i kopiując / wklejając źródło z przykładu, linie łączą się ze środkiem podgrafu, a nie z krawędziami. Jeśli otworzysz .dot w OmniGraffle są prawidłowo podłączone, podczas neatoi dotzarówno tworzyć zbędnych węzłów do klastra.
Phrogz

Odpowiedzi:

190

Podręcznik użytkownika DOT zawiera następujący przykład wykresu z klastrami z krawędziami między skupieniami:

WAŻNE: compound=truewymagane jest wstępne oświadczenie.

digraph G {
  compound=true;
  subgraph cluster0 {
    a -> b;
    a -> c;
    b -> d;
    c -> d;
  }
  subgraph cluster1 {
    e -> g;
    e -> f;
  }
  b -> f [lhead=cluster1];
  d -> e;
  c -> g [ltail=cluster0,lhead=cluster1];
  c -> e [ltail=cluster0];
  d -> h;
}

... i krawędzie między węzłami i klastrami:

wprowadź opis obrazu tutaj

Znak wysokiej wydajności
źródło
14
Dzięki - to działa, ale naprawdę wydaje się brzydki hack. Mam nadzieję , że nie mam scenariusza, w którym mam kontener bez węzłów.
Winston Smith,
5
Jeśli ktoś jest zainteresowany, może to powodować problemy z pozycjonowaniem, jeśli oznaczyłeś linki (krawędzie). Podczas gdy główka lub koniec krawędzi może być ukryty pod klastrem, etykieta jest nadal umieszczona w punkcie środkowym, co oznacza, że ​​niektóre etykiety krawędzi wydają się unosić nad gromadą zamiast być umieszczane przez samą krawędź.
Winston Smith
58
@WinstonSmith: Stare pytanie, ale miałem podobny problem i rozwiązałem go za pomocą niewidocznego fałszywego węzła na klaster, z którym można się połączyć, nawet jeśli w przeciwnym razie klaster jest pusty. DUMMY_0 [shape=point style=invis]
DevSolar,
2
Zauważyłem, że moje krawędzie między klastrami są zwinięte do samych grotów strzał, gdy używam klastrów, które są połączone tylko pionowo. Naprawiłem to z minlen = 1 na krawędziach. c -> g [ogon = klaster0, lhead = klaster1, minlen = 1];
Freenerd
3
Oto link do instrukcji z przykładem: graphviz.org/Documentation/dotguide.pdf (strona 30).
Kirill Bulygin
90

Dla ułatwienia rozwiązanie opisane w odpowiedzi HighPerformanceMark, zastosowane bezpośrednio do pierwotnego pytania, wygląda następująco:

digraph G {

    graph [fontsize=10 fontname="Verdana" compound=true];
    node [shape=record fontsize=10 fontname="Verdana"];

    subgraph cluster_0 {
        node [style=filled];
        "Item 1" "Item 2";
        label = "Container A";
        color=blue;
    }

    subgraph cluster_1 {
        node [style=filled];
        "Item 3" "Item 4";
        label = "Container B";
        color=blue;
    }

    subgraph cluster_2 {
        node [style=filled];
        "Item 5" "Item 6";
        label = "Container C";
        color=blue;
    }

    // Edges between nodes render fine
    "Item 1" -> "Item 2";
    "Item 2" -> "Item 3";

    // Edges that directly connect one cluster to another
    "Item 1" -> "Item 3" [ltail=cluster_0 lhead=cluster_1];
    "Item 1" -> "Item 5" [ltail=cluster_0 lhead=cluster_2];
}

compound=trueW graphdeklaracji jest niezbędna. To daje wynik:

wykres z połączonymi klastrami

Zauważ, że zmieniłem krawędzie na węzły odniesienia w klastrze, dodałem atrybuty ltail i lhead do każdej krawędzi, określając nazwę klastra i dodałem atrybut na poziomie wykresu „związek = prawda”.

Jeśli chodzi o obawy, że ktoś może chcieć połączyć klaster bez żadnych węzłów w nim, moim rozwiązaniem było zawsze dodawanie węzła do każdego klastra, renderowanego za pomocą style = zwykły tekst. Użyj tego węzła, aby oznaczyć klaster (zamiast wbudowanego atrybutu „label” klastra, który powinien być ustawiony na pusty ciąg (w Pythonie label='""'). Oznacza to, że nie dodam już krawędzi, które bezpośrednio łączą klastry, ale działa w mojej konkretnej sytuacji.

Jonathan Hartley
źródło
24
Uwaga: 'graph [fontsize = 10 fontname = "Verdana" związek = prawda];' jest niezbędny - jeśli przegapisz link do ltail / lhead, nie działa.
Daniel
1
@JonathanHartley, Jak w ostatnim akapicie, czy jest jakiś sposób na wyśrodkowanie tego węzła w samym środku klastra?
Pacerier,
również nazwa klastra nie powinna zaczynać się wielką literą
JCLL
7
@ s.Daniel To tylko związek = true; co jest wymagane
Dr. Max Völkel
Zamiast resetować lhead i ltail, gdy łącze „Item 1” -> „Item 3”, jak połączyć klaster_0 i klaster_1 za pomocą znaczącego kodu? Ja meam, podaj cluster_0 -> cluster_1prezent podczas drukowania. Ponieważ może być wiele elementów w klastrze_0, łącze do wielu innych elementów w klastrze_1 (wiele do wielu lub jeden do wielu). Dobrze byłoby po prostu połączyć dwa.
Mithril
11

Upewnij się, że używasz fdpukładu dla pliku. Myślę, że nie neatoobsługuje klastrów.

mihajlv
źródło
2
Ja też odkryłem doświadczalnie, że neatosilnik nie obsługuje klastrów .. Nie jestem pewien, czy to jest błąd, czy nie ..
Ross Rogers