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

Last change on this file since 4073 was 4073, checked in by ldelgass, 10 years ago

Add software rendering of legends, enabled by default. Should work with vtk
6.0 release and up (i.e. work around the bug with the scalar bar actor rendering
with newer VTK).

  • Property svn:eol-style set to native
File size: 156.4 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (C) 2004-2012  HUBzero Foundation, LLC
4 *
5 * Author: Leif Delgass <ldelgass@purdue.edu>
6 */
7
8#include <cfloat>
9#include <cstring>
10#include <cassert>
11#include <cmath>
12
13#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            _cubeAxesActor->XAutoLabelFormatOff();
1561        } else if (axis == Y_AXIS) {
1562            _cubeAxesActor->SetYLabelFormat(format);
1563            _cubeAxesActor->YAutoLabelFormatOff();
1564        } else if (axis == Z_AXIS) {
1565            _cubeAxesActor->SetZLabelFormat(format);
1566            _cubeAxesActor->ZAutoLabelFormatOff();
1567        }
1568        _needsRedraw = true;
1569    }
1570}
1571
1572/**
1573 * \brief Notify graphics objects that color map has changed
1574 */
1575void Renderer::updateColorMap(ColorMap *cmap)
1576{
1577    TRACE("%s", cmap->getName().c_str());
1578    updateGraphicsObjectColorMap<Contour2D>(cmap);
1579    updateGraphicsObjectColorMap<Contour3D>(cmap);
1580    updateGraphicsObjectColorMap<Cutplane>(cmap);
1581    updateGraphicsObjectColorMap<Glyphs>(cmap);
1582    updateGraphicsObjectColorMap<HeightMap>(cmap);
1583    updateGraphicsObjectColorMap<Image>(cmap);
1584    updateGraphicsObjectColorMap<LIC>(cmap);
1585    updateGraphicsObjectColorMap<Molecule>(cmap);
1586    updateGraphicsObjectColorMap<PolyData>(cmap);
1587    updateGraphicsObjectColorMap<PseudoColor>(cmap);
1588    updateGraphicsObjectColorMap<Streamlines>(cmap);
1589    updateGraphicsObjectColorMap<Volume>(cmap);
1590    updateGraphicsObjectColorMap<Warp>(cmap);
1591    TRACE("Leave");
1592}
1593
1594/**
1595 * \brief Check if a ColorMap is in use by graphics objects
1596 */
1597bool Renderer::colorMapUsed(ColorMap *cmap)
1598{
1599    if (graphicsObjectColorMapUsed<Contour2D>(cmap))
1600        return true;
1601    if (graphicsObjectColorMapUsed<Contour3D>(cmap))
1602        return true;
1603    if (graphicsObjectColorMapUsed<Cutplane>(cmap))
1604        return true;
1605    if (graphicsObjectColorMapUsed<Glyphs>(cmap))
1606        return true;
1607    if (graphicsObjectColorMapUsed<HeightMap>(cmap))
1608        return true;
1609    if (graphicsObjectColorMapUsed<Image>(cmap))
1610        return true;
1611    if (graphicsObjectColorMapUsed<LIC>(cmap))
1612        return true;
1613    if (graphicsObjectColorMapUsed<Molecule>(cmap))
1614        return true;
1615    if (graphicsObjectColorMapUsed<PolyData>(cmap))
1616        return true;
1617    if (graphicsObjectColorMapUsed<PseudoColor>(cmap))
1618        return true;
1619    if (graphicsObjectColorMapUsed<Streamlines>(cmap))
1620        return true;
1621    if (graphicsObjectColorMapUsed<Volume>(cmap))
1622        return true;
1623    if (graphicsObjectColorMapUsed<Warp>(cmap))
1624        return true;
1625
1626    return false;
1627}
1628
1629/**
1630 * \brief Add/replace a ColorMap for use in the Renderer
1631 */
1632void Renderer::addColorMap(const ColorMapId& id, ColorMap *colorMap)
1633{
1634    if (colorMap != NULL) {
1635        colorMap->build();
1636        if (getColorMap(id) != NULL) {
1637            TRACE("Replacing existing ColorMap %s", id.c_str());
1638            // Copy to current colormap to avoid invalidating
1639            // pointers in graphics objects using the color map
1640            *_colorMaps[id] = *colorMap;
1641            delete colorMap;
1642            // Notify graphics objects of change
1643            updateColorMap(_colorMaps[id]);
1644        } else
1645            _colorMaps[id] = colorMap;
1646    } else {
1647        ERROR("NULL ColorMap");
1648    }
1649}
1650
1651/**
1652 * \brief Return the ColorMap associated with the colormap key given
1653 */
1654ColorMap *Renderer::getColorMap(const ColorMapId& id)
1655{
1656    ColorMapHashmap::iterator itr = _colorMaps.find(id);
1657
1658    if (itr == _colorMaps.end())
1659        return NULL;
1660    else
1661        return itr->second;
1662}
1663
1664/**
1665 * \brief Remove the colormap associated with the key given
1666 *
1667 * The underlying vtkLookupTable will be deleted if it is not referenced
1668 * by any other objects
1669 */
1670void Renderer::deleteColorMap(const ColorMapId& id)
1671{
1672    ColorMapHashmap::iterator itr;
1673
1674    bool doAll = false;
1675
1676    if (id.compare("all") == 0) {
1677        itr = _colorMaps.begin();
1678        doAll = true;
1679    } else {
1680        itr = _colorMaps.find(id);
1681    }
1682
1683    if (itr == _colorMaps.end()) {
1684        ERROR("Unknown ColorMap %s", id.c_str());
1685        return;
1686    }
1687
1688    do {
1689        if (itr->second->getName().compare("default") == 0 ||
1690            itr->second->getName().compare("grayDefault") == 0 ||
1691            itr->second->getName().compare("volumeDefault") == 0 ||
1692            itr->second->getName().compare("elementDefault") == 0) {
1693            if (id.compare("all") != 0) {
1694                WARN("Cannot delete a default color map");
1695            }
1696            continue;
1697        } else if (colorMapUsed(itr->second)) {
1698            WARN("Cannot delete color map '%s', it is in use", itr->second->getName().c_str());
1699            continue;
1700        }
1701
1702        TRACE("Deleting ColorMap %s", itr->second->getName().c_str());
1703
1704        delete itr->second;
1705        itr = _colorMaps.erase(itr);
1706    } while (doAll && itr != _colorMaps.end());
1707}
1708
1709/**
1710 * \brief Set the number of discrete colors used in the colormap's lookup table
1711 *
1712 * Note that the number of table entries is independent of the number of
1713 * control points in the color/alpha ramp
1714 */
1715void Renderer::setColorMapNumberOfTableEntries(const ColorMapId& id, int numEntries)
1716{
1717    ColorMapHashmap::iterator itr;
1718
1719    bool doAll = false;
1720
1721    if (id.compare("all") == 0) {
1722        itr = _colorMaps.begin();
1723        doAll = true;
1724    } else {
1725        itr = _colorMaps.find(id);
1726    }
1727
1728    if (itr == _colorMaps.end()) {
1729        ERROR("Unknown ColorMap %s", id.c_str());
1730        return;
1731    }
1732
1733    if (numEntries < 0) {
1734        numEntries = 256;
1735        TRACE("Setting numEntries to default value of %d", numEntries);
1736    }
1737
1738    do {
1739        if (itr->second->getName() == "elementDefault") {
1740            TRACE("Can't change number of table entries for default element color map");
1741        } else {
1742            itr->second->setNumberOfTableEntries(numEntries);
1743            updateColorMap(itr->second);
1744        }
1745    } while (doAll && ++itr != _colorMaps.end());
1746
1747    _needsRedraw = true;
1748}
1749
1750/**
1751 * \brief Render a labelled legend image for the given colormap
1752 *
1753 * The field is assumed to be the active scalar or vector field
1754 * based on the legendType.
1755 *
1756 * \param[in] id ColorMap name
1757 * \param[in] dataSetID DataSet name
1758 * \param[in] legendType scalar or vector field legend
1759 * \param[in,out] title If supplied, draw title ("#auto" means to
1760 * fill in field name and draw).  If blank, do not draw title. 
1761 * If title was blank or "#auto", will be filled with field name on
1762 * return
1763 * \param[in,out] range Data range to use in legend.  Set min > max to have
1764 * range computed, will be filled with valid min and max values
1765 * \param[in] width Pixel width of legend (aspect controls orientation)
1766 * \param[in] height Pixel height of legend (aspect controls orientation)
1767 * \param[in] opaque Flag to control if legend is rendered opaque or translucent
1768 * \param[in] numLabels Number of labels to render (includes min/max)
1769 * \param[in,out] imgData Pointer to array to fill with image bytes. Array
1770 * will be resized if needed.
1771 * \return The image is rendered into the supplied array, false is
1772 * returned if the color map is not found
1773 */
1774bool Renderer::renderColorMap(const ColorMapId& id,
1775                              const DataSetId& dataSetID,
1776                              Renderer::LegendType legendType,
1777                              std::string& title,
1778                              double range[2],
1779                              int width, int height,
1780                              bool opaque,
1781                              int numLabels,
1782                              vtkUnsignedCharArray *imgData)
1783{
1784    DataSet *dataSet = NULL;
1785    if (dataSetID.compare("all") == 0) {
1786        if (_dataSets.empty()) {
1787            WARN("No DataSets exist, can't fill title or range");
1788            return renderColorMap(id, dataSetID, legendType,
1789                                  NULL,
1790                                  DataSet::POINT_DATA,
1791                                  title, range, width, height, opaque, numLabels, imgData);
1792        } else {
1793            dataSet = _dataSets.begin()->second;
1794        }
1795    } else {
1796        dataSet = getDataSet(dataSetID);
1797        if (dataSet == NULL) {
1798            ERROR("DataSet '%s' not found", dataSetID.c_str());
1799            return false;
1800        }
1801    }
1802
1803    if (legendType == LEGEND_SCALAR) {
1804        return renderColorMap(id, dataSetID, legendType,
1805                              dataSet->getActiveScalarsName(),
1806                              dataSet->getActiveScalarsType(),
1807                              title, range, width, height, opaque, numLabels, imgData);
1808    } else {
1809        return renderColorMap(id, dataSetID, legendType,
1810                              dataSet->getActiveVectorsName(),
1811                              dataSet->getActiveVectorsType(),
1812                              title, range, width, height, opaque, numLabels, imgData);
1813    }
1814}
1815
1816/**
1817 * \brief Render a labelled legend image for the given colormap
1818 *
1819 * The field is assumed to be point data, if the field is not found
1820 * as point data, cell data is used.
1821 *
1822 * \param[in] id ColorMap name
1823 * \param[in] dataSetID DataSet name
1824 * \param[in] legendType scalar or vector field legend
1825 * \param[in] fieldName Name of the field array this legend is for
1826 * \param[in,out] title If supplied, draw title ("#auto" means to
1827 * fill in field name and draw).  If blank, do not draw title. 
1828 * If title was blank or "#auto", will be filled with field name on
1829 * return
1830 * \param[in,out] range Data range to use in legend.  Set min > max to have
1831 * range computed, will be filled with valid min and max values
1832 * \param[in] width Pixel width of legend (aspect controls orientation)
1833 * \param[in] height Pixel height of legend (aspect controls orientation)
1834 * \param[in] opaque Flag to control if legend is rendered opaque or translucent
1835 * \param[in] numLabels Number of labels to render (includes min/max)
1836 * \param[in,out] imgData Pointer to array to fill with image bytes. Array
1837 * will be resized if needed.
1838 * \return The image is rendered into the supplied array, false is
1839 * returned if the color map is not found
1840 */
1841bool Renderer::renderColorMap(const ColorMapId& id,
1842                              const DataSetId& dataSetID,
1843                              Renderer::LegendType legendType,
1844                              const char *fieldName,
1845                              std::string& title,
1846                              double range[2],
1847                              int width, int height,
1848                              bool opaque,
1849                              int numLabels,
1850                              vtkUnsignedCharArray *imgData)
1851{
1852    DataSet *dataSet = NULL;
1853    if (dataSetID.compare("all") == 0) {
1854        if (_dataSets.empty()) {
1855            WARN("No DataSets exist, can't fill title or range");
1856            return renderColorMap(id, dataSetID, legendType,
1857                                  NULL,
1858                                  DataSet::POINT_DATA,
1859                                  title, range, width, height, opaque, numLabels, imgData);
1860        } else {
1861            dataSet = _dataSets.begin()->second;
1862        }
1863    } else {
1864        dataSet = getDataSet(dataSetID);
1865        if (dataSet == NULL) {
1866            ERROR("DataSet '%s' not found", dataSetID.c_str());
1867            return false;
1868        }
1869    }
1870
1871    DataSet::DataAttributeType attrType;
1872    int numComponents;
1873
1874    dataSet->getFieldInfo(fieldName, &attrType, &numComponents);
1875
1876    return renderColorMap(id, dataSetID, legendType,
1877                          fieldName,
1878                          attrType,
1879                          title, range, width, height, opaque, numLabels, imgData);
1880}
1881
1882/**
1883 * \brief Render a labelled legend image for the given colormap
1884 *
1885 * \param[in] id ColorMap name
1886 * \param[in] dataSetID DataSet name
1887 * \param[in] legendType scalar or vector field legend
1888 * \param[in] fieldName Name of the field array this legend is for
1889 * \param[in] type DataAttributeType of the field
1890 * \param[in,out] title If supplied, draw title ("#auto" means to
1891 * fill in field name and draw).  If blank, do not draw title. 
1892 * If title was blank or "#auto", will be filled with field name on
1893 * return
1894 * \param[in,out] range Data range to use in legend.  Set min > max to have
1895 * range computed, will be filled with valid min and max values
1896 * \param[in] width Pixel width of legend (aspect controls orientation)
1897 * \param[in] height Pixel height of legend (aspect controls orientation)
1898 * \param[in] opaque Flag to control if legend is rendered opaque or translucent
1899 * \param[in] numLabels Number of labels to render (includes min/max)
1900 * \param[in,out] imgData Pointer to array to fill with image bytes. Array
1901 * will be resized if needed.
1902 * \return The image is rendered into the supplied array, false is
1903 * returned if the color map is not found
1904 */
1905bool Renderer::renderColorMap(const ColorMapId& id,
1906                              const DataSetId& dataSetID,
1907                              Renderer::LegendType legendType,
1908                              const char *fieldName,
1909                              DataSet::DataAttributeType type,
1910                              std::string& title,
1911                              double range[2],
1912                              int width, int height,
1913                              bool opaque,
1914                              int numLabels,
1915                              vtkUnsignedCharArray *imgData)
1916{
1917    TRACE("Enter");
1918    ColorMap *colorMap = getColorMap(id);
1919    if (colorMap == NULL)
1920        return false;
1921#ifdef LEGEND_SOFTWARE_RENDER
1922    ColorMap::renderColorMap(colorMap, width, height, imgData, opaque, _bgColor,
1923#ifdef RENDER_TARGA
1924                             true, TARGA_BYTES_PER_PIXEL
1925#else
1926                             false
1927#endif
1928                             );
1929#else
1930    if (_legendRenderWindow == NULL) {
1931        _legendRenderWindow = vtkSmartPointer<vtkRenderWindow>::New();
1932        _legendRenderWindow->SetMultiSamples(0);
1933#ifdef USE_OFFSCREEN_RENDERING
1934        _legendRenderWindow->DoubleBufferOff();
1935        _legendRenderWindow->OffScreenRenderingOn();
1936#else
1937        _legendRenderWindow->DoubleBufferOn();
1938        _legendRenderWindow->SwapBuffersOff();
1939#endif
1940    }
1941
1942    _legendRenderWindow->SetSize(width, height);
1943
1944    if (_legendRenderer == NULL) {
1945        _legendRenderer = vtkSmartPointer<vtkRenderer>::New();
1946        _legendRenderWindow->AddRenderer(_legendRenderer);
1947    }
1948    _legendRenderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
1949
1950    if (_scalarBarActor == NULL) {
1951        _scalarBarActor = vtkSmartPointer<vtkScalarBarActor>::New();
1952        _scalarBarActor->DrawFrameOff();
1953        _scalarBarActor->DrawBackgroundOff();
1954        _legendRenderer->AddViewProp(_scalarBarActor);
1955    }
1956
1957    if (opaque) {
1958        _scalarBarActor->UseOpacityOff();
1959    } else {
1960        _scalarBarActor->UseOpacityOn();
1961    }
1962
1963    if (width > height) {
1964        _scalarBarActor->SetOrientationToHorizontal();
1965    } else {
1966        _scalarBarActor->SetOrientationToVertical();
1967    }
1968
1969    // Set viewport-relative width/height/pos
1970    if (title.empty() && numLabels == 0) {
1971#ifdef NEW_SCALAR_BAR
1972        _scalarBarActor->SetBarRatio(1);
1973        _scalarBarActor->SetTitleRatio(0);
1974#endif
1975        if (width > height) {
1976            // horizontal
1977#ifdef NEW_SCALAR_BAR
1978            _scalarBarActor->SetDisplayPosition(0, 0);
1979            _scalarBarActor->GetPosition2Coordinate()->SetCoordinateSystemToDisplay();
1980            _scalarBarActor->GetPosition2Coordinate()->SetValue(width+4, height);
1981#else
1982            _scalarBarActor->SetPosition(0, 0);
1983            _scalarBarActor->SetHeight((((double)height+1.5)/((double)height))/0.4); // VTK: floor(actorHeight * .4)
1984            _scalarBarActor->SetWidth(1); // VTK: actorWidth
1985#endif
1986        } else {
1987            // vertical
1988#ifdef NEW_SCALAR_BAR
1989            _scalarBarActor->SetDisplayPosition(0, -4);
1990            _scalarBarActor->GetPosition2Coordinate()->SetCoordinateSystemToDisplay();
1991            _scalarBarActor->GetPosition2Coordinate()->SetValue(width+1, height+5);
1992#else
1993            _scalarBarActor->SetPosition(0, 0);
1994            _scalarBarActor->SetHeight((((double)height+1.5)/((double)height))/0.86); // VTK: floor(actorHeight * .86)
1995            _scalarBarActor->SetWidth(((double)(width+5))/((double)width)); // VTK: actorWidth - 4 pixels
1996#endif
1997        }
1998    } else {
1999#ifdef NEW_SCALAR_BAR
2000        _scalarBarActor->SetBarRatio(0.375);
2001        _scalarBarActor->SetTitleRatio(0.5);
2002        _scalarBarActor->SetDisplayPosition(0, 0);
2003        _scalarBarActor->GetPosition2Coordinate()->SetCoordinateSystemToDisplay();
2004        _scalarBarActor->GetPosition2Coordinate()->SetValue(width, height);
2005#else
2006        if (width > height) {
2007            // horizontal
2008            _scalarBarActor->SetPosition(.075, .1);
2009            _scalarBarActor->SetHeight(0.8);
2010            _scalarBarActor->SetWidth(0.85);
2011        } else {
2012            // vertical
2013            _scalarBarActor->SetPosition(.1, .05);
2014            _scalarBarActor->SetHeight(0.9);
2015            _scalarBarActor->SetWidth(0.8);
2016        }
2017#endif
2018    }
2019
2020    vtkSmartPointer<vtkLookupTable> lut = colorMap->getLookupTable();
2021    DataSet *dataSet = NULL;
2022    bool cumulative = _useCumulativeRange;
2023    if (dataSetID.compare("all") == 0) {
2024        if (_dataSets.empty()) {
2025            WARN("No DataSets exist, can't fill title or range");
2026        } else {
2027            dataSet = _dataSets.begin()->second;
2028        }
2029        cumulative = true;
2030    } else {
2031        dataSet = getDataSet(dataSetID);
2032        if (dataSet == NULL) {
2033            ERROR("DataSet '%s' not found", dataSetID.c_str());
2034            return false;
2035        }
2036    }
2037
2038    bool drawTitle = false;
2039    if (!title.empty()) {
2040        drawTitle = true;
2041        if (title.compare("#auto") == 0) {
2042            title.clear();
2043        }
2044    }
2045
2046    bool needRange = false;
2047    if (range[0] > range[1]) {
2048        range[0] = 0.0;
2049        range[1] = 1.0;
2050        needRange = true;
2051    }
2052
2053    switch (legendType) {
2054    case LEGEND_VECTOR_MAGNITUDE:
2055        if (needRange) {
2056            if (cumulative) {
2057                getCumulativeDataRange(range, fieldName, type, 3);
2058            } else if (dataSet != NULL) {
2059                dataSet->getDataRange(range, fieldName, type);
2060            }
2061        }
2062
2063        lut->SetRange(range);
2064
2065        if (title.empty() && dataSet != NULL) {
2066            if (fieldName != NULL) {
2067                title = fieldName;
2068                title.append("(mag)");
2069            }
2070        }
2071        break;
2072    case LEGEND_VECTOR_X:
2073        if (needRange) {
2074            if (cumulative) {
2075                getCumulativeDataRange(range, fieldName, type, 3, 0);
2076            } else if (dataSet != NULL) {
2077                dataSet->getDataRange(range, fieldName, type, 0);
2078            }
2079        }
2080
2081        lut->SetRange(range);
2082
2083        if (title.empty() && dataSet != NULL) {
2084            if (fieldName != NULL) {
2085                title = fieldName;
2086                title.append("(x)");
2087            }
2088        }
2089        break;
2090    case LEGEND_VECTOR_Y:
2091        if (needRange) {
2092            if (cumulative) {
2093                getCumulativeDataRange(range, fieldName, type, 3, 1);
2094            } else if (dataSet != NULL) {
2095                dataSet->getDataRange(range, fieldName, type, 1);
2096            }
2097        }
2098
2099        lut->SetRange(range);
2100
2101        if (title.empty() && dataSet != NULL) {
2102            if (fieldName != NULL) {
2103                title = fieldName;
2104                title.append("(y)");
2105            }
2106        }
2107        break;
2108    case LEGEND_VECTOR_Z:
2109        if (needRange) {
2110            if (cumulative) {
2111                getCumulativeDataRange(range, fieldName, type, 3, 2);
2112            } else if (dataSet != NULL) {
2113                dataSet->getDataRange(range, fieldName, type, 1);
2114            }
2115        }
2116
2117        lut->SetRange(range);
2118
2119        if (title.empty() && dataSet != NULL) {
2120            if (fieldName != NULL) {
2121                title = fieldName;
2122                title.append("(z)");
2123            }
2124        }
2125        break;
2126    case LEGEND_SCALAR:
2127    default:
2128        if (needRange) {
2129            if (cumulative) {
2130                getCumulativeDataRange(range, fieldName, type, 1);
2131            } else if (dataSet != NULL) {
2132                dataSet->getDataRange(range, fieldName, type);
2133            }
2134        }
2135
2136        lut->SetRange(range);
2137
2138        if (title.empty() && dataSet != NULL) {
2139            if (fieldName != NULL)
2140                title = fieldName;
2141        }
2142        break;
2143    }
2144
2145    _scalarBarActor->SetLookupTable(lut);
2146    _scalarBarActor->SetMaximumNumberOfColors((width > height ? width : height));
2147
2148    if (drawTitle) {
2149        _scalarBarActor->SetTitle(title.c_str());
2150    } else {
2151        _scalarBarActor->SetTitle("");
2152    }
2153
2154    double color[3];
2155    color[0] = 1 - _bgColor[0];
2156    color[1] = 1 - _bgColor[1];
2157    color[2] = 1 - _bgColor[2];
2158
2159    _scalarBarActor->GetTitleTextProperty()->SetColor(color);
2160    _scalarBarActor->GetTitleTextProperty()->BoldOff();
2161    _scalarBarActor->GetTitleTextProperty()->ItalicOff();
2162    _scalarBarActor->GetTitleTextProperty()->ShadowOff();
2163    _scalarBarActor->SetNumberOfLabels(numLabels);
2164    _scalarBarActor->GetLabelTextProperty()->SetColor(color);
2165    _scalarBarActor->GetLabelTextProperty()->BoldOff();
2166    _scalarBarActor->GetLabelTextProperty()->ItalicOff();
2167    _scalarBarActor->GetLabelTextProperty()->ShadowOff();
2168#ifdef NEW_SCALAR_BAR
2169    if (!drawTitle && numLabels == 0) {
2170        _scalarBarActor->DrawAnnotationsOff();
2171        _scalarBarActor->SetAnnotationLeaderPadding(0);
2172        _scalarBarActor->SetTextPad(0);
2173    } else {
2174        _scalarBarActor->DrawAnnotationsOn();
2175        _scalarBarActor->SetAnnotationLeaderPadding(8);
2176        _scalarBarActor->SetTextPad(1);
2177    }
2178#endif
2179
2180    _legendRenderWindow->Render();
2181    int *sz = _legendRenderWindow->GetSize();
2182    if (sz[0] != width || sz[1] != height) {
2183        ERROR("Window size: %dx%d, but expected %dx%d", sz[0], sz[1], width, height);
2184    }
2185
2186#ifdef RENDER_TARGA
2187    _legendRenderWindow->MakeCurrent();
2188    // Must clear previous errors first.
2189    while (glGetError() != GL_NO_ERROR){
2190        ;
2191    }
2192    int bytesPerPixel = TARGA_BYTES_PER_PIXEL;
2193    int size = bytesPerPixel * width * height;
2194
2195    if (imgData->GetMaxId() + 1 != size)
2196    {
2197        imgData->SetNumberOfComponents(bytesPerPixel);
2198        imgData->SetNumberOfValues(size);
2199    }
2200    glDisable(GL_TEXTURE_2D);
2201    if (_legendRenderWindow->GetDoubleBuffer()) {
2202        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_legendRenderWindow)->GetBackLeftBuffer()));
2203    } else {
2204        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_legendRenderWindow)->GetFrontLeftBuffer()));
2205    }
2206    glPixelStorei(GL_PACK_ALIGNMENT, 1);
2207    if (bytesPerPixel == 4) {
2208        glReadPixels(0, 0, width, height, GL_BGRA,
2209                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
2210    } else {
2211        glReadPixels(0, 0, width, height, GL_BGR,
2212                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
2213    }
2214    if (glGetError() != GL_NO_ERROR) {
2215        ERROR("glReadPixels");
2216    }
2217#else
2218    _legendRenderWindow->GetPixelData(0, 0, width-1, height-1,
2219                                      !_legendRenderWindow->GetDoubleBuffer(),
2220                                      imgData);
2221#endif
2222#endif
2223    TRACE("Leave");
2224    return true;
2225}
2226
2227bool Renderer::renderColorMap(const ColorMapId& id,
2228                              int width, int height,
2229                              bool opaque,
2230                              vtkUnsignedCharArray *imgData)
2231{
2232    TRACE("Enter");
2233    ColorMap *colorMap = getColorMap(id);
2234    if (colorMap == NULL)
2235        return false;
2236#ifdef LEGEND_SOFTWARE_RENDER
2237    ColorMap::renderColorMap(colorMap, width, height, imgData, opaque, _bgColor,
2238#ifdef RENDER_TARGA
2239                             true, TARGA_BYTES_PER_PIXEL
2240#else
2241                             false
2242#endif
2243                             );
2244#else
2245    if (_legendRenderWindow == NULL) {
2246        _legendRenderWindow = vtkSmartPointer<vtkRenderWindow>::New();
2247        _legendRenderWindow->SetMultiSamples(0);
2248#ifdef USE_OFFSCREEN_RENDERING
2249        _legendRenderWindow->DoubleBufferOff();
2250        _legendRenderWindow->OffScreenRenderingOn();
2251#else
2252        _legendRenderWindow->DoubleBufferOn();
2253        _legendRenderWindow->SwapBuffersOff();
2254#endif
2255    }
2256
2257    _legendRenderWindow->SetSize(width, height);
2258
2259    if (_legendRenderer == NULL) {
2260        _legendRenderer = vtkSmartPointer<vtkRenderer>::New();
2261        _legendRenderWindow->AddRenderer(_legendRenderer);
2262    }
2263    _legendRenderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
2264
2265    if (_scalarBarActor == NULL) {
2266        _scalarBarActor = vtkSmartPointer<vtkScalarBarActor>::New();
2267        _scalarBarActor->DrawFrameOff();
2268        _scalarBarActor->DrawBackgroundOff();
2269        _scalarBarActor->DrawColorBarOn();
2270        _legendRenderer->AddViewProp(_scalarBarActor);
2271    }
2272
2273    if (opaque) {
2274        _scalarBarActor->UseOpacityOff();
2275    } else {
2276        _scalarBarActor->UseOpacityOn();
2277    }
2278
2279    if (width > height) {
2280        _scalarBarActor->SetOrientationToHorizontal();
2281    } else {
2282        _scalarBarActor->SetOrientationToVertical();
2283    }
2284
2285    // Set viewport-relative width/height/pos
2286#ifdef NEW_SCALAR_BAR
2287    _scalarBarActor->SetBarRatio(1);
2288    _scalarBarActor->SetTitleRatio(0);
2289#endif
2290    if (width > height) {
2291        // horizontal
2292#ifdef NEW_SCALAR_BAR
2293        _scalarBarActor->SetDisplayPosition(0, 0);
2294        _scalarBarActor->GetPosition2Coordinate()->SetCoordinateSystemToDisplay();
2295        _scalarBarActor->GetPosition2Coordinate()->SetValue(width+4, height);
2296#else
2297        _scalarBarActor->SetPosition(0, 0);
2298        _scalarBarActor->SetHeight((((double)height+1.5)/((double)height))/0.4); // VTK: floor(actorHeight * .4)
2299        _scalarBarActor->SetWidth(1); // VTK: actorWidth
2300#endif
2301    } else {
2302        // vertical
2303#ifdef NEW_SCALAR_BAR
2304        _scalarBarActor->SetDisplayPosition(0, -4);
2305        _scalarBarActor->GetPosition2Coordinate()->SetCoordinateSystemToDisplay();
2306        _scalarBarActor->GetPosition2Coordinate()->SetValue(width+1, height+5);
2307#else
2308        _scalarBarActor->SetPosition(0, 0);
2309        _scalarBarActor->SetHeight((((double)height+1.5)/((double)height))/0.86); // VTK: floor(actorHeight * .86)
2310        _scalarBarActor->SetWidth(((double)(width+5))/((double)width)); // VTK: actorWidth - 4 pixels
2311#endif
2312    }
2313
2314    vtkSmartPointer<vtkLookupTable> lut = colorMap->getLookupTable();
2315
2316    double range[2];
2317    range[0] = 0.0;
2318    range[1] = 1.0;
2319
2320    lut->SetRange(range);
2321
2322    _scalarBarActor->SetLookupTable(lut);
2323    _scalarBarActor->SetMaximumNumberOfColors((width > height ? width : height));
2324    _scalarBarActor->SetTitle("");
2325    _scalarBarActor->SetNumberOfLabels(0);
2326#ifdef NEW_SCALAR_BAR
2327    _scalarBarActor->DrawAnnotationsOff();
2328    _scalarBarActor->SetAnnotationLeaderPadding(0);
2329    _scalarBarActor->SetTextPad(0);
2330#endif
2331
2332    _legendRenderWindow->Render();
2333    int *sz = _legendRenderWindow->GetSize();
2334    if (sz[0] != width || sz[1] != height) {
2335        ERROR("Window size: %dx%d, but expected %dx%d", sz[0], sz[1], width, height);
2336    }
2337
2338#ifdef RENDER_TARGA
2339    _legendRenderWindow->MakeCurrent();
2340    // Must clear previous errors first.
2341    while (glGetError() != GL_NO_ERROR){
2342        ;
2343    }
2344    int bytesPerPixel = TARGA_BYTES_PER_PIXEL;
2345    int size = bytesPerPixel * width * height;
2346
2347    if (imgData->GetMaxId() + 1 != size)
2348    {
2349        imgData->SetNumberOfComponents(bytesPerPixel);
2350        imgData->SetNumberOfValues(size);
2351    }
2352    glDisable(GL_TEXTURE_2D);
2353    if (_legendRenderWindow->GetDoubleBuffer()) {
2354        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_legendRenderWindow)->GetBackLeftBuffer()));
2355    } else {
2356        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_legendRenderWindow)->GetFrontLeftBuffer()));
2357    }
2358    glPixelStorei(GL_PACK_ALIGNMENT, 1);
2359    if (bytesPerPixel == 4) {
2360        glReadPixels(0, 0, width, height, GL_BGRA,
2361                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
2362    } else {
2363        glReadPixels(0, 0, width, height, GL_BGR,
2364                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
2365    }
2366    if (glGetError() != GL_NO_ERROR) {
2367        ERROR("glReadPixels");
2368    }
2369#else
2370    _legendRenderWindow->GetPixelData(0, 0, width-1, height-1,
2371                                      !_legendRenderWindow->GetDoubleBuffer(),
2372                                      imgData);
2373#endif
2374#endif
2375    TRACE("Leave");
2376    return true;
2377}
2378
2379/**
2380 * \brief Set camera FOV based on render window height
2381 *
2382 * Computes a field-of-view angle based on some assumptions about
2383 * viewer's distance to screen and pixel density
2384 */
2385void Renderer::setViewAngle(int height)
2386{
2387    // Distance of eyes from screen in inches
2388    double d = 20.0;
2389    // Assume 72 ppi screen
2390    double h = (double)height / 72.0;
2391
2392    double angle = vtkMath::DegreesFromRadians(2.0 * atan((h/2.0)/d));
2393    _renderer->GetActiveCamera()->SetViewAngle(angle);
2394
2395    TRACE("Setting view angle: %g", angle);
2396}
2397
2398/**
2399 * \brief Resize the render window (image size for renderings)
2400 */
2401void Renderer::setWindowSize(int width, int height)
2402{
2403    if (_windowWidth == width &&
2404        _windowHeight == height) {
2405        TRACE("No change");
2406        return;
2407    }
2408
2409    TRACE("Setting window size to %dx%d", width, height);
2410
2411    //setViewAngle(height);
2412
2413    // FIXME: Fix up panning on aspect change
2414#ifdef notdef
2415    if (_cameraPan[0] != 0.0) {
2416        _cameraPan[0] *= ((double)_windowWidth / width);
2417    }
2418    if (_cameraPan[1] != 0.0) {
2419        _cameraPan[1] *= ((double)_windowHeight / height);
2420    }
2421#endif
2422    _windowWidth = width;
2423    _windowHeight = height;
2424    _renderWindow->SetSize(_windowWidth, _windowHeight);
2425
2426    if (_cameraMode == IMAGE) {
2427        if (_cameraAspect == ASPECT_WINDOW) {
2428            double imgWindowAspect = getImageCameraAspect();
2429            TRACE("Setting object aspect to %g", imgWindowAspect);
2430            setObjectAspects(imgWindowAspect);
2431            initCamera();
2432        } else {
2433            if (_userImgWorldDims[0] > 0) {
2434                _setCameraZoomRegion(_userImgWorldOrigin[0],
2435                                     _userImgWorldOrigin[1],
2436                                     _userImgWorldDims[0],
2437                                     _userImgWorldDims[1]);
2438            } else {
2439                if (isCameraMaximized()) {
2440                    initCamera();
2441                } else {
2442                    _setCameraZoomRegion(_imgWorldOrigin[0],
2443                                         _imgWorldOrigin[1],
2444                                         _imgWorldDims[0],
2445                                         _imgWorldDims[1]);
2446                }
2447            }
2448        }
2449    }
2450    _needsRedraw = true;
2451}
2452
2453void Renderer::setObjectAspects(double aspectRatio)
2454{
2455    setGraphicsObjectAspect<Arc>(aspectRatio);
2456    setGraphicsObjectAspect<Arrow>(aspectRatio);
2457    setGraphicsObjectAspect<Box>(aspectRatio);
2458    setGraphicsObjectAspect<Cone>(aspectRatio);
2459    setGraphicsObjectAspect<Contour2D>(aspectRatio);
2460    setGraphicsObjectAspect<Contour3D>(aspectRatio);
2461    setGraphicsObjectAspect<Cutplane>(aspectRatio);
2462    setGraphicsObjectAspect<Cylinder>(aspectRatio);
2463    setGraphicsObjectAspect<Disk>(aspectRatio);
2464    setGraphicsObjectAspect<Glyphs>(aspectRatio);
2465    setGraphicsObjectAspect<HeightMap>(aspectRatio);
2466    setGraphicsObjectAspect<Image>(aspectRatio);
2467    setGraphicsObjectAspect<LIC>(aspectRatio);
2468    setGraphicsObjectAspect<Line>(aspectRatio);
2469    setGraphicsObjectAspect<Molecule>(aspectRatio);
2470    setGraphicsObjectAspect<Outline>(aspectRatio);
2471    setGraphicsObjectAspect<Parallelepiped>(aspectRatio);
2472    setGraphicsObjectAspect<PolyData>(aspectRatio);
2473    setGraphicsObjectAspect<Polygon>(aspectRatio);
2474    setGraphicsObjectAspect<PseudoColor>(aspectRatio);
2475    setGraphicsObjectAspect<Sphere>(aspectRatio);
2476    setGraphicsObjectAspect<Streamlines>(aspectRatio);
2477    setGraphicsObjectAspect<Text3D>(aspectRatio);
2478    setGraphicsObjectAspect<Volume>(aspectRatio);
2479    setGraphicsObjectAspect<Warp>(aspectRatio);
2480}
2481
2482void Renderer::setCameraAspect(Aspect aspect)
2483{
2484    //if (_cameraAspect == aspect)
2485    //    return;
2486
2487    _cameraAspect = aspect;
2488
2489    double aspectRatio = 0.0;
2490    switch (aspect) {
2491    case ASPECT_SQUARE:
2492        aspectRatio = 1.0;
2493        break;
2494    case ASPECT_WINDOW:
2495        aspectRatio = 1.0;
2496        if (_cameraMode == IMAGE) {
2497            aspectRatio = getImageCameraAspect();
2498        }
2499        break;
2500    case ASPECT_NATIVE:
2501    default:
2502        aspectRatio = 0.0;
2503    }
2504
2505    setObjectAspects(aspectRatio);
2506
2507    if (_cameraMode == IMAGE)
2508        _needsCameraReset = true;
2509    _needsRedraw = true;
2510}
2511
2512/**
2513 * \brief Change the camera type: perspective, orthographic or image view
2514 *
2515 * Perspective mode is a normal 3D camera.
2516 *
2517 * Orthogrphic mode is parallel projection.
2518 *
2519 * Image mode is an orthographic camera with fixed axes and a clipping region
2520 * around the plot area, use _setCameraZoomRegion to control the displayed area
2521 *
2522 * \param[in] mode Enum specifying camera type
2523 */
2524void Renderer::setCameraMode(CameraMode mode)
2525{
2526    if (_cameraMode == mode) return;
2527
2528    CameraMode origMode = _cameraMode;
2529    _cameraMode = mode;
2530    resetAxes();
2531
2532    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2533    switch (mode) {
2534    case ORTHO: {
2535        TRACE("Set camera to Ortho mode");
2536        camera->ParallelProjectionOn();
2537        if (origMode == IMAGE) {
2538            resetCamera(true);
2539        }
2540        break;
2541    }
2542    case PERSPECTIVE: {
2543        TRACE("Set camera to Perspective mode");
2544        camera->ParallelProjectionOff();
2545        if (origMode == IMAGE) {
2546            resetCamera(true);
2547        }
2548        break;
2549    }
2550    case IMAGE: {
2551        camera->ParallelProjectionOn();
2552        _setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
2553                             _imgWorldDims[0],_imgWorldDims[1]);
2554        TRACE("Set camera to Image mode");
2555        break;
2556    }
2557    default:
2558        ERROR("Unkown camera mode: %d", mode);
2559    }
2560    _needsRedraw = true;
2561}
2562
2563/**
2564 * \brief Get the current camera mode
2565 */
2566Renderer::CameraMode Renderer::getCameraMode() const
2567{
2568    return _cameraMode;
2569}
2570
2571/**
2572 * \brief Set the VTK camera parameters based on a 4x4 view matrix
2573 */
2574void Renderer::setCameraFromMatrix(vtkCamera *camera, vtkMatrix4x4& mat)
2575{
2576    double d = camera->GetDistance();
2577    double vu[3];
2578    vu[0] = mat[1][0];
2579    vu[1] = mat[1][1];
2580    vu[2] = mat[1][2];
2581    double trans[3];
2582    trans[0] = mat[0][3];
2583    trans[1] = mat[1][3];
2584    trans[2] = mat[2][3];
2585    mat[0][3] = 0;
2586    mat[1][3] = 0;
2587    mat[2][3] = 0;
2588    double vpn[3] = {mat[2][0], mat[2][1], mat[2][2]};
2589    double pos[3];
2590    // With translation removed, we have an orthogonal matrix,
2591    // so the inverse is the transpose
2592    mat.Transpose();
2593    mat.MultiplyPoint(trans, pos);
2594    pos[0] = -pos[0];
2595    pos[1] = -pos[1];
2596    pos[2] = -pos[2];
2597    double fp[3];
2598    fp[0] = pos[0] - vpn[0] * d;
2599    fp[1] = pos[1] - vpn[1] * d;
2600    fp[2] = pos[2] - vpn[2] * d;
2601    camera->SetPosition(pos);
2602    camera->SetFocalPoint(fp);
2603    camera->SetViewUp(vu);
2604}
2605
2606/**
2607 * \brief Set the orientation of the camera from a quaternion
2608 * rotation
2609 *
2610 * \param[in] quat A quaternion with scalar part first: w,x,y,z
2611 * \param[in] absolute Is rotation absolute or relative?
2612 */
2613void Renderer::setCameraOrientation(const double quat[4], bool absolute)
2614{
2615    if (_cameraMode == IMAGE)
2616        return;
2617    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2618    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
2619    vtkSmartPointer<vtkMatrix4x4> rotMat = vtkSmartPointer<vtkMatrix4x4>::New();
2620
2621    double q[4];
2622    copyQuat(quat, q);
2623
2624    if (absolute) {
2625        double abs[4];
2626        // Save absolute rotation
2627        copyQuat(q, abs);
2628        // Compute relative rotation
2629        quatMult(quatReciprocal(_cameraOrientation), q, q);
2630        // Store absolute rotation
2631        copyQuat(abs, _cameraOrientation);
2632    } else {
2633        // Compute new absolute rotation
2634        quatMult(_cameraOrientation, q, _cameraOrientation);
2635    }
2636
2637    quaternionToTransposeMatrix4x4(q, *rotMat);
2638#ifdef DEBUG
2639    TRACE("Rotation matrix:\n %g %g %g\n %g %g %g\n %g %g %g",
2640          (*rotMat)[0][0], (*rotMat)[0][1], (*rotMat)[0][2],
2641          (*rotMat)[1][0], (*rotMat)[1][1], (*rotMat)[1][2],
2642          (*rotMat)[2][0], (*rotMat)[2][1], (*rotMat)[2][2]);
2643    vtkSmartPointer<vtkMatrix4x4> camMat = vtkSmartPointer<vtkMatrix4x4>::New();
2644    camMat->DeepCopy(camera->GetViewTransformMatrix());
2645    TRACE("Camera matrix:\n %g %g %g %g\n %g %g %g %g\n %g %g %g %g\n %g %g %g %g",
2646          (*camMat)[0][0], (*camMat)[0][1], (*camMat)[0][2], (*camMat)[0][3],
2647          (*camMat)[1][0], (*camMat)[1][1], (*camMat)[1][2], (*camMat)[1][3],
2648          (*camMat)[2][0], (*camMat)[2][1], (*camMat)[2][2], (*camMat)[2][3],
2649          (*camMat)[3][0], (*camMat)[3][1], (*camMat)[3][2], (*camMat)[3][3]);
2650    printCameraInfo(camera);
2651#endif
2652    trans->Translate(0, 0, -camera->GetDistance());
2653    trans->Concatenate(rotMat);
2654    trans->Translate(0, 0, camera->GetDistance());
2655    trans->Concatenate(camera->GetViewTransformMatrix());
2656    setCameraFromMatrix(camera, *trans->GetMatrix());
2657
2658    resetCameraClippingRange();
2659    printCameraInfo(camera);
2660    _needsRedraw = true;
2661}
2662
2663/**
2664 * \brief Set the position and orientation of the camera
2665 *
2666 * \param[in] position x,y,z position of camera in world coordinates
2667 * \param[in] focalPoint x,y,z look-at point in world coordinates
2668 * \param[in] viewUp x,y,z up vector of camera
2669 */
2670void Renderer::setCameraOrientationAndPosition(const double position[3],
2671                                               const double focalPoint[3],
2672                                               const double viewUp[3])
2673{
2674    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2675    camera->SetPosition(position);
2676    camera->SetFocalPoint(focalPoint);
2677    camera->SetViewUp(viewUp);
2678    resetCameraClippingRange();
2679    _needsRedraw = true;
2680}
2681
2682/**
2683 * \brief Get the position and orientation of the camera
2684 *
2685 * \param[out] position x,y,z position of camera in world coordinates
2686 * \param[out] focalPoint x,y,z look-at point in world coordinates
2687 * \param[out] viewUp x,y,z up vector of camera
2688 */
2689void Renderer::getCameraOrientationAndPosition(double position[3],
2690                                               double focalPoint[3],
2691                                               double viewUp[3])
2692{
2693    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2694    camera->GetPosition(position);
2695    camera->GetFocalPoint(focalPoint);
2696    camera->GetViewUp(viewUp);
2697}
2698
2699void Renderer::sceneBoundsChanged()
2700{
2701#ifdef RESET_CAMERA_ON_SCENE_CHANGE
2702    _needsCameraReset = true;
2703#else
2704    _needsAxesReset = true;
2705    _needsCameraClippingRangeReset = true;
2706#endif
2707
2708    _needsRedraw = true;
2709}
2710
2711/**
2712 * \brief Reset pan, zoom, clipping planes and optionally rotation
2713 *
2714 * \param[in] resetOrientation Reset the camera rotation/orientation also
2715 */
2716void Renderer::resetCamera(bool resetOrientation)
2717{
2718    TRACE("Enter: %d", resetOrientation ? 1 : 0);
2719    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2720    if (_cameraMode == IMAGE) {
2721        initCamera();
2722    } else {
2723        if (resetOrientation) {
2724            camera->SetPosition(0, 0, 1);
2725            camera->SetFocalPoint(0, 0, 0);
2726            camera->SetViewUp(0, 1, 0);
2727            _cameraOrientation[0] = 1.0;
2728            _cameraOrientation[1] = 0.0;
2729            _cameraOrientation[2] = 0.0;
2730            _cameraOrientation[3] = 0.0;
2731        }
2732        //setViewAngle(_windowHeight);
2733        //double bounds[6];
2734        //collectBounds(bounds, false);
2735        //_renderer->ResetCamera(bounds);
2736        if (_needsAxesReset) {
2737            resetAxes();
2738            _needsAxesReset = false;
2739        }
2740        resetVtkCamera();
2741        //computeScreenWorldCoords();
2742    }
2743
2744    printCameraInfo(camera);
2745
2746    _cameraZoomRatio = 1;
2747    _cameraPan[0] = 0;
2748    _cameraPan[1] = 0;
2749
2750    _needsRedraw = true;
2751}
2752
2753void Renderer::resetVtkCamera(double *bounds)
2754{
2755    TRACE("Enter: bounds: %p", bounds);
2756    if (bounds != NULL)
2757        _renderer->ResetCamera(bounds);
2758    else
2759        _renderer->ResetCamera();
2760    printCameraInfo(_renderer->GetActiveCamera());
2761}
2762
2763/**
2764 * \brief Set the camera near/far clipping range based on current scene bounds
2765 */
2766void Renderer::resetCameraClippingRange()
2767{
2768    _renderer->ResetCameraClippingRange();
2769    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2770    //double dist = camera->GetClippingRange()[0] + (camera->GetClippingRange()[1] - camera->GetClippingRange()[0])/2.0;
2771    //camera->SetDistance(dist);
2772    printCameraInfo(camera);
2773}
2774
2775/**
2776 * \brief Perform a relative rotation to current camera orientation
2777 *
2778 * Angles are in degrees, rotation is about focal point
2779 */
2780void Renderer::rotateCamera(double yaw, double pitch, double roll)
2781{
2782    if (_cameraMode == IMAGE)
2783        return;
2784    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2785    camera->Azimuth(yaw); // Rotate about object
2786    //camera->SetYaw(yaw); // Rotate about camera
2787    camera->Elevation(pitch); // Rotate about object
2788    //camera->SetPitch(pitch); // Rotate about camera
2789    camera->Roll(roll); // Roll about camera view axis
2790    resetCameraClippingRange();
2791    //computeScreenWorldCoords();
2792    _needsRedraw = true;
2793}
2794
2795/**
2796 * \brief Perform a 2D translation of the camera
2797 *
2798 * x,y pan amount are specified as signed absolute pan amount in viewport
2799 * units -- i.e. 0 is no pan, .5 is half the viewport, 2 is twice the viewport,
2800 * etc.
2801 *
2802 * \param[in] x Viewport coordinate horizontal panning (positive number pans
2803 * camera left, object right)
2804 * \param[in] y Viewport coordinate vertical panning (positive number pans
2805 * camera up, object down)
2806 * \param[in] absolute Control if pan amount is relative to current or absolute
2807 */
2808void Renderer::panCamera(double x, double y, bool absolute)
2809{
2810    TRACE("Enter panCamera: %g %g, current abs: %g %g",
2811          x, y, _cameraPan[0], _cameraPan[1]);
2812
2813    if (_cameraMode == IMAGE) {
2814        _userImgWorldOrigin[0] = 0;
2815        _userImgWorldOrigin[1] = 0;
2816        _userImgWorldDims[0] = -1;
2817        _userImgWorldDims[1] = -1;
2818
2819        // Reverse x rather than y, since we are panning the camera, and client
2820        // expects to be panning/moving the object
2821        x = -x * _screenWorldCoords[2];
2822        y = y * _screenWorldCoords[3];
2823
2824        if (absolute) {
2825            double panAbs[2];
2826            panAbs[0] = x;
2827            panAbs[1] = y;
2828            x -= _cameraPan[0];
2829            y -= _cameraPan[1];
2830            _cameraPan[0] = panAbs[0];
2831            _cameraPan[1] = panAbs[1];
2832        } else {
2833            _cameraPan[0] += x;
2834            _cameraPan[1] += y;
2835        }
2836
2837        _imgWorldOrigin[0] += x;
2838        _imgWorldOrigin[1] += y;
2839        _setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
2840                             _imgWorldDims[0], _imgWorldDims[1]);
2841    } else {
2842        y = -y;
2843        if (absolute) {
2844            double panAbs[2];
2845            panAbs[0] = x;
2846            panAbs[1] = y;
2847            x -= _cameraPan[0];
2848            y -= _cameraPan[1];
2849            _cameraPan[0] = panAbs[0];
2850            _cameraPan[1] = panAbs[1];
2851        } else {
2852            _cameraPan[0] += x;
2853            _cameraPan[1] += y;
2854        }
2855
2856        if (x != 0.0 || y != 0.0) {
2857            vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2858            double viewFocus[4], focalDepth, viewPoint[3];
2859            double newPickPoint[4], oldPickPoint[4], motionVector[3];
2860
2861            camera->GetFocalPoint(viewFocus);
2862            computeWorldToDisplay(viewFocus[0], viewFocus[1], viewFocus[2],
2863                                  viewFocus);
2864            focalDepth = viewFocus[2];
2865
2866            computeDisplayToWorld((x * 2. + 1.) * (double)_windowWidth / 2.0,
2867                                  (y * 2. + 1.) * (double)_windowHeight / 2.0,
2868                                  focalDepth,
2869                                  newPickPoint);
2870
2871            computeDisplayToWorld((double)_windowWidth / 2.0,
2872                                  (double)_windowHeight / 2.0,
2873                                  focalDepth,
2874                                  oldPickPoint);
2875
2876            // Camera motion is reversed
2877            motionVector[0] = oldPickPoint[0] - newPickPoint[0];
2878            motionVector[1] = oldPickPoint[1] - newPickPoint[1];
2879            motionVector[2] = oldPickPoint[2] - newPickPoint[2];
2880
2881            camera->GetFocalPoint(viewFocus);
2882            camera->GetPosition(viewPoint);
2883            camera->SetFocalPoint(motionVector[0] + viewFocus[0],
2884                                  motionVector[1] + viewFocus[1],
2885                                  motionVector[2] + viewFocus[2]);
2886
2887            camera->SetPosition(motionVector[0] + viewPoint[0],
2888                                motionVector[1] + viewPoint[1],
2889                                motionVector[2] + viewPoint[2]);
2890
2891            resetCameraClippingRange();
2892            //computeScreenWorldCoords();
2893        }
2894    }
2895
2896    TRACE("Leave panCamera: %g %g, current abs: %g %g",
2897          x, y, _cameraPan[0], _cameraPan[1]);
2898
2899    _needsRedraw = true;
2900}
2901
2902/**
2903 * \brief Dolly camera or set orthographic scaling based on camera type
2904 *
2905 * \param[in] z Ratio to change zoom (greater than 1 is zoom in, less than 1 is zoom out)
2906 * \param[in] absolute Control if zoom factor is relative to current setting or absolute
2907 */
2908void Renderer::zoomCamera(double z, bool absolute)
2909{
2910    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2911    TRACE("Enter Zoom: current abs: %g, z: %g, view angle %g",
2912          _cameraZoomRatio, z, camera->GetViewAngle());
2913
2914    if (absolute) {
2915        assert(_cameraZoomRatio > 0.0);
2916        double zAbs = z;
2917        z *= 1.0/_cameraZoomRatio;
2918        _cameraZoomRatio = zAbs;
2919    } else {
2920        _cameraZoomRatio *= z;
2921    }
2922
2923    if (_cameraMode == IMAGE) {
2924        _userImgWorldOrigin[0] = 0;
2925        _userImgWorldOrigin[1] = 0;
2926        _userImgWorldDims[0] = -1;
2927        _userImgWorldDims[1] = -1;
2928
2929        double dx = _imgWorldDims[0];
2930        double dy = _imgWorldDims[1];
2931        _imgWorldDims[0] /= z;
2932        _imgWorldDims[1] /= z;
2933        dx -= _imgWorldDims[0];
2934        dy -= _imgWorldDims[1];
2935        _imgWorldOrigin[0] += dx/2.0;
2936        _imgWorldOrigin[1] += dy/2.0;
2937        _setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
2938                             _imgWorldDims[0], _imgWorldDims[1]);
2939    } else {
2940        // Keep ortho and perspective modes in sync
2941        // Move camera forward/back for perspective camera
2942        camera->Dolly(z);
2943        // Change ortho parallel scale
2944        camera->SetParallelScale(camera->GetParallelScale()/z);
2945        resetCameraClippingRange();
2946        //computeScreenWorldCoords();
2947    }
2948
2949    TRACE("Leave Zoom: rel %g, new abs: %g, view angle %g",
2950          z, _cameraZoomRatio, camera->GetViewAngle());
2951
2952    _needsRedraw = true;
2953}
2954
2955bool Renderer::setCameraZoomRegionPixels(int x, int y, int width, int height)
2956{
2957    if (_cameraMode != IMAGE)
2958        return false;
2959
2960    double wx, wy, ww, wh;
2961
2962    y = _windowHeight - y;
2963    double pxToWorldX = _screenWorldCoords[2] / (double)_windowWidth;
2964    double pxToWorldY = _screenWorldCoords[3] / (double)_windowHeight;
2965
2966    wx = _screenWorldCoords[0] + x * pxToWorldX;
2967    wy = _screenWorldCoords[1] + y * pxToWorldY;
2968    ww = abs(width) *  pxToWorldX;
2969    wh = abs(height) * pxToWorldY;
2970    setCameraZoomRegion(wx, wy, ww, wh);
2971
2972    TRACE("\npx: %d %d %d %d\nworld: %g %g %g %g",
2973          x, y, width, height,
2974          wx, wy, ww, wh);
2975
2976    return true;
2977}
2978
2979bool Renderer::setCameraZoomRegion(double x, double y, double width, double height)
2980{
2981    if (_cameraMode != IMAGE)
2982        return false;
2983
2984    _userImgWorldOrigin[0] = x;
2985    _userImgWorldOrigin[1] = y;
2986    _userImgWorldDims[0] = width;
2987    _userImgWorldDims[1] = height;
2988    _setCameraZoomRegion(x, y, width, height);
2989
2990    return true;
2991}
2992
2993/**
2994 * \brief Set the pan/zoom using a corner and dimensions in pixel coordinates
2995 *
2996 * \param[in] x left pixel coordinate
2997 * \param[in] y bottom  pixel coordinate (with y=0 at top of window)
2998 * \param[in] width Width of zoom region in pixel coordinates
2999 * \param[in] height Height of zoom region in pixel coordinates
3000 */
3001void Renderer::_setCameraZoomRegionPixels(int x, int y, int width, int height)
3002{
3003    if (_cameraMode != IMAGE) {
3004        ERROR("Called while camera mode is not image");
3005        return;
3006    }
3007
3008    double wx, wy, ww, wh;
3009
3010    y = _windowHeight - y;
3011    double pxToWorldX = _screenWorldCoords[2] / (double)_windowWidth;
3012    double pxToWorldY = _screenWorldCoords[3] / (double)_windowHeight;
3013
3014    wx = _screenWorldCoords[0] + x * pxToWorldX;
3015    wy = _screenWorldCoords[1] + y * pxToWorldY;
3016    ww = abs(width) *  pxToWorldX;
3017    wh = abs(height) * pxToWorldY;
3018    _setCameraZoomRegion(wx, wy, ww, wh);
3019
3020    TRACE("\npx: %d %d %d %d\nworld: %g %g %g %g",
3021          x, y, width, height,
3022          wx, wy, ww, wh);
3023}
3024
3025void Renderer::getImageCameraSizes(int *imgWidthPx, int *imgHeightPx,
3026                                   int *_pxOffsetX, int *_pxOffsetY)
3027{
3028    int pxOffsetX, pxOffsetY;
3029    pxOffsetX = (int)(0.17 * (double)_windowWidth);
3030    pxOffsetX = (pxOffsetX > 100 ? 100 : pxOffsetX);
3031    pxOffsetY = (int)(0.15 * (double)_windowHeight);
3032    pxOffsetY = (pxOffsetY > 75 ? 75 : pxOffsetY);
3033
3034    int outerGutter = (int)(0.03 * (double)_windowWidth);
3035    outerGutter = (outerGutter > 15 ? 15 : outerGutter);
3036
3037    *imgWidthPx = _windowWidth - pxOffsetX - outerGutter;
3038    *imgHeightPx = _windowHeight - pxOffsetY - outerGutter;
3039    if (_pxOffsetX != NULL) *_pxOffsetX = pxOffsetX;
3040    if (_pxOffsetY != NULL) *_pxOffsetY = pxOffsetY;
3041}
3042
3043double Renderer::getImageCameraAspect()
3044{
3045    int imgWidthPx, imgHeightPx;
3046    getImageCameraSizes(&imgWidthPx, &imgHeightPx);
3047    return ((double)imgWidthPx / (double)imgHeightPx);
3048}
3049
3050/**
3051 * \brief Set the pan/zoom using a corner and dimensions in world coordinates
3052 *
3053 * \param[in] x left world coordinate
3054 * \param[in] y bottom  world coordinate
3055 * \param[in] width Width of zoom region in world coordinates
3056 * \param[in] height Height of zoom region in world coordinates
3057 */
3058void Renderer::_setCameraZoomRegion(double x, double y, double width, double height)
3059{
3060    if (_cameraMode != IMAGE) {
3061        ERROR("Called while camera mode is not image");
3062        return;
3063    }
3064
3065    int imgWidthPx, imgHeightPx;
3066    int pxOffsetX, pxOffsetY;
3067
3068    getImageCameraSizes(&imgWidthPx, &imgHeightPx, &pxOffsetX, &pxOffsetY);
3069
3070    double imgWindowAspect = (double)imgWidthPx / (double)imgHeightPx;
3071
3072    double pxToWorld;
3073    double imgWidthWorld;
3074    double imgHeightWorld;
3075
3076    double requestedAspect = width / height;
3077
3078    if (requestedAspect >= imgWindowAspect) {
3079        pxToWorld = width / (double)imgWidthPx;
3080        imgWidthWorld = width;
3081        imgHeightWorld = (double)imgHeightPx * pxToWorld;
3082    } else {
3083        pxToWorld = height / (double)imgHeightPx;
3084        imgWidthWorld = (double)imgWidthPx * pxToWorld;
3085        imgHeightWorld =  height;
3086    }
3087
3088    double offsetX = pxOffsetX * pxToWorld;
3089    double offsetY = pxOffsetY * pxToWorld;
3090    double winWidthWorld = _windowWidth * pxToWorld;
3091    double winHeightWorld = _windowHeight * pxToWorld;
3092
3093    TRACE("Window: %d %d", _windowWidth, _windowHeight);
3094    TRACE("ZoomRegion: %g %g %g %g", x, y, width, height);
3095    TRACE("pxToWorld: %g", pxToWorld);
3096    TRACE("offset: %g %g", offsetX, offsetY);
3097
3098    _imgWorldOrigin[0] = x;
3099    _imgWorldOrigin[1] = y;
3100    _imgWorldDims[0] = width;
3101    _imgWorldDims[1] = height;
3102    _imgWindowWorldDims[0] = imgWidthWorld;
3103    _imgWindowWorldDims[1] = imgHeightWorld;
3104
3105    double camPos[2];
3106    camPos[0] = _imgWorldOrigin[0] - offsetX + winWidthWorld / 2.0;
3107    camPos[1] = _imgWorldOrigin[1] - offsetY + winHeightWorld / 2.0;
3108
3109    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
3110    camera->ParallelProjectionOn();
3111    camera->SetClippingRange(1, 2);
3112    // Half of world coordinate height of viewport (Documentation is wrong)
3113    camera->SetParallelScale(winHeightWorld / 2.0);
3114
3115    if (_imgCameraPlane == PLANE_XY) {
3116        // XY plane
3117        camera->SetPosition(camPos[0], camPos[1], _imgCameraOffset + 1.);
3118        camera->SetFocalPoint(camPos[0], camPos[1], _imgCameraOffset);
3119        camera->SetViewUp(0, 1, 0);
3120        // bottom
3121        _cameraClipPlanes[0]->SetOrigin(0, _imgWorldOrigin[1], 0);
3122        _cameraClipPlanes[0]->SetNormal(0, 1, 0);
3123        // left
3124        _cameraClipPlanes[1]->SetOrigin(_imgWorldOrigin[0], 0, 0);
3125        _cameraClipPlanes[1]->SetNormal(1, 0, 0);
3126        // top
3127        _cameraClipPlanes[2]->SetOrigin(0, _imgWorldOrigin[1] + _imgWindowWorldDims[1], 0);
3128        _cameraClipPlanes[2]->SetNormal(0, -1, 0);
3129        // right
3130        _cameraClipPlanes[3]->SetOrigin(_imgWorldOrigin[0] + _imgWindowWorldDims[0], 0, 0);
3131        _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
3132
3133        _cubeAxesActor->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWindowWorldDims[0],
3134                                  _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWindowWorldDims[1],
3135                                  _imgCameraOffset, _imgCameraOffset);
3136        _cubeAxesActor->XAxisVisibilityOn();
3137        _cubeAxesActor->YAxisVisibilityOn();
3138        _cubeAxesActor->ZAxisVisibilityOff();
3139    } else if (_imgCameraPlane == PLANE_ZY) {
3140        // ZY plane
3141        camera->SetPosition(_imgCameraOffset - 1., camPos[1], camPos[0]);
3142        camera->SetFocalPoint(_imgCameraOffset, camPos[1], camPos[0]);
3143        camera->SetViewUp(0, 1, 0);
3144        // bottom
3145        _cameraClipPlanes[0]->SetOrigin(0, _imgWorldOrigin[1], 0);
3146        _cameraClipPlanes[0]->SetNormal(0, 1, 0);
3147        // left
3148        _cameraClipPlanes[1]->SetOrigin(0, 0, _imgWorldOrigin[0]);
3149        _cameraClipPlanes[1]->SetNormal(0, 0, 1);
3150        // top
3151        _cameraClipPlanes[2]->SetOrigin(0, _imgWorldOrigin[1] + _imgWindowWorldDims[1], 0);
3152        _cameraClipPlanes[2]->SetNormal(0, -1, 0);
3153        // right
3154        _cameraClipPlanes[3]->SetOrigin(0, 0, _imgWorldOrigin[0] + _imgWindowWorldDims[0]);
3155        _cameraClipPlanes[3]->SetNormal(0, 0, -1);
3156
3157        _cubeAxesActor->SetBounds(_imgCameraOffset, _imgCameraOffset,
3158                                  _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWindowWorldDims[1],
3159                                  _imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWindowWorldDims[0]);
3160        _cubeAxesActor->XAxisVisibilityOff();
3161        _cubeAxesActor->YAxisVisibilityOn();
3162        _cubeAxesActor->ZAxisVisibilityOn();
3163    } else {
3164        // XZ plane
3165        camera->SetPosition(camPos[0], _imgCameraOffset - 1., camPos[1]);
3166        camera->SetFocalPoint(camPos[0], _imgCameraOffset, camPos[1]);
3167        camera->SetViewUp(0, 0, 1);
3168        // bottom
3169        _cameraClipPlanes[0]->SetOrigin(0, 0, _imgWorldOrigin[1]);
3170        _cameraClipPlanes[0]->SetNormal(0, 0, 1);
3171        // left
3172        _cameraClipPlanes[1]->SetOrigin(_imgWorldOrigin[0], 0, 0);
3173        _cameraClipPlanes[1]->SetNormal(1, 0, 0);
3174        // top
3175        _cameraClipPlanes[2]->SetOrigin(0, 0, _imgWorldOrigin[1] + _imgWorldDims[1]);
3176        _cameraClipPlanes[2]->SetNormal(0, 0, -1);
3177        // right
3178        _cameraClipPlanes[3]->SetOrigin(_imgWorldOrigin[0] + _imgWorldDims[0], 0, 0);
3179        _cameraClipPlanes[3]->SetNormal(-1, 0, 0);
3180
3181        _cubeAxesActor->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWindowWorldDims[0],
3182                                  _imgCameraOffset, _imgCameraOffset,
3183                                  _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWindowWorldDims[1]);
3184        _cubeAxesActor->XAxisVisibilityOn();
3185        _cubeAxesActor->YAxisVisibilityOff();
3186        _cubeAxesActor->ZAxisVisibilityOn();
3187    }
3188
3189    // Fix up axis ranges based on new bounds
3190    setAxesRanges();
3191
3192    // Compute screen world coordinates
3193    computeScreenWorldCoords();
3194
3195#ifdef DEBUG
3196    printCameraInfo(camera);
3197#endif
3198
3199    _needsRedraw = true;
3200}
3201
3202/**
3203 * \brief Convert pixel/display coordinates to world coordinates based on current camera
3204 */
3205void Renderer::computeDisplayToWorld(double x, double y, double z, double worldPt[4])
3206{
3207    _renderer->SetDisplayPoint(x, y, z);
3208    _renderer->DisplayToWorld();
3209    _renderer->GetWorldPoint(worldPt);
3210    if (worldPt[3]) {
3211        worldPt[0] /= worldPt[3];
3212        worldPt[1] /= worldPt[3];
3213        worldPt[2] /= worldPt[3];
3214        worldPt[3] = 1.0;
3215    }
3216}
3217
3218/**
3219 * \brief Convert world coordinates to pixel/display coordinates based on current camera
3220 */
3221void Renderer::computeWorldToDisplay(double x, double y, double z, double displayPt[3])
3222{
3223    _renderer->SetWorldPoint(x, y, z, 1.0);
3224    _renderer->WorldToDisplay();
3225    _renderer->GetDisplayPoint(displayPt);
3226}
3227
3228/**
3229 * \brief Compute the world coordinate bounds of the display rectangle
3230 */
3231void Renderer::computeScreenWorldCoords()
3232{
3233    // Start with viewport coords [-1,1]
3234    double x0 = -1;
3235    double y0 = -1;
3236    double z0 = -1;
3237    double x1 = 1;
3238    double y1 = 1;
3239    double z1 = -1;
3240
3241    vtkMatrix4x4 *mat = vtkMatrix4x4::New();
3242    double result[4];
3243
3244    // get the perspective transformation from the active camera
3245    mat->DeepCopy(_renderer->GetActiveCamera()->
3246                  GetCompositeProjectionTransformMatrix(_renderer->GetTiledAspectRatio(),0,1));
3247
3248    // use the inverse matrix
3249    mat->Invert();
3250
3251    // Transform point to world coordinates
3252    result[0] = x0;
3253    result[1] = y0;
3254    result[2] = z0;
3255    result[3] = 1.0;
3256
3257    mat->MultiplyPoint(result, result);
3258
3259    // Get the transformed vector & set WorldPoint
3260    // while we are at it try to keep w at one
3261    if (result[3]) {
3262        x0 = result[0] / result[3];
3263        y0 = result[1] / result[3];
3264        z0 = result[2] / result[3];
3265    }
3266
3267    result[0] = x1;
3268    result[1] = y1;
3269    result[2] = z1;
3270    result[3] = 1.0;
3271
3272    mat->MultiplyPoint(result, result);
3273
3274    if (result[3]) {
3275        x1 = result[0] / result[3];
3276        y1 = result[1] / result[3];
3277        z1 = result[2] / result[3];
3278    }
3279
3280    mat->Delete();
3281
3282    if (_imgCameraPlane == PLANE_XZ) {
3283        _screenWorldCoords[0] = x0;
3284        _screenWorldCoords[1] = z0;
3285        _screenWorldCoords[2] = x1 - x0;
3286        _screenWorldCoords[3] = z1 - z0;
3287    } else if (_imgCameraPlane == PLANE_ZY) {
3288        _screenWorldCoords[0] = z0;
3289        _screenWorldCoords[1] = y0;
3290        _screenWorldCoords[2] = z1 - z0;
3291        _screenWorldCoords[3] = y1 - y0;
3292    } else {
3293        // XY
3294        _screenWorldCoords[0] = x0;
3295        _screenWorldCoords[1] = y0;
3296        _screenWorldCoords[2] = x1 - x0;
3297        _screenWorldCoords[3] = y1 - y0;
3298    }
3299}
3300
3301/**
3302 * \brief Get the world coordinates of the image camera plot area
3303 *
3304 * \param[out] xywh Array to hold x,y,width,height world coordinates
3305 */
3306void Renderer::getCameraZoomRegion(double xywh[4]) const
3307{
3308    xywh[0] = _imgWorldOrigin[0];
3309    xywh[1] = _imgWorldOrigin[1];
3310    xywh[2] = _imgWorldDims[0];
3311    xywh[3] = _imgWorldDims[1];
3312}
3313
3314/**
3315 * \brief Get the world origin and dimensions of the screen
3316 *
3317 * \param[out] xywh Array to hold x,y,width,height world coordinates
3318 */
3319void Renderer::getScreenWorldCoords(double xywh[4]) const
3320{
3321    memcpy(xywh, _screenWorldCoords, sizeof(double)*4);
3322}
3323
3324/**
3325 * \brief Compute bounding box containing the two input bounding boxes
3326 *
3327 * \param[out] boundsDest Union of the two input bounding boxes
3328 * \param[in] bounds1 Input bounding box
3329 * \param[in] bounds2 Input bounding box
3330 */
3331void Renderer::mergeBounds(double *boundsDest,
3332                           const double *bounds1, const double *bounds2)
3333{
3334    assert(boundsDest != NULL);
3335    assert(bounds1 != NULL);
3336    if (bounds2 == NULL) {
3337        WARN("NULL bounds2 array");
3338        return;
3339    }
3340    bool b1empty = (bounds1[0] > bounds1[1]);
3341    bool b2empty = (bounds2[0] > bounds2[1]);
3342
3343    if (b1empty && b2empty)
3344        return;
3345    if (b1empty) {
3346        memcpy(boundsDest, bounds2, sizeof(double) * 6);
3347        return;
3348    } else if (b2empty) {
3349        memcpy(boundsDest, bounds1, sizeof(double) * 6);
3350        return;
3351    }
3352
3353    for (int i = 0; i < 6; i++) {
3354        if (i % 2 == 0)
3355            boundsDest[i] = min2(bounds1[i], bounds2[i]);
3356        else
3357            boundsDest[i] = max2(bounds1[i], bounds2[i]);
3358    }
3359}
3360
3361/**
3362 * \brief Collect bounds of all graphics objects
3363 *
3364 * \param[out] bounds Bounds of all scene objects
3365 * \param[in] onlyVisible Only collect bounds of visible objects
3366 */
3367void Renderer::collectBounds(double *bounds, bool onlyVisible)
3368{
3369    bounds[0] = DBL_MAX;
3370    bounds[1] = -DBL_MAX;
3371    bounds[2] = DBL_MAX;
3372    bounds[3] = -DBL_MAX;
3373    bounds[4] = DBL_MAX;
3374    bounds[5] = -DBL_MAX;
3375
3376    mergeGraphicsObjectBounds<Arc>(bounds, onlyVisible);
3377    mergeGraphicsObjectBounds<Arrow>(bounds, onlyVisible);
3378    mergeGraphicsObjectBounds<Box>(bounds, onlyVisible);
3379    mergeGraphicsObjectBounds<Cone>(bounds, onlyVisible);
3380    mergeGraphicsObjectBounds<Contour2D>(bounds, onlyVisible);
3381    mergeGraphicsObjectBounds<Contour3D>(bounds, onlyVisible);
3382    mergeGraphicsObjectBounds<Cutplane>(bounds, onlyVisible);
3383    mergeGraphicsObjectBounds<Cylinder>(bounds, onlyVisible);
3384    mergeGraphicsObjectBounds<Disk>(bounds, onlyVisible);
3385    mergeGraphicsObjectBounds<Glyphs>(bounds, onlyVisible);
3386    mergeGraphicsObjectBounds<Group>(bounds, onlyVisible);
3387    mergeGraphicsObjectBounds<HeightMap>(bounds, onlyVisible);
3388    mergeGraphicsObjectBounds<Image>(bounds, onlyVisible);
3389    mergeGraphicsObjectBounds<LIC>(bounds, onlyVisible);
3390    mergeGraphicsObjectBounds<Line>(bounds, onlyVisible);
3391    mergeGraphicsObjectBounds<Molecule>(bounds, onlyVisible);
3392    mergeGraphicsObjectBounds<Outline>(bounds, onlyVisible);
3393    mergeGraphicsObjectBounds<Parallelepiped>(bounds, onlyVisible);
3394    mergeGraphicsObjectBounds<PolyData>(bounds, onlyVisible);
3395    mergeGraphicsObjectBounds<Polygon>(bounds, onlyVisible);
3396    mergeGraphicsObjectBounds<PseudoColor>(bounds, onlyVisible);
3397    mergeGraphicsObjectBounds<Sphere>(bounds, onlyVisible);
3398    mergeGraphicsObjectBounds<Streamlines>(bounds, onlyVisible);
3399    mergeGraphicsObjectBounds<Text3D>(bounds, onlyVisible);
3400    mergeGraphicsObjectBounds<Volume>(bounds, onlyVisible);
3401    mergeGraphicsObjectBounds<Warp>(bounds, onlyVisible);
3402
3403    for (int i = 0; i < 6; i += 2) {
3404        if (bounds[i+1] < bounds[i]) {
3405            bounds[i] = -0.5;
3406            bounds[i+1] = 0.5;
3407        }
3408    }
3409
3410    int numDims = 0;
3411    if (bounds[0] != bounds[1])
3412        numDims++;
3413    if (bounds[2] != bounds[3])
3414        numDims++;
3415    if (bounds[4] != bounds[5])
3416        numDims++;
3417
3418    if (numDims == 0) {
3419        bounds[0] -= .5;
3420        bounds[1] += .5;
3421        bounds[2] -= .5;
3422        bounds[3] += .5;
3423    }
3424
3425    TRACE("Bounds: %g %g %g %g %g %g",
3426          bounds[0],
3427          bounds[1],
3428          bounds[2],
3429          bounds[3],
3430          bounds[4],
3431          bounds[5]);
3432}
3433
3434/**
3435 * \brief Collect bounds of all graphics objects
3436 *
3437 * \param[out] bounds Bounds of all scene objects
3438 * \param[in] onlyVisible Only collect bounds of visible objects
3439 */
3440void Renderer::collectUnscaledBounds(double *bounds, bool onlyVisible)
3441{
3442    bounds[0] = DBL_MAX;
3443    bounds[1] = -DBL_MAX;
3444    bounds[2] = DBL_MAX;
3445    bounds[3] = -DBL_MAX;
3446    bounds[4] = DBL_MAX;
3447    bounds[5] = -DBL_MAX;
3448
3449    mergeGraphicsObjectUnscaledBounds<Arc>(bounds, onlyVisible);
3450    mergeGraphicsObjectUnscaledBounds<Arrow>(bounds, onlyVisible);
3451    mergeGraphicsObjectUnscaledBounds<Box>(bounds, onlyVisible);
3452    mergeGraphicsObjectUnscaledBounds<Cone>(bounds, onlyVisible);
3453    mergeGraphicsObjectUnscaledBounds<Contour2D>(bounds, onlyVisible);
3454    mergeGraphicsObjectUnscaledBounds<Contour3D>(bounds, onlyVisible);
3455    mergeGraphicsObjectUnscaledBounds<Cutplane>(bounds, onlyVisible);
3456    mergeGraphicsObjectUnscaledBounds<Cylinder>(bounds, onlyVisible);
3457    mergeGraphicsObjectUnscaledBounds<Disk>(bounds, onlyVisible);
3458    mergeGraphicsObjectUnscaledBounds<Glyphs>(bounds, onlyVisible);
3459    mergeGraphicsObjectUnscaledBounds<HeightMap>(bounds, onlyVisible);
3460    mergeGraphicsObjectUnscaledBounds<Image>(bounds, onlyVisible);
3461    mergeGraphicsObjectUnscaledBounds<LIC>(bounds, onlyVisible);
3462    mergeGraphicsObjectUnscaledBounds<Line>(bounds, onlyVisible);
3463    mergeGraphicsObjectUnscaledBounds<Molecule>(bounds, onlyVisible);
3464    mergeGraphicsObjectUnscaledBounds<Outline>(bounds, onlyVisible);
3465    mergeGraphicsObjectUnscaledBounds<PolyData>(bounds, onlyVisible);
3466    mergeGraphicsObjectUnscaledBounds<Parallelepiped>(bounds, onlyVisible);
3467    mergeGraphicsObjectUnscaledBounds<Polygon>(bounds, onlyVisible);
3468    mergeGraphicsObjectUnscaledBounds<PseudoColor>(bounds, onlyVisible);
3469    mergeGraphicsObjectUnscaledBounds<Sphere>(bounds, onlyVisible);
3470    mergeGraphicsObjectUnscaledBounds<Streamlines>(bounds, onlyVisible);
3471    mergeGraphicsObjectUnscaledBounds<Text3D>(bounds, onlyVisible);
3472    mergeGraphicsObjectUnscaledBounds<Volume>(bounds, onlyVisible);
3473    mergeGraphicsObjectUnscaledBounds<Warp>(bounds, onlyVisible);
3474
3475    for (int i = 0; i < 6; i += 2) {
3476        if (bounds[i+1] < bounds[i]) {
3477            bounds[i] = -0.5;
3478            bounds[i+1] = 0.5;
3479        }
3480    }
3481
3482    int numDims = 0;
3483    if (bounds[0] != bounds[1])
3484        numDims++;
3485    if (bounds[2] != bounds[3])
3486        numDims++;
3487    if (bounds[4] != bounds[5])
3488        numDims++;
3489
3490    if (numDims == 0) {
3491        bounds[0] -= .5;
3492        bounds[1] += .5;
3493        bounds[2] -= .5;
3494        bounds[3] += .5;
3495    }
3496
3497    TRACE("Bounds: %g %g %g %g %g %g",
3498          bounds[0],
3499          bounds[1],
3500          bounds[2],
3501          bounds[3],
3502          bounds[4],
3503          bounds[5]);
3504}
3505
3506/**
3507 * \brief Update data ranges for color-mapping and contours
3508 */
3509void Renderer::updateFieldRanges()
3510{
3511    collectDataRanges();
3512
3513    updateGraphicsObjectFieldRanges<Contour2D>();
3514    updateGraphicsObjectFieldRanges<Contour3D>();
3515    updateGraphicsObjectFieldRanges<Cutplane>();
3516    updateGraphicsObjectFieldRanges<Glyphs>();
3517    updateGraphicsObjectFieldRanges<HeightMap>();
3518    updateGraphicsObjectFieldRanges<Image>();
3519    updateGraphicsObjectFieldRanges<LIC>();
3520    updateGraphicsObjectFieldRanges<Molecule>();
3521    updateGraphicsObjectFieldRanges<PolyData>();
3522    updateGraphicsObjectFieldRanges<PseudoColor>();
3523    updateGraphicsObjectFieldRanges<Streamlines>();
3524    updateGraphicsObjectFieldRanges<Volume>();
3525    updateGraphicsObjectFieldRanges<Warp>();
3526}
3527
3528/**
3529 * \brief Collect cumulative data range of all DataSets
3530 *
3531 * \param[out] range Data range of all DataSets
3532 * \param[in] name Field name
3533 * \param[in] type Attribute type: e.g. POINT_DATA, CELL_DATA
3534 * \param[in] component Array component or -1 for magnitude
3535 * \param[in] onlyVisible Only collect range of visible DataSets
3536 */
3537void Renderer::collectDataRanges(double *range, const char *name,
3538                                 DataSet::DataAttributeType type,
3539                                 int component, bool onlyVisible)
3540{
3541    range[0] = DBL_MAX;
3542    range[1] = -DBL_MAX;
3543
3544    for (DataSetHashmap::iterator itr = _dataSets.begin();
3545         itr != _dataSets.end(); ++itr) {
3546        if (!onlyVisible || itr->second->getVisibility()) {
3547            double r[2];
3548            r[0] = DBL_MAX;
3549            r[1] = -DBL_MAX;
3550            itr->second->getDataRange(r, name, type, component);
3551            range[0] = min2(range[0], r[0]);
3552            range[1] = max2(range[1], r[1]);
3553        }
3554    }
3555    if (range[0] == DBL_MAX)
3556        range[0] = 0;
3557    if (range[1] == -DBL_MAX)
3558        range[1] = 1;
3559   
3560    TRACE("n:'%s' t:%d c:%d [%g,%g]", name, type, component,
3561          range[0], range[1]);
3562}
3563
3564/**
3565 * \brief Clear field range hashtables and free memory
3566 */
3567void Renderer::clearFieldRanges()
3568{
3569    TRACE("Deleting Field Ranges");
3570    for (FieldRangeHashmap::iterator itr = _scalarPointDataRange.begin();
3571         itr != _scalarPointDataRange.end(); ++itr) {
3572        delete [] itr->second;
3573    }
3574    _scalarPointDataRange.clear();
3575    for (FieldRangeHashmap::iterator itr = _scalarCellDataRange.begin();
3576         itr != _scalarCellDataRange.end(); ++itr) {
3577        delete [] itr->second;
3578    }
3579    _scalarCellDataRange.clear();
3580    for (FieldRangeHashmap::iterator itr = _scalarFieldDataRange.begin();
3581         itr != _scalarFieldDataRange.end(); ++itr) {
3582        delete [] itr->second;
3583    }
3584    _scalarFieldDataRange.clear();
3585    for (FieldRangeHashmap::iterator itr = _vectorPointDataRange.begin();
3586         itr != _vectorPointDataRange.end(); ++itr) {
3587        delete [] itr->second;
3588    }
3589    _vectorPointDataRange.clear();
3590    for (int i = 0; i < 3; i++) {
3591        for (FieldRangeHashmap::iterator itr = _vectorCompPointDataRange[i].begin();
3592             itr != _vectorCompPointDataRange[i].end(); ++itr) {
3593            delete [] itr->second;
3594        }
3595        _vectorCompPointDataRange[i].clear();
3596    }
3597    for (FieldRangeHashmap::iterator itr = _vectorCellDataRange.begin();
3598         itr != _vectorCellDataRange.end(); ++itr) {
3599        delete [] itr->second;
3600    }
3601    _vectorCellDataRange.clear();
3602    for (int i = 0; i < 3; i++) {
3603        for (FieldRangeHashmap::iterator itr = _vectorCompCellDataRange[i].begin();
3604             itr != _vectorCompCellDataRange[i].end(); ++itr) {
3605            delete [] itr->second;
3606        }
3607        _vectorCompCellDataRange[i].clear();
3608    }
3609    for (FieldRangeHashmap::iterator itr = _vectorFieldDataRange.begin();
3610         itr != _vectorFieldDataRange.end(); ++itr) {
3611        delete [] itr->second;
3612    }
3613    _vectorFieldDataRange.clear();
3614    for (int i = 0; i < 3; i++) {
3615        for (FieldRangeHashmap::iterator itr = _vectorCompFieldDataRange[i].begin();
3616             itr != _vectorCompFieldDataRange[i].end(); ++itr) {
3617            delete [] itr->second;
3618        }
3619        _vectorCompFieldDataRange[i].clear();
3620    }
3621}
3622
3623/**
3624 * \brief Clear user-defined field range hashtables and free memory
3625 */
3626void Renderer::clearUserFieldRanges()
3627{
3628    TRACE("Deleting User Field Ranges");
3629    for (FieldRangeHashmap::iterator itr = _userScalarPointDataRange.begin();
3630         itr != _userScalarPointDataRange.end(); ++itr) {
3631        delete [] itr->second;
3632    }
3633    _userScalarPointDataRange.clear();
3634    for (FieldRangeHashmap::iterator itr = _userScalarCellDataRange.begin();
3635         itr != _userScalarCellDataRange.end(); ++itr) {
3636        delete [] itr->second;
3637    }
3638    _userScalarCellDataRange.clear();
3639    for (FieldRangeHashmap::iterator itr = _userScalarFieldDataRange.begin();
3640         itr != _userScalarFieldDataRange.end(); ++itr) {
3641        delete [] itr->second;
3642    }
3643    _userScalarFieldDataRange.clear();
3644    for (FieldRangeHashmap::iterator itr = _userVectorPointDataRange.begin();
3645         itr != _userVectorPointDataRange.end(); ++itr) {
3646        delete [] itr->second;
3647    }
3648    _userVectorPointDataRange.clear();
3649    for (int i = 0; i < 3; i++) {
3650        for (FieldRangeHashmap::iterator itr = _userVectorCompPointDataRange[i].begin();
3651             itr != _userVectorCompPointDataRange[i].end(); ++itr) {
3652            delete [] itr->second;
3653        }
3654        _userVectorCompPointDataRange[i].clear();
3655    }
3656    for (FieldRangeHashmap::iterator itr = _userVectorCellDataRange.begin();
3657         itr != _userVectorCellDataRange.end(); ++itr) {
3658        delete [] itr->second;
3659    }
3660    _userVectorCellDataRange.clear();
3661    for (int i = 0; i < 3; i++) {
3662        for (FieldRangeHashmap::iterator itr = _userVectorCompCellDataRange[i].begin();
3663             itr != _userVectorCompCellDataRange[i].end(); ++itr) {
3664            delete [] itr->second;
3665        }
3666        _userVectorCompCellDataRange[i].clear();
3667    }
3668    for (FieldRangeHashmap::iterator itr = _userVectorFieldDataRange.begin();
3669         itr != _userVectorFieldDataRange.end(); ++itr) {
3670        delete [] itr->second;
3671    }
3672    _userVectorFieldDataRange.clear();
3673    for (int i = 0; i < 3; i++) {
3674        for (FieldRangeHashmap::iterator itr = _userVectorCompFieldDataRange[i].begin();
3675             itr != _userVectorCompFieldDataRange[i].end(); ++itr) {
3676            delete [] itr->second;
3677        }
3678        _userVectorCompFieldDataRange[i].clear();
3679    }
3680}
3681
3682/**
3683 * \brief Set up hashtables for min/max values of all fields from loaded
3684 * datasets
3685 *
3686 * Note that this method does not set the ranges, it just creates the table
3687 * entries
3688 */
3689void Renderer::initFieldRanges()
3690{
3691    clearFieldRanges();
3692
3693    for (DataSetHashmap::iterator itr = _dataSets.begin();
3694         itr != _dataSets.end(); ++itr) {
3695        DataSet *ds = itr->second;
3696        std::vector<std::string> names;
3697        ds->getFieldNames(names, DataSet::POINT_DATA, 1);
3698        for (std::vector<std::string>::iterator itr = names.begin();
3699             itr != names.end(); ++itr) {
3700            FieldRangeHashmap::iterator fritr =
3701                _scalarPointDataRange.find(*itr);
3702            if (fritr == _scalarPointDataRange.end()) {
3703                _scalarPointDataRange[*itr] = new double[2];
3704                _scalarPointDataRange[*itr][0] = 0;
3705                _scalarPointDataRange[*itr][1] = 1;
3706            }
3707        }
3708        names.clear();
3709        ds->getFieldNames(names, DataSet::CELL_DATA, 1);
3710        for (std::vector<std::string>::iterator itr = names.begin();
3711             itr != names.end(); ++itr) {
3712            FieldRangeHashmap::iterator fritr =
3713                _scalarCellDataRange.find(*itr);
3714            if (fritr == _scalarCellDataRange.end()) {
3715                _scalarCellDataRange[*itr] = new double[2];
3716                _scalarCellDataRange[*itr][0] = 0;
3717                _scalarCellDataRange[*itr][1] = 1;
3718            }
3719        }
3720        names.clear();
3721        ds->getFieldNames(names, DataSet::FIELD_DATA, 1);
3722        for (std::vector<std::string>::iterator itr = names.begin();
3723             itr != names.end(); ++itr) {
3724            FieldRangeHashmap::iterator fritr =
3725                _scalarFieldDataRange.find(*itr);
3726            if (fritr == _scalarFieldDataRange.end()) {
3727                _scalarFieldDataRange[*itr] = new double[2];
3728                _scalarFieldDataRange[*itr][0] = 0;
3729                _scalarFieldDataRange[*itr][1] = 1;
3730            }
3731        }
3732        names.clear();
3733        ds->getFieldNames(names, DataSet::POINT_DATA, 3);
3734        for (std::vector<std::string>::iterator itr = names.begin();
3735             itr != names.end(); ++itr) {
3736            FieldRangeHashmap::iterator fritr =
3737                _vectorPointDataRange.find(*itr);
3738            if (fritr == _vectorPointDataRange.end()) {
3739                _vectorPointDataRange[*itr] = new double[2];
3740                _vectorPointDataRange[*itr][0] = 0;
3741                _vectorPointDataRange[*itr][1] = 1;
3742            }
3743            for (int i = 0; i < 3; i++) {
3744                fritr = _vectorCompPointDataRange[i].find(*itr);
3745                if (fritr == _vectorCompPointDataRange[i].end()) {
3746                    _vectorCompPointDataRange[i][*itr] = new double[2];
3747                    _vectorCompPointDataRange[i][*itr][0] = 0;
3748                    _vectorCompPointDataRange[i][*itr][1] = 1;
3749                }
3750            }
3751        }
3752        names.clear();
3753        ds->getFieldNames(names, DataSet::CELL_DATA, 3);
3754        for (std::vector<std::string>::iterator itr = names.begin();
3755             itr != names.end(); ++itr) {
3756            FieldRangeHashmap::iterator fritr =
3757                _vectorCellDataRange.find(*itr);
3758            if (fritr == _vectorCellDataRange.end()) {
3759                _vectorCellDataRange[*itr] = new double[2];
3760                _vectorCellDataRange[*itr][0] = 0;
3761                _vectorCellDataRange[*itr][1] = 1;
3762            }
3763            for (int i = 0; i < 3; i++) {
3764                fritr = _vectorCompCellDataRange[i].find(*itr);
3765                if (fritr == _vectorCompCellDataRange[i].end()) {
3766                    _vectorCompCellDataRange[i][*itr] = new double[2];
3767                    _vectorCompCellDataRange[i][*itr][0] = 0;
3768                    _vectorCompCellDataRange[i][*itr][1] = 1;
3769                }
3770            }
3771        }
3772        names.clear();
3773        ds->getFieldNames(names, DataSet::FIELD_DATA, 3);
3774        for (std::vector<std::string>::iterator itr = names.begin();
3775             itr != names.end(); ++itr) {
3776            FieldRangeHashmap::iterator fritr =
3777                _vectorFieldDataRange.find(*itr);
3778            if (fritr == _vectorFieldDataRange.end()) {
3779                _vectorFieldDataRange[*itr] = new double[2];
3780                _vectorFieldDataRange[*itr][0] = 0;
3781                _vectorFieldDataRange[*itr][1] = 1;
3782            }
3783            for (int i = 0; i < 3; i++) {
3784                fritr = _vectorCompFieldDataRange[i].find(*itr);
3785                if (fritr == _vectorCompFieldDataRange[i].end()) {
3786                    _vectorCompFieldDataRange[i][*itr] = new double[2];
3787                    _vectorCompFieldDataRange[i][*itr][0] = 0;
3788                    _vectorCompFieldDataRange[i][*itr][1] = 1;
3789                }
3790            }
3791        }
3792    }
3793}
3794
3795/**
3796 * \brief Returns boolean flag indicating if cumulative data ranges
3797 * should be used
3798 */
3799bool Renderer::getUseCumulativeRange()
3800{
3801    return _useCumulativeRange;
3802}
3803
3804/**
3805 * \brief Set explicit range to use for mapping fields
3806 */
3807bool Renderer::setCumulativeDataRange(double *range, const char *name,
3808                                      DataSet::DataAttributeType type,
3809                                      int numComponents,
3810                                      int component)
3811{
3812    if (range == NULL || name == NULL)
3813        return false;
3814
3815    _useCumulativeRange = true;
3816    bool found = false;
3817
3818    switch (type) {
3819    case DataSet::POINT_DATA: {
3820        FieldRangeHashmap::iterator itr;
3821        if (numComponents == 1) {
3822            itr = _userScalarPointDataRange.find(name);
3823            if (itr == _userScalarPointDataRange.end()) {
3824                _userScalarPointDataRange[name] = new double[2];
3825                memcpy(_userScalarPointDataRange[name], range, sizeof(double)*2);
3826            } else {
3827                found = true;
3828                memcpy(itr->second, range, sizeof(double)*2);
3829            }
3830        } else if (numComponents == 3) {
3831            if (component == -1) {
3832                itr = _userVectorPointDataRange.find(name);
3833                if (itr == _userVectorPointDataRange.end()) {
3834                    _userVectorPointDataRange[name] = new double[2];
3835                    memcpy(itr->second, _userVectorPointDataRange[name], sizeof(double)*2);
3836                } else {
3837                    found = true;
3838                    memcpy(itr->second, range, sizeof(double)*2);
3839                }
3840                return found;
3841            } else if (component >= 0 && component <= 3) {
3842                itr = _userVectorCompPointDataRange[component].find(name);
3843                if (itr == _userVectorCompPointDataRange[component].end()) {
3844                    _userVectorCompPointDataRange[component][name] = new double[2];
3845                    memcpy(_userVectorCompPointDataRange[component][name], range, sizeof(double)*2);
3846                } else {
3847                    found = true;
3848                    memcpy(itr->second, range, sizeof(double)*2);
3849                }
3850            }
3851        }
3852    }
3853        break;
3854    case DataSet::CELL_DATA: {
3855        FieldRangeHashmap::iterator itr;
3856        if (numComponents == 1) {
3857            itr = _userScalarCellDataRange.find(name);
3858            if (itr == _userScalarCellDataRange.end()) {
3859                _userScalarCellDataRange[name] = new double[2];
3860                memcpy(_userScalarCellDataRange[name], range, sizeof(double)*2);
3861            } else {
3862                found = true;
3863                memcpy(itr->second, range, sizeof(double)*2);
3864            }
3865        } else if (numComponents == 3) {
3866            if (component == -1) {
3867                itr = _userVectorCellDataRange.find(name);
3868                if (itr == _userVectorCellDataRange.end()) {
3869                    _userVectorCellDataRange[name] = new double[2];
3870                    memcpy(_userVectorCellDataRange[name], range, sizeof(double)*2);
3871                } else {
3872                    found = true;
3873                    memcpy(itr->second, range, sizeof(double)*2);
3874                }
3875            } else if (component >= 0 && component <= 3) {
3876                itr = _userVectorCompCellDataRange[component].find(name);
3877                if (itr == _userVectorCompCellDataRange[component].end()) {
3878                    _userVectorCompCellDataRange[component][name] = new double[2];
3879                    memcpy(_userVectorCompCellDataRange[component][name], range, sizeof(double)*2);
3880                } else {
3881                    found = true;
3882                    memcpy(itr->second, range, sizeof(double)*2);
3883                }
3884            }
3885        }
3886    }
3887        break;
3888    case DataSet::FIELD_DATA: {
3889        FieldRangeHashmap::iterator itr;
3890        if (numComponents == 1) {
3891            itr = _userScalarFieldDataRange.find(name);
3892            if (itr == _userScalarFieldDataRange.end()) {
3893                _userScalarFieldDataRange[name] = new double[2];
3894                memcpy(_userScalarFieldDataRange[name], range, sizeof(double)*2);
3895            } else {
3896                found = true;
3897                memcpy(itr->second, range, sizeof(double)*2);
3898            }
3899        } else if (numComponents == 3) {
3900            if (component == -1) {
3901                itr = _userVectorFieldDataRange.find(name);
3902                if (itr == _userVectorFieldDataRange.end()) {
3903                    _userVectorFieldDataRange[name] = new double[2];
3904                    memcpy(_userVectorFieldDataRange[name], range, sizeof(double)*2);
3905                } else {
3906                    found = true;
3907                    memcpy(itr->second, range, sizeof(double)*2);
3908                }
3909            } else if (component >= 0 && component <= 3) {
3910                itr = _userVectorCompFieldDataRange[component].find(name);
3911                if (itr == _userVectorCompFieldDataRange[component].end()) {
3912                    _userVectorCompFieldDataRange[component][name] = new double[2];
3913                    memcpy(_userVectorCompFieldDataRange[component][name], range, sizeof(double)*2);
3914                } else {
3915                    found = true;
3916                    memcpy(itr->second, range, sizeof(double)*2);
3917                }
3918            }
3919        }
3920    }
3921        break;
3922    default:
3923        ERROR("Bad Field Type");
3924    }
3925
3926    // Notify graphics objects of new ranges
3927    updateFieldRanges();
3928    // Bounds may have changed
3929    sceneBoundsChanged();
3930    _needsRedraw = true;
3931
3932    TRACE("Field: %s found: %d range: %g %g", name, (found ? 1 : 0), range[0], range[1]);
3933    return found;
3934}
3935
3936/**
3937 * \brief Get the cumulative range across all DataSets for a point
3938 * data field if it exists, otherwise a cell data field if it exists,
3939 * otherwise a field data field if it exists
3940 *
3941 * \param[out] range Pointer to an array of 2 doubles
3942 * \param[in] name Field name
3943 * \param[in] numComponents Number of components in field
3944 * \param[in] component Index of component or -1 for magnitude/scalar
3945 * \return boolean indicating if field was found
3946 */
3947bool Renderer::getCumulativeDataRange(double *range, const char *name,
3948                                      int numComponents,
3949                                      int component)
3950{
3951    bool ret;
3952    if ((ret = getCumulativeDataRange(range, name, DataSet::POINT_DATA,
3953                                      numComponents, component))) {
3954        ; // Found point data
3955    } else if ((ret = getCumulativeDataRange(range, name, DataSet::CELL_DATA,
3956                                             numComponents, component))) {
3957        ; // Found cell data
3958       
3959    } else {
3960        ret = getCumulativeDataRange(range, name, DataSet::FIELD_DATA,
3961                                     numComponents, component);
3962    }
3963    return ret;
3964}
3965
3966/**
3967 * \brief Get the cumulative range across all DataSets for a field
3968 *
3969 * \param[out] range Pointer to an array of 2 doubles
3970 * \param[in] name Field name
3971 * \param[in] type DataAttributeType of field
3972 * \param[in] numComponents Number of components in field
3973 * \param[in] component Index of component or -1 for magnitude/scalar
3974 * \return boolean indicating if field was found
3975 */
3976bool Renderer::getCumulativeDataRange(double *range, const char *name,
3977                                      DataSet::DataAttributeType type,
3978                                      int numComponents,
3979                                      int component)
3980{
3981    if (range == NULL || name == NULL)
3982        return false;
3983
3984    switch (type) {
3985    case DataSet::POINT_DATA: {
3986        FieldRangeHashmap::iterator itr;
3987        if (numComponents == 1) {
3988            itr = _userScalarPointDataRange.find(name);
3989            if (itr == _userScalarPointDataRange.end()) {
3990                itr = _scalarPointDataRange.find(name);
3991                if (itr == _scalarPointDataRange.end()) {
3992                    return false;
3993                }
3994            }
3995            memcpy(range, itr->second, sizeof(double)*2);
3996            return true;
3997        } else if (numComponents == 3) {
3998            if (component == -1) {
3999                itr = _userVectorPointDataRange.find(name);
4000                if (itr == _userVectorPointDataRange.end()) {
4001                    itr = _vectorPointDataRange.find(name);
4002                    if (itr == _vectorPointDataRange.end()) {
4003                        return false;
4004                    }
4005                }
4006                memcpy(range, itr->second, sizeof(double)*2);
4007                return true;
4008            } else if (component >= 0 && component <= 3) {
4009                itr = _userVectorCompPointDataRange[component].find(name);
4010                if (itr == _userVectorCompPointDataRange[component].end()) {
4011                    itr = _vectorCompPointDataRange[component].find(name);
4012                    if (itr == _vectorCompPointDataRange[component].end()) {
4013                        return false;
4014                    }
4015                }
4016                memcpy(range, itr->second, sizeof(double)*2);
4017                return true;
4018            }
4019        }
4020    }
4021        break;
4022    case DataSet::CELL_DATA: {
4023        FieldRangeHashmap::iterator itr;
4024        if (numComponents == 1) {
4025            itr = _userScalarCellDataRange.find(name);
4026            if (itr == _userScalarCellDataRange.end()) {
4027                itr = _scalarCellDataRange.find(name);
4028                if (itr == _scalarCellDataRange.end()) {
4029                    return false;
4030                }
4031            }
4032            memcpy(range, itr->second, sizeof(double)*2);
4033            return true;
4034        } else if (numComponents == 3) {
4035            if (component == -1) {
4036                itr = _userVectorCellDataRange.find(name);
4037                if (itr == _userVectorCellDataRange.end()) {
4038                    itr = _vectorCellDataRange.find(name);
4039                    if (itr == _vectorCellDataRange.end()) {
4040                        return false;
4041                    }
4042                }
4043                memcpy(range, itr->second, sizeof(double)*2);
4044                return true;
4045            } else if (component >= 0 && component <= 3) {
4046                itr = _userVectorCompCellDataRange[component].find(name);
4047                if (itr == _userVectorCompCellDataRange[component].end()) {
4048                    itr = _vectorCompCellDataRange[component].find(name);
4049                    if (itr == _vectorCompCellDataRange[component].end()) {
4050                        return false;
4051                    }
4052                }
4053                memcpy(range, itr->second, sizeof(double)*2);
4054                return true;
4055            }
4056        }
4057    }
4058        break;
4059    case DataSet::FIELD_DATA: {
4060        FieldRangeHashmap::iterator itr;
4061        if (numComponents == 1) {
4062            itr = _userScalarFieldDataRange.find(name);
4063            if (itr == _userScalarFieldDataRange.end()) {
4064                itr = _scalarFieldDataRange.find(name);
4065                if (itr == _scalarFieldDataRange.end()) {
4066                    return false;
4067                }
4068            }
4069            memcpy(range, itr->second, sizeof(double)*2);
4070            return true;
4071        } else if (numComponents == 3) {
4072            if (component == -1) {
4073                itr = _userVectorFieldDataRange.find(name);
4074                if (itr == _userVectorFieldDataRange.end()) {
4075                    itr = _vectorFieldDataRange.find(name);
4076                    if (itr == _vectorFieldDataRange.end()) {
4077                        return false;
4078                    }
4079                }
4080                memcpy(range, itr->second, sizeof(double)*2);
4081                return true;
4082            } else if (component >= 0 && component <= 3) {
4083                itr = _userVectorCompFieldDataRange[component].find(name);
4084                if (itr == _userVectorCompFieldDataRange[component].end()) {
4085                    itr = _vectorCompFieldDataRange[component].find(name);
4086                    if (itr == _vectorCompFieldDataRange[component].end()) {
4087                        return false;
4088                    }
4089                }
4090                memcpy(range, itr->second, sizeof(double)*2);
4091                return true;
4092            }
4093        }
4094    }
4095        break;
4096    default:
4097        break;
4098    }
4099    return false;
4100}
4101
4102void Renderer::collectDataRanges()
4103{
4104    for (FieldRangeHashmap::iterator itr = _scalarPointDataRange.begin();
4105         itr != _scalarPointDataRange.end(); ++itr) {
4106        collectDataRanges(itr->second, itr->first.c_str(),
4107                          DataSet::POINT_DATA, -1,
4108                          _cumulativeRangeOnlyVisible);
4109    }
4110    for (FieldRangeHashmap::iterator itr = _scalarCellDataRange.begin();
4111         itr != _scalarCellDataRange.end(); ++itr) {
4112        collectDataRanges(itr->second, itr->first.c_str(),
4113                          DataSet::CELL_DATA, -1,
4114                          _cumulativeRangeOnlyVisible);
4115    }
4116    for (FieldRangeHashmap::iterator itr = _scalarFieldDataRange.begin();
4117         itr != _scalarFieldDataRange.end(); ++itr) {
4118        collectDataRanges(itr->second, itr->first.c_str(),
4119                          DataSet::FIELD_DATA, -1,
4120                          _cumulativeRangeOnlyVisible);
4121    }
4122    for (FieldRangeHashmap::iterator itr = _vectorPointDataRange.begin();
4123         itr != _vectorPointDataRange.end(); ++itr) {
4124        collectDataRanges(itr->second, itr->first.c_str(),
4125                          DataSet::POINT_DATA, -1,
4126                          _cumulativeRangeOnlyVisible);
4127    }
4128    for (int i = 0; i < 3; i++) {
4129        for (FieldRangeHashmap::iterator itr = _vectorCompPointDataRange[i].begin();
4130             itr != _vectorCompPointDataRange[i].end(); ++itr) {
4131            collectDataRanges(itr->second, itr->first.c_str(),
4132                              DataSet::POINT_DATA, i,
4133                              _cumulativeRangeOnlyVisible);
4134        }
4135    }
4136    for (FieldRangeHashmap::iterator itr = _vectorCellDataRange.begin();
4137         itr != _vectorCellDataRange.end(); ++itr) {
4138        collectDataRanges(itr->second, itr->first.c_str(),
4139                          DataSet::CELL_DATA, -1,
4140                          _cumulativeRangeOnlyVisible);
4141    }
4142    for (int i = 0; i < 3; i++) {
4143        for (FieldRangeHashmap::iterator itr = _vectorCompCellDataRange[i].begin();
4144             itr != _vectorCompCellDataRange[i].end(); ++itr) {
4145            collectDataRanges(itr->second, itr->first.c_str(),
4146                              DataSet::CELL_DATA, i,
4147                              _cumulativeRangeOnlyVisible);
4148        }
4149    }
4150    for (FieldRangeHashmap::iterator itr = _vectorFieldDataRange.begin();
4151         itr != _vectorFieldDataRange.end(); ++itr) {
4152        collectDataRanges(itr->second, itr->first.c_str(),
4153                          DataSet::FIELD_DATA, -1,
4154                          _cumulativeRangeOnlyVisible);
4155    }
4156    for (int i = 0; i < 3; i++) {
4157        for (FieldRangeHashmap::iterator itr = _vectorCompFieldDataRange[i].begin();
4158             itr != _vectorCompFieldDataRange[i].end(); ++itr) {
4159            collectDataRanges(itr->second, itr->first.c_str(),
4160                              DataSet::FIELD_DATA, i,
4161                              _cumulativeRangeOnlyVisible);
4162        }
4163    }
4164}
4165
4166/**
4167 * \brief Determines if AABB lies in a principal axis plane
4168 * and if so, returns the plane normal
4169 */
4170bool Renderer::is2D(const double bounds[6],
4171                    PrincipalPlane *plane,
4172                    double *offset) const
4173{
4174    if (bounds[4] == bounds[5]) {
4175        // Z = 0, XY plane
4176        if (plane)
4177            *plane = PLANE_XY;
4178        if (offset)
4179            *offset = bounds[4];
4180        return true;
4181    } else if (bounds[0] == bounds[1]) {
4182        // X = 0, ZY plane
4183        if (plane)
4184            *plane = PLANE_ZY;
4185        if (offset)
4186            *offset = bounds[0];
4187        return true;
4188    } else if (bounds[2] == bounds[3]) {
4189        // Y = 0, XZ plane
4190        if (plane)
4191            *plane = PLANE_XZ;
4192        if (offset)
4193            *offset = bounds[2];
4194        return true;
4195    }
4196    *plane = PLANE_XY;
4197    *offset = 0;
4198    return false;
4199}
4200
4201int Renderer::addLight(float pos[3])
4202{
4203    vtkSmartPointer<vtkLight> light = vtkSmartPointer<vtkLight>::New();
4204    light->SetLightTypeToCameraLight();
4205    light->SetPosition(pos[0], pos[1], pos[2]);
4206    light->SetFocalPoint(0, 0, 0);
4207    light->PositionalOff();
4208    _renderer->AddLight(light);
4209    _needsRedraw = true;
4210    return (_renderer->GetLights()->GetNumberOfItems()-1);
4211}
4212
4213vtkLight *Renderer::getLight(int lightIdx)
4214{
4215    vtkLightCollection *lights = _renderer->GetLights();
4216    if (lights->GetNumberOfItems() < lightIdx+1)
4217        return NULL;
4218    lights->InitTraversal();
4219    vtkLight *light = NULL;
4220    int i = 0;
4221    do {
4222        light = lights->GetNextItem();
4223    } while (i++ < lightIdx);
4224    return light;
4225}
4226
4227void Renderer::setLightSwitch(int lightIdx, bool state)
4228{
4229    vtkLight *light = getLight(lightIdx);
4230    if (light == NULL) {
4231        ERROR("Unknown light %d", lightIdx);
4232        return;
4233    }
4234    light->SetSwitch((state ? 1 : 0));
4235    _needsRedraw = true;
4236}
4237
4238/**
4239 * \brief Initialize the camera zoom region to include the bounding volume given
4240 */
4241void Renderer::initCamera(bool initCameraMode)
4242{
4243#ifdef WANT_TRACE
4244    switch (_cameraMode) {
4245    case IMAGE:
4246        TRACE("Image camera");
4247        break;
4248    case ORTHO:
4249        TRACE("Ortho camera");
4250        break;
4251    case PERSPECTIVE:
4252        TRACE("Perspective camera");
4253        break;
4254    default:
4255        TRACE("Unknown camera mode");
4256    }
4257#endif
4258    // Clear user requested zoom region
4259    _userImgWorldOrigin[0] = 0;
4260    _userImgWorldOrigin[1] = 0;
4261    _userImgWorldDims[0] = -1;
4262    _userImgWorldDims[1] = -1;
4263
4264    double bounds[6];
4265    collectBounds(bounds, false);
4266    bool twod = is2D(bounds, &_imgCameraPlane, &_imgCameraOffset);
4267    if (twod) {
4268        if (initCameraMode) {
4269            TRACE("Changing camera mode to image");
4270            _cameraMode = IMAGE;
4271        }
4272        if (_imgCameraPlane == PLANE_ZY) {
4273            _imgWorldOrigin[0] = bounds[4];
4274            _imgWorldOrigin[1] = bounds[2];
4275            _imgWorldDims[0] = bounds[5] - bounds[4];
4276            _imgWorldDims[1] = bounds[3] - bounds[2];
4277        } else if (_imgCameraPlane == PLANE_XZ) {
4278            _imgWorldOrigin[0] = bounds[0];
4279            _imgWorldOrigin[1] = bounds[4];
4280            _imgWorldDims[0] = bounds[1] - bounds[0];
4281            _imgWorldDims[1] = bounds[5] - bounds[4];
4282        } else {
4283            _imgWorldOrigin[0] = bounds[0];
4284            _imgWorldOrigin[1] = bounds[2];
4285            _imgWorldDims[0] = bounds[1] - bounds[0];
4286            _imgWorldDims[1] = bounds[3] - bounds[2];
4287        }
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
4295    _cameraPan[0] = 0;
4296    _cameraPan[1] = 0;
4297    _cameraZoomRatio = 1;
4298
4299    switch (_cameraMode) {
4300    case IMAGE:
4301        //_renderer->ResetCamera(bounds);
4302        _setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
4303                             _imgWorldDims[0], _imgWorldDims[1]);
4304        resetAxes(bounds);
4305        break;
4306    case ORTHO:
4307        _renderer->GetActiveCamera()->ParallelProjectionOn();
4308        resetAxes(bounds);
4309        //_renderer->ResetCamera(bounds);
4310        resetVtkCamera();
4311        //computeScreenWorldCoords();
4312        break;
4313    case PERSPECTIVE:
4314        _renderer->GetActiveCamera()->ParallelProjectionOff();
4315        resetAxes(bounds);
4316        //_renderer->ResetCamera(bounds);
4317        resetVtkCamera();
4318        //computeScreenWorldCoords();
4319        break;
4320    default:
4321        ERROR("Unknown camera mode");
4322    }
4323
4324#ifdef WANT_TRACE
4325    printCameraInfo(_renderer->GetActiveCamera());
4326#endif
4327}
4328
4329#if 0
4330/**
4331 * \brief Print debugging info about a vtkCamera
4332 */
4333void Renderer::printCameraInfo(vtkCamera *camera)
4334{
4335    TRACE("pscale: %g, angle: %g, d: %g pos: %g %g %g, fpt: %g %g %g, vup: %g %g %g, clip: %g %g",
4336          camera->GetParallelScale(),
4337          camera->GetViewAngle(),
4338          camera->GetDistance(),
4339          camera->GetPosition()[0],
4340          camera->GetPosition()[1],
4341          camera->GetPosition()[2],
4342          camera->GetFocalPoint()[0],
4343          camera->GetFocalPoint()[1],
4344          camera->GetFocalPoint()[2],
4345          camera->GetViewUp()[0],
4346          camera->GetViewUp()[1],
4347          camera->GetViewUp()[2],
4348          camera->GetClippingRange()[0],
4349          camera->GetClippingRange()[1]);
4350}
4351#endif
4352
4353/**
4354 * \brief Set the RGB background color to render into the image
4355 */
4356void Renderer::setBackgroundColor(float color[3])
4357{
4358    _bgColor[0] = color[0];
4359    _bgColor[1] = color[1];
4360    _bgColor[2] = color[2];
4361    _renderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
4362    _needsRedraw = true;
4363}
4364
4365/**
4366 * \brief Set the opacity of the specified DataSet's associated graphics objects
4367 */
4368void Renderer::setDataSetOpacity(const DataSetId& id, double opacity)
4369{
4370    DataSetHashmap::iterator itr;
4371
4372    bool doAll = false;
4373
4374    if (id.compare("all") == 0) {
4375        itr = _dataSets.begin();
4376        if (itr == _dataSets.end())
4377            return;
4378        doAll = true;
4379    } else {
4380        itr = _dataSets.find(id);
4381    }
4382    if (itr == _dataSets.end()) {
4383        ERROR("Unknown dataset %s", id.c_str());
4384        return;
4385    }
4386
4387    do {
4388        itr->second->setOpacity(opacity);
4389    } while (doAll && ++itr != _dataSets.end());
4390
4391    if (id.compare("all") == 0 || getGraphicsObject<Contour2D>(id) != NULL)
4392        setGraphicsObjectOpacity<Contour2D>(id, opacity);
4393    if (id.compare("all") == 0 || getGraphicsObject<Contour3D>(id) != NULL)
4394        setGraphicsObjectOpacity<Contour3D>(id, opacity);
4395    if (id.compare("all") == 0 || getGraphicsObject<Cutplane>(id) != NULL)
4396        setGraphicsObjectOpacity<Cutplane>(id, opacity);
4397    if (id.compare("all") == 0 || getGraphicsObject<Glyphs>(id) != NULL)
4398        setGraphicsObjectOpacity<Glyphs>(id, opacity);
4399    if (id.compare("all") == 0 || getGraphicsObject<HeightMap>(id) != NULL)
4400        setGraphicsObjectOpacity<HeightMap>(id, opacity);
4401    if (id.compare("all") == 0 || getGraphicsObject<Image>(id) != NULL)
4402        setGraphicsObjectOpacity<Image>(id, opacity);
4403    if (id.compare("all") == 0 || getGraphicsObject<LIC>(id) != NULL)
4404        setGraphicsObjectOpacity<LIC>(id, opacity);
4405    if (id.compare("all") == 0 || getGraphicsObject<Molecule>(id) != NULL)
4406        setGraphicsObjectOpacity<Molecule>(id, opacity);
4407    if (id.compare("all") == 0 || getGraphicsObject<Outline>(id) != NULL)
4408        setGraphicsObjectOpacity<Outline>(id, opacity);
4409    if (id.compare("all") == 0 || getGraphicsObject<PolyData>(id) != NULL)
4410        setGraphicsObjectOpacity<PolyData>(id, opacity);
4411    if (id.compare("all") == 0 || getGraphicsObject<PseudoColor>(id) != NULL)
4412        setGraphicsObjectOpacity<PseudoColor>(id, opacity);
4413    if (id.compare("all") == 0 || getGraphicsObject<Streamlines>(id) != NULL)
4414        setGraphicsObjectOpacity<Streamlines>(id, opacity);
4415    if (id.compare("all") == 0 || getGraphicsObject<Volume>(id) != NULL)
4416        setGraphicsObjectOpacity<Volume>(id, opacity);
4417    if (id.compare("all") == 0 || getGraphicsObject<Warp>(id) != NULL)
4418        setGraphicsObjectOpacity<Warp>(id, opacity);
4419
4420    _needsRedraw = true;
4421}
4422
4423/**
4424 * \brief Turn on/off rendering of the specified DataSet's associated graphics objects
4425 */
4426void Renderer::setDataSetVisibility(const DataSetId& id, bool state)
4427{
4428    DataSetHashmap::iterator itr;
4429
4430    bool doAll = false;
4431
4432    if (id.compare("all") == 0) {
4433        itr = _dataSets.begin();
4434        doAll = true;
4435        if (itr == _dataSets.end())
4436            return;
4437    } else {
4438        itr = _dataSets.find(id);
4439    }
4440    if (itr == _dataSets.end()) {
4441        ERROR("Unknown dataset %s", id.c_str());
4442        return;
4443    }
4444
4445    do {
4446        itr->second->setVisibility(state);
4447    } while (doAll && ++itr != _dataSets.end());
4448
4449    if (id.compare("all") == 0 || getGraphicsObject<Contour2D>(id) != NULL)
4450        setGraphicsObjectVisibility<Contour2D>(id, state);
4451    if (id.compare("all") == 0 || getGraphicsObject<Contour3D>(id) != NULL)
4452        setGraphicsObjectVisibility<Contour3D>(id, state);
4453    if (id.compare("all") == 0 || getGraphicsObject<Cutplane>(id) != NULL)
4454        setGraphicsObjectVisibility<Cutplane>(id, state);
4455    if (id.compare("all") == 0 || getGraphicsObject<Glyphs>(id) != NULL)
4456        setGraphicsObjectVisibility<Glyphs>(id, state);
4457    if (id.compare("all") == 0 || getGraphicsObject<HeightMap>(id) != NULL)
4458        setGraphicsObjectVisibility<HeightMap>(id, state);
4459    if (id.compare("all") == 0 || getGraphicsObject<Image>(id) != NULL)
4460        setGraphicsObjectVisibility<Image>(id, state);
4461    if (id.compare("all") == 0 || getGraphicsObject<LIC>(id) != NULL)
4462        setGraphicsObjectVisibility<LIC>(id, state);
4463    if (id.compare("all") == 0 || getGraphicsObject<Molecule>(id) != NULL)
4464        setGraphicsObjectVisibility<Molecule>(id, state);
4465    if (id.compare("all") == 0 || getGraphicsObject<Outline>(id) != NULL)
4466        setGraphicsObjectVisibility<Outline>(id, state);
4467    if (id.compare("all") == 0 || getGraphicsObject<PolyData>(id) != NULL)
4468        setGraphicsObjectVisibility<PolyData>(id, state);
4469    if (id.compare("all") == 0 || getGraphicsObject<PseudoColor>(id) != NULL)
4470        setGraphicsObjectVisibility<PseudoColor>(id, state);
4471    if (id.compare("all") == 0 || getGraphicsObject<Streamlines>(id) != NULL)
4472        setGraphicsObjectVisibility<Streamlines>(id, state);
4473    if (id.compare("all") == 0 || getGraphicsObject<Volume>(id) != NULL)
4474        setGraphicsObjectVisibility<Volume>(id, state);
4475    if (id.compare("all") == 0 || getGraphicsObject<Warp>(id) != NULL)
4476        setGraphicsObjectVisibility<Warp>(id, state);
4477
4478    _needsRedraw = true;
4479}
4480
4481/**
4482 * \brief Set a user clipping plane
4483 *
4484 * TODO: Fix clip plane positions after a change in actor bounds
4485 */
4486void Renderer::setClipPlane(Axis axis, double ratio, int direction)
4487{
4488    double bounds[6];
4489    collectBounds(bounds, false);
4490
4491    switch (axis) {
4492    case X_AXIS:
4493        if (direction > 0) {
4494            if (ratio > 0.0) {
4495                if (_userClipPlanes[0] == NULL) {
4496                    _userClipPlanes[0] = vtkSmartPointer<vtkPlane>::New();
4497                    _userClipPlanes[0]->SetNormal(1, 0, 0);
4498                }
4499                _userClipPlanes[0]->SetOrigin(bounds[0] + (bounds[1]-bounds[0])*ratio, 0, 0);
4500            } else {
4501                _userClipPlanes[0] = NULL;
4502            }
4503        } else {
4504            if (ratio < 1.0) {
4505                if (_userClipPlanes[1] == NULL) {
4506                    _userClipPlanes[1] = vtkSmartPointer<vtkPlane>::New();
4507                    _userClipPlanes[1]->SetNormal(-1, 0, 0);
4508                }
4509                _userClipPlanes[1]->SetOrigin(bounds[0] + (bounds[1]-bounds[0])*ratio, 0, 0);
4510            } else {
4511                _userClipPlanes[1] = NULL;
4512            }
4513        }
4514        break;
4515    case Y_AXIS:
4516        if (direction > 0) {
4517            if (ratio > 0.0) {
4518                if (_userClipPlanes[2] == NULL) {
4519                    _userClipPlanes[2] = vtkSmartPointer<vtkPlane>::New();
4520                    _userClipPlanes[2]->SetNormal(0, 1, 0);
4521                }
4522                _userClipPlanes[2]->SetOrigin(0, bounds[2] + (bounds[3]-bounds[2])*ratio, 0);
4523            } else {
4524                _userClipPlanes[2] = NULL;
4525            }
4526        } else {
4527            if (ratio < 1.0) {
4528                if (_userClipPlanes[3] == NULL) {
4529                    _userClipPlanes[3] = vtkSmartPointer<vtkPlane>::New();
4530                    _userClipPlanes[3]->SetNormal(0, -1, 0);
4531                }
4532                _userClipPlanes[3]->SetOrigin(0, bounds[2] + (bounds[3]-bounds[2])*ratio, 0);
4533            } else {
4534                _userClipPlanes[3] = NULL;
4535            }
4536        }
4537        break;
4538    case Z_AXIS:
4539        if (direction > 0) {
4540            if (ratio > 0.0) {
4541                if (_userClipPlanes[4] == NULL) {
4542                    _userClipPlanes[4] = vtkSmartPointer<vtkPlane>::New();
4543                    _userClipPlanes[4]->SetNormal(0, 0, 1);
4544                }
4545                _userClipPlanes[4]->SetOrigin(0, 0, bounds[4] + (bounds[5]-bounds[4])*ratio);
4546            } else {
4547                _userClipPlanes[4] = NULL;
4548            }
4549        } else {
4550            if (ratio < 1.0) {
4551                if (_userClipPlanes[5] == NULL) {
4552                    _userClipPlanes[5] = vtkSmartPointer<vtkPlane>::New();
4553                    _userClipPlanes[5]->SetNormal(0, 0, -1);
4554                }
4555                _userClipPlanes[5]->SetOrigin(0, 0, bounds[4] + (bounds[5]-bounds[4])*ratio);
4556            } else {
4557                _userClipPlanes[5] = NULL;
4558            }
4559        }
4560        break;
4561    default:
4562        ;
4563    }
4564
4565    _needsRedraw = true;
4566}
4567
4568/**
4569 * \brief Set up clipping planes for image camera mode if needed
4570 */
4571void Renderer::setCameraClippingPlanes()
4572{
4573    /* XXX: Note that there appears to be a bug with setting the
4574     * clipping plane collection to NULL in the VTK Mappers --
4575     * the old clip planes are still applied.  The workaround here
4576     * is to keep the PlaneCollection and add/remove the planes
4577     * to/from the PlaneCollection as needed.
4578     */
4579    if (_cameraMode == IMAGE) {
4580        if (_activeClipPlanes->GetNumberOfItems() == 0) {
4581            for (int i = 0; i < 4; i++)
4582                _activeClipPlanes->AddItem(_cameraClipPlanes[i]);
4583        }
4584    } else {
4585        if (_activeClipPlanes->GetNumberOfItems() > 0)
4586            _activeClipPlanes->RemoveAllItems();
4587        for (int i = 0; i < 6; i++) {
4588            if (_userClipPlanes[i] != NULL) {
4589                _activeClipPlanes->AddItem(_userClipPlanes[i]);
4590            }
4591        }
4592    }
4593
4594    /* Ensure all Mappers are using the PlaneCollection
4595     * This will not change the state or timestamp of
4596     * Mappers already using the PlaneCollection
4597     */
4598    setGraphicsObjectClippingPlanes<Arc>(_activeClipPlanes);
4599    setGraphicsObjectClippingPlanes<Arrow>(_activeClipPlanes);
4600    setGraphicsObjectClippingPlanes<Box>(_activeClipPlanes);
4601    setGraphicsObjectClippingPlanes<Cone>(_activeClipPlanes);
4602    setGraphicsObjectClippingPlanes<Contour2D>(_activeClipPlanes);
4603    setGraphicsObjectClippingPlanes<Contour3D>(_activeClipPlanes);
4604    setGraphicsObjectClippingPlanes<Cutplane>(_activeClipPlanes);
4605    setGraphicsObjectClippingPlanes<Cylinder>(_activeClipPlanes);
4606    setGraphicsObjectClippingPlanes<Disk>(_activeClipPlanes);
4607    setGraphicsObjectClippingPlanes<Glyphs>(_activeClipPlanes);
4608    setGraphicsObjectClippingPlanes<Group>(_activeClipPlanes);
4609    setGraphicsObjectClippingPlanes<HeightMap>(_activeClipPlanes);
4610    setGraphicsObjectClippingPlanes<Image>(_activeClipPlanes);
4611    setGraphicsObjectClippingPlanes<LIC>(_activeClipPlanes);
4612    setGraphicsObjectClippingPlanes<Line>(_activeClipPlanes);
4613    setGraphicsObjectClippingPlanes<Molecule>(_activeClipPlanes);
4614    setGraphicsObjectClippingPlanes<Outline>(_activeClipPlanes);
4615    setGraphicsObjectClippingPlanes<Parallelepiped>(_activeClipPlanes);
4616    setGraphicsObjectClippingPlanes<PolyData>(_activeClipPlanes);
4617    setGraphicsObjectClippingPlanes<Polygon>(_activeClipPlanes);
4618    setGraphicsObjectClippingPlanes<PseudoColor>(_activeClipPlanes);
4619    setGraphicsObjectClippingPlanes<Sphere>(_activeClipPlanes);
4620    setGraphicsObjectClippingPlanes<Streamlines>(_activeClipPlanes);
4621    setGraphicsObjectClippingPlanes<Text3D>(_activeClipPlanes);
4622    setGraphicsObjectClippingPlanes<Volume>(_activeClipPlanes);
4623    setGraphicsObjectClippingPlanes<Warp>(_activeClipPlanes);
4624}
4625
4626/**
4627 * \brief Control the use of two sided lighting
4628 */
4629void Renderer::setUseTwoSidedLighting(bool state)
4630{
4631    _renderer->SetTwoSidedLighting(state ? 1 : 0);
4632    _needsRedraw = true;
4633}
4634
4635/**
4636 * \brief Control parameters of depth peeling algorithm
4637 *
4638 * \param occlusionRatio define the threshold under which the algorithm
4639 * stops to iterate over peel layers. This is the ratio of the number of
4640 * pixels that have been touched by the last layer over the total number
4641 * of pixels of the viewport area. Initial value is 0.0, meaning rendering
4642 * have to be exact. Greater values may speed-up the rendering with small
4643 * impact on the quality.
4644 * \param maxPeels define the maximum number of peeling layers. Initial
4645 * value is 100. A special value of 0 means no maximum limit. It has to be
4646 * a positive value.
4647 */
4648void Renderer::setDepthPeelingParams(double occlusionRatio, int maxPeels)
4649{
4650    _renderer->SetOcclusionRatio(occlusionRatio);
4651    _renderer->SetMaximumNumberOfPeels(maxPeels);
4652    _needsRedraw = true;
4653}
4654
4655/**
4656 * \brief Control the use of the depth peeling algorithm for transparency
4657 */
4658void Renderer::setUseDepthPeeling(bool state)
4659{
4660    _renderer->SetUseDepthPeeling(state ? 1 : 0);
4661    _needsRedraw = true;
4662}
4663
4664/**
4665 * \brief Sets flag to trigger rendering next time render() is called
4666 */
4667void Renderer::eventuallyRender()
4668{
4669    _needsRedraw = true;
4670}
4671
4672/**
4673 * \brief Cause the rendering to render a new image if needed
4674 *
4675 * The _needsRedraw flag indicates if a state change has occured since
4676 * the last rendered frame
4677 */
4678bool Renderer::render()
4679{
4680    TRACE("Enter: redraw: %d axesReset: %d cameraReset: %d clippingRangeReset: %d",
4681          _needsRedraw ? 1 : 0, _needsAxesReset ? 1 : 0, _needsCameraReset ? 1 : 0, _needsCameraClippingRangeReset ? 1 : 0);
4682    if (_needsRedraw) {
4683         if (_needsAxesReset) {
4684            resetAxes();
4685            _needsAxesReset = false;
4686        }
4687        if (_needsCameraReset) {
4688            initCamera();
4689            _needsCameraReset = false;
4690            _needsCameraClippingRangeReset = false;
4691        } else if (_needsCameraClippingRangeReset && _cameraMode != IMAGE) {
4692            resetCameraClippingRange();
4693            _needsCameraClippingRangeReset = false;
4694        }
4695        setCameraClippingPlanes();
4696        _renderWindow->Render();
4697        int *sz = _renderWindow->GetSize();
4698        if (sz[0] != _windowWidth || sz[1] != _windowHeight) {
4699            ERROR("Window size: %dx%d, but expected %dx%d", sz[0], sz[1], _windowWidth, _windowHeight);
4700        }
4701        _needsRedraw = false;
4702        return true;
4703    } else
4704        return false;
4705}
4706
4707/// Get the pixel width of the render window/image
4708int Renderer::getWindowWidth() const
4709{
4710    return _windowWidth;
4711}
4712
4713/// Get the pixel height of the render window/image
4714int Renderer::getWindowHeight() const
4715{
4716    return _windowHeight;
4717}
4718
4719/**
4720 * \brief Read back the rendered framebuffer image
4721 */
4722void Renderer::getRenderedFrame(vtkUnsignedCharArray *imgData)
4723{
4724#ifdef RENDER_TARGA
4725    _renderWindow->MakeCurrent();
4726    // Must clear previous errors first.
4727    while (glGetError() != GL_NO_ERROR){
4728        ;
4729    }
4730    int bytesPerPixel = TARGA_BYTES_PER_PIXEL;
4731    int size = bytesPerPixel * _windowWidth * _windowHeight;
4732
4733    if (imgData->GetMaxId() + 1 != size)
4734    {
4735        imgData->SetNumberOfComponents(bytesPerPixel);
4736        imgData->SetNumberOfValues(size);
4737    }
4738    glDisable(GL_TEXTURE_2D);
4739    if (_renderWindow->GetDoubleBuffer()) {
4740        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_renderWindow)->GetBackLeftBuffer()));
4741    } else {
4742        glReadBuffer(static_cast<GLenum>(vtkOpenGLRenderWindow::SafeDownCast(_renderWindow)->GetFrontLeftBuffer()));
4743    }
4744    glPixelStorei(GL_PACK_ALIGNMENT, 1);
4745#ifdef WANT_TRACE
4746    struct timeval t1, t2;
4747    glFinish();
4748    gettimeofday(&t1, 0);
4749#endif
4750    if (bytesPerPixel == 4) {
4751        glReadPixels(0, 0, _windowWidth, _windowHeight, GL_BGRA,
4752                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
4753    } else {
4754        glReadPixels(0, 0, _windowWidth, _windowHeight, GL_BGR,
4755                     GL_UNSIGNED_BYTE, imgData->GetPointer(0));
4756    }
4757#ifdef WANT_TRACE
4758    gettimeofday(&t2, 0);
4759    static unsigned int numFrames = 0;
4760    static double accum = 0;
4761    numFrames++;
4762    accum += MSECS_ELAPSED(t1, t2);
4763#endif
4764    TRACE("Readback time: %g ms", MSECS_ELAPSED(t1, t2));
4765    TRACE("Readback avg: %g ms", accum/numFrames);
4766    if (glGetError() != GL_NO_ERROR) {
4767        ERROR("glReadPixels");
4768    }
4769#else
4770    _renderWindow->GetPixelData(0, 0, _windowWidth-1, _windowHeight-1,
4771                                !_renderWindow->GetDoubleBuffer(), imgData);
4772#endif
4773    TRACE("Image data size: %d", imgData->GetSize());
4774}
4775
4776/**
4777 * \brief Get nearest data value given display coordinates x,y
4778 *
4779 * FIXME: This doesn't work when actors are scaled
4780 *
4781 * Note: no interpolation is performed on data
4782 */
4783bool Renderer::getScalarValueAtPixel(const DataSetId& id, int x, int y, double *value)
4784{
4785    vtkSmartPointer<vtkCoordinate> coord = vtkSmartPointer<vtkCoordinate>::New();
4786    coord->SetCoordinateSystemToDisplay();
4787    coord->SetValue(x, _windowHeight - y, 0);
4788    double *worldCoords = coord->GetComputedWorldValue(_renderer);
4789
4790    TRACE("Pixel coords: %d, %d\nWorld coords: %g, %g, %g", x, y,
4791          worldCoords[0],
4792          worldCoords[1],
4793          worldCoords[2]);
4794
4795    return getScalarValue(id, worldCoords[0], worldCoords[1], worldCoords[2], value);
4796}
4797
4798/**
4799 * \brief Get nearest data value given world coordinates x,y,z
4800 *
4801 * Note: no interpolation is performed on data
4802 */
4803bool Renderer::getScalarValue(const DataSetId& id, double x, double y, double z, double *value)
4804{
4805    DataSet *ds = getDataSet(id);
4806    if (ds == NULL)
4807        return false;
4808
4809    return ds->getScalarValue(x, y, z, value);
4810}
4811
4812/**
4813 * \brief Get nearest data value given display coordinates x,y
4814 *
4815 * Note: no interpolation is performed on data
4816 */
4817bool Renderer::getVectorValueAtPixel(const DataSetId& id, int x, int y, double vector[3])
4818{
4819    vtkSmartPointer<vtkCoordinate> coord = vtkSmartPointer<vtkCoordinate>::New();
4820    coord->SetCoordinateSystemToDisplay();
4821    coord->SetValue(x, _windowHeight - y, 0);
4822    double *worldCoords = coord->GetComputedWorldValue(_renderer);
4823
4824    TRACE("Pixel coords: %d, %d\nWorld coords: %g, %g, %g", x, y,
4825          worldCoords[0],
4826          worldCoords[1],
4827          worldCoords[2]);
4828
4829    return getVectorValue(id, worldCoords[0], worldCoords[1], worldCoords[2], vector);
4830}
4831
4832/**
4833 * \brief Get nearest data value given world coordinates x,y,z
4834 *
4835 * Note: no interpolation is performed on data
4836 */
4837bool Renderer::getVectorValue(const DataSetId& id, double x, double y, double z, double vector[3])
4838{
4839    DataSet *ds = getDataSet(id);
4840    if (ds == NULL)
4841        return false;
4842
4843    return ds->getVectorValue(x, y, z, vector);
4844}
Note: See TracBrowser for help on using the repository browser.