source: trunk/packages/vizservers/vtkvis/Renderer.cpp @ 3797

Last change on this file since 3797 was 3797, checked in by ldelgass, 11 years ago

Move NEW_SCALAR_BAR define to Makefile - disable for building with VTK 6 pre-
releases before rc1

  • Property svn:eol-style set to native
File size: 140.6 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (C) 2004-2012  HUBzero Foundation, LLC
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 <vtkLightCollection.h>
23#include <vtkCoordinate.h>
24#include <vtkTransform.h>
25#include <vtkCharArray.h>
26#ifdef USE_CUSTOM_AXES
27#include "vtkRpCubeAxesActor.h"
28#else
29#include <vtkCubeAxesActor.h>
30#endif
31#include <vtkProperty.h>
32#include <vtkProperty2D.h>
33#include <vtkPointData.h>
34#include <vtkLookupTable.h>
35#include <vtkTextProperty.h>
36#include <vtkOpenGLRenderWindow.h>
37#include <vtkVersion.h>
38
39#include "Renderer.h"
40#include "RendererGraphicsObjs.h"
41#include "Math.h"
42#include "ColorMap.h"
43#include "Trace.h"
44
45#define MSECS_ELAPSED(t1, t2) \
46    ((t1).tv_sec == (t2).tv_sec ? (((t2).tv_usec - (t1).tv_usec)/1.0e+3) : \
47     (((t2).tv_sec - (t1).tv_sec))*1.0e+3 + (double)((t2).tv_usec - (t1).tv_usec)/1.0e+3)
48
49#define printCameraInfo(camera)                                         \
50do {                                                                    \
51    TRACE("pscale: %g, angle: %g, d: %g pos: %g %g %g, fpt: %g %g %g, vup: %g %g %g, clip: %g %g", \
52          (camera)->GetParallelScale(),                                 \
53          (camera)->GetViewAngle(),                                     \
54          (camera)->GetDistance(),                                      \
55          (camera)->GetPosition()[0],                                   \
56          (camera)->GetPosition()[1],                                   \
57          (camera)->GetPosition()[2],                                   \
58          (camera)->GetFocalPoint()[0],                                 \
59          (camera)->GetFocalPoint()[1],                                 \
60          (camera)->GetFocalPoint()[2],                                 \
61          (camera)->GetViewUp()[0],                                     \
62          (camera)->GetViewUp()[1],                                     \
63          (camera)->GetViewUp()[2],                                     \
64          (camera)->GetClippingRange()[0],                              \
65          (camera)->GetClippingRange()[1]);                             \
66} while(0)
67
68using namespace VtkVis;
69
70Renderer::Renderer() :
71    _needsRedraw(true),
72    _needsAxesReset(false),
73    _needsCameraClippingRangeReset(false),
74    _needsCameraReset(false),
75    _windowWidth(500),
76    _windowHeight(500),
77    _cameraMode(PERSPECTIVE),
78    _cameraAspect(ASPECT_NATIVE),
79    _imgCameraPlane(PLANE_XY),
80    _imgCameraOffset(0),
81    _cameraZoomRatio(1),
82    _useCumulativeRange(true),
83    _cumulativeRangeOnlyVisible(false)
84{
85    _bgColor[0] = 0;
86    _bgColor[1] = 0;
87    _bgColor[2] = 0;
88    _cameraPan[0] = 0;
89    _cameraPan[1] = 0;
90    _cameraOrientation[0] = 1.0;
91    _cameraOrientation[1] = 0.0;
92    _cameraOrientation[2] = 0.0;
93    _cameraOrientation[3] = 0.0;
94    // clipping planes to prevent overdrawing axes
95    _activeClipPlanes = vtkSmartPointer<vtkPlaneCollection>::New();
96    // bottom
97    _cameraClipPlanes[0] = vtkSmartPointer<vtkPlane>::New();
98    _cameraClipPlanes[0]->SetNormal(0, 1, 0);
99    _cameraClipPlanes[0]->SetOrigin(0, 0, 0);
100    if (_cameraMode == IMAGE)
101        _activeClipPlanes->AddItem(_cameraClipPlanes[0]);
102    // left
103    _cameraClipPlanes[1] = vtkSmartPointer<vtkPlane>::New();
104    _cameraClipPlanes[1]->SetNormal(1, 0, 0);
105    _cameraClipPlanes[1]->SetOrigin(0, 0, 0);
106    if (_cameraMode == IMAGE)
107        _activeClipPlanes->AddItem(_cameraClipPlanes[1]);
108    // top
109    _cameraClipPlanes[2] = vtkSmartPointer<vtkPlane>::New();
110    _cameraClipPlanes[2]->SetNormal(0, -1, 0);
111    _cameraClipPlanes[2]->SetOrigin(0, 1, 0);
112    if (_cameraMode == IMAGE)
113        _activeClipPlanes->AddItem(_cameraClipPlanes[2]);
114    // right
115    _cameraClipPlanes[3] = vtkSmartPointer<vtkPlane>::New();
116    _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
117    _cameraClipPlanes[3]->SetOrigin(1, 0, 0);
118    if (_cameraMode == IMAGE)
119        _activeClipPlanes->AddItem(_cameraClipPlanes[3]);
120    _renderer = vtkSmartPointer<vtkRenderer>::New();
121
122    // Global ambient (defaults to 1,1,1)
123    //_renderer->SetAmbient(.2, .2, .2);
124
125    _renderer->AutomaticLightCreationOff();
126
127    vtkSmartPointer<vtkLight> headlight = vtkSmartPointer<vtkLight>::New();
128    headlight->SetLightTypeToHeadlight();
129    headlight->PositionalOff();
130    // Light ambient color defaults to 0,0,0
131    //headlight->SetAmbientColor(1, 1, 1);
132    _renderer->AddLight(headlight);
133
134    vtkSmartPointer<vtkLight> skylight = vtkSmartPointer<vtkLight>::New();
135    skylight->SetLightTypeToCameraLight();
136    skylight->SetPosition(0, 1, 0);
137    skylight->SetFocalPoint(0, 0, 0);
138    skylight->PositionalOff();
139    // Light ambient color defaults to 0,0,0
140    //skylight->SetAmbientColor(1, 1, 1);
141    _renderer->AddLight(skylight);
142
143    _renderer->LightFollowCameraOn();
144    _renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
145#ifdef USE_OFFSCREEN_RENDERING
146    _renderWindow->DoubleBufferOff();
147    _renderWindow->OffScreenRenderingOn();
148#else
149    _renderWindow->SwapBuffersOff();
150#endif
151    _renderWindow->SetSize(_windowWidth, _windowHeight);
152    // Next 2 options needed to support depth peeling
153    _renderWindow->SetAlphaBitPlanes(1);
154    _renderWindow->SetMultiSamples(0);
155    _renderer->SetMaximumNumberOfPeels(100);
156    _renderer->SetUseDepthPeeling(1);
157    _renderer->SetTwoSidedLighting(1);
158    _renderWindow->AddRenderer(_renderer);
159    setViewAngle(_windowHeight);
160    initAxes();
161    initCamera();
162    addColorMap("default", ColorMap::getDefault());
163    addColorMap("grayDefault", ColorMap::getGrayDefault());
164    addColorMap("volumeDefault", ColorMap::getVolumeDefault());
165    addColorMap("elementDefault", ColorMap::getElementDefault());
166}
167
168Renderer::~Renderer()
169{
170    TRACE("Enter");
171
172    deleteAllGraphicsObjects<Arc>();
173    deleteAllGraphicsObjects<Arrow>();
174    deleteAllGraphicsObjects<Box>();
175    deleteAllGraphicsObjects<Cone>();
176    deleteAllGraphicsObjects<Contour2D>();
177    deleteAllGraphicsObjects<Contour3D>();
178    deleteAllGraphicsObjects<Cutplane>();
179    deleteAllGraphicsObjects<Cylinder>();
180    deleteAllGraphicsObjects<Disk>();
181    deleteAllGraphicsObjects<Glyphs>();
182    deleteAllGraphicsObjects<Group>();
183    deleteAllGraphicsObjects<HeightMap>();
184    deleteAllGraphicsObjects<LIC>();
185    deleteAllGraphicsObjects<Line>();
186    deleteAllGraphicsObjects<Molecule>();
187    deleteAllGraphicsObjects<Outline>();
188    deleteAllGraphicsObjects<PolyData>();
189    deleteAllGraphicsObjects<Polygon>();
190    deleteAllGraphicsObjects<PseudoColor>();
191    deleteAllGraphicsObjects<Sphere>();
192    deleteAllGraphicsObjects<Streamlines>();
193    deleteAllGraphicsObjects<Volume>();
194    deleteAllGraphicsObjects<Warp>();
195
196    TRACE("Deleting ColorMaps");
197    // Delete color maps and data sets last in case references still
198    // exist
199    for (ColorMapHashmap::iterator itr = _colorMaps.begin();
200         itr != _colorMaps.end(); ++itr) {
201        delete itr->second;
202    }
203    _colorMaps.clear();
204    TRACE("Deleting DataSets");
205    for (DataSetHashmap::iterator itr = _dataSets.begin();
206         itr != _dataSets.end(); ++itr) {
207        delete itr->second;
208    }
209    _dataSets.clear();
210
211    clearFieldRanges();
212    clearUserFieldRanges();
213
214    TRACE("Leave");
215}
216
217/**
218 * \brief Add a DataSet to this Renderer
219 *
220 * This just adds the DataSet to the Renderer's list of data sets.
221 * In order to render the data, a graphics object using the data
222 * set must be added to the Renderer.
223 */
224void Renderer::addDataSet(const DataSetId& id)
225{
226    if (getDataSet(id) != NULL) {
227        WARN("Replacing existing DataSet %s", id.c_str());
228        deleteDataSet(id);
229    }
230    _dataSets[id] = new DataSet(id);
231}
232
233/**
234 * \brief Remove the specified DataSet and associated rendering objects
235 *
236 * The underlying DataSet and any associated graphics
237 * objects are deleted, freeing the memory used.
238 */
239void Renderer::deleteDataSet(const DataSetId& id)
240{
241    DataSetHashmap::iterator itr;
242
243    bool doAll = false;
244
245    if (id.compare("all") == 0) {
246        itr = _dataSets.begin();
247        doAll = true;
248    } else {
249        itr = _dataSets.find(id);
250    }
251    if (itr == _dataSets.end()) {
252        ERROR("Unknown dataset %s", id.c_str());
253        return;
254    }
255
256    do {
257        TRACE("Deleting dataset %s", itr->second->getName().c_str());
258
259        deleteGraphicsObject<Contour2D>(itr->second->getName());
260        deleteGraphicsObject<Contour3D>(itr->second->getName());
261        deleteGraphicsObject<Cutplane>(itr->second->getName());
262        deleteGraphicsObject<Glyphs>(itr->second->getName());
263        deleteGraphicsObject<HeightMap>(itr->second->getName());
264        deleteGraphicsObject<LIC>(itr->second->getName());
265        deleteGraphicsObject<Molecule>(itr->second->getName());
266        deleteGraphicsObject<Outline>(itr->second->getName());
267        deleteGraphicsObject<PolyData>(itr->second->getName());
268        deleteGraphicsObject<PseudoColor>(itr->second->getName());
269        deleteGraphicsObject<Streamlines>(itr->second->getName());
270        deleteGraphicsObject<Volume>(itr->second->getName());
271        deleteGraphicsObject<Warp>(itr->second->getName());
272
273        TRACE("After deleting graphics objects");
274
275        delete itr->second;
276        itr = _dataSets.erase(itr);
277    } while (doAll && itr != _dataSets.end());
278
279    // Update cumulative data range
280    initFieldRanges();
281    updateFieldRanges();
282
283    sceneBoundsChanged();
284    _needsRedraw = true;
285}
286
287/**
288 * \brief Get a list of DataSets this Renderer knows about
289 */
290void Renderer::getDataSetNames(std::vector<std::string>& names)
291{
292    names.clear();
293    for (DataSetHashmap::iterator itr = _dataSets.begin();
294         itr != _dataSets.end(); ++itr) {
295        names.push_back(itr->second->getName());
296    }
297}
298
299/**
300 * \brief Find the DataSet for the given DataSetId key
301 *
302 * \return A pointer to the DataSet, or NULL if not found
303 */
304DataSet *Renderer::getDataSet(const DataSetId& id)
305{
306    DataSetHashmap::iterator itr = _dataSets.find(id);
307    if (itr == _dataSets.end()) {
308#ifdef DEBUG
309        TRACE("DataSet not found: %s", id.c_str());
310#endif
311        return NULL;
312    } else
313        return itr->second;
314}
315
316/**
317 * \brief (Re-)load the data for the specified DataSet key from a file
318 */
319bool Renderer::setDataFile(const DataSetId& id, const char *filename)
320{
321    DataSet *ds = getDataSet(id);
322    if (ds) {
323        bool ret = ds->setDataFile(filename);
324        initFieldRanges();
325        updateFieldRanges();
326        _needsRedraw = true;
327        return ret;
328    } else
329        return false;
330}
331
332/**
333 * \brief (Re-)load the data for the specified DataSet key from a memory buffer
334 */
335bool Renderer::setData(const DataSetId& id, char *data, int nbytes)
336{
337    DataSet *ds = getDataSet(id);
338    if (ds) {
339        bool ret = ds->setData(data, nbytes);
340        initFieldRanges();
341        updateFieldRanges();
342        _needsRedraw = true;
343        return ret;
344    } else
345        return false;
346}
347
348/**
349 * \brief Set the active scalar field array by name for a DataSet
350 */
351bool Renderer::setDataSetActiveScalars(const DataSetId& id, const char *scalarName)
352{
353    DataSetHashmap::iterator itr;
354
355    bool doAll = false;
356
357    if (id.compare("all") == 0) {
358        itr = _dataSets.begin();
359        doAll = true;
360    } else {
361        itr = _dataSets.find(id);
362    }
363    if (itr == _dataSets.end()) {
364        ERROR("DataSet not found: %s", id.c_str());
365        return false;
366    }
367
368    bool ret = true;
369    bool needRangeUpdate = false;
370    do {
371        if (strcmp(itr->second->getActiveScalarsName(), scalarName) != 0) {
372            if (!itr->second->setActiveScalars(scalarName)) {
373                ret = false;
374            } else {
375                needRangeUpdate = true;
376            }
377        }
378    } while (doAll && ++itr != _dataSets.end());
379
380    if (needRangeUpdate) {
381         updateFieldRanges();
382        _needsRedraw = true;
383    }
384
385    return ret;
386}
387
388/**
389 * \brief Set the active vector field array by name for a DataSet
390 */
391bool Renderer::setDataSetActiveVectors(const DataSetId& id, const char *vectorName)
392{
393    DataSetHashmap::iterator itr;
394
395    bool doAll = false;
396
397    if (id.compare("all") == 0) {
398        itr = _dataSets.begin();
399        doAll = true;
400    } else {
401        itr = _dataSets.find(id);
402    }
403    if (itr == _dataSets.end()) {
404        ERROR("DataSet not found: %s", id.c_str());
405        return false;
406    }
407
408    bool ret = true;
409    bool needRangeUpdate = false;
410    do {
411        if (strcmp(itr->second->getActiveVectorsName(), vectorName) != 0) {
412            if (!itr->second->setActiveVectors(vectorName)) {
413                ret = false;
414            } else {
415                needRangeUpdate = true;
416            }
417        }
418    } while (doAll && ++itr != _dataSets.end());
419
420    if (needRangeUpdate) {
421        updateFieldRanges();
422        _needsRedraw = true;
423    }
424
425    return ret;
426}
427
428/**
429 * \brief Control whether the cumulative data range of datasets is used for
430 * colormapping and contours
431 *
432 * NOTE: If using explicit range(s), setting cumulative ranges on will
433 * remove explicit ranges on all fields
434 */
435void Renderer::setUseCumulativeDataRange(bool state, bool onlyVisible)
436{
437    TRACE("use cumulative: %d, only visible: %d", (state ? 1 : 0), (onlyVisible ? 1 : 0));
438    _useCumulativeRange = state;
439    _cumulativeRangeOnlyVisible = onlyVisible;
440    clearUserFieldRanges();
441    updateFieldRanges();
442    resetAxes();
443    _needsRedraw = true;
444}
445
446void Renderer::resetAxes(double bounds[6])
447{
448    TRACE("Resetting axes");
449
450    if (_cubeAxesActor == NULL) {
451        initAxes();
452    }
453
454    if (_cameraMode == IMAGE) {
455        _cubeAxesActor->SetUse2DMode(1);
456    } else {
457        _cubeAxesActor->SetUse2DMode(0);
458    }
459
460    if (_cameraMode == IMAGE) {
461        return;
462    }
463
464    setAxesBounds(bounds);
465    setAxesRanges();
466}
467
468/**
469 * \brief Explicitly set world coordinate bounds of axes
470 *
471 * This determines the region in world coordinates around which
472 * the axes are drawn
473 */
474void Renderer::setAxesBounds(double min, double max)
475{
476    setAxisBounds(X_AXIS, min, max);
477    setAxisBounds(Y_AXIS, min, max);
478    setAxisBounds(Z_AXIS, min, max);
479}
480
481/**
482 * \brief Set axes bounds based on auto/explicit settings
483 */
484void Renderer::setAxesBounds(double boundsIn[6])
485{
486    double newBounds[6];
487    if (boundsIn != NULL) {
488        memcpy(&newBounds[0], &boundsIn[0], sizeof(double)*6);
489    } else {
490        if (_axesAutoBounds[X_AXIS] ||
491            _axesAutoBounds[Y_AXIS] ||
492            _axesAutoBounds[Z_AXIS]) {
493            collectBounds(newBounds, false);
494        }
495    }
496
497    double bounds[6];
498    if (_axesAutoBounds[X_AXIS]) {
499        memcpy(&bounds[0], &newBounds[0], sizeof(double)*2);
500    } else {
501        memcpy(&bounds[0], &_axesUserBounds[0], sizeof(double)*2);
502    }
503    if (_axesAutoBounds[Y_AXIS]) {
504        memcpy(&bounds[2], &newBounds[2], sizeof(double)*2);
505    } else {
506        memcpy(&bounds[2], &_axesUserBounds[2], sizeof(double)*2);
507    }
508    if (_axesAutoBounds[Z_AXIS]) {
509        memcpy(&bounds[4], &newBounds[4], sizeof(double)*2);
510    } else {
511        memcpy(&bounds[4], &_axesUserBounds[4], sizeof(double)*2);
512    }
513
514    _cubeAxesActor->SetBounds(bounds);
515
516    TRACE("Axis bounds: %g %g %g %g %g %g",
517              bounds[0],
518              bounds[1],
519              bounds[2],
520              bounds[3],
521              bounds[4],
522              bounds[5]);
523
524    _needsRedraw = true;
525}
526
527/**
528 * \brief Toggle automatic vs. explicit range setting on all axes
529 */
530void Renderer::setAxesAutoBounds(bool state)
531{
532    _axesAutoBounds[X_AXIS] = state;
533    _axesAutoBounds[Y_AXIS] = state;
534    _axesAutoBounds[Z_AXIS] = state;
535
536    TRACE("Set axes bounds to %s", (state ? "auto" : "explicit"));
537
538    setAxesBounds();
539
540    _needsRedraw = true;
541}
542
543/**
544 * \brief Toggle automatic vs. explicit range setting on all axes
545 */
546void Renderer::setAxisAutoBounds(Axis axis, bool state)
547{
548    _axesAutoBounds[axis] = state;
549
550    TRACE("Set axis %d bounds to %s", axis, (state ? "auto" : "explicit"));
551
552    setAxesBounds();
553
554    _needsRedraw = true;
555}
556
557/**
558 * \brief Toggle automatic vs. explicit range setting on all axes
559 */
560void Renderer::setAxesAutoRange(bool state)
561{
562    _axesRangeMode[X_AXIS] = state ? RANGE_AUTO : RANGE_EXPLICIT;
563    _axesRangeMode[Y_AXIS] = state ? RANGE_AUTO : RANGE_EXPLICIT;
564    _axesRangeMode[Z_AXIS] = state ? RANGE_AUTO : RANGE_EXPLICIT;
565
566    TRACE("Set axes range to %s", (state ? "auto" : "explicit"));
567
568    setAxesRanges();
569
570    _needsRedraw = true;
571}
572
573/**
574 * \brief Explicitly set world coordinate bounds of axis
575 *
576 * This determines the region in world coordinates around which
577 * the axes are drawn
578 */
579void Renderer::setAxisBounds(Axis axis, double min, double max)
580{
581    double bounds[6];
582    _cubeAxesActor->GetBounds(bounds);
583
584    bounds[axis*2  ] = _axesUserBounds[axis*2  ] = min;
585    bounds[axis*2+1] = _axesUserBounds[axis*2+1] = max;
586
587    _cubeAxesActor->SetBounds(bounds);
588
589    _axesAutoBounds[axis] = false;
590
591    TRACE("Axis bounds: %g %g %g %g %g %g",
592              bounds[0],
593              bounds[1],
594              bounds[2],
595              bounds[3],
596              bounds[4],
597              bounds[5]);
598
599    _needsRedraw = true;
600}
601
602/**
603 * \brief Toggle automatic vs. explicit range setting on specific axis
604 */
605void Renderer::setAxisAutoRange(Axis axis, bool state)
606{
607    _axesRangeMode[axis] = state ? RANGE_AUTO : RANGE_EXPLICIT;
608
609    TRACE("Set axis %d range to %s", axis, (state ? "auto" : "explicit"));
610
611    setAxesRanges();
612
613    _needsRedraw = true;
614}
615
616/**
617 * \brief Set explicit range on axes and disable auto range
618 */
619void Renderer::setAxesRange(double min, double max)
620{
621    setAxisRange(X_AXIS, min, max);
622    setAxisRange(Y_AXIS, min, max);
623    setAxisRange(Z_AXIS, min, max);
624}
625
626/**
627 * \brief Explicitly set power multiplier for axes labels
628 *
629 * The exponent will appear in the axis title and be omitted from labels
630 */
631void Renderer::setAxesLabelPowerScaling(int xPow, int yPow, int zPow, bool useCustom)
632{
633    _cubeAxesActor->SetLabelScaling(!useCustom, xPow, yPow, zPow);
634
635    TRACE("Set axis label scaling: custom: %d, powers: %d,%d,%d", (int)useCustom, xPow, yPow, zPow);
636
637    _needsRedraw = true;
638}
639
640/**
641 * \brief Set explicit range on axis and disable auto range
642 */
643void Renderer::setAxisRange(Axis axis, double min, double max)
644{
645    switch (axis) {
646    case X_AXIS:
647        _axesRangeMode[X_AXIS] = RANGE_EXPLICIT;
648        _cubeAxesActor->SetXAxisRange(min, max);
649        break;
650    case Y_AXIS:
651        _axesRangeMode[Y_AXIS] = RANGE_EXPLICIT;
652        _cubeAxesActor->SetYAxisRange(min, max);
653        break;
654    case Z_AXIS:
655        _axesRangeMode[Z_AXIS] = RANGE_EXPLICIT;
656        _cubeAxesActor->SetZAxisRange(min, max);
657        break;
658    default:
659        break;
660    }
661
662    TRACE("Set axis %d range to: %g, %g", axis, min, max);
663
664     _needsRedraw = true;
665}
666
667/**
668 * \brief Set axes ranges based on bounds and range modes
669 */
670void Renderer::setAxesRanges()
671{
672    computeAxesScale();
673
674    double bounds[6];
675    _cubeAxesActor->GetBounds(bounds);
676
677    double ranges[6];
678    if (_axesRangeMode[X_AXIS] != RANGE_EXPLICIT) {
679        ranges[0] = bounds[0] * _axesScale[X_AXIS];
680        ranges[1] = bounds[1] * _axesScale[X_AXIS];
681        _cubeAxesActor->SetXAxisRange(&ranges[0]);
682    } else {
683        _cubeAxesActor->GetXAxisRange(&ranges[0]);
684    }
685
686    if (_axesRangeMode[Y_AXIS] != RANGE_EXPLICIT) {
687        ranges[2] = bounds[2] * _axesScale[Y_AXIS];
688        ranges[3] = bounds[3] * _axesScale[Y_AXIS];
689        _cubeAxesActor->SetYAxisRange(&ranges[2]);
690    } else {
691        _cubeAxesActor->GetYAxisRange(&ranges[2]);
692    }
693
694    if (_axesRangeMode[Z_AXIS] != RANGE_EXPLICIT) {
695        ranges[4] = bounds[4] * _axesScale[Z_AXIS];
696        ranges[5] = bounds[5] * _axesScale[Z_AXIS];
697        _cubeAxesActor->SetZAxisRange(&ranges[4]);
698    } else {
699        _cubeAxesActor->GetZAxisRange(&ranges[4]);
700    }
701
702    TRACE("Axis ranges: %g %g %g %g %g %g",
703          ranges[0],
704          ranges[1],
705          ranges[2],
706          ranges[3],
707          ranges[4],
708          ranges[5]);
709
710    _needsRedraw = true;
711}
712
713/**
714 * \brief Compute scaling from axes bounds to ranges
715 *
716 * Uses actor scaling to determine if world coordinates
717 * need to be scaled to undo actor scaling in range labels
718 */
719void Renderer::computeAxesScale()
720{
721    if (_axesRangeMode[X_AXIS] != RANGE_AUTO &&
722        _axesRangeMode[Y_AXIS] != RANGE_AUTO &&
723        _axesRangeMode[Z_AXIS] != RANGE_AUTO)
724        return;
725
726    double bounds[6];
727    if (_cameraMode == IMAGE) {
728        collectBounds(bounds, false);
729    } else
730        _cubeAxesActor->GetBounds(bounds);
731
732    double unscaledBounds[6];
733    collectUnscaledBounds(unscaledBounds, false);
734
735    if (_axesRangeMode[X_AXIS] == RANGE_AUTO) {
736        double sx = (bounds[1] == bounds[0]) ? 1.0 : (unscaledBounds[1] - unscaledBounds[0]) / (bounds[1] - bounds[0]);
737        _axesScale[X_AXIS] = sx;
738        TRACE("Setting X axis scale to: %g", sx);
739    }
740    if (_axesRangeMode[Y_AXIS] == RANGE_AUTO) {
741        double sy = (bounds[3] == bounds[2]) ? 1.0 : (unscaledBounds[3] - unscaledBounds[2]) / (bounds[3] - bounds[2]);
742        _axesScale[Y_AXIS] = sy;
743        TRACE("Setting Y axis scale to: %g", sy);
744    }
745    if (_axesRangeMode[Y_AXIS] == RANGE_AUTO) {
746        double sz = (bounds[5] == bounds[4]) ? 1.0 : (unscaledBounds[5] - unscaledBounds[4]) / (bounds[5] - bounds[4]);
747        _axesScale[Z_AXIS] = sz;
748        TRACE("Setting Z axis scale to: %g", sz);
749    }
750}
751
752/**
753 * \brief Set scaling factor to convert world coordinates to axis
754 * label values in X, Y, Z
755 */
756void Renderer::setAxesScale(double scale)
757{
758    _axesRangeMode[X_AXIS] = RANGE_SCALE_BOUNDS;
759    _axesRangeMode[Y_AXIS] = RANGE_SCALE_BOUNDS;
760    _axesRangeMode[Z_AXIS] = RANGE_SCALE_BOUNDS;
761
762    _axesScale[X_AXIS] = scale;
763    _axesScale[Y_AXIS] = scale;
764    _axesScale[Z_AXIS] = scale;
765
766    TRACE("Setting axes scale to: %g", scale);
767
768    setAxesRanges();
769}
770
771/**
772 * \brief Set scaling factor to convert world coordinates to axis label values
773 */
774void Renderer::setAxisScale(Axis axis, double scale)
775{
776    _axesRangeMode[axis] = RANGE_SCALE_BOUNDS;
777    _axesScale[axis] = scale;
778
779    TRACE("Setting axis %d scale to: %g", axis, scale);
780
781    setAxesRanges();
782}
783
784/**
785 * \brief Set an origin point within the axes bounds where the axes will intersect
786 *
787 * \param x X coordinate of origin
788 * \param y Y coordinate of origin
789 * \param z Z coordinate of origin
790 * \param useCustom Flag indicating if custom origin is to be used/enabled
791 */
792void Renderer::setAxesOrigin(double x, double y, double z, bool useCustom)
793{
794    _cubeAxesActor->SetAxisOrigin(x, y, z);
795    _cubeAxesActor->SetUseAxisOrigin((useCustom ? 1 : 0));
796
797    _needsRedraw = true;
798}
799
800/**
801 * \brief Set inital properties on the 2D Axes
802 */
803void Renderer::initAxes()
804{
805    TRACE("Initializing axes");
806#ifdef USE_CUSTOM_AXES
807    if (_cubeAxesActor == NULL)
808        _cubeAxesActor = vtkSmartPointer<vtkRpCubeAxesActor>::New();
809#else
810    if (_cubeAxesActor == NULL)
811        _cubeAxesActor = vtkSmartPointer<vtkCubeAxesActor>::New();
812#endif
813    _cubeAxesActor->SetCamera(_renderer->GetActiveCamera());
814    _cubeAxesActor->GetProperty()->LightingOff();
815    // Don't offset labels at origin
816    _cubeAxesActor->SetCornerOffset(0);
817    _cubeAxesActor->SetFlyModeToStaticTriad();
818
819    _cubeAxesActor->SetGridLineLocation(VTK_GRID_LINES_ALL);
820    _cubeAxesActor->SetDrawXInnerGridlines(0);
821    _cubeAxesActor->SetDrawYInnerGridlines(0);
822    _cubeAxesActor->SetDrawZInnerGridlines(0);
823    _cubeAxesActor->SetDrawXGridpolys(0);
824    _cubeAxesActor->SetDrawYGridpolys(0);
825    _cubeAxesActor->SetDrawZGridpolys(0);
826    _cubeAxesActor->SetDistanceLODThreshold(1);
827    _cubeAxesActor->SetEnableDistanceLOD(0);
828    _cubeAxesActor->SetViewAngleLODThreshold(0);
829    _cubeAxesActor->SetEnableViewAngleLOD(0);
830    // Attempt to set font sizes for 2D (this currently doesn't work)
831    for (int i = 0; i < 3; i++) {
832        _cubeAxesActor->GetTitleTextProperty(i)->SetFontSize(14);
833        _cubeAxesActor->GetLabelTextProperty(i)->SetFontSize(12);
834    }
835    // Set font pixel size for 3D
836    _cubeAxesActor->SetScreenSize(8.0);
837
838    _cubeAxesActor->SetBounds(0, 1, 0, 1, 0, 1);
839    _cubeAxesActor->SetXAxisRange(0, 1);
840    _cubeAxesActor->SetYAxisRange(0, 1);
841    _cubeAxesActor->SetZAxisRange(0, 1);
842    for (int axis = 0; axis < 3; axis++) {
843        _axesAutoBounds[axis] = true;
844        _axesRangeMode[axis] = RANGE_AUTO;
845        _axesScale[axis] = 1.0;
846    }
847
848    double axesColor[] = {1,1,1};
849    setAxesColor(axesColor);
850
851    if (!_renderer->HasViewProp(_cubeAxesActor))
852        _renderer->AddViewProp(_cubeAxesActor);
853
854    if (_cameraMode == IMAGE) {
855        _cubeAxesActor->SetUse2DMode(1);
856    } else {
857        _cubeAxesActor->SetUse2DMode(0);
858    }
859}
860
861/**
862 * \brief Set Fly mode of axes
863 */
864void Renderer::setAxesFlyMode(AxesFlyMode mode)
865{
866    if (_cubeAxesActor == NULL)
867        return;
868
869    switch (mode) {
870    case FLY_STATIC_EDGES:
871        _cubeAxesActor->SetFlyModeToStaticEdges();
872        _cubeAxesActor->SetGridLineLocation(VTK_GRID_LINES_ALL);
873        break;
874    case FLY_STATIC_TRIAD:
875        _cubeAxesActor->SetFlyModeToStaticTriad();
876        _cubeAxesActor->SetGridLineLocation(VTK_GRID_LINES_ALL);
877        break;
878    case FLY_OUTER_EDGES:
879        _cubeAxesActor->SetFlyModeToOuterEdges();
880        _cubeAxesActor->SetGridLineLocation(VTK_GRID_LINES_ALL);
881        break;
882    case FLY_FURTHEST_TRIAD:
883        _cubeAxesActor->SetFlyModeToFurthestTriad();
884        _cubeAxesActor->SetGridLineLocation(VTK_GRID_LINES_FURTHEST);
885        break;
886    case FLY_CLOSEST_TRIAD:
887    default:
888        _cubeAxesActor->SetFlyModeToClosestTriad();
889        _cubeAxesActor->SetGridLineLocation(VTK_GRID_LINES_CLOSEST);
890        break;
891    }
892    _needsRedraw = true;
893}
894
895/**
896 * \brief Set color of axes, ticks, labels, titles
897 */
898void Renderer::setAxesColor(double color[3], double opacity)
899{
900    setAxesTitleColor(color, opacity);
901    setAxesLabelColor(color, opacity);
902    setAxesLinesColor(color, opacity);
903    setAxesGridlinesColor(color, opacity);
904    setAxesInnerGridlinesColor(color, opacity);
905    setAxesGridpolysColor(color, opacity);
906}
907
908/**
909 * \brief Set color of axes title text
910 */
911void Renderer::setAxesTitleColor(double color[3], double opacity)
912{
913    setAxisTitleColor(X_AXIS, color, opacity);
914    setAxisTitleColor(Y_AXIS, color, opacity);
915    setAxisTitleColor(Z_AXIS, color, opacity);
916}
917
918/**
919 * \brief Set color of axes label text
920 */
921void Renderer::setAxesLabelColor(double color[3], double opacity)
922{
923    setAxisLabelColor(X_AXIS, color, opacity);
924    setAxisLabelColor(Y_AXIS, color, opacity);
925    setAxisLabelColor(Z_AXIS, color, opacity);
926}
927
928/**
929 * \brief Set color of main axes lines
930 */
931void Renderer::setAxesLinesColor(double color[3], double opacity)
932{
933    setAxisLinesColor(X_AXIS, color, opacity);
934    setAxisLinesColor(Y_AXIS, color, opacity);
935    setAxisLinesColor(Z_AXIS, color, opacity);
936}
937
938/**
939 * \brief Set color of axis gridlines
940 */
941void Renderer::setAxesGridlinesColor(double color[3], double opacity)
942{
943    setAxisGridlinesColor(X_AXIS, color, opacity);
944    setAxisGridlinesColor(Y_AXIS, color, opacity);
945    setAxisGridlinesColor(Z_AXIS, color, opacity);
946}
947
948/**
949 * \brief Set color of axes inner gridlines
950 */
951void Renderer::setAxesInnerGridlinesColor(double color[3], double opacity)
952{
953    setAxisInnerGridlinesColor(X_AXIS, color, opacity);
954    setAxisInnerGridlinesColor(Y_AXIS, color, opacity);
955    setAxisInnerGridlinesColor(Z_AXIS, color, opacity);
956}
957
958/**
959 * \brief Set color of axes inner grid polygons
960 */
961void Renderer::setAxesGridpolysColor(double color[3], double opacity)
962{
963    setAxisGridpolysColor(X_AXIS, color, opacity);
964    setAxisGridpolysColor(Y_AXIS, color, opacity);
965    setAxisGridpolysColor(Z_AXIS, color, opacity);
966}
967
968/**
969 * \brief Turn on/off rendering of all enabled axes
970 */
971void Renderer::setAxesVisibility(bool state)
972{
973    if (_cubeAxesActor != NULL) {
974        _cubeAxesActor->SetVisibility((state ? 1 : 0));
975        _needsRedraw = true;
976    }
977}
978
979/**
980 * \brief Turn on/off rendering of all axes gridlines
981 */
982void Renderer::setAxesGridVisibility(bool state)
983{
984    setAxisGridVisibility(X_AXIS, state);
985    setAxisGridVisibility(Y_AXIS, state);
986    setAxisGridVisibility(Z_AXIS, state);
987}
988
989/**
990 * \brief Turn on/off rendering of all axes inner gridlines
991 */
992void Renderer::setAxesInnerGridVisibility(bool state)
993{
994    setAxisInnerGridVisibility(X_AXIS, state);
995    setAxisInnerGridVisibility(Y_AXIS, state);
996    setAxisInnerGridVisibility(Z_AXIS, state);
997}
998
999/**
1000 * \brief Turn on/off rendering of all axes inner grid polygons
1001 */
1002void Renderer::setAxesGridpolysVisibility(bool state)
1003{
1004    setAxisGridpolysVisibility(X_AXIS, state);
1005    setAxisGridpolysVisibility(Y_AXIS, state);
1006    setAxisGridpolysVisibility(Z_AXIS, state);
1007}
1008
1009/**
1010 * \brief Turn on/off rendering of all axis labels
1011 */
1012void Renderer::setAxesLabelVisibility(bool state)
1013{
1014    setAxisLabelVisibility(X_AXIS, state);
1015    setAxisLabelVisibility(Y_AXIS, state);
1016    setAxisLabelVisibility(Z_AXIS, state);
1017}
1018
1019/**
1020 * \brief Turn on/off rendering of all axis ticks
1021 */
1022void Renderer::setAxesTickVisibility(bool state)
1023{
1024    setAxisTickVisibility(X_AXIS, state);
1025    setAxisTickVisibility(Y_AXIS, state);
1026    setAxisTickVisibility(Z_AXIS, state);
1027}
1028
1029/**
1030 * \brief Turn on/off rendering of all axis ticks
1031 */
1032void Renderer::setAxesMinorTickVisibility(bool state)
1033{
1034    setAxisMinorTickVisibility(X_AXIS, state);
1035    setAxisMinorTickVisibility(Y_AXIS, state);
1036    setAxisMinorTickVisibility(Z_AXIS, state);
1037}
1038
1039/**
1040 * \brief Control position of ticks on 3D axes
1041 */
1042void Renderer::setAxesTickPosition(AxesTickPosition pos)
1043{
1044    if (_cubeAxesActor == NULL)
1045        return;
1046
1047    switch (pos) {
1048    case TICKS_BOTH:
1049        _cubeAxesActor->SetTickLocationToBoth();
1050        break;
1051    case TICKS_OUTSIDE:
1052        _cubeAxesActor->SetTickLocationToOutside();
1053        break;
1054    case TICKS_INSIDE:
1055    default:
1056        _cubeAxesActor->SetTickLocationToInside();
1057        break;
1058    }
1059    _needsRedraw = true;
1060}
1061
1062/**
1063 * \brief Controls label scaling by power of 10
1064 */
1065void Renderer::setAxesLabelScaling(bool autoScale, int xpow, int ypow, int zpow)
1066{
1067    if (_cubeAxesActor != NULL) {
1068        _cubeAxesActor->SetLabelScaling(autoScale, xpow, ypow, zpow);
1069        _needsRedraw = true;
1070    }
1071}
1072
1073/**
1074 * \brief Set axes title/label font size in pixels
1075 */
1076void Renderer::setAxesPixelFontSize(double screenSize)
1077{
1078    if (_cubeAxesActor != NULL) {
1079        TRACE("Setting axes font size to: %g px", screenSize);
1080        _cubeAxesActor->SetScreenSize(screenSize);
1081        _needsRedraw = true;
1082    }
1083}
1084
1085/**
1086 * \brief Set axis title font family
1087 */
1088void Renderer::setAxesTitleFont(const char *fontName)
1089{
1090    setAxisTitleFont(X_AXIS, fontName);
1091    setAxisTitleFont(Y_AXIS, fontName);
1092    setAxisTitleFont(Z_AXIS, fontName);
1093}
1094
1095/**
1096 * \brief Set axis title font size
1097 */
1098void Renderer::setAxesTitleFontSize(int sz)
1099{
1100    setAxisTitleFontSize(X_AXIS, sz);
1101    setAxisTitleFontSize(Y_AXIS, sz);
1102    setAxisTitleFontSize(Z_AXIS, sz);
1103}
1104
1105/**
1106 * \brief Set orientation (as rotation in degrees) of axis title
1107 */
1108void Renderer::setAxesTitleOrientation(double orientation)
1109{
1110    setAxisTitleOrientation(X_AXIS, orientation);
1111    setAxisTitleOrientation(Y_AXIS, orientation);
1112    setAxisTitleOrientation(Z_AXIS, orientation);
1113}
1114
1115/**
1116 * \brief Set axis label font family
1117 */
1118void Renderer::setAxesLabelFont(const char *fontName)
1119{
1120    setAxisLabelFont(X_AXIS, fontName);
1121    setAxisLabelFont(Y_AXIS, fontName);
1122    setAxisLabelFont(Z_AXIS, fontName);
1123}
1124
1125/**
1126 * \brief Set axis label font size
1127 */
1128void Renderer::setAxesLabelFontSize(int sz)
1129{
1130    setAxisLabelFontSize(X_AXIS, sz);
1131    setAxisLabelFontSize(Y_AXIS, sz);
1132    setAxisLabelFontSize(Z_AXIS, sz);
1133}
1134
1135/**
1136 * \brief Set orientation (as rotation in degrees) of axis labels
1137 */
1138void Renderer::setAxesLabelOrientation(double orientation)
1139{
1140    setAxisLabelOrientation(X_AXIS, orientation);
1141    setAxisLabelOrientation(Y_AXIS, orientation);
1142    setAxisLabelOrientation(Z_AXIS, orientation);
1143}
1144
1145/**
1146 * \brief Set printf style label format string
1147 */
1148void Renderer::setAxesLabelFormat(const char *format)
1149{
1150    setAxisLabelFormat(X_AXIS, format);
1151    setAxisLabelFormat(Y_AXIS, format);
1152    setAxisLabelFormat(Z_AXIS, format);
1153}
1154
1155/**
1156 * \brief Turn on/off rendering of the specified axis
1157 */
1158void Renderer::setAxisVisibility(Axis axis, bool state)
1159{
1160    if (_cubeAxesActor != NULL) {
1161        if (axis == X_AXIS) {
1162            _cubeAxesActor->SetXAxisVisibility((state ? 1 : 0));
1163        } else if (axis == Y_AXIS) {
1164            _cubeAxesActor->SetYAxisVisibility((state ? 1 : 0));
1165        } else if (axis == Z_AXIS) {
1166            _cubeAxesActor->SetZAxisVisibility((state ? 1 : 0));
1167        }
1168        _needsRedraw = true;
1169    }
1170}
1171
1172/**
1173 * \brief Turn on/off rendering of single axis gridlines
1174 */
1175void Renderer::setAxisGridVisibility(Axis axis, bool state)
1176{
1177    if (_cubeAxesActor != NULL) {
1178        if (axis == X_AXIS) {
1179            _cubeAxesActor->SetDrawXGridlines((state ? 1 : 0));
1180        } else if (axis == Y_AXIS) {
1181            _cubeAxesActor->SetDrawYGridlines((state ? 1 : 0));
1182        } else if (axis == Z_AXIS) {
1183            _cubeAxesActor->SetDrawZGridlines((state ? 1 : 0));
1184        }
1185        _needsRedraw = true;
1186    }
1187}
1188
1189/**
1190 * \brief Turn on/off rendering of single axis gridlines
1191 */
1192void Renderer::setAxisInnerGridVisibility(Axis axis, bool state)
1193{
1194    if (_cubeAxesActor != NULL) {
1195        if (axis == X_AXIS) {
1196            _cubeAxesActor->SetDrawXInnerGridlines((state ? 1 : 0));
1197        } else if (axis == Y_AXIS) {
1198            _cubeAxesActor->SetDrawYInnerGridlines((state ? 1 : 0));
1199        } else if (axis == Z_AXIS) {
1200            _cubeAxesActor->SetDrawZInnerGridlines((state ? 1 : 0));
1201        }
1202        _needsRedraw = true;
1203    }
1204}
1205
1206/**
1207 * \brief Turn on/off rendering of single axis gridlines
1208 */
1209void Renderer::setAxisGridpolysVisibility(Axis axis, bool state)
1210{
1211    if (_cubeAxesActor != NULL) {
1212        if (axis == X_AXIS) {
1213            _cubeAxesActor->SetDrawXGridpolys((state ? 1 : 0));
1214        } else if (axis == Y_AXIS) {
1215            _cubeAxesActor->SetDrawYGridpolys((state ? 1 : 0));
1216        } else if (axis == Z_AXIS) {
1217            _cubeAxesActor->SetDrawZGridpolys((state ? 1 : 0));
1218        }
1219        _needsRedraw = true;
1220    }
1221}
1222
1223/**
1224 * \brief Toggle label visibility for the specified axis
1225 */
1226void Renderer::setAxisLabelVisibility(Axis axis, bool state)
1227{
1228    if (_cubeAxesActor != NULL) {
1229        if (axis == X_AXIS) {
1230            _cubeAxesActor->SetXAxisLabelVisibility((state ? 1 : 0));
1231        } else if (axis == Y_AXIS) {
1232            _cubeAxesActor->SetYAxisLabelVisibility((state ? 1 : 0));
1233        } else if (axis == Z_AXIS) {
1234            _cubeAxesActor->SetZAxisLabelVisibility((state ? 1 : 0));
1235        }
1236        _needsRedraw = true;
1237    }
1238}
1239
1240/**
1241 * \brief Toggle tick visibility for the specified axis
1242 */
1243void Renderer::setAxisTickVisibility(Axis axis, bool state)
1244{
1245    if (_cubeAxesActor != NULL) {
1246        if (axis == X_AXIS) {
1247            _cubeAxesActor->SetXAxisTickVisibility((state ? 1 : 0));
1248        } else if (axis == Y_AXIS) {
1249            _cubeAxesActor->SetYAxisTickVisibility((state ? 1 : 0));
1250        } else if (axis == Z_AXIS) {
1251            _cubeAxesActor->SetZAxisTickVisibility((state ? 1 : 0));
1252        }
1253        _needsRedraw = true;
1254    }
1255}
1256
1257/**
1258 * \brief Toggle tick visibility for the specified axis
1259 */
1260void Renderer::setAxisMinorTickVisibility(Axis axis, bool state)
1261{
1262    if (_cubeAxesActor != NULL) {
1263        if (axis == X_AXIS) {
1264            _cubeAxesActor->SetXAxisMinorTickVisibility((state ? 1 : 0));
1265        } else if (axis == Y_AXIS) {
1266            _cubeAxesActor->SetYAxisMinorTickVisibility((state ? 1 : 0));
1267        } else if (axis == Z_AXIS) {
1268            _cubeAxesActor->SetZAxisMinorTickVisibility((state ? 1 : 0));
1269        }
1270        _needsRedraw = true;
1271    }
1272}
1273
1274/**
1275 * \brief Set color of axis lines, ticks, labels, titles
1276 */
1277void Renderer::setAxisColor(Axis axis, double color[3], double opacity)
1278{
1279    setAxisTitleColor(axis, color, opacity);
1280    setAxisLabelColor(axis, color, opacity);
1281    setAxisLinesColor(axis, color, opacity);
1282    setAxisGridlinesColor(axis, color, opacity);
1283    setAxisInnerGridlinesColor(axis, color, opacity);
1284    setAxisGridpolysColor(axis, color, opacity);
1285}
1286
1287/**
1288 * \brief Set color of axis title text
1289 */
1290void Renderer::setAxisTitleColor(Axis axis, double color[3], double opacity)
1291{
1292    if (_cubeAxesActor != NULL) {
1293        _cubeAxesActor->GetTitleTextProperty(axis)->SetColor(color);
1294        _cubeAxesActor->GetTitleTextProperty(axis)->SetOpacity(opacity);
1295        _needsRedraw = true;
1296    }
1297}
1298
1299/**
1300 * \brief Set color of axis label text
1301 */
1302void Renderer::setAxisLabelColor(Axis axis, double color[3], double opacity)
1303{
1304    if (_cubeAxesActor != NULL) {
1305        _cubeAxesActor->GetLabelTextProperty(axis)->SetColor(color);
1306        _cubeAxesActor->GetLabelTextProperty(axis)->SetOpacity(opacity);
1307        _needsRedraw = true;
1308    }
1309}
1310
1311/**
1312 * \brief Set color of main axis line
1313 */
1314void Renderer::setAxisLinesColor(Axis axis, double color[3], double opacity)
1315{
1316    if (_cubeAxesActor != NULL) {
1317        switch (axis) {
1318        case X_AXIS:
1319            _cubeAxesActor->GetXAxesLinesProperty()->SetColor(color);
1320            _cubeAxesActor->GetXAxesLinesProperty()->SetOpacity(opacity);
1321            break;
1322        case Y_AXIS:
1323            _cubeAxesActor->GetYAxesLinesProperty()->SetColor(color);
1324            _cubeAxesActor->GetYAxesLinesProperty()->SetOpacity(opacity);
1325            break;
1326        case Z_AXIS:
1327            _cubeAxesActor->GetZAxesLinesProperty()->SetColor(color);
1328            _cubeAxesActor->GetZAxesLinesProperty()->SetOpacity(opacity);
1329            break;
1330        default:
1331            ;
1332        }
1333        _needsRedraw = true;
1334    }
1335}
1336
1337/**
1338 * \brief Set color of axis gridlines
1339 */
1340void Renderer::setAxisGridlinesColor(Axis axis, double color[3], double opacity)
1341{
1342    if (_cubeAxesActor != NULL) {
1343        switch (axis) {
1344        case X_AXIS:
1345            _cubeAxesActor->GetXAxesGridlinesProperty()->SetColor(color);
1346            _cubeAxesActor->GetXAxesGridlinesProperty()->SetOpacity(opacity);
1347            break;
1348        case Y_AXIS:
1349            _cubeAxesActor->GetYAxesGridlinesProperty()->SetColor(color);
1350            _cubeAxesActor->GetYAxesGridlinesProperty()->SetOpacity(opacity);
1351            break;
1352        case Z_AXIS:
1353            _cubeAxesActor->GetZAxesGridlinesProperty()->SetColor(color);
1354            _cubeAxesActor->GetZAxesGridlinesProperty()->SetOpacity(opacity);
1355            break;
1356        default:
1357            ;
1358        }
1359        _needsRedraw = true;
1360    }
1361}
1362
1363/**
1364 * \brief Set color of axis inner gridlines
1365 */
1366void Renderer::setAxisInnerGridlinesColor(Axis axis, double color[3], double opacity)
1367{
1368    if (_cubeAxesActor != NULL) {
1369        switch (axis) {
1370        case X_AXIS:
1371            _cubeAxesActor->GetXAxesInnerGridlinesProperty()->SetColor(color);
1372            _cubeAxesActor->GetXAxesInnerGridlinesProperty()->SetOpacity(opacity);
1373            break;
1374        case Y_AXIS:
1375            _cubeAxesActor->GetYAxesInnerGridlinesProperty()->SetColor(color);
1376            _cubeAxesActor->GetYAxesInnerGridlinesProperty()->SetOpacity(opacity);
1377            break;
1378        case Z_AXIS:
1379            _cubeAxesActor->GetZAxesInnerGridlinesProperty()->SetColor(color);
1380            _cubeAxesActor->GetZAxesInnerGridlinesProperty()->SetOpacity(opacity);
1381            break;
1382        default:
1383            ;
1384        }
1385        _needsRedraw = true;
1386    }
1387}
1388
1389/**
1390 * \brief Set color of axis inner grid polygons
1391 *
1392 * Default opacity is 0.6
1393 */
1394void Renderer::setAxisGridpolysColor(Axis axis, double color[3], double opacity)
1395{
1396    if (_cubeAxesActor != NULL) {
1397        switch (axis) {
1398        case X_AXIS:
1399            _cubeAxesActor->GetXAxesGridpolysProperty()->SetColor(color);
1400            _cubeAxesActor->GetXAxesGridpolysProperty()->SetOpacity(opacity);
1401            break;
1402        case Y_AXIS:
1403            _cubeAxesActor->GetYAxesGridpolysProperty()->SetColor(color);
1404            _cubeAxesActor->GetYAxesGridpolysProperty()->SetOpacity(opacity);
1405            break;
1406        case Z_AXIS:
1407            _cubeAxesActor->GetZAxesGridpolysProperty()->SetColor(color);
1408            _cubeAxesActor->GetZAxesGridpolysProperty()->SetOpacity(opacity);
1409            break;
1410        default:
1411            ;
1412        }
1413        _needsRedraw = true;
1414    }
1415}
1416
1417/**
1418 * \brief Set title of the specified axis
1419 */
1420void Renderer::setAxisTitle(Axis axis, const char *title)
1421{
1422    if (_cubeAxesActor != NULL) {
1423        if (axis == X_AXIS) {
1424            _cubeAxesActor->SetXTitle(title);
1425        } else if (axis == Y_AXIS) {
1426            _cubeAxesActor->SetYTitle(title);
1427        } else if (axis == Z_AXIS) {
1428            _cubeAxesActor->SetZTitle(title);
1429        }
1430        _needsRedraw = true;
1431    }
1432}
1433
1434/**
1435 * \brief Set units of the specified axis
1436 */
1437void Renderer::setAxisUnits(Axis axis, const char *units)
1438{
1439    if (_cubeAxesActor != NULL) {
1440        if (axis == X_AXIS) {
1441            _cubeAxesActor->SetXUnits(units);
1442        } else if (axis == Y_AXIS) {
1443            _cubeAxesActor->SetYUnits(units);
1444        } else if (axis == Z_AXIS) {
1445            _cubeAxesActor->SetZUnits(units);
1446        }
1447        _needsRedraw = true;
1448    }
1449}
1450
1451/**
1452 * \brief Set axis title font family
1453 */
1454void Renderer::setAxisTitleFont(Axis axis, const char *fontName)
1455{
1456    if (_cubeAxesActor != NULL) {
1457        TRACE("Setting axis %d title font to: '%s'", axis, fontName);
1458        _cubeAxesActor->GetTitleTextProperty(axis)->SetFontFamilyAsString(fontName);
1459        _needsRedraw = true;
1460    }
1461}
1462
1463/**
1464 * \brief Set axis title font size (and optionally pixel size)
1465 */
1466void Renderer::setAxisTitleFontSize(Axis axis, int sz)
1467{
1468    if (_cubeAxesActor != NULL) {
1469        TRACE("Setting axis %d title font size to: %d", axis, sz);
1470        _cubeAxesActor->GetTitleTextProperty(axis)->SetFontSize(sz);
1471        _needsRedraw = true;
1472    }
1473}
1474
1475/**
1476 * \brief Set orientation (as rotation in degrees) of axis titles
1477 */
1478void Renderer::setAxisTitleOrientation(Axis axis, double orientation)
1479{
1480    if (_cubeAxesActor != NULL) {
1481        TRACE("Setting axis %d title orientation to: %g", axis, orientation);
1482        _cubeAxesActor->GetTitleTextProperty(axis)->SetOrientation(orientation);
1483        _needsRedraw = true;
1484    }
1485}
1486
1487/**
1488 * \brief Set axis label font family
1489 */
1490void Renderer::setAxisLabelFont(Axis axis, const char *fontName)
1491{
1492    if (_cubeAxesActor != NULL) {
1493        TRACE("Setting axis %d label font to: '%s'", axis, fontName);
1494        _cubeAxesActor->GetLabelTextProperty(axis)->SetFontFamilyAsString(fontName);
1495        _needsRedraw = true;
1496    }
1497}
1498
1499/**
1500 * \brief Set axis label font size (and optionally pixel size)
1501 */
1502void Renderer::setAxisLabelFontSize(Axis axis, int sz)
1503{
1504    if (_cubeAxesActor != NULL) {
1505        TRACE("Setting axis %d label font size to: %d", axis, sz);
1506        _cubeAxesActor->GetLabelTextProperty(axis)->SetFontSize(sz);
1507        _needsRedraw = true;
1508    }
1509}
1510
1511/**
1512 * \brief Set orientation (as rotation in degrees) of axis labels
1513 */
1514void Renderer::setAxisLabelOrientation(Axis axis, double orientation)
1515{
1516    if (_cubeAxesActor != NULL) {
1517        TRACE("Setting axis %d label orientation to: %g", axis, orientation);
1518        _cubeAxesActor->GetLabelTextProperty(axis)->SetOrientation(orientation);
1519        _needsRedraw = true;
1520    }
1521}
1522
1523/**
1524 * \brief Set printf style label format string
1525 */
1526void Renderer::setAxisLabelFormat(Axis axis, const char *format)
1527{
1528    if (_cubeAxesActor != NULL) {
1529        TRACE("Setting axis %d label format to: '%s'", axis, format);
1530        if (axis == X_AXIS) {
1531            _cubeAxesActor->SetXLabelFormat(format);
1532            _cubeAxesActor->XAutoLabelFormatOff();
1533        } else if (axis == Y_AXIS) {
1534            _cubeAxesActor->SetYLabelFormat(format);
1535            _cubeAxesActor->YAutoLabelFormatOff();
1536        } else if (axis == Z_AXIS) {
1537            _cubeAxesActor->SetZLabelFormat(format);
1538            _cubeAxesActor->ZAutoLabelFormatOff();
1539        }
1540        _needsRedraw = true;
1541    }
1542}
1543
1544/**
1545 * \brief Notify graphics objects that color map has changed
1546 */
1547void Renderer::updateColorMap(ColorMap *cmap)
1548{
1549    TRACE("%s", cmap->getName().c_str());
1550    updateGraphicsObjectColorMap<Contour2D>(cmap);
1551    updateGraphicsObjectColorMap<Contour3D>(cmap);
1552    updateGraphicsObjectColorMap<Cutplane>(cmap);
1553    updateGraphicsObjectColorMap<Glyphs>(cmap);
1554    updateGraphicsObjectColorMap<HeightMap>(cmap);
1555    updateGraphicsObjectColorMap<LIC>(cmap);
1556    updateGraphicsObjectColorMap<Molecule>(cmap);
1557    updateGraphicsObjectColorMap<PseudoColor>(cmap);
1558    updateGraphicsObjectColorMap<Streamlines>(cmap);
1559    updateGraphicsObjectColorMap<Volume>(cmap);
1560    updateGraphicsObjectColorMap<Warp>(cmap);
1561    TRACE("Leave");
1562}
1563
1564/**
1565 * \brief Check if a ColorMap is in use by graphics objects
1566 */
1567bool Renderer::colorMapUsed(ColorMap *cmap)
1568{
1569    if (graphicsObjectColorMapUsed<Contour2D>(cmap))
1570        return true;
1571    if (graphicsObjectColorMapUsed<Contour3D>(cmap))
1572        return true;
1573    if (graphicsObjectColorMapUsed<Cutplane>(cmap))
1574        return true;
1575    if (graphicsObjectColorMapUsed<Glyphs>(cmap))
1576        return true;
1577    if (graphicsObjectColorMapUsed<HeightMap>(cmap))
1578        return true;
1579    if (graphicsObjectColorMapUsed<LIC>(cmap))
1580        return true;
1581    if (graphicsObjectColorMapUsed<Molecule>(cmap))
1582        return true;
1583    if (graphicsObjectColorMapUsed<PseudoColor>(cmap))
1584        return true;
1585    if (graphicsObjectColorMapUsed<Streamlines>(cmap))
1586        return true;
1587    if (graphicsObjectColorMapUsed<Volume>(cmap))
1588        return true;
1589    if (graphicsObjectColorMapUsed<Warp>(cmap))
1590        return true;
1591
1592    return false;
1593}
1594
1595/**
1596 * \brief Add/replace a ColorMap for use in the Renderer
1597 */
1598void Renderer::addColorMap(const ColorMapId& id, ColorMap *colorMap)
1599{
1600    if (colorMap != NULL) {
1601        colorMap->build();
1602        if (getColorMap(id) != NULL) {
1603            TRACE("Replacing existing ColorMap %s", id.c_str());
1604            // Copy to current colormap to avoid invalidating
1605            // pointers in graphics objects using the color map
1606            *_colorMaps[id] = *colorMap;
1607            delete colorMap;
1608            // Notify graphics objects of change
1609            updateColorMap(_colorMaps[id]);
1610        } else
1611            _colorMaps[id] = colorMap;
1612    } else {
1613        ERROR("NULL ColorMap");
1614    }
1615}
1616
1617/**
1618 * \brief Return the ColorMap associated with the colormap key given
1619 */
1620ColorMap *Renderer::getColorMap(const ColorMapId& id)
1621{
1622    ColorMapHashmap::iterator itr = _colorMaps.find(id);
1623
1624    if (itr == _colorMaps.end())
1625        return NULL;
1626    else
1627        return itr->second;
1628}
1629
1630/**
1631 * \brief Remove the colormap associated with the key given
1632 *
1633 * The underlying vtkLookupTable will be deleted if it is not referenced
1634 * by any other objects
1635 */
1636void Renderer::deleteColorMap(const ColorMapId& id)
1637{
1638    ColorMapHashmap::iterator itr;
1639
1640    bool doAll = false;
1641
1642    if (id.compare("all") == 0) {
1643        itr = _colorMaps.begin();
1644        doAll = true;
1645    } else {
1646        itr = _colorMaps.find(id);
1647    }
1648
1649    if (itr == _colorMaps.end()) {
1650        ERROR("Unknown ColorMap %s", id.c_str());
1651        return;
1652    }
1653
1654    do {
1655        if (itr->second->getName().compare("default") == 0 ||
1656            itr->second->getName().compare("grayDefault") == 0 ||
1657            itr->second->getName().compare("volumeDefault") == 0 ||
1658            itr->second->getName().compare("elementDefault") == 0) {
1659            if (id.compare("all") != 0) {
1660                WARN("Cannot delete a default color map");
1661            }
1662            continue;
1663        } else if (colorMapUsed(itr->second)) {
1664            WARN("Cannot delete color map '%s', it is in use", itr->second->getName().c_str());
1665            continue;
1666        }
1667
1668        TRACE("Deleting ColorMap %s", itr->second->getName().c_str());
1669
1670        delete itr->second;
1671        itr = _colorMaps.erase(itr);
1672    } while (doAll && itr != _colorMaps.end());
1673}
1674
1675/**
1676 * \brief Set the number of discrete colors used in the colormap's lookup table
1677 *
1678 * Note that the number of table entries is independent of the number of
1679 * control points in the color/alpha ramp
1680 */
1681void Renderer::setColorMapNumberOfTableEntries(const ColorMapId& id, int numEntries)
1682{
1683    ColorMapHashmap::iterator itr;
1684
1685    bool doAll = false;
1686
1687    if (id.compare("all") == 0) {
1688        itr = _colorMaps.begin();
1689        doAll = true;
1690    } else {
1691        itr = _colorMaps.find(id);
1692    }
1693
1694    if (itr == _colorMaps.end()) {
1695        ERROR("Unknown ColorMap %s", id.c_str());
1696        return;
1697    }
1698
1699    if (numEntries < 0) {
1700        numEntries = 256;
1701        TRACE("Setting numEntries to default value of %d", numEntries);
1702    }
1703
1704    do {
1705        if (itr->second->getName() == "elementDefault") {
1706            TRACE("Can't change number of table entries for default element color map");
1707        } else {
1708            itr->second->setNumberOfTableEntries(numEntries);
1709            updateColorMap(itr->second);
1710        }
1711    } while (doAll && ++itr != _colorMaps.end());
1712
1713    _needsRedraw = true;
1714}
1715
1716/**
1717 * \brief Render a labelled legend image for the given colormap
1718 *
1719 * The field is assumed to be the active scalar or vector field
1720 * based on the legendType.
1721 *
1722 * \param[in] id ColorMap name
1723 * \param[in] dataSetID DataSet name
1724 * \param[in] legendType scalar or vector field legend
1725 * \param[in,out] title If supplied, draw title ("#auto" means to
1726 * fill in field name and draw).  If blank, do not draw title. 
1727 * If title was blank or "#auto", will be filled with field name on
1728 * return
1729 * \param[in,out] range Data range to use in legend.  Set min > max to have
1730 * range computed, will be filled with valid min and max values
1731 * \param[in] width Pixel width of legend (aspect controls orientation)
1732 * \param[in] height Pixel height of legend (aspect controls orientation)
1733 * \param[in] opaque Flag to control if legend is rendered opaque or translucent
1734 * \param[in] numLabels Number of labels to render (includes min/max)
1735 * \param[in,out] imgData Pointer to array to fill with image bytes. Array
1736 * will be resized if needed.
1737 * \return The image is rendered into the supplied array, false is
1738 * returned if the color map is not found
1739 */
1740bool Renderer::renderColorMap(const ColorMapId& id,
1741                              const DataSetId& dataSetID,
1742                              Renderer::LegendType legendType,
1743                              std::string& title,
1744                              double range[2],
1745                              int width, int height,
1746                              bool opaque,
1747                              int numLabels,
1748                              vtkUnsignedCharArray *imgData)
1749{
1750    DataSet *dataSet = NULL;
1751    if (dataSetID.compare("all") == 0) {
1752        if (_dataSets.empty()) {
1753            WARN("No DataSets exist, can't fill title or range");
1754            return renderColorMap(id, dataSetID, legendType,
1755                                  NULL,
1756                                  DataSet::POINT_DATA,
1757                                  title, range, width, height, opaque, numLabels, imgData);
1758        } else {
1759            dataSet = _dataSets.begin()->second;
1760        }
1761    } else {
1762        dataSet = getDataSet(dataSetID);
1763        if (dataSet == NULL) {
1764            ERROR("DataSet '%s' not found", dataSetID.c_str());
1765            return false;
1766        }
1767    }
1768
1769    if (legendType == LEGEND_SCALAR) {
1770        return renderColorMap(id, dataSetID, legendType,
1771                              dataSet->getActiveScalarsName(),
1772                              dataSet->getActiveScalarsType(),
1773                              title, range, width, height, opaque, numLabels, imgData);
1774    } else {
1775        return renderColorMap(id, dataSetID, legendType,
1776                              dataSet->getActiveVectorsName(),
1777                              dataSet->getActiveVectorsType(),
1778                              title, range, width, height, opaque, numLabels, imgData);
1779    }
1780}
1781
1782/**
1783 * \brief Render a labelled legend image for the given colormap
1784 *
1785 * The field is assumed to be point data, if the field is not found
1786 * as point data, cell data is used.
1787 *
1788 * \param[in] id ColorMap name
1789 * \param[in] dataSetID DataSet name
1790 * \param[in] legendType scalar or vector field legend
1791 * \param[in] fieldName Name of the field array this legend is for
1792 * \param[in,out] title If supplied, draw title ("#auto" means to
1793 * fill in field name and draw).  If blank, do not draw title. 
1794 * If title was blank or "#auto", will be filled with field name on
1795 * return
1796 * \param[in,out] range Data range to use in legend.  Set min > max to have
1797 * range computed, will be filled with valid min and max values
1798 * \param[in] width Pixel width of legend (aspect controls orientation)
1799 * \param[in] height Pixel height of legend (aspect controls orientation)
1800 * \param[in] opaque Flag to control if legend is rendered opaque or translucent
1801 * \param[in] numLabels Number of labels to render (includes min/max)
1802 * \param[in,out] imgData Pointer to array to fill with image bytes. Array
1803 * will be resized if needed.
1804 * \return The image is rendered into the supplied array, false is
1805 * returned if the color map is not found
1806 */
1807bool Renderer::renderColorMap(const ColorMapId& id,
1808                              const DataSetId& dataSetID,
1809                              Renderer::LegendType legendType,
1810                              const char *fieldName,
1811                              std::string& title,
1812                              double range[2],
1813                              int width, int height,
1814                              bool opaque,
1815                              int numLabels,
1816                              vtkUnsignedCharArray *imgData)
1817{
1818    DataSet *dataSet = NULL;
1819    if (dataSetID.compare("all") == 0) {
1820        if (_dataSets.empty()) {
1821            WARN("No DataSets exist, can't fill title or range");
1822            return renderColorMap(id, dataSetID, legendType,
1823                                  NULL,
1824                                  DataSet::POINT_DATA,
1825                                  title, range, width, height, opaque, numLabels, imgData);
1826        } else {
1827            dataSet = _dataSets.begin()->second;
1828        }
1829    } else {
1830        dataSet = getDataSet(dataSetID);
1831        if (dataSet == NULL) {
1832            ERROR("DataSet '%s' not found", dataSetID.c_str());
1833            return false;
1834        }
1835    }
1836
1837    DataSet::DataAttributeType attrType;
1838    int numComponents;
1839
1840    dataSet->getFieldInfo(fieldName, &attrType, &numComponents);
1841
1842    return renderColorMap(id, dataSetID, legendType,
1843                          fieldName,
1844                          attrType,
1845                          title, range, width, height, opaque, numLabels, imgData);
1846}
1847
1848/**
1849 * \brief Render a labelled legend image for the given colormap
1850 *
1851 * \param[in] id ColorMap name
1852 * \param[in] dataSetID DataSet name
1853 * \param[in] legendType scalar or vector field legend
1854 * \param[in] fieldName Name of the field array this legend is for
1855 * \param[in] type DataAttributeType of the field
1856 * \param[in,out] title If supplied, draw title ("#auto" means to
1857 * fill in field name and draw).  If blank, do not draw title. 
1858 * If title was blank or "#auto", will be filled with field name on
1859 * return
1860 * \param[in,out] range Data range to use in legend.  Set min > max to have
1861 * range computed, will be filled with valid min and max values
1862 * \param[in] width Pixel width of legend (aspect controls orientation)
1863 * \param[in] height Pixel height of legend (aspect controls orientation)
1864 * \param[in] opaque Flag to control if legend is rendered opaque or translucent
1865 * \param[in] numLabels Number of labels to render (includes min/max)
1866 * \param[in,out] imgData Pointer to array to fill with image bytes. Array
1867 * will be resized if needed.
1868 * \return The image is rendered into the supplied array, false is
1869 * returned if the color map is not found
1870 */
1871bool Renderer::renderColorMap(const ColorMapId& id,
1872                              const DataSetId& dataSetID,
1873                              Renderer::LegendType legendType,
1874                              const char *fieldName,
1875                              DataSet::DataAttributeType type,
1876                              std::string& title,
1877                              double range[2],
1878                              int width, int height,
1879                              bool opaque,
1880                              int numLabels,
1881                              vtkUnsignedCharArray *imgData)
1882{
1883    TRACE("Enter");
1884    ColorMap *colorMap = getColorMap(id);
1885    if (colorMap == NULL)
1886        return false;
1887
1888    if (_legendRenderWindow == NULL) {
1889        _legendRenderWindow = vtkSmartPointer<vtkRenderWindow>::New();
1890        _legendRenderWindow->SetMultiSamples(0);
1891#ifdef USE_OFFSCREEN_RENDERING
1892        _legendRenderWindow->DoubleBufferOff();
1893        _legendRenderWindow->OffScreenRenderingOn();
1894#else
1895        _legendRenderWindow->DoubleBufferOn();
1896        _legendRenderWindow->SwapBuffersOff();
1897#endif
1898    }
1899
1900    _legendRenderWindow->SetSize(width, height);
1901
1902    if (_legendRenderer == NULL) {
1903        _legendRenderer = vtkSmartPointer<vtkRenderer>::New();
1904        _legendRenderWindow->AddRenderer(_legendRenderer);
1905    }
1906    _legendRenderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
1907
1908    if (_scalarBarActor == NULL) {
1909        _scalarBarActor = vtkSmartPointer<vtkScalarBarActor>::New();
1910        _scalarBarActor->DrawFrameOff();
1911        _scalarBarActor->DrawBackgroundOff();
1912        _legendRenderer->AddViewProp(_scalarBarActor);
1913    }
1914
1915    if (opaque) {
1916        _scalarBarActor->UseOpacityOff();
1917    } else {
1918        _scalarBarActor->UseOpacityOn();
1919    }
1920
1921    if (width > height) {
1922        _scalarBarActor->SetOrientationToHorizontal();
1923    } else {
1924        _scalarBarActor->SetOrientationToVertical();
1925    }
1926
1927    // Set viewport-relative width/height/pos
1928    if (title.empty() && numLabels == 0) {
1929#ifdef NEW_SCALAR_BAR
1930        _scalarBarActor->SetBarRatio(1);
1931        _scalarBarActor->SetTitleRatio(0);
1932#endif
1933        if (width > height) {
1934            // horizontal
1935#ifdef NEW_SCALAR_BAR
1936            _scalarBarActor->SetDisplayPosition(0, 0);
1937            _scalarBarActor->GetPosition2Coordinate()->SetCoordinateSystemToDisplay();
1938            _scalarBarActor->GetPosition2Coordinate()->SetValue(width+4, height);
1939#else
1940            _scalarBarActor->SetPosition(0, 0);
1941            _scalarBarActor->SetHeight((((double)height+1.5)/((double)height))/0.4); // VTK: floor(actorHeight * .4)
1942            _scalarBarActor->SetWidth(1); // VTK: actorWidth
1943#endif
1944        } else {
1945            // vertical
1946#ifdef NEW_SCALAR_BAR
1947            _scalarBarActor->SetDisplayPosition(0, -4);
1948            _scalarBarActor->GetPosition2Coordinate()->SetCoordinateSystemToDisplay();
1949            _scalarBarActor->GetPosition2Coordinate()->SetValue(width+1, height+5);
1950#else
1951            _scalarBarActor->SetPosition(0, 0);
1952            _scalarBarActor->SetHeight((((double)height+1.5)/((double)height))/0.86); // VTK: floor(actorHeight * .86)
1953            _scalarBarActor->SetWidth(((double)(width+5))/((double)width)); // VTK: actorWidth - 4 pixels
1954#endif
1955        }
1956    } else {
1957#ifdef NEW_SCALAR_BAR
1958        _scalarBarActor->SetBarRatio(0.375);
1959        _scalarBarActor->SetTitleRatio(0.5);
1960        _scalarBarActor->SetDisplayPosition(0, 0);
1961        _scalarBarActor->GetPosition2Coordinate()->SetCoordinateSystemToDisplay();
1962        _scalarBarActor->GetPosition2Coordinate()->SetValue(width, height);
1963#else
1964        if (width > height) {
1965            // horizontal
1966            _scalarBarActor->SetPosition(.075, .1);
1967            _scalarBarActor->SetHeight(0.8);
1968            _scalarBarActor->SetWidth(0.85);
1969        } else {
1970            // vertical
1971            _scalarBarActor->SetPosition(.1, .05);
1972            _scalarBarActor->SetHeight(0.9);
1973            _scalarBarActor->SetWidth(0.8);
1974        }
1975#endif
1976    }
1977
1978    vtkSmartPointer<vtkLookupTable> lut = colorMap->getLookupTable();
1979    DataSet *dataSet = NULL;
1980    bool cumulative = _useCumulativeRange;
1981    if (dataSetID.compare("all") == 0) {
1982        if (_dataSets.empty()) {
1983            WARN("No DataSets exist, can't fill title or range");
1984        } else {
1985            dataSet = _dataSets.begin()->second;
1986        }
1987        cumulative = true;
1988    } else {
1989        dataSet = getDataSet(dataSetID);
1990        if (dataSet == NULL) {
1991            ERROR("DataSet '%s' not found", dataSetID.c_str());
1992            return false;
1993        }
1994    }
1995
1996    bool drawTitle = false;
1997    if (!title.empty()) {
1998        drawTitle = true;
1999        if (title.compare("#auto") == 0) {
2000            title.clear();
2001        }
2002    }
2003
2004    bool needRange = false;
2005    if (range[0] > range[1]) {
2006        range[0] = 0.0;
2007        range[1] = 1.0;
2008        needRange = true;
2009    }
2010
2011    switch (legendType) {
2012    case LEGEND_VECTOR_MAGNITUDE:
2013        if (needRange) {
2014            if (cumulative) {
2015                getCumulativeDataRange(range, fieldName, type, 3);
2016            } else if (dataSet != NULL) {
2017                dataSet->getDataRange(range, fieldName, type);
2018            }
2019        }
2020
2021        lut->SetRange(range);
2022
2023        if (title.empty() && dataSet != NULL) {
2024            if (fieldName != NULL) {
2025                title = fieldName;
2026                title.append("(mag)");
2027            }
2028        }
2029        break;
2030    case LEGEND_VECTOR_X:
2031        if (needRange) {
2032            if (cumulative) {
2033                getCumulativeDataRange(range, fieldName, type, 3, 0);
2034            } else if (dataSet != NULL) {
2035                dataSet->getDataRange(range, fieldName, type, 0);
2036            }
2037        }
2038
2039        lut->SetRange(range);
2040
2041        if (title.empty() && dataSet != NULL) {
2042            if (fieldName != NULL) {
2043                title = fieldName;
2044                title.append("(x)");
2045            }
2046        }
2047        break;
2048    case LEGEND_VECTOR_Y:
2049        if (needRange) {
2050            if (cumulative) {
2051                getCumulativeDataRange(range, fieldName, type, 3, 1);
2052            } else if (dataSet != NULL) {
2053                dataSet->getDataRange(range, fieldName, type, 1);
2054            }
2055        }
2056
2057        lut->SetRange(range);
2058
2059        if (title.empty() && dataSet != NULL) {
2060            if (fieldName != NULL) {
2061                title = fieldName;
2062                title.append("(y)");
2063            }
2064        }
2065        break;
2066    case LEGEND_VECTOR_Z:
2067        if (needRange) {
2068            if (cumulative) {
2069                getCumulativeDataRange(range, fieldName, type, 3, 2);
2070            } else if (dataSet != NULL) {
2071                dataSet->getDataRange(range, fieldName, type, 1);
2072            }
2073        }
2074
2075        lut->SetRange(range);
2076
2077        if (title.empty() && dataSet != NULL) {
2078            if (fieldName != NULL) {
2079                title = fieldName;
2080                title.append("(z)");
2081            }
2082        }
2083        break;
2084    case LEGEND_SCALAR:
2085    default:
2086        if (needRange) {
2087            if (cumulative) {
2088                getCumulativeDataRange(range, fieldName, type, 1);
2089            } else if (dataSet != NULL) {
2090                dataSet->getDataRange(range, fieldName, type);
2091            }
2092        }
2093
2094        lut->SetRange(range);
2095
2096        if (title.empty() && dataSet != NULL) {
2097            if (fieldName != NULL)
2098                title = fieldName;
2099        }
2100        break;
2101    }
2102
2103    _scalarBarActor->SetLookupTable(lut);
2104    _scalarBarActor->SetMaximumNumberOfColors((width > height ? width : height));
2105
2106    if (drawTitle) {
2107        _scalarBarActor->SetTitle(title.c_str());
2108    } else {
2109        _scalarBarActor->SetTitle("");
2110    }
2111
2112    double color[3];
2113    color[0] = 1 - _bgColor[0];
2114    color[1] = 1 - _bgColor[1];
2115    color[2] = 1 - _bgColor[2];
2116
2117    _scalarBarActor->GetTitleTextProperty()->SetColor(color);
2118    _scalarBarActor->GetTitleTextProperty()->BoldOff();
2119    _scalarBarActor->GetTitleTextProperty()->ItalicOff();
2120    _scalarBarActor->GetTitleTextProperty()->ShadowOff();
2121    _scalarBarActor->SetNumberOfLabels(numLabels);
2122    _scalarBarActor->GetLabelTextProperty()->SetColor(color);
2123    _scalarBarActor->GetLabelTextProperty()->BoldOff();
2124    _scalarBarActor->GetLabelTextProperty()->ItalicOff();
2125    _scalarBarActor->GetLabelTextProperty()->ShadowOff();
2126#ifdef NEW_SCALAR_BAR
2127    if (!drawTitle && numLabels == 0) {
2128        _scalarBarActor->DrawAnnotationsOff();
2129        _scalarBarActor->SetAnnotationLeaderPadding(0);
2130        _scalarBarActor->SetTextPad(0);
2131    } else {
2132        _scalarBarActor->DrawAnnotationsOn();
2133        _scalarBarActor->SetAnnotationLeaderPadding(8);
2134        _scalarBarActor->SetTextPad(1);
2135    }
2136#endif
2137
2138    _legendRenderWindow->Render();
2139
2140#ifdef RENDER_TARGA
2141    _legendRenderWindow->MakeCurrent();
2142    // Must clear previous errors first.
2143    while (glGetError() != GL_NO_ERROR){
2144        ;
2145    }
2146    int bytesPerPixel = TARGA_BYTES_PER_PIXEL;
2147    int size = bytesPerPixel * width * height;
2148
2149    if (imgData->GetMaxId() + 1 != size)
2150    {
2151        imgData->SetNumberOfComponents(bytesPerPixel);
2152        imgData->SetNumberOfValues(size);
2153    }
2154    glDisable(GL_TEXTURE_2D);
2155    if (_legendRenderWindow->GetDoubleBuffer()) {
2156        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_legendRenderWindow)->GetBackLeftBuffer()));
2157    } else {
2158        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_legendRenderWindow)->GetFrontLeftBuffer()));
2159    }
2160    glPixelStorei(GL_PACK_ALIGNMENT, 1);
2161    if (bytesPerPixel == 4) {
2162        glReadPixels(0, 0, width, height, GL_BGRA,
2163                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
2164    } else {
2165        glReadPixels(0, 0, width, height, GL_BGR,
2166                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
2167    }
2168    if (glGetError() != GL_NO_ERROR) {
2169        ERROR("glReadPixels");
2170    }
2171#else
2172    _legendRenderWindow->GetPixelData(0, 0, width-1, height-1,
2173                                      !_legendRenderWindow->GetDoubleBuffer(),
2174                                      imgData);
2175#endif
2176    TRACE("Leave");
2177    return true;
2178}
2179
2180/**
2181 * \brief Set camera FOV based on render window height
2182 *
2183 * Computes a field-of-view angle based on some assumptions about
2184 * viewer's distance to screen and pixel density
2185 */
2186void Renderer::setViewAngle(int height)
2187{
2188    // Distance of eyes from screen in inches
2189    double d = 20.0;
2190    // Assume 72 ppi screen
2191    double h = (double)height / 72.0;
2192
2193    double angle = vtkMath::DegreesFromRadians(2.0 * atan((h/2.0)/d));
2194    _renderer->GetActiveCamera()->SetViewAngle(angle);
2195
2196    TRACE("Setting view angle: %g", angle);
2197}
2198
2199/**
2200 * \brief Resize the render window (image size for renderings)
2201 */
2202void Renderer::setWindowSize(int width, int height)
2203{
2204    if (_windowWidth == width &&
2205        _windowHeight == height) {
2206        TRACE("No change");
2207        return;
2208    }
2209
2210    TRACE("Setting window size to %dx%d", width, height);
2211
2212    //setViewAngle(height);
2213
2214    // FIXME: Fix up panning on aspect change
2215#ifdef notdef
2216    if (_cameraPan[0] != 0.0) {
2217        _cameraPan[0] *= ((double)_windowWidth / width);
2218    }
2219    if (_cameraPan[1] != 0.0) {
2220        _cameraPan[1] *= ((double)_windowHeight / height);
2221    }
2222#endif
2223    _windowWidth = width;
2224    _windowHeight = height;
2225    _renderWindow->SetSize(_windowWidth, _windowHeight);
2226
2227    if (_cameraMode == IMAGE) {
2228        if (_cameraAspect == ASPECT_WINDOW) {
2229            double imgWindowAspect = getImageCameraAspect();
2230            TRACE("Setting object aspect to %g", imgWindowAspect);
2231            setObjectAspects(imgWindowAspect);
2232            initCamera();
2233        } else {
2234            if (_userImgWorldDims[0] > 0) {
2235                _setCameraZoomRegion(_userImgWorldOrigin[0],
2236                                     _userImgWorldOrigin[1],
2237                                     _userImgWorldDims[0],
2238                                     _userImgWorldDims[1]);
2239            } else {
2240                if (isCameraMaximized()) {
2241                    initCamera();
2242                } else {
2243                    _setCameraZoomRegion(_imgWorldOrigin[0],
2244                                         _imgWorldOrigin[1],
2245                                         _imgWorldDims[0],
2246                                         _imgWorldDims[1]);
2247                }
2248            }
2249        }
2250    }
2251    _needsRedraw = true;
2252}
2253
2254void Renderer::setObjectAspects(double aspectRatio)
2255{
2256    setGraphicsObjectAspect<Arc>(aspectRatio);
2257    setGraphicsObjectAspect<Arrow>(aspectRatio);
2258    setGraphicsObjectAspect<Box>(aspectRatio);
2259    setGraphicsObjectAspect<Cone>(aspectRatio);
2260    setGraphicsObjectAspect<Contour2D>(aspectRatio);
2261    setGraphicsObjectAspect<Contour3D>(aspectRatio);
2262    setGraphicsObjectAspect<Cutplane>(aspectRatio);
2263    setGraphicsObjectAspect<Cylinder>(aspectRatio);
2264    setGraphicsObjectAspect<Disk>(aspectRatio);
2265    setGraphicsObjectAspect<Glyphs>(aspectRatio);
2266    setGraphicsObjectAspect<HeightMap>(aspectRatio);
2267    setGraphicsObjectAspect<LIC>(aspectRatio);
2268    setGraphicsObjectAspect<Line>(aspectRatio);
2269    setGraphicsObjectAspect<Molecule>(aspectRatio);
2270    setGraphicsObjectAspect<Outline>(aspectRatio);
2271    setGraphicsObjectAspect<PolyData>(aspectRatio);
2272    setGraphicsObjectAspect<Polygon>(aspectRatio);
2273    setGraphicsObjectAspect<PseudoColor>(aspectRatio);
2274    setGraphicsObjectAspect<Sphere>(aspectRatio);
2275    setGraphicsObjectAspect<Streamlines>(aspectRatio);
2276    setGraphicsObjectAspect<Volume>(aspectRatio);
2277    setGraphicsObjectAspect<Warp>(aspectRatio);
2278}
2279
2280void Renderer::setCameraAspect(Aspect aspect)
2281{
2282    //if (_cameraAspect == aspect)
2283    //    return;
2284
2285    _cameraAspect = aspect;
2286
2287    double aspectRatio = 0.0;
2288    switch (aspect) {
2289    case ASPECT_SQUARE:
2290        aspectRatio = 1.0;
2291        break;
2292    case ASPECT_WINDOW:
2293        aspectRatio = 1.0;
2294        if (_cameraMode == IMAGE) {
2295            aspectRatio = getImageCameraAspect();
2296        }
2297        break;
2298    case ASPECT_NATIVE:
2299    default:
2300        aspectRatio = 0.0;
2301    }
2302
2303    setObjectAspects(aspectRatio);
2304
2305    if (_cameraMode == IMAGE)
2306        _needsCameraReset = true;
2307    _needsRedraw = true;
2308}
2309
2310/**
2311 * \brief Change the camera type: perspective, orthographic or image view
2312 *
2313 * Perspective mode is a normal 3D camera.
2314 *
2315 * Orthogrphic mode is parallel projection.
2316 *
2317 * Image mode is an orthographic camera with fixed axes and a clipping region
2318 * around the plot area, use _setCameraZoomRegion to control the displayed area
2319 *
2320 * \param[in] mode Enum specifying camera type
2321 */
2322void Renderer::setCameraMode(CameraMode mode)
2323{
2324    if (_cameraMode == mode) return;
2325
2326    CameraMode origMode = _cameraMode;
2327    _cameraMode = mode;
2328    resetAxes();
2329
2330    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2331    switch (mode) {
2332    case ORTHO: {
2333        TRACE("Set camera to Ortho mode");
2334        camera->ParallelProjectionOn();
2335        if (origMode == IMAGE) {
2336            resetCamera(true);
2337        }
2338        break;
2339    }
2340    case PERSPECTIVE: {
2341        TRACE("Set camera to Perspective mode");
2342        camera->ParallelProjectionOff();
2343        if (origMode == IMAGE) {
2344            resetCamera(true);
2345        }
2346        break;
2347    }
2348    case IMAGE: {
2349        camera->ParallelProjectionOn();
2350        _setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
2351                             _imgWorldDims[0],_imgWorldDims[1]);
2352        TRACE("Set camera to Image mode");
2353        break;
2354    }
2355    default:
2356        ERROR("Unkown camera mode: %d", mode);
2357    }
2358    _needsRedraw = true;
2359}
2360
2361/**
2362 * \brief Get the current camera mode
2363 */
2364Renderer::CameraMode Renderer::getCameraMode() const
2365{
2366    return _cameraMode;
2367}
2368
2369/**
2370 * \brief Set the VTK camera parameters based on a 4x4 view matrix
2371 */
2372void Renderer::setCameraFromMatrix(vtkCamera *camera, vtkMatrix4x4& mat)
2373{
2374    double d = camera->GetDistance();
2375    double vu[3];
2376    vu[0] = mat[1][0];
2377    vu[1] = mat[1][1];
2378    vu[2] = mat[1][2];
2379    double trans[3];
2380    trans[0] = mat[0][3];
2381    trans[1] = mat[1][3];
2382    trans[2] = mat[2][3];
2383    mat[0][3] = 0;
2384    mat[1][3] = 0;
2385    mat[2][3] = 0;
2386    double vpn[3] = {mat[2][0], mat[2][1], mat[2][2]};
2387    double pos[3];
2388    // With translation removed, we have an orthogonal matrix,
2389    // so the inverse is the transpose
2390    mat.Transpose();
2391    mat.MultiplyPoint(trans, pos);
2392    pos[0] = -pos[0];
2393    pos[1] = -pos[1];
2394    pos[2] = -pos[2];
2395    double fp[3];
2396    fp[0] = pos[0] - vpn[0] * d;
2397    fp[1] = pos[1] - vpn[1] * d;
2398    fp[2] = pos[2] - vpn[2] * d;
2399    camera->SetPosition(pos);
2400    camera->SetFocalPoint(fp);
2401    camera->SetViewUp(vu);
2402}
2403
2404/**
2405 * \brief Set the orientation of the camera from a quaternion
2406 * rotation
2407 *
2408 * \param[in] quat A quaternion with scalar part first: w,x,y,z
2409 * \param[in] absolute Is rotation absolute or relative?
2410 */
2411void Renderer::setCameraOrientation(const double quat[4], bool absolute)
2412{
2413    if (_cameraMode == IMAGE)
2414        return;
2415    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2416    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
2417    vtkSmartPointer<vtkMatrix4x4> rotMat = vtkSmartPointer<vtkMatrix4x4>::New();
2418
2419    double q[4];
2420    copyQuat(quat, q);
2421
2422    if (absolute) {
2423        double abs[4];
2424        // Save absolute rotation
2425        copyQuat(q, abs);
2426        // Compute relative rotation
2427        quatMult(quatReciprocal(_cameraOrientation), q, q);
2428        // Store absolute rotation
2429        copyQuat(abs, _cameraOrientation);
2430    } else {
2431        // Compute new absolute rotation
2432        quatMult(_cameraOrientation, q, _cameraOrientation);
2433    }
2434
2435    quaternionToTransposeMatrix4x4(q, *rotMat);
2436#ifdef DEBUG
2437    TRACE("Rotation matrix:\n %g %g %g\n %g %g %g\n %g %g %g",
2438          (*rotMat)[0][0], (*rotMat)[0][1], (*rotMat)[0][2],
2439          (*rotMat)[1][0], (*rotMat)[1][1], (*rotMat)[1][2],
2440          (*rotMat)[2][0], (*rotMat)[2][1], (*rotMat)[2][2]);
2441    vtkSmartPointer<vtkMatrix4x4> camMat = vtkSmartPointer<vtkMatrix4x4>::New();
2442    camMat->DeepCopy(camera->GetViewTransformMatrix());
2443    TRACE("Camera matrix:\n %g %g %g %g\n %g %g %g %g\n %g %g %g %g\n %g %g %g %g",
2444          (*camMat)[0][0], (*camMat)[0][1], (*camMat)[0][2], (*camMat)[0][3],
2445          (*camMat)[1][0], (*camMat)[1][1], (*camMat)[1][2], (*camMat)[1][3],
2446          (*camMat)[2][0], (*camMat)[2][1], (*camMat)[2][2], (*camMat)[2][3],
2447          (*camMat)[3][0], (*camMat)[3][1], (*camMat)[3][2], (*camMat)[3][3]);
2448    printCameraInfo(camera);
2449#endif
2450    trans->Translate(0, 0, -camera->GetDistance());
2451    trans->Concatenate(rotMat);
2452    trans->Translate(0, 0, camera->GetDistance());
2453    trans->Concatenate(camera->GetViewTransformMatrix());
2454    setCameraFromMatrix(camera, *trans->GetMatrix());
2455
2456    resetCameraClippingRange();
2457    printCameraInfo(camera);
2458    _needsRedraw = true;
2459}
2460
2461/**
2462 * \brief Set the position and orientation of the camera
2463 *
2464 * \param[in] position x,y,z position of camera in world coordinates
2465 * \param[in] focalPoint x,y,z look-at point in world coordinates
2466 * \param[in] viewUp x,y,z up vector of camera
2467 */
2468void Renderer::setCameraOrientationAndPosition(const double position[3],
2469                                               const double focalPoint[3],
2470                                               const double viewUp[3])
2471{
2472    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2473    camera->SetPosition(position);
2474    camera->SetFocalPoint(focalPoint);
2475    camera->SetViewUp(viewUp);
2476    resetCameraClippingRange();
2477    _needsRedraw = true;
2478}
2479
2480/**
2481 * \brief Get the position and orientation of the camera
2482 *
2483 * \param[out] position x,y,z position of camera in world coordinates
2484 * \param[out] focalPoint x,y,z look-at point in world coordinates
2485 * \param[out] viewUp x,y,z up vector of camera
2486 */
2487void Renderer::getCameraOrientationAndPosition(double position[3],
2488                                               double focalPoint[3],
2489                                               double viewUp[3])
2490{
2491    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2492    camera->GetPosition(position);
2493    camera->GetFocalPoint(focalPoint);
2494    camera->GetViewUp(viewUp);
2495}
2496
2497void Renderer::sceneBoundsChanged()
2498{
2499#ifdef RESET_CAMERA_ON_SCENE_CHANGE
2500    _needsCameraReset = true;
2501#else
2502    _needsAxesReset = true;
2503    _needsCameraClippingRangeReset = true;
2504#endif
2505
2506    _needsRedraw = true;
2507}
2508
2509/**
2510 * \brief Reset pan, zoom, clipping planes and optionally rotation
2511 *
2512 * \param[in] resetOrientation Reset the camera rotation/orientation also
2513 */
2514void Renderer::resetCamera(bool resetOrientation)
2515{
2516    TRACE("Enter: %d", resetOrientation ? 1 : 0);
2517    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2518    if (_cameraMode == IMAGE) {
2519        initCamera();
2520    } else {
2521        if (resetOrientation) {
2522            camera->SetPosition(0, 0, 1);
2523            camera->SetFocalPoint(0, 0, 0);
2524            camera->SetViewUp(0, 1, 0);
2525            _cameraOrientation[0] = 1.0;
2526            _cameraOrientation[1] = 0.0;
2527            _cameraOrientation[2] = 0.0;
2528            _cameraOrientation[3] = 0.0;
2529        }
2530        //setViewAngle(_windowHeight);
2531        //double bounds[6];
2532        //collectBounds(bounds, false);
2533        //_renderer->ResetCamera(bounds);
2534        if (_needsAxesReset) {
2535            resetAxes();
2536            _needsAxesReset = false;
2537        }
2538        resetVtkCamera();
2539        //computeScreenWorldCoords();
2540    }
2541
2542    printCameraInfo(camera);
2543
2544    _cameraZoomRatio = 1;
2545    _cameraPan[0] = 0;
2546    _cameraPan[1] = 0;
2547
2548    _needsRedraw = true;
2549}
2550
2551void Renderer::resetVtkCamera(double *bounds)
2552{
2553    TRACE("Enter: bounds: %p", bounds);
2554    if (bounds != NULL)
2555        _renderer->ResetCamera(bounds);
2556    else
2557        _renderer->ResetCamera();
2558    printCameraInfo(_renderer->GetActiveCamera());
2559}
2560
2561/**
2562 * \brief Set the camera near/far clipping range based on current scene bounds
2563 */
2564void Renderer::resetCameraClippingRange()
2565{
2566    _renderer->ResetCameraClippingRange();
2567    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2568    //double dist = camera->GetClippingRange()[0] + (camera->GetClippingRange()[1] - camera->GetClippingRange()[0])/2.0;
2569    //camera->SetDistance(dist);
2570    printCameraInfo(camera);
2571}
2572
2573/**
2574 * \brief Perform a relative rotation to current camera orientation
2575 *
2576 * Angles are in degrees, rotation is about focal point
2577 */
2578void Renderer::rotateCamera(double yaw, double pitch, double roll)
2579{
2580    if (_cameraMode == IMAGE)
2581        return;
2582    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2583    camera->Azimuth(yaw); // Rotate about object
2584    //camera->SetYaw(yaw); // Rotate about camera
2585    camera->Elevation(pitch); // Rotate about object
2586    //camera->SetPitch(pitch); // Rotate about camera
2587    camera->Roll(roll); // Roll about camera view axis
2588    resetCameraClippingRange();
2589    //computeScreenWorldCoords();
2590    _needsRedraw = true;
2591}
2592
2593/**
2594 * \brief Perform a 2D translation of the camera
2595 *
2596 * x,y pan amount are specified as signed absolute pan amount in viewport
2597 * units -- i.e. 0 is no pan, .5 is half the viewport, 2 is twice the viewport,
2598 * etc.
2599 *
2600 * \param[in] x Viewport coordinate horizontal panning (positive number pans
2601 * camera left, object right)
2602 * \param[in] y Viewport coordinate vertical panning (positive number pans
2603 * camera up, object down)
2604 * \param[in] absolute Control if pan amount is relative to current or absolute
2605 */
2606void Renderer::panCamera(double x, double y, bool absolute)
2607{
2608    TRACE("Enter panCamera: %g %g, current abs: %g %g",
2609          x, y, _cameraPan[0], _cameraPan[1]);
2610
2611    if (_cameraMode == IMAGE) {
2612        _userImgWorldOrigin[0] = 0;
2613        _userImgWorldOrigin[1] = 0;
2614        _userImgWorldDims[0] = -1;
2615        _userImgWorldDims[1] = -1;
2616
2617        // Reverse x rather than y, since we are panning the camera, and client
2618        // expects to be panning/moving the object
2619        x = -x * _screenWorldCoords[2];
2620        y = y * _screenWorldCoords[3];
2621
2622        if (absolute) {
2623            double panAbs[2];
2624            panAbs[0] = x;
2625            panAbs[1] = y;
2626            x -= _cameraPan[0];
2627            y -= _cameraPan[1];
2628            _cameraPan[0] = panAbs[0];
2629            _cameraPan[1] = panAbs[1];
2630        } else {
2631            _cameraPan[0] += x;
2632            _cameraPan[1] += y;
2633        }
2634
2635        _imgWorldOrigin[0] += x;
2636        _imgWorldOrigin[1] += y;
2637        _setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
2638                             _imgWorldDims[0], _imgWorldDims[1]);
2639    } else {
2640        y = -y;
2641        if (absolute) {
2642            double panAbs[2];
2643            panAbs[0] = x;
2644            panAbs[1] = y;
2645            x -= _cameraPan[0];
2646            y -= _cameraPan[1];
2647            _cameraPan[0] = panAbs[0];
2648            _cameraPan[1] = panAbs[1];
2649        } else {
2650            _cameraPan[0] += x;
2651            _cameraPan[1] += y;
2652        }
2653
2654        if (x != 0.0 || y != 0.0) {
2655            vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2656            double viewFocus[4], focalDepth, viewPoint[3];
2657            double newPickPoint[4], oldPickPoint[4], motionVector[3];
2658
2659            camera->GetFocalPoint(viewFocus);
2660            computeWorldToDisplay(viewFocus[0], viewFocus[1], viewFocus[2],
2661                                  viewFocus);
2662            focalDepth = viewFocus[2];
2663
2664            computeDisplayToWorld((x * 2. + 1.) * (double)_windowWidth / 2.0,
2665                                  (y * 2. + 1.) * (double)_windowHeight / 2.0,
2666                                  focalDepth,
2667                                  newPickPoint);
2668
2669            computeDisplayToWorld((double)_windowWidth / 2.0,
2670                                  (double)_windowHeight / 2.0,
2671                                  focalDepth,
2672                                  oldPickPoint);
2673
2674            // Camera motion is reversed
2675            motionVector[0] = oldPickPoint[0] - newPickPoint[0];
2676            motionVector[1] = oldPickPoint[1] - newPickPoint[1];
2677            motionVector[2] = oldPickPoint[2] - newPickPoint[2];
2678
2679            camera->GetFocalPoint(viewFocus);
2680            camera->GetPosition(viewPoint);
2681            camera->SetFocalPoint(motionVector[0] + viewFocus[0],
2682                                  motionVector[1] + viewFocus[1],
2683                                  motionVector[2] + viewFocus[2]);
2684
2685            camera->SetPosition(motionVector[0] + viewPoint[0],
2686                                motionVector[1] + viewPoint[1],
2687                                motionVector[2] + viewPoint[2]);
2688
2689            resetCameraClippingRange();
2690            //computeScreenWorldCoords();
2691        }
2692    }
2693
2694    TRACE("Leave panCamera: %g %g, current abs: %g %g",
2695          x, y, _cameraPan[0], _cameraPan[1]);
2696
2697    _needsRedraw = true;
2698}
2699
2700/**
2701 * \brief Dolly camera or set orthographic scaling based on camera type
2702 *
2703 * \param[in] z Ratio to change zoom (greater than 1 is zoom in, less than 1 is zoom out)
2704 * \param[in] absolute Control if zoom factor is relative to current setting or absolute
2705 */
2706void Renderer::zoomCamera(double z, bool absolute)
2707{
2708    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2709    TRACE("Enter Zoom: current abs: %g, z: %g, view angle %g",
2710          _cameraZoomRatio, z, camera->GetViewAngle());
2711
2712    if (absolute) {
2713        assert(_cameraZoomRatio > 0.0);
2714        double zAbs = z;
2715        z *= 1.0/_cameraZoomRatio;
2716        _cameraZoomRatio = zAbs;
2717    } else {
2718        _cameraZoomRatio *= z;
2719    }
2720
2721    if (_cameraMode == IMAGE) {
2722        _userImgWorldOrigin[0] = 0;
2723        _userImgWorldOrigin[1] = 0;
2724        _userImgWorldDims[0] = -1;
2725        _userImgWorldDims[1] = -1;
2726
2727        double dx = _imgWorldDims[0];
2728        double dy = _imgWorldDims[1];
2729        _imgWorldDims[0] /= z;
2730        _imgWorldDims[1] /= z;
2731        dx -= _imgWorldDims[0];
2732        dy -= _imgWorldDims[1];
2733        _imgWorldOrigin[0] += dx/2.0;
2734        _imgWorldOrigin[1] += dy/2.0;
2735        _setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
2736                             _imgWorldDims[0], _imgWorldDims[1]);
2737    } else {
2738        // Keep ortho and perspective modes in sync
2739        // Move camera forward/back for perspective camera
2740        camera->Dolly(z);
2741        // Change ortho parallel scale
2742        camera->SetParallelScale(camera->GetParallelScale()/z);
2743        resetCameraClippingRange();
2744        //computeScreenWorldCoords();
2745    }
2746
2747    TRACE("Leave Zoom: rel %g, new abs: %g, view angle %g",
2748          z, _cameraZoomRatio, camera->GetViewAngle());
2749
2750    _needsRedraw = true;
2751}
2752
2753bool Renderer::setCameraZoomRegionPixels(int x, int y, int width, int height)
2754{
2755    if (_cameraMode != IMAGE)
2756        return false;
2757
2758    double wx, wy, ww, wh;
2759
2760    y = _windowHeight - y;
2761    double pxToWorldX = _screenWorldCoords[2] / (double)_windowWidth;
2762    double pxToWorldY = _screenWorldCoords[3] / (double)_windowHeight;
2763
2764    wx = _screenWorldCoords[0] + x * pxToWorldX;
2765    wy = _screenWorldCoords[1] + y * pxToWorldY;
2766    ww = abs(width) *  pxToWorldX;
2767    wh = abs(height) * pxToWorldY;
2768    setCameraZoomRegion(wx, wy, ww, wh);
2769
2770    TRACE("\npx: %d %d %d %d\nworld: %g %g %g %g",
2771          x, y, width, height,
2772          wx, wy, ww, wh);
2773
2774    return true;
2775}
2776
2777bool Renderer::setCameraZoomRegion(double x, double y, double width, double height)
2778{
2779    if (_cameraMode != IMAGE)
2780        return false;
2781
2782    _userImgWorldOrigin[0] = x;
2783    _userImgWorldOrigin[1] = y;
2784    _userImgWorldDims[0] = width;
2785    _userImgWorldDims[1] = height;
2786    _setCameraZoomRegion(x, y, width, height);
2787
2788    return true;
2789}
2790
2791/**
2792 * \brief Set the pan/zoom using a corner and dimensions in pixel coordinates
2793 *
2794 * \param[in] x left pixel coordinate
2795 * \param[in] y bottom  pixel coordinate (with y=0 at top of window)
2796 * \param[in] width Width of zoom region in pixel coordinates
2797 * \param[in] height Height of zoom region in pixel coordinates
2798 */
2799void Renderer::_setCameraZoomRegionPixels(int x, int y, int width, int height)
2800{
2801    if (_cameraMode != IMAGE) {
2802        ERROR("Called while camera mode is not image");
2803        return;
2804    }
2805
2806    double wx, wy, ww, wh;
2807
2808    y = _windowHeight - y;
2809    double pxToWorldX = _screenWorldCoords[2] / (double)_windowWidth;
2810    double pxToWorldY = _screenWorldCoords[3] / (double)_windowHeight;
2811
2812    wx = _screenWorldCoords[0] + x * pxToWorldX;
2813    wy = _screenWorldCoords[1] + y * pxToWorldY;
2814    ww = abs(width) *  pxToWorldX;
2815    wh = abs(height) * pxToWorldY;
2816    _setCameraZoomRegion(wx, wy, ww, wh);
2817
2818    TRACE("\npx: %d %d %d %d\nworld: %g %g %g %g",
2819          x, y, width, height,
2820          wx, wy, ww, wh);
2821}
2822
2823void Renderer::getImageCameraSizes(int *imgWidthPx, int *imgHeightPx,
2824                                   int *_pxOffsetX, int *_pxOffsetY)
2825{
2826    int pxOffsetX, pxOffsetY;
2827    pxOffsetX = (int)(0.17 * (double)_windowWidth);
2828    pxOffsetX = (pxOffsetX > 100 ? 100 : pxOffsetX);
2829    pxOffsetY = (int)(0.15 * (double)_windowHeight);
2830    pxOffsetY = (pxOffsetY > 75 ? 75 : pxOffsetY);
2831
2832    int outerGutter = (int)(0.03 * (double)_windowWidth);
2833    outerGutter = (outerGutter > 15 ? 15 : outerGutter);
2834
2835    *imgWidthPx = _windowWidth - pxOffsetX - outerGutter;
2836    *imgHeightPx = _windowHeight - pxOffsetY - outerGutter;
2837    if (_pxOffsetX != NULL) *_pxOffsetX = pxOffsetX;
2838    if (_pxOffsetY != NULL) *_pxOffsetY = pxOffsetY;
2839}
2840
2841double Renderer::getImageCameraAspect()
2842{
2843    int imgWidthPx, imgHeightPx;
2844    getImageCameraSizes(&imgWidthPx, &imgHeightPx);
2845    return ((double)imgWidthPx / (double)imgHeightPx);
2846}
2847
2848/**
2849 * \brief Set the pan/zoom using a corner and dimensions in world coordinates
2850 *
2851 * \param[in] x left world coordinate
2852 * \param[in] y bottom  world coordinate
2853 * \param[in] width Width of zoom region in world coordinates
2854 * \param[in] height Height of zoom region in world coordinates
2855 */
2856void Renderer::_setCameraZoomRegion(double x, double y, double width, double height)
2857{
2858    if (_cameraMode != IMAGE) {
2859        ERROR("Called while camera mode is not image");
2860        return;
2861    }
2862
2863    int imgWidthPx, imgHeightPx;
2864    int pxOffsetX, pxOffsetY;
2865
2866    getImageCameraSizes(&imgWidthPx, &imgHeightPx, &pxOffsetX, &pxOffsetY);
2867
2868    double imgWindowAspect = (double)imgWidthPx / (double)imgHeightPx;
2869
2870    double pxToWorld;
2871    double imgWidthWorld;
2872    double imgHeightWorld;
2873
2874    double requestedAspect = width / height;
2875
2876    if (requestedAspect >= imgWindowAspect) {
2877        pxToWorld = width / (double)imgWidthPx;
2878        imgWidthWorld = width;
2879        imgHeightWorld = (double)imgHeightPx * pxToWorld;
2880    } else {
2881        pxToWorld = height / (double)imgHeightPx;
2882        imgWidthWorld = (double)imgWidthPx * pxToWorld;
2883        imgHeightWorld =  height;
2884    }
2885
2886    double offsetX = pxOffsetX * pxToWorld;
2887    double offsetY = pxOffsetY * pxToWorld;
2888    double winWidthWorld = _windowWidth * pxToWorld;
2889    double winHeightWorld = _windowHeight * pxToWorld;
2890
2891    TRACE("Window: %d %d", _windowWidth, _windowHeight);
2892    TRACE("ZoomRegion: %g %g %g %g", x, y, width, height);
2893    TRACE("pxToWorld: %g", pxToWorld);
2894    TRACE("offset: %g %g", offsetX, offsetY);
2895
2896    _imgWorldOrigin[0] = x;
2897    _imgWorldOrigin[1] = y;
2898    _imgWorldDims[0] = width;
2899    _imgWorldDims[1] = height;
2900    _imgWindowWorldDims[0] = imgWidthWorld;
2901    _imgWindowWorldDims[1] = imgHeightWorld;
2902
2903    double camPos[2];
2904    camPos[0] = _imgWorldOrigin[0] - offsetX + winWidthWorld / 2.0;
2905    camPos[1] = _imgWorldOrigin[1] - offsetY + winHeightWorld / 2.0;
2906
2907    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2908    camera->ParallelProjectionOn();
2909    camera->SetClippingRange(1, 2);
2910    // Half of world coordinate height of viewport (Documentation is wrong)
2911    camera->SetParallelScale(winHeightWorld / 2.0);
2912
2913    if (_imgCameraPlane == PLANE_XY) {
2914        // XY plane
2915        camera->SetPosition(camPos[0], camPos[1], _imgCameraOffset + 1.);
2916        camera->SetFocalPoint(camPos[0], camPos[1], _imgCameraOffset);
2917        camera->SetViewUp(0, 1, 0);
2918        // bottom
2919        _cameraClipPlanes[0]->SetOrigin(0, _imgWorldOrigin[1], 0);
2920        _cameraClipPlanes[0]->SetNormal(0, 1, 0);
2921        // left
2922        _cameraClipPlanes[1]->SetOrigin(_imgWorldOrigin[0], 0, 0);
2923        _cameraClipPlanes[1]->SetNormal(1, 0, 0);
2924        // top
2925        _cameraClipPlanes[2]->SetOrigin(0, _imgWorldOrigin[1] + _imgWindowWorldDims[1], 0);
2926        _cameraClipPlanes[2]->SetNormal(0, -1, 0);
2927        // right
2928        _cameraClipPlanes[3]->SetOrigin(_imgWorldOrigin[0] + _imgWindowWorldDims[0], 0, 0);
2929        _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
2930
2931        _cubeAxesActor->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWindowWorldDims[0],
2932                                  _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWindowWorldDims[1],
2933                                  _imgCameraOffset, _imgCameraOffset);
2934        _cubeAxesActor->XAxisVisibilityOn();
2935        _cubeAxesActor->YAxisVisibilityOn();
2936        _cubeAxesActor->ZAxisVisibilityOff();
2937    } else if (_imgCameraPlane == PLANE_ZY) {
2938        // ZY plane
2939        camera->SetPosition(_imgCameraOffset - 1., camPos[1], camPos[0]);
2940        camera->SetFocalPoint(_imgCameraOffset, camPos[1], camPos[0]);
2941        camera->SetViewUp(0, 1, 0);
2942        // bottom
2943        _cameraClipPlanes[0]->SetOrigin(0, _imgWorldOrigin[1], 0);
2944        _cameraClipPlanes[0]->SetNormal(0, 1, 0);
2945        // left
2946        _cameraClipPlanes[1]->SetOrigin(0, 0, _imgWorldOrigin[0]);
2947        _cameraClipPlanes[1]->SetNormal(0, 0, 1);
2948        // top
2949        _cameraClipPlanes[2]->SetOrigin(0, _imgWorldOrigin[1] + _imgWindowWorldDims[1], 0);
2950        _cameraClipPlanes[2]->SetNormal(0, -1, 0);
2951        // right
2952        _cameraClipPlanes[3]->SetOrigin(0, 0, _imgWorldOrigin[0] + _imgWindowWorldDims[0]);
2953        _cameraClipPlanes[3]->SetNormal(0, 0, -1);
2954
2955        _cubeAxesActor->SetBounds(_imgCameraOffset, _imgCameraOffset,
2956                                  _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWindowWorldDims[1],
2957                                  _imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWindowWorldDims[0]);
2958        _cubeAxesActor->XAxisVisibilityOff();
2959        _cubeAxesActor->YAxisVisibilityOn();
2960        _cubeAxesActor->ZAxisVisibilityOn();
2961    } else {
2962        // XZ plane
2963        camera->SetPosition(camPos[0], _imgCameraOffset - 1., camPos[1]);
2964        camera->SetFocalPoint(camPos[0], _imgCameraOffset, camPos[1]);
2965        camera->SetViewUp(0, 0, 1);
2966        // bottom
2967        _cameraClipPlanes[0]->SetOrigin(0, 0, _imgWorldOrigin[1]);
2968        _cameraClipPlanes[0]->SetNormal(0, 0, 1);
2969        // left
2970        _cameraClipPlanes[1]->SetOrigin(_imgWorldOrigin[0], 0, 0);
2971        _cameraClipPlanes[1]->SetNormal(1, 0, 0);
2972        // top
2973        _cameraClipPlanes[2]->SetOrigin(0, 0, _imgWorldOrigin[1] + _imgWorldDims[1]);
2974        _cameraClipPlanes[2]->SetNormal(0, 0, -1);
2975        // right
2976        _cameraClipPlanes[3]->SetOrigin(_imgWorldOrigin[0] + _imgWorldDims[0], 0, 0);
2977        _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
2978
2979        _cubeAxesActor->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWindowWorldDims[0],
2980                                  _imgCameraOffset, _imgCameraOffset,
2981                                  _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWindowWorldDims[1]);
2982        _cubeAxesActor->XAxisVisibilityOn();
2983        _cubeAxesActor->YAxisVisibilityOff();
2984        _cubeAxesActor->ZAxisVisibilityOn();
2985    }
2986
2987    // Fix up axis ranges based on new bounds
2988    setAxesRanges();
2989
2990    // Compute screen world coordinates
2991    computeScreenWorldCoords();
2992
2993#ifdef DEBUG
2994    printCameraInfo(camera);
2995#endif
2996
2997    _needsRedraw = true;
2998}
2999
3000/**
3001 * \brief Convert pixel/display coordinates to world coordinates based on current camera
3002 */
3003void Renderer::computeDisplayToWorld(double x, double y, double z, double worldPt[4])
3004{
3005    _renderer->SetDisplayPoint(x, y, z);
3006    _renderer->DisplayToWorld();
3007    _renderer->GetWorldPoint(worldPt);
3008    if (worldPt[3]) {
3009        worldPt[0] /= worldPt[3];
3010        worldPt[1] /= worldPt[3];
3011        worldPt[2] /= worldPt[3];
3012        worldPt[3] = 1.0;
3013    }
3014}
3015
3016/**
3017 * \brief Convert world coordinates to pixel/display coordinates based on current camera
3018 */
3019void Renderer::computeWorldToDisplay(double x, double y, double z, double displayPt[3])
3020{
3021    _renderer->SetWorldPoint(x, y, z, 1.0);
3022    _renderer->WorldToDisplay();
3023    _renderer->GetDisplayPoint(displayPt);
3024}
3025
3026/**
3027 * \brief Compute the world coordinate bounds of the display rectangle
3028 */
3029void Renderer::computeScreenWorldCoords()
3030{
3031    // Start with viewport coords [-1,1]
3032    double x0 = -1;
3033    double y0 = -1;
3034    double z0 = -1;
3035    double x1 = 1;
3036    double y1 = 1;
3037    double z1 = -1;
3038
3039    vtkMatrix4x4 *mat = vtkMatrix4x4::New();
3040    double result[4];
3041
3042    // get the perspective transformation from the active camera
3043    mat->DeepCopy(_renderer->GetActiveCamera()->
3044                  GetCompositeProjectionTransformMatrix(_renderer->GetTiledAspectRatio(),0,1));
3045
3046    // use the inverse matrix
3047    mat->Invert();
3048
3049    // Transform point to world coordinates
3050    result[0] = x0;
3051    result[1] = y0;
3052    result[2] = z0;
3053    result[3] = 1.0;
3054
3055    mat->MultiplyPoint(result, result);
3056
3057    // Get the transformed vector & set WorldPoint
3058    // while we are at it try to keep w at one
3059    if (result[3]) {
3060        x0 = result[0] / result[3];
3061        y0 = result[1] / result[3];
3062        z0 = result[2] / result[3];
3063    }
3064
3065    result[0] = x1;
3066    result[1] = y1;
3067    result[2] = z1;
3068    result[3] = 1.0;
3069
3070    mat->MultiplyPoint(result, result);
3071
3072    if (result[3]) {
3073        x1 = result[0] / result[3];
3074        y1 = result[1] / result[3];
3075        z1 = result[2] / result[3];
3076    }
3077
3078    mat->Delete();
3079
3080    if (_imgCameraPlane == PLANE_XZ) {
3081        _screenWorldCoords[0] = x0;
3082        _screenWorldCoords[1] = z0;
3083        _screenWorldCoords[2] = x1 - x0;
3084        _screenWorldCoords[3] = z1 - z0;
3085    } else if (_imgCameraPlane == PLANE_ZY) {
3086        _screenWorldCoords[0] = z0;
3087        _screenWorldCoords[1] = y0;
3088        _screenWorldCoords[2] = z1 - z0;
3089        _screenWorldCoords[3] = y1 - y0;
3090    } else {
3091        // XY
3092        _screenWorldCoords[0] = x0;
3093        _screenWorldCoords[1] = y0;
3094        _screenWorldCoords[2] = x1 - x0;
3095        _screenWorldCoords[3] = y1 - y0;
3096    }
3097}
3098
3099/**
3100 * \brief Get the world coordinates of the image camera plot area
3101 *
3102 * \param[out] xywh Array to hold x,y,width,height world coordinates
3103 */
3104void Renderer::getCameraZoomRegion(double xywh[4]) const
3105{
3106    xywh[0] = _imgWorldOrigin[0];
3107    xywh[1] = _imgWorldOrigin[1];
3108    xywh[2] = _imgWorldDims[0];
3109    xywh[3] = _imgWorldDims[1];
3110}
3111
3112/**
3113 * \brief Get the world origin and dimensions of the screen
3114 *
3115 * \param[out] xywh Array to hold x,y,width,height world coordinates
3116 */
3117void Renderer::getScreenWorldCoords(double xywh[4]) const
3118{
3119    memcpy(xywh, _screenWorldCoords, sizeof(double)*4);
3120}
3121
3122/**
3123 * \brief Compute bounding box containing the two input bounding boxes
3124 *
3125 * \param[out] boundsDest Union of the two input bounding boxes
3126 * \param[in] bounds1 Input bounding box
3127 * \param[in] bounds2 Input bounding box
3128 */
3129void Renderer::mergeBounds(double *boundsDest,
3130                           const double *bounds1, const double *bounds2)
3131{
3132    assert(boundsDest != NULL);
3133    assert(bounds1 != NULL);
3134    if (bounds2 == NULL) {
3135        WARN("NULL bounds2 array");
3136        return;
3137    }
3138    bool b1empty = (bounds1[0] > bounds1[1]);
3139    bool b2empty = (bounds2[0] > bounds2[1]);
3140
3141    if (b1empty && b2empty)
3142        return;
3143    if (b1empty) {
3144        memcpy(boundsDest, bounds2, sizeof(double) * 6);
3145        return;
3146    } else if (b2empty) {
3147        memcpy(boundsDest, bounds1, sizeof(double) * 6);
3148        return;
3149    }
3150
3151    for (int i = 0; i < 6; i++) {
3152        if (i % 2 == 0)
3153            boundsDest[i] = min2(bounds1[i], bounds2[i]);
3154        else
3155            boundsDest[i] = max2(bounds1[i], bounds2[i]);
3156    }
3157}
3158
3159/**
3160 * \brief Collect bounds of all graphics objects
3161 *
3162 * \param[out] bounds Bounds of all scene objects
3163 * \param[in] onlyVisible Only collect bounds of visible objects
3164 */
3165void Renderer::collectBounds(double *bounds, bool onlyVisible)
3166{
3167    bounds[0] = DBL_MAX;
3168    bounds[1] = -DBL_MAX;
3169    bounds[2] = DBL_MAX;
3170    bounds[3] = -DBL_MAX;
3171    bounds[4] = DBL_MAX;
3172    bounds[5] = -DBL_MAX;
3173
3174    mergeGraphicsObjectBounds<Arc>(bounds, onlyVisible);
3175    mergeGraphicsObjectBounds<Arrow>(bounds, onlyVisible);
3176    mergeGraphicsObjectBounds<Box>(bounds, onlyVisible);
3177    mergeGraphicsObjectBounds<Cone>(bounds, onlyVisible);
3178    mergeGraphicsObjectBounds<Contour2D>(bounds, onlyVisible);
3179    mergeGraphicsObjectBounds<Contour3D>(bounds, onlyVisible);
3180    mergeGraphicsObjectBounds<Cutplane>(bounds, onlyVisible);
3181    mergeGraphicsObjectBounds<Cylinder>(bounds, onlyVisible);
3182    mergeGraphicsObjectBounds<Disk>(bounds, onlyVisible);
3183    mergeGraphicsObjectBounds<Glyphs>(bounds, onlyVisible);
3184    mergeGraphicsObjectBounds<Group>(bounds, onlyVisible);
3185    mergeGraphicsObjectBounds<HeightMap>(bounds, onlyVisible);
3186    mergeGraphicsObjectBounds<LIC>(bounds, onlyVisible);
3187    mergeGraphicsObjectBounds<Line>(bounds, onlyVisible);
3188    mergeGraphicsObjectBounds<Molecule>(bounds, onlyVisible);
3189    mergeGraphicsObjectBounds<Outline>(bounds, onlyVisible);
3190    mergeGraphicsObjectBounds<PolyData>(bounds, onlyVisible);
3191    mergeGraphicsObjectBounds<Polygon>(bounds, onlyVisible);
3192    mergeGraphicsObjectBounds<PseudoColor>(bounds, onlyVisible);
3193    mergeGraphicsObjectBounds<Sphere>(bounds, onlyVisible);
3194    mergeGraphicsObjectBounds<Streamlines>(bounds, onlyVisible);
3195    mergeGraphicsObjectBounds<Volume>(bounds, onlyVisible);
3196    mergeGraphicsObjectBounds<Warp>(bounds, onlyVisible);
3197
3198    for (int i = 0; i < 6; i += 2) {
3199        if (bounds[i+1] < bounds[i]) {
3200            bounds[i] = -0.5;
3201            bounds[i+1] = 0.5;
3202        }
3203    }
3204
3205    int numDims = 0;
3206    if (bounds[0] != bounds[1])
3207        numDims++;
3208    if (bounds[2] != bounds[3])
3209        numDims++;
3210    if (bounds[4] != bounds[5])
3211        numDims++;
3212
3213    if (numDims == 0) {
3214        bounds[0] -= .5;
3215        bounds[1] += .5;
3216        bounds[2] -= .5;
3217        bounds[3] += .5;
3218    }
3219
3220    TRACE("Bounds: %g %g %g %g %g %g",
3221          bounds[0],
3222          bounds[1],
3223          bounds[2],
3224          bounds[3],
3225          bounds[4],
3226          bounds[5]);
3227}
3228
3229/**
3230 * \brief Collect bounds of all graphics objects
3231 *
3232 * \param[out] bounds Bounds of all scene objects
3233 * \param[in] onlyVisible Only collect bounds of visible objects
3234 */
3235void Renderer::collectUnscaledBounds(double *bounds, bool onlyVisible)
3236{
3237    bounds[0] = DBL_MAX;
3238    bounds[1] = -DBL_MAX;
3239    bounds[2] = DBL_MAX;
3240    bounds[3] = -DBL_MAX;
3241    bounds[4] = DBL_MAX;
3242    bounds[5] = -DBL_MAX;
3243
3244    mergeGraphicsObjectUnscaledBounds<Arc>(bounds, onlyVisible);
3245    mergeGraphicsObjectUnscaledBounds<Arrow>(bounds, onlyVisible);
3246    mergeGraphicsObjectUnscaledBounds<Box>(bounds, onlyVisible);
3247    mergeGraphicsObjectUnscaledBounds<Cone>(bounds, onlyVisible);
3248    mergeGraphicsObjectUnscaledBounds<Contour2D>(bounds, onlyVisible);
3249    mergeGraphicsObjectUnscaledBounds<Contour3D>(bounds, onlyVisible);
3250    mergeGraphicsObjectUnscaledBounds<Cutplane>(bounds, onlyVisible);
3251    mergeGraphicsObjectUnscaledBounds<Cylinder>(bounds, onlyVisible);
3252    mergeGraphicsObjectUnscaledBounds<Disk>(bounds, onlyVisible);
3253    mergeGraphicsObjectUnscaledBounds<Glyphs>(bounds, onlyVisible);
3254    mergeGraphicsObjectUnscaledBounds<HeightMap>(bounds, onlyVisible);
3255    mergeGraphicsObjectUnscaledBounds<LIC>(bounds, onlyVisible);
3256    mergeGraphicsObjectUnscaledBounds<Line>(bounds, onlyVisible);
3257    mergeGraphicsObjectUnscaledBounds<Molecule>(bounds, onlyVisible);
3258    mergeGraphicsObjectUnscaledBounds<Outline>(bounds, onlyVisible);
3259    mergeGraphicsObjectUnscaledBounds<PolyData>(bounds, onlyVisible);
3260    mergeGraphicsObjectUnscaledBounds<Polygon>(bounds, onlyVisible);
3261    mergeGraphicsObjectUnscaledBounds<PseudoColor>(bounds, onlyVisible);
3262    mergeGraphicsObjectUnscaledBounds<Sphere>(bounds, onlyVisible);
3263    mergeGraphicsObjectUnscaledBounds<Streamlines>(bounds, onlyVisible);
3264    mergeGraphicsObjectUnscaledBounds<Volume>(bounds, onlyVisible);
3265    mergeGraphicsObjectUnscaledBounds<Warp>(bounds, onlyVisible);
3266
3267    for (int i = 0; i < 6; i += 2) {
3268        if (bounds[i+1] < bounds[i]) {
3269            bounds[i] = -0.5;
3270            bounds[i+1] = 0.5;
3271        }
3272    }
3273
3274    int numDims = 0;
3275    if (bounds[0] != bounds[1])
3276        numDims++;
3277    if (bounds[2] != bounds[3])
3278        numDims++;
3279    if (bounds[4] != bounds[5])
3280        numDims++;
3281
3282    if (numDims == 0) {
3283        bounds[0] -= .5;
3284        bounds[1] += .5;
3285        bounds[2] -= .5;
3286        bounds[3] += .5;
3287    }
3288
3289    TRACE("Bounds: %g %g %g %g %g %g",
3290          bounds[0],
3291          bounds[1],
3292          bounds[2],
3293          bounds[3],
3294          bounds[4],
3295          bounds[5]);
3296}
3297
3298/**
3299 * \brief Update data ranges for color-mapping and contours
3300 */
3301void Renderer::updateFieldRanges()
3302{
3303    collectDataRanges();
3304
3305    updateGraphicsObjectFieldRanges<Contour2D>();
3306    updateGraphicsObjectFieldRanges<Contour3D>();
3307    updateGraphicsObjectFieldRanges<Cutplane>();
3308    updateGraphicsObjectFieldRanges<Glyphs>();
3309    updateGraphicsObjectFieldRanges<HeightMap>();
3310    updateGraphicsObjectFieldRanges<LIC>();
3311    updateGraphicsObjectFieldRanges<Molecule>();
3312    updateGraphicsObjectFieldRanges<PseudoColor>();
3313    updateGraphicsObjectFieldRanges<Streamlines>();
3314    updateGraphicsObjectFieldRanges<Volume>();
3315    updateGraphicsObjectFieldRanges<Warp>();
3316}
3317
3318/**
3319 * \brief Collect cumulative data range of all DataSets
3320 *
3321 * \param[out] range Data range of all DataSets
3322 * \param[in] name Field name
3323 * \param[in] type Attribute type: e.g. POINT_DATA, CELL_DATA
3324 * \param[in] component Array component or -1 for magnitude
3325 * \param[in] onlyVisible Only collect range of visible DataSets
3326 */
3327void Renderer::collectDataRanges(double *range, const char *name,
3328                                 DataSet::DataAttributeType type,
3329                                 int component, bool onlyVisible)
3330{
3331    range[0] = DBL_MAX;
3332    range[1] = -DBL_MAX;
3333
3334    for (DataSetHashmap::iterator itr = _dataSets.begin();
3335         itr != _dataSets.end(); ++itr) {
3336        if (!onlyVisible || itr->second->getVisibility()) {
3337            double r[2];
3338            r[0] = DBL_MAX;
3339            r[1] = -DBL_MAX;
3340            itr->second->getDataRange(r, name, type, component);
3341            range[0] = min2(range[0], r[0]);
3342            range[1] = max2(range[1], r[1]);
3343        }
3344    }
3345    if (range[0] == DBL_MAX)
3346        range[0] = 0;
3347    if (range[1] == -DBL_MAX)
3348        range[1] = 1;
3349   
3350    TRACE("n:'%s' t:%d c:%d [%g,%g]", name, type, component,
3351          range[0], range[1]);
3352}
3353
3354/**
3355 * \brief Clear field range hashtables and free memory
3356 */
3357void Renderer::clearFieldRanges()
3358{
3359    TRACE("Deleting Field Ranges");
3360    for (FieldRangeHashmap::iterator itr = _scalarPointDataRange.begin();
3361         itr != _scalarPointDataRange.end(); ++itr) {
3362        delete [] itr->second;
3363    }
3364    _scalarPointDataRange.clear();
3365    for (FieldRangeHashmap::iterator itr = _scalarCellDataRange.begin();
3366         itr != _scalarCellDataRange.end(); ++itr) {
3367        delete [] itr->second;
3368    }
3369    _scalarCellDataRange.clear();
3370    for (FieldRangeHashmap::iterator itr = _vectorPointDataRange.begin();
3371         itr != _vectorPointDataRange.end(); ++itr) {
3372        delete [] itr->second;
3373    }
3374    _vectorPointDataRange.clear();
3375    for (int i = 0; i < 3; i++) {
3376        for (FieldRangeHashmap::iterator itr = _vectorCompPointDataRange[i].begin();
3377             itr != _vectorCompPointDataRange[i].end(); ++itr) {
3378            delete [] itr->second;
3379        }
3380        _vectorCompPointDataRange[i].clear();
3381    }
3382    for (FieldRangeHashmap::iterator itr = _vectorCellDataRange.begin();
3383         itr != _vectorCellDataRange.end(); ++itr) {
3384        delete [] itr->second;
3385    }
3386    _vectorCellDataRange.clear();
3387    for (int i = 0; i < 3; i++) {
3388        for (FieldRangeHashmap::iterator itr = _vectorCompCellDataRange[i].begin();
3389             itr != _vectorCompCellDataRange[i].end(); ++itr) {
3390            delete [] itr->second;
3391        }
3392        _vectorCompCellDataRange[i].clear();
3393    }
3394}
3395
3396/**
3397 * \brief Clear user-defined field range hashtables and free memory
3398 */
3399void Renderer::clearUserFieldRanges()
3400{
3401    TRACE("Deleting User Field Ranges");
3402    for (FieldRangeHashmap::iterator itr = _userScalarPointDataRange.begin();
3403         itr != _userScalarPointDataRange.end(); ++itr) {
3404        delete [] itr->second;
3405    }
3406    _userScalarPointDataRange.clear();
3407    for (FieldRangeHashmap::iterator itr = _userScalarCellDataRange.begin();
3408         itr != _userScalarCellDataRange.end(); ++itr) {
3409        delete [] itr->second;
3410    }
3411    _userScalarCellDataRange.clear();
3412    for (FieldRangeHashmap::iterator itr = _userVectorPointDataRange.begin();
3413         itr != _userVectorPointDataRange.end(); ++itr) {
3414        delete [] itr->second;
3415    }
3416    _userVectorPointDataRange.clear();
3417    for (int i = 0; i < 3; i++) {
3418        for (FieldRangeHashmap::iterator itr = _userVectorCompPointDataRange[i].begin();
3419             itr != _userVectorCompPointDataRange[i].end(); ++itr) {
3420            delete [] itr->second;
3421        }
3422        _userVectorCompPointDataRange[i].clear();
3423    }
3424    for (FieldRangeHashmap::iterator itr = _userVectorCellDataRange.begin();
3425         itr != _userVectorCellDataRange.end(); ++itr) {
3426        delete [] itr->second;
3427    }
3428    _userVectorCellDataRange.clear();
3429    for (int i = 0; i < 3; i++) {
3430        for (FieldRangeHashmap::iterator itr = _userVectorCompCellDataRange[i].begin();
3431             itr != _userVectorCompCellDataRange[i].end(); ++itr) {
3432            delete [] itr->second;
3433        }
3434        _userVectorCompCellDataRange[i].clear();
3435    }
3436}
3437
3438/**
3439 * \brief Set up hashtables for min/max values of all fields from loaded
3440 * datasets
3441 *
3442 * Note that this method does not set the ranges, it just creates the table
3443 * entries
3444 */
3445void Renderer::initFieldRanges()
3446{
3447    clearFieldRanges();
3448
3449    for (DataSetHashmap::iterator itr = _dataSets.begin();
3450         itr != _dataSets.end(); ++itr) {
3451        DataSet *ds = itr->second;
3452        std::vector<std::string> names;
3453        ds->getFieldNames(names, DataSet::POINT_DATA, 1);
3454        for (std::vector<std::string>::iterator itr = names.begin();
3455             itr != names.end(); ++itr) {
3456            FieldRangeHashmap::iterator fritr =
3457                _scalarPointDataRange.find(*itr);
3458            if (fritr == _scalarPointDataRange.end()) {
3459                _scalarPointDataRange[*itr] = new double[2];
3460                _scalarPointDataRange[*itr][0] = 0;
3461                _scalarPointDataRange[*itr][1] = 1;
3462            }
3463        }
3464        names.clear();
3465        ds->getFieldNames(names, DataSet::CELL_DATA, 1);
3466        for (std::vector<std::string>::iterator itr = names.begin();
3467             itr != names.end(); ++itr) {
3468            FieldRangeHashmap::iterator fritr =
3469                _scalarCellDataRange.find(*itr);
3470            if (fritr == _scalarCellDataRange.end()) {
3471                _scalarCellDataRange[*itr] = new double[2];
3472                _scalarCellDataRange[*itr][0] = 0;
3473                _scalarCellDataRange[*itr][1] = 1;
3474            }
3475        }
3476        names.clear();
3477        ds->getFieldNames(names, DataSet::POINT_DATA, 3);
3478        for (std::vector<std::string>::iterator itr = names.begin();
3479             itr != names.end(); ++itr) {
3480            FieldRangeHashmap::iterator fritr =
3481                _vectorPointDataRange.find(*itr);
3482            if (fritr == _vectorPointDataRange.end()) {
3483                _vectorPointDataRange[*itr] = new double[2];
3484                _vectorPointDataRange[*itr][0] = 0;
3485                _vectorPointDataRange[*itr][1] = 1;
3486            }
3487            for (int i = 0; i < 3; i++) {
3488                fritr = _vectorCompPointDataRange[i].find(*itr);
3489                if (fritr == _vectorCompPointDataRange[i].end()) {
3490                    _vectorCompPointDataRange[i][*itr] = new double[2];
3491                    _vectorCompPointDataRange[i][*itr][0] = 0;
3492                    _vectorCompPointDataRange[i][*itr][1] = 1;
3493                }
3494            }
3495        }
3496        names.clear();
3497        ds->getFieldNames(names, DataSet::CELL_DATA, 3);
3498        for (std::vector<std::string>::iterator itr = names.begin();
3499             itr != names.end(); ++itr) {
3500            FieldRangeHashmap::iterator fritr =
3501                _vectorCellDataRange.find(*itr);
3502            if (fritr == _vectorCellDataRange.end()) {
3503                _vectorCellDataRange[*itr] = new double[2];
3504                _vectorCellDataRange[*itr][0] = 0;
3505                _vectorCellDataRange[*itr][1] = 1;
3506            }
3507            for (int i = 0; i < 3; i++) {
3508                fritr = _vectorCompCellDataRange[i].find(*itr);
3509                if (fritr == _vectorCompCellDataRange[i].end()) {
3510                    _vectorCompCellDataRange[i][*itr] = new double[2];
3511                    _vectorCompCellDataRange[i][*itr][0] = 0;
3512                    _vectorCompCellDataRange[i][*itr][1] = 1;
3513                }
3514            }
3515        }
3516    }
3517}
3518
3519/**
3520 * \brief Returns boolean flag indicating if cumulative data ranges
3521 * should be used
3522 */
3523bool Renderer::getUseCumulativeRange()
3524{
3525    return _useCumulativeRange;
3526}
3527
3528/**
3529 * \brief Set explicit range to use for mapping fields
3530 */
3531bool Renderer::setCumulativeDataRange(double *range, const char *name,
3532                                      DataSet::DataAttributeType type,
3533                                      int numComponents,
3534                                      int component)
3535{
3536    if (range == NULL || name == NULL)
3537        return false;
3538
3539    _useCumulativeRange = true;
3540    bool found = false;
3541
3542    switch (type) {
3543    case DataSet::POINT_DATA: {
3544        FieldRangeHashmap::iterator itr;
3545        if (numComponents == 1) {
3546            itr = _userScalarPointDataRange.find(name);
3547            if (itr == _userScalarPointDataRange.end()) {
3548                _userScalarPointDataRange[name] = new double[2];
3549                memcpy(_userScalarPointDataRange[name], range, sizeof(double)*2);
3550            } else {
3551                found = true;
3552                memcpy(itr->second, range, sizeof(double)*2);
3553            }
3554        } else if (numComponents == 3) {
3555            if (component == -1) {
3556                itr = _userVectorPointDataRange.find(name);
3557                if (itr == _userVectorPointDataRange.end()) {
3558                    _userVectorPointDataRange[name] = new double[2];
3559                    memcpy(itr->second, _userVectorPointDataRange[name], sizeof(double)*2);
3560                } else {
3561                    found = true;
3562                    memcpy(itr->second, range, sizeof(double)*2);
3563                }
3564                return found;
3565            } else if (component >= 0 && component <= 3) {
3566                itr = _userVectorCompPointDataRange[component].find(name);
3567                if (itr == _userVectorCompPointDataRange[component].end()) {
3568                    _userVectorCompPointDataRange[component][name] = new double[2];
3569                    memcpy(_userVectorCompPointDataRange[component][name], range, sizeof(double)*2);
3570                } else {
3571                    found = true;
3572                    memcpy(itr->second, range, sizeof(double)*2);
3573                }
3574            }
3575        }
3576    }
3577        break;
3578    case DataSet::CELL_DATA: {
3579        FieldRangeHashmap::iterator itr;
3580        if (numComponents == 1) {
3581            itr = _userScalarCellDataRange.find(name);
3582            if (itr == _userScalarCellDataRange.end()) {
3583                _userScalarCellDataRange[name] = new double[2];
3584                memcpy(_userScalarCellDataRange[name], range, sizeof(double)*2);
3585            } else {
3586                found = true;
3587                memcpy(itr->second, range, sizeof(double)*2);
3588            }
3589        } else if (numComponents == 3) {
3590            if (component == -1) {
3591                itr = _userVectorCellDataRange.find(name);
3592                if (itr == _userVectorCellDataRange.end()) {
3593                    _userVectorCellDataRange[name] = new double[2];
3594                    memcpy(_userVectorCellDataRange[name], range, sizeof(double)*2);
3595                } else {
3596                    found = true;
3597                    memcpy(itr->second, range, sizeof(double)*2);
3598                }
3599            } else if (component >= 0 && component <= 3) {
3600                itr = _userVectorCompCellDataRange[component].find(name);
3601                if (itr == _userVectorCompCellDataRange[component].end()) {
3602                    _userVectorCompCellDataRange[component][name] = new double[2];
3603                    memcpy(_userVectorCompCellDataRange[component][name], range, sizeof(double)*2);
3604                } else {
3605                    found = true;
3606                    memcpy(itr->second, range, sizeof(double)*2);
3607                }
3608            }
3609        }
3610    }
3611        break;
3612    default:
3613        ERROR("Bad Field Type");
3614    }
3615
3616    // Notify graphics objects of new ranges
3617    updateFieldRanges();
3618    // Bounds may have changed
3619    sceneBoundsChanged();
3620    _needsRedraw = true;
3621
3622    TRACE("Field: %s found: %d range: %g %g", name, (found ? 1 : 0), range[0], range[1]);
3623    return found;
3624}
3625
3626/**
3627 * \brief Get the cumulative range across all DataSets for a point
3628 * data field if it exists, otherwise a cell data field if it exists
3629 *
3630 * \param[out] range Pointer to an array of 2 doubles
3631 * \param[in] name Field name
3632 * \param[in] numComponents Number of components in field
3633 * \param[in] component Index of component or -1 for magnitude/scalar
3634 * \return boolean indicating if field was found
3635 */
3636bool Renderer::getCumulativeDataRange(double *range, const char *name,
3637                                      int numComponents,
3638                                      int component)
3639{
3640    bool ret;
3641    if (!(ret = getCumulativeDataRange(range, name, DataSet::POINT_DATA,
3642                                       numComponents, component)))
3643        ret = getCumulativeDataRange(range, name, DataSet::CELL_DATA,
3644                                     numComponents, component);
3645    return ret;
3646}
3647
3648/**
3649 * \brief Get the cumulative range across all DataSets for a field
3650 *
3651 * \param[out] range Pointer to an array of 2 doubles
3652 * \param[in] name Field name
3653 * \param[in] type DataAttributeType of field
3654 * \param[in] numComponents Number of components in field
3655 * \param[in] component Index of component or -1 for magnitude/scalar
3656 * \return boolean indicating if field was found
3657 */
3658bool Renderer::getCumulativeDataRange(double *range, const char *name,
3659                                      DataSet::DataAttributeType type,
3660                                      int numComponents,
3661                                      int component)
3662{
3663    if (range == NULL || name == NULL)
3664        return false;
3665
3666    switch (type) {
3667    case DataSet::POINT_DATA: {
3668        FieldRangeHashmap::iterator itr;
3669        if (numComponents == 1) {
3670            itr = _userScalarPointDataRange.find(name);
3671            if (itr == _userScalarPointDataRange.end()) {
3672                itr = _scalarPointDataRange.find(name);
3673                if (itr == _scalarPointDataRange.end()) {
3674                    return false;
3675                }
3676            }
3677            memcpy(range, itr->second, sizeof(double)*2);
3678            return true;
3679        } else if (numComponents == 3) {
3680            if (component == -1) {
3681                itr = _userVectorPointDataRange.find(name);
3682                if (itr == _userVectorPointDataRange.end()) {
3683                    itr = _vectorPointDataRange.find(name);
3684                    if (itr == _vectorPointDataRange.end()) {
3685                        return false;
3686                    }
3687                }
3688                memcpy(range, itr->second, sizeof(double)*2);
3689                return true;
3690            } else if (component >= 0 && component <= 3) {
3691                itr = _userVectorCompPointDataRange[component].find(name);
3692                if (itr == _userVectorCompPointDataRange[component].end()) {
3693                    itr = _vectorCompPointDataRange[component].find(name);
3694                    if (itr == _vectorCompPointDataRange[component].end()) {
3695                        return false;
3696                    }
3697                }
3698                memcpy(range, itr->second, sizeof(double)*2);
3699                return true;
3700            }
3701        }
3702    }
3703        break;
3704    case DataSet::CELL_DATA: {
3705        FieldRangeHashmap::iterator itr;
3706        if (numComponents == 1) {
3707            itr = _userScalarCellDataRange.find(name);
3708            if (itr == _userScalarCellDataRange.end()) {
3709                itr = _scalarCellDataRange.find(name);
3710                if (itr == _scalarCellDataRange.end()) {
3711                    return false;
3712                }
3713            }
3714            memcpy(range, itr->second, sizeof(double)*2);
3715            return true;
3716        } else if (numComponents == 3) {
3717            if (component == -1) {
3718                itr = _userVectorCellDataRange.find(name);
3719                if (itr == _userVectorCellDataRange.end()) {
3720                    itr = _vectorCellDataRange.find(name);
3721                    if (itr == _vectorCellDataRange.end()) {
3722                        return false;
3723                    }
3724                }
3725                memcpy(range, itr->second, sizeof(double)*2);
3726                return true;
3727            } else if (component >= 0 && component <= 3) {
3728                itr = _userVectorCompCellDataRange[component].find(name);
3729                if (itr == _userVectorCompCellDataRange[component].end()) {
3730                    itr = _vectorCompCellDataRange[component].find(name);
3731                    if (itr == _vectorCompCellDataRange[component].end()) {
3732                        return false;
3733                    }
3734                }
3735                memcpy(range, itr->second, sizeof(double)*2);
3736                return true;
3737            }
3738        }
3739    }
3740        break;
3741    default:
3742        break;
3743    }
3744    return false;
3745}
3746
3747void Renderer::collectDataRanges()
3748{
3749    for (FieldRangeHashmap::iterator itr = _scalarPointDataRange.begin();
3750         itr != _scalarPointDataRange.end(); ++itr) {
3751        collectDataRanges(itr->second, itr->first.c_str(),
3752                          DataSet::POINT_DATA, -1,
3753                          _cumulativeRangeOnlyVisible);
3754    }
3755    for (FieldRangeHashmap::iterator itr = _scalarCellDataRange.begin();
3756         itr != _scalarCellDataRange.end(); ++itr) {
3757        collectDataRanges(itr->second, itr->first.c_str(),
3758                          DataSet::CELL_DATA, -1,
3759                          _cumulativeRangeOnlyVisible);
3760    }
3761    for (FieldRangeHashmap::iterator itr = _vectorPointDataRange.begin();
3762         itr != _vectorPointDataRange.end(); ++itr) {
3763        collectDataRanges(itr->second, itr->first.c_str(),
3764                          DataSet::POINT_DATA, -1,
3765                          _cumulativeRangeOnlyVisible);
3766    }
3767    for (int i = 0; i < 3; i++) {
3768        for (FieldRangeHashmap::iterator itr = _vectorCompPointDataRange[i].begin();
3769             itr != _vectorCompPointDataRange[i].end(); ++itr) {
3770            collectDataRanges(itr->second, itr->first.c_str(),
3771                              DataSet::POINT_DATA, i,
3772                              _cumulativeRangeOnlyVisible);
3773        }
3774    }
3775    for (FieldRangeHashmap::iterator itr = _vectorCellDataRange.begin();
3776         itr != _vectorCellDataRange.end(); ++itr) {
3777        collectDataRanges(itr->second, itr->first.c_str(),
3778                          DataSet::CELL_DATA, -1,
3779                          _cumulativeRangeOnlyVisible);
3780    }
3781    for (int i = 0; i < 3; i++) {
3782        for (FieldRangeHashmap::iterator itr = _vectorCompCellDataRange[i].begin();
3783             itr != _vectorCompCellDataRange[i].end(); ++itr) {
3784            collectDataRanges(itr->second, itr->first.c_str(),
3785                              DataSet::CELL_DATA, i,
3786                              _cumulativeRangeOnlyVisible);
3787        }
3788    }
3789}
3790
3791/**
3792 * \brief Determines if AABB lies in a principal axis plane
3793 * and if so, returns the plane normal
3794 */
3795bool Renderer::is2D(const double bounds[6],
3796                    PrincipalPlane *plane,
3797                    double *offset) const
3798{
3799    if (bounds[4] == bounds[5]) {
3800        // Z = 0, XY plane
3801        if (plane)
3802            *plane = PLANE_XY;
3803        if (offset)
3804            *offset = bounds[4];
3805        return true;
3806    } else if (bounds[0] == bounds[1]) {
3807        // X = 0, ZY plane
3808        if (plane)
3809            *plane = PLANE_ZY;
3810        if (offset)
3811            *offset = bounds[0];
3812        return true;
3813    } else if (bounds[2] == bounds[3]) {
3814        // Y = 0, XZ plane
3815        if (plane)
3816            *plane = PLANE_XZ;
3817        if (offset)
3818            *offset = bounds[2];
3819        return true;
3820    }
3821    *plane = PLANE_XY;
3822    *offset = 0;
3823    return false;
3824}
3825
3826int Renderer::addLight(float pos[3])
3827{
3828    vtkSmartPointer<vtkLight> light = vtkSmartPointer<vtkLight>::New();
3829    light->SetLightTypeToCameraLight();
3830    light->SetPosition(pos[0], pos[1], pos[2]);
3831    light->SetFocalPoint(0, 0, 0);
3832    light->PositionalOff();
3833    _renderer->AddLight(light);
3834    _needsRedraw = true;
3835    return (_renderer->GetLights()->GetNumberOfItems()-1);
3836}
3837
3838vtkLight *Renderer::getLight(int lightIdx)
3839{
3840    vtkLightCollection *lights = _renderer->GetLights();
3841    if (lights->GetNumberOfItems() < lightIdx+1)
3842        return NULL;
3843    lights->InitTraversal();
3844    vtkLight *light = NULL;
3845    int i = 0;
3846    do {
3847        light = lights->GetNextItem();
3848    } while (i++ < lightIdx);
3849    return light;
3850}
3851
3852void Renderer::setLightSwitch(int lightIdx, bool state)
3853{
3854    vtkLight *light = getLight(lightIdx);
3855    if (light == NULL) {
3856        ERROR("Unknown light %d", lightIdx);
3857        return;
3858    }
3859    light->SetSwitch((state ? 1 : 0));
3860    _needsRedraw = true;
3861}
3862
3863/**
3864 * \brief Initialize the camera zoom region to include the bounding volume given
3865 */
3866void Renderer::initCamera(bool initCameraMode)
3867{
3868#ifdef WANT_TRACE
3869    switch (_cameraMode) {
3870    case IMAGE:
3871        TRACE("Image camera");
3872        break;
3873    case ORTHO:
3874        TRACE("Ortho camera");
3875        break;
3876    case PERSPECTIVE:
3877        TRACE("Perspective camera");
3878        break;
3879    default:
3880        TRACE("Unknown camera mode");
3881    }
3882#endif
3883    // Clear user requested zoom region
3884    _userImgWorldOrigin[0] = 0;
3885    _userImgWorldOrigin[1] = 0;
3886    _userImgWorldDims[0] = -1;
3887    _userImgWorldDims[1] = -1;
3888
3889    double bounds[6];
3890    collectBounds(bounds, false);
3891    bool twod = is2D(bounds, &_imgCameraPlane, &_imgCameraOffset);
3892    if (twod) {
3893        if (initCameraMode) {
3894            TRACE("Changing camera mode to image");
3895            _cameraMode = IMAGE;
3896        }
3897        if (_imgCameraPlane == PLANE_ZY) {
3898            _imgWorldOrigin[0] = bounds[4];
3899            _imgWorldOrigin[1] = bounds[2];
3900            _imgWorldDims[0] = bounds[5] - bounds[4];
3901            _imgWorldDims[1] = bounds[3] - bounds[2];
3902        } else if (_imgCameraPlane == PLANE_XZ) {
3903            _imgWorldOrigin[0] = bounds[0];
3904            _imgWorldOrigin[1] = bounds[4];
3905            _imgWorldDims[0] = bounds[1] - bounds[0];
3906            _imgWorldDims[1] = bounds[5] - bounds[4];
3907        } else {
3908            _imgWorldOrigin[0] = bounds[0];
3909            _imgWorldOrigin[1] = bounds[2];
3910            _imgWorldDims[0] = bounds[1] - bounds[0];
3911            _imgWorldDims[1] = bounds[3] - bounds[2];
3912        }
3913    } else {
3914        _imgWorldOrigin[0] = bounds[0];
3915        _imgWorldOrigin[1] = bounds[2];
3916        _imgWorldDims[0] = bounds[1] - bounds[0];
3917        _imgWorldDims[1] = bounds[3] - bounds[2];
3918    }
3919
3920    _cameraPan[0] = 0;
3921    _cameraPan[1] = 0;
3922    _cameraZoomRatio = 1;
3923
3924    switch (_cameraMode) {
3925    case IMAGE:
3926        //_renderer->ResetCamera(bounds);
3927        _setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
3928                             _imgWorldDims[0], _imgWorldDims[1]);
3929        resetAxes(bounds);
3930        break;
3931    case ORTHO:
3932        _renderer->GetActiveCamera()->ParallelProjectionOn();
3933        resetAxes(bounds);
3934        //_renderer->ResetCamera(bounds);
3935        resetVtkCamera();
3936        //computeScreenWorldCoords();
3937        break;
3938    case PERSPECTIVE:
3939        _renderer->GetActiveCamera()->ParallelProjectionOff();
3940        resetAxes(bounds);
3941        //_renderer->ResetCamera(bounds);
3942        resetVtkCamera();
3943        //computeScreenWorldCoords();
3944        break;
3945    default:
3946        ERROR("Unknown camera mode");
3947    }
3948
3949#ifdef WANT_TRACE
3950    printCameraInfo(_renderer->GetActiveCamera());
3951#endif
3952}
3953
3954#if 0
3955/**
3956 * \brief Print debugging info about a vtkCamera
3957 */
3958void Renderer::printCameraInfo(vtkCamera *camera)
3959{
3960    TRACE("pscale: %g, angle: %g, d: %g pos: %g %g %g, fpt: %g %g %g, vup: %g %g %g, clip: %g %g",
3961          camera->GetParallelScale(),
3962          camera->GetViewAngle(),
3963          camera->GetDistance(),
3964          camera->GetPosition()[0],
3965          camera->GetPosition()[1],
3966          camera->GetPosition()[2],
3967          camera->GetFocalPoint()[0],
3968          camera->GetFocalPoint()[1],
3969          camera->GetFocalPoint()[2],
3970          camera->GetViewUp()[0],
3971          camera->GetViewUp()[1],
3972          camera->GetViewUp()[2],
3973          camera->GetClippingRange()[0],
3974          camera->GetClippingRange()[1]);
3975}
3976#endif
3977
3978/**
3979 * \brief Set the RGB background color to render into the image
3980 */
3981void Renderer::setBackgroundColor(float color[3])
3982{
3983    _bgColor[0] = color[0];
3984    _bgColor[1] = color[1];
3985    _bgColor[2] = color[2];
3986    _renderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
3987    _needsRedraw = true;
3988}
3989
3990/**
3991 * \brief Set the opacity of the specified DataSet's associated graphics objects
3992 */
3993void Renderer::setDataSetOpacity(const DataSetId& id, double opacity)
3994{
3995    DataSetHashmap::iterator itr;
3996
3997    bool doAll = false;
3998
3999    if (id.compare("all") == 0) {
4000        itr = _dataSets.begin();
4001        if (itr == _dataSets.end())
4002            return;
4003        doAll = true;
4004    } else {
4005        itr = _dataSets.find(id);
4006    }
4007    if (itr == _dataSets.end()) {
4008        ERROR("Unknown dataset %s", id.c_str());
4009        return;
4010    }
4011
4012    do {
4013        itr->second->setOpacity(opacity);
4014    } while (doAll && ++itr != _dataSets.end());
4015
4016    if (id.compare("all") == 0 || getGraphicsObject<Contour2D>(id) != NULL)
4017        setGraphicsObjectOpacity<Contour2D>(id, opacity);
4018    if (id.compare("all") == 0 || getGraphicsObject<Contour3D>(id) != NULL)
4019        setGraphicsObjectOpacity<Contour3D>(id, opacity);
4020    if (id.compare("all") == 0 || getGraphicsObject<Cutplane>(id) != NULL)
4021        setGraphicsObjectOpacity<Cutplane>(id, opacity);
4022    if (id.compare("all") == 0 || getGraphicsObject<Glyphs>(id) != NULL)
4023        setGraphicsObjectOpacity<Glyphs>(id, opacity);
4024    if (id.compare("all") == 0 || getGraphicsObject<HeightMap>(id) != NULL)
4025        setGraphicsObjectOpacity<HeightMap>(id, opacity);
4026    if (id.compare("all") == 0 || getGraphicsObject<LIC>(id) != NULL)
4027        setGraphicsObjectOpacity<LIC>(id, opacity);
4028    if (id.compare("all") == 0 || getGraphicsObject<Molecule>(id) != NULL)
4029        setGraphicsObjectOpacity<Molecule>(id, opacity);
4030    if (id.compare("all") == 0 || getGraphicsObject<Outline>(id) != NULL)
4031        setGraphicsObjectOpacity<Outline>(id, opacity);
4032    if (id.compare("all") == 0 || getGraphicsObject<PolyData>(id) != NULL)
4033        setGraphicsObjectOpacity<PolyData>(id, opacity);
4034    if (id.compare("all") == 0 || getGraphicsObject<PseudoColor>(id) != NULL)
4035        setGraphicsObjectOpacity<PseudoColor>(id, opacity);
4036    if (id.compare("all") == 0 || getGraphicsObject<Streamlines>(id) != NULL)
4037        setGraphicsObjectOpacity<Streamlines>(id, opacity);
4038    if (id.compare("all") == 0 || getGraphicsObject<Volume>(id) != NULL)
4039        setGraphicsObjectOpacity<Volume>(id, opacity);
4040    if (id.compare("all") == 0 || getGraphicsObject<Warp>(id) != NULL)
4041        setGraphicsObjectOpacity<Warp>(id, opacity);
4042
4043    _needsRedraw = true;
4044}
4045
4046/**
4047 * \brief Turn on/off rendering of the specified DataSet's associated graphics objects
4048 */
4049void Renderer::setDataSetVisibility(const DataSetId& id, bool state)
4050{
4051    DataSetHashmap::iterator itr;
4052
4053    bool doAll = false;
4054
4055    if (id.compare("all") == 0) {
4056        itr = _dataSets.begin();
4057        doAll = true;
4058        if (itr == _dataSets.end())
4059            return;
4060    } else {
4061        itr = _dataSets.find(id);
4062    }
4063    if (itr == _dataSets.end()) {
4064        ERROR("Unknown dataset %s", id.c_str());
4065        return;
4066    }
4067
4068    do {
4069        itr->second->setVisibility(state);
4070    } while (doAll && ++itr != _dataSets.end());
4071
4072    if (id.compare("all") == 0 || getGraphicsObject<Contour2D>(id) != NULL)
4073        setGraphicsObjectVisibility<Contour2D>(id, state);
4074    if (id.compare("all") == 0 || getGraphicsObject<Contour3D>(id) != NULL)
4075        setGraphicsObjectVisibility<Contour3D>(id, state);
4076    if (id.compare("all") == 0 || getGraphicsObject<Cutplane>(id) != NULL)
4077        setGraphicsObjectVisibility<Cutplane>(id, state);
4078    if (id.compare("all") == 0 || getGraphicsObject<Glyphs>(id) != NULL)
4079        setGraphicsObjectVisibility<Glyphs>(id, state);
4080    if (id.compare("all") == 0 || getGraphicsObject<HeightMap>(id) != NULL)
4081        setGraphicsObjectVisibility<HeightMap>(id, state);
4082    if (id.compare("all") == 0 || getGraphicsObject<LIC>(id) != NULL)
4083        setGraphicsObjectVisibility<LIC>(id, state);
4084    if (id.compare("all") == 0 || getGraphicsObject<Molecule>(id) != NULL)
4085        setGraphicsObjectVisibility<Molecule>(id, state);
4086    if (id.compare("all") == 0 || getGraphicsObject<Outline>(id) != NULL)
4087        setGraphicsObjectVisibility<Outline>(id, state);
4088    if (id.compare("all") == 0 || getGraphicsObject<PolyData>(id) != NULL)
4089        setGraphicsObjectVisibility<PolyData>(id, state);
4090    if (id.compare("all") == 0 || getGraphicsObject<PseudoColor>(id) != NULL)
4091        setGraphicsObjectVisibility<PseudoColor>(id, state);
4092    if (id.compare("all") == 0 || getGraphicsObject<Streamlines>(id) != NULL)
4093        setGraphicsObjectVisibility<Streamlines>(id, state);
4094    if (id.compare("all") == 0 || getGraphicsObject<Volume>(id) != NULL)
4095        setGraphicsObjectVisibility<Volume>(id, state);
4096    if (id.compare("all") == 0 || getGraphicsObject<Warp>(id) != NULL)
4097        setGraphicsObjectVisibility<Warp>(id, state);
4098
4099    _needsRedraw = true;
4100}
4101
4102/**
4103 * \brief Set a user clipping plane
4104 *
4105 * TODO: Fix clip plane positions after a change in actor bounds
4106 */
4107void Renderer::setClipPlane(Axis axis, double ratio, int direction)
4108{
4109    double bounds[6];
4110    collectBounds(bounds, false);
4111
4112    switch (axis) {
4113    case X_AXIS:
4114        if (direction > 0) {
4115            if (ratio > 0.0) {
4116                if (_userClipPlanes[0] == NULL) {
4117                    _userClipPlanes[0] = vtkSmartPointer<vtkPlane>::New();
4118                    _userClipPlanes[0]->SetNormal(1, 0, 0);
4119                }
4120                _userClipPlanes[0]->SetOrigin(bounds[0] + (bounds[1]-bounds[0])*ratio, 0, 0);
4121            } else {
4122                _userClipPlanes[0] = NULL;
4123            }
4124        } else {
4125            if (ratio < 1.0) {
4126                if (_userClipPlanes[1] == NULL) {
4127                    _userClipPlanes[1] = vtkSmartPointer<vtkPlane>::New();
4128                    _userClipPlanes[1]->SetNormal(-1, 0, 0);
4129                }
4130                _userClipPlanes[1]->SetOrigin(bounds[0] + (bounds[1]-bounds[0])*ratio, 0, 0);
4131            } else {
4132                _userClipPlanes[1] = NULL;
4133            }
4134        }
4135        break;
4136    case Y_AXIS:
4137        if (direction > 0) {
4138            if (ratio > 0.0) {
4139                if (_userClipPlanes[2] == NULL) {
4140                    _userClipPlanes[2] = vtkSmartPointer<vtkPlane>::New();
4141                    _userClipPlanes[2]->SetNormal(0, 1, 0);
4142                }
4143                _userClipPlanes[2]->SetOrigin(0, bounds[2] + (bounds[3]-bounds[2])*ratio, 0);
4144            } else {
4145                _userClipPlanes[2] = NULL;
4146            }
4147        } else {
4148            if (ratio < 1.0) {
4149                if (_userClipPlanes[3] == NULL) {
4150                    _userClipPlanes[3] = vtkSmartPointer<vtkPlane>::New();
4151                    _userClipPlanes[3]->SetNormal(0, -1, 0);
4152                }
4153                _userClipPlanes[3]->SetOrigin(0, bounds[2] + (bounds[3]-bounds[2])*ratio, 0);
4154            } else {
4155                _userClipPlanes[3] = NULL;
4156            }
4157        }
4158        break;
4159    case Z_AXIS:
4160        if (direction > 0) {
4161            if (ratio > 0.0) {
4162                if (_userClipPlanes[4] == NULL) {
4163                    _userClipPlanes[4] = vtkSmartPointer<vtkPlane>::New();
4164                    _userClipPlanes[4]->SetNormal(0, 0, 1);
4165                }
4166                _userClipPlanes[4]->SetOrigin(0, 0, bounds[4] + (bounds[5]-bounds[4])*ratio);
4167            } else {
4168                _userClipPlanes[4] = NULL;
4169            }
4170        } else {
4171            if (ratio < 1.0) {
4172                if (_userClipPlanes[5] == NULL) {
4173                    _userClipPlanes[5] = vtkSmartPointer<vtkPlane>::New();
4174                    _userClipPlanes[5]->SetNormal(0, 0, -1);
4175                }
4176                _userClipPlanes[5]->SetOrigin(0, 0, bounds[4] + (bounds[5]-bounds[4])*ratio);
4177            } else {
4178                _userClipPlanes[5] = NULL;
4179            }
4180        }
4181        break;
4182    default:
4183        ;
4184    }
4185
4186    _needsRedraw = true;
4187}
4188
4189/**
4190 * \brief Set up clipping planes for image camera mode if needed
4191 */
4192void Renderer::setCameraClippingPlanes()
4193{
4194    /* XXX: Note that there appears to be a bug with setting the
4195     * clipping plane collection to NULL in the VTK Mappers --
4196     * the old clip planes are still applied.  The workaround here
4197     * is to keep the PlaneCollection and add/remove the planes
4198     * to/from the PlaneCollection as needed.
4199     */
4200    if (_cameraMode == IMAGE) {
4201        if (_activeClipPlanes->GetNumberOfItems() == 0) {
4202            for (int i = 0; i < 4; i++)
4203                _activeClipPlanes->AddItem(_cameraClipPlanes[i]);
4204        }
4205    } else {
4206        if (_activeClipPlanes->GetNumberOfItems() > 0)
4207            _activeClipPlanes->RemoveAllItems();
4208        for (int i = 0; i < 6; i++) {
4209            if (_userClipPlanes[i] != NULL) {
4210                _activeClipPlanes->AddItem(_userClipPlanes[i]);
4211            }
4212        }
4213    }
4214
4215    /* Ensure all Mappers are using the PlaneCollection
4216     * This will not change the state or timestamp of
4217     * Mappers already using the PlaneCollection
4218     */
4219    setGraphicsObjectClippingPlanes<Arc>(_activeClipPlanes);
4220    setGraphicsObjectClippingPlanes<Arrow>(_activeClipPlanes);
4221    setGraphicsObjectClippingPlanes<Box>(_activeClipPlanes);
4222    setGraphicsObjectClippingPlanes<Cone>(_activeClipPlanes);
4223    setGraphicsObjectClippingPlanes<Contour2D>(_activeClipPlanes);
4224    setGraphicsObjectClippingPlanes<Contour3D>(_activeClipPlanes);
4225    setGraphicsObjectClippingPlanes<Cutplane>(_activeClipPlanes);
4226    setGraphicsObjectClippingPlanes<Cylinder>(_activeClipPlanes);
4227    setGraphicsObjectClippingPlanes<Disk>(_activeClipPlanes);
4228    setGraphicsObjectClippingPlanes<Glyphs>(_activeClipPlanes);
4229    setGraphicsObjectClippingPlanes<Group>(_activeClipPlanes);
4230    setGraphicsObjectClippingPlanes<HeightMap>(_activeClipPlanes);
4231    setGraphicsObjectClippingPlanes<LIC>(_activeClipPlanes);
4232    setGraphicsObjectClippingPlanes<Line>(_activeClipPlanes);
4233    setGraphicsObjectClippingPlanes<Molecule>(_activeClipPlanes);
4234    setGraphicsObjectClippingPlanes<Outline>(_activeClipPlanes);
4235    setGraphicsObjectClippingPlanes<PolyData>(_activeClipPlanes);
4236    setGraphicsObjectClippingPlanes<Polygon>(_activeClipPlanes);
4237    setGraphicsObjectClippingPlanes<PseudoColor>(_activeClipPlanes);
4238    setGraphicsObjectClippingPlanes<Sphere>(_activeClipPlanes);
4239    setGraphicsObjectClippingPlanes<Streamlines>(_activeClipPlanes);
4240    setGraphicsObjectClippingPlanes<Volume>(_activeClipPlanes);
4241    setGraphicsObjectClippingPlanes<Warp>(_activeClipPlanes);
4242}
4243
4244/**
4245 * \brief Control the use of two sided lighting
4246 */
4247void Renderer::setUseTwoSidedLighting(bool state)
4248{
4249    _renderer->SetTwoSidedLighting(state ? 1 : 0);
4250    _needsRedraw = true;
4251}
4252
4253/**
4254 * \brief Control the use of the depth peeling algorithm for transparency
4255 */
4256void Renderer::setUseDepthPeeling(bool state)
4257{
4258    _renderer->SetUseDepthPeeling(state ? 1 : 0);
4259    _needsRedraw = true;
4260}
4261
4262/**
4263 * \brief Sets flag to trigger rendering next time render() is called
4264 */
4265void Renderer::eventuallyRender()
4266{
4267    _needsRedraw = true;
4268}
4269
4270/**
4271 * \brief Cause the rendering to render a new image if needed
4272 *
4273 * The _needsRedraw flag indicates if a state change has occured since
4274 * the last rendered frame
4275 */
4276bool Renderer::render()
4277{
4278    TRACE("Enter: redraw: %d axesReset: %d cameraReset: %d clippingRangeReset: %d",
4279          _needsRedraw ? 1 : 0, _needsAxesReset ? 1 : 0, _needsCameraReset ? 1 : 0, _needsCameraClippingRangeReset ? 1 : 0);
4280    if (_needsRedraw) {
4281         if (_needsAxesReset) {
4282            resetAxes();
4283            _needsAxesReset = false;
4284        }
4285        if (_needsCameraReset) {
4286            initCamera();
4287            _needsCameraReset = false;
4288            _needsCameraClippingRangeReset = false;
4289        } else if (_needsCameraClippingRangeReset && _cameraMode != IMAGE) {
4290            resetCameraClippingRange();
4291            _needsCameraClippingRangeReset = false;
4292        }
4293        setCameraClippingPlanes();
4294        _renderWindow->Render();
4295        _needsRedraw = false;
4296        return true;
4297    } else
4298        return false;
4299}
4300
4301/// Get the pixel width of the render window/image
4302int Renderer::getWindowWidth() const
4303{
4304    return _windowWidth;
4305}
4306
4307/// Get the pixel height of the render window/image
4308int Renderer::getWindowHeight() const
4309{
4310    return _windowHeight;
4311}
4312
4313/**
4314 * \brief Read back the rendered framebuffer image
4315 */
4316void Renderer::getRenderedFrame(vtkUnsignedCharArray *imgData)
4317{
4318#ifdef RENDER_TARGA
4319    _renderWindow->MakeCurrent();
4320    // Must clear previous errors first.
4321    while (glGetError() != GL_NO_ERROR){
4322        ;
4323    }
4324    int bytesPerPixel = TARGA_BYTES_PER_PIXEL;
4325    int size = bytesPerPixel * _windowWidth * _windowHeight;
4326
4327    if (imgData->GetMaxId() + 1 != size)
4328    {
4329        imgData->SetNumberOfComponents(bytesPerPixel);
4330        imgData->SetNumberOfValues(size);
4331    }
4332    glDisable(GL_TEXTURE_2D);
4333    if (_renderWindow->GetDoubleBuffer()) {
4334        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_renderWindow)->GetBackLeftBuffer()));
4335    } else {
4336        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_renderWindow)->GetFrontLeftBuffer()));
4337    }
4338    glPixelStorei(GL_PACK_ALIGNMENT, 1);
4339#ifdef WANT_TRACE
4340    struct timeval t1, t2;
4341    glFinish();
4342    gettimeofday(&t1, 0);
4343#endif
4344    if (bytesPerPixel == 4) {
4345        glReadPixels(0, 0, _windowWidth, _windowHeight, GL_BGRA,
4346                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
4347    } else {
4348        glReadPixels(0, 0, _windowWidth, _windowHeight, GL_BGR,
4349                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
4350    }
4351#ifdef WANT_TRACE
4352    gettimeofday(&t2, 0);
4353    static unsigned int numFrames = 0;
4354    static double accum = 0;
4355    numFrames++;
4356    accum += MSECS_ELAPSED(t1, t2);
4357#endif
4358    TRACE("Readback time: %g ms", MSECS_ELAPSED(t1, t2));
4359    TRACE("Readback avg: %g ms", accum/numFrames);
4360    if (glGetError() != GL_NO_ERROR) {
4361        ERROR("glReadPixels");
4362    }
4363#else
4364    _renderWindow->GetPixelData(0, 0, _windowWidth-1, _windowHeight-1,
4365                                !_renderWindow->GetDoubleBuffer(), imgData);
4366#endif
4367    TRACE("Image data size: %d", imgData->GetSize());
4368}
4369
4370/**
4371 * \brief Get nearest data value given display coordinates x,y
4372 *
4373 * FIXME: This doesn't work when actors are scaled
4374 *
4375 * Note: no interpolation is performed on data
4376 */
4377bool Renderer::getScalarValueAtPixel(const DataSetId& id, int x, int y, double *value)
4378{
4379    vtkSmartPointer<vtkCoordinate> coord = vtkSmartPointer<vtkCoordinate>::New();
4380    coord->SetCoordinateSystemToDisplay();
4381    coord->SetValue(x, _windowHeight - y, 0);
4382    double *worldCoords = coord->GetComputedWorldValue(_renderer);
4383
4384    TRACE("Pixel coords: %d, %d\nWorld coords: %g, %g, %g", x, y,
4385          worldCoords[0],
4386          worldCoords[1],
4387          worldCoords[2]);
4388
4389    return getScalarValue(id, worldCoords[0], worldCoords[1], worldCoords[2], value);
4390}
4391
4392/**
4393 * \brief Get nearest data value given world coordinates x,y,z
4394 *
4395 * Note: no interpolation is performed on data
4396 */
4397bool Renderer::getScalarValue(const DataSetId& id, double x, double y, double z, double *value)
4398{
4399    DataSet *ds = getDataSet(id);
4400    if (ds == NULL)
4401        return false;
4402
4403    return ds->getScalarValue(x, y, z, value);
4404}
4405
4406/**
4407 * \brief Get nearest data value given display coordinates x,y
4408 *
4409 * Note: no interpolation is performed on data
4410 */
4411bool Renderer::getVectorValueAtPixel(const DataSetId& id, int x, int y, double vector[3])
4412{
4413    vtkSmartPointer<vtkCoordinate> coord = vtkSmartPointer<vtkCoordinate>::New();
4414    coord->SetCoordinateSystemToDisplay();
4415    coord->SetValue(x, _windowHeight - y, 0);
4416    double *worldCoords = coord->GetComputedWorldValue(_renderer);
4417
4418    TRACE("Pixel coords: %d, %d\nWorld coords: %g, %g, %g", x, y,
4419          worldCoords[0],
4420          worldCoords[1],
4421          worldCoords[2]);
4422
4423    return getVectorValue(id, worldCoords[0], worldCoords[1], worldCoords[2], vector);
4424}
4425
4426/**
4427 * \brief Get nearest data value given world coordinates x,y,z
4428 *
4429 * Note: no interpolation is performed on data
4430 */
4431bool Renderer::getVectorValue(const DataSetId& id, double x, double y, double z, double vector[3])
4432{
4433    DataSet *ds = getDataSet(id);
4434    if (ds == NULL)
4435        return false;
4436
4437    return ds->getVectorValue(x, y, z, vector);
4438}
Note: See TracBrowser for help on using the repository browser.