// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkArrayIterator.h" #include "vtkArrayIteratorIncludes.h" #include "vtkMathUtilities.h" #include "vtkSOADataArrayTemplate.h" // Needed for portable setenv on MSVC... #include "vtksys/SystemTools.hxx" #include #include #include #include namespace { constexpr int numValues = 5; double firstData[numValues] = { 0, 1.5, 2, std::numeric_limits::max(), nan("") }; double secondData[numValues] = { 10, 11.5, 12, std::numeric_limits::infinity(), 15 }; const double voidPointerData[2 * numValues] = { -1, -2.5, std::numeric_limits::infinity(), -4, -5, -6, std::numeric_limits::min(), -8, nan(""), -10 }; const double otherFirst[numValues] = { voidPointerData[0], voidPointerData[2], voidPointerData[4], voidPointerData[6], voidPointerData[8] }; const double otherSecond[numValues] = { voidPointerData[1], voidPointerData[3], voidPointerData[5], voidPointerData[7], voidPointerData[9] }; bool ValuesInfOrNaN(double v1, double v2) { return (std::isinf(v1) && std::isinf(v2)) || (std::isnan(v1) && std::isnan(v2)); } bool HasCorrectValues(vtkDataArray* array, bool useVoidPointerData) { int numberOfComponents = array->GetNumberOfComponents(); const double* first = firstData; const double* second = secondData; if (useVoidPointerData) { first = numberOfComponents == 1 ? voidPointerData : otherFirst; second = otherSecond; } double* values = static_cast(array->GetVoidPointer(0)); double tupleValues[2]; for (int i = 0; i < numValues; i++) { array->GetTuple(i, tupleValues); if ((!vtkMathUtilities::NearlyEqual(tupleValues[0], first[i]) && !ValuesInfOrNaN(tupleValues[0], first[i])) || (numberOfComponents == 2 && (!vtkMathUtilities::NearlyEqual(tupleValues[1], second[i]) && !ValuesInfOrNaN(tupleValues[1], second[i])))) { std::cerr << "Incorrect values returned from GetTypedTuple()\n"; return false; } if ((!vtkMathUtilities::NearlyEqual(values[i * numberOfComponents], first[i]) && !ValuesInfOrNaN(values[i * numberOfComponents], first[i])) || (numberOfComponents == 2 && (!vtkMathUtilities::NearlyEqual(values[i * numberOfComponents + 1], second[i]) && !ValuesInfOrNaN(values[i * numberOfComponents + 1], second[i])))) { std::cerr << "Incorrect values returned from GetVoidPointer()\n"; return false; } } vtkArrayIteratorTemplate* iter = vtkArrayIteratorTemplate::New(); iter->Initialize(array); for (int i = 0; i < numValues; i++) { values = iter->GetTuple(i); if ((!vtkMathUtilities::NearlyEqual(values[0], first[i]) && !ValuesInfOrNaN(values[0], first[i])) || (numberOfComponents == 2 && (!vtkMathUtilities::NearlyEqual(values[1], second[i]) && !ValuesInfOrNaN(values[1], second[i])))) { std::cerr << "Incorrect values returned from NewIterator()\n"; return false; } } iter->Delete(); return true; } } int TestSOADataArray(int, char*[]) { int retVal = 0; vtkSOADataArrayTemplate* array = vtkSOADataArrayTemplate::New(); array->SetNumberOfComponents(2); array->SetNumberOfTuples(numValues); array->SetArray(0, firstData, numValues, false, true); array->SetArray(1, secondData, numValues, false, true); if (!HasCorrectValues(array, false)) { std::cerr << "Setting values through SetArray() failed\n"; retVal++; } // now test setting values through vtkArrayIterator array->Fill(0); vtkArrayIteratorTemplate* iter = vtkArrayIteratorTemplate::New(); iter->Initialize(array); for (int i = 0; i < numValues; i++) { for (int j = 0; j < 2; j++) { double value = (j == 0 ? firstData[i] : secondData[i]); iter->SetValue(i * 2 + j, value); } } iter->Delete(); if (!HasCorrectValues(array, false)) { std::cerr << "Setting values through vtkArrayIterator() failed\n"; retVal++; } // this should switch to AOS storage double* ptr = static_cast(array->GetVoidPointer(0)); std::copy(voidPointerData, voidPointerData + 2 * numValues, ptr); if (!HasCorrectValues(array, true)) { std::cerr << "Setting values through GetVoidPointer() failed\n"; retVal++; } // now go back to SOA format array->SetNumberOfComponents(2); array->SetArray(0, firstData, numValues, false, true); array->SetArray(1, secondData, numValues, false, true); if (!HasCorrectValues(array, false)) { std::cerr << "Setting values through SetArray() failed\n"; retVal++; } // now test setting values through vtkArrayIterator array->Fill(0); iter = vtkArrayIteratorTemplate::New(); iter->Initialize(array); for (int i = 0; i < 2 * numValues; i++) { iter->SetValue(i, voidPointerData[i]); } iter->Delete(); if (!HasCorrectValues(array, true)) { std::cerr << "Setting values through vtkArrayIterator() failed\n"; retVal++; } // now create a new instance from the old (like we would do in a filter) vtkDataArray* newInstance = array->NewInstance(); newInstance->SetNumberOfComponents(2); newInstance->SetNumberOfTuples(numValues); for (vtkIdType i = 0; i < numValues; i++) { double tupleValues[2] = { firstData[i], secondData[i] }; newInstance->SetTuple(i, tupleValues); } if (!HasCorrectValues(newInstance, false)) { std::cerr << "Setting values through SetTuple() failed\n"; retVal++; } newInstance->Fill(0); ptr = static_cast(newInstance->GetVoidPointer(0)); std::copy(voidPointerData, voidPointerData + 2 * numValues, ptr); if (!HasCorrectValues(newInstance, true)) { std::cerr << "Setting values through GetVoidPointer() failed\n"; retVal++; } // now test the use case of a single component. this is the situation // where we don't switch between AOS and SOA storage since we don't need to array->SetNumberOfComponents(1); array->SetNumberOfTuples(numValues); array->SetArray(0, firstData, numValues, false, true); if (!HasCorrectValues(array, false)) { std::cerr << "Setting single component values through SetArray() failed\n"; retVal++; } // now test setting values through vtkArrayIterator array->Fill(0); iter = vtkArrayIteratorTemplate::New(); iter->Initialize(array); for (int i = 0; i < numValues; i++) { iter->SetValue(i, firstData[i]); } iter->Delete(); if (!HasCorrectValues(array, false)) { std::cerr << "Setting single component values through vtkArrayIterator() failed\n"; retVal++; } ptr = static_cast(array->GetVoidPointer(0)); std::copy(voidPointerData, voidPointerData + numValues, ptr); // note that this changes values in firstData if (!HasCorrectValues(array, true)) { std::cerr << "Setting single component values through GetVoidPointer() failed\n"; retVal++; } // now go back to SOA format array->SetNumberOfComponents(1); array->SetArray(0, firstData, numValues, false, true); if (!HasCorrectValues(array, false)) { std::cerr << "Setting single component values through SetArray() failed\n"; retVal++; } // now test setting values through vtkArrayIterator array->Fill(0); iter = vtkArrayIteratorTemplate::New(); iter->Initialize(array); for (int i = 0; i < numValues; i++) { iter->SetValue(i, voidPointerData[i]); } iter->Delete(); if (!HasCorrectValues(array, true)) { std::cerr << "Setting single component values through vtkArrayIterator() failed\n"; retVal++; } newInstance->Delete(); array->Delete(); return retVal; // success has retVal = 0 }