def next_direction(direction, e, vertices, normal1, normal2): ev = edge_vector(e, vertices) from chimerax.geometry import cross_product, inner_product, normalize_vector en1, en2 = cross_product(ev, normal1), cross_product(ev, normal2) d = inner_product(direction, ev) * ev + inner_product(direction, en1) * en2 d = normalize_vector(d) return d
def extrusion_transforms(path, tangents, yaxis=None): from chimerax.geometry import identity, vector_rotation, translation tflist = [] if yaxis is None: # Make xy planes for coordinate frames at each path point not rotate # from one point to next. tf = identity() n0 = (0, 0, 1) for p1, n1 in zip(path, tangents): tf = vector_rotation(n0, n1) * tf tflist.append(translation(p1) * tf) n0 = n1 else: # Make y-axis of coordinate frames at each point align with yaxis. from chimerax.geometry import normalize_vector, cross_product, Place for p, t in zip(path, tangents): za = t xa = normalize_vector(cross_product(yaxis, za)) ya = cross_product(za, xa) tf = Place( ((xa[0], ya[0], za[0], p[0]), (xa[1], ya[1], za[1], p[1]), (xa[2], ya[2], za[2], p[2]))) tflist.append(tf) return tflist
def polygon_coordinate_frame(polygon): p = polygon c = p.center() za = p.normal() xa = p.vertices[0].coord - c from chimerax.geometry import normalize_vector, cross_product, Place ya = normalize_vector(cross_product(za, xa)) xa = normalize_vector(cross_product(ya, za)) tf = [(xa[a], ya[a], za[a], c[a]) for a in (0, 1, 2)] return Place(tf)
def next_edge(p, direction, edges, vertices): from chimerax.geometry import cross_product, inner_product for e in edges: ev1, ev2 = vertices[e[0]] - p, vertices[e[1]] - p n = cross_product(ev1, ev2) tn = cross_product(direction, n) i1, i2 = inner_product(tn, ev1), inner_product(tn, ev2) if (i1 < 0 and i2 > 0) or (i1 > 0 and i2 < 0): # Edge is split by geodesic direction f = i1 / (i1 - i2) p2 = (1 - f) * vertices[e[0]] + f * vertices[e[1]] return e, f return None, None
def edge_coordinate_frame(edge): x0, x1 = edge_alignment_points(edge) p = edge.polygon c = p.center() c01 = 0.5 * (x0 + x1) from chimerax.geometry import cross_product, normalize_vector, Place xa = normalize_vector(x1 - x0) za = normalize_vector(cross_product(xa, c01 - c)) ya = cross_product(za, xa) tf = Place(((xa[0], ya[0], za[0], c01[0]), (xa[1], ya[1], za[1], c01[1]), (xa[2], ya[2], za[2], c01[2]))) return tf
def stem_residue_orientation(self, p, next_p, pair_p): from chimerax.geometry import cross_product, orthonormal_frame from numpy import array x, y = next_p - p, array(pair_p) - p z = cross_product(x, y) tf = orthonormal_frame(z, xdir=x, origin=p) return tf
def scene_position_2d(self, hand_pose, view): # Adjust origin cpos = hand_pose.origin() - self._center # Scale millimeters to scene units cpos /= self._width # millimeters to unit width # Rotate to screen orientation from chimerax.geometry import cross_product, orthonormal_frame yaxis = self._facing zaxis = cross_product(yaxis, self._cord) cpos = orthonormal_frame(zaxis, ydir=yaxis) * cpos # Convert to window pixels w, h = view.window_size win_xy = (w / 2 + w * cpos[0], h / 2 - h * cpos[1]) # Map to scene near front clip plane. xyz_min, xyz_max = view.clip_plane_points(win_xy[0], win_xy[1]) scene_point = (0, 0, 0) if xyz_min is None else (.9 * xyz_min + .1 * xyz_max) # Convert camera coordinates to scene coordinates rot = view.camera.position.zero_translation() from chimerax.geometry import translation scene_pos = translation(scene_point) * rot return scene_pos, win_xy
def scene_position_6d(self, hand_pose, view): from chimerax.geometry import translation, cross_product from chimerax.geometry import orthonormal_frame, inner_product # Adjust origin cpos = translation(-self._center) * hand_pose # Scale millimeters to scene units scene_center = view.center_of_rotation cam = view.camera factor = cam.view_width(scene_center) / self._width cpos = cpos.scale_translation(factor) # millimeters to scene units # Rotate to screen orientation yaxis = self._facing zaxis = cross_product(yaxis, self._cord) cpos = orthonormal_frame(zaxis, ydir=yaxis) * cpos # Adjust depth origin to center of rotation. cam_pos = cam.position depth = inner_product(scene_center - cam_pos.origin(), -cam_pos.z_axis()) cpos = translation( (0, 0, -depth)) * cpos # Center in front of camera (-z axis). # Convert camera coordinates to scene coordinates scene_pos = cam_pos * cpos # Map from camera to scene coordinates return scene_pos
def interpolate_corkscrew(xf, c0, c1, minimum_rotation = 0.1): ''' Rotate and move along a circular arc perpendicular to the rotation axis and translate parallel the rotation axis. This makes the initial geometric center c0 traverse a helical path to the final center c1. The circular arc spans an angle equal to the rotation angle so it is nearly a straight segment for small angles, and for the largest possible rotation angle of 180 degrees it is a half circle centered half-way between c0 and c1 in the plane perpendicular to the rotation axis. Rotations less than the minimum (degrees) are treated as no rotation. ''' from chimerax.geometry import normalize_vector dc = c1 - c0 axis, angle = xf.rotation_axis_and_angle() # a is in degrees. if abs(angle) < minimum_rotation: # Use linear instead of corkscrew interpolation to # avoid numerical precision problems at small rotation angles. # ChimeraX bug #2928. center = c0 shift = dc else: from chimerax.geometry import inner_product, cross_product, norm tra = inner_product(dc, axis) # Magnitude of translation along rotation axis. shift = tra*axis vt = dc - tra * axis # Translation perpendicular to rotation axis. v0 = cross_product(axis, vt) if norm(v0) == 0 or angle == 0: center = c0 else: import math l = 0.5*norm(vt) / math.tan(math.radians(angle / 2)) center = c0 + 0.5*vt + l*normalize_vector(v0) return axis, angle, center, shift
def __init__(self, axis=(0, 1, 0), center=(0, 0, 0), angle=90, rock=None, wobble=None, wobble_aspect=0.3, wobble_axis=None, camera=None, models=None, atoms=None): self._axis = axis self._center = center self._full_angle = angle self._rock = rock self._wobble = wobble if wobble is not None and wobble_axis is None: from chimerax.geometry import cross_product, normalize_vector wobble_axis = normalize_vector( cross_product(camera.view_direction(), axis)) self._wobble_axis = wobble_axis self._wobble_aspect = wobble_aspect self._wobble_axis = wobble_axis self._camera = camera self._models = models self._atoms = atoms
def surface_path(session, surface, length=100, rgba=(255, 255, 0, 255), radius=1, geodesic=True): ep = edge_pairs(surface.triangles) from random import choice e = choice(tuple(ep.keys())) # e = first_element(ep.keys()) vertices = surface.vertices if geodesic: tnormals = edge_triangle_normals(surface.vertices, surface.triangles) # Start in direction perpendicular to starting edge. eo = (e[1], e[0]) ev = edge_vector(eo, vertices) from chimerax.geometry import cross_product, normalize_vector direction = normalize_vector(cross_product(tnormals[eo], ev)) else: direction, tnormals = None points = make_surface_path(e, ep, length, vertices, direction, tnormals) from chimerax.markers import MarkerSet, create_link ms = MarkerSet(session, 'surface path') mprev = None for id, xyz in enumerate(points): m = ms.create_marker(xyz, rgba, radius, id) if mprev is not None: create_link(mprev, m, rgba=rgba, radius=radius) mprev = m session.models.add([ms])
def edge_triangle_normals(vertices, triangles): tn = {} from chimerax.geometry import cross_product, normalize_vector for v1, v2, v3 in triangles: x1, x2, x3 = vertices[v1], vertices[v2], vertices[v3] n = normalize_vector(cross_product(x2 - x1, x3 - x1)) tn[(v1, v2)] = tn[(v2, v3)] = tn[(v3, v1)] = n return tn
def curve_segment_placement(curve, t0, t1): ''' Create a Place instance mapping segment (0,0,0), (0,0,length) to curve points at parameter value t0 and t1. ''' from chimerax.geometry import normalize_vector, cross_product, Place if t1 > t0: xyz0, xyz1 = curve.position(t0), curve.position(t1) x_axis = normalize_vector(xyz1 - xyz0) center = xyz0 else: x_axis = normalize_vector(curve.velocity(t0)) center = curve.position(t0) y_axis = normalize_vector(curve.normal( 0.5 * (t0 + t1))) # May not be normal to x_axis z_axis = normalize_vector(cross_product(x_axis, y_axis)) y_axis = normalize_vector(cross_product(z_axis, x_axis)) p = Place(axes=(x_axis, y_axis, z_axis), origin=center) return p
def _patient_axes(self): files = self._file_info if files: # TODO: Different files can have different orientations. # For example, study 02ef8f31ea86a45cfce6eb297c274598/series-000004. # These should probably be separated into different series. orient = files[0]._orientation if orient is not None: x_axis, y_axis = orient[0:3], orient[3:6] from chimerax.geometry import cross_product z_axis = cross_product(x_axis, y_axis) return (x_axis, y_axis, z_axis) return ((1,0,0),(0,1,0),(0,0,1))
def rna_nucleotide_templates(session, file='rna_templates_6pj6.cif', chain_id='I', residue_numbers={ 'A': 2097, 'C': 2096, 'G': 2193, 'U': 2192 }): ''' Return residues A,G,C,U with P at (0,0,0) and next residue P at (x,0,0) with x>0 and rotated so that a basepaired residue P is in the xy-plane with y > 0. These are used for building an atomic model from a P-atom backbone trace. ''' # Read template residues from mmCIF file from os.path import join, dirname path = join(dirname(__file__), file) from chimerax.mmcif import open_mmcif mols, msg = open_mmcif(session, path) m = mols[0] # Calculate transforms to standard coordinates. res_tf = [] pair_name = {'A': 'U', 'U': 'A', 'G': 'C', 'C': 'G'} from chimerax.geometry import cross_product, orthonormal_frame for resname in ('A', 'C', 'G', 'U'): r = m.find_residue(chain_id, residue_numbers[resname]) rnext = m.find_residue(chain_id, residue_numbers[resname] + 1) rpair = m.find_residue(chain_id, residue_numbers[pair_name[resname]]) a0, a1, a2 = r.find_atom('P'), rnext.find_atom('P'), rpair.find_atom( 'P') o, x, y = a0.coord, a1.coord, a2.coord z = cross_product(x - o, y - o) tf = orthonormal_frame(z, xdir=x - o, origin=o).inverse() res_tf.append((resname, r, tf)) # Transform template residues to standard coordinate frame. res = {} for name, r, tf in res_tf: ratoms = r.atoms ratoms.coords = tf * ratoms.coords res[name] = r res['T'] = res['U'] return res
def circle_intercepts(c0, c1): from chimerax.geometry import inner_product, cross_product ca01 = inner_product(c0.center, c1.center) x01 = cross_product(c0.center, c1.center) s2 = inner_product(x01, x01) if s2 == 0: return None, None ca0 = c0.cos_angle ca1 = c1.cos_angle a = (ca0 - ca01 * ca1) / s2 b = (ca1 - ca01 * ca0) / s2 d2 = (s2 - ca0 * ca0 - ca1 * ca1 + 2 * ca01 * ca0 * ca1) if d2 < 0: return None, None d = sqrt(d2) / s2 return (a * c0.center + b * c1.center - d * x01, a * c0.center + b * c1.center + d * x01)
def test_phi(dp, ap, bp, phi_plane, phi): if phi_plane: normal = normalize_vector( cross_product(phi_plane[1] - phi_plane[0], phi_plane[2] - phi_plane[1])) D = dot(normal, phi_plane[1]) bproj = project(bp, normal, D) aproj = project(ap, normal, D) dproj = project(dp, normal, D) ang = angle(bproj, aproj, dproj) if ang < phi: if hbond.verbose: print("phi criteria failed (%g < %g)" % (ang, phi)) return False if hbond.verbose: print("phi criteria OK (%g >= %g)" % (ang, phi)) else: if hbond.verbose: print("phi criteria irrelevant") return True
def moments_of_inertia(vw): from numpy import zeros, float, array, dot, outer, argsort, linalg, identity i = zeros((3,3), float) c = zeros((3,), float) w = 0 for xyz, weights in vw: xyz, weights = array(xyz), array(weights) n = len(xyz) if n > 0 : wxyz = weights.reshape((n,1)) * xyz w += weights.sum() i += (xyz*wxyz).sum()*identity(3) - dot(xyz.transpose(),wxyz) c += wxyz.sum(axis = 0) if w == 0: return None, None, None # All weights are zero. i /= w c /= w # Center of vertices i -= dot(c,c)*identity(3) - outer(c,c) eval, evect = linalg.eigh(i) # Sort by eigenvalue size. order = argsort(eval) seval = eval[order] sevect = evect[:,order] axes = sevect.transpose() from chimerax.geometry import inner_product, cross_product if inner_product(cross_product(axes[0],axes[1]),axes[2]) < 0: axes[2,:] = -axes[2,:] # Make axes a right handed coordinate system # Make rows of 3 by 3 matrix the principle axes. return axes, seval, c
def tetra_pos(bondee, bonded, bond_len, toward=None, away=None, toward2=None, away2=None): new_bonded = [] cur_bonded = bonded[:] if len(cur_bonded) == 0: pos = single_pos(bondee, bond_len, toward, away) toward = toward2 away = away2 new_bonded.append(pos) cur_bonded.append(pos) if len(cur_bonded) == 1: # add at 109.5 degree angle coplanar = toward or away if coplanar: coplanar = [coplanar] else: coplanar = None pos = angle_pos(bondee, cur_bonded[0], bond_len, 109.5, coplanar=coplanar) if toward or away: # find the other 109.5 position in the toward/away # plane and the closer/farther position as appropriate old = normalize(bondee - cur_bonded[0]) new = pos - bondee midpoint = tinyarray.array(bondee + old * norm(new) * cos705) other_pos = pos + (midpoint - pos) * 2 d1 = sqlength(pos - (toward or away)) d2 = sqlength(other_pos - (toward or away)) if toward is not None: if d2 < d1: pos = other_pos elif away is not None and d2 > d1: pos = other_pos new_bonded.append(pos) cur_bonded.append(pos) if len(cur_bonded) == 2: # add along anti-bisector of current bonds and raised up # 54.75 degrees from plane of those bonds (half of 109.5) v1 = normalize(cur_bonded[0] - bondee) v2 = normalize(cur_bonded[1] - bondee) anti_bi = normalize(tinyarray.negative(v1 + v2)) # in order to stabilize the third and fourth tetrahedral # positions, cross the longer vector by the shorter if sqlength(v1) > sqlength(v2): cross_v = normalize(cross_product(v1, v2)) else: cross_v = normalize(cross_product(v2, v1)) anti_bi = anti_bi * cos5475 * bond_len cross_v = cross_v * sin5475 * bond_len pos = bondee + anti_bi + cross_v if toward or away: other_pos = bondee + anti_bi - cross_v d1 = sqlength(pos - (toward or away)) d2 = sqlength(other_pos - (toward or away)) if toward is not None: if d2 < d1: pos = other_pos elif away is not None and d2 > d1: pos = other_pos new_bonded.append(pos) cur_bonded.append(pos) if len(cur_bonded) == 3: unitized = [] for cb in cur_bonded: v = normalize(cb - bondee) unitized.append(bondee + v) pl = Plane(unitized) normal = pl.normal # if normal on other side of plane from bondee, we need to # invert the normal; the (signed) distance from bondee # to the plane indicates if it is on the same side # (positive == same side) d = pl.distance(bondee) if d < 0.0: normal = tinyarray.negative(normal) new_bonded.append(bondee + normal * bond_len) return new_bonded
def triangle_normal(v0,v1,v2): e10, e20 = v1 - v0, v2 - v0 from chimerax.geometry import normalize_vector, cross_product n = normalize_vector(cross_product(e10, e20)) return n
def normal(self): x0, x1, x2 = [m.coord for m in self.vertices[:3]] from chimerax.geometry import normalize_vector, cross_product n = normalize_vector(cross_product(x1 - x0, x2 - x1)) return n