Tvorba grafů a diagramů s využitím doménově specifického jazyka nástroje Graphviz (2.část)

8. 6. 2021
Doba čtení: 31 minut

Sdílet

 Autor: Pavel Tišnovský
Nejprve se zaměříme na algoritmy určené pro rozmístění uzlů na ploše grafu. Následně se budeme zabývat specifikací i vizuálního zvýraznění podgrafů. Poté si ukážeme, jak lze měnit tvar uzlů, šipek či celých hran.

Obsah

1. Využití různých algoritmů pro rozmístění uzlů na plochu obrázku

2. Graf se šesticí uzlů spojených jediným cyklem

3. Výsledek aplikace jednotlivých algoritmů

4. Neorientovaný graf se středovým uzlem

5. Výsledek aplikace jednotlivých algoritmů

6. Explicitní specifikace kořenového uzlu

7. Graf se středovým uzlem a okrajovými podstromy

8. Algoritmy dot a circo a několikanásobné hrany mezi uzly

9. Symetrický orientovaný i neorientovaný graf s podstromy

10. Specifikace podgrafů

11. Postupné přidávání podgrafů

12. Barevné zvýraznění podgrafů

13. Postupné přidávání podgrafů „clusterX“ se specifikací jejich barevné výplně

14. Změna tvaru uzlů v grafech

15. Tvary šipek

16. Obsah navazujícího článku

17. Repositář s demonstračními příklady

18. Odkazy na články s tématem programové tvorby grafů a diagramů

19. Odkazy na Internetu

1. Využití různých algoritmů pro rozmístění uzlů na plochu obrázku

Na úvodní článek o nástroji Graphviz dnes navážeme a popíšeme si další možnosti, které tento nástroj (resp. přesněji řečeno sada nástrojů) uživatelům nabízí. Nejprve se zaměříme na různé algoritmy určené pro rozmístění uzlů na ploše grafu. Následně se budeme zabývat problematikou specifikace i vizuálního zvýraznění podgrafů. A ve třetí části článku si ukážeme, jak lze měnit tvar uzlů popř. tvar šipek či celých hran.

Prozatím jsme většinu grafů vykreslovali nástrojem nazvaným dot (což je současně i jméno jazyka pro definici grafů). Ve skutečnosti je ovšem Graphviz sadou většího množství nástrojů, které se od sebe odlišují především tím, jaký algoritmus je použit pro rozmístění uzlů na ploše – což do značné míry ovlivňuje přehlednost (či naopak nepřehlednost) vykresleného grafu. Těchto nástrojů-algoritmů existuje celá řada a každý se hodí pro jiné účely. Standardní algoritmy dostupné v instalaci Graphviz jsou vypsány v tabulce pod tímto odstavcem:

# Název nástroje Stručný popis
1 dot používáno pro grafy s hierarchií uzlů a skupin (podgrafy, clustery)
2 neato určeno pro symetrické grafy (typicky neorientované)
3 twopi určeno pro grafy, které mají uzly rozmístěny radiálně (paprskovitě)
4 circo určeno pro  uzly rozmístěnými na pomyslnou kružnici
5 fdp určeno především pro symetrické grafy
6 sfdp určeno především pro rozsáhlé neorientované grafy
7 patchwork typicky používáno pro stromy (tj. pro grafy bez cyklů)
8 osage určeno pro grafy s clustery, alternativa k dot

Vliv jednotlivých algoritmů si ukážeme na několika příkladech s různými typy grafů.

2. Graf se šesticí uzlů spojených jediným cyklem

Prvním demonstračním příkladem, s nímž se dnes seznámíme, je příklad obsahující definici grafu se šesticí uzlů, které jsou propojeny jediným cyklem. Konkrétně to znamená, že první uzel je propojen s uzlem druhým, ten s uzlem třetím atd. až poslední uzel je opět propojen s prvním uzlem. Jedná se o neorientovaný graf:

graph {
    rankdir=LR
 
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    a -- b
    b -- c
    c -- d;
    d -- e;
    e -- f;
    f -- a;
}

3. Výsledek aplikace jednotlivých algoritmů

Obrázek 1: Algoritmus circo.

Obrázek 2: Algoritmus dot.

Obrázek 3: Algoritmus fdp.

Obrázek 4: Algoritmus neato.

Obrázek 5: Algoritmus osage.

Obrázek 6: Algoritmus patchwork.

Obrázek 7: Algoritmus sfpd.

Obrázek 8: Algoritmus twopi.

Poznámka: podle očekávání dopadl nejlépe algoritmus circo, který všechny uzly umístil na pomyslnou kružnici. Druhý vizuálně nejhezčí graf byl vytvořen algoritmem neato.

4. Neorientovaný graf se středovým uzlem

Ve druhém grafu, na němž budeme testovat různé algoritmy pro rozložení uzlů, přidáme „středový“ uzel o s popiskem ω. Tento uzel je propojen se všemi ostatními uzly (a ty jsou navzájem propojeny stejně, jako tomu bylo v prvním demonstračním příkladu):

graph {
    rankdir=LR
 
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
    o[label="ω"]
 
    a -- b
    b -- c
    c -- d;
    d -- e;
    e -- f;
    f -- a;
 
    a -- o;
    b -- o;
    c -- o;
    d -- o;
    e -- o;
    f -- o;
}

5. Výsledek aplikace jednotlivých algoritmů

Obrázek 9: Algoritmus circo.

Obrázek 10: Algoritmus dot.

Obrázek 11: Algoritmus fdp.

Obrázek 12: Algoritmus neato.

Obrázek 13: Algoritmus osage.

Obrázek 14: Algoritmus patchwork.

Obrázek 15: Algoritmus sfpd.

Obrázek 16: Algoritmus twopi.

Poznámka: v tomto případě nejlépe dopadly algoritmy neato (znovu) a sfdp.

6. Explicitní specifikace kořenového uzlu

Algoritmus twopi vyžaduje explicitní specifikaci kořenového uzlu. Pokud neví, který uzel je kořenový, vybere náhodně libovolný uzel, takže pravděpodobnost, že vybere ten správný, je relativně malá (a nelze se na ni spoléhat). Z tohoto důvodu je možné v jazyce dot specifikovat kořenový uzel atributem root. Tento kořenový uzel bude umístěn uprostřed grafu a ostatní uzly se uspořádají do soustředných kružnic. Předchozí demonstrační příklad tedy postačuje nepatrně upravit:

graph {
    rankdir=LR
    root=o
 
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
    o[label="ω"]
 
    a -- b
    b -- c
    c -- d;
    d -- e;
    e -- f;
    f -- a;
 
    a -- o;
    b -- o;
    c -- o;
    d -- o;
    e -- o;
    f -- o;
}

Z výsledku je nyní patrné, že algoritmus twopi vytvořil estetický symetrický graf:

Obrázek 17: Výsledek práce algoritmu twopi.

7. Graf se středovým uzlem a okrajovými podstromy

V předchozích kapitolách jsme si řekli, že se algoritmus twopi snaží umístit jeden uzel do středu grafu a ostatní uzly do soustředných kružnic okolo něj. Na sedmnáctém obrázku je patrné použití jediné pomyslné kružnice, ovšem v případě složitějších grafů je možné mít těchto kružnic více. Toto chování algoritmu twopi si ukážeme na dalším grafu, jenž má (opět) explicitně zvolený středový uzel (kořenový). Z dalších šesti základních uzlů vedou hrany do dalších dvanácti uzlů:

graph {
    rankdir=LR
    root=o
 
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
    o[label="ω"]
 
    a -- b;
    b -- c;
    c -- d;
    d -- e;
    e -- f;
    f -- a;
 
    a -- o;
    b -- o;
    c -- o;
    d -- o;
    e -- o;
    f -- o;
 
    a -- a1;
    a -- a2;
    b -- b1;
    b -- b2;
    c -- c1;
    c -- c2;
    d -- d1;
    d -- d2;
    e -- e1;
    e -- e2;
    f -- f1;
    f -- f2;
}

Takto definovaný graf se s využitím algoritmu twopi zobrazí následovně:

Obrázek 18: Výsledek práce algoritmu twopi.

8. Algoritmy dot a circo a několikanásobné hrany mezi uzly

Mnohdy je nutné pracovat s grafy, v nichž jsou uzly propojeny několikanásobnými hranami (ať již orientovanými, tak i neorientovanými). Takové grafy jsou zpracovatelné všemi osmi výše zmíněnými algoritmy, ovšem s různých úspěchem. Ukažme si nyní, co se stane, pokud do grafu z předchozí kapitoly přidáme hrany mezi základní šestici uzlů α až ζ:

graph {
    rankdir=LR
    root=o
 
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
    o[label="ω"]
 
    a -- b;
    b -- c;
    c -- d;
    d -- e;
    e -- f;
    f -- a;
 
    a -- b;
    b -- c;
    c -- d;
    d -- e;
    e -- f;
    f -- a;
 
    a -- o;
    b -- o;
    c -- o;
    d -- o;
    e -- o;
    f -- o;
 
    a -- a1;
    a -- a2;
    b -- b1;
    b -- b2;
    c -- c1;
    c -- c2;
    d -- d1;
    d -- d2;
    e -- e1;
    e -- e2;
    f -- f1;
    f -- f2;
}

Základní algoritmus dot i přes snahu vytvořil dosti nečitelný graf:

Obrázek 19: Výsledek práce algoritmu dot.

Naproti tomu algoritmus twopi vytvořil graf, který je mnohem lepší jak po stránce vizuální, tak i z hlediska celkové čitelnosti:

Obrázek 20: Výsledek práce algoritmu twopi.

9. Symetrický orientovaný i neorientovaný graf s podstromy

Algoritmus circo dokáže zpracovat jak orientované, tak i neorientované grafy. Můžeme se o tom ostatně velmi snadno přesvědčit po vykreslení grafů definovaných v následující dvojici demonstračních příkladů. První z těchto příkladů je neorientovaným grafem se šesti uzly tvořícími cyklus a s dalšími dvanácti listy:

graph {
    rankdir=LR
 
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    a -- b;
    b -- c;
    c -- d;
    d -- e;
    e -- f;
    f -- a;
 
    a -- b;
    b -- c;
    c -- d;
    d -- e;
    e -- f;
    f -- a;
 
    a -- a1;
    a -- a2;
    b -- b1;
    b -- b2;
    c -- c1;
    c -- c2;
    d -- d1;
    d -- d2;
    e -- e1;
    e -- e2;
    f -- f1;
    f -- f2;
}

Druhý demonstrační příklad z této kapitoly je prakticky totožný, ovšem graf je nyní orientovaný – jeho hrany jsou tedy tvořeny šipkami:

digraph {
    rankdir=LR
 
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    a -> b;
    b -> c;
    c -> d;
    d -> e;
    e -> f;
    f -> a;
 
    b -> a;
    c -> b;
    d -> c;
    e -> d;
    f -> e;
    a -> f;
 
    a -> a1;
    a -> a2;
    b -> b1;
    b -> b2;
    c -> c1;
    c -> c2;
    d -> d1;
    d -> d2;
    e -> e1;
    e -> e2;
    f -> f1;
    f -> f2;
}

Z následující dvojice obrázků je patrné, že vykreslené grafy jsou prakticky totožné (pochopitelně až na odlišné hrany):

Obrázek 21: Výsledek práce algoritmu circo (neorientovaný graf).

Obrázek 22: Výsledek práce algoritmu circo (orientovaný graf).

10. Specifikace podgrafů

Doménově specifický jazyk dot umožňuje v grafu definovat podgrafy, což jsou uzly, které mohou mít společné vlastnosti (a to i vizuální). Pokud nějaké uzly tvoří podgraf, budou se jednotlivé algoritmy snažit takové uzly vhodným způsobem uspořádat. Samotná definice podgrafu vypadá následovně:

    ...
    ...
    ...
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    subgraph {
        rank = same;
        a;
        a1;
        a2;
    }
    ...
    ...
    ...

Z tohoto úryvku kódu je patrné, že v podgrafu můžeme pouze specifikovat uzly, které do něj patří.

Poznámka: podgrafy je možné i pojmenovat, což lze využít pro logické označení celého bloku. Ovšem navíc ještě existuje varianta pojmenování podgrafu začínající slovem „cluster“. Takové podgrafy jsou specifické, protože uzly, které do podgrafu spadají, budou orámovány. Podrobnosti si ukážeme v navazujících kapitolách.

11. Postupné přidávání podgrafů

Ukažme si nyní, jak existence podgrafů ovlivňuje způsob zobrazení grafu. Začneme s následujícím grafem (s celkem osmnácti uzly), v němž žádné podgrafy definovány nejsou:

digraph {
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    a -> b;
    b -> c;
    c -> d;
    d -> e;
    e -> f;
    f -> a;
 
    b -> a;
    c -> b;
    d -> c;
    e -> d;
    f -> e;
    a -> f;
 
    a -> a1;
    a -> a2;
    b -> b1;
    b -> b2;
    c -> c1;
    c -> c2;
    d -> d1;
    d -> d2;
    e -> e1;
    e -> e2;
    f -> f1;
    f -> f2;
}

Přidáme první podgraf:

digraph {
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    subgraph {
        rank = same;
        a;
        a1;
        a2;
    }
 
    a -> b;
    b -> c;
    c -> d;
    d -> e;
    e -> f;
    f -> a;
 
    b -> a;
    c -> b;
    d -> c;
    e -> d;
    f -> e;
    a -> f;
 
    a -> a1;
    a -> a2;
    b -> b1;
    b -> b2;
    c -> c1;
    c -> c2;
    d -> d1;
    d -> d2;
    e -> e1;
    e -> e2;
    f -> f1;
    f -> f2;
}

Přidáme druhý podgraf:

digraph {
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    subgraph {
        rank = same;
        a;
        a1;
        a2;
    }
 
    subgraph {
        rank = same;
        b;
        b1;
        b2;
    }
 
    a -> b;
    b -> c;
    c -> d;
    d -> e;
    e -> f;
    f -> a;
 
    b -> a;
    c -> b;
    d -> c;
    e -> d;
    f -> e;
    a -> f;
 
    a -> a1;
    a -> a2;
    b -> b1;
    b -> b2;
    c -> c1;
    c -> c2;
    d -> d1;
    d -> d2;
    e -> e1;
    e -> e2;
    f -> f1;
    f -> f2;
}

Přidáme třetí podgraf:

digraph {
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    subgraph {
        rank = same;
        a;
        a1;
        a2;
    }
 
    subgraph {
        rank = same;
        b;
        b1;
        b2;
    }
 
    subgraph {
        rank = same;
        c;
        c1;
        c2;
    }
 
    a -> b;
    b -> c;
    c -> d;
    d -> e;
    e -> f;
    f -> a;
 
    b -> a;
    c -> b;
    d -> c;
    e -> d;
    f -> e;
    a -> f;
 
    a -> a1;
    a -> a2;
    b -> b1;
    b -> b2;
    c -> c1;
    c -> c2;
    d -> d1;
    d -> d2;
    e -> e1;
    e -> e2;
    f -> f1;
    f -> f2;
}

Přidáme čtvrtý podgraf:

digraph {
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    subgraph {
        rank = same;
        a;
        a1;
        a2;
    }
 
    subgraph {
        rank = same;
        b;
        b1;
        b2;
    }
 
    subgraph {
        rank = same;
        c;
        c1;
        c2;
    }
 
    subgraph {
        rank = same;
        d;
        d1;
        d2;
    }
 
    a -> b;
    b -> c;
    c -> d;
    d -> e;
    e -> f;
    f -> a;
 
    b -> a;
    c -> b;
    d -> c;
    e -> d;
    f -> e;
    a -> f;
 
    a -> a1;
    a -> a2;
    b -> b1;
    b -> b2;
    c -> c1;
    c -> c2;
    d -> d1;
    d -> d2;
    e -> e1;
    e -> e2;
    f -> f1;
    f -> f2;
}

Přidáme pátý (předposlední) podgraf:

digraph {
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    subgraph {
        rank = same;
        a;
        a1;
        a2;
    }
 
    subgraph {
        rank = same;
        b;
        b1;
        b2;
    }
 
    subgraph {
        rank = same;
        c;
        c1;
        c2;
    }
 
    subgraph {
        rank = same;
        d;
        d1;
        d2;
    }
 
    subgraph {
        rank = same;
        e;
        e1;
        e2;
    }
 
    a -> b;
    b -> c;
    c -> d;
    d -> e;
    e -> f;
    f -> a;
 
    b -> a;
    c -> b;
    d -> c;
    e -> d;
    f -> e;
    a -> f;
 
    a -> a1;
    a -> a2;
    b -> b1;
    b -> b2;
    c -> c1;
    c -> c2;
    d -> d1;
    d -> d2;
    e -> e1;
    e -> e2;
    f -> f1;
    f -> f2;
}

A konečně přidáme šestý (poslední) podgraf:

digraph {
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    subgraph {
        rank = same;
        a;
        a1;
        a2;
    }
 
    subgraph {
        rank = same;
        b;
        b1;
        b2;
    }
 
    subgraph {
        rank = same;
        c;
        c1;
        c2;
    }
 
    subgraph {
        rank = same;
        d;
        d1;
        d2;
    }
 
    subgraph {
        rank = same;
        e;
        e1;
        e2;
    }
 
    subgraph {
        rank = same;
        f;
        f1;
        f2;
    }
 
    a -> b;
    b -> c;
    c -> d;
    d -> e;
    e -> f;
    f -> a;
 
    b -> a;
    c -> b;
    d -> c;
    e -> d;
    f -> e;
    a -> f;
 
    a -> a1;
    a -> a2;
    b -> b1;
    b -> b2;
    c -> c1;
    c -> c2;
    d -> d1;
    d -> d2;
    e -> e1;
    e -> e2;
    f -> f1;
    f -> f2;
}

Výsledky jsou pro lepší vizuální porovnání zobrazeny pod sebou:

Obrázek 23: Graf bez podgrafů.

Obrázek 24: Graf s jedním podgrafem.

Obrázek 25: Graf se dvěma podgrafy.

Obrázek 26: Graf se třemi podgrafy.

Obrázek 27: Graf se čtyřmi podgrafy.

Obrázek 28: Graf s pěti podgrafy.

Obrázek 29: Graf se šesti podgrafy.

Poznámka: z těchto ukázek je patrné, jak se algoritmus snaží uzly v podgrafu umístit vedle sebe popř. pod sebe.

12. Barevné zvýraznění podgrafů

V desáté kapitole jsme si řekli, že podgrafy je možné pojmenovat:

subgraph totoJeJmeno {
    rank = same;
    a;
    a1;
    a2;
}

V případě, že jméno podgrafu začíná slovem „cluster“, bude podgraf orámován. A nejen to – bude možné specifikovat i barvu výplně tohoto orámování. Následuje příklad využití této velmi užitečné vlastnosti doménově specifického jazyka dot:

subgraph cluster1 {
    rank = same;
    style = "filled";
    fillcolor = "#80ff80";
    a;
    a1;
    a2;
}

13. Postupné přidávání podgrafů „clusterX“ se specifikací jejich barevné výplně

Podobně jako v jedenácté kapitole se i nyní pokusíme o postupné přidávání podgrafů se jménem „clusterX“ (za X je dosazeno kladné celé číslo) do definice grafu a následně zkontrolujeme, jak vypadají výsledky po vykreslení grafu.

Začneme s následujícím grafem (s celkem osmnácti uzly), v němž žádné podgrafy definovány nejsou:

digraph {
    rankdir=LR
 
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    a -> b;
    b -> c;
    c -> d;
    d -> e;
    e -> f;
    f -> a;
 
    b -> a;
    c -> b;
    d -> c;
    e -> d;
    f -> e;
    a -> f;
 
    a -> a1;
    a -> a2;
    b -> b1;
    b -> b2;
    c -> c1;
    c -> c2;
    d -> d1;
    d -> d2;
    e -> e1;
    e -> e2;
    f -> f1;
    f -> f2;
}

Přidáme první podgraf:

digraph {
    rankdir=LR
 
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    subgraph cluster1 {
        rank = same;
        style = "filled";
        fillcolor = "#80ff80";
        a;
        a1;
        a2;
    }
 
    a -> b;
    b -> c;
    c -> d;
    d -> e;
    e -> f;
    f -> a;
 
    b -> a;
    c -> b;
    d -> c;
    e -> d;
    f -> e;
    a -> f;
 
    a -> a1;
    a -> a2;
    b -> b1;
    b -> b2;
    c -> c1;
    c -> c2;
    d -> d1;
    d -> d2;
    e -> e1;
    e -> e2;
    f -> f1;
    f -> f2;
}

Přidáme druhý podgraf:

digraph {
    rankdir=LR
 
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    subgraph cluster1 {
        rank = same;
        style = "filled";
        fillcolor = "#80ff80";
        a;
        a1;
        a2;
    }
 
    subgraph cluster2 {
        rank = same;
        style = "filled";
        fillcolor = "#80ffff";
        b;
        b1;
        b2;
    }
 
    a -> b;
    b -> c;
    c -> d;
    d -> e;
    e -> f;
    f -> a;
 
    b -> a;
    c -> b;
    d -> c;
    e -> d;
    f -> e;
    a -> f;
 
    a -> a1;
    a -> a2;
    b -> b1;
    b -> b2;
    c -> c1;
    c -> c2;
    d -> d1;
    d -> d2;
    e -> e1;
    e -> e2;
    f -> f1;
    f -> f2;
}

Přidáme třetí podgraf:

digraph {
    rankdir=LR
 
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    subgraph cluster1 {
        rank = same;
        style = "filled";
        fillcolor = "#80ff80";
        a;
        a1;
        a2;
    }
 
    subgraph cluster2 {
        rank = same;
        style = "filled";
        fillcolor = "#80ffff";
        b;
        b1;
        b2;
    }
 
    subgraph cluster3 {
        rank = same;
        style = "filled";
        fillcolor = "#8080ff";
        c;
        c1;
        c2;
    }
 
    a -> b;
    b -> c;
    c -> d;
    d -> e;
    e -> f;
    f -> a;
 
    b -> a;
    c -> b;
    d -> c;
    e -> d;
    f -> e;
    a -> f;
 
    a -> a1;
    a -> a2;
    b -> b1;
    b -> b2;
    c -> c1;
    c -> c2;
    d -> d1;
    d -> d2;
    e -> e1;
    e -> e2;
    f -> f1;
    f -> f2;
}

Přidáme čtvrtý podgraf:

digraph {
    rankdir=LR
 
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    subgraph cluster1 {
        rank = same;
        style = "filled";
        fillcolor = "#80ff80";
        a;
        a1;
        a2;
    }
 
    subgraph cluster2 {
        rank = same;
        style = "filled";
        fillcolor = "#80ffff";
        b;
        b1;
        b2;
    }
 
    subgraph cluster3 {
        rank = same;
        style = "filled";
        fillcolor = "#8080ff";
        c;
        c1;
        c2;
    }
 
    subgraph cluster4 {
        rank = same;
        style = "filled";
        fillcolor = "#ff80ff";
        d;
        d1;
        d2;
    }
 
    a -> b;
    b -> c;
    c -> d;
    d -> e;
    e -> f;
    f -> a;
 
    b -> a;
    c -> b;
    d -> c;
    e -> d;
    f -> e;
    a -> f;
 
    a -> a1;
    a -> a2;
    b -> b1;
    b -> b2;
    c -> c1;
    c -> c2;
    d -> d1;
    d -> d2;
    e -> e1;
    e -> e2;
    f -> f1;
    f -> f2;
}

Přidáme pátý (předposlední) podgraf:

digraph {
    rankdir=LR
 
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    subgraph cluster1 {
        rank = same;
        style = "filled";
        fillcolor = "#80ff80";
        a;
        a1;
        a2;
    }
 
    subgraph cluster2 {
        rank = same;
        style = "filled";
        fillcolor = "#80ffff";
        b;
        b1;
        b2;
    }
 
    subgraph cluster3 {
        rank = same;
        style = "filled";
        fillcolor = "#8080ff";
        c;
        c1;
        c2;
    }
 
    subgraph cluster4 {
        rank = same;
        style = "filled";
        fillcolor = "#ff80ff";
        d;
        d1;
        d2;
    }
 
    subgraph cluster5 {
        rank = same;
        style = "filled";
        fillcolor = "#ff8080";
        e;
        e1;
        e2;
    }
 
    a -> b;
    b -> c;
    c -> d;
    d -> e;
    e -> f;
    f -> a;
 
    b -> a;
    c -> b;
    d -> c;
    e -> d;
    f -> e;
    a -> f;
 
    a -> a1;
    a -> a2;
    b -> b1;
    b -> b2;
    c -> c1;
    c -> c2;
    d -> d1;
    d -> d2;
    e -> e1;
    e -> e2;
    f -> f1;
    f -> f2;
}

A konečně přidáme šestý (poslední) podgraf:

digraph {
    rankdir=LR
 
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    subgraph cluster1 {
        rank = same;
        style = "filled";
        fillcolor = "#80ff80";
        a;
        a1;
        a2;
    }
 
    subgraph cluster2 {
        rank = same;
        style = "filled";
        fillcolor = "#80ffff";
        b;
        b1;
        b2;
    }
 
    subgraph cluster3 {
        rank = same;
        style = "filled";
        fillcolor = "#8080ff";
        c;
        c1;
        c2;
    }
 
    subgraph cluster4 {
        rank = same;
        style = "filled";
        fillcolor = "#ff80ff";
        d;
        d1;
        d2;
    }
 
    subgraph cluster5 {
        rank = same;
        style = "filled";
        fillcolor = "#ff8080";
        e;
        e1;
        e2;
    }
 
    subgraph cluster6 {
        rank = same;
        style = "filled";
        fillcolor = "#ffff80";
        f;
        f1;
        f2;
    }
 
    a -> b;
    b -> c;
    c -> d;
    d -> e;
    e -> f;
    f -> a;
 
    b -> a;
    c -> b;
    d -> c;
    e -> d;
    f -> e;
    a -> f;
 
    a -> a1;
    a -> a2;
    b -> b1;
    b -> b2;
    c -> c1;
    c -> c2;
    d -> d1;
    d -> d2;
    e -> e1;
    e -> e2;
    f -> f1;
    f -> f2;
}

Výsledky jsou pro lepší vizuální porovnání zobrazeny pod sebou:

Obrázek 30: Graf bez podgrafů.

Obrázek 31: Graf s jedním podgrafem (clusterem).

Obrázek 32: Graf se dvěma podgrafy (clustery).

Obrázek 33: Graf se třemi podgrafy (clustery).

Obrázek 34: Graf se čtyřmi podgrafy (clustery).

Obrázek 35: Graf s pěti podgrafy (clustery).

Obrázek 36: Graf se šesti podgrafy (clustery).

14. Změna tvaru uzlů v grafech

S využitím atributu shape je možné měnit tvar uzlů v grafech. Opět si tuto funkcionalitu ukažme na několika demonstračních příkladech. Začneme grafem se standardními tvary uzlů:

graph {
    rankdir=LR
 
    a[label="α"]
    b[label="β"]
    c[label="γ"]
    d[label="δ"]
    e[label="ε"]
    f[label="ζ"]
 
    a -- b
    b -- c
    c -- d;
    d -- e;
    e -- f;
    f -- a;
}

Obrázek 37: Graf se standardními tvary uzlů.

Ve druhém příkladu jsou všechny uzly upraveny tak, aby se vždy zobrazily jako kružnice a nikoli jako elipsy:

graph {
    rankdir=LR
 
    a[shape="circle", label="α"]
    b[shape="circle", label="β"]
    c[shape="circle", label="γ"]
    d[shape="circle", label="δ"]
    e[shape="circle", label="ε"]
    f[shape="circle", label="ζ"]
 
    a -- b
    b -- c
    c -- d;
    d -- e;
    e -- f;
    f -- a;
}

Obrázek 38: Graf s uzly ve tvaru kružnice.

A konečně ve třetím grafu jsou všechny uzly zobrazeny obdélníkem:

graph {
    rankdir=LR
 
    a[shape="box", label="α"]
    b[shape="box", label="β"]
    c[shape="box", label="γ"]
    d[shape="box", label="δ"]
    e[shape="box", label="ε"]
    f[shape="box", label="ζ"]
 
    a -- b
    b -- c
    c -- d;
    d -- e;
    e -- f;
    f -- a;
}

Obrázek 39: Graf s uzly ve tvaru obdélníku.

Poznámka: všechny podporované tvary uzlů jsou zobrazeny na stránce https://graphviz.gitlab.i­o/doc/info/shapes.html#po­lygon/a>.

15. Tvary šipek

I tvary šipek (tedy ukončení hran) je možné modifikovat. Styl šipky lze pojmenovat, stejně jako styl uzlu, nebo (což je častější) je možné styl určit přímo u definice hrany. A právě tuto druhou možnost si ukážeme v dalším demonstračním příkladu s orientovaným grafem, v němž je definováno osm hran, každá s odlišným stylem šipky:

digraph {
    splines="line"
 
    a[label="start"]
    b[color="red"]
    c[color="blue"]
 
    a -> b [arrowhead=box];
    b -> c [arrowhead=crow];
    c -> d [arrowhead=curve];
    d -> a [arrowhead=icurve];
 
    b -> a [arrowhead=diamond];
    c -> b [arrowhead=dot];
    d -> c [arrowhead=tee];
    d -> a [arrowhead=vee];
}

Obrázek 40: Orientovaný graf s různými styly konců hran.

16. Obsah navazujícího článku

Ve třetím článku o nástroji Graphviz si ukážeme další pomocné nástroje – hledání cest, zvýraznění automaticky nalezeného podgrafu atd. Taktéž si popíšeme některé užitečné nástroje, které jsou nad Graphviz postaveny. Příkladem může být projekt schemaSpy určený pro vizualizaci schématu relační databáze.

Obrázek 41: Výsledek činnosti nástroje schemaSpy.

ict ve školství 24

17. Repositář s demonstračními příklady

Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do nového Git repositáře, který je dostupný na adrese https://github.com/tisnik/diagrams (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně jednotky kilobajtů), můžete namísto toho použít odkazy na jednotlivé demonstrační příklady, které naleznete v následující tabulce:

# Příklad Popis Cesta
1 graph01.dot graf s jediným uzlem https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h01.dot
2 graph02.dot graf s větším množstvím uzlům a neorientovanými hranami https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h02.dot
3 graph03.dot graf s hranami začínajícími a končícími ve stejném uzlu https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h03.dot
4 graph04.dot propojení uzlů větším množstvím hran https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h04.dot
5 graph05.dot popisky neorientovaných hran https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h05.dot
6 graph06.dot graf se dvěma uzly a orientovanou hranou https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h06.dot
7 graph07.dot graf s orientovanou hranou, ovšem s opačnou šipkou https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h07.dot
8 graph08.dot složitější graf s orientovanými hranami https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h08.dot
9 graph09.dot graf s hranami, které se vrací do původního uzlu https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h09.dot
10 graph10.dot neorientovaný graf se zvýrazněnými hranami https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h10.dot
11 graph11.dot orientovaný graf se zvýrazněnými hranami https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h11.dot
12 graph12.dot orientovaný graf se zvýrazněnými hranami a s uzly uspořádanými zleva doprava https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h12.dot
13 graph13.dot graf s hranami vykreslenými formou úsečky popř. oblouku. https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h13.dot
14 graph14.dot graf, v němž mají jednotlivé uzly nastaven jiný vizuální styl vykreslování https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h14.dot
15 graph15.dot graf s popisky, v nichž jsou použity HTML entity https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h15.dot
16 graph16.dot graf se šesticí uzlů cyklicky propojených orientovanými hranami https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h16.dot
       
17 graph17.dot graf se šesticí uzlů spojených jediným cyklem https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h17.dot
18 graph18.dot neorientovaný graf se středovým uzlem https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h18.dot
19 graph19.dot explicitní specifikace kořenového uzlu https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h19.dot
20 graph20.dot graf se středovým uzlem a okrajovými podstromy https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h20.dot
21 graph21.dot graf s několikanásobnými hranami mezi uzly https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h21.dot
22 graph22.dot symetrický orientovaný i neorientovaný graf s podstromy https://github.com/tisnik/di­agrams/blob/master/graphviz/grap­h22.dot
       
23 subgraph0.dot graf bez podgrafů https://github.com/tisnik/di­agrams/blob/master/graphviz/sub­graph0.dot
24 subgraph1.dot graf s jedním podgrafem https://github.com/tisnik/di­agrams/blob/master/graphviz/sub­graph1.dot
25 subgraph2.dot graf se dvěma podgrafy https://github.com/tisnik/di­agrams/blob/master/graphviz/sub­graph2.dot
26 subgraph3.dot graf se třemi podgrafy https://github.com/tisnik/di­agrams/blob/master/graphviz/sub­graph3.dot
27 subgraph4.dot graf se čtyřmi podgrafy https://github.com/tisnik/di­agrams/blob/master/graphviz/sub­graph4.dot
28 subgraph5.dot graf s pěti podgrafy https://github.com/tisnik/di­agrams/blob/master/graphviz/sub­graph5.dot
29 subgraph6.dot graf se šesti podgrafy https://github.com/tisnik/di­agrams/blob/master/graphviz/sub­graph6.dot
       
30 subgraph_c0.dot graf bez podgrafů https://github.com/tisnik/di­agrams/blob/master/graphviz/sub­graph_c0.dot
31 subgraph_c1.dot graf s jedním pojmenovaným podgrafem https://github.com/tisnik/di­agrams/blob/master/graphviz/sub­graph_c1.dot
32 subgraph_c2.dot graf se dvěma pojmenovanými podgrafy https://github.com/tisnik/di­agrams/blob/master/graphviz/sub­graph_c2.dot
33 subgraph_c3.dot graf se třemi pojmenovanými podgrafy https://github.com/tisnik/di­agrams/blob/master/graphviz/sub­graph_c3.dot
34 subgraph_c4.dot graf se čtyřmi pojmenovanými podgrafy https://github.com/tisnik/di­agrams/blob/master/graphviz/sub­graph_c4.dot
35 subgraph_c5.dot graf s pěti pojmenovanými podgrafy https://github.com/tisnik/di­agrams/blob/master/graphviz/sub­graph_c5.dot
36 subgraph_c6.dot graf se šesti pojmenovanými podgrafy https://github.com/tisnik/di­agrams/blob/master/graphviz/sub­graph_c6.dot
       
37 shapes1.dot původní graf s výchozím tvarem uzlů https://github.com/tisnik/di­agrams/blob/master/graphviz/sha­pes1.dot
38 shapes2.dot změna tvaru uzlů: kružnice https://github.com/tisnik/di­agrams/blob/master/graphviz/sha­pes2.dot
39 shapes3.dot změna tvaru uzlů: obdélníky https://github.com/tisnik/di­agrams/blob/master/graphviz/sha­pes3.dot
       
40 path1.dot výchozí příklad pro další tři operace https://github.com/tisnik/di­agrams/blob/master/graphviz/pat­h1.dot
41 path2.dot nalezení podgrafu https://github.com/tisnik/di­agrams/blob/master/graphviz/pat­h2.dot
42 path3.dot automaticky vygenerovaný soubor https://github.com/tisnik/di­agrams/blob/master/graphviz/pat­h3.dot
43 path4.dot graf, jehož uzly mohou být automaticky obarveny https://github.com/tisnik/di­agrams/blob/master/graphviz/pat­h4.dot

18. Odkazy na články s tématem programové tvorby grafů a diagramů

V této kapitole jsou uvedeny odkazy na předchozí články, v nichž jsme se zabývali tvorbou různých typů grafů a diagramů – a to v naprosté většině případů s využitím nějakého doménově specifického jazyka neboli DSL (Domain Specific Language) popř. nějakého univerzálního programovacího jazyka (zejména Python, Go, Clojure):

  1. Nástroje pro tvorbu UML diagramů
    https://www.root.cz/clanky/nastroje-pro-tvorbu-uml-diagramu/
  2. Nástroje pro tvorbu UML diagramů z příkazové řádky
    https://www.root.cz/clanky/nastroje-pro-tvorbu-uml-diagramu-z-prikazove-radky/
  3. Nástroje pro tvorbu UML diagramů z příkazové řádky (II)
    https://www.root.cz/clanky/nastroje-pro-tvorbu-uml-diagramu-z-prikazove-radky-ii/
  4. Nástroje pro tvorbu grafů a diagramů z příkazové řádky
    https://www.root.cz/clanky/nastroje-pro-tvorbu-grafu-a-diagramu-z-prikazove-radky/
  5. Sledování správy paměti v Pythonu s využitím nástroje objgraph
    https://www.root.cz/clanky/sledovani-spravy-pameti-v-pythonu-s-vyuzitim-nastroje-objgraph/
  6. Programová tvorba diagramů v jazyku Clojure s využitím knihovny Rhizome
    https://www.root.cz/clanky/programova-tvorba-diagramu-v-jazyku-clojure-s-vyuzitim-knihovny-rhizome/
  7. Tvorba sekvenčních diagramů v Pythonu s využitím knihovny Napkin
    https://www.root.cz/clanky/tvorba-sekvencnich-diagramu-v-pythonu-s-vyuzitim-knihovny-napkin/
  8. Tvorba vývojových diagramů přímo ze zdrojových kódů Pythonu
    https://www.root.cz/clanky/tvorba-vyvojovych-diagramu-primo-ze-zdrojovych-kodu-pythonu/
  9. Tvorba diagramů s architekturou systémů s využitím knihovny Diagrams
    https://www.root.cz/clanky/tvorba-diagramu-s-architekturou-systemu-s-vyuzitim-knihovny-diagrams/
  10. Knihovny Diagrams a go-diagrams určené pro tvorbu diagramů s architekturou systémů
    https://www.root.cz/clanky/knihovny-diagrams-a-go-diagrams-urcene-pro-tvorbu-diagramu-s-architekturou-systemu/
  11. Tvorba grafů a diagramů s využitím doménově specifického jazyka nástroje Graphviz
    https://www.root.cz/clanky/tvorba-grafu-a-diagramu-s-vyuzitim-domenove-specifickeho-jazyka-nastroje-graphviz/

19. Odkazy na Internetu

  1. GraphViz Pocket Reference
    https://graphs.grevian.org/example
  2. Xfig home page
    http://mcj.sourceforge.net/
  3. Xfig (Wikipedia)
    https://en.wikipedia.org/wiki/Xfig
  4. Xfig user manual
    http://mcj.sourceforge.net/
  5. HTML Entity List
    https://www.freeformatter.com/html-entities.html
  6. Flowchart (Wikipedia)
    https://en.wikipedia.org/wi­ki/Flowchart
  7. DRAKON
    https://en.wikipedia.org/wiki/DRAKON
  8. Modeling language
    https://en.wikipedia.org/wi­ki/Modeling_language
  9. Napkin na GitHubu
    https://github.com/pinetr2e/napkin
  10. Napkin 0.6.8 na PyPi
    https://pypi.org/project/napkin/
  11. PlantUML (home page)
    http://plantuml.sourceforge.net/
  12. PlantUML (download page)
    http://sourceforge.net/pro­jects/plantuml/files/plan­tuml.jar/download
  13. PlantUML (Language Reference Guide)
    http://plantuml.sourcefor­ge.net/PlantUML_Language_Re­ference_Guide.pdf
  14. Rhizome
    https://github.com/ztellman/rhizome
  15. Swagger to UML
    https://github.com/nlohman­n/swagger_to_uml
  16. pydiagrams
    https://github.com/billin­gtonm/pydiagrams
  17. graphviz(3) – Linux man page
    https://linux.die.net/man/3/graphviz
  18. dot(1) – Linux man page
    https://linux.die.net/man/1/dot
  19. neato(1) – Linux man page
    https://linux.die.net/man/1/neato
  20. twopi(1) – Linux man page
    https://linux.die.net/man/1/twopi
  21. circo(1) – Linux man page
    https://linux.die.net/man/1/circo
  22. fdp(1) – Linux man page
    https://linux.die.net/man/1/fdp
  23. sfdp(1) – Linux man page
    https://linux.die.net/man/1/sfdp
  24. Plain-text diagrams take shape in Asciidoctor!
    http://asciidoctor.org/new­s/2014/02/18/plain-text-diagrams-in-asciidoctor/
  25. Graphviz – Graph Visualization Software
    http://www.graphviz.org/
  26. graphviz (Manual Page)
    http://www.root.cz/man/7/graphviz/
  27. dot (Manual page)
    http://www.root.cz/man/1/dot/
  28. dot (Manual v PDF)
    https://graphviz.org/pdf/dot.1.pdf
  29. Ditaa home page
    http://ditaa.sourceforge.net/
  30. Ditaa introduction
    http://ditaa.sourceforge.net/#intro
  31. Ditaa usage
    http://ditaa.sourceforge.net/#usage
  32. Node, Edge and Graph Attributes
    http://www.graphviz.org/doc/in­fo/attrs.html
  33. Graphviz (Wikipedia)
    http://en.wikipedia.org/wiki/Graphviz
  34. Unified Modeling Language
    https://en.wikipedia.org/wi­ki/Unified_Modeling_Langu­age
  35. UML basics: The sequence diagram
    http://www.ibm.com/develo­perworks/rational/library/3101­.html
  36. UML 2 State Machine Diagrams: An Agile Introduction
    http://www.agilemodeling.com/ar­tifacts/stateMachineDiagram­.htm
  37. Sequence diagram (Wikipedia)
    https://en.wikipedia.org/wi­ki/Sequence_diagram
  38. UML 2 Sequence Diagrams: An Agile Introduction
    http://www.agilemodeling.com/ar­tifacts/sequenceDiagram.htm
  39. A Quick Introduction to UML Sequence Diagrams
    http://www.tracemodeler.com/ar­ticles/a_quick_introducti­on_to_uml_sequence_diagram­s/
  40. UML Sequence Diagrams
    https://www.uml-diagrams.org/sequence-diagrams.html
  41. Web Sequence Diagrams
    https://www.websequencediagrams.com/
  42. Drawing sequence diagrams “napkin style”
    https://modeling-languages.com/drawing-sequence-diagrams-napkin-style/
  43. Curated list of UML tools – 2020 edition
    https://modeling-languages.com/uml-tools/#textual
  44. Flowchart diagrams vs. UML activity diagrams
    https://stackoverflow.com/qu­estions/7081215/flowchart-diagrams-vs-uml-activity-diagrams
  45. Kopenograms – Graphical Language for Structured Algorithms
    https://kopenogram.org/As­sets/Kopenograms_Graphical_Lan­guage_for_Structured_Algo­rithms.pdf
  46. Kopenograms and Their Implementation in BlueJ
    https://link.springer.com/chap­ter/10.1007%2F978–3–319–46535–7_8
  47. The simplest way to describe your flows
    https://code2flow.com/
  48. Allan Mogensen and his Legacy
    http://www.worksimp.com/articles/allan-mogensen.htm
  49. Diagrams: Diagram as Code
    https://diagrams.mingrammer.com/
  50. Diagrams: Guides
    https://diagrams.mingrammer­.com/docs/guides/diagram
  51. Diagrams: Nodes
    https://diagrams.mingrammer­.com/docs/nodes/onprem
  52. go-diagrams
    https://github.com/blushft/go-diagrams
  53. GoJS
    https://gojs.net/latest/index.html
  54. Code visualization: How to turn complex code into diagrams
    https://www.lucidchart.com/blog/vi­sualize-code-documentation
  55. Create dependency diagrams from your code
    https://docs.microsoft.com/en-us/visualstudio/modeling/create-layer-diagrams-from-your-code?view=vs-2019
  56. Software Architecture Diagrams as Code
    https://shekhargulati.com/2020/04/21/sof­tware-architecture-diagrams-as-code/
  57. Processing spreadsheet data in Go
    https://appliedgo.net/spreadsheet/
  58. Stránka projektu plotly
    https://plot.ly/
  59. Plotly JavaScript Open Source Graphing Library
    https://plot.ly/javascript/
  60. Domain coloring
    https://en.wikipedia.org/wi­ki/Domain_coloring
  61. The Gonum Numerical Computing Package
    https://www.gonum.org/pos­t/introtogonum/
  62. Gomacro na GitHubu
    https://github.com/cosmos72/gomacro
  63. gophernotes – Use Go in Jupyter notebooks and nteract
    https://github.com/gopher­data/gophernotes
  64. gonum
    https://github.com/gonum
  65. go-gota/gota – DataFrames and data wrangling in Go (Golang)
    https://porter.io/github.com/go-gota/gota
  66. A repository for plotting and visualizing data
    https://github.com/gonum/plot
  67. Gonum Numerical Packages
    https://www.gonum.org/
  68. Getting started with Go modules
    https://medium.com/@fonse­ka.live/getting-started-with-go-modules-b3dac652066d
  69. Create projects independent of $GOPATH using Go Modules
    https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o
  70. Anatomy of Modules in Go
    https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16
  71. Modules
    https://github.com/golang/go/wi­ki/Modules
  72. Go Modules Tutorial
    https://tutorialedge.net/golang/go-modules-tutorial/
  73. Module support
    https://golang.org/cmd/go/#hdr-Module_support
  74. Go vs. Python
    https://www.peterbe.com/plog/govspy
  75. PackageManagementTools
    https://github.com/golang/go/wi­ki/PackageManagementTools
  76. Sketchviz
    https://sketchviz.com/
  77. SchemaSpy (verze pro starší JVM)
    http://schemaspy.sourceforge.net/
  78. GitHub na GitHubu
    https://github.com/schemas­py/schemaspy

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.