source: vtkvis/trunk/Renderer.cpp @ 4643

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

Fix near clipping plane for 2D (image) camera view

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