def ellipsoid_geometry(center, axes, axis_lengths, num_triangles = 1000): from chimerax.surface import sphere_geometry varray, narray, tarray = sphere_geometry(num_triangles) narray = narray.copy() # Is same as varray for sphere. from chimerax.geometry import Place, scale, normalize_vectors ptf = Place(axes = axes, origin = center) * scale(axis_lengths) ptf.transform_points(varray, in_place = True) ntf = Place(axes = axes) * scale([1/l for l in axis_lengths]) ntf.transform_vectors(narray, in_place = True) normalize_vectors(narray) return varray, narray, tarray
def scale_command(self, tokens): if len(tokens) not in (2, 3, 4): raise ValueError("Expected 'x [y [z]]' after %s" % tokens[0]) data = [self.parse_float(x) for x in tokens[1:]] if len(data) == 1: data.extend([data[0], data[0]]) elif len(data) == 2: data.append(data[0]) xform = scale(data) self.transforms.append(self.transforms[-1] * xform) self.pure.append(False)
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 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))
def arrow_between_points(arrow, xyz0, xyz1): ''' Takes an arrow drawn by simple_arrow and rotates, translates and scales it to point from xyz0 to xyz1 (or vice versa, if it's an inward-pointing arrow). In either case the arrow will have one end on xyz0. The other end will be on xyz1 if the height of the original arrow was 1 unit. ''' 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 from xyz0 to xyz1 v = xyz1 - xyz0 # Get vector length l = numpy.linalg.norm(v) rot = vector_rotation(u, v / l) trans = translation(xyz0) sc = scale(l) arrow.position = product((trans, rot, sc))
def exclamation_mark(radius=0.1, height=2, nc=8, color=[255, 0, 0, 255]): ''' An exclamation mark for annotating rotamer outliers ''' from chimerax.surface.shapes import cone_geometry, sphere_geometry2 from chimerax.geometry import translation, scale import numpy stem = cone_geometry(radius=radius, height=height, nc=nc, caps=True) spheres = list(sphere_geometry2(nc * 4)) spheres[0] = scale(radius * 0.7).transform_points(spheres[0]) v, n, t = stem vbottom = translation( (0, 0, height / 2 + radius * 1.5)).transform_points(spheres[0]) t = numpy.concatenate((t, spheres[2] + len(v))) v = numpy.concatenate((v, vbottom)) n = numpy.concatenate((n, spheres[1])) return v, n, t
def transform_by_id(seg, tf_id): from chimerax.geometry import Place, scale for tf in seg.transforms: if tf.id == tf_id: return scale((160, 160, 160)) * Place(tf.data_array) return Place()
def mesh_geometry(mesh, seg): # TODO: SFF format data structure mix vertices and normals, calling both vertices -- a nightmare. # Semantics of which normal belong with which vertices unclear (consecutive in polygon?). # Efficiency reading is horrible. Ask Paul K to make separate vertex and normal lists. nv = mesh.num_vertices // 2 from numpy import empty, float32 va = empty((nv, 3), float32) na = empty((nv, 3), float32) for i, v in enumerate(mesh.vertices): vid = v.id if vid != i: raise ValueError( 'Require mesh vertices be numbers consecutively from 0, got vertex id %d in position %d' % (vid, i)) d = v.designation # 'surface' or 'normal' if d == 'surface': if vid % 2 == 1: raise ValueError( 'Require odd mesh indices to be normals, got a vertex at position %d' % vid) va[vid // 2] = v.point elif d == 'normal': if vid % 2 == 0: raise ValueError( 'Require even mesh indices to be vertices, got a normal at position %d' % vid) na[vid // 2] = v.point else: raise ValueError( 'Vertex %d designation "%s" is not "surface" or "normal"' % (v.id, d)) ''' vids = list(set(v.id for v in mesh.vertices if v.designation == 'surface')) vids.sort() print ('vertex ids', vids[:3], 'num', len(vids), 'last', vids[-1]) ''' if mesh.transform_id is None: from chimerax.geometry import Place, scale # transform = scale((160,160,160)) * Place(seg.transforms[0].data_array) transform = Place(seg.transforms[0].data_array) * scale( (160, 160, 160)) else: transform = transform_by_id(seg, mesh.transform_id) transform.transform_points(va, in_place=True) transform.transform_normals(na, in_place=True) tri = [] for p in mesh.polygons: # print ('poly', len(p.vertex_ids), p.vertex_ids[:6]) t = tuple(vid // 2 for vid in p.vertices if vid % 2 == 0) if len(t) != 3: raise ValueError( 'Require polygons to be single triangles, got polygon with %d vertices' % len(t)) tri.append(t) ''' last_vid = None for vid in p.vertex_ids: if vid % 2 == 0: if last_vid is not None: # tri.append((vid//2,last_vid//2)) tri.append((vid//2,last_vid//2,vid//2)) last_vid = vid first_vid = p.vertex_ids[0] tri.append((last_vid//2,first_vid//2,last_vid//2)) ''' from numpy import array, int32 ta = array(tri, int32) return va, na, ta
def _head_position(self, vr_camera): from chimerax.geometry import scale return _place_matrix(vr_camera.room_position * scale(1 / vr_camera.scene_scale))
def imod_models(session, chunk_list, name, mesh, contours): # Get coordinate transform before mesh and contour chunks tf = None use_minx = False if use_minx: for c in chunk_list: if c['id'] == b'MINX': # MINX chunk comes after mesh and contours. print('MINX cscale', c['cscale'], 'ctrans', c['ctrans'], 'otrans', c['otrans'], 'crot', c['crot']) # t = [xo-xc for xo,xc in zip(c['otrans'], c['ctrans'])] t = [-xc for xc in c['ctrans']] rx, ry, rz = c['crot'] rx = ry = rz = 0 from chimerax.geometry import rotation, translation, scale tf = (rotation((0, 0, 1), rz) * rotation( (0, 1, 0), ry) * rotation( (1, 0, 0), rx) * translation(t) * scale(c['cscale'])) surfaces = [] msets = [] pixel_size = None for c in chunk_list: cid = c['id'] if cid == b'IMOD': xyz_scale = c['xscale'], c['yscale'], c['zscale'] pixel_size = c['pixsize'] units = c['units'] # Units: 0 = pixels, 3 = km, 1 = m, -2 = cm, -3 = mm, # -6 = microns, -9 = nm, -10 = Angstroms, -12 = pm # print ('IMOD pixel size =', pixel_size, 'units', units, ' scale', xyz_scale) # print 'IMOD flags', bin(c['flags']) u = -10 if units == 0 else (0 if units == 1 else units) import math pixel_size_angstroms = pixel_size * math.pow(10, 10 + u) if tf is None: xs, ys, zs = [s * pixel_size_angstroms for s in xyz_scale] from chimerax.geometry import scale tf = scale((xs, ys, zs)) elif cid == b'OBJT': alpha = 1.0 - 0.01 * c['trans'] object_rgba = (c['red'], c['green'], c['blue'], alpha) obj_name = c['name'].decode('ascii') pds = c['pdrawsize'] radius = pds * pixel_size_angstroms if pds > 0 else pixel_size_angstroms fill = c['flags'] & (1 << 8) lines = not (c['flags'] & (1 << 11)) only_points = (not lines and not fill) link = not only_points open_contours = c['flags'] & (1 << 3) mset = None elif cid == b'MESH': if mesh: surf = create_mesh(session, c, tf, object_rgba, obj_name) surfaces.append(surf) elif cid == b'CONT': if contours: if mset is None: from chimerax.markers import MarkerSet mname = '%s %s contours' % (name, obj_name) mset = MarkerSet(session, mname) msets.append(mset) create_contour(c, tf, radius, object_rgba, link, open_contours, mset) return surfaces, msets, pixel_size
def draw_slab(nd, residue, name, params): shapes = [] standard = standard_bases[name] ring_atom_names = standard["ring atom names"] atoms = get_ring(residue, ring_atom_names) if not atoms: return shapes plane = Plane([a.coord for a in atoms]) info = find_dimensions(params.dimensions) tag = standard['tag'] slab_corners = info[tag] origin = residue.find_atom(anchor(info[ANCHOR], tag)).coord origin = plane.nearest(origin) pts = [plane.nearest(a.coord) for a in atoms[0:2]] y_axis = pts[0] - pts[1] normalize_vector(y_axis) x_axis = numpy.cross(y_axis, plane.normal) xf = Place(matrix=((x_axis[0], y_axis[0], plane.normal[0], origin[0]), (x_axis[1], y_axis[1], plane.normal[1], origin[1]), (x_axis[2], y_axis[2], plane.normal[2], origin[2]))) xf = xf * standard["correction factor"] color = residue.ring_color half_thickness = params.thickness / 2 llx, lly = slab_corners[0] llz = -half_thickness urx, ury = slab_corners[1] urz = half_thickness center = (llx + urx) / 2, (lly + ury) / 2, 0 if params.shape == 'box': llb = (llx, lly, llz) urf = (urx, ury, urz) xf2 = xf va, na, ta = box_geometry(llb, urf) pure_rotation = True elif params.shape == 'muffler': radius = (urx - llx) / 2 * _SQRT2 xf2 = xf * translation(center) xf2 = xf2 * scale((1, 1, half_thickness * _SQRT2 / radius)) height = ury - lly va, na, ta = get_cylinder(radius, numpy.array((0, -height / 2, 0)), numpy.array((0, height / 2, 0))) pure_rotation = False elif params.shape == 'ellipsoid': # need to reach anchor atom xf2 = xf * translation(center) sr = (ury - lly) / 2 * _SQRT3 xf2 = xf2 * scale( ((urx - llx) / 2 * _SQRT3 / sr, 1, half_thickness * _SQRT3 / sr)) va, na, ta = get_sphere(sr, (0, 0, 0)) pure_rotation = False else: raise RuntimeError('unknown base shape') description = '%s %s' % (residue, tag) xf2.transform_points(va, in_place=True) xf2.transform_normals(na, in_place=True, is_rotation=pure_rotation) shapes.append(AtomicShapeInfo(va, na, ta, color, atoms, description)) if not params.orient: return shapes # show slab orientation by putting "bumps" on surface if tag == PYRIMIDINE: center = (llx + urx) / 2.0, (lly + ury) / 2, half_thickness va, na, ta = get_sphere(half_thickness, center) xf.transform_points(va, in_place=True) xf.transform_normals(na, in_place=True, is_rotation=True) shapes.append(AtomicShapeInfo(va, na, ta, color, atoms, description)) else: # purine center = (llx + urx) / 2.0, lly + (ury - lly) / 3, half_thickness va, na, ta = get_sphere(half_thickness, center) xf.transform_points(va, in_place=True) xf.transform_normals(na, in_place=True, is_rotation=True) shapes.append(AtomicShapeInfo(va, na, ta, color, atoms, description)) center = (llx + urx) / 2.0, lly + (ury - lly) * 2 / 3, half_thickness va, na, ta = get_sphere(half_thickness, center) xf.transform_points(va, in_place=True) xf.transform_normals(na, in_place=True, is_rotation=True) shapes.append(AtomicShapeInfo(va, na, ta, color, atoms, description)) return shapes