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

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

Refactor vtkvis to support setting colormap fields by name/attribute type
rather than always using active scalars/vectors. Also convert common
graphics objects set methods in Renderer to template methods and separate
core and graphics object related methods to separate files.

  • Property svn:eol-style set to native
File size: 106.6 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (C) 2011, Purdue Research Foundation
4 *
5 * Author: Leif Delgass <ldelgass@purdue.edu>
6 */
7
8#include <cfloat>
9#include <cstring>
10#include <cassert>
11#include <cmath>
12
13#include <GL/gl.h>
14
15#ifdef WANT_TRACE
16#include <sys/time.h>
17#endif
18
19#include <vtkMath.h>
20#include <vtkCamera.h>
21#include <vtkLight.h>
22#include <vtkCoordinate.h>
23#include <vtkTransform.h>
24#include <vtkCharArray.h>
25#include <vtkAxisActor2D.h>
26#include <vtkCubeAxesActor.h>
27#ifdef USE_CUSTOM_AXES
28#include "vtkRpCubeAxesActor2D.h"
29#else
30#include <vtkCubeAxesActor2D.h>
31#endif
32#include <vtkDataSetReader.h>
33#include <vtkDataSetMapper.h>
34#include <vtkContourFilter.h>
35#include <vtkPolyDataMapper.h>
36#include <vtkProperty.h>
37#include <vtkProperty2D.h>
38#include <vtkPointData.h>
39#include <vtkLookupTable.h>
40#include <vtkTextProperty.h>
41#include <vtkOpenGLRenderWindow.h>
42#include <vtkVersion.h>
43
44#include "RpVtkRenderer.h"
45#include "RpVtkRendererGraphicsObjs.h"
46#include "RpMath.h"
47#include "ColorMap.h"
48#include "Trace.h"
49
50#define ELAPSED_TIME(t1, t2) \
51    ((t1).tv_sec == (t2).tv_sec ? (((t2).tv_usec - (t1).tv_usec)/1.0e+3f) : \
52     (((t2).tv_sec - (t1).tv_sec))*1.0e+3f + (float)((t2).tv_usec - (t1).tv_usec)/1.0e+3f)
53
54using namespace Rappture::VtkVis;
55
56Renderer::Renderer() :
57    _needsRedraw(true),
58    _windowWidth(500),
59    _windowHeight(500),
60    _imgCameraPlane(PLANE_XY),
61    _imgCameraOffset(0),
62    _cameraZoomRatio(1),
63    _useCumulativeRange(true),
64    _cumulativeRangeOnlyVisible(false),
65    _cameraMode(PERSPECTIVE)
66{
67    _bgColor[0] = 0;
68    _bgColor[1] = 0;
69    _bgColor[2] = 0;
70    _cameraPan[0] = 0;
71    _cameraPan[1] = 0;
72    _cameraOrientation[0] = 1.0;
73    _cameraOrientation[1] = 0.0;
74    _cameraOrientation[2] = 0.0;
75    _cameraOrientation[3] = 0.0;
76    // clipping planes to prevent overdrawing axes
77    _activeClipPlanes = vtkSmartPointer<vtkPlaneCollection>::New();
78    // bottom
79    _cameraClipPlanes[0] = vtkSmartPointer<vtkPlane>::New();
80    _cameraClipPlanes[0]->SetNormal(0, 1, 0);
81    _cameraClipPlanes[0]->SetOrigin(0, 0, 0);
82    if (_cameraMode == IMAGE)
83        _activeClipPlanes->AddItem(_cameraClipPlanes[0]);
84    // left
85    _cameraClipPlanes[1] = vtkSmartPointer<vtkPlane>::New();
86    _cameraClipPlanes[1]->SetNormal(1, 0, 0);
87    _cameraClipPlanes[1]->SetOrigin(0, 0, 0);
88    if (_cameraMode == IMAGE)
89        _activeClipPlanes->AddItem(_cameraClipPlanes[1]);
90    // top
91    _cameraClipPlanes[2] = vtkSmartPointer<vtkPlane>::New();
92    _cameraClipPlanes[2]->SetNormal(0, -1, 0);
93    _cameraClipPlanes[2]->SetOrigin(0, 1, 0);
94    if (_cameraMode == IMAGE)
95        _activeClipPlanes->AddItem(_cameraClipPlanes[2]);
96    // right
97    _cameraClipPlanes[3] = vtkSmartPointer<vtkPlane>::New();
98    _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
99    _cameraClipPlanes[3]->SetOrigin(1, 0, 0);
100    if (_cameraMode == IMAGE)
101        _activeClipPlanes->AddItem(_cameraClipPlanes[3]);
102    _renderer = vtkSmartPointer<vtkRenderer>::New();
103    vtkSmartPointer<vtkLight> headlight = vtkSmartPointer<vtkLight>::New();
104    headlight->SetLightTypeToHeadlight();
105    headlight->PositionalOff();
106    //headlight->SetAmbientColor(1, 1, 1);
107    _renderer->SetAmbient(.2, .2, .2);
108    _renderer->AddLight(headlight);
109    vtkSmartPointer<vtkLight> skylight = vtkSmartPointer<vtkLight>::New();
110    skylight->SetLightTypeToCameraLight();
111    skylight->SetPosition(0, 1, 0);
112    skylight->SetFocalPoint(0, 0, 0);
113    skylight->PositionalOff();
114    //skylight->SetAmbientColor(1, 1, 1);
115    _renderer->AddLight(skylight);
116    _renderer->LightFollowCameraOn();
117    _renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
118#ifdef USE_OFFSCREEN_RENDERING
119    _renderWindow->DoubleBufferOff();
120    _renderWindow->OffScreenRenderingOn();
121#else
122    _renderWindow->SwapBuffersOff();
123#endif
124    _renderWindow->SetSize(_windowWidth, _windowHeight);
125    // Next 2 options needed to support depth peeling
126    _renderWindow->SetAlphaBitPlanes(1);
127    _renderWindow->SetMultiSamples(0);
128    _renderer->SetMaximumNumberOfPeels(100);
129    _renderer->SetUseDepthPeeling(1);
130    _renderWindow->AddRenderer(_renderer);
131    setViewAngle(_windowHeight);
132    initAxes();
133    initCamera();
134    addColorMap("default", ColorMap::getDefault());
135    addColorMap("grayDefault", ColorMap::getGrayDefault());
136    addColorMap("volumeDefault", ColorMap::getVolumeDefault());
137    addColorMap("elementDefault", ColorMap::getElementDefault());
138}
139
140Renderer::~Renderer()
141{
142    TRACE("Enter");
143    TRACE("Deleting Contour2Ds");
144    for (Contour2DHashmap::iterator itr = _contour2Ds.begin();
145         itr != _contour2Ds.end(); ++itr) {
146        delete itr->second;
147    }
148    _contour2Ds.clear();
149    TRACE("Deleting Contour3Ds");
150    for (Contour3DHashmap::iterator itr = _contour3Ds.begin();
151         itr != _contour3Ds.end(); ++itr) {
152        delete itr->second;
153    }
154    _contour3Ds.clear();
155    TRACE("Deleting Cutplanes");
156    for (CutplaneHashmap::iterator itr = _cutplanes.begin();
157         itr != _cutplanes.end(); ++itr) {
158        delete itr->second;
159    }
160    _cutplanes.clear();
161    TRACE("Deleting Glyphs");
162    for (GlyphsHashmap::iterator itr = _glyphs.begin();
163         itr != _glyphs.end(); ++itr) {
164        delete itr->second;
165    }
166    _glyphs.clear();
167    TRACE("Deleting HeightMaps");
168    for (HeightMapHashmap::iterator itr = _heightMaps.begin();
169         itr != _heightMaps.end(); ++itr) {
170        delete itr->second;
171    }
172    _heightMaps.clear();
173    TRACE("Deleting LICs");
174    for (LICHashmap::iterator itr = _lics.begin();
175         itr != _lics.end(); ++itr) {
176        delete itr->second;
177    }
178    _lics.clear();
179    TRACE("Deleting Molecules");
180    for (MoleculeHashmap::iterator itr = _molecules.begin();
181         itr != _molecules.end(); ++itr) {
182        delete itr->second;
183    }
184    _molecules.clear();
185    TRACE("Deleting PolyDatas");
186    for (PolyDataHashmap::iterator itr = _polyDatas.begin();
187         itr != _polyDatas.end(); ++itr) {
188        delete itr->second;
189    }
190    _polyDatas.clear();
191    TRACE("Deleting PseudoColors");
192    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
193         itr != _pseudoColors.end(); ++itr) {
194        delete itr->second;
195    }
196    _pseudoColors.clear();
197    TRACE("Deleting Streamlines");
198    for (StreamlinesHashmap::iterator itr = _streamlines.begin();
199         itr != _streamlines.end(); ++itr) {
200        delete itr->second;
201    }
202    _streamlines.clear();
203    TRACE("Deleting Volumes");
204    for (VolumeHashmap::iterator itr = _volumes.begin();
205         itr != _volumes.end(); ++itr) {
206        delete itr->second;
207    }
208    _volumes.clear();
209    TRACE("Deleting ColorMaps");
210    // Delete color maps and data sets last in case references still
211    // exist
212    for (ColorMapHashmap::iterator itr = _colorMaps.begin();
213         itr != _colorMaps.end(); ++itr) {
214        delete itr->second;
215    }
216    _colorMaps.clear();
217    TRACE("Deleting DataSets");
218    for (DataSetHashmap::iterator itr = _dataSets.begin();
219         itr != _dataSets.end(); ++itr) {
220        delete itr->second;
221    }
222    _dataSets.clear();
223
224    clearFieldRanges();
225
226    TRACE("Leave");
227}
228
229/**
230 * \brief Add a DataSet to this Renderer
231 *
232 * This just adds the DataSet to the Renderer's list of data sets.
233 * In order to render the data, a graphics object using the data
234 * set must be added to the Renderer.
235 */
236void Renderer::addDataSet(const DataSetId& id)
237{
238    if (getDataSet(id) != NULL) {
239        WARN("Replacing existing DataSet %s", id.c_str());
240        deleteDataSet(id);
241    }
242    _dataSets[id] = new DataSet(id);
243}
244
245/**
246 * \brief Remove the specified DataSet and associated rendering objects
247 *
248 * The underlying DataSet and any associated graphics
249 * objects are deleted, freeing the memory used.
250 */
251void Renderer::deleteDataSet(const DataSetId& id)
252{
253    DataSetHashmap::iterator itr;
254
255    bool doAll = false;
256
257    if (id.compare("all") == 0) {
258        itr = _dataSets.begin();
259        doAll = true;
260    } else {
261        itr = _dataSets.find(id);
262    }
263    if (itr == _dataSets.end()) {
264        ERROR("Unknown dataset %s", id.c_str());
265        return;
266    }
267
268    do {
269        TRACE("Deleting dataset %s", itr->second->getName().c_str());
270
271        deleteGraphicsObject<Contour2D>(itr->second->getName());
272        deleteGraphicsObject<Contour3D>(itr->second->getName());
273        deleteGraphicsObject<Cutplane>(itr->second->getName());
274        deleteGraphicsObject<Glyphs>(itr->second->getName());
275        deleteGraphicsObject<HeightMap>(itr->second->getName());
276        deleteGraphicsObject<LIC>(itr->second->getName());
277        deleteGraphicsObject<Molecule>(itr->second->getName());
278        deleteGraphicsObject<PolyData>(itr->second->getName());
279        deleteGraphicsObject<PseudoColor>(itr->second->getName());
280        deleteGraphicsObject<Streamlines>(itr->second->getName());
281        deleteGraphicsObject<Volume>(itr->second->getName());
282 
283        if (itr->second->getProp() != NULL) {
284            _renderer->RemoveViewProp(itr->second->getProp());
285        }
286
287        TRACE("After deleting graphics objects");
288
289        delete itr->second;
290        itr = _dataSets.erase(itr);
291    } while (doAll && itr != _dataSets.end());
292
293    // Update cumulative data range
294    initFieldRanges();
295    updateFieldRanges();
296
297    initCamera();
298    _needsRedraw = true;
299}
300
301void Renderer::getDataSetNames(std::vector<std::string>& names)
302{
303    names.clear();
304    for (DataSetHashmap::iterator itr = _dataSets.begin();
305         itr != _dataSets.end(); ++itr) {
306        names.push_back(itr->second->getName());
307    }
308}
309
310/**
311 * \brief Find the DataSet for the given DataSetId key
312 *
313 * \return A pointer to the DataSet, or NULL if not found
314 */
315DataSet *Renderer::getDataSet(const DataSetId& id)
316{
317    DataSetHashmap::iterator itr = _dataSets.find(id);
318    if (itr == _dataSets.end()) {
319#ifdef DEBUG
320        TRACE("DataSet not found: %s", id.c_str());
321#endif
322        return NULL;
323    } else
324        return itr->second;
325}
326
327/**
328 * \brief (Re-)load the data for the specified DataSet key from a file
329 */
330bool Renderer::setDataFile(const DataSetId& id, const char *filename)
331{
332    DataSet *ds = getDataSet(id);
333    if (ds) {
334        bool ret = ds->setDataFile(filename);
335        initFieldRanges();
336        updateFieldRanges();
337        _needsRedraw = true;
338        return ret;
339    } else
340        return false;
341}
342
343/**
344 * \brief (Re-)load the data for the specified DataSet key from a memory buffer
345 */
346bool Renderer::setData(const DataSetId& id, char *data, int nbytes)
347{
348    DataSet *ds = getDataSet(id);
349    if (ds) {
350        bool ret = ds->setData(data, nbytes);
351        initFieldRanges();
352        updateFieldRanges();
353        _needsRedraw = true;
354        return ret;
355    } else
356        return false;
357}
358
359bool Renderer::setDataSetActiveScalars(const DataSetId& id, const char *scalarName)
360{
361    DataSetHashmap::iterator itr;
362
363    bool doAll = false;
364
365    if (id.compare("all") == 0) {
366        itr = _dataSets.begin();
367        doAll = true;
368    } else {
369        itr = _dataSets.find(id);
370    }
371    if (itr == _dataSets.end()) {
372        ERROR("DataSet not found: %s", id.c_str());
373        return false;
374    }
375
376    bool ret = true;
377    bool needRangeUpdate = false;
378    do {
379        if (strcmp(itr->second->getActiveScalarsName(), scalarName) != 0) {
380            if (!itr->second->setActiveScalars(scalarName)) {
381                ret = false;
382            } else {
383                needRangeUpdate = true;
384            }
385        }
386    } while (doAll && ++itr != _dataSets.end());
387
388    if (needRangeUpdate) {
389         updateFieldRanges();
390        _needsRedraw = true;
391    }
392
393    return ret;
394}
395
396bool Renderer::setDataSetActiveVectors(const DataSetId& id, const char *vectorName)
397{
398    DataSetHashmap::iterator itr;
399
400    bool doAll = false;
401
402    if (id.compare("all") == 0) {
403        itr = _dataSets.begin();
404        doAll = true;
405    } else {
406        itr = _dataSets.find(id);
407    }
408    if (itr == _dataSets.end()) {
409        ERROR("DataSet not found: %s", id.c_str());
410        return false;
411    }
412
413    bool ret = true;
414    bool needRangeUpdate = false;
415    do {
416        if (strcmp(itr->second->getActiveVectorsName(), vectorName) != 0) {
417            if (!itr->second->setActiveVectors(vectorName)) {
418                ret = false;
419            } else {
420                needRangeUpdate = true;
421            }
422        }
423    } while (doAll && ++itr != _dataSets.end());
424
425    if (needRangeUpdate) {
426        updateFieldRanges();
427        _needsRedraw = true;
428    }
429
430    return ret;
431}
432
433/**
434 * \brief Control whether the cumulative data range of datasets is used for
435 * colormapping and contours
436 */
437void Renderer::setUseCumulativeDataRange(bool state, bool onlyVisible)
438{
439    if (state != _useCumulativeRange) {
440        _useCumulativeRange = state;
441        _cumulativeRangeOnlyVisible = onlyVisible;
442        updateFieldRanges();
443        _needsRedraw = true;
444    }
445}
446
447void Renderer::resetAxes(double bounds[6])
448{
449    TRACE("Resetting axes");
450    if (_cubeAxesActor == NULL ||
451        _cubeAxesActor2D == NULL) {
452        initAxes();
453    }
454    if (_cameraMode == IMAGE) {
455        if (_renderer->HasViewProp(_cubeAxesActor)) {
456            TRACE("Removing 3D axes");
457            _renderer->RemoveViewProp(_cubeAxesActor);
458        }
459        if (!_renderer->HasViewProp(_cubeAxesActor2D)) {
460            TRACE("Adding 2D axes");
461            _renderer->AddViewProp(_cubeAxesActor2D);
462        }
463    } else {
464        if (_renderer->HasViewProp(_cubeAxesActor2D)) {
465            TRACE("Removing 2D axes");
466            _renderer->RemoveViewProp(_cubeAxesActor2D);
467        }
468        if (!_renderer->HasViewProp(_cubeAxesActor)) {
469            TRACE("Adding 3D axes");
470            _renderer->AddViewProp(_cubeAxesActor);
471        }
472        if (bounds == NULL) {
473            double newBounds[6];
474            collectBounds(newBounds, false);
475#if ((VTK_MAJOR_VERSION > 5) || (VTK_MAJOR_VERSION == 5 && VTK_MINOR_VERSION >= 8))
476            _cubeAxesActor->SetXAxisRange(newBounds[0], newBounds[1]);
477            _cubeAxesActor->SetYAxisRange(newBounds[2], newBounds[3]);
478            _cubeAxesActor->SetZAxisRange(newBounds[4], newBounds[5]);
479#endif
480            _cubeAxesActor->SetBounds(newBounds);
481            TRACE("Bounds (computed): %g %g %g %g %g %g",
482                  newBounds[0],
483                  newBounds[1],
484                  newBounds[2],
485                  newBounds[3],
486                  newBounds[4],
487                  newBounds[5]);
488        } else {
489#if ((VTK_MAJOR_VERSION > 5) || (VTK_MAJOR_VERSION == 5 && VTK_MINOR_VERSION >= 8))
490            _cubeAxesActor->SetXAxisRange(bounds[0], bounds[1]);
491            _cubeAxesActor->SetYAxisRange(bounds[2], bounds[3]);
492            _cubeAxesActor->SetZAxisRange(bounds[4], bounds[5]);
493#endif
494            _cubeAxesActor->SetBounds(bounds);
495            TRACE("Bounds (supplied): %g %g %g %g %g %g",
496                  bounds[0],
497                  bounds[1],
498                  bounds[2],
499                  bounds[3],
500                  bounds[4],
501                  bounds[5]);
502        }
503    }
504}
505
506/**
507 * \brief Set inital properties on the 2D Axes
508 */
509void Renderer::initAxes()
510{
511    TRACE("Initializing axes");
512    if (_cubeAxesActor == NULL)
513        _cubeAxesActor = vtkSmartPointer<vtkCubeAxesActor>::New();
514    _cubeAxesActor->SetCamera(_renderer->GetActiveCamera());
515    _cubeAxesActor->GetProperty()->LightingOff();
516    // Don't offset labels at origin
517    _cubeAxesActor->SetCornerOffset(0);
518    _cubeAxesActor->SetFlyModeToStaticTriad();
519
520#ifdef USE_CUSTOM_AXES
521    if (_cubeAxesActor2D == NULL)
522        _cubeAxesActor2D = vtkSmartPointer<vtkRpCubeAxesActor2D>::New();
523#else
524    if (_cubeAxesActor2D == NULL)
525        _cubeAxesActor2D = vtkSmartPointer<vtkCubeAxesActor2D>::New();
526#endif
527
528    _cubeAxesActor2D->SetCamera(_renderer->GetActiveCamera());
529    _cubeAxesActor2D->ZAxisVisibilityOff();
530    _cubeAxesActor2D->SetCornerOffset(0);
531    _cubeAxesActor2D->SetFlyModeToClosestTriad();
532
533    _cubeAxesActor2D->ScalingOff();
534    //_cubeAxesActor2D->SetShowActualBounds(0);
535    _cubeAxesActor2D->SetFontFactor(1.25);
536    // Use "nice" range and number of ticks/labels
537    _cubeAxesActor2D->GetXAxisActor2D()->AdjustLabelsOn();
538    _cubeAxesActor2D->GetYAxisActor2D()->AdjustLabelsOn();
539    _cubeAxesActor2D->GetZAxisActor2D()->AdjustLabelsOn();
540
541#ifdef USE_CUSTOM_AXES
542    _cubeAxesActor2D->SetAxisTitleTextProperty(NULL);
543    _cubeAxesActor2D->SetAxisLabelTextProperty(NULL);
544    //_cubeAxesActor2D->GetXAxisActor2D()->SizeFontRelativeToAxisOn();
545    _cubeAxesActor2D->GetXAxisActor2D()->GetTitleTextProperty()->BoldOn();
546    _cubeAxesActor2D->GetXAxisActor2D()->GetTitleTextProperty()->ItalicOff();
547    _cubeAxesActor2D->GetXAxisActor2D()->GetTitleTextProperty()->ShadowOn();
548    _cubeAxesActor2D->GetXAxisActor2D()->GetLabelTextProperty()->BoldOff();
549    _cubeAxesActor2D->GetXAxisActor2D()->GetLabelTextProperty()->ItalicOff();
550    _cubeAxesActor2D->GetXAxisActor2D()->GetLabelTextProperty()->ShadowOff();
551
552    //_cubeAxesActor2D->GetYAxisActor2D()->SizeFontRelativeToAxisOn();
553    _cubeAxesActor2D->GetYAxisActor2D()->GetTitleTextProperty()->BoldOn();
554    _cubeAxesActor2D->GetYAxisActor2D()->GetTitleTextProperty()->ItalicOff();
555    _cubeAxesActor2D->GetYAxisActor2D()->GetTitleTextProperty()->ShadowOn();
556    _cubeAxesActor2D->GetYAxisActor2D()->GetLabelTextProperty()->BoldOff();
557    _cubeAxesActor2D->GetYAxisActor2D()->GetLabelTextProperty()->ItalicOff();
558    _cubeAxesActor2D->GetYAxisActor2D()->GetLabelTextProperty()->ShadowOff();
559
560    //_cubeAxesActor2D->GetZAxisActor2D()->SizeFontRelativeToAxisOn();
561    _cubeAxesActor2D->GetZAxisActor2D()->GetTitleTextProperty()->BoldOn();
562    _cubeAxesActor2D->GetZAxisActor2D()->GetTitleTextProperty()->ItalicOff();
563    _cubeAxesActor2D->GetZAxisActor2D()->GetTitleTextProperty()->ShadowOn();
564    _cubeAxesActor2D->GetZAxisActor2D()->GetLabelTextProperty()->BoldOff();
565    _cubeAxesActor2D->GetZAxisActor2D()->GetLabelTextProperty()->ItalicOff();
566    _cubeAxesActor2D->GetZAxisActor2D()->GetLabelTextProperty()->ShadowOff();
567#else
568    _cubeAxesActor2D->GetAxisTitleTextProperty()->BoldOn();
569    _cubeAxesActor2D->GetAxisTitleTextProperty()->ItalicOff();
570    _cubeAxesActor2D->GetAxisTitleTextProperty()->ShadowOn();
571    _cubeAxesActor2D->GetAxisLabelTextProperty()->BoldOff();
572    _cubeAxesActor2D->GetAxisLabelTextProperty()->ItalicOff();
573    _cubeAxesActor2D->GetAxisLabelTextProperty()->ShadowOff();
574#endif
575
576    if (_cameraMode == IMAGE) {
577        if (!_renderer->HasViewProp(_cubeAxesActor2D))
578            _renderer->AddViewProp(_cubeAxesActor2D);
579    } else {
580        if (!_renderer->HasViewProp(_cubeAxesActor))
581            _renderer->AddViewProp(_cubeAxesActor);
582    }
583}
584
585/**
586 * \brief Set Fly mode of axes
587 */
588void Renderer::setAxesFlyMode(AxesFlyMode mode)
589{
590    if (_cubeAxesActor == NULL)
591        initAxes();
592    switch (mode) {
593    case FLY_STATIC_EDGES:
594        _cubeAxesActor->SetFlyModeToStaticEdges();
595        break;
596    case FLY_STATIC_TRIAD:
597        _cubeAxesActor->SetFlyModeToStaticTriad();
598        break;
599    case FLY_OUTER_EDGES:
600        _cubeAxesActor->SetFlyModeToOuterEdges();
601        break;
602    case FLY_FURTHEST_TRIAD:
603        _cubeAxesActor->SetFlyModeToFurthestTriad();
604        break;
605    case FLY_CLOSEST_TRIAD:
606    default:
607        _cubeAxesActor->SetFlyModeToClosestTriad();
608        break;
609    }
610    _needsRedraw = true;
611}
612
613/**
614 * \brief Set color of axes, ticks, labels, titles
615 */
616void Renderer::setAxesColor(double color[3])
617{
618    if (_cubeAxesActor != NULL) {
619        _cubeAxesActor->GetProperty()->SetColor(color);
620        _needsRedraw = true;
621    }
622    if (_cubeAxesActor2D != NULL) {
623        _cubeAxesActor2D->GetProperty()->SetColor(color);
624#ifdef USE_CUSTOM_AXES
625        _cubeAxesActor2D->GetXAxisActor2D()->GetTitleTextProperty()->SetColor(color);
626        _cubeAxesActor2D->GetXAxisActor2D()->GetLabelTextProperty()->SetColor(color);
627        _cubeAxesActor2D->GetYAxisActor2D()->GetTitleTextProperty()->SetColor(color);
628        _cubeAxesActor2D->GetYAxisActor2D()->GetLabelTextProperty()->SetColor(color);
629        _cubeAxesActor2D->GetZAxisActor2D()->GetTitleTextProperty()->SetColor(color);
630        _cubeAxesActor2D->GetZAxisActor2D()->GetLabelTextProperty()->SetColor(color);
631#else
632        _cubeAxesActor2D->GetAxisTitleTextProperty()->SetColor(color);
633        _cubeAxesActor2D->GetAxisLabelTextProperty()->SetColor(color);
634#endif
635        _needsRedraw = true;
636    }
637}
638
639/**
640 * \brief Turn on/off rendering of all enabled axes
641 */
642void Renderer::setAxesVisibility(bool state)
643{
644    if (_cubeAxesActor != NULL) {
645        _cubeAxesActor->SetVisibility((state ? 1 : 0));
646        _needsRedraw = true;
647    }
648    if (_cubeAxesActor2D != NULL) {
649        _cubeAxesActor2D->SetVisibility((state ? 1 : 0));
650        _needsRedraw = true;
651    }
652}
653
654/**
655 * \brief Turn on/off rendering of all axes gridlines
656 */
657void Renderer::setAxesGridVisibility(bool state)
658{
659    setAxisGridVisibility(X_AXIS, state);
660    setAxisGridVisibility(Y_AXIS, state);
661    setAxisGridVisibility(Z_AXIS, state);
662}
663
664/**
665 * \brief Turn on/off rendering of all axis labels
666 */
667void Renderer::setAxesLabelVisibility(bool state)
668{
669    setAxisLabelVisibility(X_AXIS, state);
670    setAxisLabelVisibility(Y_AXIS, state);
671    setAxisLabelVisibility(Z_AXIS, state);
672}
673
674/**
675 * \brief Turn on/off rendering of all axis ticks
676 */
677void Renderer::setAxesTickVisibility(bool state)
678{
679    setAxisTickVisibility(X_AXIS, state);
680    setAxisTickVisibility(Y_AXIS, state);
681    setAxisTickVisibility(Z_AXIS, state);
682}
683
684/**
685 * \brief Control position of ticks on 3D axes
686 */
687void Renderer::setAxesTickPosition(AxesTickPosition pos)
688{
689    if (_cubeAxesActor == NULL)
690        return;
691
692    switch (pos) {
693    case TICKS_BOTH:
694        _cubeAxesActor->SetTickLocationToBoth();
695        break;
696    case TICKS_OUTSIDE:
697        _cubeAxesActor->SetTickLocationToOutside();
698        break;
699    case TICKS_INSIDE:
700    default:
701        _cubeAxesActor->SetTickLocationToInside();
702        break;
703    }
704    _needsRedraw = true;
705}
706
707/**
708 * \brief Turn on/off rendering of the specified axis
709 */
710void Renderer::setAxisVisibility(Axis axis, bool state)
711{
712    if (_cubeAxesActor != NULL) {
713        if (axis == X_AXIS) {
714            _cubeAxesActor->SetXAxisVisibility((state ? 1 : 0));
715        } else if (axis == Y_AXIS) {
716            _cubeAxesActor->SetYAxisVisibility((state ? 1 : 0));
717        } else if (axis == Z_AXIS) {
718            _cubeAxesActor->SetZAxisVisibility((state ? 1 : 0));
719        }
720        _needsRedraw = true;
721    }
722    if (_cubeAxesActor2D != NULL) {
723        if (axis == X_AXIS) {
724            _cubeAxesActor2D->SetXAxisVisibility((state ? 1 : 0));
725        } else if (axis == Y_AXIS) {
726            _cubeAxesActor2D->SetYAxisVisibility((state ? 1 : 0));
727        } else if (axis == Z_AXIS) {
728            _cubeAxesActor2D->SetZAxisVisibility((state ? 1 : 0));
729        }
730        _needsRedraw = true;
731    }
732}
733
734/**
735 * \brief Turn on/off rendering of single axis gridlines
736 */
737void Renderer::setAxisGridVisibility(Axis axis, bool state)
738{
739    if (_cubeAxesActor != NULL) {
740        if (axis == X_AXIS) {
741            _cubeAxesActor->SetDrawXGridlines((state ? 1 : 0));
742        } else if (axis == Y_AXIS) {
743            _cubeAxesActor->SetDrawYGridlines((state ? 1 : 0));
744        } else if (axis == Z_AXIS) {
745            _cubeAxesActor->SetDrawZGridlines((state ? 1 : 0));
746        }
747        _needsRedraw = true;
748    }
749}
750
751/**
752 * \brief Toggle label visibility for the specified axis
753 */
754void Renderer::setAxisLabelVisibility(Axis axis, bool state)
755{
756    if (_cubeAxesActor != NULL) {
757        if (axis == X_AXIS) {
758            _cubeAxesActor->SetXAxisLabelVisibility((state ? 1 : 0));
759        } else if (axis == Y_AXIS) {
760            _cubeAxesActor->SetYAxisLabelVisibility((state ? 1 : 0));
761        } else if (axis == Z_AXIS) {
762            _cubeAxesActor->SetZAxisLabelVisibility((state ? 1 : 0));
763        }
764        _needsRedraw = true;
765    }
766    if (_cubeAxesActor2D != NULL) {
767        if (axis == X_AXIS) {
768            _cubeAxesActor2D->GetXAxisActor2D()->SetLabelVisibility((state ? 1 : 0));
769        } else if (axis == Y_AXIS) {
770            _cubeAxesActor2D->GetYAxisActor2D()->SetLabelVisibility((state ? 1 : 0));
771        } else if (axis == Z_AXIS) {
772            _cubeAxesActor2D->GetZAxisActor2D()->SetLabelVisibility((state ? 1 : 0));
773        }
774        _needsRedraw = true;
775    }
776}
777
778/**
779 * \brief Toggle tick visibility for the specified axis
780 */
781void Renderer::setAxisTickVisibility(Axis axis, bool state)
782{
783    if (_cubeAxesActor != NULL) {
784        if (axis == X_AXIS) {
785            _cubeAxesActor->SetXAxisTickVisibility((state ? 1 : 0));
786        } else if (axis == Y_AXIS) {
787            _cubeAxesActor->SetYAxisTickVisibility((state ? 1 : 0));
788        } else if (axis == Z_AXIS) {
789            _cubeAxesActor->SetZAxisTickVisibility((state ? 1 : 0));
790        }
791        _needsRedraw = true;
792    }
793    if (_cubeAxesActor2D != NULL) {
794        if (axis == X_AXIS) {
795            _cubeAxesActor2D->GetXAxisActor2D()->SetTickVisibility((state ? 1 : 0));
796        } else if (axis == Y_AXIS) {
797            _cubeAxesActor2D->GetYAxisActor2D()->SetTickVisibility((state ? 1 : 0));
798        } else if (axis == Z_AXIS) {
799            _cubeAxesActor2D->GetZAxisActor2D()->SetTickVisibility((state ? 1 : 0));
800        }
801        _needsRedraw = true;
802    }
803}
804
805/**
806 * \brief Set title of the specified axis
807 */
808void Renderer::setAxisTitle(Axis axis, const char *title)
809{
810    if (_cubeAxesActor != NULL) {
811        if (axis == X_AXIS) {
812            _cubeAxesActor->SetXTitle(title);
813        } else if (axis == Y_AXIS) {
814            _cubeAxesActor->SetYTitle(title);
815        } else if (axis == Z_AXIS) {
816            _cubeAxesActor->SetZTitle(title);
817        }
818        _needsRedraw = true;
819    }
820    if (_cubeAxesActor2D != NULL) {
821        if (axis == X_AXIS) {
822            _cubeAxesActor2D->SetXLabel(title);
823        } else if (axis == Y_AXIS) {
824            _cubeAxesActor2D->SetYLabel(title);
825        } else if (axis == Z_AXIS) {
826            _cubeAxesActor2D->SetZLabel(title);
827        }
828        _needsRedraw = true;
829    }
830}
831
832/**
833 * \brief Set units of the specified axis
834 */
835void Renderer::setAxisUnits(Axis axis, const char *units)
836{
837    if (_cubeAxesActor != NULL) {
838        if (axis == X_AXIS) {
839            _cubeAxesActor->SetXUnits(units);
840        } else if (axis == Y_AXIS) {
841            _cubeAxesActor->SetYUnits(units);
842        } else if (axis == Z_AXIS) {
843            _cubeAxesActor->SetZUnits(units);
844        }
845        _needsRedraw = true;
846    }
847#ifdef notdef
848    if (_cubeAxesActor2D != NULL) {
849        if (axis == X_AXIS) {
850            _cubeAxesActor2D->SetXUnits(units);
851        } else if (axis == Y_AXIS) {
852            _cubeAxesActor2D->SetYUnits(units);
853        } else if (axis == Z_AXIS) {
854            _cubeAxesActor2D->SetZUnits(units);
855        }
856        _needsRedraw = true;
857    }
858#endif
859}
860
861/**
862 * \brief Notify graphics objects that color map has changed
863 */
864void Renderer::updateColorMap(ColorMap *cmap)
865{
866    for (Contour3DHashmap::iterator itr = _contour3Ds.begin();
867         itr != _contour3Ds.end(); ++itr) {
868        if (itr->second->getColorMap() == cmap) {
869            itr->second->updateColorMap();
870            _needsRedraw = true;
871        }
872    }
873    for (CutplaneHashmap::iterator itr = _cutplanes.begin();
874         itr != _cutplanes.end(); ++itr) {
875        if (itr->second->getColorMap() == cmap) {
876            itr->second->updateColorMap();
877            _needsRedraw = true;
878        }
879    }
880    for (GlyphsHashmap::iterator itr = _glyphs.begin();
881         itr != _glyphs.end(); ++itr) {
882        if (itr->second->getColorMap() == cmap) {
883            itr->second->updateColorMap();
884            _needsRedraw = true;
885        }
886    }
887    for (HeightMapHashmap::iterator itr = _heightMaps.begin();
888         itr != _heightMaps.end(); ++itr) {
889        if (itr->second->getColorMap() == cmap) {
890            itr->second->updateColorMap();
891            _needsRedraw = true;
892        }
893    }
894    for (LICHashmap::iterator itr = _lics.begin();
895         itr != _lics.end(); ++itr) {
896        if (itr->second->getColorMap() == cmap) {
897            itr->second->updateColorMap();
898            _needsRedraw = true;
899        }
900    }
901    for (MoleculeHashmap::iterator itr = _molecules.begin();
902         itr != _molecules.end(); ++itr) {
903        if (itr->second->getColorMap() == cmap) {
904            itr->second->updateColorMap();
905            _needsRedraw = true;
906        }
907    }
908    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
909         itr != _pseudoColors.end(); ++itr) {
910        if (itr->second->getColorMap() == cmap) {
911            itr->second->updateColorMap();
912            _needsRedraw = true;
913        }
914    }
915    for (StreamlinesHashmap::iterator itr = _streamlines.begin();
916         itr != _streamlines.end(); ++itr) {
917        if (itr->second->getColorMap() == cmap) {
918            itr->second->updateColorMap();
919            _needsRedraw = true;
920        }
921    }
922    for (VolumeHashmap::iterator itr = _volumes.begin();
923         itr != _volumes.end(); ++itr) {
924        if (itr->second->getColorMap() == cmap) {
925            itr->second->updateColorMap();
926            _needsRedraw = true;
927        }
928    }
929}
930
931/**
932 * \brief Check if a ColorMap is in use by graphics objects
933 */
934bool Renderer::colorMapUsed(ColorMap *cmap)
935{
936    for (Contour3DHashmap::iterator itr = _contour3Ds.begin();
937         itr != _contour3Ds.end(); ++itr) {
938        if (itr->second->getColorMap() == cmap)
939            return true;
940    }
941    for (CutplaneHashmap::iterator itr = _cutplanes.begin();
942         itr != _cutplanes.end(); ++itr) {
943        if (itr->second->getColorMap() == cmap)
944            return true;
945    }
946    for (GlyphsHashmap::iterator itr = _glyphs.begin();
947         itr != _glyphs.end(); ++itr) {
948        if (itr->second->getColorMap() == cmap)
949            return true;
950    }
951    for (HeightMapHashmap::iterator itr = _heightMaps.begin();
952         itr != _heightMaps.end(); ++itr) {
953        if (itr->second->getColorMap() == cmap)
954            return true;
955    }
956    for (LICHashmap::iterator itr = _lics.begin();
957         itr != _lics.end(); ++itr) {
958        if (itr->second->getColorMap() == cmap)
959            return true;
960    }
961    for (MoleculeHashmap::iterator itr = _molecules.begin();
962         itr != _molecules.end(); ++itr) {
963        if (itr->second->getColorMap() == cmap)
964            return true;
965    }
966    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
967         itr != _pseudoColors.end(); ++itr) {
968        if (itr->second->getColorMap() == cmap)
969            return true;
970    }
971    for (StreamlinesHashmap::iterator itr = _streamlines.begin();
972         itr != _streamlines.end(); ++itr) {
973        if (itr->second->getColorMap() == cmap)
974            return true;
975    }
976    for (VolumeHashmap::iterator itr = _volumes.begin();
977         itr != _volumes.end(); ++itr) {
978        if (itr->second->getColorMap() == cmap)
979            return true;
980    }
981    return false;
982}
983
984/**
985 * \brief Add/replace a ColorMap for use in the Renderer
986 */
987void Renderer::addColorMap(const ColorMapId& id, ColorMap *colorMap)
988{
989    if (colorMap != NULL) {
990        colorMap->build();
991        if (getColorMap(id) != NULL) {
992            TRACE("Replacing existing ColorMap %s", id.c_str());
993            // Copy to current colormap to avoid invalidating
994            // pointers in graphics objects using the color map
995            *_colorMaps[id] = *colorMap;
996            delete colorMap;
997            // Notify graphics objects of change
998            updateColorMap(_colorMaps[id]);
999        } else
1000            _colorMaps[id] = colorMap;
1001    } else {
1002        ERROR("NULL ColorMap");
1003    }
1004}
1005
1006/**
1007 * \brief Return the ColorMap associated with the colormap key given
1008 */
1009ColorMap *Renderer::getColorMap(const ColorMapId& id)
1010{
1011    ColorMapHashmap::iterator itr = _colorMaps.find(id);
1012
1013    if (itr == _colorMaps.end())
1014        return NULL;
1015    else
1016        return itr->second;
1017}
1018
1019/**
1020 * \brief Remove the colormap associated with the key given
1021 *
1022 * The underlying vtkLookupTable will be deleted if it is not referenced
1023 * by any other objects
1024 */
1025void Renderer::deleteColorMap(const ColorMapId& id)
1026{
1027    ColorMapHashmap::iterator itr;
1028
1029    bool doAll = false;
1030
1031    if (id.compare("all") == 0) {
1032        itr = _colorMaps.begin();
1033        doAll = true;
1034    } else {
1035        itr = _colorMaps.find(id);
1036    }
1037
1038    if (itr == _colorMaps.end()) {
1039        ERROR("Unknown ColorMap %s", id.c_str());
1040        return;
1041    }
1042
1043    do {
1044        if (itr->second->getName().compare("default") == 0 ||
1045            itr->second->getName().compare("grayDefault") == 0 ||
1046            itr->second->getName().compare("volumeDefault") == 0 ||
1047            itr->second->getName().compare("elementDefault") == 0) {
1048            if (id.compare("all") != 0) {
1049                WARN("Cannot delete a default color map");
1050            }
1051            continue;
1052        } else if (colorMapUsed(itr->second)) {
1053            WARN("Cannot delete color map '%s', it is in use", itr->second->getName().c_str());
1054            continue;
1055        }
1056
1057        TRACE("Deleting ColorMap %s", itr->second->getName().c_str());
1058
1059        delete itr->second;
1060        itr = _colorMaps.erase(itr);
1061    } while (doAll && itr != _colorMaps.end());
1062}
1063
1064bool Renderer::renderColorMap(const ColorMapId& id,
1065                              const DataSetId& dataSetID,
1066                              Renderer::LegendType legendType,
1067                              std::string& title,
1068                              double range[2],
1069                              int width, int height,
1070                              int numLabels,
1071                              vtkUnsignedCharArray *imgData)
1072{
1073    DataSet *dataSet = NULL;
1074    if (dataSetID.compare("all") == 0) {
1075        if (_dataSets.empty()) {
1076            WARN("No DataSets exist, can't fill title or range");
1077            return renderColorMap(id, dataSetID, legendType,
1078                                  NULL,
1079                                  DataSet::POINT_DATA,
1080                                  title, range, width, height, numLabels, imgData);
1081        } else {
1082            dataSet = _dataSets.begin()->second;
1083        }
1084    } else {
1085        dataSet = getDataSet(dataSetID);
1086        if (dataSet == NULL) {
1087            ERROR("DataSet '%s' not found", dataSetID.c_str());
1088            return false;
1089        }
1090    }
1091
1092    if (legendType == LEGEND_SCALAR) {
1093        return renderColorMap(id, dataSetID, legendType,
1094                              dataSet->getActiveScalarsName(),
1095                              dataSet->getActiveScalarsType(),
1096                              title, range, width, height, numLabels, imgData);
1097    } else {
1098        return renderColorMap(id, dataSetID, legendType,
1099                              dataSet->getActiveVectorsName(),
1100                              dataSet->getActiveVectorsType(),
1101                              title, range, width, height, numLabels, imgData);
1102    }
1103}
1104
1105
1106bool Renderer::renderColorMap(const ColorMapId& id,
1107                              const DataSetId& dataSetID,
1108                              Renderer::LegendType legendType,
1109                              const char *fieldName,
1110                              std::string& title,
1111                              double range[2],
1112                              int width, int height,
1113                              int numLabels,
1114                              vtkUnsignedCharArray *imgData)
1115{
1116    DataSet *dataSet = NULL;
1117    if (dataSetID.compare("all") == 0) {
1118        if (_dataSets.empty()) {
1119            WARN("No DataSets exist, can't fill title or range");
1120            return renderColorMap(id, dataSetID, legendType,
1121                                  NULL,
1122                                  DataSet::POINT_DATA,
1123                                  title, range, width, height, numLabels, imgData);
1124        } else {
1125            dataSet = _dataSets.begin()->second;
1126        }
1127    } else {
1128        dataSet = getDataSet(dataSetID);
1129        if (dataSet == NULL) {
1130            ERROR("DataSet '%s' not found", dataSetID.c_str());
1131            return false;
1132        }
1133    }
1134
1135    DataSet::DataAttributeType attrType;
1136    int numComponents;
1137
1138    dataSet->getFieldInfo(fieldName, &attrType, &numComponents);
1139
1140    return renderColorMap(id, dataSetID, legendType,
1141                          fieldName,
1142                          attrType,
1143                          title, range, width, height, numLabels, imgData);
1144}
1145
1146/**
1147 * \brief Render a labelled legend image for the given colormap
1148 *
1149 * \param[in] id ColorMap name
1150 * \param[in] dataSetID DataSet name
1151 * \param[in] legendType scalar or vector field legend
1152 * \param[in,out] title If supplied, draw title ("#auto" means to
1153 * fill in field name and draw).  If blank, do not draw title. 
1154 * If title was blank or "#auto", will be filled with field name on
1155 * return
1156 * \param[out] range Filled with min and max values
1157 * \param[in] width Pixel width of legend (aspect controls orientation)
1158 * \param[in] height Pixel height of legend (aspect controls orientation)
1159 * \param[in] numLabels Number of labels to render (includes min/max)
1160 * \param[in,out] imgData Pointer to array to fill with image bytes. Array
1161 * will be resized if needed.
1162 * \return The image is rendered into the supplied array, false is
1163 * returned if the color map is not found
1164 */
1165bool Renderer::renderColorMap(const ColorMapId& id,
1166                              const DataSetId& dataSetID,
1167                              Renderer::LegendType legendType,
1168                              const char *fieldName,
1169                              DataSet::DataAttributeType type,
1170                              std::string& title,
1171                              double range[2],
1172                              int width, int height,
1173                              int numLabels,
1174                              vtkUnsignedCharArray *imgData)
1175{
1176    TRACE("Enter");
1177    ColorMap *colorMap = getColorMap(id);
1178    if (colorMap == NULL)
1179        return false;
1180
1181    if (_legendRenderWindow == NULL) {
1182        _legendRenderWindow = vtkSmartPointer<vtkRenderWindow>::New();
1183        _legendRenderWindow->SetMultiSamples(0);
1184#ifdef USE_OFFSCREEN_RENDERING
1185        _legendRenderWindow->DoubleBufferOff();
1186        _legendRenderWindow->OffScreenRenderingOn();
1187#else
1188        _legendRenderWindow->DoubleBufferOn();
1189        _legendRenderWindow->SwapBuffersOff();
1190#endif
1191    }
1192
1193    _legendRenderWindow->SetSize(width, height);
1194
1195    if (_legendRenderer == NULL) {
1196        _legendRenderer = vtkSmartPointer<vtkRenderer>::New();
1197        _legendRenderWindow->AddRenderer(_legendRenderer);
1198    }
1199    _legendRenderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
1200
1201    if (_scalarBarActor == NULL) {
1202        _scalarBarActor = vtkSmartPointer<vtkScalarBarActor>::New();
1203        _scalarBarActor->UseOpacityOn();
1204        _legendRenderer->AddViewProp(_scalarBarActor);
1205    }
1206
1207    if (width > height) {
1208        _scalarBarActor->SetOrientationToHorizontal();
1209    } else {
1210        _scalarBarActor->SetOrientationToVertical();
1211    }
1212
1213    // Set viewport-relative width/height/pos
1214    if (title.empty() && numLabels == 0) {
1215        _scalarBarActor->SetPosition(0, 0);
1216        if (width > height) {
1217            // horizontal
1218            _scalarBarActor->SetHeight(2.5); // VTK: floor(actorHeight * .4)
1219            _scalarBarActor->SetWidth(1);
1220        } else {
1221            // vertical
1222            _scalarBarActor->SetHeight((double)height/(.86*height)); // VTK: floor(actorHeight * .86)
1223            _scalarBarActor->SetWidth(((double)(width+5))/((double)width)); // VTK: actorWidth - 4 pixels
1224        }
1225    } else {
1226        if (width > height) {
1227            // horizontal
1228            _scalarBarActor->SetPosition(.075, .1);
1229            _scalarBarActor->SetHeight(0.8);
1230            _scalarBarActor->SetWidth(0.85);
1231        } else {
1232            // vertical
1233            _scalarBarActor->SetPosition(.1, .05);
1234            _scalarBarActor->SetHeight(0.9);
1235            _scalarBarActor->SetWidth(0.8);
1236        }
1237    }
1238
1239    vtkSmartPointer<vtkLookupTable> lut = colorMap->getLookupTable();
1240    DataSet *dataSet = NULL;
1241    bool cumulative = _useCumulativeRange;
1242    if (dataSetID.compare("all") == 0) {
1243        if (_dataSets.empty()) {
1244            WARN("No DataSets exist, can't fill title or range");
1245        } else {
1246            dataSet = _dataSets.begin()->second;
1247        }
1248        cumulative = true;
1249    } else {
1250        dataSet = getDataSet(dataSetID);
1251        if (dataSet == NULL) {
1252            ERROR("DataSet '%s' not found", dataSetID.c_str());
1253            return false;
1254        }
1255    }
1256
1257    bool drawTitle = false;
1258    if (!title.empty()) {
1259        drawTitle = true;
1260        if (title.compare("#auto") == 0) {
1261            title.clear();
1262        }
1263    }
1264
1265    range[0] = 0.0;
1266    range[1] = 1.0;
1267
1268    switch (legendType) {
1269    case LEGEND_VECTOR_MAGNITUDE:
1270        if (cumulative) {
1271            getCumulativeDataRange(range, fieldName, type, 3);
1272        } else if (dataSet != NULL) {
1273            dataSet->getDataRange(range, fieldName, type);
1274        }
1275
1276        lut->SetRange(range);
1277
1278        if (title.empty() && dataSet != NULL) {
1279            if (fieldName != NULL) {
1280                title = fieldName;
1281                title.append("(mag)");
1282            }
1283        }
1284        break;
1285    case LEGEND_VECTOR_X:
1286        if (cumulative) {
1287            getCumulativeDataRange(range, fieldName, type, 3, 0);
1288        } else if (dataSet != NULL) {
1289            dataSet->getDataRange(range, fieldName, type, 0);
1290        }
1291
1292        lut->SetRange(range);
1293
1294        if (title.empty() && dataSet != NULL) {
1295            if (fieldName != NULL) {
1296                title = fieldName;
1297                title.append("(x)");
1298            }
1299        }
1300        break;
1301    case LEGEND_VECTOR_Y:
1302        if (cumulative) {
1303            getCumulativeDataRange(range, fieldName, type, 3, 1);
1304        } else if (dataSet != NULL) {
1305            dataSet->getDataRange(range, fieldName, type, 1);
1306        }
1307
1308        lut->SetRange(range);
1309
1310        if (title.empty() && dataSet != NULL) {
1311            if (fieldName != NULL) {
1312                title = fieldName;
1313                title.append("(y)");
1314            }
1315        }
1316        break;
1317    case LEGEND_VECTOR_Z:
1318        if (cumulative) {
1319            getCumulativeDataRange(range, fieldName, type, 3, 2);
1320        } else if (dataSet != NULL) {
1321            dataSet->getDataRange(range, fieldName, type, 1);
1322        }
1323
1324        lut->SetRange(range);
1325
1326        if (title.empty() && dataSet != NULL) {
1327            if (fieldName != NULL) {
1328                title = fieldName;
1329                title.append("(z)");
1330            }
1331        }
1332        break;
1333    case LEGEND_SCALAR:
1334    default:
1335        if (cumulative) {
1336            getCumulativeDataRange(range, fieldName, type, 1);
1337        } else if (dataSet != NULL) {
1338            dataSet->getDataRange(range, fieldName, type);
1339         }
1340
1341        lut->SetRange(range);
1342
1343        if (title.empty() && dataSet != NULL) {
1344            if (fieldName != NULL)
1345                title = fieldName;
1346        }
1347        break;
1348    }
1349
1350    _scalarBarActor->SetLookupTable(lut);
1351
1352    if (drawTitle) {
1353        _scalarBarActor->SetTitle(title.c_str());
1354    } else {
1355        _scalarBarActor->SetTitle("");
1356    }
1357    _scalarBarActor->GetTitleTextProperty()->ItalicOff();
1358    _scalarBarActor->SetNumberOfLabels(numLabels);
1359    _scalarBarActor->GetLabelTextProperty()->BoldOff();
1360    _scalarBarActor->GetLabelTextProperty()->ItalicOff();
1361    _scalarBarActor->GetLabelTextProperty()->ShadowOff();
1362
1363    _legendRenderWindow->Render();
1364
1365#ifdef RENDER_TARGA
1366    _legendRenderWindow->MakeCurrent();
1367    // Must clear previous errors first.
1368    while (glGetError() != GL_NO_ERROR){
1369        ;
1370    }
1371    int bytesPerPixel = TARGA_BYTES_PER_PIXEL;
1372    int size = bytesPerPixel * width * height;
1373
1374    if (imgData->GetMaxId() + 1 != size)
1375    {
1376        imgData->SetNumberOfComponents(bytesPerPixel);
1377        imgData->SetNumberOfValues(size);
1378    }
1379    glDisable(GL_TEXTURE_2D);
1380    if (_legendRenderWindow->GetDoubleBuffer()) {
1381        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_legendRenderWindow)->GetBackLeftBuffer()));
1382    } else {
1383        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_legendRenderWindow)->GetFrontLeftBuffer()));
1384    }
1385    glPixelStorei(GL_PACK_ALIGNMENT, 1);
1386    if (bytesPerPixel == 4) {
1387        glReadPixels(0, 0, width, height, GL_BGRA,
1388                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
1389    } else {
1390        glReadPixels(0, 0, width, height, GL_BGR,
1391                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
1392    }
1393    if (glGetError() != GL_NO_ERROR) {
1394        ERROR("glReadPixels");
1395    }
1396#else
1397    _legendRenderWindow->GetPixelData(0, 0, width-1, height-1,
1398                                      !_legendRenderWindow->GetDoubleBuffer(),
1399                                      imgData);
1400#endif
1401    TRACE("Leave");
1402    return true;
1403}
1404
1405/**
1406 * \brief Set camera FOV based on render window height
1407 *
1408 * Computes a field-of-view angle based on some assumptions about
1409 * viewer's distance to screen and pixel density
1410 */
1411void Renderer::setViewAngle(int height)
1412{
1413    // Distance of eyes from screen in inches
1414    double d = 20.0;
1415    // Assume 72 ppi screen
1416    double h = (double)height / 72.0;
1417
1418    double angle = vtkMath::DegreesFromRadians(2.0 * atan((h/2.0)/d));
1419    _renderer->GetActiveCamera()->SetViewAngle(angle);
1420
1421    TRACE("Setting view angle: %g", angle);
1422}
1423
1424/**
1425 * \brief Resize the render window (image size for renderings)
1426 */
1427void Renderer::setWindowSize(int width, int height)
1428{
1429    if (_windowWidth == width &&
1430        _windowHeight == height)
1431        return;
1432
1433    //setViewAngle(height);
1434
1435    // FIXME: Fix up panning on aspect change
1436#ifdef notdef
1437    if (_cameraPan[0] != 0.0) {
1438        _cameraPan[0] *= ((double)_windowWidth / width);
1439    }
1440    if (_cameraPan[1] != 0.0) {
1441        _cameraPan[1] *= ((double)_windowHeight / height);
1442    }
1443#endif
1444    _windowWidth = width;
1445    _windowHeight = height;
1446    _renderWindow->SetSize(_windowWidth, _windowHeight);
1447    if (_cameraMode == IMAGE) {
1448        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
1449                            _imgWorldDims[0], _imgWorldDims[1]);
1450    }
1451    _needsRedraw = true;
1452}
1453
1454/**
1455 * \brief Change the camera type: perspective, orthographic or image view
1456 *
1457 * Perspective mode is a normal 3D camera.
1458 *
1459 * Orthogrphic mode is parallel projection.
1460 *
1461 * Image mode is an orthographic camera with fixed axes and a clipping region
1462 * around the plot area, use setCameraZoomRegion to control the displayed area
1463 *
1464 * \param[in] mode Enum specifying camera type
1465 */
1466void Renderer::setCameraMode(CameraMode mode)
1467{
1468    if (_cameraMode == mode) return;
1469
1470    CameraMode origMode = _cameraMode;
1471    _cameraMode = mode;
1472    resetAxes();
1473
1474    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1475    switch (mode) {
1476    case ORTHO: {
1477        TRACE("Set camera to Ortho mode");
1478        camera->ParallelProjectionOn();
1479        if (origMode == IMAGE) {
1480            resetCamera(true);
1481        }
1482        break;
1483    }
1484    case PERSPECTIVE: {
1485        TRACE("Set camera to Perspective mode");
1486        camera->ParallelProjectionOff();
1487        if (origMode == IMAGE) {
1488            resetCamera(true);
1489        }
1490        break;
1491    }
1492    case IMAGE: {
1493        camera->ParallelProjectionOn();
1494        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
1495                            _imgWorldDims[0],_imgWorldDims[1]);
1496        TRACE("Set camera to Image mode");
1497        break;
1498    }
1499    default:
1500        ERROR("Unkown camera mode: %d", mode);
1501    }
1502    _needsRedraw = true;
1503}
1504
1505/**
1506 * \brief Get the current camera mode
1507 */
1508Renderer::CameraMode Renderer::getCameraMode() const
1509{
1510    return _cameraMode;
1511}
1512
1513/**
1514 * \brief Set the VTK camera parameters based on a 4x4 view matrix
1515 */
1516void Renderer::setCameraFromMatrix(vtkCamera *camera, vtkMatrix4x4& mat)
1517{
1518    double d = camera->GetDistance();
1519    double vu[3];
1520    vu[0] = mat[1][0];
1521    vu[1] = mat[1][1];
1522    vu[2] = mat[1][2];
1523    double trans[3];
1524    trans[0] = mat[0][3];
1525    trans[1] = mat[1][3];
1526    trans[2] = mat[2][3];
1527    mat[0][3] = 0;
1528    mat[1][3] = 0;
1529    mat[2][3] = 0;
1530    double vpn[3] = {mat[2][0], mat[2][1], mat[2][2]};
1531    double pos[3];
1532    // With translation removed, we have an orthogonal matrix,
1533    // so the inverse is the transpose
1534    mat.Transpose();
1535    mat.MultiplyPoint(trans, pos);
1536    pos[0] = -pos[0];
1537    pos[1] = -pos[1];
1538    pos[2] = -pos[2];
1539    double fp[3];
1540    fp[0] = pos[0] - vpn[0] * d;
1541    fp[1] = pos[1] - vpn[1] * d;
1542    fp[2] = pos[2] - vpn[2] * d;
1543    camera->SetPosition(pos);
1544    camera->SetFocalPoint(fp);
1545    camera->SetViewUp(vu);
1546}
1547
1548/**
1549 * \brief Set the orientation of the camera from a quaternion
1550 * rotation
1551 *
1552 * \param[in] quat A quaternion with scalar part first: w,x,y,z
1553 * \param[in] absolute Is rotation absolute or relative?
1554 */
1555void Renderer::setCameraOrientation(const double quat[4], bool absolute)
1556{
1557    if (_cameraMode == IMAGE)
1558        return;
1559    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1560    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
1561    vtkSmartPointer<vtkMatrix4x4> rotMat = vtkSmartPointer<vtkMatrix4x4>::New();
1562
1563    double q[4];
1564    copyQuat(quat, q);
1565
1566    if (absolute) {
1567        double abs[4];
1568        // Save absolute rotation
1569        copyQuat(q, abs);
1570        // Compute relative rotation
1571        quatMult(quatReciprocal(_cameraOrientation), q, q);
1572        // Store absolute rotation
1573        copyQuat(abs, _cameraOrientation);
1574    } else {
1575        // Compute new absolute rotation
1576        quatMult(_cameraOrientation, q, _cameraOrientation);
1577    }
1578
1579    quaternionToTransposeMatrix4x4(q, *rotMat);
1580#ifdef DEBUG
1581    TRACE("Rotation matrix:\n %g %g %g\n %g %g %g\n %g %g %g",
1582          (*rotMat)[0][0], (*rotMat)[0][1], (*rotMat)[0][2],
1583          (*rotMat)[1][0], (*rotMat)[1][1], (*rotMat)[1][2],
1584          (*rotMat)[2][0], (*rotMat)[2][1], (*rotMat)[2][2]);
1585    vtkSmartPointer<vtkMatrix4x4> camMat = vtkSmartPointer<vtkMatrix4x4>::New();
1586    camMat->DeepCopy(camera->GetViewTransformMatrix());
1587    TRACE("Camera matrix:\n %g %g %g %g\n %g %g %g %g\n %g %g %g %g\n %g %g %g %g",
1588          (*camMat)[0][0], (*camMat)[0][1], (*camMat)[0][2], (*camMat)[0][3],
1589          (*camMat)[1][0], (*camMat)[1][1], (*camMat)[1][2], (*camMat)[1][3],
1590          (*camMat)[2][0], (*camMat)[2][1], (*camMat)[2][2], (*camMat)[2][3],
1591          (*camMat)[3][0], (*camMat)[3][1], (*camMat)[3][2], (*camMat)[3][3]);
1592    printCameraInfo(camera);
1593#endif
1594    trans->Translate(0, 0, -camera->GetDistance());
1595    trans->Concatenate(rotMat);
1596    trans->Translate(0, 0, camera->GetDistance());
1597    trans->Concatenate(camera->GetViewTransformMatrix());
1598    setCameraFromMatrix(camera, *trans->GetMatrix());
1599
1600    _renderer->ResetCameraClippingRange();
1601    printCameraInfo(camera);
1602    _needsRedraw = true;
1603}
1604
1605/**
1606 * \brief Set the position and orientation of the camera
1607 *
1608 * \param[in] position x,y,z position of camera in world coordinates
1609 * \param[in] focalPoint x,y,z look-at point in world coordinates
1610 * \param[in] viewUp x,y,z up vector of camera
1611 */
1612void Renderer::setCameraOrientationAndPosition(const double position[3],
1613                                               const double focalPoint[3],
1614                                               const double viewUp[3])
1615{
1616    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1617    camera->SetPosition(position);
1618    camera->SetFocalPoint(focalPoint);
1619    camera->SetViewUp(viewUp);
1620    _renderer->ResetCameraClippingRange();
1621    _needsRedraw = true;
1622}
1623
1624/**
1625 * \brief Get the position and orientation of the camera
1626 *
1627 * \param[out] position x,y,z position of camera in world coordinates
1628 * \param[out] focalPoint x,y,z look-at point in world coordinates
1629 * \param[out] viewUp x,y,z up vector of camera
1630 */
1631void Renderer::getCameraOrientationAndPosition(double position[3],
1632                                               double focalPoint[3],
1633                                               double viewUp[3])
1634{
1635    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1636    camera->GetPosition(position);
1637    camera->GetFocalPoint(focalPoint);
1638    camera->GetViewUp(viewUp);
1639}
1640
1641/**
1642 * \brief Reset pan, zoom, clipping planes and optionally rotation
1643 *
1644 * \param[in] resetOrientation Reset the camera rotation/orientation also
1645 */
1646void Renderer::resetCamera(bool resetOrientation)
1647{
1648    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1649    if (_cameraMode == IMAGE) {
1650        initCamera();
1651    } else {
1652        if (resetOrientation) {
1653            camera->SetPosition(0, 0, 1);
1654            camera->SetFocalPoint(0, 0, 0);
1655            camera->SetViewUp(0, 1, 0);
1656            _cameraOrientation[0] = 1.0;
1657            _cameraOrientation[1] = 0.0;
1658            _cameraOrientation[2] = 0.0;
1659            _cameraOrientation[3] = 0.0;
1660        }
1661        //setViewAngle(_windowHeight);
1662        double bounds[6];
1663        collectBounds(bounds, false);
1664        _renderer->ResetCamera(bounds);
1665        _renderer->ResetCameraClippingRange();
1666        //computeScreenWorldCoords();
1667    }
1668
1669    printCameraInfo(camera);
1670
1671    _cameraZoomRatio = 1;
1672    _cameraPan[0] = 0;
1673    _cameraPan[1] = 0;
1674
1675    _needsRedraw = true;
1676}
1677
1678void Renderer::resetCameraClippingRange()
1679{
1680    _renderer->ResetCameraClippingRange();
1681}
1682
1683/**
1684 * \brief Perform a relative rotation to current camera orientation
1685 *
1686 * Angles are in degrees, rotation is about focal point
1687 */
1688void Renderer::rotateCamera(double yaw, double pitch, double roll)
1689{
1690    if (_cameraMode == IMAGE)
1691        return;
1692    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1693    camera->Azimuth(yaw); // Rotate about object
1694    //camera->SetYaw(yaw); // Rotate about camera
1695    camera->Elevation(pitch); // Rotate about object
1696    //camera->SetPitch(pitch); // Rotate about camera
1697    camera->Roll(roll); // Roll about camera view axis
1698    _renderer->ResetCameraClippingRange();
1699    //computeScreenWorldCoords();
1700    _needsRedraw = true;
1701}
1702
1703/**
1704 * \brief Perform a 2D translation of the camera
1705 *
1706 * x,y pan amount are specified as signed absolute pan amount in viewport
1707 * units -- i.e. 0 is no pan, .5 is half the viewport, 2 is twice the viewport,
1708 * etc.
1709 *
1710 * \param[in] x Viewport coordinate horizontal panning (positive number pans
1711 * camera left, object right)
1712 * \param[in] y Viewport coordinate vertical panning (positive number pans
1713 * camera up, object down)
1714 * \param[in] absolute Control if pan amount is relative to current or absolute
1715 */
1716void Renderer::panCamera(double x, double y, bool absolute)
1717{
1718    TRACE("Enter panCamera: %g %g, current abs: %g %g",
1719          x, y, _cameraPan[0], _cameraPan[1]);
1720
1721    if (_cameraMode == IMAGE) {
1722        // Reverse x rather than y, since we are panning the camera, and client
1723        // expects to be panning/moving the object
1724        x = -x * _screenWorldCoords[2];
1725        y = y * _screenWorldCoords[3];
1726
1727        if (absolute) {
1728            double panAbs[2];
1729            panAbs[0] = x;
1730            panAbs[1] = y;
1731            x -= _cameraPan[0];
1732            y -= _cameraPan[1];
1733            _cameraPan[0] = panAbs[0];
1734            _cameraPan[1] = panAbs[1];
1735        } else {
1736            _cameraPan[0] += x;
1737            _cameraPan[1] += y;
1738        }
1739
1740        _imgWorldOrigin[0] += x;
1741        _imgWorldOrigin[1] += y;
1742        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
1743                            _imgWorldDims[0], _imgWorldDims[1]);
1744    } else {
1745        y = -y;
1746        if (absolute) {
1747            double panAbs[2];
1748            panAbs[0] = x;
1749            panAbs[1] = y;
1750            x -= _cameraPan[0];
1751            y -= _cameraPan[1];
1752            _cameraPan[0] = panAbs[0];
1753            _cameraPan[1] = panAbs[1];
1754        } else {
1755            _cameraPan[0] += x;
1756            _cameraPan[1] += y;
1757        }
1758
1759        if (x != 0.0 || y != 0.0) {
1760            vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1761            double viewFocus[4], focalDepth, viewPoint[3];
1762            double newPickPoint[4], oldPickPoint[4], motionVector[3];
1763
1764            camera->GetFocalPoint(viewFocus);
1765            computeWorldToDisplay(viewFocus[0], viewFocus[1], viewFocus[2],
1766                                  viewFocus);
1767            focalDepth = viewFocus[2];
1768
1769            computeDisplayToWorld(( x * 2. + 1.) * _windowWidth / 2.0,
1770                                  ( y * 2. + 1.) * _windowHeight / 2.0,
1771                                  focalDepth,
1772                                  newPickPoint);
1773
1774            computeDisplayToWorld(_windowWidth / 2.0,
1775                                  _windowHeight / 2.0,
1776                                  focalDepth,
1777                                  oldPickPoint);
1778
1779            // Camera motion is reversed
1780            motionVector[0] = oldPickPoint[0] - newPickPoint[0];
1781            motionVector[1] = oldPickPoint[1] - newPickPoint[1];
1782            motionVector[2] = oldPickPoint[2] - newPickPoint[2];
1783
1784            camera->GetFocalPoint(viewFocus);
1785            camera->GetPosition(viewPoint);
1786            camera->SetFocalPoint(motionVector[0] + viewFocus[0],
1787                                  motionVector[1] + viewFocus[1],
1788                                  motionVector[2] + viewFocus[2]);
1789
1790            camera->SetPosition(motionVector[0] + viewPoint[0],
1791                                motionVector[1] + viewPoint[1],
1792                                motionVector[2] + viewPoint[2]);
1793
1794            _renderer->ResetCameraClippingRange();
1795            //computeScreenWorldCoords();
1796        }
1797    }
1798
1799    TRACE("Leave panCamera: %g %g, current abs: %g %g",
1800          x, y, _cameraPan[0], _cameraPan[1]);
1801
1802    _needsRedraw = true;
1803}
1804
1805/**
1806 * \brief Dolly camera or set orthographic scaling based on camera type
1807 *
1808 * \param[in] z Ratio to change zoom (greater than 1 is zoom in, less than 1 is zoom out)
1809 * \param[in] absolute Control if zoom factor is relative to current setting or absolute
1810 */
1811void Renderer::zoomCamera(double z, bool absolute)
1812{
1813    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1814    TRACE("Enter Zoom: current abs: %g, z: %g, view angle %g",
1815          _cameraZoomRatio, z, camera->GetViewAngle());
1816
1817    if (absolute) {
1818        assert(_cameraZoomRatio > 0.0);
1819        double zAbs = z;
1820        z *= 1.0/_cameraZoomRatio;
1821        _cameraZoomRatio = zAbs;
1822    } else {
1823        _cameraZoomRatio *= z;
1824    }
1825
1826    if (_cameraMode == IMAGE) {
1827        double dx = _imgWorldDims[0];
1828        double dy = _imgWorldDims[1];
1829        _imgWorldDims[0] /= z;
1830        _imgWorldDims[1] /= z;
1831        dx -= _imgWorldDims[0];
1832        dy -= _imgWorldDims[1];
1833        _imgWorldOrigin[0] += dx/2.0;
1834        _imgWorldOrigin[1] += dy/2.0;
1835        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
1836                            _imgWorldDims[0], _imgWorldDims[1]);
1837    } else {
1838        // Keep ortho and perspective modes in sync
1839        // Move camera forward/back for perspective camera
1840        camera->Dolly(z);
1841        // Change ortho parallel scale
1842        camera->SetParallelScale(camera->GetParallelScale()/z);
1843        _renderer->ResetCameraClippingRange();
1844        //computeScreenWorldCoords();
1845    }
1846
1847    TRACE("Leave Zoom: rel %g, new abs: %g, view angle %g",
1848          z, _cameraZoomRatio, camera->GetViewAngle());
1849
1850    _needsRedraw = true;
1851}
1852
1853/**
1854 * \brief Set the pan/zoom using a corner and dimensions in pixel coordinates
1855 *
1856 * \param[in] x left pixel coordinate
1857 * \param[in] y bottom  pixel coordinate (with y=0 at top of window)
1858 * \param[in] width Width of zoom region in pixel coordinates
1859 * \param[in] height Height of zoom region in pixel coordinates
1860 */
1861void Renderer::setCameraZoomRegionPixels(int x, int y, int width, int height)
1862{
1863    double wx, wy, ww, wh;
1864
1865    y = _windowHeight - y;
1866    double pxToWorldX = _screenWorldCoords[2] / (double)_windowWidth;
1867    double pxToWorldY = _screenWorldCoords[3] / (double)_windowHeight;
1868
1869    wx = _screenWorldCoords[0] + x * pxToWorldX;
1870    wy = _screenWorldCoords[1] + y * pxToWorldY;
1871    ww = abs(width) *  pxToWorldX;
1872    wh = abs(height) * pxToWorldY;
1873    setCameraZoomRegion(wx, wy, ww, wh);
1874
1875    TRACE("\npx: %d %d %d %d\nworld: %g %g %g %g",
1876          x, y, width, height,
1877          wx, wy, ww, wh);
1878}
1879
1880/**
1881 * \brief Set the pan/zoom using a corner and dimensions in world coordinates
1882 *
1883 * \param[in] x left world coordinate
1884 * \param[in] y bottom  world coordinate
1885 * \param[in] width Width of zoom region in world coordinates
1886 * \param[in] height Height of zoom region in world coordinates
1887 */
1888void Renderer::setCameraZoomRegion(double x, double y, double width, double height)
1889{
1890    double camPos[2];
1891
1892    int pxOffsetX = (int)(0.17 * (double)_windowWidth);
1893    pxOffsetX = (pxOffsetX > 100 ? 100 : pxOffsetX);
1894    int pxOffsetY = (int)(0.15 * (double)_windowHeight);
1895    pxOffsetY = (pxOffsetY > 75 ? 75 : pxOffsetY);
1896    int outerGutter = (int)(0.03 * (double)_windowWidth);
1897    outerGutter = (outerGutter > 15 ? 15 : outerGutter);
1898
1899    int imgHeightPx = _windowHeight - pxOffsetY - outerGutter;
1900    int imgWidthPx = _windowWidth - pxOffsetX - outerGutter;
1901
1902    double imgAspect = width / height;
1903    double winAspect = (double)_windowWidth / _windowHeight;
1904
1905    double pxToWorld;
1906
1907    if (imgAspect >= winAspect) {
1908        pxToWorld = width / imgWidthPx;
1909    } else {
1910        pxToWorld = height / imgHeightPx;
1911    }
1912
1913    double offsetX = pxOffsetX * pxToWorld;
1914    double offsetY = pxOffsetY * pxToWorld;
1915
1916    TRACE("Window: %d %d", _windowWidth, _windowHeight);
1917    TRACE("ZoomRegion: %g %g %g %g", x, y, width, height);
1918    TRACE("pxToWorld: %g", pxToWorld);
1919    TRACE("offset: %g %g", offsetX, offsetY);
1920
1921    setCameraMode(IMAGE);
1922
1923    _imgWorldOrigin[0] = x;
1924    _imgWorldOrigin[1] = y;
1925    _imgWorldDims[0] = width;
1926    _imgWorldDims[1] = height;
1927
1928    camPos[0] = _imgWorldOrigin[0] - offsetX + (_windowWidth * pxToWorld)/2.0;
1929    camPos[1] = _imgWorldOrigin[1] - offsetY + (_windowHeight * pxToWorld)/2.0;
1930
1931    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1932    camera->ParallelProjectionOn();
1933    camera->SetClippingRange(1, 2);
1934    // Half of world coordinate height of viewport (Documentation is wrong)
1935    camera->SetParallelScale(_windowHeight * pxToWorld / 2.0);
1936
1937    if (_imgCameraPlane == PLANE_XY) {
1938        // XY plane
1939        camera->SetPosition(camPos[0], camPos[1], _imgCameraOffset + 1.);
1940        camera->SetFocalPoint(camPos[0], camPos[1], _imgCameraOffset);
1941        camera->SetViewUp(0, 1, 0);
1942        // bottom
1943        _cameraClipPlanes[0]->SetOrigin(0, _imgWorldOrigin[1], 0);
1944        _cameraClipPlanes[0]->SetNormal(0, 1, 0);
1945        // left
1946        _cameraClipPlanes[1]->SetOrigin(_imgWorldOrigin[0], 0, 0);
1947        _cameraClipPlanes[1]->SetNormal(1, 0, 0);
1948        // top
1949        _cameraClipPlanes[2]->SetOrigin(0, _imgWorldOrigin[1] + _imgWorldDims[1], 0);
1950        _cameraClipPlanes[2]->SetNormal(0, -1, 0);
1951        // right
1952        _cameraClipPlanes[3]->SetOrigin(_imgWorldOrigin[0] + _imgWorldDims[0], 0, 0);
1953        _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
1954        _cubeAxesActor2D->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWorldDims[0],
1955                                    _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWorldDims[1],
1956                                    _imgCameraOffset, _imgCameraOffset);
1957        _cubeAxesActor2D->XAxisVisibilityOn();
1958        _cubeAxesActor2D->YAxisVisibilityOn();
1959        _cubeAxesActor2D->ZAxisVisibilityOff();
1960    } else if (_imgCameraPlane == PLANE_ZY) {
1961        // ZY plane
1962        camera->SetPosition(_imgCameraOffset - 1., camPos[1], camPos[0]);
1963        camera->SetFocalPoint(_imgCameraOffset, camPos[1], camPos[0]);
1964        camera->SetViewUp(0, 1, 0);
1965        // bottom
1966        _cameraClipPlanes[0]->SetOrigin(0, _imgWorldOrigin[1], 0);
1967        _cameraClipPlanes[0]->SetNormal(0, 1, 0);
1968        // left
1969        _cameraClipPlanes[1]->SetOrigin(0, 0, _imgWorldOrigin[0]);
1970        _cameraClipPlanes[1]->SetNormal(0, 0, 1);
1971        // top
1972        _cameraClipPlanes[2]->SetOrigin(0, _imgWorldOrigin[1] + _imgWorldDims[1], 0);
1973        _cameraClipPlanes[2]->SetNormal(0, -1, 0);
1974        // right
1975        _cameraClipPlanes[3]->SetOrigin(0, 0, _imgWorldOrigin[0] + _imgWorldDims[0]);
1976        _cameraClipPlanes[3]->SetNormal(0, 0, -1);
1977        _cubeAxesActor2D->SetBounds(_imgCameraOffset, _imgCameraOffset,
1978                                    _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWorldDims[1],
1979                                    _imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWorldDims[0]);
1980        _cubeAxesActor2D->XAxisVisibilityOff();
1981        _cubeAxesActor2D->YAxisVisibilityOn();
1982        _cubeAxesActor2D->ZAxisVisibilityOn();
1983    } else {
1984        // XZ plane
1985        camera->SetPosition(camPos[0], _imgCameraOffset - 1., camPos[1]);
1986        camera->SetFocalPoint(camPos[0], _imgCameraOffset, camPos[1]);
1987        camera->SetViewUp(0, 0, 1);
1988        // bottom
1989        _cameraClipPlanes[0]->SetOrigin(0, 0, _imgWorldOrigin[1]);
1990        _cameraClipPlanes[0]->SetNormal(0, 0, 1);
1991        // left
1992        _cameraClipPlanes[1]->SetOrigin(_imgWorldOrigin[0], 0, 0);
1993        _cameraClipPlanes[1]->SetNormal(1, 0, 0);
1994        // top
1995        _cameraClipPlanes[2]->SetOrigin(0, 0, _imgWorldOrigin[1] + _imgWorldDims[1]);
1996        _cameraClipPlanes[2]->SetNormal(0, 0, -1);
1997        // right
1998        _cameraClipPlanes[3]->SetOrigin(_imgWorldOrigin[0] + _imgWorldDims[0], 0, 0);
1999        _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
2000        _cubeAxesActor2D->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWorldDims[0],
2001                                    _imgCameraOffset, _imgCameraOffset,
2002                                    _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWorldDims[1]);
2003        _cubeAxesActor2D->XAxisVisibilityOn();
2004        _cubeAxesActor2D->YAxisVisibilityOff();
2005        _cubeAxesActor2D->ZAxisVisibilityOn();
2006    }
2007
2008    // Compute screen world coordinates
2009    computeScreenWorldCoords();
2010
2011#ifdef DEBUG
2012    printCameraInfo(camera);
2013#endif
2014
2015    _needsRedraw = true;
2016}
2017
2018/**
2019 * \brief Convert pixel/display coordinates to world coordinates based on current camera
2020 */
2021void Renderer::computeDisplayToWorld(double x, double y, double z, double worldPt[4])
2022{
2023    _renderer->SetDisplayPoint(x, y, z);
2024    _renderer->DisplayToWorld();
2025    _renderer->GetWorldPoint(worldPt);
2026    if (worldPt[3]) {
2027        worldPt[0] /= worldPt[3];
2028        worldPt[1] /= worldPt[3];
2029        worldPt[2] /= worldPt[3];
2030        worldPt[3] = 1.0;
2031    }
2032}
2033
2034/**
2035 * \brief Convert world coordinates to pixel/display coordinates based on current camera
2036 */
2037void Renderer::computeWorldToDisplay(double x, double y, double z, double displayPt[3])
2038{
2039    _renderer->SetWorldPoint(x, y, z, 1.0);
2040    _renderer->WorldToDisplay();
2041    _renderer->GetDisplayPoint(displayPt);
2042}
2043
2044/**
2045 * \brief Compute the world coordinate bounds of the display rectangle
2046 */
2047void Renderer::computeScreenWorldCoords()
2048{
2049    // Start with viewport coords [-1,1]
2050    double x0 = -1;
2051    double y0 = -1;
2052    double z0 = -1;
2053    double x1 = 1;
2054    double y1 = 1;
2055    double z1 = -1;
2056
2057    vtkMatrix4x4 *mat = vtkMatrix4x4::New();
2058    double result[4];
2059
2060    // get the perspective transformation from the active camera
2061    mat->DeepCopy(_renderer->GetActiveCamera()->
2062                  GetCompositeProjectionTransformMatrix(_renderer->GetTiledAspectRatio(),0,1));
2063
2064    // use the inverse matrix
2065    mat->Invert();
2066
2067    // Transform point to world coordinates
2068    result[0] = x0;
2069    result[1] = y0;
2070    result[2] = z0;
2071    result[3] = 1.0;
2072
2073    mat->MultiplyPoint(result, result);
2074
2075    // Get the transformed vector & set WorldPoint
2076    // while we are at it try to keep w at one
2077    if (result[3]) {
2078        x0 = result[0] / result[3];
2079        y0 = result[1] / result[3];
2080        z0 = result[2] / result[3];
2081    }
2082
2083    result[0] = x1;
2084    result[1] = y1;
2085    result[2] = z1;
2086    result[3] = 1.0;
2087
2088    mat->MultiplyPoint(result, result);
2089
2090    if (result[3]) {
2091        x1 = result[0] / result[3];
2092        y1 = result[1] / result[3];
2093        z1 = result[2] / result[3];
2094    }
2095
2096    mat->Delete();
2097
2098    if (_imgCameraPlane == PLANE_XZ) {
2099        _screenWorldCoords[0] = x0;
2100        _screenWorldCoords[1] = z0;
2101        _screenWorldCoords[2] = x1 - x0;
2102        _screenWorldCoords[3] = z1 - z0;
2103    } else if (_imgCameraPlane == PLANE_ZY) {
2104        _screenWorldCoords[0] = z0;
2105        _screenWorldCoords[1] = y0;
2106        _screenWorldCoords[2] = z1 - z0;
2107        _screenWorldCoords[3] = y1 - y0;
2108    } else {
2109        // XY
2110        _screenWorldCoords[0] = x0;
2111        _screenWorldCoords[1] = y0;
2112        _screenWorldCoords[2] = x1 - x0;
2113        _screenWorldCoords[3] = y1 - y0;
2114    }
2115}
2116
2117/**
2118 * \brief Get the world coordinates of the image camera plot area
2119 *
2120 * \param[out] xywh Array to hold x,y,width,height world coordinates
2121 */
2122void Renderer::getCameraZoomRegion(double xywh[4]) const
2123{
2124    xywh[0] = _imgWorldOrigin[0];
2125    xywh[1] = _imgWorldOrigin[1];
2126    xywh[2] = _imgWorldDims[0];
2127    xywh[3] = _imgWorldDims[1];
2128}
2129
2130/**
2131 * \brief Get the world origin and dimensions of the screen
2132 *
2133 * \param[out] xywh Array to hold x,y,width,height world coordinates
2134 */
2135void Renderer::getScreenWorldCoords(double xywh[4]) const
2136{
2137    memcpy(xywh, _screenWorldCoords, sizeof(double)*4);
2138}
2139
2140/**
2141 * \brief Compute bounding box containing the two input bounding boxes
2142 *
2143 * \param[out] boundsDest Union of the two input bounding boxes
2144 * \param[in] bounds1 Input bounding box
2145 * \param[in] bounds2 Input bounding box
2146 */
2147void Renderer::mergeBounds(double *boundsDest,
2148                           const double *bounds1, const double *bounds2)
2149{
2150    assert(boundsDest != NULL);
2151    assert(bounds1 != NULL);
2152    if (bounds2 == NULL) {
2153        WARN("NULL bounds2 array");
2154        return;
2155    }
2156    for (int i = 0; i < 6; i++) {
2157        if (i % 2 == 0)
2158            boundsDest[i] = min2(bounds1[i], bounds2[i]);
2159        else
2160            boundsDest[i] = max2(bounds1[i], bounds2[i]);
2161    }
2162}
2163
2164/**
2165 * \brief Collect bounds of all graphics objects
2166 *
2167 * \param[out] bounds Bounds of all scene objects
2168 * \param[in] onlyVisible Only collect bounds of visible objects
2169 */
2170void Renderer::collectBounds(double *bounds, bool onlyVisible)
2171{
2172    bounds[0] = DBL_MAX;
2173    bounds[1] = -DBL_MAX;
2174    bounds[2] = DBL_MAX;
2175    bounds[3] = -DBL_MAX;
2176    bounds[4] = DBL_MAX;
2177    bounds[5] = -DBL_MAX;
2178
2179    for (DataSetHashmap::iterator itr = _dataSets.begin();
2180             itr != _dataSets.end(); ++itr) {
2181        if ((!onlyVisible || itr->second->getVisibility()) &&
2182            itr->second->getProp() != NULL)
2183            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
2184    }
2185    for (Contour2DHashmap::iterator itr = _contour2Ds.begin();
2186             itr != _contour2Ds.end(); ++itr) {
2187        if ((!onlyVisible || itr->second->getVisibility()) &&
2188            itr->second->getProp() != NULL)
2189            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
2190    }
2191    for (Contour3DHashmap::iterator itr = _contour3Ds.begin();
2192             itr != _contour3Ds.end(); ++itr) {
2193        if ((!onlyVisible || itr->second->getVisibility()) &&
2194            itr->second->getProp() != NULL)
2195            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
2196    }
2197    for (CutplaneHashmap::iterator itr = _cutplanes.begin();
2198             itr != _cutplanes.end(); ++itr) {
2199        if ((!onlyVisible || itr->second->getVisibility()) &&
2200            itr->second->getProp() != NULL)
2201            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
2202    }
2203    for (GlyphsHashmap::iterator itr = _glyphs.begin();
2204             itr != _glyphs.end(); ++itr) {
2205        if ((!onlyVisible || itr->second->getVisibility()) &&
2206            itr->second->getProp() != NULL)
2207            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
2208    }
2209    for (HeightMapHashmap::iterator itr = _heightMaps.begin();
2210             itr != _heightMaps.end(); ++itr) {
2211        if ((!onlyVisible || itr->second->getVisibility()) &&
2212            itr->second->getProp() != NULL)
2213            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
2214    }
2215    for (LICHashmap::iterator itr = _lics.begin();
2216             itr != _lics.end(); ++itr) {
2217        if ((!onlyVisible || itr->second->getVisibility()) &&
2218            itr->second->getProp() != NULL)
2219            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
2220    }
2221    for (MoleculeHashmap::iterator itr = _molecules.begin();
2222             itr != _molecules.end(); ++itr) {
2223        if ((!onlyVisible || itr->second->getVisibility()) &&
2224            itr->second->getProp() != NULL)
2225            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
2226    }
2227    for (PolyDataHashmap::iterator itr = _polyDatas.begin();
2228             itr != _polyDatas.end(); ++itr) {
2229        if ((!onlyVisible || itr->second->getVisibility()) &&
2230            itr->second->getProp() != NULL)
2231            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
2232    }
2233    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
2234             itr != _pseudoColors.end(); ++itr) {
2235        if ((!onlyVisible || itr->second->getVisibility()) &&
2236            itr->second->getProp() != NULL)
2237            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
2238    }
2239    for (StreamlinesHashmap::iterator itr = _streamlines.begin();
2240             itr != _streamlines.end(); ++itr) {
2241        if ((!onlyVisible || itr->second->getVisibility()) &&
2242            itr->second->getProp() != NULL)
2243            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
2244    }
2245    for (VolumeHashmap::iterator itr = _volumes.begin();
2246             itr != _volumes.end(); ++itr) {
2247        if ((!onlyVisible || itr->second->getVisibility()) &&
2248            itr->second->getProp() != NULL)
2249            mergeBounds(bounds, bounds, itr->second->getProp()->GetBounds());
2250    }
2251
2252    for (int i = 0; i < 6; i += 2) {
2253        if (bounds[i+1] < bounds[i]) {
2254            bounds[i] = -0.5;
2255            bounds[i+1] = 0.5;
2256        }
2257    }
2258
2259    int numDims = 0;
2260    if (bounds[0] != bounds[1])
2261        numDims++;
2262    if (bounds[2] != bounds[3])
2263        numDims++;
2264    if (bounds[4] != bounds[5])
2265        numDims++;
2266
2267    if (numDims == 0) {
2268        bounds[0] -= .5;
2269        bounds[1] += .5;
2270        bounds[2] -= .5;
2271        bounds[3] += .5;
2272    }
2273
2274    TRACE("Bounds: %g %g %g %g %g %g",
2275          bounds[0],
2276          bounds[1],
2277          bounds[2],
2278          bounds[3],
2279          bounds[4],
2280          bounds[5]);
2281}
2282
2283/**
2284 * \brief Update data ranges for color-mapping and contours
2285 */
2286void Renderer::updateFieldRanges()
2287{
2288    collectDataRanges();
2289
2290    for (Contour2DHashmap::iterator itr = _contour2Ds.begin();
2291         itr != _contour2Ds.end(); ++itr) {
2292        itr->second->updateRanges(this);
2293    }
2294    for (Contour3DHashmap::iterator itr = _contour3Ds.begin();
2295         itr != _contour3Ds.end(); ++itr) {
2296        itr->second->updateRanges(this);
2297    }
2298    for (CutplaneHashmap::iterator itr = _cutplanes.begin();
2299         itr != _cutplanes.end(); ++itr) {
2300        itr->second->updateRanges(this);
2301    }
2302    for (GlyphsHashmap::iterator itr = _glyphs.begin();
2303         itr != _glyphs.end(); ++itr) {
2304        itr->second->updateRanges(this);
2305    }
2306    for (HeightMapHashmap::iterator itr = _heightMaps.begin();
2307         itr != _heightMaps.end(); ++itr) {
2308        itr->second->updateRanges(this);
2309    }
2310    for (LICHashmap::iterator itr = _lics.begin();
2311         itr != _lics.end(); ++itr) {
2312        itr->second->updateRanges(this);
2313    }
2314    for (MoleculeHashmap::iterator itr = _molecules.begin();
2315         itr != _molecules.end(); ++itr) {
2316        itr->second->updateRanges(this);
2317    }
2318    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
2319         itr != _pseudoColors.end(); ++itr) {
2320        itr->second->updateRanges(this);
2321    }
2322    for (StreamlinesHashmap::iterator itr = _streamlines.begin();
2323         itr != _streamlines.end(); ++itr) {
2324        itr->second->updateRanges(this);
2325    }
2326    for (VolumeHashmap::iterator itr = _volumes.begin();
2327         itr != _volumes.end(); ++itr) {
2328        itr->second->updateRanges(this);
2329    }
2330}
2331
2332/**
2333 * \brief Collect cumulative data range of all DataSets
2334 *
2335 * \param[out] range Data range of all DataSets
2336 * \param[in] name Field name
2337 * \param[in] type Attribute type: e.g. POINT_DATA, CELL_DATA
2338 * \param[in] component Array component or -1 for magnitude
2339 * \param[in] onlyVisible Only collect range of visible DataSets
2340 */
2341void Renderer::collectDataRanges(double *range, const char *name,
2342                                 DataSet::DataAttributeType type,
2343                                 int component, bool onlyVisible)
2344{
2345    range[0] = DBL_MAX;
2346    range[1] = -DBL_MAX;
2347
2348    for (DataSetHashmap::iterator itr = _dataSets.begin();
2349         itr != _dataSets.end(); ++itr) {
2350        if (!onlyVisible || itr->second->getVisibility()) {
2351            double r[2];
2352            itr->second->getDataRange(r, name, type, component);
2353            range[0] = min2(range[0], r[0]);
2354            range[1] = max2(range[1], r[1]);
2355        }
2356    }
2357    if (range[0] == DBL_MAX)
2358        range[0] = 0;
2359    if (range[1] == -DBL_MAX)
2360        range[1] = 1;
2361   
2362    TRACE("n:'%s' t:%d c:%d [%g,%g]", name, type, component,
2363          range[0], range[1]);
2364}
2365
2366/**
2367 * \brief Clear field range hashtables and free memory
2368 */
2369void Renderer::clearFieldRanges()
2370{
2371    TRACE("Deleting Field Ranges");
2372    for (FieldRangeHashmap::iterator itr = _scalarPointDataRange.begin();
2373         itr != _scalarPointDataRange.end(); ++itr) {
2374        delete [] itr->second;
2375    }
2376    _scalarPointDataRange.clear();
2377    for (FieldRangeHashmap::iterator itr = _scalarCellDataRange.begin();
2378         itr != _scalarCellDataRange.end(); ++itr) {
2379        delete [] itr->second;
2380    }
2381    _scalarCellDataRange.clear();
2382    for (FieldRangeHashmap::iterator itr = _vectorPointDataRange.begin();
2383         itr != _vectorPointDataRange.end(); ++itr) {
2384        delete [] itr->second;
2385    }
2386    _vectorPointDataRange.clear();
2387    for (int i = 0; i < 3; i++) {
2388        for (FieldRangeHashmap::iterator itr = _vectorCompPointDataRange[i].begin();
2389             itr != _vectorCompPointDataRange[i].end(); ++itr) {
2390            delete [] itr->second;
2391        }
2392        _vectorCompPointDataRange[i].clear();
2393    }
2394    for (FieldRangeHashmap::iterator itr = _vectorCellDataRange.begin();
2395         itr != _vectorCellDataRange.end(); ++itr) {
2396        delete [] itr->second;
2397    }
2398    _vectorCellDataRange.clear();
2399    for (int i = 0; i < 3; i++) {
2400        for (FieldRangeHashmap::iterator itr = _vectorCompCellDataRange[i].begin();
2401             itr != _vectorCompCellDataRange[i].end(); ++itr) {
2402            delete [] itr->second;
2403        }
2404        _vectorCompCellDataRange[i].clear();
2405    }
2406}
2407
2408void Renderer::initFieldRanges()
2409{
2410    clearFieldRanges();
2411
2412    for (DataSetHashmap::iterator itr = _dataSets.begin();
2413         itr != _dataSets.end(); ++itr) {
2414        DataSet *ds = itr->second;
2415        std::vector<std::string> names;
2416        ds->getFieldNames(names, DataSet::POINT_DATA, 1);
2417        for (std::vector<std::string>::iterator itr = names.begin();
2418             itr != names.end(); ++itr) {
2419            FieldRangeHashmap::iterator fritr =
2420                _scalarPointDataRange.find(*itr);
2421            if (fritr == _scalarPointDataRange.end()) {
2422                _scalarPointDataRange[*itr] = new double[2];
2423                _scalarPointDataRange[*itr][0] = 0;
2424                _scalarPointDataRange[*itr][1] = 1;
2425            }
2426        }
2427        names.clear();
2428        ds->getFieldNames(names, DataSet::CELL_DATA, 1);
2429        for (std::vector<std::string>::iterator itr = names.begin();
2430             itr != names.end(); ++itr) {
2431            FieldRangeHashmap::iterator fritr =
2432                _scalarCellDataRange.find(*itr);
2433            if (fritr == _scalarCellDataRange.end()) {
2434                _scalarCellDataRange[*itr] = new double[2];
2435                _scalarCellDataRange[*itr][0] = 0;
2436                _scalarCellDataRange[*itr][1] = 1;
2437            }
2438        }
2439        names.clear();
2440        ds->getFieldNames(names, DataSet::POINT_DATA, 3);
2441        for (std::vector<std::string>::iterator itr = names.begin();
2442             itr != names.end(); ++itr) {
2443            FieldRangeHashmap::iterator fritr =
2444                _vectorPointDataRange.find(*itr);
2445            if (fritr == _vectorPointDataRange.end()) {
2446                _vectorPointDataRange[*itr] = new double[2];
2447                _vectorPointDataRange[*itr][0] = 0;
2448                _vectorPointDataRange[*itr][1] = 1;
2449            }
2450            for (int i = 0; i < 3; i++) {
2451                fritr = _vectorCompPointDataRange[i].find(*itr);
2452                if (fritr == _vectorCompPointDataRange[i].end()) {
2453                    _vectorCompPointDataRange[i][*itr] = new double[2];
2454                    _vectorCompPointDataRange[i][*itr][0] = 0;
2455                    _vectorCompPointDataRange[i][*itr][1] = 1;
2456                }
2457            }
2458        }
2459        names.clear();
2460        ds->getFieldNames(names, DataSet::CELL_DATA, 3);
2461        for (std::vector<std::string>::iterator itr = names.begin();
2462             itr != names.end(); ++itr) {
2463            FieldRangeHashmap::iterator fritr =
2464                _vectorCellDataRange.find(*itr);
2465            if (fritr == _vectorCellDataRange.end()) {
2466                _vectorCellDataRange[*itr] = new double[2];
2467                _vectorCellDataRange[*itr][0] = 0;
2468                _vectorCellDataRange[*itr][1] = 1;
2469            }
2470            for (int i = 0; i < 3; i++) {
2471                fritr = _vectorCompCellDataRange[i].find(*itr);
2472                if (fritr == _vectorCompCellDataRange[i].end()) {
2473                    _vectorCompCellDataRange[i][*itr] = new double[2];
2474                    _vectorCompCellDataRange[i][*itr][0] = 0;
2475                    _vectorCompCellDataRange[i][*itr][1] = 1;
2476                }
2477            }
2478        }
2479    }
2480}
2481
2482/**
2483 * \brief Returns boolean flag indicating if cumulative data ranges
2484 * should be used
2485 */
2486bool Renderer::getUseCumulativeRange()
2487{
2488    return _useCumulativeRange;
2489}
2490
2491/**
2492 * \brief Get the cumulative range across all DataSets for a point
2493 * data field if it exists, otherwise a cell data field if it exists
2494 *
2495 * \param[out] range Pointer to an array of 2 doubles
2496 * \param[in] name Field name
2497 * \param[in] numComponents Number of components in field
2498 * \param[in] component Index of component or -1 for magnitude/scalar
2499 * \return boolean indicating if field was found
2500 */
2501bool Renderer::getCumulativeDataRange(double *range, const char *name,
2502                                      int numComponents,
2503                                      int component)
2504{
2505    bool ret;
2506    if (!(ret = getCumulativeDataRange(range, name, DataSet::POINT_DATA,
2507                                       numComponents, component)))
2508        ret = getCumulativeDataRange(range, name, DataSet::CELL_DATA,
2509                                     numComponents, component);
2510    return ret;
2511}
2512
2513/**
2514 * \brief Get the cumulative range across all DataSets for a field
2515 *
2516 * \param[out] range Pointer to an array of 2 doubles
2517 * \param[in] name Field name
2518 * \param[in] type DataAttributeType of field
2519 * \param[in] numComponents Number of components in field
2520 * \param[in] component Index of component or -1 for magnitude/scalar
2521 * \return boolean indicating if field was found
2522 */
2523bool Renderer::getCumulativeDataRange(double *range, const char *name,
2524                                      DataSet::DataAttributeType type,
2525                                      int numComponents,
2526                                      int component)
2527{
2528    switch (type) {
2529    case DataSet::POINT_DATA: {
2530        FieldRangeHashmap::iterator itr;
2531        if (numComponents == 1) {
2532            itr = _scalarPointDataRange.find(name);
2533            if (itr == _scalarPointDataRange.end()) {
2534                return false;
2535            } else {
2536                memcpy(range, itr->second, sizeof(double)*2);
2537                return true;
2538            }
2539        } else if (numComponents == 3) {
2540            if (component == -1) {
2541                itr = _vectorPointDataRange.find(name);
2542                if (itr == _vectorPointDataRange.end()) {
2543                    return false;
2544                } else {
2545                    memcpy(range, itr->second, sizeof(double)*2);
2546                    return true;
2547                }
2548            } else if (component >= 0 && component <= 3) {
2549                itr = _vectorCompPointDataRange[component].find(name);
2550                if (itr == _vectorCompPointDataRange[component].end()) {
2551                    return false;
2552                } else {
2553                    memcpy(range, itr->second, sizeof(double)*2);
2554                    return true;
2555                }
2556            }
2557        }
2558    }
2559        break;
2560    case DataSet::CELL_DATA: {
2561        FieldRangeHashmap::iterator itr;
2562        if (numComponents == 1) {
2563            itr = _scalarCellDataRange.find(name);
2564            if (itr == _scalarCellDataRange.end()) {
2565                return false;
2566            } else {
2567                memcpy(range, itr->second, sizeof(double)*2);
2568                return true;
2569            }
2570        } else if (numComponents == 3) {
2571            if (component == -1) {
2572                itr = _vectorCellDataRange.find(name);
2573                if (itr == _vectorCellDataRange.end()) {
2574                    return false;
2575                } else {
2576                    memcpy(range, itr->second, sizeof(double)*2);
2577                    return true;
2578                }
2579            } else if (component >= 0 && component <= 3) {
2580                itr = _vectorCompCellDataRange[component].find(name);
2581                if (itr == _vectorCompCellDataRange[component].end()) {
2582                    return false;
2583                } else {
2584                    memcpy(range, itr->second, sizeof(double)*2);
2585                    return true;
2586                }
2587            }
2588        }
2589    }
2590        break;
2591    default:
2592        break;
2593    }
2594    return false;
2595}
2596
2597void Renderer::collectDataRanges()
2598{
2599    for (FieldRangeHashmap::iterator itr = _scalarPointDataRange.begin();
2600         itr != _scalarPointDataRange.end(); ++itr) {
2601        collectDataRanges(itr->second, itr->first.c_str(),
2602                          DataSet::POINT_DATA, -1,
2603                          _cumulativeRangeOnlyVisible);
2604    }
2605    for (FieldRangeHashmap::iterator itr = _scalarCellDataRange.begin();
2606         itr != _scalarCellDataRange.end(); ++itr) {
2607        collectDataRanges(itr->second, itr->first.c_str(),
2608                          DataSet::CELL_DATA, -1,
2609                          _cumulativeRangeOnlyVisible);
2610    }
2611    for (FieldRangeHashmap::iterator itr = _vectorPointDataRange.begin();
2612         itr != _vectorPointDataRange.end(); ++itr) {
2613        collectDataRanges(itr->second, itr->first.c_str(),
2614                          DataSet::POINT_DATA, -1,
2615                          _cumulativeRangeOnlyVisible);
2616    }
2617    for (int i = 0; i < 3; i++) {
2618        for (FieldRangeHashmap::iterator itr = _vectorCompPointDataRange[i].begin();
2619             itr != _vectorCompPointDataRange[i].end(); ++itr) {
2620            collectDataRanges(itr->second, itr->first.c_str(),
2621                              DataSet::POINT_DATA, i,
2622                              _cumulativeRangeOnlyVisible);
2623        }
2624    }
2625    for (FieldRangeHashmap::iterator itr = _vectorCellDataRange.begin();
2626         itr != _vectorCellDataRange.end(); ++itr) {
2627        collectDataRanges(itr->second, itr->first.c_str(),
2628                          DataSet::CELL_DATA, -1,
2629                          _cumulativeRangeOnlyVisible);
2630    }
2631    for (int i = 0; i < 3; i++) {
2632        for (FieldRangeHashmap::iterator itr = _vectorCompCellDataRange[i].begin();
2633             itr != _vectorCompCellDataRange[i].end(); ++itr) {
2634            collectDataRanges(itr->second, itr->first.c_str(),
2635                              DataSet::CELL_DATA, i,
2636                              _cumulativeRangeOnlyVisible);
2637        }
2638    }
2639}
2640
2641/**
2642 * \brief Determines if AABB lies in a principal axis plane
2643 * and if so, returns the plane normal
2644 */
2645bool Renderer::is2D(const double bounds[6],
2646                    PrincipalPlane *plane,
2647                    double *offset) const
2648{
2649    if (bounds[4] == bounds[5]) {
2650        // Z = 0, XY plane
2651        if (plane)
2652            *plane = PLANE_XY;
2653        if (offset)
2654            *offset = bounds[4];
2655        return true;
2656    } else if (bounds[0] == bounds[1]) {
2657        // X = 0, ZY plane
2658        if (plane)
2659            *plane = PLANE_ZY;
2660        if (offset)
2661            *offset = bounds[0];
2662        return true;
2663    } else if (bounds[2] == bounds[3]) {
2664        // Y = 0, XZ plane
2665        if (plane)
2666            *plane = PLANE_XZ;
2667        if (offset)
2668            *offset = bounds[2];
2669        return true;
2670    }
2671    *plane = PLANE_XY;
2672    *offset = 0;
2673    return false;
2674}
2675
2676/**
2677 * \brief Initialize the camera zoom region to include the bounding volume given
2678 */
2679void Renderer::initCamera()
2680{
2681#ifdef WANT_TRACE
2682    switch (_cameraMode) {
2683    case IMAGE:
2684        TRACE("Image camera");
2685        break;
2686    case ORTHO:
2687        TRACE("Ortho camera");
2688        break;
2689    case PERSPECTIVE:
2690        TRACE("Perspective camera");
2691        break;
2692    default:
2693        TRACE("Unknown camera mode");
2694    }
2695#endif
2696    double bounds[6];
2697    collectBounds(bounds, false);
2698    bool twod = is2D(bounds, &_imgCameraPlane, &_imgCameraOffset);
2699    if (twod) {
2700        _cameraMode = IMAGE;
2701        if (_imgCameraPlane == PLANE_ZY) {
2702            _imgWorldOrigin[0] = bounds[4];
2703            _imgWorldOrigin[1] = bounds[2];
2704            _imgWorldDims[0] = bounds[5] - bounds[4];
2705            _imgWorldDims[1] = bounds[3] - bounds[2];
2706        } else if (_imgCameraPlane == PLANE_XZ) {
2707            _imgWorldOrigin[0] = bounds[0];
2708            _imgWorldOrigin[1] = bounds[4];
2709            _imgWorldDims[0] = bounds[1] - bounds[0];
2710            _imgWorldDims[1] = bounds[5] - bounds[4];
2711        } else {
2712            _imgWorldOrigin[0] = bounds[0];
2713            _imgWorldOrigin[1] = bounds[2];
2714            _imgWorldDims[0] = bounds[1] - bounds[0];
2715            _imgWorldDims[1] = bounds[3] - bounds[2];
2716        }
2717    } else {
2718        _imgWorldOrigin[0] = bounds[0];
2719        _imgWorldOrigin[1] = bounds[2];
2720        _imgWorldDims[0] = bounds[1] - bounds[0];
2721        _imgWorldDims[1] = bounds[3] - bounds[2];
2722    }
2723
2724    _cameraPan[0] = 0;
2725    _cameraPan[1] = 0;
2726    _cameraZoomRatio = 1;
2727
2728    switch (_cameraMode) {
2729    case IMAGE:
2730        _renderer->ResetCamera(bounds);
2731        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
2732                            _imgWorldDims[0], _imgWorldDims[1]);
2733        resetAxes();
2734        break;
2735    case ORTHO:
2736        _renderer->GetActiveCamera()->ParallelProjectionOn();
2737        resetAxes(bounds);
2738        _renderer->ResetCamera(bounds);
2739        //computeScreenWorldCoords();
2740        break;
2741    case PERSPECTIVE:
2742        _renderer->GetActiveCamera()->ParallelProjectionOff();
2743        resetAxes(bounds);
2744        _renderer->ResetCamera(bounds);
2745        //computeScreenWorldCoords();
2746        break;
2747    default:
2748        ERROR("Unknown camera mode");
2749    }
2750
2751#ifdef WANT_TRACE
2752    printCameraInfo(_renderer->GetActiveCamera());
2753#endif
2754}
2755
2756/**
2757 * \brief Print debugging info about a vtkCamera
2758 */
2759void Renderer::printCameraInfo(vtkCamera *camera)
2760{
2761    TRACE("pscale: %g, angle: %g, d: %g pos: %g %g %g, fpt: %g %g %g, vup: %g %g %g, clip: %g %g",
2762          camera->GetParallelScale(),
2763          camera->GetViewAngle(),
2764          camera->GetDistance(),
2765          camera->GetPosition()[0],
2766          camera->GetPosition()[1],
2767          camera->GetPosition()[2],
2768          camera->GetFocalPoint()[0],
2769          camera->GetFocalPoint()[1],
2770          camera->GetFocalPoint()[2],
2771          camera->GetViewUp()[0],
2772          camera->GetViewUp()[1],
2773          camera->GetViewUp()[2],
2774          camera->GetClippingRange()[0],
2775          camera->GetClippingRange()[1]);
2776}
2777
2778/**
2779 * \brief Set the RGB background color to render into the image
2780 */
2781void Renderer::setBackgroundColor(float color[3])
2782{
2783    _bgColor[0] = color[0];
2784    _bgColor[1] = color[1];
2785    _bgColor[2] = color[2];
2786    _renderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
2787    _needsRedraw = true;
2788}
2789
2790/**
2791 * \brief Set the opacity of the specified DataSet's associated graphics objects
2792 */
2793void Renderer::setDataSetOpacity(const DataSetId& id, double opacity)
2794{
2795    DataSetHashmap::iterator itr;
2796
2797    bool doAll = false;
2798
2799    if (id.compare("all") == 0) {
2800        itr = _dataSets.begin();
2801        doAll = true;
2802    } else {
2803        itr = _dataSets.find(id);
2804    }
2805    if (itr == _dataSets.end()) {
2806        ERROR("Unknown dataset %s", id.c_str());
2807        return;
2808    }
2809
2810    do {
2811        itr->second->setOpacity(opacity);
2812    } while (doAll && ++itr != _dataSets.end());
2813
2814    if (id.compare("all") == 0 || getGraphicsObject<Contour2D>(id) != NULL)
2815        setGraphicsObjectOpacity<Contour2D>(id, opacity);
2816    if (id.compare("all") == 0 || getGraphicsObject<Contour3D>(id) != NULL)
2817        setGraphicsObjectOpacity<Contour3D>(id, opacity);
2818    if (id.compare("all") == 0 || getGraphicsObject<Cutplane>(id) != NULL)
2819        setGraphicsObjectOpacity<Cutplane>(id, opacity);
2820    if (id.compare("all") == 0 || getGraphicsObject<Glyphs>(id) != NULL)
2821        setGraphicsObjectOpacity<Glyphs>(id, opacity);
2822    if (id.compare("all") == 0 || getGraphicsObject<HeightMap>(id) != NULL)
2823        setGraphicsObjectOpacity<HeightMap>(id, opacity);
2824    if (id.compare("all") == 0 || getGraphicsObject<LIC>(id) != NULL)
2825        setGraphicsObjectOpacity<LIC>(id, opacity);
2826    if (id.compare("all") == 0 || getGraphicsObject<Molecule>(id) != NULL)
2827        setGraphicsObjectOpacity<Molecule>(id, opacity);
2828    if (id.compare("all") == 0 || getGraphicsObject<PolyData>(id) != NULL)
2829        setGraphicsObjectOpacity<PolyData>(id, opacity);
2830    if (id.compare("all") == 0 || getGraphicsObject<PseudoColor>(id) != NULL)
2831        setGraphicsObjectOpacity<PseudoColor>(id, opacity);
2832    if (id.compare("all") == 0 || getGraphicsObject<Streamlines>(id) != NULL)
2833        setGraphicsObjectOpacity<Streamlines>(id, opacity);
2834    if (id.compare("all") == 0 || getGraphicsObject<Volume>(id) != NULL)
2835        setGraphicsObjectOpacity<Volume>(id, opacity);
2836
2837    _needsRedraw = true;
2838}
2839
2840/**
2841 * \brief Turn on/off rendering of the specified DataSet's associated graphics objects
2842 */
2843void Renderer::setDataSetVisibility(const DataSetId& id, bool state)
2844{
2845    DataSetHashmap::iterator itr;
2846
2847    bool doAll = false;
2848
2849    if (id.compare("all") == 0) {
2850        itr = _dataSets.begin();
2851        doAll = true;
2852    } else {
2853        itr = _dataSets.find(id);
2854    }
2855    if (itr == _dataSets.end()) {
2856        ERROR("Unknown dataset %s", id.c_str());
2857        return;
2858    }
2859
2860    do {
2861        itr->second->setVisibility(state);
2862    } while (doAll && ++itr != _dataSets.end());
2863
2864    if (id.compare("all") == 0 || getGraphicsObject<Contour2D>(id) != NULL)
2865        setGraphicsObjectVisibility<Contour2D>(id, state);
2866    if (id.compare("all") == 0 || getGraphicsObject<Contour3D>(id) != NULL)
2867        setGraphicsObjectVisibility<Contour3D>(id, state);
2868    if (id.compare("all") == 0 || getGraphicsObject<Cutplane>(id) != NULL)
2869        setGraphicsObjectVisibility<Cutplane>(id, state);
2870    if (id.compare("all") == 0 || getGraphicsObject<Glyphs>(id) != NULL)
2871        setGraphicsObjectVisibility<Glyphs>(id, state);
2872    if (id.compare("all") == 0 || getGraphicsObject<HeightMap>(id) != NULL)
2873        setGraphicsObjectVisibility<HeightMap>(id, state);
2874    if (id.compare("all") == 0 || getGraphicsObject<LIC>(id) != NULL)
2875        setGraphicsObjectVisibility<LIC>(id, state);
2876    if (id.compare("all") == 0 || getGraphicsObject<Molecule>(id) != NULL)
2877        setGraphicsObjectVisibility<Molecule>(id, state);
2878    if (id.compare("all") == 0 || getGraphicsObject<PolyData>(id) != NULL)
2879        setGraphicsObjectVisibility<PolyData>(id, state);
2880    if (id.compare("all") == 0 || getGraphicsObject<PseudoColor>(id) != NULL)
2881        setGraphicsObjectVisibility<PseudoColor>(id, state);
2882    if (id.compare("all") == 0 || getGraphicsObject<Streamlines>(id) != NULL)
2883        setGraphicsObjectVisibility<Streamlines>(id, state);
2884    if (id.compare("all") == 0 || getGraphicsObject<Volume>(id) != NULL)
2885        setGraphicsObjectVisibility<Volume>(id, state);
2886
2887    _needsRedraw = true;
2888}
2889
2890/**
2891 * \brief Toggle rendering of actors' bounding box
2892 */
2893void Renderer::setDataSetShowBounds(const DataSetId& id, bool state)
2894{
2895    DataSetHashmap::iterator itr;
2896
2897    bool doAll = false;
2898
2899    if (id.compare("all") == 0) {
2900        itr = _dataSets.begin();
2901        doAll = true;
2902    } else {
2903        itr = _dataSets.find(id);
2904    }
2905    if (itr == _dataSets.end()) {
2906        ERROR("Unknown dataset %s", id.c_str());
2907        return;
2908    }
2909
2910    do {
2911        if (!state && itr->second->getProp()) {
2912            _renderer->RemoveViewProp(itr->second->getProp());
2913        }
2914
2915        itr->second->showOutline(state);
2916
2917        if (state && !_renderer->HasViewProp(itr->second->getProp())) {
2918            _renderer->AddViewProp(itr->second->getProp());
2919        }
2920    } while (doAll && ++itr != _dataSets.end());
2921
2922    initCamera();
2923    _needsRedraw = true;
2924}
2925
2926/**
2927 * \brief Set color of outline bounding box
2928 */
2929void Renderer::setDataSetOutlineColor(const DataSetId& id, float color[3])
2930{
2931    DataSetHashmap::iterator itr;
2932
2933    bool doAll = false;
2934
2935    if (id.compare("all") == 0) {
2936        itr = _dataSets.begin();
2937        doAll = true;
2938    } else {
2939        itr = _dataSets.find(id);
2940    }
2941    if (itr == _dataSets.end()) {
2942        ERROR("Unknown dataset %s", id.c_str());
2943        return;
2944    }
2945
2946    do {
2947        itr->second->setOutlineColor(color);
2948    } while (doAll && ++itr != _dataSets.end());
2949
2950    _needsRedraw = true;
2951}
2952
2953/**
2954 * \brief Set a user clipping plane
2955 *
2956 * TODO: Fix clip plane positions after a change in actor bounds
2957 */
2958void Renderer::setClipPlane(Axis axis, double ratio, int direction)
2959{
2960    double bounds[6];
2961    collectBounds(bounds, false);
2962
2963    switch (axis) {
2964    case X_AXIS:
2965        if (direction > 0) {
2966            if (ratio > 0.0) {
2967                if (_userClipPlanes[0] == NULL) {
2968                    _userClipPlanes[0] = vtkSmartPointer<vtkPlane>::New();
2969                    _userClipPlanes[0]->SetNormal(1, 0, 0);
2970                }
2971                _userClipPlanes[0]->SetOrigin(bounds[0] + (bounds[1]-bounds[0])*ratio, 0, 0);
2972            } else {
2973                _userClipPlanes[0] = NULL;
2974            }
2975        } else {
2976            if (ratio < 1.0) {
2977                if (_userClipPlanes[1] == NULL) {
2978                    _userClipPlanes[1] = vtkSmartPointer<vtkPlane>::New();
2979                    _userClipPlanes[1]->SetNormal(-1, 0, 0);
2980                }
2981                _userClipPlanes[1]->SetOrigin(bounds[0] + (bounds[1]-bounds[0])*ratio, 0, 0);
2982            } else {
2983                _userClipPlanes[1] = NULL;
2984            }
2985        }
2986        break;
2987    case Y_AXIS:
2988        if (direction > 0) {
2989            if (ratio > 0.0) {
2990                if (_userClipPlanes[2] == NULL) {
2991                    _userClipPlanes[2] = vtkSmartPointer<vtkPlane>::New();
2992                    _userClipPlanes[2]->SetNormal(0, 1, 0);
2993                }
2994                _userClipPlanes[2]->SetOrigin(0, bounds[2] + (bounds[3]-bounds[2])*ratio, 0);
2995            } else {
2996                _userClipPlanes[2] = NULL;
2997            }
2998        } else {
2999            if (ratio < 1.0) {
3000                if (_userClipPlanes[3] == NULL) {
3001                    _userClipPlanes[3] = vtkSmartPointer<vtkPlane>::New();
3002                    _userClipPlanes[3]->SetNormal(0, -1, 0);
3003                }
3004                _userClipPlanes[3]->SetOrigin(0, bounds[2] + (bounds[3]-bounds[2])*ratio, 0);
3005            } else {
3006                _userClipPlanes[3] = NULL;
3007            }
3008        }
3009        break;
3010    case Z_AXIS:
3011        if (direction > 0) {
3012            if (ratio > 0.0) {
3013                if (_userClipPlanes[4] == NULL) {
3014                    _userClipPlanes[4] = vtkSmartPointer<vtkPlane>::New();
3015                    _userClipPlanes[4]->SetNormal(0, 0, 1);
3016                }
3017                _userClipPlanes[4]->SetOrigin(0, 0, bounds[4] + (bounds[5]-bounds[4])*ratio);
3018            } else {
3019                _userClipPlanes[4] = NULL;
3020            }
3021        } else {
3022            if (ratio < 1.0) {
3023                if (_userClipPlanes[5] == NULL) {
3024                    _userClipPlanes[5] = vtkSmartPointer<vtkPlane>::New();
3025                    _userClipPlanes[5]->SetNormal(0, 0, -1);
3026                }
3027                _userClipPlanes[5]->SetOrigin(0, 0, bounds[4] + (bounds[5]-bounds[4])*ratio);
3028            } else {
3029                _userClipPlanes[5] = NULL;
3030            }
3031        }
3032        break;
3033    default:
3034        ;
3035    }
3036
3037    _needsRedraw = true;
3038}
3039
3040/**
3041 * \brief Set up clipping planes for image camera mode if needed
3042 */
3043void Renderer::setCameraClippingPlanes()
3044{
3045    /* XXX: Note that there appears to be a bug with setting the
3046     * clipping plane collection to NULL in the VTK Mappers --
3047     * the old clip planes are still applied.  The workaround here
3048     * is to keep the PlaneCollection and add/remove the planes
3049     * to/from the PlaneCollection as needed.
3050     */
3051    if (_cameraMode == IMAGE) {
3052        if (_activeClipPlanes->GetNumberOfItems() == 0) {
3053            for (int i = 0; i < 4; i++)
3054                _activeClipPlanes->AddItem(_cameraClipPlanes[i]);
3055        }
3056    } else {
3057        if (_activeClipPlanes->GetNumberOfItems() > 0)
3058            _activeClipPlanes->RemoveAllItems();
3059        for (int i = 0; i < 6; i++) {
3060            if (_userClipPlanes[i] != NULL) {
3061                _activeClipPlanes->AddItem(_userClipPlanes[i]);
3062            }
3063        }
3064    }
3065
3066    /* Ensure all Mappers are using the PlaneCollection
3067     * This will not change the state or timestamp of
3068     * Mappers already using the PlaneCollection
3069     */
3070    for (Contour2DHashmap::iterator itr = _contour2Ds.begin();
3071         itr != _contour2Ds.end(); ++itr) {
3072        itr->second->setClippingPlanes(_activeClipPlanes);
3073    }
3074    for (Contour3DHashmap::iterator itr = _contour3Ds.begin();
3075         itr != _contour3Ds.end(); ++itr) {
3076        itr->second->setClippingPlanes(_activeClipPlanes);
3077    }
3078    for (CutplaneHashmap::iterator itr = _cutplanes.begin();
3079         itr != _cutplanes.end(); ++itr) {
3080        itr->second->setClippingPlanes(_activeClipPlanes);
3081    }
3082    for (GlyphsHashmap::iterator itr = _glyphs.begin();
3083         itr != _glyphs.end(); ++itr) {
3084        itr->second->setClippingPlanes(_activeClipPlanes);
3085    }
3086    for (HeightMapHashmap::iterator itr = _heightMaps.begin();
3087         itr != _heightMaps.end(); ++itr) {
3088        itr->second->setClippingPlanes(_activeClipPlanes);
3089    }
3090    for (LICHashmap::iterator itr = _lics.begin();
3091         itr != _lics.end(); ++itr) {
3092        itr->second->setClippingPlanes(_activeClipPlanes);
3093    }
3094    for (MoleculeHashmap::iterator itr = _molecules.begin();
3095         itr != _molecules.end(); ++itr) {
3096        itr->second->setClippingPlanes(_activeClipPlanes);
3097    }
3098    for (PolyDataHashmap::iterator itr = _polyDatas.begin();
3099         itr != _polyDatas.end(); ++itr) {
3100        itr->second->setClippingPlanes(_activeClipPlanes);
3101    }
3102    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
3103         itr != _pseudoColors.end(); ++itr) {
3104        itr->second->setClippingPlanes(_activeClipPlanes);
3105    }
3106    for (StreamlinesHashmap::iterator itr = _streamlines.begin();
3107         itr != _streamlines.end(); ++itr) {
3108        itr->second->setClippingPlanes(_activeClipPlanes);
3109    }
3110    for (VolumeHashmap::iterator itr = _volumes.begin();
3111         itr != _volumes.end(); ++itr) {
3112        itr->second->setClippingPlanes(_activeClipPlanes);
3113    }
3114}
3115
3116/**
3117 * \brief Control the use of two sided lighting
3118 */
3119void Renderer::setUseTwoSidedLighting(bool state)
3120{
3121    _renderer->SetTwoSidedLighting(state ? 1 : 0);
3122    _needsRedraw = true;
3123}
3124
3125/**
3126 * \brief Control the use of the depth peeling algorithm for transparency
3127 */
3128void Renderer::setUseDepthPeeling(bool state)
3129{
3130    _renderer->SetUseDepthPeeling(state ? 1 : 0);
3131    _needsRedraw = true;
3132}
3133
3134/**
3135 * \brief Sets flag to trigger rendering next time render() is called
3136 */
3137void Renderer::eventuallyRender()
3138{
3139    _needsRedraw = true;
3140}
3141
3142/**
3143 * \brief Cause the rendering to render a new image if needed
3144 *
3145 * The _needsRedraw flag indicates if a state change has occured since
3146 * the last rendered frame
3147 */
3148bool Renderer::render()
3149{
3150    if (_needsRedraw) {
3151        setCameraClippingPlanes();
3152        _renderWindow->Render();
3153        _needsRedraw = false;
3154        return true;
3155    } else
3156        return false;
3157}
3158
3159/// Get the pixel width of the render window/image
3160int Renderer::getWindowWidth() const
3161{
3162    return _windowWidth;
3163}
3164
3165/// Get the pixel height of the render window/image
3166int Renderer::getWindowHeight() const
3167{
3168    return _windowHeight;
3169}
3170
3171/**
3172 * \brief Read back the rendered framebuffer image
3173 */
3174void Renderer::getRenderedFrame(vtkUnsignedCharArray *imgData)
3175{
3176#ifdef RENDER_TARGA
3177    _renderWindow->MakeCurrent();
3178    // Must clear previous errors first.
3179    while (glGetError() != GL_NO_ERROR){
3180        ;
3181    }
3182    int bytesPerPixel = TARGA_BYTES_PER_PIXEL;
3183    int size = bytesPerPixel * _windowWidth * _windowHeight;
3184
3185    if (imgData->GetMaxId() + 1 != size)
3186    {
3187        imgData->SetNumberOfComponents(bytesPerPixel);
3188        imgData->SetNumberOfValues(size);
3189    }
3190    glDisable(GL_TEXTURE_2D);
3191    if (_renderWindow->GetDoubleBuffer()) {
3192        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_renderWindow)->GetBackLeftBuffer()));
3193    } else {
3194        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_renderWindow)->GetFrontLeftBuffer()));
3195    }
3196    glPixelStorei(GL_PACK_ALIGNMENT, 1);
3197#ifdef WANT_TRACE
3198    struct timeval t1, t2;
3199    glFinish();
3200    gettimeofday(&t1, 0);
3201#endif
3202    if (bytesPerPixel == 4) {
3203        glReadPixels(0, 0, _windowWidth, _windowHeight, GL_BGRA,
3204                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
3205    } else {
3206        glReadPixels(0, 0, _windowWidth, _windowHeight, GL_BGR,
3207                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
3208    }
3209#ifdef WANT_TRACE
3210    gettimeofday(&t2, 0);
3211    static unsigned int numFrames = 0;
3212    static double accum = 0;
3213    numFrames++;
3214    accum += ELAPSED_TIME(t1, t2);
3215#endif
3216    TRACE("Readback time: %g ms", ELAPSED_TIME(t1, t2));
3217    TRACE("Readback avg: %g ms", accum/numFrames);
3218    if (glGetError() != GL_NO_ERROR) {
3219        ERROR("glReadPixels");
3220    }
3221#else
3222    _renderWindow->GetPixelData(0, 0, _windowWidth-1, _windowHeight-1,
3223                                !_renderWindow->GetDoubleBuffer(), imgData);
3224#endif
3225    TRACE("Image data size: %d", imgData->GetSize());
3226}
3227
3228/**
3229 * \brief Get nearest data value given display coordinates x,y
3230 *
3231 * Note: no interpolation is performed on data
3232 */
3233bool Renderer::getScalarValueAtPixel(const DataSetId& id, int x, int y, double *value)
3234{
3235    vtkSmartPointer<vtkCoordinate> coord = vtkSmartPointer<vtkCoordinate>::New();
3236    coord->SetCoordinateSystemToDisplay();
3237    coord->SetValue(x, _windowHeight - y, 0);
3238    double *worldCoords = coord->GetComputedWorldValue(_renderer);
3239
3240    TRACE("Pixel coords: %d, %d\nWorld coords: %g, %g, %g", x, y,
3241          worldCoords[0],
3242          worldCoords[1],
3243          worldCoords[2]);
3244
3245    return getScalarValue(id, worldCoords[0], worldCoords[1], worldCoords[2], value);
3246}
3247
3248/**
3249 * \brief Get nearest data value given world coordinates x,y,z
3250 *
3251 * Note: no interpolation is performed on data
3252 */
3253bool Renderer::getScalarValue(const DataSetId& id, double x, double y, double z, double *value)
3254{
3255    DataSet *ds = getDataSet(id);
3256    if (ds == NULL)
3257        return false;
3258
3259    return ds->getScalarValue(x, y, z, value);
3260}
3261
3262/**
3263 * \brief Get nearest data value given display coordinates x,y
3264 *
3265 * Note: no interpolation is performed on data
3266 */
3267bool Renderer::getVectorValueAtPixel(const DataSetId& id, int x, int y, double vector[3])
3268{
3269    vtkSmartPointer<vtkCoordinate> coord = vtkSmartPointer<vtkCoordinate>::New();
3270    coord->SetCoordinateSystemToDisplay();
3271    coord->SetValue(x, _windowHeight - y, 0);
3272    double *worldCoords = coord->GetComputedWorldValue(_renderer);
3273
3274    TRACE("Pixel coords: %d, %d\nWorld coords: %g, %g, %g", x, y,
3275          worldCoords[0],
3276          worldCoords[1],
3277          worldCoords[2]);
3278
3279    return getVectorValue(id, worldCoords[0], worldCoords[1], worldCoords[2], vector);
3280}
3281
3282/**
3283 * \brief Get nearest data value given world coordinates x,y,z
3284 *
3285 * Note: no interpolation is performed on data
3286 */
3287bool Renderer::getVectorValue(const DataSetId& id, double x, double y, double z, double vector[3])
3288{
3289    DataSet *ds = getDataSet(id);
3290    if (ds == NULL)
3291        return false;
3292
3293    return ds->getVectorValue(x, y, z, vector);
3294}
Note: See TracBrowser for help on using the repository browser.