def draw_circle(ax, x, y, color, scale=0.1, line_weight=1.0, zorder=2): path = Path(Path.unit_circle().vertices * scale, Path.unit_circle().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=zorder) ax.add_patch(patch) return
def draw_star(ax, x, y, color, scale=0.1): path = Path(unit_star.vertices * scale, unit_star.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
def draw_generic(ax, x, y, name, n_points=6, scale=0.1): unit_polygon = Path.unit_regular_polygon(n_points) path = Path(unit_polygon.vertices * scale, unit_polygon.codes) trans = matplotlib.transforms.Affine2D().translate(x, y) t_path = path.transformed(trans) name = TextPath((x - (0.35 * scale), y), s=name, size=2 * scale * .25) patch = patches.PathPatch(t_path, facecolor="white", lw=line_weight, zorder=2) a = ax.add_patch(patch) patch = patches.PathPatch(name, lw=line_weight, zorder=2) s = ax.add_patch(patch) ma = MonosaccharidePatch(saccharide_shape=(a,), saccharide_label=(s,)) return ma
def make_custom_marker(text, flip_y=False): from matplotlib.path import Path from matplotlib.textpath import TextPath from matplotlib.font_manager import FontProperties textPath = TextPath((0, 4), text, size=3) textPath = textPath.transformed(mpl.transforms.Affine2D().translate( -1 * len(text), 0)) circle = Path.unit_circle() triangle = Path([[0, 0], [1, 0], [0.5, 0.5], [0, 0]], [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY]) triangle = triangle.transformed(mpl.transforms.Affine2D().translate( -0.5, 0).scale(3, -4).translate(0, 3)) verts = np.concatenate( [circle.vertices, textPath.vertices, triangle.vertices]) codes = np.concatenate([circle.codes, textPath.codes, triangle.codes]) combined_marker = Path(verts, codes) combined_marker = combined_marker.transformed( mpl.transforms.Affine2D().scale(1000, -1000 if flip_y else 1000)) return combined_marker
def _render_patches(self, axes, aa_pixel_size=0, **kwargs): result = [] vertices = self.vertices scale_factors = np.linalg.norm(self.orientations, axis=-1)**2 if self.outline > 0: outer_vertices = vertices vertices = geometry.insetPolygon(vertices, self.outline) commands = [ Path.MOVETO ] + (vertices.shape[0] - 1) * [Path.LINETO] + [Path.CLOSEPOLY] commands = 2 * commands # reverse the inner vertices order to make an open # polygon. Duplicate the first vertex of each polygon to # close the shapes. outline_vertices = np.concatenate([ outer_vertices, outer_vertices[:1], vertices[::-1], vertices[:1] ], axis=0) outline_path = Path(outline_vertices, commands) patches = [] for (position, angle, scale) in zip(self.positions, self.angles, scale_factors): tf = Affine2D().scale(scale).rotate(angle).translate(*position) patches.append(PathPatch(outline_path.transformed(tf))) outline_colors = np.zeros_like(self.colors) outline_colors[:, 3] = self.colors[:, 3] result.append((patches, outline_colors)) vertices += np.sign(vertices) * aa_pixel_size patches = [] for (position, angle, scale) in zip(self.positions, self.angles, scale_factors): tf = Affine2D().scale(scale).rotate(angle).translate(*position) patches.append(Polygon(vertices, closed=True, transform=tf)) result.append((patches, self.colors)) return result
def __init__(self, id, position, direction, colour, t, y): super().__init__(id, position) self._direction = direction self._colour = colour # Scale y values so absolute maximum becomes 1 maxabs = y[np.argmax(abs(y))] if maxabs != 0.0: y /= maxabs xy = np.empty((len(t), 2), dtype=np.float_) xy[:, 0] = t xy[:, 1] = y # Create a simplified path, scaled to ensure it will have a resonable number of points scale = 1000.0/abs(t[1] - t[0]) xfm = Affine2D().scale(scale, scale) simplified = Path(xy).cleaned(transform=xfm, simplify=True) # Generate the SVG path, transforming back to the original data values self._path = _path.convert_to_string(simplified.transformed(xfm.inverted()), None, None, False, None, 6, [b'M', b'L', b'Q', b'C', b'z'], False).decode('ascii')
def agent(self, step: Step = None, agent_name: str = None, location: Location = None, rotation: float = None, color=None, size: float = 40.0, show_trajectory: bool = True, marker: Path = None): if step: agent_name = step.agent_name location = step.location rotation = step.rotation # if show_trajectory: # self.agents_trajectories.append(step) # x = self.agents_trajectories.get_agent_trajectory(agent_name).get("location").get("x") # y = self.agents_trajectories.get_agent_trajectory(agent_name).get("location").get("y") # self.agents[agent_name], = self.ax.plot(x, y, c=color) if not marker: if agent_name in self.agents_markers: marker = self.agents_markers[agent_name] else: if agent_name == "predator": marker = Agent_markers.robot() else: marker = Agent_markers.mouse() if agent_name not in self.agents: self.agents[agent_name], = self.ax.plot(location.x, location.y, marker=marker, c=color, markersize=size) t = Affine2D().rotate_deg_around(0, 0, -rotation) self.agents[agent_name].set_marker(marker.transformed(t)) self.agents[agent_name].set_xdata(location.x) self.agents[agent_name].set_ydata(location.y) self.agents[agent_name].set_color(color)
def draw_square(ax, x, y, color, scale=0.1): square_verts = np.array([ (0.5, 0.5), (0.5, -0.5), (-0.5, -0.5), (-0.5, 0.5), (0.5, 0.5), (0., 0.), ]) * 2 square_codes = [ Path.MOVETO, 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
def _render_patches(self, axes, aa_pixel_size=0, **kwargs): result = [] vertices = self.vertices # distance vector from each vertex to the next vertex in a given shape delta_verts = np.roll(vertices, -1, axis=0) - vertices delta_verts /= np.linalg.norm(delta_verts, axis=-1, keepdims=True) # the normal vector of the edge originating at each vertex vert_normals = np.transpose([delta_verts[:, 1], -delta_verts[:, 0]]) # start and end angles for the arc around each vertex, in degrees degree_ends = 180/np.pi*np.arctan2(vert_normals[..., 1], vert_normals[..., 0]) degree_starts = np.roll(degree_ends, 1, axis=0) radius = self.radius outline = self.outline delta_outline = radius - outline if outline > 0: # sketch out the outline, using the full radius commands = [Path.MOVETO] positions = [vertices[-1] + vert_normals[-1]*radius] for (vert, norm, degrees_start, degrees_end) in zip( vertices, np.roll(vert_normals, 1, axis=0), degree_starts, degree_ends): commands.append(Path.LINETO) positions.append(vert + norm*radius) arc = Path.arc(degrees_start, degrees_end) for (pos, cmd) in arc.iter_segments(): if cmd in {Path.STOP, Path.MOVETO}: continue pos = pos.reshape((-1, 2)) commands.extend(pos.shape[0]*[cmd]) positions.extend(vert[np.newaxis] + pos*radius) # repeat the outline path creation but in reverse to make # the hole for the colored portion of the shape commands.append(Path.MOVETO) positions.append(vertices[0] + vert_normals[-1]*delta_outline) for (vert, norm, degrees_end, degrees_start) in zip( vertices[::-1], vert_normals[::-1], degree_ends[::-1], degree_starts[::-1]): commands.append(Path.LINETO) positions.append(vert + norm*delta_outline) arc = Path.arc(degrees_start, degrees_end) for (pos, cmd) in reversed(list(arc.iter_segments())): if cmd in {Path.STOP, Path.MOVETO}: continue pos = pos.reshape((-1, 2))[::-1] commands.extend(pos.shape[0]*[cmd]) positions.extend(vert[np.newaxis] + pos*delta_outline) path = Path(positions, commands) patches = [] for (position, angle) in zip(self.positions, self.angles): tf = Affine2D().rotate(angle).translate(*position) patches.append(PathPatch(path.transformed(tf))) outline_colors = np.zeros_like(self.colors) outline_colors[:, 3] = self.colors[:, 3] result.append((patches, outline_colors)) vertices += np.sign(vertices)*aa_pixel_size # create the path for the filled/colored portion of the shape commands = [Path.MOVETO] positions = [vertices[-1] + vert_normals[-1]*delta_outline] for (vert, norm, degrees_start, degrees_end) in zip( vertices, np.roll(vert_normals, 1, axis=0), degree_starts, degree_ends): commands.append(Path.LINETO) positions.append(vert + norm*delta_outline) arc = Path.arc(degrees_start, degrees_end) for (pos, cmd) in arc.iter_segments(): if cmd in {Path.STOP, Path.MOVETO}: continue pos = pos.reshape((-1, 2))*delta_outline commands.extend(pos.shape[0]*[cmd]) positions.extend(vert[np.newaxis] + pos) path = Path(positions, commands) patches = [] for (position, angle) in zip(self.positions, self.angles): tf = Affine2D().rotate(angle).translate(*position) patches.append(PathPatch(path.transformed(tf))) result.append((patches, self.colors)) return result
def plot_fish(ax, fish, pos=(0, 0), direction=(1, 0), size=20.0, bend=0, scaley=1, bodykwargs={}, finkwargs={}): """ Plot body and fin of an electric fish. Parameters ---------- ax: matplotlib axes Axes where to draw the fish. fish: string or tuple or dict Specifies a fish to show: - any of the strings defining a shape contained in the `fish_shapes` dictionary, - a tuple with the name of the fish as the first element and 'top' or 'side' as the second element, - a dictionary with at least a 'body' key holding pathes to be drawn. pos: tuple of floats Coordinates of the fish's position (its center). direction: tuple of floats Coordinates of a vector defining the orientation of the fish. size: float Size of the fish. bend: float Bending angle of the fish's tail in degree. scaley: float Scale factor applied in y direction after bending and rotation to compensate for differently scaled axes. bodykwargs: dict Key-word arguments for PathPatch used to draw the fish's body. finkwargs: dict Key-word arguments for PathPatch used to draw the fish's fins. Returns ------- bpatch: matplotlib.patches.PathPatch The fish's body. Can be used for set_clip_path(). Example ------- ``` fig, ax = plt.subplots() bodykwargs=dict(lw=1, edgecolor='k', facecolor='k') finkwargs=dict(lw=1, edgecolor='k', facecolor='grey') fish = (('Eigenmannia', 'side'), (0, 0), (1, 0), 20.0, -25) plot_fish(ax, *fish, bodykwargs=bodykwargs, finkwargs=finkwargs) ax.set_xlim(-15, 15) ax.set_ylim(-10, 10) plt.show() ``` """ # retrieve fish shape: if not isinstance(fish, dict): if isinstance(fish, (tuple, list)): if fish[1] == 'top': fish = fish_top_shapes[fish[0]] else: fish = fish_side_shapes[fish[0]] else: fish = fish_shapes[fish] bpatch = None size_fac = 1.1 bbox = bbox_pathes(*fish.values()) for part, verts in fish.items(): verts = bend_path(verts, bend, size, size_fac) codes = np.zeros(len(verts)) codes[:] = Path.LINETO codes[0] = Path.MOVETO codes[-1] = Path.CLOSEPOLY path = Path(verts, codes) #pixelx = np.abs(np.diff(ax.get_window_extent().get_points()[:,0]))[0] #pixely = np.abs(np.diff(ax.get_window_extent().get_points()[:,1]))[0] #xmin, xmax = ax.get_xlim() #ymin, ymax = ax.get_ylim() #dxu = np.abs(xmax - xmin)/pixelx #dyu = np.abs(ymax - ymin)/pixely trans = mpl.transforms.Affine2D() angle = np.arctan2(direction[1], direction[0]) trans.rotate(angle) #trans.scale(dxu/dyu, dyu/dxu) # what is the right scaling???? trans.scale(1, scaley) trans.translate(*pos) path = path.transformed(trans) kwargs = bodykwargs if part == 'body' else finkwargs patch = PathPatch(path, **kwargs) if part == 'body': bpatch = patch ax.add_patch(patch) return bpatch