source: vtkvis/trunk/ImageCutplane.cpp @ 4635

Last change on this file since 4635 was 4256, checked in by ldelgass, 10 years ago

Add resmapling of non-uniform datasets to imagecutplane. Note: would eventually
like to share the resampled dataset among multiple pipelines (e.g. for volumes
and cutplanes).

File size: 16.9 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#include <cfloat>
10#include <cstring>
11
12#include <vtkDataSet.h>
13#include <vtkPointData.h>
14#include <vtkCellData.h>
15#include <vtkRectilinearGrid.h>
16#include <vtkStructuredGrid.h>
17#include <vtkPolyDataMapper.h>
18#include <vtkProperty.h>
19#include <vtkImageData.h>
20#include <vtkProbeFilter.h>
21#include <vtkGaussianSplatter.h>
22#include <vtkLookupTable.h>
23#include <vtkTransform.h>
24#include <vtkExtractVOI.h>
25#include <vtkDataSetSurfaceFilter.h>
26
27#include "ImageCutplane.h"
28#include "Renderer.h"
29#include "Trace.h"
30
31using namespace VtkVis;
32
33ImageCutplane::ImageCutplane() :
34    GraphicsObject(),
35    _pipelineInitialized(false),
36    _colorMap(NULL),
37    _renderer(NULL)
38{
39}
40
41ImageCutplane::~ImageCutplane()
42{
43#ifdef WANT_TRACE
44    if (_dataSet != NULL)
45        TRACE("Deleting ImageCutplane for %s", _dataSet->getName().c_str());
46    else
47        TRACE("Deleting ImageCutplane with NULL DataSet");
48#endif
49}
50
51/**
52 * \brief Create and initialize a VTK Prop to render the object
53 */
54void ImageCutplane::initProp()
55{
56    if (_prop == NULL) {
57        _prop = vtkSmartPointer<vtkAssembly>::New();
58
59        for (int i = 0; i < 3; i++) {
60            _actor[i] = vtkSmartPointer<vtkImageSlice>::New();
61            _borderActor[i] = vtkSmartPointer<vtkActor>::New();
62            vtkImageProperty *imgProperty = _actor[i]->GetProperty();
63            imgProperty->SetInterpolationTypeToLinear();
64            imgProperty->SetBackingColor(_color[0], _color[1], _color[2]);
65            imgProperty->BackingOff();
66            imgProperty->SetOpacity(_opacity);
67            imgProperty->SetAmbient(0.0);
68            imgProperty->SetDiffuse(1.0);
69            vtkProperty *property = _borderActor[i]->GetProperty();
70            property->SetColor(_color[0], _color[1], _color[2]);
71            property->SetEdgeColor(_edgeColor[0], _edgeColor[1], _edgeColor[2]);
72            property->SetLineWidth(_edgeWidth);
73            property->SetPointSize(_pointSize);
74            property->EdgeVisibilityOff();
75            property->SetOpacity(_opacity);
76            property->LightingOff();
77           
78            getAssembly()->AddPart(_borderActor[i]);
79        }
80    } else {
81        for (int i = 0; i < 3; i++) {
82            getAssembly()->RemovePart(_actor[i]);
83        }
84    }
85}
86
87void ImageCutplane::setInterpolationType(InterpType type)
88{
89    for (int i = 0; i < 3; i++) {
90        switch (type) {
91        case INTERP_NEAREST:
92            _actor[i]->GetProperty()->SetInterpolationTypeToNearest();
93            break;
94        case INTERP_LINEAR:
95            _actor[i]->GetProperty()->SetInterpolationTypeToLinear();
96            break;
97        case INTERP_CUBIC:
98            _actor[i]->GetProperty()->SetInterpolationTypeToCubic();
99            break;
100        }
101    }
102}
103
104/**
105 * \brief Set the material color (sets ambient, diffuse, and specular)
106 */
107void ImageCutplane::setColor(float color[3])
108{
109    GraphicsObject::setColor(color);
110
111    for (int i = 0; i < 3; i++) {
112        if (_actor[i] != NULL) {
113            _actor[i]->GetProperty()->SetBackingColor(color[0], color[1], color[2]);
114        }
115        if (_borderActor[i] != NULL) {
116            _borderActor[i]->GetProperty()->SetColor(color[0], color[1], color[2]);
117        }
118    }
119}
120
121void ImageCutplane::setAmbient(double ambient)
122{
123    for (int i = 0; i < 3; i++) {
124        if (_actor[i] != NULL) {
125            _actor[i]->GetProperty()->SetAmbient(ambient);
126        }
127    }
128}
129
130void ImageCutplane::setDiffuse(double diffuse)
131{
132    for (int i = 0; i < 3; i++) {
133        if (_actor[i] != NULL) {
134            _actor[i]->GetProperty()->SetDiffuse(diffuse);
135        }
136    }
137}
138
139void ImageCutplane::setOpacity(double opacity)
140{
141    for (int i = 0; i < 3; i++) {
142        if (_actor[i] != NULL) {
143            _actor[i]->GetProperty()->SetOpacity(opacity);
144        }
145    }
146}
147
148void ImageCutplane::update()
149{
150    if (_dataSet == NULL)
151        return;
152
153    vtkDataSet *ds = _dataSet->getVtkDataSet();
154
155    if (_dataSet->is2D()) {
156        USER_ERROR("Image cutplane requires a 3D data set");
157        _dataSet = NULL;
158        return;
159    }
160
161    if (ds->GetPointData() == NULL ||
162        ds->GetPointData()->GetScalars() == NULL) {
163        USER_ERROR("No scalar field was found in the data set");
164        return;
165    }
166
167    double bounds[6];
168    _dataSet->getBounds(bounds);
169    // Mapper, actor to render color-mapped data set
170    for (int i = 0; i < 3; i++) {
171        if (_cutPlane[i] == NULL) {
172            _cutPlane[i] = vtkSmartPointer<vtkPlane>::New();
173            switch (i) {
174            case 0:
175                _cutPlane[i]->SetNormal(1, 0, 0);
176                _cutPlane[i]->SetOrigin(bounds[0] + (bounds[1]-bounds[0])/2.,
177                                        0,
178                                        0);
179                break;
180            case 1:
181                _cutPlane[i]->SetNormal(0, 1, 0);
182                _cutPlane[i]->SetOrigin(0,
183                                        bounds[2] + (bounds[3]-bounds[2])/2.,
184                                        0);
185                break;
186            case 2:
187            default:
188                _cutPlane[i]->SetNormal(0, 0, 1);
189                _cutPlane[i]->SetOrigin(0,
190                                        0,
191                                        bounds[4] + (bounds[5]-bounds[4])/2.);
192                break;
193            }
194        }
195        if (_mapper[i] == NULL) {
196            _mapper[i] = vtkSmartPointer<vtkImageResliceMapper>::New();
197            _mapper[i]->SetSlicePlane(_cutPlane[i]);
198        }
199    }
200
201    if (!_pipelineInitialized) {
202        vtkImageData *imgData = vtkImageData::SafeDownCast(ds);
203        if (imgData == NULL) {
204            // Need to resample to ImageData
205            if (_dataSet->isCloud()) {
206                // DataSet is a 3D point cloud
207                vtkSmartPointer<vtkGaussianSplatter> splatter = vtkSmartPointer<vtkGaussianSplatter>::New();
208#ifdef USE_VTK6
209                splatter->SetInputData(ds);
210#else
211                splatter->SetInput(ds);
212#endif
213                int dims[3];
214                dims[0] = dims[1] = dims[2] = 64;
215                if (vtkStructuredGrid::SafeDownCast(ds) != NULL) {
216                    vtkStructuredGrid::SafeDownCast(ds)->GetDimensions(dims);
217                } else if (vtkRectilinearGrid::SafeDownCast(ds) != NULL) {
218                    vtkRectilinearGrid::SafeDownCast(ds)->GetDimensions(dims);
219                }
220                TRACE("Generating volume with dims (%d,%d,%d) from %d points",
221                      dims[0], dims[1], dims[2], ds->GetNumberOfPoints());
222                splatter->SetSampleDimensions(dims);
223                splatter->Update();
224
225                TRACE("Done generating volume");
226
227                for (int i = 0; i < 3; i++) {
228                    _mapper[i]->SetInputConnection(splatter->GetOutputPort());
229                }
230            } else {
231                // (Slow) Resample using ProbeFilter
232                double xLen = bounds[1] - bounds[0];
233                double yLen = bounds[3] - bounds[2];
234                double zLen = bounds[5] - bounds[4];
235
236                int dims[3];
237                dims[0] = dims[1] = dims[2] = 64;
238                if (xLen == 0.0) dims[0] = 1;
239                if (yLen == 0.0) dims[1] = 1;
240                if (zLen == 0.0) dims[2] = 1;
241                if (vtkStructuredGrid::SafeDownCast(ds) != NULL) {
242                    vtkStructuredGrid::SafeDownCast(ds)->GetDimensions(dims);
243                } else if (vtkRectilinearGrid::SafeDownCast(ds) != NULL) {
244                    vtkRectilinearGrid::SafeDownCast(ds)->GetDimensions(dims);
245                }
246                TRACE("Generating volume with dims (%d,%d,%d) from %d points",
247                      dims[0], dims[1], dims[2], ds->GetNumberOfPoints());
248
249                double xSpacing = (dims[0] == 1 ? 0.0 : xLen/((double)(dims[0]-1)));
250                double ySpacing = (dims[1] == 1 ? 0.0 : yLen/((double)(dims[1]-1)));
251                double zSpacing = (dims[2] == 1 ? 0.0 : zLen/((double)(dims[2]-1)));
252
253                vtkSmartPointer<vtkImageData> resampleGrid = vtkSmartPointer<vtkImageData>::New();
254                resampleGrid->SetDimensions(dims[0], dims[1], dims[2]);
255                resampleGrid->SetOrigin(bounds[0], bounds[2], bounds[4]);
256                resampleGrid->SetSpacing(xSpacing, ySpacing, zSpacing);
257
258                vtkSmartPointer<vtkProbeFilter> probe = vtkSmartPointer<vtkProbeFilter>::New();
259#ifdef USE_VTK6
260                probe->SetInputData(resampleGrid);
261                probe->SetSourceData(ds);
262#else
263                probe->SetInput(resampleGrid);
264                probe->SetSource(ds);
265#endif
266                probe->Update();
267
268                TRACE("Done generating volume");
269
270                for (int i = 0; i < 3; i++) {
271                    _mapper[i]->SetInputConnection(probe->GetOutputPort());
272                }
273            }
274        } else {
275            // Have ImageData
276            for (int i = 0; i < 3; i++) {
277#ifdef USE_VTK6
278                _mapper[i]->SetInputData(imgData);
279#else
280                _mapper[i]->SetInput(imgData);
281#endif
282            }
283        }
284    }
285
286    initProp();
287
288    for (int i = 0; i < 3; i++) {
289        if (_mapper[i] != NULL && _borderMapper[i] == NULL) {
290            _borderMapper[i] = vtkSmartPointer<vtkPolyDataMapper>::New();
291            _outlineSource[i] = vtkSmartPointer<vtkOutlineSource>::New();
292            switch (i) {
293            case 0:
294                _outlineSource[i]->SetBounds(bounds[0] + (bounds[1]-bounds[0])/2.,
295                                             bounds[0] + (bounds[1]-bounds[0])/2.,
296                                             bounds[2], bounds[3],
297                                             bounds[4], bounds[5]);
298                break;
299            case 1:
300                _outlineSource[i]->SetBounds(bounds[0], bounds[1],
301                                             bounds[2] + (bounds[3]-bounds[2])/2.,
302                                             bounds[2] + (bounds[3]-bounds[2])/2.,
303                                             bounds[4], bounds[5]);
304                break;
305            case 2:
306                _outlineSource[i]->SetBounds(bounds[0], bounds[1],
307                                             bounds[2], bounds[3],
308                                             bounds[4] + (bounds[5]-bounds[4])/2.,
309                                             bounds[4] + (bounds[5]-bounds[4])/2.);
310                break;
311            default:
312                ;
313            }
314            _borderMapper[i]->SetInputConnection(_outlineSource[i]->GetOutputPort());
315            _borderMapper[i]->SetResolveCoincidentTopologyToPolygonOffset();
316        }
317    }
318
319    if (_lut == NULL) {
320        setColorMap(ColorMap::getDefault());
321    }
322
323    _pipelineInitialized = true;
324
325    for (int i = 0; i < 3; i++) {
326        if (_mapper[i] != NULL) {
327            _actor[i]->SetMapper(_mapper[i]);
328            vtkImageMapper3D *im3d = _mapper[i];
329            im3d->Update();
330        }
331        if (_borderMapper[i] != NULL) {
332            _borderActor[i]->SetMapper(_borderMapper[i]);
333            _borderMapper[i]->Update();
334        }
335        // Only add cutter actor to assembly if geometry was
336        // produced, in order to prevent messing up assembly bounds
337        double bounds[6];
338        _actor[i]->GetBounds(bounds);
339        if (bounds[0] <= bounds[1]) {
340            getAssembly()->AddPart(_actor[i]);
341        }
342    }
343}
344
345/**
346 * \brief Select a 2D slice plane from a 3D DataSet
347 *
348 * \param[in] axis Axis of slice plane
349 * \param[in] ratio Position [0,1] of slice plane along axis
350 */
351void ImageCutplane::selectVolumeSlice(Axis axis, double ratio)
352{
353    if (_dataSet->is2D()) {
354        WARN("DataSet not 3D, returning");
355        return;
356    }
357
358    if ((axis == X_AXIS &&_cutPlane[0] == NULL) ||
359        (axis == Y_AXIS &&_cutPlane[1] == NULL) ||
360        (axis == Z_AXIS &&_cutPlane[2] == NULL)) {
361        WARN("Called before update() or DataSet is not a volume");
362        return;
363    }
364
365    double bounds[6];
366    _dataSet->getBounds(bounds);
367#if 0
368    if (ratio == 0.0)
369        ratio = 0.001;
370    if (ratio == 1.0)
371        ratio = 0.999;
372#endif
373    switch (axis) {
374    case X_AXIS:
375        _cutPlane[0]->SetOrigin(bounds[0] + (bounds[1]-bounds[0]) * ratio,
376                                0,
377                                0);
378        _outlineSource[0]->SetBounds(bounds[0] + (bounds[1]-bounds[0]) * ratio,
379                                     bounds[0] + (bounds[1]-bounds[0]) * ratio,
380                                     bounds[2], bounds[3],
381                                     bounds[4], bounds[5]);
382        break;
383    case Y_AXIS:
384        _cutPlane[1]->SetOrigin(0,
385                                bounds[2] + (bounds[3]-bounds[2]) * ratio,
386                                0);
387        _outlineSource[1]->SetBounds(bounds[0], bounds[1],
388                                     bounds[2] + (bounds[3]-bounds[2]) * ratio,
389                                     bounds[2] + (bounds[3]-bounds[2]) * ratio,
390                                     bounds[4], bounds[5]);
391        break;
392    case Z_AXIS:
393        _cutPlane[2]->SetOrigin(0,
394                                0,
395                                bounds[4] + (bounds[5]-bounds[4]) * ratio);
396        _outlineSource[2]->SetBounds(bounds[0], bounds[1],
397                                     bounds[2], bounds[3],
398                                     bounds[4] + (bounds[5]-bounds[4]) * ratio,
399                                     bounds[4] + (bounds[5]-bounds[4]) * ratio);
400        break;
401    default:
402        ERROR("Invalid Axis");
403        return;
404    }
405    update();
406}
407
408void ImageCutplane::updateRanges(Renderer *renderer)
409{
410    GraphicsObject::updateRanges(renderer);
411
412    updateColorMap();
413}
414
415/**
416 * \brief Called when the color map has been edited
417 */
418void ImageCutplane::updateColorMap()
419{
420    setColorMap(_colorMap);
421}
422
423/**
424 * \brief Associate a colormap lookup table with the DataSet
425 */
426void ImageCutplane::setColorMap(ColorMap *cmap)
427{
428    if (cmap == NULL) {
429        _colorMap = NULL;
430        _lut = NULL;
431        for (int i = 0; i < 3; i++) {
432            if (_actor[i] != NULL) {
433                _actor[i]->GetProperty()->SetLookupTable(NULL);
434            }
435        }
436        return;
437    }
438
439    _colorMap = cmap;
440
441    if (_lut == NULL) {
442        _lut = vtkSmartPointer<vtkLookupTable>::New();
443        for (int i = 0; i < 3; i++) {
444            if (_actor[i] != NULL) {
445                _actor[i]->GetProperty()->UseLookupTableScalarRangeOn();
446                _actor[i]->GetProperty()->SetLookupTable(_lut);
447            }
448        }
449        _lut->DeepCopy(cmap->getLookupTable());
450        _lut->SetRange(_dataRange);
451    } else {
452        double range[2];
453        _lut->GetTableRange(range);
454        _lut->DeepCopy(cmap->getLookupTable());
455        _lut->SetRange(range);
456        _lut->Modified();
457    }
458}
459
460void ImageCutplane::setUseWindowLevel(bool state)
461{
462    for (int i = 0; i < 3; i++) {
463        if (_actor[i] != NULL) {
464            _actor[i]->GetProperty()->SetUseLookupTableScalarRange((state ? 0 : 1));
465        }
466    }
467}
468
469void ImageCutplane::setWindow(double window)
470{
471    for (int i = 0; i < 3; i++) {
472        if (_actor[i] != NULL) {
473            _actor[i]->GetProperty()->UseLookupTableScalarRangeOff();
474            _actor[i]->GetProperty()->SetColorWindow(window);
475        }
476    }
477}
478
479void ImageCutplane::setLevel(double level)
480{
481    for (int i = 0; i < 3; i++) {
482        if (_actor[i] != NULL) {
483            _actor[i]->GetProperty()->UseLookupTableScalarRangeOff();
484            _actor[i]->GetProperty()->SetColorLevel(level);
485        }
486    }
487}
488
489/**
490 * \brief Turn on/off rendering of outlines
491 */
492void ImageCutplane::setOutlineVisibility(bool state)
493{
494    for (int i = 0; i < 3; i++) {
495        if (_borderActor[i] != NULL) {
496            _borderActor[i]->SetVisibility((state ? 1 : 0));
497        }
498    }
499}
500
501/**
502 * \brief Set visibility of cutplane on specified axis
503 */
504void ImageCutplane::setSliceVisibility(Axis axis, bool state)
505{
506    switch (axis) {
507    case X_AXIS:
508        if (_actor[0] != NULL)
509            _actor[0]->SetVisibility((state ? 1 : 0));
510        if (_borderActor[0] != NULL)
511            _borderActor[0]->SetVisibility((state ? 1 : 0));
512        break;
513    case Y_AXIS:
514        if (_actor[1] != NULL)
515            _actor[1]->SetVisibility((state ? 1 : 0));
516        if (_borderActor[1] != NULL)
517            _borderActor[1]->SetVisibility((state ? 1 : 0));
518        break;
519    case Z_AXIS:
520    default:
521        if (_actor[2] != NULL)
522            _actor[2]->SetVisibility((state ? 1 : 0));
523        if (_borderActor[2] != NULL)
524            _borderActor[2]->SetVisibility((state ? 1 : 0));
525        break;
526    }
527}
528
529/**
530 * \brief Set a group of world coordinate planes to clip rendering
531 *
532 * Passing NULL for planes will remove all cliping planes
533 */
534void ImageCutplane::setClippingPlanes(vtkPlaneCollection *planes)
535{
536    for (int i = 0; i < 3; i++) {
537        if (_mapper[i] != NULL) {
538            _mapper[i]->SetClippingPlanes(planes);
539        }
540        if (_borderMapper[i] != NULL) {
541            _borderMapper[i]->SetClippingPlanes(planes);
542        }
543    }
544}
Note: See TracBrowser for help on using the repository browser.