source: vtkvis/trunk/LIC.cpp @ 5107

Last change on this file since 5107 was 4649, checked in by ldelgass, 10 years ago

Support building with experimental VTK OpenGL2 rendering backend

  • Property svn:eol-style set to native
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
10#include <vtkActor.h>
11#include <vtkProperty.h>
12#include <vtkPointData.h>
13#include <vtkCellData.h>
14#include <vtkCellDataToPointData.h>
15#include <vtkImageData.h>
16#include <vtkPolyData.h>
17#include <vtkDataSetMapper.h>
18#include <vtkPainterPolyDataMapper.h>
19#include <vtkDataSetSurfaceFilter.h>
20#include <vtkCutter.h>
21#include <vtkImageMask.h>
22#include <vtkImageCast.h>
23#include <vtkDelaunay2D.h>
24#include <vtkDelaunay3D.h>
25#include <vtkTransform.h>
26
27#include "LIC.h"
28#include "Trace.h"
29
30using namespace VtkVis;
31
32LIC::LIC() :
33    GraphicsObject(),
34    _sliceAxis(Z_AXIS),
35    _colorMap(NULL),
36    _resolution(128)
37{
38}
39
40LIC::~LIC()
41{
42#ifdef WANT_TRACE
43    if (_dataSet != NULL)
44        TRACE("Deleting LIC for %s", _dataSet->getName().c_str());
45    else
46        TRACE("Deleting LIC with NULL DataSet");
47#endif
48}
49
50void LIC::initProp()
51{
52    if (_prop == NULL) {
53        _prop = vtkSmartPointer<vtkActor>::New();
54        vtkProperty *property = getActor()->GetProperty();
55        property->SetOpacity(_opacity);
56        property->SetEdgeColor(_edgeColor[0], _edgeColor[1], _edgeColor[2]);
57        property->SetLineWidth(_edgeWidth);
58        property->EdgeVisibilityOff();
59        property->SetAmbient(.2);
60        if (!_lighting)
61            property->LightingOff();
62    }
63}
64
65void LIC::update()
66{
67    if (_dataSet == NULL)
68        return;
69
70    vtkDataSet *ds = _dataSet->getVtkDataSet();
71
72    vtkSmartPointer<vtkCellDataToPointData> cellToPtData;
73    if (ds->GetPointData() == NULL ||
74        ds->GetPointData()->GetVectors() == NULL) {
75         TRACE("No vector point data in dataset %s", _dataSet->getName().c_str());
76         if (ds->GetCellData() != NULL &&
77             ds->GetCellData()->GetVectors() != NULL) {
78             cellToPtData =
79                 vtkSmartPointer<vtkCellDataToPointData>::New();
80#ifdef USE_VTK6
81             cellToPtData->SetInputData(ds);
82#else
83             cellToPtData->SetInput(ds);
84#endif
85             //cellToPtData->PassCellDataOn();
86             cellToPtData->Update();
87             ds = cellToPtData->GetOutput();
88         } else {
89             USER_ERROR("No vector field was found in the data set");
90             return;
91         }
92    }
93
94#ifdef HAVE_LIC
95    vtkImageData *imageData = vtkImageData::SafeDownCast(ds);
96    if (imageData != NULL) {
97        if (_lic == NULL) {
98            _lic = vtkSmartPointer<vtkImageDataLIC2D>::New();
99        }
100        if (!_dataSet->is2D()) {
101            // 3D image/volume/uniform grid
102            if (_volumeSlicer == NULL)
103                _volumeSlicer = vtkSmartPointer<vtkExtractVOI>::New();
104            int dims[3];
105            imageData->GetDimensions(dims);
106            TRACE("Image data dimensions: %d %d %d", dims[0], dims[1], dims[2]);
107#ifdef USE_VTK6
108            _volumeSlicer->SetInputData(ds);
109#else
110            _volumeSlicer->SetInput(ds);
111#endif
112            _volumeSlicer->SetVOI(0, dims[0]-1, 0, dims[1]-1, (dims[2]-1)/2, (dims[2]-1)/2);
113            _volumeSlicer->SetSampleRate(1, 1, 1);
114            _lic->SetInputConnection(_volumeSlicer->GetOutputPort());
115        } else {
116            // 2D image/uniform grid
117#ifdef USE_VTK6
118            _lic->SetInputData(ds);
119#else
120            _lic->SetInput(ds);
121#endif
122        }
123        if (_mapper == NULL) {
124            _mapper = vtkSmartPointer<vtkDataSetMapper>::New();
125        }
126        _mapper->SetInputConnection(_lic->GetOutputPort());
127    } else if (vtkPolyData::SafeDownCast(ds) == NULL ||
128               _dataSet->isCloud() ||
129               _dataSet->is2D()) {
130        // structured/rectilinear/unstructured grid, cloud or 2D polydata
131        if (_lic == NULL) {
132            _lic = vtkSmartPointer<vtkImageDataLIC2D>::New();
133        }
134
135        // Need to convert to vtkImageData
136        double bounds[6];
137        ds->GetBounds(bounds);
138        double xSize = bounds[1] - bounds[0];
139        double ySize = bounds[3] - bounds[2];
140        double zSize = bounds[5] - bounds[4];
141        int minDir = 2;
142        if (xSize < ySize && xSize < zSize)
143            minDir = 0;
144        if (ySize < xSize && ySize < zSize)
145            minDir = 1;
146        if (_probeFilter == NULL)
147            _probeFilter = vtkSmartPointer<vtkProbeFilter>::New();
148
149        PrincipalPlane plane;
150        double offset;
151        if (!_dataSet->is2D(&plane, &offset)) {
152            // 3D Data: Sample a plane within the bounding box
153            vtkSmartPointer<vtkCutter> cutter = vtkSmartPointer<vtkCutter>::New();
154            if (_cutPlane == NULL) {
155                _cutPlane = vtkSmartPointer<vtkPlane>::New();
156            }
157            if (minDir == 0) {
158                _cutPlane->SetNormal(1, 0, 0);
159                _cutPlane->SetOrigin(bounds[0] + (bounds[1]-bounds[0])/2.,
160                                     0,
161                                     0);
162            } else if (minDir == 1) {
163                _cutPlane->SetNormal(0, 1, 0);
164                _cutPlane->SetOrigin(0,
165                                     bounds[2] + (bounds[3]-bounds[2])/2.,
166                                     0);
167            } else {
168                _cutPlane->SetNormal(0, 0, 1);
169                _cutPlane->SetOrigin(0,
170                                     0,
171                                     bounds[4] + (bounds[5]-bounds[4])/2.);
172            }
173
174            if (_dataSet->isCloud()) {
175                // 3D cloud -- Need to mesh it before we can resample
176                vtkSmartPointer<vtkDelaunay3D> mesher = vtkSmartPointer<vtkDelaunay3D>::New();
177#ifdef USE_VTK6
178                mesher->SetInputData(ds);
179#else
180                mesher->SetInput(ds);
181#endif
182                cutter->SetInputConnection(mesher->GetOutputPort());
183            } else {
184#ifdef USE_VTK6
185                cutter->SetInputData(ds);
186#else
187                cutter->SetInput(ds);
188#endif
189            }
190            cutter->SetCutFunction(_cutPlane);
191            _probeFilter->SetSourceConnection(cutter->GetOutputPort());
192        } else {
193            if (_dataSet->isCloud()) {
194                // 2D cloud -- Need to mesh it before we can resample
195                vtkSmartPointer<vtkDelaunay2D> mesher = vtkSmartPointer<vtkDelaunay2D>::New();
196                if (plane == PLANE_ZY) {
197                    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
198                    trans->RotateWXYZ(90, 0, 1, 0);
199                    if (offset != 0.0) {
200                        trans->Translate(-offset, 0, 0);
201                    }
202                    mesher->SetTransform(trans);
203                } else if (plane == PLANE_XZ) {
204                    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
205                    trans->RotateWXYZ(-90, 1, 0, 0);
206                    if (offset != 0.0) {
207                        trans->Translate(0, -offset, 0);
208                    }
209                    mesher->SetTransform(trans);
210                } else if (offset != 0.0) {
211                    // XY with Z offset
212                    vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
213                    trans->Translate(0, 0, -offset);
214                    mesher->SetTransform(trans);
215                }
216#ifdef USE_VTK6
217                mesher->SetInputData(ds);
218#else
219                mesher->SetInput(ds);
220#endif
221                _probeFilter->SetSourceConnection(mesher->GetOutputPort());
222            } else {
223#ifdef USE_VTK6
224                _probeFilter->SetSourceData(ds);
225#else
226                _probeFilter->SetSource(ds);
227#endif
228            }
229        }
230
231        vtkSmartPointer<vtkImageData> imageData = vtkSmartPointer<vtkImageData>::New();
232        int xdim, ydim, zdim;
233        double origin[3], spacing[3];
234        int res = _resolution;
235        if (minDir == 0) {
236            xdim = 1;
237            ydim = res;
238            zdim = res;
239            origin[0] = bounds[0] + xSize/2.;
240            origin[1] = bounds[2];
241            origin[2] = bounds[4];
242            spacing[0] = 0;
243            spacing[1] = ySize/((double)(ydim-1));
244            spacing[2] = zSize/((double)(zdim-1));
245            _sliceAxis = X_AXIS;
246        } else if (minDir == 1) {
247            xdim = res;
248            ydim = 1;
249            zdim = res;
250            origin[0] = bounds[0];
251            origin[1] = bounds[2] + ySize/2.;
252            origin[2] = bounds[4];
253            spacing[0] = xSize/((double)(xdim-1));
254            spacing[1] = 0;
255            spacing[2] = zSize/((double)(zdim-1));
256            _sliceAxis = Y_AXIS;
257        } else {
258            xdim = res;
259            ydim = res;
260            zdim = 1;
261            origin[0] = bounds[0];
262            origin[1] = bounds[2];
263            origin[2] = bounds[4] + zSize/2.;
264            spacing[0] = xSize/((double)(xdim-1));
265            spacing[1] = ySize/((double)(ydim-1));
266            spacing[2] = 0;
267            _sliceAxis = Z_AXIS;
268        }
269        imageData->SetDimensions(xdim, ydim, zdim);
270        imageData->SetOrigin(origin);
271        imageData->SetSpacing(spacing);
272#ifdef USE_VTK6
273        _probeFilter->SetInputData(imageData);
274#else
275        _probeFilter->SetInput(imageData);
276#endif
277        _lic->SetInputConnection(_probeFilter->GetOutputPort());
278
279        if (_mapper == NULL) {
280            _mapper = vtkSmartPointer<vtkDataSetMapper>::New();
281            _mapper->SetColorModeToMapScalars();
282        }
283        _lic->Update();
284        if (1) {
285            vtkSmartPointer<vtkImageData> imgData = _probeFilter->GetImageDataOutput();
286            imgData->GetPointData()->SetActiveScalars(_probeFilter->GetValidPointMaskArrayName());
287            vtkSmartPointer<vtkImageCast> maskCast = vtkSmartPointer<vtkImageCast>::New();
288            maskCast->SetInputData(imgData);
289            //maskCast->SetInputConnection(_probeFilter->GetOutputPort());
290            //maskCast->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS,
291            //                                 _probeFilter->GetValidPointMaskArrayName());
292            maskCast->SetOutputScalarTypeToUnsignedChar();
293            vtkSmartPointer<vtkImageCast> licCast = vtkSmartPointer<vtkImageCast>::New();
294            licCast->SetInputConnection(_lic->GetOutputPort());
295            licCast->SetOutputScalarTypeToDouble();
296            vtkSmartPointer<vtkImageMask> mask = vtkSmartPointer<vtkImageMask>::New();
297            mask->SetInputConnection(0, licCast->GetOutputPort());
298            mask->SetInputConnection(1, maskCast->GetOutputPort());
299            _mapper->SetInputConnection(mask->GetOutputPort());
300        } else {
301            // Mask not working in VTK 6.1?
302            _mapper->SetInputConnection(_lic->GetOutputPort());
303        }
304    } else {
305        // DataSet is a 3D PolyData with cells
306        vtkPolyData *pd = vtkPolyData::SafeDownCast(ds);
307        assert(pd != NULL);
308
309        // XXX: This mapper seems to conflict with offscreen rendering, so the main
310        // renderer needs to be set not to use FBOs for this to work
311        _mapper = vtkSmartPointer<vtkPainterPolyDataMapper>::New();
312        vtkPainterPolyDataMapper *ppdmapper = vtkPainterPolyDataMapper::SafeDownCast(_mapper);
313        if (_painter == NULL) {
314            _painter = vtkSmartPointer<vtkSurfaceLICPainter>::New();
315            _painter->SetDelegatePainter(ppdmapper->GetPainter());
316         }
317        ppdmapper->SetPainter(_painter);
318#ifdef USE_VTK6
319        ppdmapper->SetInputData(pd);
320#else
321        ppdmapper->SetInput(pd);
322#endif
323    }
324
325    setInterpolateBeforeMapping(true);
326
327    if (_lut == NULL) {
328        setColorMap(ColorMap::getGrayDefault());
329    }
330
331    initProp();
332    getActor()->SetMapper(_mapper);
333
334    _mapper->Update();
335#endif
336}
337
338void LIC::setInterpolateBeforeMapping(bool state)
339{
340    if (_mapper != NULL) {
341        _mapper->SetInterpolateScalarsBeforeMapping((state ? 1 : 0));
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 LIC::selectVolumeSlice(Axis axis, double ratio)
352{
353    if (_dataSet->is2D()) {
354        WARN("DataSet not 3D, returning");
355        return;
356    }
357
358    if (_volumeSlicer == NULL &&
359        _probeFilter == NULL) {
360        WARN("Called before update() or DataSet is not a volume");
361        return;
362    }
363
364    int dims[3];
365
366    vtkImageData *imageData = vtkImageData::SafeDownCast(_dataSet->getVtkDataSet());
367    if (imageData == NULL) {
368        if (_probeFilter != NULL) {
369            imageData = vtkImageData::SafeDownCast(_probeFilter->GetInput());
370            if (imageData == NULL) {
371                ERROR("Couldn't get probe filter input image");
372                return;
373            }
374        } else {
375            ERROR("Not a volume data set");
376            return;
377        }
378    }
379    imageData->GetDimensions(dims);
380
381    _sliceAxis = axis;
382
383    if (_probeFilter != NULL) {
384        vtkImageData *imageData = vtkImageData::SafeDownCast(_probeFilter->GetInput());
385        double bounds[6];
386        assert(vtkDataSet::SafeDownCast(_probeFilter->GetSource()) != NULL);
387        _dataSet->getBounds(bounds);
388        int dim = _resolution;
389
390        switch (axis) {
391        case X_AXIS:
392            imageData->SetDimensions(1, dim, dim);
393            imageData->SetOrigin(bounds[0] + (bounds[1]-bounds[0])*ratio, bounds[2], bounds[4]);
394            imageData->SetSpacing(0,
395                                  (bounds[3]-bounds[2])/((double)(dim-1)),
396                                  (bounds[5]-bounds[4])/((double)(dim-1)));
397            if (_cutPlane != NULL) {
398                _cutPlane->SetNormal(1, 0, 0);
399                _cutPlane->SetOrigin(bounds[0] + (bounds[1]-bounds[0])*ratio, 0, 0);
400            }
401            break;
402        case Y_AXIS:
403            imageData->SetDimensions(dim, 1, dim);
404            imageData->SetOrigin(bounds[0], bounds[2] + (bounds[3]-bounds[2])*ratio, bounds[4]);
405            imageData->SetSpacing((bounds[1]-bounds[0])/((double)(dim-1)),
406                                  0,
407                                  (bounds[5]-bounds[4])/((double)(dim-1)));
408            if (_cutPlane != NULL) {
409                _cutPlane->SetNormal(0, 1, 0);
410                _cutPlane->SetOrigin(0, bounds[2] + (bounds[3]-bounds[2])*ratio, 0);
411            }
412            break;
413        case Z_AXIS:
414            imageData->SetDimensions(dim, dim, 1);
415            imageData->SetOrigin(bounds[0], bounds[2], bounds[4] + (bounds[5]-bounds[4])*ratio);
416            imageData->SetSpacing((bounds[1]-bounds[0])/((double)(dim-1)),
417                                  (bounds[3]-bounds[2])/((double)(dim-1)),
418                                  0);
419            if (_cutPlane != NULL) {
420                _cutPlane->SetNormal(0, 0, 1);
421                _cutPlane->SetOrigin(0, 0, bounds[4] + (bounds[5]-bounds[4])*ratio);
422            }
423            break;
424        default:
425            ERROR("Invalid Axis");
426            return;
427        }
428    } else {
429        int voi[6];
430
431        switch (axis) {
432        case X_AXIS:
433            voi[0] = voi[1] = (int)((dims[0]-1) * ratio);
434            voi[2] = 0;
435            voi[3] = dims[1]-1;
436            voi[4] = 0;
437            voi[5] = dims[2]-1;
438            break;
439        case Y_AXIS:
440            voi[2] = voi[3] = (int)((dims[1]-1) * ratio);
441            voi[0] = 0;
442            voi[1] = dims[0]-1;
443            voi[4] = 0;
444            voi[5] = dims[2]-1;
445            break;
446        case Z_AXIS:
447            voi[4] = voi[5] = (int)((dims[2]-1) * ratio);
448            voi[0] = 0;
449            voi[1] = dims[0]-1;
450            voi[2] = 0;
451            voi[3] = dims[1]-1;
452            break;
453        default:
454            ERROR("Invalid Axis");
455            return;
456        }
457
458        _volumeSlicer->SetVOI(voi);
459    }
460#ifdef HAVE_LIC
461    if (_lic != NULL)
462        _lic->Update();
463#endif
464    if (_mapper != NULL)
465        _mapper->Update();
466}
467
468/**
469 * \brief Called when the color map has been edited
470 */
471void LIC::updateColorMap()
472{
473    setColorMap(_colorMap);
474}
475
476/**
477 * \brief Associate a colormap lookup table with the DataSet
478 */
479void LIC::setColorMap(ColorMap *cmap)
480{
481    if (cmap == NULL)
482        return;
483
484    _colorMap = cmap;
485 
486    if (_lut == NULL) {
487        _lut = vtkSmartPointer<vtkLookupTable>::New();
488        if (_mapper != NULL) {
489            _mapper->UseLookupTableScalarRangeOff();
490            _mapper->SetLookupTable(_lut);
491        }
492    }
493
494    _lut->DeepCopy(cmap->getLookupTable());
495    _lut->SetRange(_dataRange);
496    _lut->Modified();
497}
498
499void LIC::updateRanges(Renderer *renderer)
500{
501    GraphicsObject::updateRanges(renderer);
502
503    if (_lut != NULL) {
504        _lut->SetRange(_dataRange);
505    }
506}
507
508/**
509 * \brief Set a group of world coordinate planes to clip rendering
510 *
511 * Passing NULL for planes will remove all cliping planes
512 */
513void LIC::setClippingPlanes(vtkPlaneCollection *planes)
514{
515    if (_mapper != NULL) {
516        _mapper->SetClippingPlanes(planes);
517    }
518}
Note: See TracBrowser for help on using the repository browser.