summaryrefslogtreecommitdiff
path: root/cad/src/graphics/drawing/CS_draw_primitives.py
blob: b510b0d67245763543b2eb2fdea6711abaa226e4 (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
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
# Copyright 2004-2009 Nanorex, Inc.  See LICENSE file for details.
"""
CS_draw_primitives.py - Public entry points for ColorSorter drawing primitives.

These functions all call ColorSorter.schedule_* methods, which record object
data for sorting, including the object color and an eventual call on the
appropriate drawing worker function.

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

History:

Originated by Josh in drawer.py .

Various developers extended it since then, including Brad G (ColorSorter).

080311 piotr Added a "drawpolycone_multicolor" function for drawing polycone
tubes with per-vertex colors (necessary for DNA display style)

080420 piotr Solved highlighting and selection problems for multi-colored
objects (e.g. rainbow colored DNA structures).

080519 russ pulled the globals into a drawing_globals module and broke drawer.py
into 10 smaller chunks: glprefs.py setup_draw.py shape_vertices.py
ColorSorter.py CS_workers.py c_renderer.py CS_draw_primitives.py drawers.py
gl_lighting.py gl_buffers.py
"""

from OpenGL.GL import GL_BACK
from OpenGL.GL import GL_CULL_FACE
from OpenGL.GL import glDisable
from OpenGL.GL import glEnable
from OpenGL.GL import GL_FILL
from OpenGL.GL import GL_FRONT
from OpenGL.GL import GL_LIGHTING
from OpenGL.GL import GL_LINE
from OpenGL.GL import glPolygonMode

from geometry.VQT import norm, vlen
import utilities.debug as debug # for debug.print_compact_traceback

from graphics.drawing.ColorSorter import ColorSorter
from graphics.drawing.drawers import drawPoint
from graphics.drawing.gl_GLE import gleSetNumSides

def drawsphere(color, pos, radius, detailLevel,
               opacity = 1.0,
               testloop = 0
               ):
    """
    Schedule a sphere for rendering whenever ColorSorter thinks is appropriate.

    @param detailLevel: 0 (icosahedron), or 1 (4x as many triangles),
                        or 2 (16x as many triangles)
    @type detailLevel: int (0, 1, or 2)
    """
    ColorSorter.schedule_sphere(
        color,
        pos,
        radius,
        detailLevel, # see: _NUM_SPHERE_SIZES, len(drawing_globals.sphereList)
        opacity = opacity,
        testloop = testloop )

def drawwiresphere(color, pos, radius, detailLevel = 1):
    """
    Schedule a wireframe sphere for rendering whenever ColorSorter thinks is
    appropriate.
    """
    ColorSorter.schedule_wiresphere(color, pos, radius,
                                    detailLevel = detailLevel)

def drawcylinder(color, pos1, pos2, radius, capped = 0, opacity = 1.0):
    """
    Schedule a cylinder for rendering whenever ColorSorter thinks is
    appropriate.
    """
    if 1:
        #bruce 060304 optimization: don't draw zero-length or almost-zero-length
        # cylinders.  (This happens a lot, apparently for both long-bond
        # indicators and for open bonds.  The callers hitting this should be
        # fixed directly! That might provide a further optim by making a lot
        # more single bonds draw as single cylinders.)  The reason the
        # threshhold depends on capped is in case someone draws a very thin
        # cylinder as a way of drawing a disk. But they have to use some
        # positive length (or the direction would be undefined), so we still
        # won't permit zero-length then.
        cyllen = vlen(pos1 - pos2)
        if cyllen < (capped and 0.000000000001 or 0.0001):
            # Uncomment this to find the callers that ought to be optimized.
            #e optim or remove this test; until then it's commented out.
##            if env.debug():
##                print ("skipping drawcylinder since length is only %5g" %
##                       (cyllen,)), \
##                       ("  (color is (%0.2f, %0.2f, %0.2f))" %
##                        (color[0], color[1], color[2]))
            return
        pass
    ColorSorter.schedule_cylinder(color, pos1, pos2, radius,
                                  capped = capped, opacity = opacity)

def drawcylinder_wireframe(color, end1, end2, radius): #bruce 060608
    """
    Draw a wireframe cylinder (not too pretty, definitely could look nicer, but
    it works.)
    """
    # display polys as their edges (see drawer.py's drawwirecube or Jig.draw for
    # related code) (probably we should instead create a suitable lines display
    # list, or even use a wire-frame-like texture so various lengths work well)
    glPolygonMode(GL_FRONT, GL_LINE)
    glPolygonMode(GL_BACK, GL_LINE)
    glDisable(GL_LIGHTING)
    # This makes motors look too busy, but without it, they look too weird
    # (which seems worse.)
    glDisable(GL_CULL_FACE)
    try:
        ##k Not sure if this color will end up controlling the edge color; we
        ## hope it will.
        drawcylinder(color, end1, end2, radius)
    except:
        debug.print_compact_traceback("bug, ignored: ")
    # The following assumes that we are never called as part of a jig's drawing
    # method, or it will mess up drawing of the rest of the jig if it's
    # disabled.
    glEnable(GL_CULL_FACE)
    glEnable(GL_LIGHTING)
    glPolygonMode(GL_FRONT, GL_FILL)
    glPolygonMode(GL_BACK, GL_FILL) # could probably use GL_FRONT_AND_BACK
    return

def drawDirectionArrow(color,
                       tailPoint,
                       arrowBasePoint,
                       tailRadius,
                       scale,
                       tailRadiusLimits = (),
                       flipDirection = False,
                       opacity = 1.0,
                       numberOfSides = 20,
                       glpane = None,
                       scale_to_glpane = False
                       ):
    """
    Draw a directional arrow staring at <tailPoint> with an endpoint decided
    by the vector between <arrowBasePoint> and <tailPoint>
    and the glpane scale <scale>
    @param color : The arrow color
    @type  color: Array
    @param tailPoint: The point on the arrow tail where the arrow begins.
    @type   tailPoint: V
    @param arrowBasePoint: A point on the arrow where the arrow head begins(??
    @type  arrowBasePoint: V
    @param tailRadius: The radius of the arrow tail (cylinder radius
                       representing the arrow tail)
    @type  tailRaius: float
    @param opacity: The opacity decides the opacity (or transparent display)
                    of the rendered arrow. By default it is rendered as a solid
                    arrow. It varies between 0.0 to 1.0 ... 1.0 represents the
                    solid arrow renderring style
    @type opacity: float
    @param numberOfSides: The total number of sides for the arrow head
                        (a glePolycone) The default value if 20 (20 sided
                        polycone)
    @type  numberOfSides: int

    @param scale_to_glpane: If True, the arrow size will be determined by the
                            glpane scale.
    """
    #@See DnaSegment_ResizeHandle to see how the tailRadiusLimits
    #are defined. See also exprs.Arrow. Note that we are not using
    #argument 'scale' for this purpose because the
    if scale_to_glpane and glpane is not None:
        scaled_tailRadius = tailRadius*0.05*glpane.scale
        if tailRadiusLimits:
            min_tailRadius = tailRadiusLimits[0]
            max_tailRadius = tailRadiusLimits[1]
            if scaled_tailRadius < min_tailRadius:
                pass #use the provided tailRadius
            elif scaled_tailRadius > max_tailRadius:
                tailRadius = max_tailRadius
            else:
                tailRadius = scaled_tailRadius
        else:
            tailRadius = scaled_tailRadius



    vec = arrowBasePoint - tailPoint
    vec = scale*0.07*vec
    arrowBase =  tailRadius*3.0
    arrowHeight =  arrowBase*1.5
    axis = norm(vec)

    #as of 2008-03-03 scaledBasePoint is not used so commenting out.
    #(will be removed after more testing)
    ##scaledBasePoint = tailPoint + vlen(vec)*axis
    drawcylinder(color, tailPoint, arrowBasePoint, tailRadius, capped = True,
                 opacity = opacity)

    ##pos = scaledBasePoint
    pos = arrowBasePoint
    arrowRadius = arrowBase
    gleSetNumSides(numberOfSides)
    drawpolycone(color,
                 # Point array (the two endpoints not drawn.)
                 [[pos[0] - 1 * axis[0],
                   pos[1] - 1 * axis[1],
                   pos[2] - 1 * axis[2]],
                  [pos[0],# - axis[0],
                   pos[1], #- axis[1],
                   pos[2]], #- axis[2],
                  [pos[0] + arrowHeight * axis[0],
                   pos[1] + arrowHeight * axis[1],
                   pos[2] + arrowHeight * axis[2]],
                  [pos[0] + (arrowHeight + 1) * axis[0],
                   pos[1] + (arrowHeight + 1) * axis[1],
                   pos[2] + (arrowHeight + 1) * axis[2]]],
                 [arrowRadius, arrowRadius, 0, 0], # Radius array
                 opacity = opacity
                 )
    #reset the gle number of sides to the gle default of '20'
    gleSetNumSides(20)

def drawpolycone(color, pos_array, rad_array, opacity = 1.0):
    """Schedule a polycone for rendering whenever ColorSorter thinks is
    appropriate."""
    ColorSorter.schedule_polycone(color, pos_array, rad_array,
                                  opacity = opacity)

def drawpolycone_multicolor(color, pos_array, color_array, rad_array,
                            opacity = 1.0):
    """Schedule a polycone for rendering whenever ColorSorter thinks is
    appropriate. Accepts color_array for per-vertex coloring. """
    ColorSorter.schedule_polycone_multicolor(color, pos_array, color_array,
                                             rad_array, opacity = opacity)

def drawsurface(color, pos, radius, tm, nm):
    """
    Schedule a surface for rendering whenever ColorSorter thinks is
    appropriate.
    """
    ColorSorter.schedule_surface(color, pos, radius, tm, nm)

def drawsurface_wireframe(color, pos, radius, tm, nm):
    glPolygonMode(GL_FRONT, GL_LINE)
    glPolygonMode(GL_BACK, GL_LINE)
    glDisable(GL_LIGHTING)
    glDisable(GL_CULL_FACE)
    try:
        drawsurface(color, pos, radius, tm, nm)
    except:
        debug.print_compact_traceback("bug, ignored: ")
    glEnable(GL_CULL_FACE)
    glEnable(GL_LIGHTING)
    glPolygonMode(GL_FRONT, GL_FILL)
    glPolygonMode(GL_BACK, GL_FILL)
    return

def drawline(color,
             endpt1,
             endpt2,
             dashEnabled = False,
             stipleFactor = 1,
             width = 1,
             isSmooth = False):
    """
    Draw a line from endpt1 to endpt2 in the given color.  Actually, schedule
    it for rendering whenever ColorSorter thinks is appropriate.

    @param endpt1: First endpoint.
    @type  endpt1: point

    @param endpt2: Second endpoint.
    @type  endpt2: point

    @param dashEnabled: If dashEnabled is True, it will be dashed.
    @type  dashEnabled: boolean

    @param stipleFactor: The stiple factor.
    @param stipleFactor: int

    @param width: The line width in pixels. The default is 1.
    @type  width: int or float

    @param isSmooth: Enables GL_LINE_SMOOTH. The default is False.
    @type  isSmooth: boolean

    @note: Whether the line is antialiased is determined by GL state variables
    which are not set in this function.

    @warning: Some callers pass dashEnabled as a positional argument rather
    than a named argument.
    """
    ColorSorter.schedule_line(color, endpt1, endpt2, dashEnabled,
                              stipleFactor, width, isSmooth)

def drawtriangle_strip(color, triangles, normals, colors):
    ColorSorter.schedule_triangle_strip(color, triangles, normals, colors)

def drawTag(color, basePoint, endPoint, pointSize = 20.0):
    """
    Draw a tag (or a 'flag') as a line ending with a circle (like a balloon
    with a string). Note: The word 'Flag' is intentionally not used in the
    method nameto avoid potential confusion with a boolean flag.

    @param color: color of the tag
    @type color: A
    @param basePoint: The base point of the tag
    @type basePoint: V
    @param endPoint: The end point of the tag
    @type endPoint: V
    @param pointSize: The pointSize of the point to be drawin at the <endPoint>
    @type  pointSize: float

    @see: GraphicsMode._drawTags where it is called (an example)

    """
    drawline(color, basePoint, endPoint)
    drawPoint(color, endPoint, pointSize = 20.0)

def draw3DFlag(glpane,
              color,
              basePoint,
              cylRadius,
              cylHeight,
              direction = None,
              opacity = 1.0):
    """
    Draw a 3D flag with its base as a 'cylinder' and the head as a sphere.

    @param glpane: The GLPane object
    @type glpane: B{GLPane}

    @param color: color of the tag
    @type color: A

    @param basePoint: The base point of the tag
    @type basePoint: V

    @param cylRadius: Radius of the base cylinder  of the flag
    @type  cylRadius: float

    @param cylHeight: Height of the base cylinder of the flag
    @type  clyHeight: float

    @param direction: direction in which to draw the 3D flag. If this is not
                      spcified, it draws the flag using glpane.up
    @type direction: V  (or None)

    @param opacity: Flag opacity (a value bet 0.0 to 1.0)
    @type opacity: float
    """
    if direction is None:
        direction = glpane.up

    scale = glpane.scale

    height = cylHeight
    endPoint = basePoint + direction*height

    sphereRadius = cylHeight*0.7
    sphereCenter = endPoint + direction*0.8*sphereRadius
    SPHERE_DRAWLEVEL = 2

    drawcylinder(color,
                 basePoint,
                 endPoint,
                 cylRadius,
                 capped = True,
                 opacity = opacity)

    drawsphere(color,
               sphereCenter,
               sphereRadius,
               SPHERE_DRAWLEVEL,
               opacity = opacity)
    return

# end