Esempio n. 1
0
 def __init__(self, point1=None, point2=None, slab=None):
     # Huaicai 4/23/05: added some comments below to help understand the code.
     if slab:
         # convert from 2d (x, y) coordinates into its 3d world (x, y, 0)
         #coordinates(the lower-left and upper-right corner).
         #In another word, the 3d coordinates minus the z offset of the plane
         x = dot(A(point1), A(point2))
         # Get the vector from upper-right point to the lower-left point
         dx = subtract.reduce(x)
         # Get the upper-left and lower right corner points
         oc = x[1] + V(point2[0] * dot(dx, point2[0]),
                       point2[1] * dot(dx, point2[1]))
         # Get the four 3d cooridinates on the bottom crystal-cutting plane
         sq1 = cat(x, oc) + slab.normal * dot(slab.point, slab.normal)
         # transfer the above 4 3d coordinates in parallel to get that on
         #the top plane, put them together
         sq1 = cat(sq1, sq1 + slab.thickness * slab.normal)
         self.data = V(maximum.reduce(sq1), minimum.reduce(sq1))
     elif point2:
         # just 2 3d points
         self.data = V(maximum(point1, point2), minimum(point1, point2))
     elif point1:
         # list of points: could be 2d or 3d?  +/- 1.8 to make the bounding
         #box enclose the vDw ball of an atom?
         self.data = V(
             maximum.reduce(point1) + BBOX_MARGIN,
             minimum.reduce(point1) - BBOX_MARGIN)
     else:
         # a null bbox
         self.data = None
Esempio n. 2
0
def subdivide(tri,deep):
    if deep:
        a = tri[0]
        b = tri[1]
        c = tri[2]
        a1 = norm(A(tri[0]))
        b1 = norm(A(tri[1]))
        c1 = norm(A(tri[2]))
        d = tuple(norm(a1+b1))
        e = tuple(norm(b1+c1))
        f = tuple(norm(c1+a1))
        return subdivide((a,d,f), deep-1) + subdivide((d,e,f), deep-1) +\
               subdivide((d,b,e), deep-1) + subdivide((f,e,c), deep-1)
    else: return [tri]
Esempio n. 3
0
    def leftDrag(self, event):
        """
        Compute the changing rubber band window ending point. Erase the
        previous window, draw the new window.
        """
        # bugs 1190, 1818 wware 4/05/2006 - sometimes Qt neglects to call leftDown
        # before this
        if not hasattr(self, "pWxy") or not hasattr(self, "firstDraw"):
            return
        cWxy = (event.pos().x(), self.glpane.height - event.pos().y())

        rbwcolor = self.command.rbwcolor

        if not self.firstDraw:  #Erase the previous rubberband window
            drawrectangle(self.pStart, self.pPrev, self.glpane.up,
                          self.glpane.right, rbwcolor)
        self.firstDraw = False

        self.pPrev = A(gluUnProject(cWxy[0], cWxy[1], 0.005))
        # draw the new rubberband window
        drawrectangle(self.pStart, self.pPrev, self.glpane.up,
                      self.glpane.right, rbwcolor)

        glFlush()
        self.glpane.swapBuffers()  # Update display

        # Based on a suggestion in bug 2961, I added this second call to
        # swapBuffers(). It definitely helps, but the rectangle disappears
        # once the zoom cursor stops moving. I suspect this is due to
        # a gl_update() elsewhere.  I'll ask Bruce about his thoughts on
        # this. --Mark 2008-12-22.
        self.glpane.swapBuffers()
        return
Esempio n. 4
0
    def schedule_polycone(color, pos_array, rad_array, capped=0, opacity=1.0):
        """
        Schedule a polycone for rendering whenever ColorSorter thinks is
        appropriate.

        @note: this never uses shaders, even if it could. For a simple cone
            or tapered cylinder, you can pass a tuple of 2 radii to drawcylinder
            which will use shaders when available.
        """
        if ColorSorter._parent_csdl and ColorSorter._parent_csdl.reentrant:
            # todo: use different flag than .reentrant
            pos_array = [
                ColorSorter._transform_point(A(pos)) for pos in pos_array
            ]

        if ColorSorter.glpane.glprefs.use_c_renderer and ColorSorter.sorting and 0:  #bruce 090225 'and 0'
            if len(color) == 3:
                lcolor = (color[0], color[1], color[2], 1.0)
            else:
                lcolor = color
            assert 0, "Need to implement a C add_polycone_multicolor function."
            ColorSorter._cur_shapelist.add_polycone(
                lcolor, pos_array, rad_array, ColorSorter._gl_name_stack[-1],
                capped)
        else:
            if len(color) == 3:
                lcolor = (color[0], color[1], color[2], opacity)
            else:
                lcolor = color

            ColorSorter.schedule(lcolor, drawpolycone_worker,
                                 (pos_array, rad_array))
def inertia_eigenvectors(basepos, already_centered=False):
    """
    Given basepos (an array of positions),
    compute and return (as a 2-tuple) the lists of eigenvalues and
    eigenvectors of the inertia tensor (computed as if all points had the same
    mass). These lists are always length 3, even for len(basepos) of 0,1, or 2,
    overlapping or colinear points, etc, though some evals will be 0 in these cases.
       Optional small speedup: if caller knows basepos is centered at the origin, it can say so.
    """
    #bruce 060119 split this out of shakedown_poly_evals_evecs_axis() in chunk.py
    basepos = A(basepos)  # make sure it's a Numeric array
    if not already_centered and len(basepos):
        center = add.reduce(basepos) / len(basepos)
        basepos = basepos - center
    # compute inertia tensor
    tensor = zeros((3, 3), Float)
    for p in basepos:
        rsq = dot(p, p)
        m = -multiply.outer(p, p)
        m[0, 0] += rsq
        m[1, 1] += rsq
        m[2, 2] += rsq
        tensor += m
    evals, evecs = eigenvectors(tensor)
    assert len(evals) == len(evecs) == 3
    return evals, evecs
Esempio n. 6
0
def init_diamond():
    # a chunk of diamond grid, to be tiled out in 3d
    drawing_globals.sp0 = sp0 = 0.0
    #bruce 051102 replaced 1.52 with this constant (1.544),
    #  re bug 900 (partial fix.)
    drawing_globals.sp1 = sp1 = DIAMOND_BOND_LENGTH / sqrt(3.0)
    sp2 = 2.0 * sp1
    sp3 = 3.0 * sp1
    drawing_globals.sp4 = sp4 = 4.0 * sp1

    digrid = [[[sp0, sp0, sp0], [sp1, sp1, sp1]],
              [[sp1, sp1, sp1], [sp2, sp2, sp0]],
              [[sp2, sp2, sp0], [sp3, sp3, sp1]],
              [[sp3, sp3, sp1], [sp4, sp4, sp0]],
              [[sp2, sp0, sp2], [sp3, sp1, sp3]],
              [[sp3, sp1, sp3], [sp4, sp2, sp2]],
              [[sp2, sp0, sp2], [sp1, sp1, sp1]],
              [[sp1, sp1, sp1], [sp0, sp2, sp2]],
              [[sp0, sp2, sp2], [sp1, sp3, sp3]],
              [[sp1, sp3, sp3], [sp2, sp4, sp2]],
              [[sp2, sp4, sp2], [sp3, sp3, sp1]],
              [[sp3, sp3, sp1], [sp4, sp2, sp2]],
              [[sp4, sp0, sp4], [sp3, sp1, sp3]],
              [[sp3, sp1, sp3], [sp2, sp2, sp4]],
              [[sp2, sp2, sp4], [sp1, sp3, sp3]],
              [[sp1, sp3, sp3], [sp0, sp4, sp4]]]
    drawing_globals.digrid = A(digrid)
    drawing_globals.DiGridSp = sp4
    return
Esempio n. 7
0
 def recompute_center_axis(
         self,
         glpane):  #bruce 060120 replaced los arg with glpane re bug 1344
     # try to point in direction of prior axis, or along line of sight if no old axis (self.axis is V(0,0,0) then)
     nears = [self.axis, glpane.lineOfSight, glpane.down]
     pos = A(map(lambda a: a.posn(), self.atoms))
     self.center = sum(pos) / len(pos)
     relpos = pos - self.center
     from geometry.geometryUtilities import compute_heuristic_axis
     axis = compute_heuristic_axis(relpos,
                                   'normal',
                                   already_centered=True,
                                   nears=nears,
                                   dflt=None)
     if not axis:
         #e warning? I think so... BTW we pass dflt = None to make the warning come out more often;
         # I don't know if we'd need to check for it here if we didn't.
         env.history.message(
             orangemsg(
                 "Warning: motor axis chosen arbitrarily since atom arrangement doesn't suggest one."
             ))
         #k can this come out too often during movie-playing? No, because we don't recompute axis then at all.
         axis = glpane.lineOfSight
     self.axis = axis
     self.assy.changed(
     )  #bruce 060116 fix unreported bug analogous to bug 1331
     self._initial_posns = None  #bruce 050518; needed in RotaryMotor, harmless in others
     return
    def leftDrag(self, event):
        """
        Compute the changing rubber band window ending point. Erase the
        previous window, draw the new window.
        """
        # bugs 1190, 1818 wware 4/05/2006 - sometimes Qt neglects to call leftDown
        # before this
        if not hasattr(self, "pWxy") or not hasattr(self, "firstDraw"):
            return
        cWxy = (event.pos().x(), self.glpane.height - event.pos().y())

        rbwcolor = self.command.rbwcolor

        if not self.firstDraw:  #Erase the previous rubber window
            drawrectangle(self.pStart, self.pPrev, self.glpane.up,
                          self.glpane.right, rbwcolor)
        self.firstDraw = False

        self.pPrev = A(gluUnProject(cWxy[0], cWxy[1], 0.005))
        # draw the new rubber band
        drawrectangle(self.pStart, self.pPrev, self.glpane.up,
                      self.glpane.right, rbwcolor)
        glFlush()
        self.glpane.swapBuffers()  # Update display
        return
    def leftDown(self, event):
        """
        Compute the rubber band window starting point, which
        lies on the near clipping plane, projecting into the same 
        point that current cursor points at on the screen plane.
        """
        self.pWxy = (event.pos().x(), self.glpane.height - event.pos().y())
        p1 = A(gluUnProject(self.pWxy[0], self.pWxy[1], 0.005))

        self.pStart = p1
        self.pPrev = p1
        self.firstDraw = True

        self.command.glStatesChanged = True
        # this warns our exit code to undo the following OpenGL state changes:

        self.glpane.redrawGL = False
        glDisable(GL_DEPTH_TEST)
        glDisable(GL_LIGHTING)
        rbwcolor = self.command.rbwcolor
        glColor3d(rbwcolor[0], rbwcolor[1], rbwcolor[2])

        glEnable(GL_COLOR_LOGIC_OP)
        glLogicOp(GL_XOR)

        return
    def Enter(self):
        super(ZoomToAreaMode, self).Enter()
        bg = self.glpane.backgroundColor

        # rubber window shows as white color normally, but when the
        # background becomes bright, we'll set it as black.
        brightness = bg[0] + bg[1] + bg[2]
        if brightness > 1.5:
            self.rbwcolor = bg
            # note: accessed as self.command.rbwcolor in our GraphicsMode part
        else:
            self.rbwcolor = A((1.0, 1.0, 1.0)) - A(bg)

        self.glStatesChanged = False
        # note: accessed as self.command.glStatesChanged in our GraphicsMode part
        return
Esempio n. 11
0
def mymousepoints(glpane, x, y): #bruce 071017 moved this here from testdraw.py
    ### TODO: rename, docstring

    # modified from GLPane.mousepoints; x and y are window coords (except y is 0 at bottom, positive as you go up [guess 070124])
    self = glpane
    just_beyond = 0.0
    p1 = A(gluUnProject(x, y, just_beyond))
    p2 = A(gluUnProject(x, y, 1.0))

    los = self.lineOfSight # isn't this just norm(p2 - p1)?? Probably not, if we're in perspective mode! [bruce Q 061206]
        # note: this might be in abs coords (not sure!) even though p1 and p2 would be in local coords.
        # I need to review that in GLPane.__getattr__. ###k
    
    k = dot(los, -self.pov - p1) / dot(los, p2 - p1)

    p2 = p1 + k*(p2-p1)
    return (p1, p2)
Esempio n. 12
0
 def norm_project_posns(self, posns):
     """
     [Private helper for getrotation]
     Given a Numeric array of position vectors relative to self.center,
     project them along self.axis and normalize them (and return that --
     but we take ownership of posns passed to us, so we might or might not
     modify it and might or might not return the same (modified) object.
     """
     axis = self.axis
     dots = dot(posns, axis)
     ## axis_times_dots = axis * dots #  guess from this line: exceptions.ValueError: frames are not aligned
     axis_times_dots = A(len(dots) * [axis]) * reshape(dots,(len(dots),1)) #k would it be ok to just use axis * ... instead?
     posns -= axis_times_dots
     ##posns = norm(posns) # some exception from this
     posns = A(map(norm, posns))
         # assumes no posns are right on the axis! now we think they are on a unit circle perp to the axis...
     # posns are now projected to a plane perp to axis and centered on self.center, and turned into unit-length vectors.
     return posns # (note: in this implem, we did modify the mutable argument posns, but are returning a different object anyway.)
Esempio n. 13
0
    def mmp_record_jigspecific_midpart(self):
        color = map(int, A(self.fill_color) * 255)

        dataline = "%.2f %.2f %d (%f, %f, %f) (%f, %f, %f, %f) %.2f (%d, %d, %d) %d %.2f %.2f" % \
                 (self.width, self.height, self.resolution,
                  self.center[0], self.center[1], self.center[2],
                  self.quat.w, self.quat.x, self.quat.y, self.quat.z,
                  self.opacity, color[0], color[1], color[2], self.show_esp_bbox, self.image_offset, self.edge_offset)
        return " " + dataline
Esempio n. 14
0
    def grab_untransformed_data(self, primID):  #bruce 090223
        """
        """
        endptRad0 = self.endptRad0Hunks.getData(primID)
        endptRad1 = self.endptRad1Hunks.getData(primID)

        #### these can be removed after debugging:
        assert len(
            endptRad0) == 4, "len(endptRad0) should be 4: %r" % (endptRad0, )
        assert len(
            endptRad1) == 4, "len(endptRad1) should be 4: %r" % (endptRad1, )
        assert len(endptRad0[:3]
                   ) == 3, "len slice of 3 (in endptRad0) should be 3: %r" % (
                       endptRad0[:3], )
        assert len(endptRad1[:3]
                   ) == 3, "len slice of 3 (in endptRad1) should be 3: %r" % (
                       endptRad1[:3], )

        return A(endptRad0[:3]), endptRad0[3], A(endptRad1[:3]), endptRad1[3]
Esempio n. 15
0
 def _drawtext(self, text, color):
     # use atom positions to compute center, where text should go
     if self.picked:
         # move the text to the lower left corner, and make it big
         pos = A(gluUnProject(5, 5, 0))
         drawtext(text, color, pos, 3 * self.font_size, self.assy.o)
     else:
         pos1 = self.atoms[0].posn()
         pos2 = self.atoms[-1].posn()
         pos = (pos1 + pos2) / 2
         drawtext(text, color, pos, self.font_size, self.assy.o)
Esempio n. 16
0
    def viewNormalTo(self): # 
        """
        Set view to the normal vector of the plane defined by 3 or more
        selected atoms or a jig's (Motor or RectGadget) axis.
        """
        cmd = greenmsg("Set View Normal To: ")

        chunks = self.assy.selmols
        jigs = self.assy.getSelectedJigs()
        atoms = self.assy.selatoms_list()

        #following fixes bug 1748 ninad 061003. 
        if len(chunks) > 0 and len(atoms) == 0:
            # Even though chunks have an axis, it is not necessarily the same
            # axis attr stored in the chunk.  Get the chunks atoms and let
            # compute_heuristic_axis() recompute them.
            for c in range(len(chunks)):
                atoms += chunks[c].atoms.values()
        elif len(jigs) == 1 and len(atoms) == 0:
            # Warning: RectGadgets have no atoms.  We handle this special case below.
            atoms = jigs[0].atoms 
        elif len(atoms) < 3:
            # There is a problem when allowing only 2 selected atoms. 
            # Changing requirement to 3 atoms fixes bug 1418. mark 060322
            msg = redmsg("Please select some atoms, jigs, and/or chunks, covering at least 3 atoms")
            print "ops_view.py len(atoms) = ", len(atoms)
            env.history.message(cmd + msg)
            return

        # This check is needed for jigs that have no atoms.  Currently, this 
        # is the case for RectGadgets (ESP Image and Grid Plane) only.
        if len(atoms):
            pos = A( map( lambda a: a.posn(), atoms ) )
            nears = [ self.glpane.out, self.glpane.up ]
            axis = compute_heuristic_axis( pos, 'normal', already_centered = False, nears = nears, dflt = None )
        else: # We have a jig with no atoms.
            axis = jigs[0].getaxis() # Get the jig's axis.
            # If axis is pointing into the screen, negate (reverse) axis.
            if dot(axis, self.glpane.lineOfSight) > 0:
                axis = -axis

        if not axis:
            msg = orangemsg( "Warning: Normal axis could not be determined. No change in view." )
            env.history.message(cmd + msg)
            return

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

        self.glpane.rotateView(q2)

        info = 'View set to normal vector of the plane defined by the selected atoms.'
        env.history.message(cmd + info)
Esempio n. 17
0
    def command_entered(self):
        super(ZoomToAreaMode, self).command_entered()
        bg = self.glpane.backgroundColor

        # rubber window shows as white color normally, but when the
        # background becomes bright, we'll set it as black.
        brightness = bg[0] + bg[1] + bg[2]
        if brightness > 1.5:
            self.rbwcolor = bg
            # note: accessed as self.command.rbwcolor in our GraphicsMode part
        else:
            # REVIEW: should the following color be converted to tuple(),
            # in case it's black and the Numeric.array version
            # would fool some code due to being boolean false?
            # [bruce 080829 question]
            self.rbwcolor = A((1.0, 1.0, 1.0)) - A(bg)

        self.glStatesChanged = False
        # note: accessed as self.command.glStatesChanged in our GraphicsMode part
        return
Esempio n. 18
0
    def mmp_record_jigspecific_midpart(self):
        """
        format: width height (cx, cy, cz) (w, x, y, z) grid_type line_type x_space y_space (gr, gg, gb)
        """
        color = map(int, A(self.grid_color) * 255)

        dataline = "%.2f %.2f (%f, %f, %f) (%f, %f, %f, %f) %d %d %.2f %.2f (%d, %d, %d)" % \
           (self.width, self.height, self.center[0], self.center[1], self.center[2],
            self.quat.w, self.quat.x, self.quat.y, self.quat.z, self.grid_type, self.line_type,
            self.x_spacing, self.y_spacing, color[0], color[1], color[2])
        return " " + dataline
Esempio n. 19
0
 def _setup_shaderCubeList(self):  # not used
     self.shaderCubeList = glGenLists(1)
     glNewList(self.shaderCubeList, GL_COMPILE)
     verts = self.shaderCubeVerts
     indices = self.shaderCubeIndices
     glBegin(GL_QUADS)
     for i in range(6):
         for j in range(4):
             glVertex3fv(A(verts[indices[i][j]]))
             continue
         continue
     glEnd()
     glEndList()
Esempio n. 20
0
    def viewNormalTo_NEW(self):
        """
        Set view to the normal vector of the plane defined by 3 or more
        selected atoms or a jig's (Motor or RectGadget) axis.
        """
        # This implementation has two serious problems:
        #   1. it selects a normal based on the atoms and not the axis of a jig (e.g. a moved rotary motor).
        #   2. doesn't consider selected jigs that have no atoms.
        # Bruce and I will discuss this and determine the best implem.
        # For A7, I've decide to use the original version. This version will be reinstated in A8
        # after fixing these problems. mark 060322.

        cmd = greenmsg("Set View Normal To: ")

        atoms = self.assy.getSelectedAtoms()

        if len(atoms) < 3:
            # There is a problem when allowing only 2 selected atoms.
            # Changing requirement to 3 atoms fixes bug 1418. mark 060322
            msg = redmsg(
                "Please select some atoms, jigs, and/or chunks, covering at least 3 atoms"
            )
            env.history.message(cmd + msg)
            return

        pos = A(map(lambda a: a.posn(),
                    atoms))  # build list of atom xyz positions.
        nears = [self.glpane.out, self.glpane.up]
        axis = compute_heuristic_axis(pos,
                                      'normal',
                                      already_centered=False,
                                      nears=nears,
                                      dflt=None)

        if not axis:
            msg = orangemsg(
                "Warning: Normal axis could not be determined. No change in view."
            )
            env.history.message(cmd + msg)
            return

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

        self.glpane.rotateView(q2)

        info = 'View set to normal of the plane defined by the selection.'
        env.history.message(cmd + info)
Esempio n. 21
0
    def mmp_record_jigspecific_midpart(self):
        """
        Returns the "midpart" of the Plane's MMP record in the format:
         - width height (cx, cy, cz) (w, x, y, z)

        @return: The midpart of the Plane's MMP record
        @rtype:  str
        """

        gridColor = map(int, A(self.gridColor) * 255)

        #This value is used in method mmp_record  of class Jig
        dataline = "%.2f %.2f (%f, %f, %f) (%f, %f, %f, %f) " %\
                 (self.width, self.height,
                  self.center[0], self.center[1], self.center[2],
                  self.quat.w, self.quat.x, self.quat.y, self.quat.z)
        return " " + dataline
Esempio n. 22
0
def insertin(assy, filename):
    _init()

    dir, nodename = os.path.split(filename)

    mol = Chunk(assy, nodename)
    mol.showOverlayText = True

    file = open(filename)
    lines = file.readlines()
    atoms = {}
    transform = InternalCoordinatesToCartesian(len(lines), None)
    for line in lines:
        columns = line.strip().split()
        index = int(columns[0])
        name = columns[1]
        type = columns[2]
        na = int(columns[4])
        nb = int(columns[5])
        nc = int(columns[6])
        r = float(columns[7])
        theta = float(columns[8])
        phi = float(columns[9])

        transform.addInternal(index, na, nb, nc, r, theta, phi)
        xyz = transform.getCartesian(index)

        if (index > 3):
            if (AMBER_AtomTypes.has_key(type)):
                sym = AMBER_AtomTypes[type]
            else:
                print "unknown AMBER atom type, substituting Carbon: %s" % type
                sym = "C"

            a = Atom(sym, A(xyz), mol)
            atoms[index] = a
            a.setOverlayText(type)
            if (na > 3):
                a2 = atoms[na]
                bond_atoms(a, a2)

    assy.addmol(mol)
Esempio n. 23
0
    def writemmp(self, mapping):
        """
        [extends ReferenceGeometry method]
        """
        # piotr 080613 added this method
        super = ReferenceGeometry
        super.writemmp(self, mapping)

        # Write plane "info" record.
        # Ninad 2008-06-25: Added support for various grid attrs.
        gridColor = map(int, A(self.gridColor) * 255)
        gridColorString = "%d %d %d" % (gridColor[0], gridColor[1],
                                        gridColor[2])

        line = "info plane gridColor = " + gridColorString + "\n"

        mapping.write(line)

        line = "info plane gridLineType = %d\n" % (self.gridLineType)
        mapping.write(line)

        line = "info plane gridXSpacing = %0.2f\n" % (self.gridXSpacing)
        mapping.write(line)

        line = "info plane gridYSpacing = %0.2f\n" % (self.gridYSpacing)
        mapping.write(line)

        line = "info plane originLocation = %d\n" % (self.originLocation)
        mapping.write(line)

        line = "info plane displayLabelStyle =  %d\n" % (
            self.displayLabelStyle)
        mapping.write(line)

        line = "info plane image_file = %s\n" % (self.imagePath)
        mapping.write(line)
        line = "info plane image_settings = %d\n" % (self.display_image)
        mapping.write(line)

        return
def move_atoms_and_normalize_bondpoints(alist, newPositions):
    """
    Move the atoms in alist to the new positions in the given array or sequence
    (which must have the same length);
    then for any singlets in alist, correct their positions using Atom.snuggle.

    @warning: it would be wrong to call this on several alists in a row if they
              might overlap or were connected by bonded atoms, for the same
              reason that the snuggle has to be done in a separate loop
              (see snuggle docstring for details, re bug 1239).

    @warning: I'm not sure this does all required invals; doesn't do gl_update.
    """
    #bruce 051221 split this out of class Movie so its bug1239 fix can be used
    # in jig_Gamess. [later: Those callers have duplicated code which should be
    # cleaned up.]
    #bruce 090112 renamed from move_alist_and_snuggle
    #todo: refile into a new file in operations package
    assert len(alist) == len(newPositions)
    singlets = []
    for a, newPos in zip(alist, newPositions):
        #bruce 050406 this needs a special case for singlets, in case they are H
        # in the xyz file (and therefore have the wrong distance from their base
        # atom). Rather than needing to know whether or not they were H during
        # the sim, we can just regularize the singlet-baseatom distance for all
        # singlets. For now I'll just use setposn to set the direction and
        # snuggle to fix the distance.
        # REVIEW: should it also regularize the distance for H itself? Maybe
        # only if sim value is wildly wrong, and it should also complain.
        # I won't do this for now.
        a.setposn(A(newPos))
        if a.is_singlet():  # same code as in movend()
            #bruce 051221 to fix bug 1239: do all snuggles after all moves;
            # see snuggle docstring warning
            singlets.append(a)
        continue
    for a in singlets:
        a.snuggle()  # includes a.setposn
    return
Esempio n. 25
0
    def schedule_polycone_multicolor(color,
                                     pos_array,
                                     color_array,
                                     rad_array,
                                     capped=0,
                                     opacity=1.0):
        """
        Schedule a polycone for rendering whenever ColorSorter thinks is
        appropriate.

        piotr 080311: this variant accepts a color array as an additional
        parameter
        """
        if ColorSorter._parent_csdl and ColorSorter._parent_csdl.reentrant:
            # todo: use different flag than .reentrant
            pos_array = [
                ColorSorter._transform_point(A(pos)) for pos in pos_array
            ]

        if ColorSorter.glpane.glprefs.use_c_renderer and ColorSorter.sorting:
            if len(color) == 3:
                lcolor = (color[0], color[1], color[2], 1.0)
            else:
                lcolor = color
            assert 0, "Need to implement a C add_polycone function."
            ColorSorter._cur_shapelist.add_polycone_multicolor(
                lcolor, pos_array, color_array, rad_array,
                ColorSorter._gl_name_stack[-1], capped)
        else:
            if len(color) == 3:
                lcolor = (color[0], color[1], color[2], opacity)
            else:
                lcolor = color

            ColorSorter.schedule(lcolor, drawpolycone_multicolor_worker,
                                 (pos_array, color_array, rad_array))
Esempio n. 26
0
    def select(self, wX, wY):
        """
        Use the OpenGL picking/selection to select any object. Return the
        selected object, otherwise, return None. Restore projection and
        modelview matrices before returning.
        """
        ### NOTE: this code is similar to (and was copied and modified from)
        # GLPane_highlighting_methods.do_glselect_if_wanted, but also differs
        # in significant ways (too much to make it worth merging, unless we
        # decide to merge the differing algorithms as well). It's one of
        # several instances of hit-test code that calls glRenderMode.
        # [bruce 060721/080917 comment]
        wZ = glReadPixelsf(wX, wY, 1, 1, GL_DEPTH_COMPONENT)
        gz = wZ[0][0]

        if gz >= GL_FAR_Z:  # Empty space was clicked
            return None

        pxyz = A(gluUnProject(wX, wY, gz))
        pn = self.out
        pxyz -= 0.0002 * pn
        # Note: if this runs before the model is drawn, this can have an
        # exception "OverflowError: math range error", presumably because
        # appropriate state for gluUnProject was not set up. That doesn't
        # normally happen but can happen due to bugs (no known open bugs
        # of that kind).
        # Sometimes our drawing area can become "stuck at gray",
        # and when that happens, the same exception can occur from this line.
        # Could it be that too many accidental mousewheel scrolls occurred
        # and made the scale unreasonable? (To mitigate, we should prevent
        # those from doing anything unless we have a valid model, and also
        # reset that scale when loading a new model (latter is probably
        # already done, but I didn't check). See also the comments
        # in def wheelEvent.) [bruce 080220 comment]
        dp = -dot(pxyz, pn)

        # Save projection matrix before it's changed.
        glMatrixMode(GL_PROJECTION)
        glPushMatrix()

        current_glselect = (wX, wY, 1, 1)
        self._setup_projection(glselect=current_glselect)

        glSelectBuffer(self.SIZE_FOR_glSelectBuffer)
        glRenderMode(GL_SELECT)
        glInitNames()
        glMatrixMode(GL_MODELVIEW)
        # Save model view matrix before it's changed.
        glPushMatrix()

        # Draw model using glRenderMode(GL_SELECT) as set up above
        try:
            glClipPlane(GL_CLIP_PLANE0, (pn[0], pn[1], pn[2], dp))
            glEnable(GL_CLIP_PLANE0)
            self._drawModel_using_DrawingSets()
        except:
            #bruce 080917 fixed predicted bugs in this except clause (untested)
            print_compact_traceback(
                "exception in ThumbView._drawModel_using_DrawingSets() during GL_SELECT; ignored; restoring matrices: "
            )
            glDisable(GL_CLIP_PLANE0)
            glMatrixMode(GL_PROJECTION)
            glPopMatrix()
            glMatrixMode(GL_MODELVIEW)
            glPopMatrix()
            glRenderMode(GL_RENDER)
            return None
        else:
            glDisable(GL_CLIP_PLANE0)
            # Restore model/view matrix
            glPopMatrix()

        # Restore projection matrix and set matrix mode to Model/View
        glMatrixMode(GL_PROJECTION)
        glPopMatrix()
        glMatrixMode(GL_MODELVIEW)

        glFlush()

        hit_records = list(glRenderMode(GL_RENDER))
        ## print "%d hits" % len(hit_records)
        for (near, far, names) in hit_records:
            ## print "hit record: near, far, names:", near, far, names
            # note from testing: near/far are too far apart to give actual depth,
            # in spite of the 1-pixel drawing window (presumably they're vertices
            # taken from unclipped primitives, not clipped ones).
            ### REVIEW: this just returns the first candidate object found.
            # The clip plane may restrict the set of candidates well enough to
            # make sure that's the right one, but this is untested and unreviewed.
            # (And it's just my guess that that was Huaicai's intention in
            #  setting up clipping, since it's not documented. I'm guessing that
            #  the plane is just behind the hitpoint, but haven't confirmed this.)
            # [bruce 080917 comment]
            if names:
                name = names[-1]
                assy = self.assy
                obj = assy and assy.object_for_glselect_name(name)
                #k should always return an obj
                return obj
        return None  # from ThumbView.select
Esempio n. 27
0
    def _leftDown_preparation_for_dragging(self, objectUnderMouse, event):
        """
        Handle left down event. Preparation for translation and/or selection
        This method is called inside of self.leftDown.
        @param event: The mouse left down event.
        @type  event: QMouseEvent instance
        @see: self.leftDown
        @see: self.leftDragTranslation
        Overrides _superclass._leftDown_preparation_for_dragging
        """
        _superclass._leftDown_preparation_for_dragging(self, objectUnderMouse,
                                                       event)

        self.o.SaveMouse(event)
        self.picking = True
        self.dragdist = 0.0
        self.transDelta = 0  # X, Y or Z deltas for translate.
        self.moveOffset = [0.0, 0.0, 0.0]  # X, Y and Z offset for move.

        farQ_junk, self.movingPoint = self.dragstart_using_GL_DEPTH(event)
        # Following is in leftDrag() to compute move offset during drag op.
        self.startpt = self.movingPoint

        # Translate section

        if self.moveOption != 'MOVEDEFAULT':
            if self.moveOption == 'TRANSX':
                ma = V(1, 0, 0)  # X Axis
                self.axis = 'X'
            elif self.moveOption == 'TRANSY':
                ma = V(0, 1, 0)  # Y Axis
                self.axis = 'Y'
            elif self.moveOption == 'TRANSZ':
                ma = V(0, 0, 1)  # Z Axis
                self.axis = 'Z'
            elif self.moveOption == 'ROT_TRANS_ALONG_AXIS':
                #The method 'self._leftDown_preparation_for_dragging should
                #never be reached if self.moveOption is 'ROT_TRANS_ALONG_AXIS'
                #If this code is reached, it indicates a bug. So fail gracefully
                #by calling self.leftADown()
                if debug_flags.atom_debug:
                    print_compact_stack("bug: _leftDown_preparation_for_dragging"\
                                        " called for translate option"\
                                        "'ROT_TRANS_ALONG_AXIS'")
                self.leftADown(objectUnderMouse, event)
                return
            else:
                print "modifyMode: Error - unknown moveOption value =", self.moveOption

            ma = norm(V(dot(ma, self.o.right), dot(ma, self.o.up)))
            # When in the front view, right = 1,0,0 and up = 0,1,0, so ma will
            #be computed as 0,0.
            # This creates a special case problem when the user wants to
            #constrain rotation around
            # the Z axis because Zmat will be zero.  So we have to test for
            #this case (ma = 0,0) and
            # fix ma to -1,0.  This was needed to fix bug 537.  Mark 050420
            if ma[0] == 0.0 and ma[1] == 0.0: ma = [-1.0, 0.0]
            self.Zmat = A([ma, [-ma[1], ma[0]]])

            # end of Translate section

        self.leftDownType = 'TRANSLATE'

        return
Esempio n. 28
0
def setup_drawer():
    """
    Set up the usual constant display lists in the current OpenGL context.

    WARNING: THIS IS ONLY CORRECT IF ONLY ONE GL CONTEXT CONTAINS DISPLAY LISTS
    -- or more precisely, only the GL context this has last been called in (or
    one which shares its display lists) will work properly with the routines in
    drawer.py, since the allocated display list names are stored in globals set
    by this function, but in general those names might differ if this was called
    in different GL contexts.
    """
    #bruce 060613 added docstring, cleaned up display list name allocation
    # bruce 071030 renamed from setup to setup_drawer

    spherelistbase = glGenLists(numSphereSizes)
    sphereList = []
    for i in range(numSphereSizes):
        sphereList += [spherelistbase + i]
        glNewList(sphereList[i], GL_COMPILE)
        glBegin(GL_TRIANGLE_STRIP)  # GL_LINE_LOOP to see edges.
        stripVerts = getSphereTriStrips(i)
        for vertNorm in stripVerts:
            glNormal3fv(vertNorm)
            glVertex3fv(vertNorm)
            continue
        glEnd()
        glEndList()
        continue
    drawing_globals.sphereList = sphereList

    # Sphere triangle-strip vertices for each level of detail.
    # (Cache and re-use the work of making them.)
    # Can use in converter-wrappered calls like glVertexPointerfv,
    # but the python arrays are re-copied to C each time.
    sphereArrays = []
    for i in range(numSphereSizes):
        sphereArrays += [getSphereTriStrips(i)]
        continue
    drawing_globals.sphereArrays = sphereArrays

    # Sphere glDrawArrays triangle-strip vertices for C calls.
    # (Cache and re-use the work of converting a C version.)
    # Used in thinly-wrappered calls like glVertexPointer.
    sphereCArrays = []
    for i in range(numSphereSizes):
        CArray = numpy.array(sphereArrays[i], dtype=numpy.float32)
        sphereCArrays += [CArray]
        continue
    drawing_globals.sphereCArrays = sphereCArrays

    # Sphere indexed vertices.
    # (Cache and re-use the work of making the indexes.)
    # Can use in converter-wrappered calls like glDrawElementsui,
    # but the python arrays are re-copied to C each time.
    sphereElements = []  # Pairs of lists (index, verts) .
    for i in range(numSphereSizes):
        sphereElements += [indexVerts(sphereArrays[i], .0001)]
        continue
    drawing_globals.sphereElements = sphereElements

    # Sphere glDrawElements index and vertex arrays for C calls.
    sphereCIndexTypes = []  # numpy index unsigned types.
    sphereGLIndexTypes = []  # GL index types for drawElements.
    sphereCElements = []  # Pairs of numpy arrays (Cindex, Cverts) .
    for i in range(numSphereSizes):
        (index, verts) = sphereElements[i]
        if len(index) < 256:
            Ctype = numpy.uint8
            GLtype = GL_UNSIGNED_BYTE
        else:
            Ctype = numpy.uint16
            GLtype = GL_UNSIGNED_SHORT
            pass
        sphereCIndexTypes += [Ctype]
        sphereGLIndexTypes += [GLtype]
        sphereCIndex = numpy.array(index, dtype=Ctype)
        sphereCVerts = numpy.array(verts, dtype=numpy.float32)
        sphereCElements += [(sphereCIndex, sphereCVerts)]
        continue
    drawing_globals.sphereCIndexTypes = sphereCIndexTypes
    drawing_globals.sphereGLIndexTypes = sphereGLIndexTypes
    drawing_globals.sphereCElements = sphereCElements

    if glGetString(GL_EXTENSIONS).find("GL_ARB_vertex_buffer_object") >= 0:

        # A GLBufferObject version for glDrawArrays.
        sphereArrayVBOs = []
        for i in range(numSphereSizes):
            vbo = GLBufferObject(GL_ARRAY_BUFFER_ARB, sphereCArrays[i],
                                 GL_STATIC_DRAW)
            sphereArrayVBOs += [vbo]
            continue
        drawing_globals.sphereArrayVBOs = sphereArrayVBOs

        # A GLBufferObject version for glDrawElements indexed verts.
        sphereElementVBOs = []  # Pairs of (IBO, VBO)
        for i in range(numSphereSizes):
            ibo = GLBufferObject(GL_ELEMENT_ARRAY_BUFFER_ARB,
                                 sphereCElements[i][0], GL_STATIC_DRAW)
            vbo = GLBufferObject(GL_ARRAY_BUFFER_ARB, sphereCElements[i][1],
                                 GL_STATIC_DRAW)
            sphereElementVBOs += [(ibo, vbo)]
            continue
        drawing_globals.sphereElementVBOs = sphereElementVBOs

        ibo.unbind()
        vbo.unbind()
        pass

    #bruce 060415
    drawing_globals.wiresphere1list = wiresphere1list = glGenLists(1)
    glNewList(wiresphere1list, GL_COMPILE)
    didlines = {}  # don't draw each triangle edge more than once

    def shoulddoline(v1, v2):
        # make sure not list (unhashable) or Numeric array (bug in __eq__)
        v1 = tuple(v1)
        v2 = tuple(v2)
        if (v1, v2) not in didlines:
            didlines[(v1, v2)] = didlines[(v2, v1)] = None
            return True
        return False

    def doline(v1, v2):
        if shoulddoline(v1, v2):
            glVertex3fv(v1)
            glVertex3fv(v2)
        return

    glBegin(GL_LINES)
    ocdec = getSphereTriangles(1)
    for tri in ocdec:
        #e Could probably optim this more, e.g. using a vertex array or VBO or
        #  maybe GL_LINE_STRIP.
        doline(tri[0], tri[1])
        doline(tri[1], tri[2])
        doline(tri[2], tri[0])
    glEnd()
    glEndList()

    drawing_globals.CylList = CylList = glGenLists(1)
    glNewList(CylList, GL_COMPILE)
    glBegin(GL_TRIANGLE_STRIP)
    for (vtop, ntop, vbot, nbot) in drawing_globals.cylinderEdges:
        glNormal3fv(nbot)
        glVertex3fv(vbot)
        glNormal3fv(ntop)
        glVertex3fv(vtop)
    glEnd()
    glEndList()

    drawing_globals.CapList = CapList = glGenLists(1)
    glNewList(CapList, GL_COMPILE)
    glNormal3fv(drawing_globals.cap0n)
    glBegin(GL_POLYGON)
    for p in drawing_globals.drum0:
        glVertex3fv(p)
    glEnd()
    glNormal3fv(drawing_globals.cap1n)
    glBegin(GL_POLYGON)
    #bruce 060609 fix "ragged edge" bug in this endcap: drum1 -> drum2
    for p in drawing_globals.drum2:
        glVertex3fv(p)
    glEnd()
    glEndList()

    drawing_globals.diamondGridList = diamondGridList = glGenLists(1)
    glNewList(diamondGridList, GL_COMPILE)
    glBegin(GL_LINES)
    for p in drawing_globals.digrid:
        glVertex(p[0])
        glVertex(p[1])
    glEnd()
    glEndList()

    drawing_globals.lonsGridList = lonsGridList = glGenLists(1)
    glNewList(lonsGridList, GL_COMPILE)
    glBegin(GL_LINES)
    for p in drawing_globals.lonsEdges:
        glVertex(p[0])
        glVertex(p[1])
    glEnd()
    glEndList()

    drawing_globals.CubeList = CubeList = glGenLists(1)
    glNewList(CubeList, GL_COMPILE)
    glBegin(GL_QUAD_STRIP)
    # note: CubeList has only 4 faces of the cube; only suitable for use in
    # wireframes; see also solidCubeList [bruce 051215 comment reporting
    # grantham 20051213 observation]
    glVertex((-1, -1, -1))
    glVertex((1, -1, -1))
    glVertex((-1, 1, -1))
    glVertex((1, 1, -1))
    glVertex((-1, 1, 1))
    glVertex((1, 1, 1))
    glVertex((-1, -1, 1))
    glVertex((1, -1, 1))
    glVertex((-1, -1, -1))
    glVertex((1, -1, -1))
    glEnd()
    glEndList()

    drawing_globals.solidCubeList = solidCubeList = glGenLists(1)
    glNewList(solidCubeList, GL_COMPILE)
    glBegin(GL_QUADS)
    for i in xrange(len(drawing_globals.cubeIndices)):
        avenormals = V(0, 0, 0)  #bruce 060302 fixed normals for flat shading
        for j in xrange(4):
            nTuple = tuple(
                drawing_globals.cubeNormals[drawing_globals.cubeIndices[i][j]])
            avenormals += A(nTuple)
        avenormals = norm(avenormals)
        for j in xrange(4):
            vTuple = tuple(drawing_globals.cubeVertices[
                drawing_globals.cubeIndices[i][j]])
            #bruce 060302 made size compatible with glut.glutSolidCube(1.0)
            vTuple = A(vTuple) * 0.5
            glNormal3fv(avenormals)
            glVertex3fv(vTuple)
    glEnd()
    glEndList()

    drawing_globals.rotSignList = rotSignList = glGenLists(1)
    glNewList(rotSignList, GL_COMPILE)
    glBegin(GL_LINE_STRIP)
    for ii in xrange(len(drawing_globals.rotS0n)):
        glVertex3fv(tuple(drawing_globals.rotS0n[ii]))
    glEnd()
    glBegin(GL_LINE_STRIP)
    for ii in xrange(len(drawing_globals.rotS1n)):
        glVertex3fv(tuple(drawing_globals.rotS1n[ii]))
    glEnd()
    glBegin(GL_TRIANGLES)
    for v in drawing_globals.arrow0Vertices + drawing_globals.arrow1Vertices:
        glVertex3f(v[0], v[1], v[2])
    glEnd()
    glEndList()

    drawing_globals.linearArrowList = linearArrowList = glGenLists(1)
    glNewList(linearArrowList, GL_COMPILE)
    glBegin(GL_TRIANGLES)
    for v in drawing_globals.linearArrowVertices:
        glVertex3f(v[0], v[1], v[2])
    glEnd()
    glEndList()

    drawing_globals.linearLineList = linearLineList = glGenLists(1)
    glNewList(linearLineList, GL_COMPILE)
    glEnable(GL_LINE_SMOOTH)
    glBegin(GL_LINES)
    glVertex3f(0.0, 0.0, -drawing_globals.halfHeight)
    glVertex3f(0.0, 0.0, drawing_globals.halfHeight)
    glEnd()
    glDisable(GL_LINE_SMOOTH)
    glEndList()

    drawing_globals.circleList = circleList = glGenLists(1)
    glNewList(circleList, GL_COMPILE)
    glBegin(GL_LINE_LOOP)
    for ii in range(60):
        x = cos(ii * 2.0 * pi / 60)
        y = sin(ii * 2.0 * pi / 60)
        glVertex3f(x, y, 0.0)
    glEnd()
    glEndList()

    # piotr 080405
    drawing_globals.filledCircleList = filledCircleList = glGenLists(1)
    glNewList(filledCircleList, GL_COMPILE)
    glBegin(GL_POLYGON)
    for ii in range(60):
        x = cos(ii * 2.0 * pi / 60)
        y = sin(ii * 2.0 * pi / 60)
        glVertex3f(x, y, 0.0)
    glEnd()
    glEndList()

    drawing_globals.lineCubeList = lineCubeList = glGenLists(1)
    glNewList(lineCubeList, GL_COMPILE)
    glBegin(GL_LINES)
    cvIndices = [
        0, 1, 2, 3, 4, 5, 6, 7, 0, 3, 1, 2, 5, 6, 4, 7, 0, 4, 1, 5, 2, 6, 3, 7
    ]
    for i in cvIndices:
        glVertex3fv(tuple(drawing_globals.cubeVertices[i]))
    glEnd()
    glEndList()

    # Debug Preferences
    from utilities.debug_prefs import debug_pref, Choice_boolean_True
    from utilities.debug_prefs import Choice_boolean_False
    choices = [Choice_boolean_False, Choice_boolean_True]

    # 20060314 grantham
    initial_choice = choices[drawing_globals.allow_color_sorting_default]
    drawing_globals.allow_color_sorting_pref = debug_pref(
        "Use Color Sorting?",
        initial_choice,
        prefs_key=drawing_globals.allow_color_sorting_prefs_key)
    #bruce 060323 removed non_debug = True for A7 release, changed default
    #value to False (far above), and changed its prefs_key so developers
    #start with the new default value.
    #russ 080225: Added.
    initial_choice = choices[drawing_globals.use_color_sorted_dls_default]
    drawing_globals.use_color_sorted_dls_pref = debug_pref(
        "Use Color-sorted Display Lists?",
        initial_choice,
        prefs_key=drawing_globals.use_color_sorted_dls_prefs_key)
    #russ 080225: Added.
    initial_choice = choices[drawing_globals.use_color_sorted_vbos_default]
    drawing_globals.use_color_sorted_vbos_pref = debug_pref(
        "Use Color-sorted Vertex Buffer Objects?",
        initial_choice,
        prefs_key=drawing_globals.use_color_sorted_vbos_prefs_key)

    #russ 080403: Added drawing variant selection
    variants = [
        "0. OpenGL 1.0 - glBegin/glEnd tri-strips vertex-by-vertex.",
        "1. OpenGL 1.1 - glDrawArrays from CPU RAM.",
        "2. OpenGL 1.1 - glDrawElements indexed arrays from CPU RAM.",
        "3. OpenGL 1.5 - glDrawArrays from graphics RAM VBO.",
        "4. OpenGL 1.5 - glDrawElements, verts in VBO, index in CPU.",
        "5. OpenGL 1.5 - VBO/IBO buffered glDrawElements."
    ]
    drawing_globals.use_drawing_variant = debug_pref(
        "GLPane: drawing method",
        Choice(names=variants,
               values=range(len(variants)),
               defaultValue=drawing_globals.use_drawing_variant_default),
        prefs_key=drawing_globals.use_drawing_variant_prefs_key)

    # temporarily always print this, while default setting might be in flux,
    # and to avoid confusion if the two necessary prefs are set differently
    # [bruce 080305]
    if (drawing_globals.allow_color_sorting_pref
            and drawing_globals.use_color_sorted_dls_pref):
        print "\nnote: this session WILL use color sorted display lists"
    else:
        print "\nnote: this session will NOT use color sorted display lists"
    if (drawing_globals.allow_color_sorting_pref
            and drawing_globals.use_color_sorted_vbos_pref):
        print "note: this session WILL use", \
              "color sorted Vertex Buffer Objects\n"
    else:
        print "note: this session will NOT use", \
              "color sorted Vertex Buffer Objects\n"

    # 20060313 grantham Added use_c_renderer debug pref, can
    # take out when C renderer used by default.
    if drawing_globals.quux_module_import_succeeded:
        initial_choice = choices[drawing_globals.use_c_renderer_default]
        drawing_globals.use_c_renderer = (debug_pref(
            "Use native C renderer?",
            initial_choice,
            prefs_key=drawing_globals.use_c_renderer_prefs_key))
        #bruce 060323 removed non_debug = True for A7 release, and changed
        # its prefs_key so developers start over with the default value.

    #initTexture('C:\\Huaicai\\atom\\temp\\newSample.png', 128,128)
    return  # from setup_drawer
Esempio n. 29
0
 def getrotation(self): #bruce 050518 new feature for showing rotation of rmotor in its cap-arrow
     """
     Return a rotation angle for the motor. This is arbitrary, but rotates smoothly
     with the atoms, averaging out their individual thermal motion.
     It is not history-dependent -- e.g. it will be consistent regardless of how you jump around
     among the frames of a movie. But if we ever implement remaking or revising the motor position,
     or if you delete some of the motor's atoms, this angle is forgotten and essentially resets to 0.
     (That could be fixed, and the angle even saved in the mmp file, if desired. See code comments
     for other possible improvements.)
     """
     # possible future enhancements:
     # - might need to preserve rotation when we forget old posns, by setting an arb offset then;
     # - might need to preserve it in mmp file??
     # - might need to draw it into PovRay file??
     # - might need to preserve it when we translate or rotate entire jig with its atoms (doing which is NIM for now)
     # - could improve and generalize alg, and/or have sim do it (see comments below for details).
     #
     posns = A(map( lambda a: a.posn(), self.atoms ))
     posns -= self.center
     if self._initial_posns is None:
         # (we did this after -= center, so no need to forget posns if we translate the entire jig)
         self._initial_posns = posns # note, we're storing *relative* positions, in spite of the name!
         self._initial_quats = None # compute these the first time they're needed (since maybe never needed)
         return 0.0 # returning this now (rather than computing it below) is just an optim, in theory
     assert len(self._initial_posns) == len(posns), "bug in invalidating self._initial_posns when rmotor atoms change"
     if not (self._initial_posns != posns): # have to use not (x != y) rather than (x == y) due to Numeric semantics!
         # no (noticable) change in positions - return quickly
         # (but don't change stored posns, in case this misses tiny changes which could accumulate over time)
         # (we do this before the subsequent stuff, to not waste redraw time when posns don't change;
         #  just re correctness, we could do it at a later stage)
         return 0.0
     # now we know the posns are different, and we have the old ones to compare them to.
     posns = self.norm_project_posns( posns) # this might modify posns object, and might return same or different object
     quats = self._initial_quats
     if quats is None:
         # precompute a quat to rotate new posns into a standard coord system for comparison to old ones
         # (Q args must be orthonormal and right-handed)
         oldposns = + self._initial_posns # don't modify those stored initial posns
             # (though it probably wouldn't matter if we did -- from now on,
             #  they are only compared to None and checked for length, as of 050518)
         oldposns = self.norm_project_posns( oldposns)
         axis = self.axis
         quats = self._initial_quats = [ Q(axis,pos1,cross(axis,pos1)) for pos1 in oldposns ]
     angs = []
     for qq, pos2 in zip( self._initial_quats, posns):
         npos2 = qq.unrot(pos2)
         # now npos2 is in yz plane, and pos1 (if transformed) would just be the y axis in that plane;
         # just get its angle in that plane (defined so that if pos2 = pos1, ie npos2 = (0,1,0), then angle is 0)
         ang = angle(npos2[1], npos2[2]) # in degrees
         angs.append(ang)
     # now average these angles, paying attention to their being on a circle
     # (which means the average of 1 and 359 is 0, not 180!)
     angs.sort()
         # Warning: this sort is only correct since we know they're in the range [0,360] (inclusive range is ok).
         # It might be correct for any range that covers the circle exactly once, e.g. [-180,180]
         # (not fully analyzed for that), but it would definitely be wrong for e.g. [-0.001, 360.001]!
         # So be careful if you change how angle() works.
     angs = A(angs)
     gaps = angs[1:] - angs[:-1]
     gaps = [angs[0] - angs[-1] + 360] + list(gaps)
     i = argmax(gaps)
     ##e Someday we should check whether this largest gap is large enough for this to make sense (>>180);
     # we are treating the angles as "clustered together in the part of the circle other than this gap"
     # and averaging them within that cluster. It would also make sense to discard outliers,
     # but doing this without jittering the rotation angle (as individual points become closer
     # to being outliers) would be challenging. Maybe better to just give up unless gap is, say, >>340.
     ##e Before any of that, just get the sim to do this in a better way -- interpret the complete set of
     # atom motions as approximating some overall translation and rotation, and tell us this, so we can show
     # not only rotation, but axis wobble and misalignment, and so these can be plotted.
     angs = list(angs)
     angs = angs[i:] + angs[:i] # start with the one just after the largest gap
     relang0 = angs[0]
     angs = A(angs) - relang0 # be relative to that, when we average them
     # but let them all be in the range [0,360)!
     angs = (angs + 720) % 360
         # We need to add 720 since Numeric's mod produces negative outputs
         # for negative inputs (unlike Python's native mod, which is correct)!
         # How amazingly ridiculous.
     ang = (sum(angs) / len(angs)) + relang0
     ang = ang % 360 # this is Python mod, so it's safe
     return ang
Esempio n. 30
0
def writemdlfile(part, glpane, filename): #bruce 050927 replaced assy argument with part and glpane args, added docstring
    "write the given part into a new MDL file with the given name, using glpane.displayMode"
    alist = [] #bruce 050325 changed assy.alist to localvar alist
    natoms = 0
    # Specular values keyed by atom color 
    # Only Carbon, Hydrogen and Silicon supported here
    specValues = {(117,117,117):((183, 183, 183), 16, 44), \
                       (256,256,256):((183, 183, 183), 15, 44), \
                       (111,93,133):((187,176,200), 16, 44)}

    # Determine the number of visible atoms in the part.
    # Invisible atoms are drawn.  Hidden atoms are not drawn.
    # This is a bug to be fixed in the future.  Will require work in chunk/chem.writemdl, too.  
    # writepov may have this problem, too.
    # Mark [04-12-05]     
    # To test this, we need to get a copy of Animation Master.
    # Mark [05-01-14]
    for mol in part.molecules: 
        if (not mol.hidden) and (mol.display != diINVISIBLE): natoms += len(mol.atoms) #bruce 050421 disp->display (bugfix?)
#    print "fileIO: natoms =", natoms

    f = open(filename, 'w');
    
    # Write the header
    f.write(mdlheader)
    
    # Write atoms with spline coordinates
    f.write("Splines=%d\n"%(13*natoms))
    part.topnode.writemdl(alist, f, glpane.displayMode)
        #bruce 050421 changed assy.tree to assy.part.topnode to fix an assy/part bug
        #bruce 050927 changed assy.part -> new part arg
    
    # Write the GROUP information
    # Currently, each atom is 
    f.write("[ENDMESH]\n[GROUPS]\n")
    
    atomindex = 0 
    
    for mol in part.molecules:
        col = mol.color # Color of molecule
        for a in mol.atoms.values():
            
            # Begin GROUP record for this atom.
            f.write("[GROUP]\nName=Atom%d\nCount=80\n"%atomindex)
            
            # Write atom mesh IDs
            for j in range(80):
                f.write("%d\n"%(98-j+atomindex*80))

            # Write Pivot record for this atom.
#            print "a.pos = ", a.posn()
            xyz=a.posn()
            n=(float(xyz[0]), float(xyz[1]), float(xyz[2]))
            f.write("Pivot= %f %f %f\n" % n)
            
            # Add DiffuseColor record for this atom.
            color = col or a.element.color
                # if this was color = a.drawing_color() it would mess up the specularity lookup below;
                # could be fixed somehow... [bruce 070417 comment]
            rgb=map(int,A(color)*255) # rgb = 3-tuple of int
            color=(int(rgb[0]), int(rgb[1]), int(rgb[2]))
            f.write("DiffuseColor=%d %d %d\n"%color)

            # Added specularity per John Burch's request
            # Specular values keyed by atom color           
            (specColor, specSize, specIntensity) = \
             specValues.get(color, ((183,183,183),16,44))
            f.write("SpecularColor=%d %d %d\n"%specColor)
            f.write("SpecularSize=%d\n"%specSize)
            f.write("SpecularIntensity=%d\n"%specIntensity)
            
            # End the group for this atom.
            f.write("[ENDGROUP]\n")
            
            atomindex += 1
        
    # ENDGROUPS
    f.write("[ENDGROUPS]\n")

    # Write the footer and close
    fpos = f.tell()
    f.write(mdlfooter)
    f.write("FileInfoPos=%d\n"%fpos)
    f.close()