summaryrefslogtreecommitdiff
path: root/cad/src/graphics/model_drawing/bond_drawer.py
blob: 40ccbbc681755f7683a75efd2a70a4d9c6df992b (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
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
# Copyright 2004-2009 Nanorex, Inc.  See LICENSE file for details. 
"""
bond_drawer.py -- implementations of Bond.draw and Bond.writepov.

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

History:

050727 bruce moved bodies of Bond.draw and Bond.writepov into functions in this
file, in preparation for further extending Bond.draw (and someday Bond.writepov)
for higher-order bonds.

090213 bruce refiled this module into graphics.model_drawing package
"""

from OpenGL.GL import glPushName
from OpenGL.GL import glPopName
from OpenGL.GL import GL_LIGHTING
from OpenGL.GL import glDisable
from OpenGL.GL import glEnable

from PyQt4.Qt import QFont, QString, QColor

from geometry.VQT import V
from geometry.VQT import norm, vlen

from graphics.drawing.ColorSorter import ColorSorter
from graphics.drawing.CS_draw_primitives import drawline
from graphics.drawing.CS_draw_primitives import drawcylinder
from graphics.drawing.CS_draw_primitives import drawsphere

from graphics.model_drawing.special_drawing import USE_CURRENT
from graphics.model_drawing.special_drawing import SPECIAL_DRAWING_STRAND_END

import foundation.env as env
from utilities import debug_flags

from graphics.rendering.povray.povheader import povpoint
from utilities.Printing import Vector3ToString
from model.elements import Singlet

from utilities.debug import print_compact_stack, print_compact_traceback

from utilities.constants import diDEFAULT
from utilities.constants import diINVISIBLE
from utilities.constants import diLINES
from utilities.constants import diBALL
from utilities.constants import diTUBES
from utilities.constants import diTrueCPK
from utilities.constants import diDNACYLINDER

from utilities.constants import TubeRadius
from utilities.constants import diBALL_SigmaBondRadius
from utilities.constants import diDNACYLINDER_SigmaBondRadius

from utilities.constants import ave_colors
from utilities.constants import green
from utilities.constants import yellow
from utilities.constants import red
from utilities.constants import blue
from utilities.constants import black
from utilities.constants import white
from utilities.constants import orange
from utilities.constants import lighterblue

from model.bond_constants import V_SINGLE
from model.bond_constants import V_DOUBLE
from model.bond_constants import V_TRIPLE
from model.bond_constants import bond_letter_from_v6
from model.bond_constants import V_AROMATIC
from model.bond_constants import V_GRAPHITE
from model.bond_constants import V_CARBOMERIC

## not yet in prefs db?
from utilities.prefs_constants import _default_toolong_hicolor

from utilities.prefs_constants import diBALL_BondCylinderRadius_prefs_key
from utilities.prefs_constants import diDNACYLINDER_BondCylinderRadius_prefs_key
from utilities.prefs_constants import pibondLetters_prefs_key
from utilities.prefs_constants import pibondStyle_prefs_key
from utilities.prefs_constants import arrowsOnFivePrimeEnds_prefs_key
from utilities.prefs_constants import arrowsOnThreePrimeEnds_prefs_key
from utilities.prefs_constants import arrowsOnBackBones_prefs_key
from utilities.prefs_constants import showBondStretchIndicators_prefs_key
from utilities.prefs_constants import linesDisplayModeThickness_prefs_key
from utilities.prefs_constants import bondStretchColor_prefs_key
from utilities.prefs_constants import diBALL_bondcolor_prefs_key
from utilities.prefs_constants import dnaStrutScaleFactor_prefs_key

from utilities.GlobalPreferences import disable_do_not_draw_open_bonds

# ==

# To modularize drawing, I'll pass in a drawing place which has methods like
# drawcylinder, which can be either the drawer module itself (or an object made
# here to encapsulate it) or a writepov-to-specific-file object. This is
# experimental code, so for now it's only here in bond_drawer.py.
#
# In the future, it should be created once per writepov event, and write the
# povheader, and then it should be passed in to individual writepov
# routines. Farther ahead, it should be able to write new macros which embody
# env.prefs values as needed, so individual atom/bond drawing calls don't need
# to each incorporate the effects of prefs values, but so that single macros can
# be revised manually in the output file to effectively change prefs values (a
# longstanding NFR from the SAB).
#
# [bruce 060622]

class writepov_to_file:
    def __init__(self, file, col = None):
        self.file = file # a file object, not just its name
        # does not currently write the povheader, assumes it was already written
        self.bondColor = col
        return
    # for now, the following methods have the same names and arg orders as the
    # macro calls that were used directly in writepov_bond.
    def line(self, a1pos, a2pos, color):
        self.file.write("line(" + povpoint(a1pos) +
                        "," + povpoint(a2pos) +
                        ", <" + str(color[0]) +"," + str(color[1]) +
                        ", " + str(color[2]) + ">)\n")
    def writeradmacro(self, rad, radmacro, noradmacro):
        if rad is not None:
            self.file.write("%s(" % radmacro + str(rad) + ", " )
        else:
            self.file.write("%s(" % noradmacro )
    def bond(self, a1pos, a2pos, col, rad = None):
        self.writeradmacro(rad, "bondr", "bond")
        self.file.write(povpoint(a1pos) +
                   "," + povpoint(a2pos) + 
                   "," + Vector3ToString(col) + ")\n")
    def tube3(self, a1pos, a2pos, col, rad = None):
        self.writeradmacro(rad, "tube3r", "tube3")
        self.file.write(povpoint(a1pos) +
                   ", " + povpoint(a2pos) +
                   ", " + Vector3ToString(col) + ")\n")
    def tube2(self, a1pos, color1, center, a2pos, color2, rad = None):
        if 1:
            #e Possible optim: if color1 == color2, this could reduce to tube3.
            # That might speed up povray in tubes mode by a factor of 2 or so
            # (for bonds that are not toolong), or maybe by 5/4 if half of the
            # bonds are toolong.  It seems to work (from manual inspection of
            # the output) so I'll leave it in.  [bruce 060622]
            if color1 == color2:
                self.tube3(a1pos, a2pos, color1, rad)
                return
        self.writeradmacro(rad, "tube2r", "tube2")
        self.file.write(povpoint(a1pos) +
           "," + Vector3ToString(color1) +
           "," + povpoint(center) + "," +
           povpoint(a2pos) + "," +
           Vector3ToString(color2) + ")\n")

    def tube1(self, a1pos, color1, c1, c2, a2pos, color2, rad = None):
        self.writeradmacro(rad, "tube1r", "tube1")
        self.file.write(povpoint(a1pos) +
           "," + Vector3ToString(color1) +
           "," + povpoint(c1) + "," +
           povpoint(c2) + "," + 
           povpoint(a2pos) + "," +
           Vector3ToString(color2) + ")\n")

    # arg order compatible with drawer.drawcylinder
    def drawcylinder(self, color, pos1, pos2, radius):
        self.tube3(pos1, pos2, color, radius)

    # Arg order compatible with drawer.drawsphere, except no detailLevel; not
    # yet called or tested. [060622]
    def drawsphere(self, color, pos, radius):
        ###k not compared with other calls of atom macro, or tested; kluge that
        ###it uses atom macro, since not all spheres are atoms
        self.file.write("atom(" + str(pos) + ", " +
                        str(radius) + ", " + Vector3ToString(color) + ")\n")
    
    def getBondColor(self):
        """
        Returns the self.bondColor (rgb value) 
        @return: L{self.bondColor}
        @see: L{self.old_writepov_bondcyl}
        @Note:  this whole file needs code cleanup.  
        """
        return self.bondColor

# ==

def bond_draw_in_CPK(self): #bruce 080212 # todo: make this a Bond method
    """
    Should the bond 'self' be drawn in CPK display mode?
    """
    if self.is_rung_bond():
        return False
    dispjunk, radius1 = self.atom1.howdraw(diTrueCPK) # inline for speed??
    dispjunk, radius2 = self.atom2.howdraw(diTrueCPK)
    # use baseposn for speed? (would only be correct for internal bonds)
    pos1 = self.atom1.posn()
    pos2 = self.atom2.posn()
    # don't bother drawing if atoms touch, even if some of self would be visible
    # if they just barely touch.
    return ( vlen(pos1 - pos2) > radius1 + radius2 )
    
def draw_bond(self,
              glpane,
              dispdef,
              col,
              detailLevel,
              highlighted = False,
              special_drawing_handler = None,
              special_drawing_prefs = USE_CURRENT
             ):
    #bruce 050702 adding shorten_tubes option;
    # 050727 that's now implied by new highlighted option.
    """
    Draw the bond 'self'. This function is only meant to be called as the
    implementation of Bond.draw.

    See that method's docstring for details of how it's called.

    The highlighted option says to modify our appearance as appropriate for
    being highlighted (but the highlight color, if any, is passed as a non-false
    value of col).
    """
    atom1 = self.atom1
    atom2 = self.atom2
    
    disp = max(atom1.display, atom2.display)
    if disp == diDEFAULT:
        disp = dispdef

    # piotr 080312  
    if disp == diDNACYLINDER:
        return
    
    if disp in (diTrueCPK, diDNACYLINDER):
        # new feature (previously we never drew these bonds):
        # only draw the bond if it's sufficiently long to be visible
        # and not a "dna rung bond".
        # warning: this code is duplicated in two places in this file.
        # [bruce 080212, after discussion]
        if bond_draw_in_CPK(self):
            # Determines bond thickness and style; might be revised.
            disp = diDNACYLINDER
        else:
            return
    
    if disp not in (diLINES, diBALL, diTUBES, diDNACYLINDER):
        return
        
    # set proper glname, for highlighting (must be done whether or not
    # highlighted is true)
    if atom1.element is Singlet:
        #bruce 050708 new feature -- borrow name from our singlet
        # (only works because we have at most one)
        # (also required a change in Atom.draw_in_abs_coords)
        glname = atom1.get_glname(glpane)
    elif atom2.element is Singlet:
        glname = atom2.get_glname(glpane)
    else:
        glname = self.glname

    ColorSorter.pushName(glname) #bruce 051206, part of fixing bug 1179
        # for non-open bonds; we have to do it both immediately and in
        # ColorSorter since bonds include both sorted and non-sorted openGL
        # drawing. Note, we don't yet protect against sorting not being active
        # now, but that should be fixed in drawer.py rather than here, and for
        # now it will always be active since all bond drawing in chunk.py is
        # done inside it (even external bonds).
    glPushName(glname) #bruce 050610
        # Note: we have to do this all the time, since display lists made
        # outside GL_SELECT mode can be used inside it.  And since that display
        # list might be used arbitrarily far into the future, self.glname needs
        # to remain the same (and we need to remain registered under it) as long
        # as we live.

    try: #bruce 050610 to ensure calling glPopName    
        povfile = None
        draw_bond_main(self, glpane, disp, col, detailLevel, highlighted, 
                       povfile,
                       special_drawing_handler = special_drawing_handler,
                       special_drawing_prefs = special_drawing_prefs,
                       glname = glname )
    except:
        glPopName()
        #bruce 060622 moved this before ColorSorter.popName
        print_compact_traceback(
            "ignoring exception when drawing bond %r: " % self)
        ColorSorter.popName() #bruce 051206
    else:
        glPopName()
        ColorSorter.popName() #bruce 051206
    
    return # from draw_bond, implem of Bond.draw

def draw_bond_main(self,
                   glpane,
                   disp,
                   col,
                   detailLevel,
                   highlighted,
                   povfile = None,
                   special_drawing_handler = None,
                   special_drawing_prefs = USE_CURRENT,
                   glname = None # glname arg is a kluge for fixing bug 2945
                  ):
    """
    [private helper function for this module only.]
    self is a bond. For other doc, see the calls.
    """
    _our_args = (self, glpane, disp, col, detailLevel, highlighted, povfile)
        # This must be kept in agreement with all args of this function except
        # special_*.  We include self in the tuple, since this function is not a
        # method. [bruce 080605]
    
    # figure out how this display mode draws bonds; return now if it doesn't
    # [moved inside this function, bruce 060622]
    if disp == diLINES:
        sigmabond_cyl_radius = diBALL_SigmaBondRadius / 5.0
            # used for multiple bond spacing (optimized here for that, by the "/
            # 5.0") and for pi orbital vanes (for which "/ 1.0" would probably
            # be better)
    elif disp == diBALL:
        # Used for single, double and triple bonds. 
        sigmabond_cyl_radius = diBALL_SigmaBondRadius \
            * env.prefs[diBALL_BondCylinderRadius_prefs_key]
            # mark 051003 added.
    elif disp == diTUBES:
        sigmabond_cyl_radius = TubeRadius
    elif disp == diDNACYLINDER:
        # diDNACYLINDER_BondCylinderRadius_prefs_key is not yet settable by 
        # user. It's value is 1.0. Mark 2008-02-13.
        sigmabond_cyl_radius = diDNACYLINDER_SigmaBondRadius \
                        * env.prefs[diDNACYLINDER_BondCylinderRadius_prefs_key]
    else:
        # bonds need not be drawn at all in the other display modes. (note: some
        # callers already checked this.)
        return

    # Figure out preferences. (Should do this less often somehow -- once per
    # user event, or at least, once per separate use of
    # begin_tracking_usage/end_tracking_usage.)
    if self.v6 != V_SINGLE:
        if debug_flags.atom_debug:
            #bruce 050716 debug code (permanent, since this would always
            # indicate a bug)
            if not self.legal_for_atomtypes():
                print_compact_stack(
                    "atom_debug: drawing bond %r %s" %
                    (self, "which is illegal for its atomtypes: "))
        if povfile is not None:
            # not yet supported, and i worry about side effects of env usage
            # tracking.
            draw_bond_letters = False
        else:
            draw_bond_letters = (not highlighted and
                                 env.prefs[ pibondLetters_prefs_key])
        # One of ['multicyl','vane','ribbon'].
        pi_bond_style = env.prefs[ pibondStyle_prefs_key]
        draw_cyls = (pi_bond_style == 'multicyl')
        draw_vanes = not draw_cyls # this is true for either vanes or ribbons
        draw_sigma_cyl = not draw_cyls
        if povfile is not None:
            draw_vanes = False # not yet supported
    else:
        # single bond -- no need to check prefs or set variables for vanes, etc
        draw_cyls = False
        draw_sigma_cyl = True
    # Whether to ever draw bands around the cylinders for aromatic/graphite
    # bonds.
    draw_bands = draw_cyls
    if draw_bands:
        v6_for_bands = self.v6
    else:
        v6_for_bands = V_SINGLE
    
    shorten_tubes = highlighted

     # Usual case for non-directional bonds; draws nothing; change this below if
     # necessary.
    dir_info = (0, False)
    direction_error = False # change below if necessary
    
    if self._direction: #bruce 070415, revised 071016
        # We might want to show this bond's direction, or (someday)
        # its ability to have one. Figure out what to actually show.

        if not self.is_directional():
            direction_error = True
            # but no sensible way to set dir_info, so leave it as default,
            # partly to avoid other errors in the else case
            # which assumes the bond is directional.
        else:
            # apply this general pref test first, to be faster when it's turned
            # off.
            
            # ninad070504: added the bond arrows preferences to Preferences
            # dialog.  using this preference key instead of debug preference.
                    
                ### POSSIBLE BUG (by experience, not understood in code, not
                # always repeatable): changing this debug_pref [back when the
                # following prefs were debug_prefs] fails to redraw internal
                # bonds accordingly (tho fine for external bonds).  This is as
                # if it's not usage tracked by the chunk display list compile,
                # or as if the change to it via the debug menu is not change
                # tracked.
                # Workaround (not very convenient): Undo past where you created
                # the DNA, and redo.
                # Or, probably, just change the chunk display style.
                # The bug is not always repeatable: I changed display to cpk
                # (all arrows gone, finally correct), then back to tubes, then
                # turned arrows on, and this time they all showed up, and again
                # as I try off/on.
                # Maybe it was an artifact of reloading this code??
                # [bruce 070415]
                # update, bruce 071016: could it have been simply that
                # debug_prefs are not change_tracked?  I am not sure, but IIRC,
                # they're not.

            if self.isFivePrimeOpenBond() or \
               self.isThreePrimeOpenBond():
                # draw self into the "strand end" display list, if caller has
                # one [bruce 080605]
                if (special_drawing_handler and 
                    special_drawing_handler.should_defer(
                      SPECIAL_DRAWING_STRAND_END)):
                    # defer all drawing of self to special_drawing_handler
                    def func(special_drawing_prefs, args = _our_args, glname = glname):
                        # KLUGE to fix bug 2945: make sure we do the same
                        # pushname/popname as the caller has already done,
                        # since draw_bond_main doesn't otherwise do it.
                        # This is the true cause of bug 2945 and this fix is
                        # logically correct; I only call it a kluge because
                        # the code needs cleanup, at least in this file
                        # (so the pushname is only done in one place),
                        # but preferably by just making sure all open bonds
                        # are always drawn as part of drawing their bondpoints,
                        # which would allow a bunch of code which now has to be
                        # kept in correspondence between bond and atom drawing
                        # to be centralized and simplified. [bruce 081211]
                        ColorSorter.pushName(glname) # review: also need glPushName?
                        try:
                            draw_bond_main(
                                *args,
                                **dict(special_drawing_prefs =
                                       special_drawing_prefs))
                        finally:
                            ColorSorter.popName()
                        return # from func
                    special_drawing_handler.draw_by_calling_with_prefsvalues(
                        SPECIAL_DRAWING_STRAND_END, func )
                    return

                # otherwise, draw now, using special_drawing_prefs
                
                _disable_do_not_draw = disable_do_not_draw_open_bonds()
                    # for debugging [bruce 080122]
                            
                # Determine whether cylinders of strand open bonds should be
                # drawn.  Atom._draw_atom_style() takes care of drawing singlets
                # as arrowheads (or not drawing them at all) based on these two
                # user prefs. - mark 2007-10-20.
                if self.isFivePrimeOpenBond():
                    if not special_drawing_prefs[
                          arrowsOnFivePrimeEnds_prefs_key]:
                        # Don't draw bond 5' open bond cylinder.
                        if _disable_do_not_draw:
                            if not highlighted and debug_flags.atom_debug:
                                #bruce 800406 Revised color & cond.
                                col = lighterblue
                                pass
                            pass
                        else:
                            return
                if self.isThreePrimeOpenBond():
                    if not special_drawing_prefs[
                          arrowsOnThreePrimeEnds_prefs_key]:
                        # Don't draw bond 3' open bond cylinder.
                        if _disable_do_not_draw:
                            if not highlighted and debug_flags.atom_debug:
                                col = lighterblue
                                pass
                            pass
                        else:
                            return
                        pass
                    pass
                # note: This might fall through -- only some cases above return.
                pass

            # If we didn't defer, we don't need to use special_drawing_handler
            # at all.
            del special_drawing_handler

            del special_drawing_prefs # not used below (fyi)
            
            bool_arrowsOnAll = env.prefs[arrowsOnBackBones_prefs_key]

            if bool_arrowsOnAll:
                # Draw arrow on bond unless there is some reason not to.

                # If either atom will look like an arrowhead (before prefs are
                # applied) -- i.e. if either one is a strand_end -- we don't
                # want the bond to have its own arrowhead. [bruce 071016,
                # feature requested by mark]

                # update, bruce 071018 -- an arrow-atom in front should always
                # suppress self's arrow, but an arrow atom in back should only
                # turn it off if self is an open bond, since we draw the arrow
                # only on the front half of the bond.  (Front and back are
                # relative to self's bond_direction.)
                
                # These flags might be changed during the following loop.
                direction_error = False 
                suppress_self_arrow_always = False # Set by arrowhead in front.
                # Set by arrowhead in back.
                suppress_self_arrow_if_open_bond = False
                
                for atom in (self.atom1, self.atom2):
                    # Does atom look like an arrowhead (i.e end_bond is not
                    # None), and if so, should that suppress_self_arrow?
                    end_bond = atom.strand_end_bond()
                    if end_bond is self:
                        if atom.is_singlet():
                            suppress_self_arrow_always = True
                        elif self.bond_direction_from(atom) == -1:
                            # atom is in front
                            suppress_self_arrow_always = True
                        else:
                            # Atom is in back (since we know self has a
                            # direction set.)
                            suppress_self_arrow_if_open_bond = True
                    elif end_bond is not None:
                        # end_bond not None or self -- We're directional, but
                        # not the directional bond atom thinks is a strand end!
                        # Error or bug, so always show self's arrow too.

                        ## REVIEW: are there other bond direction errors we need
                        ## to indicate as well?
                        direction_error = True
                    continue
                
                if suppress_self_arrow_if_open_bond and self.is_open_bond():
                    suppress_self_arrow_always = True
                
                if direction_error or not suppress_self_arrow_always:
                    dir_info = (self.bond_direction_from(self.atom1), True)
                pass

            pass
        pass
    
    # do calcs common to all bond-cylinders for multi-cylinder bonds

    atom1 = self.atom1
    atom2 = self.atom2
    
    color1 = col or atom1.drawing_color()
    color2 = col or atom2.drawing_color()
    ## if None, we look up the value when it's used [bruce 050805]
    bondcolor = col or None
    # note: bondcolor is only used in diBALL display style. todo: rename it.

    if (direction_error or atom1._dna_updater__error or
        atom2._dna_updater__error):
        # bruce 080130 added _dna_updater__error condition; not sure if 'and' or
        # 'or' is better in it; for now, use 'and' below for color, but use 'or'
        # for a debug print.  The outer condition is to optimize the usual case
        # where all these are false.
        if not highlighted:
            #bruce 080406 added "not highlighted" condition as bugfix
            # (this also required a bugfix in Bond.draw_in_abs_coords to pass
            #  highlighted = True for external bonds)
            if (direction_error or
                (atom1._dna_updater__error and atom2._dna_updater__error)):
                # note: no effect except in diBALL display style
                bondcolor = orange
                # Work around bug in uses above of drawing_color method:
                # [bruce 080131]
            if atom1._dna_updater__error or direction_error:
                color1 = orange
                # todo: also change toolong_color if we can
            if atom2._dna_updater__error or direction_error:
                color2 = orange
                # todo: also change toolong_color if we can
        if atom1._dna_updater__error or atom2._dna_updater__error:
            if not atom1._dna_updater__error and atom2._dna_updater__error:
                # Debug print if self is a rung bond (means error in dna
                #   updater.)
                # Note: always on, for now;
                # TODO: condition on DEBUG_DNA_UPDATER but first move that out
                #   of dna package
                roles = (atom1.element.role, atom2.element.role)
                     # Inlined self.is_rung_bond() .
                if roles == ('axis', 'strand') or roles == ('strand', 'axis'):
                    print ("\n*** bug in dna updater: %s %r" %
                           ("errors not propogated along", self))
        #bruce 071016 (tentative -- needs mouseover msg, as said above)
        # (TODO: we could set an error message string on self, but set it to
        # None whenever not setting the error color; since this is done whenever
        # self is redrawn, it will always be up to date when highlighting or
        # tooltip happens.)
   
    v1 = atom1.display != diINVISIBLE
    v2 = atom2.display != diINVISIBLE
        ###e bruce 041104 suspects v1, v2 wrong for external bonds, needs
        # to look at each mol's .hidden (but this is purely a guess)

    # compute geometry (almost always needed eventually, below)
    fix_geom = (povfile is not None) and (atom1.molecule is atom2.molecule)
    if fix_geom:
        # in this case, bond.geom is wrong, needs to be absolute but isn't 
        selfgeom = self._recompute_geom(abs_coords = True)
    else:
        #e perhaps could be optimized to only compute a1pos, a2pos
        selfgeom = self.geom
        
    howmany = 1 # modified below
    if draw_cyls:
        # Draw 1, 2, or 3 central cyls, depending on bond type (only 1 for
        # aromatic bonds) (#e what's best for carbomeric?)
        # Note: this code sets howmany, and if it's not 1, draws that many cyls;
        # otherwise howmany == 1 is noticed below (not inside this 'if'
        # statement) so the central cyl is drawn.
        howmany = { V_DOUBLE: 2, V_TRIPLE: 3 }.get(self.v6, 1)

        if howmany > 1:
            # Figure out where to draw them, and cyl thickness to use; this
            # might depend on disp and/or on sigmabond_cyl_radius .
            if fix_geom:
                pi_info = self.get_pi_info(abs_coords = True)
            else:
                #k This could probably be the same call as above, with
                #  abs_coords = fix_geom .
                pi_info = self.get_pi_info()
                pass
            if pi_info is None:
                # Should never happen;
                # if it does, work around the bug this way.
                howmany = 1
            else:
                # Vectors are in bond's coordsys.
                ((a1py, a1pz), (a2py, a2pz), ord_pi_y, ord_pi_z) = pi_info
                del ord_pi_y, ord_pi_z
                pvecs1 = multicyl_pvecs(howmany, a1py, a1pz)
                # Leaves them as unit vectors for now.
                pvecs2 = multicyl_pvecs(howmany, a2py, a2pz)
                if disp == diLINES:
                    # Arbitrary, since cylinder thickness is not used when
                    # drawing lines.
                    scale = 1
                    offset = 2
                elif disp == diBALL:
                    scale = 1
                    offset = 2 # in units of sigmabond_cyl_radius
                elif disp == diTUBES:
                    # I don't like this being so small, but it's in the spec.
                    scale = 0.333
                    offset = 0.333 * 2
                else:
                    # pure guesses; used for diTrueCPK and diDNACYLINDER
                    #   [bruce 080213]
                    scale = 0.333
                    offset = 0.333 * 2
                # now modify geom for these other cyls
                a1pos, c1, center, c2, a2pos, toolong = selfgeom
                del c1, center, c2, toolong
                cylrad = scale * sigmabond_cyl_radius
                offset *= sigmabond_cyl_radius # use this offset in the loop
                for pvec1, pvec2 in zip(pvecs1, pvecs2):
                    a1posm = a1pos + offset * pvec1
                    a2posm = a2pos + offset * pvec2
                    # Correct in either abs or rel coords.
                    geom = self.geom_from_posns(a1posm, a2posm)
                    draw_bond_cyl(atom1, atom2, disp, v1, v2, color1, color2,
                                  bondcolor, highlighted, detailLevel,
                                  cylrad, shorten_tubes, geom, v6_for_bands,
                                  povfile, dir_info )

    if draw_sigma_cyl or howmany == 1:
        # draw one central cyl, regardless of bond type
        geom = selfgeom #e could be optimized to compute less for CPK case
        cylrad = sigmabond_cyl_radius
        draw_bond_cyl(atom1, atom2, disp, v1, v2, color1, color2,
                      bondcolor, highlighted, detailLevel,
                      cylrad, shorten_tubes, geom, v6_for_bands,
                      povfile, dir_info )

    if self.v6 != V_SINGLE:
        if draw_vanes:
            if debug_flags.atom_debug:
                import graphics.drawing.draw_bond_vanes as draw_bond_vanes
                import utilities.debug as debug
                #bruce 050825 renabled this, using reload_once_per_event
                debug.reload_once_per_event(draw_bond_vanes)
                pass
            from graphics.drawing.draw_bond_vanes import draw_bond_vanes
            # This calls self.get_pi_info().
            draw_bond_vanes(self, glpane, sigmabond_cyl_radius, col)
            pass
        if draw_bond_letters and glpane.permit_draw_bond_letters:
            # note: bruce 071023 added glpane.permit_draw_bond_letters to
            # replace this test and remove some import cycles:
            ## isinstance(glpane, MMKitView):
            # [Huaicai 11/14/05: added the MMKitView test to fix bug 969,884]
            #
            # Ideally, it would be better to disable the bond letter feature
            # completely in the MMKit thumbview for Library, but not for single
            # atoms (though those use the same glpane)... could we do this by
            # testing ratio of atomcount to glpane size? or by the controlling
            # code setting a flag?  (For now, just ignore the issue, and disable
            # it in all thumbviews.)  [bruce 051110/071023]
            try:
                glpane_out = glpane.out
            except AttributeError:
                # kluge for Element Selector [bruce 050507 bugfix]
                glpane_out = V(0.0, 0.0, 1.0)
                pass
            textpos = self.center + glpane_out * 0.6
                # Note -- this depends on the current rotation when the display
                # list is made! But it's ok for now.  We could fix this by
                # having a separate display list, or no display list, for these
                # kinds of things -- would need a separate display list per
                # chunk and per offset. Not worth it for now.
            text = bond_letter_from_v6(self.v6).upper()
            text_qcolor = QColor(255, 255, 255) # white
            
            # fontname and fontsize: only some combos work, e.g. Times 10
            # (maybe) but not 12, and Helvetica 12 but not 10, and this might be
            # platform-dependent; when it fails, for Mac it just draws nothing
            # (bug 1113) but for Windows & Linux it tracebacks (bug 1112).  So
            # to fix those bugs, I'm just using the same fontname/fontsize as in
            # all our other renderText calls.
            # (Some of those have pushMatrix but that is not needed.  Most of
            # those disable depth test, but that looks bad here and is also not
            # needed.  We don't call drawer.drawtext since it always disables
            # depth test.)  [bruce 051111]
            font = QFont(QString("Helvetica"), 12)
                ###e should adjust fontsize based on scale, depth... (if not for
                ###  the bugs mentioned above)
                #e should construct font only once, keep in glpane

            glDisable(GL_LIGHTING)
            glpane.qglColor(text_qcolor)
            p = textpos
            
            #k need explicit QString??
            glpane.renderText(p[0], p[1], p[2], QString(text), font)
                ### BUG: it seems that this text is not stored in display lists.
                # Evidence: for external bonds (not presently in display lists)
                # this works reliably, but for internal bonds, it seems to only
                # get rendered when the display list is remade (e.g. when one
                # of the chunk's atoms is selected), not when it's redrawn
                # (e.g. for redraws due to highlighting or viewpoint changes).
                # I know that in the past (when this was first implemented),
                # that was not the case (since rotating the chunk made the bond
                # letters show up in the wrong direction due to glpane.out
                # no longer being valid). It might be a new bug with Qt 4 --
                # I'm not sure how new it is.
                # [bruce 081204 comment]
                
            # bug 969 traceback (not on Mac) claims "illegal OpenGL op" in this
            # line! [as of 051110 night] [which line?]
            
            glEnable(GL_LIGHTING)
            pass
        pass
    return # from draw_bond_main

def multicyl_pvecs(howmany, a2py, a2pz):
    if howmany == 2:
        # note, for proper double-bond alignment, this has to be a2py, not a2pz!
        return [a2py, -a2py]
    elif howmany == 3:
        # 0.866 is sqrt(3)/2
        return [a2py, - a2py * 0.5 + a2pz * 0.866, - a2py * 0.5 - a2pz * 0.866]
    else:
        assert 0
    pass

def draw_bond_cyl(atom1, atom2, disp, v1, v2, color1, color2,
                  bondcolor, highlighted, detailLevel,
                  sigmabond_cyl_radius, shorten_tubes, geom, v6,
                  povfile, dir_info ):
    """
    Draw one cylinder, which might be for a sigma bond, or one of 2 or 3 cyls
    for double or triple bonds.

    [private function for a single caller, which is the only reason such a long
    arglist is tolerable]
    """    
    a1pos, c1, center, c2, a2pos, toolong = geom
    
    #following turns off the bond stretch indicators based on the user
    #preference
    bool_showBondStretch = env.prefs[showBondStretchIndicators_prefs_key]
    if not bool_showBondStretch:
        toolong = False
       
    # kluge, bruce 080130:
    if (atom1._dna_updater__error and atom2._dna_updater__error):
        toolong = False
    
    # If atom1 or atom2 is a PAM atom, we recompute the sigmabond_cyl_radius.
    # After experimenting, the standard <TubeRadius> works well for the 
    # standard radius for both diBALL and diTUBES display styles. 
    # This is multiplied by the "DNA Strut Scale Factor" user preference to
    # compute the final radius. Mark 2008-01-31.
    ### REVIEW: is this correct for diTrueCPK and/or diDNACYLINDER?
    ### [bruce comment 080213]
    if (atom1.element.pam or atom2.element.pam):            
        if disp == diBALL or disp == diTUBES:
            # The following increases the radius of the axis bonds by the
            # 'axisFactor'. The new radius makes it easy to drag the dna segment
            # while in DnaSegment_EditCommand. Another possibility is to
            # increase this radius only in DnaSegment_EditCommand. But
            # retrieving the current command information in this method may not
            # be straigtforward and is infact kludgy (like this) The radius of
            # axis bonds was increased per Mark's request for his presentation
            # at FNANO08 -- Ninad 2008-04-22
            if atom1.element.role == 'axis' and atom2.element.role == 'axis':
                axisFactor = 2.0
            else:
                axisFactor = 1.0
                
            sigmabond_cyl_radius = \
                TubeRadius * env.prefs[dnaStrutScaleFactor_prefs_key]*axisFactor
    
        
        
    # Figure out banding (only in CPK [review -- old or true cpk?] or Tubes
    # display modes).  This is only done in multicyl mode, because caller makes
    # our v6 equal V_SINGLE otherwise.  If new color args are needed, they
    # should be figured out here (perhaps by env.prefs lookup).  This code ought
    # to be good enough for A6, but if Mark wants to, he and Huaicai can modify
    # it in this file.  [bruce 050806]
    if v6 == V_AROMATIC:
        # Use aromatic banding on this cylinder (bond order 1.5).
        banding = V_AROMATIC
        band_color = ave_colors(0.5, green, yellow)
    elif v6 == V_GRAPHITE:
        # Use graphite banding on this cylinder (bond order 1.33).
        banding = V_GRAPHITE
        band_color = ave_colors(0.8, green, yellow)
    elif v6 == V_CARBOMERIC:
        # Use carbomeric banding on this cylinder (bond order 2.5) (length is
        # same as aromatic for now).
        banding = V_AROMATIC
        band_color = ave_colors(0.7, red, white)
    else:
        banding = None # no banding needed on this cylinder
    if banding and disp not in (diBALL, diTUBES):
        # review: do we want banding in diTrueCPK as well?
        # todo: if this ever happens, could optimize above instead
        banding = None
    if banding:
        # 0.33, 0.5, or for carbomeric, in principle 1.5 but in practice 0.5
        band_order = float(banding - V_SINGLE)/V_SINGLE
        bandvec = (a2pos - a1pos)/2.0 # from center to a2pos
        # If this was 1 we'd cover it entirely; this is measured to atom
        # centers, not just visible part...
        bandextent = band_order/2.5
        bandpos1 = center - bandvec * bandextent
        bandpos2 = center + bandvec * bandextent
        if highlighted:
            band_color = ave_colors(0.5, band_color, blue)
        band_color = ave_colors(0.8, band_color, black)
    # End of figuring out banding, though to use it, the code below must be
    # modified.

    if povfile is not None:
        # Ideal situation, worth doing when we have time:
        # if povfile had the equivalent of drawcylinder and drawsphere (and took
        # radius prefs into account in tube macros), we could just run the
        # non-povfile code below on it, and get identical pov and non-pov
        # rendering.
        # (for bonds, incl multicyl and banded, tho not for vanes/ribbons or
        #   bond letters or half-invisible bonds
        # (with spheres -- not so great a feature anyway)
        # until other code is modified and povfile has a few more primitives
        # needed for those).
        #
        # Current situation: for povfiles, we just replace the rest of this
        # routine with the old povfile bond code, modified to use macros that
        # take a radius, plus a separate call for banding.  This should cover:
        # multiple cyls, banding, radius prefs; but won't cover: color prefs,
        # other fancy things listed above.  [bruce 060622]
        #
        # It also doesn't yet cover the debug_pref "draw arrows on all
        # directional bonds?" -- i.e. for now that does not affect
        # POV-Ray. [bruce 070415]
        old_writepov_bondcyl(atom1, atom2, disp, a1pos, c1, center, c2, a2pos,
                             toolong, color1, color2,
                             povfile, sigmabond_cyl_radius)
        if banding and disp in (diBALL, diTUBES):
            povfile.drawcylinder(band_color, bandpos1, bandpos2,
                                 sigmabond_cyl_radius * 1.2)
        return

    a1pos_not_shortened = + a1pos
    a2pos_not_shortened = + a2pos

    if disp == diLINES:
        width = env.prefs[linesDisplayModeThickness_prefs_key] #bruce 050831
        if width <= 0:
            # fix illegal value to prevent exception
            width = 1
        if not toolong:
            drawline(color1, a1pos, center, width = width)
            drawline(color2, a2pos, center, width = width)
        else:
            drawline(color1, a1pos, c1, width = width)
            drawline(color2, a2pos, c2, width = width)
            toolong_color = env.prefs.get(bondStretchColor_prefs_key)
                # toolong_color is never highlighted here, since we're not sure
                # highlighting bonds in LINES mode is good at all
            drawline(toolong_color, c1, c2, width = width)
    elif disp == diBALL:
        if bondcolor is None: #bruce 050805
            ## bondColor [before bruce 050805]
            bondcolor = env.prefs.get(diBALL_bondcolor_prefs_key)
            pass
        drawcylinder(bondcolor, a1pos, a2pos, sigmabond_cyl_radius)
        if banding:
            drawcylinder(band_color, bandpos1, bandpos2,
                         sigmabond_cyl_radius * 1.2)
    elif disp == diDNACYLINDER:
        # note: this case is also used for diTrueCPK as of Mark 080212,
        # since disp is reset from diTrueCPK to diDNACYLINDER by caller
        # [bruce comment 080213]
        # [note: there is a similar case in draw_bond_cyl and
        # old_writepov_bondcyl as of bruce 080213 povray bugfix]
        if bondcolor is None:
            # OK to use diBALL_bondcolor_prefs_key for now. Mark 2008-02-13.
            bondcolor = env.prefs.get(diBALL_bondcolor_prefs_key) 
        drawcylinder(bondcolor, a1pos, a2pos, sigmabond_cyl_radius)
    elif disp == diTUBES:
        if shorten_tubes:
            # see Atom.howdraw for tubes; constant (0.9) might need adjusting
            #bruce 050726 changed that constant from 1.0 to 0.9
            rad = TubeRadius * 1.1 * 0.9

            # warning: if atom1 is a singlet, a1pos == center, so center - a1pos
            # is not good to use here.
            vec = norm(a2pos - a1pos)
            if atom1.element is not Singlet:
                a1pos = a1pos + vec * rad
            if atom2.element is not Singlet:
                a2pos = a2pos - vec * rad
            # note: this does not affect bandpos1, bandpos2 (which is good)
        ###e bruce 050513 future optim idea: when color1 == color2, draw just
        # one longer cylinder, then overdraw toolong indicator if needed.
        # Significant for big parts. BUT, why spend time on this when I
        # expect we'll do this drawing in C code before too long?

        if not toolong:
            # "not !=" is in case colors are Numeric arrays
            # (don't know if possible)
            if v1 and v2 and (not color1 != color2):
                #bruce 050516 optim: draw only one cylinder in this common case
                drawcylinder(color1, a1pos, a2pos, sigmabond_cyl_radius)
            else:
                if v1:
                    drawcylinder(color1, a1pos, center, sigmabond_cyl_radius)
                if v2:
                    drawcylinder(color2, center, a2pos, sigmabond_cyl_radius)
                        #bruce 070921 bugfix: draw in consistent direction! This
                        # affects alignment of cylinder cross section (a 13-gon)
                        # around cylinder axis. Highlighted bond might differ
                        # from regular bond in whether color1 != color2, so
                        # without this fix, it can be slightly rotated, causing
                        # part of the unhighlighted one to show through.
                if not (v1 and v2):
                    drawsphere(black, center, sigmabond_cyl_radius, detailLevel)
        else:
            if highlighted:
                toolong_color = _default_toolong_hicolor ## not yet in prefs db
            else:
                toolong_color = env.prefs.get(bondStretchColor_prefs_key)
            drawcylinder(toolong_color, c1, c2, sigmabond_cyl_radius)
            if v1:
                drawcylinder(color1, a1pos, c1, sigmabond_cyl_radius)
            else:
                drawsphere(black, c1, sigmabond_cyl_radius, detailLevel)
            if v2:
                drawcylinder(color2, c2, a2pos, sigmabond_cyl_radius)
                    #bruce 070921 bugfix: draw in consistent direction!
                    # See comment above.
            else:
                drawsphere(black, c2, sigmabond_cyl_radius, detailLevel)
        if banding:
            drawcylinder(band_color, bandpos1, bandpos2,
                         sigmabond_cyl_radius * 1.2)
        pass

    # maybe draw arrowhead showing bond direction [bruce 070415]
    # review: do we want this in diTrueCPK and/or diDNACYLINDER?
    # [bruce comment 080213]
    direction, is_directional = dir_info
    if (direction or is_directional) and (disp in (diBALL, diTUBES)):        
        # If the bond has a direction, draw an arrowhead in the middle of the
        # bond-cylinder to show it.  (Make that gray if this is ok, or red if
        # this is a non-directional bond.)  If it has no direction but "wants
        # one" (is_directional), draw something to indicate that, not sure
        # what. ##e
        #
        # To fix a bug in highlighting of the arrowhead, use a1pos_not_shortened
        # and a2pos_not_shortened rather than a1pos and a2pos.  Also split
        # draw_bond_cyl_arrowhead out of this code. [bruce 080121]
        draw_bond_cyl_arrowhead(a1pos_not_shortened,
                                a2pos_not_shortened,
                                direction,
                                is_directional,
                                color1,
                                color2,
                                sigmabond_cyl_radius)
                                 
        pass

    return # from draw_bond_cyl

# ==

#bruce 080121 Split this out.
def draw_bond_cyl_arrowhead(a1pos,
                            a2pos,
                            direction, # bond direction, relative to atom1
                            is_directional,
                            color1,
                            color2,
                            sigmabond_cyl_radius):
    """
    [private helper for draw_bond_cyl]
    Draw the bond-direction arrowhead for the bond cylinder with the
    given geometric/color/bond_direction parameters.

    Note that a1pos and a2pos should be the same for highlighted
    or unhighlighted drawing (even if they differ when the caller
    draws the main bond cylinder), or the arrowhead highlight might not
    properly align with its unhighlighted form.
    """
    if direction < 0:
        a1pos, a2pos = a2pos, a1pos
        direction = - direction
        color1, color2 = color2, color1
    error = direction and not is_directional
    if error:
        color = red
    else:
        color = color2
    if direction == 0:
        # print "draw a confused/unknown direction somehow"
        # two orange arrows? no arrow?
        pass
    else:            
        # draw arrowhead pointing from a1pos to a2pos, closer to a2pos.
        pos = a1pos
        axis = a2pos - a1pos
        drawrad = sigmabond_cyl_radius
        pts = [pos + t * axis for t in (0.6, 1.0)]
        # call drawcylinder with two radii for taper.
        # note: this is drawn by shader cylinders or as a polycone
        # depending on debug_prefs [bruce 090225 revision]
        drawcylinder(color, pts[0], pts[1], (drawrad * 2, 0))
    return

# ==

def writepov_bond(self, file, dispdef, col):
    """
    Write this bond 'self' to a povray file (always using absolute coords, even
    for internal bonds).
    """
    disp = max(self.atom1.display, self.atom2.display)
    if disp == diDEFAULT:
        disp = dispdef
    if disp < 0:
        disp = dispdef
    if disp in (diTrueCPK, diDNACYLINDER):
        # new feature, described in the other instance of this code.
        # warning: this code is duplicated in two places in this file.
        # [bruce 080212]
        if bond_draw_in_CPK(self):
            disp = diDNACYLINDER
        else:
            return
    if disp in (diLINES, diBALL, diTUBES, diDNACYLINDER):
        # (note: self is a bond.)
        povfile = writepov_to_file(file, col)
        detailLevel = 2 #k value probably has no effect
        glpane = None #k value probably has no effect
        highlighted = False
        draw_bond_main(self, glpane, disp, col, detailLevel, highlighted, povfile)
    return

def old_writepov_bondcyl(atom1, atom2, disp, a1pos, c1, center, c2, a2pos,
                         toolong, color1, color2, povfile, rad = None):
    """
    [private function for this module, still used by new multicyl code 060622,
    once per cyl]

    Write one bond cylinder. atom args are only for checking rcovs vs DELTA. 
    """
    if disp == diLINES:
        if not toolong:
            povfile.line(a1pos, a2pos, color1)
        else:
            povfile.line(a1pos, center, color1)
            povfile.line(center, a2pos, color2)
    if disp == diBALL:
        bondColor = povfile.getBondColor()
        if not bondColor:
            bondColor = color1
        povfile.bond(a1pos, a2pos, bondColor, rad)
    elif disp == diDNACYLINDER:
        # note: this case is also used for diTrueCPK as of Mark 080212,
        # since disp is reset from diTrueCPK to diDNACYLINDER by caller
        # [bruce comment 080213]
        # [note: there is a similar case in draw_bond_cyl and
        # old_writepov_bondcyl as of bruce 080213 povray bugfix]
        if 1:
            # bruce 080213 pure guesses about how best to do this in povray;
            # ideally we'd just clean up all this code to use the same
            # drawcylinder calling API in both povray and non-povray.
            bondColor = povfile.getBondColor()
            if not bondColor:
                bondColor = color1
            povfile.bond(a1pos, a2pos, bondColor, rad) # .tube3 or .bond??
    if disp == diTUBES:
        #Huaicai: If rcovalent is close to 0, like singlets, avoid 0 length 
        # cylinder written to a pov file    
        DELTA = 1.0E-5
        isSingleCylinder = False
        if atom1.atomtype.rcovalent < DELTA:
                col = color2
                isSingleCylinder = True
        if atom2.atomtype.rcovalent < DELTA:
                col = color1
                isSingleCylinder = True
        if isSingleCylinder:
            povfile.tube3(a1pos, a2pos, col, rad)
        else:
            if not toolong:
                povfile.tube2(a1pos, color1, center, a2pos, color2, rad)
            else:
                povfile.tube1(a1pos, color1, c1, c2, a2pos, color2, rad)
    return # from old_writepov_bondcyl

# end