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

Last change on this file since 4045 was 3813, checked in by ldelgass, 11 years ago

Fix bug in 'add' method of viewer widgets: list search for existing dataobj
entry was wrong (list and pattern transposed), causing potential duplicate
entries in dataobj list.

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