def _updateSpecialContrastColors(self): # [probably by Mark, circa 080710]
     """
     [private]
     Update the special contrast colors (used to draw lines, etc.) to a 
     shade that contrasts well with the current background.
     @see: get_background_contrast_color()
     """
     # REVIEW: the following is only legitimate since these prefs variables
     # are (I think) not actual user prefs, but just global state variables.
     # However, if that's true, they should not really be stored in the
     # prefs db. Furthermore, if we had multiple GLPanes with different
     # background colors, I think these variables would need to be
     # per-glpane, so really they ought to be GLPane instance variables.
     # [bruce 080711 comment]
     env.prefs.restore_defaults([DarkBackgroundContrastColor_prefs_key,
                                 LightBackgroundContrastColor_prefs_key])
     dark_color = env.prefs[DarkBackgroundContrastColor_prefs_key]  # black
     lite_color = env.prefs[LightBackgroundContrastColor_prefs_key] # white
     gradient = env.prefs[ backgroundGradient_prefs_key ]
     
     if gradient == bgSOLID:
         if not colors_differ_sufficiently(self.backgroundColor, dark_color):
             env.prefs[DarkBackgroundContrastColor_prefs_key] = ave_colors( 0.5, dark_color, white)
         if not colors_differ_sufficiently(self.backgroundColor, lite_color):
             env.prefs[LightBackgroundContrastColor_prefs_key] = ave_colors( 0.5, lite_color, black)
     elif gradient == bgEVENING_SKY:
         env.prefs[DarkBackgroundContrastColor_prefs_key] = ave_colors( 0.6, dark_color, white)
     return
Пример #2
0
    def highlightAtomChunks(self):
        """
        highlight atoms
        """
        if not self.highlightChecked:
            return 

        atomChunks = self.findObjsInside()
        for m in atomChunks:
            if isinstance(m, Chunk):
                for a in m.atoms.itervalues():
                    a.overdraw_with_special_color(ave_colors( 0.8, green, black))
            else:
                m.overdraw_with_special_color(ave_colors( 0.8, green, black))
        return
Пример #3
0
 def drawchunk(self, glpane, chunk, memo, highlighted):
     """Draw chunk in glpane in the whole-chunk display mode represented by this ChunkDisplayMode subclass.
     Assume we're already in chunk's local coordinate system
     (i.e. do all drawing using atom coordinates in chunk.basepos, not chunk.atpos).
        If highlighted is true, draw it in hover-highlighted form (but note that it may have
     already been drawn in unhighlighted form in the same frame, so normally the highlighted form should
     augment or obscure the unhighlighted form).
        Draw it as unselected, whether or not chunk.picked is true. See also self.drawchunk_selection_frame.
     (The reason that's a separate method is to permit future drawing optimizations when a chunk is selected
     or deselected but does not otherwise change in appearance or position.)
        If this drawing requires info about chunk which it is useful to precompute (as an optimization),
     that info should be computed by our compute_memo method and will be passed as the memo argument
     (whose format and content is whatever self.compute_memo returns). That info must not depend on
     the highlighted variable or on whether the chunk is selected.
     """
     if not chunk.atoms:
         return
     pos, radius, color, tm, nm = memo
     if highlighted:
         color = ave_colors(0.5, color, env.prefs[chunkHighlightColor_prefs_key]) #e should the caller compute this somehow?
     # THIS IS WHERE OLEKSANDR SHOULD CALL HIS NEW CODE TO RENDER THE SURFACE (NOT CYLINDER).
     #   But if this requires time-consuming computations which depend on atom positions (etc) but not on point of view,
     # those should be done in compute_memo, not here, and their results will be passed here in the memo argument.
     # (This method drawchunk will not be called on every frame, but it will usually be called much more often than compute_memo.)
     #   For example, memo might contain a Pyrex object pointer to a C object representing some sort of mesh,
     # which can be rendered quickly by calling a Pyrex method on it.
     drawsurface(color, pos, radius, tm, nm)
     return
 def Draw_highlighted_selobj(self, glpane, selobj, hicolor):
     """
     [overrides superclass method]
     @see: self._get_objects_to_highlight()
     @see: self.drawHighlightedChunk()
     """
     # Ninad 070214 wrote this in GLPane; bruce 071008 moved it into
     # SelectChunks_GraphicsMode and slightly revised it.
     ## hicolor2 = orange # intended to visually differ from hicolor
     HHColor = env.prefs[hoverHighlightingColor_prefs_key]
     hicolor2 = ave_colors(0.5, HHColor, orange)
         #bruce 080217 revision to hicolor2 (since orange is a warning color)
     skip_usual_selobj_highlighting = self.drawHighlightedChunk(glpane,
                                                                selobj,
                                                                hicolor,
                                                                hicolor2)
         # Note: if subclasses don't want that call, they should override
         # drawHighlightedChunk to do nothing and return False.
         # The prior code was equivalent to every subclass doing that.
         # - [bruce 071008]
     if not skip_usual_selobj_highlighting:
         _superclass.Draw_highlighted_selobj(self,
                                             glpane,
                                             selobj,
                                             hicolor)
     return
Пример #5
0
    def highlightAtomChunks(self):
        """
        highlight atoms
        """
        if not self.highlightChecked:
            return

        atomChunks = self.findObjsInside()
        for m in atomChunks:
            if isinstance(m, Chunk):
                for a in m.atoms.itervalues():
                    a.overdraw_with_special_color(ave_colors(
                        0.8, green, black))
            else:
                m.overdraw_with_special_color(ave_colors(0.8, green, black))
        return
Пример #6
0
    def draw(self,
             glpane,
             offset=V(0, 0, 0),
             color=None,
             info={}):  # modified copy of superclass draw method
        "draw our spheres (in practice we'll need to extend this for different sets...)"
        ##        self.radius_multiplier = 1.0 # this might be changed by certain subclass's process_optional_info method
        ##        self.process_optional_info(info) # might reset instvars that affect following code... (kluge?)
        color = color or self.color
        ##detailLevel = 0 # just an icosahedron
        detailLevel = 1  # easier to click on this way
        ##radius = 0.33 # the one we store might be too large? no, i guess it's ok.
        #e (i might prefer an octahedron, or a miniature-convex-hull-of-extrude-unit)
        offset = offset + self.origin
        radius_multiplier = self.radius_multiplier
        special_pos = self.special_pos  # patched in ###nim?
        special_pos = special_pos + offset  #k?? or just self.origin??
        special_color = self.special_color  # default is used
        ##        count = 0
        for (pos, radius, info) in self.handles:
            radius *= radius_multiplier
            pos = pos + offset
            dist = vlen(special_pos - pos)
            if dist <= radius:
                color2 = ave_colors(1.0 - dist / radius, special_color, color)
##                count += 1
            else:
                color2 = color
            ## experiment 050218: add alpha factor to color
            color2 = tuple(color2) + (0.25, )
            drawsphere(color2, pos, radius, detailLevel)
##        self.color2_count = count # kluge, probably not used since should equal nbonds
        return
Пример #7
0
 def drawchunk(self, glpane, chunk, memo, highlighted):
     """Draw chunk in glpane in the whole-chunk display mode represented by this ChunkDisplayMode subclass.
     Assume we're already in chunk's local coordinate system
     (i.e. do all drawing using atom coordinates in chunk.basepos, not chunk.atpos).
        If highlighted is true, draw it in hover-highlighted form (but note that it may have
     already been drawn in unhighlighted form in the same frame, so normally the highlighted form should
     augment or obscure the unhighlighted form).
        Draw it as unselected, whether or not chunk.picked is true. See also self.drawchunk_selection_frame.
     (The reason that's a separate method is to permit future drawing optimizations when a chunk is selected
     or deselected but does not otherwise change in appearance or position.)
        If this drawing requires info about chunk which it is useful to precompute (as an optimization),
     that info should be computed by our compute_memo method and will be passed as the memo argument
     (whose format and content is whatever self.compute_memo returns). That info must not depend on
     the highlighted variable or on whether the chunk is selected.
     """
     if not chunk.atoms:
         return
     pos, radius, color, tm, nm = memo
     if highlighted:
         color = ave_colors(0.5, color,
                            env.prefs[chunkHighlightColor_prefs_key]
                            )  #e should the caller compute this somehow?
     # THIS IS WHERE OLEKSANDR SHOULD CALL HIS NEW CODE TO RENDER THE SURFACE (NOT CYLINDER).
     #   But if this requires time-consuming computations which depend on atom positions (etc) but not on point of view,
     # those should be done in compute_memo, not here, and their results will be passed here in the memo argument.
     # (This method drawchunk will not be called on every frame, but it will usually be called much more often than compute_memo.)
     #   For example, memo might contain a Pyrex object pointer to a C object representing some sort of mesh,
     # which can be rendered quickly by calling a Pyrex method on it.
     drawsurface(color, pos, radius, tm, nm)
     return
 def _updateOriginAxisColor(self):
     """
     [private]
     Update the color of the origin axis to a shade that 
     will contrast well with the background.
     """
     env.prefs.restore_defaults([originAxisColor_prefs_key])
     axisColor = env.prefs[originAxisColor_prefs_key]
     gradient = env.prefs[ backgroundGradient_prefs_key ]
     
     if gradient == bgSOLID:
         if not colors_differ_sufficiently(self.backgroundColor, axisColor):
             env.prefs[originAxisColor_prefs_key] = ave_colors( 0.5, axisColor, white)
     elif gradient == bgEVENING_SKY:
         env.prefs[originAxisColor_prefs_key] = ave_colors( 0.9, axisColor, white)
     return
Пример #9
0
 def drawchunk(self, glpane, chunk, memo, highlighted):
     """
     Draw chunk in glpane in the whole-chunk display mode represented by this ChunkDisplayMode subclass.
     Assume we're already in chunk's local coordinate system
     (i.e. do all drawing using atom coordinates in chunk.basepos, not chunk.atpos).
        If highlighted is true, draw it in hover-highlighted form (but note that it may have
     already been drawn in unhighlighted form in the same frame, so normally the highlighted form should
     augment or obscure the unhighlighted form).
        Draw it as unselected, whether or not chunk.picked is true. See also self.drawchunk_selection_frame.
     (The reason that's a separate method is to permit future drawing optimizations when a chunk is selected
     or deselected but does not otherwise change in appearance or position.)
        If this drawing requires info about chunk which it is useful to precompute (as an optimization),
     that info should be computed by our compute_memo method and will be passed as the memo argument
     (whose format and content is whatever self.compute_memo returns). That info must not depend on
     the highlighted variable or on whether the chunk is selected.
     """
     if not chunk.atoms:
         return
     end1, end2, radius, color = memo
     if highlighted:
         color = ave_colors(0.5, color,
                            env.prefs[chunkHighlightColor_prefs_key]
                            )  #e should the caller compute this somehow?
     drawcylinder(color, end1, end2, radius, capped=True)
     return
Пример #10
0
    def _highlightObjsInside(self):
        """
        highlight atoms covered by self
        """
        # Note: I refactored how this is called [and also renamed it
        # from highlightAtomChunks], so it doesn't slow down
        # every draw of every model by a complete scan of nodes just for
        # this (even when this rarely-used Node class is not present
        # in the model). It used to be called by its own model scanner,
        # _highlightAtoms, in SelectAtoms_basicGraphicsMode, called during
        # graphicsMode.Draw. Now it's called directly by self.draw (which is
        # also called during .Draw, or after today's refactoring of .Draw,
        # by .Draw_model).
        #
        # This is predicted to cause one observable change: we'll call this
        # during all modes, not only subclasses of SelectAtoms mode.
        # This is not obviously bad, and is hard to change, and this Node
        # is no longer considered very important, so I'm not worrying about
        # it for now. If you want to change it, don't reintroduce
        # anything that slows down all drawing, like the old code did!
        #
        # [bruce 090310]

        if not self.highlightChecked:
            return 

        color = ave_colors( 0.8, green, black)
        atomChunks = self._findObjsInside() # atoms or chunks
        for m in atomChunks:
            if isinstance(m, Chunk):
                for a in m.atoms.itervalues():
                    a.overdraw_with_special_color(color)
            else:
                m.overdraw_with_special_color(color)
        return
Пример #11
0
    def draw(self, glpane, offset = V(0,0,0), color = None, info = {}): # modified copy of superclass draw method
        "draw our spheres (in practice we'll need to extend this for different sets...)"
##        self.radius_multiplier = 1.0 # this might be changed by certain subclass's process_optional_info method
##        self.process_optional_info(info) # might reset instvars that affect following code... (kluge?)
        color = color or self.color
        ##detailLevel = 0 # just an icosahedron
        detailLevel = 1 # easier to click on this way
        ##radius = 0.33 # the one we store might be too large? no, i guess it's ok.
        #e (i might prefer an octahedron, or a miniature-convex-hull-of-extrude-unit)
        offset = offset + self.origin
        radius_multiplier = self.radius_multiplier
        special_pos = self.special_pos # patched in ###nim?
        special_pos = special_pos + offset #k?? or just self.origin??
        special_color = self.special_color # default is used
##        count = 0
        for (pos,radius,info) in self.handles:
            radius *= radius_multiplier
            pos = pos + offset
            dist = vlen(special_pos - pos)
            if dist <= radius:
                color2 = ave_colors( 1.0 - dist/radius, special_color, color )
##                count += 1
            else:
                color2 = color
            ## experiment 050218: add alpha factor to color
            color2 = tuple(color2) + (0.25,)
            drawsphere(color2, pos, radius, detailLevel)
##        self.color2_count = count # kluge, probably not used since should equal nbonds
        return
 def get_background_contrast_color(self):
     """
     Return a color that contrasts well with the background color of the 
     3D workspace (self). 
     @see: MultipleDnaSegmentResize_GraphicsMode where it is used for rendering 
     text with a proper contrast. 
     @see: self._updateSpecialContrastColors()
     """
     #NOTE: This method mitigates bug 2927
     
     dark_color = env.prefs[DarkBackgroundContrastColor_prefs_key]  # black
     ##lite_color = env.prefs[LightBackgroundContrastColor_prefs_key] # white
     gradient = env.prefs[ backgroundGradient_prefs_key ]
     
     color = black
             
     if gradient == bgSOLID:
         if not colors_differ_sufficiently(self.backgroundColor, dark_color):
             color = ave_colors( 0.5, dark_color, white)            
     elif gradient == bgEVENING_SKY:
         color = ave_colors( 0.6, dark_color, white)
         
     return color
 def _drawing_color(self):
     r0 = self._r0 # pm
     ks = self._ks # N/m
     length = self._getLength() # pm
     frac = 1 - 1 / (0.08 * ks * abs(r0 - length) + 1)
     if length < r0:
         # compressed: blue (gray if not at all, blue if a lot; should use ks somehow to get energy or force...)
         # limit = r0 * 0.5
         #frac = (r0 - length) / (r0 * 0.5) # if > 1, we'll use 1 below
         limit_color = blue
     else:
         # stretched
         # limit = r0 * 1.5
         #frac = (length - r0) / (r0 * 0.5)
         limit_color = red
     ## neutral_color = gray
     neutral_color = (0.8, 0.8, 0.8) # "very light gray"
     if frac > 1.0:
         frac = 1.0
     color = ave_colors( frac, limit_color, neutral_color )
     return color
 def _drawing_color(self):
     r0 = self._r0  # pm
     ks = self._ks  # N/m
     length = self._getLength()  # pm
     frac = 1 - 1 / (0.08 * ks * abs(r0 - length) + 1)
     if length < r0:
         # compressed: blue (gray if not at all, blue if a lot; should use ks somehow to get energy or force...)
         # limit = r0 * 0.5
         #frac = (r0 - length) / (r0 * 0.5) # if > 1, we'll use 1 below
         limit_color = blue
     else:
         # stretched
         # limit = r0 * 1.5
         #frac = (length - r0) / (r0 * 0.5)
         limit_color = red
     ## neutral_color = gray
     neutral_color = (0.8, 0.8, 0.8)  # "very light gray"
     if frac > 1.0:
         frac = 1.0
     color = ave_colors(frac, limit_color, neutral_color)
     return color
Пример #15
0
 def drawchunk(self, glpane, chunk, memo, highlighted):
     """
     Draw chunk in glpane in the whole-chunk display mode represented by this ChunkDisplayMode subclass.
     Assume we're already in chunk's local coordinate system
     (i.e. do all drawing using atom coordinates in chunk.basepos, not chunk.atpos).
        If highlighted is true, draw it in hover-highlighted form (but note that it may have
     already been drawn in unhighlighted form in the same frame, so normally the highlighted form should
     augment or obscure the unhighlighted form).
        Draw it as unselected, whether or not chunk.picked is true. See also self.drawchunk_selection_frame.
     (The reason that's a separate method is to permit future drawing optimizations when a chunk is selected
     or deselected but does not otherwise change in appearance or position.)
        If this drawing requires info about chunk which it is useful to precompute (as an optimization),
     that info should be computed by our compute_memo method and will be passed as the memo argument
     (whose format and content is whatever self.compute_memo returns). That info must not depend on
     the highlighted variable or on whether the chunk is selected.
     """
     if not chunk.atoms:
         return
     end1, end2, radius, color = memo
     if highlighted:
         color = ave_colors(0.5, color, env.prefs[chunkHighlightColor_prefs_key]) #e should the caller compute this somehow?
     drawcylinder(color, end1, end2, radius, capped = True)
     return
Пример #16
0
    if len(color) == 3:
        r, g, b = color
        a = 1.0
    elif len(color) == 4:
        r, g, b, a = color
    else:
        assert len(color) in (3, 4)
    return (
        float(r), float(g), float(b), float(a)
    )  # alpha will get discarded by ave_colors for now, but shouldn't crash [070215]


#e define brown somewhere, and new funcs to lighten or darken a color

lightblue = ave_colors(
    0.2, blue, white
)  # WARNING: differs from at least one version of this in constants.py
halfblue = ave_colors(0.5, blue, white)


def translucent_color(color, opacity=0.5):  #e refile with ave_colors
    """
    Make color (a 3- or 4-tuple of floats) have the given opacity (default 0.5, might be revised);
    if it was already translucent, this multiplies the opacity it had.
    """
    if len(color) == 3:
        c1, c2, c3 = color
        c4 = 1.0
    else:
        c1, c2, c3, c4 = color
    return (c1, c2, c3, c4 * opacity)
Пример #17
0
    #bruce 071102 renamed vv -> exprs_globals and moved it out of this file,
    # to avoid import cycle

exprs_globals.reload_counter += 1
    # the first time we load testdraw, this changes that counter
    # from -1 to 0; when we reload it, it changes it to 1, 2, etc;
    # thus the counter counts the number of reloads of testdraw.

### a lot of the following constants are probably obs here, redundant with ones now defined in exprs module [070408 comment]

printdraw = False # debug flag

from graphics.drawing.texture_fonts import ensure_courierfile_loaded

##lightblue = ave_colors( 0.2, blue, white)
halfblue = ave_colors( 0.5, blue, white)
##purple = ave_colors(0.5, red, blue)

def translucent_color(color, opacity = 0.5): #e refile with ave_colors
    """Make color (a 3- or 4-tuple of floats) have the given opacity (default 0.5, might be revised);
    if it was already translucent, this multiplies the opacity it had.
    """
    if len(color) == 3:
        c1, c2, c3 = color
        c4 = 1.0
    else:
        c1, c2, c3, c4 = color
    return (c1, c2, c3, c4 * opacity)

trans_blue = translucent_color(halfblue)
trans_red = translucent_color(red)
Пример #18
0
    """
    Make sure color is a 4-tuple of floats.
    (Not a numeric array -- too likely to hit the == bug for those.)
    """
    if len(color) == 3:
        r,g,b = color
        a = 1.0
    elif len(color) == 4:
        r,g,b,a = color
    else:
        assert len(color) in (3,4)
    return ( float(r), float(g), float(b), float(a)) # alpha will get discarded by ave_colors for now, but shouldn't crash [070215]

#e define brown somewhere, and new funcs to lighten or darken a color

lightblue = ave_colors( 0.2, blue, white) # WARNING: differs from at least one version of this in constants.py
halfblue = ave_colors( 0.5, blue, white)

def translucent_color(color, opacity = 0.5): #e refile with ave_colors
    """
    Make color (a 3- or 4-tuple of floats) have the given opacity (default 0.5, might be revised);
    if it was already translucent, this multiplies the opacity it had.
    """
    if len(color) == 3:
        c1, c2, c3 = color
        c4 = 1.0
    else:
        c1, c2, c3, c4 = color
    return (c1, c2, c3, c4 * opacity)

trans_blue = translucent_color(halfblue)
Пример #19
0
class DraggableObject(DelegatingInstanceOrExpr):
    """DraggableObject(obj) is a wrapper which makes any model object draggable (###doc the details),
    and also helps provides a context menu specific to obj.
    [##e It may be extended to make obj click-selectable or even region-selectable, at the proper times, too.]
       WARNING: Experimental -- API/organization will surely change,
    integrating not only rotation, but click to select, etc.
    The resulting wrapper will typically be applied by model->view macros.
       In fact, it's more complicated than that: the selection-click controller will wrap single objects,
    but the draggability wrapper is more likely to be organized something like this,
    where the named localvars refer to sets whose membership depends on selection:
      visibles = DisplayListChunk(fixed_stuff) + distortedly_moving_stuff +
        DraggableObject(DisplayListChunk(dragging_as_a_unit_stuff)).
    The distortedly_moving_stuff includes things like external bonds between fixed and being-dragged atoms,
    which have to stretch in individual ways during the drag.
    """
    # args
    obj = Arg(ModelObject)

    # options
    #e selectable = Option(bool, True, doc = "whether to let this object be click-selectable in the standard way") [see selected]
    rotatable = Option(
        bool,
        True,
        doc=
        "whether to let this object rotate about its center using MMB/Alt/Option drags"
    )
    # This is intended to implement an initial subset of the "New motion UI" [070225 new feature]
    # [###e default will change to False after testing]
    # WARNING: as an optim, we might require that this be True initially, or even always (i.e. be a constant),
    # if it will ever be True during the Instance's lifetime -- not sure. If so, this requirement must at least be documented,
    # and preferably error-detected. ###FIX (if we do require that)
    # experimental kluge 070314
    _kluge_drag_handler = Option(
        Anything,
        _self,
        doc=
        "object to receive our on_press/on_drag/on_release events, in place of _self"
    )

    # state
    selected = State(bool,
                     False)  ###KLUGE test stub, only set when debug070209
    translation = Option(
        Vector,
        V(0, 0, 0),  #070404
        doc=
        "initial translation [WARNING: might merge with misnamed self.motion (a State attr) to make a StateOption]"
    )
    motion = State(
        Vector, _self.translation
    )  # publicly visible and settable (but only with =, not +=).
    ##e rename to translation? (by making it a StateOption)
    ##e (or deprecate the concept of StateOption but make any State settable initially by a special option not just with same name?
    ##   eg either initial_attr or initial_data = [something with dict or attr access to the data] ??)
    ##e NOTE [070404]: I miscoded translation as Arg rather than Option, and said StateArg rather than StateOption in docstring,
    # though intending only named uses of it -- is this evidence that Arg / Option / Parameter should be the same,
    # that Option should be the default meaning, and positional arglists should be handled differently and as an extra thing
    # (eg like the old _args feature -- which leads to clearer code when subclassing)?? Guess: quite possibly, but needs more thought.
    # WARNING: use of += has two distinct bugs, neither error directly detectable:
    # - changes due to += (or the like) would not be change tracked.
    #   (But all changes to this need to be tracked, so our drawing effects are invalidated when it changes.)
    # - value might be a shared Numeric array -- right now use of = to set this doesn't copy the array to make us own it.
    rotation = State(Quat,
                     Q(1, 0, 0,
                       0))  #070225 new feature -- applied around object center

    # experiment 070312: works (see test_StateArrayRefs_2) ###doc ##e clean up ##k is it making the usual case slow in a significant way??
    delta_stateref = Option(StateRef,
                            call_Expr(LvalueFromObjAndAttr, _self, 'motion'),
                            doc="#doc")
    use_motion = delta_stateref.value

    # geometric attrs should delegate to obj, but be translated by motion as appropriate.
    ##e Someday we need to say that in two ways:
    # - the attrs in the "geometric object interface" delegate as a group (rather than listing each one of them here)
    # - but when they do, they get passed through a change-of-coords boundary, and they know their own coordsystems,
    #   so the right thing happens.
    # But for now we have no way to say either thing, so we'll add specific formulas for specific attrs as needed. [070208]
    ##e Note that before the obj types know how to translate due to type, the interface (which knows the attrs indivly)
    # could know it. So, delegation of all attrs in an interface can be done by special glue code which also knows
    # how to transform them in useful ways, by knowing about those attrs and what transforms are useful.
    # This is useful enough to keep, even once its default transforms can come from declared attr types &
    # values knowing their coordsys. It adds value to that since interfaces can always know special cases about specific attrs.

    if 0:
        # update 070209 late: try doing this in Translate below, with the other involved exprs delegating as usual... ####k
        center = obj.center + motion
        # following comments are from when the above was 'if 1' a day or two ago -- still relevant since general [##e refile??]:

        # Problem: won't work for objs with no center! Solution for now: don't try to eval the self attr then.
        # Not perfect, since what ought to be AttributeError will turn into some other exception.
        ##e One better solution would involve declared interfaces for obj, and delegation of all attrs in interfaces
        # of a certain kind (geometric), so if obj met more interfaces and had more attrs, those would be included,
        # but if not, we would not have them either.
        ##e Or alternatively, we could provide an easy way to modify the above formula
        # to specify a condition under which center should seem to exist here, with that cond being whether it exists on obj.
        ### A potential problem with both solutions: misleasing AttributeError messages, referring to self rather than obj,
        # would hurt debugging. So we probably want to reraise the original AttributeError in cases like that, whatever
        # the way in which we ask for that behavior. That means one construct for "passing along attr missingness",
        # but *not* a composition of one construct for saying when this attr is there, and one for asking whether another is.

        # Note: can't we delegate center (& other geometry) through the display delegate below, if Highlightable passes it through
        # and Translate does the coordinate transformation? ###e

    # appearance

    obj_name = call_Expr(node_name, obj)  #070216
    # Note: node_name is used in MT_try2; it's better than using _e_model_type_you_make (for our use in sbar_text, below).
    # BTW, node_name is a helper function associated with ModelTreeNodeInterface (informal so far).
    #
    # If you want to wrap an object with extra info which specifies its node_name, use ... what? ###k hmm, I forget if there
    # is a way partway through being implemented...
    # maybe WithAttributes( Center(Rect(0.4, 0.4, green)), mt_name = "green rect #n" )... i guess yes, ### TRY IT
    # should clean this situation up, use Adaptor term for that pattern
    # of interface conversion, etc... [070404 updated comment]

    # (Note [070216]: I had a bug when I had a comma after the above def. This made obj_name, included directly in another expr,
    #  turn into a singleton tuple of the call_Expr value, but when included as _self.obj_name (normally equivalent to obj_name),
    #  turn into something else (since eval of a tuple must not descend inside it -- guess, might have been a tuple_Expr).
    #  I'm not sure how to detect this error except to stop permitting tuple(expr) to be allowed as abbrev for a tuple_Expr --
    #  which seems too inconvenient -- or to figure out a way for the formula scanner to detect it (and make it illegal as the
    #  rhs of an assignment into a class namespace -- probably ok to make illegal). ##DOIT sometime)

    obj_drawn = If(
        selected,
        Overlay(obj, Rect(
            1, 1,
            blue)),  ##### WRONG LOOK for selected, but should work [070209]
        #BUG: Rect(1,lightblue) is gray, not light blue -- oh, it's that failure to use type to guess which arg it is!
        obj)

    sbar_text_for_maybe_selected = If(selected, " (selected)", "")

    delegate = Highlightable(
        # Note 070317: since Highlightable is outside of RotateTranslate, its coordsys doesn't change during a drag,
        # thus avoiding, here in DraggableObject, the bug that came up in the first implem of DraggablyBoxed,
        # whose highlightable rectframe was moving during the drag, but was also being used to supply the coordsys
        # for the drag events. This bug is actually in SimpleDragBehavior above, and the fix will be confined to that class.
        #
        # plain appearance
        RotateTranslate(obj_drawn, rotation, use_motion),
        # hover-highlighted appearance (also used when dragging, below)
        highlighted=RotateTranslate(
            DisplayListChunk(
                # This inner DisplayListChunk, in theory, might help make up for current implem of disabling them inside WarpColors...
                # in my tests, it didn't make a noticeable difference (probably since obj is fast to draw). [070216 2pm]
                #
                # Note: if obj has its own DisplayListChunk, does that notice the value of whatever dynenv var is altered by WarpColors??
                # We'll have to make it do so somehow -- perhaps by altering the displist name by that, or turning off displists due to it.
                # For this initial implem [070215 4pm], we did the latter.

                ## WarpColors( obj_drawn, lambda color: ave_colors( 0.3, white, color ) ), # whiten the color -- ugly
                ## WarpColors( obj_drawn, lambda color: yellow ), # "ignore color, use yellow" -- even uglier
                ## WarpColors( obj_drawn, lambda color: ave_colors( 0.2, white, color ) ), # whiten, but not as much -- less ugly
                WarpColors(
                    obj_drawn, lambda color: ave_colors(0.1, white, color)
                ),  # whiten, even less -- even less ugly [best so far]
                ## WarpColors( obj_drawn, lambda color: ave_colors( 0.2, gray, color ) ), # gray-end instead of whiten -- not quite as good
                ## WarpColors( obj_drawn, lambda color: (color[1],color[2],color[0]) ), # permute the hues...
            ),
            rotation,
            use_motion),
        pressed=_my.highlighted,  # pressed_in and pressed_out appearance
        ###BUG (when we gave pressed_in and pressed_out separately -- ###UNTESTED since then):
        # this pressed_out appearance seems to work for DNA cyls but not for draggable PalletteWell items! [070215 4pm]
        ## sbar_text = format_Expr( "Draggable %r", obj ),
        ##e should use %s on obj.name or obj.name_for_sbar, and add those attrs to ModelObject interface
        # (they would delegate through viewing wrappers on obj, if any, and get to the MT-visible name of the model object itself)
        ##e [Can we implem something like try_Expr( try1, try2, try3) which evals to the first one evalling without an exception??
        # But that doesn't seem safe unless you have to list the permissible exceptions (like in Python try/except).
        # The use of this here (temporary) would be to look for obj.name, then try a different format_Expr if that fails.
        # getattr(obj, 'name', dflt) would get us by, but would not as easily permit alternate format_Exprs in the two cases.]
        ##        # older highlighted or pressed_in appearance (not sure which it was before I inserted the args above this) -- zapping it 070216 late
        ##        If( eval_Expr(constant_Expr(constant_Expr(debug070209))),
        ##                ###e need option or variant of If to turn off warning that cond is a constant: warn_if_constant = False??
        ##                # also make the printed warning give a clue who we are -- even if we have to pass an option with the text of the clue??
        ##            Translate( Boxed(obj), motion),
        ##                #######070209 TEST THIS KLUGE -- note it does not include selected appearance
        ##                    # (but HL might incl it anyway? sometimes yes sometimes no, not sure why that would be -- ah, it depends on whether
        ##                    # mouse is over the moved object (which is silly but i recall it as happening in regular ne1 too -- ###BUG)
        ##                #e not good highlight form
        ##                ####BUG: the layout attrs (lbox attrs, eg bleft) are apparently not delegated, so the box is small and mostly obscured
        ##            Translate( obj, motion)
        ##         ),
        ## _obj_name = call_Expr(node_name, obj), #070216
        # that can't work yet -- it tries to define a new attr in an object (this Highlightable) from outside,
        # accessible in other option formulae as _this(Highlightable)._obj_name...
        # instead, I moved this def into _self (far above) for now.
        sbar_text=format_Expr("%s%s (can be dragged)", obj_name,
                              sbar_text_for_maybe_selected),  # revised 070216
        # This adds some info to sbar_text about what we can do with obj (drag, select, etc)...
        #e someday, maybe the dynenv wants to control how info of several kinds turns into actual sbar_text.
        ##        on_press = _self.on_press,
        ##        on_drag = _self.on_drag,
        ##        on_release = _self.on_release,
        on_press=_kluge_drag_handler.on_press,
        on_drag=_kluge_drag_handler.on_drag,
        on_release=_kluge_drag_handler.on_release,
        cmenu_maker=
        obj  ###e 070204 experimental, API very likely to be revised; makes Highlightable look for obj.make_selobj_cmenu_items
    )

    ### DESIGN Q: do we also include the actual event binding (on_press and on_drag) -- for now, we do --
    # or just supply the Draggable interface for moving self.obj
    # and let the caller supply the binding to our internal "cmd" drag_from_to?? ###e

    # has Draggable interface (see demo_polygon.py for explan) for changing self.motion

    def _cmd_drag_from_to(
            self, p1,
            p2):  #e rename drag_hitpoint_from_to? (in the Draggable Interface)
        """[part of the Draggable Interface; but this interface
        is not general enough if it only has this method -- some objects need more info eg a moving mouseray, screenrect, etc.
        Either this gets passed more info (eg a dragevent obj),
        or we keep the kluge of separate self dynenv queries (like for mousepoint and screenrect),
        or we provide glue code to look for this method but use more general ones if it's not there. ###e
        BTW, that interface is a myth at present; all actual dragging so far is done using on_press/on_drag/on_release,
        with this method at best used internally on some objs, like this one. [as of 070313]]
        """
        if self._delegate.altkey:
            assert 0, "should no longer be called"
##            ###KLUGE, just a hack for testing Highlightable.altkey [070224]; later, do rotation instead (per "New motion UI")
##            # (Is it also a ###KLUGE to detect altkey within this method, rather than caller detecting it and passing a flag
##            #  or calling a different method? YES.)
##            ## self.motion = self.motion + (p2 - p1) * -1
##            # change self.rotation... by a quat which depends on p2 - p1 projected onto the screen... or the similar mouse x,y delta...
##            ###KLUGE: assume DZ is toward screen and scale is standard....
##            # wait, ###BUG, we don't even have enough info to do this right, or not simply, starting from p1, rather than startpoint...
##            dx,dy,dz = p2 - p1
##            rotby = Q(p1,p2) ###WRONG but ought to be legal and visible and might even pretend to be a trackball in some cases and ways
##            self.rotation = self.rotation + rotby
##            # print "%r motion = %r rotation = %r" % (self, self.motion, self.rotation)
        else:
            ## self.motion = self.motion + (p2 - p1)
            self.delta_stateref.value = self.delta_stateref.value + (p2 - p1)
        return

    ##e something to start & end the drag? that could include flush if desired...

    # can push changes into the object

    def flush(self, newmotion=V(0, 0, 0)):
        self.delegate.move(
            self.use_motion + newmotion
        )  ###k ASSUMES ModelObject always supports move (even if it's a noop) ###IMPLEM
        # note, nothing wrong with modelobjects usually having one coordsys state which this affects
        # and storing the rest of their data relative to that, if they want to -- but only some do.
        ## self.motion = V(0,0,0)
        self.delta_stateref.value = V(0, 0, 0)

    # if told to move, flush at the same time

    def move(self, motion):
        self.flush(motion)
        return

    # on_press etc methods are modified from demo_polygon.py class typical_DragCommand

    #e note: it may happen that we add an option to pass something other than self to supply these methods.
    # then these methods would be just the default for when that was not passed
    # (or we might move them into a helper class, one of which can be made to delegate to self and be the default obj). [070313]

    def on_press(self):
        point = self.current_event_mousepoint(
        )  # the touched point on the visible object (hitpoint)
        # (this method is defined in the Highlightable which is self.delegate)
        self.oldpoint = self.startpoint = point
        # decide type of drag now, so it's clearly constant during drag, and so decision code is only in one place.
        # (but note that some modkey meanings might require that changes to them during the same drag are detected [nim].)
        if self._delegate.altkey:
            self._this_drag = 'free x-y rotate'
            #e more options later, and/or more flags like this (maybe some should be booleans)
            ###e or better, set up a function or object which turns later points into their effects... hmm, a DragCommand instance!
            ##e or should that be renamed DragOperation??
            self._screenrect = (ll, lr, ur,
                                ul) = self.screenrect(self.startpoint)
            # these points should be valid in our delegate's coords == self's coords
            self._dx = _dx = norm(lr - ll)
            self._dy = _dy = norm(ur - lr)
            self._dz = cross(
                _dx, _dy
            )  # towards the eye (if view is ortho) (but alg is correct whether or not it is, i think)
            ###k check cross direction sign
            self._scale = min(vlen(lr - ll), vlen(ur - lr)) * 0.4
            # New motion UI suggests that 40% of that distance means 180 degrees of rotation.
            # We'll draw an axis whose length is chosen so that dragging on a sphere of that size
            # would have the same effect. (Maybe.)
            self._objcenter = self._delegate.center
            self.startrot = +self.rotation
        else:
            self._this_drag = 'free x-y translate'
        if debug070209:
            self.ndrags = 0
        return

    def on_drag(self):
        # Note: we can assume this is a "real drag", since the caller (ultimately a selectMode method in testmode, as of 070209)
        # is tracking mouse motion and not calling this until it becomes large enough, as the debug070209 prints show.
        oldpoint = self.oldpoint  # was saved by prior on_drag or by on_press
        point = self.current_event_mousepoint(plane=self.startpoint)
        if debug070209:
            self.ndrags += 1


##            if (self.ndrags == 1) or 1:
##                print "drag event %d, model distance = %r, pixel dist not computed" % (self.ndrags, vlen(oldpoint - point),)
        if self._this_drag == 'free x-y rotate':
            # rotate using New motion UI
            #  [probably works for this specific kind of rotation, one of 4 that UI proposes;
            #   doesn't yet have fancy cursors or during-rotation graphics; add those only after it's a DragCommand]
            # two implem choices:
            # 1. know the eye direction and the screen dims in plane of startpoint, in model coords; compute in model coords
            # 2. get the mouse positions (startpoint and point) and screen dims in window x,y coords, compute rotation in eye coords,
            #   but remember to reorient it to correspond with model if model coords are rotated already.
            # Not sure which one is better.
            #   In general, placing user into model coords (or more precisely, into object local coords) seems more general --
            # for example, what if there were several interacting users, each visible to the others?
            # We'd want each user's eye & screen to be visible! (Maybe even an image of their face & screen, properly scaled and aligned?)
            # And we'd want their posns to be used in the computations here, all in model coords.
            # (Even if zoom had occurred, which means, even the user's *size* is quite variable!)
            #   I need "user in model coords" for other reasons too, so ok, I'll do it that way.
            #
            # [Hey, I might as well fix the bug in current_event_mousepoint which fakes the center of view, at the same time.
            # (I can't remember its details right now, but I think it assumed the local origin was the cov, which is obviously wrong.)
            # (But I didn't look at that code or fix that bug now.)]
            vec = point - self.startpoint
            uvec = norm(vec)  #k needed??
            axisvec = cross(
                self._dz, uvec
            )  # unit length (suitable for glRotate -- but we need to use it to make a quat!)
            axisvec = norm(
                axisvec)  # just to be sure (or to reduce numerical errors)
            scale = self._scale
            draw_axisvec = axisvec * scale  #e times some other length constant too?
            center = self._objcenter
            self.axisends = (center - axisvec, center + axisvec
                             )  # draw a rotation axis here ###e
            self.degrees = degrees = vlen(
                vec
            ) / scale * 180.0  # draw a textual indicator with degrees (and axisvec angle too) ###e
            ###e or print that info into sbar? or somewhere fixed in glpane? or in glpane near mouse?
            # now set self.rotation to a quat made from axisvec and degrees
            theta = degrees / 360.0 * 2 * pi
            # print "axisvec %r, degrees %r, theta %r" % (axisvec ,degrees,theta)
            rot = Q(axisvec, theta)
            self.rotation = self.startrot + rot  # note use of self.startrot rather than self.rotation on rhs
            # avoid += to make sure it gets changed-tracked -- and since it would be the wrong op!

        elif self._this_drag == 'free x-y translate':
            self._cmd_drag_from_to(
                oldpoint, point)  # use Draggable interface cmd on self
        else:
            assert 0
        self.oldpoint = point
        return

    def on_release(self):
        #e here is where we'd decide if this was really just a "click", and if so, do something like select the object,
        # if we are generalized to become the wrapper which handles that too.
        if debug070209:
            if not self.ndrags:
                # print "release (no drags)" # ie a click
                self.selected = not self.selected  ###KLUGE test stub
            else:
                pass  # print "release after %d drags" % self.ndrags
            self.ndrags = 0
        pass

    pass  # end of class DraggableObject
Пример #20
0
class _MT_try2_node_helper(DelegatingInstanceOrExpr):
    """
    [private helper expr class for MT_try2]
    One MT item view -- specific to one node, one whole MT, and one (possibly time-varying) position with it.
    """
    # args ####e REORDER THEM
    node = Arg(ModelNode, doc = "any node that needs to be displayed in this MT")
        ###e NOTE: type coercion to this is nim; while that's true, we use helper functions like node_name(node) below;
        # once type coercion is implemented
        # (or simulated by hand by wrapping this arg with a helper expr like ModelTreeNode_trivial_glue),
        #  we could instead use node.mt_name, etc.)
    mt = Arg(MT_try2, doc = "the whole MT view, in which we store MT items for nodes, and keep other central state or prefs if needed")
    name_suffix = Option(str, "")
    initial_open = Option(bool, False, doc = "initial value of boolean state 'open'; only used when this item is first created")
        ##e should ask the node itself for the initial value of open (e.g. so new groups, trying to start open, can do so),
        # and also advise it when we open/close it, in case it wants to make that state persistent in some manner
        
    # WARNING: compare to MT_try1 -- lots of copied code after this point
    # WARNING: the comments are also copied, and not yet reviewed much for their new context! (so they could be wrong or obs) ###k
    
    # state refs
    open = State(bool, initial_open)
    
    # other formulae
    ###e optim: some of these could have shared instances over this class, since they don't depend on _self; should autodetect this
    # Note, + means openable (ie closed), - means closable (ie open) -- this is the Windows convention (I guess; not sure about Linux)
    # and until now I had them reversed. This is defined in two files and in more than one place in one of them. [bruce 070123]
    open_icon   = Overlay(Rect(0.4), TextRect('-',1,1))
    closed_icon = Overlay(Rect(0.4), TextRect('+',1,1))
    openclose_spacer = Spacer(0.4)
        #e or Invisible(open_icon); otoh that's no simpler, since open_icon & closed_icon have to be same size anyway

    # the openclose icon, when open or close is visible (i.e. for openable nodes)
    openclose_visible = Highlightable(
        If( open, open_icon, closed_icon ),
        on_press = Set(open, not_Expr(open)),
        sbar_text = getattr_Expr( _self, '_e_serno') #070301 this permits finding out how often MT gets remade/shared
            # (results as of 070301: remade when main instance is, even if going back to a prior testexpr, out of _19i & _30i)
     )
    
    openclose_slot = If( call_Expr(node_openable, node), openclose_visible, openclose_spacer )


    if 0:
        # cross-highlighting experiment, 070210, but disabled since approach seems wrong (as explained in comment)
        yellow = DZ = 'need to import these'
        indicator_over_obj_center = Center(Rect(0.4, 0.4, yellow))
        position_over_obj_center = node.center + DZ * 3 ###BUG: DZ does not point towards screen if trackballing was done
            ###STUB:
            # - should be drawn in a fixed close-to-screen plane, or cov plane (if obscuring is not an issue),
            #   - so indicator size is constant in pixels, even in perspective view (I guess),
            #   - also so it's not obscured (especially by node itself) -- or, draw it in a way visible behind obscuring things (might be a better feature)
            # - what we draw here should depend on what node is
            # - we also want to draw a line from type icon to node indicator (requires transforming coords differently)
            # - needs to work if node.center is not defined (use getattr_Expr - but what dflt? or use some Ifs about it)
        pointer_to_obj = DrawInCenter( Translate( indicator_over_obj_center, position_over_obj_center))
            #bug: Translate gets neutralized by DrawInCorner [fixed now]
            ###BUG: fundamentally wrong -- wrong coord system. We wanted DrawInAbsCoords or really DrawInThingsCoords,
            # but this is not well-defined (if thing drawn multiply) or easy (see comments about the idea in projection.py).
    else:
        # What we want instead is to set a variable which affects how the obj is drawn.
        # If this was something all objs compared themselves to, then all objs would track its use (when they compared)
        # and therefore want to redraw when we changed it! Instead we need only the involved objs (old & new value) to redraw,
        # so we need a dict from obj to this flag (drawing prefs set by this MT). Maybe the app would pass this dict to MT_try2
        # as an argument. It would be a dict of individually trackable state elements. (Key could be node_id, I guess.)
        # ### TRY IT SOMETIME -- for now, cross-highlighting experiment is disabled.
        pointer_to_obj = None

    # selection indications can use this
    node_is_selected = call_Expr( mt_node_selected, node)
    kluge_icon_color = If( node_is_selected, blue, green)
    sbar_format_for_name = If( node_is_selected, "%s (selected)", "%s")
    
    ###STUB for the type_icon ##e the Highlightable would be useful on the label too
    icon = Highlightable(
        Rect(0.4, 0.4, kluge_icon_color), ##stub; btw, would be easy to make color show hiddenness or type, bfr real icons work
        Overlay( Rect(0.4, 0.4, ave_colors(0.1, white, kluge_icon_color)),
                 #070216 mix white into the color like DraggableObject does
                 pointer_to_obj ),
        sbar_text = format_Expr( sbar_format_for_name, call_Expr(node_name, node) )
     )
    
    ##e selection behavior too

    label = DisplayListChunk(
        # added DisplayListChunk 070213 late -- does it speed it up? not much; big new-item slowness bug remains. retain, since doesn't hurt.
        TextRect( call_Expr(node_name, node) + name_suffix )
     )
        ###e will need revision to Node or proxy for it, so node.name is usage/mod-tracked
        ##e selection behavior too --
        #e probably not in these items but in the surrounding Row (incl invis bg? maybe not, in case model appears behind it!)
        ##e italic for disabled nodes
        ##e support cmenu
    
    delegate = SimpleRow(
        CenterY(openclose_slot),
        SimpleColumn(
            SimpleRow(CenterY(icon), CenterY(label)),
                #070124 added CenterY, hoping to improve text pixel alignment (after drawfont2 improvements) -- doesn't work
            If( open,
                _MT_try2_kids_helper( call_Expr(node_kids, node) , _self.mt ), # 070218 added _self.mt -- always intended, first used now
                None
                    # Note: this None used to be Spacer(0), due to a bug mentioned in a comment in ToggleShow.py
                    # (but unfortunately not explained there -- it just says "I wanted None here, but it exposes a logic bug,
                    # not trivial to fix, discuss in If or Column" -- my recollected bug-theory is described just below).
                    # On 070302 I confirmed that None seems to work (even in testexpr_18i with a group of 2 chunks, plus two more below).
                    # I don't fully know why it works, since I thought the bug was that SimpleColumn's None specialcase
                    # didn't run, since the element was not None but the If, and then delegating lbox attrs to None didn't work.
                    # (Fixable by using the newer If that evals, but for some reason that's not yet standard, I guess just because
                    # I didn't have time to test it enough or think it through fully re ipath or instance caching or something.)
                    # But as long as it works, use it -- ask Qs later. A recent perhaps-related change: None is allowed in drawkid.
                    # (A memory scrap -- does instantiating None conceivably produce a spacer?? ###k)
             )
         )
     )
    pass # end of class _MT_try2_node_helper