// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkUncertaintyTubeFilter.h" #include "vtkCellArray.h" #include "vtkCellData.h" #include "vtkDataSet.h" #include "vtkDoubleArray.h" #include "vtkFloatArray.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkMath.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkPolyData.h" #include "vtkPolyLine.h" VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkUncertaintyTubeFilter); // // Special classes for manipulating data // class vtkTubePoint { //;prevent man page generation public: vtkTubePoint(); // method sets up storage vtkTubePoint& operator=(const vtkTubePoint& hp); // for resizing double X[3]; // position double V0[3]; // the line tangent double V1[3]; // the normal to the line double V2[3]; // another, orthogonal normal to the line double Vector[3]; // local uncertainty vector }; class vtkTubeArray { //;prevent man page generation public: vtkTubeArray(); ~vtkTubeArray() { delete[] this->Array; } vtkIdType GetNumberOfPoints() { return this->MaxId + 1; } vtkTubePoint* GetTubePoint(vtkIdType i) { return this->Array + i; } vtkTubePoint* InsertNextTubePoint() { if (++this->MaxId >= this->Size) { this->Resize(this->MaxId); } return this->Array + this->MaxId; } vtkTubePoint* Resize(vtkIdType sz); // reallocates data void Reset() { this->MaxId = -1; } vtkTubePoint* Array; // pointer to data vtkIdType MaxId; // maximum index inserted thus far vtkIdType Size; // allocated size of data vtkIdType Extend; // grow array by this amount }; //------------------------------------------------------------------------------ vtkTubePoint::vtkTubePoint() = default; //------------------------------------------------------------------------------ vtkTubePoint& vtkTubePoint::operator=(const vtkTubePoint& hp) { if (this == &hp) { return *this; } for (int i = 0; i < 3; i++) { this->X[i] = hp.X[i]; this->V0[i] = hp.V0[i]; this->V1[i] = hp.V1[i]; this->V2[i] = hp.V2[i]; } this->Vector[0] = hp.Vector[0]; this->Vector[1] = hp.Vector[1]; this->Vector[2] = hp.Vector[2]; return *this; } //------------------------------------------------------------------------------ vtkTubeArray::vtkTubeArray() { this->MaxId = -1; this->Array = new vtkTubePoint[1000]; this->Size = 1000; this->Extend = 5000; } //------------------------------------------------------------------------------ vtkTubePoint* vtkTubeArray::Resize(vtkIdType sz) { vtkTubePoint* newArray; vtkIdType newSize, i; if (sz >= this->Size) { newSize = this->Size + this->Extend * (((sz - this->Size) / this->Extend) + 1); } else { newSize = sz; } newArray = new vtkTubePoint[newSize]; for (i = 0; i < sz; i++) { newArray[i] = this->Array[i]; } this->Size = newSize; delete[] this->Array; this->Array = newArray; return this->Array; } //------------------------------------------------------------------------------ // Construct object with initial starting position (0,0,0); integration step // length 0.2; step length 0.01; forward integration; terminal eigenvalue 0.0; // number of sides 6; radius 0.5; and logarithmic scaling off. vtkUncertaintyTubeFilter::vtkUncertaintyTubeFilter() { this->Tubes = nullptr; this->NumberOfSides = 12; } //------------------------------------------------------------------------------ vtkUncertaintyTubeFilter::~vtkUncertaintyTubeFilter() { delete[] this->Tubes; } //------------------------------------------------------------------------------ static double IntersectEllipse(double vector[3], double v[3]) { double a = vector[0]; double b = vector[1]; double c = vector[2]; double num = a * a * b * b * c * c; double den = v[0] * v[0] * b * b * c * c + v[1] * v[1] * a * a * c * c + v[2] * v[2] * a * a * b * b; if (den <= 0.0) { return 0.0; } else { return sqrt(num / den); } } //------------------------------------------------------------------------------ int vtkUncertaintyTubeFilter::RequestData(vtkInformation* vtkNotUsed(request), vtkInformationVector** inputVector, vtkInformationVector* outputVector) { // get the info objects vtkInformation* inInfo = inputVector[0]->GetInformationObject(0); vtkInformation* outInfo = outputVector->GetInformationObject(0); // get the input and output vtkPolyData* input = vtkPolyData::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT())); vtkPolyData* output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); vtkPointData* pd = input->GetPointData(); vtkPointData* outPD = output->GetPointData(); vtkCellData* cd = input->GetCellData(); vtkCellData* outCD = output->GetCellData(); vtkDataArray* inVectors; vtkTubePoint *sPrev, *sPtr, *sNext; int k; double v0[3], v1[3]; vtkDebugMacro(<< "Generating uncertainty tubes"); vtkPoints* inPts = input->GetPoints(); vtkCellArray* inLines = input->GetLines(); if (!inPts || !inLines) { return 1; } vtkIdType numPts = inPts->GetNumberOfPoints(); vtkIdType numLines = inLines->GetNumberOfCells(); if (numPts < 1 || numLines < 1) { return 1; } if (!(inVectors = pd->GetVectors())) { return 1; } // Initialize the data outPD->CopyNormalsOff(); outPD->CopyAllocate(pd); outCD->CopyAllocate(cd); this->NumberOfTubes = numLines; this->Tubes = new vtkTubeArray[this->NumberOfTubes]; vtkDoubleArray* newNormals = vtkDoubleArray::New(); newNormals->SetName("TubeNormals"); newNormals->SetNumberOfComponents(3); newNormals->Allocate(numPts); vtkIdType npts; const vtkIdType* pts; vtkIdType idx; vtkCellArray* singlePolyline = vtkCellArray::New(); double* normal; for (k = 0, inLines->InitTraversal(); inLines->GetNextCell(npts, pts); k++) { if (this->CheckAbort()) { break; } singlePolyline->Reset(); // avoid instantiation singlePolyline->InsertNextCell(npts, pts); // avoid crossing confusion if (!vtkPolyLine::GenerateSlidingNormals(inPts, singlePolyline, newNormals)) { vtkWarningMacro("Could not generate normals for line. " "Skipping to next."); continue; // skip tubing this polyline } for (idx = 0; idx < npts; idx++) { // This advances to the next point sPtr = this->Tubes[k].InsertNextTubePoint(); inPts->GetPoint(pts[idx], sPtr->X); // The normal to the line normal = newNormals->GetTuple(idx); sPtr->V1[0] = normal[0]; sPtr->V1[1] = normal[1]; sPtr->V1[2] = normal[2]; } // Okay build the rest of the coordinate system. We've got to find the // vector along the line segment, and then cross the normal/vector to // get the third axis. for (idx = 0; idx < npts; idx++) { sPtr = this->Tubes[k].GetTubePoint(idx); inVectors->GetTuple(pts[idx], sPtr->Vector); if (idx == 0) { sNext = this->Tubes[k].GetTubePoint(1); sPtr->V0[0] = sNext->X[0] - sPtr->X[0]; sPtr->V0[1] = sNext->X[1] - sPtr->X[1]; sPtr->V0[2] = sNext->X[2] - sPtr->X[2]; } else if (idx == (npts - 1)) { sPrev = this->Tubes[k].GetTubePoint(npts - 2); sPtr->V0[0] = sPtr->X[0] - sPrev->X[0]; sPtr->V0[1] = sPtr->X[1] - sPrev->X[1]; sPtr->V0[2] = sPtr->X[2] - sPrev->X[2]; } else { sPrev = this->Tubes[k].GetTubePoint(idx - 1); sNext = this->Tubes[k].GetTubePoint(idx + 1); v0[0] = sPtr->X[0] - sPrev->X[0]; v0[1] = sPtr->X[1] - sPrev->X[1]; v0[2] = sPtr->X[2] - sPrev->X[2]; vtkMath::Normalize(v0); v1[0] = sNext->X[0] - sPtr->X[0]; v1[1] = sNext->X[1] - sPtr->X[1]; v1[2] = sNext->X[2] - sPtr->X[2]; vtkMath::Normalize(v1); sPtr->V0[0] = (v0[0] + v1[0]) / 2.0; // average vector sPtr->V0[1] = (v0[1] + v1[1]) / 2.0; sPtr->V0[2] = (v0[2] + v1[2]) / 2.0; } vtkMath::Normalize(sPtr->V0); // Produce orthogonal axis vtkMath::Cross(sPtr->V0, sPtr->V1, sPtr->V2); vtkMath::Normalize(sPtr->V2); vtkMath::Cross(sPtr->V2, sPtr->V0, sPtr->V1); vtkMath::Normalize(sPtr->V1); } // for all points in polyline } // for all polylines newNormals->Delete(); singlePolyline->Delete(); // Build the uncertainty tubes int retval = this->BuildTubes(pd, outPD, cd, outCD, output); return retval; } //------------------------------------------------------------------------------ int vtkUncertaintyTubeFilter::BuildTubes( vtkPointData* pd, vtkPointData* outPD, vtkCellData* cd, vtkCellData* outCD, vtkPolyData* output) { vtkTubePoint* sPtr; vtkPoints* newPts; vtkFloatArray* newNormals; vtkCellArray* newStrips; vtkIdType i, npts, ptOffset = 0; int cellId, j, id, k, i1, i2; double x[3], r1[3], r2[3]; double xT[3], normal[3], *vector, t; double theta = 2.0 * vtkMath::Pi() / this->NumberOfSides; vtkIdType numPts; // Initialize // vtkDebugMacro(<< "Creating uncertainty tubes"); if (this->NumberOfTubes <= 0) { return 0; } // Allocate // newPts = vtkPoints::New(); newPts->Allocate(2500); newNormals = vtkFloatArray::New(); newNormals->SetNumberOfComponents(3); newNormals->Allocate(7500); newStrips = vtkCellArray::New(); newStrips->AllocateEstimate(3 * this->NumberOfTubes, VTK_CELL_SIZE); // // Loop over all polylines generating points // for (cellId = 0; cellId < this->NumberOfTubes; cellId++) { if (this->CheckAbort()) { break; } if ((numPts = this->Tubes[cellId].GetNumberOfPoints()) < 2) { continue; } for (npts = 0, i = 0; i < numPts; i++) { sPtr = this->Tubes[cellId].GetTubePoint(i); for (j = 0; j < 3; j++) // compute point in center of tube { x[j] = sPtr->X[j]; r1[j] = sPtr->V1[j]; r2[j] = sPtr->V2[j]; } vector = sPtr->Vector; // construct points around tube for (k = 0; k < this->NumberOfSides; k++) { for (j = 0; j < 3; j++) { normal[j] = r1[j] * cos((double)k * theta) + r2[j] * sin((double)k * theta); } vtkMath::Normalize(normal); t = IntersectEllipse(vector, normal); xT[0] = x[0] + 0.5 * t * normal[0]; xT[1] = x[1] + 0.5 * t * normal[1]; xT[2] = x[2] + 0.5 * t * normal[2]; id = newPts->InsertNextPoint(xT); outPD->CopyData(pd, i, id); vtkMath::Normalize(normal); newNormals->InsertTuple(id, normal); } npts++; } // for this polyline // Generate the strips for this tube // for (k = 0; k < this->NumberOfSides; k++) { i1 = (k + 1) % this->NumberOfSides; id = newStrips->InsertNextCell(npts * 2); outCD->CopyData(cd, cellId, id); for (i = 0; i < npts; i++) { i2 = i * this->NumberOfSides; newStrips->InsertCellPoint(ptOffset + i2 + k); newStrips->InsertCellPoint(ptOffset + i2 + i1); } } // for all tube sides ptOffset += this->NumberOfSides * npts; } // for all tubes // Update ourselves // output->SetPoints(newPts); newPts->Delete(); output->SetStrips(newStrips); newStrips->Delete(); outPD->SetNormals(newNormals); newNormals->Delete(); output->Squeeze(); return 1; } //------------------------------------------------------------------------------ void vtkUncertaintyTubeFilter::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "Number Of Sides: " << this->NumberOfSides << "\n"; } VTK_ABI_NAMESPACE_END