// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkRegressionTestImage.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { vtkSmartPointer MakePolyhedron1(); vtkSmartPointer MakePolyhedron2(); template bool testValue(const std::string& value, T gotVal, T expectedVal); } //------------------------------------------------------------------------------ int TestPolyhedronDecompose(int argc, char* argv[]) { ////////// Setup data objects ////////// // Create neighboring polyhedrons auto polyhedron1 = ::MakePolyhedron1(); auto polyhedron2 = ::MakePolyhedron2(); // Add some cell data vtkNew cellArray; cellArray->SetNumberOfValues(2); cellArray->SetName("Cell array"); cellArray->SetValue(0, 1.5); cellArray->SetValue(1, 1.5); vtkNew cellData; cellData->AddArray(cellArray); // Add some point data constexpr std::array doubleValues = { 2, 5, 2, 2, 2, 3, 2, 3, 2, 5, 2, 2, 2, 3, 2, 3 }; const std::array stringValues = { "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A" }; constexpr std::array bitValues = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; vtkNew pointArrayDouble; // Will be dispatched pointArrayDouble->SetNumberOfValues(16); pointArrayDouble->SetName("Doubles"); vtkNew pointArrayString; // Will not pointArrayString->SetNumberOfValues(16); pointArrayString->SetName("Strings"); vtkNew pointArrayBits; // Will not pointArrayBits->SetNumberOfValues(16); pointArrayBits->SetName("Bits"); for (int i = 0; i < 16; i++) { pointArrayDouble->SetValue(i, doubleValues[i]); pointArrayString->SetValue(i, stringValues[i]); pointArrayBits->SetValue(i, bitValues[i]); } vtkNew pointData; pointData->AddArray(pointArrayDouble); pointData->AddArray(pointArrayString); pointData->AddArray(pointArrayBits); // Decompose polyhedra auto decomposedUG1 = vtkPolyhedronUtilities::Decompose(polyhedron1, pointData, 0, cellData); auto decomposedUG2 = vtkPolyhedronUtilities::Decompose(polyhedron2, pointData, 1, cellData); std::array, 2> decomposedUGs = { decomposedUG1, decomposedUG2 }; for (auto const& decomposedUG : decomposedUGs) { ////////// Test geometry ////////// // New number of pts = original pts + face barycenters + cell barycenter auto numberOfPts = decomposedUG->GetNumberOfPoints(); if (!testValue("number of points", numberOfPts, vtkIdType(8 + 6 + 1))) { return EXIT_FAILURE; } // New number of cells = original nb of faces * 4 auto numberOfCells = decomposedUG->GetNumberOfCells(); if (!testValue("number of cells", numberOfCells, vtkIdType(6 * 4))) { return EXIT_FAILURE; } ////////// Test data ////////// auto pointDataDec = decomposedUG->GetPointData(); auto cellDataDec = decomposedUG->GetCellData(); // Test barycenters point data // Face barycenter: mean value of face point data // Cell barycenter (last one): mean value of face barycenters point data vtkDoubleArray* doubleArray = vtkDoubleArray::SafeDownCast(pointDataDec->GetAbstractArray("Doubles")); if (!doubleArray) { std::cerr << "Unable to retrieve \"Doubles\" point data." << std::endl; } if (!testValue("point data (\"Doubles\") nb of tuples", doubleArray->GetNumberOfTuples(), decomposedUG->GetNumberOfPoints())) { return EXIT_FAILURE; } constexpr double expectedValues[7] = { 2.75, 3, 2, 3.25, 2.25, 2.5, 2.625 }; for (int i = 0; i < 7; i++) { if (!testValue("point data (\"Doubles\") at index " + vtk::to_string(i), doubleArray->GetValue(i + 8), expectedValues[i])) { return EXIT_FAILURE; } } // vtkStringArray is not dispatched, check that the fallback initialized // the values as an empty string vtkStringArray* stringArray = vtkStringArray::SafeDownCast(pointDataDec->GetAbstractArray("Strings")); if (!stringArray) { std::cerr << "Unable to retrieve \"Strings\" point data." << std::endl; } if (!testValue("point data (\"Strings\") nb of tuples", stringArray->GetNumberOfTuples(), decomposedUG->GetNumberOfPoints())) { return EXIT_FAILURE; } for (int i = 0; i < 7; i++) { if (!testValue("point data (\"Strings\") at index " + vtk::to_string(i), stringArray->GetValue(i + 8), vtkStdString())) { return EXIT_FAILURE; } } // vtkBitArray is not dispatched, check that the fallback initialized // the values with 0 vtkBitArray* bitArray = vtkBitArray::SafeDownCast(pointDataDec->GetAbstractArray("Bits")); if (!bitArray) { std::cerr << "Unable to retrieve \"Bits\" point data." << std::endl; } if (!testValue("point data (\"Bits\") nb of tuples", bitArray->GetNumberOfTuples(), decomposedUG->GetNumberOfPoints())) { return EXIT_FAILURE; } for (int i = 0; i < 7; i++) { if (!testValue( "point data (\"Bits\") at index " + vtk::to_string(i), bitArray->GetValue(i + 8), 0)) { return EXIT_FAILURE; } } // Cell data should be copied to all cells vtkDoubleArray* doubleArrayCells = vtkDoubleArray::SafeDownCast(cellDataDec->GetAbstractArray("Cell array")); if (!doubleArrayCells) { std::cerr << "Unable to retrieve \"Cell array\" cell data." << std::endl; } for (vtkIdType cellId = 0; cellId < decomposedUG->GetNumberOfCells(); cellId++) { if (!testValue("Cell array", doubleArrayCells->GetValue(cellId), 1.5)) { return EXIT_FAILURE; } } } ////////// Test contour ////////// // Extract contours from decomposed ugs vtkNew contourFilter; contourFilter->SetInputData(decomposedUG1); contourFilter->SetInputArrayToProcess( 0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, "Doubles"); contourFilter->SetNumberOfContours(1); contourFilter->SetValue(0, 3.5); contourFilter->Update(); vtkNew contour1; contour1->DeepCopy(vtkPolyData::SafeDownCast(contourFilter->GetOutputDataObject(0))); contourFilter->SetInputData(decomposedUG2); contourFilter->Update(); vtkNew contour2; contour2->DeepCopy(vtkPolyData::SafeDownCast(contourFilter->GetOutputDataObject(0))); // Extract surface from decomposed ugs for rendering vtkNew filter; filter->SetInputDataObject(decomposedUG1); filter->Update(); vtkNew ugSurface1; ugSurface1->DeepCopy(vtkPolyData::SafeDownCast(filter->GetOutputDataObject(0))); filter->SetInputDataObject(decomposedUG2); filter->Update(); vtkNew ugSurface2; ugSurface2->DeepCopy(vtkPolyData::SafeDownCast(filter->GetOutputDataObject(0))); ugSurface2->DeepCopy(vtkPolyData::SafeDownCast(filter->GetOutputDataObject(0))); // Mappers vtkNew ugMapper1; ugMapper1->SetInputData(ugSurface1); vtkNew ugMapper2; ugMapper2->SetInputData(ugSurface2); vtkNew contourMapper1; contourMapper1->SetInputData(contour1); vtkNew contourMapper2; contourMapper2->SetInputData(contour2); // Actors vtkNew ugActor1; ugActor1->SetMapper(ugMapper1); ugActor1->GetProperty()->SetOpacity(0.1); vtkNew ugActor2; ugActor2->SetMapper(ugMapper2); ugActor2->GetProperty()->SetOpacity(0.1); vtkNew contourActor1; contourActor1->SetMapper(contourMapper1); vtkNew contourActor2; contourActor2->SetMapper(contourMapper2); // Renderer vtkNew renderer; renderer->AddActor(ugActor1); renderer->AddActor(ugActor2); renderer->AddActor(contourActor1); renderer->AddActor(contourActor2); // Camera renderer->GetActiveCamera()->Azimuth(135); renderer->ResetCamera(); // Render window vtkNew renderWindow; renderWindow->AddRenderer(renderer); renderWindow->SetSize(300, 300); // Interactor vtkNew renderWindowInteractor; renderWindowInteractor->SetRenderWindow(renderWindow); // Regression image testing renderWindow->Render(); int retVal = vtkRegressionTestImage(renderWindow); if (retVal == vtkRegressionTester::DO_INTERACTOR) { renderWindowInteractor->Start(); retVal = vtkRegressionTester::PASSED; } return (retVal == vtkRegressionTester::PASSED) ? EXIT_SUCCESS : EXIT_FAILURE; } namespace { //------------------------------------------------------------------------------ vtkSmartPointer MakePolyhedron1() { vtkSmartPointer polyhedron = vtkSmartPointer::New(); // Point Ids for (int i = 0; i < 8; ++i) { polyhedron->GetPointIds()->InsertNextId(i); } // Points polyhedron->GetPoints()->InsertNextPoint(2.5, -7.5, 2.5); polyhedron->GetPoints()->InsertNextPoint(5.31, -5.31, 4.68); polyhedron->GetPoints()->InsertNextPoint(2.5, -2.5, 2.5); polyhedron->GetPoints()->InsertNextPoint(7.5, -2.5, 2.5); polyhedron->GetPoints()->InsertNextPoint(2.5, -7.5, 7.5); polyhedron->GetPoints()->InsertNextPoint(6.25, -6.25, 6.25); polyhedron->GetPoints()->InsertNextPoint(2.5, -2.5, 7.5); polyhedron->GetPoints()->InsertNextPoint(6.25, -3.75, 6.25); // Faces vtkIdType face_offsets[7] = { 0, 4, 8, 12, 16, 20, 24 }; vtkIdType face_conns[24] = { 0, 1, 3, 2, 0, 4, 5, 1, 0, 2, 6, 4, 1, 5, 7, 3, 3, 7, 6, 2, 4, 6, 7, 5 }; vtkNew faces; vtkNew offsets_arr; vtkNew conns_arr; offsets_arr->SetArray(face_offsets, 7, 1); conns_arr->SetArray(face_conns, 24, 1); faces->SetData(offsets_arr, conns_arr); polyhedron->SetCellFaces(faces); polyhedron->Initialize(); return polyhedron; } //------------------------------------------------------------------------------ vtkSmartPointer MakePolyhedron2() { vtkSmartPointer polyhedron = vtkSmartPointer::New(); // Point Ids for (int i = 8; i < 16; ++i) { polyhedron->GetPointIds()->InsertNextId(i); } // Points polyhedron->GetPoints()->InsertNextPoint(2.5, -7.5, 2.5); polyhedron->GetPoints()->InsertNextPoint(5.31, -5.31, 4.68); polyhedron->GetPoints()->InsertNextPoint(2.5, -12.5, 2.5); polyhedron->GetPoints()->InsertNextPoint(7.5, -12.5, 2.5); polyhedron->GetPoints()->InsertNextPoint(2.5, -7.5, 7.5); polyhedron->GetPoints()->InsertNextPoint(6.25, -6.25, 6.25); polyhedron->GetPoints()->InsertNextPoint(2.5, -12.5, 7.5); polyhedron->GetPoints()->InsertNextPoint(6.25, -13.75, 6.25); // Faces vtkIdType face_offsets[7] = { 0, 4, 8, 12, 16, 20, 24 }; vtkIdType face_conns[24] = { 10, 11, 9, 8, 9, 13, 12, 8, 12, 14, 10, 8, 11, 15, 13, 9, 10, 14, 15, 11, 13, 15, 14, 12 }; vtkNew faces; vtkNew offsets_arr; vtkNew conns_arr; offsets_arr->SetArray(face_offsets, 7, 1); conns_arr->SetArray(face_conns, 24, 1); faces->SetData(offsets_arr, conns_arr); polyhedron->SetCellFaces(faces); polyhedron->Initialize(); return polyhedron; } //------------------------------------------------------------------------------ template bool testValue(const std::string& value, T gotVal, T expectedVal) { if (gotVal != expectedVal) { std::cerr << "Wrong " << value << ". Got " << gotVal << ", expected is : " << expectedVal << std::endl; return false; } return true; } }