diff --git a/.gitlab/ci/config/sccache.sh b/.gitlab/ci/config/sccache.sh index f05b8e62d..8e6ce6dae 100755 --- a/.gitlab/ci/config/sccache.sh +++ b/.gitlab/ci/config/sccache.sh @@ -2,8 +2,8 @@ set -e -readonly version="nvcc_v3" -readonly sha256sum="d5b56dd9e7d4597f4a47a90d6327e30a259151b59b897607e1804d6d3513f491" +readonly version="nvcc_v4" +readonly sha256sum="260779b4a740fe8373d251d1e318541a98dd5cd2f8051eedd55227a5a852fdf7" readonly filename="sccache-0.2.14-$version-x86_64-unknown-linux-musl" readonly tarball="$filename.tar.gz" diff --git a/.gitlab/ci/ctest_build.cmake b/.gitlab/ci/ctest_build.cmake index 3795d6d00..38a55c8c8 100644 --- a/.gitlab/ci/ctest_build.cmake +++ b/.gitlab/ci/ctest_build.cmake @@ -24,7 +24,14 @@ ctest_build(APPEND RETURN_VALUE build_result) if(NOT DEFINED ENV{GITLAB_CI_EMULATION}) - ctest_submit(PARTS Build) + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.15) + ctest_submit(PARTS Build BUILD_ID build_id) + message(STATUS "Build submission build_id: ${build_id}") + else() + ctest_submit(PARTS Build) + endif() + + endif() if (build_result) diff --git a/.gitlab/ci/ctest_configure.cmake b/.gitlab/ci/ctest_configure.cmake index e426ae0e6..6169dcbff 100644 --- a/.gitlab/ci/ctest_configure.cmake +++ b/.gitlab/ci/ctest_configure.cmake @@ -38,8 +38,15 @@ ctest_configure(APPEND # We can now submit because we've configured. if(NOT DEFINED ENV{GITLAB_CI_EMULATION}) - ctest_submit(PARTS Update) - ctest_submit(PARTS Configure) + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.15) + ctest_submit(PARTS Update BUILD_ID build_id) + message(STATUS "Update submission build_id: ${build_id}") + ctest_submit(PARTS Configure BUILD_ID build_id) + message(STATUS "Configure submission build_id: ${build_id}") + else() + ctest_submit(PARTS Update) + ctest_submit(PARTS Configure) + endif() endif() if (configure_result) diff --git a/.gitlab/ci/ctest_memcheck.cmake b/.gitlab/ci/ctest_memcheck.cmake index 529a57817..abf80a34c 100644 --- a/.gitlab/ci/ctest_memcheck.cmake +++ b/.gitlab/ci/ctest_memcheck.cmake @@ -48,7 +48,8 @@ ctest_memcheck( EXCLUDE "${test_exclusions}" DEFECT_COUNT defects) -ctest_submit(PARTS Memcheck) +ctest_submit(PARTS Memcheck BUILD_ID build_id) + message(STATUS "Memcheck submission build_id: ${build_id}") if (defects) message(FATAL_ERROR "Found ${defects} memcheck defects") diff --git a/.gitlab/ci/ctest_test.cmake b/.gitlab/ci/ctest_test.cmake index 9cf46e975..4ff74a4a9 100644 --- a/.gitlab/ci/ctest_test.cmake +++ b/.gitlab/ci/ctest_test.cmake @@ -35,7 +35,8 @@ ctest_test(APPEND ) if(NOT DEFINED ENV{GITLAB_CI_EMULATION}) - ctest_submit(PARTS Test) + ctest_submit(PARTS Test BUILD_ID build_id) + message(STATUS "Test submission build_id: ${build_id}") endif() if (test_result) diff --git a/CMake/VTKmWrappers.cmake b/CMake/VTKmWrappers.cmake index 08c359053..7e1f1b728 100644 --- a/CMake/VTKmWrappers.cmake +++ b/CMake/VTKmWrappers.cmake @@ -378,7 +378,7 @@ function(vtkm_library) EXTENDS_VTKM DEVICE_SOURCES ${VTKm_LIB_DEVICE_SOURCES} ) - if(NOT VTKm_USE_DEFAULT_SYMBOL_VISIBILITY) + if(VTKm_HIDE_PRIVATE_SYMBOLS) set_property(TARGET ${lib_name} PROPERTY CUDA_VISIBILITY_PRESET "hidden") set_property(TARGET ${lib_name} PROPERTY CXX_VISIBILITY_PRESET "hidden") endif() diff --git a/CMake/testing/VTKmTestWrappers.cmake b/CMake/testing/VTKmTestWrappers.cmake index 5ea49b5b6..8ec6042f9 100644 --- a/CMake/testing/VTKmTestWrappers.cmake +++ b/CMake/testing/VTKmTestWrappers.cmake @@ -134,7 +134,7 @@ function(vtkm_unit_tests) endif() vtkm_add_target_information(${test_prog} DEVICE_SOURCES ${device_sources}) - if(NOT VTKm_USE_DEFAULT_SYMBOL_VISIBILITY) + if(VTKm_HIDE_PRIVATE_SYMBOLS) set_property(TARGET ${test_prog} PROPERTY CUDA_VISIBILITY_PRESET "hidden") set_property(TARGET ${test_prog} PROPERTY CXX_VISIBILITY_PRESET "hidden") endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 093c223a5..aa1b0aeaa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,7 +118,7 @@ vtkm_option(VTKm_INSTALL_ONLY_LIBRARIES "install only vtk-m libraries and no hea # rather than exporting all symbols. This flag is added so that consumers # which require static builds can force all symbols on, which is something # VTK does. -vtkm_option(VTKm_USE_DEFAULT_SYMBOL_VISIBILITY "Don't explicitly hide symbols from libraries." OFF) +vtkm_option(VTKm_HIDE_PRIVATE_SYMBOLS "Hide symbols from libraries." ON) vtkm_option(BUILD_SHARED_LIBS "Build VTK-m with shared libraries" OFF) set(VTKm_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) @@ -136,7 +136,7 @@ mark_as_advanced( VTKm_ENABLE_LOGGING VTKm_NO_ASSERT VTKm_INSTALL_ONLY_LIBRARIES - VTKm_USE_DEFAULT_SYMBOL_VISIBILITY + VTKm_HIDE_PRIVATE_SYMBOLS VTKm_ENABLE_DEVELOPER_FLAGS VTKm_NO_INSTALL_README_LICENSE ) diff --git a/benchmarking/BenchmarkDeviceAdapter.cxx b/benchmarking/BenchmarkDeviceAdapter.cxx index 637a91982..453ce57ff 100644 --- a/benchmarking/BenchmarkDeviceAdapter.cxx +++ b/benchmarking/BenchmarkDeviceAdapter.cxx @@ -404,7 +404,7 @@ template void BenchCopy(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); state.SetLabel(SizeAndValuesString(numBytes, numValues)); @@ -580,7 +580,7 @@ template void BenchFillArrayHandle(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); state.SetLabel(SizeAndValuesString(numBytes, numValues)); @@ -610,7 +610,7 @@ VTKM_BENCHMARK_TEMPLATES_OPTS(BenchFillArrayHandle, void BenchFillBitFieldBool(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numBits = numBytes * CHAR_BIT; const bool value = state.range(1) != 0; @@ -640,7 +640,7 @@ template void BenchFillBitFieldMask(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numBits = numBytes * CHAR_BIT; const WordType mask = static_cast(0x1); @@ -717,7 +717,7 @@ template void BenchReduce(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); state.SetLabel(SizeAndValuesString(numBytes, numValues)); @@ -752,10 +752,10 @@ void BenchReduceByKey(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); - const vtkm::Id percentKeys = state.range(1); + const vtkm::Id percentKeys = static_cast(state.range(1)); const vtkm::Id numKeys = std::max((numValues * percentKeys) / 100, vtkm::Id{ 1 }); { @@ -807,7 +807,7 @@ template void BenchScanExclusive(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); state.SetLabel(SizeAndValuesString(numBytes, numValues)); @@ -841,7 +841,7 @@ template void BenchScanExtended(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); state.SetLabel(SizeAndValuesString(numBytes, numValues)); @@ -875,7 +875,7 @@ template void BenchScanInclusive(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); state.SetLabel(SizeAndValuesString(numBytes, numValues)); @@ -909,7 +909,7 @@ template void BenchSort(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); state.SetLabel(SizeAndValuesString(numBytes, numValues)); @@ -950,7 +950,7 @@ void BenchSortByKey(benchmark::State& state) const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); - const vtkm::Id percentKeys = state.range(1); + const vtkm::Id percentKeys = static_cast(state.range(1)); const vtkm::Id numKeys = std::max((numValues * percentKeys) / 100, vtkm::Id{ 1 }); { @@ -1005,7 +1005,7 @@ template void BenchStableSortIndices(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); state.SetLabel(SizeAndValuesString(numBytes, numValues)); @@ -1042,10 +1042,10 @@ template void BenchStableSortIndicesUnique(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); - const vtkm::Id percentUnique = state.range(1); + const vtkm::Id percentUnique = static_cast(state.range(1)); const vtkm::Id numUnique = std::max((numValues * percentUnique) / 100, vtkm::Id{ 1 }); { @@ -1105,10 +1105,10 @@ template void BenchUnique(benchmark::State& state) { const vtkm::cont::DeviceAdapterId device = Config.Device; - const vtkm::Id numBytes = state.range(0); + const vtkm::Id numBytes = static_cast(state.range(0)); const vtkm::Id numValues = BytesToWords(numBytes); - const vtkm::Id percentUnique = state.range(1); + const vtkm::Id percentUnique = static_cast(state.range(1)); const vtkm::Id numUnique = std::max((numValues * percentUnique) / 100, vtkm::Id{ 1 }); { diff --git a/benchmarking/CMakeLists.txt b/benchmarking/CMakeLists.txt index a504d395e..bbfbd8c2a 100644 --- a/benchmarking/CMakeLists.txt +++ b/benchmarking/CMakeLists.txt @@ -52,7 +52,7 @@ set(VTKm_BENCHS_RANGE_UPPER_BOUNDARY 134217728 CACHE STRING "Biggest sample for mark_as_advanced(VTKm_BENCHS_RANGE_LOWER_BOUNDARY VTKm_BENCHS_RANGE_UPPER_BOUNDARY) foreach (benchmark ${benchmarks}) - add_benchmark(NAME ${benchmark} FILE ${benchmark}.cxx LIBS vtkm_source vtkm_filter) + add_benchmark(NAME ${benchmark} FILE ${benchmark}.cxx LIBS vtkm_source vtkm_filter vtkm_io) endforeach () target_compile_definitions(BenchmarkDeviceAdapter PUBLIC VTKm_BENCHS_RANGE_LOWER_BOUNDARY=${VTKm_BENCHS_RANGE_LOWER_BOUNDARY}) diff --git a/docs/changelog/coordinate-transform-results.md b/docs/changelog/coordinate-transform-results.md new file mode 100644 index 000000000..124fb79cc --- /dev/null +++ b/docs/changelog/coordinate-transform-results.md @@ -0,0 +1,13 @@ +# Result DataSet of coordinate transform has its CoordinateSystem changed + +When you run one of the coordinate transform filters, +`CylindricalCoordinateTransform` or `SphericalCoordinateTransform`, the +transform coordiantes are placed as the first `CoordinateSystem` in the +returned `DataSet`. This means that after running this filter, the data +will be moved to this new coordinate space. + +Previously, the result of these filters was just placed in a named `Field` +of the output. This caused some confusion because the filter did not seem +to have any effect (unless you knew to modify the output data). Not using +the result as the coordinate system seems like a dubious use case (and not +hard to work around), so this is much better behavior. diff --git a/docs/changelog/dataset-unique-field-names.md b/docs/changelog/dataset-unique-field-names.md new file mode 100644 index 000000000..90a1f4af7 --- /dev/null +++ b/docs/changelog/dataset-unique-field-names.md @@ -0,0 +1,17 @@ +# DataSet now only allows unique field names + +When you add a `vtkm::cont::Field` to a `vtkm::cont::DataSet`, it now +requires every `Field` to have a unique name. When you attempt to add a +`Field` to a `DataSet` that already has a `Field` of the same name and +association, the old `Field` is removed and replaced with the new `Field`. + +You are allowed, however, to have two `Field`s with the same name but +different associations. For example, you could have a point `Field` named +"normals" and also have a cell `Field` named "normals" in the same +`DataSet`. + +This new behavior matches how VTK's data sets manage fields. + +The old behavior allowed you to add multiple `Field`s with the same name, +but it would be unclear which one you would get if you asked for a `Field` +by name. diff --git a/docs/changelog/filter-specifies-own-field-types.md b/docs/changelog/filter-specifies-own-field-types.md new file mode 100644 index 000000000..6dc50fab1 --- /dev/null +++ b/docs/changelog/filter-specifies-own-field-types.md @@ -0,0 +1,8 @@ +# Filters specify their own field types + +Previously, the policy specified which field types the filter should +operate on. The filter could remove some types, but it was not able to +add any types. + +This is backward. Instead, the filter should specify what types its +supports and the policy may cull out some of those. diff --git a/docs/changelog/flying-edges.md b/docs/changelog/flying-edges.md new file mode 100644 index 000000000..b174bbb37 --- /dev/null +++ b/docs/changelog/flying-edges.md @@ -0,0 +1,15 @@ +# Flying Edges + +Added the flying edges contouring algorithm to VTK-m. This algorithm only +works on structured grids, but operates much faster than the traditional +Marching Cubes algorithm. + +The speed of VTK-m's flying edges is comprable to VTK's running on the same +CPUs. VTK-m's implementation also works well on CUDA hardware. + +The Flying Edges algorithm was introduced in this paper: + +Schroeder, W.; Maynard, R. & Geveci, B. +"Flying edges: A high-performance scalable isocontouring algorithm." +Large Data Analysis and Visualization (LDAV), 2015. +DOI 10.1109/LDAV.2015.7348069 diff --git a/docs/changelog/image_io.md b/docs/changelog/image_io.md new file mode 100644 index 000000000..d4ca4d03c --- /dev/null +++ b/docs/changelog/image_io.md @@ -0,0 +1,32 @@ +# Implemented PNG/PPM image Readers/Writers + +The original implementation of writing image data was only performed as a +proxy through the Canvas rendering class. In order to implement true support +for image-based regression testing, this interface needed to be expanded upon +to support reading/writing arbitrary image data and storing it in a `vtkm::DataSet`. +Using the new `vtkm::io::PNGReader` and `vtkm::io::PPMReader` it is possible +to read data from files and Cavases directly and store them as a point field +in a 2D uniform `vtkm::DataSet` + +```cpp +auto reader = vtkm::io::PNGReader(); +auto imageDataSet = reader.ReadFromFile("read_image.png"); +``` + +Similarly, the new `vtkm::io::PNGWriter` and `vtkm::io::PPMWriter` make it possible +to write out a 2D uniform `vtkm::DataSet` directly to a file. + +```cpp +auto writer = vtkm::io::PNGWriter(); +writer.WriteToFile("write_image.png", imageDataSet); +``` + +If canvas data is to be written out, the reader provides a method for converting +a canvas's data to a `vtkm::DataSet`. + +```cpp +auto reader = vtkm::io::PNGReader(); +auto dataSet = reader.CreateImageDataSet(canvas); +auto writer = vtkm::io::PNGWriter(); +writer.WriteToFile("output.png", dataSet); +``` diff --git a/docs/changelog/no-cell-op-errors.md b/docs/changelog/no-cell-op-errors.md new file mode 100644 index 000000000..a9ed3608b --- /dev/null +++ b/docs/changelog/no-cell-op-errors.md @@ -0,0 +1,72 @@ +# Avoid raising errors when operating on cells + +Cell operations like interpolate and finding parametric coordinates can +fail under certain conditions. The previous behavior was to call +`RaiseError` on the worklet. By design, this would cause the worklet +execution to fail. However, that makes the worklet unstable for a conditin +that might be relatively common in data. For example, you wouldn't want a +large streamline worklet to fail just because one cell was not found +correctly. + +To work around this, many of the cell operations in the execution +environment have been changed to return an error code rather than raise an +error in the worklet. + +## Error Codes + +To support cell operations efficiently returning errors, a new enum named +`vtkm::ErrorCode` is available. This is the current implementation of +`ErrorCode`. + +``` cpp +enum class ErrorCode +{ + Success, + InvalidShapeId, + InvalidNumberOfPoints, + WrongShapeIdForTagType, + InvalidPointId, + InvalidEdgeId, + InvalidFaceId, + SolutionDidNotConverge, + MatrixFactorizationFailed, + DegenerateCellDetected, + MalformedCellDetected, + OperationOnEmptyCell, + CellNotFound, + + UnknownError +}; +``` + +A convenience function named `ErrorString` is provided to make it easy to +convert the `ErrorCode` to a descriptive string that can be placed in an +error. + +## New Calling Specification + +Previously, most execution environment functions took as an argument the +worklet calling the function. This made it possible to call `RaiseError` on +the worklet. The result of the operation was typically returned. For +example, here is how the _old_ version of interpolate was called. + +``` cpp +FieldType interpolatedValue = + vtkm::exec::CellInterpolate(fieldValues, pcoord, shape, worklet); +``` + +The worklet is now no longer passed to the function. It is no longer needed +because an error is never directly raised. Instead, an `ErrorCode` is +returned from the function. Because the `ErrorCode` is returned, the +computed result of the function is returned by passing in a reference to a +variable. This is usually placed as the last argument (where the worklet +used to be). here is the _new_ version of how interpolate is called. + +``` cpp +FieldType interpolatedValue; +vtkm::ErrorCode result = + vtkm::exec::CellInterpolate(fieldValues, pcoord, shape, interpolatedValue); +``` + +The success of the operation can be determined by checking that the +returned `ErrorCode` is equal to `vtkm::ErrorCode::Success`. diff --git a/docs/changelog/remove-opengl-rendering-classes.md b/docs/changelog/remove-opengl-rendering-classes.md new file mode 100644 index 000000000..e49c6c6f6 --- /dev/null +++ b/docs/changelog/remove-opengl-rendering-classes.md @@ -0,0 +1,13 @@ +# Removed OpenGL Rendering Classes + +When the rendering library was first built, OpenGL was used to implement +the components (windows, mappers, annotation, etc.). However, as the native +ray casting became viable, the majority of the work has focused on using +that. Since then, the original OpenGL classes have been largely ignored. + +It has for many months been determined that it is not work attempting to +maintain two different versions of the rendering libraries as features are +added and changed. Thus, the OpenGL classes have fallen out of date and did +not actually work. + +These classes have finally been officially removed. diff --git a/docs/changelog/vtk-io-in-library.md b/docs/changelog/vtk-io-in-library.md new file mode 100644 index 000000000..37ed2f4f9 --- /dev/null +++ b/docs/changelog/vtk-io-in-library.md @@ -0,0 +1,13 @@ +# Move VTK file readers and writers into vtkm_io + +The legacy VTK file reader and writer were created back when VTK-m was a +header-only library. Things have changed and we now compile quite a bit of +code into libraries. At this point, there is no reason why the VTK file +reader/writer should be any different. + +Thus, `VTKDataSetReader`, `VTKDataSetWriter`, and several supporting +classes are now compiled into the `vtkm_io` library. Also similarly updated +`BOVDataSetReader` for good measure. + +As a side effect, code using VTK-m will need to link to `vtkm_io` if they +are using any readers or writers. diff --git a/examples/clipping/CMakeLists.txt b/examples/clipping/CMakeLists.txt index 6f26353ce..46bc5bf18 100644 --- a/examples/clipping/CMakeLists.txt +++ b/examples/clipping/CMakeLists.txt @@ -14,7 +14,7 @@ project(Clipping CXX) find_package(VTKm REQUIRED QUIET) add_executable(Clipping Clipping.cxx) -target_link_libraries(Clipping PRIVATE vtkm_filter) +target_link_libraries(Clipping PRIVATE vtkm_filter vtkm_io) vtkm_add_target_information(Clipping DROP_UNUSED_SYMBOLS MODIFY_CUDA_FLAGS diff --git a/examples/contour_tree_augmented/ContourTreeApp.cxx b/examples/contour_tree_augmented/ContourTreeApp.cxx index a0037977e..056625c1f 100644 --- a/examples/contour_tree_augmented/ContourTreeApp.cxx +++ b/examples/contour_tree_augmented/ContourTreeApp.cxx @@ -130,7 +130,7 @@ public: } else { - return (it - this->mCLOptions.begin()); + return static_cast(it - this->mCLOptions.begin()); } } diff --git a/examples/hello_worklet/CMakeLists.txt b/examples/hello_worklet/CMakeLists.txt index 806f69b57..eff9dfcae 100644 --- a/examples/hello_worklet/CMakeLists.txt +++ b/examples/hello_worklet/CMakeLists.txt @@ -14,7 +14,7 @@ project(HelloWorklet CXX) find_package(VTKm REQUIRED QUIET) add_executable(HelloWorklet HelloWorklet.cxx) -target_link_libraries(HelloWorklet PRIVATE vtkm_filter) +target_link_libraries(HelloWorklet PRIVATE vtkm_filter vtkm_io) vtkm_add_target_information(HelloWorklet DROP_UNUSED_SYMBOLS MODIFY_CUDA_FLAGS diff --git a/examples/mesh_quality/CMakeLists.txt b/examples/mesh_quality/CMakeLists.txt index 3987f3da2..6308b37b6 100644 --- a/examples/mesh_quality/CMakeLists.txt +++ b/examples/mesh_quality/CMakeLists.txt @@ -26,7 +26,7 @@ project(MeshQuality CXX) find_package(VTKm REQUIRED QUIET) add_executable(MeshQuality MeshQuality.cxx) -target_link_libraries(MeshQuality PRIVATE vtkm_filter) +target_link_libraries(MeshQuality PRIVATE vtkm_filter vtkm_io) if(TARGET vtkm::tbb) target_compile_definitions(MeshQuality PRIVATE BUILDING_TBB_VERSION) diff --git a/examples/multi_backend/MultiDeviceGradient.hxx b/examples/multi_backend/MultiDeviceGradient.hxx index 12d0daecf..8ff601c92 100644 --- a/examples/multi_backend/MultiDeviceGradient.hxx +++ b/examples/multi_backend/MultiDeviceGradient.hxx @@ -62,19 +62,19 @@ void process_partition_tbb(RuntimeTaskQueue& queue) void process_partition_openMP(RuntimeTaskQueue& queue) { - //Step 1. Set the device adapter to this thread to TBB. + //Step 1. Set the device adapter to this thread to openMP. //This makes sure that any vtkm::filters used by our - //task operate only on TBB. The "global" thread tracker + //task operate only on openMP. The "global" thread tracker //is actually thread-local, so we can use that. // vtkm::cont::GetRuntimeDeviceTracker().ForceDevice(vtkm::cont::DeviceAdapterTagOpenMP{}); while (queue.hasTasks()) { - //Step 2. Get the task to run on TBB + //Step 2. Get the task to run on openMP auto task = queue.pop(); - //Step 3. Run the task on TBB. We check the validity + //Step 3. Run the task on openMP. We check the validity //of the task since we could be given an empty task //when the queue is empty and we are shutting down if (task != nullptr) @@ -84,7 +84,8 @@ void process_partition_openMP(RuntimeTaskQueue& queue) //Step 4. Notify the queue that we finished processing this task queue.completedTask(); - std::cout << "finished a partition on tbb (" << std::this_thread::get_id() << ")" << std::endl; + std::cout << "finished a partition on openMP (" << std::this_thread::get_id() << ")" + << std::endl; } } diff --git a/examples/particle_advection/CMakeLists.txt b/examples/particle_advection/CMakeLists.txt index 5c0b9a006..7866c56c6 100644 --- a/examples/particle_advection/CMakeLists.txt +++ b/examples/particle_advection/CMakeLists.txt @@ -14,7 +14,7 @@ project(ParticleAdvection CXX) find_package(VTKm REQUIRED QUIET) add_executable(Particle_Advection ParticleAdvection.cxx) -target_link_libraries(Particle_Advection PRIVATE vtkm_filter) +target_link_libraries(Particle_Advection PRIVATE vtkm_filter vtkm_io) vtkm_add_target_information(Particle_Advection DROP_UNUSED_SYMBOLS MODIFY_CUDA_FLAGS DEVICE_SOURCES ParticleAdvection.cxx) diff --git a/examples/redistribute_points/CMakeLists.txt b/examples/redistribute_points/CMakeLists.txt index 8a2ff67e9..59948d79f 100644 --- a/examples/redistribute_points/CMakeLists.txt +++ b/examples/redistribute_points/CMakeLists.txt @@ -13,7 +13,7 @@ project(RedistributePoints CXX) #Find the VTK-m package find_package(VTKm REQUIRED QUIET) add_executable(RedistributePoints RedistributePoints.cxx RedistributePoints.h) -target_link_libraries(RedistributePoints PRIVATE vtkm_filter) +target_link_libraries(RedistributePoints PRIVATE vtkm_filter vtkm_io) vtkm_add_target_information(RedistributePoints DROP_UNUSED_SYMBOLS MODIFY_CUDA_FLAGS DEVICE_SOURCES RedistributePoints.cxx) diff --git a/examples/temporal_advection/CMakeLists.txt b/examples/temporal_advection/CMakeLists.txt index 027a24f7a..eeed9ff44 100644 --- a/examples/temporal_advection/CMakeLists.txt +++ b/examples/temporal_advection/CMakeLists.txt @@ -19,4 +19,4 @@ add_executable(Temporal_Advection TemporalAdvection.cxx) vtkm_add_target_information(Temporal_Advection DROP_UNUSED_SYMBOLS MODIFY_CUDA_FLAGS DEVICE_SOURCES TemporalAdvection.cxx) -target_link_libraries(Temporal_Advection PRIVATE vtkm_filter) +target_link_libraries(Temporal_Advection PRIVATE vtkm_filter vtkm_io) diff --git a/examples/tetrahedra/CMakeLists.txt b/examples/tetrahedra/CMakeLists.txt index c894daee5..7bd8a4e67 100644 --- a/examples/tetrahedra/CMakeLists.txt +++ b/examples/tetrahedra/CMakeLists.txt @@ -14,10 +14,10 @@ project(Tetrahedra CXX) find_package(VTKm REQUIRED QUIET) add_executable(Tetrahedralize Tetrahedralize.cxx) -target_link_libraries(Tetrahedralize PRIVATE vtkm_filter) +target_link_libraries(Tetrahedralize PRIVATE vtkm_filter vtkm_io) add_executable(Triangulate Triangulate.cxx) -target_link_libraries(Triangulate PRIVATE vtkm_filter) +target_link_libraries(Triangulate PRIVATE vtkm_filter vtkm_io) vtkm_add_target_information(Tetrahedralize Triangulate DROP_UNUSED_SYMBOLS diff --git a/vtkm/Deprecated.h b/vtkm/Deprecated.h index fe08a6c14..45493bbae 100644 --- a/vtkm/Deprecated.h +++ b/vtkm/Deprecated.h @@ -60,7 +60,9 @@ // [[deprecated]] is supported, then VTK_M_DEPRECATED_ATTRIBUTE_SUPPORTED will get defined. #ifndef VTK_M_DEPRECATED_ATTRIBUTE_SUPPORTED -#if __cplusplus >= 201402L && !defined(__NVCC__) +#if defined(__NVCC__) +// Currently nvcc has zero support deprecated attributes +#elif __cplusplus >= 201402L // C++14 and better supports [[deprecated]] // Except in these cases: @@ -68,7 +70,6 @@ #define VTK_M_DEPRECATED_ATTRIBUTE_SUPPORTED #elif defined(VTKM_GCC) - // GCC has supported [[deprecated]] since version 5.0, but using it on enum was not // supported until 6.0. So we have to make a special case to only use it for high // enough revisions. @@ -76,14 +77,14 @@ #define VTK_M_DEPRECATED_ATTRIBUTE_SUPPORTED #endif // Too old GCC -#elif defined(__has_cpp_attribute) && !defined(__NVCC__) +#elif defined(__has_cpp_attribute) #if __has_cpp_attribute(deprecated) // Compiler not fully C++14 compliant, but it reports to support [[deprecated]] #define VTK_M_DEPRECATED_ATTRIBUTE_SUPPORTED #endif // __has_cpp_attribute(deprecated) -#elif defined(VTKM_MSVC) && (_MSC_VER >= 1900) && !defined(__NVCC__) +#elif defined(VTKM_MSVC) && (_MSC_VER >= 1900) #define VTK_M_DEPRECATED_ATTRIBUTE_SUPPORTED diff --git a/vtkm/cont/CellLocatorUniformGrid.cxx b/vtkm/cont/CellLocatorUniformGrid.cxx index 3310ee3fa..d39535d2a 100644 --- a/vtkm/cont/CellLocatorUniformGrid.cxx +++ b/vtkm/cont/CellLocatorUniformGrid.cxx @@ -54,9 +54,10 @@ void CellLocatorUniformGrid::Build() } UniformType uniformCoords = coords.GetData().Cast(); - this->Origin = uniformCoords.ReadPortal().GetOrigin(); + auto coordsPortal = uniformCoords.ReadPortal(); + this->Origin = coordsPortal.GetOrigin(); - vtkm::Vec3f spacing = uniformCoords.ReadPortal().GetSpacing(); + vtkm::Vec3f spacing = coordsPortal.GetSpacing(); vtkm::Vec3f unitLength; unitLength[0] = static_cast(this->PointDims[0] - 1); unitLength[1] = static_cast(this->PointDims[1] - 1); diff --git a/vtkm/cont/CellSetExplicit.h b/vtkm/cont/CellSetExplicit.h index 4e92fb1a3..261ea25bf 100644 --- a/vtkm/cont/CellSetExplicit.h +++ b/vtkm/cont/CellSetExplicit.h @@ -186,6 +186,9 @@ public: VTKM_CONT vtkm::IdComponent GetNumberOfPointsInCell(vtkm::Id cellid) const override; VTKM_CONT void GetCellPointIds(vtkm::Id id, vtkm::Id* ptids) const override; + VTKM_CONT typename vtkm::cont::ArrayHandle::ReadPortalType + ShapesReadPortal() const; + VTKM_CONT vtkm::UInt8 GetCellShape(vtkm::Id cellid) const override; template diff --git a/vtkm/cont/CellSetExplicit.hxx b/vtkm/cont/CellSetExplicit.hxx index 27789b8e1..815df2f8d 100644 --- a/vtkm/cont/CellSetExplicit.hxx +++ b/vtkm/cont/CellSetExplicit.hxx @@ -9,7 +9,7 @@ //============================================================================ #ifndef vtk_m_cont_CellSetExplicit_hxx #define vtk_m_cont_CellSetExplicit_hxx - +#include #include #include @@ -176,14 +176,22 @@ vtkm::IdComponent CellSetExplicit template VTKM_CONT +typename vtkm::cont::ArrayHandle::ReadPortalType +CellSetExplicit::ShapesReadPortal() const +{ + return this->Data->CellPointIds.Shapes.ReadPortal(); +} + +template +VTKM_CONT +VTKM_DEPRECATED(1.6, "Calling GetCellShape(cellid) is a performance bug. Call ShapesReadPortal() and loop over the .Get.") vtkm::UInt8 CellSetExplicit ::GetCellShape(vtkm::Id cellid) const { - // Looping over GetCellShape(cellid) is a performance bug. - // Don't know quite what to do about it right now. - return this->Data->CellPointIds.Shapes.ReadPortal().Get(cellid); + return this->ShapesReadPortal().Get(cellid); } + template template VTKM_CONT diff --git a/vtkm/cont/testing/TestingCellLocatorUniformBins.h b/vtkm/cont/testing/TestingCellLocatorUniformBins.h index 23e7e01c9..0380f277a 100644 --- a/vtkm/cont/testing/TestingCellLocatorUniformBins.h +++ b/vtkm/cont/testing/TestingCellLocatorUniformBins.h @@ -204,11 +204,14 @@ void TestCellLocator(const vtkm::Vec& dim, vtkm::Id number vtkm::worklet::DispatcherMapField dispatcher; dispatcher.Invoke(points, locator, cellIds, pcoords); + auto cellIdsPortal = cellIds.ReadPortal(); + auto expCellIdsPortal = expCellIds.ReadPortal(); + auto pcoordsPortal = pcoords.ReadPortal(); + auto expPCoordsPortal = expPCoords.ReadPortal(); for (vtkm::Id i = 0; i < numberOfPoints; ++i) { - VTKM_TEST_ASSERT(cellIds.ReadPortal().Get(i) == expCellIds.ReadPortal().Get(i), - "Incorrect cell ids"); - VTKM_TEST_ASSERT(test_equal(pcoords.ReadPortal().Get(i), expPCoords.ReadPortal().Get(i), 1e-3), + VTKM_TEST_ASSERT(cellIdsPortal.Get(i) == expCellIdsPortal.Get(i), "Incorrect cell ids"); + VTKM_TEST_ASSERT(test_equal(pcoordsPortal.Get(i), expPCoordsPortal.Get(i), 1e-3), "Incorrect parameteric coordinates"); } } diff --git a/vtkm/cont/testing/TestingDataSetExplicit.h b/vtkm/cont/testing/TestingDataSetExplicit.h index 469fd9e38..6d7896935 100644 --- a/vtkm/cont/testing/TestingDataSetExplicit.h +++ b/vtkm/cont/testing/TestingDataSetExplicit.h @@ -42,9 +42,10 @@ private: return false; } + auto ahPortal = ah.ReadPortal(); for (vtkm::Id i = 0; i < size; ++i) { - if (ah.ReadPortal().Get(i) != expected[i]) + if (ahPortal.Get(i) != expected[i]) { return false; } @@ -115,6 +116,7 @@ private: VTKM_TEST_ASSERT(conn.GetNumberOfValues() == connectivitySize, "Connectivity array wrong size."); vtkm::Id connectivityIndex = 0; + auto connPortal = conn.ReadPortal(); for (vtkm::Id pointIndex = 0; pointIndex < numPoints; pointIndex++) { vtkm::IdComponent numIncidentCells = correctNumIndices[pointIndex]; @@ -125,7 +127,7 @@ private: } for (vtkm::IdComponent cellIndex = 0; cellIndex < numIncidentCells; cellIndex++) { - vtkm::Id expectedCell = conn.ReadPortal().Get(connectivityIndex + cellIndex); + vtkm::Id expectedCell = connPortal.Get(connectivityIndex + cellIndex); std::set::iterator foundCell = correctIncidentCells.find(expectedCell); VTKM_TEST_ASSERT(foundCell != correctIncidentCells.end(), "An incident cell in the connectivity list is wrong or repeated."); diff --git a/vtkm/cont/testing/UnitTestArrayHandleConcatenate.cxx b/vtkm/cont/testing/UnitTestArrayHandleConcatenate.cxx index 329b5dae2..1becee061 100644 --- a/vtkm/cont/testing/UnitTestArrayHandleConcatenate.cxx +++ b/vtkm/cont/testing/UnitTestArrayHandleConcatenate.cxx @@ -36,9 +36,10 @@ void TestArrayHandleConcatenate() array5 = vtkm::cont::make_ArrayHandleConcatenate(array3, array4); } + auto array5Portal = array5.ReadPortal(); for (vtkm::Id index = 0; index < array5.GetNumberOfValues(); index++) { - std::cout << array5.ReadPortal().Get(index) << std::endl; + std::cout << array5Portal.Get(index) << std::endl; } } @@ -59,8 +60,9 @@ void TestConcatenateEmptyArray() ArrayConcat arrConc(arr2, arr1); ArrayConcat2 arrConc2(arrConc, arr3); + auto arrConc2Portal = arrConc2.ReadPortal(); for (vtkm::Id i = 0; i < arrConc2.GetNumberOfValues(); i++) - std::cout << arrConc2.ReadPortal().Get(i) << std::endl; + std::cout << arrConc2Portal.Get(i) << std::endl; } } // namespace UnitTestArrayHandleIndexNamespace diff --git a/vtkm/cont/testing/UnitTestArrayHandleCounting.cxx b/vtkm/cont/testing/UnitTestArrayHandleCounting.cxx index 4aca0a7eb..b5e975fa3 100644 --- a/vtkm/cont/testing/UnitTestArrayHandleCounting.cxx +++ b/vtkm/cont/testing/UnitTestArrayHandleCounting.cxx @@ -102,14 +102,17 @@ struct TemplatedTests "Counting array using raw array handle + tag has wrong size."); ValueType properValue = startingValue; + auto arrayConstPortal = arrayConst.ReadPortal(); + auto arrayMakePortal = arrayMake.ReadPortal(); + auto arrayHandlePortal = arrayHandle.ReadPortal(); for (vtkm::Id index = 0; index < ARRAY_SIZE; index++) { - VTKM_TEST_ASSERT(arrayConst.ReadPortal().Get(index) == properValue, + VTKM_TEST_ASSERT(arrayConstPortal.Get(index) == properValue, "Counting array using constructor has unexpected value."); - VTKM_TEST_ASSERT(arrayMake.ReadPortal().Get(index) == properValue, + VTKM_TEST_ASSERT(arrayMakePortal.Get(index) == properValue, "Counting array using make has unexpected value."); - VTKM_TEST_ASSERT(arrayHandle.ReadPortal().Get(index) == properValue, + VTKM_TEST_ASSERT(arrayHandlePortal.Get(index) == properValue, "Counting array using raw array handle + tag has unexpected value."); properValue = properValue + step; } @@ -128,6 +131,7 @@ void TestArrayHandleCounting() TemplatedTests()(StringInt(10), StringInt(2)); } + } // namespace UnitTestArrayHandleCountingNamespace int UnitTestArrayHandleCounting(int argc, char* argv[]) diff --git a/vtkm/cont/testing/UnitTestArrayHandleImplicit.cxx b/vtkm/cont/testing/UnitTestArrayHandleImplicit.cxx index 13c75b77c..fa7d79f0f 100644 --- a/vtkm/cont/testing/UnitTestArrayHandleImplicit.cxx +++ b/vtkm/cont/testing/UnitTestArrayHandleImplicit.cxx @@ -42,12 +42,13 @@ struct ImplicitTests using ImplicitHandle = vtkm::cont::ArrayHandleImplicit; - ImplicitHandle implict = vtkm::cont::make_ArrayHandleImplicit(functor, ARRAY_SIZE); + ImplicitHandle implicit = vtkm::cont::make_ArrayHandleImplicit(functor, ARRAY_SIZE); //verify that the control portal works + auto implicitPortal = implicit.ReadPortal(); for (int i = 0; i < ARRAY_SIZE; ++i) { - const ValueType v = implict.ReadPortal().Get(i); + const ValueType v = implicitPortal.Get(i); const ValueType correct_value = functor(i); VTKM_TEST_ASSERT(v == correct_value, "Implicit Handle Failed"); } @@ -56,7 +57,7 @@ struct ImplicitTests vtkm::cont::Token token; using Device = vtkm::cont::DeviceAdapterTagSerial; using CEPortal = typename ImplicitHandle::template ExecutionTypes::PortalConst; - CEPortal execPortal = implict.PrepareForInput(Device(), token); + CEPortal execPortal = implicit.PrepareForInput(Device(), token); for (int i = 0; i < ARRAY_SIZE; ++i) { const ValueType v = execPortal.Get(i); diff --git a/vtkm/cont/testing/UnitTestCellLocatorGeneral.cxx b/vtkm/cont/testing/UnitTestCellLocatorGeneral.cxx index 737601b0e..c90fee88e 100644 --- a/vtkm/cont/testing/UnitTestCellLocatorGeneral.cxx +++ b/vtkm/cont/testing/UnitTestCellLocatorGeneral.cxx @@ -188,11 +188,14 @@ void TestWithDataSet(vtkm::cont::CellLocatorGeneral& locator, const vtkm::cont:: // CellLocatorGeneral is non-copyable. Pass it via a pointer. dispatcher.Invoke(points, &locator, cellIds, pcoords); + auto cellIdPortal = cellIds.ReadPortal(); + auto expCellIdsPortal = expCellIds.ReadPortal(); + auto pcoordsPortal = pcoords.ReadPortal(); + auto expPCoordsPortal = expPCoords.ReadPortal(); for (vtkm::Id i = 0; i < 128; ++i) { - VTKM_TEST_ASSERT(cellIds.ReadPortal().Get(i) == expCellIds.ReadPortal().Get(i), - "Incorrect cell ids"); - VTKM_TEST_ASSERT(test_equal(pcoords.ReadPortal().Get(i), expPCoords.ReadPortal().Get(i), 1e-3), + VTKM_TEST_ASSERT(cellIdPortal.Get(i) == expCellIdsPortal.Get(i), "Incorrect cell ids"); + VTKM_TEST_ASSERT(test_equal(pcoordsPortal.Get(i), expPCoordsPortal.Get(i), 1e-3), "Incorrect parameteric coordinates"); } diff --git a/vtkm/cont/testing/UnitTestCellSet.cxx b/vtkm/cont/testing/UnitTestCellSet.cxx index ef889a99a..733d1da00 100644 --- a/vtkm/cont/testing/UnitTestCellSet.cxx +++ b/vtkm/cont/testing/UnitTestCellSet.cxx @@ -117,13 +117,13 @@ void TestAgainstBaseLine(const vtkm::cont::CellSet& cellset, : PermutationArray.GetNumberOfValues(); VTKM_TEST_ASSERT(numCells == expectedNumCell, "Wrong number of cells"); + auto permutationPortal = PermutationArray.ReadPortal(); for (vtkm::Id i = 0; i < numCells; ++i) { VTKM_TEST_ASSERT(cellset.GetCellShape(i) == vtkm::CELL_SHAPE_HEXAHEDRON, "Wrong shape"); VTKM_TEST_ASSERT(cellset.GetNumberOfPointsInCell(i) == 8, "Wrong number of points-of-cell"); - vtkm::Id baseLineCellId = - (flag == IsPermutationCellSet::YES) ? PermutationArray.ReadPortal().Get(i) : i; + vtkm::Id baseLineCellId = (flag == IsPermutationCellSet::YES) ? permutationPortal.Get(i) : i; auto baseLinePointIds = baseLineStructure.GetPointsOfCell(baseLineCellId); vtkm::Id pointIds[8]; diff --git a/vtkm/cont/testing/UnitTestDataSetPermutation.cxx b/vtkm/cont/testing/UnitTestDataSetPermutation.cxx index 9d7df938c..4586d29da 100644 --- a/vtkm/cont/testing/UnitTestDataSetPermutation.cxx +++ b/vtkm/cont/testing/UnitTestDataSetPermutation.cxx @@ -32,9 +32,10 @@ bool TestArrayHandle(const vtkm::cont::ArrayHandle& ah, return false; } + auto ahPortal = ah.ReadPortal(); for (vtkm::Id i = 0; i < size; ++i) { - if (ah.ReadPortal().Get(i) != expected[i]) + if (ahPortal.Get(i) != expected[i]) { return false; } @@ -110,9 +111,10 @@ void TestDataSet_Explicit() //iterate same cell 4 times vtkm::Float32 expected[4] = { 30.1667f, 30.1667f, 30.1667f, 30.1667f }; + auto resultPortal = result.ReadPortal(); for (int i = 0; i < 4; ++i) { - VTKM_TEST_ASSERT(test_equal(result.ReadPortal().Get(i), expected[i]), + VTKM_TEST_ASSERT(test_equal(resultPortal.Get(i), expected[i]), "Wrong result for CellAverage worklet on explicit subset data"); } } @@ -145,9 +147,10 @@ void TestDataSet_Structured2D() dispatcher.Invoke(subset, dataSet.GetField("pointvar"), result); vtkm::Float32 expected[4] = { 40.1f, 40.1f, 40.1f, 40.1f }; + auto resultPortal = result.ReadPortal(); for (int i = 0; i < 4; ++i) { - VTKM_TEST_ASSERT(test_equal(result.ReadPortal().Get(i), expected[i]), + VTKM_TEST_ASSERT(test_equal(resultPortal.Get(i), expected[i]), "Wrong result for CellAverage worklet on 2d structured subset data"); } } @@ -180,9 +183,10 @@ void TestDataSet_Structured3D() dispatcher.Invoke(subset, dataSet.GetField("pointvar"), result); vtkm::Float32 expected[4] = { 70.2125f, 70.2125f, 70.2125f, 70.2125f }; + auto resultPortal = result.ReadPortal(); for (int i = 0; i < 4; ++i) { - VTKM_TEST_ASSERT(test_equal(result.ReadPortal().Get(i), expected[i]), + VTKM_TEST_ASSERT(test_equal(resultPortal.Get(i), expected[i]), "Wrong result for CellAverage worklet on 2d structured subset data"); } } diff --git a/vtkm/cont/testing/UnitTestLogging.cxx b/vtkm/cont/testing/UnitTestLogging.cxx index 047173e3d..920e2bd18 100644 --- a/vtkm/cont/testing/UnitTestLogging.cxx +++ b/vtkm/cont/testing/UnitTestLogging.cxx @@ -97,6 +97,7 @@ void RunTests() int UnitTestLogging(int, char* []) { // Test that parameterless init works: + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Log before intialize"); vtkm::cont::InitLogging(); RunTests(); diff --git a/vtkm/cont/testing/UnitTestTimer.cxx b/vtkm/cont/testing/UnitTestTimer.cxx index 1f6c3ec91..4c34bc73a 100644 --- a/vtkm/cont/testing/UnitTestTimer.cxx +++ b/vtkm/cont/testing/UnitTestTimer.cxx @@ -23,8 +23,7 @@ namespace using TimerTestDevices = vtkm::ListAppend>; -constexpr long long waitTimeMilliseconds = 100; -constexpr vtkm::Float64 waitTimeSeconds = vtkm::Float64(waitTimeMilliseconds) / 1000; +constexpr long long waitTimeMilliseconds = 5; struct Waiter { @@ -44,17 +43,10 @@ struct Waiter long long millisecondsToSleep = this->ExpectedTimeMilliseconds - elapsedMilliseconds; std::cout << " Sleeping for " << millisecondsToSleep << "ms (to " << expectedTimeSeconds - << "s)" << std::endl; + << "s)\n"; std::this_thread::sleep_for(std::chrono::milliseconds(millisecondsToSleep)); - VTKM_TEST_ASSERT(std::chrono::duration_cast( - std::chrono::high_resolution_clock::now() - this->Start) - .count() < - (this->ExpectedTimeMilliseconds + ((3 * waitTimeMilliseconds) / 4)), - "Internal test error: Sleep lasted longer than expected. System must be busy. " - "Might need to increase waitTimeMilliseconds."); - return expectedTimeSeconds; } }; @@ -64,14 +56,11 @@ void CheckTime(const vtkm::cont::Timer& timer, vtkm::Float64 expectedTime) vtkm::Float64 elapsedTime = timer.GetElapsedTime(); VTKM_TEST_ASSERT( elapsedTime > (expectedTime - 0.001), "Timer did not capture full wait. ", elapsedTime); - VTKM_TEST_ASSERT(elapsedTime < (expectedTime + waitTimeSeconds), - "Timer counted too far or system really busy. ", - elapsedTime); } void DoTimerCheck(vtkm::cont::Timer& timer) { - std::cout << " Starting timer" << std::endl; + std::cout << " Starting timer\n"; timer.Start(); VTKM_TEST_ASSERT(timer.Started(), "Timer fails to track started status"); VTKM_TEST_ASSERT(!timer.Stopped(), "Timer fails to track non stopped status"); @@ -85,14 +74,14 @@ void DoTimerCheck(vtkm::cont::Timer& timer) CheckTime(timer, expectedTime); - std::cout << " Make sure timer is still running" << std::endl; + std::cout << " Make sure timer is still running\n"; VTKM_TEST_ASSERT(!timer.Stopped(), "Timer fails to track stopped status"); expectedTime = waiter.Wait(); CheckTime(timer, expectedTime); - std::cout << " Stop the timer" << std::endl; + std::cout << " Stop the timer\n"; timer.Stop(); VTKM_TEST_ASSERT(timer.Stopped(), "Timer fails to track stopped status"); @@ -100,7 +89,7 @@ void DoTimerCheck(vtkm::cont::Timer& timer) waiter.Wait(); // Do not advanced expected time - std::cout << " Check that timer legitimately stopped" << std::endl; + std::cout << " Check that timer legitimately stopped\n"; CheckTime(timer, expectedTime); } @@ -146,7 +135,7 @@ struct TimerCheckFunctor void DoTimerTest() { - std::cout << "Check default timer" << std::endl; + std::cout << "Check default timer\n"; vtkm::cont::Timer timer; DoTimerCheck(timer); diff --git a/vtkm/exec/arg/FetchExtrude.h b/vtkm/exec/arg/FetchExtrude.h index 05bac2a33..4189328be 100644 --- a/vtkm/exec/arg/FetchExtrude.h +++ b/vtkm/exec/arg/FetchExtrude.h @@ -30,10 +30,11 @@ template struct Fetch { VTKM_SUPPRESS_EXEC_WARNINGS - template - VTKM_EXEC auto Load(const vtkm::exec::arg::ThreadIndicesTopologyMap< - vtkm::exec::ConnectivityExtrude>& indices, - const ExecObjectType&) const -> vtkm::Vec + template + VTKM_EXEC auto Load( + const vtkm::exec::arg::ThreadIndicesTopologyMap, + ScatterAndMaskMode>& indices, + const ExecObjectType&) const -> vtkm::Vec { // std::cout << "opimized fetch for point ids" << std::endl; const auto& xgcidx = indices.GetIndicesIncident(); @@ -50,9 +51,10 @@ struct Fetch - VTKM_EXEC auto Load(const vtkm::exec::arg::ThreadIndicesTopologyMap& indices, - const ExecObjectType&) const -> decltype(indices.GetIndicesIncident()) + template + VTKM_EXEC auto Load( + const vtkm::exec::arg::ThreadIndicesTopologyMap& indices, + const ExecObjectType&) const -> decltype(indices.GetIndicesIncident()) { return indices.GetIndicesIncident(); } @@ -133,10 +135,11 @@ struct Fetch - VTKM_EXEC auto Load(const vtkm::exec::arg::ThreadIndicesTopologyMap< - vtkm::exec::ReverseConnectivityExtrude>& indices, - const vtkm::exec::ArrayPortalExtrude& points) + template + VTKM_EXEC auto Load( + const vtkm::exec::arg::ThreadIndicesTopologyMap, + ScatterAndMaskMode>& indices, + const vtkm::exec::ArrayPortalExtrude& points) -> decltype(points.Get(indices.GetIndexLogical())) { // std::cout << "optimized fetch for point coordinates" << std::endl; diff --git a/vtkm/exec/arg/FetchTagArrayTopologyMapIn.h b/vtkm/exec/arg/FetchTagArrayTopologyMapIn.h index 28af1c867..60074269f 100644 --- a/vtkm/exec/arg/FetchTagArrayTopologyMapIn.h +++ b/vtkm/exec/arg/FetchTagArrayTopologyMapIn.h @@ -213,11 +213,11 @@ struct Fetch - VTKM_EXEC auto Load(const vtkm::exec::arg::ThreadIndicesTopologyMap< - vtkm::exec::ConnectivityExtrude>& indices, - const ExecObjectType& portal) - -> vtkm::Vec + template + VTKM_EXEC auto Load( + const vtkm::exec::arg::ThreadIndicesTopologyMap, + ScatterAndMaskMode>& indices, + const ExecObjectType& portal) -> vtkm::Vec { // std::cout << "opimized fetch for point values" << std::endl; const auto& xgcidx = indices.GetIndicesIncident(); diff --git a/vtkm/exec/arg/ThreadIndicesExtrude.h b/vtkm/exec/arg/ThreadIndicesExtrude.h index 21f9f1f5c..bcc95abbb 100644 --- a/vtkm/exec/arg/ThreadIndicesExtrude.h +++ b/vtkm/exec/arg/ThreadIndicesExtrude.h @@ -21,8 +21,8 @@ namespace arg { // Specialization for extrude types. -template -class ThreadIndicesTopologyMap> +template +class ThreadIndicesTopologyMap, ScatterAndMaskMode> { using ConnectivityType = vtkm::exec::ConnectivityExtrude; @@ -175,8 +175,8 @@ private: }; // Specialization for extrude types. -template -class ThreadIndicesTopologyMap> +template +class ThreadIndicesTopologyMap, ScatterAndMaskMode> { using ConnectivityType = vtkm::exec::ReverseConnectivityExtrude; diff --git a/vtkm/exec/arg/ThreadIndicesTopologyMap.h b/vtkm/exec/arg/ThreadIndicesTopologyMap.h index a32f85a0b..6c5fb6ffe 100644 --- a/vtkm/exec/arg/ThreadIndicesTopologyMap.h +++ b/vtkm/exec/arg/ThreadIndicesTopologyMap.h @@ -69,6 +69,17 @@ static inline VTKM_EXEC vtkm::Id2 Deflate(const vtkm::Id3& index, vtkm::Id2) } // namespace detail +/// \brief Uses spaces optimizations when using MaskNone and ScatterIdentity +/// +struct DefaultScatterAndMaskTag +{ +}; + +/// \brief Used for when not using MaskNone and ScatterIdentity. +/// +struct CustomScatterOrMaskTag +{ +}; /// \brief Container for thread indices in a topology map /// @@ -80,7 +91,7 @@ static inline VTKM_EXEC vtkm::Id2 Deflate(const vtkm::Id3& index, vtkm::Id2) /// This class is templated on the type that stores the connectivity (such /// as \c ConnectivityExplicit or \c ConnectivityStructured). /// -template +template class ThreadIndicesTopologyMap : public vtkm::exec::arg::ThreadIndicesBasic { using Superclass = vtkm::exec::arg::ThreadIndicesBasic; @@ -144,10 +155,11 @@ private: CellShapeTag CellShape; }; -// Specialization for structured connectivity types. +/// \brief Specialization for CustomScatterOrMaskTag template class ThreadIndicesTopologyMap< - vtkm::exec::ConnectivityStructured> + vtkm::exec::ConnectivityStructured, + CustomScatterOrMaskTag> { using ConnectivityType = vtkm::exec::ConnectivityStructured; @@ -160,15 +172,15 @@ public: vtkm::exec::ConnectivityStructured; VTKM_EXEC ThreadIndicesTopologyMap(vtkm::Id threadIndex, - vtkm::Id inIndex, + vtkm::Id inputIndex, vtkm::IdComponent visitIndex, - vtkm::Id outIndex, + vtkm::Id outputIndex, const ConnectivityType& connectivity) { this->ThreadIndex = threadIndex; - this->InputIndex = inIndex; + this->InputIndex = inputIndex; this->VisitIndex = visitIndex; - this->OutputIndex = outIndex; + this->OutputIndex = outputIndex; this->LogicalIndex = connectivity.FlatToLogicalToIndex(this->InputIndex); this->IndicesIncident = connectivity.GetIndices(this->LogicalIndex); this->CellShape = connectivity.GetCellShape(this->InputIndex); @@ -181,9 +193,6 @@ public: // This constructor handles multidimensional indices on one-to-one input-to-output auto logicalIndex = detail::Deflate(threadIndex3D, LogicalIndexType()); this->ThreadIndex = threadIndex1D; - this->InputIndex = threadIndex1D; - this->OutputIndex = threadIndex1D; - this->VisitIndex = 0; this->LogicalIndex = logicalIndex; this->IndicesIncident = connectivity.GetIndices(logicalIndex); this->CellShape = connectivity.GetCellShape(threadIndex1D); @@ -207,6 +216,7 @@ public: this->CellShape = connectivity.GetCellShape(threadIndex1D); } + /// \brief The index of the thread or work invocation. /// /// This index refers to which instance of the worklet is being invoked. Every invocation of the @@ -292,20 +302,176 @@ public: private: vtkm::Id ThreadIndex; - vtkm::Id InputIndex; vtkm::IdComponent VisitIndex; + LogicalIndexType LogicalIndex; + IndicesIncidentType IndicesIncident; + CellShapeTag CellShape; + vtkm::Id InputIndex; vtkm::Id OutputIndex; +}; + +/// \brief Specialization for DefaultScatterAndMaskTag +/// +/// It does not store VisitIndex, InputIndex and OutputIndex +/// since this is used only when Scatter is set as ScatterIdentity +/// and Mask is set as MaskNone which does not performs any transformation onto the +/// indices. +/// +template +class ThreadIndicesTopologyMap< + vtkm::exec::ConnectivityStructured, + DefaultScatterAndMaskTag> +{ + using ConnectivityType = + vtkm::exec::ConnectivityStructured; + +public: + using IndicesIncidentType = typename ConnectivityType::IndicesType; + using CellShapeTag = typename ConnectivityType::CellShapeTag; + using LogicalIndexType = typename ConnectivityType::SchedulingRangeType; + using Connectivity = ConnectivityType; + + VTKM_EXEC ThreadIndicesTopologyMap(vtkm::Id threadIndex, + vtkm::Id inputIndex, + vtkm::IdComponent vtkmNotUsed(visitIndex), + vtkm::Id vtkmNotUsed(outputIndex), + const ConnectivityType& connectivity) + { + this->ThreadIndex = threadIndex; + this->LogicalIndex = connectivity.FlatToLogicalToIndex(inputIndex); + this->IndicesIncident = connectivity.GetIndices(this->LogicalIndex); + this->CellShape = connectivity.GetCellShape(inputIndex); + } + + + + + VTKM_EXEC ThreadIndicesTopologyMap(const vtkm::Id3& threadIndex3D, + vtkm::Id threadIndex1D, + const ConnectivityType& connectivity) + { + // This constructor handles multidimensional indices on one-to-one input-to-output + auto logicalIndex = detail::Deflate(threadIndex3D, LogicalIndexType()); + this->ThreadIndex = threadIndex1D; + this->LogicalIndex = logicalIndex; + this->IndicesIncident = connectivity.GetIndices(logicalIndex); + this->CellShape = connectivity.GetCellShape(threadIndex1D); + } + + VTKM_EXEC ThreadIndicesTopologyMap(const vtkm::Id3& threadIndex3D, + vtkm::Id threadIndex1D, + vtkm::Id vtkmNotUsed(inIndex), + vtkm::IdComponent vtkmNotUsed(visitIndex), + vtkm::Id vtkmNotUsed(outIndex), + const ConnectivityType& connectivity) + { + // This constructor handles multidimensional indices on many-to-many input-to-output + auto logicalIndex = detail::Deflate(threadIndex3D, LogicalIndexType()); + this->ThreadIndex = threadIndex1D; + this->LogicalIndex = logicalIndex; + this->IndicesIncident = connectivity.GetIndices(logicalIndex); + this->CellShape = connectivity.GetCellShape(threadIndex1D); + } + + /// \brief The index of the thread or work invocation. + /// + /// This index refers to which instance of the worklet is being invoked. Every invocation of the + /// worklet has a unique thread index. This is also called the work index depending on the + /// context. + /// + VTKM_EXEC + vtkm::Id GetThreadIndex() const { return this->ThreadIndex; } + + /// \brief The logical index into the input domain. + /// + /// This is similar to \c GetIndex3D except the Vec size matches the actual + /// dimensions of the data. + /// + VTKM_EXEC + LogicalIndexType GetIndexLogical() const { return this->LogicalIndex; } + + /// \brief The index into the input domain. + /// + /// This index refers to the input element (array value, cell, etc.) that + /// this thread is being invoked for. This is the typical index used during + /// fetches. + /// + VTKM_EXEC + vtkm::Id GetInputIndex() const { return this->ThreadIndex; } + + /// \brief The 3D index into the input domain. + /// + /// Overloads the implementation in the base class to return the 3D index + /// for the input. + /// + VTKM_EXEC + vtkm::Id3 GetInputIndex3D() const { return detail::InflateTo3D(this->GetIndexLogical()); } + + /// \brief The index into the output domain. + /// + /// This index refers to the output element (array value, cell, etc.) that + /// this thread is creating. This is the typical index used during + /// Fetch::Store. + /// + VTKM_EXEC + vtkm::Id GetOutputIndex() const { return this->ThreadIndex; } + + /// \brief The visit index. + /// + /// When multiple output indices have the same input index, they are + /// distinguished using the visit index. + /// + VTKM_EXEC + vtkm::IdComponent GetVisitIndex() const { return 0; } + + /// \brief The indices of the incident elements. + /// + /// A topology map has "visited" and "incident" elements (e.g. points, cells, + /// etc). For each worklet invocation, there is exactly one visited element, + /// but there can be several incident elements. This method returns a + /// Vec-like object containing the indices to the incident elements. + /// + VTKM_EXEC + const IndicesIncidentType& GetIndicesIncident() const { return this->IndicesIncident; } + + /// \brief The input indices of the incident elements in pointer form. + /// + /// Returns the same object as GetIndicesIncident except that it returns a + /// pointer to the internally held object rather than a reference or copy. + /// Since the from indices can be a sizeable Vec (8 entries is common), it is + /// best not to have a bunch a copies. Thus, you can pass around a pointer + /// instead. However, care should be taken to make sure that this object does + /// not go out of scope, at which time the returned pointer becomes invalid. + /// + VTKM_EXEC + const IndicesIncidentType* GetIndicesIncidentPointer() const { return &this->IndicesIncident; } + + /// \brief The shape of the input cell. + /// + /// In topology maps that map from points to something, the indices make up + /// the structure of a cell. Although the shape tag is not technically and + /// index, it defines the meaning of the indices, so we put it here. (That + /// and this class is the only convenient place to store it.) + /// + VTKM_EXEC + CellShapeTag GetCellShape() const { return this->CellShape; } + +private: + vtkm::Id ThreadIndex; LogicalIndexType LogicalIndex; IndicesIncidentType IndicesIncident; CellShapeTag CellShape; }; -// Specialization for permuted structured connectivity types. + +/// \brief Specialization for permuted structured connectivity types. template class ThreadIndicesTopologyMap>> + PermutationPortal, + vtkm::exec::ConnectivityStructured>, + CustomScatterOrMaskTag> { using PermutedConnectivityType = vtkm::exec::ConnectivityPermutedVisitCellsWithPoints< PermutationPortal, diff --git a/vtkm/exec/arg/testing/UnitTestFetchArrayTopologyMapIn.cxx b/vtkm/exec/arg/testing/UnitTestFetchArrayTopologyMapIn.cxx index 1ab639ed1..db2d68364 100644 --- a/vtkm/exec/arg/testing/UnitTestFetchArrayTopologyMapIn.cxx +++ b/vtkm/exec/arg/testing/UnitTestFetchArrayTopologyMapIn.cxx @@ -87,7 +87,9 @@ struct FetchArrayTopologyMapInTests void TryInvocation(const Invocation& invocation) const { using ConnectivityType = typename Invocation::InputDomainType; - using ThreadIndicesType = vtkm::exec::arg::ThreadIndicesTopologyMap; + using ThreadIndicesType = + vtkm::exec::arg::ThreadIndicesTopologyMap; using FetchType = vtkm::exec::arg::Fetch; + using ThreadIndicesType = + vtkm::exec::arg::ThreadIndicesTopologyMap; vtkm::exec::arg::Fetch + +#include +#include +#include + +#include + +namespace +{ + +enum class DataFormat +{ + ByteData, + ShortData, + IntegerData, + FloatData, + DoubleData +}; + +template +void ReadBuffer(const std::string& fName, const vtkm::Id& sz, std::vector& buff) +{ + FILE* fp = fopen(fName.c_str(), "rb"); + size_t readSize = static_cast(sz); + if (fp == nullptr) + { + throw vtkm::io::ErrorIO("Unable to open data file: " + fName); + } + buff.resize(readSize); + size_t nread = fread(&buff[0], sizeof(T), readSize, fp); + if (nread != readSize) + { + throw vtkm::io::ErrorIO("Data file read failed: " + fName); + } + fclose(fp); +} + +template +void ReadScalar(const std::string& fName, const vtkm::Id& nTuples, vtkm::cont::ArrayHandle& var) +{ + std::vector buff; + ReadBuffer(fName, nTuples, buff); + var.Allocate(nTuples); + auto writePortal = var.WritePortal(); + for (vtkm::Id i = 0; i < nTuples; i++) + { + writePortal.Set(i, buff[static_cast(i)]); + } +} + +template +void ReadVector(const std::string& fName, + const vtkm::Id& nTuples, + vtkm::cont::ArrayHandle>& var) +{ + std::vector buff; + ReadBuffer(fName, nTuples * 3, buff); + + var.Allocate(nTuples); + vtkm::Vec v; + auto writePortal = var.WritePortal(); + for (vtkm::Id i = 0; i < nTuples; i++) + { + v[0] = buff[static_cast(i * 3 + 0)]; + v[1] = buff[static_cast(i * 3 + 1)]; + v[2] = buff[static_cast(i * 3 + 2)]; + writePortal.Set(i, v); + } +} + +} // anonymous namespace + +namespace vtkm +{ +namespace io +{ + +BOVDataSetReader::BOVDataSetReader(const char* fileName) + : FileName(fileName) + , Loaded(false) + , DataSet() +{ +} + +BOVDataSetReader::BOVDataSetReader(const std::string& fileName) + : FileName(fileName) + , Loaded(false) + , DataSet() +{ +} + +const vtkm::cont::DataSet& BOVDataSetReader::ReadDataSet() +{ + try + { + this->LoadFile(); + } + catch (std::ifstream::failure& e) + { + std::string message("IO Error: "); + throw vtkm::io::ErrorIO(message + e.what()); + } + return this->DataSet; +} + +void BOVDataSetReader::LoadFile() +{ + if (this->Loaded) + return; + + std::ifstream stream(this->FileName); + if (stream.fail()) + throw vtkm::io::ErrorIO("Failed to open file: " + this->FileName); + + DataFormat dataFormat = DataFormat::ByteData; + std::string bovFile, line, token, options, variableName; + vtkm::Id numComponents = 1; + vtkm::Id3 dim; + vtkm::Vec3f origin(0, 0, 0); + vtkm::Vec3f spacing(1, 1, 1); + bool spacingSet = false; + + while (stream.good()) + { + std::getline(stream, line); + if (line.size() == 0 || line[0] == '#') + continue; + //std::cout<<"::"<> bovFile >> std::ws; + } + else if (token.find("DATA") != std::string::npos && token.find("SIZE") != std::string::npos) + { + strStream >> dim[0] >> dim[1] >> dim[2] >> std::ws; + } + else if (token.find("BRICK") != std::string::npos && token.find("ORIGIN") != std::string::npos) + { + strStream >> origin[0] >> origin[1] >> origin[2] >> std::ws; + } + + //DRP + else if (token.find("BRICK") != std::string::npos && token.find("SIZE") != std::string::npos) + { + strStream >> spacing[0] >> spacing[1] >> spacing[2] >> std::ws; + spacingSet = true; + } + else if (token.find("DATA") != std::string::npos && token.find("FORMAT") != std::string::npos) + { + std::string opt; + strStream >> opt >> std::ws; + if (opt.find("FLOAT") != std::string::npos || opt.find("REAL") != std::string::npos) + dataFormat = DataFormat::FloatData; + else if (opt.find("DOUBLE") != std::string::npos) + dataFormat = DataFormat::DoubleData; + else + throw vtkm::io::ErrorIO("Unsupported data type: " + token); + } + else if (token.find("DATA") != std::string::npos && + token.find("COMPONENTS") != std::string::npos) + { + strStream >> numComponents >> std::ws; + if (numComponents != 1 && numComponents != 3) + throw vtkm::io::ErrorIO("Unsupported number of components"); + } + else if (token.find("VARIABLE") != std::string::npos && + token.find("PALETTE") == std::string::npos) + { + strStream >> variableName >> std::ws; + if (variableName[0] == '"') + variableName = variableName.substr(1, variableName.size() - 2); + } + } + + if (spacingSet) + { + spacing[0] = (spacing[0]) / static_cast(dim[0] - 1); + spacing[1] = (spacing[1]) / static_cast(dim[1] - 1); + spacing[2] = (spacing[2]) / static_cast(dim[2] - 1); + } + + std::string fullPathDataFile; + std::size_t pos = FileName.rfind("/"); + if (pos != std::string::npos) + { + std::string baseDir; + baseDir = this->FileName.substr(0, pos); + fullPathDataFile = baseDir + "/" + bovFile; + } + else + fullPathDataFile = bovFile; + + + vtkm::cont::DataSetBuilderUniform dataSetBuilder; + vtkm::cont::DataSetFieldAdd dsf; + this->DataSet = dataSetBuilder.Create(dim, origin, spacing); + + vtkm::Id numTuples = dim[0] * dim[1] * dim[2]; + if (numComponents == 1) + { + if (dataFormat == DataFormat::FloatData) + { + vtkm::cont::ArrayHandle var; + ReadScalar(fullPathDataFile, numTuples, var); + dsf.AddPointField(this->DataSet, variableName, var); + } + else if (dataFormat == DataFormat::DoubleData) + { + vtkm::cont::ArrayHandle var; + ReadScalar(fullPathDataFile, numTuples, var); + dsf.AddPointField(this->DataSet, variableName, var); + } + } + else if (numComponents == 3) + { + if (dataFormat == DataFormat::FloatData) + { + vtkm::cont::ArrayHandle var; + ReadVector(fullPathDataFile, numTuples, var); + dsf.AddPointField(this->DataSet, variableName, var); + } + else if (dataFormat == DataFormat::DoubleData) + { + vtkm::cont::ArrayHandle var; + ReadVector(fullPathDataFile, numTuples, var); + dsf.AddPointField(this->DataSet, variableName, var); + } + } + + this->Loaded = true; +} +} +} // namespace vtkm::io diff --git a/vtkm/io/BOVDataSetReader.h b/vtkm/io/BOVDataSetReader.h index 95b7e28ac..dbd2147c5 100644 --- a/vtkm/io/BOVDataSetReader.h +++ b/vtkm/io/BOVDataSetReader.h @@ -10,239 +10,31 @@ #ifndef vtk_m_io_BOVDataSetReader_h #define vtk_m_io_BOVDataSetReader_h -#include #include -#include -#include -#include + +#include namespace vtkm { namespace io { -class BOVDataSetReader +class VTKM_IO_EXPORT BOVDataSetReader { public: - BOVDataSetReader(const char* fileName) - : FileName(fileName) - , Loaded(false) - , DataSet() - { - } - BOVDataSetReader(const std::string& fileName) - : FileName(fileName) - , Loaded(false) - , DataSet() - { - } + VTKM_CONT BOVDataSetReader(const char* fileName); + VTKM_CONT BOVDataSetReader(const std::string& fileName); - const vtkm::cont::DataSet& ReadDataSet() - { - try - { - LoadFile(); - } - catch (std::ifstream::failure& e) - { - std::string message("IO Error: "); - throw vtkm::io::ErrorIO(message + e.what()); - } - return this->DataSet; - } + VTKM_CONT const vtkm::cont::DataSet& ReadDataSet(); private: - using DataFormat = enum { ByteData, ShortData, IntegerData, FloatData, DoubleData }; - - void LoadFile() - { - if (this->Loaded) - return; - - std::ifstream stream(this->FileName); - if (stream.fail()) - throw vtkm::io::ErrorIO("Failed to open file: " + this->FileName); - - DataFormat dataFormat = ByteData; - std::string bovFile, line, token, options, variableName; - vtkm::Id numComponents = 1; - vtkm::Id3 dim; - vtkm::Vec3f origin(0, 0, 0); - vtkm::Vec3f spacing(1, 1, 1); - bool spacingSet = false; - - while (stream.good()) - { - std::getline(stream, line); - if (line.size() == 0 || line[0] == '#') - continue; - //std::cout<<"::"<> bovFile >> std::ws; - } - else if (token.find("DATA") != std::string::npos && token.find("SIZE") != std::string::npos) - { - strStream >> dim[0] >> dim[1] >> dim[2] >> std::ws; - } - else if (token.find("BRICK") != std::string::npos && - token.find("ORIGIN") != std::string::npos) - { - strStream >> origin[0] >> origin[1] >> origin[2] >> std::ws; - } - - //DRP - else if (token.find("BRICK") != std::string::npos && token.find("SIZE") != std::string::npos) - { - strStream >> spacing[0] >> spacing[1] >> spacing[2] >> std::ws; - spacingSet = true; - } - else if (token.find("DATA") != std::string::npos && token.find("FORMAT") != std::string::npos) - { - std::string opt; - strStream >> opt >> std::ws; - if (opt.find("FLOAT") != std::string::npos || opt.find("REAL") != std::string::npos) - dataFormat = FloatData; - else if (opt.find("DOUBLE") != std::string::npos) - dataFormat = DoubleData; - else - throw vtkm::io::ErrorIO("Unsupported data type: " + token); - } - else if (token.find("DATA") != std::string::npos && - token.find("COMPONENTS") != std::string::npos) - { - strStream >> numComponents >> std::ws; - if (numComponents != 1 && numComponents != 3) - throw vtkm::io::ErrorIO("Unsupported number of components"); - } - else if (token.find("VARIABLE") != std::string::npos && - token.find("PALETTE") == std::string::npos) - { - strStream >> variableName >> std::ws; - if (variableName[0] == '"') - variableName = variableName.substr(1, variableName.size() - 2); - } - } - - if (spacingSet) - { - spacing[0] = (spacing[0]) / static_cast(dim[0] - 1); - spacing[1] = (spacing[1]) / static_cast(dim[1] - 1); - spacing[2] = (spacing[2]) / static_cast(dim[2] - 1); - } - - std::string fullPathDataFile; - std::size_t pos = FileName.rfind("/"); - if (pos != std::string::npos) - { - std::string baseDir; - baseDir = this->FileName.substr(0, pos); - fullPathDataFile = baseDir + "/" + bovFile; - } - else - fullPathDataFile = bovFile; - - - vtkm::cont::DataSetBuilderUniform dataSetBuilder; - vtkm::cont::DataSetFieldAdd dsf; - this->DataSet = dataSetBuilder.Create(dim, origin, spacing); - - vtkm::Id numTuples = dim[0] * dim[1] * dim[2]; - if (numComponents == 1) - { - if (dataFormat == FloatData) - { - vtkm::cont::ArrayHandle var; - ReadScalar(fullPathDataFile, numTuples, var); - dsf.AddPointField(this->DataSet, variableName, var); - } - else if (dataFormat == DoubleData) - { - vtkm::cont::ArrayHandle var; - ReadScalar(fullPathDataFile, numTuples, var); - dsf.AddPointField(this->DataSet, variableName, var); - } - } - else if (numComponents == 3) - { - if (dataFormat == FloatData) - { - vtkm::cont::ArrayHandle var; - ReadVector(fullPathDataFile, numTuples, var); - dsf.AddPointField(this->DataSet, variableName, var); - } - else if (dataFormat == DoubleData) - { - vtkm::cont::ArrayHandle var; - ReadVector(fullPathDataFile, numTuples, var); - dsf.AddPointField(this->DataSet, variableName, var); - } - } - - this->Loaded = true; - } - - template - void ReadBuffer(const std::string& fName, const vtkm::Id& sz, std::vector& buff) - { - FILE* fp = fopen(fName.c_str(), "rb"); - size_t readSize = static_cast(sz); - if (fp == nullptr) - throw vtkm::io::ErrorIO("Unable to open data file: " + fName); - buff.resize(readSize); - size_t nread = fread(&buff[0], sizeof(T), readSize, fp); - if (nread != readSize) - throw vtkm::io::ErrorIO("Data file read failed: " + fName); - fclose(fp); - } - - template - void ReadScalar(const std::string& fName, - const vtkm::Id& nTuples, - vtkm::cont::ArrayHandle& var) - { - std::vector buff; - ReadBuffer(fName, nTuples, buff); - var.Allocate(nTuples); - for (vtkm::Id i = 0; i < nTuples; i++) - var.WritePortal().Set(i, buff[(size_t)i]); - } - - template - void ReadVector(const std::string& fName, - const vtkm::Id& nTuples, - vtkm::cont::ArrayHandle>& var) - { - std::vector buff; - ReadBuffer(fName, nTuples * 3, buff); - - var.Allocate(nTuples); - vtkm::Vec v; - for (vtkm::Id i = 0; i < nTuples; i++) - { - v[0] = buff[static_cast(i * 3 + 0)]; - v[1] = buff[static_cast(i * 3 + 1)]; - v[2] = buff[static_cast(i * 3 + 2)]; - var.WritePortal().Set(i, v); - } - } + VTKM_CONT void LoadFile(); std::string FileName; bool Loaded; vtkm::cont::DataSet DataSet; }; } -} } // vtkm::io #endif // vtk_m_io_BOVReader_h diff --git a/vtkm/io/CMakeLists.txt b/vtkm/io/CMakeLists.txt index e8dfb0465..6c9eaeedb 100644 --- a/vtkm/io/CMakeLists.txt +++ b/vtkm/io/CMakeLists.txt @@ -13,14 +13,23 @@ set(headers ErrorIO.h DecodePNG.h EncodePNG.h + ImageReader.h + ImageWriter.h + PixelTypes.h VTKDataSetReader.h VTKDataSetReaderBase.h + VTKDataSetWriter.h VTKPolyDataReader.h VTKRectilinearGridReader.h VTKStructuredGridReader.h VTKStructuredPointsReader.h VTKUnstructuredGridReader.h - VTKDataSetWriter.h +) + +set(template_sources + ImageReader.hxx + ImageWriter.hxx + PixelTypes.hxx ) set(sources @@ -28,12 +37,34 @@ set(sources EncodePNG.cxx ) -vtkm_declare_headers(${headers}) +# TODO: None of these codes actually use a device. Rather, they access ArrayHandle, and we +# currently need to ensure that ArrayHandle is correctly compiled for all devices. This is +# kind of silly, so hopefully sometime in the future you will no longer need to compile for +# devices for ArrayHandle, and this requirement will go away. +set(device_sources + BOVDataSetReader.cxx + VTKDataSetReader.cxx + VTKDataSetReaderBase.cxx + VTKDataSetWriter.cxx + VTKPolyDataReader.cxx + VTKRectilinearGridReader.cxx + VTKStructuredGridReader.cxx + VTKStructuredPointsReader.cxx + VTKUnstructuredGridReader.cxx + ) -vtkm_library( NAME vtkm_io - SOURCES ${sources} - HEADERS ${headers} - ) +vtkm_declare_headers( + ${headers} + ${template_sources} +) + +vtkm_library( + NAME vtkm_io + SOURCES ${sources} + DEVICE_SOURCES ${device_sources} + HEADERS ${headers} + TEMPLATE_SOURCES ${template_sources} +) target_link_libraries(vtkm_io PUBLIC vtkm_cont PRIVATE vtkm_lodepng) diff --git a/vtkm/io/DecodePNG.cxx b/vtkm/io/DecodePNG.cxx index 8d89e15b9..fa1c71434 100644 --- a/vtkm/io/DecodePNG.cxx +++ b/vtkm/io/DecodePNG.cxx @@ -13,7 +13,6 @@ #include #include - VTKM_THIRDPARTY_PRE_INCLUDE #include VTKM_THIRDPARTY_POST_INCLUDE diff --git a/vtkm/io/ImageReader.h b/vtkm/io/ImageReader.h new file mode 100644 index 000000000..06d470edd --- /dev/null +++ b/vtkm/io/ImageReader.h @@ -0,0 +1,155 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ +#ifndef vtk_m_io_ImageReader_h +#define vtk_m_io_ImageReader_h + +#include +#include +#include + +namespace vtkm +{ + +// Forward Declare +namespace rendering +{ +class Canvas; +} // namespace rendering + +namespace io +{ + +/// \brief Manages reading, and loading data from images +/// +/// \c BaseImageReader implements methods for loading imaging data from a canvas or +/// ArrayHandle and storing that data in a vtkm::cont::DataSet. Image rgb values +/// are represented as a point field in a 2D uniform dataset. +/// +/// \c BaseImageReader can be constructed from a file, canvas, or ArrayHandle. It can +/// also be empy constructed and filled in with a dataset later. +/// +/// \c BaseImageReader implements virtual methods for reading files. Ideally, +/// these methods will be overriden in various subclasses to implement specific +/// functionality for reading data to specific image file-types. +/// +/// \c The templated type is used when Filling the ImageDataSet. +/// +class BaseImageReader +{ +public: + /// Constructs an emtpy BaseImageReader. + /// + BaseImageReader() = default; + explicit BaseImageReader(const vtkm::Id& maxColorValue) + : MaxColorValue(maxColorValue) + { + } + ~BaseImageReader() noexcept = default; + + /// Reads image data from a file. Meant to be implemented in overriden + /// image-specific classes + /// + virtual vtkm::cont::DataSet ReadFromFile(const std::string& fileName) = 0; + + /// Creates an ImageDataSet from a Canvas object + /// + vtkm::cont::DataSet CreateImageDataSet(const vtkm::rendering::Canvas& canvas); + + /// Creates an ImageDataSet from a RGBA 32bit float color buffer + /// Assumes the color buffer is stored in row-major ordering + /// + vtkm::cont::DataSet CreateImageDataSet(const vtkm::cont::ArrayHandle& colorBuffer, + const vtkm::Id& width, + const vtkm::Id& height); + + const std::string& GetPointFieldName() const { return this->PointFieldName; } + +protected: + vtkm::cont::DataSet InitializeImageDataSet(const vtkm::Id& width, const vtkm::Id& height); + + std::string PointFieldName = "pixel-data"; + vtkm::Id MaxColorValue{ 0 }; +}; + +/// \brief Manages reading images using the PNG format via lodepng +/// +/// \c PNGReader extends BaseImageReader and implements reading images in a valid +/// PNG format. It utilizes lodepng's decode file functions to read +/// PNG images that are automatically compressed to optimal sizes relative to +/// the actual bit complexity of the image. +/// +/// \c PNGReader will automatically upsample/downsample read image data +/// to a 16 bit RGB no matter how the image is compressed. It is up to the user to +/// decide the pixel format for input PNGs +class PNGReader : public BaseImageReader +{ + using Superclass = BaseImageReader; + +public: + using Superclass::Superclass; + PNGReader() = default; + ~PNGReader() noexcept = default; + + /// Reads PNG data from the provided file and stores it + /// as a 16bit RGB value + /// + vtkm::cont::DataSet ReadFromFile(const std::string& fileName) override; + + /// Reads PNG data from the provided file and stores it + /// according to the method's templated PixelType + /// + template + vtkm::cont::DataSet ReadFromFile(const std::string& fileName); +}; + + +/// \brief Manages reading images using the PNM format +/// +/// \c PNMImage extends BaseImage, and implements reading images from a +/// valid PNM format (for magic number P6). More details on the PNM +/// format can be found here: http://netpbm.sourceforge.net/doc/ppm.html +/// +/// When a file is read the parsed MagicNumber and MaxColorSize provided +/// are utilized to correctly parse the bits from the file +class PNMReader : public BaseImageReader +{ + using Superclass = BaseImageReader; + +public: + using Superclass::Superclass; + PNMReader() = default; + ~PNMReader() noexcept = default; + + /// Attempts to read the provided file into a DataSet object. + /// Will pull the image's MaxColorValue from the file and then Decode + /// with the appropriate RGB PixelType bit depth. + /// + vtkm::cont::DataSet ReadFromFile(const std::string& fileName) override; + +protected: + /// Reads image data from the provided inStream with the supplied width/height + /// Stores the data in a vector of PixelType which is converted to an DataSet + /// + template + vtkm::cont::DataSet DecodeFile(std::ifstream& inStream, + const vtkm::Id& width, + const vtkm::Id& height); + + // This is set to only work for P6 pnm image types for now (ie ppm) + std::string MagicNumber{ "P6" }; +}; + + +} // namespace io +} // namespace vtkm + +#include + +#endif diff --git a/vtkm/io/ImageReader.hxx b/vtkm/io/ImageReader.hxx new file mode 100644 index 000000000..60afc7f9b --- /dev/null +++ b/vtkm/io/ImageReader.hxx @@ -0,0 +1,176 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ +#ifndef vtk_m_io_ImageReader_hxx +#define vtk_m_io_ImageReader_hxx + +#include +#include +#include +#include +#include +#include + +VTKM_THIRDPARTY_PRE_INCLUDE +#include +VTKM_THIRDPARTY_POST_INCLUDE + +namespace vtkm +{ +namespace io +{ + +// Start BaseReaderImage Class Template Implementations +VTKM_CONT +vtkm::cont::DataSet BaseImageReader::CreateImageDataSet(const vtkm::rendering::Canvas& canvas) +{ + return this->CreateImageDataSet(canvas.GetColorBuffer(), canvas.GetWidth(), canvas.GetHeight()); +} + +VTKM_CONT +vtkm::cont::DataSet BaseImageReader::CreateImageDataSet( + const vtkm::cont::ArrayHandle& colorBuffer, + const vtkm::Id& width, + const vtkm::Id& height) +{ + vtkm::cont::ArrayHandle::ReadPortalType colorPortal = colorBuffer.ReadPortal(); + std::vector fieldData; + for (vtkm::Id yIndex = 0; yIndex < height; yIndex++) + { + for (vtkm::Id xIndex = 0; xIndex < width; xIndex++) + { + vtkm::Vec4f_32 tuple = colorPortal.Get(yIndex * width + xIndex); + fieldData.push_back(tuple); + } + } + auto dataSet = this->InitializeImageDataSet(width, height); + vtkm::cont::DataSetFieldAdd dsf; + dsf.AddPointField(dataSet, this->PointFieldName, fieldData); + return dataSet; +} + +VTKM_CONT +vtkm::cont::DataSet BaseImageReader::InitializeImageDataSet(const vtkm::Id& width, + const vtkm::Id& height) +{ + vtkm::cont::DataSetBuilderUniform dsb; + vtkm::Id2 dimensions(width, height); + return dsb.Create(dimensions); +} +// End BaseReaderImage Class Template Implementations + +// Start PNGReader Class Template Implementations +VTKM_CONT +vtkm::cont::DataSet PNGReader::ReadFromFile(const std::string& fileName) +{ + return this->ReadFromFile(fileName); +} + +VTKM_CONT +template +vtkm::cont::DataSet PNGReader::ReadFromFile(const std::string& fileName) +{ + unsigned char* imageData; + unsigned uwidth, uheight; + vtkm::Id width, height; + vtkm::png::lodepng_decode_file(&imageData, + &uwidth, + &uheight, + fileName.c_str(), + PixelType::PNG_COLOR_TYPE, + PixelType::BIT_DEPTH); + + width = static_cast(uwidth); + height = static_cast(uheight); + + // Fill in the data starting from the end (Images are read Top-Left to Bottom-Right, + // but are stored from Bottom-Left to Top-Right) + std::vector fieldData; + for (vtkm::Id yIndex = static_cast(height - 1); yIndex >= 0; yIndex--) + { + for (vtkm::Id xIndex = 0; xIndex < static_cast(width); xIndex++) + { + vtkm::Id index = static_cast(yIndex * width + xIndex); + fieldData.push_back(PixelType(imageData, index).ToVec4f()); + } + } + + auto dataSet = this->InitializeImageDataSet(width, height); + vtkm::cont::DataSetFieldAdd dsf; + dsf.AddPointField(dataSet, this->PointFieldName, fieldData); + + free(imageData); + return dataSet; +} +// End PNGReader Class Template Implementations + +// Start PNMReader Class Template Implementations +VTKM_CONT +vtkm::cont::DataSet PNMReader::ReadFromFile(const std::string& fileName) +{ + std::ifstream inStream(fileName.c_str(), std::ios_base::binary | std::ios_base::in); + vtkm::Id width; + vtkm::Id height; + std::string val; + + inStream >> val; + if (this->MagicNumber != val) + { + throw vtkm::cont::ErrorBadValue("MagicNumber: " + this->MagicNumber + " in file: " + fileName + + " did not match: " + val); + } + + inStream >> width >> height >> this->MaxColorValue; + inStream.get(); + + switch (this->MaxColorValue) + { + case 255: + return this->DecodeFile(inStream, width, height); + case 65535: + return this->DecodeFile(inStream, width, height); + default: + throw vtkm::cont::ErrorBadValue("MaxColorValue: " + std::to_string(this->MaxColorValue) + + " from file: " + fileName + " was not one of: {8, 16}"); + } +} + +VTKM_CONT +template +vtkm::cont::DataSet PNMReader::DecodeFile(std::ifstream& inStream, + const vtkm::Id& width, + const vtkm::Id& height) +{ + vtkm::UInt32 imageSize = static_cast(width * height * PixelType::BYTES_PER_PIXEL); + std::vector imageData(imageSize); + inStream.read((char*)imageData.data(), imageSize); + + // Fill in the data starting from the end (Images are read Top-Left to Bottom-Right, + // but are stored from Bottom-Left to Top-Right) + std::vector fieldData; + for (vtkm::Id yIndex = height - 1; yIndex >= 0; yIndex--) + { + for (vtkm::Id xIndex = 0; xIndex < width; xIndex++) + { + vtkm::Id index = yIndex * width + xIndex; + fieldData.push_back(PixelType(imageData.data(), index).ToVec4f()); + } + } + + auto dataSet = this->InitializeImageDataSet(width, height); + vtkm::cont::DataSetFieldAdd dsf; + dsf.AddPointField(dataSet, this->PointFieldName, fieldData); + return dataSet; +} +// End PNMReader Class Template Implementations + +} // namespace io +} // namespace vtkm + +#endif diff --git a/vtkm/io/ImageWriter.h b/vtkm/io/ImageWriter.h new file mode 100644 index 000000000..7ccc1284a --- /dev/null +++ b/vtkm/io/ImageWriter.h @@ -0,0 +1,140 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ +#ifndef vtk_m_io_ImageWriter_h +#define vtk_m_io_ImageWriter_h + +#include +#include +#include + +namespace vtkm +{ +namespace io +{ + +/// \brief Manages writing, and loading data from images +/// +/// \c BaseImageWriter implements methods for loading imaging data from a canvas or +/// ArrayHandle and storing that data in a vtkm::cont::DataSet. Image rgb values +/// are represented as a point field in a 2D uniform dataset. +/// +/// \c BaseImageWriter can be constructed from a file, canvas, or ArrayHandle. It can +/// also be empy constructed and filled in with a dataset later. +/// +/// \c BaseImageWriter implements virtual methods for writing files. Ideally, +/// these methods will be overriden in various subclasses to implement specific +/// functionality for writing data to specific image file-types. +/// +class BaseImageWriter +{ +public: + /// Constructs an emtpy BaseImageWriter. + /// + BaseImageWriter() = default; + explicit BaseImageWriter(const vtkm::Id& maxColorValue) + : MaxColorValue(maxColorValue) + { + } + ~BaseImageWriter() noexcept = default; + + /// Write and store ImageDataSet to a file. Meant to be implemented in + /// overriden image-specific classes + /// + virtual void WriteToFile(const std::string& fileName, + const vtkm::cont::DataSet& dataSet) const = 0; + + vtkm::Id GetImageWidth(vtkm::cont::DataSet dataSet) const; + vtkm::Id GetImageHeight(vtkm::cont::DataSet dataSet) const; + + const std::string& GetPointFieldName() const { return this->PointFieldName; } + void SetMaxColorValue(const vtkm::Id& maxColorValue) { this->MaxColorValue = maxColorValue; } + +protected: + std::string PointFieldName = "pixel-data"; + vtkm::Id MaxColorValue{ 0 }; +}; + +/// \brief Manages writing images using the PNM format +/// +/// \c PNMWriter extends BaseImageWriter, and implements writing images in a +/// valid PNM format (for magic number P6). More details on the PNM +/// format can be found here: http://netpbm.sourceforge.net/doc/ppm.html +/// +/// When a file is writen the MaxColorValue found in the file is used to +/// determine the PixelType required to stored PixelType is instead dependent +/// upon the read MaxColorValue obtained from the file +class PNMWriter : public BaseImageWriter +{ + using Superclass = BaseImageWriter; + +public: + using Superclass::Superclass; + PNMWriter() = default; + ~PNMWriter() noexcept = default; + + /// Attempts to write the ImageDataSet to a PNM file. The MaxColorValue + /// set in the file with either be selected from the stored MaxColorValue + /// member variable, or from the templated type if MaxColorValue hasn't been + /// set from a read file. + /// + void WriteToFile(const std::string& fileName, const vtkm::cont::DataSet& dataSet) const override; + +protected: + /// Writes image data stored in ImageDataSet to the provided outStream + /// Casts the data to the provided PixelType + /// + template + void EncodeFile(std::ofstream& outStream, const vtkm::cont::DataSet& dataSet) const; + + // Currently only works with P6 PNM files (PPM) + std::string MagicNumber{ "P6" }; +}; + +/// \brief Manages writing images using the PNG format via lodepng +/// +/// \c PNGWriter extends BaseImageWriter and implements writing images in a valid +/// PNG format. It utilizes lodepng's encode file functions to write +/// PNG images that are automatically compressed to optimal sizes relative to +/// the actual bit complexity of the image. +/// +/// \c PNGImage will automatically upsample/downsample written image data +/// to the supplied templated PixelType. For example, it is possible to write +/// a 1-bit greyscale image into a 16bit RGB PNG object. It is up to the user to +/// decide the pixel format for output PNGs +class PNGWriter : public BaseImageWriter +{ + using Superclass = BaseImageWriter; + +public: + using Superclass::Superclass; + PNGWriter() = default; + ~PNGWriter() noexcept = default; + + /// Writes stored data matched to the class's templated type + /// to a file in PNG format. Relies upon the lodepng encoding + /// method to optimize compression and choose the best storage format. + /// + void WriteToFile(const std::string& fileName, const vtkm::cont::DataSet& dataSet) const override; + + /// Writes stored data matched to the method's templated type + /// to a file in PNG format. Relies upon the lodepng encoding + /// method to optimize compression and choose the best storage format. + /// + template + void WriteToFile(const std::string& fileName, const vtkm::cont::DataSet& dataSet) const; +}; + + +} // namespace io +} // namespace vtkm + +#include + +#endif diff --git a/vtkm/io/ImageWriter.hxx b/vtkm/io/ImageWriter.hxx new file mode 100644 index 000000000..9a056fe8c --- /dev/null +++ b/vtkm/io/ImageWriter.hxx @@ -0,0 +1,173 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ +#ifndef vtk_m_io_ImageWriter_hxx +#define vtk_m_io_ImageWriter_hxx + +#include +#include +#include +#include +#include + +VTKM_THIRDPARTY_PRE_INCLUDE +#include +VTKM_THIRDPARTY_POST_INCLUDE + +namespace vtkm +{ +namespace io +{ + +VTKM_CONT +vtkm::Id BaseImageWriter::GetImageWidth(vtkm::cont::DataSet dataSet) const +{ + if (dataSet.GetNumberOfCoordinateSystems() > 0) + { + // Add 1 since the Bounds are 0 indexed + return static_cast(dataSet.GetCoordinateSystem().GetBounds().X.Max) + 1; + } + return 0; +} + +VTKM_CONT +vtkm::Id BaseImageWriter::GetImageHeight(vtkm::cont::DataSet dataSet) const +{ + if (dataSet.GetNumberOfCoordinateSystems() > 0) + { + // Add 1 since the Bounds are 0 indexed + return static_cast(dataSet.GetCoordinateSystem().GetBounds().Y.Max) + 1; + } + return 0; +} + +VTKM_CONT +void PNMWriter::WriteToFile(const std::string& fileName, const vtkm::cont::DataSet& dataSet) const +{ + if (!dataSet.HasField(this->PointFieldName)) + { + throw vtkm::cont::ErrorBadValue( + "No pixel data found in DataSet, cannot write without image data!"); + } + + std::ofstream outStream(fileName.c_str(), std::ios_base::binary | std::ios_base::out); + outStream << this->MagicNumber << std::endl + << this->GetImageWidth(dataSet) << " " << this->GetImageHeight(dataSet) << std::endl; + + switch (this->MaxColorValue) + { + case 0: + this->EncodeFile(outStream, dataSet); + break; + case 255: + this->EncodeFile(outStream, dataSet); + break; + case 65535: + this->EncodeFile(outStream, dataSet); + break; + default: + throw vtkm::cont::ErrorBadValue("MaxColorValue: " + std::to_string(this->MaxColorValue) + + " was not one of: {255, 65535}"); + } +} + +VTKM_CONT +template +void PNMWriter::EncodeFile(std::ofstream& outStream, const vtkm::cont::DataSet& dataSet) const +{ + outStream << PixelType::MAX_COLOR_VALUE << std::endl; + auto pixelField = dataSet.GetPointField(this->PointFieldName) + .GetData() + .template Cast>(); + auto pixelPortal = pixelField.ReadPortal(); + + vtkm::UInt32 imageSize = + static_cast(pixelField.GetNumberOfValues() * PixelType::BYTES_PER_PIXEL); + std::vector imageData(imageSize); + + // Write out the data starting from the end (Images are stored Bottom-Left to Top-Right, + // but are viewed from Top-Left to Bottom-Right) + vtkm::Id imageIndex = 0; + vtkm::Id imageHeight = this->GetImageHeight(dataSet); + vtkm::Id imageWidth = this->GetImageWidth(dataSet); + for (vtkm::Id yIndex = imageHeight - 1; yIndex >= 0; yIndex--) + { + for (vtkm::Id xIndex = 0; xIndex < imageWidth; xIndex++, imageIndex++) + { + vtkm::Id index = yIndex * imageWidth + xIndex; + PixelType(pixelPortal.Get(index)).FillImageAtIndexWithPixel(imageData.data(), imageIndex); + } + } + outStream.write((char*)imageData.data(), imageSize); + outStream.close(); +} + +VTKM_CONT +void PNGWriter::WriteToFile(const std::string& fileName, const vtkm::cont::DataSet& dataSet) const +{ + switch (this->MaxColorValue) + { + case 0: + WriteToFile(fileName, dataSet); + break; + case 255: + WriteToFile(fileName, dataSet); + break; + case 65535: + WriteToFile(fileName, dataSet); + break; + default: + throw vtkm::cont::ErrorBadValue("MaxColorValue: " + std::to_string(this->MaxColorValue) + + " was not one of: {255, 65535}"); + } +} + +VTKM_CONT +template +void PNGWriter::WriteToFile(const std::string& fileName, const vtkm::cont::DataSet& dataSet) const +{ + if (!dataSet.HasField(this->PointFieldName)) + { + throw vtkm::cont::ErrorBadValue( + "No pixel data found in DataSet, cannot write without image data!"); + } + + auto pixelField = dataSet.GetPointField(this->PointFieldName) + .GetData() + .template Cast>(); + auto pixelPortal = pixelField.ReadPortal(); + std::vector imageData(static_cast::size_type>( + pixelField.GetNumberOfValues() * PixelType::BYTES_PER_PIXEL)); + + // Write out the data starting from the end (Images are stored Bottom-Left to Top-Right, + // but are viewed from Top-Left to Bottom-Right) + vtkm::Id imageIndex = 0; + vtkm::Id imageHeight = this->GetImageHeight(dataSet); + vtkm::Id imageWidth = this->GetImageWidth(dataSet); + for (vtkm::Id yIndex = imageHeight - 1; yIndex >= 0; yIndex--) + { + for (vtkm::Id xIndex = 0; xIndex < imageWidth; xIndex++, imageIndex++) + { + vtkm::Id index = yIndex * imageWidth + xIndex; + PixelType(pixelPortal.Get(index)).FillImageAtIndexWithPixel(imageData.data(), imageIndex); + } + } + + vtkm::png::lodepng_encode_file(fileName.c_str(), + imageData.data(), + static_cast(imageWidth), + static_cast(imageHeight), + PixelType::PNG_COLOR_TYPE, + PixelType::BIT_DEPTH); +} + +} // namespace io +} // namespace vtkm + +#endif diff --git a/vtkm/io/PixelTypes.h b/vtkm/io/PixelTypes.h new file mode 100644 index 000000000..c6a7b9034 --- /dev/null +++ b/vtkm/io/PixelTypes.h @@ -0,0 +1,201 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ +#ifndef vtk_m_io_PixelTypes_h +#define vtk_m_io_PixelTypes_h + +#include +#include + +VTKM_THIRDPARTY_PRE_INCLUDE +#include +VTKM_THIRDPARTY_POST_INCLUDE + +namespace vtkm +{ +namespace io +{ + +// ---------------------------------------------------------------------- +// Define custom SFINAE structures to calculate the VTKM types associated +// with provided BitDepths +template +struct ComponentTypeFromSize +{ +}; + +template +struct ComponentTypeFromSize::type> +{ + using type = vtkm::UInt8; +}; +template +struct ComponentTypeFromSize::type> +{ + using type = vtkm::UInt16; +}; +// ---------------------------------------------------------------------- + +/// \brief Base type for more complex pixels (RGB, Greyscale, etc) that describes various values +/// such as bit-depth, channel width, bytes per pixel, and how various data should be polled +/// +/// \c BasePixel takes BitDepth and Channels as template parameters. BitDepth describes the number +/// of bits in the pixel, while Channels describes the multiple of bits that are available. +/// BasePixel extends vtkm::Vec. The ComponentType is pulled from the ComponentTypeFromSize +/// SFINAE struct defined above. This helps with optimizing the pixel size for a given +/// bit-depth. The Size is pulled from the Channels param. +/// +/// \c BasePixel requires BitDepths that are > 8 and powers of 2 at the moment. BitDepths of +/// 4, 2, or 1 bit are not correctly handled at the moment. +/// +/// \c BasePixel describes how to populate itself from an unsigned char pointer (assuming that +/// the data stored within the pointer matches the bit-depth and channels described by the +/// BasePixel type), and how to fill in data for an unsigned char pointer. This is mostly +/// useful in serialization and deserialization to various image formats. +/// +template +class BasePixel : public vtkm::Vec::type, Channels> +{ + static_assert(BitDepth >= 8, "BitDepth not >= 8"); + static_assert(!(BitDepth & (BitDepth - 1)), "BitDepth not a power of 2"); + +public: + using Superclass = vtkm::Vec::type, Channels>; + using ComponentType = typename Superclass::ComponentType; + using BaseType = BasePixel; + + static constexpr vtkm::IdComponent BIT_DEPTH = BitDepth; + static constexpr vtkm::IdComponent NUM_BYTES = BitDepth / 8; + static constexpr vtkm::IdComponent MAX_COLOR_VALUE = (1 << BitDepth) - 1; + static constexpr vtkm::IdComponent NUM_CHANNELS = Superclass::NUM_COMPONENTS; + static constexpr vtkm::IdComponent BYTES_PER_PIXEL = NUM_CHANNELS * NUM_BYTES; + + using Superclass::Superclass; + BasePixel() = default; + + /// Fills in this->Components by calling ConstructPixelFromImage. Requires + /// the base vec values to be zeroed out initially. + /// + BasePixel(const unsigned char* imageData, const vtkm::Id index) + : Superclass(0) + { + ConstructPixelFromImage(imageData, index); + } + + virtual ~BasePixel() = default; + + /// Calculates this difference between two pixels as a single value. + /// + virtual ComponentType Diff(const BaseType& pixel) const = 0; + + /// Generates a Vec4f_32 from the current data available in the pixel + /// + virtual vtkm::Vec4f_32 ToVec4f() const = 0; + + /// Implement the << operator for this class type. Will call the overloaded + /// print method for the subclassed type. + /// + friend std::ostream& operator<<(std::ostream& os, const BaseType& basePixel) + { + basePixel.print(os); + return os; + } + + /// Takes an output imageData pointer and in index to a location in that dataset + /// and fills in the pixel data at the location. Utilizes BIT_DEPTH and + /// NUM_CHANNELS to fill in multiple bytes worth of data if necessary. + /// + void FillImageAtIndexWithPixel(unsigned char* imageData, const vtkm::Id index); + +protected: + /// Takes an input imageData pointer and an index to a location in that dataset + /// and fills in this->Components correctly using the provided BIT_DEPTH and + /// NUM_CHANNELS. Does NOT 0 out the current Components. + /// + void ConstructPixelFromImage(const unsigned char* imageData, const vtkm::Id index); + + virtual void print(std::ostream& os) const = 0; +}; + + +template +class RGBPixel : public BasePixel +{ +public: + // RGB values are stored in a vtkm::Vec + using Superclass = BasePixel; + using ComponentType = typename Superclass::ComponentType; + static constexpr vtkm::png::LodePNGColorType PNG_COLOR_TYPE = + vtkm::png::LodePNGColorType::LCT_RGB; + + using Superclass::Superclass; + RGBPixel() = default; + RGBPixel(vtkm::Vec4f_32 tuple) + : Superclass(static_cast(tuple[0] * this->MAX_COLOR_VALUE), + static_cast(tuple[1] * this->MAX_COLOR_VALUE), + static_cast(tuple[2] * this->MAX_COLOR_VALUE)) + { + } + + virtual ~RGBPixel() = default; + + ComponentType Diff(const Superclass& pixel) const override; + vtkm::Vec4f_32 ToVec4f() const override; + +protected: + void print(std::ostream& os) const override + { + os << "(" << (int)this->Components[0] << "," << (int)this->Components[1] << "," + << (int)this->Components[2] << ")"; + } +}; + +// Default types for 8 and 16 bit RGB pixels +using RGBPixel_8 = RGBPixel<8>; +using RGBPixel_16 = RGBPixel<16>; + +template +class GreyPixel : public BasePixel +{ +public: + // Grey values are stored in a vtkm::Vec + // Note: A vec of size 1 is used instead of just a `ComponentType` + // in order to simplify the pixel helper functions + using Superclass = BasePixel; + using ComponentType = typename Superclass::ComponentType; + static constexpr vtkm::png::LodePNGColorType PNG_COLOR_TYPE = + vtkm::png::LodePNGColorType::LCT_GREY; + + using Superclass::Superclass; + GreyPixel() = default; + GreyPixel(vtkm::Vec4f_32 tuple) + : Superclass( + static_cast((tuple[0] + tuple[1] + tuple[2]) * this->MAX_COLOR_VALUE / 3)) + { + } + + virtual ~GreyPixel() = default; + + ComponentType Diff(const Superclass& pixel) const override; + vtkm::Vec4f_32 ToVec4f() const override; + +protected: + void print(std::ostream& os) const override { os << "(" << (int)this->Components[0] << ")"; } +}; + +// Default types for 8 and 16 bit Grey pixels +using GreyPixel_16 = GreyPixel<16>; +using GreyPixel_8 = GreyPixel<8>; + +} // namespace io +} // namespace vtkm + +#include + +#endif //vtk_m_io_PixelTypes_h diff --git a/vtkm/io/PixelTypes.hxx b/vtkm/io/PixelTypes.hxx new file mode 100644 index 000000000..4b7dcbe38 --- /dev/null +++ b/vtkm/io/PixelTypes.hxx @@ -0,0 +1,85 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ +#ifndef vtk_m_io_PixelTypes_hxx +#define vtk_m_io_PixelTypes_hxx + +#include +#include + +namespace vtkm +{ +namespace io +{ + +template +void BasePixel::FillImageAtIndexWithPixel(unsigned char* imageData, const vtkm::Id index) +{ + vtkm::Id initShift = BIT_DEPTH - 8; + for (vtkm::Id channel = 0; channel < NUM_CHANNELS; channel++) + { + for (vtkm::Id shift = initShift, i = 0; shift >= 0; shift -= 8, i++) + { + imageData[index * BYTES_PER_PIXEL + i + (channel * NUM_BYTES)] = + static_cast((this->Components[channel] & (0xff << shift)) >> shift); + } + } +} + +template +void BasePixel::ConstructPixelFromImage(const unsigned char* imageData, const vtkm::Id index) +{ + vtkm::Id initShift = BIT_DEPTH - 8; + for (vtkm::Id channel = 0; channel < NUM_CHANNELS; channel++) + { + for (vtkm::Id shift = initShift, i = 0; shift >= 0; shift -= 8, i++) + { + this->Components[channel] |= imageData[index * BYTES_PER_PIXEL + i + (channel * NUM_BYTES)] + << shift; + } + } +} + +template +typename RGBPixel::ComponentType RGBPixel::Diff(const Superclass& pixel) const +{ + return static_cast::ComponentType>(vtkm::Abs(this->Components[0] - pixel[0]) + + vtkm::Abs(this->Components[1] - pixel[1]) + + vtkm::Abs(this->Components[2] - pixel[2])); +} + +template +vtkm::Vec4f_32 RGBPixel::ToVec4f() const +{ + return vtkm::Vec4f_32(static_cast(this->Components[0]) / this->MAX_COLOR_VALUE, + static_cast(this->Components[1]) / this->MAX_COLOR_VALUE, + static_cast(this->Components[2]) / this->MAX_COLOR_VALUE, + 1); +} + +template +typename GreyPixel::ComponentType GreyPixel::Diff(const Superclass& pixel) const +{ + return static_cast::ComponentType>(vtkm::Abs(this->Components[0] - pixel[0])); +} + +template +vtkm::Vec4f_32 GreyPixel::ToVec4f() const +{ + return vtkm::Vec4f_32(static_cast(this->Components[0]) / this->MAX_COLOR_VALUE, + static_cast(this->Components[0]) / this->MAX_COLOR_VALUE, + static_cast(this->Components[0]) / this->MAX_COLOR_VALUE, + 1); +} + + +} // namespace io +} // namespace vtkm + +#endif //vtk_m_io_PixelTypes_h diff --git a/vtkm/io/VTKDataSetReader.cxx b/vtkm/io/VTKDataSetReader.cxx new file mode 100644 index 000000000..a1fdbbdb9 --- /dev/null +++ b/vtkm/io/VTKDataSetReader.cxx @@ -0,0 +1,92 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +#include +#include +#include +#include +#include + +#include + +namespace vtkm +{ +namespace io +{ + +VTKDataSetReader::VTKDataSetReader(const char* fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKDataSetReader::VTKDataSetReader(const std::string& fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKDataSetReader::~VTKDataSetReader() +{ +} + +void VTKDataSetReader::PrintSummary(std::ostream& out) const +{ + if (this->Reader) + { + this->Reader->PrintSummary(out); + } + else + { + VTKDataSetReaderBase::PrintSummary(out); + } +} + +void VTKDataSetReader::CloseFile() +{ + if (this->Reader) + { + this->Reader->CloseFile(); + } + else + { + VTKDataSetReaderBase::CloseFile(); + } +} + +void VTKDataSetReader::Read() +{ + switch (this->DataFile->Structure) + { + case vtkm::io::internal::DATASET_STRUCTURED_POINTS: + this->Reader.reset(new VTKStructuredPointsReader("")); + break; + case vtkm::io::internal::DATASET_STRUCTURED_GRID: + this->Reader.reset(new VTKStructuredGridReader("")); + break; + case vtkm::io::internal::DATASET_RECTILINEAR_GRID: + this->Reader.reset(new VTKRectilinearGridReader("")); + break; + case vtkm::io::internal::DATASET_POLYDATA: + this->Reader.reset(new VTKPolyDataReader("")); + break; + case vtkm::io::internal::DATASET_UNSTRUCTURED_GRID: + this->Reader.reset(new VTKUnstructuredGridReader("")); + break; + default: + throw vtkm::io::ErrorIO("Unsupported DataSet type."); + } + + this->TransferDataFile(*this->Reader.get()); + this->Reader->Read(); + this->DataSet = this->Reader->GetDataSet(); +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKDataSetReader.h b/vtkm/io/VTKDataSetReader.h index 166c9466d..49e4b4537 100644 --- a/vtkm/io/VTKDataSetReader.h +++ b/vtkm/io/VTKDataSetReader.h @@ -11,86 +11,27 @@ #define vtk_m_io_VTKDataSetReader_h #include -#include -#include -#include -#include -#include - -#include namespace vtkm { namespace io { -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKDataSetReader : public VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKDataSetReader : public VTKDataSetReaderBase { public: - explicit VTKDataSetReader(const char* fileName) - : VTKDataSetReaderBase(fileName) - { - } + VTKM_CONT VTKDataSetReader(const char* fileName); + VTKM_CONT VTKDataSetReader(const std::string& fileName); + VTKM_CONT ~VTKDataSetReader() override; - explicit VTKDataSetReader(const std::string& fileName) - : VTKDataSetReaderBase(fileName) - { - } + VTKDataSetReader(const VTKDataSetReader&) = delete; + void operator=(const VTKDataSetReader&) = delete; - virtual void PrintSummary(std::ostream& out) const - { - if (this->Reader) - { - this->Reader->PrintSummary(out); - } - else - { - VTKDataSetReaderBase::PrintSummary(out); - } - } + VTKM_CONT void PrintSummary(std::ostream& out) const override; private: - virtual void CloseFile() - { - if (this->Reader) - { - this->Reader->CloseFile(); - } - else - { - VTKDataSetReaderBase::CloseFile(); - } - } - - virtual void Read() - { - switch (this->DataFile->Structure) - { - case vtkm::io::internal::DATASET_STRUCTURED_POINTS: - this->Reader.reset(new VTKStructuredPointsReader("")); - break; - case vtkm::io::internal::DATASET_STRUCTURED_GRID: - this->Reader.reset(new VTKStructuredGridReader("")); - break; - case vtkm::io::internal::DATASET_RECTILINEAR_GRID: - this->Reader.reset(new VTKRectilinearGridReader("")); - break; - case vtkm::io::internal::DATASET_POLYDATA: - this->Reader.reset(new VTKPolyDataReader("")); - break; - case vtkm::io::internal::DATASET_UNSTRUCTURED_GRID: - this->Reader.reset(new VTKUnstructuredGridReader("")); - break; - default: - throw vtkm::io::ErrorIO("Unsupported DataSet type."); - } - - this->TransferDataFile(*this->Reader.get()); - this->Reader->Read(); - this->DataSet = this->Reader->GetDataSet(); - } + VTKM_CONT void CloseFile() override; + VTKM_CONT void Read() override; std::unique_ptr Reader; }; diff --git a/vtkm/io/VTKDataSetReaderBase.cxx b/vtkm/io/VTKDataSetReaderBase.cxx new file mode 100644 index 000000000..dcbace6e5 --- /dev/null +++ b/vtkm/io/VTKDataSetReaderBase.cxx @@ -0,0 +1,826 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace +{ + +inline void PrintVTKDataFileSummary(const vtkm::io::internal::VTKDataSetFile& df, std::ostream& out) +{ + out << "\tFile: " << df.FileName << std::endl; + out << "\tVersion: " << df.Version[0] << "." << df.Version[0] << std::endl; + out << "\tTitle: " << df.Title << std::endl; + out << "\tFormat: " << (df.IsBinary ? "BINARY" : "ASCII") << std::endl; + out << "\tDataSet type: " << vtkm::io::internal::DataSetStructureString(df.Structure) + << std::endl; +} + +// Since Fields and DataSets store data in the default VariantArrayHandle, convert +// the data to the closest type supported by default. The following will +// need to be updated if VariantArrayHandle or TypeListCommon changes. +template +struct ClosestCommonType +{ + using Type = T; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int32; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int32; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int32; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int32; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int64; +}; +template <> +struct ClosestCommonType +{ + using Type = vtkm::Int64; +}; + +template +struct ClosestFloat +{ + using Type = T; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float32; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float32; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float32; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float32; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float64; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float64; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float64; +}; +template <> +struct ClosestFloat +{ + using Type = vtkm::Float64; +}; + +template +vtkm::cont::VariantArrayHandle CreateVariantArrayHandle(const std::vector& vec) +{ + switch (vtkm::VecTraits::NUM_COMPONENTS) + { + case 1: + { + using CommonType = typename ClosestCommonType::Type; + constexpr bool not_same = !std::is_same::value; + if (not_same) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + "Type " << vtkm::io::internal::DataTypeName::Name() + << " is currently unsupported. Converting to " + << vtkm::io::internal::DataTypeName::Name() + << "."); + } + + vtkm::cont::ArrayHandle output; + output.Allocate(static_cast(vec.size())); + auto portal = output.WritePortal(); + for (vtkm::Id i = 0; i < output.GetNumberOfValues(); ++i) + { + portal.Set(i, static_cast(vec[static_cast(i)])); + } + + return vtkm::cont::VariantArrayHandle(output); + } + case 2: + case 3: + case 9: + { + constexpr auto numComps = vtkm::VecTraits::NUM_COMPONENTS; + + using InComponentType = typename vtkm::VecTraits::ComponentType; + using OutComponentType = typename ClosestFloat::Type; + using CommonType = vtkm::Vec; + constexpr bool not_same = !std::is_same::value; + if (not_same) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + "Type " << vtkm::io::internal::DataTypeName::Name() << "[" + << vtkm::VecTraits::GetNumberOfComponents(T()) + << "] " + << "is currently unsupported. Converting to " + << vtkm::io::internal::DataTypeName::Name() + << "[" + << numComps + << "]."); + } + + vtkm::cont::ArrayHandle output; + output.Allocate(static_cast(vec.size())); + auto portal = output.WritePortal(); + for (vtkm::Id i = 0; i < output.GetNumberOfValues(); ++i) + { + CommonType outval = CommonType(); + for (vtkm::IdComponent j = 0; j < numComps; ++j) + { + outval[j] = static_cast( + vtkm::VecTraits::GetComponent(vec[static_cast(i)], j)); + } + portal.Set(i, outval); + } + + return vtkm::cont::VariantArrayHandle(output); + } + default: + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Only 1, 2, 3, or 9 components supported. Skipping."); + return vtkm::cont::VariantArrayHandle(vtkm::cont::ArrayHandle()); + } + } +} + +} // anonymous namespace + +namespace vtkm +{ +namespace io +{ + +VTKDataSetReaderBase::VTKDataSetReaderBase(const char* fileName) + : DataFile(new internal::VTKDataSetFile) + , DataSet() + , Loaded(false) +{ + this->DataFile->FileName = fileName; +} + +VTKDataSetReaderBase::VTKDataSetReaderBase(const std::string& fileName) + : DataFile(new internal::VTKDataSetFile) + , DataSet() + , Loaded(false) +{ + this->DataFile->FileName = fileName; +} + +VTKDataSetReaderBase::~VTKDataSetReaderBase() +{ +} + +const vtkm::cont::DataSet& VTKDataSetReaderBase::ReadDataSet() +{ + if (!this->Loaded) + { + try + { + this->OpenFile(); + this->ReadHeader(); + this->Read(); + this->CloseFile(); + this->Loaded = true; + } + catch (std::ifstream::failure& e) + { + std::string message("IO Error: "); + throw vtkm::io::ErrorIO(message + e.what()); + } + } + + return this->DataSet; +} + +void VTKDataSetReaderBase::PrintSummary(std::ostream& out) const +{ + out << "VTKDataSetReader" << std::endl; + PrintVTKDataFileSummary(*this->DataFile.get(), out); + this->DataSet.PrintSummary(out); +} + +void VTKDataSetReaderBase::ReadPoints() +{ + std::string dataType; + std::size_t numPoints; + this->DataFile->Stream >> numPoints >> dataType >> std::ws; + + vtkm::cont::VariantArrayHandle points = + this->DoReadArrayVariant(vtkm::cont::Field::Association::POINTS, dataType, numPoints, 3); + + this->DataSet.AddCoordinateSystem(vtkm::cont::CoordinateSystem("coordinates", points)); +} + +void VTKDataSetReaderBase::ReadCells(vtkm::cont::ArrayHandle& connectivity, + vtkm::cont::ArrayHandle& numIndices) +{ + vtkm::Id numCells, numInts; + this->DataFile->Stream >> numCells >> numInts >> std::ws; + + connectivity.Allocate(numInts - numCells); + numIndices.Allocate(numCells); + + std::vector buffer(static_cast(numInts)); + this->ReadArray(buffer); + + vtkm::Int32* buffp = &buffer[0]; + auto connectivityPortal = connectivity.WritePortal(); + auto numIndicesPortal = numIndices.WritePortal(); + for (vtkm::Id i = 0, connInd = 0; i < numCells; ++i) + { + vtkm::IdComponent numInds = static_cast(*buffp++); + numIndicesPortal.Set(i, numInds); + for (vtkm::IdComponent j = 0; j < numInds; ++j, ++connInd) + { + connectivityPortal.Set(connInd, static_cast(*buffp++)); + } + } +} + +void VTKDataSetReaderBase::ReadShapes(vtkm::cont::ArrayHandle& shapes) +{ + std::string tag; + vtkm::Id numCells; + this->DataFile->Stream >> tag >> numCells >> std::ws; + internal::parseAssert(tag == "CELL_TYPES"); + + shapes.Allocate(numCells); + std::vector buffer(static_cast(numCells)); + this->ReadArray(buffer); + + vtkm::Int32* buffp = &buffer[0]; + auto shapesPortal = shapes.WritePortal(); + for (vtkm::Id i = 0; i < numCells; ++i) + { + shapesPortal.Set(i, static_cast(*buffp++)); + } +} + +void VTKDataSetReaderBase::ReadAttributes() +{ + if (this->DataFile->Stream.eof()) + { + return; + } + + vtkm::cont::Field::Association association = vtkm::cont::Field::Association::ANY; + std::size_t size; + + std::string tag; + this->DataFile->Stream >> tag; + while (!this->DataFile->Stream.eof()) + { + if (tag == "POINT_DATA") + { + association = vtkm::cont::Field::Association::POINTS; + } + else if (tag == "CELL_DATA") + { + association = vtkm::cont::Field::Association::CELL_SET; + } + else + { + internal::parseAssert(false); + } + + this->DataFile->Stream >> size; + while (!this->DataFile->Stream.eof()) + { + this->DataFile->Stream >> tag; + if (tag == "SCALARS") + { + this->ReadScalars(association, size); + } + else if (tag == "COLOR_SCALARS") + { + this->ReadColorScalars(association, size); + } + else if (tag == "LOOKUP_TABLE") + { + this->ReadLookupTable(); + } + else if (tag == "VECTORS" || tag == "NORMALS") + { + this->ReadVectors(association, size); + } + else if (tag == "TEXTURE_COORDINATES") + { + this->ReadTextureCoordinates(association, size); + } + else if (tag == "TENSORS") + { + this->ReadTensors(association, size); + } + else if (tag == "FIELD") + { + this->ReadFields(association, size); + } + else + { + break; + } + } + } +} + +void VTKDataSetReaderBase::CloseFile() +{ + this->DataFile->Stream.close(); +} + +void VTKDataSetReaderBase::OpenFile() +{ + this->DataFile->Stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); + try + { + this->DataFile->Stream.open(this->DataFile->FileName.c_str(), + std::ios_base::in | std::ios_base::binary); + } + catch (std::ifstream::failure&) + { + std::string message("could not open file \"" + this->DataFile->FileName + "\""); + throw vtkm::io::ErrorIO(message); + } +} + +void VTKDataSetReaderBase::ReadHeader() +{ + char vstring[] = "# vtk DataFile Version"; + const std::size_t vlen = sizeof(vstring); + + // Read version line + char vbuf[vlen]; + this->DataFile->Stream.read(vbuf, vlen - 1); + vbuf[vlen - 1] = '\0'; + if (std::string(vbuf) != std::string(vstring)) + { + throw vtkm::io::ErrorIO("Incorrect file format."); + } + + char dot; + this->DataFile->Stream >> this->DataFile->Version[0] >> dot >> this->DataFile->Version[1]; + // skip rest of the line + std::string skip; + std::getline(this->DataFile->Stream, skip); + + if ((this->DataFile->Version[0] > 4) || + (this->DataFile->Version[0] == 4 && this->DataFile->Version[1] > 2)) + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Reader may not correctly read >v4.2 files. Reading version " + << this->DataFile->Version[0] + << "." + << this->DataFile->Version[1] + << ".\n"); + } + + // Read title line + std::getline(this->DataFile->Stream, this->DataFile->Title); + + // Read format line + this->DataFile->IsBinary = false; + std::string format; + this->DataFile->Stream >> format >> std::ws; + if (format == "BINARY") + { + this->DataFile->IsBinary = true; + } + else if (format != "ASCII") + { + throw vtkm::io::ErrorIO("Unsupported Format."); + } + + // Read structure line + std::string tag, structStr; + this->DataFile->Stream >> tag >> structStr >> std::ws; + internal::parseAssert(tag == "DATASET"); + + this->DataFile->Structure = vtkm::io::internal::DataSetStructureId(structStr); + if (this->DataFile->Structure == vtkm::io::internal::DATASET_UNKNOWN) + { + throw vtkm::io::ErrorIO("Unsupported DataSet type."); + } +} + + +void VTKDataSetReaderBase::AddField(const std::string& name, + vtkm::cont::Field::Association association, + vtkm::cont::VariantArrayHandle& data) +{ + if (data.GetNumberOfValues() > 0) + { + switch (association) + { + case vtkm::cont::Field::Association::POINTS: + case vtkm::cont::Field::Association::WHOLE_MESH: + this->DataSet.AddField(vtkm::cont::Field(name, association, data)); + break; + case vtkm::cont::Field::Association::CELL_SET: + this->DataSet.AddField(vtkm::cont::Field(name, association, data)); + break; + default: + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Not recording field '" << name << "' because it has an unknown association"); + break; + } + } +} + +void VTKDataSetReaderBase::ReadScalars(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + std::string dataName, dataType, lookupTableName; + vtkm::IdComponent numComponents = 1; + this->DataFile->Stream >> dataName >> dataType; + std::string tag; + this->DataFile->Stream >> tag; + if (tag != "LOOKUP_TABLE") + { + try + { + numComponents = std::stoi(tag); + } + catch (std::invalid_argument&) + { + internal::parseAssert(false); + } + this->DataFile->Stream >> tag; + } + + internal::parseAssert(tag == "LOOKUP_TABLE"); + this->DataFile->Stream >> lookupTableName >> std::ws; + + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, numComponents); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadColorScalars(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Support for COLOR_SCALARS is not implemented. Skipping."); + + std::string dataName; + vtkm::IdComponent numComponents; + this->DataFile->Stream >> dataName >> numComponents >> std::ws; + std::string dataType = this->DataFile->IsBinary ? "unsigned_char" : "float"; + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, numComponents); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadLookupTable() +{ + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Support for LOOKUP_TABLE is not implemented. Skipping."); + + std::string dataName; + std::size_t numEntries; + this->DataFile->Stream >> dataName >> numEntries >> std::ws; + this->SkipArray(numEntries, vtkm::Vec()); +} + +void VTKDataSetReaderBase::ReadTextureCoordinates(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + std::string dataName; + vtkm::IdComponent numComponents; + std::string dataType; + this->DataFile->Stream >> dataName >> numComponents >> dataType >> std::ws; + + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, numComponents); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadVectors(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + std::string dataName; + std::string dataType; + this->DataFile->Stream >> dataName >> dataType >> std::ws; + + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, 3); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadTensors(vtkm::cont::Field::Association association, + std::size_t numElements) +{ + std::string dataName; + std::string dataType; + this->DataFile->Stream >> dataName >> dataType >> std::ws; + + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numElements, 9); + this->AddField(dataName, association, data); +} + +void VTKDataSetReaderBase::ReadFields(vtkm::cont::Field::Association association, + std::size_t expectedNumElements) +{ + std::string dataName; + vtkm::Id numArrays; + this->DataFile->Stream >> dataName >> numArrays >> std::ws; + for (vtkm::Id i = 0; i < numArrays; ++i) + { + std::size_t numTuples; + vtkm::IdComponent numComponents; + std::string arrayName, dataType; + this->DataFile->Stream >> arrayName >> numComponents >> numTuples >> dataType >> std::ws; + if (numTuples == expectedNumElements) + { + vtkm::cont::VariantArrayHandle data = + this->DoReadArrayVariant(association, dataType, numTuples, numComponents); + this->AddField(arrayName, association, data); + } + else + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Field " << arrayName + << "'s size does not match expected number of elements. Skipping"); + } + } +} + +void VTKDataSetReaderBase::ReadGlobalFields(std::vector* visitBounds) +{ + std::string dataName; + vtkm::Id numArrays; + this->DataFile->Stream >> dataName >> numArrays >> std::ws; + for (vtkm::Id i = 0; i < numArrays; ++i) + { + std::size_t numTuples; + vtkm::IdComponent numComponents; + std::string arrayName, dataType; + this->DataFile->Stream >> arrayName >> numComponents >> numTuples >> dataType >> std::ws; + if (arrayName == "avtOriginalBounds" && visitBounds) + { + visitBounds->resize(6); + internal::parseAssert(numComponents == 1 && numTuples == 6); + // parse the bounds and fill the bounds vector + this->ReadArray(*visitBounds); + } + else + { + VTKM_LOG_S(vtkm::cont::LogLevel::Info, + "Support for global field " << arrayName << " not implemented. Skipping."); + this->DoSkipArrayVariant(dataType, numTuples, numComponents); + } + } +} + +class VTKDataSetReaderBase::SkipArrayVariant +{ +public: + SkipArrayVariant(VTKDataSetReaderBase* reader, std::size_t numElements) + : Reader(reader) + , NumElements(numElements) + { + } + + template + void operator()(T) const + { + this->Reader->SkipArray(this->NumElements, T()); + } + + template + void operator()(vtkm::IdComponent numComponents, T) const + { + this->Reader->SkipArray(this->NumElements * static_cast(numComponents), T()); + } + +protected: + VTKDataSetReaderBase* Reader; + std::size_t NumElements; +}; + +class VTKDataSetReaderBase::ReadArrayVariant : public SkipArrayVariant +{ +public: + ReadArrayVariant(VTKDataSetReaderBase* reader, + vtkm::cont::Field::Association association, + std::size_t numElements, + vtkm::cont::VariantArrayHandle& data) + : SkipArrayVariant(reader, numElements) + , Association(association) + , Data(&data) + { + } + + template + void operator()(T) const + { + std::vector buffer(this->NumElements); + this->Reader->ReadArray(buffer); + if ((this->Association != vtkm::cont::Field::Association::CELL_SET) || + (this->Reader->GetCellsPermutation().GetNumberOfValues() < 1)) + { + *this->Data = CreateVariantArrayHandle(buffer); + } + else + { + // If we are reading data associated with a cell set, we need to (sometimes) permute the + // data due to differences between VTK and VTK-m cell shapes. + auto permutation = this->Reader->GetCellsPermutation().ReadPortal(); + vtkm::Id outSize = permutation.GetNumberOfValues(); + std::vector permutedBuffer(static_cast(outSize)); + for (vtkm::Id outIndex = 0; outIndex < outSize; outIndex++) + { + std::size_t inIndex = static_cast(permutation.Get(outIndex)); + permutedBuffer[static_cast(outIndex)] = buffer[inIndex]; + } + *this->Data = CreateVariantArrayHandle(permutedBuffer); + } + } + + template + void operator()(vtkm::IdComponent numComponents, T) const + { + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Support for " << numComponents << " components not implemented. Skipping."); + SkipArrayVariant::operator()(numComponents, T()); + } + +private: + vtkm::cont::Field::Association Association; + vtkm::cont::VariantArrayHandle* Data; +}; + +void VTKDataSetReaderBase::DoSkipArrayVariant(std::string dataType, + std::size_t numElements, + vtkm::IdComponent numComponents) +{ + // string is unsupported for SkipArrayVariant, so it requires some + // special handling + if (dataType == "string") + { + const vtkm::Id stringCount = numComponents * static_cast(numElements); + for (vtkm::Id i = 0; i < stringCount; ++i) + { + std::string trash; + this->DataFile->Stream >> trash; + } + } + else + { + vtkm::io::internal::DataType typeId = vtkm::io::internal::DataTypeId(dataType); + vtkm::io::internal::SelectTypeAndCall( + typeId, numComponents, SkipArrayVariant(this, numElements)); + } +} + +vtkm::cont::VariantArrayHandle VTKDataSetReaderBase::DoReadArrayVariant( + vtkm::cont::Field::Association association, + std::string dataType, + std::size_t numElements, + vtkm::IdComponent numComponents) +{ + // Create empty data to start so that the return can check if data were actually read + vtkm::cont::ArrayHandle empty; + vtkm::cont::VariantArrayHandle data(empty); + + vtkm::io::internal::DataType typeId = vtkm::io::internal::DataTypeId(dataType); + vtkm::io::internal::SelectTypeAndCall( + typeId, numComponents, ReadArrayVariant(this, association, numElements, data)); + + return data; +} + +void VTKDataSetReaderBase::ReadArray(std::vector& buffer) +{ + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, + "Support for data type 'bit' is not implemented. Skipping."); + this->SkipArray(buffer.size(), vtkm::io::internal::DummyBitType()); + buffer.clear(); +} + +void VTKDataSetReaderBase::SkipArray(std::size_t numElements, + vtkm::io::internal::DummyBitType, + vtkm::IdComponent numComponents) +{ + if (this->DataFile->IsBinary) + { + numElements = (numElements + 7) / 8; + this->DataFile->Stream.seekg(static_cast(numElements), std::ios_base::cur); + } + else + { + for (std::size_t i = 0; i < numElements; ++i) + { + vtkm::UInt16 val; + this->DataFile->Stream >> val; + } + } + this->DataFile->Stream >> std::ws; + this->SkipArrayMetaData(numComponents); +} + +void VTKDataSetReaderBase::SkipArrayMetaData(vtkm::IdComponent numComponents) +{ + if (!this->DataFile->Stream.good()) + { + return; + } + + auto begining = this->DataFile->Stream.tellg(); + + std::string tag; + this->DataFile->Stream >> tag; + if (tag != "METADATA") + { + this->DataFile->Stream.seekg(begining); + return; + } + + VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "METADATA is not supported. Attempting to Skip."); + + this->DataFile->Stream >> tag >> std::ws; + if (tag == "COMPONENT_NAMES") + { + std::string name; + for (vtkm::IdComponent i = 0; i < numComponents; ++i) + { + this->DataFile->Stream >> name >> std::ws; + } + } + else if (tag == "INFORMATION") + { + int numKeys = 0; + this->DataFile->Stream >> numKeys >> std::ws; + + // Skipping INFORMATION is tricky. The reader needs to be aware of the types of the + // information, which is not provided in the file. + // Here we will just skip until an empty line is found. + // However, if there are no keys, then there is nothing to read (and the stream tends + // to skip over empty lines. + if (numKeys > 0) + { + std::string line; + do + { + std::getline(this->DataFile->Stream, line); + } while (this->DataFile->Stream.good() && !line.empty()); + + // Eat any remaining whitespace after the INFORMATION to be ready to read the next token + this->DataFile->Stream >> std::ws; + } + } + else + { + internal::parseAssert(false); + } +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKDataSetReaderBase.h b/vtkm/io/VTKDataSetReaderBase.h index 70292392c..245d46eb0 100644 --- a/vtkm/io/VTKDataSetReaderBase.h +++ b/vtkm/io/VTKDataSetReaderBase.h @@ -10,25 +10,16 @@ #ifndef vtk_m_io_VTKDataSetReaderBase_h #define vtk_m_io_VTKDataSetReaderBase_h +#include +#include +#include +#include + #include -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include #include -#include -#include namespace vtkm { @@ -48,16 +39,6 @@ struct VTKDataSetFile std::ifstream Stream; }; -inline void PrintVTKDataFileSummary(const VTKDataSetFile& df, std::ostream& out) -{ - out << "\tFile: " << df.FileName << std::endl; - out << "\tVersion: " << df.Version[0] << "." << df.Version[0] << std::endl; - out << "\tTitle: " << df.Title << std::endl; - out << "\tFormat: " << (df.IsBinary ? "BINARY" : "ASCII") << std::endl; - out << "\tDataSet type: " << vtkm::io::internal::DataSetStructureString(df.Structure) - << std::endl; -} - inline void parseAssert(bool condition) { if (!condition) @@ -82,166 +63,6 @@ struct StreamIOType using Type = vtkm::UInt16; }; -// Since Fields and DataSets store data in the default VariantArrayHandle, convert -// the data to the closest type supported by default. The following will -// need to be updated if VariantArrayHandle or TypeListCommon changes. -template -struct ClosestCommonType -{ - using Type = T; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int32; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int64; -}; -template <> -struct ClosestCommonType -{ - using Type = vtkm::Int64; -}; - -template -struct ClosestFloat -{ - using Type = T; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float32; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; -template <> -struct ClosestFloat -{ - using Type = vtkm::Float64; -}; - -template -vtkm::cont::VariantArrayHandle CreateVariantArrayHandle(const std::vector& vec) -{ - switch (vtkm::VecTraits::NUM_COMPONENTS) - { - case 1: - { - using CommonType = typename ClosestCommonType::Type; - constexpr bool not_same = !std::is_same::value; - if (not_same) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "Type " << vtkm::io::internal::DataTypeName::Name() - << " is currently unsupported. Converting to " - << vtkm::io::internal::DataTypeName::Name() - << "."); - } - - vtkm::cont::ArrayHandle output; - output.Allocate(static_cast(vec.size())); - auto portal = output.WritePortal(); - for (vtkm::Id i = 0; i < output.GetNumberOfValues(); ++i) - { - portal.Set(i, static_cast(vec[static_cast(i)])); - } - - return vtkm::cont::VariantArrayHandle(output); - } - case 2: - case 3: - case 9: - { - constexpr auto numComps = vtkm::VecTraits::NUM_COMPONENTS; - - using InComponentType = typename vtkm::VecTraits::ComponentType; - using OutComponentType = typename ClosestFloat::Type; - using CommonType = vtkm::Vec; - constexpr bool not_same = !std::is_same::value; - if (not_same) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "Type " << vtkm::io::internal::DataTypeName::Name() << "[" - << vtkm::VecTraits::GetNumberOfComponents(T()) - << "] " - << "is currently unsupported. Converting to " - << vtkm::io::internal::DataTypeName::Name() - << "[" - << numComps - << "]."); - } - - vtkm::cont::ArrayHandle output; - output.Allocate(static_cast(vec.size())); - auto portal = output.WritePortal(); - for (vtkm::Id i = 0; i < output.GetNumberOfValues(); ++i) - { - CommonType outval = CommonType(); - for (vtkm::IdComponent j = 0; j < numComps; ++j) - { - outval[j] = static_cast( - vtkm::VecTraits::GetComponent(vec[static_cast(i)], j)); - } - portal.Set(i, outval); - } - - return vtkm::cont::VariantArrayHandle(output); - } - default: - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Only 1, 2, 3, or 9 components supported. Skipping."); - return vtkm::cont::VariantArrayHandle(vtkm::cont::ArrayHandle()); - } - } -} - inline vtkm::cont::DynamicCellSet CreateCellSetStructured(const vtkm::Id3& dim) { if (dim[0] > 1 && dim[1] > 1 && dim[2] > 1) @@ -272,564 +93,102 @@ inline vtkm::cont::DynamicCellSet CreateCellSetStructured(const vtkm::Id3& dim) } // namespace internal -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKDataSetReaderBase { +protected: + std::unique_ptr DataFile; + vtkm::cont::DataSet DataSet; + +private: + bool Loaded; + vtkm::cont::ArrayHandle CellsPermutation; + + friend class VTKDataSetReader; + public: - explicit VTKDataSetReaderBase(const char* fileName) - : DataFile(new internal::VTKDataSetFile) - , DataSet() - , Loaded(false) - { - this->DataFile->FileName = fileName; - } + explicit VTKM_CONT VTKDataSetReaderBase(const char* fileName); - explicit VTKDataSetReaderBase(const std::string& fileName) - : DataFile(new internal::VTKDataSetFile) - , DataSet() - , Loaded(false) - { - this->DataFile->FileName = fileName; - } + explicit VTKM_CONT VTKDataSetReaderBase(const std::string& fileName); - virtual ~VTKDataSetReaderBase() {} + virtual VTKM_CONT ~VTKDataSetReaderBase(); - const vtkm::cont::DataSet& ReadDataSet() - { - if (!this->Loaded) - { - try - { - this->OpenFile(); - this->ReadHeader(); - this->Read(); - this->CloseFile(); - this->Loaded = true; - } - catch (std::ifstream::failure& e) - { - std::string message("IO Error: "); - throw vtkm::io::ErrorIO(message + e.what()); - } - } + VTKDataSetReaderBase(const VTKDataSetReaderBase&) = delete; + void operator=(const VTKDataSetReaderBase&) = delete; - return this->DataSet; - } + const VTKM_CONT vtkm::cont::DataSet& ReadDataSet(); const vtkm::cont::DataSet& GetDataSet() const { return this->DataSet; } - virtual void PrintSummary(std::ostream& out) const - { - out << "VTKDataSetReader" << std::endl; - PrintVTKDataFileSummary(*this->DataFile.get(), out); - this->DataSet.PrintSummary(out); - } + virtual VTKM_CONT void PrintSummary(std::ostream& out) const; protected: - void ReadPoints() - { - std::string dataType; - std::size_t numPoints; - this->DataFile->Stream >> numPoints >> dataType >> std::ws; + VTKM_CONT void ReadPoints(); - vtkm::cont::VariantArrayHandle points = - this->DoReadArrayVariant(vtkm::cont::Field::Association::POINTS, dataType, numPoints, 3); + VTKM_CONT void ReadCells(vtkm::cont::ArrayHandle& connectivity, + vtkm::cont::ArrayHandle& numIndices); - this->DataSet.AddCoordinateSystem(vtkm::cont::CoordinateSystem("coordinates", points)); - } + VTKM_CONT void ReadShapes(vtkm::cont::ArrayHandle& shapes); - void ReadCells(vtkm::cont::ArrayHandle& connectivity, - vtkm::cont::ArrayHandle& numIndices) - { - vtkm::Id numCells, numInts; - this->DataFile->Stream >> numCells >> numInts >> std::ws; - - connectivity.Allocate(numInts - numCells); - numIndices.Allocate(numCells); - - std::vector buffer(static_cast(numInts)); - this->ReadArray(buffer); - - vtkm::Int32* buffp = &buffer[0]; - auto connectivityPortal = connectivity.WritePortal(); - auto numIndicesPortal = numIndices.WritePortal(); - for (vtkm::Id i = 0, connInd = 0; i < numCells; ++i) - { - vtkm::IdComponent numInds = static_cast(*buffp++); - numIndicesPortal.Set(i, numInds); - for (vtkm::IdComponent j = 0; j < numInds; ++j, ++connInd) - { - connectivityPortal.Set(connInd, static_cast(*buffp++)); - } - } - } - - void ReadShapes(vtkm::cont::ArrayHandle& shapes) - { - std::string tag; - vtkm::Id numCells; - this->DataFile->Stream >> tag >> numCells >> std::ws; - internal::parseAssert(tag == "CELL_TYPES"); - - shapes.Allocate(numCells); - std::vector buffer(static_cast(numCells)); - this->ReadArray(buffer); - - vtkm::Int32* buffp = &buffer[0]; - auto shapesPortal = shapes.WritePortal(); - for (vtkm::Id i = 0; i < numCells; ++i) - { - shapesPortal.Set(i, static_cast(*buffp++)); - } - } - - void ReadAttributes() - { - if (this->DataFile->Stream.eof()) - { - return; - } - - vtkm::cont::Field::Association association = vtkm::cont::Field::Association::ANY; - std::size_t size; - - std::string tag; - this->DataFile->Stream >> tag; - while (!this->DataFile->Stream.eof()) - { - if (tag == "POINT_DATA") - { - association = vtkm::cont::Field::Association::POINTS; - } - else if (tag == "CELL_DATA") - { - association = vtkm::cont::Field::Association::CELL_SET; - } - else - { - internal::parseAssert(false); - } - - this->DataFile->Stream >> size; - while (!this->DataFile->Stream.eof()) - { - this->DataFile->Stream >> tag; - if (tag == "SCALARS") - { - this->ReadScalars(association, size); - } - else if (tag == "COLOR_SCALARS") - { - this->ReadColorScalars(association, size); - } - else if (tag == "LOOKUP_TABLE") - { - this->ReadLookupTable(); - } - else if (tag == "VECTORS" || tag == "NORMALS") - { - this->ReadVectors(association, size); - } - else if (tag == "TEXTURE_COORDINATES") - { - this->ReadTextureCoordinates(association, size); - } - else if (tag == "TENSORS") - { - this->ReadTensors(association, size); - } - else if (tag == "FIELD") - { - this->ReadFields(association, size); - } - else - { - break; - } - } - } - } + VTKM_CONT void ReadAttributes(); void SetCellsPermutation(const vtkm::cont::ArrayHandle& permutation) { this->CellsPermutation = permutation; } - vtkm::cont::ArrayHandle GetCellsPermutation() const { return this->CellsPermutation; } + VTKM_CONT vtkm::cont::ArrayHandle GetCellsPermutation() const + { + return this->CellsPermutation; + } - void TransferDataFile(VTKDataSetReaderBase& reader) + VTKM_CONT void TransferDataFile(VTKDataSetReaderBase& reader) { reader.DataFile.swap(this->DataFile); this->DataFile.reset(nullptr); } - virtual void CloseFile() { this->DataFile->Stream.close(); } + VTKM_CONT virtual void CloseFile(); + + VTKM_CONT virtual void Read() = 0; private: - void OpenFile() - { - this->DataFile->Stream.exceptions(std::ifstream::failbit | std::ifstream::badbit); - try - { - this->DataFile->Stream.open(this->DataFile->FileName.c_str(), - std::ios_base::in | std::ios_base::binary); - } - catch (std::ifstream::failure&) - { - std::string message("could not open file \"" + this->DataFile->FileName + "\""); - throw vtkm::io::ErrorIO(message); - } - } - - void ReadHeader() - { - char vstring[] = "# vtk DataFile Version"; - const std::size_t vlen = sizeof(vstring); - - // Read version line - char vbuf[vlen]; - this->DataFile->Stream.read(vbuf, vlen - 1); - vbuf[vlen - 1] = '\0'; - if (std::string(vbuf) != std::string(vstring)) - { - throw vtkm::io::ErrorIO("Incorrect file format."); - } - - char dot; - this->DataFile->Stream >> this->DataFile->Version[0] >> dot >> this->DataFile->Version[1]; - // skip rest of the line - std::string skip; - std::getline(this->DataFile->Stream, skip); - - if ((this->DataFile->Version[0] > 4) || - (this->DataFile->Version[0] == 4 && this->DataFile->Version[1] > 2)) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Reader may not correctly read >v4.2 files. Reading version " - << this->DataFile->Version[0] - << "." - << this->DataFile->Version[1] - << ".\n"); - } - - // Read title line - std::getline(this->DataFile->Stream, this->DataFile->Title); - - // Read format line - this->DataFile->IsBinary = false; - std::string format; - this->DataFile->Stream >> format >> std::ws; - if (format == "BINARY") - { - this->DataFile->IsBinary = true; - } - else if (format != "ASCII") - { - throw vtkm::io::ErrorIO("Unsupported Format."); - } - - // Read structure line - std::string tag, structStr; - this->DataFile->Stream >> tag >> structStr >> std::ws; - internal::parseAssert(tag == "DATASET"); - - this->DataFile->Structure = vtkm::io::internal::DataSetStructureId(structStr); - if (this->DataFile->Structure == vtkm::io::internal::DATASET_UNKNOWN) - { - throw vtkm::io::ErrorIO("Unsupported DataSet type."); - } - } - - virtual void Read() = 0; - - void AddField(const std::string& name, - vtkm::cont::Field::Association association, - vtkm::cont::VariantArrayHandle& data) - { - if (data.GetNumberOfValues() > 0) - { - switch (association) - { - case vtkm::cont::Field::Association::POINTS: - case vtkm::cont::Field::Association::WHOLE_MESH: - this->DataSet.AddField(vtkm::cont::Field(name, association, data)); - break; - case vtkm::cont::Field::Association::CELL_SET: - this->DataSet.AddField(vtkm::cont::Field(name, association, data)); - break; - default: - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Not recording field '" << name << "' because it has an unknown association"); - break; - } - } - } - - void ReadScalars(vtkm::cont::Field::Association association, std::size_t numElements) - { - std::string dataName, dataType, lookupTableName; - vtkm::IdComponent numComponents = 1; - this->DataFile->Stream >> dataName >> dataType; - std::string tag; - this->DataFile->Stream >> tag; - if (tag != "LOOKUP_TABLE") - { - try - { - numComponents = std::stoi(tag); - } - catch (std::invalid_argument&) - { - internal::parseAssert(false); - } - this->DataFile->Stream >> tag; - } - - internal::parseAssert(tag == "LOOKUP_TABLE"); - this->DataFile->Stream >> lookupTableName >> std::ws; - - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, numComponents); - this->AddField(dataName, association, data); - } - - void ReadColorScalars(vtkm::cont::Field::Association association, std::size_t numElements) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Support for COLOR_SCALARS is not implemented. Skipping."); - - std::string dataName; - vtkm::IdComponent numComponents; - this->DataFile->Stream >> dataName >> numComponents >> std::ws; - std::string dataType = this->DataFile->IsBinary ? "unsigned_char" : "float"; - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, numComponents); - this->AddField(dataName, association, data); - } - - void ReadLookupTable() - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Support for LOOKUP_TABLE is not implemented. Skipping."); - - std::string dataName; - std::size_t numEntries; - this->DataFile->Stream >> dataName >> numEntries >> std::ws; - this->SkipArray(numEntries, vtkm::Vec()); - } - - void ReadTextureCoordinates(vtkm::cont::Field::Association association, std::size_t numElements) - { - std::string dataName; - vtkm::IdComponent numComponents; - std::string dataType; - this->DataFile->Stream >> dataName >> numComponents >> dataType >> std::ws; - - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, numComponents); - this->AddField(dataName, association, data); - } - - void ReadVectors(vtkm::cont::Field::Association association, std::size_t numElements) - { - std::string dataName; - std::string dataType; - this->DataFile->Stream >> dataName >> dataType >> std::ws; - - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, 3); - this->AddField(dataName, association, data); - } - - void ReadTensors(vtkm::cont::Field::Association association, std::size_t numElements) - { - std::string dataName; - std::string dataType; - this->DataFile->Stream >> dataName >> dataType >> std::ws; - - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numElements, 9); - this->AddField(dataName, association, data); - } - - void ReadFields(vtkm::cont::Field::Association association, std::size_t expectedNumElements) - { - std::string dataName; - vtkm::Id numArrays; - this->DataFile->Stream >> dataName >> numArrays >> std::ws; - for (vtkm::Id i = 0; i < numArrays; ++i) - { - std::size_t numTuples; - vtkm::IdComponent numComponents; - std::string arrayName, dataType; - this->DataFile->Stream >> arrayName >> numComponents >> numTuples >> dataType >> std::ws; - if (numTuples == expectedNumElements) - { - vtkm::cont::VariantArrayHandle data = - this->DoReadArrayVariant(association, dataType, numTuples, numComponents); - this->AddField(arrayName, association, data); - } - else - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Field " << arrayName - << "'s size does not match expected number of elements. Skipping"); - } - } - } + VTKM_CONT void OpenFile(); + VTKM_CONT void ReadHeader(); + VTKM_CONT void AddField(const std::string& name, + vtkm::cont::Field::Association association, + vtkm::cont::VariantArrayHandle& data); + VTKM_CONT void ReadScalars(vtkm::cont::Field::Association association, std::size_t numElements); + VTKM_CONT void ReadColorScalars(vtkm::cont::Field::Association association, + std::size_t numElements); + VTKM_CONT void ReadLookupTable(); + VTKM_CONT void ReadTextureCoordinates(vtkm::cont::Field::Association association, + std::size_t numElements); + VTKM_CONT void ReadVectors(vtkm::cont::Field::Association association, std::size_t numElements); + VTKM_CONT void ReadTensors(vtkm::cont::Field::Association association, std::size_t numElements); + VTKM_CONT void ReadFields(vtkm::cont::Field::Association association, + std::size_t expectedNumElements); protected: - void ReadGlobalFields(std::vector* visitBounds = nullptr) - { - std::string dataName; - vtkm::Id numArrays; - this->DataFile->Stream >> dataName >> numArrays >> std::ws; - for (vtkm::Id i = 0; i < numArrays; ++i) - { - std::size_t numTuples; - vtkm::IdComponent numComponents; - std::string arrayName, dataType; - this->DataFile->Stream >> arrayName >> numComponents >> numTuples >> dataType >> std::ws; - if (arrayName == "avtOriginalBounds" && visitBounds) - { - visitBounds->resize(6); - internal::parseAssert(numComponents == 1 && numTuples == 6); - // parse the bounds and fill the bounds vector - this->ReadArray(*visitBounds); - } - else - { - VTKM_LOG_S(vtkm::cont::LogLevel::Info, - "Support for global field " << arrayName << " not implemented. Skipping."); - this->DoSkipArrayVariant(dataType, numTuples, numComponents); - } - } - } + VTKM_CONT void ReadGlobalFields(std::vector* visitBounds = nullptr); private: - class SkipArrayVariant - { - public: - SkipArrayVariant(VTKDataSetReaderBase* reader, std::size_t numElements) - : Reader(reader) - , NumElements(numElements) - { - } - - template - void operator()(T) const - { - this->Reader->SkipArray(this->NumElements, T()); - } - - template - void operator()(vtkm::IdComponent numComponents, T) const - { - this->Reader->SkipArray(this->NumElements * static_cast(numComponents), T()); - } - - protected: - VTKDataSetReaderBase* Reader; - std::size_t NumElements; - }; - - class ReadArrayVariant : public SkipArrayVariant - { - public: - ReadArrayVariant(VTKDataSetReaderBase* reader, - vtkm::cont::Field::Association association, - std::size_t numElements, - vtkm::cont::VariantArrayHandle& data) - : SkipArrayVariant(reader, numElements) - , Association(association) - , Data(&data) - { - } - - template - void operator()(T) const - { - std::vector buffer(this->NumElements); - this->Reader->ReadArray(buffer); - if ((this->Association != vtkm::cont::Field::Association::CELL_SET) || - (this->Reader->GetCellsPermutation().GetNumberOfValues() < 1)) - { - *this->Data = internal::CreateVariantArrayHandle(buffer); - } - else - { - // If we are reading data associated with a cell set, we need to (sometimes) permute the - // data due to differences between VTK and VTK-m cell shapes. - auto permutation = this->Reader->GetCellsPermutation().ReadPortal(); - vtkm::Id outSize = permutation.GetNumberOfValues(); - std::vector permutedBuffer(static_cast(outSize)); - for (vtkm::Id outIndex = 0; outIndex < outSize; outIndex++) - { - std::size_t inIndex = static_cast(permutation.Get(outIndex)); - permutedBuffer[static_cast(outIndex)] = buffer[inIndex]; - } - *this->Data = internal::CreateVariantArrayHandle(permutedBuffer); - } - } - - template - void operator()(vtkm::IdComponent numComponents, T) const - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Support for " << numComponents << " components not implemented. Skipping."); - SkipArrayVariant::operator()(numComponents, T()); - } - - private: - vtkm::cont::Field::Association Association; - vtkm::cont::VariantArrayHandle* Data; - }; + class SkipArrayVariant; + class ReadArrayVariant; //Make the Array parsing methods protected so that derived classes //can call the methods. protected: - void DoSkipArrayVariant(std::string dataType, - std::size_t numElements, - vtkm::IdComponent numComponents) - { - // string is unsupported for SkipArrayVariant, so it requires some - // special handling - if (dataType == "string") - { - const vtkm::Id stringCount = numComponents * static_cast(numElements); - for (vtkm::Id i = 0; i < stringCount; ++i) - { - std::string trash; - this->DataFile->Stream >> trash; - } - } - else - { - vtkm::io::internal::DataType typeId = vtkm::io::internal::DataTypeId(dataType); - vtkm::io::internal::SelectTypeAndCall( - typeId, numComponents, SkipArrayVariant(this, numElements)); - } - } - - vtkm::cont::VariantArrayHandle DoReadArrayVariant(vtkm::cont::Field::Association association, - std::string dataType, - std::size_t numElements, - vtkm::IdComponent numComponents) - { - // Create empty data to start so that the return can check if data were actually read - vtkm::cont::ArrayHandle empty; - vtkm::cont::VariantArrayHandle data(empty); - - vtkm::io::internal::DataType typeId = vtkm::io::internal::DataTypeId(dataType); - vtkm::io::internal::SelectTypeAndCall( - typeId, numComponents, ReadArrayVariant(this, association, numElements, data)); - - return data; - } + VTKM_CONT void DoSkipArrayVariant(std::string dataType, + std::size_t numElements, + vtkm::IdComponent numComponents); + VTKM_CONT vtkm::cont::VariantArrayHandle DoReadArrayVariant( + vtkm::cont::Field::Association association, + std::string dataType, + std::size_t numElements, + vtkm::IdComponent numComponents); template - void ReadArray(std::vector& buffer) + VTKM_CONT void ReadArray(std::vector& buffer) { using ComponentType = typename vtkm::VecTraits::ComponentType; constexpr vtkm::IdComponent numComponents = vtkm::VecTraits::NUM_COMPONENTS; @@ -861,7 +220,8 @@ protected: } template - void ReadArray(std::vector>& buffer) + VTKM_CONT void ReadArray( + std::vector>& buffer) { VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "Support for data type 'bit' is not implemented. Skipping."); @@ -869,13 +229,7 @@ protected: buffer.clear(); } - void ReadArray(std::vector& buffer) - { - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, - "Support for data type 'bit' is not implemented. Skipping."); - this->SkipArray(buffer.size(), vtkm::io::internal::DummyBitType()); - buffer.clear(); - } + VTKM_CONT void ReadArray(std::vector& buffer); template void SkipArray(std::size_t numElements, T) @@ -912,99 +266,13 @@ protected: NumComponents); } - void SkipArray(std::size_t numElements, - vtkm::io::internal::DummyBitType, - vtkm::IdComponent numComponents = 1) - { - if (this->DataFile->IsBinary) - { - numElements = (numElements + 7) / 8; - this->DataFile->Stream.seekg(static_cast(numElements), std::ios_base::cur); - } - else - { - for (std::size_t i = 0; i < numElements; ++i) - { - vtkm::UInt16 val; - this->DataFile->Stream >> val; - } - } - this->DataFile->Stream >> std::ws; - this->SkipArrayMetaData(numComponents); - } + VTKM_CONT void SkipArray(std::size_t numElements, + vtkm::io::internal::DummyBitType, + vtkm::IdComponent numComponents = 1); - void SkipArrayMetaData(vtkm::IdComponent numComponents) - { - if (!this->DataFile->Stream.good()) - { - return; - } - - auto begining = this->DataFile->Stream.tellg(); - - std::string tag; - this->DataFile->Stream >> tag; - if (tag != "METADATA") - { - this->DataFile->Stream.seekg(begining); - return; - } - - VTKM_LOG_S(vtkm::cont::LogLevel::Warn, "METADATA is not supported. Attempting to Skip."); - - this->DataFile->Stream >> tag >> std::ws; - if (tag == "COMPONENT_NAMES") - { - std::string name; - for (vtkm::IdComponent i = 0; i < numComponents; ++i) - { - this->DataFile->Stream >> name >> std::ws; - } - } - else if (tag == "INFORMATION") - { - int numKeys = 0; - this->DataFile->Stream >> numKeys >> std::ws; - - // Skipping INFORMATION is tricky. The reader needs to be aware of the types of the - // information, which is not provided in the file. - // Here we will just skip until an empty line is found. - // However, if there are no keys, then there is nothing to read (and the stream tends - // to skip over empty lines. - if (numKeys > 0) - { - std::string line; - do - { - std::getline(this->DataFile->Stream, line); - } while (this->DataFile->Stream.good() && !line.empty()); - - // Eat any remaining whitespace after the INFORMATION to be ready to read the next token - this->DataFile->Stream >> std::ws; - } - } - else - { - internal::parseAssert(false); - } - } - -protected: - std::unique_ptr DataFile; - vtkm::cont::DataSet DataSet; - -private: - bool Loaded; - vtkm::cont::ArrayHandle CellsPermutation; - - friend class VTKDataSetReader; + VTKM_CONT void SkipArrayMetaData(vtkm::IdComponent numComponents); }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // vtkm::io -VTKM_BASIC_TYPE_VECTOR(vtkm::io::internal::ColorChannel8) -VTKM_BASIC_TYPE_VECTOR(vtkm::io::internal::DummyBitType) - #endif // vtk_m_io_VTKDataSetReaderBase_h diff --git a/vtkm/io/VTKDataSetWriter.cxx b/vtkm/io/VTKDataSetWriter.cxx new file mode 100644 index 000000000..6f3eac1a3 --- /dev/null +++ b/vtkm/io/VTKDataSetWriter.cxx @@ -0,0 +1,407 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + +struct OutputPointsFunctor +{ +private: + std::ostream& out; + + template + VTKM_CONT void Output(const PortalType& portal) const + { + for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); index++) + { + const int VTKDims = 3; // VTK files always require 3 dims for points + + using ValueType = typename PortalType::ValueType; + using VecType = typename vtkm::VecTraits; + + const ValueType& value = portal.Get(index); + + vtkm::IdComponent numComponents = VecType::GetNumberOfComponents(value); + for (vtkm::IdComponent c = 0; c < numComponents && c < VTKDims; c++) + { + out << (c == 0 ? "" : " ") << VecType::GetComponent(value, c); + } + for (vtkm::IdComponent c = numComponents; c < VTKDims; c++) + { + out << " 0"; + } + out << '\n'; + } + } + +public: + VTKM_CONT + OutputPointsFunctor(std::ostream& o) + : out(o) + { + } + + template + VTKM_CONT void operator()(const vtkm::cont::ArrayHandle& array) const + { + this->Output(array.ReadPortal()); + } +}; + +struct OutputFieldFunctor +{ +private: + std::ostream& out; + + template + VTKM_CONT void Output(const PortalType& portal) const + { + for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); index++) + { + using ValueType = typename PortalType::ValueType; + using VecType = typename vtkm::VecTraits; + + const ValueType& value = portal.Get(index); + + vtkm::IdComponent numComponents = VecType::GetNumberOfComponents(value); + for (vtkm::IdComponent c = 0; c < numComponents; c++) + { + out << (c == 0 ? "" : " ") << VecType::GetComponent(value, c); + } + out << '\n'; + } + } + +public: + VTKM_CONT + OutputFieldFunctor(std::ostream& o) + : out(o) + { + } + + template + VTKM_CONT void operator()(const vtkm::cont::ArrayHandle& array) const + { + this->Output(array.ReadPortal()); + } +}; + +class GetDataTypeName +{ +public: + GetDataTypeName(std::string& name) + : Name(&name) + { + } + + template + void operator()(const ArrayHandleType&) const + { + using DataType = typename vtkm::VecTraits::ComponentType; + *this->Name = vtkm::io::internal::DataTypeName::Name(); + } + +private: + std::string* Name; +}; + +void WritePoints(std::ostream& out, const vtkm::cont::DataSet& dataSet) +{ + ///\todo: support other coordinate systems + int cindex = 0; + auto cdata = dataSet.GetCoordinateSystem(cindex).GetData(); + + vtkm::Id npoints = cdata.GetNumberOfValues(); + out << "POINTS " << npoints << " " << vtkm::io::internal::DataTypeName::Name() + << " " << '\n'; + + OutputPointsFunctor{ out }(cdata); +} + +template +void WriteExplicitCells(std::ostream& out, const CellSetType& cellSet) +{ + vtkm::Id nCells = cellSet.GetNumberOfCells(); + + vtkm::Id conn_length = 0; + for (vtkm::Id i = 0; i < nCells; ++i) + { + conn_length += 1 + cellSet.GetNumberOfPointsInCell(i); + } + + out << "CELLS " << nCells << " " << conn_length << '\n'; + + for (vtkm::Id i = 0; i < nCells; ++i) + { + vtkm::cont::ArrayHandle ids; + vtkm::Id nids = cellSet.GetNumberOfPointsInCell(i); + cellSet.GetIndices(i, ids); + out << nids; + auto IdPortal = ids.ReadPortal(); + for (int j = 0; j < nids; ++j) + out << " " << IdPortal.Get(j); + out << '\n'; + } + + out << "CELL_TYPES " << nCells << '\n'; + for (vtkm::Id i = 0; i < nCells; ++i) + { + vtkm::Id shape = cellSet.GetCellShape(i); + out << shape << '\n'; + } +} + +void WriteVertexCells(std::ostream& out, const vtkm::cont::DataSet& dataSet) +{ + vtkm::Id nCells = dataSet.GetCoordinateSystem(0).GetNumberOfPoints(); + + out << "CELLS " << nCells << " " << nCells * 2 << '\n'; + for (int i = 0; i < nCells; i++) + { + out << "1 " << i << '\n'; + } + out << "CELL_TYPES " << nCells << '\n'; + for (int i = 0; i < nCells; i++) + { + out << vtkm::CELL_SHAPE_VERTEX << '\n'; + } +} + +void WritePointFields(std::ostream& out, const vtkm::cont::DataSet& dataSet) +{ + bool wrote_header = false; + for (vtkm::Id f = 0; f < dataSet.GetNumberOfFields(); f++) + { + const vtkm::cont::Field field = dataSet.GetField(f); + + if (field.GetAssociation() != vtkm::cont::Field::Association::POINTS) + { + continue; + } + + vtkm::Id npoints = field.GetNumberOfValues(); + int ncomps = field.GetData().GetNumberOfComponents(); + if (ncomps > 4) + { + continue; + } + + if (!wrote_header) + { + out << "POINT_DATA " << npoints << '\n'; + wrote_header = true; + } + + std::string typeName; + vtkm::cont::CastAndCall(field.GetData().ResetTypes(vtkm::TypeListAll{}), + GetDataTypeName(typeName)); + std::string name = field.GetName(); + for (auto& c : name) + { + if (std::isspace(c)) + { + c = '_'; + } + } + out << "SCALARS " << name << " " << typeName << " " << ncomps << '\n'; + out << "LOOKUP_TABLE default" << '\n'; + + vtkm::cont::CastAndCall(field.GetData().ResetTypes(vtkm::TypeListAll{}), + OutputFieldFunctor(out)); + } +} + +void WriteCellFields(std::ostream& out, const vtkm::cont::DataSet& dataSet) +{ + bool wrote_header = false; + for (vtkm::Id f = 0; f < dataSet.GetNumberOfFields(); f++) + { + const vtkm::cont::Field field = dataSet.GetField(f); + if (!field.IsFieldCell()) + { + continue; + } + + + vtkm::Id ncells = field.GetNumberOfValues(); + int ncomps = field.GetData().GetNumberOfComponents(); + if (ncomps > 4) + continue; + + if (!wrote_header) + { + out << "CELL_DATA " << ncells << '\n'; + wrote_header = true; + } + + std::string typeName; + vtkm::cont::CastAndCall(field.GetData().ResetTypes(vtkm::TypeListAll{}), + GetDataTypeName(typeName)); + + std::string name = field.GetName(); + for (auto& c : name) + { + if (std::isspace(c)) + { + c = '_'; + } + } + + out << "SCALARS " << name << " " << typeName << " " << ncomps << '\n'; + out << "LOOKUP_TABLE default" << '\n'; + + vtkm::cont::CastAndCall(field.GetData().ResetTypes(vtkm::TypeListAll{}), + OutputFieldFunctor(out)); + } +} + +void WriteDataSetAsPoints(std::ostream& out, const vtkm::cont::DataSet& dataSet) +{ + out << "DATASET UNSTRUCTURED_GRID" << '\n'; + WritePoints(out, dataSet); + WriteVertexCells(out, dataSet); +} + +template +void WriteDataSetAsUnstructured(std::ostream& out, + const vtkm::cont::DataSet& dataSet, + const CellSetType& cellSet) +{ + out << "DATASET UNSTRUCTURED_GRID" << '\n'; + WritePoints(out, dataSet); + WriteExplicitCells(out, cellSet); +} + +template +void WriteDataSetAsStructured(std::ostream& out, + const vtkm::cont::DataSet& dataSet, + const vtkm::cont::CellSetStructured& cellSet) +{ + ///\todo: support uniform/rectilinear + out << "DATASET STRUCTURED_GRID" << '\n'; + + auto pointDimensions = cellSet.GetPointDimensions(); + using VTraits = vtkm::VecTraits; + + out << "DIMENSIONS "; + out << VTraits::GetComponent(pointDimensions, 0) << " "; + out << (DIM > 1 ? VTraits::GetComponent(pointDimensions, 1) : 1) << " "; + out << (DIM > 2 ? VTraits::GetComponent(pointDimensions, 2) : 1) << " "; + + WritePoints(out, dataSet); +} + +void Write(std::ostream& out, const vtkm::cont::DataSet& dataSet, bool just_points = false) +{ + // The Paraview parser cannot handle scientific notation: + out << std::fixed; + out << "# vtk DataFile Version 3.0" << '\n'; + out << "vtk output" << '\n'; + out << "ASCII" << '\n'; + + if (just_points) + { + WriteDataSetAsPoints(out, dataSet); + WritePointFields(out, dataSet); + } + else + { + vtkm::cont::DynamicCellSet cellSet = dataSet.GetCellSet(); + if (cellSet.IsType>()) + { + WriteDataSetAsUnstructured(out, dataSet, cellSet.Cast>()); + } + else if (cellSet.IsType>()) + { + WriteDataSetAsStructured(out, dataSet, cellSet.Cast>()); + } + else if (cellSet.IsType>()) + { + WriteDataSetAsStructured(out, dataSet, cellSet.Cast>()); + } + else if (cellSet.IsType>()) + { + WriteDataSetAsStructured(out, dataSet, cellSet.Cast>()); + } + else if (cellSet.IsType>()) + { + // these function just like explicit cell sets + WriteDataSetAsUnstructured(out, dataSet, cellSet.Cast>()); + } + else + { + throw vtkm::cont::ErrorBadType("Could not determine type to write out."); + } + + WritePointFields(out, dataSet); + WriteCellFields(out, dataSet); + } +} + +} // anonymous namespace + +namespace vtkm +{ +namespace io +{ + +VTKDataSetWriter::VTKDataSetWriter(const char* fileName) + : FileName(fileName) +{ +} + +VTKDataSetWriter::VTKDataSetWriter(const std::string& fileName) + : FileName(fileName) +{ +} + +void VTKDataSetWriter::WriteDataSet(const vtkm::cont::DataSet& dataSet, bool just_points) const +{ + if (dataSet.GetNumberOfCoordinateSystems() < 1) + { + throw vtkm::cont::ErrorBadValue( + "DataSet has no coordinate system, which is not supported by VTK file format."); + } + try + { + std::ofstream fileStream(this->FileName.c_str(), std::fstream::trunc); + Write(fileStream, dataSet, just_points); + fileStream.close(); + } + catch (std::ofstream::failure& error) + { + throw vtkm::io::ErrorIO(error.what()); + } +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKDataSetWriter.h b/vtkm/io/VTKDataSetWriter.h index 946ac5c3d..e666f0b7d 100644 --- a/vtkm/io/VTKDataSetWriter.h +++ b/vtkm/io/VTKDataSetWriter.h @@ -10,400 +10,22 @@ #ifndef vtk_m_io_DataSetWriter_h #define vtk_m_io_DataSetWriter_h -#include -#include - -#include -#include -#include #include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include +#include namespace vtkm { namespace io { -namespace detail -{ -struct OutputPointsFunctor -{ -private: - std::ostream& out; - - template - VTKM_CONT void Output(const PortalType& portal) const - { - for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); index++) - { - const int VTKDims = 3; // VTK files always require 3 dims for points - - using ValueType = typename PortalType::ValueType; - using VecType = typename vtkm::VecTraits; - - const ValueType& value = portal.Get(index); - - vtkm::IdComponent numComponents = VecType::GetNumberOfComponents(value); - for (vtkm::IdComponent c = 0; c < numComponents && c < VTKDims; c++) - { - out << (c == 0 ? "" : " ") << VecType::GetComponent(value, c); - } - for (vtkm::IdComponent c = numComponents; c < VTKDims; c++) - { - out << " 0"; - } - out << '\n'; - } - } - -public: - VTKM_CONT - OutputPointsFunctor(std::ostream& o) - : out(o) - { - } - - template - VTKM_CONT void operator()(const vtkm::cont::ArrayHandle& array) const - { - this->Output(array.ReadPortal()); - } -}; - -struct OutputFieldFunctor -{ -private: - std::ostream& out; - - template - VTKM_CONT void Output(const PortalType& portal) const - { - for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); index++) - { - using ValueType = typename PortalType::ValueType; - using VecType = typename vtkm::VecTraits; - - const ValueType& value = portal.Get(index); - - vtkm::IdComponent numComponents = VecType::GetNumberOfComponents(value); - for (vtkm::IdComponent c = 0; c < numComponents; c++) - { - out << (c == 0 ? "" : " ") << VecType::GetComponent(value, c); - } - out << '\n'; - } - } - -public: - VTKM_CONT - OutputFieldFunctor(std::ostream& o) - : out(o) - { - } - - template - VTKM_CONT void operator()(const vtkm::cont::ArrayHandle& array) const - { - this->Output(array.ReadPortal()); - } -}; - -class GetDataTypeName +struct VTKM_IO_EXPORT VTKDataSetWriter { public: - GetDataTypeName(std::string& name) - : Name(&name) - { - } + VTKM_CONT VTKDataSetWriter(const char* fileName); + VTKM_CONT VTKDataSetWriter(const std::string& fileName); - template - void operator()(const ArrayHandleType&) const - { - using DataType = typename vtkm::VecTraits::ComponentType; - *this->Name = vtkm::io::internal::DataTypeName::Name(); - } - -private: - std::string* Name; -}; - -} // namespace detail - -struct VTKDataSetWriter -{ -private: - static void WritePoints(std::ostream& out, const vtkm::cont::DataSet& dataSet) - { - ///\todo: support other coordinate systems - int cindex = 0; - auto cdata = dataSet.GetCoordinateSystem(cindex).GetData(); - - vtkm::Id npoints = cdata.GetNumberOfValues(); - out << "POINTS " << npoints << " " - << vtkm::io::internal::DataTypeName::Name() << " " << '\n'; - - detail::OutputPointsFunctor{ out }(cdata); - } - - template - static void WriteExplicitCells(std::ostream& out, const CellSetType& cellSet) - { - vtkm::Id nCells = cellSet.GetNumberOfCells(); - - vtkm::Id conn_length = 0; - for (vtkm::Id i = 0; i < nCells; ++i) - { - conn_length += 1 + cellSet.GetNumberOfPointsInCell(i); - } - - out << "CELLS " << nCells << " " << conn_length << '\n'; - - for (vtkm::Id i = 0; i < nCells; ++i) - { - vtkm::cont::ArrayHandle ids; - vtkm::Id nids = cellSet.GetNumberOfPointsInCell(i); - cellSet.GetIndices(i, ids); - out << nids; - auto IdPortal = ids.ReadPortal(); - for (int j = 0; j < nids; ++j) - out << " " << IdPortal.Get(j); - out << '\n'; - } - - out << "CELL_TYPES " << nCells << '\n'; - for (vtkm::Id i = 0; i < nCells; ++i) - { - vtkm::Id shape = cellSet.GetCellShape(i); - out << shape << '\n'; - } - } - - static void WriteVertexCells(std::ostream& out, const vtkm::cont::DataSet& dataSet) - { - vtkm::Id nCells = dataSet.GetCoordinateSystem(0).GetNumberOfPoints(); - - out << "CELLS " << nCells << " " << nCells * 2 << '\n'; - for (int i = 0; i < nCells; i++) - { - out << "1 " << i << '\n'; - } - out << "CELL_TYPES " << nCells << '\n'; - for (int i = 0; i < nCells; i++) - { - out << vtkm::CELL_SHAPE_VERTEX << '\n'; - } - } - - static void WritePointFields(std::ostream& out, const vtkm::cont::DataSet& dataSet) - { - bool wrote_header = false; - for (vtkm::Id f = 0; f < dataSet.GetNumberOfFields(); f++) - { - const vtkm::cont::Field field = dataSet.GetField(f); - - if (field.GetAssociation() != vtkm::cont::Field::Association::POINTS) - { - continue; - } - - vtkm::Id npoints = field.GetNumberOfValues(); - int ncomps = field.GetData().GetNumberOfComponents(); - if (ncomps > 4) - { - continue; - } - - if (!wrote_header) - { - out << "POINT_DATA " << npoints << '\n'; - wrote_header = true; - } - - std::string typeName; - vtkm::cont::CastAndCall(field.GetData().ResetTypes(TypeListAll{}), - detail::GetDataTypeName(typeName)); - std::string name = field.GetName(); - for (auto& c : name) - { - if (std::isspace(c)) - { - c = '_'; - } - } - out << "SCALARS " << name << " " << typeName << " " << ncomps << '\n'; - out << "LOOKUP_TABLE default" << '\n'; - - vtkm::cont::CastAndCall(field.GetData().ResetTypes(TypeListAll{}), - detail::OutputFieldFunctor(out)); - } - } - - static void WriteCellFields(std::ostream& out, const vtkm::cont::DataSet& dataSet) - { - bool wrote_header = false; - for (vtkm::Id f = 0; f < dataSet.GetNumberOfFields(); f++) - { - const vtkm::cont::Field field = dataSet.GetField(f); - if (!field.IsFieldCell()) - { - continue; - } - - - vtkm::Id ncells = field.GetNumberOfValues(); - int ncomps = field.GetData().GetNumberOfComponents(); - if (ncomps > 4) - continue; - - if (!wrote_header) - { - out << "CELL_DATA " << ncells << '\n'; - wrote_header = true; - } - - std::string typeName; - vtkm::cont::CastAndCall(field.GetData().ResetTypes(TypeListAll{}), - detail::GetDataTypeName(typeName)); - - std::string name = field.GetName(); - for (auto& c : name) - { - if (std::isspace(c)) - { - c = '_'; - } - } - - out << "SCALARS " << name << " " << typeName << " " << ncomps << '\n'; - out << "LOOKUP_TABLE default" << '\n'; - - vtkm::cont::CastAndCall(field.GetData().ResetTypes(TypeListAll{}), - detail::OutputFieldFunctor(out)); - } - } - - static void WriteDataSetAsPoints(std::ostream& out, const vtkm::cont::DataSet& dataSet) - { - out << "DATASET UNSTRUCTURED_GRID" << '\n'; - WritePoints(out, dataSet); - WriteVertexCells(out, dataSet); - } - - template - static void WriteDataSetAsUnstructured(std::ostream& out, - const vtkm::cont::DataSet& dataSet, - const CellSetType& cellSet) - { - out << "DATASET UNSTRUCTURED_GRID" << '\n'; - WritePoints(out, dataSet); - WriteExplicitCells(out, cellSet); - } - - template - static void WriteDataSetAsStructured(std::ostream& out, - const vtkm::cont::DataSet& dataSet, - const vtkm::cont::CellSetStructured& cellSet) - { - ///\todo: support uniform/rectilinear - out << "DATASET STRUCTURED_GRID" << '\n'; - - auto pointDimensions = cellSet.GetPointDimensions(); - using VTraits = vtkm::VecTraits; - - out << "DIMENSIONS "; - out << VTraits::GetComponent(pointDimensions, 0) << " "; - out << (DIM > 1 ? VTraits::GetComponent(pointDimensions, 1) : 1) << " "; - out << (DIM > 2 ? VTraits::GetComponent(pointDimensions, 2) : 1) << " "; - - WritePoints(out, dataSet); - } - - static void Write(std::ostream& out, const vtkm::cont::DataSet& dataSet, bool just_points = false) - { - // The Paraview parser cannot handle scientific notation: - out << std::fixed; - out << "# vtk DataFile Version 3.0" << '\n'; - out << "vtk output" << '\n'; - out << "ASCII" << '\n'; - - if (just_points) - { - WriteDataSetAsPoints(out, dataSet); - WritePointFields(out, dataSet); - } - else - { - vtkm::cont::DynamicCellSet cellSet = dataSet.GetCellSet(); - if (cellSet.IsType>()) - { - WriteDataSetAsUnstructured(out, dataSet, cellSet.Cast>()); - } - else if (cellSet.IsType>()) - { - WriteDataSetAsStructured(out, dataSet, cellSet.Cast>()); - } - else if (cellSet.IsType>()) - { - WriteDataSetAsStructured(out, dataSet, cellSet.Cast>()); - } - else if (cellSet.IsType>()) - { - WriteDataSetAsStructured(out, dataSet, cellSet.Cast>()); - } - else if (cellSet.IsType>()) - { - // these function just like explicit cell sets - WriteDataSetAsUnstructured(out, dataSet, cellSet.Cast>()); - } - else - { - throw vtkm::cont::ErrorBadType("Could not determine type to write out."); - } - - WritePointFields(out, dataSet); - WriteCellFields(out, dataSet); - } - } - -public: - VTKM_CONT - explicit VTKDataSetWriter(const std::string& filename) - : FileName(filename) - { - } - - VTKM_CONT - void WriteDataSet(const vtkm::cont::DataSet& dataSet, bool just_points = false) const - { - if (dataSet.GetNumberOfCoordinateSystems() < 1) - { - throw vtkm::cont::ErrorBadValue( - "DataSet has no coordinate system, which is not supported by VTK file format."); - } - try - { - std::ofstream fileStream(this->FileName.c_str(), std::fstream::trunc); - this->Write(fileStream, dataSet, just_points); - fileStream.close(); - } - catch (std::ofstream::failure& error) - { - throw vtkm::io::ErrorIO(error.what()); - } - } + VTKM_CONT void WriteDataSet(const vtkm::cont::DataSet& dataSet, bool just_points = false) const; private: std::string FileName; diff --git a/vtkm/io/VTKPolyDataReader.cxx b/vtkm/io/VTKPolyDataReader.cxx new file mode 100644 index 000000000..e134d6160 --- /dev/null +++ b/vtkm/io/VTKPolyDataReader.cxx @@ -0,0 +1,152 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +namespace +{ + +template +inline vtkm::cont::ArrayHandle ConcatinateArrayHandles( + const std::vector>& arrays) +{ + vtkm::Id size = 0; + for (std::size_t i = 0; i < arrays.size(); ++i) + { + size += arrays[i].GetNumberOfValues(); + } + + vtkm::cont::ArrayHandle out; + out.Allocate(size); + + auto outp = vtkm::cont::ArrayPortalToIteratorBegin(out.WritePortal()); + for (std::size_t i = 0; i < arrays.size(); ++i) + { + std::copy(vtkm::cont::ArrayPortalToIteratorBegin(arrays[i].ReadPortal()), + vtkm::cont::ArrayPortalToIteratorEnd(arrays[i].ReadPortal()), + outp); + using DifferenceType = typename std::iterator_traits::difference_type; + std::advance(outp, static_cast(arrays[i].GetNumberOfValues())); + } + + return out; +} +} + +namespace vtkm +{ +namespace io +{ + +VTKPolyDataReader::VTKPolyDataReader(const char* fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKPolyDataReader::VTKPolyDataReader(const std::string& fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +void VTKPolyDataReader::Read() +{ + if (this->DataFile->Structure != vtkm::io::internal::DATASET_POLYDATA) + { + throw vtkm::io::ErrorIO("Incorrect DataSet type"); + } + + //We need to be able to handle VisIt files which dump Field data + //at the top of a VTK file + std::string tag; + this->DataFile->Stream >> tag; + if (tag == "FIELD") + { + this->ReadGlobalFields(); + this->DataFile->Stream >> tag; + } + + // Read the points + internal::parseAssert(tag == "POINTS"); + this->ReadPoints(); + + vtkm::Id numPoints = this->DataSet.GetNumberOfPoints(); + + // Read the cellset + std::vector> connectivityArrays; + std::vector> numIndicesArrays; + std::vector shapesBuffer; + while (!this->DataFile->Stream.eof()) + { + vtkm::UInt8 shape = vtkm::CELL_SHAPE_EMPTY; + this->DataFile->Stream >> tag; + if (tag == "VERTICES") + { + shape = vtkm::io::internal::CELL_SHAPE_POLY_VERTEX; + } + else if (tag == "LINES") + { + shape = vtkm::io::internal::CELL_SHAPE_POLY_LINE; + } + else if (tag == "POLYGONS") + { + shape = vtkm::CELL_SHAPE_POLYGON; + } + else if (tag == "TRIANGLE_STRIPS") + { + shape = vtkm::io::internal::CELL_SHAPE_TRIANGLE_STRIP; + } + else + { + this->DataFile->Stream.seekg(-static_cast(tag.length()), std::ios_base::cur); + break; + } + + vtkm::cont::ArrayHandle cellConnectivity; + vtkm::cont::ArrayHandle cellNumIndices; + this->ReadCells(cellConnectivity, cellNumIndices); + + connectivityArrays.push_back(cellConnectivity); + numIndicesArrays.push_back(cellNumIndices); + shapesBuffer.insert( + shapesBuffer.end(), static_cast(cellNumIndices.GetNumberOfValues()), shape); + } + + vtkm::cont::ArrayHandle connectivity = ConcatinateArrayHandles(connectivityArrays); + vtkm::cont::ArrayHandle numIndices = ConcatinateArrayHandles(numIndicesArrays); + vtkm::cont::ArrayHandle shapes; + shapes.Allocate(static_cast(shapesBuffer.size())); + std::copy(shapesBuffer.begin(), + shapesBuffer.end(), + vtkm::cont::ArrayPortalToIteratorBegin(shapes.WritePortal())); + + vtkm::cont::ArrayHandle permutation; + vtkm::io::internal::FixupCellSet(connectivity, numIndices, shapes, permutation); + this->SetCellsPermutation(permutation); + + if (vtkm::io::internal::IsSingleShape(shapes)) + { + vtkm::cont::CellSetSingleType<> cellSet; + cellSet.Fill( + numPoints, shapes.ReadPortal().Get(0), numIndices.ReadPortal().Get(0), connectivity); + this->DataSet.SetCellSet(cellSet); + } + else + { + auto offsets = vtkm::cont::ConvertNumIndicesToOffsets(numIndices); + vtkm::cont::CellSetExplicit<> cellSet; + cellSet.Fill(numPoints, shapes, connectivity, offsets); + this->DataSet.SetCellSet(cellSet); + } + + // Read points and cell attributes + this->ReadAttributes(); +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKPolyDataReader.h b/vtkm/io/VTKPolyDataReader.h index 3629f62ca..449c1747f 100644 --- a/vtkm/io/VTKPolyDataReader.h +++ b/vtkm/io/VTKPolyDataReader.h @@ -11,6 +11,7 @@ #define vtk_m_io_VTKPolyDataReader_h #include +#include #include @@ -21,147 +22,15 @@ namespace vtkm namespace io { -namespace internal -{ - -template -inline vtkm::cont::ArrayHandle ConcatinateArrayHandles( - const std::vector>& arrays) -{ - vtkm::Id size = 0; - for (std::size_t i = 0; i < arrays.size(); ++i) - { - size += arrays[i].GetNumberOfValues(); - } - - vtkm::cont::ArrayHandle out; - out.Allocate(size); - - auto outp = vtkm::cont::ArrayPortalToIteratorBegin(out.WritePortal()); - for (std::size_t i = 0; i < arrays.size(); ++i) - { - std::copy(vtkm::cont::ArrayPortalToIteratorBegin(arrays[i].ReadPortal()), - vtkm::cont::ArrayPortalToIteratorEnd(arrays[i].ReadPortal()), - outp); - using DifferenceType = typename std::iterator_traits::difference_type; - std::advance(outp, static_cast(arrays[i].GetNumberOfValues())); - } - - return out; -} - -} // namespace internal - -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKPolyDataReader : public VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKPolyDataReader : public VTKDataSetReaderBase { public: - explicit VTKPolyDataReader(const char* fileName) - : VTKDataSetReaderBase(fileName) - { - } + explicit VTKM_CONT VTKPolyDataReader(const char* fileName); + explicit VTKM_CONT VTKPolyDataReader(const std::string& fileName); private: - virtual void Read() - { - if (this->DataFile->Structure != vtkm::io::internal::DATASET_POLYDATA) - { - throw vtkm::io::ErrorIO("Incorrect DataSet type"); - } - - //We need to be able to handle VisIt files which dump Field data - //at the top of a VTK file - std::string tag; - this->DataFile->Stream >> tag; - if (tag == "FIELD") - { - this->ReadGlobalFields(); - this->DataFile->Stream >> tag; - } - - // Read the points - internal::parseAssert(tag == "POINTS"); - this->ReadPoints(); - - vtkm::Id numPoints = this->DataSet.GetNumberOfPoints(); - - // Read the cellset - std::vector> connectivityArrays; - std::vector> numIndicesArrays; - std::vector shapesBuffer; - while (!this->DataFile->Stream.eof()) - { - vtkm::UInt8 shape = vtkm::CELL_SHAPE_EMPTY; - this->DataFile->Stream >> tag; - if (tag == "VERTICES") - { - shape = vtkm::io::internal::CELL_SHAPE_POLY_VERTEX; - } - else if (tag == "LINES") - { - shape = vtkm::io::internal::CELL_SHAPE_POLY_LINE; - } - else if (tag == "POLYGONS") - { - shape = vtkm::CELL_SHAPE_POLYGON; - } - else if (tag == "TRIANGLE_STRIPS") - { - shape = vtkm::io::internal::CELL_SHAPE_TRIANGLE_STRIP; - } - else - { - this->DataFile->Stream.seekg(-static_cast(tag.length()), - std::ios_base::cur); - break; - } - - vtkm::cont::ArrayHandle cellConnectivity; - vtkm::cont::ArrayHandle cellNumIndices; - this->ReadCells(cellConnectivity, cellNumIndices); - - connectivityArrays.push_back(cellConnectivity); - numIndicesArrays.push_back(cellNumIndices); - shapesBuffer.insert( - shapesBuffer.end(), static_cast(cellNumIndices.GetNumberOfValues()), shape); - } - - vtkm::cont::ArrayHandle connectivity = - internal::ConcatinateArrayHandles(connectivityArrays); - vtkm::cont::ArrayHandle numIndices = - internal::ConcatinateArrayHandles(numIndicesArrays); - vtkm::cont::ArrayHandle shapes; - shapes.Allocate(static_cast(shapesBuffer.size())); - std::copy(shapesBuffer.begin(), - shapesBuffer.end(), - vtkm::cont::ArrayPortalToIteratorBegin(shapes.WritePortal())); - - vtkm::cont::ArrayHandle permutation; - vtkm::io::internal::FixupCellSet(connectivity, numIndices, shapes, permutation); - this->SetCellsPermutation(permutation); - - if (vtkm::io::internal::IsSingleShape(shapes)) - { - vtkm::cont::CellSetSingleType<> cellSet; - cellSet.Fill( - numPoints, shapes.ReadPortal().Get(0), numIndices.ReadPortal().Get(0), connectivity); - this->DataSet.SetCellSet(cellSet); - } - else - { - auto offsets = vtkm::cont::ConvertNumIndicesToOffsets(numIndices); - vtkm::cont::CellSetExplicit<> cellSet; - cellSet.Fill(numPoints, shapes, connectivity, offsets); - this->DataSet.SetCellSet(cellSet); - } - - // Read points and cell attributes - this->ReadAttributes(); - } + void Read() override; }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // namespace vtkm::io diff --git a/vtkm/io/VTKRectilinearGridReader.cxx b/vtkm/io/VTKRectilinearGridReader.cxx new file mode 100644 index 000000000..9dcf3a5ca --- /dev/null +++ b/vtkm/io/VTKRectilinearGridReader.cxx @@ -0,0 +1,95 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +namespace vtkm +{ +namespace io +{ + +VTKRectilinearGridReader::VTKRectilinearGridReader(const char* fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKRectilinearGridReader::VTKRectilinearGridReader(const std::string& fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +void VTKRectilinearGridReader::Read() +{ + if (this->DataFile->Structure != vtkm::io::internal::DATASET_RECTILINEAR_GRID) + throw vtkm::io::ErrorIO("Incorrect DataSet type"); + + //We need to be able to handle VisIt files which dump Field data + //at the top of a VTK file + std::string tag; + this->DataFile->Stream >> tag; + if (tag == "FIELD") + { + this->ReadGlobalFields(); + this->DataFile->Stream >> tag; + } + + // Read structured grid specific meta-data + internal::parseAssert(tag == "DIMENSIONS"); + vtkm::Id3 dim; + this->DataFile->Stream >> dim[0] >> dim[1] >> dim[2] >> std::ws; + + //Read the points. + std::string dataType; + std::size_t numPoints[3]; + vtkm::cont::VariantArrayHandle X, Y, Z; + + // Always read coordinates as vtkm::FloatDefault + std::string readDataType = vtkm::io::internal::DataTypeName::Name(); + + this->DataFile->Stream >> tag >> numPoints[0] >> dataType >> std::ws; + if (tag != "X_COORDINATES") + throw vtkm::io::ErrorIO("X_COORDINATES tag not found"); + X = this->DoReadArrayVariant(vtkm::cont::Field::Association::ANY, readDataType, numPoints[0], 1); + + this->DataFile->Stream >> tag >> numPoints[1] >> dataType >> std::ws; + if (tag != "Y_COORDINATES") + throw vtkm::io::ErrorIO("Y_COORDINATES tag not found"); + Y = this->DoReadArrayVariant(vtkm::cont::Field::Association::ANY, readDataType, numPoints[1], 1); + + this->DataFile->Stream >> tag >> numPoints[2] >> dataType >> std::ws; + if (tag != "Z_COORDINATES") + throw vtkm::io::ErrorIO("Z_COORDINATES tag not found"); + Z = this->DoReadArrayVariant(vtkm::cont::Field::Association::ANY, readDataType, numPoints[2], 1); + + if (dim != vtkm::Id3(static_cast(numPoints[0]), + static_cast(numPoints[1]), + static_cast(numPoints[2]))) + throw vtkm::io::ErrorIO("DIMENSIONS not equal to number of points"); + + vtkm::cont::ArrayHandleCartesianProduct, + vtkm::cont::ArrayHandle, + vtkm::cont::ArrayHandle> + coords; + + vtkm::cont::ArrayHandle Xc, Yc, Zc; + X.CopyTo(Xc); + Y.CopyTo(Yc); + Z.CopyTo(Zc); + coords = vtkm::cont::make_ArrayHandleCartesianProduct(Xc, Yc, Zc); + vtkm::cont::CoordinateSystem coordSys("coordinates", coords); + this->DataSet.AddCoordinateSystem(coordSys); + + this->DataSet.SetCellSet(internal::CreateCellSetStructured(dim)); + + // Read points and cell attributes + this->ReadAttributes(); +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKRectilinearGridReader.h b/vtkm/io/VTKRectilinearGridReader.h index 92a5c6e05..37c681c28 100644 --- a/vtkm/io/VTKRectilinearGridReader.h +++ b/vtkm/io/VTKRectilinearGridReader.h @@ -17,89 +17,15 @@ namespace vtkm namespace io { -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKRectilinearGridReader : public VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKRectilinearGridReader : public VTKDataSetReaderBase { public: - explicit VTKRectilinearGridReader(const char* fileName) - : VTKDataSetReaderBase(fileName) - { - } + explicit VTKM_CONT VTKRectilinearGridReader(const char* fileName); + explicit VTKM_CONT VTKRectilinearGridReader(const std::string& fileName); private: - virtual void Read() - { - if (this->DataFile->Structure != vtkm::io::internal::DATASET_RECTILINEAR_GRID) - throw vtkm::io::ErrorIO("Incorrect DataSet type"); - - //We need to be able to handle VisIt files which dump Field data - //at the top of a VTK file - std::string tag; - this->DataFile->Stream >> tag; - if (tag == "FIELD") - { - this->ReadGlobalFields(); - this->DataFile->Stream >> tag; - } - - // Read structured grid specific meta-data - internal::parseAssert(tag == "DIMENSIONS"); - vtkm::Id3 dim; - this->DataFile->Stream >> dim[0] >> dim[1] >> dim[2] >> std::ws; - - //Read the points. - std::string dataType; - std::size_t numPoints[3]; - vtkm::cont::VariantArrayHandle X, Y, Z; - - // Always read coordinates as vtkm::FloatDefault - std::string readDataType = vtkm::io::internal::DataTypeName::Name(); - - this->DataFile->Stream >> tag >> numPoints[0] >> dataType >> std::ws; - if (tag != "X_COORDINATES") - throw vtkm::io::ErrorIO("X_COORDINATES tag not found"); - X = - this->DoReadArrayVariant(vtkm::cont::Field::Association::ANY, readDataType, numPoints[0], 1); - - this->DataFile->Stream >> tag >> numPoints[1] >> dataType >> std::ws; - if (tag != "Y_COORDINATES") - throw vtkm::io::ErrorIO("Y_COORDINATES tag not found"); - Y = - this->DoReadArrayVariant(vtkm::cont::Field::Association::ANY, readDataType, numPoints[1], 1); - - this->DataFile->Stream >> tag >> numPoints[2] >> dataType >> std::ws; - if (tag != "Z_COORDINATES") - throw vtkm::io::ErrorIO("Z_COORDINATES tag not found"); - Z = - this->DoReadArrayVariant(vtkm::cont::Field::Association::ANY, readDataType, numPoints[2], 1); - - if (dim != vtkm::Id3(static_cast(numPoints[0]), - static_cast(numPoints[1]), - static_cast(numPoints[2]))) - throw vtkm::io::ErrorIO("DIMENSIONS not equal to number of points"); - - vtkm::cont::ArrayHandleCartesianProduct, - vtkm::cont::ArrayHandle, - vtkm::cont::ArrayHandle> - coords; - - vtkm::cont::ArrayHandle Xc, Yc, Zc; - X.CopyTo(Xc); - Y.CopyTo(Yc); - Z.CopyTo(Zc); - coords = vtkm::cont::make_ArrayHandleCartesianProduct(Xc, Yc, Zc); - vtkm::cont::CoordinateSystem coordSys("coordinates", coords); - this->DataSet.AddCoordinateSystem(coordSys); - - this->DataSet.SetCellSet(internal::CreateCellSetStructured(dim)); - - // Read points and cell attributes - this->ReadAttributes(); - } + VTKM_CONT void Read() override; }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // namespace vtkm::io diff --git a/vtkm/io/VTKStructuredGridReader.cxx b/vtkm/io/VTKStructuredGridReader.cxx new file mode 100644 index 000000000..76b7d2101 --- /dev/null +++ b/vtkm/io/VTKStructuredGridReader.cxx @@ -0,0 +1,62 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +namespace vtkm +{ +namespace io +{ + +VTKStructuredGridReader::VTKStructuredGridReader(const char* fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKStructuredGridReader::VTKStructuredGridReader(const std::string& fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +void VTKStructuredGridReader::Read() +{ + if (this->DataFile->Structure != vtkm::io::internal::DATASET_STRUCTURED_GRID) + { + throw vtkm::io::ErrorIO("Incorrect DataSet type"); + } + + std::string tag; + + //We need to be able to handle VisIt files which dump Field data + //at the top of a VTK file + this->DataFile->Stream >> tag; + if (tag == "FIELD") + { + this->ReadGlobalFields(); + this->DataFile->Stream >> tag; + } + + // Read structured grid specific meta-data + internal::parseAssert(tag == "DIMENSIONS"); + vtkm::Id3 dim; + this->DataFile->Stream >> dim[0] >> dim[1] >> dim[2] >> std::ws; + + this->DataSet.SetCellSet(internal::CreateCellSetStructured(dim)); + + // Read the points + this->DataFile->Stream >> tag; + internal::parseAssert(tag == "POINTS"); + this->ReadPoints(); + + // Read points and cell attributes + this->ReadAttributes(); +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKStructuredGridReader.h b/vtkm/io/VTKStructuredGridReader.h index b37e98301..745cef2b7 100644 --- a/vtkm/io/VTKStructuredGridReader.h +++ b/vtkm/io/VTKStructuredGridReader.h @@ -17,53 +17,15 @@ namespace vtkm namespace io { -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKStructuredGridReader : public VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKStructuredGridReader : public VTKDataSetReaderBase { public: - explicit VTKStructuredGridReader(const char* fileName) - : VTKDataSetReaderBase(fileName) - { - } + explicit VTKM_CONT VTKStructuredGridReader(const char* fileName); + explicit VTKM_CONT VTKStructuredGridReader(const std::string& fileName); private: - virtual void Read() - { - if (this->DataFile->Structure != vtkm::io::internal::DATASET_STRUCTURED_GRID) - { - throw vtkm::io::ErrorIO("Incorrect DataSet type"); - } - - std::string tag; - - //We need to be able to handle VisIt files which dump Field data - //at the top of a VTK file - this->DataFile->Stream >> tag; - if (tag == "FIELD") - { - this->ReadGlobalFields(); - this->DataFile->Stream >> tag; - } - - // Read structured grid specific meta-data - internal::parseAssert(tag == "DIMENSIONS"); - vtkm::Id3 dim; - this->DataFile->Stream >> dim[0] >> dim[1] >> dim[2] >> std::ws; - - this->DataSet.SetCellSet(internal::CreateCellSetStructured(dim)); - - // Read the points - this->DataFile->Stream >> tag; - internal::parseAssert(tag == "POINTS"); - this->ReadPoints(); - - // Read points and cell attributes - this->ReadAttributes(); - } + VTKM_CONT void Read() override; }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // namespace vtkm::io diff --git a/vtkm/io/VTKStructuredPointsReader.cxx b/vtkm/io/VTKStructuredPointsReader.cxx new file mode 100644 index 000000000..1293da374 --- /dev/null +++ b/vtkm/io/VTKStructuredPointsReader.cxx @@ -0,0 +1,79 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +namespace vtkm +{ +namespace io +{ + +VTKStructuredPointsReader::VTKStructuredPointsReader(const char* fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKStructuredPointsReader::VTKStructuredPointsReader(const std::string& fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +void VTKStructuredPointsReader::Read() +{ + if (this->DataFile->Structure != vtkm::io::internal::DATASET_STRUCTURED_POINTS) + { + throw vtkm::io::ErrorIO("Incorrect DataSet type"); + } + + std::string tag; + + // Read structured points specific meta-data + vtkm::Id3 dim; + vtkm::Vec3f_32 origin, spacing; + + //Two ways the file can describe the dimensions. The proper way is by + //using the DIMENSIONS keyword, but VisIt written VTK files spicify data + //bounds instead, as a FIELD + std::vector visitBounds; + this->DataFile->Stream >> tag; + if (tag == "FIELD") + { + this->ReadGlobalFields(&visitBounds); + this->DataFile->Stream >> tag; + } + if (visitBounds.empty()) + { + internal::parseAssert(tag == "DIMENSIONS"); + this->DataFile->Stream >> dim[0] >> dim[1] >> dim[2] >> std::ws; + this->DataFile->Stream >> tag; + } + + internal::parseAssert(tag == "SPACING"); + this->DataFile->Stream >> spacing[0] >> spacing[1] >> spacing[2] >> std::ws; + if (!visitBounds.empty()) + { + //now with spacing and physical bounds we can back compute the dimensions + dim[0] = static_cast((visitBounds[1] - visitBounds[0]) / spacing[0]); + dim[1] = static_cast((visitBounds[3] - visitBounds[2]) / spacing[1]); + dim[2] = static_cast((visitBounds[5] - visitBounds[4]) / spacing[2]); + } + + this->DataFile->Stream >> tag >> origin[0] >> origin[1] >> origin[2] >> std::ws; + internal::parseAssert(tag == "ORIGIN"); + + this->DataSet.SetCellSet(internal::CreateCellSetStructured(dim)); + this->DataSet.AddCoordinateSystem( + vtkm::cont::CoordinateSystem("coordinates", dim, origin, spacing)); + + // Read points and cell attributes + this->ReadAttributes(); +} +} +} // namespace vtkm::io diff --git a/vtkm/io/VTKStructuredPointsReader.h b/vtkm/io/VTKStructuredPointsReader.h index 17d786380..2e998f96f 100644 --- a/vtkm/io/VTKStructuredPointsReader.h +++ b/vtkm/io/VTKStructuredPointsReader.h @@ -17,70 +17,15 @@ namespace vtkm namespace io { -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKStructuredPointsReader : public VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKStructuredPointsReader : public VTKDataSetReaderBase { public: - explicit VTKStructuredPointsReader(const char* fileName) - : VTKDataSetReaderBase(fileName) - { - } + explicit VTKM_CONT VTKStructuredPointsReader(const char* fileName); + explicit VTKM_CONT VTKStructuredPointsReader(const std::string& fileName); private: - virtual void Read() - { - if (this->DataFile->Structure != vtkm::io::internal::DATASET_STRUCTURED_POINTS) - { - throw vtkm::io::ErrorIO("Incorrect DataSet type"); - } - - std::string tag; - - // Read structured points specific meta-data - vtkm::Id3 dim; - vtkm::Vec3f_32 origin, spacing; - - //Two ways the file can describe the dimensions. The proper way is by - //using the DIMENSIONS keyword, but VisIt written VTK files spicify data - //bounds instead, as a FIELD - std::vector visitBounds; - this->DataFile->Stream >> tag; - if (tag == "FIELD") - { - this->ReadGlobalFields(&visitBounds); - this->DataFile->Stream >> tag; - } - if (visitBounds.empty()) - { - internal::parseAssert(tag == "DIMENSIONS"); - this->DataFile->Stream >> dim[0] >> dim[1] >> dim[2] >> std::ws; - this->DataFile->Stream >> tag; - } - - internal::parseAssert(tag == "SPACING"); - this->DataFile->Stream >> spacing[0] >> spacing[1] >> spacing[2] >> std::ws; - if (!visitBounds.empty()) - { - //now with spacing and physical bounds we can back compute the dimensions - dim[0] = static_cast((visitBounds[1] - visitBounds[0]) / spacing[0]); - dim[1] = static_cast((visitBounds[3] - visitBounds[2]) / spacing[1]); - dim[2] = static_cast((visitBounds[5] - visitBounds[4]) / spacing[2]); - } - - this->DataFile->Stream >> tag >> origin[0] >> origin[1] >> origin[2] >> std::ws; - internal::parseAssert(tag == "ORIGIN"); - - this->DataSet.SetCellSet(internal::CreateCellSetStructured(dim)); - this->DataSet.AddCoordinateSystem( - vtkm::cont::CoordinateSystem("coordinates", dim, origin, spacing)); - - // Read points and cell attributes - this->ReadAttributes(); - } + VTKM_CONT void Read() override; }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // namespace vtkm::io diff --git a/vtkm/io/VTKUnstructuredGridReader.cxx b/vtkm/io/VTKUnstructuredGridReader.cxx new file mode 100644 index 000000000..0fb69cacb --- /dev/null +++ b/vtkm/io/VTKUnstructuredGridReader.cxx @@ -0,0 +1,88 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include + +#include + +namespace vtkm +{ +namespace io +{ + +VTKUnstructuredGridReader::VTKUnstructuredGridReader(const char* fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +VTKUnstructuredGridReader::VTKUnstructuredGridReader(const std::string& fileName) + : VTKDataSetReaderBase(fileName) +{ +} + +void VTKUnstructuredGridReader::Read() +{ + if (this->DataFile->Structure != vtkm::io::internal::DATASET_UNSTRUCTURED_GRID) + { + throw vtkm::io::ErrorIO("Incorrect DataSet type"); + } + + //We need to be able to handle VisIt files which dump Field data + //at the top of a VTK file + std::string tag; + this->DataFile->Stream >> tag; + if (tag == "FIELD") + { + this->ReadGlobalFields(); + this->DataFile->Stream >> tag; + } + + // Read the points + internal::parseAssert(tag == "POINTS"); + this->ReadPoints(); + + vtkm::Id numPoints = this->DataSet.GetNumberOfPoints(); + + // Read the cellset + vtkm::cont::ArrayHandle connectivity; + vtkm::cont::ArrayHandle numIndices; + vtkm::cont::ArrayHandle shapes; + + this->DataFile->Stream >> tag; + internal::parseAssert(tag == "CELLS"); + + this->ReadCells(connectivity, numIndices); + this->ReadShapes(shapes); + + vtkm::cont::ArrayHandle permutation; + vtkm::io::internal::FixupCellSet(connectivity, numIndices, shapes, permutation); + this->SetCellsPermutation(permutation); + + //DRP + if (false) //vtkm::io::internal::IsSingleShape(shapes)) + { + vtkm::cont::CellSetSingleType<> cellSet; + cellSet.Fill( + numPoints, shapes.ReadPortal().Get(0), numIndices.ReadPortal().Get(0), connectivity); + this->DataSet.SetCellSet(cellSet); + } + else + { + auto offsets = vtkm::cont::ConvertNumIndicesToOffsets(numIndices); + vtkm::cont::CellSetExplicit<> cellSet; + cellSet.Fill(numPoints, shapes, connectivity, offsets); + this->DataSet.SetCellSet(cellSet); + } + + // Read points and cell attributes + this->ReadAttributes(); +} +} +} diff --git a/vtkm/io/VTKUnstructuredGridReader.h b/vtkm/io/VTKUnstructuredGridReader.h index bee4e06f8..4d60e1d39 100644 --- a/vtkm/io/VTKUnstructuredGridReader.h +++ b/vtkm/io/VTKUnstructuredGridReader.h @@ -17,77 +17,15 @@ namespace vtkm namespace io { -VTKM_SILENCE_WEAK_VTABLE_WARNING_START - -class VTKUnstructuredGridReader : public VTKDataSetReaderBase +class VTKM_IO_EXPORT VTKUnstructuredGridReader : public VTKDataSetReaderBase { public: - explicit VTKUnstructuredGridReader(const char* fileName) - : VTKDataSetReaderBase(fileName) - { - } + explicit VTKM_CONT VTKUnstructuredGridReader(const char* fileName); + explicit VTKM_CONT VTKUnstructuredGridReader(const std::string& fileName); private: - virtual void Read() - { - if (this->DataFile->Structure != vtkm::io::internal::DATASET_UNSTRUCTURED_GRID) - { - throw vtkm::io::ErrorIO("Incorrect DataSet type"); - } - - //We need to be able to handle VisIt files which dump Field data - //at the top of a VTK file - std::string tag; - this->DataFile->Stream >> tag; - if (tag == "FIELD") - { - this->ReadGlobalFields(); - this->DataFile->Stream >> tag; - } - - // Read the points - internal::parseAssert(tag == "POINTS"); - this->ReadPoints(); - - vtkm::Id numPoints = this->DataSet.GetNumberOfPoints(); - - // Read the cellset - vtkm::cont::ArrayHandle connectivity; - vtkm::cont::ArrayHandle numIndices; - vtkm::cont::ArrayHandle shapes; - - this->DataFile->Stream >> tag; - internal::parseAssert(tag == "CELLS"); - - this->ReadCells(connectivity, numIndices); - this->ReadShapes(shapes); - - vtkm::cont::ArrayHandle permutation; - vtkm::io::internal::FixupCellSet(connectivity, numIndices, shapes, permutation); - this->SetCellsPermutation(permutation); - - //DRP - if (false) //vtkm::io::internal::IsSingleShape(shapes)) - { - vtkm::cont::CellSetSingleType<> cellSet; - cellSet.Fill( - numPoints, shapes.ReadPortal().Get(0), numIndices.ReadPortal().Get(0), connectivity); - this->DataSet.SetCellSet(cellSet); - } - else - { - auto offsets = vtkm::cont::ConvertNumIndicesToOffsets(numIndices); - vtkm::cont::CellSetExplicit<> cellSet; - cellSet.Fill(numPoints, shapes, connectivity, offsets); - this->DataSet.SetCellSet(cellSet); - } - - // Read points and cell attributes - this->ReadAttributes(); - } + VTKM_CONT void Read() override; }; - -VTKM_SILENCE_WEAK_VTABLE_WARNING_END } } // namespace vtkm::io diff --git a/vtkm/io/internal/VTKDataSetTypes.h b/vtkm/io/internal/VTKDataSetTypes.h index 8210ff197..30e9ece20 100644 --- a/vtkm/io/internal/VTKDataSetTypes.h +++ b/vtkm/io/internal/VTKDataSetTypes.h @@ -11,6 +11,7 @@ #define vtk_m_io_internal_VTKDataSetTypes_h #include +#include #include #include @@ -238,4 +239,7 @@ inline void SelectTypeAndCall(DataType dtype, } } // namespace vtkm::io::internal +VTKM_BASIC_TYPE_VECTOR(vtkm::io::internal::ColorChannel8) +VTKM_BASIC_TYPE_VECTOR(vtkm::io::internal::DummyBitType) + #endif // vtk_m_io_internal_VTKDataSetTypes_h diff --git a/vtkm/io/reader/CMakeLists.txt b/vtkm/io/reader/CMakeLists.txt index 25047e830..2128ba0a7 100644 --- a/vtkm/io/reader/CMakeLists.txt +++ b/vtkm/io/reader/CMakeLists.txt @@ -13,4 +13,6 @@ set(headers VTKDataSetReader.h ) -vtkm_declare_headers(${headers}) +vtkm_declare_headers( + ${headers} +) diff --git a/vtkm/io/testing/CMakeLists.txt b/vtkm/io/testing/CMakeLists.txt index 39c832efe..33ee8762e 100644 --- a/vtkm/io/testing/CMakeLists.txt +++ b/vtkm/io/testing/CMakeLists.txt @@ -9,8 +9,20 @@ ##============================================================================ set(unit_tests + UnitTestBOVDataSetReader.cxx + UnitTestPixelTypes.cxx UnitTestVTKDataSetReader.cxx UnitTestVTKDataSetWriter.cxx - ) +) -vtkm_unit_tests(SOURCES ${unit_tests} ALL_BACKENDS) +set(unit_test_libraries vtkm_lodepng vtkm_io) + +if(VTKm_ENABLE_RENDERING) + set(unit_tests ${unit_tests} + UnitTestImageWriter.cxx + ) + + set(unit_test_libraries ${unit_test_libraries} vtkm_rendering) +endif() + +vtkm_unit_tests(SOURCES ${unit_tests} ALL_BACKENDS LIBRARIES ${unit_test_libraries}) diff --git a/vtkm/io/testing/UnitTestBOVDataSetReader.cxx b/vtkm/io/testing/UnitTestBOVDataSetReader.cxx new file mode 100644 index 000000000..f1af8c023 --- /dev/null +++ b/vtkm/io/testing/UnitTestBOVDataSetReader.cxx @@ -0,0 +1,70 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include +#include +#include +#include + +namespace +{ + +inline vtkm::cont::DataSet readBOVDataSet(const char* fname) +{ + vtkm::cont::DataSet ds; + vtkm::io::BOVDataSetReader reader(fname); + try + { + ds = reader.ReadDataSet(); + } + catch (vtkm::io::ErrorIO& e) + { + std::string message("Error reading "); + message += fname; + message += ", "; + message += e.GetMessage(); + + VTKM_TEST_FAIL(message.c_str()); + } + + return ds; +} + +} // anonymous namespace + +void TestReadingBOVDataSet() +{ + std::string bovFile = vtkm::cont::testing::Testing::GetTestDataBasePath() + "/uniform/noise.bov"; + + auto const& ds = readBOVDataSet(bovFile.data()); + + VTKM_TEST_ASSERT(ds.GetNumberOfFields() == 1, "Incorrect number of fields"); + // See the .bov file: DATA SIZE: 50 50 50 + VTKM_TEST_ASSERT(ds.GetNumberOfPoints() == 50 * 50 * 50, "Incorrect number of points"); + VTKM_TEST_ASSERT(ds.GetCellSet().GetNumberOfPoints() == 50 * 50 * 50, + "Incorrect number of points (from cell set)"); + VTKM_TEST_ASSERT(ds.GetNumberOfCells() == 49 * 49 * 49, "Incorrect number of cells"); + // See the .bov file: VARIABLE: "var" + VTKM_TEST_ASSERT(ds.HasField("var"), "Should have field 'var', but does not."); + VTKM_TEST_ASSERT(ds.GetNumberOfFields() == 1, "There is only one field in noise.bov"); + VTKM_TEST_ASSERT(ds.GetNumberOfCoordinateSystems() == 1, + "There is only one coordinate system in noise.bov"); + + auto const& field = ds.GetField("var"); + // I'm pretty sure that all .bov files have their fields associated with points . . . + VTKM_TEST_ASSERT(field.GetAssociation() == vtkm::cont::Field::Association::POINTS, + "The field should be associated with points."); +} + + +int UnitTestBOVDataSetReader(int argc, char* argv[]) +{ + return vtkm::cont::testing::Testing::Run(TestReadingBOVDataSet, argc, argv); +} diff --git a/vtkm/io/testing/UnitTestImageWriter.cxx b/vtkm/io/testing/UnitTestImageWriter.cxx new file mode 100644 index 000000000..8be7521aa --- /dev/null +++ b/vtkm/io/testing/UnitTestImageWriter.cxx @@ -0,0 +1,165 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ + +using namespace vtkm::io; +using namespace vtkm::rendering; + +template +void TestFilledImage(vtkm::cont::DataSet& dataSet, + const std::string& fieldName, + const vtkm::rendering::Canvas& canvas) +{ + VTKM_TEST_ASSERT(dataSet.HasPointField(fieldName), "Point Field Not Found: " + fieldName); + + auto pointField = dataSet.GetPointField(fieldName); + VTKM_TEST_ASSERT(pointField.GetNumberOfValues() == canvas.GetWidth() * canvas.GetHeight(), + "wrong image dimensions"); + VTKM_TEST_ASSERT(pointField.GetData().template IsType>(), + "wrong ArrayHandle type"); + auto pixelPortal = + pointField.GetData().template Cast>().ReadPortal(); + + auto colorPortal = canvas.GetColorBuffer().ReadPortal(); + for (vtkm::Id y = 0; y < canvas.GetHeight(); y++) + { + std::ostringstream row; + row << "["; + for (vtkm::Id x = 0; x < canvas.GetWidth(); x++) + { + auto tuple = colorPortal.Get(y * canvas.GetWidth() + x); + auto pixelVec = PixelType(pixelPortal.Get(y * canvas.GetWidth() + x)); + std::ostringstream data; + data << pixelVec << ":" << PixelType(tuple) << std::endl; + VTKM_TEST_ASSERT(pixelVec == PixelType(tuple), + "Stored pixel did not match canvas value" + data.str()); + row << pixelVec << ","; + } + row << "]"; + } +} + +template +void TestCreateImageDataSet(BaseImageReader& reader, const vtkm::rendering::Canvas& canvas) +{ + auto dataSet = reader.CreateImageDataSet(canvas); + TestFilledImage(dataSet, reader.GetPointFieldName(), canvas); +} + +template +void TestReadAndWritePNG(const vtkm::rendering::Canvas& canvas, std::string image) +{ + auto pngReader = PNGReader(); + auto pngWriter = PNGWriter(); + vtkm::cont::DataSet dataSet; + bool throws = false; + try + { + pngWriter.WriteToFile(image, dataSet); + } + catch (const vtkm::cont::ErrorBadValue&) + { + throws = true; + } + VTKM_TEST_ASSERT(throws, "Fill Image did not throw with empty data"); + + dataSet = pngReader.CreateImageDataSet(canvas); + pngWriter.WriteToFile(image, dataSet); + dataSet = pngReader.ReadFromFile(image); + pngWriter.WriteToFile(image, dataSet); + dataSet = pngReader.ReadFromFile(image); + TestFilledImage(dataSet, pngReader.GetPointFieldName(), canvas); +} + +template +void TestReadAndWritePNM(const vtkm::rendering::Canvas& canvas) +{ + using PixelType = RGBPixel; + PNMWriter ppmWriter((1 << BitDepth) - 1); + PNMReader ppmReader((1 << BitDepth) - 1); + vtkm::cont::DataSet dataSet; + bool throws = false; + try + { + ppmWriter.WriteToFile("ppmTestFile" + std::to_string(BitDepth) + "bit.ppm", dataSet); + } + catch (const vtkm::cont::ErrorBadValue&) + { + throws = true; + } + VTKM_TEST_ASSERT(throws, "Fill Image did not throw with empty data"); + + dataSet = ppmReader.CreateImageDataSet(canvas); + ppmWriter.WriteToFile("ppmTestFile.ppm", dataSet); + dataSet = ppmReader.ReadFromFile("ppmTestFile.ppm"); + ppmWriter.WriteToFile("ppmTestFile2.ppm", dataSet); + dataSet = ppmReader.ReadFromFile("ppmTestFile2.ppm"); + TestFilledImage(dataSet, ppmReader.GetPointFieldName(), canvas); +} + + +void TestBaseImageMethods(const vtkm::rendering::Canvas& canvas) +{ + auto reader = PNGReader(); + TestCreateImageDataSet(reader, canvas); + TestCreateImageDataSet(reader, canvas); + TestCreateImageDataSet(reader, canvas); + TestCreateImageDataSet(reader, canvas); +} + +void TestPNMImage(const vtkm::rendering::Canvas& canvas) +{ + TestReadAndWritePNM<8>(canvas); + TestReadAndWritePNM<16>(canvas); +} + +void TestPNGImage(const vtkm::rendering::Canvas& canvas) +{ + TestReadAndWritePNG(canvas, "pngRGB8Test.png"); + TestReadAndWritePNG(canvas, "pngRGB16Test.png"); + TestReadAndWritePNG(canvas, "pngGrey8Test.png"); + TestReadAndWritePNG(canvas, "pngGrey16Test.png"); +} + +void TestImage() +{ + vtkm::rendering::Canvas canvas(16, 16); + canvas.SetBackgroundColor(vtkm::rendering::Color::red); + canvas.Initialize(); + canvas.Activate(); + canvas.Clear(); + // Line from top left to bottom right, ensures correct transposedness + canvas.AddLine(-0.9, 0.9, 0.9, -0.9, 2.0f, vtkm::rendering::Color::black); + vtkm::Bounds colorBarBounds(-0.8, -0.6, -0.8, 0.8, 0, 0); + canvas.AddColorBar(colorBarBounds, vtkm::cont::ColorTable("inferno"), false); + canvas.BlendBackground(); + canvas.SaveAs("baseline.ppm"); + + TestBaseImageMethods(canvas); + TestPNMImage(canvas); + TestPNGImage(canvas); +} +} + +int UnitTestImageWriter(int argc, char* argv[]) +{ + return vtkm::cont::testing::Testing::Run(TestImage, argc, argv); +} diff --git a/vtkm/io/testing/UnitTestPixelTypes.cxx b/vtkm/io/testing/UnitTestPixelTypes.cxx new file mode 100644 index 000000000..f734cb65f --- /dev/null +++ b/vtkm/io/testing/UnitTestPixelTypes.cxx @@ -0,0 +1,176 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include +#include + +#include + +using namespace vtkm::io; + +template +void TestPixelTypeOperations(const vtkm::UInt16& numPixels = 10) +{ + using ValType = typename PixelType::ComponentType; + const ValType numBytes = static_cast(PixelType::NUM_BYTES); + const ValType numChannels = static_cast(PixelType::NUM_CHANNELS); + + // Fill in the imageData through FillPixelData + std::vector imageData(numPixels * numBytes * numChannels); + std::vector pixelVector(numPixels); + for (ValType i = 0; i < numPixels; i++) + { + ValType pixelVal = 0; + for (ValType j = 0, shift = numBytes - 1; j < numBytes; shift--, j++) + { + pixelVal += (i + j) << (shift * 8); + } + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "pixelVal[" << i << "] = " << pixelVal); + + PixelType pixel(pixelVal); + pixelVector[i] = pixel; + pixel.FillImageAtIndexWithPixel(imageData.data(), i); + } + + // Test that the imageData values were set correctly + VTKM_TEST_ASSERT(static_cast(imageData.size()) == numPixels * numChannels * numBytes, + "Wrong number of elements"); + for (ValType j = 0; j < numBytes; j++) + { + for (ValType i = 0; i < numPixels; i++) + { + for (ValType k = numChannels * i; k < numChannels * i + numChannels; k++) + { + VTKM_TEST_ASSERT(imageData[k * numBytes + j] == i + j, + "Wrong value at index[" + std::to_string(k * numBytes + j) + "]: " + + std::to_string(imageData[k * numBytes + j]) + " != " + + std::to_string(i + j)); + } + } + } + + // Test that a pixel can be retreived from the filled out data vector + for (vtkm::Id i = 0; i < numPixels; i++) + { + VTKM_TEST_ASSERT(pixelVector[static_cast::size_type>(i)] == + PixelType(imageData.data(), i), + "Incorrect pixel value"); + } +} + +void TestDifferentPixelTypes() +{ + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Testing 8 bit RGB"); + TestPixelTypeOperations(); + + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Testing 8 bit Grey"); + TestPixelTypeOperations(); + + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Testing 16 bit RGB"); + TestPixelTypeOperations(); + + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Testing 16 bit Grey"); + TestPixelTypeOperations(); +} + +void TestGreyPixelConstructors() +{ + std::vector initData{ 1, 2 }; + + auto pixel_1 = GreyPixel_8(1); + auto pixel_2 = GreyPixel_8(1); + auto pixel_3 = GreyPixel_8(2); + auto pixel_4 = GreyPixel_8(initData.data(), 0); + auto pixel_5 = GreyPixel_8(initData.data(), 1); + auto pixel_6 = GreyPixel_16(initData.data(), 0); + + float color = 10.0f / GreyPixel_16::MAX_COLOR_VALUE; + auto pixel_7 = GreyPixel_16({ color, color, color, 5 }); + + VTKM_TEST_ASSERT(vtkm::UInt16(1) == pixel_1[0], "Type mis-match"); + VTKM_TEST_ASSERT(vtkm::FloatDefault(0) == pixel_1.Diff(pixel_2), "Incorrect Diff"); + VTKM_TEST_ASSERT(vtkm::FloatDefault(1) == pixel_1.Diff(pixel_3), "Incorrect Diff"); + VTKM_TEST_ASSERT(vtkm::Vec4f_32(1.0f / 255, 1.0f / 255, 1.0f / 255, 1) == pixel_1.ToVec4f(), + "Incorrect Conversion"); + VTKM_TEST_ASSERT(vtkm::Vec(1) == pixel_4, "Bad 1st value 8 bit construct"); + VTKM_TEST_ASSERT(vtkm::Vec(2) == pixel_5, "Bad 2nd value 8 bit construct"); + VTKM_TEST_ASSERT(vtkm::Vec(258) == pixel_6, "Bad 16 bit construct"); + VTKM_TEST_ASSERT(vtkm::Vec4f_32(258.0f / 65535, 258.0f / 65535, 258.0f / 65535, 1) == + pixel_6.ToVec4f(), + "Incorrect Conversion"); + VTKM_TEST_ASSERT(vtkm::Vec(10) == pixel_7, "Bad Vec4f_32 construction"); + + VTKM_TEST_ASSERT(GreyPixel<16>::BIT_DEPTH == 16, "Bad BitDepth"); + VTKM_TEST_ASSERT(GreyPixel<16>::NUM_BYTES == 2, "Bad NumBytes"); + VTKM_TEST_ASSERT(GreyPixel<16>::MAX_COLOR_VALUE == 65535, "Bad NumBytes"); + VTKM_TEST_ASSERT(GreyPixel<16>::NUM_CHANNELS == 1, "Bad NumChannels"); + VTKM_TEST_ASSERT(GreyPixel<16>::BYTES_PER_PIXEL == 2, "Wrong Pixel Byte distance"); + + // Shouldn't compile + // auto pixel_4 = RGBPixel_8(1, 1, 1); + // pixel_1.Diff(pixel_4); +} + +void TestRGBPixelConstructors() +{ + std::vector initData{ 1, 2, 3, 4, 5, 6 }; + + auto pixel_1 = RGBPixel_8(1, 1, 1); + auto pixel_2 = RGBPixel_8(1, 1, 1); + auto pixel_3 = RGBPixel_8(1); + auto pixel_4 = RGBPixel_8(2, 2, 2); + auto pixel_5 = RGBPixel_8(initData.data(), 0); + auto pixel_6 = RGBPixel_8(initData.data(), 1); + auto pixel_7 = RGBPixel_16(initData.data(), 0); + + float color = 10.0f / RGBPixel_16::MAX_COLOR_VALUE; + auto pixel_8 = RGBPixel_16({ color, color, color, 5 }); + + VTKM_TEST_ASSERT(vtkm::Vec3ui_8(1, 1, 1) == pixel_1, "Type mis-match"); + VTKM_TEST_ASSERT(vtkm::FloatDefault(0) == pixel_1.Diff(pixel_2), "Incorrect Diff"); + VTKM_TEST_ASSERT(vtkm::FloatDefault(0) == pixel_1.Diff(pixel_3), "Incorrect Diff"); + VTKM_TEST_ASSERT(vtkm::FloatDefault(3) == pixel_1.Diff(pixel_4), "Incorrect Diff"); + VTKM_TEST_ASSERT(vtkm::Vec4f_32(1.0f / 255, 1.0f / 255, 1.0f / 255, 1) == pixel_1.ToVec4f(), + "Incorrect Conversion"); + VTKM_TEST_ASSERT(vtkm::Vec3ui_8(1, 2, 3) == pixel_5, "Bad 1st value 8 bit construct"); + VTKM_TEST_ASSERT(vtkm::Vec3ui_8(4, 5, 6) == pixel_6, "Bad 2nd value 8 bit construct"); + VTKM_TEST_ASSERT(vtkm::Vec3ui_16(258, 772, 1286) == pixel_7, "Bad 16 bit construct"); + VTKM_TEST_ASSERT(vtkm::Vec4f_32(258.0f / 65535, 772.0f / 65535, 1286.0f / 65535, 1) == + pixel_7.ToVec4f(), + "Incorrect Conversion"); + VTKM_TEST_ASSERT(vtkm::Vec(10, 10, 10) == pixel_8, "Bad Vec4f_32 construction"); + + VTKM_TEST_ASSERT(RGBPixel<16>::BIT_DEPTH == 16, "Bad BitDepth"); + VTKM_TEST_ASSERT(RGBPixel<16>::NUM_BYTES == 2, "Bad NumBytes"); + VTKM_TEST_ASSERT(RGBPixel<16>::MAX_COLOR_VALUE == 65535, "Bad NumBytes"); + VTKM_TEST_ASSERT(RGBPixel<16>::NUM_CHANNELS == 3, "Bad NumChannels"); + VTKM_TEST_ASSERT(RGBPixel<16>::BYTES_PER_PIXEL == 6, "Wrong Pixel Byte distance"); + + // Shouldn't compile + // auto pixel_8 = GreyPixel_8(1); + // pixel_1.Diff(pixel_8); +} + +void TestPixelTypes() +{ + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Testing RGBPixel"); + TestRGBPixelConstructors(); + + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Testing GreyPixel"); + TestGreyPixelConstructors(); + + VTKM_LOG_S(vtkm::cont::LogLevel::Info, "Testing Pixel Types"); + TestDifferentPixelTypes(); +} + +int UnitTestPixelTypes(int argc, char* argv[]) +{ + return vtkm::cont::testing::Testing::Run(TestPixelTypes, argc, argv); +} diff --git a/vtkm/io/writer/CMakeLists.txt b/vtkm/io/writer/CMakeLists.txt index 1bebb0959..9d88e46f2 100644 --- a/vtkm/io/writer/CMakeLists.txt +++ b/vtkm/io/writer/CMakeLists.txt @@ -10,6 +10,8 @@ set(headers VTKDataSetWriter.h - ) +) -vtkm_declare_headers(${headers}) +vtkm_declare_headers( + ${headers} +) diff --git a/vtkm/worklet/WorkletMapTopology.h b/vtkm/worklet/WorkletMapTopology.h index 72f122975..e10c63e19 100644 --- a/vtkm/worklet/WorkletMapTopology.h +++ b/vtkm/worklet/WorkletMapTopology.h @@ -164,15 +164,17 @@ public: typename VisitArrayType, typename ThreadToOutArrayType, typename InputDomainType> - VTKM_EXEC vtkm::exec::arg::ThreadIndicesTopologyMap GetThreadIndices( - vtkm::Id threadIndex, - const OutToInArrayType& outToIn, - const VisitArrayType& visit, - const ThreadToOutArrayType& threadToOut, - const InputDomainType& connectivity) const + VTKM_EXEC vtkm::exec::arg::ThreadIndicesTopologyMap + GetThreadIndices(vtkm::Id threadIndex, + const OutToInArrayType& outToIn, + const VisitArrayType& visit, + const ThreadToOutArrayType& threadToOut, + const InputDomainType& connectivity) const { const vtkm::Id outIndex = threadToOut.Get(threadIndex); - return vtkm::exec::arg::ThreadIndicesTopologyMap( + return vtkm::exec::arg::ThreadIndicesTopologyMap( threadIndex, outToIn.Get(outIndex), visit.Get(outIndex), outIndex, connectivity); } @@ -199,7 +201,10 @@ public: typename InputDomainType, bool S = IsScatterIdentity, bool M = IsMaskNone> - VTKM_EXEC EnableFnWhen> + VTKM_EXEC EnableFnWhen< + S && M, + vtkm::exec::arg::ThreadIndicesTopologyMap> GetThreadIndices(vtkm::Id threadIndex1D, const vtkm::Id3& threadIndex3D, const OutToInArrayType& vtkmNotUsed(outToIn), @@ -207,7 +212,8 @@ public: const ThreadToOutArrayType& vtkmNotUsed(threadToOut), const InputDomainType& connectivity) const { - return vtkm::exec::arg::ThreadIndicesTopologyMap( + return vtkm::exec::arg::ThreadIndicesTopologyMap( threadIndex3D, threadIndex1D, connectivity); } @@ -219,21 +225,26 @@ public: typename InputDomainType, bool S = IsScatterIdentity, bool M = IsMaskNone> - VTKM_EXEC EnableFnWhen> - GetThreadIndices(vtkm::Id threadIndex1D, - const vtkm::Id3& threadIndex3D, - const OutToInArrayType& outToIn, - const VisitArrayType& visit, - const ThreadToOutArrayType& threadToOut, - const InputDomainType& connectivity) const + VTKM_EXEC + EnableFnWhen> + GetThreadIndices(vtkm::Id threadIndex1D, + const vtkm::Id3& threadIndex3D, + const OutToInArrayType& outToIn, + const VisitArrayType& visit, + const ThreadToOutArrayType& threadToOut, + const InputDomainType& connectivity) const { const vtkm::Id outIndex = threadToOut.Get(threadIndex1D); - return vtkm::exec::arg::ThreadIndicesTopologyMap(threadIndex3D, - threadIndex1D, - outToIn.Get(outIndex), - visit.Get(outIndex), - outIndex, - connectivity); + return vtkm::exec::arg::ThreadIndicesTopologyMap( + threadIndex3D, + threadIndex1D, + outToIn.Get(outIndex), + visit.Get(outIndex), + outIndex, + connectivity); } }; diff --git a/vtkm/worklet/cosmotools/CosmoTools.h b/vtkm/worklet/cosmotools/CosmoTools.h index 282e2ad45..faa7da3a0 100644 --- a/vtkm/worklet/cosmotools/CosmoTools.h +++ b/vtkm/worklet/cosmotools/CosmoTools.h @@ -88,8 +88,6 @@ #include #include -//#define DEBUG_PRINT 1 - namespace { @@ -103,9 +101,10 @@ void DebugPrint(const char* msg, vtkm::cont::ArrayHandle& array) { vtkm::Id count = 20; count = std::min(count, array.GetNumberOfValues()); + auto portal = array.ReadPortal(); std::cout << std::setw(15) << msg << ": "; for (vtkm::Id i = 0; i < count; i++) - std::cout << std::setprecision(3) << std::setw(5) << array.ReadPortal().Get(i) << " "; + std::cout << std::setprecision(3) << std::setw(5) << portal.Get(i) << " "; std::cout << std::endl; } @@ -114,9 +113,10 @@ void DebugPrint(const char* msg, vtkm::cont::ArrayHandleReverse; + // Use optimized ThreadIndicesTopologyMap + using CellThreadIndices = + vtkm::exec::arg::ThreadIndicesTopologyMap; using ValueType = typename WholeFieldIn::ValueType; using CellShapeTag = typename CellSetInType::CellShapeTag; diff --git a/vtkm/worklet/testing/UnitTestCellGradient.cxx b/vtkm/worklet/testing/UnitTestCellGradient.cxx index 12763c9ea..29b8c2ee1 100644 --- a/vtkm/worklet/testing/UnitTestCellGradient.cxx +++ b/vtkm/worklet/testing/UnitTestCellGradient.cxx @@ -191,16 +191,18 @@ void TestCellGradientUniform3DWithVectorField2() { { 10.025, 10.025, 10.025 }, { 30.075, 30.075, 30.075 }, { 60.175, 60.175, 60.175 } } }; + auto vorticityPortal = extraOutput.Vorticity.ReadPortal(); + auto divergencePortal = extraOutput.Divergence.ReadPortal(); for (int i = 0; i < 4; ++i) { vtkm::Vec eg = expected_gradients[i]; - vtkm::Float64 d = extraOutput.Divergence.ReadPortal().Get(i); + vtkm::Float64 d = divergencePortal.Get(i); VTKM_TEST_ASSERT(test_equal((eg[0][0] + eg[1][1] + eg[2][2]), d), "Wrong result for Divergence on 3D uniform data"); vtkm::Vec3f_64 ev(eg[1][2] - eg[2][1], eg[2][0] - eg[0][2], eg[0][1] - eg[1][0]); - vtkm::Vec3f_64 v = extraOutput.Vorticity.ReadPortal().Get(i); + vtkm::Vec3f_64 v = vorticityPortal.Get(i); VTKM_TEST_ASSERT(test_equal(ev, v), "Wrong result for Vorticity on 3D uniform data"); } } @@ -220,9 +222,10 @@ void TestCellGradientExplicit() result = gradient.Run(dataSet.GetCellSet(), dataSet.GetCoordinateSystem(), input); vtkm::Vec3f_32 expected[2] = { { 10.f, 10.1f, 0.0f }, { 10.f, 10.1f, -0.0f } }; + auto resultPortal = result.ReadPortal(); for (int i = 0; i < 2; ++i) { - VTKM_TEST_ASSERT(test_equal(result.ReadPortal().Get(i), expected[i]), + VTKM_TEST_ASSERT(test_equal(resultPortal.Get(i), expected[i]), "Wrong result for CellGradient worklet on 3D explicit data"); } } diff --git a/vtkm/worklet/testing/UnitTestCosmoTools.cxx b/vtkm/worklet/testing/UnitTestCosmoTools.cxx index e569e8edb..71d9d7885 100644 --- a/vtkm/worklet/testing/UnitTestCosmoTools.cxx +++ b/vtkm/worklet/testing/UnitTestCosmoTools.cxx @@ -36,9 +36,11 @@ bool TestArrayHandle(const vtkm::cont::ArrayHandle& ah, return false; } + auto ahPortal = ah.ReadPortal(); + auto expectedPortal = expected.ReadPortal(); for (vtkm::Id i = 0; i < size; ++i) { - if (ah.ReadPortal().Get(i) != expected.ReadPortal().Get(i)) + if (ahPortal.Get(i) != expectedPortal.Get(i)) { return false; }