source: trunk/packages/vizservers/vtkvis/RpVtkRenderer.cpp @ 2453

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

Add legend type option for selecting active scalar field, active vector field
magnitude or component ranges for labels.

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