From ec4fd08373005be1d04af26798e09e1242eede04 Mon Sep 17 00:00:00 2001 From: "James D. Mitchell" Date: Fri, 3 May 2024 17:46:43 +0100 Subject: [PATCH] Updates for the Digraphs package --- PackageInfo.g | 33 +- etc/gaplint.sh | 4 + etc/tst-local-vars.py | 40 ++ examples/{angles.gi => angles.g} | 18 +- examples/btree.g | 39 ++ examples/btree.gi | 27 -- examples/{cluster.gi => cluster.g} | 12 + examples/{cluster_edge.gi => cluster_edge.g} | 15 +- examples/colors.g | 35 ++ examples/colors.gi | 23 - examples/{er.gi => er.g} | 14 +- examples/{fsm.gi => fsm.g} | 14 +- examples/{g_c_n.gi => g_c_n.g} | 14 +- examples/hello.g | 17 + examples/hello.gi | 6 - examples/{process.gi => process.g} | 13 + examples/{rank_same.gi => rank_same.g} | 12 + examples/structs.g | 55 +++ examples/structs.gi | 43 -- ...traffic_lights_WIP.gi => traffic_lights.g} | 18 +- examples/{unix.gi => unix.g} | 14 +- gap/dot.gd | 18 + gap/dot.gi | 441 +++++++++++++++--- gap/splash.gi | 73 +-- init.g | 16 +- makedoc.g | 18 +- read.g | 16 +- tst/dot.tst | 30 +- tst/edge.tst | 11 +- tst/examples/angles.tst | 145 ++++++ tst/examples/btree.tst | 73 +++ tst/examples/cluster.tst | 87 ++++ tst/examples/cluster_edge.tst | 63 +++ tst/examples/colors.tst | 68 +++ tst/examples/er.tst | 134 ++++++ tst/examples/fsm.tst | 89 ++++ tst/examples/g_c_n.tst | 66 +++ tst/examples/hello.tst | 27 ++ tst/examples/process.tst | 58 +++ tst/examples/rank_same.tst | 67 +++ tst/examples/structs.tst | 80 ++++ tst/examples/traffic_lights.tst | 94 ++++ tst/examples/unix.tst | 286 ++++++++++++ tst/graph.tst | 13 +- tst/node.tst | 15 +- tst/subgraph.tst | 43 +- tst/testall.g | 10 +- 47 files changed, 2213 insertions(+), 294 deletions(-) create mode 100755 etc/gaplint.sh create mode 100755 etc/tst-local-vars.py rename examples/{angles.gi => angles.g} (76%) create mode 100644 examples/btree.g delete mode 100644 examples/btree.gi rename examples/{cluster.gi => cluster.g} (77%) rename examples/{cluster_edge.gi => cluster_edge.g} (65%) create mode 100644 examples/colors.g delete mode 100644 examples/colors.gi rename examples/{er.gi => er.g} (83%) rename examples/{fsm.gi => fsm.g} (80%) rename examples/{g_c_n.gi => g_c_n.g} (58%) create mode 100644 examples/hello.g delete mode 100644 examples/hello.gi rename examples/{process.gi => process.g} (65%) rename examples/{rank_same.gi => rank_same.g} (62%) create mode 100644 examples/structs.g delete mode 100644 examples/structs.gi rename examples/{traffic_lights_WIP.gi => traffic_lights.g} (77%) rename examples/{unix.gi => unix.g} (85%) create mode 100644 tst/examples/angles.tst create mode 100644 tst/examples/btree.tst create mode 100644 tst/examples/cluster.tst create mode 100644 tst/examples/cluster_edge.tst create mode 100644 tst/examples/colors.tst create mode 100644 tst/examples/er.tst create mode 100644 tst/examples/fsm.tst create mode 100644 tst/examples/g_c_n.tst create mode 100644 tst/examples/hello.tst create mode 100644 tst/examples/process.tst create mode 100644 tst/examples/rank_same.tst create mode 100644 tst/examples/structs.tst create mode 100644 tst/examples/traffic_lights.tst create mode 100644 tst/examples/unix.tst diff --git a/PackageInfo.g b/PackageInfo.g index 74e51c1..cfb5046 100644 --- a/PackageInfo.g +++ b/PackageInfo.g @@ -1,12 +1,13 @@ -# -# graphviz: This package facilitates the creation of graph descriptions in the -# DOT language of the Graphviz graph drawing software from GAP -# -# This file contains package meta data. For additional information on -# the meaning and correct usage of these fields, please consult the -# manual of the "Example" package as well as the comments in its -# PackageInfo.g file. -# +############################################################################# +## +## PackageInfo.g +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + SetPackageInfo(rec( PackageName := "graphviz", @@ -26,12 +27,24 @@ Persons := [ PostalAddress := Concatenation("Mathematical Institute, North Haugh,", " St Andrews, Fife, KY16 9SS, Scotland"), Place := "St Andrews", + Institution := "University of St Andrews"), + rec( + FirstNames := "Matthew", + LastName := "Pancer", + Email := "mp322@st-andrews.ac.uk", + IsAuthor := true, + IsMaintainer := true, + PostalAddress := Concatenation("Mathematical Institute, North Haugh,", + " St Andrews, Fife, KY16 9SS, Scotland"), + # TODO correct? Or should be cs? + Place := "St Andrews", Institution := "University of St Andrews")], SourceRepository := rec(Type := "git", URL := "https://github.com/digraphs/graphviz"), IssueTrackerURL := "https://github.com/digraphs/graphviz/issues", -PackageWWWHome := "TODO", +PackageWWWHome := Concatenation("https://digraphs.github.io/", + ~.PackageName), PackageInfoURL := Concatenation(~.PackageWWWHome, "PackageInfo.g"), README_URL := Concatenation(~.PackageWWWHome, "README.md"), diff --git a/etc/gaplint.sh b/etc/gaplint.sh new file mode 100755 index 0000000..0d8af69 --- /dev/null +++ b/etc/gaplint.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -e + +gaplint --disable W004 $@ *.g gap/* read.g init.g PackageInfo.g makedoc.g tst/testall.g tst/*.tst tst/examples/*.tst diff --git a/etc/tst-local-vars.py b/etc/tst-local-vars.py new file mode 100755 index 0000000..915c453 --- /dev/null +++ b/etc/tst-local-vars.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +""" +This simple script adds the local variables used in a GAP tst file to the top +of the file. +""" +import os +import re +import sys +import textwrap + +import yaml +from bs4 import BeautifulSoup + + +def main(): + if sys.version_info[0] < 3: + raise Exception("Python 3 is required") + args = sys.argv[1:] + pattern1 = re.compile(r"(\w+)\s*:=") + pattern2 = re.compile(r"for (\w+) in") + for fname in args: + lvars = [] + with open(fname, "r") as f: + lines = f.read() + lvars.extend([x.group(1) for x in re.finditer(pattern1, lines)]) + lvars.extend([x.group(1) for x in re.finditer(pattern2, lines)]) + lvars = ", ".join(sorted([*{*lvars}])) + lvars = [x if x[-1] != "," else x[:-1] for x in textwrap.wrap(lvars, width=72)] + lvars = [""] + ["#@local " + x for x in lvars] + lines = lines.split("\n") + pos = next(i for i in range(len(lines)) if "START_TEST" in lines[i]) + lines = lines[:pos] + lvars + lines[pos:] + lines = "\n".join(lines) + with open(fname, "w") as f: + print(f"Writing local variables to {fname}...") + f.write(lines) + + +if __name__ == "__main__": + main() diff --git a/examples/angles.gi b/examples/angles.g similarity index 76% rename from examples/angles.gi rename to examples/angles.g index 723cafa..219b20f 100644 --- a/examples/angles.gi +++ b/examples/angles.g @@ -1,3 +1,13 @@ +############################################################################# +## +## angles.g +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + # https://graphviz.readthedocs.io/en/stable/examples.html # https://www.graphviz.org/Gallery/gradient/angles.html @@ -21,7 +31,7 @@ for pair in pairs do od; GraphvizSetAttr(cluster1, "label", - "\"Linear Angle Variations (white to black gradient)\""); + "Linear Angle Variations (white to black gradient)"); cluster2 := GraphvizAddSubgraph(g, "cluster_2"); GraphvizSetAttr(cluster2, "fontcolor", "white"); @@ -38,7 +48,9 @@ for pair in pairs do pair[1], pair[2])); od; GraphvizSetAttr(cluster2, "label", - "\"Radial Angle Variations (white to black gradient)\""); + "Radial Angle Variations (white to black gradient)"); GraphvizAddEdge(g, "n5", "n14"); -Print(AsString(g), "\n"); +Print(AsString(g)); +Splash(g); +QUIT; diff --git a/examples/btree.g b/examples/btree.g new file mode 100644 index 0000000..2596c68 --- /dev/null +++ b/examples/btree.g @@ -0,0 +1,39 @@ +############################################################################# +## +## btree.g +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# https://graphviz.readthedocs.io/en/stable/examples.html +# https://graphviz.org/Gallery/directed/unix.html +LoadPackage("graphviz"); + +s := GraphvizDigraph("g"); +GraphvizSetAttr(s, "node [shape=record, height=.1]"); + +GraphvizSetAttr(GraphvizAddNode(s, "node0"), "label", " | G|"); +GraphvizSetAttr(GraphvizAddNode(s, "node1"), "label", " | E|"); +GraphvizSetAttr(GraphvizAddNode(s, "node2"), "label", " | B|"); +GraphvizSetAttr(GraphvizAddNode(s, "node3"), "label", " | F|"); +GraphvizSetAttr(GraphvizAddNode(s, "node4"), "label", " | R|"); +GraphvizSetAttr(GraphvizAddNode(s, "node5"), "label", " | H|"); +GraphvizSetAttr(GraphvizAddNode(s, "node6"), "label", " | Y|"); +GraphvizSetAttr(GraphvizAddNode(s, "node7"), "label", " | A|"); +GraphvizSetAttr(GraphvizAddNode(s, "node8"), "label", " | C|"); + +GraphvizAddEdge(s, "node0:f2", "node4:f1"); +GraphvizAddEdge(s, "node0:f0", "node1:f1"); +GraphvizAddEdge(s, "node1:f0", "node2:f1"); +GraphvizAddEdge(s, "node1:f2", "node3:f1"); +GraphvizAddEdge(s, "node2:f2", "node8:f1"); +GraphvizAddEdge(s, "node2:f0", "node7:f1"); +GraphvizAddEdge(s, "node4:f2", "node6:f1"); +GraphvizAddEdge(s, "node4:f0", "node5:f1"); + +Print(AsString(s)); +Splash(s); +QUIT; diff --git a/examples/btree.gi b/examples/btree.gi deleted file mode 100644 index c8ea6d6..0000000 --- a/examples/btree.gi +++ /dev/null @@ -1,27 +0,0 @@ -# https://graphviz.readthedocs.io/en/stable/examples.html -# """https://graphviz.org/Gallery/directed/unix.html""" -LoadPackage("graphviz"); - -s := GraphvizDigraph("g"); -GraphvizSetAttr(s, "node [shape=record, height=.1]"); - -GraphvizSetAttr(GraphvizAddNode(s, "node0"), "label", "\" | G| \""); -GraphvizSetAttr(GraphvizAddNode(s, "node1"), "label", "\" | E| \""); -GraphvizSetAttr(GraphvizAddNode(s, "node2"), "label", "\" | B| \""); -GraphvizSetAttr(GraphvizAddNode(s, "node3"), "label", "\" | F| \""); -GraphvizSetAttr(GraphvizAddNode(s, "node4"), "label", "\" | R| \""); -GraphvizSetAttr(GraphvizAddNode(s, "node5"), "label", "\" | H| \""); -GraphvizSetAttr(GraphvizAddNode(s, "node6"), "label", "\" | Y| \""); -GraphvizSetAttr(GraphvizAddNode(s, "node7"), "label", "\" | A| \""); -GraphvizSetAttr(GraphvizAddNode(s, "node8"), "label", "\" | C| \""); - -GraphvizAddEdge(s, "node0:f2", "node4:f1"); -GraphvizAddEdge(s, "node0:f0", "node1:f1"); -GraphvizAddEdge(s, "node1:f0", "node2:f1"); -GraphvizAddEdge(s, "node1:f2", "node3:f1"); -GraphvizAddEdge(s, "node2:f2", "node8:f1"); -GraphvizAddEdge(s, "node2:f0", "node7:f1"); -GraphvizAddEdge(s, "node4:f2", "node6:f1"); -GraphvizAddEdge(s, "node4:f0", "node5:f1"); - -Print(AsString(s)); diff --git a/examples/cluster.gi b/examples/cluster.g similarity index 77% rename from examples/cluster.gi rename to examples/cluster.g index 1b70802..0460bd4 100644 --- a/examples/cluster.gi +++ b/examples/cluster.g @@ -1,3 +1,13 @@ +############################################################################# +## +## cluster.g +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + # https://graphviz.readthedocs.io/en/stable/examples.html LoadPackage("graphviz"); graph := GraphvizDigraph("G"); @@ -31,3 +41,5 @@ GraphvizSetAttr(graph["start"], "shape", "Mdiamond"); GraphvizSetAttr(graph["end"], "shape", "Msquare"); Print(AsString(graph)); +Splash(graph); +QUIT; diff --git a/examples/cluster_edge.gi b/examples/cluster_edge.g similarity index 65% rename from examples/cluster_edge.gi rename to examples/cluster_edge.g index 813bf88..9ce9556 100644 --- a/examples/cluster_edge.gi +++ b/examples/cluster_edge.g @@ -1,5 +1,15 @@ +############################################################################# +## +## cluster_edge.g +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + # https://graphviz.readthedocs.io/en/stable/examples.html -# """https://www.graphviz.org/pdf/dotguide.pdf, Figure 20""" +# https://www.graphviz.org/pdf/dotguide.pdf, Figure 20 LoadPackage("graphviz"); @@ -28,4 +38,5 @@ GraphvizSetAttr(e, "ltail", "cluster0"); GraphvizAddEdge(g, "d", "h"); -Print(AsString(g)); +Splash(g); +QUIT; diff --git a/examples/colors.g b/examples/colors.g new file mode 100644 index 0000000..9a6f2e5 --- /dev/null +++ b/examples/colors.g @@ -0,0 +1,35 @@ +############################################################################# +## +## colors.g +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# https://graphviz.readthedocs.io/en/stable/examples.html +# https://graphviz.org/docs/attr-types/color + +LoadPackage("graphviz"); +g := GraphvizGraph(); + +node := GraphvizAddNode(g, "RGB: #40e0d0"); +GraphvizSetAttr(node, "style", "filled"); +GraphvizSetAttr(node, "fillcolor", "\"#40e0d0\""); + +node := GraphvizAddNode(g, "RGBA: #ff000042"); +GraphvizSetAttr(node, "style", "filled"); +GraphvizSetAttr(node, "fillcolor", "\"#ff000042\""); + +node := GraphvizAddNode(g, "HSV: 0.051 0.718 0.627"); +GraphvizSetAttr(node, "style", "filled"); +GraphvizSetAttr(node, "fillcolor", "0.051 0.718 0.627"); + +node := GraphvizAddNode(g, "name: deeppink"); +GraphvizSetAttr(node, "style", "filled"); +GraphvizSetAttr(node, "fillcolor", "deeppink"); + +Print(AsString(g)); +Splash(g); +QUIT; diff --git a/examples/colors.gi b/examples/colors.gi deleted file mode 100644 index 2a9c9bc..0000000 --- a/examples/colors.gi +++ /dev/null @@ -1,23 +0,0 @@ -# https://graphviz.readthedocs.io/en/stable/examples.html -# """https://graphviz.org/docs/attr-types/color""" - -LoadPackage("graphviz"); -g := GraphvizGraph(); - -node := GraphvizAddNode(g, "RGB #40e0d0"); -GraphvizSetAttr(node, "style", "filled"); -GraphvizSetAttr(node, "fillcolor", "#40e0d0"); - -node := GraphvizAddNode(g, "RGBA #ff000042"); -GraphvizSetAttr(node, "style", "filled"); -GraphvizSetAttr(node, "fillcolor", "#ff000042"); - -node := GraphvizAddNode(g, "HSV 0.051 0.718 0.627"); -GraphvizSetAttr(node, "style", "filled"); -GraphvizSetAttr(node, "fillcolor", "0.051 0.718 0.627"); - -node := GraphvizAddNode(g, "name deeppink"); -GraphvizSetAttr(node, "style", "filled"); -GraphvizSetAttr(node, "fillcolor", "deeppink"); - -Print(AsString(g)); diff --git a/examples/er.gi b/examples/er.g similarity index 83% rename from examples/er.gi rename to examples/er.g index dea5a6d..2025c07 100644 --- a/examples/er.gi +++ b/examples/er.g @@ -1,5 +1,15 @@ +############################################################################# +## +## er.g +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + # https://graphviz.readthedocs.io/en/stable/examples.html -# """https://graphviz.org/Gallery/undirected/ER.html""" +# https://graphviz.org/Gallery/undirected/ER.html LoadPackage("graphviz"); e := GraphvizGraph("ER"); GraphvizSetAttr(e, "engine=\"neato\""); @@ -49,3 +59,5 @@ GraphvizSetAttr(e, "label=\"Entity Relation Diagram\ndrawn by NEATO\""); GraphvizSetAttr(e, "fontsize=\"20\""); Print(AsString(e)); +Splash(e); +QUIT; diff --git a/examples/fsm.gi b/examples/fsm.g similarity index 80% rename from examples/fsm.gi rename to examples/fsm.g index 3ba54ad..46a8126 100644 --- a/examples/fsm.gi +++ b/examples/fsm.g @@ -1,5 +1,15 @@ +############################################################################# +## +## fsm.g +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + # https://graphviz.readthedocs.io/en/stable/examples.html -# """https://graphviz.org/Gallery/directed/fsm.html""" +# https://graphviz.org/Gallery/directed/fsm.html LoadPackage("graphviz"); f := GraphvizDigraph("finite_state_machine"); @@ -31,3 +41,5 @@ GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_8", "LR_6"), "label", "\"S(b)\""); GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_8", "LR_5"), "label", "\"S(a)\""); Print(AsString(f)); +Splash(f); +QUIT; diff --git a/examples/g_c_n.gi b/examples/g_c_n.g similarity index 58% rename from examples/g_c_n.gi rename to examples/g_c_n.g index 5a083f1..1a47f7b 100644 --- a/examples/g_c_n.gi +++ b/examples/g_c_n.g @@ -1,5 +1,15 @@ +############################################################################# +## +## g_c_n.g +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + # https://graphviz.readthedocs.io/en/stable/examples.html -# """https://www.graphviz.org/Gallery/gradient/g_c_n.html""" +# https://www.graphviz.org/Gallery/gradient/g_c_n.html LoadPackage("graphviz"); @@ -16,3 +26,5 @@ GraphvizSetAttr(cluster1, Concatenation( GraphvizAddNode(cluster1, "anode"); Print(AsString(g)); +Splash(g); +QUIT; diff --git a/examples/hello.g b/examples/hello.g new file mode 100644 index 0000000..917c5bd --- /dev/null +++ b/examples/hello.g @@ -0,0 +1,17 @@ +############################################################################# +## +## hello.g +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# https://graphviz.readthedocs.io/en/stable/examples.html +LoadPackage("graphviz"); +graph := GraphvizDigraph("G"); +GraphvizAddEdge(graph, "hello", "world"); +Print(AsString(graph)); +Splash(graph); +QUIT; diff --git a/examples/hello.gi b/examples/hello.gi deleted file mode 100644 index 51ec486..0000000 --- a/examples/hello.gi +++ /dev/null @@ -1,6 +0,0 @@ -# https://graphviz.readthedocs.io/en/stable/examples.html -LoadPackage("graphviz"); -graph := GraphvizDigraph("G"); -GraphvizAddEdge(graph, "hello", "world"); -Print(AsString(graph)); - diff --git a/examples/process.gi b/examples/process.g similarity index 65% rename from examples/process.gi rename to examples/process.g index c82ae2e..d9242dc 100644 --- a/examples/process.gi +++ b/examples/process.g @@ -1,4 +1,15 @@ +############################################################################# +## +## process.g +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + # https://graphviz.readthedocs.io/en/stable/examples.html + LoadPackage("graphviz"); graph := GraphvizGraph("G"); GraphvizSetAttr(graph, "engine=\"sfdp\""); @@ -16,3 +27,5 @@ GraphvizAddEdge(graph, "runswap", "new"); GraphvizAddEdge(graph, "runswap", "runmem"); GraphvizAddEdge(graph, "new", "runmem"); Print(AsString(graph)); +Splash(graph); +QUIT; diff --git a/examples/rank_same.gi b/examples/rank_same.g similarity index 62% rename from examples/rank_same.gi rename to examples/rank_same.g index 64824a2..6dd8ca0 100644 --- a/examples/rank_same.gi +++ b/examples/rank_same.g @@ -1,3 +1,13 @@ +############################################################################# +## +## rank_same.g +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + # https://graphviz.readthedocs.io/en/stable/examples.html # https://stackoverflow.com/questions/25734244 @@ -23,3 +33,5 @@ GraphvizAddEdge(g, "C", "D"); GraphvizAddEdge(g, "X", "Y"); Print(AsString(g)); +Splash(g); +QUIT; diff --git a/examples/structs.g b/examples/structs.g new file mode 100644 index 0000000..a764a7e --- /dev/null +++ b/examples/structs.g @@ -0,0 +1,55 @@ +############################################################################# +## +## structs.g +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# https://graphviz.readthedocs.io/en/stable/examples.html +# https://graphviz.org/Gallery/directed/unix.html +LoadPackage("graphviz"); + +s := GraphvizDigraph("structs"); +GraphvizSetAttr(s, "node [shape=\"plaintext\"]"); + +GraphvizSetAttr(GraphvizAddNode(s, "struct1"), "label", +"""< + + + + +
leftmiddleright
>"""); +GraphvizSetAttr(GraphvizAddNode(s, "struct2"), "label", +"""< + + + + +
onetwo
>"""); +GraphvizSetAttr(GraphvizAddNode(s, "struct3"), "label", +"""< + + + + + + + + + + + + + + +
hello
world
bgh
cde
f
>"""); + +GraphvizAddEdge(s, "struct1:f1", "struct2:f0"); +GraphvizAddEdge(s, "struct1:f2", "struct3:here"); + +Print(AsString(s)); +Splash(s); +QUIT; diff --git a/examples/structs.gi b/examples/structs.gi deleted file mode 100644 index 24f13fc..0000000 --- a/examples/structs.gi +++ /dev/null @@ -1,43 +0,0 @@ -# # https://graphviz.readthedocs.io/en/stable/examples.html -# # """https://graphviz.org/Gallery/directed/unix.html""" -LoadPackage("graphviz"); - -s := GraphvizDigraph("structs"); -GraphvizSetAttr(s, "node [shape=\"plaintext\"]"); - -GraphvizSetAttr(GraphvizAddNode(s, "struct1"), "label", -"""< - - - - -
leftmiddleright
>"""); -GraphvizSetAttr(GraphvizAddNode(s, "struct2"), "label", -"""< - - - - -
onetwo
>"""); -GraphvizSetAttr(GraphvizAddNode(s, "struct3"), "label", -"""< - - - - - - - - - - - - - - -
hello
world
bgh
cde
f
>"""); - -GraphvizAddEdge(s, "struct1:f1", "struct2:f0"); -GraphvizAddEdge(s, "struct1:f2", "struct3:here"); - -Print(AsString(s)); diff --git a/examples/traffic_lights_WIP.gi b/examples/traffic_lights.g similarity index 77% rename from examples/traffic_lights_WIP.gi rename to examples/traffic_lights.g index 410872a..1162f2a 100644 --- a/examples/traffic_lights_WIP.gi +++ b/examples/traffic_lights.g @@ -1,5 +1,15 @@ +############################################################################# +## +## traffic_lights.g +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + # https://graphviz.readthedocs.io/en/stable/examples.html -# """https://graphviz.org/Gallery/directed/unix.html""" +# https://graphviz.org/Gallery/directed/unix.html LoadPackage("graphviz"); t := GraphvizDigraph("TrafficLights"); @@ -46,9 +56,11 @@ od; GraphvizSetAttr(t, "overlap=\"false\""); GraphvizSetAttr(t, -"""label=\"PetriNet Model TrafficLights -Extracted from ConceptBase and laid out by Graphviz\" +"""label="PetriNet Model TrafficLights +Extracted from ConceptBase and laid out by Graphviz" """); GraphvizSetAttr(t, "fontsize=12"); Print(AsString(t)); +Splash(t); +QUIT; diff --git a/examples/unix.gi b/examples/unix.g similarity index 85% rename from examples/unix.gi rename to examples/unix.g index f5c7aed..65abf5a 100644 --- a/examples/unix.gi +++ b/examples/unix.g @@ -1,5 +1,15 @@ +############################################################################# +## +## unix.g +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + # https://graphviz.readthedocs.io/en/stable/examples.html -# """https://graphviz.org/Gallery/directed/unix.html""" +# https://graphviz.org/Gallery/directed/unix.html LoadPackage("graphviz"); u := GraphvizDigraph("unix"); @@ -57,3 +67,5 @@ GraphvizAddEdge(u, "System V.0", "System V.2"); GraphvizAddEdge(u, "System V.2", "System V.3"); Print(AsString(u)); +Splash(u); +QUIT; diff --git a/gap/dot.gd b/gap/dot.gd index 51d2f20..48cf367 100644 --- a/gap/dot.gd +++ b/gap/dot.gd @@ -1,3 +1,13 @@ +############################################################################# +## +## dot.gd +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + #! @Chapter #! @ChapterTitle An introduction to the DOT language and Graphviz. #! This chapter explains what the DOT and graphviz are, @@ -78,6 +88,7 @@ DeclareOperation("GraphvizAttrs", [IsGVObject]); #! @Description Gets the nodes of the provided graphviz graph. #! Node names can only be [a-zA-Z0-9_£] TODO check exact docs. DeclareOperation("GraphvizNodes", [IsGVGraph]); +DeclareOperation("GraphvizNode", [IsGVGraph, IsObject]); #! @Arguments graph #! @Returns the subgraphs of the provided graphviz graph. @@ -98,6 +109,7 @@ DeclareOperation("GraphvizFindGraph", [IsGVGraph, IsObject]); #! @Returns the edges of the provided graphviz graph. #! @Description Gets the edges of the provided graphviz graph. DeclareOperation("GraphvizEdges", [IsGVGraph]); +DeclareOperation("GraphvizEdges", [IsGVGraph, IsObject, IsObject]); #! @Subsection For only edges. @@ -182,6 +194,7 @@ DeclareOperation("GraphvizSetAttr", [IsGVObject, IsObject]); #! Updates the label of the object. #! If a label already exists and a new value is provided, the old value will #! be overwritten. +# TODO remove DeclareOperation("GraphvizSetLabel", [IsGVObject, IsObject]); #! @Arguments obj, color @@ -190,6 +203,7 @@ DeclareOperation("GraphvizSetLabel", [IsGVObject, IsObject]); #! Updates the color of the object. #! If a color already exists and a new value is provided, the old value will #! be overwritten. +# TODO remove DeclareOperation("GraphvizSetColor", [IsGVObject, IsObject]); #! @Arguments obj, attr @@ -208,3 +222,7 @@ DeclareOperation("AsString", [IsGVGraph]); #! Unimplemented operation which depending packages can implement. #! Should output the graphviz package representation of the object. DeclareOperation("Graphviz", [IsObject]); + +DeclareOperation("GraphvizSetNodeColors", [IsGVGraph, IsList]); +DeclareOperation("GraphvizSetNodeLabels", [IsGVGraph, IsList]); +DeclareGlobalFunction("ErrorFormatted"); diff --git a/gap/dot.gi b/gap/dot.gi index d91fb55..de12924 100644 --- a/gap/dot.gi +++ b/gap/dot.gi @@ -1,10 +1,59 @@ -# ############################################################################## +############################################################################# +## +## dot.gi +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +############################################################################# # Private functionality -# ############################################################################## +############################################################################# + +BindGlobal("NumberOfSubstrings", +function(string, substring) + local pos, count; + + pos := 0; + count := 0; + while pos <= Length(string) and pos <> fail do + pos := PositionSublist(string, substring, pos); + if pos <> fail then + count := count + 1; + fi; + od; + return count; +end); + +InstallGlobalFunction(ErrorFormatted, +function(arg...) + local pos, fmt, n, msg; + + pos := PositionProperty(arg, x -> not IsString(x)); + if pos = fail then + pos := Length(arg) + 1; + fi; + fmt := Concatenation(arg{[1 .. pos - 1]}); + n := NumberOfSubstrings(fmt, "{}"); + arg := Concatenation([Concatenation(arg{[1 .. Length(arg) - n]})], + arg{[Length(arg) - n + 1 .. Length(arg)]}); + msg := CallFuncList(StringFormatted, arg); + # RemoveCharacters(msg, "\\\n"); + ErrorInner( + rec(context := ParentLVars(GetCurrentLVars()), + mayReturnVoid := false, + mayReturnObj := false, + lateMessage := "type 'quit;' to quit to outer loop", + printThisStatement := false), + [msg]); +end); DeclareOperation("GV_GetCounter", [IsGVGraph]); DeclareOperation("GV_IncCounter", [IsGVGraph]); DeclareCategory("IsGV_Map", IsObject); +DeclareAttribute("Size", IsGV_Map); DeclareOperation("GV_StringifyGraphHead", [IsGVGraph]); DeclareOperation("GV_StringifyDigraphHead", [IsGVGraph]); @@ -52,6 +101,122 @@ BindGlobal("GV_KNOWN_ATTRS", [ "z" ]); +BindGlobal("GV_ValidColorNames", + ["aliceblue", "antiquewhite", "antiquewhite1", "antiquewhite2", + "antiquewhite3", "antiquewhite4", "aquamarine", "aquamarine1", "aquamarine2", + "aquamarine3", "aquamarine4", "azure", "azure1", "azure2", "azure3", + "azure4", "beige", "bisque", "bisque1", "bisque2", "bisque3", "bisque4", + "black", "blanchedalmond", "blue", "blue1", "blue2", "blue3", "blue4", + "blueviolet", "brown", "brown1", "brown2", "brown3", "brown4", "burlywood", + "burlywood1", "burlywood2", "burlywood3", "burlywood4", "cadetblue", + "cadetblue1", "cadetblue2", "cadetblue3", "cadetblue4", "chartreuse", + "chartreuse1", "chartreuse2", "chartreuse3", "chartreuse4", "chocolate", + "chocolate1", "chocolate2", "chocolate3", "chocolate4", "coral", "coral1", + "coral2", "coral3", "coral4", "cornflowerblue", "cornsilk", "cornsilk1", + "cornsilk2", "cornsilk3", "cornsilk4", "crimson", "cyan", "cyan1", "cyan2", + "cyan3", "cyan4", "darkgoldenrod", "darkgoldenrod1", "darkgoldenrod2", + "darkgoldenrod3", "darkgoldenrod4", "darkgreen", "darkkhaki", + "darkolivegreen", "darkolivegreen1", "darkolivegreen2", "darkolivegreen3", + "darkolivegreen4", "darkorange", "darkorange1", "darkorange2", "darkorange3", + "darkorange4", "darkorchid", "darkorchid1", "darkorchid2", "darkorchid3", + "darkorchid4", "darksalmon", "darkseagreen", "darkseagreen1", + "darkseagreen2", "darkseagreen3", "darkseagreen4", "darkslateblue", + "darkslategray", "darkslategray1", "darkslategray2", "darkslategray3", + "darkslategray4", "darkslategrey", "darkturquoise", "darkviolet", "deeppink", + "deeppink1", "deeppink2", "deeppink3", "deeppink4", "deepskyblue", + "deepskyblue1", "deepskyblue2", "deepskyblue3", "deepskyblue4", "dimgray", + "dimgrey", "dodgerblue", "dodgerblue1", "dodgerblue2", "dodgerblue3", + "dodgerblue4", "firebrick", "firebrick1", "firebrick2", "firebrick3", + "firebrick4", "floralwhite", "forestgreen", "gainsboro", "ghostwhite", + "gold", "gold1", "gold2", "gold3", "gold4", "goldenrod", "goldenrod1", + "goldenrod2", "goldenrod3", "goldenrod4", "gray", "gray0", "gray1", "gray10", + "gray100", "gray11", "gray12", "gray13", "gray14", "gray15", "gray16", + "gray17", "gray18", "gray19", "gray2", "gray20", "gray21", "gray22", + "gray23", "gray24", "gray25", "gray26", "gray27", "gray28", "gray29", + "gray3", "gray30", "gray31", "gray32", "gray33", "gray34", "gray35", + "gray36", "gray37", "gray38", "gray39", "gray4", "gray40", "gray41", + "gray42", "gray43", "gray44", "gray45", "gray46", "gray47", "gray48", + "gray49", "gray5", "gray50", "gray51", "gray52", "gray53", "gray54", + "gray55", "gray56", "gray57", "gray58", "gray59", "gray6", "gray60", + "gray61", "gray62", "gray63", "gray64", "gray65", "gray66", "gray67", + "gray68", "gray69", "gray7", "gray70", "gray71", "gray72", "gray73", + "gray74", "gray75", "gray76", "gray77", "gray78", "gray79", "gray8", + "gray80", "gray81", "gray82", "gray83", "gray84", "gray85", "gray86", + "gray87", "gray88", "gray89", "gray9", "gray90", "gray91", "gray92", + "gray93", "gray94", "gray95", "gray96", "gray97", "gray98", "gray99", + "green", "green1", "green2", "green3", "green4", "greenyellow", "grey", + "grey0", "grey1", "grey10", "grey100", "grey11", "grey12", "grey13", + "grey14", "grey15", "grey16", "grey17", "grey18", "grey19", "grey2", + "grey20", "grey21", "grey22", "grey23", "grey24", "grey25", "grey26", + "grey27", "grey28", "grey29", "grey3", "grey30", "grey31", "grey32", + "grey33", "grey34", "grey35", "grey36", "grey37", "grey38", "grey39", + "grey4", "grey40", "grey41", "grey42", "grey43", "grey44", "grey45", + "grey46", "grey47", "grey48", "grey49", "grey5", "grey50", "grey51", + "grey52", "grey53", "grey54", "grey55", "grey56", "grey57", "grey58", + "grey59", "grey6", "grey60", "grey61", "grey62", "grey63", "grey64", + "grey65", "grey66", "grey67", "grey68", "grey69", "grey7", "grey70", + "grey71", "grey72", "grey73", "grey74", "grey75", "grey76", "grey77", + "grey78", "grey79", "grey8", "grey80", "grey81", "grey82", "grey83", + "grey84", "grey85", "grey86", "grey87", "grey88", "grey89", "grey9", + "grey90", "grey91", "grey92", "grey93", "grey94", "grey95", "grey96", + "grey97", "grey98", "grey99", "honeydew", "honeydew1", "honeydew2", + "honeydew3", "honeydew4", "hotpink", "hotpink1", "hotpink2", "hotpink3", + "hotpink4", "indianred", "indianred1", "indianred2", "indianred3", + "indianred4", "indigo", "invis", "ivory", "ivory1", "ivory2", "ivory3", + "ivory4", "khaki", "khaki1", "khaki2", "khaki3", "khaki4", "lavender", + "lavenderblush", "lavenderblush1", "lavenderblush2", "lavenderblush3", + "lavenderblush4", "lawngreen", "lemonchiffon", "lemonchiffon1", + "lemonchiffon2", "lemonchiffon3", "lemonchiffon4", "lightblue", "lightblue1", + "lightblue2", "lightblue3", "lightblue4", "lightcoral", "lightcyan", + "lightcyan1", "lightcyan2", "lightcyan3", "lightcyan4", "lightgoldenrod", + "lightgoldenrod1", "lightgoldenrod2", "lightgoldenrod3", "lightgoldenrod4", + "lightgoldenrodyellow", "lightgray", "lightgrey", "lightpink", "lightpink1", + "lightpink2", "lightpink3", "lightpink4", "lightsalmon", "lightsalmon1", + "lightsalmon2", "lightsalmon3", "lightsalmon4", "lightseagreen", + "lightskyblue", "lightskyblue1", "lightskyblue2", "lightskyblue3", + "lightskyblue4", "lightslateblue", "lightslategray", "lightslategrey", + "lightsteelblue", "lightsteelblue1", "lightsteelblue2", "lightsteelblue3", + "lightsteelblue4", "lightyellow", "lightyellow1", "lightyellow2", + "lightyellow3", "lightyellow4", "limegreen", "linen", "magenta", "magenta1", + "magenta2", "magenta3", "magenta4", "maroon", "maroon1", "maroon2", + "maroon3", "maroon4", "mediumaquamarine", "mediumblue", "mediumorchid", + "mediumorchid1", "mediumorchid2", "mediumorchid3", "mediumorchid4", + "mediumpurple", "mediumpurple1", "mediumpurple2", "mediumpurple3", + "mediumpurple4", "mediumseagreen", "mediumslateblue", "mediumspringgreen", + "mediumturquoise", "mediumvioletred", "midnightblue", "mintcream", + "mistyrose", "mistyrose1", "mistyrose2", "mistyrose3", "mistyrose4", + "moccasin", "navajowhite", "navajowhite1", "navajowhite2", "navajowhite3", + "navajowhite4", "navy", "navyblue", "none", "oldlace", "olivedrab", + "olivedrab1", "olivedrab2", "olivedrab3", "olivedrab4", "orange", "orange1", + "orange2", "orange3", "orange4", "orangered", "orangered1", "orangered2", + "orangered3", "orangered4", "orchid", "orchid1", "orchid2", "orchid3", + "orchid4", "palegoldenrod", "palegreen", "palegreen1", "palegreen2", + "palegreen3", "palegreen4", "paleturquoise", "paleturquoise1", + "paleturquoise2", "paleturquoise3", "paleturquoise4", "palevioletred", + "palevioletred1", "palevioletred2", "palevioletred3", "palevioletred4", + "papayawhip", "peachpuff", "peachpuff1", "peachpuff2", "peachpuff3", + "peachpuff4", "peru", "pink", "pink1", "pink2", "pink3", "pink4", "plum", + "plum1", "plum2", "plum3", "plum4", "powderblue", "purple", "purple1", + "purple2", "purple3", "purple4", "red", "red1", "red2", "red3", "red4", + "rosybrown", "rosybrown1", "rosybrown2", "rosybrown3", "rosybrown4", + "royalblue", "royalblue1", "royalblue2", "royalblue3", "royalblue4", + "saddlebrown", "salmon", "salmon1", "salmon2", "salmon3", "salmon4", + "sandybrown", "seagreen", "seagreen1", "seagreen2", "seagreen3", "seagreen4", + "seashell", "seashell1", "seashell2", "seashell3", "seashell4", "sienna", + "sienna1", "sienna2", "sienna3", "sienna4", "skyblue", "skyblue1", + "skyblue2", "skyblue3", "skyblue4", "slateblue", "slateblue1", "slateblue2", + "slateblue3", "slateblue4", "slategray", "slategray1", "slategray2", + "slategray3", "slategray4", "slategrey", "snow", "snow1", "snow2", "snow3", + "snow4", "springgreen", "springgreen1", "springgreen2", "springgreen3", + "springgreen4", "steelblue", "steelblue1", "steelblue2", "steelblue3", + "steelblue4", "tan", "tan1", "tan2", "tan3", "tan4", "thistle", "thistle1", + "thistle2", "thistle3", "thistle4", "tomato", "tomato1", "tomato2", + "tomato3", "tomato4", "transparent", "turquoise", "turquoise1", "turquoise2", + "turquoise3", "turquoise4", "violet", "violetred", "violetred1", + "violetred2", "violetred3", "violetred4", "wheat", "wheat1", "wheat2", + "wheat3", "wheat4", "white", "whitesmoke", "yellow", "yellow1", "yellow2", + "yellow3", "yellow4", "yellowgreen"]); + # code from the GAP standard library InstallMethod(GV_Pluralize, "for an integer and a string", @@ -135,7 +300,7 @@ end); ############################################################################### # Family + type -# ############################################################################## +############################################################################### BindGlobal("GV_ObjectFamily", NewFamily("GV_ObjectFamily", @@ -171,9 +336,14 @@ BindGlobal("GV_ContextType", NewType(GV_ObjectFamily, IsComponentObjectRep and IsAttributeStoringRep)); -# ############################################################################## +InstallMethod(\=, "for IsGVNode and IsGVNode", +[IsGVNode, IsGVNode], +{n1, n2} -> GraphvizName(n1) = GraphvizName(n2)); + +############################################################################### # Constuctors etc -# ############################################################################## +############################################################################### + DeclareOperation("GV_Node", [IsGVGraph, IsString]); DeclareOperation("GV_Edge", [IsGVGraph, IsGVNode, IsGVNode]); DeclareOperation("GV_Graph", [IsGVGraph, IsString]); @@ -184,13 +354,12 @@ DeclareOperation("GV_Map", []); InstallMethod(GV_Map, "for no args", [], {} -> Objectify(GV_MapType, rec(Data := rec()))); -InstallMethod(GV_Node, -"for a string", +InstallMethod(GV_Node, "for a string", [IsGVGraph, IsString], function(graph, name) local out; if Length(name) = 0 then - return ErrorNoReturn("Node name cannot be empty."); + ErrorNoReturn("the 2nd argument (string/node name) cannot be empty"); fi; out := Objectify(GV_NodeType, rec( @@ -317,6 +486,7 @@ InstallMethod(GraphvizDigraph, "for no args", [], {} -> GraphvizDigraph("")); # ########################################################### # Graphviz Map Functions # ########################################################### + InstallOtherMethod(\[\], "for a graphviz map and an object", [IsGV_Map, IsObject], @@ -354,12 +524,14 @@ InstallMethod(GV_MapNames, "for a graphviz map", InstallMethod(ViewString, "for a graphviz map", [IsGV_Map], m -> String(m!.Data)); -# ########################################################### +InstallMethod(Size, "for a graphviz map", +[IsGV_Map], m -> Length(GV_MapNames(m))); + +############################################################ # Stringify -# ########################################################### -InstallMethod(ViewString, -"for a graphviz node", -[IsGVNode], +############################################################ + +InstallMethod(ViewString, "for a graphviz node", [IsGVNode], n -> StringFormatted("", GraphvizName(n))); InstallMethod(ViewString, "for a graphviz edge", [IsGVEdge], @@ -368,7 +540,8 @@ function(e) head := GraphvizHead(e); tail := GraphvizTail(e); return StringFormatted("", - GraphvizName(head), GraphvizName(tail)); + GraphvizName(head), + GraphvizName(tail)); end); InstallMethod(ViewString, "for a graphviz graph", [IsGVGraph], @@ -377,7 +550,8 @@ function(g) result := ""; edges := Length(GraphvizEdges(g)); - nodes := Length(GV_MapNames(GraphvizNodes(g))); + nodes := + Length(GV_MapNames(GraphvizNodes(g))); if IsGVDigraph(g) then kind := "digraph"; @@ -402,32 +576,42 @@ end); ############################################################ # Getters ############################################################ -InstallMethod(GraphvizName, -"for a graphviz object", -[IsGVObject], -x -> x!.Name); -InstallMethod(GraphvizAttrs, -"for a graphviz object", -[IsGVObject], +InstallMethod(GraphvizName, "for a graphviz object", [IsGVObject], x -> x!.Name); + +InstallMethod(GraphvizAttrs, "for a graphviz object", [IsGVObject], x -> x!.Attrs); -InstallMethod(GraphvizNodes, -"for a graphviz graph", -[IsGVGraph], +# TODO remove, the returned value is mutable, looks like a record but isn't +# one. Mutability is a problem, since if we do GraphvizNodes(gv)[1] := 2; then +# the object is corrupt. +InstallMethod(GraphvizNodes, "for a graphviz graph", [IsGVGraph], x -> x!.Nodes); +InstallMethod(GraphvizNode, "for a graphviz graph and object", +[IsGVGraph, IsObject], {gv, obj} -> gv!.Nodes[String(obj)]); + +# TODO remove for the same reason as GraphvizNodes, unless we can make the list +# immutable or return a copy. +InstallMethod(GraphvizEdges, "for a graphviz graph", +[IsGVGraph], x -> x!.Edges); + InstallMethod(GraphvizEdges, -"for a graphviz graph", -[IsGVGraph], -x -> x!.Edges); +"for a graphviz graph, object, and object", +[IsGVGraph, IsObject, IsObject], +function(gv, head, tail) + head := GraphvizNode(gv, head); + tail := GraphvizNode(gv, tail); + # TODO if head = fail then... + return Filtered(GraphvizEdges(gv), x -> GraphvizHead(x) = head and + GraphvizTail(x) = tail); +end); -InstallMethod(GraphvizSubgraphs, -"for a graphviz graph", -[IsGVGraph], +InstallMethod(GraphvizSubgraphs, "for a graphviz graph", [IsGVGraph], x -> x!.Subgraphs); InstallMethod(GraphvizTail, "for a graphviz edge", [IsGVEdge], x -> x!.Tail); + InstallMethod(GraphvizHead, "for a graphviz edge", [IsGVEdge], x -> x!.Head); InstallMethod(GraphvizGetSubgraph, @@ -447,15 +631,13 @@ function(x) x!.Counter := x!.Counter + 1; end); -InstallMethod(GV_GetCounter, -"for a graphviz graph", -[IsGVGraph], +InstallMethod(GV_GetCounter, "for a graphviz graph", [IsGVGraph], x -> x!.Counter); # Converting strings DeclareOperation("GV_EnsureString", [IsObject]); -# TODO required? +# TODO required? Replace with AsString InstallMethod(GV_EnsureString, "for an object", [IsObject], ViewString); @@ -543,6 +725,7 @@ InstallMethod(GV_GetParent, [IsGVGraph], graph -> graph!.Parent); DeclareOperation("GV_GraphTreeSearch", [IsGVGraph, IsFunction]); + InstallMethod(GV_GraphTreeSearch, "for a graphviz graph and a predicate", [IsGVGraph, IsFunction], @@ -621,9 +804,10 @@ function(g, n) return graph[n]; end); -# ########################################################### +############################################################ # Setters -# ########################################################### +############################################################ + InstallMethod(GraphvizSetName, "for a graphviz object and string", [IsGVGraph, IsString], function(x, name) @@ -648,8 +832,14 @@ end); InstallMethod(GraphvizSetAttr, "for a graphviz object, object and object", [IsGVObject, IsObject, IsObject], function(x, name, value) + local msg; + if not name in GV_KNOWN_ATTRS then - Print(StringFormatted("[WARNING] Unknown attribute {}\n", name)); + msg := Concatenation( + StringFormatted("unknown attribute \"{}\", the", name), + " graphviz object may no longer be valid, it can", + " be removed using GraphvizRemoveAttr"); + Info(InfoWarning, 1, msg); fi; GraphvizAttrs(x)[String(name)] := String(value); return x; @@ -658,22 +848,27 @@ end); InstallMethod(GraphvizSetAttr, "for a graphviz graph, object and object", [IsGVGraph, IsObject, IsObject], function(x, name, value) - local attrs, string; + local attrs, string, msg; - # display warning if not known attribute if not name in GV_KNOWN_ATTRS then - Print(StringFormatted("[WARNING] Unknown attribute {}\n", name)); + msg := Concatenation( + StringFormatted("unknown attribute \"{}\", the", name), + " graphviz object may no longer be valid, it can", + " be removed using GraphvizRemoveAttr"); + Info(InfoWarning, 1, msg); fi; attrs := GraphvizAttrs(x); + name := String(name); + value := String(value); if ' ' in name then - name := StringFormatted("{}", name); + name := StringFormatted("\"{}\"", name); fi; if ' ' in value then - value := StringFormatted("{}", value); + value := StringFormatted("\"{}\"", value); fi; - string := StringFormatted("{}={}", String(name), String(value)); + string := StringFormatted("{}={}", name, value); Add(attrs, string); return x; end); @@ -711,7 +906,7 @@ function(x, node) found := GV_FindGraphWithNode(x, name); if found <> fail then error := "Already node with name {} in graph {}."; - return ErrorNoReturn(StringFormatted(error, name, GraphvizName(found))); + ErrorNoReturn(StringFormatted(error, name, GraphvizName(found))); fi; nodes[name] := node; @@ -766,14 +961,15 @@ function(x, edge) # make sure the nodes exist / are the same as existing ones if hg <> fail and not IsIdenticalObj(head, hg[head_name]) then - error := "Different node in graph {} with name {}."; - return ErrorNoReturn(StringFormatted(error, - GraphvizName(hg), - head_name)); + # TODO improve + ErrorFormatted("Different node in graph {} with name {}", + GraphvizName(hg), + head_name); fi; if tg <> fail and not IsIdenticalObj(tail, tg[tail_name]) then + # TODO improve error := "Different node in graph {} with name {}."; - return ErrorNoReturn(StringFormatted(error, + ErrorNoReturn(StringFormatted(error, GraphvizName(tg), tail_name)); fi; @@ -845,7 +1041,7 @@ function(graph, name) subgraphs := GraphvizSubgraphs(graph); if IsBound(subgraphs[name]) then error := "The graph already contains a subgraph with name {}."; - return ErrorNoReturn(StringFormatted(error, name)); + ErrorNoReturn(StringFormatted(error, name)); fi; if IsGVDigraph(graph) then @@ -853,7 +1049,7 @@ function(graph, name) elif IsGVGraph(graph) then subgraph := GV_Graph(graph, name); else - return ErrorNoReturn("Filter must be a filter for a graph category."); + ErrorNoReturn("Filter must be a filter for a graph category."); fi; subgraphs[name] := subgraph; @@ -882,7 +1078,7 @@ function(graph, name) subgraphs := GraphvizSubgraphs(graph); if IsBound(subgraphs[name]) then error := "The graph already contains a subgraph with name {}."; - return ErrorNoReturn(StringFormatted(error, name)); + ErrorNoReturn(StringFormatted(error, name)); fi; ctx := GV_Context(graph, name); @@ -905,10 +1101,13 @@ InstallMethod(GraphvizRemoveNode, "for a graphviz graph and node", [IsGVGraph, IsGVNode], {g, node} -> GraphvizRemoveNode(g, GraphvizName(node))); +# TODO GraphvizRemoveEdges(gv, n1, n2) + InstallMethod(GraphvizRemoveNode, "for a graphviz graph and a string", [IsGVGraph, IsString], function(g, name) local nodes; + # TODO error if there's no such node nodes := GraphvizNodes(g); Unbind(nodes[name]); @@ -1016,13 +1215,34 @@ graph -> StringFormatted("subgraph {} {{\n", GraphvizName(graph))); InstallMethod(GV_StringifyContextHead, "for a string", [IsGVContext], graph -> StringFormatted("// {} context \n", GraphvizName(graph))); +BindGlobal("GV_StringifyNodeName", +function(node) + local name, old; + + Assert(0, IsGVNode(node)); + name := GraphvizName(node); + if (ForAny("- .+", x -> x in name) + or (IsDigitChar(First(name)) and IsAlphaChar(Last(name)))) + and not StartsWith(name, "\"") then + old := name; + name := StringFormatted("\"{}\"", name); + Info(InfoWarning, + 1, + "invalid node name ", + old, + " using ", + name, + " instead"); + fi; + return name; +end); + # @ Return DOT node statement line. InstallMethod(GV_StringifyNode, "for string and record", [IsGVNode], function(node) - local attrs, name; - - name := GraphvizName(node); + local name, attrs; + name := GV_StringifyNodeName(node); attrs := GraphvizAttrs(node); return StringFormatted("\t{}{}\n", name, GV_StringifyNodeEdgeAttrs(attrs)); end); @@ -1033,8 +1253,8 @@ function(edge, edge_str) local head, tail, attrs; Assert(0, IsGVEdge(edge)); Assert(0, IsString(edge_str)); - head := GraphvizName(GraphvizHead(edge)); - tail := GraphvizName(GraphvizTail(edge)); + head := GV_StringifyNodeName(GraphvizHead(edge)); + tail := GV_StringifyNodeName(GraphvizTail(edge)); attrs := GraphvizAttrs(edge); # handle : syntax @@ -1065,7 +1285,7 @@ function(graph) end); InstallMethod(GV_StringifyNodeEdgeAttrs, -"for a record", +"for a GV_Map", [IsGV_Map], function(attrs) local result, keys, key, val, n, i, tmp; @@ -1084,12 +1304,13 @@ function(attrs) if "label" = key and StartsWith(tmp, "<<") and EndsWith(tmp, ">>") then val := StringFormatted("{}", val); else - if ' ' in key then - key := StringFormatted("\"{}\"", key); - fi; - if ' ' in val then - val := StringFormatted("\"{}\"", val); - fi; + if ' ' in key then + key := StringFormatted("\"{}\"", key); + fi; + if ' ' in val or '>' in val or '^' in val or '#' in val then + # TODO avoid code duplication here, and below + val := StringFormatted("\"{}\"", val); + fi; fi; Append(result, @@ -1109,7 +1330,7 @@ function(attrs) if ' ' in key then key := StringFormatted("\"{}\"", key); fi; - if ' ' in val or '>' in val then + if ' ' in val or '>' in val or '^' in val or '#' in val then # TODO what are the allowed things in the value? val := StringFormatted("\"{}\"", val); fi; @@ -1166,16 +1387,19 @@ function(graph, is_subgraph) elif IsGVGraph(graph) or IsGVDigraph(graph) then Append(result, GV_StringifySubgraphHead(graph)); else - return ErrorNoReturn("Invalid subgraph type."); + ErrorNoReturn("Invalid subgraph type."); fi; elif IsGVDigraph(graph) then + Append(result, "//dot\n"); Append(result, GV_StringifyDigraphHead(graph)); elif IsGVGraph(graph) then + Append(result, "//dot\n"); Append(result, GV_StringifyGraphHead(graph)); elif IsGVContext(graph) then + Append(result, "//dot\n"); Append(result, GV_StringifyContextHead(graph)); else - return ErrorNoReturn("Invalid graph type."); + ErrorNoReturn("Invalid graph type."); fi; Append(result, GV_StringifyGraphAttrs(graph)); @@ -1193,7 +1417,7 @@ function(graph, is_subgraph) Append(result, GV_StringifyEdge(obj, "--")); fi; else - return ErrorNoReturn("Invalid graphviz object type."); + ErrorNoReturn("Invalid graphviz object type."); fi; od; @@ -1212,3 +1436,82 @@ end); InstallMethod(AsString, "for a graphviz graph", [IsGVGraph], graph -> GV_StringifyGraph(graph, false)); + +BindGlobal("GV_IsValidRGBColor", +function(str) + local valid, i; + + valid := "0123456789ABCDEFabcdef"; + + if Length(str) <> 7 or str[1] <> '#' then + return false; + fi; + + for i in [2 .. 7] do + if not str[i] in valid then + return false; + fi; + od; + return true; +end); + +BindGlobal("GV_IsValidColor", +c -> IsString(c) and (GV_IsValidRGBColor(c) or c in GV_ValidColorNames)); + +BindGlobal("GV_ErrorIfNotValidColor", +function(c) + if not GV_IsValidColor(c) then + if IsString(c) then + c := StringFormatted("\"{}\"", c); + fi; + ErrorFormatted("invalid color {} ({}), ", + "valid colors are RGB values or names from ", + "the GraphViz 2.44.1 X11 Color Scheme", + " http://graphviz.org/doc/info/colors.html", + c, + TNAM_OBJ(c)); + fi; +end); + +BindGlobal("GV_ErrorIfNotNodeColoring", +function(gv, colors) + local N; + N := Size(GraphvizNodes(gv)); + if Length(colors) <> N then + ErrorFormatted( + "the number of node colors must be the same as the number", + " of nodes, expected {} but found {}", N, Length(colors)); + fi; + Perform(colors, GV_ErrorIfNotValidColor); +end); + +InstallMethod(GraphvizSetNodeLabels, +"for a graphviz graph and list of colors", +[IsGVGraph, IsList], +function(gv, labels) + local nodes, i; + # TODO error if labels and nodes aren't same size + # TODO GV_ErrorIfNotValidLabel + nodes := GraphvizNodes(gv); + for i in [1 .. Size(nodes)] do + GraphvizSetAttr(nodes[i], "label", labels[i]); + od; + return gv; +end); + +InstallMethod(GraphvizSetNodeColors, +"for a graphviz graph and list of colors", +[IsGVGraph, IsList], +function(gv, colors) + local nodes, i; + + GV_ErrorIfNotNodeColoring(gv, colors); + + nodes := GraphvizNodes(gv); + + for i in [1 .. Size(nodes)] do + GraphvizSetAttr(nodes[i], "color", colors[i]); + GraphvizSetAttr(nodes[i], "style", "filled"); + od; + return gv; +end); diff --git a/gap/splash.gi b/gap/splash.gi index 517e017..4effacb 100644 --- a/gap/splash.gi +++ b/gap/splash.gi @@ -1,22 +1,24 @@ +# TODO file header +# TODO rename file to remove "i" suffix -if not IsBound(Splash) then # This function is written by A. Egri-Nagy +if not IsBound(Splash) then BindGlobal("VizViewers", ["xpdf", "xdg-open", "open", "evince", "okular", "gv"]); BindGlobal("Splash", - function(arg) - local str, opt, path, dir, tdir, file, viewer, inn, filetype, out, + function(arg...) + local str, opt, path, dir, tdir, file, viewer, type, inn, filetype, out, engine; - if not IsString(arg[1]) and not IsGVGraph(arg[1]) then - ErrorNoReturn("the 1st argument must be a string or graphviz graph."); - fi; - - if IsString(arg[1]) then - str := arg[1]; + if IsEmpty(arg) then + ErrorNoReturn("the must be at least 1 argument, found none"); + elif not IsString(arg[1]) and not IsGVGraph(arg[1]) then + ErrorFormatted("the 1st argument must be a string or ", + "graphviz graph, found {}", TNAM_OBJ(arg[1])); elif IsGVGraph(arg[1]) then - str := AsString(arg[1]); + arg[1] := AsString(arg[1]); fi; + str := arg[1]; opt := rec(); if IsBound(arg[2]) and IsRecord(arg[2]) then @@ -25,13 +27,11 @@ if not IsBound(Splash) then # This function is written by A. Egri-Nagy ErrorNoReturn("the 2nd argument must be a record,"); fi; - # path - path := UserHomeExpand("~/"); # default + path := UserHomeExpand("~/"); if IsBound(opt.path) then path := opt.path; fi; - # directory if IsBound(opt.directory) then if not opt.directory in DirectoryContents(path) then Exec(Concatenation("mkdir ", path, opt.directory)); @@ -40,28 +40,27 @@ if not IsBound(Splash) then # This function is written by A. Egri-Nagy elif IsBound(opt.path) then if not "tmp.viz" in DirectoryContents(path) then tdir := Directory(Concatenation(path, "/", "tmp.viz")); - dir := Filename(tdir, ""); + dir := Filename(tdir, ""); fi; else tdir := DirectoryTemporary(); - dir := Filename(tdir, ""); + dir := Filename(tdir, ""); fi; - # file - file := "vizpicture"; # default + # TODO use the graphs name + file := "vizpicture"; if IsBound(opt.filename) then file := opt.filename; fi; - # viewer if IsBound(opt.viewer) then viewer := opt.viewer; if not IsString(viewer) then ErrorNoReturn("the option `viewer` must be a string, not an ", - TNAM_OBJ(viewer), ","); + TNAM_OBJ(viewer)); elif Filename(DirectoriesSystemPrograms(), viewer) = fail then ErrorNoReturn("the viewer \"", viewer, "\" specified in the option ", - "`viewer` is not available,"); + "`viewer` is not available"); fi; else viewer := First(VizViewers, x -> @@ -73,17 +72,29 @@ if not IsBound(Splash) then # This function is written by A. Egri-Nagy fi; fi; - inn := Concatenation(dir, file, ".dot"); + if IsBound(opt.type) and (opt.type = "latex" or opt.type = "dot") then + type := opt.type; + elif Length(str) >= 6 and str{[1 .. 6]} = "%latex" then + type := "latex"; + elif Length(str) >= 5 and str{[1 .. 5]} = "//dot" then + type := "dot"; + else + ErrorNoReturn("the component \"type\" of the 2nd argument ", + " must be \"dot\" or \"latex\","); + fi; + if type = "latex" then + inn := Concatenation(dir, file, ".tex"); + else + inn := Concatenation(dir, file, ".dot"); + fi; - # output type and name - filetype := "pdf"; # default - if IsBound(opt.filetype) and IsString(opt.filetype) then + filetype := "pdf"; + if IsBound(opt.filetype) and IsString(opt.filetype) and type <> "latex" then filetype := opt.filetype; fi; out := Concatenation(dir, file, ".", filetype); - # engine - engine := "dot"; # default + engine := "dot"; if IsBound(opt.engine) then engine := opt.engine; if not engine in ["dot", "neato", "twopi", "circo", @@ -95,11 +106,13 @@ if not IsBound(Splash) then # This function is written by A. Egri-Nagy fi; fi; - # Write and compile the file FileString(inn, str); - # Requires GAP >= 4.11: - # Exec(StringFormatted("{} -T {} {} -o {}", engine, filetype, inn, out)); - Exec(Concatenation(engine, " -T", filetype, " ", inn, " -o ", out)); + if type = "latex" then + Exec(Concatenation("cd ", dir, ";", + "pdflatex ", file, " 2>/dev/null 1>/dev/null")); + else + Exec(Concatenation(engine, " -T", filetype, " ", inn, " -o ", out)); + fi; Exec(Concatenation(viewer, " ", out, " 2>/dev/null 1>/dev/null &")); end); fi; diff --git a/init.g b/init.g index a252105..2d54325 100644 --- a/init.g +++ b/init.g @@ -1,9 +1,11 @@ -# -# graphviz: This package facilitates the creation of graph descriptions in the -# DOT language of the Graphviz graph drawing software from GAP -# -# Reading the declaration part of the package. -# +############################################################################# +## +## init.g +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## ReadPackage("graphviz", "gap/dot.gd"); -ReadPackage("graphviz", "gap/defaults.gd"); \ No newline at end of file diff --git a/makedoc.g b/makedoc.g index b846e95..a04d383 100644 --- a/makedoc.g +++ b/makedoc.g @@ -1,9 +1,15 @@ -# -# graphviz: This package facilitates the creation of graph descriptions in the -# DOT language of the Graphviz graph drawing software from GAP -# -# This file is a script which compiles the package manual. -# +############################################################################# +## +## makedoc.g +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +## This file is a script which compiles the package manual. + if fail = LoadPackage("AutoDoc", "2022.10.20") then Error("AutoDoc version 2022.10.20 or newer is required."); fi; diff --git a/read.g b/read.g index bd72a88..4dbac1a 100644 --- a/read.g +++ b/read.g @@ -1,10 +1,12 @@ -# -# graphviz: This package facilitates the creation of graph descriptions in the -# DOT language of the Graphviz graph drawing software from GAP -# -# Reading the implementation part of the package. -# - +############################################################################# +## +## read.g +## Copyright (C) 2024 Matthew Pancer +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## ReadPackage("graphviz", "gap/dot.gi"); ReadPackage("graphviz", "gap/splash.gi"); ReadPackage("graphviz", "gap/defaults.gi"); diff --git a/tst/dot.tst b/tst/dot.tst index 522e34a..4f0c708 100644 --- a/tst/dot.tst +++ b/tst/dot.tst @@ -1,12 +1,14 @@ ############################################################################# ## -## standard/dot.tst -## Copyright (C) 2022 James D. Mitchell +## dot.tst +## Copyright (C) 2024 Matthew Pancer ## ## Licensing information can be found in the README file of this package. ## ############################################################################# ## + +#@local a, b, color, e, g, label, n, shape gap> START_TEST("graphviz package: dot.tst"); gap> LoadPackage("graphviz", false);; @@ -34,7 +36,7 @@ gap> g := GraphvizGraph();; gap> n := GraphvizAddNode(g, "test");; gap> GraphvizSetAttrs(n, rec(color := "red", label := "lab"));; gap> AsString(g); -"graph {\n\ttest [color=red, label=lab]\n}\n" +"//dot\ngraph {\n\ttest [color=red, label=lab]\n}\n" # Test stringify with edge (digraphs) gap> g := GraphvizDigraph();; @@ -45,7 +47,8 @@ gap> GraphvizSetAttrs(b, rec(color := "red"));; gap> e := GraphvizAddEdge(g, a, b);; gap> GraphvizSetAttrs(e, rec(color := "green"));; gap> AsString(g); -"digraph {\n\ta [color=blue]\n\tb [color=red]\n\ta -> b [color=green]\n}\n" +"//dot\ndigraph {\n\ta [color=blue]\n\tb [color=red]\n\ta -> b [color=green]\ +\n}\n" # Test stringify with edge (graph) gap> g := GraphvizGraph();; @@ -56,24 +59,25 @@ gap> GraphvizSetAttrs(b, rec(color := "red"));; gap> e := GraphvizAddEdge(g, a, b);; gap> GraphvizSetAttrs(e, rec(color := "green"));; gap> AsString(g); -"graph {\n\ta [color=blue]\n\tb [color=red]\n\ta -- b [color=green]\n}\n" +"//dot\ngraph {\n\ta [color=blue]\n\tb [color=red]\n\ta -- b [color=green]\n}\ +\n" # Test stringify empty gap> g := GraphvizGraph();; gap> AsString(g); -"graph {\n}\n" +"//dot\ngraph {\n}\n" # Test unknown attributes (node) gap> g := GraphvizGraph();; gap> n := GraphvizAddNode(g, "n");; gap> GraphvizSetAttr(n, "test", "false"); -[WARNING] Unknown attribute test +#I unknown attribute "test", the graphviz object may no longer be valid, it can be removed using GraphvizRemoveAttr # Test unknown attributes (graph) gap> g := GraphvizGraph();; gap> GraphvizSetAttr(g, "test", "false"); -[WARNING] Unknown attribute test +#I unknown attribute "test", the graphviz object may no longer be valid, it can be removed using GraphvizRemoveAttr # Test strngifying labels with ">>" inside (node attrs) @@ -81,24 +85,24 @@ gap> g := GraphvizGraph();; gap> n := GraphvizAddNode(g, "node");; gap> GraphvizSetAttr(n, "label", ">>hello");; gap> AsString(g); -"graph {\n\tnode [label=\">>hello\"]\n}\n" +"//dot\ngraph {\n\tnode [label=\">>hello\"]\n}\n" gap> g := GraphvizGraph();; gap> n := GraphvizAddNode(g, "node");; gap> GraphvizSetAttr(n, "label", "before>>hello");; gap> AsString(g); -"graph {\n\tnode [label=\"before>>hello\"]\n}\n" +"//dot\ngraph {\n\tnode [label=\"before>>hello\"]\n}\n" # Test strngifying labels with ">>" inside (edge attrs) gap> g := GraphvizGraph();; gap> e := GraphvizAddEdge(g, "a", "b");; gap> GraphvizSetAttr(e, "label", ">>hello");; gap> AsString(g); -"graph {\n\ta\n\tb\n\ta -- b [label=\">>hello\"]\n}\n" +"//dot\ngraph {\n\ta\n\tb\n\ta -- b [label=\">>hello\"]\n}\n" gap> g := GraphvizGraph();; gap> e := GraphvizAddEdge(g, "a", "b");; gap> GraphvizSetAttr(e, "label", "before>>hello");; gap> AsString(g); -"graph {\n\ta\n\tb\n\ta -- b [label=\"before>>hello\"]\n}\n" +"//dot\ngraph {\n\ta\n\tb\n\ta -- b [label=\"before>>hello\"]\n}\n" # -gap> STOP_TEST("Digraphs package: standard/oper.tst", 0); +gap> STOP_TEST("graphviz package: dot.tst", 0); diff --git a/tst/edge.tst b/tst/edge.tst index 8cd7a95..7ed6407 100644 --- a/tst/edge.tst +++ b/tst/edge.tst @@ -1,13 +1,15 @@ ############################################################################# ## -## standard/dot.tst -## Copyright (C) 2022 James D. Mitchell +## dot.tst +## Copyright (C) 2024 Matthew Pancer ## ## Licensing information can be found in the README file of this package. ## ############################################################################# ## -gap> START_TEST("graphviz package: dot.tst"); + +#@local a, a1, a2, ab, b, c, cd, color, d, e, e1, e2, g, g1, label, n +gap> START_TEST("graphviz package: edge.tst"); gap> LoadPackage("graphviz", false);; # Test edge constructor @@ -81,7 +83,7 @@ gap> a2 := GraphvizAddNode(g1, "a");; gap> c := GraphvizAddNode(g1, "c");; gap> e1 := GraphvizAddEdge(g, d, a1);; gap> e2 := GraphvizAddEdge(g, a2, c);; -Error, Different node in graph with name a. +Error, Different node in graph with name a gap> GraphvizEdges(g); [ ] @@ -153,3 +155,4 @@ gap> GraphvizAttrs(n); rec( color := "red" ) # +gap> STOP_TEST("graphviz package: edge.tst", 0); diff --git a/tst/examples/angles.tst b/tst/examples/angles.tst new file mode 100644 index 0000000..fc39502 --- /dev/null +++ b/tst/examples/angles.tst @@ -0,0 +1,145 @@ +############################################################################# +## +## examples/angles.tst +## Copyright (C) 2024 James D. Mitchell +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# https://graphviz.readthedocs.io/en/stable/examples.html +# https://www.graphviz.org/Gallery/gradient/angles.html + +#@local cluster1, cluster2, g, node, pair, pairs +gap> START_TEST("graphviz package: examples/angles.tst"); +gap> LoadPackage("graphviz"); +true +gap> g := GraphvizDigraph("G"); + +gap> GraphvizSetAttr(g, "bgcolor", "blue"); + +gap> cluster1 := GraphvizAddSubgraph(g, "cluster_1"); + +gap> GraphvizSetAttr(cluster1, "fontcolor", "white"); + +gap> GraphvizSetAttr(cluster1, Concatenation("node[shape=circle, style=filled,", +> "fillcolor=\"white:black\", gradientangle=360, label=\"n9:n360\",", +> "fontcolor=black]")); + +gap> GraphvizAddNode(cluster1, "n9"); + +gap> pairs := ListN([8, 7 .. 1], [315, 270 .. 0], {x, y} -> [x, y]); +[ [ 8, 315 ], [ 7, 270 ], [ 6, 225 ], [ 5, 180 ], [ 4, 135 ], [ 3, 90 ], + [ 2, 45 ], [ 1, 0 ] ] +gap> for pair in pairs do +> node := GraphvizAddNode(cluster1, StringFormatted("n{}", pair[1])); +> GraphvizSetAttr(node, "gradientangle", StringFormatted("{}", pair[2])); +> GraphvizSetAttr(node, "label", +> StringFormatted("\"n{}:{}\"", pair[1], pair[2])); +> od; +gap> GraphvizSetAttr(cluster1, +> "label", +> "Linear Angle Variations (white to black gradient)"); + +gap> cluster2 := GraphvizAddSubgraph(g, "cluster_2"); + +gap> GraphvizSetAttr(cluster2, "fontcolor", "white"); + +gap> GraphvizSetAttr(cluster2, Concatenation("node[shape=circle, style=radial,", +> "fillcolor=\"white:black\", gradientangle=360,", +> "label=\"n9:n360\", fontcolor=black]")); + +gap> GraphvizAddNode(cluster2, "n18"); + +gap> pairs := ListN([17, 16 .. 10], [315, 270 .. 0], {x, y} -> [x, y]); +[ [ 17, 315 ], [ 16, 270 ], [ 15, 225 ], [ 14, 180 ], [ 13, 135 ], + [ 12, 90 ], [ 11, 45 ], [ 10, 0 ] ] +gap> for pair in pairs do +> node := GraphvizAddNode(cluster2, StringFormatted("n{}", pair[1])); +> GraphvizSetAttr(node, "gradientangle", StringFormatted("{}", pair[2])); +> GraphvizSetAttr(node, "label", StringFormatted("\"n{}:{}\"", +> pair[1], pair[2])); +> od; +gap> GraphvizSetAttr(cluster2, "label", +> "Radial Angle Variations (white to black gradient)"); + +gap> GraphvizAddEdge(g, "n5", "n14"); + + +#@if CompareVersionNumbers(GAPInfo.Version, "4.12.0") +gap> Print(AsString(g)); +//dot +digraph G { + bgcolor=blue +subgraph cluster_1 { + fontcolor=white node[shape=circle, style=filled,fillcolor="white:black", grad\ +ientangle=360, label="n9:n360",fontcolor=black] label="Linear Angle Variations\ + (white to black gradient)" + n9 + n8 [gradientangle=315, label="n8:315"] + n7 [gradientangle=270, label="n7:270"] + n6 [gradientangle=225, label="n6:225"] + n5 [gradientangle=180, label="n5:180"] + n4 [gradientangle=135, label="n4:135"] + n3 [gradientangle=90, label="n3:90"] + n2 [gradientangle=45, label="n2:45"] + n1 [gradientangle=0, label="n1:0"] +} +subgraph cluster_2 { + fontcolor=white node[shape=circle, style=radial,fillcolor="white:black", grad\ +ientangle=360,label="n9:n360", fontcolor=black] label="Radial Angle Variations\ + (white to black gradient)" + n18 + n17 [gradientangle=315, label="n17:315"] + n16 [gradientangle=270, label="n16:270"] + n15 [gradientangle=225, label="n15:225"] + n14 [gradientangle=180, label="n14:180"] + n13 [gradientangle=135, label="n13:135"] + n12 [gradientangle=90, label="n12:90"] + n11 [gradientangle=45, label="n11:45"] + n10 [gradientangle=0, label="n10:0"] +} + n5 -> n14 +} +#@else +gap> Print(AsString(g)); +//dot +digraph G { + bgcolor=blue +subgraph cluster_1 { + fontcolor=white node[shape=circle, style=filled,fillcolor="white:black", grad\ +ientangle=360, la\ +bel="n9:n360",fontcolor=black] label="Linear Angle Variations (white to black \ +gradient)" + n9 + n8 [gradientangle=315, label="n8:315"] + n7 [gradientangle=270, label="n7:270"] + n6 [gradientangle=225, label="n6:225"] + n5 [gradientangle=180, label="n5:180"] + n4 [gradientangle=135, label="n4:135"] + n3 [gradientangle=90, label="n3:90"] + n2 [gradientangle=45, label="n2:45"] + n1 [gradientangle=0, label="n1:0"] +} +subgraph cluster_2 { + fontcolor=white node[shape=circle, style=radial,fillcolor="white:black", grad\ +ientangle=360,lab\ +el="n9:n360", fontcolor=black] label="Radial Angle Variations (white to black \ +gradient)" + n18 + n17 [gradientangle=315, label="n17:315"] + n16 [gradientangle=270, label="n16:270"] + n15 [gradientangle=225, label="n15:225"] + n14 [gradientangle=180, label="n14:180"] + n13 [gradientangle=135, label="n13:135"] + n12 [gradientangle=90, label="n12:90"] + n11 [gradientangle=45, label="n11:45"] + n10 [gradientangle=0, label="n10:0"] +} + n5 -> n14 +} +#@fi + +# +gap> STOP_TEST("graphviz package: angles.tst"); diff --git a/tst/examples/btree.tst b/tst/examples/btree.tst new file mode 100644 index 0000000..555996e --- /dev/null +++ b/tst/examples/btree.tst @@ -0,0 +1,73 @@ +############################################################################# +## +## btree.tst +## Copyright (C) 2024 James Mitchell +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# https://graphviz.readthedocs.io/en/stable/examples.html +# https://graphviz.org/Gallery/directed/unix.html + +#@local s +gap> START_TEST("graphviz package: examples/btree.tst"); +gap> LoadPackage("graphviz"); +true + +# +gap> s := GraphvizDigraph("g"); + +gap> GraphvizSetAttr(s, "node [shape=record, height=.1]"); + +gap> GraphvizSetAttr(GraphvizAddNode(s, "node0"), "label", " | G|"); + +gap> GraphvizSetAttr(GraphvizAddNode(s, "node1"), "label", " | E|"); + +gap> GraphvizSetAttr(GraphvizAddNode(s, "node2"), "label", " | B|"); + +gap> GraphvizSetAttr(GraphvizAddNode(s, "node3"), "label", " | F|"); + +gap> GraphvizSetAttr(GraphvizAddNode(s, "node4"), "label", " | R|"); + +gap> GraphvizSetAttr(GraphvizAddNode(s, "node5"), "label", " | H|"); + +gap> GraphvizSetAttr(GraphvizAddNode(s, "node6"), "label", " | Y|"); + +gap> GraphvizSetAttr(GraphvizAddNode(s, "node7"), "label", " | A|"); + +gap> GraphvizSetAttr(GraphvizAddNode(s, "node8"), "label", " | C|"); + +gap> GraphvizAddEdge(s, "node0:f2", "node4:f1"); + +gap> GraphvizAddEdge(s, "node0:f0", "node1:f1"); + +gap> GraphvizAddEdge(s, "node1:f0", "node2:f1"); + +gap> GraphvizAddEdge(s, "node1:f2", "node3:f1"); + +gap> GraphvizAddEdge(s, "node2:f2", "node8:f1"); + +gap> GraphvizAddEdge(s, "node2:f0", "node7:f1"); + +gap> GraphvizAddEdge(s, "node4:f2", "node6:f1"); + +gap> GraphvizAddEdge(s, "node4:f0", "node5:f1"); + +gap> AsString(s) = +> "//dot\ndigraph g {\n\tnode [shape=record, height=.1] \n\tnode0 [label=\" \ +> | G|\"]\n\tnode1 [label=\" | E|\"]\n\tnode2 [label=\" \ +> | B|\"]\n\tnode3 [label=\" | F|\"]\n\tnode4 [label=\" \ +> | R|\"]\n\tnode5 [label=\" | H|\"]\n\tnode6 [label=\" \ +> | Y|\"]\n\tnode7 [label=\" | A|\"]\n\tnode8 [label=\" \ +> | C|\"]\n\tnode0:f2\n\tnode4:f1\n\tnode0:f2 -> node4:f1\n\tnode0:f0\n\ +> \tnode1:f1\n\tnode0:f0 -> node1:f1\n\tnode1:f0\n\tnode2:f1\n\tnode1:f0 -> node\ +> 2:f1\n\tnode1:f2\n\tnode3:f1\n\tnode1:f2 -> node3:f1\n\tnode2:f2\n\tnode8:f1\n\ +> \tnode2:f2 -> node8:f1\n\tnode2:f0\n\tnode7:f1\n\tnode2:f0 -> node7:f1\n\tnode\ +> 4:f2\n\tnode6:f1\n\tnode4:f2 -> node6:f1\n\tnode4:f0\n\tnode5:f1\n\tnode4:f0 -\ +> > node5:f1\n}\n"; +true + +# +gap> STOP_TEST("graphviz package: btree.tst"); diff --git a/tst/examples/cluster.tst b/tst/examples/cluster.tst new file mode 100644 index 0000000..2786feb --- /dev/null +++ b/tst/examples/cluster.tst @@ -0,0 +1,87 @@ +############################################################################# +## +## cluster.tst +## Copyright (C) 2024 James Mitchell +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# https://graphviz.readthedocs.io/en/stable/examples.html + +#@local cluster0, cluster1, graph +gap> START_TEST("graphviz package: examples/cluster.tst"); +gap> LoadPackage("graphviz"); +true +gap> graph := GraphvizDigraph("G"); + + +# +gap> cluster0 := GraphvizAddSubgraph(graph, "cluster_0"); + +gap> GraphvizSetAttr(cluster0, "color=\"lightgrey\""); + +gap> GraphvizSetAttr(cluster0, "style=\"filled\""); + +gap> GraphvizSetAttr(cluster0, "node [color=\"white\", style=\"filled\"]"); + +gap> GraphvizAddEdge(cluster0, "a0", "a1"); + +gap> GraphvizAddEdge(cluster0, "a1", "a2"); + +gap> GraphvizAddEdge(cluster0, "a2", "a3"); + +gap> GraphvizSetAttr(cluster0, "label=\"process #1\""); + + +# +gap> cluster1 := GraphvizAddSubgraph(graph, "cluster_1"); + +gap> GraphvizSetAttr(cluster1, "color=\"blue\""); + +gap> GraphvizSetAttr(cluster1, "node [style=\"filled\"]"); + +gap> GraphvizAddEdge(cluster1, "b0", "b1"); + +gap> GraphvizAddEdge(cluster1, "b1", "b2"); + +gap> GraphvizAddEdge(cluster1, "b2", "b3"); + +gap> GraphvizSetAttr(cluster1, "label=\"process #2\""); + + +# +gap> GraphvizAddEdge(graph, "start", "a0"); + +gap> GraphvizAddEdge(graph, "start", "b0"); + +gap> GraphvizAddEdge(graph, "a1", "b3"); + +gap> GraphvizAddEdge(graph, "b2", "a3"); + +gap> GraphvizAddEdge(graph, "a3", "a0"); + +gap> GraphvizAddEdge(graph, "a3", "end"); + +gap> GraphvizAddEdge(graph, "b3", "end"); + + +# +gap> GraphvizSetAttr(graph["start"], "shape", "Mdiamond"); + +gap> GraphvizSetAttr(graph["end"], "shape", "Msquare"); + + +# +gap> AsString(graph); +"//dot\ndigraph G {\nsubgraph cluster_0 {\n\tcolor=\"lightgrey\" style=\"fille\ +d\" node [color=\"white\", style=\"filled\"] label=\"process #1\" \n\ta0\n\ta1\ +\n\ta0 -> a1\n\ta2\n\ta1 -> a2\n\ta3\n\ta2 -> a3\n}\nsubgraph cluster_1 {\n\tc\ +olor=\"blue\" node [style=\"filled\"] label=\"process #2\" \n\tb0\n\tb1\n\tb0 \ +-> b1\n\tb2\n\tb1 -> b2\n\tb3\n\tb2 -> b3\n}\n\tstart [shape=Mdiamond]\n\tstar\ +t -> a0\n\tstart -> b0\n\ta1 -> b3\n\tb2 -> a3\n\ta3 -> a0\n\tend [shape=Msqua\ +re]\n\ta3 -> end\n\tb3 -> end\n}\n" + +# +gap> STOP_TEST("graphviz package: cluster.tst"); diff --git a/tst/examples/cluster_edge.tst b/tst/examples/cluster_edge.tst new file mode 100644 index 0000000..3a017fe --- /dev/null +++ b/tst/examples/cluster_edge.tst @@ -0,0 +1,63 @@ +############################################################################# +## +## cluster_edge.tst +## Copyright (C) 2024 James D. Mitchell +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# https://graphviz.readthedocs.io/en/stable/examples.html +# https://www.graphviz.org/pdf/dotguide.pdf, Figure 20 + +#@local cluster0, cluster1, e, g +gap> START_TEST("graphviz package: examples/cluster_edge.tst"); +gap> LoadPackage("graphviz"); +true + +# +gap> g := GraphvizDigraph("G"); + +gap> GraphvizSetAttr(g, "compound=true"); + +gap> cluster0 := GraphvizAddSubgraph(g, "cluster0"); + +gap> GraphvizAddEdge(cluster0, "a", "b"); + +gap> GraphvizAddEdge(cluster0, "a", "c"); + +gap> GraphvizAddEdge(cluster0, "b", "d"); + +gap> GraphvizAddEdge(cluster0, "c", "d"); + +gap> cluster1 := GraphvizAddSubgraph(g, "cluster1"); + +gap> GraphvizAddEdge(cluster1, "e", "g"); + +gap> GraphvizAddEdge(cluster1, "e", "f"); + +gap> GraphvizSetAttr(GraphvizAddEdge(g, "b", "f"), "lhead", "cluster1"); + +gap> GraphvizAddEdge(g, "d", "e"); + +gap> e := GraphvizAddEdge(g, "c", "g"); + +gap> GraphvizSetAttr(e, "ltail", "cluster0"); + +gap> GraphvizSetAttr(e, "lhead", "cluster1"); + +gap> e := GraphvizAddEdge(g, "c", "e"); + +gap> GraphvizSetAttr(e, "ltail", "cluster0"); + +gap> GraphvizAddEdge(g, "d", "h"); + +gap> AsString(g); +"//dot\ndigraph G {\n\tcompound=true \nsubgraph cluster0 {\n\ta\n\tb\n\ta -> b\ +\n\tc\n\ta -> c\n\td\n\tb -> d\n\tc -> d\n}\nsubgraph cluster1 {\n\te\n\tg\n\t\ +e -> g\n\tf\n\te -> f\n}\n\tb -> f [lhead=cluster1]\n\td -> e\n\tc -> g [lhead\ +=cluster1, ltail=cluster0]\n\tc -> e [ltail=cluster0]\n\th\n\td -> h\n}\n" + +# +gap> STOP_TEST("graphviz package: cluster_edge.tst"); diff --git a/tst/examples/colors.tst b/tst/examples/colors.tst new file mode 100644 index 0000000..d1f4d8c --- /dev/null +++ b/tst/examples/colors.tst @@ -0,0 +1,68 @@ +############################################################################# +## +## colors.tst +## Copyright (C) 2024 James Mitchell +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# https://graphviz.readthedocs.io/en/stable/examples.html +# https://graphviz.org/docs/attr-types/color + + +#@local g, node +gap> START_TEST("graphviz package: examples/colors.tst"); +gap> LoadPackage("graphviz"); +true + +# +gap> g := GraphvizGraph(); + + +# +gap> node := GraphvizAddNode(g, "RGB: #40e0d0"); + +gap> GraphvizSetAttr(node, "style", "filled"); + +gap> GraphvizSetAttr(node, "fillcolor", "\"#40e0d0\""); + + +# +gap> node := GraphvizAddNode(g, "RGBA: #ff000042"); + +gap> GraphvizSetAttr(node, "style", "filled"); + +gap> GraphvizSetAttr(node, "fillcolor", "\"#ff000042\""); + + +# +gap> node := GraphvizAddNode(g, "HSV: 0.051 0.718 0.627"); + +gap> GraphvizSetAttr(node, "style", "filled"); + +gap> GraphvizSetAttr(node, "fillcolor", "0.051 0.718 0.627"); + + +# +gap> node := GraphvizAddNode(g, "name: deeppink"); + +gap> GraphvizSetAttr(node, "style", "filled"); + +gap> GraphvizSetAttr(node, "fillcolor", "deeppink"); + + +# +gap> AsString(g); +#I invalid node name RGB: #40e0d0 using "RGB: #40e0d0" instead +#I invalid node name RGBA: #ff000042 using "RGBA: #ff000042" instead +#I invalid node name HSV: 0.051 0.718 0.627 using "HSV: 0.051 0.718 0.627" instead +#I invalid node name name: deeppink using "name: deeppink" instead +"//dot\ngraph {\n\t\"RGB: #40e0d0\" [fillcolor=\"\"#40e0d0\"\", style=filled]\ +\n\t\"RGBA: #ff000042\" [fillcolor=\"\"#ff000042\"\", style=filled]\n\t\"HSV: \ +0.051 0.718 0.627\" [fillcolor=\"0.051 0.718 0.627\", style=filled]\n\t\"name:\ + deeppink\" [fillcolor=deeppink, style=filled]\n}\n" + +# +gap> STOP_TEST("graphviz package: colors.tst"); diff --git a/tst/examples/er.tst b/tst/examples/er.tst new file mode 100644 index 0000000..a1dcbf3 --- /dev/null +++ b/tst/examples/er.tst @@ -0,0 +1,134 @@ +############################################################################# +## +## er.tst +## Copyright (C) 2024 James Mitchell +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# https://graphviz.readthedocs.io/en/stable/examples.html +# https://graphviz.org/Gallery/undirected/ER.html + +#@local context1, context2, e, label, len, start +gap> START_TEST("graphviz package: examples/er.tst"); +gap> LoadPackage("graphviz"); +true + +# +gap> e := GraphvizGraph("ER"); + +gap> GraphvizSetAttr(e, "engine=\"neato\""); + + +# +gap> start := GraphvizAddContext(e, "context_start"); + +gap> GraphvizSetAttr(start, "node[shape=\"box\"]"); + +gap> GraphvizAddNode(start, "course"); + +gap> GraphvizAddNode(start, "institute"); + +gap> GraphvizAddNode(start, "student"); + + +# +gap> context1 := GraphvizAddContext(e, "context1"); + +gap> GraphvizSetAttr(context1, "node [shape=\"ellipse\"]"); + +gap> GraphvizSetAttr(GraphvizAddNode(context1, "name0"), "label", "name"); + +gap> GraphvizSetAttr(GraphvizAddNode(context1, "name1"), "label", "name"); + +gap> GraphvizSetAttr(GraphvizAddNode(context1, "name2"), "label", "name"); + +gap> GraphvizAddNode(context1, "code"); + +gap> GraphvizAddNode(context1, "grade"); + +gap> GraphvizAddNode(context1, "number"); + + +# +gap> context2 := GraphvizAddContext(e, "context2"); + +gap> GraphvizSetAttr(context2, +> "node [shape=\"diamond\", style=\"filled\", color=\"lightgrey\"]"); + +gap> GraphvizAddNode(context2, "C-I"); + +gap> GraphvizAddNode(context2, "S-C"); + +gap> GraphvizAddNode(context2, "S-I"); + + +# +gap> GraphvizAddEdge(e, "name0", "course"); + +gap> GraphvizAddEdge(e, "code", "course"); + +gap> GraphvizSetAttrs(GraphvizAddEdge(e, "C-I", "course"), +> rec(label := "n", len := "1.00")); + +gap> GraphvizSetAttrs(GraphvizAddEdge(e, "institute", "C-I"), +> rec(label := "1", len := "1.00")); + +gap> GraphvizAddEdge(e, "name1", "institute"); + +gap> GraphvizSetAttrs(GraphvizAddEdge(e, "S-I", "institute"), +> rec(label := "1", len := "1.00")); + +gap> GraphvizSetAttrs(GraphvizAddEdge(e, "student", "S-I"), +> rec(label := "n", len := "1.00")); + +gap> GraphvizAddEdge(e, "grade", "student"); + +gap> GraphvizAddEdge(e, "name2", "student"); + +gap> GraphvizAddEdge(e, "number", "student"); + +gap> GraphvizSetAttrs(GraphvizAddEdge(e, "S-C", "student"), +> rec(label := "m", len := "1.00")); + +gap> GraphvizSetAttrs(GraphvizAddEdge(e, "course", "S-C"), +> rec(label := "n", len := "1.00")); + + +# +gap> GraphvizSetAttr(e, "label=\"Entity Relation Diagram\ndrawn by NEATO\""); + +gap> GraphvizSetAttr(e, "fontsize=\"20\""); + + +# +gap> AsString(e); +#I invalid node name C-I using "C-I" instead +#I invalid node name S-C using "S-C" instead +#I invalid node name S-I using "S-I" instead +#I invalid node name C-I using "C-I" instead +#I invalid node name C-I using "C-I" instead +#I invalid node name S-I using "S-I" instead +#I invalid node name S-I using "S-I" instead +#I invalid node name S-C using "S-C" instead +#I invalid node name S-C using "S-C" instead +"//dot\ngraph ER {\n\tengine=\"neato\" label=\"Entity Relation Diagram\ndrawn \ +by NEATO\" fontsize=\"20\" \n// context_start context \n\tnode[shape=\"box\"] \ +\n\tcourse\n\tinstitute\n\tstudent\n\tengine=\"neato\" label=\"Entity Relation\ + Diagram\ndrawn by NEATO\" fontsize=\"20\" \n\n// context1 context \n\tnode [s\ +hape=\"ellipse\"] \n\tname0 [label=name]\n\tname1 [label=name]\n\tname2 [label\ +=name]\n\tcode\n\tgrade\n\tnumber\n\tengine=\"neato\" label=\"Entity Relation \ +Diagram\ndrawn by NEATO\" fontsize=\"20\" \n\n// context2 context \n\tnode [sh\ +ape=\"diamond\", style=\"filled\", color=\"lightgrey\"] \n\t\"C-I\"\n\t\"S-C\"\ +\n\t\"S-I\"\n\tengine=\"neato\" label=\"Entity Relation Diagram\ndrawn by NEAT\ +O\" fontsize=\"20\" \n\n\tname0 -- course\n\tcode -- course\n\t\"C-I\" -- cour\ +se [label=n, len=1.00]\n\tinstitute -- \"C-I\" [label=1, len=1.00]\n\tname1 --\ + institute\n\t\"S-I\" -- institute [label=1, len=1.00]\n\tstudent -- \"S-I\" [\ +label=n, len=1.00]\n\tgrade -- student\n\tname2 -- student\n\tnumber -- studen\ +t\n\t\"S-C\" -- student [label=m, len=1.00]\n\tcourse -- \"S-C\" [label=n, len\ +=1.00]\n}\n" + +# +gap> STOP_TEST("graphviz package: er.tst"); diff --git a/tst/examples/fsm.tst b/tst/examples/fsm.tst new file mode 100644 index 0000000..ad70da3 --- /dev/null +++ b/tst/examples/fsm.tst @@ -0,0 +1,89 @@ +############################################################################# +## +## fsm.tst +## Copyright (C) 2024 James Mitchell +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# https://graphviz.readthedocs.io/en/stable/examples.html +# https://graphviz.org/Gallery/directed/fsm.html + +#@local f, nodes, terminals +gap> START_TEST("graphviz package: examples/fsm.tst"); +gap> LoadPackage("graphviz"); +true + +# +gap> f := GraphvizDigraph("finite_state_machine"); + +gap> GraphvizSetAttr(f, "rankdir=LR"); + +gap> GraphvizSetAttr(f, "size=\"8,5\""); + + +# +gap> terminals := GraphvizAddContext(f, "terminals"); + +gap> GraphvizSetAttr(terminals, "node [shape=doublecircle]"); + +gap> GraphvizAddNode(terminals, "LR_0"); + +gap> GraphvizAddNode(terminals, "LR_3"); + +gap> GraphvizAddNode(terminals, "LR_4"); + +gap> GraphvizAddNode(terminals, "LR_8"); + + +# +gap> nodes := GraphvizAddContext(f, "nodes"); + +gap> GraphvizSetAttr(nodes, "node [shape=circle]"); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_0", "LR_2"), "label", "\"SS(B)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_0", "LR_1"), "label", "\"SS(S)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_1", "LR_3"), "label", "\"S($end)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_2", "LR_6"), "label", "\"SS(b)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_2", "LR_5"), "label", "\"SS(a)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_2", "LR_4"), "label", "\"S(A)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_5", "LR_7"), "label", "\"S(b)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_5", "LR_5"), "label", "\"S(a)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_6", "LR_6"), "label", "\"S(b)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_6", "LR_5"), "label", "\"S(a)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_7", "LR_8"), "label", "\"S(b)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_7", "LR_5"), "label", "\"S(a)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_8", "LR_6"), "label", "\"S(b)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_8", "LR_5"), "label", "\"S(a)\""); + + +# +gap> AsString(f); +"//dot\ndigraph finite_state_machine {\n\trankdir=LR size=\"8,5\" \n// termina\ +ls context \n\tnode [shape=doublecircle] \n\tLR_0\n\tLR_3\n\tLR_4\n\tLR_8\n\tr\ +ankdir=LR size=\"8,5\" \n\n// nodes context \n\tnode [shape=circle] \n\tLR_2\n\ +\tLR_0 -> LR_2 [label=\"SS(B)\"]\n\tLR_1\n\tLR_0 -> LR_1 [label=\"SS(S)\"]\n\t\ +LR_1 -> LR_3 [label=\"S($end)\"]\n\tLR_6\n\tLR_2 -> LR_6 [label=\"SS(b)\"]\n\t\ +LR_5\n\tLR_2 -> LR_5 [label=\"SS(a)\"]\n\tLR_2 -> LR_4 [label=\"S(A)\"]\n\tLR_\ +7\n\tLR_5 -> LR_7 [label=\"S(b)\"]\n\tLR_5 -> LR_5 [label=\"S(a)\"]\n\tLR_6 ->\ + LR_6 [label=\"S(b)\"]\n\tLR_6 -> LR_5 [label=\"S(a)\"]\n\tLR_7 -> LR_8 [label\ +=\"S(b)\"]\n\tLR_7 -> LR_5 [label=\"S(a)\"]\n\tLR_8 -> LR_6 [label=\"S(b)\"]\n\ +\tLR_8 -> LR_5 [label=\"S(a)\"]\n\trankdir=LR size=\"8,5\" \n\n}\n" + +# +gap> STOP_TEST("graphviz package: fsm.tst"); diff --git a/tst/examples/g_c_n.tst b/tst/examples/g_c_n.tst new file mode 100644 index 0000000..f38f5b7 --- /dev/null +++ b/tst/examples/g_c_n.tst @@ -0,0 +1,66 @@ +############################################################################# +## +## g_c_n.tst +## Copyright (C) 2024 James Mitchell +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# https://graphviz.readthedocs.io/en/stable/examples.html +# https://www.graphviz.org/Gallery/gradient/g_c_n.html + +#@local cluster1, g +gap> START_TEST("graphviz package: examples/g_c_n.tst"); +gap> LoadPackage("graphviz"); +true + +# +gap> g := GraphvizGraph("G"); + +gap> GraphvizSetAttr(g, +> "bgcolor=\"purple:pink\" label=\"agraph\" fontcolor=\"white\""); + +gap> cluster1 := GraphvizAddSubgraph(g, "cluster1"); + +gap> GraphvizSetAttr(cluster1, Concatenation( +> "fillcolor=\"blue:cyan\" label=\"acluster\" fontcolor=\"white\"", +> "style=\"filled\" gradientangle=270\n")); + +gap> GraphvizSetAttr(cluster1, Concatenation( +> "node [shape=box, fillcolor=\"red:yellow\",", +> " style=\"filled\", gradientangle=90]")); + +gap> GraphvizAddNode(cluster1, "anode"); + + +#@if CompareVersionNumbers(GAPInfo.Version, "4.12.0") +gap> Print(AsString(g)); +//dot +graph G { + bgcolor="purple:pink" label="agraph" fontcolor="white" +subgraph cluster1 { + fillcolor="blue:cyan" label="acluster" fontcolor="white"style="filled" gradie\ +ntangle=270 + node [shape=box, fillcolor="red:yellow", style="filled", gradientangle=90] + anode +} +} +#@else +gap> Print(AsString(g)); +//dot +graph G { + bgcolor="purple:pink" label="agraph" fontcolor="white" +subgraph cluster1 { + fillcolor="blue:cyan" label="acluster" fontcolor="white"style="filled" gradie\ +n\ +tangle=270 + node [shape=box, fillcolor="red:yellow", style="filled", gradientangle=90] + anode +} +} +#@fi + +# +gap> STOP_TEST("graphviz package: g_c_n.tst"); diff --git a/tst/examples/hello.tst b/tst/examples/hello.tst new file mode 100644 index 0000000..d9af8ae --- /dev/null +++ b/tst/examples/hello.tst @@ -0,0 +1,27 @@ +############################################################################# +## +## hello.tst +## Copyright (C) 2024 James Mitchell +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# https://graphviz.readthedocs.io/en/stable/examples.html + +#@local graph +gap> START_TEST("graphviz package: examples/hello.tst"); +gap> LoadPackage("graphviz"); +true + +# +gap> graph := GraphvizDigraph("G"); + +gap> GraphvizAddEdge(graph, "hello", "world"); + +gap> AsString(graph); +"//dot\ndigraph G {\n\thello\n\tworld\n\thello -> world\n}\n" + +# +gap> STOP_TEST("graphviz package: hello.tst"); diff --git a/tst/examples/process.tst b/tst/examples/process.tst new file mode 100644 index 0000000..9b922b8 --- /dev/null +++ b/tst/examples/process.tst @@ -0,0 +1,58 @@ +############################################################################# +## +## process.tst +## Copyright (C) 2024 James Mitchell +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# https://graphviz.readthedocs.io/en/stable/examples.html + + +#@local graph +gap> START_TEST("graphviz package: examples/process.tst"); +gap> LoadPackage("graphviz"); +true + +# +gap> graph := GraphvizGraph("G"); + +gap> GraphvizSetAttr(graph, "engine=\"sfdp\""); + + +# +gap> GraphvizAddEdge(graph, "run", "intr"); + +gap> GraphvizAddEdge(graph, "intr", "runbl"); + +gap> GraphvizAddEdge(graph, "runbl", "run"); + +gap> GraphvizAddEdge(graph, "run", "kernel"); + +gap> GraphvizAddEdge(graph, "kernel", "zombie"); + +gap> GraphvizAddEdge(graph, "kernel", "sleep"); + +gap> GraphvizAddEdge(graph, "kernel", "runmem"); + +gap> GraphvizAddEdge(graph, "sleep", "swap"); + +gap> GraphvizAddEdge(graph, "swap", "runswap"); + +gap> GraphvizAddEdge(graph, "runswap", "new"); + +gap> GraphvizAddEdge(graph, "runswap", "runmem"); + +gap> GraphvizAddEdge(graph, "new", "runmem"); + +gap> AsString(graph); +"//dot\ngraph G {\n\tengine=\"sfdp\" \n\trun\n\tintr\n\trun -- intr\n\trunbl\n\ +\tintr -- runbl\n\trunbl -- run\n\tkernel\n\trun -- kernel\n\tzombie\n\tkernel\ + -- zombie\n\tsleep\n\tkernel -- sleep\n\trunmem\n\tkernel -- runmem\n\tswap\n\ +\tsleep -- swap\n\trunswap\n\tswap -- runswap\n\tnew\n\trunswap -- new\n\truns\ +wap -- runmem\n\tnew -- runmem\n}\n" + +# +gap> STOP_TEST("graphviz package: process.tst"); diff --git a/tst/examples/rank_same.tst b/tst/examples/rank_same.tst new file mode 100644 index 0000000..685dee2 --- /dev/null +++ b/tst/examples/rank_same.tst @@ -0,0 +1,67 @@ +############################################################################# +## +## rank_same.tst +## Copyright (C) 2024 James Mitchell +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# https://graphviz.readthedocs.io/en/stable/examples.html +# https://stackoverflow.com/questions/25734244 + + +#@local g, s1, s2 +gap> START_TEST("graphviz package: examples/rank_same.tst"); +gap> LoadPackage("graphviz"); +true + +# +gap> g := GraphvizDigraph(); + + +# +gap> s1 := GraphvizAddSubgraph(g); + +gap> GraphvizSetAttr(s1, "rank=same"); + +gap> GraphvizAddNode(s1, "A"); + +gap> GraphvizAddNode(s1, "X"); + + +# +gap> GraphvizAddNode(g, "C"); + + +# +gap> s2 := GraphvizAddSubgraph(g); + +gap> GraphvizSetAttr(s2, "rank=same"); + +gap> GraphvizAddNode(s2, "B"); + +gap> GraphvizAddNode(s2, "D"); + +gap> GraphvizAddNode(s2, "Y"); + + +# +gap> GraphvizAddEdge(g, "A", "B"); + +gap> GraphvizAddEdge(g, "A", "C"); + +gap> GraphvizAddEdge(g, "C", "D"); + +gap> GraphvizAddEdge(g, "X", "Y"); + + +# +gap> AsString(g); +"//dot\ndigraph {\nsubgraph no_name_1 {\n\trank=same \n\tA\n\tX\n}\n\tC\nsubg\ +raph no_name_3 {\n\trank=same \n\tB\n\tD\n\tY\n}\n\tA -> B\n\tA -> C\n\tC -> D\ +\n\tX -> Y\n}\n" + +# +gap> STOP_TEST("graphviz package: rank_same.tst"); diff --git a/tst/examples/structs.tst b/tst/examples/structs.tst new file mode 100644 index 0000000..2068f9f --- /dev/null +++ b/tst/examples/structs.tst @@ -0,0 +1,80 @@ +############################################################################# +## +## structs.tst +## Copyright (C) 2024 James Mitchell +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# https://graphviz.readthedocs.io/en/stable/examples.html +# https://graphviz.org/Gallery/directed/unix.html + +#@local s +gap> START_TEST("graphviz package: examples/structs.tst"); +gap> LoadPackage("graphviz"); +true +gap> s := GraphvizDigraph("structs"); + +gap> GraphvizSetAttr(s, "node [shape=\"plaintext\"]"); + + +# +gap> GraphvizSetAttr(GraphvizAddNode(s, "struct1"), "label", +> """< +> +> +> +> +>
leftmiddleright
>"""); + +gap> GraphvizSetAttr(GraphvizAddNode(s, "struct2"), "label", +> """< +> +> +> +> +>
onetwo
>"""); + +gap> GraphvizSetAttr(GraphvizAddNode(s, "struct3"), "label", +> """< +> +> +> +> +> +> +> +> +> +> +> +> +> +> +>
hello
world
bgh
cde
f
>"""); + + +# +gap> GraphvizAddEdge(s, "struct1:f1", "struct2:f0"); + +gap> GraphvizAddEdge(s, "struct1:f2", "struct3:here"); + + +# +gap> AsString(s); +"//dot\ndigraph structs {\n\tnode [shape=\"plaintext\"] \n\tstruct1 [label=<\n\nleft\nmiddleright\n\n>]\n\tstru\ +ct2 [label=<\n\none\n\n\n
two
>]\n\tstruct3 [label=<\n\n<\ +TD ROWSPAN=\"3\">hello
world\nb\ng\nh\n\n\nc\nd\ne\n\n\nf\n\n>]\n\tstruct1:f1\n\tstruct2:f0\n\tstruct1:f1 -> struct2:f0\n\tstruct1:f2\n\ts\ +truct3:here\n\tstruct1:f2 -> struct3:here\n}\n" + +# +gap> STOP_TEST("graphviz package: structs.tst"); diff --git a/tst/examples/traffic_lights.tst b/tst/examples/traffic_lights.tst new file mode 100644 index 0000000..295ac0f --- /dev/null +++ b/tst/examples/traffic_lights.tst @@ -0,0 +1,94 @@ +############################################################################# +## +## traffic_lights.tst +## Copyright (C) 2024 James Mitchell +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# https://graphviz.readthedocs.io/en/stable/examples.html +# https://graphviz.org/Gallery/directed/unix.html + +#@local ctx1, ctx2, i, j, pair, t +gap> START_TEST("graphviz package: examples/traffic_lights.tst"); +gap> LoadPackage("graphviz"); +true +gap> t := GraphvizDigraph("TrafficLights"); + +gap> GraphvizSetAttr(t, "engine=neato"); + + +# +gap> ctx1 := GraphvizAddSubgraph(t, "ctx1"); + +gap> GraphvizSetAttr(ctx1, "node [shape=\"box\"]"); + +gap> for i in [2, 1] do +> GraphvizAddNode(ctx1, StringFormatted("gy{}", i)); +> GraphvizAddNode(ctx1, StringFormatted("yr{}", i)); +> GraphvizAddNode(ctx1, StringFormatted("rg{}", i)); +> od; + +# +gap> ctx2 := GraphvizAddSubgraph(t, "ctx2"); + +gap> GraphvizSetAttr(ctx2, "node [shape=\"circle\", fixedsize=true, width=0.9]"); + +gap> for i in [2, 1] do +> GraphvizAddNode(ctx2, StringFormatted("green{}", i)); +> GraphvizAddNode(ctx2, StringFormatted("yellow{}", i)); +> GraphvizAddNode(ctx2, StringFormatted("red{}", i)); +> GraphvizAddNode(ctx2, StringFormatted("safe{}", i)); +> od; + +# +gap> pair := fail; +fail +gap> for pair in [[2, 1], [1, 2]] do +> i := pair[1]; +> j := pair[2]; +> GraphvizAddEdge( +> t, StringFormatted("gy{}", i), StringFormatted("yellow{}", i)); +> GraphvizAddEdge( +> t, StringFormatted("rg{}", i), StringFormatted("green{}", i)); +> GraphvizAddEdge( +> t, StringFormatted("yr{}", i), StringFormatted("safe{}", j)); +> GraphvizAddEdge( +> t, StringFormatted("yr{}", i), StringFormatted("red{}", i)); +> GraphvizAddEdge( +> t, StringFormatted("safe{}", i), StringFormatted("rg{}", i)); +> GraphvizAddEdge( +> t, StringFormatted("green{}", i), StringFormatted("gy{}", i)); +> GraphvizAddEdge( +> t, StringFormatted("yellow{}", i), StringFormatted("yr{}", i)); +> GraphvizAddEdge( +> t, StringFormatted("red{}", i), StringFormatted("rg{}", i)); +> od; + +# +gap> GraphvizSetAttr(t, "overlap=\"false\""); + +gap> GraphvizSetAttr(t, +> """label="PetriNet Model TrafficLights +> Extracted from ConceptBase and laid out by Graphviz" +> """); + +gap> GraphvizSetAttr(t, "fontsize=12"); + + +# +gap> AsString(t); +"//dot\ndigraph TrafficLights {\n\tengine=neato overlap=\"false\" label=\"Petr\ +iNet Model TrafficLights\nExtracted from ConceptBase and laid out by Graphviz\ +\"\n fontsize=12 \nsubgraph ctx1 {\n\tnode [shape=\"box\"] \n\tgy2\n\tyr2\n\tr\ +g2\n\tgy1\n\tyr1\n\trg1\n}\nsubgraph ctx2 {\n\tnode [shape=\"circle\", fixedsi\ +ze=true, width=0.9] \n\tgreen2\n\tyellow2\n\tred2\n\tsafe2\n\tgreen1\n\tyellow\ +1\n\tred1\n\tsafe1\n}\n\tgy2 -> yellow2\n\trg2 -> green2\n\tyr2 -> safe1\n\tyr\ +2 -> red2\n\tsafe2 -> rg2\n\tgreen2 -> gy2\n\tyellow2 -> yr2\n\tred2 -> rg2\n\ +\tgy1 -> yellow1\n\trg1 -> green1\n\tyr1 -> safe2\n\tyr1 -> red1\n\tsafe1 -> r\ +g1\n\tgreen1 -> gy1\n\tyellow1 -> yr1\n\tred1 -> rg1\n}\n" + +# +gap> STOP_TEST("graphviz package: traffic_lights.tst"); diff --git a/tst/examples/unix.tst b/tst/examples/unix.tst new file mode 100644 index 0000000..5207345 --- /dev/null +++ b/tst/examples/unix.tst @@ -0,0 +1,286 @@ +############################################################################# +## +## unix.tst +## Copyright (C) 2024 James Mitchell +## +## Licensing information can be found in the README file of this package. +## +############################################################################# +## + +# https://graphviz.readthedocs.io/en/stable/examples.html +# https://graphviz.org/Gallery/directed/unix.html + +#@local u +gap> START_TEST("graphviz package: examples/unix.tst"); +gap> LoadPackage("graphviz"); +true + +# +gap> u := GraphvizDigraph("unix"); + +gap> GraphvizSetAttr( +> u, "node [color=\"lightblue2\", style=\"filled\", size=\"6,6\"]"); + + +# +gap> GraphvizAddEdge(u, "5th Edition", "6th Edition"); + +gap> GraphvizAddEdge(u, "5th Edition", "PWB 1.0"); + +gap> GraphvizAddEdge(u, "6th Edition", "LSX"); + +gap> GraphvizAddEdge(u, "6th Edition", "1 BSD"); + +gap> GraphvizAddEdge(u, "6th Edition", "Mini Unix"); + +gap> GraphvizAddEdge(u, "6th Edition", "Wollongong"); + +gap> GraphvizAddEdge(u, "6th Edition", "Interdata"); + +gap> GraphvizAddEdge(u, "Unix/TS 3.0", "Interdata"); + +gap> GraphvizAddEdge(u, "Interdata", "PWB 2.0"); + +gap> GraphvizAddEdge(u, "Interdata", "7th Edition"); + +gap> GraphvizAddEdge(u, "7th Edition", "8th Edition"); + +gap> GraphvizAddEdge(u, "7th Edition", "32V"); + +gap> GraphvizAddEdge(u, "7th Edition", "V7M"); + +gap> GraphvizAddEdge(u, "7th Edition", "Ultrix-11"); + +gap> GraphvizAddEdge(u, "7th Edition", "Xenix"); + +gap> GraphvizAddEdge(u, "7th Edition", "UniPlus+"); + +gap> GraphvizAddEdge(u, "V7M", "Ultrix-11"); + +gap> GraphvizAddEdge(u, "8th Edition", "9th Edition"); + +gap> GraphvizAddEdge(u, "1 BSD", "2 BSD"); + +gap> GraphvizAddEdge(u, "2 BSD", "2.8 BSD"); + +gap> GraphvizAddEdge(u, "2.8 BSD", "Ultrix-11"); + +gap> GraphvizAddEdge(u, "2.8 BSD", "2.9 BSD"); + +gap> GraphvizAddEdge(u, "32V", "3 BSD"); + +gap> GraphvizAddEdge(u, "3 BSD", "4 BSD"); + +gap> GraphvizAddEdge(u, "4 BSD", "4.1 BSD"); + +gap> GraphvizAddEdge(u, "4.1 BSD", "4.2 BSD"); + +gap> GraphvizAddEdge(u, "4.1 BSD", "2.8 BSD"); + +gap> GraphvizAddEdge(u, "4.1 BSD", "8th Edition"); + +gap> GraphvizAddEdge(u, "4.2 BSD", "4.3 BSD"); + +gap> GraphvizAddEdge(u, "4.2 BSD", "Ultrix-32"); + +gap> GraphvizAddEdge(u, "PWB 1.0", "PWB 1.2"); + +gap> GraphvizAddEdge(u, "PWB 1.0", "USG 1.0"); + +gap> GraphvizAddEdge(u, "PWB 1.2", "PWB 2.0"); + +gap> GraphvizAddEdge(u, "USG 1.0", "CB Unix 1"); + +gap> GraphvizAddEdge(u, "USG 1.0", "USG 2.0"); + +gap> GraphvizAddEdge(u, "CB Unix 1", "CB Unix 2"); + +gap> GraphvizAddEdge(u, "CB Unix 2", "CB Unix 3"); + +gap> GraphvizAddEdge(u, "CB Unix 3", "Unix/TS++"); + +gap> GraphvizAddEdge(u, "CB Unix 3", "PDP-11 Sys V"); + +gap> GraphvizAddEdge(u, "USG 2.0", "USG 3.0"); + +gap> GraphvizAddEdge(u, "USG 3.0", "Unix/TS 3.0"); + +gap> GraphvizAddEdge(u, "PWB 2.0", "Unix/TS 3.0"); + +gap> GraphvizAddEdge(u, "Unix/TS 1.0", "Unix/TS 3.0"); + +gap> GraphvizAddEdge(u, "Unix/TS 3.0", "TS 4.0"); + +gap> GraphvizAddEdge(u, "Unix/TS++", "TS 4.0"); + +gap> GraphvizAddEdge(u, "CB Unix 3", "TS 4.0"); + +gap> GraphvizAddEdge(u, "TS 4.0", "System V.0"); + +gap> GraphvizAddEdge(u, "System V.0", "System V.2"); + +gap> GraphvizAddEdge(u, "System V.2", "System V.3"); + + +# +gap> AsString(u) = +> "//dot\ndigraph unix {\n\tnode [color=\"lightblue2\", style=\"filled\", size=\ +> \"6,6\"] \n\t\"5th Edition\"\n\t\"6th Edition\"\n\t\"5th Edition\" -> \"6th Ed\ +> ition\"\n\t\"PWB 1.0\"\n\t\"5th Edition\" -> \"PWB 1.0\"\n\tLSX\n\t\"6th Editi\ +> on\" -> LSX\n\t\"1 BSD\"\n\t\"6th Edition\" -> \"1 BSD\"\n\t\"Mini Unix\"\n\t\ +> \"6th Edition\" -> \"Mini Unix\"\n\tWollongong\n\t\"6th Edition\" -> Wollongon\ +> g\n\tInterdata\n\t\"6th Edition\" -> Interdata\n\t\"Unix/TS 3.0\"\n\t\"Unix/TS\ +> 3.0\" -> Interdata\n\t\"PWB 2.0\"\n\tInterdata -> \"PWB 2.0\"\n\t\"7th Editio\ +> n\"\n\tInterdata -> \"7th Edition\"\n\t\"8th Edition\"\n\t\"7th Edition\" -> \ +> \"8th Edition\"\n\t\"32V\"\n\t\"7th Edition\" -> \"32V\"\n\tV7M\n\t\"7th Editi\ +> on\" -> V7M\n\t\"Ultrix-11\"\n\t\"7th Edition\" -> \"Ultrix-11\"\n\tXenix\n\t\ +> \"7th Edition\" -> Xenix\n\t\"UniPlus+\"\n\t\"7th Edition\" -> \"UniPlus+\"\n\ +> \tV7M -> \"Ultrix-11\"\n\t\"9th Edition\"\n\t\"8th Edition\" -> \"9th Edition\ +> \"\n\t\"2 BSD\"\n\t\"1 BSD\" -> \"2 BSD\"\n\t\"2.8 BSD\"\n\t\"2 BSD\" -> \"2.8\ +> BSD\"\n\t\"2.8 BSD\" -> \"Ultrix-11\"\n\t\"2.9 BSD\"\n\t\"2.8 BSD\" -> \"2.9 \ +> BSD\"\n\t\"3 BSD\"\n\t\"32V\" -> \"3 BSD\"\n\t\"4 BSD\"\n\t\"3 BSD\" -> \"4 BS\ +> D\"\n\t\"4.1 BSD\"\n\t\"4 BSD\" -> \"4.1 BSD\"\n\t\"4.2 BSD\"\n\t\"4.1 BSD\" -\ +> > \"4.2 BSD\"\n\t\"4.1 BSD\" -> \"2.8 BSD\"\n\t\"4.1 BSD\" -> \"8th Edition\"\ +> \n\t\"4.3 BSD\"\n\t\"4.2 BSD\" -> \"4.3 BSD\"\n\t\"Ultrix-32\"\n\t\"4.2 BSD\" \ +> -> \"Ultrix-32\"\n\t\"PWB 1.2\"\n\t\"PWB 1.0\" -> \"PWB 1.2\"\n\t\"USG 1.0\"\n\ +> \t\"PWB 1.0\" -> \"USG 1.0\"\n\t\"PWB 1.2\" -> \"PWB 2.0\"\n\t\"CB Unix 1\"\n\ +> \t\"USG 1.0\" -> \"CB Unix 1\"\n\t\"USG 2.0\"\n\t\"USG 1.0\" -> \"USG 2.0\"\n\ +> \t\"CB Unix 2\"\n\t\"CB Unix 1\" -> \"CB Unix 2\"\n\t\"CB Unix 3\"\n\t\"CB Uni\ +> x 2\" -> \"CB Unix 3\"\n\t\"Unix/TS++\"\n\t\"CB Unix 3\" -> \"Unix/TS++\"\n\t\ +> \"PDP-11 Sys V\"\n\t\"CB Unix 3\" -> \"PDP-11 Sys V\"\n\t\"USG 3.0\"\n\t\"USG \ +> 2.0\" -> \"USG 3.0\"\n\t\"USG 3.0\" -> \"Unix/TS 3.0\"\n\t\"PWB 2.0\" -> \"Uni\ +> x/TS 3.0\"\n\t\"Unix/TS 1.0\"\n\t\"Unix/TS 1.0\" -> \"Unix/TS 3.0\"\n\t\"TS 4.\ +> 0\"\n\t\"Unix/TS 3.0\" -> \"TS 4.0\"\n\t\"Unix/TS++\" -> \"TS 4.0\"\n\t\"CB Un\ +> ix 3\" -> \"TS 4.0\"\n\t\"System V.0\"\n\t\"TS 4.0\" -> \"System V.0\"\n\t\"Sy\ +> stem V.2\"\n\t\"System V.0\" -> \"System V.2\"\n\t\"System V.3\"\n\t\"System V\ +> .2\" -> \"System V.3\"\n}\n"; +#I invalid node name 5th Edition using "5th Edition" instead +#I invalid node name 6th Edition using "6th Edition" instead +#I invalid node name 5th Edition using "5th Edition" instead +#I invalid node name 6th Edition using "6th Edition" instead +#I invalid node name PWB 1.0 using "PWB 1.0" instead +#I invalid node name 5th Edition using "5th Edition" instead +#I invalid node name PWB 1.0 using "PWB 1.0" instead +#I invalid node name 6th Edition using "6th Edition" instead +#I invalid node name 1 BSD using "1 BSD" instead +#I invalid node name 6th Edition using "6th Edition" instead +#I invalid node name 1 BSD using "1 BSD" instead +#I invalid node name Mini Unix using "Mini Unix" instead +#I invalid node name 6th Edition using "6th Edition" instead +#I invalid node name Mini Unix using "Mini Unix" instead +#I invalid node name 6th Edition using "6th Edition" instead +#I invalid node name 6th Edition using "6th Edition" instead +#I invalid node name Unix/TS 3.0 using "Unix/TS 3.0" instead +#I invalid node name Unix/TS 3.0 using "Unix/TS 3.0" instead +#I invalid node name PWB 2.0 using "PWB 2.0" instead +#I invalid node name PWB 2.0 using "PWB 2.0" instead +#I invalid node name 7th Edition using "7th Edition" instead +#I invalid node name 7th Edition using "7th Edition" instead +#I invalid node name 8th Edition using "8th Edition" instead +#I invalid node name 7th Edition using "7th Edition" instead +#I invalid node name 8th Edition using "8th Edition" instead +#I invalid node name 32V using "32V" instead +#I invalid node name 7th Edition using "7th Edition" instead +#I invalid node name 32V using "32V" instead +#I invalid node name 7th Edition using "7th Edition" instead +#I invalid node name Ultrix-11 using "Ultrix-11" instead +#I invalid node name 7th Edition using "7th Edition" instead +#I invalid node name Ultrix-11 using "Ultrix-11" instead +#I invalid node name 7th Edition using "7th Edition" instead +#I invalid node name UniPlus+ using "UniPlus+" instead +#I invalid node name 7th Edition using "7th Edition" instead +#I invalid node name UniPlus+ using "UniPlus+" instead +#I invalid node name Ultrix-11 using "Ultrix-11" instead +#I invalid node name 9th Edition using "9th Edition" instead +#I invalid node name 8th Edition using "8th Edition" instead +#I invalid node name 9th Edition using "9th Edition" instead +#I invalid node name 2 BSD using "2 BSD" instead +#I invalid node name 1 BSD using "1 BSD" instead +#I invalid node name 2 BSD using "2 BSD" instead +#I invalid node name 2.8 BSD using "2.8 BSD" instead +#I invalid node name 2 BSD using "2 BSD" instead +#I invalid node name 2.8 BSD using "2.8 BSD" instead +#I invalid node name 2.8 BSD using "2.8 BSD" instead +#I invalid node name Ultrix-11 using "Ultrix-11" instead +#I invalid node name 2.9 BSD using "2.9 BSD" instead +#I invalid node name 2.8 BSD using "2.8 BSD" instead +#I invalid node name 2.9 BSD using "2.9 BSD" instead +#I invalid node name 3 BSD using "3 BSD" instead +#I invalid node name 32V using "32V" instead +#I invalid node name 3 BSD using "3 BSD" instead +#I invalid node name 4 BSD using "4 BSD" instead +#I invalid node name 3 BSD using "3 BSD" instead +#I invalid node name 4 BSD using "4 BSD" instead +#I invalid node name 4.1 BSD using "4.1 BSD" instead +#I invalid node name 4 BSD using "4 BSD" instead +#I invalid node name 4.1 BSD using "4.1 BSD" instead +#I invalid node name 4.2 BSD using "4.2 BSD" instead +#I invalid node name 4.1 BSD using "4.1 BSD" instead +#I invalid node name 4.2 BSD using "4.2 BSD" instead +#I invalid node name 4.1 BSD using "4.1 BSD" instead +#I invalid node name 2.8 BSD using "2.8 BSD" instead +#I invalid node name 4.1 BSD using "4.1 BSD" instead +#I invalid node name 8th Edition using "8th Edition" instead +#I invalid node name 4.3 BSD using "4.3 BSD" instead +#I invalid node name 4.2 BSD using "4.2 BSD" instead +#I invalid node name 4.3 BSD using "4.3 BSD" instead +#I invalid node name Ultrix-32 using "Ultrix-32" instead +#I invalid node name 4.2 BSD using "4.2 BSD" instead +#I invalid node name Ultrix-32 using "Ultrix-32" instead +#I invalid node name PWB 1.2 using "PWB 1.2" instead +#I invalid node name PWB 1.0 using "PWB 1.0" instead +#I invalid node name PWB 1.2 using "PWB 1.2" instead +#I invalid node name USG 1.0 using "USG 1.0" instead +#I invalid node name PWB 1.0 using "PWB 1.0" instead +#I invalid node name USG 1.0 using "USG 1.0" instead +#I invalid node name PWB 1.2 using "PWB 1.2" instead +#I invalid node name PWB 2.0 using "PWB 2.0" instead +#I invalid node name CB Unix 1 using "CB Unix 1" instead +#I invalid node name USG 1.0 using "USG 1.0" instead +#I invalid node name CB Unix 1 using "CB Unix 1" instead +#I invalid node name USG 2.0 using "USG 2.0" instead +#I invalid node name USG 1.0 using "USG 1.0" instead +#I invalid node name USG 2.0 using "USG 2.0" instead +#I invalid node name CB Unix 2 using "CB Unix 2" instead +#I invalid node name CB Unix 1 using "CB Unix 1" instead +#I invalid node name CB Unix 2 using "CB Unix 2" instead +#I invalid node name CB Unix 3 using "CB Unix 3" instead +#I invalid node name CB Unix 2 using "CB Unix 2" instead +#I invalid node name CB Unix 3 using "CB Unix 3" instead +#I invalid node name Unix/TS++ using "Unix/TS++" instead +#I invalid node name CB Unix 3 using "CB Unix 3" instead +#I invalid node name Unix/TS++ using "Unix/TS++" instead +#I invalid node name PDP-11 Sys V using "PDP-11 Sys V" instead +#I invalid node name CB Unix 3 using "CB Unix 3" instead +#I invalid node name PDP-11 Sys V using "PDP-11 Sys V" instead +#I invalid node name USG 3.0 using "USG 3.0" instead +#I invalid node name USG 2.0 using "USG 2.0" instead +#I invalid node name USG 3.0 using "USG 3.0" instead +#I invalid node name USG 3.0 using "USG 3.0" instead +#I invalid node name Unix/TS 3.0 using "Unix/TS 3.0" instead +#I invalid node name PWB 2.0 using "PWB 2.0" instead +#I invalid node name Unix/TS 3.0 using "Unix/TS 3.0" instead +#I invalid node name Unix/TS 1.0 using "Unix/TS 1.0" instead +#I invalid node name Unix/TS 1.0 using "Unix/TS 1.0" instead +#I invalid node name Unix/TS 3.0 using "Unix/TS 3.0" instead +#I invalid node name TS 4.0 using "TS 4.0" instead +#I invalid node name Unix/TS 3.0 using "Unix/TS 3.0" instead +#I invalid node name TS 4.0 using "TS 4.0" instead +#I invalid node name Unix/TS++ using "Unix/TS++" instead +#I invalid node name TS 4.0 using "TS 4.0" instead +#I invalid node name CB Unix 3 using "CB Unix 3" instead +#I invalid node name TS 4.0 using "TS 4.0" instead +#I invalid node name System V.0 using "System V.0" instead +#I invalid node name TS 4.0 using "TS 4.0" instead +#I invalid node name System V.0 using "System V.0" instead +#I invalid node name System V.2 using "System V.2" instead +#I invalid node name System V.0 using "System V.0" instead +#I invalid node name System V.2 using "System V.2" instead +#I invalid node name System V.3 using "System V.3" instead +#I invalid node name System V.2 using "System V.2" instead +#I invalid node name System V.3 using "System V.3" instead +true + +# +gap> STOP_TEST("graphviz package: unix.tst"); diff --git a/tst/graph.tst b/tst/graph.tst index 0679058..dcffd6c 100644 --- a/tst/graph.tst +++ b/tst/graph.tst @@ -1,13 +1,15 @@ ############################################################################# ## -## standard/dot.tst -## Copyright (C) 2022 James D. Mitchell +## graph.tst +## Copyright (C) 2024 Matthew Pancer ## ## Licensing information can be found in the README file of this package. ## ############################################################################# ## -gap> START_TEST("graphviz package: dot.tst"); + +#@local a, ab, b, c, cd, d, g, n, n1, n2, x +gap> START_TEST("graphviz package: graph.tst"); gap> LoadPackage("graphviz", false);; # Test graph constructor @@ -175,9 +177,9 @@ gap> g := GraphvizGraph();; gap> GraphvizSetAttr(g, "color", "red");; gap> GraphvizSetAttr(g, "color", "blue");; gap> AsString(g); -"graph {\n\tcolor=red color=blue \n}\n" +"//dot\ngraph {\n\tcolor=red color=blue \n}\n" -# # Test removing attributes from a graph +# # Test removing attributes from a graph TODO uncomment or delete # gap> g := GraphvizGraph();; # gap> GraphvizSetAttr(g, "color", "red");; # gap> GraphvizSetAttr(g, "shape", "circle");; @@ -240,3 +242,4 @@ gap> GraphvizAttrs(g); [ "color=red" ] # +gap> STOP_TEST("graphviz package: graph.tst", 0); diff --git a/tst/node.tst b/tst/node.tst index 17600d4..6388f06 100644 --- a/tst/node.tst +++ b/tst/node.tst @@ -1,13 +1,15 @@ ############################################################################# ## -## standard/dot.tst -## Copyright (C) 2022 James D. Mitchell +## node.tst +## Copyright (C) 2024 Matthew Pancer ## ## Licensing information can be found in the README file of this package. ## ############################################################################# ## -gap> START_TEST("graphviz package: dot.tst"); + +#@local color, g, label, n, s, shape +gap> START_TEST("graphviz package: node.tst"); gap> LoadPackage("graphviz", false);; # Test node constructor @@ -26,7 +28,7 @@ gap> n := GraphvizAddNode(GraphvizGraph(), " "); # Test making a node with empty name fails gap> n := GraphvizAddNode(GraphvizGraph(), ""); -Error, Node name cannot be empty. +Error, the 2nd argument (string/node name) cannot be empty # Test whitespace in node names gap> n := GraphvizAddNode(GraphvizGraph(), "a a "); @@ -53,14 +55,14 @@ rec( shape := "circle" ) gap> g := GraphvizGraph();; gap> GraphvizAddNode(g, "test:colon");; gap> AsString(g); -"graph {\n\ttest:colon\n}\n" +"//dot\ngraph {\n\ttest:colon\n}\n" # Test non-string name containing ':' gap> g := GraphvizGraph();; gap> GraphvizAddNode(g, 111); gap> AsString(g); -"graph {\n\t111\n}\n" +"//dot\ngraph {\n\t111\n}\n" # Test removing a node with a non-string name gap> g := GraphvizGraph();; @@ -111,3 +113,4 @@ gap> GraphvizAddNode(s, n); Error, Cannot add node objects directly to graphs. Please use the node's name. # +gap> STOP_TEST("graphviz package: node.tst", 0); diff --git a/tst/subgraph.tst b/tst/subgraph.tst index 1d1ae4c..94d0bc3 100644 --- a/tst/subgraph.tst +++ b/tst/subgraph.tst @@ -1,13 +1,16 @@ ############################################################################# ## -## standard/dot.tst -## Copyright (C) 2022 James D. Mitchell +## subgraph.tst +## Copyright (C) 2024 Matthew Pancer ## ## Licensing information can be found in the README file of this package. ## ############################################################################# ## -gap> START_TEST("graphviz package: dot.tst"); + +#@local a, a1, a2, a3, b, b1, b2, b3, c, child, ctx, g, main, n, o, parent, s +#@local s1, s11, s2, sibling +gap> START_TEST("graphviz package: subgraph.tst"); gap> LoadPackage("graphviz", false);; # Test creating subgraphs (named) @@ -132,8 +135,8 @@ gap> GraphvizSetAttr(s, "color", "red");; gap> GraphvizSetAttr(s, "node [color=red]");; gap> GraphvizSetAttr(s, "edge [color=red]");; gap> AsString(g); -"digraph {\nsubgraph a {\n\tcolor=red node [color=red] edge [color=red] \n}\n\ -\tx\n\ty\n\tx -> y\n}\n" +"//dot\ndigraph {\nsubgraph a {\n\tcolor=red node [color=red] edge [color=red\ +] \n}\n\tx\n\ty\n\tx -> y\n}\n" # Test stringifying subgraph graph gap> g := GraphvizGraph();; @@ -143,8 +146,8 @@ gap> GraphvizSetAttr(s, "color", "red");; gap> GraphvizSetAttr(s, "node [color=red]");; gap> GraphvizSetAttr(s, "edge [color=red]");; gap> AsString(g); -"graph {\nsubgraph a {\n\tcolor=red node [color=red] edge [color=red] \n}\n\t\ -x\n\ty\n\tx -- y\n}\n" +"//dot\ngraph {\nsubgraph a {\n\tcolor=red node [color=red] edge [color=red] \ +\n}\n\tx\n\ty\n\tx -- y\n}\n" # Test stringifying subgraph context (graph) gap> g := GraphvizGraph();; @@ -154,8 +157,8 @@ gap> GraphvizSetAttr(s, "color", "red");; gap> GraphvizSetAttr(s, "node [color=red]");; gap> GraphvizSetAttr(s, "edge [color=red]");; gap> AsString(g); -"graph {\n// a context \n\tcolor=red node [color=red] edge [color=red] \n\n\t\ -x\n\ty\n\tx -- y\n}\n" +"//dot\ngraph {\n// a context \n\tcolor=red node [color=red] edge [color=red]\ + \n\n\tx\n\ty\n\tx -- y\n}\n" # Test stringifying subgraph context (digraph) gap> g := GraphvizDigraph();; @@ -165,14 +168,14 @@ gap> GraphvizSetAttr(s, "color", "red");; gap> GraphvizSetAttr(s, "node [color=red]");; gap> GraphvizSetAttr(s, "edge [color=red]");; gap> AsString(g); -"digraph {\n// a context \n\tcolor=red node [color=red] edge [color=red] \n\n\ -\tx\n\ty\n\tx -> y\n}\n" +"//dot\ndigraph {\n// a context \n\tcolor=red node [color=red] edge [color=re\ +d] \n\n\tx\n\ty\n\tx -> y\n}\n" # Test stringifying subgraph w/o name gap> g := GraphvizDigraph();; gap> s := GraphvizAddSubgraph(g);; gap> AsString(g); -"digraph {\nsubgraph no_name_1 {\n}\n}\n" +"//dot\ndigraph {\nsubgraph no_name_1 {\n}\n}\n" # finding a node in a sibling graph gap> g := GraphvizDigraph();; @@ -242,9 +245,9 @@ gap> GraphvizSetAttr(g, "edge[color=blue]");; gap> GraphvizSetAttr(ctx, "node[color=red]");; gap> GraphvizAddNode(ctx, "a");; gap> AsString(g); -"digraph {\n\tcolor=green edge [label=testing123] node[color=blue] edge[color\ -=blue] \n// no_name_1 context \n\tnode[color=red] \n\ta\n\tcolor=green edge [l\ -abel=testing123] node[color=blue] edge[color=blue] \n\n}\n" +"//dot\ndigraph {\n\tcolor=green edge [label=testing123] node[color=blue] edg\ +e[color=blue] \n// no_name_1 context \n\tnode[color=red] \n\ta\n\tcolor=green \ +edge [label=testing123] node[color=blue] edge[color=blue] \n\n}\n" # Test adding subgraphs with the same name gap> g := GraphvizDigraph();; @@ -288,7 +291,7 @@ gap> g := GraphvizGraph();; gap> s1 := GraphvizAddSubgraph(g, "a");; gap> s2 := GraphvizAddSubgraph(s1, "c");; gap> AsString(g); -"graph {\nsubgraph a {\nsubgraph c {\n}\n}\n}\n" +"//dot\ngraph {\nsubgraph a {\nsubgraph c {\n}\n}\n}\n" # Test subgraphs with non-string names gap> g := GraphvizGraph();; @@ -357,7 +360,8 @@ gap> parent := GraphvizAddContext(g, "parent");; gap> ctx := GraphvizAddContext(parent, "ctx");; gap> GraphvizAddEdge(ctx, "a", "b");; gap> AsString(g); -"graph g {\n// parent context \n// ctx context \n\ta\n\tb\n\ta -- b\n\n\n}\n" +"//dot\ngraph g {\n// parent context \n// ctx context \n\ta\n\tb\n\ta -- b\n\n\ +\n}\n" # Test nested contexts have correct edge types (digraph) gap> g := GraphvizDigraph("g");; @@ -365,8 +369,8 @@ gap> parent := GraphvizAddContext(g, "parent");; gap> ctx := GraphvizAddContext(parent, "ctx");; gap> GraphvizAddEdge(ctx, "a", "b");; gap> AsString(g); -"digraph g {\n// parent context \n// ctx context \n\ta\n\tb\n\ta -> b\n\n\n}\n\ -" +"//dot\ndigraph g {\n// parent context \n// ctx context \n\ta\n\tb\n\ta -> b\n\ +\n\n}\n" # Test finding subgraph (non-string name) gap> g := GraphvizGraph("r");; @@ -377,3 +381,4 @@ gap> IsIdenticalObj(o, s); true # +gap> STOP_TEST("graphviz package: subgraph.tst", 0); diff --git a/tst/testall.g b/tst/testall.g index d891b2f..a5ad9d1 100644 --- a/tst/testall.g +++ b/tst/testall.g @@ -1,10 +1,6 @@ -# -# graphviz: This package facilitates the creation of graph descriptions in the -# DOT language of the Graphviz graph drawing software from GAP -# -# This file runs package tests. It is also referenced in the package -# metadata in PackageInfo.g. -# +# This file runs package tests. It is also referenced in the package metadata +# in PackageInfo.g. + LoadPackage("graphviz"); TestDirectory(DirectoriesPackageLibrary("graphviz", "tst"),