def _make_histogram(self, bins=50): a = self.axes a.set_title('Crosslink lengths') a.set_xlabel(r'length ($\AA$)') a.set_ylabel('model count') a1, a2 = self._crosslink_atoms() e = self.ensemble_model acid = e.active_coordset_id cset_ids = e.coordset_ids from numpy import empty, float32 d = empty((len(cset_ids), ), float32) from chimerax.geometry import distance # TODO: Optimize. Changing coordset scans all coordsets. # Make routine to return coords for all coordsets for an atom? for i, id in enumerate(cset_ids): e.active_coordset_id = id d[i] = distance(a1.scene_coord, a2.scene_coord) e.active_coordset_id = acid n, be, self._patches = a.hist(d, bins=bins) self._bin_edges = be if bins > 0: # Rightmost bin edge is exactly at max data value. # This makes it fall outside bins in numpy.digitize(). # Remedy this by extending the rightmost bin edge a little. be[bins] += 0.01 * (be[bins] - be[bins - 1]) # Map bin to list of coordset ids. from numpy import digitize bi = digitize(d, self._bin_edges) - 1 self._bin_coordset_ids = bcs = {} for i, cs in zip(bi, cset_ids): bcs.setdefault(i, []).append(cs)
def first_volume_maxima(xyz_in, xyz_out, vlist): line = (xyz_in, xyz_out) # Scene coords hits = [] from chimerax.geometry import distance for v in vlist: if not v.shown(): continue v_xyz_in, v_xyz_out = data_slice(v, line) if v_xyz_in is None: continue threshold = v.minimum_surface_level if threshold is None: if len(v.image_levels) == 0: return None, None threshold = min(lev for lev, h in v.image_levels) f = first_maximum_along_ray(v, v_xyz_in, v_xyz_out, threshold) if f is None: continue vxyz = (1 - f) * v_xyz_in + f * v_xyz_out sxyz = v.position * vxyz d = distance(sxyz, xyz_in) hits.append((d, sxyz, v)) if len(hits) == 0: return None, None d, sxyz, v = min(hits, key=lambda h: h[0]) return sxyz, v
def _picked_object(self, pick): warning = lambda txt: self.session.logger.status( "Distance mouse mode: %s" % txt, color = "red") message = self.session.logger.status from chimerax.atomic import PickedAtom, PickedPseudobond from chimerax.core.commands import run if isinstance(pick, PickedAtom): if self._first_atom and self._first_atom.deleted: self._first_atom = None if self._first_atom: if pick.atom == self._first_atom: warning("same atom picked twice") else: a1, a2 = self._first_atom, pick.atom command = "dist %s %s" % (a1.string(style="command line"), a2.string(style="command line")) from chimerax.geometry import distance message("Distance from %s to %s is %g" % (a1, a2, distance(a1.scene_coord, a2.scene_coord))) self._first_atom = None run(self.session, command) else: self._first_atom = pick.atom message("Distance from %s to..." % pick.atom) elif isinstance(pick, PickedPseudobond): if pick.pbond.group.name == "distances": a1, a2 = pick.pbond.atoms command = "~dist %s %s" % (a1.string(style="command line"), a2.string(style="command line")) message("Removing distance") run(self.session, command) else: warning("not a distance") else: warning("no atom/distance picked by mouse click")
def volume_plane_intercept(xyz_in, xyz_out, vlist): line = (xyz_in, xyz_out) # Scene coords hits = [] from chimerax.geometry import distance for v in vlist: if not v.shown(): continue plane = (v.single_plane() or v.showing_image('orthoplanes') or v.showing_image('box faces') or v.showing_image('tilted slab')) if not plane: continue v_xyz_in, v_xyz_out = data_slice(v, line) if v_xyz_in is None: continue vxyz = .5 * v_xyz_in + .5 * v_xyz_out sxyz = v.position * vxyz d = distance(sxyz, xyz_in) hits.append((d, sxyz, v)) if len(hits) == 0: return None, None d, sxyz, v = min(hits, key=lambda h: h[0]) return sxyz, v
def _label_text_and_height(self): from chimerax.geometry import distance m1, m2 = self._markers d = distance(m1.coord, m2.coord) text = '%.4g' % d h = max(2 * self._radius, 0.1 * d) return text, h
def picked_object(self, win_x, win_y, exclude=unpickable, beyond=None, max_transparent_layers=3): ''' Return a Pick object for the frontmost object below the given screen window position (specified in pixels). This Pick object will have an attribute position giving the point where the intercept occurs. This is used when hovering the mouse over an object (e.g. an atom) to get a description of that object. Beyond is minimum distance as fraction from front to rear clip plane. ''' xyz1, xyz2 = self.clip_plane_points(win_x, win_y) if xyz1 is None or xyz2 is None: p = None else: p = self.picked_object_on_segment( xyz1, xyz2, exclude=exclude, beyond=beyond, max_transparent_layers=max_transparent_layers) # If scene clipping and some models disable clipping, try picking those. if self.clip_planes.have_scene_plane( ) and not self.drawing.all_allow_clipping(): ucxyz1, ucxyz2 = self.clip_plane_points( win_x, win_y, include_scene_clipping=False) if ucxyz1 is not None and ucxyz2 is not None: def exclude_clipped(d, exclude=exclude): return exclude(d) or d.allow_clipping ucp = self.picked_object_on_segment( ucxyz1, ucxyz2, max_transparent_layers=max_transparent_layers, exclude=exclude_clipped) if ucp: from chimerax.geometry import distance if p is None or ucp.distance * distance( ucxyz1, ucxyz2) < distance(ucxyz1, xyz1): p = ucp return p
def apply_restraints(trs, rrs, adjust_for_confidence, confidence_type): template_as = [] restrained_as = [] for tr, rr in zip(trs, rrs): ta_names = set(tr.atoms.names).intersection(atom_names) ra_names = set(rr.atoms.names).intersection(atom_names) common_names = list(ta_names.intersection(ra_names)) template_as.extend([tr.find_atom(name) for name in common_names]) restrained_as.extend([rr.find_atom(name) for name in common_names]) # template_as.append(tr.atoms[numpy.in1d(tr.atoms.names, common_names)]) # restrained_as.append(rr.atoms[numpy.in1d(rr.atoms.names, common_names)]) from chimerax.atomic import Atoms template_as = Atoms(template_as) restrained_as = Atoms(restrained_as) template_coords = template_as.coords from math import sqrt for i, ra1 in enumerate(restrained_as): query_coord = numpy.array([template_coords[i]]) indices = find_close_points(query_coord, template_coords, distance_cutoff)[1] indices = indices[indices != i] for ind in indices: ra2 = restrained_as[ind] if ra1.residue == ra2.residue: continue if adjust_for_confidence: if confidence_type == 'plddt': scores = [ template_as[i].bfactor * confidence_multiplier, template_as[ind].bfactor * confidence_multiplier ] elif confidence_type == 'pae': scores = [ pae_matrix[template_as[i].residue.number - 1, template_as[ind].residue.number - 1] ] kappa_adj, tol_adj, falloff_adj = adjust_distance_restraint_terms_by_confidence( scores, confidence_type) if kappa_adj == 0: continue else: kappa_adj = tol_adj = 1 falloff_adj = 0 try: dr = adrm.add_restraint(ra1, ra2) except ValueError: continue dist = distance(query_coord[0], template_coords[ind]) dr.tolerance = tolerance * dist * tol_adj dr.target = dist dr.c = max(sqrt(dist) * well_half_width, 0.1) #dr.effective_spring_constant = spring_constant dr.kappa = kappa * kappa_adj from math import log dr.alpha = -1 - fall_off * log( (max(dist - 1, 1))) - falloff_adj dr.enabled = True
def vseries_measure(session, series, output = None, centroids = True, color = None, radius = None): '''Report centroid motion of a map series.''' rgba = (170,170,170,255) if color is None else color.uint8x4() from chimerax.surface import surface_volume_and_area from chimerax.std_commands import measure_inertia meas = [] for s in series: n = s.number_of_times() start_time = s.last_shown_time for t in range(n): if t != start_time: s.copy_display_parameters(start_time, t) s.show_time(t) v = s.maps[t] v.update_drawings() # Compute surface. Normally does not happen until rendered. level = v.minimum_surface_level if level is None: from chimerax.core.errors import UserError raise UserError('vseries measure (#%s) requires surface style display' % s.id_string + ' since the surface threshold is used for computing centroid,' + ' enclosed volume, area, and size') vol, area, holes = surface_volume_and_area(v) axes, d2, c = measure_inertia.map_inertia([v]) elen = measure_inertia.inertia_ellipsoid_size(d2) meas.append((level, c, vol, area, elen)) s.show_time(start_time) if centroids: if radius is None: radius = min(v.data.step) create_centroid_path(session, 'centroid path', tuple(m[1] for m in meas), radius, rgba) # Make text output lines = ['# Volume series measurements: %s\n' % s.name, '# n level x y z step distance volume area inertia ellipsoid size \n'] d = 0 cprev = None step = 0 from chimerax.geometry import distance for n, (level, c, vol, area, elen) in enumerate(meas): if not cprev is None: step = distance(cprev, c) d += step cprev = c lines.append('%5d %12.5g %12.5g %12.5g %12.5g %12.5g %12.5g %12.5g %12.5g %12.5g %12.5g %12.5g\n' % (n, level, c[0], c[1], c[2], step, d, vol, area, elen[0], elen[1], elen[2])) text = ''.join(lines) if output: from os import path path = path.expanduser(output) f = open(path, 'w') f.write(text) f.close() else: session.logger.info(text)
def vr_press(self, event): # Virtual reality hand controller button press. pick = event.picked_object(self.view) self._bond_rot = br = self._bond_rotation(pick) if br: br.bond.selected = True # Move the side of the bond the VR click is closest to. # Would like to have a command to enable this mode for rotating bonds # with small ligands move_closer_side = False if move_closer_side and self._bond_rot is not None: br = self._bond_rot atom1 = br.moving_side atom2 = br.bond.other_atom(atom1) p = event.tip_position from chimerax.geometry import distance if distance(p, atom2.scene_coord) < distance(p, atom1.scene_coord): br.moving_side = atom2
def build_next_atom_from_geometry(residue, residue_anchor, template_anchor, template_new_atom): from chimerax.atomic import struct_edit from chimerax.geometry import distance, angle, dihedral r = residue m = r.structure tnext = template_new_atom if tnext is None: raise TypeError('Template does not contain an atom with that name!') tstub = template_anchor rstub = residue_anchor existing_rstub_neighbors = rstub.neighbors n1 = rstub n2 = n3 = None t_direct_neighbors = [] r_direct_neighbors = [] for a2 in tstub.neighbors: if a2.element.name != 'H': n2 = r.find_atom(a2.name) if n2: t_direct_neighbors.append(a2) r_direct_neighbors.append(n2) if len(t_direct_neighbors) > 1: a2, a3 = t_direct_neighbors[:2] n2, n3 = r_direct_neighbors[:2] else: a2 = t_direct_neighbors[0] n2 = r_direct_neighbors[0] if not n2: raise TypeError( 'No n2 found - Not enough connected atoms to form a dihedral!') if not n3: for a3 in a2.neighbors: if a3 not in (a2, tstub) and a3.element.name != 'H': n3 = r.find_atom(a3.name) if n3: break if not n3: raise TypeError( 'No n3 found - Not enough connected atoms to form a dihedral!') # print('Building next atom {} from geometry of {}'.format(template_new_atom.name, # ','.join([n.name for n in (n1, n2, n3)]))) dist = distance(tnext.coord, tstub.coord) ang = angle(tnext.coord, tstub.coord, a2.coord) dihe = dihedral(tnext.coord, tstub.coord, a2.coord, a3.coord) # print('{}: {} {} {}'.format(next_atom_name, dist, ang, dihe)) a = struct_edit.add_dihedral_atom(tnext.name, tnext.element, n1, n2, n3, dist, ang, dihe) a.occupancy = rstub.occupancy a.bfactor = rstub.bfactor return a
def find_nearest(pos, atom, exclude, check_dist, avoid_metal_info=None): nearby = search_tree.search(pos, check_dist) near_pos = n = near_atom = None exclude_pos = set([tuple(ex._addh_coord) for ex in exclude]) exclude_pos.add(tuple(atom._addh_coord)) from chimerax.geometry import distance for nb in nearby: n_pos = nb._addh_coord if tuple(n_pos) in exclude_pos: # excludes identical models also... continue if nb.structure != atom.structure and ( atom.structure.id is None or (len(nb.structure.id) > 1 and (nb.structure.id[:-1] == atom.structure.id[:-1])) or nb.structure in ident_pos_models[atom.structure]): # (1) unopen models only "clash" with themselves # (2) don't consider atoms in sibling submodels continue if nb not in atom.neighbors: if avoid_metal_info and nb.element.is_metal and nb.structure == atom.structure: if metal_clash(nb._addh_coord, pos, atom._addh_coord, atom, avoid_metal_info): return n_pos, 0.0, nb d = distance(n_pos, pos) - vdw_radius(nb) if near_pos is None or d < n: near_pos = n_pos n = d near_atom = nb # only heavy atoms in tree... for nbb in nb.neighbors: if nbb.element.number != 1: continue if tuple(nbb._addh_coord) in exclude_pos: continue n_pos = nbb._addh_coord d = distance(n_pos, pos) - h_rad if near_pos is None or d < n: near_pos = n_pos n = d near_atom = nbb return near_pos, n, near_atom
def atom_motion(session, atoms, to_atoms): amap = {(a.residue.chain_id, a.residue.number, a.name):a for a in atoms} count = 0 from chimerax.geometry import distance for a in to_atoms: a2 = amap.get((a.residue.chain_id, a.residue.number, a.name)) if a2: d = distance(a.scene_coord, a2.scene_coord) a2.motion = a.motion = d count += 1 session.logger.status('%d atom pairs' % count, log = True)
def closest_slow(session, atoms, to_atoms, max_dist=10, show=False): '''Loop through every pair of atoms in Python. This is slow.''' dmin = amin1 = amin2 = None from chimerax.geometry import distance for a1 in atoms: for a2 in to_atoms: d = distance(a1.scene_coord, a2.scene_coord) if (dmin is None or d < dmin) and d <= max_dist: dmin, amin1, amin2 = d, a1, a2 report_closest(session, dmin, amin1, amin2, show, max_dist) return dmin, amin1, amin2
def unique_symmetry_position(tf, center, ref_point, sym_list): if sym_list is None or len(sym_list) == 0: return tf from chimerax.geometry import distance import numpy as n i = n.argmin([distance(sym*tf*center, ref_point) for sym in sym_list]) if sym_list[i].is_identity(): return tf return sym_list[i]*tf
def sphere_intersection(c0, r0, c1, r1): from chimerax.geometry import distance d = distance(c0, c1) if d > r0 + r1 or r1 + d < r0 or r0 + d < r1: return None ca = (r0 * r0 + d * d - r1 * r1) / (2 * r0 * d) if ca < -1 or ca > 1: return None c = (c1 - c0) / d return Circle(c, ca)
def restrain_ca_distances_to_template(template_residues, restrained_residues, distance_cutoff=8, spring_constant=500): ''' Creates a "web" of distance restraints between nearby CA atoms, restraining one set of residues to the same spatial organisation as another. Args: * template_residues: - a :class:`chimerax.atomic.Residues` instance. All residues must be from a single model, but need no be contiguous * restrained_residues: - a :class:`chimerax.atomic.Residues` instance. All residues must be from a single model (which may or may not be the same model as for `template_residues`). May be the same array as `template_residues` (which will just restrain all distances to their current values). * distance_cutoff (default = 8): - for each CA atom in `restrained_residues`, a distance restraint will be created between it and every other CA atom where the equivalent atom in `template_residues` is within `distance_cutoff` of its template equivalent. * spring_constant (default = 500): - the strength of each restraint, in :math:`kJ mol^{-1} nm^{-2}` ''' from chimerax.isolde import session_extensions as sx if len(template_residues) != len(restrained_residues): raise TypeError( 'Template and restrained residue arrays must be the same length!') template_us = template_residues.unique_structures if len(template_us) != 1: raise TypeError('Template residues must be from a single model!') restrained_us = restrained_residues.unique_structures if len(restrained_us) != 1: raise TypeError('Restrained residues must be from a single model!') restrained_model = restrained_us[0] template_cas = template_residues.atoms[template_residues.atoms.names == 'CA'] restrained_cas = restrained_residues.atoms[restrained_residues.atoms.names == 'CA'] template_coords = template_cas.coords drm = sx.get_distance_restraint_mgr(restrained_model) from chimerax.geometry import find_close_points, distance for i, rca1 in enumerate(restrained_cas): query_coord = numpy.array([template_coords[i]]) indices = find_close_points(query_coord, template_coords, distance_cutoff)[1] indices = indices[indices != i] for ind in indices: rca2 = restrained_cas[ind] dr = drm.add_restraint(rca1, rca2) dr.spring_constant = spring_constant dr.target = distance(query_coord[0], template_coords[ind]) dr.enabled = True
def _pick_atoms(self, pick): if pick is None: return None a = None r = None if hasattr(pick, 'atom'): a = pick.atom r = a.residue elif hasattr(pick, 'bond'): b = pick.bond coords = [a.coord for a in b.atoms] from chimerax.geometry import distance distances = [distance(c, pick.position) for c in coords] a = b.atoms[distances.index(min(distances))] r = a.residue elif hasattr(pick, 'residue'): r = pick.residue # Tug heavy atoms instead of hydrogens if a: if a.element.name == 'H': h_mode = self._tug_mgr.allow_hydrogens if h_mode == 'no': self.session.logger.warning( 'Tugging of hydrogens is not enabled. ' 'Applying tug to the nearest bonded heavy atom.') a = a.neighbors[0] elif h_mode == 'polar': for n in a.neighbors: if n.element.name == 'C': self.session.logger.warning( 'Tugging of non-polar hydrogens is not enabled. ' 'Applying tug to the nearest bonded heavy atom.' ) a = n break self._focal_atom = a #a = self._focal_atom = pick tm = self.tug_mode if tm == "atom" and a: from chimerax.atomic import Atoms pa = Atoms([a]) elif tm == "residue" and r: pa = r.atoms if a is not None: self._focal_atom = a else: self._focal_atom = r.principal_atom else: pa = None return pa
def pseudobond_connections(structures): pcon = {} from chimerax.atomic import concatenate, Atoms, interatom_pseudobonds from chimerax.geometry import distance satoms = concatenate([s.atoms for s in structures], Atoms) for pb in interatom_pseudobonds(satoms): a1, a2 = pb.atoms if pb.shown and pb.group.display: d12 = distance(a1.scene_coord, a2.scene_coord) pcon.setdefault(a1, []).append((a2,d12)) pcon.setdefault(a2, []).append((a1,d12)) return pcon
def interpolate_dihedral(i0, i1, i2, i3, coords0, coords1, f, coord_set): """ Computer coordinate of atom a0 by interpolating dihedral angle defined by atoms (a0, a1, a2, a3). """ t0 = time() from chimerax.geometry import distance, angle, dihedral, dihedral_point c00 = coords0[i0] c01 = coords0[i1] c02 = coords0[i2] c03 = coords0[i3] length0 = distance(c00, c01) angle0 = angle(c00, c01, c02) dihed0 = dihedral(c00, c01, c02, c03) c10 = coords1[i0] c11 = coords1[i1] c12 = coords1[i2] c13 = coords1[i3] length1 = distance(c10, c11) angle1 = angle(c10, c11, c12) dihed1 = dihedral(c10, c11, c12, c13) length = length0 + (length1 - length0) * f angle = angle0 + (angle1 - angle0) * f ddihed = dihed1 - dihed0 if ddihed > 180: ddihed -= 360 elif ddihed < -180: ddihed += 360 dihed = dihed0 + ddihed * f c1 = coord_set[i1, :] c2 = coord_set[i2, :] c3 = coord_set[i3, :] t2 = time() c0 = dihedral_point(c1, c2, c3, length, angle, dihed) t3 = time() coord_set[i0:] = c0 t1 = time() global iit, dpt iit += t1 - t0 dpt += t3 - t2
def metal_clash(metal_pos, pos, parent_pos, parent_atom, parent_type_info): if parent_atom.element.valence < 5 and parent_type_info.geometry != linear: # non-sp1 carbons, et al, can't coordinate metals return False from chimerax.geometry import distance, angle if distance(metal_pos, pos) > 2.7: # "_metal_dist" is 2.7 + putative S-H bond length of 1.25; # see nitrogen stripping in CYS 77 and 120 in 3r24 return False # 135.0 is not strict enough (see :1004.a in 1nyr) if angle(parent_pos, pos, metal_pos) > 120.0: return True return False
def _stitched_triangles(loop1_vertices, offset1, loop2_vertices, offset2): triangles = [] n1, n2 = len(loop1_vertices), len(loop2_vertices) i1 = i2 = 0 from chimerax.geometry import distance while i1 < n1 or i2 < n2: v1, v2 = loop1_vertices[i1%n1], loop2_vertices[i2%n2] vn1, vn2 = loop1_vertices[(i1+1)%n1], loop2_vertices[(i2+1)%n2] if i2 == n2: step1 = True elif i1 == n1: step1 = False else: step1 = (distance(v2,vn1) < distance(v1,vn2)) i3 = (offset1 + (i1+1)%n1) if step1 else (offset2 + (i2+1)%n2) triangles.append((offset1+i1%n1, offset2+i2%n2, i3)) if step1: i1 += 1 else: i2 += 1 return triangles
def get_cylinder(radius, p0, p1, bottom=True, top=True): h = distance(p0, p1) # TODO: chose number of triangles # TODO: separate cap into bottom and top vertices, normals, triangles = cylinder_geometry(radius, height=h, caps=bottom or top, nc=30) # rotate so z-axis matches p0->p1 xf = z_align(p0, p1) inverse = xf.inverse() vertices = inverse * (vertices + [0, 0, h / 2]) inverse.transform_normals(normals, in_place=True, is_rotation=True) return vertices, normals, triangles
def _file_output(file_name, info, naming_style): overlap_cutoff, hbond_allowance, bond_separation, intra_res, intra_mol, \ clashes, output_grouping, test_type, res_separation = info from chimerax.io import open_output out_file = open_output(file_name, 'utf-8') if test_type != "distances": print("Allowed overlap: %g" % overlap_cutoff, file=out_file) print("H-bond overlap reduction: %g" % hbond_allowance, file=out_file) print("Ignore %s between atoms separated by %d bonds or less" % (test_type, bond_separation), file=out_file) if res_separation: print( "Ignore %s between atoms in residues less than %d apart in sequence" % (test_type, res_separation), file=out_file) print("Detect intra-residue %s:" % test_type, intra_res, file=out_file) print("Detect intra-molecule %s:" % test_type, intra_mol, file=out_file) seen = set() data = [] from chimerax.geometry import distance for a, aclashes in clashes.items(): for c, val in aclashes.items(): if (c, a) in seen: continue seen.add((a, c)) if a in output_grouping: out1, out2 = a, c else: out1, out2 = c, a l1, l2 = out1.string(style=naming_style), out2.string( style=naming_style) data.append( (val, l1, l2, distance(out1.scene_coord, out2.scene_coord))) data.sort() data.reverse() print("\n%d %s" % (len(data), test_type), file=out_file) field_width1 = max([len(l1) for v, l1, l2, d in data] + [5]) field_width2 = max([len(l2) for v, l1, l2, d in data] + [5]) #print("%*s %*s overlap distance" % (0-field_width1, "atom1", 0-field_width2, "atom2"), print( f"{'atom1':^{field_width1}} {'atom2':^{field_width2}} overlap distance", file=out_file) for v, l1, l2, d in data: print(f"%*s %*s %5.3f %5.3f" % (0 - field_width1, l1, 0 - field_width2, l2, v, d), file=out_file) if file_name != out_file: # only close file if we opened it... out_file.close()
def _len_angle(new, n1, n2, template, bond_cache, angle_cache): from chimerax.geometry import distance, angle bond_key = (n1, new) angle_key = (n2, n1, new) try: bl = bond_cache[bond_key] ang = angle_cache[angle_key] except KeyError: n2pos = template.find_atom(n2).coord n1pos = template.find_atom(n1).coord newpos = template.find_atom(new).coord bond_cache[bond_key] = bl = distance(newpos, n1pos) angle_cache[angle_key] = ang = angle(newpos, n1pos, n2pos) return bl, ang
def spline_path(path, grid_spacing): oversample = 3 from chimerax.geometry import distance subdiv = max([ int(oversample * distance(path[i + 1], path[i]) / grid_spacing) for i in range(len(path) - 1) ]) from numpy import array npath = array(path) from chimerax.geometry import natural_cubic_spline points, tangents = natural_cubic_spline(npath, subdiv) epoints = equispaced_points(points, grid_spacing) return epoints
def get_cone(radius, p0, p1, bottom=False, xform=None, pure=False): h = distance(p0, p1) vertices, normals, triangles = surface.cone_geometry(radius, height=h, caps=bottom) from chimerax.geometry import z_align xf = z_align(p0, p1) inverse = xf.inverse() vertices = inverse * (vertices + [0, 0, h / 2]) inverse.transform_normals(normals, in_place=True, is_rotation=True) if xform is not None: xform.transform_points(vertices, in_place=True) xform.transform_normals(normals, in_place=True, is_rotation=pure) return vertices, normals, triangles
def get_cylinder(radius, p0, p1, closed=True, xform=None, pure=False): h = distance(p0, p1) vertices, normals, triangles = surface.cylinder_geometry(radius, height=h, caps=closed) # rotate so z-axis matches p0->p1 xf = z_align(p0, p1) inverse = xf.inverse() vertices = inverse * (vertices + [0, 0, h / 2]) inverse.transform_normals(normals, in_place=True, is_rotation=True) if xform is not None: xform.transform_points(vertices, in_place=True) xform.transform_normals(normals, in_place=True, is_rotation=pure) return vertices, normals, triangles
def array_slice_values(array, ijk_in, ijk_out, spacing=0.5, method='linear'): from chimerax.geometry import distance, place d = distance(ijk_in, ijk_out) steps = 1 + max(1, int(d / spacing)) from numpy import empty, single as floatc, arange, outer trace = empty((steps, 2), floatc) trace[:, 0] = t = arange(steps, dtype=floatc) / steps ijk = outer(1 - t, ijk_in) + outer(t, ijk_out) from _interpolate import interpolate_volume_data trace[:, 1], outside = interpolate_volume_data(ijk, place.identity(), array, method) return trace
def donor_hyd(don, cs_id, acc_coord): from chimerax.geometry import distance dha = hyd = None for h in don.neighbors: if h.element.number != 1: continue if cs_id is None: h_coord = h.scene_coord else: h_coord = h.get_coordset_coord(cs_id) d = distance(h_coord, acc_coord) if dha is None or d < dha: dha = d hyd = h return dha, hyd
def brace(atoms, max_length, max_loop_length, model, log): # Find all atom pairs within distance d of each other. apairs = [] xyz = atoms.scene_coords na = len(atoms) for i1 in range(na): if log and i1 > 0 and i1 % 1000 == 0: log.status('Close atom pairs %d of %d atoms' % (i1, na)) dxyz = xyz[i1+1:] - xyz[i1] d2 = (dxyz*dxyz).sum(axis=1) close = (d2 <= (max_length*max_length)) for i2 in close.nonzero()[0]: apairs.append((d2[i2],i1,i1+1+i2)) apairs.sort(key = lambda ap: ap[0]) # Map atom index to list of connected (atom index, distance) sc = {} con, ai = connections(atoms, max_loop_length, log) for i1,i2,d in con: sc.setdefault(i1,[]).append((i2,d)) # Add preexisting pseudobond connections for pb in model.pseudobonds: a1,a2 = pb.atoms i1,i2 = ai[a1], ai[a2] d = pb.length sc.setdefault(i1,[]).append((i2,d)) sc.setdefault(i2,[]).append((i1,d)) # Add connections between close atom pairs which are distantly connected. struts = [] from chimerax.geometry import distance for c, (d12, i1, i2) in enumerate(apairs): if log and c > 0 and c % 1000 == 0: log.status('Evaluating struts %d of %d' % (c, len(apairs))) if not short_connection(i1, i2, max_loop_length, sc): struts.append((i1,i2)) d = distance(xyz[i1], xyz[i2]) sc.setdefault(i1,[]).append((i2,d)) sc.setdefault(i2,[]).append((i1,d)) # Create pseudobonds for struts for i1,i2 in struts: a1, a2 = atoms[i1], atoms[i2] b = model.new_pseudobond(a1, a2) for a in (a1, a2): a.display = True