def arcbox(self, **options) -> str: """ Aymptote formatting for arc of a circle. """ if self.arc is None: # We have a doughnut graph and this is the inner blank hole of that. # It is an empty circle return _roundbox(self, **options) x, y, rx, ry, sx, sy, ex, ey, large_arc = self._arc_params() def path(closed): if closed: yield "(%s,%s)--(%s,%s)--" % tuple( asy_number(t) for t in (x, y, sx, sy)) yield "arc((%s,%s), (%s, %s), (%s, %s))" % tuple( asy_number(t) for t in (x, y, sx, sy, ex, ey)) if closed: yield "--cycle" l = self.style.get_line_width(face_element=self.face_element) pen = asy_create_pens( edge_color=self.edge_color, face_color=self.face_color, stroke_width=l, is_face_element=self.face_element, ) command = "filldraw" if self.face_element else "draw" asy = "%s(%s, %s);" % (command, "".join(path(self.face_element)), pen) # print("### arcbox", asy) return asy
def line3dbox(self, **options) -> str: # l = self.style.get_line_width(face_element=False) pen = asy_create_pens(edge_color=self.edge_color, stroke_width=1) return "".join("draw({0}, {1});".format( "--".join("({0},{1},{2})".format(*coords.pos()[0]) for coords in line), pen, ) for line in self.lines)
def arrow_box(self, **options) -> str: width = self.style.get_line_width(face_element=False) pen = asy_create_pens(edge_color=self.edge_color, stroke_width=width) polyline = self.curve.make_draw_asy(pen) arrow_pen = asy_create_pens(face_color=self.edge_color, stroke_width=width) def polygon(points): yield "filldraw(" yield "--".join(["(%.5g,%5g)" % xy for xy in points]) yield "--cycle, % s);" % arrow_pen extent = self.graphics.view_width or 0 default_arrow = self._default_arrow(polygon) custom_arrow = self._custom_arrow("asy", _ASYTransform) asy = "".join(self._draw(polyline, default_arrow, custom_arrow, extent)) # print("### arrowbox", asy) return asy
def linebox(self) -> str: line_width = self.style.get_line_width(face_element=False) pen = asy_create_pens(edge_color=self.edge_color, stroke_width=line_width) asy = "" for line in self.lines: path = "--".join(["(%.5g,%5g)" % coords.pos() for coords in line]) asy += "draw(%s, %s);" % (path, pen) # print("### linebox", asy) return asy
def point3dbox(self, **options) -> str: face_color = self.face_color # Tempoary bug fix: default Point color should be black not white if list(face_color.to_rgba()[:3]) == [1, 1, 1]: face_color = RGBColor(components=(0, 0, 0, face_color.to_rgba()[3])) pen = asy_create_pens(face_color=face_color, is_face_element=False) return "".join(f"path3 g={0}--cycle;dot(g, {pen});".format("--".join( "(%.5g,%.5g,%.5g)" % coords.pos()[0] for coords in line)) for line in self.lines)
def bezier_curve_box(self, **options) -> str: line_width = self.style.get_line_width(face_element=False) pen = asy_create_pens(edge_color=self.edge_color, stroke_width=line_width) asy = "" for line in self.lines: for path in asy_bezier( (self.spline_degree, [xy.pos() for xy in line])): if path[:2] == "..": path = "(0.,0.)" + path asy += "draw(%s, %s);" % (path, pen) return asy
def arcbox(self, **options) -> str: """ Aymptote formatting for an arc of a circle or an ellipse. """ if self.arc is None: # We have a doughnut graph and this is the inner blank hole of that. # It is an empty circle return _roundbox(self, **options) x, y, rx, ry, sx, sy, ex, ey, large_arc = self._arc_params() ry = max(ry, 0.1) # Avoid division by 0 yscale = ry / rx def create_arc_path(is_closed: bool, yscale: float) -> str: """Constructs arc path taking into account whether the path is closed and the scaling along the Y dimension (i.e. Mathics disks support ellipses. An Asymptote string for the path is returned. """ arc_path = "" if is_closed: arc_path = "(%s,%s)--(%s,%s)--" % tuple( asy_number(t) for t in (x, y, sx, sy) ) arc_path += "arc((%s,%s), (%s, %s), (%s, %s))" % tuple( asy_number(t) for t in (x, y, sx, sy, ex, ey) ) if is_closed: arc_path += "--cycle" if yscale != 1.0: arc_path = f"yscale({yscale}) * ({arc_path})" return arc_path l = self.style.get_line_width(face_element=self.face_element) pen = asy_create_pens( edge_color=self.edge_color, face_color=self.face_color, stroke_width=l, is_face_element=self.face_element, ) command = "filldraw" if self.face_element else "draw" arc_path = create_arc_path(self.face_element, yscale) asy = f"""// ArcBox {command}({arc_path}, {pen});""" # print("### arcbox", asy) return asy
def filled_curve_box(self, **options) -> str: line_width = self.style.get_line_width(face_element=False) pen = asy_create_pens(edge_color=self.edge_color, stroke_width=line_width) if not pen: pen = "currentpen" def components(): for component in self.components: transformed = [(k, [xy.pos() for xy in p]) for k, p in component] yield "fill(%s--cycle, %s);" % ("".join(asy_bezier(*transformed)), pen) return "".join(components())
def insetbox(self, **options) -> str: x, y = self.pos.pos() content = self.content.boxes_to_tex(evaluation=self.graphics.evaluation) pen = asy_create_pens(edge_color=self.color) asy = 'label("$%s$", (%s,%s), (%s,%s), %s);' % ( content, x, y, -self.opos[0], -self.opos[1], pen, ) return asy
def bezier_curve_box(self, **options) -> str: """ Asymptote formatter for BezerCurveBox. """ line_width = self.style.get_line_width(face_element=False) pen = asy_create_pens(edge_color=self.edge_color, stroke_width=line_width) asy = "// BezierCurveBox\n" asy += asy_add_graph_import(self) asy += asy_add_bezier_fn(self) for i, line in enumerate(self.lines): pts = [str(xy.pos()) for xy in line] for j in range(1, len(pts) - 1, 3): triple = ", ".join(pts[j - 1 : j + 3]) asy += """pair[] P%d_%d={%s};\n""" % (i, j, triple) asy += """pair G%d_%d(real t){return Bezier(P%d_%d,t);}\n""" % (i, j, i, j) asy += """draw(shift(0, -2)*graph(G%d_%d,0,1,350), %s);\n""" % (i, j, pen) # print("BezierCurveBox: " asy) return asy
def polygonbox(self, **options) -> str: line_width = self.style.get_line_width(face_element=True) if self.vertex_colors is None: face_color = self.face_color else: face_color = None pens = asy_create_pens( edge_color=self.edge_color, face_color=face_color, stroke_width=line_width, is_face_element=True, ) asy = "" if self.vertex_colors is not None: paths = [] colors = [] edges = [] for index, line in enumerate(self.lines): paths.append( "--".join(["(%.5g,%.5g)" % coords.pos() for coords in line]) + "--cycle") # ignore opacity colors.append(",".join( [asy_color(color)[0] for color in self.vertex_colors[index]])) edges.append(",".join(["0"] + ["1"] * (len(self.vertex_colors[index]) - 1))) asy += "gouraudshade(%s, new pen[] {%s}, new int[] {%s});" % ( "^^".join(paths), ",".join(colors), ",".join(edges), ) if pens and pens != "nullpen": for line in self.lines: path = ( "--".join(["(%.5g,%.5g)" % coords.pos() for coords in line]) + "--cycle") asy += "filldraw(%s, evenodd+%s);" % (path, pens) # print("### polygonbox", asy) return asy
def pointbox(self, **options) -> str: point_size, _ = self.style.get_style(PointSize, face_element=False) if point_size is None: point_size = PointSize(self.graphics, value=DEFAULT_POINT_FACTOR) # We'll use the heuristic that the default line width is 1 should correspond # to the DEFAULT_POINT_FACTOR dotfactor = INVERSE_POINT_FACTOR * point_size.value pen = asy_create_pens(face_color=self.face_color, is_face_element=False, dotfactor=dotfactor) asy = "" for line in self.lines: for coords in line: asy += "dot(%s, %s);" % (coords.pos(), pen) # print("### pointbox", asy) return asy
def _roundbox(self, **options): x, y = self.c.pos() rx, ry = self.r.pos() rx -= x ry -= y line_width = self.style.get_line_width(face_element=self.face_element) pen = asy_create_pens( edge_color=self.edge_color, face_color=self.face_color, stroke_width=line_width, is_face_element=self.face_element, ) cmd = "filldraw" if self.face_element else "draw" return "%s(ellipse((%s,%s),%s,%s), %s);" % ( cmd, asy_number(x), asy_number(y), asy_number(rx), asy_number(ry), pen, )
def arrow3dbox(self, **options) -> str: """ Aymptote 3D formatter for Arrow3DBox """ # Set style parameters. pen = asy_create_pens(edge_color=self.edge_color, stroke_width=1) # Draw lines between all points except the last. lines_str = "--".join( ["({0},{1},{2})".format(*(coords.pos()[0])) for coords in self.lines[0][:-1]] ) asy = f"draw({lines_str}, {pen});\n" # Draw an arrow between the penultimate and the last point. last_line_str = "--".join( ["({0},{1},{2})".format(*(coords.pos()[0])) for coords in self.lines[0][-2:]] ) asy += f"draw(({last_line_str}), {pen}, Arrow3);\n" # print(asy) return asy
def polygon3dbox(self, **options) -> str: l = self.style.get_line_width(face_element=True) if self.vertex_colors is None: face_color = self.face_color else: face_color = None pen = asy_create_pens( edge_color=self.edge_color, face_color=face_color, stroke_width=l, is_face_element=True, ) asy = "" for line in self.lines: asy += ("path3 g=" + "--".join( ["(%.5g,%.5g,%.5g)" % coords.pos()[0] for coords in line]) + "--cycle;") asy += "draw(surface(g), %s);" % (pen) # print("### polygon3dbox", asy) return asy
def point3dbox(self, **options) -> str: """ Aymptote 3D formatter for Point3DBox """ face_color = self.face_color # Tempoary bug fix: default Point color should be black not white if list(face_color.to_rgba()[:3]) == [1, 1, 1]: face_color = RGBColor(components=(0, 0, 0, face_color.to_rgba()[3])) pen = asy_create_pens(face_color=face_color, is_face_element=False) points = [] for line in self.lines: point_coords = "--".join( "(%.5g,%.5g,%.5g)" % coords.pos()[0] for coords in line ) point = f"path3 g={point_coords}--cycle;dot(g, {pen});\n" points.append(point) asy = "// Point3DBox\n" + "\n".join(points) # print asy return asy
def rectanglebox(self, **options) -> str: line_width = self.style.get_line_width(face_element=True) x1, y1 = self.p1.pos() x2, y2 = self.p2.pos() pens = asy_create_pens( self.edge_color, self.face_color, line_width, is_face_element=True ) x1, x2, y1, y2 = asy_number(x1), asy_number(x2), asy_number(y1), asy_number(y2) asy = "// RectangleBox\n" asy += "filldraw((%s,%s)--(%s,%s)--(%s,%s)--(%s,%s)--cycle, %s);" % ( x1, y1, x2, y1, x2, y2, x1, y2, pens, ) # print("### rectanglebox", asy) return asy
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