source: trunk/gui/scripts/molvisviewer.tcl @ 3770

Last change on this file since 3770 was 3637, checked in by gah, 11 years ago

add pdb to vtk converter to drawing

File size: 73.5 KB
Line 
1# -*- mode: tcl; indent-tabs-mode: nil -*-
2
3# ----------------------------------------------------------------------
4#  COMPONENT: molvisviewer - view a molecule in 3D
5#
6#  This widget brings up a 3D representation of a molecule
7#  It connects to the Molvis server running on a rendering farm,
8#  transmits data, and displays the results.
9# ======================================================================
10#  AUTHOR:  Michael McLennan, Purdue University
11#  Copyright (c) 2004-2012  HUBzero Foundation, LLC
12#
13#  See the file "license.terms" for information on usage and
14#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15# ======================================================================
16package require Itk
17package require BLT
18package require Img
19
20option add *MolvisViewer.width 4i widgetDefault
21option add *MolvisViewer.height 4i widgetDefault
22option add *MolvisViewer.foreground black widgetDefault
23option add *MolvisViewer.font -*-helvetica-medium-r-normal-*-12-* widgetDefault
24
25# must use this name -- plugs into Rappture::resources::load
26proc MolvisViewer_init_resources {} {
27    Rappture::resources::register \
28        molvis_server Rappture::MolvisViewer::SetServerList
29}
30
31set debug 0
32proc debug { args } {
33    global debug
34    if { $debug } {
35        puts stderr "[info level -1]: $args"
36    }
37}
38
39itcl::class Rappture::MolvisViewer {
40    inherit Rappture::VisViewer
41
42    itk_option define -device device Device ""
43
44    private variable _icon 0
45    private variable _getimage 0
46    private variable _mevent;           # info used for mouse event operations
47    private variable _rocker;           # info used for rock operations
48    private variable _dlist "";         # list of dataobj objects
49    private variable _dataobjs;         # data objects on server
50    private variable _dobj2transparency;# maps dataobj => transparency
51    private variable _dobj2raise;       # maps dataobj => raise flag 0/1
52
53    private variable _active;           # array of active models.
54    private variable _obj2models;       # array containing list of models
55                                        # for each data object.
56    private variable _view
57    private variable _click
58
59    private variable _model
60    private variable _mlist
61    private variable _mrep "ballnstick"
62
63    private variable _imagecache
64    private variable _state
65    private variable _labels  "default"
66    private variable _cacheid ""
67    private variable _cacheimage ""
68    private variable _first     ""
69
70    private common _settings  ;         # Array of settings for all known
71                                        # widgets
72    private variable _initialized
73
74    private common _downloadPopup;      # Download options from popup
75    private variable _pdbdata;          # PDB data from run file sent to pymol
76    private common _hardcopy
77    private variable _nextToken 0
78    private variable _resizePending 0;
79    private variable _updatePending 0;
80    private variable _rotatePending 0;
81    private variable _width
82    private variable _height
83    private variable _reset 1;          # Restore camera settings
84    private variable _cell 0;           # Restore camera settings
85
86    constructor { servers args } {
87        Rappture::VisViewer::constructor $servers
88    } {
89        # defined below
90    }
91    destructor {
92        # defined below
93    }
94    public proc SetServerList { namelist } {
95        Rappture::VisViewer::SetServerList "pymol" $namelist
96    }
97    private method BuildSettingsTab {}
98    private method DoResize {}
99    private method DoRotate {}
100    private method DoUpdate {}
101    private method EventuallyResize { w h }
102    private method EventuallyRotate { a b c }
103    private method EventuallyChangeSettings { args }
104    private method GetImage { widget }
105    private method ReceiveImage { size cacheid frame rock }
106    private method WaitIcon { option widget }
107    private method AddImageControls { frame widget }
108    private method SetWaitVariable { value } {
109        set _getimage $value
110    }
111    private method WaitForResponse {} {
112        tkwait variable [itcl::scope _getimage]
113        return $_getimage
114    }
115    protected method Map {}
116    protected method Pan {option x y}
117    protected method Rebuild { }
118    protected method Rotate {option x y}
119    protected method ServerCmd { string }
120    protected method Unmap {}
121    protected method Vmouse  {option b m x y}
122    protected method Vmouse2 {option b m x y}
123    protected method Zoom {option {factor 10}}
124
125    public method Connect {}
126    public method Disconnect {}
127    public method ResetView {}
128    public method add {dataobj {options ""}}
129    public method delete {args}
130    public method download {option args}
131    public method get {}
132    public method isconnected {}
133    public method labels {option {model "all"}}
134    public method parameters {title args} {
135        # do nothing
136    }
137
138    private method UpdateState { args }
139
140    public method snap { w h }
141    private method Opacity {option}
142    private method SphereScale {option {models "all"} }
143    private method StickRadius {option {models "all"} }
144    private method OrthoProjection {option}
145    private method Representation { {option ""} }
146    private method CartoonTrace {option {model "all"}}
147    private method ComputeParallelepipedVertices { dataobj }
148    private method Cell {option}
149    private method Rock {option}
150}
151
152itk::usual MolvisViewer {
153    keep -background -foreground -cursor -font
154}
155
156# ----------------------------------------------------------------------
157# CONSTRUCTOR
158# ----------------------------------------------------------------------
159itcl::body Rappture::MolvisViewer::constructor {servers args} {
160    set _serverType "pymol"
161
162    # Register events to the dispatcher.  Base class expects !rebuild
163    # event to be registered.
164
165    # Rebuild
166    $_dispatcher register !rebuild
167    $_dispatcher dispatch $this !rebuild "[itcl::code $this Rebuild]; list"
168
169    # Resize event
170    $_dispatcher register !resize
171    $_dispatcher dispatch $this !resize "[itcl::code $this DoResize]; list"
172
173    # Update state event
174    $_dispatcher register !update
175    $_dispatcher dispatch $this !update "[itcl::code $this DoUpdate]; list"
176
177    # Rotate event
178    $_dispatcher register !rotate
179    $_dispatcher dispatch $this !rotate "[itcl::code $this DoRotate]; list"
180
181    # Rocker
182    $_dispatcher register !rocker
183    $_dispatcher dispatch $this !rocker "[itcl::code $this Rock step]; list"
184    # Mouse Event
185    $_dispatcher register !mevent
186    $_dispatcher dispatch $this !mevent "[itcl::code $this _mevent]; list"
187    $_dispatcher register !pngtimeout
188    $_dispatcher register !waiticon
189
190    array set _downloadPopup {
191        format draft
192    }
193
194    # Populate the slave interpreter with commands to handle responses from
195    # the visualization server.
196    $_parser alias image [itcl::code $this ReceiveImage]
197
198    set _rocker(dir) 1
199    set _rocker(client) 0
200    set _rocker(server) 0
201    set _rocker(on) 0
202    set _state(server) 1
203    set _state(client) 1
204    set _reset 1
205
206    array set _view {
207        theta   45
208        phi     45
209        psi     0
210        vx      0
211        vy      0
212        vz      0
213        zoom    0
214        mx      0
215        my      0
216        mz      0
217        x       0
218        y       0
219        z       0
220        width   0
221        height  0
222    }
223
224    # Setup default settings for widget.
225    array set _settings [subst {
226        $this-spherescale 0.25
227        $this-stickradius 0.14
228        $this-cartoontrace no
229        $this-model     ballnstick
230        $this-modelimg  [Rappture::icon ballnstick]
231        $this-opacity   1.0
232        $this-ortho     no
233        $this-rock      no
234        $this-showlabels no
235        $this-showcell  yes
236        $this-showlabels-initialized no
237    }]
238   
239    itk_component add 3dview {
240        label $itk_component(plotarea).view -image $_image(plot) \
241            -highlightthickness 0 -borderwidth 0
242    } {
243        usual
244        ignore -highlightthickness -borderwidth  -background
245    }
246    bind $itk_component(3dview) <Control-F1> [itcl::code $this ToggleConsole]
247
248    set f [$itk_component(main) component controls]
249    itk_component add reset {
250        button $f.reset -borderwidth 1 -padx 1 -pady 1 \
251            -highlightthickness 0 \
252            -image [Rappture::icon reset-view] \
253            -command [itcl::code $this ResetView]
254    } {
255        usual
256        ignore -highlightthickness
257    }
258    pack $itk_component(reset) -padx 1 -pady 2
259    Rappture::Tooltip::for $itk_component(reset) \
260        "Reset the view to the default zoom level"
261
262    itk_component add zoomin {
263        button $f.zin -borderwidth 1 -padx 1 -pady 1 \
264            -highlightthickness 0 \
265            -image [Rappture::icon zoom-in] \
266            -command [itcl::code $this Zoom in]
267    } {
268        usual
269        ignore -highlightthickness
270    }
271    pack $itk_component(zoomin) -padx 2 -pady 2
272    Rappture::Tooltip::for $itk_component(zoomin) "Zoom in"
273
274    itk_component add zoomout {
275        button $f.zout -borderwidth 1 -padx 1 -pady 1 \
276            -highlightthickness 0 \
277            -image [Rappture::icon zoom-out] \
278            -command [itcl::code $this Zoom out]
279    } {
280        usual
281        ignore -highlightthickness
282    }
283    pack $itk_component(zoomout) -padx 2 -pady 2
284    Rappture::Tooltip::for $itk_component(zoomout) "Zoom out"
285
286    itk_component add labels {
287        Rappture::PushButton $f.labels \
288            -onimage [Rappture::icon molvis-labels-view] \
289            -offimage [Rappture::icon molvis-labels-view] \
290            -command [itcl::code $this labels update] \
291            -variable [itcl::scope _settings($this-showlabels)]
292    }
293    $itk_component(labels) deselect
294    Rappture::Tooltip::for $itk_component(labels) \
295        "Show/hide the labels on atoms"
296    pack $itk_component(labels) -padx 2 -pady {6 2}
297
298    itk_component add rock {
299        Rappture::PushButton $f.rock \
300            -onimage [Rappture::icon molvis-rock-view] \
301            -offimage [Rappture::icon molvis-rock-view] \
302            -command [itcl::code $this Rock toggle] \
303            -variable [itcl::scope _settings($this-rock)]
304    }
305    pack $itk_component(rock) -padx 2 -pady 2
306    Rappture::Tooltip::for $itk_component(rock) "Rock model back and forth"
307
308    itk_component add ortho {
309        label $f.ortho -borderwidth 1 -padx 1 -pady 1 \
310            -relief "raised" -image [Rappture::icon molvis-3dpers]
311    }
312    pack $itk_component(ortho) -padx 2 -pady 2 -ipadx 1 -ipady 1
313    Rappture::Tooltip::for $itk_component(ortho) \
314        "Use orthoscopic projection"
315
316    bind $itk_component(ortho) <ButtonPress> \
317        [itcl::code $this OrthoProjection toggle]
318
319    BuildSettingsTab
320
321    # HACK ALERT. Initially force a requested width of the 3dview label.
322
323    # It's a chicken-and-the-egg problem.  The size of the 3dview label is set
324    # from the size of the image retrieved from the server.  But the size of
325    # the image is specified by the viewport which is the size of the label.
326    # The fly-in-the-ointment is that it takes a non-trival amount of time to
327    # get the first image back from the server.  In the meantime the idletasks
328    # have already kicked in.  We end up with a 1x1 viewport and image.
329
330    # So the idea is to force a ridiculously big requested width on the label
331    # (that's why we're using the blt::table to manage the geometry).  It has
332    # to be big, because we don't know how big the user may want to stretch
333    # the window.  This at least forces the sidebarframe to give the 3dview
334    # the maximum size available, which is perfect for an initially closed
335    # sidebar.
336
337    blt::table $itk_component(plotarea) \
338        0,0 $itk_component(3dview) -fill both -reqwidth 10000
339    #
340    # RENDERING AREA
341    #
342
343    set _image(id) ""
344
345    # set up bindings for rotation
346    if 0 {
347        bind $itk_component(3dview) <ButtonPress-1> \
348            [itcl::code $this Rotate click %x %y]
349        bind $itk_component(3dview) <B1-Motion> \
350            [itcl::code $this Rotate drag %x %y]
351        bind $itk_component(3dview) <ButtonRelease-1> \
352            [itcl::code $this Rotate release %x %y]
353    } else {
354        bind $itk_component(3dview) <ButtonPress-1> \
355            [itcl::code $this Vmouse click %b %s %x %y]
356        bind $itk_component(3dview) <B1-Motion> \
357            [itcl::code $this Vmouse drag 1 %s %x %y]
358        bind $itk_component(3dview) <ButtonRelease-1> \
359            [itcl::code $this Vmouse release %b %s %x %y]
360    }
361
362    bind $itk_component(3dview) <ButtonPress-2> \
363        [itcl::code $this Pan click %x %y]
364    bind $itk_component(3dview) <B2-Motion> \
365        [itcl::code $this Pan drag %x %y]
366    bind $itk_component(3dview) <ButtonRelease-2> \
367        [itcl::code $this Pan release %x %y]
368
369    bind $itk_component(3dview) <KeyPress-Left> \
370        [itcl::code $this Pan set -10 0]
371    bind $itk_component(3dview) <KeyPress-Right> \
372        [itcl::code $this Pan set 10 0]
373    bind $itk_component(3dview) <KeyPress-Up> \
374        [itcl::code $this Pan set 0 -10]
375    bind $itk_component(3dview) <KeyPress-Down> \
376        [itcl::code $this Pan set 0 10]
377    bind $itk_component(3dview) <Shift-KeyPress-Left> \
378        [itcl::code $this Pan set -50 0]
379    bind $itk_component(3dview) <Shift-KeyPress-Right> \
380        [itcl::code $this Pan set 50 0]
381    bind $itk_component(3dview) <Shift-KeyPress-Up> \
382        [itcl::code $this Pan set 0 -50]
383    bind $itk_component(3dview) <Shift-KeyPress-Down> \
384        [itcl::code $this Pan set 0 50]
385    bind $itk_component(3dview) <KeyPress-Prior> \
386        [itcl::code $this Zoom out 2]
387    bind $itk_component(3dview) <KeyPress-Next> \
388        [itcl::code $this Zoom in 2]
389
390    bind $itk_component(3dview) <Enter> "focus $itk_component(3dview)"
391
392
393    if {[string equal "x11" [tk windowingsystem]]} {
394        bind $itk_component(3dview) <4> [itcl::code $this Zoom out 2]
395        bind $itk_component(3dview) <5> [itcl::code $this Zoom in 2]
396    }
397
398    # set up bindings to bridge mouse events to server
399    #bind $itk_component(3dview) <ButtonPress> \
400    #   [itcl::code $this Vmouse2 click %b %s %x %y]
401    #bind $itk_component(3dview) <ButtonRelease> \
402    #    [itcl::code $this Vmouse2 release %b %s %x %y]
403    #bind $itk_component(3dview) <B1-Motion> \
404    #    [itcl::code $this Vmouse2 drag 1 %s %x %y]
405    #bind $itk_component(3dview) <B2-Motion> \
406    #    [itcl::code $this Vmouse2 drag 2 %s %x %y]
407    #bind $itk_component(3dview) <B3-Motion> \
408    #    [itcl::code $this Vmouse2 drag 3 %s %x %y]
409    #bind $itk_component(3dview) <Motion> \
410    #    [itcl::code $this Vmouse2 move 0 %s %x %y]
411
412    bind $itk_component(3dview) <Configure> \
413        [itcl::code $this EventuallyResize %w %h]
414    bind $itk_component(3dview) <Unmap> \
415        [itcl::code $this Unmap]
416    bind $itk_component(3dview) <Map> \
417        [itcl::code $this Map]
418
419    eval itk_initialize $args
420    Connect
421}
422
423# ----------------------------------------------------------------------
424# DESTRUCTOR
425# ----------------------------------------------------------------------
426itcl::body Rappture::MolvisViewer::destructor {} {
427    VisViewer::Disconnect
428
429    image delete $_image(plot)
430    array unset _settings $this-*
431}
432
433
434# ----------------------------------------------------------------------
435# USAGE: add <dataobj> ?<settings>?
436#
437# Clients use this to add a data object to the plot.  The optional
438# <settings> are used to configure the plot.  Allowed settings are
439# -color, -brightness, -width, -linestyle, and -raise. Only
440# -brightness and -raise do anything.
441# ----------------------------------------------------------------------
442itcl::body Rappture::MolvisViewer::add { dataobj {options ""}} {
443    array set params {
444        -color          auto
445        -brightness     0
446        -width          1
447        -raise          0
448        -linestyle      solid
449        -description    ""
450        -param          ""
451    }
452
453    foreach {opt val} $options {
454        if {![info exists params($opt)]} {
455            error "bad settings \"$opt\": should be [join [lsort [array names params]] {, }]"
456        }
457        set params($opt) $val
458    }
459
460    set pos [lsearch -exact $dataobj $_dlist]
461
462    if {$pos < 0} {
463        if {![Rappture::library isvalid $dataobj]} {
464            error "bad value \"$dataobj\": should be Rappture::library object"
465        }
466
467        if { !$_settings($this-showlabels-initialized) } {
468            set showlabels [$dataobj get components.molecule.about.emblems]
469            if { $showlabels != "" && [string is boolean $showlabels] } {
470                set _settings($this-showlabels) $showlabels
471            }
472        }
473
474        lappend _dlist $dataobj
475        if { $params(-brightness) >= 0.5 } {
476            set _dobj2transparency($dataobj) "ghost"
477        } else {
478            set _dobj2transparency($dataobj) "normal"
479        }
480        set _dobj2raise($dataobj) $params(-raise)
481        debug "setting parameters for $dataobj\n"
482
483        if { [isconnected] } {
484            $_dispatcher event -idle !rebuild
485        }
486    }
487}
488
489# ----------------------------------------------------------------------
490# USAGE: delete ?<dataobj> <dataobj> ...?
491#
492# Clients use this to delete a dataobj from the plot. If no dataobjs
493# are specified, then all dataobjs are deleted.
494# ----------------------------------------------------------------------
495itcl::body Rappture::MolvisViewer::delete { args } {
496    if {[llength $args] == 0} {
497        set args $_dlist
498    }
499
500    # delete all specified dataobjs
501    set changed 0
502    foreach dataobj $args {
503        set pos [lsearch -exact $_dlist $dataobj]
504        if {$pos >= 0} {
505            set _dlist [lreplace $_dlist $pos $pos]
506            if { [info exists _obj2models($dataobj)] } {
507                foreach model $_obj2models($dataobj) {
508                    array unset _active $model
509                }
510            }
511            array unset _obj2models $dataobj
512            array unset _dobj2transparency $dataobj
513            array unset _dobj2color $dataobj
514            array unset _dobj2width $dataobj
515            array unset _dobj2dashes $dataobj
516            array unset _dobj2raise $dataobj
517            set changed 1
518        }
519    }
520
521    # if anything changed, then rebuild the plot
522    if {$changed} {
523        if { [isconnected] } {
524            $_dispatcher event -idle !rebuild
525        }
526    }
527}
528
529# ----------------------------------------------------------------------
530# USAGE: get
531#
532# Clients use this to query the list of objects being plotted, in
533# order from bottom to top of this result.
534# ----------------------------------------------------------------------
535itcl::body Rappture::MolvisViewer::get {} {
536    # put the dataobj list in order according to -raise options
537    set dlist $_dlist
538    foreach obj $dlist {
539        if {[info exists _dobj2raise($obj)] && $_dobj2raise($obj)} {
540            set i [lsearch -exact $dlist $obj]
541            if {$i >= 0} {
542                set dlist [lreplace $dlist $i $i]
543                lappend dlist $obj
544            }
545        }
546    }
547    return $dlist
548}
549
550# ----------------------------------------------------------------------
551# USAGE: download coming
552# USAGE: download controls <downloadCommand>
553# USAGE: download now
554#
555# Clients use this method to create a downloadable representation
556# of the plot.  Returns a list of the form {ext string}, where
557# "ext" is the file extension (indicating the type of data) and
558# "string" is the data itself.
559# ----------------------------------------------------------------------
560itcl::body Rappture::MolvisViewer::download {option args} {
561    switch $option {
562        coming {}
563        controls {
564            set popup .molvisviewerdownload
565            if {![winfo exists $popup]} {
566                # if we haven't created the popup yet, do it now
567                Rappture::Balloon $popup \
568                    -title "[Rappture::filexfer::label downloadWord] as..."
569                set inner [$popup component inner]
570                label $inner.summary -text "" -anchor w
571                radiobutton $inner.pdb \
572                    -text "PDB Protein Data Bank Format File" \
573                    -variable [itcl::scope _downloadPopup(format)] \
574                    -font "Arial 10 " \
575                    -value pdb 
576                Rappture::Tooltip::for $inner.pdb \
577                    "Save as PDB Protein Data Bank format file."
578                radiobutton $inner.image -text "Image (PNG/JPEG/GIF)" \
579                    -variable [itcl::scope _downloadPopup(format)] \
580                    -font "Arial 10 " \
581                    -value image
582                Rappture::Tooltip::for $inner.image \
583                    "Save as image."
584                set f [frame $inner.frame]
585                button $f.ok -text "Save" \
586                    -highlightthickness 0 -pady 3 -padx 3 \
587                    -command [lindex $args 0] \
588                    -compound left \
589                    -image [Rappture::icon download]
590                button $f.cancel -text "Cancel" \
591                    -highlightthickness 0 -pady 3 -padx 3 \
592                    -command [list $popup deactivate] \
593                    -compound left \
594                    -image [Rappture::icon cancel]
595                blt::table $f \
596                    0,0 $f.ok \
597                    0,1 $f.cancel
598                blt::table $inner \
599                    0,0 $inner.summary -anchor w \
600                    1,0 $inner.pdb -anchor w \
601                    2,0 $inner.image -anchor w \
602                    3,0 $f -fill x -pady 4
603                $inner.pdb select
604            } else {
605                set inner [$popup component inner]
606            }
607            set num [llength [get]]
608            set num [expr {($num == 1) ? "1 result" : "$num results"}]
609            set word [Rappture::filexfer::label downloadWord]
610            $inner.summary configure -text "$word $num in the following format:"
611            update idletasks ;          # Fix initial sizes
612            return $popup
613        }
614        now {
615            set popup .molvisviewerdownload
616            if {[winfo exists $popup]} {
617                $popup deactivate
618            }
619            switch -- $_downloadPopup(format) {
620                "pdb" {
621                    return [list .pdb $_pdbdata]
622                }
623                "image" {
624                    set popup .molvisviewerimage
625                    if { ![winfo exists $popup] } {
626                        # Create the balloon popup and and the print image
627                        # dialog widget to it.
628                        Rappture::Balloon $popup -title "Save as image..." \
629                            -deactivatecommand \
630                            [itcl::code $this SetWaitVariable 0]
631                        set inner [$popup component inner]
632                        # Add image controls to the ballon popup
633                        AddImageControls $inner [lindex $args 0]
634                    } else {
635                        set inner [$popup component inner]
636                    }                   
637                    update
638                    # Activate the popup and call for the output.
639                    foreach { widget toolName plotName } $args break
640                    SetWaitVariable 0
641                    $popup activate $widget left
642                    set bool [WaitForResponse]
643                    $popup deactivate
644                    if { $bool } {
645                        return [GetImage $widget]
646                    }
647                    return ""
648                }
649            }
650        }
651        default {
652            error "bad option \"$option\": should be coming, controls, now"
653        }
654    }
655}
656
657#
658# isconnected --
659#
660#       Indicates if we are currently connected to the visualization server.
661#
662itcl::body Rappture::MolvisViewer::isconnected {} {
663    return [VisViewer::IsConnected]
664}
665
666
667#
668# Connect --
669#
670#       Establishes a connection to a new visualization server.
671#
672itcl::body Rappture::MolvisViewer::Connect {} {
673    global readyForNextFrame
674    set readyForNextFrame 1
675    if { [isconnected] } {
676        return 1
677    }
678    set hosts [GetServerList "pymol"]
679    if { "" == $hosts } {
680        return 0
681    }
682    set _reset 1
683    set result [VisViewer::Connect $hosts]
684    if { $result } {
685        if { $_reportClientInfo }  {
686            # Tell the server the viewer, hub, user and session.
687            # Do this immediately on connect before buffing any commands
688            global env
689
690            set info {}
691            set user "???"
692            if { [info exists env(USER)] } {
693                set user $env(USER)
694            }
695            set session "???"
696            if { [info exists env(SESSION)] } {
697                set session $env(SESSION)
698            }
699            lappend info "hub" [exec hostname]
700            lappend info "client" "molvisviewer"
701            lappend info "user" $user
702            lappend info "session" $session
703            ServerCmd "clientinfo [list $info]"
704        }
705
706        $_dispatcher event -idle !rebuild
707    }
708    return $result
709}
710
711#
712# Disconnect --
713#
714#       Clients use this method to disconnect from the current rendering
715#       server.
716#
717itcl::body Rappture::MolvisViewer::Disconnect {} {
718    VisViewer::Disconnect
719
720    # disconnected -- no more data sitting on server
721    catch { after cancel $_rocker(afterid) }
722    catch { after cancel $_mevent(afterid) }
723    array unset _dataobjs
724    array unset _model
725    array unset _mlist
726    array unset _imagecache
727
728    set _state(server) 1
729    set _state(client) 1
730    global readyForNextFrame
731    set readyForNextFrame 1
732    set _reset 1
733}
734
735itcl::body Rappture::MolvisViewer::ServerCmd { cmd } {
736    debug "in ServerCmd ($cmd)\n"
737
738    if { $_state(server) != $_state(client) } {
739        SendCmd "frame -defer $_state(client)"
740        set _state(server) $_state(client)
741    }
742    if { $_rocker(server) != $_rocker(client) } {
743        SendCmd "rock -defer $_rocker(client)"
744        set _rocker(server) $_rocker(client)
745    }
746    SendCmd "$cmd"
747}
748
749#
750# ReceiveImage -bytes <size>
751#
752#     Invoked automatically whenever the "image" command comes in from
753#     the rendering server.  Indicates that binary image data with the
754#     specified <size> will follow.
755#
756set count 0
757itcl::body Rappture::MolvisViewer::ReceiveImage { size cacheid frame rock } {
758    global readyForNextFrame
759    set readyForNextFrame 1
760    set tag "$frame,$rock"
761    global count
762    incr count
763    if { $cacheid != $_cacheid } {
764        array unset _imagecache
765        set _cacheid $cacheid
766    }
767    set data [ReceiveBytes $size]
768    #debug "success: reading $size bytes from proxy\n"
769    if { [string match "print*" $cacheid] } {
770        # $frame is the token that we sent to the proxy.
771        set _hardcopy($this-$cacheid) $data
772        puts stderr "setting _hardcopy($this-$cacheid)"
773    } else {
774        set _imagecache($tag) $data
775        #debug "CACHED: $tag,$cacheid"
776        $_image(plot) configure -data $data
777        set _image(id) $tag
778    }
779}
780
781itcl::body Rappture::MolvisViewer::BuildSettingsTab {} {
782    set fg [option get $itk_component(hull) font Font]
783
784    set inner [$itk_component(main) insert end \
785        -title "Settings" \
786        -icon [Rappture::icon wrench]]
787    $inner configure -borderwidth 4
788
789    label $inner.pict -image $_settings($this-modelimg)
790
791    label $inner.rep_l -text "Molecule Representation" \
792        -font "Arial 9"
793
794    itk_component add representation {
795        Rappture::Combobox $inner.rep -width 20 -editable no
796    }
797    $inner.rep choices insert end \
798        "ballnstick"  "ball and stick" \
799        "spheres"     "spheres"         \
800        "sticks"      "sticks"          \
801        "lines"       "lines"           \
802        "cartoon"     "cartoon"         
803
804    bind $inner.rep <<Value>> [itcl::code $this Representation]
805    $inner.rep value "ball and stick"
806
807    scale $inner.spherescale -width 10 -font "Arial 9" \
808        -from 0.1 -to 2.0 -resolution 0.05 -label "Sphere Scale" \
809        -showvalue true -orient horizontal \
810        -command [itcl::code $this EventuallyChangeSettings] \
811        -variable Rappture::MolvisViewer::_settings($this-spherescale)
812    $inner.spherescale set $_settings($this-spherescale)
813    Rappture::Tooltip::for $inner.spherescale \
814        "Adjust scale of atoms (spheres or balls). 1.0 is the full VDW radius."
815
816    scale $inner.stickradius -width 10 -font "Arial 9" \
817        -from 0.1 -to 1.0 -resolution 0.025 -label "Stick Radius" \
818        -showvalue true -orient horizontal \
819        -command [itcl::code $this EventuallyChangeSettings] \
820        -variable Rappture::MolvisViewer::_settings($this-stickradius)
821    Rappture::Tooltip::for $inner.stickradius \
822        "Adjust scale of bonds (sticks)."
823    $inner.stickradius set $_settings($this-stickradius)
824
825    checkbutton $inner.labels -text "Show labels on atoms" \
826        -command [itcl::code $this labels update] \
827        -variable [itcl::scope _settings($this-showlabels)] \
828        -font "Arial 9"
829    Rappture::Tooltip::for $inner.labels \
830        "Display atom symbol and serial number."
831
832    checkbutton $inner.rock -text "Rock model back and forth" \
833        -command [itcl::code $this Rock toggle] \
834        -variable Rappture::MolvisViewer::_settings($this-rock) \
835        -font "Arial 9"
836    Rappture::Tooltip::for $inner.rock \
837        "Rotate the object back and forth around the y-axis."
838
839    checkbutton $inner.ortho -text "Orthoscopic projection" \
840        -command [itcl::code $this OrthoProjection update] \
841        -variable Rappture::MolvisViewer::_settings($this-ortho) \
842         -font "Arial 9"
843    Rappture::Tooltip::for $inner.ortho \
844        "Toggle between orthoscopic/perspective projection modes."
845
846    checkbutton $inner.cartoontrace -text "Cartoon Trace" \
847        -command [itcl::code $this CartoonTrace update] \
848        -variable [itcl::scope _settings($this-cartoontrace)] \
849        -font "Arial 9"
850    Rappture::Tooltip::for $inner.cartoontrace \
851        "Set cartoon representation of bonds (sticks)."
852
853    checkbutton $inner.cell -text "Parallelepiped" \
854        -command [itcl::code $this Cell toggle] \
855        -font "Arial 9"
856    $inner.cell select
857
858    label $inner.spacer
859    blt::table $inner \
860        0,0 $inner.labels -anchor w -pady {1 0} \
861        1,0 $inner.rock -anchor w -pady {1 0} \
862        2,0 $inner.ortho -anchor w -pady {1 0} \
863        3,0 $inner.cartoontrace -anchor w -pady {1 0} \
864        4,0 $inner.cell -anchor w  -pady {1 0} \
865        5,0 $inner.rep_l -anchor w -pady { 2 0 } \
866        6,0 $inner.rep -anchor w  \
867        7,0 $inner.spherescale -fill x -pady {3 0} \
868        8,0 $inner.stickradius -fill x -pady {1 0} \
869
870    blt::table configure $inner r* -resize none
871    blt::table configure $inner r10 -resize expand
872}
873
874# ----------------------------------------------------------------------
875# USAGE: Rebuild
876#
877# Called automatically whenever something changes that affects the
878# data in the widget.  Clears any existing data and rebuilds the
879# widget to display new data.
880# ----------------------------------------------------------------------
881itcl::body Rappture::MolvisViewer::Rebuild {} {
882    debug "in rebuild"
883    set changed 0
884
885    # Turn on buffering of commands to the server.  We don't want to
886    # be preempted by a server disconnect/reconnect (that automatically
887    # generates a new call to Rebuild).   
888    StartBufferingCommands
889    set _cell 0
890
891    if { $_reset } {
892        set _rocker(server) 0
893        set _cacheid 0
894
895        ServerCmd "raw -defer {set auto_color,0}"
896        ServerCmd "raw -defer {set auto_show_lines,0}"
897    }
898    set _first ""
899    set dlist [get]
900    foreach dataobj $dlist {
901        if { $_first == "" } {
902            set _first $dataobj
903        }
904        set model [$dataobj get components.molecule.model]
905        if {"" == $model } {
906            set model "molecule"
907            scan $dataobj "::libraryObj%d" suffix
908            set model $model$suffix
909        }
910        lappend _obj2models($dataobj) $model
911        set state [$dataobj get components.molecule.state]
912        if {"" == $state} {
913            set state $_state(server)
914        }
915        if { ![info exists _mlist($model)] } {  # new, turn on
916            set _mlist($model) 2
917        } elseif { $_mlist($model) == 1 } {     # on, leave on
918            set _mlist($model) 3
919        } elseif { $_mlist($model) == 0 } {     # off, turn on
920            set _mlist($model) 2
921        }
922        if { ![info exists _dataobjs($model-$state)] } {
923            set data1      ""
924            set serial    1
925
926            if { $_reportClientInfo }  {
927                set parent [$dataobj parent -as object]
928                while { $parent != "" } {
929                    set xmlobj $parent
930                    set parent [$parent parent -as object]
931                }
932                set info {}
933                lappend info "tool_id"      [$xmlobj get tool.id]
934                lappend info "tool_name"    [$xmlobj get tool.name]
935                lappend info "tool_title"   [$xmlobj get tool.title]
936                lappend info "tool_command" [$xmlobj get tool.execute]
937                lappend info "tool_revision" \
938                    [$xmlobj get tool.version.application.revision]
939                ServerCmd "clientinfo [list $info]"
940            }
941            foreach _atom [$dataobj children -type atom components.molecule] {
942                set symbol [$dataobj get components.molecule.$_atom.symbol]
943                set xyz [$dataobj get components.molecule.$_atom.xyz]
944                regsub {,} $xyz {} xyz
945                scan $xyz "%f %f %f" x y z
946                set recname  "ATOM  "
947                set altLoc   ""
948                set resName  ""
949                set chainID  ""
950                set Seqno    ""
951                set occupancy  1
952                set tempFactor 0
953                set recID      ""
954                set segID      ""
955                set element    ""
956                set charge     ""
957                set atom $symbol
958                set line [format "%6s%5d %4s%1s%3s %1s%5s   %8.3f%8.3f%8.3f%6.2f%6.2f%8s\n" $recname $serial $atom $altLoc $resName $chainID $Seqno $x $y $z $occupancy $tempFactor $recID]
959                append data1 $line
960                incr serial
961            }
962            if {"" != $data1} {
963                # Save the PDB data in case the user wants to later save it.
964                set _pdbdata $data1
965                set numBytes [string length $data1]
966
967                # We know we're buffered here, so append the "loadpdb" command
968                # with the data payload immediately afterwards.
969                ServerCmd "loadpdb -defer follows $model $state $numBytes"
970                append _outbuf $data1
971                set _dataobjs($model-$state)  1
972            }
973            # note that pdb files always overwrite xyz files
974            set data2 [$dataobj get components.molecule.pdb]
975            if {"" != $data2} {
976                # Save the PDB data in case the user wants to later save it.
977                set _pdbdata $data2
978                set numBytes [string length $data2]
979
980                # We know we're buffered here, so append the "loadpdb" command
981                # with the data payload immediately afterwards.
982                ServerCmd "loadpdb -defer follows $model $state $numBytes"
983                append _outbuf $data2
984                set _dataobjs($model-$state)  1
985            }
986            # lammps dump file overwrites pdb file (change this?)
987            set lammpstypemap [$dataobj get components.molecule.lammpstypemap]
988            set lammpsdata [$dataobj get components.molecule.lammps]
989            if {"" != $lammpsdata} {
990                set data3 ""
991                set modelcount 0
992                foreach lammpsline [split $lammpsdata "\n"] {
993                    if {[scan $lammpsline "%d %d %f %f %f" id type x y z] == 5} {
994                        set recname  "ATOM  "
995                        set altLoc   ""
996                        set resName  ""
997                        set chainID  ""
998                        set Seqno    ""
999                        set occupancy  1
1000                        set tempFactor 0
1001                        set recID      ""
1002                        set segID      ""
1003                        set element    ""
1004                        set charge     ""
1005                        if { "" == $lammpstypemap} {
1006                            set atom $type
1007                        } else {
1008                            set atom [lindex $lammpstypemap [expr {$type - 1}]]
1009                            if { "" == $atom} {
1010                              set atom $type
1011                            }
1012                        }
1013                        set pdbline [format "%6s%5d %4s%1s%3s %1s%5s   %8.3f%8.3f%8.3f%6.2f%6.2f%8s\n" $recname $id $atom $altLoc $resName $chainID $Seqno $x $y $z $occupancy $tempFactor $recID]
1014                        append data3 $pdbline
1015                    }
1016                    # only read first model
1017                    if {[regexp "^ITEM: ATOMS" $lammpsline]} {
1018                      incr modelcount
1019                      if {$modelcount > 1} {
1020                        break
1021                      }
1022                    }
1023                }
1024                if {"" != $data3} {
1025                    # Save the PDB data in case the user wants to later save it.
1026                    set _pdbdata $data3
1027                    set numBytes [string length $data3]
1028
1029                    # We know we're buffered here, so append the "loadpdb"
1030                    # command with the data payload immediately afterwards.
1031                    ServerCmd "loadpdb -defer follows $model $state $numBytes"
1032                    append _outbuf $data3
1033                }
1034                set _dataobjs($model-$state) 1
1035            }
1036        }
1037        if { ![info exists _model($model-transparency)] } {
1038            set _model($model-transparency) ""
1039        }
1040        if { ![info exists _model($model-rep)] } {
1041            set _model($model-rep) ""
1042            set _model($model-newrep) $_mrep
1043        }
1044        if { $_model($model-transparency) != $_dobj2transparency($dataobj) } {
1045            set _model($model-newtransparency) $_dobj2transparency($dataobj)
1046        }
1047        if { $_dobj2transparency($dataobj) == "ghost"} {
1048            array unset _active $model
1049        } else {
1050            set _active($model) $dataobj
1051        }
1052        set vector [$dataobj get components.parallelepiped.vector]
1053        if { $vector != "" } {
1054            set vertices [ComputeParallelepipedVertices $dataobj]
1055            ServerCmd "raw -defer {verts = \[$vertices\]\n}"
1056            ServerCmd "raw -defer {run \$PYMOL_SITE_PATH/rappture/box.py\n}"
1057            ServerCmd "raw -defer {draw_box(verts)\n}"
1058            set _cell 1
1059        }
1060    }
1061       
1062    # enable/disable models as required (0=off->off, 1=on->off, 2=off->on,
1063    # 3=on->on)
1064
1065    foreach model [array names _mlist] {
1066        if { $_mlist($model) == 1 } {
1067            ServerCmd "disable -defer $model"
1068            set _mlist($model) 0
1069            set changed 1
1070        } elseif { $_mlist($model) == 2 } {
1071            set _mlist($model) 1
1072            ServerCmd "enable -defer $model"
1073            set changed 1
1074        } elseif { $_mlist($model) == 3 } {
1075            set _mlist($model) 1
1076        }
1077        if { $_mlist($model) == 1 } {
1078            if {  [info exists _model($model-newtransparency)] ||
1079                  [info exists _model($model-newrep)] } {
1080                if { ![info exists _model($model-newrep)] } {
1081                    set _model($model-newrep) $_model($model-rep)
1082                }
1083                if { ![info exists _model($model-newtransparency)] } {
1084                    set _model($model-newtransparency) $_model($model-transparency)
1085                }
1086                set rep $_model($model-newrep)
1087                set transp $_model($model-newtransparency)
1088                ServerCmd "representation -defer -model $model $rep"
1089                set changed 1
1090                set _model($model-transparency) $_model($model-newtransparency)
1091                set _model($model-rep) $_model($model-newrep)
1092                catch {
1093                    unset _model($model-newtransparency)
1094                    unset _model($model-newrep)
1095                }
1096            }
1097        }
1098
1099    }
1100
1101    if { $changed } {
1102        array unset _imagecache
1103    }
1104    if { $dlist == "" } {
1105        set _state(server) 1
1106        set _state(client) 1
1107        ServerCmd "frame 1"
1108        set flush 1
1109    } elseif { ![info exists _imagecache($state,$_rocker(client))] } {
1110        set _state(server) $state
1111        set _state(client) $state
1112        ServerCmd "frame $state"
1113        set flush 1
1114    } else {
1115        set _state(client) $state
1116        UpdateState
1117        set flush 0
1118    }
1119    if { $_reset } {
1120        # Set or restore viewing parameters.  We do this for the first
1121        # model and assume this works for everything else.
1122        set w  [winfo width $itk_component(3dview)]
1123        set h  [winfo height $itk_component(3dview)]
1124        ServerCmd [subst {
1125            reset
1126            screen $w $h
1127            rotate $_view(mx) $_view(my) $_view(mz)
1128            pan $_view(x) $_view(y)
1129            zoom $_view(zoom)
1130        }]
1131        debug "rebuild: rotate $_view(mx) $_view(my) $_view(mz)"
1132
1133        ServerCmd "raw -defer {zoom complete=1}"
1134        set _reset 0
1135    }
1136    if { $changed } {
1137        # Default settings for all models.
1138        SphereScale update
1139        StickRadius update
1140        labels update
1141        Opacity update
1142        CartoonTrace update
1143        Cell update
1144        OrthoProjection update
1145        Representation update
1146    }
1147    set inner [$itk_component(main) panel "Settings"]
1148    if { $_cell } {
1149        $inner.cell configure -state normal
1150    } else {
1151        $inner.cell configure -state disabled
1152    }
1153    if { $flush } {
1154        global readyForNextFrame
1155        set readyForNextFrame 0;        # Don't advance to the next frame
1156                                        # until we get an image.
1157        #ServerCmd "ppm";                 # Flush the results.
1158    }
1159    blt::busy hold $itk_component(hull)
1160    StopBufferingCommands
1161    blt::busy release $itk_component(hull)
1162
1163    debug "exiting rebuild"
1164}
1165
1166itcl::body Rappture::MolvisViewer::Unmap { } {
1167    # Pause rocking loop while unmapped (saves CPU time)
1168    Rock pause
1169
1170    # Blank image, mark current image dirty
1171    # This will force reload from cache, or remain blank if cache is cleared
1172    # This prevents old image from briefly appearing when a new result is added
1173    # by result viewer
1174
1175    #$_image(plot) blank
1176    set _image(id) ""
1177}
1178
1179itcl::body Rappture::MolvisViewer::Map { } {
1180    if { [isconnected] } {
1181        # Resume rocking loop if it was on
1182        Rock unpause
1183        # Rebuild image if modified, or redisplay cached image if not
1184        $_dispatcher event -idle !rebuild
1185    }
1186}
1187
1188itcl::body Rappture::MolvisViewer::DoResize { } {
1189    ServerCmd "screen $_width $_height"
1190    $_image(plot) configure -width $_width -height $_height
1191    # Immediately invalidate cache, defer update until mapped
1192    array unset _imagecache
1193    set _resizePending 0
1194}
1195   
1196itcl::body Rappture::MolvisViewer::EventuallyResize { w h } {
1197    set _width $w
1198    set _height $h
1199    if { !$_resizePending } {
1200        $_dispatcher event -after 400 !resize
1201        set _resizePending 1
1202    }
1203}
1204
1205itcl::body Rappture::MolvisViewer::DoRotate {} {
1206    ServerCmd "rotate $_view(a) $_view(b) $_view(c)"
1207    array unset _imagecache
1208    set _rotatePending 0
1209}
1210   
1211itcl::body Rappture::MolvisViewer::EventuallyRotate { a b c } {
1212    set _view(a) $a
1213    set _view(b) $b
1214    set _view(c) $c
1215    if { !$_rotatePending } {
1216        $_dispatcher event -after 100 !rotate
1217        set _rotatePending 1
1218    }
1219}
1220
1221itcl::body Rappture::MolvisViewer::DoUpdate { } {
1222    array unset _imagecache
1223    set models [array names _mlist]
1224    SphereScale $_settings($this-spherescale) $models
1225    StickRadius $_settings($this-stickradius) $models
1226    set _updatePending 0
1227}
1228
1229itcl::body Rappture::MolvisViewer::EventuallyChangeSettings { args } {
1230    if { !$_updatePending } {
1231        $_dispatcher event -after 400 !update
1232        set _updatePending 1
1233    }
1234}
1235
1236# ----------------------------------------------------------------------
1237# USAGE: $this Pan click x y
1238#        $this Pan drag x y
1239#        $this Pan release x y
1240#
1241# Called automatically when the user clicks on one of the zoom
1242# controls for this widget.  Changes the zoom for the current view.
1243# ----------------------------------------------------------------------
1244itcl::body Rappture::MolvisViewer::Pan {option x y} {
1245    if { $option == "set" } {
1246        set dx $x
1247        set dy $y
1248        set _view(x) [expr $_view(x) + $dx]
1249        set _view(y) [expr $_view(y) + $dy]
1250        array unset _imagecache
1251        ServerCmd "pan $dx $dy"
1252        return
1253    }
1254    if { ![info exists _mevent(x)] } {
1255        set option "click"
1256    }
1257    if { $option == "click" } {
1258        $itk_component(3dview) configure -cursor hand1
1259    }
1260    if { $option == "drag" || $option == "release" } {
1261        set dx [expr $x - $_mevent(x)]
1262        set dy [expr $y - $_mevent(y)]
1263        set _view(x) [expr $_view(x) + $dx]
1264        set _view(y) [expr $_view(y) + $dy]
1265        array unset _imagecache
1266        ServerCmd "pan $dx $dy"
1267    }
1268    set _mevent(x) $x
1269    set _mevent(y) $y
1270    if { $option == "release" } {
1271        $itk_component(3dview) configure -cursor ""
1272    }
1273}
1274
1275# ----------------------------------------------------------------------
1276# USAGE: Zoom in
1277# USAGE: Zoom out
1278# USAGE: Zoom reset
1279#
1280# Called automatically when the user clicks on one of the zoom
1281# controls for this widget.  Changes the zoom for the current view.
1282# ----------------------------------------------------------------------
1283itcl::body Rappture::MolvisViewer::Zoom {option {factor 10}} {
1284    switch -- $option {
1285        "in" {
1286            set _view(zoom) [expr $_view(zoom) + $factor]
1287            ServerCmd "zoom $factor"
1288        }
1289        "out" {
1290            set _view(zoom) [expr $_view(zoom) - $factor]
1291            ServerCmd "zoom -$factor"
1292        }
1293        "reset" {
1294            set _view(zoom) 0
1295            ServerCmd "reset"
1296        }
1297    }
1298    array unset _imagecache
1299}
1300
1301itcl::body Rappture::MolvisViewer::UpdateState { args } {
1302    set tag "$_state(client),$_rocker(client)"
1303    if { $_image(id) != "$tag" } {
1304        if { [info exists _imagecache($tag)] } {
1305            $_image(plot) configure -data $_imagecache($tag)
1306            set _image(id) "$tag"
1307        }
1308    }
1309}
1310
1311# ----------------------------------------------------------------------
1312# USAGE: Rock on|off|toggle
1313# USAGE: Rock pause|unpause|step
1314#
1315# Used to control the "rocking" model for the molecule being displayed.
1316# Clients should use only the on/off/toggle options; the rest are for
1317# internal control of the rocking motion.
1318# ----------------------------------------------------------------------
1319itcl::body Rappture::MolvisViewer::Rock { option } {
1320    # cancel any pending rocks
1321    if { [info exists _rocker(afterid)] } {
1322        after cancel $_rocker(afterid)
1323        unset _rocker(afterid)
1324    }
1325    if { ![winfo viewable $itk_component(3dview)] } {
1326        return
1327    }
1328    set _rocker(on) $_settings($this-rock)
1329    if { $option == "step"} {
1330        if { $_rocker(client) >= 10 } {
1331            set _rocker(dir) -1
1332        } elseif { $_rocker(client) <= -10 } {
1333            set _rocker(dir) 1
1334        }
1335        set _rocker(client) [expr {$_rocker(client) + $_rocker(dir)}]
1336        if { ![info exists _imagecache($_state(server),$_rocker(client))] } {
1337            set _rocker(server) $_rocker(client)
1338            ServerCmd "rock $_rocker(client)"
1339        }
1340        UpdateState
1341    }
1342    if { $_rocker(on) && $option != "pause" } {
1343         set _rocker(afterid) [after 200 [itcl::code $this Rock step]]
1344    }
1345}
1346
1347
1348itcl::body Rappture::MolvisViewer::Vmouse2 {option b m x y} {
1349    set now [clock clicks -milliseconds]
1350    set vButton [expr $b - 1]
1351    set vModifier 0
1352    set vState 1
1353
1354    if { $m & 1 }      { set vModifier [expr $vModifier | 1 ] }
1355    if { $m & 4 }      { set vModifier [expr $vModifier | 2 ] }
1356    if { $m & 131072 } { set vModifier [expr $vModifier | 4 ] }
1357
1358    if { $option == "click"   } { set vState 0 }
1359    if { $option == "release" } { set vState 1 }
1360    if { $option == "drag"    } { set vState 2 }
1361    if { $option == "move"    } { set vState 3 }
1362
1363    if { $vState == 2 || $vState == 3} {
1364        set diff 0
1365
1366        catch { set diff [expr $now - $_mevent(time)] }
1367        if {$diff < 75} { # 75ms between motion updates
1368            return
1369        }
1370    }
1371    ServerCmd "vmouse $vButton $vModifier $vState $x $y"
1372    set _mevent(time) $now
1373}
1374
1375itcl::body Rappture::MolvisViewer::Vmouse {option b m x y} {
1376    set now  [clock clicks -milliseconds]
1377    # cancel any pending delayed dragging events
1378    if { [info exists _mevent(afterid)] } {
1379        after cancel $_mevent(afterid)
1380        unset _mevent(afterid)
1381    }
1382
1383    if { ![info exists _mevent(x)] } {
1384        set option "click"
1385    }
1386    if { $option == "click" } {
1387        $itk_component(3dview) configure -cursor fleur
1388    }
1389    if { $option == "drag" || $option == "release" } {
1390        set diff 0
1391         catch { set diff [expr $now - $_mevent(time) ] }
1392         if {$diff < 25 && $option == "drag" } { # 75ms between motion updates
1393             set _mevent(afterid) [after [expr 25 - $diff] [itcl::code $this Vmouse drag $b $m $x $y]]
1394             return
1395         }
1396        set w [winfo width $itk_component(3dview)]
1397        set h [winfo height $itk_component(3dview)]
1398        if {$w <= 0 || $h <= 0} {
1399            return
1400        }
1401        set x1 [expr double($w) / 3]
1402        set x2 [expr $x1 * 2]
1403        set y1 [expr double($h) / 3]
1404        set y2 [expr $y1 * 2]
1405        set dx [expr $x - $_mevent(x)]
1406        set dy [expr $y - $_mevent(y)]
1407        set mx 0
1408        set my 0
1409        set mz 0
1410
1411        if { $_mevent(x) < $x1 } {
1412            set mz $dy
1413        } elseif { $_mevent(x) < $x2 } {
1414            set mx $dy
1415        } else {
1416            set mz [expr -$dy]
1417        }
1418
1419        if { $_mevent(y) < $y1 } {
1420            set mz [expr -$dx]
1421        } elseif { $_mevent(y) < $y2 } {
1422            set my $dx
1423        } else {
1424            set mz $dx
1425        }
1426        # Accumlate movements
1427        set _view(mx) [expr {$_view(mx) + $mx}]
1428        set _view(my) [expr {$_view(my) + $my}]
1429        set _view(mz) [expr {$_view(mz) + $mz}]
1430        #ServerCmd "rotate $mx $my $mz"
1431        EventuallyRotate $mx $my $mz
1432        debug "_vmmouse: rotate $_view(mx) $_view(my) $_view(mz)"
1433    }
1434    set _mevent(x) $x
1435    set _mevent(y) $y
1436    set _mevent(time) $now
1437    if { $option == "release" } {
1438        $itk_component(3dview) configure -cursor ""
1439    }
1440}
1441
1442# ----------------------------------------------------------------------
1443# USAGE: Rotate click <x> <y>
1444# USAGE: Rotate drag <x> <y>
1445# USAGE: Rotate release <x> <y>
1446#
1447# Called automatically when the user clicks/drags/releases in the
1448# plot area.  Moves the plot according to the user's actions.
1449# ----------------------------------------------------------------------
1450itcl::body Rappture::MolvisViewer::Rotate {option x y} {
1451    set now  [clock clicks -milliseconds]
1452    #update idletasks
1453    # cancel any pending delayed dragging events
1454    if { [info exists _mevent(afterid)] } {
1455        after cancel $_mevent(afterid)
1456        unset _mevent(afterid)
1457    }
1458    switch -- $option {
1459        click {
1460            $itk_component(3dview) configure -cursor fleur
1461            set _click(x) $x
1462            set _click(y) $y
1463            set _click(theta) $_view(theta)
1464            set _click(phi) $_view(phi)
1465        }
1466        drag {
1467            if {[array size _click] == 0} {
1468                Rotate click $x $y
1469            } else {
1470                set w [winfo width $itk_component(3dview)]
1471                set h [winfo height $itk_component(3dview)]
1472                if {$w <= 0 || $h <= 0} {
1473                    return
1474                }
1475#         set diff 0
1476#          catch { set diff [expr $now - $_mevent(time) ] }
1477#          if {$diff < 175 && $option == "drag" } { # 75ms between motion updates
1478#              set _mevent(afterid) [after [expr 175 - $diff] [itcl::code $this Rotate drag $x $y]]
1479#              return
1480#          }
1481
1482                if {[catch {
1483                    # this fails sometimes for no apparent reason
1484                    set dx [expr {double($x-$_click(x))/$w}]
1485                    set dy [expr {double($y-$_click(y))/$h}]
1486                }]} {
1487                    return
1488                }
1489
1490                #
1491                # Rotate the camera in 3D
1492                #
1493                if {$_view(psi) > 90 || $_view(psi) < -90} {
1494                    # when psi is flipped around, theta moves backwards
1495                    set dy [expr {-$dy}]
1496                }
1497                set theta [expr {$_view(theta) - $dy*180}]
1498                while {$theta < 0} { set theta [expr {$theta+180}] }
1499                while {$theta > 180} { set theta [expr {$theta-180}] }
1500
1501                if {abs($theta) >= 30 && abs($theta) <= 160} {
1502                    set phi [expr {$_view(phi) - $dx*360}]
1503                    while {$phi < 0} { set phi [expr {$phi+360}] }
1504                    while {$phi > 360} { set phi [expr {$phi-360}] }
1505                    set psi $_view(psi)
1506                } else {
1507                    set phi $_view(phi)
1508                    set psi [expr {$_view(psi) - $dx*360}]
1509                    while {$psi < -180} { set psi [expr {$psi+360}] }
1510                    while {$psi > 180} { set psi [expr {$psi-360}] }
1511                }
1512                array set _view [subst {
1513                    theta $theta
1514                    phi $phi
1515                    psi $psi
1516                }]
1517                foreach { vx vy vz } [Euler2XYZ $theta $phi $psi] break
1518                set a [expr $vx - $_view(vx)]
1519                set a [expr -$a]
1520                set b [expr $vy - $_view(vy)]
1521                set c [expr $vz - $_view(vz)]
1522                array set _view [subst {
1523                    vx $vx
1524                    vy $vy
1525                    vz $vz
1526                }]
1527                EventuallyRotate $a $b $c
1528                #ServerCmd "rotate $a $b $c"
1529                debug "Rotate $x $y: rotate $_view(vx) $_view(vy) $_view(vz)"
1530                set _click(x) $x
1531                set _click(y) $y
1532            }
1533        }
1534        release {
1535            Rotate drag $x $y
1536            $itk_component(3dview) configure -cursor ""
1537            catch {unset _click}
1538        }
1539        default {
1540            error "bad option \"$option\": should be click, drag, release"
1541        }
1542    }
1543    set _mevent(time) $now
1544}
1545
1546# ----------------------------------------------------------------------
1547# USAGE: Representation spheres|ballnstick|lines|sticks
1548#
1549# Used internally to change the molecular representation used to render
1550# our scene.
1551# ----------------------------------------------------------------------
1552itcl::body Rappture::MolvisViewer::Representation { { option "" } } {
1553    if { $option == "" } {
1554        set value [$itk_component(representation) value]
1555        set option [$itk_component(representation) translate $value]
1556    }
1557    if { $option == $_mrep } {
1558        return
1559    }
1560    if { $option == "update" } {
1561        set option $_settings($this-model)
1562    }
1563    array unset _imagecache
1564    if { $option == "sticks" } {
1565        set _settings($this-modelimg) [Rappture::icon lines]
1566    }  else {
1567        set _settings($this-modelimg) [Rappture::icon $option]
1568    }
1569    set inner [$itk_component(main) panel "Settings"]
1570    $inner.pict configure -image $_settings($this-modelimg)
1571
1572    # Save the current option to set all radiobuttons -- just in case.
1573    # This method gets called without the user clicking on a radiobutton.
1574    set _settings($this-model) $option
1575    set _mrep $option
1576
1577    foreach model [array names _mlist] {
1578        if { [info exists _model($model-rep)] } {
1579            if { $_model($model-rep) != $option } {
1580                set _model($model-newrep) $option
1581            } else {
1582                catch { unset _model($model-newrep) }
1583            }
1584        }
1585    }
1586    if { [isconnected] } {
1587        ServerCmd "representation -model $model $option"
1588        #$_dispatcher event -idle !rebuild
1589    }
1590}
1591
1592
1593# ----------------------------------------------------------------------
1594# USAGE: OrthoProjection on|off|toggle
1595# USAGE: OrthoProjection update
1596#
1597# Used internally to turn labels associated with atoms on/off, and to
1598# update the positions of the labels so they sit on top of each atom.
1599# ----------------------------------------------------------------------
1600itcl::body Rappture::MolvisViewer::OrthoProjection {option} {
1601    array unset _imagecache
1602    switch -- $option {
1603        "orthoscopic" {
1604            set ortho 1
1605        }
1606        "perspective" {
1607            set ortho 0
1608        }
1609        "toggle" {
1610            set ortho [expr {$_settings($this-ortho) == 0}]
1611        }
1612        "update" {
1613            set ortho $_settings($this-ortho)
1614        }
1615        default {
1616            error "bad option \"$option\": should be on, off, toggle, or update"
1617        }
1618    }
1619    if { $ortho == $_settings($this-ortho) && $option != "update"} {
1620        # nothing to do
1621        return
1622    }
1623    if { $ortho } {
1624        $itk_component(ortho) configure -image [Rappture::icon molvis-3dorth]
1625        Rappture::Tooltip::for $itk_component(ortho) \
1626            "Use perspective projection"
1627        set _settings($this-ortho) 1
1628        ServerCmd "orthoscopic on"
1629    } else {
1630        $itk_component(ortho) configure -image [Rappture::icon molvis-3dpers]
1631        Rappture::Tooltip::for $itk_component(ortho) \
1632            "Use orthoscopic projection"
1633        set _settings($this-ortho) 0
1634        ServerCmd "orthoscopic off"
1635    }
1636}
1637
1638# ----------------------------------------------------------------------
1639# USAGE: Cell on|off|toggle
1640#
1641# Used internally to turn labels associated with atoms on/off, and to
1642# update the positions of the labels so they sit on top of each atom.
1643# ----------------------------------------------------------------------
1644itcl::body Rappture::MolvisViewer::Cell {option} {
1645    switch -- $option {
1646        "on" - "off" {
1647            set cell $option
1648        }
1649        "toggle" {
1650            set cell [expr {$_settings($this-showcell) == 0}]
1651        }
1652        "update" {
1653            set cell $_settings($this-showcell)
1654        }
1655        default {
1656            error "bad option \"$option\": should be on, off, toggle, or update"
1657        }
1658    }
1659    if { $cell == $_settings($this-showcell) && $option != "update"} {
1660        # nothing to do
1661        return
1662    }
1663    array unset _imagecache
1664    if { $cell } {
1665        Rappture::Tooltip::for $itk_component(ortho) \
1666            "Hide the cell."
1667        set _settings($this-showcell) 1
1668        ServerCmd "raw {show everything,unitcell}"
1669    } else {
1670        Rappture::Tooltip::for $itk_component(ortho) \
1671            "Show the cell."
1672        set _settings($this-showcell) 0
1673        ServerCmd "raw {hide everything,unitcell}"
1674    }
1675}
1676
1677
1678#
1679# ResetView
1680#
1681itcl::body Rappture::MolvisViewer::ResetView {} {
1682    array set _view {
1683        theta   45
1684        phi     45
1685        psi     0
1686        mx      0
1687        my      0
1688        mz      0
1689        x       0
1690        y       0
1691        z       0
1692        zoom    0
1693        width   0
1694        height  0
1695    }
1696    ServerCmd "reset"
1697    DoResize
1698    ServerCmd "rotate $_view(mx) $_view(my) $_view(mz)"
1699    debug "ResetView: rotate $_view(mx) $_view(my) $_view(mz)"
1700    ServerCmd "pan $_view(x) $_view(y)"
1701    ServerCmd "zoom $_view(zoom)"
1702}
1703
1704
1705itcl::body Rappture::MolvisViewer::WaitIcon  { option widget } {
1706    switch -- $option {
1707        "start" {
1708            $_dispatcher dispatch $this !waiticon \
1709                "[itcl::code $this WaitIcon "next" $widget] ; list"
1710            set _icon 0
1711            $widget configure -image [Rappture::icon bigroller${_icon}]
1712            $_dispatcher event -after 100 !waiticon
1713        }
1714        "next" {
1715            incr _icon
1716            if { $_icon >= 8 } {
1717                set _icon 0
1718            }
1719            $widget configure -image [Rappture::icon bigroller${_icon}]
1720            $_dispatcher event -after 100 !waiticon
1721        }
1722        "stop" {
1723            $_dispatcher cancel !waiticon
1724        }
1725    }
1726}
1727           
1728itcl::body Rappture::MolvisViewer::GetImage { widget } {
1729    set token "print[incr _nextToken]"
1730    set var ::Rappture::MolvisViewer::_hardcopy($this-$token)
1731    set $var ""
1732
1733    set controls $_downloadPopup(image_controls)
1734    set combo $controls.size
1735    set size [$combo translate [$combo value]]
1736    switch -- $size {
1737        "standard" {
1738            set width 1200
1739            set height 1200
1740        }
1741        "highquality" {
1742            set width 2400
1743            set height 2400
1744        }
1745        "draft" {
1746            set width 400
1747            set height 400
1748        }
1749        default {
1750            error "unknown image size [$combo value]"
1751        }
1752    }
1753    # Setup an automatic timeout procedure.
1754    $_dispatcher dispatch $this !pngtimeout "set $var {} ; list"
1755   
1756    set popup .molvisviewerimagedownload
1757    if { ![winfo exists $popup] } {
1758        Rappture::Balloon $popup -title "Generating file..."
1759        set inner [$popup component inner]
1760        label $inner.title -text "Generating hardcopy." -font "Arial 10 bold"
1761        label $inner.please -text "This may take a minute." -font "Arial 10"
1762        label $inner.icon -image [Rappture::icon bigroller0]
1763        button $inner.cancel -text "Cancel" -font "Arial 10 bold" \
1764            -command [list set $var ""]
1765        blt::table $inner \
1766            0,0 $inner.title -cspan 2 \
1767            1,0 $inner.please -anchor w \
1768            1,1 $inner.icon -anchor e  \
1769            2,0 $inner.cancel -cspan 2
1770        blt::table configure $inner r0 -pady 4
1771        blt::table configure $inner r2 -pady 4
1772        bind $inner.cancel <Return> [list $inner.cancel invoke]
1773        bind $inner.cancel <KP_Enter> [list $inner.cancel invoke]
1774    } else {
1775        set inner [$popup component inner]
1776    }
1777    set combo $controls.bgcolor
1778    set bgcolor [$combo translate [$combo value]]
1779   
1780    $_dispatcher event -after 60000 !pngtimeout
1781    WaitIcon start $inner.icon
1782    grab set $inner
1783    focus $inner.cancel
1784   
1785    ServerCmd "print $token $width $height $bgcolor"
1786
1787    $popup activate $widget below
1788    # We wait here for either
1789    #  1) the png to be delivered or
1790    #  2) timeout or 
1791    #  3) user cancels the operation.
1792    tkwait variable $var
1793
1794    # Clean up.
1795    $_dispatcher cancel !pngtimeout
1796    WaitIcon stop $inner.icon
1797    grab release $inner
1798    $popup deactivate
1799    update
1800
1801    if { $_hardcopy($this-$token) != "" } {
1802        set combo $controls.format
1803        set fmt [$combo translate [$combo value]]
1804        switch -- $fmt {
1805            "jpg" {
1806                set img [image create photo -data $_hardcopy($this-$token)]
1807                set bytes [$img data -format "jpeg -quality 100"]
1808                set bytes [Rappture::encoding::decode -as b64 $bytes]
1809                return [list .jpg $bytes]
1810            }
1811            "gif" {
1812                set img [image create photo -data $_hardcopy($this-$token)]
1813                set bytes [$img data -format "gif"]
1814                set bytes [Rappture::encoding::decode -as b64 $bytes]
1815                return [list .gif $bytes]
1816            }
1817            "png" {
1818                return [list .png $_hardcopy($this-$token)]
1819            }
1820        }
1821    }
1822    return ""
1823}
1824
1825# ----------------------------------------------------------------------
1826# USAGE: SphereScale radius ?model?
1827#        SphereScale update ?model?
1828#
1829# Used internally to change the molecular atom scale used to render
1830# our scene. 
1831#
1832# Note: Only sets the specified radius for active models.  If the model
1833#       is inactive, then it overridden with the value "0.1".
1834# ----------------------------------------------------------------------
1835
1836itcl::body Rappture::MolvisViewer::SphereScale { option {models "all"} } {
1837    if { $option == "update" } {
1838        set radius $_settings($this-spherescale)
1839    } elseif { [string is double $option] } {
1840        set radius $option
1841        if { ($radius < 0.1) || ($radius > 2.0) } {
1842            error "bad atom size \"$radius\""
1843        }
1844    } else {
1845        error "bad option \"$option\""
1846    }
1847    set _settings($this-spherescale) $radius
1848    if { $models == "all" } {
1849        ServerCmd "spherescale -model all $radius"
1850        return
1851    }
1852    set overrideradius [expr $radius * 0.8]
1853    ServerCmd "spherescale -model all $overrideradius"
1854    foreach model $models {
1855        if { [info exists _active($model)] } {
1856            ServerCmd "spherescale -model $model $radius"
1857        }
1858    }
1859}
1860
1861# ----------------------------------------------------------------------
1862# USAGE: StickRadius radius ?models?
1863#        StickRadius update ?models?
1864#
1865# Used internally to change the stick radius used to render
1866# our scene.
1867#
1868# Note: Only sets the specified radius for active models.  If the model
1869#       is inactive, then it overridden with the value "0.25".
1870# ----------------------------------------------------------------------
1871
1872itcl::body Rappture::MolvisViewer::StickRadius { option {models "all"} } {
1873    if { $option == "update" } {
1874        set radius $_settings($this-stickradius)
1875    } elseif { [string is double $option] } {
1876        set radius $option
1877        if { ($radius < 0.1) || ($radius > 2.0) } {
1878            error "bad stick radius \"$radius\""
1879        }
1880    } else {
1881        error "bad option \"$option\""
1882    }
1883    set _settings($this-stickradius) $radius
1884    if { $models == "all" } {
1885        ServerCmd "stickradius -model all $radius"
1886        return
1887    }
1888    set overrideradius [expr $radius * 0.8]
1889    ServerCmd "stickradius -model all $overrideradius"
1890    foreach model $models {
1891        if { [info exists _active($model)] } {
1892            ServerCmd "stickradius -model $model $radius"
1893        }
1894    }
1895}
1896
1897# ----------------------------------------------------------------------
1898# USAGE: Opacity value ?models?
1899#        Opacity update ?models?
1900#
1901# Used internally to change the opacity (transparency) used to render
1902# our scene.
1903#
1904# Note: Only sets the specified transparency for active models.  If the model
1905#       is inactive, then it overridden with the value "0.75".
1906# ----------------------------------------------------------------------
1907
1908itcl::body Rappture::MolvisViewer::Opacity { option } {
1909    array unset _imagecache
1910    if { $option == "update" } {
1911        set opacity $_settings($this-opacity)
1912    } elseif { [string is double $option] } {
1913        set opacity $option
1914        if { ($opacity < 0.0) || ($opacity > 1.0) } {
1915            error "bad opacity \"$opacity\""
1916        }
1917    } else {
1918        error "bad option \"$option\""
1919    }
1920    set _settings($this-opacity) $opacity
1921    set transparency [expr 1.0 - $opacity]
1922    set models [array names _active]
1923    if { [llength $models] == 0 } {
1924        ServerCmd "transparency -model all $transparency"
1925        return
1926    }
1927    set overridetransparency 0.60
1928    ServerCmd "transparency -model all $overridetransparency"
1929    foreach model $models {
1930        ServerCmd "transparency -model $model $transparency"
1931    }
1932}
1933
1934# ----------------------------------------------------------------------
1935# USAGE: labels on|off|toggle
1936# USAGE: labels update
1937#
1938# Used internally to turn labels associated with atoms on/off, and to
1939# update the positions of the labels so they sit on top of each atom.
1940# ----------------------------------------------------------------------
1941itcl::body Rappture::MolvisViewer::labels {option {models "all"}} {
1942    set showlabels $_settings($this-showlabels)
1943    if { $option == "update" } {
1944        set showlabels $_settings($this-showlabels)
1945    } elseif { [string is boolean $option] } {
1946        set showlabels $option
1947    } else {
1948        error "bad option \"$option\""
1949    }
1950    # Clear the image cache
1951    array unset _imagecache
1952    set _settings($this-showlabels) $showlabels
1953    if { $models == "all" } {
1954        ServerCmd "label -model all $showlabels"
1955        return
1956    }
1957    ServerCmd "label -model all off"
1958    if { $showlabels } {
1959        foreach model $models {
1960            if { [info exists _active($model)] } {
1961                ServerCmd "label -model $model $showlabels"
1962            }
1963        }
1964    }
1965}
1966
1967# ----------------------------------------------------------------------
1968# USAGE: CartoonTrace on|off|toggle
1969# USAGE: CartoonTrace update
1970#
1971# Used internally to turn labels associated with atoms on/off, and to
1972# update the positions of the labels so they sit on top of each atom.
1973# ----------------------------------------------------------------------
1974itcl::body Rappture::MolvisViewer::CartoonTrace {option {models "all"}} {
1975    array unset _imagecache
1976    set trace $_settings($this-cartoontrace)
1977    if { $option == "update" } {
1978        set trace $_settings($this-cartoontrace)
1979    } elseif { [string is boolean $option] } {
1980        set trace $option
1981    } else {
1982        error "bad option \"$option\""
1983    }
1984    set _settings($this-cartoontrace) $trace
1985    if { $models == "all" } {
1986        ServerCmd "cartoontrace -model all $trace"
1987        return
1988    }
1989    ServerCmd "cartoontrace -model all off"
1990    if { $trace } {
1991        foreach model $models {
1992            if { [info exists _active($model)] } {
1993                ServerCmd "cartoontrace -model $model $trace"
1994            }
1995        }
1996    }
1997}
1998
1999itcl::body Rappture::MolvisViewer::AddImageControls { inner widget } {
2000    label $inner.size_l -text "Size:" -font "Arial 9"
2001    set _downloadPopup(image_controls) $inner
2002    set img $_image(plot)
2003    set res "[image width $img]x[image height $img]"
2004    Rappture::Combobox $inner.size -width 30 -editable no
2005    $inner.size choices insert end \
2006        "draft"  "Draft (400x400)"         \
2007        "standard"  "Standard (1200x1200)"          \
2008        "highquality"  "High Quality (2400x2400)"
2009
2010    label $inner.bgcolor_l -text "Background:" -font "Arial 9"
2011    Rappture::Combobox $inner.bgcolor -width 30 -editable no
2012    $inner.bgcolor choices insert end \
2013        "black"  "Black" \
2014        "white"  "White" \
2015        "none"  "Transparent (PNG only)"         
2016
2017    label $inner.format_l -text "Format:" -font "Arial 9"
2018    Rappture::Combobox $inner.format -width 30 -editable no
2019    $inner.format choices insert end \
2020        "png"  "PNG (Portable Network Graphics format)" \
2021        "jpg"  "JPEG (Joint Photographic Experts Group format)" \
2022        "gif"  "GIF (GIF Graphics Interchange Format)"
2023
2024    set f [frame $inner.frame]
2025    button $f.ok -text "Save" \
2026        -highlightthickness 0 -pady 3 -padx 3 \
2027        -command [itcl::code $this SetWaitVariable 1] \
2028        -compound left \
2029        -image [Rappture::icon download
2030]
2031    button $f.cancel -text "Cancel" \
2032        -highlightthickness 0 -pady 3 -padx 3 \
2033        -command [itcl::code $this SetWaitVariable 0] \
2034        -compound left \
2035        -image [Rappture::icon cancel]
2036    blt::table $f \
2037        0,0 $f.ok  \
2038        0,1 $f.cancel
2039
2040    blt::table $inner \
2041        0,0 $inner.format_l -anchor e \
2042        0,1 $inner.format -anchor w -fill x  \
2043        1,0 $inner.size_l -anchor e \
2044        1,1 $inner.size -anchor w -fill x \
2045        2,0 $inner.bgcolor_l -anchor e \
2046        2,1 $inner.bgcolor -anchor w -fill x \
2047        3,0 $f -cspan 2 -fill x
2048    blt::table configure $inner r0 r1 r2 r3 -pady { 4 0 }
2049    blt::table configure $inner r3 -pady { 4 4 }
2050    $inner.bgcolor value "Black"
2051    $inner.size value "Draft (400x400)"
2052    $inner.format value  "PNG (Portable Network Graphics format)"
2053}
2054
2055itcl::body Rappture::MolvisViewer::snap { w h } {
2056    if { $w <= 0 || $h <= 0 } {
2057        set w [image width $_image(plot)]
2058        set h [image height $_image(plot)]
2059    }
2060    set tag "$_state(client),$_rocker(client)"
2061    if { $_image(id) != "$tag" } {
2062        while { ![info exists _imagecache($tag)] } {
2063            update idletasks
2064            update
2065            after 100
2066        }
2067        if { [info exists _imagecache($tag)] } {
2068            $_image(plot) configure -data $_imagecache($tag)
2069            set _image(id) "$tag"
2070        }
2071    }
2072    set img [image create picture -width $w -height $h]
2073    $img resample $_image(plot)
2074    return $img
2075}
2076
2077# FIXME: Handle 2D vectors
2078itcl::body Rappture::MolvisViewer::ComputeParallelepipedVertices { dataobj } {
2079    # Create a vector for every 3D point
2080    blt::vector point0(3) point1(3) point2(3) point3(3) point4(3) point5(3) \
2081        point6(3) point7(3) origin(3) scale(3)
2082
2083    set count 0
2084    set parent [$dataobj element -as object "components.parallelepiped"]
2085    foreach child [$parent children] {
2086        if { ![string match "vector*" $child] } {
2087            continue
2088        }
2089        incr count
2090        set values [$parent get $child]
2091        regexp -all {,} $values { } values
2092        point$count set $values
2093    }
2094    itcl::delete object $parent
2095    if { $count < 1 || $count > 3 } {
2096        error "bad number of vectors supplied to parallelepiped"
2097    }
2098    point0 set { 0.0 0.0 0.0 }
2099    point4 expr {point2 + point1}
2100    point5 expr {point4 + point3}
2101    point6 expr {point2 + point3}
2102    point7 expr {point1 + point3}
2103
2104    set values [$dataobj get components.parallelepiped.scale]
2105    set n [llength $values]
2106    scale set { 1.0 1.0 1.0 }
2107    if { $n == 1 } {
2108        set scale(0:2) [lindex $values 0]
2109    } elseif { $n == 2 } {
2110        set scale(0:1) [lindex $values 0]
2111    } elseif { $n == 3 } {
2112        scale set $values
2113    }
2114    set values [$dataobj get components.parallelepiped.origin]
2115    set n [llength $values]
2116    origin set { 0.0 0.0 0.0 }
2117    if { $n == 1 } {
2118        set origin(0) [lindex $values 0]
2119    } elseif { $n == 2 } {
2120        set origin(0) [lindex $values 0]
2121        set origin(1) [lindex $values 1]
2122    } elseif { $n == 3 } {
2123        origin set $values
2124    }
2125
2126    # Scale and translate points
2127    for { set i 0 } { $i < 8 } { incr i } {
2128        point${i} expr "(point${i} * scale) + origin"
2129    }
2130
2131    # Generate vertices as a string for PyMOL
2132    set vertices ""
2133    foreach n { 0 1 0 2 0 3 1 4 2 4 2 6 1 7 3 7 5 7 4 5 3 6 5 } {
2134        set values [point${n} range 0 end]
2135        append vertices "\[ [join $values {, }] \], \\\n"
2136    }
2137    set values [point6 range 0 end]
2138    append vertices "\[ [join $values {, }] \]  \\\n"
2139    blt::vector destroy point0 point1 point2 point3 point4 point5 point6 \
2140        point7 origin scale
2141    return $vertices
2142}
2143
2144
2145# ----------------------------------------------------------------------
2146# OPTION: -device
2147# ----------------------------------------------------------------------
2148itcl::configbody Rappture::MolvisViewer::device {
2149    if {$itk_option(-device) != "" } {
2150        if {![Rappture::library isvalid $itk_option(-device)]} {
2151            error "bad value \"$itk_option(-device)\": should be Rappture::library object"
2152        }
2153        $this delete
2154        $this add $itk_option(-device)
2155    } else {
2156        $this delete
2157    }
2158
2159    if { [isconnected] } {
2160        $_dispatcher event -idle !rebuild
2161    }
2162}
2163
Note: See TracBrowser for help on using the repository browser.