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