source: trunk/packages/vizservers/vtkvis/RpStreamlines.cpp @ 2507

Last change on this file since 2507 was 2507, checked in by ldelgass, 13 years ago

Add random point in cell specializations for most common VTK cell types. Most
types should now have points within the cell, but not all are uniform
distributions within a cell. Triangles are tetrahedra have uniform point
distribution inside; however, cells are still chosen without regard to
area/volume.

  • Property svn:eol-style set to native
File size: 43.0 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (C) 2011, Purdue Research Foundation
4 *
5 * Author: Leif Delgass <ldelgass@purdue.edu>
6 */
7
8#include <cstdlib>
9#include <ctime>
10#include <cfloat>
11#include <cmath>
12
13#include <vtkMath.h>
14#include <vtkActor.h>
15#include <vtkProperty.h>
16#include <vtkPoints.h>
17#include <vtkCellArray.h>
18#include <vtkPolyLine.h>
19#include <vtkRegularPolygonSource.h>
20#include <vtkPointData.h>
21#include <vtkCellData.h>
22#include <vtkCellDataToPointData.h>
23#include <vtkPolygon.h>
24#include <vtkPolyData.h>
25#include <vtkTubeFilter.h>
26#include <vtkRibbonFilter.h>
27#include <vtkTransform.h>
28#include <vtkTransformPolyDataFilter.h>
29#include <vtkVertexGlyphFilter.h>
30
31#include "RpStreamlines.h"
32#include "Trace.h"
33
34using namespace Rappture::VtkVis;
35
36Streamlines::Streamlines() :
37    VtkGraphicsObject(),
38    _lineType(LINES),
39    _colorMode(COLOR_BY_VECTOR_MAGNITUDE),
40    _colorMap(NULL),
41    _seedVisible(true),
42    _dataScale(1)
43{
44    _faceCulling = true;
45    _color[0] = 1.0f;
46    _color[1] = 1.0f;
47    _color[2] = 1.0f;
48    _seedColor[0] = 1.0f;
49    _seedColor[1] = 1.0f;
50    _seedColor[2] = 1.0f;
51    vtkMath::RandomSeed((int)time(NULL));
52    srand((unsigned int)time(NULL));
53}
54
55Streamlines::~Streamlines()
56{
57}
58
59void Streamlines::setDataSet(DataSet *dataSet,
60                             bool useCumulative,
61                             double scalarRange[2],
62                             double vectorMagnitudeRange[2],
63                             double vectorComponentRange[3][2])
64{
65    if (_dataSet != dataSet) {
66        _dataSet = dataSet;
67
68        if (useCumulative) {
69            _dataRange[0] = scalarRange[0];
70            _dataRange[1] = scalarRange[1];
71            _vectorMagnitudeRange[0] = vectorMagnitudeRange[0];
72            _vectorMagnitudeRange[1] = vectorMagnitudeRange[1];
73            for (int i = 0; i < 3; i++) {
74                _vectorComponentRange[i][0] = vectorComponentRange[i][0];
75                _vectorComponentRange[i][1] = vectorComponentRange[i][1];
76            }
77        } else {
78            _dataSet->getScalarRange(_dataRange);
79            _dataSet->getVectorRange(_vectorMagnitudeRange);
80            for (int i = 0; i < 3; i++) {
81                _dataSet->getVectorRange(_vectorComponentRange[i], i);
82            }
83        }
84
85        update();
86    }
87}
88
89/**
90 * \brief Create and initialize a VTK Prop to render Streamlines
91 */
92void Streamlines::initProp()
93{
94    if (_linesActor == NULL) {
95        _linesActor = vtkSmartPointer<vtkActor>::New();
96        _linesActor->GetProperty()->SetColor(_color[0], _color[1], _color[2]);
97        _linesActor->GetProperty()->SetEdgeColor(_edgeColor[0], _edgeColor[1], _edgeColor[2]);
98        _linesActor->GetProperty()->SetLineWidth(_edgeWidth);
99        _linesActor->GetProperty()->SetOpacity(_opacity);
100        _linesActor->GetProperty()->SetAmbient(.2);
101        if (!_lighting)
102            _linesActor->GetProperty()->LightingOff();
103        switch (_lineType) {
104        case LINES:
105            setCulling(_linesActor->GetProperty(), false);
106            _linesActor->GetProperty()->SetRepresentationToWireframe();
107            _linesActor->GetProperty()->EdgeVisibilityOff();
108            break;
109        case TUBES:
110            if (_faceCulling && _opacity == 1.0)
111                setCulling(_linesActor->GetProperty(), true);
112            _linesActor->GetProperty()->SetRepresentationToSurface();
113            _linesActor->GetProperty()->EdgeVisibilityOff();
114            break;
115        case RIBBONS:
116            setCulling(_linesActor->GetProperty(), false);
117            _linesActor->GetProperty()->SetRepresentationToSurface();
118            _linesActor->GetProperty()->EdgeVisibilityOff();
119            break;
120        default:
121            ;
122        }
123    }
124    if (_seedActor == NULL) {
125        _seedActor = vtkSmartPointer<vtkActor>::New();
126        _seedActor->GetProperty()->SetColor(_seedColor[0], _seedColor[1], _seedColor[2]);
127        _seedActor->GetProperty()->SetEdgeColor(_seedColor[0], _seedColor[1], _seedColor[2]);
128        _seedActor->GetProperty()->SetLineWidth(1);
129        _seedActor->GetProperty()->SetPointSize(2);
130        _seedActor->GetProperty()->SetOpacity(_opacity);
131        _seedActor->GetProperty()->SetRepresentationToWireframe();
132        _seedActor->GetProperty()->LightingOff();
133        setSeedVisibility(_seedVisible);
134    }
135    if (_prop == NULL) {
136        _prop = vtkSmartPointer<vtkAssembly>::New();
137        getAssembly()->AddPart(_linesActor);
138        getAssembly()->AddPart(_seedActor);
139    }
140}
141
142/**
143 * \brief Get a pseudo-random number in range [min,max]
144 */
145double Streamlines::getRandomNum(double min, double max)
146{
147#if 1
148    return vtkMath::Random(min, max);
149#else
150    int r = rand();
151    return (min + ((double)r / RAND_MAX) * (max - min));
152#endif
153}
154
155/**
156 * \brief Get a random 3D point within an AABB
157 *
158 * \param[out] pt The random point
159 * \param[in] bounds The bounds of the AABB
160 */
161void Streamlines::getRandomPoint(double pt[3], const double bounds[6])
162{
163    pt[0] = getRandomNum(bounds[0], bounds[1]);
164    pt[1] = getRandomNum(bounds[2], bounds[3]);
165    pt[2] = getRandomNum(bounds[4], bounds[5]);
166}
167
168/**
169 * \brief Get a random point within a triangle (including edges)
170 *
171 * \param[out] pt The random point
172 * \param[in] v1 Triangle vertex 1
173 * \param[in] v2 Triangle vertex 2
174 * \param[in] v3 Triangle vertex 3
175 */
176void Streamlines::getRandomPointInTriangle(double pt[3],
177                                           const double v0[3],
178                                           const double v1[3],
179                                           const double v2[3])
180{
181    // Choose random barycentric coordinates
182    double bary[3];
183    bary[0] = getRandomNum(0, 1);
184    bary[1] = getRandomNum(0, 1);
185    if (bary[0] + bary[1] > 1.0) {
186        bary[0] = 1.0 - bary[0];
187        bary[1] = 1.0 - bary[1];
188    }
189    bary[2] = 1.0 - bary[0] - bary[1];
190
191    TRACE("bary %g %g %g", bary[0], bary[1], bary[2]);
192    // Convert to cartesian coords
193    for (int i = 0; i < 3; i++) {
194        pt[i] = v0[i] * bary[0] + v1[i] * bary[1] + v2[i] * bary[2];
195    }
196}
197
198void Streamlines::getRandomPointInTetrahedron(double pt[3],
199                                              const double v0[3],
200                                              const double v1[3],
201                                              const double v2[3],
202                                              const double v3[3])
203{
204    // Choose random barycentric coordinates
205    double bary[4];
206    bary[0] = getRandomNum(0, 1);
207    bary[1] = getRandomNum(0, 1);
208    bary[2] = getRandomNum(0, 1);
209    if (bary[0] + bary[1] > 1.0) {
210        bary[0] = 1.0 - bary[0];
211        bary[1] = 1.0 - bary[1];
212    }
213    if (bary[1] + bary[2] > 1.0) {
214        double tmp = bary[2];
215        bary[2] = 1.0 - bary[0] - bary[1];
216        bary[1] = 1.0 - tmp;
217    } else if (bary[0] + bary[1] + bary[2] > 1.0) {
218        double tmp = bary[2];
219        bary[2] = bary[0] + bary[1] + bary[2] - 1.0;
220        bary[0] = 1.0 - bary[1] - tmp;
221    }
222    bary[3] = 1.0 - bary[0] - bary[1] - bary[2];
223    TRACE("bary %g %g %g %g", bary[0], bary[1], bary[2], bary[3]);
224    // Convert to cartesian coords
225    for (int i = 0; i < 3; i++) {
226#if 0
227        pt[i] = (v0[i] - v3[i]) * bary[0] +
228                (v1[i] - v3[i]) * bary[1] +
229                (v2[i] - v3[i]) * bary[2] + v3[i];
230#else
231        pt[i] = v0[i] * bary[0] + v1[i] * bary[1] +
232                v2[i] * bary[2] + v3[i] * bary[3];
233#endif
234    }
235}
236
237/**
238 * \brief Get a random point on a line segment (including endpoints)
239 */
240void Streamlines::getRandomPointOnLineSegment(double pt[3],
241                                              const double endpt[3],
242                                              const double endpt2[3])
243{
244    double ratio = getRandomNum(0, 1);
245    pt[0] = endpt[0] + ratio * (endpt2[0] - endpt[0]);
246    pt[1] = endpt[1] + ratio * (endpt2[1] - endpt[1]);
247    pt[2] = endpt[2] + ratio * (endpt2[2] - endpt[2]);
248}
249
250/**
251 * \brief Get a random point within a vtkDataSet's mesh
252 *
253 * Note: This currently doesn't always give a uniform distribution
254 * of points in space and can generate points outside the mesh for
255 * unusual cell types
256 */
257void Streamlines::getRandomCellPt(double pt[3], vtkDataSet *ds)
258{
259    int numCells = (int)ds->GetNumberOfCells();
260    // XXX: Not uniform distribution (shouldn't use mod, and assumes
261    // all cells are equal area/volume)
262    int cell = rand() % numCells;
263    int type = ds->GetCellType(cell);
264    if (type == VTK_VERTEX) {
265        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
266        ds->GetCellPoints(cell, ptIds);
267        assert(ptIds->GetNumberOfIds() == 1);
268        ds->GetPoint(ptIds->GetId(0), pt);
269    } else if (type == VTK_POLY_VERTEX) {
270        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
271        ds->GetCellPoints(cell, ptIds);
272        assert(ptIds->GetNumberOfIds() >= 1);
273        int id = rand() % ptIds->GetNumberOfIds();
274        ds->GetPoint(ptIds->GetId(id), pt);
275    } else if (type == VTK_LINE) {
276        double v[2][3];
277        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
278        ds->GetCellPoints(cell, ptIds);
279        assert(ptIds->GetNumberOfIds() == 2);
280        for (int i = 0; i < 2; i++) {
281            ds->GetPoint(ptIds->GetId(i), v[i]);
282        }
283        getRandomPointOnLineSegment(pt, v[0], v[1]);
284    } else if (type == VTK_POLY_LINE) {
285        double v[2][3];
286        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
287        ds->GetCellPoints(cell, ptIds);
288        assert(ptIds->GetNumberOfIds() >= 2);
289        int id = rand() % (ptIds->GetNumberOfIds()-1);
290        for (int i = 0; i < 2; i++) {
291            ds->GetPoint(ptIds->GetId(id+i), v[i]);
292        }
293        getRandomPointOnLineSegment(pt, v[0], v[1]);
294    } else if (type == VTK_TRIANGLE) {
295        double v[3][3];
296        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
297        ds->GetCellPoints(cell, ptIds);
298        assert(ptIds->GetNumberOfIds() == 3);
299        for (int i = 0; i < 3; i++) {
300            ds->GetPoint(ptIds->GetId(i), v[i]);
301        }
302        getRandomPointInTriangle(pt, v[0], v[1], v[2]);
303    } else if (type == VTK_TRIANGLE_STRIP) {
304        double v[3][3];
305        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
306        ds->GetCellPoints(cell, ptIds);
307        assert(ptIds->GetNumberOfIds() >= 3);
308        int id = rand() % (ptIds->GetNumberOfIds()-2);
309        for (int i = 0; i < 3; i++) {
310            ds->GetPoint(ptIds->GetId(id+i), v[i]);
311        }
312        getRandomPointInTriangle(pt, v[0], v[1], v[2]);
313    } else if (type == VTK_POLYGON) {
314        vtkPolygon *poly = vtkPolygon::SafeDownCast(ds->GetCell(cell));
315        assert (poly != NULL);
316        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
317        poly->Triangulate(ptIds);
318        assert(ptIds->GetNumberOfIds() >= 3 && ptIds->GetNumberOfIds() % 3 == 0);
319        int tri = rand() % (ptIds->GetNumberOfIds()/3);
320        double v[3][3];
321        for (int i = 0; i < 3; i++) {
322            ds->GetPoint(ptIds->GetId(i + tri * 3), v[i]);
323        }
324        getRandomPointInTriangle(pt, v[0], v[1], v[2]);
325    } else if (type == VTK_QUAD) {
326        double v[4][3];
327        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
328        ds->GetCellPoints(cell, ptIds);
329        assert(ptIds->GetNumberOfIds() == 4);
330        for (int i = 0; i < 4; i++) {
331            ds->GetPoint(ptIds->GetId(i), v[i]);
332        }
333        int tri = rand() & 0x1;
334        if (tri) {
335            getRandomPointInTriangle(pt, v[0], v[1], v[2]);
336        } else {
337            getRandomPointInTriangle(pt, v[0], v[2], v[3]);
338        }
339    } else if (type == VTK_TETRA) {
340        double v[4][3];
341        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
342        ds->GetCellPoints(cell, ptIds);
343        assert(ptIds->GetNumberOfIds() == 4);
344        for (int i = 0; i < 4; i++) {
345            ds->GetPoint(ptIds->GetId(i), v[i]);
346        }
347        getRandomPointInTetrahedron(pt, v[0], v[1], v[2], v[3]);
348    } else if (type == VTK_HEXAHEDRON) {
349        double v[8][3];
350        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
351        ds->GetCellPoints(cell, ptIds);
352        assert(ptIds->GetNumberOfIds() == 8);
353        for (int i = 0; i < 8; i++) {
354            ds->GetPoint(ptIds->GetId(i), v[i]);
355        }
356        int tetra = rand() % 5;
357        switch (tetra) {
358        case 0:
359            getRandomPointInTetrahedron(pt, v[0], v[1], v[2], v[5]);
360            break;
361        case 1:
362            getRandomPointInTetrahedron(pt, v[0], v[2], v[7], v[3]);
363            break;
364        case 2:
365            getRandomPointInTetrahedron(pt, v[0], v[5], v[7], v[4]);
366            break;
367        case 3:
368            getRandomPointInTetrahedron(pt, v[5], v[2], v[7], v[6]);
369            break;
370        case 4:
371        default:
372            getRandomPointInTetrahedron(pt, v[0], v[2], v[7], v[5]);
373            break;
374        }
375    } else if (type == VTK_WEDGE) {
376        double v[6][3];
377        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
378        ds->GetCellPoints(cell, ptIds);
379        assert(ptIds->GetNumberOfIds() == 6);
380        for (int i = 0; i < 6; i++) {
381            ds->GetPoint(ptIds->GetId(i), v[i]);
382        }
383        double vv[3][3];
384        getRandomPointOnLineSegment(vv[0], v[0], v[3]);
385        getRandomPointOnLineSegment(vv[1], v[1], v[4]);
386        getRandomPointOnLineSegment(vv[2], v[2], v[5]);
387        getRandomPointInTriangle(pt, vv[0], vv[1], vv[2]);
388    } else if (type == VTK_PYRAMID) {
389        double v[5][3];
390        vtkSmartPointer<vtkIdList> ptIds = vtkSmartPointer<vtkIdList>::New();
391        ds->GetCellPoints(cell, ptIds);
392        assert(ptIds->GetNumberOfIds() == 5);
393        for (int i = 0; i < 5; i++) {
394            ds->GetPoint(ptIds->GetId(i), v[i]);
395        }
396        int tetra = rand() & 0x1;
397        if (tetra) {
398            getRandomPointInTetrahedron(pt, v[0], v[1], v[2], v[4]);
399        } else {
400            getRandomPointInTetrahedron(pt, v[0], v[2], v[3], v[4]);
401        }
402    } else {
403        double bounds[6];
404        ds->GetCellBounds(cell, bounds);
405        // Note: For pixel/voxel cells, this is exact.  However, if the cell is
406        // not an axis aligned box, the point may be outside the cell
407        getRandomPoint(pt, bounds);
408    }
409}
410
411/**
412 * \brief Internal method to set up pipeline after a state change
413 */
414void Streamlines::update()
415{
416    if (_dataSet == NULL) {
417        return;
418    }
419
420    vtkDataSet *ds = _dataSet->getVtkDataSet();
421
422    double bounds[6];
423    _dataSet->getBounds(bounds);
424    double maxBound = 0.0;
425    if (bounds[1] - bounds[0] > maxBound) {
426        maxBound = bounds[1] - bounds[0];
427    }
428    if (bounds[3] - bounds[2] > maxBound) {
429        maxBound = bounds[3] - bounds[2];
430    }
431    if (bounds[5] - bounds[4] > maxBound) {
432        maxBound = bounds[5] - bounds[4];
433    }
434
435    double cellSizeRange[2];
436    double avgSize;
437    _dataSet->getCellSizeRange(cellSizeRange, &avgSize);
438    _dataScale = avgSize / 8.;
439
440    vtkSmartPointer<vtkCellDataToPointData> cellToPtData;
441
442    if (ds->GetPointData() == NULL ||
443        ds->GetPointData()->GetVectors() == NULL) {
444        TRACE("No vector point data found in DataSet %s", _dataSet->getName().c_str());
445        if (ds->GetCellData() == NULL ||
446            ds->GetCellData()->GetVectors() == NULL) {
447            ERROR("No vectors found in DataSet %s", _dataSet->getName().c_str());
448        } else {
449            cellToPtData =
450                vtkSmartPointer<vtkCellDataToPointData>::New();
451            cellToPtData->SetInput(ds);
452            //cellToPtData->PassCellDataOn();
453            cellToPtData->Update();
454            ds = cellToPtData->GetOutput();
455        }
456    }
457
458    if (_streamTracer == NULL) {
459        _streamTracer = vtkSmartPointer<vtkStreamTracer>::New();
460    }
461
462    _streamTracer->SetInput(ds);
463    _streamTracer->SetMaximumPropagation(maxBound);
464
465    if (_pdMapper == NULL) {
466        _pdMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
467        _pdMapper->SetResolveCoincidentTopologyToPolygonOffset();
468        _pdMapper->ScalarVisibilityOn();
469    }
470    if (_seedMapper == NULL) {
471        _seedMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
472        _seedMapper->SetResolveCoincidentTopologyToPolygonOffset();
473        _seedMapper->ScalarVisibilityOff();
474    }
475
476    // Set up seed source object
477    setSeedToFilledMesh(200);
478
479    switch (_lineType) {
480    case LINES: {
481        _streamTracer->SetComputeVorticity(false);
482        _pdMapper->SetInputConnection(_streamTracer->GetOutputPort());
483    }
484        break;
485    case TUBES: {
486        _streamTracer->SetComputeVorticity(true);
487        _lineFilter = vtkSmartPointer<vtkTubeFilter>::New();
488        vtkTubeFilter *tubeFilter = vtkTubeFilter::SafeDownCast(_lineFilter);
489        tubeFilter->SetNumberOfSides(5);
490        _lineFilter->SetInputConnection(_streamTracer->GetOutputPort());
491        _pdMapper->SetInputConnection(_lineFilter->GetOutputPort());
492    }
493        break;
494    case RIBBONS: {
495        _streamTracer->SetComputeVorticity(true);
496        _lineFilter = vtkSmartPointer<vtkRibbonFilter>::New();
497        _lineFilter->SetInputConnection(_streamTracer->GetOutputPort());
498        _pdMapper->SetInputConnection(_lineFilter->GetOutputPort());
499    }
500        break;
501    default:
502        ERROR("Unknown LineType: %d", _lineType);
503    }
504
505#if defined(DEBUG) && defined(WANT_TRACE)
506    _streamTracer->Update();
507    vtkPolyData *pd = _streamTracer->GetOutput();
508    TRACE("Verts: %d Lines: %d Polys: %d Strips: %d",
509                  pd->GetNumberOfVerts(),
510                  pd->GetNumberOfLines(),
511                  pd->GetNumberOfPolys(),
512                  pd->GetNumberOfStrips());
513#endif
514
515    initProp();
516
517    _seedActor->SetMapper(_seedMapper);
518
519    if (_lut == NULL) {
520        setColorMap(ColorMap::getDefault());
521    }
522
523    setColorMode(_colorMode);
524
525    _linesActor->SetMapper(_pdMapper);
526    _pdMapper->Update();
527    _seedMapper->Update();
528}
529
530/**
531 * \brief Use points of the DataSet associated with this
532 * Streamlines as seeds
533 */
534void Streamlines::setSeedToMeshPoints()
535{
536    setSeedToMeshPoints(_dataSet->getVtkDataSet());
537}
538
539/**
540 * \brief Use seed points randomly distributed within the cells
541 * of the DataSet associated with this Streamlines
542 *
543 * Note: The current implementation doesn't give a uniform
544 * distribution of points, and points outside the mesh bounds
545 * may be generated
546 *
547 * \param[in] numPoints Number of random seed points to generate
548 */
549void Streamlines::setSeedToFilledMesh(int numPoints)
550{
551    setSeedToFilledMesh(_dataSet->getVtkDataSet(), numPoints);
552}
553
554/**
555 * \brief Use points of a supplied vtkDataSet as seeds
556 *
557 * \param[in] seed vtkDataSet with points to use as seeds
558 */
559void Streamlines::setSeedToMeshPoints(vtkDataSet *seed)
560{
561    if (_streamTracer != NULL) {
562        TRACE("Seed points: %d", seed->GetNumberOfPoints());
563        vtkSmartPointer<vtkDataSet> oldSeed;
564        if (_streamTracer->GetSource() != NULL) {
565            oldSeed = _streamTracer->GetSource();
566        }
567
568        _streamTracer->SetSource(seed);
569        if (oldSeed != NULL) {
570            oldSeed->SetPipelineInformation(NULL);
571        }
572
573        if (vtkPolyData::SafeDownCast(seed) != NULL) {
574            _seedMapper->SetInput(vtkPolyData::SafeDownCast(seed));
575        } else {
576            vtkSmartPointer<vtkVertexGlyphFilter> vertFilter = vtkSmartPointer<vtkVertexGlyphFilter>::New();
577            vertFilter->SetInput(seed);
578            _seedMapper->SetInputConnection(vertFilter->GetOutputPort());
579        }
580    }
581}
582
583/**
584 * \brief Use seed points randomly distributed within the cells
585 * of a supplied vtkDataSet
586 *
587 * Note: The current implementation doesn't give a uniform
588 * distribution of points, and points outside the mesh bounds
589 * may be generated
590 *
591 * \param[in] ds vtkDataSet containing cells
592 * \param[in] numPoints Number of random seed points to generate
593 */
594void Streamlines::setSeedToFilledMesh(vtkDataSet *ds, int numPoints)
595{
596    if (_streamTracer != NULL) {
597        // Set up seed source object
598        vtkSmartPointer<vtkPolyData> seed = vtkSmartPointer<vtkPolyData>::New();
599        vtkSmartPointer<vtkPoints> pts = vtkSmartPointer<vtkPoints>::New();
600        vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
601
602        if (ds->GetNumberOfCells() < 1) {
603            ERROR("No cells in mesh");
604        }
605
606        for (int i = 0; i < numPoints; i++) {
607            double pt[3];
608            getRandomCellPt(pt, ds);
609            //TRACE("Seed pt: %g %g %g", pt[0], pt[1], pt[2]);
610            pts->InsertNextPoint(pt);
611            cells->InsertNextCell(1);
612            cells->InsertCellPoint(i);
613        }
614
615        seed->SetPoints(pts);
616        seed->SetVerts(cells);
617
618        TRACE("Seed points: %d", seed->GetNumberOfPoints());
619        vtkSmartPointer<vtkDataSet> oldSeed;
620        if (_streamTracer->GetSource() != NULL) {
621            oldSeed = _streamTracer->GetSource();
622        }
623
624        _streamTracer->SetSource(seed);
625        if (oldSeed != NULL) {
626            oldSeed->SetPipelineInformation(NULL);
627        }
628
629        _seedMapper->SetInput(seed);
630    }
631}
632
633/**
634 * \brief Use seed points along a line
635 *
636 * \param[in] start Starting point of rake line
637 * \param[in] end End point of rake line
638 * \param[in] numPoints Number of points along line to generate
639 */
640void Streamlines::setSeedToRake(double start[3], double end[3], int numPoints)
641{
642    if (numPoints < 2)
643        return;
644    if (_streamTracer != NULL) {
645        // Set up seed source object
646        vtkSmartPointer<vtkPolyData> seed = vtkSmartPointer<vtkPolyData>::New();
647        vtkSmartPointer<vtkPoints> pts = vtkSmartPointer<vtkPoints>::New();
648        vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
649        vtkSmartPointer<vtkPolyLine> polyline = vtkSmartPointer<vtkPolyLine>::New();
650
651        double dir[3];
652        for (int i = 0; i < 3; i++) {
653            dir[i] = end[i] - start[i];
654        }
655
656        polyline->GetPointIds()->SetNumberOfIds(numPoints);
657        for (int i = 0; i < numPoints; i++) {
658            double pt[3];
659            for (int ii = 0; ii < 3; ii++) {
660                pt[ii] = start[ii] + dir[ii] * ((double)i / (numPoints-1));
661            }
662            //TRACE("Seed pt: %g %g %g", pt[0], pt[1], pt[2]);
663            pts->InsertNextPoint(pt);
664            polyline->GetPointIds()->SetId(i, i);
665        }
666
667        cells->InsertNextCell(polyline);
668        seed->SetPoints(pts);
669        seed->SetLines(cells);
670
671        TRACE("Seed points: %d", seed->GetNumberOfPoints());
672        vtkSmartPointer<vtkDataSet> oldSeed;
673        if (_streamTracer->GetSource() != NULL) {
674            oldSeed = _streamTracer->GetSource();
675        }
676
677        _streamTracer->SetSource(seed);
678        if (oldSeed != NULL) {
679            oldSeed->SetPipelineInformation(NULL);
680        }
681
682        _seedMapper->SetInput(seed);
683    }
684}
685
686/**
687 * \brief Create seed points inside a disk with an optional hole
688 *
689 * \param[in] center Center point of disk
690 * \param[in] normal Normal vector to orient disk
691 * \param[in] radius Radius of disk
692 * \param[in] innerRadius Radius of hole at center of disk
693 * \param[in] numPoints Number of random points to generate
694 */
695void Streamlines::setSeedToDisk(double center[3],
696                                double normal[3],
697                                double radius,
698                                double innerRadius,
699                                int numPoints)
700{
701    if (_streamTracer != NULL) {
702        // Set up seed source object
703        vtkSmartPointer<vtkPolyData> seed = vtkSmartPointer<vtkPolyData>::New();
704        vtkSmartPointer<vtkPoints> pts = vtkSmartPointer<vtkPoints>::New();
705        vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
706
707        // The following code is based on vtkRegularPolygonSource::RequestData
708
709        double px[3];
710        double py[3];
711        double axis[3] = {1., 0., 0.};
712
713        if (vtkMath::Normalize(normal) == 0.0) {
714            normal[0] = 0.0;
715            normal[1] = 0.0;
716            normal[2] = 1.0;
717        }
718
719        // Find axis in plane (orthogonal to normal)
720        bool done = false;
721        vtkMath::Cross(normal, axis, px);
722        if (vtkMath::Normalize(px) > 1.0e-3) {
723            done = true;
724        }
725        if (!done) {
726            axis[0] = 0.0;
727            axis[1] = 1.0;
728            axis[2] = 0.0;
729            vtkMath::Cross(normal, axis, px);
730            if (vtkMath::Normalize(px) > 1.0e-3) {
731                done = true;
732            }
733        }
734        if (!done) {
735            axis[0] = 0.0;
736            axis[1] = 0.0;
737            axis[2] = 1.0;
738            vtkMath::Cross(normal, axis, px);
739            vtkMath::Normalize(px);
740        }
741        // Create third orthogonal basis vector
742        vtkMath::Cross(px, normal, py);
743
744        double minSquared = (innerRadius*innerRadius)/(radius*radius);
745        for (int j = 0; j < numPoints; j++) {
746            // Get random sweep angle and radius
747            double angle = getRandomNum(0, 2.0 * vtkMath::DoublePi());
748            // Need sqrt to get uniform distribution
749            double r = sqrt(getRandomNum(minSquared, 1)) * radius;
750            double pt[3];
751            for (int i = 0; i < 3; i++) {
752                pt[i] = center[i] + r * (px[i] * cos(angle) + py[i] * sin(angle));
753            }
754            //TRACE("Seed pt: %g %g %g", pt[0], pt[1], pt[2]);
755            pts->InsertNextPoint(pt);
756            cells->InsertNextCell(1);
757            cells->InsertCellPoint(j);
758        }
759
760        seed->SetPoints(pts);
761        seed->SetVerts(cells);
762
763        TRACE("Seed points: %d", seed->GetNumberOfPoints());
764        vtkSmartPointer<vtkDataSet> oldSeed;
765        if (_streamTracer->GetSource() != NULL) {
766            oldSeed = _streamTracer->GetSource();
767        }
768
769        _streamTracer->SetSource(seed);
770        if (oldSeed != NULL) {
771            oldSeed->SetPipelineInformation(NULL);
772        }
773
774        _seedMapper->SetInput(seed);
775    }
776}
777
778/**
779 * \brief Use seed points from an n-sided polygon
780 *
781 * \param[in] center Center point of polygon
782 * \param[in] normal Normal vector to orient polygon
783 * \param[in] angle Angle in degrees to rotate about normal
784 * \param[in] radius Radius of circumscribing circle
785 * \param[in] numSides Number of polygon sides (and points) to generate
786 */
787void Streamlines::setSeedToPolygon(double center[3],
788                                   double normal[3],
789                                   double angle,
790                                   double radius,
791                                   int numSides)
792{
793    if (_streamTracer != NULL) {
794        // Set up seed source object
795        vtkSmartPointer<vtkRegularPolygonSource> seed = vtkSmartPointer<vtkRegularPolygonSource>::New();
796
797        seed->SetCenter(center);
798        seed->SetNormal(normal);
799        seed->SetRadius(radius);
800        seed->SetNumberOfSides(numSides);
801        seed->GeneratePolygonOn();
802
803        if (angle != 0.0) {
804            vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
805            trans->RotateWXYZ(angle, normal);
806            vtkSmartPointer<vtkTransformPolyDataFilter> transFilt =
807                vtkSmartPointer<vtkTransformPolyDataFilter>::New();
808            transFilt->SetInputConnection(seed->GetOutputPort());
809            transFilt->SetTransform(trans);
810        }
811
812        TRACE("Seed points: %d", numSides);
813        vtkSmartPointer<vtkDataSet> oldSeed;
814        if (_streamTracer->GetSource() != NULL) {
815            oldSeed = _streamTracer->GetSource();
816        }
817
818        if (angle != 0.0) {
819            vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
820            trans->Translate(+center[0], +center[1], +center[2]);
821            trans->RotateWXYZ(angle, normal);
822            trans->Translate(-center[0], -center[1], -center[2]);
823            vtkSmartPointer<vtkTransformPolyDataFilter> transFilt =
824                vtkSmartPointer<vtkTransformPolyDataFilter>::New();
825            transFilt->SetInputConnection(seed->GetOutputPort());
826            transFilt->SetTransform(trans);
827            _streamTracer->SetSourceConnection(transFilt->GetOutputPort());
828            _seedMapper->SetInputConnection(transFilt->GetOutputPort());
829        } else {
830            _streamTracer->SetSourceConnection(seed->GetOutputPort());
831            _seedMapper->SetInputConnection(seed->GetOutputPort());
832        }
833
834        if (oldSeed != NULL) {
835            oldSeed->SetPipelineInformation(NULL);
836        }
837    }
838}
839
840/**
841 * \brief Use seed points from an n-sided polygon
842 *
843 * \param[in] center Center point of polygon
844 * \param[in] normal Normal vector to orient polygon
845 * \param[in] angle Angle in degrees to rotate about normal
846 * \param[in] radius Radius of circumscribing circle
847 * \param[in] numSides Number of polygon sides (and points) to generate
848 * \param[in] numPoints Number of random points to generate
849 */
850void Streamlines::setSeedToFilledPolygon(double center[3],
851                                         double normal[3],
852                                         double angle,
853                                         double radius,
854                                         int numSides,
855                                         int numPoints)
856{
857    if (_streamTracer != NULL) {
858         // Set up seed source object
859        vtkSmartPointer<vtkPolyData> seed = vtkSmartPointer<vtkPolyData>::New();
860        vtkSmartPointer<vtkPoints> pts = vtkSmartPointer<vtkPoints>::New();
861        vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
862
863        // The following code is based on vtkRegularPolygonSource::RequestData
864
865        double px[3];
866        double py[3];
867        double axis[3] = {1., 0., 0.};
868
869        if (vtkMath::Normalize(normal) == 0.0) {
870            normal[0] = 0.0;
871            normal[1] = 0.0;
872            normal[2] = 1.0;
873        }
874
875        // Find axis in plane (orthogonal to normal)
876        bool done = false;
877        vtkMath::Cross(normal, axis, px);
878        if (vtkMath::Normalize(px) > 1.0e-3) {
879            done = true;
880        }
881        if (!done) {
882            axis[0] = 0.0;
883            axis[1] = 1.0;
884            axis[2] = 0.0;
885            vtkMath::Cross(normal, axis, px);
886            if (vtkMath::Normalize(px) > 1.0e-3) {
887                done = true;
888            }
889        }
890        if (!done) {
891            axis[0] = 0.0;
892            axis[1] = 0.0;
893            axis[2] = 1.0;
894            vtkMath::Cross(normal, axis, px);
895            vtkMath::Normalize(px);
896        }
897        // Create third orthogonal basis vector
898        vtkMath::Cross(px, normal, py);
899
900        double verts[numSides][3];
901        double sliceTheta = 2.0 * vtkMath::DoublePi() / (double)numSides;
902        angle = vtkMath::RadiansFromDegrees(angle);
903        for (int j = 0; j < numSides; j++) {
904            for (int i = 0; i < 3; i++) {
905                double theta = sliceTheta * (double)j - angle;
906                verts[j][i] = center[i] + radius * (px[i] * cos(theta) +
907                                                    py[i] * sin(theta));
908            }
909            //TRACE("Vert %d: %g %g %g", j, verts[j][0], verts[j][1], verts[j][2]);
910        }
911
912        // Note: this gives a uniform distribution because the polygon is regular and
913        // the triangular sections have equal area
914        if (numSides == 3) {
915            for (int j = 0; j < numPoints; j++) {
916                double pt[3];
917                getRandomPointInTriangle(pt, verts[0], verts[1], verts[2]);
918                //TRACE("Seed pt: %g %g %g", pt[0], pt[1], pt[2]);
919                pts->InsertNextPoint(pt);
920                cells->InsertNextCell(1);
921                cells->InsertCellPoint(j);
922            }
923        } else {
924            for (int j = 0; j < numPoints; j++) {
925                // Get random triangle section
926                int tri = rand() % numSides;
927                double pt[3];
928                getRandomPointInTriangle(pt, center, verts[tri], verts[(tri+1) % numSides]);
929                //TRACE("Seed pt: %g %g %g", pt[0], pt[1], pt[2]);
930                pts->InsertNextPoint(pt);
931                cells->InsertNextCell(1);
932                cells->InsertCellPoint(j);
933            }
934        }
935
936        seed->SetPoints(pts);
937        seed->SetVerts(cells);
938
939        TRACE("Seed points: %d", seed->GetNumberOfPoints());
940        vtkSmartPointer<vtkDataSet> oldSeed;
941        if (_streamTracer->GetSource() != NULL) {
942            oldSeed = _streamTracer->GetSource();
943        }
944
945        _streamTracer->SetSource(seed);
946        if (oldSeed != NULL) {
947            oldSeed->SetPipelineInformation(NULL);
948        }
949
950        _seedMapper->SetInput(seed);
951    }
952}
953
954/**
955 * \brief Set maximum length of stream lines in world coordinates
956 */
957void Streamlines::setMaxPropagation(double length)
958{
959    if (_streamTracer != NULL) {
960        _streamTracer->SetMaximumPropagation(length);
961    }
962}
963
964/**
965 * \brief Set streamline type to polylines
966 */
967void Streamlines::setLineTypeToLines()
968{
969    _lineType = LINES;
970    if (_streamTracer != NULL &&
971        _pdMapper != NULL) {
972        _streamTracer->SetComputeVorticity(false);
973        _pdMapper->SetInputConnection(_streamTracer->GetOutputPort());
974        _lineFilter = NULL;
975        setCulling(_linesActor->GetProperty(), false);
976        _linesActor->GetProperty()->SetRepresentationToWireframe();
977        _linesActor->GetProperty()->LightingOff();
978    }
979}
980
981/**
982 * \brief Set streamline type to 3D tubes
983 *
984 * \param[in] numSides Number of sides (>=3) for tubes
985 * \param[in] radius World coordinate minimum tube radius
986 */
987void Streamlines::setLineTypeToTubes(int numSides, double radius)
988{
989    _lineType = TUBES;
990    if (_streamTracer != NULL) {
991        _streamTracer->SetComputeVorticity(true);
992        if (vtkTubeFilter::SafeDownCast(_lineFilter) == NULL) {
993            _lineFilter = vtkSmartPointer<vtkTubeFilter>::New();
994            _lineFilter->SetInputConnection(_streamTracer->GetOutputPort());
995        }
996        vtkTubeFilter *tubeFilter = vtkTubeFilter::SafeDownCast(_lineFilter);
997        if (numSides < 3)
998            numSides = 3;
999        tubeFilter->SetNumberOfSides(numSides);
1000        tubeFilter->SetRadius(_dataScale * radius);
1001        _pdMapper->SetInputConnection(_lineFilter->GetOutputPort());
1002        if (_faceCulling && _opacity == 1.0)
1003            setCulling(_linesActor->GetProperty(), true);
1004        _linesActor->GetProperty()->SetRepresentationToSurface();
1005        _linesActor->GetProperty()->LightingOn();
1006     }
1007}
1008
1009/**
1010 * \brief Set streamline type to 3D ribbons
1011 *
1012 * \param[in] width Minimum half-width of ribbons
1013 * \param[in] angle Default ribbon angle in degrees from normal
1014 */
1015void Streamlines::setLineTypeToRibbons(double width, double angle)
1016{
1017    _lineType = RIBBONS;
1018    if (_streamTracer != NULL) {
1019        _streamTracer->SetComputeVorticity(true);
1020        if (vtkRibbonFilter::SafeDownCast(_lineFilter) == NULL) {
1021            _lineFilter = vtkSmartPointer<vtkRibbonFilter>::New();
1022            _lineFilter->SetInputConnection(_streamTracer->GetOutputPort());
1023        }
1024        vtkRibbonFilter *ribbonFilter = vtkRibbonFilter::SafeDownCast(_lineFilter);
1025        ribbonFilter->SetWidth(_dataScale * width);
1026        ribbonFilter->SetAngle(angle);
1027        ribbonFilter->UseDefaultNormalOn();
1028        _pdMapper->SetInputConnection(_lineFilter->GetOutputPort());
1029        setCulling(_linesActor->GetProperty(), false);
1030        _linesActor->GetProperty()->SetRepresentationToSurface();
1031        _linesActor->GetProperty()->LightingOn();
1032    }
1033}
1034
1035void Streamlines::updateRanges(bool useCumulative,
1036                               double scalarRange[2],
1037                               double vectorMagnitudeRange[2],
1038                               double vectorComponentRange[3][2])
1039{
1040    if (useCumulative) {
1041        _dataRange[0] = scalarRange[0];
1042        _dataRange[1] = scalarRange[1];
1043        _vectorMagnitudeRange[0] = vectorMagnitudeRange[0];
1044        _vectorMagnitudeRange[1] = vectorMagnitudeRange[1];
1045        for (int i = 0; i < 3; i++) {
1046            _vectorComponentRange[i][0] = vectorComponentRange[i][0];
1047            _vectorComponentRange[i][1] = vectorComponentRange[i][1];
1048        }
1049    } else {
1050        _dataSet->getScalarRange(_dataRange);
1051        _dataSet->getVectorRange(_vectorMagnitudeRange);
1052        for (int i = 0; i < 3; i++) {
1053            _dataSet->getVectorRange(_vectorComponentRange[i], i);
1054        }
1055    }
1056
1057    // Need to update color map ranges and/or active vector field
1058    setColorMode(_colorMode);
1059}
1060
1061void Streamlines::setColorMode(ColorMode mode)
1062{
1063    _colorMode = mode;
1064    if (_dataSet == NULL || _pdMapper == NULL)
1065        return;
1066
1067    vtkDataSet *ds = _dataSet->getVtkDataSet();
1068
1069    switch (mode) {
1070    case COLOR_BY_SCALAR: {
1071        _pdMapper->ScalarVisibilityOn();
1072        _pdMapper->SetScalarModeToDefault();
1073        if (_lut != NULL) {
1074            _lut->SetRange(_dataRange);
1075        }
1076    }
1077        break;
1078    case COLOR_BY_VECTOR_MAGNITUDE: {
1079        _pdMapper->ScalarVisibilityOn();
1080        _pdMapper->SetScalarModeToUsePointFieldData();
1081        if (ds->GetPointData() != NULL &&
1082            ds->GetPointData()->GetVectors() != NULL) {
1083            _pdMapper->SelectColorArray(ds->GetPointData()->GetVectors()->GetName());
1084        }
1085        if (_lut != NULL) {
1086            _lut->SetRange(_vectorMagnitudeRange);
1087            _lut->SetVectorModeToMagnitude();
1088        }
1089    }
1090        break;
1091    case COLOR_BY_VECTOR_X:
1092        _pdMapper->ScalarVisibilityOn();
1093        _pdMapper->SetScalarModeToUsePointFieldData();
1094        if (ds->GetPointData() != NULL &&
1095            ds->GetPointData()->GetVectors() != NULL) {
1096            _pdMapper->SelectColorArray(ds->GetPointData()->GetVectors()->GetName());
1097        }
1098        if (_lut != NULL) {
1099            _lut->SetRange(_vectorComponentRange[0]);
1100            _lut->SetVectorModeToComponent();
1101            _lut->SetVectorComponent(0);
1102        }
1103        break;
1104    case COLOR_BY_VECTOR_Y:
1105        _pdMapper->ScalarVisibilityOn();
1106        _pdMapper->SetScalarModeToUsePointFieldData();
1107        if (ds->GetPointData() != NULL &&
1108            ds->GetPointData()->GetVectors() != NULL) {
1109            _pdMapper->SelectColorArray(ds->GetPointData()->GetVectors()->GetName());
1110        }
1111        if (_lut != NULL) {
1112            _lut->SetRange(_vectorComponentRange[1]);
1113            _lut->SetVectorModeToComponent();
1114            _lut->SetVectorComponent(1);
1115        }
1116        break;
1117    case COLOR_BY_VECTOR_Z:
1118        _pdMapper->ScalarVisibilityOn();
1119        _pdMapper->SetScalarModeToUsePointFieldData();
1120        if (ds->GetPointData() != NULL &&
1121            ds->GetPointData()->GetVectors() != NULL) {
1122            _pdMapper->SelectColorArray(ds->GetPointData()->GetVectors()->GetName());
1123        }
1124        if (_lut != NULL) {
1125            _lut->SetRange(_vectorComponentRange[2]);
1126            _lut->SetVectorModeToComponent();
1127            _lut->SetVectorComponent(2);
1128        }
1129        break;
1130    case COLOR_CONSTANT:
1131    default:
1132        _pdMapper->ScalarVisibilityOff();
1133        break;
1134    }
1135}
1136
1137/**
1138 * \brief Called when the color map has been edited
1139 */
1140void Streamlines::updateColorMap()
1141{
1142    setColorMap(_colorMap);
1143}
1144
1145/**
1146 * \brief Associate a colormap lookup table with the DataSet
1147 */
1148void Streamlines::setColorMap(ColorMap *cmap)
1149{
1150    if (cmap == NULL)
1151        return;
1152
1153    _colorMap = cmap;
1154 
1155    if (_lut == NULL) {
1156        _lut = vtkSmartPointer<vtkLookupTable>::New();
1157        if (_pdMapper != NULL) {
1158            _pdMapper->UseLookupTableScalarRangeOn();
1159            _pdMapper->SetLookupTable(_lut);
1160        }
1161    }
1162
1163    _lut->DeepCopy(cmap->getLookupTable());
1164
1165    switch (_colorMode) {
1166    case COLOR_CONSTANT:
1167    case COLOR_BY_SCALAR:
1168        _lut->SetRange(_dataRange);
1169        break;
1170    case COLOR_BY_VECTOR_MAGNITUDE:
1171        _lut->SetVectorModeToMagnitude();
1172        _lut->SetRange(_vectorMagnitudeRange);
1173        break;
1174    case COLOR_BY_VECTOR_X:
1175        _lut->SetVectorModeToComponent();
1176        _lut->SetVectorComponent(0);
1177        _lut->SetRange(_vectorComponentRange[0]);
1178        break;
1179    case COLOR_BY_VECTOR_Y:
1180        _lut->SetVectorModeToComponent();
1181        _lut->SetVectorComponent(1);
1182        _lut->SetRange(_vectorComponentRange[1]);
1183        break;
1184    case COLOR_BY_VECTOR_Z:
1185        _lut->SetVectorModeToComponent();
1186        _lut->SetVectorComponent(2);
1187        _lut->SetRange(_vectorComponentRange[2]);
1188        break;
1189    default:
1190         break;
1191    }
1192}
1193
1194/**
1195 * \brief Turn on/off lighting of this object
1196 */
1197void Streamlines::setLighting(bool state)
1198{
1199    _lighting = state;
1200    if (_linesActor != NULL)
1201        _linesActor->GetProperty()->SetLighting((state ? 1 : 0));
1202}
1203
1204/**
1205 * \brief Set opacity of this object
1206 */
1207void Streamlines::setOpacity(double opacity)
1208{
1209    _opacity = opacity;
1210    if (_linesActor != NULL) {
1211        _linesActor->GetProperty()->SetOpacity(_opacity);
1212        if (_opacity < 1.0)
1213            setCulling(_linesActor->GetProperty(), false);
1214        else if (_faceCulling && _lineType == TUBES)
1215            setCulling(_linesActor->GetProperty(), true);
1216    }
1217    if (_seedActor != NULL) {
1218        _seedActor->GetProperty()->SetOpacity(_opacity);
1219    }
1220}
1221
1222/**
1223 * \brief Turn on/off rendering of this Streamlines
1224 */
1225void Streamlines::setVisibility(bool state)
1226{
1227    if (_linesActor != NULL) {
1228        _linesActor->SetVisibility((state ? 1 : 0));
1229    }
1230    if (_seedActor != NULL) {
1231        if (!state ||
1232            (state && _seedVisible)) {
1233            _seedActor->SetVisibility((state ? 1 : 0));
1234        }
1235    }
1236}
1237
1238/**
1239 * \brief Turn on/off rendering of the seed geometry
1240 */
1241void Streamlines::setSeedVisibility(bool state)
1242{
1243    _seedVisible = state;
1244    if (_seedActor != NULL) {
1245        _seedActor->SetVisibility((state ? 1 : 0));
1246    }
1247}
1248
1249/**
1250 * \brief Get visibility state of the Streamlines
1251 *
1252 * \return Are the Streamlines visible?
1253 */
1254bool Streamlines::getVisibility()
1255{
1256    if (_linesActor == NULL) {
1257        return false;
1258    } else {
1259        return (_linesActor->GetVisibility() != 0);
1260    }
1261}
1262
1263/**
1264 * \brief Turn on/off rendering of edges
1265 */
1266void Streamlines::setEdgeVisibility(bool state)
1267{
1268    if (_linesActor != NULL) {
1269        _linesActor->GetProperty()->SetEdgeVisibility((state ? 1 : 0));
1270    }
1271}
1272
1273/**
1274 * \brief Set RGB color of stream lines
1275 */
1276void Streamlines::setColor(float color[3])
1277{
1278    _color[0] = color[0];
1279    _color[1] = color[1];
1280    _color[2] = color[2];
1281    if (_linesActor != NULL)
1282        _linesActor->GetProperty()->SetColor(_color[0], _color[1], _color[2]);
1283}
1284
1285/**
1286 * \brief Set RGB color of stream line edges
1287 */
1288void Streamlines::setEdgeColor(float color[3])
1289{
1290    _edgeColor[0] = color[0];
1291    _edgeColor[1] = color[1];
1292    _edgeColor[2] = color[2];
1293    if (_linesActor != NULL)
1294        _linesActor->GetProperty()->SetEdgeColor(_edgeColor[0], _edgeColor[1], _edgeColor[2]);
1295}
1296
1297/**
1298 * \brief Set RGB color of seed geometry
1299 */
1300void Streamlines::setSeedColor(float color[3])
1301{
1302    _seedColor[0] = color[0];
1303    _seedColor[1] = color[1];
1304    _seedColor[2] = color[2];
1305    if (_seedActor != NULL) {
1306        _seedActor->GetProperty()->SetColor(_seedColor[0], _seedColor[1], _seedColor[2]);
1307        _seedActor->GetProperty()->SetEdgeColor(_seedColor[0], _seedColor[1], _seedColor[2]);
1308    }
1309}
1310
1311/**
1312 * \brief Set pixel width of stream lines (may be a no-op)
1313 */
1314void Streamlines::setEdgeWidth(float edgeWidth)
1315{
1316    _edgeWidth = edgeWidth;
1317    if (_linesActor != NULL)
1318        _linesActor->GetProperty()->SetLineWidth(_edgeWidth);
1319}
1320
1321/**
1322 * \brief Set a group of world coordinate planes to clip rendering
1323 *
1324 * Passing NULL for planes will remove all cliping planes
1325 */
1326void Streamlines::setClippingPlanes(vtkPlaneCollection *planes)
1327{
1328    if (_pdMapper != NULL) {
1329        _pdMapper->SetClippingPlanes(planes);
1330    }
1331    if (_seedMapper != NULL) {
1332        _seedMapper->SetClippingPlanes(planes);
1333    }
1334}
Note: See TracBrowser for help on using the repository browser.