summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Smith <bruce@nanorex.com>2008-03-14 19:25:28 +0000
committerBruce Smith <bruce@nanorex.com>2008-03-14 19:25:28 +0000
commitda391751b40207b230758118d460bb2b1655ab0a (patch)
tree6635effa40190a38019b983e7d493d038a9c9734
parenta704788e1c5bb68826a3b7cffacdf0cdee024ec4 (diff)
downloadnanoengineer-theirix-da391751b40207b230758118d460bb2b1655ab0a.tar.gz
nanoengineer-theirix-da391751b40207b230758118d460bb2b1655ab0a.zip
split BorrowerChunk out of chunk.py
-rwxr-xr-xcad/src/commands/Select/Select_GraphicsMode.py2
-rw-r--r--cad/src/model/BorrowerChunk.py289
-rwxr-xr-xcad/src/model/chunk.py247
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):