// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkEvenlySpacedStreamlines2D.h" #include "vtkAMRInterpolatedVelocityField.h" #include "vtkAbstractInterpolatedVelocityField.h" #include "vtkAppendPolyData.h" #include "vtkCellData.h" #include "vtkCellLocatorStrategy.h" #include "vtkClosestPointStrategy.h" #include "vtkCompositeDataIterator.h" #include "vtkCompositeDataPipeline.h" #include "vtkCompositeDataSet.h" #include "vtkCompositeInterpolatedVelocityField.h" #include "vtkDataSetAttributes.h" #include "vtkDoubleArray.h" #include "vtkGenericCell.h" #include "vtkImageData.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkIntArray.h" #include "vtkMath.h" #include "vtkMathUtilities.h" #include "vtkModifiedBSPTree.h" #include "vtkMultiBlockDataSet.h" #include "vtkNew.h" #include "vtkObjectFactory.h" #include "vtkOverlappingAMR.h" #include "vtkPointData.h" #include "vtkPointSet.h" #include "vtkPolyData.h" #include "vtkPolyDataCollection.h" #include "vtkPolyLine.h" #include "vtkRungeKutta2.h" #include "vtkRungeKutta4.h" #include "vtkSmartPointer.h" #include "vtkStreamTracer.h" #include #include #include #include VTK_ABI_NAMESPACE_BEGIN vtkObjectFactoryNewMacro(vtkEvenlySpacedStreamlines2D); vtkCxxSetObjectMacro(vtkEvenlySpacedStreamlines2D, Integrator, vtkInitialValueProblemSolver); vtkCxxSetObjectMacro( vtkEvenlySpacedStreamlines2D, InterpolatorPrototype, vtkAbstractInterpolatedVelocityField); //------------------------------------------------------------------------------ vtkEvenlySpacedStreamlines2D::vtkEvenlySpacedStreamlines2D() { this->Integrator = vtkRungeKutta2::New(); for (int i = 0; i < 3; i++) { this->StartPosition[i] = 0.0; } this->IntegrationStepUnit = vtkStreamTracer::CELL_LENGTH_UNIT; this->InitialIntegrationStep = 0.5; this->ClosedLoopMaximumDistance = 1.0e-6; this->ClosedLoopMaximumDistanceArcLength = 1.0e-6; this->LoopAngle = 0.349066; // 20 degrees in radians this->MaximumNumberOfSteps = 2000; this->MinimumNumberOfLoopPoints = 4; this->DirectionStart = 0; // invalid integration direction so that we trigger a change the first time this->PreviousDirection = 0; this->TerminalSpeed = 1.0E-12; this->ComputeVorticity = true; this->InterpolatorPrototype = nullptr; // by default process active point vectors this->SeparatingDistance = 1; this->SeparatingDistanceArcLength = 1; this->SeparatingDistanceRatio = 0.5; this->SuperposedGrid = vtkImageData::New(); this->Streamlines = vtkPolyDataCollection::New(); // by default process active point vectors this->SetInputArrayToProcess( 0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, vtkDataSetAttributes::VECTORS); } //------------------------------------------------------------------------------ vtkEvenlySpacedStreamlines2D::~vtkEvenlySpacedStreamlines2D() { this->SetIntegrator(nullptr); this->SetInterpolatorPrototype(nullptr); this->SuperposedGrid->Delete(); this->Streamlines->Delete(); } //------------------------------------------------------------------------------ int vtkEvenlySpacedStreamlines2D::RequestData(vtkInformation* vtkNotUsed(request), vtkInformationVector** inputVector, vtkInformationVector* outputVector) { vtkInformation* inInfo = inputVector[0]->GetInformationObject(0); vtkInformation* outInfo = outputVector->GetInformationObject(0); if (!this->SetupOutput(inInfo, outInfo)) { return 0; } double bounds[6]; vtkEvenlySpacedStreamlines2D::GetBounds(this->InputData, bounds); if (!vtkMathUtilities::FuzzyCompare(bounds[4], bounds[5])) { this->InputData->UnRegister(this); vtkErrorMacro("vtkEvenlySpacedStreamlines2D does not support planes not aligned with XY."); return 0; } std::array v = { { bounds[1] - bounds[0], bounds[3] - bounds[2], bounds[5] - bounds[4] } }; double length = vtkMath::Norm(v.data()); vtkPolyData* output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); // compute the separating distance arc length double cellLength = 0; if (!this->ComputeCellLength(&cellLength)) { this->InputData->UnRegister(this); return 0; } this->SeparatingDistanceArcLength = this->ConvertToLength(this->SeparatingDistance, this->IntegrationStepUnit, cellLength); this->ClosedLoopMaximumDistanceArcLength = this->ConvertToLength(this->ClosedLoopMaximumDistance, this->IntegrationStepUnit, cellLength); this->InitializeSuperposedGrid(bounds); auto streamTracer = vtkSmartPointer::New(); streamTracer->SetInputDataObject(this->InputData); streamTracer->SetMaximumPropagation(length); streamTracer->SetMaximumNumberOfSteps(this->MaximumNumberOfSteps); streamTracer->SetIntegrationDirection(vtkStreamTracer::BOTH); streamTracer->SetInputArrayToProcess(0, this->GetInputArrayInformation(0)); streamTracer->SetStartPosition(this->StartPosition); streamTracer->SetTerminalSpeed(this->TerminalSpeed); streamTracer->SetInitialIntegrationStep(this->InitialIntegrationStep); streamTracer->SetIntegrationStepUnit(this->IntegrationStepUnit); streamTracer->SetIntegrator(this->Integrator); streamTracer->SetComputeVorticity(this->ComputeVorticity); streamTracer->SetInterpolatorPrototype(this->InterpolatorPrototype); // we end streamlines after one loop iteration streamTracer->AddCustomTerminationCallback(&vtkEvenlySpacedStreamlines2D::IsStreamlineLooping, this, vtkStreamTracer::FIXED_REASONS_FOR_TERMINATION_COUNT); streamTracer->SetContainerAlgorithm(this); streamTracer->Update(); auto streamline = vtkSmartPointer::New(); streamline->ShallowCopy(streamTracer->GetOutput()); this->AddToAllPoints(streamline); auto append = vtkSmartPointer::New(); append->UserManagedInputsOn(); append->SetNumberOfInputs(2); output->ShallowCopy(streamline); append->SetContainerAlgorithm(this); int currentSeedId = 1; int processedSeedId = 0; this->Streamlines->RemoveAllItems(); this->Streamlines->AddItem(streamline); // we also end streamlines when they are close to other streamlines streamTracer->AddCustomTerminationCallback( &vtkEvenlySpacedStreamlines2D::IsStreamlineTooCloseToOthers, this, vtkStreamTracer::FIXED_REASONS_FOR_TERMINATION_COUNT + 1); const char* velocityName = this->GetInputArrayToProcessName(); double deltaOne = this->SeparatingDistanceArcLength / 1000; double delta[3] = { deltaOne, deltaOne, deltaOne }; int maxNumberOfItems = 0; float lastProgress = 0.0; while (this->Streamlines->GetNumberOfItems()) { int numberOfItems = this->Streamlines->GetNumberOfItems(); maxNumberOfItems = std::max(numberOfItems, maxNumberOfItems); if (processedSeedId % 10 == 0) { float progress = (static_cast(maxNumberOfItems) - numberOfItems) / maxNumberOfItems; if (progress > lastProgress) { this->UpdateProgress(progress); if (this->CheckAbort()) { break; } lastProgress = progress; } } streamline = vtkPolyData::SafeDownCast(this->Streamlines->GetItemAsObject(0)); vtkDataArray* velocity = streamline->GetPointData()->GetArray(velocityName); for (vtkIdType pointId = 0; pointId < streamline->GetNumberOfPoints(); ++pointId) { // generate 2 new seeds for every streamline point double newSeedVector[3]; double normal[3] = { 0, 0, 1 }; vtkMath::Cross(normal, velocity->GetTuple(pointId), newSeedVector); // floating point errors move newSeedVector out of XY plane. newSeedVector[2] = 0; vtkMath::Normalize(newSeedVector); vtkMath::MultiplyScalar(newSeedVector, this->SeparatingDistanceArcLength); double point[3]; streamline->GetPoint(pointId, point); std::array, 2> newSeeds; vtkMath::Add(point, newSeedVector, newSeeds[0].data()); vtkMath::Subtract(point, newSeedVector, newSeeds[1].data()); for (auto newSeed : newSeeds) { if (vtkMath::PointIsWithinBounds(newSeed.data(), bounds, delta) && !this->ForEachCell(newSeed.data(), &vtkEvenlySpacedStreamlines2D::IsTooClose)) { streamTracer->SetStartPosition(newSeed.data()); streamTracer->Update(); auto newStreamline = vtkSmartPointer::New(); newStreamline->ShallowCopy(streamTracer->GetOutput()); vtkIntArray* seedIds = vtkIntArray::SafeDownCast(newStreamline->GetCellData()->GetArray("SeedIds")); for (int cellId = 0; cellId < newStreamline->GetNumberOfCells(); ++cellId) { seedIds->SetValue(cellId, currentSeedId); } currentSeedId++; this->AddToAllPoints(newStreamline); append->SetInputDataByNumber(0, output); append->SetInputDataByNumber(1, newStreamline); append->Update(); output->ShallowCopy(append->GetOutput()); this->Streamlines->AddItem(newStreamline); } } } this->Streamlines->RemoveItem(0); ++processedSeedId; } this->InputData->UnRegister(this); return 1; } //------------------------------------------------------------------------------ int vtkEvenlySpacedStreamlines2D::ComputeCellLength(double* cellLength) { vtkAbstractInterpolatedVelocityField* func; int maxCellSize = 0; if (this->CheckInputs(func, &maxCellSize) != VTK_OK) { if (func) { func->Delete(); } return 0; } vtkDataSet* input; auto cell = vtkSmartPointer::New(); double velocity[3]; // access the start position if (!func->FunctionValues(this->StartPosition, velocity)) { func->Delete(); return 0; } // Make sure we use the dataset found by the vtkAbstractInterpolatedVelocityField input = func->GetLastDataSet(); input->GetCell(func->GetLastCellId(), cell); *cellLength = sqrt(cell->GetLength2()); func->Delete(); return 1; } //------------------------------------------------------------------------------ int vtkEvenlySpacedStreamlines2D::FillInputPortInformation(int port, vtkInformation* info) { if (port == 0) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataObject"); } return 1; } //------------------------------------------------------------------------------ bool vtkEvenlySpacedStreamlines2D::IsStreamlineTooCloseToOthers( void* clientdata, vtkPoints* points, vtkDataArray* velocity, int direction) { (void)velocity; (void)direction; vtkEvenlySpacedStreamlines2D* This = static_cast(clientdata); vtkIdType count = points->GetNumberOfPoints(); double point[3]; points->GetPoint(count - 1, point); return This->ForEachCell(point, &vtkEvenlySpacedStreamlines2D::IsTooClose); } bool vtkEvenlySpacedStreamlines2D::IsStreamlineLooping( void* clientdata, vtkPoints* points, vtkDataArray* velocity, int direction) { vtkEvenlySpacedStreamlines2D* This = static_cast(clientdata); vtkIdType p0 = points->GetNumberOfPoints() - 1; // reinitialize when changing direction if (direction != This->PreviousDirection) { This->InitializePoints(This->CurrentPoints); This->InitializeMinPointIds(); This->PreviousDirection = direction; This->DirectionStart = p0; } double p0Point[3]; points->GetPoint(p0, p0Point); int ijk[3] = { 0, 0, 0 }; ijk[0] = floor(p0Point[0] / This->SeparatingDistanceArcLength); ijk[1] = floor(p0Point[1] / This->SeparatingDistanceArcLength); vtkIdType cellId = This->SuperposedGrid->ComputeCellId(&ijk[0]); bool retVal = This->ForEachCell( p0Point, &vtkEvenlySpacedStreamlines2D::IsLooping, points, velocity, direction); // add the point to the list This->CurrentPoints[cellId].push_back(p0); This->MinPointIds[cellId] = std::min(p0, This->MinPointIds[cellId]); return retVal; } //------------------------------------------------------------------------------ template bool vtkEvenlySpacedStreamlines2D::ForEachCell( double* point, CellCheckerType checker, vtkPoints* points, vtkDataArray* velocity, int direction) { // point current cell int ijk[3] = { 0, 0, 0 }; ijk[0] = floor(point[0] / this->SeparatingDistanceArcLength); ijk[1] = floor(point[1] / this->SeparatingDistanceArcLength); vtkIdType cellId = this->SuperposedGrid->ComputeCellId(&ijk[0]); if ((this->*checker)(point, cellId, points, velocity, direction)) { return true; } // and check cells around the current cell std::array, 8> around = { { { { ijk[0] - 1, ijk[1] + 1, ijk[2] } }, { { ijk[0], ijk[1] + 1, ijk[2] } }, { { ijk[0] + 1, ijk[1] + 1, ijk[2] } }, { { ijk[0] - 1, ijk[1], ijk[2] } }, { { ijk[0] + 1, ijk[1], ijk[2] } }, { { ijk[0] - 1, ijk[1] - 1, ijk[2] } }, { { ijk[0], ijk[1] - 1, ijk[2] } }, { { ijk[0] + 1, ijk[1] - 1, ijk[2] } }, } }; int extent[6]; this->SuperposedGrid->GetExtent(extent); for (auto cellPos : around) { cellId = this->SuperposedGrid->ComputeCellId(cellPos.data()); if (cellPos[0] >= extent[0] && cellPos[0] < extent[1] && cellPos[1] >= extent[2] && cellPos[1] < extent[3] && (this->*checker)(point, cellId, points, velocity, direction)) { return true; } } return false; } //------------------------------------------------------------------------------ bool vtkEvenlySpacedStreamlines2D::IsLooping( double* point, vtkIdType cellId, vtkPoints* points, vtkDataArray* velocity, int direction) { (void)point; // do we have enough points to form a loop vtkIdType p0 = points->GetNumberOfPoints() - 1; vtkIdType minLoopPoints = std::max(vtkIdType(3), this->MinimumNumberOfLoopPoints); if (!this->CurrentPoints[cellId].empty() && p0 - this->MinPointIds[cellId] + 1 >= minLoopPoints) { vtkIdType p1 = p0 - 1; double testDistance2 = this->SeparatingDistanceArcLength * this->SeparatingDistanceArcLength * this->SeparatingDistanceRatio * this->SeparatingDistanceRatio; double maxDistance2 = this->ClosedLoopMaximumDistanceArcLength * this->ClosedLoopMaximumDistanceArcLength; for (vtkIdType q : this->CurrentPoints[cellId]) { // do we have enough points to form a loop if (p0 - q + 1 < minLoopPoints) { continue; } double p0Point[3]; points->GetPoint(p0, p0Point); double qPoint[3]; points->GetPoint(q, qPoint); double distance2 = vtkMath::Distance2BetweenPoints(p0Point, qPoint); if (distance2 <= maxDistance2) { // closed loop return true; } if (distance2 >= testDistance2) { // we might loop but points are too far. continue; } double p1Point[3]; points->GetPoint(p1, p1Point); double v1[3]; vtkMath::Subtract(p0Point, p1Point, v1); vtkMath::MultiplyScalar(v1, direction); double* qVector = velocity->GetTuple(q); if (vtkMath::Dot(qVector, v1) < cos(this->LoopAngle)) { // qVector makes a large angle with p0p1 continue; } double u0[3], u1[3]; vtkMath::Subtract(p0Point, qPoint, u0); vtkMath::MultiplyScalar(u0, direction); vtkMath::Subtract(p1Point, qPoint, u1); vtkMath::MultiplyScalar(u1, direction); if (vtkMath::Dot(u0, v1) >= 0 && vtkMath::Dot(u1, v1) >= 0) { // we found a "proponent point" See Liu et al. continue; } // the algorithm in Liu at al. has another test that determines if the // loop is closed or spiraling. We don't care about that so we skip it. return true; } } return false; } //------------------------------------------------------------------------------ template bool vtkEvenlySpacedStreamlines2D::IsTooClose( double* point, vtkIdType cellId, vtkPoints* points, vtkDataArray* velocity, int direction) { (void)points; (void)velocity; (void)direction; double testDistance2 = this->SeparatingDistanceArcLength * this->SeparatingDistanceArcLength; if (distanceType == DISTANCE_RATIO) { testDistance2 *= (this->SeparatingDistanceRatio * this->SeparatingDistanceRatio); } for (auto cellPoint : this->AllPoints[cellId]) { double distance2 = vtkMath::Distance2BetweenPoints(point, cellPoint.data()); if (distance2 < testDistance2) { return true; } } return false; } //------------------------------------------------------------------------------ int vtkEvenlySpacedStreamlines2D::GetIntegratorType() { if (!this->Integrator) { return vtkStreamTracer::NONE; } if (!strcmp(this->Integrator->GetClassName(), "vtkRungeKutta2")) { return vtkStreamTracer::RUNGE_KUTTA2; } if (!strcmp(this->Integrator->GetClassName(), "vtkRungeKutta4")) { return vtkStreamTracer::RUNGE_KUTTA4; } return vtkStreamTracer::UNKNOWN; } //------------------------------------------------------------------------------ void vtkEvenlySpacedStreamlines2D::SetInterpolatorTypeToDataSetPointLocator() { this->SetInterpolatorType( static_cast(vtkStreamTracer::INTERPOLATOR_WITH_DATASET_POINT_LOCATOR)); } void vtkEvenlySpacedStreamlines2D::SetInterpolatorTypeToCellLocator() { this->SetInterpolatorType(static_cast(vtkStreamTracer::INTERPOLATOR_WITH_CELL_LOCATOR)); } //------------------------------------------------------------------------------ void vtkEvenlySpacedStreamlines2D::SetInterpolatorType(int interpType) { vtkNew cIVF; if (interpType == vtkStreamTracer::INTERPOLATOR_WITH_CELL_LOCATOR) { // create an interpolator equipped with a cell locator vtkNew strategy; // specify the type of the cell locator attached to the interpolator vtkNew cellLocType; strategy->SetCellLocator(cellLocType); cIVF->SetFindCellStrategy(strategy); } else { // create an interpolator equipped with a point locator (by default) vtkNew strategy; cIVF->SetFindCellStrategy(strategy); } this->SetInterpolatorPrototype(cIVF); } //------------------------------------------------------------------------------ void vtkEvenlySpacedStreamlines2D::SetIntegratorType(int type) { vtkInitialValueProblemSolver* ivp = nullptr; switch (type) { case vtkStreamTracer::RUNGE_KUTTA2: ivp = vtkRungeKutta2::New(); break; case vtkStreamTracer::RUNGE_KUTTA4: ivp = vtkRungeKutta4::New(); break; default: vtkWarningMacro("Unrecognized integrator type. Keeping old one."); break; } if (ivp) { this->SetIntegrator(ivp); ivp->Delete(); } } //------------------------------------------------------------------------------ void vtkEvenlySpacedStreamlines2D::SetIntegrationStepUnit(int unit) { if (unit != vtkStreamTracer::LENGTH_UNIT && unit != vtkStreamTracer::CELL_LENGTH_UNIT) { unit = vtkStreamTracer::CELL_LENGTH_UNIT; } if (unit == this->IntegrationStepUnit) { return; } this->IntegrationStepUnit = unit; this->Modified(); } //------------------------------------------------------------------------------ double vtkEvenlySpacedStreamlines2D::ConvertToLength(double interval, int unit, double cellLength) { double retVal = 0.0; if (unit == vtkStreamTracer::LENGTH_UNIT) { retVal = interval; } else if (unit == vtkStreamTracer::CELL_LENGTH_UNIT) { retVal = interval * cellLength; } return retVal; } //------------------------------------------------------------------------------ int vtkEvenlySpacedStreamlines2D::SetupOutput(vtkInformation* inInfo, vtkInformation* outInfo) { int piece = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER()); int numPieces = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES()); vtkDataObject* input = inInfo->Get(vtkDataObject::DATA_OBJECT()); vtkCompositeDataSet* hdInput = vtkCompositeDataSet::SafeDownCast(input); vtkDataSet* dsInput = vtkDataSet::SafeDownCast(input); if (hdInput) { this->InputData = hdInput; hdInput->Register(this); return 1; } else if (dsInput) { auto mb = vtkSmartPointer::New(); mb->SetNumberOfBlocks(numPieces); mb->SetBlock(piece, dsInput); this->InputData = mb; mb->Register(this); return 1; } else { vtkErrorMacro( "This filter cannot handle input of type: " << (input ? input->GetClassName() : "(none)")); return 0; } } //------------------------------------------------------------------------------ int vtkEvenlySpacedStreamlines2D::CheckInputs( vtkAbstractInterpolatedVelocityField*& func, int* maxCellSize) { if (!this->InputData) { return VTK_ERROR; } vtkOverlappingAMR* amrData = vtkOverlappingAMR::SafeDownCast(this->InputData); vtkSmartPointer iter; iter.TakeReference(this->InputData->NewIterator()); vtkDataSet* input0 = nullptr; iter->GoToFirstItem(); while (!iter->IsDoneWithTraversal() && input0 == nullptr) { input0 = vtkDataSet::SafeDownCast(iter->GetCurrentDataObject()); iter->GoToNextItem(); } if (!input0) { return VTK_ERROR; } int vecType(0); vtkDataArray* vectors = this->GetInputArrayToProcess(0, input0, vecType); if (!vectors) { return VTK_ERROR; } // Set the function set to be integrated if (!this->InterpolatorPrototype) { if (amrData) { func = vtkAMRInterpolatedVelocityField::New(); } else { func = vtkCompositeInterpolatedVelocityField::New(); } } else { if (amrData && vtkAMRInterpolatedVelocityField::SafeDownCast(this->InterpolatorPrototype) == nullptr) { this->InterpolatorPrototype = vtkAMRInterpolatedVelocityField::New(); } func = this->InterpolatorPrototype->NewInstance(); func->CopyParameters(this->InterpolatorPrototype); } if (vtkAMRInterpolatedVelocityField::SafeDownCast(func)) { assert(amrData); vtkAMRInterpolatedVelocityField::SafeDownCast(func)->SetAMRData(amrData); if (maxCellSize) { *maxCellSize = 8; } } else if (vtkCompositeInterpolatedVelocityField::SafeDownCast(func)) { iter->GoToFirstItem(); while (!iter->IsDoneWithTraversal()) { vtkDataSet* inp = vtkDataSet::SafeDownCast(iter->GetCurrentDataObject()); if (inp) { int cellSize = inp->GetMaxCellSize(); *maxCellSize = std::max(cellSize, *maxCellSize); vtkCompositeInterpolatedVelocityField::SafeDownCast(func)->AddDataSet(inp); } iter->GoToNextItem(); } } else { assert(false); } const char* vecName = vectors->GetName(); func->SelectVectors(vecType, vecName); // This initializes / builds the data processing cache in support of threading etc. // It takes into account the input to the filter (which may be a composite dataset) // as well as any additional added datasets via AddDataSet(). func->Initialize(this->InputData); return VTK_OK; } //------------------------------------------------------------------------------ void vtkEvenlySpacedStreamlines2D::InitializeSuperposedGrid(double* bounds) { this->SuperposedGrid->SetExtent(floor(bounds[0] / this->SeparatingDistanceArcLength), ceil(bounds[1] / this->SeparatingDistanceArcLength), floor(bounds[2] / this->SeparatingDistanceArcLength), ceil(bounds[3] / this->SeparatingDistanceArcLength), 0, 0); this->SuperposedGrid->SetSpacing(this->SeparatingDistanceArcLength, this->SeparatingDistanceArcLength, this->SeparatingDistanceArcLength); this->InitializePoints(this->AllPoints); this->InitializePoints(this->CurrentPoints); } //------------------------------------------------------------------------------ template void vtkEvenlySpacedStreamlines2D::InitializePoints(T& points) { points.resize(this->SuperposedGrid->GetNumberOfCells()); for (std::size_t i = 0; i < points.size(); ++i) { points[i].clear(); } } //------------------------------------------------------------------------------ void vtkEvenlySpacedStreamlines2D::InitializeMinPointIds() { this->MinPointIds.resize(this->SuperposedGrid->GetNumberOfCells()); for (std::size_t i = 0; i < this->MinPointIds.size(); ++i) { this->MinPointIds[i] = std::numeric_limits::max(); } } //------------------------------------------------------------------------------ void vtkEvenlySpacedStreamlines2D::AddToAllPoints(vtkPolyData* streamline) { vtkPoints* points = streamline->GetPoints(); if (points) { for (vtkIdType i = 0; i < points->GetNumberOfPoints(); ++i) { double point[3]; points->GetPoint(i, point); int ijk[3] = { 0, 0, 0 }; ijk[0] = floor(point[0] / this->SeparatingDistanceArcLength); ijk[1] = floor(point[1] / this->SeparatingDistanceArcLength); vtkIdType cellId = this->SuperposedGrid->ComputeCellId(ijk); this->AllPoints[cellId].push_back({ { point[0], point[1], point[2] } }); } } } //------------------------------------------------------------------------------ void vtkEvenlySpacedStreamlines2D::GetBounds(vtkCompositeDataSet* cds, double bounds[6]) { if (vtkOverlappingAMR::SafeDownCast(cds)) { vtkOverlappingAMR* amr = vtkOverlappingAMR::SafeDownCast(cds); amr->GetBounds(bounds); } else { // initialize bounds for (int i : { 0, 2, 4 }) { bounds[i] = std::numeric_limits::max(); } for (int i : { 1, 3, 5 }) { bounds[i] = -std::numeric_limits::max(); } // go over all datasets in the composite data and find min,max // for components of all bounds vtkSmartPointer iter; iter.TakeReference(cds->NewIterator()); iter->GoToFirstItem(); while (!iter->IsDoneWithTraversal()) { vtkDataSet* input = vtkDataSet::SafeDownCast(iter->GetCurrentDataObject()); if (input) { double b[6]; input->GetBounds(b); for (int i : { 0, 2, 4 }) { bounds[i] = std::min(b[i], bounds[i]); } for (int i : { 1, 3, 5 }) { bounds[i] = std::max(b[i], bounds[i]); } } iter->GoToNextItem(); } } } //------------------------------------------------------------------------------ const char* vtkEvenlySpacedStreamlines2D::GetInputArrayToProcessName() { vtkSmartPointer iter; iter.TakeReference(this->InputData->NewIterator()); vtkDataSet* input0 = nullptr; iter->GoToFirstItem(); while (!iter->IsDoneWithTraversal() && input0 == nullptr) { input0 = vtkDataSet::SafeDownCast(iter->GetCurrentDataObject()); iter->GoToNextItem(); } if (!input0) { return ""; } int vecType(0); vtkDataArray* vectors = this->GetInputArrayToProcess(0, input0, vecType); if (vectors) { return vectors->GetName(); } else { vtkErrorMacro("vtkEvenlySpacedStreamlines2D::SetInputArrayToProcess was not called"); return nullptr; } } //------------------------------------------------------------------------------ void vtkEvenlySpacedStreamlines2D::SetIntegratorTypeToRungeKutta2() { this->SetIntegratorType(vtkStreamTracer::RUNGE_KUTTA2); } //------------------------------------------------------------------------------ void vtkEvenlySpacedStreamlines2D::SetIntegratorTypeToRungeKutta4() { this->SetIntegratorType(vtkStreamTracer::RUNGE_KUTTA4); } //------------------------------------------------------------------------------ void vtkEvenlySpacedStreamlines2D::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "Start position: " << this->StartPosition[0] << " " << this->StartPosition[1] << " " << this->StartPosition[2] << endl; os << indent << "Terminal speed: " << this->TerminalSpeed << endl; os << indent << "Integration step unit: " << ((this->IntegrationStepUnit == vtkStreamTracer::LENGTH_UNIT) ? "length." : "cell length.") << endl; os << indent << "Initial integration step: " << this->InitialIntegrationStep << endl; os << indent << "Separation distance: " << this->SeparatingDistance << endl; os << indent << "Integrator: " << this->Integrator << endl; os << indent << "Vorticity computation: " << (this->ComputeVorticity ? " On" : " Off") << endl; } VTK_ABI_NAMESPACE_END