Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/graphs/graph_latex.py
8815 views
1
r"""
2
LaTeX options for graphs
3
4
This module provides a class to hold, manipulate and employ various
5
options for rendering a graph in LaTeX, in addition to providing
6
the code that actually generates a LaTeX representation
7
of a (combinatorial) graph.
8
9
AUTHORS:
10
11
- Rob Beezer (2009-05-20): :class:`~sage.graphs.graph_latex.GraphLatex` class
12
- Fidel Barerra Cruz (2009-05-20): ``tkz-graph`` commands to render a graph
13
- Nicolas M. Thiery (2010-02): dot2tex/graphviz interface
14
- Rob Beezer (2010-05-29): Extended range of ``tkz-graph`` options
15
16
LaTeX Versions of Graphs
17
-------------------------------------
18
19
.. image:: ../../media/heawood-graph-latex.png
20
:align: center
21
22
Many mathematical objects in Sage have LaTeX representations, and graphs are no exception. For a graph ``g``, the command ``view(g)``, issued at the Sage command line or in the notebook, will create a graphic version of ``g``. Similarly, ``latex(g)`` will return a (long) string that is a representation of the graph in LaTeX. Other ways of employing LaTeX in Sage, such as ``%latex`` in a notebook cell, or the Typeset checkbox in the notebook, will handle ``g`` appropriately.
23
24
Support through the ``tkz-graph`` package is by Alain Matthes, the author of ``tkz-graph``, whose work can be found at his `Altermundus.com <http://altermundus.com/>`_ site.
25
26
The range of possible options for customizing the appearance of a graph are carefully documented at :meth:`sage.graphs.graph_latex.GraphLatex.set_option`. As a broad overview, the following options are supported:
27
28
- Pre-built Styles: the pre-built styles of the tkz-graph package provide nice drawings quickly
29
- Dimensions: can be specified in natural units, then uniformly scaled after design work
30
- Vertex Colors: the perimeter and fill color for vertices can be specified, including on a per-vertex basis
31
- Vertex Shapes: may be circles, shaded spheres, rectangles or diamonds, including on a per-vertex basis
32
- Vertex Sizes: may be specified as minimums, and will automatically sized to contain vertex labels, including on a per-vertex basis
33
- Vertex Labels: can use latex formatting, and may have their colors specified, including on a per-vertex basis
34
- Vertex Label Placement: can be interior to the vertex, or external at a configurable location
35
- Edge Colors: a solid color with or without a second color down the middle, on a per-edge basis
36
- Edge Thickness: can be set, including on a per-edge basis
37
- Edge Labels: can use latex formatting, and may have their colors specified, including on a per-edge basis
38
- Edge Label Placement: can be to the left, right, above, below, inline, and then sloped or horizontal
39
- Digraph Edges: are slightly curved, with arrowheads
40
- Loops: may be specified by their size, and with a direction equaling one of the four compass points
41
42
To use LaTeX in Sage you of course need a working TeX installation and it will work best if you have the ``dvipng`` and ``convert`` utilities. For graphs you need the ``tkz-graph.sty`` and ``tkz-berge.sty`` style files of the tkz-graph package. TeX, dvipng, and convert should be widely available through package managers or installers. You may need to install the tkz-graph style files in the appropriate locations, a task beyond the scope of this introduction. Primary locations for these programs are:
43
44
- TeX: http://ctan.org/
45
- dvipng: http://sourceforge.net/projects/dvipng/
46
- convert: http://www.imagemagick.org (the ImageMagick suite)
47
- tkz-graph: http://altermundus.com/pages/graph.html
48
49
Customizing the output is accomplished in several ways. Suppose ``g`` is a graph, then ``g.set_latex_options()`` can be used to efficiently set or modify various options. Setting individual options, or querying options, can be accomplished by first using a command like ``opts = g.latex_options()`` to obtain a :class:`sage.graphs.graph_latex.GraphLatex` object which has several methods to set and retrieve options.
50
51
Here is a minimal session demonstrating how to use these features. The following setup should work in the notebook or at the command-line, though the call to :meth:`~sage.misc.latex.Latex.mathjax_avoid_list` is only needed in the notebook. ::
52
53
54
sage: from sage.graphs.graph_latex import setup_latex_preamble
55
sage: setup_latex_preamble()
56
sage: print latex.extra_preamble() # random - depends on TeX installation
57
\usepackage{tikz}
58
\usepackage{tkz-graph}
59
\usepackage{tkz-berge}
60
\usetikzlibrary{arrows,shapes}
61
sage: latex.engine('pdflatex')
62
sage: latex.mathjax_avoid_list('tikzpicture')
63
sage: H=graphs.HeawoodGraph()
64
sage: H.set_latex_options(
65
... graphic_size=(5,5),
66
... vertex_size=0.2,
67
... edge_thickness=0.04,
68
... edge_color='green',
69
... vertex_color='green',
70
... vertex_label_color='red'
71
... )
72
73
At this point, ``view(H)`` should call ``pdflatex`` to process the string created by ``latex(H)`` and then display the resulting graphic. These lines (excluding the last two) only need to be run once in a Sage session, and the line requesting display of the extra preamble is included here just as part of the demonstration.
74
75
To use this image in a LaTeX document, you could of course just copy and save the resulting graphic. However, the ``latex()`` command will produce the underlying LaTeX code, which can be incorporated into a standalone LaTeX document. ::
76
77
sage: from sage.graphs.graph_latex import check_tkz_graph
78
sage: check_tkz_graph() # random - depends on TeX installation
79
sage: latex(H)
80
\begin{tikzpicture}
81
%
82
\useasboundingbox (0,0) rectangle (5.0cm,5.0cm);
83
%
84
\definecolor{cv0}{rgb}{0.0,0.502,0.0}
85
\definecolor{cfv0}{rgb}{1.0,1.0,1.0}
86
\definecolor{clv0}{rgb}{1.0,0.0,0.0}
87
\definecolor{cv1}{rgb}{0.0,0.502,0.0}
88
\definecolor{cfv1}{rgb}{1.0,1.0,1.0}
89
\definecolor{clv1}{rgb}{1.0,0.0,0.0}
90
\definecolor{cv2}{rgb}{0.0,0.502,0.0}
91
\definecolor{cfv2}{rgb}{1.0,1.0,1.0}
92
\definecolor{clv2}{rgb}{1.0,0.0,0.0}
93
\definecolor{cv3}{rgb}{0.0,0.502,0.0}
94
\definecolor{cfv3}{rgb}{1.0,1.0,1.0}
95
\definecolor{clv3}{rgb}{1.0,0.0,0.0}
96
\definecolor{cv4}{rgb}{0.0,0.502,0.0}
97
\definecolor{cfv4}{rgb}{1.0,1.0,1.0}
98
\definecolor{clv4}{rgb}{1.0,0.0,0.0}
99
\definecolor{cv5}{rgb}{0.0,0.502,0.0}
100
\definecolor{cfv5}{rgb}{1.0,1.0,1.0}
101
\definecolor{clv5}{rgb}{1.0,0.0,0.0}
102
\definecolor{cv6}{rgb}{0.0,0.502,0.0}
103
\definecolor{cfv6}{rgb}{1.0,1.0,1.0}
104
\definecolor{clv6}{rgb}{1.0,0.0,0.0}
105
\definecolor{cv7}{rgb}{0.0,0.502,0.0}
106
\definecolor{cfv7}{rgb}{1.0,1.0,1.0}
107
\definecolor{clv7}{rgb}{1.0,0.0,0.0}
108
\definecolor{cv8}{rgb}{0.0,0.502,0.0}
109
\definecolor{cfv8}{rgb}{1.0,1.0,1.0}
110
\definecolor{clv8}{rgb}{1.0,0.0,0.0}
111
\definecolor{cv9}{rgb}{0.0,0.502,0.0}
112
\definecolor{cfv9}{rgb}{1.0,1.0,1.0}
113
\definecolor{clv9}{rgb}{1.0,0.0,0.0}
114
\definecolor{cv10}{rgb}{0.0,0.502,0.0}
115
\definecolor{cfv10}{rgb}{1.0,1.0,1.0}
116
\definecolor{clv10}{rgb}{1.0,0.0,0.0}
117
\definecolor{cv11}{rgb}{0.0,0.502,0.0}
118
\definecolor{cfv11}{rgb}{1.0,1.0,1.0}
119
\definecolor{clv11}{rgb}{1.0,0.0,0.0}
120
\definecolor{cv12}{rgb}{0.0,0.502,0.0}
121
\definecolor{cfv12}{rgb}{1.0,1.0,1.0}
122
\definecolor{clv12}{rgb}{1.0,0.0,0.0}
123
\definecolor{cv13}{rgb}{0.0,0.502,0.0}
124
\definecolor{cfv13}{rgb}{1.0,1.0,1.0}
125
\definecolor{clv13}{rgb}{1.0,0.0,0.0}
126
\definecolor{cv0v1}{rgb}{0.0,0.502,0.0}
127
\definecolor{cv0v5}{rgb}{0.0,0.502,0.0}
128
\definecolor{cv0v13}{rgb}{0.0,0.502,0.0}
129
\definecolor{cv1v2}{rgb}{0.0,0.502,0.0}
130
\definecolor{cv1v10}{rgb}{0.0,0.502,0.0}
131
\definecolor{cv2v3}{rgb}{0.0,0.502,0.0}
132
\definecolor{cv2v7}{rgb}{0.0,0.502,0.0}
133
\definecolor{cv3v4}{rgb}{0.0,0.502,0.0}
134
\definecolor{cv3v12}{rgb}{0.0,0.502,0.0}
135
\definecolor{cv4v5}{rgb}{0.0,0.502,0.0}
136
\definecolor{cv4v9}{rgb}{0.0,0.502,0.0}
137
\definecolor{cv5v6}{rgb}{0.0,0.502,0.0}
138
\definecolor{cv6v7}{rgb}{0.0,0.502,0.0}
139
\definecolor{cv6v11}{rgb}{0.0,0.502,0.0}
140
\definecolor{cv7v8}{rgb}{0.0,0.502,0.0}
141
\definecolor{cv8v9}{rgb}{0.0,0.502,0.0}
142
\definecolor{cv8v13}{rgb}{0.0,0.502,0.0}
143
\definecolor{cv9v10}{rgb}{0.0,0.502,0.0}
144
\definecolor{cv10v11}{rgb}{0.0,0.502,0.0}
145
\definecolor{cv11v12}{rgb}{0.0,0.502,0.0}
146
\definecolor{cv12v13}{rgb}{0.0,0.502,0.0}
147
%
148
\Vertex[style={minimum size=0.2cm,draw=cv0,fill=cfv0,text=clv0,shape=circle},LabelOut=false,L=\hbox{$0$},x=2.5cm,y=5.0cm]{v0}
149
\Vertex[style={minimum size=0.2cm,draw=cv1,fill=cfv1,text=clv1,shape=circle},LabelOut=false,L=\hbox{$1$},x=1.3874cm,y=4.7524cm]{v1}
150
\Vertex[style={minimum size=0.2cm,draw=cv2,fill=cfv2,text=clv2,shape=circle},LabelOut=false,L=\hbox{$2$},x=0.4952cm,y=4.0587cm]{v2}
151
\Vertex[style={minimum size=0.2cm,draw=cv3,fill=cfv3,text=clv3,shape=circle},LabelOut=false,L=\hbox{$3$},x=0.0cm,y=3.0563cm]{v3}
152
\Vertex[style={minimum size=0.2cm,draw=cv4,fill=cfv4,text=clv4,shape=circle},LabelOut=false,L=\hbox{$4$},x=0.0cm,y=1.9437cm]{v4}
153
\Vertex[style={minimum size=0.2cm,draw=cv5,fill=cfv5,text=clv5,shape=circle},LabelOut=false,L=\hbox{$5$},x=0.4952cm,y=0.9413cm]{v5}
154
\Vertex[style={minimum size=0.2cm,draw=cv6,fill=cfv6,text=clv6,shape=circle},LabelOut=false,L=\hbox{$6$},x=1.3874cm,y=0.2476cm]{v6}
155
\Vertex[style={minimum size=0.2cm,draw=cv7,fill=cfv7,text=clv7,shape=circle},LabelOut=false,L=\hbox{$7$},x=2.5cm,y=0.0cm]{v7}
156
\Vertex[style={minimum size=0.2cm,draw=cv8,fill=cfv8,text=clv8,shape=circle},LabelOut=false,L=\hbox{$8$},x=3.6126cm,y=0.2476cm]{v8}
157
\Vertex[style={minimum size=0.2cm,draw=cv9,fill=cfv9,text=clv9,shape=circle},LabelOut=false,L=\hbox{$9$},x=4.5048cm,y=0.9413cm]{v9}
158
\Vertex[style={minimum size=0.2cm,draw=cv10,fill=cfv10,text=clv10,shape=circle},LabelOut=false,L=\hbox{$10$},x=5.0cm,y=1.9437cm]{v10}
159
\Vertex[style={minimum size=0.2cm,draw=cv11,fill=cfv11,text=clv11,shape=circle},LabelOut=false,L=\hbox{$11$},x=5.0cm,y=3.0563cm]{v11}
160
\Vertex[style={minimum size=0.2cm,draw=cv12,fill=cfv12,text=clv12,shape=circle},LabelOut=false,L=\hbox{$12$},x=4.5048cm,y=4.0587cm]{v12}
161
\Vertex[style={minimum size=0.2cm,draw=cv13,fill=cfv13,text=clv13,shape=circle},LabelOut=false,L=\hbox{$13$},x=3.6126cm,y=4.7524cm]{v13}
162
%
163
\Edge[lw=0.04cm,style={color=cv0v1,},](v0)(v1)
164
\Edge[lw=0.04cm,style={color=cv0v5,},](v0)(v5)
165
\Edge[lw=0.04cm,style={color=cv0v13,},](v0)(v13)
166
\Edge[lw=0.04cm,style={color=cv1v2,},](v1)(v2)
167
\Edge[lw=0.04cm,style={color=cv1v10,},](v1)(v10)
168
\Edge[lw=0.04cm,style={color=cv2v3,},](v2)(v3)
169
\Edge[lw=0.04cm,style={color=cv2v7,},](v2)(v7)
170
\Edge[lw=0.04cm,style={color=cv3v4,},](v3)(v4)
171
\Edge[lw=0.04cm,style={color=cv3v12,},](v3)(v12)
172
\Edge[lw=0.04cm,style={color=cv4v5,},](v4)(v5)
173
\Edge[lw=0.04cm,style={color=cv4v9,},](v4)(v9)
174
\Edge[lw=0.04cm,style={color=cv5v6,},](v5)(v6)
175
\Edge[lw=0.04cm,style={color=cv6v7,},](v6)(v7)
176
\Edge[lw=0.04cm,style={color=cv6v11,},](v6)(v11)
177
\Edge[lw=0.04cm,style={color=cv7v8,},](v7)(v8)
178
\Edge[lw=0.04cm,style={color=cv8v9,},](v8)(v9)
179
\Edge[lw=0.04cm,style={color=cv8v13,},](v8)(v13)
180
\Edge[lw=0.04cm,style={color=cv9v10,},](v9)(v10)
181
\Edge[lw=0.04cm,style={color=cv10v11,},](v10)(v11)
182
\Edge[lw=0.04cm,style={color=cv11v12,},](v11)(v12)
183
\Edge[lw=0.04cm,style={color=cv12v13,},](v12)(v13)
184
%
185
\end{tikzpicture}
186
187
EXAMPLES:
188
189
This example illustrates switching between the built-in styles when using the tkz_graph format. ::
190
191
sage: g = graphs.PetersenGraph()
192
sage: g.set_latex_options(tkz_style = 'Classic')
193
sage: from sage.graphs.graph_latex import check_tkz_graph
194
sage: check_tkz_graph() # random - depends on TeX installation
195
sage: latex(g)
196
\begin{tikzpicture}
197
...
198
\GraphInit[vstyle=Classic]
199
...
200
\end{tikzpicture}
201
sage: opts = g.latex_options()
202
sage: opts
203
LaTeX options for Petersen graph: {'tkz_style': 'Classic'}
204
sage: g.set_latex_options(tkz_style = 'Art')
205
sage: opts.get_option('tkz_style')
206
'Art'
207
sage: opts
208
LaTeX options for Petersen graph: {'tkz_style': 'Art'}
209
sage: latex(g)
210
\begin{tikzpicture}
211
...
212
\GraphInit[vstyle=Art]
213
...
214
\end{tikzpicture}
215
216
This example illustrates using the optional dot2tex module::
217
218
sage: g = graphs.PetersenGraph()
219
sage: g.set_latex_options(format='dot2tex',prog='neato') # optional - dot2tex
220
sage: from sage.graphs.graph_latex import check_tkz_graph
221
sage: check_tkz_graph() # random - depends on TeX installation
222
sage: latex(g) # optional - dot2tex
223
\begin{tikzpicture}[>=latex,line join=bevel,]
224
...
225
\end{tikzpicture}
226
227
Among other things, this supports the flexible ``edge_options`` option
228
(see :meth:`sage.graphs.generic_graph.GenericGraph.graphviz_string`);
229
here we color in red all edges touching the vertex ``0``::
230
231
sage: g = graphs.PetersenGraph()
232
sage: g.set_latex_options(format="dot2tex", edge_options = lambda (u,v,label): {"color": "red"} if u==0 else {})
233
sage: latex(g) # optional - dot2tex
234
\begin{tikzpicture}[>=latex,line join=bevel,]
235
...
236
\end{tikzpicture}
237
238
239
TEST:
240
241
This graph will look horrible, but it illustrates (and tests) a
242
great variety of the possible options available through Sage's
243
interface to the ``tkz-graph`` package. So it is worth viewing
244
this in the notebook to see the effects of various defaults and
245
choices. ::
246
247
sage: var('x y u w')
248
(x, y, u, w)
249
sage: G = Graph(loops=True)
250
sage: for i in range(5):
251
... for j in range(i+1, 5):
252
... G.add_edge((i, j), label=(x^i*y^j).expand())
253
sage: G.add_edge((0,0), label=sin(u))
254
sage: G.add_edge((4,4), label=w^5)
255
sage: G.set_pos(G.layout_circular())
256
sage: G.set_latex_options(
257
... units='in',
258
... graphic_size=(8,8),
259
... margins=(1,2,2,1),
260
... scale=0.5,
261
... vertex_color='0.8',
262
... vertex_colors={1:'aqua', 3:'y', 4:'#0000FF'},
263
... vertex_fill_color='blue',
264
... vertex_fill_colors={1:'green', 3:'b', 4:'#FF00FF'},
265
... vertex_label_color='brown',
266
... vertex_label_colors={0:'g',1:'purple',2:'#007F00'},
267
... vertex_shape='diamond',
268
... vertex_shapes={1:'rectangle', 2:'sphere', 3:'sphere', 4:'circle'},
269
... vertex_size=0.3,
270
... vertex_sizes={0:1.0, 2:0.3, 4:1.0},
271
... vertex_label_placements = {2:(0.6, 180), 4:(0,45)},
272
... edge_color='purple',
273
... edge_colors={(0,2):'g',(3,4):'red'},
274
... edge_fills=True,
275
... edge_fill_color='green',
276
... edge_label_colors={(2,3):'y',(0,4):'blue'},
277
... edge_thickness=0.05,
278
... edge_thicknesses={(3,4):0.2, (0,4):0.02},
279
... edge_labels=True,
280
... edge_label_sloped=True,
281
... edge_label_slopes={(0,3):False, (2,4):False},
282
... edge_label_placement=0.50,
283
... edge_label_placements={(0,4):'above', (2,3):'left', (0,0):'above', (4,4):'below'},
284
... loop_placement=(2.0, 'NO'),
285
... loop_placements={4:(8.0, 'EA')}
286
... )
287
sage: from sage.graphs.graph_latex import check_tkz_graph
288
sage: check_tkz_graph() # random - depends on TeX installation
289
sage: print latex(G)
290
\begin{tikzpicture}
291
%
292
\useasboundingbox (0,0) rectangle (4.0in,4.0in);
293
%
294
\definecolor{cv0}{rgb}{0.8,0.8,0.8}
295
\definecolor{cfv0}{rgb}{0.0,0.0,1.0}
296
\definecolor{clv0}{rgb}{0.0,0.5,0.0}
297
\definecolor{cv1}{rgb}{0.0,1.0,1.0}
298
\definecolor{cfv1}{rgb}{0.0,0.502,0.0}
299
\definecolor{clv1}{rgb}{0.502,0.0,0.502}
300
\definecolor{cv2}{rgb}{0.8,0.8,0.8}
301
\definecolor{cfv2}{rgb}{0.0,0.0,1.0}
302
\definecolor{clv2}{rgb}{0.0,0.498,0.0}
303
\definecolor{cv3}{rgb}{0.75,0.75,0.0}
304
\definecolor{cfv3}{rgb}{0.0,0.0,1.0}
305
\definecolor{clv3}{rgb}{0.6471,0.1647,0.1647}
306
\definecolor{cv4}{rgb}{0.0,0.0,1.0}
307
\definecolor{cfv4}{rgb}{1.0,0.0,1.0}
308
\definecolor{clv4}{rgb}{0.6471,0.1647,0.1647}
309
\definecolor{cv0v0}{rgb}{0.502,0.0,0.502}
310
\definecolor{cfv0v0}{rgb}{0.0,0.502,0.0}
311
\definecolor{clv0v0}{rgb}{0.0,0.0,0.0}
312
\definecolor{cv0v1}{rgb}{0.502,0.0,0.502}
313
\definecolor{cfv0v1}{rgb}{0.0,0.502,0.0}
314
\definecolor{clv0v1}{rgb}{0.0,0.0,0.0}
315
\definecolor{cv0v2}{rgb}{0.0,0.5,0.0}
316
\definecolor{cfv0v2}{rgb}{0.0,0.502,0.0}
317
\definecolor{clv0v2}{rgb}{0.0,0.0,0.0}
318
\definecolor{cv0v3}{rgb}{0.502,0.0,0.502}
319
\definecolor{cfv0v3}{rgb}{0.0,0.502,0.0}
320
\definecolor{clv0v3}{rgb}{0.0,0.0,0.0}
321
\definecolor{cv0v4}{rgb}{0.502,0.0,0.502}
322
\definecolor{cfv0v4}{rgb}{0.0,0.502,0.0}
323
\definecolor{clv0v4}{rgb}{0.0,0.0,1.0}
324
\definecolor{cv1v2}{rgb}{0.502,0.0,0.502}
325
\definecolor{cfv1v2}{rgb}{0.0,0.502,0.0}
326
\definecolor{clv1v2}{rgb}{0.0,0.0,0.0}
327
\definecolor{cv1v3}{rgb}{0.502,0.0,0.502}
328
\definecolor{cfv1v3}{rgb}{0.0,0.502,0.0}
329
\definecolor{clv1v3}{rgb}{0.0,0.0,0.0}
330
\definecolor{cv1v4}{rgb}{0.502,0.0,0.502}
331
\definecolor{cfv1v4}{rgb}{0.0,0.502,0.0}
332
\definecolor{clv1v4}{rgb}{0.0,0.0,0.0}
333
\definecolor{cv2v3}{rgb}{0.502,0.0,0.502}
334
\definecolor{cfv2v3}{rgb}{0.0,0.502,0.0}
335
\definecolor{clv2v3}{rgb}{0.75,0.75,0.0}
336
\definecolor{cv2v4}{rgb}{0.502,0.0,0.502}
337
\definecolor{cfv2v4}{rgb}{0.0,0.502,0.0}
338
\definecolor{clv2v4}{rgb}{0.0,0.0,0.0}
339
\definecolor{cv3v4}{rgb}{1.0,0.0,0.0}
340
\definecolor{cfv3v4}{rgb}{0.0,0.502,0.0}
341
\definecolor{clv3v4}{rgb}{0.0,0.0,0.0}
342
\definecolor{cv4v4}{rgb}{0.502,0.0,0.502}
343
\definecolor{cfv4v4}{rgb}{0.0,0.502,0.0}
344
\definecolor{clv4v4}{rgb}{0.0,0.0,0.0}
345
%
346
\Vertex[style={minimum size=0.5in,draw=cv0,fill=cfv0,text=clv0,shape=diamond},LabelOut=false,L=\hbox{$0$},x=1.75in,y=3.0in]{v0}
347
\Vertex[style={minimum size=0.15in,draw=cv1,fill=cfv1,text=clv1,shape=rectangle},LabelOut=false,L=\hbox{$1$},x=0.5in,y=2.0451in]{v1}
348
\Vertex[style={minimum size=0.15in,draw=cv2,fill=cfv2,text=clv2,shape=circle,shading=ball,line width=0pt,ball color=cv2,},LabelOut=true,Ldist=0.3in,Lpos=180.0,L=\hbox{$2$},x=0.9775in,y=0.5in]{v2}
349
\Vertex[style={minimum size=0.15in,draw=cv3,fill=cfv3,text=clv3,shape=circle,shading=ball,line width=0pt,ball color=cv3,},LabelOut=false,L=\hbox{$3$},x=2.5225in,y=0.5in]{v3}
350
\Vertex[style={minimum size=0.5in,draw=cv4,fill=cfv4,text=clv4,shape=circle},LabelOut=true,Ldist=0.0in,Lpos=45.0,L=\hbox{$4$},x=3.0in,y=2.0451in]{v4}
351
%
352
\Loop[dist=1.0in,dir=NO,style={color=cv0v0,double=cfv0v0},labelstyle={sloped,above,text=clv0v0,},label=\hbox{$\sin\left(u\right)$},](v0)
353
\Edge[lw=0.025in,style={color=cv0v1,double=cfv0v1},labelstyle={sloped,pos=0.5,text=clv0v1,},label=\hbox{$y$},](v0)(v1)
354
\Edge[lw=0.025in,style={color=cv0v2,double=cfv0v2},labelstyle={sloped,pos=0.5,text=clv0v2,},label=\hbox{$y^{2}$},](v0)(v2)
355
\Edge[lw=0.025in,style={color=cv0v3,double=cfv0v3},labelstyle={pos=0.5,text=clv0v3,},label=\hbox{$y^{3}$},](v0)(v3)
356
\Edge[lw=0.01in,style={color=cv0v4,double=cfv0v4},labelstyle={sloped,above,text=clv0v4,},label=\hbox{$y^{4}$},](v0)(v4)
357
\Edge[lw=0.025in,style={color=cv1v2,double=cfv1v2},labelstyle={sloped,pos=0.5,text=clv1v2,},label=\hbox{$x y^{2}$},](v1)(v2)
358
\Edge[lw=0.025in,style={color=cv1v3,double=cfv1v3},labelstyle={sloped,pos=0.5,text=clv1v3,},label=\hbox{$x y^{3}$},](v1)(v3)
359
\Edge[lw=0.025in,style={color=cv1v4,double=cfv1v4},labelstyle={sloped,pos=0.5,text=clv1v4,},label=\hbox{$x y^{4}$},](v1)(v4)
360
\Edge[lw=0.025in,style={color=cv2v3,double=cfv2v3},labelstyle={sloped,left,text=clv2v3,},label=\hbox{$x^{2} y^{3}$},](v2)(v3)
361
\Edge[lw=0.025in,style={color=cv2v4,double=cfv2v4},labelstyle={pos=0.5,text=clv2v4,},label=\hbox{$x^{2} y^{4}$},](v2)(v4)
362
\Edge[lw=0.1in,style={color=cv3v4,double=cfv3v4},labelstyle={sloped,pos=0.5,text=clv3v4,},label=\hbox{$x^{3} y^{4}$},](v3)(v4)
363
\Loop[dist=4.0in,dir=EA,style={color=cv4v4,double=cfv4v4},labelstyle={sloped,below,text=clv4v4,},label=\hbox{$w^{5}$},](v4)
364
%
365
\end{tikzpicture}
366
367
GraphLatex class and functions
368
------------------------------
369
"""
370
#*****************************************************************************
371
# Copyright (C) 2009 Robert Beezer <[email protected]>
372
# Copyright (C) 2009 Fidel Barrera Cruz <[email protected]>
373
#
374
# Distributed under the terms of the GNU General Public License (GPL)
375
# as published by the Free Software Foundation; either version 2 of
376
# the License, or (at your option) any later version.
377
# http://www.gnu.org/licenses/
378
#*****************************************************************************
379
380
from sage.structure.sage_object import SageObject
381
from sage.misc.cachefunc import cached_function
382
from sage.misc.latex import latex
383
384
def check_tkz_graph():
385
r"""
386
Checks if the proper LaTeX
387
packages for the ``tikzpicture`` environment are
388
installed in the user's environment, and issue
389
a warning otherwise.
390
391
The warning is only issued on the first call to this function. So
392
any doctest that illustrates the use of the tkz-graph packages
393
should call this once as having random output to exhaust the
394
warnings before testing output.
395
396
See also :meth:`sage.misc.latex.Latex.check_file`
397
398
TESTS::
399
400
sage: from sage.graphs.graph_latex import check_tkz_graph
401
sage: check_tkz_graph() # random - depends on TeX installation
402
sage: check_tkz_graph() # at least the second time, so no output
403
"""
404
latex.check_file("tikz.sty", """This package is required to render graphs in LaTeX.
405
Visit '...'.
406
""")
407
latex.check_file("tkz-graph.sty", """This package is required to render graphs in LaTeX.
408
Visit 'http://altermundus.com/pages/graph.html'.
409
""")
410
latex.check_file("tkz-berge.sty", """This package is required to render graphs in LaTeX.
411
Visit 'http://altermundus.com/pages/graph.html'.
412
""")
413
414
def have_tkz_graph():
415
r"""
416
Returns ``True`` if the proper LaTeX packages
417
for the ``tikzpicture`` environment are installed in the
418
user's environment, namely tikz, tkz-graph and tkz-berge.
419
420
The result is cached.
421
422
See also :meth:`sage.misc.latex.Latex.has_file`
423
424
TESTS::
425
426
sage: from sage.graphs.graph_latex import have_tkz_graph
427
sage: have_tkz_graph() # random - depends on TeX installation
428
sage: have_tkz_graph() in [True, False]
429
True
430
"""
431
return latex.has_file("tikz.sty") and latex.has_file("tkz-graph.sty") and latex.has_file("tkz-berge.sty")
432
433
@cached_function
434
def setup_latex_preamble():
435
"""
436
Adds appropriate ``\usepackage{...}``, and other instructions to
437
the latex preamble for the packages that are needed for processing
438
graphs(``tikz``, ``tkz-graph``, ``tkz-berge``), if available
439
in the ``LaTeX`` installation.
440
441
See also :meth:`sage.misc.latex.Latex.add_package_to_preamble_if_available`.
442
443
EXAMPLES::
444
445
sage: sage.graphs.graph_latex.setup_latex_preamble()
446
447
TESTS::
448
449
sage: ("\\usepackage{tikz}" in latex.extra_preamble()) == latex.has_file("tikz.sty")
450
True
451
"""
452
latex.add_package_to_preamble_if_available("tikz")
453
latex.add_package_to_preamble_if_available("tkz-graph")
454
latex.add_package_to_preamble_if_available("tkz-berge")
455
if have_tkz_graph():
456
latex.add_to_preamble("\\usetikzlibrary{arrows,shapes}")
457
458
class GraphLatex(SageObject):
459
r"""
460
A class to hold, manipulate and employ options for converting
461
a graph to LaTeX.
462
463
This class serves two purposes. First it holds the values of
464
various options designed to work with the ``tkz-graph``
465
LaTeX package for rendering graphs. As such, a
466
graph that uses this class will hold a reference to it. Second,
467
this class contains the code to convert a graph into the
468
corresponding LaTeX constructs, returning a string.
469
470
EXAMPLES::
471
472
sage: from sage.graphs.graph_latex import GraphLatex
473
sage: opts = GraphLatex(graphs.PetersenGraph())
474
sage: opts
475
LaTeX options for Petersen graph: {}
476
sage: g = graphs.PetersenGraph()
477
sage: opts = g.latex_options()
478
sage: g == loads(dumps(g))
479
True
480
"""
481
482
# These are the "allowed" options for a graph, private to the class,
483
# along with their default value and description
484
# This allows intelligent errors when non-existent options are referenced
485
# Additionally, for each new option added here:
486
# 1. Document values in GraphLatex.set_option() docstring
487
# 2. Describe also in docstring for the sage.graphs.graph_latex module
488
#
489
# TODO: use some standard option handling mechanism
490
# This dictionary could also contain type information (list of admissible values)
491
# and a description
492
# See e.g. @option
493
__graphlatex_options = {
494
'tkz_style': 'Custom',
495
'format': 'tkz_graph',
496
'layout': 'acyclic',
497
'prog': 'dot',
498
'units': 'cm',
499
'scale': 1.0,
500
'graphic_size': (5, 5),
501
'margins': (0,0,0,0),
502
'vertex_color': 'black',
503
'vertex_colors': {},
504
'vertex_fill_color': 'white',
505
'vertex_fill_colors': {},
506
'vertex_shape': 'circle',
507
'vertex_shapes': {},
508
'vertex_size': 1.0,
509
'vertex_sizes': {},
510
'vertex_labels': True,
511
'vertex_labels_math': True,
512
'vertex_label_color': 'black',
513
'vertex_label_colors': {},
514
'vertex_label_placement': 'center',
515
'vertex_label_placements': {},
516
'edge_options': (),
517
'edge_color': 'black',
518
'edge_colors': {},
519
'edge_fills': False,
520
'edge_fill_color': 'black',
521
'edge_fill_colors': {},
522
'edge_thickness': 0.1,
523
'edge_thicknesses': {},
524
'edge_labels': False,
525
'edge_labels_math': True,
526
'edge_label_color': 'black',
527
'edge_label_colors': {},
528
'edge_label_sloped': True,
529
'edge_label_slopes': {},
530
'edge_label_placement': 0.50,
531
'edge_label_placements': {},
532
'loop_placement': (3.0, 'NO'),
533
'loop_placements': {},
534
'color_by_label' : False,
535
}
536
537
def __init__(self, graph, **options):
538
r"""
539
Returns a GraphLatex object, which holds all the parameters needed for
540
creating a LaTeX string that will be rendered as a picture of the graph.
541
542
See :mod:`sage.graphs.graph_latex` for more documentation.
543
544
EXAMPLES::
545
546
sage: from sage.graphs.graph_latex import GraphLatex
547
sage: GraphLatex(graphs.PetersenGraph())
548
LaTeX options for Petersen graph: {}
549
"""
550
self._graph = graph
551
self._options = {}
552
self.set_options(**options)
553
554
def __eq__(self, other):
555
r"""
556
Two :class:`sage.graphs.graph_latex.GraphLatex` objects
557
are equal if their options are equal.
558
559
The graphs they are associated with are ignored in the comparison.
560
561
TESTS::
562
563
sage: from sage.graphs.graph_latex import GraphLatex
564
sage: opts1 = GraphLatex(graphs.PetersenGraph())
565
sage: opts2 = GraphLatex(graphs.CompleteGraph(10))
566
sage: opts1.set_option('tkz_style', 'Art')
567
sage: opts2.set_option('tkz_style', 'Art')
568
sage: opts1 == opts2
569
True
570
sage: opts2.set_option('tkz_style', 'Normal')
571
sage: opts1 == opts2
572
False
573
"""
574
if not(isinstance(other, GraphLatex)):
575
return False
576
else:
577
return self._options == other._options
578
579
def _repr_(self):
580
r"""
581
Returns a string representation of a
582
:class:`sage.graphs.graph_latex.GraphLatex` object
583
which includes the name of the graph and the dictionary
584
of current options.
585
586
EXAMPLES::
587
588
sage: g = graphs.PetersenGraph()
589
sage: opts = g.latex_options()
590
sage: opts.set_option('tkz_style', 'Classic')
591
sage: opts.set_option('vertex_size', 3.6)
592
sage: print opts._repr_()
593
LaTeX options for Petersen graph: {'tkz_style': 'Classic', 'vertex_size': 3.60000000000000}
594
"""
595
return "LaTeX options for %s: %s"%(self._graph, self._options)
596
597
def set_option(self, option_name, option_value = None):
598
r"""
599
Sets, modifies, clears a LaTeX
600
option for controlling the rendering of a graph.
601
602
The possible options are documented here, because ultimately it is this
603
routine that sets the values. However, the
604
:meth:`sage.graphs.generic_graph.GenericGraph.set_latex_options` method
605
is the easiest way to set options, and allows several to be set at once.
606
607
INPUTS:
608
609
- ``option_name`` - a string for a latex option contained in the list
610
``sage.graphs.graph_latex.GraphLatex.__graphlatex_options``. A
611
``ValueError`` is raised if the option is not allowed.
612
613
- ``option_value`` - a value for the option. If omitted, or
614
set to ``None``, the option will use the default value.
615
616
The output can be either handled internally by ``Sage``, or
617
delegated to the external software ``dot2tex`` and
618
``graphviz``. This is controlled by the option 'format':
619
620
- ``format`` -- default: 'tkz_graph' -- either 'dot2tex'
621
or 'tkz_graph'.
622
623
If format is 'dot2tex', then all the LaTeX generation
624
will be delegated to ``dot2tex`` (which must be installed).
625
626
For ``tkz_graph``, the possible option names, and associated
627
values are given below. This first group allows you to set a
628
style for a graph and specify some sizes related to the eventual
629
image. (For more information consult the
630
documentation for the ``tkz-graph`` package.)
631
632
- ``tkz_style`` -- default: 'Custom' -- the name of a pre-defined
633
``tkz-graph`` style such as 'Shade', 'Art', 'Normal', 'Dijkstra',
634
'Welsh', 'Classic', and 'Simple', or the string 'Custom'. Using
635
one of these styles alone will often give a reasonably good
636
drawing with minimal effort. For a custom appearance set this
637
to 'Custom' and use the options described below to override
638
the default values.
639
640
- ``units`` -- default: 'cm' -- a natural unit of measurement
641
used for all dimensions. Possible values are:
642
'in','mm','cm','pt', 'em', 'ex'
643
644
- ``scale`` -- default: '1.0' -- a dimensionless number that
645
multiplies every linear dimension. So you can design at sizes
646
you are accustomed to, then shrink or expand to meet other needs.
647
Though fonts do not scale.
648
649
- ``graphic_size`` -- default: (5,5) -- overall dimensions
650
(width, length) of the bounding box around the entire graphic image
651
652
- ``margins`` -- default: (0,0,0,0) -- portion of graphic given
653
over to a plain border as a tuple of four numbers:
654
(left, right, top, bottom). These are subtracted from the
655
``graphic_size`` to create the area left for the vertices
656
of the graph itself. Note that the processing done by
657
Sage will trim the graphic down to the minimum
658
possible size, removing any border. So this is only useful
659
if you use the latex string in a latex document.
660
661
662
If not using a pre-built style the following options are used, so
663
the following defaults will apply. It is not possible to begin with
664
a pre-built style and modify it (other than editing the latex
665
string by hand after the fact).
666
667
- ``vertex_color`` -- default: 'black' -- a single color
668
to use as the default for outline of vertices. For the
669
``sphere`` shape this color is used for the entire vertex,
670
which is drawn with a 3D shading. Colors must be specified
671
as a string recognized by the matplotlib library:
672
a standard color name like 'red', or a hex string like
673
'#2D87A7', or a single character from the choices
674
'rgbcmykw'. Additionally, a number between 0 and 1
675
will create a grayscale value. These color specifications
676
are consistent throughout the options for a ``tkzpicture``.
677
678
- ``vertex_colors`` -- a dictionary whose keys are vertices
679
of the graph and whose values are colors. These will be used
680
to color the outline of vertices. See the explanation
681
above for the ``vertex_color`` option to see possible values.
682
These values need only be specified for a proper subset of the
683
vertices. Specified values will supersede a default value.
684
685
- ``vertex_fill_color`` -- default: 'white' -- a single color
686
to use as the default for the fill color of vertices. See
687
the explanation above for the ``vertex_color`` option
688
to see possible values. This color is ignored for the
689
``sphere`` vertex shape.
690
691
- ``vertex__fill_colors`` -- a dictionary whose keys are vertices
692
of the graph and whose values are colors. These will be used
693
to fill the interior of vertices. See the explanation
694
above for the ``vertex_color`` option to see possible values.
695
These values need only be specified for a proper subset of the
696
vertices. Specified values will supersede a default value.
697
698
- ``vertex_shape`` -- default: 'circle' -- a string for
699
the shape of the vertices. Allowable values are 'circle',
700
'sphere', 'rectangle', 'diamond'. The sphere shape has
701
a 3D look to its coloring and is uses only one color,
702
that specified by ``vertex_color`` and ``vertex_colors``,
703
which are normally used for the outline of the vertex.
704
705
- ``vertex_shapes`` -- a dictionary whose keys are vertices
706
of the graph and whose values are shapes. See ``vertex_shape``
707
for the allowable possibilities.
708
709
- ``vertex_size``-- default: 1.0 -- the minimum size of a vertex
710
as a number. Vertices will expand to contain their labels if
711
the labels are placed inside the vertices. If you set this
712
value to zero the vertex will be as small as possible
713
(up to tkz-graph's "inner sep" parameter), while still
714
containing labels. However, if labels are not of a uniform
715
size, then the verrices will not be either.
716
717
- ``vertex_sizes`` -- a dictionary of sizes for some of the vertices.
718
719
- ``vertex_labels`` -- default: ``True`` -- a boolean to
720
determine whether or not to display the vertex labels.
721
If ``False`` subsequent options about vertex labels are ignored.
722
723
- ``vertex_labels_math`` -- default: ``True`` -- when true, if a label
724
is a string that begins and ends with dollar signs, then the string
725
will be rendered as a latex string. Otherwise, the label will be
726
automatically subjected to the ``latex()`` method and rendered
727
accordingly. If ``False`` the label is rendered as its textual
728
representation according to the ``_repr`` method. Support for
729
arbitrarily-complicated mathematics is not especially robust.
730
731
- ``vertex_label_color`` -- default: 'black' -- a single color to use
732
as the default for labels of vertices. See the explanation above
733
for the ``vertex_color`` option to see possible values.
734
735
- ``vertex_label_colors`` -- a dictionary whose keys are vertices
736
of the graph and whose values are colors. These will be used
737
for the text of the labels of vertices. See the explanation
738
above for the ``vertex_color`` option to see possible values.
739
These values need only be specified for a proper subset of the
740
vertices. Specified values will supersede a default value.
741
742
- ``vertex_label_placement`` -- default: 'center' -- if 'center'
743
the label is centered in the interior of the vertex and the vertex
744
will expand to contain the label. Giving instead a pair of numbers
745
will place the label exterior to the vertex at a certain distance
746
from the edge, and at an angle to the positive x-axis, similar
747
in spirt to polar coordinates.
748
749
- ``vertex_label_placements`` -- a dictionary of placements
750
indexed by the vertices. See the explanation for
751
``vertex_label_placement`` for the possible values.
752
753
- ``edge_color`` -- default: 'black' -- a single color to use as
754
the default for an edge. See the explanation above for the
755
``vertex_color`` option to see possible values.
756
757
- ``edge_colors`` -- a dictionary whose keys are edges of the
758
graph and whose values are colors. These will be used to
759
color the edges.See the explanation above for the
760
``vertex_color`` option to see possible values. These
761
values need only be specified for a proper subset of the
762
vertices. Specified values will supersede a default value.
763
764
- ``edge_fills`` -- default: ``False`` -- a boolean that
765
determines if an edge has a second color running down
766
the middle. This can be a useful effect for highlighting
767
edge crossings.
768
769
- ``edge_fill_color`` -- default: 'black' -- a single color
770
to use as the default for the fill color of an edge.
771
The boolean switch ``edge_fills`` must be set to True
772
for theis to have an effect. See the explanation above
773
for the ``vertex_color`` option to see possible values.
774
775
- ``edge__fill_colors`` -- a dictionary whose keys are edges
776
of the graph and whose values are colors. See the explanation
777
above for the ``vertex_color`` option to see possible values.
778
These values need only be specified for a proper subset of the
779
vertices. Specified values will supersede a default value.
780
781
- ``edge_thickness`` -- default: 0.1 - a number specifying the
782
width of the edges. Note that tkz-graph does not interpret
783
this number for loops.
784
785
- ``edge_thicknesses`` -- a dictionary of thicknesses for
786
some of the edges of a graph. These values need only
787
be specified for a proper subset of the vertices. Specified
788
values will supersede a default value.
789
790
- ``edge_labels`` -- default: ``False`` -- a boolean that
791
determines if edge labels are shown. If ``False`` subsequent
792
options about edge labels are ignored.
793
794
- ``edge_labels_math`` -- default: ``True`` -- a boolean that
795
controls how edge labels are rendered. Read the explanation
796
for the ``vertex_labels_math`` option, which behaves identically.
797
Support for arbitrarily-complicated mathematics is not
798
especially robust.
799
800
- ``edge_label_color`` -- default: 'black' -- a single color
801
to use as the default for labels of edges. See the explanation
802
above for the ``vertex_color`` option to see possible values.
803
804
- ``edge_label_colors`` -- a dictionary whose keys are edges
805
of the graph and whose values are colors. These will be used
806
for the text of the labels of edges. See the explanation
807
above for the ``vertex_color`` option to see possible values.
808
These values need only be specified for a proper subset of
809
the vertices. Specified values will supersede a default
810
value. Note that labels must be used for this to have any
811
effect, and no care is taken to ensure that label and
812
fill colors work well together.
813
814
- ``edge_label_sloped`` -- default: ``True`` a boolean that
815
specifies how edge labels are place. ``False`` results
816
in a horizontal label, while ``True`` means the label
817
is rotated to follow the direction of the edge it labels.
818
819
- ``edge_label_slopes`` -- a dictionary of booleans, indexed
820
by some subset of the edges. See the ``edge_label_sloped``
821
option for a description of sloped edge labels.
822
823
- ``edge_label_placement`` -- default: 0.50 -- a number between
824
0.0 and 1.0, or one of: 'above', 'below', 'left', 'right'. These
825
adjust the location of an edge label along an edge. A
826
number specifies how far along the edge the label is
827
located. ``left`` and ``right`` are conveniences.
828
``above`` and ``below`` move the label off the edge
829
itself while leaving it near the midpoint of the edge.
830
The default value of ``0.50`` places the label on the
831
midpoint of the edge.
832
833
- ``edge_label_placements`` -- a dictionary of edge placements,
834
indexed by the edges. See the ``edge_label_placement`` option
835
for a description of the allowable values.
836
837
- ``loop_placement`` -- default: (3.0, 'NO') -- a pair,
838
that determines how loops are rendered. the first
839
element of the pair is a distance, which determines
840
how big the loop is and the second element is a string
841
specifying a compass point (North, South, East, West)
842
as one of 'NO','SO','EA','WE'.
843
844
- ``loop_placements`` -- a dictionary of loop placements.
845
See the ``loop_placements`` option for the allowable values.
846
While loops are technically edges, this dictionary is
847
indexed by vertices.
848
849
For the 'dot2tex' format, the possible option names and
850
associated values are given below:
851
852
- ``prog`` -- the program used for the layout. It must be a
853
string corresponding to one of the software of the graphviz
854
suite: 'dot', 'neato', 'twopi', 'circo' or 'fdp'.
855
856
- ``edge_labels`` -- a boolean (default: False). Whether to
857
display the labels on edges.
858
859
- ``edge_colors`` -- a color. Can be used to set a global
860
color to the edge of the graph.
861
862
- ``color_by_label`` - a boolean (default: False). Colors the
863
edges according to their labels
864
865
OUTPUTS:
866
867
There are none. Success happens silently.
868
869
EXAMPLES:
870
871
Set, then modify, then clear the ``tkz_style`` option, and
872
finally show an error for an unrecognized option name::
873
874
sage: g = graphs.PetersenGraph()
875
sage: opts = g.latex_options()
876
sage: opts
877
LaTeX options for Petersen graph: {}
878
sage: opts.set_option('tkz_style', 'Art')
879
sage: opts
880
LaTeX options for Petersen graph: {'tkz_style': 'Art'}
881
sage: opts.set_option('tkz_style', 'Simple')
882
sage: opts
883
LaTeX options for Petersen graph: {'tkz_style': 'Simple'}
884
sage: opts.set_option('tkz_style')
885
sage: opts
886
LaTeX options for Petersen graph: {}
887
sage: opts.set_option('bad_name', 'nonsense')
888
Traceback (most recent call last):
889
...
890
ValueError: bad_name is not a LaTeX option for a graph.
891
892
See :meth:`sage.graphs.generic_graph.GenericGraph.layout_graphviz` for
893
installation instructions for ``graphviz`` and ``dot2tex``. Further
894
more, pgf >= 2.00 should be available inside LaTeX's tree for LaTeX
895
compilation (e.g. when using ``view``). In case your LaTeX distribution
896
does not provide it, here are short instructions:
897
898
- download pgf from http://sourceforge.net/projects/pgf/
899
- unpack it in ``/usr/share/texmf/tex/generic`` (depends on your system)
900
- clean out remaining pgf files from older version
901
- run texhash
902
903
904
TESTS:
905
906
These test all of the options and one example of each allowable
907
proper input. They should all execute silently. ::
908
909
sage: G=Graph()
910
sage: G.add_edge((0,1))
911
sage: opts = G.latex_options()
912
sage: opts.set_option('tkz_style', 'Custom')
913
sage: opts.set_option('tkz_style', 'Art')
914
sage: opts.set_option('format', 'tkz_graph')
915
sage: opts.set_option('layout', 'acyclic')
916
sage: opts.set_option('prog', 'dot')
917
sage: opts.set_option('units', 'cm')
918
sage: opts.set_option('scale', 1.0)
919
sage: opts.set_option('graphic_size', (5, 5))
920
sage: opts.set_option('margins', (0,0,0,0))
921
sage: opts.set_option('vertex_color', 'black')
922
sage: opts.set_option('vertex_colors', {0:'#ABCDEF'})
923
sage: opts.set_option('vertex_fill_color', 'white')
924
sage: opts.set_option('vertex_fill_colors', {0:'c'})
925
sage: opts.set_option('vertex_shape', 'circle')
926
sage: opts.set_option('vertex_shapes', {0:'sphere'})
927
sage: opts.set_option('vertex_size', 1.0)
928
sage: opts.set_option('vertex_sizes', {0:3.4})
929
sage: opts.set_option('vertex_labels', True)
930
sage: opts.set_option('vertex_labels_math', True)
931
sage: opts.set_option('vertex_label_color', 'black')
932
sage: opts.set_option('vertex_label_colors', {0:'.23'})
933
sage: opts.set_option('vertex_label_placement', 'center')
934
sage: opts.set_option('vertex_label_placement', (3, 4.2))
935
sage: opts.set_option('vertex_label_placements', {0:'center'})
936
sage: opts.set_option('vertex_label_placements', {0:(4.7,1)})
937
sage: opts.set_option('edge_color', 'black')
938
sage: opts.set_option('edge_colors', {(0,1):'w'})
939
sage: opts.set_option('edge_fills', False)
940
sage: opts.set_option('edge_fill_color', 'black')
941
sage: opts.set_option('edge_fill_colors', {(0,1):"#123456"})
942
sage: opts.set_option('edge_thickness', 0.1)
943
sage: opts.set_option('edge_thicknesses', {(0,1):5.2})
944
sage: opts.set_option('edge_labels', False)
945
sage: opts.set_option('edge_labels_math', True)
946
sage: opts.set_option('edge_label_color', 'black')
947
sage: opts.set_option('edge_label_colors', {(0,1):'red'})
948
sage: opts.set_option('edge_label_sloped', True)
949
sage: opts.set_option('edge_label_slopes', {(0,1): False})
950
sage: opts.set_option('edge_label_placement', 'left')
951
sage: opts.set_option('edge_label_placement', 0.50)
952
sage: opts.set_option('edge_label_placements', {(0,1):'above'})
953
sage: opts.set_option('edge_label_placements', {(0,1):0.75})
954
sage: opts.set_option('loop_placement', (3.0, 'NO'))
955
sage: opts.set_option('loop_placements', {0:(5.7,'WE')})
956
957
These test some of the logic of possible failures. Some tests,
958
such as inputs of colors, are handled by somewhat general sections
959
of code and are not tested for each possible option. ::
960
961
sage: G=Graph()
962
sage: G.add_edge((0,1))
963
sage: opts = G.latex_options()
964
sage: opts.set_option('tkz_style', 'Crazed')
965
Traceback (most recent call last):
966
...
967
ValueError: tkz_style is not "Custom", nor an implemented tkz-graph style
968
sage: opts.set_option('format', 'NonExistent')
969
Traceback (most recent call last):
970
...
971
ValueError: format option must be one of: tkz_graph, dot2tex not NonExistent
972
sage: opts.set_option('units', 'furlongs')
973
Traceback (most recent call last):
974
...
975
ValueError: units option must be one of: in, mm, cm, pt, em, ex, not furlongs
976
sage: opts.set_option('graphic_size', (1,2,3))
977
Traceback (most recent call last):
978
...
979
ValueError: graphic_size option must be an ordered pair, not (1, 2, 3)
980
sage: opts.set_option('margins', (1,2,3))
981
Traceback (most recent call last):
982
...
983
ValueError: margins option must be 4-tuple, not (1, 2, 3)
984
sage: opts.set_option('vertex_color', 'chartruse')
985
Traceback (most recent call last):
986
...
987
ValueError: vertex_color option needs to be a matplotlib color (always as a string), not chartruse
988
sage: opts.set_option('vertex_labels_math', 'maybe')
989
Traceback (most recent call last):
990
...
991
ValueError: vertex_labels_math option must be True or False, not maybe
992
sage: opts.set_option('vertex_shape', 'decagon')
993
Traceback (most recent call last):
994
...
995
ValueError: vertex_shape option must be the shape of a vertex, not decagon
996
sage: opts.set_option('scale', 'big')
997
Traceback (most recent call last):
998
...
999
ValueError: scale option must be a positive number, not big
1000
sage: opts.set_option('scale', -6)
1001
Traceback (most recent call last):
1002
...
1003
ValueError: scale option must be a positive number, not -6
1004
sage: opts.set_option('vertex_label_placement', (2,-4))
1005
Traceback (most recent call last):
1006
...
1007
ValueError: vertex_label_placement option must be None, or a pair of positive numbers, not (2, -4)
1008
sage: opts.set_option('edge_label_placement', 3.6)
1009
Traceback (most recent call last):
1010
...
1011
ValueError: edge_label_placement option must be a number between 0.0 and 1.0 or a place (like "above"), not 3.60000000000000
1012
sage: opts.set_option('loop_placement', (5,'SW'))
1013
Traceback (most recent call last):
1014
...
1015
ValueError: loop_placement option must be a pair that is a positive number followed by a compass point abbreviation, not (5, 'SW')
1016
sage: opts.set_option('vertex_fill_colors', {0:'#GG0000'})
1017
Traceback (most recent call last):
1018
...
1019
ValueError: vertex_fill_colors option for 0 needs to be a matplotlib color (always as a string), not #GG0000
1020
sage: opts.set_option('vertex_sizes', {0:-10})
1021
Traceback (most recent call last):
1022
...
1023
ValueError: vertex_sizes option for 0 needs to be a positive number, not -10
1024
sage: opts.set_option('edge_label_slopes', {(0,1):'possibly'})
1025
Traceback (most recent call last):
1026
...
1027
ValueError: edge_label_slopes option for (0, 1) needs to be True or False, not possibly
1028
sage: opts.set_option('vertex_shapes', {0:'pentagon'})
1029
Traceback (most recent call last):
1030
...
1031
ValueError: vertex_shapes option for 0 needs to be a vertex shape, not pentagon
1032
sage: opts.set_option('vertex_label_placements', {0:(1,2,3)})
1033
Traceback (most recent call last):
1034
...
1035
ValueError: vertex_label_placements option for 0 needs to be None or a pair of positive numbers, not (1, 2, 3)
1036
sage: opts.set_option('edge_label_placements', {(0,1):'partway'})
1037
Traceback (most recent call last):
1038
...
1039
ValueError: edge_label_placements option for (0, 1) needs to be a number between 0.0 and 1.0 or a place (like "above"), not partway
1040
sage: opts.set_option('loop_placements', {0:(-3,'WE')})
1041
Traceback (most recent call last):
1042
...
1043
ValueError: loop_placements option for 0 needs to be a positive number and a compass point (like "EA"), not (-3, 'WE')
1044
sage: opts.set_option('margins', (1,2,3,-5))
1045
Traceback (most recent call last):
1046
...
1047
ValueError: margins option of (1, 2, 3, -5) cannot contain -5
1048
"""
1049
#TODO: Needed improvements, possible extensions, dubious ideas
1050
#- digraph edges should be optionally curved or straight with
1051
#perhaps a variable curvature (exit angle from vertex). Always
1052
#curved now to allow for bidirectional.
1053
#- the "draw" option will make boxes around labels as
1054
#extensions of the edge color and thickness
1055
#- edge labels can have colored backgrounds (which look like
1056
#fills when boxed.
1057
#- edge label fonts can be sized (latex style), which will
1058
#make scaling work totally
1059
#- edges can be dotted or dashed, Beezer suggests calling
1060
#this "edge shape" to mirror vertex shapes
1061
#- "line width" works for vertices, should be configurable
1062
#- allow injection of latex code to style a pre-built style
1063
#for example, \SetUpVertex[style={fill=green}] could overide
1064
#color selection in a style like "Art"
1065
#- "inner sep" is distance from vertex label to edge of vertex
1066
#this should be set as small as possible - but bigger than the
1067
#line width.
1068
#- aspect ratio could be preserved, see hints near
1069
#creation of affine transformation.
1070
#- "outer sep" causes edges to stop some distance before
1071
#reaching vertices. Seems of limited value.
1072
#- Multi-edges are not supported. Need to recognize them,
1073
#twiddle keys in dictionaries, plot with a spectrum of bends.
1074
#Seems like a substantial project.
1075
1076
from matplotlib.colors import ColorConverter
1077
from sage.rings.integer import Integer
1078
from sage.rings.real_mpfr import RealLiteral
1079
1080
1081
cc = ColorConverter() # used as a color tester
1082
1083
if not(option_name in GraphLatex.__graphlatex_options):
1084
raise ValueError( "%s is not a LaTeX option for a graph." % option_name )
1085
if option_value == None: # clear the option, if set
1086
if option_name in self._options:
1087
del self._options[option_name]
1088
else:
1089
# Test options here when attempt to set
1090
name = option_name; value = option_value
1091
#
1092
# Tuples of constants
1093
#
1094
formats = ('tkz_graph', 'dot2tex')
1095
styles = ('Custom', 'Shade', 'Art', 'Normal', 'Dijkstra', 'Welsh', 'Classic', 'Simple')
1096
unit_names = ('in','mm','cm','pt', 'em', 'ex')
1097
shape_names = ('circle', 'sphere','rectangle', 'diamond')
1098
label_places = ('above', 'below', 'right', 'left')
1099
compass_points = ('NO', 'SO', 'EA', 'WE')
1100
number_types = (int, Integer, float, RealLiteral)
1101
#
1102
# Options with structurally similar tests
1103
#
1104
boolean_options = ('vertex_labels','vertex_labels_math','edge_fills','edge_labels','edge_labels_math','edge_label_sloped')
1105
color_options = ('vertex_color', 'vertex_fill_color', 'vertex_label_color','edge_color','edge_fill_color','edge_label_color')
1106
color_dicts = ('vertex_colors','vertex_fill_colors','vertex_label_colors','edge_colors','edge_fill_colors','edge_label_colors')
1107
boolean_dicts = ('edge_label_slopes',)
1108
positive_scalars = ('scale', 'vertex_size', 'edge_thickness')
1109
positive_scalar_dicts=('vertex_sizes', 'edge_thicknesses')
1110
positive_tuples=('graphic_size', 'margins')
1111
#
1112
# Checks/test on single values (ie graph-wide defaults)
1113
#
1114
if name == 'tkz_style' and not( value in styles ):
1115
raise ValueError('%s is not "Custom", nor an implemented tkz-graph style' % name)
1116
elif name == 'format' and not( value in formats ):
1117
raise ValueError('%s option must be one of: tkz_graph, dot2tex not %s' % (name, value))
1118
elif name == 'units' and not( value in unit_names ):
1119
raise ValueError('%s option must be one of: in, mm, cm, pt, em, ex, not %s' % (name, value))
1120
elif name == 'graphic_size' and not( type(value) == tuple and (len(value) == 2) ):
1121
raise ValueError( '%s option must be an ordered pair, not %s' % (name, value))
1122
elif name == 'margins' and not( (type(value) == tuple) and (len(value) == 4) ):
1123
raise ValueError( '%s option must be 4-tuple, not %s' % (name, value))
1124
elif name in color_options:
1125
try:
1126
cc.to_rgb(value)
1127
except Exception:
1128
raise ValueError('%s option needs to be a matplotlib color (always as a string), not %s' % (name, value))
1129
elif name in boolean_options and not type(value) == bool:
1130
raise ValueError('%s option must be True or False, not %s' % (name, value))
1131
elif name == 'vertex_shape' and not value in shape_names:
1132
raise ValueError('%s option must be the shape of a vertex, not %s' % (name, value))
1133
elif name in positive_scalars and not ( type(value) in number_types and (value >= 0.0) ):
1134
raise ValueError( '%s option must be a positive number, not %s' % (name, value))
1135
elif name == 'vertex_label_placement' and not( value == 'center') and not( type(value) == tuple and len(value) == 2 and type(value[0]) in number_types and value[0]>=0 and type(value[1]) in number_types and value[1]>=0 ):
1136
raise ValueError( '%s option must be None, or a pair of positive numbers, not %s' % (name, value))
1137
elif name == 'edge_label_placement' and not( ((type(value) in number_types) and (0<= value) and (value <= 1)) or (value in label_places)):
1138
raise ValueError( '%s option must be a number between 0.0 and 1.0 or a place (like "above"), not %s' % (name, value))
1139
elif name == 'loop_placement' and not( (type(value) == tuple) and (len(value) == 2) and (value[0] >=0) and (value[1] in compass_points) ):
1140
raise ValueError( '%s option must be a pair that is a positive number followed by a compass point abbreviation, not %s' % (name, value))
1141
#
1142
# Checks/test on dictionaries of values (ie per-vertex or per-edge defaults)
1143
#
1144
elif name in color_dicts:
1145
if not type(value) == dict:
1146
raise TypeError('%s option must be a dictionary, not %s' (name, value))
1147
else:
1148
for key, c in value.items():
1149
try:
1150
cc.to_rgb(c)
1151
except Exception:
1152
raise ValueError('%s option for %s needs to be a matplotlib color (always as a string), not %s' % (name, key, c))
1153
elif name in positive_scalar_dicts:
1154
if not type(value) == dict:
1155
raise TypeError('%s option must be a dictionary, not %s' (name, value))
1156
else:
1157
for key, x in value.items():
1158
if not type(x) in [int, Integer, float, RealLiteral] or not x >= 0.0:
1159
raise ValueError('%s option for %s needs to be a positive number, not %s' % (name, key, x))
1160
elif name in boolean_dicts:
1161
if not type(value) == dict:
1162
raise TypeError('%s option must be a dictionary, not %s' (name, value))
1163
else:
1164
for key, b in value.items():
1165
if not type(b) == bool:
1166
raise ValueError('%s option for %s needs to be True or False, not %s' % (name, key, b))
1167
elif name == 'vertex_shapes':
1168
if not type(value) == dict:
1169
raise TypeError('%s option must be a dictionary, not %s' (name, value))
1170
else:
1171
for key, s in value.items():
1172
if not s in shape_names:
1173
raise ValueError('%s option for %s needs to be a vertex shape, not %s' % (name, key, s))
1174
elif name == 'vertex_label_placements':
1175
if not type(value) == dict:
1176
raise TypeError('%s option must be a dictionary, not %s' (name, value))
1177
else:
1178
for key, p in value.items():
1179
if not( p == 'center') and not( type(p) == tuple and len(p) == 2 and type(p[0]) in number_types and p[0]>=0 and type(p[1]) in number_types and p[1]>=0 ):
1180
raise ValueError('%s option for %s needs to be None or a pair of positive numbers, not %s' % (name, key, p))
1181
elif name == 'edge_label_placements':
1182
if not type(value) == dict:
1183
raise TypeError('%s option must be a dictionary, not %s' (name, value))
1184
else:
1185
for key, p in value.items():
1186
if not(type(p) in [float, RealLiteral] and (0 <= p) and (p <= 1)) and not(p in label_places):
1187
raise ValueError('%s option for %s needs to be a number between 0.0 and 1.0 or a place (like "above"), not %s' % (name, key, p))
1188
elif name == 'loop_placements':
1189
if not type(value) == dict:
1190
raise TypeError('%s option must be a dictionary, not %s' (name, value))
1191
else:
1192
for key, p in value.items():
1193
if not( (type(p) == tuple) and (len(p)==2) and (p[0] >=0) and (p[1] in compass_points) ):
1194
raise ValueError('%s option for %s needs to be a positive number and a compass point (like "EA"), not %s' % (name, key, p))
1195
# These have been verified as tuples before going into this next check
1196
elif name in positive_tuples:
1197
for x in value:
1198
if not type(x) in [int, Integer, float, RealLiteral] or not x >= 0.0:
1199
raise ValueError( '%s option of %s cannot contain %s' % (name, value, x))
1200
#
1201
# Verified. Set it.
1202
self._options[option_name] = option_value
1203
1204
1205
def set_options(self, **kwds):
1206
r"""
1207
Set several LaTeX options for a graph all at once.
1208
1209
INPUTS:
1210
1211
- kwds - any number of option/value pairs to se many graph latex
1212
options at once (a variable number, in any order). Existing
1213
values are overwritten, new values are added. Existing
1214
values can be cleared by setting the value to ``None``.
1215
Errors are raised in the :func:`set_option` method.
1216
1217
EXAMPLES::
1218
1219
sage: g = graphs.PetersenGraph()
1220
sage: opts = g.latex_options()
1221
sage: opts.set_options(tkz_style = 'Welsh')
1222
sage: opts.get_option('tkz_style')
1223
'Welsh'
1224
"""
1225
if kwds:
1226
for name, value in kwds.items():
1227
self.set_option(name, value)
1228
1229
def get_option(self, option_name):
1230
r"""
1231
Returns the current value of the named option.
1232
1233
INPUT:
1234
1235
- option_name - the name of an option
1236
1237
OUTPUT:
1238
1239
If the name is not present in
1240
``__graphlatex_options`` it is an
1241
error to ask for it. If an option has not been set then the
1242
default value is returned. Otherwise, the value of the
1243
option is returned.
1244
1245
EXAMPLES::
1246
1247
sage: g = graphs.PetersenGraph()
1248
sage: opts = g.latex_options()
1249
sage: opts.set_option('tkz_style', 'Art')
1250
sage: opts.get_option('tkz_style')
1251
'Art'
1252
sage: opts.set_option('tkz_style')
1253
sage: opts.get_option('tkz_style') == "Custom"
1254
True
1255
sage: opts.get_option('bad_name')
1256
Traceback (most recent call last):
1257
...
1258
ValueError: bad_name is not a Latex option for a graph.
1259
"""
1260
if not(option_name in GraphLatex.__graphlatex_options):
1261
raise ValueError( "%s is not a Latex option for a graph." % option_name )
1262
else:
1263
if option_name in self._options:
1264
return self._options[option_name]
1265
else:
1266
return GraphLatex.__graphlatex_options[option_name]
1267
1268
def latex(self):
1269
r"""
1270
Returns a string in LaTeX representing a graph.
1271
1272
This is the command that is invoked by
1273
``sage.graphs.generic_graph.GenericGraph._latex_`` for a graph, so
1274
it returns a string of LaTeX commands that can be incorporated into a
1275
LaTeX document unmodified. The exact contents of this string are
1276
influenced by the options set via the methods
1277
:meth:`sage.graphs.generic_graph.GenericGraph.set_latex_options`,
1278
:meth:`set_option`, and :meth:`set_options`.
1279
1280
By setting the ``format`` option different packages can be used to
1281
create the latex version of a graph. Supported packages are
1282
``tkz-graph`` and ``dot2tex``.
1283
1284
EXAMPLES::
1285
1286
sage: from sage.graphs.graph_latex import check_tkz_graph
1287
sage: check_tkz_graph() # random - depends on TeX installation
1288
sage: g = graphs.CompleteGraph(2)
1289
sage: opts = g.latex_options()
1290
sage: print opts.latex()
1291
\begin{tikzpicture}
1292
%
1293
\useasboundingbox (0,0) rectangle (5.0cm,5.0cm);
1294
%
1295
\definecolor{cv0}{rgb}{0.0,0.0,0.0}
1296
\definecolor{cfv0}{rgb}{1.0,1.0,1.0}
1297
\definecolor{clv0}{rgb}{0.0,0.0,0.0}
1298
\definecolor{cv1}{rgb}{0.0,0.0,0.0}
1299
\definecolor{cfv1}{rgb}{1.0,1.0,1.0}
1300
\definecolor{clv1}{rgb}{0.0,0.0,0.0}
1301
\definecolor{cv0v1}{rgb}{0.0,0.0,0.0}
1302
%
1303
\Vertex[style={minimum size=1.0cm,draw=cv0,fill=cfv0,text=clv0,shape=circle},LabelOut=false,L=\hbox{$0$},x=5.0cm,y=5.0cm]{v0}
1304
\Vertex[style={minimum size=1.0cm,draw=cv1,fill=cfv1,text=clv1,shape=circle},LabelOut=false,L=\hbox{$1$},x=0.0cm,y=0.0cm]{v1}
1305
%
1306
\Edge[lw=0.1cm,style={color=cv0v1,},](v0)(v1)
1307
%
1308
\end{tikzpicture}
1309
"""
1310
format = self.get_option('format')
1311
if format == "tkz_graph":
1312
return self.tkz_picture()
1313
elif format == "dot2tex":
1314
return self.dot2tex_picture()
1315
1316
def dot2tex_picture(self):
1317
r"""
1318
Calls dot2tex to construct a string of LaTeX commands
1319
representing a graph as a ``tikzpicture``.
1320
1321
EXAMPLES::
1322
1323
sage: g = digraphs.ButterflyGraph(1)
1324
sage: from sage.graphs.graph_latex import check_tkz_graph
1325
sage: check_tkz_graph() # random - depends on TeX installation
1326
sage: print g.latex_options().dot2tex_picture() # optional - dot2tex graphviz
1327
\begin{tikzpicture}[>=latex,line join=bevel,]
1328
%%
1329
\node (0+1) at (...bp,...bp) [draw,draw=none] {$\left(\text{0}, 1\right)$};
1330
\node (0+0) at (...bp,...bp) [draw,draw=none] {$\left(\text{0}, 0\right)$};
1331
\node (1+0) at (...bp,...bp) [draw,draw=none] {$\left(\text{1}, 0\right)$};
1332
\node (1+1) at (...bp,...bp) [draw,draw=none] {$\left(\text{1}, 1\right)$};
1333
\draw [->] (1+0) ..controls (...bp,...bp) and (...bp,...bp) .. (0+1);
1334
\draw [->] (0+0) ..controls (...bp,...bp) and (...bp,...bp) .. (0+1);
1335
\draw [->] (0+0) ..controls (...bp,...bp) and (...bp,...bp) .. (1+1);
1336
\draw [->] (1+0) ..controls (...bp,...bp) and (...bp,...bp) .. (1+1);
1337
%
1338
\end{tikzpicture}
1339
1340
1341
Note: there is a lot of overlap between what tkz_picture and
1342
dot2tex do. It would be best to merge them! dot2tex probably
1343
can work without graphviz if layout information is provided.
1344
"""
1345
from sage.graphs.dot2tex_utils import assert_have_dot2tex
1346
assert_have_dot2tex()
1347
1348
options = self.__graphlatex_options.copy()
1349
options.update(self._options)
1350
dotdata = self._graph.graphviz_string(labels="latex", **options)
1351
import dot2tex
1352
return dot2tex.dot2tex(
1353
dotdata,
1354
format = 'tikz',
1355
autosize = True,
1356
crop = True,
1357
figonly = 'True',
1358
prog=self.get_option('prog'))
1359
# usepdflatex = True, debug = True)
1360
1361
def tkz_picture(self):
1362
r"""
1363
Return a string of LaTeX commands representing a graph as a ``tikzpicture``.
1364
1365
This routine interprets the graph's properties and the options in
1366
``_options`` to render the graph with commands from the ``tkz-graph``
1367
LaTeX package.
1368
1369
This requires that the LaTeX optional packages
1370
tkz-graph and tkz-berge be installed. You may also need a
1371
current version of the pgf package. If the tkz-graph and
1372
tkz-berge packages are present in the system's TeX
1373
installation, the appropriate ``\\usepackage{}`` commands
1374
will be added to the LaTeX preamble as part of
1375
the initialization of the graph. If these two packages
1376
are not present, then this command will return a warning
1377
on its first use, but will return a string that could be
1378
used elsewhere, such as a LaTeX document.
1379
1380
For more information about tkz-graph you can visit
1381
`Altermundus.com <http://altermundus.com/>`_
1382
1383
EXAMPLES:
1384
1385
With a pre-built ``tkz-graph`` style specified, the latex
1386
representation will be relatively simple. ::
1387
1388
sage: from sage.graphs.graph_latex import check_tkz_graph
1389
sage: check_tkz_graph() # random - depends on TeX installation
1390
sage: g = graphs.CompleteGraph(3)
1391
sage: opts = g.latex_options()
1392
sage: g.set_latex_options(tkz_style='Art')
1393
sage: print opts.tkz_picture()
1394
\begin{tikzpicture}
1395
%
1396
\GraphInit[vstyle=Art]
1397
%
1398
\useasboundingbox (0,0) rectangle (5.0cm,5.0cm);
1399
%
1400
\Vertex[L=\hbox{$0$},x=2.5cm,y=5.0cm]{v0}
1401
\Vertex[L=\hbox{$1$},x=0.0cm,y=0.0cm]{v1}
1402
\Vertex[L=\hbox{$2$},x=5.0cm,y=0.0cm]{v2}
1403
%
1404
\Edge[](v0)(v1)
1405
\Edge[](v0)(v2)
1406
\Edge[](v1)(v2)
1407
%
1408
\end{tikzpicture}
1409
1410
Setting the style to "Custom" results in various configurable
1411
aspects set to the defaults, so the string is more involved. ::
1412
1413
sage: from sage.graphs.graph_latex import check_tkz_graph
1414
sage: check_tkz_graph() # random - depends on TeX installation
1415
sage: g = graphs.CompleteGraph(3)
1416
sage: opts = g.latex_options()
1417
sage: g.set_latex_options(tkz_style='Custom')
1418
sage: print opts.tkz_picture()
1419
\begin{tikzpicture}
1420
%
1421
\useasboundingbox (0,0) rectangle (5.0cm,5.0cm);
1422
%
1423
\definecolor{cv0}{rgb}{0.0,0.0,0.0}
1424
\definecolor{cfv0}{rgb}{1.0,1.0,1.0}
1425
\definecolor{clv0}{rgb}{0.0,0.0,0.0}
1426
\definecolor{cv1}{rgb}{0.0,0.0,0.0}
1427
\definecolor{cfv1}{rgb}{1.0,1.0,1.0}
1428
\definecolor{clv1}{rgb}{0.0,0.0,0.0}
1429
\definecolor{cv2}{rgb}{0.0,0.0,0.0}
1430
\definecolor{cfv2}{rgb}{1.0,1.0,1.0}
1431
\definecolor{clv2}{rgb}{0.0,0.0,0.0}
1432
\definecolor{cv0v1}{rgb}{0.0,0.0,0.0}
1433
\definecolor{cv0v2}{rgb}{0.0,0.0,0.0}
1434
\definecolor{cv1v2}{rgb}{0.0,0.0,0.0}
1435
%
1436
\Vertex[style={minimum size=1.0cm,draw=cv0,fill=cfv0,text=clv0,shape=circle},LabelOut=false,L=\hbox{$0$},x=2.5cm,y=5.0cm]{v0}
1437
\Vertex[style={minimum size=1.0cm,draw=cv1,fill=cfv1,text=clv1,shape=circle},LabelOut=false,L=\hbox{$1$},x=0.0cm,y=0.0cm]{v1}
1438
\Vertex[style={minimum size=1.0cm,draw=cv2,fill=cfv2,text=clv2,shape=circle},LabelOut=false,L=\hbox{$2$},x=5.0cm,y=0.0cm]{v2}
1439
%
1440
\Edge[lw=0.1cm,style={color=cv0v1,},](v0)(v1)
1441
\Edge[lw=0.1cm,style={color=cv0v2,},](v0)(v2)
1442
\Edge[lw=0.1cm,style={color=cv1v2,},](v1)(v2)
1443
%
1444
\end{tikzpicture}
1445
1446
See the introduction to the :mod:`~sage.graphs.graph_latex` module
1447
for more information on the use of this routine.
1448
1449
TESTS:
1450
1451
Graphs with preset layouts that are vertical or horizontal
1452
can cause problems. First test is a horizontal layout on a
1453
path with three vertices. ::
1454
1455
sage: from sage.graphs.graph_latex import check_tkz_graph
1456
sage: check_tkz_graph() # random - depends on TeX installation
1457
sage: g = graphs.PathGraph(3)
1458
sage: opts = g.latex_options()
1459
sage: print opts.tkz_picture()
1460
\begin{tikzpicture}
1461
...
1462
\end{tikzpicture}
1463
1464
Scaling to a bounding box is problematic for graphs with
1465
just one vertex, or none. ::
1466
1467
sage: from sage.graphs.graph_latex import check_tkz_graph
1468
sage: check_tkz_graph() # random - depends on TeX installation
1469
sage: g = graphs.CompleteGraph(1)
1470
sage: opts = g.latex_options()
1471
sage: print opts.tkz_picture()
1472
\begin{tikzpicture}
1473
...
1474
\end{tikzpicture}
1475
"""
1476
1477
# This routine does not handle multiple edges
1478
# It will properly handle digraphs where a pair of vertices
1479
# has an edge in each direction, since edges of a digraph are
1480
# curved.
1481
if self._graph.has_multiple_edges():
1482
raise NotImplementedError('it is not possible create a tkz-graph version of a graph with multiple edges')
1483
1484
from matplotlib.colors import ColorConverter
1485
from sage.misc.latex import latex
1486
from sage.rings.real_mpfr import RealLiteral # remove?
1487
import copy
1488
1489
# On first use of this method, the next call may print warnings
1490
# as a side effect, but will be silent on any subsequent use.
1491
check_tkz_graph()
1492
1493
# Overhead
1494
cc = ColorConverter() # .to_rgb method to convert "colors" to triples
1495
prefix = 'v' # leading string on internal (to tkz-graph) vertex names
1496
1497
####################
1498
### Pre-built syles
1499
####################
1500
1501
# We preserve the pre-built style OR
1502
# get defaults for each option, but we do not mix the two
1503
style = self.get_option('tkz_style')
1504
customized = (style == 'Custom')
1505
# We don't do much for a pre-built style
1506
# Layout information from the graph
1507
# And vertex labels (if used) are the latex representation of Sage objects
1508
if not customized:
1509
vertex_labels_math = True
1510
1511
###################################
1512
### Layout, image sizing placement
1513
###################################
1514
1515
units = self.get_option('units')
1516
scale = self.get_option('scale')
1517
graphic_size = self.get_option('graphic_size')
1518
margins = self.get_option('margins')
1519
1520
# The positions of the vertices will get scaled to fill the
1521
# specified size of the image, as given by graphic_size.
1522
# But first a border is subtracted away and the graph
1523
# is scaled to fit there.
1524
1525
# Lower left, upper right corners of box inside borders
1526
llx = margins[0]; lly = margins[3]
1527
urx = graphic_size[0]-margins[1]; ury = graphic_size[1]-margins[2]
1528
# width and height of space
1529
w = urx - llx; h = ury - lly
1530
1531
# TODO: Could use self._graph._layout_bounding_box(pos)
1532
# trans = lambda x,y: [x[0]-y[0],x[1]-y[1]]
1533
# Determine the spread in the x and y directions (i.e. xmax, ymax)
1534
# Needs care for perfectly horizontal and vertical layouts
1535
1536
# We grab the graph's layout (or it is computed as a consequence of the request)
1537
pos = self._graph.layout()
1538
1539
if len(pos.values()) > 0:
1540
xmin = min([ i[0] for i in pos.values()])
1541
ymin = min([ i[1] for i in pos.values()])
1542
xmax = max([ i[0] for i in pos.values()])
1543
ymax = max([ i[1] for i in pos.values()])
1544
else:
1545
xmax, ymax = 0, 0
1546
1547
# Linear scaling factors that will be used to scale the image to
1548
# fit into the bordered region. Purely horizontal, or purely vertical,
1549
# layouts get put in the middle of the bounding box by setting the
1550
# scaling to a constant value on a midline
1551
xspread = xmax - xmin
1552
if xspread == 0:
1553
x_scale = 0.0
1554
llx = llx + 0.5*w
1555
else:
1556
x_scale = float(w)/xspread
1557
yspread = ymax - ymin
1558
if yspread == 0:
1559
y_scale = 0.0
1560
lly = lly + 0.5*h
1561
else:
1562
y_scale = float(h)/yspread
1563
# Could preserve aspect ratio here by setting both scale factors to the minimum
1564
# and doing a shift of the larger to center
1565
# A linear function will map layout positions into the bordered graphic space
1566
translate = lambda p: ((p[0]-xmin)*x_scale+llx, (p[1]-ymin)*y_scale+lly)
1567
1568
1569
# The positions of the vertices will get scaled to fill the
1570
# specified size of the image, as given by graphic_size.
1571
# But first a border is subtracted away and the graph
1572
# is scaled to fit there.
1573
1574
# Lower left, upper right corners of box inside borders
1575
llx = margins[0]; lly = margins[3]
1576
urx = graphic_size[0]-margins[1]; ury = graphic_size[1]-margins[2]
1577
# width and height of space
1578
w = urx - llx; h = ury - lly
1579
1580
# TODO: Could use self._graph._layout_bounding_box(pos)
1581
# trans = lambda x,y: [x[0]-y[0],x[1]-y[1]]
1582
# Determine the spread in the x and y directions (i.e. xmax, ymax)
1583
# Needs care for perfectly horizontal and vertical layouts
1584
### pos = copy.deepcopy(self._graph.layout(layout = layout, labels = "latex"))
1585
pos = self._graph.layout()
1586
if len(pos.values()) > 0:
1587
xmin = min([ i[0] for i in pos.values()])
1588
ymin = min([ i[1] for i in pos.values()])
1589
xmax = max([ i[0] for i in pos.values()])
1590
ymax = max([ i[1] for i in pos.values()])
1591
else:
1592
xmax, ymax = 0, 0
1593
1594
# Linear scaling factors that will be used to scale the image to
1595
# fit into the bordered region. Purely horizontal, or purely vertical,
1596
# layouts get put in the middle of the bounding box by setting the
1597
# scaling to a constant value on a midline
1598
xspread = xmax - xmin
1599
if xspread == 0:
1600
x_scale = 0.0
1601
llx = llx + 0.5*w
1602
else:
1603
x_scale = float(w)/xspread
1604
yspread = ymax - ymin
1605
if yspread == 0:
1606
y_scale = 0.0
1607
lly = lly + 0.5*h
1608
else:
1609
y_scale = float(h)/yspread
1610
1611
# Could preserve aspect ratio here by setting both scale factors to the minimum
1612
# and doing a shift of the larger to center
1613
# A linear function will map layout positions into the bordered graphic space
1614
translate = lambda p: ((p[0]-xmin)*x_scale+llx, (p[1]-ymin)*y_scale+lly)
1615
1616
#############
1617
### Vertices
1618
#############
1619
1620
# We record the index of each vertex in the graph's list of vertices
1621
# Which is just a convenience for forming vertex names internal to tkz-graph
1622
index_of_vertex={}
1623
vertex_list = self._graph.vertices()
1624
for u in self._graph:
1625
index_of_vertex[u]=vertex_list.index(u)
1626
1627
# Vertex labels can be switched on/off, and we don't record
1628
# or use this type of extra information if they are switched off
1629
vertex_labels = self.get_option('vertex_labels')
1630
1631
# We collect options for vertices, default values and and for-some-vertices information
1632
# These are combined into dictionaries on a per-vertex basis, for all vertices
1633
# This only applies for a custom style
1634
#
1635
# Defaults
1636
#
1637
if customized:
1638
dvc = cc.to_rgb(self.get_option('vertex_color'))
1639
dvfc = cc.to_rgb(self.get_option('vertex_fill_color'))
1640
dsh = self.get_option( 'vertex_shape' )
1641
dvs = self.get_option('vertex_size')
1642
#
1643
# Default label information, if using vertex labels
1644
#
1645
if vertex_labels:
1646
vertex_labels_math = self.get_option('vertex_labels_math')
1647
dvlc = cc.to_rgb(self.get_option('vertex_label_color'))
1648
dvlp = self.get_option('vertex_label_placement')
1649
# needs test for a pair of numbers, angle and distance (or None)
1650
1651
# Retrieve dictionaries for selected vertices
1652
vertex_colors = self.get_option('vertex_colors')
1653
vertex_fill_colors = self.get_option('vertex_fill_colors')
1654
vertex_shapes = self.get_option('vertex_shapes')
1655
vertex_sizes = self.get_option('vertex_sizes')
1656
if vertex_labels:
1657
vertex_label_colors = self.get_option('vertex_label_colors')
1658
vertex_label_placements = self.get_option('vertex_label_placements')
1659
1660
# Form dictionaries, each indexed for all vertices
1661
v_color = {}
1662
vf_color = {}
1663
v_shape = {}
1664
v_size = {}
1665
if vertex_labels:
1666
vl_color = {}
1667
vl_placement = {}
1668
for u in vertex_list:
1669
#
1670
c = dvc
1671
if vertex_colors.has_key(u):
1672
c = cc.to_rgb(vertex_colors[u])
1673
v_color[ u ] = c
1674
#
1675
c = dvfc
1676
if vertex_fill_colors.has_key(u):
1677
c = cc.to_rgb(vertex_fill_colors[u])
1678
vf_color[u] = c
1679
#
1680
sh = dsh
1681
if vertex_shapes.has_key(u):
1682
sh = vertex_shapes[u]
1683
v_shape[u] = sh
1684
#
1685
vs = dvs
1686
if vertex_sizes.has_key(u):
1687
vs = vertex_sizes[u]
1688
v_size[u] = vs
1689
#
1690
if vertex_labels:
1691
#
1692
c = dvlc
1693
if vertex_label_colors.has_key(u):
1694
c = cc.to_rgb(vertex_label_colors[u])
1695
vl_color[u] = c
1696
#
1697
vlp = dvlp
1698
if vertex_label_placements.has_key(u):
1699
vlp = vertex_label_placements[u]
1700
# test vlp here
1701
vl_placement[u] = vlp
1702
1703
##########
1704
### Edges
1705
##########
1706
1707
if customized:
1708
# An "edge fill" is a bit unusual, so we allow it to
1709
# be turned off as the default.
1710
edge_fills = self.get_option('edge_fills')
1711
1712
# Edge labels can be switched on/off, and we don't record
1713
# or use this type of extra information if they are switched off
1714
edge_labels = self.get_option('edge_labels')
1715
1716
# We collect options for edges, default values and for-some-edges information
1717
# These are combined into dictionaries on a per-edge basis, for all edges
1718
#
1719
# Defaults
1720
#
1721
dec = cc.to_rgb(self.get_option('edge_color'))
1722
if edge_fills:
1723
defc = cc.to_rgb(self.get_option('edge_fill_color'))
1724
det = self.get_option('edge_thickness')
1725
#
1726
if edge_labels:
1727
edge_labels_math = self.get_option('edge_labels_math')
1728
delc = cc.to_rgb(self.get_option('edge_label_color'))
1729
dels = self.get_option('edge_label_sloped')
1730
delp = self.get_option('edge_label_placement')
1731
1732
# Retrieve dictionaries for selected edges
1733
edge_colors = self.get_option('edge_colors')
1734
if edge_fills:
1735
edge_fill_colors = self.get_option('edge_fill_colors')
1736
edge_thicknesses = self.get_option('edge_thicknesses')
1737
if edge_labels:
1738
edge_label_colors = self.get_option('edge_label_colors')
1739
edge_label_slopes = self.get_option('edge_label_slopes')
1740
edge_label_placements = self.get_option('edge_label_placements')
1741
1742
# Form dictionaries, each indexed for all edges
1743
#
1744
# A key of a dictionary indexed by edges may be
1745
# set for an edge of an undirected
1746
# graph in the "wrong" order, so we use a
1747
# "reverse" to test for this case. Everything formed
1748
# here conforms to the order used in the graph.
1749
#
1750
e_color = {}
1751
if edge_fills:
1752
ef_color = {}
1753
e_thick = {}
1754
if edge_labels:
1755
el_color = {}
1756
el_slope={}
1757
el_placement={}
1758
1759
for e in self._graph.edges():
1760
edge=(e[0],e[1]); reverse=(e[1],e[0])
1761
#
1762
c = dec
1763
if edge_colors.has_key(edge) or (not self._graph.is_directed() and edge_colors.has_key(reverse)):
1764
if edge_colors.has_key(edge):
1765
c = cc.to_rgb(edge_colors[edge])
1766
else:
1767
c = cc.to_rgb(edge_colors[reverse])
1768
e_color[edge] = c
1769
#
1770
if edge_fills:
1771
c = defc
1772
if edge_fill_colors.has_key(edge) or (not self._graph.is_directed() and edge_fill_colors.has_key(reverse)):
1773
if edge_colors.has_key(edge):
1774
c = cc.to_rgb(edge_fill_colors[edge])
1775
else:
1776
c = cc.to_rgb(edge_fill_colors[reverse])
1777
ef_color[edge] = c
1778
#
1779
et = det
1780
if edge_thicknesses.has_key(edge) or (not self._graph.is_directed() and edge_thicknesses.has_key(reverse)):
1781
if edge_thicknesses.has_key(edge):
1782
et = edge_thicknesses[edge]
1783
else:
1784
et = edge_thicknesses[reverse]
1785
e_thick[edge] = et
1786
#
1787
if edge_labels:
1788
c = delc
1789
if edge_label_colors.has_key(edge) or (not self._graph.is_directed() and edge_label_colors.has_key(reverse)):
1790
if edge_label_colors.has_key(edge):
1791
c = cc.to_rgb(edge_label_colors[edge])
1792
else:
1793
c = cc.to_rgb(edge_label_colors[reverse])
1794
el_color[edge] = c
1795
#
1796
els = dels
1797
if edge_label_slopes.has_key(edge) or (not self._graph.is_directed() and edge_label_slopes.has_key(reverse)):
1798
if edge_label_slopes.has_key(edge):
1799
els = edge_label_slopes[edge]
1800
else:
1801
els = edge_label_slopes[reverse]
1802
el_slope[edge] = els
1803
#
1804
elp = delp
1805
if edge_label_placements.has_key(edge) or (not self._graph.is_directed() and edge_label_placements.has_key(reverse)):
1806
if edge_label_placements.has_key(edge):
1807
elp = edge_label_placements[edge]
1808
else:
1809
elp = edge_label_placements[reverse]
1810
el_placement[edge] = elp
1811
1812
##########
1813
### Loops
1814
##########
1815
1816
# Loops can be styled much like any other edge
1817
# By indexing on a pair of two equal vertices
1818
# Though edge thickness is not implemented in tkz-graph!
1819
# Size and direction are unique, and are indexed by the vertex
1820
# rather than on edges.
1821
1822
# Loop placements are pairs of length, compass-point
1823
if customized:
1824
if self._graph.has_loops():
1825
dlp = self.get_option('loop_placement')
1826
loop_placements = self.get_option('loop_placements')
1827
lp_placement = {}
1828
for u in vertex_list:
1829
lp = dlp
1830
if loop_placements.has_key(u):
1831
lp = loop_placements[u]
1832
lp_placement[u] = lp
1833
1834
1835
############################
1836
### Build the output string
1837
############################
1838
1839
# s is the eventual tkz string
1840
# Everything should now be in place
1841
# We build a list and then concatenate it as the return value
1842
s = ['\\begin{tikzpicture}\n%\n']
1843
1844
if not customized:
1845
s+=['\\GraphInit[vstyle=', style, ']\n%\n']
1846
1847
# Specify the bounding box for the latex result
1848
# If too big, then the latex paper size may need to be expanded
1849
s+=['\\useasboundingbox (0,0) rectangle (', str(round(scale*graphic_size[0],4)), units, ',', str(round(scale*graphic_size[1],4)), units, ');\n%\n']
1850
1851
# Internal strings representing colors are defined here in custom style
1852
if customized:
1853
# Define all the colors for the vertices: perimeter, fill, label
1854
vertex_color_names = {}
1855
vertex_fill_color_names = {}
1856
vertex_label_color_names = {}
1857
for u in vertex_list:
1858
vertex_color_names[ u ] = 'c' + prefix + str(index_of_vertex[ u ])
1859
s+=['\definecolor{', vertex_color_names[ u ], '}{rgb}', '{']
1860
s+=[str(round( v_color[u][0],4)), ',']
1861
s+=[str(round( v_color[u][1],4)), ',']
1862
s+=[str(round( v_color[u][2],4)), '}\n']
1863
vertex_fill_color_names[ u ] = 'cf' + prefix + str(index_of_vertex[ u ])
1864
s+=['\definecolor{', vertex_fill_color_names[ u ], '}{rgb}', '{']
1865
s+=[str(round( vf_color[u][0],4)), ',']
1866
s+=[str(round( vf_color[u][1],4)), ',']
1867
s+=[str(round( vf_color[u][2],4)), '}\n']
1868
if vertex_labels:
1869
vertex_label_color_names[u] = 'cl' + prefix + str(index_of_vertex[ u ])
1870
s+=['\definecolor{', vertex_label_color_names[ u ], '}{rgb}{']
1871
s+=[str(round( vl_color[u][0],4)), ',']
1872
s+=[str(round( vl_color[u][1],4)), ',']
1873
s+=[str(round( vl_color[u][2],4)), '}\n']
1874
# Define all the colors for the edges: perimeter, fill, label
1875
edge_color_names = {}
1876
edge_fill_color_names = {}
1877
edge_label_color_names = {}
1878
for e in self._graph.edges():
1879
edge = (e[0], e[1])
1880
edge_color_names[edge] = 'c' + prefix + str(index_of_vertex[edge[0]])+ prefix + str(index_of_vertex[edge[1]])
1881
s+=['\definecolor{', edge_color_names[edge], '}{rgb}{']
1882
s+=[str(round( e_color[edge][0],4)), ',']
1883
s+=[str(round( e_color[edge][1],4)), ',']
1884
s+=[str(round( e_color[edge][2],4)), '}\n']
1885
if edge_fills:
1886
edge_fill_color_names[edge] = 'cf' + prefix + str(index_of_vertex[edge[0]])+ prefix + str(index_of_vertex[edge[1]])
1887
s+=['\definecolor{', edge_fill_color_names[edge], '}{rgb}{']
1888
s+=[str(round( ef_color[edge][0],4)), ',']
1889
s+=[str(round( ef_color[edge][1],4)), ',']
1890
s+=[str(round( ef_color[edge][2],4)), '}\n']
1891
if edge_labels:
1892
edge_label_color_names[edge] = 'cl' + prefix + str(index_of_vertex[edge[0]])+ prefix + str(index_of_vertex[edge[1]])
1893
s+=['\definecolor{', edge_label_color_names[edge], '}{rgb}{']
1894
s+=[str(round( el_color[edge][0],4)), ',']
1895
s+=[str(round( el_color[edge][1],4)), ',']
1896
s+=[str(round( el_color[edge][2],4)), '}\n']
1897
s = s+['%\n']
1898
1899
# Create each vertex
1900
for u in vertex_list:
1901
s+=['\\Vertex[']
1902
# colors, shapes, sizes, labels/placement for 'Custom' style
1903
if customized:
1904
s+=['style={'] # begin style list
1905
s+=['minimum size=', str(round(scale*v_size[u],4)), units, ',']
1906
s+=['draw=', vertex_color_names[u], ',']
1907
s+=['fill=', vertex_fill_color_names[u], ',']
1908
if vertex_labels:
1909
s+=['text=', vertex_label_color_names[u], ',']
1910
if v_shape[u] == 'sphere':
1911
s+=['shape=circle,shading=ball,line width=0pt,ball color=', vertex_color_names[u], ',']
1912
else:
1913
s+=['shape=', v_shape[u]]
1914
s+=['},'] # end style list
1915
if vertex_labels:
1916
if vl_placement[u] == 'center':
1917
s+=['LabelOut=false,']
1918
else:
1919
s+=['LabelOut=true,']
1920
s+=['Ldist=', str(round(scale*vl_placement[u][0],4)), units, ',']
1921
s+=['Lpos=',str(round(vl_placement[u][1],4)), ','] # degrees, no units
1922
else:
1923
s+=['NoLabel,']
1924
# vertex label information is available to all pre-built styles
1925
# but may be ignored by the style, so not apparent
1926
if vertex_labels or not customized:
1927
if vertex_labels_math and not (type(u)==str and u[0]=='$' and u[-1]=='$'):
1928
lab = '\hbox{$%s$}' % latex(u)
1929
else:
1930
lab = '\hbox{%s}' % u
1931
s+=['L=', lab, ',']
1932
scaled_pos = translate(pos[u])
1933
s+=['x=', str(round(scale*scaled_pos[0],4)), units, ',']
1934
s+=['y=', str(round(scale*scaled_pos[1],4)), units]
1935
s+=[']']
1936
s+=['{', prefix, str(index_of_vertex[u]), '}\n']
1937
s+=['%\n']
1938
1939
# Create each edge or loop
1940
for e in self._graph.edges():
1941
edge = (e[0],e[1])
1942
loop = e[0] == e[1]
1943
if loop:
1944
u=e[0]
1945
s+=['\\Loop[']
1946
if customized:
1947
s+=['dist=', str(round(scale*lp_placement[u][0],4)), units, ',']
1948
s+=['dir=', lp_placement[u][1], ',']
1949
else:
1950
s+=['\\Edge[']
1951
# colors, shapes, sizes, labels/placement for 'Custom' style
1952
if customized:
1953
if not loop: # lw not available for loops!
1954
s+=['lw=', str(round(scale*e_thick[edge],4)), units, ',']
1955
s+=['style={'] # begin style list
1956
if self._graph.is_directed() and not loop:
1957
s+=['post, bend right', ',']
1958
s+=['color=', edge_color_names[edge], ',']
1959
if edge_fills:
1960
s+=['double=', edge_fill_color_names[edge]]
1961
s+=['},'] # end style list
1962
if edge_labels:
1963
s+=['labelstyle={']
1964
if el_slope[edge]:
1965
s+=['sloped,']
1966
if type(el_placement[edge]) == str:
1967
s+=[el_placement[edge],',']
1968
else:
1969
s+=['pos=', str(round(el_placement[edge],4)), ','] # no units needed
1970
s+=['text=', edge_label_color_names[edge], ',']
1971
s+=['},']
1972
el = self._graph.edge_label(edge[0],edge[1])
1973
if edge_labels_math and not (type(el)==str and el[0]=='$' and el[-1]=='$'):
1974
lab = '\hbox{$%s$}' % latex(el)
1975
else:
1976
lab = '\hbox{%s}' % el
1977
s+=['label=', lab, ',']
1978
s+=[']']
1979
if not loop:
1980
s+=['(', prefix, str(index_of_vertex[e[0]]), ')']
1981
s+=['(', prefix, str(index_of_vertex[e[1]]), ')\n']
1982
1983
# Wrap it up
1984
s+=['%\n']
1985
s+=['\\end{tikzpicture}']
1986
1987
return ''.join(s)
1988
1989