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

Last change on this file since 3902 was 3902, checked in by ldelgass, 11 years ago

Fix crash when setting active scalar if no current scalar is set

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