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

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

Use new custom vtk 3d axes (when USE_CUSTOM_AXES is defined). Fixes gridlines
in 3D.

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