summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Smith <bruce@nanorex.com>2008-06-05 05:27:11 +0000
committerBruce Smith <bruce@nanorex.com>2008-06-05 05:27:11 +0000
commitaae2f86f28cdf94ab65d3f068d2a894d322612a5 (patch)
tree461325325014b0dceca9809de4f2379aedd118fa
parentf4a60dbc358aa47e2c83a5879d819b1c97b4aabb (diff)
downloadnanoengineer-aae2f86f28cdf94ab65d3f068d2a894d322612a5.tar.gz
nanoengineer-aae2f86f28cdf94ab65d3f068d2a894d322612a5.zip
support for extra_displists in class Chunk (not done)
-rwxr-xr-xcad/src/model/chunk.py28
-rw-r--r--cad/src/scratch/chunk-extra-displists.py301
2 files changed, 326 insertions, 3 deletions
diff --git a/cad/src/model/chunk.py b/cad/src/model/chunk.py
index 0422003dd..527914d12 100755
--- a/cad/src/model/chunk.py
+++ b/cad/src/model/chunk.py
@@ -66,8 +66,6 @@ from OpenGL.GL import glEnable
from OpenGL.GL import glPopName
from OpenGL.GL import glPushName
-# chunk and chem formed a two element import cycle -- as of 080508 that's fixed,
-# but they are still both part of larger cycles involving the dna package.
# (chem is used here only for class Atom, as of 080510)
import model.chem as chem
@@ -1979,8 +1977,18 @@ class Chunk(NodeWithAtomContents, InvalMixin,
matprefs = drawing_globals.glprefs.materialprefs_summary() #bruce 051126
#bruce 060215 adding drawLevel to havelist
if self.havelist == (disp, eltprefs, matprefs, drawLevel): # value must agree with set of havelist, below
+ # our main display list is still valid -- use it
self.displist.draw_dl()
+ # note: this draws differently depending on selectedness
+ # stored in self.displist (redundant with self.picked,
+ # needs to be kept synchronized -- this ought to be refactored
+ # (to use the new self.displist.draw() method, once it's tested)
+ # so that state is not in self.displist [bruce 080604 comment])
+ for extra_displist in self.extra_displists.itervalues(): #####
+ extra_displist.draw_but_first_recompile_if_needed(glpane, selected = self.picked) #####
+ # todo: pass wantlist? yes in theory, but not urgent.
else:
+ # our main display list (and all extra lists) needs to be remade
if 1:
#bruce 060608: record info to help per-chunk display modes
# figure out whether they need to invalidate their memo data.
@@ -1993,9 +2001,11 @@ class Chunk(NodeWithAtomContents, InvalMixin,
self.havelist = 0 #bruce 051209: this is now needed
try:
wantlist = not env.mainwindow().movie_is_playing #bruce 051209
+ # warning: use of env.mainwindow is a KLUGE
except:
print_compact_traceback("exception (a bug) ignored: ")
wantlist = True
+ self.extra_displists = {} # we'll make new ones as needed
if wantlist:
##print "Regenerating display list for %s" % self.name
match_checking_code = self.begin_tracking_usage()
@@ -2010,6 +2020,10 @@ class Chunk(NodeWithAtomContents, InvalMixin,
# invalid operation. (Also done in shape.py; not needed in drawer.py.)
try:
self.draw_displist(glpane, disp, (hd, delegate_draw_atoms, delegate_draw_chunk))
+ ##### pass special-list-policy args?
+ # incl kind -> class, wantlist, self.extra_displists for where to put them
+ # or self to ask all that stuff (already passed of course).
+ # this needs to add things to self.extra_displists as needed
except:
print_compact_traceback("exception in Chunk.draw_displist ignored: ")
@@ -2029,7 +2043,15 @@ class Chunk(NodeWithAtomContents, InvalMixin,
# so it doesn't keep happening with every redraw of this Chunk.
#e (in future it might be safer to remake the display list to contain
# only a known-safe thing, like a bbox and an indicator of the bug.)
- pass
+
+ # draw the extra_displists (only needed if wantlist? not sure, so do always;
+ # guess: there will be none of them unless wantlist is set, so it doesn't matter)
+ for extra_displist in self.extra_displists.itervalues():
+ extra_displist.draw_but_first_recompile_if_needed(glpane,
+ selected = self.picked,
+ wantlist = wantlist)
+
+ pass # end of the case where our "main display list (and all extra lists) needs to be remade"
#@@ninad 070219 disabling the following--
#self._draw_selection_frame(glpane, delegate_selection_wireframe, hd) #bruce 060608 moved this here
diff --git a/cad/src/scratch/chunk-extra-displists.py b/cad/src/scratch/chunk-extra-displists.py
new file mode 100644
index 000000000..72981789a
--- /dev/null
+++ b/cad/src/scratch/chunk-extra-displists.py
@@ -0,0 +1,301 @@
+# Copyright 2008 Nanorex, Inc. See LICENSE file for details.
+"""
+chunk-extra-displists.py - scratch file to help with extra_displist code in class chunk
+
+@author: Bruce
+@version: $Id$
+@copyright: 2008 Nanorex, Inc. See LICENSE file for details.
+"""
+
+from graphics.drawing.ColorSorter import ColorSorter
+from graphics.drawing.ColorSorter import ColorSortedDisplayList
+
+from utilities.Comparison import same_vals
+from foundation.state_utils import copy_val
+
+class ExtraChunkDisplayList(object):
+ """
+ Holds one ColorSortedDisplayList used for doing some kind of extra
+ drawing associated with a Chunk but not drawn as part of its main
+ CSDL, along with the state that determines whether this CSDL is invalid,
+ and under what conditions it will remain valid.
+
+ Helps figure out when to invalidate it, redraw it, etc.
+ """
+
+ # class constants
+
+ _comparator_class = None # subclass must override, with a subclass of UsedValueTrackerAndComparator
+
+ # default values of instance variables
+
+ valid = False
+
+ def __init__(self):
+ self.csdl = ColorSortedDisplayList()
+ self._drawing_functions = [] # needed?
+ self.comparator = self._comparator_class()
+ return
+
+ # == methods related to the client compiling its *main* display list
+ # (not this extra one, but that's when it finds out what goes into
+ # this extra one, or that it needs it at all)
+
+ def _before_client_main_recompile(self): #e rename?
+ self._drawing_functions = []
+ return
+
+ def add_another_drawing_function(self, func): # during client main recompile
+ self._drawing_functions.append( func)
+ return
+
+ def _after_client_main_recompile(self): #e rename?
+ return
+
+ # == methods for drawing self (with or without recompiling)
+
+ def draw_but_first_recompile_if_needed(self, glpane, selected = False, highlighted = False, wantlist = True):
+ """
+ Recompile self as needed, then draw.
+ Only make internal display lists (in cases where we need to remake them)
+ if wantlist is true.
+
+ @param selected: whether to draw in selected or plain style. (The same csdl handles both.)
+
+ @param highlighted: whether to draw highlighted, or not. (The same csdl handles both.)
+ """
+ if not self.valid or self.comparator.do_we_need_to_recompute(): ##### also compare havelist, if some data not tracked
+ self._draw_by_remaking(glpane, selected, highlighted, wantlist)
+ else:
+ self._draw_by_reusing(glpane, selected, highlighted)
+ return
+
+ def _draw_by_remaking(self, glpane, selected, highlighted, wantlist):
+ """
+ """
+ # modified from similar code in Chunk.draw
+ if wantlist:
+ match_checking_code = self.begin_tracking_usage() ##### REQUIRES superclass
+ ColorSorter.start(self.csdl)
+ try:
+ self._do_drawing()
+ except:
+ print_compact_traceback("bug: exception in %r._do_drawing, skipping the rest: " % self)
+ pass
+ if wantlist:
+ ColorSorter.finish()
+ self._glpane = glpane # needed by self.inval_display_list for gl_update
+ self.end_tracking_usage( match_checking_code, self.inval_display_list )
+ return
+
+ def _draw_by_reusing(self, glpane, selected, highlighted):
+ """
+ """
+ # see also: code in Chunk.draw which uses its self.displist
+ self.csdl.draw(selected = selected, highlighted = highlighted)
+ return
+
+ # ==
+
+ def invalidate(self):
+ self.valid = False
+ return
+
+ def inval_display_list(self):
+ """
+ This is meant to be called when something whose usage we tracked
+ (while making our display list) next changes.
+ """
+ self.invalidate()
+ self._glpane.gl_update() # self._glpane should always exist
+ return
+
+ # ==
+
+ def _do_drawing(self):
+ # drawsphere(...), drawcylinder(...), drawpolycone(...), and so on.
+ for func in self._drawing_functions:
+ func() ### args?? e.g. self? some drawing prefs??
+ return
+
+ ### REVIEW below here, probably obs
+
+ def set_input_data(self, data):
+ """
+ The client chunk has computed new data which determines
+ (along with its own state?) what we should draw.
+ Store it, and if it differs from old data, invalidate.
+ """
+ if not same_vals( self._data, data):
+ self._data = copy_val(data)
+ self.invalidate()
+ return
+ def set_validity_data(self, vdata):
+ # for havelist contents i guess?
+ if not same_vals( self._vdata, vdata):
+ self._vdata = copy_val(vdata)
+ self.invalidate()
+ return
+ pass
+
+# ==
+
+class StrandEnd_ExtraChunkDisplayList(ExtraChunkDisplayList):
+ """
+ """
+ # note about current usage by client code (chunk drawing code):
+ # this will be created on demand when something being drawn
+ # during a chunk's main displist (CSDL) compile
+ # wants to put things into a StrandEnd display list instead,
+ # since that might need recompiling more often than the main one.
+ # The chunk knows which subclass of ExtraChunkDisplayList to use
+ # for each type of extra drawing, and allocates one on demand.
+ # Then the object doing the drawing will give us a function
+ # to use for redrawing itself, passing it to add_another_drawing_function.
+
+ # subclass constants
+
+ _comparator_class = StrandEnd_UsedValueTrackerAndComparator
+
+ pass # end of class
+
+# ======
+
+class UsedValueTrackerAndComparator(object):
+ """
+ Client code will have one of these...
+ when worried, call self.do_we_need_to_recompute,
+ then if true, call self.before_recompute,
+ do the computation, call self.after_recompute.
+ """
+
+ _recomputing = False
+ valid = False
+
+ def __init__(self):
+ # since not valid, no need to _reset
+ return
+
+ # == methods to use during a recomputation by the client
+
+ def before_recompute(self): #e rename
+ """
+ Prepare for the client to do another recomputation
+ which will call self.get_value for all keys whose values
+ we need to track usage of.
+ """
+ assert not self._recomputing
+ self._ordered_key_val_pairs = [] # in order of usage
+ # note: order can matter, if some keys' values are not safe
+ # to access for comparison unless others before them
+ # still have the same values
+ self._keys_used = {} # maps key -> val, but unordered
+ self.valid = False
+ # review: del self.valid?
+ self._recomputing = True
+ return
+
+ def get_value(self, key, context): ##### @@@@@ CALL THIS SOMEHOW in place of env.prefs when drawing extra displist contents
+ """
+ ... call this while redoing the client computation...
+ It will record the value it returns, both to optimize repeated calls,
+ and so it can be compared to the new current value
+ by self.are_all_values_still_the_same()
+ when the client next considers doing a recomputation.
+ """
+ if not self._keys_used.has_key(key):
+ val = self._compute_current_value(key, context)
+ # inline _track_use
+ self._keys_used[key] = val
+ self._ordered_key_val_pairs += [(key, val)]
+ else:
+ val = self._keys_used[key]
+ return val
+
+ def _compute_current_value(self, key, context):
+ assert 0, "subclass must implement %r._compute_current_value for key %r" % \
+ (self, key)
+ pass
+
+ def _track_use(self, key, val):
+ """
+ """
+ # safe public version, ok to call more than once for same key;
+ # inlined by get_value;
+ # REVIEW: perhaps this has no direct calls and can be removed?
+ if not self._keys_used.has_key(key):
+ self._keys_used[key] = val
+ self._ordered_key_val_pairs += [(key, val)]
+ return
+
+ def after_recompute(self):
+ """
+ ... must be called by the client after it finishes its recomputation ...
+ """
+ assert self._recomputing
+ del self._recomputing
+ self.valid = True
+ return
+
+ # ==
+
+ ## def invalidate(self): #### ambiguous: cause recompute or cause decision?
+ ## # del self._keys_used and self._ordered_key_val_pairs ?
+ ## pass
+
+ # == methods to use when deciding whether to do a recomputation
+
+ def do_we_need_to_recompute(self, context):
+ """
+ Do we need to recompute, now that we're in the current state
+ of the given context (for purposes of current values at keys,
+ as computed by self._compute_current_value)?
+ """
+ assert not self._recomputing
+ if not self.valid:
+ # following code would be wrong in this case
+ return True
+ for key, val in self.ordered_key_val_pairs():
+ newval = self._compute_current_value(key, context) # note: doesn't call _track_use, which would be a noop
+ if not same_vals(val, newval):
+ #e could optim the test for specific keys; probably not worth it though
+ #e could optim self.before_recompute (for some callers) to leave the values
+ # cached that were already found to be the same (by prior iterations
+ # of this loop)
+ return True
+ return False
+
+ def ordered_key_val_pairs(self):
+ """
+ @return: a list of zero of more (key, val) pairs
+ that were used by our client computation the last time it ran.
+
+ @warning: caller must not modify the list we return.
+ """
+ return self._ordered_key_val_pairs
+
+ pass # end of class UsedValueTrackerAndComparator
+
+# ==
+
+class StrandEnd_UsedValueTrackerAndComparator(UsedValueTrackerAndComparator):
+ """
+ ... if necessary, reset and re-track the values used this time... knows how to compute them...
+ """
+ def __init__(self):
+ UsedValueTrackerAndComparator.__init__(self)
+ return
+
+ def _compute_current_value(self, key, context):
+ """
+ Compute current value for key in context
+ when needed by self.get_value
+ (since it has no cached value for this key).
+ """
+ # require key to be in a hardcoded list??
+ graphicsMode = context
+ methodname = key
+ method = getattr(graphicsMode, methodname)
+ return method()
+
+ pass