def horseshoe_segment(self, side_length, semicircle_length, spacing, start_index): ''' Make a horseshoe segment with two sides each with side_length nucleotides parallel the y axis, curved top has semicircle_length nucleotides, spacing of nucleotides is spacing. Starting nucleotide index is start_index. ''' from chimerax.geometry import translation, rotation c = {} # Horseshoe side going up in y. ns = side_length b = start_index ls = spacing for i in range(ns): c[b + i] = translation((0, i * ls, 0)) * rotation((0, 0, 1), 90) from math import pi, cos, sin nc = semicircle_length r = 0.5 * ls / sin(0.5 * pi / (nc - 1)) for i in range(nc): a = i * pi / (nc - 1) a_deg = a * 180 / pi ca, sa = cos(a), sin(a) # Horeshoe curve c[b + ns + i] = translation( (r * (1 - ca), r * sa + ns * ls, 0)) * rotation( (0, 0, 1), 90 - a_deg) # Horseshoe side going down in y. for i in range(ns): c[b + ns + nc + i] = translation( (2 * r, (ns - 1 - i) * ls, 0)) * rotation((0, 0, 1), -90) return Segment(c, 2 * r, spacing)
def ring_arrow(major_radius, minor_radius, circle_segments, ring_segments, head_length, head_radius): import numpy #Find the starting angle from the length of the arrow head from math import asin, pi, degrees starting_angle = 2 * asin(head_length / (2 * major_radius)) vr, nr, tr = split_torus_geometry(major_radius, minor_radius, circle_segments, ring_segments, starting_angle * 0.8, 3 * pi / 2) vh, nh, th = cone_geometry(head_radius, head_length, circle_segments) from chimerax.geometry import rotation # Rotate the cone move_dir = numpy.array(((major_radius, 0, -head_length), ), numpy.double) r1 = rotation((1, 0, 0), -90) r1.transform_points(vh, in_place=True) r1.transform_points(move_dir, in_place=True) r2 = rotation((0, 0, 1), degrees(starting_angle)) r2.transform_points(vh, in_place=True) r2.transform_points(move_dir, in_place=True) # ... and move it into position vh += move_dir from numpy import concatenate v = concatenate((vr, vh)) n = concatenate((nr, nh)) t = concatenate((tr, th + len(vr))) return v, n, t
def _wobble_position(self, f): amax = 0.5 * self._full_angle from math import pi, sin a = sin(2 * pi * f) * amax wa = sin(4 * pi * f) * amax * self._wobble_aspect from chimerax.geometry import rotation r = rotation(self._axis, a, self._center) rw = rotation(self._wobble_axis, wa, self._center) return rw * r
def _set_pointer_shape(self, cross_diameter=1.0, stick_diameter=0.1): from chimerax.surface import cylinder_geometry, combine_geometry_vnt cva, cna, cta = cylinder_geometry(radius=0.5 * stick_diameter, height=cross_diameter) from chimerax.geometry import rotation rx90, ry90 = rotation((1, 0, 0), 90), rotation((0, 1, 0), 90) vax, nax = ry90 * cva, ry90.transform_vectors(cna) vay, nay = rx90 * cva, rx90.transform_vectors(cna) geom = [(vax, nax, cta), (vay, nay, cta)] if self._emulate == 'vr': geom.append((cva, cna, cta)) # z-axis va, na, ta = combine_geometry_vnt(geom) self.set_geometry(va, na, ta)
def correct_o_position(res, psi): n, ca, c, o = [res.find_atom(name) for name in ['N', 'CA', 'C', 'O']] from chimerax.geometry import rotation, dihedral d = dihedral(*[a.coord for a in (n, ca, c, o)]) r = rotation(c.coord - ca.coord, psi + 180 - d, center=c.coord) from chimerax.atomic import Atoms Atoms([o]).transform(r)
def angle_pos(atom_pos, bond_pos, bond_length, degrees, coplanar=None): if coplanar is not None and len(coplanar) > 0: # may have one or two coplanar positions specified, # if two, compute both resultant positions and average # (the up vector has to be negated for the second one) xforms = [] if len(coplanar) > 2: raise ValueError("More than 2 coplanar positions specified!") for cpos in coplanar: up = cpos - atom_pos if xforms: up = tinyarray.negative(up) # lookAt puts ref point opposite that of zAlign, so # also rotate 180 degrees around y axis xform = rotation( (0.0, 1.0, 0.0), 180.0) * look_at(atom_pos, bond_pos, up) xforms.append(xform) else: xforms = [z_align(atom_pos, bond_pos)] points = [] for xform in xforms: radians = pi * degrees / 180.0 angle = tinyarray.array( [0.0, bond_length * sin(radians), bond_length * cos(radians)]) points.append(xform.inverse() * angle) if len(points) > 1: midpoint = points[0] + (points[1] - points[0]) / 2.0 v = midpoint - atom_pos v = v * bond_length / norm(v) return atom_pos + v return tinyarray.array(points[0])
def helix_segment(self, base_index, count, spacing, rise): ''' Lay out a single turn helix on x axis. Radius is calculated to fit the specified number of nucleotides. ''' # Choose radius so one helix turn has desired length. # Helix arc length = s, radius = r, rise = h: s**2 = r**2 + (h/(2*pi))**2 length = spacing * count from math import sqrt, pi radius = sqrt((length / (2 * pi))**2 - (rise / (2 * pi))**2) # Compute screw motion to advance along helix angle = 2 * pi / count angle_deg = angle * 180 / pi from chimerax.geometry import translation, rotation, vector_rotation, Place step = translation((rise / count, 0, 0)) * rotation( (1, 0, 0), angle_deg, center=(0, radius, 0)) # Compute first nucleotide so P-P lies on helix. orient = vector_rotation((1, 0, 0), step * (0, 0, 0)) # Place nucleotides on helix. place = {} p = Place() for i in range(count): place[base_index + i] = p * orient p = step * p return Segment(place, rise, pad=0)
def _rotation_changed(self): v = self._map if v is None: return data = v.data if data.rotation == ((1,0,0),(0,1,0),(0,0,1)): axis, angle = (0,0,1), 0 else: from chimerax.geometry import Place axis, angle = Place(axes = data.rotation).rotation_axis_and_angle() rax = self._rotation_axis.value from .volume_viewer import vector_value naxis = vector_value(rax, axis) if naxis is None: self._status('Invalid rotation axis. Must be 3 numbers separated by spaces.') return if tuple(naxis) == (0,0,0): self._status('Rotation axis must be non-zero') return nangle = self._rotation_angle.value if tuple(naxis) != tuple(axis) or nangle != angle: from chimerax.geometry import rotation r = rotation(naxis, nangle) data.set_rotation(r.matrix[:,:3]) # Have to change xyz origin for index origin to remain the same. self._origin_changed() self._redraw_regions(data) self._status('Set rotation axis %s, angle %.3g' % (rax, nangle))
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 clip_rotate(self, axis, angle): v = self.view scene_axis = v.camera.position.transform_vector(axis) from chimerax.geometry import rotation r = rotation(scene_axis, angle, v.center_of_rotation) for p in self._planes(): p.normal = r.transform_vector(p.normal) p.plane_point = r * p.plane_point
def _extend_ends(self, centers, frac): n = len(centers) if self.curved: tc = self.center # Torus center r0,r1 = centers[0] - tc, centers[-1] - tc # radial vectors from center of torus. from chimerax.geometry import angle, rotation a = frac * angle(r0,r1) / (n-1) c0 = tc + rotation(self.axis, -a) * r0 c1 = tc + rotation(self.axis, a) * r1 else: e = frac/(n-1) * (centers[-1] - centers[0]) c0 = centers[0] - e c1 = centers[-1] + e n = len(centers) from numpy import concatenate ecenters = concatenate((c0.reshape(1,3), centers, c1.reshape(1,3))) return ecenters
def parse(text, session): from chimerax.core.commands import FloatsArg aa, text, rest = FloatsArg.parse(text, session) if len(aa) != 4: raise AnnotationError('Axis-angle must be 4 comma-separated floats, got %d from %s' % (len(aa), text)) from chimerax.geometry import rotation v = rotation(aa[:3], aa[3]) return v, text, rest
def _exclamation_mark(self): v, n, t = exclamation_mark(radius=0.1, height=0.5, nc = 8) flip = rotation((1,0,0), 180) flip.transform_points(v, in_place=True) flip.transform_vectors(n, in_place=True) translation((0,1,0)).transform_points(v, in_place=True) d = Drawing('rotamer cb indicator') d.set_geometry(v, n, t) return d
def circle_layout(self, segs, params, gap): ''' Layout on circle with a gap at the bottom, progressing clockwise from bottom left gap end point which is at the origin. This is used both for laying out a top level circle pattern and also for laying out the loops and stems attached in a circle to the end of a stem. ''' # Compute circle radius that exactly fits the segments # with segment end points lying on the circle. seg_lengths = [seg.width for seg in segs] + [seg.pad for seg in segs] + [gap] wc = value_counts(seg_lengths) radius = polygon_radius(list(wc.items())) # Translate from origin at bottom of circle to origin # at lower left gap end point. gap_angle_step = circle_angle(gap, radius) from math import pi, sin, cos a = 0.5 * gap_angle_step * pi / 180 from chimerax.geometry import translation, rotation stf = translation((radius * sin(a), -radius + radius * cos(a), 0)) # Layout segments on the circle coords = {} angle = 0.5 * gap_angle_step for seg in segs: angle_step = circle_angle(seg.width, radius) rtf = rotation((0, 0, 1), 180 - 0.5 * angle_step) if params.branch_tilt != 0: from random import random btf = rotation((1, 0, 0), params.branch_tilt * (1 - 2 * random())) rtf = rtf * btf ptf = rotation((0, 0, 1), -angle, center=(0, radius, 0)) ctf = stf * ptf * rtf for b, tf in seg.placements.items(): coords[b] = ctf * tf # Next position along circle. gap_step = circle_angle(seg.pad, radius) angle += angle_step + gap_step return coords
def point_symmetry(v, center, xyz, w, minimum_correlation=0.99, nmax=8): from chimerax import geometry # Look for icosahedral symmetry. csname = icosahedral_symmetry(v, center, xyz, w, minimum_correlation) if csname: syms = geometry.icosahedral_symmetry_matrices(csname, center) return ('Icosahedral %s' % csname), syms # Look for z-axis cyclic symmetry. n = cyclic_symmetry(nmax, v, (0, 0, 1), center, xyz, w, minimum_correlation) if n is None: return None, None # Check for octahedral symmetry. if n == 4: c4x, cm = correlation(v, xyz, w, (1, 0, 0), 90, center) if c4x >= minimum_correlation: syms = geometry.octahedral_symmetry_matrices(center) return 'Octahedral', syms # Check for tetrahedral symmetry 3-fold on z, EMAN convention. if n == 3: from math import sqrt a3yz = (0, -2 * sqrt(2) / 3, -1.0 / 3) # 3-fold in yz plane c3yz, cm = correlation(v, xyz, w, a3yz, 120, center) if c3yz >= minimum_correlation: syms = geometry.tetrahedral_symmetry_matrices('z3', center) return 'Tetrahedral z3', syms # Check for tetrahedral symmetry, 2-folds on x,y,z. c2x, cm = correlation(v, xyz, w, (1, 0, 0), 180, center) c2y, cm = correlation(v, xyz, w, (0, 1, 0), 180, center) if n == 2 and c2x >= minimum_correlation and c2y >= minimum_correlation: c3d, cm = correlation(v, xyz, w, (1, 1, 1), 120, center) if c3d >= minimum_correlation: syms = geometry.tetrahedral_symmetry_matrices('222', center) return 'Tetrahedral', syms # Check for dihedral symmetry, x axis flip. if c2x >= minimum_correlation: syms = geometry.dihedral_symmetry_matrices(n, center) return 'D%d' % n, syms # Check for dihedral symmetry, y axis flip. if c2y >= minimum_correlation: zrot = geometry.rotation((0, 0, 1), -90) syms = geometry.dihedral_symmetry_matrices(n).transform_coordinates( zrot) syms = geometry.recenter_symmetries(syms, center) return 'D%d, y flip' % n, syms syms = geometry.cyclic_symmetry_matrices(n, center) return ('C%d' % n), syms
def _rotate(self, axis, angle): # Convert axis from camera to scene coordinates saxis = self.camera_position.transform_vector(axis) angle *= self.speed if self._moving_atoms: from chimerax.geometry import rotation self._move_atoms( rotation(saxis, angle, center=self._atoms_center())) else: self.view.rotate(saxis, angle, self.models())
def rotate_nucleotides(self, segs, max_rotation): '''Rotate each nucleotide a random angle around P-P backbone.''' if max_rotation == 0: return from chimerax.geometry import rotation from random import random for seg in segs: for b, place in seg.placements.items(): a = (2 * random() - 1) * max_rotation seg.placements[b] = place * rotation((1, 0, 0), a)
def random_loop_orientations(coords, pmap, angle=90, center=(0, 0, 0)): ''' Rotate nucleotides in loops about x-axis by random amount. ''' from random import random from chimerax.geometry import rotation for b, tf in tuple(coords.items()): if not b in pmap: a = (2 * random() - 1) * angle rx = rotation((1, 0, 0), a, center) coords[b] = tf * rx
def correlation(v, xyz, w, axis, angle, center, rise=None): from chimerax.geometry import rotation, translation tf = rotation(axis, angle, center) if not rise is None: shift = translation([x * rise for x in axis]) tf = shift * tf xf = v.scene_position * tf wtf = v.interpolated_values(xyz, xf) from chimerax.map_fit.fitmap import overlap_and_correlation olap, cor, corm = overlap_and_correlation(w, wtf) return cor, corm
def update_position(self, *_): m = self.model fr = self.frame if fr >= self.frames: m.position = self.pos1 from chimerax.core.triggerset import DEREGISTER return DEREGISTER else: f = fr / self.frames from chimerax.geometry import translation, rotation m.position = translation(f * (self.c1 - self.c0)) * rotation( self.axis, f * self.angle, self.c0) * self.pos0 self.frame += 1
def post_geometry(radius, height, caps=False): ''' Returns a simple cylinder, rotated and translated so its base is on the origin and it points along (1,0,0) ''' from chimerax.surface.shapes import cylinder_geometry from chimerax.geometry import rotation, translation v, n, t = cylinder_geometry(radius=radius, height=height, caps=caps, nc=6) tr = translation([0, 0, height / 2]) tr.transform_points(v, in_place=True) r = rotation([0, 1, 0], 90) r.transform_points(v, in_place=True) r.transform_vectors(n, in_place=True) return v, n, t
def box_path(points, width, twist=0): from numpy import dot, concatenate, array # Find normal to bisecting plane through each point. n = len(points) p = array(points) from chimerax.geometry import normalize_vector as nv tangents = array([ nv( array(nv(p[min(i + 1, n - 1)] - p[i])) + array(nv(p[i] - p[max(i - 1, 0)]))) for i in range(n) ]) # Trace edge of square cross-section from start to finish. edges = [] y, z = p[2] - p[0], tangents[0] from chimerax.geometry import rotation, orthonormal_frame if twist != 0: y = rotation(z, twist) * y f = orthonormal_frame(z, y) xa, ya = f.axes()[:2] corners = ((1, 1), (-1, 1), (-1, -1), (1, -1)) for x, y in corners: ep = [points[0] + (x * 0.5 * width) * xa + (y * 0.5 * width) * ya] for i in range(n - 1): e0, p0, p1, t = ep[i], p[i], p[i + 1], tangents[i + 1] ep.append(e0 + (dot(p1 - e0, t) / dot(p1 - p0, t)) * (p1 - p0)) edges.append(ep) # Calculate triangles for each face of a surface model. # Make sharp edges. va = concatenate(edges + edges + edges + edges) ta = [] nc = len(corners) for s in range(n - 1): for c in range(nc): c1 = (c + 1) % nc + nc t = s + (s % 2) * 2 * nc * n ta.append((c * n + t, c1 * n + 1 + t, c * n + 1 + t)) ta.append((c * n + t, c1 * n + t, c1 * n + 1 + t)) # Add end caps. ta.extend([(nc * n + 0, nc * n + (2 + c) * n, nc * n + (1 + c) * n) for c in range(nc - 2)]) ta.extend([(n - 1, (1 + c) * n + n - 1, (2 + c) * n + n - 1) for c in range(nc - 2)]) ta = array(ta) return va, ta
def straight_layout(self, segs, params): '''Layout on x-axis starting at the origin, proceeding in positive x direction.''' coords = {} s = 0 from chimerax.geometry import translation, rotation for i, seg in enumerate(segs): rtf = rotation((1, 0, 0), params.branch_twist * i) stf = translation((s, 0, 0)) ptf = stf * rtf for b, tf in seg.placements.items(): coords[b] = ptf * tf # Next position along line s += seg.width + seg.pad return coords
def set_angle(self, angle): if angle == self._angle: return delta = angle - self._angle self._angle = angle moving, fixed = self.moving_side.coord, self.bond.other_atom( self.moving_side).coord from chimerax.geometry import z_align, rotation za = z_align(moving, fixed) update = za.inverse() * rotation((0, 0, -1), delta) * za side_atoms = self.bond.side_atoms(self.moving_side) coords = side_atoms.coords # avoid a copy... update.transform_points(coords, in_place=True) side_atoms.coords = coords self.rotation._rotater_update(self, delta)
def _rota_indicator(self): v1, n1, t1 = exclamation_mark(radius=0.1, height=0.5, nc = 8) v2, n2, t2 = spiral(major_radius=0.3, minor_radius = 0.05, height=0.4, turn_segments=6, circle_segments=3) translation((0,0,-0.15)).transform_points(v2, in_place=True) v = numpy.concatenate((v1, v2)) n = numpy.concatenate((n1, n2)) t = numpy.concatenate((t1, t2+len(v1))) r = rotation((1,0,0),180) r.transform_points(v, in_place=True) r.transform_vectors(n, in_place=True) translation((0,0.5,0.25)).transform_points(v, in_place=True) d = Drawing('rotamer indicator') d.skip_bounds = True d.pickable = False d.set_geometry(v, n, t) return d
def rotate(self, axis, angle, drawings=None): ''' Move camera to simulate a rotation of drawings about current rotation center. Axis is in scene coordinates and angle is in degrees. ''' if drawings: from chimerax.geometry import bounds b = bounds.union_bounds(d.bounds() for d in drawings) if b is None: return center = b.center() else: center = self.center_of_rotation from chimerax.geometry import rotation r = rotation(axis, angle, center) self.move(r, drawings)
def _rotate_slab(self, axis, angle, center=None): v = self._map vaxis = v.scene_position.inverse().transform_vector(axis) ro = v.rendering_options saxis, soffset = ro.tilted_slab_axis, ro.tilted_slab_offset thickness = ro.tilted_slab_spacing * (ro.tilted_slab_plane_count - 1) if center is None: rcenter = self._center else: rcenter = v.scene_position.inverse() * center from chimerax.geometry import rotation, inner_product axis = rotation(vaxis, angle).transform_vector(saxis) # offset = inner_product(axis, rcenter) - 0.5*thickness offset = soffset + inner_product(axis - saxis, rcenter) offset = keep_tilted_slab_in_box(v, offset, axis=axis) for m in [v] + self._matching_maps: m.set_parameters(tilted_slab_axis=axis, tilted_slab_offset=offset) # Make sure new plane is shown before another mouse event shows another plane. self.session.update_loop.update_graphics_now()
def rotate_command(self, tokens): if len(tokens) not in (3, 5): raise ValueError("Expected 'angle axis' after %s" % tokens[0]) if len(tokens) == 3: angle = self.parse_float(tokens[1]) if tokens[2] == 'x': axis = (1., 0., 0.) elif tokens[2] == 'y': axis = (1., 0., 0.) elif tokens[2] == 'z': axis = (1., 0., 0.) else: raise UserError("Expected 'x', 'y', or 'z' axis in %s" % tokens[0]) else: data = [self.parse_float(x) for x in tokens[1:5]] angle = data[0] axis = array(data[1:4]) xform = rotation(axis, angle) self.transforms.append(self.transforms[-1] * xform) self.pure.append(self.pure[-1])
def sym_axis_drawing_standard(fold_symmetry, axyz0, axyz1, base_radius=0.3): import numpy radius = base_radius * (1 + 0.1 * fold_symmetry) n = len(axyz0) radii = numpy.ones(n, numpy.float32) * radius from chimerax.geometry import Places, cylinder_rotations, rotation rot44 = numpy.empty([n, 4, 4], numpy.float32) cylinder_rotations(axyz0, axyz1, radii, rot44) rot44[:, 3, :3] = 0.5 * (axyz0 + axyz1) vertices = [] normals = [] triangles = [] colors = [] angle = 360 / fold_symmetry v, n, t = cylindrical_wedge_geometry(angle=angle, nc=3) colors_base = numpy.ones((len(v), 4), numpy.uint8) # colors_1 = numpy.array([_symmetry_colors[fold_symmetry]]*len(v), numpy.uint8) # colors_2 = numpy.ones(colors_1.shape, numpy.uint8) * 255 for i in range(fold_symmetry): colors_base[:, :] = _symmetry_colors[fold_symmetry][i] r = rotation((0, 0, 1), angle * i) vertices.append(r * v) normals.append(r.apply_without_translation(n)) triangles.append(t + len(v) * i) colors.append(colors_base.copy()) vertices = numpy.concatenate(vertices) normals = numpy.concatenate(normals) triangles = numpy.concatenate(triangles) colors = numpy.concatenate(colors) from chimerax.graphics import Drawing d = Drawing('{}-fold symmetry axis'.format(fold_symmetry)) d.set_geometry(vertices, normals, triangles) d.set_vertex_colors(colors) d.positions = Places(opengl_array=rot44) # d.position = Place(axes=place.axes(), origin = constant_point + (f0+f1/2)*axis_direction) return d
def curve_layout(self, segs, params, curve): ''' Layout along a curve which is a class instance such as HelixCurve or SphereSpiral defining position(t), velocity(t), normal(t) methods. Layout starts at t = 0 and goes toward increasing t values until all segments are layed out. ''' seg_lengths = [(seg.width, seg.pad) for seg in segs] placements = segments_on_curve(curve, seg_lengths) coords = {} from random import random from chimerax.geometry import rotation random_tilt = params.branch_tilt for seg, place in zip(segs, placements): tf = place if random_tilt != 0: tf = tf * rotation((1, 0, 0), random_tilt * (1 - 2 * random())) for b, stf in seg.placements.items(): coords[b] = tf * stf return coords