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
|
# Copyright 2005-2007 Nanorex, Inc. See LICENSE file for details.
"""
draw_bond_vanes.py -- part of the drawing code for higher-order bonds --
represent pi orbitals as "vanes".
@author: bruce
@version: $Id$
@copyright: 2005-2007 Nanorex, Inc. See LICENSE file for details.
"""
import math
from Numeric import dot
from OpenGL.GL import GL_CULL_FACE
from OpenGL.GL import glDisable
from OpenGL.GL import GL_LIGHT_MODEL_TWO_SIDE
from OpenGL.GL import GL_TRUE
from OpenGL.GL import glLightModelfv
from OpenGL.GL import GL_TRIANGLE_STRIP
from OpenGL.GL import glBegin
from OpenGL.GL import glNormal3fv
from OpenGL.GL import glVertex3fv
from OpenGL.GL import glEnd
from OpenGL.GL import GL_QUADS
from OpenGL.GL import GL_LIGHTING
from OpenGL.GL import GL_LINES
from OpenGL.GL import glColor3fv
from OpenGL.GL import GL_LINE_STRIP
from OpenGL.GL import glEnable
from OpenGL.GL import GL_FALSE
from geometry.VQT import cross, vlen, norm
import foundation.env as env
from graphics.drawing.gl_lighting import apply_material
from utilities.constants import white
from utilities.prefs_constants import pibondStyle_prefs_key
# permissible twist of one vane segment (5 degrees -- just a guess)
MAXTWIST = 5 * math.pi / 180
def draw_bond_vanes(bond, glpane, sigmabond_cyl_radius, col):
"""
Given a bond with some pi orbital occupancy (i.e. not a single bond),
draw some "vanes" representing the pi orbitals.
DON'T use glpane for .out and .up when arbitrary choices are needed -- use
coords derived from model-space out and up.
"""
del glpane
pi_info = bond.get_pi_info()
if pi_info is not None:
# vectors are in bond's coordsys
((a1py, a1pz), (a2py, a2pz), ord_pi_y, ord_pi_z) = pi_info
rad = sigmabond_cyl_radius
if ord_pi_y:
#k does this mean pi_vectors retval order is wrong?
draw_vane( bond, a1py, a2py, ord_pi_y, rad, col)
pass
if ord_pi_z:
# this only happens for triple or carbomeric bonds
draw_vane( bond, a1pz, a2pz, ord_pi_z, rad, col)
return
def draw_vane( bond, a1p, a2p, ord_pi, rad, col ):
"""
Draw a vane (extending on two opposite sides of the bond axis) [#doc more];
use ord_pi to determine how intense it is (not yet sure how, maybe by mixing
in bgcolor??);
a1p and a2p should be unit vectors perp to bond and no more than 90 degrees
apart when seen along it; they should be in the bond's coordinate system.
rad is inner radius of vanes, typically the cylinder radius for the sigma
bond graphic.
If col is not boolean false, use it as the vane color; otherwise, use a
constant color which might be influenced by the pi orbital occupancy.
"""
from utilities.debug_prefs import debug_pref
from utilities.debug_prefs import Choice_boolean_True, Choice_boolean_False
## twisted = debug_pref('pi vanes/ribbons', Choice_boolean_False)
# one of ['multicyl','vane','ribbon']
pi_bond_style = env.prefs[ pibondStyle_prefs_key]
twisted = (pi_bond_style == 'ribbon')
poles = debug_pref('pi vanes/poles', Choice_boolean_True)
draw_outer_edges = debug_pref('pi vanes/draw edges', Choice_boolean_True)
#bruce 050730 new feature, so that edge-on vanes are still visible
draw_normals = debug_pref('pi vanes/draw normals', Choice_boolean_False)
print_vane_params = debug_pref('pi vanes/print params',
Choice_boolean_False)
if print_vane_params:
print "draw vane",a1p,vlen(a1p),a2p,vlen(a2p),ord_pi
if twisted:
d12 = dot(a1p, a2p)
## assert d12 >= 0.0
if d12 < 0.0:
d12 = 0.0
if d12 > 1.0:
d12 = 1.0
twist = math.acos(d12) # in radians
# this is numerically inaccurate (since d12 is) near d12 == 1.0, but
# that's ok, since it's only compared to threshholds (by ceil())
# which correspond to d12 values not near 1.0.
# (#e btw, we could optim the common case (ntwists == 1) by
# inverting this comparison to get the equivalent threshhold for
# d12.)
maxtwist = MAXTWIST # debug_pref doesn't yet have a PrefsType for this
# number of segments needed, to limit each segment's twist to MAXTWIST
ntwists = max(1, int( math.ceil( twist / maxtwist ) ))
pass
if col:
color = col
else:
#bruce 050804: initial test of bond color prefs; inadequate in several
# ways ###@@@
from foundation.preferences import prefs_context
prefs = prefs_context()
from utilities.prefs_constants import bondVaneColor_prefs_key
#k I hope this color tuple of floats is in the correct prefs format
color = prefs.get(bondVaneColor_prefs_key)
# protect following code from color being None (which causes bus error,
# maybe in PyOpenGL)
assert len(color) == 3
###@@@ it would be much faster to update this pref (or almost any
# graphics color pref) if the OpenGL command to set the color was in its
# own display list, redefined when the redraw starts, and called from
# inside every other display list that needs it. Then when you changed
# it, gl_update would be enough -- the chunk display lists would not
# need to be remade.
###@@@ problems include:
# - being fast enough
# + dflt should be specified in just one place, and earlier than in this
# place, so it can show up in prefs ui before this runs (in fact, even
# earlier than when this module is first imported, which might be only
# when needed),
# - existing prefs don't have all the color vars needed (eg no
# toolong-highlighted color)
# - being able to track them only when finally used, not when pulled
# into convenience vars before final use -- this might even be an
# issue if we precompute a formula from a color-pref, but only count
# it as used if that result is used. (we could decide to track the
# formula res as a separate thing, i suppose)
## a1pos = bond.atom1.posn()
## a2pos = bond.atom2.posn()
a1pos, c1, center, c2, a2pos, toolong = bond.geom
if not toolong:
c1 = c2 = center # don't know if needed
x_axis = a2pos - a1pos
# from 1 to 2.5, with 1 moved out to shrink width according to ord_pi
width = 1.5 * ord_pi
inner, outer = 2.5 - width, 2.5
radius_pairs = [(outer, inner, outer), (-inner, -outer, -outer)]
# the order within each pair matters, since it affects the polys drawn
# below being CW or CCW! but for purposes of edges, we also have to
# know which one is *really* outer... thus the third elt (edge_outer)
# of the tuple.
# OpenGL code
#e could optim this to use Numeric calcs and OpenGL vertex array, with
# vertex normals and smooth shading, maybe even color ramp of some kind...
#e want polygon outlines?
#e want a 1d texture to emphasize the vane's ribbonness & help show ord_pi?
glDisable(GL_CULL_FACE)
#bruce 051215 use apply_material(color) instead of glMaterialfv, partly to
# prevent bug of passing 3-tuple to glMaterialfv, partly to make the vanes
# appear specular, partly to thereby fix bug 1216 (which was probably caused
# by not setting specular color here, thus letting it remain from whatever
# happened to be drawn just before). If we decide vanes should *not* appear
# specular, we have to actively turn off specular color here rather than
# ignoring the issue. One way would be to create and use some new
# functionality like apply_material(color, specular=False).
apply_material(color)
# gl args partly guessed #e should add specularity, shininess...
## glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color)
glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE)
# For vane lighting to be correct, two-sided polygon lighting is
# required, and every polygon's vertex winding order (CCW) has to match
# its normal vector, as both are produced by the following code. This
# means that any changes to the orders of lists of vertices or vectors
# in this code have to be considered carefully. But it's ok if a1p and
# a2p are negated (fortunately, since calling code arbitrarily negates
# them), since this reverses the poly scan directions in two ways at
# once (e.g. via pvec and perpvec in the quads case below). (For ribbon
# option, it's only ok if they're negated together; for quads case,
# independent negation is ok. I'm pretty sure calling code only negates
# them together.) [bruce 050725]
for outer, inner, edge_outer in radius_pairs:
normals = []
edgeverts = []
if twisted:
glBegin(GL_TRIANGLE_STRIP)
for i in range(1+ntwists):
t = float(i) / ntwists # 0.0 to 1.0
# one minus t (python syntax doesn't let me name it just 1mt)
_1mt = 1.0 - t
#e could optimize, not sure it matters
axispos = _1mt * a1pos + t * a2pos
# let it get smaller in the middle for large twist (rather than
# using angles in calc)
pvec = _1mt * a1p + t * a2p
# (rationale: shows weakness of bond. real reason: programmer is
# in a hurry.)
#e btw, it might be better to show twist by mismatch of
# larger rectangular vanes, in the middle; and it's faster
# to draw.
# Could also "* ord_pi" rather than using color, if we worked
# out proper min radius.
## pvec *= (rad * 2.5)
perpvec = norm(cross(x_axis, pvec))
# test shows this is needed not only for smoothness, but to make
# the lighting work at all
glNormal3fv( perpvec)
outervert = axispos + pvec * rad * outer
innervert = axispos + pvec * rad * inner
glVertex3fv( outervert)
## not needed, since the same normal as above
## glNormal3fv( perpvec)
glVertex3fv( innervert)
#e color? want to smooth-shade it using atom colors, or the
# blue/gray for bond order, gray in center?
if draw_normals:
normals.append(( axispos + pvec * rad * edge_outer,
perpvec ))
if draw_outer_edges:
edgeverts.append( axispos + pvec * rad * edge_outer )
glEnd()
else:
glBegin(GL_QUADS)
for axispos, axispos_c, pvec, normalfactor in \
[(a1pos,c1,a1p,-1), (a2pos,c2,a2p,1)]:
perpvec = norm(cross(x_axis, pvec)) * normalfactor
glNormal3fv( perpvec)
glVertex3fv( axispos + pvec * rad * inner)
glVertex3fv( axispos + pvec * rad * outer)
glVertex3fv( axispos_c + pvec * rad * outer)
# This (axispos_c + pvec * rad * outer) would be the corner
# we connect by a line, even when not draw_outer_edges, if
# outer == edge_outer -- but it might or might not be.
glVertex3fv( axispos_c + pvec * rad * inner)
if draw_normals:
normals.append(( axispos/2.0 +
axispos_c/2.0 + pvec * rad * edge_outer,
perpvec ))
if draw_outer_edges:
# Kluge to reverse order of first loop body but not second.
edgeverts.reverse()
edgeverts.append( axispos_c + pvec * rad * edge_outer)
edgeverts.append( axispos + pvec * rad * edge_outer)
else:
# At least connect the halves of each vane, so that twist
# doesn't make them look like 2 vanes.
edgeverts.append( axispos_c + pvec * rad * edge_outer)
glEnd()
## glBegin(GL_LINES)
## glColor3fv(color)
## for axispos, axispos_c, pvec in [(a1pos,c1,a1p), (a2pos,c2,a2p)]:
## glVertex3fv( axispos_c + pvec * rad * outer)
## glEnd()
glDisable(GL_LIGHTING) # for lines... don't know if this matters
if poles:
glBegin(GL_LINES)
glColor3fv(color)
for axispos, pvec in [(a1pos,a1p), (a2pos,a2p)]:
glVertex3fv( axispos + pvec * rad * outer)
glVertex3fv( axispos + pvec * rad * -outer)
glEnd()
if normals:
glBegin(GL_LINES)
glColor3fv(white)
for base, vec in normals:
glVertex3fv(base)
glVertex3fv(base + vec)
glEnd()
if edgeverts:
glBegin(GL_LINE_STRIP)
glColor3fv(color)
for vert in edgeverts:
glVertex3fv(vert)
glEnd()
glEnable(GL_LIGHTING)
glEnable(GL_CULL_FACE)
glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE)
return
# end
|