コード例 #1
0
ファイル: Rect.py プロジェクト: elfion/nanoengineer
class Sphere(
        Widget2D
):  # the superclass is to give it a 2D lbox. We'll need to think about whether it needs renaming.
    # or maybe this super will be Widget3D and that will inherit Widget2D?? hmm...
    """
    Sphere(radius, color, center) represents a spherical surface
    of the given radius (default 1), color (default gray), and center (default the local origin) [partly nim if not #e].
    [There is also an undocumented option, detailLevel.]
    """
    # args
    radius = ArgOrOption(Width, 1)
    color = ArgOrOption(Color, gray)
    center = ArgOrOption(
        Position,
        ORIGIN)  # this is not yet supported in determining the layout box,
    # since I'm tempted to say, if this is supplied, turn into a macro, Translate(Sphere(...))
    # so I won't need to uglify those calcs. Not yet sure quite how to most easily organize that --
    # best would be a general way to make some options or uses of them "turn into macros",
    # sort of like in a math paper saying "w/o loss of generality, assume center == ORIGIN". ###e
    detailLevel = Option(
        int, 2
    )  #k guess: 1 or 2 or 3, i think -- undocumented since needs a better name and maybe arg meaning
    # formulae
    bright = _self.radius
    bleft = _self.radius
    btop = _self.radius
    bbottom = _self.radius

    def draw(self):
        drawsphere(self.fix_color(self.color), self.center, self.radius,
                   self.detailLevel)

    pass
コード例 #2
0
ファイル: Rect.py プロジェクト: elfion/nanoengineer
class RectFrame(Widget2D):
    """
    RectFrame(width, height, thickness, color) is an empty rect (of the given outer dims)
    with a filled border of the given thickness (like a picture frame with nothing inside).
    """
    # args
    width = Arg(Width, 10)
    # NOTE: Widget2D now [061114] defines width from bright & bleft (risking circularity), & similarly height;
    # I think that's ok, since we zap that def here, so the set of active defs is not circular
    # (and they're consistent in relations they produce, too, as it happens); a test (_7b) shows no problem.
    height = Arg(Width, width)
    thickness = ArgOrOption(Width, 4 * PIXELS)
    color = ArgOrOption(Color, white)
    # layout formulas
    bright = _self.width
    btop = _self.height

    ## debug code from 061110 removed, see rev 1.25 for commented-out version:
    ## override _e_eval to print _self (in all envs from inner to outer) to see if lexenv_Expr is working
    def draw(self):
        glDisable(GL_CULL_FACE)
        draw_filled_rect_frame(ORIGIN, DX * self.width, DY * self.height,
                               self.thickness, self.fix_color(self.color))
        glEnable(GL_CULL_FACE)

    pass
コード例 #3
0
class ChoiceButton(InstanceMacro):
    """ChoiceButton(choiceval, choiceref, content, background, background_off) [most args optional]
    displays and permits control of a choice variable stored externally in choiceref,
    looking like Overlay(background, content) or Overlay(background_off, content)
    when its own choice value is chosen or unchosen (ie equal or unequal to the stored one), respectively.
       Most args are optional with useful defaults, or can be given as simpler convenience types (eg colors or text);
    all args but choiceval can be given as named options, which is useful for customization.
       (Example: it's useful to put several of these with the same choiceref but different choicevals into a Column.
    This can be done by mapping one customized variant over a list of choicevals.)
       The choosing-action occurs on_press of entire thing -- this is not yet changeable
    (to other kinds of button actions), but should be. #e
    """
    # args
    choiceval = Arg(Anything)
        #e declare it as having to be constant per-Instance? Or can it legally vary?? I guess it could;
        # and I guess it's no error, just weird, for two of these (eg in a column) to share the same choiceval;
        # in fact, if they're physically separated it's not even weird.

    sbar_text = Option(str, format_Expr("%s", _self.choiceval)) # mouseover text for statusbar

    choiceref = ArgOrOption(StateRef) ###k need value-type??

    content = ArgOrOption(stubtype, TextRect(format_Expr("%s", _self.choiceval)) ) # Widget2D or something "displayable" in one (eg text or color); defaults to displayed choiceval;
        # can caller pass a formula in terms of the other options to _self?
        # Maybe, but not by saying _self! _this(ChoiceButton) == _my? [yes -- _my is now implemented, 061205]

    background = ArgOrOption(stubtype, Rect(_self.width, _self.height, lightblue) ) # Widget2D, or color (used in Rect a bit larger than content)
    
    background_off = ArgOrOption(stubtype, Spacer(_self.width, _self.height)) # ditto, defaults to transparent
        ##k problem: what we want is to compute an lbox and then use this here in the spacer... or align the content... or ....
        ##e note that a lot of people find it more convenient to pass around a size, or even pass around a rect,
        # than to always work with 4 or 6 rect-related attrs...

    # formulae
    chosen = eq_Expr( choiceref.value, choiceval) #k
    ## print "chosen is",chosen

    ###k assume useful conversions of named options happened already
    ###e use _value; is it as simple as renaming it delegate and using DelegatingMixin?? Can InstanceMacro do it for us??
    # [if we use one of those, be careful not to inherit from Widget2D here, due to its lbox defaults!]
    _value = Highlightable( Overlay( SpacerFor(Boxed(content)),
                                         # kluge to make room around content in _self.width and _self.height,
                                         # and make those non-circular; WRONG because won't be properly aligned with backgrounds,
                                         # even if content itself would be;
                                         # could fix by shifting, but better to figure out how to have better rectlike size options ###e
                                     Overlay( ###KLUGE since "Overlay is a stub which only works with exactly two args"
                                         If(chosen, background, background_off),
                                         content ),
                                     ),
                            ## old code: on_press = SetStateRefValue(choiceref, choiceval),
                            # try this code 061211 1113a -- it works, use it:
                            on_press = Set(choiceref.value, choiceval),
                                ##e probably best to say Set(choiceref.value, choiceval), but I think that's nim -- not sure --
                                # should retest it after Set is revised later today to work with arg1 being lval eg getattr_Expr [061204]
                            sbar_text = sbar_text
                           )
    pass # end of class ChoiceButton
コード例 #4
0
ファイル: staterefs.py プロジェクト: elfion/nanoengineer
class PrefsKey_StateRef(InstanceOrExpr): # guess, 061204
    """
    return something which instantiates to something with .value which is settable state,
    shared with env.prefs[key],
    properly usage-tracked in a compatible way with the rest of NE1
    """
    prefs_key = Arg(str) # note: it's probably ok if this is a time-varying formula, tho I don't know why it would be useful.
    defaultValue = ArgOrOption(Anything, None) ##e default of this should depend on type, in same way it does for Arg or Option --
        # nonetheless it's so much more common to specify a default value than a type, that I decided to put default value first.
        #
        ##BUG (optimization issue only, and not yet important in practice):
        # [re-noticed 070228 -- also the topic of the printnim and older comment just below!]
        # this defaultValue should be evaluated with usage tracking discarded, unless prefs_key changes,
        # in which case it should be reevalled once (doable if its eval pretends to use prefs_key) -- except ideally the value
        # for older prefs_keys being reused again would be cached (eg using MemoDict) (not important in practice).
        # This bug is not yet serious since the actual args so far are constants [as of 070228].
        # It's purely an optimization issue; but the worst-case effects are that the default value changes a lot more
        # often than the prefs value itself, causing needless inval propogation into the get_value caller,
        # and thus needless recomputation of an arbitrary amount of memoized results which care about the prefs value.
        # (But for all I know, the usual constant case would slow down due to the overhead of discarding usage tracking.
        #  Actually that's not an issue since we'd also rarely recompute it, not nearly on every get_value.) 
    printnim("need a way to declare that this arg should not be usage-tracked, or have its use as default val do that for that use")
        # [older version of the comment above; contains still-useful suggestions:]
        ###e we need a way to declare that this arg should not be usage-tracked re time-variation!!! or to assert it uses nothing.
        # right now, if it uses something that will silently cause bugs, probably just invisible performance bugs from extra invals.
        # CAN THERE BE A GENERAL SOLN based on what we use this for (default vals of things that don't care about updates)?
        # that is more correct in principle, since that's what matters -- eg what if someone added another use of the same arg. ###e
    type = ArgOrOption(Type, Anything) # this arg is not yet passed by anything, or used in this implem;
        # moved type from arg2->arg3, and Arg -> ArgOrOption (as being after ArgOrOption arg2 ought to force anyway), 061211 night
    def get_value(self):
        #e should coerce this to self.type before returning it -- or add glue code wrt actual type, or....
        prefs_key = self.prefs_key
        assert type(prefs_key) == type("") #k redundant?
        import foundation.env as env
        return env.prefs.get( prefs_key, self.defaultValue ) # note: this computes defaultValue at least once, even if not needed.
    def set_value(self, val): 
        #e should coerce that to self.type, or add glue code...
        prefs_key = self.prefs_key
        assert type(prefs_key) == type("") #k redundant?
        import foundation.env as env
        env.prefs[prefs_key] = val
        if 0 and debug_flags.atom_debug:
            print "fyi: %r set env.prefs[%r] = %r" % (self, prefs_key, val)
        return
    value = property(get_value, set_value)
    def _init_instance(self):
        super(PrefsKey_StateRef, self)._init_instance()
        # also set default value of this prefs key (noting that it may or may not already have a saved value) --
        # i think this (since it does .get with default arg) is the official way:
        self.get_value()
    pass
コード例 #5
0
ファイル: controls.py プロジェクト: vcsrc/nanoengineer
class checkbox_pref(InstanceMacro):
    #e rename -- Checkbox(...), with various kinds of args to say what state it uses in different ways?
    #e make it one of several prefs controls for other types of pref and control
    #e generalize to all named state -- e.g. see also LocalVariable_StateRef -- permit passing in the stateref?
    #e get dflt label from stateref??
    # note: this was split out of kluge_dragtool_state_checkbox_expr 061214,
    # extended here into a def (later renamed checkbox_pref_OLDER), then a class
    prefs_key = Arg(str)
    label = Arg(Anything)  # string or Widget2D
    dflt = ArgOrOption(bool, False)
    sbar_text = Option(str, '')
    use_label = If(call_Expr(lambda label: type(label) == type(""), label),
                   TextRect(label), label)  ## was TextRect(label,1,20)
    use_sbar_text = or_Expr(
        sbar_text,
        If(call_Expr(lambda label: type(label) == type(""), label), label, ""))
    stateref = Instance(PrefsKey_StateRef(prefs_key, dflt))
    # note: without Instance here, next line stateref.value says (correctly):
    ## AssertionError: compute method asked for on non-Instance <PrefsKey_StateRef#47221(a)>
    var = stateref.value
    checkbox = If(
        var,
        checkbox_image('mac_checkbox_on.png'),
        checkbox_image('mac_checkbox_off.png'),
    )
    _value = DisplayListChunk(
        Highlightable(
            SimpleRow(CenterY(checkbox),
                      CenterY(use_label)),  # align = CenterY is nim
            ## on_press = Set(debug_evals_of_Expr(stateref.value), not_Expr(var) ), #070119 debug_evals_of_Expr - worked
            on_press=_self.on_press,
            # the following works too, but I wanted to intercept it to add some py code [070305]:
            ## Set( stateref.value, not_Expr(var) ),
            sbar_text=use_sbar_text))
    # note: using DisplayListChunk in _value works & is faster [070103]
    #070124 comment: the order DisplayListChunk( Highlightable( )) presumably means that the selobj
    # (which draws the highlightable's delegate) doesn't include the displist; doesn't matter much;
    # that CenterY(use_label) inside might be ok, or might be a bug which is made up for by the +0.5 I'm adding to drawfont2
    # in testdraw.py today -- not sure.
    incr_drawable = Instance(
        Boxed(CenterY(checkbox), pixelgap=0, bordercolor=gray, borderwidth=2))

    # I tried orange as a warning color -- means the checkbox reflects an intention but not yet the reality.
    # But it was annoyingly too visible. So I'll try gray.
    # If all colorboxeds are unpopular, then try an image that has a little spray of lines coming from the center, instead.
    def on_press(self):
        self.stateref.value = not self.stateref.value  # was, in the expr: Set( stateref.value, not_Expr(var) )

        ###e revise this code to use self.draw_incrementally once that's refiled into Highlightable ###e
        def func(self=self):
            self.incr_drawable.draw()
            ## self.draw() # includes the label - probably a waste but who cares
            self.env.glpane.swapBuffers()  # update display [needed]

        ran_already_flag, funcres = self.run_OpenGL_in_local_coords(
            func)  # this method runs in the Highlightable made in _value
        assert ran_already_flag
        return

    pass  # end of class checkbox_pref
コード例 #6
0
ファイル: staterefs.py プロジェクト: vcsrc/nanoengineer
class LocalVariable_StateRef(InstanceOrExpr):  # guess, 061130
    # [moved here from controls.py, 061203; will probably become obs once State works]
    """
    return something which instantiates to something with .value which is settable state...
    """
    #e older name: StateRefFromIpath; is this almost the same as the proposed State() thing? it may differ in how to alter ipath
    # or some other arg saying where to store the ref, or in not letting you change how to store it (value encoding),
    # and worst, in whether it's an lval or not -- I think State is an lval (no need for .value) and this is a stateref.
    type = Arg(Type, Anything)
    defaultValue = ArgOrOption(
        Anything, None
    )  ##e default of this should depend on type, in same way it does for Arg or Option

    # see comments in  about problems if this is a formula which uses anything usage-tracked -- same probably applies here.
    def get_value(self):
        #e should coerce this to self.type before returning it -- or add glue code wrt actual type, or....
        return self.transient_state.value  ###e let transient_state not be the only option? does the stateplace even matter??

    def set_value(self, val):
        self.transient_state.value = val  #e should coerce that to self.type, or add glue code...
        return

    value = property(get_value, set_value)

    def _init_instance(self):
        super(LocalVariable_StateRef, self)._init_instance()
        set_default_attrs(
            self.transient_state,
            value=self.defaultValue)  #e should coerce that to self.type

    pass
コード例 #7
0
class Clipped(InstanceOrExpr):
    "#doc"
    # note: we're not delegating anything.
    # e.g. the best lbox attrs for this are specified by the caller and relate to the planes passed to us.
    thing = Arg(Widget2D)
    planes = ArgOrOption(list_Expr(ClippingPlane))
    def draw(self):
        planes = self.planes
        assert len(planes) <= len(GL_CLIP_PLANE_table), "no more than %d clipping planes are permitted" % len(GL_CLIP_PLANE_table)
            # even if your OpenGL driver supports more -- no sense writing an expr that not everyone can draw!
            #    WARNING: this ignores the issue of nested Clipped constructs!
            # In fact, it assumes nothing in thing or above self uses any clipping planes.
            # (Not merely "assumes no more than 6 in all", because we hardcode which specific planes to use!)
            #    Worse, we don't even detect the error. Fixing the behavior is just as easy
            # (let graphical dynenv (self.env) know which planes are still available to any drawing-kid), so do that instead. ##e
            # Note that we might need to work inside a display list, and therefore we'd need to get the "next plane to use"
            # from an env which stays fixed (or from an env var which is changedtracked), not just from each draw call's caller
            # (i.e. a glpane attr).
        # enable the planes
        for i, plane in zip(range(len(planes)), planes):
            assert len(plane) == 4
            glEnable( GL_CLIP_PLANE_table[i] )
            glClipPlane( GL_CLIP_PLANE_table[i], plane)
        # draw thing
        self.drawkid( self.thing)
        # disable the planes
        for i in range(len(planes)):
            glDisable( GL_CLIP_PLANE_table[i] )
        return
    pass
コード例 #8
0
class Arrow(Widget2D):
    """
    Arrow class provides a 3D arrow with a 'tail' and an arrow head.
    @see: B{DnaSegment_ResizeHandle} where this is used to draw a resize handle 
          while editing a DnaSegment.
    @see: exprs.Rect.py which defines some other 3 D objects. 
    @see: DirectionArrow.py -- Don't confuse it with this class. 
    """
    color = ArgOrOption(Color, gray)        
    tailPoint = ArgOrOption(Position, ORIGIN)
    arrowBasePoint = ArgOrOption(Position, ORIGIN + 2*DX)
    scale = ArgOrOption(float, 10.0)
    tailRadius = ArgOrOption(float, 0.4)
    #tailRadiusLimits ensure a min and max size for the arrow (including arrow 
    #tail and arrow head. drawDirectionArrow method uses tailRadius as a reference
    #to derive other params such as arrowhead base radius etc. Thats why this arg
    #is named this way.
    #@See DnaSegment_ResizeHandle to see how these limits are defined. 
    tailRadiusLimits = ArgOrOption(tuple, ())
    glpane = ArgOrOption(Anything, None)
    scale_to_glpane = Option(bool, False)
    
    def draw(self):        
        drawDirectionArrow(
            self.color, 
            self.tailPoint, 
            self.arrowBasePoint,
            self.tailRadius,
            self.scale,
            tailRadiusLimits = self.tailRadiusLimits,
            numberOfSides = 6,
            glpane = self.glpane,
            scale_to_glpane = self.scale_to_glpane
        )          
コード例 #9
0
class StatefulRect(DelegatingInstanceOrExpr):  ###UNFINISHED but works for now
    # args
    rect = ArgOrOption(Movable(Rect),
                       Rect(1, 1, yellow),
                       doc="copy state from a snapshot of this guy's state")
    ###k not sure of Movable; for now, we ignore it and just grab out the state attrs we happen to know about as Rect attrs
    # state (maybe stateargs?)
    width = State(Width, rect.width)
    height = State(Width, rect.height)
    color = State(Color, rect.color)
    # appearance ###k missing the Movable -- does that belong in here, anyway??
    delegate = Rect(width, height, color)
    pass
コード例 #10
0
ファイル: Rect.py プロジェクト: elfion/nanoengineer
class Rect(Widget2D):  # finally working as of 061106
    """
    Rect(width, height, color) renders as a filled x/y-aligned rectangle
    of the given dimensions and color, with the origin on bottomleft,
    and a layout box equal to its size (no margin).

    If color is not given, it will be gray [#e should be a default attrval from env].

    If height is not given, it will be a square (even if width is a formula and/or random).

    See also: RectFrame, ...
    """
    # args
    width = Arg(Width, 5)  # changed 10 to 5 late on 061109
    # Note: Widget2D defines width & height, making this seem circular, but it's ok (see comment in RectFrame)
    height = Arg(Width, width)
    color = ArgOrOption(Color, gray)
    # formulas
    if 0:
        # use this to test whatever scheme we use to detect this error, once we put one in [disabled 061105 until other things work]
        bright = width  ######@@@@@ PROBLEM: in ns, width and bright will have same value, no ordering possible -- how can it tell
        # which one should be used to name the arg? It can't, so it'll need to detect this error and make you use _self. prefix.
        # (in theory, if it could scan source code, or turn on debugging during class imports, it could figure this out...
        #  or you could put the argname in Arg or have an _args decl... but I think just using _self.attr in these cases is simpler.)
        printnim("make sure it complains about bright and width here")
        btop = height
    else:
        if debug_flags.atom_debug:
            printfyi(
                "not yet trying to trigger the error warning for 'bright = width'"
            )  # (since it's nim, even as of 061114 i think)
        bright = _self.width
        btop = _self.height
    # bbottom and bleft are not needed (same as the defaults in Widget2D), except that we use them in the formula for center;
    # it would be more correct to say bleft = _self.bleft, but less efficient I think (not much), and hasn't been tested (should be #e).
    bbottom = 0
    bleft = 0

    ##    center = V_expr( (bright - bleft) / 2.0, (btop - bbottom) / 2.0, 0.0) #070211 #e someday this could be deduced from lbox, generally
    ###e should move this def into Spacer, RectFrame, etc -- or arrange to deduce it from lbox on any Widget2D, somehow...
    #  [070227] hmm, can't it just be moved from here into Widget2D itself? Yes, that works!
    def draw(self):
        glDisable(GL_CULL_FACE)
        draw_filled_rect(
            ORIGIN, DX * self.bright, DY * self.btop, self.fix_color(
                self.color))  #e move fix_color into draw_filled_rect?
        glEnable(GL_CULL_FACE)

    pass
コード例 #11
0
ファイル: Rect.py プロジェクト: elfion/nanoengineer
class Line(InstanceOrExpr
           ):  #070211; revised 070419 (Widget2D -> IorE, more options)
    end1 = Arg(Point)
    end2 = Arg(Point)
    color = ArgOrOption(Color, black)
    width = Option(int, 1)  #e rename linewidth?
    dashed = Option(bool, False)

    def draw(self):
        color = self.fix_color(self.color)
        end1, end2 = self.end1, self.end2
        width = self.width
        dashed = self.dashed
        drawline(color[:3], end1, end2, width=width,
                 dashEnabled=dashed)  ###k dashEnabled untested here

    pass
コード例 #12
0
ファイル: Rect.py プロジェクト: elfion/nanoengineer
class Spacer(
        Widget2D
):  # rewritten 061205 to not inherit from Rect, so arg defaults can change --
    ###e it would be better if that was not the only safe way [tho I didn't even bother trying the simpler way, I admit]
    """
    Accept same args as Rect, but draw as nothing.
    Equivalent to SpacerFor(Rect( same args)).
    """
    width = Arg(Width, 0)
    # Note: Widget2D defines width & height, making this seem circular, but it's ok (see comment in RectFrame)
    height = Arg(Width, width)
    color = ArgOrOption(
        Color, gray
    )  # not used, but should be accepted, since it's accepted as arg or public option name in Rect
    # formulas
    bright = _self.width
    btop = _self.height

    def draw(self):
        return

    pass
コード例 #13
0
ファイル: projection.py プロジェクト: elfion/nanoengineer
class DrawInCorner(DelegatingInstanceOrExpr):
    """
    DrawInCorner( thing, (-1,-1)) draws thing in the lower left corner of the screen
    (positioning thing so that its layout box's lower left corner nests directly into that corner of the screen).
    The depth can be specified by the option want_depth (between 0.0 and 1.0), which by default is 0.01 (very near the front).
    [###UNTESTED: it may be that non-default depths have never been tested.]

    The "corner" can be any corner, or any edge, or the center of the screen;
    it can be specified as a 2nd argument (x,y), or by the option corner = (x,y),
    where x can be -1, 0, or 1 (for left, center, or right respectively)
    and y can be -1, 0, or 1 (for bottom, center, or top).

    For the corners, the abbreviations defined in prefs_constants (small ints called UPPER_RIGHT, UPPER_LEFT,
    LOWER_LEFT, LOWER_RIGHT) are also permitted (and for convenience can also be imported from this class's source file).

    When thing does not need to touch a screen boundary (in one or both dimensions),
    it is not shifted at all, meaning its local origin is aligned with the specified position in that dimension.
    For drawing in an edge or the center, consider wrapping thing in Center or the like to modify this.
    (Without this feature, DrawInCorner( thing, (0,0)) would be equivalent to DrawInCorner( Center(thing), (0,0)).)

    ###BUG: The current implem (as of 070210) probably doesn't work properly after coordinate changes inside display lists.
    """
    ##e should we reverse the arg order? [recent suggestion as of 070302]
    delegate = Arg(Widget2D)
    corner = ArgOrOption(
        int,
        LOWER_RIGHT,
        doc=
        "the corner/edge/center to draw in, as named int or 2-tuple; see class docstring for details"
    )
    # note: semi-misnamed option, since it can also be an edge or the center
    ###KLUGE: type spec of 'int' is wrong -- we also allow it to be a pair of ints for x,y "symbolic posn" respectively
    want_depth = Option(
        float, 0.01
    )  # this choice is nearer than cov_depth (I think!) but doesn't preclude 3D effects (I hope).

    def draw(self):
        if self.delegate is None:
            # 070210 -- but I'm not sure if this is a complete ###KLUGE, or good but ought to be done more generally,
            # or if it would be better to do it totally differently by instantiating None into something like Spacer(). ##k
            return

        glMatrixMode(GL_MODELVIEW)  # not needed
        glPushMatrix()
        glLoadIdentity()
        try:
            glpane = self.env.glpane
            aspect = glpane.aspect  # revised by bruce 070919
            corner = self.corner
            delegate = self.delegate
            want_depth = self.want_depth
            # note about cov_depth:
            ## self.near = 0.25
            ## self.far = 12.0
            # so I predict cov_depth is 0.75 / 11.75 == 0.063829787234042548
            # but let's print it from a place that computes it, and see.
            # ... hmm, only place under that name is in selectMode.py, and that prints 0.765957458814 -- I bet it's something
            # different, but not sure. ###k (doesn't matter for now)

            # modified from _setup_modelview:

            saveplace = self.transient_state  # see if this fixes the bug 061211 1117a mentioned below -- it does, use it.
            # BUG (probably not related to this code, but not known for sure):
            # mousewheel zoom makes checkboxes inside DrawInCorner
            # either not highlight, or if they are close enough to
            # the center of the screen (I guess), highlight in the
            # wrong size and place. For more info and a partial theory,
            # see the 081202 update in DisplayListChunk.py docstring.
            # Before I realized the connection to DisplayListChunk,
            # I tried using saveplace = self.per_frame_state instead of
            # self.transient_state above, but that had no effect on the bug.
            # That was before I fixed some bugs in same_vals related to
            # numpy.ndarray, when I was plagued with mysterious behavior
            # from those in my debug code (since solved), but I tried it
            # again afterwards and it still doesn't fix this bug, which
            # makes sense if my partial theory about it is true.
            #
            # In principle (unrelated to this bug), I'm dubious we're
            # storing this state in the right place, but I won't change
            # it for now (see related older comments below).
            #
            # Note: I fixed the bug in another way, by changing
            # Highlightable's projection option to default True.
            # [bruce 081202]

            if glpane.current_glselect or (0 and 'KLUGE' and hasattr(
                    saveplace, '_saved_stuff')):
                # kluge did make it faster; still slow, and confounded by the highlighting-delay bug;
                # now I fixed that bug, and now it seems only normally slow for this module -- ok for now.

                # when that cond is false, we have a nonstandard projection matrix
                # (see also the related comments in save_coords_if_safe in Highlightable.py)
                # [bruce 081204 comment]

                x1, y1, z1 = saveplace._saved_stuff  # this is needed to make highlighting work!
                ###BUG [061211 1117a; fixed now, see above, using saveplace = self.transient_state]:
                # if we click on an object in testexpr_15d (with DrawInCorner used for other objs in the testbed)
                # before it has a chance to show its highlighted form, at least after a recent reload, we get an attrerror here.
                # Easy to repeat in the test conditions mentioned (on g5). Not sure how it can affect a different obj (self)
                # than the one clicked on too quickly. Best fix would be to let glpane give us the requested info,
                # which is usually the same for all callers anyway, and the same across reloads (just not across resizes).
                # But it does depend on want_depth, and (via gluUnProject) on the current modelview coords
                # (and projection coords if we ever changed them). So it's not completely clear how to combine ease, efficiency,
                # and safety, for that optim in general, even w/o needing this bugfix.
                #    But the bug is easy to hit, so needs a soon fix... maybe memoize it with a key corresponding to your own
                # assumed choice of modelview coords and want_depth? Or maybe enough to put it into the transient_state? TRY THAT. works.
                if _DEBUG_SAVED_STUFF:
                    print "_DEBUG_SAVED_STUFF: retrieved", x1, y1, z1
            else:
                x1, y1, z1 = saveplace._saved_stuff = \
                             gluUnProject(glpane.width, glpane.height, want_depth)
                # max x and y, i.e. right top
                # (note, to get the min x and y we'd want to pass (0, 0, want_depth),
                #  since these are windows coords -- (0, 0) is bottom left corner (not center))
                #
                # Note: Using gluUnProject is probably better than knowing and reversing _setup_projection,
                # since it doesn't depend on knowing the setup code, except meaning of glpane height & width attrs,
                # and knowing that origin is centered between them and 0.
                if _DEBUG_SAVED_STUFF:
                    print "_DEBUG_SAVED_STUFF: saved", x1, y1, z1


##            print x1,y1,z1
# use glScale to compensate for zoom * scale in _setup_projection,
# for error in PIXELS, and for want_depth != cov_depth
            x1wish = glpane.width / 2.0 * PIXELS  # / 2.0 because in these coords, screen center indeed has x == y == 0
            r = x1 / x1wish
            glScale(r, r, r)
            ##            x1 /= r
            ##            y1 /= r
            z1 /= r
            # now the following might work except for z, so fix z here
            glTranslatef(0.0, 0.0, z1)
            del x1, y1  # not presently used
            if _DEBUG_SAVED_STUFF:
                print "_DEBUG_SAVED_STUFF:     r = %r, translated z by z1 == %r" % (
                    r, z1)

            # I don't think we need to usage-track glpane height & width (or scale or zoomFactor etc)
            # since we'll redraw when those change, and redo this calc every time we draw.
            # The only exception would be if we're rendering into a display list.
            # I don't know if this code (gluUnProject) would even work in that case.
            # [I think I wrote a similar comment in some other file earlier today. #k]

            # move to desired corner, and align it with same corner of lbox
            # (#e could use an alignment prim for the corner if we had one)

            if corner in corner_abbrevs:
                # normalize how corner is expressed, into a 2-tuple of +-1's
                corner = corner_abbrevs[corner]

            x, y = corner

            if x == -1:  # left
                x_offset = -glpane.width / 2.0 * PIXELS + delegate.bleft
            elif x == +1:  # right
                x_offset = +glpane.width / 2.0 * PIXELS - delegate.bright
            elif x == 0:  # center(x)
                x_offset = 0
                # note: before 070210 this was (+ delegate.bleft - delegate.bright) / 2.0,
                # which has an unwanted (since unavoidable) centering effect; use explicit Center if desired.
            else:
                print "invalid corner", corner  ###
                raise ValueError, "invalid corner %r" % (corner, )

            if y == -1:  # bottom
                y_offset = -glpane.height / 2.0 * PIXELS + delegate.bbottom
            elif y == +1:  # top
                y_offset = +glpane.height / 2.0 * PIXELS - delegate.btop
            elif y == 0:  # center(y)
                y_offset = 0
                # note: # note: before 070210 this was (+ delegate.bbottom - delegate.btop) / 2.0
            else:
                print "invalid corner", corner  ###
                raise ValueError, "invalid corner %r" % (corner, )

            offset = (x_offset, y_offset)
            glTranslatef(offset[0], offset[1], 0.0)

            if _DEBUG_SAVED_STUFF:
                print "_DEBUG_SAVED_STUFF:     offset =", offset

            self.drawkid(delegate)  ## delegate.draw()

        finally:
            glMatrixMode(GL_MODELVIEW)  # not needed
            glPopMatrix()

        return

    pass  # end of class DrawInCorner
コード例 #14
0
class DisplayListChunk( DelegatingInstanceOrExpr, SelfUsageTrackingMixin, SubUsageTrackingMixin ):
    """
    #doc
    [Note: the implicit value for SelfUsageTrackingMixin is the total drawing effect of calling our displist in immediate mode.
     This has an inval flag and invalidator, but we also have another pair of those for our display list's OpenGL contents,
     making this more complicated a use of that mixin than class Lval or its other uses.]
    [old long docstring and comment moved to outtakes file since unreviewed and partly redundant, 070102]
    """
    # default values of instance variables
    _direct_sublists_dict = 2 # intentional error if this default value is taken seriously
    contents_valid = False
    drawing_effects_valid = False
        # we don't yet have a specific flag for validity of sublist drawing effects (unknown when not contents_valid);
        # that doesn't matter for now; a useful optim someday would be a dict of all invalid-effects direct sublists #e
    
    # args
    delegate = Arg(Widget)
    
    # options
    debug_prints = ArgOrOption(str, None) # flag to do debug prints, and (unless a boolean) name for doing them

    def _C__debug_print_name(self): # compute self._debug_print_name
        """
        return False (meaning don't do debug prints), or a name string to prefix them with
        """
        # use this to say when we emit a calllist (and if so, immediate or into what other displist), and/or recompile our list
        #e and use in repr
        if self.debug_prints:
            if self.debug_prints == True:
                return "%r" % self ###BUG (suspected): this looks like an infrecur. Not sure if ever tested. [070110 comment]
            return str(self.debug_prints)
        return False
    
    def _init_instance(self):
        self._key = id(self) # set attribute to use as dict key (could probably use display list name, but it's not allocated yet)
        self.glpane = self.env.glpane #e refile into superclass??
        self._disabled = not hasattr(self.glpane, 'glGenLists')
        if self._disabled:
            # this should never happen after the merge of GLPane_overrider into GLPane done today [070110]
            print "bug: %r is disabled since its GLPane is missing required methods" % self
        return
        
    def _C_displist(self): # compute method for self.displist
        ### WARNING: this doesn't recycle displists when instances are remade at same ipath (but it probably should),
        # and it never frees them. To recycle them, just change it to use transient_state.
        # When we start using more than one GL Context which share display lists, we'll have to revise this somehow.
        #
        ### NOTE: usage tracking should turn up nothing -- we use nothing
        """
        allocate a new display list name (a 32-bit int) in our GL context
        """
        if self.displist_disabled(): # revised this cond (true more often), 070215
            printfyi("bug: why does .displist get requested in a disabled DisplayListChunk??") # (i never saw this)
            return 0
        
        self.glpane.makeCurrent() # not sure when this compute method might get called, so make sure our GL context is current
        displist = self.glpane.glGenLists(1) # allocate the display list name [#k does this do makeCurrent??]
        # make sure it's a nonzero int or long
        assert type(displist) in (type(1), type(1L))
        assert displist, "error: allocated displist was zero"
        if self._debug_print_name:
            print "%s: allocated display list name %r" % (self._debug_print_name, displist)
        return displist

    def __repr__(self):
        try:
            if not self._e_is_instance:
                return super(DisplayListChunk, self).__repr__()
            _debug_print_name = self._debug_print_name # only legal on an instance
        except:
            print "exception in self._debug_print_name discarded" #e print more, at least id(self), maybe traceback, if this happens
            # (but don't print self or you might infrecur!!)
            ##e can we print self.displist and/or self.delegate?
            return "<%s at %#x>" % (self.__class__.__name__, id(self))
        else:
            return "<%s(%s) at %#x>" % (self.__class__.__name__, _debug_print_name or "<no name>", id(self))
        pass

    def displist_disabled(self): #070215 split this out, modified it to notice _exprs__warpfuncs
        """
        Is the use of our displist (or of all displists) disabled at the moment?
        """
        return self._disabled or \
               debug_pref("disable DisplayListChunk?", Choice_boolean_False, prefs_key = True) or \
               getattr(self.env.glpane, '_exprs__warpfuncs', None) ###BUG: this will be too inefficient a response for nice dragging.
    
    def draw(self):
        """
        Basically, we draw by emitting glCallList, whether our caller is currently
        compiling another display list or executing OpenGL in immediate mode.
           But if our total drawing effects are invalid (i.e. our own list and/or some list it calls
        has invalid contents), we must do some extra things, and do them differently in those two cases.
           In immediate mode, if our own display list contents are invalid, we have to recompile it (without
        executing it) and only later emit the call, since compiling it is the only way to find out what other
        display lists it currently calls (when using the current widget expr .draw() API). Further, whether
        or not we recompiled it, we have to make sure any other lists it calls are valid, recompiling them
        if not, even though they too might call other lists we can't discover until we recompile them.
           Since OpenGL doesn't permit nested compiles of display lists, we use a helper function:
        given a set of display lists, make sure all of them are ok to call (by compiling zero or more of them,
        one at a time), extending this set recursively to all lists they indirectly call, as found out when
        recompiling them. This involves calling .draw methods of arbitrary widgets, with a glpane flag telling
        them we're compiling a display list. (In fact, we tell them its controller object, e.g. self,
        so they can record info that helps with later redrawing them for highlighting or transparency.)
           When not in immediate mode, it means we're inside the recursive algorithm described above,
        which is compiling some other display list that calls us. We can safely emit our glCallList before or after
        our other effects (since it's not executed immediately), but we also have to add ourselves to
        the set of displists directly called by whatever list is being compiled. (Doing that allows the
        recursive algorithm to add those to the set it needs to check for validity. That alg can optim by
        only doing that to the ones we know might be invalid, but it has to record them as sublists of
        the list being compiled whether or not we're valid now, since it might use that later when we're
        no longer valid.)
           We do all that via methods and/or private dynamic vars in self.glpane. ###doc which
           Note that the recursive algorithm may call us more than once. We should, of course, emit a glCallList
        every time, but get recompiled by the recursive algorithm at most once, whether that happens due to side effects
        here or in the algorithm or both. (Both might be possible, depending on what optims are implemented. ###k)
           Note that recursive display list calls are possible
        (due to bugs in client code), even though they are OpenGL errors if present when executed. (BTW, it's possible
        for cycles to exist transiently in display list contents without an error, if this only happens when
        some but not all display lists have been updated after a change. This needs no noticing or handling in the code.)
        """ 
        # docstring revised 070102.
        
        _debug_print_name = self._debug_print_name
        
        if self.displist_disabled():
            self.drawkid( self.delegate) ## self.delegate.draw()
            # I hope it's ok that this has no explicit effect on usage tracking or inval propogation... I think so.
            # It's equivalent to wrapping the whole thing in an If on this cond, so it must be ok.
            return
        
        self.displist
            # make sure we have a display list allocated
            # (this calls the compute method to allocate one if necessary)
            # [probably not needed explicitly, but might as well get it over with at the beginning]
            
            ###e NOTE: if someday we keep more than one displist, compiled under different drawing conditions in dynenv
            # (e.g. different effective values of glpane._exprs__warpfuncs),
            # then some or all of our attrs need splitting by the case of which displist to use,
            # and we also have to avoid usage-tracking the attrs that eval that case in the same way
            # as those we use when compiling displists. (As if we're If(cond, displistchunk1, displistchunk2),
            #  but also making sure nothing in displistchunk1 tracks those same attrs in a way we see as our own usage,
            #  or if it does, making sure we don't subscribe inval of a displist to that, by adding new code
            #  to end_tracking_usage to handle those usages specially, having known what they were by tracking cond, perhaps --
            #  or by explicit description, in case cond also tracks other stuff which doesn't show up in its value.
            #  Or another way, if the cond-value-attrs are special (e.g. glpane._exprs__warpfuncs),
            #  is for them to not be natively usage-tracked, but only when we eval cond (not sure this is ok re outsider tracking
            #  of them -- maybe better to track them except when we set a specialcase dynenv flag next to them,
            #  which they notice to decide whether to track uses, which we set when inside the specific case for the cond-value.)
            #  [070215]

        # are we being compiled into another display list?
        parent_dlist = self.glpane.compiling_displist_owned_by # note: a dlist owner or None, not a dlist name
        
        if parent_dlist:
            # We're being compiled into a display list (owned by parent_dlist); tell parent that its list calls ours.
            # (Note: even if we're fully valid now, we have to tell it,
            #  since it uses this info not only now if we're invalid, but later,
            #  when we might have become invalid. If we ever have a concept of
            #  our content and/or drawing effects being "finalized", we can optim
            #  in that case, by reducing what we report here.)
            if _debug_print_name:
                print "%s: compiling glCallList into parent list %r" % (_debug_print_name, parent_dlist)

            parent_dlist.__you_called_dlist( self) #e optim: inline this
                # (note: this will also make sure the alg recompiles us and whatever lists we turn out to call,
                #  before calling any list that calls us, if our drawing effects are not valid now.)
            
        elif self.glpane.compiling_displist:
            print "warning: compiling dlist %r with no owner" % self.glpane.compiling_displist
            #e If this ever happens, decide then whether anything but glCallList is needed.
            # (Can it happen when compiling a "fixed display list"? Not likely if we define that using a widget expr.)
            
        else:
            # immediate mode -- do all needed recompiles before emitting the glCallList,
            # and make sure glpane will be updated if anything used by our total drawing effect changes.
            if _debug_print_name:
                print "%s: prepare to emit glCallList in immediate mode" % (_debug_print_name, )
            
            self.glpane.ensure_dlist_ready_to_call( self) # (this might print "compiling glCallList" for sublists)
                # note: does transclose starting with self, calls _recompile_if_needed_and_return_sublists_dict
            self.track_use() # defined in SelfUsageTrackingMixin; compare to class Lval
                # This makes sure the GLPane itself (or whatever GL context we're drawing into, if it has proper usage tracking)
                # knows it needs to redraw if our drawing effects change.
                # Note: this draw method only does track_use in immediate mode (since it would be wrong otherwise),
                # but when compiling displists, it can be done by the external recursive algorithm via track_use_of_drawing_effects.
        if _debug_print_name:
            print "%s: emit glCallList(%r)" % (_debug_print_name, self.displist)
        self.do_glCallList() #e optim: inline this
        return # from draw

        # some old comments which might still be useful:
            
            # 061023 comments: analogous to Lval.get_value, both in .draw always being such, and in what's happening in following code.

            # Another issue - intermediate levels in a formula might need no lval objs, only ordinary compute calls,
            # unless they have something to memoize or inval at that level... do ordinary draw methods, when shared,
            # need this (their own capturing of inval flag, one for opengl and one for total effect,
            # and their own pair of usage lists too, one of called lists which can be scanned)??

    # this doesn't work due to quirks of Python:
    ## track_use_of_drawing_effects = track_use # this is semipublic; track_use itself (defined in SelfUsageTrackingMixin) is private
    # so rather than slow it down by a def,
    # I'll just rename the calls track_use but comment them as really track_use_of_drawing_effects

    def _your_drawing_effects_are_valid(self): ##e should inline as optim
        """
        """
        assert self.contents_valid
            # this failed 070104 with the exception shown in long string below, when I used clear "button" (070103 kluge) on one node...
            # theory: the world.nodelist change (by clear button run inside draw method, which is illegal -- that's the kluge)
            # invalidated it right when it was being recompiled (or just after, but before the overall recomp alg sent this call).
            # So that kluge has to go [later: it's gone],
            # and the underlying error of changing an input to an ongoing recompute has to be better detected. ####e
        self.drawing_effects_valid = True
        return

# the exception mentioned above: 
    """
atom_debug: fyi: <OneTimeSubsList(<LvalForState(<World#16291(i)>|transient.nodelist|('world', (0, (0, 'NullIpath')))) at 0x101a7b98>) at 0x10583850>'s 
event already occurred, fulfilling new subs 
<bound method usage_tracker_obj.standard_inval of <usage_tracker_obj(<DisplayListChunk(<no name>) at 0x111b7670>) at 0x112086e8>> immediately: 

[atom.py:414] [GLPane.py:1847] [GLPane.py:1884] [testmode.py:67] [testdraw.py:251] [GLPane_overrider.py:127]
[GLPane_overrider.py:138] [GLPane_overrider.py:298] [testmode.py:75] [testdraw.py:275] [testdraw.py:385]
[testdraw.py:350] [testdraw.py:384] [testdraw.py:530] [test.py:1231] [Overlay.py:57] [Overlay.py:57]
[Overlay.py:57] [DisplayListChunk.py:286] [DisplayListChunk.py:124] [state_utils.py:156] [DisplayListChunk.py:116]
[DisplayListChunk.py:358] [changes.py:352] [changes.py:421] [changes.py:72]

exception in testdraw.py's drawfunc call ignored: exceptions.AssertionError:

[testdraw.py:350] [testdraw.py:384] [testdraw.py:530] [test.py:1231] [Overlay.py:57] [Overlay.py:57]
[Overlay.py:57] [DisplayListChunk.py:286] [DisplayListChunk.py:127] [DisplayListChunk.py:314]
"""
    def __you_called_dlist(self, dlist):
        self.__new_sublists_dict[ dlist._key ] = dlist
            # note: this will intentionally fail if called at wrong time, since self.__new_sublists_dict won't be a dict then
        return
    
    def _recompile_if_needed_and_return_sublists_dict(self):
        """
        [private helper method for glpane.ensure_dlist_ready_to_call()]
        Ensure updatedness of our displist's contents (i.e. the OpenGL instructions last emitted for it)
        and of our record of its direct sublists (a dict of owners of other displists it directly calls).
        Return the dict of direct sublists.
           As an optim [mostly nim], it's ok to return a subset of that, which includes all direct sublists
        whose drawing effects might be invalid. (In particular, if our own drawing effects are valid,
        except perhaps for our own displist's contents, it's ok to return {}. [That's the part of this optim we do.])
        """ 
        # doc revised 070102
        if not self.contents_valid:
            # we need to recompile our own displist.
            if self._debug_print_name:
                print "%s: compiling our displist(%r)" % (self._debug_print_name, self.displist)
            
            self._direct_sublists_dict = 3 # intentional error if this temporary value is used as a dict
                # (note: this might detect the error of a recursive direct or indirect call -- untested ##k)
            self.__new_sublists_dict = new_sublists_dict = {}
                # this is added to by draw methods of owners of display lists we call
            mc = self.begin_tracking_usage()
                # Note: we use this twice here, for different implicit values, which is ok since it doesn't store anything on self.
                # [#e i should make these non-methods to clarify that.]
                # This begin/end pair is to track whatever affects the OpenGL commands we compile;
                # the one below is to track the total drawing effects of the display lists called during that
                # (or more generally, any other drawing effects not included in that, but tracking for any other
                #  kinds of effects, like contents of textures we draw to the screen ### WHICH MIGHT MATTER, is nim).
                #
                # Note that track_use and track_inval do NOT have that property -- they store self.__subslist.
            try:
                self.recompile_our_displist()
                    # render our contents into our displist using glNewList, self.drawkid( self.delegate), glEndList
                    # note: has try/except so always does endlist ##e make it tell us if error but no exception??
            finally:
                self.contents_valid = True
                    # Q: is it ok that we do this now but caller might look at it?
                    # A: yes, since caller never needs to see it -- it's effectively private.
                self.end_tracking_usage( mc, self.invalidate_contents) # same invalidator even if exception during recompile or its draw
                self._direct_sublists_dict = dict(new_sublists_dict)
                    #e optim: this copy is only for bug-safety in case something kept a ref and modifies it later
                self.__new_sublists_dict = 4 # illegal dict value

            mc2 = self.begin_tracking_usage() # this tracks how our drawing effects depend on those of the sublists we call
            try:
                for sublist in self._direct_sublists_dict.itervalues():
                    sublist.track_use() # really track_use_of_drawing_effects (note: that's tracked into the global env, as always for track_use)
            finally:
                self.end_tracking_usage( mc2, self.invalidate_drawing_effects )
                    # this subscribes invalidate_drawing_effects to inval of total effects of all sublists
                    # (effectively including indirectly called ones too);
                    # the only thing it doesn't cover is subscribing it to inval of our own displist's contents,
                    # so we manually call it in invalidate_contents.
        if self.drawing_effects_valid:
            if debug_pref("DisplayListChunk: permit optim 070204?", Choice_boolean_False):
                ###BUG: this old optim seems to cause the bug 070203 -- I don't know why, but disabling it seems to fix the bug ###k
                print "doing optim 070204"#e and listnames [#e only print if the optim makes a difference?]
                return {} # optim; only possible when self.contents_valid,
                    # tho if we had a separate flag for sublist contents alone,
                    # we could correctly use that here as a better optim #e
            else:
                pass ## print "not doing optim 070204"#e and listnames
        return self._direct_sublists_dict

    def invalidate_contents(self):
        """
        [private] 
        called when something changes which might affect 
        the sequence of OpenGL commands that should be compiled into self.displist
        """
        if self.contents_valid:
            self.contents_valid = False
            self.invalidate_drawing_effects()
        else:
            # this clause would not be needed except for fear of bugs; it detects/complains/works around certain bugs.
            if self.drawing_effects_valid:
                print "bug: self.drawing_effects_valid when not self.contents_valid. Error, but invalidate_drawing_effects anyway."
                self.invalidate_drawing_effects()
            pass
        return
    
    def invalidate_drawing_effects(self):
        # note: compare to class Lval
        if self.drawing_effects_valid:
            self.drawing_effects_valid = False
            # propogate inval to whoever used our drawing effects
            self.track_inval() # (defined in SelfUsageTrackingMixin)
        return

    def recompile_our_displist(self):
        """
        [private] call glNewList/draw/glEndList in the appropriate way, 
        but do no usage tracking or valid-setting
        """
        glpane = self.glpane
        displist = self.displist
        glpane.glNewList(displist, GL_COMPILE, owner = self)
            # note: not normally a glpane method, but we'll turn all gl calls into methods so that glpane can intercept
            # the ones it wants to (e.g. in this case, so it can update glpane.compiling_displist)
            # note: we have no correct way to use GL_COMPILE_AND_EXECUTE, as explained in draw docstring
        try:
            self.drawkid( self.delegate) ## self.delegate.draw()
        except:
            print_compact_traceback("exception while compiling display list for %r ignored, but terminates the list: " % self )
            ###e should restore both stack depths and as much gl state as we can
            # (but what if the list contents are not *supposed* to preserve stack depth? then it'd be better to imitate their intent re depths)
            pass
        glpane.glEndList(displist)
            # note: glEndList doesn't normally have an arg, but this arg lets glpane version of that method do more error-checking
        return
    
    def do_glCallList(self):
        """
        emit a call of our display list, whether or not we're called in immediate mode
        """
        self.glpane.glCallList( self.displist)
        return
    
    pass # end of class DisplayListChunk
コード例 #15
0
ファイル: demo_drag.py プロジェクト: vcsrc/nanoengineer
class Vertex(
        ModelObject
):  # renamed Node -> Vertex, to avoid confusion (tho it added some too, since localvars not all changed,
    # and since the world ought to contain model objs anyway, in general, of which Vertex is only one type, and current implem
    # also has its own graphics code)...
    # see below about better ways to rename it...
    """It has a position, initializable from arg1 but also settable as state under name pos, and an arb appearance.
    Position is relative to whatever coords it's drawn in.
    When you drag it, it chooses depth just nearer than hitpoint -- which means if you wiggle it over itself,
    it gets nearer and nearer (design flaw). [see comment for possible fix]
    """
    # 070223 possible fix to seeing depth of self: during a drag, tell glpane not to draw dragobj into depth or during glselect
    # mode... so it finds some other selobj besides dragobj. means it should draw dragobj last i guess... maybe transparent
    # so you can see selobj behind it, or diff color when there is a selobj...

    ###e about what to rename it... Hmm, is it a general "Draggable"? .lookslike arg2 -> .islike or .thing arg1?

    # BTW, the highlightability is not yet specified here ###nim; it relates to the fact that we can bind commands to it
    # (as opposed to things it contains or is contained in, tho that should be permitted at the same time,
    #  tho implem requires nested glnames to sometimes ignore inner ones, when those are inactive);
    # but we might add it in a wrapper which makes it into a view of it that lets commands be bound to it,
    # except that World.draw assumes we draw the actual model objs, not wraps of them - also an issue if env says they
    # look different -- so maybe World needs to wrap all it draws with glue to add looks and behavior to it, in fact.
    # [update much later 070201: or maybe ModelObject knows about this and asks env whether to override default drawing code.]

    # but for now, can we do this here?

    pos0 = Arg(Position)
    pos = State(
        Position, pos0
    )  ###BUG -- does this work -- is pos0 set in time for this? not sure it's working... 061205 1009p
    #e we probably want to combine pos0/pos into one StateArg so it's obvious how they relate,
    # and only one gets saved in file, and only one self.attr name is used up and accessible

    # the rest of this class is for drawing it and interacting with it.
    # Can we move that code into e.g. VertexDrawable or VertexView
    # and have Vertex itself just delegate (for sake of draw, lbox, etc) to some rule in the env for getting a Drawable for it,
    # probably looking it up in a dynamic memoizing mapping in the env? ####e [070105]
    # I think YES.
    # - a main difference between a model & view object -- a view obj knows how to draw itself, a model obj asks the env.
    #   (and the answer is quite different in different envs, e.g. 3d area vs MT. but what if we mix those?)
    # related notes:
    # - it might want a different delegate for drawing purposes (draw, lbox) and sim purposes (or others).
    # - does it also have an attr corresponding to its index/address/id in the model world?
    # - and maybe some for relations to other model objs, eg MT-parent, tags, name, etc?
    # - and maybe some graphics prefs like color or whatever, even if it's up to the drawing rule whether/how to use these?
    # - and for that matter some arbitrary other data (named attrs), as we'll let any model object have?
    # simplest thing that could work --
    # - make some sort of MemoDict for model-view and put it into the env, even if we kluge how to set it up for now.
    #   how does demo_MT do it? With a hardcoded helper function using a MemoDict in a simple way -- no involvement of env.
    #   So see above for copied code from that... ###e

    #e so now what we want is for this (in super modelobject) to delegate to a computed viewer from the helper func above

    #e but also to have more args that can be used to set the color [do this only in Vertex_new, below]
    ## color = Option(Color, color)

    ##    pass

    ## class VertexView(xx): -- SOMETHING LIKE THIS IS THE INTENT ####

    #e and then we want this new class to have the hardcoded appearance & behavior which the code below now passes in lookslike arg

    lookslike = ArgOrOption(Anything)  # OrOption is so it's customizable
    ###BAD for lookslike to be an attr of the Vertex, at least if this should be a good example of editing a sketch. [070105]
    # (a fancy sketch might have custom point types, but they'd have tags or typenames, with rules to control what they look like)

    cmenu_maker = State(
        Anything, None
    )  # meant to be set from outside; let's hope None is a legal value [070223 HACK]
    ## delegate = _self.lookslike #k
    delegate = Highlightable(
        Translate(lookslike, pos),
        ## eval_Expr( call_Expr(lookslike.copy,)( color = yellow) ),  #####?? weird exc don't know why - reload?
        ## AssertionError: _this failed to find referent for '_this_Vertex'
        on_drag=_self.on_drag,  # 070103 kluge experiment, works (eg in _19d)
        cmenu_maker=
        cmenu_maker  #070223 HACK [#e rename cmenu_maker to cmenu_obj as I guessed it was named??]
    )

    #e actions? or do this in a per-tool wrapper which knows the actions?
    # or, do this here and make the actions delegate to the current tool for the current parent? guess: the latter.

    ##e digr: will we want to rename delegate so it's only for appearance? *is it*? does it sim like this too?
    # Guess: it's for everything -- looks, sim qualities, etc -- except what we override or grab from special members.
    # [if so, no need to rename, except to clarify, leaving it general.]
    #e might need something for how to save it in a file, Undo policy, etc

    def on_drag(
        self
    ):  # 070103 kluge experiment, copied/modified from on_drag_bg in bigger class below

        # Note: this bug reported in BUGS breaks dragging of these nodes; worked around by increasing DZFUZZ:
        # 070115 "highlightable-finder can be fooled due to check_target_depth_fudge_factor of 0.0001"

        # where i am 070103 447p (one of two places with that comment)
        # - sort of sometimes works, but posns are sometimes same as bg, not sure why that DZ would be needed,
        # oh i guess the mousepos comes from the gray bg rect not the self unless we drag very slow...

        point = self.current_event_mousepoint(
        )  ### MIGHT BE WRONG COORDS? guess: no
        #k guess: will have snap-to-fixed-point bug for generic drag code
        newpos = point + DZ * DZFUZZ  # used for different things, depending #### DZ needed but might cause trouble too
        self.pos = newpos
        ## print "in-node action set %r.pos = %r" % (self, newpos) # sometimes works
        self.KLUGE_gl_update()  #k needed?
        return

    pass  # end of class Vertex