source: vtkvis/trunk/ImageCutplane.cpp @ 6313

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

Require VTK >= 6.0.0

File size: 16.6 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                splatter->SetInputData(ds);
209                int dims[3];
210                dims[0] = dims[1] = dims[2] = 64;
211                if (vtkStructuredGrid::SafeDownCast(ds) != NULL) {
212                    vtkStructuredGrid::SafeDownCast(ds)->GetDimensions(dims);
213                } else if (vtkRectilinearGrid::SafeDownCast(ds) != NULL) {
214                    vtkRectilinearGrid::SafeDownCast(ds)->GetDimensions(dims);
215                }
216                TRACE("Generating volume with dims (%d,%d,%d) from %d points",
217                      dims[0], dims[1], dims[2], ds->GetNumberOfPoints());
218                splatter->SetSampleDimensions(dims);
219                splatter->Update();
220
221                TRACE("Done generating volume");
222
223                for (int i = 0; i < 3; i++) {
224                    _mapper[i]->SetInputConnection(splatter->GetOutputPort());
225                }
226            } else {
227                // (Slow) Resample using ProbeFilter
228                double xLen = bounds[1] - bounds[0];
229                double yLen = bounds[3] - bounds[2];
230                double zLen = bounds[5] - bounds[4];
231
232                int dims[3];
233                dims[0] = dims[1] = dims[2] = 64;
234                if (xLen == 0.0) dims[0] = 1;
235                if (yLen == 0.0) dims[1] = 1;
236                if (zLen == 0.0) dims[2] = 1;
237                if (vtkStructuredGrid::SafeDownCast(ds) != NULL) {
238                    vtkStructuredGrid::SafeDownCast(ds)->GetDimensions(dims);
239                } else if (vtkRectilinearGrid::SafeDownCast(ds) != NULL) {
240                    vtkRectilinearGrid::SafeDownCast(ds)->GetDimensions(dims);
241                }
242                TRACE("Generating volume with dims (%d,%d,%d) from %d points",
243                      dims[0], dims[1], dims[2], ds->GetNumberOfPoints());
244
245                double xSpacing = (dims[0] == 1 ? 0.0 : xLen/((double)(dims[0]-1)));
246                double ySpacing = (dims[1] == 1 ? 0.0 : yLen/((double)(dims[1]-1)));
247                double zSpacing = (dims[2] == 1 ? 0.0 : zLen/((double)(dims[2]-1)));
248
249                vtkSmartPointer<vtkImageData> resampleGrid = vtkSmartPointer<vtkImageData>::New();
250                resampleGrid->SetDimensions(dims[0], dims[1], dims[2]);
251                resampleGrid->SetOrigin(bounds[0], bounds[2], bounds[4]);
252                resampleGrid->SetSpacing(xSpacing, ySpacing, zSpacing);
253
254                vtkSmartPointer<vtkProbeFilter> probe = vtkSmartPointer<vtkProbeFilter>::New();
255                probe->SetInputData(resampleGrid);
256                probe->SetSourceData(ds);
257                probe->Update();
258
259                TRACE("Done generating volume");
260
261                for (int i = 0; i < 3; i++) {
262                    _mapper[i]->SetInputConnection(probe->GetOutputPort());
263                }
264            }
265        } else {
266            // Have ImageData
267            for (int i = 0; i < 3; i++) {
268                _mapper[i]->SetInputData(imgData);
269            }
270        }
271    }
272
273    initProp();
274
275    for (int i = 0; i < 3; i++) {
276        if (_mapper[i] != NULL && _borderMapper[i] == NULL) {
277            _borderMapper[i] = vtkSmartPointer<vtkPolyDataMapper>::New();
278            _outlineSource[i] = vtkSmartPointer<vtkOutlineSource>::New();
279            switch (i) {
280            case 0:
281                _outlineSource[i]->SetBounds(bounds[0] + (bounds[1]-bounds[0])/2.,
282                                             bounds[0] + (bounds[1]-bounds[0])/2.,
283                                             bounds[2], bounds[3],
284                                             bounds[4], bounds[5]);
285                break;
286            case 1:
287                _outlineSource[i]->SetBounds(bounds[0], bounds[1],
288                                             bounds[2] + (bounds[3]-bounds[2])/2.,
289                                             bounds[2] + (bounds[3]-bounds[2])/2.,
290                                             bounds[4], bounds[5]);
291                break;
292            case 2:
293                _outlineSource[i]->SetBounds(bounds[0], bounds[1],
294                                             bounds[2], bounds[3],
295                                             bounds[4] + (bounds[5]-bounds[4])/2.,
296                                             bounds[4] + (bounds[5]-bounds[4])/2.);
297                break;
298            default:
299                ;
300            }
301            _borderMapper[i]->SetInputConnection(_outlineSource[i]->GetOutputPort());
302            _borderMapper[i]->SetResolveCoincidentTopologyToPolygonOffset();
303        }
304    }
305
306    if (_lut == NULL) {
307        setColorMap(ColorMap::getDefault());
308    }
309
310    _pipelineInitialized = true;
311
312    for (int i = 0; i < 3; i++) {
313        if (_mapper[i] != NULL) {
314            _actor[i]->SetMapper(_mapper[i]);
315            vtkImageMapper3D *im3d = _mapper[i];
316            im3d->Update();
317        }
318        if (_borderMapper[i] != NULL) {
319            _borderActor[i]->SetMapper(_borderMapper[i]);
320            _borderMapper[i]->Update();
321        }
322        // Only add cutter actor to assembly if geometry was
323        // produced, in order to prevent messing up assembly bounds
324        double bounds[6];
325        _actor[i]->GetBounds(bounds);
326        if (bounds[0] <= bounds[1]) {
327            getAssembly()->AddPart(_actor[i]);
328        }
329    }
330}
331
332/**
333 * \brief Select a 2D slice plane from a 3D DataSet
334 *
335 * \param[in] axis Axis of slice plane
336 * \param[in] ratio Position [0,1] of slice plane along axis
337 */
338void ImageCutplane::selectVolumeSlice(Axis axis, double ratio)
339{
340    if (_dataSet->is2D()) {
341        WARN("DataSet not 3D, returning");
342        return;
343    }
344
345    if ((axis == X_AXIS &&_cutPlane[0] == NULL) ||
346        (axis == Y_AXIS &&_cutPlane[1] == NULL) ||
347        (axis == Z_AXIS &&_cutPlane[2] == NULL)) {
348        WARN("Called before update() or DataSet is not a volume");
349        return;
350    }
351
352    double bounds[6];
353    _dataSet->getBounds(bounds);
354#if 0
355    if (ratio == 0.0)
356        ratio = 0.001;
357    if (ratio == 1.0)
358        ratio = 0.999;
359#endif
360    switch (axis) {
361    case X_AXIS:
362        _cutPlane[0]->SetOrigin(bounds[0] + (bounds[1]-bounds[0]) * ratio,
363                                0,
364                                0);
365        _outlineSource[0]->SetBounds(bounds[0] + (bounds[1]-bounds[0]) * ratio,
366                                     bounds[0] + (bounds[1]-bounds[0]) * ratio,
367                                     bounds[2], bounds[3],
368                                     bounds[4], bounds[5]);
369        break;
370    case Y_AXIS:
371        _cutPlane[1]->SetOrigin(0,
372                                bounds[2] + (bounds[3]-bounds[2]) * ratio,
373                                0);
374        _outlineSource[1]->SetBounds(bounds[0], bounds[1],
375                                     bounds[2] + (bounds[3]-bounds[2]) * ratio,
376                                     bounds[2] + (bounds[3]-bounds[2]) * ratio,
377                                     bounds[4], bounds[5]);
378        break;
379    case Z_AXIS:
380        _cutPlane[2]->SetOrigin(0,
381                                0,
382                                bounds[4] + (bounds[5]-bounds[4]) * ratio);
383        _outlineSource[2]->SetBounds(bounds[0], bounds[1],
384                                     bounds[2], bounds[3],
385                                     bounds[4] + (bounds[5]-bounds[4]) * ratio,
386                                     bounds[4] + (bounds[5]-bounds[4]) * ratio);
387        break;
388    default:
389        ERROR("Invalid Axis");
390        return;
391    }
392    update();
393}
394
395void ImageCutplane::updateRanges(Renderer *renderer)
396{
397    GraphicsObject::updateRanges(renderer);
398
399    updateColorMap();
400}
401
402/**
403 * \brief Called when the color map has been edited
404 */
405void ImageCutplane::updateColorMap()
406{
407    setColorMap(_colorMap);
408}
409
410/**
411 * \brief Associate a colormap lookup table with the DataSet
412 */
413void ImageCutplane::setColorMap(ColorMap *cmap)
414{
415    if (cmap == NULL) {
416        _colorMap = NULL;
417        _lut = NULL;
418        for (int i = 0; i < 3; i++) {
419            if (_actor[i] != NULL) {
420                _actor[i]->GetProperty()->SetLookupTable(NULL);
421            }
422        }
423        return;
424    }
425
426    _colorMap = cmap;
427
428    if (_lut == NULL) {
429        _lut = vtkSmartPointer<vtkLookupTable>::New();
430        for (int i = 0; i < 3; i++) {
431            if (_actor[i] != NULL) {
432                _actor[i]->GetProperty()->UseLookupTableScalarRangeOn();
433                _actor[i]->GetProperty()->SetLookupTable(_lut);
434            }
435        }
436        _lut->DeepCopy(cmap->getLookupTable());
437        _lut->SetRange(_dataRange);
438    } else {
439        double range[2];
440        _lut->GetTableRange(range);
441        _lut->DeepCopy(cmap->getLookupTable());
442        _lut->SetRange(range);
443        _lut->Modified();
444    }
445}
446
447void ImageCutplane::setUseWindowLevel(bool state)
448{
449    for (int i = 0; i < 3; i++) {
450        if (_actor[i] != NULL) {
451            _actor[i]->GetProperty()->SetUseLookupTableScalarRange((state ? 0 : 1));
452        }
453    }
454}
455
456void ImageCutplane::setWindow(double window)
457{
458    for (int i = 0; i < 3; i++) {
459        if (_actor[i] != NULL) {
460            _actor[i]->GetProperty()->UseLookupTableScalarRangeOff();
461            _actor[i]->GetProperty()->SetColorWindow(window);
462        }
463    }
464}
465
466void ImageCutplane::setLevel(double level)
467{
468    for (int i = 0; i < 3; i++) {
469        if (_actor[i] != NULL) {
470            _actor[i]->GetProperty()->UseLookupTableScalarRangeOff();
471            _actor[i]->GetProperty()->SetColorLevel(level);
472        }
473    }
474}
475
476/**
477 * \brief Turn on/off rendering of outlines
478 */
479void ImageCutplane::setOutlineVisibility(bool state)
480{
481    for (int i = 0; i < 3; i++) {
482        if (_borderActor[i] != NULL) {
483            _borderActor[i]->SetVisibility((state ? 1 : 0));
484        }
485    }
486}
487
488/**
489 * \brief Set visibility of cutplane on specified axis
490 */
491void ImageCutplane::setSliceVisibility(Axis axis, bool state)
492{
493    switch (axis) {
494    case X_AXIS:
495        if (_actor[0] != NULL)
496            _actor[0]->SetVisibility((state ? 1 : 0));
497        if (_borderActor[0] != NULL)
498            _borderActor[0]->SetVisibility((state ? 1 : 0));
499        break;
500    case Y_AXIS:
501        if (_actor[1] != NULL)
502            _actor[1]->SetVisibility((state ? 1 : 0));
503        if (_borderActor[1] != NULL)
504            _borderActor[1]->SetVisibility((state ? 1 : 0));
505        break;
506    case Z_AXIS:
507    default:
508        if (_actor[2] != NULL)
509            _actor[2]->SetVisibility((state ? 1 : 0));
510        if (_borderActor[2] != NULL)
511            _borderActor[2]->SetVisibility((state ? 1 : 0));
512        break;
513    }
514}
515
516/**
517 * \brief Set a group of world coordinate planes to clip rendering
518 *
519 * Passing NULL for planes will remove all cliping planes
520 */
521void ImageCutplane::setClippingPlanes(vtkPlaneCollection *planes)
522{
523    for (int i = 0; i < 3; i++) {
524        if (_mapper[i] != NULL) {
525            _mapper[i]->SetClippingPlanes(planes);
526        }
527        if (_borderMapper[i] != NULL) {
528            _borderMapper[i]->SetClippingPlanes(planes);
529        }
530    }
531}
Note: See TracBrowser for help on using the repository browser.