source: vtkvis/tags/1.8.0/Renderer.cpp @ 4832

Last change on this file since 4832 was 4813, checked in by ldelgass, 9 years ago

Revert a few changes that were made in the trunk

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