diff --git a/CMake/vtkLegacyData.cmake b/CMake/vtkLegacyData.cmake index 0921fced275bd3f56cc917918285a1da8bc0f0f8..865e91e3da80862b20efc5561f4e497251d89c66 100644 --- a/CMake/vtkLegacyData.cmake +++ b/CMake/vtkLegacyData.cmake @@ -3,6 +3,7 @@ # TODO: Reference testing data from each module only as needed. set(data "DATA{${VTK_TEST_INPUT_DIR}/,REGEX:.*}") foreach(d + FiberSurface Infovis Infovis/SQLite Infovis/XML diff --git a/Filters/Topology/CMakeLists.txt b/Filters/Topology/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8af0cc798165dfeb7edd245c616561b3e7095008 --- /dev/null +++ b/Filters/Topology/CMakeLists.txt @@ -0,0 +1,5 @@ +set(Module_SRCS + vtkFiberSurface.cxx + ) + +vtk_module_library(vtkFiltersTopology ${Module_SRCS}) diff --git a/Filters/Topology/Testing/Cxx/CMakeLists.txt b/Filters/Topology/Testing/Cxx/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..5e542b6de11047c8dc40137b20941fab6db076a9 --- /dev/null +++ b/Filters/Topology/Testing/Cxx/CMakeLists.txt @@ -0,0 +1,4 @@ +vtk_add_test_cxx(${vtk-module}CxxTests tests + TestFiberSurface.cxx,NO_VALID + ) +vtk_test_cxx_executable(${vtk-module}CxxTests tests) diff --git a/Filters/Topology/Testing/Cxx/TestFiberSurface.cxx b/Filters/Topology/Testing/Cxx/TestFiberSurface.cxx new file mode 100644 index 0000000000000000000000000000000000000000..35ab1990e7f39a6a9c16527300b47a74c6a4867a --- /dev/null +++ b/Filters/Topology/Testing/Cxx/TestFiberSurface.cxx @@ -0,0 +1,214 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkPolyDataAlgorithm.h + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm 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. + +=========================================================================*/ + + +// PURPOSE: FiberSurface test cases output generator (new vtkFiberSurface filter) + +#include <cstring> +#include <sstream> +#include <string> + +#include "vtkFiberSurface.h" +#include "vtkTestUtilities.h" +#include "vtkActor.h" +#include "vtkCellArray.h" +#include "vtkCleanPolyData.h" +#include "vtkFloatArray.h" +#include "vtkPolyData.h" +#include "vtkPolyData.h" +#include "vtkPolyDataReader.h" +#include "vtkPolyDataWriter.h" +#include "vtkSmartPointer.h" +#include "vtkUnstructuredGrid.h" +#include "vtkUnstructuredGridReader.h" + +int TestFiberSurface(int argc, char* argv[]) +{ + bool pass = true; + + /************** Input File paths using vtkTestUtilities ****************/ + const char* inputDataFiles[3] = { vtkTestUtilities::ExpandDataFileName( + argc, argv, "Data/FiberSurface/one_cube.vtk"), + vtkTestUtilities::ExpandDataFileName(argc, argv, + "Data/FiberSurface/one_cube_both_forking.vtk"), + vtkTestUtilities::ExpandDataFileName(argc, argv, + "Data/FiberSurface/one_cube_closed.vtk") }; + + /************** FSCP File paths using vtkTestUtilities ****************/ + const char* inputFSCPFiles[15] = { vtkTestUtilities::ExpandDataFileName( + argc, argv, "Data/FiberSurface/line_01.vtk"), + vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/FiberSurface/line_02.vtk"), + vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/FiberSurface/line_03.vtk"), + vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/FiberSurface/line_04.vtk"), + vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/FiberSurface/line_05.vtk"), + vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/FiberSurface/line_11.vtk"), + vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/FiberSurface/line_12.vtk"), + vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/FiberSurface/line_13.vtk"), + vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/FiberSurface/line_14.vtk"), + vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/FiberSurface/line_15.vtk"), + vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/FiberSurface/line_21.vtk"), + vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/FiberSurface/line_22.vtk"), + vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/FiberSurface/line_23.vtk"), + vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/FiberSurface/line_24.vtk"), + vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/FiberSurface/line_25.vtk") }; + /**********************************************/ + + /********************* Desired output arrays *********************/ + std::string dataToCompare[15] = { "0.779,0.000,0.000,0.659,0.000,0.341,1.000,0.000,0.624,1.000,0." + "797,0.000,0.659,0.659,1.000,1.000,1.000,0.203,1.000,0.376,1." + "000,0.779,1.000,1.000", + "0.775,0.000,0.775,0.798,0.000,1.000,0.768,0.232,0.768,0.874,0.000,0.000,0.889,0.000,0.111,0." + "918,0.918,0.082,0.889,0.889,1.000,0.874,1.000,1.000,0.775,0.225,1.000", + "0.000,0.800,0.000,0.000,0.000,0.667,0.286,0.000,0.286,0.200,0.000,0.000,0.000,0.333,1.000,0." + "200,1.000,1.000,0.286,0.714,1.000,0.000,1.000,0.200", + "0.331,0.000,0.331,0.167,0.833,0.167,0.202,0.000,1.000,0.285,0.000,0.000,0.169,0.000,0.831,0." + "210,0.210,0.790,0.169,0.169,1.000,0.285,1.000,1.000,0.331,0.669,1.000", + "0.650,0.000,1.000,0.500,0.500,0.500,0.000,0.053,0.000,0.000,0.000,0.029,1.000,0.000,0.478,0." + "057,0.000,0.000,1.000,0.868,0.000,0.931,0.931,0.069,1.000,0.522,1.000,1.000,1.000,0.132,0.000," + "0.971,1.000,0.057,1.000,1.000,0.000,1.000,0.947", + "0.500,0.500,1.000,1.000,1.000,0.714,1.000,0.667,1.000,0.714,1.000,1.000", + "0.000,0.901,0.000,0.233,0.767,0.233,0.000,0.271,0.729,0.886,0.000,0.000,0.241,0.000,0.759,1." + "000,0.000,0.263,1.000,0.201,0.000,0.064,0.064,1.000,1.000,1.000,0.460,1.000,0.376,1.000,0.469," + "1.000,1.000,0.000,1.000,0.172", + "0.000,0.571,0.000,0.000,0.000,0.667,1.000,0.000,1.000,0.571,0.000,0.000,0.667,0.667,0.333,1." + "000,0.000,1.000,1.000,0.750,0.000,1.000,1.000,0.143,0.000,0.667,1.000,0.143,1.000,1.000,1.000," + "0.000,1.000,0.000,1.000,0.750", + "0.000,0.250,0.000,0.000,0.000,0.167,0.250,0.000,0.250,0.100,0.000,0.000,0.000,0.833,1.000,0." + "100,1.000,1.000,0.250,0.750,1.000,0.000,1.000,0.750", + "0.333,0.000,0.000,0.333,0.000,0.667,1.000,0.000,0.667,0.333,0.333,0.667,1.000,0.333,0.667,1." + "000,1.000,0.667", + "0.000,0.000,0.300,0.300,0.000,0.300,0.300,0.700,0.300,0.000,0.700,0.300,0.700,0.000,0.300,1." + "000,0.000,0.300,0.700,0.700,0.300,1.000,0.700,0.300,1.000,0.700,1.000,0.700,0.700,1.000,0.300," + "0.700,1.000,0.000,0.700,1.000", + "0.800,0.200,0.800,0.800,0.000,0.800,0.000,0.000,0.800,0.000,0.200,0.800,0.200,0.000,0.800,1." + "000,0.000,0.800,1.000,0.200,0.800,0.200,0.200,0.800,0.200,0.200,1.000,1.000,0.200,1.000,0.800," + "0.200,1.000,0.000,0.200,1.000", + "0.828,0.000,0.828,0.737,0.000,1.000,0.856,0.144,0.856,1.000,0.000,0.828,1.000,0.144,0.856,1." + "000,0.172,1.000,0.856,0.144,1.000", + "0.000,0.739,0.000,0.000,0.000,0.146,0.427,0.000,0.427,0.854,0.000,0.146,1.000,0.000,0.427,1." + "000,0.739,0.000,0.854,0.854,1.000,1.000,1.000,0.261,1.000,0.573,1.000,0.261,1.000,1.000", + "0.977,0.023,0.977,0.980,0.000,0.980,0.000,0.000,0.671,0.000,0.363,0.637,0.329,0.000,0.671,1." + "000,0.000,0.980,1.000,0.023,0.977,0.363,0.363,0.637,0.329,0.329,1.000,1.000,0.020,1.000,0.977," + "0.023,1.000,0.000,0.363,1.000" }; + + std::string outputString; + char firstFieldName[] = "f1"; + char secondFieldName[] = "f2"; + int cmpIndex = 0; + + /************** output to error file ****************/ + /****************************************************/ + + printf("FiberSurface test cases"); + /****************************************************/ + + for (int i = 0; i < 3; i++) // loop for three input files + { + + for (int j = 0; j < 5; j++) // loop for fifteen fscp files + { + // read and load .vtk input data file + vtkSmartPointer<vtkUnstructuredGridReader> mesh_reader = + vtkSmartPointer<vtkUnstructuredGridReader>::New(); + mesh_reader->SetFileName(inputDataFiles[i]); + mesh_reader->Update(); + + // read and load .vtu file containing control polyline + vtkSmartPointer<vtkPolyDataReader> polyline_reader = + vtkSmartPointer<vtkPolyDataReader>::New(); + polyline_reader->SetFileName(inputFSCPFiles[cmpIndex]); + polyline_reader->Update(); + + // extract polydata surface + vtkSmartPointer<vtkFiberSurface> fs = vtkSmartPointer<vtkFiberSurface>::New(); + fs->SetInputData(0, mesh_reader->GetOutput()); + fs->SetInputData(1, polyline_reader->GetOutput()); + fs->SetField1(firstFieldName); + fs->SetField2(secondFieldName); + fs->Update(); + + /********************* cleaning and comparing FS Soup to desired output ********************/ + + vtkPolyData* polyData = fs->GetOutput(); + + /**** Duplicate Ponts ****/ + int numOfPoints = polyData->GetNumberOfPoints(); + int index = 0; + double* coords; + + /**** NON Duplicate Ponts ****/ + vtkSmartPointer<vtkCleanPolyData> clean = vtkSmartPointer<vtkCleanPolyData>::New(); + clean->SetInputData(polyData); + clean->Update(); + numOfPoints = clean->GetOutput()->GetNumberOfPoints(); + std::cout << "."; + + index = 0; + outputString = ""; + std::stringstream ss; + + // out non duplicate points + while (index < numOfPoints) + { + coords = clean->GetOutput()->GetPoint(index); + ss << std::fixed << std::setprecision(3) << coords[0] << "," << std::fixed + << std::setprecision(3) << coords[1] << "," << std::fixed << std::setprecision(3) + << coords[2]; + if (index > 0) + outputString.append(","); + outputString.append(ss.str()); + ss.str(std::string()); + ss.clear(); + index++; + } // end while + + /**********************************************/ + /*********** comparing the results ************/ + + if (strcmp(outputString.c_str(), dataToCompare[cmpIndex].c_str()) != 0) + { + pass = false; + { // writing to file + printf("\n\n/**************************************/"); + printf("\n/********Test Unsuccessful*************/"); + printf("\nInput Data: %s", (std::string(inputDataFiles[i]).substr(6, 31)).c_str()); + printf( + "\nInput FSCP: %s", (std::string(inputFSCPFiles[cmpIndex]).substr(6, 30)).c_str()); + printf("\nString to compare: %s", dataToCompare[cmpIndex].c_str()); + printf("\nOutput String : %s", outputString.c_str()); + printf("\n/**************************************/"); + } // end of writing + } + cmpIndex++; + } // end for + } // end for + + for (int i = 0; i < 15; ++i) + { + delete [] inputFSCPFiles[i]; + } + delete [] inputDataFiles[0]; + delete [] inputDataFiles[1]; + delete [] inputDataFiles[2]; + + if (pass) + { + cout << "\nTest Successful!!!" << endl; + return EXIT_SUCCESS; + } + + cout << "\nTest Unsuccessful." << endl; + return EXIT_FAILURE; +} diff --git a/Filters/Topology/module.cmake b/Filters/Topology/module.cmake new file mode 100644 index 0000000000000000000000000000000000000000..a47982435c277b2e46dd998bb2132ebcc0dd836c --- /dev/null +++ b/Filters/Topology/module.cmake @@ -0,0 +1,13 @@ +vtk_module(vtkFiltersTopology + GROUPS + StandAlone + TEST_DEPENDS + vtkTestingRendering + vtkIOLegacy + KIT + vtkFilters + DEPENDS + vtkCommonCore + vtkCommonDataModel + vtkCommonExecutionModel + ) diff --git a/Filters/Topology/vtkFiberSurface.cxx b/Filters/Topology/vtkFiberSurface.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2265e1e667947ecad2683dfd82d32ffcd8103ae8 --- /dev/null +++ b/Filters/Topology/vtkFiberSurface.cxx @@ -0,0 +1,1075 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkPolyDataAlgorithm.h + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm 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 "vtkFiberSurface.h" + +#include "vtkCell.h" +#include "vtkCellArray.h" +#include "vtkDoubleArray.h" +#include "vtkInformation.h" +#include "vtkInformationVector.h" +#include "vtkObjectFactory.h" +#include "vtkPointData.h" +#include "vtkUnstructuredGrid.h" + +// lookup table for powers of 3 shifts in the marching tetrahedra cases +// which is described in greyTetTriangles table. +// remind that we use 0, 1 and 2 to represent (W)hite, (G)rey and (B)lack cases. For each +// tetrahedron, cases for four vertices can be represented +// by a four-digit number, such as 0001. We assume that all vertices are in CCW order. +// The array greyTetTriangles actually records all of 81 such cases. The order of case +// index starts from right-most to the left-most digit: starting from 0000 to 0002, +// then from 0010 to 0022, then from 0100 to 0222, finally from 1000 to 2222. +// It is easy to observe that: +// from 0001 to 0002, as case number in the first dight is incremented by 1, +// we only need to skip 1 index in the greyTetTriangles table. +// +// from 0010 to 0020, as case number in the second dight is incremented by 1, +// we need to skip 3 indices (0010, 0011, 0012, 0020) +// +// from 0100 to 0200, as case number in the third dight is incremented by 1, +// we need to skip 9 indices (0100, 0101, 0102, 0110, 0111, +// 0112, 0120, 0121, 0122, 0200) +// +// from 1000 to 2000, as case number in the fourth dight is incremented by 1, +// we need to skip 27 indices (1000, 1001, 1002, 1010, 1011, +// 1012, 1020, 1021, 1022, 1100, +// 1101, 1102, 1110, 1111, 1112, +// 1120, 1121, 1122, 1200, 1201, +// 1202, 1210, 1211, 1212, 1220, +// 1221, 1222, 2000) +// Given case classifications for four vertices in a tetrahedron, this ternaryShift array +// could be used to quickly locate the index number in the marching tetrahedron case +// table. This array can also be used in the clipping case look-up table +// clipTriangleVertices. +static const char ternaryShift[4] = { 1, 3, 9, 27 }; + +//---------------------------------------------------------------------------- + +// In the Marching Tethrhedron with Grey case, the iso-surface can be either a triangle +// ,quad or null. The number of triangles in each case is at most 2. This array +// records the number of triangles for every case. +static const int nTriangles[81] = { + 0, 0, 1, 0, 0, 1, 1, 1, 2, // cases 0000-0022 + 0, 0, 1, 0, 1, 1, 1, 1, 1, // cases 0100-0122 + 1, 1, 2, 1, 1, 1, 2, 1, 1, // cases 0200-0222 + + 0, 0, 1, 0, 1, 1, 1, 1, 1, // cases 1000-1022 + 0, 1, 1, 1, 0, 1, 1, 1, 0, // cases 1100-1122 + 1, 1, 1, 1, 1, 0, 1, 0, 0, // cases 1200-1222 + + 1, 1, 2, 1, 1, 1, 2, 1, 1, // cases 2000-2022 + 1, 1, 1, 1, 1, 0, 1, 0, 0, // cases 2100-2122 + 2, 1, 1, 1, 0, 0, 1, 0, 0 // cases 2200-2222 +}; + +//---------------------------------------------------------------------------- + +// array of vertices for triangles in the marching tetrahedron cases +// each vertex on the tetra is marked as (B)lack, (W)hite or (G)rey +// there are total 81 cases. Each case contains at most two triangles. +// The order these cases arranged are as follows: starting from 0000 to 0002, +// then from 0010 to 0022, then from 0100 to 0222, finally from 1000 to 2222. +static const vtkFiberSurface::BaseVertexType greyTetTriangles[81][2][3] = { + { // 0. case 0000 (A) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 1. case 0001 (B) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 2. case 0002 (D) + { vtkFiberSurface::bv_edge_01, vtkFiberSurface::bv_edge_02, vtkFiberSurface::bv_edge_03 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 3. case 0010 (B) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 4. case 0011 (C) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 5. case 0012 (F) + { vtkFiberSurface::bv_vertex_1, vtkFiberSurface::bv_edge_02, vtkFiberSurface::bv_edge_03 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 6. case 0020 (D) + { vtkFiberSurface::bv_edge_01, vtkFiberSurface::bv_edge_13, vtkFiberSurface::bv_edge_12 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 7. case 0021 (F) + { vtkFiberSurface::bv_vertex_0, vtkFiberSurface::bv_edge_13, vtkFiberSurface::bv_edge_12 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 8. case 0022 (I) + { vtkFiberSurface::bv_edge_02, vtkFiberSurface::bv_edge_03, vtkFiberSurface::bv_edge_13 }, + + { vtkFiberSurface::bv_edge_02, vtkFiberSurface::bv_edge_13, vtkFiberSurface::bv_edge_12 } }, + { // 9. case 0100 (B) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 10. case 0101 (C) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 11. case 0102 (F) + { vtkFiberSurface::bv_vertex_2, vtkFiberSurface::bv_edge_03, vtkFiberSurface::bv_edge_01 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 12. case 0110 (C) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 13. case 0111 (E) + { vtkFiberSurface::bv_vertex_0, vtkFiberSurface::bv_vertex_1, vtkFiberSurface::bv_vertex_2 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 14. case 0112 (H) + { vtkFiberSurface::bv_vertex_1, vtkFiberSurface::bv_vertex_2, vtkFiberSurface::bv_edge_03 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 15. case 0120 (F) + { vtkFiberSurface::bv_vertex_2, vtkFiberSurface::bv_edge_01, vtkFiberSurface::bv_edge_13 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 16. case 0121 (H) + { vtkFiberSurface::bv_vertex_2, vtkFiberSurface::bv_vertex_0, vtkFiberSurface::bv_edge_13 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 17. case 0122 (K) + { vtkFiberSurface::bv_vertex_2, vtkFiberSurface::bv_edge_03, vtkFiberSurface::bv_edge_13 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 18. case 0200 (D) + { vtkFiberSurface::bv_edge_02, vtkFiberSurface::bv_edge_12, vtkFiberSurface::bv_edge_23 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 19. case 0201 (F) + { vtkFiberSurface::bv_vertex_0, vtkFiberSurface::bv_edge_12, vtkFiberSurface::bv_edge_23 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 20. case 0202 (I) + { vtkFiberSurface::bv_edge_12, vtkFiberSurface::bv_edge_23, vtkFiberSurface::bv_edge_03 }, + + { vtkFiberSurface::bv_edge_12, vtkFiberSurface::bv_edge_03, vtkFiberSurface::bv_edge_01 } }, + { // 21. case 0210 (F) + { vtkFiberSurface::bv_vertex_1, vtkFiberSurface::bv_edge_23, vtkFiberSurface::bv_edge_02 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 22. case 0211 (H) + { vtkFiberSurface::bv_vertex_0, vtkFiberSurface::bv_vertex_1, vtkFiberSurface::bv_edge_23 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 23. case 0212 (K) + { vtkFiberSurface::bv_vertex_1, vtkFiberSurface::bv_edge_03, vtkFiberSurface::bv_edge_23 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 24. case 0220 (I) + { vtkFiberSurface::bv_edge_01, vtkFiberSurface::bv_edge_13, vtkFiberSurface::bv_edge_23 }, + + { vtkFiberSurface::bv_edge_01, vtkFiberSurface::bv_edge_23, vtkFiberSurface::bv_edge_02 } }, + { // 25. case 0221 (K) + { vtkFiberSurface::bv_vertex_0, vtkFiberSurface::bv_edge_13, vtkFiberSurface::bv_edge_23 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 26. case 0222 (M) + { vtkFiberSurface::bv_edge_03, vtkFiberSurface::bv_edge_13, vtkFiberSurface::bv_edge_23 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 27. case 1000 (B) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 28. case 1001 (C) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 29. case 1002 (F) + { vtkFiberSurface::bv_vertex_3, vtkFiberSurface::bv_edge_01, vtkFiberSurface::bv_edge_02 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 30. case 1010 (C) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 31. case 1011 (E) + { vtkFiberSurface::bv_vertex_0, vtkFiberSurface::bv_vertex_3, vtkFiberSurface::bv_vertex_1 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 32. case 1012 (H) + { vtkFiberSurface::bv_vertex_3, vtkFiberSurface::bv_vertex_1, vtkFiberSurface::bv_edge_02 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 33. case 1020 (F) + { vtkFiberSurface::bv_vertex_3, vtkFiberSurface::bv_edge_12, vtkFiberSurface::bv_edge_01 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 34. case 1021 (H) + { vtkFiberSurface::bv_vertex_0, vtkFiberSurface::bv_vertex_3, vtkFiberSurface::bv_edge_12 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 35. case 1022 (K) + { vtkFiberSurface::bv_vertex_3, vtkFiberSurface::bv_edge_12, vtkFiberSurface::bv_edge_02 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 36. case 1100 (C) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 37. case 1101 (E) + { vtkFiberSurface::bv_vertex_0, vtkFiberSurface::bv_vertex_2, vtkFiberSurface::bv_vertex_3 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 38. case 1102 (H) + { vtkFiberSurface::bv_vertex_2, vtkFiberSurface::bv_vertex_3, vtkFiberSurface::bv_edge_01 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 39. case 1110 (E) + { vtkFiberSurface::bv_vertex_1, vtkFiberSurface::bv_vertex_3, vtkFiberSurface::bv_vertex_2 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 40. case 1111 (G) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 41. case 1112 (J) + { vtkFiberSurface::bv_vertex_1, vtkFiberSurface::bv_vertex_2, vtkFiberSurface::bv_vertex_3 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 42. case 1120 (H) + { vtkFiberSurface::bv_vertex_3, vtkFiberSurface::bv_vertex_2, vtkFiberSurface::bv_edge_01 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 43. case 1121 (J) + { vtkFiberSurface::bv_vertex_0, vtkFiberSurface::bv_vertex_3, vtkFiberSurface::bv_vertex_2 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 44. case 1122 (L) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 45. case 1200 (F) + { vtkFiberSurface::bv_vertex_3, vtkFiberSurface::bv_edge_02, vtkFiberSurface::bv_edge_12 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 46. case 1201 (H) + { vtkFiberSurface::bv_vertex_3, vtkFiberSurface::bv_vertex_0, vtkFiberSurface::bv_edge_12 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 47. case 1202 (K) + { vtkFiberSurface::bv_vertex_3, vtkFiberSurface::bv_edge_01, vtkFiberSurface::bv_edge_12 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 48. case 1210 (H) + { vtkFiberSurface::bv_vertex_1, vtkFiberSurface::bv_vertex_3, vtkFiberSurface::bv_edge_02 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 49. case 1211 (J) + { vtkFiberSurface::bv_vertex_0, vtkFiberSurface::bv_vertex_1, vtkFiberSurface::bv_vertex_3 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 50. case 1212 (L) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 51. case 1220 (K) + { vtkFiberSurface::bv_vertex_3, vtkFiberSurface::bv_edge_02, vtkFiberSurface::bv_edge_01 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 52. case 1221 (L) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 53. case 1222 (N) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 54. case 2000 (D) + { vtkFiberSurface::bv_edge_03, vtkFiberSurface::bv_edge_23, vtkFiberSurface::bv_edge_13 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 55. case 2001 (F) + { vtkFiberSurface::bv_vertex_0, vtkFiberSurface::bv_edge_23, vtkFiberSurface::bv_edge_13 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 56. case 2002 (I) + { vtkFiberSurface::bv_edge_23, vtkFiberSurface::bv_edge_13, vtkFiberSurface::bv_edge_01 }, + + { vtkFiberSurface::bv_edge_23, vtkFiberSurface::bv_edge_01, vtkFiberSurface::bv_edge_02 } }, + { // 57. case 2010 (F) + { vtkFiberSurface::bv_vertex_1, vtkFiberSurface::bv_edge_03, vtkFiberSurface::bv_edge_23 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 58. case 2011 (H) + { vtkFiberSurface::bv_vertex_1, vtkFiberSurface::bv_vertex_0, vtkFiberSurface::bv_edge_23 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 59. case 2012 (K) + { vtkFiberSurface::bv_vertex_1, vtkFiberSurface::bv_edge_02, vtkFiberSurface::bv_edge_23 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 60. case 2020 (I) + { vtkFiberSurface::bv_edge_12, vtkFiberSurface::bv_edge_01, vtkFiberSurface::bv_edge_03 }, + + { vtkFiberSurface::bv_edge_12, vtkFiberSurface::bv_edge_03, vtkFiberSurface::bv_edge_23 } }, + { // 61. case 2021 (K) + { vtkFiberSurface::bv_vertex_0, vtkFiberSurface::bv_edge_23, vtkFiberSurface::bv_edge_12 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 62. case 2022 (M) + { vtkFiberSurface::bv_edge_02, vtkFiberSurface::bv_edge_23, vtkFiberSurface::bv_edge_12 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 63. case 2100 (F) + { vtkFiberSurface::bv_vertex_2, vtkFiberSurface::bv_edge_13, vtkFiberSurface::bv_edge_03 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 64. case 2101 (H) + { vtkFiberSurface::bv_vertex_0, vtkFiberSurface::bv_vertex_2, vtkFiberSurface::bv_edge_13 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 65. case 2102 (K) + { vtkFiberSurface::bv_vertex_2, vtkFiberSurface::bv_edge_13, vtkFiberSurface::bv_edge_01 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 66. case 2110 (H) + { vtkFiberSurface::bv_vertex_2, vtkFiberSurface::bv_vertex_1, vtkFiberSurface::bv_edge_03 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 67. case 2111 (J) + { vtkFiberSurface::bv_vertex_0, vtkFiberSurface::bv_vertex_2, vtkFiberSurface::bv_vertex_1 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 68. case 2112 (L) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 69. case 2120 (K) + { vtkFiberSurface::bv_vertex_2, vtkFiberSurface::bv_edge_01, vtkFiberSurface::bv_edge_03 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 70. case 2121 (L) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 71. case 2122 (N) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 72. case 2200 (I) + { vtkFiberSurface::bv_edge_13, vtkFiberSurface::bv_edge_03, vtkFiberSurface::bv_edge_02 }, + + { vtkFiberSurface::bv_edge_13, vtkFiberSurface::bv_edge_02, vtkFiberSurface::bv_edge_12 } }, + { // 73. case 2201 (K) + { vtkFiberSurface::bv_vertex_0, vtkFiberSurface::bv_edge_12, vtkFiberSurface::bv_edge_13 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 74. case 2202 (M) + { vtkFiberSurface::bv_edge_01, vtkFiberSurface::bv_edge_12, vtkFiberSurface::bv_edge_13 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 75. case 2210 (K) + { vtkFiberSurface::bv_vertex_1, vtkFiberSurface::bv_edge_03, vtkFiberSurface::bv_edge_02 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 76. case 2211 (L) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 77. case 2212 (N) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 78. case 2220 (M) + { vtkFiberSurface::bv_edge_01, vtkFiberSurface::bv_edge_03, vtkFiberSurface::bv_edge_02 }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 79. case 2221 (N) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } }, + { // 80. case 2222 (O) + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used }, + + { vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used, vtkFiberSurface::bv_not_used } } +}; + +//---------------------------------------------------------------------------- + +// conversion from the enum semantics for edges to actual edge numbers +// depends on the ordering of bv_edge_** in BaseVertexType enum +static const int edge2endpoints[6][2] = { { 0, 1 }, { 0, 2 }, { 0, 3 }, { 1, 2 }, { 1, 3 }, + { 2, 3 } }; + +//---------------------------------------------------------------------------- + +// convert edge_*_param_* enum to edge numbers +// depends on the ordering of edge_0 and edge_1 enums (i.e. edge_0 + 2 == edge_1 + 1 +// == edge_2) +static const int clip2points[3][2] = { { 1, 2 }, { 2, 0 }, { 0, 1 } }; + +//---------------------------------------------------------------------------- + +// this table lists the number of triangles per case for fiber clipping +static const int nClipTriangles[27] = { + 0, 1, 2, 1, 2, 3, 2, 3, 2, // cases 000 - 022 + 1, 2, 3, 2, 1, 2, 3, 2, 1, // cases 100 - 122 + 2, 3, 2, 3, 2, 1, 2, 1, 0 // cases 200 - 222 +}; + +//---------------------------------------------------------------------------- + +// with up to three triangles, we can have up to 9 vertices specified +// note that this may lead to redundant interpolation (as in MC/MT), but we gain in +// clarity by doing it this way. +// This array therefore specifies the vertices of each triangle to be rendered in the +// clipping process. +static const int clipTriangleVertices[27][3][3] = { + // 0. case 000: A - empty + { { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 1. case 001: B - point-triangle + { { vtkFiberSurface::vertex_0, vtkFiberSurface::edge_2_parm_0, vtkFiberSurface::edge_1_parm_0 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 2. case 002: D - stripe + { { vtkFiberSurface::edge_2_parm_0, vtkFiberSurface::edge_1_parm_0, + vtkFiberSurface::edge_1_parm_1 }, + + { vtkFiberSurface::edge_2_parm_0, vtkFiberSurface::edge_1_parm_1, + vtkFiberSurface::edge_2_parm_1 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 3. case 010: B - point-triangle + { { vtkFiberSurface::vertex_1, vtkFiberSurface::edge_0_parm_0, vtkFiberSurface::edge_2_parm_0 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 4. case 011: C - edge-quad + { { vtkFiberSurface::vertex_0, vtkFiberSurface::vertex_1, vtkFiberSurface::edge_0_parm_0 }, + + { vtkFiberSurface::vertex_0, vtkFiberSurface::edge_0_parm_0, vtkFiberSurface::edge_1_parm_0 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 5. case 012: E - point-stripe + { { vtkFiberSurface::vertex_1, vtkFiberSurface::edge_0_parm_0, vtkFiberSurface::edge_2_parm_1 }, + + { vtkFiberSurface::edge_2_parm_1, vtkFiberSurface::edge_0_parm_0, + vtkFiberSurface::edge_1_parm_1 }, + + { vtkFiberSurface::edge_1_parm_1, vtkFiberSurface::edge_0_parm_0, + vtkFiberSurface::edge_1_parm_0 } }, + // 6. case 020: D - stripe + { { vtkFiberSurface::edge_0_parm_0, vtkFiberSurface::edge_2_parm_0, + vtkFiberSurface::edge_2_parm_1 }, + + { vtkFiberSurface::edge_0_parm_0, vtkFiberSurface::edge_2_parm_1, + vtkFiberSurface::edge_0_parm_1 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 7. case 021: E - point-stripe + { { vtkFiberSurface::vertex_0, vtkFiberSurface::edge_2_parm_1, vtkFiberSurface::edge_1_parm_0 }, + + { vtkFiberSurface::edge_1_parm_0, vtkFiberSurface::edge_2_parm_1, + vtkFiberSurface::edge_0_parm_0 }, + + { vtkFiberSurface::edge_0_parm_0, vtkFiberSurface::edge_2_parm_1, + vtkFiberSurface::edge_0_parm_1 } }, + // 8. case 022: D - stripe + { { vtkFiberSurface::edge_1_parm_1, vtkFiberSurface::edge_0_parm_1, + vtkFiberSurface::edge_0_parm_0 }, + + { vtkFiberSurface::edge_1_parm_1, vtkFiberSurface::edge_0_parm_0, + vtkFiberSurface::edge_1_parm_0 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 9. case 100: B - point-triangle + { { vtkFiberSurface::vertex_2, vtkFiberSurface::edge_1_parm_0, vtkFiberSurface::edge_0_parm_0 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 10. case 101: C - edge-quad + { { vtkFiberSurface::vertex_2, vtkFiberSurface::vertex_0, vtkFiberSurface::edge_2_parm_0 }, + + { vtkFiberSurface::vertex_2, vtkFiberSurface::edge_2_parm_0, vtkFiberSurface::edge_0_parm_0 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 11. case 102: E - point-stripe + { { vtkFiberSurface::vertex_2, vtkFiberSurface::edge_1_parm_1, vtkFiberSurface::edge_0_parm_0 }, + + { vtkFiberSurface::edge_0_parm_0, vtkFiberSurface::edge_1_parm_1, + vtkFiberSurface::edge_2_parm_0 }, + + { vtkFiberSurface::edge_2_parm_0, vtkFiberSurface::edge_1_parm_1, + vtkFiberSurface::edge_2_parm_1 } }, + // 12. case 110: C - edge-quad + { { vtkFiberSurface::vertex_1, vtkFiberSurface::vertex_2, vtkFiberSurface::edge_1_parm_0 }, + + { vtkFiberSurface::vertex_1, vtkFiberSurface::edge_1_parm_0, vtkFiberSurface::edge_2_parm_0 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 13. case 111: F - entire triangle + { { vtkFiberSurface::vertex_0, vtkFiberSurface::vertex_1, vtkFiberSurface::vertex_2 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 14. case 112: C - edge-quad + { { vtkFiberSurface::vertex_1, vtkFiberSurface::vertex_2, vtkFiberSurface::edge_1_parm_1 }, + + { vtkFiberSurface::vertex_1, vtkFiberSurface::edge_1_parm_1, vtkFiberSurface::edge_2_parm_1 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 15. case 120: E - point-stripe + { { vtkFiberSurface::vertex_2, vtkFiberSurface::edge_1_parm_0, vtkFiberSurface::edge_0_parm_1 }, + + { vtkFiberSurface::edge_0_parm_1, vtkFiberSurface::edge_1_parm_0, + vtkFiberSurface::edge_2_parm_1 }, + + { vtkFiberSurface::edge_2_parm_1, vtkFiberSurface::edge_1_parm_0, + vtkFiberSurface::edge_2_parm_0 } }, + // 16. case 121: C - edge-quad + { { vtkFiberSurface::vertex_2, vtkFiberSurface::vertex_0, vtkFiberSurface::edge_2_parm_1 }, + + { vtkFiberSurface::vertex_2, vtkFiberSurface::edge_2_parm_1, vtkFiberSurface::edge_0_parm_1 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 17. case 122: B - point-triangle + { { vtkFiberSurface::vertex_2, vtkFiberSurface::edge_1_parm_1, vtkFiberSurface::edge_0_parm_1 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 18. case 200: D - stripe + { { vtkFiberSurface::edge_1_parm_0, vtkFiberSurface::edge_0_parm_0, + vtkFiberSurface::edge_0_parm_1 }, + + { vtkFiberSurface::edge_1_parm_0, vtkFiberSurface::edge_0_parm_1, + vtkFiberSurface::edge_1_parm_1 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 19. case 201: E - point-stripe + { { vtkFiberSurface::vertex_0, vtkFiberSurface::edge_2_parm_0, vtkFiberSurface::edge_1_parm_1 }, + + { vtkFiberSurface::edge_1_parm_1, vtkFiberSurface::edge_2_parm_0, + vtkFiberSurface::edge_0_parm_1 }, + + { vtkFiberSurface::edge_0_parm_1, vtkFiberSurface::edge_2_parm_0, + vtkFiberSurface::edge_0_parm_0 } }, + // 20. case 202: D - stripe + { { vtkFiberSurface::edge_0_parm_1, vtkFiberSurface::edge_2_parm_1, + vtkFiberSurface::edge_2_parm_0 }, + + { vtkFiberSurface::edge_0_parm_1, vtkFiberSurface::edge_2_parm_0, + vtkFiberSurface::edge_0_parm_0 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 21. case 210: E - point-stripe + { { vtkFiberSurface::vertex_1, vtkFiberSurface::edge_0_parm_1, vtkFiberSurface::edge_2_parm_0 }, + + { vtkFiberSurface::edge_2_parm_0, vtkFiberSurface::edge_0_parm_1, + vtkFiberSurface::edge_1_parm_0 }, + + { vtkFiberSurface::edge_1_parm_0, vtkFiberSurface::edge_0_parm_1, + vtkFiberSurface::edge_1_parm_1 } }, + // 22. case 211: C - edge-quad + { { vtkFiberSurface::vertex_0, vtkFiberSurface::vertex_1, vtkFiberSurface::edge_0_parm_1 }, + + { vtkFiberSurface::vertex_0, vtkFiberSurface::edge_0_parm_1, vtkFiberSurface::edge_1_parm_1 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 23. case 212: B - point-triangle + { { vtkFiberSurface::vertex_1, vtkFiberSurface::edge_0_parm_1, vtkFiberSurface::edge_2_parm_1 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 24. case 220: D - stripe + { { vtkFiberSurface::edge_2_parm_1, vtkFiberSurface::edge_1_parm_1, + vtkFiberSurface::edge_1_parm_0 }, + + { vtkFiberSurface::edge_2_parm_1, vtkFiberSurface::edge_1_parm_0, + vtkFiberSurface::edge_2_parm_0 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 25. case 221: B - point-triangle + { { vtkFiberSurface::vertex_0, vtkFiberSurface::edge_2_parm_1, vtkFiberSurface::edge_1_parm_1 }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } }, + // 26. case 222: A - empty + { { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used }, + + { vtkFiberSurface::not_used, vtkFiberSurface::not_used, vtkFiberSurface::not_used } } +}; + +//---------------------------------------------------------------------------- + +vtkStandardNewMacro(vtkFiberSurface); + +//---------------------------------------------------------------------------- + +vtkFiberSurface::vtkFiberSurface() +{ + // number of input ports is 2 + this->SetNumberOfInputPorts(2); + this->Fields[0] = this->Fields[1] = NULL; +} + +//---------------------------------------------------------------------------- + +void vtkFiberSurface::SetField1(char* nm) +{ + this->Fields[0] = nm; +} + +//---------------------------------------------------------------------------- + +void vtkFiberSurface::SetField2(char* nm) +{ + this->Fields[1] = nm; +} + +//---------------------------------------------------------------------------- + +void vtkFiberSurface::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); +} + +//---------------------------------------------------------------------------- + +int vtkFiberSurface::FillInputPortInformation(int port, vtkInformation* info) +{ + switch (port) + { + // port 0 expects a tetrahedral mesh as input data + case 0: + info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkUnstructuredGrid"); + return 1; + // port 1 expects a fiber surface control polygon (FSCP) + case 1: + info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData"); + return 1; + } + return 0; +} + +//---------------------------------------------------------------------------- + +int vtkFiberSurface::RequestData(vtkInformation* vtkNotUsed(request), + vtkInformationVector** inputVector, vtkInformationVector* outputVector) +{ + // obtain the input/output port info + vtkInformation* inMeshInfo = inputVector[0]->GetInformationObject(0); + vtkInformation* inLinesInfo = inputVector[1]->GetInformationObject(0); + vtkInformation* outInfo = outputVector->GetInformationObject(0); + + // get the input regular grid and fiber surface control polygon (FSCP) + vtkUnstructuredGrid* mesh = + vtkUnstructuredGrid::SafeDownCast(inMeshInfo->Get(vtkDataObject::DATA_OBJECT())); + vtkPolyData* lines = vtkPolyData::SafeDownCast(inLinesInfo->Get(vtkDataObject::DATA_OBJECT())); + vtkPolyData* output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); + + // get the dataset statistics + vtkIdType numCells = mesh->GetNumberOfCells(); + vtkIdType numPts = mesh->GetNumberOfPoints(); + vtkIdType numArrays = mesh->GetPointData()->GetNumberOfArrays(); + + // check if the data set is empty or not. + // check if the data set contains at least two scalar arrays. + if (numCells < 1 || numPts < 1 || numArrays < 2) + { + vtkErrorMacro("No input data. Two fields are required for fiber surface generation"); + return 1; + } + + // check if two scalar fields are specified by the user. + // if not specified, an error message will be shown. + if (!this->Fields[0] || !this->Fields[1]) + { + vtkErrorMacro("Two scalar fields need to be specified."); + return 1; + } + else + { + vtkSmartPointer<vtkDataArray> array; + for (int i = 0; i < 2; i++) + { + array = mesh->GetPointData()->GetArray(this->Fields[i]); + if (!array) + { + vtkErrorMacro("Names of the scalar array do not exist."); + return 1; + } + } + } + + // extract the points of the subdivided tetrahedra + vtkPoints* meshPoints = mesh->GetPoints(); + + // allocate points and cell storage for computing fiber surface structure + vtkSmartPointer<vtkPoints> newPoints = vtkSmartPointer<vtkPoints>::New(); + vtkSmartPointer<vtkCellArray> newPolys = vtkSmartPointer<vtkCellArray>::New(); + + // extract two scalar field arrays and put into one structure + vtkDataArray* fieldScalars[2] = { mesh->GetPointData()->GetArray(this->Fields[0]), + mesh->GetPointData()->GetArray(this->Fields[1]) }; + + // extract a fiber surface for every edge in the FSCP. + // if the FSCP has no edges, this loop will not start. + // Algorithm: + // 1. Extract base fiber surface using marching tetrahedron + // 2. Clip the base fiber surface to extract the exact fiber surface with respect to + // each line segment in FSCP + const vtkIdType numberOfLines = lines->GetNumberOfCells(); + for (vtkIdType lineIndex = 0; lineIndex < numberOfLines; ++lineIndex) + { + // For each line segment of the FSCP + vtkCell* line = lines->GetCell(lineIndex); + + // test if the current cell of FSCP is a line or not. + // The computation only proceed if the current cell is a line. + if (line->GetNumberOfPoints() != 2) + { + vtkWarningMacro("Current cell index " << lineIndex << " in the FSCP is not of a line type."); + continue; + } + + // get the starting point of the line + double pointStart[3], pointEnd[3]; + lines->GetPoints()->GetPoint(line->GetPointId(0), pointStart); + + // get the end point of the line + lines->GetPoints()->GetPoint(line->GetPointId(1), pointEnd); + + // first point is the origin of the parametric form of line + const double origin[2] = { pointStart[0], pointStart[1] }; + + // if the input point coordinates are normalised values. We need to interpolate these + // values to the actual scalar values. + // obtain the direction vector of the line segment + const double direction[2] = { pointEnd[0] - origin[0], pointEnd[1] - origin[1] }; + + // compute length of the line segment + const double length = sqrt(direction[0] * direction[0] + direction[1] * direction[1]); + + // if the length of the current line is zero, then skip to the next cell. + if (length == 0.0f) + { + vtkWarningMacro("End points of the current line index " + << lineIndex << " in the FSCP colocate on the same point."); + continue; + } + + // compute the normal vector to the line segment + const double normal[2] = { direction[1] / length, -direction[0] / length }; + + // given a line segment with one of its endpoint origin and its normal vector normal + // given an arbitrary point p + // the signed distance from p to the line can be computed using the Hess Normal Form: + // signedDistance = dot(p - origin, normal) + // = dot(p,normal) - dot(origin,normal) + // since dot(origin,normal) is an invariant, therefore we compute it first to avoid + // duplicate computation in the following steps. + const double dotOriginNormal = normal[0] * origin[0] + normal[1] * origin[1]; + + // get the number of tetras in the domain + const vtkIdType numberOfTets = mesh->GetNumberOfCells(); + + // iterate through every cell of the domain and extract its fiber surface. + // note that each cell is a tetrahedron. + for (vtkIdType tetIndex = 0; tetIndex < numberOfTets; ++tetIndex) + { + // update progress of the extraction + this->UpdateProgress((tetIndex + 1.0) / numberOfTets); + + // obtain the current tetra cell + vtkCell* tet = mesh->GetCell(tetIndex); + + // check if the current cell is a tetrahedron type or not + // if not, skip this cell. + if (mesh->GetCellType(tetIndex) != VTK_TETRA || tet->GetNumberOfPoints() != 4) + { + vtkWarningMacro("Current cell " << tetIndex << "is not of a tetrahedron type."); + continue; + } + + // case number for the current tetra cell, initially set to zero + unsigned caseNumber = 0; + + // classify four vertices of the tetra with respect to the signed distance to the + // line + double distancesToLine[4]; + + // for each vertex in the tetra cell + for (int vertexIndex = 0; vertexIndex < 4; ++vertexIndex) + { + // get the Id of the vertex of the tetra + const vtkIdType pointId = tet->GetPointId(vertexIndex); + + // compute signed distance between the image of tetra vertex in the range + // and the control line using Hessian Normal Form: + // signedDistance = dot(p - origin, normal) + // = dot(p,normal) - dot(origin,normal) + distancesToLine[vertexIndex] = fieldScalars[0]->GetTuple1(pointId) * normal[0] + + fieldScalars[1]->GetTuple1(pointId) * normal[1] - dotOriginNormal; + + // classify the tetra vertex based on the sign of the distance + // if distance == 0 : then p is on the line + // if distance > 0 : then p is on the right side + // if distance < 0; then p is on the left side + if (distancesToLine[vertexIndex] == 0.0) + { + // locate the index number using ternaryShift array. + // please see the comment on this array. + caseNumber += ternaryShift[vertexIndex]; + } + else if (distancesToLine[vertexIndex] > 0.0) + { + // locate the index number using ternaryShift array. + // please see the comment on this array. + caseNumber += 2 * ternaryShift[vertexIndex]; + } + } + + // extract the base fiber surface using Marching Tetrahedra + // loop starts only when there is at least one triangle in this case. + for (int triangleIndex = 0; triangleIndex < nTriangles[caseNumber]; ++triangleIndex) + { + // Coordinates for each triangle points + double trianglePoints[3][3]; + + // Clipping parameter for the base fiber surface + double triangleParameters[3]; + + // Clipping case number is initially set to zero. + unsigned triangleCaseNumber = 0; + + // global index in the input data of the current vertex + vtkIdType dataIndex; + + // scalar values of the two fields of the current vertex; + double pointField1, pointField2; + + // For each vertex in the base fiber surface + for (int pointIndex = 0; pointIndex < 3; ++pointIndex) + { + // get the type of the triangle vertex from the lookup table + const int type = greyTetTriangles[caseNumber][triangleIndex][pointIndex]; + switch (type) + { + case bv_vertex_0: + case bv_vertex_1: + case bv_vertex_2: + case bv_vertex_3: + // if the triangle vertex coincides with the tetra vertex, + // there will be a grey case. + // copy grey vertex to output triangle point array + double point[3]; + dataIndex = tet->GetPointId(type - bv_vertex_0); + meshPoints->GetPoint(dataIndex, point); + memcpy(trianglePoints[pointIndex], point, sizeof(point)); + + // get the current vertex scalar values + pointField1 = fieldScalars[0]->GetTuple1(dataIndex); + pointField2 = fieldScalars[1]->GetTuple1(dataIndex); + + // compute parameter on the parametric line for each triangle point. + // Assume edgeRange is a vector to represent the vector beteen interpolated + // range values and origin of polygon line edge. + // Assume direction is the direction vector of the current polygon edge. + // The projection of the range values onto the polygon edge + // can be computed as: + // Proj^direction_edgeRange = dot(edgeRange,direction) / |direction|^2 + // this projection is the parameter t used in the clipping case. + // if t < 0 or t > 1: then the current vertex is outside the current line + // segment of FSCP + // if 0 << t << 1 : then the current vertex is within the current line + // segment of FSCP + triangleParameters[pointIndex] = ((pointField1 - origin[0]) * direction[0] + + (pointField2 - origin[1]) * direction[1]) / + (length * length); + + if (triangleParameters[pointIndex] > 1.0) + { + // locate the index number in the clipping case table + triangleCaseNumber += 2 * ternaryShift[pointIndex]; + } + else if (triangleParameters[pointIndex] >= 0.0) + { + // locate the index number in the clipping case table + triangleCaseNumber += ternaryShift[pointIndex]; + } + // less than zero case adds nothing to triangleCaseNumber + break; + + case bv_edge_01: + case bv_edge_02: + case bv_edge_03: + case bv_edge_12: + case bv_edge_13: + case bv_edge_23: + { + // If the triangle vertex happens on the tetra edges, + // compute interpolation mixing value. + // Note that (type - bv_edge_01) represents the current edge index. + // Assume an edge with two end points u and v. + // Since we are now in edge case, given the signed distances of u and v, + // the alpha value can be computed as: + // alpha = signedDistance(u) / (signedDistance(u) - signedDistance(v)) + const double alpha = distancesToLine[edge2endpoints[type - bv_edge_01][0]] / + (distancesToLine[edge2endpoints[type - bv_edge_01][0]] - + distancesToLine[edge2endpoints[type - bv_edge_01][1]]); + + // convert enum to pair of endpoints and get their id in the point set + const vtkIdType pointIds[2] = { tet->GetPointId(edge2endpoints[type - bv_edge_01][0]), + tet->GetPointId(edge2endpoints[type - bv_edge_01][1]) }; + + // get coordinates of the edge end points + double point0[3], point1[3]; + meshPoints->GetPoint(pointIds[0], point0); + meshPoints->GetPoint(pointIds[1], point1); + + // compute triangle vertex coordinates using linear interpolation + trianglePoints[pointIndex][0] = (1.0 - alpha) * point0[0] + alpha * point1[0]; + trianglePoints[pointIndex][1] = (1.0 - alpha) * point0[1] + alpha * point1[1]; + trianglePoints[pointIndex][2] = (1.0 - alpha) * point0[2] + alpha * point1[2]; + + // compute triangle vertex range values using linear interpolation. + const double edgeFields[2] = { (1.0 - alpha) * + fieldScalars[0]->GetTuple1(pointIds[0]) + + alpha * fieldScalars[0]->GetTuple1(pointIds[1]), + (1.0 - alpha) * fieldScalars[1]->GetTuple1(pointIds[0]) + + alpha * fieldScalars[1]->GetTuple1(pointIds[1]) }; + + // compute parameter on the parametric line for each triangle point. + // Assume edgeRange is a vector to represent the vector beteen interpolated + // range values and origin of polygon line edge. + // Assume direction is the direction vector of the current polygon edge. + // The projection of the range values onto the polygon edge + // can be computed as: + // Proj^direction_edgeRange = dot(edgeRange,direction) / |direction|^2 + // this projection is the parameter t used in the clipping case. + // if t < 0 or t > 1: then the current vertex is outside the current line + // segment of FSCP + // if 0 << t << 1 : then the current vertex is within the current line + // segment of FSCP + triangleParameters[pointIndex] = ((edgeFields[0] - origin[0]) * direction[0] + + (edgeFields[1] - origin[1]) * direction[1]) / + (length * length); + + if (triangleParameters[pointIndex] > 1.0) + { + // locate the index number in the clipping case table + triangleCaseNumber += 2 * ternaryShift[pointIndex]; + } + else if (triangleParameters[pointIndex] >= 0.0) + { + // locate the index number in the clipping case table + triangleCaseNumber += ternaryShift[pointIndex]; + } + // less than zero case adds nothing to triangleCaseNumber + break; + } + default: + // report error in case an invalid triangle is being extracted + vtkErrorMacro(<< "Invalid value in the marching tetrahedra case: " << caseNumber); + break; + } + } + // clip or cull the triangle from the base fiber surface. + int counter = 0; + vtkIdType pts[3]; + for (int tindex = 0; tindex != nClipTriangles[triangleCaseNumber]; + ++tindex) + { + for (int vertexIndex = 0; vertexIndex != 3; ++vertexIndex) + { + // array to holds indices of the two edge endpoints + int ids[2]; + + // interpolant value of the triangle vertex with respect to the control line + double alpha; + const int type = clipTriangleVertices[triangleCaseNumber][tindex][vertexIndex]; + switch (type) + { + case vertex_0: + case vertex_1: + case vertex_2: + pts[counter] = newPoints->InsertNextPoint(trianglePoints[type - vertex_0]); + break; + case edge_0_parm_0: + case edge_1_parm_0: + case edge_2_parm_0: + ids[0] = clip2points[type - edge_0_parm_0][0]; + ids[1] = clip2points[type - edge_0_parm_0][1]; + + // compute which endpoint we are clipping and its interpolant + alpha = (0.0 - triangleParameters[ids[0]]) / + (triangleParameters[ids[1]] - triangleParameters[ids[0]]); + + // interpolate point position to clipping corner + pts[counter] = newPoints->InsertNextPoint( + (1.0 - alpha) * trianglePoints[ids[0]][0] + alpha * trianglePoints[ids[1]][0], + (1.0 - alpha) * trianglePoints[ids[0]][1] + alpha * trianglePoints[ids[1]][1], + (1.0 - alpha) * trianglePoints[ids[0]][2] + alpha * trianglePoints[ids[1]][2]); + break; + case edge_0_parm_1: + case edge_1_parm_1: + case edge_2_parm_1: + ids[0] = clip2points[type - edge_0_parm_1][0]; + ids[1] = clip2points[type - edge_0_parm_1][1]; + + // compute which endpoint we are clipping and its interpolant + alpha = (1.0 - triangleParameters[ids[0]]) / + (triangleParameters[ids[1]] - triangleParameters[ids[0]]); + + // interpolate point position to clipping corner + pts[counter] = newPoints->InsertNextPoint( + (1.0 - alpha) * trianglePoints[ids[0]][0] + alpha * trianglePoints[ids[1]][0], + (1.0 - alpha) * trianglePoints[ids[0]][1] + alpha * trianglePoints[ids[1]][1], + (1.0 - alpha) * trianglePoints[ids[0]][2] + alpha * trianglePoints[ids[1]][2]); + break; + default: + vtkErrorMacro(<< "Invalid value in clipping triangle case: " << triangleCaseNumber); + } + if (++counter == 3) + { + newPolys->InsertNextCell(3, pts); + counter = 0; + } + } + } + } + } + } + // store the fiber surface structure to the output polyData + output->SetPoints(newPoints); + output->SetPolys(newPolys); + return 1; +} diff --git a/Filters/Topology/vtkFiberSurface.h b/Filters/Topology/vtkFiberSurface.h new file mode 100644 index 0000000000000000000000000000000000000000..5635680e257e574733c93cf2ee04f335e6d779a8 --- /dev/null +++ b/Filters/Topology/vtkFiberSurface.h @@ -0,0 +1,400 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkPolyDataAlgorithm.h + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm 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. + +=========================================================================*/ +/** +* @class vtkFiberSurface +* @brief Given a fiber surface control polygon (FSCP) and an +* unstructured grid composed of tetrahedral cells with two scalar arrays, this filter +* computes the corresponding fiber surfaces. +* +* @section Introduction +* Fiber surfaces are constructed from sets of fibers, the multivariate analogues +* of isolines. The original paper [0] offers a general purpose method that produces +* separating surfaces representing boundaries in bivariate fields. This filter is based +* on an improvement over [0] which computes accurate and exact fiber surfaces. It can +* handle arbitrary input polygons including open polygons or self-intersecting polygons. +* The current implementation can better captures sharp features induced by polygon +* bends [1]. +* +* [0] Hamish Carr, Zhao Geng, Julien Tierny, Amit Chattopadhyay and Aaron Knoll, +* Fiber Surfaces: Generalizing Isosurfaces to Bivariate Data, +* Computer Graphics Forum, Volume 34, Issue 3, Pages 241-250, (EuroVis 2015) +* +* [1] Pavol Klacansky, Julien Tierny, Hamish Carr, Zhao Geng, +* Fast and Exact Fiber Surfaces for Tetrahedral Meshes, +* Paper in submission, 2015 +* +* @section Algorithm For Extracting An Exact Fiber Surface +* Require: R.1 A 3D domain space represented by an unstructured grid composed of +* tetrahedral cells +* R.2 Two scalar fields, f1 and f2, that map the domain space to a 2D range +* space. These fields are assumed to be known at vertices of the +* unstructured grid: i.e they are two fields associated with the +* unstructured grid. +* R.3 A Fiber Surface Control Polygon (FSCP) defined in the range space as a +* list of line segments (l1, l2, ..., ln). The FSCP may be an open polyline +* or a self-intersecting polygon. +* +* 1. For each line segment l in FSCP, we ignore the endpoints of the line and assume +* this line extends to infinity. This line will then separate the range and its +* inverse image, i.e fiber surfaces, will also separate the domain. Based on the +* signed distance d between the image of a cell vertex v and line l in the range, +* v can be classified as white (d < 0), grey (d == 0) or black (d>0). The +* interpolation parameter between two vertices v1 and v2 in a cell edge can be +* computed as |d1| / (|d2|+|d1|), where d1 and d2 are the signed distances between +* images of v1,v2 and line l in the range. Once the classification and interpolation +* parameters for all vertices in a cell are known, then we can apply the Marching +* Tetrahedra algorithm. For each tetrahedron, this produces a planar cut which we +* refer to as a base fiber surface. +* +* 2. After generating the base fiber surface in each cell, we need a further clipping +* process to obtain the accurate fiber surface. Clipping is based on classifying the +* vertices of each triangle as follows: +* Given a line segment in the fiber surface control polygon (FSCP) parameterised +* from 0 to 1, we know that every triangle vertex in the base fiber surface belongs +* to the fiber surface, and that the fiber through each vertex can be parameterised +* with respect to the line segment. Therefore, we compute the parameter t for each +* vertex and use it to classify the vertex as: +* 0: t < 0 Vertex is below the clipping range [0,1] and will be removed +* 1: 0 ≤ t ≤ 1 Vertex is inside the clipping range [0,1] and is retained +* 2: 1 < t Vertex is above the clipping range [0,1] and will be removed +* Based on the classification, we can further clip the triangle to obtain the final +* surface. +* +* 3. Repeating steps 1 and 2 for every line segment in FSCP and iterating through each +* cell will generate the final fiber surfaces in the domain. +* +* @section VTK Filter Design +* As stated in part B (R.1), we will compute the fiber surface over an unstructured grid. +* This grid will have to be an input of the VTK filter. The two scalar fields, however, +* are assumed to be stored in the VTK unstructured grid, and will be specified using the +* SetField1() and SetField2() accessors. The FSCP will be passed into the filter as a +* second input port. The data type of FSCP is required to be a vtkPolyData, with each of +* its cell defined as a line segment and its point coordinates defined in the range of +* the bivariate fields of the input grid. +* +* @section Case Tables +* @subsection Marching tetrahedra with grey cases +* In this filter, we have added one vertex classification in Marching Tetrahedra. The +* reason is because we need a grey classification to ensure that surfaces coincident with +* the boundary of the tetrahedra will also be included in the output. Given an iso-value, +* each vertex on the tetrahedron can be classified into three types, including +* equal-(G)rey, below-(W)hite or above-(B)lack the isovalue. +* The notation of the classifications are represented as: +* W or 0 --- below an iso-value +* G or 1 --- equal an iso-value +* B or 2 --- above an iso-value +* The following illustration summarises all of the surface cases (Asterisk * is used to +* highlight the outline of the triangle): +* Case A (no triangles): 0000 +* No triangle is generated. +* +* W +* ... +* . . . +* . . . +* . .W. . +* . . . . +* W...........W +* +* Case B (one grey vertex): 0001, 0010, 0100, 1000 +* Only a vertex is kept, no triangle is generated. +* W +* ... +* . . . +* . . . +* . .G. . +* . . . . +* W...........W +* +* Case C (one grey edge): 0011, 0101, 0110, 1001, 1010, 1100 +* Only an edge is kept, no triangle is generated. +* G +* ... +* . . . +* . . . +* . .G. . +* . . . . +* W...........W +* +* Case D (standard triangle case): 0002, 0020, 0200, 2000 +* One triangle is generated +* W W +* ... ... +* . . . . * . +* . . . . *.* . +* . .B. . -> . * B * . +* . . . . . ** * ** . +* W...........W W...........W +* +* Case E (one grey face): 0111, 1011, 1101, 1110 +* The triangle on one face of the tetra is generated. +* G G +* ... .** +* . . . . * * +* . . . -> . * * +* . .G. . . .G* * +* . . . . . . * * +* W...........G W...........G +* +* Case F (triangle through vertex): 0012 0021 0102 0120 0201 0210 +* 1002 1020 1200 2001 2010 2100 +* A triangle connecting one of the tetra vertex is generated. +* G G +* ... .*. +* . . . .*.*. +* . . . -> . *.* . +* . .B. . . *.B.* . +* . . . . . * * * * . +* W...........W W...........W +* +* Case G (grey tet - treat as empty): 1111 +* No triangle is generated. +* G +* ... +* . . . +* . . . +* . .G. . +* . . . . +* G...........G +* +* Case H (triangle through edge): 0112 0121 0211 1012 1021 1102 +* 1120 1201 1210 2011 2101 2110 +* A triangle containing an edge of the tetra is generated. +* +* G G +* ... ..* +* . . . . . * +* . . . . *. * +* . . . . . * +* . . . -> . * . * +* . . W . . . . W . * +* . . . . . * . * +* . . . . . . * . * +* B.............. G B...............G +* +* Case I (standard quad case): 0022 0202 0220 2002 2020 2200 +* A quand is generated, which can be split to two triangles. +* +* W W +* ... ... +* . . . . . . +* . . . . . . +* . . . * *. * * +* . . . -> .* . *. +* . . W . . . * . W . * . +* . . . . . * * * * . +* . . . . . . . . +* B.............. B B..................B +* +* Case J (complement of Case E): 1112 1121 1211 2111 +* Case K (complement of Case F): 0122 0212 0221 1022 1202 +* 1220 2012 2021 2102 2120 2201 2210 +* Case L (complement of Case C): 1122 1212 1221 2112 2121 2211 +* Case M (complement of Case D): 0222 2022 2202 2220 +* Case N (complement of Case B): 1222 2122 2212 2221 +* Case O (complement of Case A): 2222 +* +* @subsection Clipping cases of the base fiber surface +* After generating the base fiber surface in each cell, we need a further clipping +* process to obtain the accurate fiber surface. Clipping is based on classifying the +* vertices of each triangle to several cases, which will be described in this section. +* In order to keep things consistent, we assume that the vertices are ordered CCW, and +* that edges are numbered according to the opposing vertex: +* +* v0 +* / \ +* e2 e1 +* / \ +* v1---e0---v2 +* +* ======================================================================= +* There are six cases for clipping the base fiber surface (subject to the usual +* symmetries & complementarity) +*------------------------------------------------------------------------ +* Case A (No triangles): Cases 000 & 222 +* Here, the entire triangle is discarded +* +* 000(A): 0 +* . . +* . . +* . . +* . . +* . . +* 0...........0 +* +*------------------------------------------------------------------------ +* Case B (Point-triangle): Cases 001, 010, 100, 122, 212, 221 +* One vertex is kept, and a single triangle is extracted +* +* 001(B): 1 +* / \ +* / \ +* E-----E +* . . +* . . +* 0...........0 +* +*------------------------------------------------------------------------ +* Case C (Edge Quad): Cases 011, 101, 110, 112, 121, 211 +* Two vertices are kept, and a quad is extracted based on the edge +* +* 011(C): 1 +* /|\ +* / | \ +* / | E +* / | / . +* / |/ . +* 1-----E.....0 +* +*------------------------------------------------------------------------ +* Case D (Stripe): Cases 002, 020, 022, 200, 202, 220 +* No vertices are kept, but a stripe across the middle is generated +* +* 022(D): 2 +* . . +* . E +* . /|\ +* . / | E +* . / |/ . +* 2...E---E...0 +* +*------------------------------------------------------------------------ +* Case E (Point-Stripe): Cases 012, 021, 102, 120, 201, 210 +* One vertex is kept, with a stripe through the triangle +* +* 021(E): 1 +* / \ +* E---E +* .|\ |. +* . | \ | . +* . | \| . +* 2...E---E...0 +* +*------------------------------------------------------------------------ +* Case F (Entire): Case 111 +* All three vertices are kept, along with the triangle +* +* 111(F): 1 +* / \ +* / \ +* / \ +* / \ +* / \ +* 1-----------1 +* +* +* @section How to use this filter +* Suppose we have a tetrahedral mesh stored in a vtkUnstructuredGrid, we call this +* data set "inputData". This data set has two scalar arrays whose names are "f1" +* and "f2" respectively. Assume the "inputPoly" is a valid input polygon. Given +* these input above, this filter can be used as follows in c++ sample code: +* +* vtkSmartPointer<vtkFiberSurface> fiberSurface = +* vtkSmartPointer<vtkFiberSurface>::New(); +* fiberSurface->SetInputData(0,inputData); +* fiberSurface->SetInputData(1,inputPoly); +* fiberSurface->SetField1("f1"); +* fiberSurface->SetField2("f2"); +* fiberSurface->Update(); +* +* The filter output which can be called by "fiberSurface->GetOutput()", will be a +* vtkPolyData containing the output fiber surfaces. +*/ + +#ifndef vtkFiberSurface_h +#define vtkFiberSurface_h + +#include "vtkFiltersTopologyModule.h" // For export macro +#include "vtkPolyDataAlgorithm.h" + +class VTKFILTERSTOPOLOGY_EXPORT vtkFiberSurface : public vtkPolyDataAlgorithm +{ +public: + static vtkFiberSurface* New(); + vtkTypeMacro(vtkFiberSurface, vtkPolyDataAlgorithm); + void PrintSelf(ostream& os, vtkIndent indent) VTK_OVERRIDE; + + /** + * Specify the first field name to be used in this filter. + */ + void SetField1(char* fieldName); + + /** + * Specify the second field name to be used in the filter. + */ + void SetField2(char* fieldName); + + /** + * This structure lists the vertices to use for the marching tetrahedra, + * Some of these vertices need to be interpolated, but others are the vertices of + * the tetrahedron already, and for this we need to store the type of vertex. + * bv_not_used: Symbol for an unused entry + * bv_vertex_*: Vertices on a tetrahedron + * bv_edge_*: Vertices on the edges of a tetrahedron + */ + enum BaseVertexType + { + bv_not_used, + bv_vertex_0, + bv_vertex_1, + bv_vertex_2, + bv_vertex_3, + bv_edge_01, + bv_edge_02, + bv_edge_03, + bv_edge_12, + bv_edge_13, + bv_edge_23 + }; + + /** + * After generating the base fiber surface in each cell, we need a further clipping + * process to obtain the accurate fiber surface. Clipping is based on classifying the + * vertices of each triangle, this structure lists the type of vertices to be used for + * the clipping triangles. + * Some of these vertices need to be interpolated, but others are the vertices of + * the triangle already, and for this we need to store the type of vertex. + * Moreover, vertices along the edges of the triangle may be interpolated either at + * parameter value 0 or at parameter value 1. In other words, there are three classes + * of vertex with three choices of each, with a total of nine vertices. We therefore + * store an ID code for each possibility as follows: + */ + enum ClipVertexType + { + not_used, + vertex_0, + vertex_1, + vertex_2, + edge_0_parm_0, + edge_1_parm_0, + edge_2_parm_0, + edge_0_parm_1, + edge_1_parm_1, + edge_2_parm_1, + }; + +protected: + vtkFiberSurface(); + virtual int FillInputPortInformation(int port, vtkInformation* info) VTK_OVERRIDE; + virtual int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) VTK_OVERRIDE; + + // name of the input array names. + char* Fields[2]; + +private: + vtkFiberSurface(const vtkFiberSurface&) VTK_DELETE_FUNCTION; + void operator=(const vtkFiberSurface&) VTK_DELETE_FUNCTION; +}; +#endif diff --git a/Infovis/Core/CMakeLists.txt b/Infovis/Core/CMakeLists.txt index ece8bea78804e9d30e13cb05cc74783c7a1679a2..026fc9062aec6c4e1f4979e90a51699d675850ae 100644 --- a/Infovis/Core/CMakeLists.txt +++ b/Infovis/Core/CMakeLists.txt @@ -5,6 +5,7 @@ set(Module_SRCS vtkArrayToTable.cxx vtkCollapseGraph.cxx vtkCollapseVerticesByArray.cxx + vtkContinuousScatterplot.cxx vtkDataObjectToTable.cxx vtkDotProductSimilarity.cxx vtkExtractSelectedTree.cxx diff --git a/Infovis/Core/Testing/Cxx/CMakeLists.txt b/Infovis/Core/Testing/Cxx/CMakeLists.txt index e8488525317e903829eb7088ba6f9aebe724006a..14b9e40772446d2d491207d16cbcda48c7924c95 100644 --- a/Infovis/Core/Testing/Cxx/CMakeLists.txt +++ b/Infovis/Core/Testing/Cxx/CMakeLists.txt @@ -12,6 +12,7 @@ vtk_add_test_cxx(${vtk-module}CxxTests tests ArrayTransposeMatrix.cxx,NO_VALID TestArrayNorm.cxx,NO_VALID,NO_DATA TestCollapseVerticesByArray.cxx,NO_VALID + TestContinuousScatterPlot.cxx,NO_VALID TestDataObjectToTable.cxx,NO_VALID TestExtractSelectedTree.cxx,NO_VALID TestExtractSelectedGraph.cxx,NO_VALID diff --git a/Infovis/Core/Testing/Cxx/TestContinuousScatterPlot.cxx b/Infovis/Core/Testing/Cxx/TestContinuousScatterPlot.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d63ed183ff5b8de1c4ebd51a5c4d261dbc67c618 --- /dev/null +++ b/Infovis/Core/Testing/Cxx/TestContinuousScatterPlot.cxx @@ -0,0 +1,264 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkPolyDataAlgorithm.h + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm 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. + +=========================================================================*/ + +// PURPOSE: test new vtkContinuousScatterplot filter +#include <cstring> +#include <sstream> +#include <string> + +#include "vtkContinuousScatterplot.h" +#include "vtkTestUtilities.h" +#include <vtkActor.h> +#include <vtkImageActor.h> +#include <vtkImageData.h> +#include <vtkImageMapper3D.h> +#include <vtkImageWriter.h> +#include <vtkJPEGWriter.h> +#include <vtkPolyData.h> +#include <vtkPolyDataMapper.h> +#include <vtkRenderWindow.h> +#include <vtkRenderWindowInteractor.h> +#include <vtkRenderer.h> +#include <vtkSmartPointer.h> +#include <vtkUnstructuredGrid.h> +#include <vtkXMLImageDataWriter.h> +#include <vtkXMLUnstructuredGridReader.h> + +int TestContinuousScatterPlot(int argc, char* argv[]) +{ + std::string outputString; + bool pass = true; + + char* inputFile = + vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/cube.vtu"); + + int ResX[5] = { 10, 20, 30, 40, 50 }; + int ResY[5] = { 10, 20, 30, 40, 50 }; + + char fieldOneName[] = "f1"; + char fieldTwoName[] = "f2"; + + int cmpIndex = 0; + + /********************* Desired output arrays *********************/ + std::string dataToCompare[5] = { "2,1,0,0,0,0,0,0,0,0,2,17,9,0,0,0,0,0,0,0,0,12,44,28,0,0,0,0,0," + "0,0,0,40,81,64,0,0,0,0,0,0,0,0,42,137,112,0,0,0,0,0,0,0,0,48," + "181,159,0,0,0,0,0,0,0,0,46,255,215,0,0,0,0,0,0,0,0,34,208,152," + "0,0,0,0,0,0,0,0,18,157,55,0,0,0,0,0,0,0,0,5,61", + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,8,4,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,17,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,29,18,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,19,46,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,67,51,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,37,102,64,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,113,64,3,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,47,105,86,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,126,62,1,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,50,120,167,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,255,228,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,42,250,222,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,225,197,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,26,183,157,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,134,108,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,11,97,56,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,47,14,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,1,16", + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,7," + "3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,13,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,21,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "16,30,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,46,29,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,67,40,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,41,94,45,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,58,92,44,4,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,99,33,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,52,85,42,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,56,95,44,23," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,59,104,65,28,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,113,81,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,61,121,97,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,129,118,82,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,58,135,156,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,54,140,161,75,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,143," + "150,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,146,237,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,255,225,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,29,209,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,168,153,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,126,113,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,11,90,69,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,54," + "31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,27,17,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,7,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,12,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,10,19,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,15,30,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,19,43,23,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26," + "55,31,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,64,29,2," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,74,29,4,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,49,78,36,7,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,55,76,46,12,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,42,86,36,28,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,44,70,53,36,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,47,60,64,34,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,1,28,64,63,37,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,1,27,68,71,48,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,27,116,86,60,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,74,123,120,78,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "75,144,143,34,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,75," + "147,141,39,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,74,165," + "156,163,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,170,252," + "189,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,143,255,191," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,63,145,255,181,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,144,244,159,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,53,143,222,133,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,46,142,193,92,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,40,141,163,48,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,33,140,220,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,217,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,171,162,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,132,131,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,12,94,85,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,8,63,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,4,38,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2," + "24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,0", + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,15,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,22,11,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,29,15,1,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,37," + "15,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,22,42,19,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,26,47,21,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,56,21,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,39,56,26,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,43,64,33,12,1,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,48,60,41,26,1,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,38,67,55,32,2,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,40," + "71,48,38,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,5,41,58,57,37,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,6,42,62,69,53,5,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,43,79,81,28,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,45,67,73,18,4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,27,69,73,22,6,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,25,70,89,28,7,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,24,81,96,34," + "14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "2,22,84,103,46,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,1,21,85,88,55,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,66,95,65,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,111,102,88,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65,111,109,94,43,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,62,111,117,95,35," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "59,111,124,89,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,55,110,132,193,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,53,109,255,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,48,106,231,178,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,104,214,158,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,102,191," + "130,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,33,99,168,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,29,99,178,69,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,132,121,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,94,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,141,159,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,124,109,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,79," + "77,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,7,55,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,5,37,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,3,22,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,11,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0," + "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" }; + + /************** output to error log ****************/ + /****************************************************/ + + /****************************************************/ + + for (int i = 0; i < 5; i++) + { + + // read .vtu file from the the command line + vtkSmartPointer<vtkXMLUnstructuredGridReader> mesh_reader = + vtkSmartPointer<vtkXMLUnstructuredGridReader>::New(); + mesh_reader->SetFileName(inputFile); + mesh_reader->Update(); + + // Define the CSP filter. + vtkSmartPointer<vtkContinuousScatterplot> csp = + vtkSmartPointer<vtkContinuousScatterplot>::New(); + csp->SetInputData(mesh_reader->GetOutput()); + csp->SetField1(fieldOneName, ResX[cmpIndex]); + csp->SetField2(fieldTwoName, ResY[cmpIndex]); + csp->Update(); + + /************** Console output ****************/ + { + std::stringstream ss; + int indexLocal = 0; + + // Retrieve the entries from the image data + for (int z = 0; z < 1; z++) + { + for (int y = 0; y < ResY[cmpIndex]; y++) + { + for (int x = 0; x < ResX[cmpIndex]; x++) + { + double* pixel = static_cast<double*>(csp->GetOutput()->GetScalarPointer(x, y, z)); + if (indexLocal > 0) + { + outputString.append(","); + } + ss << (int)(pixel[0] + 0.5); + outputString.append(ss.str()); + ss.str(std::string()); + ss.clear(); + indexLocal++; + } + } + } + + } // end of console output + + /*********** comparing the strings ************/ + if (strcmp(outputString.c_str(), dataToCompare[cmpIndex].c_str()) != 0) + { + pass = false; + { // writing to file + printf("\n\n/**************************************/"); + printf("\n/********Test Unsuccessful*************/"); + printf("\nInput Data: %s", (std::string(inputFile).substr(6, 49)).c_str()); + printf("\nCS Plot test case %d x %d \n", ResX[cmpIndex], ResY[cmpIndex]); + printf("\nString to compare: %s", dataToCompare[cmpIndex].c_str()); + printf("\nOutput String : %s", outputString.c_str()); + printf("\n/**************************************/"); + } // end of writing + } + + cmpIndex++; + outputString = ""; + + } // end for + + delete [] inputFile; + + if (pass) + { + cout << "\nTest Successful!!!" << endl; + return EXIT_SUCCESS; + } + + cout << "\nTest Unsuccessful." << endl; + return EXIT_FAILURE; +} diff --git a/Infovis/Core/vtkContinuousScatterplot.cxx b/Infovis/Core/vtkContinuousScatterplot.cxx new file mode 100644 index 0000000000000000000000000000000000000000..5534b1baf45d6ecc3b5d173ccb4de9ec297378ad --- /dev/null +++ b/Infovis/Core/vtkContinuousScatterplot.cxx @@ -0,0 +1,957 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkPolyDataAlgorithm.h + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm 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 "vtkContinuousScatterplot.h" + +#include "vtkCell.h" +#include "vtkCellArray.h" +#include "vtkCellData.h" +#include "vtkCellType.h" +#include "vtkDataSetAttributes.h" +#include "vtkExecutive.h" +#include "vtkFloatArray.h" +#include "vtkIdList.h" +#include "vtkIdTypeArray.h" +#include "vtkImageData.h" +#include "vtkInformation.h" +#include "vtkInformationVector.h" +#include "vtkMassProperties.h" +#include "vtkMath.h" +#include "vtkMergePoints.h" +#include "vtkObjectFactory.h" +#include "vtkPointData.h" +#include "vtkPoints.h" +#include "vtkPolyData.h" +#include "vtkPolygon.h" +#include "vtkSmartPointer.h" +#include "vtkTriangleFilter.h" +#include "vtkUnstructuredGrid.h" + +vtkStandardNewMacro(vtkContinuousScatterplot); + +// Data structure to store the fragment faces. +// Each face of the fragment can be represented using a vtkIdList. +typedef std::vector<vtkSmartPointer<vtkIdList> >* Polytope; + +//---------------------------------------------------------------------------- +vtkContinuousScatterplot::vtkContinuousScatterplot() +{ + // value for floating comparison. Suppose two floating values a and b: + // if fabs(a-b) <= dEpsilon : then a == b. + // if (a-b) > dEpsilon : then a > b. + // if (b-a) > dEpsilon : then b > a. + this->Epsilon = 1.0e-6; + // The number of output port is one. + this->SetNumberOfOutputPorts(1); + // Resolution of the output image is set to 100 as default. + this->ResX = this->ResY = 100; + // Create the vtkImageData object and pass to the output port. + vtkImageData* output = vtkImageData::New(); + this->GetExecutive()->SetOutputData(0, output); + output->Delete(); + this->Fields[0] = this->Fields[1] = NULL; +} +//---------------------------------------------------------------------------- +void vtkContinuousScatterplot::SetField1(char* nm, vtkIdType xRes) +{ + this->Fields[0] = nm; + this->ResX = xRes; +} +//---------------------------------------------------------------------------- +void vtkContinuousScatterplot::SetField2(char* nm, vtkIdType yRes) +{ + this->Fields[1] = nm; + this->ResY = yRes; +} +//---------------------------------------------------------------------------- +void vtkContinuousScatterplot::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); +} +//---------------------------------------------------------------------------- +int vtkContinuousScatterplot::FillInputPortInformation(int, vtkInformation* info) +{ + info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkUnstructuredGrid"); + return 1; +} +//---------------------------------------------------------------------------- +int vtkContinuousScatterplot::FillOutputPortInformation(int, vtkInformation* info) +{ + info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkImageData"); + return 1; +} +//---------------------------------------------------------------------------- +int vtkContinuousScatterplot::RequestData( + vtkInformation*, vtkInformationVector** inputVector, vtkInformationVector* outputVector) +{ + // get the input and output ports information. + vtkInformation* inInfo = inputVector[0]->GetInformationObject(0); + vtkInformation* outInfo = outputVector->GetInformationObject(0); + + // get the input data set, which is required to be a vtkUnstructuredGrid. + vtkUnstructuredGrid* input = + vtkUnstructuredGrid::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT())); + // get the output data set, which is required to be a vtkImageData. + vtkImageData* output = vtkImageData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); + + // get the number of points and cells of the input grid. + vtkIdType numCells = input->GetNumberOfCells(); + vtkIdType numPts = input->GetNumberOfPoints(); + + // get the dataset statistics and check if it is not an empty grid. + if (numCells < 1 || numPts < 1) + { + vtkErrorMacro("No input data."); + return 1; + } + + // check if the output image resolution is a valid number. + if (this->ResX <= 0 || this->ResY <= 0) + { + vtkErrorMacro("The resolution of the output image has to be a positive number."); + return 1; + } + + // check if the names of the input scalar fields are specified. + if (!this->Fields[0] || !this->Fields[1]) + { + vtkErrorMacro("At least two fields need to be specified."); + return 1; + } + + // Input point data, which should include the arrays defining the range space. + vtkDataSetAttributes* inPD = input->GetPointData(); + // current data array of the given array names from the user input. + vtkSmartPointer<vtkDataArray> array; + // scalar range in two fields, where fieldInterval[0] = f1_max - f1_min, + // fieldInterval[1] = f2_max - f2_min + float fieldInterval[2]; + + // fragment width of two fields, where fragWidth[0] = fieldInterval[0] / resX, + // fragWidth[0] = fieldInterval[1] / resY, (resX, resY) is the output image + // resolution. + float fragWidth[2]; + + // Collect the first scalar field based on array names, + array = inPD->GetArray(this->Fields[0]); + // confirming that this field is present in the input. + if (array) + { + // Collect field ranges for later use. + fieldInterval[0] = array->GetRange()[1] - array->GetRange()[0]; + // Interval between cutting planes in this field. + fragWidth[0] = (float)fieldInterval[0] / this->ResX; + } + else + { + vtkErrorMacro("Array not found in input point data: " << this->Fields[0]); + return 1; + } + + // Collect the second field data array. + array = inPD->GetArray(this->Fields[1]); + // confirming that this field is present in the input. + if (array) + { + // The range interval of the field. + fieldInterval[1] = array->GetRange()[1] - array->GetRange()[0]; + // Interval between cutting planes in this field. + fragWidth[1] = (float)fieldInterval[1] / this->ResY; + } + else + { + vtkErrorMacro("Array not found in input point data: " << this->Fields[1]); + return 1; + } + + // Devide the tetrahedron into four faces. The index of each face. + const int tetTemplate[4][3] = { { 0, 1, 2 }, { 0, 1, 3 }, { 0, 2, 3 }, { 1, 2, 3 } }; + + // fragments of current cell: each cell is placed into the inputQ, + // then fresh fragments from current slice put into outputQ. + std::vector<Polytope> inputQ = std::vector<Polytope>(); + std::vector<Polytope> outputQ = std::vector<Polytope>(); + + // structure for storing the vertices of cutting planes used to subdivide the cell. + vtkSmartPointer<vtkIdList> cut = vtkSmartPointer<vtkIdList>::New(); + + // Initially the points in the cutting plane are not ordered. These parameters are used + // to compute convex hull for points lying on the cutting plane. The sorting process + // utilizes the angular information between the plane edges. + float theta[100]; + vtkIdType index[100]; + // number of points in the current cutting plane. + int nrPoints; + float base[3], vec[3]; + float baseNorm, ang; + vtkIdType pnt, pnt0, pnt1; + + // min and max scalar value of the current field in a tetrahedron + float maxCell, minCell; + // min and max scalar values of the current field in the whole domain. + float minField = 0; + float maxField = 0; + + // first and last cutting planes' field values. This threshold is used to traverse + // through all of the cutting planes in the range. + float initThreshold, lastThreshold; + + // variables for walking around a cell face. + vtkIdType newPointId, thisPointId, prevPointId; + // number of the vertices in the current cell face. + int nrFaceIds; + + // For each cutting plane, determine its intersecting position along the current edge. + int mask; + + // scalar values of the end points of the current edge. + float thisScalar, prevScalar; + + // if the cutting plane intersects in the middle of the given edge, these parameters + // are used to compute these intersecting points using linear interpolation + double delta, t, p[3], p0[3], p1[3], p2[3]; + + // fragment: list of faces forming the fragments in the current cell. + // residual: list of faces forming the rest of the cell (except fragments). + // working: collect the residual faces for the next iteration of subdivision. + Polytope fragment = NULL, residual = NULL, working = NULL; + + // structure for storing the current framgent vertices in each cell face. + vtkSmartPointer<vtkIdList> fragmentFace = NULL; + // structure for storing the vertices which are not belonging to the current + // fragment in each cell face. + vtkSmartPointer<vtkIdList> residualFace = NULL; + + // reading/writing cells from input, output and cell arrays. + vtkSmartPointer<vtkIdList> cell = NULL; + // reading/writing faces from input, output and cell arrays. + vtkSmartPointer<vtkIdList> face = NULL; + + // Each fragment can be mapped to a 2-D bin based on its range values. The density + // value in each bin equals to the total geometric volume of the fragments in this bin. + // The following structure creates and initialise such a 2-D bin with a resolution + // ResX * ResY. + // float imageBin[this->ResX][this->ResY]; + float** imageBin = new float*[this->ResX]; + for (int xIndex = 0; xIndex < this->ResX; ++xIndex) + { + imageBin[xIndex] = new float[this->ResY]; + for (int yIndex = 0; yIndex < this->ResY; ++yIndex) + { + imageBin[xIndex][yIndex] = 0.0; + } + } + // maximal volume value in the output image bin. + float maxBinSize = 0; + + // The VTK filter for computing the fragment geometric volume. This filter only + // accepts triangular mesh as input, while our fragments are stored as a polygonal + // mesh. Therefore, we need a mesh conversion. + vtkSmartPointer<vtkMassProperties> volume = vtkSmartPointer<vtkMassProperties>::New(); + // This VTK filter convert a polygonal mesh to a triangular mesh. + vtkSmartPointer<vtkTriangleFilter> triangleFilter = vtkSmartPointer<vtkTriangleFilter>::New(); + // geometric volume of the current polyhedral fragments. + float fragVolume; + + // mapped index of a fragment into the 2-D bin. + vtkIdType binIndexFirst = 0, binIndexSecond = 0; + + // id for the newly interpolated fragment vertices. Since we only need to update point + // structure, therefore this id is not of interest and can be ignored for the moment. + vtkIdType ignored = 0; + // point coordinates in the input grid + vtkPoints* inputGridPoints = input->GetPoints(); + // point coordinates in the output fragments + vtkSmartPointer<vtkPoints> newPoints = vtkSmartPointer<vtkPoints>::New(); + + // Each fragment is composed of a number of points. This data structure contains + // scalar values for all these points. + vtkSmartPointer<vtkDataSetAttributes> newPointsPD = vtkSmartPointer<vtkDataSetAttributes>::New(); + + // locator for inserting new interpolated points into the structure. This search + // structure ensures that redundant points will not be added to the point collection. + vtkSmartPointer<vtkMergePoints> pointLocator = vtkSmartPointer<vtkMergePoints>::New(); + + // Each fragment will have a range value. This array records the range values + // for all of the fragments. Since we iteratively subdivide the cell based on + // two fields, therefore the first n elements in this array record the + // range values of the fragments in the first field subdivision, and the following m + // elements record the range values in the second field subdivision. + vtkSmartPointer<vtkFloatArray> fragScalar = vtkSmartPointer<vtkFloatArray>::New(); + + // Each fragment will have range values from two fields. + float fragRangeValue[2]; + // number of fragments in the first field subdivision. + size_t firstFragNr = 0; + + // data structure containing scalar values of the vertices of current tetrahedron of + // the input grid. + vtkSmartPointer<vtkDataSetAttributes> tetraPD = vtkSmartPointer<vtkDataSetAttributes>::New(); + + // scalar values of two fields in the current tetrahedron of the input grid. + // these two arrays are added to the tetrahedron point data. + vtkSmartPointer<vtkFloatArray> tetraF1 = vtkSmartPointer<vtkFloatArray>::New(), + tetraF2 = vtkSmartPointer<vtkFloatArray>::New(); + tetraF1->SetNumberOfComponents(1); + tetraF1->SetNumberOfTuples(4); + tetraF2->SetNumberOfComponents(1); + tetraF2->SetNumberOfTuples(4); + // add the two scalar arrays to the point data of the tetrahedron + tetraPD->AddArray(tetraF1); + tetraPD->AddArray(tetraF2); + + // structure containing the polygonal faces of polyhedral fragments + vtkSmartPointer<vtkPolyData> polyhedra = vtkSmartPointer<vtkPolyData>::New(); + + // estimate the total number of cells of the fragments in each input cell. + // consider edge can be maximally subdivided to resX number + // of fragments for the first field and resY number of fragments for the second field. + // In total, there will be maximal resX * resY number of new points in each edge. + int estOutputPointSize = this->ResX * this->ResY * 4; + // Allocate the memory for the framgent points. + newPoints->Allocate(estOutputPointSize); + + // main loop ... + // For each tetrahedron in a gird + for (vtkIdType tetraIndex = 0; tetraIndex < input->GetNumberOfCells(); tetraIndex++) + { + // current tetrahedron vertex list. + cell = vtkSmartPointer<vtkIdList>::New(); + input->GetCellPoints(tetraIndex, cell); + + // Test if the current cell is a tetrahedron or not. If not, ignore this cell. + if (input->GetCellType(tetraIndex) != VTK_TETRA || cell->GetNumberOfIds() != 4) + { + vtkWarningMacro("Current cell " << tetraIndex << " is not of a tetrahedron type."); + continue; + } + + // initialise the point structure for storing fragment vertices. + newPoints->Reset(); + // initialise the search structure for new points insertion. + pointLocator->InitPointInsertion(newPoints, input->GetCell(tetraIndex)->GetBounds()); + + // initialise data structure containing the scalar values for the fragment vertices. + newPointsPD->Initialize(); + // Set the storage for interpolating the field values of the fragment vertices. + newPointsPD->InterpolateAllocate(tetraPD, estOutputPointSize, ignored, false); + newPointsPD->CopyScalarsOn(); + + // initialise data structure containing the scalar values of the whole fragment. + fragScalar->Initialize(); + fragScalar->Allocate(this->ResX * this->ResY); + // two components are needed to store the bivariate fields of the framgent. + fragScalar->SetNumberOfComponents(2); + + // Initialise the scalar values in this tetrahedral cell. + for (vtkIdType cellIndex = 0; cellIndex < cell->GetNumberOfIds(); cellIndex++) + { + int pointId = cell->GetId(cellIndex); + pointLocator->InsertNextPoint(inputGridPoints->GetPoint(pointId)); + tetraF1->SetComponent( + cellIndex, 0, inPD->GetArray(this->Fields[0])->GetComponent(pointId, 0)); + tetraF2->SetComponent( + cellIndex, 0, inPD->GetArray(this->Fields[1])->GetComponent(pointId, 0)); + } + + // The scalar values of the framgent points are based on the interpolation of the + // point data of the tetrahedral cell (tetraPD). + for (vtkIdType cellPDIndex = 0; cellPDIndex < tetraPD->GetNumberOfTuples(); cellPDIndex++) + { + newPointsPD->CopyData(tetraPD, cellPDIndex, cellPDIndex); + } + + /* + cout << "ARRAY SUMMARY:\n" << endl; + for (int i = 0; i < tetraPD->GetNumberOfTuples(); i++) + { + cout << i << "\t" << inPD->GetArray(this->Fields[0])->GetComponent(i,0) + << "\t" << inPD->GetArray(this->Fields[1])->GetComponent(i,0) + << "\t" << newPointsPD->GetArray(0)->GetComponent(i,0) + << "\t" << newPointsPD->GetArray(1)->GetComponent(i,0) << endl; + + } + + */ + // Get the next cell from input, and place into the working queue. + // We place into outputQ as each field takes the output of the + // last step as its input, swapping queues BEFORE processing. + // OutputQ : a list of faces of the output framgent + Polytope ptp = new std::vector<vtkSmartPointer<vtkIdList> >(); + outputQ.push_back(ptp); + + // min and max range value of the current cell + minCell = maxField; + maxCell = minField; + + // for each point in the cell + for (int fnr = 0; fnr < 4; fnr++) + { + // get the faces of the cell and push into outputQ structure. + face = vtkSmartPointer<vtkIdList>::New(); + face->SetNumberOfIds(3); + for (int pnr = 0; pnr < 3; pnr++) + { + face->SetId(pnr, tetTemplate[fnr][pnr]); + } + outputQ[0]->push_back(face); + } + + // For each scalar field: + for (size_t fieldNr = 0; fieldNr < 2; fieldNr++) + { + // Swap role of outputQ and inputQ + std::swap(outputQ, inputQ); + outputQ.clear(); + + // If this is the second round subdivision, then we need to record the number + // of fragments produced in the first round. This number is used to locate + // the range values of the output fragment. + if (fieldNr == 1) + { + firstFragNr = inputQ.size(); + } + + // min value of the whole field + minField = inPD->GetArray(this->Fields[fieldNr])->GetRange()[0]; + // max value of the whole field + maxField = inPD->GetArray(this->Fields[fieldNr])->GetRange()[1]; + // Initialize the value + minCell = maxField; + maxCell = minField; + + ////cout << "tet " << tetraIndex << ", field " << fieldNr << ", pnr: " << + ///newPointsPD->GetNumberOfTuples() << endl; + ////cout << "min/max init: " << minCell << ", " << maxCell << endl; + + // obtain the minimal and maximal scalar values of the cell. + for (int pnr = 0; pnr < newPointsPD->GetNumberOfTuples(); pnr++) + { + double fval = newPointsPD->GetArray((int)fieldNr)->GetComponent(pnr, 0); + + ////cout << " fval: " << fval << endl; + if (maxCell < fval) + { + maxCell = fval; + } + if (minCell > fval) + { + minCell = fval; + } + } + + ////cout << "D" << endl; + ////cout << "Cell min/max: " << minCell << " " << maxCell << endl; + ////cout << "field min/max[0] " << minField << " " << maxField << endl; + ////cout << "field widths " << fragWidth[0] << " " << fragWidth[1] << endl; + + // in each field, the smallest threshold of cutting plane to start with. + // since each field is sliced uniformly, in other words, the interval between + // fragment in each field is a constant. Therefore in each cell, the smallest + // threshold to start with, should be the first fragment value of the field which + // is greater or equal to the minimal range value of the cell. + initThreshold = + minField + (1 + floor((minCell - minField) / fragWidth[fieldNr])) * fragWidth[fieldNr]; + + // Iterate through the faces of the current mesh. + // 1. The first field is initially traversed and processed on the input grid. + // The inputQ should be the initial tetrahedron mesh. And the outputQ should be + // the collection of computed fragments with respect to the first field. + // 2. The second field is then processed based on the outputQ of step 1. The inputQ + // now becomes the fragments in the first field subdivision. The OutputQ + // in this step should be the fragments of the bivariate fields. + // Fragment: array records the faces of the fragments. + // Residual: array records the faces of rest of the cell in addition to fragments. + // Cut: array records the vertices of the current cutting plane. + // For each faces of the input mesh. + for (size_t cp = 0; cp < inputQ.size(); cp++) + { + // working[0] : faces of current input mesh. + working = inputQ[cp]; + + // initialise the first and last cutting planes in this cell. + lastThreshold = initThreshold; + + // Traverse from the minimal to the maximal scalar values in a cell, every time + // the threshold is increased by one fragmentWidth + for (double threshold = initThreshold; threshold < maxCell; threshold += fragWidth[fieldNr]) + { + // Initialise framgent face structure for the current cutting plane. + if (fragment) + { + delete fragment; + } + if (residual) + { + delete residual; + } + fragment = new std::vector<vtkSmartPointer<vtkIdList> >(); + residual = new std::vector<vtkSmartPointer<vtkIdList> >(); + + // Create the new cutting plane. + cut = vtkSmartPointer<vtkIdList>::New(); + + // Effectively, we start processing a new cell at this point. + for (std::vector<vtkSmartPointer<vtkIdList> >::iterator faceIt = working->begin(); + faceIt != working->end(); faceIt++) + { + fragmentFace = vtkSmartPointer<vtkIdList>::New(); + residualFace = vtkSmartPointer<vtkIdList>::New(); + + // number of points in the current cell face + nrFaceIds = (*faceIt)->GetNumberOfIds(); + + // get the previous point id in the face + prevPointId = (*faceIt)->GetId(nrFaceIds - 1); + // the scalar value of the previous point in the face + prevScalar = newPointsPD->GetArray( + (int)fieldNr)->GetComponent(prevPointId, 0); + + // Walk around the edge, comparing the range values between the current + // cutting plane and the edge end points. Classify the each end point of the + // edge into three classes based on the comparison result. Three classes + // includes: + // fragmentFace: if current edge point belongs to the fragment. + // cut: if current edge point belongs to the cutting plane. + // residual: if current edge point belongs to neither of the above classes. + // For each edge + for (int i = 0; i < nrFaceIds; i++) + { + // get the current point Id in the face + thisPointId = (*faceIt)->GetId(i); + // get scalar value of the current point + thisScalar = newPointsPD->GetArray( + (int)fieldNr)->GetComponent(thisPointId, 0); + + ////cout << ">>> " << thisPointId << " " << thisScalar << " " << prevPointId << " " + ///<< prevScalar << endl; + + // zero bitweise or to any value equals that value + // threshold = minField + n * sw + mask = 0; + // thisScalar < threshold (0001) + if (threshold - thisScalar > this->Epsilon) + mask |= 1; + // thisScalar > threshold (0010) + if (thisScalar - threshold > this->Epsilon) + mask |= 2; + // prevScalar < threshold (0100) + if (threshold - prevScalar > this->Epsilon) + mask |= 4; + // prevScalar > threshold (1000) + if (prevScalar - threshold > this->Epsilon) + mask |= 8; + // thisScalar == threshold (0011) + if (fabs(thisScalar - threshold) <= this->Epsilon) + mask |= 3; + // prevScalar == threshold (1100) + if (fabs(prevScalar - threshold) <= this->Epsilon) + mask |= 12; + switch (mask) + { + // threshold == thisScalar + case 3: + case 7: + // threshold == thisScalar, + // threshold < preScalar + case 11: + // threshold == thisScalar + // threshold == preScalar + case 15: + // fragment face array insert current point + fragmentFace->InsertNextId(thisPointId); + // residual face array insert current point + residualFace->InsertNextId(thisPointId); + // if the current point is not in the cut array + if (cut->IsId(thisPointId) < 0) + { + cut->InsertNextId(thisPointId); + } + break; + // threshold == thisScalar + // threshold > preScalar + case 5: + // threshold > thisScalar + // threshold == prevScalar + case 13: + fragmentFace->InsertNextId(thisPointId); + break; + // threshold < thisScalar + // threshold < prevScalar + case 10: + // threshold < thisScalar + // threshold == prevScalar + case 14: + residualFace->InsertNextId(thisPointId); + break; + // threshold > thisScalar + // threshold < preScalar + case 9: + // Moving to this point crosses the threshold hi-lo + // Insert interpolated point into both. + delta = prevScalar - thisScalar; + t = (threshold - thisScalar) / delta; + // * PREV ------- T -------- THIS * + newPoints->GetPoint(thisPointId, p1); + newPoints->GetPoint(prevPointId, p2); + for (int j = 0; j < 3; j++) + { + p[j] = p1[j] + t * (p2[j] - p1[j]); + } + if (pointLocator->InsertUniquePoint(p, newPointId)) + { + newPointsPD->InterpolateEdge( + newPointsPD, newPointId, thisPointId, prevPointId, t); + } + fragmentFace->InsertNextId(newPointId); + fragmentFace->InsertNextId(thisPointId); + residualFace->InsertNextId(newPointId); + // We have found a cut point, add it if first visit. + if (cut->IsId(newPointId) < 0) + { + cut->InsertNextId(newPointId); + } + break; + // threshold < thisScalar + // threshold > preScalar + case 6: + // this >, prev < + // Moving to this point crosses the threshold lo-hi + // Insert interpolated point into both. + delta = thisScalar - prevScalar; + t = (threshold - prevScalar) / delta; + newPoints->GetPoint(thisPointId, p1); + newPoints->GetPoint(prevPointId, p2); + for (int j = 0; j < 3; j++) + { + p[j] = p2[j] + t * (p1[j] - p2[j]); + } + if (pointLocator->InsertUniquePoint(p, newPointId)) + { + newPointsPD->InterpolateEdge( + newPointsPD, newPointId, prevPointId, thisPointId, t); + } + fragmentFace->InsertNextId(newPointId); + residualFace->InsertNextId(newPointId); + residualFace->InsertNextId(thisPointId); + if (cut->IsId(newPointId) < 0) + { + cut->InsertNextId(newPointId); + } + break; + default: + vtkErrorMacro("Incomparable scalars " << prevScalar << ", " << thisScalar << ", " + << threshold); + } // switch mask + prevPointId = thisPointId; + prevScalar = thisScalar; + } // for-each edge + // Output fragment and residue into new cells, as appropriate. + // Test if the fragment and residual faces are well defined. + if (fragmentFace->GetNumberOfIds() > 2) + { + fragmentFace->Squeeze(); + fragment->push_back(fragmentFace); + } + if (residualFace->GetNumberOfIds() > 2) + { + residualFace->Squeeze(); + residual->push_back(residualFace); + } + } // for each face. + + // We need to compute the face defined by the cut points. + // We cannot guarantee that points in the cut-list are ordered wrt + // polygon boundary, so we recompute an order by effectively working + // the convex hull. + // The cut-list is added to the framgent and residual array. + if (cut->GetNumberOfIds() > 2) + { + nrPoints = cut->GetNumberOfIds(); + pnt0 = cut->GetId(0); + pnt1 = cut->GetId(1); + // 2. Compute vector from p[0] to p[1]. + newPoints->GetPoint(pnt0, p0); + newPoints->GetPoint(pnt1, p1); + for (int i = 0; i < 3; i++) + { + base[i] = p1[i] - p0[i]; + } + baseNorm = vtkMath::Norm(base); + theta[0] = 0.0; + index[0] = pnt1; + // 3. For the remaining points p, compute angle between p-p0 and base. + for (int i = 2; i < nrPoints; i++) + { + newPoints->GetPoint(cut->GetId(i), p1); + for (int j = 0; j < 3; j++) + { + vec[j] = p1[j] - p0[j]; + } + theta[i - 1] = acos(vtkMath::Dot(base, vec) / (baseNorm * vtkMath::Norm(vec))); + index[i - 1] = cut->GetId(i); + } + // 4. Sort angles. + for (int j = 1; j < nrPoints - 1; j++) + { + ang = theta[j]; + pnt = index[j]; + int i = j - 1; + while (i >= 0 && theta[i] > ang) + { + theta[i + 1] = theta[i]; + index[i + 1] = index[i]; + i--; + } + theta[i + 1] = ang; + index[i + 1] = pnt; + } + cut->Reset(); + cut->InsertNextId(pnt0); + for (int i = 0; i < nrPoints - 1; i++) + { + cut->InsertNextId(index[i]); + } + fragment->push_back(cut); + residual->push_back(cut); + } // Generate cut plane. // Generate cut plane. + + // OUTPUT PHASE ----------------------------------------- + // Have completed traversing each face. + // Now update output with new fragment. + // Iterate through each edge and record for each fragment, which are the + // divided points included. + // ------------------------------------------------------ + if (fragment->size() > 3) + { + // add the current framgent to the outpuQ structure. + outputQ.push_back(fragment); + fragment = NULL; + // set threshold at which this fragment was created + if (fieldNr) + { + // if this is the second field subdivision, we need to retrieve + // the scalar value from the first field subdivision first. And insert the + // retrieved first field value together with the current second field value + // into the fragment scalar array. + float firstFieldRange = fragScalar->GetComponent((vtkIdType)cp, 0); + fragScalar->InsertNextTuple2(firstFieldRange, threshold); + } + else + { + // if this is the first field subdivisions, just insert the scalar value + // into the array. + fragScalar->InsertNextTuple2(threshold, 0); + } + } + else + { + // Remove any partial fragments. + while (fragment->size() > 0) + { + fragment->pop_back(); + } + } + // Faces defining the next working polyhedron are in the residual array. + std::swap(working, residual); + // Clear out anything left in residual + while (residual->size() > 0) + { + residual->pop_back(); + } + lastThreshold += fragWidth[fieldNr]; // Track final threshold + } // for each threshold + + // If we are left with a residual, add it as a fragment. + // However, note that final step in loop is to swap residual + // with working, so need to look in the working list. + if (working->size() > 3) + { + outputQ.push_back(working); + // set threshold at which this fragment was created. + if (fieldNr) + { + // if this is the second field subdivision, we need to retrieve + // the scalar value from the first field subdivision first. And insert the + // retrieved first field value together with the current second field value + // into the fragment scalar array. + float firstFieldRange = fragScalar->GetComponent((vtkIdType)cp, 0); + fragScalar->InsertNextTuple2(firstFieldRange, lastThreshold); + } + else + { + // if this is the first field subdivisions, just insert the scalar value + // into the array. + fragScalar->InsertNextTuple2(lastThreshold, 0); + } + } + else + { + while (working->size() > 0) + { + working->pop_back(); + } + + if(working) + { + delete working; + working = nullptr; + } + } + } // for each fragment of cell: faces, edges. + // CutIds are the edges with current dividing fragments. + } // for each field + + // OUTPUT PHASE: ---------------------------------------------- + // Output generated fragments into main output dataset. + // ------------------------------------------------------------ + // For each output framgent, we compute its geometric volume and aggregate over + // the cells. + for (size_t co = 0; co < outputQ.size(); co++) + { + // The current fragment needs to be converted to a polygonal mesh. + // Array for recording the vertices of the polygonal mesh. + polyhedra->Initialize(); + polyhedra->Allocate((vtkIdType)outputQ[co]->size()); + + // for each face of the fragment + vtkSmartPointer<vtkIdList> poly = vtkSmartPointer<vtkIdList>::New(); + for (std::vector<vtkSmartPointer<vtkIdList> >::iterator fc = outputQ[co]->begin(); + fc != outputQ[co]->end(); fc++) + { + poly->Reset(); + for (int pnr = 0; pnr < (*fc)->GetNumberOfIds(); pnr++) + { + poly->InsertNextId((*fc)->GetId(pnr)); + } + // insert the polygon face. + polyhedra->InsertNextCell(VTK_POLYGON, poly); + } + polyhedra->SetPoints(newPoints); + + // convert the polygon faces to triangular faces. + triangleFilter->SetInputData(polyhedra); + triangleFilter->Update(); + + if (triangleFilter->GetOutput()->GetNumberOfCells() > 0) + { + // Compute the volume of the fragment + volume->SetInputData(triangleFilter->GetOutput()); + volume->Update(); + fragVolume = volume->GetVolume(); + } + else + { + fragVolume = 0; + } + + // Range values for the current fragment. + fragRangeValue[0] = fragScalar->GetComponent((vtkIdType)(firstFragNr + co), 0); + fragRangeValue[1] = fragScalar->GetComponent((vtkIdType)(firstFragNr + co), 1); + + // Map the current fragment into the 2-Dimensional bin based on its range values. + binIndexFirst = (int)(this->ResX - 1) * + (fragRangeValue[0] - inPD->GetArray(this->Fields[0])->GetRange()[0]) / fieldInterval[0]; + binIndexSecond = (int)(this->ResY - 1) * + (fragRangeValue[1] - inPD->GetArray(this->Fields[1])->GetRange()[0]) / fieldInterval[1]; + + ////cout << "biF: " << binIndexFirst << "\tbiS: " << binIndexSecond << "\t" << fragVolume << + ///endl; + + // aggregate the fragment volumes in each bin + if (binIndexFirst >= 0 && binIndexFirst < this->ResX && binIndexSecond >= 0 && + binIndexSecond < this->ResY) + { + imageBin[binIndexFirst][binIndexSecond] += fragVolume; + } + + // finding the largest volume in a bin. + if (imageBin[binIndexFirst][binIndexSecond] > maxBinSize) + { + maxBinSize = imageBin[binIndexFirst][binIndexSecond]; + } + + // Clear faces from current polytope in output queue. + while (outputQ[co]->size() > 0) + { + outputQ[co]->pop_back(); + } + } // end for loop in outputQ + + // Release the memory for the local structure. + while (outputQ.size() > 0) + { + Polytope plocal; + plocal = outputQ.back(); + outputQ.pop_back(); + delete plocal; + } + outputQ.clear(); + } // For each cell + + if (fragment) + { + delete fragment; + fragment = nullptr; + } + if (residual) + { + delete residual; + residual = nullptr; + } + + + // Create the output image data. + output->SetExtent(0, this->ResX - 1, 0, this->ResY - 1, 0, 0); + output->SetOrigin(0, 0, 0); + output->SetSpacing(1, 1, 1); + output->AllocateScalars(VTK_DOUBLE, 1); + + // A scalar array is attached onto the output vtkImageData. + // This array records the total volume of the fragments in each bin. + vtkSmartPointer<vtkFloatArray> volumeArray = vtkSmartPointer<vtkFloatArray>::New(); + volumeArray->SetName("volume"); + volumeArray->SetNumberOfComponents(1); + volumeArray->SetNumberOfTuples(this->ResX * this->ResY); + + // Pixel density are computed by the fragment volume. + for (int xIndex = 0; xIndex < this->ResX; xIndex++) + { + for (int yIndex = 0; yIndex < this->ResY; yIndex++) + { + float size = imageBin[xIndex][yIndex]; + int idx = xIndex * this->ResY + yIndex; + volumeArray->SetComponent(idx, 0, size); + double* pixel = static_cast<double*>(output->GetScalarPointer(xIndex, yIndex, 0)); + if (imageBin[xIndex][yIndex] > 0) + { + pixel[0] = 255.0 * imageBin[xIndex][yIndex] / maxBinSize; + } + else + { + pixel[0] = 0; + } + } + } + output->GetPointData()->AddArray(volumeArray); + output->Squeeze(); + + for (int xIndex = 0; xIndex < this->ResX; ++xIndex) + { + delete[] imageBin[xIndex]; + } + delete[] imageBin; + + return 1; +} diff --git a/Infovis/Core/vtkContinuousScatterplot.h b/Infovis/Core/vtkContinuousScatterplot.h new file mode 100644 index 0000000000000000000000000000000000000000..d2c508d0466d105b3eb7ebb3299e29350e370959 --- /dev/null +++ b/Infovis/Core/vtkContinuousScatterplot.h @@ -0,0 +1,228 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkPolyDataAlgorithm.h + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm 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. + +=========================================================================*/ +/** +* @class vtkContinuousScatterplot +* @brief Given a 3D domain space represented by an +* unstructured grid composed of tetrahedral cells with bivariate fields, this filter +* tessellates each cell in the domain to polyhedral fragments by intersecting the +* projection of the cell into 2-D range space against two sets of cutting planes, one set +* is defined along the first field, the second set is defined along the second field. The +* volume of these subdivided polyhedral fragments can be computed and aggregated over +* cells to depict the density distribution of the data projection in the bivariate range +* space. +* +* @section Introduction +* Given a bivariate field (f1,f2) defined on an unstructured grid which is +* composed of tetrahedral cells, we can initially subdivide each cell based on its +* projection in the range into a number of fragments along the first field f1, we refer +* to these polyhedral fragments as Frag(f1) = {frag(f1)_1, frag(f1)_2, ... , frag(f1)_n}, +* where frag(f1)_n refers to the nth fragment along the first field subdivision. Each +* fragment has a range value and the value difference between the neighbouring fragments +* is represented as fragment width fw_f1, which is uniformly distributed across the +* range. +* Based on the structure of Frag(f1), for each of its cell "frag(f1)_n", we +* can further subdivide this cell based on the second field f2 using fragment width +* fw_f2. The tessellation along the second field results in an even finer fragment +* collection which we refer to as Frag(f1,f2) = {frag(f1,f2)_1, frag(f1,f2)_2, ... , +* frag(f1,f2)_m}. We can observe that Frag(f1,f2) is a finer tessellation of the domain +* than Frag(f1) and will be used to compute the density distribution in the bivariate +* range space. The algorithm for fragment computation is similar to the first stage of +* the work in [0]. +* Each fragment "s" in Frag(f1,f2) has range values (f1(s), f2(s)) in the bivariate +* fields. These values can be further mapped to a 2-D bin with a resolution rexX * resY. +* The mapped bin index (binIndexX, binIndexY) of the fragment can be computed by linear +* interpolation on its range values : +* binIndexX = (int) resX * (f1(s) - f1_min) / (f1_max - f1_min) +* binIndexY = (int) resY * (f2(s) - f2_min) / (f2_max - f2_min), +* where (f1_min, f1_max) is the range in first field. +* Once we know which bin a fragment coincides, the density value in each bin equals to +* the total geometric volume of the fragments in this bin. This volume distribution +* over the bins will be exported as a point data array in the output data structure. +* If we map this 2-D bin to a 2-D image with each bin corresponding to a pixel and +* bin density to pixel transparency, then the image can be displayed as a continuous +* scatterplot. + +* @section Algorithm +* The algorithm of this filter can be described as: +* Require: R.1 The domain space is an unstructured grid data set composed of +* tetrahedral cells; +* R.2 The range space contains two scalar fields, say f1 and f2. +* +* The most important step is to compute the fragments. The implementation processes +* the input grid one cell at a time, explicitly computing the intersection of the cell +* with the cutting planes defined by the fragment boundaries in each scalar field. +* In order to subdivide the cell, we need to define a list of cutting planes in each +* field. The interval between neighbouring cutting planes is related to the output 2-D +* bin resolution (resX, resY) and can be computed as : +* fw_f1 = (f1_max - f1_min) / resX +* fw_f2 = (f2_max - f2_min) / resY, +* where (f1_max,f1_min) is the scalar range of first field. +* +* 1. For each tetrahedron T in the input grid: +* +* 1.1 Subdivide the cell T based on the first field f1, we will obtain a list +* of fragments: Frag(f1) = {frag(f1)_1, frag(f1)_2, ... , frag(f1)_n}. The +* steps for subdivision can be described as: +* +* 1.1.1 For each cutting plane s with respect to the first field f1, +* its field value f1(s) = f1_min + n * fw_f1, where n refers to the n-th +* cutting plane: +* +* 1.1.2. Traverse each edge e starting from point a to b in the cell, we +* will maintain three data classes, namely fragmentFace, +* residualFace and cutSet: +* A. fragmentFace contains vertices in the current fragment. +* B. cutSet contains vertices whose range values equal to f1(s). +* This set contains the current cutting plane. +* C. residualFace contains the rest of the vertices in the cell. +* In order to classify edge vertices into these classes, the +* following case table is used for each vertex "a" : +* case 0 : f1(a)------ f1(s) ------f1(b) +* condition: f1(a) < f1(s) , f1(b) > f1(s) +* class: p(s,e), a -> fragmentFace +* p(s,e) -> cutSet +* p(s,e) -> residualFace +* +* case 1 : f1(b)------ f1(s) ------f1(a) +* condition: f1(a) > f1(s) , f1(b) < f1(s) +* class: p(s,e) -> fragmentFace +* p(s,e) -> cutSet +* a -> residualFace +* +* case 2 : f1(s),f1(a)-------------------f1(b) +* condition: f1(s) == f1(a), f1(s) <= f1(b) +* class: a -> fragmentFace +* a -> residualFace +* a -> cutSet +* +* case 3 : f1(a)-------------------f1(b), f1(s) +* condition: f1(s) > f1(a), f1(s) == f1(b) +* class: a -> fragmentFace +* +* case 4 : f1(s),f1(b)-------------------f1(a) +* condition: f1(s) < f1(a), f1(s) == f1(b) +* class: a -> residualFace +* Remark: 1. we use "->" to indicate "belongs to" relation. +* 2. p(s,e) refers to the interpolated point of range value +* f1(s) on the edge e. +* +* 1.1.3. After we have traversed every edge in a cell for the cutting plane +* s, three classes for storing fragment, cutting plane and residual +* faces are updated. The faces of the current fragment frag(f1) +* are the union of all elements in fragmentFace and cutSet. +* +* 1.2 Take the output of step 1.1, traverse each fragment in Frag(f1), define a list +* of cutting planes with respect to field f2, further subdivide the fragments in +* Frag(f1) following steps from 1.1.2 to 1.1.3. The output of this step will be +* the fragment collection Frag(f1,f2). Each fragment in Frag(f1,f2) can be further +* mapped to a 2-D bin based on its range values. The density value in each bin +* equals to the total geometric volume of the fragments in this bin. This volume +* distribution over the bins will be exported as a point data array in the output +* data structure. +* +* @section VTK Filter Design +* The input and output ports of the filter: +* Input port : the input data set should be a vtkUnstructuredGrid, with each of its +* cell defined as a tetrahedron. At least two scalar fields are +* associated with the data. The user needs to specify the name of the +* two scalar arrays beforehand. +* Output port: the output data set is a 2D image stored as a vtkImageData. +* The resolution of the output image can be set by the user. +* The volume distribution of fragments in each pixel or bin +* is stored in an point data array named "volume" in the output +* vtkImageData. +* +* @section How To Use This Filter +* Suppose we have a tetrahedral mesh stored in a vtkUnstructuredGrid, we call this +* data set "inputData". This data set has two scalar arrays whose names are "f1" +* and "f2" respectively. We would like the resolution of output image set to (resX,resY). +* Given these input, this filter can be called as follows in c++ sample code: +* +* vtkSmartPointer<vtkContinuousScatterplot> csp = +* vtkSmartPointer<vtkContinuousScatterplot>::New(); +* csp->SetInputData(inputData); +* csp->SetField1("f1",resX); +* csp->SetField2("f2",resY); +* csp->Update(); +* +* Then the output, "csp->GetOutput()", will be a vtkImageData containing a scalar +* array whose name is "volume". This array contains the volume distribution of the +* fragments. +* +* [0] H.Carr and D.Duke, Joint contour nets: Topological analysis of multivariate data. +* IEEE Transactions on Visualization and Computer Graphics, volume 20, +* issue 08, pages 1100-1113, 2014 +*/ + +#ifndef vtkContinuousScatterplot_h +#define vtkContinuousScatterplot_h + +#include "vtkInfovisCoreModule.h" // For export macro +#include "vtkImageAlgorithm.h" + +class VTKINFOVISCORE_EXPORT vtkContinuousScatterplot : public vtkImageAlgorithm +{ +public: + static vtkContinuousScatterplot* New(); + vtkTypeMacro(vtkContinuousScatterplot, vtkImageAlgorithm); + void PrintSelf(ostream& os, vtkIndent indent) VTK_OVERRIDE; + + /** + * Get the tolerance used when comparing floating point numbers for equality. + */ + vtkGetMacro(Epsilon, double); + + /** + * Set the tolerance used when comparing floating point numbers for equality. + */ + vtkSetMacro(Epsilon, double); + + /** + * Specify the name of the first field to be used in subdividing the dataset. + * Specify the resolution along x axis of the output image. + */ + void SetField1(char* fieldName, vtkIdType ResX); + + /** + * Specify the name of the second field to be used in subdividing the dataset. + * Specify the resolution along y axis of the output image. + */ + void SetField2(char* fieldName, vtkIdType ResY); + +protected: + vtkContinuousScatterplot(); + + // Configure input port to accept only vtkUnstructuredGrid. + virtual int FillInputPortInformation(int port, vtkInformation* info) VTK_OVERRIDE; + + // Configure out port to be a vtkImageData data set. + virtual int FillOutputPortInformation(int port, vtkInformation* info) VTK_OVERRIDE; + virtual int RequestData( + vtkInformation*, vtkInformationVector**, vtkInformationVector*) VTK_OVERRIDE; + + // Set the tolerance used when comparing floating numbers for equality. + double Epsilon; + + // Names of the scalar fields to be used in the filter. + char* Fields[2]; + + // Resolution of the output image. + vtkIdType ResX, ResY; + +private: + vtkContinuousScatterplot(const vtkContinuousScatterplot&) VTK_DELETE_FUNCTION; + void operator=(const vtkContinuousScatterplot&) VTK_DELETE_FUNCTION; +}; +#endif diff --git a/Testing/Data/FiberSurface/README.txt.md5 b/Testing/Data/FiberSurface/README.txt.md5 new file mode 100644 index 0000000000000000000000000000000000000000..5374264f5fafeedf20a145530da245769866909b --- /dev/null +++ b/Testing/Data/FiberSurface/README.txt.md5 @@ -0,0 +1 @@ +c50c97be6b11d9ee05e7a2f6fde46a0d diff --git a/Testing/Data/FiberSurface/line_01.vtk.md5 b/Testing/Data/FiberSurface/line_01.vtk.md5 new file mode 100644 index 0000000000000000000000000000000000000000..418951264d24fc18b1eaa7e9f3c8430a6519cecb --- /dev/null +++ b/Testing/Data/FiberSurface/line_01.vtk.md5 @@ -0,0 +1 @@ +98fdbe475730fabc6a2934e76b2621cb diff --git a/Testing/Data/FiberSurface/line_02.vtk.md5 b/Testing/Data/FiberSurface/line_02.vtk.md5 new file mode 100644 index 0000000000000000000000000000000000000000..0c9ca9447d2c38d63d0f66ffb9d31f8fc81e4f09 --- /dev/null +++ b/Testing/Data/FiberSurface/line_02.vtk.md5 @@ -0,0 +1 @@ +0c890c7ddea677b325c86230629aa69a diff --git a/Testing/Data/FiberSurface/line_03.vtk.md5 b/Testing/Data/FiberSurface/line_03.vtk.md5 new file mode 100644 index 0000000000000000000000000000000000000000..4a8b6f42900748ea435d8c0b4fc36d29f748e485 --- /dev/null +++ b/Testing/Data/FiberSurface/line_03.vtk.md5 @@ -0,0 +1 @@ +f7c178c7284c94227a1494fb22d00432 diff --git a/Testing/Data/FiberSurface/line_04.vtk.md5 b/Testing/Data/FiberSurface/line_04.vtk.md5 new file mode 100644 index 0000000000000000000000000000000000000000..67566862844e72dec59dc0fe9b1e970032f6e1ea --- /dev/null +++ b/Testing/Data/FiberSurface/line_04.vtk.md5 @@ -0,0 +1 @@ +aae2336f45f5d4742f2e790f0887fbbc diff --git a/Testing/Data/FiberSurface/line_05.vtk.md5 b/Testing/Data/FiberSurface/line_05.vtk.md5 new file mode 100644 index 0000000000000000000000000000000000000000..17af2690fdfb70d92f1a545dd5676a0761d29df1 --- /dev/null +++ b/Testing/Data/FiberSurface/line_05.vtk.md5 @@ -0,0 +1 @@ +546fa8373c92e42d28f9fa15e246c393 diff --git a/Testing/Data/FiberSurface/line_11.vtk.md5 b/Testing/Data/FiberSurface/line_11.vtk.md5 new file mode 100644 index 0000000000000000000000000000000000000000..e9f43c2102c5f4588cf95c9c8de2673a8e376727 --- /dev/null +++ b/Testing/Data/FiberSurface/line_11.vtk.md5 @@ -0,0 +1 @@ +07640d24818c141b3247df9506da9b7d diff --git a/Testing/Data/FiberSurface/line_12.vtk.md5 b/Testing/Data/FiberSurface/line_12.vtk.md5 new file mode 100644 index 0000000000000000000000000000000000000000..eb03e1f4bf5dba7bdbf352e2cafa6e5382e36024 --- /dev/null +++ b/Testing/Data/FiberSurface/line_12.vtk.md5 @@ -0,0 +1 @@ +b6edfe4419678ec9d7117bb9f9b44e43 diff --git a/Testing/Data/FiberSurface/line_13.vtk.md5 b/Testing/Data/FiberSurface/line_13.vtk.md5 new file mode 100644 index 0000000000000000000000000000000000000000..b67ff3b551d8f8a7204ae5d064f4def39c2a7e9d --- /dev/null +++ b/Testing/Data/FiberSurface/line_13.vtk.md5 @@ -0,0 +1 @@ +e16c7189d8dd0dcac081f3a1b8d72f95 diff --git a/Testing/Data/FiberSurface/line_14.vtk.md5 b/Testing/Data/FiberSurface/line_14.vtk.md5 new file mode 100644 index 0000000000000000000000000000000000000000..86024230aab09c87bf83871960843e624862178e --- /dev/null +++ b/Testing/Data/FiberSurface/line_14.vtk.md5 @@ -0,0 +1 @@ +29705575fe5fd9f93441c3f5fed4f4de diff --git a/Testing/Data/FiberSurface/line_15.vtk.md5 b/Testing/Data/FiberSurface/line_15.vtk.md5 new file mode 100644 index 0000000000000000000000000000000000000000..1d2c330e64baa855de24c7408718e9b74d7e329d --- /dev/null +++ b/Testing/Data/FiberSurface/line_15.vtk.md5 @@ -0,0 +1 @@ +445cb1432886b360c2b0ca7d0379f6d0 diff --git a/Testing/Data/FiberSurface/line_21.vtk.md5 b/Testing/Data/FiberSurface/line_21.vtk.md5 new file mode 100644 index 0000000000000000000000000000000000000000..51c7da161dc2bc3b309bbe61ba49c2fa8be55bc0 --- /dev/null +++ b/Testing/Data/FiberSurface/line_21.vtk.md5 @@ -0,0 +1 @@ +90492c670c1ff8e0fce58b9c810ab235 diff --git a/Testing/Data/FiberSurface/line_22.vtk.md5 b/Testing/Data/FiberSurface/line_22.vtk.md5 new file mode 100644 index 0000000000000000000000000000000000000000..7e086d06a3b0859d02c03ae1362055cc34d40840 --- /dev/null +++ b/Testing/Data/FiberSurface/line_22.vtk.md5 @@ -0,0 +1 @@ +9c2bbe4f8f0eddf5f7b3ee4e8d711e75 diff --git a/Testing/Data/FiberSurface/line_23.vtk.md5 b/Testing/Data/FiberSurface/line_23.vtk.md5 new file mode 100644 index 0000000000000000000000000000000000000000..c39b71e4889e49b37c9fb3303efc22bdf94db57c --- /dev/null +++ b/Testing/Data/FiberSurface/line_23.vtk.md5 @@ -0,0 +1 @@ +4037a6f1f94d2e24a1b80f071c7150b3 diff --git a/Testing/Data/FiberSurface/line_24.vtk.md5 b/Testing/Data/FiberSurface/line_24.vtk.md5 new file mode 100644 index 0000000000000000000000000000000000000000..9034d83b778da4ac00513b4229730eed952b6d8a --- /dev/null +++ b/Testing/Data/FiberSurface/line_24.vtk.md5 @@ -0,0 +1 @@ +71527541d8d17d68820ab2ac97c35cdf diff --git a/Testing/Data/FiberSurface/line_25.vtk.md5 b/Testing/Data/FiberSurface/line_25.vtk.md5 new file mode 100644 index 0000000000000000000000000000000000000000..83bd25725dc718f643008695df064aed86f474a9 --- /dev/null +++ b/Testing/Data/FiberSurface/line_25.vtk.md5 @@ -0,0 +1 @@ +ab733b35f8938e6e72c1b8697d68d853 diff --git a/Testing/Data/FiberSurface/one_cube.vtk.md5 b/Testing/Data/FiberSurface/one_cube.vtk.md5 new file mode 100644 index 0000000000000000000000000000000000000000..93f2c8f7e7e328dfd1ae7972f74f0d2c147a4455 --- /dev/null +++ b/Testing/Data/FiberSurface/one_cube.vtk.md5 @@ -0,0 +1 @@ +f5a0f148371af48ecc7d289f0d58342c diff --git a/Testing/Data/FiberSurface/one_cube_both_forking.vtk.md5 b/Testing/Data/FiberSurface/one_cube_both_forking.vtk.md5 new file mode 100644 index 0000000000000000000000000000000000000000..4660d7b35b6715de4394c0d3e5440c9292fd6852 --- /dev/null +++ b/Testing/Data/FiberSurface/one_cube_both_forking.vtk.md5 @@ -0,0 +1 @@ +292fbf06bd030d917da38bbae7d1d39d diff --git a/Testing/Data/FiberSurface/one_cube_closed.vtk.md5 b/Testing/Data/FiberSurface/one_cube_closed.vtk.md5 new file mode 100644 index 0000000000000000000000000000000000000000..f81705f58902913dab406e3a03520635c45405f7 --- /dev/null +++ b/Testing/Data/FiberSurface/one_cube_closed.vtk.md5 @@ -0,0 +1 @@ +460894a81078cef1eb159e1dbb24bc01 diff --git a/Testing/Data/cube.vtu.md5 b/Testing/Data/cube.vtu.md5 new file mode 100644 index 0000000000000000000000000000000000000000..b73b319e6e4bafd355d16e5f972671601bba0ce2 --- /dev/null +++ b/Testing/Data/cube.vtu.md5 @@ -0,0 +1 @@ +876bdbeba77529faf39aca1503b8d201