Source code for plato.draw.matplotlib.Spheropolygons

from matplotlib.path import Path
from matplotlib.patches import PathPatch
from matplotlib.transforms import Affine2D
import numpy as np

from ... import draw
from ... import mesh
from .internal import PatchUser

[docs]class Spheropolygons(draw.Spheropolygons, PatchUser): __doc__ = draw.Spheropolygons.__doc__ def _render_patches(self, axes, aa_pixel_size=0, **kwargs): result = [] (shape_positions, shape_angles, shape_colors) = mesh.unfoldProperties([ self.positions, self.angles, self.colors]) 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(shape_positions, shape_angles): tf = Affine2D().rotate(angle).translate(*position) patches.append(PathPatch(path.transformed(tf))) outline_colors = np.zeros_like(shape_colors) outline_colors[:, 3] = shape_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(shape_positions, shape_angles): tf = Affine2D().rotate(angle).translate(*position) patches.append(PathPatch(path.transformed(tf))) result.append((patches, shape_colors)) return result