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

Last change on this file since 4801 was 4801, checked in by ldelgass, 10 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.