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

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

Improved cloud support in vtkvis: handle ugrids with no cells as well as
polydata clouds, add cloudstyle options to some graphics objects.

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