def dashed_cylinder_command(self, tokens): if len(tokens) not in (9, 10) or (len(tokens) == 10 and tokens[9] != 'open'): raise ValueError( "Expected 'count x1 y1 z1 x2 y2 z2 radius [open]' after %s" % tokens[0]) count = self.parse_int(tokens[1]) data = [self.parse_float(x) for x in tokens[2:9]] p0 = array(data[0:3]) p1 = array(data[3:6]) radius = data[6] if len(tokens) < 10: closed = True else: closed = False self.num_objects += 1 if self.cur_description is not None: description = self.cur_description else: description = 'object %d: dashed cylinder' % self.num_objects vertices, normals, triangles = get_dashed_cylinder( count, radius, p0, p1, closed=closed, xform=self.transforms[-1], pure=self.pure[-1]) shape = AtomicShapeInfo(vertices, normals, triangles, _cvt_color(self.cur_color), self.cur_atoms, description) self.shapes.append(shape)
def polygon_command(self, tokens): # TODO: use GLU to tesselate polygon # for now, find center and make a triangle fan if len(tokens) % 3 != 1: raise UserError("Expected 'x1 y1 z1 ... xN yN zN' after %s" % tokens[0]) data = [self.parse_float(x) for x in tokens[1:]] vertices = array(data, dtype=float32) n = len(data) // 3 vertices.shape = (n, 3) if n < 3: raise UserError("Need at least 3 vertices in a polygon") self.num_objects += 1 if self.cur_description is not None: description = self.cur_description else: description = 'object %d: polygon' % self.num_objects from chimerax.geometry import Plane plane = Plane(vertices) loops = ((0, len(vertices) - 1), ) t = surface.triangulate_polygon(loops, plane.normal, vertices) normals = empty(vertices.shape, dtype=float32) normals[:] = plane.normal triangles = array(t, dtype=int32) shape = AtomicShapeInfo(vertices, normals, triangles, _cvt_color(self.cur_color), self.cur_atoms, description) self.shapes.append(shape)
def orient_planar_ring(nd, atoms, ring_indices): shapes = [] r = atoms[0].residue # TODO: # if not r.fill_display or r.fill_mode != r.Thick: # # can't show orientation of thin nor aromatic ring # return shapes pts = [a.coord for a in atoms] bonds = bonds_between(atoms) # if chimera.Bond.Wire in [b.draw_mode for b in bonds]: # radius = 0 # else: if 1: radius = min([b.radius for b in bonds]) if radius == 0: # can't show orientation of thin ring return shapes color = r.ring_color # non-zero radius planeEq = Plane(pts) offset = planeEq.normal * radius for r in ring_indices: center = numpy.average([pts[i] for i in r], axis=0) + offset va, na, ta = get_sphere(radius, center) shapes.append(AtomicShapeInfo(va, na, ta, color, str(atoms))) return shapes
def cone_command(self, tokens): if len(tokens) not in (8, 9) or (len(tokens) == 9 and tokens[8] != 'open'): raise ValueError( "Expected 'x1 y1 z1 x2 y2 z2 radius [open]' after %s" % tokens[0]) data = [self.parse_float(x) for x in tokens[1:8]] p0 = array(data[0:3]) p1 = array(data[3:6]) radius = data[6] if len(tokens) < 9: bottom = True else: bottom = False self.num_objects += 1 if self.cur_description is not None: description = self.cur_description else: description = 'object %d: cone' % self.num_objects vertices, normals, triangles = get_cone(radius, p0, p1, bottom=bottom, xform=self.transforms[-1], pure=self.pure[-1]) shape = AtomicShapeInfo(vertices, normals, triangles, _cvt_color(self.cur_color), self.cur_atoms, description) self.shapes.append(shape)
def draw_tube(nd, residue, name, params): shapes = [] if params.anchor == RIBOSE: show_gly = False else: show_gly = params.show_gly if params.anchor == RIBOSE or show_gly: aname = "C1'" else: tag = standard_bases[name]['tag'] aname = _BaseAnchors[tag] if not aname: return False a = residue.find_atom(aname) if not a or not a.display: return shapes ep0 = a.coord if params.radius is None: radius = a.structure.bond_radius else: radius = params.radius color = residue.ring_color # calculate position between C3' and C4' on ribbon c3p = residue.find_atom("C3'") if not c3p: return shapes c4p = residue.find_atom("C4'") if not c4p: return shapes c3p_coord = c3p.ribbon_coord c4p_coord = c4p.ribbon_coord if c3p_coord is None or c4p_coord is None: ep1 = (c3p.coord + c4p.coord) / 2 else: ep1 = (c3p_coord + c4p_coord) / 2 description = '%s ribose' % residue va, na, ta = get_cylinder(radius, ep0, ep1, bottom=False) shapes.append(AtomicShapeInfo(va, na, ta, color, None, description)) va, na, ta = get_sphere(radius, ep0) shapes.append(AtomicShapeInfo(va, na, ta, color, None, description)) return shapes
def draw_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]] xyz = array(data[0:3]) radius = self.LINE_RADIUS p0 = self.cur_pos if tokens[0] in ('.draw', '.d'): p1 = xyz else: p1 = p0 + xyz self.num_objects += 1 if self.cur_description is not None: description = self.cur_description else: description = 'object %d: vector' % self.num_objects vertices, normals, triangles = get_sphere(radius, p1, self.transforms[-1], pure=self.pure[-1]) vertices2, normals2, triangles2 = get_cylinder( radius, p0, p1, closed=False, xform=self.transforms[-1], pure=self.pure[-1]) if self.cur_pos_is_move: vertices3, normals3, triangles3 = get_sphere(radius, p0, self.transforms[-1], pure=self.pure[-1]) vertices, normals, triangles = combine_triangles( ((vertices, normals, triangles), (vertices2, normals2, triangles2), (vertices3, normals3, triangles3))) else: vertices, normals, triangles = combine_triangles( ((vertices, normals, triangles), (vertices2, normals2, triangles2))) shape = AtomicShapeInfo(vertices, normals, triangles, _cvt_color(self.cur_color), self.cur_atoms, description) self.shapes.append(shape) self.cur_pos = p1 self.cur_pos_is_move = False
def sphere_command(self, tokens): if len(tokens) != 5: raise UserError("Expected 'x y z radius' after %s" % tokens[0]) data = [self.parse_float(x) for x in tokens[1:5]] center = array(data[0:3]) radius = data[3] self.num_objects += 1 if self.cur_description is not None: description = self.cur_description else: description = 'object %d: sphere' % self.num_objects vertices, normals, triangles = get_sphere(radius, center, self.transforms[-1], pure=self.pure[-1]) shape = AtomicShapeInfo(vertices, normals, triangles, _cvt_color(self.cur_color), self.cur_atoms, description) self.shapes.append(shape)
def box_command(self, tokens): if len(tokens) != 7: raise ValueError("Expected 'x1 y1 z1 x2 y2 z2' after %s" % tokens[0]) data = [self.parse_float(x) for x in tokens[1:7]] llb = array(data[0:3]) urf = array(data[3:6]) self.num_objects += 1 if self.cur_description is not None: description = self.cur_description else: description = 'object %d: box' % self.num_objects vertices, normals, triangles = get_box(llb, urf, self.transforms[-1], pure=self.pure[-1]) shape = AtomicShapeInfo(vertices, normals, triangles, _cvt_color(self.cur_color), self.cur_atoms, description) self.shapes.append(shape)
def marker_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]] center = array(data[0:3]) self.num_objects += 1 if self.cur_description is not None: description = self.cur_description else: description = 'object %d: marker' % self.num_objects llb = center - 0.5 urf = center + 0.5 vertices, normals, triangles = get_box(llb, urf, self.transforms[-1], pure=self.pure[-1]) shape = AtomicShapeInfo(vertices, normals, triangles, _cvt_color(self.cur_color), self.cur_atoms, description) self.shapes.append(shape) self.cur_pos = center self.cur_pos_is_move = False
def arrow_command(self, tokens): if len(tokens) not in (7, 8, 9, 10): raise ValueError( "Expected 'x1 y1 z1 x2 y2 z2 [r1 [r2 [rho]]]' after %s" % tokens[0]) data = [self.parse_float(x) for x in tokens[1:]] r1 = data[6] if len(tokens) > 7 else 0.1 r2 = data[7] if len(tokens) > 8 else 4 * r1 rho = data[8] if len(tokens) > 9 else 0.75 p1 = array(data[0:3]) p2 = array(data[3:6]) junction = p1 + rho * (p2 - p1) self.num_objects += 1 if self.cur_description is not None: description = self.cur_description else: description = 'object %d: arrow' % self.num_objects vertices, normals, triangles = get_cylinder(r1, p1, junction, closed=True, xform=self.transforms[-1], pure=self.pure[-1]) vertices2, normals2, triangles2 = get_cone(r2, junction, p2, bottom=True, xform=self.transforms[-1], pure=self.pure[-1]) vertices, normals, triangles = combine_triangles( ((vertices, normals, triangles), (vertices2, normals2, triangles2))) shape = AtomicShapeInfo(vertices, normals, triangles, _cvt_color(self.cur_color), self.cur_atoms, description) self.shapes.append(shape)
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
def make_ladder(nd, residues, params): """generate links between residues that are hydrogen bonded together""" # returns set of residues whose bases are drawn as rungs and # and have their atoms hidden all_shapes = [] # Create list of atoms from residues for donors and acceptors mol = residues[0].structure # make a set for quick inclusion test residue_set = set(residues) pbg = mol.pseudobond_group(mol.PBG_HYDROGEN_BONDS, create_type=None) if not pbg: bonds = () else: bonds = (p.atoms for p in pbg.pseudobonds) # only make one rung between residues even if there is more than one # h-bond depict_bonds = {} for a0, a1 in bonds: r0 = a0.residue r1 = a1.residue if r0 not in residue_set or r1 not in residue_set: continue non_base = (BackboneRiboseRE.match(a0.name), BackboneRiboseRE.match(a1.name)) if params.skip_nonbase_Hbonds and any(non_base): continue if r0.connects_to(r1): # skip covalently bonded residues continue if r1 < r0: r0, r1 = r1, r0 non_base = (non_base[1], non_base[0]) c3p0 = _c3pos(r0) if not c3p0: continue c3p1 = _c3pos(r1) if not c3p1: continue if params.rung_radius and not any(non_base): radius = params.rung_radius # elif r0.ribbon_display and r1.ribbon_display: # mgr = mol.ribbon_xs_mgr # radius = min(mgr.scale_nucleic) else: # TODO: radius = a0.structure.stickScale \ # * chimera.Molecule.DefaultBondRadius radius = a0.structure.bond_radius key = (r0, r1) if key in depict_bonds: prev_radius = depict_bonds[key][2] if prev_radius >= radius: continue depict_bonds[key] = (c3p0, c3p1, radius, non_base) matched_residues = set() if not params.stubs_only: for (r0, r1), (c3p0, c3p1, radius, non_base) in depict_bonds.items(): a0 = r0.find_atom("C2") a1 = r1.find_atom("C2") r0color = r0.ring_color r1color = r1.ring_color # choose mid-point to make purine larger try: is_purine0 = standard_bases[nucleic3to1( r0.name)]['tag'] == PURINE is_purine1 = standard_bases[nucleic3to1( r1.name)]['tag'] == PURINE except KeyError: is_purine0 = False is_purine1 = False if any(non_base) or is_purine0 == is_purine1: mid = 0.5 elif is_purine0: mid = purine_pyrimidine_ratio else: mid = 1.0 - purine_pyrimidine_ratio midpt = c3p0[1] + mid * (c3p1[1] - c3p0[1]) va, na, ta = get_cylinder(radius, c3p0[1], midpt, top=False) all_shapes.append( AtomicShapeInfo(va, na, ta, r0color, r0.atoms, str(r0))) va, na, ta = get_cylinder(radius, c3p1[1], midpt, top=False) all_shapes.append( AtomicShapeInfo(va, na, ta, r1color, r1.atoms, str(r1))) if not non_base[0]: matched_residues.add(r0) if not non_base[1]: matched_residues.add(r1) if not params.show_stubs: if params.hide: return all_shapes, matched_residues return all_shapes, () # draw stubs for unmatched nucleotide residues for r in residues: if r in matched_residues: continue c3p = _c3pos(r) if not c3p: continue ep0 = c3p[1] a = r.find_atom("C2") color = r.ring_color ep1 = None name = nucleic3to1(r.name) if name not in standard_bases: continue is_purine = standard_bases[name]['tag'] == PURINE if is_purine: a = r.find_atom('N1') if a: ep1 = a.coord else: # pyrimidine a = r.find_atom('N3') if a: ep1 = a.coord if ep1 is None: # find farthest atom from C3' dist_atom = (0, None) for a in r.atoms: dist = distance_squared(ep0, a.coord) if dist > dist_atom[0]: dist_atom = (dist, a) ep1 = dist_atom[1].coord va, na, ta = get_cylinder(params.rung_radius, ep0, ep1) all_shapes.append(AtomicShapeInfo(va, na, ta, color, r.atoms, str(r))) # make exposed end rounded (TODO: use a hemisphere) va, na, ta = get_sphere(params.rung_radius, ep1) all_shapes.append(AtomicShapeInfo(va, na, ta, color, r.atoms, str(r))) matched_residues.add(r) if params.hide: return all_shapes, matched_residues return all_shapes, ()