1 /*=========================================================================
2 
3 Program:   Visualization Toolkit
4 Module:    vtkStreamTracer.cxx
5 
6 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7 All rights reserved.
8 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10 This software is distributed WITHOUT ANY WARRANTY; without even
11 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12 PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkStreamTracer.h"
16 
17 #include "vtkAMRInterpolatedVelocityField.h"
18 #include "vtkAbstractInterpolatedVelocityField.h"
19 #include "vtkCellArray.h"
20 #include "vtkCellData.h"
21 #include "vtkCellLocatorInterpolatedVelocityField.h"
22 #include "vtkCompositeDataIterator.h"
23 #include "vtkCompositeDataPipeline.h"
24 #include "vtkCompositeDataSet.h"
25 #include "vtkDataSetAttributes.h"
26 #include "vtkDoubleArray.h"
27 #include "vtkExecutive.h"
28 #include "vtkGenericCell.h"
29 #include "vtkIdList.h"
30 #include "vtkInformation.h"
31 #include "vtkInformationVector.h"
32 #include "vtkIntArray.h"
33 #include "vtkInterpolatedVelocityField.h"
34 #include "vtkMath.h"
35 #include "vtkMultiBlockDataSet.h"
36 #include "vtkNew.h"
37 #include "vtkObjectFactory.h"
38 #include "vtkOverlappingAMR.h"
39 #include "vtkPointData.h"
40 #include "vtkPointSet.h"
41 #include "vtkPolyData.h"
42 #include "vtkPolyLine.h"
43 #include "vtkRungeKutta2.h"
44 #include "vtkRungeKutta4.h"
45 #include "vtkRungeKutta45.h"
46 #include "vtkSmartPointer.h"
47 #include "vtkStaticCellLocator.h"
48 
49 #include <vector>
50 
51 vtkObjectFactoryNewMacro(vtkStreamTracer);
52 vtkCxxSetObjectMacro(vtkStreamTracer, Integrator, vtkInitialValueProblemSolver);
53 vtkCxxSetObjectMacro(vtkStreamTracer, InterpolatorPrototype, vtkAbstractInterpolatedVelocityField);
54 
55 const double vtkStreamTracer::EPSILON = 1.0E-12;
56 
57 namespace
58 {
59 // special function to interpolate the point data from the input to the output
60 // if fast == true, then it just calls the usual InterpolatePoint function,
61 // otherwise,
62 // it makes sure the array exists in the input before trying to copy it to the
63 // output. if it doesn't exist in the input but is in the output then we
64 // remove it from the output instead of having bad values there.
65 // this is meant for multiblock data sets where the grids may not have the
66 // same point data arrays or have them in different orders.
InterpolatePoint(vtkDataSetAttributes * outPointData,vtkDataSetAttributes * inPointData,vtkIdType toId,vtkIdList * ids,double * weights,bool fast)67 void InterpolatePoint(vtkDataSetAttributes* outPointData, vtkDataSetAttributes* inPointData,
68   vtkIdType toId, vtkIdList* ids, double* weights, bool fast)
69 {
70   if (fast)
71   {
72     outPointData->InterpolatePoint(inPointData, toId, ids, weights);
73   }
74   else
75   {
76     for (int i = outPointData->GetNumberOfArrays() - 1; i >= 0; i--)
77     {
78       vtkAbstractArray* toArray = outPointData->GetAbstractArray(i);
79       if (vtkAbstractArray* fromArray = inPointData->GetAbstractArray(toArray->GetName()))
80       {
81         toArray->InterpolateTuple(toId, ids, fromArray, weights);
82       }
83       else
84       {
85         outPointData->RemoveArray(toArray->GetName());
86       }
87     }
88   }
89 }
90 
91 }
92 
93 //------------------------------------------------------------------------------
vtkStreamTracer()94 vtkStreamTracer::vtkStreamTracer()
95 {
96   this->Integrator = vtkRungeKutta2::New();
97   this->IntegrationDirection = FORWARD;
98   for (int i = 0; i < 3; i++)
99   {
100     this->StartPosition[i] = 0.0;
101   }
102 
103   this->MaximumPropagation = 1.0;
104   this->IntegrationStepUnit = CELL_LENGTH_UNIT;
105   this->InitialIntegrationStep = 0.5;
106   this->MinimumIntegrationStep = 1.0E-2;
107   this->MaximumIntegrationStep = 1.0;
108 
109   this->MaximumError = 1.0e-6;
110   this->MaximumNumberOfSteps = 2000;
111   this->TerminalSpeed = EPSILON;
112 
113   this->ComputeVorticity = true;
114   this->RotationScale = 1.0;
115 
116   this->LastUsedStepSize = 0.0;
117 
118   this->GenerateNormalsInIntegrate = true;
119 
120   this->InterpolatorPrototype = nullptr;
121 
122   this->SetNumberOfInputPorts(2);
123 
124   // by default process active point vectors
125   this->SetInputArrayToProcess(
126     0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, vtkDataSetAttributes::VECTORS);
127 
128   this->HasMatchingPointAttributes = true;
129 
130   this->SurfaceStreamlines = false;
131 }
132 
133 //------------------------------------------------------------------------------
~vtkStreamTracer()134 vtkStreamTracer::~vtkStreamTracer()
135 {
136   this->SetIntegrator(nullptr);
137   this->SetInterpolatorPrototype(nullptr);
138 }
139 
140 //------------------------------------------------------------------------------
SetSourceConnection(vtkAlgorithmOutput * algOutput)141 void vtkStreamTracer::SetSourceConnection(vtkAlgorithmOutput* algOutput)
142 {
143   this->SetInputConnection(1, algOutput);
144 }
145 
146 //------------------------------------------------------------------------------
SetSourceData(vtkDataSet * source)147 void vtkStreamTracer::SetSourceData(vtkDataSet* source)
148 {
149   this->SetInputData(1, source);
150 }
151 
152 //------------------------------------------------------------------------------
GetSource()153 vtkDataSet* vtkStreamTracer::GetSource()
154 {
155   if (this->GetNumberOfInputConnections(1) < 1)
156   {
157     return nullptr;
158   }
159   return vtkDataSet::SafeDownCast(this->GetExecutive()->GetInputData(1, 0));
160 }
161 
162 //------------------------------------------------------------------------------
GetIntegratorType()163 int vtkStreamTracer::GetIntegratorType()
164 {
165   if (!this->Integrator)
166   {
167     return NONE;
168   }
169   if (!strcmp(this->Integrator->GetClassName(), "vtkRungeKutta2"))
170   {
171     return RUNGE_KUTTA2;
172   }
173   if (!strcmp(this->Integrator->GetClassName(), "vtkRungeKutta4"))
174   {
175     return RUNGE_KUTTA4;
176   }
177   if (!strcmp(this->Integrator->GetClassName(), "vtkRungeKutta45"))
178   {
179     return RUNGE_KUTTA45;
180   }
181   return UNKNOWN;
182 }
183 
184 //------------------------------------------------------------------------------
SetInterpolatorTypeToDataSetPointLocator()185 void vtkStreamTracer::SetInterpolatorTypeToDataSetPointLocator()
186 {
187   this->SetInterpolatorType(static_cast<int>(INTERPOLATOR_WITH_DATASET_POINT_LOCATOR));
188 }
189 
190 //------------------------------------------------------------------------------
SetInterpolatorTypeToCellLocator()191 void vtkStreamTracer::SetInterpolatorTypeToCellLocator()
192 {
193   this->SetInterpolatorType(static_cast<int>(INTERPOLATOR_WITH_CELL_LOCATOR));
194 }
195 
196 //------------------------------------------------------------------------------
SetInterpolatorType(int interpType)197 void vtkStreamTracer::SetInterpolatorType(int interpType)
198 {
199   if (interpType == INTERPOLATOR_WITH_CELL_LOCATOR)
200   {
201     // create an interpolator equipped with a cell locator
202     vtkNew<vtkCellLocatorInterpolatedVelocityField> cellLoc;
203 
204     // specify the type of the cell locator attached to the interpolator
205     constexpr double tolerance = 1e-6;
206     vtkNew<vtkStaticCellLocator> cellLocType;
207     cellLocType->SetTolerance(tolerance);
208     cellLocType->UseDiagonalLengthToleranceOn();
209     cellLoc->SetCellLocatorPrototype(cellLocType);
210 
211     this->SetInterpolatorPrototype(cellLoc);
212   }
213   else
214   {
215     // create an interpolator equipped with a point locator (by default)
216     vtkSmartPointer<vtkInterpolatedVelocityField> pntLoc =
217       vtkSmartPointer<vtkInterpolatedVelocityField>::New();
218     this->SetInterpolatorPrototype(pntLoc);
219   }
220 }
221 
222 //------------------------------------------------------------------------------
SetIntegratorType(int type)223 void vtkStreamTracer::SetIntegratorType(int type)
224 {
225   vtkInitialValueProblemSolver* ivp = nullptr;
226   switch (type)
227   {
228     case RUNGE_KUTTA2:
229       ivp = vtkRungeKutta2::New();
230       break;
231     case RUNGE_KUTTA4:
232       ivp = vtkRungeKutta4::New();
233       break;
234     case RUNGE_KUTTA45:
235       ivp = vtkRungeKutta45::New();
236       break;
237     default:
238       vtkWarningMacro("Unrecognized integrator type. Keeping old one.");
239       break;
240   }
241   if (ivp)
242   {
243     this->SetIntegrator(ivp);
244     ivp->Delete();
245   }
246 }
247 
248 //------------------------------------------------------------------------------
SetIntegrationStepUnit(int unit)249 void vtkStreamTracer::SetIntegrationStepUnit(int unit)
250 {
251   if (unit != LENGTH_UNIT && unit != CELL_LENGTH_UNIT)
252   {
253     unit = CELL_LENGTH_UNIT;
254   }
255 
256   if (unit == this->IntegrationStepUnit)
257   {
258     return;
259   }
260 
261   this->IntegrationStepUnit = unit;
262   this->Modified();
263 }
264 
265 //------------------------------------------------------------------------------
ConvertToLength(double interval,int unit,double cellLength)266 double vtkStreamTracer::ConvertToLength(double interval, int unit, double cellLength)
267 {
268   double retVal = 0.0;
269   if (unit == LENGTH_UNIT)
270   {
271     retVal = interval;
272   }
273   else if (unit == CELL_LENGTH_UNIT)
274   {
275     retVal = interval * cellLength;
276   }
277   return retVal;
278 }
279 
280 //------------------------------------------------------------------------------
ConvertToLength(vtkStreamTracer::IntervalInformation & interval,double cellLength)281 double vtkStreamTracer::ConvertToLength(
282   vtkStreamTracer::IntervalInformation& interval, double cellLength)
283 {
284   return ConvertToLength(interval.Interval, interval.Unit, cellLength);
285 }
286 
287 //------------------------------------------------------------------------------
ConvertIntervals(double & step,double & minStep,double & maxStep,int direction,double cellLength)288 void vtkStreamTracer::ConvertIntervals(
289   double& step, double& minStep, double& maxStep, int direction, double cellLength)
290 {
291   minStep = maxStep = step = direction *
292     this->ConvertToLength(this->InitialIntegrationStep, this->IntegrationStepUnit, cellLength);
293 
294   if (this->MinimumIntegrationStep > 0.0)
295   {
296     minStep =
297       this->ConvertToLength(this->MinimumIntegrationStep, this->IntegrationStepUnit, cellLength);
298   }
299 
300   if (this->MaximumIntegrationStep > 0.0)
301   {
302     maxStep =
303       this->ConvertToLength(this->MaximumIntegrationStep, this->IntegrationStepUnit, cellLength);
304   }
305 }
306 
307 //------------------------------------------------------------------------------
CalculateVorticity(vtkGenericCell * cell,double pcoords[3],vtkDoubleArray * cellVectors,double vorticity[3])308 void vtkStreamTracer::CalculateVorticity(
309   vtkGenericCell* cell, double pcoords[3], vtkDoubleArray* cellVectors, double vorticity[3])
310 {
311   double* cellVel;
312   double derivs[9];
313 
314   cellVel = cellVectors->GetPointer(0);
315   cell->Derivatives(0, pcoords, cellVel, 3, derivs);
316   vorticity[0] = derivs[7] - derivs[5];
317   vorticity[1] = derivs[2] - derivs[6];
318   vorticity[2] = derivs[3] - derivs[1];
319 }
320 
321 //------------------------------------------------------------------------------
InitializeSeeds(vtkDataArray * & seeds,vtkIdList * & seedIds,vtkIntArray * & integrationDirections,vtkDataSet * source)322 void vtkStreamTracer::InitializeSeeds(vtkDataArray*& seeds, vtkIdList*& seedIds,
323   vtkIntArray*& integrationDirections, vtkDataSet* source)
324 {
325   seedIds = vtkIdList::New();
326   integrationDirections = vtkIntArray::New();
327   seeds = nullptr;
328 
329   if (source)
330   {
331     vtkIdType numSeeds = source->GetNumberOfPoints();
332     if (numSeeds > 0)
333     {
334       // For now, one thread will do all
335 
336       if (this->IntegrationDirection == BOTH)
337       {
338         seedIds->SetNumberOfIds(2 * numSeeds);
339         for (vtkIdType i = 0; i < numSeeds; ++i)
340         {
341           seedIds->SetId(i, i);
342           seedIds->SetId(numSeeds + i, i);
343         }
344       }
345       else
346       {
347         seedIds->SetNumberOfIds(numSeeds);
348         for (vtkIdType i = 0; i < numSeeds; ++i)
349         {
350           seedIds->SetId(i, i);
351         }
352       }
353       // Check if the source is a PointSet
354       vtkPointSet* seedPts = vtkPointSet::SafeDownCast(source);
355       if (seedPts)
356       {
357         // If it is, use it's points as source
358         vtkDataArray* orgSeeds = seedPts->GetPoints()->GetData();
359         seeds = orgSeeds->NewInstance();
360         seeds->DeepCopy(orgSeeds);
361       }
362       else
363       {
364         // Else, create a seed source
365         seeds = vtkDoubleArray::New();
366         seeds->SetNumberOfComponents(3);
367         seeds->SetNumberOfTuples(numSeeds);
368         for (vtkIdType i = 0; i < numSeeds; ++i)
369         {
370           seeds->SetTuple(i, source->GetPoint(i));
371         }
372       }
373     }
374   }
375   else
376   {
377     seeds = vtkDoubleArray::New();
378     seeds->SetNumberOfComponents(3);
379     seeds->InsertNextTuple(this->StartPosition);
380     seedIds->InsertNextId(0);
381     if (this->IntegrationDirection == BOTH)
382     {
383       seedIds->InsertNextId(0);
384     }
385   }
386 
387   if (seeds)
388   {
389     vtkIdType i;
390     vtkIdType numSeeds = seeds->GetNumberOfTuples();
391     if (this->IntegrationDirection == BOTH)
392     {
393       for (i = 0; i < numSeeds; i++)
394       {
395         integrationDirections->InsertNextValue(FORWARD);
396       }
397       for (i = 0; i < numSeeds; i++)
398       {
399         integrationDirections->InsertNextValue(BACKWARD);
400       }
401     }
402     else
403     {
404       for (i = 0; i < numSeeds; i++)
405       {
406         integrationDirections->InsertNextValue(this->IntegrationDirection);
407       }
408     }
409   }
410 }
411 
412 //------------------------------------------------------------------------------
SetupOutput(vtkInformation * inInfo,vtkInformation * outInfo)413 int vtkStreamTracer::SetupOutput(vtkInformation* inInfo, vtkInformation* outInfo)
414 {
415   if (!inInfo || !outInfo)
416   {
417     vtkErrorMacro("Input/Output informations are not set, aborting.");
418     return 0;
419   }
420 
421   int piece = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER());
422   int numPieces = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES());
423 
424   vtkDataObject* input = inInfo->Get(vtkDataObject::DATA_OBJECT());
425   vtkDataObject* output = outInfo->Get(vtkDataObject::DATA_OBJECT());
426 
427   // Pass through field data
428   output->GetFieldData()->PassData(input->GetFieldData());
429 
430   vtkCompositeDataSet* hdInput = vtkCompositeDataSet::SafeDownCast(input);
431   vtkDataSet* dsInput = vtkDataSet::SafeDownCast(input);
432   if (hdInput)
433   {
434     this->InputData = hdInput;
435     hdInput->Register(this);
436     return 1;
437   }
438   else if (dsInput)
439   {
440     vtkMultiBlockDataSet* mb = vtkMultiBlockDataSet::New();
441     mb->SetNumberOfBlocks(numPieces);
442     mb->SetBlock(piece, dsInput);
443     this->InputData = mb;
444     mb->Register(this);
445     mb->Delete();
446     return 1;
447   }
448   else
449   {
450     vtkErrorMacro(
451       "This filter cannot handle input of type: " << (input ? input->GetClassName() : "(none)"));
452     return 0;
453   }
454 }
455 
456 //------------------------------------------------------------------------------
RequestData(vtkInformation * vtkNotUsed (request),vtkInformationVector ** inputVector,vtkInformationVector * outputVector)457 int vtkStreamTracer::RequestData(vtkInformation* vtkNotUsed(request),
458   vtkInformationVector** inputVector, vtkInformationVector* outputVector)
459 {
460   vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
461   vtkInformation* outInfo = outputVector->GetInformationObject(0);
462 
463   if (!this->SetupOutput(inInfo, outInfo))
464   {
465     return 0;
466   }
467 
468   vtkInformation* sourceInfo = inputVector[1]->GetInformationObject(0);
469   vtkDataSet* source = nullptr;
470   if (sourceInfo)
471   {
472     source = vtkDataSet::SafeDownCast(sourceInfo->Get(vtkDataObject::DATA_OBJECT()));
473   }
474   vtkPolyData* output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));
475 
476   vtkDataArray* seeds = nullptr;
477   vtkIdList* seedIds = nullptr;
478   vtkIntArray* integrationDirections = nullptr;
479   this->InitializeSeeds(seeds, seedIds, integrationDirections, source);
480 
481   if (seeds)
482   {
483     double lastPoint[3];
484     vtkAbstractInterpolatedVelocityField* func = nullptr;
485     int maxCellSize = 0;
486     if (this->CheckInputs(func, &maxCellSize) != VTK_OK)
487     {
488       vtkDebugMacro("No appropriate inputs have been found. Can not execute.");
489       if (func)
490       {
491         func->Delete();
492       }
493       seeds->Delete();
494       integrationDirections->Delete();
495       seedIds->Delete();
496       this->InputData->UnRegister(this);
497       return 1;
498     }
499 
500     if (vtkOverlappingAMR::SafeDownCast(this->InputData))
501     {
502       vtkOverlappingAMR* amr = vtkOverlappingAMR::SafeDownCast(this->InputData);
503       amr->GenerateParentChildInformation();
504     }
505 
506     vtkCompositeDataIterator* iter = this->InputData->NewIterator();
507     vtkSmartPointer<vtkCompositeDataIterator> iterP(iter);
508     iter->Delete();
509 
510     iterP->GoToFirstItem();
511     vtkDataSet* input0 = nullptr;
512     if (!iterP->IsDoneWithTraversal() && !input0)
513     {
514       input0 = vtkDataSet::SafeDownCast(iterP->GetCurrentDataObject());
515       iterP->GoToNextItem();
516     }
517     int vecType(0);
518     vtkDataArray* vectors = this->GetInputArrayToProcess(0, input0, vecType);
519     if (vectors)
520     {
521       const char* vecName = vectors->GetName();
522       double propagation = 0;
523       vtkIdType numSteps = 0;
524       double integrationTime = 0;
525       this->Integrate(input0->GetPointData(), output, seeds, seedIds, integrationDirections,
526         lastPoint, func, maxCellSize, vecType, vecName, propagation, numSteps, integrationTime);
527     }
528     func->Delete();
529     seeds->Delete();
530   }
531 
532   integrationDirections->Delete();
533   seedIds->Delete();
534 
535   this->InputData->UnRegister(this);
536   return 1;
537 }
538 
539 //------------------------------------------------------------------------------
CheckInputs(vtkAbstractInterpolatedVelocityField * & func,int * maxCellSize)540 int vtkStreamTracer::CheckInputs(vtkAbstractInterpolatedVelocityField*& func, int* maxCellSize)
541 {
542   if (!this->InputData)
543   {
544     return VTK_ERROR;
545   }
546 
547   vtkOverlappingAMR* amrData = vtkOverlappingAMR::SafeDownCast(this->InputData);
548 
549   vtkSmartPointer<vtkCompositeDataIterator> iter;
550   iter.TakeReference(this->InputData->NewIterator());
551 
552   vtkDataSet* input0 = nullptr;
553   iter->GoToFirstItem();
554   while (!iter->IsDoneWithTraversal() && input0 == nullptr)
555   {
556     input0 = vtkDataSet::SafeDownCast(iter->GetCurrentDataObject());
557     iter->GoToNextItem();
558   }
559   if (!input0)
560   {
561     return VTK_ERROR;
562   }
563 
564   int vecType(0);
565   vtkDataArray* vectors = this->GetInputArrayToProcess(0, input0, vecType);
566   if (!vectors)
567   {
568     return VTK_ERROR;
569   }
570 
571   // Set the function set to be integrated
572   if (!this->InterpolatorPrototype)
573   {
574     if (amrData)
575     {
576       func = vtkAMRInterpolatedVelocityField::New();
577     }
578     else
579     {
580       func = vtkInterpolatedVelocityField::New();
581     }
582     // turn on the following segment, in place of the above line, if an
583     // interpolator equipped with a cell locator is dedired as the default
584     //
585     // func = vtkCellLocatorInterpolatedVelocityField::New();
586     // vtkSmartPointer< vtkStaticCellLocator > locator =
587     // vtkSmartPointer< vtkStaticCellLocator >::New();
588     // vtkCellLocatorInterpolatedVelocityField::SafeDownCast( func )
589     //   ->SetCellLocatorPrototype( locator );
590   }
591   else
592   {
593     if (amrData &&
594       vtkAMRInterpolatedVelocityField::SafeDownCast(this->InterpolatorPrototype) == nullptr)
595     {
596       this->InterpolatorPrototype = vtkAMRInterpolatedVelocityField::New();
597     }
598     func = this->InterpolatorPrototype->NewInstance();
599     func->CopyParameters(this->InterpolatorPrototype);
600   }
601 
602   if (vtkAMRInterpolatedVelocityField::SafeDownCast(func))
603   {
604     assert(amrData);
605     vtkAMRInterpolatedVelocityField::SafeDownCast(func)->SetAMRData(amrData);
606     if (maxCellSize)
607     {
608       *maxCellSize = 8;
609     }
610   }
611   else if (vtkCompositeInterpolatedVelocityField::SafeDownCast(func))
612   {
613     iter->GoToFirstItem();
614     while (!iter->IsDoneWithTraversal())
615     {
616       vtkDataSet* inp = vtkDataSet::SafeDownCast(iter->GetCurrentDataObject());
617       if (inp)
618       {
619         int cellSize = inp->GetMaxCellSize();
620         if (cellSize > *maxCellSize)
621         {
622           *maxCellSize = cellSize;
623         }
624         vtkCompositeInterpolatedVelocityField::SafeDownCast(func)->AddDataSet(inp);
625       }
626       iter->GoToNextItem();
627     }
628   }
629   else
630   {
631     assert(false);
632   }
633 
634   const char* vecName = vectors->GetName();
635   func->SelectVectors(vecType, vecName);
636 
637   // Check if the data attributes match, warn if not
638   vtkPointData* pd0 = input0->GetPointData();
639   int numPdArrays = pd0->GetNumberOfArrays();
640   this->HasMatchingPointAttributes = true;
641   for (iter->GoToFirstItem(); !iter->IsDoneWithTraversal(); iter->GoToNextItem())
642   {
643     vtkDataSet* data = vtkDataSet::SafeDownCast(iter->GetCurrentDataObject());
644     vtkPointData* pd = data->GetPointData();
645     if (pd->GetNumberOfArrays() != numPdArrays)
646     {
647       this->HasMatchingPointAttributes = false;
648     }
649     for (int i = 0; i < numPdArrays; i++)
650     {
651       if (!pd->GetArray(pd0->GetArrayName(i)) || !pd0->GetArray(pd->GetArrayName(i)))
652       {
653         this->HasMatchingPointAttributes = false;
654       }
655     }
656   }
657   return VTK_OK;
658 }
659 
660 //------------------------------------------------------------------------------
Integrate(vtkPointData * input0Data,vtkPolyData * output,vtkDataArray * seedSource,vtkIdList * seedIds,vtkIntArray * integrationDirections,double lastPoint[3],vtkAbstractInterpolatedVelocityField * func,int maxCellSize,int vecType,const char * vecName,double & inPropagation,vtkIdType & inNumSteps,double & inIntegrationTime)661 void vtkStreamTracer::Integrate(vtkPointData* input0Data, vtkPolyData* output,
662   vtkDataArray* seedSource, vtkIdList* seedIds, vtkIntArray* integrationDirections,
663   double lastPoint[3], vtkAbstractInterpolatedVelocityField* func, int maxCellSize, int vecType,
664   const char* vecName, double& inPropagation, vtkIdType& inNumSteps, double& inIntegrationTime)
665 {
666   vtkIdType numLines = seedIds->GetNumberOfIds();
667   double propagation = inPropagation;
668   vtkIdType numSteps = inNumSteps;
669   double integrationTime = inIntegrationTime;
670 
671   // Useful pointers
672   vtkDataSetAttributes* outputPD = output->GetPointData();
673   vtkDataSetAttributes* outputCD = output->GetCellData();
674   vtkPointData* inputPD;
675   vtkDataSet* input;
676   vtkDataArray* inVectors;
677 
678   int direction = 1;
679 
680   if (this->GetIntegrator() == nullptr)
681   {
682     vtkErrorMacro("No integrator is specified.");
683     return;
684   }
685 
686   double* weights = nullptr;
687   if (maxCellSize > 0)
688   {
689     weights = new double[maxCellSize];
690   }
691 
692   // Used in GetCell()
693   vtkGenericCell* cell = vtkGenericCell::New();
694 
695   // Create a new integrator, the type is the same as Integrator
696   vtkInitialValueProblemSolver* integrator = this->GetIntegrator()->NewInstance();
697   integrator->SetFunctionSet(func);
698 
699   // Check Surface option
700   vtkInterpolatedVelocityField* surfaceFunc = nullptr;
701   if (this->SurfaceStreamlines == true)
702   {
703     surfaceFunc = vtkInterpolatedVelocityField::SafeDownCast(func);
704     if (surfaceFunc)
705     {
706       surfaceFunc->SetForceSurfaceTangentVector(true);
707       surfaceFunc->SetSurfaceDataset(true);
708     }
709   }
710 
711   // Since we do not know what the total number of points
712   // will be, we do not allocate any. This is important for
713   // cases where a lot of streamers are used at once. If we
714   // were to allocate any points here, potentially, we can
715   // waste a lot of memory if a lot of streamers are used.
716   // Always insert the first point
717   vtkPoints* outputPoints = vtkPoints::New();
718   vtkCellArray* outputLines = vtkCellArray::New();
719 
720   // We will keep track of integration time in this array
721   vtkDoubleArray* time = vtkDoubleArray::New();
722   time->SetName("IntegrationTime");
723 
724   // This array explains why the integration stopped
725   vtkIntArray* retVals = vtkIntArray::New();
726   retVals->SetName("ReasonForTermination");
727 
728   vtkIntArray* sids = vtkIntArray::New();
729   sids->SetName("SeedIds");
730 
731   vtkSmartPointer<vtkDoubleArray> velocityVectors;
732   if (vecType != vtkDataObject::POINT)
733   {
734     velocityVectors = vtkSmartPointer<vtkDoubleArray>::New();
735     velocityVectors->SetName(vecName);
736     velocityVectors->SetNumberOfComponents(3);
737   }
738   vtkDoubleArray* cellVectors = nullptr;
739   vtkDoubleArray* vorticity = nullptr;
740   vtkDoubleArray* rotation = nullptr;
741   vtkDoubleArray* angularVel = nullptr;
742   if (this->ComputeVorticity)
743   {
744     cellVectors = vtkDoubleArray::New();
745     cellVectors->SetNumberOfComponents(3);
746     cellVectors->Allocate(3 * VTK_CELL_SIZE);
747 
748     vorticity = vtkDoubleArray::New();
749     vorticity->SetName("Vorticity");
750     vorticity->SetNumberOfComponents(3);
751 
752     rotation = vtkDoubleArray::New();
753     rotation->SetName("Rotation");
754 
755     angularVel = vtkDoubleArray::New();
756     angularVel->SetName("AngularVelocity");
757   }
758 
759   // We will interpolate all point attributes of the input on each point of
760   // the output (unless they are turned off). Note that we are using only
761   // the first input, if there are more than one, the attributes have to match.
762   //
763   // Note: We have to use a specific value (safe to employ the maximum number
764   //       of steps) as the size of the initial memory allocation here. The
765   //       use of the default argument might incur a crash problem (due to
766   //       "insufficient memory") in the parallel mode. This is the case when
767   //       a streamline intensely shuttles between two processes in an exactly
768   //       interleaving fashion --- only one point is produced on each process
769   //       (and actually two points, after point duplication, are saved to a
770   //       vtkPolyData in vtkDistributedStreamTracer::NoBlockProcessTask) and
771   //       as a consequence a large number of such small vtkPolyData objects
772   //       are needed to represent a streamline, consuming up the memory before
773   //       the intermediate memory is timely released.
774   outputPD->InterpolateAllocate(input0Data, this->MaximumNumberOfSteps);
775 
776   vtkIdType numPtsTotal = 0;
777   double velocity[3];
778 
779   int shouldAbort = 0;
780 
781   for (int currentLine = 0; currentLine < numLines; currentLine++)
782   {
783     double progress = static_cast<double>(currentLine) / numLines;
784     this->UpdateProgress(progress);
785 
786     switch (integrationDirections->GetValue(currentLine))
787     {
788       case FORWARD:
789         direction = 1;
790         break;
791       case BACKWARD:
792         direction = -1;
793         break;
794     }
795 
796     // temporary variables used in the integration
797     double point1[3], point2[3], pcoords[3], vort[3], omega;
798     vtkIdType index, numPts = 0;
799 
800     // Clear the last cell to avoid starting a search from
801     // the last point in the streamline
802     func->ClearLastCellId();
803 
804     // Initial point
805     seedSource->GetTuple(seedIds->GetId(currentLine), point1);
806     memcpy(point2, point1, 3 * sizeof(double));
807     if (!func->FunctionValues(point1, velocity))
808     {
809       continue;
810     }
811 
812     if (propagation >= this->MaximumPropagation || numSteps > this->MaximumNumberOfSteps)
813     {
814       continue;
815     }
816 
817     numPts++;
818     numPtsTotal++;
819     vtkIdType nextPoint = outputPoints->InsertNextPoint(point1);
820     double lastInsertedPoint[3];
821     outputPoints->GetPoint(nextPoint, lastInsertedPoint);
822     time->InsertNextValue(integrationTime);
823 
824     // We will always pass an arc-length step size to the integrator.
825     // If the user specifies a step size in cell length unit, we will
826     // have to convert it to arc length.
827     IntervalInformation stepSize; // either positive or negative
828     stepSize.Unit = LENGTH_UNIT;
829     stepSize.Interval = 0;
830     IntervalInformation aStep; // always positive
831     aStep.Unit = LENGTH_UNIT;
832     double step, minStep = 0, maxStep = 0;
833     double stepTaken;
834     double speed;
835     double cellLength;
836     int retVal = OUT_OF_LENGTH, tmp;
837 
838     // Make sure we use the dataset found by the vtkAbstractInterpolatedVelocityField
839     input = func->GetLastDataSet();
840     inputPD = input->GetPointData();
841     inVectors = input->GetAttributesAsFieldData(vecType)->GetArray(vecName);
842     // Convert intervals to arc-length unit
843     input->GetCell(func->GetLastCellId(), cell);
844     cellLength = sqrt(static_cast<double>(cell->GetLength2()));
845     speed = vtkMath::Norm(velocity);
846     // Never call conversion methods if speed == 0
847     if (speed != 0.0)
848     {
849       this->ConvertIntervals(stepSize.Interval, minStep, maxStep, direction, cellLength);
850     }
851 
852     // Interpolate all point attributes on first point
853     func->GetLastWeights(weights);
854     InterpolatePoint(
855       outputPD, inputPD, nextPoint, cell->PointIds, weights, this->HasMatchingPointAttributes);
856     // handle both point and cell velocity attributes.
857     vtkDataArray* outputVelocityVectors = outputPD->GetArray(vecName);
858     if (vecType != vtkDataObject::POINT)
859     {
860       velocityVectors->InsertNextTuple(velocity);
861       outputVelocityVectors = velocityVectors;
862     }
863 
864     // Compute vorticity if required
865     // This can be used later for streamribbon generation.
866     if (this->ComputeVorticity)
867     {
868       if (vecType == vtkDataObject::POINT)
869       {
870         inVectors->GetTuples(cell->PointIds, cellVectors);
871         func->GetLastLocalCoordinates(pcoords);
872         vtkStreamTracer::CalculateVorticity(cell, pcoords, cellVectors, vort);
873       }
874       else
875       {
876         vort[0] = 0;
877         vort[1] = 0;
878         vort[2] = 0;
879       }
880       vorticity->InsertNextTuple(vort);
881       // rotation
882       // local rotation = vorticity . unit tangent ( i.e. velocity/speed )
883       if (speed != 0.0)
884       {
885         omega = vtkMath::Dot(vort, velocity);
886         omega /= speed;
887         omega *= this->RotationScale;
888       }
889       else
890       {
891         omega = 0.0;
892       }
893       angularVel->InsertNextValue(omega);
894       rotation->InsertNextValue(0.0);
895     }
896 
897     double error = 0;
898 
899     // Integrate until the maximum propagation length is reached,
900     // maximum number of steps is reached or until a boundary is encountered.
901     // Begin Integration
902     while (propagation < this->MaximumPropagation)
903     {
904 
905       if (numSteps > this->MaximumNumberOfSteps)
906       {
907         retVal = OUT_OF_STEPS;
908         break;
909       }
910 
911       bool endIntegration = false;
912       for (std::size_t i = 0; i < this->CustomTerminationCallback.size(); ++i)
913       {
914         if (this->CustomTerminationCallback[i](
915               this->CustomTerminationClientData[i], outputPoints, outputVelocityVectors, direction))
916         {
917           retVal = this->CustomReasonForTermination[i];
918           endIntegration = true;
919           break;
920         }
921       }
922       if (endIntegration)
923       {
924         break;
925       }
926 
927       if (numSteps++ % 1000 == 1)
928       {
929         progress = (currentLine + propagation / this->MaximumPropagation) / numLines;
930         this->UpdateProgress(progress);
931 
932         if (this->GetAbortExecute())
933         {
934           shouldAbort = 1;
935           break;
936         }
937       }
938 
939       // Never call conversion methods if speed == 0
940       if ((speed == 0) || (speed <= this->TerminalSpeed))
941       {
942         retVal = STAGNATION;
943         break;
944       }
945 
946       // If, with the next step, propagation will be larger than
947       // max, reduce it so that it is (approximately) equal to max.
948       aStep.Interval = fabs(stepSize.Interval);
949 
950       if ((propagation + aStep.Interval) > this->MaximumPropagation)
951       {
952         aStep.Interval = this->MaximumPropagation - propagation;
953         if (stepSize.Interval >= 0)
954         {
955           stepSize.Interval = this->ConvertToLength(aStep, cellLength);
956         }
957         else
958         {
959           stepSize.Interval = this->ConvertToLength(aStep, cellLength) * (-1.0);
960         }
961         maxStep = stepSize.Interval;
962       }
963       this->LastUsedStepSize = stepSize.Interval;
964 
965       // Calculate the next step using the integrator provided
966       // Break if the next point is out of bounds.
967       func->SetNormalizeVector(true);
968       tmp = integrator->ComputeNextStep(point1, point2, 0, stepSize.Interval, stepTaken, minStep,
969         maxStep, this->MaximumError, error);
970       func->SetNormalizeVector(false);
971       if (tmp != 0)
972       {
973         retVal = tmp;
974         memcpy(lastPoint, point2, 3 * sizeof(double));
975         break;
976       }
977 
978       // This is the next starting point
979       if (this->SurfaceStreamlines && surfaceFunc != nullptr)
980       {
981         if (surfaceFunc->SnapPointOnCell(point2, point1) != 1)
982         {
983           retVal = OUT_OF_DOMAIN;
984           memcpy(lastPoint, point2, 3 * sizeof(double));
985           break;
986         }
987       }
988       else
989       {
990         for (int i = 0; i < 3; i++)
991         {
992           point1[i] = point2[i];
993         }
994       }
995 
996       // Interpolate the velocity at the next point
997       if (!func->FunctionValues(point2, velocity))
998       {
999         retVal = OUT_OF_DOMAIN;
1000         memcpy(lastPoint, point2, 3 * sizeof(double));
1001         break;
1002       }
1003 
1004       // It is not enough to use the starting point for stagnation calculation
1005       // Use average speed to check if it is below stagnation threshold
1006       double speed2 = vtkMath::Norm(velocity);
1007       if ((speed + speed2) / 2 <= this->TerminalSpeed)
1008       {
1009         retVal = STAGNATION;
1010         break;
1011       }
1012 
1013       integrationTime += stepTaken / speed;
1014       // Calculate propagation (using the same units as MaximumPropagation
1015       propagation += fabs(stepSize.Interval);
1016 
1017       // Make sure we use the dataset found by the vtkAbstractInterpolatedVelocityField
1018       input = func->GetLastDataSet();
1019       inputPD = input->GetPointData();
1020       inVectors = input->GetAttributesAsFieldData(vecType)->GetArray(vecName);
1021 
1022       // Calculate cell length and speed to be used in unit conversions
1023       input->GetCell(func->GetLastCellId(), cell);
1024       cellLength = sqrt(static_cast<double>(cell->GetLength2()));
1025       speed = speed2;
1026 
1027       // Check if conversion to float will produce a point in same place
1028       float convertedPoint[3];
1029       for (int i = 0; i < 3; i++)
1030       {
1031         convertedPoint[i] = point1[i];
1032       }
1033       if (lastInsertedPoint[0] != convertedPoint[0] || lastInsertedPoint[1] != convertedPoint[1] ||
1034         lastInsertedPoint[2] != convertedPoint[2])
1035       {
1036         // Point is valid. Insert it.
1037         numPts++;
1038         numPtsTotal++;
1039         nextPoint = outputPoints->InsertNextPoint(point1);
1040         outputPoints->GetPoint(nextPoint, lastInsertedPoint);
1041         time->InsertNextValue(integrationTime);
1042 
1043         // Interpolate all point attributes on current point
1044         func->GetLastWeights(weights);
1045         InterpolatePoint(
1046           outputPD, inputPD, nextPoint, cell->PointIds, weights, this->HasMatchingPointAttributes);
1047 
1048         if (vecType != vtkDataObject::POINT)
1049         {
1050           velocityVectors->InsertNextTuple(velocity);
1051         }
1052         // Compute vorticity if required
1053         // This can be used later for streamribbon generation.
1054         if (this->ComputeVorticity)
1055         {
1056           if (vecType == vtkDataObject::POINT)
1057           {
1058             inVectors->GetTuples(cell->PointIds, cellVectors);
1059             func->GetLastLocalCoordinates(pcoords);
1060             vtkStreamTracer::CalculateVorticity(cell, pcoords, cellVectors, vort);
1061           }
1062           else
1063           {
1064             vort[0] = 0;
1065             vort[1] = 0;
1066             vort[2] = 0;
1067           }
1068           vorticity->InsertNextTuple(vort);
1069           // rotation
1070           // angular velocity = vorticity . unit tangent ( i.e. velocity/speed )
1071           // rotation = sum ( angular velocity * stepSize )
1072           omega = vtkMath::Dot(vort, velocity);
1073           omega /= speed;
1074           omega *= this->RotationScale;
1075           index = angularVel->InsertNextValue(omega);
1076           rotation->InsertNextValue(rotation->GetValue(index - 1) +
1077             (angularVel->GetValue(index - 1) + omega) / 2 *
1078               (integrationTime - time->GetValue(index - 1)));
1079         }
1080       }
1081 
1082       // Never call conversion methods if speed == 0
1083       if ((speed == 0) || (speed <= this->TerminalSpeed))
1084       {
1085         retVal = STAGNATION;
1086         break;
1087       }
1088 
1089       // Convert all intervals to arc length
1090       this->ConvertIntervals(step, minStep, maxStep, direction, cellLength);
1091 
1092       // If the solver is adaptive and the next step size (stepSize.Interval)
1093       // that the solver wants to use is smaller than minStep or larger
1094       // than maxStep, re-adjust it. This has to be done every step
1095       // because minStep and maxStep can change depending on the cell
1096       // size (unless it is specified in arc-length unit)
1097       if (integrator->IsAdaptive())
1098       {
1099         if (fabs(stepSize.Interval) < fabs(minStep))
1100         {
1101           stepSize.Interval = fabs(minStep) * stepSize.Interval / fabs(stepSize.Interval);
1102         }
1103         else if (fabs(stepSize.Interval) > fabs(maxStep))
1104         {
1105           stepSize.Interval = fabs(maxStep) * stepSize.Interval / fabs(stepSize.Interval);
1106         }
1107       }
1108       else
1109       {
1110         stepSize.Interval = step;
1111       }
1112     }
1113 
1114     if (shouldAbort)
1115     {
1116       break;
1117     }
1118 
1119     if (numPts > 1)
1120     {
1121       outputLines->InsertNextCell(numPts);
1122       for (int i = numPtsTotal - numPts; i < numPtsTotal; i++)
1123       {
1124         outputLines->InsertCellPoint(i);
1125       }
1126       retVals->InsertNextValue(retVal);
1127       sids->InsertNextValue(seedIds->GetId(currentLine));
1128     }
1129 
1130     // Initialize these to 0 before starting the next line.
1131     // The values passed in the function call are only used
1132     // for the first line.
1133     inPropagation = propagation;
1134     inNumSteps = numSteps;
1135     inIntegrationTime = integrationTime;
1136 
1137     propagation = 0;
1138     numSteps = 0;
1139     integrationTime = 0;
1140   }
1141 
1142   if (!shouldAbort)
1143   {
1144     // Create the output polyline
1145     output->SetPoints(outputPoints);
1146     outputPD->AddArray(time);
1147     if (vecType != vtkDataObject::POINT)
1148     {
1149       outputPD->AddArray(velocityVectors);
1150     }
1151     if (vorticity)
1152     {
1153       outputPD->AddArray(vorticity);
1154       outputPD->AddArray(rotation);
1155       outputPD->AddArray(angularVel);
1156     }
1157 
1158     vtkIdType numPts = outputPoints->GetNumberOfPoints();
1159     if (numPts > 1)
1160     {
1161       // Assign geometry and attributes
1162       output->SetLines(outputLines);
1163       if (this->GenerateNormalsInIntegrate)
1164       {
1165         this->GenerateNormals(output, nullptr, vecName);
1166       }
1167 
1168       outputCD->AddArray(retVals);
1169       outputCD->AddArray(sids);
1170     }
1171   }
1172 
1173   if (vorticity)
1174   {
1175     vorticity->Delete();
1176     rotation->Delete();
1177     angularVel->Delete();
1178   }
1179 
1180   if (cellVectors)
1181   {
1182     cellVectors->Delete();
1183   }
1184   retVals->Delete();
1185   sids->Delete();
1186 
1187   outputPoints->Delete();
1188   outputLines->Delete();
1189 
1190   time->Delete();
1191 
1192   integrator->Delete();
1193   cell->Delete();
1194 
1195   delete[] weights;
1196 
1197   output->Squeeze();
1198 }
1199 
1200 //------------------------------------------------------------------------------
GenerateNormals(vtkPolyData * output,double * firstNormal,const char * vecName)1201 void vtkStreamTracer::GenerateNormals(vtkPolyData* output, double* firstNormal, const char* vecName)
1202 {
1203   // Useful pointers
1204   vtkDataSetAttributes* outputPD = output->GetPointData();
1205 
1206   vtkPoints* outputPoints = output->GetPoints();
1207   vtkCellArray* outputLines = output->GetLines();
1208 
1209   vtkDataArray* rotation = outputPD->GetArray("Rotation");
1210 
1211   vtkIdType numPts = outputPoints->GetNumberOfPoints();
1212   if (numPts > 1)
1213   {
1214     if (this->ComputeVorticity)
1215     {
1216       vtkPolyLine* lineNormalGenerator = vtkPolyLine::New();
1217       vtkDoubleArray* normals = vtkDoubleArray::New();
1218       normals->SetNumberOfComponents(3);
1219       normals->SetNumberOfTuples(numPts);
1220       // Make sure the normals are initialized in case
1221       // GenerateSlidingNormals() fails and returns before
1222       // creating all normals
1223       for (vtkIdType idx = 0; idx < numPts; idx++)
1224       {
1225         normals->SetTuple3(idx, 1, 0, 0);
1226       }
1227 
1228       vtkPolyLine::GenerateSlidingNormals(outputPoints, outputLines, normals, firstNormal);
1229       lineNormalGenerator->Delete();
1230 
1231       vtkIdType i;
1232       int j;
1233       double normal[3], local1[3], local2[3], theta, costheta, sintheta, length;
1234       double velocity[3];
1235       normals->SetName("Normals");
1236       vtkDataArray* newVectors = outputPD->GetVectors(vecName);
1237       for (i = 0; i < numPts; i++)
1238       {
1239         normals->GetTuple(i, normal);
1240         if (newVectors == nullptr || newVectors->GetNumberOfTuples() != numPts)
1241         { // This should never happen.
1242           vtkErrorMacro("Bad velocity array.");
1243           return;
1244         }
1245         newVectors->GetTuple(i, velocity);
1246         // obtain two unit orthogonal vectors on the plane perpendicular to
1247         // the streamline
1248         for (j = 0; j < 3; j++)
1249         {
1250           local1[j] = normal[j];
1251         }
1252         length = vtkMath::Normalize(local1);
1253         vtkMath::Cross(local1, velocity, local2);
1254         vtkMath::Normalize(local2);
1255         // Rotate the normal with theta
1256         rotation->GetTuple(i, &theta);
1257         costheta = cos(theta);
1258         sintheta = sin(theta);
1259         for (j = 0; j < 3; j++)
1260         {
1261           normal[j] = length * (costheta * local1[j] + sintheta * local2[j]);
1262         }
1263         normals->SetTuple(i, normal);
1264       }
1265       outputPD->AddArray(normals);
1266       outputPD->SetActiveAttribute("Normals", vtkDataSetAttributes::VECTORS);
1267       normals->Delete();
1268     }
1269   }
1270 }
1271 
1272 //------------------------------------------------------------------------------
1273 // This is used by sub-classes in certain situations. It
1274 // does a lot less (for example, does not compute attributes)
1275 // than Integrate.
SimpleIntegrate(double seed[3],double lastPoint[3],double stepSize,vtkAbstractInterpolatedVelocityField * func)1276 double vtkStreamTracer::SimpleIntegrate(
1277   double seed[3], double lastPoint[3], double stepSize, vtkAbstractInterpolatedVelocityField* func)
1278 {
1279   vtkIdType numSteps = 0;
1280   vtkIdType maxSteps = 20;
1281   double error = 0;
1282   double stepTaken = 0;
1283   double point1[3], point2[3];
1284   double velocity[3];
1285   double speed;
1286   int stepResult;
1287 
1288   (void)seed; // Seed is not used
1289 
1290   memcpy(point1, lastPoint, 3 * sizeof(double));
1291 
1292   // Create a new integrator, the type is the same as Integrator
1293   vtkInitialValueProblemSolver* integrator = this->GetIntegrator()->NewInstance();
1294   integrator->SetFunctionSet(func);
1295 
1296   while (true)
1297   {
1298 
1299     if (numSteps++ > maxSteps)
1300     {
1301       break;
1302     }
1303 
1304     // Calculate the next step using the integrator provided
1305     // Break if the next point is out of bounds.
1306     func->SetNormalizeVector(true);
1307     double tmpStepTaken = 0;
1308     stepResult =
1309       integrator->ComputeNextStep(point1, point2, 0, stepSize, tmpStepTaken, 0, 0, 0, error);
1310     stepTaken += tmpStepTaken;
1311     func->SetNormalizeVector(false);
1312     if (stepResult != 0)
1313     {
1314       memcpy(lastPoint, point2, 3 * sizeof(double));
1315       break;
1316     }
1317 
1318     // This is the next starting point
1319     for (int i = 0; i < 3; i++)
1320     {
1321       point1[i] = point2[i];
1322     }
1323 
1324     // Interpolate the velocity at the next point
1325     if (!func->FunctionValues(point2, velocity))
1326     {
1327       memcpy(lastPoint, point2, 3 * sizeof(double));
1328       break;
1329     }
1330 
1331     speed = vtkMath::Norm(velocity);
1332 
1333     // Never call conversion methods if speed == 0
1334     if ((speed == 0) || (speed <= this->TerminalSpeed))
1335     {
1336       break;
1337     }
1338 
1339     memcpy(point1, point2, 3 * sizeof(double));
1340     // End Integration
1341   }
1342 
1343   integrator->Delete();
1344   return stepTaken;
1345 }
1346 
1347 //------------------------------------------------------------------------------
FillInputPortInformation(int port,vtkInformation * info)1348 int vtkStreamTracer::FillInputPortInformation(int port, vtkInformation* info)
1349 {
1350   if (port == 0)
1351   {
1352     info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataObject");
1353   }
1354   else if (port == 1)
1355   {
1356     info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet");
1357     info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1);
1358   }
1359   return 1;
1360 }
1361 
1362 //------------------------------------------------------------------------------
AddCustomTerminationCallback(CustomTerminationCallbackType callback,void * clientdata,int reasonForTermination)1363 void vtkStreamTracer::AddCustomTerminationCallback(
1364   CustomTerminationCallbackType callback, void* clientdata, int reasonForTermination)
1365 {
1366   this->CustomTerminationCallback.push_back(callback);
1367   this->CustomTerminationClientData.push_back(clientdata);
1368   this->CustomReasonForTermination.push_back(reasonForTermination);
1369   this->Modified();
1370 }
1371 
1372 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)1373 void vtkStreamTracer::PrintSelf(ostream& os, vtkIndent indent)
1374 {
1375   this->Superclass::PrintSelf(os, indent);
1376   os << indent << "Start position: " << this->StartPosition[0] << " " << this->StartPosition[1]
1377      << " " << this->StartPosition[2] << endl;
1378   os << indent << "Terminal speed: " << this->TerminalSpeed << endl;
1379 
1380   os << indent << "Maximum propagation: " << this->MaximumPropagation << " unit: length." << endl;
1381 
1382   os << indent << "Integration step unit: "
1383      << ((this->IntegrationStepUnit == LENGTH_UNIT) ? "length." : "cell length.") << endl;
1384 
1385   os << indent << "Initial integration step: " << this->InitialIntegrationStep << endl;
1386 
1387   os << indent << "Minimum integration step: " << this->MinimumIntegrationStep << endl;
1388 
1389   os << indent << "Maximum integration step: " << this->MaximumIntegrationStep << endl;
1390 
1391   os << indent << "Integration direction: ";
1392   switch (this->IntegrationDirection)
1393   {
1394     case FORWARD:
1395       os << "forward.";
1396       break;
1397     case BACKWARD:
1398       os << "backward.";
1399       break;
1400     case BOTH:
1401       os << "both directions.";
1402       break;
1403   }
1404   os << endl;
1405 
1406   os << indent << "Integrator: " << this->Integrator << endl;
1407   os << indent << "Maximum error: " << this->MaximumError << endl;
1408   os << indent << "Maximum number of steps: " << this->MaximumNumberOfSteps << endl;
1409   os << indent << "Vorticity computation: " << (this->ComputeVorticity ? " On" : " Off") << endl;
1410   os << indent << "Rotation scale: " << this->RotationScale << endl;
1411 }
1412 
1413 //------------------------------------------------------------------------------
CreateDefaultExecutive()1414 vtkExecutive* vtkStreamTracer::CreateDefaultExecutive()
1415 {
1416   return vtkCompositeDataPipeline::New();
1417 }
1418