summaryrefslogtreecommitdiff
path: root/cad/src/graphics/drawing/glselect_name_dict.py
blob: 3735799f89163acf2a2493e0fb4b59f3854e0d77 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# Copyright 2005-2008 Nanorex, Inc.  See LICENSE file for details. 
"""
glselect_name_dict.py - allocate GL_SELECT names and record their owners.

@author: Bruce
@version: $Id$
@copyright: 2005-2008 Nanorex, Inc.  See LICENSE file for details. 

Module classification:  [bruce 080223]

It imports nothing, but the code that needs to use it is OpenGL drawing code
or a framework that supports it, so it seems more natural as graphics/drawing.
(It's really an "opengl drawing utility".)

History:

bruce 080220 split this out of env.py so we can make it per-assy.
"""

_last_glselect_name = 0

def _new_glselect_name():
    """
    Return a session-unique 32-bit unsigned int (not 0) for use as a GL_SELECT
    name.

    @note: these are unique even between instances of class glselect_name_dict.
    """
    #e We could recycle these for dead objects (and revise docstring),
    # but that's a pain, and unneeded (I think), since it's very unlikely
    # we'll create more than 4 billion objects in one session.
    global _last_glselect_name
    _last_glselect_name += 1
    return _last_glselect_name

class glselect_name_dict(object):
    """
    Allocate OpenGL GL_SELECT names for use when drawing one model (e.g. an
    assembly), and record their owners.

    @note: The allocated names are session-unique 32-bit unsigned ints (not 0).
           They are unique even between instances of this class.

    @note: Callers are free to share one instance of this class over multiple
           models, or (with suitable changes in current code in GLPane) to use
           more than one instance for objects drawn together in the same frame.

    @note: The allocated names can be used in OpenGL display lists, provided
           their owning objects remain alive as long as those display lists
           remain in use.
    """
    def __init__(self):
        self.obj_with_glselect_name = {} # public for lookup
            # [Note: we might make this private
            #  when access from env.py is no longer needed]
            # TODO: clear this when destroying the assy which owns self
    
    def alloc_my_glselect_name(self, obj):
        """
        Allocate and return a new GL_SELECT name, recording obj as its owner.
        Obj should provide the Selobj_API so it can handle GLPane callbacks
        during hit-testing.
        
        @see: Selobj_API
        """
        glname = _new_glselect_name()
        self.obj_with_glselect_name[glname] = obj
        return glname

    def object_for_glselect_name(self, glname):
        """
        #doc [todo: get material from docstring of same method in assembly.py]
        """
        return self.obj_with_glselect_name.get( glname)

    # Todo: add a variant of object_for_glselect_name to which the entire
    # name stack should be passed. Current code (as of 080220) passes the last
    # (innermost) element of the name stack.
    
    # Maybe todo: add methods for temporarily removing glname from dict (without
    # deallocating it) and for restoring it, so killed objects needn't have
    # names in the dict.  This would mean we needn't remove the name when
    # destroying a killed object, thus, needn't find the object at all. But when
    # this class is per-assy, there won't be a need for that when destroying an
    # entire assy, so it might not be needed at all.
    
    def dealloc_my_glselect_name(self, obj, glname):
        """
        #doc [todo: get material from docstring of same method in assembly.py]

        @note: the glname arg is permitted to be 0 or None, in which case we
               do nothing, to make it easier for callers to implement repeatable
               destroy methods which forget their glname, or work if they never
               allocated one yet. (Note that this is only ok because we never
               allocate a glname of 0.)
               
        @see: Selobj_API
        """
        # objs have to pass the glname, since we don't know where they keep it
        # and don't want to have to keep a reverse dict; but we make sure they
        # own it before zapping it!
        #e This function could be dispensed with if our dict was weak, but maybe
        #  it's useful for other reasons too, who knows.
        if not glname:
            return
        obj1 = self.obj_with_glselect_name.get(glname)
        if obj1 is obj:
            del self.obj_with_glselect_name[glname]
    ##    elif obj1 is None:
    ##        # Repeated destroy should be legal, and might call this (unlikely,
    ##        # since obj would not know glname -- hmm... ok, zap it.)
    ##        pass
        else:
            print ("bug: %s %r: real owner is %r, not" %
                ("dealloc_my_glselect_name(obj, glname) mismatch for glname",
                 glname, obj1)), obj
                # Print obj separately in case of exceptions in its repr.
        return

    pass # end of class

# end