diff options
author | Bruce Smith <bruce@nanorex.com> | 2008-03-14 19:25:28 +0000 |
---|---|---|
committer | Bruce Smith <bruce@nanorex.com> | 2008-03-14 19:25:28 +0000 |
commit | da391751b40207b230758118d460bb2b1655ab0a (patch) | |
tree | 6635effa40190a38019b983e7d493d038a9c9734 | |
parent | a704788e1c5bb68826a3b7cffacdf0cdee024ec4 (diff) | |
download | nanoengineer-theirix-da391751b40207b230758118d460bb2b1655ab0a.tar.gz nanoengineer-theirix-da391751b40207b230758118d460bb2b1655ab0a.zip |
split BorrowerChunk out of chunk.py
-rwxr-xr-x | cad/src/commands/Select/Select_GraphicsMode.py | 2 | ||||
-rw-r--r-- | cad/src/model/BorrowerChunk.py | 289 | ||||
-rwxr-xr-x | cad/src/model/chunk.py | 247 |
3 files changed, 296 insertions, 242 deletions
diff --git a/cad/src/commands/Select/Select_GraphicsMode.py b/cad/src/commands/Select/Select_GraphicsMode.py index 1895dbf42..3a43d0349 100755 --- a/cad/src/commands/Select/Select_GraphicsMode.py +++ b/cad/src/commands/Select/Select_GraphicsMode.py @@ -1033,7 +1033,7 @@ class Select_basicGraphicsMode(Select_GraphicsMode_DrawMethod_preMixin, continue pass # list is empty, just return a new one - from model.chunk import BorrowerChunk + from model.BorrowerChunk import BorrowerChunk return BorrowerChunk(self.o.assy) def deallocate_borrowerchunk(self, bc): diff --git a/cad/src/model/BorrowerChunk.py b/cad/src/model/BorrowerChunk.py new file mode 100644 index 000000000..fbd511aa5 --- /dev/null +++ b/cad/src/model/BorrowerChunk.py @@ -0,0 +1,289 @@ +# Copyright 2006-2008 Nanorex, Inc. See LICENSE file for details. +""" +BorrowerChunk.py -- a chunk which temporarily borrows the atoms +from another one, for an experimental optimization of display lists +when moving subsets of the atoms of a chunk. + +Not used by default. Deprecated, since some more principled and +more general loosening of the chunk <-> display list correspondence +would be much better. + +@author: Bruce +@version: $Id$ +@copyright: 2006-2008 Nanorex, Inc. See LICENSE file for details. + +History: + +Bruce wrote this inside chunk.py + +Bruce 080314 split this into its own file. +""" + +# note: this may not have been tested since it was split out of chunk.py. + +from utilities.debug import register_debug_menu_command + +from model.chunk import Chunk + +import model.chem as chem + +import foundation.env as env + +from utilities.Log import orangemsg, redmsg, quote_html + +from utilities.debug import safe_repr + +# == + +# Some notes about BorrowerChunk [bruce 060411] +## If an undo checkpoint occurs while the atoms are stolen, +## it won't contain them (it will be as if they were deleted, I think). +## This might be tolerable for A7 (if tested for safety), since cp's during drag +## are rare -- or it might not, if problems are caused by bonds from existing to dead atoms! +## +## When we want to fix it, here are some possible ways: +# - make the missing-atoms scheme work, by making the bonds from existing to dead atoms also seem to be missing, somehow. +## - let this chunk be scanned by Undo (ie make it a child of the assy, in a special place +## so not in the MT), and let it have an _undo_update method +## (or the like) which merges its atoms back into their homes. (It might turn out this is required +## anyway, if having it missing during a cp causes unforseen problems.) +## - disable cp's during drag. +## - merge undo diffs from the drag. + +# == + +class BorrowerChunk(Chunk): + """ + A temporary Chunk (mostly transparent to users and Undo, when not added to MT) whose atoms belong in other chunks. + Useful for optimizing redraws, since it has one perhaps-small display list, and the other chunks' display lists + won't be modified when our atoms change (except once when we first steal their atoms, and when we add them back). + + @warning: removing atoms from, or adding atoms to, this pseudo-chunk is not supported. + Except for debugging purposes, it should never be added to the MT, or permitted to exist when arbitrary user-ops are possible. + Its only known safe usage pattern is to be created, used, and destroyed, during one extended operation such as a mouse-drag. + [If more uses are thought of, these limitations could all be removed. #e] + + update 060412: trying to make it fully safe for Undo cp, and in case it's accidently left alive (in GLPane or MT). + But not trying to make results perfectly transparent or "correct" in those cases, since we'll try to prevent them. + E.g. for mmp save, it'll save as a normal Chunk would. + """ + def __init__(self, assy, atomset = None, name = None): # revised 060413 + """ + #doc; for doc of atomset, see take_atomset + """ + Chunk.__init__(self, assy, self._name_when_empty(assy)) + if atomset is not None: + self.take_atomset(atomset, name = name) + return + + def _name_when_empty(self, assy = None): + if assy is None: + assy = self.assy + del assy # not yet used; might use id or repr someday + return "(empty borrower id %#x)" % id(self) + + def take_atomset(self, atomset, name = None): + """ + #doc; atomset maps atom.key -> atom for some atoms we'll temporarily own + @warning: if all of another chunk's atoms are in atomset, creating us will kill that chunk. + @warning: it's up to the caller to make sure all singlet neighbors of atoms in atomset + are also in atomset! Likely bugs if it doesn't. + If you want to call this again on new atoms, call self.demolish first. + """ + if not name: + name = "(borrower of %d atoms, id %#x)" % (len(atomset), id(self)) #e __repr__ also incls this info + self.name = name # no use for prior value of self.name #k is there a set_name we should be using?? + atoms = atomset.values() #e could optim this -- only use is to let us pick one arbitrary atom + egatom = atoms[0] + egmol = egatom.molecule # do this now, since we're going to change it in the loop + del atoms, egatom + assy = egmol.assy + assert assy is self.assy + # now steal the atoms, but remember their homes and don't add ourselves to assy.tree. + # WARNING: if we steal *all* atoms from another chunk, that will cause trouble, + # but preventing this is up to the caller! [#e put this into another method, so it can be called again later??] + # We optimize this by lots of inlining, since we need it to be fast for lots of atoms. + harmedmols = {} # id(mol) -> mol for all mols whose atoms we steal from + origmols = {} # atom.key - original atom.molecule + self.origmols = origmols + self.harmedmols = harmedmols + # self.atoms was initialized to {} in Chunk.__init__, or restored to that in self.demolish() + for key, atom in atomset.iteritems(): + mol = atom.molecule + assert mol is not self + if isinstance(mol, self.__class__): + print "%r: borrowing %r from another borrowerchunk %r is not supported" % (self, atom, mol) + # whether it might work, I have no idea + harmedmols[id(mol)] = mol + # inline part of mol.delatom(atom): + #e do this later: mol.invalidate_atom_lists() + chem._changed_parent_Atoms[key] = atom + del mol.atoms[key] # callers can check for KeyError, always an error + # don't do this (but i don't think it prevents all harm from stealing all mol's atoms): + ## if not mol.atoms: + ## mol.kill() + # inline part of self.addatom(atom): + atom.molecule = self + atom.index = -1 # illegal value + self.atoms[key] = atom + #e do this later: self.invalidate_atom_lists() + # remember where atom came from: + origmols[key] = mol + # do what we saved for later in the inlined delatom and addatom calls: + for mol in harmedmols.itervalues(): + natoms = len(mol.atoms) + if not natoms: + print "bug: BorrowerChunk stole all atoms from %r; potential for harm is not yet known" % mol + mol.invalidate_atom_lists() + self.invalidate_atom_lists() + + try: + part = egmol.part + part.add(self) ###e not 100% sure this is ok; need to call part.remove too (and we do) + assert part is self.part # Part.add should do this (if it was not already done) + except: + print "data from following exception: egmol = %r, its part = %r, self.part = %r" % \ + ( egmol, part, self.part ) + raise + + return # from take_atomset + + # instead of overriding draw_displist, it's enough to define _colorfunc and _dispfunc to help it: + def _colorfunc(self, atm): + """ + Define this to use atm's home mol's color instead of self.color, and also so that self._dispfunc gets called + [overrides self._colorfunc = None; this scheme will get messed up if self ever gets copied, + since the copy code (two methods in Chunk) will set an instance attribute pointing to the bound method of the original] + """ + #e this has bugs if we removed atoms from self -- that's not supported (#e could override delatom to support it) + return self.origmols[atm.key].drawing_color() + def _dispfunc(self, atm): + origmol = self.origmols[atm.key] + glpane = origmol.glpane # set shortly before this call, in origmol.draw_displist (kluge) + disp = origmol.get_dispdef(glpane) + return disp + def restore_atoms_to_their_homes(self): + """ + put your atoms back where they belong + (calling this multiple times should be ok) + """ + #e this has bugs if we added atoms to self -- that's not supported (#e could override addatom to support it) + origmols = self.origmols + for key, atom in self.atoms.iteritems(): + chem._changed_parent_Atoms[key] = atom + origmol = origmols[key] + atom.molecule = origmol + atom.index = -1 # illegal value + origmol.atoms[key] = atom + for mol in self.harmedmols.itervalues(): + mol.invalidate_atom_lists() + self.atoms = {} + self.origmols = {} + self.harmedmols = {} + ## self.part = None + self.invalidate_atom_lists() # might not matter anymore; hope it's ok when we have no atoms + if self.part is not None: + self.part.remove(self) + return + def demolish(self): + """ + Restore atoms, and make self reusable + (but up to caller to remove self from any .dad it might have) + """ + self.restore_atoms_to_their_homes() + self.name = self._name_when_empty() + return + def take_atoms_from_list(self, atomlist): + """ + We must be empty (ready for reuse). + Divide atoms in atomlist by chunk; take the atoms we can (without taking all atoms from any chunk); + return a pair of lists (other_chunks, other_atoms), where other_chunks are chunks whose atoms were all in atomlist, + and other_atoms is a list of atoms we did not take for some other reason + (presently always [] since there is no other reason we can't take an atom). + """ + # note: some recent selectMode code for setting up dragatoms is similar enough (in finding other_chunks) + # that it might make sense to pull out a common helper routine + other_chunks = [] + other_atoms = [] # never changed + our_atoms = [] + chunks_and_atoms = divide_atomlist_by_chunk(atomlist) # list of pairs (chunk, atoms in it) + for chunk, atlist in chunks_and_atoms: + if len(chunk.atoms) == len(atlist): + other_chunks.append(chunk) + else: + our_atoms.extend(atlist) + atomset = dict([(a.key, a) for a in our_atoms]) + self.take_atomset( atomset) + return other_chunks, other_atoms + def kill(self): + self.restore_atoms_to_their_homes() # or should we delete them instead?? (this should never matter in our planned uses) + # this includes self.part = None + Chunk.kill(self) + def destroy(self): + self.kill() + self.name = "(destroyed borrowerchunk)" + # for testing, we might let one of these show up in the MT, and then we need these cmenu methods for it: + def __CM_Restore_Atoms_To_Their_Homes(self): + self.restore_atoms_to_their_homes() + assy = self.assy + self.kill() + assy.w.win_update() # at least mt_update is needed + #e we might need destroy and/or kill methods which call restore_atoms_to_their_homes + pass # end of class BorrowerChunk + +# == + +def divide_atomlist_by_chunk(atomlist): # only used in this file as of 080314, but might be more generally useful + # note: similar to some recent code for setting up dragatoms in selectMode, but not identical + """ + Given a list of atoms, return a list of pairs + (chunk, atoms in that chunk from that list). + Assume no atom appears twice. + """ + resdict = {} # id(chunk) -> list of one or more atoms from it + for at in atomlist: + chunk = at.molecule + resdict.setdefault(id(chunk), []).append(at) + return [(atlist[0].molecule, atlist) for atlist in resdict.itervalues()] + +def debug_make_BorrowerChunk(target): + """ + (for debugging only) + """ + debug_make_BorrowerChunk_raw(True) + +def debug_make_BorrowerChunk_no_addmol(target): + """ + (for debugging only) + """ + debug_make_BorrowerChunk_raw(False) + +def debug_make_BorrowerChunk_raw(do_addmol = True): + win = env.mainwindow() + atomset = win.assy.selatoms + if not atomset: + env.history.message(redmsg("Need selected atoms to make a BorrowerChunk (for debugging only)")) + else: + atomset = dict(atomset) # copy it, since we shouldn't really add singlets to assy.selatoms... + for atom in atomset.values(): # not itervalues, we're changing it in the loop! + # BTW Python is nicer about this than I expected: + # exceptions.RuntimeError: dictionary changed size during iteration + for bp in atom.singNeighbors(): # likely bugs if these are not added into the set! + atomset[bp.key] = bp + assy = atom.molecule.assy # these are all the same, and we do this at least once + chunk = BorrowerChunk(assy, atomset) + if do_addmol: + win.assy.addmol(chunk) + import __main__ + __main__._bc = chunk + env.history.message(orangemsg("__main__._bc = %s (for debugging only)" % quote_html(safe_repr(chunk)))) + win.win_update() #k is this done by caller? + return + +# == + +register_debug_menu_command("make BorrowerChunk", debug_make_BorrowerChunk) +register_debug_menu_command("make BorrowerChunk (no addmol)", debug_make_BorrowerChunk_no_addmol) + +# end + diff --git a/cad/src/model/chunk.py b/cad/src/model/chunk.py index dc5bc1449..b18b14278 100755 --- a/cad/src/model/chunk.py +++ b/cad/src/model/chunk.py @@ -73,7 +73,7 @@ from geometry.VQT import V, Q, A, vlen from foundation.NodeWithAtomContents import NodeWithAtomContents -from utilities.Log import orangemsg, redmsg, quote_html, graymsg +from utilities.Log import graymsg from utilities.debug import print_compact_stack, print_compact_traceback, safe_repr @@ -950,8 +950,11 @@ class Chunk(NodeWithAtomContents, InvalMixin, SelfUsageTrackingMixin, SubUsageTr if not store_if_invalid: # (when that's true, it's important not to recompute self.hotspot, even in an assertion) # now recompute self.hotspot from the new self._hotspot (to check whether it's valid) self.hotspot # this has side effects we depend on! - assert self.hotspot is hotspot or silently_fix_if_invalid, "getattr bug, or specified hotspot %s is invalid" % safe_repr(hotspot) - assert not 'hotspot' in self.__dict__.keys(), "bug in getattr for hotspot or in set_hotspot" + assert self.hotspot is hotspot or silently_fix_if_invalid, \ + "getattr bug, or specified hotspot %s is invalid" % \ + safe_repr(hotspot) + assert not 'hotspot' in self.__dict__.keys(), \ + "bug in getattr for hotspot or in set_hotspot" return def _get_hotspot(self): #bruce 050217; used by getattr @@ -3586,244 +3589,6 @@ class _nullMol_Chunk(Chunk): # == -class BorrowerChunk(Chunk): - """ - A temporary Chunk (mostly transparent to users and Undo, when not added to MT) whose atoms belong in other chunks. - Useful for optimizing redraws, since it has one perhaps-small display list, and the other chunks' display lists - won't be modified when our atoms change (except once when we first steal their atoms, and when we add them back). - - @warning: removing atoms from, or adding atoms to, this pseudo-chunk is not supported. - Except for debugging purposes, it should never be added to the MT, or permitted to exist when arbitrary user-ops are possible. - Its only known safe usage pattern is to be created, used, and destroyed, during one extended operation such as a mouse-drag. - [If more uses are thought of, these limitations could all be removed. #e] - - update 060412: trying to make it fully safe for Undo cp, and in case it's accidently left alive (in GLPane or MT). - But not trying to make results perfectly transparent or "correct" in those cases, since we'll try to prevent them. - E.g. for mmp save, it'll save as a normal Chunk would. - """ - def __init__(self, assy, atomset = None, name = None): # revised 060413 - """ - #doc; for doc of atomset, see take_atomset - """ - Chunk.__init__(self, assy, self._name_when_empty(assy)) - if atomset is not None: - self.take_atomset(atomset, name = name) - return - - def _name_when_empty(self, assy = None): - if assy is None: - assy = self.assy - del assy # not yet used; might use id or repr someday - return "(empty borrower id %#x)" % id(self) - - def take_atomset(self, atomset, name = None): - """ - #doc; atomset maps atom.key -> atom for some atoms we'll temporarily own - @warning: if all of another chunk's atoms are in atomset, creating us will kill that chunk. - @warning: it's up to the caller to make sure all singlet neighbors of atoms in atomset - are also in atomset! Likely bugs if it doesn't. - If you want to call this again on new atoms, call self.demolish first. - """ - if not name: - name = "(borrower of %d atoms, id %#x)" % (len(atomset), id(self)) #e __repr__ also incls this info - self.name = name # no use for prior value of self.name #k is there a set_name we should be using?? - atoms = atomset.values() #e could optim this -- only use is to let us pick one arbitrary atom - egatom = atoms[0] - egmol = egatom.molecule # do this now, since we're going to change it in the loop - del atoms, egatom - assy = egmol.assy - assert assy is self.assy - # now steal the atoms, but remember their homes and don't add ourselves to assy.tree. - # WARNING: if we steal *all* atoms from another chunk, that will cause trouble, - # but preventing this is up to the caller! [#e put this into another method, so it can be called again later??] - # We optimize this by lots of inlining, since we need it to be fast for lots of atoms. - harmedmols = {} # id(mol) -> mol for all mols whose atoms we steal from - origmols = {} # atom.key - original atom.molecule - self.origmols = origmols - self.harmedmols = harmedmols - # self.atoms was initialized to {} in Chunk.__init__, or restored to that in self.demolish() - for key, atom in atomset.iteritems(): - mol = atom.molecule - assert mol is not self - if isinstance(mol, self.__class__): - print "%r: borrowing %r from another borrowerchunk %r is not supported" % (self, atom, mol) - # whether it might work, I have no idea - harmedmols[id(mol)] = mol - # inline part of mol.delatom(atom): - #e do this later: mol.invalidate_atom_lists() - chem._changed_parent_Atoms[key] = atom - del mol.atoms[key] # callers can check for KeyError, always an error - # don't do this (but i don't think it prevents all harm from stealing all mol's atoms): - ## if not mol.atoms: - ## mol.kill() - # inline part of self.addatom(atom): - atom.molecule = self - atom.index = -1 # illegal value - self.atoms[key] = atom - #e do this later: self.invalidate_atom_lists() - # remember where atom came from: - origmols[key] = mol - # do what we saved for later in the inlined delatom and addatom calls: - for mol in harmedmols.itervalues(): - natoms = len(mol.atoms) - if not natoms: - print "bug: BorrowerChunk stole all atoms from %r; potential for harm is not yet known" % mol - mol.invalidate_atom_lists() - self.invalidate_atom_lists() - - try: - part = egmol.part - part.add(self) ###e not 100% sure this is ok; need to call part.remove too (and we do) - assert part is self.part # Part.add should do this (if it was not already done) - except: - print "data from following exception: egmol = %r, its part = %r, self.part = %r" % \ - ( egmol, part, self.part ) - raise - - return # from take_atomset - - # instead of overriding draw_displist, it's enough to define _colorfunc and _dispfunc to help it: - def _colorfunc(self, atm): - """ - Define this to use atm's home mol's color instead of self.color, and also so that self._dispfunc gets called - [overrides self._colorfunc = None; this scheme will get messed up if self ever gets copied, - since the copy code (two methods in Chunk) will set an instance attribute pointing to the bound method of the original] - """ - #e this has bugs if we removed atoms from self -- that's not supported (#e could override delatom to support it) - return self.origmols[atm.key].drawing_color() - def _dispfunc(self, atm): - origmol = self.origmols[atm.key] - glpane = origmol.glpane # set shortly before this call, in origmol.draw_displist (kluge) - disp = origmol.get_dispdef(glpane) - return disp - def restore_atoms_to_their_homes(self): - """ - put your atoms back where they belong - (calling this multiple times should be ok) - """ - #e this has bugs if we added atoms to self -- that's not supported (#e could override addatom to support it) - origmols = self.origmols - for key, atom in self.atoms.iteritems(): - chem._changed_parent_Atoms[key] = atom - origmol = origmols[key] - atom.molecule = origmol - atom.index = -1 # illegal value - origmol.atoms[key] = atom - for mol in self.harmedmols.itervalues(): - mol.invalidate_atom_lists() - self.atoms = {} - self.origmols = {} - self.harmedmols = {} - ## self.part = None - self.invalidate_atom_lists() # might not matter anymore; hope it's ok when we have no atoms - if self.part is not None: - self.part.remove(self) - return - def demolish(self): - """ - Restore atoms, and make self reusable - (but up to caller to remove self from any .dad it might have) - """ - self.restore_atoms_to_their_homes() - self.name = self._name_when_empty() - return - def take_atoms_from_list(self, atomlist): - """ - We must be empty (ready for reuse). - Divide atoms in atomlist by chunk; take the atoms we can (without taking all atoms from any chunk); - return a pair of lists (other_chunks, other_atoms), where other_chunks are chunks whose atoms were all in atomlist, - and other_atoms is a list of atoms we did not take for some other reason - (presently always [] since there is no other reason we can't take an atom). - """ - # note: some recent selectMode code for setting up dragatoms is similar enough (in finding other_chunks) - # that it might make sense to pull out a common helper routine - other_chunks = [] - other_atoms = [] # never changed - our_atoms = [] - chunks_and_atoms = divide_atomlist_by_chunk(atomlist) # list of pairs (chunk, atoms in it) - for chunk, atlist in chunks_and_atoms: - if len(chunk.atoms) == len(atlist): - other_chunks.append(chunk) - else: - our_atoms.extend(atlist) - atomset = dict([(a.key, a) for a in our_atoms]) - self.take_atomset( atomset) - return other_chunks, other_atoms - def kill(self): - self.restore_atoms_to_their_homes() # or should we delete them instead?? (this should never matter in our planned uses) - # this includes self.part = None - Chunk.kill(self) - def destroy(self): - self.kill() - self.name = "(destroyed borrowerchunk)" - # for testing, we might let one of these show up in the MT, and then we need these cmenu methods for it: - def __CM_Restore_Atoms_To_Their_Homes(self): - self.restore_atoms_to_their_homes() - assy = self.assy - self.kill() - assy.w.win_update() # at least mt_update is needed - #e we might need destroy and/or kill methods which call restore_atoms_to_their_homes - pass - - # Some notes about BorrowerChunk [bruce 060411] - ## If an undo checkpoint occurs while the atoms are stolen, - ## it won't contain them (it will be as if they were deleted, I think). - ## This might be tolerable for A7 (if tested for safety), since cp's during drag - ## are rare -- or it might not, if problems are caused by bonds from existing to dead atoms! - ## - ## When we want to fix it, here are some possible ways: - # - make the missing-atoms scheme work, by making the bonds from existing to dead atoms also seem to be missing, somehow. - ## - let this chunk be scanned by Undo (ie make it a child of the assy, in a special place - ## so not in the MT), and let it have an _undo_update method - ## (or the like) which merges its atoms back into their homes. (It might turn out this is required - ## anyway, if having it missing during a cp causes unforseen problems.) - ## - disable cp's during drag. - ## - merge undo diffs from the drag. - -def divide_atomlist_by_chunk(atomlist): # similar to some recent code for setting up dragatoms in selectMode, but not identical - "Given a list of atoms, return a list of pairs (chunk, atoms in that chunk from that list). Assume no atom appears twice." - resdict = {} # id(chunk) -> list of one or more atoms from it - for at in atomlist: - chunk = at.molecule - resdict.setdefault(id(chunk), []).append(at) - return [(atlist[0].molecule, atlist) for atlist in resdict.itervalues()] - -def debug_make_BorrowerChunk(target): - "(for debugging only)" - debug_make_BorrowerChunk_raw(True) - -def debug_make_BorrowerChunk_no_addmol(target): - "(for debugging only)" - debug_make_BorrowerChunk_raw(False) - -def debug_make_BorrowerChunk_raw(do_addmol = True): - win = env.mainwindow() - atomset = win.assy.selatoms - if not atomset: - env.history.message(redmsg("Need selected atoms to make a BorrowerChunk (for debugging only)")) - else: - atomset = dict(atomset) # copy it, since we shouldn't really add singlets to assy.selatoms... - for atom in atomset.values(): # not itervalues, we're changing it in the loop! - # BTW Python is nicer about this than I expected: - # exceptions.RuntimeError: dictionary changed size during iteration - for bp in atom.singNeighbors(): # likely bugs if these are not added into the set! - atomset[bp.key] = bp - assy = atom.molecule.assy # these are all the same, and we do this at least once - chunk = BorrowerChunk(assy, atomset) - if do_addmol: - win.assy.addmol(chunk) - import __main__ - __main__._bc = chunk - env.history.message(orangemsg("__main__._bc = %s (for debugging only)" % quote_html(safe_repr(chunk)))) - win.win_update() #k is this done by caller? - return - -from utilities.debug import register_debug_menu_command -register_debug_menu_command("make BorrowerChunk", debug_make_BorrowerChunk) -register_debug_menu_command("make BorrowerChunk (no addmol)", debug_make_BorrowerChunk_no_addmol) - -# == - from geometry.geometryUtilities import selection_polyhedron, inertia_eigenvectors, compute_heuristic_axis def shakedown_poly_evals_evecs_axis(basepos): |