source: trunk/packages/vizservers/vtkvis/RpVtkRenderer.cpp @ 2200

Last change on this file since 2200 was 2200, checked in by ldelgass, 13 years ago

New commands to set and get absolute camera orientation/position, fix for
panning in 3D, better aspect ratio handling with 2D camera.

  • Property svn:eol-style set to native
File size: 64.2 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (C) 2011, Purdue Research Foundation
4 *
5 * Author: Leif Delgass <ldelgass@purdue.edu>
6 */
7
8#include <cfloat>
9#include <cstring>
10#include <cassert>
11
12#include <vtkCamera.h>
13#include <vtkCoordinate.h>
14#include <vtkTransform.h>
15#include <vtkCharArray.h>
16#include <vtkAxisActor2D.h>
17#ifdef USE_CUSTOM_AXES
18#include <vtkRpCubeAxesActor2D.h>
19#else
20#include <vtkCubeAxesActor.h>
21#include <vtkCubeAxesActor2D.h>
22#endif
23#include <vtkDataSetReader.h>
24#include <vtkDataSetMapper.h>
25#include <vtkContourFilter.h>
26#include <vtkPolyDataMapper.h>
27#include <vtkProperty.h>
28#include <vtkProperty2D.h>
29#include <vtkPointData.h>
30#include <vtkLookupTable.h>
31#include <vtkTextProperty.h>
32
33#include "RpVtkRenderer.h"
34#include "ColorMap.h"
35#include "Trace.h"
36
37using namespace Rappture::VtkVis;
38
39Renderer::Renderer() :
40    _needsRedraw(true),
41    _windowWidth(320),
42    _windowHeight(320),
43    _cameraZoomRatio(1),
44    _useCumulativeRange(true),
45    _cumulativeRangeOnlyVisible(false)
46{
47    _bgColor[0] = 0;
48    _bgColor[1] = 0;
49    _bgColor[2] = 0;
50    _cameraPan[0] = 0;
51    _cameraPan[1] = 0;
52    _cumulativeDataRange[0] = 0.0;
53    _cumulativeDataRange[1] = 1.0;
54    // clipping planes to prevent overdrawing axes
55    _clippingPlanes = vtkSmartPointer<vtkPlaneCollection>::New();
56    // bottom
57    vtkSmartPointer<vtkPlane> plane0 = vtkSmartPointer<vtkPlane>::New();
58    plane0->SetNormal(0, 1, 0);
59    plane0->SetOrigin(0, 0, 0);
60    _clippingPlanes->AddItem(plane0);
61    // left
62    vtkSmartPointer<vtkPlane> plane1 = vtkSmartPointer<vtkPlane>::New();
63    plane1->SetNormal(1, 0, 0);
64    plane1->SetOrigin(0, 0, 0);
65    _clippingPlanes->AddItem(plane1);
66   // top
67    vtkSmartPointer<vtkPlane> plane2 = vtkSmartPointer<vtkPlane>::New();
68    plane2->SetNormal(0, -1, 0);
69    plane2->SetOrigin(0, 1, 0);
70    _clippingPlanes->AddItem(plane2);
71    // right
72    vtkSmartPointer<vtkPlane> plane3 = vtkSmartPointer<vtkPlane>::New();
73    plane3->SetNormal(-1, 0, 0);
74    plane3->SetOrigin(1, 0, 0);
75    _clippingPlanes->AddItem(plane3);
76    _renderer = vtkSmartPointer<vtkRenderer>::New();
77    _renderer->LightFollowCameraOn();
78    _cameraMode = PERSPECTIVE;
79    initAxes();
80    initCamera();
81    storeCameraOrientation();
82    _renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
83    _renderWindow->DoubleBufferOff();
84    //_renderWindow->BordersOff();
85    _renderWindow->SetSize(_windowWidth, _windowHeight);
86    _renderWindow->OffScreenRenderingOn();
87    _renderWindow->AddRenderer(_renderer);
88    addColorMap("default", ColorMap::createDefault());
89}
90
91Renderer::~Renderer()
92{
93    for (ColorMapHashmap::iterator itr = _colorMaps.begin();
94             itr != _colorMaps.end(); ++itr) {
95        delete itr->second;
96    }
97    _colorMaps.clear();
98    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
99             itr != _pseudoColors.end(); ++itr) {
100        delete itr->second;
101    }
102    _pseudoColors.clear();
103    for (Contour2DHashmap::iterator itr = _contours.begin();
104             itr != _contours.end(); ++itr) {
105        delete itr->second;
106    }
107    _contours.clear();
108    for (PolyDataHashmap::iterator itr = _polyDatas.begin();
109             itr != _polyDatas.end(); ++itr) {
110        delete itr->second;
111    }
112    _polyDatas.clear();
113    for (DataSetHashmap::iterator itr = _dataSets.begin();
114             itr != _dataSets.end(); ++itr) {
115        delete itr->second;
116    }
117    _dataSets.clear();
118}
119
120/**
121 * \brief Add a DataSet to this Renderer
122 *
123 * This just adds the DataSet to the Renderer's list of data sets.
124 * In order to render the data, a PseudoColor or Contour2D must
125 * be added to the Renderer.
126 */
127void Renderer::addDataSet(const DataSetId& id)
128{
129    if (getDataSet(id) != NULL) {
130        WARN("Replacing existing dataset %s", id.c_str());
131        deleteDataSet(id);
132    }
133    _dataSets[id] = new DataSet(id);
134}
135
136/**
137 * \brief Remove the PseudoColor mapping for the specified DataSet
138 *
139 * The underlying PseudoColor object is deleted, freeing its memory
140 */
141void Renderer::deletePseudoColor(const DataSetId& id)
142{
143    PseudoColorHashmap::iterator itr;
144
145    bool doAll = false;
146
147    if (id.compare("all") == 0) {
148        itr = _pseudoColors.begin();
149        doAll = true;
150    } else {
151        itr = _pseudoColors.find(id);
152    }
153    if (itr == _pseudoColors.end()) {
154        ERROR("PseudoColor not found: %s", id.c_str());
155        return;
156    }
157
158    TRACE("Deleting PseudoColors for %s", id.c_str());
159
160    do  {
161        PseudoColor *ps = itr->second;
162        if (ps->getActor())
163            _renderer->RemoveActor(ps->getActor());
164        delete ps;
165
166        _pseudoColors.erase(itr);
167    } while (doAll && ++itr != _pseudoColors.end());
168
169    _needsRedraw = true;
170}
171
172/**
173 * \brief Remove the Contour2D isolines for the specified DataSet
174 *
175 * The underlying Contour2D is deleted, freeing its memory
176 */
177void Renderer::deleteContour2D(const DataSetId& id)
178{
179    Contour2DHashmap::iterator itr;
180
181    bool doAll = false;
182
183    if (id.compare("all") == 0) {
184        itr = _contours.begin();
185        doAll = true;
186    } else {
187        itr = _contours.find(id);
188    }
189    if (itr == _contours.end()) {
190        ERROR("Contour2D not found: %s", id.c_str());
191        return;
192    }
193
194    TRACE("Deleting Contour2Ds for %s", id.c_str());
195
196    do {
197        Contour2D *contour = itr->second;
198        if (contour->getActor())
199            _renderer->RemoveActor(contour->getActor());
200        delete contour;
201
202        _contours.erase(itr);
203    } while (doAll && ++itr != _contours.end());
204
205    _needsRedraw = true;
206}
207
208/**
209 * \brief Remove the PolyData mesh for the specified DataSet
210 *
211 * The underlying PolyData is deleted, freeing its memory
212 */
213void Renderer::deletePolyData(const DataSetId& id)
214{
215    PolyDataHashmap::iterator itr;
216   
217    bool doAll = false;
218
219    if (id.compare("all") == 0) {
220        itr = _polyDatas.begin();
221        doAll = true;
222    } else {
223        itr = _polyDatas.find(id);
224    }
225    if (itr == _polyDatas.end()) {
226        ERROR("PolyData not found: %s", id.c_str());
227        return;
228    }
229
230    TRACE("Deleting PolyDatas for %s", id.c_str());
231
232    do {
233        PolyData *polyData = itr->second;
234        if (polyData->getActor())
235            _renderer->RemoveActor(polyData->getActor());
236        delete polyData;
237
238        _polyDatas.erase(itr);
239    } while (doAll && ++itr != _polyDatas.end());
240
241    _needsRedraw = true;
242}
243
244/**
245 * \brief Remove the specified DataSet and associated rendering objects
246 *
247 * The underlying DataSet and any associated Contour2D and PseudoColor
248 * objects are deleted, freeing the memory used.
249 */
250void Renderer::deleteDataSet(const DataSetId& id)
251{
252    DataSetHashmap::iterator itr;
253
254    bool doAll = false;
255
256    if (id.compare("all") == 0) {
257        itr = _dataSets.begin();
258        doAll = true;
259    } else {
260        itr = _dataSets.find(id);
261    }
262    if (itr == _dataSets.end()) {
263        ERROR("Unknown dataset %s", id.c_str());
264        return;
265    }
266
267    do {
268        TRACE("Deleting dataset %s", itr->second->getName().c_str());
269
270        deletePseudoColor(itr->second->getName());
271        deleteContour2D(itr->second->getName());
272        deletePolyData(itr->second->getName());
273   
274        TRACE("After deleting graphics objects");
275
276        delete itr->second;
277        _dataSets.erase(itr);
278    } while (doAll && ++itr != _dataSets.end());
279
280    // Update cumulative data range
281    collectDataRanges(_cumulativeDataRange, _cumulativeRangeOnlyVisible);
282    updateRanges(_useCumulativeRange);
283
284    _needsRedraw = true;
285}
286
287/**
288 * \brief Find the DataSet for the given DataSetId key
289 *
290 * \return A pointer to the DataSet, or NULL if not found
291 */
292DataSet *Renderer::getDataSet(const DataSetId& id)
293{
294    DataSetHashmap::iterator itr = _dataSets.find(id);
295    if (itr == _dataSets.end()) {
296        TRACE("DataSet not found: %s", id.c_str());
297        return NULL;
298    } else
299        return itr->second;
300}
301
302/**
303 * \brief (Re-)load the data for the specified DataSet key from a file
304 */
305bool Renderer::setDataFile(const DataSetId& id, const char *filename)
306{
307    DataSet *ds = getDataSet(id);
308    if (ds) {
309        bool ret = ds->setDataFile(filename);
310        collectDataRanges(_cumulativeDataRange, _cumulativeRangeOnlyVisible);
311        updateRanges(_useCumulativeRange);
312        _needsRedraw = true;
313        return ret;
314    } else
315        return false;
316}
317
318/**
319 * \brief (Re-)load the data for the specified DataSet key from a memory buffer
320 */
321bool Renderer::setData(const DataSetId& id, char *data, int nbytes)
322{
323    DataSet *ds = getDataSet(id);
324    if (ds) {
325        bool ret = ds->setData(data, nbytes);
326        collectDataRanges(_cumulativeDataRange, _cumulativeRangeOnlyVisible);
327        updateRanges(_useCumulativeRange);
328        _needsRedraw = true;
329        return ret;
330    } else
331        return false;
332}
333
334/**
335 * \brief Control whether the cumulative data range of datasets is used for
336 * colormapping and contours
337 */
338void Renderer::setUseCumulativeDataRange(bool state, bool onlyVisible)
339{
340    if (state != _useCumulativeRange) {
341        _useCumulativeRange = state;
342        _cumulativeRangeOnlyVisible = onlyVisible;
343        collectDataRanges(_cumulativeDataRange, _cumulativeRangeOnlyVisible);
344        updateRanges(_useCumulativeRange);
345        _needsRedraw = true;
346    }
347}
348
349void Renderer::resetAxes()
350{
351    TRACE("Resetting axes");
352    if (_cubeAxesActor == NULL ||
353        _cubeAxesActor2D == NULL) {
354        initAxes();
355    }
356    if (_cameraMode == IMAGE) {
357        if (_renderer->HasViewProp(_cubeAxesActor)) {
358            TRACE("Removing 3D axes");
359            _renderer->RemoveActor(_cubeAxesActor);
360        }
361        if (!_renderer->HasViewProp(_cubeAxesActor2D)) {
362            TRACE("Adding 2D axes");
363            _renderer->AddActor(_cubeAxesActor2D);
364        }
365    } else {
366        if (_renderer->HasViewProp(_cubeAxesActor2D)) {
367            TRACE("Removing 2D axes");
368            _renderer->RemoveActor(_cubeAxesActor2D);
369        }
370        if (!_renderer->HasViewProp(_cubeAxesActor)) {
371            TRACE("Adding 3D axes");
372            _renderer->AddActor(_cubeAxesActor);
373        }
374        double bounds[6];
375        collectBounds(bounds, true);
376        _cubeAxesActor->SetBounds(bounds);
377    }
378}
379
380/**
381 * \brief Set inital properties on the 2D Axes
382 */
383void Renderer::initAxes()
384{
385    TRACE("Initializing axes");
386    if (_cubeAxesActor == NULL)
387        _cubeAxesActor = vtkSmartPointer<vtkCubeAxesActor>::New();
388    _cubeAxesActor->SetCamera(_renderer->GetActiveCamera());
389    // Don't offset labels at origin
390    _cubeAxesActor->SetCornerOffset(0);
391    _cubeAxesActor->SetFlyModeToClosestTriad();
392
393#ifdef USE_CUSTOM_AXES
394    if (_cubeAxesActor2D == NULL)
395        _cubeAxesActor2D = vtkSmartPointer<vtkRpCubeAxesActor2D>::New();
396#else
397    if (_cubeAxesActor2D == NULL)
398        _cubeAxesActor2D = vtkSmartPointer<vtkCubeAxesActor2D>::New();
399#endif
400
401    _cubeAxesActor2D->SetCamera(_renderer->GetActiveCamera());
402    _cubeAxesActor2D->ZAxisVisibilityOff();
403    _cubeAxesActor2D->SetCornerOffset(0);
404    _cubeAxesActor2D->SetFlyModeToClosestTriad();
405
406    _cubeAxesActor2D->ScalingOff();
407    //_cubeAxesActor2D->SetShowActualBounds(0);
408    _cubeAxesActor2D->SetFontFactor(1.25);
409    // Use "nice" range and number of ticks/labels
410    _cubeAxesActor2D->GetXAxisActor2D()->AdjustLabelsOn();
411    _cubeAxesActor2D->GetYAxisActor2D()->AdjustLabelsOn();
412
413#ifdef USE_CUSTOM_AXES
414    _cubeAxesActor2D->SetAxisTitleTextProperty(NULL);
415    _cubeAxesActor2D->SetAxisLabelTextProperty(NULL);
416    //_cubeAxesActor2D->GetXAxisActor2D()->SizeFontRelativeToAxisOn();
417    _cubeAxesActor2D->GetXAxisActor2D()->GetTitleTextProperty()->BoldOn();
418    _cubeAxesActor2D->GetXAxisActor2D()->GetTitleTextProperty()->ItalicOff();
419    _cubeAxesActor2D->GetXAxisActor2D()->GetTitleTextProperty()->ShadowOn();
420    _cubeAxesActor2D->GetXAxisActor2D()->GetLabelTextProperty()->BoldOff();
421    _cubeAxesActor2D->GetXAxisActor2D()->GetLabelTextProperty()->ItalicOff();
422    _cubeAxesActor2D->GetXAxisActor2D()->GetLabelTextProperty()->ShadowOff();
423
424    //_cubeAxesActor2D->GetYAxisActor2D()->SizeFontRelativeToAxisOn();
425    _cubeAxesActor2D->GetYAxisActor2D()->GetTitleTextProperty()->BoldOn();
426    _cubeAxesActor2D->GetYAxisActor2D()->GetTitleTextProperty()->ItalicOff();
427    _cubeAxesActor2D->GetYAxisActor2D()->GetTitleTextProperty()->ShadowOn();
428    _cubeAxesActor2D->GetYAxisActor2D()->GetLabelTextProperty()->BoldOff();
429    _cubeAxesActor2D->GetYAxisActor2D()->GetLabelTextProperty()->ItalicOff();
430    _cubeAxesActor2D->GetYAxisActor2D()->GetLabelTextProperty()->ShadowOff();
431#else
432    _cubeAxesActor2D->GetAxisTitleTextProperty()->BoldOn();
433    _cubeAxesActor2D->GetAxisTitleTextProperty()->ItalicOff();
434    _cubeAxesActor2D->GetAxisTitleTextProperty()->ShadowOn();
435    _cubeAxesActor2D->GetAxisLabelTextProperty()->BoldOff();
436    _cubeAxesActor2D->GetAxisLabelTextProperty()->ItalicOff();
437    _cubeAxesActor2D->GetAxisLabelTextProperty()->ShadowOff();
438#endif
439
440    if (_cameraMode == IMAGE) {
441        if (!_renderer->HasViewProp(_cubeAxesActor2D))
442            _renderer->AddActor(_cubeAxesActor2D);
443    } else {
444        if (!_renderer->HasViewProp(_cubeAxesActor))
445            _renderer->AddActor(_cubeAxesActor);
446    }
447}
448
449/**
450 * \brief Set color of axes, ticks, labels, titles
451 */
452void Renderer::setAxesColor(double color[3])
453{
454    if (_cubeAxesActor != NULL) {
455        _cubeAxesActor->GetProperty()->SetColor(color);
456        _needsRedraw = true;
457    }
458    if (_cubeAxesActor2D != NULL) {
459        _cubeAxesActor2D->GetProperty()->SetColor(color);
460#ifdef USE_CUSTOM_AXES
461        _cubeAxesActor2D->GetXAxisActor2D()->GetTitleTextProperty()->SetColor(color);
462        _cubeAxesActor2D->GetXAxisActor2D()->GetLabelTextProperty()->SetColor(color);
463        _cubeAxesActor2D->GetYAxisActor2D()->GetTitleTextProperty()->SetColor(color);
464        _cubeAxesActor2D->GetYAxisActor2D()->GetLabelTextProperty()->SetColor(color);
465#else
466        _cubeAxesActor2D->GetAxisTitleTextProperty()->SetColor(color);
467        _cubeAxesActor2D->GetAxisLabelTextProperty()->SetColor(color);
468#endif
469        _needsRedraw = true;
470    }
471}
472
473/**
474 * \brief Turn on/off rendering of all axes gridlines
475 */
476void Renderer::setAxesGridVisibility(bool state)
477{
478    if (_cubeAxesActor != NULL) {
479        _cubeAxesActor->SetDrawXGridlines((state ? 1 : 0));
480        _cubeAxesActor->SetDrawYGridlines((state ? 1 : 0));
481        _cubeAxesActor->SetDrawZGridlines((state ? 1 : 0));
482        _needsRedraw = true;
483    }
484}
485
486/**
487 * \brief Turn on/off rendering of single axis gridlines
488 */
489void Renderer::setAxisGridVisibility(Axis axis, bool state)
490{
491    if (_cubeAxesActor != NULL) {
492        if (axis == X_AXIS) {
493            _cubeAxesActor->SetDrawXGridlines((state ? 1 : 0));
494        } else if (axis == Y_AXIS) {
495            _cubeAxesActor->SetDrawYGridlines((state ? 1 : 0));
496        } else if (axis == Z_AXIS) {
497            _cubeAxesActor->SetDrawZGridlines((state ? 1 : 0));
498        }
499        _needsRedraw = true;
500    }
501}
502
503/**
504 * \brief Turn on/off rendering of all axes
505 */
506void Renderer::setAxesVisibility(bool state)
507{
508    if (_cubeAxesActor != NULL) {
509        _cubeAxesActor->SetVisibility((state ? 1 : 0));
510        _needsRedraw = true;
511    }
512    if (_cubeAxesActor2D != NULL) {
513        _cubeAxesActor2D->SetVisibility((state ? 1 : 0));
514        _needsRedraw = true;
515    }
516    setAxisVisibility(X_AXIS, state);
517    setAxisVisibility(Y_AXIS, state);
518    setAxisVisibility(Z_AXIS, state);
519}
520
521/**
522 * \brief Turn on/off rendering of the specified axis
523 */
524void Renderer::setAxisVisibility(Axis axis, bool state)
525{
526    if (_cubeAxesActor != NULL) {
527        if (axis == X_AXIS) {
528            _cubeAxesActor->SetXAxisVisibility((state ? 1 : 0));
529        } else if (axis == Y_AXIS) {
530            _cubeAxesActor->SetYAxisVisibility((state ? 1 : 0));
531        } else if (axis == Z_AXIS) {
532            _cubeAxesActor->SetZAxisVisibility((state ? 1 : 0));
533        }
534        _needsRedraw = true;
535    }
536    if (_cubeAxesActor2D != NULL) {
537        if (axis == X_AXIS) {
538            _cubeAxesActor2D->SetXAxisVisibility((state ? 1 : 0));
539        } else if (axis == Y_AXIS) {
540            _cubeAxesActor2D->SetYAxisVisibility((state ? 1 : 0));
541        }
542        _needsRedraw = true;
543    }
544}
545
546/**
547 * \brief Set title of the specified axis
548 */
549void Renderer::setAxisTitle(Axis axis, const char *title)
550{
551    if (_cubeAxesActor != NULL) {
552        if (axis == X_AXIS) {
553            _cubeAxesActor->SetXTitle(title);
554        } else if (axis == Y_AXIS) {
555            _cubeAxesActor->SetYTitle(title);
556        } else if (axis == Z_AXIS) {
557            _cubeAxesActor->SetZTitle(title);
558        }
559        _needsRedraw = true;
560    }
561    if (_cubeAxesActor2D != NULL) {
562        if (axis == X_AXIS) {
563            _cubeAxesActor2D->SetXLabel(title);
564        } else if (axis == Y_AXIS) {
565            _cubeAxesActor2D->SetYLabel(title);
566        }
567        _needsRedraw = true;
568    }
569}
570
571/**
572 * \brief Set units of the specified axis
573 */
574void Renderer::setAxisUnits(Axis axis, const char *units)
575{
576    if (_cubeAxesActor != NULL) {
577        if (axis == X_AXIS) {
578            _cubeAxesActor->SetXUnits(units);
579        } else if (axis == Y_AXIS) {
580            _cubeAxesActor->SetYUnits(units);
581        } else if (axis == Z_AXIS) {
582            _cubeAxesActor->SetZUnits(units);
583        }
584        _needsRedraw = true;
585    }
586#ifdef notdef
587    if (_cubeAxesActor2D != NULL) {
588        if (axis == X_AXIS) {
589            _cubeAxesActor2D->SetXUnits(units);
590        } else if (axis == Y_AXIS) {
591            _cubeAxesActor2D->SetYUnits(units);
592        }
593        _needsRedraw = true;
594    }
595#endif
596}
597
598/**
599 * \brief Add a color map for use in the Renderer
600 */
601void Renderer::addColorMap(const ColorMapId& id, ColorMap *colorMap)
602{
603    if (colorMap != NULL) {
604        colorMap->build();
605        if (getColorMap(id) != NULL) {
606            WARN("Replacing existing colormap %s", id.c_str());
607            deleteColorMap(id);
608        }
609        _colorMaps[id] = colorMap;
610    } else {
611        ERROR("NULL ColorMap");
612    }
613}
614
615/**
616 * \brief Return the ColorMap associated with the colormap key given
617 */
618ColorMap *Renderer::getColorMap(const ColorMapId& id)
619{
620    ColorMapHashmap::iterator itr = _colorMaps.find(id);
621
622    if (itr == _colorMaps.end())
623        return NULL;
624    else
625        return itr->second;
626}
627
628/**
629 * \brief Remove the colormap associated with the key given
630 *
631 * The underlying vtkLookupTable will be deleted if it is not referenced
632 * by any other objects
633 */
634void Renderer::deleteColorMap(const ColorMapId& id)
635{
636    ColorMapHashmap::iterator itr;
637
638    bool doAll = false;
639
640    if (id.compare("all") == 0) {
641        itr = _colorMaps.begin();
642        doAll = true;
643    } else {
644        itr = _colorMaps.find(id);
645    }
646
647    if (itr == _colorMaps.end()) {
648        ERROR("Unknown ColorMap %s", id.c_str());
649        return;
650    }
651
652    do {
653        TRACE("Deleting ColorMap %s", itr->second->getName().c_str());
654
655        // TODO: Check if color map is used in PseudoColors?
656        delete itr->second;
657        _colorMaps.erase(itr);
658    } while (doAll && ++itr != _colorMaps.end());
659}
660
661/**
662 * \brief Render a labelled legend image for the given colormap
663 *
664 * \return The image is rendered into the supplied array, false is
665 * returned if the color map is not found
666 */
667bool Renderer::renderColorMap(const ColorMapId& id,
668                              const DataSetId& dataSetID,
669                              const char *title,
670                              int width, int height,
671                              vtkUnsignedCharArray *imgData)
672{
673    ColorMap *colorMap = getColorMap(id);
674    if (colorMap == NULL)
675        return false;
676
677    if (_legendRenderWindow == NULL) {
678        _legendRenderWindow = vtkSmartPointer<vtkRenderWindow>::New();
679        _legendRenderWindow->DoubleBufferOff();
680        _legendRenderWindow->OffScreenRenderingOn();
681    }
682
683    _legendRenderWindow->SetSize(width, height);
684
685    if (_legendRenderer == NULL) {
686        _legendRenderer = vtkSmartPointer<vtkRenderer>::New();
687        _legendRenderWindow->AddRenderer(_legendRenderer);
688    }
689    _legendRenderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
690
691    if (_scalarBarActor == NULL) {
692        _scalarBarActor = vtkSmartPointer<vtkScalarBarActor>::New();
693        _scalarBarActor->UseOpacityOn();
694        _legendRenderer->AddActor(_scalarBarActor);
695    }
696
697    vtkSmartPointer<vtkLookupTable> lut = colorMap->getLookupTable();
698    if (dataSetID.compare("all") == 0) {
699        lut->SetRange(_cumulativeDataRange);
700    } else {
701        DataSet *dataSet = getDataSet(dataSetID);
702        if (dataSet == NULL) {
703            lut->SetRange(_cumulativeDataRange);
704        } else {
705            double range[2];
706            dataSet->getDataRange(range);
707            lut->SetRange(range);
708        }
709    }
710    _scalarBarActor->SetLookupTable(lut);
711
712    // Set viewport-relative width/height/pos
713    if (width > height) {
714        _scalarBarActor->SetOrientationToHorizontal();
715        _scalarBarActor->SetHeight(0.8);
716        _scalarBarActor->SetWidth(0.85);
717        _scalarBarActor->SetPosition(.075, .1);
718    } else {
719        _scalarBarActor->SetOrientationToVertical();
720        _scalarBarActor->SetHeight(0.9);
721        _scalarBarActor->SetWidth(0.8);
722        _scalarBarActor->SetPosition(.1, .05);
723    }
724    _scalarBarActor->SetTitle(title);
725    _scalarBarActor->GetTitleTextProperty()->ItalicOff();
726    _scalarBarActor->GetLabelTextProperty()->BoldOff();
727    _scalarBarActor->GetLabelTextProperty()->ItalicOff();
728    _scalarBarActor->GetLabelTextProperty()->ShadowOff();
729
730    _legendRenderWindow->Render();
731
732    _legendRenderWindow->GetPixelData(0, 0, width-1, height-1, 1, imgData);
733    return true;
734}
735
736/**
737 * \brief Create a new PseudoColor rendering for the specified DataSet
738 */
739void Renderer::addPseudoColor(const DataSetId& id)
740{
741    DataSetHashmap::iterator itr;
742
743    bool doAll = false;
744
745    if (id.compare("all") == 0) {
746        itr = _dataSets.begin();
747    } else {
748        itr = _dataSets.find(id);
749    }
750    if (itr == _dataSets.end()) {
751        ERROR("Unknown dataset %s", id.c_str());
752        return;
753    }
754
755    do {
756        DataSet *ds = itr->second;
757        const DataSetId& dsID = ds->getName();
758
759        if (getPseudoColor(dsID)) {
760            WARN("Replacing existing pseudocolor %s", dsID.c_str());
761            deletePseudoColor(dsID);
762        }
763        PseudoColor *pc = new PseudoColor();
764        _pseudoColors[dsID] = pc;
765
766        pc->setDataSet(ds);
767
768        // Use the default color map
769        vtkSmartPointer<vtkLookupTable> lut = vtkSmartPointer<vtkLookupTable>::New();
770        ColorMap *cmap = getColorMap("default");
771        lut->DeepCopy(cmap->getLookupTable());
772        if (_useCumulativeRange) {
773            lut->SetRange(_cumulativeDataRange);
774        } else {
775            double range[2];
776            ds->getDataRange(range);
777            lut->SetRange(range);
778        }
779
780        pc->setLookupTable(lut);
781
782        _renderer->AddActor(pc->getActor());
783    } while (doAll && ++itr != _dataSets.end());
784
785    initCamera();
786    _needsRedraw = true;
787}
788
789/**
790 * \brief Get the PseudoColor associated with the specified DataSet
791 */
792PseudoColor *Renderer::getPseudoColor(const DataSetId& id)
793{
794    PseudoColorHashmap::iterator itr = _pseudoColors.find(id);
795
796    if (itr == _pseudoColors.end()) {
797        TRACE("PseudoColor not found: %s", id.c_str());
798        return NULL;
799    } else
800        return itr->second;
801}
802
803/**
804 * \brief Associate an existing named color map with a DataSet
805 */
806void Renderer::setPseudoColorColorMap(const DataSetId& id, const ColorMapId& colorMapId)
807{
808    PseudoColorHashmap::iterator itr;
809
810    bool doAll = false;
811
812    if (id.compare("all") == 0) {
813        itr = _pseudoColors.begin();
814        doAll = true;
815    } else {
816        itr = _pseudoColors.find(id);
817    }
818
819    if (itr == _pseudoColors.end()) {
820        ERROR("PseudoColor not found: %s", id.c_str());
821        return;
822    }
823
824    ColorMap *cmap = getColorMap(colorMapId);
825    if (cmap == NULL) {
826        ERROR("Unknown colormap: %s", colorMapId.c_str());
827        return;
828    }
829
830    do {
831        TRACE("Set color map: %s for dataset %s", colorMapId.c_str(),
832              itr->second->getDataSet()->getName().c_str());
833
834        // Make a copy of the generic colormap lookup table, so
835        // data range can be set in the copy table to match the
836        // dataset being plotted
837        vtkSmartPointer<vtkLookupTable> lut = vtkSmartPointer<vtkLookupTable>::New();
838        lut->DeepCopy(cmap->getLookupTable());
839
840        if (_useCumulativeRange) {
841            lut->SetRange(_cumulativeDataRange);
842        } else {
843            if (itr->second->getDataSet() != NULL) {
844                double range[2];
845                itr->second->getDataSet()->getDataRange(range);
846                lut->SetRange(range);
847            }
848        }
849
850        itr->second->setLookupTable(lut);
851    } while (doAll && ++itr != _pseudoColors.end());
852
853    _needsRedraw = true;
854}
855
856/**
857 * \brief Get the color map (vtkLookupTable) for the given DataSet
858 *
859 * \return The associated lookup table or NULL if not found
860 */
861vtkLookupTable *Renderer::getPseudoColorColorMap(const DataSetId& id)
862{
863    PseudoColor *pc = getPseudoColor(id);
864    if (pc)
865        return pc->getLookupTable();
866    else
867        return NULL;
868}
869
870/**
871 * \brief Set opacity of the PseudoColor for the given DataSet
872 */
873void Renderer::setPseudoColorOpacity(const DataSetId& id, double opacity)
874{
875    PseudoColorHashmap::iterator itr;
876
877    bool doAll = false;
878
879    if (id.compare("all") == 0) {
880        itr = _pseudoColors.begin();
881        doAll = true;
882    } else {
883        itr = _pseudoColors.find(id);
884    }
885
886    if (itr == _pseudoColors.end()) {
887        ERROR("PseudoColor not found: %s", id.c_str());
888        return;
889    }
890
891    do {
892        itr->second->setOpacity(opacity);
893    } while (doAll && ++itr != _pseudoColors.end());
894
895    _needsRedraw = true;
896}
897
898/**
899 * \brief Turn on/off rendering of the PseudoColor mapper for the given DataSet
900 */
901void Renderer::setPseudoColorVisibility(const DataSetId& id, bool state)
902{
903    PseudoColorHashmap::iterator itr;
904
905    bool doAll = false;
906
907    if (id.compare("all") == 0) {
908        itr = _pseudoColors.begin();
909        doAll = true;
910    } else {
911        itr = _pseudoColors.find(id);
912    }
913
914    if (itr == _pseudoColors.end()) {
915        ERROR("PseudoColor not found: %s", id.c_str());
916        return;
917    }
918
919    do {
920        itr->second->setVisibility(state);
921    } while (doAll && ++itr != _pseudoColors.end());
922
923    _needsRedraw = true;
924}
925
926/**
927 * \brief Set the visibility of polygon edges for the specified DataSet
928 */
929void Renderer::setPseudoColorEdgeVisibility(const DataSetId& id, bool state)
930{
931    PseudoColorHashmap::iterator itr;
932
933    bool doAll = false;
934
935    if (id.compare("all") == 0) {
936        itr = _pseudoColors.begin();
937        doAll = true;
938    } else {
939        itr = _pseudoColors.find(id);
940    }
941
942    if (itr == _pseudoColors.end()) {
943        ERROR("PseudoColor not found: %s", id.c_str());
944        return;
945    }
946
947    do {
948        itr->second->setEdgeVisibility(state);
949    } while (doAll && ++itr != _pseudoColors.end());
950
951    _needsRedraw = true;
952}
953
954/**
955 * \brief Set the RGB polygon edge color for the specified DataSet
956 */
957void Renderer::setPseudoColorEdgeColor(const DataSetId& id, float color[3])
958{
959    PseudoColorHashmap::iterator itr;
960
961    bool doAll = false;
962
963    if (id.compare("all") == 0) {
964        itr = _pseudoColors.begin();
965        doAll = true;
966    } else {
967        itr = _pseudoColors.find(id);
968    }
969
970    if (itr == _pseudoColors.end()) {
971        ERROR("PseudoColor not found: %s", id.c_str());
972        return;
973    }
974
975    do {
976        itr->second->setEdgeColor(color);
977    } while (doAll && ++itr != _pseudoColors.end());
978
979    _needsRedraw = true;
980}
981
982/**
983 * \brief Set the polygon edge width for the specified DataSet (may be a no-op)
984 *
985 * If the OpenGL implementation/hardware does not support wide lines,
986 * this function may not have an effect.
987 */
988void Renderer::setPseudoColorEdgeWidth(const DataSetId& id, float edgeWidth)
989{
990    PseudoColorHashmap::iterator itr;
991
992    bool doAll = false;
993
994    if (id.compare("all") == 0) {
995        itr = _pseudoColors.begin();
996        doAll = true;
997    } else {
998        itr = _pseudoColors.find(id);
999    }
1000
1001    if (itr == _pseudoColors.end()) {
1002        ERROR("PseudoColor not found: %s", id.c_str());
1003        return;
1004    }
1005
1006    do {
1007        itr->second->setEdgeWidth(edgeWidth);
1008    } while (doAll && ++itr != _pseudoColors.end());
1009
1010    _needsRedraw = true;
1011}
1012
1013/**
1014 * \brief Turn mesh lighting on/off for the specified DataSet
1015 */
1016void Renderer::setPseudoColorLighting(const DataSetId& id, bool state)
1017{
1018    PseudoColorHashmap::iterator itr;
1019
1020    bool doAll = false;
1021
1022    if (id.compare("all") == 0) {
1023        itr = _pseudoColors.begin();
1024        doAll = true;
1025    } else {
1026        itr = _pseudoColors.find(id);
1027    }
1028
1029    if (itr == _pseudoColors.end()) {
1030        ERROR("PseudoColor not found: %s", id.c_str());
1031        return;
1032    }
1033
1034    do {
1035        itr->second->setLighting(state);
1036    } while (doAll && ++itr != _pseudoColors.end());
1037
1038    _needsRedraw = true;
1039}
1040
1041/**
1042 * \brief Create a new Contour2D and associate it with the named DataSet
1043 */
1044void Renderer::addContour2D(const DataSetId& id)
1045{
1046    DataSetHashmap::iterator itr;
1047
1048    bool doAll = false;
1049
1050    if (id.compare("all") == 0) {
1051        itr = _dataSets.begin();
1052    } else {
1053        itr = _dataSets.find(id);
1054    }
1055    if (itr == _dataSets.end()) {
1056        ERROR("Unknown dataset %s", id.c_str());
1057        return;
1058    }
1059
1060    do {
1061        DataSet *ds = itr->second;
1062        const DataSetId& dsID = ds->getName();
1063
1064        if (getContour2D(dsID)) {
1065            WARN("Replacing existing contour2d %s", dsID.c_str());
1066            deleteContour2D(dsID);
1067        }
1068
1069        Contour2D *contour = new Contour2D();
1070        _contours[dsID] = contour;
1071
1072        contour->setDataSet(ds);
1073
1074        _renderer->AddActor(contour->getActor());
1075    } while (doAll && ++itr != _dataSets.end());
1076
1077    initCamera();
1078    _needsRedraw = true;
1079}
1080
1081/**
1082 * \brief Get the Contour2D associated with a named DataSet
1083 */
1084Contour2D *Renderer::getContour2D(const DataSetId& id)
1085{
1086    Contour2DHashmap::iterator itr = _contours.find(id);
1087
1088    if (itr == _contours.end()) {
1089        TRACE("Contour2D not found: %s", id.c_str());
1090        return NULL;
1091    } else
1092        return itr->second;
1093}
1094
1095/**
1096 * \brief Set the number of equally spaced contour isolines for the given DataSet
1097 */
1098void Renderer::setContours(const DataSetId& id, int numContours)
1099{
1100    Contour2DHashmap::iterator itr;
1101
1102    bool doAll = false;
1103
1104    if (id.compare("all") == 0) {
1105        itr = _contours.begin();
1106        doAll = true;
1107    } else {
1108        itr = _contours.find(id);
1109    }
1110    if (itr == _contours.end()) {
1111        ERROR("Contour2D not found: %s", id.c_str());
1112        return;
1113    }
1114
1115    do {
1116        if (_useCumulativeRange) {
1117            itr->second->setContours(numContours, _cumulativeDataRange);
1118        } else {
1119            itr->second->setContours(numContours);
1120        }
1121    } while (doAll && ++itr != _contours.end());
1122
1123    _needsRedraw = true;
1124}
1125
1126/**
1127 * \brief Set a list of isovalues for the given DataSet
1128 */
1129void Renderer::setContourList(const DataSetId& id, const std::vector<double>& contours)
1130{
1131    Contour2DHashmap::iterator itr;
1132
1133    bool doAll = false;
1134
1135    if (id.compare("all") == 0) {
1136        itr = _contours.begin();
1137        doAll = true;
1138    } else {
1139        itr = _contours.find(id);
1140    }
1141    if (itr == _contours.end()) {
1142        ERROR("Contour2D not found: %s", id.c_str());
1143        return;
1144    }
1145
1146    do {
1147        itr->second->setContourList(contours);
1148    } while (doAll && ++itr != _contours.end());
1149
1150     _needsRedraw = true;
1151}
1152
1153/**
1154 * \brief Set opacity of contour lines for the given DataSet
1155 */
1156void Renderer::setContourOpacity(const DataSetId& id, double opacity)
1157{
1158    Contour2DHashmap::iterator itr;
1159
1160    bool doAll = false;
1161
1162    if (id.compare("all") == 0) {
1163        itr = _contours.begin();
1164        doAll = true;
1165    } else {
1166        itr = _contours.find(id);
1167    }
1168    if (itr == _contours.end()) {
1169        ERROR("Contour2D not found: %s", id.c_str());
1170        return;
1171    }
1172
1173    do {
1174        itr->second->setOpacity(opacity);
1175    } while (doAll && ++itr != _contours.end());
1176
1177    _needsRedraw = true;
1178}
1179
1180/**
1181 * \brief Turn on/off rendering contour lines for the given DataSet
1182 */
1183void Renderer::setContourVisibility(const DataSetId& id, bool state)
1184{
1185    Contour2DHashmap::iterator itr;
1186
1187    bool doAll = false;
1188
1189    if (id.compare("all") == 0) {
1190        itr = _contours.begin();
1191        doAll = true;
1192    } else {
1193        itr = _contours.find(id);
1194    }
1195    if (itr == _contours.end()) {
1196        ERROR("Contour2D not found: %s", id.c_str());
1197        return;
1198    }
1199
1200    do {
1201        itr->second->setVisibility(state);
1202    } while (doAll && ++itr != _contours.end());
1203
1204    _needsRedraw = true;
1205}
1206
1207/**
1208 * \brief Set the RGB isoline color for the specified DataSet
1209 */
1210void Renderer::setContourEdgeColor(const DataSetId& id, float color[3])
1211{
1212    Contour2DHashmap::iterator itr;
1213
1214    bool doAll = false;
1215
1216    if (id.compare("all") == 0) {
1217        itr = _contours.begin();
1218        doAll = true;
1219    } else {
1220        itr = _contours.find(id);
1221    }
1222    if (itr == _contours.end()) {
1223        ERROR("Contour2D not found: %s", id.c_str());
1224        return;
1225    }
1226
1227    do {
1228        itr->second->setEdgeColor(color);
1229    } while (doAll && ++itr != _contours.end());
1230
1231    _needsRedraw = true;
1232}
1233
1234/**
1235 * \brief Set the isoline width for the specified DataSet (may be a no-op)
1236 *
1237 * If the OpenGL implementation/hardware does not support wide lines,
1238 * this function may not have an effect.
1239 */
1240void Renderer::setContourEdgeWidth(const DataSetId& id, float edgeWidth)
1241{
1242    Contour2DHashmap::iterator itr;
1243
1244    bool doAll = false;
1245
1246    if (id.compare("all") == 0) {
1247        itr = _contours.begin();
1248        doAll = true;
1249    } else {
1250        itr = _contours.find(id);
1251    }
1252    if (itr == _contours.end()) {
1253        ERROR("Contour2D not found: %s", id.c_str());
1254        return;
1255    }
1256
1257    do {
1258        itr->second->setEdgeWidth(edgeWidth);
1259    } while (doAll && ++itr != _contours.end());
1260
1261    _needsRedraw = true;
1262}
1263
1264/**
1265 * \brief Turn contour lighting on/off for the specified DataSet
1266 */
1267void Renderer::setContourLighting(const DataSetId& id, bool state)
1268{
1269    Contour2DHashmap::iterator itr;
1270
1271    bool doAll = false;
1272
1273    if (id.compare("all") == 0) {
1274        itr = _contours.begin();
1275        doAll = true;
1276    } else {
1277        itr = _contours.find(id);
1278    }
1279    if (itr == _contours.end()) {
1280        ERROR("Contour2D not found: %s", id.c_str());
1281        return;
1282    }
1283
1284    do {
1285        itr->second->setLighting(state);
1286    } while (doAll && ++itr != _contours.end());
1287    _needsRedraw = true;
1288}
1289
1290/**
1291 * \brief Create a new PolyData and associate it with the named DataSet
1292 */
1293void Renderer::addPolyData(const DataSetId& id)
1294{
1295    DataSetHashmap::iterator itr;
1296
1297    bool doAll = false;
1298
1299    if (id.compare("all") == 0) {
1300        itr = _dataSets.begin();
1301    } else {
1302        itr = _dataSets.find(id);
1303    }
1304    if (itr == _dataSets.end()) {
1305        ERROR("Unknown dataset %s", id.c_str());
1306        return;
1307    }
1308
1309    do {
1310        DataSet *ds = itr->second;
1311        const DataSetId& dsID = ds->getName();
1312
1313        if (getPolyData(dsID)) {
1314            WARN("Replacing existing polydata %s", dsID.c_str());
1315            deletePolyData(dsID);
1316        }
1317
1318        PolyData *polyData = new PolyData();
1319        _polyDatas[dsID] = polyData;
1320
1321        polyData->setDataSet(ds);
1322
1323        _renderer->AddActor(polyData->getActor());
1324    } while (doAll && ++itr != _dataSets.end());
1325
1326    if (_cameraMode == IMAGE)
1327        setCameraMode(PERSPECTIVE);
1328    initCamera();
1329    _needsRedraw = true;
1330}
1331
1332/**
1333 * \brief Get the PolyData associated with a named DataSet
1334 */
1335PolyData *Renderer::getPolyData(const DataSetId& id)
1336{
1337    PolyDataHashmap::iterator itr = _polyDatas.find(id);
1338
1339    if (itr == _polyDatas.end()) {
1340        TRACE("PolyData not found: %s", id.c_str());
1341        return NULL;
1342    } else
1343        return itr->second;
1344}
1345
1346/**
1347 * \brief Set opacity of the PolyData for the given DataSet
1348 */
1349void Renderer::setPolyDataOpacity(const DataSetId& id, double opacity)
1350{
1351    PolyDataHashmap::iterator itr;
1352   
1353    bool doAll = false;
1354
1355    if (id.compare("all") == 0) {
1356        itr = _polyDatas.begin();
1357        doAll = true;
1358    } else {
1359        itr = _polyDatas.find(id);
1360    }
1361    if (itr == _polyDatas.end()) {
1362        ERROR("PolyData not found: %s", id.c_str());
1363        return;
1364    }
1365
1366    do {
1367        itr->second->setOpacity(opacity);
1368    } while (doAll && ++itr != _polyDatas.end());
1369
1370    _needsRedraw = true;
1371}
1372
1373/**
1374 * \brief Turn on/off rendering of the PolyData mapper for the given DataSet
1375 */
1376void Renderer::setPolyDataVisibility(const DataSetId& id, bool state)
1377{
1378    PolyDataHashmap::iterator itr;
1379   
1380    bool doAll = false;
1381
1382    if (id.compare("all") == 0) {
1383        itr = _polyDatas.begin();
1384        doAll = true;
1385    } else {
1386        itr = _polyDatas.find(id);
1387    }
1388    if (itr == _polyDatas.end()) {
1389        ERROR("PolyData not found: %s", id.c_str());
1390        return;
1391    }
1392
1393    do {
1394        itr->second->setVisibility(state);
1395    } while (doAll && ++itr != _polyDatas.end());
1396
1397    _needsRedraw = true;
1398}
1399
1400/**
1401 * \brief Set the RGB polygon face color for the specified DataSet
1402 */
1403void Renderer::setPolyDataColor(const DataSetId& id, float color[3])
1404{
1405    PolyDataHashmap::iterator itr;
1406   
1407    bool doAll = false;
1408
1409    if (id.compare("all") == 0) {
1410        itr = _polyDatas.begin();
1411        doAll = true;
1412    } else {
1413        itr = _polyDatas.find(id);
1414    }
1415    if (itr == _polyDatas.end()) {
1416        ERROR("PolyData not found: %s", id.c_str());
1417        return;
1418    }
1419
1420    do {
1421        itr->second->setColor(color);
1422    } while (doAll && ++itr != _polyDatas.end());
1423    _needsRedraw = true;
1424}
1425
1426/**
1427 * \brief Set the visibility of polygon edges for the specified DataSet
1428 */
1429void Renderer::setPolyDataEdgeVisibility(const DataSetId& id, bool state)
1430{
1431    PolyDataHashmap::iterator itr;
1432   
1433    bool doAll = false;
1434
1435    if (id.compare("all") == 0) {
1436        itr = _polyDatas.begin();
1437        doAll = true;
1438    } else {
1439        itr = _polyDatas.find(id);
1440    }
1441    if (itr == _polyDatas.end()) {
1442        ERROR("PolyData not found: %s", id.c_str());
1443        return;
1444    }
1445
1446    do {
1447        itr->second->setEdgeVisibility(state);
1448    } while (doAll && ++itr != _polyDatas.end());
1449
1450    _needsRedraw = true;
1451}
1452
1453/**
1454 * \brief Set the RGB polygon edge color for the specified DataSet
1455 */
1456void Renderer::setPolyDataEdgeColor(const DataSetId& id, float color[3])
1457{
1458    PolyDataHashmap::iterator itr;
1459   
1460    bool doAll = false;
1461
1462    if (id.compare("all") == 0) {
1463        itr = _polyDatas.begin();
1464        doAll = true;
1465    } else {
1466        itr = _polyDatas.find(id);
1467    }
1468    if (itr == _polyDatas.end()) {
1469        ERROR("PolyData not found: %s", id.c_str());
1470        return;
1471    }
1472
1473    do {
1474        itr->second->setEdgeColor(color);
1475    } while (doAll && ++itr != _polyDatas.end());
1476
1477    _needsRedraw = true;
1478}
1479
1480/**
1481 * \brief Set the polygon edge width for the specified DataSet (may be a no-op)
1482 *
1483 * If the OpenGL implementation/hardware does not support wide lines,
1484 * this function may not have an effect.
1485 */
1486void Renderer::setPolyDataEdgeWidth(const DataSetId& id, float edgeWidth)
1487{
1488    PolyDataHashmap::iterator itr;
1489   
1490    bool doAll = false;
1491
1492    if (id.compare("all") == 0) {
1493        itr = _polyDatas.begin();
1494        doAll = true;
1495    } else {
1496        itr = _polyDatas.find(id);
1497    }
1498    if (itr == _polyDatas.end()) {
1499        ERROR("PolyData not found: %s", id.c_str());
1500        return;
1501    }
1502
1503    do {
1504        itr->second->setEdgeWidth(edgeWidth);
1505    } while (doAll && ++itr != _polyDatas.end());
1506
1507    _needsRedraw = true;
1508}
1509
1510/**
1511 * \brief Set wireframe rendering for the specified DataSet
1512 */
1513void Renderer::setPolyDataWireframe(const DataSetId& id, bool state)
1514{
1515    PolyDataHashmap::iterator itr;
1516   
1517    bool doAll = false;
1518
1519    if (id.compare("all") == 0) {
1520        itr = _polyDatas.begin();
1521        doAll = true;
1522    } else {
1523        itr = _polyDatas.find(id);
1524    }
1525    if (itr == _polyDatas.end()) {
1526        ERROR("PolyData not found: %s", id.c_str());
1527        return;
1528    }
1529
1530    do {
1531        itr->second->setWireframe(state);
1532    } while (doAll && ++itr != _polyDatas.end());
1533
1534    _needsRedraw = true;
1535}
1536
1537/**
1538 * \brief Turn mesh lighting on/off for the specified DataSet
1539 */
1540void Renderer::setPolyDataLighting(const DataSetId& id, bool state)
1541{
1542    PolyDataHashmap::iterator itr;
1543   
1544    bool doAll = false;
1545
1546    if (id.compare("all") == 0) {
1547        itr = _polyDatas.begin();
1548        doAll = true;
1549    } else {
1550        itr = _polyDatas.find(id);
1551    }
1552    if (itr == _polyDatas.end()) {
1553        ERROR("PolyData not found: %s", id.c_str());
1554        return;
1555    }
1556
1557    do {
1558        itr->second->setLighting(state);
1559    } while (doAll && ++itr != _polyDatas.end());
1560
1561    _needsRedraw = true;
1562}
1563
1564/**
1565 * \brief Resize the render window (image size for renderings)
1566 */
1567void Renderer::setWindowSize(int width, int height)
1568{
1569    _windowWidth = width;
1570    _windowHeight = height;
1571    _renderWindow->SetSize(_windowWidth, _windowHeight);
1572    if (_cameraMode == IMAGE) {
1573        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
1574                            _imgWorldDims[0], _imgWorldDims[1]);
1575    }
1576    _needsRedraw = true;
1577}
1578
1579/**
1580 * \brief Change the camera type: perspective, orthographic or image view
1581 *
1582 * Perspective mode is a normal 3D camera.
1583 *
1584 * Orthogrphic mode is parallel projection.
1585 *
1586 * Image mode is an orthographic camera with fixed axes and a clipping region
1587 * around the plot area, use setCameraZoomRegion to control the displayed area
1588 *
1589 * \param[in] mode Enum specifying camera type
1590 */
1591void Renderer::setCameraMode(CameraMode mode)
1592{
1593    if (_cameraMode == mode) return;
1594
1595    CameraMode origMode = _cameraMode;
1596    _cameraMode = mode;
1597    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1598    switch (mode) {
1599    case ORTHO: {
1600        TRACE("Set camera to Ortho mode");
1601        camera->ParallelProjectionOn();
1602        if (origMode == IMAGE) {
1603            resetCamera(false);
1604        }
1605        break;
1606    }
1607    case PERSPECTIVE: {
1608        TRACE("Set camera to Perspective mode");
1609        camera->ParallelProjectionOff();
1610        if (origMode == IMAGE) {
1611            resetCamera(false);
1612        }
1613        break;
1614    }
1615    case IMAGE: {
1616        camera->ParallelProjectionOn();
1617        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
1618                            _imgWorldDims[0],_imgWorldDims[1]);
1619        TRACE("Set camera to Image mode");
1620        break;
1621    }
1622    default:
1623        ERROR("Unkown camera mode: %d", mode);
1624    }
1625    resetAxes();
1626    _needsRedraw = true;
1627}
1628
1629/**
1630 * \brief Get the current camera mode
1631 */
1632Renderer::CameraMode Renderer::getCameraMode() const
1633{
1634    return _cameraMode;
1635}
1636
1637void Renderer::setCameraOrientation(double position[3],
1638                                    double focalPoint[3],
1639                                    double viewUp[3])
1640{
1641    memcpy(_cameraPos, position, sizeof(double)*3);
1642    memcpy(_cameraFocalPoint, focalPoint, sizeof(double)*3);
1643    memcpy(_cameraUp, viewUp, sizeof(double)*3);
1644    // Apply the new parameters to the VTK camera
1645    restoreCameraOrientation();
1646    _needsRedraw = true;
1647}
1648
1649void Renderer::getCameraOrientation(double position[3],
1650                                    double focalPoint[3],
1651                                    double viewUp[3])
1652{
1653    memcpy(position, _cameraPos, sizeof(double)*3);
1654    memcpy(focalPoint, _cameraFocalPoint, sizeof(double)*3);
1655    memcpy(viewUp, _cameraUp, sizeof(double)*3);
1656}
1657
1658void Renderer::storeCameraOrientation()
1659{
1660    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1661    camera->GetPosition(_cameraPos);
1662    camera->GetFocalPoint(_cameraFocalPoint);
1663    camera->GetViewUp(_cameraUp);
1664}
1665
1666void Renderer::restoreCameraOrientation()
1667{
1668    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1669    camera->SetPosition(_cameraPos);
1670    camera->SetFocalPoint(_cameraFocalPoint);
1671    camera->SetViewUp(_cameraUp);
1672}
1673
1674/**
1675 * \brief Reset pan, zoom, clipping planes and optionally rotation
1676 *
1677 * \param[in] resetOrientation Reset the camera rotation/orientation also
1678 */
1679void Renderer::resetCamera(bool resetOrientation)
1680{
1681    if (_cameraMode == IMAGE) {
1682        initCamera();
1683    } else {
1684        if (resetOrientation) {
1685            vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1686            camera->SetPosition(0, 0, 1);
1687            camera->SetFocalPoint(0, 0, 0);
1688            camera->SetViewUp(0, 1, 0);
1689            storeCameraOrientation();
1690        } else {
1691            restoreCameraOrientation();
1692        }
1693        _renderer->ResetCamera();
1694        _renderer->ResetCameraClippingRange();
1695        computeScreenWorldCoords();
1696    }
1697    _cameraZoomRatio = 1;
1698    _cameraPan[0] = 0;
1699    _cameraPan[1] = 0;
1700
1701    _needsRedraw = true;
1702}
1703
1704/**
1705 * \brief Perform a relative rotation to current camera orientation
1706 *
1707 * Angles are in degrees, rotation is about focal point
1708 */
1709void Renderer::rotateCamera(double yaw, double pitch, double roll)
1710{
1711    if (_cameraMode == IMAGE)
1712        return;
1713    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1714    camera->Azimuth(yaw); // Rotate about object
1715    //camera->SetYaw(yaw); // Rotate about camera
1716    camera->Elevation(pitch); // Rotate about object
1717    //camera->SetPitch(pitch); // Rotate about camera
1718    camera->Roll(roll); // Roll about camera view axis
1719    _renderer->ResetCameraClippingRange();
1720    storeCameraOrientation();
1721    computeScreenWorldCoords();
1722    _needsRedraw = true;
1723}
1724
1725/**
1726 * \brief Perform a 2D translation of the camera
1727 *
1728 * x,y pan amount are specified as signed absolute pan amount in viewport
1729 * units -- i.e. 0 is no pan, .5 is half the viewport, 2 is twice the viewport,
1730 * etc.
1731 *
1732 * \param[in] x Viewport coordinate horizontal panning
1733 * \param[in] y Viewport coordinate vertical panning (with origin at top)
1734 * \param[in] absolute Control if pan amount is relative to current or absolute
1735 */
1736void Renderer::panCamera(double x, double y, bool absolute)
1737{
1738    if (_cameraMode == IMAGE) {
1739        // Reverse x rather than y, since we are panning the camera, and client
1740        // expects to be panning/moving the object
1741        x = -x * _screenWorldCoords[2];
1742        y = y * _screenWorldCoords[3];
1743
1744        if (absolute) {
1745            double panAbs[2];
1746            panAbs[0] = x;
1747            panAbs[1] = y;
1748            x -= _cameraPan[0];
1749            y -= _cameraPan[1];
1750            _cameraPan[0] = panAbs[0];
1751            _cameraPan[1] = panAbs[1];
1752        } else {
1753            _cameraPan[0] += x;
1754            _cameraPan[1] += y;
1755        }
1756
1757        _imgWorldOrigin[0] += x;
1758        _imgWorldOrigin[1] += y;
1759        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
1760                            _imgWorldDims[0], _imgWorldDims[1]);
1761    } else {
1762        y = -y;
1763        if (absolute) {
1764            double panAbs[2];
1765            panAbs[0] = x;
1766            panAbs[1] = y;
1767            x -= _cameraPan[0];
1768            y -= _cameraPan[1];
1769            _cameraPan[0] = panAbs[0];
1770            _cameraPan[1] = panAbs[1];
1771        } else {
1772            _cameraPan[0] += x;
1773            _cameraPan[1] += y;
1774        }
1775
1776        vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1777        double viewFocus[4], focalDepth, viewPoint[3];
1778        double newPickPoint[4], oldPickPoint[4], motionVector[3];
1779
1780        camera->GetFocalPoint(viewFocus);
1781        computeWorldToDisplay(viewFocus[0], viewFocus[1], viewFocus[2],
1782                              viewFocus);
1783        focalDepth = viewFocus[2];
1784
1785        computeDisplayToWorld(( x * 2. + 1.) * _windowWidth / 2.0,
1786                              ( y * 2. + 1.) * _windowHeight / 2.0,
1787                              focalDepth,
1788                              newPickPoint);
1789
1790        computeDisplayToWorld(_windowWidth / 2.0,
1791                              _windowHeight / 2.0,
1792                              focalDepth,
1793                              oldPickPoint);
1794 
1795        // Camera motion is reversed
1796        motionVector[0] = oldPickPoint[0] - newPickPoint[0];
1797        motionVector[1] = oldPickPoint[1] - newPickPoint[1];
1798        motionVector[2] = oldPickPoint[2] - newPickPoint[2];
1799
1800        camera->GetFocalPoint(viewFocus);
1801        camera->GetPosition(viewPoint);
1802        camera->SetFocalPoint(motionVector[0] + viewFocus[0],
1803                              motionVector[1] + viewFocus[1],
1804                              motionVector[2] + viewFocus[2]);
1805
1806        camera->SetPosition(motionVector[0] + viewPoint[0],
1807                            motionVector[1] + viewPoint[1],
1808                            motionVector[2] + viewPoint[2]);
1809
1810        _renderer->ResetCameraClippingRange();
1811        storeCameraOrientation();
1812        computeScreenWorldCoords();
1813    }
1814    _needsRedraw = true;
1815}
1816
1817/**
1818 * \brief Change the FOV of the camera
1819 *
1820 * \param[in] z Ratio to change zoom (greater than 1 is zoom in, less than 1 is zoom out)
1821 * \param[in] absolute Control if zoom factor is relative to current setting or absolute
1822 */
1823void Renderer::zoomCamera(double z, bool absolute)
1824{
1825    if (absolute) {
1826        assert(_cameraZoomRatio > 0.0);
1827        double zAbs = z;
1828        z *= 1.0/_cameraZoomRatio;
1829        _cameraZoomRatio = zAbs;
1830    } else {
1831        _cameraZoomRatio *= z;
1832    }
1833    if (_cameraMode == IMAGE) {
1834        double dx = _imgWorldDims[0];
1835        double dy = _imgWorldDims[1];
1836        _imgWorldDims[0] /= z;
1837        _imgWorldDims[1] /= z;
1838        dx -= _imgWorldDims[0];
1839        dy -= _imgWorldDims[1];
1840        _imgWorldOrigin[0] += dx/2.0;
1841        _imgWorldOrigin[1] += dy/2.0;
1842        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
1843                            _imgWorldDims[0], _imgWorldDims[1]);
1844    } else {
1845        vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1846        camera->Zoom(z); // Change perspective FOV angle or ortho parallel scale
1847        //camera->Dolly(z); // Move camera forward/back
1848        _renderer->ResetCameraClippingRange();
1849        storeCameraOrientation();
1850    }
1851    _needsRedraw = true;
1852}
1853
1854/**
1855 * \brief Set the pan/zoom using a corner and dimensions in world coordinates
1856 *
1857 * \param[in] x left world coordinate
1858 * \param[in] y bottom  world coordinate
1859 * \param[in] width Width of zoom region in world coordinates
1860 * \param[in] height Height of zoom region in world coordinates
1861 */
1862void Renderer::setCameraZoomRegion(double x, double y, double width, double height)
1863{
1864    double camPos[2];
1865
1866    int pxOffsetX = 85;
1867    int pxOffsetY = 75;
1868    int outerGutter = 15;
1869
1870    int imgHeightPx = _windowHeight - pxOffsetY - outerGutter;
1871    int imgWidthPx = _windowWidth - pxOffsetX - outerGutter;
1872
1873    double imgAspect = width / height;
1874    double winAspect = (double)_windowWidth / _windowHeight;
1875
1876    double pxToWorld;
1877
1878    if (imgAspect >= winAspect) {
1879        pxToWorld = width / imgWidthPx;
1880    } else {
1881        pxToWorld = height / imgHeightPx;
1882    }
1883
1884    double offsetX = pxOffsetX * pxToWorld;
1885    double offsetY = pxOffsetY * pxToWorld;
1886
1887    TRACE("Window: %d %d", _windowWidth, _windowHeight);
1888    TRACE("ZoomRegion: %g %g %g %g", x, y, width, height);
1889    TRACE("pxToWorld: %g", pxToWorld);
1890    TRACE("offset: %g %g", offsetX, offsetY);
1891
1892    setCameraMode(IMAGE);
1893
1894    _imgWorldOrigin[0] = x;
1895    _imgWorldOrigin[1] = y;
1896    _imgWorldDims[0] = width;
1897    _imgWorldDims[1] = height;
1898
1899    camPos[0] = _imgWorldOrigin[0] - offsetX + (_windowWidth * pxToWorld)/2.0;
1900    camPos[1] = _imgWorldOrigin[1] - offsetY + (_windowHeight * pxToWorld)/2.0;
1901
1902    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1903    camera->ParallelProjectionOn();
1904    camera->SetPosition(camPos[0], camPos[1], 1);
1905    camera->SetFocalPoint(camPos[0], camPos[1], 0);
1906    camera->SetViewUp(0, 1, 0);
1907    camera->SetClippingRange(1, 2);
1908    // Half of world coordinate height of viewport (Documentation is wrong)
1909    camera->SetParallelScale(_windowHeight * pxToWorld / 2.0);
1910
1911    // bottom
1912    _clippingPlanes->GetItem(0)->SetOrigin(0, _imgWorldOrigin[1], 0);
1913    // left
1914    _clippingPlanes->GetItem(1)->SetOrigin(_imgWorldOrigin[0], 0, 0);
1915    // top
1916    _clippingPlanes->GetItem(2)->SetOrigin(0, _imgWorldOrigin[1] + _imgWorldDims[1], 0);
1917    // right
1918    _clippingPlanes->GetItem(3)->SetOrigin(_imgWorldOrigin[0] + _imgWorldDims[0], 0, 0);
1919
1920    _cubeAxesActor2D->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWorldDims[0],
1921                                _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWorldDims[1], 0, 0);
1922
1923    // Compute screen world coordinates
1924    computeScreenWorldCoords();
1925
1926#ifdef DEBUG
1927    printCameraInfo(camera);
1928#endif
1929
1930    _needsRedraw = true;
1931}
1932
1933void Renderer::computeDisplayToWorld(double x, double y, double z, double worldPt[4])
1934{
1935    _renderer->SetDisplayPoint(x, y, z);
1936    _renderer->DisplayToWorld();
1937    _renderer->GetWorldPoint(worldPt);
1938    if (worldPt[3]) {
1939        worldPt[0] /= worldPt[3];
1940        worldPt[1] /= worldPt[3];
1941        worldPt[2] /= worldPt[3];
1942        worldPt[3] = 1.0;
1943    }
1944}
1945
1946void Renderer::computeWorldToDisplay(double x, double y, double z, double displayPt[3])
1947{
1948    _renderer->SetWorldPoint(x, y, z, 1.0);
1949    _renderer->WorldToDisplay();
1950    _renderer->GetDisplayPoint(displayPt);
1951}
1952
1953void Renderer::computeScreenWorldCoords()
1954{
1955    // Start with viewport coords [-1,1]
1956    double x0 = -1;
1957    double y0 = -1;
1958    double z0 = -1;
1959    double x1 = 1;
1960    double y1 = 1;
1961    double z1 = -1;
1962
1963    vtkMatrix4x4 *mat = vtkMatrix4x4::New();
1964    double result[4];
1965
1966    // get the perspective transformation from the active camera
1967    mat->DeepCopy(_renderer->GetActiveCamera()->
1968                  GetCompositeProjectionTransformMatrix(_renderer->GetTiledAspectRatio(),0,1));
1969
1970    // use the inverse matrix
1971    mat->Invert();
1972
1973    // Transform point to world coordinates
1974    result[0] = x0;
1975    result[1] = y0;
1976    result[2] = z0;
1977    result[3] = 1.0;
1978
1979    mat->MultiplyPoint(result, result);
1980
1981    // Get the transformed vector & set WorldPoint
1982    // while we are at it try to keep w at one
1983    if (result[3]) {
1984        x0 = result[0] / result[3];
1985        y0 = result[1] / result[3];
1986        z0 = result[2] / result[3];
1987    }
1988
1989    result[0] = x1;
1990    result[1] = y1;
1991    result[2] = z1;
1992    result[3] = 1.0;
1993
1994    mat->MultiplyPoint(result, result);
1995
1996    if (result[3]) {
1997        x1 = result[0] / result[3];
1998        y1 = result[1] / result[3];
1999        z1 = result[2] / result[3];
2000    }
2001
2002    mat->Delete();
2003
2004    _screenWorldCoords[0] = x0;
2005    _screenWorldCoords[1] = y0;
2006    _screenWorldCoords[2] = x1 - x0;
2007    _screenWorldCoords[3] = y1 - y0;
2008}
2009
2010/**
2011 * \brief Get the world coordinates of the image camera plot area
2012 *
2013 * \param[out] xywh Array to hold x,y,width,height world coordinates
2014 */
2015void Renderer::getCameraZoomRegion(double xywh[4]) const
2016{
2017    xywh[0] = _imgWorldOrigin[0];
2018    xywh[1] = _imgWorldOrigin[1];
2019    xywh[2] = _imgWorldDims[0];
2020    xywh[3] = _imgWorldDims[1];
2021}
2022
2023/**
2024 * \brief Get the world origin and dimensions of the screen
2025 *
2026 * \param[out] xywh Array to hold x,y,width,height world coordinates
2027 */
2028void Renderer::getScreenWorldCoords(double xywh[4]) const
2029{
2030    memcpy(xywh, _screenWorldCoords, sizeof(double)*4);
2031}
2032
2033/**
2034 * \brief Compute bounding box containing the two input bounding boxes
2035 *
2036 * \param[out] boundsDest Union of the two input bounding boxes
2037 * \param[in] bounds1 Input bounding box
2038 * \param[in] bounds2 Input bounding box
2039 */
2040void Renderer::mergeBounds(double *boundsDest,
2041                           const double *bounds1, const double *bounds2)
2042{
2043    for (int i = 0; i < 6; i++) {
2044        if (i % 2 == 0)
2045            boundsDest[i] = min2(bounds1[i], bounds2[i]);
2046        else
2047            boundsDest[i] = max2(bounds1[i], bounds2[i]);
2048    }
2049}
2050
2051/**
2052 * \brief Collect bounds of all graphics objects
2053 *
2054 * \param[out] bounds Bounds of all scene objects
2055 * \param[in] onlyVisible Only collect bounds of visible objects
2056 */
2057void Renderer::collectBounds(double *bounds, bool onlyVisible)
2058{
2059    bounds[0] = DBL_MAX;
2060    bounds[1] = -DBL_MAX;
2061    bounds[2] = DBL_MAX;
2062    bounds[3] = -DBL_MAX;
2063    bounds[4] = DBL_MAX;
2064    bounds[5] = -DBL_MAX;
2065
2066    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
2067             itr != _pseudoColors.end(); ++itr) {
2068        if (!onlyVisible || itr->second->getVisibility())
2069            mergeBounds(bounds, bounds, itr->second->getActor()->GetBounds());
2070    }
2071    for (Contour2DHashmap::iterator itr = _contours.begin();
2072             itr != _contours.end(); ++itr) {
2073        if (!onlyVisible || itr->second->getVisibility())
2074            mergeBounds(bounds, bounds, itr->second->getActor()->GetBounds());
2075    }
2076    for (PolyDataHashmap::iterator itr = _polyDatas.begin();
2077             itr != _polyDatas.end(); ++itr) {
2078        if (!onlyVisible || itr->second->getVisibility())
2079            mergeBounds(bounds, bounds, itr->second->getActor()->GetBounds());
2080    }
2081    for (int i = 0; i < 6; i++) {
2082        if (i % 2 == 0) {
2083            if (bounds[i] == DBL_MAX)
2084                bounds[i] = 0;
2085        } else {
2086            if (bounds[i] == -DBL_MAX)
2087                bounds[i] = 1;
2088        }
2089    }
2090}
2091
2092/**
2093 * \brief Update data ranges for color-mapping and contours
2094 *
2095 * \param[in] useCumulative Use cumulative range of all DataSets
2096 */
2097void Renderer::updateRanges(bool useCumulative)
2098{
2099    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
2100         itr != _pseudoColors.end(); ++itr) {
2101        vtkLookupTable *lut = itr->second->getLookupTable();
2102        if (lut) {
2103            if (useCumulative) {
2104                lut->SetRange(_cumulativeDataRange);
2105            } else {
2106                double range[2];
2107                if (itr->second->getDataSet()) {
2108                    itr->second->getDataSet()->getDataRange(range);
2109                    lut->SetRange(range);
2110                }
2111            }
2112        }
2113    }
2114    for (Contour2DHashmap::iterator itr = _contours.begin();
2115         itr != _contours.end(); ++itr) {
2116        // Only need to update range if using evenly spaced contours
2117        if (itr->second->getContourList().empty()) {
2118            if (useCumulative) {
2119                itr->second->setContours(itr->second->getNumContours(), _cumulativeDataRange);
2120            } else {
2121                itr->second->setContours(itr->second->getNumContours());
2122            }
2123        }
2124    }
2125}
2126
2127/**
2128 * \brief Collect cumulative data range of all DataSets
2129 *
2130 * \param[inout] range Data range of all DataSets
2131 * \param[in] onlyVisible Only collect range of visible DataSets
2132 */
2133void Renderer::collectDataRanges(double *range, bool onlyVisible)
2134{
2135    range[0] = DBL_MAX;
2136    range[1] = -DBL_MAX;
2137
2138    for (DataSetHashmap::iterator itr = _dataSets.begin();
2139         itr != _dataSets.end(); ++itr) {
2140        if (!onlyVisible || itr->second->getVisibility()) {
2141            double r[2];
2142            itr->second->getDataRange(r);
2143            range[0] = min2(range[0], r[0]);
2144            range[1] = max2(range[1], r[1]);
2145        }
2146    }
2147    if (range[0] == DBL_MAX)
2148        range[0] = 0;
2149    if (range[1] == -DBL_MAX)
2150        range[1] = 1;
2151}
2152
2153#ifdef notdef
2154void Renderer::setPerspectiveCameraByBounds(double bounds[6])
2155{
2156    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2157    camera->ParallelProjectionOff();
2158    camera->Reset();
2159}
2160#endif
2161
2162/**
2163 * \brief Initialize the camera zoom region to include the bounding volume given
2164 */
2165void Renderer::initCamera()
2166{
2167    double bounds[6];
2168    collectBounds(bounds, true);
2169    _imgWorldOrigin[0] = bounds[0];
2170    _imgWorldOrigin[1] = bounds[2];
2171    _imgWorldDims[0] = bounds[1] - bounds[0];
2172    _imgWorldDims[1] = bounds[3] - bounds[2];
2173    _cameraPan[0] = 0;
2174    _cameraPan[1] = 0;
2175    _cameraZoomRatio = 1;
2176
2177    if (_cameraMode == IMAGE) {
2178        _renderer->ResetCamera();
2179        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
2180                            _imgWorldDims[0], _imgWorldDims[1]);
2181        resetAxes();
2182    } else if (_cameraMode == ORTHO) {
2183        _renderer->GetActiveCamera()->ParallelProjectionOn();
2184        resetAxes();
2185        _renderer->ResetCamera();
2186        computeScreenWorldCoords();
2187    } else if (_cameraMode == PERSPECTIVE) {
2188        _renderer->GetActiveCamera()->ParallelProjectionOff();
2189        resetAxes();
2190        _renderer->ResetCamera();
2191        computeScreenWorldCoords();
2192    }
2193}
2194
2195/**
2196 * \brief Print debugging info about a vtkCamera
2197 */
2198void Renderer::printCameraInfo(vtkCamera *camera)
2199{
2200    TRACE("Parallel Scale: %g, Cam pos: %g %g %g, focal pt: %g %g %g, view up: %g %g %g, Clipping range: %g %g",
2201          camera->GetParallelScale(),
2202          camera->GetPosition()[0],
2203          camera->GetPosition()[1],
2204          camera->GetPosition()[2],
2205          camera->GetFocalPoint()[0],
2206          camera->GetFocalPoint()[1],
2207          camera->GetFocalPoint()[2],
2208          camera->GetViewUp()[0],
2209          camera->GetViewUp()[1],
2210          camera->GetViewUp()[2],
2211          camera->GetClippingRange()[0],
2212          camera->GetClippingRange()[1]);
2213}
2214
2215/**
2216 * \brief Set the RGB background color to render into the image
2217 */
2218void Renderer::setBackgroundColor(float color[3])
2219{
2220    _bgColor[0] = color[0];
2221    _bgColor[1] = color[1];
2222    _bgColor[2] = color[2];
2223    _renderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
2224    _needsRedraw = true;
2225}
2226
2227/**
2228 * \brief Set the opacity of the specified DataSet's associated graphics objects
2229 */
2230void Renderer::setOpacity(const DataSetId& id, double opacity)
2231{
2232    setPseudoColorOpacity(id, opacity);
2233    setContourOpacity(id, opacity);
2234    setPolyDataOpacity(id, opacity);
2235}
2236
2237/**
2238 * \brief Turn on/off rendering of the specified DataSet's associated graphics objects
2239 */
2240void Renderer::setVisibility(const DataSetId& id, bool state)
2241{
2242    DataSetHashmap::iterator itr;
2243
2244    bool doAll = false;
2245
2246    if (id.compare("all") == 0) {
2247        itr = _dataSets.begin();
2248        doAll = true;
2249    } else {
2250        itr = _dataSets.find(id);
2251    }
2252    if (itr == _dataSets.end()) {
2253        ERROR("Unknown dataset %s", id.c_str());
2254        return;
2255    }
2256
2257    do {
2258        itr->second->setVisibility(state);
2259    } while (doAll && ++itr != _dataSets.end());
2260
2261    setPseudoColorVisibility(id, state);
2262    setContourVisibility(id, state);
2263    setPolyDataVisibility(id, state);
2264}
2265
2266/**
2267 * \brief Cause the rendering to render a new image if needed
2268 *
2269 * The _needsRedraw flag indicates if a state change has occured since
2270 * the last rendered frame
2271 */
2272bool Renderer::render()
2273{
2274    if (_needsRedraw) {
2275        if (_cameraMode == IMAGE) {
2276            for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
2277                 itr != _pseudoColors.end(); ++itr) {
2278                itr->second->setClippingPlanes(_clippingPlanes);
2279            }
2280            for (Contour2DHashmap::iterator itr = _contours.begin();
2281                 itr != _contours.end(); ++itr) {
2282                itr->second->setClippingPlanes(_clippingPlanes);
2283            }
2284            for (PolyDataHashmap::iterator itr = _polyDatas.begin();
2285                 itr != _polyDatas.end(); ++itr) {
2286                itr->second->setClippingPlanes(_clippingPlanes);
2287            }
2288        } else {
2289            for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
2290                 itr != _pseudoColors.end(); ++itr) {
2291                itr->second->setClippingPlanes(NULL);
2292            }
2293            for (Contour2DHashmap::iterator itr = _contours.begin();
2294                 itr != _contours.end(); ++itr) {
2295                itr->second->setClippingPlanes(NULL);
2296            }
2297            for (PolyDataHashmap::iterator itr = _polyDatas.begin();
2298                 itr != _polyDatas.end(); ++itr) {
2299                itr->second->setClippingPlanes(NULL);
2300            }
2301        }
2302        _renderWindow->Render();
2303        _needsRedraw = false;
2304        return true;
2305    } else
2306        return false;
2307}
2308
2309/// Get the pixel width of the render window/image
2310int Renderer::getWindowWidth() const
2311{
2312    return _windowWidth;
2313}
2314
2315/// Get the pixel height of the render window/image
2316int Renderer::getWindowHeight() const
2317{
2318    return _windowHeight;
2319}
2320
2321/**
2322 * \brief Read back the rendered framebuffer image
2323 */
2324void Renderer::getRenderedFrame(vtkUnsignedCharArray *imgData)
2325{
2326    _renderWindow->GetPixelData(0, 0, _windowWidth-1, _windowHeight-1, 1, imgData);
2327    TRACE("Image data size: %d", imgData->GetSize());
2328}
2329
2330/**
2331 * \brief Get nearest data value given display coordinates x,y
2332 *
2333 * Note: no interpolation is performed on data
2334 */
2335double Renderer::getDataValueAtPixel(const DataSetId& id, int x, int y)
2336{
2337    vtkSmartPointer<vtkCoordinate> coord = vtkSmartPointer<vtkCoordinate>::New();
2338    coord->SetCoordinateSystemToDisplay();
2339    coord->SetValue(x, y, 0);
2340    double *worldCoords = coord->GetComputedWorldValue(_renderer);
2341
2342    TRACE("Pixel coords: %d, %d\nWorld coords: %.12e, %.12e, %12e", x, y,
2343          worldCoords[0],
2344          worldCoords[1],
2345          worldCoords[2]);
2346
2347    return getDataValue(id, worldCoords[0], worldCoords[1], worldCoords[2]);
2348}
2349
2350/**
2351 * \brief Get nearest data value given world coordinates x,y,z
2352 *
2353 * Note: no interpolation is performed on data
2354 */
2355double Renderer::getDataValue(const DataSetId& id, double x, double y, double z)
2356{
2357    DataSet *ds = getDataSet(id);
2358    if (ds == NULL)
2359        return 0;
2360    vtkDataSet *vtkds = ds->getVtkDataSet();
2361    vtkIdType pt = vtkds->FindPoint(x, y, z);
2362    return vtkds->GetPointData()->GetScalars()->GetComponent(pt, 0);
2363}
Note: See TracBrowser for help on using the repository browser.