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 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