source: branches/blt4/packages/vizservers/vtkvis/RpVtkRenderer.cpp @ 2201

Last change on this file since 2201 was 2201, checked in by gah, 13 years ago
File size: 60.7 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    storeCameraOrientation();
79    _cameraMode = PERSPECTIVE;
80    initAxes();
81    initCamera();
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::storeCameraOrientation()
1638{
1639    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1640    camera->GetPosition(_cameraPos);
1641    camera->GetFocalPoint(_cameraFocalPoint);
1642    camera->GetViewUp(_cameraUp);
1643}
1644
1645void Renderer::restoreCameraOrientation()
1646{
1647    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1648    camera->SetPosition(_cameraPos);
1649    camera->SetFocalPoint(_cameraFocalPoint);
1650    camera->SetViewUp(_cameraUp);
1651}
1652
1653/**
1654 * \brief Reset pan, zoom, clipping planes and optionally rotation
1655 *
1656 * \param[in] resetOrientation Reset the camera rotation/orientation also
1657 */
1658void Renderer::resetCamera(bool resetOrientation)
1659{
1660    if (_cameraMode == IMAGE) {
1661        initCamera();
1662    } else {
1663        if (resetOrientation) {
1664            vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1665            camera->SetPosition(0, 0, 1);
1666            camera->SetFocalPoint(0, 0, 0);
1667            camera->SetViewUp(0, 1, 0);
1668            storeCameraOrientation();
1669        } else {
1670            restoreCameraOrientation();
1671        }
1672        _renderer->ResetCamera();
1673        _renderer->ResetCameraClippingRange();
1674        computeScreenWorldCoords();
1675    }
1676    _cameraZoomRatio = 1;
1677    _cameraPan[0] = 0;
1678    _cameraPan[1] = 0;
1679    _needsRedraw = true;
1680}
1681
1682/**
1683 * \brief Perform a relative rotation to current camera orientation
1684 *
1685 * Angles are in degrees, rotation is about focal point
1686 */
1687void Renderer::rotateCamera(double yaw, double pitch, double roll)
1688{
1689    if (_cameraMode == IMAGE)
1690        return;
1691    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1692    camera->Azimuth(yaw); // Rotate about object
1693    //camera->SetYaw(yaw); // Rotate about camera
1694    camera->Elevation(pitch); // Rotate about object
1695    //camera->SetPitch(pitch); // Rotate about camera
1696    camera->Roll(roll); // Roll about camera view axis
1697    _renderer->ResetCameraClippingRange();
1698    storeCameraOrientation();
1699    computeScreenWorldCoords();
1700    _needsRedraw = true;
1701}
1702
1703/**
1704 * \brief Perform a 2D translation of the camera
1705 *
1706 * \param[in] x [0,1] Viewport coordinate horizontal panning
1707 * \param[in] y [0,1] Viewport coordinate vertical panning (with origin at top)
1708 * \param[in] absolute Control if pan amount is relative to current or absolute
1709 */
1710void Renderer::panCamera(double x, double y, bool absolute)
1711{
1712    // Reverse x rather than y, since we are panning the camera, and client
1713    // expects to be panning/moving the object
1714    x = -x * _screenWorldCoords[2];
1715    y = y * _screenWorldCoords[3];
1716
1717    if (absolute) {
1718        double panAbs[2];
1719        panAbs[0] = x;
1720        panAbs[1] = y;
1721        x -= _cameraPan[0];
1722        y -= _cameraPan[1];
1723        _cameraPan[0] = panAbs[0];
1724        _cameraPan[1] = panAbs[1];
1725    } else {
1726        _cameraPan[0] += x;
1727        _cameraPan[1] += y;
1728    }
1729    if (_cameraMode == IMAGE) {
1730        _imgWorldOrigin[0] += x;
1731        _imgWorldOrigin[1] += y;
1732        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
1733                            _imgWorldDims[0], _imgWorldDims[1]);
1734    } else {
1735        vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1736        vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
1737        trans->Translate(x, y, 0);
1738        camera->ApplyTransform(trans);
1739        _renderer->ResetCameraClippingRange();
1740        storeCameraOrientation();
1741        computeScreenWorldCoords();
1742    }
1743    _needsRedraw = true;
1744}
1745
1746/**
1747 * \brief Change the FOV of the camera
1748 *
1749 * \param[in] z Ratio to change zoom (greater than 1 is zoom in, less than 1 is zoom out)
1750 * \param[in] absolute Control if zoom factor is relative to current setting or absolute
1751 */
1752void Renderer::zoomCamera(double z, bool absolute)
1753{
1754    if (absolute) {
1755        assert(_cameraZoomRatio > 0.0);
1756        double zAbs = z;
1757        z *= 1.0/_cameraZoomRatio;
1758        _cameraZoomRatio = zAbs;
1759    } else {
1760        _cameraZoomRatio *= z;
1761    }
1762    if (_cameraMode == IMAGE) {
1763        double dx = _imgWorldDims[0];
1764        double dy = _imgWorldDims[1];
1765        _imgWorldDims[0] /= z;
1766        _imgWorldDims[1] /= z;
1767        dx -= _imgWorldDims[0];
1768        dy -= _imgWorldDims[1];
1769        _imgWorldOrigin[0] += dx/2.0;
1770        _imgWorldOrigin[1] += dy/2.0;
1771        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
1772                            _imgWorldDims[0], _imgWorldDims[1]);
1773    } else {
1774        vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1775        camera->Zoom(z); // Change perspective FOV angle or ortho parallel scale
1776        //camera->Dolly(z); // Move camera forward/back
1777        _renderer->ResetCameraClippingRange();
1778        storeCameraOrientation();
1779    }
1780    _needsRedraw = true;
1781}
1782
1783/**
1784 * \brief Set the pan/zoom using a corner and dimensions in world coordinates
1785 *
1786 * \param[in] x left world coordinate
1787 * \param[in] y bottom  world coordinate
1788 * \param[in] width Width of zoom region in world coordinates
1789 * \param[in] height Height of zoom region in world coordinates
1790 */
1791void Renderer::setCameraZoomRegion(double x, double y, double width, double height)
1792{
1793    double camPos[2];
1794
1795    int pxOffsetX = 85;
1796    int pxOffsetY = 75;
1797    int outerGutter = 15;
1798
1799    int imgHeightPx = _windowHeight - pxOffsetY - outerGutter;
1800    int imgWidthPx = _windowWidth - pxOffsetX - outerGutter;
1801    double pxToWorld;
1802    if (height > width)
1803        pxToWorld = height / imgHeightPx;
1804    else
1805        pxToWorld = width / imgWidthPx;
1806    double offsetX = pxOffsetX * pxToWorld;
1807    double offsetY = pxOffsetY * pxToWorld;
1808
1809    TRACE("Window: %d %d", _windowWidth, _windowHeight);
1810    TRACE("ZoomRegion: %g %g %g %g", x, y, width, height);
1811    TRACE("pxToWorld: %g", pxToWorld);
1812    TRACE("offset: %g %g", offsetX, offsetY);
1813
1814    setCameraMode(IMAGE);
1815
1816    _imgWorldOrigin[0] = x;
1817    _imgWorldOrigin[1] = y;
1818    _imgWorldDims[0] = width;
1819    _imgWorldDims[1] = height;
1820
1821    camPos[0] = _imgWorldOrigin[0] - offsetX + (_windowWidth * pxToWorld)/2.0;
1822    camPos[1] = _imgWorldOrigin[1] - offsetY + (_windowHeight * pxToWorld)/2.0;
1823
1824    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
1825    camera->ParallelProjectionOn();
1826    camera->SetPosition(camPos[0], camPos[1], 1);
1827    camera->SetFocalPoint(camPos[0], camPos[1], 0);
1828    camera->SetViewUp(0, 1, 0);
1829    camera->SetClippingRange(1, 2);
1830    // Half of world coordinate height of viewport (Documentation is wrong)
1831    camera->SetParallelScale(_windowHeight * pxToWorld / 2.0);
1832
1833    // bottom
1834    _clippingPlanes->GetItem(0)->SetOrigin(0, _imgWorldOrigin[1], 0);
1835    // left
1836    _clippingPlanes->GetItem(1)->SetOrigin(_imgWorldOrigin[0], 0, 0);
1837    // top
1838    _clippingPlanes->GetItem(2)->SetOrigin(0, _imgWorldOrigin[1] + _imgWorldDims[1], 0);
1839    // right
1840    _clippingPlanes->GetItem(3)->SetOrigin(_imgWorldOrigin[0] + _imgWorldDims[0], 0, 0);
1841
1842    _cubeAxesActor2D->SetBounds(_imgWorldOrigin[0], _imgWorldOrigin[0] + _imgWorldDims[0],
1843                                _imgWorldOrigin[1], _imgWorldOrigin[1] + _imgWorldDims[1], 0, 0);
1844
1845    // Compute screen world coordinates
1846    computeScreenWorldCoords();
1847
1848#ifdef DEBUG
1849    printCameraInfo(camera);
1850#endif
1851
1852    _needsRedraw = true;
1853}
1854
1855void Renderer::computeScreenWorldCoords()
1856{
1857    // Start with viewport coords [-1,1]
1858    double x0 = -1;
1859    double y0 = -1;
1860    double z0 = 0;
1861    double x1 = 1;
1862    double y1 = 1;
1863    double z1 = 0;
1864
1865    vtkMatrix4x4 *mat = vtkMatrix4x4::New();
1866    double result[4];
1867
1868    // get the perspective transformation from the active camera
1869    mat->DeepCopy(_renderer->GetActiveCamera()->
1870                  GetCompositeProjectionTransformMatrix(_renderer->GetTiledAspectRatio(),0,1));
1871
1872    // use the inverse matrix
1873    mat->Invert();
1874
1875    // Transform point to world coordinates
1876    result[0] = x0;
1877    result[1] = y0;
1878    result[2] = z0;
1879    result[3] = 1.0;
1880
1881    mat->MultiplyPoint(result, result);
1882
1883    // Get the transformed vector & set WorldPoint
1884    // while we are at it try to keep w at one
1885    if (result[3]) {
1886        x0 = result[0] / result[3];
1887        y0 = result[1] / result[3];
1888        z0 = result[2] / result[3];
1889    }
1890
1891    result[0] = x1;
1892    result[1] = y1;
1893    result[2] = z1;
1894    result[3] = 1.0;
1895
1896    mat->MultiplyPoint(result, result);
1897
1898    if (result[3]) {
1899        x1 = result[0] / result[3];
1900        y1 = result[1] / result[3];
1901        z1 = result[2] / result[3];
1902    }
1903
1904    mat->Delete();
1905
1906    _screenWorldCoords[0] = x0;
1907    _screenWorldCoords[1] = y0;
1908    _screenWorldCoords[2] = x1 - x0;
1909    _screenWorldCoords[3] = y1 - y0;
1910}
1911
1912/**
1913 * \brief Get the world coordinates of the image camera plot area
1914 *
1915 * \param[out] xywh Array to hold x,y,width,height world coordinates
1916 */
1917void Renderer::getCameraZoomRegion(double xywh[4]) const
1918{
1919    xywh[0] = _imgWorldOrigin[0];
1920    xywh[1] = _imgWorldOrigin[1];
1921    xywh[2] = _imgWorldDims[0];
1922    xywh[3] = _imgWorldDims[1];
1923}
1924
1925/**
1926 * \brief Get the world origin and dimensions of the screen
1927 *
1928 * \param[out] xywh Array to hold x,y,width,height world coordinates
1929 */
1930void Renderer::getScreenWorldCoords(double xywh[4]) const
1931{
1932    memcpy(xywh, _screenWorldCoords, sizeof(double)*4);
1933}
1934
1935/**
1936 * \brief Compute bounding box containing the two input bounding boxes
1937 *
1938 * \param[out] boundsDest Union of the two input bounding boxes
1939 * \param[in] bounds1 Input bounding box
1940 * \param[in] bounds2 Input bounding box
1941 */
1942void Renderer::mergeBounds(double *boundsDest,
1943                           const double *bounds1, const double *bounds2)
1944{
1945    for (int i = 0; i < 6; i++) {
1946        if (i % 2 == 0)
1947            boundsDest[i] = min2(bounds1[i], bounds2[i]);
1948        else
1949            boundsDest[i] = max2(bounds1[i], bounds2[i]);
1950    }
1951}
1952
1953/**
1954 * \brief Collect bounds of all graphics objects
1955 *
1956 * \param[out] bounds Bounds of all scene objects
1957 * \param[in] onlyVisible Only collect bounds of visible objects
1958 */
1959void Renderer::collectBounds(double *bounds, bool onlyVisible)
1960{
1961    bounds[0] = DBL_MAX;
1962    bounds[1] = -DBL_MAX;
1963    bounds[2] = DBL_MAX;
1964    bounds[3] = -DBL_MAX;
1965    bounds[4] = DBL_MAX;
1966    bounds[5] = -DBL_MAX;
1967
1968    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
1969             itr != _pseudoColors.end(); ++itr) {
1970        if (!onlyVisible || itr->second->getVisibility())
1971            mergeBounds(bounds, bounds, itr->second->getActor()->GetBounds());
1972    }
1973    for (Contour2DHashmap::iterator itr = _contours.begin();
1974             itr != _contours.end(); ++itr) {
1975        if (!onlyVisible || itr->second->getVisibility())
1976            mergeBounds(bounds, bounds, itr->second->getActor()->GetBounds());
1977    }
1978    for (PolyDataHashmap::iterator itr = _polyDatas.begin();
1979             itr != _polyDatas.end(); ++itr) {
1980        if (!onlyVisible || itr->second->getVisibility())
1981            mergeBounds(bounds, bounds, itr->second->getActor()->GetBounds());
1982    }
1983    for (int i = 0; i < 6; i++) {
1984        if (i % 2 == 0) {
1985            if (bounds[i] == DBL_MAX)
1986                bounds[i] = 0;
1987        } else {
1988            if (bounds[i] == -DBL_MAX)
1989                bounds[i] = 1;
1990        }
1991    }
1992}
1993
1994/**
1995 * \brief Update data ranges for color-mapping and contours
1996 *
1997 * \param[in] useCumulative Use cumulative range of all DataSets
1998 */
1999void Renderer::updateRanges(bool useCumulative)
2000{
2001    for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
2002         itr != _pseudoColors.end(); ++itr) {
2003        vtkLookupTable *lut = itr->second->getLookupTable();
2004        if (lut) {
2005            if (useCumulative) {
2006                lut->SetRange(_cumulativeDataRange);
2007            } else {
2008                double range[2];
2009                if (itr->second->getDataSet()) {
2010                    itr->second->getDataSet()->getDataRange(range);
2011                    lut->SetRange(range);
2012                }
2013            }
2014        }
2015    }
2016    for (Contour2DHashmap::iterator itr = _contours.begin();
2017         itr != _contours.end(); ++itr) {
2018        // Only need to update range if using evenly spaced contours
2019        if (itr->second->getContourList().empty()) {
2020            if (useCumulative) {
2021                itr->second->setContours(itr->second->getNumContours(), _cumulativeDataRange);
2022            } else {
2023                itr->second->setContours(itr->second->getNumContours());
2024            }
2025        }
2026    }
2027}
2028
2029/**
2030 * \brief Collect cumulative data range of all DataSets
2031 *
2032 * \param[inout] range Data range of all DataSets
2033 * \param[in] onlyVisible Only collect range of visible DataSets
2034 */
2035void Renderer::collectDataRanges(double *range, bool onlyVisible)
2036{
2037    range[0] = DBL_MAX;
2038    range[1] = -DBL_MAX;
2039
2040    for (DataSetHashmap::iterator itr = _dataSets.begin();
2041         itr != _dataSets.end(); ++itr) {
2042        if (!onlyVisible || itr->second->getVisibility()) {
2043            double r[2];
2044            itr->second->getDataRange(r);
2045            range[0] = min2(range[0], r[0]);
2046            range[1] = max2(range[1], r[1]);
2047        }
2048    }
2049    if (range[0] == DBL_MAX)
2050        range[0] = 0;
2051    if (range[1] == -DBL_MAX)
2052        range[1] = 1;
2053}
2054
2055#ifdef notdef
2056void Renderer::setPerspectiveCameraByBounds(double bounds[6])
2057{
2058    vtkSmartPointer<vtkCamera> camera = _renderer->GetActiveCamera();
2059    camera->ParallelProjectionOff();
2060    camera->Reset();
2061}
2062#endif
2063
2064/**
2065 * \brief Initialize the camera zoom region to include the bounding volume given
2066 */
2067void Renderer::initCamera()
2068{
2069    double bounds[6];
2070    collectBounds(bounds, true);
2071    _imgWorldOrigin[0] = bounds[0];
2072    _imgWorldOrigin[1] = bounds[2];
2073    _imgWorldDims[0] = bounds[1] - bounds[0];
2074    _imgWorldDims[1] = bounds[3] - bounds[2];
2075    _cameraPan[0] = 0;
2076    _cameraPan[1] = 0;
2077    _cameraZoomRatio = 1;
2078
2079    if (_cameraMode == IMAGE) {
2080        _renderer->ResetCamera();
2081        setCameraZoomRegion(_imgWorldOrigin[0], _imgWorldOrigin[1],
2082                            _imgWorldDims[0], _imgWorldDims[1]);
2083        resetAxes();
2084    } else if (_cameraMode == ORTHO) {
2085        _renderer->GetActiveCamera()->ParallelProjectionOn();
2086        resetAxes();
2087        _renderer->ResetCamera();
2088        computeScreenWorldCoords();
2089    } else if (_cameraMode == PERSPECTIVE) {
2090        _renderer->GetActiveCamera()->ParallelProjectionOff();
2091        resetAxes();
2092        _renderer->ResetCamera();
2093        computeScreenWorldCoords();
2094    }
2095}
2096
2097/**
2098 * \brief Print debugging info about a vtkCamera
2099 */
2100void Renderer::printCameraInfo(vtkCamera *camera)
2101{
2102    TRACE("Parallel Scale: %g, Cam pos: %g %g %g, Clipping range: %g %g",
2103          camera->GetParallelScale(),
2104          camera->GetPosition()[0],
2105          camera->GetPosition()[1],
2106          camera->GetPosition()[2],
2107          camera->GetClippingRange()[0],
2108          camera->GetClippingRange()[1]);
2109}
2110
2111/**
2112 * \brief Set the RGB background color to render into the image
2113 */
2114void Renderer::setBackgroundColor(float color[3])
2115{
2116    _bgColor[0] = color[0];
2117    _bgColor[1] = color[1];
2118    _bgColor[2] = color[2];
2119    _renderer->SetBackground(_bgColor[0], _bgColor[1], _bgColor[2]);
2120    _needsRedraw = true;
2121}
2122
2123/**
2124 * \brief Set the opacity of the specified DataSet's associated graphics objects
2125 */
2126void Renderer::setOpacity(const DataSetId& id, double opacity)
2127{
2128    setPseudoColorOpacity(id, opacity);
2129    setContourOpacity(id, opacity);
2130    setPolyDataOpacity(id, opacity);
2131}
2132
2133/**
2134 * \brief Turn on/off rendering of the specified DataSet's associated graphics objects
2135 */
2136void Renderer::setVisibility(const DataSetId& id, bool state)
2137{
2138    DataSetHashmap::iterator itr;
2139
2140    bool doAll = false;
2141
2142    if (id.compare("all") == 0) {
2143        itr = _dataSets.begin();
2144        doAll = true;
2145    } else {
2146        itr = _dataSets.find(id);
2147    }
2148    if (itr == _dataSets.end()) {
2149        ERROR("Unknown dataset %s", id.c_str());
2150        return;
2151    }
2152
2153    do {
2154        itr->second->setVisibility(state);
2155    } while (doAll && ++itr != _dataSets.end());
2156
2157    setPseudoColorVisibility(id, state);
2158    setContourVisibility(id, state);
2159    setPolyDataVisibility(id, state);
2160}
2161
2162/**
2163 * \brief Cause the rendering to render a new image if needed
2164 *
2165 * The _needsRedraw flag indicates if a state change has occured since
2166 * the last rendered frame
2167 */
2168bool Renderer::render()
2169{
2170    if (_needsRedraw) {
2171        if (_cameraMode == IMAGE) {
2172            for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
2173                 itr != _pseudoColors.end(); ++itr) {
2174                itr->second->setClippingPlanes(_clippingPlanes);
2175            }
2176            for (Contour2DHashmap::iterator itr = _contours.begin();
2177                 itr != _contours.end(); ++itr) {
2178                itr->second->setClippingPlanes(_clippingPlanes);
2179            }
2180            for (PolyDataHashmap::iterator itr = _polyDatas.begin();
2181                 itr != _polyDatas.end(); ++itr) {
2182                itr->second->setClippingPlanes(_clippingPlanes);
2183            }
2184        } else {
2185            for (PseudoColorHashmap::iterator itr = _pseudoColors.begin();
2186                 itr != _pseudoColors.end(); ++itr) {
2187                itr->second->setClippingPlanes(NULL);
2188            }
2189            for (Contour2DHashmap::iterator itr = _contours.begin();
2190                 itr != _contours.end(); ++itr) {
2191                itr->second->setClippingPlanes(NULL);
2192            }
2193            for (PolyDataHashmap::iterator itr = _polyDatas.begin();
2194                 itr != _polyDatas.end(); ++itr) {
2195                itr->second->setClippingPlanes(NULL);
2196            }
2197        }
2198        _renderWindow->Render();
2199        _needsRedraw = false;
2200        return true;
2201    } else
2202        return false;
2203}
2204
2205/// Get the pixel width of the render window/image
2206int Renderer::getWindowWidth() const
2207{
2208    return _windowWidth;
2209}
2210
2211/// Get the pixel height of the render window/image
2212int Renderer::getWindowHeight() const
2213{
2214    return _windowHeight;
2215}
2216
2217/**
2218 * \brief Read back the rendered framebuffer image
2219 */
2220void Renderer::getRenderedFrame(vtkUnsignedCharArray *imgData)
2221{
2222    _renderWindow->GetPixelData(0, 0, _windowWidth-1, _windowHeight-1, 1, imgData);
2223    TRACE("Image data size: %d", imgData->GetSize());
2224}
2225
2226/**
2227 * \brief Get nearest data value given display coordinates x,y
2228 *
2229 * Note: no interpolation is performed on data
2230 */
2231double Renderer::getDataValueAtPixel(const DataSetId& id, int x, int y)
2232{
2233    vtkSmartPointer<vtkCoordinate> coord = vtkSmartPointer<vtkCoordinate>::New();
2234    coord->SetCoordinateSystemToDisplay();
2235    coord->SetValue(x, y, 0);
2236    double *worldCoords = coord->GetComputedWorldValue(_renderer);
2237
2238    TRACE("Pixel coords: %d, %d\nWorld coords: %.12e, %.12e, %12e", x, y,
2239          worldCoords[0],
2240          worldCoords[1],
2241          worldCoords[2]);
2242
2243    return getDataValue(id, worldCoords[0], worldCoords[1], worldCoords[2]);
2244}
2245
2246/**
2247 * \brief Get nearest data value given world coordinates x,y,z
2248 *
2249 * Note: no interpolation is performed on data
2250 */
2251double Renderer::getDataValue(const DataSetId& id, double x, double y, double z)
2252{
2253    DataSet *ds = getDataSet(id);
2254    if (ds == NULL)
2255        return 0;
2256    vtkDataSet *vtkds = ds->getVtkDataSet();
2257    vtkIdType pt = vtkds->FindPoint(x, y, z);
2258    return vtkds->GetPointData()->GetScalars()->GetComponent(pt, 0);
2259}
Note: See TracBrowser for help on using the repository browser.