66
77# uses networkx
88
9+ import base64
10+ import tempfile
11+
912from collections import defaultdict
1013from inspect import isgenerator
11- from typing import Callable , Optional
14+ from typing import Callable , Optional , Tuple
1215
1316from mathics .builtin .base import AtomBuiltin , Builtin
14- from mathics .builtin .box .graphics import GraphicsBox
1517from mathics .core .atoms import Atom , Integer , Integer0 , Integer1 , Integer2 , Real , String
1618from mathics .core .convert .expression import ListExpression , from_python
17- from mathics .core .element import BaseElement
19+ from mathics .core .element import BaseElement , BoxElementMixin
1820from mathics .core .expression import Expression
1921from mathics .core .symbols import Symbol , SymbolList , SymbolTrue
2022from mathics .core .systemsymbols import (
2123 SymbolBlank ,
2224 SymbolCases ,
2325 SymbolFailed ,
24- SymbolGraphics ,
25- SymbolMakeBoxes ,
2626 SymbolMissing ,
2727 SymbolRGBColor ,
2828 SymbolRule ,
2929)
30- from mathics .eval .makeboxes import _boxed_string
3130from mathics .eval .patterns import Matcher
3231
32+ from pymathics .graph .format import png_format_graph , svg_format_graph
3333from pymathics .graph .graphsymbols import (
3434 SymbolDirectedEdge ,
3535 SymbolGraph ,
3636 SymbolTwoWayRule ,
3737 SymbolUndirectedEdge ,
3838)
3939
40+
4041WL_MARKER_TO_NETWORKX = {
4142 "Circle" : "o" ,
4243 "Diamond" : "D" ,
@@ -566,9 +567,9 @@ class Graph(Atom):
566567
567568 options = DEFAULT_GRAPH_OPTIONS
568569
569- def __init__ (self , G , ** kwargs ):
570+ def __init__ (self , Gr , ** kwargs ):
570571 super (Graph , self ).__init__ ()
571- self .G = G
572+ self .G = Gr
572573 self .mixed = kwargs .get ("mixed" , False )
573574
574575 def __hash__ (self ):
@@ -577,8 +578,8 @@ def __hash__(self):
577578 def __str__ (self ):
578579 return "-Graph-"
579580
580- def atom_to_boxes (self , f , evaluation ) -> _boxed_string :
581- return _boxed_string ( "-Graph-" )
581+ def atom_to_boxes (self , f , evaluation ) -> "GraphBox" :
582+ return GraphBox ( self . G )
582583
583584 def add_edges (self , new_edges , new_edge_properties ):
584585 G = self .G .copy ()
@@ -1621,25 +1622,64 @@ def eval_1(self, vertices, edges, evaluation, options):
16211622 )
16221623
16231624
1624- class GraphBox (GraphicsBox ):
1625- def _graphics_box (self , elements , options ):
1626- evaluation = options ["evaluation" ]
1627- graph , form = elements
1628- primitives = graph ._layout (evaluation )
1629- graphics = Expression (SymbolGraphics , primitives )
1630- graphics_box = Expression (SymbolMakeBoxes , graphics , form ).evaluate (evaluation )
1631- return graphics_box
1625+ class GraphBox (BoxElementMixin ):
1626+ def __init__ (self , G , ** options ):
1627+ self .G = G
1628+ self .options = options
16321629
1633- def boxes_to_text (self , elements , ** options ):
1634- return "-Graph-"
1630+ def boxes_to_b64text (
1631+ self , elements : Tuple [BaseElement ] = None , ** options
1632+ ) -> Tuple [bytes , Tuple [int , int ]]:
1633+ """
1634+ Produces a base64 png representation and a tuple with the size of the pillow image
1635+ associated to the object.
1636+ """
1637+ contents , size = self .boxes_to_png (elements , ** options )
1638+ encoded = base64 .b64encode (contents )
1639+ encoded = b"data:image/png;base64," + encoded
1640+ return encoded , size
1641+
1642+ def boxes_to_png (self , elements = None , ** options ) -> Tuple [bytes , Tuple [int , int ]]:
1643+ """
1644+ returns a tuple with the set of bytes with a png representation of the image
1645+ and the scaled size.
1646+ """
1647+ return png_format_graph (self .G , ** self .options ), (800 , 600 )
16351648
1636- def boxes_to_xml (self , elements , ** options ):
1637- # Figure out what to do here.
1638- return "-Graph-XML-"
1649+ def boxes_to_svg (self , elements = None , ** options ):
1650+ return svg_format_graph (self .G , ** self .options ), (400 , 300 )
1651+
1652+ def boxes_to_tex (self , elements = None , ** options ) -> str :
1653+ """
1654+ Store the associated image as a png file and return
1655+ a LaTeX command for including it.
1656+ """
1657+
1658+ data , size = self .boxes_to_png (elements , ** options )
1659+ res = 100 # pixels/cm
1660+ width_str , height_str = (str (n / res ).strip () for n in size )
1661+ head = rf"\includegraphics[width={ width_str } cm,height={ height_str } cm]"
1662+
1663+ # This produces a random name, where the png file is going to be stored.
1664+ # LaTeX does not have a native way to store an figure embeded in
1665+ # the source.
1666+ fp = tempfile .NamedTemporaryFile (delete = True , suffix = ".png" )
1667+ path = fp .name
1668+ fp .close ()
1669+
1670+ with open (path , "wb" ) as imgfile :
1671+ imgfile .write (data )
1672+
1673+ return head + "{" + format (path ) + "}"
1674+
1675+ def boxes_to_text (self , elements = None , ** options ):
1676+ return "-Graph-"
16391677
1640- def boxes_to_tex (self , elements , ** options ):
1641- # Figure out what to do here.
1642- return "-Graph-TeX-"
1678+ def boxes_to_mathml (self , elements = None , ** options ):
1679+ encoded , size = self .boxes_to_b64text (elements , ** options )
1680+ decoded = encoded .decode ("utf8" )
1681+ # see https://tools.ietf.org/html/rfc2397
1682+ return f'<mglyph src="{ decoded } " width="{ size [0 ]} px" height="{ size [1 ]} px" />'
16431683
16441684
16451685class HITSCentrality (_Centrality ):
0 commit comments