|
7 | 7 |
|
8 | 8 |
|
9 | 9 | from math import floor, ceil, log10, sin, cos, pi, sqrt, atan2, degrees, radians, exp |
10 | | -import json |
11 | 10 | import base64 |
12 | 11 | from itertools import chain |
13 | 12 |
|
|
33 | 32 | system_symbols_dict, |
34 | 33 | from_python, |
35 | 34 | ) |
| 35 | +from mathics.core.formatter import lookup_method |
| 36 | + |
36 | 37 | from mathics.builtin.drawing.colors import convert as convert_color |
37 | 38 | from mathics.core.numbers import machine_epsilon |
38 | 39 |
|
@@ -323,7 +324,11 @@ def _extract_graphics(graphics, format, evaluation): |
323 | 324 | if format == "asy": |
324 | 325 | code = "\n".join(element.to_asy() for element in elements.elements) |
325 | 326 | elif format == "svg": |
326 | | - code = elements.to_svg() |
| 327 | + format_fn = lookup_method(elements, "svg") |
| 328 | + if format_fn is not None: |
| 329 | + code = format_fn(elements) |
| 330 | + else: |
| 331 | + code = elements.to_svg() |
327 | 332 | else: |
328 | 333 | raise NotImplementedError |
329 | 334 |
|
@@ -1231,26 +1236,6 @@ def extent(self): |
1231 | 1236 | ) |
1232 | 1237 | return result |
1233 | 1238 |
|
1234 | | - def to_svg(self, offset=None): |
1235 | | - l = self.style.get_line_width(face_element=True) |
1236 | | - x1, y1 = self.p1.pos() |
1237 | | - x2, y2 = self.p2.pos() |
1238 | | - xmin = min(x1, x2) |
1239 | | - ymin = min(y1, y2) |
1240 | | - w = max(x1, x2) - xmin |
1241 | | - h = max(y1, y2) - ymin |
1242 | | - if offset: |
1243 | | - x1, x2 = x1 + offset[0], x2 + offset[0] |
1244 | | - y1, y2 = y1 + offset[1], y2 + offset[1] |
1245 | | - style = create_css(self.edge_color, self.face_color, l) |
1246 | | - return '<rect x="%f" y="%f" width="%f" height="%f" style="%s" />' % ( |
1247 | | - xmin, |
1248 | | - ymin, |
1249 | | - w, |
1250 | | - h, |
1251 | | - style, |
1252 | | - ) |
1253 | | - |
1254 | 1239 | def to_asy(self): |
1255 | 1240 | l = self.style.get_line_width(face_element=True) |
1256 | 1241 | x1, y1 = self.p1.pos() |
@@ -1302,21 +1287,6 @@ def extent(self): |
1302 | 1287 | ry += l |
1303 | 1288 | return [(x - rx, y - ry), (x - rx, y + ry), (x + rx, y - ry), (x + rx, y + ry)] |
1304 | 1289 |
|
1305 | | - def to_svg(self, offset=None): |
1306 | | - x, y = self.c.pos() |
1307 | | - rx, ry = self.r.pos() |
1308 | | - rx -= x |
1309 | | - ry = y - ry |
1310 | | - l = self.style.get_line_width(face_element=self.face_element) |
1311 | | - style = create_css(self.edge_color, self.face_color, stroke_width=l) |
1312 | | - return '<ellipse cx="%f" cy="%f" rx="%f" ry="%f" style="%s" />' % ( |
1313 | | - x, |
1314 | | - y, |
1315 | | - rx, |
1316 | | - ry, |
1317 | | - style, |
1318 | | - ) |
1319 | | - |
1320 | 1290 | def to_asy(self): |
1321 | 1291 | x, y = self.c.pos() |
1322 | 1292 | rx, ry = self.r.pos() |
@@ -1390,11 +1360,15 @@ def _arc_params(self): |
1390 | 1360 | return x, y, abs(rx), abs(ry), sx, sy, ex, ey, large_arc |
1391 | 1361 |
|
1392 | 1362 | def to_svg(self, offset=None): |
| 1363 | + # FIXME: figure out how to put in svg.py |
1393 | 1364 | if self.arc is None: |
1394 | 1365 | return super(_ArcBox, self).to_svg(offset) |
1395 | 1366 |
|
1396 | 1367 | x, y, rx, ry, sx, sy, ex, ey, large_arc = self._arc_params() |
1397 | 1368 |
|
| 1369 | + format_fn = lookup_method(self, "svg") |
| 1370 | + if format_fn is not None: |
| 1371 | + return format_fn(self, offset) |
1398 | 1372 | def path(closed): |
1399 | 1373 | if closed: |
1400 | 1374 | yield "M %f,%f" % (x, y) |
@@ -1522,26 +1496,6 @@ def init(self, graphics, style, item=None): |
1522 | 1496 | else: |
1523 | 1497 | raise BoxConstructError |
1524 | 1498 |
|
1525 | | - def to_svg(self, offset=None): |
1526 | | - point_size, _ = self.style.get_style(PointSize, face_element=False) |
1527 | | - if point_size is None: |
1528 | | - point_size = PointSize(self.graphics, value=0.005) |
1529 | | - size = point_size.get_size() |
1530 | | - |
1531 | | - style = create_css( |
1532 | | - edge_color=self.edge_color, stroke_width=0, face_color=self.face_color |
1533 | | - ) |
1534 | | - svg = "" |
1535 | | - for line in self.lines: |
1536 | | - for coords in line: |
1537 | | - svg += '<circle cx="%f" cy="%f" r="%f" style="%s" />' % ( |
1538 | | - coords.pos()[0], |
1539 | | - coords.pos()[1], |
1540 | | - size, |
1541 | | - style, |
1542 | | - ) |
1543 | | - return svg |
1544 | | - |
1545 | 1499 | def to_asy(self): |
1546 | 1500 | pen = create_pens(face_color=self.face_color, is_face_element=False) |
1547 | 1501 |
|
@@ -1586,17 +1540,6 @@ def init(self, graphics, style, item=None, lines=None): |
1586 | 1540 | else: |
1587 | 1541 | raise BoxConstructError |
1588 | 1542 |
|
1589 | | - def to_svg(self, offset=None): |
1590 | | - l = self.style.get_line_width(face_element=False) |
1591 | | - style = create_css(edge_color=self.edge_color, stroke_width=l) |
1592 | | - svg = "" |
1593 | | - for line in self.lines: |
1594 | | - svg += '<polyline points="%s" style="%s" />' % ( |
1595 | | - " ".join(["%f,%f" % coords.pos() for coords in line]), |
1596 | | - style, |
1597 | | - ) |
1598 | | - return svg |
1599 | | - |
1600 | 1543 | def to_asy(self): |
1601 | 1544 | l = self.style.get_line_width(face_element=False) |
1602 | 1545 | pen = create_pens(edge_color=self.edge_color, stroke_width=l) |
@@ -1741,16 +1684,6 @@ def init(self, graphics, style, item, options): |
1741 | 1684 | raise BoxConstructError |
1742 | 1685 | self.spline_degree = spline_degree.get_int_value() |
1743 | 1686 |
|
1744 | | - def to_svg(self, offset=None): |
1745 | | - l = self.style.get_line_width(face_element=False) |
1746 | | - style = create_css(edge_color=self.edge_color, stroke_width=l) |
1747 | | - |
1748 | | - svg = "" |
1749 | | - for line in self.lines: |
1750 | | - s = " ".join(_svg_bezier((self.spline_degree, [xy.pos() for xy in line]))) |
1751 | | - svg += '<path d="%s" style="%s"/>' % (s, style) |
1752 | | - return svg |
1753 | | - |
1754 | 1687 | def to_asy(self): |
1755 | 1688 | l = self.style.get_line_width(face_element=False) |
1756 | 1689 | pen = create_pens(edge_color=self.edge_color, stroke_width=l) |
@@ -1828,22 +1761,6 @@ def parse_component(segments): |
1828 | 1761 | else: |
1829 | 1762 | raise BoxConstructError |
1830 | 1763 |
|
1831 | | - def to_svg(self, offset=None): |
1832 | | - l = self.style.get_line_width(face_element=False) |
1833 | | - style = create_css( |
1834 | | - edge_color=self.edge_color, face_color=self.face_color, stroke_width=l |
1835 | | - ) |
1836 | | - |
1837 | | - def components(): |
1838 | | - for component in self.components: |
1839 | | - transformed = [(k, [xy.pos() for xy in p]) for k, p in component] |
1840 | | - yield " ".join(_svg_bezier(*transformed)) + " Z" |
1841 | | - |
1842 | | - return '<path d="%s" style="%s" fill-rule="evenodd"/>' % ( |
1843 | | - " ".join(components()), |
1844 | | - style, |
1845 | | - ) |
1846 | | - |
1847 | 1764 | def to_asy(self): |
1848 | 1765 | l = self.style.get_line_width(face_element=False) |
1849 | 1766 | pen = create_pens(edge_color=self.edge_color, stroke_width=l) |
@@ -1933,32 +1850,6 @@ def process_option(self, name, value): |
1933 | 1850 | else: |
1934 | 1851 | raise BoxConstructError |
1935 | 1852 |
|
1936 | | - def to_svg(self, offset=None): |
1937 | | - l = self.style.get_line_width(face_element=True) |
1938 | | - if self.vertex_colors is None: |
1939 | | - face_color = self.face_color |
1940 | | - else: |
1941 | | - face_color = None |
1942 | | - style = create_css( |
1943 | | - edge_color=self.edge_color, face_color=face_color, stroke_width=l |
1944 | | - ) |
1945 | | - svg = "" |
1946 | | - if self.vertex_colors is not None: |
1947 | | - mesh = [] |
1948 | | - for index, line in enumerate(self.lines): |
1949 | | - data = [ |
1950 | | - [coords.pos(), color.to_js()] |
1951 | | - for coords, color in zip(line, self.vertex_colors[index]) |
1952 | | - ] |
1953 | | - mesh.append(data) |
1954 | | - svg += '<meshgradient data="%s" />' % json.dumps(mesh) |
1955 | | - for line in self.lines: |
1956 | | - svg += '<polygon points="%s" style="%s" />' % ( |
1957 | | - " ".join("%f,%f" % coords.pos() for coords in line), |
1958 | | - style, |
1959 | | - ) |
1960 | | - return svg |
1961 | | - |
1962 | 1853 | def to_asy(self): |
1963 | 1854 | l = self.style.get_line_width(face_element=True) |
1964 | 1855 | if self.vertex_colors is None: |
@@ -2617,35 +2508,6 @@ def extent(self): |
2617 | 2508 | y = p[1] - h / 2.0 + opos[1] * h / 2.0 |
2618 | 2509 | return [(x, y), (x + w, y + h)] |
2619 | 2510 |
|
2620 | | - def to_svg(self, offset=None): |
2621 | | - x, y = self.pos.pos() |
2622 | | - if offset: |
2623 | | - x = x + offset[0] |
2624 | | - y = y + offset[1] |
2625 | | - |
2626 | | - if hasattr(self.content, "to_svg"): |
2627 | | - content = self.content.to_svg(noheader=True, offset=(x, y)) |
2628 | | - svg = "\n" + content + "\n" |
2629 | | - else: |
2630 | | - css_style = create_css( |
2631 | | - font_color=self.color, |
2632 | | - edge_color=self.color, |
2633 | | - face_color=self.color, |
2634 | | - opacity=self.opacity, |
2635 | | - ) |
2636 | | - text_pos_opts = f'x="{x}" y="{y}" ox="{self.opos[0]}" oy="{self.opos[1]}"' |
2637 | | - # FIXME: don't hard code text_style_opts, but allow these to be adjustable. |
2638 | | - text_style_opts = "text-anchor:middle; dominant-baseline:middle;" |
2639 | | - content = self.content.boxes_to_text(evaluation=self.graphics.evaluation) |
2640 | | - svg = f'<text {text_pos_opts} style="{text_style_opts} {css_style}">{content}</text>' |
2641 | | - |
2642 | | - # content = self.content.boxes_to_mathml(evaluation=self.graphics.evaluation) |
2643 | | - # style = create_css(font_color=self.color) |
2644 | | - # svg = ( |
2645 | | - # '<foreignObject x="%f" y="%f" ox="%f" oy="%f" style="%s">' |
2646 | | - # "<math>%s</math></foreignObject>") |
2647 | | - |
2648 | | - return svg |
2649 | 2511 |
|
2650 | 2512 | def to_asy(self): |
2651 | 2513 | x, y = self.pos.pos() |
@@ -2960,9 +2822,6 @@ def extent(self, completely_visible_only=False): |
2960 | 2822 | ymax *= 2 |
2961 | 2823 | return xmin, xmax, ymin, ymax |
2962 | 2824 |
|
2963 | | - def to_svg(self, offset=None): |
2964 | | - return "\n".join(element.to_svg(offset) for element in self.elements) |
2965 | | - |
2966 | 2825 | def to_asy(self): |
2967 | 2826 | return "\n".join(element.to_asy() for element in self.elements) |
2968 | 2827 |
|
@@ -3265,7 +3124,11 @@ def to_svg(self, leaves=None, **options): |
3265 | 3124 |
|
3266 | 3125 | elements.view_width = w |
3267 | 3126 |
|
3268 | | - svg = elements.to_svg(offset=options.get("offset", None)) |
| 3127 | + format_fn = lookup_method(elements, "svg") |
| 3128 | + if format_fn is not None: |
| 3129 | + svg = format_fn(elements) |
| 3130 | + else: |
| 3131 | + svg = elements.to_svg(offset=options.get("offset", None)) |
3269 | 3132 |
|
3270 | 3133 | if self.background_color is not None: |
3271 | 3134 | svg = '<rect x="%f" y="%f" width="%f" height="%f" style="fill:%s"/>%s' % ( |
|
0 commit comments