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

Last change on this file since 2742 was 2742, checked in by gah, 11 years ago

sync with trunk

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