def close(self): # The 'def __del__(self)' is not guaranteed to be called. # It is not called in my try on Windows. [Huaicai] """ Save color/radius preference before deleting """ prefs = prefs_context() elms = {} for elm in self._periodicTable.values(): elms[elm.symbol] = (elm.rvdw, elm.color) prefs.update(elms)
def _validate_gamess_program(self): """ Private method: Checks that the GAMESS program path exists in the user pref database and that the file it points to exists. If the GAMESS path does not exist, the user will be prompted with the file chooser to select the GAMESS executable. This function does not check whether the GAMESS path is actually GAMESS or if it is the correct version of GAMESS for this platform (i.e. PC GAMESS for Windows). Returns: 0 = Valid 1 = Invalid """ # Get GAMESS executable path from the user preferences prefs = preferences.prefs_context() self.server.program = prefs.get(gmspath_prefs_key) if not self.server.program: msg = "The GAMESS executable path is not set.\n" elif os.path.exists(self.server.program): return 0 else: msg = self.server.program + " does not exist.\n" # GAMESS Prop Dialog is the parent for messagebox and file chooser. parent = self.edit_cntl ret = QMessageBox.warning( parent, "GAMESS Executable Path", msg + "Please select OK to set the location of GAMESS for this computer.", "&OK", "Cancel", "", 0, 1 ) if ret == 0: # OK self.server.program = \ get_filename_and_save_in_prefs(parent, gmspath_prefs_key, "Choose GAMESS Executable") if not self.server.program: return 1 # Cancelled from file chooser. # Enable GAMESS Plug-in. Mark 060112. env.prefs[gamess_enabled_prefs_key] = True elif ret == 1: # Cancel return 1 return 0
def _validate_gamess_program(self): """ Private method: Checks that the GAMESS program path exists in the user pref database and that the file it points to exists. If the GAMESS path does not exist, the user will be prompted with the file chooser to select the GAMESS executable. This function does not check whether the GAMESS path is actually GAMESS or if it is the correct version of GAMESS for this platform (i.e. PC GAMESS for Windows). Returns: 0 = Valid 1 = Invalid """ # Get GAMESS executable path from the user preferences prefs = preferences.prefs_context() self.server.program = prefs.get(gmspath_prefs_key) if not self.server.program: msg = "The GAMESS executable path is not set.\n" elif os.path.exists(self.server.program): return 0 else: msg = self.server.program + " does not exist.\n" # GAMESS Prop Dialog is the parent for messagebox and file chooser. parent = self.edit_cntl ret = QMessageBox.warning( parent, "GAMESS Executable Path", msg + "Please select OK to set the location of GAMESS for this computer.", "&OK", "Cancel", "", 0, 1) if ret == 0: # OK self.server.program = \ get_filename_and_save_in_prefs(parent, gmspath_prefs_key, "Choose GAMESS Executable") if not self.server.program: return 1 # Cancelled from file chooser. # Enable GAMESS Plug-in. Mark 060112. env.prefs[gamess_enabled_prefs_key] = True elif ret == 1: # Cancel return 1 return 0
def loadLighting(self, gl_update=True): """ load new lighting values from the standard preferences database, if possible; if correct values were loaded, start using them, and do gl_update unless option for that is False; return True if you loaded new values, False if that failed """ try: prefs = preferences.prefs_context() key = glpane_lights_prefs_key try: val = prefs[key] except KeyError: # none were saved; not an error and not even worthy of a message # since this is called on startup and it's common for nothing to be saved. # Return with no changes. return False # At this point, you have a saved prefs val, and if this is wrong it's an error. # val format is described (partly implicitly) in saveLighting method. res = [ ] # will become new argument to pass to self.setLighting method, if we succeed for name in ['light0', 'light1', 'light2']: params = val[ name] # a dict of ambient, diffuse, specular, x, y, z, enabled color = params['color'] # light color (r,g,b) a = params['ambient_intensity'] # ambient intensity d = params['diffuse_intensity'] # diffuse intensity s = params['specular_intensity'] # specular intensity x = params['xpos'] # X position y = params['ypos'] # Y position z = params['zpos'] # Z position e = params['enabled'] # boolean res.append((color, a, d, s, x, y, z, e)) self.setLighting(res, gl_update=gl_update) if _DEBUG_LIGHTING: print "_DEBUG_LIGHTING: fyi: Lighting preferences loaded" return True except: print_compact_traceback( "bug: exception in loadLighting (current prefs not altered): ") #e redmsg? return False pass
def saveLighting(self): """ save the current lighting values in the standard preferences database """ try: prefs = preferences.prefs_context() key = glpane_lights_prefs_key # we'll store everything in a single value at this key, # making it a dict of dicts so it's easy to add more lighting attrs (or lights) later # in an upward-compatible way. # [update, bruce 051206: it turned out that when we added lots of info it became # not upward compatible, causing bug 1181 and making the only practical fix of that bug # a change in this prefs key. In successive commits I moved this key to prefs_constants, # then renamed it (variable and key string) to try to fix bug 1181. I would also like to find out # what's up with our two redundant storings of light color in prefs db, ###@@@ # but I think bug 1181 can be fixed safely this way without my understanding that.] (((r0,g0,b0),a0,d0,s0,x0,y0,z0,e0), \ ( (r1,g1,b1),a1,d1,s1,x1,y1,z1,e1), \ ( (r2,g2,b2),a2,d2,s2,x2,y2,z2,e2)) = self._lights # now process it in a cleaner way val = {} for (i, (c, a, d, s, x, y, z, e)) in zip(range(3), self._lights): name = "light%d" % i params = dict( color = c, \ ambient_intensity = a, \ diffuse_intensity = d, \ specular_intensity = s, \ xpos = x, ypos = y, zpos = z, \ enabled = e ) val[name] = params # save the prefs to the database file prefs[key] = val # This was printing many redundant messages since this method is called # many times while changing lighting parameters in the Preferences | Lighting dialog. # Mark 051125. #env.history.message( greenmsg( "Lighting preferences saved" )) except: print_compact_traceback( "bug: exception in saveLighting (pref changes not saved): ") #e redmsg? return
def saveLighting(self): """ save the current lighting values in the standard preferences database """ try: prefs = preferences.prefs_context() key = glpane_lights_prefs_key # we'll store everything in a single value at this key, # making it a dict of dicts so it's easy to add more lighting attrs (or lights) later # in an upward-compatible way. # [update, bruce 051206: it turned out that when we added lots of info it became # not upward compatible, causing bug 1181 and making the only practical fix of that bug # a change in this prefs key. In successive commits I moved this key to prefs_constants, # then renamed it (variable and key string) to try to fix bug 1181. I would also like to find out # what's up with our two redundant storings of light color in prefs db, ###@@@ # but I think bug 1181 can be fixed safely this way without my understanding that.] (((r0,g0,b0),a0,d0,s0,x0,y0,z0,e0), \ ( (r1,g1,b1),a1,d1,s1,x1,y1,z1,e1), \ ( (r2,g2,b2),a2,d2,s2,x2,y2,z2,e2)) = self._lights # now process it in a cleaner way val = {} for (i, (c,a,d,s,x,y,z,e)) in zip(range(3), self._lights): name = "light%d" % i params = dict( color = c, \ ambient_intensity = a, \ diffuse_intensity = d, \ specular_intensity = s, \ xpos = x, ypos = y, zpos = z, \ enabled = e ) val[name] = params # save the prefs to the database file prefs[key] = val # This was printing many redundant messages since this method is called # many times while changing lighting parameters in the Preferences | Lighting dialog. # Mark 051125. #env.history.message( greenmsg( "Lighting preferences saved" )) except: print_compact_traceback("bug: exception in saveLighting (pref changes not saved): ") #e redmsg? return
def loadLighting(self, gl_update = True): """ load new lighting values from the standard preferences database, if possible; if correct values were loaded, start using them, and do gl_update unless option for that is False; return True if you loaded new values, False if that failed """ try: prefs = preferences.prefs_context() key = glpane_lights_prefs_key try: val = prefs[key] except KeyError: # none were saved; not an error and not even worthy of a message # since this is called on startup and it's common for nothing to be saved. # Return with no changes. return False # At this point, you have a saved prefs val, and if this is wrong it's an error. # val format is described (partly implicitly) in saveLighting method. res = [] # will become new argument to pass to self.setLighting method, if we succeed for name in ['light0','light1','light2']: params = val[name] # a dict of ambient, diffuse, specular, x, y, z, enabled color = params['color'] # light color (r,g,b) a = params['ambient_intensity'] # ambient intensity d = params['diffuse_intensity'] # diffuse intensity s = params['specular_intensity'] # specular intensity x = params['xpos'] # X position y = params['ypos'] # Y position z = params['zpos'] # Z position e = params['enabled'] # boolean res.append( (color,a,d,s,x,y,z,e) ) self.setLighting( res, gl_update = gl_update) if _DEBUG_LIGHTING: print "_DEBUG_LIGHTING: fyi: Lighting preferences loaded" return True except: print_compact_traceback("bug: exception in loadLighting (current prefs not altered): ") #e redmsg? return False pass
def get_job_manager_job_id_and_dir(): """ Returns a unique Job Id number and JobManager subdirectory for this Job Id. The Job Id is stored in the User Preference db. """ from foundation.preferences import prefs_context prefs = prefs_context() job_id = prefs.get('JobId') if not job_id: job_id = 100 # Start with Job Id 100 ##Temporarily comment out by Huaicai 6/22/05 #else: # job_id += 1 # Increment the Job Id # Get the Job Manager directory from platform_dependent.PlatformDependent import find_or_make_Nanorex_subdir jobdir = find_or_make_Nanorex_subdir('JobManager') while 1: # Create Job Id subdir (i.e. ~/Nanorex/JobManager/123/) job_id_dir = os.path.join(jobdir, str(job_id)) # Make sure there isn't already a Job Id subdir in ~/Nanorex/JobManager/ if os.path.exists(job_id_dir): job_id += 1 # It is there, so increment the Job Id and try again. else: from utilities.debug import print_compact_traceback try: os.mkdir(job_id_dir) except: print_compact_traceback( "exception in creating directory: \"%s\"" % job_id_dir) return -1, 0 prefs['JobId'] = 100 #job_id # Save the most recent Job Id touch_job_id_status_file(job_id, 'Queued') return str(job_id), job_id_dir
def get_job_manager_job_id_and_dir(): """ Returns a unique Job Id number and JobManager subdirectory for this Job Id. The Job Id is stored in the User Preference db. """ from foundation.preferences import prefs_context prefs = prefs_context() job_id = prefs.get('JobId') if not job_id: job_id = 100 # Start with Job Id 100 ##Temporarily comment out by Huaicai 6/22/05 #else: # job_id += 1 # Increment the Job Id # Get the Job Manager directory from platform_dependent.PlatformDependent import find_or_make_Nanorex_subdir jobdir = find_or_make_Nanorex_subdir('JobManager') while 1: # Create Job Id subdir (i.e. ~/Nanorex/JobManager/123/) job_id_dir = os.path.join(jobdir, str(job_id)) # Make sure there isn't already a Job Id subdir in ~/Nanorex/JobManager/ if os.path.exists(job_id_dir): job_id += 1 # It is there, so increment the Job Id and try again. else: from utilities.debug import print_compact_traceback try: os.mkdir(job_id_dir) except: print_compact_traceback("exception in creating directory: \"%s\"" % job_id_dir) return -1, 0 prefs['JobId'] = 100#job_id # Save the most recent Job Id touch_job_id_status_file(job_id, 'Queued') return str(job_id), job_id_dir
def addElements(self, elmTable, _defaultRad_Color, _altRad_Color, directional_bond_elements=(), default_options={}): #bruce 071105 modified from def _createElements(self, elmTable): """ Create elements for all members of <elmTable> (list of tuples). (Ok to call this more than once for non-overlapping elmTables (unique element symbols). Use preference value for radius and color of each element, if available (using element symbol as prefs key); otherwise, use values from _defaultRad_Color dictionary, which must have values for all element symbols in elmTable. (Make sure it has the value, even if we don't need it due to prefs.) Also store all values in _defaultRad_Color, _altRad_Color tables for later use by loadDefaults or loadAlternates methods. @param elmTable: a list of elements to create, as tuples of a format documented in elements_data.py. @param _defaultRad_Color: a dictionary of radius, color pairs, indexed by element symbol. Must be complete. Used now when not overridden by prefs. Stored for optional later use by loadDefaults. @param _altRad_Color: an alternate dictionary of radius, color pairs. Need not be complete; missing entries are effectively taken from _defaultRad_Color. Stored for optional later use by loadAlternates. @param directional_bond_elements: a list of elements in elmTable which support directional bonds. """ prefs = prefs_context() symbols = {} for elm in elmTable: options = dict(default_options) assert len(elm) in (5, 6) if len(elm) >= 6: options.update(elm[5]) symbols[elm[0]] = 1 # record element symbols seen in this call rad_color = prefs.get(elm[0], _defaultRad_Color[elm[0]]) el = Elem(elm[2], elm[0], elm[1], elm[3], rad_color[0], rad_color[1], elm[4], **options) assert not self._periodicTable.has_key(el.eltnum), \ "duplicate def of element number %r (prior: %r)" % \ (el.eltnum, self._periodicTable[el.eltnum] ) assert not self._eltName2Num.has_key(el.name), \ "duplicate def of element name %r" % (el.name,) assert not self._eltSym2Num.has_key(el.symbol), \ "duplicate def of element symbol %r" % (el.symbol,) self._periodicTable[el.eltnum] = el self._eltName2Num[el.name] = el.eltnum self._eltSym2Num[el.symbol] = el.eltnum if elm[0] in directional_bond_elements: #bruce 071015 # TODO: put this in the options field? or infer it from # pam and role? el.bonds_can_be_directional = True assert el.bonds_can_be_directional == (el.symbol == 'X' or el.role == 'strand') # once this works, we can clean up the code to not hardcode those list args # [bruce 080117] for key in _defaultRad_Color.iterkeys(): assert key in symbols for key in _altRad_Color.iterkeys(): assert key in symbols self._defaultRad_Color.update(_defaultRad_Color) self._altRad_Color.update(_altRad_Color) return
def draw_vane( bond, a1p, a2p, ord_pi, rad, col ): """ Draw a vane (extending on two opposite sides of the bond axis) [#doc more]; use ord_pi to determine how intense it is (not yet sure how, maybe by mixing in bgcolor??); a1p and a2p should be unit vectors perp to bond and no more than 90 degrees apart when seen along it; they should be in the bond's coordinate system. rad is inner radius of vanes, typically the cylinder radius for the sigma bond graphic. If col is not boolean false, use it as the vane color; otherwise, use a constant color which might be influenced by the pi orbital occupancy. """ from utilities.debug_prefs import debug_pref from utilities.debug_prefs import Choice_boolean_True, Choice_boolean_False ## twisted = debug_pref('pi vanes/ribbons', Choice_boolean_False) # one of ['multicyl','vane','ribbon'] pi_bond_style = env.prefs[ pibondStyle_prefs_key] twisted = (pi_bond_style == 'ribbon') poles = debug_pref('pi vanes/poles', Choice_boolean_True) draw_outer_edges = debug_pref('pi vanes/draw edges', Choice_boolean_True) #bruce 050730 new feature, so that edge-on vanes are still visible draw_normals = debug_pref('pi vanes/draw normals', Choice_boolean_False) print_vane_params = debug_pref('pi vanes/print params', Choice_boolean_False) if print_vane_params: print "draw vane",a1p,vlen(a1p),a2p,vlen(a2p),ord_pi if twisted: d12 = dot(a1p, a2p) ## assert d12 >= 0.0 if d12 < 0.0: d12 = 0.0 if d12 > 1.0: d12 = 1.0 twist = math.acos(d12) # in radians # this is numerically inaccurate (since d12 is) near d12 == 1.0, but # that's ok, since it's only compared to threshholds (by ceil()) # which correspond to d12 values not near 1.0. # (#e btw, we could optim the common case (ntwists == 1) by # inverting this comparison to get the equivalent threshhold for # d12.) maxtwist = MAXTWIST # debug_pref doesn't yet have a PrefsType for this # number of segments needed, to limit each segment's twist to MAXTWIST ntwists = max(1, int( math.ceil( twist / maxtwist ) )) pass if col: color = col else: #bruce 050804: initial test of bond color prefs; inadequate in several # ways #######@@@@@@@ from foundation.preferences import prefs_context prefs = prefs_context() from utilities.prefs_constants import bondVaneColor_prefs_key #k I hope this color tuple of floats is in the correct prefs format color = prefs.get(bondVaneColor_prefs_key) # protect following code from color being None (which causes bus error, # maybe in PyOpenGL) assert len(color) == 3 #####@@@@@ it would be much faster to update this pref (or almost any # graphics color pref) if the OpenGL command to set the color was in its # own display list, redefined when the redraw starts, and called from # inside every other display list that needs it. Then when you changed # it, gl_update would be enough -- the chunk display lists would not # need to be remade. ###@@@ problems include: # - being fast enough # + dflt should be specified in just one place, and earlier than in this # place, so it can show up in prefs ui before this runs (in fact, even # earlier than when this module is first imported, which might be only # when needed), # - existing prefs don't have all the color vars needed (eg no # toolong-highlighted color) # - being able to track them only when finally used, not when pulled # into convenience vars before final use -- this might even be an # issue if we precompute a formula from a color-pref, but only count # it as used if that result is used. (we could decide to track the # formula res as a separate thing, i suppose) ## a1pos = bond.atom1.posn() ## a2pos = bond.atom2.posn() a1pos, c1, center, c2, a2pos, toolong = bond.geom if not toolong: c1 = c2 = center # don't know if needed x_axis = a2pos - a1pos # from 1 to 2.5, with 1 moved out to shrink width according to ord_pi width = 1.5 * ord_pi inner, outer = 2.5 - width, 2.5 radius_pairs = [(outer, inner, outer), (-inner, -outer, -outer)] # the order within each pair matters, since it affects the polys drawn # below being CW or CCW! but for purposes of edges, we also have to # know which one is *really* outer... thus the third elt (edge_outer) # of the tuple. # OpenGL code #e could optim this to use Numeric calcs and OpenGL vertex array, with # vertex normals and smooth shading, maybe even color ramp of some kind... #e want polygon outlines? #e want a 1d texture to emphasize the vane's ribbonness & help show ord_pi? glDisable(GL_CULL_FACE) #bruce 051215 use apply_material(color) instead of glMaterialfv, partly to # prevent bug of passing 3-tuple to glMaterialfv, partly to make the vanes # appear specular, partly to thereby fix bug 1216 (which was probably caused # by not setting specular color here, thus letting it remain from whatever # happened to be drawn just before). If we decide vanes should *not* appear # specular, we have to actively turn off specular color here rather than # ignoring the issue. One way would be to create and use some new # functionality like apply_material(color, specular=False). apply_material(color) # gl args partly guessed #e should add specularity, shininess... ## glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color) glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE) # For vane lighting to be correct, two-sided polygon lighting is # required, and every polygon's vertex winding order (CCW) has to match # its normal vector, as both are produced by the following code. This # means that any changes to the orders of lists of vertices or vectors # in this code have to be considered carefully. But it's ok if a1p and # a2p are negated (fortunately, since calling code arbitrarily negates # them), since this reverses the poly scan directions in two ways at # once (e.g. via pvec and perpvec in the quads case below). (For ribbon # option, it's only ok if they're negated together; for quads case, # independent negation is ok. I'm pretty sure calling code only negates # them together.) [bruce 050725] for outer, inner, edge_outer in radius_pairs: normals = [] edgeverts = [] if twisted: glBegin(GL_TRIANGLE_STRIP) for i in range(1+ntwists): t = float(i) / ntwists # 0.0 to 1.0 # one minus t (python syntax doesn't let me name it just 1mt) _1mt = 1.0 - t #e could optimize, not sure it matters axispos = _1mt * a1pos + t * a2pos # let it get smaller in the middle for large twist (rather than # using angles in calc) pvec = _1mt * a1p + t * a2p # (rationale: shows weakness of bond. real reason: programmer is # in a hurry.) #e btw, it might be better to show twist by mismatch of # larger rectangular vanes, in the middle; and it's faster # to draw. # Could also "* ord_pi" rather than using color, if we worked # out proper min radius. ## pvec *= (rad * 2.5) perpvec = norm(cross(x_axis, pvec)) # test shows this is needed not only for smoothness, but to make # the lighting work at all glNormal3fv( perpvec) outervert = axispos + pvec * rad * outer innervert = axispos + pvec * rad * inner glVertex3fv( outervert) ## not needed, since the same normal as above ## glNormal3fv( perpvec) glVertex3fv( innervert) #e color? want to smooth-shade it using atom colors, or the # blue/gray for bond order, gray in center? if draw_normals: normals.append(( axispos + pvec * rad * edge_outer, perpvec )) if draw_outer_edges: edgeverts.append( axispos + pvec * rad * edge_outer ) glEnd() else: glBegin(GL_QUADS) for axispos, axispos_c, pvec, normalfactor in \ [(a1pos,c1,a1p,-1), (a2pos,c2,a2p,1)]: perpvec = norm(cross(x_axis, pvec)) * normalfactor glNormal3fv( perpvec) glVertex3fv( axispos + pvec * rad * inner) glVertex3fv( axispos + pvec * rad * outer) glVertex3fv( axispos_c + pvec * rad * outer) # This (axispos_c + pvec * rad * outer) would be the corner # we connect by a line, even when not draw_outer_edges, if # outer == edge_outer -- but it might or might not be. glVertex3fv( axispos_c + pvec * rad * inner) if draw_normals: normals.append(( axispos/2.0 + axispos_c/2.0 + pvec * rad * edge_outer, perpvec )) if draw_outer_edges: # Kluge to reverse order of first loop body but not second. edgeverts.reverse() edgeverts.append( axispos_c + pvec * rad * edge_outer) edgeverts.append( axispos + pvec * rad * edge_outer) else: # At least connect the halves of each vane, so that twist # doesn't make them look like 2 vanes. edgeverts.append( axispos_c + pvec * rad * edge_outer) glEnd() ## glBegin(GL_LINES) ## glColor3fv(color) ## for axispos, axispos_c, pvec in [(a1pos,c1,a1p), (a2pos,c2,a2p)]: ## glVertex3fv( axispos_c + pvec * rad * outer) ## glEnd() glDisable(GL_LIGHTING) # for lines... don't know if this matters if poles: glBegin(GL_LINES) glColor3fv(color) for axispos, pvec in [(a1pos,a1p), (a2pos,a2p)]: glVertex3fv( axispos + pvec * rad * outer) glVertex3fv( axispos + pvec * rad * -outer) glEnd() if normals: glBegin(GL_LINES) glColor3fv(white) for base, vec in normals: glVertex3fv(base) glVertex3fv(base + vec) glEnd() if edgeverts: glBegin(GL_LINE_STRIP) glColor3fv(color) for vert in edgeverts: glVertex3fv(vert) glEnd() glEnable(GL_LIGHTING) glEnable(GL_CULL_FACE) glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE) return
def draw_vane( bond, a1p, a2p, ord_pi, rad, col ): """ Draw a vane (extending on two opposite sides of the bond axis) [#doc more]; use ord_pi to determine how intense it is (not yet sure how, maybe by mixing in bgcolor??); a1p and a2p should be unit vectors perp to bond and no more than 90 degrees apart when seen along it; they should be in the bond's coordinate system. rad is inner radius of vanes, typically the cylinder radius for the sigma bond graphic. If col is not boolean false, use it as the vane color; otherwise, use a constant color which might be influenced by the pi orbital occupancy. """ from utilities.debug_prefs import debug_pref from utilities.debug_prefs import Choice_boolean_True, Choice_boolean_False ## twisted = debug_pref('pi vanes/ribbons', Choice_boolean_False) # one of ['multicyl','vane','ribbon'] pi_bond_style = env.prefs[ pibondStyle_prefs_key] twisted = (pi_bond_style == 'ribbon') poles = debug_pref('pi vanes/poles', Choice_boolean_True) draw_outer_edges = debug_pref('pi vanes/draw edges', Choice_boolean_True) #bruce 050730 new feature, so that edge-on vanes are still visible draw_normals = debug_pref('pi vanes/draw normals', Choice_boolean_False) print_vane_params = debug_pref('pi vanes/print params', Choice_boolean_False) if print_vane_params: print "draw vane",a1p,vlen(a1p),a2p,vlen(a2p),ord_pi if twisted: d12 = dot(a1p, a2p) ## assert d12 >= 0.0 if d12 < 0.0: d12 = 0.0 if d12 > 1.0: d12 = 1.0 twist = math.acos(d12) # in radians # this is numerically inaccurate (since d12 is) near d12 == 1.0, but # that's ok, since it's only compared to threshholds (by ceil()) # which correspond to d12 values not near 1.0. # (#e btw, we could optim the common case (ntwists == 1) by # inverting this comparison to get the equivalent threshhold for # d12.) maxtwist = MAXTWIST # debug_pref doesn't yet have a PrefsType for this # number of segments needed, to limit each segment's twist to MAXTWIST ntwists = max(1, int( math.ceil( twist / maxtwist ) )) pass if col: color = col else: #bruce 050804: initial test of bond color prefs; inadequate in several # ways ###@@@ from foundation.preferences import prefs_context prefs = prefs_context() from utilities.prefs_constants import bondVaneColor_prefs_key #k I hope this color tuple of floats is in the correct prefs format color = prefs.get(bondVaneColor_prefs_key) # protect following code from color being None (which causes bus error, # maybe in PyOpenGL) assert len(color) == 3 ###@@@ it would be much faster to update this pref (or almost any # graphics color pref) if the OpenGL command to set the color was in its # own display list, redefined when the redraw starts, and called from # inside every other display list that needs it. Then when you changed # it, gl_update would be enough -- the chunk display lists would not # need to be remade. ###@@@ problems include: # - being fast enough # + dflt should be specified in just one place, and earlier than in this # place, so it can show up in prefs ui before this runs (in fact, even # earlier than when this module is first imported, which might be only # when needed), # - existing prefs don't have all the color vars needed (eg no # toolong-highlighted color) # - being able to track them only when finally used, not when pulled # into convenience vars before final use -- this might even be an # issue if we precompute a formula from a color-pref, but only count # it as used if that result is used. (we could decide to track the # formula res as a separate thing, i suppose) ## a1pos = bond.atom1.posn() ## a2pos = bond.atom2.posn() a1pos, c1, center, c2, a2pos, toolong = bond.geom if not toolong: c1 = c2 = center # don't know if needed x_axis = a2pos - a1pos # from 1 to 2.5, with 1 moved out to shrink width according to ord_pi width = 1.5 * ord_pi inner, outer = 2.5 - width, 2.5 radius_pairs = [(outer, inner, outer), (-inner, -outer, -outer)] # the order within each pair matters, since it affects the polys drawn # below being CW or CCW! but for purposes of edges, we also have to # know which one is *really* outer... thus the third elt (edge_outer) # of the tuple. # OpenGL code #e could optim this to use Numeric calcs and OpenGL vertex array, with # vertex normals and smooth shading, maybe even color ramp of some kind... #e want polygon outlines? #e want a 1d texture to emphasize the vane's ribbonness & help show ord_pi? glDisable(GL_CULL_FACE) #bruce 051215 use apply_material(color) instead of glMaterialfv, partly to # prevent bug of passing 3-tuple to glMaterialfv, partly to make the vanes # appear specular, partly to thereby fix bug 1216 (which was probably caused # by not setting specular color here, thus letting it remain from whatever # happened to be drawn just before). If we decide vanes should *not* appear # specular, we have to actively turn off specular color here rather than # ignoring the issue. One way would be to create and use some new # functionality like apply_material(color, specular=False). apply_material(color) # gl args partly guessed #e should add specularity, shininess... ## glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color) glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE) # For vane lighting to be correct, two-sided polygon lighting is # required, and every polygon's vertex winding order (CCW) has to match # its normal vector, as both are produced by the following code. This # means that any changes to the orders of lists of vertices or vectors # in this code have to be considered carefully. But it's ok if a1p and # a2p are negated (fortunately, since calling code arbitrarily negates # them), since this reverses the poly scan directions in two ways at # once (e.g. via pvec and perpvec in the quads case below). (For ribbon # option, it's only ok if they're negated together; for quads case, # independent negation is ok. I'm pretty sure calling code only negates # them together.) [bruce 050725] for outer, inner, edge_outer in radius_pairs: normals = [] edgeverts = [] if twisted: glBegin(GL_TRIANGLE_STRIP) for i in range(1+ntwists): t = float(i) / ntwists # 0.0 to 1.0 # one minus t (python syntax doesn't let me name it just 1mt) _1mt = 1.0 - t #e could optimize, not sure it matters axispos = _1mt * a1pos + t * a2pos # let it get smaller in the middle for large twist (rather than # using angles in calc) pvec = _1mt * a1p + t * a2p # (rationale: shows weakness of bond. real reason: programmer is # in a hurry.) #e btw, it might be better to show twist by mismatch of # larger rectangular vanes, in the middle; and it's faster # to draw. # Could also "* ord_pi" rather than using color, if we worked # out proper min radius. ## pvec *= (rad * 2.5) perpvec = norm(cross(x_axis, pvec)) # test shows this is needed not only for smoothness, but to make # the lighting work at all glNormal3fv( perpvec) outervert = axispos + pvec * rad * outer innervert = axispos + pvec * rad * inner glVertex3fv( outervert) ## not needed, since the same normal as above ## glNormal3fv( perpvec) glVertex3fv( innervert) #e color? want to smooth-shade it using atom colors, or the # blue/gray for bond order, gray in center? if draw_normals: normals.append(( axispos + pvec * rad * edge_outer, perpvec )) if draw_outer_edges: edgeverts.append( axispos + pvec * rad * edge_outer ) glEnd() else: glBegin(GL_QUADS) for axispos, axispos_c, pvec, normalfactor in \ [(a1pos,c1,a1p,-1), (a2pos,c2,a2p,1)]: perpvec = norm(cross(x_axis, pvec)) * normalfactor glNormal3fv( perpvec) glVertex3fv( axispos + pvec * rad * inner) glVertex3fv( axispos + pvec * rad * outer) glVertex3fv( axispos_c + pvec * rad * outer) # This (axispos_c + pvec * rad * outer) would be the corner # we connect by a line, even when not draw_outer_edges, if # outer == edge_outer -- but it might or might not be. glVertex3fv( axispos_c + pvec * rad * inner) if draw_normals: normals.append(( axispos/2.0 + axispos_c/2.0 + pvec * rad * edge_outer, perpvec )) if draw_outer_edges: # Kluge to reverse order of first loop body but not second. edgeverts.reverse() edgeverts.append( axispos_c + pvec * rad * edge_outer) edgeverts.append( axispos + pvec * rad * edge_outer) else: # At least connect the halves of each vane, so that twist # doesn't make them look like 2 vanes. edgeverts.append( axispos_c + pvec * rad * edge_outer) glEnd() ## glBegin(GL_LINES) ## glColor3fv(color) ## for axispos, axispos_c, pvec in [(a1pos,c1,a1p), (a2pos,c2,a2p)]: ## glVertex3fv( axispos_c + pvec * rad * outer) ## glEnd() glDisable(GL_LIGHTING) # for lines... don't know if this matters if poles: glBegin(GL_LINES) glColor3fv(color) for axispos, pvec in [(a1pos,a1p), (a2pos,a2p)]: glVertex3fv( axispos + pvec * rad * outer) glVertex3fv( axispos + pvec * rad * -outer) glEnd() if normals: glBegin(GL_LINES) glColor3fv(white) for base, vec in normals: glVertex3fv(base) glVertex3fv(base + vec) glEnd() if edgeverts: glBegin(GL_LINE_STRIP) glColor3fv(color) for vert in edgeverts: glVertex3fv(vert) glEnd() glEnable(GL_LIGHTING) glEnable(GL_CULL_FACE) glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE) return
def addElements(self, elmTable, _defaultRad_Color, _altRad_Color, directional_bond_elements = (), default_options = {} ): #bruce 071105 modified from def _createElements(self, elmTable): """ Create elements for all members of <elmTable> (list of tuples). (Ok to call this more than once for non-overlapping elmTables (unique element symbols). Use preference value for radius and color of each element, if available (using element symbol as prefs key); otherwise, use values from _defaultRad_Color dictionary, which must have values for all element symbols in elmTable. (Make sure it has the value, even if we don't need it due to prefs.) Also store all values in _defaultRad_Color, _altRad_Color tables for later use by loadDefaults or loadAlternates methods. @param elmTable: a list of elements to create, as tuples of a format documented in elements_data.py. @param _defaultRad_Color: a dictionary of radius, color pairs, indexed by element symbol. Must be complete. Used now when not overridden by prefs. Stored for optional later use by loadDefaults. @param _altRad_Color: an alternate dictionary of radius, color pairs. Need not be complete; missing entries are effectively taken from _defaultRad_Color. Stored for optional later use by loadAlternates. @param directional_bond_elements: a list of elements in elmTable which support directional bonds. """ prefs = prefs_context() symbols = {} for elm in elmTable: options = dict(default_options) assert len(elm) in (5, 6) if len(elm) >= 6: options.update(elm[5]) symbols[elm[0]] = 1 # record element symbols seen in this call rad_color = prefs.get(elm[0], _defaultRad_Color[elm[0]]) el = Elem(elm[2], elm[0], elm[1], elm[3], rad_color[0], rad_color[1], elm[4], ** options) assert not self._periodicTable.has_key(el.eltnum), \ "duplicate def of element number %r (prior: %r)" % \ (el.eltnum, self._periodicTable[el.eltnum] ) assert not self._eltName2Num.has_key(el.name), \ "duplicate def of element name %r" % (el.name,) assert not self._eltSym2Num.has_key(el.symbol), \ "duplicate def of element symbol %r" % (el.symbol,) self._periodicTable[el.eltnum] = el self._eltName2Num[el.name] = el.eltnum self._eltSym2Num[el.symbol] = el.eltnum if elm[0] in directional_bond_elements: #bruce 071015 # TODO: put this in the options field? or infer it from # pam and role? el.bonds_can_be_directional = True assert el.bonds_can_be_directional == (el.symbol == 'X' or el.role == 'strand') # once this works, we can clean up the code to not hardcode those list args # [bruce 080117] for key in _defaultRad_Color.iterkeys(): assert key in symbols for key in _altRad_Color.iterkeys(): assert key in symbols self._defaultRad_Color.update(_defaultRad_Color) self._altRad_Color.update(_altRad_Color) return