[436] | 1 | # ---------------------------------------------------------------------- |
---|
| 2 | # COMPONENT: nanovisviewer - 3D volume rendering |
---|
| 3 | # |
---|
| 4 | # This widget performs volume rendering on 3D scalar/vector datasets. |
---|
| 5 | # It connects to the Nanovis server running on a rendering farm, |
---|
| 6 | # transmits data, and displays the results. |
---|
| 7 | # ====================================================================== |
---|
| 8 | # AUTHOR: Michael McLennan, Purdue University |
---|
| 9 | # Copyright (c) 2004-2005 Purdue Research Foundation |
---|
| 10 | # |
---|
| 11 | # See the file "license.terms" for information on usage and |
---|
| 12 | # redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
---|
| 13 | # ====================================================================== |
---|
| 14 | package require Itk |
---|
| 15 | package require BLT |
---|
| 16 | package require Img |
---|
| 17 | |
---|
| 18 | option add *NanovisViewer.width 4i widgetDefault |
---|
| 19 | option add *NanovisViewer.height 4i widgetDefault |
---|
| 20 | option add *NanovisViewer.foreground black widgetDefault |
---|
| 21 | option add *NanovisViewer.controlBackground gray widgetDefault |
---|
| 22 | option add *NanovisViewer.controlDarkBackground #999999 widgetDefault |
---|
| 23 | option add *NanovisViewer.plotBackground black widgetDefault |
---|
| 24 | option add *NanovisViewer.plotForeground white widgetDefault |
---|
| 25 | option add *NanovisViewer.plotOutline gray widgetDefault |
---|
| 26 | option add *NanovisViewer.font \ |
---|
[676] | 27 | -*-helvetica-medium-r-normal-*-12-* widgetDefault |
---|
[436] | 28 | |
---|
| 29 | itcl::class Rappture::NanovisViewer { |
---|
| 30 | inherit itk::Widget |
---|
| 31 | |
---|
| 32 | itk_option define -plotforeground plotForeground Foreground "" |
---|
| 33 | itk_option define -plotbackground plotBackground Background "" |
---|
| 34 | itk_option define -plotoutline plotOutline PlotOutline "" |
---|
[572] | 35 | itk_option define -sendcommand sendCommand SendCommand "" |
---|
| 36 | itk_option define -receivecommand receiveCommand ReceiveCommand "" |
---|
[436] | 37 | |
---|
[462] | 38 | constructor {hostlist args} { # defined below } |
---|
[436] | 39 | destructor { # defined below } |
---|
| 40 | |
---|
| 41 | public method add {dataobj {settings ""}} |
---|
[572] | 42 | public method get {args} |
---|
[436] | 43 | public method delete {args} |
---|
| 44 | public method scale {args} |
---|
[464] | 45 | public method download {option args} |
---|
[436] | 46 | |
---|
[462] | 47 | public method connect {{hostlist ""}} |
---|
[436] | 48 | public method disconnect {} |
---|
| 49 | public method isconnected {} |
---|
| 50 | |
---|
[447] | 51 | protected method _send {args} |
---|
[572] | 52 | protected method _send_text {string} |
---|
[447] | 53 | protected method _send_dataobjs {} |
---|
[572] | 54 | protected method _send_echo {channel {data ""}} |
---|
[436] | 55 | protected method _receive {} |
---|
| 56 | protected method _receive_image {option size} |
---|
[456] | 57 | protected method _receive_legend {ivol vmin vmax size} |
---|
[572] | 58 | protected method _receive_echo {channel {data ""}} |
---|
[436] | 59 | |
---|
| 60 | protected method _rebuild {} |
---|
[519] | 61 | protected method _currentVolumeIds {{what -all}} |
---|
[436] | 62 | protected method _zoom {option} |
---|
| 63 | protected method _move {option x y} |
---|
| 64 | protected method _slice {option args} |
---|
| 65 | protected method _slicertip {axis} |
---|
[459] | 66 | protected method _probe {option args} |
---|
| 67 | |
---|
[447] | 68 | protected method _state {comp} |
---|
| 69 | protected method _fixSettings {what {value ""}} |
---|
[456] | 70 | protected method _fixLegend {} |
---|
[459] | 71 | protected method _serverDown {} |
---|
| 72 | protected method _getTransfuncData {dataobj comp} |
---|
[436] | 73 | protected method _color2rgb {color} |
---|
| 74 | protected method _euler2xyz {theta phi psi} |
---|
| 75 | |
---|
[456] | 76 | private variable _dispatcher "" ;# dispatcher for !events |
---|
| 77 | |
---|
[462] | 78 | private variable _nvhosts "" ;# list of hosts for nanovis server |
---|
[436] | 79 | private variable _sid "" ;# socket connection to nanovis server |
---|
| 80 | private variable _parser "" ;# interpreter for incoming commands |
---|
[447] | 81 | private variable _buffer ;# buffer for incoming/outgoing commands |
---|
[456] | 82 | private variable _image ;# image displayed in plotting area |
---|
[447] | 83 | |
---|
[436] | 84 | private variable _dlist "" ;# list of data objects |
---|
| 85 | private variable _dims "" ;# dimensionality of data objects |
---|
[447] | 86 | private variable _obj2style ;# maps dataobj => style settings |
---|
[456] | 87 | private variable _obj2ovride ;# maps dataobj => style override |
---|
[447] | 88 | private variable _obj2id ;# maps dataobj => volume ID in server |
---|
| 89 | private variable _sendobjs "" ;# list of data objs to send to server |
---|
| 90 | |
---|
[436] | 91 | private variable _click ;# info used for _move operations |
---|
| 92 | private variable _limits ;# autoscale min/max for all axes |
---|
| 93 | private variable _view ;# view params for 3D view |
---|
| 94 | } |
---|
| 95 | |
---|
| 96 | itk::usual NanovisViewer { |
---|
| 97 | keep -background -foreground -cursor -font |
---|
| 98 | keep -plotbackground -plotforeground |
---|
| 99 | } |
---|
| 100 | |
---|
| 101 | # ---------------------------------------------------------------------- |
---|
| 102 | # CONSTRUCTOR |
---|
| 103 | # ---------------------------------------------------------------------- |
---|
[462] | 104 | itcl::body Rappture::NanovisViewer::constructor {hostlist args} { |
---|
[456] | 105 | Rappture::dispatcher _dispatcher |
---|
| 106 | $_dispatcher register !legend |
---|
| 107 | $_dispatcher dispatch $this !legend "[itcl::code $this _fixLegend]; list" |
---|
[459] | 108 | $_dispatcher register !serverDown |
---|
| 109 | $_dispatcher dispatch $this !serverDown "[itcl::code $this _serverDown]; list" |
---|
[456] | 110 | |
---|
[447] | 111 | set _buffer(in) "" |
---|
| 112 | set _buffer(out) "" |
---|
| 113 | |
---|
[436] | 114 | # |
---|
| 115 | # Create a parser to handle incoming requests |
---|
| 116 | # |
---|
| 117 | set _parser [interp create -safe] |
---|
| 118 | foreach cmd [$_parser eval {info commands}] { |
---|
| 119 | $_parser hide $cmd |
---|
| 120 | } |
---|
| 121 | $_parser alias image [itcl::code $this _receive_image] |
---|
[456] | 122 | $_parser alias legend [itcl::code $this _receive_legend] |
---|
[436] | 123 | |
---|
| 124 | # |
---|
| 125 | # Set up the widgets in the main body |
---|
| 126 | # |
---|
| 127 | option add hull.width hull.height |
---|
| 128 | pack propagate $itk_component(hull) no |
---|
| 129 | |
---|
| 130 | set _view(theta) 45 |
---|
| 131 | set _view(phi) 45 |
---|
| 132 | set _view(psi) 0 |
---|
| 133 | set _view(zoom) 1 |
---|
| 134 | set _view(xfocus) 0 |
---|
| 135 | set _view(yfocus) 0 |
---|
| 136 | set _view(zfocus) 0 |
---|
[447] | 137 | set _obj2id(count) 0 |
---|
[436] | 138 | |
---|
| 139 | itk_component add controls { |
---|
| 140 | frame $itk_interior.cntls |
---|
| 141 | } { |
---|
| 142 | usual |
---|
| 143 | rename -background -controlbackground controlBackground Background |
---|
| 144 | } |
---|
| 145 | pack $itk_component(controls) -side right -fill y |
---|
| 146 | |
---|
| 147 | itk_component add zoom { |
---|
| 148 | frame $itk_component(controls).zoom |
---|
| 149 | } { |
---|
| 150 | usual |
---|
| 151 | rename -background -controlbackground controlBackground Background |
---|
| 152 | } |
---|
| 153 | pack $itk_component(zoom) -side top |
---|
| 154 | |
---|
| 155 | itk_component add reset { |
---|
| 156 | button $itk_component(zoom).reset \ |
---|
| 157 | -borderwidth 1 -padx 1 -pady 1 \ |
---|
| 158 | -bitmap [Rappture::icon reset] \ |
---|
| 159 | -command [itcl::code $this _zoom reset] |
---|
| 160 | } { |
---|
| 161 | usual |
---|
| 162 | ignore -borderwidth |
---|
| 163 | rename -highlightbackground -controlbackground controlBackground Background |
---|
| 164 | } |
---|
[447] | 165 | pack $itk_component(reset) -side left -padx {4 1} -pady 4 |
---|
[436] | 166 | Rappture::Tooltip::for $itk_component(reset) "Reset the view to the default zoom level" |
---|
| 167 | |
---|
| 168 | itk_component add zoomin { |
---|
| 169 | button $itk_component(zoom).zin \ |
---|
| 170 | -borderwidth 1 -padx 1 -pady 1 \ |
---|
| 171 | -bitmap [Rappture::icon zoomin] \ |
---|
| 172 | -command [itcl::code $this _zoom in] |
---|
| 173 | } { |
---|
| 174 | usual |
---|
| 175 | ignore -borderwidth |
---|
| 176 | rename -highlightbackground -controlbackground controlBackground Background |
---|
| 177 | } |
---|
[447] | 178 | pack $itk_component(zoomin) -side left -padx 1 -pady 4 |
---|
[436] | 179 | Rappture::Tooltip::for $itk_component(zoomin) "Zoom in" |
---|
| 180 | |
---|
| 181 | itk_component add zoomout { |
---|
| 182 | button $itk_component(zoom).zout \ |
---|
| 183 | -borderwidth 1 -padx 1 -pady 1 \ |
---|
| 184 | -bitmap [Rappture::icon zoomout] \ |
---|
| 185 | -command [itcl::code $this _zoom out] |
---|
| 186 | } { |
---|
| 187 | usual |
---|
| 188 | ignore -borderwidth |
---|
| 189 | rename -highlightbackground -controlbackground controlBackground Background |
---|
| 190 | } |
---|
[447] | 191 | pack $itk_component(zoomout) -side left -padx {1 4} -pady 4 |
---|
[436] | 192 | Rappture::Tooltip::for $itk_component(zoomout) "Zoom out" |
---|
| 193 | |
---|
| 194 | # |
---|
| 195 | # Create slicer controls... |
---|
| 196 | # |
---|
| 197 | itk_component add slicers { |
---|
| 198 | frame $itk_component(controls).slicers |
---|
| 199 | } { |
---|
| 200 | usual |
---|
| 201 | rename -background -controlbackground controlBackground Background |
---|
| 202 | } |
---|
| 203 | pack $itk_component(slicers) -side bottom -padx 4 -pady 4 |
---|
| 204 | grid rowconfigure $itk_component(slicers) 1 -weight 1 |
---|
| 205 | # |
---|
| 206 | # X-value slicer... |
---|
| 207 | # |
---|
| 208 | itk_component add xslice { |
---|
| 209 | label $itk_component(slicers).xslice \ |
---|
| 210 | -borderwidth 1 -relief raised -padx 1 -pady 1 \ |
---|
| 211 | -bitmap [Rappture::icon x] |
---|
| 212 | } { |
---|
| 213 | usual |
---|
| 214 | ignore -borderwidth |
---|
| 215 | rename -highlightbackground -controlbackground controlBackground Background |
---|
| 216 | } |
---|
| 217 | bind $itk_component(xslice) <ButtonPress> \ |
---|
| 218 | [itcl::code $this _slice axis x toggle] |
---|
| 219 | Rappture::Tooltip::for $itk_component(xslice) \ |
---|
| 220 | "Toggle the X cut plane on/off" |
---|
| 221 | grid $itk_component(xslice) -row 1 -column 0 -sticky ew -padx 1 |
---|
| 222 | |
---|
| 223 | itk_component add xslicer { |
---|
| 224 | ::scale $itk_component(slicers).xval -from 100 -to 0 \ |
---|
| 225 | -width 10 -orient vertical -showvalue off \ |
---|
| 226 | -borderwidth 1 -highlightthickness 0 \ |
---|
| 227 | -command [itcl::code $this _slice move x] |
---|
| 228 | } { |
---|
| 229 | usual |
---|
| 230 | ignore -borderwidth |
---|
| 231 | ignore -highlightthickness |
---|
| 232 | rename -highlightbackground -controlbackground controlBackground Background |
---|
| 233 | rename -troughcolor -controldarkbackground controlDarkBackground Background |
---|
| 234 | } |
---|
| 235 | $itk_component(xslicer) set 50 |
---|
| 236 | $itk_component(xslicer) configure -state disabled |
---|
| 237 | grid $itk_component(xslicer) -row 2 -column 0 -padx 1 |
---|
| 238 | Rappture::Tooltip::for $itk_component(xslicer) \ |
---|
| 239 | "@[itcl::code $this _slicertip x]" |
---|
| 240 | |
---|
| 241 | # |
---|
| 242 | # Y-value slicer... |
---|
| 243 | # |
---|
| 244 | itk_component add yslice { |
---|
| 245 | label $itk_component(slicers).yslice \ |
---|
| 246 | -borderwidth 1 -relief raised -padx 1 -pady 1 \ |
---|
| 247 | -bitmap [Rappture::icon y] |
---|
| 248 | } { |
---|
| 249 | usual |
---|
| 250 | ignore -borderwidth |
---|
| 251 | rename -highlightbackground -controlbackground controlBackground Background |
---|
| 252 | } |
---|
| 253 | bind $itk_component(yslice) <ButtonPress> \ |
---|
| 254 | [itcl::code $this _slice axis y toggle] |
---|
| 255 | Rappture::Tooltip::for $itk_component(yslice) \ |
---|
| 256 | "Toggle the Y cut plane on/off" |
---|
| 257 | grid $itk_component(yslice) -row 1 -column 1 -sticky ew -padx 1 |
---|
| 258 | |
---|
| 259 | itk_component add yslicer { |
---|
| 260 | ::scale $itk_component(slicers).yval -from 100 -to 0 \ |
---|
| 261 | -width 10 -orient vertical -showvalue off \ |
---|
| 262 | -borderwidth 1 -highlightthickness 0 \ |
---|
| 263 | -command [itcl::code $this _slice move y] |
---|
| 264 | } { |
---|
| 265 | usual |
---|
| 266 | ignore -borderwidth |
---|
| 267 | ignore -highlightthickness |
---|
| 268 | rename -highlightbackground -controlbackground controlBackground Background |
---|
| 269 | rename -troughcolor -controldarkbackground controlDarkBackground Background |
---|
| 270 | } |
---|
| 271 | $itk_component(yslicer) set 50 |
---|
| 272 | $itk_component(yslicer) configure -state disabled |
---|
| 273 | grid $itk_component(yslicer) -row 2 -column 1 -padx 1 |
---|
| 274 | Rappture::Tooltip::for $itk_component(yslicer) \ |
---|
| 275 | "@[itcl::code $this _slicertip y]" |
---|
| 276 | |
---|
| 277 | # |
---|
| 278 | # Z-value slicer... |
---|
| 279 | # |
---|
| 280 | itk_component add zslice { |
---|
| 281 | label $itk_component(slicers).zslice \ |
---|
| 282 | -borderwidth 1 -relief raised -padx 1 -pady 1 \ |
---|
| 283 | -bitmap [Rappture::icon z] |
---|
| 284 | } { |
---|
| 285 | usual |
---|
| 286 | ignore -borderwidth |
---|
| 287 | rename -highlightbackground -controlbackground controlBackground Background |
---|
| 288 | } |
---|
| 289 | grid $itk_component(zslice) -row 1 -column 2 -sticky ew -padx 1 |
---|
| 290 | bind $itk_component(zslice) <ButtonPress> \ |
---|
| 291 | [itcl::code $this _slice axis z toggle] |
---|
| 292 | Rappture::Tooltip::for $itk_component(zslice) \ |
---|
| 293 | "Toggle the Z cut plane on/off" |
---|
| 294 | |
---|
| 295 | itk_component add zslicer { |
---|
| 296 | ::scale $itk_component(slicers).zval -from 100 -to 0 \ |
---|
| 297 | -width 10 -orient vertical -showvalue off \ |
---|
| 298 | -borderwidth 1 -highlightthickness 0 \ |
---|
| 299 | -command [itcl::code $this _slice move z] |
---|
| 300 | } { |
---|
| 301 | usual |
---|
| 302 | ignore -borderwidth |
---|
| 303 | ignore -highlightthickness |
---|
| 304 | rename -highlightbackground -controlbackground controlBackground Background |
---|
| 305 | rename -troughcolor -controldarkbackground controlDarkBackground Background |
---|
| 306 | } |
---|
| 307 | $itk_component(zslicer) set 50 |
---|
| 308 | $itk_component(zslicer) configure -state disabled |
---|
| 309 | grid $itk_component(zslicer) -row 2 -column 2 -padx 1 |
---|
| 310 | Rappture::Tooltip::for $itk_component(zslicer) \ |
---|
| 311 | "@[itcl::code $this _slicertip z]" |
---|
| 312 | |
---|
| 313 | # |
---|
[447] | 314 | # Volume toggle... |
---|
| 315 | # |
---|
| 316 | itk_component add volume { |
---|
| 317 | label $itk_component(slicers).volume \ |
---|
| 318 | -borderwidth 1 -relief sunken -padx 1 -pady 1 \ |
---|
| 319 | -text "Volume" |
---|
| 320 | } { |
---|
| 321 | usual |
---|
| 322 | ignore -borderwidth |
---|
| 323 | rename -highlightbackground -controlbackground controlBackground Background |
---|
| 324 | } |
---|
| 325 | bind $itk_component(volume) <ButtonPress> \ |
---|
| 326 | [itcl::code $this _slice volume toggle] |
---|
| 327 | Rappture::Tooltip::for $itk_component(volume) \ |
---|
| 328 | "Toggle the volume cloud on/off" |
---|
| 329 | grid $itk_component(volume) -row 0 -column 0 -columnspan 3 \ |
---|
| 330 | -sticky ew -padx 1 -pady 3 |
---|
| 331 | |
---|
| 332 | # |
---|
| 333 | # Settings panel... |
---|
| 334 | # |
---|
| 335 | itk_component add settings { |
---|
| 336 | button $itk_component(controls).settings -text "Settings..." \ |
---|
| 337 | -borderwidth 1 -relief flat -overrelief raised \ |
---|
| 338 | -padx 2 -pady 1 \ |
---|
| 339 | -command [list $itk_component(controls).panel activate $itk_component(controls).settings left] |
---|
| 340 | } { |
---|
| 341 | usual |
---|
| 342 | ignore -borderwidth |
---|
| 343 | rename -background -controlbackground controlBackground Background |
---|
| 344 | rename -highlightbackground -controlbackground controlBackground Background |
---|
| 345 | } |
---|
| 346 | pack $itk_component(settings) -side top -pady 8 |
---|
| 347 | |
---|
| 348 | Rappture::Balloon $itk_component(controls).panel -title "Settings" |
---|
| 349 | set inner [$itk_component(controls).panel component inner] |
---|
| 350 | frame $inner.scales |
---|
| 351 | pack $inner.scales -side top -fill x |
---|
| 352 | grid columnconfigure $inner.scales 1 -weight 1 |
---|
| 353 | set fg [option get $itk_component(hull) font Font] |
---|
| 354 | |
---|
| 355 | label $inner.scales.diml -text "Dim" -font $fg |
---|
| 356 | grid $inner.scales.diml -row 0 -column 0 -sticky e |
---|
| 357 | ::scale $inner.scales.light -from 0 -to 100 -orient horizontal \ |
---|
| 358 | -showvalue off -command [itcl::code $this _fixSettings light] |
---|
| 359 | grid $inner.scales.light -row 0 -column 1 -sticky ew |
---|
| 360 | label $inner.scales.brightl -text "Bright" -font $fg |
---|
| 361 | grid $inner.scales.brightl -row 0 -column 2 -sticky w |
---|
| 362 | $inner.scales.light set 40 |
---|
| 363 | |
---|
| 364 | label $inner.scales.fogl -text "Fog" -font $fg |
---|
| 365 | grid $inner.scales.fogl -row 1 -column 0 -sticky e |
---|
| 366 | ::scale $inner.scales.transp -from 0 -to 100 -orient horizontal \ |
---|
| 367 | -showvalue off -command [itcl::code $this _fixSettings transp] |
---|
| 368 | grid $inner.scales.transp -row 1 -column 1 -sticky ew |
---|
| 369 | label $inner.scales.plasticl -text "Plastic" -font $fg |
---|
| 370 | grid $inner.scales.plasticl -row 1 -column 2 -sticky w |
---|
| 371 | $inner.scales.transp set 50 |
---|
| 372 | |
---|
| 373 | # |
---|
[436] | 374 | # RENDERING AREA |
---|
| 375 | # |
---|
[459] | 376 | itk_component add area { |
---|
| 377 | frame $itk_interior.area |
---|
| 378 | } |
---|
| 379 | pack $itk_component(area) -expand yes -fill both |
---|
| 380 | |
---|
[456] | 381 | set _image(legend) [image create photo] |
---|
| 382 | itk_component add legend { |
---|
[459] | 383 | canvas $itk_component(area).legend -height 50 -highlightthickness 0 |
---|
[456] | 384 | } { |
---|
| 385 | usual |
---|
| 386 | ignore -highlightthickness |
---|
| 387 | rename -background -plotbackground plotBackground Background |
---|
| 388 | } |
---|
| 389 | pack $itk_component(legend) -side bottom -fill x |
---|
| 390 | bind $itk_component(legend) <Configure> \ |
---|
| 391 | [list $_dispatcher event -idle !legend] |
---|
| 392 | |
---|
| 393 | set _image(plot) [image create photo] |
---|
[459] | 394 | itk_component add 3dview { |
---|
| 395 | label $itk_component(area).vol -image $_image(plot) \ |
---|
| 396 | -highlightthickness 0 |
---|
[456] | 397 | } { |
---|
| 398 | usual |
---|
| 399 | ignore -highlightthickness |
---|
[459] | 400 | rename -background -plotbackground plotBackground Background |
---|
[436] | 401 | } |
---|
[459] | 402 | pack $itk_component(3dview) -expand yes -fill both |
---|
[436] | 403 | |
---|
| 404 | # set up bindings for rotation |
---|
[459] | 405 | bind $itk_component(3dview) <ButtonPress> \ |
---|
[436] | 406 | [itcl::code $this _move click %x %y] |
---|
[459] | 407 | bind $itk_component(3dview) <B1-Motion> \ |
---|
[436] | 408 | [itcl::code $this _move drag %x %y] |
---|
[459] | 409 | bind $itk_component(3dview) <ButtonRelease> \ |
---|
[436] | 410 | [itcl::code $this _move release %x %y] |
---|
[459] | 411 | bind $itk_component(3dview) <Configure> \ |
---|
[436] | 412 | [itcl::code $this _send screen %w %h] |
---|
| 413 | |
---|
[459] | 414 | set _image(download) [image create photo] |
---|
| 415 | |
---|
[436] | 416 | eval itk_initialize $args |
---|
| 417 | |
---|
[462] | 418 | connect $hostlist |
---|
[436] | 419 | } |
---|
| 420 | |
---|
| 421 | # ---------------------------------------------------------------------- |
---|
| 422 | # DESTRUCTOR |
---|
| 423 | # ---------------------------------------------------------------------- |
---|
| 424 | itcl::body Rappture::NanovisViewer::destructor {} { |
---|
[447] | 425 | set _sendobjs "" ;# stop any send in progress |
---|
| 426 | after cancel [itcl::code $this _send_dataobjs] |
---|
[436] | 427 | after cancel [itcl::code $this _rebuild] |
---|
[456] | 428 | image delete $_image(plot) |
---|
| 429 | image delete $_image(legend) |
---|
[459] | 430 | image delete $_image(download) |
---|
[436] | 431 | interp delete $_parser |
---|
| 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. |
---|
| 440 | # ---------------------------------------------------------------------- |
---|
| 441 | itcl::body Rappture::NanovisViewer::add {dataobj {settings ""}} { |
---|
| 442 | array set params { |
---|
| 443 | -color auto |
---|
| 444 | -width 1 |
---|
| 445 | -linestyle solid |
---|
| 446 | -brightness 0 |
---|
| 447 | -raise 0 |
---|
[464] | 448 | -description "" |
---|
[436] | 449 | } |
---|
| 450 | foreach {opt val} $settings { |
---|
| 451 | if {![info exists params($opt)]} { |
---|
| 452 | error "bad setting \"$opt\": should be [join [lsort [array names params]] {, }]" |
---|
| 453 | } |
---|
| 454 | set params($opt) $val |
---|
| 455 | } |
---|
| 456 | if {$params(-color) == "auto" || $params(-color) == "autoreset"} { |
---|
| 457 | # can't handle -autocolors yet |
---|
| 458 | set params(-color) black |
---|
| 459 | } |
---|
| 460 | |
---|
| 461 | set pos [lsearch -exact $dataobj $_dlist] |
---|
| 462 | if {$pos < 0} { |
---|
| 463 | lappend _dlist $dataobj |
---|
[456] | 464 | set _obj2ovride($dataobj-color) $params(-color) |
---|
| 465 | set _obj2ovride($dataobj-width) $params(-width) |
---|
| 466 | set _obj2ovride($dataobj-raise) $params(-raise) |
---|
[436] | 467 | |
---|
| 468 | after cancel [itcl::code $this _rebuild] |
---|
| 469 | after idle [itcl::code $this _rebuild] |
---|
| 470 | } |
---|
| 471 | } |
---|
| 472 | |
---|
| 473 | # ---------------------------------------------------------------------- |
---|
[572] | 474 | # USAGE: get ?-objects? |
---|
| 475 | # USAGE: get ?-image 3dview|legend? |
---|
[436] | 476 | # |
---|
| 477 | # Clients use this to query the list of objects being plotted, in |
---|
[572] | 478 | # order from bottom to top of this result. The optional "-image" |
---|
| 479 | # flag can also request the internal images being shown. |
---|
[436] | 480 | # ---------------------------------------------------------------------- |
---|
[572] | 481 | itcl::body Rappture::NanovisViewer::get {args} { |
---|
| 482 | if {[llength $args] == 0} { |
---|
| 483 | set args "-objects" |
---|
| 484 | } |
---|
| 485 | |
---|
| 486 | set op [lindex $args 0] |
---|
| 487 | switch -- $op { |
---|
| 488 | -objects { |
---|
| 489 | # put the dataobj list in order according to -raise options |
---|
| 490 | set dlist $_dlist |
---|
| 491 | foreach obj $dlist { |
---|
| 492 | if {[info exists _obj2ovride($obj-raise)] && $_obj2ovride($obj-raise)} { |
---|
| 493 | set i [lsearch -exact $dlist $obj] |
---|
| 494 | if {$i >= 0} { |
---|
| 495 | set dlist [lreplace $dlist $i $i] |
---|
| 496 | lappend dlist $obj |
---|
| 497 | } |
---|
[436] | 498 | } |
---|
| 499 | } |
---|
[572] | 500 | return $dlist |
---|
| 501 | } |
---|
| 502 | -image { |
---|
| 503 | if {[llength $args] != 2} { |
---|
| 504 | error "wrong # args: should be \"get -image 3dview|legend\"" |
---|
| 505 | } |
---|
| 506 | switch -- [lindex $args end] { |
---|
| 507 | 3dview { |
---|
| 508 | return $_image(plot) |
---|
| 509 | } |
---|
| 510 | legend { |
---|
| 511 | return $_image(legend) |
---|
| 512 | } |
---|
| 513 | default { |
---|
| 514 | error "bad image name \"[lindex $args end]\": should be 3dview or legend" |
---|
| 515 | } |
---|
| 516 | } |
---|
| 517 | } |
---|
| 518 | default { |
---|
| 519 | error "bad option \"$op\": should be -objects or -image" |
---|
| 520 | } |
---|
[436] | 521 | } |
---|
| 522 | } |
---|
| 523 | |
---|
| 524 | # ---------------------------------------------------------------------- |
---|
| 525 | # USAGE: delete ?<dataobj1> <dataobj2> ...? |
---|
| 526 | # |
---|
| 527 | # Clients use this to delete a dataobj from the plot. If no dataobjs |
---|
| 528 | # are specified, then all dataobjs are deleted. |
---|
| 529 | # ---------------------------------------------------------------------- |
---|
| 530 | itcl::body Rappture::NanovisViewer::delete {args} { |
---|
| 531 | if {[llength $args] == 0} { |
---|
| 532 | set args $_dlist |
---|
| 533 | } |
---|
| 534 | |
---|
| 535 | # delete all specified dataobjs |
---|
| 536 | set changed 0 |
---|
| 537 | foreach dataobj $args { |
---|
| 538 | set pos [lsearch -exact $_dlist $dataobj] |
---|
| 539 | if {$pos >= 0} { |
---|
| 540 | set _dlist [lreplace $_dlist $pos $pos] |
---|
[456] | 541 | foreach key [array names _obj2ovride $dataobj-*] { |
---|
| 542 | unset _obj2ovride($key) |
---|
[447] | 543 | } |
---|
[436] | 544 | set changed 1 |
---|
| 545 | } |
---|
| 546 | } |
---|
| 547 | |
---|
| 548 | # if anything changed, then rebuild the plot |
---|
| 549 | if {$changed} { |
---|
| 550 | after cancel [itcl::code $this _rebuild] |
---|
| 551 | after idle [itcl::code $this _rebuild] |
---|
| 552 | } |
---|
| 553 | } |
---|
| 554 | |
---|
| 555 | # ---------------------------------------------------------------------- |
---|
| 556 | # USAGE: scale ?<data1> <data2> ...? |
---|
| 557 | # |
---|
| 558 | # Sets the default limits for the overall plot according to the |
---|
| 559 | # limits of the data for all of the given <data> objects. This |
---|
| 560 | # accounts for all objects--even those not showing on the screen. |
---|
| 561 | # Because of this, the limits are appropriate for all objects as |
---|
| 562 | # the user scans through data in the ResultSet viewer. |
---|
| 563 | # ---------------------------------------------------------------------- |
---|
| 564 | itcl::body Rappture::NanovisViewer::scale {args} { |
---|
| 565 | foreach val {xmin xmax ymin ymax zmin zmax vmin vmax} { |
---|
| 566 | set _limits($val) "" |
---|
| 567 | } |
---|
| 568 | foreach obj $args { |
---|
| 569 | foreach axis {x y z v} { |
---|
| 570 | foreach {min max} [$obj limits $axis] break |
---|
| 571 | if {"" != $min && "" != $max} { |
---|
| 572 | if {"" == $_limits(${axis}min)} { |
---|
| 573 | set _limits(${axis}min) $min |
---|
| 574 | set _limits(${axis}max) $max |
---|
| 575 | } else { |
---|
| 576 | if {$min < $_limits(${axis}min)} { |
---|
| 577 | set _limits(${axis}min) $min |
---|
| 578 | } |
---|
| 579 | if {$max > $_limits(${axis}max)} { |
---|
| 580 | set _limits(${axis}max) $max |
---|
| 581 | } |
---|
| 582 | } |
---|
| 583 | } |
---|
| 584 | } |
---|
| 585 | } |
---|
| 586 | } |
---|
| 587 | |
---|
| 588 | # ---------------------------------------------------------------------- |
---|
| 589 | # USAGE: download coming |
---|
[464] | 590 | # USAGE: download controls <downloadCommand> |
---|
[436] | 591 | # USAGE: download now |
---|
| 592 | # |
---|
| 593 | # Clients use this method to create a downloadable representation |
---|
| 594 | # of the plot. Returns a list of the form {ext string}, where |
---|
| 595 | # "ext" is the file extension (indicating the type of data) and |
---|
| 596 | # "string" is the data itself. |
---|
| 597 | # ---------------------------------------------------------------------- |
---|
[464] | 598 | itcl::body Rappture::NanovisViewer::download {option args} { |
---|
[436] | 599 | switch $option { |
---|
| 600 | coming { |
---|
[459] | 601 | if {[catch {blt::winop snap $itk_component(area) $_image(download)}]} { |
---|
| 602 | $_image(download) configure -width 1 -height 1 |
---|
| 603 | $_image(download) put #000000 |
---|
| 604 | } |
---|
[436] | 605 | } |
---|
[464] | 606 | controls { |
---|
| 607 | # no controls for this download yet |
---|
| 608 | return "" |
---|
| 609 | } |
---|
[436] | 610 | now { |
---|
| 611 | # |
---|
| 612 | # Hack alert! Need data in binary format, |
---|
| 613 | # so we'll save to a file and read it back. |
---|
| 614 | # |
---|
| 615 | set tmpfile /tmp/image[pid].jpg |
---|
[459] | 616 | $_image(download) write $tmpfile -format jpeg |
---|
[436] | 617 | set fid [open $tmpfile r] |
---|
| 618 | fconfigure $fid -encoding binary -translation binary |
---|
| 619 | set bytes [read $fid] |
---|
| 620 | close $fid |
---|
| 621 | file delete -force $tmpfile |
---|
| 622 | |
---|
| 623 | return [list .jpg $bytes] |
---|
| 624 | } |
---|
| 625 | default { |
---|
[464] | 626 | error "bad option \"$option\": should be coming, controls, now" |
---|
[436] | 627 | } |
---|
| 628 | } |
---|
| 629 | } |
---|
| 630 | |
---|
| 631 | # ---------------------------------------------------------------------- |
---|
[462] | 632 | # USAGE: connect ?<host:port>,<host:port>...? |
---|
[436] | 633 | # |
---|
| 634 | # Clients use this method to establish a connection to a new |
---|
| 635 | # server, or to reestablish a connection to the previous server. |
---|
| 636 | # Any existing connection is automatically closed. |
---|
| 637 | # ---------------------------------------------------------------------- |
---|
[462] | 638 | itcl::body Rappture::NanovisViewer::connect {{hostlist ""}} { |
---|
[436] | 639 | disconnect |
---|
| 640 | |
---|
[462] | 641 | if {"" != $hostlist} { set _nvhosts $hostlist } |
---|
[436] | 642 | |
---|
[462] | 643 | if {"" == $_nvhosts} { |
---|
[436] | 644 | return 0 |
---|
| 645 | } |
---|
| 646 | |
---|
[459] | 647 | blt::busy hold $itk_component(hull); update idletasks |
---|
| 648 | |
---|
[436] | 649 | # HACK ALERT! punt on this for now |
---|
| 650 | set memorySize 10000 |
---|
| 651 | |
---|
| 652 | # |
---|
| 653 | # Connect to the nanovis server. Send the server some estimate |
---|
| 654 | # of the size of our job. If it's too busy, that server may |
---|
| 655 | # forward us to another. |
---|
| 656 | # |
---|
[462] | 657 | set try [split $_nvhosts ,] |
---|
| 658 | foreach {hostname port} [split [lindex $try 0] :] break |
---|
| 659 | set try [lrange $try 1 end] |
---|
| 660 | |
---|
[436] | 661 | while {1} { |
---|
[572] | 662 | _send_echo <<line "connecting to $hostname:$port..." |
---|
[462] | 663 | if {[catch {socket $hostname $port} sid]} { |
---|
| 664 | if {[llength $try] == 0} { |
---|
| 665 | return 0 |
---|
| 666 | } |
---|
| 667 | foreach {hostname port} [split [lindex $try 0] :] break |
---|
| 668 | set try [lrange $try 1 end] |
---|
| 669 | continue |
---|
[436] | 670 | } |
---|
| 671 | fconfigure $sid -translation binary -encoding binary |
---|
| 672 | |
---|
| 673 | # send memory requirement to the load balancer |
---|
| 674 | puts -nonewline $sid [binary format i $memorySize] |
---|
| 675 | flush $sid |
---|
| 676 | |
---|
| 677 | # read back a reconnection order |
---|
| 678 | set data [read $sid 4] |
---|
| 679 | if {[binary scan $data cccc b1 b2 b3 b4] != 4} { |
---|
| 680 | error "couldn't read redirection request" |
---|
| 681 | } |
---|
| 682 | set addr [format "%u.%u.%u.%u" \ |
---|
| 683 | [expr {$b1 & 0xff}] \ |
---|
| 684 | [expr {$b2 & 0xff}] \ |
---|
| 685 | [expr {$b3 & 0xff}] \ |
---|
| 686 | [expr {$b4 & 0xff}]] |
---|
[572] | 687 | _receive_echo <<line $addr |
---|
[436] | 688 | |
---|
| 689 | if {[string equal $addr "0.0.0.0"]} { |
---|
| 690 | fconfigure $sid -buffering line |
---|
| 691 | fileevent $sid readable [itcl::code $this _receive] |
---|
| 692 | set _sid $sid |
---|
[572] | 693 | blt::busy release $itk_component(hull) |
---|
[436] | 694 | return 1 |
---|
| 695 | } |
---|
| 696 | set hostname $addr |
---|
| 697 | } |
---|
[459] | 698 | blt::busy release $itk_component(hull) |
---|
| 699 | |
---|
[436] | 700 | return 0 |
---|
| 701 | } |
---|
| 702 | |
---|
| 703 | # ---------------------------------------------------------------------- |
---|
| 704 | # USAGE: disconnect |
---|
| 705 | # |
---|
| 706 | # Clients use this method to disconnect from the current rendering |
---|
| 707 | # server. |
---|
| 708 | # ---------------------------------------------------------------------- |
---|
| 709 | itcl::body Rappture::NanovisViewer::disconnect {} { |
---|
| 710 | if {"" != $_sid} { |
---|
| 711 | catch {close $_sid} |
---|
| 712 | set _sid "" |
---|
[459] | 713 | } |
---|
[447] | 714 | |
---|
[459] | 715 | set _buffer(in) "" |
---|
| 716 | set _buffer(out) "" |
---|
[447] | 717 | |
---|
[459] | 718 | # disconnected -- no more data sitting on server |
---|
| 719 | catch {unset _obj2id} |
---|
| 720 | set _obj2id(count) 0 |
---|
| 721 | set _sendobjs "" |
---|
[436] | 722 | } |
---|
| 723 | |
---|
| 724 | # ---------------------------------------------------------------------- |
---|
| 725 | # USAGE: isconnected |
---|
| 726 | # |
---|
| 727 | # Clients use this method to see if we are currently connected to |
---|
| 728 | # a server. |
---|
| 729 | # ---------------------------------------------------------------------- |
---|
| 730 | itcl::body Rappture::NanovisViewer::isconnected {} { |
---|
| 731 | return [expr {"" != $_sid}] |
---|
| 732 | } |
---|
| 733 | |
---|
| 734 | # ---------------------------------------------------------------------- |
---|
| 735 | # USAGE: _send <arg> <arg> ... |
---|
| 736 | # |
---|
| 737 | # Used internally to send commands off to the rendering server. |
---|
[572] | 738 | # This is a more convenient form of _send_text, which actually |
---|
| 739 | # does the sending. |
---|
[436] | 740 | # ---------------------------------------------------------------------- |
---|
| 741 | itcl::body Rappture::NanovisViewer::_send {args} { |
---|
[572] | 742 | _send_text $args |
---|
| 743 | } |
---|
| 744 | |
---|
| 745 | # ---------------------------------------------------------------------- |
---|
| 746 | # USAGE: _send_text <string> |
---|
| 747 | # |
---|
| 748 | # Used internally to send commands off to the rendering server. |
---|
| 749 | # ---------------------------------------------------------------------- |
---|
| 750 | itcl::body Rappture::NanovisViewer::_send_text {string} { |
---|
[436] | 751 | if {"" == $_sid} { |
---|
[459] | 752 | $_dispatcher cancel !serverDown |
---|
[436] | 753 | set x [expr {[winfo rootx $itk_component(area)]+10}] |
---|
| 754 | set y [expr {[winfo rooty $itk_component(area)]+10}] |
---|
| 755 | Rappture::Tooltip::cue @$x,$y "Connecting..." |
---|
| 756 | |
---|
[447] | 757 | if {[catch {connect} ok] == 0 && $ok} { |
---|
[459] | 758 | set w [winfo width $itk_component(3dview)] |
---|
| 759 | set h [winfo height $itk_component(3dview)] |
---|
[572] | 760 | |
---|
[447] | 761 | puts $_sid "screen $w $h" |
---|
[572] | 762 | _send_echo >>line "screen $w $h" |
---|
| 763 | |
---|
[447] | 764 | set _view(theta) 45 |
---|
| 765 | set _view(phi) 45 |
---|
| 766 | set _view(psi) 0 |
---|
| 767 | set _view(zoom) 1.0 |
---|
[459] | 768 | after idle [itcl::code $this _rebuild] |
---|
[436] | 769 | Rappture::Tooltip::cue hide |
---|
| 770 | return |
---|
| 771 | } |
---|
[459] | 772 | Rappture::Tooltip::cue @$x,$y "Can't connect to visualization server. This may be a network problem. Wait a few moments and try resetting the view." |
---|
| 773 | return |
---|
[436] | 774 | } |
---|
| 775 | if {"" != $_sid} { |
---|
[447] | 776 | # if we're transmitting objects, then buffer this command |
---|
| 777 | if {[llength $_sendobjs] > 0} { |
---|
[572] | 778 | append _buffer(out) $string "\n" |
---|
[447] | 779 | } else { |
---|
[572] | 780 | puts $_sid $string |
---|
| 781 | foreach line [split $string \n] { |
---|
| 782 | _send_echo >>line $line |
---|
| 783 | } |
---|
[447] | 784 | } |
---|
[436] | 785 | } |
---|
| 786 | } |
---|
| 787 | |
---|
| 788 | # ---------------------------------------------------------------------- |
---|
[447] | 789 | # USAGE: _send_dataobjs |
---|
| 790 | # |
---|
| 791 | # Used internally to send a series of volume objects off to the |
---|
| 792 | # server. Sends each object, a little at a time, with updates in |
---|
| 793 | # between so the interface doesn't lock up. |
---|
| 794 | # ---------------------------------------------------------------------- |
---|
| 795 | itcl::body Rappture::NanovisViewer::_send_dataobjs {} { |
---|
[459] | 796 | blt::busy hold $itk_component(hull); update idletasks |
---|
| 797 | |
---|
[447] | 798 | foreach dataobj $_sendobjs { |
---|
| 799 | foreach comp [$dataobj components] { |
---|
| 800 | # send the data as one huge base64-encoded mess -- yuck! |
---|
| 801 | set data [$dataobj values $comp] |
---|
| 802 | |
---|
| 803 | # tell the engine to expect some data |
---|
| 804 | set cmdstr "volume data follows [string length $data]" |
---|
[572] | 805 | _send_echo >>line $cmdstr |
---|
[447] | 806 | if {[catch {puts $_sid $cmdstr} err]} { |
---|
| 807 | disconnect |
---|
[459] | 808 | $_dispatcher event -after 750 !serverDown |
---|
[447] | 809 | return |
---|
| 810 | } |
---|
| 811 | |
---|
| 812 | while {[string length $data] > 0} { |
---|
| 813 | update |
---|
| 814 | |
---|
| 815 | set chunk [string range $data 0 8095] |
---|
| 816 | set data [string range $data 8096 end] |
---|
| 817 | |
---|
[572] | 818 | _send_echo >>line $chunk |
---|
[447] | 819 | if {[catch {puts -nonewline $_sid $chunk} err]} { |
---|
| 820 | disconnect |
---|
[459] | 821 | $_dispatcher event -after 750 !serverDown |
---|
[447] | 822 | return |
---|
| 823 | } |
---|
| 824 | catch {flush $_sid} |
---|
| 825 | } |
---|
[572] | 826 | _send_echo >>line "" |
---|
[447] | 827 | puts $_sid "" |
---|
| 828 | |
---|
| 829 | set _obj2id($dataobj-$comp) $_obj2id(count) |
---|
| 830 | incr _obj2id(count) |
---|
[456] | 831 | |
---|
| 832 | # |
---|
| 833 | # Determine the transfer function needed for this volume |
---|
| 834 | # and make sure that it's defined on the server. |
---|
| 835 | # |
---|
[459] | 836 | foreach {sname cmap wmap} [_getTransfuncData $dataobj $comp] break |
---|
[456] | 837 | |
---|
| 838 | set cmdstr [list transfunc define $sname $cmap $wmap] |
---|
[572] | 839 | _send_echo >>line $cmdstr |
---|
[456] | 840 | if {[catch {puts $_sid $cmdstr} err]} { |
---|
| 841 | disconnect |
---|
[459] | 842 | $_dispatcher event -after 750 !serverDown |
---|
[456] | 843 | return |
---|
| 844 | } |
---|
| 845 | |
---|
| 846 | set _obj2style($dataobj-$comp) $sname |
---|
[447] | 847 | } |
---|
| 848 | } |
---|
| 849 | set _sendobjs "" |
---|
[459] | 850 | blt::busy release $itk_component(hull) |
---|
[447] | 851 | |
---|
| 852 | # activate the proper volume |
---|
| 853 | set first [lindex [get] 0] |
---|
[468] | 854 | if {"" != $first} { |
---|
| 855 | set axis [$first hints updir] |
---|
| 856 | if {"" != $axis} { |
---|
| 857 | _send up $axis |
---|
| 858 | } |
---|
[459] | 859 | } |
---|
| 860 | |
---|
[447] | 861 | foreach key [array names _obj2id *-*] { |
---|
| 862 | set state [string match $first-* $key] |
---|
| 863 | _send volume state $state $_obj2id($key) |
---|
[456] | 864 | if {[info exists _obj2style($key)]} { |
---|
| 865 | _send volume shading transfunc $_obj2style($key) $_obj2id($key) |
---|
| 866 | } |
---|
[447] | 867 | } |
---|
| 868 | |
---|
| 869 | # sync the state of slicers |
---|
[519] | 870 | set vols [_currentVolumeIds -cutplanes] |
---|
[447] | 871 | foreach axis {x y z} { |
---|
[465] | 872 | eval _send cutplane state [_state ${axis}slice] $axis $vols |
---|
| 873 | set pos [expr {0.01*[$itk_component(${axis}slicer) get]}] |
---|
| 874 | eval _send cutplane position $pos $axis $vols |
---|
[447] | 875 | } |
---|
[465] | 876 | eval _send volume data state [_state volume] $vols |
---|
[447] | 877 | |
---|
| 878 | # if there are any commands in the buffer, send them now that we're done |
---|
[572] | 879 | _send_echo >>line $_buffer(out) |
---|
[447] | 880 | if {[catch {puts $_sid $_buffer(out)} err]} { |
---|
| 881 | disconnect |
---|
[459] | 882 | $_dispatcher event -after 750 !serverDown |
---|
[447] | 883 | } |
---|
| 884 | set _buffer(out) "" |
---|
[456] | 885 | |
---|
| 886 | $_dispatcher event -idle !legend |
---|
[447] | 887 | } |
---|
| 888 | |
---|
| 889 | # ---------------------------------------------------------------------- |
---|
[572] | 890 | # USAGE: _send_echo <channel> ?<data>? |
---|
| 891 | # |
---|
| 892 | # Used internally to echo sent data to clients interested in |
---|
| 893 | # this widget. If the -sendcommand option is set, then it is |
---|
| 894 | # invoked in the global scope with the <channel> and <data> values |
---|
| 895 | # as arguments. Otherwise, this does nothing. |
---|
| 896 | # ---------------------------------------------------------------------- |
---|
| 897 | itcl::body Rappture::NanovisViewer::_send_echo {channel {data ""}} { |
---|
| 898 | if {[string length $itk_option(-sendcommand)] > 0} { |
---|
| 899 | uplevel #0 $itk_option(-sendcommand) [list $channel $data] |
---|
| 900 | } |
---|
| 901 | } |
---|
| 902 | |
---|
| 903 | # ---------------------------------------------------------------------- |
---|
[436] | 904 | # USAGE: _receive |
---|
| 905 | # |
---|
| 906 | # Invoked automatically whenever a command is received from the |
---|
| 907 | # rendering server. Reads the incoming command and executes it in |
---|
| 908 | # a safe interpreter to handle the action. |
---|
| 909 | # ---------------------------------------------------------------------- |
---|
| 910 | itcl::body Rappture::NanovisViewer::_receive {} { |
---|
| 911 | if {"" != $_sid} { |
---|
| 912 | if {[gets $_sid line] < 0} { |
---|
| 913 | disconnect |
---|
[572] | 914 | _receive_echo closed |
---|
[459] | 915 | $_dispatcher event -after 750 !serverDown |
---|
[436] | 916 | } elseif {[string equal [string range $line 0 2] "nv>"]} { |
---|
[572] | 917 | _receive_echo <<line $line |
---|
[447] | 918 | append _buffer(in) [string range $line 3 end] |
---|
| 919 | if {[info complete $_buffer(in)]} { |
---|
| 920 | set request $_buffer(in) |
---|
| 921 | set _buffer(in) "" |
---|
[436] | 922 | $_parser eval $request |
---|
| 923 | } |
---|
| 924 | } else { |
---|
[459] | 925 | # this shows errors coming back from the engine |
---|
[572] | 926 | _receive_echo <<error $line |
---|
| 927 | puts stderr $line |
---|
[436] | 928 | } |
---|
| 929 | } |
---|
| 930 | } |
---|
| 931 | |
---|
| 932 | # ---------------------------------------------------------------------- |
---|
| 933 | # USAGE: _receive_image -bytes <size> |
---|
| 934 | # |
---|
| 935 | # Invoked automatically whenever the "image" command comes in from |
---|
| 936 | # the rendering server. Indicates that binary image data with the |
---|
| 937 | # specified <size> will follow. |
---|
| 938 | # ---------------------------------------------------------------------- |
---|
| 939 | itcl::body Rappture::NanovisViewer::_receive_image {option size} { |
---|
| 940 | if {"" != $_sid} { |
---|
| 941 | set bytes [read $_sid $size] |
---|
[456] | 942 | $_image(plot) configure -data $bytes |
---|
[572] | 943 | _receive_echo <<line "<read $size bytes for [image width $_image(plot)]x[image height $_image(plot)] image>" |
---|
[436] | 944 | } |
---|
| 945 | } |
---|
| 946 | |
---|
| 947 | # ---------------------------------------------------------------------- |
---|
[456] | 948 | # USAGE: _receive_legend <volume> <vmin> <vmax> <size> |
---|
| 949 | # |
---|
| 950 | # Invoked automatically whenever the "legend" command comes in from |
---|
| 951 | # the rendering server. Indicates that binary image data with the |
---|
| 952 | # specified <size> will follow. |
---|
| 953 | # ---------------------------------------------------------------------- |
---|
| 954 | itcl::body Rappture::NanovisViewer::_receive_legend {ivol vmin vmax size} { |
---|
| 955 | if {"" != $_sid} { |
---|
| 956 | set bytes [read $_sid $size] |
---|
| 957 | $_image(legend) configure -data $bytes |
---|
[572] | 958 | _receive_echo <<line "<read $size bytes for [image width $_image(legend)]x[image height $_image(legend)] legend>" |
---|
[456] | 959 | |
---|
| 960 | set c $itk_component(legend) |
---|
| 961 | set w [winfo width $c] |
---|
| 962 | set h [winfo height $c] |
---|
| 963 | if {"" == [$c find withtag transfunc]} { |
---|
[459] | 964 | $c create image 10 10 -anchor nw \ |
---|
[456] | 965 | -image $_image(legend) -tags transfunc |
---|
[459] | 966 | |
---|
| 967 | $c bind transfunc <ButtonPress-1> \ |
---|
| 968 | [itcl::code $this _probe start %x %y] |
---|
| 969 | $c bind transfunc <B1-Motion> \ |
---|
| 970 | [itcl::code $this _probe update %x %y] |
---|
| 971 | $c bind transfunc <ButtonRelease-1> \ |
---|
| 972 | [itcl::code $this _probe end %x %y] |
---|
| 973 | |
---|
| 974 | $c create text 10 [expr {$h-8}] -anchor sw \ |
---|
| 975 | -fill $itk_option(-plotforeground) -tags vmin |
---|
| 976 | $c create text [expr {$w-10}] [expr {$h-8}] -anchor se \ |
---|
| 977 | -fill $itk_option(-plotforeground) -tags vmax |
---|
[456] | 978 | } |
---|
[459] | 979 | |
---|
| 980 | $c itemconfigure vmin -text $vmin |
---|
| 981 | $c coords vmin 10 [expr {$h-8}] |
---|
| 982 | |
---|
| 983 | $c itemconfigure vmax -text $vmax |
---|
| 984 | $c coords vmax [expr {$w-10}] [expr {$h-8}] |
---|
[456] | 985 | } |
---|
| 986 | } |
---|
| 987 | |
---|
| 988 | # ---------------------------------------------------------------------- |
---|
[572] | 989 | # USAGE: _receive_echo <channel> ?<data>? |
---|
| 990 | # |
---|
| 991 | # Used internally to echo received data to clients interested in |
---|
| 992 | # this widget. If the -receivecommand option is set, then it is |
---|
| 993 | # invoked in the global scope with the <channel> and <data> values |
---|
| 994 | # as arguments. Otherwise, this does nothing. |
---|
| 995 | # ---------------------------------------------------------------------- |
---|
| 996 | itcl::body Rappture::NanovisViewer::_receive_echo {channel {data ""}} { |
---|
| 997 | if {[string length $itk_option(-receivecommand)] > 0} { |
---|
| 998 | uplevel #0 $itk_option(-receivecommand) [list $channel $data] |
---|
| 999 | } |
---|
| 1000 | } |
---|
| 1001 | |
---|
| 1002 | # ---------------------------------------------------------------------- |
---|
[436] | 1003 | # USAGE: _rebuild |
---|
| 1004 | # |
---|
| 1005 | # Called automatically whenever something changes that affects the |
---|
| 1006 | # data in the widget. Clears any existing data and rebuilds the |
---|
| 1007 | # widget to display new data. |
---|
| 1008 | # ---------------------------------------------------------------------- |
---|
| 1009 | itcl::body Rappture::NanovisViewer::_rebuild {} { |
---|
[447] | 1010 | # in the midst of sending data? then bail out |
---|
| 1011 | if {[llength $_sendobjs] > 0} { |
---|
| 1012 | return |
---|
| 1013 | } |
---|
| 1014 | |
---|
| 1015 | # |
---|
| 1016 | # Find any new data that needs to be sent to the server. |
---|
| 1017 | # Queue this up on the _sendobjs list, and send it out |
---|
| 1018 | # a little at a time. Do this first, before we rebuild |
---|
| 1019 | # the rest. |
---|
| 1020 | # |
---|
| 1021 | foreach dataobj [get] { |
---|
| 1022 | set comp [lindex [$dataobj components] 0] |
---|
| 1023 | if {![info exists _obj2id($dataobj-$comp)]} { |
---|
| 1024 | set i [lsearch -exact $_sendobjs $dataobj] |
---|
| 1025 | if {$i < 0} { |
---|
| 1026 | lappend _sendobjs $dataobj |
---|
| 1027 | } |
---|
| 1028 | } |
---|
| 1029 | } |
---|
| 1030 | if {[llength $_sendobjs] > 0} { |
---|
| 1031 | # send off new data objects |
---|
| 1032 | after idle [itcl::code $this _send_dataobjs] |
---|
| 1033 | } else { |
---|
| 1034 | # nothing to send -- activate the proper volume |
---|
| 1035 | set first [lindex [get] 0] |
---|
[468] | 1036 | if {"" != $first} { |
---|
| 1037 | set axis [$first hints updir] |
---|
| 1038 | if {"" != $axis} { |
---|
| 1039 | _send up $axis |
---|
| 1040 | } |
---|
[459] | 1041 | } |
---|
[447] | 1042 | foreach key [array names _obj2id *-*] { |
---|
| 1043 | set state [string match $first-* $key] |
---|
| 1044 | _send volume state $state $_obj2id($key) |
---|
[456] | 1045 | if {[info exists _obj2style($key)]} { |
---|
| 1046 | _send volume shading transfunc $_obj2style($key) $_obj2id($key) |
---|
| 1047 | } |
---|
[447] | 1048 | } |
---|
| 1049 | |
---|
| 1050 | # sync the state of slicers |
---|
[519] | 1051 | set vols [_currentVolumeIds -cutplanes] |
---|
[447] | 1052 | foreach axis {x y z} { |
---|
[465] | 1053 | eval _send cutplane state [_state ${axis}slice] $axis $vols |
---|
| 1054 | set pos [expr {0.01*[$itk_component(${axis}slicer) get]}] |
---|
| 1055 | eval _send cutplane position $pos $axis $vols |
---|
[447] | 1056 | } |
---|
[465] | 1057 | eval _send volume data state [_state volume] $vols |
---|
[456] | 1058 | $_dispatcher event -idle !legend |
---|
[447] | 1059 | } |
---|
| 1060 | |
---|
| 1061 | # |
---|
| 1062 | # Reset the camera and other view parameters |
---|
| 1063 | # |
---|
[436] | 1064 | eval _send camera angle [_euler2xyz $_view(theta) $_view(phi) $_view(psi)] |
---|
| 1065 | _send camera zoom $_view(zoom) |
---|
[447] | 1066 | #_fixSettings light |
---|
| 1067 | #_fixSettings transp |
---|
[436] | 1068 | |
---|
| 1069 | if {"" == $itk_option(-plotoutline)} { |
---|
| 1070 | _send volume outline state off |
---|
| 1071 | } else { |
---|
| 1072 | _send volume outline state on |
---|
| 1073 | _send volume outline color [_color2rgb $itk_option(-plotoutline)] |
---|
| 1074 | } |
---|
[459] | 1075 | _send volume axis label x "" |
---|
| 1076 | _send volume axis label y "" |
---|
| 1077 | _send volume axis label z "" |
---|
[436] | 1078 | } |
---|
| 1079 | |
---|
| 1080 | # ---------------------------------------------------------------------- |
---|
[519] | 1081 | # USAGE: _currentVolumeIds ?-cutplanes? |
---|
[447] | 1082 | # |
---|
| 1083 | # Returns a list of volume server IDs for the current volume being |
---|
| 1084 | # displayed. This is normally a single ID, but it might be a list |
---|
| 1085 | # of IDs if the current data object has multiple components. |
---|
| 1086 | # ---------------------------------------------------------------------- |
---|
[519] | 1087 | itcl::body Rappture::NanovisViewer::_currentVolumeIds {{what -all}} { |
---|
[447] | 1088 | set rlist "" |
---|
| 1089 | |
---|
| 1090 | set first [lindex [get] 0] |
---|
| 1091 | foreach key [array names _obj2id *-*] { |
---|
[456] | 1092 | if {[string match $first-* $key]} { |
---|
[519] | 1093 | array set style { |
---|
| 1094 | -cutplanes 1 |
---|
| 1095 | } |
---|
| 1096 | foreach {dataobj comp} [split $key -] break |
---|
| 1097 | array set style [lindex [$dataobj components -style $comp] 0] |
---|
| 1098 | |
---|
| 1099 | if {$what != "-cutplanes" || $style(-cutplanes)} { |
---|
| 1100 | lappend rlist $_obj2id($key) |
---|
| 1101 | } |
---|
[456] | 1102 | } |
---|
[447] | 1103 | } |
---|
| 1104 | return $rlist |
---|
| 1105 | } |
---|
| 1106 | |
---|
| 1107 | # ---------------------------------------------------------------------- |
---|
[436] | 1108 | # USAGE: _zoom in |
---|
| 1109 | # USAGE: _zoom out |
---|
| 1110 | # USAGE: _zoom reset |
---|
| 1111 | # |
---|
| 1112 | # Called automatically when the user clicks on one of the zoom |
---|
| 1113 | # controls for this widget. Changes the zoom for the current view. |
---|
| 1114 | # ---------------------------------------------------------------------- |
---|
| 1115 | itcl::body Rappture::NanovisViewer::_zoom {option} { |
---|
| 1116 | switch -- $option { |
---|
| 1117 | in { |
---|
| 1118 | set _view(zoom) [expr {$_view(zoom)*1.25}] |
---|
| 1119 | _send camera zoom $_view(zoom) |
---|
| 1120 | } |
---|
| 1121 | out { |
---|
| 1122 | set _view(zoom) [expr {$_view(zoom)*0.8}] |
---|
| 1123 | _send camera zoom $_view(zoom) |
---|
| 1124 | } |
---|
| 1125 | reset { |
---|
| 1126 | set _view(theta) 45 |
---|
| 1127 | set _view(phi) 45 |
---|
| 1128 | set _view(psi) 0 |
---|
| 1129 | set _view(zoom) 1.0 |
---|
| 1130 | eval _send camera angle [_euler2xyz $_view(theta) $_view(phi) $_view(psi)] |
---|
| 1131 | _send camera zoom $_view(zoom) |
---|
| 1132 | } |
---|
| 1133 | } |
---|
| 1134 | } |
---|
| 1135 | |
---|
| 1136 | # ---------------------------------------------------------------------- |
---|
| 1137 | # USAGE: _move click <x> <y> |
---|
| 1138 | # USAGE: _move drag <x> <y> |
---|
| 1139 | # USAGE: _move release <x> <y> |
---|
| 1140 | # |
---|
| 1141 | # Called automatically when the user clicks/drags/releases in the |
---|
| 1142 | # plot area. Moves the plot according to the user's actions. |
---|
| 1143 | # ---------------------------------------------------------------------- |
---|
| 1144 | itcl::body Rappture::NanovisViewer::_move {option x y} { |
---|
| 1145 | switch -- $option { |
---|
| 1146 | click { |
---|
[459] | 1147 | $itk_component(3dview) configure -cursor fleur |
---|
[436] | 1148 | set _click(x) $x |
---|
| 1149 | set _click(y) $y |
---|
| 1150 | set _click(theta) $_view(theta) |
---|
| 1151 | set _click(phi) $_view(phi) |
---|
| 1152 | } |
---|
| 1153 | drag { |
---|
| 1154 | if {[array size _click] == 0} { |
---|
| 1155 | _move click $x $y |
---|
| 1156 | } else { |
---|
[459] | 1157 | set w [winfo width $itk_component(3dview)] |
---|
| 1158 | set h [winfo height $itk_component(3dview)] |
---|
[436] | 1159 | if {$w <= 0 || $h <= 0} { |
---|
| 1160 | return |
---|
| 1161 | } |
---|
| 1162 | |
---|
| 1163 | if {[catch { |
---|
| 1164 | # this fails sometimes for no apparent reason |
---|
| 1165 | set dx [expr {double($x-$_click(x))/$w}] |
---|
| 1166 | set dy [expr {double($y-$_click(y))/$h}] |
---|
| 1167 | }]} { |
---|
| 1168 | return |
---|
| 1169 | } |
---|
| 1170 | |
---|
| 1171 | # |
---|
| 1172 | # Rotate the camera in 3D |
---|
| 1173 | # |
---|
| 1174 | if {$_view(psi) > 90 || $_view(psi) < -90} { |
---|
| 1175 | # when psi is flipped around, theta moves backwards |
---|
| 1176 | set dy [expr {-$dy}] |
---|
| 1177 | } |
---|
| 1178 | set theta [expr {$_view(theta) - $dy*180}] |
---|
| 1179 | while {$theta < 0} { set theta [expr {$theta+180}] } |
---|
| 1180 | while {$theta > 180} { set theta [expr {$theta-180}] } |
---|
| 1181 | |
---|
[447] | 1182 | if {abs($theta) >= 30 && abs($theta) <= 160} { |
---|
[436] | 1183 | set phi [expr {$_view(phi) - $dx*360}] |
---|
| 1184 | while {$phi < 0} { set phi [expr {$phi+360}] } |
---|
| 1185 | while {$phi > 360} { set phi [expr {$phi-360}] } |
---|
| 1186 | set psi $_view(psi) |
---|
| 1187 | } else { |
---|
| 1188 | set phi $_view(phi) |
---|
| 1189 | set psi [expr {$_view(psi) - $dx*360}] |
---|
| 1190 | while {$psi < -180} { set psi [expr {$psi+360}] } |
---|
| 1191 | while {$psi > 180} { set psi [expr {$psi-360}] } |
---|
| 1192 | } |
---|
| 1193 | |
---|
| 1194 | set _view(theta) $theta |
---|
| 1195 | set _view(phi) $phi |
---|
| 1196 | set _view(psi) $psi |
---|
| 1197 | eval _send camera angle [_euler2xyz $_view(theta) $_view(phi) $_view(psi)] |
---|
| 1198 | |
---|
| 1199 | set _click(x) $x |
---|
| 1200 | set _click(y) $y |
---|
| 1201 | } |
---|
| 1202 | } |
---|
| 1203 | release { |
---|
| 1204 | _move drag $x $y |
---|
[459] | 1205 | $itk_component(3dview) configure -cursor "" |
---|
[436] | 1206 | catch {unset _click} |
---|
| 1207 | } |
---|
| 1208 | default { |
---|
| 1209 | error "bad option \"$option\": should be click, drag, release" |
---|
| 1210 | } |
---|
| 1211 | } |
---|
| 1212 | } |
---|
| 1213 | |
---|
| 1214 | # ---------------------------------------------------------------------- |
---|
| 1215 | # USAGE: _slice axis x|y|z ?on|off|toggle? |
---|
| 1216 | # USAGE: _slice move x|y|z <newval> |
---|
| 1217 | # USAGE: _slice volume ?on|off|toggle? |
---|
| 1218 | # |
---|
| 1219 | # Called automatically when the user drags the slider to move the |
---|
| 1220 | # cut plane that slices 3D data. Gets the current value from the |
---|
| 1221 | # slider and moves the cut plane to the appropriate point in the |
---|
| 1222 | # data set. |
---|
| 1223 | # ---------------------------------------------------------------------- |
---|
| 1224 | itcl::body Rappture::NanovisViewer::_slice {option args} { |
---|
| 1225 | switch -- $option { |
---|
| 1226 | axis { |
---|
| 1227 | if {[llength $args] < 1 || [llength $args] > 2} { |
---|
| 1228 | error "wrong # args: should be \"_slice axis x|y|z ?on|off|toggle?\"" |
---|
| 1229 | } |
---|
| 1230 | set axis [lindex $args 0] |
---|
| 1231 | set op [lindex $args 1] |
---|
| 1232 | if {$op == ""} { set op "on" } |
---|
| 1233 | |
---|
[447] | 1234 | set current [_state ${axis}slice] |
---|
[436] | 1235 | if {$op == "toggle"} { |
---|
| 1236 | if {$current == "on"} { set op "off" } else { set op "on" } |
---|
| 1237 | } |
---|
| 1238 | |
---|
| 1239 | if {$op} { |
---|
| 1240 | $itk_component(${axis}slicer) configure -state normal |
---|
[519] | 1241 | eval _send cutplane state on $axis [_currentVolumeIds -cutplanes] |
---|
[436] | 1242 | $itk_component(${axis}slice) configure -relief sunken |
---|
| 1243 | } else { |
---|
| 1244 | $itk_component(${axis}slicer) configure -state disabled |
---|
[519] | 1245 | eval _send cutplane state off $axis [_currentVolumeIds -cutplanes] |
---|
[436] | 1246 | $itk_component(${axis}slice) configure -relief raised |
---|
| 1247 | } |
---|
| 1248 | } |
---|
| 1249 | move { |
---|
| 1250 | if {[llength $args] != 2} { |
---|
| 1251 | error "wrong # args: should be \"_slice move x|y|z newval\"" |
---|
| 1252 | } |
---|
| 1253 | set axis [lindex $args 0] |
---|
| 1254 | set newval [lindex $args 1] |
---|
| 1255 | |
---|
| 1256 | set newpos [expr {0.01*$newval}] |
---|
[447] | 1257 | # set newval [expr {0.01*($newval-50) |
---|
| 1258 | # *($_limits(${axis}max)-$_limits(${axis}min)) |
---|
| 1259 | # + 0.5*($_limits(${axis}max)+$_limits(${axis}min))}] |
---|
[436] | 1260 | |
---|
| 1261 | # show the current value in the readout |
---|
[447] | 1262 | #puts "readout: $axis = $newval" |
---|
[436] | 1263 | |
---|
[519] | 1264 | eval _send cutplane position $newpos $axis [_currentVolumeIds -cutplanes] |
---|
[436] | 1265 | } |
---|
| 1266 | volume { |
---|
| 1267 | if {[llength $args] > 1} { |
---|
| 1268 | error "wrong # args: should be \"_slice volume ?on|off|toggle?\"" |
---|
| 1269 | } |
---|
| 1270 | set op [lindex $args 0] |
---|
| 1271 | if {$op == ""} { set op "on" } |
---|
| 1272 | |
---|
[447] | 1273 | set current [_state volume] |
---|
[436] | 1274 | if {$op == "toggle"} { |
---|
| 1275 | if {$current == "on"} { set op "off" } else { set op "on" } |
---|
| 1276 | } |
---|
| 1277 | |
---|
| 1278 | if {$op} { |
---|
[447] | 1279 | eval _send volume data state on [_currentVolumeIds] |
---|
[436] | 1280 | $itk_component(volume) configure -relief sunken |
---|
| 1281 | } else { |
---|
[447] | 1282 | eval _send volume data state off [_currentVolumeIds] |
---|
[436] | 1283 | $itk_component(volume) configure -relief raised |
---|
| 1284 | } |
---|
| 1285 | } |
---|
| 1286 | default { |
---|
| 1287 | error "bad option \"$option\": should be axis, move, or volume" |
---|
| 1288 | } |
---|
| 1289 | } |
---|
| 1290 | } |
---|
| 1291 | |
---|
| 1292 | # ---------------------------------------------------------------------- |
---|
| 1293 | # USAGE: _slicertip <axis> |
---|
| 1294 | # |
---|
| 1295 | # Used internally to generate a tooltip for the x/y/z slicer controls. |
---|
| 1296 | # Returns a message that includes the current slicer value. |
---|
| 1297 | # ---------------------------------------------------------------------- |
---|
| 1298 | itcl::body Rappture::NanovisViewer::_slicertip {axis} { |
---|
| 1299 | set val [$itk_component(${axis}slicer) get] |
---|
[447] | 1300 | # set val [expr {0.01*($val-50) |
---|
| 1301 | # *($_limits(${axis}max)-$_limits(${axis}min)) |
---|
| 1302 | # + 0.5*($_limits(${axis}max)+$_limits(${axis}min))}] |
---|
[459] | 1303 | return "Move the [string toupper $axis] cut plane.\nCurrently: $axis = $val%" |
---|
[436] | 1304 | } |
---|
| 1305 | |
---|
| 1306 | # ---------------------------------------------------------------------- |
---|
[459] | 1307 | # USAGE: _probe start <x> <y> |
---|
| 1308 | # USAGE: _probe update <x> <y> |
---|
| 1309 | # USAGE: _probe end <x> <y> |
---|
| 1310 | # |
---|
| 1311 | # Used internally to handle the various probe operations, when the |
---|
| 1312 | # user clicks and drags on the legend area. The probe changes the |
---|
| 1313 | # transfer function to highlight the area being selected in the |
---|
| 1314 | # legend. |
---|
| 1315 | # ---------------------------------------------------------------------- |
---|
| 1316 | itcl::body Rappture::NanovisViewer::_probe {option args} { |
---|
| 1317 | set c $itk_component(legend) |
---|
| 1318 | set w [winfo width $c] |
---|
| 1319 | set h [winfo height $c] |
---|
| 1320 | set y0 10 |
---|
| 1321 | set y1 [expr {$y0+[image height $_image(legend)]-1}] |
---|
| 1322 | |
---|
| 1323 | set dataobj [lindex [get] 0] |
---|
[468] | 1324 | if {"" == $dataobj} { |
---|
| 1325 | return |
---|
| 1326 | } |
---|
[459] | 1327 | set comp [lindex [$dataobj components] 0] |
---|
| 1328 | if {![info exists _obj2style($dataobj-$comp)]} { |
---|
| 1329 | return |
---|
| 1330 | } |
---|
| 1331 | |
---|
| 1332 | switch -- $option { |
---|
| 1333 | start { |
---|
| 1334 | # create the probe marker on the legend |
---|
| 1335 | $c create rect 0 0 5 $h -width 3 \ |
---|
| 1336 | -outline black -fill "" -tags markerbg |
---|
| 1337 | $c create rect 0 0 5 $h -width 1 \ |
---|
| 1338 | -outline white -fill "" -tags marker |
---|
| 1339 | |
---|
| 1340 | # define a new transfer function |
---|
| 1341 | _send transfunc define probe {0 0 0 0 1 0 0 0} {0 0 1 0} |
---|
| 1342 | _send volume shading transfunc probe $_obj2id($dataobj-$comp) |
---|
| 1343 | |
---|
| 1344 | # now, probe this point |
---|
| 1345 | eval _probe update $args |
---|
| 1346 | } |
---|
| 1347 | update { |
---|
| 1348 | set x [lindex $args 0] |
---|
| 1349 | if {$x < 10} {set x 10} |
---|
| 1350 | if {$x > $w-10} {set x [expr {$w-10}]} |
---|
| 1351 | foreach tag {markerbg marker} { |
---|
| 1352 | $c coords $tag [expr {$x-2}] [expr {$y0-2}] \ |
---|
| 1353 | [expr {$x+2}] [expr {$y1+2}] |
---|
| 1354 | } |
---|
| 1355 | |
---|
| 1356 | # value of the probe point, in the range 0-1 |
---|
| 1357 | set val [expr {double($x-10)/($w-20)}] |
---|
| 1358 | set dl [expr {($val > 0.1) ? 0.1 : $val}] |
---|
| 1359 | set dr [expr {($val < 0.9) ? 0.1 : 1-$val}] |
---|
| 1360 | |
---|
| 1361 | # compute a transfer function for the probe value |
---|
| 1362 | foreach {sname cmap wmap} [_getTransfuncData $dataobj $comp] break |
---|
| 1363 | set wmap "0.0 0.0 [expr {$val-$dl}] 0.0 $val 1.0 [expr {$val+$dr}] 0.0 1.0 0.0" |
---|
| 1364 | _send transfunc define probe $cmap $wmap |
---|
| 1365 | } |
---|
| 1366 | end { |
---|
| 1367 | $c delete marker markerbg |
---|
| 1368 | |
---|
| 1369 | # put the volume back to its old transfer function |
---|
| 1370 | _send volume shading transfunc $_obj2style($dataobj-$comp) $_obj2id($dataobj-$comp) |
---|
| 1371 | } |
---|
| 1372 | default { |
---|
| 1373 | error "bad option \"$option\": should be start, update, end" |
---|
| 1374 | } |
---|
| 1375 | } |
---|
| 1376 | } |
---|
| 1377 | |
---|
| 1378 | # ---------------------------------------------------------------------- |
---|
[447] | 1379 | # USAGE: _state <component> |
---|
| 1380 | # |
---|
| 1381 | # Used internally to determine the state of a toggle button. |
---|
| 1382 | # The <component> is the itk component name of the button. |
---|
| 1383 | # Returns on/off for the state of the button. |
---|
| 1384 | # ---------------------------------------------------------------------- |
---|
| 1385 | itcl::body Rappture::NanovisViewer::_state {comp} { |
---|
| 1386 | if {[$itk_component($comp) cget -relief] == "sunken"} { |
---|
| 1387 | return "on" |
---|
| 1388 | } |
---|
| 1389 | return "off" |
---|
| 1390 | } |
---|
| 1391 | |
---|
| 1392 | # ---------------------------------------------------------------------- |
---|
| 1393 | # USAGE: _fixSettings <what> ?<value>? |
---|
| 1394 | # |
---|
| 1395 | # Used internally to update rendering settings whenever parameters |
---|
| 1396 | # change in the popup settings panel. Sends the new settings off |
---|
| 1397 | # to the back end. |
---|
| 1398 | # ---------------------------------------------------------------------- |
---|
| 1399 | itcl::body Rappture::NanovisViewer::_fixSettings {what {value ""}} { |
---|
| 1400 | set inner [$itk_component(controls).panel component inner] |
---|
| 1401 | switch -- $what { |
---|
| 1402 | light { |
---|
| 1403 | if {[isconnected]} { |
---|
| 1404 | set val [$inner.scales.light get] |
---|
| 1405 | set sval [expr {0.1*$val}] |
---|
| 1406 | _send volume shading diffuse $sval |
---|
| 1407 | |
---|
| 1408 | set sval [expr {sqrt($val+1.0)}] |
---|
| 1409 | _send volume shading specular $sval |
---|
| 1410 | } |
---|
| 1411 | } |
---|
| 1412 | transp { |
---|
| 1413 | if {[isconnected]} { |
---|
| 1414 | set val [$inner.scales.transp get] |
---|
| 1415 | set sval [expr {0.2*$val+1}] |
---|
| 1416 | _send volume shading opacity $sval |
---|
| 1417 | } |
---|
| 1418 | } |
---|
| 1419 | default { |
---|
| 1420 | error "don't know how to fix $what" |
---|
| 1421 | } |
---|
| 1422 | } |
---|
| 1423 | } |
---|
| 1424 | |
---|
| 1425 | # ---------------------------------------------------------------------- |
---|
[456] | 1426 | # USAGE: _fixLegend |
---|
| 1427 | # |
---|
| 1428 | # Used internally to update the legend area whenever it changes size |
---|
| 1429 | # or when the field changes. Asks the server to send a new legend |
---|
| 1430 | # for the current field. |
---|
| 1431 | # ---------------------------------------------------------------------- |
---|
| 1432 | itcl::body Rappture::NanovisViewer::_fixLegend {} { |
---|
[459] | 1433 | set lineht [font metrics $itk_option(-font) -linespace] |
---|
[456] | 1434 | set w [expr {[winfo width $itk_component(legend)]-20}] |
---|
[459] | 1435 | set h [expr {[winfo height $itk_component(legend)]-20-$lineht}] |
---|
[456] | 1436 | set ivol "" |
---|
| 1437 | |
---|
| 1438 | set dataobj [lindex [get] 0] |
---|
[468] | 1439 | if {"" != $dataobj} { |
---|
| 1440 | set comp [lindex [$dataobj components] 0] |
---|
| 1441 | if {[info exists _obj2id($dataobj-$comp)]} { |
---|
| 1442 | set ivol $_obj2id($dataobj-$comp) |
---|
| 1443 | } |
---|
[456] | 1444 | } |
---|
| 1445 | |
---|
| 1446 | if {$w > 0 && $h > 0 && "" != $ivol} { |
---|
| 1447 | _send legend $ivol $w $h |
---|
| 1448 | } else { |
---|
| 1449 | $itk_component(legend) delete all |
---|
| 1450 | } |
---|
| 1451 | } |
---|
| 1452 | |
---|
| 1453 | # ---------------------------------------------------------------------- |
---|
[459] | 1454 | # USAGE: _serverDown |
---|
| 1455 | # |
---|
| 1456 | # Used internally to let the user know when the connection to the |
---|
| 1457 | # visualization server has been lost. Puts up a tip encouraging the |
---|
| 1458 | # user to press any control to reconnect. |
---|
| 1459 | # ---------------------------------------------------------------------- |
---|
| 1460 | itcl::body Rappture::NanovisViewer::_serverDown {} { |
---|
| 1461 | set x [expr {[winfo rootx $itk_component(area)]+10}] |
---|
| 1462 | set y [expr {[winfo rooty $itk_component(area)]+10}] |
---|
| 1463 | Rappture::Tooltip::cue @$x,$y "Lost connection to visualization server. This happens sometimes when there are too many users and the system runs out of memory.\n\nTo reconnect, reset the view or press any other control. Your picture should come right back up." |
---|
| 1464 | } |
---|
| 1465 | |
---|
| 1466 | # ---------------------------------------------------------------------- |
---|
| 1467 | # USAGE: _getTransfuncData <dataobj> <comp> |
---|
| 1468 | # |
---|
| 1469 | # Used internally to compute the colormap and alpha map used to define |
---|
| 1470 | # a transfer function for the specified component in a data object. |
---|
| 1471 | # Returns: name {v r g b ...} {v w ...} |
---|
| 1472 | # ---------------------------------------------------------------------- |
---|
| 1473 | itcl::body Rappture::NanovisViewer::_getTransfuncData {dataobj comp} { |
---|
| 1474 | array set style { |
---|
| 1475 | -color rainbow |
---|
| 1476 | -levels 6 |
---|
| 1477 | -opacity 0.5 |
---|
| 1478 | } |
---|
| 1479 | array set style [lindex [$dataobj components -style $comp] 0] |
---|
| 1480 | set sname "$style(-color):$style(-levels):$style(-opacity)" |
---|
| 1481 | |
---|
| 1482 | if {$style(-color) == "rainbow"} { |
---|
| 1483 | set style(-color) "white:yellow:green:cyan:blue:magenta" |
---|
| 1484 | } |
---|
| 1485 | set clist [split $style(-color) :] |
---|
| 1486 | set cmap "0.0 [_color2rgb white] " |
---|
| 1487 | for {set i 0} {$i < [llength $clist]} {incr i} { |
---|
| 1488 | set xval [expr {double($i+1)/([llength $clist]+1)}] |
---|
| 1489 | set color [lindex $clist $i] |
---|
| 1490 | append cmap "$xval [_color2rgb $color] " |
---|
| 1491 | } |
---|
| 1492 | append cmap "1.0 [_color2rgb $color]" |
---|
| 1493 | |
---|
| 1494 | set max $style(-opacity) |
---|
| 1495 | set levels $style(-levels) |
---|
[519] | 1496 | if {[string is int $levels]} { |
---|
| 1497 | set wmap "0.0 0.0 " |
---|
| 1498 | set delta [expr {0.125/($levels+1)}] |
---|
| 1499 | for {set i 1} {$i <= $levels} {incr i} { |
---|
| 1500 | # add spikes in the middle |
---|
| 1501 | set xval [expr {double($i)/($levels+1)}] |
---|
| 1502 | append wmap "[expr {$xval-$delta-0.01}] 0.0 [expr {$xval-$delta}] $max [expr {$xval+$delta}] $max [expr {$xval+$delta+0.01}] 0.0 " |
---|
| 1503 | } |
---|
| 1504 | append wmap "1.0 0.0 " |
---|
| 1505 | } else { |
---|
| 1506 | set wmap "0.0 0.0 " |
---|
| 1507 | set delta 0.05 |
---|
| 1508 | foreach xval [split $levels ,] { |
---|
| 1509 | append wmap "[expr {$xval-$delta}] 0.0 $xval $max [expr {$xval+$delta}] 0.0 " |
---|
| 1510 | } |
---|
| 1511 | append wmap "1.0 0.0 " |
---|
[459] | 1512 | } |
---|
| 1513 | |
---|
| 1514 | return [list $sname $cmap $wmap] |
---|
| 1515 | } |
---|
| 1516 | |
---|
| 1517 | # ---------------------------------------------------------------------- |
---|
[436] | 1518 | # USAGE: _color2rgb <color> |
---|
| 1519 | # |
---|
| 1520 | # Used internally to convert a color name to a set of {r g b} values |
---|
| 1521 | # needed for the engine. Each r/g/b component is scaled in the |
---|
| 1522 | # range 0-1. |
---|
| 1523 | # ---------------------------------------------------------------------- |
---|
| 1524 | itcl::body Rappture::NanovisViewer::_color2rgb {color} { |
---|
| 1525 | foreach {r g b} [winfo rgb $itk_component(hull) $color] break |
---|
| 1526 | set r [expr {$r/65535.0}] |
---|
| 1527 | set g [expr {$g/65535.0}] |
---|
| 1528 | set b [expr {$b/65535.0}] |
---|
| 1529 | return [list $r $g $b] |
---|
| 1530 | } |
---|
| 1531 | |
---|
| 1532 | # ---------------------------------------------------------------------- |
---|
| 1533 | # USAGE: _euler2xyz <theta> <phi> <psi> |
---|
| 1534 | # |
---|
| 1535 | # Used internally to convert euler angles for the camera placement |
---|
| 1536 | # the to angles of rotation about the x/y/z axes, used by the engine. |
---|
| 1537 | # Returns a list: {xangle, yangle, zangle}. |
---|
| 1538 | # ---------------------------------------------------------------------- |
---|
| 1539 | itcl::body Rappture::NanovisViewer::_euler2xyz {theta phi psi} { |
---|
| 1540 | set xangle [expr {$theta-90.0}] |
---|
| 1541 | set yangle [expr {180-$phi}] |
---|
| 1542 | set zangle $psi |
---|
| 1543 | return [list $xangle $yangle $zangle] |
---|
| 1544 | } |
---|
| 1545 | |
---|
| 1546 | # ---------------------------------------------------------------------- |
---|
| 1547 | # CONFIGURATION OPTION: -plotbackground |
---|
| 1548 | # ---------------------------------------------------------------------- |
---|
| 1549 | itcl::configbody Rappture::NanovisViewer::plotbackground { |
---|
| 1550 | foreach {r g b} [_color2rgb $itk_option(-plotbackground)] break |
---|
[459] | 1551 | #fix this! |
---|
[436] | 1552 | #_send color background $r $g $b |
---|
| 1553 | } |
---|
| 1554 | |
---|
| 1555 | # ---------------------------------------------------------------------- |
---|
| 1556 | # CONFIGURATION OPTION: -plotforeground |
---|
| 1557 | # ---------------------------------------------------------------------- |
---|
| 1558 | itcl::configbody Rappture::NanovisViewer::plotforeground { |
---|
| 1559 | foreach {r g b} [_color2rgb $itk_option(-plotforeground)] break |
---|
[459] | 1560 | #fix this! |
---|
[436] | 1561 | #_send color background $r $g $b |
---|
| 1562 | } |
---|
| 1563 | |
---|
| 1564 | # ---------------------------------------------------------------------- |
---|
| 1565 | # CONFIGURATION OPTION: -plotoutline |
---|
| 1566 | # ---------------------------------------------------------------------- |
---|
| 1567 | itcl::configbody Rappture::NanovisViewer::plotoutline { |
---|
| 1568 | if {[isconnected]} { |
---|
| 1569 | if {"" == $itk_option(-plotoutline)} { |
---|
| 1570 | _send volume outline state off |
---|
| 1571 | } else { |
---|
| 1572 | _send volume outline state on |
---|
| 1573 | _send volume outline color [_color2rgb $itk_option(-plotoutline)] |
---|
| 1574 | } |
---|
| 1575 | } |
---|
| 1576 | } |
---|