예제 #1
0
def interpolate_corkscrew(xf, c0, c1, minimum_rotation = 0.1):
        '''
        Rotate and move along a circular arc perpendicular to the rotation axis and
        translate parallel the rotation axis.  This makes the initial geometric center c0
        traverse a helical path to the final center c1.  The circular arc spans an angle
        equal to the rotation angle so it is nearly a straight segment for small angles,
        and for the largest possible rotation angle of 180 degrees it is a half circle
        centered half-way between c0 and c1 in the plane perpendicular to the rotation axis.
        Rotations less than the minimum (degrees) are treated as no rotation.
        '''
        from chimerax.geometry import normalize_vector
        dc = c1 - c0
        axis, angle = xf.rotation_axis_and_angle()      # a is in degrees.
        if abs(angle) < minimum_rotation:
                # Use linear instead of corkscrew interpolation to
                # avoid numerical precision problems at small rotation angles.
                # ChimeraX bug #2928.
                center = c0
                shift = dc
        else:
                from chimerax.geometry import inner_product, cross_product, norm
                tra = inner_product(dc, axis)           # Magnitude of translation along rotation axis.
                shift = tra*axis
                vt = dc - tra * axis                    # Translation perpendicular to rotation axis.
                v0 = cross_product(axis, vt)
                if norm(v0) == 0 or angle == 0:
                        center = c0
                else:
                        import math
                        l = 0.5*norm(vt) / math.tan(math.radians(angle / 2))
                        center = c0 + 0.5*vt + l*normalize_vector(v0)

        return axis, angle, center, shift
예제 #2
0
def curve_segment_end(curve, t, length, tolerance=1e-3, max_steps=100):
    '''
    Compute a point on the curve beyond curve parameter value t which is
    a distance length from the point at position t.  Can return None if
    convergence fails.  This iteratively takes linear steps based on the
    curve velocity vector to find the desired end point.
    '''
    if length == 0:
        return t
    xyz0 = curve.position(t)
    v = curve.velocity(t)
    frac = 0.5
    from chimerax.geometry import norm, inner_product
    t1 = t + frac * length / norm(v)
    for step in range(max_steps):
        xyz1 = curve.position(t1)
        d = norm(xyz1 - xyz0)
        if abs(d - length) < tolerance * length:
            return t1
        v1 = curve.velocity(t1)
        # Want |xyz1 + v1*dt - xyz0| = length
        delta = xyz1 - xyz0
        a, b, c = (inner_product(v1, v1), 2 * inner_product(v1, delta),
                   inner_product(delta, delta) - length * length)
        dt1, dt2 = quadratic_roots(a, b, c)
        if dt1 is None:
            # No point in tangent line is within target length.
            # Go to closest approach
            dt1 = dt2 = -b / 2 * a
        dt_min = dt1 if abs(dt1) < abs(dt2) else dt2
        t1 += frac * dt_min
    return None
예제 #3
0
def rotation_step(points,
                  point_weights,
                  center,
                  data_array,
                  xyz_to_ijk_transform,
                  ijk_step_size,
                  metric,
                  syminv=[],
                  values=None,
                  gradients=None):

    axis = torque_axis(points,
                       point_weights,
                       center,
                       data_array,
                       xyz_to_ijk_transform,
                       metric,
                       syminv=syminv,
                       values=values,
                       gradients=gradients)

    from chimerax.geometry import norm, place
    na = norm(axis)
    if len(points) == 1 or na == 0:
        axis = (0, 0, 1)
        angle = 0
    else:
        axis /= na
        angle = angle_step(axis, points, center, xyz_to_ijk_transform,
                           ijk_step_size)
    move_tf = place.rotation(axis, angle, center)
    return move_tf
예제 #4
0
def translation_step(points,
                     point_weights,
                     center,
                     data_array,
                     xyz_to_ijk_transform,
                     ijk_step_size,
                     metric,
                     syminv=[],
                     values=None,
                     gradients=None):

    g = gradient_direction(points,
                           point_weights,
                           data_array,
                           xyz_to_ijk_transform,
                           metric,
                           syminv=syminv,
                           values=values,
                           gradients=gradients)
    from numpy import array, float, dot as matrix_multiply
    gijk = matrix_multiply(xyz_to_ijk_transform.matrix[:, :3], g)
    from chimerax.geometry import norm, place
    n = norm(gijk)
    if n > 0:
        delta = g * (ijk_step_size / n)
    else:
        delta = array((0, 0, 0), float)

    delta_tf = place.translation(delta)

    return delta_tf
예제 #5
0
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])
예제 #6
0
    def mouse_drag(self, event):
        v = self._map
        if v is None or self._xy_last is None:
            self.mouse_down(event)
            return

        xl, yl = self._xy_last
        x, y = event.position()
        dx, dy = (x - xl, yl - y)
        if dx == 0 and dy == 0:
            return

        camera = self.session.main_view.camera
        if event.shift_down():
            # Translate slab
            ro = v.rendering_options
            spacing = ro.tilted_slab_spacing
            slab_normal = v.scene_position.transform_vector(
                ro.tilted_slab_axis)
            move_dir = camera.position.transform_vector((dx, dy, 0))
            from chimerax.geometry import inner_product, norm
            sign = 1 if inner_product(move_dir, slab_normal) > 0 else -1
            dist = sign * norm(move_dir) * spacing
            self._move_slab(dist)
        else:
            # Rotate slab
            from math import sqrt
            dn = sqrt(dx * dx + dy * dy)
            rangle = dn
            raxis = camera.position.transform_vector((-dy / dn, dx / dn, 0))
            self._rotate_slab(raxis, rangle)
        self._xy_last = (x, y)
예제 #7
0
def _line_orientation(from_point, to_point, axis, height):
    if from_point is None and to_point is None and axis is None:
        return None

    c,h,r = None, height, None

    from chimerax.core.commands import Axis, Center
    if isinstance(axis, Axis):
        axis = axis.scene_coordinates()
    if isinstance(from_point, Center):
        from_point = from_point.scene_coordinates()
    if isinstance(to_point, Center):
        to_point = to_point.scene_coordinates()
        
    from chimerax.geometry import vector_rotation, norm
    if axis is not None:
        r = vector_rotation((0,0,1), axis)
    else:
        from numpy import array, float32
        axis = array((0,0,1), float32)
        
    if from_point is not None and to_point is not None:
        c = 0.5 * (from_point + to_point)
        v = to_point - from_point
        r = vector_rotation((0,0,1), v)
        h = norm(v)
    elif from_point is not None and to_point is None:
        c = from_point + 0.5*height*axis
    elif from_point is None and to_point is not None:
        c = to_point - 0.5*height*axis
        
    return h, c, r
예제 #8
0
def buried_sphere_area(i, centers, radii, draw=False):

    if draw:
        surf0 = sphere_model([i], centers, radii)
        from chimerax.geometry import norm
        jlist = [
            j for j in range(len(centers))
            if j != i and norm(centers[j] - centers[i]) < radii[j] + radii[i]
        ]
        #        jlist = list(range(i)) + list(range(i+1,len(centers)))
        print(len(jlist), 'spheres intersect sphere', i)
        surfn = sphere_model(jlist, centers, radii)
        for p in surfn.child_drawings():
            p.color = (.7, .7, .9, .7)
        draw.add_models((surf0, surfn))

    r = radii[i]

    # Check if sphere is completely contained in another sphere
    if sphere_in_another_sphere(i, centers, radii):
        area = 4 * pi * r * r
        return area

    # Compute sphere intersections
    circles = sphere_intersection_circles(i, centers, radii)

    # Compute analytical buried area on sphere.
    area = area_in_circles_on_unit_sphere(circles, draw, centers[i], r) * r * r

    return area
예제 #9
0
def random_direction():

    z = (1,1,1)
    from chimerax.geometry import norm, normalize_vector
    from random import random
    while norm(z) > 1:
        z = (1-2*random(), 1-2*random(), 1-2*random())
    return normalize_vector(z)
예제 #10
0
def line_position(xyz, line):
    xyz1, xyz2 = line
    dxyz = xyz - xyz1
    xyz12 = xyz2 - xyz1
    from chimerax.geometry import norm, inner_product
    d2 = norm(xyz12)
    f = inner_product(dxyz, xyz12) / d2
    return f
예제 #11
0
 def _center_point_matching_depth(self, point):
     cam_pos = self.camera.position.origin()
     vd = self.camera.view_direction()
     hyp = point - cam_pos
     from chimerax.geometry import inner_product, norm
     distance = inner_product(hyp, vd)
     cr = cam_pos + distance * vd
     old_cofr = self._center_of_rotation
     if norm(cr - old_cofr) < 1e-6 * distance:
         # Avoid jitter if camera has not moved
         cr = old_cofr
     return cr
예제 #12
0
def cut_distances(va, nc=4):

    from chimerax.geometry import norm
    cuts = []
    cut = [0] * nc
    n = len(va) // (4 * nc)
    for s in range(n - 1):
        for c in range(nc):
            e = va[c * n:] if s % 2 == 0 else va[((c + nc // 2) % nc) * n:]
            cut[c] += norm(e[s + 1] - e[s])
        cuts.append(tuple(cut))
    return cuts
예제 #13
0
def tetra_pos(bondee,
              bonded,
              bond_len,
              toward=None,
              away=None,
              toward2=None,
              away2=None):
    new_bonded = []
    cur_bonded = bonded[:]
    if len(cur_bonded) == 0:
        pos = single_pos(bondee, bond_len, toward, away)
        toward = toward2
        away = away2
        new_bonded.append(pos)
        cur_bonded.append(pos)

    if len(cur_bonded) == 1:
        # add at 109.5 degree angle
        coplanar = toward or away
        if coplanar:
            coplanar = [coplanar]
        else:
            coplanar = None
        pos = angle_pos(bondee,
                        cur_bonded[0],
                        bond_len,
                        109.5,
                        coplanar=coplanar)
        if toward or away:
            # find the other 109.5 position in the toward/away
            # plane and the closer/farther position as appropriate
            old = normalize(bondee - cur_bonded[0])
            new = pos - bondee
            midpoint = tinyarray.array(bondee + old * norm(new) * cos705)
            other_pos = pos + (midpoint - pos) * 2
            d1 = sqlength(pos - (toward or away))
            d2 = sqlength(other_pos - (toward or away))
            if toward is not None:
                if d2 < d1:
                    pos = other_pos
            elif away is not None and d2 > d1:
                pos = other_pos

        new_bonded.append(pos)
        cur_bonded.append(pos)

    if len(cur_bonded) == 2:
        # add along anti-bisector of current bonds and raised up
        # 54.75 degrees from plane of those bonds (half of 109.5)
        v1 = normalize(cur_bonded[0] - bondee)
        v2 = normalize(cur_bonded[1] - bondee)
        anti_bi = normalize(tinyarray.negative(v1 + v2))
        # in order to stabilize the third and fourth tetrahedral
        # positions, cross the longer vector by the shorter
        if sqlength(v1) > sqlength(v2):
            cross_v = normalize(cross_product(v1, v2))
        else:
            cross_v = normalize(cross_product(v2, v1))

        anti_bi = anti_bi * cos5475 * bond_len
        cross_v = cross_v * sin5475 * bond_len

        pos = bondee + anti_bi + cross_v
        if toward or away:
            other_pos = bondee + anti_bi - cross_v
            d1 = sqlength(pos - (toward or away))
            d2 = sqlength(other_pos - (toward or away))
            if toward is not None:
                if d2 < d1:
                    pos = other_pos
            elif away is not None and d2 > d1:
                pos = other_pos
        new_bonded.append(pos)
        cur_bonded.append(pos)

    if len(cur_bonded) == 3:
        unitized = []
        for cb in cur_bonded:
            v = normalize(cb - bondee)
            unitized.append(bondee + v)
        pl = Plane(unitized)
        normal = pl.normal
        # if normal on other side of plane from bondee, we need to
        # invert the normal;  the (signed) distance from bondee
        # to the plane indicates if it is on the same side
        # (positive == same side)
        d = pl.distance(bondee)
        if d < 0.0:
            normal = tinyarray.negative(normal)
        new_bonded.append(bondee + normal * bond_len)
    return new_bonded
예제 #14
0
    def layout(self, params, circuit_segments):

        p = params
        from chimerax.geometry import translation, rotation, Place, vector_rotation, norm
        from math import pi, cos, sin

        # Compute helix axis direction.
        # The helix rotation axis is does not make a 90 degree angle with
        # the P-P basepair line.  It makes an angle of approximately 110 degrees.
        pa = p.pair_tilt * pi / 180
        from numpy import array
        axis = array((cos(pa), sin(pa), 0))

        # Compute screw motion that advances basepair along helix.
        # Initial basepair P-P is spans (0,0,0) to (pair_width,0,0).
        rtf = rotation(axis,
                       p.stem_twist,
                       center=(0.5 * p.pair_width, 0, -p.pair_off_axis))
        ttf = translation(p.pair_spacing * axis)
        stf = ttf * rtf

        # Specify initial orientations of basepair nucleotides
        # so P-atoms are on helix and paired P atom is in the
        # oriented xy plane.
        p0, p1 = (0, 0, 0), (p.pair_width, 0, 0)
        tf1 = self.stem_residue_orientation(p0, stf * p0, p1)
        p2 = stf.inverse() * p1
        tf2 = self.stem_residue_orientation(p1, p2, p0)

        # Keep track of basepair P-P orientation used to
        # orient the loop at the end of the stem.
        ctf = Place()

        # Layout nucleotides for both strands of double helix.
        coords = {}
        for i in range(self.length):
            coords[self.base5p + i] = tf1
            coords[self.base3p - i] = tf2
            tf1 = stf * tf1
            tf2 = stf * tf2
            ctf = stf * ctf

        # The next segment after the stem should put its first
        # P-atom at a position on the helix so that the last
        # nucleotide of the stem has the right orientation to
        # make a base pair.  To achieve this the initial basepair
        # P-P does not lie on the x-axis.  Instead we tilt the
        # entire helix so the paired P is advanced up the helix
        # by one position.
        width = norm(p2)
        ttf = vector_rotation(p2, p1)
        for b, tf in coords.items():
            coords[b] = ttf * coords[b]

        # Added circuit at end of stem.
        ccoords = self.circuit.layout(p, gap=width)

        # Position the end circuit using the P-P orientation at
        # the end of the stem and taking account of the tilt of
        # the entire helix.
        ctf = ttf * ctf * ttf.inverse()
        for b, tf in ccoords.items():
            coords[b] = ctf * tf

        # Use segment width for the tilted helix.
        seg = [Segment(coords, width, 0)]

        return seg
예제 #15
0
def tile(session,
         models=None,
         columns=None,
         spacing_factor=1.3,
         view_all=True):
    """Tile models onto a square(ish) grid."""

    # Keep only models that we think might work
    # Surfaces are excluded on the assumption that they will move
    # with their associated models
    from chimerax.atomic import Structure
    from chimerax.map.volume import Volume
    tilable_classes = [Structure, Volume]

    def tilable(m):
        for klass in tilable_classes:
            if isinstance(m, klass):
                return True
        return False

    if models is None:
        models = [m for m in session.models.list() if tilable(m)]
    models = [m for m in models if tilable(m) and m.bounds() is not None]
    if len(models) == 0:
        from chimerax.core.errors import UserError
        raise UserError("No models found for tiling.")

    # Size each tile to the maximum size (+buffer) among all models
    spacing = max([m.bounds().radius() for m in models]) * spacing_factor

    # First model is the anchor
    anchor = models[0].bounds().center()
    anchor_spec = '#' + models[0].id_string

    from .view import UndoView
    undo = UndoView("tile", session, models)
    with session.undo.block():
        # Simultaneously ove model toward anchor in scene coordinate system
        # and toward final target position in screen coordinate system
        import math, numpy
        from chimerax.geometry import norm
        if columns is None:
            columns = int(round(math.sqrt(len(models))))
        commands = []
        for i, m in enumerate(models[1:], start=1):
            center = m.bounds().center()
            # First move in scene coordinates to superimpose model onto anchor
            view_delta = anchor - center
            view_dist = norm(view_delta)
            if view_dist > 0:
                commands.append("move %.2f,%.2f,%.2f %.2f coord %s models %s" %
                                (view_delta[0], view_delta[1], view_delta[2],
                                 view_dist, anchor_spec, m.atomspec))
            # Then move in screen coordinates to final grid position
            row, col = divmod(i, columns)
            screen_delta = numpy.array([col * spacing, -row * spacing, 0],
                                       dtype=numpy.float32)
            screen_dist = norm(screen_delta)
            if screen_dist > 0:
                commands.append("move %.2f,%.2f,%.2f %.2f models %s" %
                                (screen_delta[0], screen_delta[1],
                                 screen_delta[2], screen_dist, m.atomspec))
        # Make everything visible on screen
        if view_all:
            commands.append("view")
        from chimerax.core.commands import run
        run(session, "; ".join(commands), log=False)
    undo.finish(session, models)
    session.undo.register(undo)
    session.logger.info("%d model%s tiled" %
                        (len(models), "s" if len(models) != 1 else ""))