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

Last change on this file since 4952 was 4801, checked in by ldelgass, 9 years ago

For release branch, set default back to using all bounds, not just visible
object bounds.

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