// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkParametricFunctionSource.h" #include "vtkCellArray.h" #include "vtkFloatArray.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkMath.h" #include "vtkObjectFactory.h" #include "vtkParametricFunction.h" #include "vtkPointData.h" #include "vtkPoints.h" #include "vtkPolyData.h" #include "vtkPolyDataNormals.h" #include "vtkSmartPointer.h" #include "vtkTriangleFilter.h" #include #include VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkParametricFunctionSource); vtkCxxSetObjectMacro(vtkParametricFunctionSource, ParametricFunction, vtkParametricFunction); //------------------------------------------------------------------------------ vtkParametricFunctionSource::vtkParametricFunctionSource() : ParametricFunction(nullptr) , UResolution(50) , VResolution(50) , WResolution(50) , GenerateTextureCoordinates(0) , ScalarMode(vtkParametricFunctionSource::SCALAR_NONE) , OutputPointsPrecision(vtkAlgorithm::SINGLE_PRECISION) { this->SetNumberOfInputPorts(0); this->GenerateNormals = 1; } //------------------------------------------------------------------------------ vtkParametricFunctionSource::~vtkParametricFunctionSource() { this->SetParametricFunction(nullptr); } namespace { /** * Make the cells containing the ordered point Ids. * */ void AddTriCells(vtkCellArray* cellArray, int id1, int id2, int id3, int id4, bool clockwise) { cellArray->InsertNextCell(3); if (clockwise) { cellArray->InsertCellPoint(id1); cellArray->InsertCellPoint(id2); cellArray->InsertCellPoint(id3); cellArray->InsertNextCell(3); cellArray->InsertCellPoint(id1); cellArray->InsertCellPoint(id3); cellArray->InsertCellPoint(id4); } else { cellArray->InsertCellPoint(id1); cellArray->InsertCellPoint(id3); cellArray->InsertCellPoint(id2); cellArray->InsertNextCell(3); cellArray->InsertCellPoint(id1); cellArray->InsertCellPoint(id4); cellArray->InsertCellPoint(id3); } } } // anonymous namespace //------------------------------------------------------------------------------ void vtkParametricFunctionSource::MakeTriangles(vtkCellArray* cells, int PtsU, int PtsV) { int id1 = 0; int id2 = 0; int id3 = 0; int id4 = 0; vtkDebugMacro(<< "Executing MakeTriangles()"); bool clockwise = (this->ParametricFunction->GetClockwiseOrdering() != 0); vtkIdType numCells = (PtsU + this->ParametricFunction->GetJoinU() - 1) * (PtsV + this->ParametricFunction->GetJoinV() - 1) * 2; cells->AllocateExact(numCells, numCells * 3); for (int i = 0; i < PtsU - 1; ++i) { // Fill the allocated space with the indexes to the points. for (int j = 0; j < PtsV - 1; ++j) { id1 = j + i * PtsV; id2 = id1 + PtsV; id3 = id2 + 1; id4 = id1 + 1; AddTriCells(cells, id1, id2, id3, id4, clockwise); } // If necessary, connect the ends of the triangle strip. if (this->ParametricFunction->GetJoinV()) { id1 = id4; id2 = id3; if (this->ParametricFunction->GetTwistV()) { id3 = (i + 1) * PtsV; id4 = i * PtsV; } else { id3 = i * PtsV; id4 = (i + 1) * PtsV; } AddTriCells(cells, id1, id2, id3, id4, clockwise); } } // If required, connect the last triangle strip to the first by // adding a new triangle strip and filling it with the indexes // to the points. if (this->ParametricFunction->GetJoinU()) { for (int j = 0; j < PtsV - 1; ++j) { id1 = j + (PtsU - 1) * PtsV; id3 = id1 + 1; if (this->ParametricFunction->GetTwistU()) { id2 = PtsV - 1 - j; id4 = id2 - 1; } else { id2 = j; id4 = id2 + 1; } AddTriCells(cells, id1, id2, id3, id4, clockwise); } // If necessary, connect the ends of the triangle strip. if (this->ParametricFunction->GetJoinV()) { id1 = id3; id2 = id4; if (this->ParametricFunction->GetTwistU()) { if (this->ParametricFunction->GetTwistV()) { id3 = PtsV - 1; id4 = (PtsU - 1) * PtsV; } else { id3 = (PtsU - 1) * PtsV; id4 = PtsV - 1; } } else { if (this->ParametricFunction->GetTwistV()) { id3 = 0; id4 = (PtsU - 1) * PtsV; } else { id3 = (PtsU - 1) * PtsV; id4 = 0; } } AddTriCells(cells, id1, id2, id3, id4, clockwise); } } cells->Modified(); vtkDebugMacro(<< "MakeTriangles() finished."); } //------------------------------------------------------------------------------ int vtkParametricFunctionSource::RequestData(vtkInformation* vtkNotUsed(info), vtkInformationVector** vtkNotUsed(inputV), vtkInformationVector* output) { vtkDebugMacro(<< "Executing"); // Check that a parametric function has been defined if (!this->ParametricFunction) { vtkErrorMacro(<< "Parametric function not defined"); return 1; } switch (this->ParametricFunction->GetDimension()) { case 1: this->Produce1DOutput(output); break; case 2: this->Produce2DOutput(output); break; default: vtkErrorMacro("Functions of dimension " << this->ParametricFunction->GetDimension() << " are not supported."); } return 1; } //------------------------------------------------------------------------------ void vtkParametricFunctionSource::Produce1DOutput(vtkInformationVector* output) { vtkIdType numPts = this->UResolution + 1; vtkSmartPointer lines = vtkSmartPointer::New(); vtkSmartPointer pts = vtkSmartPointer::New(); // Set the desired precision for the points in the output. if (this->OutputPointsPrecision == vtkAlgorithm::DOUBLE_PRECISION) { pts->SetDataType(VTK_DOUBLE); } else { pts->SetDataType(VTK_FLOAT); } pts->SetNumberOfPoints(numPts); vtkIdType i; double x[3], Du[3], t[3]; lines->AllocateEstimate(1, numPts); lines->InsertNextCell(numPts); // Insert points and cell points for (i = 0; i < numPts; i++) { t[0] = (double)i / this->UResolution; this->ParametricFunction->Evaluate(t, x, Du); pts->SetPoint(i, x); lines->InsertCellPoint(i); } vtkInformation* outInfo = output->GetInformationObject(0); vtkPolyData* outData = static_cast(outInfo->Get(vtkDataObject::DATA_OBJECT())); outData->SetPoints(pts); outData->SetLines(lines); } //------------------------------------------------------------------------------ void vtkParametricFunctionSource::Produce2DOutput(vtkInformationVector* output) { // Adjust so the ranges: // this->MinimumU ... this->ParametricFunction->GetMaximumU(), // this->MinimumV ... this->ParametricFunction->GetMaximumV() // are included in the triangulation. double MaxU = this->ParametricFunction->GetMaximumU() + (this->ParametricFunction->GetMaximumU() - this->ParametricFunction->GetMinimumU()) / (this->UResolution - 1); int PtsU = this->UResolution; double MaxV = this->ParametricFunction->GetMaximumV() + (this->ParametricFunction->GetMaximumV() - this->ParametricFunction->GetMinimumV()) / (this->VResolution - 1); int PtsV = this->VResolution; int totPts = PtsU * PtsV; // Scalars associated with each point vtkSmartPointer sval = vtkSmartPointer::New(); if (this->ScalarMode != SCALAR_NONE) { sval->SetNumberOfTuples(totPts); sval->SetName("Scalars"); } // The normals to the surface vtkSmartPointer nval = vtkSmartPointer::New(); if (this->GenerateNormals) { nval->SetNumberOfComponents(3); nval->SetNumberOfTuples(totPts); nval->SetName("Normals"); } // Texture coordinates vtkSmartPointer newTCoords = vtkSmartPointer::New(); if (this->GenerateTextureCoordinates != 0) { newTCoords->SetNumberOfComponents(2); newTCoords->Allocate(2 * totPts); newTCoords->SetName("Textures"); } vtkSmartPointer points = vtkSmartPointer::New(); // Set the desired precision for the points in the output. if (this->OutputPointsPrecision == vtkAlgorithm::DOUBLE_PRECISION) { points->SetDataType(VTK_DOUBLE); } else { points->SetDataType(VTK_FLOAT); } points->SetNumberOfPoints(totPts); double uStep = (MaxU - this->ParametricFunction->GetMinimumU()) / PtsU; double vStep = (MaxV - this->ParametricFunction->GetMinimumV()) / PtsV; // Find the mid points of the (u,v) map. double u0 = this->ParametricFunction->GetMinimumU(); double u_mp = (MaxU - u0) / 2.0 + u0 - uStep; while (u0 < u_mp) { u0 += uStep; } double v0 = this->ParametricFunction->GetMinimumV(); double v_mp = (MaxV - v0) / 2.0 + v0 - vStep; while (v0 < v_mp) { v0 += vStep; } u_mp += uStep; v_mp += vStep; // At this point (u_mp, v_mp) is the midpoint of the (u,v) map and (u0,v0) // corresponds to the nearest grid point to the midpoint of the (u,v) map. // double rel_u = 0; // will be u - u_mp double rel_v = 0; // will be v - v_mp int k = 0; double uv[3]; uv[0] = this->ParametricFunction->GetMinimumU() - uStep; float MaxI = PtsU - 1; float MaxJ = PtsV - 1; for (int i = 0; i < PtsU; ++i) { uv[0] += uStep; uv[1] = this->ParametricFunction->GetMinimumV() - vStep; double tc[2]; if (this->GenerateTextureCoordinates != 0) { tc[0] = i / MaxI; } for (int j = 0; j < PtsV; ++j) { uv[1] += vStep; if (this->GenerateTextureCoordinates != 0) { tc[1] = 1.0 - j / MaxJ; newTCoords->InsertNextTuple(tc); } // The point double Pt[3]; // Partial derivative at Pt with respect to u,v,w. double Du[9]; // Partial derivative at Pt with respect to v. double* Dv = Du + 3; // Calculate fn(u)->(Pt,Du). this->ParametricFunction->Evaluate(uv, Pt, Du); // Insert the points and scalar. points->InsertPoint(k, Pt[0], Pt[1], Pt[2]); double scalar; if (this->ScalarMode != SCALAR_NONE) { switch (this->ScalarMode) { case SCALAR_U: scalar = uv[0]; break; case SCALAR_V: scalar = uv[1]; break; case SCALAR_U0: scalar = uv[0] == u0 ? 1 : 0; break; case SCALAR_V0: scalar = uv[1] == v0 ? 1 : 0; break; case SCALAR_U0V0: scalar = 0; // u0, v0 if (uv[0] == u0 && uv[1] == v0) { scalar = 3; } else { // u0 line if (uv[0] == u0) { scalar = 1; } else { // v0 line if (uv[1] == v0) { scalar = 2; } } } break; case SCALAR_MODULUS: rel_u = uv[0] - u_mp; rel_v = uv[1] - v_mp; scalar = sqrt(rel_u * rel_u + rel_v * rel_v); break; case SCALAR_PHASE: rel_u = uv[0] - u_mp; rel_v = uv[1] - v_mp; if (rel_v == 0 && rel_u == 0) { scalar = 0; } else { scalar = vtkMath::DegreesFromRadians(atan2(rel_v, rel_u)); if (scalar < 0) { scalar += 360; } } break; case SCALAR_QUADRANT: if (uv[0] >= u0 && uv[1] >= v0) { scalar = 1; break; } if (uv[0] < u0 && uv[1] >= v0) { scalar = 2; break; } if (uv[0] < u0 && uv[1] < v0) { scalar = 3; } else { scalar = 4; } break; case SCALAR_X: scalar = Pt[0]; break; case SCALAR_Y: scalar = Pt[1]; break; case SCALAR_Z: scalar = Pt[2]; break; case SCALAR_DISTANCE: scalar = sqrt(Pt[0] * Pt[0] + Pt[1] * Pt[1] + Pt[2] * Pt[2]); break; case SCALAR_FUNCTION_DEFINED: scalar = this->ParametricFunction->EvaluateScalar(uv, Pt, Du); break; case SCALAR_NONE: default: scalar = 0; } sval->SetValue(k, scalar); } // Calculate the normal. if (this->ParametricFunction->GetDerivativesAvailable() && this->GenerateNormals) { double n[3]; if (this->ParametricFunction->GetClockwiseOrdering() == 0) { // Anti-clockwise ordering vtkMath::Cross(Dv, Du, n); } else { // Clockwise ordering vtkMath::Cross(Du, Dv, n); } nval->SetTuple3(k, n[0], n[1], n[2]); } ++k; } } vtkInformation* outInfo = output->GetInformationObject(0); vtkPolyData* outData = static_cast(outInfo->Get(vtkDataObject::DATA_OBJECT())); vtkCellArray* tris = vtkCellArray::New(); this->MakeTriangles(tris, PtsU, PtsV); outData->SetPoints(points); outData->SetPolys(tris); if (this->GenerateNormals) { if (this->ParametricFunction->GetDerivativesAvailable()) { outData->GetPointData()->SetNormals(nval); } else { // Used to hold the surface vtkSmartPointer pd = vtkSmartPointer::New(); pd->SetPoints(points); pd->SetPolys(tris); vtkSmartPointer norm = vtkSmartPointer::New(); // we prevent vtkPolyDataNormals from generating new points // so that the number of newTCoords matches the number of points. norm->SplittingOff(); norm->SetInputData(pd); norm->Update(); outData->DeepCopy(norm->GetOutput()); } } tris->Delete(); if (this->ScalarMode != SCALAR_NONE) { outData->GetPointData()->SetScalars(sval); } if (this->GenerateTextureCoordinates != 0) { outData->GetPointData()->SetTCoords(newTCoords); } outData->Modified(); } //------------------------------------------------------------------------------ vtkMTimeType vtkParametricFunctionSource::GetMTime() { vtkMTimeType mTime = this->Superclass::GetMTime(); vtkMTimeType funcMTime; if (this->ParametricFunction != nullptr) { funcMTime = this->ParametricFunction->GetMTime(); mTime = (funcMTime > mTime ? funcMTime : mTime); } return mTime; } //------------------------------------------------------------------------------ void vtkParametricFunctionSource::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "U Resolution: " << this->UResolution << "\n"; os << indent << "V Resolution: " << this->VResolution << "\n"; os << indent << "W Resolution: " << this->WResolution << "\n"; if (this->ParametricFunction) { os << indent << "Parametric Function: " << this->ParametricFunction << "\n"; } else { os << indent << "No Parametric function defined\n"; } std::string s; switch (this->ScalarMode) { case SCALAR_NONE: s = "SCALAR_NONE"; break; case SCALAR_U: s = "SCALAR_U"; break; case SCALAR_V: s = "SCALAR_V"; break; case SCALAR_U0: s = "SCALAR_U0"; break; case SCALAR_V0: s = "SCALAR_V0"; break; case SCALAR_U0V0: s = "SCALAR_U0V0"; break; case SCALAR_MODULUS: s = "SCALAR_MODULUS"; break; case SCALAR_PHASE: s = "SCALAR_PHASE"; break; case SCALAR_QUADRANT: s = "SCALAR_QUADRANT"; break; case SCALAR_X: s = "SCALAR_X"; break; case SCALAR_Y: s = "SCALAR_Y"; break; case SCALAR_Z: s = "SCALAR_Z"; break; case SCALAR_DISTANCE: s = "SCALAR_DISTANCE"; break; case SCALAR_FUNCTION_DEFINED: s = "SCALAR_FUNCTION_DEFINED"; break; default: s = "Unknown scalar mode."; } os << indent << "Scalar Mode: " << s << "\n"; os << indent << "GenerateTextureCoordinates:" << (this->GenerateTextureCoordinates ? "On" : "Off") << "\n"; os << indent << "Output Points Precision: " << this->OutputPointsPrecision << "\n"; } VTK_ABI_NAMESPACE_END