def boxes_to_tex(self, leaves=None, **options) -> str: if not leaves: leaves = self._leaves fields = self._prepare_elements(leaves, options, max_width=450) if len(fields) == 2: elements, calc_dimensions = fields else: elements, calc_dimensions = fields[0], fields[-2] fields = calc_dimensions() if len(fields) == 8: xmin, xmax, ymin, ymax, w, h, width, height = fields elements.view_width = w else: assert len(fields) == 9 xmin, xmax, ymin, ymax, _, _, _, width, height = fields elements.view_width = width asy_completely_visible = "\n".join( lookup_method(element, "asy")(element) for element in elements.elements if element.is_completely_visible) asy_regular = "\n".join( lookup_method(element, "asy")(element) for element in elements.elements if not element.is_completely_visible) asy_box = "box((%s,%s), (%s,%s))" % ( asy_number(xmin), asy_number(ymin), asy_number(xmax), asy_number(ymax), ) if self.background_color is not None: color, opacity = asy_color(self.background_color) asy_background = "filldraw(%s, %s);" % (asy_box, color) else: asy_background = "" tex = r""" \begin{asy} usepackage("amsmath"); size(%scm, %scm); %s %s clip(%s); %s \end{asy} """ % ( asy_number(width / 60), asy_number(height / 60), asy_background, asy_regular, asy_box, asy_completely_visible, ) return tex
def _extract_graphics(graphics, format, evaluation): graphics_box = Expression(SymbolMakeBoxes, graphics).evaluate(evaluation) # builtin = GraphicsBox(expression=False) elements, calc_dimensions = graphics_box._prepare_elements( graphics_box.leaves, {"evaluation": evaluation}, neg_y=True ) xmin, xmax, ymin, ymax, _, _, _, _ = calc_dimensions() # xmin, xmax have always been moved to 0 here. the untransformed # and unscaled bounds are found in elements.xmin, elements.ymin, # elements.extent_width, elements.extent_height. # now compute the position of origin (0, 0) in the transformed # coordinate space. ex = elements.extent_width ey = elements.extent_height sx = (xmax - xmin) / ex sy = (ymax - ymin) / ey ox = -elements.xmin * sx + xmin oy = -elements.ymin * sy + ymin # generate code for svg or asy. if format in ("asy", "svg"): format_fn = lookup_method(elements, format) code = format_fn(elements) else: raise NotImplementedError return xmin, xmax, ymin, ymax, ox, oy, ex, ey, code
def boxes_to_json(self, leaves=None, **options): """Turn the Graphics3DBox to into a something JSON like. This can be used to embed in something else like MathML or Javascript. In contrast to to javascript or MathML, no enclosing tags are included. the caller will do that if it is needed. """ if not leaves: leaves = self._leaves ( elements, axes, ticks, ticks_style, calc_dimensions, boxscale, ) = self._prepare_elements(leaves, options) js_ticks_style = [s.to_js() for s in ticks_style] elements._apply_boxscaling(boxscale) xmin, xmax, ymin, ymax, zmin, zmax, boxscale, w, h = calc_dimensions() elements.view_width = w # FIXME: json is the only thing we can convert MathML into. # Handle other graphics formats. format_fn = lookup_method(elements, "json") json_repr = format_fn(elements, **options) # TODO: Cubeoid (like this) # json_repr = [{'faceColor': (1, 1, 1, 1), 'position': [(0,0,0), None], # 'size':[(1,1,1), None], 'type': 'cube'}] json_repr = json.dumps({ "elements": json_repr, "axes": { "hasaxes": axes, "ticks": ticks, "ticks_style": js_ticks_style, }, "extent": { "xmin": xmin, "xmax": xmax, "ymin": ymin, "ymax": ymax, "zmin": zmin, "zmax": zmax, }, "lighting": self.lighting, "viewpoint": self.viewpoint, }) return json_repr
def graphics_elements(self, **options) -> str: result = [] for element in self.elements: format_fn = lookup_method(element, "asy") if format_fn is None: result.append(element.to_asy(**options)) else: result.append(format_fn(element)) return "\n".join(result)
def graphics_3D_elements(self, **options) -> list: """Iterates over self.elements to convert each item. The list of converted items is returned. """ result = [] for element in self.elements: format_fn = lookup_method(element, "json") result += format_fn(element) # print("### json Graphics3DElements", result) return result
def graphics_3D_elements(self, **options): result = [] for element in self.elements: format_fn = lookup_method(element, "json") if format_fn is None: result += element.to_json() else: result += format_fn(element) # print("### json Graphics3DElements", result) return result
def graphics_elements(self, **options) -> str: """ SVG Formatting for a list of graphics elements. """ result = ["<!--GraphicsElements-->"] for element in self.elements: format_fn = lookup_method(element, "svg") if format_fn is None: result.append(element.to_svg(**options)) else: result.append(format_fn(element, **options)) svg = "\n".join(result) # print("GraphicsElements: ", svg) return svg
def get_svg(expression): options = {} boxes = MakeBoxes(expression).evaluate(evaluation) # Would be nice to DRY this boilerplate from boxes_to_mathml leaves = boxes._leaves elements, calc_dimensions = boxes._prepare_elements(leaves, options=options, neg_y=True) xmin, xmax, ymin, ymax, w, h, width, height = calc_dimensions() data = (elements, xmin, xmax, ymin, ymax, w, h, width, height) format_fn = lookup_method(boxes, "svg") return format_fn(boxes, leaves, data=data, options=options)
def boxes_to_svg(self, leaves=None, **options) -> str: if not leaves: leaves = self._leaves elements, calc_dimensions = self._prepare_elements(leaves, options, neg_y=True) xmin, xmax, ymin, ymax, w, h, self.width, self.height = calc_dimensions( ) data = (elements, xmin, xmax, ymin, ymax, w, h, self.width, self.height) elements.view_width = w format_fn = lookup_method(self, "svg") svg_body = format_fn(self, leaves, data=data, **options) return svg_body
def boxes_to_svg(self, leaves=None, **options) -> str: """This is the top-level function that converts a Mathics Expression in to something suitable for SVG rendering. """ if not leaves: leaves = self._leaves elements, calc_dimensions = self._prepare_elements(leaves, options, neg_y=True) xmin, xmax, ymin, ymax, w, h, self.width, self.height = calc_dimensions( ) data = (elements, xmin, xmax, ymin, ymax, w, h, self.width, self.height) elements.view_width = w format_fn = lookup_method(self, "svg") svg_body = format_fn(self, leaves, data=data, **options) return svg_body
def graphics_box(self, leaves=None, **options) -> str: if not leaves: leaves = self._leaves data = options.get("data", None) if data: ( elements, xmin, xmax, ymin, ymax, self.boxwidth, self.boxheight, width, height, ) = data else: elements, calc_dimensions = self._prepare_elements(leaves, options, neg_y=True) ( xmin, xmax, ymin, ymax, self.boxwidth, self.boxheight, width, height, ) = calc_dimensions() elements.view_width = self.boxwidth format_fn = lookup_method(elements, "svg") if format_fn is not None: svg_body = format_fn(elements, **options) else: svg_body = elements.to_svg(**options) self.boxwidth = options.get("width", self.boxwidth) self.boxheight = options.get("height", self.boxheight) if self.background_color is not None: # Wrap svg_elements in a rectangle svg_body = '<rect x="%f" y="%f" width="%f" height="%f" style="fill:%s"/>%s' % ( xmin, ymin, self.boxwidth, self.boxheight, self.background_color.to_css()[0], svg_body, ) if options.get("noheader", False): return svg_body svg_main = f"""<svg width="{self.boxwidth}px" height="{self.boxheight}px" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="%s"> %s </svg> """ % ( " ".join("%f" % t for t in (xmin, ymin, self.boxwidth, self.boxheight)), svg_body, ) # print("svg_main", svg_main) return svg_main # , width, height
def boxes_to_tex(self, leaves=None, **options) -> str: """This is the top-level function that converts a Mathics Expression in to something suitable for LaTeX. (Yes, the name "tex" is perhaps misleading of vague.) However right now the only LaTeX support for graphics is via Asymptote and that seems to be the package of choice in general for LaTeX. """ if not leaves: leaves = self._leaves fields = self._prepare_elements(leaves, options, max_width=450) if len(fields) == 2: elements, calc_dimensions = fields else: elements, calc_dimensions = fields[0], fields[-2] fields = calc_dimensions() if len(fields) == 8: xmin, xmax, ymin, ymax, w, h, width, height = fields elements.view_width = w else: assert len(fields) == 9 xmin, xmax, ymin, ymax, _, _, _, width, height = fields elements.view_width = width asy_completely_visible = "\n".join( lookup_method(element, "asy")(element) for element in elements.elements if element.is_completely_visible) asy_regular = "\n".join( lookup_method(element, "asy")(element) for element in elements.elements if not element.is_completely_visible) asy_box = "box((%s,%s), (%s,%s))" % ( asy_number(xmin), asy_number(ymin), asy_number(xmax), asy_number(ymax), ) if self.background_color is not None: color, opacity = asy_color(self.background_color) asy_background = "filldraw(%s, %s);" % (asy_box, color) else: asy_background = "" tex = r""" \begin{asy} usepackage("amsmath"); size(%scm, %scm); %s %s clip(%s); %s \end{asy} """ % ( asy_number(width / 60), asy_number(height / 60), asy_background, asy_regular, asy_box, asy_completely_visible, ) return tex
def boxes_to_tex(self, leaves=None, **options): if not leaves: leaves = self._leaves ( elements, axes, ticks, ticks_style, calc_dimensions, boxscale, ) = self._prepare_elements(leaves, options, max_width=450) elements._apply_boxscaling(boxscale) format_fn = lookup_method(elements, "asy") if format_fn is not None: asy = format_fn(elements) else: asy = elements.to_asy() xmin, xmax, ymin, ymax, zmin, zmax, boxscale, w, h = calc_dimensions() # TODO: Intelligently place the axes on the longest non-middle edge. # See algorithm used by web graphics in mathics/web/media/graphics.js # for details of this. (Projection to sceen etc). # Choose axes placement (boundbox edge vertices) axes_indices = [] if axes[0]: axes_indices.append(0) if axes[1]: axes_indices.append(6) if axes[2]: axes_indices.append(8) # Draw boundbox and axes boundbox_asy = "" boundbox_lines = self.get_boundbox_lines(xmin, xmax, ymin, ymax, zmin, zmax) for i, line in enumerate(boundbox_lines): if i in axes_indices: pen = asy_create_pens(edge_color=RGBColor(components=(0, 0, 0, 1)), stroke_width=1.5) else: pen = asy_create_pens(edge_color=RGBColor(components=(0.4, 0.4, 0.4, 1)), stroke_width=1) path = "--".join(["(%.5g,%.5g,%.5g)" % coords for coords in line]) boundbox_asy += "draw((%s), %s);\n" % (path, pen) # TODO: Intelligently draw the axis ticks such that they are always # directed inward and choose the coordinate direction which makes the # ticks the longest. Again, details in mathics/web/media/graphics.js # Draw axes ticks ticklength = 0.05 * max([xmax - xmin, ymax - ymin, zmax - zmin]) pen = asy_create_pens(edge_color=RGBColor(components=(0, 0, 0, 1)), stroke_width=1.2) for xi in axes_indices: if xi < 4: # x axis for i, tick in enumerate(ticks[0][0]): line = [ (tick, boundbox_lines[xi][0][1], boundbox_lines[xi][0][2]), ( tick, boundbox_lines[xi][0][1], boundbox_lines[xi][0][2] + ticklength, ), ] path = "--".join( ["({0},{1},{2})".format(*coords) for coords in line]) boundbox_asy += "draw(({0}), {1});\n".format(path, pen) boundbox_asy += 'label("{0}",{1},{2});\n'.format( ticks[0][2][i], (tick, boundbox_lines[xi][0][1], boundbox_lines[xi][0][2]), "S", ) for small_tick in ticks[0][1]: line = [ ( small_tick, boundbox_lines[xi][0][1], boundbox_lines[xi][0][2], ), ( small_tick, boundbox_lines[xi][0][1], boundbox_lines[xi][0][2] + 0.5 * ticklength, ), ] path = "--".join( ["({0},{1},{2})".format(*coords) for coords in line]) boundbox_asy += "draw(({0}), {1});\n".format(path, pen) if 4 <= xi < 8: # y axis for i, tick in enumerate(ticks[1][0]): line = [ (boundbox_lines[xi][0][0], tick, boundbox_lines[xi][0][2]), ( boundbox_lines[xi][0][0], tick, boundbox_lines[xi][0][2] - ticklength, ), ] path = "--".join( ["({0},{1},{2})".format(*coords) for coords in line]) boundbox_asy += "draw(({0}), {1});\n".format(path, pen) boundbox_asy += 'label("{0}",{1},{2});\n'.format( ticks[1][2][i], (boundbox_lines[xi][0][0], tick, boundbox_lines[xi][0][2]), "NW", ) for small_tick in ticks[1][1]: line = [ ( boundbox_lines[xi][0][0], small_tick, boundbox_lines[xi][0][2], ), ( boundbox_lines[xi][0][0], small_tick, boundbox_lines[xi][0][2] - 0.5 * ticklength, ), ] path = "--".join( ["({0},{1},{2})".format(*coords) for coords in line]) boundbox_asy += "draw(({0}), {1});\n".format(path, pen) if 8 <= xi: # z axis for i, tick in enumerate(ticks[2][0]): line = [ (boundbox_lines[xi][0][0], boundbox_lines[xi][0][1], tick), ( boundbox_lines[xi][0][0], boundbox_lines[xi][0][1] + ticklength, tick, ), ] path = "--".join( ["({0},{1},{2})".format(*coords) for coords in line]) boundbox_asy += "draw(({0}), {1});\n".format(path, pen) boundbox_asy += 'label("{0}",{1},{2});\n'.format( ticks[2][2][i], (boundbox_lines[xi][0][0], boundbox_lines[xi][0][1], tick), "W", ) for small_tick in ticks[2][1]: line = [ ( boundbox_lines[xi][0][0], boundbox_lines[xi][0][1], small_tick, ), ( boundbox_lines[xi][0][0], boundbox_lines[xi][0][1] + 0.5 * ticklength, small_tick, ), ] path = "--".join( ["({0},{1},{2})".format(*coords) for coords in line]) boundbox_asy += "draw(({0}), {1});\n".format(path, pen) (height, width) = (400, 400) # TODO: Proper size tex = r""" \begin{{asy}} import three; import solids; size({0}cm, {1}cm); currentprojection=perspective({2[0]},{2[1]},{2[2]}); currentlight=light(rgb(0.5,0.5,1), specular=red, (2,0,2), (2,2,2), (0,2,2)); {3} {4} \end{{asy}} """.format( asy_number(width / 60), asy_number(height / 60), self.viewpoint, asy, boundbox_asy, ) return tex