def segment_surface(v, sindex, descrip, color): from chimerax.core.models import Surface s = Surface('segment %d %s' % (sindex, descrip), v.session) va, na, ta = segment_surface_geometry(v, sindex) s.set_geometry(va, na, ta) s.color = segment_color(color) return s
def _transform_schematic(session, transform, center, from_rgba, to_rgba, length, width, thickness): axis, rot_center, angle_deg, shift = transform.axis_center_angle_shift() # Align rot_center at same position along axis as center. from chimerax.geometry import inner_product rot_center += inner_product(center - rot_center, axis) * axis width_axis = center - rot_center varray, narray, tarray = _axis_square(axis, rot_center, width_axis, length, width, thickness) from chimerax.core.models import Model, Surface s1 = Surface('slab 1', session) s1.set_geometry(varray, narray, tarray) s1.color = from_rgba s2 = Surface('slab 2', session) from chimerax.geometry import rotation, translation rot2 = translation(shift * axis) * rotation( axis, angle_deg, center=rot_center) varray2 = rot2 * varray narray2 = rot2.transform_vectors(narray) s2.set_geometry(varray2, narray2, tarray) s2.color = to_rgba m = Model('transform schematic', session) m.add([s1, s2]) return m
def surfaces_from_nodes(nodes, color, place, instances, session): from collada.scene import GeometryNode, Node from chimerax.geometry import Place from chimerax.core.models import Surface splist = [] for n in nodes: if isinstance(n, GeometryNode): materials = dict((m.symbol, m.target) for m in n.materials) g = n.geometry colors = g.sourceById if g.id in instances: add_geometry_instance(g.primitives, instances[g.id], place, color, materials) else: instances[g.id] = spieces = geometry_node_surfaces( g.primitives, place, color, materials, colors, session) splist.extend(spieces) elif isinstance(n, Node): pl = place * Place(n.matrix[:3, :]) spieces = surfaces_from_nodes(n.children, color, pl, instances, session) name = n.xmlnode.get('name') if len(spieces) > 1: m = Surface(name, session) m.add(spieces) splist.append(m) elif len(spieces) == 1: s = spieces[0] s.name = name splist.append(s) return splist
def update_selection(self, *, fire_trigger=True): asel = self.atoms.selected tmask = self._atom_triangle_mask(asel) if tmask is None: sel_val = False else: sel_val = (tmask.sum() > 0) Surface.set_selected(self, sel_val, fire_trigger=fire_trigger) self.highlighted_triangles_mask = tmask
def spherical_surface( session, radius_function, move=None, # a Place instance to rotate and translate surface theta_steps=100, phi_steps=50, positive_color=(255, 100, 100, 255), # red, green, blue, alpha, 0-255 negative_color=(100, 100, 255, 255)): # Compute vertices and vertex colors vertices = [] colors = [] for t in range(theta_steps): theta = (t / theta_steps) * 2 * pi ct, st = cos(theta), sin(theta) for p in range(phi_steps): phi = (p / (phi_steps - 1)) * pi cp, sp = cos(phi), sin(phi) r = radius_function(theta, phi) xyz = (r * sp * ct, r * sp * st, r * cp) vertices.append(xyz) color = positive_color if r >= 0 else negative_color colors.append(color) # Compute triangles, triples of vertex indices triangles = [] for t in range(theta_steps): for p in range(phi_steps - 1): i = t * phi_steps + p t1 = (t + 1) % theta_steps i1 = t1 * phi_steps + p triangles.append((i, i + 1, i1 + 1)) triangles.append((i, i1 + 1, i1)) # Create numpy arrays from numpy import array, float32, uint8, int32 va = array(vertices, float32) ca = array(colors, uint8) ta = array(triangles, int32) # Rotate and translate vertices to a new location. if move is not None: move.transform_points(va, in_place=True) # Compute average vertex normal vectors from chimerax.surface import calculate_vertex_normals na = calculate_vertex_normals(va, ta) # Create ChimeraX surface model from chimerax.core.models import Surface s = Surface('surface', session) s.set_geometry(va, na, ta) s.vertex_colors = ca session.models.add([s]) return s
def triangles_model(session, points, triangles, name='vtk polygons', color=(180, 180, 180, 255)): from chimerax.core.models import Surface m = Surface(name, session) from chimerax import surface normals = surface.calculate_vertex_normals(points, triangles) m.set_geometry(points, normals, triangles) m.color = color return m
def new_cap(drawing, cap_name): from chimerax.core.models import Model, Surface if isinstance(drawing, Model): # Make cap a model when capping a model so color can be set by command. c = Surface(cap_name, drawing.session) c.display_style = drawing.display_style c.SESSION_SAVE = False drawing.add([c]) else: # Cap is on a Drawing that is not a Model c = drawing.new_drawing(cap_name) c.is_clip_cap = True return c
def _cage_surface(session, name, replace): # Make new surface model or find an existing one. sm = None from chimerax.core.models import Surface if replace: mlist = [m for m in session.models.list(type=Surface) if hasattr(m, 'hkcage')] if mlist: sm = mlist[0] sm.name = name if sm is None: sm = Surface(name, session) sm.hkcage = True return sm
def restore_snapshot(session, data): d = data s = MolecularSurface(session, d['atoms'], d['show_atoms'], d['probe_radius'], d['grid_spacing'], d['resolution'], d['level'], d['name'], d['color'], d['visible_patches'], d['sharp_boundaries']) Surface.set_state_from_snapshot(s, session, d['model state']) geom_attrs = ('vertices', 'normals', 'triangles') s.set_geometry(d['vertices'], d['normals'], d['triangles']) for attr in MolecularSurface._save_attrs: if attr in d and attr not in geom_attrs: setattr(s, attr, d[attr]) return s
def ellipsoid_surface(axes, lengths, center, color, surface, submodel_name = None, num_triangles = 1000): xf = surface.position.inverse() sa, sc = transform_ellipsoid(axes, center, xf) varray, narray, tarray = ellipsoid_geometry(sc, sa, lengths, num_triangles = num_triangles) if submodel_name is None: s = surface else: from chimerax.core.models import Surface s = Surface(submodel_name, surface.session) surface.add([s]) s.set_geometry(varray, narray, tarray) s.color = color return s
def _surface_model(name, place, model_id, replace, session): from chimerax.core.models import Surface if not model_id is None: slist = session.models.list(model_id = model_id, type = Surface) if slist: s = slist[0] if replace: session.models.close([s]) else: return s s = Surface(name, session) s.id = model_id s.position = place session.models.add([s]) return s
def load_surface(session, tensor_file, sc=2.09, theta_steps=100, phi_steps=50, positive_color=(255, 100, 100, 255), negative_color=(100, 100, 255, 255), marker_pos_color=(100, 255, 100, 255), marker_neg_color=(255, 255, 100, 255)): Delta, Eta, Euler, Pos, Marker = load_tensor(tensor_file) from chimerax.core.models import Surface from chimerax.surface import calculate_vertex_normals, combine_geometry_vntc geom = list() for k, (delta, eta, euler, pos, marker) in enumerate(zip(Delta, Eta, Euler, Pos, Marker)): if marker == 1: pc = marker_pos_color nc = marker_neg_color else: pc = positive_color nc = negative_color xyz,tri,colors=spherical_surface(\ delta=delta,eta=eta,euler=euler,pos=pos,\ sc=sc,theta_steps=theta_steps,\ phi_steps=phi_steps,\ positive_color=pc,\ negative_color=nc) norm_vecs = calculate_vertex_normals(xyz, tri) geom.append((xyz, norm_vecs, tri, colors)) xyz, norm_vecs, tri, colors = combine_geometry_vntc(geom) s = Surface('surface', session) s.set_geometry(xyz, norm_vecs, tri) s.vertex_colors = colors session.models.add([s]) return s
def mesh_models(session, seg): surfs = [] from chimerax.core.models import Surface from chimerax.surface import combine_geometry_vnt for segment in seg.segments: if segment.mesh_list is None: continue geoms = [mesh_geometry(mesh, seg) for mesh in segment.mesh_list] if len(geoms) == 0: continue va, na, ta = combine_geometry_vnt(geoms) s = Surface('mesh %d' % segment.id, session) s.set_geometry(va, na, ta) # s.display_style = s.Mesh # s.use_lighting = False s.color = segment_color(segment.colour) surfs.append(s) return surfs
def take_snapshot(self, session, flags): init_attrs = ('atoms', 'show_atoms', 'probe_radius', 'grid_spacing', 'resolution', 'level', 'name', 'color', 'visible_patches', 'sharp_boundaries') data = {attr: getattr(self, attr) for attr in init_attrs} data['model state'] = Surface.take_snapshot(self, session, flags) data.update({ attr: getattr(self, attr) for attr in self._save_attrs if hasattr(self, attr) }) data['version'] = MOLSURF_STATE_VERSION return data
def __init__(self, session, enclose_atoms, show_atoms, probe_radius, grid_spacing, resolution, level, name, color, visible_patches, sharp_boundaries, update=True): Surface.__init__(self, name, session) self.selection_coupled = enclose_atoms.unique_structures self.atoms = enclose_atoms self._atom_count = len( self.atoms) # Used to determine when atoms deleted. self.show_atoms = show_atoms # Atoms for surface patch to show self.probe_radius = probe_radius # Only used for solvent excluded surface self.grid_spacing = grid_spacing self.resolution = resolution # Only used for Gaussian surface self.level = level # Contour level for Gaussian surface, atomic number units self.color = color self._atom_patch_colors = None self._atom_patch_color_mask = None self.visible_patches = visible_patches self.sharp_boundaries = sharp_boundaries self._joined_triangles = None self._refinement_steps = 1 # Used for fixing sharp edge problems near 3 atom junctions. self._auto_update_handler = None self.auto_update = update self._vertex_to_atom = None self._vertex_to_atom_count = None # Used to check if atoms deleted self._max_radius = None self.clip_cap = True
def _surface_model(session, model_id, shape_name, position = None): s = None if model_id is None else _find_surface_model(session, model_id) if s is None: from chimerax.core.models import Surface s = Surface(shape_name, session) if model_id is not None: s.id = model_id if position is not None: s.position = position s.SESSION_SAVE_DRAWING = True s.clip_cap = True # Cap surface when clipped return s
def meshes_as_models(session, meshes, buf_arrays): mesh_models = [] ba = buf_arrays from numpy import int32 from chimerax.core.models import Surface for m in meshes: if 'primitives' not in m: raise glTFError('glTF mesh has no "primitives": %s' % str(j)) pdlist = [] for pi, p in enumerate(m['primitives']): if 'mode' in p and p['mode'] != GLTF_TRIANGLES: raise glTFError( 'glTF reader only handles triangles, got mode %d' % p['mode']) if 'indices' not in p: raise glTFError('glTF missing "indices" in primitive %s' % str(p)) ta = ba[p['indices']] if len(ta.shape) == 1: ta = ta.reshape((len(ta) // 3, 3)) ta = ta.astype(int32, copy=False) if 'attributes' not in p: raise glTFError('glTF missing "attributes" in primitive %s' % str(p)) pa = p['attributes'] if 'POSITION' not in pa: raise glTFError( 'glTF missing "POSITION" attribute in primitive %s' % str(p)) va = ba[pa['POSITION']] if 'NORMAL' in pa: na = ba[pa['NORMAL']] else: from chimerax import surface na = surface.calculate_vertex_normals(va, ta) if 'COLOR_0' in pa: vc = ba[pa['COLOR_0']] else: vc = None pd = Surface('p%d' % pi, session) set_geometry(pd, va, na, vc, ta) pdlist.append(pd) mesh_models.append(pdlist) return mesh_models
def geometry_node_surfaces(primitives, place, color, materials, colors, session): from collada import polylist, triangleset splist = [] for p in primitives: if isinstance(p, polylist.Polylist): p = p.triangleset() if not isinstance(p, triangleset.TriangleSet): continue # Skip line sets. t = p.vertex_index # N by 3 array of vertex indices for triangles t = t.copy() # array from pycollada is not contiguous. v = p.vertex # M by 3 array of floats for vertex positions ni = p.normal_index # N by 3 array of normal indices for triangles n = p.normal # M by 3 array of floats for vertex normals # Collada allows different normals on the same vertex in different triangles, # but Hydra only allows one normal per vertex. if n is None: vn = None else: from numpy import empty vn = empty(v.shape, n.dtype) vn[t.ravel(), :] = n[ni.ravel(), :] vcolors = vertex_colors(p, t, len(v), colors) c = material_color(materials.get(p.material), color) name = '%d' % (len(splist) + 1) from chimerax.core.models import Surface sp = Surface(name, session) sp.set_geometry(v, vn, t) sp.color_list = [c] sp.position_list = [place] if not vcolors is None: sp.vertex_colors = vcolors sp.clip_cap = True splist.append(sp) return splist
def show_hk_lattice(session, h, k, radius, orientation='222', color=(255, 255, 255, 255), sphere_factor=0, edge_radius=None, mesh=False, replace=True): varray, tarray, hex_edges = hk_icosahedron_lattice(h, k, radius, orientation) interpolate_with_sphere(varray, radius, sphere_factor) name = 'Icosahedron h = %d, k = %d' % (h, k) if mesh: model = sm = _cage_surface(session, name, replace) sm.set_geometry(varray, None, tarray) sm.color = color sm.display_style = sm.Mesh sm.edge_mask = hex_edges # Hide spokes of hexagons. if sm.id is None: session.models.add([sm]) else: # Make cage from markers. from chimerax.core.models import Surface sm = Surface(name, session) sm.set_geometry(varray, None, tarray) sm.color = color sm.display_style = sm.Mesh sm.edge_mask = hex_edges # Hide spokes of hexagons. if edge_radius is None: edge_radius = .01 * radius mset = _cage_markers(session, name) if replace else None from chimerax.markers.cmd import markers_from_mesh model = markers_from_mesh(session, [sm], color=color, edge_radius=edge_radius, markers=mset) model.name = name if mset: mset._prev_markers.delete() model.hkcage = True return model
def create_mesh(session, c, transform, rgba, name): varray, tarray = mesh_geometry(c) if len(tarray) == 0: return None transform.transform_points(varray, in_place=True) from chimerax.core.models import Surface s = Surface(name, session) s.SESSION_SAVE_DRAWING = True s.clip_cap = True # Cap surface when clipped from chimerax.surface import calculate_vertex_normals narray = calculate_vertex_normals(varray, tarray) s.set_geometry(varray, narray, tarray) rgba8 = [int(r * 255) for r in rgba] s.color = rgba8 return s
def ellipsoid_mesh(session, name, center, radii, rotation, divisions, color, opacity): vertices, edges = lattitude_longtitude_circles(divisions) # Stretch, rotate and center vertices *= radii rotation.transform_points(vertices, in_place=True) vertices += center # Create ellipsoid model from chimerax.core.models import Surface s = Surface(name, session) normals = None s.set_geometry(vertices, normals, edges) s.color = [int(255 * r) for r in color + [opacity]] s.display_style = s.Mesh session.models.add([s])
def vbur_vis( session, geom, targets, radii, scale, radius, center, point_spacing, intersection_scale, volume_type, vbur, labels, basis=None, key_atoms=None, ): # from cProfile import Profile # # profile = Profile() # profile.enable() # number of points is based on surface area n_grid = int(4 * np.pi * radius**2 / point_spacing) if volume_type == "buried": model = Surface("%%Vbur for %s" % geom.name, session) else: model = Surface("%%Vfree for %s" % geom.name, session) # verts, norms, and triangles for the drawing vertices = [] normals = [] triangles = [] if isinstance(center, np.ndarray): center_coords = center else: center_coords = geom.COM(center) if isinstance(radii, dict): radii_dict = radii elif radii.lower() == "umn": radii_dict = VDW_RADII elif radii.lower() == "bondi": radii_dict = BONDI_RADII else: raise RuntimeError("received %s for radii, must be umn or bondi" % radii) if not hasattr(center, "__iter__"): center = [center] if targets is None: if len(center) == 1: atoms = [atom for atom in geom.atoms if atom not in center] else: atoms = geom.atoms else: atoms = geom.find(targets) atoms_within_radius = [] for atom in atoms: # determine which atom's radii extend within the sphere # reduces the number of distances we need to calculate d = np.linalg.norm(center_coords - atom.coords) inner_edge = d - scale * radii_dict[atom.element] if inner_edge < radius: atoms_within_radius.append(atom) # sort atoms based on their distance to the center # this makes is so we usually break out of looping over the atoms faster atoms_within_radius.sort( key=lambda a, c=center_coords: np.linalg.norm(a.coords - c)) radius_list = [] for atom in atoms_within_radius: radius_list.append(scale * radii_dict[atom.element]) coords = geom.coordinates(atoms_within_radius) atom_dist = distance_matrix(coords, coords) sphere = fibonacci_sphere(num=n_grid, radius=radius) # add points right where spheres intersect # this makes the intersections look less pokey d_ac = distance_matrix(coords, [center_coords]) center_added_points = [] atom_added_points = [[] for atom in atoms_within_radius] for i in range(0, len(coords)): r1 = radius_list[i] v_n = coords[i] - center_coords v_n /= np.linalg.norm(v_n) d = d_ac[i, 0] # intersection with big sphere if d + r1 > radius: theta = np.arccos((r1**2 - radius**2 - d**2) / (-2 * d * radius)) h = radius * np.sin(theta) b = radius * np.cos(theta) p = b * v_n circ = 2 * np.pi * h n_added = int(np.ceil(intersection_scale * circ / point_spacing)) r_t = perp_vector(v_n) r0 = np.cross(r_t, v_n) r0 *= h / np.linalg.norm(r0) rot_angle = 2 * np.pi / n_added R = rotation_matrix(rot_angle, v_n) prev_r = r0 prev_r_list = [] for x in np.linspace(0, 2 * np.pi, num=n_added): prev_r = np.dot(R, prev_r) prev_r_list.append(prev_r) prev_r_list = np.array(prev_r_list) d_ep = distance_matrix(prev_r_list + p + center_coords, coords) # if the point is obscured by another atom, don't bother adding it # this saves time on triangulation diff_mat = d_ep - radius_list diff_mat[:, i] = 1 mask = np.invert(np.any(diff_mat < 0, axis=1)) center_added_points.extend(prev_r_list[mask] + p) atom_added_points[i].extend(prev_r_list[mask] + p + center_coords) for j in range(0, i): d = atom_dist[i, j] r2 = radius_list[j] if d < r1 + r2 and d > abs(r1 - r2): v_n = coords[j] - coords[i] v_n /= np.linalg.norm(v_n) theta = np.arccos((r2**2 - r1**2 - d**2) / (-2 * r1 * d)) h = r1 * np.sin(theta) b = r1 * np.cos(theta) p = b * v_n circ = 2 * np.pi * h n_added = int( np.ceil(intersection_scale * circ / point_spacing)) r_t = perp_vector(v_n) r0 = np.cross(r_t, v_n) r0 *= h / np.linalg.norm(r0) rot_angle = 2 * np.pi / n_added R = rotation_matrix(rot_angle, v_n) prev_r = r0 prev_r_list = [] for x in np.linspace(0, 2 * np.pi, num=n_added): prev_r = np.dot(R, prev_r) prev_r_list.append(prev_r) prev_r_list = np.array(prev_r_list) norm_mask = np.linalg.norm(center_coords - (prev_r_list + p + coords[i]), axis=1) < radius prev_r_list = prev_r_list[norm_mask] if not len(prev_r_list): continue d_ep = distance_matrix(prev_r_list + p + coords[i], coords) diff_mat = d_ep - radius_list diff_mat[:, i] = 1 diff_mat[:, j] = 1 mask = np.invert(np.any(diff_mat < 0, axis=1)) if any(mask): atom_added_points[i].extend(prev_r_list[mask] + p + coords[i]) atom_added_points[j].extend(prev_r_list[mask] + p + coords[i]) tol = 0.3 * point_spacing for i in range(0, len(coords)): # get a grid of points around each atom # remove any points that are close to an intersection # this makes it less likely for the triangulation to choose # one of these points instead of one that we already added # then, if we have to remove a triangle involving one of these points later, # it won't leave a gap n_atom_grid = int(radius_list[i]**2 * n_grid / radius**2) atom_sphere = fibonacci_sphere(radius=radius_list[i], num=n_atom_grid) mask = np.ones(len(atom_sphere), dtype=bool) n_atom_grid = len(atom_sphere) if len(atom_added_points[i]) > 0: remove_ndx = [] dist_mat = distance_matrix( atom_sphere, np.array(atom_added_points[i]) - coords[i]) mask *= np.any((dist_mat - tol) < 0) atom_sphere = atom_sphere[mask] atom_sphere = np.array(atom_sphere) n_atom_grid = len(atom_sphere) atom_sphere = np.concatenate( (atom_sphere, np.array(atom_added_points[i]) - coords[i])) if len(atom_sphere) < 4: continue atom_hull = ConvexHull(atom_sphere / radius_list[i]) tri = atom_hull.simplices atom_sphere += coords[i] remove_v = [] new_ndx = np.zeros(len(atom_sphere), dtype=int) # remove any points that are covered by another atom # only loop over n_atom_grid points so we don't remove # any points right on the intersection b/c of # numerical issues del_count = 0 atom_grid_dist = distance_matrix(atom_sphere, coords) center_atom_grid_dist = distance_matrix(atom_sphere, [center_coords])[:, 0] for j in range(0, n_atom_grid): if center_atom_grid_dist[j] > radius: remove_v.append(j) del_count += 1 continue for k in range(0, len(coords)): if i == k: continue if atom_grid_dist[j, k] < radius_list[k]: remove_v.append(j) del_count += 1 break new_ndx[j] = j - del_count for j in range(n_atom_grid, len(atom_sphere)): new_ndx[j] = j - del_count if len(remove_v) == len(atom_sphere): continue atom_sphere = atom_sphere.tolist() for vi in remove_v[::-1]: atom_sphere.pop(vi) tri = tri[np.all(tri != vi, axis=1)] for j, ti in enumerate(tri): for k, v in enumerate(ti): tri[j][k] = new_ndx[v] atom_sphere = np.array(atom_sphere) norms = -(atom_sphere - coords[i]) / radius_list[i] triangles.extend(tri + len(vertices)) vertices.extend(atom_sphere) normals.extend(norms) remove_ndx = [] if len(center_added_points) > 0: dist_mat = distance_matrix(sphere, center_added_points) for i in range(0, len(sphere)): for j in range(0, len(center_added_points)): if dist_mat[i, j] < tol: remove_ndx.append(i) break sphere = sphere.tolist() for ndx in remove_ndx[::-1]: sphere.pop(ndx) sphere = np.array(sphere) n_sphere = len(sphere) if center_added_points: sphere = np.concatenate((sphere, np.array(center_added_points))) center_hull = ConvexHull(sphere / radius) sphere += center_coords tri = center_hull.simplices remove_v = [] new_ndx = np.zeros(len(sphere), dtype=int) del_count = 0 center_grid_dist = distance_matrix(sphere, coords) for i in range(0, n_sphere): if volume_type == "free": for j in range(0, len(coords)): if center_grid_dist[i, j] < radius_list[j]: remove_v.append(i) del_count += 1 break elif volume_type == "buried": if all(center_grid_dist[i, j] > radius_list[j] for j in range(0, len(coords))): remove_v.append(i) del_count += 1 new_ndx[i] = i - del_count for i in range(n_sphere, len(sphere)): new_ndx[i] = i - del_count sphere = sphere.tolist() mask = np.ones(len(tri), dtype=bool) for vi in remove_v[::-1]: sphere.pop(vi) mask *= np.invert(np.any(tri == vi, axis=1)) tri = tri[mask] new_t = tri if volume_type == "buried": mask = np.invert(np.all(new_t >= n_sphere, axis=1)) new_t = new_t[mask] new_t = np.array(new_t) for i, ti in enumerate(new_t): new_t[i] = new_ndx[ti] norms = [] if sphere: sphere = np.array(sphere) norms = (sphere - center_coords) / radius triangles.extend(new_t + len(vertices)) vertices.extend(sphere) normals.extend(norms) # the triangles need to be reordered so the points are # clockwise (or counterclockwise? i don't remember) vertices = np.array(vertices) normals = np.array(normals) triangles = np.array(triangles) v1 = vertices[triangles[:, 1]] - vertices[triangles[:, 0]] v2 = vertices[triangles[:, 2]] - vertices[triangles[:, 0]] c = np.cross(v1, v2) mask = np.sum(c * normals[triangles[:, 0]], axis=1) < 0 triangles[mask] = triangles[mask, ::-1] model.set_geometry(vertices, normals, triangles) # profile.disable() # from TestManager.stream_holder import StreamHolder # import pstats # stream = StreamHolder(session) # pstats.Stats(profile, stream=stream).strip_dirs().sort_stats(-1).print_stats() # stream.flush() if labels != "none": d = model.new_drawing("octants") d.display_style = d.Mesh d.use_lighting = False d.casts_shadows = False d.pickable = False d_theta = np.pi / 180 oct_radius = 1.01 * radius verts = [np.zeros(3)] triangles = [] for k, v in enumerate(basis.T): if labels == "quadrants" and k == 2: continue start_ndx = len(verts) prev_vert = oct_radius * perp_vector(v) verts.append(prev_vert) R = rotation_matrix(d_theta, v) for i in range(1, 360): triangles.append([0, start_ndx + i - 1, start_ndx + i]) prev_vert = np.dot(R, prev_vert) verts.append(prev_vert) triangles.append([0, start_ndx, start_ndx + i - 1]) for v2 in basis.T[:k]: v3 = np.cross(v, v2) v3 /= np.linalg.norm(v3) v3 *= oct_radius ndx = len(verts) verts.append(v) verts.append(v3) triangles.append([ndx, 0, ndx + 1]) ndx = len(verts) verts.append(v) verts.append(-v3) triangles.append([ndx, 0, ndx + 1]) ndx = len(verts) verts.append(-v) verts.append(-v3) triangles.append([ndx, 0, ndx + 1]) ndx = len(verts) verts.append(-v) verts.append(v3) triangles.append([ndx, 0, ndx + 1]) norms = np.array(verts) / oct_radius verts = np.array(verts) + center_coords triangles = np.array(triangles) d.set_geometry(verts, norms, triangles) d.set_edge_mask(2 * np.ones(len(triangles), dtype=int)) label_list = [] x = basis.T[0] x = x / np.linalg.norm(x) y = basis.T[1] y = y / np.linalg.norm(y) z = basis.T[2] z = z / np.linalg.norm(z) if labels == "octants": for i, val in enumerate(vbur): coordinates = np.zeros(3) if any(i == n for n in [1, 2, 5, 6]): coordinates -= x else: coordinates += x if any(i == n for n in [2, 3, 4, 5]): coordinates -= y else: coordinates += y if i > 3: coordinates -= z else: coordinates += z if volume_type == "free": l = "%.1f%%" % (100 / 8 - val) else: l = "%.1f%%" % (val) coordinates /= np.linalg.norm(coordinates) coordinates *= radius coordinates += center_coords label_list.append(VoidLabel(l, coordinates, session.main_view)) else: quad_vbur = [ vbur[0] + vbur[7], vbur[1] + vbur[6], vbur[2] + vbur[5], vbur[3] + vbur[4], ] for i, val in enumerate(quad_vbur): coordinates = np.zeros(3) if any(i == n for n in [1, 2]): coordinates -= x else: coordinates += x if any(i == n for n in [2, 3]): coordinates -= y else: coordinates += y if volume_type == "free": l = "%.1f%%" % (25 - val) else: l = "%.1f%%" % (val) coordinates /= np.linalg.norm(coordinates) coordinates *= radius coordinates += center_coords label_list.append(VoidLabel(l, coordinates, session.main_view)) label_object = VoidLabels(session) label_object.add_labels(label_list) model.add([label_object]) return [model] return [model]
def calculate_segmentation_surfaces(seg, where=None, each=None, region='all', step=None, color=None): # Warn if number of surfaces is large. if where is None and each is None: max_seg_id = _maximum_segment_id(seg) if max_seg_id > 100: from chimerax.core.errors import UserError raise UserError( 'Segmentation %s (#%s) has %d segments (> 100).' ' To create surface for each segment use "each segment" option.' % (seg.name, seg.id_string, max_seg_id)) # Compute surfaces conditions = (where if where else []) + ([each] if each else []) group, attribute_name = _which_segments(seg, conditions) matrix = seg.matrix(step=step, subregion=region) from . import segmentation_surfaces surfs = segmentation_surfaces(matrix, group) # Transform vertices from index to scene units and compute normals. geom = [] tf = seg.matrix_indices_to_xyz_transform(step=step, subregion=region) for region_id, va, ta in surfs: tf.transform_points(va, in_place=True) from chimerax.surface import calculate_vertex_normals na = calculate_vertex_normals(va, ta) geom.append((region_id, va, na, ta)) # Determine surface coloring. if color is None: attr = None if attribute_name == 'segment' else attribute_name colors = _attribute_colors(seg, attr).attribute_rgba # Create one or more surface models from chimerax.core.models import Surface from chimerax.surface import combine_geometry_xvnt segsurfs = [] if each is None and len(geom) > 1: # Combine multiple surfaces into one. va, na, ta = combine_geometry_xvnt(geom) name = '%s %d %ss' % (seg.name, len(geom), attribute_name) s = Surface(name, seg.session) s.clip_cap = True # Cap surface when clipped s.set_geometry(va, na, ta) if color is None: color_counts = [(colors[region_id], len(sva)) for region_id, sva, sna, sta in geom] s.vertex_colors = _vertex_colors(len(va), color_counts) else: s.color = color segsurfs.append(s) else: # Create multiple surface models for region_id, va, na, ta in geom: name = '%s %s %d' % (seg.name, attribute_name, region_id) s = Surface(name, seg.session) s.clip_cap = True # Cap surface when clipped s.set_geometry(va, na, ta) s.color = colors[region_id] if color is None else color segsurfs.append(s) return segsurfs
def take_snapshot(self, session, flags): return Surface.save_geometry(self, session, flags)
def __init__(self, session): Surface.__init__(self, 'blob outline box', session) self.pickable = False
def vbur_vis( session, geom, targets, radii, scale, radius, center, point_spacing, intersection_scale, volume_type, ): # number of points is based on surface area n_grid = int(4 * np.pi * radius**2 / point_spacing) if volume_type == "buried": model = Surface("%%Vbur for %s" % geom.name, session) else: model = Surface("%%Vfree for %s" % geom.name, session) # verts, norms, and triangles for the drawing vertices = [] normals = [] triangles = [] if isinstance(center, np.ndarray): center_coords = center else: center_coords = geom.COM(center) if isinstance(radii, dict): radii_dict = radii elif radii.lower() == "umn": radii_dict = VDW_RADII elif radii.lower() == "bondi": radii_dict = BONDI_RADII else: raise RuntimeError("received %s for radii, must be umn or bondi" % radii) if not hasattr(center, "__iter__"): center = [center] if targets is None: if len(center) == 1: atoms = [atom for atom in geom.atoms if atom not in center] else: atoms = geom.atoms else: atoms = geom.find(targets) atoms_within_radius = [] for atom in atoms: # determine which atom's radii extend within the sphere # reduces the number of distances we need to calculate d = np.linalg.norm(center_coords - atom.coords) inner_edge = d - scale * radii_dict[atom.element] outer_edge = inner_edge + 2 * scale * radii_dict[atom.element] if inner_edge < radius: atoms_within_radius.append(atom) # sort atoms based on their distance to the center # this makes is so we usually break out of looping over the atoms faster atoms_within_radius.sort( key=lambda a, c=center_coords: np.linalg.norm(a.coords - c)) radius_list = [] for atom in atoms_within_radius: radius_list.append(scale * radii_dict[atom.element]) coords = geom.coordinates(atoms_within_radius) atom_dist = distance_matrix(coords, coords) sphere = fibonacci_sphere(num=n_grid, radius=radius) # add points right where spheres intersect # this makes the intersections look less pokey d_ac = distance_matrix(coords, [center_coords]) center_added_points = [] atom_added_points = [[] for atom in atoms_within_radius] for i in range(0, len(coords)): r1 = radius_list[i] v_n = coords[i] - center_coords v_n /= np.linalg.norm(v_n) d = d_ac[i, 0] # intersection with big sphere if d + r1 > radius: theta = np.arccos((r1**2 - radius**2 - d**2) / (-2 * d * radius)) h = radius * np.sin(theta) b = radius * np.cos(theta) p = b * v_n circ = 2 * np.pi * h n_added = int(np.ceil(intersection_scale * circ / point_spacing)) r_t = np.zeros(3) for j in range(0, 3): if v_n[j] != 0: if j == 0: r_t[1] = v_n[j] r_t[0] = v_n[1] else: r_t[0] = v_n[j] r_t[1] = v_n[0] break r0 = np.cross(r_t, v_n) r0 *= h / np.linalg.norm(r0) rot_angle = 2 * np.pi / n_added R = rotation_matrix(rot_angle, v_n) prev_r = r0 for x in np.linspace(0, 2 * np.pi, num=n_added): prev_r = np.dot(R, prev_r) d_ep = distance_matrix(coords, [prev_r + p + center_coords]) # if the point is obscured by another atom, don't bother adding it # this saves time on triangulation for j in range(0, len(coords)): if i == j: continue if d_ep[j, 0] <= radius_list[j]: break else: center_added_points.append(prev_r + p) atom_added_points[i].append(prev_r + p + center_coords) for j in range(0, i): d = atom_dist[i, j] r2 = radius_list[j] if d < r1 + r2 and d > abs(r1 - r2): v_n = coords[j] - coords[i] v_n /= np.linalg.norm(v_n) theta = np.arccos((r2**2 - r1**2 - d**2) / (-2 * r1 * d)) h = r1 * np.sin(theta) b = r1 * np.cos(theta) p = b * v_n circ = 2 * np.pi * h n_added = int( np.ceil(intersection_scale * circ / point_spacing)) r_t = np.zeros(3) for k in range(0, 3): if v_n[k] != 0: if k == 0: r_t[1] = v_n[k] r_t[0] = v_n[1] else: r_t[0] = v_n[k] r_t[1] = v_n[0] break r0 = np.cross(r_t, v_n) r0 *= h / np.linalg.norm(r0) rot_angle = 2 * np.pi / n_added R = rotation_matrix(rot_angle, v_n) prev_r = r0 for x in np.linspace(0, 2 * np.pi, num=n_added): prev_r = np.dot(R, prev_r) if np.linalg.norm(center_coords - (prev_r + p + coords[i])) > radius: continue d_ep = distance_matrix(coords, [prev_r + p + coords[i]]) for k in range(0, len(coords)): if i == k or j == k: continue if d_ep[k, 0] <= radius_list[k]: break else: atom_added_points[i].append(prev_r + p + coords[i]) atom_added_points[j].append(prev_r + p + coords[i]) for i in range(0, len(coords)): # get a grid of points around each atom # remove any points that are close to an intersection # this makes it less likely for the triangulation to choose # one of these points instead of one that we already added # then, if we have to remove a triangle involving one of these points later, # it won't leave a gap n_atom_grid = int(radius_list[i]**2 * n_grid / radius**2) atom_sphere = fibonacci_sphere(radius=radius_list[i], num=n_atom_grid) n_atom_grid = len(atom_sphere) if len(atom_added_points[i]) > 0: remove_ndx = [] dist_mat = distance_matrix( atom_sphere, np.array(atom_added_points[i]) - coords[i]) for j in range(0, len(atom_sphere)): for k in range(0, len(atom_added_points[i])): if dist_mat[j, k] < 0.3 * point_spacing: remove_ndx.append(j) break atom_sphere = atom_sphere.tolist() for ndx in remove_ndx[::-1]: atom_sphere.pop(ndx) atom_sphere = np.array(atom_sphere) n_atom_grid = len(atom_sphere) atom_sphere = np.concatenate( (atom_sphere, np.array(atom_added_points[i]) - coords[i])) atom_hull = ConvexHull(atom_sphere / radius_list[i]) tri = atom_hull.simplices atom_sphere += coords[i] remove_v = [] new_ndx = np.zeros(len(atom_sphere), dtype=int) # remove any points that are covered by another atom # only loop over n_atom_grid points so we don't remove # any points right on the intersection b/c of # numerical issues del_count = 0 atom_grid_dist = distance_matrix(atom_sphere, coords) center_atom_grid_dist = distance_matrix(atom_sphere, [center_coords])[:, 0] for j in range(0, n_atom_grid): if center_atom_grid_dist[j] > radius: remove_v.append(j) del_count += 1 continue for k in range(0, len(coords)): if i == k: continue if atom_grid_dist[j, k] < radius_list[k]: remove_v.append(j) del_count += 1 break new_ndx[j] = j - del_count for j in range(n_atom_grid, len(atom_sphere)): new_ndx[j] = j - del_count if len(remove_v) == len(atom_sphere): continue atom_sphere = atom_sphere.tolist() for vi in remove_v[::-1]: atom_sphere.pop(vi) tri = tri[np.all(tri != vi, axis=1)] new_t = tri.tolist() # remove_t = [] # for j, ti in enumerate(new_t): # if all(t >= n_atom_grid for t in ti): # remove_t.append(j) # # for vi in remove_t[::-1]: # new_t.pop(vi) new_t = np.array(new_t) for j, ti in enumerate(new_t): for k, v in enumerate(ti): new_t[j][k] = new_ndx[v] atom_sphere = np.array(atom_sphere) norms = -(atom_sphere - coords[i]) / radius_list[i] triangles.extend(new_t + len(vertices)) vertices.extend(atom_sphere) normals.extend(norms) remove_ndx = [] if len(center_added_points) > 0: dist_mat = distance_matrix(sphere, center_added_points) for i in range(0, len(sphere)): for j in range(0, len(center_added_points)): if dist_mat[i, j] < 0.3 * point_spacing: remove_ndx.append(i) break sphere = sphere.tolist() for ndx in remove_ndx[::-1]: sphere.pop(ndx) sphere = np.array(sphere) n_sphere = len(sphere) sphere = np.concatenate((sphere, np.array(center_added_points))) center_hull = ConvexHull(sphere / radius) sphere += center_coords tri = center_hull.simplices remove_v = [] new_ndx = np.zeros(len(sphere), dtype=int) del_count = 0 center_grid_dist = distance_matrix(sphere, coords) for i in range(0, n_sphere): if volume_type == "free": for j in range(0, len(coords)): if center_grid_dist[i, j] < radius_list[j]: remove_v.append(i) del_count += 1 break elif volume_type == "buried": if all(center_grid_dist[i, j] > radius_list[j] for j in range(0, len(coords))): remove_v.append(i) del_count += 1 new_ndx[i] = i - del_count for i in range(n_sphere, len(sphere)): new_ndx[i] = i - del_count sphere = sphere.tolist() for vi in remove_v[::-1]: sphere.pop(vi) tri = tri[np.all(tri != vi, axis=1)] new_t = tri.tolist() if volume_type == "buried": remove_t = [] for j, ti in enumerate(new_t): if all(t >= n_sphere for t in ti): remove_t.append(j) for vi in remove_t[::-1]: new_t.pop(vi) new_t = np.array(new_t) for i, ti in enumerate(new_t): for j, v in enumerate(ti): new_t[i][j] = new_ndx[v] sphere = np.array(sphere) norms = (sphere - center_coords) / radius triangles.extend(new_t + len(vertices)) vertices.extend(sphere) normals.extend(norms) # the triangles need to be reordered so the points are # clockwise (or counterclockwise? i don't remember) for i in range(0, len(triangles)): t = triangles[i] v1 = vertices[t[1]] - vertices[t[0]] v2 = vertices[t[2]] - vertices[t[0]] c = np.cross(v1, v2) if np.dot(c, normals[t[0]]) < 0: triangles[i] = t[::-1] model.set_geometry(np.array(vertices), np.array(normals), np.array(triangles)) return model
def delete(self): self.auto_update = False # Remove auto update handler Surface.delete(self)
def restore_snapshot(cls, session, data): inst = cls(session, None, data['plane'], data['thickness'], data['radius'], data['color']) Surface.set_state_from_snapshot(inst, session, data['base data']) return inst