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 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 _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 pin_geometry(handle_radius, pin_radius, total_height): ''' Simple 3D representation of a drawing pin. Args: handle_radius: The radius of the "handle" in Angstroms pin_radius: The radius of the "pin" in Angstroms height: The overall height of the drawing ''' import numpy from chimerax.surface.shapes import cylinder_geometry, cone_geometry from chimerax.geometry import translation pin_height = total_height * 5 / 12 handle_height = total_height * 7 / 12 tb_height = total_height / 4 pin = list( cone_geometry(radius=pin_radius, height=pin_height, points_up=False)) handle_bottom = list( cone_geometry(radius=handle_radius, height=tb_height, nc=8)) handle_middle = list( cylinder_geometry(radius=handle_radius / 2, height=handle_height, nc=8, caps=False)) handle_top = list( cone_geometry(radius=handle_radius, height=tb_height, nc=8, points_up=False)) pint = translation((0, 0, pin_height / 2)) pin[0] = pint.transform_points(pin[0]) hbt = translation((0, 0, pin_height + tb_height / 2.05)) handle_bottom[0] = hbt.transform_points(handle_bottom[0]) hmt = translation((0, 0, handle_height / 2 + pin_height)) handle_middle[0] = hmt.transform_points(handle_middle[0]) htt = translation((0, 0, total_height - tb_height / 2.05)) handle_top[0] = htt.transform_points(handle_top[0]) vertices = numpy.concatenate( (pin[0], handle_bottom[0], handle_middle[0], handle_top[0])) normals = numpy.concatenate( (pin[1], handle_bottom[1], handle_middle[1], handle_top[1])) ntri = len(pin[0]) triangles = pin[2] for d in (handle_bottom, handle_middle, handle_top): triangles = numpy.concatenate((triangles, d[2] + ntri)) ntri += len(d[0]) return vertices, normals, triangles
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 random_translation(bounds): from random import random shift = [x0+random()*(x1-x0) for x0,x1 in zip(bounds.xyz_min, bounds.xyz_max)] from chimerax.geometry import translation tf = translation(shift) return tf
def _interpolate_camera(v1, v2, f, camera): c1, c2 = v1.camera, v2.camera # Interpolate camera position from chimerax.geometry import interpolate_rotation, interpolate_points p1, p2 = c1['position'], c2['position'] r = interpolate_rotation(p1, p2, f) la = interpolate_points(v1.look_at, v2.look_at, f) # Look-at points in camera coordinates cl1 = p1.inverse() * v1.look_at cl2 = p2.inverse() * v2.look_at cla = interpolate_points(cl1, cl2, f) # Make camera translation so that camera coordinate look-at point # maps to scene coordinate look-at point r*cla + t = la. from chimerax.geometry import translation t = translation(la - r * cla) camera.position = t * r # Interpolate field of view if 'field_of_view' in c1 and 'field_of_view' in c2: camera.field_of_view = (1 - f) * c1['field_of_view'] + f * c2['field_of_view'] elif 'field_width' in c1 and 'field_width' in c2: camera.field_width = (1 - f) * c1['field_width'] + f * c2['field_width'] camera.redraw_needed = True
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 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 translate_command(self, tokens): if len(tokens) != 4: raise ValueError("Expected 'x y z' after %s" % tokens[0]) data = [self.parse_float(x) for x in tokens[1:4]] xform = translation(data) self.transforms.append(self.transforms[-1] * xform) self.pure.append(self.pure[-1])
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 random_translation_step(center, radius): v = random_direction() from random import random r = radius * random() from chimerax.geometry import translation tf = translation(center + r*v) return tf
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 _translate(self, shift): psize = self.pixel_size() s = tuple(dx * psize * self.speed for dx in shift) # Scene units step = self.camera_position.transform_vector(s) # Scene coord system if self._moving_atoms: from chimerax.geometry import translation self._move_atoms(translation(step)) else: self.view.translate(step, self.models())
def apply_transform(self, tf): v = self.view cam = v.camera cp = cam.position cpinv = cp.inverse() if self.fly_mode: cr = cpinv * cam.position.origin() tf = tf.inverse() else: if tf.rotation_angle() <= 1e-5: v._update_center_of_rotation = True # Translation cr = cpinv * v.center_of_rotation from chimerax.geometry import translation stf = cp * translation(cr) * tf * translation(-cr) * cpinv if self.collision(stf.inverse() * cam.position.origin()): return v.move(stf)
def _orient(self): gc = [] la = [] for g, (x, y) in zip(self.groups, self._layout_positions): if g.shown(): gc.append(g.centroid()) la.append((x, y, 0)) if len(gc) < 2: return from chimerax.geometry import align_points, translation from numpy import array, mean p, rms = align_points(array(gc), array(la)) ra = p.zero_translation() center = mean(gc, axis=0) v = self._session().main_view rc = v.camera.position.zero_translation() rot = translation(center) * rc * ra * translation(-center) v.move(rot)
def _update_geometry(self): from chimerax.shape.shape import cylinder_geometry varray, tarray = cylinder_geometry( self.radius, self.thickness, max(2, int(self.thickness / 3 + 0.5)), max(40, int(20.0 * self.radius + 0.5)), True) from chimerax.geometry import translation, vector_rotation varray = (translation(self.plane.origin) * vector_rotation( (0, 0, 1), self.plane.normal)).transform_points(varray) from chimerax.surface import calculate_vertex_normals narray = calculate_vertex_normals(varray, tarray) self.set_geometry(varray, narray, tarray)
def update_pointer(self, msg): if 'name' in msg: if 'id' in msg: # If id not in msg leave name as "my pointer". self.name = '%s pointer' % msg['name'] if 'color' in msg: self.color = msg['color'] if 'mouse' in msg: xyz, axis = msg['mouse'] from chimerax.geometry import vector_rotation, translation p = translation(xyz) * vector_rotation((0, 0, 1), axis) self.position = p
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 __init__(self, name, session, color, center, radius): self._num_triangles = 1000 Model.__init__(self, name, session) from chimerax.surface import sphere_geometry2 va, na, ta = sphere_geometry2(self._num_triangles) self._unit_vertices = va self.set_geometry(radius * va, na, ta) self.color = color from chimerax.geometry import translation self.position = translation(center) self._radius = radius session.models.add([self])
def translate(self, shift, drawings=None): '''Move camera to simulate a translation of drawings. Translation is in scene coordinates.''' if shift[0] == 0 and shift[1] == 0 and shift[2] == 0: return if self._center_of_rotation_method in ('front center', 'center of view'): self._update_center_of_rotation = True self._shift_near_far_clip_planes(shift) from chimerax.geometry import translation t = translation(shift) self.move(t, drawings)
def surface_projection_coordinates(surfaces, projection_axis, volume): g = volume.data # Scale rotated surface coordinates to grid index units. axis_aligned = (tuple(projection_axis) in ((1, 0, 0), (0, 1, 0), (0, 0, 1)) and tuple(g.cell_angles) == (90, 90, 90) and g.rotation == ((1, 0, 0), (0, 1, 0), (0, 0, 1))) if axis_aligned: grid_spacing = g.step else: s = min(g.plane_spacings()) grid_spacing = (s, s, s) # Determine transform from vertex coordinates to depth array indices # Rotate projection axis to z. from chimerax.geometry import orthonormal_frame, scale, translation tfrs = orthonormal_frame(projection_axis).inverse() * scale( [1 / s for s in grid_spacing]) # Transform vertices to depth array coordinates. zsurf = [] tcount = 0 for vertices, triangles in surfaces: varray = tfrs.transform_points(vertices) zsurf.append((varray, triangles)) tcount += len(triangles) if tcount == 0: return None # Compute origin for depth grid vmin, vmax = bounding_box(zsurf) if axis_aligned: o = tfrs * g.origin offset = [(vmin[a] - o[a]) for a in (0, 1, 2)] from math import floor align_frac = [offset[a] - floor(offset[a]) for a in (0, 1, 2)] vmin -= align_frac else: vmin -= 0.5 tf = translation(-vmin) * tfrs # Shift surface vertices by depth grid origin for varray, triangles in zsurf: varray -= vmin # Compute size of depth grid from math import ceil size = tuple(int(ceil(vmax[a] - vmin[a] + 1)) for a in (0, 1)) return zsurf, size, tf
def unbend_volume(volume, path, yaxis, xsize, ysize, grid_spacing, subregion='all', step=1, model_id=None): # Compute correctly spaced cubic splined path points. points = spline_path(path, grid_spacing) axes = path_point_axes(points, yaxis) nx = int(xsize / grid_spacing) + 1 ny = int(ysize / grid_spacing) + 1 nz = len(points) # Create a rectangle of point positions to interpolate at. from numpy import empty, float32, arange section = empty((ny, nx, 3), float32) x = arange(nx, dtype=float32) * grid_spacing - 0.5 * (xsize - 1.0) y = arange(ny, dtype=float32) * grid_spacing - 0.5 * (ysize - 1.0) for j in range(ny): section[j, :, 0] = x for i in range(nx): section[:, i, 1] = y section[:, :, 2] = 0 s = section.reshape((ny * nx, 3)) # Interpolate planes to fill straightened array. from chimerax.geometry import translation m = empty((nz, ny, nx), float32) for k in range(nz): tf = translation(points[k]) * axes[k] m[k, :, :] = volume.interpolated_values(s, tf, subregion=subregion, step=step).reshape((ny, nx)) # Create volume. from chimerax.map_data import ArrayGridData step = [grid_spacing] * 3 origin = [0, 0, 0] g = ArrayGridData(m, origin, step, name='unbend') from chimerax.map import volume_from_grid_data v = volume_from_grid_data(g, volume.session, model_id=model_id) v.copy_settings_from(volume, copy_region=False, copy_active=False, copy_xform=open) return v
def zoom_camera(c, point, factor): if hasattr(c, 'field_width'): # Orthographic camera c.field_width /= factor else: # Perspective camera v = c.view_direction() p = c.position from chimerax.geometry import inner_product, translation delta_z = inner_product(p.origin() - point, v) zmove = (delta_z * (1 - factor) / factor) * v c.position = translation(zmove) * p c.redraw_needed = True
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 move_camera_and_cofr(self, dz): cofr = self.view.center_of_rotation cofr_method = self.view.center_of_rotation_method camera = self.view.camera cpos = self.camera_position.origin() vd = camera.view_direction() import numpy cc = cofr-cpos shift_vec = numpy.dot(cc/numpy.linalg.norm(cc), vd) *vd * dz from chimerax.geometry import translation t = translation(shift_vec) self.view.center_of_rotation += shift_vec self.view.center_of_rotation_method = cofr_method camera.set_position(t*camera.position)
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 map_covering_box(v, ijk_min, ijk_max, ijk_cell_size, symmetries, step): d = v.data if ijk_cell_size == d.size and (symmetries is None or len(symmetries) == 0): # Full unit cell and no symmetries to average. g = v.grid_data(subregion='all', step=step, mask_zone=False) from chimerax.map import volume cg = volume.map_from_periodic_map(g, ijk_min, ijk_max) return cg out_ijk_size = tuple(a - b + 1 for a, b in zip(ijk_max, ijk_min)) from chimerax.geometry import translation out_ijk_to_vijk_transform = translation(ijk_min) ijk_symmetries = ijk_symmetry_matrices(d, symmetries) from numpy import empty, float32 shape = list(reversed(out_ijk_size)) m = empty(shape, float32) from chimerax.map import extend_crystal_map nnc, dmax = extend_crystal_map(v.full_matrix(), ijk_cell_size, ijk_symmetries.array(), m, out_ijk_to_vijk_transform.matrix) log = v.session.logger log.info( 'Extended map %s to box of size (%d, %d, %d),\n' % ((v.name, ) + out_ijk_size) + ' cell size (%d, %d, %d) grid points, %d symmetry operations,\n' % (tuple(ijk_cell_size) + (len(ijk_symmetries), )) + ' %d points not covered by any symmetry,\n' % nnc + ' maximum value difference where symmetric map copies overlap = %.5g\n' % dmax) if nnc > 0: log.status('%d grid points not covered' % nnc) origin = d.ijk_to_xyz(ijk_min) from chimerax.map_data import ArrayGridData g = ArrayGridData(m, origin, d.step, d.cell_angles, name=v.name + ' extended') return g
def arrow_along_force_vector(arrow, xyz0, force, scale=1.0): ''' Takes an arrow drawn by simple_arrow and rotates, translates and scales it to point from xyz0 along a force vector, with length scaled according to the magnitude of the force. ''' from chimerax.geometry.place import translation, vector_rotation, scale, Place, product import numpy # Original arrow points along the z axis u = numpy.array((0, 0, 1), dtype='float32') # Get vector length l = numpy.linalg.norm(force) rot = vector_rotation(u, force / l) trans = translation(xyz0) sc = scale(l) arrow.position = product((trans, rot, sc))