예제 #1
0
 def closest_pt_params_to_ray(self, ray):
     ""
     p2, v2 = ray.params  # note: at first I wrote self.params() (using method not attr)
     p1, v1 = self.params
     # do some math, solve for k in p1 + k * v1 = that point (remember that the vecs can be of any length):
     # way 1: express p2-p1 as a weighted sum of v1, v2, cross(v1,v2), then take the v1 term in that sum and add it to p1.
     # way 2: There must be a NumPy function that would just do this in about one step...
     # way 3: or maybe we can mess around with dot(v1,v2) sort of like in corner_analyzer in demo_polygon...
     # way 4: or we could google for "closest points on two lines" or so...
     # way 5: or we could call it the intersection of self with the plane containing p2, and directions v2 and the cross prod,
     # and use a formula in VQT. Yes, that may not be self-contained but it's fastest to code!
     v1n = norm(v1)
     v2n = norm(v2)
     perp0 = cross(v1n, v2n)
     if vlen(perp0) < 0.01:
         ##k btw what if the lines are parallel, in what way should we fail?
         # and for that matter what if they are *almost* parallel so that we're too sensitive -- do we use an env param
         # to decide whether to fail in that case too? If we're an Instance we could do that from self.env... #e
         print "closest_pt_params_to_ray: too sensitive, returning None"  ###### teach caller to handle this; let 0.01 be option
         return None
     perpn = norm(perp0)
     perpperp = cross(perpn, v2n)
     inter = planeXline(
         p2, perpperp, p1, v1n
     )  # intersect plane (as plane point and normal) with line (as point and vector)
     if inter is None:
         print "inter is None (unexpected); data:", p1, v1, p2, v2, perp0
         return None
     # inter is the retval for a variant which just wants the closest point itself, i.e. closest_pt_to_ray
     return dot(inter - p1, v1n) / vlen(v1)
예제 #2
0
 def on_press(self):
     point = self.current_event_mousepoint() # the touched point on the visible object (hitpoint)
         # (this method is defined in the Highlightable which is self.delegate)
     self.oldpoint = self.startpoint = point
     # decide type of drag now, so it's clearly constant during drag, and so decision code is only in one place.
     # (but note that some modkey meanings might require that changes to them during the same drag are detected [nim].)
     if self._delegate.altkey:
         self._this_drag = 'free x-y rotate'
             #e more options later, and/or more flags like this (maybe some should be booleans)
             ###e or better, set up a function or object which turns later points into their effects... hmm, a DragCommand instance!
             ##e or should that be renamed DragOperation??
         self._screenrect = (ll, lr, ur, ul) = self.screenrect( self.startpoint)
             # these points should be valid in our delegate's coords == self's coords
         self._dx = _dx = norm(lr - ll)
         self._dy = _dy = norm(ur - lr)
         self._dz = cross(_dx, _dy) # towards the eye (if view is ortho) (but alg is correct whether or not it is, i think)
             ###k check cross direction sign
         self._scale = min(vlen(lr - ll), vlen(ur - lr)) * 0.4
             # New motion UI suggests that 40% of that distance means 180 degrees of rotation.
             # We'll draw an axis whose length is chosen so that dragging on a sphere of that size
             # would have the same effect. (Maybe.)
         self._objcenter = self._delegate.center
         self.startrot = + self.rotation
     else:
         self._this_drag = 'free x-y translate'
     if debug070209:
         self.ndrags = 0
     return
예제 #3
0
 def getAtomDistDeltas(self, isAtomDistDeltas, atomDistPrecision,
                       selectedAtom):
     """
     Returns atom distance deltas (delX, delY, delZ) string if the 
     'Show atom distance delta info' in dynamic tooltip is checked from
     the user prefs. Otherwise returns None.
     """
     glpane = self.glpane
     if isAtomDistDeltas:
         xyz = glpane.selobj.posn()
         xyzSelAtom = selectedAtom.posn()
         deltaX = str(round(vlen(xyz[0] - xyzSelAtom[0]),
                            atomDistPrecision))
         deltaY = str(round(vlen(xyz[1] - xyzSelAtom[1]),
                            atomDistPrecision))
         deltaZ = str(round(vlen(xyz[2] - xyzSelAtom[2]),
                            atomDistPrecision))
         atomDistDeltas = "<font color=\"#0000FF\">DeltaX:</font> " \
                        + deltaX + "<br>" \
                        + "<font color=\"#0000FF\">DeltaY:</font> " \
                        + deltaY + "<br>" \
                        + "<font color=\"#0000FF\">DeltaZ:</font> " \
                        + deltaZ
         return atomDistDeltas
     else:
         return None
예제 #4
0
 def closest_pt_params_to_ray(self, ray):
     ""
     p2, v2 = ray.params # note: at first I wrote self.params() (using method not attr)
     p1, v1 = self.params
     # do some math, solve for k in p1 + k * v1 = that point (remember that the vecs can be of any length):
     # way 1: express p2-p1 as a weighted sum of v1, v2, cross(v1,v2), then take the v1 term in that sum and add it to p1.
     # way 2: There must be a NumPy function that would just do this in about one step...
     # way 3: or maybe we can mess around with dot(v1,v2) sort of like in corner_analyzer in demo_polygon...
     # way 4: or we could google for "closest points on two lines" or so...
     # way 5: or we could call it the intersection of self with the plane containing p2, and directions v2 and the cross prod,
     # and use a formula in VQT. Yes, that may not be self-contained but it's fastest to code!
     v1n = norm(v1)
     v2n = norm(v2)
     perp0 = cross(v1n, v2n)
     if vlen(perp0) < 0.01:
         ##k btw what if the lines are parallel, in what way should we fail?
         # and for that matter what if they are *almost* parallel so that we're too sensitive -- do we use an env param
         # to decide whether to fail in that case too? If we're an Instance we could do that from self.env... #e
         print "closest_pt_params_to_ray: too sensitive, returning None" ###### teach caller to handle this; let 0.01 be option
         return None
     perpn = norm(perp0)
     perpperp = cross(perpn,v2n)
     inter = planeXline(p2, perpperp, p1, v1n) # intersect plane (as plane point and normal) with line (as point and vector)
     if inter is None:
         print "inter is None (unexpected); data:",p1,v1,p2,v2,perp0
         return None
     # inter is the retval for a variant which just wants the closest point itself, i.e. closest_pt_to_ray
     return dot(inter - p1, v1n) / vlen(v1)
예제 #5
0
 def on_press(self):
     point = self.current_event_mousepoint(
     )  # the touched point on the visible object (hitpoint)
     # (this method is defined in the Highlightable which is self.delegate)
     self.oldpoint = self.startpoint = point
     # decide type of drag now, so it's clearly constant during drag, and so decision code is only in one place.
     # (but note that some modkey meanings might require that changes to them during the same drag are detected [nim].)
     if self._delegate.altkey:
         self._this_drag = 'free x-y rotate'
         #e more options later, and/or more flags like this (maybe some should be booleans)
         ###e or better, set up a function or object which turns later points into their effects... hmm, a DragCommand instance!
         ##e or should that be renamed DragOperation??
         self._screenrect = (ll, lr, ur,
                             ul) = self.screenrect(self.startpoint)
         # these points should be valid in our delegate's coords == self's coords
         self._dx = _dx = norm(lr - ll)
         self._dy = _dy = norm(ur - lr)
         self._dz = cross(
             _dx, _dy
         )  # towards the eye (if view is ortho) (but alg is correct whether or not it is, i think)
         ###k check cross direction sign
         self._scale = min(vlen(lr - ll), vlen(ur - lr)) * 0.4
         # New motion UI suggests that 40% of that distance means 180 degrees of rotation.
         # We'll draw an axis whose length is chosen so that dragging on a sphere of that size
         # would have the same effect. (Maybe.)
         self._objcenter = self._delegate.center
         self.startrot = +self.rotation
     else:
         self._this_drag = 'free x-y translate'
     if debug070209:
         self.ndrags = 0
     return
예제 #6
0
 def getAxisVector(self, atomAtVectorOrigin = None):
     """
     Returns the unit axis vector of the segment (vector between two axis 
     end points)
     """
     endPoint1, endPoint2 = self.getAxisEndPoints()
     
     if endPoint1 is None or endPoint2 is None:
         return V(0, 0, 0)
     
     
     if atomAtVectorOrigin is not None:
         #If atomAtVectorOrigin is specified, we will return a vector that
         #starts at this atom and ends at endPoint1 or endPoint2 . 
         #Which endPoint to choose will be dicided by the distance between
         #atomAtVectorOrigin and the respective endPoints. (will choose the 
         #frthest endPoint
         origin = atomAtVectorOrigin.posn()
         if vlen(endPoint2 - origin ) > vlen(endPoint1 - origin):
             return norm(endPoint2 - endPoint1)
         else:
             return norm(endPoint1 - endPoint2)
    
     
     return norm(endPoint2 - endPoint1)
예제 #7
0
    def recompute_geom_from_quats(self):  # non-ring class
        """
        [not a ring; various possible end situations]
        """
        out = self.out
        up = self.up
        atoms = self.lista
        bonds = self.listb
        # (I wish I could rename these attrs to "atoms" and "bonds",
        #  but self.atoms conflicts with the Jig attr, and self.chain_atoms is too long.)
        # default values:
        self.bm1R_pvec = "not needed"  # and bug if used, we hope, unless we later change this value
        self.twist = None
        # Figure out p vectors for atom[0]-end (left end) of bond[0], and atom[-1]-end (right end) of bond[-1].
        # If both are determined from outside this chain (i.e. if the subrs here don't return None),
        # or if we are a ring (so they are forced to be the same, except for the projection due to bond bending at the ring-joining atom),
        # then [using self.recompute_geom_both_ends_constrained()] they must be made to match (up to an arbitrary sign),
        # using a combination of some twist (to be computed here) along each bond,
        # plus a projection and (in some cases, depending on bond types i think -- see self.twist90)
        # 90-degree turn at each bond-bond connection;
        # the projection part for all the bond-bond connections is already accumulated in self.chain_quat_cum.

        # note: the following p-vec retvals are in abs coords, as they should be
        #e rename the funcs, since they are not only for sp2, but for any atom that ends our chain of pi bonds
        pvec1 = p_vector_from_sp2_atom(atoms[0], bonds[0], out=out,
                                       up=up)  # might be None
        pvec2 = p_vector_from_sp2_atom(
            atoms[-1], bonds[-1], out=out, up=up
        )  # ideally, close to negative or positive of pvec1 ###@@@ handle neg
        # handle one being None (use other one to determine the twist) or both being None (use arb vectors)
        if pvec1 is None:
            if pvec2 is None:
                # use arbitrary vectors on left end of bonds[0], perp to bond and to out; compute differently if bond axis ~= out
                axis = self.axes[0]
                pvec = cross(out, axis)
                lenpvec = vlen(pvec)
                if lenpvec < 0.01:
                    # bond axis is approx parallel to out
                    pvec = cross(up, axis)
                    lenpvec = vlen(
                        pvec
                    )  # won't be too small -- bond can't be parallel to both up and out
                pvec /= lenpvec
                self.b0L_pvec = pvec
            else:
                # pvec2 is defined, pvec1 is not. Need to transport pvec2 back to coords of pvec1
                # so our standard code (pvec_i, which wants pvec1, i.e. self.b0L_pvec) can be used.
                self.b0L_pvec = self.chain_quat_cum.unrot(pvec2)
        else:
            if pvec2 is None:
                self.b0L_pvec = pvec1
            else:
                # both vectors not None -- use recompute_geom_both_ends_constrained
                self.b0L_pvec = pvec1
                self.bm1R_pvec = pvec2
                self.recompute_geom_both_ends_constrained()
        return  # from non-ring recompute_geom_from_quats
예제 #8
0
 def constrainedPosition(self):
     a = self.atoms
     pos, p0, p1 = self.center() + self.handle_offset, a[0].posn(), a[1].posn()
     z = p1 - p0
     nz = norm(z)
     dotprod = dot(pos - p0, nz)
     if dotprod < 0.0:
         return pos - dotprod * nz
     elif dotprod > vlen(z):
         return pos - (dotprod - vlen(z)) * nz
     else:
         return pos
예제 #9
0
 def constrainedPosition(self):
     a = self.atoms
     pos, p0, p1 = self.center() + self.handle_offset, a[0].posn(), a[1].posn()
     z = p1 - p0
     nz = norm(z)
     dotprod = dot(pos - p0, nz)
     if dotprod < 0.0:
         return pos - dotprod * nz
     elif dotprod > vlen(z):
         return pos - (dotprod - vlen(z)) * nz
     else:
         return pos
예제 #10
0
 def recompute_geom_from_quats(self): # non-ring class
     """
     [not a ring; various possible end situations]
     """
     out = self.out
     up = self.up
     atoms = self.lista
     bonds = self.listb
         # (I wish I could rename these attrs to "atoms" and "bonds",
         #  but self.atoms conflicts with the Jig attr, and self.chain_atoms is too long.)
     # default values:
     self.bm1R_pvec = "not needed" # and bug if used, we hope, unless we later change this value
     self.twist = None
     # Figure out p vectors for atom[0]-end (left end) of bond[0], and atom[-1]-end (right end) of bond[-1].
     # If both are determined from outside this chain (i.e. if the subrs here don't return None),
     # or if we are a ring (so they are forced to be the same, except for the projection due to bond bending at the ring-joining atom),
     # then [using self.recompute_geom_both_ends_constrained()] they must be made to match (up to an arbitrary sign),
     # using a combination of some twist (to be computed here) along each bond,
     # plus a projection and (in some cases, depending on bond types i think -- see self.twist90)
     # 90-degree turn at each bond-bond connection;
     # the projection part for all the bond-bond connections is already accumulated in self.chain_quat_cum.
     
     # note: the following p-vec retvals are in abs coords, as they should be
     #e rename the funcs, since they are not only for sp2, but for any atom that ends our chain of pi bonds
     pvec1 = p_vector_from_sp2_atom(atoms[0], bonds[0], out = out, up = up) # might be None
     pvec2 = p_vector_from_sp2_atom(atoms[-1], bonds[-1], out = out, up = up) # ideally, close to negative or positive of pvec1 ###@@@ handle neg
     # handle one being None (use other one to determine the twist) or both being None (use arb vectors)
     if pvec1 is None:
         if pvec2 is None:
             # use arbitrary vectors on left end of bonds[0], perp to bond and to out; compute differently if bond axis ~= out
             axis = self.axes[0]
             pvec = cross(out, axis)
             lenpvec = vlen(pvec)
             if lenpvec < 0.01:
                 # bond axis is approx parallel to out
                 pvec = cross(up, axis)
                 lenpvec = vlen(pvec) # won't be too small -- bond can't be parallel to both up and out
             pvec /= lenpvec
             self.b0L_pvec = pvec
         else:
             # pvec2 is defined, pvec1 is not. Need to transport pvec2 back to coords of pvec1
             # so our standard code (pvec_i, which wants pvec1, i.e. self.b0L_pvec) can be used.
             self.b0L_pvec = self.chain_quat_cum.unrot(pvec2)
     else:
         if pvec2 is None:
             self.b0L_pvec = pvec1
         else:
             # both vectors not None -- use recompute_geom_both_ends_constrained
             self.b0L_pvec = pvec1
             self.bm1R_pvec = pvec2
             self.recompute_geom_both_ends_constrained()
     return # from non-ring recompute_geom_from_quats
 def _determine_how_to_change_length(self): #@ NEEDS WORK
     """
     Returns the difference in length between the original nanotube and the
     modified nanotube, where:
        0 = no change in length
      > 0 = lengthen
      < 0 = trim
     """
     nanotubeRise = self.struct.nanotube.getRise()
     endPoint1, endPoint2 = self.struct.nanotube.getEndPoints() #@ 
     original_nanotube_length = vlen(endPoint1 - endPoint2)
     new_nanotube_length      = vlen(endPoint1 - endPoint2) #@
     return new_nanotube_length - original_nanotube_length #@ ALWAYS RETURNS ZERO
예제 #12
0
def p_vector_from_3_bonds(atom, bond, out = DFLT_OUT, up = DFLT_UP):
    """
    Given an sp2 atom with 3 bonds, and one of those bonds which we assume has pi orbitals in it,
    return a unit vector from atom along its p orbital, guaranteed perpendicular to bond,
    for purposes of drawing the pi orbital component of bond.
    Note that it's arbitrary whether we return a given vector or its opposite.
    [##e should we fix that, using out and up? I don't think we can in a continuous way, so don't bother.]
       We don't verify the atom is sp2, since we don't need to for this code to work,
    though our result would probably not make sense otherwise.
    """
    others = map( lambda bond: bond.other(atom), atom.bonds)
    assert len(others) == 3
    other1 = bond.other(atom)
    others.remove(other1)
    other2, other3 = others
    apos = atom.posn()
    v1 = other1.posn() - apos
    # if v1 has 0 length, we should return some default value here; this might sometimes happen so I better handle it.
    # actually i'm not sure the remaining code would fail in this case! If not, I might revise this.
    if vlen(v1) < 0.01: # in angstroms
        return + up
    v2 = other2.posn() - apos
    v3 = other3.posn() - apos
    # projecting along v1, we hope v2 and v3 are opposite, and then we return something perpendicular to them.
    # if one is zero, just be perp. to the other one alone.
    # (If both zero? Present code returns near-0. Should never happen, but fix. #e)
    # otherwise if they are not opposite, use perps to each one, "averaged",
    # which means (for normalized vectors), normalize the larger of the sum or difference
    # (equivalent to clustering them in the way (of choice of sign for each) that spans the smallest angle).
    # Optim: no need to project them before taking cross products to get the perps to use.
    ## v2 -= v1 * dot(v2,v1)
    ## v3 -= v1 * dot(v3,v1)
    v2p = cross(v2,v1)
    v3p = cross(v3,v1)
    lenv2p = vlen(v2p)
    if lenv2p < 0.01:
        return norm(v3p)
    v2p /= lenv2p
    lenv3p = vlen(v3p)
    if lenv3p < 0.01:
        return v2p # normalized above
    v3p /= lenv3p
    r1 = v2p + v3p
    r2 = v2p - v3p
    lenr1 = vlen(r1)
    lenr2 = vlen(r2)
    if lenr1 > lenr2:
        return r1/lenr1
    else:
        return r2/lenr2
    pass
예제 #13
0
def p_vector_from_3_bonds(atom, bond, out=DFLT_OUT, up=DFLT_UP):
    """
    Given an sp2 atom with 3 bonds, and one of those bonds which we assume has pi orbitals in it,
    return a unit vector from atom along its p orbital, guaranteed perpendicular to bond,
    for purposes of drawing the pi orbital component of bond.
    Note that it's arbitrary whether we return a given vector or its opposite.
    [##e should we fix that, using out and up? I don't think we can in a continuous way, so don't bother.]
       We don't verify the atom is sp2, since we don't need to for this code to work,
    though our result would probably not make sense otherwise.
    """
    others = map(lambda bond: bond.other(atom), atom.bonds)
    assert len(others) == 3
    other1 = bond.other(atom)
    others.remove(other1)
    other2, other3 = others
    apos = atom.posn()
    v1 = other1.posn() - apos
    # if v1 has 0 length, we should return some default value here; this might sometimes happen so I better handle it.
    # actually i'm not sure the remaining code would fail in this case! If not, I might revise this.
    if vlen(v1) < 0.01:  # in angstroms
        return +up
    v2 = other2.posn() - apos
    v3 = other3.posn() - apos
    # projecting along v1, we hope v2 and v3 are opposite, and then we return something perpendicular to them.
    # if one is zero, just be perp. to the other one alone.
    # (If both zero? Present code returns near-0. Should never happen, but fix. #e)
    # otherwise if they are not opposite, use perps to each one, "averaged",
    # which means (for normalized vectors), normalize the larger of the sum or difference
    # (equivalent to clustering them in the way (of choice of sign for each) that spans the smallest angle).
    # Optim: no need to project them before taking cross products to get the perps to use.
    ## v2 -= v1 * dot(v2,v1)
    ## v3 -= v1 * dot(v3,v1)
    v2p = cross(v2, v1)
    v3p = cross(v3, v1)
    lenv2p = vlen(v2p)
    if lenv2p < 0.01:
        return norm(v3p)
    v2p /= lenv2p
    lenv3p = vlen(v3p)
    if lenv3p < 0.01:
        return v2p  # normalized above
    v3p /= lenv3p
    r1 = v2p + v3p
    r2 = v2p - v3p
    lenr1 = vlen(r1)
    lenr2 = vlen(r2)
    if lenr1 > lenr2:
        return r1 / lenr1
    else:
        return r2 / lenr2
    pass
예제 #14
0
    def viewParallelTo(self):
        """
        Set view parallel to the vector defined by 2 selected atoms.
        """
        cmd = greenmsg("Set View Parallel To: ")

        atoms = self.assy.selatoms_list()

        if len(atoms) != 2:
            msg = redmsg("You must select 2 atoms.")
            env.history.message(cmd + msg)
            return

        v = norm(atoms[0].posn()-atoms[1].posn())

        if vlen(v) < 0.0001: # Atoms are on top of each other.
            info = 'The selected atoms are on top of each other.  No change in view.'
            env.history.message(cmd + info)
            return

        # If vec is pointing into the screen, negate (reverse) vec.
        if dot(v, self.glpane.lineOfSight) > 0:
            v = -v

        # Compute the destination quat (q2).
        q2 = Q(V(0,0,1), v)
        q2 = q2.conj()

        self.glpane.rotateView(q2)

        info = 'View set parallel to the vector defined by the 2 selected atoms.'
        env.history.message(cmd + info)
예제 #15
0
def inferBonds(mol):  # [probably by Will; TODO: needs docstring]

    # bruce 071030 moved this from bonds.py to bonds_from_atoms.py

    # not sure how big a margin we should have for "coincident"
    maxBondLength = 2.0
    # first remove any coincident singlets
    singlets = filter(lambda a: a.is_singlet(), mol.atoms.values())
    removable = {}
    sngen = NeighborhoodGenerator(singlets, maxBondLength)
    for sing1 in singlets:
        key1 = sing1.key
        pos1 = sing1.posn()
        for sing2 in sngen.region(pos1):
            key2 = sing2.key
            dist = vlen(pos1 - sing2.posn())
            if key1 != key2:
                removable[key1] = sing1
                removable[key2] = sing2
    for badGuy in removable.values():
        badGuy.kill()
    from operations.bonds_from_atoms import make_bonds

    make_bonds(mol.atoms.values())
    return
예제 #16
0
 def carbons(self):
     if self._carbons == None:
         edges = self.edges()
         lst = []
         carbonIndex = 0
         for tri in self.triangles():
             a, b, c = tri
             ab = edges.edgeFor(a, b)
             ab.add_atom(carbonIndex)
             ac = edges.edgeFor(a, c)
             ac.add_atom(carbonIndex)
             bc = edges.edgeFor(b, c)
             bc.add_atom(carbonIndex)
             v0, v1, v2 = self.lst[a], self.lst[b], self.lst[c]
             v = (v0 + v1 + v2) / 3.0
             lst.append(v)
             carbonIndex += 1
         # get a bond length
         a1, a2 = edges.anyOldBond()
         bondlen = vlen(lst[a1] - lst[a2])
         # scale to get the correct bond length
         factor = self.bondlength / bondlen
         for i in range(len(lst)):
             lst[i] = factor * lst[i]
         self._carbons = lst
         # invalidate _bonds
         self._bonds = None
     return self._carbons
예제 #17
0
 def _getCursorText_length(self, vec):
     """
     Subclasses may override this method.
     @see: self._drawCursorText() for details.
     """
     dist = vlen(vec)
     return "%5.2A" % (dist)
예제 #18
0
def indexVerts(verts, close):
    """
    Compress a vertex array into an array of unique vertices, and an array of
    index values into the unique vertices.  This is good for converting input
    for glDrawArrays into input for glDrawElements.

    The second arg is 'close', the distance between vertices which are close
    enough to be considered a single vertex.

    The return value is a pair of arrays (index, verts).
    """
    unique = []
    index = []
    for v in verts:
        for i in range(len(unique)):
            if vlen(unique[i] - v) < close:
                index += [i]
                break
            pass
        else:
            index += [len(unique)]
            unique += [v]
            pass
        continue
    return (index, unique)
예제 #19
0
 def __init__(self, shp, ptlist, origin, selSense, opts):
     """
     ptlist is a list of 3d points describing a selection
     (in a subclass-specific manner).
     origin is the center of view, and shp.normal gives the direction
     of the line of light.
     """
     # store orthonormal screen-coordinates from shp
     self.right = shp.right
     self.up = shp.up
     self.normal = shp.normal
     
     # store other args
     self.ptlist = ptlist
     self.org = origin + 0.0
     self.selSense = selSense
     self.slab = opts.get('slab', None) # how thick in what direction
     self.eyeball = opts.get('eye', None) # for projecting if not in ortho mode
     
     if self.eyeball:
         self.eye2Pov = vlen(self.org - self.eyeball)
     
     # project the (3d) path onto the plane. Warning: arbitrary 2d origin!
     # Note: original code used project_2d_noeyeball, and I think this worked
     # since the points were all in the same screen-parallel plane as
     # self.org (this is a guess), but it seems better to not require this
     # but just to use project_2d here (taking eyeball into account).
     self._computeBBox()
    def getCursorText(self, endPoint1, endPoint2):
        """
        This is used as a callback method in CntLine mode 
        @see: NanotubeLineMode.setParams, NanotubeLineMode_GM.Draw
        """
        if endPoint1 is None or endPoint2 is None:
            return
        
        if not env.prefs[insertNanotubeEditCommand_showCursorTextCheckBox_prefs_key]:
            return '', black

        textColor = black
        vec = endPoint2 - endPoint1
        ntLength = vlen(vec)
        
        lengthString = self._getCursorText_length(ntLength)
        
        thetaString = ''
        if env.prefs[insertNanotubeEditCommand_cursorTextCheckBox_angle_prefs_key]:
            theta = self.glpane.get_angle_made_with_screen_right(vec)
            thetaString = '%5.2f deg'%theta
            
            
        commaString = ", "
        
        text = lengthString

        if text and thetaString:
            text += commaString

        text += thetaString
        
        return text , textColor
예제 #21
0
    def writepov(self, file, dispdef):
        if self.hidden: return
        if self.is_disabled(): return  #bruce 050421
        c = self.posn()
        a = self.axen()

        xrot = -atan2(a[1], sqrt(1 - a[1] * a[1])) * 180 / pi
        yrot = atan2(a[0], sqrt(1 - a[0] * a[0])) * 180 / pi

        file.write("lmotor(" \
                   + povpoint([self.width *  0.5, self.width *  0.5, self.length *  0.5]) + "," \
                   + povpoint([self.width * -0.5, self.width * -0.5, self.length * -0.5]) + "," \
                   + "<0.0, " + str(yrot) + ", 0.0>," \
                   + "<" + str(xrot) + ", 0.0, 0.0>," \
                   + povpoint(c) + "," \
                   + "<" + str(self.color[0]) + "," + str(self.color[1]) + "," + str(self.color[2]) + ">)\n")

        for a in self.atoms:
            if vlen(
                    c - a.posn()
            ) > 0.001:  #bruce 060808 add condition to see if this fixes bug 719 (two places in this file)
                file.write("spoke(" + povpoint(c) + "," + povpoint(a.posn()) +
                           "," + str(self.sradius) + ",<" +
                           str(self.color[0]) + "," + str(self.color[1]) +
                           "," + str(self.color[2]) + ">)\n")
예제 #22
0
 def _subtriangulate(self, n):
     # don't put new vectors right on top of old ones
     # this is similar to the neighborhood generator
     gap = 0.001
     occupied = { }
     def quantize(v):
         return (int(floor(v[0] / gap)),
                 int(floor(v[1] / gap)),
                 int(floor(v[2] / gap)))
     def overlap(v):
         x0, y0, z0 = quantize(v)
         for x in range(x0 - 1, x0 + 2):
             for y in range(y0 - 1, y0 + 2):
                 for z in range(z0 - 1, z0 + 2):
                     key = (x, y, z)
                     if occupied.has_key(key):
                         return True
         return False
     def occupy(v):
         key = quantize(v)
         occupied[key] = 1
     def add_if_ok(v):
         if not overlap(v):
             occupy(v)
             self.lst.append(v)
     for v in self.lst:
         occupy(v)
     for tri in self.triangles():
         v0, v1, v2 = self.lst[tri[0]], self.lst[tri[1]], self.lst[tri[2]]
         v10, v21 = v1 - v0, v2 - v1
         for i in range(n + 1):
             for j in range(i + 1):
                 v = v21 * (1. * j / n) + v10 * (1. * i / n) + v0
                 add_if_ok(v / vlen(v))
     self.order *= n
예제 #23
0
def drawcylinder(color, pos1, pos2, radius, capped = 0, opacity = 1.0):
    """
    Schedule a cylinder for rendering whenever ColorSorter thinks is
    appropriate.
    """
    if 1:
        #bruce 060304 optimization: don't draw zero-length or almost-zero-length
        # cylinders.  (This happens a lot, apparently for both long-bond
        # indicators and for open bonds.  The callers hitting this should be
        # fixed directly! That might provide a further optim by making a lot
        # more single bonds draw as single cylinders.)  The reason the
        # threshhold depends on capped is in case someone draws a very thin
        # cylinder as a way of drawing a disk. But they have to use some
        # positive length (or the direction would be undefined), so we still
        # won't permit zero-length then.
        cyllen = vlen(pos1 - pos2)
        if cyllen < (capped and 0.000000000001 or 0.0001):
            # Uncomment this to find the callers that ought to be optimized.
            #e optim or remove this test; until then it's commented out.
##            if env.debug():
##                print ("skipping drawcylinder since length is only %5g" %
##                       (cyllen,)), \
##                       ("  (color is (%0.2f, %0.2f, %0.2f))" %
##                        (color[0], color[1], color[2]))
            return
        pass
    ColorSorter.schedule_cylinder(color, pos1, pos2, radius, 
                                  capped = capped, opacity = opacity)
예제 #24
0
 def rtz(self, pt):
     d = pt - self.p0
     z = dot(d, self.zn)
     d = d - z * self.zn
     r = vlen(d)
     theta = Numeric.arctan2(dot(d, self.v), dot(d, self.u))
     return Numeric.array((r, theta, z), 'd')
    def getCursorText(self):
        """
        This is used as a callback method in NanotubeLine mode 
        @see: NanotubeLineMode.setParams, NanotubeLineMode_GM.Draw
        """
        if self.grabbedHandle is None:
            return
        
        if not env.prefs[nanotubeSegmentEditCommand_showCursorTextCheckBox_prefs_key]:
            return '', black

        text = ""
        textColor = black

        currentPosition = self.grabbedHandle.currentPosition
        fixedEndOfStructure = self.grabbedHandle.fixedEndOfStructure

        nanotubeLength = vlen( currentPosition - fixedEndOfStructure )

        nanotubeLengthString = self._getCursorText_length(nanotubeLength)
        
        text = nanotubeLengthString
   
        #@TODO: The following updates the PM as the cursor moves. 
        #Need to rename this method so that you that it also does more things 
        #than just to return a textString -- Ninad 2007-12-20
        self.propMgr.ntLengthLineEdit.setText(nanotubeLengthString)

        return text, textColor
예제 #26
0
 def _getCursorText_length(self, vec):
     """
     Subclasses may override this method. 
     @see: self._drawCursorText() for details. 
     """
     dist = vlen(vec)
     return "%5.2A"%(dist)
    def getCursorText(self, endPoint1, endPoint2):
        """
        This is used as a callback method in CntLine mode 
        @see: NanotubeLineMode.setParams, NanotubeLineMode_GM.Draw
        """
        if endPoint1 is None or endPoint2 is None:
            return

        if not env.prefs[
                insertNanotubeEditCommand_showCursorTextCheckBox_prefs_key]:
            return '', black

        textColor = black
        vec = endPoint2 - endPoint1
        ntLength = vlen(vec)

        lengthString = self._getCursorText_length(ntLength)

        thetaString = ''
        if env.prefs[
                insertNanotubeEditCommand_cursorTextCheckBox_angle_prefs_key]:
            theta = self.glpane.get_angle_made_with_screen_right(vec)
            thetaString = '%5.2f deg' % theta

        commaString = ", "

        text = lengthString

        if text and thetaString:
            text += commaString

        text += thetaString

        return text, textColor
예제 #28
0
    def getCursorText(self, endPoint1, endPoint2):
        """
        This is used as a callback method in PeptideLineLine mode 
        @see: PeptideLine_GraphicsMode.setParams, PeptideLine_GraphicsMode.Draw
        """
        
        text = ''
        textColor = env.prefs[cursorTextColor_prefs_key]
        
        if endPoint1 is None or endPoint2 is None:
            return text, textColor
        
        vec = endPoint2 - endPoint1
        from geometry.VQT import vlen
        peptideLength = vlen(vec)
        ss_idx, phi, psi, aa_type = self._gatherParameters()
        peptideLength = self.structGenerator.get_number_of_res(endPoint1, endPoint2, phi, psi)
        lengthString = self._getCursorText_length(peptideLength)
        
        thetaString = ''
        #Urmi 20080804: not sure if angle will be required later
        theta = self.glpane.get_angle_made_with_screen_right(vec)
        thetaString = '%5.2f deg'%theta
                        
        commaString = ", "
        
        text = lengthString

        if text and thetaString:
            text += commaString

        text += thetaString
        
        return text, textColor
예제 #29
0
 def carbons(self):
     if self._carbons == None:
         edges = self.edges()
         lst = [ ]
         carbonIndex = 0
         for tri in self.triangles():
             a, b, c = tri
             ab = edges.edgeFor(a, b)
             ab.add_atom(carbonIndex)
             ac = edges.edgeFor(a, c)
             ac.add_atom(carbonIndex)
             bc = edges.edgeFor(b, c)
             bc.add_atom(carbonIndex)
             v0, v1, v2 = self.lst[a], self.lst[b], self.lst[c]
             v = (v0 + v1 + v2) / 3.0
             lst.append(v)
             carbonIndex += 1
         # get a bond length
         a1, a2 = edges.anyOldBond()
         bondlen = vlen(lst[a1] - lst[a2])
         # scale to get the correct bond length
         factor = self.bondlength / bondlen
         for i in range(len(lst)):
             lst[i] = factor * lst[i]
         self._carbons = lst
         # invalidate _bonds
         self._bonds = None
     return self._carbons
예제 #30
0
def list_potential_bonds(atmlist0):
    """
    Given a list of atoms, return a list of triples (cost, atm1, atm2) for all bondable pairs of atoms in the list.
    Each pair of atoms is considered separately, as if only it would be bonded, in addition to all existing bonds.
    In other words, the returned bonds can't necessarily all be made (due to atom valence), but any one alone can be made,
    in addition to whatever bonds the atoms currently have.
       Warning: the current implementation takes quadratic time in len(atmlist0). The return value will have reasonable
    size for physically realistic atmlists, but could be quadratic in size for unrealistic ones (e.g. if all atom
    positions were compressed into a small region of space).
    """
    atmlist = filter( bondable_atm, atmlist0 )
    lst = []
    maxBondLength = 2.0
    ngen = NeighborhoodGenerator(atmlist, maxBondLength)
    for atm1 in atmlist:
        key1 = atm1.key
        pos1 = atm1.posn()
        for atm2 in ngen.region(pos1):
            bondLen = vlen(pos1 - atm2.posn())
            idealBondLen = idealBondLength(atm1, atm2)
            if atm2.key < key1 and bondLen < max_dist_ratio(atm1, atm2) * idealBondLen:
                # i.e. for each pair (atm1, atm2) of bondable atoms
                cost = bond_cost(atm1, atm2)
                if cost is not None:
                    lst.append((cost, atm1, atm2))
    lst.sort() # least cost first
    return lst
예제 #31
0
def baseframe_from_pam3_data(ss1, ax, ss2):
    """
    Given the positions of the Ss3-Ax3-Ss3 atoms in a PAM3 basepair,
    return the first Ss3's baseframe (and y_m) as a tuple of
    (origin, rel_to_abs_quat, y_m).
    """
    yprime_vector = ss2 - ss1
    yprime_length = vlen(yprime_vector)
    
    y_direction = yprime_vector / yprime_length # optimization of norm
    
    z_direction = norm(cross(ax - ss1, y_direction))
        # BUG: nothing checks for cross product being too small

    x_direction = norm(cross(y_direction, z_direction))
        # this norm is redundant, but might help with numerical stability

    rel_to_abs_quat = Q(x_direction, y_direction, z_direction)

    # still need origin, easy since we know SPRIME_D_SDFRAME -- but we do have to rotate that, using the quat

    # rel_to_abs_quat.rot( SPRIME_D_SDFRAME ) # this is Ss5 to Ss3 vector, abs coords

    Ss3_d_abspos = ss1
    Ss5_d_abspos = Ss3_d_abspos - rel_to_abs_quat.rot( SPRIME_D_SDFRAME )

    origin = Ss5_d_abspos

    # y_m = (|S'_u - S'_d| / 2) + y_s'
    y_m = yprime_length / 2.0 + Y_SPRIME

    return ( origin, rel_to_abs_quat, y_m )
예제 #32
0
    def sameAsCurrentView(self, view = None):
        """
        Tests if self is the same as I{view}, or the current view if I{view}
        is None (the default).
        
        @param view: A named view to compare with self. If None (the default),
                     self is compared to the current view (i.e. the 3D graphics
                     area).
        @type  view: L{NamedView}
        
        @return: True if they are the same. Otherwise, returns False.
        @rtype:  boolean
        """
        # Note: I'm guessing this could be rewritten to be more 
        # efficient/concise. For example, it seems possible to implement 
        # this using a simple conditional like this:
        #        
        # if self == view:
        #    return True
        # else:
        #    return False
        # 
        # It occurs to me that the GPLane class should use a NamedView attr 
        # along with (or in place of) quat, scale, pov and zoomFactor attrs.
        # That would make this method (and possibly other code) easier to 
        # write and understand.
        # 
        # Ask Bruce about all this.
        # 
        # BTW, this code was originally copied/borrowed from 
        # GLPane.animateToView(). Mark 2008-02-03.
    
        # Make copies of self parameters.
        q1 = Q(self.quat)
        s1 = self.scale
        p1 = V(self.pov[0], self.pov[1], self.pov[2])
        z1 = self.zoomFactor
        
        if view is None:
            # use the graphics area in which self is displayed
            # (usually the main 3D graphics area; code in this class
            #  has not been reviewed for working in other GLPane_minimal instances)
            view = self.assy.glpane
        
        # Copy the parameters of view for comparison
        q2 = Q(view.quat)
        s2 = view.scale
        p2 = V(view.pov[0], view.pov[1], view.pov[2])
        z2 = view.zoomFactor

        # Compute the deltas
        deltaq = q2 - q1
        deltap = vlen(p2 - p1)
        deltas = abs(s2 - s1)
        deltaz = abs(z2 - z1)

        if deltaq.angle + deltap + deltas + deltaz == 0:
            return True
        else:
            return False
예제 #33
0
 def rtz(self, pt):
     d = pt - self.p0
     z = dot(d, self.zn)
     d = d - z * self.zn
     r = vlen(d)
     theta = Numeric.arctan2(dot(d, self.v), dot(d, self.u))
     return Numeric.array((r, theta, z), 'd')
예제 #34
0
    def is_lozenge_visible(self, pos1, pos2, radius): # piotr 080402
        """
        Perform a simple frustum culling test against a "lozenge" object
        in absolute model space coordinates. The lozenge is a cylinder
        with two hemispherical caps. Assume that the frustum planes 
        are allocated, i.e. glpane._compute_frustum_planes was already called.
        (If it wasn't, the test will always succeed.)

        Currently, this is a loose (but correct) approximation which calls
        glpane.is_sphere_visible on the lozenge's bounding sphere.

        @warning: this will give incorrect results unless the current
                  GL matrices are in the same state as when _compute_frustum_planes
                  was last called (i.e. in absolute model space coordinates).
        """

        if self._frustum_planes_available:
            center = 0.5 * (pos1 + pos2)
            sphere_radius = 0.5 * vlen(pos2 - pos1) + radius
            res = self.is_sphere_visible(center, sphere_radius)
            # Read Bruce's comment in glpane.is_sphere_visible
            # It applies here, as well.
            return res 

        return True
예제 #35
0
    def draw(self,
             glpane,
             offset=V(0, 0, 0),
             color=None,
             info={}):  # modified copy of superclass draw method
        "draw our spheres (in practice we'll need to extend this for different sets...)"
        ##        self.radius_multiplier = 1.0 # this might be changed by certain subclass's process_optional_info method
        ##        self.process_optional_info(info) # might reset instvars that affect following code... (kluge?)
        color = color or self.color
        ##detailLevel = 0 # just an icosahedron
        detailLevel = 1  # easier to click on this way
        ##radius = 0.33 # the one we store might be too large? no, i guess it's ok.
        #e (i might prefer an octahedron, or a miniature-convex-hull-of-extrude-unit)
        offset = offset + self.origin
        radius_multiplier = self.radius_multiplier
        special_pos = self.special_pos  # patched in ###nim?
        special_pos = special_pos + offset  #k?? or just self.origin??
        special_color = self.special_color  # default is used
        ##        count = 0
        for (pos, radius, info) in self.handles:
            radius *= radius_multiplier
            pos = pos + offset
            dist = vlen(special_pos - pos)
            if dist <= radius:
                color2 = ave_colors(1.0 - dist / radius, special_color, color)
##                count += 1
            else:
                color2 = color
            ## experiment 050218: add alpha factor to color
            color2 = tuple(color2) + (0.25, )
            drawsphere(color2, pos, radius, detailLevel)
##        self.color2_count = count # kluge, probably not used since should equal nbonds
        return
예제 #36
0
def baseframe_from_pam5_data(ss1, gv, ss2):
    """
    Given the positions of the Ss5-Gv5-Ss5 atoms in a PAM5 basepair,
    return the first Ss5's baseframe (and y_m) as a tuple of
    (origin, rel_to_abs_quat, y_m).

    @note: this is correct even if gv is actually an Ax5 position.
    """
    # y axis is parallel to inter-sugar line

    # base plane orientation comes from the other atom, Gv

    # so get x and z axis around that line

    origin = ss1

    y_vector = ss2 - ss1
    y_length = vlen(y_vector)

    ## y_direction = norm(ss2 - ss1)
    y_direction = y_vector / y_length  # optimization

    z_direction = norm(cross(gv - ss1, y_direction))
    # BUG: nothing checks for cross product being too small

    x_direction = norm(cross(y_direction, z_direction))
    # this norm is redundant, but might help with numerical stability

    rel_to_abs_quat = Q(x_direction, y_direction, z_direction)

    y_m = y_length / 2.0

    return (origin, rel_to_abs_quat, y_m)
예제 #37
0
def baseframe_from_pam3_data(ss1, ax, ss2):
    """
    Given the positions of the Ss3-Ax3-Ss3 atoms in a PAM3 basepair,
    return the first Ss3's baseframe (and y_m) as a tuple of
    (origin, rel_to_abs_quat, y_m).
    """
    yprime_vector = ss2 - ss1
    yprime_length = vlen(yprime_vector)

    y_direction = yprime_vector / yprime_length  # optimization of norm

    z_direction = norm(cross(ax - ss1, y_direction))
    # BUG: nothing checks for cross product being too small

    x_direction = norm(cross(y_direction, z_direction))
    # this norm is redundant, but might help with numerical stability

    rel_to_abs_quat = Q(x_direction, y_direction, z_direction)

    # still need origin, easy since we know SPRIME_D_SDFRAME -- but we do have to rotate that, using the quat

    # rel_to_abs_quat.rot( SPRIME_D_SDFRAME ) # this is Ss5 to Ss3 vector, abs coords

    Ss3_d_abspos = ss1
    Ss5_d_abspos = Ss3_d_abspos - rel_to_abs_quat.rot(SPRIME_D_SDFRAME)

    origin = Ss5_d_abspos

    # y_m = (|S'_u - S'_d| / 2) + y_s'
    y_m = yprime_length / 2.0 + Y_SPRIME

    return (origin, rel_to_abs_quat, y_m)
예제 #38
0
    def on_drag(self):
        # Note: we can assume this is a "real drag", since the caller (ultimately a selectMode method in testmode, as of 070209)
        # is tracking mouse motion and not calling this until it becomes large enough, as the debug070209 prints show.
        oldpoint = self.oldpoint  # was saved by prior on_drag or by on_press
        point = self.current_event_mousepoint(plane=self.startpoint)
        if debug070209:
            self.ndrags += 1


##            if (self.ndrags == 1) or 1:
##                print "drag event %d, model distance = %r, pixel dist not computed" % (self.ndrags, vlen(oldpoint - point),)
        if self._this_drag == 'free x-y rotate':
            # rotate using New motion UI
            #  [probably works for this specific kind of rotation, one of 4 that UI proposes;
            #   doesn't yet have fancy cursors or during-rotation graphics; add those only after it's a DragCommand]
            # two implem choices:
            # 1. know the eye direction and the screen dims in plane of startpoint, in model coords; compute in model coords
            # 2. get the mouse positions (startpoint and point) and screen dims in window x,y coords, compute rotation in eye coords,
            #   but remember to reorient it to correspond with model if model coords are rotated already.
            # Not sure which one is better.
            #   In general, placing user into model coords (or more precisely, into object local coords) seems more general --
            # for example, what if there were several interacting users, each visible to the others?
            # We'd want each user's eye & screen to be visible! (Maybe even an image of their face & screen, properly scaled and aligned?)
            # And we'd want their posns to be used in the computations here, all in model coords.
            # (Even if zoom had occurred, which means, even the user's *size* is quite variable!)
            #   I need "user in model coords" for other reasons too, so ok, I'll do it that way.
            #
            # [Hey, I might as well fix the bug in current_event_mousepoint which fakes the center of view, at the same time.
            # (I can't remember its details right now, but I think it assumed the local origin was the cov, which is obviously wrong.)
            # (But I didn't look at that code or fix that bug now.)]
            vec = point - self.startpoint
            uvec = norm(vec)  #k needed??
            axisvec = cross(
                self._dz, uvec
            )  # unit length (suitable for glRotate -- but we need to use it to make a quat!)
            axisvec = norm(
                axisvec)  # just to be sure (or to reduce numerical errors)
            scale = self._scale
            draw_axisvec = axisvec * scale  #e times some other length constant too?
            center = self._objcenter
            self.axisends = (center - axisvec, center + axisvec
                             )  # draw a rotation axis here ###e
            self.degrees = degrees = vlen(
                vec
            ) / scale * 180.0  # draw a textual indicator with degrees (and axisvec angle too) ###e
            ###e or print that info into sbar? or somewhere fixed in glpane? or in glpane near mouse?
            # now set self.rotation to a quat made from axisvec and degrees
            theta = degrees / 360.0 * 2 * pi
            # print "axisvec %r, degrees %r, theta %r" % (axisvec ,degrees,theta)
            rot = Q(axisvec, theta)
            self.rotation = self.startrot + rot  # note use of self.startrot rather than self.rotation on rhs
            # avoid += to make sure it gets changed-tracked -- and since it would be the wrong op!

        elif self._this_drag == 'free x-y translate':
            self._cmd_drag_from_to(
                oldpoint, point)  # use Draggable interface cmd on self
        else:
            assert 0
        self.oldpoint = point
        return
예제 #39
0
    def sameAsCurrentView(self, view = None):
        """
        Tests if self is the same as I{view}, or the current view if I{view}
        is None (the default).

        @param view: A named view to compare with self. If None (the default),
                     self is compared to the current view (i.e. the 3D graphics
                     area).
        @type  view: L{NamedView}

        @return: True if they are the same. Otherwise, returns False.
        @rtype:  boolean
        """
        # Note: I'm guessing this could be rewritten to be more
        # efficient/concise. For example, it seems possible to implement
        # this using a simple conditional like this:
        #
        # if self == view:
        #    return True
        # else:
        #    return False
        #
        # It occurs to me that the GPLane class should use a NamedView attr
        # along with (or in place of) quat, scale, pov and zoomFactor attrs.
        # That would make this method (and possibly other code) easier to
        # write and understand.
        #
        # Ask Bruce about all this.
        #
        # BTW, this code was originally copied/borrowed from
        # GLPane.animateToView(). Mark 2008-02-03.

        # Make copies of self parameters.
        q1 = Q(self.quat)
        s1 = self.scale
        p1 = V(self.pov[0], self.pov[1], self.pov[2])
        z1 = self.zoomFactor

        if view is None:
            # use the graphics area in which self is displayed
            # (usually the main 3D graphics area; code in this class
            #  has not been reviewed for working in other GLPane_minimal instances)
            view = self.assy.glpane

        # Copy the parameters of view for comparison
        q2 = Q(view.quat)
        s2 = view.scale
        p2 = V(view.pov[0], view.pov[1], view.pov[2])
        z2 = view.zoomFactor

        # Compute the deltas
        deltaq = q2 - q1
        deltap = vlen(p2 - p1)
        deltas = abs(s2 - s1)
        deltaz = abs(z2 - z1)

        if deltaq.angle + deltap + deltas + deltaz == 0:
            return True
        else:
            return False
예제 #40
0
def indexVerts(verts, close):
    """
    Compress a vertex array into an array of unique vertices, and an array of
    index values into the unique vertices.  This is good for converting input
    for glDrawArrays into input for glDrawElements.

    The second arg is 'close', the distance between vertices which are close
    enough to be considered a single vertex.

    The return value is a pair of arrays (index, verts).
    """
    unique = []
    index = []
    for v in verts:
        for i in range(len(unique)):
            if vlen(unique[i] - v) < close:
                index += [i]
                break
            pass
        else:
            index += [len(unique)]
            unique += [v]
            pass
        continue
    return (index, unique)
예제 #41
0
def drawcylinder(color, pos1, pos2, radius, capped = 0, opacity = 1.0):
    """
    Schedule a cylinder for rendering whenever ColorSorter thinks is
    appropriate.
    """
    if 1:
        #bruce 060304 optimization: don't draw zero-length or almost-zero-length
        # cylinders.  (This happens a lot, apparently for both long-bond
        # indicators and for open bonds.  The callers hitting this should be
        # fixed directly! That might provide a further optim by making a lot
        # more single bonds draw as single cylinders.)  The reason the
        # threshhold depends on capped is in case someone draws a very thin
        # cylinder as a way of drawing a disk. But they have to use some
        # positive length (or the direction would be undefined), so we still
        # won't permit zero-length then.
        cyllen = vlen(pos1 - pos2)
        if cyllen < (capped and 0.000000000001 or 0.0001):
            # Uncomment this to find the callers that ought to be optimized.
            #e optim or remove this test; until then it's commented out.
##            if env.debug():
##                print ("skipping drawcylinder since length is only %5g" %
##                       (cyllen,)), \
##                       ("  (color is (%0.2f, %0.2f, %0.2f))" %
##                        (color[0], color[1], color[2]))
            return
        pass
    ColorSorter.schedule_cylinder(color, pos1, pos2, radius, 
                                  capped = capped, opacity = opacity)
예제 #42
0
    def _drawCursorText(self):
        """"
        """       
        if self.endPoint1 is None or self.endPoint2 is None:
            return

        self.text = ''
        textColor = black

        #Draw the text next to the cursor that gives info about 
        #number of base pairs etc. So this class and its command class needs 
        #cleanup. e.g. callbackMethodForCursorTextString should be simply
        #self.command.getCursorText() and like that. -- Ninad2008-04-17
        if self.command and hasattr(self.command, 
                                    'callbackMethodForCursorTextString'):            
            self.text, textColor = self.command.callbackMethodForCursorTextString(
                self.endPoint1, 
                self.endPoint2)            
        else:
            vec = self.endPoint2 - self.endPoint1
            theta = self.glpane.get_angle_made_with_screen_right(vec)
            dist = vlen(vec)
            self.text = "%5.2fA, %5.2f deg"%(dist, theta)

        self.glpane.renderTextNearCursor(self.text, color = textColor)
예제 #43
0
 def __init__(self, shp, ptlist, origin, selSense, opts):
     """
     ptlist is a list of 3d points describing a selection
     (in a subclass-specific manner).
     origin is the center of view, and shp.normal gives the direction
     of the line of light.
     """
     # store orthonormal screen-coordinates from shp
     self.right = shp.right
     self.up = shp.up
     self.normal = shp.normal
     
     # store other args
     self.ptlist = ptlist
     self.org = origin + 0.0
     self.selSense = selSense
     self.slab = opts.get('slab', None) # how thick in what direction
     self.eyeball = opts.get('eye', None) # for projecting if not in ortho mode
     
     if self.eyeball:
         self.eye2Pov = vlen(self.org - self.eyeball)
     
     # project the (3d) path onto the plane. Warning: arbitrary 2d origin!
     # Note: original code used project_2d_noeyeball, and I think this worked
     # since the points were all in the same screen-parallel plane as
     # self.org (this is a guess), but it seems better to not require this
     # but just to use project_2d here (taking eyeball into account).
     self._computeBBox()
예제 #44
0
    def update_numberOfBases(self):
        """
        Updates the numberOfBases in the PM while a resize handle is being 
        dragged. 
        @see: self.getCursorText() where it is called. 
        """
        #@Note: originally (before 2008-04-05, it was called in 
        #DnaStrand_ResizeHandle.on_drag() but that 'may' have some bugs 
        #(not verified) also see self.getCursorText() to know why it is
        #called there (basically a workaround for bug 2729
        if self.grabbedHandle is None:
            return

        currentPosition = self.grabbedHandle.currentPosition
        resize_end = self.grabbedHandle.origin

        new_duplexLength = vlen( currentPosition - resize_end )

        numberOfBasePairs_to_change = getNumberOfBasePairsFromDuplexLength('B-DNA', 
                                                                           new_duplexLength)

        original_numberOfBases = self.struct.getNumberOfBases()
        #If the dot product of handle direction and the direction in which it 
        #is dragged is negative, this means we need to subtract bases
        direction_of_drag = norm(currentPosition - resize_end)
        if dot(self.grabbedHandle.direction, direction_of_drag ) < 0:
            total_number_of_bases = original_numberOfBases - numberOfBasePairs_to_change
            self.propMgr.numberOfBasesSpinBox.setValue(total_number_of_bases)
        else:
            total_number_of_bases = original_numberOfBases + numberOfBasePairs_to_change
            self.propMgr.numberOfBasesSpinBox.setValue(total_number_of_bases - 1)
예제 #45
0
    def draw(self, glpane, offset = V(0,0,0), color = None, info = {}): # modified copy of superclass draw method
        "draw our spheres (in practice we'll need to extend this for different sets...)"
##        self.radius_multiplier = 1.0 # this might be changed by certain subclass's process_optional_info method
##        self.process_optional_info(info) # might reset instvars that affect following code... (kluge?)
        color = color or self.color
        ##detailLevel = 0 # just an icosahedron
        detailLevel = 1 # easier to click on this way
        ##radius = 0.33 # the one we store might be too large? no, i guess it's ok.
        #e (i might prefer an octahedron, or a miniature-convex-hull-of-extrude-unit)
        offset = offset + self.origin
        radius_multiplier = self.radius_multiplier
        special_pos = self.special_pos # patched in ###nim?
        special_pos = special_pos + offset #k?? or just self.origin??
        special_color = self.special_color # default is used
##        count = 0
        for (pos,radius,info) in self.handles:
            radius *= radius_multiplier
            pos = pos + offset
            dist = vlen(special_pos - pos)
            if dist <= radius:
                color2 = ave_colors( 1.0 - dist/radius, special_color, color )
##                count += 1
            else:
                color2 = color
            ## experiment 050218: add alpha factor to color
            color2 = tuple(color2) + (0.25,)
            drawsphere(color2, pos, radius, detailLevel)
##        self.color2_count = count # kluge, probably not used since should equal nbonds
        return
예제 #46
0
    def getCursorText(self, endPoint1, endPoint2):
        """
        This is used as a callback method in PeptideLineLine mode
        @see: PeptideLine_GraphicsMode.setParams, PeptideLine_GraphicsMode.Draw
        """

        text = ''
        textColor = env.prefs[cursorTextColor_prefs_key]

        if endPoint1 is None or endPoint2 is None:
            return text, textColor

        vec = endPoint2 - endPoint1
        from geometry.VQT import vlen
        peptideLength = vlen(vec)
        ss_idx, phi, psi, aa_type = self._gatherParameters()
        peptideLength = self.structGenerator.get_number_of_res(
            endPoint1, endPoint2, phi, psi)
        lengthString = self._getCursorText_length(peptideLength)

        thetaString = ''
        #Urmi 20080804: not sure if angle will be required later
        theta = self.glpane.get_angle_made_with_screen_right(vec)
        thetaString = '%5.2f deg' % theta

        commaString = ", "

        text = lengthString

        if text and thetaString:
            text += commaString

        text += thetaString

        return text, textColor
예제 #47
0
def baseframe_from_pam5_data(ss1, gv, ss2):
    """
    Given the positions of the Ss5-Gv5-Ss5 atoms in a PAM5 basepair,
    return the first Ss5's baseframe (and y_m) as a tuple of
    (origin, rel_to_abs_quat, y_m).

    @note: this is correct even if gv is actually an Ax5 position.
    """
    # y axis is parallel to inter-sugar line

    # base plane orientation comes from the other atom, Gv

    # so get x and z axis around that line
    
    origin = ss1

    y_vector = ss2 - ss1
    y_length = vlen(y_vector)
    
    ## y_direction = norm(ss2 - ss1)
    y_direction = y_vector / y_length # optimization
    
    z_direction = norm(cross(gv - ss1, y_direction))
        # BUG: nothing checks for cross product being too small

    x_direction = norm(cross(y_direction, z_direction))
        # this norm is redundant, but might help with numerical stability

    rel_to_abs_quat = Q(x_direction, y_direction, z_direction)

    y_m = y_length / 2.0

    return ( origin, rel_to_abs_quat, y_m )
예제 #48
0
    def _snapEndPointHorizontalOrVertical(self):
        """
        Snap the second endpoint of the line (and thus the whole line) to the
        screen horizontal or vertical vectors. 
        @return: The new endPoint2 i.e. the moving endpoint of the rubberband 
                 line . This value may be same as previous or snapped so that 
                 line is horizontal or vertical depending upon the angle it 
                 makes with the horizontal and vertical. 
        @rtype: B{A}
        """
        up = self.glpane.up
        down = self.glpane.down
        left = self.glpane.left
        right = self.glpane.right  

        endPoint2 = self.endPoint2

        snapVector = V(0, 0, 0)

        currentLineVector = norm(self.endPoint2 - self.endPoint1)

        theta_horizontal = angleBetween(right, currentLineVector) 
        theta_vertical = angleBetween(up, currentLineVector) 

        theta_horizontal_old = theta_horizontal
        theta_vertical_old = theta_vertical

        if theta_horizontal != 90.0:            
            theta_horizontal = min(theta_horizontal, 
                                   (180.0 - theta_horizontal))

        if theta_vertical != 90.0:            
            theta_vertical = min(theta_vertical, 
                                 180.0 - theta_vertical)

        theta = min(theta_horizontal, theta_vertical)

        if theta <= 2.0 and theta != 0.0:
            self._snapOn = True
            if theta == theta_horizontal:
                self._snapType = 'HORIZONTAL'
                if theta_horizontal == theta_horizontal_old:
                    snapVector = right                   
                else:
                    snapVector = left
            elif theta == theta_vertical:
                self._snapType = 'VERTICAL'
                if theta_vertical == theta_vertical_old:
                    snapVector = up
                else:
                    snapVector = down

            endPoint2 = self.endPoint1 + \
                      vlen(self.endPoint1 - self.endPoint2)*snapVector

        else:                
            self._snapOn = False

        return endPoint2
예제 #49
0
    def _snapEndPointHorizontalOrVertical(self):
        """
        Snap the second endpoint of the line (and thus the whole line) to the
        screen horizontal or vertical vectors. 
        @return: The new endPoint2 i.e. the moving endpoint of the rubberband 
                 line . This value may be same as previous or snapped so that 
                 line is horizontal or vertical depending upon the angle it 
                 makes with the horizontal and vertical. 
        @rtype: B{A}
        """
        up = self.glpane.up
        down = self.glpane.down
        left = self.glpane.left
        right = self.glpane.right  

        endPoint2 = self.endPoint2

        snapVector = V(0, 0, 0)

        currentLineVector = norm(self.endPoint2 - self.endPoint1)

        theta_horizontal = angleBetween(right, currentLineVector) 
        theta_vertical = angleBetween(up, currentLineVector) 

        theta_horizontal_old = theta_horizontal
        theta_vertical_old = theta_vertical

        if theta_horizontal != 90.0:            
            theta_horizontal = min(theta_horizontal, 
                                   (180.0 - theta_horizontal))

        if theta_vertical != 90.0:            
            theta_vertical = min(theta_vertical, 
                                 180.0 - theta_vertical)

        theta = min(theta_horizontal, theta_vertical)

        if theta <= 2.0 and theta != 0.0:
            self._snapOn = True
            if theta == theta_horizontal:
                self._snapType = 'HORIZONTAL'
                if theta_horizontal == theta_horizontal_old:
                    snapVector = right                   
                else:
                    snapVector = left
            elif theta == theta_vertical:
                self._snapType = 'VERTICAL'
                if theta_vertical == theta_vertical_old:
                    snapVector = up
                else:
                    snapVector = down

            endPoint2 = self.endPoint1 + \
                      vlen(self.endPoint1 - self.endPoint2)*snapVector

        else:                
            self._snapOn = False

        return endPoint2
예제 #50
0
 def __init__(self, point0, z, uhint, uhint2):
     # u and v and zn are unit vectors
     # z is NOT a unit vector
     self.p0 = point0
     self.p1 = point1 = point0 + z
     self.z = z
     zlen = vlen(z)
     if zlen < 1.0e-6:
         raise ZeroLengthCylinder()
     self.zinv = 1.0 / zlen
     self.zn = zn = norm(z)
     u = norm(uhint - (dot(uhint, z) / zlen**2) * z)
     if vlen(u) < 1.0e-4:
         u = norm(uhint2 - (dot(uhint2, z) / zlen**2) * z)
     v = cross(zn, u)
     self.u = u
     self.v = v
 def updateLength( self ):
     """
     Update the nanotube Length lineEdit widget.
     """
     nanotubeLength = vlen(self.endPoint1 - self.endPoint2)
     text = "%-7.4f Angstroms" % (nanotubeLength)
     self.ntLengthLineEdit.setText(text)
     return
예제 #52
0
 def updateLength(self):
     """
     Update the nanotube Length lineEdit widget.
     """
     nanotubeLength = vlen(self.endPoint1 - self.endPoint2)
     text = "%-7.4f Angstroms" % (nanotubeLength)
     self.ntLengthLineEdit.setText(text)
     return
예제 #53
0
 def __init__(self, point0, z, uhint, uhint2):
     # u and v and zn are unit vectors
     # z is NOT a unit vector
     self.p0 = point0
     self.p1 = point1 = point0 + z
     self.z = z
     zlen = vlen(z)
     if zlen < 1.0e-6:
         raise ZeroLengthCylinder()
     self.zinv = 1.0 / zlen
     self.zn = zn = norm(z)
     u = norm(uhint - (dot(uhint, z) / zlen**2) * z)
     if vlen(u) < 1.0e-4:
         u = norm(uhint2 - (dot(uhint2, z) / zlen**2) * z)
     v = cross(zn, u)
     self.u = u
     self.v = v
예제 #54
0
    def _draw(self, flipDirection = False, highlighted = False):
        """ Main drawing code. 
        @param flipDirection: This flag decides the direction in which the 
               arrow is drawn. This value is set in the leftClick method. 
               The default value is 'False'
        @type   flipDirection: bool
        @param highlighted: Decids the color of the arrow based on whether 
               it is highlighted. The default value is 'False'
        @type  highlighted: bool

        """

        if highlighted:
            color = orange
        else:
            color = gray

        if flipDirection:
            #@NOTE: Remember we are drawing the arrow inside of the _draw_geometry 
            #so its drawing it in the translated coordinate system (translated
            #at the center of the Plane. So we should do the following. 
            #(i.e. use V(0,0,1)). This will change if we decide to draw the 
            #direction arrow outside of the parent object 
            #requesting this drawing.--ninad 20070612
            #Using headPoint = self.tailPoint + V(0,0,1) * 2.0 etc along with 
            #the transformation matrix in self.draw_in_abs_coordinate()
            #fixes bug 2702 and 2703 -- Ninad 2008-06-11
            headPoint = self.tailPoint + V(0,0,1) * 2.0
            ##headPoint = self.tailPoint + 2.0 * norm(self.parent.getaxis())
        else:            
            headPoint = self.tailPoint - V(0,0,1) * 2.0
            ##headPoint = self.tailPoint - 2.0 * self.parent.getaxis()



        vec = vlen(headPoint - self.tailPoint)
        vec = self.glpane.scale*0.07*vec
        tailRadius = vlen(vec)*0.16

        drawDirectionArrow(color, 
                           self.tailPoint, 
                           headPoint,
                           tailRadius,
                           self.glpane.scale,
                           flipDirection = flipDirection)