diff --git a/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst b/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst index 9fb6be2700e..a0c98ea3836 100644 --- a/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst +++ b/src/doc/en/thematic_tutorials/geometry/polytope_tikz.rst @@ -15,8 +15,9 @@ paper. TikZ is a very versatile tool to draw in scientific documents and Sage can deal easily with 3-dimensional polytopes. Finally sagetex makes everything work together nicely between Sage, TikZ and LaTeX. Since version 6.3 of Sage, there is a function for (projection -of) polytopes to output a TikZ picture of the polytope. This short -tutorial shows how it all works. +of) polytopes to output a TikZ picture of the polytope. Since version 9.7 of +SageMath, the tikz output can be a ``TikzPicture`` object from the sage module +``sage.misc.latex_standalone``. This short tutorial shows how it all works. Instructions """""""""""" @@ -30,21 +31,23 @@ To put an image of a 3D-polytope in LaTeX using TikZ and Sage, simply follow the - Visualize the polytope P using the command ``P.show(aspect_ratio=1)`` - This will open an interactive view in your default browser, where you can rotate the polytope. - Once the desired view angle is found, click on the information icon in the lower right-hand corner and select *Get Viewpoint*. This will copy a string of the form '[x,y,z],angle' to your local clipboard. -- Go back to Sage and type ``Img = P.tikz([x,y,z],angle)``. You can paste the string here to save some typing. -- *Img* now contains a Sage object of type ``LatexExpr`` containing the raw TikZ picture of your polytope +- Go back to Sage and type ``Img = P.tikz([x,y,z],angle,output_type='LatexExpr')``. You can paste the string here to save some typing. +- *Img* now contains a Sage object of type ``LatexExpr`` containing the raw TikZ picture of your polytope. -Then, you can either copy-paste it to your article by typing ``Img`` in Sage or save it to a file, by doing +Alternatively, you can save the tikz image to a file, by doing .. CODE-BLOCK:: python - f = open('Img_poly.tex','w') - f.write(Img) - f.close() + Img = P.tikz([x,y,z], angle, output_type='TikzPicture') + Img.tex('Img_poly.tex') + Img.tex('Img_poly.tex', content_only=True) + Img.pdf('Img_poly.pdf') .. end of output Then in the pwd (present working directory of sage, the one of your article) -you will have a file named ``Img_poly.tex`` containing the tikzpicture of your polytope. +you will have two files named ``Img_poly.tex`` and ``Img_poly.pdf`` containing the +tikzpicture of the polytope ``P``. Customization """"""""""""" @@ -57,6 +60,9 @@ You can customize the polytope using the following options in the command ``P.ti - ``vertex_color`` : string (default: ``green``) representing colors which tikz recognize, - ``opacity`` : real number (default: ``0.8``) between 0 and 1 giving the opacity of the front facets, - ``axis`` : Boolean (default: ``False``) draw the axes at the origin or not. +- ``output_type`` : string (default: ``None``) ``None``, ``'LatexExpr'`` or + ``'TikzPicture'``, the type of the output. Since SageMath 9.7, the value ``None`` is deprecated + as the default value will soon be changed from ``'LatexExpr'`` to ``'TikzPicture'``. Examples """""""" @@ -80,15 +86,20 @@ When you found a good angle, follow the above procedure to obtain the values :: - Img = P.tikz([674,108,-731],112) + Img = P.tikz([674,108,-731], 112, output_type='TikzPicture') .. end of output +Note: the ``output_type='TikzPicture'`` is necessary since SagMath 9.7 to avoid +a deprecation warning message since the default output type will soon change +from a ``LatexExpr`` (Python str) to a ``TikzPicture`` object (allowing more +versatility, like being able to view it directly in the Jupyter notebook). + Or you may want to customize using the command :: - Img = P.tikz([674,108,-731],112,scale=2, edge_color='orange',facet_color='red',vertex_color='blue',opacity=0.4) + Img = P.tikz([674,108,-731],112,scale=2, edge_color='orange',facet_color='red',vertex_color='blue',opacity=0.4, output_type='TikzPicture') .. end of output @@ -134,7 +145,7 @@ some possibilities. .. CODE-BLOCK:: latex - \sagestr{(polytopes.permutahedron(4)).tikz([4,5,6],45,scale=0.75, facet_color='red',vertex_color='yellow',opacity=0.3)} + \sagestr{(polytopes.permutahedron(4)).tikz([4,5,6],45,scale=0.75, facet_color='red',vertex_color='yellow',opacity=0.3, output_type='LatexExpr')} .. end of output @@ -142,8 +153,8 @@ some possibilities. .. CODE-BLOCK:: latex - \newcommand{\polytopeimg}[4]{\sagestr{(#1).tikz(#2,#3,#4)}} - \newcommand{\polytopeimgopt}[9]{\sagestr{(#1).tikz(#2,#3,#4,#5,#6,#7,#8,#9)}} + \newcommand{\polytopeimg}[4]{\sagestr{(#1).tikz(#2,#3,#4,output_type='LatexExpr')}} + \newcommand{\polytopeimgopt}[9]{\sagestr{(#1).tikz(#2,#3,#4,#5,#6,#7,#8,#9,output_type='LatexExpr')}} .. end of output diff --git a/src/doc/en/thematic_tutorials/geometry/visualization.rst b/src/doc/en/thematic_tutorials/geometry/visualization.rst index ac7412e665c..438b6cff4c8 100644 --- a/src/doc/en/thematic_tutorials/geometry/visualization.rst +++ b/src/doc/en/thematic_tutorials/geometry/visualization.rst @@ -113,11 +113,22 @@ This method returns a tikz picture of the polytope (must be 2 or :: sage: c = polytopes.cube() - sage: c.tikz().splitlines()[:5] - ['\\begin{tikzpicture}%', - '\t[x={(1.000000cm, 0.000000cm)},', - '\ty={(-0.000000cm, 1.000000cm)},', - '\tz={(0.000000cm, -0.000000cm)},', - '\tscale=1.000000,'] + sage: c.tikz(output_type='TikzPicture') + \documentclass[tikz]{standalone} + \begin{document} + \begin{tikzpicture}% + [x={(1.000000cm, 0.000000cm)}, + y={(-0.000000cm, 1.000000cm)}, + z={(0.000000cm, -0.000000cm)}, + scale=1.000000, + ... + Use print to see the full content. + ... + \node[vertex] at (-1.00000, -1.00000, 1.00000) {}; + \node[vertex] at (-1.00000, 1.00000, 1.00000) {}; + %% + %% + \end{tikzpicture} + \end{document} .. end of output diff --git a/src/sage/geometry/polyhedron/base6.py b/src/sage/geometry/polyhedron/base6.py index 3257c27fe0a..ac1bde55b2b 100644 --- a/src/sage/geometry/polyhedron/base6.py +++ b/src/sage/geometry/polyhedron/base6.py @@ -48,7 +48,10 @@ class Polyhedron_base6(Polyhedron_base5): sage: P = polytopes.cube() sage: Polyhedron_base6.plot(P) Graphics3d Object - sage: Polyhedron_base6.tikz(P) + sage: print(Polyhedron_base6.tikz(P, output_type='TikzPicture')) + \RequirePackage{luatex85} + \documentclass[tikz]{standalone} + \begin{document} \begin{tikzpicture}% [x={(1.000000cm, 0.000000cm)}, y={(-0.000000cm, 1.000000cm)}, @@ -125,6 +128,7 @@ class Polyhedron_base6(Polyhedron_base5): %% %% \end{tikzpicture} + \end{document} sage: Q = polytopes.hypercube(4) sage: Polyhedron_base6.show(Q) @@ -473,9 +477,11 @@ def show(self, **kwds): def tikz(self, view=[0, 0, 1], angle=0, scale=1, edge_color='blue!95!black', facet_color='blue!95!black', - opacity=0.8, vertex_color='green', axis=False): + opacity=0.8, vertex_color='green', axis=False, + output_type=None): r""" - Return a string ``tikz_pic`` consisting of a tikz picture of ``self`` + Return a tikz picture of ``self`` as a string or as a + :class:`~sage.misc.latex_standalone.TikzPicture` according to a projection ``view`` and an angle ``angle`` obtained via the threejs viewer. ``self`` must be bounded. @@ -494,10 +500,15 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, - ``opacity`` - real number (default: 0.8) between 0 and 1 giving the opacity of the front facets. - ``axis`` - Boolean (default: False) draw the axes at the origin or not. + - ``output_type`` - string (default: ``None``), valid values + are ``None`` (deprecated), ``'LatexExpr'`` and ``'TikzPicture'``, + whether to return a LatexExpr object (which inherits from Python + str) or a ``TikzPicture`` object from module + :mod:`sage.misc.latex_standalone` OUTPUT: - - LatexExpr -- containing the TikZ picture. + - LatexExpr object or TikzPicture object .. NOTE:: @@ -535,18 +546,25 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, EXAMPLES:: sage: co = polytopes.cuboctahedron() - sage: Img = co.tikz([0,0,1], 0) - sage: print('\n'.join(Img.splitlines()[:9])) + sage: Img = co.tikz([0,0,1], 0, output_type='TikzPicture') + sage: Img + \documentclass[tikz]{standalone} + \begin{document} \begin{tikzpicture}% - [x={(1.000000cm, 0.000000cm)}, - y={(0.000000cm, 1.000000cm)}, - z={(0.000000cm, 0.000000cm)}, - scale=1.000000, - back/.style={loosely dotted, thin}, - edge/.style={color=blue!95!black, thick}, - facet/.style={fill=blue!95!black,fill opacity=0.800000}, - vertex/.style={inner sep=1pt,circle,draw=green!25!black,fill=green!75!black,thick}] - sage: print('\n'.join(Img.splitlines()[12:21])) + [x={(1.000000cm, 0.000000cm)}, + y={(0.000000cm, 1.000000cm)}, + z={(0.000000cm, 0.000000cm)}, + scale=1.000000, + ... + Use print to see the full content. + ... + \node[vertex] at (1.00000, 0.00000, 1.00000) {}; + \node[vertex] at (1.00000, 1.00000, 0.00000) {}; + %% + %% + \end{tikzpicture} + \end{document} + sage: print('\n'.join(Img.content().splitlines()[12:21])) %% with the command: ._tikz_3d_in_3d and parameters: %% view = [0, 0, 1] %% angle = 0 @@ -556,15 +574,40 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, %% opacity = 0.8 %% vertex_color = green %% axis = False - sage: print('\n'.join(Img.splitlines()[22:26])) + sage: print('\n'.join(Img.content().splitlines()[22:26])) %% Coordinate of the vertices: %% \coordinate (-1.00000, -1.00000, 0.00000) at (-1.00000, -1.00000, 0.00000); \coordinate (-1.00000, 0.00000, -1.00000) at (-1.00000, 0.00000, -1.00000); + + When output type is a :class:`sage.misc.latex_standalone.TikzPicture`:: + + sage: co = polytopes.cuboctahedron() + sage: t = co.tikz([674,108,-731], 112, output_type='TikzPicture') + sage: t + \documentclass[tikz]{standalone} + \begin{document} + \begin{tikzpicture}% + [x={(0.249656cm, -0.577639cm)}, + y={(0.777700cm, -0.358578cm)}, + z={(-0.576936cm, -0.733318cm)}, + scale=1.000000, + ... + Use print to see the full content. + ... + \node[vertex] at (1.00000, 0.00000, 1.00000) {}; + \node[vertex] at (1.00000, 1.00000, 0.00000) {}; + %% + %% + \end{tikzpicture} + \end{document} + sage: path_to_file = t.pdf() # not tested + """ return self.projection().tikz(view, angle, scale, edge_color, facet_color, - opacity, vertex_color, axis) + opacity, vertex_color, axis, + output_type=output_type) def _rich_repr_(self, display_manager, **kwds): r""" diff --git a/src/sage/geometry/polyhedron/plot.py b/src/sage/geometry/polyhedron/plot.py index 2c959b61195..6e40f119c1e 100644 --- a/src/sage/geometry/polyhedron/plot.py +++ b/src/sage/geometry/polyhedron/plot.py @@ -1228,11 +1228,13 @@ def render_3d(self, point_opts=None, line_opts=None, polygon_opts=None): def tikz(self, view=[0, 0, 1], angle=0, scale=1, edge_color='blue!95!black', facet_color='blue!95!black', - opacity=0.8, vertex_color='green', axis=False): + opacity=0.8, vertex_color='green', axis=False, + output_type=None): r""" - Return a string ``tikz_pic`` consisting of a tikz picture of ``self`` + Return a tikz picture of ``self`` as a string or as a + :class:`~sage.misc.latex_standalone.TikzPicture` according to a projection ``view`` and an angle ``angle`` - obtained via Jmol through the current state property. + obtained via the threejs viewer. INPUT: @@ -1249,10 +1251,15 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, - ``opacity`` - real number (default: 0.8) between 0 and 1 giving the opacity of the front facets. - ``axis`` - Boolean (default: False) draw the axes at the origin or not. + - ``output_type`` - string (default: ``None``), valid values + are ``None`` (deprecated), ``'LatexExpr'`` and ``'TikzPicture'``, + whether to return a LatexExpr object (which inherits from Python + str) or a ``TikzPicture`` object from module + :mod:`sage.misc.latex_standalone` OUTPUT: - - LatexExpr -- containing the TikZ picture. + - LatexExpr object or TikzPicture object .. NOTE:: @@ -1285,19 +1292,56 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, EXAMPLES:: sage: P1 = polytopes.small_rhombicuboctahedron() - sage: Image1 = P1.projection().tikz([1,3,5], 175, scale=4) + sage: Image1 = P1.projection().tikz([1,3,5], 175, scale=4, output_type='TikzPicture') sage: type(Image1) - - sage: print('\n'.join(Image1.splitlines()[:4])) + + sage: Image1 + \documentclass[tikz]{standalone} + \begin{document} \begin{tikzpicture}% - [x={(-0.939161cm, 0.244762cm)}, - y={(0.097442cm, -0.482887cm)}, - z={(0.329367cm, 0.840780cm)}, - sage: with open('polytope-tikz1.tex', 'w') as f: # not tested - ....: _ = f.write(Image1) + [x={(-0.939161cm, 0.244762cm)}, + y={(0.097442cm, -0.482887cm)}, + z={(0.329367cm, 0.840780cm)}, + scale=4.000000, + ... + Use print to see the full content. + ... + \node[vertex] at (-2.41421, 1.00000, -1.00000) {}; + \node[vertex] at (-2.41421, -1.00000, 1.00000) {}; + %% + %% + \end{tikzpicture} + \end{document} + sage: _ = Image1.tex('polytope-tikz1.tex') # not tested + sage: _ = Image1.png('polytope-tikz1.png') # not tested + sage: _ = Image1.pdf('polytope-tikz1.pdf') # not tested + sage: _ = Image1.svg('polytope-tikz1.svg') # not tested + + A second example:: sage: P2 = Polyhedron(vertices=[[1, 1],[1, 2],[2, 1]]) - sage: Image2 = P2.projection().tikz(scale=3, edge_color='blue!95!black', facet_color='orange!95!black', opacity=0.4, vertex_color='yellow', axis=True) + sage: Image2 = P2.projection().tikz(scale=3, edge_color='blue!95!black', facet_color='orange!95!black', opacity=0.4, vertex_color='yellow', axis=True, output_type='TikzPicture') + sage: Image2 + \documentclass[tikz]{standalone} + \begin{document} + \begin{tikzpicture}% + [scale=3.000000, + back/.style={loosely dotted, thin}, + edge/.style={color=blue!95!black, thick}, + facet/.style={fill=orange!95!black,fill opacity=0.400000}, + ... + Use print to see the full content. + ... + \node[vertex] at (1.00000, 2.00000) {}; + \node[vertex] at (2.00000, 1.00000) {}; + %% + %% + \end{tikzpicture} + \end{document} + + The second example using a LatexExpr as output type:: + + sage: Image2 = P2.projection().tikz(scale=3, edge_color='blue!95!black', facet_color='orange!95!black', opacity=0.4, vertex_color='yellow', axis=True, output_type='LatexExpr') sage: type(Image2) sage: print('\n'.join(Image2.splitlines()[:4])) @@ -1308,22 +1352,40 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, sage: with open('polytope-tikz2.tex', 'w') as f: # not tested ....: _ = f.write(Image2) + A third example:: + sage: P3 = Polyhedron(vertices=[[-1, -1, 2],[-1, 2, -1],[2, -1, -1]]) sage: P3 A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices - sage: Image3 = P3.projection().tikz([0.5,-1,-0.1], 55, scale=3, edge_color='blue!95!black',facet_color='orange!95!black', opacity=0.7, vertex_color='yellow', axis=True) - sage: print('\n'.join(Image3.splitlines()[:4])) + sage: Image3 = P3.projection().tikz([0.5,-1,-0.1], 55, scale=3, edge_color='blue!95!black',facet_color='orange!95!black', opacity=0.7, vertex_color='yellow', axis=True, output_type='TikzPicture') + sage: Image3 + \documentclass[tikz]{standalone} + \begin{document} \begin{tikzpicture}% - [x={(0.658184cm, -0.242192cm)}, - y={(-0.096240cm, 0.912008cm)}, - z={(-0.746680cm, -0.331036cm)}, - sage: with open('polytope-tikz3.tex', 'w') as f: # not tested - ....: _ = f.write(Image3) + [x={(0.658184cm, -0.242192cm)}, + y={(-0.096240cm, 0.912008cm)}, + z={(-0.746680cm, -0.331036cm)}, + scale=3.000000, + ... + Use print to see the full content. + ... + \node[vertex] at (-1.00000, 2.00000, -1.00000) {}; + \node[vertex] at (2.00000, -1.00000, -1.00000) {}; + %% + %% + \end{tikzpicture} + \end{document} + sage: _ = Image3.tex('polytope-tikz3.tex') # not tested + sage: _ = Image3.png('polytope-tikz3.png') # not tested + sage: _ = Image3.pdf('polytope-tikz3.pdf') # not tested + sage: _ = Image3.svg('polytope-tikz3.svg') # not tested + + A fourth example:: sage: P = Polyhedron(vertices=[[1,1,0,0],[1,2,0,0],[2,1,0,0],[0,0,1,0],[0,0,0,1]]) sage: P A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices - sage: P.projection().tikz() + sage: P.projection().tikz(output_type='TikzPicture') Traceback (most recent call last): ... NotImplementedError: The polytope has to live in 2 or 3 dimensions. @@ -1335,7 +1397,7 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, sage: P=Polyhedron(vertices=[[1,1,0,0],[1,2,0,0],[2,1,0,0],[0,0,1,0],[0,0,0,1]]) sage: P A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices - sage: P.projection().tikz() + sage: P.projection().tikz(output_type='TikzPicture') Traceback (most recent call last): ... NotImplementedError: The polytope has to live in 2 or 3 dimensions. @@ -1347,15 +1409,40 @@ def tikz(self, view=[0, 0, 1], angle=0, scale=1, elif self.polyhedron_dim < 2 or self.polyhedron_dim > 3: raise NotImplementedError("The polytope has to be 2 or 3-dimensional.") elif self.polyhedron_ambient_dim == 2: # self is a polygon in 2-space - return self._tikz_2d(scale, edge_color, facet_color, opacity, + tikz_string = self._tikz_2d(scale, edge_color, facet_color, opacity, vertex_color, axis) elif self.polyhedron_dim == 2: # self is a polygon in 3-space - return self._tikz_2d_in_3d(view, angle, scale, edge_color, + tikz_string = self._tikz_2d_in_3d(view, angle, scale, edge_color, facet_color, opacity, vertex_color, axis) else: # self is a 3-polytope in 3-space - return self._tikz_3d_in_3d(view, angle, scale, edge_color, + tikz_string = self._tikz_3d_in_3d(view, angle, scale, edge_color, facet_color, opacity, vertex_color, axis) + # set default value + if output_type is None: + from sage.misc.superseded import deprecation + msg = ("The default type of the returned object will soon be " + "changed from `sage.misc.latex.LatexExpr` to " + "`sage.misc.latex_standalone.TikzPicture`. Please " + "update your code to specify the desired output type as " + "`.tikz(output_type='LatexExpr')` to keep the old " + "behavior or `.tikz(output_type='TikzPicture')` to use " + "the future default behavior.") + deprecation(33002, msg) + output_type = 'LatexExpr' + + # return + if output_type == 'LatexExpr': + return tikz_string + elif output_type == 'TikzPicture': + from sage.misc.latex_standalone import TikzPicture + return TikzPicture(tikz_string, standalone_config=None, + usepackage=None, usetikzlibrary=None, macros=None, + use_sage_preamble=False) + else: + raise ValueError("output_type (='{}') must be 'LatexExpr' or" + " 'TikzPicture'".format(output_type)) + def _tikz_2d(self, scale, edge_color, facet_color, opacity, vertex_color, axis): r""" Return a string ``tikz_pic`` consisting of a tikz picture of @@ -1394,9 +1481,9 @@ def _tikz_2d(self, scale, edge_color, facet_color, opacity, vertex_color, axis): Scientific notation is not used in the output (:trac:`16519`):: - sage: P=Polyhedron([[2*10^-10,0],[0,1],[1,0]],base_ring=QQ) - sage: tikzstr=P.projection().tikz() - sage: 'e-10' in tikzstr + sage: P = Polyhedron([[2*10^-10,0],[0,1],[1,0]],base_ring=QQ) + sage: tikz = P.projection().tikz(output_type='TikzPicture') + sage: 'e-10' in tikz.content() False .. NOTE:: @@ -1522,9 +1609,11 @@ def _tikz_2d_in_3d(self, view, angle, scale, edge_color, facet_color, sage: with open('polytope-tikz3.tex', 'w') as f: # not tested ....: _ = f.write(Image) + :: + sage: p = Polyhedron(vertices=[[1,0,0],[0,1,0],[0,0,1]]) sage: proj = p.projection() - sage: Img = proj.tikz([1,1,1],130,axis=True) + sage: Img = proj.tikz([1,1,1],130,axis=True, output_type='LatexExpr') sage: print('\n'.join(Img.splitlines()[12:21])) %% with the command: ._tikz_2d_in_3d and parameters: %% view = [1, 1, 1] @@ -1670,8 +1759,10 @@ def _tikz_3d_in_3d(self, view, angle, scale, edge_color, sage: with open('polytope-tikz1.tex', 'w') as f: # not tested ....: _ = f.write(Image) + :: + sage: Associahedron = Polyhedron(vertices=[[1,0,1],[1,0,0],[1,1,0],[0,0,-1],[0,1,0],[-1,0,0],[0,1,1],[0,0,1],[0,-1,0]]).polar() - sage: ImageAsso = Associahedron.projection().tikz([-15,-755,-655], 116, scale=1) + sage: ImageAsso = Associahedron.projection().tikz([-15,-755,-655], 116, scale=1, output_type='LatexExpr') sage: print('\n'.join(ImageAsso.splitlines()[12:30])) %% with the command: ._tikz_3d_in_3d and parameters: %% view = [-15, -755, -655] diff --git a/src/sage/misc/latex_standalone.py b/src/sage/misc/latex_standalone.py index 8ba1aa62211..cdfdd6b6cbb 100644 --- a/src/sage/misc/latex_standalone.py +++ b/src/sage/misc/latex_standalone.py @@ -158,7 +158,7 @@ sage: from sage.misc.latex_standalone import TikzPicture sage: V = [[1,0,1],[1,0,0],[1,1,0],[0,0,-1],[0,1,0],[-1,0,0],[0,1,1],[0,0,1],[0,-1,0]] sage: P = Polyhedron(vertices=V).polar() - sage: s = P.projection().tikz([674,108,-731],112) + sage: s = P.projection().tikz([674,108,-731],112, output_type='LatexExpr') sage: t = TikzPicture(s) Open the image in a viewer (the returned value is a string giving the