source: vtkvis/branches/1.7/Renderer.cpp @ 4641

Last change on this file since 4641 was 4641, checked in by ldelgass, 7 years ago

Merge r4640 from trunk, bump version

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