source: trunk/gui/scripts/moleculeViewer.tcl @ 22

Last change on this file since 22 was 22, checked in by mmc, 19 years ago

Lots of changes to support Huckel-IV:

  • Support for embedded <tool> declarations
  • New <integer> entry
  • Support for numbers and structures as output elements
  • Atom numbers/symbols in MoleculeViewer?
File size: 16.9 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: MoleculeViewer - view a molecule in 3D
3#
4#  This widget brings up a 3D representation of a molecule, which you
5#  can rotate.  It extracts atoms and bonds from the Rappture XML
6#  representation for a <molecule>.
7# ======================================================================
8#  AUTHOR:  Michael McLennan, Purdue University
9#  Copyright (c) 2004-2005
10#  Purdue Research Foundation, West Lafayette, IN
11# ======================================================================
12package require Itk
13package require vtk
14package require vtkinteraction
15package require BLT
16
17option add *MoleculeViewer.width 4i widgetDefault
18option add *MoleculeViewer.height 4i widgetDefault
19option add *MoleculeViewer.backdrop black widgetDefault
20
21blt::bitmap define MoleculeViewer-reset {
22#define reset_width 12
23#define reset_height 12
24static unsigned char reset_bits[] = {
25   0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02,
26   0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00};
27}
28
29blt::bitmap define MoleculeViewer-zoomin {
30#define zoomin_width 12
31#define zoomin_height 12
32static unsigned char zoomin_bits[] = {
33   0x7c, 0x00, 0x82, 0x00, 0x11, 0x01, 0x11, 0x01, 0x7d, 0x01, 0x11, 0x01,
34   0x11, 0x01, 0x82, 0x03, 0xfc, 0x07, 0x80, 0x0f, 0x00, 0x0f, 0x00, 0x06};
35}
36
37blt::bitmap define MoleculeViewer-zoomout {
38#define zoomout_width 12
39#define zoomout_height 12
40static unsigned char zoomout_bits[] = {
41   0x7c, 0x00, 0x82, 0x00, 0x01, 0x01, 0x01, 0x01, 0x7d, 0x01, 0x01, 0x01,
42   0x01, 0x01, 0x82, 0x03, 0xfc, 0x07, 0x80, 0x0f, 0x00, 0x0f, 0x00, 0x06};
43}
44
45blt::bitmap define MoleculeViewer-atoms {
46#define atoms_width 12
47#define atoms_height 12
48static unsigned char atoms_bits[] = {
49   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x02, 0x4c, 0x02, 0xc8, 0x03,
50   0x48, 0x02, 0x48, 0x02, 0x5c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
51}
52
53itcl::class Rappture::MoleculeViewer {
54    inherit itk::Widget
55
56    itk_option define -backdrop backdrop Backdrop "black"
57    itk_option define -device device Device ""
58
59    constructor {tool args} { # defined below }
60    destructor { # defined below }
61
62    protected method _render {}
63    protected method _zoom {option}
64    protected method _move {option x y}
65    protected method _3dView {theta phi}
66    protected method _fixLabels {{option position}}
67    protected method _color2rgb {color}
68
69    private variable _tool ""    ;# tool containing this viewer
70    private variable _actors ""  ;# list of actors in renderer
71    private variable _label2atom ;# maps 2D text actor => underlying atom
72    private variable _view       ;# view params for 3D view
73    private variable _limits     ;# limits of x/y/z axes
74    private variable _click      ;# info used for _move operations
75}
76                                                                               
77itk::usual MoleculeViewer {
78}
79
80# ----------------------------------------------------------------------
81# CONSTRUCTOR
82# ----------------------------------------------------------------------
83itcl::body Rappture::MoleculeViewer::constructor {tool args} {
84    set _tool $tool
85
86    itk_option add hull.width hull.height
87    pack propagate $itk_component(hull) no
88
89    vtkRenderWindow $this-renWin
90    vtkRenderer $this-ren
91    $this-renWin AddRenderer $this-ren
92
93    vtkRenderWindowInteractor $this-int
94    $this-int SetRenderWindow $this-renWin
95
96    vtkSphereSource $this-sphere
97    $this-sphere SetRadius 1.0
98    $this-sphere SetThetaResolution 18
99    $this-sphere SetPhiResolution 18
100
101    vtkPolyDataMapper $this-map
102    $this-map SetInput [$this-sphere GetOutput]
103
104    vtkCoordinate $this-xyzconv
105    $this-xyzconv SetCoordinateSystemToWorld
106
107    set _view(theta) 0
108    set _view(phi) 0
109
110    itk_component add controls {
111        frame $itk_interior.cntls
112    } {
113        usual
114        rename -background -controlbackground controlBackground Background
115    }
116    pack $itk_component(controls) -side right -fill y
117
118    itk_component add reset {
119        button $itk_component(controls).reset \
120            -borderwidth 1 -padx 1 -pady 1 \
121            -bitmap MoleculeViewer-reset \
122            -command [itcl::code $this _zoom reset]
123    } {
124        usual
125        ignore -borderwidth
126        rename -highlightbackground -controlbackground controlBackground Background
127    }
128    pack $itk_component(reset) -padx 4 -pady 4
129    Rappture::Tooltip::for $itk_component(reset) "Reset the view to the default zoom level"
130
131    itk_component add zoomin {
132        button $itk_component(controls).zin \
133            -borderwidth 1 -padx 1 -pady 1 \
134            -bitmap MoleculeViewer-zoomin \
135            -command [itcl::code $this _zoom in]
136    } {
137        usual
138        ignore -borderwidth
139        rename -highlightbackground -controlbackground controlBackground Background
140    }
141    pack $itk_component(zoomin) -padx 4 -pady 4
142    Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
143
144    itk_component add zoomout {
145        button $itk_component(controls).zout \
146            -borderwidth 1 -padx 1 -pady 1 \
147            -bitmap MoleculeViewer-zoomout \
148            -command [itcl::code $this _zoom out]
149    } {
150        usual
151        ignore -borderwidth
152        rename -highlightbackground -controlbackground controlBackground Background
153    }
154    pack $itk_component(zoomout) -padx 4 -pady 4
155    Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
156
157    itk_component add labels {
158        label $itk_component(controls).labels \
159            -borderwidth 1 -padx 1 -pady 1 \
160            -bitmap MoleculeViewer-atoms
161    } {
162        usual
163        ignore -borderwidth
164        rename -highlightbackground -controlbackground controlBackground Background
165    }
166    pack $itk_component(labels) -padx 4 -pady 8 -ipadx 1 -ipady 1
167    Rappture::Tooltip::for $itk_component(labels) "Show/hide the labels on atoms"
168    bind $itk_component(labels) <ButtonPress> \
169        [itcl::code $this _fixLabels toggle]
170
171    #
172    # RENDERING AREA
173    #
174    itk_component add area {
175        frame $itk_interior.area
176    }
177    pack $itk_component(area) -expand yes -fill both
178    bind $itk_component(area) <Configure> \
179        [itcl::code $this _fixLabels]
180
181    itk_component add renderer {
182        vtkTkRenderWidget $itk_component(area).ren -rw $this-renWin
183    } {
184    }
185    pack $itk_component(renderer) -expand yes -fill both
186
187    eval itk_initialize $args
188
189    # prevent interactions -- use our own
190    blt::busy hold $itk_component(area) -cursor left_ptr
191    bind $itk_component(area)_Busy <ButtonPress> \
192        [itcl::code $this _move click %x %y]
193    bind $itk_component(area)_Busy <B1-Motion> \
194        [itcl::code $this _move drag %x %y]
195    bind $itk_component(area)_Busy <ButtonRelease> \
196        [itcl::code $this _move release %x %y]
197
198    _fixLabels on
199}
200
201# ----------------------------------------------------------------------
202# DESTRUCTOR
203# ----------------------------------------------------------------------
204itcl::body Rappture::MoleculeViewer::destructor {} {
205    rename $this-renWin ""
206    rename $this-ren ""
207    rename $this-int ""
208    rename $this-sphere ""
209    rename $this-map ""
210    rename $this-xyzconv ""
211}
212
213# ----------------------------------------------------------------------
214# USAGE: _render
215#
216# Used internally to rebuild the scene whenever options within this
217# widget change.  Destroys all actors and rebuilds them from scratch.
218# ----------------------------------------------------------------------
219itcl::body Rappture::MoleculeViewer::_render {} {
220    foreach a $_actors {
221        $this-ren RemoveActor $a
222        rename $a ""
223    }
224    set _actors ""
225    catch {unset _label2atom}
226
227    foreach lim {xmin xmax ymin ymax zmin zmax} {
228        set _limits($lim) ""
229    }
230
231    if {$itk_option(-device) != ""} {
232        set dev $itk_option(-device)
233        set lib [Rappture::library standard]
234
235        set counter 0
236        foreach atom [$dev children -type atom components.molecule] {
237            set symbol [$dev get components.molecule.$atom.symbol]
238            set xyz [$dev get components.molecule.$atom.xyz]
239            regsub {,} $xyz {} xyz
240
241            # update overall limits for molecules along all axes
242            foreach axis {x y z} val $xyz {
243                if {"" == $_limits(${axis}min)} {
244                    set _limits(${axis}min) $val
245                    set _limits(${axis}max) $val
246                } else {
247                    if {$val < $_limits(${axis}min)} {
248                        set _limits(${axis}min) $val
249                    }
250                    if {$val > $_limits(${axis}max)} {
251                        set _limits(${axis}max) $val
252                    }
253                }
254            }
255
256            # create an actor for each atom
257            set aname $this-actor[incr counter]
258            vtkActor $aname
259            $aname SetMapper $this-map
260            eval $aname SetPosition $xyz
261            $this-ren AddActor $aname
262
263            set sfac 0.7
264            set scale [$lib get elements.($symbol).scale]
265            if {$scale != ""} {
266                $aname SetScale [expr {$sfac*$scale}]
267            }
268            set color [$lib get elements.($symbol).color]
269            if {$color != ""} {
270                eval [$aname GetProperty] SetColor [_color2rgb $color]
271            }
272
273            lappend _actors $aname
274
275            # create a label for each atom
276            set lname $this-label$counter
277            vtkTextActor $lname
278            $lname SetInput "$counter $symbol"
279            $lname ScaledTextOff
280
281            set tprop [$lname GetTextProperty]
282            $tprop SetJustificationToCentered
283            $tprop SetVerticalJustificationToCentered
284            $tprop ShadowOn
285            $tprop SetColor 1 1 1
286
287            set _label2atom($lname) $aname
288            lappend _actors $lname
289        }
290        if {[$itk_component(labels) cget -relief] == "sunken"} {
291            _fixLabels on
292        }
293        after cancel [list catch [itcl::code $this _zoom reset]]
294        after 200 [list catch [itcl::code $this _zoom reset]]
295    }
296    $this-ren ResetCamera
297    $this-renWin Render
298}
299
300# ----------------------------------------------------------------------
301# USAGE: _zoom in
302# USAGE: _zoom out
303# USAGE: _zoom reset
304#
305# Called automatically when the user clicks on one of the zoom
306# controls for this widget.  Changes the zoom for the current view.
307# ----------------------------------------------------------------------
308itcl::body Rappture::MoleculeViewer::_zoom {option} {
309    switch -- $option {
310        in {
311            [$this-ren GetActiveCamera] Zoom 1.25
312            _fixLabels
313            $this-renWin Render
314        }
315        out {
316            [$this-ren GetActiveCamera] Zoom 0.8
317            _fixLabels
318            $this-renWin Render
319        }
320        reset {
321            [$this-ren GetActiveCamera] SetViewAngle 30
322            $this-ren ResetCamera
323            [$this-ren GetActiveCamera] Zoom 1.25
324            _3dView 45 45
325            $this-renWin Render
326
327            after cancel [list catch [itcl::code $this _fixLabels]]
328            after 2000 [list catch [itcl::code $this _fixLabels]]
329        }
330    }
331}
332
333# ----------------------------------------------------------------------
334# USAGE: _move click <x> <y>
335# USAGE: _move drag <x> <y>
336# USAGE: _move release <x> <y>
337#
338# Called automatically when the user clicks/drags/releases in the
339# plot area.  Moves the plot according to the user's actions.
340# ----------------------------------------------------------------------
341itcl::body Rappture::MoleculeViewer::_move {option x y} {
342    switch -- $option {
343        click {
344            blt::busy configure $itk_component(area) -cursor fleur
345            set _click(x) $x
346            set _click(y) $y
347            set _click(theta) $_view(theta)
348            set _click(phi) $_view(phi)
349        }
350        drag {
351            if {[array size _click] == 0} {
352                _move click $x $y
353            } else {
354                set w [winfo width $itk_component(renderer)]
355                set h [winfo height $itk_component(renderer)]
356                set dx [expr {double($x-$_click(x))/$w}]
357                set dy [expr {double($y-$_click(y))/$h}]
358
359                #
360                # Rotate the camera in 3D
361                #
362                set theta [expr {$_view(theta) - $dy*180}]
363                if {$theta < 2} { set theta 2 }
364                if {$theta > 178} { set theta 178 }
365                set phi [expr {$_view(phi) - $dx*360}]
366
367                _3dView $theta $phi
368                _fixLabels
369                $this-renWin Render
370
371                set _click(x) $x
372                set _click(y) $y
373            }
374        }
375        release {
376            _move drag $x $y
377            blt::busy configure $itk_component(area) -cursor left_ptr
378            catch {unset _click}
379        }
380        default {
381            error "bad option \"$option\": should be click, drag, release"
382        }
383    }
384}
385
386# ----------------------------------------------------------------------
387# USAGE: _3dView <theta> <phi>
388#
389# Used internally to change the position of the camera for 3D data
390# sets.  Sets the camera according to the angles <theta> (angle from
391# the z-axis) and <phi> (angle from the x-axis in the x-y plane).
392# Both angles are in degrees.
393# ----------------------------------------------------------------------
394itcl::body Rappture::MoleculeViewer::_3dView {theta phi} {
395    set deg2rad 0.0174532927778
396    set xn [expr {sin($theta*$deg2rad)*cos($phi*$deg2rad)}]
397    set yn [expr {sin($theta*$deg2rad)*sin($phi*$deg2rad)}]
398    set zn [expr {cos($theta*$deg2rad)}]
399
400    set xm [expr {0.5*($_limits(xmax)+$_limits(xmin))}]
401    set ym [expr {0.5*($_limits(ymax)+$_limits(ymin))}]
402    set zm [expr {0.5*($_limits(zmax)+$_limits(zmin))}]
403
404    set cam [$this-ren GetActiveCamera]
405    set zoom [$cam GetViewAngle]
406    $cam SetViewAngle 30
407
408    $cam SetFocalPoint $xm $ym $zm
409    $cam SetPosition [expr {$xm-$xn}] [expr {$ym-$yn}] [expr {$zm+$zn}]
410    $cam ComputeViewPlaneNormal
411    $cam SetViewUp 0 0 1  ;# z-dir is up
412    $cam OrthogonalizeViewUp
413    $this-ren ResetCamera
414    $cam SetViewAngle $zoom
415
416    # fix up the labels so they sit over the new atom positions
417    _fixLabels
418
419    set _view(theta) $theta
420    set _view(phi) $phi
421}
422
423# ----------------------------------------------------------------------
424# USAGE: _fixLabels on
425# USAGE: _fixLabels off
426# USAGE: _fixLabels toggle
427# USAGE: _fixLabels position
428#
429# Used internally to turn labels associated with atoms on/off, and to
430# update the positions of the labels so they sit on top of each atom.
431# ----------------------------------------------------------------------
432itcl::body Rappture::MoleculeViewer::_fixLabels {{option position}} {
433    switch -- $option {
434        on {
435            set state 1
436        }
437        off {
438            set state 0
439        }
440        toggle {
441            if {[$itk_component(labels) cget -relief] == "sunken"} {
442                set state 0
443            } else {
444                set state 1
445            }
446        }
447        position {
448            foreach lname [array names _label2atom] {
449                set aname $_label2atom($lname)
450                set xyz [$aname GetPosition]
451                eval $this-xyzconv SetValue $xyz
452                set xy [$this-xyzconv GetComputedViewportValue $this-ren]
453                eval $lname SetDisplayPosition $xy
454            }
455            return
456        }
457        default {
458            error "bad option \"$option\": should be on, off, toggle, position"
459        }
460    }
461
462    if {$state} {
463        $itk_component(labels) configure -relief sunken
464        foreach lname [array names _label2atom] {
465            catch {$this-ren AddActor2D $lname}
466        }
467        _fixLabels position
468    } else {
469        $itk_component(labels) configure -relief raised
470        foreach lname [array names _label2atom] {
471            catch {$this-ren RemoveActor $lname}
472        }
473    }
474    $this-renWin Render
475}
476
477# ----------------------------------------------------------------------
478# USAGE: _color2rgb color
479#
480# Used internally to convert a Tk color name into the r,g,b values
481# used in Vtk (scaled 0-1).
482# ----------------------------------------------------------------------
483itcl::body Rappture::MoleculeViewer::_color2rgb {color} {
484    foreach {r g b} [winfo rgb $itk_component(hull) $color] {}
485    set r [expr {$r/65535.0}]
486    set g [expr {$g/65535.0}]
487    set b [expr {$b/65535.0}]
488    return [list $r $g $b]
489}
490
491# ----------------------------------------------------------------------
492# OPTION: -backdrop
493# ----------------------------------------------------------------------
494itcl::configbody Rappture::MoleculeViewer::backdrop {
495    eval $this-ren SetBackground [_color2rgb $itk_option(-backdrop)]
496    $this-renWin Render
497}
498
499# ----------------------------------------------------------------------
500# OPTION: -device
501# ----------------------------------------------------------------------
502itcl::configbody Rappture::MoleculeViewer::device {
503    if {$itk_option(-device) != ""
504          && ![Rappture::library isvalid $itk_option(-device)]} {
505        error "bad value \"$itk_option(-device)\": should be Rappture::library object"
506    }
507    after idle [itcl::code $this _render]
508}
Note: See TracBrowser for help on using the repository browser.