# Copyright 2004-2009 Nanorex, Inc. See LICENSE file for details. """ CrystalShape.py -- handle freehand curves for crystal-cutting (?) @author: Huaicai, maybe others @version: $Id$ @copyright: 2004-2009 Nanorex, Inc. See LICENSE file for details. History: bruce 071215 split class CrystalShape out of shape.py into its own module. Module classification: Contains graphics_behavior and operations and perhaps internal transient model code, all to help the graphics_mode (and command?) for Build Crystal (presently an unsplit_mode, BuildCrystal_Command). So the overall classification is not clear -- for now say "command" since nothing less does all the above. But it'll end up in a package for Build Crystal, so this might be ok. [bruce 071215] Note about cleaning up how this uses ColorSortedDisplayList [bruce 090114]: * it allocates one, and sometimes draws it in the usual ColorSorter.start/finish manner, other times uses glCallList on its .dl directly, and other times directly compiles its own OpenGL code into its .dl member. * part of this could be converted into CSDL.draw() calls, but the direct compiling of our own OpenGL code into a display list kept in the CSDL is not a formally supported use of the CSDL, and it won't necessarily keep working with CSDL.draw() (once we're using batched shader primitives for any primitives we draw here). Either we should add that kind of feature to the CSDL API (let any CSDL contain one or more optional "extra display lists for arbitrary outside use, to be drawn whenever that CSDL is drawn"), or fix this in some other way. * Until then, this code may stop drawing properly when batched shader primitives are fully implemented, and its use of .dl may become the only reason we need to keep that member around in CSDL. """ from Numeric import dot, floor from geometry.VQT import vlen, V from OpenGL.GL import glNewList, glEndList, glCallList from OpenGL.GL import GL_COMPILE_AND_EXECUTE from graphics.drawing.drawers import drawCircle from graphics.drawing.drawers import genDiam from graphics.drawing.CS_draw_primitives import drawcylinder from graphics.drawing.CS_draw_primitives import drawsphere from graphics.drawing.CS_draw_primitives import drawline from graphics.drawing.ColorSorter import ColorSorter from graphics.drawing.ColorSortedDisplayList import ColorSortedDisplayList from utilities.constants import SUBTRACT_FROM_SELECTION from utilities.constants import OUTSIDE_SUBTRACT_FROM_SELECTION from utilities.constants import ADD_TO_SELECTION from utilities.constants import START_NEW_SELECTION from utilities.constants import white from utilities.debug import print_compact_traceback from geometry.BoundingBox import BBox from graphics.behaviors.shape import simple_shape_2d from graphics.behaviors.shape import get_selCurve_color from graphics.behaviors.shape import shape from model.bonds import bond_atoms # == class _Circle(simple_shape_2d): """ Represents the area of a circle ortho projection intersecting with a slab. """ def __init__(self, shp, ptlist, origin, selSense, **opts): """ ptlist: the circle center and a point on the perimeter """ simple_shape_2d.__init__( self, shp, ptlist, origin, selSense, opts) def draw(self): """ the profile circle draw """ color = get_selCurve_color(self.selSense) drawCircle(color, self.ptlist[0], self.rad, self.slab.normal) def isin(self, pt): """ Test if a point is in the area """ if self.slab and not self.slab.isin(pt): return False p2d = self.project_2d(pt) dist = vlen(p2d - self.cirCenter) if dist <= self.rad : return True else: return False def _computeBBox(self): """ Construct the 3D bounding box for this volume. """ self.rad = vlen(self.ptlist[1] - self.ptlist[0]) self.cirCenter = self.project_2d(self.ptlist[0]) bbhi = self.cirCenter + V(self.rad, self.rad) bblo = self.cirCenter - V(self.rad, self.rad) x, y = self.right, self.up self.bbox = BBox(V(bblo, bbhi), V(x, y), self.slab) pass # == class CrystalShape(shape): """ This class is used to create cookies. It supports multiple parallel layers, each curve sits on a particular layer. """ def __init__(self, right, up, normal, mode, latticeType): shape.__init__(self, right, up, normal) # Each element is a dictionary object storing "carbon" info for a layer self.carbonPosDict = {} self.hedroPosDict = {} self.markedAtoms = {} # Each element is a dictionary for the bonds info for a layer self.bondLayers = {} self.displist = ColorSortedDisplayList() self.havelist = 0 self.dispMode = mode self.latticeType = latticeType self.layerThickness = {} self.layeredCurves = {} # A list of (merged bb, curves) for each layer def pushdown(self, lastLayer): """ Put down one layer from last layer """ th, n = self.layerThickness[lastLayer] #print "th, n", th, n return th * n def _saveMaxThickness(self, layer, thickness, normal): if layer not in self.layerThickness: self.layerThickness[layer] = (thickness, normal) elif thickness > self.layerThickness[layer][0]: self.layerThickness[layer] = (thickness, normal) def isin(self, pt, curves = None): """ returns 1 if is properly enclosed by the curves. """ #& To do: docstring needs to be updated. mark 060211. # bruce 041214 comment: this might be a good place to exclude points # which are too close to the screen to be drawn. Not sure if this # place would be sufficient (other methods call c.isin too). # Not done yet. ###e val = 0 if not curves: curves = self.curves for c in curves: if c.selSense == START_NEW_SELECTION or c.selSense == ADD_TO_SELECTION: val = val or c.isin(pt) elif c.selSense == OUTSIDE_SUBTRACT_FROM_SELECTION: val = val and c.isin(pt) elif c.selSense == SUBTRACT_FROM_SELECTION: val = val and not c.isin(pt) return val def pickCircle(self, ptlist, origin, selSense, layer, slabC): """ Add a new circle to the shape. """ c = _Circle(self, ptlist, origin, selSense, slab = slabC) self._saveMaxThickness(layer, slabC.thickness, slabC.normal) self._cutCookie(layer, c) self._addCurve(layer, c) def pickline(self, ptlist, origin, selSense, layer, slabC): """ Add a new curve to the shape. Args define the curve (see curve) and the selSense operator for the curve telling whether it adds or removes material. """ # Review: does "(see curve)" in this docstring # refer to class curve in shape.py, # which is not used in this module which was split from shape.py? # If not, what does it mean? # [bruce 071215 question] c = shape.pickline(self, ptlist, origin, selSense, slab = slabC) self._saveMaxThickness(layer, slabC.thickness, slabC.normal) self._cutCookie(layer, c) self._addCurve(layer, c) def pickrect(self, pt1, pt2, org, selSense, layer, slabC): """ Add a new rectangle to the shape. Args define the rectangle and the selSense operator for the curve telling whether it adds or removes material. """ c = shape.pickrect(self, pt1, pt2, org, selSense, slab = slabC) self._saveMaxThickness(layer, slabC.thickness, slabC.normal) self._cutCookie(layer, c) self._addCurve(layer, c) def _updateBBox(self, curveList): """ Recompute the bounding box for the list of curves """ bbox = BBox() for c in curveList[1:]: bbox.merge(c.bbox) curveList[0] = bbox def undo(self, currentLayer): """ This would work for shapes, if anyone called it. """ if self.layeredCurves.has_key(currentLayer): curves = self.layeredCurves[currentLayer] if len(curves) > 1: curves = curves[:-1] self._updateBBox(curves) self.layeredCurves[currentLayer] = curves ##Kludge to make the undo work. self.carbonPosDict[currentLayer] = {} self.hedroPosDict[currentLayer] = {} self.bondLayers[currentLayer] = {} for c in curves[1:]: self._cutCookie(currentLayer, c) self.havelist = 0 def clear(self, currentLayer): """ This would work for shapes, if anyone called it. """ curves = self.layeredCurves[currentLayer] curves = [] self.layeredCurves[currentLayer] = curves self.havelist = 0 def anyCurvesLeft(self): """ Return True if there are curve(s) left, otherwise, False. This can be used by user to decide if the shape object can be deleted. """ for cbs in self.layeredCurves.values(): if len(cbs) > 1: return True return False def combineLayers(self): """ """ # Experimental code to add all curves and bbox together # to make the molmake working. It may be removed later. for cbs in self.layeredCurves.values(): if cbs: self.bbox.merge(cbs[0]) self.curves += cbs[1:] def _hashAtomPos(self, pos): return int(dot(V(1000000, 1000, 1), floor(pos * 1.2))) def _addCurve(self, layer, c): """ Add curve into its own layer, update the bbox """ self.havelist = 0 if not layer in self.layeredCurves: bbox = BBox() self.layeredCurves[layer] = [bbox, c] else: self.layeredCurves[layer] += [c] self.layeredCurves[layer][0].merge(c.bbox) def _cellDraw(self, color, p0, p1): hasSinglet = False if type(p1) == type((1,)): v1 = p1[0] hasSinglet = True else: v1 = p1 if self.dispMode == 'Tubes': drawcylinder(color, p0, v1, 0.2) else: drawsphere(color, p0, 0.5, 1) if hasSinglet: drawsphere(color, v1, 0.2, 1) else: drawsphere(color, v1, 0.5, 1) drawline(white, p0, v1) def _anotherDraw(self, layerColor): """ The original way of selecting cookies, but do it layer by layer, so we can control how to display each layer. """ if self.havelist: glCallList(self.displist.dl) return glNewList(self.displist.dl, GL_COMPILE_AND_EXECUTE) for layer in self.layeredCurves.keys(): bbox = self.layeredCurves[layer][0] curves = self.layeredCurves[layer][1:] if not curves: continue color = layerColor[layer] for c in curves: c.draw() try: bblo, bbhi = bbox.data[1], bbox.data[0] allCells = genDiam(bblo - 1.6, bbhi + 1.6, self.latticeType) for cell in allCells: for pp in cell: p1 = p2 = None if self.isin(pp[0], curves): if self.isin(pp[1], curves): p1 = pp[0]; p2 = pp[1] else: p1 = pp[0]; p2 = ((pp[1]+pp[0])/2, ) elif self.isin(pp[1], curves): p1 = pp[1]; p2 = ((pp[1]+pp[0])/2, ) if p1 and p2: self._cellDraw(color, p1, p2) except: # bruce 041028 -- protect against exceptions while making display # list, or OpenGL will be left in an unusable state (due to the lack # of a matching glEndList) in which any subsequent glNewList is an # invalid operation. (Also done in chem.py; see more comments there.) print_compact_traceback( "bug: exception in shape.draw's displist; ignored: ") glEndList() self.havelist = 1 # return def _cutCookie(self, layer, c): """ For each user defined curve, cut the crystal for it, store carbon postion into a global dictionary, store the bond information into each layer. """ self.havelist = 0 bblo, bbhi = c.bbox.data[1], c.bbox.data[0] #Without +(-) 1.6, crystal for lonsdaileite may not be right allCells = genDiam(bblo - 1.6, bbhi + 1.6, self.latticeType) if self.carbonPosDict.has_key(layer): carbons = self.carbonPosDict[layer] else: carbons = {} if self.hedroPosDict.has_key(layer): hedrons = self.hedroPosDict[layer] else: hedrons = {} if c.selSense == SUBTRACT_FROM_SELECTION: markedAtoms = self.markedAtoms if not self.bondLayers or not self.bondLayers.has_key(layer): return else: bonds = self.bondLayers[layer] for cell in allCells: for pp in cell: ppInside = [False, False] for ii in range(2): if c.isin(pp[ii]): ppInside[ii] = True if ppInside[0] or ppInside[1]: self._logic0Bond(carbons, bonds, markedAtoms, hedrons, ppInside, pp) self. _removeMarkedAtoms(bonds, markedAtoms, carbons, hedrons) elif c.selSense == OUTSIDE_SUBTRACT_FROM_SELECTION: #& This differs from the standard selection scheme for Shift + Drag. mark 060211. #& This is marked for removal. mark 060320. if not self.bondLayers or not self.bondLayers.has_key(layer): return bonds = self.bondLayers[layer] newBonds = {}; newCarbons = {}; newHedrons = {}; insideAtoms = {} newStorage = (newBonds, newCarbons, newHedrons) for cell in allCells: for pp in cell: pph = [None, None] for ii in range(2): if c.isin(pp[ii]): pph[ii] = self._hashAtomPos(pp[ii]) if bonds.has_key(pph[ii]): insideAtoms[pph[ii]] = pp[ii] if (not pph[0]) and pph[1] and carbons.has_key(pph[1]): pph[0] = self._hashAtomPos(pp[0]) if bonds.has_key(pph[0]): newCarbons[pph[1]] = pp[1] newHedrons[pph[0]] = pp[0] if not newBonds.has_key(pph[0]): newBonds[pph[0]] = [(pph[1], 1)] else: newBonds[pph[0]] += [(pph[1], 1)] if insideAtoms: self._logic2Bond(carbons, bonds, hedrons, insideAtoms, newStorage) bonds, carbons, hedrons = newStorage elif c.selSense == ADD_TO_SELECTION: if self.bondLayers.has_key(layer): bonds = self.bondLayers[layer] else: bonds = {} for cell in allCells: for pp in cell: pph=[None, None] ppInside = [False, False] for ii in range(2): pph[ii] = self._hashAtomPos(pp[ii]) if c.isin(pp[ii]): ppInside[ii] = True if ppInside[0] or ppInside[1]: self._logic1Bond(carbons, hedrons, bonds, pp, pph, ppInside) elif c.selSense == START_NEW_SELECTION: # Added to make crystal cutter selection behavior # consistent when no modkeys pressed. mark 060320. carbons = {} bonds = {} hedrons = {} for cell in allCells: for pp in cell: pph = [None, None] ppInside = [False, False] for ii in range(2): pph[ii] = self._hashAtomPos(pp[ii]) if c.isin(pp[ii]): ppInside[ii] = True if ppInside[0] or ppInside[1]: self._logic1Bond(carbons, hedrons, bonds, pp, pph, ppInside) self.bondLayers[layer] = bonds self.carbonPosDict[layer] = carbons self.hedroPosDict[layer] = hedrons #print "bonds", bonds self.havelist = 1 return def _logic0Bond(self, carbons, bonds, markedAtoms, hedrons, ppInside, pp): """ For each pair of points, if both points are inside the curve and are existed carbons, delete the bond, and mark the 'should be' removed atoms. Otherwise, delete half bond or change full to half bond accoringly. """ def _deleteHalfBond(which_in): """ Internal function: when the value-- carbon atom is removed from an half bond, delete the half bond. """ markedAtoms[pph[which_in]] = pp[which_in] try: values = bonds[pph[0]] values.remove((pph[1], which_in)) bonds[pph[0]] = values if len(values) == 0: del bonds[pph[0]] #print "Delete half bond: ", pph[0], (pph[1], which_in) except: print "No such half bond: ", pph[0], (pph[1], which_in) def _changeFull2Half(del_id, which_in): """ internal function: If there is a full bond and when the value (2nd in a bond pair) carbon atom is removed, change it to half bond """ if not hedrons.has_key(pph[del_id]): hedrons[pph[del_id]] = pp[del_id] markedAtoms[pph[del_id]] = pp[del_id] if bonds.has_key(pph[0]): values = bonds[pph[0]] idex = values.index(pph[1]) values[idex] = (pph[1], which_in) bonds[pph[0]] = values ## print "Change full to half bond: ", pph[0], (pph[1], which_in) pph = [] pph += [self._hashAtomPos(pp[0])] pph += [self._hashAtomPos(pp[1])] if ppInside[0] and ppInside[1]: # Delete full bond if carbons.has_key(pph[0]) and carbons.has_key(pph[1]): markedAtoms[pph[0]] = pp[0] markedAtoms[pph[1]] = pp[1] values = bonds[pph[0]] values.remove(pph[1]) bonds[pph[0]] = values if len(values) == 0: del bonds[pph[0]] # Delete half bond elif carbons.has_key(pph[0]): #markedAtoms[pph[0]] = pp[0] _deleteHalfBond(0) # Delete half bond elif carbons.has_key(pph[1]): _deleteHalfBond(1) elif ppInside[0]: # Full bond becomes half bond, carbon becomes hedron if carbons.has_key(pph[0]) and carbons.has_key(pph[1]): markedAtoms[pph[0]] = pp[0] #_changeFull2Half(0, 1) # Delete half bond elif carbons.has_key(pph[0]): #markedAtoms[pph[0]] = pp[0] _deleteHalfBond(0) elif ppInside[1]: # Full bond becomes half bond, carbon becomes hedron if carbons.has_key(pph[1]) and carbons.has_key(pph[0]): _changeFull2Half(1, 0) # Delete half bond elif carbons.has_key(pph[1]): _deleteHalfBond(1) def _logic1Bond(self, carbons, hedrons, bonds, pp, pph, ppInside): """ For each pair of points , create a full bond if necessary and if both points are inside the curve ; otherwise, if one point is in while the other is not, create a half bond if necessary. """ if ppInside[0] and ppInside[1]: if (not pph[0] in carbons) and (not pph[1] in carbons): if pph[0] in hedrons: del hedrons[pph[0]] if pph[1] in hedrons: del hedrons[pph[1]] carbons[pph[0]] = pp[0] carbons[pph[1]] = pp[1] # create a new full bond self._createBond(bonds, pph[0], pph[1], -1, True) elif not pph[0] in carbons: if pph[0] in hedrons: del hedrons[pph[0]] carbons[pph[0]] = pp[0] # update half bond to full bond self._changeHf2FullBond(bonds, pph[0], pph[1], 1) elif not pph[1] in carbons: if pph[1] in hedrons: del hedrons[pph[1]] carbons[pph[1]] = pp[1] # update half bond to full bond self._changeHf2FullBond(bonds, pph[0], pph[1], 0) # create full bond else: self._createBond(bonds, pph[0], pph[1]) elif ppInside[0]: if (not pph[0] in carbons) and (not pph[1] in carbons): if pph[0] in hedrons: del hedrons[pph[0]] carbons[pph[0]] = pp[0] if not pph[1] in hedrons: hedrons[pph[1]] = pp[1] # create new half bond self._createBond(bonds, pph[0], pph[1], 0, True) elif not pph[0] in carbons: if pph[0] in hedrons: del hedrons[pph[0]] carbons[pph[0]] = pp[0] #update half bond to full bond self._changeHf2FullBond(bonds, pph[0], pph[1], 1) elif not pph[1] in carbons: if not pph[1] in hedrons: hedrons[pph[1]] = pp[1] # create half bond, with 0 in, 1 out self._createBond(bonds, pph[0], pph[1], 0) # create full bond else: self._createBond(bonds, pph[0], pph[1]) elif ppInside[1]: if (not pph[0] in carbons) and (not pph[1] in carbons): if pph[1] in hedrons: del hedrons[pph[1]] carbons[pph[1]] = pp[1] if not pph[0] in hedrons: hedrons[pph[0]] = pp[0] # create new half bond, with 1 in, 0 out self._createBond(bonds, pph[0], pph[1], 1, True) elif not pph[0] in carbons: if not pph[0] in hedrons: hedrons[pph[0]] = pp[0] # create half bond, with 1 in, 0 out self._createBond(bonds, pph[0], pph[1], 1) elif not pph[1] in carbons: if pph[1] in hedrons: del hedrons[pph[1]] carbons[pph[1]] = pp[1] # update half bond to full bond self._changeHf2FullBond(bonds, pph[0], pph[1], 0) # create full bond else: self._createBond(bonds, pph[0], pph[1]) return def _logic2Bond(self, carbons, bonds, hedrons, insideAtoms, newStorage): """ Processing all bonds having key inside the current selection curve. For a bond with the key outside, the value inside the selection curve, we deal with it when we scan the edges of each cell. To make sure no such bonds are lost, we need to enlarge the bounding box at least 1 lattice cell. """ newBonds, newCarbons, newHedrons = newStorage for a in insideAtoms.keys(): values = bonds[a] newValues = [] # The key is carbon: if carbons.has_key(a): if not newCarbons.has_key(a): newCarbons[a] = insideAtoms[a] for b in values: if type(b) == type(1): #Full bond # If the carbon inside, keep the bond if insideAtoms.has_key(b): if not newCarbons.has_key(b): newCarbons[b] = insideAtoms[b] newValues += [b] else: # outside carbon, change it to h-bond if not newHedrons.has_key(b): newHedrons[b] = carbons[b] newValues += [(b, 0)] else: # Half bond, keep it if insideAtoms.has_key(b[0]): p = insideAtoms[b[0]] elif hedrons.has_key(b[0]): p = hedrons[b[0]] else: raise ValueError, (a, b[0]) if not newHedrons.has_key(b[0]): newHedrons[b[0]] = p newValues += [b] else: # The key is not a carbon if not newHedrons.has_key(a): newHedrons[a] = insideAtoms[a] for b in values: # Inside h-bond, keep it if insideAtoms.has_key(b[0]): if not newHedrons.has_key(b[0]): newHedrons[b[0]] = insideAtoms[b[0]] newValues += [b] if newValues: newBonds[a] = newValues def _removeMarkedAtoms(self, bonds, markedAtoms, carbons, hedrons): """ Remove all carbons that should have been removed because of the new selection curve. Update bonds that have the carbon as key. For a bond who has the carbon as its value, we'll leave them as they are, untill the draw() call. When it finds a value of a bond can't find its carbon position, either remove the bond if it was a half bond or change it to half bond if it was full bond, and find its carbon position in markedAtoms{} """ for ph in markedAtoms.keys(): if carbons.has_key(ph): ## print "Remove carbon: ", ph if bonds.has_key(ph): values = bonds[ph] for b in values[:]: if type(b) == type(1): idex = values.index(b) values[idex] = (b, 1) ## print "Post processing: Change full to half bond: ", ph, values[idex] else: values.remove(b) ## print "Erase half bond:", ph, b # commented out. Mark 060205. bonds[ph] = values if len(values) == 0: del bonds[ph] else: hedrons[ph] = carbons[ph] del carbons[ph] def _changeHf2FullBond(self, bonds, key, value, which_in): """ If there is a half bond, change it to full bond. Otherwise, create a new full bond. : the atom which exists before. """ foundHalfBond = False if bonds.has_key(key): values = bonds[key] for ii in range(len(values)): if type(values[ii]) == type((1, 1)) and values[ii][0] == value: values[ii] = value foundHalfBond = True break if not foundHalfBond: values += [value] ## bonds[key] = values elif not bonds.has_key(key): bonds[key] = [value] def _createBond(self, dict, key, value, half_in = -1, new_bond = False): """ Create a new bond if is True. Otherwise, search if there is such a full/half bond, change it appropriately if found. Otherwise, create a new bond. If == -1, it's a full bond; otherwise, it means a half bond with the atom of is inside. """ if not key in dict: if half_in < 0: dict[key] = [value] else: dict[key] = [(value, half_in)] else: values = dict[key] if half_in < 0: if new_bond: values += [value] else: found = False for ii in range(len(values)): if type(values[ii]) == type(1): if value == values[ii]: found = True break elif value == values[ii][0]: values[ii] = value found = True break if not found: values += [value] else: if new_bond: values +=[(value, half_in)] else: try: idex = values.index((value, half_in)) except: values += [(value, half_in)] dict[key] = values def changeDisplayMode(self, mode): self.dispMode = mode self.havelist = 0 def _bondDraw(self, color, p0, p1, carbonAt): if self.dispMode == 'Tubes': drawcylinder(color, p0, p1, 0.2) else: if carbonAt < 0: drawsphere(color, p0, 0.5, 1) drawsphere(color, p1, 0.5, 1) elif carbonAt == 0: drawsphere(color, p0, 0.5, 1) drawsphere(color, p1, 0.2, 1) elif carbonAt == 1: drawsphere(color, p0, 0.2, 1) drawsphere(color, p1, 0.5, 1) drawline(white, p0, p1) def draw(self, glpane, layerColor): """ Draw the shape. Find the bounding box for the curve and check the position of each carbon atom in a lattice would occupy for being 'in' the shape. A tube representation of the atoms thus selected is saved as a GL call list for fast drawing. This method is only for crystal-cutter mode. --Huaicai """ #bruce 090220 renamed first arg from win to glpane (which is what # was actually passed) and used it in ColorSorter.start (required now). if 0: self._anotherDraw(layerColor) return markedAtoms = self.markedAtoms if self.havelist: glCallList(self.displist.dl) return #russ 080225: Moved glNewList into ColorSorter.start for displist re-org. #russ 080225: displist side effect allocates a ColorSortedDisplayList. ColorSorter.start(glpane, self.displist) # grantham 20051205 try: for layer, bonds in self.bondLayers.items(): color = layerColor[layer] self.layeredCurves[layer][-1].draw() bonds = self.bondLayers[layer] carbons = self.carbonPosDict[layer] hedrons = self.hedroPosDict[layer] for cK, bList in bonds.items(): if carbons.has_key(cK): p0 = carbons[cK] for b in bList[:]: carbonAt = -1 if type(b) == type(1): #Full bond if carbons.has_key(b): p1 = carbons[b] else: #which means the carbon was removed p1 = markedAtoms[b] #print "Carbon was removed: ", b, p1 idex = bList.index(b) bList[idex] = (b, 0) hedrons[b] = p1 p1 = (p0 + p1) / 2.0 carbonAt = 0 else: #Half bond carbonAt = b[1] if b[1]: if carbons.has_key(b[0]): # otherwise, means the carbon has been removed. p1 = carbons[b[0]] if hedrons.has_key(cK): p0 = hedrons[cK] p0 = (p0 + p1) / 2.0 else: #half bond becomes full bond because of new selection p0 = carbons[cK] idex = bList.index(b) bList[idex] = b[0] else: # remove the half bond bList.remove(b) #print "delete half bond: (%d: " %cK, b if len(bList) == 0: del bonds[cK] break continue else: if hedrons.has_key(b[0]): p1 = hedrons[b[0]] p1 = (p0 + p1) / 2.0 else: # Which means half bond becomes full bond because of new selection p1 = carbons[b[0]] idex = bList.index(b) bList[idex] = b[0] self._bondDraw(color, p0, p1, carbonAt) bonds[cK] = bList except: # bruce 041028 -- protect against exceptions while making display # list, or OpenGL will be left in an unusable state (due to the lack # of a matching glEndList) in which any subsequent glNewList is an # invalid operation. (Also done in chem.py; see more comments there.) print "cK: ", cK print_compact_traceback( "bug: exception in shape.draw's displist; ignored: ") self.markedAtoms = {} ColorSorter.finish(draw_now = True) self.havelist = 1 # always set this flag, even if exception happened. def buildChunk(self, assy): """ Build Chunk for the cookies. First, combine bonds from all layers together, which may fuse some half bonds to full bonds. """ from model.chunk import Chunk from model.chem import Atom from utilities.constants import gensym numLayers = len(self.bondLayers) if numLayers: allBonds = {} allCarbons = {} #Copy the bonds, carbons and hedron from the first layer for ii in range(numLayers): if self.bondLayers.has_key(ii): for bKey, bValue in self.bondLayers[ii].items(): allBonds[bKey] = bValue del self.bondLayers[ii] break for carbons in self.carbonPosDict.values(): for cKey, cValue in carbons.items(): allCarbons[cKey] = cValue for hedrons in self.hedroPosDict.values(): for hKey, hValue in hedrons.items(): allCarbons[hKey] = hValue for bonds in self.bondLayers.values(): for bKey, bValues in bonds.items(): if bKey in allBonds: existValues = allBonds[bKey] for bValue in bValues: if type(bValue) == type((1, 1)): if bValue[1]: ctValue = (bValue[0], 0) else: ctValue = (bValue[0], 1) if ctValue in existValues: idex = existValues.index(ctValue) existValues[idex] = bValue[0] else: existValues += [bValue] else: existValues += [bValue] allBonds[bKey] = existValues else: allBonds[bKey] = bValues #print "allbonds: ", allBonds #print "allCarbons: ", allCarbons carbonAtoms = {} mol = Chunk(assy, gensym("Crystal", assy)) for bKey, bBonds in allBonds.items(): keyHedron = True if len(bBonds): for bond in bBonds: if keyHedron: if type(bBonds[0]) == type(1) or (not bBonds[0][1]): if not bKey in carbonAtoms: keyAtom = Atom("C", allCarbons[bKey], mol) carbonAtoms[bKey] = keyAtom else: keyAtom = carbonAtoms[bKey] keyHedron = False if keyHedron: if type(bond) != type((1, 1)): raise ValueError, (bKey, bond, bBonds) else: xp = (allCarbons[bKey] + allCarbons[bond[0]])/2.0 keyAtom = Atom("X", xp, mol) if type(bond) == type(1) or bond[1]: if type(bond) == type(1): bvKey = bond else: bvKey = bond[0] if not bvKey in carbonAtoms: bondAtom = Atom("C", allCarbons[bvKey], mol) carbonAtoms[bvKey] = bondAtom else: bondAtom = carbonAtoms[bvKey] else: xp = (allCarbons[bKey] + allCarbons[bond[0]])/2.0 bondAtom = Atom("X", xp, mol) bond_atoms(keyAtom, bondAtom) if len(mol.atoms) > 0: #bruce 050222 comment: much of this is not needed, since mol.pick() does it. # Note: this method is similar to one in BuildCrystal_Command.py. assy.addmol(mol) assy.unpickall_in_GLPane() # was unpickparts; not sure _in_GLPane is best (or that # this is needed at all) [bruce 060721] mol.pick() assy.mt.mt_update() return # from buildChunk pass # end of class CrystalShape # end