source: branches/vtkvis_threaded/RpVtkRenderer.cpp @ 2495

Last change on this file since 2495 was 2480, checked in by ldelgass, 13 years ago

Fix for setting height of vertical legend

  • Property svn:eol-style set to native
File size: 223.6 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (C) 2011, Purdue Research Foundation
4 *
5 * Author: Leif Delgass <ldelgass@purdue.edu>
6 */
7
8#include <cfloat>
9#include <cstring>
10#include <cassert>
11#include <cmath>
12
13#include <GL/gl.h>
14
15#ifdef WANT_TRACE
16#include <sys/time.h>
17#endif
18
19#include <vtkMath.h>
20#include <vtkCamera.h>
21#include <vtkLight.h>
22#include <vtkCoordinate.h>
23#include <vtkTransform.h>
24#include <vtkCharArray.h>
25#include <vtkAxisActor2D.h>
26#include <vtkCubeAxesActor.h>
27#ifdef USE_CUSTOM_AXES
28#include "vtkRpCubeAxesActor2D.h"
29#else
30#include <vtkCubeAxesActor2D.h>
31#endif
32#include <vtkDataSetReader.h>
33#include <vtkDataSetMapper.h>
34#include <vtkContourFilter.h>
35#include <vtkPolyDataMapper.h>
36#include <vtkProperty.h>
37#include <vtkProperty2D.h>
38#include <vtkPointData.h>
39#include <vtkLookupTable.h>
40#include <vtkTextProperty.h>
41#include <vtkOpenGLRenderWindow.h>
42
43#include "RpVtkRenderer.h"
44#include "ColorMap.h"
45#include "Trace.h"
46
47#define ELAPSED_TIME(t1, t2) \
48    ((t1).tv_sec == (t2).tv_sec ? (((t2).tv_usec - (t1).tv_usec)/1.0e+3f) : \
49     (((t2).tv_sec - (t1).tv_sec))*1.0e+3f + (float)((t2).tv_usec - (t1).tv_usec)/1.0e+3f)
50
51using namespace Rappture::VtkVis;
52
53Renderer::Renderer() :
54    _needsRedraw(true),
55    _windowWidth(500),
56    _windowHeight(500),
57    _imgCameraPlane(PLANE_XY),
58    _imgCameraOffset(0),
59    _cameraZoomRatio(1),
60    _useCumulativeRange(true),
61    _cumulativeRangeOnlyVisible(false),
62    _cameraMode(PERSPECTIVE)
63{
64    _bgColor[0] = 0;
65    _bgColor[1] = 0;
66    _bgColor[2] = 0;
67    _cameraPan[0] = 0;
68    _cameraPan[1] = 0;
69    _cameraOrientation[0] = 1.0;
70    _cameraOrientation[1] = 0.0;
71    _cameraOrientation[2] = 0.0;
72    _cameraOrientation[3] = 0.0;
73    _cumulativeScalarRange[0] = 0.0;
74    _cumulativeScalarRange[1] = 1.0;
75    _cumulativeVectorMagnitudeRange[0] = 0.0;
76    _cumulativeVectorMagnitudeRange[1] = 1.0;
77    for (int i = 0; i < 3; i++) {
78        _cumulativeVectorComponentRange[i][0] = 0.0;
79        _cumulativeVectorComponentRange[i][1] = 1.0;
80    }
81    // clipping planes to prevent overdrawing axes
82    _activeClipPlanes = vtkSmartPointer<vtkPlaneCollection>::New();
83    // bottom
84    _cameraClipPlanes[0] = vtkSmartPointer<vtkPlane>::New();
85    _cameraClipPlanes[0]->SetNormal(0, 1, 0);
86    _cameraClipPlanes[0]->SetOrigin(0, 0, 0);
87    if (_cameraMode == IMAGE)
88        _activeClipPlanes->AddItem(_cameraClipPlanes[0]);
89    // left
90    _cameraClipPlanes[1] = vtkSmartPointer<vtkPlane>::New();
91    _cameraClipPlanes[1]->SetNormal(1, 0, 0);
92    _cameraClipPlanes[1]->SetOrigin(0, 0, 0);
93    if (_cameraMode == IMAGE)
94        _activeClipPlanes->AddItem(_cameraClipPlanes[1]);
95    // top
96    _cameraClipPlanes[2] = vtkSmartPointer<vtkPlane>::New();
97    _cameraClipPlanes[2]->SetNormal(0, -1, 0);
98    _cameraClipPlanes[2]->SetOrigin(0, 1, 0);
99    if (_cameraMode == IMAGE)
100        _activeClipPlanes->AddItem(_cameraClipPlanes[2]);
101    // right
102    _cameraClipPlanes[3] = vtkSmartPointer<vtkPlane>::New();
103    _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
104    _cameraClipPlanes[3]->SetOrigin(1, 0, 0);
105    if (_cameraMode == IMAGE)
106        _activeClipPlanes->AddItem(_cameraClipPlanes[3]);
107    _renderer = vtkSmartPointer<vtkRenderer>::New();
108    vtkSmartPointer<vtkLight> headlight = vtkSmartPointer<vtkLight>::New();
109    headlight->SetLightTypeToHeadlight();
110    headlight->PositionalOff();
111    //headlight->SetAmbientColor(1, 1, 1);
112    _renderer->SetAmbient(.2, .2, .2);
113    _renderer->AddLight(headlight);
114    vtkSmartPointer<vtkLight> skylight = vtkSmartPointer<vtkLight>::New();
115    skylight->SetLightTypeToCameraLight();
116    skylight->SetPosition(0, 1, 0);
117    skylight->SetFocalPoint(0, 0, 0);
118    skylight->PositionalOff();
119    //skylight->SetAmbientColor(1, 1, 1);
120    _renderer->AddLight(skylight);
121    _renderer->LightFollowCameraOn();
122    _renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
123#ifdef USE_OFFSCREEN_RENDERING
124    _renderWindow->DoubleBufferOff();
125    _renderWindow->OffScreenRenderingOn();
126#else
127    _renderWindow->SwapBuffersOff();
128#endif
129    _renderWindow->SetSize(_windowWidth, _windowHeight);
130    // Next 2 options needed to support depth peeling
131    _renderWindow->SetAlphaBitPlanes(1);
132    _renderWindow->SetMultiSamples(0);
133    _renderer->SetMaximumNumberOfPeels(100);
134    _renderer->SetUseDepthPeeling(1);
135    _renderWindow->AddRenderer(_renderer);
136    setViewAngle(_windowHeight);
137    initAxes();
138    initCamera();
139    addColorMap("default", ColorMap::getDefault());
140    addColorMap("grayDefault", ColorMap::getGrayDefault());
141    addColorMap("volumeDefault", ColorMap::getVolumeDefault());
142    addColorMap("elementDefault", ColorMap::getElementDefault());
143}
144
145Renderer::~Renderer()
146{
147    TRACE("Enter");
148    TRACE("Deleting Contour2Ds");
149    for (Contour2DHashmap::iterator itr = _contour2Ds.begin();
150             itr != _contour2Ds.end(); ++itr) {
151        delete itr->second;
152    }
153    _contour2Ds.clear();
154    TRACE("Deleting Contour3Ds");
155    for (Contour3DHashmap::iterator itr = _contour3Ds.begin();
156             itr != _contour3Ds.end(); ++itr) {
157        delete itr->second;
158    }
159    _contour3Ds.clear();
160    TRACE("Deleting Glyphs");
161    for (GlyphsHashmap::iterator itr = _glyphs.begin();
162             itr != _glyphs.end(); ++itr) {
163        delete itr->second;
164    }
165    _glyphs.clear();
166    TRACE("Deleting HeightMaps");
167    for (HeightMapHashmap::iterator itr = _heightMaps.begin();
168             itr != _heightMaps.end(); ++itr) {
169        delete itr->second;
170    }
171    _heightMaps.clear();
172    TRACE("Deleting LICs");
173    for (LICHashmap::iterator itr = _lics.begin();
174             itr != _lics.end(); ++itr) {
175        delete itr->second;
176    }
177    _lics.clear();
178    TRACE("Deleting Molecules");
179    for (MoleculeHashmap::iterator itr = _molecules.begin();
180             itr != _molecules.end(); ++itr) {
181        delete itr->second;
182    }
183    _molecules.clear();
184    TRACE("Deleting PolyDatas");
185    for (PolyDataHashmap::iterator itr = _polyDatas.begin();
186             itr != _polyDatas.end(); ++itr) {
187        delete itr->second;
188    }
189    _polyDatas.clear();
190    TRACE("Deleting PseudoColors");
191    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
192             itr != _pseudoColors.end(); ++itr) {
193        delete itr->second;
194    }
195    _pseudoColors.clear();
196    TRACE("Deleting Streamlines");
197    for (StreamlinesHashmap::iterator itr = _streamlines.begin();
198             itr != _streamlines.end(); ++itr) {
199        delete itr->second;
200    }
201    _streamlines.clear();
202    TRACE("Deleting Volumes");
203    for (VolumeHashmap::iterator itr = _volumes.begin();
204             itr != _volumes.end(); ++itr) {
205        delete itr->second;
206    }
207    _volumes.clear();
208    TRACE("Deleting ColorMaps");
209    // Delete color maps and data sets last in case references still
210    // exist
211    for (ColorMapHashmap::iterator itr = _colorMaps.begin();
212             itr != _colorMaps.end(); ++itr) {
213        delete itr->second;
214    }
215    _colorMaps.clear();
216    TRACE("Deleting DataSets");
217    for (DataSetHashmap::iterator itr = _dataSets.begin();
218             itr != _dataSets.end(); ++itr) {
219        delete itr->second;
220    }
221    _dataSets.clear();
222    TRACE("Leave");
223}
224
225/**
226 * \brief Add a DataSet to this Renderer
227 *
228 * This just adds the DataSet to the Renderer's list of data sets.
229 * In order to render the data, a graphics object using the data
230 * set must be added to the Renderer.
231 */
232void Renderer::addDataSet(const DataSetId& id)
233{
234    if (getDataSet(id) != NULL) {
235        WARN("Replacing existing DataSet %s", id.c_str());
236        deleteDataSet(id);
237    }
238    _dataSets[id] = new DataSet(id);
239}
240
241/**
242 * \brief Remove the Contour2D isolines for the specified DataSet
243 *
244 * The underlying Contour2D is deleted, freeing its memory
245 */
246void Renderer::deleteContour2D(const DataSetId& id)
247{
248    Contour2DHashmap::iterator itr;
249
250    bool doAll = false;
251
252    if (id.compare("all") == 0) {
253        itr = _contour2Ds.begin();
254        doAll = true;
255    } else {
256        itr = _contour2Ds.find(id);
257    }
258    if (itr == _contour2Ds.end()) {
259        ERROR("Contour2D not found: %s", id.c_str());
260        return;
261    }
262
263    TRACE("Deleting Contour2Ds for %s", id.c_str());
264
265    do {
266        Contour2D *contour = itr->second;
267        if (contour->getProp())
268            _renderer->RemoveViewProp(contour->getProp());
269        delete contour;
270
271        itr = _contour2Ds.erase(itr);
272    } while (doAll && itr != _contour2Ds.end());
273
274    initCamera();
275    _needsRedraw = true;
276}
277
278/**
279 * \brief Remove the Contour3D isosurfaces for the specified DataSet
280 *
281 * The underlying Contour3D is deleted, freeing its memory
282 */
283void Renderer::deleteContour3D(const DataSetId& id)
284{
285    Contour3DHashmap::iterator itr;
286
287    bool doAll = false;
288
289    if (id.compare("all") == 0) {
290        itr = _contour3Ds.begin();
291        doAll = true;
292    } else {
293        itr = _contour3Ds.find(id);
294    }
295    if (itr == _contour3Ds.end()) {
296        ERROR("Contour3D not found: %s", id.c_str());
297        return;
298    }
299
300    TRACE("Deleting Contour3Ds for %s", id.c_str());
301
302    do {
303        Contour3D *contour = itr->second;
304        if (contour->getProp())
305            _renderer->RemoveViewProp(contour->getProp());
306        delete contour;
307
308        itr = _contour3Ds.erase(itr);
309    } while (doAll && itr != _contour3Ds.end());
310
311    initCamera();
312    _needsRedraw = true;
313}
314
315/**
316 * \brief Remove the Glyphs for the specified DataSet
317 *
318 * The underlying Glyphs is deleted, freeing its memory
319 */
320void Renderer::deleteGlyphs(const DataSetId& id)
321{
322    GlyphsHashmap::iterator itr;
323
324    bool doAll = false;
325
326    if (id.compare("all") == 0) {
327        itr = _glyphs.begin();
328        doAll = true;
329    } else {
330        itr = _glyphs.find(id);
331    }
332    if (itr == _glyphs.end()) {
333        ERROR("Glyphs not found: %s", id.c_str());
334        return;
335    }
336
337    TRACE("Deleting Glyphs for %s", id.c_str());
338
339    do {
340        Glyphs *glyphs = itr->second;
341        if (glyphs->getProp())
342            _renderer->RemoveViewProp(glyphs->getProp());
343        delete glyphs;
344
345        itr = _glyphs.erase(itr);
346    } while (doAll && itr != _glyphs.end());
347
348    initCamera();
349    _needsRedraw = true;
350}
351
352/**
353 * \brief Remove the HeightMap for the specified DataSet
354 *
355 * The underlying HeightMap is deleted, freeing its memory
356 */
357void Renderer::deleteHeightMap(const DataSetId& id)
358{
359    HeightMapHashmap::iterator itr;
360
361    bool doAll = false;
362
363    if (id.compare("all") == 0) {
364        itr = _heightMaps.begin();
365        doAll = true;
366    } else {
367        itr = _heightMaps.find(id);
368    }
369    if (itr == _heightMaps.end()) {
370        ERROR("HeightMap not found: %s", id.c_str());
371        return;
372    }
373
374    TRACE("Deleting HeightMaps for %s", id.c_str());
375
376    do {
377        HeightMap *hmap = itr->second;
378        if (hmap->getProp())
379            _renderer->RemoveViewProp(hmap->getProp());
380        delete hmap;
381
382        itr = _heightMaps.erase(itr);
383    } while (doAll && itr != _heightMaps.end());
384
385    initCamera();
386    _needsRedraw = true;
387}
388
389/**
390 * \brief Remove the LIC for the specified DataSet
391 *
392 * The underlying LIC is deleted, freeing its memory
393 */
394void Renderer::deleteLIC(const DataSetId& id)
395{
396    LICHashmap::iterator itr;
397
398    bool doAll = false;
399
400    if (id.compare("all") == 0) {
401        itr = _lics.begin();
402        doAll = true;
403    } else {
404        itr = _lics.find(id);
405    }
406    if (itr == _lics.end()) {
407        ERROR("LIC not found: %s", id.c_str());
408        return;
409    }
410
411    TRACE("Deleting LICs for %s", id.c_str());
412
413    do {
414        LIC *lic = itr->second;
415        if (lic->getProp())
416            _renderer->RemoveViewProp(lic->getProp());
417        delete lic;
418
419        itr = _lics.erase(itr);
420    } while (doAll && itr != _lics.end());
421
422    initCamera();
423    _needsRedraw = true;
424}
425
426/**
427 * \brief Remove the Molecule for the specified DataSet
428 *
429 * The underlying Molecule is deleted, freeing its memory
430 */
431void Renderer::deleteMolecule(const DataSetId& id)
432{
433    MoleculeHashmap::iterator itr;
434
435    bool doAll = false;
436
437    if (id.compare("all") == 0) {
438        itr = _molecules.begin();
439        doAll = true;
440    } else {
441        itr = _molecules.find(id);
442    }
443    if (itr == _molecules.end()) {
444        ERROR("Molecule not found: %s", id.c_str());
445        return;
446    }
447
448    TRACE("Deleting Molecules for %s", id.c_str());
449
450    do {
451        Molecule *molecule = itr->second;
452        if (molecule->getProp())
453            _renderer->RemoveViewProp(molecule->getProp());
454        delete molecule;
455
456        itr = _molecules.erase(itr);
457    } while (doAll && itr != _molecules.end());
458
459    initCamera();
460    _needsRedraw = true;
461}
462
463/**
464 * \brief Remove the PolyData mesh for the specified DataSet
465 *
466 * The underlying PolyData is deleted, freeing its memory
467 */
468void Renderer::deletePolyData(const DataSetId& id)
469{
470    PolyDataHashmap::iterator itr;
471
472    bool doAll = false;
473
474    if (id.compare("all") == 0) {
475        itr = _polyDatas.begin();
476        doAll = true;
477    } else {
478        itr = _polyDatas.find(id);
479    }
480    if (itr == _polyDatas.end()) {
481        ERROR("PolyData not found: %s", id.c_str());
482        return;
483    }
484
485    TRACE("Deleting PolyDatas for %s", id.c_str());
486
487    do {
488        PolyData *polyData = itr->second;
489        if (polyData->getProp())
490            _renderer->RemoveViewProp(polyData->getProp());
491        delete polyData;
492
493        itr = _polyDatas.erase(itr);
494    } while (doAll && itr != _polyDatas.end());
495
496    initCamera();
497    _needsRedraw = true;
498}
499
500/**
501 * \brief Remove the PseudoColor mapping for the specified DataSet
502 *
503 * The underlying PseudoColor object is deleted, freeing its memory
504 */
505void Renderer::deletePseudoColor(const DataSetId& id)
506{
507    PseudoColorHashmap::iterator itr;
508
509    bool doAll = false;
510
511    if (id.compare("all") == 0) {
512        itr = _pseudoColors.begin();
513        doAll = true;
514    } else {
515        itr = _pseudoColors.find(id);
516    }
517    if (itr == _pseudoColors.end()) {
518        ERROR("PseudoColor not found: %s", id.c_str());
519        return;
520    }
521
522    TRACE("Deleting PseudoColors for %s", id.c_str());
523
524    do  {
525        PseudoColor *ps = itr->second;
526        if (ps->getProp())
527            _renderer->RemoveViewProp(ps->getProp());
528        delete ps;
529
530        itr = _pseudoColors.erase(itr);
531    } while (doAll && itr != _pseudoColors.end());
532
533    initCamera();
534    _needsRedraw = true;
535}
536
537/**
538 * \brief Remove the Streamlines mapping for the specified DataSet
539 *
540 * The underlying Streamlines object is deleted, freeing its memory
541 */
542void Renderer::deleteStreamlines(const DataSetId& id)
543{
544    StreamlinesHashmap::iterator itr;
545
546    bool doAll = false;
547
548    if (id.compare("all") == 0) {
549        itr = _streamlines.begin();
550        doAll = true;
551    } else {
552        itr = _streamlines.find(id);
553    }
554    if (itr == _streamlines.end()) {
555        ERROR("Streamlines not found: %s", id.c_str());
556        return;
557    }
558
559    TRACE("Deleting Streamlines for %s", id.c_str());
560
561    do  {
562        Streamlines *sl = itr->second;
563        if (sl->getProp())
564            _renderer->RemoveViewProp(sl->getProp());
565        delete sl;
566
567        itr = _streamlines.erase(itr);
568    } while (doAll && itr != _streamlines.end());
569
570    initCamera();
571    _needsRedraw = true;
572}
573
574/**
575 * \brief Remove the Volume for the specified DataSet
576 *
577 * The underlying Volume is deleted, freeing its memory
578 */
579void Renderer::deleteVolume(const DataSetId& id)
580{
581    VolumeHashmap::iterator itr;
582
583    bool doAll = false;
584
585    if (id.compare("all") == 0) {
586        itr = _volumes.begin();
587        doAll = true;
588    } else {
589        itr = _volumes.find(id);
590    }
591    if (itr == _volumes.end()) {
592        ERROR("Volume not found: %s", id.c_str());
593        return;
594    }
595
596    TRACE("Deleting Volumes for %s", id.c_str());
597
598    do {
599        Volume *volume = itr->second;
600        if (volume->getProp())
601            _renderer->RemoveViewProp(volume->getProp());
602        delete volume;
603
604        itr = _volumes.erase(itr);
605    } while (doAll && itr != _volumes.end());
606
607    initCamera();
608    _needsRedraw = true;
609}
610
611/**
612 * \brief Remove the specified DataSet and associated rendering objects
613 *
614 * The underlying DataSet and any associated graphics
615 * objects are deleted, freeing the memory used.
616 */
617void Renderer::deleteDataSet(const DataSetId& id)
618{
619    DataSetHashmap::iterator itr;
620
621    bool doAll = false;
622
623    if (id.compare("all") == 0) {
624        itr = _dataSets.begin();
625        doAll = true;
626    } else {
627        itr = _dataSets.find(id);
628    }
629    if (itr == _dataSets.end()) {
630        ERROR("Unknown dataset %s", id.c_str());
631        return;
632    }
633
634    do {
635        TRACE("Deleting dataset %s", itr->second->getName().c_str());
636
637        deleteContour2D(itr->second->getName());
638        deleteContour3D(itr->second->getName());
639        deleteGlyphs(itr->second->getName());
640        deleteHeightMap(itr->second->getName());
641        deleteLIC(itr->second->getName());
642        deleteMolecule(itr->second->getName());
643        deletePolyData(itr->second->getName());
644        deletePseudoColor(itr->second->getName());
645        deleteStreamlines(itr->second->getName());
646        deleteVolume(itr->second->getName());
647 
648        if (itr->second->getProp() != NULL) {
649            _renderer->RemoveViewProp(itr->second->getProp());
650        }
651
652        TRACE("After deleting graphics objects");
653
654        delete itr->second;
655        itr = _dataSets.erase(itr);
656    } while (doAll && itr != _dataSets.end());
657
658    // Update cumulative data range
659    updateRanges();
660
661    initCamera();
662    _needsRedraw = true;
663}
664
665void Renderer::getDataSetNames(std::vector<std::string>& names)
666{
667    names.clear();
668    for (DataSetHashmap::iterator itr = _dataSets.begin();
669         itr != _dataSets.end(); ++itr) {
670        names.push_back(itr->second->getName());
671    }
672}
673
674/**
675 * \brief Find the DataSet for the given DataSetId key
676 *
677 * \return A pointer to the DataSet, or NULL if not found
678 */
679DataSet *Renderer::getDataSet(const DataSetId& id)
680{
681    DataSetHashmap::iterator itr = _dataSets.find(id);
682    if (itr == _dataSets.end()) {
683#ifdef DEBUG
684        TRACE("DataSet not found: %s", id.c_str());
685#endif
686        return NULL;
687    } else
688        return itr->second;
689}
690
691/**
692 * \brief (Re-)load the data for the specified DataSet key from a file
693 */
694bool Renderer::setDataFile(const DataSetId& id, const char *filename)
695{
696    DataSet *ds = getDataSet(id);
697    if (ds) {
698        bool ret = ds->setDataFile(filename);
699        updateRanges();
700        _needsRedraw = true;
701        return ret;
702    } else
703        return false;
704}
705
706/**
707 * \brief (Re-)load the data for the specified DataSet key from a memory buffer
708 */
709bool Renderer::setData(const DataSetId& id, char *data, int nbytes)
710{
711    DataSet *ds = getDataSet(id);
712    if (ds) {
713        bool ret = ds->setData(data, nbytes);
714        updateRanges();
715        _needsRedraw = true;
716        return ret;
717    } else
718        return false;
719}
720
721bool Renderer::setDataSetActiveScalars(const DataSetId& id, const char *scalarName)
722{
723    DataSetHashmap::iterator itr;
724
725    bool doAll = false;
726
727    if (id.compare("all") == 0) {
728        itr = _dataSets.begin();
729        doAll = true;
730    } else {
731        itr = _dataSets.find(id);
732    }
733    if (itr == _dataSets.end()) {
734        ERROR("DataSet not found: %s", id.c_str());
735        return false;
736    }
737
738    bool ret = true;
739    do {
740        if (!itr->second->setActiveScalars(scalarName)) {
741            ret = false;
742        }
743    } while (doAll && ++itr != _dataSets.end());
744
745    if (ret) {
746         updateRanges();
747        _needsRedraw = true;
748    }
749
750    return ret;
751}
752
753bool Renderer::setDataSetActiveVectors(const DataSetId& id, const char *vectorName)
754{
755    DataSetHashmap::iterator itr;
756
757    bool doAll = false;
758
759    if (id.compare("all") == 0) {
760        itr = _dataSets.begin();
761        doAll = true;
762    } else {
763        itr = _dataSets.find(id);
764    }
765    if (itr == _dataSets.end()) {
766        ERROR("DataSet not found: %s", id.c_str());
767        return false;
768    }
769
770    bool ret = true;
771    do {
772        if (!itr->second->setActiveVectors(vectorName)) {
773            ret = false;
774        }
775    } while (doAll && ++itr != _dataSets.end());
776
777    if (ret) {
778        updateRanges();
779        _needsRedraw = true;
780    }
781
782    return ret;
783}
784
785/**
786 * \brief Control whether the cumulative data range of datasets is used for
787 * colormapping and contours
788 */
789void Renderer::setUseCumulativeDataRange(bool state, bool onlyVisible)
790{
791    if (state != _useCumulativeRange) {
792        _useCumulativeRange = state;
793        _cumulativeRangeOnlyVisible = onlyVisible;
794        updateRanges();
795        _needsRedraw = true;
796    }
797}
798
799void Renderer::resetAxes(double bounds[6])
800{
801    TRACE("Resetting axes");
802    if (_cubeAxesActor == NULL ||
803        _cubeAxesActor2D == NULL) {
804        initAxes();
805    }
806    if (_cameraMode == IMAGE) {
807        if (_renderer->HasViewProp(_cubeAxesActor)) {
808            TRACE("Removing 3D axes");
809            _renderer->RemoveViewProp(_cubeAxesActor);
810        }
811        if (!_renderer->HasViewProp(_cubeAxesActor2D)) {
812            TRACE("Adding 2D axes");
813            _renderer->AddViewProp(_cubeAxesActor2D);
814        }
815    } else {
816        if (_renderer->HasViewProp(_cubeAxesActor2D)) {
817            TRACE("Removing 2D axes");
818            _renderer->RemoveViewProp(_cubeAxesActor2D);
819        }
820        if (!_renderer->HasViewProp(_cubeAxesActor)) {
821            TRACE("Adding 3D axes");
822            _renderer->AddViewProp(_cubeAxesActor);
823        }
824        if (bounds == NULL) {
825            double newBounds[6];
826            collectBounds(newBounds, false);
827            _cubeAxesActor->SetBounds(newBounds);
828            TRACE("Bounds (computed): %g %g %g %g %g %g",
829                  newBounds[0],
830                  newBounds[1],
831                  newBounds[2],
832                  newBounds[3],
833                  newBounds[4],
834                  newBounds[5]);
835        } else {
836            _cubeAxesActor->SetBounds(bounds);
837            TRACE("Bounds (supplied): %g %g %g %g %g %g",
838                  bounds[0],
839                  bounds[1],
840                  bounds[2],
841                  bounds[3],
842                  bounds[4],
843                  bounds[5]);
844        }
845    }
846}
847
848/**
849 * \brief Set inital properties on the 2D Axes
850 */
851void Renderer::initAxes()
852{
853    TRACE("Initializing axes");
854    if (_cubeAxesActor == NULL)
855        _cubeAxesActor = vtkSmartPointer<vtkCubeAxesActor>::New();
856    _cubeAxesActor->SetCamera(_renderer->GetActiveCamera());
857    _cubeAxesActor->GetProperty()->LightingOff();
858    // Don't offset labels at origin
859    _cubeAxesActor->SetCornerOffset(0);
860    _cubeAxesActor->SetFlyModeToClosestTriad();
861
862#ifdef USE_CUSTOM_AXES
863    if (_cubeAxesActor2D == NULL)
864        _cubeAxesActor2D = vtkSmartPointer<vtkRpCubeAxesActor2D>::New();
865#else
866    if (_cubeAxesActor2D == NULL)
867        _cubeAxesActor2D = vtkSmartPointer<vtkCubeAxesActor2D>::New();
868#endif
869
870    _cubeAxesActor2D->SetCamera(_renderer->GetActiveCamera());
871    _cubeAxesActor2D->ZAxisVisibilityOff();
872    _cubeAxesActor2D->SetCornerOffset(0);
873    _cubeAxesActor2D->SetFlyModeToClosestTriad();
874
875    _cubeAxesActor2D->ScalingOff();
876    //_cubeAxesActor2D->SetShowActualBounds(0);
877    _cubeAxesActor2D->SetFontFactor(1.25);
878    // Use "nice" range and number of ticks/labels
879    _cubeAxesActor2D->GetXAxisActor2D()->AdjustLabelsOn();
880    _cubeAxesActor2D->GetYAxisActor2D()->AdjustLabelsOn();
881    _cubeAxesActor2D->GetZAxisActor2D()->AdjustLabelsOn();
882
883#ifdef USE_CUSTOM_AXES
884    _cubeAxesActor2D->SetAxisTitleTextProperty(NULL);
885    _cubeAxesActor2D->SetAxisLabelTextProperty(NULL);
886    //_cubeAxesActor2D->GetXAxisActor2D()->SizeFontRelativeToAxisOn();
887    _cubeAxesActor2D->GetXAxisActor2D()->GetTitleTextProperty()->BoldOn();
888    _cubeAxesActor2D->GetXAxisActor2D()->GetTitleTextProperty()->ItalicOff();
889    _cubeAxesActor2D->GetXAxisActor2D()->GetTitleTextProperty()->ShadowOn();
890    _cubeAxesActor2D->GetXAxisActor2D()->GetLabelTextProperty()->BoldOff();
891    _cubeAxesActor2D->GetXAxisActor2D()->GetLabelTextProperty()->ItalicOff();
892    _cubeAxesActor2D->GetXAxisActor2D()->GetLabelTextProperty()->ShadowOff();
893
894    //_cubeAxesActor2D->GetYAxisActor2D()->SizeFontRelativeToAxisOn();
895    _cubeAxesActor2D->GetYAxisActor2D()->GetTitleTextProperty()->BoldOn();
896    _cubeAxesActor2D->GetYAxisActor2D()->GetTitleTextProperty()->ItalicOff();
897    _cubeAxesActor2D->GetYAxisActor2D()->GetTitleTextProperty()->ShadowOn();
898    _cubeAxesActor2D->GetYAxisActor2D()->GetLabelTextProperty()->BoldOff();
899    _cubeAxesActor2D->GetYAxisActor2D()->GetLabelTextProperty()->ItalicOff();
900    _cubeAxesActor2D->GetYAxisActor2D()->GetLabelTextProperty()->ShadowOff();
901
902    //_cubeAxesActor2D->GetZAxisActor2D()->SizeFontRelativeToAxisOn();
903    _cubeAxesActor2D->GetZAxisActor2D()->GetTitleTextProperty()->BoldOn();
904    _cubeAxesActor2D->GetZAxisActor2D()->GetTitleTextProperty()->ItalicOff();
905    _cubeAxesActor2D->GetZAxisActor2D()->GetTitleTextProperty()->ShadowOn();
906    _cubeAxesActor2D->GetZAxisActor2D()->GetLabelTextProperty()->BoldOff();
907    _cubeAxesActor2D->GetZAxisActor2D()->GetLabelTextProperty()->ItalicOff();
908    _cubeAxesActor2D->GetZAxisActor2D()->GetLabelTextProperty()->ShadowOff();
909#else
910    _cubeAxesActor2D->GetAxisTitleTextProperty()->BoldOn();
911    _cubeAxesActor2D->GetAxisTitleTextProperty()->ItalicOff();
912    _cubeAxesActor2D->GetAxisTitleTextProperty()->ShadowOn();
913    _cubeAxesActor2D->GetAxisLabelTextProperty()->BoldOff();
914    _cubeAxesActor2D->GetAxisLabelTextProperty()->ItalicOff();
915    _cubeAxesActor2D->GetAxisLabelTextProperty()->ShadowOff();
916#endif
917
918    if (_cameraMode == IMAGE) {
919        if (!_renderer->HasViewProp(_cubeAxesActor2D))
920            _renderer->AddViewProp(_cubeAxesActor2D);
921    } else {
922        if (!_renderer->HasViewProp(_cubeAxesActor))
923            _renderer->AddViewProp(_cubeAxesActor);
924    }
925}
926
927/**
928 * \brief Set Fly mode of axes
929 */
930void Renderer::setAxesFlyMode(AxesFlyMode mode)
931{
932    if (_cubeAxesActor == NULL)
933        initAxes();
934    switch (mode) {
935    case FLY_STATIC_EDGES:
936        _cubeAxesActor->SetFlyModeToStaticEdges();
937        break;
938    case FLY_STATIC_TRIAD:
939        _cubeAxesActor->SetFlyModeToStaticTriad();
940        break;
941    case FLY_OUTER_EDGES:
942        _cubeAxesActor->SetFlyModeToOuterEdges();
943        break;
944    case FLY_FURTHEST_TRIAD:
945        _cubeAxesActor->SetFlyModeToFurthestTriad();
946        break;
947    case FLY_CLOSEST_TRIAD:
948    default:
949        _cubeAxesActor->SetFlyModeToClosestTriad();
950        break;
951    }
952    _needsRedraw = true;
953}
954
955/**
956 * \brief Set color of axes, ticks, labels, titles
957 */
958void Renderer::setAxesColor(double color[3])
959{
960    if (_cubeAxesActor != NULL) {
961        _cubeAxesActor->GetProperty()->SetColor(color);
962        _needsRedraw = true;
963    }
964    if (_cubeAxesActor2D != NULL) {
965        _cubeAxesActor2D->GetProperty()->SetColor(color);
966#ifdef USE_CUSTOM_AXES
967        _cubeAxesActor2D->GetXAxisActor2D()->GetTitleTextProperty()->SetColor(color);
968        _cubeAxesActor2D->GetXAxisActor2D()->GetLabelTextProperty()->SetColor(color);
969        _cubeAxesActor2D->GetYAxisActor2D()->GetTitleTextProperty()->SetColor(color);
970        _cubeAxesActor2D->GetYAxisActor2D()->GetLabelTextProperty()->SetColor(color);
971        _cubeAxesActor2D->GetZAxisActor2D()->GetTitleTextProperty()->SetColor(color);
972        _cubeAxesActor2D->GetZAxisActor2D()->GetLabelTextProperty()->SetColor(color);
973#else
974        _cubeAxesActor2D->GetAxisTitleTextProperty()->SetColor(color);
975        _cubeAxesActor2D->GetAxisLabelTextProperty()->SetColor(color);
976#endif
977        _needsRedraw = true;
978    }
979}
980
981/**
982 * \brief Turn on/off rendering of all enabled axes
983 */
984void Renderer::setAxesVisibility(bool state)
985{
986    if (_cubeAxesActor != NULL) {
987        _cubeAxesActor->SetVisibility((state ? 1 : 0));
988        _needsRedraw = true;
989    }
990    if (_cubeAxesActor2D != NULL) {
991        _cubeAxesActor2D->SetVisibility((state ? 1 : 0));
992        _needsRedraw = true;
993    }
994}
995
996/**
997 * \brief Turn on/off rendering of all axes gridlines
998 */
999void Renderer::setAxesGridVisibility(bool state)
1000{
1001    setAxisGridVisibility(X_AXIS, state);
1002    setAxisGridVisibility(Y_AXIS, state);
1003    setAxisGridVisibility(Z_AXIS, state);
1004}
1005
1006/**
1007 * \brief Turn on/off rendering of all axis labels
1008 */
1009void Renderer::setAxesLabelVisibility(bool state)
1010{
1011    setAxisLabelVisibility(X_AXIS, state);
1012    setAxisLabelVisibility(Y_AXIS, state);
1013    setAxisLabelVisibility(Z_AXIS, state);
1014}
1015
1016/**
1017 * \brief Turn on/off rendering of all axis ticks
1018 */
1019void Renderer::setAxesTickVisibility(bool state)
1020{
1021    setAxisTickVisibility(X_AXIS, state);
1022    setAxisTickVisibility(Y_AXIS, state);
1023    setAxisTickVisibility(Z_AXIS, state);
1024}
1025
1026/**
1027 * \brief Control position of ticks on 3D axes
1028 */
1029void Renderer::setAxesTickPosition(AxesTickPosition pos)
1030{
1031    if (_cubeAxesActor == NULL)
1032        return;
1033
1034    switch (pos) {
1035    case TICKS_BOTH:
1036        _cubeAxesActor->SetTickLocationToBoth();
1037        break;
1038    case TICKS_OUTSIDE:
1039        _cubeAxesActor->SetTickLocationToOutside();
1040        break;
1041    case TICKS_INSIDE:
1042    default:
1043        _cubeAxesActor->SetTickLocationToInside();
1044        break;
1045    }
1046    _needsRedraw = true;
1047}
1048
1049/**
1050 * \brief Turn on/off rendering of the specified axis
1051 */
1052void Renderer::setAxisVisibility(Axis axis, bool state)
1053{
1054    if (_cubeAxesActor != NULL) {
1055        if (axis == X_AXIS) {
1056            _cubeAxesActor->SetXAxisVisibility((state ? 1 : 0));
1057        } else if (axis == Y_AXIS) {
1058            _cubeAxesActor->SetYAxisVisibility((state ? 1 : 0));
1059        } else if (axis == Z_AXIS) {
1060            _cubeAxesActor->SetZAxisVisibility((state ? 1 : 0));
1061        }
1062        _needsRedraw = true;
1063    }
1064    if (_cubeAxesActor2D != NULL) {
1065        if (axis == X_AXIS) {
1066            _cubeAxesActor2D->SetXAxisVisibility((state ? 1 : 0));
1067        } else if (axis == Y_AXIS) {
1068            _cubeAxesActor2D->SetYAxisVisibility((state ? 1 : 0));
1069        } else if (axis == Z_AXIS) {
1070            _cubeAxesActor2D->SetZAxisVisibility((state ? 1 : 0));
1071        }
1072        _needsRedraw = true;
1073    }
1074}
1075
1076/**
1077 * \brief Turn on/off rendering of single axis gridlines
1078 */
1079void Renderer::setAxisGridVisibility(Axis axis, bool state)
1080{
1081    if (_cubeAxesActor != NULL) {
1082        if (axis == X_AXIS) {
1083            _cubeAxesActor->SetDrawXGridlines((state ? 1 : 0));
1084        } else if (axis == Y_AXIS) {
1085            _cubeAxesActor->SetDrawYGridlines((state ? 1 : 0));
1086        } else if (axis == Z_AXIS) {
1087            _cubeAxesActor->SetDrawZGridlines((state ? 1 : 0));
1088        }
1089        _needsRedraw = true;
1090    }
1091}
1092
1093/**
1094 * \brief Toggle label visibility for the specified axis
1095 */
1096void Renderer::setAxisLabelVisibility(Axis axis, bool state)
1097{
1098    if (_cubeAxesActor != NULL) {
1099        if (axis == X_AXIS) {
1100            _cubeAxesActor->SetXAxisLabelVisibility((state ? 1 : 0));
1101        } else if (axis == Y_AXIS) {
1102            _cubeAxesActor->SetYAxisLabelVisibility((state ? 1 : 0));
1103        } else if (axis == Z_AXIS) {
1104            _cubeAxesActor->SetZAxisLabelVisibility((state ? 1 : 0));
1105        }
1106        _needsRedraw = true;
1107    }
1108    if (_cubeAxesActor2D != NULL) {
1109        if (axis == X_AXIS) {
1110            _cubeAxesActor2D->GetXAxisActor2D()->SetLabelVisibility((state ? 1 : 0));
1111        } else if (axis == Y_AXIS) {
1112            _cubeAxesActor2D->GetYAxisActor2D()->SetLabelVisibility((state ? 1 : 0));
1113        } else if (axis == Z_AXIS) {
1114            _cubeAxesActor2D->GetZAxisActor2D()->SetLabelVisibility((state ? 1 : 0));
1115        }
1116        _needsRedraw = true;
1117    }
1118}
1119
1120/**
1121 * \brief Toggle tick visibility for the specified axis
1122 */
1123void Renderer::setAxisTickVisibility(Axis axis, bool state)
1124{
1125    if (_cubeAxesActor != NULL) {
1126        if (axis == X_AXIS) {
1127            _cubeAxesActor->SetXAxisTickVisibility((state ? 1 : 0));
1128        } else if (axis == Y_AXIS) {
1129            _cubeAxesActor->SetYAxisTickVisibility((state ? 1 : 0));
1130        } else if (axis == Z_AXIS) {
1131            _cubeAxesActor->SetZAxisTickVisibility((state ? 1 : 0));
1132        }
1133        _needsRedraw = true;
1134    }
1135    if (_cubeAxesActor2D != NULL) {
1136        if (axis == X_AXIS) {
1137            _cubeAxesActor2D->GetXAxisActor2D()->SetTickVisibility((state ? 1 : 0));
1138        } else if (axis == Y_AXIS) {
1139            _cubeAxesActor2D->GetYAxisActor2D()->SetTickVisibility((state ? 1 : 0));
1140        } else if (axis == Z_AXIS) {
1141            _cubeAxesActor2D->GetZAxisActor2D()->SetTickVisibility((state ? 1 : 0));
1142        }
1143        _needsRedraw = true;
1144    }
1145}
1146
1147/**
1148 * \brief Set title of the specified axis
1149 */
1150void Renderer::setAxisTitle(Axis axis, const char *title)
1151{
1152    if (_cubeAxesActor != NULL) {
1153        if (axis == X_AXIS) {
1154            _cubeAxesActor->SetXTitle(title);
1155        } else if (axis == Y_AXIS) {
1156            _cubeAxesActor->SetYTitle(title);
1157        } else if (axis == Z_AXIS) {
1158            _cubeAxesActor->SetZTitle(title);
1159        }
1160        _needsRedraw = true;
1161    }
1162    if (_cubeAxesActor2D != NULL) {
1163        if (axis == X_AXIS) {
1164            _cubeAxesActor2D->SetXLabel(title);
1165        } else if (axis == Y_AXIS) {
1166            _cubeAxesActor2D->SetYLabel(title);
1167        } else if (axis == Z_AXIS) {
1168            _cubeAxesActor2D->SetZLabel(title);
1169        }
1170        _needsRedraw = true;
1171    }
1172}
1173
1174/**
1175 * \brief Set units of the specified axis
1176 */
1177void Renderer::setAxisUnits(Axis axis, const char *units)
1178{
1179    if (_cubeAxesActor != NULL) {
1180        if (axis == X_AXIS) {
1181            _cubeAxesActor->SetXUnits(units);
1182        } else if (axis == Y_AXIS) {
1183            _cubeAxesActor->SetYUnits(units);
1184        } else if (axis == Z_AXIS) {
1185            _cubeAxesActor->SetZUnits(units);
1186        }
1187        _needsRedraw = true;
1188    }
1189#ifdef notdef
1190    if (_cubeAxesActor2D != NULL) {
1191        if (axis == X_AXIS) {
1192            _cubeAxesActor2D->SetXUnits(units);
1193        } else if (axis == Y_AXIS) {
1194            _cubeAxesActor2D->SetYUnits(units);
1195        } else if (axis == Z_AXIS) {
1196            _cubeAxesActor2D->SetZUnits(units);
1197        }
1198        _needsRedraw = true;
1199    }
1200#endif
1201}
1202
1203/**
1204 * \brief Notify graphics objects that color map has changed
1205 */
1206void Renderer::updateColorMap(ColorMap *cmap)
1207{
1208    for (Contour3DHashmap::iterator itr = _contour3Ds.begin();
1209         itr != _contour3Ds.end(); ++itr) {
1210        if (itr->second->getColorMap() == cmap) {
1211            itr->second->updateColorMap();
1212            _needsRedraw = true;
1213        }
1214    }
1215    for (GlyphsHashmap::iterator itr = _glyphs.begin();
1216         itr != _glyphs.end(); ++itr) {
1217        if (itr->second->getColorMap() == cmap) {
1218            itr->second->updateColorMap();
1219            _needsRedraw = true;
1220        }
1221    }
1222    for (HeightMapHashmap::iterator itr = _heightMaps.begin();
1223         itr != _heightMaps.end(); ++itr) {
1224        if (itr->second->getColorMap() == cmap) {
1225            itr->second->updateColorMap();
1226            _needsRedraw = true;
1227        }
1228    }
1229    for (LICHashmap::iterator itr = _lics.begin();
1230         itr != _lics.end(); ++itr) {
1231        if (itr->second->getColorMap() == cmap) {
1232            itr->second->updateColorMap();
1233            _needsRedraw = true;
1234        }
1235    }
1236    for (MoleculeHashmap::iterator itr = _molecules.begin();
1237         itr != _molecules.end(); ++itr) {
1238        if (itr->second->getColorMap() == cmap) {
1239            itr->second->updateColorMap();
1240            _needsRedraw = true;
1241        }
1242    }
1243    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
1244         itr != _pseudoColors.end(); ++itr) {
1245        if (itr->second->getColorMap() == cmap) {
1246            itr->second->updateColorMap();
1247            _needsRedraw = true;
1248        }
1249    }
1250    for (StreamlinesHashmap::iterator itr = _streamlines.begin();
1251         itr != _streamlines.end(); ++itr) {
1252        if (itr->second->getColorMap() == cmap) {
1253            itr->second->updateColorMap();
1254            _needsRedraw = true;
1255        }
1256    }
1257    for (VolumeHashmap::iterator itr = _volumes.begin();
1258         itr != _volumes.end(); ++itr) {
1259        if (itr->second->getColorMap() == cmap) {
1260            itr->second->updateColorMap();
1261            _needsRedraw = true;
1262        }
1263    }
1264}
1265
1266/**
1267 * \brief Check if a ColorMap is in use by graphics objects
1268 */
1269bool Renderer::colorMapUsed(ColorMap *cmap)
1270{
1271    for (Contour3DHashmap::iterator itr = _contour3Ds.begin();
1272         itr != _contour3Ds.end(); ++itr) {
1273        if (itr->second->getColorMap() == cmap)
1274            return true;
1275    }
1276    for (GlyphsHashmap::iterator itr = _glyphs.begin();
1277         itr != _glyphs.end(); ++itr) {
1278        if (itr->second->getColorMap() == cmap)
1279            return true;
1280    }
1281    for (HeightMapHashmap::iterator itr = _heightMaps.begin();
1282         itr != _heightMaps.end(); ++itr) {
1283        if (itr->second->getColorMap() == cmap)
1284            return true;
1285    }
1286    for (LICHashmap::iterator itr = _lics.begin();
1287         itr != _lics.end(); ++itr) {
1288        if (itr->second->getColorMap() == cmap)
1289            return true;
1290    }
1291    for (MoleculeHashmap::iterator itr = _molecules.begin();
1292         itr != _molecules.end(); ++itr) {
1293        if (itr->second->getColorMap() == cmap)
1294            return true;
1295    }
1296    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
1297         itr != _pseudoColors.end(); ++itr) {
1298        if (itr->second->getColorMap() == cmap)
1299            return true;
1300    }
1301    for (StreamlinesHashmap::iterator itr = _streamlines.begin();
1302         itr != _streamlines.end(); ++itr) {
1303        if (itr->second->getColorMap() == cmap)
1304            return true;
1305    }
1306    for (VolumeHashmap::iterator itr = _volumes.begin();
1307         itr != _volumes.end(); ++itr) {
1308        if (itr->second->getColorMap() == cmap)
1309            return true;
1310    }
1311    return false;
1312}
1313
1314/**
1315 * \brief Add/replace a ColorMap for use in the Renderer
1316 */
1317void Renderer::addColorMap(const ColorMapId& id, ColorMap *colorMap)
1318{
1319    if (colorMap != NULL) {
1320        colorMap->build();
1321        if (getColorMap(id) != NULL) {
1322            TRACE("Replacing existing ColorMap %s", id.c_str());
1323            // Copy to current colormap to avoid invalidating
1324            // pointers in graphics objects using the color map
1325            *_colorMaps[id] = *colorMap;
1326            delete colorMap;
1327            // Notify graphics objects of change
1328            updateColorMap(_colorMaps[id]);
1329        } else
1330            _colorMaps[id] = colorMap;
1331    } else {
1332        ERROR("NULL ColorMap");
1333    }
1334}
1335
1336/**
1337 * \brief Return the ColorMap associated with the colormap key given
1338 */
1339ColorMap *Renderer::getColorMap(const ColorMapId& id)
1340{
1341    ColorMapHashmap::iterator itr = _colorMaps.find(id);
1342
1343    if (itr == _colorMaps.end())
1344        return NULL;
1345    else
1346        return itr->second;
1347}
1348
1349/**
1350 * \brief Remove the colormap associated with the key given
1351 *
1352 * The underlying vtkLookupTable will be deleted if it is not referenced
1353 * by any other objects
1354 */
1355void Renderer::deleteColorMap(const ColorMapId& id)
1356{
1357    ColorMapHashmap::iterator itr;
1358
1359    bool doAll = false;
1360
1361    if (id.compare("all") == 0) {
1362        itr = _colorMaps.begin();
1363        doAll = true;
1364    } else {
1365        itr = _colorMaps.find(id);
1366    }
1367
1368    if (itr == _colorMaps.end()) {
1369        ERROR("Unknown ColorMap %s", id.c_str());
1370        return;
1371    }
1372
1373    do {
1374        if (itr->second->getName().compare("default") == 0 ||
1375            itr->second->getName().compare("grayDefault") == 0 ||
1376            itr->second->getName().compare("volumeDefault") == 0 ||
1377            itr->second->getName().compare("elementDefault") == 0) {
1378            if (id.compare("all") != 0) {
1379                WARN("Cannot delete a default color map");
1380            }
1381            continue;
1382        } else if (colorMapUsed(itr->second)) {
1383            WARN("Cannot delete color map '%s', it is in use", itr->second->getName().c_str());
1384            continue;
1385        }
1386
1387        TRACE("Deleting ColorMap %s", itr->second->getName().c_str());
1388
1389        delete itr->second;
1390        itr = _colorMaps.erase(itr);
1391    } while (doAll && itr != _colorMaps.end());
1392}
1393
1394/**
1395 * \brief Render a labelled legend image for the given colormap
1396 *
1397 * \param[in] id ColorMap name
1398 * \param[in] dataSetID DataSet name
1399 * \param[in] legendType scalar or vector field legend
1400 * \param[in,out] title If supplied, draw title ("#auto" means to
1401 * fill in field name and draw).  If blank, do not draw title. 
1402 * If title was blank or "#auto", will be filled with field name on
1403 * return
1404 * \param[out] range Filled with min and max values
1405 * \param[in] width Pixel width of legend (aspect controls orientation)
1406 * \param[in] height Pixel height of legend (aspect controls orientation)
1407 * \param[in] numLabels Number of labels to render (includes min/max)
1408 * \param[in,out] imgData Pointer to array to fill with image bytes. Array
1409 * will be resized if needed.
1410 * \return The image is rendered into the supplied array, false is
1411 * returned if the color map is not found
1412 */
1413bool Renderer::renderColorMap(const ColorMapId& id,
1414                              const DataSetId& dataSetID,
1415                              Renderer::LegendType legendType,
1416                              std::string& title,
1417                              double range[2],
1418                              int width, int height,
1419                              int numLabels,
1420                              vtkUnsignedCharArray *imgData)
1421{
1422    TRACE("Enter");
1423    ColorMap *colorMap = getColorMap(id);
1424    if (colorMap == NULL)
1425        return false;
1426
1427    if (_legendRenderWindow == NULL) {
1428        _legendRenderWindow = vtkSmartPointer<vtkRenderWindow>::New();
1429#ifdef USE_OFFSCREEN_RENDERING
1430        _legendRenderWindow->DoubleBufferOff();
1431        _legendRenderWindow->OffScreenRenderingOn();
1432#else
1433        _legendRenderWindow->DoubleBufferOn();
1434        _legendRenderWindow->SwapBuffersOff();
1435#endif
1436    }
1437
1438    _legendRenderWindow->SetSize(width, height);
1439
1440    if (_legendRenderer == NULL) {
1441        _legendRenderer = vtkSmartPointer<vtkRenderer>::New();
1442        _legendRenderWindow->AddRenderer(_legendRenderer);
1443    }
1444    _legendRenderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
1445
1446    if (_scalarBarActor == NULL) {
1447        _scalarBarActor = vtkSmartPointer<vtkScalarBarActor>::New();
1448        _scalarBarActor->UseOpacityOn();
1449        _legendRenderer->AddViewProp(_scalarBarActor);
1450    }
1451
1452    if (width > height) {
1453        _scalarBarActor->SetOrientationToHorizontal();
1454    } else {
1455        _scalarBarActor->SetOrientationToVertical();
1456    }
1457
1458    // Set viewport-relative width/height/pos
1459    if (title.empty() && numLabels == 0) {
1460        _scalarBarActor->SetPosition(0, 0);
1461        if (width > height) {
1462            // horizontal
1463            _scalarBarActor->SetHeight(2.5); // VTK: floor(actorHeight * .4)
1464            _scalarBarActor->SetWidth(1);
1465        } else {
1466            // vertical
1467            _scalarBarActor->SetHeight((double)height/(.86*height)); // VTK: floor(actorHeight * .86)
1468            _scalarBarActor->SetWidth(((double)(width+5))/((double)width)); // VTK: actorWidth - 4 pixels
1469        }
1470    } else {
1471        if (width > height) {
1472            // horizontal
1473            _scalarBarActor->SetPosition(.075, .1);
1474            _scalarBarActor->SetHeight(0.8);
1475            _scalarBarActor->SetWidth(0.85);
1476        } else {
1477            // vertical
1478            _scalarBarActor->SetPosition(.1, .05);
1479            _scalarBarActor->SetHeight(0.9);
1480            _scalarBarActor->SetWidth(0.8);
1481        }
1482    }
1483
1484    vtkSmartPointer<vtkLookupTable> lut = colorMap->getLookupTable();
1485    DataSet *dataSet = NULL;
1486    bool cumulative = _useCumulativeRange;
1487    if (dataSetID.compare("all") == 0) {
1488        if (_dataSets.empty()) {
1489            WARN("No DataSets exist, can't fill title or range");
1490        } else {
1491            dataSet = _dataSets.begin()->second;
1492        }
1493        cumulative = true;
1494    } else {
1495        dataSet = getDataSet(dataSetID);
1496        if (dataSet == NULL) {
1497            ERROR("DataSet '%s' not found", dataSetID.c_str());
1498            return false;
1499        }
1500    }
1501
1502    bool drawTitle = false;
1503    if (!title.empty()) {
1504        drawTitle = true;
1505        if (title.compare("#auto") == 0) {
1506            title.clear();
1507        }
1508    }
1509
1510    range[0] = 0.0;
1511    range[1] = 1.0;
1512
1513    switch (legendType) {
1514    case ACTIVE_VECTOR_MAGNITUDE:
1515        if (cumulative) {
1516            lut->SetRange(_cumulativeVectorMagnitudeRange);
1517            range[0] = _cumulativeVectorMagnitudeRange[0];
1518            range[1] = _cumulativeVectorMagnitudeRange[1];
1519        } else if (dataSet != NULL) {
1520            dataSet->getVectorRange(range);
1521            lut->SetRange(range);
1522        }
1523        if (title.empty() && dataSet != NULL) {
1524            const char *name = dataSet->getActiveVectorsName();
1525            if (name != NULL) {
1526                title = name;
1527                title.append("(mag)");
1528            }
1529        }
1530        break;
1531    case ACTIVE_VECTOR_X:
1532        if (cumulative) {
1533            lut->SetRange(_cumulativeVectorComponentRange[0]);
1534            range[0] = _cumulativeVectorComponentRange[0][0];
1535            range[1] = _cumulativeVectorComponentRange[0][1];
1536        } else if (dataSet != NULL) {
1537            dataSet->getVectorRange(range, 0);
1538            lut->SetRange(range);
1539        }
1540        if (title.empty() && dataSet != NULL) {
1541            const char *name = dataSet->getActiveVectorsName();
1542            if (name != NULL) {
1543                title = name;
1544                title.append("(x)");
1545            }
1546        }
1547        break;
1548    case ACTIVE_VECTOR_Y:
1549        if (cumulative) {
1550            lut->SetRange(_cumulativeVectorComponentRange[1]);
1551            range[0] = _cumulativeVectorComponentRange[1][0];
1552            range[1] = _cumulativeVectorComponentRange[1][1];
1553        } else if (dataSet != NULL) {
1554            dataSet->getVectorRange(range, 1);
1555            lut->SetRange(range);
1556        }
1557        if (title.empty() && dataSet != NULL) {
1558            const char *name = dataSet->getActiveVectorsName();
1559            if (name != NULL) {
1560                title = name;
1561                title.append("(y)");
1562            }
1563        }
1564        break;
1565    case ACTIVE_VECTOR_Z:
1566        if (cumulative) {
1567            lut->SetRange(_cumulativeVectorComponentRange[2]);
1568            range[0] = _cumulativeVectorComponentRange[2][0];
1569            range[1] = _cumulativeVectorComponentRange[2][1];
1570        } else if (dataSet != NULL) {
1571            dataSet->getVectorRange(range, 1);
1572            lut->SetRange(range);
1573        }
1574        if (title.empty() && dataSet != NULL) {
1575            const char *name = dataSet->getActiveVectorsName();
1576            if (name != NULL) {
1577                title = name;
1578                title.append("(z)");
1579            }
1580        }
1581        break;
1582    case ACTIVE_SCALAR:
1583    default:
1584        if (cumulative) {
1585            lut->SetRange(_cumulativeScalarRange);
1586            range[0] = _cumulativeScalarRange[0];
1587            range[1] = _cumulativeScalarRange[1];
1588        } else if (dataSet != NULL) {
1589            dataSet->getScalarRange(range);
1590            lut->SetRange(range);
1591        }
1592        if (title.empty() && dataSet != NULL) {
1593            const char *name = dataSet->getActiveScalarsName();
1594            if (name != NULL)
1595                title = name;
1596        }
1597        break;
1598    }
1599
1600    _scalarBarActor->SetLookupTable(lut);
1601
1602    if (drawTitle) {
1603        _scalarBarActor->SetTitle(title.c_str());
1604    } else {
1605        _scalarBarActor->SetTitle("");
1606    }
1607    _scalarBarActor->GetTitleTextProperty()->ItalicOff();
1608    _scalarBarActor->SetNumberOfLabels(numLabels);
1609    _scalarBarActor->GetLabelTextProperty()->BoldOff();
1610    _scalarBarActor->GetLabelTextProperty()->ItalicOff();
1611    _scalarBarActor->GetLabelTextProperty()->ShadowOff();
1612
1613    _legendRenderWindow->Render();
1614
1615#ifdef RENDER_TARGA
1616    _legendRenderWindow->MakeCurrent();
1617    // Must clear previous errors first.
1618    while (glGetError() != GL_NO_ERROR){
1619        ;
1620    }
1621    int bytesPerPixel = TARGA_BYTES_PER_PIXEL;
1622    int size = bytesPerPixel * width * height;
1623
1624    if (imgData->GetMaxId() + 1 != size)
1625    {
1626        imgData->SetNumberOfComponents(bytesPerPixel);
1627        imgData->SetNumberOfValues(size);
1628    }
1629    glDisable(GL_TEXTURE_2D);
1630    glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_renderWindow)->GetFrontLeftBuffer()));
1631    glPixelStorei(GL_PACK_ALIGNMENT, 1);
1632    if (bytesPerPixel == 4) {
1633        glReadPixels(0, 0, width, height, GL_BGRA,
1634                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
1635    } else {
1636        glReadPixels(0, 0, width, height, GL_BGR,
1637                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
1638    }
1639    if (glGetError() != GL_NO_ERROR) {
1640        ERROR("glReadPixels");
1641    }
1642#else
1643    _legendRenderWindow->GetPixelData(0, 0, width-1, height-1,
1644                                      !_legendRenderWindow->GetDoubleBuffer(),
1645                                      imgData);
1646#endif
1647    TRACE("Leave");
1648    return true;
1649}
1650
1651/**
1652 * \brief Create a new Contour2D and associate it with the named DataSet
1653 */
1654bool Renderer::addContour2D(const DataSetId& id, int numContours)
1655{
1656    DataSetHashmap::iterator itr;
1657
1658    bool doAll = false;
1659
1660    if (id.compare("all") == 0) {
1661        itr = _dataSets.begin();
1662    } else {
1663        itr = _dataSets.find(id);
1664    }
1665    if (itr == _dataSets.end()) {
1666        ERROR("Unknown dataset %s", id.c_str());
1667        return false;
1668    }
1669
1670    do {
1671        DataSet *ds = itr->second;
1672        const DataSetId& dsID = ds->getName();
1673
1674        if (getContour2D(dsID)) {
1675            WARN("Replacing existing Contour2D %s", dsID.c_str());
1676            deleteContour2D(dsID);
1677        }
1678
1679        Contour2D *contour = new Contour2D(numContours);
1680 
1681        contour->setDataSet(ds,
1682                            _useCumulativeRange,
1683                            _cumulativeScalarRange,
1684                            _cumulativeVectorMagnitudeRange,
1685                            _cumulativeVectorComponentRange);
1686
1687        if (contour->getProp() == NULL) {
1688            delete contour;
1689            return false;
1690        } else {
1691            _renderer->AddViewProp(contour->getProp());
1692        }
1693
1694        _contour2Ds[dsID] = contour;
1695    } while (doAll && ++itr != _dataSets.end());
1696
1697    initCamera();
1698    _needsRedraw = true;
1699    return true;
1700}
1701
1702/**
1703 * \brief Create a new Contour2D and associate it with the named DataSet
1704 */
1705bool Renderer::addContour2D(const DataSetId& id, const std::vector<double>& contours)
1706{
1707    DataSetHashmap::iterator itr;
1708
1709    bool doAll = false;
1710
1711    if (id.compare("all") == 0) {
1712        itr = _dataSets.begin();
1713    } else {
1714        itr = _dataSets.find(id);
1715    }
1716    if (itr == _dataSets.end()) {
1717        ERROR("Unknown dataset %s", id.c_str());
1718        return false;
1719    }
1720
1721    do {
1722        DataSet *ds = itr->second;
1723        const DataSetId& dsID = ds->getName();
1724
1725        if (getContour2D(dsID)) {
1726            WARN("Replacing existing Contour2D %s", dsID.c_str());
1727            deleteContour2D(dsID);
1728        }
1729
1730        Contour2D *contour = new Contour2D(contours);
1731
1732        contour->setDataSet(ds,
1733                            _useCumulativeRange,
1734                            _cumulativeScalarRange,
1735                            _cumulativeVectorMagnitudeRange,
1736                            _cumulativeVectorComponentRange);
1737
1738        if (contour->getProp() == NULL) {
1739            delete contour;
1740            return false;
1741        } else {
1742            _renderer->AddViewProp(contour->getProp());
1743        }
1744
1745        _contour2Ds[dsID] = contour;
1746    } while (doAll && ++itr != _dataSets.end());
1747
1748    initCamera();
1749    _needsRedraw = true;
1750    return true;
1751}
1752
1753/**
1754 * \brief Get the Contour2D associated with a named DataSet
1755 */
1756Contour2D *Renderer::getContour2D(const DataSetId& id)
1757{
1758    Contour2DHashmap::iterator itr = _contour2Ds.find(id);
1759
1760    if (itr == _contour2Ds.end()) {
1761#ifdef DEBUG
1762        TRACE("Contour2D not found: %s", id.c_str());
1763#endif
1764        return NULL;
1765    } else
1766        return itr->second;
1767}
1768
1769/**
1770 * \brief Set the prop orientation with a quaternion
1771 */
1772void Renderer::setContour2DTransform(const DataSetId& id, vtkMatrix4x4 *trans)
1773{
1774    Contour2DHashmap::iterator itr;
1775
1776    bool doAll = false;
1777
1778    if (id.compare("all") == 0) {
1779        itr = _contour2Ds.begin();
1780        doAll = true;
1781    } else {
1782        itr = _contour2Ds.find(id);
1783    }
1784    if (itr == _contour2Ds.end()) {
1785        ERROR("Contour2D not found: %s", id.c_str());
1786        return;
1787    }
1788
1789    do {
1790        itr->second->setTransform(trans);
1791    } while (doAll && ++itr != _contour2Ds.end());
1792
1793    resetAxes();
1794    _needsRedraw = true;
1795}
1796
1797/**
1798 * \brief Set the prop orientation with a quaternion
1799 */
1800void Renderer::setContour2DOrientation(const DataSetId& id, double quat[4])
1801{
1802    Contour2DHashmap::iterator itr;
1803
1804    bool doAll = false;
1805
1806    if (id.compare("all") == 0) {
1807        itr = _contour2Ds.begin();
1808        doAll = true;
1809    } else {
1810        itr = _contour2Ds.find(id);
1811    }
1812    if (itr == _contour2Ds.end()) {
1813        ERROR("Contour2D not found: %s", id.c_str());
1814        return;
1815    }
1816
1817    do {
1818        itr->second->setOrientation(quat);
1819    } while (doAll && ++itr != _contour2Ds.end());
1820
1821    resetAxes();
1822    _needsRedraw = true;
1823}
1824
1825/**
1826 * \brief Set the prop orientation with a rotation about an axis
1827 */
1828void Renderer::setContour2DOrientation(const DataSetId& id, double angle, double axis[3])
1829{
1830    Contour2DHashmap::iterator itr;
1831
1832    bool doAll = false;
1833
1834    if (id.compare("all") == 0) {
1835        itr = _contour2Ds.begin();
1836        doAll = true;
1837    } else {
1838        itr = _contour2Ds.find(id);
1839    }
1840    if (itr == _contour2Ds.end()) {
1841        ERROR("Contour2D not found: %s", id.c_str());
1842        return;
1843    }
1844
1845    do {
1846        itr->second->setOrientation(angle, axis);
1847    } while (doAll && ++itr != _contour2Ds.end());
1848
1849    resetAxes();
1850    _needsRedraw = true;
1851}
1852
1853/**
1854 * \brief Set the prop position in world coords
1855 */
1856void Renderer::setContour2DPosition(const DataSetId& id, double pos[3])
1857{
1858    Contour2DHashmap::iterator itr;
1859
1860    bool doAll = false;
1861
1862    if (id.compare("all") == 0) {
1863        itr = _contour2Ds.begin();
1864        doAll = true;
1865    } else {
1866        itr = _contour2Ds.find(id);
1867    }
1868    if (itr == _contour2Ds.end()) {
1869        ERROR("Contour2D not found: %s", id.c_str());
1870        return;
1871    }
1872
1873    do {
1874        itr->second->setPosition(pos);
1875    } while (doAll && ++itr != _contour2Ds.end());
1876
1877    resetAxes();
1878    _needsRedraw = true;
1879}
1880
1881/**
1882 * \brief Set the prop scaling
1883 */
1884void Renderer::setContour2DScale(const DataSetId& id, double scale[3])
1885{
1886    Contour2DHashmap::iterator itr;
1887
1888    bool doAll = false;
1889
1890    if (id.compare("all") == 0) {
1891        itr = _contour2Ds.begin();
1892        doAll = true;
1893    } else {
1894        itr = _contour2Ds.find(id);
1895    }
1896    if (itr == _contour2Ds.end()) {
1897        ERROR("Contour2D not found: %s", id.c_str());
1898        return;
1899    }
1900
1901    do {
1902        itr->second->setScale(scale);
1903    } while (doAll && ++itr != _contour2Ds.end());
1904
1905    resetAxes();
1906    _needsRedraw = true;
1907}
1908
1909/**
1910 * \brief Set the number of equally spaced contour isolines for the given DataSet
1911 */
1912void Renderer::setContour2DContours(const DataSetId& id, int numContours)
1913{
1914    Contour2DHashmap::iterator itr;
1915
1916    bool doAll = false;
1917
1918    if (id.compare("all") == 0) {
1919        itr = _contour2Ds.begin();
1920        doAll = true;
1921    } else {
1922        itr = _contour2Ds.find(id);
1923    }
1924    if (itr == _contour2Ds.end()) {
1925        ERROR("Contour2D not found: %s", id.c_str());
1926        return;
1927    }
1928
1929    do {
1930        itr->second->setContours(numContours);
1931    } while (doAll && ++itr != _contour2Ds.end());
1932
1933    _needsRedraw = true;
1934}
1935
1936/**
1937 * \brief Set a list of isovalues for the given DataSet
1938 */
1939void Renderer::setContour2DContourList(const DataSetId& id, const std::vector<double>& contours)
1940{
1941    Contour2DHashmap::iterator itr;
1942
1943    bool doAll = false;
1944
1945    if (id.compare("all") == 0) {
1946        itr = _contour2Ds.begin();
1947        doAll = true;
1948    } else {
1949        itr = _contour2Ds.find(id);
1950    }
1951    if (itr == _contour2Ds.end()) {
1952        ERROR("Contour2D not found: %s", id.c_str());
1953        return;
1954    }
1955
1956    do {
1957        itr->second->setContourList(contours);
1958    } while (doAll && ++itr != _contour2Ds.end());
1959
1960     _needsRedraw = true;
1961}
1962
1963/**
1964 * \brief Set opacity of contour lines for the given DataSet
1965 */
1966void Renderer::setContour2DOpacity(const DataSetId& id, double opacity)
1967{
1968    Contour2DHashmap::iterator itr;
1969
1970    bool doAll = false;
1971
1972    if (id.compare("all") == 0) {
1973        itr = _contour2Ds.begin();
1974        doAll = true;
1975    } else {
1976        itr = _contour2Ds.find(id);
1977    }
1978    if (itr == _contour2Ds.end()) {
1979        ERROR("Contour2D not found: %s", id.c_str());
1980        return;
1981    }
1982
1983    do {
1984        itr->second->setOpacity(opacity);
1985    } while (doAll && ++itr != _contour2Ds.end());
1986
1987    _needsRedraw = true;
1988}
1989
1990/**
1991 * \brief Turn on/off rendering contour lines for the given DataSet
1992 */
1993void Renderer::setContour2DVisibility(const DataSetId& id, bool state)
1994{
1995    Contour2DHashmap::iterator itr;
1996
1997    bool doAll = false;
1998
1999    if (id.compare("all") == 0) {
2000        itr = _contour2Ds.begin();
2001        doAll = true;
2002    } else {
2003        itr = _contour2Ds.find(id);
2004    }
2005    if (itr == _contour2Ds.end()) {
2006        ERROR("Contour2D not found: %s", id.c_str());
2007        return;
2008    }
2009
2010    do {
2011        itr->second->setVisibility(state);
2012    } while (doAll && ++itr != _contour2Ds.end());
2013
2014    _needsRedraw = true;
2015}
2016
2017/**
2018 * \brief Set the RGB isoline color for the specified DataSet
2019 */
2020void Renderer::setContour2DColor(const DataSetId& id, float color[3])
2021{
2022    Contour2DHashmap::iterator itr;
2023
2024    bool doAll = false;
2025
2026    if (id.compare("all") == 0) {
2027        itr = _contour2Ds.begin();
2028        doAll = true;
2029    } else {
2030        itr = _contour2Ds.find(id);
2031    }
2032    if (itr == _contour2Ds.end()) {
2033        ERROR("Contour2D not found: %s", id.c_str());
2034        return;
2035    }
2036
2037    do {
2038        itr->second->setColor(color);
2039    } while (doAll && ++itr != _contour2Ds.end());
2040
2041    _needsRedraw = true;
2042}
2043
2044/**
2045 * \brief Set the isoline width for the specified DataSet (may be a no-op)
2046 *
2047 * If the OpenGL implementation/hardware does not support wide lines,
2048 * this function may not have an effect.
2049 */
2050void Renderer::setContour2DEdgeWidth(const DataSetId& id, float edgeWidth)
2051{
2052    Contour2DHashmap::iterator itr;
2053
2054    bool doAll = false;
2055
2056    if (id.compare("all") == 0) {
2057        itr = _contour2Ds.begin();
2058        doAll = true;
2059    } else {
2060        itr = _contour2Ds.find(id);
2061    }
2062    if (itr == _contour2Ds.end()) {
2063        ERROR("Contour2D not found: %s", id.c_str());
2064        return;
2065    }
2066
2067    do {
2068        itr->second->setEdgeWidth(edgeWidth);
2069    } while (doAll && ++itr != _contour2Ds.end());
2070
2071    _needsRedraw = true;
2072}
2073
2074/**
2075 * \brief Turn contour lighting on/off for the specified DataSet
2076 */
2077void Renderer::setContour2DLighting(const DataSetId& id, bool state)
2078{
2079    Contour2DHashmap::iterator itr;
2080
2081    bool doAll = false;
2082
2083    if (id.compare("all") == 0) {
2084        itr = _contour2Ds.begin();
2085        doAll = true;
2086    } else {
2087        itr = _contour2Ds.find(id);
2088    }
2089    if (itr == _contour2Ds.end()) {
2090        ERROR("Contour2D not found: %s", id.c_str());
2091        return;
2092    }
2093
2094    do {
2095        itr->second->setLighting(state);
2096    } while (doAll && ++itr != _contour2Ds.end());
2097    _needsRedraw = true;
2098}
2099
2100/**
2101 * \brief Create a new Contour3D and associate it with the named DataSet
2102 */
2103bool Renderer::addContour3D(const DataSetId& id, int numContours)
2104{
2105    DataSetHashmap::iterator itr;
2106
2107    bool doAll = false;
2108
2109    if (id.compare("all") == 0) {
2110        itr = _dataSets.begin();
2111    } else {
2112        itr = _dataSets.find(id);
2113    }
2114    if (itr == _dataSets.end()) {
2115        ERROR("Unknown dataset %s", id.c_str());
2116        return false;
2117    }
2118
2119    do {
2120        DataSet *ds = itr->second;
2121        const DataSetId& dsID = ds->getName();
2122
2123        if (getContour3D(dsID)) {
2124            WARN("Replacing existing Contour3D %s", dsID.c_str());
2125            deleteContour3D(dsID);
2126        }
2127
2128        Contour3D *contour = new Contour3D(numContours);
2129
2130        contour->setDataSet(ds,
2131                            _useCumulativeRange,
2132                            _cumulativeScalarRange,
2133                            _cumulativeVectorMagnitudeRange,
2134                            _cumulativeVectorComponentRange);
2135
2136        if (contour->getProp() == NULL) {
2137            delete contour;
2138            return false;
2139        } else {
2140            _renderer->AddViewProp(contour->getProp());
2141        }
2142
2143        _contour3Ds[dsID] = contour;
2144    } while (doAll && ++itr != _dataSets.end());
2145
2146    if (_cameraMode == IMAGE)
2147        setCameraMode(PERSPECTIVE);
2148    initCamera();
2149    _needsRedraw = true;
2150    return true;
2151}
2152
2153/**
2154 * \brief Create a new Contour3D and associate it with the named DataSet
2155 */
2156bool Renderer::addContour3D(const DataSetId& id,const std::vector<double>& contours)
2157{
2158    DataSetHashmap::iterator itr;
2159
2160    bool doAll = false;
2161
2162    if (id.compare("all") == 0) {
2163        itr = _dataSets.begin();
2164    } else {
2165        itr = _dataSets.find(id);
2166    }
2167    if (itr == _dataSets.end()) {
2168        ERROR("Unknown dataset %s", id.c_str());
2169        return false;
2170    }
2171
2172    do {
2173        DataSet *ds = itr->second;
2174        const DataSetId& dsID = ds->getName();
2175
2176        if (getContour3D(dsID)) {
2177            WARN("Replacing existing Contour3D %s", dsID.c_str());
2178            deleteContour3D(dsID);
2179        }
2180
2181        Contour3D *contour = new Contour3D(contours);
2182
2183        contour->setDataSet(ds,
2184                            _useCumulativeRange,
2185                            _cumulativeScalarRange,
2186                            _cumulativeVectorMagnitudeRange,
2187                            _cumulativeVectorComponentRange);
2188
2189        if (contour->getProp() == NULL) {
2190            delete contour;
2191            return false;
2192        } else {
2193            _renderer->AddViewProp(contour->getProp());
2194        }
2195
2196        _contour3Ds[dsID] = contour;
2197    } while (doAll && ++itr != _dataSets.end());
2198
2199    if (_cameraMode == IMAGE)
2200        setCameraMode(PERSPECTIVE);
2201    initCamera();
2202    _needsRedraw = true;
2203    return true;
2204}
2205
2206/**
2207 * \brief Get the Contour3D associated with a named DataSet
2208 */
2209Contour3D *Renderer::getContour3D(const DataSetId& id)
2210{
2211    Contour3DHashmap::iterator itr = _contour3Ds.find(id);
2212
2213    if (itr == _contour3Ds.end()) {
2214#ifdef DEBUG
2215        TRACE("Contour3D not found: %s", id.c_str());
2216#endif
2217        return NULL;
2218    } else
2219        return itr->second;
2220}
2221
2222/**
2223 * \brief Set the prop orientation with a quaternion
2224 */
2225void Renderer::setContour3DTransform(const DataSetId& id, vtkMatrix4x4 *trans)
2226{
2227    Contour3DHashmap::iterator itr;
2228
2229    bool doAll = false;
2230
2231    if (id.compare("all") == 0) {
2232        itr = _contour3Ds.begin();
2233        doAll = true;
2234    } else {
2235        itr = _contour3Ds.find(id);
2236    }
2237    if (itr == _contour3Ds.end()) {
2238        ERROR("Contour3D not found: %s", id.c_str());
2239        return;
2240    }
2241
2242    do {
2243        itr->second->setTransform(trans);
2244    } while (doAll && ++itr != _contour3Ds.end());
2245
2246    resetAxes();
2247    _needsRedraw = true;
2248}
2249
2250/**
2251 * \brief Set the prop orientation with a quaternion
2252 */
2253void Renderer::setContour3DOrientation(const DataSetId& id, double quat[4])
2254{
2255    Contour3DHashmap::iterator itr;
2256
2257    bool doAll = false;
2258
2259    if (id.compare("all") == 0) {
2260        itr = _contour3Ds.begin();
2261        doAll = true;
2262    } else {
2263        itr = _contour3Ds.find(id);
2264    }
2265    if (itr == _contour3Ds.end()) {
2266        ERROR("Contour3D not found: %s", id.c_str());
2267        return;
2268    }
2269
2270    do {
2271        itr->second->setOrientation(quat);
2272    } while (doAll && ++itr != _contour3Ds.end());
2273
2274    resetAxes();
2275    _needsRedraw = true;
2276}
2277
2278/**
2279 * \brief Set the prop orientation with a rotation about an axis
2280 */
2281void Renderer::setContour3DOrientation(const DataSetId& id, double angle, double axis[3])
2282{
2283    Contour3DHashmap::iterator itr;
2284
2285    bool doAll = false;
2286
2287    if (id.compare("all") == 0) {
2288        itr = _contour3Ds.begin();
2289        doAll = true;
2290    } else {
2291        itr = _contour3Ds.find(id);
2292    }
2293    if (itr == _contour3Ds.end()) {
2294        ERROR("Contour3D not found: %s", id.c_str());
2295        return;
2296    }
2297
2298    do {
2299        itr->second->setOrientation(angle, axis);
2300    } while (doAll && ++itr != _contour3Ds.end());
2301
2302    resetAxes();
2303    _needsRedraw = true;
2304}
2305
2306/**
2307 * \brief Set the prop position in world coords
2308 */
2309void Renderer::setContour3DPosition(const DataSetId& id, double pos[3])
2310{
2311    Contour3DHashmap::iterator itr;
2312
2313    bool doAll = false;
2314
2315    if (id.compare("all") == 0) {
2316        itr = _contour3Ds.begin();
2317        doAll = true;
2318    } else {
2319        itr = _contour3Ds.find(id);
2320    }
2321    if (itr == _contour3Ds.end()) {
2322        ERROR("Contour3D not found: %s", id.c_str());
2323        return;
2324    }
2325
2326    do {
2327        itr->second->setPosition(pos);
2328    } while (doAll && ++itr != _contour3Ds.end());
2329
2330    resetAxes();
2331    _needsRedraw = true;
2332}
2333
2334/**
2335 * \brief Set the prop scaling
2336 */
2337void Renderer::setContour3DScale(const DataSetId& id, double scale[3])
2338{
2339    Contour3DHashmap::iterator itr;
2340
2341    bool doAll = false;
2342
2343    if (id.compare("all") == 0) {
2344        itr = _contour3Ds.begin();
2345        doAll = true;
2346    } else {
2347        itr = _contour3Ds.find(id);
2348    }
2349    if (itr == _contour3Ds.end()) {
2350        ERROR("Contour3D not found: %s", id.c_str());
2351        return;
2352    }
2353
2354    do {
2355        itr->second->setScale(scale);
2356    } while (doAll && ++itr != _contour3Ds.end());
2357
2358    resetAxes();
2359    _needsRedraw = true;
2360}
2361
2362/**
2363 * \brief Set the number of equally spaced isosurfaces for the given DataSet
2364 */
2365void Renderer::setContour3DContours(const DataSetId& id, int numContours)
2366{
2367    Contour3DHashmap::iterator itr;
2368
2369    bool doAll = false;
2370
2371    if (id.compare("all") == 0) {
2372        itr = _contour3Ds.begin();
2373        doAll = true;
2374    } else {
2375        itr = _contour3Ds.find(id);
2376    }
2377    if (itr == _contour3Ds.end()) {
2378        ERROR("Contour3D not found: %s", id.c_str());
2379        return;
2380    }
2381
2382    do {
2383        itr->second->setContours(numContours);
2384     } while (doAll && ++itr != _contour3Ds.end());
2385
2386    initCamera();
2387    _needsRedraw = true;
2388}
2389
2390/**
2391 * \brief Set a list of isovalues for the given DataSet
2392 */
2393void Renderer::setContour3DContourList(const DataSetId& id, const std::vector<double>& contours)
2394{
2395    Contour3DHashmap::iterator itr;
2396
2397    bool doAll = false;
2398
2399    if (id.compare("all") == 0) {
2400        itr = _contour3Ds.begin();
2401        doAll = true;
2402    } else {
2403        itr = _contour3Ds.find(id);
2404    }
2405    if (itr == _contour3Ds.end()) {
2406        ERROR("Contour3D not found: %s", id.c_str());
2407        return;
2408    }
2409
2410    do {
2411        itr->second->setContourList(contours);
2412    } while (doAll && ++itr != _contour3Ds.end());
2413
2414    initCamera();
2415     _needsRedraw = true;
2416}
2417
2418/**
2419 * \brief Set opacity of isosurfaces for the given DataSet
2420 */
2421void Renderer::setContour3DOpacity(const DataSetId& id, double opacity)
2422{
2423    Contour3DHashmap::iterator itr;
2424
2425    bool doAll = false;
2426
2427    if (id.compare("all") == 0) {
2428        itr = _contour3Ds.begin();
2429        doAll = true;
2430    } else {
2431        itr = _contour3Ds.find(id);
2432    }
2433    if (itr == _contour3Ds.end()) {
2434        ERROR("Contour3D not found: %s", id.c_str());
2435        return;
2436    }
2437
2438    do {
2439        itr->second->setOpacity(opacity);
2440    } while (doAll && ++itr != _contour3Ds.end());
2441
2442    _needsRedraw = true;
2443}
2444
2445/**
2446 * \brief Turn on/off rendering isosurfaces for the given DataSet
2447 */
2448void Renderer::setContour3DVisibility(const DataSetId& id, bool state)
2449{
2450    Contour3DHashmap::iterator itr;
2451
2452    bool doAll = false;
2453
2454    if (id.compare("all") == 0) {
2455        itr = _contour3Ds.begin();
2456        doAll = true;
2457    } else {
2458        itr = _contour3Ds.find(id);
2459    }
2460    if (itr == _contour3Ds.end()) {
2461        ERROR("Contour3D not found: %s", id.c_str());
2462        return;
2463    }
2464
2465    do {
2466        itr->second->setVisibility(state);
2467    } while (doAll && ++itr != _contour3Ds.end());
2468
2469    _needsRedraw = true;
2470}
2471
2472/**
2473 * \brief Associate an existing named color map with a Contour3D for the given
2474 * DataSet
2475 */
2476void Renderer::setContour3DColorMap(const DataSetId& id, const ColorMapId& colorMapId)
2477{
2478    Contour3DHashmap::iterator itr;
2479
2480    bool doAll = false;
2481
2482    if (id.compare("all") == 0) {
2483        itr = _contour3Ds.begin();
2484        doAll = true;
2485    } else {
2486        itr = _contour3Ds.find(id);
2487    }
2488
2489    if (itr == _contour3Ds.end()) {
2490        ERROR("Contour3D not found: %s", id.c_str());
2491        return;
2492    }
2493
2494    ColorMap *cmap = getColorMap(colorMapId);
2495    if (cmap == NULL) {
2496        ERROR("Unknown colormap: %s", colorMapId.c_str());
2497        return;
2498    }
2499
2500    do {
2501        TRACE("Set Contour3D color map: %s for dataset %s", colorMapId.c_str(),
2502              itr->second->getDataSet()->getName().c_str());
2503
2504        itr->second->setColorMap(cmap);
2505    } while (doAll && ++itr != _contour3Ds.end());
2506
2507    _needsRedraw = true;
2508}
2509
2510/**
2511 * \brief Set the RGB isosurface color for the specified DataSet
2512 */
2513void Renderer::setContour3DColor(const DataSetId& id, float color[3])
2514{
2515    Contour3DHashmap::iterator itr;
2516
2517    bool doAll = false;
2518
2519    if (id.compare("all") == 0) {
2520        itr = _contour3Ds.begin();
2521        doAll = true;
2522    } else {
2523        itr = _contour3Ds.find(id);
2524    }
2525    if (itr == _contour3Ds.end()) {
2526        ERROR("Contour3D not found: %s", id.c_str());
2527        return;
2528    }
2529
2530    do {
2531        itr->second->setColor(color);
2532    } while (doAll && ++itr != _contour3Ds.end());
2533    _needsRedraw = true;
2534}
2535
2536/**
2537 * \brief Turn on/off rendering isosurface edges for the given DataSet
2538 */
2539void Renderer::setContour3DEdgeVisibility(const DataSetId& id, bool state)
2540{
2541    Contour3DHashmap::iterator itr;
2542
2543    bool doAll = false;
2544
2545    if (id.compare("all") == 0) {
2546        itr = _contour3Ds.begin();
2547        doAll = true;
2548    } else {
2549        itr = _contour3Ds.find(id);
2550    }
2551    if (itr == _contour3Ds.end()) {
2552        ERROR("Contour3D not found: %s", id.c_str());
2553        return;
2554    }
2555
2556    do {
2557        itr->second->setEdgeVisibility(state);
2558    } while (doAll && ++itr != _contour3Ds.end());
2559
2560    _needsRedraw = true;
2561}
2562
2563/**
2564 * \brief Set the RGB isosurface edge color for the specified DataSet
2565 */
2566void Renderer::setContour3DEdgeColor(const DataSetId& id, float color[3])
2567{
2568    Contour3DHashmap::iterator itr;
2569
2570    bool doAll = false;
2571
2572    if (id.compare("all") == 0) {
2573        itr = _contour3Ds.begin();
2574        doAll = true;
2575    } else {
2576        itr = _contour3Ds.find(id);
2577    }
2578    if (itr == _contour3Ds.end()) {
2579        ERROR("Contour3D not found: %s", id.c_str());
2580        return;
2581    }
2582
2583    do {
2584        itr->second->setEdgeColor(color);
2585    } while (doAll && ++itr != _contour3Ds.end());
2586
2587    _needsRedraw = true;
2588}
2589
2590/**
2591 * \brief Set the isosurface edge width for the specified DataSet (may be a no-op)
2592 *
2593 * If the OpenGL implementation/hardware does not support wide lines,
2594 * this function may not have an effect.
2595 */
2596void Renderer::setContour3DEdgeWidth(const DataSetId& id, float edgeWidth)
2597{
2598    Contour3DHashmap::iterator itr;
2599
2600    bool doAll = false;
2601
2602    if (id.compare("all") == 0) {
2603        itr = _contour3Ds.begin();
2604        doAll = true;
2605    } else {
2606        itr = _contour3Ds.find(id);
2607    }
2608    if (itr == _contour3Ds.end()) {
2609        ERROR("Contour3D not found: %s", id.c_str());
2610        return;
2611    }
2612
2613    do {
2614        itr->second->setEdgeWidth(edgeWidth);
2615    } while (doAll && ++itr != _contour3Ds.end());
2616
2617    _needsRedraw = true;
2618}
2619
2620/**
2621 * \brief Set wireframe rendering for the specified DataSet
2622 */
2623void Renderer::setContour3DWireframe(const DataSetId& id, bool state)
2624{
2625    Contour3DHashmap::iterator itr;
2626
2627    bool doAll = false;
2628
2629    if (id.compare("all") == 0) {
2630        itr = _contour3Ds.begin();
2631        doAll = true;
2632    } else {
2633        itr = _contour3Ds.find(id);
2634    }
2635    if (itr == _contour3Ds.end()) {
2636        ERROR("Contour3D not found: %s", id.c_str());
2637        return;
2638    }
2639
2640    do {
2641        itr->second->setWireframe(state);
2642    } while (doAll && ++itr != _contour3Ds.end());
2643
2644    _needsRedraw = true;
2645}
2646
2647/**
2648 * \brief Turn contour lighting on/off for the specified DataSet
2649 */
2650void Renderer::setContour3DLighting(const DataSetId& id, bool state)
2651{
2652    Contour3DHashmap::iterator itr;
2653
2654    bool doAll = false;
2655
2656    if (id.compare("all") == 0) {
2657        itr = _contour3Ds.begin();
2658        doAll = true;
2659    } else {
2660        itr = _contour3Ds.find(id);
2661    }
2662    if (itr == _contour3Ds.end()) {
2663        ERROR("Contour3D not found: %s", id.c_str());
2664        return;
2665    }
2666
2667    do {
2668        itr->second->setLighting(state);
2669    } while (doAll && ++itr != _contour3Ds.end());
2670    _needsRedraw = true;
2671}
2672
2673/**
2674 * \brief Create a new Glyphs and associate it with the named DataSet
2675 */
2676bool Renderer::addGlyphs(const DataSetId& id, Glyphs::GlyphShape shape)
2677{
2678    DataSetHashmap::iterator itr;
2679
2680    bool doAll = false;
2681
2682    if (id.compare("all") == 0) {
2683        itr = _dataSets.begin();
2684    } else {
2685        itr = _dataSets.find(id);
2686    }
2687    if (itr == _dataSets.end()) {
2688        ERROR("Unknown dataset %s", id.c_str());
2689        return false;
2690    }
2691
2692    do {
2693        DataSet *ds = itr->second;
2694        const DataSetId& dsID = ds->getName();
2695
2696        if (getGlyphs(dsID)) {
2697            WARN("Replacing existing Glyphs %s", dsID.c_str());
2698            deleteGlyphs(dsID);
2699        }
2700
2701        Glyphs *glyphs = new Glyphs(shape);
2702
2703        glyphs->setDataSet(ds,
2704                           _useCumulativeRange,
2705                           _cumulativeScalarRange,
2706                           _cumulativeVectorMagnitudeRange,
2707                           _cumulativeVectorComponentRange);
2708
2709        if (glyphs->getProp() == NULL) {
2710            delete glyphs;
2711            return false;
2712        } else {
2713            _renderer->AddViewProp(glyphs->getProp());
2714        }
2715
2716        _glyphs[dsID] = glyphs;
2717    } while (doAll && ++itr != _dataSets.end());
2718
2719    if (_cameraMode == IMAGE)
2720        setCameraMode(PERSPECTIVE);
2721    initCamera();
2722
2723    _needsRedraw = true;
2724    return true;
2725}
2726
2727/**
2728 * \brief Get the Glyphs associated with a named DataSet
2729 */
2730Glyphs *Renderer::getGlyphs(const DataSetId& id)
2731{
2732    GlyphsHashmap::iterator itr = _glyphs.find(id);
2733
2734    if (itr == _glyphs.end()) {
2735#ifdef DEBUG
2736        TRACE("Glyphs not found: %s", id.c_str());
2737#endif
2738        return NULL;
2739    } else
2740        return itr->second;
2741}
2742
2743/**
2744 * \brief Set the prop orientation with a quaternion
2745 */
2746void Renderer::setGlyphsTransform(const DataSetId& id, vtkMatrix4x4 *trans)
2747{
2748    GlyphsHashmap::iterator itr;
2749
2750    bool doAll = false;
2751
2752    if (id.compare("all") == 0) {
2753        itr = _glyphs.begin();
2754        doAll = true;
2755    } else {
2756        itr = _glyphs.find(id);
2757    }
2758    if (itr == _glyphs.end()) {
2759        ERROR("Glyphs not found: %s", id.c_str());
2760        return;
2761    }
2762
2763    do {
2764        itr->second->setTransform(trans);
2765    } while (doAll && ++itr != _glyphs.end());
2766
2767    resetAxes();
2768    _needsRedraw = true;
2769}
2770
2771/**
2772 * \brief Set the prop orientation with a quaternion
2773 */
2774void Renderer::setGlyphsOrientation(const DataSetId& id, double quat[4])
2775{
2776    GlyphsHashmap::iterator itr;
2777
2778    bool doAll = false;
2779
2780    if (id.compare("all") == 0) {
2781        itr = _glyphs.begin();
2782        doAll = true;
2783    } else {
2784        itr = _glyphs.find(id);
2785    }
2786    if (itr == _glyphs.end()) {
2787        ERROR("Glyphs not found: %s", id.c_str());
2788        return;
2789    }
2790
2791    do {
2792        itr->second->setOrientation(quat);
2793    } while (doAll && ++itr != _glyphs.end());
2794
2795    resetAxes();
2796    _needsRedraw = true;
2797}
2798
2799/**
2800 * \brief Set the prop orientation with a rotation about an axis
2801 */
2802void Renderer::setGlyphsOrientation(const DataSetId& id, double angle, double axis[3])
2803{
2804    GlyphsHashmap::iterator itr;
2805
2806    bool doAll = false;
2807
2808    if (id.compare("all") == 0) {
2809        itr = _glyphs.begin();
2810        doAll = true;
2811    } else {
2812        itr = _glyphs.find(id);
2813    }
2814    if (itr == _glyphs.end()) {
2815        ERROR("Glyphs not found: %s", id.c_str());
2816        return;
2817    }
2818
2819    do {
2820        itr->second->setOrientation(angle, axis);
2821    } while (doAll && ++itr != _glyphs.end());
2822
2823    resetAxes();
2824    _needsRedraw = true;
2825}
2826
2827/**
2828 * \brief Set the prop position in world coords
2829 */
2830void Renderer::setGlyphsPosition(const DataSetId& id, double pos[3])
2831{
2832    GlyphsHashmap::iterator itr;
2833
2834    bool doAll = false;
2835
2836    if (id.compare("all") == 0) {
2837        itr = _glyphs.begin();
2838        doAll = true;
2839    } else {
2840        itr = _glyphs.find(id);
2841    }
2842    if (itr == _glyphs.end()) {
2843        ERROR("Glyphs not found: %s", id.c_str());
2844        return;
2845    }
2846
2847    do {
2848        itr->second->setPosition(pos);
2849    } while (doAll && ++itr != _glyphs.end());
2850
2851    resetAxes();
2852    _needsRedraw = true;
2853}
2854
2855/**
2856 * \brief Set the prop scaling
2857 */
2858void Renderer::setGlyphsScale(const DataSetId& id, double scale[3])
2859{
2860    GlyphsHashmap::iterator itr;
2861
2862    bool doAll = false;
2863
2864    if (id.compare("all") == 0) {
2865        itr = _glyphs.begin();
2866        doAll = true;
2867    } else {
2868        itr = _glyphs.find(id);
2869    }
2870    if (itr == _glyphs.end()) {
2871        ERROR("Glyphs not found: %s", id.c_str());
2872        return;
2873    }
2874
2875    do {
2876        itr->second->setScale(scale);
2877    } while (doAll && ++itr != _glyphs.end());
2878
2879    resetAxes();
2880    _needsRedraw = true;
2881}
2882
2883/**
2884 * \brief Set the RGB polygon color for the specified DataSet
2885 */
2886void Renderer::setGlyphsColor(const DataSetId& id, float color[3])
2887{
2888    GlyphsHashmap::iterator itr;
2889
2890    bool doAll = false;
2891
2892    if (id.compare("all") == 0) {
2893        itr = _glyphs.begin();
2894        doAll = true;
2895    } else {
2896        itr = _glyphs.find(id);
2897    }
2898    if (itr == _glyphs.end()) {
2899        ERROR("Glyphs not found: %s", id.c_str());
2900        return;
2901    }
2902
2903    do {
2904        itr->second->setColor(color);
2905    } while (doAll && ++itr != _glyphs.end());
2906
2907    _needsRedraw = true;
2908}
2909
2910/**
2911 * \brief Associate an existing named color map with a Glyphs for the given DataSet
2912 */
2913void Renderer::setGlyphsColorMap(const DataSetId& id, const ColorMapId& colorMapId)
2914{
2915    GlyphsHashmap::iterator itr;
2916
2917    bool doAll = false;
2918
2919    if (id.compare("all") == 0) {
2920        itr = _glyphs.begin();
2921        doAll = true;
2922    } else {
2923        itr = _glyphs.find(id);
2924    }
2925
2926    if (itr == _glyphs.end()) {
2927        ERROR("Glyphs not found: %s", id.c_str());
2928        return;
2929    }
2930
2931    ColorMap *cmap = getColorMap(colorMapId);
2932    if (cmap == NULL) {
2933        ERROR("Unknown colormap: %s", colorMapId.c_str());
2934        return;
2935    }
2936
2937    do {
2938        TRACE("Set Glyphs color map: %s for dataset %s", colorMapId.c_str(),
2939              itr->second->getDataSet()->getName().c_str());
2940
2941        itr->second->setColorMap(cmap);
2942    } while (doAll && ++itr != _glyphs.end());
2943
2944    _needsRedraw = true;
2945}
2946
2947/**
2948 * \brief Controls the array used to color glyphs for the given DataSet
2949 */
2950void Renderer::setGlyphsColorMode(const DataSetId& id, Glyphs::ColorMode mode)
2951{
2952    GlyphsHashmap::iterator itr;
2953
2954    bool doAll = false;
2955
2956    if (id.compare("all") == 0) {
2957        itr = _glyphs.begin();
2958        doAll = true;
2959    } else {
2960        itr = _glyphs.find(id);
2961    }
2962    if (itr == _glyphs.end()) {
2963        ERROR("Glyphs not found: %s", id.c_str());
2964        return;
2965    }
2966
2967    do {
2968        itr->second->setColorMode(mode);
2969    } while (doAll && ++itr != _glyphs.end());
2970
2971    _needsRedraw = true;
2972}
2973
2974/**
2975 * \brief Controls the array used to scale glyphs for the given DataSet
2976 */
2977void Renderer::setGlyphsScalingMode(const DataSetId& id, Glyphs::ScalingMode mode)
2978{
2979    GlyphsHashmap::iterator itr;
2980
2981    bool doAll = false;
2982
2983    if (id.compare("all") == 0) {
2984        itr = _glyphs.begin();
2985        doAll = true;
2986    } else {
2987        itr = _glyphs.find(id);
2988    }
2989    if (itr == _glyphs.end()) {
2990        ERROR("Glyphs not found: %s", id.c_str());
2991        return;
2992    }
2993
2994    do {
2995        itr->second->setScalingMode(mode);
2996    } while (doAll && ++itr != _glyphs.end());
2997
2998    _renderer->ResetCameraClippingRange();
2999    _needsRedraw = true;
3000}
3001
3002/**
3003 * \brief Set the shape of Glyphs for the given DataSet
3004 */
3005void Renderer::setGlyphsShape(const DataSetId& id, Glyphs::GlyphShape shape)
3006{
3007    GlyphsHashmap::iterator itr;
3008
3009    bool doAll = false;
3010
3011    if (id.compare("all") == 0) {
3012        itr = _glyphs.begin();
3013        doAll = true;
3014    } else {
3015        itr = _glyphs.find(id);
3016    }
3017    if (itr == _glyphs.end()) {
3018        ERROR("Glyphs not found: %s", id.c_str());
3019        return;
3020    }
3021
3022    do {
3023        itr->second->setGlyphShape(shape);
3024    } while (doAll && ++itr != _glyphs.end());
3025
3026    _renderer->ResetCameraClippingRange();
3027    _needsRedraw = true;
3028}
3029
3030/**
3031 * \brief Set the glyph scaling factor for the given DataSet
3032 */
3033void Renderer::setGlyphsScaleFactor(const DataSetId& id, double scale)
3034{
3035    GlyphsHashmap::iterator itr;
3036
3037    bool doAll = false;
3038
3039    if (id.compare("all") == 0) {
3040        itr = _glyphs.begin();
3041        doAll = true;
3042    } else {
3043        itr = _glyphs.find(id);
3044    }
3045    if (itr == _glyphs.end()) {
3046        ERROR("Glyphs not found: %s", id.c_str());
3047        return;
3048    }
3049
3050    do {
3051        itr->second->setScaleFactor(scale);
3052    } while (doAll && ++itr != _glyphs.end());
3053
3054    initCamera();
3055    _needsRedraw = true;
3056}
3057
3058/**
3059 * \brief Set the visibility of polygon edges for the specified DataSet
3060 */
3061void Renderer::setGlyphsEdgeVisibility(const DataSetId& id, bool state)
3062{
3063    GlyphsHashmap::iterator itr;
3064
3065    bool doAll = false;
3066
3067    if (id.compare("all") == 0) {
3068        itr = _glyphs.begin();
3069        doAll = true;
3070    } else {
3071        itr = _glyphs.find(id);
3072    }
3073    if (itr == _glyphs.end()) {
3074        ERROR("Glyphs not found: %s", id.c_str());
3075        return;
3076    }
3077
3078    do {
3079        itr->second->setEdgeVisibility(state);
3080    } while (doAll && ++itr != _glyphs.end());
3081
3082    _needsRedraw = true;
3083}
3084
3085/**
3086 * \brief Set the RGB polygon edge color for the specified DataSet
3087 */
3088void Renderer::setGlyphsEdgeColor(const DataSetId& id, float color[3])
3089{
3090    GlyphsHashmap::iterator itr;
3091
3092    bool doAll = false;
3093
3094    if (id.compare("all") == 0) {
3095        itr = _glyphs.begin();
3096        doAll = true;
3097    } else {
3098        itr = _glyphs.find(id);
3099    }
3100    if (itr == _glyphs.end()) {
3101        ERROR("Glyphs not found: %s", id.c_str());
3102        return;
3103    }
3104
3105    do {
3106        itr->second->setEdgeColor(color);
3107    } while (doAll && ++itr != _glyphs.end());
3108
3109    _needsRedraw = true;
3110}
3111
3112/**
3113 * \brief Set the polygon edge width for the specified DataSet (may be a no-op)
3114 *
3115 * If the OpenGL implementation/hardware does not support wide lines,
3116 * this function may not have an effect.
3117 */
3118void Renderer::setGlyphsEdgeWidth(const DataSetId& id, float edgeWidth)
3119{
3120    GlyphsHashmap::iterator itr;
3121
3122    bool doAll = false;
3123
3124    if (id.compare("all") == 0) {
3125        itr = _glyphs.begin();
3126        doAll = true;
3127    } else {
3128        itr = _glyphs.find(id);
3129    }
3130    if (itr == _glyphs.end()) {
3131        ERROR("Glyphs not found: %s", id.c_str());
3132        return;
3133    }
3134
3135    do {
3136        itr->second->setEdgeWidth(edgeWidth);
3137    } while (doAll && ++itr != _glyphs.end());
3138
3139    _needsRedraw = true;
3140}
3141
3142/**
3143 * \brief Turn Glyphs lighting on/off for the specified DataSet
3144 */
3145void Renderer::setGlyphsLighting(const DataSetId& id, bool state)
3146{
3147    GlyphsHashmap::iterator itr;
3148
3149    bool doAll = false;
3150
3151    if (id.compare("all") == 0) {
3152        itr = _glyphs.begin();
3153        doAll = true;
3154    } else {
3155        itr = _glyphs.find(id);
3156    }
3157    if (itr == _glyphs.end()) {
3158        ERROR("Glyphs not found: %s", id.c_str());
3159        return;
3160    }
3161
3162    do {
3163        itr->second->setLighting(state);
3164    } while (doAll && ++itr != _glyphs.end());
3165    _needsRedraw = true;
3166}
3167
3168/**
3169 * \brief Set opacity of Glyphs for the given DataSet
3170 */
3171void Renderer::setGlyphsOpacity(const DataSetId& id, double opacity)
3172{
3173    GlyphsHashmap::iterator itr;
3174
3175    bool doAll = false;
3176
3177    if (id.compare("all") == 0) {
3178        itr = _glyphs.begin();
3179        doAll = true;
3180    } else {
3181        itr = _glyphs.find(id);
3182    }
3183    if (itr == _glyphs.end()) {
3184        ERROR("Glyphs not found: %s", id.c_str());
3185        return;
3186    }
3187
3188    do {
3189        itr->second->setOpacity(opacity);
3190    } while (doAll && ++itr != _glyphs.end());
3191
3192    _needsRedraw = true;
3193}
3194
3195/**
3196 * \brief Turn on/off rendering Glyphs for the given DataSet
3197 */
3198void Renderer::setGlyphsVisibility(const DataSetId& id, bool state)
3199{
3200    GlyphsHashmap::iterator itr;
3201
3202    bool doAll = false;
3203
3204    if (id.compare("all") == 0) {
3205        itr = _glyphs.begin();
3206        doAll = true;
3207    } else {
3208        itr = _glyphs.find(id);
3209    }
3210    if (itr == _glyphs.end()) {
3211        ERROR("Glyphs not found: %s", id.c_str());
3212        return;
3213    }
3214
3215    do {
3216        itr->second->setVisibility(state);
3217    } while (doAll && ++itr != _glyphs.end());
3218
3219    _needsRedraw = true;
3220}
3221
3222/**
3223 * \brief Turn on/off wireframe rendering of Glyphs for the given DataSet
3224 */
3225void Renderer::setGlyphsWireframe(const DataSetId& id, bool state)
3226{
3227    GlyphsHashmap::iterator itr;
3228
3229    bool doAll = false;
3230
3231    if (id.compare("all") == 0) {
3232        itr = _glyphs.begin();
3233        doAll = true;
3234    } else {
3235        itr = _glyphs.find(id);
3236    }
3237    if (itr == _glyphs.end()) {
3238        ERROR("Glyphs not found: %s", id.c_str());
3239        return;
3240    }
3241
3242    do {
3243        itr->second->setWireframe(state);
3244    } while (doAll && ++itr != _glyphs.end());
3245
3246    _needsRedraw = true;
3247}
3248
3249/**
3250 * \brief Create a new HeightMap and associate it with the named DataSet
3251 */
3252bool Renderer::addHeightMap(const DataSetId& id, int numContours, double heightScale)
3253{
3254    DataSetHashmap::iterator itr;
3255
3256    bool doAll = false;
3257
3258    if (id.compare("all") == 0) {
3259        itr = _dataSets.begin();
3260    } else {
3261        itr = _dataSets.find(id);
3262    }
3263    if (itr == _dataSets.end()) {
3264        ERROR("Unknown dataset %s", id.c_str());
3265        return false;
3266    }
3267
3268    do {
3269        DataSet *ds = itr->second;
3270        const DataSetId& dsID = ds->getName();
3271
3272        if (getHeightMap(dsID)) {
3273            WARN("Replacing existing HeightMap %s", dsID.c_str());
3274            deleteHeightMap(dsID);
3275        }
3276
3277        HeightMap *hmap = new HeightMap(numContours, heightScale);
3278
3279        hmap->setDataSet(ds,
3280                         _useCumulativeRange,
3281                         _cumulativeScalarRange,
3282                         _cumulativeVectorMagnitudeRange,
3283                         _cumulativeVectorComponentRange);
3284
3285        if (hmap->getProp() == NULL) {
3286            delete hmap;
3287            return false;
3288        } else {
3289            _renderer->AddViewProp(hmap->getProp());
3290        }
3291
3292        _heightMaps[dsID] = hmap;
3293    } while (doAll && ++itr != _dataSets.end());
3294
3295    if (_cameraMode == IMAGE)
3296        setCameraMode(PERSPECTIVE);
3297    initCamera();
3298
3299    _needsRedraw = true;
3300    return true;
3301}
3302
3303/**
3304 * \brief Create a new HeightMap and associate it with the named DataSet
3305 */
3306bool Renderer::addHeightMap(const DataSetId& id, const std::vector<double>& contours, double heightScale)
3307{
3308    DataSetHashmap::iterator itr;
3309
3310    bool doAll = false;
3311
3312    if (id.compare("all") == 0) {
3313        itr = _dataSets.begin();
3314    } else {
3315        itr = _dataSets.find(id);
3316    }
3317    if (itr == _dataSets.end()) {
3318        ERROR("Unknown dataset %s", id.c_str());
3319        return false;
3320    }
3321
3322    do {
3323        DataSet *ds = itr->second;
3324        const DataSetId& dsID = ds->getName();
3325
3326        if (getHeightMap(dsID)) {
3327            WARN("Replacing existing HeightMap %s", dsID.c_str());
3328            deleteHeightMap(dsID);
3329        }
3330
3331        HeightMap *hmap = new HeightMap(contours, heightScale);
3332
3333        hmap->setDataSet(ds,
3334                         _useCumulativeRange,
3335                         _cumulativeScalarRange,
3336                         _cumulativeVectorMagnitudeRange,
3337                         _cumulativeVectorComponentRange);
3338
3339        if (hmap->getProp() == NULL) {
3340            delete hmap;
3341            return false;
3342        } else {
3343            _renderer->AddViewProp(hmap->getProp());
3344        }
3345
3346        _heightMaps[dsID] = hmap;
3347    } while (doAll && ++itr != _dataSets.end());
3348
3349    if (_cameraMode == IMAGE)
3350        setCameraMode(PERSPECTIVE);
3351    initCamera();
3352
3353    _needsRedraw = true;
3354    return true;
3355}
3356
3357/**
3358 * \brief Get the HeightMap associated with a named DataSet
3359 */
3360HeightMap *Renderer::getHeightMap(const DataSetId& id)
3361{
3362    HeightMapHashmap::iterator itr = _heightMaps.find(id);
3363
3364    if (itr == _heightMaps.end()) {
3365#ifdef DEBUG
3366        TRACE("HeightMap not found: %s", id.c_str());
3367#endif
3368        return NULL;
3369    } else
3370        return itr->second;
3371}
3372
3373/**
3374 * \brief Set an additional transform on the prop
3375 */
3376void Renderer::setHeightMapTransform(const DataSetId& id, vtkMatrix4x4 *trans)
3377{
3378    HeightMapHashmap::iterator itr;
3379
3380    bool doAll = false;
3381
3382    if (id.compare("all") == 0) {
3383        itr = _heightMaps.begin();
3384        doAll = true;
3385    } else {
3386        itr = _heightMaps.find(id);
3387    }
3388    if (itr == _heightMaps.end()) {
3389        ERROR("HeightMap not found: %s", id.c_str());
3390        return;
3391    }
3392
3393    do {
3394        itr->second->setTransform(trans);
3395    } while (doAll && ++itr != _heightMaps.end());
3396
3397    resetAxes();
3398    _needsRedraw = true;
3399}
3400
3401/**
3402 * \brief Set the prop orientation with a quaternion
3403 */
3404void Renderer::setHeightMapOrientation(const DataSetId& id, double quat[4])
3405{
3406    HeightMapHashmap::iterator itr;
3407
3408    bool doAll = false;
3409
3410    if (id.compare("all") == 0) {
3411        itr = _heightMaps.begin();
3412        doAll = true;
3413    } else {
3414        itr = _heightMaps.find(id);
3415    }
3416    if (itr == _heightMaps.end()) {
3417        ERROR("HeightMap not found: %s", id.c_str());
3418        return;
3419    }
3420
3421    do {
3422        itr->second->setOrientation(quat);
3423    } while (doAll && ++itr != _heightMaps.end());
3424
3425    resetAxes();
3426    _needsRedraw = true;
3427}
3428
3429/**
3430 * \brief Set the prop orientation with a rotation about an axis
3431 */
3432void Renderer::setHeightMapOrientation(const DataSetId& id, double angle, double axis[3])
3433{
3434    HeightMapHashmap::iterator itr;
3435
3436    bool doAll = false;
3437
3438    if (id.compare("all") == 0) {
3439        itr = _heightMaps.begin();
3440        doAll = true;
3441    } else {
3442        itr = _heightMaps.find(id);
3443    }
3444    if (itr == _heightMaps.end()) {
3445        ERROR("HeightMap not found: %s", id.c_str());
3446        return;
3447    }
3448
3449    do {
3450        itr->second->setOrientation(angle, axis);
3451    } while (doAll && ++itr != _heightMaps.end());
3452
3453    resetAxes();
3454    _needsRedraw = true;
3455}
3456
3457/**
3458 * \brief Set the prop position in world coords
3459 */
3460void Renderer::setHeightMapPosition(const DataSetId& id, double pos[3])
3461{
3462    HeightMapHashmap::iterator itr;
3463
3464    bool doAll = false;
3465
3466    if (id.compare("all") == 0) {
3467        itr = _heightMaps.begin();
3468        doAll = true;
3469    } else {
3470        itr = _heightMaps.find(id);
3471    }
3472    if (itr == _heightMaps.end()) {
3473        ERROR("HeightMap not found: %s", id.c_str());
3474        return;
3475    }
3476
3477    do {
3478        itr->second->setPosition(pos);
3479    } while (doAll && ++itr != _heightMaps.end());
3480
3481    resetAxes();
3482    _needsRedraw = true;
3483}
3484
3485/**
3486 * \brief Set the prop scaling
3487 */
3488void Renderer::setHeightMapScale(const DataSetId& id, double scale[3])
3489{
3490    HeightMapHashmap::iterator itr;
3491
3492    bool doAll = false;
3493
3494    if (id.compare("all") == 0) {
3495        itr = _heightMaps.begin();
3496        doAll = true;
3497    } else {
3498        itr = _heightMaps.find(id);
3499    }
3500    if (itr == _heightMaps.end()) {
3501        ERROR("HeightMap not found: %s", id.c_str());
3502        return;
3503    }
3504
3505    do {
3506        itr->second->setScale(scale);
3507    } while (doAll && ++itr != _heightMaps.end());
3508
3509    resetAxes();
3510    _needsRedraw = true;
3511}
3512
3513/**
3514 * \brief Set the volume slice used for mapping volumetric data
3515 */
3516void Renderer::setHeightMapVolumeSlice(const DataSetId& id, HeightMap::Axis axis, double ratio)
3517{
3518    HeightMapHashmap::iterator itr;
3519
3520    bool doAll = false;
3521
3522    if (id.compare("all") == 0) {
3523        itr = _heightMaps.begin();
3524        doAll = true;
3525    } else {
3526        itr = _heightMaps.find(id);
3527    }
3528
3529    if (itr == _heightMaps.end()) {
3530        ERROR("HeightMap not found: %s", id.c_str());
3531        return;
3532    }
3533
3534    do {
3535        itr->second->selectVolumeSlice(axis, ratio);
3536     } while (doAll && ++itr != _heightMaps.end());
3537
3538    initCamera();
3539    _needsRedraw = true;
3540}
3541
3542/**
3543 * \brief Set amount to scale scalar values when creating elevations
3544 * in the height map
3545 */
3546void Renderer::setHeightMapHeightScale(const DataSetId& id, double scale)
3547{
3548    HeightMapHashmap::iterator itr;
3549
3550    bool doAll = false;
3551
3552    if (id.compare("all") == 0) {
3553        itr = _heightMaps.begin();
3554        doAll = true;
3555    } else {
3556        itr = _heightMaps.find(id);
3557    }
3558
3559    if (itr == _heightMaps.end()) {
3560        ERROR("HeightMap not found: %s", id.c_str());
3561        return;
3562    }
3563
3564    do {
3565        itr->second->setHeightScale(scale);
3566     } while (doAll && ++itr != _heightMaps.end());
3567
3568    initCamera();
3569    _needsRedraw = true;
3570}
3571
3572/**
3573 * \brief Associate an existing named color map with a HeightMap for the given DataSet
3574 */
3575void Renderer::setHeightMapColorMap(const DataSetId& id, const ColorMapId& colorMapId)
3576{
3577    HeightMapHashmap::iterator itr;
3578
3579    bool doAll = false;
3580
3581    if (id.compare("all") == 0) {
3582        itr = _heightMaps.begin();
3583        doAll = true;
3584    } else {
3585        itr = _heightMaps.find(id);
3586    }
3587
3588    if (itr == _heightMaps.end()) {
3589        ERROR("HeightMap not found: %s", id.c_str());
3590        return;
3591    }
3592
3593    ColorMap *cmap = getColorMap(colorMapId);
3594    if (cmap == NULL) {
3595        ERROR("Unknown colormap: %s", colorMapId.c_str());
3596        return;
3597    }
3598
3599    do {
3600        TRACE("Set HeightMap color map: %s for dataset %s", colorMapId.c_str(),
3601              itr->second->getDataSet()->getName().c_str());
3602
3603        itr->second->setColorMap(cmap);
3604    } while (doAll && ++itr != _heightMaps.end());
3605
3606    _needsRedraw = true;
3607}
3608
3609/**
3610 * \brief Set the number of equally spaced contour isolines for the given DataSet
3611 */
3612void Renderer::setHeightMapNumContours(const DataSetId& id, int numContours)
3613{
3614    HeightMapHashmap::iterator itr;
3615
3616    bool doAll = false;
3617
3618    if (id.compare("all") == 0) {
3619        itr = _heightMaps.begin();
3620        doAll = true;
3621    } else {
3622        itr = _heightMaps.find(id);
3623    }
3624    if (itr == _heightMaps.end()) {
3625        ERROR("HeightMap not found: %s", id.c_str());
3626        return;
3627    }
3628
3629    do {
3630        itr->second->setNumContours(numContours);
3631    } while (doAll && ++itr != _heightMaps.end());
3632
3633    _needsRedraw = true;
3634}
3635
3636/**
3637 * \brief Set a list of height map contour isovalues for the given DataSet
3638 */
3639void Renderer::setHeightMapContourList(const DataSetId& id, const std::vector<double>& contours)
3640{
3641    HeightMapHashmap::iterator itr;
3642
3643    bool doAll = false;
3644
3645    if (id.compare("all") == 0) {
3646        itr = _heightMaps.begin();
3647        doAll = true;
3648    } else {
3649        itr = _heightMaps.find(id);
3650    }
3651    if (itr == _heightMaps.end()) {
3652        ERROR("HeightMap not found: %s", id.c_str());
3653        return;
3654    }
3655
3656    do {
3657        itr->second->setContourList(contours);
3658    } while (doAll && ++itr != _heightMaps.end());
3659
3660     _needsRedraw = true;
3661}
3662
3663/**
3664 * \brief Set opacity of height map for the given DataSet
3665 */
3666void Renderer::setHeightMapOpacity(const DataSetId& id, double opacity)
3667{
3668    HeightMapHashmap::iterator itr;
3669
3670    bool doAll = false;
3671
3672    if (id.compare("all") == 0) {
3673        itr = _heightMaps.begin();
3674        doAll = true;
3675    } else {
3676        itr = _heightMaps.find(id);
3677    }
3678    if (itr == _heightMaps.end()) {
3679        ERROR("HeightMap not found: %s", id.c_str());
3680        return;
3681    }
3682
3683    do {
3684        itr->second->setOpacity(opacity);
3685    } while (doAll && ++itr != _heightMaps.end());
3686
3687    _needsRedraw = true;
3688}
3689
3690/**
3691 * \brief Turn on/off rendering height map for the given DataSet
3692 */
3693void Renderer::setHeightMapVisibility(const DataSetId& id, bool state)
3694{
3695    HeightMapHashmap::iterator itr;
3696
3697    bool doAll = false;
3698
3699    if (id.compare("all") == 0) {
3700        itr = _heightMaps.begin();
3701        doAll = true;
3702    } else {
3703        itr = _heightMaps.find(id);
3704    }
3705    if (itr == _heightMaps.end()) {
3706        ERROR("HeightMap not found: %s", id.c_str());
3707        return;
3708    }
3709
3710    do {
3711        itr->second->setVisibility(state);
3712    } while (doAll && ++itr != _heightMaps.end());
3713
3714    _needsRedraw = true;
3715}
3716
3717/**
3718 * \brief Set wireframe rendering for the specified DataSet
3719 */
3720void Renderer::setHeightMapWireframe(const DataSetId& id, bool state)
3721{
3722    HeightMapHashmap::iterator itr;
3723
3724    bool doAll = false;
3725
3726    if (id.compare("all") == 0) {
3727        itr = _heightMaps.begin();
3728        doAll = true;
3729    } else {
3730        itr = _heightMaps.find(id);
3731    }
3732    if (itr == _heightMaps.end()) {
3733        ERROR("HeightMap not found: %s", id.c_str());
3734        return;
3735    }
3736
3737    do {
3738        itr->second->setWireframe(state);
3739    } while (doAll && ++itr != _heightMaps.end());
3740
3741    _needsRedraw = true;
3742}
3743
3744/**
3745 * \brief Turn on/off rendering height map mesh edges for the given DataSet
3746 */
3747void Renderer::setHeightMapEdgeVisibility(const DataSetId& id, bool state)
3748{
3749    HeightMapHashmap::iterator itr;
3750
3751    bool doAll = false;
3752
3753    if (id.compare("all") == 0) {
3754        itr = _heightMaps.begin();
3755        doAll = true;
3756    } else {
3757        itr = _heightMaps.find(id);
3758    }
3759    if (itr == _heightMaps.end()) {
3760        ERROR("HeightMap not found: %s", id.c_str());
3761        return;
3762    }
3763
3764    do {
3765        itr->second->setEdgeVisibility(state);
3766    } while (doAll && ++itr != _heightMaps.end());
3767
3768    _needsRedraw = true;
3769}
3770
3771/**
3772 * \brief Set the RGB height map mesh edge color for the specified DataSet
3773 */
3774void Renderer::setHeightMapEdgeColor(const DataSetId& id, float color[3])
3775{
3776    HeightMapHashmap::iterator itr;
3777
3778    bool doAll = false;
3779
3780    if (id.compare("all") == 0) {
3781        itr = _heightMaps.begin();
3782        doAll = true;
3783    } else {
3784        itr = _heightMaps.find(id);
3785    }
3786    if (itr == _heightMaps.end()) {
3787        ERROR("HeightMap not found: %s", id.c_str());
3788        return;
3789    }
3790
3791    do {
3792        itr->second->setEdgeColor(color);
3793    } while (doAll && ++itr != _heightMaps.end());
3794
3795    _needsRedraw = true;
3796}
3797
3798/**
3799 * \brief Set the height map mesh edge width for the specified DataSet (may be a no-op)
3800 *
3801 * If the OpenGL implementation/hardware does not support wide lines,
3802 * this function may not have an effect.
3803 */
3804void Renderer::setHeightMapEdgeWidth(const DataSetId& id, float edgeWidth)
3805{
3806    HeightMapHashmap::iterator itr;
3807
3808    bool doAll = false;
3809
3810    if (id.compare("all") == 0) {
3811        itr = _heightMaps.begin();
3812        doAll = true;
3813    } else {
3814        itr = _heightMaps.find(id);
3815    }
3816    if (itr == _heightMaps.end()) {
3817        ERROR("HeightMap not found: %s", id.c_str());
3818        return;
3819    }
3820
3821    do {
3822        itr->second->setEdgeWidth(edgeWidth);
3823    } while (doAll && ++itr != _heightMaps.end());
3824
3825    _needsRedraw = true;
3826}
3827
3828/**
3829 * \brief Turn on/off rendering height map contour lines for the given DataSet
3830 */
3831void Renderer::setHeightMapContourLineVisibility(const DataSetId& id, bool state)
3832{
3833    HeightMapHashmap::iterator itr;
3834
3835    bool doAll = false;
3836
3837    if (id.compare("all") == 0) {
3838        itr = _heightMaps.begin();
3839        doAll = true;
3840    } else {
3841        itr = _heightMaps.find(id);
3842    }
3843    if (itr == _heightMaps.end()) {
3844        ERROR("HeightMap not found: %s", id.c_str());
3845        return;
3846    }
3847
3848    do {
3849        itr->second->setContourLineVisibility(state);
3850    } while (doAll && ++itr != _heightMaps.end());
3851
3852    _needsRedraw = true;
3853}
3854
3855/**
3856 * \brief Turn on/off rendering height map colormap surface for the given DataSet
3857 */
3858void Renderer::setHeightMapContourSurfaceVisibility(const DataSetId& id, bool state)
3859{
3860    HeightMapHashmap::iterator itr;
3861
3862    bool doAll = false;
3863
3864    if (id.compare("all") == 0) {
3865        itr = _heightMaps.begin();
3866        doAll = true;
3867    } else {
3868        itr = _heightMaps.find(id);
3869    }
3870    if (itr == _heightMaps.end()) {
3871        ERROR("HeightMap not found: %s", id.c_str());
3872        return;
3873    }
3874
3875    do {
3876        itr->second->setContourSurfaceVisibility(state);
3877    } while (doAll && ++itr != _heightMaps.end());
3878
3879    _needsRedraw = true;
3880}
3881
3882/**
3883 * \brief Set the RGB height map isoline color for the specified DataSet
3884 */
3885void Renderer::setHeightMapContourEdgeColor(const DataSetId& id, float color[3])
3886{
3887    HeightMapHashmap::iterator itr;
3888
3889    bool doAll = false;
3890
3891    if (id.compare("all") == 0) {
3892        itr = _heightMaps.begin();
3893        doAll = true;
3894    } else {
3895        itr = _heightMaps.find(id);
3896    }
3897    if (itr == _heightMaps.end()) {
3898        ERROR("HeightMap not found: %s", id.c_str());
3899        return;
3900    }
3901
3902    do {
3903        itr->second->setContourEdgeColor(color);
3904    } while (doAll && ++itr != _heightMaps.end());
3905
3906    _needsRedraw = true;
3907}
3908
3909/**
3910 * \brief Set the height map isoline width for the specified DataSet (may be a no-op)
3911 *
3912 * If the OpenGL implementation/hardware does not support wide lines,
3913 * this function may not have an effect.
3914 */
3915void Renderer::setHeightMapContourEdgeWidth(const DataSetId& id, float edgeWidth)
3916{
3917    HeightMapHashmap::iterator itr;
3918
3919    bool doAll = false;
3920
3921    if (id.compare("all") == 0) {
3922        itr = _heightMaps.begin();
3923        doAll = true;
3924    } else {
3925        itr = _heightMaps.find(id);
3926    }
3927    if (itr == _heightMaps.end()) {
3928        ERROR("HeightMap not found: %s", id.c_str());
3929        return;
3930    }
3931
3932    do {
3933        itr->second->setContourEdgeWidth(edgeWidth);
3934    } while (doAll && ++itr != _heightMaps.end());
3935
3936    _needsRedraw = true;
3937}
3938
3939/**
3940 * \brief Turn height map lighting on/off for the specified DataSet
3941 */
3942void Renderer::setHeightMapLighting(const DataSetId& id, bool state)
3943{
3944    HeightMapHashmap::iterator itr;
3945
3946    bool doAll = false;
3947
3948    if (id.compare("all") == 0) {
3949        itr = _heightMaps.begin();
3950        doAll = true;
3951    } else {
3952        itr = _heightMaps.find(id);
3953    }
3954    if (itr == _heightMaps.end()) {
3955        ERROR("HeightMap not found: %s", id.c_str());
3956        return;
3957    }
3958
3959    do {
3960        itr->second->setLighting(state);
3961    } while (doAll && ++itr != _heightMaps.end());
3962    _needsRedraw = true;
3963}
3964
3965/**
3966 * \brief Create a new LIC and associate it with the named DataSet
3967 */
3968bool Renderer::addLIC(const DataSetId& id)
3969{
3970    DataSetHashmap::iterator itr;
3971
3972    bool doAll = false;
3973
3974    if (id.compare("all") == 0) {
3975        itr = _dataSets.begin();
3976    } else {
3977        itr = _dataSets.find(id);
3978    }
3979    if (itr == _dataSets.end()) {
3980        ERROR("Unknown dataset %s", id.c_str());
3981        return false;
3982    }
3983
3984    do {
3985        DataSet *ds = itr->second;
3986        const DataSetId& dsID = ds->getName();
3987
3988        if (getLIC(dsID)) {
3989            WARN("Replacing existing LIC %s", dsID.c_str());
3990            deleteLIC(dsID);
3991        }
3992
3993        LIC *lic = new LIC();
3994        _lics[dsID] = lic;
3995
3996        lic->setDataSet(ds,
3997                        _useCumulativeRange,
3998                        _cumulativeScalarRange,
3999                        _cumulativeVectorMagnitudeRange,
4000                        _cumulativeVectorComponentRange);
4001
4002        _renderer->AddViewProp(lic->getProp());
4003    } while (doAll && ++itr != _dataSets.end());
4004
4005    if (_cameraMode == IMAGE)
4006        setCameraMode(PERSPECTIVE);
4007    initCamera();
4008    _needsRedraw = true;
4009    return true;
4010}
4011
4012/**
4013 * \brief Get the LIC associated with a named DataSet
4014 */
4015LIC *Renderer::getLIC(const DataSetId& id)
4016{
4017    LICHashmap::iterator itr = _lics.find(id);
4018
4019    if (itr == _lics.end()) {
4020#ifdef DEBUG
4021        TRACE("LIC not found: %s", id.c_str());
4022#endif
4023        return NULL;
4024    } else
4025        return itr->second;
4026}
4027
4028/**
4029 * \brief Set the prop orientation with a quaternion
4030 */
4031void Renderer::setLICTransform(const DataSetId& id, vtkMatrix4x4 *trans)
4032{
4033    LICHashmap::iterator itr;
4034
4035    bool doAll = false;
4036
4037    if (id.compare("all") == 0) {
4038        itr = _lics.begin();
4039        doAll = true;
4040    } else {
4041        itr = _lics.find(id);
4042    }
4043    if (itr == _lics.end()) {
4044        ERROR("LIC not found: %s", id.c_str());
4045        return;
4046    }
4047
4048    do {
4049        itr->second->setTransform(trans);
4050    } while (doAll && ++itr != _lics.end());
4051
4052    resetAxes();
4053    _needsRedraw = true;
4054}
4055
4056/**
4057 * \brief Set the prop orientation with a quaternion
4058 */
4059void Renderer::setLICOrientation(const DataSetId& id, double quat[4])
4060{
4061    LICHashmap::iterator itr;
4062
4063    bool doAll = false;
4064
4065    if (id.compare("all") == 0) {
4066        itr = _lics.begin();
4067        doAll = true;
4068    } else {
4069        itr = _lics.find(id);
4070    }
4071    if (itr == _lics.end()) {
4072        ERROR("LIC not found: %s", id.c_str());
4073        return;
4074    }
4075
4076    do {
4077        itr->second->setOrientation(quat);
4078    } while (doAll && ++itr != _lics.end());
4079
4080    resetAxes();
4081    _needsRedraw = true;
4082}
4083
4084/**
4085 * \brief Set the prop orientation with a rotation about an axis
4086 */
4087void Renderer::setLICOrientation(const DataSetId& id, double angle, double axis[3])
4088{
4089    LICHashmap::iterator itr;
4090
4091    bool doAll = false;
4092
4093    if (id.compare("all") == 0) {
4094        itr = _lics.begin();
4095        doAll = true;
4096    } else {
4097        itr = _lics.find(id);
4098    }
4099    if (itr == _lics.end()) {
4100        ERROR("LIC not found: %s", id.c_str());
4101        return;
4102    }
4103
4104    do {
4105        itr->second->setOrientation(angle, axis);
4106    } while (doAll && ++itr != _lics.end());
4107
4108    resetAxes();
4109    _needsRedraw = true;
4110}
4111
4112/**
4113 * \brief Set the prop position in world coords
4114 */
4115void Renderer::setLICPosition(const DataSetId& id, double pos[3])
4116{
4117    LICHashmap::iterator itr;
4118
4119    bool doAll = false;
4120
4121    if (id.compare("all") == 0) {
4122        itr = _lics.begin();
4123        doAll = true;
4124    } else {
4125        itr = _lics.find(id);
4126    }
4127    if (itr == _lics.end()) {
4128        ERROR("LIC not found: %s", id.c_str());
4129        return;
4130    }
4131
4132    do {
4133        itr->second->setPosition(pos);
4134    } while (doAll && ++itr != _lics.end());
4135
4136    resetAxes();
4137    _needsRedraw = true;
4138}
4139
4140/**
4141 * \brief Set the prop scaling
4142 */
4143void Renderer::setLICScale(const DataSetId& id, double scale[3])
4144{
4145    LICHashmap::iterator itr;
4146
4147    bool doAll = false;
4148
4149    if (id.compare("all") == 0) {
4150        itr = _lics.begin();
4151        doAll = true;
4152    } else {
4153        itr = _lics.find(id);
4154    }
4155    if (itr == _lics.end()) {
4156        ERROR("LIC not found: %s", id.c_str());
4157        return;
4158    }
4159
4160    do {
4161        itr->second->setScale(scale);
4162    } while (doAll && ++itr != _lics.end());
4163
4164    resetAxes();
4165    _needsRedraw = true;
4166}
4167
4168/**
4169 * \brief Set the volume slice used for mapping volumetric data
4170 */
4171void Renderer::setLICVolumeSlice(const DataSetId& id, LIC::Axis axis, double ratio)
4172{
4173    LICHashmap::iterator itr;
4174
4175    bool doAll = false;
4176
4177    if (id.compare("all") == 0) {
4178        itr = _lics.begin();
4179        doAll = true;
4180    } else {
4181        itr = _lics.find(id);
4182    }
4183
4184    if (itr == _lics.end()) {
4185        ERROR("LIC not found: %s", id.c_str());
4186        return;
4187    }
4188
4189    do {
4190        itr->second->selectVolumeSlice(axis, ratio);
4191     } while (doAll && ++itr != _lics.end());
4192
4193    initCamera();
4194    _needsRedraw = true;
4195}
4196
4197/**
4198 * \brief Associate an existing named color map with an LIC for the given DataSet
4199 */
4200void Renderer::setLICColorMap(const DataSetId& id, const ColorMapId& colorMapId)
4201{
4202    LICHashmap::iterator itr;
4203
4204    bool doAll = false;
4205
4206    if (id.compare("all") == 0) {
4207        itr = _lics.begin();
4208        doAll = true;
4209    } else {
4210        itr = _lics.find(id);
4211    }
4212
4213    if (itr == _lics.end()) {
4214        ERROR("LIC not found: %s", id.c_str());
4215        return;
4216    }
4217
4218    ColorMap *cmap = getColorMap(colorMapId);
4219    if (cmap == NULL) {
4220        ERROR("Unknown colormap: %s", colorMapId.c_str());
4221        return;
4222    }
4223
4224    do {
4225        TRACE("Set LIC color map: %s for dataset %s", colorMapId.c_str(),
4226              itr->second->getDataSet()->getName().c_str());
4227
4228        itr->second->setColorMap(cmap);
4229    } while (doAll && ++itr != _lics.end());
4230
4231    _needsRedraw = true;
4232}
4233
4234/**
4235 * \brief Set opacity of the LIC for the given DataSet
4236 */
4237void Renderer::setLICOpacity(const DataSetId& id, double opacity)
4238{
4239    LICHashmap::iterator itr;
4240
4241    bool doAll = false;
4242
4243    if (id.compare("all") == 0) {
4244        itr = _lics.begin();
4245        doAll = true;
4246    } else {
4247        itr = _lics.find(id);
4248    }
4249    if (itr == _lics.end()) {
4250        ERROR("LIC not found: %s", id.c_str());
4251        return;
4252    }
4253
4254    do {
4255        itr->second->setOpacity(opacity);
4256    } while (doAll && ++itr != _lics.end());
4257
4258    _needsRedraw = true;
4259}
4260
4261/**
4262 * \brief Turn on/off rendering of the LIC for the given DataSet
4263 */
4264void Renderer::setLICVisibility(const DataSetId& id, bool state)
4265{
4266    LICHashmap::iterator itr;
4267
4268    bool doAll = false;
4269
4270    if (id.compare("all") == 0) {
4271        itr = _lics.begin();
4272        doAll = true;
4273    } else {
4274        itr = _lics.find(id);
4275    }
4276    if (itr == _lics.end()) {
4277        ERROR("LIC not found: %s", id.c_str());
4278        return;
4279    }
4280
4281    do {
4282        itr->second->setVisibility(state);
4283    } while (doAll && ++itr != _lics.end());
4284
4285    _needsRedraw = true;
4286}
4287
4288/**
4289 * \brief Set the visibility of polygon edges for the specified DataSet
4290 */
4291void Renderer::setLICEdgeVisibility(const DataSetId& id, bool state)
4292{
4293    LICHashmap::iterator itr;
4294
4295    bool doAll = false;
4296
4297    if (id.compare("all") == 0) {
4298        itr = _lics.begin();
4299        doAll = true;
4300    } else {
4301        itr = _lics.find(id);
4302    }
4303    if (itr == _lics.end()) {
4304        ERROR("LIC not found: %s", id.c_str());
4305        return;
4306    }
4307
4308    do {
4309        itr->second->setEdgeVisibility(state);
4310    } while (doAll && ++itr != _lics.end());
4311
4312    _needsRedraw = true;
4313}
4314
4315/**
4316 * \brief Set the RGB polygon edge color for the specified DataSet
4317 */
4318void Renderer::setLICEdgeColor(const DataSetId& id, float color[3])
4319{
4320    LICHashmap::iterator itr;
4321
4322    bool doAll = false;
4323
4324    if (id.compare("all") == 0) {
4325        itr = _lics.begin();
4326        doAll = true;
4327    } else {
4328        itr = _lics.find(id);
4329    }
4330    if (itr == _lics.end()) {
4331        ERROR("LIC not found: %s", id.c_str());
4332        return;
4333    }
4334
4335    do {
4336        itr->second->setEdgeColor(color);
4337    } while (doAll && ++itr != _lics.end());
4338
4339    _needsRedraw = true;
4340}
4341
4342/**
4343 * \brief Set the polygon edge width for the specified DataSet (may be a no-op)
4344 *
4345 * If the OpenGL implementation/hardware does not support wide lines,
4346 * this function may not have an effect.
4347 */
4348void Renderer::setLICEdgeWidth(const DataSetId& id, float edgeWidth)
4349{
4350    LICHashmap::iterator itr;
4351
4352    bool doAll = false;
4353
4354    if (id.compare("all") == 0) {
4355        itr = _lics.begin();
4356        doAll = true;
4357    } else {
4358        itr = _lics.find(id);
4359    }
4360    if (itr == _lics.end()) {
4361        ERROR("LIC not found: %s", id.c_str());
4362        return;
4363    }
4364
4365    do {
4366        itr->second->setEdgeWidth(edgeWidth);
4367    } while (doAll && ++itr != _lics.end());
4368
4369    _needsRedraw = true;
4370}
4371
4372/**
4373 * \brief Turn LIC lighting on/off for the specified DataSet
4374 */
4375void Renderer::setLICLighting(const DataSetId& id, bool state)
4376{
4377    LICHashmap::iterator itr;
4378
4379    bool doAll = false;
4380
4381    if (id.compare("all") == 0) {
4382        itr = _lics.begin();
4383        doAll = true;
4384    } else {
4385        itr = _lics.find(id);
4386    }
4387    if (itr == _lics.end()) {
4388        ERROR("LIC not found: %s", id.c_str());
4389        return;
4390    }
4391
4392    do {
4393        itr->second->setLighting(state);
4394    } while (doAll && ++itr != _lics.end());
4395
4396    _needsRedraw = true;
4397}
4398
4399/**
4400 * \brief Create a new Molecule and associate it with the named DataSet
4401 */
4402bool Renderer::addMolecule(const DataSetId& id)
4403{
4404    DataSetHashmap::iterator itr;
4405
4406    bool doAll = false;
4407
4408    if (id.compare("all") == 0) {
4409        itr = _dataSets.begin();
4410    } else {
4411        itr = _dataSets.find(id);
4412    }
4413    if (itr == _dataSets.end()) {
4414        ERROR("Unknown dataset %s", id.c_str());
4415        return false;
4416    }
4417
4418    do {
4419        DataSet *ds = itr->second;
4420        const DataSetId& dsID = ds->getName();
4421
4422        if (getMolecule(dsID)) {
4423            WARN("Replacing existing Molecule %s", dsID.c_str());
4424            deleteMolecule(dsID);
4425        }
4426
4427        Molecule *molecule = new Molecule();
4428        _molecules[dsID] = molecule;
4429
4430        molecule->setDataSet(ds);
4431
4432        _renderer->AddViewProp(molecule->getProp());
4433    } while (doAll && ++itr != _dataSets.end());
4434
4435    if (_cameraMode == IMAGE)
4436        setCameraMode(PERSPECTIVE);
4437    initCamera();
4438    _needsRedraw = true;
4439    return true;
4440}
4441
4442/**
4443 * \brief Get the Molecule associated with a named DataSet
4444 */
4445Molecule *Renderer::getMolecule(const DataSetId& id)
4446{
4447    MoleculeHashmap::iterator itr = _molecules.find(id);
4448
4449    if (itr == _molecules.end()) {
4450#ifdef DEBUG
4451        TRACE("Molecule not found: %s", id.c_str());
4452#endif
4453        return NULL;
4454    } else
4455        return itr->second;
4456}
4457
4458/**
4459 * \brief Set the prop orientation with a quaternion
4460 */
4461void Renderer::setMoleculeTransform(const DataSetId& id, vtkMatrix4x4 *trans)
4462{
4463    MoleculeHashmap::iterator itr;
4464
4465    bool doAll = false;
4466
4467    if (id.compare("all") == 0) {
4468        itr = _molecules.begin();
4469        doAll = true;
4470    } else {
4471        itr = _molecules.find(id);
4472    }
4473    if (itr == _molecules.end()) {
4474        ERROR("Molecule not found: %s", id.c_str());
4475        return;
4476    }
4477
4478    do {
4479        itr->second->setTransform(trans);
4480    } while (doAll && ++itr != _molecules.end());
4481
4482    resetAxes();
4483    _needsRedraw = true;
4484}
4485
4486/**
4487 * \brief Set the prop orientation with a quaternion
4488 */
4489void Renderer::setMoleculeOrientation(const DataSetId& id, double quat[4])
4490{
4491    MoleculeHashmap::iterator itr;
4492
4493    bool doAll = false;
4494
4495    if (id.compare("all") == 0) {
4496        itr = _molecules.begin();
4497        doAll = true;
4498    } else {
4499        itr = _molecules.find(id);
4500    }
4501    if (itr == _molecules.end()) {
4502        ERROR("Molecule not found: %s", id.c_str());
4503        return;
4504    }
4505
4506    do {
4507        itr->second->setOrientation(quat);
4508    } while (doAll && ++itr != _molecules.end());
4509
4510    resetAxes();
4511    _needsRedraw = true;
4512}
4513
4514/**
4515 * \brief Set the prop orientation with a rotation about an axis
4516 */
4517void Renderer::setMoleculeOrientation(const DataSetId& id, double angle, double axis[3])
4518{
4519    MoleculeHashmap::iterator itr;
4520
4521    bool doAll = false;
4522
4523    if (id.compare("all") == 0) {
4524        itr = _molecules.begin();
4525        doAll = true;
4526    } else {
4527        itr = _molecules.find(id);
4528    }
4529    if (itr == _molecules.end()) {
4530        ERROR("Molecule not found: %s", id.c_str());
4531        return;
4532    }
4533
4534    do {
4535        itr->second->setOrientation(angle, axis);
4536    } while (doAll && ++itr != _molecules.end());
4537
4538    resetAxes();
4539    _needsRedraw = true;
4540}
4541
4542/**
4543 * \brief Set the prop position in world coords
4544 */
4545void Renderer::setMoleculePosition(const DataSetId& id, double pos[3])
4546{
4547    MoleculeHashmap::iterator itr;
4548
4549    bool doAll = false;
4550
4551    if (id.compare("all") == 0) {
4552        itr = _molecules.begin();
4553        doAll = true;
4554    } else {
4555        itr = _molecules.find(id);
4556    }
4557    if (itr == _molecules.end()) {
4558        ERROR("Molecule not found: %s", id.c_str());
4559        return;
4560    }
4561
4562    do {
4563        itr->second->setPosition(pos);
4564    } while (doAll && ++itr != _molecules.end());
4565
4566    resetAxes();
4567    _needsRedraw = true;
4568}
4569
4570/**
4571 * \brief Set the prop scaling
4572 */
4573void Renderer::setMoleculeScale(const DataSetId& id, double scale[3])
4574{
4575    MoleculeHashmap::iterator itr;
4576
4577    bool doAll = false;
4578
4579    if (id.compare("all") == 0) {
4580        itr = _molecules.begin();
4581        doAll = true;
4582    } else {
4583        itr = _molecules.find(id);
4584    }
4585    if (itr == _molecules.end()) {
4586        ERROR("Molecule not found: %s", id.c_str());
4587        return;
4588    }
4589
4590    do {
4591        itr->second->setScale(scale);
4592    } while (doAll && ++itr != _molecules.end());
4593
4594    resetAxes();
4595    _needsRedraw = true;
4596}
4597
4598/**
4599 * \brief Associate an existing named color map with a Molecule for the given DataSet
4600 */
4601void Renderer::setMoleculeColorMap(const DataSetId& id, const ColorMapId& colorMapId)
4602{
4603    MoleculeHashmap::iterator itr;
4604
4605    bool doAll = false;
4606
4607    if (id.compare("all") == 0) {
4608        itr = _molecules.begin();
4609        doAll = true;
4610    } else {
4611        itr = _molecules.find(id);
4612    }
4613
4614    if (itr == _molecules.end()) {
4615        ERROR("Molecule not found: %s", id.c_str());
4616        return;
4617    }
4618
4619    ColorMap *cmap = getColorMap(colorMapId);
4620    if (cmap == NULL) {
4621        ERROR("Unknown colormap: %s", colorMapId.c_str());
4622        return;
4623    }
4624
4625    do {
4626        TRACE("Set Molecule color map: %s for dataset %s", colorMapId.c_str(),
4627              itr->second->getDataSet()->getName().c_str());
4628
4629        itr->second->setColorMap(cmap);
4630    } while (doAll && ++itr != _molecules.end());
4631
4632    _needsRedraw = true;
4633}
4634
4635/**
4636 * \brief Set opacity of the Molecule for the given DataSet
4637 */
4638void Renderer::setMoleculeOpacity(const DataSetId& id, double opacity)
4639{
4640    MoleculeHashmap::iterator itr;
4641
4642    bool doAll = false;
4643
4644    if (id.compare("all") == 0) {
4645        itr = _molecules.begin();
4646        doAll = true;
4647    } else {
4648        itr = _molecules.find(id);
4649    }
4650    if (itr == _molecules.end()) {
4651        ERROR("Molecule not found: %s", id.c_str());
4652        return;
4653    }
4654
4655    do {
4656        itr->second->setOpacity(opacity);
4657    } while (doAll && ++itr != _molecules.end());
4658
4659    _needsRedraw = true;
4660}
4661
4662/**
4663 * \brief Set radius standard for scaling atoms
4664 */
4665void Renderer::setMoleculeAtomScaling(const DataSetId& id, Molecule::AtomScaling scaling)
4666{
4667    MoleculeHashmap::iterator itr;
4668
4669    bool doAll = false;
4670
4671    if (id.compare("all") == 0) {
4672        itr = _molecules.begin();
4673        doAll = true;
4674    } else {
4675        itr = _molecules.find(id);
4676    }
4677    if (itr == _molecules.end()) {
4678        ERROR("Molecule not found: %s", id.c_str());
4679        return;
4680    }
4681
4682    do {
4683        itr->second->setAtomScaling(scaling);
4684    } while (doAll && ++itr != _molecules.end());
4685
4686    _needsRedraw = true;
4687}
4688
4689/**
4690 * \brief Turn on/off rendering of the Molecule atoms for the given DataSet
4691 */
4692void Renderer::setMoleculeAtomVisibility(const DataSetId& id, bool state)
4693{
4694    MoleculeHashmap::iterator itr;
4695
4696    bool doAll = false;
4697
4698    if (id.compare("all") == 0) {
4699        itr = _molecules.begin();
4700        doAll = true;
4701    } else {
4702        itr = _molecules.find(id);
4703    }
4704    if (itr == _molecules.end()) {
4705        ERROR("Molecule not found: %s", id.c_str());
4706        return;
4707    }
4708
4709    do {
4710        itr->second->setAtomVisibility(state);
4711    } while (doAll && ++itr != _molecules.end());
4712
4713    _needsRedraw = true;
4714}
4715
4716/**
4717 * \brief Turn on/off rendering of the Molecule bonds for the given DataSet
4718 */
4719void Renderer::setMoleculeBondVisibility(const DataSetId& id, bool state)
4720{
4721    MoleculeHashmap::iterator itr;
4722
4723    bool doAll = false;
4724
4725    if (id.compare("all") == 0) {
4726        itr = _molecules.begin();
4727        doAll = true;
4728    } else {
4729        itr = _molecules.find(id);
4730    }
4731    if (itr == _molecules.end()) {
4732        ERROR("Molecule not found: %s", id.c_str());
4733        return;
4734    }
4735
4736    do {
4737        itr->second->setBondVisibility(state);
4738    } while (doAll && ++itr != _molecules.end());
4739
4740    _needsRedraw = true;
4741}
4742
4743/**
4744 * \brief Turn on/off rendering of the Molecule for the given DataSet
4745 */
4746void Renderer::setMoleculeVisibility(const DataSetId& id, bool state)
4747{
4748    MoleculeHashmap::iterator itr;
4749
4750    bool doAll = false;
4751
4752    if (id.compare("all") == 0) {
4753        itr = _molecules.begin();
4754        doAll = true;
4755    } else {
4756        itr = _molecules.find(id);
4757    }
4758    if (itr == _molecules.end()) {
4759        ERROR("Molecule not found: %s", id.c_str());
4760        return;
4761    }
4762
4763    do {
4764        itr->second->setVisibility(state);
4765    } while (doAll && ++itr != _molecules.end());
4766
4767    _needsRedraw = true;
4768}
4769
4770/**
4771 * \brief Set the visibility of polygon edges for the specified DataSet
4772 */
4773void Renderer::setMoleculeEdgeVisibility(const DataSetId& id, bool state)
4774{
4775    MoleculeHashmap::iterator itr;
4776
4777    bool doAll = false;
4778
4779    if (id.compare("all") == 0) {
4780        itr = _molecules.begin();
4781        doAll = true;
4782    } else {
4783        itr = _molecules.find(id);
4784    }
4785    if (itr == _molecules.end()) {
4786        ERROR("Molecule not found: %s", id.c_str());
4787        return;
4788    }
4789
4790    do {
4791        itr->second->setEdgeVisibility(state);
4792    } while (doAll && ++itr != _molecules.end());
4793
4794    _needsRedraw = true;
4795}
4796
4797/**
4798 * \brief Set the RGB polygon edge color for the specified DataSet
4799 */
4800void Renderer::setMoleculeEdgeColor(const DataSetId& id, float color[3])
4801{
4802    MoleculeHashmap::iterator itr;
4803
4804    bool doAll = false;
4805
4806    if (id.compare("all") == 0) {
4807        itr = _molecules.begin();
4808        doAll = true;
4809    } else {
4810        itr = _molecules.find(id);
4811    }
4812    if (itr == _molecules.end()) {
4813        ERROR("Molecule not found: %s", id.c_str());
4814        return;
4815    }
4816
4817    do {
4818        itr->second->setEdgeColor(color);
4819    } while (doAll && ++itr != _molecules.end());
4820
4821    _needsRedraw = true;
4822}
4823
4824/**
4825 * \brief Set the polygon edge width for the specified DataSet (may be a no-op)
4826 *
4827 * If the OpenGL implementation/hardware does not support wide lines,
4828 * this function may not have an effect.
4829 */
4830void Renderer::setMoleculeEdgeWidth(const DataSetId& id, float edgeWidth)
4831{
4832    MoleculeHashmap::iterator itr;
4833
4834    bool doAll = false;
4835
4836    if (id.compare("all") == 0) {
4837        itr = _molecules.begin();
4838        doAll = true;
4839    } else {
4840        itr = _molecules.find(id);
4841    }
4842    if (itr == _molecules.end()) {
4843        ERROR("Molecule not found: %s", id.c_str());
4844        return;
4845    }
4846
4847    do {
4848        itr->second->setEdgeWidth(edgeWidth);
4849    } while (doAll && ++itr != _molecules.end());
4850
4851    _needsRedraw = true;
4852}
4853
4854/**
4855 * \brief Set wireframe rendering for the specified DataSet
4856 */
4857void Renderer::setMoleculeWireframe(const DataSetId& id, bool state)
4858{
4859    MoleculeHashmap::iterator itr;
4860
4861    bool doAll = false;
4862
4863    if (id.compare("all") == 0) {
4864        itr = _molecules.begin();
4865        doAll = true;
4866    } else {
4867        itr = _molecules.find(id);
4868    }
4869    if (itr == _molecules.end()) {
4870        ERROR("Molecule not found: %s", id.c_str());
4871        return;
4872    }
4873
4874    do {
4875        itr->second->setWireframe(state);
4876    } while (doAll && ++itr != _molecules.end());
4877
4878    _needsRedraw = true;
4879}
4880
4881/**
4882 * \brief Turn Molecule lighting on/off for the specified DataSet
4883 */
4884void Renderer::setMoleculeLighting(const DataSetId& id, bool state)
4885{
4886    MoleculeHashmap::iterator itr;
4887
4888    bool doAll = false;
4889
4890    if (id.compare("all") == 0) {
4891        itr = _molecules.begin();
4892        doAll = true;
4893    } else {
4894        itr = _molecules.find(id);
4895    }
4896    if (itr == _molecules.end()) {
4897        ERROR("Molecule not found: %s", id.c_str());
4898        return;
4899    }
4900
4901    do {
4902        itr->second->setLighting(state);
4903    } while (doAll && ++itr != _molecules.end());
4904
4905    _needsRedraw = true;
4906}
4907
4908/**
4909 * \brief Create a new PolyData and associate it with the named DataSet
4910 */
4911bool Renderer::addPolyData(const DataSetId& id)
4912{
4913    DataSetHashmap::iterator itr;
4914
4915    bool doAll = false;
4916
4917    if (id.compare("all") == 0) {
4918        itr = _dataSets.begin();
4919    } else {
4920        itr = _dataSets.find(id);
4921    }
4922    if (itr == _dataSets.end()) {
4923        ERROR("Unknown dataset %s", id.c_str());
4924        return false;
4925    }
4926
4927    do {
4928        DataSet *ds = itr->second;
4929        const DataSetId& dsID = ds->getName();
4930
4931        if (getPolyData(dsID)) {
4932            WARN("Replacing existing PolyData %s", dsID.c_str());
4933            deletePolyData(dsID);
4934        }
4935
4936        PolyData *polyData = new PolyData();
4937        _polyDatas[dsID] = polyData;
4938
4939        polyData->setDataSet(ds);
4940
4941        _renderer->AddViewProp(polyData->getProp());
4942    } while (doAll && ++itr != _dataSets.end());
4943
4944    if (_cameraMode == IMAGE)
4945        setCameraMode(PERSPECTIVE);
4946    initCamera();
4947    _needsRedraw = true;
4948    return true;
4949}
4950
4951/**
4952 * \brief Get the PolyData associated with a named DataSet
4953 */
4954PolyData *Renderer::getPolyData(const DataSetId& id)
4955{
4956    PolyDataHashmap::iterator itr = _polyDatas.find(id);
4957
4958    if (itr == _polyDatas.end()) {
4959#ifdef DEBUG
4960        TRACE("PolyData not found: %s", id.c_str());
4961#endif
4962        return NULL;
4963    } else
4964        return itr->second;
4965}
4966
4967/**
4968 * \brief Set an additional transform on the prop
4969 */
4970void Renderer::setPolyDataTransform(const DataSetId& id, vtkMatrix4x4 *trans)
4971{
4972    PolyDataHashmap::iterator itr;
4973
4974    bool doAll = false;
4975
4976    if (id.compare("all") == 0) {
4977        itr = _polyDatas.begin();
4978        doAll = true;
4979    } else {
4980        itr = _polyDatas.find(id);
4981    }
4982    if (itr == _polyDatas.end()) {
4983        ERROR("PolyData not found: %s", id.c_str());
4984        return;
4985    }
4986
4987    do {
4988        itr->second->setTransform(trans);
4989    } while (doAll && ++itr != _polyDatas.end());
4990
4991    resetAxes();
4992    _needsRedraw = true;
4993}
4994
4995/**
4996 * \brief Set the prop orientation with a quaternion
4997 */
4998void Renderer::setPolyDataOrientation(const DataSetId& id, double quat[4])
4999{
5000    PolyDataHashmap::iterator itr;
5001
5002    bool doAll = false;
5003
5004    if (id.compare("all") == 0) {
5005        itr = _polyDatas.begin();
5006        doAll = true;
5007    } else {
5008        itr = _polyDatas.find(id);
5009    }
5010    if (itr == _polyDatas.end()) {
5011        ERROR("PolyData not found: %s", id.c_str());
5012        return;
5013    }
5014
5015    do {
5016        itr->second->setOrientation(quat);
5017    } while (doAll && ++itr != _polyDatas.end());
5018
5019    resetAxes();
5020    _needsRedraw = true;
5021}
5022
5023/**
5024 * \brief Set the prop orientation with a rotation about an axis
5025 */
5026void Renderer::setPolyDataOrientation(const DataSetId& id, double angle, double axis[3])
5027{
5028    PolyDataHashmap::iterator itr;
5029
5030    bool doAll = false;
5031
5032    if (id.compare("all") == 0) {
5033        itr = _polyDatas.begin();
5034        doAll = true;
5035    } else {
5036        itr = _polyDatas.find(id);
5037    }
5038    if (itr == _polyDatas.end()) {
5039        ERROR("PolyData not found: %s", id.c_str());
5040        return;
5041    }
5042
5043    do {
5044        itr->second->setOrientation(angle, axis);
5045    } while (doAll && ++itr != _polyDatas.end());
5046
5047    resetAxes();
5048    _needsRedraw = true;
5049}
5050
5051/**
5052 * \brief Set the prop position in world coords
5053 */
5054void Renderer::setPolyDataPosition(const DataSetId& id, double pos[3])
5055{
5056    PolyDataHashmap::iterator itr;
5057
5058    bool doAll = false;
5059
5060    if (id.compare("all") == 0) {
5061        itr = _polyDatas.begin();
5062        doAll = true;
5063    } else {
5064        itr = _polyDatas.find(id);
5065    }
5066    if (itr == _polyDatas.end()) {
5067        ERROR("PolyData not found: %s", id.c_str());
5068        return;
5069    }
5070
5071    do {
5072        itr->second->setPosition(pos);
5073    } while (doAll && ++itr != _polyDatas.end());
5074
5075    resetAxes();
5076    _needsRedraw = true;
5077}
5078
5079/**
5080 * \brief Set the prop scaling
5081 */
5082void Renderer::setPolyDataScale(const DataSetId& id, double scale[3])
5083{
5084    PolyDataHashmap::iterator itr;
5085
5086    bool doAll = false;
5087
5088    if (id.compare("all") == 0) {
5089        itr = _polyDatas.begin();
5090        doAll = true;
5091    } else {
5092        itr = _polyDatas.find(id);
5093    }
5094    if (itr == _polyDatas.end()) {
5095        ERROR("PolyData not found: %s", id.c_str());
5096        return;
5097    }
5098
5099    do {
5100        itr->second->setScale(scale);
5101    } while (doAll && ++itr != _polyDatas.end());
5102
5103    resetAxes();
5104    _needsRedraw = true;
5105}
5106
5107/**
5108 * \brief Set opacity of the PolyData for the given DataSet
5109 */
5110void Renderer::setPolyDataOpacity(const DataSetId& id, double opacity)
5111{
5112    PolyDataHashmap::iterator itr;
5113
5114    bool doAll = false;
5115
5116    if (id.compare("all") == 0) {
5117        itr = _polyDatas.begin();
5118        doAll = true;
5119    } else {
5120        itr = _polyDatas.find(id);
5121    }
5122    if (itr == _polyDatas.end()) {
5123        ERROR("PolyData not found: %s", id.c_str());
5124        return;
5125    }
5126
5127    do {
5128        itr->second->setOpacity(opacity);
5129    } while (doAll && ++itr != _polyDatas.end());
5130
5131    _needsRedraw = true;
5132}
5133
5134/**
5135 * \brief Turn on/off rendering of the PolyData mapper for the given DataSet
5136 */
5137void Renderer::setPolyDataVisibility(const DataSetId& id, bool state)
5138{
5139    PolyDataHashmap::iterator itr;
5140
5141    bool doAll = false;
5142
5143    if (id.compare("all") == 0) {
5144        itr = _polyDatas.begin();
5145        doAll = true;
5146    } else {
5147        itr = _polyDatas.find(id);
5148    }
5149    if (itr == _polyDatas.end()) {
5150        ERROR("PolyData not found: %s", id.c_str());
5151        return;
5152    }
5153
5154    do {
5155        itr->second->setVisibility(state);
5156    } while (doAll && ++itr != _polyDatas.end());
5157
5158    _needsRedraw = true;
5159}
5160
5161/**
5162 * \brief Set the RGB polygon face color for the specified DataSet
5163 */
5164void Renderer::setPolyDataColor(const DataSetId& id, float color[3])
5165{
5166    PolyDataHashmap::iterator itr;
5167
5168    bool doAll = false;
5169
5170    if (id.compare("all") == 0) {
5171        itr = _polyDatas.begin();
5172        doAll = true;
5173    } else {
5174        itr = _polyDatas.find(id);
5175    }
5176    if (itr == _polyDatas.end()) {
5177        ERROR("PolyData not found: %s", id.c_str());
5178        return;
5179    }
5180
5181    do {
5182        itr->second->setColor(color);
5183    } while (doAll && ++itr != _polyDatas.end());
5184    _needsRedraw = true;
5185}
5186
5187/**
5188 * \brief Set the visibility of polygon edges for the specified DataSet
5189 */
5190void Renderer::setPolyDataEdgeVisibility(const DataSetId& id, bool state)
5191{
5192    PolyDataHashmap::iterator itr;
5193
5194    bool doAll = false;
5195
5196    if (id.compare("all") == 0) {
5197        itr = _polyDatas.begin();
5198        doAll = true;
5199    } else {
5200        itr = _polyDatas.find(id);
5201    }
5202    if (itr == _polyDatas.end()) {
5203        ERROR("PolyData not found: %s", id.c_str());
5204        return;
5205    }
5206
5207    do {
5208        itr->second->setEdgeVisibility(state);
5209    } while (doAll && ++itr != _polyDatas.end());
5210
5211    _needsRedraw = true;
5212}
5213
5214/**
5215 * \brief Set the RGB polygon edge color for the specified DataSet
5216 */
5217void Renderer::setPolyDataEdgeColor(const DataSetId& id, float color[3])
5218{
5219    PolyDataHashmap::iterator itr;
5220
5221    bool doAll = false;
5222
5223    if (id.compare("all") == 0) {
5224        itr = _polyDatas.begin();
5225        doAll = true;
5226    } else {
5227        itr = _polyDatas.find(id);
5228    }
5229    if (itr == _polyDatas.end()) {
5230        ERROR("PolyData not found: %s", id.c_str());
5231        return;
5232    }
5233
5234    do {
5235        itr->second->setEdgeColor(color);
5236    } while (doAll && ++itr != _polyDatas.end());
5237
5238    _needsRedraw = true;
5239}
5240
5241/**
5242 * \brief Set the polygon edge width for the specified DataSet (may be a no-op)
5243 *
5244 * If the OpenGL implementation/hardware does not support wide lines,
5245 * this function may not have an effect.
5246 */
5247void Renderer::setPolyDataEdgeWidth(const DataSetId& id, float edgeWidth)
5248{
5249    PolyDataHashmap::iterator itr;
5250
5251    bool doAll = false;
5252
5253    if (id.compare("all") == 0) {
5254        itr = _polyDatas.begin();
5255        doAll = true;
5256    } else {
5257        itr = _polyDatas.find(id);
5258    }
5259    if (itr == _polyDatas.end()) {
5260        ERROR("PolyData not found: %s", id.c_str());
5261        return;
5262    }
5263
5264    do {
5265        itr->second->setEdgeWidth(edgeWidth);
5266    } while (doAll && ++itr != _polyDatas.end());
5267
5268    _needsRedraw = true;
5269}
5270
5271/**
5272 * \brief Set the point size for the specified DataSet (may be a no-op)
5273 *
5274 * If the OpenGL implementation/hardware does not support wide points,
5275 * this function may not have an effect.
5276 */
5277void Renderer::setPolyDataPointSize(const DataSetId& id, float size)
5278{
5279    PolyDataHashmap::iterator itr;
5280
5281    bool doAll = false;
5282
5283    if (id.compare("all") == 0) {
5284        itr = _polyDatas.begin();
5285        doAll = true;
5286    } else {
5287        itr = _polyDatas.find(id);
5288    }
5289    if (itr == _polyDatas.end()) {
5290        ERROR("PolyData not found: %s", id.c_str());
5291        return;
5292    }
5293
5294    do {
5295        itr->second->setPointSize(size);
5296    } while (doAll && ++itr != _polyDatas.end());
5297
5298    _needsRedraw = true;
5299}
5300
5301/**
5302 * \brief Set wireframe rendering for the specified DataSet
5303 */
5304void Renderer::setPolyDataWireframe(const DataSetId& id, bool state)
5305{
5306    PolyDataHashmap::iterator itr;
5307
5308    bool doAll = false;
5309
5310    if (id.compare("all") == 0) {
5311        itr = _polyDatas.begin();
5312        doAll = true;
5313    } else {
5314        itr = _polyDatas.find(id);
5315    }
5316    if (itr == _polyDatas.end()) {
5317        ERROR("PolyData not found: %s", id.c_str());
5318        return;
5319    }
5320
5321    do {
5322        itr->second->setWireframe(state);
5323    } while (doAll && ++itr != _polyDatas.end());
5324
5325    _needsRedraw = true;
5326}
5327
5328/**
5329 * \brief Turn mesh lighting on/off for the specified DataSet
5330 */
5331void Renderer::setPolyDataLighting(const DataSetId& id, bool state)
5332{
5333    PolyDataHashmap::iterator itr;
5334
5335    bool doAll = false;
5336
5337    if (id.compare("all") == 0) {
5338        itr = _polyDatas.begin();
5339        doAll = true;
5340    } else {
5341        itr = _polyDatas.find(id);
5342    }
5343    if (itr == _polyDatas.end()) {
5344        ERROR("PolyData not found: %s", id.c_str());
5345        return;
5346    }
5347
5348    do {
5349        itr->second->setLighting(state);
5350    } while (doAll && ++itr != _polyDatas.end());
5351
5352    _needsRedraw = true;
5353}
5354
5355/**
5356 * \brief Create a new PseudoColor rendering for the specified DataSet
5357 */
5358bool Renderer::addPseudoColor(const DataSetId& id)
5359{
5360    DataSetHashmap::iterator itr;
5361
5362    bool doAll = false;
5363
5364    if (id.compare("all") == 0) {
5365        itr = _dataSets.begin();
5366    } else {
5367        itr = _dataSets.find(id);
5368    }
5369    if (itr == _dataSets.end()) {
5370        ERROR("Unknown dataset %s", id.c_str());
5371        return false;
5372    }
5373
5374    do {
5375        DataSet *ds = itr->second;
5376        const DataSetId& dsID = ds->getName();
5377
5378        if (getPseudoColor(dsID)) {
5379            WARN("Replacing existing PseudoColor %s", dsID.c_str());
5380            deletePseudoColor(dsID);
5381        }
5382        PseudoColor *pc = new PseudoColor();
5383        _pseudoColors[dsID] = pc;
5384
5385        pc->setDataSet(ds,
5386                       _useCumulativeRange,
5387                       _cumulativeScalarRange,
5388                       _cumulativeVectorMagnitudeRange,
5389                       _cumulativeVectorComponentRange);
5390
5391        _renderer->AddViewProp(pc->getProp());
5392    } while (doAll && ++itr != _dataSets.end());
5393
5394    initCamera();
5395    _needsRedraw = true;
5396    return true;
5397}
5398
5399/**
5400 * \brief Get the PseudoColor associated with the specified DataSet
5401 */
5402PseudoColor *Renderer::getPseudoColor(const DataSetId& id)
5403{
5404    PseudoColorHashmap::iterator itr = _pseudoColors.find(id);
5405
5406    if (itr == _pseudoColors.end()) {
5407#ifdef DEBUG
5408        TRACE("PseudoColor not found: %s", id.c_str());
5409#endif
5410        return NULL;
5411    } else
5412        return itr->second;
5413}
5414
5415/**
5416 * \brief Set the prop orientation with a quaternion
5417 */
5418void Renderer::setPseudoColorTransform(const DataSetId& id, vtkMatrix4x4 *trans)
5419{
5420    PseudoColorHashmap::iterator itr;
5421
5422    bool doAll = false;
5423
5424    if (id.compare("all") == 0) {
5425        itr = _pseudoColors.begin();
5426        doAll = true;
5427    } else {
5428        itr = _pseudoColors.find(id);
5429    }
5430    if (itr == _pseudoColors.end()) {
5431        ERROR("PseudoColor not found: %s", id.c_str());
5432        return;
5433    }
5434
5435    do {
5436        itr->second->setTransform(trans);
5437    } while (doAll && ++itr != _pseudoColors.end());
5438
5439    resetAxes();
5440    _needsRedraw = true;
5441}
5442
5443/**
5444 * \brief Set the prop orientation with a quaternion
5445 */
5446void Renderer::setPseudoColorOrientation(const DataSetId& id, double quat[4])
5447{
5448    PseudoColorHashmap::iterator itr;
5449
5450    bool doAll = false;
5451
5452    if (id.compare("all") == 0) {
5453        itr = _pseudoColors.begin();
5454        doAll = true;
5455    } else {
5456        itr = _pseudoColors.find(id);
5457    }
5458    if (itr == _pseudoColors.end()) {
5459        ERROR("PseudoColor not found: %s", id.c_str());
5460        return;
5461    }
5462
5463    do {
5464        itr->second->setOrientation(quat);
5465    } while (doAll && ++itr != _pseudoColors.end());
5466
5467    resetAxes();
5468    _needsRedraw = true;
5469}
5470
5471/**
5472 * \brief Set the prop orientation with a rotation about an axis
5473 */
5474void Renderer::setPseudoColorOrientation(const DataSetId& id, double angle, double axis[3])
5475{
5476    PseudoColorHashmap::iterator itr;
5477
5478    bool doAll = false;
5479
5480    if (id.compare("all") == 0) {
5481        itr = _pseudoColors.begin();
5482        doAll = true;
5483    } else {
5484        itr = _pseudoColors.find(id);
5485    }
5486    if (itr == _pseudoColors.end()) {
5487        ERROR("PseudoColor not found: %s", id.c_str());
5488        return;
5489    }
5490
5491    do {
5492        itr->second->setOrientation(angle, axis);
5493    } while (doAll && ++itr != _pseudoColors.end());
5494
5495    resetAxes();
5496    _needsRedraw = true;
5497}
5498
5499/**
5500 * \brief Set the prop position in world coords
5501 */
5502void Renderer::setPseudoColorPosition(const DataSetId& id, double pos[3])
5503{
5504    PseudoColorHashmap::iterator itr;
5505
5506    bool doAll = false;
5507
5508    if (id.compare("all") == 0) {
5509        itr = _pseudoColors.begin();
5510        doAll = true;
5511    } else {
5512        itr = _pseudoColors.find(id);
5513    }
5514    if (itr == _pseudoColors.end()) {
5515        ERROR("PseudoColor not found: %s", id.c_str());
5516        return;
5517    }
5518
5519    do {
5520        itr->second->setPosition(pos);
5521    } while (doAll && ++itr != _pseudoColors.end());
5522
5523    resetAxes();
5524    _needsRedraw = true;
5525}
5526
5527/**
5528 * \brief Set the prop scaling
5529 */
5530void Renderer::setPseudoColorScale(const DataSetId& id, double scale[3])
5531{
5532    PseudoColorHashmap::iterator itr;
5533
5534    bool doAll = false;
5535
5536    if (id.compare("all") == 0) {
5537        itr = _pseudoColors.begin();
5538        doAll = true;
5539    } else {
5540        itr = _pseudoColors.find(id);
5541    }
5542    if (itr == _pseudoColors.end()) {
5543        ERROR("PseudoColor not found: %s", id.c_str());
5544        return;
5545    }
5546
5547    do {
5548        itr->second->setScale(scale);
5549    } while (doAll && ++itr != _pseudoColors.end());
5550
5551    resetAxes();
5552    _needsRedraw = true;
5553}
5554
5555/**
5556 * \brief Associate an existing named color map with a PseudoColor for the given DataSet
5557 */
5558void Renderer::setPseudoColorColorMap(const DataSetId& id, const ColorMapId& colorMapId)
5559{
5560    PseudoColorHashmap::iterator itr;
5561
5562    bool doAll = false;
5563
5564    if (id.compare("all") == 0) {
5565        itr = _pseudoColors.begin();
5566        doAll = true;
5567    } else {
5568        itr = _pseudoColors.find(id);
5569    }
5570
5571    if (itr == _pseudoColors.end()) {
5572        ERROR("PseudoColor not found: %s", id.c_str());
5573        return;
5574    }
5575
5576    ColorMap *cmap = getColorMap(colorMapId);
5577    if (cmap == NULL) {
5578        ERROR("Unknown colormap: %s", colorMapId.c_str());
5579        return;
5580    }
5581
5582    do {
5583        TRACE("Set PseudoColor color map: %s for dataset %s", colorMapId.c_str(),
5584              itr->second->getDataSet()->getName().c_str());
5585
5586        itr->second->setColorMap(cmap);
5587    } while (doAll && ++itr != _pseudoColors.end());
5588
5589    _needsRedraw = true;
5590}
5591
5592/**
5593 * \brief Set opacity of the PseudoColor for the given DataSet
5594 */
5595void Renderer::setPseudoColorOpacity(const DataSetId& id, double opacity)
5596{
5597    PseudoColorHashmap::iterator itr;
5598
5599    bool doAll = false;
5600
5601    if (id.compare("all") == 0) {
5602        itr = _pseudoColors.begin();
5603        doAll = true;
5604    } else {
5605        itr = _pseudoColors.find(id);
5606    }
5607
5608    if (itr == _pseudoColors.end()) {
5609        ERROR("PseudoColor not found: %s", id.c_str());
5610        return;
5611    }
5612
5613    do {
5614        itr->second->setOpacity(opacity);
5615    } while (doAll && ++itr != _pseudoColors.end());
5616
5617    _needsRedraw = true;
5618}
5619
5620/**
5621 * \brief Turn on/off rendering of the PseudoColor mapper for the given DataSet
5622 */
5623void Renderer::setPseudoColorVisibility(const DataSetId& id, bool state)
5624{
5625    PseudoColorHashmap::iterator itr;
5626
5627    bool doAll = false;
5628
5629    if (id.compare("all") == 0) {
5630        itr = _pseudoColors.begin();
5631        doAll = true;
5632    } else {
5633        itr = _pseudoColors.find(id);
5634    }
5635
5636    if (itr == _pseudoColors.end()) {
5637        ERROR("PseudoColor not found: %s", id.c_str());
5638        return;
5639    }
5640
5641    do {
5642        itr->second->setVisibility(state);
5643    } while (doAll && ++itr != _pseudoColors.end());
5644
5645    _needsRedraw = true;
5646}
5647
5648/**
5649 * \brief Set the visibility of polygon edges for the specified DataSet
5650 */
5651void Renderer::setPseudoColorEdgeVisibility(const DataSetId& id, bool state)
5652{
5653    PseudoColorHashmap::iterator itr;
5654
5655    bool doAll = false;
5656
5657    if (id.compare("all") == 0) {
5658        itr = _pseudoColors.begin();
5659        doAll = true;
5660    } else {
5661        itr = _pseudoColors.find(id);
5662    }
5663
5664    if (itr == _pseudoColors.end()) {
5665        ERROR("PseudoColor not found: %s", id.c_str());
5666        return;
5667    }
5668
5669    do {
5670        itr->second->setEdgeVisibility(state);
5671    } while (doAll && ++itr != _pseudoColors.end());
5672
5673    _needsRedraw = true;
5674}
5675
5676/**
5677 * \brief Set the RGB polygon edge color for the specified DataSet
5678 */
5679void Renderer::setPseudoColorEdgeColor(const DataSetId& id, float color[3])
5680{
5681    PseudoColorHashmap::iterator itr;
5682
5683    bool doAll = false;
5684
5685    if (id.compare("all") == 0) {
5686        itr = _pseudoColors.begin();
5687        doAll = true;
5688    } else {
5689        itr = _pseudoColors.find(id);
5690    }
5691
5692    if (itr == _pseudoColors.end()) {
5693        ERROR("PseudoColor not found: %s", id.c_str());
5694        return;
5695    }
5696
5697    do {
5698        itr->second->setEdgeColor(color);
5699    } while (doAll && ++itr != _pseudoColors.end());
5700
5701    _needsRedraw = true;
5702}
5703
5704/**
5705 * \brief Set the polygon edge width for the specified DataSet (may be a no-op)
5706 *
5707 * If the OpenGL implementation/hardware does not support wide lines,
5708 * this function may not have an effect.
5709 */
5710void Renderer::setPseudoColorEdgeWidth(const DataSetId& id, float edgeWidth)
5711{
5712    PseudoColorHashmap::iterator itr;
5713
5714    bool doAll = false;
5715
5716    if (id.compare("all") == 0) {
5717        itr = _pseudoColors.begin();
5718        doAll = true;
5719    } else {
5720        itr = _pseudoColors.find(id);
5721    }
5722
5723    if (itr == _pseudoColors.end()) {
5724        ERROR("PseudoColor not found: %s", id.c_str());
5725        return;
5726    }
5727
5728    do {
5729        itr->second->setEdgeWidth(edgeWidth);
5730    } while (doAll && ++itr != _pseudoColors.end());
5731
5732    _needsRedraw = true;
5733}
5734
5735/**
5736 * \brief Set wireframe rendering for the specified DataSet
5737 */
5738void Renderer::setPseudoColorWireframe(const DataSetId& id, bool state)
5739{
5740    PseudoColorHashmap::iterator itr;
5741
5742    bool doAll = false;
5743
5744    if (id.compare("all") == 0) {
5745        itr = _pseudoColors.begin();
5746        doAll = true;
5747    } else {
5748        itr = _pseudoColors.find(id);
5749    }
5750    if (itr == _pseudoColors.end()) {
5751        ERROR("PseudoColor not found: %s", id.c_str());
5752        return;
5753    }
5754
5755    do {
5756        itr->second->setWireframe(state);
5757    } while (doAll && ++itr != _pseudoColors.end());
5758
5759    _needsRedraw = true;
5760}
5761
5762/**
5763 * \brief Turn mesh lighting on/off for the specified DataSet
5764 */
5765void Renderer::setPseudoColorLighting(const DataSetId& id, bool state)
5766{
5767    PseudoColorHashmap::iterator itr;
5768
5769    bool doAll = false;
5770
5771    if (id.compare("all") == 0) {
5772        itr = _pseudoColors.begin();
5773        doAll = true;
5774    } else {
5775        itr = _pseudoColors.find(id);
5776    }
5777
5778    if (itr == _pseudoColors.end()) {
5779        ERROR("PseudoColor not found: %s", id.c_str());
5780        return;
5781    }
5782
5783    do {
5784        itr->second->setLighting(state);
5785    } while (doAll && ++itr != _pseudoColors.end());
5786
5787    _needsRedraw = true;
5788}
5789
5790/**
5791 * \brief Create a new Streamlines and associate it with the named DataSet
5792 */
5793bool Renderer::addStreamlines(const DataSetId& id)
5794{
5795    DataSetHashmap::iterator itr;
5796
5797    bool doAll = false;
5798
5799    if (id.compare("all") == 0) {
5800        itr = _dataSets.begin();
5801    } else {
5802        itr = _dataSets.find(id);
5803    }
5804    if (itr == _dataSets.end()) {
5805        ERROR("Unknown dataset %s", id.c_str());
5806        return false;
5807    }
5808
5809    do {
5810        DataSet *ds = itr->second;
5811        const DataSetId& dsID = ds->getName();
5812
5813        if (getStreamlines(dsID)) {
5814            WARN("Replacing existing Streamlines %s", dsID.c_str());
5815            deleteStreamlines(dsID);
5816        }
5817
5818        Streamlines *streamlines = new Streamlines();
5819        _streamlines[dsID] = streamlines;
5820
5821        streamlines->setDataSet(ds,
5822                                _useCumulativeRange,
5823                                _cumulativeScalarRange,
5824                                _cumulativeVectorMagnitudeRange,
5825                                _cumulativeVectorComponentRange);
5826
5827        _renderer->AddViewProp(streamlines->getProp());
5828    } while (doAll && ++itr != _dataSets.end());
5829
5830    initCamera();
5831    _needsRedraw = true;
5832    return true;
5833}
5834
5835/**
5836 * \brief Get the Streamlines associated with a named DataSet
5837 */
5838Streamlines *Renderer::getStreamlines(const DataSetId& id)
5839{
5840    StreamlinesHashmap::iterator itr = _streamlines.find(id);
5841
5842    if (itr == _streamlines.end()) {
5843#ifdef DEBUG
5844        TRACE("Streamlines not found: %s", id.c_str());
5845#endif
5846        return NULL;
5847    } else
5848        return itr->second;
5849}
5850
5851/**
5852 * \brief Set the prop orientation with a quaternion
5853 */
5854void Renderer::setStreamlinesTransform(const DataSetId& id, vtkMatrix4x4 *trans)
5855{
5856    StreamlinesHashmap::iterator itr;
5857
5858    bool doAll = false;
5859
5860    if (id.compare("all") == 0) {
5861        itr = _streamlines.begin();
5862        doAll = true;
5863    } else {
5864        itr = _streamlines.find(id);
5865    }
5866    if (itr == _streamlines.end()) {
5867        ERROR("Streamlines not found: %s", id.c_str());
5868        return;
5869    }
5870
5871    do {
5872        itr->second->setTransform(trans);
5873    } while (doAll && ++itr != _streamlines.end());
5874
5875    resetAxes();
5876    _needsRedraw = true;
5877}
5878
5879/**
5880 * \brief Set the prop orientation with a quaternion
5881 */
5882void Renderer::setStreamlinesOrientation(const DataSetId& id, double quat[4])
5883{
5884    StreamlinesHashmap::iterator itr;
5885
5886    bool doAll = false;
5887
5888    if (id.compare("all") == 0) {
5889        itr = _streamlines.begin();
5890        doAll = true;
5891    } else {
5892        itr = _streamlines.find(id);
5893    }
5894    if (itr == _streamlines.end()) {
5895        ERROR("Streamlines not found: %s", id.c_str());
5896        return;
5897    }
5898
5899    do {
5900        itr->second->setOrientation(quat);
5901    } while (doAll && ++itr != _streamlines.end());
5902
5903    resetAxes();
5904    _needsRedraw = true;
5905}
5906
5907/**
5908 * \brief Set the prop orientation with a rotation about an axis
5909 */
5910void Renderer::setStreamlinesOrientation(const DataSetId& id, double angle, double axis[3])
5911{
5912    StreamlinesHashmap::iterator itr;
5913
5914    bool doAll = false;
5915
5916    if (id.compare("all") == 0) {
5917        itr = _streamlines.begin();
5918        doAll = true;
5919    } else {
5920        itr = _streamlines.find(id);
5921    }
5922    if (itr == _streamlines.end()) {
5923        ERROR("Streamlines not found: %s", id.c_str());
5924        return;
5925    }
5926
5927    do {
5928        itr->second->setOrientation(angle, axis);
5929    } while (doAll && ++itr != _streamlines.end());
5930
5931    resetAxes();
5932    _needsRedraw = true;
5933}
5934
5935/**
5936 * \brief Set the prop position in world coords
5937 */
5938void Renderer::setStreamlinesPosition(const DataSetId& id, double pos[3])
5939{
5940    StreamlinesHashmap::iterator itr;
5941
5942    bool doAll = false;
5943
5944    if (id.compare("all") == 0) {
5945        itr = _streamlines.begin();
5946        doAll = true;
5947    } else {
5948        itr = _streamlines.find(id);
5949    }
5950    if (itr == _streamlines.end()) {
5951        ERROR("Streamlines not found: %s", id.c_str());
5952        return;
5953    }
5954
5955    do {
5956        itr->second->setPosition(pos);
5957    } while (doAll && ++itr != _streamlines.end());
5958
5959    resetAxes();
5960    _needsRedraw = true;
5961}
5962
5963/**
5964 * \brief Set the prop scaling
5965 */
5966void Renderer::setStreamlinesScale(const DataSetId& id, double scale[3])
5967{
5968    StreamlinesHashmap::iterator itr;
5969
5970    bool doAll = false;
5971
5972    if (id.compare("all") == 0) {
5973        itr = _streamlines.begin();
5974        doAll = true;
5975    } else {
5976        itr = _streamlines.find(id);
5977    }
5978    if (itr == _streamlines.end()) {
5979        ERROR("Streamlines not found: %s", id.c_str());
5980        return;
5981    }
5982
5983    do {
5984        itr->second->setScale(scale);
5985    } while (doAll && ++itr != _streamlines.end());
5986
5987    resetAxes();
5988    _needsRedraw = true;
5989}
5990
5991/**
5992 * \brief Set the streamlines seed to points distributed randomly inside
5993 * cells of DataSet
5994 */
5995void Renderer::setStreamlinesSeedToRandomPoints(const DataSetId& id, int numPoints)
5996{
5997    StreamlinesHashmap::iterator itr;
5998
5999    bool doAll = false;
6000
6001    if (id.compare("all") == 0) {
6002        itr = _streamlines.begin();
6003        doAll = true;
6004    } else {
6005        itr = _streamlines.find(id);
6006    }
6007    if (itr == _streamlines.end()) {
6008        ERROR("Streamlines not found: %s", id.c_str());
6009        return;
6010    }
6011
6012    do {
6013        itr->second->setSeedToRandomPoints(numPoints);
6014    } while (doAll && ++itr != _streamlines.end());
6015
6016    _needsRedraw = true;
6017}
6018
6019/**
6020 * \brief Set the streamlines seed to points along a line
6021 */
6022void Renderer::setStreamlinesSeedToRake(const DataSetId& id,
6023                                        double start[3], double end[3],
6024                                        int numPoints)
6025{
6026    StreamlinesHashmap::iterator itr;
6027
6028    bool doAll = false;
6029
6030    if (id.compare("all") == 0) {
6031        itr = _streamlines.begin();
6032        doAll = true;
6033    } else {
6034        itr = _streamlines.find(id);
6035    }
6036    if (itr == _streamlines.end()) {
6037        ERROR("Streamlines not found: %s", id.c_str());
6038        return;
6039    }
6040
6041    do {
6042        itr->second->setSeedToRake(start, end, numPoints);
6043    } while (doAll && ++itr != _streamlines.end());
6044
6045    _needsRedraw = true;
6046}
6047
6048/**
6049 * \brief Set the streamlines seed to points inside a disk, with optional
6050 * hole
6051 */
6052void Renderer::setStreamlinesSeedToDisk(const DataSetId& id,
6053                                        double center[3], double normal[3],
6054                                        double radius, double innerRadius,
6055                                        int numPoints)
6056{
6057    StreamlinesHashmap::iterator itr;
6058
6059    bool doAll = false;
6060
6061    if (id.compare("all") == 0) {
6062        itr = _streamlines.begin();
6063        doAll = true;
6064    } else {
6065        itr = _streamlines.find(id);
6066    }
6067    if (itr == _streamlines.end()) {
6068        ERROR("Streamlines not found: %s", id.c_str());
6069        return;
6070    }
6071
6072    do {
6073        itr->second->setSeedToDisk(center, normal, radius, innerRadius, numPoints);
6074    } while (doAll && ++itr != _streamlines.end());
6075
6076    _needsRedraw = true;
6077}
6078
6079/**
6080 * \brief Set the streamlines seed to vertices of an n-sided polygon
6081 */
6082void Renderer::setStreamlinesSeedToPolygon(const DataSetId& id,
6083                                           double center[3], double normal[3],
6084                                           double angle, double radius,
6085                                           int numSides)
6086{
6087    StreamlinesHashmap::iterator itr;
6088
6089    bool doAll = false;
6090
6091    if (id.compare("all") == 0) {
6092        itr = _streamlines.begin();
6093        doAll = true;
6094    } else {
6095        itr = _streamlines.find(id);
6096    }
6097    if (itr == _streamlines.end()) {
6098        ERROR("Streamlines not found: %s", id.c_str());
6099        return;
6100    }
6101
6102    do {
6103        itr->second->setSeedToPolygon(center, normal, angle, radius, numSides);
6104    } while (doAll && ++itr != _streamlines.end());
6105
6106    _needsRedraw = true;
6107}
6108
6109/**
6110 * \brief Set the streamlines seed to vertices of an n-sided polygon
6111 */
6112void Renderer::setStreamlinesSeedToFilledPolygon(const DataSetId& id,
6113                                                 double center[3],
6114                                                 double normal[3],
6115                                                 double angle, double radius,
6116                                                 int numSides, int numPoints)
6117{
6118    StreamlinesHashmap::iterator itr;
6119
6120    bool doAll = false;
6121
6122    if (id.compare("all") == 0) {
6123        itr = _streamlines.begin();
6124        doAll = true;
6125    } else {
6126        itr = _streamlines.find(id);
6127    }
6128    if (itr == _streamlines.end()) {
6129        ERROR("Streamlines not found: %s", id.c_str());
6130        return;
6131    }
6132
6133    do {
6134        itr->second->setSeedToFilledPolygon(center, normal, angle,
6135                                            radius, numSides, numPoints);
6136    } while (doAll && ++itr != _streamlines.end());
6137
6138    _needsRedraw = true;
6139}
6140
6141/**
6142 * \brief Set Streamlines rendering to polylines for the specified DataSet
6143 */
6144void Renderer::setStreamlinesTypeToLines(const DataSetId& id)
6145{
6146    StreamlinesHashmap::iterator itr;
6147
6148    bool doAll = false;
6149
6150    if (id.compare("all") == 0) {
6151        itr = _streamlines.begin();
6152        doAll = true;
6153    } else {
6154        itr = _streamlines.find(id);
6155    }
6156    if (itr == _streamlines.end()) {
6157        ERROR("Streamlines not found: %s", id.c_str());
6158        return;
6159    }
6160
6161    do {
6162        itr->second->setLineTypeToLines();
6163    } while (doAll && ++itr != _streamlines.end());
6164
6165    _needsRedraw = true;
6166}
6167
6168/**
6169 * \brief Set Streamlines rendering to tubes for the specified DataSet
6170 */
6171void Renderer::setStreamlinesTypeToTubes(const DataSetId& id, int numSides, double radius)
6172{
6173    StreamlinesHashmap::iterator itr;
6174
6175    bool doAll = false;
6176
6177    if (id.compare("all") == 0) {
6178        itr = _streamlines.begin();
6179        doAll = true;
6180    } else {
6181        itr = _streamlines.find(id);
6182    }
6183    if (itr == _streamlines.end()) {
6184        ERROR("Streamlines not found: %s", id.c_str());
6185        return;
6186    }
6187
6188    do {
6189        itr->second->setLineTypeToTubes(numSides, radius);
6190    } while (doAll && ++itr != _streamlines.end());
6191
6192    _needsRedraw = true;
6193}
6194
6195/**
6196 * \brief Set Streamlines rendering to ribbons for the specified DataSet
6197 */
6198void Renderer::setStreamlinesTypeToRibbons(const DataSetId& id, double width, double angle)
6199{
6200    StreamlinesHashmap::iterator itr;
6201
6202    bool doAll = false;
6203
6204    if (id.compare("all") == 0) {
6205        itr = _streamlines.begin();
6206        doAll = true;
6207    } else {
6208        itr = _streamlines.find(id);
6209    }
6210    if (itr == _streamlines.end()) {
6211        ERROR("Streamlines not found: %s", id.c_str());
6212        return;
6213    }
6214
6215    do {
6216        itr->second->setLineTypeToRibbons(width, angle);
6217    } while (doAll && ++itr != _streamlines.end());
6218
6219    _needsRedraw = true;
6220}
6221
6222/**
6223 * \brief Set Streamlines maximum length for the specified DataSet
6224 */
6225void Renderer::setStreamlinesLength(const DataSetId& id, double length)
6226{
6227    StreamlinesHashmap::iterator itr;
6228
6229    bool doAll = false;
6230
6231    if (id.compare("all") == 0) {
6232        itr = _streamlines.begin();
6233        doAll = true;
6234    } else {
6235        itr = _streamlines.find(id);
6236    }
6237    if (itr == _streamlines.end()) {
6238        ERROR("Streamlines not found: %s", id.c_str());
6239        return;
6240    }
6241
6242    do {
6243        itr->second->setMaxPropagation(length);
6244    } while (doAll && ++itr != _streamlines.end());
6245
6246    _needsRedraw = true;
6247}
6248
6249/**
6250 * \brief Set Streamlines opacity scaling for the specified DataSet
6251 */
6252void Renderer::setStreamlinesOpacity(const DataSetId& id, double opacity)
6253{
6254    StreamlinesHashmap::iterator itr;
6255
6256    bool doAll = false;
6257
6258    if (id.compare("all") == 0) {
6259        itr = _streamlines.begin();
6260        doAll = true;
6261    } else {
6262        itr = _streamlines.find(id);
6263    }
6264    if (itr == _streamlines.end()) {
6265        ERROR("Streamlines not found: %s", id.c_str());
6266        return;
6267    }
6268
6269    do {
6270        itr->second->setOpacity(opacity);
6271    } while (doAll && ++itr != _streamlines.end());
6272
6273    _needsRedraw = true;
6274}
6275
6276/**
6277 * \brief Turn on/off rendering of the Streamlines mapper for the given DataSet
6278 */
6279void Renderer::setStreamlinesVisibility(const DataSetId& id, bool state)
6280{
6281    StreamlinesHashmap::iterator itr;
6282
6283    bool doAll = false;
6284
6285    if (id.compare("all") == 0) {
6286        itr = _streamlines.begin();
6287        doAll = true;
6288    } else {
6289        itr = _streamlines.find(id);
6290    }
6291    if (itr == _streamlines.end()) {
6292        ERROR("Streamlines not found: %s", id.c_str());
6293        return;
6294    }
6295
6296    do {
6297        itr->second->setVisibility(state);
6298    } while (doAll && ++itr != _streamlines.end());
6299
6300    _needsRedraw = true;
6301}
6302
6303/**
6304 * \brief Turn on/off rendering of the Streamlines seed geometry for the given DataSet
6305 */
6306void Renderer::setStreamlinesSeedVisibility(const DataSetId& id, bool state)
6307{
6308    StreamlinesHashmap::iterator itr;
6309
6310    bool doAll = false;
6311
6312    if (id.compare("all") == 0) {
6313        itr = _streamlines.begin();
6314        doAll = true;
6315    } else {
6316        itr = _streamlines.find(id);
6317    }
6318    if (itr == _streamlines.end()) {
6319        ERROR("Streamlines not found: %s", id.c_str());
6320        return;
6321    }
6322
6323    do {
6324        itr->second->setSeedVisibility(state);
6325    } while (doAll && ++itr != _streamlines.end());
6326
6327    _needsRedraw = true;
6328}
6329
6330/**
6331 * \brief Set the color mode for the specified DataSet
6332 */
6333void Renderer::setStreamlinesColorMode(const DataSetId& id, Streamlines::ColorMode mode)
6334{
6335    StreamlinesHashmap::iterator itr;
6336
6337    bool doAll = false;
6338
6339    if (id.compare("all") == 0) {
6340        itr = _streamlines.begin();
6341        doAll = true;
6342    } else {
6343        itr = _streamlines.find(id);
6344    }
6345    if (itr == _streamlines.end()) {
6346        ERROR("Streamlines not found: %s", id.c_str());
6347        return;
6348    }
6349
6350    do {
6351        itr->second->setColorMode(mode);
6352    } while (doAll && ++itr != _streamlines.end());
6353
6354    _needsRedraw = true;
6355}
6356
6357/**
6358 * \brief Associate an existing named color map with a Streamlines for the given DataSet
6359 */
6360void Renderer::setStreamlinesColorMap(const DataSetId& id, const ColorMapId& colorMapId)
6361{
6362    StreamlinesHashmap::iterator itr;
6363
6364    bool doAll = false;
6365
6366    if (id.compare("all") == 0) {
6367        itr = _streamlines.begin();
6368        doAll = true;
6369    } else {
6370        itr = _streamlines.find(id);
6371    }
6372
6373    if (itr == _streamlines.end()) {
6374        ERROR("Streamlines not found: %s", id.c_str());
6375        return;
6376    }
6377
6378    ColorMap *cmap = getColorMap(colorMapId);
6379    if (cmap == NULL) {
6380        ERROR("Unknown colormap: %s", colorMapId.c_str());
6381        return;
6382    }
6383
6384    do {
6385        TRACE("Set Streamlines color map: %s for dataset %s", colorMapId.c_str(),
6386              itr->second->getDataSet()->getName().c_str());
6387
6388        itr->second->setColorMap(cmap);
6389    } while (doAll && ++itr != _streamlines.end());
6390
6391    _needsRedraw = true;
6392}
6393
6394/**
6395 * \brief Set the RGB line/edge color for the specified DataSet
6396 */
6397void Renderer::setStreamlinesSeedColor(const DataSetId& id, float color[3])
6398{
6399    StreamlinesHashmap::iterator itr;
6400
6401    bool doAll = false;
6402
6403    if (id.compare("all") == 0) {
6404        itr = _streamlines.begin();
6405        doAll = true;
6406    } else {
6407        itr = _streamlines.find(id);
6408    }
6409    if (itr == _streamlines.end()) {
6410        ERROR("Streamlines not found: %s", id.c_str());
6411        return;
6412    }
6413
6414    do {
6415        itr->second->setSeedColor(color);
6416    } while (doAll && ++itr != _streamlines.end());
6417
6418    _needsRedraw = true;
6419}
6420
6421/**
6422 * \brief Set the visibility of polygon edges for the specified DataSet
6423 */
6424void Renderer::setStreamlinesEdgeVisibility(const DataSetId& id, bool state)
6425{
6426    StreamlinesHashmap::iterator itr;
6427
6428    bool doAll = false;
6429
6430    if (id.compare("all") == 0) {
6431        itr = _streamlines.begin();
6432        doAll = true;
6433    } else {
6434        itr = _streamlines.find(id);
6435    }
6436
6437    if (itr == _streamlines.end()) {
6438        ERROR("Streamlines not found: %s", id.c_str());
6439        return;
6440    }
6441
6442    do {
6443        itr->second->setEdgeVisibility(state);
6444    } while (doAll && ++itr != _streamlines.end());
6445
6446    _needsRedraw = true;
6447}
6448
6449/**
6450 * \brief Set the RGB color for the specified DataSet
6451 */
6452void Renderer::setStreamlinesColor(const DataSetId& id, float color[3])
6453{
6454    StreamlinesHashmap::iterator itr;
6455
6456    bool doAll = false;
6457
6458    if (id.compare("all") == 0) {
6459        itr = _streamlines.begin();
6460        doAll = true;
6461    } else {
6462        itr = _streamlines.find(id);
6463    }
6464    if (itr == _streamlines.end()) {
6465        ERROR("Streamlines not found: %s", id.c_str());
6466        return;
6467    }
6468
6469    do {
6470        itr->second->setColor(color);
6471    } while (doAll && ++itr != _streamlines.end());
6472
6473    _needsRedraw = true;
6474}
6475
6476/**
6477 * \brief Set the RGB line/edge color for the specified DataSet
6478 */
6479void Renderer::setStreamlinesEdgeColor(const DataSetId& id, float color[3])
6480{
6481    StreamlinesHashmap::iterator itr;
6482
6483    bool doAll = false;
6484
6485    if (id.compare("all") == 0) {
6486        itr = _streamlines.begin();
6487        doAll = true;
6488    } else {
6489        itr = _streamlines.find(id);
6490    }
6491    if (itr == _streamlines.end()) {
6492        ERROR("Streamlines not found: %s", id.c_str());
6493        return;
6494    }
6495
6496    do {
6497        itr->second->setEdgeColor(color);
6498    } while (doAll && ++itr != _streamlines.end());
6499
6500    _needsRedraw = true;
6501}
6502
6503/**
6504 * \brief Set the line/edge width for the specified DataSet (may be a no-op)
6505 *
6506 * If the OpenGL implementation/hardware does not support wide lines,
6507 * this function may not have an effect.
6508 */
6509void Renderer::setStreamlinesEdgeWidth(const DataSetId& id, float edgeWidth)
6510{
6511    StreamlinesHashmap::iterator itr;
6512
6513    bool doAll = false;
6514
6515    if (id.compare("all") == 0) {
6516        itr = _streamlines.begin();
6517        doAll = true;
6518    } else {
6519        itr = _streamlines.find(id);
6520    }
6521    if (itr == _streamlines.end()) {
6522        ERROR("Streamlines not found: %s", id.c_str());
6523        return;
6524    }
6525
6526    do {
6527        itr->second->setEdgeWidth(edgeWidth);
6528    } while (doAll && ++itr != _streamlines.end());
6529
6530    _needsRedraw = true;
6531}
6532
6533/**
6534 * \brief Turn Streamlines lighting on/off for the specified DataSet
6535 */
6536void Renderer::setStreamlinesLighting(const DataSetId& id, bool state)
6537{
6538    StreamlinesHashmap::iterator itr;
6539
6540    bool doAll = false;
6541
6542    if (id.compare("all") == 0) {
6543        itr = _streamlines.begin();
6544        doAll = true;
6545    } else {
6546        itr = _streamlines.find(id);
6547    }
6548    if (itr == _streamlines.end()) {
6549        ERROR("Streamlines not found: %s", id.c_str());
6550        return;
6551    }
6552
6553    do {
6554        itr->second->setLighting(state);
6555    } while (doAll && ++itr != _streamlines.end());
6556
6557    _needsRedraw = true;
6558}
6559
6560/**
6561 * \brief Create a new Volume and associate it with the named DataSet
6562 */
6563bool Renderer::addVolume(const DataSetId& id)
6564{
6565    DataSetHashmap::iterator itr;
6566
6567    bool doAll = false;
6568
6569    if (id.compare("all") == 0) {
6570        itr = _dataSets.begin();
6571    } else {
6572        itr = _dataSets.find(id);
6573    }
6574    if (itr == _dataSets.end()) {
6575        ERROR("Unknown dataset %s", id.c_str());
6576        return false;
6577    }
6578
6579    do {
6580        DataSet *ds = itr->second;
6581        const DataSetId& dsID = ds->getName();
6582
6583        if (getVolume(dsID)) {
6584            WARN("Replacing existing Volume %s", dsID.c_str());
6585            deleteVolume(dsID);
6586        }
6587
6588        Volume *volume = new Volume();
6589        _volumes[dsID] = volume;
6590
6591        volume->setDataSet(ds,
6592                           _useCumulativeRange,
6593                           _cumulativeScalarRange,
6594                           _cumulativeVectorMagnitudeRange,
6595                           _cumulativeVectorComponentRange );
6596
6597        _renderer->AddViewProp(volume->getProp());
6598    } while (doAll && ++itr != _dataSets.end());
6599
6600    if (_cameraMode == IMAGE)
6601        setCameraMode(PERSPECTIVE);
6602    initCamera();
6603    _needsRedraw = true;
6604    return true;
6605}
6606
6607/**
6608 * \brief Get the Volume associated with a named DataSet
6609 */
6610Volume *Renderer::getVolume(const DataSetId& id)
6611{
6612    VolumeHashmap::iterator itr = _volumes.find(id);
6613
6614    if (itr == _volumes.end()) {
6615#ifdef DEBUG
6616        TRACE("Volume not found: %s", id.c_str());
6617#endif
6618        return NULL;
6619    } else
6620        return itr->second;
6621}
6622
6623/**
6624 * \brief Set the prop orientation with a quaternion
6625 */
6626void Renderer::setVolumeTransform(const DataSetId& id, vtkMatrix4x4 *trans)
6627{
6628    VolumeHashmap::iterator itr;
6629
6630    bool doAll = false;
6631
6632    if (id.compare("all") == 0) {
6633        itr = _volumes.begin();
6634        doAll = true;
6635    } else {
6636        itr = _volumes.find(id);
6637    }
6638    if (itr == _volumes.end()) {
6639        ERROR("Volume not found: %s", id.c_str());
6640        return;
6641    }
6642
6643    do {
6644        itr->second->setTransform(trans);
6645    } while (doAll && ++itr != _volumes.end());
6646
6647    resetAxes();
6648    _needsRedraw = true;
6649}
6650
6651/**
6652 * \brief Set the prop orientation with a quaternion
6653 */
6654void Renderer::setVolumeOrientation(const DataSetId& id, double quat[4])
6655{
6656    VolumeHashmap::iterator itr;
6657
6658    bool doAll = false;
6659
6660    if (id.compare("all") == 0) {
6661        itr = _volumes.begin();
6662        doAll = true;
6663    } else {
6664        itr = _volumes.find(id);
6665    }
6666    if (itr == _volumes.end()) {
6667        ERROR("Volume not found: %s", id.c_str());
6668        return;
6669    }
6670
6671    do {
6672        itr->second->setOrientation(quat);
6673    } while (doAll && ++itr != _volumes.end());
6674
6675    resetAxes();
6676    _needsRedraw = true;
6677}
6678
6679/**
6680 * \brief Set the prop orientation with a rotation about an axis
6681 */
6682void Renderer::setVolumeOrientation(const DataSetId& id, double angle, double axis[3])
6683{
6684    VolumeHashmap::iterator itr;
6685
6686    bool doAll = false;
6687
6688    if (id.compare("all") == 0) {
6689        itr = _volumes.begin();
6690        doAll = true;
6691    } else {
6692        itr = _volumes.find(id);
6693    }
6694    if (itr == _volumes.end()) {
6695        ERROR("Volume not found: %s", id.c_str());
6696        return;
6697    }
6698
6699    do {
6700        itr->second->setOrientation(angle, axis);
6701    } while (doAll && ++itr != _volumes.end());
6702
6703    resetAxes();
6704    _needsRedraw = true;
6705}
6706
6707/**
6708 * \brief Set the prop position in world coords
6709 */
6710void Renderer::setVolumePosition(const DataSetId& id, double pos[3])
6711{
6712    VolumeHashmap::iterator itr;
6713
6714    bool doAll = false;
6715
6716    if (id.compare("all") == 0) {
6717        itr = _volumes.begin();
6718        doAll = true;
6719    } else {
6720        itr = _volumes.find(id);
6721    }
6722    if (itr == _volumes.end()) {
6723        ERROR("Volume not found: %s", id.c_str());
6724        return;
6725    }
6726
6727    do {
6728        itr->second->setPosition(pos);
6729    } while (doAll && ++itr != _volumes.end());
6730
6731    resetAxes();
6732    _needsRedraw = true;
6733}
6734
6735/**
6736 * \brief Set the prop scaling
6737 */
6738void Renderer::setVolumeScale(const DataSetId& id, double scale[3])
6739{
6740    VolumeHashmap::iterator itr;
6741
6742    bool doAll = false;
6743
6744    if (id.compare("all") == 0) {
6745        itr = _volumes.begin();
6746        doAll = true;
6747    } else {
6748        itr = _volumes.find(id);
6749    }
6750    if (itr == _volumes.end()) {
6751        ERROR("Volume not found: %s", id.c_str());
6752        return;
6753    }
6754
6755    do {
6756        itr->second->setScale(scale);
6757    } while (doAll && ++itr != _volumes.end());
6758
6759    resetAxes();
6760    _needsRedraw = true;
6761}
6762
6763/**
6764 * \brief Associate an existing named color map with a Volume for the given DataSet
6765 */
6766void Renderer::setVolumeColorMap(const DataSetId& id, const ColorMapId& colorMapId)
6767{
6768    VolumeHashmap::iterator itr;
6769
6770    bool doAll = false;
6771
6772    if (id.compare("all") == 0) {
6773        itr = _volumes.begin();
6774        doAll = true;
6775    } else {
6776        itr = _volumes.find(id);
6777    }
6778
6779    if (itr == _volumes.end()) {
6780        ERROR("Volume not found: %s", id.c_str());
6781        return;
6782    }
6783
6784    ColorMap *cmap = getColorMap(colorMapId);
6785    if (cmap == NULL) {
6786        ERROR("Unknown colormap: %s", colorMapId.c_str());
6787        return;
6788    }
6789
6790    do {
6791        TRACE("Set Volume color map: %s for dataset %s", colorMapId.c_str(),
6792              itr->second->getDataSet()->getName().c_str());
6793
6794        itr->second->setColorMap(cmap);
6795    } while (doAll && ++itr != _volumes.end());
6796
6797    _needsRedraw = true;
6798}
6799
6800/**
6801 * \brief Set Volume opacity scaling for the specified DataSet
6802 */
6803void Renderer::setVolumeOpacity(const DataSetId& id, double opacity)
6804{
6805    VolumeHashmap::iterator itr;
6806
6807    bool doAll = false;
6808
6809    if (id.compare("all") == 0) {
6810        itr = _volumes.begin();
6811        doAll = true;
6812    } else {
6813        itr = _volumes.find(id);
6814    }
6815    if (itr == _volumes.end()) {
6816        ERROR("Volume not found: %s", id.c_str());
6817        return;
6818    }
6819
6820    do {
6821        itr->second->setOpacity(opacity);
6822    } while (doAll && ++itr != _volumes.end());
6823
6824    _needsRedraw = true;
6825}
6826
6827/**
6828 * \brief Turn on/off rendering of the Volume mapper for the given DataSet
6829 */
6830void Renderer::setVolumeVisibility(const DataSetId& id, bool state)
6831{
6832    VolumeHashmap::iterator itr;
6833
6834    bool doAll = false;
6835
6836    if (id.compare("all") == 0) {
6837        itr = _volumes.begin();
6838        doAll = true;
6839    } else {
6840        itr = _volumes.find(id);
6841    }
6842    if (itr == _volumes.end()) {
6843        ERROR("Volume not found: %s", id.c_str());
6844        return;
6845    }
6846
6847    do {
6848        itr->second->setVisibility(state);
6849    } while (doAll && ++itr != _volumes.end());
6850
6851    _needsRedraw = true;
6852}
6853
6854/**
6855 * \brief Set volume ambient lighting/shading coefficient for the specified DataSet
6856 */
6857void Renderer::setVolumeAmbient(const DataSetId& id, double coeff)
6858{
6859    VolumeHashmap::iterator itr;
6860
6861    bool doAll = false;
6862
6863    if (id.compare("all") == 0) {
6864        itr = _volumes.begin();
6865        doAll = true;
6866    } else {
6867        itr = _volumes.find(id);
6868    }
6869    if (itr == _volumes.end()) {
6870        ERROR("Volume not found: %s", id.c_str());
6871        return;
6872    }
6873
6874    do {
6875        itr->second->setAmbient(coeff);
6876    } while (doAll && ++itr != _volumes.end());
6877
6878    _needsRedraw = true;
6879}
6880
6881/**
6882 * \brief Set volume diffuse lighting/shading coefficient for the specified DataSet
6883 */
6884void Renderer::setVolumeDiffuse(const DataSetId& id, double coeff)
6885{
6886    VolumeHashmap::iterator itr;
6887
6888    bool doAll = false;
6889
6890    if (id.compare("all") == 0) {
6891        itr = _volumes.begin();
6892        doAll = true;
6893    } else {
6894        itr = _volumes.find(id);
6895    }
6896    if (itr == _volumes.end()) {
6897        ERROR("Volume not found: %s", id.c_str());
6898        return;
6899    }
6900
6901    do {
6902        itr->second->setDiffuse(coeff);
6903    } while (doAll && ++itr != _volumes.end());
6904
6905    _needsRedraw = true;
6906}
6907
6908/**
6909 * \brief Set volume specular lighting/shading coefficient and power for the specified DataSet
6910 */
6911void Renderer::setVolumeSpecular(const DataSetId& id, double coeff, double power)
6912{
6913    VolumeHashmap::iterator itr;
6914
6915    bool doAll = false;
6916
6917    if (id.compare("all") == 0) {
6918        itr = _volumes.begin();
6919        doAll = true;
6920    } else {
6921        itr = _volumes.find(id);
6922    }
6923    if (itr == _volumes.end()) {
6924        ERROR("Volume not found: %s", id.c_str());
6925        return;
6926    }
6927
6928    do {
6929        itr->second->setSpecular(coeff, power);
6930    } while (doAll && ++itr != _volumes.end());
6931
6932    _needsRedraw = true;
6933}
6934
6935/**
6936 * \brief Turn volume lighting/shading on/off for the specified DataSet
6937 */
6938void Renderer::setVolumeLighting(const DataSetId& id, bool state)
6939{
6940    VolumeHashmap::iterator itr;
6941
6942    bool doAll = false;
6943
6944    if (id.compare("all") == 0) {
6945        itr = _volumes.begin();
6946        doAll = true;
6947    } else {
6948        itr = _volumes.find(id);
6949    }
6950    if (itr == _volumes.end()) {
6951        ERROR("Volume not found: %s", id.c_str());
6952        return;
6953    }
6954
6955    do {
6956        itr->second->setLighting(state);
6957    } while (doAll && ++itr != _volumes.end());
6958
6959    _needsRedraw = true;
6960}
6961
6962/**
6963 * \brief Set camera FOV based on render window height
6964 *
6965 * Computes a field-of-view angle based on some assumptions about
6966 * viewer's distance to screen and pixel density
6967 */
6968void Renderer::setViewAngle(int height)
6969{
6970    // Distance of eyes from screen in inches
6971    double d = 20.0;
6972    // Assume 72 ppi screen
6973    double h = (double)height / 72.0;
6974
6975    double angle = vtkMath::DegreesFromRadians(2.0 * atan((h/2.0)/d));
6976    _renderer->GetActiveCamera()->SetViewAngle(angle);
6977
6978    TRACE("Setting view angle: %g", angle);
6979}
6980
6981/**
6982 * \brief Resize the render window (image size for renderings)
6983 */
6984void Renderer::setWindowSize(int width, int height)
6985{
6986    if (_windowWidth == width &&
6987        _windowHeight == height)
6988        return;
6989
6990    //setViewAngle(height);
6991
6992    // FIXME: Fix up panning on aspect change
6993#ifdef notdef
6994    if (_cameraPan[0] != 0.0) {
6995        _cameraPan[0] *= ((double)_windowWidth / width);
6996    }
6997    if (_cameraPan[1] != 0.0) {
6998        _cameraPan[1] *= ((double)_windowHeight / height);
6999    }
7000#endif
7001    _windowWidth = width;
7002    _windowHeight = height;
7003    _renderWindow->SetSize(_windowWidth, _windowHeight);
7004    if (_cameraMode == IMAGE) {
7005        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
7006                            _imgWorldDims[0], _imgWorldDims[1]);
7007    }
7008    _needsRedraw = true;
7009}
7010
7011/**
7012 * \brief Change the camera type: perspective, orthographic or image view
7013 *
7014 * Perspective mode is a normal 3D camera.
7015 *
7016 * Orthogrphic mode is parallel projection.
7017 *
7018 * Image mode is an orthographic camera with fixed axes and a clipping region
7019 * around the plot area, use setCameraZoomRegion to control the displayed area
7020 *
7021 * \param[in] mode Enum specifying camera type
7022 */
7023void Renderer::setCameraMode(CameraMode mode)
7024{
7025    if (_cameraMode == mode) return;
7026
7027    CameraMode origMode = _cameraMode;
7028    _cameraMode = mode;
7029    resetAxes();
7030
7031    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
7032    switch (mode) {
7033    case ORTHO: {
7034        TRACE("Set camera to Ortho mode");
7035        camera->ParallelProjectionOn();
7036        if (origMode == IMAGE) {
7037            resetCamera(true);
7038        }
7039        break;
7040    }
7041    case PERSPECTIVE: {
7042        TRACE("Set camera to Perspective mode");
7043        camera->ParallelProjectionOff();
7044        if (origMode == IMAGE) {
7045            resetCamera(true);
7046        }
7047        break;
7048    }
7049    case IMAGE: {
7050        camera->ParallelProjectionOn();
7051        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
7052                            _imgWorldDims[0],_imgWorldDims[1]);
7053        TRACE("Set camera to Image mode");
7054        break;
7055    }
7056    default:
7057        ERROR("Unkown camera mode: %d", mode);
7058    }
7059    _needsRedraw = true;
7060}
7061
7062/**
7063 * \brief Get the current camera mode
7064 */
7065Renderer::CameraMode Renderer::getCameraMode() const
7066{
7067    return _cameraMode;
7068}
7069
7070/**
7071 * \brief Set the VTK camera parameters based on a 4x4 view matrix
7072 */
7073void Renderer::setCameraFromMatrix(vtkCamera *camera, vtkMatrix4x4& mat)
7074{
7075    double d = camera->GetDistance();
7076    double vu[3];
7077    vu[0] = mat[1][0];
7078    vu[1] = mat[1][1];
7079    vu[2] = mat[1][2];
7080    double trans[3];
7081    trans[0] = mat[0][3];
7082    trans[1] = mat[1][3];
7083    trans[2] = mat[2][3];
7084    mat[0][3] = 0;
7085    mat[1][3] = 0;
7086    mat[2][3] = 0;
7087    double vpn[3] = {mat[2][0], mat[2][1], mat[2][2]};
7088    double pos[3];
7089    // With translation removed, we have an orthogonal matrix,
7090    // so the inverse is the transpose
7091    mat.Transpose();
7092    mat.MultiplyPoint(trans, pos);
7093    pos[0] = -pos[0];
7094    pos[1] = -pos[1];
7095    pos[2] = -pos[2];
7096    double fp[3];
7097    fp[0] = pos[0] - vpn[0] * d;
7098    fp[1] = pos[1] - vpn[1] * d;
7099    fp[2] = pos[2] - vpn[2] * d;
7100    camera->SetPosition(pos);
7101    camera->SetFocalPoint(fp);
7102    camera->SetViewUp(vu);
7103}
7104
7105inline void quaternionToMatrix4x4(const double quat[4], vtkMatrix4x4& mat)
7106{
7107    double ww = quat[0]*quat[0];
7108    double wx = quat[0]*quat[1];
7109    double wy = quat[0]*quat[2];
7110    double wz = quat[0]*quat[3];
7111
7112    double xx = quat[1]*quat[1];
7113    double yy = quat[2]*quat[2];
7114    double zz = quat[3]*quat[3];
7115
7116    double xy = quat[1]*quat[2];
7117    double xz = quat[1]*quat[3];
7118    double yz = quat[2]*quat[3];
7119
7120    double rr = xx + yy + zz;
7121    // normalization factor, just in case quaternion was not normalized
7122    double f = double(1)/double(sqrt(ww + rr));
7123    double s = (ww - rr)*f;
7124    f *= 2;
7125
7126    mat[0][0] = xx*f + s;
7127    mat[1][0] = (xy + wz)*f;
7128    mat[2][0] = (xz - wy)*f;
7129
7130    mat[0][1] = (xy - wz)*f;
7131    mat[1][1] = yy*f + s;
7132    mat[2][1] = (yz + wx)*f;
7133
7134    mat[0][2] = (xz + wy)*f;
7135    mat[1][2] = (yz - wx)*f;
7136    mat[2][2] = zz*f + s;
7137}
7138
7139inline void quaternionToTransposeMatrix4x4(const double quat[4], vtkMatrix4x4& mat)
7140{
7141    double ww = quat[0]*quat[0];
7142    double wx = quat[0]*quat[1];
7143    double wy = quat[0]*quat[2];
7144    double wz = quat[0]*quat[3];
7145
7146    double xx = quat[1]*quat[1];
7147    double yy = quat[2]*quat[2];
7148    double zz = quat[3]*quat[3];
7149
7150    double xy = quat[1]*quat[2];
7151    double xz = quat[1]*quat[3];
7152    double yz = quat[2]*quat[3];
7153
7154    double rr = xx + yy + zz;
7155    // normalization factor, just in case quaternion was not normalized
7156    double f = double(1)/double(sqrt(ww + rr));
7157    double s = (ww - rr)*f;
7158    f *= 2;
7159
7160    mat[0][0] = xx*f + s;
7161    mat[0][1] = (xy + wz)*f;
7162    mat[0][2] = (xz - wy)*f;
7163
7164    mat[1][0] = (xy - wz)*f;
7165    mat[1][1] = yy*f + s;
7166    mat[1][2] = (yz + wx)*f;
7167
7168    mat[2][0] = (xz + wy)*f;
7169    mat[2][1] = (yz - wx)*f;
7170    mat[2][2] = zz*f + s;
7171}
7172
7173inline double *quatMult(const double q1[4], const double q2[4], double result[4])
7174{
7175    double q1w = q1[0];
7176    double q1x = q1[1];
7177    double q1y = q1[2];
7178    double q1z = q1[3];
7179    double q2w = q2[0];
7180    double q2x = q2[1];
7181    double q2y = q2[2];
7182    double q2z = q2[3];
7183    result[0] = (q1w*q2w) - (q1x*q2x) - (q1y*q2y) - (q1z*q2z);
7184    result[1] = (q1w*q2x) + (q1x*q2w) + (q1y*q2z) - (q1z*q2y);
7185    result[2] = (q1w*q2y) + (q1y*q2w) + (q1z*q2x) - (q1x*q2z);
7186    result[3] = (q1w*q2z) + (q1z*q2w) + (q1x*q2y) - (q1y*q2x);
7187    return result;
7188}
7189
7190inline double *quatConjugate(const double quat[4], double result[4])
7191{
7192    result[1] = -quat[1];
7193    result[2] = -quat[2];
7194    result[3] = -quat[3];
7195    return result;
7196}
7197
7198inline double *quatReciprocal(const double quat[4], double result[4])
7199{
7200    double denom =
7201        quat[0]*quat[0] +
7202        quat[1]*quat[1] +
7203        quat[2]*quat[2] +
7204        quat[3]*quat[3];
7205    quatConjugate(quat, result);
7206    for (int i = 0; i < 4; i++) {
7207        result[i] /= denom;
7208    }
7209    return result;
7210}
7211
7212inline double *quatReciprocal(double quat[4])
7213{
7214    return quatReciprocal(quat, quat);
7215}
7216
7217inline void copyQuat(const double quat[4], double result[4])
7218{
7219    memcpy(result, quat, sizeof(double)*4);
7220}
7221
7222/**
7223 * \brief Set the orientation of the camera from a quaternion
7224 * rotation
7225 *
7226 * \param[in] quat A quaternion with scalar part first: w,x,y,z
7227 * \param[in] absolute Is rotation absolute or relative?
7228 */
7229void Renderer::setCameraOrientation(const double quat[4], bool absolute)
7230{
7231    if (_cameraMode == IMAGE)
7232        return;
7233    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
7234    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
7235    vtkSmartPointer<vtkMatrix4x4> rotMat = vtkSmartPointer<vtkMatrix4x4>::New();
7236
7237    double q[4];
7238    copyQuat(quat, q);
7239
7240    if (absolute) {
7241        double abs[4];
7242        // Save absolute rotation
7243        copyQuat(q, abs);
7244        // Compute relative rotation
7245        quatMult(quatReciprocal(_cameraOrientation), q, q);
7246        // Store absolute rotation
7247        copyQuat(abs, _cameraOrientation);
7248    } else {
7249        // Compute new absolute rotation
7250        quatMult(_cameraOrientation, q, _cameraOrientation);
7251    }
7252
7253    quaternionToTransposeMatrix4x4(q, *rotMat);
7254#ifdef DEBUG
7255    TRACE("Rotation matrix:\n %g %g %g\n %g %g %g\n %g %g %g",
7256          (*rotMat)[0][0], (*rotMat)[0][1], (*rotMat)[0][2],
7257          (*rotMat)[1][0], (*rotMat)[1][1], (*rotMat)[1][2],
7258          (*rotMat)[2][0], (*rotMat)[2][1], (*rotMat)[2][2]);
7259    vtkSmartPointer<vtkMatrix4x4> camMat = vtkSmartPointer<vtkMatrix4x4>::New();
7260    camMat->DeepCopy(camera->GetViewTransformMatrix());
7261    TRACE("Camera matrix:\n %g %g %g %g\n %g %g %g %g\n %g %g %g %g\n %g %g %g %g",
7262          (*camMat)[0][0], (*camMat)[0][1], (*camMat)[0][2], (*camMat)[0][3],
7263          (*camMat)[1][0], (*camMat)[1][1], (*camMat)[1][2], (*camMat)[1][3],
7264          (*camMat)[2][0], (*camMat)[2][1], (*camMat)[2][2], (*camMat)[2][3],
7265          (*camMat)[3][0], (*camMat)[3][1], (*camMat)[3][2], (*camMat)[3][3]);
7266    printCameraInfo(camera);
7267#endif
7268    trans->Translate(0, 0, -camera->GetDistance());
7269    trans->Concatenate(rotMat);
7270    trans->Translate(0, 0, camera->GetDistance());
7271    trans->Concatenate(camera->GetViewTransformMatrix());
7272    setCameraFromMatrix(camera, *trans->GetMatrix());
7273
7274    _renderer->ResetCameraClippingRange();
7275    printCameraInfo(camera);
7276    _needsRedraw = true;
7277}
7278
7279/**
7280 * \brief Set the position and orientation of the camera
7281 *
7282 * \param[in] position x,y,z position of camera in world coordinates
7283 * \param[in] focalPoint x,y,z look-at point in world coordinates
7284 * \param[in] viewUp x,y,z up vector of camera
7285 */
7286void Renderer::setCameraOrientationAndPosition(const double position[3],
7287                                               const double focalPoint[3],
7288                                               const double viewUp[3])
7289{
7290    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
7291    camera->SetPosition(position);
7292    camera->SetFocalPoint(focalPoint);
7293    camera->SetViewUp(viewUp);
7294    _renderer->ResetCameraClippingRange();
7295    _needsRedraw = true;
7296}
7297
7298/**
7299 * \brief Get the position and orientation of the camera
7300 *
7301 * \param[out] position x,y,z position of camera in world coordinates
7302 * \param[out] focalPoint x,y,z look-at point in world coordinates
7303 * \param[out] viewUp x,y,z up vector of camera
7304 */
7305void Renderer::getCameraOrientationAndPosition(double position[3],
7306                                               double focalPoint[3],
7307                                               double viewUp[3])
7308{
7309    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
7310    camera->GetPosition(position);
7311    camera->GetFocalPoint(focalPoint);
7312    camera->GetViewUp(viewUp);
7313}
7314
7315/**
7316 * \brief Reset pan, zoom, clipping planes and optionally rotation
7317 *
7318 * \param[in] resetOrientation Reset the camera rotation/orientation also
7319 */
7320void Renderer::resetCamera(bool resetOrientation)
7321{
7322    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
7323    if (_cameraMode == IMAGE) {
7324        initCamera();
7325    } else {
7326        if (resetOrientation) {
7327            camera->SetPosition(0, 0, 1);
7328            camera->SetFocalPoint(0, 0, 0);
7329            camera->SetViewUp(0, 1, 0);
7330            _cameraOrientation[0] = 1.0;
7331            _cameraOrientation[1] = 0.0;
7332            _cameraOrientation[2] = 0.0;
7333            _cameraOrientation[3] = 0.0;
7334        }
7335        setViewAngle(_windowHeight);
7336        double bounds[6];
7337        collectBounds(bounds, false);
7338        _renderer->ResetCamera(bounds);
7339        _renderer->ResetCameraClippingRange();
7340        //computeScreenWorldCoords();
7341    }
7342
7343    printCameraInfo(camera);
7344
7345    _cameraZoomRatio = 1;
7346    _cameraPan[0] = 0;
7347    _cameraPan[1] = 0;
7348
7349    _needsRedraw = true;
7350}
7351
7352void Renderer::resetCameraClippingRange()
7353{
7354    _renderer->ResetCameraClippingRange();
7355}
7356
7357/**
7358 * \brief Perform a relative rotation to current camera orientation
7359 *
7360 * Angles are in degrees, rotation is about focal point
7361 */
7362void Renderer::rotateCamera(double yaw, double pitch, double roll)
7363{
7364    if (_cameraMode == IMAGE)
7365        return;
7366    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
7367    camera->Azimuth(yaw); // Rotate about object
7368    //camera->SetYaw(yaw); // Rotate about camera
7369    camera->Elevation(pitch); // Rotate about object
7370    //camera->SetPitch(pitch); // Rotate about camera
7371    camera->Roll(roll); // Roll about camera view axis
7372    _renderer->ResetCameraClippingRange();
7373    //computeScreenWorldCoords();
7374    _needsRedraw = true;
7375}
7376
7377/**
7378 * \brief Perform a 2D translation of the camera
7379 *
7380 * x,y pan amount are specified as signed absolute pan amount in viewport
7381 * units -- i.e. 0 is no pan, .5 is half the viewport, 2 is twice the viewport,
7382 * etc.
7383 *
7384 * \param[in] x Viewport coordinate horizontal panning (positive number pans
7385 * camera left, object right)
7386 * \param[in] y Viewport coordinate vertical panning (positive number pans
7387 * camera up, object down)
7388 * \param[in] absolute Control if pan amount is relative to current or absolute
7389 */
7390void Renderer::panCamera(double x, double y, bool absolute)
7391{
7392    TRACE("Enter panCamera: %g %g, current abs: %g %g",
7393          x, y, _cameraPan[0], _cameraPan[1]);
7394
7395    if (_cameraMode == IMAGE) {
7396        // Reverse x rather than y, since we are panning the camera, and client
7397        // expects to be panning/moving the object
7398        x = -x * _screenWorldCoords[2];
7399        y = y * _screenWorldCoords[3];
7400
7401        if (absolute) {
7402            double panAbs[2];
7403            panAbs[0] = x;
7404            panAbs[1] = y;
7405            x -= _cameraPan[0];
7406            y -= _cameraPan[1];
7407            _cameraPan[0] = panAbs[0];
7408            _cameraPan[1] = panAbs[1];
7409        } else {
7410            _cameraPan[0] += x;
7411            _cameraPan[1] += y;
7412        }
7413
7414        _imgWorldOrigin[0] += x;
7415        _imgWorldOrigin[1] += y;
7416        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
7417                            _imgWorldDims[0], _imgWorldDims[1]);
7418    } else {
7419        y = -y;
7420        if (absolute) {
7421            double panAbs[2];
7422            panAbs[0] = x;
7423            panAbs[1] = y;
7424            x -= _cameraPan[0];
7425            y -= _cameraPan[1];
7426            _cameraPan[0] = panAbs[0];
7427            _cameraPan[1] = panAbs[1];
7428        } else {
7429            _cameraPan[0] += x;
7430            _cameraPan[1] += y;
7431        }
7432
7433        if (x != 0.0 || y != 0.0) {
7434            vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
7435            double viewFocus[4], focalDepth, viewPoint[3];
7436            double newPickPoint[4], oldPickPoint[4], motionVector[3];
7437
7438            camera->GetFocalPoint(viewFocus);
7439            computeWorldToDisplay(viewFocus[0], viewFocus[1], viewFocus[2],
7440                                  viewFocus);
7441            focalDepth = viewFocus[2];
7442
7443            computeDisplayToWorld(( x * 2. + 1.) * _windowWidth / 2.0,
7444                                  ( y * 2. + 1.) * _windowHeight / 2.0,
7445                                  focalDepth,
7446                                  newPickPoint);
7447
7448            computeDisplayToWorld(_windowWidth / 2.0,
7449                                  _windowHeight / 2.0,
7450                                  focalDepth,
7451                                  oldPickPoint);
7452
7453            // Camera motion is reversed
7454            motionVector[0] = oldPickPoint[0] - newPickPoint[0];
7455            motionVector[1] = oldPickPoint[1] - newPickPoint[1];
7456            motionVector[2] = oldPickPoint[2] - newPickPoint[2];
7457
7458            camera->GetFocalPoint(viewFocus);
7459            camera->GetPosition(viewPoint);
7460            camera->SetFocalPoint(motionVector[0] + viewFocus[0],
7461                                  motionVector[1] + viewFocus[1],
7462                                  motionVector[2] + viewFocus[2]);
7463
7464            camera->SetPosition(motionVector[0] + viewPoint[0],
7465                                motionVector[1] + viewPoint[1],
7466                                motionVector[2] + viewPoint[2]);
7467
7468            _renderer->ResetCameraClippingRange();
7469            //computeScreenWorldCoords();
7470        }
7471    }
7472
7473    TRACE("Leave panCamera: %g %g, current abs: %g %g",
7474          x, y, _cameraPan[0], _cameraPan[1]);
7475
7476    _needsRedraw = true;
7477}
7478
7479/**
7480 * \brief Dolly camera or set orthographic scaling based on camera type
7481 *
7482 * \param[in] z Ratio to change zoom (greater than 1 is zoom in, less than 1 is zoom out)
7483 * \param[in] absolute Control if zoom factor is relative to current setting or absolute
7484 */
7485void Renderer::zoomCamera(double z, bool absolute)
7486{
7487    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
7488    TRACE("Enter Zoom: current abs: %g, z: %g, view angle %g",
7489          _cameraZoomRatio, z, camera->GetViewAngle());
7490
7491    if (absolute) {
7492        assert(_cameraZoomRatio > 0.0);
7493        double zAbs = z;
7494        z *= 1.0/_cameraZoomRatio;
7495        _cameraZoomRatio = zAbs;
7496    } else {
7497        _cameraZoomRatio *= z;
7498    }
7499
7500    if (_cameraMode == IMAGE) {
7501        double dx = _imgWorldDims[0];
7502        double dy = _imgWorldDims[1];
7503        _imgWorldDims[0] /= z;
7504        _imgWorldDims[1] /= z;
7505        dx -= _imgWorldDims[0];
7506        dy -= _imgWorldDims[1];
7507        _imgWorldOrigin[0] += dx/2.0;
7508        _imgWorldOrigin[1] += dy/2.0;
7509        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
7510                            _imgWorldDims[0], _imgWorldDims[1]);
7511    } else {
7512        // Keep ortho and perspective modes in sync
7513        // Move camera forward/back for perspective camera
7514        camera->Dolly(z);
7515        // Change ortho parallel scale
7516        camera->SetParallelScale(camera->GetParallelScale()/z);
7517        _renderer->ResetCameraClippingRange();
7518        //computeScreenWorldCoords();
7519    }
7520
7521    TRACE("Leave Zoom: rel %g, new abs: %g, view angle %g",
7522          z, _cameraZoomRatio, camera->GetViewAngle());
7523
7524    _needsRedraw = true;
7525}
7526
7527/**
7528 * \brief Set the pan/zoom using a corner and dimensions in pixel coordinates
7529 *
7530 * \param[in] x left pixel coordinate
7531 * \param[in] y bottom  pixel coordinate (with y=0 at top of window)
7532 * \param[in] width Width of zoom region in pixel coordinates
7533 * \param[in] height Height of zoom region in pixel coordinates
7534 */
7535void Renderer::setCameraZoomRegionPixels(int x, int y, int width, int height)
7536{
7537    double wx, wy, ww, wh;
7538
7539    y = _windowHeight - y;
7540    double pxToWorldX = _screenWorldCoords[2] / (double)_windowWidth;
7541    double pxToWorldY = _screenWorldCoords[3] / (double)_windowHeight;
7542
7543    wx = _screenWorldCoords[0] + x * pxToWorldX;
7544    wy = _screenWorldCoords[1] + y * pxToWorldY;
7545    ww = abs(width) *  pxToWorldX;
7546    wh = abs(height) * pxToWorldY;
7547    setCameraZoomRegion(wx, wy, ww, wh);
7548
7549    TRACE("\npx: %d %d %d %d\nworld: %g %g %g %g",
7550          x, y, width, height,
7551          wx, wy, ww, wh);
7552}
7553
7554/**
7555 * \brief Set the pan/zoom using a corner and dimensions in world coordinates
7556 *
7557 * \param[in] x left world coordinate
7558 * \param[in] y bottom  world coordinate
7559 * \param[in] width Width of zoom region in world coordinates
7560 * \param[in] height Height of zoom region in world coordinates
7561 */
7562void Renderer::setCameraZoomRegion(double x, double y, double width, double height)
7563{
7564    double camPos[2];
7565
7566    int pxOffsetX = (int)(0.17 * (double)_windowWidth);
7567    pxOffsetX = (pxOffsetX > 100 ? 100 : pxOffsetX);
7568    int pxOffsetY = (int)(0.15 * (double)_windowHeight);
7569    pxOffsetY = (pxOffsetY > 75 ? 75 : pxOffsetY);
7570    int outerGutter = (int)(0.03 * (double)_windowWidth);
7571    outerGutter = (outerGutter > 15 ? 15 : outerGutter);
7572
7573    int imgHeightPx = _windowHeight - pxOffsetY - outerGutter;
7574    int imgWidthPx = _windowWidth - pxOffsetX - outerGutter;
7575
7576    double imgAspect = width / height;
7577    double winAspect = (double)_windowWidth / _windowHeight;
7578
7579    double pxToWorld;
7580
7581    if (imgAspect >= winAspect) {
7582        pxToWorld = width / imgWidthPx;
7583    } else {
7584        pxToWorld = height / imgHeightPx;
7585    }
7586
7587    double offsetX = pxOffsetX * pxToWorld;
7588    double offsetY = pxOffsetY * pxToWorld;
7589
7590    TRACE("Window: %d %d", _windowWidth, _windowHeight);
7591    TRACE("ZoomRegion: %g %g %g %g", x, y, width, height);
7592    TRACE("pxToWorld: %g", pxToWorld);
7593    TRACE("offset: %g %g", offsetX, offsetY);
7594
7595    setCameraMode(IMAGE);
7596
7597    _imgWorldOrigin[0] = x;
7598    _imgWorldOrigin[1] = y;
7599    _imgWorldDims[0] = width;
7600    _imgWorldDims[1] = height;
7601
7602    camPos[0] = _imgWorldOrigin[0] - offsetX + (_windowWidth * pxToWorld)/2.0;
7603    camPos[1] = _imgWorldOrigin[1] - offsetY + (_windowHeight * pxToWorld)/2.0;
7604
7605    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
7606    camera->ParallelProjectionOn();
7607    camera->SetClippingRange(1, 2);
7608    // Half of world coordinate height of viewport (Documentation is wrong)
7609    camera->SetParallelScale(_windowHeight * pxToWorld / 2.0);
7610
7611    if (_imgCameraPlane == PLANE_XY) {
7612        // XY plane
7613        camera->SetPosition(camPos[0], camPos[1], _imgCameraOffset + 1.);
7614        camera->SetFocalPoint(camPos[0], camPos[1], _imgCameraOffset);
7615        camera->SetViewUp(0, 1, 0);
7616        // bottom
7617        _cameraClipPlanes[0]->SetOrigin(0, _imgWorldOrigin[1], 0);
7618        _cameraClipPlanes[0]->SetNormal(0, 1, 0);
7619        // left
7620        _cameraClipPlanes[1]->SetOrigin(_imgWorldOrigin[0], 0, 0);
7621        _cameraClipPlanes[1]->SetNormal(1, 0, 0);
7622        // top
7623        _cameraClipPlanes[2]->SetOrigin(0, _imgWorldOrigin[1] + _imgWorldDims[1], 0);
7624        _cameraClipPlanes[2]->SetNormal(0, -1, 0);
7625        // right
7626        _cameraClipPlanes[3]->SetOrigin(_imgWorldOrigin[0] + _imgWorldDims[0], 0, 0);
7627        _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
7628        _cubeAxesActor2D->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWorldDims[0],
7629                                    _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWorldDims[1],
7630                                    _imgCameraOffset, _imgCameraOffset);
7631        _cubeAxesActor2D->XAxisVisibilityOn();
7632        _cubeAxesActor2D->YAxisVisibilityOn();
7633        _cubeAxesActor2D->ZAxisVisibilityOff();
7634    } else if (_imgCameraPlane == PLANE_ZY) {
7635        // ZY plane
7636        camera->SetPosition(_imgCameraOffset - 1., camPos[1], camPos[0]);
7637        camera->SetFocalPoint(_imgCameraOffset, camPos[1], camPos[0]);
7638        camera->SetViewUp(0, 1, 0);
7639        // bottom
7640        _cameraClipPlanes[0]->SetOrigin(0, _imgWorldOrigin[1], 0);
7641        _cameraClipPlanes[0]->SetNormal(0, 1, 0);
7642        // left
7643        _cameraClipPlanes[1]->SetOrigin(0, 0, _imgWorldOrigin[0]);
7644        _cameraClipPlanes[1]->SetNormal(0, 0, 1);
7645        // top
7646        _cameraClipPlanes[2]->SetOrigin(0, _imgWorldOrigin[1] + _imgWorldDims[1], 0);
7647        _cameraClipPlanes[2]->SetNormal(0, -1, 0);
7648        // right
7649        _cameraClipPlanes[3]->SetOrigin(0, 0, _imgWorldOrigin[0] + _imgWorldDims[0]);
7650        _cameraClipPlanes[3]->SetNormal(0, 0, -1);
7651        _cubeAxesActor2D->SetBounds(_imgCameraOffset, _imgCameraOffset,
7652                                    _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWorldDims[1],
7653                                    _imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWorldDims[0]);
7654        _cubeAxesActor2D->XAxisVisibilityOff();
7655        _cubeAxesActor2D->YAxisVisibilityOn();
7656        _cubeAxesActor2D->ZAxisVisibilityOn();
7657    } else {
7658        // XZ plane
7659        camera->SetPosition(camPos[0], _imgCameraOffset - 1., camPos[1]);
7660        camera->SetFocalPoint(camPos[0], _imgCameraOffset, camPos[1]);
7661        camera->SetViewUp(0, 0, 1);
7662        // bottom
7663        _cameraClipPlanes[0]->SetOrigin(0, 0, _imgWorldOrigin[1]);
7664        _cameraClipPlanes[0]->SetNormal(0, 0, 1);
7665        // left
7666        _cameraClipPlanes[1]->SetOrigin(_imgWorldOrigin[0], 0, 0);
7667        _cameraClipPlanes[1]->SetNormal(1, 0, 0);
7668        // top
7669        _cameraClipPlanes[2]->SetOrigin(0, 0, _imgWorldOrigin[1] + _imgWorldDims[1]);
7670        _cameraClipPlanes[2]->SetNormal(0, 0, -1);
7671        // right
7672        _cameraClipPlanes[3]->SetOrigin(_imgWorldOrigin[0] + _imgWorldDims[0], 0, 0);
7673        _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
7674        _cubeAxesActor2D->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWorldDims[0],
7675                                    _imgCameraOffset, _imgCameraOffset,
7676                                    _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWorldDims[1]);
7677        _cubeAxesActor2D->XAxisVisibilityOn();
7678        _cubeAxesActor2D->YAxisVisibilityOff();
7679        _cubeAxesActor2D->ZAxisVisibilityOn();
7680    }
7681
7682    // Compute screen world coordinates
7683    computeScreenWorldCoords();
7684
7685#ifdef DEBUG
7686    printCameraInfo(camera);
7687#endif
7688
7689    _needsRedraw = true;
7690}
7691
7692/**
7693 * \brief Convert pixel/display coordinates to world coordinates based on current camera
7694 */
7695void Renderer::computeDisplayToWorld(double x, double y, double z, double worldPt[4])
7696{
7697    _renderer->SetDisplayPoint(x, y, z);
7698    _renderer->DisplayToWorld();
7699    _renderer->GetWorldPoint(worldPt);
7700    if (worldPt[3]) {
7701        worldPt[0] /= worldPt[3];
7702        worldPt[1] /= worldPt[3];
7703        worldPt[2] /= worldPt[3];
7704        worldPt[3] = 1.0;
7705    }
7706}
7707
7708/**
7709 * \brief Convert world coordinates to pixel/display coordinates based on current camera
7710 */
7711void Renderer::computeWorldToDisplay(double x, double y, double z, double displayPt[3])
7712{
7713    _renderer->SetWorldPoint(x, y, z, 1.0);
7714    _renderer->WorldToDisplay();
7715    _renderer->GetDisplayPoint(displayPt);
7716}
7717
7718/**
7719 * \brief Compute the world coordinate bounds of the display rectangle
7720 */
7721void Renderer::computeScreenWorldCoords()
7722{
7723    // Start with viewport coords [-1,1]
7724    double x0 = -1;
7725    double y0 = -1;
7726    double z0 = -1;
7727    double x1 = 1;
7728    double y1 = 1;
7729    double z1 = -1;
7730
7731    vtkMatrix4x4 *mat = vtkMatrix4x4::New();
7732    double result[4];
7733
7734    // get the perspective transformation from the active camera
7735    mat->DeepCopy(_renderer->GetActiveCamera()->
7736                  GetCompositeProjectionTransformMatrix(_renderer->GetTiledAspectRatio(),0,1));
7737
7738    // use the inverse matrix
7739    mat->Invert();
7740
7741    // Transform point to world coordinates
7742    result[0] = x0;
7743    result[1] = y0;
7744    result[2] = z0;
7745    result[3] = 1.0;
7746
7747    mat->MultiplyPoint(result, result);
7748
7749    // Get the transformed vector & set WorldPoint
7750    // while we are at it try to keep w at one
7751    if (result[3]) {
7752        x0 = result[0] / result[3];
7753        y0 = result[1] / result[3];
7754        z0 = result[2] / result[3];
7755    }
7756
7757    result[0] = x1;
7758    result[1] = y1;
7759    result[2] = z1;
7760    result[3] = 1.0;
7761
7762    mat->MultiplyPoint(result, result);
7763
7764    if (result[3]) {
7765        x1 = result[0] / result[3];
7766        y1 = result[1] / result[3];
7767        z1 = result[2] / result[3];
7768    }
7769
7770    mat->Delete();
7771
7772    if (_imgCameraPlane == PLANE_XZ) {
7773        _screenWorldCoords[0] = x0;
7774        _screenWorldCoords[1] = z0;
7775        _screenWorldCoords[2] = x1 - x0;
7776        _screenWorldCoords[3] = z1 - z0;
7777    } else if (_imgCameraPlane == PLANE_ZY) {
7778        _screenWorldCoords[0] = z0;
7779        _screenWorldCoords[1] = y0;
7780        _screenWorldCoords[2] = z1 - z0;
7781        _screenWorldCoords[3] = y1 - y0;
7782    } else {
7783        // XY
7784        _screenWorldCoords[0] = x0;
7785        _screenWorldCoords[1] = y0;
7786        _screenWorldCoords[2] = x1 - x0;
7787        _screenWorldCoords[3] = y1 - y0;
7788    }
7789}
7790
7791/**
7792 * \brief Get the world coordinates of the image camera plot area
7793 *
7794 * \param[out] xywh Array to hold x,y,width,height world coordinates
7795 */
7796void Renderer::getCameraZoomRegion(double xywh[4]) const
7797{
7798    xywh[0] = _imgWorldOrigin[0];
7799    xywh[1] = _imgWorldOrigin[1];
7800    xywh[2] = _imgWorldDims[0];
7801    xywh[3] = _imgWorldDims[1];
7802}
7803
7804/**
7805 * \brief Get the world origin and dimensions of the screen
7806 *
7807 * \param[out] xywh Array to hold x,y,width,height world coordinates
7808 */
7809void Renderer::getScreenWorldCoords(double xywh[4]) const
7810{
7811    memcpy(xywh, _screenWorldCoords, sizeof(double)*4);
7812}
7813
7814/**
7815 * \brief Compute bounding box containing the two input bounding boxes
7816 *
7817 * \param[out] boundsDest Union of the two input bounding boxes
7818 * \param[in] bounds1 Input bounding box
7819 * \param[in] bounds2 Input bounding box
7820 */
7821void Renderer::mergeBounds(double *boundsDest,
7822                           const double *bounds1, const double *bounds2)
7823{
7824    assert(boundsDest != NULL);
7825    assert(bounds1 != NULL);
7826    if (bounds2 == NULL) {
7827        WARN("NULL bounds2 array");
7828        return;
7829    }
7830    for (int i = 0; i < 6; i++) {
7831        if (i % 2 == 0)
7832            boundsDest[i] = min2(bounds1[i], bounds2[i]);
7833        else
7834            boundsDest[i] = max2(bounds1[i], bounds2[i]);
7835    }
7836}
7837
7838/**
7839 * \brief Collect bounds of all graphics objects
7840 *
7841 * \param[out] bounds Bounds of all scene objects
7842 * \param[in] onlyVisible Only collect bounds of visible objects
7843 */
7844void Renderer::collectBounds(double *bounds, bool onlyVisible)
7845{
7846    bounds[0] = DBL_MAX;
7847    bounds[1] = -DBL_MAX;
7848    bounds[2] = DBL_MAX;
7849    bounds[3] = -DBL_MAX;
7850    bounds[4] = DBL_MAX;
7851    bounds[5] = -DBL_MAX;
7852
7853    for (DataSetHashmap::iterator itr = _dataSets.begin();
7854             itr != _dataSets.end(); ++itr) {
7855        if ((!onlyVisible || itr->second->getVisibility()) &&
7856            itr->second->getProp() != NULL)
7857            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7858    }
7859    for (Contour2DHashmap::iterator itr = _contour2Ds.begin();
7860             itr != _contour2Ds.end(); ++itr) {
7861        if ((!onlyVisible || itr->second->getVisibility()) &&
7862            itr->second->getProp() != NULL)
7863            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7864    }
7865    for (Contour3DHashmap::iterator itr = _contour3Ds.begin();
7866             itr != _contour3Ds.end(); ++itr) {
7867        if ((!onlyVisible || itr->second->getVisibility()) &&
7868            itr->second->getProp() != NULL)
7869            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7870    }
7871    for (GlyphsHashmap::iterator itr = _glyphs.begin();
7872             itr != _glyphs.end(); ++itr) {
7873        if ((!onlyVisible || itr->second->getVisibility()) &&
7874            itr->second->getProp() != NULL)
7875            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7876    }
7877    for (HeightMapHashmap::iterator itr = _heightMaps.begin();
7878             itr != _heightMaps.end(); ++itr) {
7879        if ((!onlyVisible || itr->second->getVisibility()) &&
7880            itr->second->getProp() != NULL)
7881            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7882    }
7883    for (LICHashmap::iterator itr = _lics.begin();
7884             itr != _lics.end(); ++itr) {
7885        if ((!onlyVisible || itr->second->getVisibility()) &&
7886            itr->second->getProp() != NULL)
7887            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7888    }
7889    for (MoleculeHashmap::iterator itr = _molecules.begin();
7890             itr != _molecules.end(); ++itr) {
7891        if ((!onlyVisible || itr->second->getVisibility()) &&
7892            itr->second->getProp() != NULL)
7893            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7894    }
7895    for (PolyDataHashmap::iterator itr = _polyDatas.begin();
7896             itr != _polyDatas.end(); ++itr) {
7897        if ((!onlyVisible || itr->second->getVisibility()) &&
7898            itr->second->getProp() != NULL)
7899            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7900    }
7901    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
7902             itr != _pseudoColors.end(); ++itr) {
7903        if ((!onlyVisible || itr->second->getVisibility()) &&
7904            itr->second->getProp() != NULL)
7905            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7906    }
7907    for (StreamlinesHashmap::iterator itr = _streamlines.begin();
7908             itr != _streamlines.end(); ++itr) {
7909        if ((!onlyVisible || itr->second->getVisibility()) &&
7910            itr->second->getProp() != NULL)
7911            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7912    }
7913    for (VolumeHashmap::iterator itr = _volumes.begin();
7914             itr != _volumes.end(); ++itr) {
7915        if ((!onlyVisible || itr->second->getVisibility()) &&
7916            itr->second->getProp() != NULL)
7917            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7918    }
7919
7920    for (int i = 0; i < 6; i += 2) {
7921        if (bounds[i+1] < bounds[i]) {
7922            bounds[i] = -0.5;
7923            bounds[i+1] = 0.5;
7924        }
7925    }
7926
7927    int numDims = 0;
7928    if (bounds[0] != bounds[1])
7929        numDims++;
7930    if (bounds[2] != bounds[3])
7931        numDims++;
7932    if (bounds[4] != bounds[5])
7933        numDims++;
7934
7935    if (numDims == 0) {
7936        bounds[0] -= .5;
7937        bounds[1] += .5;
7938        bounds[2] -= .5;
7939        bounds[3] += .5;
7940    }
7941
7942    TRACE("Bounds: %g %g %g %g %g %g",
7943          bounds[0],
7944          bounds[1],
7945          bounds[2],
7946          bounds[3],
7947          bounds[4],
7948          bounds[5]);
7949}
7950
7951/**
7952 * \brief Update data ranges for color-mapping and contours
7953 */
7954void Renderer::updateRanges()
7955{
7956    collectDataRanges();
7957
7958    for (Contour2DHashmap::iterator itr = _contour2Ds.begin();
7959         itr != _contour2Ds.end(); ++itr) {
7960        itr->second->updateRanges(_useCumulativeRange,
7961                                  _cumulativeScalarRange,
7962                                  _cumulativeVectorMagnitudeRange,
7963                                  _cumulativeVectorComponentRange);
7964    }
7965    for (Contour3DHashmap::iterator itr = _contour3Ds.begin();
7966         itr != _contour3Ds.end(); ++itr) {
7967        itr->second->updateRanges(_useCumulativeRange,
7968                                  _cumulativeScalarRange,
7969                                  _cumulativeVectorMagnitudeRange,
7970                                  _cumulativeVectorComponentRange);
7971    }
7972    for (GlyphsHashmap::iterator itr = _glyphs.begin();
7973         itr != _glyphs.end(); ++itr) {
7974        itr->second->updateRanges(_useCumulativeRange,
7975                                  _cumulativeScalarRange,
7976                                  _cumulativeVectorMagnitudeRange,
7977                                  _cumulativeVectorComponentRange);
7978    }
7979    for (HeightMapHashmap::iterator itr = _heightMaps.begin();
7980         itr != _heightMaps.end(); ++itr) {
7981        itr->second->updateRanges(_useCumulativeRange,
7982                                  _cumulativeScalarRange,
7983                                  _cumulativeVectorMagnitudeRange,
7984                                  _cumulativeVectorComponentRange);
7985    }
7986    for (LICHashmap::iterator itr = _lics.begin();
7987         itr != _lics.end(); ++itr) {
7988        itr->second->updateRanges(_useCumulativeRange,
7989                                  _cumulativeScalarRange,
7990                                  _cumulativeVectorMagnitudeRange,
7991                                  _cumulativeVectorComponentRange);
7992    }
7993    for (MoleculeHashmap::iterator itr = _molecules.begin();
7994         itr != _molecules.end(); ++itr) {
7995        itr->second->updateRanges(_useCumulativeRange,
7996                                  _cumulativeScalarRange,
7997                                  _cumulativeVectorMagnitudeRange,
7998                                  _cumulativeVectorComponentRange);
7999    }
8000    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
8001         itr != _pseudoColors.end(); ++itr) {
8002        itr->second->updateRanges(_useCumulativeRange,
8003                                  _cumulativeScalarRange,
8004                                  _cumulativeVectorMagnitudeRange,
8005                                  _cumulativeVectorComponentRange);
8006    }
8007    for (StreamlinesHashmap::iterator itr = _streamlines.begin();
8008         itr != _streamlines.end(); ++itr) {
8009        itr->second->updateRanges(_useCumulativeRange,
8010                                  _cumulativeScalarRange,
8011                                  _cumulativeVectorMagnitudeRange,
8012                                  _cumulativeVectorComponentRange);
8013    }
8014    for (VolumeHashmap::iterator itr = _volumes.begin();
8015         itr != _volumes.end(); ++itr) {
8016        itr->second->updateRanges(_useCumulativeRange,
8017                                  _cumulativeScalarRange,
8018                                  _cumulativeVectorMagnitudeRange,
8019                                  _cumulativeVectorComponentRange);
8020    }
8021}
8022
8023void Renderer::collectDataRanges()
8024{
8025    collectScalarRanges(_cumulativeScalarRange,
8026                        _cumulativeRangeOnlyVisible);
8027    collectVectorMagnitudeRanges(_cumulativeVectorMagnitudeRange,
8028                                 _cumulativeRangeOnlyVisible);
8029    for (int i = 0; i < 3; i++) {
8030        collectVectorComponentRanges(_cumulativeVectorComponentRange[i], i,
8031                                     _cumulativeRangeOnlyVisible);
8032    }
8033
8034    TRACE("Cumulative scalar range: %g, %g",
8035          _cumulativeScalarRange[0],
8036          _cumulativeScalarRange[1]);
8037    TRACE("Cumulative vmag range: %g, %g",
8038          _cumulativeVectorMagnitudeRange[0],
8039          _cumulativeVectorMagnitudeRange[1]);
8040    for (int i = 0; i < 3; i++) {
8041        TRACE("Cumulative v[%d] range: %g, %g", i,
8042              _cumulativeVectorComponentRange[i][0],
8043              _cumulativeVectorComponentRange[i][1]);
8044    }
8045}
8046
8047/**
8048 * \brief Collect cumulative data range of all DataSets
8049 *
8050 * \param[in,out] range Data range of all DataSets
8051 * \param[in] onlyVisible Only collect range of visible DataSets
8052 */
8053void Renderer::collectScalarRanges(double *range, bool onlyVisible)
8054{
8055    range[0] = DBL_MAX;
8056    range[1] = -DBL_MAX;
8057
8058    for (DataSetHashmap::iterator itr = _dataSets.begin();
8059         itr != _dataSets.end(); ++itr) {
8060        if (!onlyVisible || itr->second->getVisibility()) {
8061            double r[2];
8062            itr->second->getScalarRange(r);
8063            range[0] = min2(range[0], r[0]);
8064            range[1] = max2(range[1], r[1]);
8065        }
8066    }
8067    if (range[0] == DBL_MAX)
8068        range[0] = 0;
8069    if (range[1] == -DBL_MAX)
8070        range[1] = 1;
8071}
8072
8073/**
8074 * \brief Collect cumulative data range of all DataSets
8075 *
8076 * \param[in,out] range Data range of all DataSets
8077 * \param[in] onlyVisible Only collect range of visible DataSets
8078 */
8079void Renderer::collectVectorMagnitudeRanges(double *range, bool onlyVisible)
8080{
8081    range[0] = DBL_MAX;
8082    range[1] = -DBL_MAX;
8083
8084    for (DataSetHashmap::iterator itr = _dataSets.begin();
8085         itr != _dataSets.end(); ++itr) {
8086        if (!onlyVisible || itr->second->getVisibility()) {
8087            double r[2];
8088            itr->second->getVectorRange(r);
8089            range[0] = min2(range[0], r[0]);
8090            range[1] = max2(range[1], r[1]);
8091        }
8092    }
8093    if (range[0] == DBL_MAX)
8094        range[0] = 0;
8095    if (range[1] == -DBL_MAX)
8096        range[1] = 1;
8097}
8098
8099/**
8100 * \brief Collect cumulative data range of all DataSets
8101 *
8102 * \param[in,out] range Data range of all DataSets
8103 * \param[in] onlyVisible Only collect range of visible DataSets
8104 */
8105void Renderer::collectVectorComponentRanges(double *range, int component, bool onlyVisible)
8106{
8107    range[0] = DBL_MAX;
8108    range[1] = -DBL_MAX;
8109
8110    for (DataSetHashmap::iterator itr = _dataSets.begin();
8111         itr != _dataSets.end(); ++itr) {
8112        if (!onlyVisible || itr->second->getVisibility()) {
8113            double r[2];
8114            itr->second->getVectorRange(r, component);
8115            range[0] = min2(range[0], r[0]);
8116            range[1] = max2(range[1], r[1]);
8117        }
8118    }
8119    if (range[0] == DBL_MAX)
8120        range[0] = 0;
8121    if (range[1] == -DBL_MAX)
8122        range[1] = 1;
8123 }
8124
8125/**
8126 * \brief Determines if AABB lies in a principal axis plane
8127 * and if so, returns the plane normal
8128 */
8129bool Renderer::is2D(const double bounds[6],
8130                    Renderer::PrincipalPlane *plane,
8131                    double *offset) const
8132{
8133    if (bounds[4] == bounds[5]) {
8134        // Z = 0, XY plane
8135        if (plane)
8136            *plane = PLANE_XY;
8137        if (offset)
8138            *offset = bounds[4];
8139        return true;
8140    } else if (bounds[0] == bounds[1]) {
8141        // X = 0, ZY plane
8142        if (plane)
8143            *plane = PLANE_ZY;
8144        if (offset)
8145            *offset = bounds[0];
8146        return true;
8147    } else if (bounds[2] == bounds[3]) {
8148        // Y = 0, XZ plane
8149        if (plane)
8150            *plane = PLANE_XZ;
8151        if (offset)
8152            *offset = bounds[2];
8153        return true;
8154    }
8155    *plane = PLANE_XY;
8156    *offset = 0;
8157    return false;
8158}
8159
8160/**
8161 * \brief Initialize the camera zoom region to include the bounding volume given
8162 */
8163void Renderer::initCamera()
8164{
8165#ifdef WANT_TRACE
8166    switch (_cameraMode) {
8167    case IMAGE:
8168        TRACE("Image camera");
8169        break;
8170    case ORTHO:
8171        TRACE("Ortho camera");
8172        break;
8173    case PERSPECTIVE:
8174        TRACE("Perspective camera");
8175        break;
8176    default:
8177        TRACE("Unknown camera mode");
8178    }
8179#endif
8180    double bounds[6];
8181    collectBounds(bounds, false);
8182    bool twod = is2D(bounds, &_imgCameraPlane, &_imgCameraOffset);
8183    if (twod) {
8184        _cameraMode = IMAGE;
8185        if (_imgCameraPlane == PLANE_ZY) {
8186            _imgWorldOrigin[0] = bounds[4];
8187            _imgWorldOrigin[1] = bounds[2];
8188            _imgWorldDims[0] = bounds[5] - bounds[4];
8189            _imgWorldDims[1] = bounds[3] - bounds[2];
8190        } else if (_imgCameraPlane == PLANE_XZ) {
8191            _imgWorldOrigin[0] = bounds[0];
8192            _imgWorldOrigin[1] = bounds[4];
8193            _imgWorldDims[0] = bounds[1] - bounds[0];
8194            _imgWorldDims[1] = bounds[5] - bounds[4];
8195        } else {
8196            _imgWorldOrigin[0] = bounds[0];
8197            _imgWorldOrigin[1] = bounds[2];
8198            _imgWorldDims[0] = bounds[1] - bounds[0];
8199            _imgWorldDims[1] = bounds[3] - bounds[2];
8200        }
8201    } else {
8202        _imgWorldOrigin[0] = bounds[0];
8203        _imgWorldOrigin[1] = bounds[2];
8204        _imgWorldDims[0] = bounds[1] - bounds[0];
8205        _imgWorldDims[1] = bounds[3] - bounds[2];
8206    }
8207
8208    _cameraPan[0] = 0;
8209    _cameraPan[1] = 0;
8210    _cameraZoomRatio = 1;
8211
8212    switch (_cameraMode) {
8213    case IMAGE:
8214        _renderer->ResetCamera(bounds);
8215        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
8216                            _imgWorldDims[0], _imgWorldDims[1]);
8217        resetAxes();
8218        break;
8219    case ORTHO:
8220        _renderer->GetActiveCamera()->ParallelProjectionOn();
8221        resetAxes(bounds);
8222        _renderer->ResetCamera(bounds);
8223        //computeScreenWorldCoords();
8224        break;
8225    case PERSPECTIVE:
8226        _renderer->GetActiveCamera()->ParallelProjectionOff();
8227        resetAxes(bounds);
8228        _renderer->ResetCamera(bounds);
8229        //computeScreenWorldCoords();
8230        break;
8231    default:
8232        ERROR("Unknown camera mode");
8233    }
8234
8235#ifdef WANT_TRACE
8236    printCameraInfo(_renderer->GetActiveCamera());
8237#endif
8238}
8239
8240/**
8241 * \brief Print debugging info about a vtkCamera
8242 */
8243void Renderer::printCameraInfo(vtkCamera *camera)
8244{
8245    TRACE("pscale: %g, angle: %g, d: %g pos: %g %g %g, fpt: %g %g %g, vup: %g %g %g, clip: %g %g",
8246          camera->GetParallelScale(),
8247          camera->GetViewAngle(),
8248          camera->GetDistance(),
8249          camera->GetPosition()[0],
8250          camera->GetPosition()[1],
8251          camera->GetPosition()[2],
8252          camera->GetFocalPoint()[0],
8253          camera->GetFocalPoint()[1],
8254          camera->GetFocalPoint()[2],
8255          camera->GetViewUp()[0],
8256          camera->GetViewUp()[1],
8257          camera->GetViewUp()[2],
8258          camera->GetClippingRange()[0],
8259          camera->GetClippingRange()[1]);
8260}
8261
8262/**
8263 * \brief Set the RGB background color to render into the image
8264 */
8265void Renderer::setBackgroundColor(float color[3])
8266{
8267    _bgColor[0] = color[0];
8268    _bgColor[1] = color[1];
8269    _bgColor[2] = color[2];
8270    _renderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
8271    _needsRedraw = true;
8272}
8273
8274/**
8275 * \brief Set the opacity of the specified DataSet's associated graphics objects
8276 */
8277void Renderer::setDataSetOpacity(const DataSetId& id, double opacity)
8278{
8279    DataSetHashmap::iterator itr;
8280
8281    bool doAll = false;
8282
8283    if (id.compare("all") == 0) {
8284        itr = _dataSets.begin();
8285        doAll = true;
8286    } else {
8287        itr = _dataSets.find(id);
8288    }
8289    if (itr == _dataSets.end()) {
8290        ERROR("Unknown dataset %s", id.c_str());
8291        return;
8292    }
8293
8294    do {
8295        itr->second->setOpacity(opacity);
8296    } while (doAll && ++itr != _dataSets.end());
8297
8298    if (id.compare("all") == 0 || getContour2D(id) != NULL)
8299        setContour2DOpacity(id, opacity);
8300    if (id.compare("all") == 0 || getContour3D(id) != NULL)
8301        setContour3DOpacity(id, opacity);
8302    if (id.compare("all") == 0 || getGlyphs(id) != NULL)
8303        setGlyphsOpacity(id, opacity);
8304    if (id.compare("all") == 0 || getHeightMap(id) != NULL)
8305        setHeightMapOpacity(id, opacity);
8306    if (id.compare("all") == 0 || getLIC(id) != NULL)
8307        setLICOpacity(id, opacity);
8308    if (id.compare("all") == 0 || getMolecule(id) != NULL)
8309        setMoleculeOpacity(id, opacity);
8310    if (id.compare("all") == 0 || getPolyData(id) != NULL)
8311        setPolyDataOpacity(id, opacity);
8312    if (id.compare("all") == 0 || getPseudoColor(id) != NULL)
8313        setPseudoColorOpacity(id, opacity);
8314    if (id.compare("all") == 0 || getStreamlines(id) != NULL)
8315        setStreamlinesOpacity(id, opacity);
8316    if (id.compare("all") == 0 || getVolume(id) != NULL)
8317        setVolumeOpacity(id, opacity);
8318
8319    _needsRedraw = true;
8320}
8321
8322/**
8323 * \brief Turn on/off rendering of the specified DataSet's associated graphics objects
8324 */
8325void Renderer::setDataSetVisibility(const DataSetId& id, bool state)
8326{
8327    DataSetHashmap::iterator itr;
8328
8329    bool doAll = false;
8330
8331    if (id.compare("all") == 0) {
8332        itr = _dataSets.begin();
8333        doAll = true;
8334    } else {
8335        itr = _dataSets.find(id);
8336    }
8337    if (itr == _dataSets.end()) {
8338        ERROR("Unknown dataset %s", id.c_str());
8339        return;
8340    }
8341
8342    do {
8343        itr->second->setVisibility(state);
8344    } while (doAll && ++itr != _dataSets.end());
8345
8346    if (id.compare("all") == 0 || getContour2D(id) != NULL)
8347        setContour2DVisibility(id, state);
8348    if (id.compare("all") == 0 || getContour3D(id) != NULL)
8349        setContour3DVisibility(id, state);
8350    if (id.compare("all") == 0 || getGlyphs(id) != NULL)
8351        setGlyphsVisibility(id, state);
8352    if (id.compare("all") == 0 || getHeightMap(id) != NULL)
8353        setHeightMapVisibility(id, state);
8354    if (id.compare("all") == 0 || getLIC(id) != NULL)
8355        setLICVisibility(id, state);
8356    if (id.compare("all") == 0 || getMolecule(id) != NULL)
8357        setMoleculeVisibility(id, state);
8358    if (id.compare("all") == 0 || getPolyData(id) != NULL)
8359        setPolyDataVisibility(id, state);
8360    if (id.compare("all") == 0 || getPseudoColor(id) != NULL)
8361        setPseudoColorVisibility(id, state);
8362    if (id.compare("all") == 0 || getStreamlines(id) != NULL)
8363        setStreamlinesVisibility(id, state);
8364    if (id.compare("all") == 0 || getVolume(id) != NULL)
8365        setVolumeVisibility(id, state);
8366
8367    _needsRedraw = true;
8368}
8369
8370/**
8371 * \brief Toggle rendering of actors' bounding box
8372 */
8373void Renderer::setDataSetShowBounds(const DataSetId& id, bool state)
8374{
8375    DataSetHashmap::iterator itr;
8376
8377    bool doAll = false;
8378
8379    if (id.compare("all") == 0) {
8380        itr = _dataSets.begin();
8381        doAll = true;
8382    } else {
8383        itr = _dataSets.find(id);
8384    }
8385    if (itr == _dataSets.end()) {
8386        ERROR("Unknown dataset %s", id.c_str());
8387        return;
8388    }
8389
8390    do {
8391        if (!state && itr->second->getProp()) {
8392            _renderer->RemoveViewProp(itr->second->getProp());
8393        }
8394
8395        itr->second->showOutline(state);
8396
8397        if (state && !_renderer->HasViewProp(itr->second->getProp())) {
8398            _renderer->AddViewProp(itr->second->getProp());
8399        }
8400    } while (doAll && ++itr != _dataSets.end());
8401
8402    initCamera();
8403    _needsRedraw = true;
8404}
8405
8406/**
8407 * \brief Set color of outline bounding box
8408 */
8409void Renderer::setDataSetOutlineColor(const DataSetId& id, float color[3])
8410{
8411    DataSetHashmap::iterator itr;
8412
8413    bool doAll = false;
8414
8415    if (id.compare("all") == 0) {
8416        itr = _dataSets.begin();
8417        doAll = true;
8418    } else {
8419        itr = _dataSets.find(id);
8420    }
8421    if (itr == _dataSets.end()) {
8422        ERROR("Unknown dataset %s", id.c_str());
8423        return;
8424    }
8425
8426    do {
8427        itr->second->setOutlineColor(color);
8428    } while (doAll && ++itr != _dataSets.end());
8429
8430    _needsRedraw = true;
8431}
8432
8433/**
8434 * \brief Set a user clipping plane
8435 *
8436 * TODO: Fix clip plane positions after a change in actor bounds
8437 */
8438void Renderer::setClipPlane(Axis axis, double ratio, int direction)
8439{
8440    double bounds[6];
8441    collectBounds(bounds, false);
8442
8443    switch (axis) {
8444    case X_AXIS:
8445        if (direction > 0) {
8446            if (ratio > 0.0) {
8447                if (_userClipPlanes[0] == NULL) {
8448                    _userClipPlanes[0] = vtkSmartPointer<vtkPlane>::New();
8449                    _userClipPlanes[0]->SetNormal(1, 0, 0);
8450                }
8451                _userClipPlanes[0]->SetOrigin(bounds[0] + (bounds[1]-bounds[0])*ratio, 0, 0);
8452            } else {
8453                _userClipPlanes[0] = NULL;
8454            }
8455        } else {
8456            if (ratio > 0.0) {
8457                if (_userClipPlanes[1] == NULL) {
8458                    _userClipPlanes[1] = vtkSmartPointer<vtkPlane>::New();
8459                    _userClipPlanes[1]->SetNormal(-1, 0, 0);
8460                }
8461                _userClipPlanes[1]->SetOrigin(bounds[0] + (bounds[1]-bounds[0])*ratio, 0, 0);
8462            } else {
8463                _userClipPlanes[1] = NULL;
8464            }
8465        }
8466        break;
8467    case Y_AXIS:
8468        if (direction > 0) {
8469            if (ratio > 0.0) {
8470                if (_userClipPlanes[2] == NULL) {
8471                    _userClipPlanes[2] = vtkSmartPointer<vtkPlane>::New();
8472                    _userClipPlanes[2]->SetNormal(0, 1, 0);
8473                }
8474                _userClipPlanes[2]->SetOrigin(0, bounds[2] + (bounds[3]-bounds[2])*ratio, 0);
8475            } else {
8476                _userClipPlanes[2] = NULL;
8477            }
8478        } else {
8479            if (ratio > 0.0) {
8480                if (_userClipPlanes[3] == NULL) {
8481                    _userClipPlanes[3] = vtkSmartPointer<vtkPlane>::New();
8482                    _userClipPlanes[3]->SetNormal(0, -1, 0);
8483                }
8484                _userClipPlanes[3]->SetOrigin(0, bounds[2] + (bounds[3]-bounds[2])*ratio, 0);
8485            } else {
8486                _userClipPlanes[3] = NULL;
8487            }
8488        }
8489        break;
8490    case Z_AXIS:
8491        if (direction > 0) {
8492            if (ratio > 0.0) {
8493                if (_userClipPlanes[4] == NULL) {
8494                    _userClipPlanes[4] = vtkSmartPointer<vtkPlane>::New();
8495                    _userClipPlanes[4]->SetNormal(0, 0, 1);
8496                }
8497                _userClipPlanes[4]->SetOrigin(0, 0, bounds[4] + (bounds[5]-bounds[4])*ratio);
8498            } else {
8499                _userClipPlanes[4] = NULL;
8500            }
8501        } else {
8502            if (ratio > 0.0) {
8503                if (_userClipPlanes[5] == NULL) {
8504                    _userClipPlanes[5] = vtkSmartPointer<vtkPlane>::New();
8505                    _userClipPlanes[5]->SetNormal(0, 0, -1);
8506                }
8507                _userClipPlanes[5]->SetOrigin(0, 0, bounds[4] + (bounds[5]-bounds[4])*ratio);
8508            } else {
8509                _userClipPlanes[5] = NULL;
8510            }
8511        }
8512        break;
8513    default:
8514        ;
8515    }
8516
8517    _needsRedraw = true;
8518}
8519
8520/**
8521 * \brief Set up clipping planes for image camera mode if needed
8522 */
8523void Renderer::setCameraClippingPlanes()
8524{
8525    /* XXX: Note that there appears to be a bug with setting the
8526     * clipping plane collection to NULL in the VTK Mappers --
8527     * the old clip planes are still applied.  The workaround here
8528     * is to keep the PlaneCollection and add/remove the planes
8529     * to/from the PlaneCollection as needed.
8530     */
8531    if (_cameraMode == IMAGE) {
8532        if (_activeClipPlanes->GetNumberOfItems() == 0) {
8533            for (int i = 0; i < 4; i++)
8534                _activeClipPlanes->AddItem(_cameraClipPlanes[i]);
8535        }
8536    } else {
8537        if (_activeClipPlanes->GetNumberOfItems() > 0)
8538            _activeClipPlanes->RemoveAllItems();
8539        for (int i = 0; i < 6; i++) {
8540            if (_userClipPlanes[i] != NULL) {
8541                _activeClipPlanes->AddItem(_userClipPlanes[i]);
8542            }
8543        }
8544    }
8545
8546    /* Ensure all Mappers are using the PlaneCollection
8547     * This will not change the state or timestamp of
8548     * Mappers already using the PlaneCollection
8549     */
8550    for (Contour2DHashmap::iterator itr = _contour2Ds.begin();
8551         itr != _contour2Ds.end(); ++itr) {
8552        itr->second->setClippingPlanes(_activeClipPlanes);
8553    }
8554    for (Contour3DHashmap::iterator itr = _contour3Ds.begin();
8555         itr != _contour3Ds.end(); ++itr) {
8556        itr->second->setClippingPlanes(_activeClipPlanes);
8557    }
8558    for (GlyphsHashmap::iterator itr = _glyphs.begin();
8559         itr != _glyphs.end(); ++itr) {
8560        itr->second->setClippingPlanes(_activeClipPlanes);
8561    }
8562    for (HeightMapHashmap::iterator itr = _heightMaps.begin();
8563         itr != _heightMaps.end(); ++itr) {
8564        itr->second->setClippingPlanes(_activeClipPlanes);
8565    }
8566    for (LICHashmap::iterator itr = _lics.begin();
8567         itr != _lics.end(); ++itr) {
8568        itr->second->setClippingPlanes(_activeClipPlanes);
8569    }
8570    for (MoleculeHashmap::iterator itr = _molecules.begin();
8571         itr != _molecules.end(); ++itr) {
8572        itr->second->setClippingPlanes(_activeClipPlanes);
8573    }
8574    for (PolyDataHashmap::iterator itr = _polyDatas.begin();
8575         itr != _polyDatas.end(); ++itr) {
8576        itr->second->setClippingPlanes(_activeClipPlanes);
8577    }
8578    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
8579         itr != _pseudoColors.end(); ++itr) {
8580        itr->second->setClippingPlanes(_activeClipPlanes);
8581    }
8582    for (StreamlinesHashmap::iterator itr = _streamlines.begin();
8583         itr != _streamlines.end(); ++itr) {
8584        itr->second->setClippingPlanes(_activeClipPlanes);
8585    }
8586    for (VolumeHashmap::iterator itr = _volumes.begin();
8587         itr != _volumes.end(); ++itr) {
8588        itr->second->setClippingPlanes(_activeClipPlanes);
8589    }
8590}
8591
8592/**
8593 * \brief Control the use of two sided lighting
8594 */
8595void Renderer::setUseTwoSidedLighting(bool state)
8596{
8597    _renderer->SetTwoSidedLighting(state ? 1 : 0);
8598    _needsRedraw = true;
8599}
8600
8601/**
8602 * \brief Control the use of the depth peeling algorithm for transparency
8603 */
8604void Renderer::setUseDepthPeeling(bool state)
8605{
8606    _renderer->SetUseDepthPeeling(state ? 1 : 0);
8607    _needsRedraw = true;
8608}
8609
8610/**
8611 * \brief Sets flag to trigger rendering next time render() is called
8612 */
8613void Renderer::eventuallyRender()
8614{
8615    _needsRedraw = true;
8616}
8617
8618/**
8619 * \brief Cause the rendering to render a new image if needed
8620 *
8621 * The _needsRedraw flag indicates if a state change has occured since
8622 * the last rendered frame
8623 */
8624bool Renderer::render()
8625{
8626    if (_needsRedraw) {
8627        setCameraClippingPlanes();
8628        _renderWindow->Render();
8629        _needsRedraw = false;
8630        return true;
8631    } else
8632        return false;
8633}
8634
8635/// Get the pixel width of the render window/image
8636int Renderer::getWindowWidth() const
8637{
8638    return _windowWidth;
8639}
8640
8641/// Get the pixel height of the render window/image
8642int Renderer::getWindowHeight() const
8643{
8644    return _windowHeight;
8645}
8646
8647/**
8648 * \brief Read back the rendered framebuffer image
8649 */
8650void Renderer::getRenderedFrame(vtkUnsignedCharArray *imgData)
8651{
8652#ifdef RENDER_TARGA
8653    _renderWindow->MakeCurrent();
8654    // Must clear previous errors first.
8655    while (glGetError() != GL_NO_ERROR){
8656        ;
8657    }
8658    int bytesPerPixel = TARGA_BYTES_PER_PIXEL;
8659    int size = bytesPerPixel * _windowWidth * _windowHeight;
8660
8661    if (imgData->GetMaxId() + 1 != size)
8662    {
8663        imgData->SetNumberOfComponents(bytesPerPixel);
8664        imgData->SetNumberOfValues(size);
8665    }
8666    glDisable(GL_TEXTURE_2D);
8667    if (_renderWindow->GetDoubleBuffer()) {
8668        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_renderWindow)->GetBackLeftBuffer()));
8669    } else {
8670        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_renderWindow)->GetFrontLeftBuffer()));
8671    }
8672    glPixelStorei(GL_PACK_ALIGNMENT, 1);
8673#ifdef WANT_TRACE
8674    struct timeval t1, t2;
8675    glFinish();
8676    gettimeofday(&t1, 0);
8677#endif
8678    if (bytesPerPixel == 4) {
8679        glReadPixels(0, 0, _windowWidth, _windowHeight, GL_BGRA,
8680                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
8681    } else {
8682        glReadPixels(0, 0, _windowWidth, _windowHeight, GL_BGR,
8683                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
8684    }
8685#ifdef WANT_TRACE
8686    gettimeofday(&t2, 0);
8687    static unsigned int numFrames = 0;
8688    static double accum = 0;
8689    numFrames++;
8690    accum += ELAPSED_TIME(t1, t2);
8691#endif
8692    TRACE("Readback time: %g ms", ELAPSED_TIME(t1, t2));
8693    TRACE("Readback avg: %g ms", accum/numFrames);
8694    if (glGetError() != GL_NO_ERROR) {
8695        ERROR("glReadPixels");
8696    }
8697#else
8698    _renderWindow->GetPixelData(0, 0, _windowWidth-1, _windowHeight-1,
8699                                !_renderWindow->GetDoubleBuffer(), imgData);
8700#endif
8701    TRACE("Image data size: %d", imgData->GetSize());
8702}
8703
8704/**
8705 * \brief Get nearest data value given display coordinates x,y
8706 *
8707 * Note: no interpolation is performed on data
8708 */
8709bool Renderer::getScalarValueAtPixel(const DataSetId& id, int x, int y, double *value)
8710{
8711    vtkSmartPointer<vtkCoordinate> coord = vtkSmartPointer<vtkCoordinate>::New();
8712    coord->SetCoordinateSystemToDisplay();
8713    coord->SetValue(x, _windowHeight - y, 0);
8714    double *worldCoords = coord->GetComputedWorldValue(_renderer);
8715
8716    TRACE("Pixel coords: %d, %d\nWorld coords: %g, %g, %g", x, y,
8717          worldCoords[0],
8718          worldCoords[1],
8719          worldCoords[2]);
8720
8721    return getScalarValue(id, worldCoords[0], worldCoords[1], worldCoords[2], value);
8722}
8723
8724/**
8725 * \brief Get nearest data value given world coordinates x,y,z
8726 *
8727 * Note: no interpolation is performed on data
8728 */
8729bool Renderer::getScalarValue(const DataSetId& id, double x, double y, double z, double *value)
8730{
8731    DataSet *ds = getDataSet(id);
8732    if (ds == NULL)
8733        return false;
8734
8735    return ds->getScalarValue(x, y, z, value);
8736}
8737
8738/**
8739 * \brief Get nearest data value given display coordinates x,y
8740 *
8741 * Note: no interpolation is performed on data
8742 */
8743bool Renderer::getVectorValueAtPixel(const DataSetId& id, int x, int y, double vector[3])
8744{
8745    vtkSmartPointer<vtkCoordinate> coord = vtkSmartPointer<vtkCoordinate>::New();
8746    coord->SetCoordinateSystemToDisplay();
8747    coord->SetValue(x, _windowHeight - y, 0);
8748    double *worldCoords = coord->GetComputedWorldValue(_renderer);
8749
8750    TRACE("Pixel coords: %d, %d\nWorld coords: %g, %g, %g", x, y,
8751          worldCoords[0],
8752          worldCoords[1],
8753          worldCoords[2]);
8754
8755    return getVectorValue(id, worldCoords[0], worldCoords[1], worldCoords[2], vector);
8756}
8757
8758/**
8759 * \brief Get nearest data value given world coordinates x,y,z
8760 *
8761 * Note: no interpolation is performed on data
8762 */
8763bool Renderer::getVectorValue(const DataSetId& id, double x, double y, double z, double vector[3])
8764{
8765    DataSet *ds = getDataSet(id);
8766    if (ds == NULL)
8767        return false;
8768
8769    return ds->getVectorValue(x, y, z, vector);
8770}
Note: See TracBrowser for help on using the repository browser.