source: vtkvis/trunk/HeightMap.cpp @ 5816

Last change on this file since 5816 was 5816, checked in by ldelgass, 9 years ago

merge r5814:r5815 from vtkvis release branch

  • Property svn:eol-style set to native
File size: 43.2 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (C) 2004-2012  HUBzero Foundation, LLC
4 *
5 * Author: Leif Delgass <ldelgass@purdue.edu>
6 */
7
8#include <cassert>
9
10#include <vtkDataSet.h>
11#include <vtkPointData.h>
12#include <vtkCellData.h>
13#include <vtkTrivialProducer.h>
14#include <vtkCellDataToPointData.h>
15#include <vtkPolyDataMapper.h>
16#include <vtkUnstructuredGrid.h>
17#include <vtkProperty.h>
18#include <vtkImageData.h>
19#include <vtkLookupTable.h>
20#include <vtkTransform.h>
21#include <vtkDelaunay2D.h>
22#include <vtkDelaunay3D.h>
23#include <vtkGaussianSplatter.h>
24#include <vtkExtractVOI.h>
25#include <vtkDataSetSurfaceFilter.h>
26#include <vtkContourFilter.h>
27#include <vtkStripper.h>
28#include <vtkWarpScalar.h>
29#include <vtkPropAssembly.h>
30#include <vtkCutter.h>
31#include <vtkPlane.h>
32
33#include "HeightMap.h"
34#include "Renderer.h"
35#include "Trace.h"
36
37using namespace VtkVis;
38
39#define TRANSLATE_TO_ZORIGIN
40
41HeightMap::HeightMap(int numContours, double heightScale) :
42    GraphicsObject(),
43    _numContours(numContours),
44    _contourColorMap(false),
45    _contourEdgeWidth(1.0),
46    _warpScale(heightScale),
47    _sliceAxis(Z_AXIS),
48    _pipelineInitialized(false),
49    _cloudStyle(CLOUD_MESH),
50    _colorMap(NULL),
51    _colorMode(COLOR_BY_SCALAR),
52    _colorFieldType(DataSet::POINT_DATA),
53    _renderer(NULL)
54{
55    _contourEdgeColor[0] = 0.0f;
56    _contourEdgeColor[1] = 0.0f;
57    _contourEdgeColor[2] = 0.0f;
58    _colorFieldRange[0] = DBL_MAX;
59    _colorFieldRange[1] = -DBL_MAX;
60}
61
62HeightMap::HeightMap(const std::vector<double>& contours, double heightScale) :
63    GraphicsObject(),
64    _numContours(contours.size()),
65    _contours(contours),
66    _contourColorMap(false),
67    _contourEdgeWidth(1.0),
68    _warpScale(heightScale),
69    _sliceAxis(Z_AXIS),
70    _pipelineInitialized(false),
71    _cloudStyle(CLOUD_MESH),
72    _colorMap(NULL),
73    _colorMode(COLOR_BY_SCALAR),
74    _colorFieldType(DataSet::POINT_DATA),
75    _renderer(NULL)
76{
77    _contourEdgeColor[0] = 0.0f;
78    _contourEdgeColor[1] = 0.0f;
79    _contourEdgeColor[2] = 0.0f;
80    _colorFieldRange[0] = DBL_MAX;
81    _colorFieldRange[1] = -DBL_MAX;
82}
83
84HeightMap::~HeightMap()
85{
86#ifdef WANT_TRACE
87    if (_dataSet != NULL)
88        TRACE("Deleting HeightMap for %s", _dataSet->getName().c_str());
89    else
90        TRACE("Deleting HeightMap with NULL DataSet");
91#endif
92}
93
94void HeightMap::setDataSet(DataSet *dataSet,
95                           Renderer *renderer)
96{
97    if (_dataSet != dataSet) {
98        _dataSet = dataSet;
99        _renderer = renderer;
100
101        if (_dataSet != NULL) {
102            TRACE("DataSet name: '%s' type: %s",
103                  _dataSet->getName().c_str(),
104                  _dataSet->getVtkType());
105
106            if (renderer->getUseCumulativeRange()) {
107                renderer->getCumulativeDataRange(_dataRange,
108                                                 _dataSet->getActiveScalarsName(),
109                                                 1);
110                const char *activeVectors = _dataSet->getActiveVectorsName();
111                if (activeVectors != NULL) {
112                    renderer->getCumulativeDataRange(_vectorMagnitudeRange,
113                                                     activeVectors,
114                                                     3);
115                    for (int i = 0; i < 3; i++) {
116                        renderer->getCumulativeDataRange(_vectorComponentRange[i],
117                                                         activeVectors,
118                                                         3, i);
119                    }
120                }
121            } else {
122                _dataSet->getScalarRange(_dataRange);
123                _dataSet->getVectorRange(_vectorMagnitudeRange);
124                for (int i = 0; i < 3; i++) {
125                    _dataSet->getVectorRange(_vectorComponentRange[i], i);
126                }
127            }
128        }
129
130        update();
131    }
132}
133
134/**
135 * Compute a data scaling factor to make maximum height (at default _warpScale)
136 * equivalent to largest dimension of data set
137 */
138void HeightMap::computeDataScale()
139{
140    if (_dataSet == NULL)
141        return;
142
143    double bounds[6];
144    double boundsRange = 0.0;
145    _dataSet->getBounds(bounds);
146    for (int i = 0; i < 6; i+=2) {
147        double r = bounds[i+1] - bounds[i];
148        if (r > boundsRange)
149            boundsRange = r;
150    }
151    double datRange = _dataRange[1] - _dataRange[0];
152    if (datRange < 1.0e-17) {
153        _dataScale = 1.0;
154    } else {
155        _dataScale = boundsRange / datRange;
156    }
157    TRACE("Bounds range: %g data range: %g scaling: %g",
158          boundsRange, datRange, _dataScale);
159}
160
161/**
162 * \brief Create and initialize VTK Props to render the colormapped dataset
163 */
164void HeightMap::initProp()
165{
166    if (_dsActor == NULL) {
167        _dsActor = vtkSmartPointer<vtkActor>::New();
168        _dsActor->GetProperty()->SetOpacity(_opacity);
169        _dsActor->GetProperty()->SetColor(_color[0],
170                                          _color[1],
171                                          _color[2]);
172        _dsActor->GetProperty()->SetEdgeColor(_edgeColor[0],
173                                              _edgeColor[1],
174                                              _edgeColor[2]);
175        _dsActor->GetProperty()->SetLineWidth(_edgeWidth);
176        _dsActor->GetProperty()->EdgeVisibilityOff();
177        _dsActor->GetProperty()->SetAmbient(0.2);
178        _dsActor->GetProperty()->LightingOn();
179    }
180    if (_contourActor == NULL) {
181        _contourActor = vtkSmartPointer<vtkActor>::New();
182        _contourActor->GetProperty()->SetOpacity(_opacity);
183        _contourActor->GetProperty()->SetColor(_contourEdgeColor[0],
184                                               _contourEdgeColor[1],
185                                               _contourEdgeColor[2]);
186        _contourActor->GetProperty()->SetEdgeColor(_contourEdgeColor[0],
187                                                   _contourEdgeColor[1],
188                                                   _contourEdgeColor[2]);
189        _contourActor->GetProperty()->SetLineWidth(_contourEdgeWidth);
190        _contourActor->GetProperty()->EdgeVisibilityOff();
191        _contourActor->GetProperty()->SetAmbient(1.0);
192        _contourActor->GetProperty()->LightingOff();
193    }
194    // Contour actor is added in update() if contours are produced
195    if (_prop == NULL) {
196        _prop = vtkSmartPointer<vtkAssembly>::New();
197        getAssembly()->AddPart(_dsActor);
198    } else {
199        getAssembly()->RemovePart(_contourActor);
200    }
201}
202
203/**
204 * \brief Internal method to set up pipeline after a state change
205 */
206void HeightMap::update()
207{
208    if (_dataSet == NULL)
209        return;
210
211    TRACE("DataSet: %s", _dataSet->getName().c_str());
212
213    vtkDataSet *ds = _dataSet->getVtkDataSet();
214
215    computeDataScale();
216
217    // Contour filter to generate isolines
218    if (_contourFilter == NULL) {
219        _contourFilter = vtkSmartPointer<vtkContourFilter>::New();
220    }
221
222    // Mapper, actor to render color-mapped data set
223    if (_mapper == NULL) {
224        _mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
225        // Map scalars through lookup table regardless of type
226        _mapper->SetColorModeToMapScalars();
227    }
228
229    if (!_pipelineInitialized) {
230        vtkAlgorithmOutput *dsOutput = NULL;
231        vtkSmartPointer<vtkCellDataToPointData> cellToPtData;
232        vtkSmartPointer<vtkTrivialProducer> prod;
233
234        if (ds->GetPointData() == NULL ||
235            ds->GetPointData()->GetScalars() == NULL) {
236            TRACE("No scalar point data in dataset %s", _dataSet->getName().c_str());
237            if (ds->GetCellData() != NULL &&
238                ds->GetCellData()->GetScalars() != NULL) {
239                cellToPtData =
240                    vtkSmartPointer<vtkCellDataToPointData>::New();
241#ifdef USE_VTK6
242                cellToPtData->SetInputData(ds);
243#else
244                cellToPtData->SetInput(ds);
245#endif
246                cellToPtData->PassCellDataOn();
247                dsOutput = cellToPtData->GetOutputPort();
248            } else {
249                USER_ERROR("No scalar field was found in the data set.");
250                return;
251            }
252        } else {
253            prod = vtkSmartPointer<vtkTrivialProducer>::New();
254            prod->SetOutput(ds);
255            dsOutput = prod->GetOutputPort();
256        }
257
258        _splatter = NULL;
259
260        if (_dataSet->isCloud()) {
261            // DataSet is a point cloud
262            PrincipalPlane plane;
263            double offset;
264            if (_dataSet->is2D(&plane, &offset)) {
265                if (_cloudStyle == CLOUD_MESH) {
266                    // Result of Delaunay2D is a PolyData
267                    vtkSmartPointer<vtkDelaunay2D> mesher = vtkSmartPointer<vtkDelaunay2D>::New();
268                    if (plane == PLANE_ZY) {
269                        vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
270                        trans->RotateWXYZ(90, 0, 1, 0);
271                        if (offset != 0.0) {
272                            trans->Translate(-offset, 0, 0);
273                        }
274                        mesher->SetTransform(trans);
275                        _sliceAxis = X_AXIS;
276                    } else if (plane == PLANE_XZ) {
277                        vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
278                        trans->RotateWXYZ(-90, 1, 0, 0);
279                        if (offset != 0.0) {
280                            trans->Translate(0, -offset, 0);
281                        }
282                        mesher->SetTransform(trans);
283                        _sliceAxis = Y_AXIS;
284                    } else if (offset != 0.0) {
285                        // XY with Z offset
286                        vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
287                        trans->Translate(0, 0, -offset);
288                        mesher->SetTransform(trans);
289                    }
290                    mesher->SetInputConnection(dsOutput);
291                    vtkAlgorithmOutput *warpOutput = initWarp(mesher->GetOutputPort());
292                    _mapper->SetInputConnection(warpOutput);
293                    _contourFilter->SetInputConnection(warpOutput);
294                } else {
295                    // _cloudStyle == CLOUD_SPLAT
296                    if (_splatter == NULL)
297                        _splatter = vtkSmartPointer<vtkGaussianSplatter>::New();
298                    if (_volumeSlicer == NULL)
299                        _volumeSlicer = vtkSmartPointer<vtkExtractVOI>::New();
300                    _splatter->SetInputConnection(dsOutput);
301                    int dims[3];
302                    _splatter->GetSampleDimensions(dims);
303                    TRACE("Sample dims: %d %d %d", dims[0], dims[1], dims[2]);
304                    if (plane == PLANE_ZY) {
305                        dims[0] = 3;
306                        _volumeSlicer->SetVOI(1, 1, 0, dims[1]-1, 0, dims[1]-1);
307                        _sliceAxis = X_AXIS;
308                    } else if (plane == PLANE_XZ) {
309                        dims[1] = 3;
310                        _volumeSlicer->SetVOI(0, dims[0]-1, 1, 1, 0, dims[2]-1);
311                        _sliceAxis = Y_AXIS;
312                    } else {
313                        dims[2] = 3;
314                        _volumeSlicer->SetVOI(0, dims[0]-1, 0, dims[1]-1, 1, 1);
315                    }
316                    _splatter->SetSampleDimensions(dims);
317                    double bounds[6];
318                    _splatter->Update();
319                    _splatter->GetModelBounds(bounds);
320                    TRACE("Model bounds: %g %g %g %g %g %g",
321                          bounds[0], bounds[1],
322                          bounds[2], bounds[3],
323                          bounds[4], bounds[5]);
324                    _volumeSlicer->SetInputConnection(_splatter->GetOutputPort());
325                    _volumeSlicer->SetSampleRate(1, 1, 1);
326                    vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
327                    gf->UseStripsOn();
328                    gf->SetInputConnection(_volumeSlicer->GetOutputPort());
329                    vtkAlgorithmOutput *warpOutput = initWarp(gf->GetOutputPort());
330                    _mapper->SetInputConnection(warpOutput);
331                    _contourFilter->SetInputConnection(warpOutput);
332                }
333            } else {
334                // 3D point cloud
335                vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
336                gf->UseStripsOn();
337                if (_cloudStyle == CLOUD_MESH) {
338                     // Result of Delaunay3D mesher is unstructured grid
339                    vtkSmartPointer<vtkDelaunay3D> mesher = vtkSmartPointer<vtkDelaunay3D>::New();
340                    mesher->SetInputConnection(dsOutput);
341                    // Run the mesher
342                    mesher->Update();
343                    // Get bounds of resulting grid
344                    double bounds[6];
345                    mesher->GetOutput()->GetBounds(bounds);
346                    // Sample a plane within the grid bounding box
347                    vtkSmartPointer<vtkCutter> cutter = vtkSmartPointer<vtkCutter>::New();
348                    cutter->SetInputConnection(mesher->GetOutputPort());
349                    if (_cutPlane == NULL) {
350                        _cutPlane = vtkSmartPointer<vtkPlane>::New();
351                    }
352                    _cutPlane->SetNormal(0, 0, 1);
353                    _cutPlane->SetOrigin(0,
354                                         0,
355                                         bounds[4] + (bounds[5]-bounds[4])/2.);
356                    cutter->SetCutFunction(_cutPlane);
357                    gf->SetInputConnection(cutter->GetOutputPort());
358                } else {
359                    // _cloudStyle == CLOUD_SPLAT
360                    if (_splatter == NULL)
361                        _splatter = vtkSmartPointer<vtkGaussianSplatter>::New();
362                    _splatter->SetInputConnection(dsOutput);
363                    int dims[3];
364                    _splatter->GetSampleDimensions(dims);
365                    TRACE("Sample dims: %d %d %d", dims[0], dims[1], dims[2]);
366                    dims[2] = 3;
367                    _splatter->SetSampleDimensions(dims);
368                    double bounds[6];
369                    _splatter->Update();
370                    _splatter->GetModelBounds(bounds);
371                    TRACE("Model bounds: %g %g %g %g %g %g",
372                          bounds[0], bounds[1],
373                          bounds[2], bounds[3],
374                          bounds[4], bounds[5]);
375                    if (_volumeSlicer == NULL)
376                        _volumeSlicer = vtkSmartPointer<vtkExtractVOI>::New();
377                    _volumeSlicer->SetInputConnection(_splatter->GetOutputPort());
378                    _volumeSlicer->SetVOI(0, dims[0]-1, 0, dims[1]-1, 1, 1);
379                    _volumeSlicer->SetSampleRate(1, 1, 1);
380                    gf->SetInputConnection(_volumeSlicer->GetOutputPort());
381                }
382                vtkAlgorithmOutput *warpOutput = initWarp(gf->GetOutputPort());
383                _mapper->SetInputConnection(warpOutput);
384                _contourFilter->SetInputConnection(warpOutput);
385            }
386        } else if (vtkPolyData::SafeDownCast(ds) != NULL) {
387            // DataSet is a vtkPolyData with lines and/or polygons
388            vtkAlgorithmOutput *warpOutput = initWarp(dsOutput);
389            if (warpOutput != NULL) {
390                _mapper->SetInputConnection(warpOutput);
391                _contourFilter->SetInputConnection(warpOutput);
392            } else {
393                _mapper->SetInputConnection(dsOutput);
394                _contourFilter->SetInputConnection(dsOutput);
395            }
396        } else {
397            // DataSet is NOT a vtkPolyData
398            // Can be: image/volume/uniform grid, structured grid, unstructured grid, rectilinear grid
399            vtkSmartPointer<vtkDataSetSurfaceFilter> gf = vtkSmartPointer<vtkDataSetSurfaceFilter>::New();
400            gf->UseStripsOn();
401            vtkImageData *imageData = vtkImageData::SafeDownCast(ds);
402            if (!_dataSet->is2D() && imageData != NULL) {
403                // 3D image/volume/uniform grid
404                if (_volumeSlicer == NULL)
405                    _volumeSlicer = vtkSmartPointer<vtkExtractVOI>::New();
406                int dims[3];
407                imageData->GetDimensions(dims);
408                TRACE("Image data dimensions: %d %d %d", dims[0], dims[1], dims[2]);
409                _volumeSlicer->SetInputConnection(dsOutput);
410                _volumeSlicer->SetVOI(0, dims[0]-1, 0, dims[1]-1, (dims[2]-1)/2, (dims[2]-1)/2);
411                _volumeSlicer->SetSampleRate(1, 1, 1);
412                gf->SetInputConnection(_volumeSlicer->GetOutputPort());
413            } else if (!_dataSet->is2D() && imageData == NULL) {
414                // 3D structured grid, unstructured grid, or rectilinear grid
415                double bounds[6];
416                ds->GetBounds(bounds);
417                // Sample a plane within the grid bounding box
418                vtkSmartPointer<vtkCutter> cutter = vtkSmartPointer<vtkCutter>::New();
419                cutter->SetInputConnection(dsOutput);
420                if (_cutPlane == NULL) {
421                    _cutPlane = vtkSmartPointer<vtkPlane>::New();
422                }
423                _cutPlane->SetNormal(0, 0, 1);
424                _cutPlane->SetOrigin(0,
425                                     0,
426                                     bounds[4] + (bounds[5]-bounds[4])/2.);
427                cutter->SetCutFunction(_cutPlane);
428                gf->SetInputConnection(cutter->GetOutputPort());
429            } else {
430                // 2D data
431                gf->SetInputConnection(dsOutput);
432            }
433            vtkAlgorithmOutput *warpOutput = initWarp(gf->GetOutputPort());
434            _mapper->SetInputConnection(warpOutput);
435            _contourFilter->SetInputConnection(warpOutput);
436        }
437    }
438
439    _contourFilter->ComputeNormalsOff();
440    _contourFilter->ComputeGradientsOff();
441
442    // Speed up multiple contour computation at cost of extra memory use
443    if (_numContours > 1) {
444        _contourFilter->UseScalarTreeOn();
445    } else {
446        _contourFilter->UseScalarTreeOff();
447    }
448
449    _contourFilter->SetNumberOfContours(_numContours);
450
451    if (_contours.empty()) {
452        // Evenly spaced isovalues
453        if (_colorFieldRange[0] <= _colorFieldRange[1]) {
454            _contourFilter->GenerateValues(_numContours, _colorFieldRange[0], _colorFieldRange[1]);
455        } else {
456            _contourFilter->GenerateValues(_numContours, _dataRange[0], _dataRange[1]);
457        }
458    } else {
459        // User-supplied isovalues
460        for (int i = 0; i < _numContours; i++) {
461            _contourFilter->SetValue(i, _contours[i]);
462        }
463    }
464
465    initProp();
466
467    if (_contourMapper == NULL) {
468        _contourMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
469        _contourMapper->ScalarVisibilityOff();
470        _contourMapper->SetResolveCoincidentTopologyToPolygonOffset();
471        vtkSmartPointer<vtkStripper> stripper = vtkSmartPointer<vtkStripper>::New();
472        stripper->SetInputConnection(_contourFilter->GetOutputPort());
473        _contourMapper->SetInputConnection(stripper->GetOutputPort());
474        _contourActor->SetMapper(_contourMapper);
475        //_contourActor->InterpolateScalarsBeforeMappingOn();
476    }
477
478    setInterpolateBeforeMapping(true);
479
480    if (_lut == NULL) {
481        setColorMap(ColorMap::getDefault());
482        setColorMode(_colorMode);
483    } else if (!_pipelineInitialized) {
484        double *rangePtr = _colorFieldRange;
485        if (_colorFieldRange[0] > _colorFieldRange[1]) {
486            rangePtr = NULL;
487        }
488        setColorMode(_colorMode, _colorFieldType, _colorFieldName.c_str(), rangePtr);
489    }
490
491    _pipelineInitialized = true;
492
493    TRACE("DataSet active scalars: %s", _dataSet->getActiveScalarsName());
494    // Ensure updated dataScale is applied
495    if (_warp != NULL) {
496        _warp->SetScaleFactor(_warpScale * _dataScale);
497    }
498#ifdef TRANSLATE_TO_ZORIGIN
499    double pos[3];
500    pos[0] = 0;
501    pos[1] = 0;
502    pos[2] = - _warpScale * _dataScale * _dataRange[0];
503    setPosition(pos);
504    TRACE("Z_POS: %g", pos[2]);
505#endif
506    _dsActor->SetMapper(_mapper);
507
508    _mapper->Update();
509    _contourMapper->Update();
510
511    // Only add contour actor to assembly if contours were
512    // produced, in order to prevent messing up assembly bounds
513    double bounds[6];
514    _contourActor->GetBounds(bounds);
515    if (bounds[0] <= bounds[1]) {
516        getAssembly()->AddPart(_contourActor);
517    }
518}
519
520void HeightMap::setCloudStyle(CloudStyle style)
521{
522    if (style != _cloudStyle) {
523        _cloudStyle = style;
524        if (_dataSet != NULL) {
525            _pipelineInitialized = false;
526            update();
527        }
528    }
529}
530
531void HeightMap::setInterpolateBeforeMapping(bool state)
532{
533    if (_mapper != NULL) {
534        _mapper->SetInterpolateScalarsBeforeMapping((state ? 1 : 0));
535    }
536}
537
538void HeightMap::setAspect(double aspect)
539{
540    double bounds[6];
541    vtkDataSet *ds = _dataSet->getVtkDataSet();
542    ds->GetBounds(bounds);
543    double size[3];
544    size[0] = bounds[1] - bounds[0];
545    size[1] = bounds[3] - bounds[2];
546    size[2] = bounds[5] - bounds[4];
547    double scale[3];
548    scale[0] = scale[1] = scale[2] = 1.;
549
550    if (aspect == 1.0) {
551        // Square
552        switch (_sliceAxis) {
553        case X_AXIS: {
554            if (size[1] > size[2] && size[2] > 0.0) {
555                scale[2] = size[1] / size[2];
556            } else if (size[2] > size[1] && size[1] > 0.0) {
557                scale[1] = size[2] / size[1];
558            }
559        }
560            break;
561        case Y_AXIS: {
562            if (size[0] > size[2] && size[2] > 0.0) {
563                scale[2] = size[0] / size[2];
564            } else if (size[2] > size[0] && size[0] > 0.0) {
565                scale[0] = size[2] / size[0];
566            }
567        }
568            break;
569        case Z_AXIS: {
570            if (size[0] > size[1] && size[1] > 0.0) {
571                scale[1] = size[0] / size[1];
572            } else if (size[1] > size[0] && size[0] > 0.0) {
573                scale[0] = size[1] / size[0];
574            }
575        }
576            break;
577        }
578    } else if (aspect != 0.0) {
579        switch (_sliceAxis) {
580        case X_AXIS: {
581            if (aspect > 1.0) {
582                if (size[2] > size[1] && size[1] > 0.0) {
583                    scale[1] = (size[2] / aspect) / size[1];
584                } else if (size[2] > 0.0) {
585                    scale[2] = (size[1] * aspect) / size[2];
586                }
587            } else {
588                if (size[1] > size[2] && size[2] > 0.0) {
589                    scale[2] = (size[1] * aspect) / size[2];
590                } else if (size[1] > 0.0) {
591                    scale[1] = (size[2] / aspect) / size[1];
592                }
593            }
594        }
595            break;
596        case Y_AXIS: {
597            if (aspect > 1.0) {
598                if (size[0] > size[2] && size[2] > 0.0) {
599                    scale[2] = (size[0] / aspect) / size[2];
600                } else if (size[0] > 0.0) {
601                    scale[0] = (size[2] * aspect) / size[0];
602                }
603            } else {
604                if (size[2] > size[0] && size[0] > 0.0) {
605                    scale[0] = (size[2] * aspect) / size[0];
606                } else if (size[2] > 0.0) {
607                    scale[2] = (size[0] / aspect) / size[2];
608                }
609            }
610        }
611            break;
612        case Z_AXIS: {
613            if (aspect > 1.0) {
614                if (size[0] > size[1] && size[1] > 0.0) {
615                    scale[1] = (size[0] / aspect) / size[1];
616                } else if (size[0] > 0.0) {
617                    scale[0] = (size[1] * aspect) / size[0];
618                }
619            } else {
620                if (size[1] > size[0] && size[0] > 0.0) {
621                    scale[0] = (size[1] * aspect) / size[0];
622                } else if (size[1] > 0.0) {
623                    scale[1] = (size[0] / aspect) / size[1];
624                }
625            }
626        }
627            break;
628        }
629    }
630
631    TRACE("%s dims %g,%g,%g", _dataSet->getName().c_str(),
632          size[0], size[1], size[2]);
633    TRACE("Setting scale to %g,%g,%g", scale[0], scale[1], scale[2]);
634    setScale(scale);
635}
636
637vtkAlgorithmOutput *HeightMap::initWarp(vtkAlgorithmOutput *input)
638{
639    TRACE("Warp scale: %g", _warpScale);
640    if (_warpScale == 0.0) {
641        _warp = NULL;
642        _normalsGenerator = NULL;
643        return input;
644    } else if (input == NULL) {
645        ERROR("NULL input");
646        return input;
647    } else {
648        if (_warp == NULL)
649            _warp = vtkSmartPointer<vtkWarpScalar>::New();
650        if (_normalsGenerator == NULL)
651            _normalsGenerator = vtkSmartPointer<vtkPolyDataNormals>::New();
652        switch (_sliceAxis) {
653        case X_AXIS:
654            _warp->SetNormal(1, 0, 0);
655            break;
656        case Y_AXIS:
657            _warp->SetNormal(0, 1, 0);
658            break;
659        default:
660            _warp->SetNormal(0, 0, 1);
661        }
662        _warp->UseNormalOn();
663        _warp->SetScaleFactor(_warpScale * _dataScale);
664        _warp->SetInputConnection(input);
665        _normalsGenerator->SetInputConnection(_warp->GetOutputPort());
666        _normalsGenerator->SetFeatureAngle(90.);
667        _normalsGenerator->AutoOrientNormalsOff();
668        _normalsGenerator->ComputePointNormalsOn();
669        return _normalsGenerator->GetOutputPort();
670    }
671}
672
673vtkAlgorithmOutput *HeightMap::initWarp(vtkDataSet *dsInput)
674{
675    TRACE("Warp scale: %g", _warpScale);
676    if (_warpScale == 0.0) {
677        _warp = NULL;
678        _normalsGenerator = NULL;
679        return NULL;
680    } else if (dsInput == NULL) {
681        ERROR("NULL input");
682        return NULL;
683    } else {
684        if (_warp == NULL)
685            _warp = vtkSmartPointer<vtkWarpScalar>::New();
686        if (_normalsGenerator == NULL)
687            _normalsGenerator = vtkSmartPointer<vtkPolyDataNormals>::New();
688        switch (_sliceAxis) {
689        case X_AXIS:
690            _warp->SetNormal(1, 0, 0);
691            break;
692        case Y_AXIS:
693            _warp->SetNormal(0, 1, 0);
694            break;
695        default:
696            _warp->SetNormal(0, 0, 1);
697        }
698        _warp->UseNormalOn();
699        _warp->SetScaleFactor(_warpScale * _dataScale);
700#ifdef USE_VTK6
701        _warp->SetInputData(dsInput);
702#else
703        _warp->SetInput(dsInput);
704#endif
705        _normalsGenerator->SetInputConnection(_warp->GetOutputPort());
706        _normalsGenerator->SetFeatureAngle(90.);
707        _normalsGenerator->AutoOrientNormalsOff();
708        _normalsGenerator->ComputePointNormalsOn();
709        return _normalsGenerator->GetOutputPort();
710    }
711}
712
713/**
714 * \brief Controls relative scaling of height of mountain plot
715 */
716void HeightMap::setHeightScale(double scale)
717{
718    if (_warpScale == scale)
719        return;
720
721    _warpScale = scale;
722    if (_warp == NULL && scale != 0.0) {
723        vtkAlgorithmOutput *warpOutput = initWarp(_mapper->GetInputConnection(0, 0));
724        _mapper->SetInputConnection(warpOutput);
725        _contourFilter->SetInputConnection(warpOutput);
726    } else if (scale == 0.0) {
727        vtkAlgorithmOutput *warpInput = _warp->GetInputConnection(0, 0);
728        _mapper->SetInputConnection(warpInput);
729        _contourFilter->SetInputConnection(warpInput);
730        _warp = NULL;
731    } else {
732        _warp->SetScaleFactor(_warpScale * _dataScale);
733    }
734#ifdef TRANSLATE_TO_ZORIGIN
735    double pos[3];
736    pos[0] = 0;
737    pos[1] = 0;
738    pos[2] = - _warpScale * _dataScale * _dataRange[0];
739    setPosition(pos);
740    TRACE("Z_POS: %g", pos[2]);
741#endif
742    if (_mapper != NULL)
743        _mapper->Update();
744    if (_contourMapper != NULL)
745        _contourMapper->Update();
746}
747
748/**
749 * \brief Select a 2D slice plane from a 3D DataSet
750 *
751 * \param[in] axis Axis of slice plane
752 * \param[in] ratio Position [0,1] of slice plane along axis
753 */
754void HeightMap::selectVolumeSlice(Axis axis, double ratio)
755{
756    if (_dataSet->is2D()) {
757        WARN("DataSet not 3D, returning");
758        return;
759    }
760
761    if (_volumeSlicer == NULL &&
762        _cutPlane == NULL) {
763        WARN("Called before update() or DataSet is not a volume");
764        return;
765    }
766
767    _sliceAxis = axis;
768    if (_warp != NULL) {
769        switch (axis) {
770        case X_AXIS:
771            _warp->SetNormal(1, 0, 0);
772            break;
773        case Y_AXIS:
774            _warp->SetNormal(0, 1, 0);
775            break;
776        case Z_AXIS:
777            _warp->SetNormal(0, 0, 1);
778            break;
779        default:
780            ERROR("Invalid Axis");
781            return;
782        }
783    }
784
785    if (_cutPlane != NULL) {
786        double bounds[6];
787        _dataSet->getBounds(bounds);
788        switch (axis) {
789        case X_AXIS:
790            _cutPlane->SetNormal(1, 0, 0);
791            _cutPlane->SetOrigin(bounds[0] + (bounds[1]-bounds[0]) * ratio,
792                                 0,
793                                 0);
794            break;
795        case Y_AXIS:
796            _cutPlane->SetNormal(0, 1, 0);
797            _cutPlane->SetOrigin(0,
798                                 bounds[2] + (bounds[3]-bounds[2]) * ratio,
799                                 0);
800            break;
801        case Z_AXIS:
802            _cutPlane->SetNormal(0, 0, 1);
803            _cutPlane->SetOrigin(0,
804                                 0,
805                                 bounds[4] + (bounds[5]-bounds[4]) * ratio);
806            break;
807        default:
808            ERROR("Invalid Axis");
809            return;
810        }
811    } else {
812        int dims[3];
813        if (_splatter != NULL) {
814            _splatter->GetSampleDimensions(dims);
815        } else {
816            vtkImageData *imageData = vtkImageData::SafeDownCast(_dataSet->getVtkDataSet());
817            if (imageData == NULL) {
818                ERROR("Not a volume data set");
819                return;
820            }
821            imageData->GetDimensions(dims);
822        }
823        int voi[6];
824
825        switch (axis) {
826        case X_AXIS:
827            voi[0] = voi[1] = (int)((dims[0]-1) * ratio);
828            voi[2] = 0;
829            voi[3] = dims[1]-1;
830            voi[4] = 0;
831            voi[5] = dims[2]-1;
832            break;
833        case Y_AXIS:
834            voi[0] = 0;
835            voi[1] = dims[0]-1;
836            voi[2] = voi[3] = (int)((dims[1]-1) * ratio);
837            voi[4] = 0;
838            voi[5] = dims[2]-1;
839            break;
840        case Z_AXIS:
841            voi[0] = 0;
842            voi[1] = dims[0]-1;
843            voi[2] = 0;
844            voi[3] = dims[1]-1;
845            voi[4] = voi[5] = (int)((dims[2]-1) * ratio);
846            break;
847        default:
848            ERROR("Invalid Axis");
849            return;
850        }
851
852        _volumeSlicer->SetVOI(voi);
853    }
854
855    if (_mapper != NULL)
856        _mapper->Update();
857    if (_contourMapper != NULL)
858        _contourMapper->Update();
859}
860
861void HeightMap::updateRanges(Renderer *renderer)
862{
863    TRACE("Enter");
864
865    if (_dataSet == NULL) {
866        ERROR("called before setDataSet");
867        return;
868    }
869
870    if (renderer->getUseCumulativeRange()) {
871        renderer->getCumulativeDataRange(_dataRange,
872                                         _dataSet->getActiveScalarsName(),
873                                         1);
874        const char *activeVectors = _dataSet->getActiveVectorsName();
875        if (activeVectors != NULL) {
876            renderer->getCumulativeDataRange(_vectorMagnitudeRange,
877                                             activeVectors,
878                                             3);
879            for (int i = 0; i < 3; i++) {
880                renderer->getCumulativeDataRange(_vectorComponentRange[i],
881                                                 activeVectors,
882                                                 3, i);
883            }
884        }
885    } else {
886        _dataSet->getScalarRange(_dataRange);
887        _dataSet->getVectorRange(_vectorMagnitudeRange);
888        for (int i = 0; i < 3; i++) {
889            _dataSet->getVectorRange(_vectorComponentRange[i], i);
890        }
891    }
892 
893    // Need to update color map ranges
894    double *rangePtr = _colorFieldRange;
895    if (_colorFieldRange[0] > _colorFieldRange[1]) {
896        rangePtr = NULL;
897    }
898    setColorMode(_colorMode, _colorFieldType, _colorFieldName.c_str(), rangePtr);
899
900    if ((_contours.empty() && _numContours > 0) ||
901        _warpScale > 0.0) {
902        // Contour isovalues and/or warp need to be recomputed
903        update();
904    } else {
905        computeDataScale();
906    }
907
908    TRACE("Leave");
909}
910
911void HeightMap::setContourLineColorMapEnabled(bool mode)
912{
913    _contourColorMap = mode;
914
915    double *rangePtr = _colorFieldRange;
916    if (_colorFieldRange[0] > _colorFieldRange[1]) {
917        rangePtr = NULL;
918    }
919    setColorMode(_colorMode, _colorFieldType, _colorFieldName.c_str(), rangePtr);
920}
921
922void HeightMap::setColorMode(ColorMode mode)
923{
924    _colorMode = mode;
925    if (_dataSet == NULL)
926        return;
927
928    switch (mode) {
929    case COLOR_BY_SCALAR:
930        setColorMode(mode,
931                     _dataSet->getActiveScalarsType(),
932                     _dataSet->getActiveScalarsName());
933        break;
934    case COLOR_BY_VECTOR_MAGNITUDE:
935        setColorMode(mode,
936                     _dataSet->getActiveVectorsType(),
937                     _dataSet->getActiveVectorsName());
938        break;
939    case COLOR_BY_VECTOR_X:
940        setColorMode(mode,
941                     _dataSet->getActiveVectorsType(),
942                     _dataSet->getActiveVectorsName());
943        break;
944    case COLOR_BY_VECTOR_Y:
945        setColorMode(mode,
946                     _dataSet->getActiveVectorsType(),
947                     _dataSet->getActiveVectorsName());
948        break;
949    case COLOR_BY_VECTOR_Z:
950        setColorMode(mode,
951                     _dataSet->getActiveVectorsType(),
952                     _dataSet->getActiveVectorsName());
953        break;
954    case COLOR_CONSTANT:
955    default:
956        setColorMode(mode, DataSet::POINT_DATA, NULL, NULL);
957        break;
958    }
959}
960
961void HeightMap::setColorMode(ColorMode mode,
962                             const char *name, double range[2])
963{
964    if (_dataSet == NULL)
965        return;
966    DataSet::DataAttributeType type = DataSet::POINT_DATA;
967    int numComponents = 1;
968    if (name != NULL && strlen(name) > 0 &&
969        !_dataSet->getFieldInfo(name, &type, &numComponents)) {
970        ERROR("Field not found: %s", name);
971        return;
972    }
973    setColorMode(mode, type, name, range);
974}
975
976void HeightMap::setColorMode(ColorMode mode, DataSet::DataAttributeType type,
977                             const char *name, double range[2])
978{
979    _colorMode = mode;
980    _colorFieldType = type;
981    if (name == NULL)
982        _colorFieldName.clear();
983    else
984        _colorFieldName = name;
985    if (range == NULL) {
986        _colorFieldRange[0] = DBL_MAX;
987        _colorFieldRange[1] = -DBL_MAX;
988    } else {
989        memcpy(_colorFieldRange, range, sizeof(double)*2);
990    }
991
992    if (_dataSet == NULL || _mapper == NULL)
993        return;
994
995    switch (type) {
996    case DataSet::POINT_DATA:
997        _mapper->SetScalarModeToUsePointFieldData();
998        _contourMapper->SetScalarModeToUsePointFieldData();
999        break;
1000    case DataSet::CELL_DATA:
1001        _mapper->SetScalarModeToUseCellFieldData();
1002        _contourMapper->SetScalarModeToUseCellFieldData();
1003        break;
1004    default:
1005        ERROR("Unsupported DataAttributeType: %d", type);
1006        return;
1007    }
1008
1009    if (_splatter != NULL) {
1010        if (name != NULL && strlen(name) > 0) {
1011            _splatter->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, name);
1012        }
1013        _mapper->SelectColorArray("SplatterValues");
1014        _contourMapper->SelectColorArray("SplatterValues");
1015    } else if (name != NULL && strlen(name) > 0) {
1016        _mapper->SelectColorArray(name);
1017        _contourMapper->SelectColorArray(name);
1018    } else {
1019        _mapper->SetScalarModeToDefault();
1020        _contourMapper->SetScalarModeToDefault();
1021    }
1022
1023    if (_lut != NULL) {
1024        if (range != NULL) {
1025            _lut->SetRange(range);
1026            TRACE("range %g, %g", range[0], range[1]);
1027        } else if (name != NULL && strlen(name) > 0) {
1028            double r[2];
1029            int comp = -1;
1030            if (mode == COLOR_BY_VECTOR_X)
1031                comp = 0;
1032            else if (mode == COLOR_BY_VECTOR_Y)
1033                comp = 1;
1034            else if (mode == COLOR_BY_VECTOR_Z)
1035                comp = 2;
1036
1037            if (_renderer->getUseCumulativeRange()) {
1038                int numComponents;
1039                if  (!_dataSet->getFieldInfo(name, type, &numComponents)) {
1040                    ERROR("Field not found: %s, type: %d", name, type);
1041                    return;
1042                } else if (numComponents < comp+1) {
1043                    ERROR("Request for component %d in field with %d components",
1044                          comp, numComponents);
1045                    return;
1046                }
1047                _renderer->getCumulativeDataRange(r, name, type, numComponents, comp);
1048            } else {
1049                _dataSet->getDataRange(r, name, type, comp);
1050            }
1051            _lut->SetRange(r);
1052            TRACE("%s range %g, %g", name, r[0], r[1]);
1053        }
1054        _lut->Modified();
1055    }
1056
1057    if (_contourColorMap) {
1058        _contourMapper->ScalarVisibilityOn();
1059    } else {
1060        _contourMapper->ScalarVisibilityOff();
1061    }
1062
1063    switch (mode) {
1064    case COLOR_BY_SCALAR:
1065        _mapper->ScalarVisibilityOn();
1066        break;
1067    case COLOR_BY_VECTOR_MAGNITUDE:
1068        _mapper->ScalarVisibilityOn();
1069        if (_lut != NULL) {
1070            _lut->SetVectorModeToMagnitude();
1071        }
1072        break;
1073    case COLOR_BY_VECTOR_X:
1074        _mapper->ScalarVisibilityOn();
1075        if (_lut != NULL) {
1076            _lut->SetVectorModeToComponent();
1077            _lut->SetVectorComponent(0);
1078        }
1079        break;
1080    case COLOR_BY_VECTOR_Y:
1081        _mapper->ScalarVisibilityOn();
1082        if (_lut != NULL) {
1083            _lut->SetVectorModeToComponent();
1084            _lut->SetVectorComponent(1);
1085        }
1086        break;
1087    case COLOR_BY_VECTOR_Z:
1088        _mapper->ScalarVisibilityOn();
1089        if (_lut != NULL) {
1090            _lut->SetVectorModeToComponent();
1091            _lut->SetVectorComponent(2);
1092        }
1093        break;
1094    case COLOR_CONSTANT:
1095    default:
1096        _mapper->ScalarVisibilityOff();
1097        break;
1098    }
1099}
1100
1101/**
1102 * \brief Called when the color map has been edited
1103 */
1104void HeightMap::updateColorMap()
1105{
1106    setColorMap(_colorMap);
1107}
1108
1109/**
1110 * \brief Associate a colormap lookup table with the DataSet
1111 */
1112void HeightMap::setColorMap(ColorMap *cmap)
1113{
1114    if (cmap == NULL)
1115        return;
1116
1117    _colorMap = cmap;
1118
1119    if (_lut == NULL) {
1120        _lut = vtkSmartPointer<vtkLookupTable>::New();
1121        if (_mapper != NULL) {
1122            _mapper->UseLookupTableScalarRangeOn();
1123            _mapper->SetLookupTable(_lut);
1124        }
1125        if (_contourMapper != NULL) {
1126            _contourMapper->UseLookupTableScalarRangeOn();
1127            _contourMapper->SetLookupTable(_lut);
1128        }
1129        _lut->DeepCopy(cmap->getLookupTable());
1130        switch (_colorMode) {
1131        case COLOR_CONSTANT:
1132        case COLOR_BY_SCALAR:
1133            _lut->SetRange(_dataRange);
1134            break;
1135        case COLOR_BY_VECTOR_MAGNITUDE:
1136            _lut->SetRange(_vectorMagnitudeRange);
1137            break;
1138        case COLOR_BY_VECTOR_X:
1139            _lut->SetRange(_vectorComponentRange[0]);
1140            break;
1141        case COLOR_BY_VECTOR_Y:
1142            _lut->SetRange(_vectorComponentRange[1]);
1143            break;
1144        case COLOR_BY_VECTOR_Z:
1145            _lut->SetRange(_vectorComponentRange[2]);
1146            break;
1147        default:
1148            break;
1149        }
1150    } else {
1151        double range[2];
1152        _lut->GetTableRange(range);
1153        _lut->DeepCopy(cmap->getLookupTable());
1154        _lut->SetRange(range);
1155        _lut->Modified();
1156    }
1157
1158    switch (_colorMode) {
1159    case COLOR_BY_VECTOR_MAGNITUDE:
1160        _lut->SetVectorModeToMagnitude();
1161        break;
1162    case COLOR_BY_VECTOR_X:
1163        _lut->SetVectorModeToComponent();
1164        _lut->SetVectorComponent(0);
1165        break;
1166    case COLOR_BY_VECTOR_Y:
1167        _lut->SetVectorModeToComponent();
1168        _lut->SetVectorComponent(1);
1169        break;
1170    case COLOR_BY_VECTOR_Z:
1171        _lut->SetVectorModeToComponent();
1172        _lut->SetVectorComponent(2);
1173        break;
1174    default:
1175         break;
1176    }
1177}
1178
1179/**
1180 * \brief Specify number of evenly spaced contour lines to render
1181 *
1182 * Will override any existing contours
1183 */
1184void HeightMap::setNumContours(int numContours)
1185{
1186    _contours.clear();
1187    _numContours = numContours;
1188
1189    update();
1190}
1191
1192/**
1193 * \brief Specify an explicit list of contour isovalues
1194 *
1195 * Will override any existing contours
1196 */
1197void HeightMap::setContourList(const std::vector<double>& contours)
1198{
1199    _contours = contours;
1200    _numContours = (int)_contours.size();
1201
1202    update();
1203}
1204
1205/**
1206 * \brief Get the number of contours
1207 */
1208int HeightMap::getNumContours() const
1209{
1210    return _numContours;
1211}
1212
1213/**
1214 * \brief Get the contour list (may be empty if number of contours
1215 * was specified in place of a list)
1216 */
1217const std::vector<double>& HeightMap::getContourList() const
1218{
1219    return _contours;
1220}
1221
1222/**
1223 * \brief Turn on/off lighting of this object
1224 */
1225void HeightMap::setLighting(bool state)
1226{
1227    _lighting = state;
1228    if (_dsActor != NULL)
1229        _dsActor->GetProperty()->SetLighting((state ? 1 : 0));
1230}
1231
1232/**
1233 * \brief Turn on/off rendering of mesh edges
1234 */
1235void HeightMap::setEdgeVisibility(bool state)
1236{
1237    if (_dsActor != NULL) {
1238        _dsActor->GetProperty()->SetEdgeVisibility((state ? 1 : 0));
1239    }
1240}
1241
1242/**
1243 * \brief Turn on/off rendering of colormaped surface
1244 */
1245void HeightMap::setContourSurfaceVisibility(bool state)
1246{
1247    if (_dsActor != NULL) {
1248        _dsActor->SetVisibility((state ? 1 : 0));
1249    }
1250}
1251
1252/**
1253 * \brief Turn on/off rendering of contour isolines
1254 */
1255void HeightMap::setContourLineVisibility(bool state)
1256{
1257    if (_contourActor != NULL) {
1258        _contourActor->SetVisibility((state ? 1 : 0));
1259    }
1260}
1261
1262/**
1263 * \brief Set RGB color of mesh
1264 */
1265void HeightMap::setColor(float color[3])
1266{
1267    _color[0] = color[0];
1268    _color[1] = color[1];
1269    _color[2] = color[2];
1270    if (_dsActor != NULL)
1271        _dsActor->GetProperty()->SetColor(_color[0], _color[1], _color[2]);
1272}
1273
1274/**
1275 * \brief Set RGB color of polygon edges
1276 */
1277void HeightMap::setEdgeColor(float color[3])
1278{
1279    _edgeColor[0] = color[0];
1280    _edgeColor[1] = color[1];
1281    _edgeColor[2] = color[2];
1282    if (_dsActor != NULL)
1283        _dsActor->GetProperty()->SetEdgeColor(_edgeColor[0], _edgeColor[1], _edgeColor[2]);
1284}
1285
1286/**
1287 * \brief Set pixel width of polygon edges (may be a no-op)
1288 */
1289void HeightMap::setEdgeWidth(float edgeWidth)
1290{
1291    _edgeWidth = edgeWidth;
1292    if (_dsActor != NULL)
1293        _dsActor->GetProperty()->SetLineWidth(_edgeWidth);
1294}
1295
1296/**
1297 * \brief Set RGB color of contour isolines
1298 */
1299void HeightMap::setContourEdgeColor(float color[3])
1300{
1301    _contourEdgeColor[0] = color[0];
1302    _contourEdgeColor[1] = color[1];
1303    _contourEdgeColor[2] = color[2];
1304    if (_contourActor != NULL) {
1305        _contourActor->GetProperty()->SetColor(_contourEdgeColor[0],
1306                                               _contourEdgeColor[1],
1307                                               _contourEdgeColor[2]);
1308        _contourActor->GetProperty()->SetEdgeColor(_contourEdgeColor[0],
1309                                                   _contourEdgeColor[1],
1310                                                   _contourEdgeColor[2]);
1311    }
1312}
1313
1314/**
1315 * \brief Set pixel width of contour isolines (may be a no-op)
1316 */
1317void HeightMap::setContourEdgeWidth(float edgeWidth)
1318{
1319    _contourEdgeWidth = edgeWidth;
1320    if (_contourActor != NULL)
1321        _contourActor->GetProperty()->SetLineWidth(_contourEdgeWidth);
1322}
1323
1324/**
1325 * \brief Set a group of world coordinate planes to clip rendering
1326 *
1327 * Passing NULL for planes will remove all cliping planes
1328 */
1329void HeightMap::setClippingPlanes(vtkPlaneCollection *planes)
1330{
1331    if (_mapper != NULL) {
1332        _mapper->SetClippingPlanes(planes);
1333    }
1334    if (_contourMapper != NULL) {
1335        _contourMapper->SetClippingPlanes(planes);
1336    }
1337}
1338
Note: See TracBrowser for help on using the repository browser.