def edge_mesh(sheet, coords, **edge_specs): """ Creates a ipyvolume Mesh of the edge lines to be displayed in Jupyter Notebooks Returns ------- mesh: a :class:`ipyvolume.widgets.Mesh` mesh widget """ spec = sheet_spec()["edge"] spec.update(**edge_specs) if callable(spec["color"]): spec["color"] = spec["color"](sheet) if isinstance(spec["color"], str): color = spec["color"] elif hasattr(spec["color"], "__len__"): color = _wire_color_from_sequence(spec, sheet)[:, :3] u, v, w = coords mesh = ipv.Mesh( x=sheet.vert_df[u], y=sheet.vert_df[v], z=sheet.vert_df[w], lines=sheet.edge_df[["srce", "trgt"]].astype(dtype=np.uint32), color=color, ) return mesh
def plot_trisurf(x, y, z, triangles=None, lines=None, color=default_color, u=None, v=None, texture=None): """Draws a polygon/triangle mesh defined by a coordinate and triangle indices The following example plots a rectangle in the z==2 plane, consisting of 2 triangles: >>> plot_trisurf([0, 0, 3., 3.], [0, 4., 0, 4.], 2, triangles=[[0, 2, 3], [0, 3, 1]]) Note that the z value is constant, and thus not a list/array. For guidance, the triangles refer to the vertices in this manner:: ^ ydir | 2 3 0 1 ---> x dir Note that if you want per face/triangle colors, you need to duplicate each vertex. :param x: {x} :param y: {y} :param z: {z} :param triangles: numpy array with indices referring to the vertices, defining the triangles, with shape (M, 3) :param lines: numpy array with indices referring to the vertices, defining the lines, with shape (K, 2) :param color: {color} :param u: {u} :param v: {v} :param texture: {texture} :return: :any:`Mesh` """ fig = gcf() if triangles is not None: triangles = np.array(triangles).astype(dtype=np.uint32) if lines is not None: lines = np.array(lines).astype(dtype=np.uint32) mesh = ipv.Mesh(x=x, y=y, z=z, triangles=triangles, lines=lines, color=color, u=u, v=v, texture=texture) _grow_limits( np.array(x).reshape(-1), np.array(y).reshape(-1), np.array(z).reshape(-1)) fig.meshes = fig.meshes + [mesh] return mesh
def face_mesh(sheet, coords, **face_draw_specs): """ Creates a ipyvolume Mesh of the face polygons """ Ne, Nf = sheet.Ne, sheet.Nf if callable(face_draw_specs["color"]): face_draw_specs["color"] = face_draw_specs["color"](sheet) if isinstance(face_draw_specs["color"], str): color = face_draw_specs["color"] elif hasattr(face_draw_specs["color"], "__len__"): color = _face_color_from_sequence(face_draw_specs, sheet)[:, :3] if "visible" in sheet.face_df.columns: edges = sheet.edge_df[sheet.upcast_face( sheet.face_df["visible"])].index _sheet = get_sub_eptm(sheet, edges) if _sheet is not None: sheet = _sheet if isinstance(color, np.ndarray): faces = sheet.face_df["face_o"].values.astype(np.uint32) edges = edges.values.astype(np.uint32) indexer = np.concatenate([faces, edges + Nf, edges + Ne + Nf]) color = color.take(indexer, axis=0) epsilon = face_draw_specs.get("epsilon", 0) up_srce = sheet.edge_df[["s" + c for c in coords]] up_trgt = sheet.edge_df[["t" + c for c in coords]] Ne, Nf = sheet.Ne, sheet.Nf if epsilon > 0: up_face = sheet.edge_df[["f" + c for c in coords]].values up_srce = (up_srce - up_face) * (1 - epsilon) + up_face up_trgt = (up_trgt - up_face) * (1 - epsilon) + up_face mesh_ = np.concatenate( [sheet.face_df[coords].values, up_srce.values, up_trgt.values]) triangles = np.vstack( [sheet.edge_df["face"], np.arange(Ne) + Nf, np.arange(Ne) + Ne + Nf]).T.astype(dtype=np.uint32) mesh = ipv.Mesh(x=mesh_[:, 0], y=mesh_[:, 1], z=mesh_[:, 2], triangles=triangles, color=color) return mesh
def _default_plotter(self, **kwargs): """ Basic plot function to be used if no custom function is specified. This is called by plot, you shouldn't call it directly. """ self.lengths = np.array([length(x) for x in self.lines2use_]) if not ("grayscale" in kwargs and kwargs["grayscale"]): self.colors = np.array([color(x) for x in self.lines2use_]) else: self.colors = np.zeros((len(self.lines2use_), 3), dtype=np.float16) self.colors[:] = [0.5, 0.5, 0.5] self.state = {"threshold": 0, "indices": []} width = 600 height = 600 perc = 80 if "width" in kwargs: width = kwargs["width"] if "height" in kwargs: height = kwargs["height"] if "percentile" in kwargs: perc = kwargs["percentile"] ipv.clear() fig = ipv.figure(width=width, height=height) self.state["fig"] = fig with fig.hold_sync(): x, y, z, indices, colors, self.line_pointers = self._create_mesh() limits = np.array( [ min([x.min(), y.min(), z.min()]), max([x.max(), y.max(), z.max()]), ] ) mesh = ipv.Mesh(x=x, y=y, z=z, lines=indices, color=colors) fig.meshes = [mesh] if "style" not in kwargs: fig.style = { "axes": { "color": "black", "label": {"color": "black"}, "ticklabel": {"color": "black"}, "visible": False, }, "background-color": "white", "box": {"visible": False}, } else: fig.style = kwargs["style"] ipv.pylab._grow_limits(limits, limits, limits) fig.camera_fov = 1 ipv.show() interact( self._plot_lines, state=fixed(self.state), threshold=widgets.FloatSlider( value=np.percentile(self.lengths, perc), min=self.lengths.min() - 1, max=self.lengths.max() - 1, continuous_update=False, ), )
def plot_mesh(x, y, z, color=default_color, wireframe=True, surface=True, wrapx=False, wrapy=False, u=None, v=None, texture=None): """Draws a 2d wireframe+surface in 3d: generalization of :any:`plot_wireframe` and :any:`plot_surface` :param x: {x2d} :param y: {y2d} :param z: {z2d} :param color: {color2d} :param bool wireframe: draw lines between the vertices :param bool surface: draw faces/triangles between the vertices :param bool wrapx: when True, the x direction is assumed to wrap, and polygons are drawn between the begin and end points :param boool wrapy: idem for y :param u: {u} :param v: {v} :param texture: {texture} :return: :any:`Mesh` """ fig = gcf() # assert len(x.shape) == 2 # assert len(y.shape) == 2 # assert len(z.shape) == 2 # if isinstance(color, np.ndarray): # assert len(color.shape) == 3 # assert color.shape[:2] == x.shape # color = color.reshape(-1) def dim(x): d = 0 el = x while True: try: el = el[0] d += 1 except: break return d if dim(x) == 2: nx, ny = shape = x.shape else: nx, ny = shape = x[0].shape # assert len(x.shape) == 2, "Array x must be 2 dimensional." # assert len(y.shape) == 2, "Array y must be 2 dimensional." # assert len(z.shape) == 2, "Array z must be 2 dimensional." # assert x.shape == y.shape, "Arrays x and y must have same shape." # assert y.shape == z.shape, "Arrays y and z must have same shape." # convert x, y, z from shape (nx, ny) to (nx * ny) or # (frame, nx, ny) to (frame, nx*ny) def reshape(ar): if dim(ar) == 3: return [k.reshape(-1) for k in ar] else: return ar.reshape(-1) x = reshape(x) y = reshape(y) z = reshape(z) # similar for texture coordinates if u is not None: u = reshape(u) if v is not None: v = reshape(v) # convert color from shape (nx, ny, {3,4}) to (nx * ny, {3, 4}) or # (frame, nx, ny, {3,4}) to (frame, nx*ny, {3,4}) def reshape_color(ar): if dim(ar) == 4: return [k.reshape(-1, k.shape[-1]) for k in ar] else: return ar.reshape(-1, ar.shape[-1]) if isinstance(color, np.ndarray): color = reshape_color(color) _grow_limits(np.array(x).reshape(-1), np.array(y).reshape(-1), np.array(z).reshape(-1)) triangles, lines = _make_triangles_lines((nx,ny) ,wrapx,wrapy) mesh = ipv.Mesh(x=x, y=y, z=z, triangles=triangles if surface else None, color=color, lines=lines if wireframe else None, u=u, v=v, texture=texture) fig.meshes = fig.meshes + [mesh] return mesh
def plot_mesh(x, y, z, color=default_color, wireframe=True, surface=True, wrapx=False, wrapy=False, u=None, v=None, texture=None): fig = gcf() # assert len(x.shape) == 2 # assert len(y.shape) == 2 # assert len(z.shape) == 2 # if isinstance(color, np.ndarray): # assert len(color.shape) == 3 # assert color.shape[:2] == x.shape # color = color.reshape(-1) def dim(x): d = 0 el = x while True: try: el = el[0] d += 1 except: break return d if dim(x) == 2: nx, ny = shape = x.shape else: nx, ny = shape = x[0].shape def reshape(ar): if dim(ar) == 3: return [k.reshape(-1) for k in ar] else: return ar.reshape(-1) def reshape_color(ar): if dim(ar) == 4: return [k.reshape(-1, 3) for k in ar] else: return ar.reshape(-1, 3) if isinstance(color, np.ndarray): # if dim(color) == 4: # color = color.reshape((color.shape[0], -1, color.shape[-1])) color = reshape_color(color) # print(color.shape) x = reshape(x) y = reshape(y) z = reshape(z) if u is not None: u = reshape(u) if v is not None: v = reshape(v) _grow_limits( np.array(x).reshape(-1), np.array(y).reshape(-1), np.array(z).reshape(-1)) mx = nx if wrapx else nx - 1 my = ny if wrapy else ny - 1 triangles = np.zeros(((mx) * (my) * 2, 3), dtype=np.uint32) lines = np.zeros(((mx) * (my) * 4, 2), dtype=np.uint32) def index_from2d(i, j): xi = (i % nx) yi = (j % ny) return nx * xi + yi """ ^ ydir | 2 3 0 1 ---> x dir """ for i in range(mx): for j in range(my): p0 = index_from2d(i, j) p1 = index_from2d(i + 1, j) p2 = index_from2d(i, j + 1) p3 = index_from2d(i + 1, j + 1) triangle_index = (i * mx) + j triangles[triangle_index * 2 + 0, :] = [p0, p1, p3] triangles[triangle_index * 2 + 1, :] = [p0, p3, p2] lines[triangle_index * 4 + 0, :] = [p0, p1] lines[triangle_index * 4 + 1, :] = [p0, p2] lines[triangle_index * 4 + 2, :] = [p2, p3] lines[triangle_index * 4 + 3, :] = [p1, p3] # print(i, j, p0, p1, p2, p3) mesh = ipv.Mesh(x=x, y=y, z=z, triangles=triangles if surface else None, color=color, lines=lines if wireframe else None, u=u, v=v, texture=texture) fig.meshes = fig.meshes + [mesh] return mesh
def plot_mesh(x, y, z, color=default_color, wireframe=True, surface=True, wrapx=False, wrapy=False, u=None, v=None, texture=None): """Draws a 2d wireframe+surface in 3d: generalization of :any:`plot_wireframe` and :any:`plot_surface` :param x: {x2d} :param y: {y2d} :param z: {z2d} :param color: {color2d} :param bool wireframe: draw lines between the vertices :param bool surface: draw faces/triangles between the vertices :param bool wrapx: when True, the x direction is assumed to wrap, and polygons are drawn between the begin and end points :param boool wrapy: idem for y :param u: {u} :param v: {v} :param texture: {texture} :return: :any:`Mesh` """ fig = gcf() # assert len(x.shape) == 2 # assert len(y.shape) == 2 # assert len(z.shape) == 2 # if isinstance(color, np.ndarray): # assert len(color.shape) == 3 # assert color.shape[:2] == x.shape # color = color.reshape(-1) def dim(x): d = 0 el = x while True: try: el = el[0] d += 1 except: break return d if dim(x) == 2: nx, ny = shape = x.shape else: nx, ny = shape = x[0].shape def reshape(ar): if dim(ar) == 3: return [k.reshape(-1) for k in ar] else: return ar.reshape(-1) def reshape_color(ar): if dim(ar) == 4: return [k.reshape(-1, 3) for k in ar] else: return ar.reshape(-1, 3) if isinstance(color, np.ndarray): # if dim(color) == 4: # color = color.reshape((color.shape[0], -1, color.shape[-1])) color = reshape_color(color) # print(color.shape) x = reshape(x) y = reshape(y) z = reshape(z) if u is not None: u = reshape(u) if v is not None: v = reshape(v) _grow_limits( np.array(x).reshape(-1), np.array(y).reshape(-1), np.array(z).reshape(-1)) mx = nx if wrapx else nx - 1 my = ny if wrapy else ny - 1 triangles = np.zeros(((mx) * (my) * 2, 3), dtype=np.uint32) lines = np.zeros(((mx) * (my) * 4, 2), dtype=np.uint32) def index_from2d(i, j): xi = (i % nx) yi = (j % ny) return nx * xi + yi """ ^ ydir | 2 3 0 1 ---> x dir """ for i in range(mx): for j in range(my): p0 = index_from2d(i, j) p1 = index_from2d(i + 1, j) p2 = index_from2d(i, j + 1) p3 = index_from2d(i + 1, j + 1) triangle_index = (i * mx) + j triangles[triangle_index * 2 + 0, :] = [p0, p1, p3] triangles[triangle_index * 2 + 1, :] = [p0, p3, p2] lines[triangle_index * 4 + 0, :] = [p0, p1] lines[triangle_index * 4 + 1, :] = [p0, p2] lines[triangle_index * 4 + 2, :] = [p2, p3] lines[triangle_index * 4 + 3, :] = [p1, p3] # print(i, j, p0, p1, p2, p3) mesh = ipv.Mesh(x=x, y=y, z=z, triangles=triangles if surface else None, color=color, lines=lines if wireframe else None, u=u, v=v, texture=texture) fig.meshes = fig.meshes + [mesh] return mesh
def _default_plotter(self, **kwargs): """ Basic plot function to be used if no custom function is specified. This is called by plot, you shouldn't call it directly. """ self.lengths = np.array([length(x) for x in self.lines2use_]) if not ('grayscale' in kwargs and kwargs['grayscale']): self.colors = np.array([color(x) for x in self.lines2use_]) else: self.colors = np.zeros((len(self.lines2use_), 3), dtype=np.float16) self.colors[:] = [0.5, 0.5, 0.5] self.state = {'threshold': 0, 'indices': []} width = 600 height = 600 perc = 80 if 'width' in kwargs: width = kwargs['width'] if 'height' in kwargs: height = kwargs['height'] if 'percentile' in kwargs: perc = kwargs['percentile'] ipv.clear() fig = ipv.figure(width=width, height=height) self.state['fig'] = fig with fig.hold_sync(): x, y, z, indices, colors, self.line_pointers = self._create_mesh() limits = np.array([ min([x.min(), y.min(), z.min()]), max([x.max(), y.max(), z.max()]) ]) mesh = ipv.Mesh(x=x, y=y, z=z, lines=indices, color=colors) fig.meshes = [mesh] if 'style' not in kwargs: fig.style = { 'axes': { 'color': 'black', 'label': { 'color': 'black' }, 'ticklabel': { 'color': 'black' }, 'visible': False }, 'background-color': 'white', 'box': { 'visible': False } } else: fig.style = kwargs['style'] ipv.pylab._grow_limits(limits, limits, limits) fig.camera_fov = 1 ipv.show() interact(self._plot_lines, state=fixed(self.state), threshold=widgets.FloatSlider(value=np.percentile( self.lengths, perc), min=self.lengths.min() - 1, max=self.lengths.max() - 1, continuous_update=False))