Skip to content

Commit 57a1161

Browse files
authored
handling boxes (#17)
* handling boxes * handling boxes_to_latex via boxes_to_png
1 parent 1bbdff9 commit 57a1161

File tree

2 files changed

+464
-26
lines changed

2 files changed

+464
-26
lines changed

pymathics/graph/base.py

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,38 @@
66

77
# uses networkx
88

9+
import base64
10+
import tempfile
11+
912
from collections import defaultdict
1013
from inspect import isgenerator
11-
from typing import Callable, Optional
14+
from typing import Callable, Optional, Tuple
1215

1316
from mathics.builtin.base import AtomBuiltin, Builtin
14-
from mathics.builtin.box.graphics import GraphicsBox
1517
from mathics.core.atoms import Atom, Integer, Integer0, Integer1, Integer2, Real, String
1618
from mathics.core.convert.expression import ListExpression, from_python
17-
from mathics.core.element import BaseElement
19+
from mathics.core.element import BaseElement, BoxElementMixin
1820
from mathics.core.expression import Expression
1921
from mathics.core.symbols import Symbol, SymbolList, SymbolTrue
2022
from 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
3130
from mathics.eval.patterns import Matcher
3231

32+
from pymathics.graph.format import png_format_graph, svg_format_graph
3333
from pymathics.graph.graphsymbols import (
3434
SymbolDirectedEdge,
3535
SymbolGraph,
3636
SymbolTwoWayRule,
3737
SymbolUndirectedEdge,
3838
)
3939

40+
4041
WL_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

16451685
class HITSCentrality(_Centrality):

0 commit comments

Comments
 (0)