Пример #1
0
    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
Пример #2
0
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
Пример #3
0
    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
Пример #4
0
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)
Пример #5
0
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
Пример #6
0
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
Пример #7
0
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
Пример #8
0
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)
Пример #9
0
    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
Пример #10
0
    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
Пример #11
0
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
Пример #12
0
    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
Пример #13
0
    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