summaryrefslogtreecommitdiff
path: root/cad/src/exprs/testdraw.py
blob: ca80359b364768976fab77db18ef3b1bd90e8cbf (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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# Copyright 2006-2009 Nanorex, Inc.  See LICENSE file for details. 
"""
testdraw.py -- drawing code for testmode, which tests the exprs package.

[for doc, see testmode.py]

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

BUGS:

- 081204: testmode messes up ruler text and clipboard part text:
  mouse motion over a highlightable alternately turns highlighting and text colors on and off;
  with highlight on, text fg is white and bg clear (wrong but readable);
  with highlight off (a bug in itself), text fg and bg are both the same
  as whatever i set for text fg bfr renderText;
  there are two redraws per mouse motion (normal for motion over a *non*-highlighted object) when rulers are on;
  rulers *or* clipboard text also messes up some of the highlightable behavior in testmode,
  making it alternate (as described above).

  guesses as to the cause:
  - could it be related to calling qglClearColor with the text color,
    in the text rendering code?
  - could renderText mess up something else related to highlighting,
    like stencil buffer?
  mostly it's a mystery.

### WARNING: most of the rest of this docstring is obs:

known bugs:
+ slowness of redraw, and redraw happens whenever i go on or off a highlighted thing, or on or move around on something else.
  this is much worse on my iMacG4 than my iMac G5, but not because of redraw speed,
  but because on the G4, the mouse cursor stops following the mouse during the redraw!!!
  Possible fixes: different hit test alg, C-coded redraw or hit test, use of display list or saved color/depth buffer for
  nonhighlighted stuff.
  ... i put in a display list, which fixed it, but it's not invalled enough. (it also needs inval by trackball, unlike a chunk's.)

- highlight orange is slightly wider on right than nonhighlight pink (guess: glpane translates in model space, not just depth space)

- see the exception from the selobj alive test, which happens on clicks (left or cmenu) and on highlight not covering selobj,
  described below (in class Highlightable) [fixed now?]

- DraggedOn and ReleasedOn are nim (and need renaming) [no longer true, as of very long before 061120]

- region selection - works to select atoms in a rect, but the rect for it is not being drawn  [even w/o displist optim]
  - is it a failure to delegate some method to superclass, maybe emptySpaceLeftDown?
  - is it related to the reload that happens on that leftDown? (not sure why it would be)
  solved: it was failure to call super.Draw! Tested, but fix reverted, since i wanted to test the old way again. ###@@@

- singlet-drag to bond - also works but lacks rubberband line [even w/o displist optim] [might be fixed now??]

- draw_later doesn't make thing highlightable even when i don't touch color enablement and do it not much later,
so how can I test the real thing? [relates to Invisible] [would it work better as Transparent? Probably not yet...]
[###@@@ maybe fixed now]

- TextRect:
  - size is wrong
  - origin is wrong [probably fixed]
  + if too few lines, testchars at top rather than blanklines at bottom [fixed]
  - sometimes fuzzy (should fix several of these by using pixmap/displist text, or the bitmap text in pyrex_atoms)
  - fails to notice hits, in Button... no idea why. interesting Q - does it draw into depth buffer at all?
    yes it does; also, if i slide off black rect onto text, it works fine, only if i slide onto text does it fail.
    AHA, i bet it does something funny with size/pos, which messes up rendering by draw_in_abs_coords.

==

todo:
- try a display list optim in main draw function, for keeping non-highlighted stuff in one;
might work almost as well as a color/depth buffer, for this code;
but this requires knowing when it changed. One advantage: works across rotations
(as long as we keep any billboarded stuff out of it). Note: some text rendering here
shrinks when it gets nonparallel, but that's a bug, not a form of billboarding.
- more & better-organized widget exprs.
- widget exprs that map from external state (such as that in Nodes), and let us edit it.
  - i'm suspecting that one widget expr should often be used to draw all the things that use the same formulas it was set up with,
    so it would never carry per-object state -- it would be more like code. (But right now, Highlightable does carry some.)
  - for this to work, but fit with simple compounds like Row, the widget-subexprs need to get their own sub-state;
    this (as func of main state) could either be a code-id from their instance, or a posn from their caller,
    and done at compile time (their __init__ time) or draw time -- but it needs to work when they get reloaded/remade,
    suggesting that (for these simple compound -- not for iteratives used to describe rules)
    it's mainly positional, or "positional but sorted by type" so that a few kinds of code-changes don't destroy data.
    (But with optional explicit ids per instance, so that old data *can* be destroyed if you want, or preserved more readily.
    The auto ids are really only needed for transient things like glnames and saved modelview matrices, which any highlightable needs,
    and maybe for saved display lists, inval data, etc; so maybe it doesn't matter if those change on reload.
    And for anything referring to user-editable data, you need explicit ids anyway so you can recode and not obsolete your data.)
    - likely state implem (if we ignore letting it be "externally storable"): linked dicts; any convenient pyobj-constant keys,
    or maybe string keys.
    Possible convention (not thought through): the ids usable as attrs are the ones whose chars work in a py identifier.
- more todos findable by searching for "todo", "Drawable", and (older ones) "next up", "wishlist", and (implicit ones) "kluge".
"""

__author__ = "bruce"

import time

from OpenGL.GL import glPushMatrix
from OpenGL.GL import glPopMatrix
from OpenGL.GL import glTranslate

import foundation.env as env
from utilities import debug_flags

from utilities.debug import print_compact_traceback

from utilities.constants import ave_colors
from utilities.constants import blue
from utilities.constants import white
from utilities.constants import green
from utilities.constants import red

from exprs.reload import exprs_globals
    #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)
trans_green = translucent_color(green)

# ==

# note: class avoidHighlightSlowness was defined here in rev. 1.6, but didn't work and probably can't work, so I zapped it.
# Its goal was to avoid the redraws on every mouseover motion of drawing that doesn't use glname to make itself highlightable.
# Instead, just get all drawing to be inside a highlightable object. (It's ok if the highlighting is not visible.
# It just needs to get into the stencil and depth buffers like the plain object, so we don't worry whether we're over other objects
# during the mouseover.)

# ==

if debug_flags.atom_debug:
    print "\ntestdraw: %d reloads" % exprs_globals.reload_counter

def end_of_Enter(glpane):
    # called by testmode.Enter after it does everything else including super Enter; was never called before 070103
    ## glpane.win.setViewHome() # not tested, might be wrong; redundant now (if home view is the default)
    init_glpane_vars(glpane)
    # print "did end_of_Enter on",glpane # works
    
def init_glpane_vars(glpane):
    # print "init_glpane_vars on glpane %r" % glpane # called by Draw and by end_of_Enter (as of 070103)
    glpane._glnames = []
    glpane._testmode_stuff = []
    glpane._testmode_stuff_2 = []
    glpane._testmode_stuff_3 = []
    glpane._alpha = 1.0
    
def leftDown(mode, event, glpane, superclass): # called from testmode.leftDown
    "[mode is needed to call superclass.leftDown]"
    ####@@@@ LOGIC BUG: when we reload, we replace one highlightable with a new one in the same place --
    # but don't replace selobj with the new one! So we predict not selobj_still_ok -- should print that from there ###@@@
    # [fixed now? note, reload is not right here, but in superclass.leftDown when it calls testmode.emptySpaceLeftDown]
    if printdraw: print "\ntestdraw leftDown" ###@@@
    superclass.leftDown(mode, event) # this might call testmode.emptySpaceLeftDown (or other class-specific leftDown methods in it)
    glpane.gl_update() # always, for now [might be redundant with superclass.leftDown, too]

def render_scene(mode, glpane): # called by testmode.render_scene # 061208
    # print "calling glpane.render_scene from testdraw.render_scene" -- works
    glpane.render_scene()
    return
    
def Draw_preparation(mode, glpane, superclass): # called by testmode.Draw_preparation
    init_glpane_vars(glpane)
    return

# no need so far for Draw_other_before_model

def Draw_model(mode, glpane, superclass): # called by testmode.Draw_model
    if env.prefs.get("A9 devel/testdraw/super.Draw first?", True): #070404
        glPushMatrix() #k needed??
        superclass.Draw_model(mode)
        glPopMatrix()
    # glpane.part.draw(glpane) # this doesn't draw the model in any different place as when done below... [061211]
    return

def Draw_other(mode, glpane, superclass): # called by testmode.Draw_other
    glPushMatrix()
    try:
        drawtest0(glpane) # this does all our special drawing, and sometimes puts some of it into a display list
        # [no point in putting the main model drawing into it, for now -- but when we can know when to inval, there might be]
    except:
        print_compact_traceback("exc ignored: ")
    glPopMatrix() # it turns out this is needed, if drawtest0 does glTranslate, or our coords are messed up when glselect code
    # [not sure what this next comment is about:]
    # makes us draw twice! noticed on g4, should happen on g5 too, did it happen but less??
    if env.prefs.get("A9 devel/testdraw/super.Draw last?", False): # revised prefs key and default, 070404
        # NOTE: after 090310 refactoring of Draw API, this is happening at the wrong time
        # (in Draw_other rather than in Draw_model) which may make it slower and/or have bugs.
        # [bruce 090310 comment]
        glPushMatrix()
        if 1:
            superclass.Draw_model(mode) # needed for region selection's separate xor-drawing;
            # I suspect this is slower than the other case. Does it draw more than once (for glselect) or something like that? ####@@@@
        else:
            # region selection's drawing [later: xor mode, i guess] won't work in this case, though its selection op itself will work
            glpane.part.draw(glpane) # highlighting works fine on this copy...
        if 0 and 'draw copy2 of model':
            glTranslate(5,0,0)
            glpane.part.draw(glpane) # but not on this one - i see why it doesn't draw it there, but why not complain about not finding it?
        glPopMatrix()
    # draw invisible stuff
    #e (we'll want to split this into stages for more than one kind of such stuff;
    #   later, to go through a "rendering pass widget expr")
    for func in glpane._testmode_stuff_2: # colors, for translucent stuff
        func()
    for func in glpane._testmode_stuff_3: # depths, for translucent stuff (matters if they are highlightable)
        func()
    for func in glpane._testmode_stuff: # depths, for invisible stuff
        func()
    return

def Draw_after_highlighting(mode, pickCheckOnly, glpane, superclass):
    ## print "testdraw.Draw_after_highlighting(pickCheckOnly = %r)" % (pickCheckOnly,) # pickCheckOnly is True once when I click
    return superclass.Draw_after_highlighting(mode, pickCheckOnly)

# ==

def drawtest0(glpane):
    """
    run drawtest1, protected from exceptions, after setting up some of its environment
    """
    # load the texture for the courier bitmap font; params incl tex_name are in private texture_fonts.vv object
    ensure_courierfile_loaded() # used to be done inside drawtest1
    glpane.kluge_reset_texture_mode_to_work_around_renderText_bug() #bruce 081205; ok this soon?

    exprs_globals.start_time = time.time()
        # anything that gets drawn can compare this with realtime
        # to get time so far in this frame, but it's up to it to be drawn
        # near the end of what we draw in the frame
    
    try:
        if printdraw:
            print "drawfunc (redraw %d)" % env.redraw_counter
        drawtest1(glpane)
    except:
        # this happens sometimes
        print_compact_traceback("exception in testdraw.py's drawfunc call ignored: ")
    return

def drawtest1(glpane):
    # main special drawing -- let the exprs module do it
    _reload_exprs_test()
    from exprs.test import drawtest1_innards
    drawtest1_innards(glpane)
    return

def _reload_exprs_test():
    # will need REVIEW when we have a new reloading system to replace heavy manual use of reload_once
    from exprs.reload import reload_once
    from exprs import test
    reload_once(test)
    return

# ==

# (some outtakes removed to bruceG5 testdraw-outtakes.py, last here in rev 1.11)
# (lots more removed 070408 over several commits, last here in rev 1.55)

# end