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

Last change on this file since 2402 was 2402, checked in by ldelgass, 13 years ago
  • Let graphics objects handle DataSet? cumulative range changes, track vectors as well as scalars, also supply cumulative ranges in setDataSet()
  • Be more consistent about naming enums and commands for vectors
  • Add constructor arguments to some graphics objects to speed initialization (eliminates some pipeline changes)
  • Apply a scale factor to glyphs based on cell sizes
  • Add line glyph shape
  • Don't delete ColorMaps? in use
  • Update graphics objects when a ColorMap? is edited
  • Property svn:eol-style set to native
File size: 33.7 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 <vtkPolyData.h>
24#include <vtkTubeFilter.h>
25#include <vtkRibbonFilter.h>
26#include <vtkTransform.h>
27#include <vtkTransformPolyDataFilter.h>
28
29#include "RpStreamlines.h"
30#include "Trace.h"
31
32using namespace Rappture::VtkVis;
33
34Streamlines::Streamlines() :
35    VtkGraphicsObject(),
36    _lineType(LINES),
37    _colorMode(COLOR_BY_VECTOR_MAGNITUDE),
38    _colorMap(NULL),
39    _seedVisible(true)
40{
41    _faceCulling = true;
42    _color[0] = 1.0f;
43    _color[1] = 1.0f;
44    _color[2] = 1.0f;
45    _seedColor[0] = 1.0f;
46    _seedColor[1] = 1.0f;
47    _seedColor[2] = 1.0f;
48    vtkMath::RandomSeed((int)time(NULL));
49    srand((unsigned int)time(NULL));
50}
51
52Streamlines::~Streamlines()
53{
54}
55
56void Streamlines::setDataSet(DataSet *dataSet,
57                             bool useCumulative,
58                             double scalarRange[2],
59                             double vectorMagnitudeRange[2],
60                             double vectorComponentRange[3][2])
61{
62    if (_dataSet != dataSet) {
63        _dataSet = dataSet;
64
65        if (useCumulative) {
66            _dataRange[0] = scalarRange[0];
67            _dataRange[1] = scalarRange[1];
68            _vectorMagnitudeRange[0] = vectorMagnitudeRange[0];
69            _vectorMagnitudeRange[1] = vectorMagnitudeRange[1];
70            for (int i = 0; i < 3; i++) {
71                _vectorComponentRange[i][0] = vectorComponentRange[i][0];
72                _vectorComponentRange[i][1] = vectorComponentRange[i][1];
73            }
74        } else {
75            _dataSet->getScalarRange(_dataRange);
76            _dataSet->getVectorRange(_vectorMagnitudeRange);
77            for (int i = 0; i < 3; i++) {
78                _dataSet->getVectorRange(_vectorComponentRange[i], i);
79            }
80        }
81
82        update();
83    }
84}
85
86/**
87 * \brief Create and initialize a VTK Prop to render Streamlines
88 */
89void Streamlines::initProp()
90{
91    if (_linesActor == NULL) {
92        _linesActor = vtkSmartPointer<vtkActor>::New();
93        _linesActor->GetProperty()->SetColor(_color[0], _color[1], _color[2]);
94        _linesActor->GetProperty()->SetEdgeColor(_edgeColor[0], _edgeColor[1], _edgeColor[2]);
95        _linesActor->GetProperty()->SetLineWidth(_edgeWidth);
96        _linesActor->GetProperty()->SetOpacity(_opacity);
97        _linesActor->GetProperty()->SetAmbient(.2);
98        if (!_lighting)
99            _linesActor->GetProperty()->LightingOff();
100        switch (_lineType) {
101        case LINES:
102            setCulling(_linesActor->GetProperty(), false);
103            _linesActor->GetProperty()->SetRepresentationToWireframe();
104            _linesActor->GetProperty()->EdgeVisibilityOff();
105            break;
106        case TUBES:
107            if (_faceCulling && _opacity == 1.0)
108                setCulling(_linesActor->GetProperty(), true);
109            _linesActor->GetProperty()->SetRepresentationToSurface();
110            _linesActor->GetProperty()->EdgeVisibilityOff();
111            break;
112        case RIBBONS:
113            setCulling(_linesActor->GetProperty(), false);
114            _linesActor->GetProperty()->SetRepresentationToSurface();
115            _linesActor->GetProperty()->EdgeVisibilityOff();
116            break;
117        default:
118            ;
119        }
120    }
121    if (_seedActor == NULL) {
122        _seedActor = vtkSmartPointer<vtkActor>::New();
123        _seedActor->GetProperty()->SetColor(_seedColor[0], _seedColor[1], _seedColor[2]);
124        _seedActor->GetProperty()->SetLineWidth(1);
125        _seedActor->GetProperty()->SetPointSize(2);
126        _seedActor->GetProperty()->SetOpacity(_opacity);
127        _seedActor->GetProperty()->SetRepresentationToWireframe();
128        _seedActor->GetProperty()->LightingOff();
129    }
130    if (_prop == NULL) {
131        _prop = vtkSmartPointer<vtkAssembly>::New();
132        getAssembly()->AddPart(_linesActor);
133        getAssembly()->AddPart(_seedActor);
134    }
135}
136
137/**
138 * \brief Get a pseudo-random number in range [min,max]
139 */
140double Streamlines::getRandomNum(double min, double max)
141{
142#if 1
143    return vtkMath::Random(min, max);
144#else
145    int r = rand();
146    return (min + ((double)r / RAND_MAX) * (max - min));
147#endif
148}
149
150/**
151 * \brief Get a random 3D point within an AABB
152 *
153 * \param[out] pt The random point
154 * \param[in] bounds The bounds of the AABB
155 */
156void Streamlines::getRandomPoint(double pt[3], const double bounds[6])
157{
158    pt[0] = getRandomNum(bounds[0], bounds[1]);
159    pt[1] = getRandomNum(bounds[2], bounds[3]);
160    pt[2] = getRandomNum(bounds[4], bounds[5]);
161}
162
163/**
164 * \brief Get a random point within a triangle (including edges)
165 *
166 * \param[out] pt The random point
167 * \param[in] v1 Triangle vertex 1
168 * \param[in] v2 Triangle vertex 2
169 * \param[in] v3 Triangle vertex 3
170 */
171void Streamlines::getRandomPointInTriangle(double pt[3],
172                                           const double v1[3],
173                                           const double v2[3],
174                                           const double v3[3])
175{
176    // Choose random barycentric coordinates
177    double bary[3];
178    bary[0] = getRandomNum(0, 1);
179    bary[1] = getRandomNum(0, 1);
180    if (bary[0] + bary[1] > 1.0) {
181        bary[0] = 1.0 - bary[0];
182        bary[1] = 1.0 - bary[1];
183    }
184    bary[2] = 1.0 - bary[0] - bary[1];
185
186    TRACE("bary %g %g %g", bary[0], bary[1], bary[2]);
187    // Convert to cartesian coords
188    for (int i = 0; i < 3; i++) {
189        pt[i] = v1[i] * bary[0] + v2[i] * bary[1] + v3[i] * bary[2];
190    }
191}
192
193/**
194 * \brief Get a random point on a line segment (including endpoints)
195 */
196void Streamlines::getRandomPointOnLineSegment(double pt[3],
197                                              const double endpt[3],
198                                              const double endpt2[3])
199{
200    double ratio = getRandomNum(0, 1);
201    pt[0] = endpt[0] + ratio * (endpt2[0] - endpt[0]);
202    pt[1] = endpt[1] + ratio * (endpt2[1] - endpt[1]);
203    pt[2] = endpt[2] + ratio * (endpt2[2] - endpt[2]);
204}
205
206/**
207 * \brief Get a random point within a vtkDataSet's mesh
208 *
209 * Note: This currently doesn't give a uniform distribution of
210 * points in space and can generate points outside the mesh
211 */
212void Streamlines::getRandomCellPt(double pt[3], vtkDataSet *ds)
213{
214    int numCells = (int)ds->GetNumberOfCells();
215    // XXX: Not uniform distribution (shouldn't use mod, and assumes
216    // all cells are equal area/volume)
217    int cell = rand() % numCells;
218    double bounds[6];
219    ds->GetCellBounds(cell, bounds);
220    // Note: point is inside AABB of cell, but may be outside the cell
221    getRandomPoint(pt, bounds);
222}
223
224/**
225 * \brief Internal method to set up pipeline after a state change
226 */
227void Streamlines::update()
228{
229    if (_dataSet == NULL) {
230        return;
231    }
232
233    vtkDataSet *ds = _dataSet->getVtkDataSet();
234
235    double bounds[6];
236    _dataSet->getBounds(bounds);
237    double maxBound = 0.0;
238    if (bounds[1] - bounds[0] > maxBound) {
239        maxBound = bounds[1] - bounds[0];
240    }
241    if (bounds[3] - bounds[2] > maxBound) {
242        maxBound = bounds[3] - bounds[2];
243    }
244    if (bounds[5] - bounds[4] > maxBound) {
245        maxBound = bounds[5] - bounds[4];
246    }
247
248    vtkSmartPointer<vtkCellDataToPointData> cellToPtData;
249
250    if (ds->GetPointData() == NULL ||
251        ds->GetPointData()->GetVectors() == NULL) {
252        TRACE("No vector point data found in DataSet %s", _dataSet->getName().c_str());
253        if (ds->GetCellData() == NULL ||
254            ds->GetCellData()->GetVectors() == NULL) {
255            ERROR("No vectors found in DataSet %s", _dataSet->getName().c_str());
256        } else {
257            cellToPtData =
258                vtkSmartPointer<vtkCellDataToPointData>::New();
259            cellToPtData->SetInput(ds);
260            //cellToPtData->PassCellDataOn();
261            cellToPtData->Update();
262            ds = cellToPtData->GetOutput();
263        }
264    }
265
266    if (_streamTracer == NULL) {
267        _streamTracer = vtkSmartPointer<vtkStreamTracer>::New();
268    }
269
270    _streamTracer->SetInput(ds);
271    _streamTracer->SetMaximumPropagation(maxBound);
272
273    if (_pdMapper == NULL) {
274        _pdMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
275        _pdMapper->SetResolveCoincidentTopologyToPolygonOffset();
276        _pdMapper->ScalarVisibilityOn();
277    }
278    if (_seedMapper == NULL) {
279        _seedMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
280        _seedMapper->SetResolveCoincidentTopologyToPolygonOffset();
281    }
282
283    // Set up seed source object
284    setSeedToRandomPoints(200);
285
286    switch (_lineType) {
287    case LINES: {
288        _streamTracer->SetComputeVorticity(false);
289        _pdMapper->SetInputConnection(_streamTracer->GetOutputPort());
290    }
291        break;
292    case TUBES: {
293        _streamTracer->SetComputeVorticity(true);
294        _lineFilter = vtkSmartPointer<vtkTubeFilter>::New();
295        vtkTubeFilter *tubeFilter = vtkTubeFilter::SafeDownCast(_lineFilter);
296        tubeFilter->SetNumberOfSides(5);
297        _lineFilter->SetInputConnection(_streamTracer->GetOutputPort());
298        _pdMapper->SetInputConnection(_lineFilter->GetOutputPort());
299    }
300        break;
301    case RIBBONS: {
302        _streamTracer->SetComputeVorticity(true);
303        _lineFilter = vtkSmartPointer<vtkRibbonFilter>::New();
304        _lineFilter->SetInputConnection(_streamTracer->GetOutputPort());
305        _pdMapper->SetInputConnection(_lineFilter->GetOutputPort());
306    }
307        break;
308    default:
309        ERROR("Unknown LineType: %d", _lineType);
310    }
311
312#if defined(DEBUG) && defined(WANT_TRACE)
313    _streamTracer->Update();
314    vtkPolyData *pd = _streamTracer->GetOutput();
315    TRACE("Verts: %d Lines: %d Polys: %d Strips: %d",
316                  pd->GetNumberOfVerts(),
317                  pd->GetNumberOfLines(),
318                  pd->GetNumberOfPolys(),
319                  pd->GetNumberOfStrips());
320#endif
321
322    initProp();
323
324    _seedActor->SetMapper(_seedMapper);
325
326    if (_lut == NULL) {
327        setColorMap(ColorMap::getDefault());
328    }
329
330    setColorMode(_colorMode);
331
332    _linesActor->SetMapper(_pdMapper);
333    _pdMapper->Update();
334    _seedMapper->Update();
335}
336
337/**
338 * \brief Use randomly distributed seed points
339 *
340 * Note: The current implementation doesn't give a uniform
341 * distribution of points, and points outside the mesh bounds
342 * may be generated
343 *
344 * \param[in] numPoints Number of random seed points to generate
345 */
346void Streamlines::setSeedToRandomPoints(int numPoints)
347{
348    if (_streamTracer != NULL) {
349        // Set up seed source object
350        vtkSmartPointer<vtkPolyData> seed = vtkSmartPointer<vtkPolyData>::New();
351        vtkSmartPointer<vtkPoints> pts = vtkSmartPointer<vtkPoints>::New();
352        vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
353
354        for (int i = 0; i < numPoints; i++) {
355            double pt[3];
356            getRandomCellPt(pt, _dataSet->getVtkDataSet());
357            TRACE("Seed pt: %g %g %g", pt[0], pt[1], pt[2]);
358            pts->InsertNextPoint(pt);
359            cells->InsertNextCell(1);
360            cells->InsertCellPoint(i);
361        }
362
363        seed->SetPoints(pts);
364        seed->SetVerts(cells);
365
366        TRACE("Seed points: %d", seed->GetNumberOfPoints());
367        vtkSmartPointer<vtkDataSet> oldSeed;
368        if (_streamTracer->GetSource() != NULL) {
369            oldSeed = _streamTracer->GetSource();
370        }
371
372        _streamTracer->SetSource(seed);
373        if (oldSeed != NULL) {
374            oldSeed->SetPipelineInformation(NULL);
375        }
376
377        _seedMapper->SetInput(seed);
378    }
379}
380
381/**
382 * \brief Use seed points along a line
383 *
384 * \param[in] start Starting point of rake line
385 * \param[in] end End point of rake line
386 * \param[in] numPoints Number of points along line to generate
387 */
388void Streamlines::setSeedToRake(double start[3], double end[3], int numPoints)
389{
390    if (numPoints < 2)
391        return;
392    if (_streamTracer != NULL) {
393        // Set up seed source object
394        vtkSmartPointer<vtkPolyData> seed = vtkSmartPointer<vtkPolyData>::New();
395        vtkSmartPointer<vtkPoints> pts = vtkSmartPointer<vtkPoints>::New();
396        vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
397        vtkSmartPointer<vtkPolyLine> polyline = vtkSmartPointer<vtkPolyLine>::New();
398
399        double dir[3];
400        for (int i = 0; i < 3; i++) {
401            dir[i] = end[i] - start[i];
402        }
403
404        polyline->GetPointIds()->SetNumberOfIds(numPoints);
405        for (int i = 0; i < numPoints; i++) {
406            double pt[3];
407            for (int ii = 0; ii < 3; ii++) {
408                pt[ii] = start[ii] + dir[ii] * ((double)i / (numPoints-1));
409            }
410            TRACE("Seed pt: %g %g %g", pt[0], pt[1], pt[2]);
411            pts->InsertNextPoint(pt);
412            polyline->GetPointIds()->SetId(i, i);
413        }
414
415        cells->InsertNextCell(polyline);
416        seed->SetPoints(pts);
417        seed->SetLines(cells);
418
419        TRACE("Seed points: %d", seed->GetNumberOfPoints());
420        vtkSmartPointer<vtkDataSet> oldSeed;
421        if (_streamTracer->GetSource() != NULL) {
422            oldSeed = _streamTracer->GetSource();
423        }
424
425        _streamTracer->SetSource(seed);
426        if (oldSeed != NULL) {
427            oldSeed->SetPipelineInformation(NULL);
428        }
429
430        _seedMapper->SetInput(seed);
431    }
432}
433
434/**
435 * \brief Create seed points inside a disk with an optional hole
436 *
437 * \param[in] center Center point of disk
438 * \param[in] normal Normal vector to orient disk
439 * \param[in] radius Radius of disk
440 * \param[in] innerRadius Radius of hole at center of disk
441 * \param[in] numPoints Number of random points to generate
442 */
443void Streamlines::setSeedToDisk(double center[3],
444                                double normal[3],
445                                double radius,
446                                double innerRadius,
447                                int numPoints)
448{
449    if (_streamTracer != NULL) {
450        // Set up seed source object
451        vtkSmartPointer<vtkPolyData> seed = vtkSmartPointer<vtkPolyData>::New();
452        vtkSmartPointer<vtkPoints> pts = vtkSmartPointer<vtkPoints>::New();
453        vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
454
455        // The following code is based on vtkRegularPolygonSource::RequestData
456
457        double px[3];
458        double py[3];
459        double axis[3] = {1., 0., 0.};
460
461        if (vtkMath::Normalize(normal) == 0.0) {
462            normal[0] = 0.0;
463            normal[1] = 0.0;
464            normal[2] = 1.0;
465        }
466
467        // Find axis in plane (orthogonal to normal)
468        bool done = false;
469        vtkMath::Cross(normal, axis, px);
470        if (vtkMath::Normalize(px) > 1.0e-3) {
471            done = true;
472        }
473        if (!done) {
474            axis[0] = 0.0;
475            axis[1] = 1.0;
476            axis[2] = 0.0;
477            vtkMath::Cross(normal, axis, px);
478            if (vtkMath::Normalize(px) > 1.0e-3) {
479                done = true;
480            }
481        }
482        if (!done) {
483            axis[0] = 0.0;
484            axis[1] = 0.0;
485            axis[2] = 1.0;
486            vtkMath::Cross(normal, axis, px);
487            vtkMath::Normalize(px);
488        }
489        // Create third orthogonal basis vector
490        vtkMath::Cross(px, normal, py);
491
492        double minSquared = (innerRadius*innerRadius)/(radius*radius);
493        for (int j = 0; j < numPoints; j++) {
494            // Get random sweep angle and radius
495            double angle = getRandomNum(0, 2.0 * vtkMath::DoublePi());
496            // Need sqrt to get uniform distribution
497            double r = sqrt(getRandomNum(minSquared, 1)) * radius;
498            double pt[3];
499            for (int i = 0; i < 3; i++) {
500                pt[i] = center[i] + r * (px[i] * cos(angle) + py[i] * sin(angle));
501            }
502            TRACE("Seed pt: %g %g %g", pt[0], pt[1], pt[2]);
503            pts->InsertNextPoint(pt);
504            cells->InsertNextCell(1);
505            cells->InsertCellPoint(j);
506        }
507
508        seed->SetPoints(pts);
509        seed->SetVerts(cells);
510
511        TRACE("Seed points: %d", seed->GetNumberOfPoints());
512        vtkSmartPointer<vtkDataSet> oldSeed;
513        if (_streamTracer->GetSource() != NULL) {
514            oldSeed = _streamTracer->GetSource();
515        }
516
517        _streamTracer->SetSource(seed);
518        if (oldSeed != NULL) {
519            oldSeed->SetPipelineInformation(NULL);
520        }
521
522        _seedMapper->SetInput(seed);
523    }
524}
525
526/**
527 * \brief Use seed points from an n-sided polygon
528 *
529 * \param[in] center Center point of polygon
530 * \param[in] normal Normal vector to orient polygon
531 * \param[in] angle Angle in degrees to rotate about normal
532 * \param[in] radius Radius of circumscribing circle
533 * \param[in] numSides Number of polygon sides (and points) to generate
534 */
535void Streamlines::setSeedToPolygon(double center[3],
536                                   double normal[3],
537                                   double angle,
538                                   double radius,
539                                   int numSides)
540{
541    if (_streamTracer != NULL) {
542        // Set up seed source object
543        vtkSmartPointer<vtkRegularPolygonSource> seed = vtkSmartPointer<vtkRegularPolygonSource>::New();
544
545        seed->SetCenter(center);
546        seed->SetNormal(normal);
547        seed->SetRadius(radius);
548        seed->SetNumberOfSides(numSides);
549        seed->GeneratePolygonOn();
550
551        if (angle != 0.0) {
552            vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
553            trans->RotateWXYZ(angle, normal);
554            vtkSmartPointer<vtkTransformPolyDataFilter> transFilt =
555                vtkSmartPointer<vtkTransformPolyDataFilter>::New();
556            transFilt->SetInputConnection(seed->GetOutputPort());
557            transFilt->SetTransform(trans);
558        }
559
560        TRACE("Seed points: %d", numSides);
561        vtkSmartPointer<vtkDataSet> oldSeed;
562        if (_streamTracer->GetSource() != NULL) {
563            oldSeed = _streamTracer->GetSource();
564        }
565
566        if (angle != 0.0) {
567            vtkSmartPointer<vtkTransform> trans = vtkSmartPointer<vtkTransform>::New();
568            trans->Translate(+center[0], +center[1], +center[2]);
569            trans->RotateWXYZ(angle, normal);
570            trans->Translate(-center[0], -center[1], -center[2]);
571            vtkSmartPointer<vtkTransformPolyDataFilter> transFilt =
572                vtkSmartPointer<vtkTransformPolyDataFilter>::New();
573            transFilt->SetInputConnection(seed->GetOutputPort());
574            transFilt->SetTransform(trans);
575            _streamTracer->SetSourceConnection(transFilt->GetOutputPort());
576            _seedMapper->SetInputConnection(transFilt->GetOutputPort());
577        } else {
578            _streamTracer->SetSourceConnection(seed->GetOutputPort());
579            _seedMapper->SetInputConnection(seed->GetOutputPort());
580        }
581
582        if (oldSeed != NULL) {
583            oldSeed->SetPipelineInformation(NULL);
584        }
585    }
586}
587
588/**
589 * \brief Use seed points from an n-sided polygon
590 *
591 * \param[in] center Center point of polygon
592 * \param[in] normal Normal vector to orient polygon
593 * \param[in] angle Angle in degrees to rotate about normal
594 * \param[in] radius Radius of circumscribing circle
595 * \param[in] numSides Number of polygon sides (and points) to generate
596 * \param[in] numPoints Number of random points to generate
597 */
598void Streamlines::setSeedToFilledPolygon(double center[3],
599                                         double normal[3],
600                                         double angle,
601                                         double radius,
602                                         int numSides,
603                                         int numPoints)
604{
605    if (_streamTracer != NULL) {
606         // Set up seed source object
607        vtkSmartPointer<vtkPolyData> seed = vtkSmartPointer<vtkPolyData>::New();
608        vtkSmartPointer<vtkPoints> pts = vtkSmartPointer<vtkPoints>::New();
609        vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
610
611        // The following code is based on vtkRegularPolygonSource::RequestData
612
613        double px[3];
614        double py[3];
615        double axis[3] = {1., 0., 0.};
616
617        if (vtkMath::Normalize(normal) == 0.0) {
618            normal[0] = 0.0;
619            normal[1] = 0.0;
620            normal[2] = 1.0;
621        }
622
623        // Find axis in plane (orthogonal to normal)
624        bool done = false;
625        vtkMath::Cross(normal, axis, px);
626        if (vtkMath::Normalize(px) > 1.0e-3) {
627            done = true;
628        }
629        if (!done) {
630            axis[0] = 0.0;
631            axis[1] = 1.0;
632            axis[2] = 0.0;
633            vtkMath::Cross(normal, axis, px);
634            if (vtkMath::Normalize(px) > 1.0e-3) {
635                done = true;
636            }
637        }
638        if (!done) {
639            axis[0] = 0.0;
640            axis[1] = 0.0;
641            axis[2] = 1.0;
642            vtkMath::Cross(normal, axis, px);
643            vtkMath::Normalize(px);
644        }
645        // Create third orthogonal basis vector
646        vtkMath::Cross(px, normal, py);
647
648        double verts[numSides][3];
649        double sliceTheta = 2.0 * vtkMath::DoublePi() / (double)numSides;
650        angle = vtkMath::RadiansFromDegrees(angle);
651        for (int j = 0; j < numSides; j++) {
652            for (int i = 0; i < 3; i++) {
653                double theta = sliceTheta * (double)j - angle;
654                verts[j][i] = center[i] + radius * (px[i] * cos(theta) +
655                                                    py[i] * sin(theta));
656            }
657            TRACE("Vert %d: %g %g %g", j, verts[j][0], verts[j][1], verts[j][2]);
658        }
659
660        // Note: this gives a uniform distribution because the polygon is regular and
661        // the triangular sections have equal area
662        if (numSides == 3) {
663            for (int j = 0; j < numPoints; j++) {
664                double pt[3];
665                getRandomPointInTriangle(pt, verts[0], verts[1], verts[2]);
666                TRACE("Seed pt: %g %g %g", pt[0], pt[1], pt[2]);
667                pts->InsertNextPoint(pt);
668                cells->InsertNextCell(1);
669                cells->InsertCellPoint(j);
670            }
671        } else {
672            for (int j = 0; j < numPoints; j++) {
673                // Get random triangle section
674                int tri = rand() % numSides;
675                double pt[3];
676                getRandomPointInTriangle(pt, center, verts[tri], verts[(tri+1) % numSides]);
677                TRACE("Seed pt: %g %g %g", pt[0], pt[1], pt[2]);
678                pts->InsertNextPoint(pt);
679                cells->InsertNextCell(1);
680                cells->InsertCellPoint(j);
681            }
682        }
683
684        seed->SetPoints(pts);
685        seed->SetVerts(cells);
686
687        TRACE("Seed points: %d", seed->GetNumberOfPoints());
688        vtkSmartPointer<vtkDataSet> oldSeed;
689        if (_streamTracer->GetSource() != NULL) {
690            oldSeed = _streamTracer->GetSource();
691        }
692
693        _streamTracer->SetSource(seed);
694        if (oldSeed != NULL) {
695            oldSeed->SetPipelineInformation(NULL);
696        }
697
698        _seedMapper->SetInput(seed);
699    }
700}
701
702/**
703 * \brief Set maximum length of stream lines in world coordinates
704 */
705void Streamlines::setMaxPropagation(double length)
706{
707    if (_streamTracer != NULL) {
708        _streamTracer->SetMaximumPropagation(length);
709    }
710}
711
712/**
713 * \brief Set streamline type to polylines
714 */
715void Streamlines::setLineTypeToLines()
716{
717    _lineType = LINES;
718    if (_streamTracer != NULL &&
719        _pdMapper != NULL) {
720        _streamTracer->SetComputeVorticity(false);
721        _pdMapper->SetInputConnection(_streamTracer->GetOutputPort());
722        _lineFilter = NULL;
723        setCulling(_linesActor->GetProperty(), false);
724        _linesActor->GetProperty()->SetRepresentationToWireframe();
725        _linesActor->GetProperty()->LightingOff();
726    }
727}
728
729/**
730 * \brief Set streamline type to 3D tubes
731 *
732 * \param[in] numSides Number of sides (>=3) for tubes
733 * \param[in] radius World coordinate minimum tube radius
734 */
735void Streamlines::setLineTypeToTubes(int numSides, double radius)
736{
737    _lineType = TUBES;
738    if (_streamTracer != NULL) {
739        _streamTracer->SetComputeVorticity(true);
740        if (vtkTubeFilter::SafeDownCast(_lineFilter) == NULL) {
741            _lineFilter = vtkSmartPointer<vtkTubeFilter>::New();
742            _lineFilter->SetInputConnection(_streamTracer->GetOutputPort());
743        }
744        vtkTubeFilter *tubeFilter = vtkTubeFilter::SafeDownCast(_lineFilter);
745        if (numSides < 3)
746            numSides = 3;
747        tubeFilter->SetNumberOfSides(numSides);
748        tubeFilter->SetRadius(radius);
749        _pdMapper->SetInputConnection(_lineFilter->GetOutputPort());
750        if (_faceCulling && _opacity == 1.0)
751            setCulling(_linesActor->GetProperty(), true);
752        _linesActor->GetProperty()->SetRepresentationToSurface();
753        _linesActor->GetProperty()->LightingOn();
754     }
755}
756
757/**
758 * \brief Set streamline type to 3D ribbons
759 *
760 * \param[in] width Minimum half-width of ribbons
761 * \param[in] angle Default ribbon angle in degrees from normal
762 */
763void Streamlines::setLineTypeToRibbons(double width, double angle)
764{
765    _lineType = RIBBONS;
766    if (_streamTracer != NULL) {
767        _streamTracer->SetComputeVorticity(true);
768        if (vtkRibbonFilter::SafeDownCast(_lineFilter) == NULL) {
769            _lineFilter = vtkSmartPointer<vtkRibbonFilter>::New();
770            _lineFilter->SetInputConnection(_streamTracer->GetOutputPort());
771        }
772        vtkRibbonFilter *ribbonFilter = vtkRibbonFilter::SafeDownCast(_lineFilter);
773        ribbonFilter->SetWidth(width);
774        ribbonFilter->SetAngle(angle);
775        ribbonFilter->UseDefaultNormalOn();
776        _pdMapper->SetInputConnection(_lineFilter->GetOutputPort());
777        setCulling(_linesActor->GetProperty(), false);
778        _linesActor->GetProperty()->SetRepresentationToSurface();
779        _linesActor->GetProperty()->LightingOn();
780    }
781}
782
783void Streamlines::updateRanges(bool useCumulative,
784                               double scalarRange[2],
785                               double vectorMagnitudeRange[2],
786                               double vectorComponentRange[3][2])
787{
788    if (useCumulative) {
789        _dataRange[0] = scalarRange[0];
790        _dataRange[1] = scalarRange[1];
791        _vectorMagnitudeRange[0] = vectorMagnitudeRange[0];
792        _vectorMagnitudeRange[1] = vectorMagnitudeRange[1];
793        for (int i = 0; i < 3; i++) {
794            _vectorComponentRange[i][0] = vectorComponentRange[i][0];
795            _vectorComponentRange[i][1] = vectorComponentRange[i][1];
796        }
797    } else {
798        _dataSet->getScalarRange(_dataRange);
799        _dataSet->getVectorRange(_vectorMagnitudeRange);
800        for (int i = 0; i < 3; i++) {
801            _dataSet->getVectorRange(_vectorComponentRange[i], i);
802        }
803    }
804
805    // Need to update color map ranges and/or active vector field
806    setColorMode(_colorMode);
807}
808
809void Streamlines::setColorMode(ColorMode mode)
810{
811    _colorMode = mode;
812    if (_dataSet == NULL || _pdMapper == NULL)
813        return;
814
815    vtkDataSet *ds = _dataSet->getVtkDataSet();
816
817    switch (mode) {
818    case COLOR_BY_SCALAR: {
819        _pdMapper->ScalarVisibilityOn();
820        _pdMapper->SetScalarModeToDefault();
821        if (_lut != NULL) {
822            _lut->SetRange(_dataRange);
823        }
824    }
825        break;
826    case COLOR_BY_VECTOR_MAGNITUDE: {
827        _pdMapper->ScalarVisibilityOn();
828        _pdMapper->SetScalarModeToUsePointFieldData();
829        if (ds->GetPointData() != NULL &&
830            ds->GetPointData()->GetVectors() != NULL) {
831            _pdMapper->SelectColorArray(ds->GetPointData()->GetVectors()->GetName());
832        }
833        if (_lut != NULL) {
834            _lut->SetRange(_vectorMagnitudeRange);
835            _lut->SetVectorModeToMagnitude();
836        }
837    }
838        break;
839    case COLOR_BY_VECTOR_X:
840        _pdMapper->ScalarVisibilityOn();
841        _pdMapper->SetScalarModeToUsePointFieldData();
842        if (ds->GetPointData() != NULL &&
843            ds->GetPointData()->GetVectors() != NULL) {
844            _pdMapper->SelectColorArray(ds->GetPointData()->GetVectors()->GetName());
845        }
846        if (_lut != NULL) {
847            _lut->SetRange(_vectorComponentRange[0]);
848            _lut->SetVectorModeToComponent();
849            _lut->SetVectorComponent(0);
850        }
851        break;
852    case COLOR_BY_VECTOR_Y:
853        _pdMapper->ScalarVisibilityOn();
854        _pdMapper->SetScalarModeToUsePointFieldData();
855        if (ds->GetPointData() != NULL &&
856            ds->GetPointData()->GetVectors() != NULL) {
857            _pdMapper->SelectColorArray(ds->GetPointData()->GetVectors()->GetName());
858        }
859        if (_lut != NULL) {
860            _lut->SetRange(_vectorComponentRange[1]);
861            _lut->SetVectorModeToComponent();
862            _lut->SetVectorComponent(1);
863        }
864        break;
865    case COLOR_BY_VECTOR_Z:
866        _pdMapper->ScalarVisibilityOn();
867        _pdMapper->SetScalarModeToUsePointFieldData();
868        if (ds->GetPointData() != NULL &&
869            ds->GetPointData()->GetVectors() != NULL) {
870            _pdMapper->SelectColorArray(ds->GetPointData()->GetVectors()->GetName());
871        }
872        if (_lut != NULL) {
873            _lut->SetRange(_vectorComponentRange[2]);
874            _lut->SetVectorModeToComponent();
875            _lut->SetVectorComponent(2);
876        }
877        break;
878    case COLOR_CONSTANT:
879    default:
880        _pdMapper->ScalarVisibilityOff();
881        break;
882    }
883}
884
885/**
886 * \brief Called when the color map has been edited
887 */
888void Streamlines::updateColorMap()
889{
890    setColorMap(_colorMap);
891}
892
893/**
894 * \brief Associate a colormap lookup table with the DataSet
895 */
896void Streamlines::setColorMap(ColorMap *cmap)
897{
898    if (cmap == NULL)
899        return;
900
901    _colorMap = cmap;
902 
903    if (_lut == NULL) {
904        _lut = vtkSmartPointer<vtkLookupTable>::New();
905        if (_pdMapper != NULL) {
906            _pdMapper->UseLookupTableScalarRangeOn();
907            _pdMapper->SetLookupTable(_lut);
908        }
909    }
910
911    _lut->DeepCopy(cmap->getLookupTable());
912
913    switch (_colorMode) {
914    case COLOR_CONSTANT:
915    case COLOR_BY_SCALAR:
916        _lut->SetRange(_dataRange);
917        break;
918    case COLOR_BY_VECTOR_MAGNITUDE:
919        _lut->SetVectorModeToMagnitude();
920        _lut->SetRange(_vectorMagnitudeRange);
921        break;
922    case COLOR_BY_VECTOR_X:
923        _lut->SetVectorModeToComponent();
924        _lut->SetVectorComponent(0);
925        _lut->SetRange(_vectorComponentRange[0]);
926        break;
927    case COLOR_BY_VECTOR_Y:
928        _lut->SetVectorModeToComponent();
929        _lut->SetVectorComponent(1);
930        _lut->SetRange(_vectorComponentRange[1]);
931        break;
932    case COLOR_BY_VECTOR_Z:
933        _lut->SetVectorModeToComponent();
934        _lut->SetVectorComponent(2);
935        _lut->SetRange(_vectorComponentRange[2]);
936        break;
937    default:
938         break;
939    }
940}
941
942/**
943 * \brief Turn on/off lighting of this object
944 */
945void Streamlines::setLighting(bool state)
946{
947    _lighting = state;
948    if (_linesActor != NULL)
949        _linesActor->GetProperty()->SetLighting((state ? 1 : 0));
950}
951
952/**
953 * \brief Set opacity of this object
954 */
955void Streamlines::setOpacity(double opacity)
956{
957    _opacity = opacity;
958    if (_linesActor != NULL) {
959        _linesActor->GetProperty()->SetOpacity(_opacity);
960        if (_opacity < 1.0)
961            setCulling(_linesActor->GetProperty(), false);
962        else if (_faceCulling && _lineType == TUBES)
963            setCulling(_linesActor->GetProperty(), true);
964    }
965    if (_seedActor != NULL) {
966        _seedActor->GetProperty()->SetOpacity(_opacity);
967    }
968}
969
970/**
971 * \brief Turn on/off rendering of this Streamlines
972 */
973void Streamlines::setVisibility(bool state)
974{
975    if (_linesActor != NULL) {
976        _linesActor->SetVisibility((state ? 1 : 0));
977    }
978    if (_seedActor != NULL) {
979        if (!state ||
980            (state && _seedVisible)) {
981            _seedActor->SetVisibility((state ? 1 : 0));
982        }
983    }
984}
985
986/**
987 * \brief Turn on/off rendering of the seed geometry
988 */
989void Streamlines::setSeedVisibility(bool state)
990{
991    _seedVisible = state;
992    if (_seedActor != NULL) {
993        _seedActor->SetVisibility((state ? 1 : 0));
994    }
995}
996
997/**
998 * \brief Get visibility state of the Streamlines
999 *
1000 * \return Are the Streamlines visible?
1001 */
1002bool Streamlines::getVisibility()
1003{
1004    if (_linesActor == NULL) {
1005        return false;
1006    } else {
1007        return (_linesActor->GetVisibility() != 0);
1008    }
1009}
1010
1011/**
1012 * \brief Turn on/off rendering of edges
1013 */
1014void Streamlines::setEdgeVisibility(bool state)
1015{
1016    if (_linesActor != NULL) {
1017        _linesActor->GetProperty()->SetEdgeVisibility((state ? 1 : 0));
1018    }
1019}
1020
1021/**
1022 * \brief Set RGB color of stream lines
1023 */
1024void Streamlines::setColor(float color[3])
1025{
1026    _color[0] = color[0];
1027    _color[1] = color[1];
1028    _color[2] = color[2];
1029    if (_linesActor != NULL)
1030        _linesActor->GetProperty()->SetColor(_color[0], _color[1], _color[2]);
1031}
1032
1033/**
1034 * \brief Set RGB color of stream line edges
1035 */
1036void Streamlines::setEdgeColor(float color[3])
1037{
1038    _edgeColor[0] = color[0];
1039    _edgeColor[1] = color[1];
1040    _edgeColor[2] = color[2];
1041    if (_linesActor != NULL)
1042        _linesActor->GetProperty()->SetEdgeColor(_edgeColor[0], _edgeColor[1], _edgeColor[2]);
1043}
1044
1045/**
1046 * \brief Set RGB color of seed geometry
1047 */
1048void Streamlines::setSeedColor(float color[3])
1049{
1050    _seedColor[0] = color[0];
1051    _seedColor[1] = color[1];
1052    _seedColor[2] = color[2];
1053    if (_seedActor != NULL)
1054        _seedActor->GetProperty()->SetColor(_seedColor[0], _seedColor[1], _seedColor[2]);
1055}
1056
1057/**
1058 * \brief Set pixel width of stream lines (may be a no-op)
1059 */
1060void Streamlines::setEdgeWidth(float edgeWidth)
1061{
1062    _edgeWidth = edgeWidth;
1063    if (_linesActor != NULL)
1064        _linesActor->GetProperty()->SetLineWidth(_edgeWidth);
1065}
1066
1067/**
1068 * \brief Set a group of world coordinate planes to clip rendering
1069 *
1070 * Passing NULL for planes will remove all cliping planes
1071 */
1072void Streamlines::setClippingPlanes(vtkPlaneCollection *planes)
1073{
1074    if (_pdMapper != NULL) {
1075        _pdMapper->SetClippingPlanes(planes);
1076    }
1077    if (_seedMapper != NULL) {
1078        _seedMapper->SetClippingPlanes(planes);
1079    }
1080}
Note: See TracBrowser for help on using the repository browser.