|
297 | 297 | :meth:`~GenericGraph.show3d` | Plot the graph using :class:`~sage.plot.plot3d.tachyon.Tachyon`, and shows the resulting plot. |
298 | 298 | :meth:`~GenericGraph.graphviz_string` | Return a representation in the ``dot`` language. |
299 | 299 | :meth:`~GenericGraph.graphviz_to_file_named` | Write a representation in the ``dot`` language in a file. |
| 300 | + :meth:`~GenericGraph.tikz` | Return a :class:`~sage.misc.latex_standalone.TikzPicture` object representing the (di)graph. |
300 | 301 |
|
301 | 302 | **Algorithmically hard stuff:** |
302 | 303 |
|
@@ -939,6 +940,190 @@ def _latex_(self): |
939 | 940 |
|
940 | 941 | return self.latex_options().latex() |
941 | 942 |
|
| 943 | + def tikz(self, format='dot2tex', edge_labels=None, |
| 944 | + color_by_label=False, prog='dot', rankdir='down', |
| 945 | + standalone_config=None, usepackage=None, |
| 946 | + usetikzlibrary=None, macros=None, |
| 947 | + use_sage_preamble=None, **kwds): |
| 948 | + r""" |
| 949 | + Return a TikzPicture of the graph. |
| 950 | + |
| 951 | + If graphviz and dot2tex are available, it uses these packages for |
| 952 | + placements of vertices and edges. |
| 953 | + |
| 954 | + INPUT: |
| 955 | + |
| 956 | + - ``format`` -- string (default: ``None``), ``'dot2tex'`` or |
| 957 | + ``'tkz_graph'``. If ``None``, it is set to ``'dot2tex'`` if |
| 958 | + dot2tex is present, otherwise it is set to ``'tkz_graph'``. |
| 959 | + - ``edge_labels`` -- bool (default: ``None``), if ``None`` |
| 960 | + it is set to ``True`` if and only if format is ``'dot2tex'`` |
| 961 | + - ``color_by_label`` -- boolean or dictionary or function (default: |
| 962 | + ``False``); whether to color each edge with a different color |
| 963 | + according to its label; the colors are chosen along a rainbow, unless |
| 964 | + they are specified by a function or dictionary mapping labels to |
| 965 | + colors; |
| 966 | + |
| 967 | + When using format ``'dot2tex'``, the following inputs are considered: |
| 968 | + |
| 969 | + - ``prog`` -- string (default: ``'dot'``) the program used for the |
| 970 | + layout corresponding to one of the software of the graphviz |
| 971 | + suite: 'dot', 'neato', 'twopi', 'circo' or 'fdp'. |
| 972 | + - ``rankdir`` -- string (default: ``'down'``), direction of graph layout |
| 973 | + when prog is ``'dot'``, possible values are ``'down'``, |
| 974 | + ``'up'``, ``'right'`` and ``'left'``. |
| 975 | + - ``subgraph_clusters`` -- (default: ``[]``) a list of lists of |
| 976 | + vertices, if supported by the layout engine, nodes belonging to |
| 977 | + the same cluster subgraph are drawn together, with the entire |
| 978 | + drawing of the cluster contained within a bounding rectangle. |
| 979 | + |
| 980 | + Additionnal keywords arguments are forwarded to |
| 981 | + :meth:`sage.graphs.graph_latex.GraphLatex.set_option`. |
| 982 | + |
| 983 | + The following inputs define the preamble of the latex standalone |
| 984 | + document class file containing the tikzpicture: |
| 985 | + |
| 986 | + - ``standalone_config`` -- list of strings (default: ``["border=4mm"]``); |
| 987 | + latex document class standalone configuration options |
| 988 | + - ``usepackage`` -- list of strings (default: ``[]``); latex |
| 989 | + packages |
| 990 | + - ``usetikzlibrary`` -- list of strings (default: ``[]``); tikz |
| 991 | + libraries to use |
| 992 | + - ``macros`` -- list of strings (default: ``[]``); list of |
| 993 | + newcommands needed for the picture |
| 994 | + - ``use_sage_preamble`` -- bool (default: ``None``), if ``None`` |
| 995 | + it is set to ``True`` if and only if format is ``'tkz_graph'`` |
| 996 | + |
| 997 | + OUTPUT: |
| 998 | + |
| 999 | + An instance of :mod:`sage.misc.latex_standalone.TikzPicture`. |
| 1000 | + |
| 1001 | + .. NOTE:: |
| 1002 | + |
| 1003 | + Prerequisite: dot2tex optional Sage package and graphviz must be |
| 1004 | + installed when using format ``'dot2tex'``. |
| 1005 | + |
| 1006 | + EXAMPLES:: |
| 1007 | + |
| 1008 | + sage: g = graphs.PetersenGraph() |
| 1009 | + sage: tikz = g.tikz() # optional - dot2tex graphviz # long time |
| 1010 | + sage: _ = tikz.pdf(view=False) # optional - dot2tex graphviz latex # long time |
| 1011 | + |
| 1012 | + :: |
| 1013 | + |
| 1014 | + sage: tikz = g.tikz(format='tkz_graph') |
| 1015 | + sage: _ = tikz.pdf(view=False) # optional - latex |
| 1016 | + |
| 1017 | + Using another value for ``prog``:: |
| 1018 | + |
| 1019 | + sage: tikz = g.tikz(prog='neato') # optional - dot2tex graphviz # long time |
| 1020 | + sage: _ = tikz.pdf() # optional - dot2tex graphviz latex # long time |
| 1021 | + |
| 1022 | + Using ``color_by_label`` with default rainbow colors:: |
| 1023 | + |
| 1024 | + sage: G = DiGraph({0: {1: 333, 2: 444}, 1: {0: 444}, 2: {0: 555}}) |
| 1025 | + sage: t = G.tikz(color_by_label=True) # optional - dot2tex graphviz # long time |
| 1026 | + sage: _ = t.pdf(view=False) # optional - dot2tex graphviz latex # long time |
| 1027 | + |
| 1028 | + Using ``color_by_label`` with colors given as a dictionary:: |
| 1029 | + |
| 1030 | + sage: G = DiGraph({0: {1: 333, 2: 444}, 1: {0: 444}, 2: {0: 555}}) |
| 1031 | + sage: cbl = {333:'orange', 444: 'yellow', 555: 'purple'} |
| 1032 | + sage: t = G.tikz(color_by_label=cbl) # optional - dot2tex graphviz # long time |
| 1033 | + sage: _ = t.pdf(view=False) # optional - dot2tex graphviz latex # long time |
| 1034 | + |
| 1035 | + Using ``color_by_label`` with colors given as a function:: |
| 1036 | + |
| 1037 | + sage: G = DiGraph({0: {1: -333, 2: -444}, 1: {0: 444}, 2: {0: 555}}) |
| 1038 | + sage: cbl = lambda label:'green' if label >= 0 else 'orange' |
| 1039 | + sage: t = G.tikz(color_by_label=cbl) # optional - dot2tex graphviz # long time |
| 1040 | + sage: _ = t.pdf(view=False) # optional - dot2tex graphviz latex # long time |
| 1041 | + |
| 1042 | + Using another value for ``rankdir``:: |
| 1043 | + |
| 1044 | + sage: tikz = g.tikz(rankdir='right') # optional - dot2tex graphviz # long time |
| 1045 | + sage: _ = tikz.pdf(view=False) # optional - dot2tex graphviz latex # long time |
| 1046 | + |
| 1047 | + Using subgraphs clusters (broken when using labels, see |
| 1048 | + :issue:`22070`):: |
| 1049 | + |
| 1050 | + sage: S = FiniteSetMaps(5) |
| 1051 | + sage: I = S((0,1,2,3,4)) |
| 1052 | + sage: a = S((0,1,3,0,0)) |
| 1053 | + sage: b = S((0,2,4,1,0)) |
| 1054 | + sage: roots = [I] |
| 1055 | + sage: succ = lambda v: [v*a,v*b,a*v,b*v] |
| 1056 | + sage: R = RecursivelyEnumeratedSet(roots, succ) |
| 1057 | + sage: G = R.to_digraph() |
| 1058 | + sage: G |
| 1059 | + Looped multi-digraph on 27 vertices |
| 1060 | + sage: C = G.strongly_connected_components() |
| 1061 | + sage: tikz = G.tikz(subgraph_clusters=C)# optional - dot2tex graphviz # long time |
| 1062 | + sage: tikz.add_usepackage('amstext') # optional - dot2tex graphviz # long time |
| 1063 | + sage: _ = tikz.pdf(view=False) # optional - dot2tex graphviz latex # long time |
| 1064 | + |
| 1065 | + An example coming from ``graphviz_string`` documentation in SageMath:: |
| 1066 | + |
| 1067 | + sage: # needs sage.symbolic |
| 1068 | + sage: f(x) = -1 / x |
| 1069 | + sage: g(x) = 1 / (x + 1) |
| 1070 | + sage: G = DiGraph() |
| 1071 | + sage: G.add_edges((i, f(i), f) for i in (1, 2, 1/2, 1/4)) |
| 1072 | + sage: G.add_edges((i, g(i), g) for i in (1, 2, 1/2, 1/4)) |
| 1073 | + sage: tikz = G.tikz(format='dot2tex') # optional - dot2tex graphviz # long time |
| 1074 | + sage: _ = tikz.pdf(view=False) # optional - dot2tex graphviz latex # long time |
| 1075 | + sage: def edge_options(data): |
| 1076 | + ....: u, v, label = data |
| 1077 | + ....: options = {"color": {f: "red", g: "blue"}[label]} |
| 1078 | + ....: if (u,v) == (1/2, -2): options["label"] = "coucou"; options["label_style"] = "string" |
| 1079 | + ....: if (u,v) == (1/2,2/3): options["dot"] = "x=1,y=2" |
| 1080 | + ....: if (u,v) == (1, -1): options["label_style"] = "latex" |
| 1081 | + ....: if (u,v) == (1, 1/2): options["dir"] = "back" |
| 1082 | + ....: return options |
| 1083 | + sage: tikz = G.tikz(format='dot2tex', # optional - dot2tex graphviz # long time |
| 1084 | + ....: edge_options=edge_options) |
| 1085 | + sage: _ = tikz.pdf(view=False) # optional - dot2tex graphviz latex # long time |
| 1086 | + """ |
| 1087 | + # use format dot2tex by default |
| 1088 | + if format is None: |
| 1089 | + from sage.features import PythonModule |
| 1090 | + if PythonModule("dot2tex").is_present(): |
| 1091 | + format = 'dot2tex' |
| 1092 | + else: |
| 1093 | + format = 'tkz_graph' |
| 1094 | + |
| 1095 | + # by default draw edge_labels for dot2tex but not for tkz_graph |
| 1096 | + # (because tkz_graph draws None everywhere which is ugly, whereas |
| 1097 | + # dot2tex ignores the labels when they are ``None``) |
| 1098 | + if edge_labels is None: |
| 1099 | + if format == 'tkz_graph': |
| 1100 | + edge_labels = False |
| 1101 | + elif format == 'dot2tex': |
| 1102 | + edge_labels = True |
| 1103 | + |
| 1104 | + self.latex_options().set_options(format=format, |
| 1105 | + edge_labels=edge_labels, color_by_label=color_by_label, |
| 1106 | + prog=prog, rankdir=rankdir, **kwds) |
| 1107 | + |
| 1108 | + # by default use sage preamble only for format tkz_graph |
| 1109 | + # because content generated by tkz_graph depends on it |
| 1110 | + if use_sage_preamble is None: |
| 1111 | + if format == 'tkz_graph': |
| 1112 | + use_sage_preamble = True |
| 1113 | + elif format == 'dot2tex': |
| 1114 | + use_sage_preamble = False |
| 1115 | + |
| 1116 | + if standalone_config is None: |
| 1117 | + standalone_config = ["border=4mm"] |
| 1118 | + |
| 1119 | + from sage.misc.latex_standalone import TikzPicture |
| 1120 | + return TikzPicture(self._latex_(), |
| 1121 | + standalone_config=standalone_config, |
| 1122 | + usepackage=usepackage, |
| 1123 | + usetikzlibrary=usetikzlibrary, |
| 1124 | + macros=macros, |
| 1125 | + use_sage_preamble=use_sage_preamble) |
| 1126 | + |
942 | 1127 | def _matrix_(self, R=None, vertices=None): |
943 | 1128 | """ |
944 | 1129 | Return the adjacency matrix of the graph over the specified ring. |
|
0 commit comments