summaryrefslogtreecommitdiff
path: root/cad/src/graphics/drawing/Guides.py
blob: fe7357c52ed5a97b11d33377a1560c92ab5e1499 (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
# Copyright 2008 Nanorex, Inc.  See LICENSE file for details.
"""
Guides.py - Draws horizontal and vertical rulers along the edges of the
3D graphics area.

@author:    Mark Sims
@copyright: 2008 Nanorex, Inc.  See LICENSE file for details.
@version:   $Id$
@license:   GPL
"""

from OpenGL.GL import glDisable
from OpenGL.GL import glEnable
from OpenGL.GL import GL_LIGHTING
from OpenGL.GL import glPopMatrix
from OpenGL.GL import glPushMatrix
from OpenGL.GL import GL_BLEND
from OpenGL.GL import glBegin
from OpenGL.GL import glBlendFunc
from OpenGL.GL import glColor3fv
from OpenGL.GL import glColor4fv
from OpenGL.GL import glDepthMask
from OpenGL.GL import GL_DEPTH_TEST
from OpenGL.GL import glEnd
from OpenGL.GL import GL_FALSE
from OpenGL.GL import GL_LINES
from OpenGL.GL import glLoadIdentity
from OpenGL.GL import glMatrixMode
from OpenGL.GL import GL_MODELVIEW
from OpenGL.GL import GL_TRUE
from OpenGL.GL import glVertex
from OpenGL.GL import glViewport
from OpenGL.GLU import gluOrtho2D
from OpenGL.GL import GL_SRC_ALPHA
from OpenGL.GL import GL_ONE_MINUS_SRC_ALPHA
from OpenGL.GL import glRectf
from OpenGL.GL import GL_PROJECTION

from OpenGL.GLU import gluUnProject # piotr 080326

from PyQt4.Qt import QFont, QFontMetrics, QString, QColor
from widgets.widget_helpers import RGBf_to_QColor

from geometry.VQT import V
from utilities.constants import lightgray, darkgray, black

from utilities.debug import print_compact_stack

import sys
import foundation.env as env
from utilities.prefs_constants import displayVertRuler_prefs_key
from utilities.prefs_constants import displayHorzRuler_prefs_key
from utilities.prefs_constants import rulerPosition_prefs_key
from utilities.prefs_constants import rulerColor_prefs_key
from utilities.prefs_constants import rulerOpacity_prefs_key

# These must match the order items appear in the ruler "Position" combobox
# in the preferences dialog.
_lower_left = 0
_upper_left = 1
_lower_right = 2
_upper_right = 3

def getRulerDrawingParameters(width, height, aspect,
                              scale, zoomFactor,
                              ruler_position):
    """
    Compute and return all the ruler drawing parameters needed by draw().

    @param width: Width of the 3D graphics area in pixels.
    @type  width: int

    @param height: Height of the 3D graphics area in pixels.
    @type  height: int

    @param aspect: The aspect ratio of the 3D graphics area (width / height)
    @type  aspect: float

    @param scale: Half-height of the field of view in Angstroms.
    @type  scale: float

    @param zoomFactor: Zoom factor.
    @type  zoomFactor: float

    @param ruler_position: The ruler position, where:
                           - 0 = lower left
                           - 1 = upper left
                           - 2 = lower right
                           - 3 = upper right
    @type ruler_position: int
    """

    # Note: Bruce's "drawRulers.py" review email (2008-02-24) includes
    # two good suggestions for making code in this method clearer and
    # more general. I plan to implement them when time allows, but it is
    # not urgent. --Mark.

    viewHeight = scale * zoomFactor * 2.0 # In Angstroms.

    # num_horz_ticks gets initialized here, but may get changed further below.
    # num_vert_ticks is set after num_horz_ticks is finally set.
    num_horz_ticks = int(viewHeight)

    tickmark_spacing = 1.0 / viewHeight * height
    tickmark_spacing_multiplier = 1.0

    # This section computes the the tick mark drawing parameters based on
    # the viewHeight of the 3D graphics area. Be careful if you change these
    # values. Mark 2008-02-07
    if viewHeight <= 5.5:
        units_text = "A" # Angstroms
        units_format = "%2d"
        units_scale = 1.0
        unit_label_inc = 1
        long_tickmark_inc = 10
        medium_tickmark_inc = 5
    elif viewHeight <= 20.1:
        units_text = "A" # Angstroms
        units_format = "%2d"
        units_scale = 1.0
        unit_label_inc = 5
        long_tickmark_inc = 10
        medium_tickmark_inc = 5
    elif viewHeight <= 50.1:
        units_text = "nm" # nanometers
        units_format = "%-3.1f"
        units_scale = 0.1
        unit_label_inc = 5
        long_tickmark_inc = 10
        medium_tickmark_inc = 5
    elif viewHeight <= 101.0:
        units_text = "nm" # nanometers
        units_format = "%2d"
        units_scale = 0.1
        unit_label_inc = 10
        long_tickmark_inc = 10
        medium_tickmark_inc = 5
    elif viewHeight <= 201.0:
        units_text = "nm" # nanometers
        units_format = "%2d"
        units_scale = .5
        unit_label_inc = 2
        long_tickmark_inc = 10
        medium_tickmark_inc = 2
        num_horz_ticks = int(num_horz_ticks * 0.2)
        tickmark_spacing_multiplier = 5.0
    elif viewHeight <= 1001.0:
        units_text = "nm" # nanometers
        units_format = "%2d"
        units_scale = 1.0
        unit_label_inc = 5
        long_tickmark_inc = 10
        medium_tickmark_inc = 5
        num_horz_ticks = int(num_horz_ticks * 0.1)
        tickmark_spacing_multiplier = 10.0
    elif viewHeight <= 2001.0:
        units_text = "nm" # nanometers
        units_format = "%2d"
        units_scale = 1.0
        unit_label_inc = 10
        long_tickmark_inc = 10
        medium_tickmark_inc = 5
        num_horz_ticks = int(num_horz_ticks * 0.1)
        tickmark_spacing_multiplier = 10.0
    elif viewHeight <= 5005.0:
        units_text = "nm" # nanometers
        units_format = "%2d"
        units_scale = 2.0
        unit_label_inc = 10
        long_tickmark_inc = 10
        medium_tickmark_inc = 5
        num_horz_ticks = int(num_horz_ticks * 0.05)
        tickmark_spacing_multiplier = 20.0
    elif viewHeight <= 10005.0:
        units_text = "Um" # micrometers
        units_format = "%-3.1f"
        units_scale = 0.01
        unit_label_inc = 10
        long_tickmark_inc = 10
        medium_tickmark_inc = 5
        num_horz_ticks = int(num_horz_ticks * 0.01)
        tickmark_spacing_multiplier = 100.0
    elif viewHeight <= 30005.0:
        units_text = "Um" # micrometers
        units_format = "%-3.1f"
        units_scale = 0.1
        unit_label_inc = 1
        long_tickmark_inc = 10
        medium_tickmark_inc = 5
        num_horz_ticks = int(num_horz_ticks * 0.001)
        tickmark_spacing_multiplier = 1000.0
    elif viewHeight <= 50005.0:
        units_text = "Um" # micrometers
        units_format = "%-3.1f"
        units_scale = 0.1
        unit_label_inc = 5
        long_tickmark_inc = 10
        medium_tickmark_inc = 5
        num_horz_ticks = int(num_horz_ticks * 0.001)
        tickmark_spacing_multiplier = 1000.0
    else:
        units_text = "Um" # micrometers
        units_format = "%2d"
        units_scale = 0.1
        unit_label_inc = 10
        long_tickmark_inc = 10
        medium_tickmark_inc = 5
        num_horz_ticks = int(num_horz_ticks * 0.001)
        tickmark_spacing_multiplier = 1000.0

    # Kludge alert. If viewHeight gets larger than 250000.0 (25 Um), set a
    # flag passed to draw() so that it won't draw tick marks and unit text
    # on rulers.
    if viewHeight > 250000.0:
        draw_ticks_and_text = False
    else:
        draw_ticks_and_text = True

    num_vert_ticks = int(num_horz_ticks * aspect) + 1

    _DEBUG = False
    if _DEBUG:
        print "hr ticks=", num_horz_ticks, "vr ticks=", num_vert_ticks
        print "tickmark_spacing=", tickmark_spacing, "viewHeight=", viewHeight
        print "units_scale=", units_scale, "unit_label_inc=", unit_label_inc

    # Compute ruler width based on font size, which should be a user pref
    # for far-sighted users that need larger unit text in rulers.
    # Mark 2008-02-20
    ruler_thickness = 15.0

    vr_thickness = ruler_thickness
    hr_thickness = ruler_thickness

    vr_long_tick_len   = vr_thickness * 0.75
    vr_medium_tick_len = vr_thickness * 0.5
    vr_short_tick_len  = vr_thickness * 0.25

    hr_long_tick_len   = vr_thickness * 0.75
    hr_medium_tick_len = vr_thickness * 0.5
    hr_short_tick_len  = vr_thickness * 0.25

    if ruler_position == _lower_left:
        ruler_origin = V(0.0, 0.0, 0.0)
        #units_text_origin = V(3.0, 5.0, 0.0)
        # piotr 080326: moved the text origin to the cell center
        units_text_origin = ruler_origin + V(hr_thickness/2,vr_thickness/2,0.0)

        ruler_start_pt = ruler_origin \
                       + V(vr_thickness, hr_thickness, 0.0)

        origin_square_pt1 = V(0.0, 0.0, 0.0)
        origin_square_pt2 = V(vr_thickness, hr_thickness, 0.0)

        # VR vars.
        vr_rect_pt1 = V(0.0, hr_thickness, 0.0)
        vr_rect_pt2 = V(vr_thickness, height, 0.0)

        vr_line_pt1 = V(vr_thickness, 0.0, 0.0)
        vr_line_pt2 = V(vr_thickness, height, 0.0)

        vr_long_tick_len   *= -1.0
        vr_medium_tick_len *= -1.0
        vr_short_tick_len  *= -1.0

        vr_units_x_offset =  -vr_thickness
        vr_units_y_offset =  3.0

        vr_tickmark_spacing = tickmark_spacing

        # HR vars.
        hr_rect_pt1 = V(vr_thickness, 0.0, 0.0)
        hr_rect_pt2 = V(width, hr_thickness, 0.0)

        hr_line_pt1 = V(0.0, hr_thickness, 0.0)
        hr_line_pt2 = V(width, hr_thickness, 0.0)

        hr_long_tick_len   *= -1.0
        hr_medium_tick_len *= -1.0
        hr_short_tick_len  *= -1.0

        hr_units_x_offset =  3.0
        hr_units_y_offset =  -hr_thickness * 0.8

        hr_tickmark_spacing = tickmark_spacing

    elif ruler_position == _upper_left:
        hr_thickness *= -1 # Note: Thickness is a negative value.

        ruler_origin = V(0.0, height, 0.0)
        #units_text_origin = ruler_origin \
        #                  + V(3.0, hr_thickness + 4.0, 0.0)
        # piotr 080326: moved the text origin to the cell center
        units_text_origin = ruler_origin + V(vr_thickness/2,hr_thickness/2,0.0)

        ruler_start_pt = ruler_origin \
                       + V(vr_thickness, hr_thickness, 0.0)

        origin_square_pt1 = V(0.0, height + hr_thickness, 0.0)
        origin_square_pt2 = V(vr_thickness, height, 0.0)

        # VR vars.
        vr_rect_pt1 = V(0.0, 0.0, 0.0)
        vr_rect_pt2 = V(vr_thickness, height + hr_thickness, 0.0)

        vr_line_pt1 = V(vr_thickness, 0.0, 0.0)
        vr_line_pt2 = V(vr_thickness, height, 0.0)

        vr_long_tick_len   *= -1.0
        vr_medium_tick_len *= -1.0
        vr_short_tick_len  *= -1.0

        vr_units_x_offset =  -vr_thickness
        vr_units_y_offset =  -10.0

        # HR vars.
        hr_rect_pt1 = V(vr_thickness, height + hr_thickness, 0.0)
        hr_rect_pt2 = V(width, height, 0.0)

        hr_line_pt1 = V(0, height + hr_thickness, 0.0)
        hr_line_pt2 = V(width, height + hr_thickness, 0.0)

        hr_units_x_offset =  3.0
        hr_units_y_offset =  -hr_thickness * 0.5

        vr_tickmark_spacing = -tickmark_spacing
        hr_tickmark_spacing =  tickmark_spacing

    elif ruler_position == _lower_right:
        ruler_origin = V(width, 0.0, 0.0)
        vr_thickness *= -1 # Note: Thickness is a negative value.
        #units_text_origin = ruler_origin + V(vr_thickness + 3.0, 2.0, 0.0)
        # piotr 080326: moved the text origin to the cell center
        units_text_origin = ruler_origin + V(vr_thickness/2,hr_thickness/2,0.0)

        ruler_start_pt = ruler_origin \
                       + V(vr_thickness, hr_thickness, 0.0)

        origin_square_pt1 = V(width + vr_thickness, 0.0, 0.0)
        origin_square_pt2 = V(width, hr_thickness, 0.0)

        # VR vars.
        vr_rect_pt1 = V(width + vr_thickness, hr_thickness, 0.0)
        vr_rect_pt2 = V(width, height, 0.0)

        vr_line_pt1 = V(width + vr_thickness, 0.0, 0.0)
        vr_line_pt2 = V(width + vr_thickness, height, 0.0)

        vr_units_x_offset =  3.0 #@ Need way to right justify unit text!
        vr_units_y_offset =  3.0

        vr_tickmark_spacing = tickmark_spacing

        # HR vars.
        hr_rect_pt1 = V(0.0, 0.0, 0.0)
        hr_rect_pt2 = V(width + vr_thickness, hr_thickness, 0.0)

        hr_line_pt1 = V(0.0, hr_thickness, 0.0)
        hr_line_pt2 = V(width, hr_thickness, 0.0)

        hr_long_tick_len   *= -1.0
        hr_medium_tick_len *= -1.0
        hr_short_tick_len  *= -1.0

        hr_units_x_offset =  -15.0 #@ Need way to right justify unit text!
        hr_units_y_offset =  -hr_thickness * 0.8

        hr_tickmark_spacing = -tickmark_spacing

    elif ruler_position == _upper_right:
        ruler_origin = V(width, height, 0.0)
        vr_thickness *= -1 # Note: Thickness is a negative value.
        hr_thickness *= -1 # Note: Thickness is a negative value.
        #units_text_origin = ruler_origin + V(vr_thickness * 0.8,
        #                                     hr_thickness * 0.8,
        #                                     0.0)
        # piotr 080326: moved the text origin to the cell center
        units_text_origin = ruler_origin + V(hr_thickness/2,vr_thickness/2,0.0)

        ruler_start_pt = ruler_origin \
                       + V(vr_thickness, hr_thickness, 0.0)

        origin_square_pt1 = V(width + vr_thickness,
                              height + hr_thickness,
                              0.0)
        origin_square_pt2 = V(width, height, 0.0)

        # VR vars.
        vr_rect_pt1 = V(width + vr_thickness, 0.0, 0.0)
        vr_rect_pt2 = V(width, height + hr_thickness, 0.0)

        vr_line_pt1 = V(width + vr_thickness,    0.0, 0.0)
        vr_line_pt2 = V(width + vr_thickness, height, 0.0)

        vr_units_x_offset =  3.0 #@ Need way to right justify unit text!
        vr_units_y_offset =  -10.0

        vr_tickmark_spacing = -tickmark_spacing

        # HR vars.
        hr_rect_pt1 = V(0.0, height + hr_thickness, 0.0)
        hr_rect_pt2 = V(width + vr_thickness, height, 0.0)

        hr_line_pt1 = V(  0.0, height + hr_thickness, 0.0)
        hr_line_pt2 = V(width, height + hr_thickness, 0.0)

        hr_units_x_offset =  -15.0 #@ Need way to right justify unit text!
        hr_units_y_offset =  -hr_thickness * 0.5

        hr_tickmark_spacing = -tickmark_spacing

    else:
        msg = "bug: Illegal ruler position value (must be 0-3). Current "\
            "value is %d. Ignoring." % ruler_position
        print_compact_stack(msg)

    return (draw_ticks_and_text,
            units_text,
            units_format,
            units_scale,
            unit_label_inc,
            long_tickmark_inc,
            medium_tickmark_inc,
            num_vert_ticks,
            num_horz_ticks,
            tickmark_spacing_multiplier,

            ruler_origin, ruler_start_pt,

            units_text_origin, origin_square_pt1, origin_square_pt2,

            vr_thickness, vr_tickmark_spacing,
            vr_long_tick_len, vr_medium_tick_len, vr_short_tick_len,
            vr_rect_pt1, vr_rect_pt2,
            vr_line_pt1, vr_line_pt2,
            vr_units_x_offset, vr_units_y_offset,

            hr_thickness, hr_tickmark_spacing,
            hr_long_tick_len, hr_medium_tick_len, hr_short_tick_len,
            hr_rect_pt1, hr_rect_pt2,
            hr_line_pt1, hr_line_pt2,
            hr_units_x_offset, hr_units_y_offset)

class Guides(object):

    """
    Creates a set of vertical and horizontal rulers in the 3D graphics area.

    A 2D window (pixel) coordinate system is created locally, where:
    - The lower left corner is  (   0.0,    0.0, 0.0)
    - The upper right corner is ( width, height, 0.0)

    This schematic shows the 2D window coodinate system with the rulers
    positioned in the lower left corner (their default position).

                                                             (width, height)
                                                              /
                  +---+--------------------------------------+
                  |   |                                      |
                  | v |                                      |
                  | e |                                      |
                  | r |                                      |
                  | t |                                      |
                  | i |                                      |
                  | c |                                      |
                  | a |  ruler_start_pt                      |
                  | l | (vr_thickness, hr_thickness, 0.0)    |
                  |   |/                                     |
                  +---+--------------------------------------+
                  | A |              horizontal              |
                  +---+--------------------------------------+
                 /
     ruler_origin
    (0.0, 0.0, 0.0)

    It doesn't matter what coordinate system you are in when you call this
    function, and the system will not be harmed, but it does use one level
    on each matrix stack, and it does set matrixmode to GL_MODELVIEW before
    returning.

    Still to do:
    - Optimize. Don't call drawLine() multiple times; create a point list
      and render all tick marks at once.
    - Add support for 2D grid lines.
    - Allow user to control ruler thickness, ruler font size, tick mark color,
      etc via user preferences. (nice to have)
    - Center unit text in origin square (using QFontMetrics class to do this).
      (fixed by piotr 080326)

    @param glpane: the 3D graphics area.
    @type  glpane: L{GLPane)

    @note: There is a pref key called I{displayRulers_prefs_key} that is set
    from the "View > Rulers" menu item. It is used in this function's sole
    caller (GLPane.standard_repaint_0()) to determine whether to draw any
    ruler(s). It is not used here.
    """

    ruler_position = None
    scale = 0.0
    zoomFactor = 0.0
    aspect = 0.0
    ruler_drawing_params = ()

    if sys.platform == "darwin":
        # WARNING: Anything smaller than 9 pt on Mac OS X results in
        # un-rendered text. Not sure why. --Mark 2008-02-27
        rulerFontPointSize = 9
    else:
        rulerFontPointSize = 7
    rulerFont = QFont( QString("Helvetica"), rulerFontPointSize)
    rulerFontMetrics = QFontMetrics(rulerFont) # piotr 080326

    def __init__(self, glpane):
        """
        Constructor for the guide objects (i.e. rulers).
        """
        self.glpane = glpane

    def draw(self):
        """
        Draws the rulers.
        """

        width = self.glpane.width
        height = self.glpane.height

        # These 3 attrs (scale, aspect, and ruler_position) are checked to
        # determine if they've changed. If any of them have,
        # getRulerDrawingParameters() must be called to get new drawing parms.
        if (self.scale  != self.glpane.scale) or \
           (self.zoomFactor  != self.glpane.zoomFactor) or \
           (self.aspect != self.glpane.aspect) or \
           (self.ruler_position != env.prefs[rulerPosition_prefs_key]):

            self.scale = self.glpane.scale
            self.zoomFactor = self.glpane.zoomFactor
            self.aspect = self.glpane.aspect
            self.ruler_position = env.prefs[rulerPosition_prefs_key]

            self.ruler_drawing_params = \
                getRulerDrawingParameters(width, height, self.aspect,
                                          self.scale, self.zoomFactor,
                                          self.ruler_position)

        (draw_ticks_and_text,
         units_text,
         units_format,
         units_scale,
         unit_label_inc,
         long_tickmark_inc,
         medium_tickmark_inc,
         num_vert_ticks,
         num_horz_ticks,
         tickmark_spacing_multiplier,
         ruler_origin, ruler_start_pt,
         units_text_origin, origin_square_pt1, origin_square_pt2,
         vr_thickness, vr_tickmark_spacing,
         vr_long_tick_len, vr_medium_tick_len, vr_short_tick_len,
         vr_rect_pt1, vr_rect_pt2,
         vr_line_pt1, vr_line_pt2,
         vr_units_x_offset, vr_units_y_offset,
         hr_thickness, hr_tickmark_spacing,
         hr_long_tick_len, hr_medium_tick_len, hr_short_tick_len,
         hr_rect_pt1, hr_rect_pt2,
         hr_line_pt1, hr_line_pt2,
         hr_units_x_offset, hr_units_y_offset) = self.ruler_drawing_params

        ruler_color = env.prefs[rulerColor_prefs_key]
        ruler_opacity = env.prefs[rulerOpacity_prefs_key]

        # These may become user preferences in the future.
        tickmark_color = darkgray
        text_color = black

        # Set up 2D (window) coordinate system.
        # Bruce - please review this section.
        glMatrixMode(GL_MODELVIEW)
        glPushMatrix()
        glLoadIdentity()
        glMatrixMode(GL_PROJECTION)
        glPushMatrix()
        glLoadIdentity() # needed!
        gluOrtho2D(0.0, float(width), 0.0, float(height))
        glMatrixMode(GL_MODELVIEW)
        # About this glMatrixMode(GL_MODELVIEW) call, Bruce wrote in a review:
        # The only reason this is desirable (it's not really needed) is if,
        # when someone is editing the large body of drawing code after this,
        # they inadvertently do something which is only correct if the matrix
        # mode is GL_MODELVIEW (e.g. if they use glTranslate to shift a ruler
        # or tickmark position). Most of our drawing code assumes it can do
        # this, so a typical NE1 OpenGL programmer may naturally assume this,
        # which is why it's good to leave the matrix mode as GL_MODELVIEW when
        # entering into a large hunk of ordinary drawing code (especially if
        # it might call other drawing functions, now or in the future).
        # Mark 2008-03-03

        glDisable(GL_LIGHTING)
        glDisable(GL_DEPTH_TEST)
            # Note: disabling GL_DEPTH_TEST is not honored by the 3d version of
            # glpane.renderText -- ruler text can still be obscured by
            # previously drawn less-deep model objects.
            # But the 2d call of renderText in part.py does honor this.
            # The Qt doc says why, if I guess how to interpret it:
            #   http://doc.trolltech.com/4.3/qglwidget.html#renderText
            # says, for the 3d version of renderText only (passing three model
            # coords as opposed to two window coords):
            #   Note that this function only works properly if GL_DEPTH_TEST
            #   is enabled, and you have a properly initialized depth buffer.
            # Possible fixes:
            # - revise ruler_origin to be very close to the screen,
            #   and hope that this disable is merely ignored, not a messup;
            # - or, use the 2d version of the function.
            # I did the latter fix (2d renderText, below) and it seems to work.
            # [bruce 081204 comment]

        # Suppress writing into the depth buffer so anything behind the ruler
        # can still be highlighted/selected.
        glDepthMask(GL_FALSE)

        # == Draw v/h ruler rectangles in the user defined color and opacity.

        glColor4fv(list(ruler_color) + [ruler_opacity])
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        glRectf(origin_square_pt1[0], origin_square_pt1[1],
                origin_square_pt2[0], origin_square_pt2[1]) # Origin square
        if env.prefs[displayVertRuler_prefs_key]:
            glRectf(vr_rect_pt1[0], vr_rect_pt1[1],
                    vr_rect_pt2[0], vr_rect_pt2[1]) # Vertical ruler
        if env.prefs[displayHorzRuler_prefs_key]:
            glRectf(hr_rect_pt1[0], hr_rect_pt1[1],
                    hr_rect_pt2[0], hr_rect_pt2[1]) # Horizontal ruler

        glDisable(GL_BLEND)

        # Set color of ruler lines, tick marks and text.
        glColor3fv(tickmark_color)
        self.glpane.qglColor(RGBf_to_QColor(text_color))

        # Draw unit of measurement in corner (A or nm).
        # piotr 080326: replaced drawText with drawCenteredText
        self.drawCenteredText(units_text, units_text_origin)

        # Kludge alert. Finish drawing ruler edge(s) if we will not be
        # drawing the ruler tick marks and text (only happens when the user
        # is zoomed out to an "absurd scale factor").
        if not draw_ticks_and_text:
            if env.prefs[displayVertRuler_prefs_key]:
                self.drawLine(vr_line_pt1, vr_line_pt2)
            if env.prefs[displayHorzRuler_prefs_key]:
                self.drawLine(hr_line_pt1, hr_line_pt2)

        # == Draw vertical ruler line(s) and tick marks

        if env.prefs[displayVertRuler_prefs_key] and draw_ticks_and_text:

            # Draw vertical line along right/left edge of ruler.
            self.drawLine(vr_line_pt1, vr_line_pt2)

            # Initialize pt1 and pt2, the tick mark endpoints. The first tick
            # mark will span the entire width of the ruler,  which serves as a
            # divider b/w the unit of measure text (i.e. A or nm) and the rest
            # of the ruler.
            pt1 = ruler_start_pt
            pt2 = ruler_start_pt + V(-vr_thickness, 0.0, 0.0)

            # Draw vertical ruler tickmarks, including numeric unit labels
            for tick_num in range(num_horz_ticks + 1):

                # pt1 and pt2 are modified by each iteration of the loop.
                self.drawLine(pt1, pt2)

                # Draw units number beside long tickmarks.
                if not tick_num % unit_label_inc:
                    units_num_origin = pt1 \
                                     + V(vr_units_x_offset,
                                         vr_units_y_offset,
                                         0.0)
                    units_num = units_format % (tick_num * units_scale)
                    self.drawText(units_num, units_num_origin)

                # Update tickmark endpoints for next tickmark.
                pt1 = ruler_start_pt + \
                    V(0.0,
                      vr_tickmark_spacing * tickmark_spacing_multiplier
                      * (tick_num + 1),
                      0.0)

                if not (tick_num + 1) % long_tickmark_inc:
                    pt2 = pt1 + V(vr_long_tick_len, 0.0, 0.0)
                elif not (tick_num + 1) % medium_tickmark_inc:
                    pt2 = pt1 + V(vr_medium_tick_len, 0.0, 0.0)
                else:
                    pt2 = pt1 + V(vr_short_tick_len, 0.0, 0.0)

            # End vertical ruler

        # == Draw horizontal ruler line(s) and tick marks

        if env.prefs[displayHorzRuler_prefs_key] and draw_ticks_and_text:
            # Draw horizontal line along top/bottom edge of ruler.
            self.drawLine(hr_line_pt1, hr_line_pt2)

            # Initialize pt1 and pt2, the tick mark endpoints. The first tick
            # mark will span the entire width of the ruler,  which serves as a
            # divider b/w the unit of measure text (i.e. A or nm) and the rest
            # of the ruler.
            pt1 = ruler_start_pt
            pt2 = ruler_start_pt + V(0.0, -hr_thickness, 0.0)

            # Draw horizontal ruler (with vertical) tickmarks, including its
            # numeric unit labels
            for tick_num in range(num_vert_ticks + 1):

                # pt1 and pt2 are modified by each iteration of the loop.
                self.drawLine(pt1, pt2)

                # Draw units number beside long tickmarks.
                if not tick_num % unit_label_inc:
                    units_num_origin = pt1 \
                                     + V(hr_units_x_offset,
                                         hr_units_y_offset,
                                         0.0)
                    units_num = units_format % (tick_num * units_scale)
                    self.drawText(units_num, units_num_origin)

                # Update tickmark endpoints for next tickmark.
                pt1 = \
                    ruler_start_pt + \
                    V(hr_tickmark_spacing * tickmark_spacing_multiplier
                      * (tick_num + 1),
                      0.0,
                      0.0)

                if not (tick_num + 1) % long_tickmark_inc:
                    pt2 = pt1 + V(0.0, hr_long_tick_len, 0.0)
                elif not (tick_num + 1) % medium_tickmark_inc:
                    pt2 = pt1 + V(0.0, hr_medium_tick_len, 0.0)
                else:
                    pt2 = pt1 + V(0.0, hr_short_tick_len, 0.0)

            # End horizontal ruler

        # Restore OpenGL state.
        glDepthMask(GL_TRUE)
        glEnable(GL_DEPTH_TEST)
        glEnable(GL_LIGHTING)
        glDepthMask(GL_TRUE)
        glMatrixMode(GL_PROJECTION)
        glPopMatrix()
        glMatrixMode(GL_MODELVIEW)
        glPopMatrix()
        return # from drawRulers

    def drawLine(self, pt1, pt2):
        """
        Draws ruler lines that are 1 pixel wide.
        """
        if 0: # Used for debugging.
            return

        glBegin(GL_LINES)
        glVertex(pt1[0], pt1[1], pt1[2])
        glVertex(pt2[0], pt2[1], pt2[2])
        glEnd()
        return

    def drawText(self, text, origin):
        """
        Draws ruler text.
        """
        # WARNING: if we want to optimize by putting the ruler into an OpenGL display list,
        # this might not work anymore, since renderText seems to not work in display lists
        # when used for drawing bond letters in bond_drawer.py. [bruce 081204 comment]

        if not text:
            return

        self.glpane.renderText(
            ## origin[0], origin[1], origin[2], # model coordinates
            origin[0], self.glpane.height - origin[1], # window coordinates
                # using 2d window coordinates fixes the bug in which this text
                # is obscured by model objects which should be behind it.
                # For explanation, see comment near GL_DEPTH_TEST above.
                # [bruce 081204 bugfix]
            QString(text), self.rulerFont )
        return

    def drawCenteredText(self, text, origin):
        """
        Draws ruler text centered, so text center == origin.
        """

        # added by piotr 080326

        if not text:
            return

        fm = self.rulerFontMetrics

        # get the text dimensions in world coordinates
        x0, y0, z0 = gluUnProject(0,0,0)
        x1, y1, z1 = gluUnProject(fm.width(text),fm.ascent(),0)

        # compute a new origin relative to the old one
        new_origin = origin - 0.5 * V(x1-x0, y1-y0, z1-z0)

        # render the text
        self.drawText( text, new_origin)
        return