source: branches/blt4/packages/vizservers/vtkvis/RpVtkRenderer.cpp @ 2542

Last change on this file since 2542 was 2542, checked in by gah, 12 years ago

update from trunk

File size: 225.4 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 Controls if field data range is normalized to [0,1] before
3004 * applying scale factor for the given DataSet
3005 */
3006void Renderer::setGlyphsNormalizeScale(const DataSetId& id, bool normalize)
3007{
3008    GlyphsHashmap::iterator itr;
3009
3010    bool doAll = false;
3011
3012    if (id.compare("all") == 0) {
3013        itr = _glyphs.begin();
3014        doAll = true;
3015    } else {
3016        itr = _glyphs.find(id);
3017    }
3018    if (itr == _glyphs.end()) {
3019        ERROR("Glyphs not found: %s", id.c_str());
3020        return;
3021    }
3022
3023    do {
3024        itr->second->setNormalizeScale(normalize);
3025    } while (doAll && ++itr != _glyphs.end());
3026
3027    _renderer->ResetCameraClippingRange();
3028    _needsRedraw = true;
3029}
3030
3031/**
3032 * \brief Set the shape of Glyphs for the given DataSet
3033 */
3034void Renderer::setGlyphsShape(const DataSetId& id, Glyphs::GlyphShape shape)
3035{
3036    GlyphsHashmap::iterator itr;
3037
3038    bool doAll = false;
3039
3040    if (id.compare("all") == 0) {
3041        itr = _glyphs.begin();
3042        doAll = true;
3043    } else {
3044        itr = _glyphs.find(id);
3045    }
3046    if (itr == _glyphs.end()) {
3047        ERROR("Glyphs not found: %s", id.c_str());
3048        return;
3049    }
3050
3051    do {
3052        itr->second->setGlyphShape(shape);
3053    } while (doAll && ++itr != _glyphs.end());
3054
3055    _renderer->ResetCameraClippingRange();
3056    _needsRedraw = true;
3057}
3058
3059/**
3060 * \brief Set the glyph scaling factor for the given DataSet
3061 */
3062void Renderer::setGlyphsScaleFactor(const DataSetId& id, double scale)
3063{
3064    GlyphsHashmap::iterator itr;
3065
3066    bool doAll = false;
3067
3068    if (id.compare("all") == 0) {
3069        itr = _glyphs.begin();
3070        doAll = true;
3071    } else {
3072        itr = _glyphs.find(id);
3073    }
3074    if (itr == _glyphs.end()) {
3075        ERROR("Glyphs not found: %s", id.c_str());
3076        return;
3077    }
3078
3079    do {
3080        itr->second->setScaleFactor(scale);
3081    } while (doAll && ++itr != _glyphs.end());
3082
3083    initCamera();
3084    _needsRedraw = true;
3085}
3086
3087/**
3088 * \brief Set the visibility of polygon edges for the specified DataSet
3089 */
3090void Renderer::setGlyphsEdgeVisibility(const DataSetId& id, bool state)
3091{
3092    GlyphsHashmap::iterator itr;
3093
3094    bool doAll = false;
3095
3096    if (id.compare("all") == 0) {
3097        itr = _glyphs.begin();
3098        doAll = true;
3099    } else {
3100        itr = _glyphs.find(id);
3101    }
3102    if (itr == _glyphs.end()) {
3103        ERROR("Glyphs not found: %s", id.c_str());
3104        return;
3105    }
3106
3107    do {
3108        itr->second->setEdgeVisibility(state);
3109    } while (doAll && ++itr != _glyphs.end());
3110
3111    _needsRedraw = true;
3112}
3113
3114/**
3115 * \brief Set the RGB polygon edge color for the specified DataSet
3116 */
3117void Renderer::setGlyphsEdgeColor(const DataSetId& id, float color[3])
3118{
3119    GlyphsHashmap::iterator itr;
3120
3121    bool doAll = false;
3122
3123    if (id.compare("all") == 0) {
3124        itr = _glyphs.begin();
3125        doAll = true;
3126    } else {
3127        itr = _glyphs.find(id);
3128    }
3129    if (itr == _glyphs.end()) {
3130        ERROR("Glyphs not found: %s", id.c_str());
3131        return;
3132    }
3133
3134    do {
3135        itr->second->setEdgeColor(color);
3136    } while (doAll && ++itr != _glyphs.end());
3137
3138    _needsRedraw = true;
3139}
3140
3141/**
3142 * \brief Set the polygon edge width for the specified DataSet (may be a no-op)
3143 *
3144 * If the OpenGL implementation/hardware does not support wide lines,
3145 * this function may not have an effect.
3146 */
3147void Renderer::setGlyphsEdgeWidth(const DataSetId& id, float edgeWidth)
3148{
3149    GlyphsHashmap::iterator itr;
3150
3151    bool doAll = false;
3152
3153    if (id.compare("all") == 0) {
3154        itr = _glyphs.begin();
3155        doAll = true;
3156    } else {
3157        itr = _glyphs.find(id);
3158    }
3159    if (itr == _glyphs.end()) {
3160        ERROR("Glyphs not found: %s", id.c_str());
3161        return;
3162    }
3163
3164    do {
3165        itr->second->setEdgeWidth(edgeWidth);
3166    } while (doAll && ++itr != _glyphs.end());
3167
3168    _needsRedraw = true;
3169}
3170
3171/**
3172 * \brief Turn Glyphs lighting on/off for the specified DataSet
3173 */
3174void Renderer::setGlyphsLighting(const DataSetId& id, bool state)
3175{
3176    GlyphsHashmap::iterator itr;
3177
3178    bool doAll = false;
3179
3180    if (id.compare("all") == 0) {
3181        itr = _glyphs.begin();
3182        doAll = true;
3183    } else {
3184        itr = _glyphs.find(id);
3185    }
3186    if (itr == _glyphs.end()) {
3187        ERROR("Glyphs not found: %s", id.c_str());
3188        return;
3189    }
3190
3191    do {
3192        itr->second->setLighting(state);
3193    } while (doAll && ++itr != _glyphs.end());
3194    _needsRedraw = true;
3195}
3196
3197/**
3198 * \brief Set opacity of Glyphs for the given DataSet
3199 */
3200void Renderer::setGlyphsOpacity(const DataSetId& id, double opacity)
3201{
3202    GlyphsHashmap::iterator itr;
3203
3204    bool doAll = false;
3205
3206    if (id.compare("all") == 0) {
3207        itr = _glyphs.begin();
3208        doAll = true;
3209    } else {
3210        itr = _glyphs.find(id);
3211    }
3212    if (itr == _glyphs.end()) {
3213        ERROR("Glyphs not found: %s", id.c_str());
3214        return;
3215    }
3216
3217    do {
3218        itr->second->setOpacity(opacity);
3219    } while (doAll && ++itr != _glyphs.end());
3220
3221    _needsRedraw = true;
3222}
3223
3224/**
3225 * \brief Turn on/off rendering Glyphs for the given DataSet
3226 */
3227void Renderer::setGlyphsVisibility(const DataSetId& id, bool state)
3228{
3229    GlyphsHashmap::iterator itr;
3230
3231    bool doAll = false;
3232
3233    if (id.compare("all") == 0) {
3234        itr = _glyphs.begin();
3235        doAll = true;
3236    } else {
3237        itr = _glyphs.find(id);
3238    }
3239    if (itr == _glyphs.end()) {
3240        ERROR("Glyphs not found: %s", id.c_str());
3241        return;
3242    }
3243
3244    do {
3245        itr->second->setVisibility(state);
3246    } while (doAll && ++itr != _glyphs.end());
3247
3248    _needsRedraw = true;
3249}
3250
3251/**
3252 * \brief Turn on/off wireframe rendering of Glyphs for the given DataSet
3253 */
3254void Renderer::setGlyphsWireframe(const DataSetId& id, bool state)
3255{
3256    GlyphsHashmap::iterator itr;
3257
3258    bool doAll = false;
3259
3260    if (id.compare("all") == 0) {
3261        itr = _glyphs.begin();
3262        doAll = true;
3263    } else {
3264        itr = _glyphs.find(id);
3265    }
3266    if (itr == _glyphs.end()) {
3267        ERROR("Glyphs not found: %s", id.c_str());
3268        return;
3269    }
3270
3271    do {
3272        itr->second->setWireframe(state);
3273    } while (doAll && ++itr != _glyphs.end());
3274
3275    _needsRedraw = true;
3276}
3277
3278/**
3279 * \brief Create a new HeightMap and associate it with the named DataSet
3280 */
3281bool Renderer::addHeightMap(const DataSetId& id, int numContours, double heightScale)
3282{
3283    DataSetHashmap::iterator itr;
3284
3285    bool doAll = false;
3286
3287    if (id.compare("all") == 0) {
3288        itr = _dataSets.begin();
3289    } else {
3290        itr = _dataSets.find(id);
3291    }
3292    if (itr == _dataSets.end()) {
3293        ERROR("Unknown dataset %s", id.c_str());
3294        return false;
3295    }
3296
3297    do {
3298        DataSet *ds = itr->second;
3299        const DataSetId& dsID = ds->getName();
3300
3301        if (getHeightMap(dsID)) {
3302            WARN("Replacing existing HeightMap %s", dsID.c_str());
3303            deleteHeightMap(dsID);
3304        }
3305
3306        HeightMap *hmap = new HeightMap(numContours, heightScale);
3307
3308        hmap->setDataSet(ds,
3309                         _useCumulativeRange,
3310                         _cumulativeScalarRange,
3311                         _cumulativeVectorMagnitudeRange,
3312                         _cumulativeVectorComponentRange);
3313
3314        if (hmap->getProp() == NULL) {
3315            delete hmap;
3316            return false;
3317        } else {
3318            _renderer->AddViewProp(hmap->getProp());
3319        }
3320
3321        _heightMaps[dsID] = hmap;
3322    } while (doAll && ++itr != _dataSets.end());
3323
3324    if (_cameraMode == IMAGE)
3325        setCameraMode(PERSPECTIVE);
3326    initCamera();
3327
3328    _needsRedraw = true;
3329    return true;
3330}
3331
3332/**
3333 * \brief Create a new HeightMap and associate it with the named DataSet
3334 */
3335bool Renderer::addHeightMap(const DataSetId& id, const std::vector<double>& contours, double heightScale)
3336{
3337    DataSetHashmap::iterator itr;
3338
3339    bool doAll = false;
3340
3341    if (id.compare("all") == 0) {
3342        itr = _dataSets.begin();
3343    } else {
3344        itr = _dataSets.find(id);
3345    }
3346    if (itr == _dataSets.end()) {
3347        ERROR("Unknown dataset %s", id.c_str());
3348        return false;
3349    }
3350
3351    do {
3352        DataSet *ds = itr->second;
3353        const DataSetId& dsID = ds->getName();
3354
3355        if (getHeightMap(dsID)) {
3356            WARN("Replacing existing HeightMap %s", dsID.c_str());
3357            deleteHeightMap(dsID);
3358        }
3359
3360        HeightMap *hmap = new HeightMap(contours, heightScale);
3361
3362        hmap->setDataSet(ds,
3363                         _useCumulativeRange,
3364                         _cumulativeScalarRange,
3365                         _cumulativeVectorMagnitudeRange,
3366                         _cumulativeVectorComponentRange);
3367
3368        if (hmap->getProp() == NULL) {
3369            delete hmap;
3370            return false;
3371        } else {
3372            _renderer->AddViewProp(hmap->getProp());
3373        }
3374
3375        _heightMaps[dsID] = hmap;
3376    } while (doAll && ++itr != _dataSets.end());
3377
3378    if (_cameraMode == IMAGE)
3379        setCameraMode(PERSPECTIVE);
3380    initCamera();
3381
3382    _needsRedraw = true;
3383    return true;
3384}
3385
3386/**
3387 * \brief Get the HeightMap associated with a named DataSet
3388 */
3389HeightMap *Renderer::getHeightMap(const DataSetId& id)
3390{
3391    HeightMapHashmap::iterator itr = _heightMaps.find(id);
3392
3393    if (itr == _heightMaps.end()) {
3394#ifdef DEBUG
3395        TRACE("HeightMap not found: %s", id.c_str());
3396#endif
3397        return NULL;
3398    } else
3399        return itr->second;
3400}
3401
3402/**
3403 * \brief Set an additional transform on the prop
3404 */
3405void Renderer::setHeightMapTransform(const DataSetId& id, vtkMatrix4x4 *trans)
3406{
3407    HeightMapHashmap::iterator itr;
3408
3409    bool doAll = false;
3410
3411    if (id.compare("all") == 0) {
3412        itr = _heightMaps.begin();
3413        doAll = true;
3414    } else {
3415        itr = _heightMaps.find(id);
3416    }
3417    if (itr == _heightMaps.end()) {
3418        ERROR("HeightMap not found: %s", id.c_str());
3419        return;
3420    }
3421
3422    do {
3423        itr->second->setTransform(trans);
3424    } while (doAll && ++itr != _heightMaps.end());
3425
3426    resetAxes();
3427    _needsRedraw = true;
3428}
3429
3430/**
3431 * \brief Set the prop orientation with a quaternion
3432 */
3433void Renderer::setHeightMapOrientation(const DataSetId& id, double quat[4])
3434{
3435    HeightMapHashmap::iterator itr;
3436
3437    bool doAll = false;
3438
3439    if (id.compare("all") == 0) {
3440        itr = _heightMaps.begin();
3441        doAll = true;
3442    } else {
3443        itr = _heightMaps.find(id);
3444    }
3445    if (itr == _heightMaps.end()) {
3446        ERROR("HeightMap not found: %s", id.c_str());
3447        return;
3448    }
3449
3450    do {
3451        itr->second->setOrientation(quat);
3452    } while (doAll && ++itr != _heightMaps.end());
3453
3454    resetAxes();
3455    _needsRedraw = true;
3456}
3457
3458/**
3459 * \brief Set the prop orientation with a rotation about an axis
3460 */
3461void Renderer::setHeightMapOrientation(const DataSetId& id, double angle, double axis[3])
3462{
3463    HeightMapHashmap::iterator itr;
3464
3465    bool doAll = false;
3466
3467    if (id.compare("all") == 0) {
3468        itr = _heightMaps.begin();
3469        doAll = true;
3470    } else {
3471        itr = _heightMaps.find(id);
3472    }
3473    if (itr == _heightMaps.end()) {
3474        ERROR("HeightMap not found: %s", id.c_str());
3475        return;
3476    }
3477
3478    do {
3479        itr->second->setOrientation(angle, axis);
3480    } while (doAll && ++itr != _heightMaps.end());
3481
3482    resetAxes();
3483    _needsRedraw = true;
3484}
3485
3486/**
3487 * \brief Set the prop position in world coords
3488 */
3489void Renderer::setHeightMapPosition(const DataSetId& id, double pos[3])
3490{
3491    HeightMapHashmap::iterator itr;
3492
3493    bool doAll = false;
3494
3495    if (id.compare("all") == 0) {
3496        itr = _heightMaps.begin();
3497        doAll = true;
3498    } else {
3499        itr = _heightMaps.find(id);
3500    }
3501    if (itr == _heightMaps.end()) {
3502        ERROR("HeightMap not found: %s", id.c_str());
3503        return;
3504    }
3505
3506    do {
3507        itr->second->setPosition(pos);
3508    } while (doAll && ++itr != _heightMaps.end());
3509
3510    resetAxes();
3511    _needsRedraw = true;
3512}
3513
3514/**
3515 * \brief Set the prop scaling
3516 */
3517void Renderer::setHeightMapScale(const DataSetId& id, double scale[3])
3518{
3519    HeightMapHashmap::iterator itr;
3520
3521    bool doAll = false;
3522
3523    if (id.compare("all") == 0) {
3524        itr = _heightMaps.begin();
3525        doAll = true;
3526    } else {
3527        itr = _heightMaps.find(id);
3528    }
3529    if (itr == _heightMaps.end()) {
3530        ERROR("HeightMap not found: %s", id.c_str());
3531        return;
3532    }
3533
3534    do {
3535        itr->second->setScale(scale);
3536    } while (doAll && ++itr != _heightMaps.end());
3537
3538    resetAxes();
3539    _needsRedraw = true;
3540}
3541
3542/**
3543 * \brief Set the volume slice used for mapping volumetric data
3544 */
3545void Renderer::setHeightMapVolumeSlice(const DataSetId& id, HeightMap::Axis axis, double ratio)
3546{
3547    HeightMapHashmap::iterator itr;
3548
3549    bool doAll = false;
3550
3551    if (id.compare("all") == 0) {
3552        itr = _heightMaps.begin();
3553        doAll = true;
3554    } else {
3555        itr = _heightMaps.find(id);
3556    }
3557
3558    if (itr == _heightMaps.end()) {
3559        ERROR("HeightMap not found: %s", id.c_str());
3560        return;
3561    }
3562
3563    do {
3564        itr->second->selectVolumeSlice(axis, ratio);
3565     } while (doAll && ++itr != _heightMaps.end());
3566
3567    initCamera();
3568    _needsRedraw = true;
3569}
3570
3571/**
3572 * \brief Set amount to scale scalar values when creating elevations
3573 * in the height map
3574 */
3575void Renderer::setHeightMapHeightScale(const DataSetId& id, double scale)
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    do {
3594        itr->second->setHeightScale(scale);
3595     } while (doAll && ++itr != _heightMaps.end());
3596
3597    initCamera();
3598    _needsRedraw = true;
3599}
3600
3601/**
3602 * \brief Associate an existing named color map with a HeightMap for the given DataSet
3603 */
3604void Renderer::setHeightMapColorMap(const DataSetId& id, const ColorMapId& colorMapId)
3605{
3606    HeightMapHashmap::iterator itr;
3607
3608    bool doAll = false;
3609
3610    if (id.compare("all") == 0) {
3611        itr = _heightMaps.begin();
3612        doAll = true;
3613    } else {
3614        itr = _heightMaps.find(id);
3615    }
3616
3617    if (itr == _heightMaps.end()) {
3618        ERROR("HeightMap not found: %s", id.c_str());
3619        return;
3620    }
3621
3622    ColorMap *cmap = getColorMap(colorMapId);
3623    if (cmap == NULL) {
3624        ERROR("Unknown colormap: %s", colorMapId.c_str());
3625        return;
3626    }
3627
3628    do {
3629        TRACE("Set HeightMap color map: %s for dataset %s", colorMapId.c_str(),
3630              itr->second->getDataSet()->getName().c_str());
3631
3632        itr->second->setColorMap(cmap);
3633    } while (doAll && ++itr != _heightMaps.end());
3634
3635    _needsRedraw = true;
3636}
3637
3638/**
3639 * \brief Set the number of equally spaced contour isolines for the given DataSet
3640 */
3641void Renderer::setHeightMapNumContours(const DataSetId& id, int numContours)
3642{
3643    HeightMapHashmap::iterator itr;
3644
3645    bool doAll = false;
3646
3647    if (id.compare("all") == 0) {
3648        itr = _heightMaps.begin();
3649        doAll = true;
3650    } else {
3651        itr = _heightMaps.find(id);
3652    }
3653    if (itr == _heightMaps.end()) {
3654        ERROR("HeightMap not found: %s", id.c_str());
3655        return;
3656    }
3657
3658    do {
3659        itr->second->setNumContours(numContours);
3660    } while (doAll && ++itr != _heightMaps.end());
3661
3662    _needsRedraw = true;
3663}
3664
3665/**
3666 * \brief Set a list of height map contour isovalues for the given DataSet
3667 */
3668void Renderer::setHeightMapContourList(const DataSetId& id, const std::vector<double>& contours)
3669{
3670    HeightMapHashmap::iterator itr;
3671
3672    bool doAll = false;
3673
3674    if (id.compare("all") == 0) {
3675        itr = _heightMaps.begin();
3676        doAll = true;
3677    } else {
3678        itr = _heightMaps.find(id);
3679    }
3680    if (itr == _heightMaps.end()) {
3681        ERROR("HeightMap not found: %s", id.c_str());
3682        return;
3683    }
3684
3685    do {
3686        itr->second->setContourList(contours);
3687    } while (doAll && ++itr != _heightMaps.end());
3688
3689     _needsRedraw = true;
3690}
3691
3692/**
3693 * \brief Set opacity of height map for the given DataSet
3694 */
3695void Renderer::setHeightMapOpacity(const DataSetId& id, double opacity)
3696{
3697    HeightMapHashmap::iterator itr;
3698
3699    bool doAll = false;
3700
3701    if (id.compare("all") == 0) {
3702        itr = _heightMaps.begin();
3703        doAll = true;
3704    } else {
3705        itr = _heightMaps.find(id);
3706    }
3707    if (itr == _heightMaps.end()) {
3708        ERROR("HeightMap not found: %s", id.c_str());
3709        return;
3710    }
3711
3712    do {
3713        itr->second->setOpacity(opacity);
3714    } while (doAll && ++itr != _heightMaps.end());
3715
3716    _needsRedraw = true;
3717}
3718
3719/**
3720 * \brief Turn on/off rendering height map for the given DataSet
3721 */
3722void Renderer::setHeightMapVisibility(const DataSetId& id, bool state)
3723{
3724    HeightMapHashmap::iterator itr;
3725
3726    bool doAll = false;
3727
3728    if (id.compare("all") == 0) {
3729        itr = _heightMaps.begin();
3730        doAll = true;
3731    } else {
3732        itr = _heightMaps.find(id);
3733    }
3734    if (itr == _heightMaps.end()) {
3735        ERROR("HeightMap not found: %s", id.c_str());
3736        return;
3737    }
3738
3739    do {
3740        itr->second->setVisibility(state);
3741    } while (doAll && ++itr != _heightMaps.end());
3742
3743    _needsRedraw = true;
3744}
3745
3746/**
3747 * \brief Set wireframe rendering for the specified DataSet
3748 */
3749void Renderer::setHeightMapWireframe(const DataSetId& id, bool state)
3750{
3751    HeightMapHashmap::iterator itr;
3752
3753    bool doAll = false;
3754
3755    if (id.compare("all") == 0) {
3756        itr = _heightMaps.begin();
3757        doAll = true;
3758    } else {
3759        itr = _heightMaps.find(id);
3760    }
3761    if (itr == _heightMaps.end()) {
3762        ERROR("HeightMap not found: %s", id.c_str());
3763        return;
3764    }
3765
3766    do {
3767        itr->second->setWireframe(state);
3768    } while (doAll && ++itr != _heightMaps.end());
3769
3770    _needsRedraw = true;
3771}
3772
3773/**
3774 * \brief Turn on/off rendering height map mesh edges for the given DataSet
3775 */
3776void Renderer::setHeightMapEdgeVisibility(const DataSetId& id, bool state)
3777{
3778    HeightMapHashmap::iterator itr;
3779
3780    bool doAll = false;
3781
3782    if (id.compare("all") == 0) {
3783        itr = _heightMaps.begin();
3784        doAll = true;
3785    } else {
3786        itr = _heightMaps.find(id);
3787    }
3788    if (itr == _heightMaps.end()) {
3789        ERROR("HeightMap not found: %s", id.c_str());
3790        return;
3791    }
3792
3793    do {
3794        itr->second->setEdgeVisibility(state);
3795    } while (doAll && ++itr != _heightMaps.end());
3796
3797    _needsRedraw = true;
3798}
3799
3800/**
3801 * \brief Set the RGB height map mesh edge color for the specified DataSet
3802 */
3803void Renderer::setHeightMapEdgeColor(const DataSetId& id, float color[3])
3804{
3805    HeightMapHashmap::iterator itr;
3806
3807    bool doAll = false;
3808
3809    if (id.compare("all") == 0) {
3810        itr = _heightMaps.begin();
3811        doAll = true;
3812    } else {
3813        itr = _heightMaps.find(id);
3814    }
3815    if (itr == _heightMaps.end()) {
3816        ERROR("HeightMap not found: %s", id.c_str());
3817        return;
3818    }
3819
3820    do {
3821        itr->second->setEdgeColor(color);
3822    } while (doAll && ++itr != _heightMaps.end());
3823
3824    _needsRedraw = true;
3825}
3826
3827/**
3828 * \brief Set the height map mesh edge width for the specified DataSet (may be a no-op)
3829 *
3830 * If the OpenGL implementation/hardware does not support wide lines,
3831 * this function may not have an effect.
3832 */
3833void Renderer::setHeightMapEdgeWidth(const DataSetId& id, float edgeWidth)
3834{
3835    HeightMapHashmap::iterator itr;
3836
3837    bool doAll = false;
3838
3839    if (id.compare("all") == 0) {
3840        itr = _heightMaps.begin();
3841        doAll = true;
3842    } else {
3843        itr = _heightMaps.find(id);
3844    }
3845    if (itr == _heightMaps.end()) {
3846        ERROR("HeightMap not found: %s", id.c_str());
3847        return;
3848    }
3849
3850    do {
3851        itr->second->setEdgeWidth(edgeWidth);
3852    } while (doAll && ++itr != _heightMaps.end());
3853
3854    _needsRedraw = true;
3855}
3856
3857/**
3858 * \brief Turn on/off rendering height map contour lines for the given DataSet
3859 */
3860void Renderer::setHeightMapContourLineVisibility(const DataSetId& id, bool state)
3861{
3862    HeightMapHashmap::iterator itr;
3863
3864    bool doAll = false;
3865
3866    if (id.compare("all") == 0) {
3867        itr = _heightMaps.begin();
3868        doAll = true;
3869    } else {
3870        itr = _heightMaps.find(id);
3871    }
3872    if (itr == _heightMaps.end()) {
3873        ERROR("HeightMap not found: %s", id.c_str());
3874        return;
3875    }
3876
3877    do {
3878        itr->second->setContourLineVisibility(state);
3879    } while (doAll && ++itr != _heightMaps.end());
3880
3881    _needsRedraw = true;
3882}
3883
3884/**
3885 * \brief Turn on/off rendering height map colormap surface for the given DataSet
3886 */
3887void Renderer::setHeightMapContourSurfaceVisibility(const DataSetId& id, bool state)
3888{
3889    HeightMapHashmap::iterator itr;
3890
3891    bool doAll = false;
3892
3893    if (id.compare("all") == 0) {
3894        itr = _heightMaps.begin();
3895        doAll = true;
3896    } else {
3897        itr = _heightMaps.find(id);
3898    }
3899    if (itr == _heightMaps.end()) {
3900        ERROR("HeightMap not found: %s", id.c_str());
3901        return;
3902    }
3903
3904    do {
3905        itr->second->setContourSurfaceVisibility(state);
3906    } while (doAll && ++itr != _heightMaps.end());
3907
3908    _needsRedraw = true;
3909}
3910
3911/**
3912 * \brief Set the RGB height map isoline color for the specified DataSet
3913 */
3914void Renderer::setHeightMapContourEdgeColor(const DataSetId& id, float color[3])
3915{
3916    HeightMapHashmap::iterator itr;
3917
3918    bool doAll = false;
3919
3920    if (id.compare("all") == 0) {
3921        itr = _heightMaps.begin();
3922        doAll = true;
3923    } else {
3924        itr = _heightMaps.find(id);
3925    }
3926    if (itr == _heightMaps.end()) {
3927        ERROR("HeightMap not found: %s", id.c_str());
3928        return;
3929    }
3930
3931    do {
3932        itr->second->setContourEdgeColor(color);
3933    } while (doAll && ++itr != _heightMaps.end());
3934
3935    _needsRedraw = true;
3936}
3937
3938/**
3939 * \brief Set the height map isoline width for the specified DataSet (may be a no-op)
3940 *
3941 * If the OpenGL implementation/hardware does not support wide lines,
3942 * this function may not have an effect.
3943 */
3944void Renderer::setHeightMapContourEdgeWidth(const DataSetId& id, float edgeWidth)
3945{
3946    HeightMapHashmap::iterator itr;
3947
3948    bool doAll = false;
3949
3950    if (id.compare("all") == 0) {
3951        itr = _heightMaps.begin();
3952        doAll = true;
3953    } else {
3954        itr = _heightMaps.find(id);
3955    }
3956    if (itr == _heightMaps.end()) {
3957        ERROR("HeightMap not found: %s", id.c_str());
3958        return;
3959    }
3960
3961    do {
3962        itr->second->setContourEdgeWidth(edgeWidth);
3963    } while (doAll && ++itr != _heightMaps.end());
3964
3965    _needsRedraw = true;
3966}
3967
3968/**
3969 * \brief Turn height map lighting on/off for the specified DataSet
3970 */
3971void Renderer::setHeightMapLighting(const DataSetId& id, bool state)
3972{
3973    HeightMapHashmap::iterator itr;
3974
3975    bool doAll = false;
3976
3977    if (id.compare("all") == 0) {
3978        itr = _heightMaps.begin();
3979        doAll = true;
3980    } else {
3981        itr = _heightMaps.find(id);
3982    }
3983    if (itr == _heightMaps.end()) {
3984        ERROR("HeightMap not found: %s", id.c_str());
3985        return;
3986    }
3987
3988    do {
3989        itr->second->setLighting(state);
3990    } while (doAll && ++itr != _heightMaps.end());
3991    _needsRedraw = true;
3992}
3993
3994/**
3995 * \brief Create a new LIC and associate it with the named DataSet
3996 */
3997bool Renderer::addLIC(const DataSetId& id)
3998{
3999    DataSetHashmap::iterator itr;
4000
4001    bool doAll = false;
4002
4003    if (id.compare("all") == 0) {
4004        itr = _dataSets.begin();
4005    } else {
4006        itr = _dataSets.find(id);
4007    }
4008    if (itr == _dataSets.end()) {
4009        ERROR("Unknown dataset %s", id.c_str());
4010        return false;
4011    }
4012
4013    do {
4014        DataSet *ds = itr->second;
4015        const DataSetId& dsID = ds->getName();
4016
4017        if (getLIC(dsID)) {
4018            WARN("Replacing existing LIC %s", dsID.c_str());
4019            deleteLIC(dsID);
4020        }
4021
4022        LIC *lic = new LIC();
4023        _lics[dsID] = lic;
4024
4025        lic->setDataSet(ds,
4026                        _useCumulativeRange,
4027                        _cumulativeScalarRange,
4028                        _cumulativeVectorMagnitudeRange,
4029                        _cumulativeVectorComponentRange);
4030
4031        _renderer->AddViewProp(lic->getProp());
4032    } while (doAll && ++itr != _dataSets.end());
4033
4034    if (_cameraMode == IMAGE)
4035        setCameraMode(PERSPECTIVE);
4036    initCamera();
4037    _needsRedraw = true;
4038    return true;
4039}
4040
4041/**
4042 * \brief Get the LIC associated with a named DataSet
4043 */
4044LIC *Renderer::getLIC(const DataSetId& id)
4045{
4046    LICHashmap::iterator itr = _lics.find(id);
4047
4048    if (itr == _lics.end()) {
4049#ifdef DEBUG
4050        TRACE("LIC not found: %s", id.c_str());
4051#endif
4052        return NULL;
4053    } else
4054        return itr->second;
4055}
4056
4057/**
4058 * \brief Set the prop orientation with a quaternion
4059 */
4060void Renderer::setLICTransform(const DataSetId& id, vtkMatrix4x4 *trans)
4061{
4062    LICHashmap::iterator itr;
4063
4064    bool doAll = false;
4065
4066    if (id.compare("all") == 0) {
4067        itr = _lics.begin();
4068        doAll = true;
4069    } else {
4070        itr = _lics.find(id);
4071    }
4072    if (itr == _lics.end()) {
4073        ERROR("LIC not found: %s", id.c_str());
4074        return;
4075    }
4076
4077    do {
4078        itr->second->setTransform(trans);
4079    } while (doAll && ++itr != _lics.end());
4080
4081    resetAxes();
4082    _needsRedraw = true;
4083}
4084
4085/**
4086 * \brief Set the prop orientation with a quaternion
4087 */
4088void Renderer::setLICOrientation(const DataSetId& id, double quat[4])
4089{
4090    LICHashmap::iterator itr;
4091
4092    bool doAll = false;
4093
4094    if (id.compare("all") == 0) {
4095        itr = _lics.begin();
4096        doAll = true;
4097    } else {
4098        itr = _lics.find(id);
4099    }
4100    if (itr == _lics.end()) {
4101        ERROR("LIC not found: %s", id.c_str());
4102        return;
4103    }
4104
4105    do {
4106        itr->second->setOrientation(quat);
4107    } while (doAll && ++itr != _lics.end());
4108
4109    resetAxes();
4110    _needsRedraw = true;
4111}
4112
4113/**
4114 * \brief Set the prop orientation with a rotation about an axis
4115 */
4116void Renderer::setLICOrientation(const DataSetId& id, double angle, double axis[3])
4117{
4118    LICHashmap::iterator itr;
4119
4120    bool doAll = false;
4121
4122    if (id.compare("all") == 0) {
4123        itr = _lics.begin();
4124        doAll = true;
4125    } else {
4126        itr = _lics.find(id);
4127    }
4128    if (itr == _lics.end()) {
4129        ERROR("LIC not found: %s", id.c_str());
4130        return;
4131    }
4132
4133    do {
4134        itr->second->setOrientation(angle, axis);
4135    } while (doAll && ++itr != _lics.end());
4136
4137    resetAxes();
4138    _needsRedraw = true;
4139}
4140
4141/**
4142 * \brief Set the prop position in world coords
4143 */
4144void Renderer::setLICPosition(const DataSetId& id, double pos[3])
4145{
4146    LICHashmap::iterator itr;
4147
4148    bool doAll = false;
4149
4150    if (id.compare("all") == 0) {
4151        itr = _lics.begin();
4152        doAll = true;
4153    } else {
4154        itr = _lics.find(id);
4155    }
4156    if (itr == _lics.end()) {
4157        ERROR("LIC not found: %s", id.c_str());
4158        return;
4159    }
4160
4161    do {
4162        itr->second->setPosition(pos);
4163    } while (doAll && ++itr != _lics.end());
4164
4165    resetAxes();
4166    _needsRedraw = true;
4167}
4168
4169/**
4170 * \brief Set the prop scaling
4171 */
4172void Renderer::setLICScale(const DataSetId& id, double scale[3])
4173{
4174    LICHashmap::iterator itr;
4175
4176    bool doAll = false;
4177
4178    if (id.compare("all") == 0) {
4179        itr = _lics.begin();
4180        doAll = true;
4181    } else {
4182        itr = _lics.find(id);
4183    }
4184    if (itr == _lics.end()) {
4185        ERROR("LIC not found: %s", id.c_str());
4186        return;
4187    }
4188
4189    do {
4190        itr->second->setScale(scale);
4191    } while (doAll && ++itr != _lics.end());
4192
4193    resetAxes();
4194    _needsRedraw = true;
4195}
4196
4197/**
4198 * \brief Set the volume slice used for mapping volumetric data
4199 */
4200void Renderer::setLICVolumeSlice(const DataSetId& id, LIC::Axis axis, double ratio)
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    do {
4219        itr->second->selectVolumeSlice(axis, ratio);
4220     } while (doAll && ++itr != _lics.end());
4221
4222    initCamera();
4223    _needsRedraw = true;
4224}
4225
4226/**
4227 * \brief Associate an existing named color map with an LIC for the given DataSet
4228 */
4229void Renderer::setLICColorMap(const DataSetId& id, const ColorMapId& colorMapId)
4230{
4231    LICHashmap::iterator itr;
4232
4233    bool doAll = false;
4234
4235    if (id.compare("all") == 0) {
4236        itr = _lics.begin();
4237        doAll = true;
4238    } else {
4239        itr = _lics.find(id);
4240    }
4241
4242    if (itr == _lics.end()) {
4243        ERROR("LIC not found: %s", id.c_str());
4244        return;
4245    }
4246
4247    ColorMap *cmap = getColorMap(colorMapId);
4248    if (cmap == NULL) {
4249        ERROR("Unknown colormap: %s", colorMapId.c_str());
4250        return;
4251    }
4252
4253    do {
4254        TRACE("Set LIC color map: %s for dataset %s", colorMapId.c_str(),
4255              itr->second->getDataSet()->getName().c_str());
4256
4257        itr->second->setColorMap(cmap);
4258    } while (doAll && ++itr != _lics.end());
4259
4260    _needsRedraw = true;
4261}
4262
4263/**
4264 * \brief Set opacity of the LIC for the given DataSet
4265 */
4266void Renderer::setLICOpacity(const DataSetId& id, double opacity)
4267{
4268    LICHashmap::iterator itr;
4269
4270    bool doAll = false;
4271
4272    if (id.compare("all") == 0) {
4273        itr = _lics.begin();
4274        doAll = true;
4275    } else {
4276        itr = _lics.find(id);
4277    }
4278    if (itr == _lics.end()) {
4279        ERROR("LIC not found: %s", id.c_str());
4280        return;
4281    }
4282
4283    do {
4284        itr->second->setOpacity(opacity);
4285    } while (doAll && ++itr != _lics.end());
4286
4287    _needsRedraw = true;
4288}
4289
4290/**
4291 * \brief Turn on/off rendering of the LIC for the given DataSet
4292 */
4293void Renderer::setLICVisibility(const DataSetId& id, bool state)
4294{
4295    LICHashmap::iterator itr;
4296
4297    bool doAll = false;
4298
4299    if (id.compare("all") == 0) {
4300        itr = _lics.begin();
4301        doAll = true;
4302    } else {
4303        itr = _lics.find(id);
4304    }
4305    if (itr == _lics.end()) {
4306        ERROR("LIC not found: %s", id.c_str());
4307        return;
4308    }
4309
4310    do {
4311        itr->second->setVisibility(state);
4312    } while (doAll && ++itr != _lics.end());
4313
4314    _needsRedraw = true;
4315}
4316
4317/**
4318 * \brief Set the visibility of polygon edges for the specified DataSet
4319 */
4320void Renderer::setLICEdgeVisibility(const DataSetId& id, bool state)
4321{
4322    LICHashmap::iterator itr;
4323
4324    bool doAll = false;
4325
4326    if (id.compare("all") == 0) {
4327        itr = _lics.begin();
4328        doAll = true;
4329    } else {
4330        itr = _lics.find(id);
4331    }
4332    if (itr == _lics.end()) {
4333        ERROR("LIC not found: %s", id.c_str());
4334        return;
4335    }
4336
4337    do {
4338        itr->second->setEdgeVisibility(state);
4339    } while (doAll && ++itr != _lics.end());
4340
4341    _needsRedraw = true;
4342}
4343
4344/**
4345 * \brief Set the RGB polygon edge color for the specified DataSet
4346 */
4347void Renderer::setLICEdgeColor(const DataSetId& id, float color[3])
4348{
4349    LICHashmap::iterator itr;
4350
4351    bool doAll = false;
4352
4353    if (id.compare("all") == 0) {
4354        itr = _lics.begin();
4355        doAll = true;
4356    } else {
4357        itr = _lics.find(id);
4358    }
4359    if (itr == _lics.end()) {
4360        ERROR("LIC not found: %s", id.c_str());
4361        return;
4362    }
4363
4364    do {
4365        itr->second->setEdgeColor(color);
4366    } while (doAll && ++itr != _lics.end());
4367
4368    _needsRedraw = true;
4369}
4370
4371/**
4372 * \brief Set the polygon edge width for the specified DataSet (may be a no-op)
4373 *
4374 * If the OpenGL implementation/hardware does not support wide lines,
4375 * this function may not have an effect.
4376 */
4377void Renderer::setLICEdgeWidth(const DataSetId& id, float edgeWidth)
4378{
4379    LICHashmap::iterator itr;
4380
4381    bool doAll = false;
4382
4383    if (id.compare("all") == 0) {
4384        itr = _lics.begin();
4385        doAll = true;
4386    } else {
4387        itr = _lics.find(id);
4388    }
4389    if (itr == _lics.end()) {
4390        ERROR("LIC not found: %s", id.c_str());
4391        return;
4392    }
4393
4394    do {
4395        itr->second->setEdgeWidth(edgeWidth);
4396    } while (doAll && ++itr != _lics.end());
4397
4398    _needsRedraw = true;
4399}
4400
4401/**
4402 * \brief Turn LIC lighting on/off for the specified DataSet
4403 */
4404void Renderer::setLICLighting(const DataSetId& id, bool state)
4405{
4406    LICHashmap::iterator itr;
4407
4408    bool doAll = false;
4409
4410    if (id.compare("all") == 0) {
4411        itr = _lics.begin();
4412        doAll = true;
4413    } else {
4414        itr = _lics.find(id);
4415    }
4416    if (itr == _lics.end()) {
4417        ERROR("LIC not found: %s", id.c_str());
4418        return;
4419    }
4420
4421    do {
4422        itr->second->setLighting(state);
4423    } while (doAll && ++itr != _lics.end());
4424
4425    _needsRedraw = true;
4426}
4427
4428/**
4429 * \brief Create a new Molecule and associate it with the named DataSet
4430 */
4431bool Renderer::addMolecule(const DataSetId& id)
4432{
4433    DataSetHashmap::iterator itr;
4434
4435    bool doAll = false;
4436
4437    if (id.compare("all") == 0) {
4438        itr = _dataSets.begin();
4439    } else {
4440        itr = _dataSets.find(id);
4441    }
4442    if (itr == _dataSets.end()) {
4443        ERROR("Unknown dataset %s", id.c_str());
4444        return false;
4445    }
4446
4447    do {
4448        DataSet *ds = itr->second;
4449        const DataSetId& dsID = ds->getName();
4450
4451        if (getMolecule(dsID)) {
4452            WARN("Replacing existing Molecule %s", dsID.c_str());
4453            deleteMolecule(dsID);
4454        }
4455
4456        Molecule *molecule = new Molecule();
4457        _molecules[dsID] = molecule;
4458
4459        molecule->setDataSet(ds);
4460
4461        _renderer->AddViewProp(molecule->getProp());
4462    } while (doAll && ++itr != _dataSets.end());
4463
4464    if (_cameraMode == IMAGE)
4465        setCameraMode(PERSPECTIVE);
4466    initCamera();
4467    _needsRedraw = true;
4468    return true;
4469}
4470
4471/**
4472 * \brief Get the Molecule associated with a named DataSet
4473 */
4474Molecule *Renderer::getMolecule(const DataSetId& id)
4475{
4476    MoleculeHashmap::iterator itr = _molecules.find(id);
4477
4478    if (itr == _molecules.end()) {
4479#ifdef DEBUG
4480        TRACE("Molecule not found: %s", id.c_str());
4481#endif
4482        return NULL;
4483    } else
4484        return itr->second;
4485}
4486
4487/**
4488 * \brief Set the prop orientation with a quaternion
4489 */
4490void Renderer::setMoleculeTransform(const DataSetId& id, vtkMatrix4x4 *trans)
4491{
4492    MoleculeHashmap::iterator itr;
4493
4494    bool doAll = false;
4495
4496    if (id.compare("all") == 0) {
4497        itr = _molecules.begin();
4498        doAll = true;
4499    } else {
4500        itr = _molecules.find(id);
4501    }
4502    if (itr == _molecules.end()) {
4503        ERROR("Molecule not found: %s", id.c_str());
4504        return;
4505    }
4506
4507    do {
4508        itr->second->setTransform(trans);
4509    } while (doAll && ++itr != _molecules.end());
4510
4511    resetAxes();
4512    _needsRedraw = true;
4513}
4514
4515/**
4516 * \brief Set the prop orientation with a quaternion
4517 */
4518void Renderer::setMoleculeOrientation(const DataSetId& id, double quat[4])
4519{
4520    MoleculeHashmap::iterator itr;
4521
4522    bool doAll = false;
4523
4524    if (id.compare("all") == 0) {
4525        itr = _molecules.begin();
4526        doAll = true;
4527    } else {
4528        itr = _molecules.find(id);
4529    }
4530    if (itr == _molecules.end()) {
4531        ERROR("Molecule not found: %s", id.c_str());
4532        return;
4533    }
4534
4535    do {
4536        itr->second->setOrientation(quat);
4537    } while (doAll && ++itr != _molecules.end());
4538
4539    resetAxes();
4540    _needsRedraw = true;
4541}
4542
4543/**
4544 * \brief Set the prop orientation with a rotation about an axis
4545 */
4546void Renderer::setMoleculeOrientation(const DataSetId& id, double angle, double axis[3])
4547{
4548    MoleculeHashmap::iterator itr;
4549
4550    bool doAll = false;
4551
4552    if (id.compare("all") == 0) {
4553        itr = _molecules.begin();
4554        doAll = true;
4555    } else {
4556        itr = _molecules.find(id);
4557    }
4558    if (itr == _molecules.end()) {
4559        ERROR("Molecule not found: %s", id.c_str());
4560        return;
4561    }
4562
4563    do {
4564        itr->second->setOrientation(angle, axis);
4565    } while (doAll && ++itr != _molecules.end());
4566
4567    resetAxes();
4568    _needsRedraw = true;
4569}
4570
4571/**
4572 * \brief Set the prop position in world coords
4573 */
4574void Renderer::setMoleculePosition(const DataSetId& id, double pos[3])
4575{
4576    MoleculeHashmap::iterator itr;
4577
4578    bool doAll = false;
4579
4580    if (id.compare("all") == 0) {
4581        itr = _molecules.begin();
4582        doAll = true;
4583    } else {
4584        itr = _molecules.find(id);
4585    }
4586    if (itr == _molecules.end()) {
4587        ERROR("Molecule not found: %s", id.c_str());
4588        return;
4589    }
4590
4591    do {
4592        itr->second->setPosition(pos);
4593    } while (doAll && ++itr != _molecules.end());
4594
4595    resetAxes();
4596    _needsRedraw = true;
4597}
4598
4599/**
4600 * \brief Set the prop scaling
4601 */
4602void Renderer::setMoleculeScale(const DataSetId& id, double scale[3])
4603{
4604    MoleculeHashmap::iterator itr;
4605
4606    bool doAll = false;
4607
4608    if (id.compare("all") == 0) {
4609        itr = _molecules.begin();
4610        doAll = true;
4611    } else {
4612        itr = _molecules.find(id);
4613    }
4614    if (itr == _molecules.end()) {
4615        ERROR("Molecule not found: %s", id.c_str());
4616        return;
4617    }
4618
4619    do {
4620        itr->second->setScale(scale);
4621    } while (doAll && ++itr != _molecules.end());
4622
4623    resetAxes();
4624    _needsRedraw = true;
4625}
4626
4627/**
4628 * \brief Associate an existing named color map with a Molecule for the given DataSet
4629 */
4630void Renderer::setMoleculeColorMap(const DataSetId& id, const ColorMapId& colorMapId)
4631{
4632    MoleculeHashmap::iterator itr;
4633
4634    bool doAll = false;
4635
4636    if (id.compare("all") == 0) {
4637        itr = _molecules.begin();
4638        doAll = true;
4639    } else {
4640        itr = _molecules.find(id);
4641    }
4642
4643    if (itr == _molecules.end()) {
4644        ERROR("Molecule not found: %s", id.c_str());
4645        return;
4646    }
4647
4648    ColorMap *cmap = getColorMap(colorMapId);
4649    if (cmap == NULL) {
4650        ERROR("Unknown colormap: %s", colorMapId.c_str());
4651        return;
4652    }
4653
4654    do {
4655        TRACE("Set Molecule color map: %s for dataset %s", colorMapId.c_str(),
4656              itr->second->getDataSet()->getName().c_str());
4657
4658        itr->second->setColorMap(cmap);
4659    } while (doAll && ++itr != _molecules.end());
4660
4661    _needsRedraw = true;
4662}
4663
4664/**
4665 * \brief Set opacity of the Molecule for the given DataSet
4666 */
4667void Renderer::setMoleculeOpacity(const DataSetId& id, double opacity)
4668{
4669    MoleculeHashmap::iterator itr;
4670
4671    bool doAll = false;
4672
4673    if (id.compare("all") == 0) {
4674        itr = _molecules.begin();
4675        doAll = true;
4676    } else {
4677        itr = _molecules.find(id);
4678    }
4679    if (itr == _molecules.end()) {
4680        ERROR("Molecule not found: %s", id.c_str());
4681        return;
4682    }
4683
4684    do {
4685        itr->second->setOpacity(opacity);
4686    } while (doAll && ++itr != _molecules.end());
4687
4688    _needsRedraw = true;
4689}
4690
4691/**
4692 * \brief Set radius standard for scaling atoms
4693 */
4694void Renderer::setMoleculeAtomScaling(const DataSetId& id, Molecule::AtomScaling scaling)
4695{
4696    MoleculeHashmap::iterator itr;
4697
4698    bool doAll = false;
4699
4700    if (id.compare("all") == 0) {
4701        itr = _molecules.begin();
4702        doAll = true;
4703    } else {
4704        itr = _molecules.find(id);
4705    }
4706    if (itr == _molecules.end()) {
4707        ERROR("Molecule not found: %s", id.c_str());
4708        return;
4709    }
4710
4711    do {
4712        itr->second->setAtomScaling(scaling);
4713    } while (doAll && ++itr != _molecules.end());
4714
4715    _needsRedraw = true;
4716}
4717
4718/**
4719 * \brief Turn on/off rendering of the Molecule atoms for the given DataSet
4720 */
4721void Renderer::setMoleculeAtomVisibility(const DataSetId& id, bool state)
4722{
4723    MoleculeHashmap::iterator itr;
4724
4725    bool doAll = false;
4726
4727    if (id.compare("all") == 0) {
4728        itr = _molecules.begin();
4729        doAll = true;
4730    } else {
4731        itr = _molecules.find(id);
4732    }
4733    if (itr == _molecules.end()) {
4734        ERROR("Molecule not found: %s", id.c_str());
4735        return;
4736    }
4737
4738    do {
4739        itr->second->setAtomVisibility(state);
4740    } while (doAll && ++itr != _molecules.end());
4741
4742    _needsRedraw = true;
4743}
4744
4745/**
4746 * \brief Turn on/off rendering of the Molecule bonds for the given DataSet
4747 */
4748void Renderer::setMoleculeBondVisibility(const DataSetId& id, bool state)
4749{
4750    MoleculeHashmap::iterator itr;
4751
4752    bool doAll = false;
4753
4754    if (id.compare("all") == 0) {
4755        itr = _molecules.begin();
4756        doAll = true;
4757    } else {
4758        itr = _molecules.find(id);
4759    }
4760    if (itr == _molecules.end()) {
4761        ERROR("Molecule not found: %s", id.c_str());
4762        return;
4763    }
4764
4765    do {
4766        itr->second->setBondVisibility(state);
4767    } while (doAll && ++itr != _molecules.end());
4768
4769    _needsRedraw = true;
4770}
4771
4772/**
4773 * \brief Turn on/off rendering of the Molecule for the given DataSet
4774 */
4775void Renderer::setMoleculeVisibility(const DataSetId& id, bool state)
4776{
4777    MoleculeHashmap::iterator itr;
4778
4779    bool doAll = false;
4780
4781    if (id.compare("all") == 0) {
4782        itr = _molecules.begin();
4783        doAll = true;
4784    } else {
4785        itr = _molecules.find(id);
4786    }
4787    if (itr == _molecules.end()) {
4788        ERROR("Molecule not found: %s", id.c_str());
4789        return;
4790    }
4791
4792    do {
4793        itr->second->setVisibility(state);
4794    } while (doAll && ++itr != _molecules.end());
4795
4796    _needsRedraw = true;
4797}
4798
4799/**
4800 * \brief Set the visibility of polygon edges for the specified DataSet
4801 */
4802void Renderer::setMoleculeEdgeVisibility(const DataSetId& id, bool state)
4803{
4804    MoleculeHashmap::iterator itr;
4805
4806    bool doAll = false;
4807
4808    if (id.compare("all") == 0) {
4809        itr = _molecules.begin();
4810        doAll = true;
4811    } else {
4812        itr = _molecules.find(id);
4813    }
4814    if (itr == _molecules.end()) {
4815        ERROR("Molecule not found: %s", id.c_str());
4816        return;
4817    }
4818
4819    do {
4820        itr->second->setEdgeVisibility(state);
4821    } while (doAll && ++itr != _molecules.end());
4822
4823    _needsRedraw = true;
4824}
4825
4826/**
4827 * \brief Set the RGB polygon edge color for the specified DataSet
4828 */
4829void Renderer::setMoleculeEdgeColor(const DataSetId& id, float color[3])
4830{
4831    MoleculeHashmap::iterator itr;
4832
4833    bool doAll = false;
4834
4835    if (id.compare("all") == 0) {
4836        itr = _molecules.begin();
4837        doAll = true;
4838    } else {
4839        itr = _molecules.find(id);
4840    }
4841    if (itr == _molecules.end()) {
4842        ERROR("Molecule not found: %s", id.c_str());
4843        return;
4844    }
4845
4846    do {
4847        itr->second->setEdgeColor(color);
4848    } while (doAll && ++itr != _molecules.end());
4849
4850    _needsRedraw = true;
4851}
4852
4853/**
4854 * \brief Set the polygon edge width for the specified DataSet (may be a no-op)
4855 *
4856 * If the OpenGL implementation/hardware does not support wide lines,
4857 * this function may not have an effect.
4858 */
4859void Renderer::setMoleculeEdgeWidth(const DataSetId& id, float edgeWidth)
4860{
4861    MoleculeHashmap::iterator itr;
4862
4863    bool doAll = false;
4864
4865    if (id.compare("all") == 0) {
4866        itr = _molecules.begin();
4867        doAll = true;
4868    } else {
4869        itr = _molecules.find(id);
4870    }
4871    if (itr == _molecules.end()) {
4872        ERROR("Molecule not found: %s", id.c_str());
4873        return;
4874    }
4875
4876    do {
4877        itr->second->setEdgeWidth(edgeWidth);
4878    } while (doAll && ++itr != _molecules.end());
4879
4880    _needsRedraw = true;
4881}
4882
4883/**
4884 * \brief Set wireframe rendering for the specified DataSet
4885 */
4886void Renderer::setMoleculeWireframe(const DataSetId& id, bool state)
4887{
4888    MoleculeHashmap::iterator itr;
4889
4890    bool doAll = false;
4891
4892    if (id.compare("all") == 0) {
4893        itr = _molecules.begin();
4894        doAll = true;
4895    } else {
4896        itr = _molecules.find(id);
4897    }
4898    if (itr == _molecules.end()) {
4899        ERROR("Molecule not found: %s", id.c_str());
4900        return;
4901    }
4902
4903    do {
4904        itr->second->setWireframe(state);
4905    } while (doAll && ++itr != _molecules.end());
4906
4907    _needsRedraw = true;
4908}
4909
4910/**
4911 * \brief Turn Molecule lighting on/off for the specified DataSet
4912 */
4913void Renderer::setMoleculeLighting(const DataSetId& id, bool state)
4914{
4915    MoleculeHashmap::iterator itr;
4916
4917    bool doAll = false;
4918
4919    if (id.compare("all") == 0) {
4920        itr = _molecules.begin();
4921        doAll = true;
4922    } else {
4923        itr = _molecules.find(id);
4924    }
4925    if (itr == _molecules.end()) {
4926        ERROR("Molecule not found: %s", id.c_str());
4927        return;
4928    }
4929
4930    do {
4931        itr->second->setLighting(state);
4932    } while (doAll && ++itr != _molecules.end());
4933
4934    _needsRedraw = true;
4935}
4936
4937/**
4938 * \brief Create a new PolyData and associate it with the named DataSet
4939 */
4940bool Renderer::addPolyData(const DataSetId& id)
4941{
4942    DataSetHashmap::iterator itr;
4943
4944    bool doAll = false;
4945
4946    if (id.compare("all") == 0) {
4947        itr = _dataSets.begin();
4948    } else {
4949        itr = _dataSets.find(id);
4950    }
4951    if (itr == _dataSets.end()) {
4952        ERROR("Unknown dataset %s", id.c_str());
4953        return false;
4954    }
4955
4956    do {
4957        DataSet *ds = itr->second;
4958        const DataSetId& dsID = ds->getName();
4959
4960        if (getPolyData(dsID)) {
4961            WARN("Replacing existing PolyData %s", dsID.c_str());
4962            deletePolyData(dsID);
4963        }
4964
4965        PolyData *polyData = new PolyData();
4966        _polyDatas[dsID] = polyData;
4967
4968        polyData->setDataSet(ds);
4969
4970        _renderer->AddViewProp(polyData->getProp());
4971    } while (doAll && ++itr != _dataSets.end());
4972
4973    if (_cameraMode == IMAGE)
4974        setCameraMode(PERSPECTIVE);
4975    initCamera();
4976    _needsRedraw = true;
4977    return true;
4978}
4979
4980/**
4981 * \brief Get the PolyData associated with a named DataSet
4982 */
4983PolyData *Renderer::getPolyData(const DataSetId& id)
4984{
4985    PolyDataHashmap::iterator itr = _polyDatas.find(id);
4986
4987    if (itr == _polyDatas.end()) {
4988#ifdef DEBUG
4989        TRACE("PolyData not found: %s", id.c_str());
4990#endif
4991        return NULL;
4992    } else
4993        return itr->second;
4994}
4995
4996/**
4997 * \brief Set an additional transform on the prop
4998 */
4999void Renderer::setPolyDataTransform(const DataSetId& id, vtkMatrix4x4 *trans)
5000{
5001    PolyDataHashmap::iterator itr;
5002
5003    bool doAll = false;
5004
5005    if (id.compare("all") == 0) {
5006        itr = _polyDatas.begin();
5007        doAll = true;
5008    } else {
5009        itr = _polyDatas.find(id);
5010    }
5011    if (itr == _polyDatas.end()) {
5012        ERROR("PolyData not found: %s", id.c_str());
5013        return;
5014    }
5015
5016    do {
5017        itr->second->setTransform(trans);
5018    } while (doAll && ++itr != _polyDatas.end());
5019
5020    resetAxes();
5021    _needsRedraw = true;
5022}
5023
5024/**
5025 * \brief Set the prop orientation with a quaternion
5026 */
5027void Renderer::setPolyDataOrientation(const DataSetId& id, double quat[4])
5028{
5029    PolyDataHashmap::iterator itr;
5030
5031    bool doAll = false;
5032
5033    if (id.compare("all") == 0) {
5034        itr = _polyDatas.begin();
5035        doAll = true;
5036    } else {
5037        itr = _polyDatas.find(id);
5038    }
5039    if (itr == _polyDatas.end()) {
5040        ERROR("PolyData not found: %s", id.c_str());
5041        return;
5042    }
5043
5044    do {
5045        itr->second->setOrientation(quat);
5046    } while (doAll && ++itr != _polyDatas.end());
5047
5048    resetAxes();
5049    _needsRedraw = true;
5050}
5051
5052/**
5053 * \brief Set the prop orientation with a rotation about an axis
5054 */
5055void Renderer::setPolyDataOrientation(const DataSetId& id, double angle, double axis[3])
5056{
5057    PolyDataHashmap::iterator itr;
5058
5059    bool doAll = false;
5060
5061    if (id.compare("all") == 0) {
5062        itr = _polyDatas.begin();
5063        doAll = true;
5064    } else {
5065        itr = _polyDatas.find(id);
5066    }
5067    if (itr == _polyDatas.end()) {
5068        ERROR("PolyData not found: %s", id.c_str());
5069        return;
5070    }
5071
5072    do {
5073        itr->second->setOrientation(angle, axis);
5074    } while (doAll && ++itr != _polyDatas.end());
5075
5076    resetAxes();
5077    _needsRedraw = true;
5078}
5079
5080/**
5081 * \brief Set the prop position in world coords
5082 */
5083void Renderer::setPolyDataPosition(const DataSetId& id, double pos[3])
5084{
5085    PolyDataHashmap::iterator itr;
5086
5087    bool doAll = false;
5088
5089    if (id.compare("all") == 0) {
5090        itr = _polyDatas.begin();
5091        doAll = true;
5092    } else {
5093        itr = _polyDatas.find(id);
5094    }
5095    if (itr == _polyDatas.end()) {
5096        ERROR("PolyData not found: %s", id.c_str());
5097        return;
5098    }
5099
5100    do {
5101        itr->second->setPosition(pos);
5102    } while (doAll && ++itr != _polyDatas.end());
5103
5104    resetAxes();
5105    _needsRedraw = true;
5106}
5107
5108/**
5109 * \brief Set the prop scaling
5110 */
5111void Renderer::setPolyDataScale(const DataSetId& id, double scale[3])
5112{
5113    PolyDataHashmap::iterator itr;
5114
5115    bool doAll = false;
5116
5117    if (id.compare("all") == 0) {
5118        itr = _polyDatas.begin();
5119        doAll = true;
5120    } else {
5121        itr = _polyDatas.find(id);
5122    }
5123    if (itr == _polyDatas.end()) {
5124        ERROR("PolyData not found: %s", id.c_str());
5125        return;
5126    }
5127
5128    do {
5129        itr->second->setScale(scale);
5130    } while (doAll && ++itr != _polyDatas.end());
5131
5132    resetAxes();
5133    _needsRedraw = true;
5134}
5135
5136/**
5137 * \brief Set opacity of the PolyData for the given DataSet
5138 */
5139void Renderer::setPolyDataOpacity(const DataSetId& id, double opacity)
5140{
5141    PolyDataHashmap::iterator itr;
5142
5143    bool doAll = false;
5144
5145    if (id.compare("all") == 0) {
5146        itr = _polyDatas.begin();
5147        doAll = true;
5148    } else {
5149        itr = _polyDatas.find(id);
5150    }
5151    if (itr == _polyDatas.end()) {
5152        ERROR("PolyData not found: %s", id.c_str());
5153        return;
5154    }
5155
5156    do {
5157        itr->second->setOpacity(opacity);
5158    } while (doAll && ++itr != _polyDatas.end());
5159
5160    _needsRedraw = true;
5161}
5162
5163/**
5164 * \brief Turn on/off rendering of the PolyData mapper for the given DataSet
5165 */
5166void Renderer::setPolyDataVisibility(const DataSetId& id, bool state)
5167{
5168    PolyDataHashmap::iterator itr;
5169
5170    bool doAll = false;
5171
5172    if (id.compare("all") == 0) {
5173        itr = _polyDatas.begin();
5174        doAll = true;
5175    } else {
5176        itr = _polyDatas.find(id);
5177    }
5178    if (itr == _polyDatas.end()) {
5179        ERROR("PolyData not found: %s", id.c_str());
5180        return;
5181    }
5182
5183    do {
5184        itr->second->setVisibility(state);
5185    } while (doAll && ++itr != _polyDatas.end());
5186
5187    _needsRedraw = true;
5188}
5189
5190/**
5191 * \brief Set the RGB polygon face color for the specified DataSet
5192 */
5193void Renderer::setPolyDataColor(const DataSetId& id, float color[3])
5194{
5195    PolyDataHashmap::iterator itr;
5196
5197    bool doAll = false;
5198
5199    if (id.compare("all") == 0) {
5200        itr = _polyDatas.begin();
5201        doAll = true;
5202    } else {
5203        itr = _polyDatas.find(id);
5204    }
5205    if (itr == _polyDatas.end()) {
5206        ERROR("PolyData not found: %s", id.c_str());
5207        return;
5208    }
5209
5210    do {
5211        itr->second->setColor(color);
5212    } while (doAll && ++itr != _polyDatas.end());
5213    _needsRedraw = true;
5214}
5215
5216/**
5217 * \brief Set the visibility of polygon edges for the specified DataSet
5218 */
5219void Renderer::setPolyDataEdgeVisibility(const DataSetId& id, bool state)
5220{
5221    PolyDataHashmap::iterator itr;
5222
5223    bool doAll = false;
5224
5225    if (id.compare("all") == 0) {
5226        itr = _polyDatas.begin();
5227        doAll = true;
5228    } else {
5229        itr = _polyDatas.find(id);
5230    }
5231    if (itr == _polyDatas.end()) {
5232        ERROR("PolyData not found: %s", id.c_str());
5233        return;
5234    }
5235
5236    do {
5237        itr->second->setEdgeVisibility(state);
5238    } while (doAll && ++itr != _polyDatas.end());
5239
5240    _needsRedraw = true;
5241}
5242
5243/**
5244 * \brief Set the RGB polygon edge color for the specified DataSet
5245 */
5246void Renderer::setPolyDataEdgeColor(const DataSetId& id, float color[3])
5247{
5248    PolyDataHashmap::iterator itr;
5249
5250    bool doAll = false;
5251
5252    if (id.compare("all") == 0) {
5253        itr = _polyDatas.begin();
5254        doAll = true;
5255    } else {
5256        itr = _polyDatas.find(id);
5257    }
5258    if (itr == _polyDatas.end()) {
5259        ERROR("PolyData not found: %s", id.c_str());
5260        return;
5261    }
5262
5263    do {
5264        itr->second->setEdgeColor(color);
5265    } while (doAll && ++itr != _polyDatas.end());
5266
5267    _needsRedraw = true;
5268}
5269
5270/**
5271 * \brief Set the polygon edge width for the specified DataSet (may be a no-op)
5272 *
5273 * If the OpenGL implementation/hardware does not support wide lines,
5274 * this function may not have an effect.
5275 */
5276void Renderer::setPolyDataEdgeWidth(const DataSetId& id, float edgeWidth)
5277{
5278    PolyDataHashmap::iterator itr;
5279
5280    bool doAll = false;
5281
5282    if (id.compare("all") == 0) {
5283        itr = _polyDatas.begin();
5284        doAll = true;
5285    } else {
5286        itr = _polyDatas.find(id);
5287    }
5288    if (itr == _polyDatas.end()) {
5289        ERROR("PolyData not found: %s", id.c_str());
5290        return;
5291    }
5292
5293    do {
5294        itr->second->setEdgeWidth(edgeWidth);
5295    } while (doAll && ++itr != _polyDatas.end());
5296
5297    _needsRedraw = true;
5298}
5299
5300/**
5301 * \brief Set the point size for the specified DataSet (may be a no-op)
5302 *
5303 * If the OpenGL implementation/hardware does not support wide points,
5304 * this function may not have an effect.
5305 */
5306void Renderer::setPolyDataPointSize(const DataSetId& id, float size)
5307{
5308    PolyDataHashmap::iterator itr;
5309
5310    bool doAll = false;
5311
5312    if (id.compare("all") == 0) {
5313        itr = _polyDatas.begin();
5314        doAll = true;
5315    } else {
5316        itr = _polyDatas.find(id);
5317    }
5318    if (itr == _polyDatas.end()) {
5319        ERROR("PolyData not found: %s", id.c_str());
5320        return;
5321    }
5322
5323    do {
5324        itr->second->setPointSize(size);
5325    } while (doAll && ++itr != _polyDatas.end());
5326
5327    _needsRedraw = true;
5328}
5329
5330/**
5331 * \brief Set wireframe rendering for the specified DataSet
5332 */
5333void Renderer::setPolyDataWireframe(const DataSetId& id, bool state)
5334{
5335    PolyDataHashmap::iterator itr;
5336
5337    bool doAll = false;
5338
5339    if (id.compare("all") == 0) {
5340        itr = _polyDatas.begin();
5341        doAll = true;
5342    } else {
5343        itr = _polyDatas.find(id);
5344    }
5345    if (itr == _polyDatas.end()) {
5346        ERROR("PolyData not found: %s", id.c_str());
5347        return;
5348    }
5349
5350    do {
5351        itr->second->setWireframe(state);
5352    } while (doAll && ++itr != _polyDatas.end());
5353
5354    _needsRedraw = true;
5355}
5356
5357/**
5358 * \brief Turn mesh lighting on/off for the specified DataSet
5359 */
5360void Renderer::setPolyDataLighting(const DataSetId& id, bool state)
5361{
5362    PolyDataHashmap::iterator itr;
5363
5364    bool doAll = false;
5365
5366    if (id.compare("all") == 0) {
5367        itr = _polyDatas.begin();
5368        doAll = true;
5369    } else {
5370        itr = _polyDatas.find(id);
5371    }
5372    if (itr == _polyDatas.end()) {
5373        ERROR("PolyData not found: %s", id.c_str());
5374        return;
5375    }
5376
5377    do {
5378        itr->second->setLighting(state);
5379    } while (doAll && ++itr != _polyDatas.end());
5380
5381    _needsRedraw = true;
5382}
5383
5384/**
5385 * \brief Create a new PseudoColor rendering for the specified DataSet
5386 */
5387bool Renderer::addPseudoColor(const DataSetId& id)
5388{
5389    DataSetHashmap::iterator itr;
5390
5391    bool doAll = false;
5392
5393    if (id.compare("all") == 0) {
5394        itr = _dataSets.begin();
5395    } else {
5396        itr = _dataSets.find(id);
5397    }
5398    if (itr == _dataSets.end()) {
5399        ERROR("Unknown dataset %s", id.c_str());
5400        return false;
5401    }
5402
5403    do {
5404        DataSet *ds = itr->second;
5405        const DataSetId& dsID = ds->getName();
5406
5407        if (getPseudoColor(dsID)) {
5408            WARN("Replacing existing PseudoColor %s", dsID.c_str());
5409            deletePseudoColor(dsID);
5410        }
5411        PseudoColor *pc = new PseudoColor();
5412        _pseudoColors[dsID] = pc;
5413
5414        pc->setDataSet(ds,
5415                       _useCumulativeRange,
5416                       _cumulativeScalarRange,
5417                       _cumulativeVectorMagnitudeRange,
5418                       _cumulativeVectorComponentRange);
5419
5420        _renderer->AddViewProp(pc->getProp());
5421    } while (doAll && ++itr != _dataSets.end());
5422
5423    initCamera();
5424    _needsRedraw = true;
5425    return true;
5426}
5427
5428/**
5429 * \brief Get the PseudoColor associated with the specified DataSet
5430 */
5431PseudoColor *Renderer::getPseudoColor(const DataSetId& id)
5432{
5433    PseudoColorHashmap::iterator itr = _pseudoColors.find(id);
5434
5435    if (itr == _pseudoColors.end()) {
5436#ifdef DEBUG
5437        TRACE("PseudoColor not found: %s", id.c_str());
5438#endif
5439        return NULL;
5440    } else
5441        return itr->second;
5442}
5443
5444/**
5445 * \brief Set the prop orientation with a quaternion
5446 */
5447void Renderer::setPseudoColorTransform(const DataSetId& id, vtkMatrix4x4 *trans)
5448{
5449    PseudoColorHashmap::iterator itr;
5450
5451    bool doAll = false;
5452
5453    if (id.compare("all") == 0) {
5454        itr = _pseudoColors.begin();
5455        doAll = true;
5456    } else {
5457        itr = _pseudoColors.find(id);
5458    }
5459    if (itr == _pseudoColors.end()) {
5460        ERROR("PseudoColor not found: %s", id.c_str());
5461        return;
5462    }
5463
5464    do {
5465        itr->second->setTransform(trans);
5466    } while (doAll && ++itr != _pseudoColors.end());
5467
5468    resetAxes();
5469    _needsRedraw = true;
5470}
5471
5472/**
5473 * \brief Set the prop orientation with a quaternion
5474 */
5475void Renderer::setPseudoColorOrientation(const DataSetId& id, double quat[4])
5476{
5477    PseudoColorHashmap::iterator itr;
5478
5479    bool doAll = false;
5480
5481    if (id.compare("all") == 0) {
5482        itr = _pseudoColors.begin();
5483        doAll = true;
5484    } else {
5485        itr = _pseudoColors.find(id);
5486    }
5487    if (itr == _pseudoColors.end()) {
5488        ERROR("PseudoColor not found: %s", id.c_str());
5489        return;
5490    }
5491
5492    do {
5493        itr->second->setOrientation(quat);
5494    } while (doAll && ++itr != _pseudoColors.end());
5495
5496    resetAxes();
5497    _needsRedraw = true;
5498}
5499
5500/**
5501 * \brief Set the prop orientation with a rotation about an axis
5502 */
5503void Renderer::setPseudoColorOrientation(const DataSetId& id, double angle, double axis[3])
5504{
5505    PseudoColorHashmap::iterator itr;
5506
5507    bool doAll = false;
5508
5509    if (id.compare("all") == 0) {
5510        itr = _pseudoColors.begin();
5511        doAll = true;
5512    } else {
5513        itr = _pseudoColors.find(id);
5514    }
5515    if (itr == _pseudoColors.end()) {
5516        ERROR("PseudoColor not found: %s", id.c_str());
5517        return;
5518    }
5519
5520    do {
5521        itr->second->setOrientation(angle, axis);
5522    } while (doAll && ++itr != _pseudoColors.end());
5523
5524    resetAxes();
5525    _needsRedraw = true;
5526}
5527
5528/**
5529 * \brief Set the prop position in world coords
5530 */
5531void Renderer::setPseudoColorPosition(const DataSetId& id, double pos[3])
5532{
5533    PseudoColorHashmap::iterator itr;
5534
5535    bool doAll = false;
5536
5537    if (id.compare("all") == 0) {
5538        itr = _pseudoColors.begin();
5539        doAll = true;
5540    } else {
5541        itr = _pseudoColors.find(id);
5542    }
5543    if (itr == _pseudoColors.end()) {
5544        ERROR("PseudoColor not found: %s", id.c_str());
5545        return;
5546    }
5547
5548    do {
5549        itr->second->setPosition(pos);
5550    } while (doAll && ++itr != _pseudoColors.end());
5551
5552    resetAxes();
5553    _needsRedraw = true;
5554}
5555
5556/**
5557 * \brief Set the prop scaling
5558 */
5559void Renderer::setPseudoColorScale(const DataSetId& id, double scale[3])
5560{
5561    PseudoColorHashmap::iterator itr;
5562
5563    bool doAll = false;
5564
5565    if (id.compare("all") == 0) {
5566        itr = _pseudoColors.begin();
5567        doAll = true;
5568    } else {
5569        itr = _pseudoColors.find(id);
5570    }
5571    if (itr == _pseudoColors.end()) {
5572        ERROR("PseudoColor not found: %s", id.c_str());
5573        return;
5574    }
5575
5576    do {
5577        itr->second->setScale(scale);
5578    } while (doAll && ++itr != _pseudoColors.end());
5579
5580    resetAxes();
5581    _needsRedraw = true;
5582}
5583
5584/**
5585 * \brief Set the RGB color for the specified DataSet
5586 */
5587void Renderer::setPseudoColorColor(const DataSetId& id, float color[3])
5588{
5589    PseudoColorHashmap::iterator itr;
5590
5591    bool doAll = false;
5592
5593    if (id.compare("all") == 0) {
5594        itr = _pseudoColors.begin();
5595        doAll = true;
5596    } else {
5597        itr = _pseudoColors.find(id);
5598    }
5599    if (itr == _pseudoColors.end()) {
5600        ERROR("PseudoColor not found: %s", id.c_str());
5601        return;
5602    }
5603
5604    do {
5605        itr->second->setColor(color);
5606    } while (doAll && ++itr != _pseudoColors.end());
5607
5608    _needsRedraw = true;
5609}
5610
5611/**
5612 * \brief Associate an existing named color map with a PseudoColor for the given DataSet
5613 */
5614void Renderer::setPseudoColorColorMap(const DataSetId& id, const ColorMapId& colorMapId)
5615{
5616    PseudoColorHashmap::iterator itr;
5617
5618    bool doAll = false;
5619
5620    if (id.compare("all") == 0) {
5621        itr = _pseudoColors.begin();
5622        doAll = true;
5623    } else {
5624        itr = _pseudoColors.find(id);
5625    }
5626
5627    if (itr == _pseudoColors.end()) {
5628        ERROR("PseudoColor not found: %s", id.c_str());
5629        return;
5630    }
5631
5632    ColorMap *cmap = getColorMap(colorMapId);
5633    if (cmap == NULL) {
5634        ERROR("Unknown colormap: %s", colorMapId.c_str());
5635        return;
5636    }
5637
5638    do {
5639        TRACE("Set PseudoColor color map: %s for dataset %s", colorMapId.c_str(),
5640              itr->second->getDataSet()->getName().c_str());
5641
5642        itr->second->setColorMap(cmap);
5643    } while (doAll && ++itr != _pseudoColors.end());
5644
5645    _needsRedraw = true;
5646}
5647
5648/**
5649 * \brief Set the color mode for the specified DataSet
5650 */
5651void Renderer::setPseudoColorColorMode(const DataSetId& id, PseudoColor::ColorMode mode)
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    if (itr == _pseudoColors.end()) {
5664        ERROR("PseudoColor not found: %s", id.c_str());
5665        return;
5666    }
5667
5668    do {
5669        itr->second->setColorMode(mode);
5670    } while (doAll && ++itr != _pseudoColors.end());
5671
5672    _needsRedraw = true;
5673}
5674
5675/**
5676 * \brief Set opacity of the PseudoColor for the given DataSet
5677 */
5678void Renderer::setPseudoColorOpacity(const DataSetId& id, double opacity)
5679{
5680    PseudoColorHashmap::iterator itr;
5681
5682    bool doAll = false;
5683
5684    if (id.compare("all") == 0) {
5685        itr = _pseudoColors.begin();
5686        doAll = true;
5687    } else {
5688        itr = _pseudoColors.find(id);
5689    }
5690
5691    if (itr == _pseudoColors.end()) {
5692        ERROR("PseudoColor not found: %s", id.c_str());
5693        return;
5694    }
5695
5696    do {
5697        itr->second->setOpacity(opacity);
5698    } while (doAll && ++itr != _pseudoColors.end());
5699
5700    _needsRedraw = true;
5701}
5702
5703/**
5704 * \brief Turn on/off rendering of the PseudoColor mapper for the given DataSet
5705 */
5706void Renderer::setPseudoColorVisibility(const DataSetId& id, bool state)
5707{
5708    PseudoColorHashmap::iterator itr;
5709
5710    bool doAll = false;
5711
5712    if (id.compare("all") == 0) {
5713        itr = _pseudoColors.begin();
5714        doAll = true;
5715    } else {
5716        itr = _pseudoColors.find(id);
5717    }
5718
5719    if (itr == _pseudoColors.end()) {
5720        ERROR("PseudoColor not found: %s", id.c_str());
5721        return;
5722    }
5723
5724    do {
5725        itr->second->setVisibility(state);
5726    } while (doAll && ++itr != _pseudoColors.end());
5727
5728    _needsRedraw = true;
5729}
5730
5731/**
5732 * \brief Set the visibility of polygon edges for the specified DataSet
5733 */
5734void Renderer::setPseudoColorEdgeVisibility(const DataSetId& id, bool state)
5735{
5736    PseudoColorHashmap::iterator itr;
5737
5738    bool doAll = false;
5739
5740    if (id.compare("all") == 0) {
5741        itr = _pseudoColors.begin();
5742        doAll = true;
5743    } else {
5744        itr = _pseudoColors.find(id);
5745    }
5746
5747    if (itr == _pseudoColors.end()) {
5748        ERROR("PseudoColor not found: %s", id.c_str());
5749        return;
5750    }
5751
5752    do {
5753        itr->second->setEdgeVisibility(state);
5754    } while (doAll && ++itr != _pseudoColors.end());
5755
5756    _needsRedraw = true;
5757}
5758
5759/**
5760 * \brief Set the RGB polygon edge color for the specified DataSet
5761 */
5762void Renderer::setPseudoColorEdgeColor(const DataSetId& id, float color[3])
5763{
5764    PseudoColorHashmap::iterator itr;
5765
5766    bool doAll = false;
5767
5768    if (id.compare("all") == 0) {
5769        itr = _pseudoColors.begin();
5770        doAll = true;
5771    } else {
5772        itr = _pseudoColors.find(id);
5773    }
5774
5775    if (itr == _pseudoColors.end()) {
5776        ERROR("PseudoColor not found: %s", id.c_str());
5777        return;
5778    }
5779
5780    do {
5781        itr->second->setEdgeColor(color);
5782    } while (doAll && ++itr != _pseudoColors.end());
5783
5784    _needsRedraw = true;
5785}
5786
5787/**
5788 * \brief Set the polygon edge width for the specified DataSet (may be a no-op)
5789 *
5790 * If the OpenGL implementation/hardware does not support wide lines,
5791 * this function may not have an effect.
5792 */
5793void Renderer::setPseudoColorEdgeWidth(const DataSetId& id, float edgeWidth)
5794{
5795    PseudoColorHashmap::iterator itr;
5796
5797    bool doAll = false;
5798
5799    if (id.compare("all") == 0) {
5800        itr = _pseudoColors.begin();
5801        doAll = true;
5802    } else {
5803        itr = _pseudoColors.find(id);
5804    }
5805
5806    if (itr == _pseudoColors.end()) {
5807        ERROR("PseudoColor not found: %s", id.c_str());
5808        return;
5809    }
5810
5811    do {
5812        itr->second->setEdgeWidth(edgeWidth);
5813    } while (doAll && ++itr != _pseudoColors.end());
5814
5815    _needsRedraw = true;
5816}
5817
5818/**
5819 * \brief Set wireframe rendering for the specified DataSet
5820 */
5821void Renderer::setPseudoColorWireframe(const DataSetId& id, bool state)
5822{
5823    PseudoColorHashmap::iterator itr;
5824
5825    bool doAll = false;
5826
5827    if (id.compare("all") == 0) {
5828        itr = _pseudoColors.begin();
5829        doAll = true;
5830    } else {
5831        itr = _pseudoColors.find(id);
5832    }
5833    if (itr == _pseudoColors.end()) {
5834        ERROR("PseudoColor not found: %s", id.c_str());
5835        return;
5836    }
5837
5838    do {
5839        itr->second->setWireframe(state);
5840    } while (doAll && ++itr != _pseudoColors.end());
5841
5842    _needsRedraw = true;
5843}
5844
5845/**
5846 * \brief Turn mesh lighting on/off for the specified DataSet
5847 */
5848void Renderer::setPseudoColorLighting(const DataSetId& id, bool state)
5849{
5850    PseudoColorHashmap::iterator itr;
5851
5852    bool doAll = false;
5853
5854    if (id.compare("all") == 0) {
5855        itr = _pseudoColors.begin();
5856        doAll = true;
5857    } else {
5858        itr = _pseudoColors.find(id);
5859    }
5860
5861    if (itr == _pseudoColors.end()) {
5862        ERROR("PseudoColor not found: %s", id.c_str());
5863        return;
5864    }
5865
5866    do {
5867        itr->second->setLighting(state);
5868    } while (doAll && ++itr != _pseudoColors.end());
5869
5870    _needsRedraw = true;
5871}
5872
5873/**
5874 * \brief Create a new Streamlines and associate it with the named DataSet
5875 */
5876bool Renderer::addStreamlines(const DataSetId& id)
5877{
5878    DataSetHashmap::iterator itr;
5879
5880    bool doAll = false;
5881
5882    if (id.compare("all") == 0) {
5883        itr = _dataSets.begin();
5884    } else {
5885        itr = _dataSets.find(id);
5886    }
5887    if (itr == _dataSets.end()) {
5888        ERROR("Unknown dataset %s", id.c_str());
5889        return false;
5890    }
5891
5892    do {
5893        DataSet *ds = itr->second;
5894        const DataSetId& dsID = ds->getName();
5895
5896        if (getStreamlines(dsID)) {
5897            WARN("Replacing existing Streamlines %s", dsID.c_str());
5898            deleteStreamlines(dsID);
5899        }
5900
5901        Streamlines *streamlines = new Streamlines();
5902        _streamlines[dsID] = streamlines;
5903
5904        streamlines->setDataSet(ds,
5905                                _useCumulativeRange,
5906                                _cumulativeScalarRange,
5907                                _cumulativeVectorMagnitudeRange,
5908                                _cumulativeVectorComponentRange);
5909
5910        _renderer->AddViewProp(streamlines->getProp());
5911    } while (doAll && ++itr != _dataSets.end());
5912
5913    initCamera();
5914    _needsRedraw = true;
5915    return true;
5916}
5917
5918/**
5919 * \brief Get the Streamlines associated with a named DataSet
5920 */
5921Streamlines *Renderer::getStreamlines(const DataSetId& id)
5922{
5923    StreamlinesHashmap::iterator itr = _streamlines.find(id);
5924
5925    if (itr == _streamlines.end()) {
5926#ifdef DEBUG
5927        TRACE("Streamlines not found: %s", id.c_str());
5928#endif
5929        return NULL;
5930    } else
5931        return itr->second;
5932}
5933
5934/**
5935 * \brief Set the prop orientation with a quaternion
5936 */
5937void Renderer::setStreamlinesTransform(const DataSetId& id, vtkMatrix4x4 *trans)
5938{
5939    StreamlinesHashmap::iterator itr;
5940
5941    bool doAll = false;
5942
5943    if (id.compare("all") == 0) {
5944        itr = _streamlines.begin();
5945        doAll = true;
5946    } else {
5947        itr = _streamlines.find(id);
5948    }
5949    if (itr == _streamlines.end()) {
5950        ERROR("Streamlines not found: %s", id.c_str());
5951        return;
5952    }
5953
5954    do {
5955        itr->second->setTransform(trans);
5956    } while (doAll && ++itr != _streamlines.end());
5957
5958    resetAxes();
5959    _needsRedraw = true;
5960}
5961
5962/**
5963 * \brief Set the prop orientation with a quaternion
5964 */
5965void Renderer::setStreamlinesOrientation(const DataSetId& id, double quat[4])
5966{
5967    StreamlinesHashmap::iterator itr;
5968
5969    bool doAll = false;
5970
5971    if (id.compare("all") == 0) {
5972        itr = _streamlines.begin();
5973        doAll = true;
5974    } else {
5975        itr = _streamlines.find(id);
5976    }
5977    if (itr == _streamlines.end()) {
5978        ERROR("Streamlines not found: %s", id.c_str());
5979        return;
5980    }
5981
5982    do {
5983        itr->second->setOrientation(quat);
5984    } while (doAll && ++itr != _streamlines.end());
5985
5986    resetAxes();
5987    _needsRedraw = true;
5988}
5989
5990/**
5991 * \brief Set the prop orientation with a rotation about an axis
5992 */
5993void Renderer::setStreamlinesOrientation(const DataSetId& id, double angle, double axis[3])
5994{
5995    StreamlinesHashmap::iterator itr;
5996
5997    bool doAll = false;
5998
5999    if (id.compare("all") == 0) {
6000        itr = _streamlines.begin();
6001        doAll = true;
6002    } else {
6003        itr = _streamlines.find(id);
6004    }
6005    if (itr == _streamlines.end()) {
6006        ERROR("Streamlines not found: %s", id.c_str());
6007        return;
6008    }
6009
6010    do {
6011        itr->second->setOrientation(angle, axis);
6012    } while (doAll && ++itr != _streamlines.end());
6013
6014    resetAxes();
6015    _needsRedraw = true;
6016}
6017
6018/**
6019 * \brief Set the prop position in world coords
6020 */
6021void Renderer::setStreamlinesPosition(const DataSetId& id, double pos[3])
6022{
6023    StreamlinesHashmap::iterator itr;
6024
6025    bool doAll = false;
6026
6027    if (id.compare("all") == 0) {
6028        itr = _streamlines.begin();
6029        doAll = true;
6030    } else {
6031        itr = _streamlines.find(id);
6032    }
6033    if (itr == _streamlines.end()) {
6034        ERROR("Streamlines not found: %s", id.c_str());
6035        return;
6036    }
6037
6038    do {
6039        itr->second->setPosition(pos);
6040    } while (doAll && ++itr != _streamlines.end());
6041
6042    resetAxes();
6043    _needsRedraw = true;
6044}
6045
6046/**
6047 * \brief Set the prop scaling
6048 */
6049void Renderer::setStreamlinesScale(const DataSetId& id, double scale[3])
6050{
6051    StreamlinesHashmap::iterator itr;
6052
6053    bool doAll = false;
6054
6055    if (id.compare("all") == 0) {
6056        itr = _streamlines.begin();
6057        doAll = true;
6058    } else {
6059        itr = _streamlines.find(id);
6060    }
6061    if (itr == _streamlines.end()) {
6062        ERROR("Streamlines not found: %s", id.c_str());
6063        return;
6064    }
6065
6066    do {
6067        itr->second->setScale(scale);
6068    } while (doAll && ++itr != _streamlines.end());
6069
6070    resetAxes();
6071    _needsRedraw = true;
6072}
6073
6074/**
6075 * \brief Set the streamlines seed to points distributed randomly inside
6076 * cells of DataSet
6077 */
6078void Renderer::setStreamlinesSeedToRandomPoints(const DataSetId& id, int numPoints)
6079{
6080    StreamlinesHashmap::iterator itr;
6081
6082    bool doAll = false;
6083
6084    if (id.compare("all") == 0) {
6085        itr = _streamlines.begin();
6086        doAll = true;
6087    } else {
6088        itr = _streamlines.find(id);
6089    }
6090    if (itr == _streamlines.end()) {
6091        ERROR("Streamlines not found: %s", id.c_str());
6092        return;
6093    }
6094
6095    do {
6096        itr->second->setSeedToRandomPoints(numPoints);
6097    } while (doAll && ++itr != _streamlines.end());
6098
6099    _needsRedraw = true;
6100}
6101
6102/**
6103 * \brief Set the streamlines seed to points along a line
6104 */
6105void Renderer::setStreamlinesSeedToRake(const DataSetId& id,
6106                                        double start[3], double end[3],
6107                                        int numPoints)
6108{
6109    StreamlinesHashmap::iterator itr;
6110
6111    bool doAll = false;
6112
6113    if (id.compare("all") == 0) {
6114        itr = _streamlines.begin();
6115        doAll = true;
6116    } else {
6117        itr = _streamlines.find(id);
6118    }
6119    if (itr == _streamlines.end()) {
6120        ERROR("Streamlines not found: %s", id.c_str());
6121        return;
6122    }
6123
6124    do {
6125        itr->second->setSeedToRake(start, end, numPoints);
6126    } while (doAll && ++itr != _streamlines.end());
6127
6128    _needsRedraw = true;
6129}
6130
6131/**
6132 * \brief Set the streamlines seed to points inside a disk, with optional
6133 * hole
6134 */
6135void Renderer::setStreamlinesSeedToDisk(const DataSetId& id,
6136                                        double center[3], double normal[3],
6137                                        double radius, double innerRadius,
6138                                        int numPoints)
6139{
6140    StreamlinesHashmap::iterator itr;
6141
6142    bool doAll = false;
6143
6144    if (id.compare("all") == 0) {
6145        itr = _streamlines.begin();
6146        doAll = true;
6147    } else {
6148        itr = _streamlines.find(id);
6149    }
6150    if (itr == _streamlines.end()) {
6151        ERROR("Streamlines not found: %s", id.c_str());
6152        return;
6153    }
6154
6155    do {
6156        itr->second->setSeedToDisk(center, normal, radius, innerRadius, numPoints);
6157    } while (doAll && ++itr != _streamlines.end());
6158
6159    _needsRedraw = true;
6160}
6161
6162/**
6163 * \brief Set the streamlines seed to vertices of an n-sided polygon
6164 */
6165void Renderer::setStreamlinesSeedToPolygon(const DataSetId& id,
6166                                           double center[3], double normal[3],
6167                                           double angle, double radius,
6168                                           int numSides)
6169{
6170    StreamlinesHashmap::iterator itr;
6171
6172    bool doAll = false;
6173
6174    if (id.compare("all") == 0) {
6175        itr = _streamlines.begin();
6176        doAll = true;
6177    } else {
6178        itr = _streamlines.find(id);
6179    }
6180    if (itr == _streamlines.end()) {
6181        ERROR("Streamlines not found: %s", id.c_str());
6182        return;
6183    }
6184
6185    do {
6186        itr->second->setSeedToPolygon(center, normal, angle, radius, numSides);
6187    } while (doAll && ++itr != _streamlines.end());
6188
6189    _needsRedraw = true;
6190}
6191
6192/**
6193 * \brief Set the streamlines seed to vertices of an n-sided polygon
6194 */
6195void Renderer::setStreamlinesSeedToFilledPolygon(const DataSetId& id,
6196                                                 double center[3],
6197                                                 double normal[3],
6198                                                 double angle, double radius,
6199                                                 int numSides, int numPoints)
6200{
6201    StreamlinesHashmap::iterator itr;
6202
6203    bool doAll = false;
6204
6205    if (id.compare("all") == 0) {
6206        itr = _streamlines.begin();
6207        doAll = true;
6208    } else {
6209        itr = _streamlines.find(id);
6210    }
6211    if (itr == _streamlines.end()) {
6212        ERROR("Streamlines not found: %s", id.c_str());
6213        return;
6214    }
6215
6216    do {
6217        itr->second->setSeedToFilledPolygon(center, normal, angle,
6218                                            radius, numSides, numPoints);
6219    } while (doAll && ++itr != _streamlines.end());
6220
6221    _needsRedraw = true;
6222}
6223
6224/**
6225 * \brief Set Streamlines rendering to polylines for the specified DataSet
6226 */
6227void Renderer::setStreamlinesTypeToLines(const DataSetId& id)
6228{
6229    StreamlinesHashmap::iterator itr;
6230
6231    bool doAll = false;
6232
6233    if (id.compare("all") == 0) {
6234        itr = _streamlines.begin();
6235        doAll = true;
6236    } else {
6237        itr = _streamlines.find(id);
6238    }
6239    if (itr == _streamlines.end()) {
6240        ERROR("Streamlines not found: %s", id.c_str());
6241        return;
6242    }
6243
6244    do {
6245        itr->second->setLineTypeToLines();
6246    } while (doAll && ++itr != _streamlines.end());
6247
6248    _needsRedraw = true;
6249}
6250
6251/**
6252 * \brief Set Streamlines rendering to tubes for the specified DataSet
6253 */
6254void Renderer::setStreamlinesTypeToTubes(const DataSetId& id, int numSides, double radius)
6255{
6256    StreamlinesHashmap::iterator itr;
6257
6258    bool doAll = false;
6259
6260    if (id.compare("all") == 0) {
6261        itr = _streamlines.begin();
6262        doAll = true;
6263    } else {
6264        itr = _streamlines.find(id);
6265    }
6266    if (itr == _streamlines.end()) {
6267        ERROR("Streamlines not found: %s", id.c_str());
6268        return;
6269    }
6270
6271    do {
6272        itr->second->setLineTypeToTubes(numSides, radius);
6273    } while (doAll && ++itr != _streamlines.end());
6274
6275    _needsRedraw = true;
6276}
6277
6278/**
6279 * \brief Set Streamlines rendering to ribbons for the specified DataSet
6280 */
6281void Renderer::setStreamlinesTypeToRibbons(const DataSetId& id, double width, double angle)
6282{
6283    StreamlinesHashmap::iterator itr;
6284
6285    bool doAll = false;
6286
6287    if (id.compare("all") == 0) {
6288        itr = _streamlines.begin();
6289        doAll = true;
6290    } else {
6291        itr = _streamlines.find(id);
6292    }
6293    if (itr == _streamlines.end()) {
6294        ERROR("Streamlines not found: %s", id.c_str());
6295        return;
6296    }
6297
6298    do {
6299        itr->second->setLineTypeToRibbons(width, angle);
6300    } while (doAll && ++itr != _streamlines.end());
6301
6302    _needsRedraw = true;
6303}
6304
6305/**
6306 * \brief Set Streamlines maximum length for the specified DataSet
6307 */
6308void Renderer::setStreamlinesLength(const DataSetId& id, double length)
6309{
6310    StreamlinesHashmap::iterator itr;
6311
6312    bool doAll = false;
6313
6314    if (id.compare("all") == 0) {
6315        itr = _streamlines.begin();
6316        doAll = true;
6317    } else {
6318        itr = _streamlines.find(id);
6319    }
6320    if (itr == _streamlines.end()) {
6321        ERROR("Streamlines not found: %s", id.c_str());
6322        return;
6323    }
6324
6325    do {
6326        itr->second->setMaxPropagation(length);
6327    } while (doAll && ++itr != _streamlines.end());
6328
6329    _needsRedraw = true;
6330}
6331
6332/**
6333 * \brief Set Streamlines opacity scaling for the specified DataSet
6334 */
6335void Renderer::setStreamlinesOpacity(const DataSetId& id, double opacity)
6336{
6337    StreamlinesHashmap::iterator itr;
6338
6339    bool doAll = false;
6340
6341    if (id.compare("all") == 0) {
6342        itr = _streamlines.begin();
6343        doAll = true;
6344    } else {
6345        itr = _streamlines.find(id);
6346    }
6347    if (itr == _streamlines.end()) {
6348        ERROR("Streamlines not found: %s", id.c_str());
6349        return;
6350    }
6351
6352    do {
6353        itr->second->setOpacity(opacity);
6354    } while (doAll && ++itr != _streamlines.end());
6355
6356    _needsRedraw = true;
6357}
6358
6359/**
6360 * \brief Turn on/off rendering of the Streamlines mapper for the given DataSet
6361 */
6362void Renderer::setStreamlinesVisibility(const DataSetId& id, bool state)
6363{
6364    StreamlinesHashmap::iterator itr;
6365
6366    bool doAll = false;
6367
6368    if (id.compare("all") == 0) {
6369        itr = _streamlines.begin();
6370        doAll = true;
6371    } else {
6372        itr = _streamlines.find(id);
6373    }
6374    if (itr == _streamlines.end()) {
6375        ERROR("Streamlines not found: %s", id.c_str());
6376        return;
6377    }
6378
6379    do {
6380        itr->second->setVisibility(state);
6381    } while (doAll && ++itr != _streamlines.end());
6382
6383    _needsRedraw = true;
6384}
6385
6386/**
6387 * \brief Turn on/off rendering of the Streamlines seed geometry for the given DataSet
6388 */
6389void Renderer::setStreamlinesSeedVisibility(const DataSetId& id, bool state)
6390{
6391    StreamlinesHashmap::iterator itr;
6392
6393    bool doAll = false;
6394
6395    if (id.compare("all") == 0) {
6396        itr = _streamlines.begin();
6397        doAll = true;
6398    } else {
6399        itr = _streamlines.find(id);
6400    }
6401    if (itr == _streamlines.end()) {
6402        ERROR("Streamlines not found: %s", id.c_str());
6403        return;
6404    }
6405
6406    do {
6407        itr->second->setSeedVisibility(state);
6408    } while (doAll && ++itr != _streamlines.end());
6409
6410    _needsRedraw = true;
6411}
6412
6413/**
6414 * \brief Set the color mode for the specified DataSet
6415 */
6416void Renderer::setStreamlinesColorMode(const DataSetId& id, Streamlines::ColorMode mode)
6417{
6418    StreamlinesHashmap::iterator itr;
6419
6420    bool doAll = false;
6421
6422    if (id.compare("all") == 0) {
6423        itr = _streamlines.begin();
6424        doAll = true;
6425    } else {
6426        itr = _streamlines.find(id);
6427    }
6428    if (itr == _streamlines.end()) {
6429        ERROR("Streamlines not found: %s", id.c_str());
6430        return;
6431    }
6432
6433    do {
6434        itr->second->setColorMode(mode);
6435    } while (doAll && ++itr != _streamlines.end());
6436
6437    _needsRedraw = true;
6438}
6439
6440/**
6441 * \brief Associate an existing named color map with a Streamlines for the given DataSet
6442 */
6443void Renderer::setStreamlinesColorMap(const DataSetId& id, const ColorMapId& colorMapId)
6444{
6445    StreamlinesHashmap::iterator itr;
6446
6447    bool doAll = false;
6448
6449    if (id.compare("all") == 0) {
6450        itr = _streamlines.begin();
6451        doAll = true;
6452    } else {
6453        itr = _streamlines.find(id);
6454    }
6455
6456    if (itr == _streamlines.end()) {
6457        ERROR("Streamlines not found: %s", id.c_str());
6458        return;
6459    }
6460
6461    ColorMap *cmap = getColorMap(colorMapId);
6462    if (cmap == NULL) {
6463        ERROR("Unknown colormap: %s", colorMapId.c_str());
6464        return;
6465    }
6466
6467    do {
6468        TRACE("Set Streamlines color map: %s for dataset %s", colorMapId.c_str(),
6469              itr->second->getDataSet()->getName().c_str());
6470
6471        itr->second->setColorMap(cmap);
6472    } while (doAll && ++itr != _streamlines.end());
6473
6474    _needsRedraw = true;
6475}
6476
6477/**
6478 * \brief Set the RGB line/edge color for the specified DataSet
6479 */
6480void Renderer::setStreamlinesSeedColor(const DataSetId& id, float color[3])
6481{
6482    StreamlinesHashmap::iterator itr;
6483
6484    bool doAll = false;
6485
6486    if (id.compare("all") == 0) {
6487        itr = _streamlines.begin();
6488        doAll = true;
6489    } else {
6490        itr = _streamlines.find(id);
6491    }
6492    if (itr == _streamlines.end()) {
6493        ERROR("Streamlines not found: %s", id.c_str());
6494        return;
6495    }
6496
6497    do {
6498        itr->second->setSeedColor(color);
6499    } while (doAll && ++itr != _streamlines.end());
6500
6501    _needsRedraw = true;
6502}
6503
6504/**
6505 * \brief Set the visibility of polygon edges for the specified DataSet
6506 */
6507void Renderer::setStreamlinesEdgeVisibility(const DataSetId& id, bool state)
6508{
6509    StreamlinesHashmap::iterator itr;
6510
6511    bool doAll = false;
6512
6513    if (id.compare("all") == 0) {
6514        itr = _streamlines.begin();
6515        doAll = true;
6516    } else {
6517        itr = _streamlines.find(id);
6518    }
6519
6520    if (itr == _streamlines.end()) {
6521        ERROR("Streamlines not found: %s", id.c_str());
6522        return;
6523    }
6524
6525    do {
6526        itr->second->setEdgeVisibility(state);
6527    } while (doAll && ++itr != _streamlines.end());
6528
6529    _needsRedraw = true;
6530}
6531
6532/**
6533 * \brief Set the RGB color for the specified DataSet
6534 */
6535void Renderer::setStreamlinesColor(const DataSetId& id, float color[3])
6536{
6537    StreamlinesHashmap::iterator itr;
6538
6539    bool doAll = false;
6540
6541    if (id.compare("all") == 0) {
6542        itr = _streamlines.begin();
6543        doAll = true;
6544    } else {
6545        itr = _streamlines.find(id);
6546    }
6547    if (itr == _streamlines.end()) {
6548        ERROR("Streamlines not found: %s", id.c_str());
6549        return;
6550    }
6551
6552    do {
6553        itr->second->setColor(color);
6554    } while (doAll && ++itr != _streamlines.end());
6555
6556    _needsRedraw = true;
6557}
6558
6559/**
6560 * \brief Set the RGB line/edge color for the specified DataSet
6561 */
6562void Renderer::setStreamlinesEdgeColor(const DataSetId& id, float color[3])
6563{
6564    StreamlinesHashmap::iterator itr;
6565
6566    bool doAll = false;
6567
6568    if (id.compare("all") == 0) {
6569        itr = _streamlines.begin();
6570        doAll = true;
6571    } else {
6572        itr = _streamlines.find(id);
6573    }
6574    if (itr == _streamlines.end()) {
6575        ERROR("Streamlines not found: %s", id.c_str());
6576        return;
6577    }
6578
6579    do {
6580        itr->second->setEdgeColor(color);
6581    } while (doAll && ++itr != _streamlines.end());
6582
6583    _needsRedraw = true;
6584}
6585
6586/**
6587 * \brief Set the line/edge width for the specified DataSet (may be a no-op)
6588 *
6589 * If the OpenGL implementation/hardware does not support wide lines,
6590 * this function may not have an effect.
6591 */
6592void Renderer::setStreamlinesEdgeWidth(const DataSetId& id, float edgeWidth)
6593{
6594    StreamlinesHashmap::iterator itr;
6595
6596    bool doAll = false;
6597
6598    if (id.compare("all") == 0) {
6599        itr = _streamlines.begin();
6600        doAll = true;
6601    } else {
6602        itr = _streamlines.find(id);
6603    }
6604    if (itr == _streamlines.end()) {
6605        ERROR("Streamlines not found: %s", id.c_str());
6606        return;
6607    }
6608
6609    do {
6610        itr->second->setEdgeWidth(edgeWidth);
6611    } while (doAll && ++itr != _streamlines.end());
6612
6613    _needsRedraw = true;
6614}
6615
6616/**
6617 * \brief Turn Streamlines lighting on/off for the specified DataSet
6618 */
6619void Renderer::setStreamlinesLighting(const DataSetId& id, bool state)
6620{
6621    StreamlinesHashmap::iterator itr;
6622
6623    bool doAll = false;
6624
6625    if (id.compare("all") == 0) {
6626        itr = _streamlines.begin();
6627        doAll = true;
6628    } else {
6629        itr = _streamlines.find(id);
6630    }
6631    if (itr == _streamlines.end()) {
6632        ERROR("Streamlines not found: %s", id.c_str());
6633        return;
6634    }
6635
6636    do {
6637        itr->second->setLighting(state);
6638    } while (doAll && ++itr != _streamlines.end());
6639
6640    _needsRedraw = true;
6641}
6642
6643/**
6644 * \brief Create a new Volume and associate it with the named DataSet
6645 */
6646bool Renderer::addVolume(const DataSetId& id)
6647{
6648    DataSetHashmap::iterator itr;
6649
6650    bool doAll = false;
6651
6652    if (id.compare("all") == 0) {
6653        itr = _dataSets.begin();
6654    } else {
6655        itr = _dataSets.find(id);
6656    }
6657    if (itr == _dataSets.end()) {
6658        ERROR("Unknown dataset %s", id.c_str());
6659        return false;
6660    }
6661
6662    do {
6663        DataSet *ds = itr->second;
6664        const DataSetId& dsID = ds->getName();
6665
6666        if (getVolume(dsID)) {
6667            WARN("Replacing existing Volume %s", dsID.c_str());
6668            deleteVolume(dsID);
6669        }
6670
6671        Volume *volume = new Volume();
6672        _volumes[dsID] = volume;
6673
6674        volume->setDataSet(ds,
6675                           _useCumulativeRange,
6676                           _cumulativeScalarRange,
6677                           _cumulativeVectorMagnitudeRange,
6678                           _cumulativeVectorComponentRange );
6679
6680        _renderer->AddViewProp(volume->getProp());
6681    } while (doAll && ++itr != _dataSets.end());
6682
6683    if (_cameraMode == IMAGE)
6684        setCameraMode(PERSPECTIVE);
6685    initCamera();
6686    _needsRedraw = true;
6687    return true;
6688}
6689
6690/**
6691 * \brief Get the Volume associated with a named DataSet
6692 */
6693Volume *Renderer::getVolume(const DataSetId& id)
6694{
6695    VolumeHashmap::iterator itr = _volumes.find(id);
6696
6697    if (itr == _volumes.end()) {
6698#ifdef DEBUG
6699        TRACE("Volume not found: %s", id.c_str());
6700#endif
6701        return NULL;
6702    } else
6703        return itr->second;
6704}
6705
6706/**
6707 * \brief Set the prop orientation with a quaternion
6708 */
6709void Renderer::setVolumeTransform(const DataSetId& id, vtkMatrix4x4 *trans)
6710{
6711    VolumeHashmap::iterator itr;
6712
6713    bool doAll = false;
6714
6715    if (id.compare("all") == 0) {
6716        itr = _volumes.begin();
6717        doAll = true;
6718    } else {
6719        itr = _volumes.find(id);
6720    }
6721    if (itr == _volumes.end()) {
6722        ERROR("Volume not found: %s", id.c_str());
6723        return;
6724    }
6725
6726    do {
6727        itr->second->setTransform(trans);
6728    } while (doAll && ++itr != _volumes.end());
6729
6730    resetAxes();
6731    _needsRedraw = true;
6732}
6733
6734/**
6735 * \brief Set the prop orientation with a quaternion
6736 */
6737void Renderer::setVolumeOrientation(const DataSetId& id, double quat[4])
6738{
6739    VolumeHashmap::iterator itr;
6740
6741    bool doAll = false;
6742
6743    if (id.compare("all") == 0) {
6744        itr = _volumes.begin();
6745        doAll = true;
6746    } else {
6747        itr = _volumes.find(id);
6748    }
6749    if (itr == _volumes.end()) {
6750        ERROR("Volume not found: %s", id.c_str());
6751        return;
6752    }
6753
6754    do {
6755        itr->second->setOrientation(quat);
6756    } while (doAll && ++itr != _volumes.end());
6757
6758    resetAxes();
6759    _needsRedraw = true;
6760}
6761
6762/**
6763 * \brief Set the prop orientation with a rotation about an axis
6764 */
6765void Renderer::setVolumeOrientation(const DataSetId& id, double angle, double axis[3])
6766{
6767    VolumeHashmap::iterator itr;
6768
6769    bool doAll = false;
6770
6771    if (id.compare("all") == 0) {
6772        itr = _volumes.begin();
6773        doAll = true;
6774    } else {
6775        itr = _volumes.find(id);
6776    }
6777    if (itr == _volumes.end()) {
6778        ERROR("Volume not found: %s", id.c_str());
6779        return;
6780    }
6781
6782    do {
6783        itr->second->setOrientation(angle, axis);
6784    } while (doAll && ++itr != _volumes.end());
6785
6786    resetAxes();
6787    _needsRedraw = true;
6788}
6789
6790/**
6791 * \brief Set the prop position in world coords
6792 */
6793void Renderer::setVolumePosition(const DataSetId& id, double pos[3])
6794{
6795    VolumeHashmap::iterator itr;
6796
6797    bool doAll = false;
6798
6799    if (id.compare("all") == 0) {
6800        itr = _volumes.begin();
6801        doAll = true;
6802    } else {
6803        itr = _volumes.find(id);
6804    }
6805    if (itr == _volumes.end()) {
6806        ERROR("Volume not found: %s", id.c_str());
6807        return;
6808    }
6809
6810    do {
6811        itr->second->setPosition(pos);
6812    } while (doAll && ++itr != _volumes.end());
6813
6814    resetAxes();
6815    _needsRedraw = true;
6816}
6817
6818/**
6819 * \brief Set the prop scaling
6820 */
6821void Renderer::setVolumeScale(const DataSetId& id, double scale[3])
6822{
6823    VolumeHashmap::iterator itr;
6824
6825    bool doAll = false;
6826
6827    if (id.compare("all") == 0) {
6828        itr = _volumes.begin();
6829        doAll = true;
6830    } else {
6831        itr = _volumes.find(id);
6832    }
6833    if (itr == _volumes.end()) {
6834        ERROR("Volume not found: %s", id.c_str());
6835        return;
6836    }
6837
6838    do {
6839        itr->second->setScale(scale);
6840    } while (doAll && ++itr != _volumes.end());
6841
6842    resetAxes();
6843    _needsRedraw = true;
6844}
6845
6846/**
6847 * \brief Associate an existing named color map with a Volume for the given DataSet
6848 */
6849void Renderer::setVolumeColorMap(const DataSetId& id, const ColorMapId& colorMapId)
6850{
6851    VolumeHashmap::iterator itr;
6852
6853    bool doAll = false;
6854
6855    if (id.compare("all") == 0) {
6856        itr = _volumes.begin();
6857        doAll = true;
6858    } else {
6859        itr = _volumes.find(id);
6860    }
6861
6862    if (itr == _volumes.end()) {
6863        ERROR("Volume not found: %s", id.c_str());
6864        return;
6865    }
6866
6867    ColorMap *cmap = getColorMap(colorMapId);
6868    if (cmap == NULL) {
6869        ERROR("Unknown colormap: %s", colorMapId.c_str());
6870        return;
6871    }
6872
6873    do {
6874        TRACE("Set Volume color map: %s for dataset %s", colorMapId.c_str(),
6875              itr->second->getDataSet()->getName().c_str());
6876
6877        itr->second->setColorMap(cmap);
6878    } while (doAll && ++itr != _volumes.end());
6879
6880    _needsRedraw = true;
6881}
6882
6883/**
6884 * \brief Set Volume opacity scaling for the specified DataSet
6885 */
6886void Renderer::setVolumeOpacity(const DataSetId& id, double opacity)
6887{
6888    VolumeHashmap::iterator itr;
6889
6890    bool doAll = false;
6891
6892    if (id.compare("all") == 0) {
6893        itr = _volumes.begin();
6894        doAll = true;
6895    } else {
6896        itr = _volumes.find(id);
6897    }
6898    if (itr == _volumes.end()) {
6899        ERROR("Volume not found: %s", id.c_str());
6900        return;
6901    }
6902
6903    do {
6904        itr->second->setOpacity(opacity);
6905    } while (doAll && ++itr != _volumes.end());
6906
6907    _needsRedraw = true;
6908}
6909
6910/**
6911 * \brief Turn on/off rendering of the Volume mapper for the given DataSet
6912 */
6913void Renderer::setVolumeVisibility(const DataSetId& id, bool state)
6914{
6915    VolumeHashmap::iterator itr;
6916
6917    bool doAll = false;
6918
6919    if (id.compare("all") == 0) {
6920        itr = _volumes.begin();
6921        doAll = true;
6922    } else {
6923        itr = _volumes.find(id);
6924    }
6925    if (itr == _volumes.end()) {
6926        ERROR("Volume not found: %s", id.c_str());
6927        return;
6928    }
6929
6930    do {
6931        itr->second->setVisibility(state);
6932    } while (doAll && ++itr != _volumes.end());
6933
6934    _needsRedraw = true;
6935}
6936
6937/**
6938 * \brief Set volume ambient lighting/shading coefficient for the specified DataSet
6939 */
6940void Renderer::setVolumeAmbient(const DataSetId& id, double coeff)
6941{
6942    VolumeHashmap::iterator itr;
6943
6944    bool doAll = false;
6945
6946    if (id.compare("all") == 0) {
6947        itr = _volumes.begin();
6948        doAll = true;
6949    } else {
6950        itr = _volumes.find(id);
6951    }
6952    if (itr == _volumes.end()) {
6953        ERROR("Volume not found: %s", id.c_str());
6954        return;
6955    }
6956
6957    do {
6958        itr->second->setAmbient(coeff);
6959    } while (doAll && ++itr != _volumes.end());
6960
6961    _needsRedraw = true;
6962}
6963
6964/**
6965 * \brief Set volume diffuse lighting/shading coefficient for the specified DataSet
6966 */
6967void Renderer::setVolumeDiffuse(const DataSetId& id, double coeff)
6968{
6969    VolumeHashmap::iterator itr;
6970
6971    bool doAll = false;
6972
6973    if (id.compare("all") == 0) {
6974        itr = _volumes.begin();
6975        doAll = true;
6976    } else {
6977        itr = _volumes.find(id);
6978    }
6979    if (itr == _volumes.end()) {
6980        ERROR("Volume not found: %s", id.c_str());
6981        return;
6982    }
6983
6984    do {
6985        itr->second->setDiffuse(coeff);
6986    } while (doAll && ++itr != _volumes.end());
6987
6988    _needsRedraw = true;
6989}
6990
6991/**
6992 * \brief Set volume specular lighting/shading coefficient and power for the specified DataSet
6993 */
6994void Renderer::setVolumeSpecular(const DataSetId& id, double coeff, double power)
6995{
6996    VolumeHashmap::iterator itr;
6997
6998    bool doAll = false;
6999
7000    if (id.compare("all") == 0) {
7001        itr = _volumes.begin();
7002        doAll = true;
7003    } else {
7004        itr = _volumes.find(id);
7005    }
7006    if (itr == _volumes.end()) {
7007        ERROR("Volume not found: %s", id.c_str());
7008        return;
7009    }
7010
7011    do {
7012        itr->second->setSpecular(coeff, power);
7013    } while (doAll && ++itr != _volumes.end());
7014
7015    _needsRedraw = true;
7016}
7017
7018/**
7019 * \brief Turn volume lighting/shading on/off for the specified DataSet
7020 */
7021void Renderer::setVolumeLighting(const DataSetId& id, bool state)
7022{
7023    VolumeHashmap::iterator itr;
7024
7025    bool doAll = false;
7026
7027    if (id.compare("all") == 0) {
7028        itr = _volumes.begin();
7029        doAll = true;
7030    } else {
7031        itr = _volumes.find(id);
7032    }
7033    if (itr == _volumes.end()) {
7034        ERROR("Volume not found: %s", id.c_str());
7035        return;
7036    }
7037
7038    do {
7039        itr->second->setLighting(state);
7040    } while (doAll && ++itr != _volumes.end());
7041
7042    _needsRedraw = true;
7043}
7044
7045/**
7046 * \brief Set camera FOV based on render window height
7047 *
7048 * Computes a field-of-view angle based on some assumptions about
7049 * viewer's distance to screen and pixel density
7050 */
7051void Renderer::setViewAngle(int height)
7052{
7053    // Distance of eyes from screen in inches
7054    double d = 20.0;
7055    // Assume 72 ppi screen
7056    double h = (double)height / 72.0;
7057
7058    double angle = vtkMath::DegreesFromRadians(2.0 * atan((h/2.0)/d));
7059    _renderer->GetActiveCamera()->SetViewAngle(angle);
7060
7061    TRACE("Setting view angle: %g", angle);
7062}
7063
7064/**
7065 * \brief Resize the render window (image size for renderings)
7066 */
7067void Renderer::setWindowSize(int width, int height)
7068{
7069    if (_windowWidth == width &&
7070        _windowHeight == height)
7071        return;
7072
7073    //setViewAngle(height);
7074
7075    // FIXME: Fix up panning on aspect change
7076#ifdef notdef
7077    if (_cameraPan[0] != 0.0) {
7078        _cameraPan[0] *= ((double)_windowWidth / width);
7079    }
7080    if (_cameraPan[1] != 0.0) {
7081        _cameraPan[1] *= ((double)_windowHeight / height);
7082    }
7083#endif
7084    _windowWidth = width;
7085    _windowHeight = height;
7086    _renderWindow->SetSize(_windowWidth, _windowHeight);
7087    if (_cameraMode == IMAGE) {
7088        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
7089                            _imgWorldDims[0], _imgWorldDims[1]);
7090    }
7091    _needsRedraw = true;
7092}
7093
7094/**
7095 * \brief Change the camera type: perspective, orthographic or image view
7096 *
7097 * Perspective mode is a normal 3D camera.
7098 *
7099 * Orthogrphic mode is parallel projection.
7100 *
7101 * Image mode is an orthographic camera with fixed axes and a clipping region
7102 * around the plot area, use setCameraZoomRegion to control the displayed area
7103 *
7104 * \param[in] mode Enum specifying camera type
7105 */
7106void Renderer::setCameraMode(CameraMode mode)
7107{
7108    if (_cameraMode == mode) return;
7109
7110    CameraMode origMode = _cameraMode;
7111    _cameraMode = mode;
7112    resetAxes();
7113
7114    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
7115    switch (mode) {
7116    case ORTHO: {
7117        TRACE("Set camera to Ortho mode");
7118        camera->ParallelProjectionOn();
7119        if (origMode == IMAGE) {
7120            resetCamera(true);
7121        }
7122        break;
7123    }
7124    case PERSPECTIVE: {
7125        TRACE("Set camera to Perspective mode");
7126        camera->ParallelProjectionOff();
7127        if (origMode == IMAGE) {
7128            resetCamera(true);
7129        }
7130        break;
7131    }
7132    case IMAGE: {
7133        camera->ParallelProjectionOn();
7134        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
7135                            _imgWorldDims[0],_imgWorldDims[1]);
7136        TRACE("Set camera to Image mode");
7137        break;
7138    }
7139    default:
7140        ERROR("Unkown camera mode: %d", mode);
7141    }
7142    _needsRedraw = true;
7143}
7144
7145/**
7146 * \brief Get the current camera mode
7147 */
7148Renderer::CameraMode Renderer::getCameraMode() const
7149{
7150    return _cameraMode;
7151}
7152
7153/**
7154 * \brief Set the VTK camera parameters based on a 4x4 view matrix
7155 */
7156void Renderer::setCameraFromMatrix(vtkCamera *camera, vtkMatrix4x4& mat)
7157{
7158    double d = camera->GetDistance();
7159    double vu[3];
7160    vu[0] = mat[1][0];
7161    vu[1] = mat[1][1];
7162    vu[2] = mat[1][2];
7163    double trans[3];
7164    trans[0] = mat[0][3];
7165    trans[1] = mat[1][3];
7166    trans[2] = mat[2][3];
7167    mat[0][3] = 0;
7168    mat[1][3] = 0;
7169    mat[2][3] = 0;
7170    double vpn[3] = {mat[2][0], mat[2][1], mat[2][2]};
7171    double pos[3];
7172    // With translation removed, we have an orthogonal matrix,
7173    // so the inverse is the transpose
7174    mat.Transpose();
7175    mat.MultiplyPoint(trans, pos);
7176    pos[0] = -pos[0];
7177    pos[1] = -pos[1];
7178    pos[2] = -pos[2];
7179    double fp[3];
7180    fp[0] = pos[0] - vpn[0] * d;
7181    fp[1] = pos[1] - vpn[1] * d;
7182    fp[2] = pos[2] - vpn[2] * d;
7183    camera->SetPosition(pos);
7184    camera->SetFocalPoint(fp);
7185    camera->SetViewUp(vu);
7186}
7187
7188inline void quaternionToMatrix4x4(const double quat[4], vtkMatrix4x4& mat)
7189{
7190    double ww = quat[0]*quat[0];
7191    double wx = quat[0]*quat[1];
7192    double wy = quat[0]*quat[2];
7193    double wz = quat[0]*quat[3];
7194
7195    double xx = quat[1]*quat[1];
7196    double yy = quat[2]*quat[2];
7197    double zz = quat[3]*quat[3];
7198
7199    double xy = quat[1]*quat[2];
7200    double xz = quat[1]*quat[3];
7201    double yz = quat[2]*quat[3];
7202
7203    double rr = xx + yy + zz;
7204    // normalization factor, just in case quaternion was not normalized
7205    double f = double(1)/double(sqrt(ww + rr));
7206    double s = (ww - rr)*f;
7207    f *= 2;
7208
7209    mat[0][0] = xx*f + s;
7210    mat[1][0] = (xy + wz)*f;
7211    mat[2][0] = (xz - wy)*f;
7212
7213    mat[0][1] = (xy - wz)*f;
7214    mat[1][1] = yy*f + s;
7215    mat[2][1] = (yz + wx)*f;
7216
7217    mat[0][2] = (xz + wy)*f;
7218    mat[1][2] = (yz - wx)*f;
7219    mat[2][2] = zz*f + s;
7220}
7221
7222inline void quaternionToTransposeMatrix4x4(const double quat[4], vtkMatrix4x4& mat)
7223{
7224    double ww = quat[0]*quat[0];
7225    double wx = quat[0]*quat[1];
7226    double wy = quat[0]*quat[2];
7227    double wz = quat[0]*quat[3];
7228
7229    double xx = quat[1]*quat[1];
7230    double yy = quat[2]*quat[2];
7231    double zz = quat[3]*quat[3];
7232
7233    double xy = quat[1]*quat[2];
7234    double xz = quat[1]*quat[3];
7235    double yz = quat[2]*quat[3];
7236
7237    double rr = xx + yy + zz;
7238    // normalization factor, just in case quaternion was not normalized
7239    double f = double(1)/double(sqrt(ww + rr));
7240    double s = (ww - rr)*f;
7241    f *= 2;
7242
7243    mat[0][0] = xx*f + s;
7244    mat[0][1] = (xy + wz)*f;
7245    mat[0][2] = (xz - wy)*f;
7246
7247    mat[1][0] = (xy - wz)*f;
7248    mat[1][1] = yy*f + s;
7249    mat[1][2] = (yz + wx)*f;
7250
7251    mat[2][0] = (xz + wy)*f;
7252    mat[2][1] = (yz - wx)*f;
7253    mat[2][2] = zz*f + s;
7254}
7255
7256inline double *quatMult(const double q1[4], const double q2[4], double result[4])
7257{
7258    double q1w = q1[0];
7259    double q1x = q1[1];
7260    double q1y = q1[2];
7261    double q1z = q1[3];
7262    double q2w = q2[0];
7263    double q2x = q2[1];
7264    double q2y = q2[2];
7265    double q2z = q2[3];
7266    result[0] = (q1w*q2w) - (q1x*q2x) - (q1y*q2y) - (q1z*q2z);
7267    result[1] = (q1w*q2x) + (q1x*q2w) + (q1y*q2z) - (q1z*q2y);
7268    result[2] = (q1w*q2y) + (q1y*q2w) + (q1z*q2x) - (q1x*q2z);
7269    result[3] = (q1w*q2z) + (q1z*q2w) + (q1x*q2y) - (q1y*q2x);
7270    return result;
7271}
7272
7273inline double *quatConjugate(const double quat[4], double result[4])
7274{
7275    result[1] = -quat[1];
7276    result[2] = -quat[2];
7277    result[3] = -quat[3];
7278    return result;
7279}
7280
7281inline double *quatReciprocal(const double quat[4], double result[4])
7282{
7283    double denom =
7284        quat[0]*quat[0] +
7285        quat[1]*quat[1] +
7286        quat[2]*quat[2] +
7287        quat[3]*quat[3];
7288    quatConjugate(quat, result);
7289    for (int i = 0; i < 4; i++) {
7290        result[i] /= denom;
7291    }
7292    return result;
7293}
7294
7295inline double *quatReciprocal(double quat[4])
7296{
7297    return quatReciprocal(quat, quat);
7298}
7299
7300inline void copyQuat(const double quat[4], double result[4])
7301{
7302    memcpy(result, quat, sizeof(double)*4);
7303}
7304
7305/**
7306 * \brief Set the orientation of the camera from a quaternion
7307 * rotation
7308 *
7309 * \param[in] quat A quaternion with scalar part first: w,x,y,z
7310 * \param[in] absolute Is rotation absolute or relative?
7311 */
7312void Renderer::setCameraOrientation(const double quat[4], bool absolute)
7313{
7314    if (_cameraMode == IMAGE)
7315        return;
7316    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
7317    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
7318    vtkSmartPointer<vtkMatrix4x4> rotMat = vtkSmartPointer<vtkMatrix4x4>::New();
7319
7320    double q[4];
7321    copyQuat(quat, q);
7322
7323    if (absolute) {
7324        double abs[4];
7325        // Save absolute rotation
7326        copyQuat(q, abs);
7327        // Compute relative rotation
7328        quatMult(quatReciprocal(_cameraOrientation), q, q);
7329        // Store absolute rotation
7330        copyQuat(abs, _cameraOrientation);
7331    } else {
7332        // Compute new absolute rotation
7333        quatMult(_cameraOrientation, q, _cameraOrientation);
7334    }
7335
7336    quaternionToTransposeMatrix4x4(q, *rotMat);
7337#ifdef DEBUG
7338    TRACE("Rotation matrix:\n %g %g %g\n %g %g %g\n %g %g %g",
7339          (*rotMat)[0][0], (*rotMat)[0][1], (*rotMat)[0][2],
7340          (*rotMat)[1][0], (*rotMat)[1][1], (*rotMat)[1][2],
7341          (*rotMat)[2][0], (*rotMat)[2][1], (*rotMat)[2][2]);
7342    vtkSmartPointer<vtkMatrix4x4> camMat = vtkSmartPointer<vtkMatrix4x4>::New();
7343    camMat->DeepCopy(camera->GetViewTransformMatrix());
7344    TRACE("Camera matrix:\n %g %g %g %g\n %g %g %g %g\n %g %g %g %g\n %g %g %g %g",
7345          (*camMat)[0][0], (*camMat)[0][1], (*camMat)[0][2], (*camMat)[0][3],
7346          (*camMat)[1][0], (*camMat)[1][1], (*camMat)[1][2], (*camMat)[1][3],
7347          (*camMat)[2][0], (*camMat)[2][1], (*camMat)[2][2], (*camMat)[2][3],
7348          (*camMat)[3][0], (*camMat)[3][1], (*camMat)[3][2], (*camMat)[3][3]);
7349    printCameraInfo(camera);
7350#endif
7351    trans->Translate(0, 0, -camera->GetDistance());
7352    trans->Concatenate(rotMat);
7353    trans->Translate(0, 0, camera->GetDistance());
7354    trans->Concatenate(camera->GetViewTransformMatrix());
7355    setCameraFromMatrix(camera, *trans->GetMatrix());
7356
7357    _renderer->ResetCameraClippingRange();
7358    printCameraInfo(camera);
7359    _needsRedraw = true;
7360}
7361
7362/**
7363 * \brief Set the position and orientation of the camera
7364 *
7365 * \param[in] position x,y,z position of camera in world coordinates
7366 * \param[in] focalPoint x,y,z look-at point in world coordinates
7367 * \param[in] viewUp x,y,z up vector of camera
7368 */
7369void Renderer::setCameraOrientationAndPosition(const double position[3],
7370                                               const double focalPoint[3],
7371                                               const double viewUp[3])
7372{
7373    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
7374    camera->SetPosition(position);
7375    camera->SetFocalPoint(focalPoint);
7376    camera->SetViewUp(viewUp);
7377    _renderer->ResetCameraClippingRange();
7378    _needsRedraw = true;
7379}
7380
7381/**
7382 * \brief Get the position and orientation of the camera
7383 *
7384 * \param[out] position x,y,z position of camera in world coordinates
7385 * \param[out] focalPoint x,y,z look-at point in world coordinates
7386 * \param[out] viewUp x,y,z up vector of camera
7387 */
7388void Renderer::getCameraOrientationAndPosition(double position[3],
7389                                               double focalPoint[3],
7390                                               double viewUp[3])
7391{
7392    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
7393    camera->GetPosition(position);
7394    camera->GetFocalPoint(focalPoint);
7395    camera->GetViewUp(viewUp);
7396}
7397
7398/**
7399 * \brief Reset pan, zoom, clipping planes and optionally rotation
7400 *
7401 * \param[in] resetOrientation Reset the camera rotation/orientation also
7402 */
7403void Renderer::resetCamera(bool resetOrientation)
7404{
7405    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
7406    if (_cameraMode == IMAGE) {
7407        initCamera();
7408    } else {
7409        if (resetOrientation) {
7410            camera->SetPosition(0, 0, 1);
7411            camera->SetFocalPoint(0, 0, 0);
7412            camera->SetViewUp(0, 1, 0);
7413            _cameraOrientation[0] = 1.0;
7414            _cameraOrientation[1] = 0.0;
7415            _cameraOrientation[2] = 0.0;
7416            _cameraOrientation[3] = 0.0;
7417        }
7418        setViewAngle(_windowHeight);
7419        double bounds[6];
7420        collectBounds(bounds, false);
7421        _renderer->ResetCamera(bounds);
7422        _renderer->ResetCameraClippingRange();
7423        //computeScreenWorldCoords();
7424    }
7425
7426    printCameraInfo(camera);
7427
7428    _cameraZoomRatio = 1;
7429    _cameraPan[0] = 0;
7430    _cameraPan[1] = 0;
7431
7432    _needsRedraw = true;
7433}
7434
7435void Renderer::resetCameraClippingRange()
7436{
7437    _renderer->ResetCameraClippingRange();
7438}
7439
7440/**
7441 * \brief Perform a relative rotation to current camera orientation
7442 *
7443 * Angles are in degrees, rotation is about focal point
7444 */
7445void Renderer::rotateCamera(double yaw, double pitch, double roll)
7446{
7447    if (_cameraMode == IMAGE)
7448        return;
7449    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
7450    camera->Azimuth(yaw); // Rotate about object
7451    //camera->SetYaw(yaw); // Rotate about camera
7452    camera->Elevation(pitch); // Rotate about object
7453    //camera->SetPitch(pitch); // Rotate about camera
7454    camera->Roll(roll); // Roll about camera view axis
7455    _renderer->ResetCameraClippingRange();
7456    //computeScreenWorldCoords();
7457    _needsRedraw = true;
7458}
7459
7460/**
7461 * \brief Perform a 2D translation of the camera
7462 *
7463 * x,y pan amount are specified as signed absolute pan amount in viewport
7464 * units -- i.e. 0 is no pan, .5 is half the viewport, 2 is twice the viewport,
7465 * etc.
7466 *
7467 * \param[in] x Viewport coordinate horizontal panning (positive number pans
7468 * camera left, object right)
7469 * \param[in] y Viewport coordinate vertical panning (positive number pans
7470 * camera up, object down)
7471 * \param[in] absolute Control if pan amount is relative to current or absolute
7472 */
7473void Renderer::panCamera(double x, double y, bool absolute)
7474{
7475    TRACE("Enter panCamera: %g %g, current abs: %g %g",
7476          x, y, _cameraPan[0], _cameraPan[1]);
7477
7478    if (_cameraMode == IMAGE) {
7479        // Reverse x rather than y, since we are panning the camera, and client
7480        // expects to be panning/moving the object
7481        x = -x * _screenWorldCoords[2];
7482        y = y * _screenWorldCoords[3];
7483
7484        if (absolute) {
7485            double panAbs[2];
7486            panAbs[0] = x;
7487            panAbs[1] = y;
7488            x -= _cameraPan[0];
7489            y -= _cameraPan[1];
7490            _cameraPan[0] = panAbs[0];
7491            _cameraPan[1] = panAbs[1];
7492        } else {
7493            _cameraPan[0] += x;
7494            _cameraPan[1] += y;
7495        }
7496
7497        _imgWorldOrigin[0] += x;
7498        _imgWorldOrigin[1] += y;
7499        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
7500                            _imgWorldDims[0], _imgWorldDims[1]);
7501    } else {
7502        y = -y;
7503        if (absolute) {
7504            double panAbs[2];
7505            panAbs[0] = x;
7506            panAbs[1] = y;
7507            x -= _cameraPan[0];
7508            y -= _cameraPan[1];
7509            _cameraPan[0] = panAbs[0];
7510            _cameraPan[1] = panAbs[1];
7511        } else {
7512            _cameraPan[0] += x;
7513            _cameraPan[1] += y;
7514        }
7515
7516        if (x != 0.0 || y != 0.0) {
7517            vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
7518            double viewFocus[4], focalDepth, viewPoint[3];
7519            double newPickPoint[4], oldPickPoint[4], motionVector[3];
7520
7521            camera->GetFocalPoint(viewFocus);
7522            computeWorldToDisplay(viewFocus[0], viewFocus[1], viewFocus[2],
7523                                  viewFocus);
7524            focalDepth = viewFocus[2];
7525
7526            computeDisplayToWorld(( x * 2. + 1.) * _windowWidth / 2.0,
7527                                  ( y * 2. + 1.) * _windowHeight / 2.0,
7528                                  focalDepth,
7529                                  newPickPoint);
7530
7531            computeDisplayToWorld(_windowWidth / 2.0,
7532                                  _windowHeight / 2.0,
7533                                  focalDepth,
7534                                  oldPickPoint);
7535
7536            // Camera motion is reversed
7537            motionVector[0] = oldPickPoint[0] - newPickPoint[0];
7538            motionVector[1] = oldPickPoint[1] - newPickPoint[1];
7539            motionVector[2] = oldPickPoint[2] - newPickPoint[2];
7540
7541            camera->GetFocalPoint(viewFocus);
7542            camera->GetPosition(viewPoint);
7543            camera->SetFocalPoint(motionVector[0] + viewFocus[0],
7544                                  motionVector[1] + viewFocus[1],
7545                                  motionVector[2] + viewFocus[2]);
7546
7547            camera->SetPosition(motionVector[0] + viewPoint[0],
7548                                motionVector[1] + viewPoint[1],
7549                                motionVector[2] + viewPoint[2]);
7550
7551            _renderer->ResetCameraClippingRange();
7552            //computeScreenWorldCoords();
7553        }
7554    }
7555
7556    TRACE("Leave panCamera: %g %g, current abs: %g %g",
7557          x, y, _cameraPan[0], _cameraPan[1]);
7558
7559    _needsRedraw = true;
7560}
7561
7562/**
7563 * \brief Dolly camera or set orthographic scaling based on camera type
7564 *
7565 * \param[in] z Ratio to change zoom (greater than 1 is zoom in, less than 1 is zoom out)
7566 * \param[in] absolute Control if zoom factor is relative to current setting or absolute
7567 */
7568void Renderer::zoomCamera(double z, bool absolute)
7569{
7570    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
7571    TRACE("Enter Zoom: current abs: %g, z: %g, view angle %g",
7572          _cameraZoomRatio, z, camera->GetViewAngle());
7573
7574    if (absolute) {
7575        assert(_cameraZoomRatio > 0.0);
7576        double zAbs = z;
7577        z *= 1.0/_cameraZoomRatio;
7578        _cameraZoomRatio = zAbs;
7579    } else {
7580        _cameraZoomRatio *= z;
7581    }
7582
7583    if (_cameraMode == IMAGE) {
7584        double dx = _imgWorldDims[0];
7585        double dy = _imgWorldDims[1];
7586        _imgWorldDims[0] /= z;
7587        _imgWorldDims[1] /= z;
7588        dx -= _imgWorldDims[0];
7589        dy -= _imgWorldDims[1];
7590        _imgWorldOrigin[0] += dx/2.0;
7591        _imgWorldOrigin[1] += dy/2.0;
7592        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
7593                            _imgWorldDims[0], _imgWorldDims[1]);
7594    } else {
7595        // Keep ortho and perspective modes in sync
7596        // Move camera forward/back for perspective camera
7597        camera->Dolly(z);
7598        // Change ortho parallel scale
7599        camera->SetParallelScale(camera->GetParallelScale()/z);
7600        _renderer->ResetCameraClippingRange();
7601        //computeScreenWorldCoords();
7602    }
7603
7604    TRACE("Leave Zoom: rel %g, new abs: %g, view angle %g",
7605          z, _cameraZoomRatio, camera->GetViewAngle());
7606
7607    _needsRedraw = true;
7608}
7609
7610/**
7611 * \brief Set the pan/zoom using a corner and dimensions in pixel coordinates
7612 *
7613 * \param[in] x left pixel coordinate
7614 * \param[in] y bottom  pixel coordinate (with y=0 at top of window)
7615 * \param[in] width Width of zoom region in pixel coordinates
7616 * \param[in] height Height of zoom region in pixel coordinates
7617 */
7618void Renderer::setCameraZoomRegionPixels(int x, int y, int width, int height)
7619{
7620    double wx, wy, ww, wh;
7621
7622    y = _windowHeight - y;
7623    double pxToWorldX = _screenWorldCoords[2] / (double)_windowWidth;
7624    double pxToWorldY = _screenWorldCoords[3] / (double)_windowHeight;
7625
7626    wx = _screenWorldCoords[0] + x * pxToWorldX;
7627    wy = _screenWorldCoords[1] + y * pxToWorldY;
7628    ww = abs(width) *  pxToWorldX;
7629    wh = abs(height) * pxToWorldY;
7630    setCameraZoomRegion(wx, wy, ww, wh);
7631
7632    TRACE("\npx: %d %d %d %d\nworld: %g %g %g %g",
7633          x, y, width, height,
7634          wx, wy, ww, wh);
7635}
7636
7637/**
7638 * \brief Set the pan/zoom using a corner and dimensions in world coordinates
7639 *
7640 * \param[in] x left world coordinate
7641 * \param[in] y bottom  world coordinate
7642 * \param[in] width Width of zoom region in world coordinates
7643 * \param[in] height Height of zoom region in world coordinates
7644 */
7645void Renderer::setCameraZoomRegion(double x, double y, double width, double height)
7646{
7647    double camPos[2];
7648
7649    int pxOffsetX = (int)(0.17 * (double)_windowWidth);
7650    pxOffsetX = (pxOffsetX > 100 ? 100 : pxOffsetX);
7651    int pxOffsetY = (int)(0.15 * (double)_windowHeight);
7652    pxOffsetY = (pxOffsetY > 75 ? 75 : pxOffsetY);
7653    int outerGutter = (int)(0.03 * (double)_windowWidth);
7654    outerGutter = (outerGutter > 15 ? 15 : outerGutter);
7655
7656    int imgHeightPx = _windowHeight - pxOffsetY - outerGutter;
7657    int imgWidthPx = _windowWidth - pxOffsetX - outerGutter;
7658
7659    double imgAspect = width / height;
7660    double winAspect = (double)_windowWidth / _windowHeight;
7661
7662    double pxToWorld;
7663
7664    if (imgAspect >= winAspect) {
7665        pxToWorld = width / imgWidthPx;
7666    } else {
7667        pxToWorld = height / imgHeightPx;
7668    }
7669
7670    double offsetX = pxOffsetX * pxToWorld;
7671    double offsetY = pxOffsetY * pxToWorld;
7672
7673    TRACE("Window: %d %d", _windowWidth, _windowHeight);
7674    TRACE("ZoomRegion: %g %g %g %g", x, y, width, height);
7675    TRACE("pxToWorld: %g", pxToWorld);
7676    TRACE("offset: %g %g", offsetX, offsetY);
7677
7678    setCameraMode(IMAGE);
7679
7680    _imgWorldOrigin[0] = x;
7681    _imgWorldOrigin[1] = y;
7682    _imgWorldDims[0] = width;
7683    _imgWorldDims[1] = height;
7684
7685    camPos[0] = _imgWorldOrigin[0] - offsetX + (_windowWidth * pxToWorld)/2.0;
7686    camPos[1] = _imgWorldOrigin[1] - offsetY + (_windowHeight * pxToWorld)/2.0;
7687
7688    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
7689    camera->ParallelProjectionOn();
7690    camera->SetClippingRange(1, 2);
7691    // Half of world coordinate height of viewport (Documentation is wrong)
7692    camera->SetParallelScale(_windowHeight * pxToWorld / 2.0);
7693
7694    if (_imgCameraPlane == PLANE_XY) {
7695        // XY plane
7696        camera->SetPosition(camPos[0], camPos[1], _imgCameraOffset + 1.);
7697        camera->SetFocalPoint(camPos[0], camPos[1], _imgCameraOffset);
7698        camera->SetViewUp(0, 1, 0);
7699        // bottom
7700        _cameraClipPlanes[0]->SetOrigin(0, _imgWorldOrigin[1], 0);
7701        _cameraClipPlanes[0]->SetNormal(0, 1, 0);
7702        // left
7703        _cameraClipPlanes[1]->SetOrigin(_imgWorldOrigin[0], 0, 0);
7704        _cameraClipPlanes[1]->SetNormal(1, 0, 0);
7705        // top
7706        _cameraClipPlanes[2]->SetOrigin(0, _imgWorldOrigin[1] + _imgWorldDims[1], 0);
7707        _cameraClipPlanes[2]->SetNormal(0, -1, 0);
7708        // right
7709        _cameraClipPlanes[3]->SetOrigin(_imgWorldOrigin[0] + _imgWorldDims[0], 0, 0);
7710        _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
7711        _cubeAxesActor2D->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWorldDims[0],
7712                                    _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWorldDims[1],
7713                                    _imgCameraOffset, _imgCameraOffset);
7714        _cubeAxesActor2D->XAxisVisibilityOn();
7715        _cubeAxesActor2D->YAxisVisibilityOn();
7716        _cubeAxesActor2D->ZAxisVisibilityOff();
7717    } else if (_imgCameraPlane == PLANE_ZY) {
7718        // ZY plane
7719        camera->SetPosition(_imgCameraOffset - 1., camPos[1], camPos[0]);
7720        camera->SetFocalPoint(_imgCameraOffset, camPos[1], camPos[0]);
7721        camera->SetViewUp(0, 1, 0);
7722        // bottom
7723        _cameraClipPlanes[0]->SetOrigin(0, _imgWorldOrigin[1], 0);
7724        _cameraClipPlanes[0]->SetNormal(0, 1, 0);
7725        // left
7726        _cameraClipPlanes[1]->SetOrigin(0, 0, _imgWorldOrigin[0]);
7727        _cameraClipPlanes[1]->SetNormal(0, 0, 1);
7728        // top
7729        _cameraClipPlanes[2]->SetOrigin(0, _imgWorldOrigin[1] + _imgWorldDims[1], 0);
7730        _cameraClipPlanes[2]->SetNormal(0, -1, 0);
7731        // right
7732        _cameraClipPlanes[3]->SetOrigin(0, 0, _imgWorldOrigin[0] + _imgWorldDims[0]);
7733        _cameraClipPlanes[3]->SetNormal(0, 0, -1);
7734        _cubeAxesActor2D->SetBounds(_imgCameraOffset, _imgCameraOffset,
7735                                    _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWorldDims[1],
7736                                    _imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWorldDims[0]);
7737        _cubeAxesActor2D->XAxisVisibilityOff();
7738        _cubeAxesActor2D->YAxisVisibilityOn();
7739        _cubeAxesActor2D->ZAxisVisibilityOn();
7740    } else {
7741        // XZ plane
7742        camera->SetPosition(camPos[0], _imgCameraOffset - 1., camPos[1]);
7743        camera->SetFocalPoint(camPos[0], _imgCameraOffset, camPos[1]);
7744        camera->SetViewUp(0, 0, 1);
7745        // bottom
7746        _cameraClipPlanes[0]->SetOrigin(0, 0, _imgWorldOrigin[1]);
7747        _cameraClipPlanes[0]->SetNormal(0, 0, 1);
7748        // left
7749        _cameraClipPlanes[1]->SetOrigin(_imgWorldOrigin[0], 0, 0);
7750        _cameraClipPlanes[1]->SetNormal(1, 0, 0);
7751        // top
7752        _cameraClipPlanes[2]->SetOrigin(0, 0, _imgWorldOrigin[1] + _imgWorldDims[1]);
7753        _cameraClipPlanes[2]->SetNormal(0, 0, -1);
7754        // right
7755        _cameraClipPlanes[3]->SetOrigin(_imgWorldOrigin[0] + _imgWorldDims[0], 0, 0);
7756        _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
7757        _cubeAxesActor2D->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWorldDims[0],
7758                                    _imgCameraOffset, _imgCameraOffset,
7759                                    _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWorldDims[1]);
7760        _cubeAxesActor2D->XAxisVisibilityOn();
7761        _cubeAxesActor2D->YAxisVisibilityOff();
7762        _cubeAxesActor2D->ZAxisVisibilityOn();
7763    }
7764
7765    // Compute screen world coordinates
7766    computeScreenWorldCoords();
7767
7768#ifdef DEBUG
7769    printCameraInfo(camera);
7770#endif
7771
7772    _needsRedraw = true;
7773}
7774
7775/**
7776 * \brief Convert pixel/display coordinates to world coordinates based on current camera
7777 */
7778void Renderer::computeDisplayToWorld(double x, double y, double z, double worldPt[4])
7779{
7780    _renderer->SetDisplayPoint(x, y, z);
7781    _renderer->DisplayToWorld();
7782    _renderer->GetWorldPoint(worldPt);
7783    if (worldPt[3]) {
7784        worldPt[0] /= worldPt[3];
7785        worldPt[1] /= worldPt[3];
7786        worldPt[2] /= worldPt[3];
7787        worldPt[3] = 1.0;
7788    }
7789}
7790
7791/**
7792 * \brief Convert world coordinates to pixel/display coordinates based on current camera
7793 */
7794void Renderer::computeWorldToDisplay(double x, double y, double z, double displayPt[3])
7795{
7796    _renderer->SetWorldPoint(x, y, z, 1.0);
7797    _renderer->WorldToDisplay();
7798    _renderer->GetDisplayPoint(displayPt);
7799}
7800
7801/**
7802 * \brief Compute the world coordinate bounds of the display rectangle
7803 */
7804void Renderer::computeScreenWorldCoords()
7805{
7806    // Start with viewport coords [-1,1]
7807    double x0 = -1;
7808    double y0 = -1;
7809    double z0 = -1;
7810    double x1 = 1;
7811    double y1 = 1;
7812    double z1 = -1;
7813
7814    vtkMatrix4x4 *mat = vtkMatrix4x4::New();
7815    double result[4];
7816
7817    // get the perspective transformation from the active camera
7818    mat->DeepCopy(_renderer->GetActiveCamera()->
7819                  GetCompositeProjectionTransformMatrix(_renderer->GetTiledAspectRatio(),0,1));
7820
7821    // use the inverse matrix
7822    mat->Invert();
7823
7824    // Transform point to world coordinates
7825    result[0] = x0;
7826    result[1] = y0;
7827    result[2] = z0;
7828    result[3] = 1.0;
7829
7830    mat->MultiplyPoint(result, result);
7831
7832    // Get the transformed vector & set WorldPoint
7833    // while we are at it try to keep w at one
7834    if (result[3]) {
7835        x0 = result[0] / result[3];
7836        y0 = result[1] / result[3];
7837        z0 = result[2] / result[3];
7838    }
7839
7840    result[0] = x1;
7841    result[1] = y1;
7842    result[2] = z1;
7843    result[3] = 1.0;
7844
7845    mat->MultiplyPoint(result, result);
7846
7847    if (result[3]) {
7848        x1 = result[0] / result[3];
7849        y1 = result[1] / result[3];
7850        z1 = result[2] / result[3];
7851    }
7852
7853    mat->Delete();
7854
7855    if (_imgCameraPlane == PLANE_XZ) {
7856        _screenWorldCoords[0] = x0;
7857        _screenWorldCoords[1] = z0;
7858        _screenWorldCoords[2] = x1 - x0;
7859        _screenWorldCoords[3] = z1 - z0;
7860    } else if (_imgCameraPlane == PLANE_ZY) {
7861        _screenWorldCoords[0] = z0;
7862        _screenWorldCoords[1] = y0;
7863        _screenWorldCoords[2] = z1 - z0;
7864        _screenWorldCoords[3] = y1 - y0;
7865    } else {
7866        // XY
7867        _screenWorldCoords[0] = x0;
7868        _screenWorldCoords[1] = y0;
7869        _screenWorldCoords[2] = x1 - x0;
7870        _screenWorldCoords[3] = y1 - y0;
7871    }
7872}
7873
7874/**
7875 * \brief Get the world coordinates of the image camera plot area
7876 *
7877 * \param[out] xywh Array to hold x,y,width,height world coordinates
7878 */
7879void Renderer::getCameraZoomRegion(double xywh[4]) const
7880{
7881    xywh[0] = _imgWorldOrigin[0];
7882    xywh[1] = _imgWorldOrigin[1];
7883    xywh[2] = _imgWorldDims[0];
7884    xywh[3] = _imgWorldDims[1];
7885}
7886
7887/**
7888 * \brief Get the world origin and dimensions of the screen
7889 *
7890 * \param[out] xywh Array to hold x,y,width,height world coordinates
7891 */
7892void Renderer::getScreenWorldCoords(double xywh[4]) const
7893{
7894    memcpy(xywh, _screenWorldCoords, sizeof(double)*4);
7895}
7896
7897/**
7898 * \brief Compute bounding box containing the two input bounding boxes
7899 *
7900 * \param[out] boundsDest Union of the two input bounding boxes
7901 * \param[in] bounds1 Input bounding box
7902 * \param[in] bounds2 Input bounding box
7903 */
7904void Renderer::mergeBounds(double *boundsDest,
7905                           const double *bounds1, const double *bounds2)
7906{
7907    assert(boundsDest != NULL);
7908    assert(bounds1 != NULL);
7909    if (bounds2 == NULL) {
7910        WARN("NULL bounds2 array");
7911        return;
7912    }
7913    for (int i = 0; i < 6; i++) {
7914        if (i % 2 == 0)
7915            boundsDest[i] = min2(bounds1[i], bounds2[i]);
7916        else
7917            boundsDest[i] = max2(bounds1[i], bounds2[i]);
7918    }
7919}
7920
7921/**
7922 * \brief Collect bounds of all graphics objects
7923 *
7924 * \param[out] bounds Bounds of all scene objects
7925 * \param[in] onlyVisible Only collect bounds of visible objects
7926 */
7927void Renderer::collectBounds(double *bounds, bool onlyVisible)
7928{
7929    bounds[0] = DBL_MAX;
7930    bounds[1] = -DBL_MAX;
7931    bounds[2] = DBL_MAX;
7932    bounds[3] = -DBL_MAX;
7933    bounds[4] = DBL_MAX;
7934    bounds[5] = -DBL_MAX;
7935
7936    for (DataSetHashmap::iterator itr = _dataSets.begin();
7937             itr != _dataSets.end(); ++itr) {
7938        if ((!onlyVisible || itr->second->getVisibility()) &&
7939            itr->second->getProp() != NULL)
7940            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7941    }
7942    for (Contour2DHashmap::iterator itr = _contour2Ds.begin();
7943             itr != _contour2Ds.end(); ++itr) {
7944        if ((!onlyVisible || itr->second->getVisibility()) &&
7945            itr->second->getProp() != NULL)
7946            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7947    }
7948    for (Contour3DHashmap::iterator itr = _contour3Ds.begin();
7949             itr != _contour3Ds.end(); ++itr) {
7950        if ((!onlyVisible || itr->second->getVisibility()) &&
7951            itr->second->getProp() != NULL)
7952            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7953    }
7954    for (GlyphsHashmap::iterator itr = _glyphs.begin();
7955             itr != _glyphs.end(); ++itr) {
7956        if ((!onlyVisible || itr->second->getVisibility()) &&
7957            itr->second->getProp() != NULL)
7958            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7959    }
7960    for (HeightMapHashmap::iterator itr = _heightMaps.begin();
7961             itr != _heightMaps.end(); ++itr) {
7962        if ((!onlyVisible || itr->second->getVisibility()) &&
7963            itr->second->getProp() != NULL)
7964            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7965    }
7966    for (LICHashmap::iterator itr = _lics.begin();
7967             itr != _lics.end(); ++itr) {
7968        if ((!onlyVisible || itr->second->getVisibility()) &&
7969            itr->second->getProp() != NULL)
7970            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7971    }
7972    for (MoleculeHashmap::iterator itr = _molecules.begin();
7973             itr != _molecules.end(); ++itr) {
7974        if ((!onlyVisible || itr->second->getVisibility()) &&
7975            itr->second->getProp() != NULL)
7976            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7977    }
7978    for (PolyDataHashmap::iterator itr = _polyDatas.begin();
7979             itr != _polyDatas.end(); ++itr) {
7980        if ((!onlyVisible || itr->second->getVisibility()) &&
7981            itr->second->getProp() != NULL)
7982            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7983    }
7984    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
7985             itr != _pseudoColors.end(); ++itr) {
7986        if ((!onlyVisible || itr->second->getVisibility()) &&
7987            itr->second->getProp() != NULL)
7988            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7989    }
7990    for (StreamlinesHashmap::iterator itr = _streamlines.begin();
7991             itr != _streamlines.end(); ++itr) {
7992        if ((!onlyVisible || itr->second->getVisibility()) &&
7993            itr->second->getProp() != NULL)
7994            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
7995    }
7996    for (VolumeHashmap::iterator itr = _volumes.begin();
7997             itr != _volumes.end(); ++itr) {
7998        if ((!onlyVisible || itr->second->getVisibility()) &&
7999            itr->second->getProp() != NULL)
8000            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
8001    }
8002
8003    for (int i = 0; i < 6; i += 2) {
8004        if (bounds[i+1] < bounds[i]) {
8005            bounds[i] = -0.5;
8006            bounds[i+1] = 0.5;
8007        }
8008    }
8009
8010    int numDims = 0;
8011    if (bounds[0] != bounds[1])
8012        numDims++;
8013    if (bounds[2] != bounds[3])
8014        numDims++;
8015    if (bounds[4] != bounds[5])
8016        numDims++;
8017
8018    if (numDims == 0) {
8019        bounds[0] -= .5;
8020        bounds[1] += .5;
8021        bounds[2] -= .5;
8022        bounds[3] += .5;
8023    }
8024
8025    TRACE("Bounds: %g %g %g %g %g %g",
8026          bounds[0],
8027          bounds[1],
8028          bounds[2],
8029          bounds[3],
8030          bounds[4],
8031          bounds[5]);
8032}
8033
8034/**
8035 * \brief Update data ranges for color-mapping and contours
8036 */
8037void Renderer::updateRanges()
8038{
8039    collectDataRanges();
8040
8041    for (Contour2DHashmap::iterator itr = _contour2Ds.begin();
8042         itr != _contour2Ds.end(); ++itr) {
8043        itr->second->updateRanges(_useCumulativeRange,
8044                                  _cumulativeScalarRange,
8045                                  _cumulativeVectorMagnitudeRange,
8046                                  _cumulativeVectorComponentRange);
8047    }
8048    for (Contour3DHashmap::iterator itr = _contour3Ds.begin();
8049         itr != _contour3Ds.end(); ++itr) {
8050        itr->second->updateRanges(_useCumulativeRange,
8051                                  _cumulativeScalarRange,
8052                                  _cumulativeVectorMagnitudeRange,
8053                                  _cumulativeVectorComponentRange);
8054    }
8055    for (GlyphsHashmap::iterator itr = _glyphs.begin();
8056         itr != _glyphs.end(); ++itr) {
8057        itr->second->updateRanges(_useCumulativeRange,
8058                                  _cumulativeScalarRange,
8059                                  _cumulativeVectorMagnitudeRange,
8060                                  _cumulativeVectorComponentRange);
8061    }
8062    for (HeightMapHashmap::iterator itr = _heightMaps.begin();
8063         itr != _heightMaps.end(); ++itr) {
8064        itr->second->updateRanges(_useCumulativeRange,
8065                                  _cumulativeScalarRange,
8066                                  _cumulativeVectorMagnitudeRange,
8067                                  _cumulativeVectorComponentRange);
8068    }
8069    for (LICHashmap::iterator itr = _lics.begin();
8070         itr != _lics.end(); ++itr) {
8071        itr->second->updateRanges(_useCumulativeRange,
8072                                  _cumulativeScalarRange,
8073                                  _cumulativeVectorMagnitudeRange,
8074                                  _cumulativeVectorComponentRange);
8075    }
8076    for (MoleculeHashmap::iterator itr = _molecules.begin();
8077         itr != _molecules.end(); ++itr) {
8078        itr->second->updateRanges(_useCumulativeRange,
8079                                  _cumulativeScalarRange,
8080                                  _cumulativeVectorMagnitudeRange,
8081                                  _cumulativeVectorComponentRange);
8082    }
8083    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
8084         itr != _pseudoColors.end(); ++itr) {
8085        itr->second->updateRanges(_useCumulativeRange,
8086                                  _cumulativeScalarRange,
8087                                  _cumulativeVectorMagnitudeRange,
8088                                  _cumulativeVectorComponentRange);
8089    }
8090    for (StreamlinesHashmap::iterator itr = _streamlines.begin();
8091         itr != _streamlines.end(); ++itr) {
8092        itr->second->updateRanges(_useCumulativeRange,
8093                                  _cumulativeScalarRange,
8094                                  _cumulativeVectorMagnitudeRange,
8095                                  _cumulativeVectorComponentRange);
8096    }
8097    for (VolumeHashmap::iterator itr = _volumes.begin();
8098         itr != _volumes.end(); ++itr) {
8099        itr->second->updateRanges(_useCumulativeRange,
8100                                  _cumulativeScalarRange,
8101                                  _cumulativeVectorMagnitudeRange,
8102                                  _cumulativeVectorComponentRange);
8103    }
8104}
8105
8106void Renderer::collectDataRanges()
8107{
8108    collectScalarRanges(_cumulativeScalarRange,
8109                        _cumulativeRangeOnlyVisible);
8110    collectVectorMagnitudeRanges(_cumulativeVectorMagnitudeRange,
8111                                 _cumulativeRangeOnlyVisible);
8112    for (int i = 0; i < 3; i++) {
8113        collectVectorComponentRanges(_cumulativeVectorComponentRange[i], i,
8114                                     _cumulativeRangeOnlyVisible);
8115    }
8116
8117    TRACE("Cumulative scalar range: %g, %g",
8118          _cumulativeScalarRange[0],
8119          _cumulativeScalarRange[1]);
8120    TRACE("Cumulative vmag range: %g, %g",
8121          _cumulativeVectorMagnitudeRange[0],
8122          _cumulativeVectorMagnitudeRange[1]);
8123    for (int i = 0; i < 3; i++) {
8124        TRACE("Cumulative v[%d] range: %g, %g", i,
8125              _cumulativeVectorComponentRange[i][0],
8126              _cumulativeVectorComponentRange[i][1]);
8127    }
8128}
8129
8130/**
8131 * \brief Collect cumulative data range of all DataSets
8132 *
8133 * \param[in,out] range Data range of all DataSets
8134 * \param[in] onlyVisible Only collect range of visible DataSets
8135 */
8136void Renderer::collectScalarRanges(double *range, bool onlyVisible)
8137{
8138    range[0] = DBL_MAX;
8139    range[1] = -DBL_MAX;
8140
8141    for (DataSetHashmap::iterator itr = _dataSets.begin();
8142         itr != _dataSets.end(); ++itr) {
8143        if (!onlyVisible || itr->second->getVisibility()) {
8144            double r[2];
8145            itr->second->getScalarRange(r);
8146            range[0] = min2(range[0], r[0]);
8147            range[1] = max2(range[1], r[1]);
8148        }
8149    }
8150    if (range[0] == DBL_MAX)
8151        range[0] = 0;
8152    if (range[1] == -DBL_MAX)
8153        range[1] = 1;
8154}
8155
8156/**
8157 * \brief Collect cumulative data range of all DataSets
8158 *
8159 * \param[in,out] range Data range of all DataSets
8160 * \param[in] onlyVisible Only collect range of visible DataSets
8161 */
8162void Renderer::collectVectorMagnitudeRanges(double *range, bool onlyVisible)
8163{
8164    range[0] = DBL_MAX;
8165    range[1] = -DBL_MAX;
8166
8167    for (DataSetHashmap::iterator itr = _dataSets.begin();
8168         itr != _dataSets.end(); ++itr) {
8169        if (!onlyVisible || itr->second->getVisibility()) {
8170            double r[2];
8171            itr->second->getVectorRange(r);
8172            range[0] = min2(range[0], r[0]);
8173            range[1] = max2(range[1], r[1]);
8174        }
8175    }
8176    if (range[0] == DBL_MAX)
8177        range[0] = 0;
8178    if (range[1] == -DBL_MAX)
8179        range[1] = 1;
8180}
8181
8182/**
8183 * \brief Collect cumulative data range of all DataSets
8184 *
8185 * \param[in,out] range Data range of all DataSets
8186 * \param[in] onlyVisible Only collect range of visible DataSets
8187 */
8188void Renderer::collectVectorComponentRanges(double *range, int component, bool onlyVisible)
8189{
8190    range[0] = DBL_MAX;
8191    range[1] = -DBL_MAX;
8192
8193    for (DataSetHashmap::iterator itr = _dataSets.begin();
8194         itr != _dataSets.end(); ++itr) {
8195        if (!onlyVisible || itr->second->getVisibility()) {
8196            double r[2];
8197            itr->second->getVectorRange(r, component);
8198            range[0] = min2(range[0], r[0]);
8199            range[1] = max2(range[1], r[1]);
8200        }
8201    }
8202    if (range[0] == DBL_MAX)
8203        range[0] = 0;
8204    if (range[1] == -DBL_MAX)
8205        range[1] = 1;
8206 }
8207
8208/**
8209 * \brief Determines if AABB lies in a principal axis plane
8210 * and if so, returns the plane normal
8211 */
8212bool Renderer::is2D(const double bounds[6],
8213                    Renderer::PrincipalPlane *plane,
8214                    double *offset) const
8215{
8216    if (bounds[4] == bounds[5]) {
8217        // Z = 0, XY plane
8218        if (plane)
8219            *plane = PLANE_XY;
8220        if (offset)
8221            *offset = bounds[4];
8222        return true;
8223    } else if (bounds[0] == bounds[1]) {
8224        // X = 0, ZY plane
8225        if (plane)
8226            *plane = PLANE_ZY;
8227        if (offset)
8228            *offset = bounds[0];
8229        return true;
8230    } else if (bounds[2] == bounds[3]) {
8231        // Y = 0, XZ plane
8232        if (plane)
8233            *plane = PLANE_XZ;
8234        if (offset)
8235            *offset = bounds[2];
8236        return true;
8237    }
8238    *plane = PLANE_XY;
8239    *offset = 0;
8240    return false;
8241}
8242
8243/**
8244 * \brief Initialize the camera zoom region to include the bounding volume given
8245 */
8246void Renderer::initCamera()
8247{
8248#ifdef WANT_TRACE
8249    switch (_cameraMode) {
8250    case IMAGE:
8251        TRACE("Image camera");
8252        break;
8253    case ORTHO:
8254        TRACE("Ortho camera");
8255        break;
8256    case PERSPECTIVE:
8257        TRACE("Perspective camera");
8258        break;
8259    default:
8260        TRACE("Unknown camera mode");
8261    }
8262#endif
8263    double bounds[6];
8264    collectBounds(bounds, false);
8265    bool twod = is2D(bounds, &_imgCameraPlane, &_imgCameraOffset);
8266    if (twod) {
8267        _cameraMode = IMAGE;
8268        if (_imgCameraPlane == PLANE_ZY) {
8269            _imgWorldOrigin[0] = bounds[4];
8270            _imgWorldOrigin[1] = bounds[2];
8271            _imgWorldDims[0] = bounds[5] - bounds[4];
8272            _imgWorldDims[1] = bounds[3] - bounds[2];
8273        } else if (_imgCameraPlane == PLANE_XZ) {
8274            _imgWorldOrigin[0] = bounds[0];
8275            _imgWorldOrigin[1] = bounds[4];
8276            _imgWorldDims[0] = bounds[1] - bounds[0];
8277            _imgWorldDims[1] = bounds[5] - bounds[4];
8278        } else {
8279            _imgWorldOrigin[0] = bounds[0];
8280            _imgWorldOrigin[1] = bounds[2];
8281            _imgWorldDims[0] = bounds[1] - bounds[0];
8282            _imgWorldDims[1] = bounds[3] - bounds[2];
8283        }
8284    } else {
8285        _imgWorldOrigin[0] = bounds[0];
8286        _imgWorldOrigin[1] = bounds[2];
8287        _imgWorldDims[0] = bounds[1] - bounds[0];
8288        _imgWorldDims[1] = bounds[3] - bounds[2];
8289    }
8290
8291    _cameraPan[0] = 0;
8292    _cameraPan[1] = 0;
8293    _cameraZoomRatio = 1;
8294
8295    switch (_cameraMode) {
8296    case IMAGE:
8297        _renderer->ResetCamera(bounds);
8298        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
8299                            _imgWorldDims[0], _imgWorldDims[1]);
8300        resetAxes();
8301        break;
8302    case ORTHO:
8303        _renderer->GetActiveCamera()->ParallelProjectionOn();
8304        resetAxes(bounds);
8305        _renderer->ResetCamera(bounds);
8306        //computeScreenWorldCoords();
8307        break;
8308    case PERSPECTIVE:
8309        _renderer->GetActiveCamera()->ParallelProjectionOff();
8310        resetAxes(bounds);
8311        _renderer->ResetCamera(bounds);
8312        //computeScreenWorldCoords();
8313        break;
8314    default:
8315        ERROR("Unknown camera mode");
8316    }
8317
8318#ifdef WANT_TRACE
8319    printCameraInfo(_renderer->GetActiveCamera());
8320#endif
8321}
8322
8323/**
8324 * \brief Print debugging info about a vtkCamera
8325 */
8326void Renderer::printCameraInfo(vtkCamera *camera)
8327{
8328    TRACE("pscale: %g, angle: %g, d: %g pos: %g %g %g, fpt: %g %g %g, vup: %g %g %g, clip: %g %g",
8329          camera->GetParallelScale(),
8330          camera->GetViewAngle(),
8331          camera->GetDistance(),
8332          camera->GetPosition()[0],
8333          camera->GetPosition()[1],
8334          camera->GetPosition()[2],
8335          camera->GetFocalPoint()[0],
8336          camera->GetFocalPoint()[1],
8337          camera->GetFocalPoint()[2],
8338          camera->GetViewUp()[0],
8339          camera->GetViewUp()[1],
8340          camera->GetViewUp()[2],
8341          camera->GetClippingRange()[0],
8342          camera->GetClippingRange()[1]);
8343}
8344
8345/**
8346 * \brief Set the RGB background color to render into the image
8347 */
8348void Renderer::setBackgroundColor(float color[3])
8349{
8350    _bgColor[0] = color[0];
8351    _bgColor[1] = color[1];
8352    _bgColor[2] = color[2];
8353    _renderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
8354    _needsRedraw = true;
8355}
8356
8357/**
8358 * \brief Set the opacity of the specified DataSet's associated graphics objects
8359 */
8360void Renderer::setDataSetOpacity(const DataSetId& id, double opacity)
8361{
8362    DataSetHashmap::iterator itr;
8363
8364    bool doAll = false;
8365
8366    if (id.compare("all") == 0) {
8367        itr = _dataSets.begin();
8368        doAll = true;
8369    } else {
8370        itr = _dataSets.find(id);
8371    }
8372    if (itr == _dataSets.end()) {
8373        ERROR("Unknown dataset %s", id.c_str());
8374        return;
8375    }
8376
8377    do {
8378        itr->second->setOpacity(opacity);
8379    } while (doAll && ++itr != _dataSets.end());
8380
8381    if (id.compare("all") == 0 || getContour2D(id) != NULL)
8382        setContour2DOpacity(id, opacity);
8383    if (id.compare("all") == 0 || getContour3D(id) != NULL)
8384        setContour3DOpacity(id, opacity);
8385    if (id.compare("all") == 0 || getGlyphs(id) != NULL)
8386        setGlyphsOpacity(id, opacity);
8387    if (id.compare("all") == 0 || getHeightMap(id) != NULL)
8388        setHeightMapOpacity(id, opacity);
8389    if (id.compare("all") == 0 || getLIC(id) != NULL)
8390        setLICOpacity(id, opacity);
8391    if (id.compare("all") == 0 || getMolecule(id) != NULL)
8392        setMoleculeOpacity(id, opacity);
8393    if (id.compare("all") == 0 || getPolyData(id) != NULL)
8394        setPolyDataOpacity(id, opacity);
8395    if (id.compare("all") == 0 || getPseudoColor(id) != NULL)
8396        setPseudoColorOpacity(id, opacity);
8397    if (id.compare("all") == 0 || getStreamlines(id) != NULL)
8398        setStreamlinesOpacity(id, opacity);
8399    if (id.compare("all") == 0 || getVolume(id) != NULL)
8400        setVolumeOpacity(id, opacity);
8401
8402    _needsRedraw = true;
8403}
8404
8405/**
8406 * \brief Turn on/off rendering of the specified DataSet's associated graphics objects
8407 */
8408void Renderer::setDataSetVisibility(const DataSetId& id, bool state)
8409{
8410    DataSetHashmap::iterator itr;
8411
8412    bool doAll = false;
8413
8414    if (id.compare("all") == 0) {
8415        itr = _dataSets.begin();
8416        doAll = true;
8417    } else {
8418        itr = _dataSets.find(id);
8419    }
8420    if (itr == _dataSets.end()) {
8421        ERROR("Unknown dataset %s", id.c_str());
8422        return;
8423    }
8424
8425    do {
8426        itr->second->setVisibility(state);
8427    } while (doAll && ++itr != _dataSets.end());
8428
8429    if (id.compare("all") == 0 || getContour2D(id) != NULL)
8430        setContour2DVisibility(id, state);
8431    if (id.compare("all") == 0 || getContour3D(id) != NULL)
8432        setContour3DVisibility(id, state);
8433    if (id.compare("all") == 0 || getGlyphs(id) != NULL)
8434        setGlyphsVisibility(id, state);
8435    if (id.compare("all") == 0 || getHeightMap(id) != NULL)
8436        setHeightMapVisibility(id, state);
8437    if (id.compare("all") == 0 || getLIC(id) != NULL)
8438        setLICVisibility(id, state);
8439    if (id.compare("all") == 0 || getMolecule(id) != NULL)
8440        setMoleculeVisibility(id, state);
8441    if (id.compare("all") == 0 || getPolyData(id) != NULL)
8442        setPolyDataVisibility(id, state);
8443    if (id.compare("all") == 0 || getPseudoColor(id) != NULL)
8444        setPseudoColorVisibility(id, state);
8445    if (id.compare("all") == 0 || getStreamlines(id) != NULL)
8446        setStreamlinesVisibility(id, state);
8447    if (id.compare("all") == 0 || getVolume(id) != NULL)
8448        setVolumeVisibility(id, state);
8449
8450    _needsRedraw = true;
8451}
8452
8453/**
8454 * \brief Toggle rendering of actors' bounding box
8455 */
8456void Renderer::setDataSetShowBounds(const DataSetId& id, bool state)
8457{
8458    DataSetHashmap::iterator itr;
8459
8460    bool doAll = false;
8461
8462    if (id.compare("all") == 0) {
8463        itr = _dataSets.begin();
8464        doAll = true;
8465    } else {
8466        itr = _dataSets.find(id);
8467    }
8468    if (itr == _dataSets.end()) {
8469        ERROR("Unknown dataset %s", id.c_str());
8470        return;
8471    }
8472
8473    do {
8474        if (!state && itr->second->getProp()) {
8475            _renderer->RemoveViewProp(itr->second->getProp());
8476        }
8477
8478        itr->second->showOutline(state);
8479
8480        if (state && !_renderer->HasViewProp(itr->second->getProp())) {
8481            _renderer->AddViewProp(itr->second->getProp());
8482        }
8483    } while (doAll && ++itr != _dataSets.end());
8484
8485    initCamera();
8486    _needsRedraw = true;
8487}
8488
8489/**
8490 * \brief Set color of outline bounding box
8491 */
8492void Renderer::setDataSetOutlineColor(const DataSetId& id, float color[3])
8493{
8494    DataSetHashmap::iterator itr;
8495
8496    bool doAll = false;
8497
8498    if (id.compare("all") == 0) {
8499        itr = _dataSets.begin();
8500        doAll = true;
8501    } else {
8502        itr = _dataSets.find(id);
8503    }
8504    if (itr == _dataSets.end()) {
8505        ERROR("Unknown dataset %s", id.c_str());
8506        return;
8507    }
8508
8509    do {
8510        itr->second->setOutlineColor(color);
8511    } while (doAll && ++itr != _dataSets.end());
8512
8513    _needsRedraw = true;
8514}
8515
8516/**
8517 * \brief Set a user clipping plane
8518 *
8519 * TODO: Fix clip plane positions after a change in actor bounds
8520 */
8521void Renderer::setClipPlane(Axis axis, double ratio, int direction)
8522{
8523    double bounds[6];
8524    collectBounds(bounds, false);
8525
8526    switch (axis) {
8527    case X_AXIS:
8528        if (direction > 0) {
8529            if (ratio > 0.0) {
8530                if (_userClipPlanes[0] == NULL) {
8531                    _userClipPlanes[0] = vtkSmartPointer<vtkPlane>::New();
8532                    _userClipPlanes[0]->SetNormal(1, 0, 0);
8533                }
8534                _userClipPlanes[0]->SetOrigin(bounds[0] + (bounds[1]-bounds[0])*ratio, 0, 0);
8535            } else {
8536                _userClipPlanes[0] = NULL;
8537            }
8538        } else {
8539            if (ratio > 0.0) {
8540                if (_userClipPlanes[1] == NULL) {
8541                    _userClipPlanes[1] = vtkSmartPointer<vtkPlane>::New();
8542                    _userClipPlanes[1]->SetNormal(-1, 0, 0);
8543                }
8544                _userClipPlanes[1]->SetOrigin(bounds[0] + (bounds[1]-bounds[0])*ratio, 0, 0);
8545            } else {
8546                _userClipPlanes[1] = NULL;
8547            }
8548        }
8549        break;
8550    case Y_AXIS:
8551        if (direction > 0) {
8552            if (ratio > 0.0) {
8553                if (_userClipPlanes[2] == NULL) {
8554                    _userClipPlanes[2] = vtkSmartPointer<vtkPlane>::New();
8555                    _userClipPlanes[2]->SetNormal(0, 1, 0);
8556                }
8557                _userClipPlanes[2]->SetOrigin(0, bounds[2] + (bounds[3]-bounds[2])*ratio, 0);
8558            } else {
8559                _userClipPlanes[2] = NULL;
8560            }
8561        } else {
8562            if (ratio > 0.0) {
8563                if (_userClipPlanes[3] == NULL) {
8564                    _userClipPlanes[3] = vtkSmartPointer<vtkPlane>::New();
8565                    _userClipPlanes[3]->SetNormal(0, -1, 0);
8566                }
8567                _userClipPlanes[3]->SetOrigin(0, bounds[2] + (bounds[3]-bounds[2])*ratio, 0);
8568            } else {
8569                _userClipPlanes[3] = NULL;
8570            }
8571        }
8572        break;
8573    case Z_AXIS:
8574        if (direction > 0) {
8575            if (ratio > 0.0) {
8576                if (_userClipPlanes[4] == NULL) {
8577                    _userClipPlanes[4] = vtkSmartPointer<vtkPlane>::New();
8578                    _userClipPlanes[4]->SetNormal(0, 0, 1);
8579                }
8580                _userClipPlanes[4]->SetOrigin(0, 0, bounds[4] + (bounds[5]-bounds[4])*ratio);
8581            } else {
8582                _userClipPlanes[4] = NULL;
8583            }
8584        } else {
8585            if (ratio > 0.0) {
8586                if (_userClipPlanes[5] == NULL) {
8587                    _userClipPlanes[5] = vtkSmartPointer<vtkPlane>::New();
8588                    _userClipPlanes[5]->SetNormal(0, 0, -1);
8589                }
8590                _userClipPlanes[5]->SetOrigin(0, 0, bounds[4] + (bounds[5]-bounds[4])*ratio);
8591            } else {
8592                _userClipPlanes[5] = NULL;
8593            }
8594        }
8595        break;
8596    default:
8597        ;
8598    }
8599
8600    _needsRedraw = true;
8601}
8602
8603/**
8604 * \brief Set up clipping planes for image camera mode if needed
8605 */
8606void Renderer::setCameraClippingPlanes()
8607{
8608    /* XXX: Note that there appears to be a bug with setting the
8609     * clipping plane collection to NULL in the VTK Mappers --
8610     * the old clip planes are still applied.  The workaround here
8611     * is to keep the PlaneCollection and add/remove the planes
8612     * to/from the PlaneCollection as needed.
8613     */
8614    if (_cameraMode == IMAGE) {
8615        if (_activeClipPlanes->GetNumberOfItems() == 0) {
8616            for (int i = 0; i < 4; i++)
8617                _activeClipPlanes->AddItem(_cameraClipPlanes[i]);
8618        }
8619    } else {
8620        if (_activeClipPlanes->GetNumberOfItems() > 0)
8621            _activeClipPlanes->RemoveAllItems();
8622        for (int i = 0; i < 6; i++) {
8623            if (_userClipPlanes[i] != NULL) {
8624                _activeClipPlanes->AddItem(_userClipPlanes[i]);
8625            }
8626        }
8627    }
8628
8629    /* Ensure all Mappers are using the PlaneCollection
8630     * This will not change the state or timestamp of
8631     * Mappers already using the PlaneCollection
8632     */
8633    for (Contour2DHashmap::iterator itr = _contour2Ds.begin();
8634         itr != _contour2Ds.end(); ++itr) {
8635        itr->second->setClippingPlanes(_activeClipPlanes);
8636    }
8637    for (Contour3DHashmap::iterator itr = _contour3Ds.begin();
8638         itr != _contour3Ds.end(); ++itr) {
8639        itr->second->setClippingPlanes(_activeClipPlanes);
8640    }
8641    for (GlyphsHashmap::iterator itr = _glyphs.begin();
8642         itr != _glyphs.end(); ++itr) {
8643        itr->second->setClippingPlanes(_activeClipPlanes);
8644    }
8645    for (HeightMapHashmap::iterator itr = _heightMaps.begin();
8646         itr != _heightMaps.end(); ++itr) {
8647        itr->second->setClippingPlanes(_activeClipPlanes);
8648    }
8649    for (LICHashmap::iterator itr = _lics.begin();
8650         itr != _lics.end(); ++itr) {
8651        itr->second->setClippingPlanes(_activeClipPlanes);
8652    }
8653    for (MoleculeHashmap::iterator itr = _molecules.begin();
8654         itr != _molecules.end(); ++itr) {
8655        itr->second->setClippingPlanes(_activeClipPlanes);
8656    }
8657    for (PolyDataHashmap::iterator itr = _polyDatas.begin();
8658         itr != _polyDatas.end(); ++itr) {
8659        itr->second->setClippingPlanes(_activeClipPlanes);
8660    }
8661    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
8662         itr != _pseudoColors.end(); ++itr) {
8663        itr->second->setClippingPlanes(_activeClipPlanes);
8664    }
8665    for (StreamlinesHashmap::iterator itr = _streamlines.begin();
8666         itr != _streamlines.end(); ++itr) {
8667        itr->second->setClippingPlanes(_activeClipPlanes);
8668    }
8669    for (VolumeHashmap::iterator itr = _volumes.begin();
8670         itr != _volumes.end(); ++itr) {
8671        itr->second->setClippingPlanes(_activeClipPlanes);
8672    }
8673}
8674
8675/**
8676 * \brief Control the use of two sided lighting
8677 */
8678void Renderer::setUseTwoSidedLighting(bool state)
8679{
8680    _renderer->SetTwoSidedLighting(state ? 1 : 0);
8681    _needsRedraw = true;
8682}
8683
8684/**
8685 * \brief Control the use of the depth peeling algorithm for transparency
8686 */
8687void Renderer::setUseDepthPeeling(bool state)
8688{
8689    _renderer->SetUseDepthPeeling(state ? 1 : 0);
8690    _needsRedraw = true;
8691}
8692
8693/**
8694 * \brief Sets flag to trigger rendering next time render() is called
8695 */
8696void Renderer::eventuallyRender()
8697{
8698    _needsRedraw = true;
8699}
8700
8701/**
8702 * \brief Cause the rendering to render a new image if needed
8703 *
8704 * The _needsRedraw flag indicates if a state change has occured since
8705 * the last rendered frame
8706 */
8707bool Renderer::render()
8708{
8709    if (_needsRedraw) {
8710        setCameraClippingPlanes();
8711        _renderWindow->Render();
8712        _needsRedraw = false;
8713        return true;
8714    } else
8715        return false;
8716}
8717
8718/// Get the pixel width of the render window/image
8719int Renderer::getWindowWidth() const
8720{
8721    return _windowWidth;
8722}
8723
8724/// Get the pixel height of the render window/image
8725int Renderer::getWindowHeight() const
8726{
8727    return _windowHeight;
8728}
8729
8730/**
8731 * \brief Read back the rendered framebuffer image
8732 */
8733void Renderer::getRenderedFrame(vtkUnsignedCharArray *imgData)
8734{
8735#ifdef RENDER_TARGA
8736    _renderWindow->MakeCurrent();
8737    // Must clear previous errors first.
8738    while (glGetError() != GL_NO_ERROR){
8739        ;
8740    }
8741    int bytesPerPixel = TARGA_BYTES_PER_PIXEL;
8742    int size = bytesPerPixel * _windowWidth * _windowHeight;
8743
8744    if (imgData->GetMaxId() + 1 != size)
8745    {
8746        imgData->SetNumberOfComponents(bytesPerPixel);
8747        imgData->SetNumberOfValues(size);
8748    }
8749    glDisable(GL_TEXTURE_2D);
8750    if (_renderWindow->GetDoubleBuffer()) {
8751        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_renderWindow)->GetBackLeftBuffer()));
8752    } else {
8753        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_renderWindow)->GetFrontLeftBuffer()));
8754    }
8755    glPixelStorei(GL_PACK_ALIGNMENT, 1);
8756#ifdef WANT_TRACE
8757    struct timeval t1, t2;
8758    glFinish();
8759    gettimeofday(&t1, 0);
8760#endif
8761    if (bytesPerPixel == 4) {
8762        glReadPixels(0, 0, _windowWidth, _windowHeight, GL_BGRA,
8763                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
8764    } else {
8765        glReadPixels(0, 0, _windowWidth, _windowHeight, GL_BGR,
8766                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
8767    }
8768#ifdef WANT_TRACE
8769    gettimeofday(&t2, 0);
8770    static unsigned int numFrames = 0;
8771    static double accum = 0;
8772    numFrames++;
8773    accum += ELAPSED_TIME(t1, t2);
8774#endif
8775    TRACE("Readback time: %g ms", ELAPSED_TIME(t1, t2));
8776    TRACE("Readback avg: %g ms", accum/numFrames);
8777    if (glGetError() != GL_NO_ERROR) {
8778        ERROR("glReadPixels");
8779    }
8780#else
8781    _renderWindow->GetPixelData(0, 0, _windowWidth-1, _windowHeight-1,
8782                                !_renderWindow->GetDoubleBuffer(), imgData);
8783#endif
8784    TRACE("Image data size: %d", imgData->GetSize());
8785}
8786
8787/**
8788 * \brief Get nearest data value given display coordinates x,y
8789 *
8790 * Note: no interpolation is performed on data
8791 */
8792bool Renderer::getScalarValueAtPixel(const DataSetId& id, int x, int y, double *value)
8793{
8794    vtkSmartPointer<vtkCoordinate> coord = vtkSmartPointer<vtkCoordinate>::New();
8795    coord->SetCoordinateSystemToDisplay();
8796    coord->SetValue(x, _windowHeight - y, 0);
8797    double *worldCoords = coord->GetComputedWorldValue(_renderer);
8798
8799    TRACE("Pixel coords: %d, %d\nWorld coords: %g, %g, %g", x, y,
8800          worldCoords[0],
8801          worldCoords[1],
8802          worldCoords[2]);
8803
8804    return getScalarValue(id, worldCoords[0], worldCoords[1], worldCoords[2], value);
8805}
8806
8807/**
8808 * \brief Get nearest data value given world coordinates x,y,z
8809 *
8810 * Note: no interpolation is performed on data
8811 */
8812bool Renderer::getScalarValue(const DataSetId& id, double x, double y, double z, double *value)
8813{
8814    DataSet *ds = getDataSet(id);
8815    if (ds == NULL)
8816        return false;
8817
8818    return ds->getScalarValue(x, y, z, value);
8819}
8820
8821/**
8822 * \brief Get nearest data value given display coordinates x,y
8823 *
8824 * Note: no interpolation is performed on data
8825 */
8826bool Renderer::getVectorValueAtPixel(const DataSetId& id, int x, int y, double vector[3])
8827{
8828    vtkSmartPointer<vtkCoordinate> coord = vtkSmartPointer<vtkCoordinate>::New();
8829    coord->SetCoordinateSystemToDisplay();
8830    coord->SetValue(x, _windowHeight - y, 0);
8831    double *worldCoords = coord->GetComputedWorldValue(_renderer);
8832
8833    TRACE("Pixel coords: %d, %d\nWorld coords: %g, %g, %g", x, y,
8834          worldCoords[0],
8835          worldCoords[1],
8836          worldCoords[2]);
8837
8838    return getVectorValue(id, worldCoords[0], worldCoords[1], worldCoords[2], vector);
8839}
8840
8841/**
8842 * \brief Get nearest data value given world coordinates x,y,z
8843 *
8844 * Note: no interpolation is performed on data
8845 */
8846bool Renderer::getVectorValue(const DataSetId& id, double x, double y, double z, double vector[3])
8847{
8848    DataSet *ds = getDataSet(id);
8849    if (ds == NULL)
8850        return false;
8851
8852    return ds->getVectorValue(x, y, z, vector);
8853}
Note: See TracBrowser for help on using the repository browser.