source: vtkvis/trunk/Renderer.cpp

Last change on this file was 6568, checked in by ldelgass, 8 years ago

Add optional idle timeout to vtkvis server. Allows server to close connection
and exit after a specified period without commands from the client.

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