source: branches/blt4/packages/vizservers/vtkvis/RpStreamlines.cpp @ 2542

Last change on this file since 2542 was 2542, checked in by gah, 12 years ago

update from trunk

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