source: vtkvis/trunk/Renderer.cpp @ 6307

Last change on this file since 6307 was 6179, checked in by ldelgass, 8 years ago

Remove early return

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