def fancy_marker(): codes = [] verts = [] rect = Path.unit_rectangle() for i, j in [(0.5, -0.5), (-0.5, 0.5), (-0.5, -1.5), (-1.5, -0.5)]: codes.append(rect.codes) verts.append(rect.vertices + np.asarray([i, j])[np.newaxis, :]) codes = np.concatenate(codes) verts = np.concatenate(verts, axis=0) path = Path(verts, codes) fig, ax = plt.subplots(figsize=(10, 10)) ax.plot(np.arange(10)**2, '--r', marker=path, markersize=30) return fig
'flag', 'prism', 'ocean', 'gist_earth', 'terrain', 'gist_stern', 'gnuplot', 'gnuplot2', 'CMRmap', 'cubehelix', 'brg', 'gist_rainbow', 'rainbow', 'jet', 'nipy_spectral', 'gist_ncar'])] colormaplist = sum([x[1] for x in colormap_from_reference],[]) # # Artist may not reture the same value as given by setter... # Also, matplotlib has a routine to get a list of valid property values. # With get_valid_values, it may be possible to implement more automatic # property list generation. # # The following is an initial attempt. Needs to check more how well # matplotlib is organized in this aspect. obj = PathCollection(Path.unit_rectangle()) #plinestylelist, plinestyle_rlist = artist_property_checker(obj, 'linestyle') pedgecolorlist, pedgecolor_rlist = artist_property_checker( obj, 'edgecolor', values="'"+"','".join(collist)+"'") plinestylelist = ['solid', 'dotted', 'dashed', 'dashdot'] plinestyle_rlist = ['solid', 'dotted', 'dashed', 'dashdot'] if isMPL2: plinestylelist = plinestylelist[:4] plinestyle_rlist = plinestyle_rlist[:4] def colormap_list(): return colormaplist def scratch_file():
def draw(self, renderer): """ Ellipses are normally drawn using an approximation that uses eight cubic bezier splines. The error of this approximation is 1.89818e-6, according to this unverified source: Lancaster, Don. Approximating a Circle or an Ellipse Using Four Bezier Cubic Splines. http://www.tinaja.com/glib/ellipse4.pdf There is a use case where very large ellipses must be drawn with very high accuracy, and it is too expensive to render the entire ellipse with enough segments (either splines or line segments). Therefore, in the case where either radius of the ellipse is large enough that the error of the spline approximation will be visible (greater than one pixel offset from the ideal), a different technique is used. In that case, only the visible parts of the ellipse are drawn, with each visible arc using a fixed number of spline segments (8). The algorithm proceeds as follows: 1. The points where the ellipse intersects the axes bounding box are located. (This is done be performing an inverse transformation on the axes bbox such that it is relative to the unit circle -- this makes the intersection calculation much easier than doing rotated ellipse intersection directly). This uses the "line intersecting a circle" algorithm from: Vince, John. Geometry for Computer Graphics: Formulae, Examples & Proofs. London: Springer-Verlag, 2005. 2. The angles of each of the intersection points are calculated. 3. Proceeding counterclockwise starting in the positive x-direction, each of the visible arc-segments between the pairs of vertices are drawn using the bezier arc approximation technique implemented in Path.arc(). """ if not hasattr(self, 'axes'): raise RuntimeError('Arcs can only be used in Axes instances') self._recompute_transform() # Get the width and height in pixels width = self.convert_xunits(self.width) height = self.convert_yunits(self.height) width, height = self.get_transform().transform_point( (width, height)) inv_error = (1.0 / 1.89818e-6) * 0.5 if width < inv_error and height < inv_error: self._path = Path.arc(self.theta1, self.theta2) return Patch.draw(self, renderer) def iter_circle_intersect_on_line(x0, y0, x1, y1): dx = x1 - x0 dy = y1 - y0 dr2 = dx*dx + dy*dy D = x0*y1 - x1*y0 D2 = D*D discrim = dr2 - D2 # Single (tangential) intersection if discrim == 0.0: x = (D*dy) / dr2 y = (-D*dx) / dr2 yield x, y elif discrim > 0.0: # The definition of "sign" here is different from # np.sign: we never want to get 0.0 if dy < 0.0: sign_dy = -1.0 else: sign_dy = 1.0 sqrt_discrim = np.sqrt(discrim) for sign in (1., -1.): x = (D*dy + sign * sign_dy * dx * sqrt_discrim) / dr2 y = (-D*dx + sign * np.abs(dy) * sqrt_discrim) / dr2 yield x, y def iter_circle_intersect_on_line_seg(x0, y0, x1, y1): epsilon = 1e-9 if x1 < x0: x0e, x1e = x1, x0 else: x0e, x1e = x0, x1 if y1 < y0: y0e, y1e = y1, y0 else: y0e, y1e = y0, y1 x0e -= epsilon y0e -= epsilon x1e += epsilon y1e += epsilon for x, y in iter_circle_intersect_on_line(x0, y0, x1, y1): if x >= x0e and x <= x1e and y >= y0e and y <= y1e: yield x, y # Transforms the axes box_path so that it is relative to the unit # circle in the same way that it is relative to the desired # ellipse. box_path = Path.unit_rectangle() box_path_transform = transforms.BboxTransformTo(self.axes.bbox) + \ self.get_transform().inverted() box_path = box_path.transformed(box_path_transform) PI = np.pi TWOPI = PI * 2.0 RAD2DEG = 180.0 / PI DEG2RAD = PI / 180.0 theta1 = self.theta1 theta2 = self.theta2 thetas = {} # For each of the point pairs, there is a line segment for p0, p1 in zip(box_path.vertices[:-1], box_path.vertices[1:]): x0, y0 = p0 x1, y1 = p1 for x, y in iter_circle_intersect_on_line_seg(x0, y0, x1, y1): theta = np.arccos(x) if y < 0: theta = TWOPI - theta # Convert radians to angles theta *= RAD2DEG if theta > theta1 and theta < theta2: thetas[theta] = None thetas = thetas.keys() thetas.sort() thetas.append(theta2) last_theta = theta1 theta1_rad = theta1 * DEG2RAD inside = box_path.contains_point((np.cos(theta1_rad), np.sin(theta1_rad))) for theta in thetas: if inside: self._path = Path.arc(last_theta, theta, 8) Patch.draw(self, renderer) inside = False else: inside = True last_theta = theta
def get_path(self): """ Return the vertices of the rectangle """ return Path.unit_rectangle()
def draw(self, renderer): """ Ellipses are normally drawn using an approximation that uses eight cubic bezier splines. The error of this approximation is 1.89818e-6, according to this unverified source: Lancaster, Don. Approximating a Circle or an Ellipse Using Four Bezier Cubic Splines. http://www.tinaja.com/glib/ellipse4.pdf There is a use case where very large ellipses must be drawn with very high accuracy, and it is too expensive to render the entire ellipse with enough segments (either splines or line segments). Therefore, in the case where either radius of the ellipse is large enough that the error of the spline approximation will be visible (greater than one pixel offset from the ideal), a different technique is used. In that case, only the visible parts of the ellipse are drawn, with each visible arc using a fixed number of spline segments (8). The algorithm proceeds as follows: 1. The points where the ellipse intersects the axes bounding box are located. (This is done be performing an inverse transformation on the axes bbox such that it is relative to the unit circle -- this makes the intersection calculation much easier than doing rotated ellipse intersection directly). This uses the "line intersecting a circle" algorithm from: Vince, John. Geometry for Computer Graphics: Formulae, Examples & Proofs. London: Springer-Verlag, 2005. 2. The angles of each of the intersection points are calculated. 3. Proceeding counterclockwise starting in the positive x-direction, each of the visible arc-segments between the pairs of vertices are drawn using the bezier arc approximation technique implemented in Path.arc(). """ if not hasattr(self, 'axes'): raise RuntimeError('Arcs can only be used in Axes instances') self._recompute_transform() # Get the width and height in pixels width = self.convert_xunits(self.width) height = self.convert_yunits(self.height) width, height = self.get_transform().transform_point( (width, height)) inv_error = (1.0 / 1.89818e-6) * 0.5 if width < inv_error and height < inv_error: self._path = Path.arc(self.theta1, self.theta2) return Patch.draw(self, renderer) def iter_circle_intersect_on_line(x0, y0, x1, y1): dx = x1 - x0 dy = y1 - y0 dr2 = dx*dx + dy*dy D = x0*y1 - x1*y0 D2 = D*D discrim = dr2 - D2 # Single (tangential) intersection if discrim == 0.0: x = (D*dy) / dr2 y = (-D*dx) / dr2 yield x, y elif discrim > 0.0: # The definition of "sign" here is different from # npy.sign: we never want to get 0.0 if dy < 0.0: sign_dy = -1.0 else: sign_dy = 1.0 sqrt_discrim = npy.sqrt(discrim) for sign in (1., -1.): x = (D*dy + sign * sign_dy * dx * sqrt_discrim) / dr2 y = (-D*dx + sign * npy.abs(dy) * sqrt_discrim) / dr2 yield x, y def iter_circle_intersect_on_line_seg(x0, y0, x1, y1): epsilon = 1e-9 if x1 < x0: x0e, x1e = x1, x0 else: x0e, x1e = x0, x1 if y1 < y0: y0e, y1e = y1, y0 else: y0e, y1e = y0, y1 x0e -= epsilon y0e -= epsilon x1e += epsilon y1e += epsilon for x, y in iter_circle_intersect_on_line(x0, y0, x1, y1): if x >= x0e and x <= x1e and y >= y0e and y <= y1e: yield x, y # Transforms the axes box_path so that it is relative to the unit # circle in the same way that it is relative to the desired # ellipse. box_path = Path.unit_rectangle() box_path_transform = transforms.BboxTransformTo(self.axes.bbox) + \ self.get_transform().inverted() box_path = box_path.transformed(box_path_transform) PI = npy.pi TWOPI = PI * 2.0 RAD2DEG = 180.0 / PI DEG2RAD = PI / 180.0 theta1 = self.theta1 theta2 = self.theta2 thetas = {} # For each of the point pairs, there is a line segment for p0, p1 in zip(box_path.vertices[:-1], box_path.vertices[1:]): x0, y0 = p0 x1, y1 = p1 for x, y in iter_circle_intersect_on_line_seg(x0, y0, x1, y1): theta = npy.arccos(x) if y < 0: theta = TWOPI - theta # Convert radians to angles theta *= RAD2DEG if theta > theta1 and theta < theta2: thetas[theta] = None thetas = thetas.keys() thetas.sort() thetas.append(theta2) last_theta = theta1 theta1_rad = theta1 * DEG2RAD inside = box_path.contains_point((npy.cos(theta1_rad), npy.sin(theta1_rad))) for theta in thetas: if inside: self._path = Path.arc(last_theta, theta, 8) Patch.draw(self, renderer) inside = False else: inside = True last_theta = theta
"Simple", "Wedge","BarAB"] arrowstylelist = ["-","->","-[","-|>","<-","<->", "<|-","<|-|>","]-","]-[", "fancy", "simple", "wedge","|-|"] colormaplist=sorted(m for m in matplotlib.cm.datad if not m.endswith('_r')) # # Artist may not reture the same value as given by setter... # Also, matplotlib has a routine to get a list of valid property values. # With get_valid_values, it may be possible to implement more automatic # property list generation. # # The following is an initial attempt. Needs to check more how well # matplotlib is organized in this aspect. obj = PathCollection(Path.unit_rectangle()) plinestylelist, plinestyle_rlist = artist_property_checker(obj, 'linestyle') pedgecolorlist, pedgecolor_rlist = artist_property_checker(obj, 'edgecolor', values = "'"+"','".join(collist)+"'") if isMPL2: plinestylelist = plinestylelist[:4] plinestyle_rlist = plinestyle_rlist[:4] def colormap_list(): return colormaplist def scratch_file(): return scratch def color_list(): return collist def color_list_face(): return collistf
Path.LINETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY, ] path = Path(square_verts * scale, square_codes) trans = matplotlib.transforms.Affine2D().translate(x, y) t_path = path.transformed(trans) patch = patches.PathPatch(t_path, facecolor=color.value, lw=line_weight, zorder=2) a = ax.add_patch(patch) ma = MonosaccharidePatch(saccharide_shape=(a,)) return ma # return (a,) draw_map[ResidueShape.square] = draw_square unit_rectangle = Path.unit_rectangle() def draw_triangle(ax, x, y, color, scale=0.1): path = Path(unit_triangle.vertices * scale, unit_triangle.codes) trans = matplotlib.transforms.Affine2D().translate(x, y).rotate_deg_around(x, y, -90) t_path = path.transformed(trans) patch = patches.PathPatch(t_path, facecolor=color.value, lw=line_weight, zorder=2) a = ax.add_patch(patch) ma = MonosaccharidePatch(saccharide_shape=(a,)) return ma # return (a,) draw_map[ResidueShape.triangle] = draw_triangle unit_triangle = Path.unit_regular_polygon(3)