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

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