From f6a17078fb2c0924d8c6c3f0d2a2fcc0722a2baa Mon Sep 17 00:00:00 2001 From: Pietro Incardona <incardon@mpi-cbg.de> Date: Fri, 26 Jun 2015 18:22:20 +0200 Subject: [PATCH] Added CSV and VTK writer --- src/CSVWriter.hpp | 268 +++++++++++++++ src/CSVWriter_unit_tests.hpp | 39 +++ src/GraphMLWriter.hpp | 10 +- src/GraphMLWriter_unit_tests.hpp | 26 +- src/VTKWriter.hpp | 252 +++++++++++++++ src/VTKWriter_graph.hpp | 539 +++++++++++++++++++++++++++++++ src/VTKWriter_unit_tests.hpp | 226 +++++++++++++ src/VTKWriter_vector_box.hpp | 458 ++++++++++++++++++++++++++ vtk_graph_test.vtk | 140 ++++++++ 9 files changed, 1940 insertions(+), 18 deletions(-) create mode 100644 src/CSVWriter.hpp create mode 100644 src/CSVWriter_unit_tests.hpp create mode 100644 src/VTKWriter.hpp create mode 100644 src/VTKWriter_graph.hpp create mode 100644 src/VTKWriter_unit_tests.hpp create mode 100644 src/VTKWriter_vector_box.hpp create mode 100644 vtk_graph_test.vtk diff --git a/src/CSVWriter.hpp b/src/CSVWriter.hpp new file mode 100644 index 0000000..e272a3b --- /dev/null +++ b/src/CSVWriter.hpp @@ -0,0 +1,268 @@ +/* + * CSVWriter.hpp + * + * Created on: Dec 15, 2014 + * Author: Pietro Incardona + */ + +#ifndef CSVWRITER_HPP_ +#define CSVWRITER_HPP_ + +#include <iostream> +#include <boost/fusion/include/mpl.hpp> +#include <boost/fusion/include/for_each.hpp> +#include <fstream> +#include "common.hpp" +#include <boost/mpl/range_c.hpp> +#include <boost/mpl/for_each.hpp> +#include "Point_test.hpp" +#include "csv_multiarray.hpp" +#include "util.hpp" + +/*! \brief this class is a functor for "for_each" algorithm + * + * For each element of the boost::vector the operator() is called. + * Is mainly used to create a string containing all the properties of the object + * + * \tparam Tobj object + * + */ + +template<typename Tobj> +struct csv_prp +{ + // String + std::stringstream & str; + + // Object to write + Tobj & obj; + + /*! \brief Constructor + * + * Create a vertex properties list + * + * \param str streamstring + * \param obj object to write + * + */ + csv_prp(std::stringstream & str, Tobj & obj) + :str(str),obj(obj) + { + }; + + //! It call the functor for each member + template<typename T> + void operator()(T& t) + { + // This is the type of the csv column + typedef typename boost::fusion::result_of::at_c<typename Tobj::type,T::value>::type col_type; + + // Remove the reference from the column type + typedef typename boost::remove_reference<col_type>::type col_rtype; + + csv_value_str<col_rtype>(obj.template get<T::value>(),str); + } +}; + +/*! \brief this class is a functor for "for_each" algorithm + * + * For each element of the boost::vector the operator() is called. + * Is mainly used to create a string containing all the properties of the object + * + * \tparam T object + * + */ + +template<typename Tobj, bool attr> +struct csv_col +{ + std::stringstream & str; + + csv_col(std::stringstream & str) + :str(str) + { + }; + + //! It call the functor for each member + template<typename T> + void operator()(T& t) + { + // This is the type of the csv column + typedef typename boost::fusion::result_of::at_c<typename Tobj::type,T::value>::type col_type; + + // Remove the reference from the column type + typedef typename boost::remove_reference<col_type>::type col_rtype; + + csv_col_str<col_rtype>(std::string(Tobj::attributes::name[T::value]),str); + } +}; + +/*! \brief this class is a functor for "for_each" algorithm + * + * This class is a functor for "for_each" algorithm. For each + * element of the boost::vector the operator() is called. + * Is mainly used to create a string containing all the vertex + * properties + * + * Specialization when we do not have vertex attributes + * + * \tparam G graph type + * + */ + +template<typename Tobj> +struct csv_col<Tobj,false> +{ + std::stringstream & str; + + csv_col(std::stringstream & str) + :str(str) + { + }; + + //! It call the functor for each member + template<typename T> + void operator()(T& t) + { + // This is the type of the csv column + typedef typename boost::fusion::result_of::at_c<typename Tobj::type,T::value>::type col_type; + + // Remove the reference from the column type + typedef typename boost::remove_reference<col_type>::type col_rtype; + + std::stringstream str2; + str2 << "column_" << T::value; + + csv_col_str<col_rtype>(str2.str(),str); + } +}; + +#define VECTOR 1 + +/*! \brief CSV Writer + * + * It write in CSV format vector of objects living into an N-dimensional space + * + * \tparam v_pos Positional vector + * \tparam v_prp Property vector + * + */ +template <typename v_pos, typename v_prp, unsigned int impl = VECTOR> +class CSVWriter +{ + /*! \brief Get the colums name (also the positional name) + * + */ + std::string get_csv_colums() + { + std::stringstream str; + + // write positional columns + for (int i = 0 ; i < v_pos::value_type::dims ; i++) + { + if (i == 0) + str << "x[" << i << "]"; + else + str << "," << "x[" << i << "]"; + } + + // write positional information + + csv_col<typename v_prp::value_type,has_attributes<typename v_prp::value_type>::value> col(str); + + // Iterate through all the vertex and create the vertex list + boost::mpl::for_each< boost::mpl::range_c<int,0,v_prp::value_type::max_prop> >(col); + + str << "\n"; + + return str.str(); + } + + /*! \brief Get the csv data section + * + * \param v_pos vector that contain the positional information + * \param v_prp vector that contain the property information + * + */ + std::string get_csv_data(v_pos & vp, v_prp & vpr) + { + std::stringstream str; + + // The position and property vector size must match + if (vp.size() != vpr.size()) + { + std::cerr << "Error: " << __FILE__ << ":" << __LINE__ << " position vector and property vector must have the same size \n"; + return std::string(""); + } + + // Write the data + for (size_t i = 0 ; i < vp.size() ; i++) + { + for (size_t j = 0 ; j < v_pos::value_type::dims ; j++) + { + if (j == 0) + str << vp.template get<0>(i)[j]; + else + str << "," << vp.template get<0>(i)[j]; + } + + // Object to write + typename v_prp::value_type obj = vpr.get(i); + + csv_prp<typename v_prp::value_type> c_prp(str,obj); + + // write the properties to the stream string + boost::mpl::for_each< boost::mpl::range_c<int,0,v_prp::value_type::max_prop> >(c_prp); + + str << "\n"; + } + + return str.str(); + } + +public: + + /*! \brief It write a CSV file + * + * \tparam prp which properties to output [default = -1 (all)] + * + * \param file path where to write + * \param v_pos positional vector + * \param v_prp properties vector + * + */ + + bool write(std::string file, v_pos & v , v_prp & prp) + { + // Header for csv (colums name) + std::string csv_header; + // Data point + std::string point_data; + + // Get csv columns + csv_header = get_csv_colums(); + + // For each property in the vertex type produce a point data + point_data = get_csv_data(v,prp); + + // write the file + std::ofstream ofs(file); + + // Check if the file is open + if (ofs.is_open() == false) + {std::cerr << "Error cannot create the CSV file: " + file;} + + ofs << csv_header << point_data; + + // Close the file + + ofs.close(); + + // Completed succefully + return true; + } +}; + + +#endif /* CSVWRITER_HPP_ */ + diff --git a/src/CSVWriter_unit_tests.hpp b/src/CSVWriter_unit_tests.hpp new file mode 100644 index 0000000..048ed85 --- /dev/null +++ b/src/CSVWriter_unit_tests.hpp @@ -0,0 +1,39 @@ +#ifndef CSVWRITER_UNIT_TESTS_HPP_ +#define CSVWRITER_UNIT_TESTS_HPP_ + +#include "CSVWriter.hpp" +#include "Vector/vector_test_util.hpp" + +BOOST_AUTO_TEST_SUITE( csv_writer_test ) + + +BOOST_AUTO_TEST_CASE( csv_writer_particles ) +{ + // Allocate a property vector + auto v_prp = allocate_openfpm(16); + // Vector of position + openfpm::vector<Point<3,float>> v_pos; + + // create a positional vector + for (size_t i = 0 ; i < v_prp.size() ; i++) + { + Point<3,float> p({1.0,2.0,3.0}); + + v_pos.add(p); + } + + // CSVWriter test + CSVWriter<openfpm::vector<Point<3,float>>, openfpm::vector<Point_test<float>> > csv_writer; + + // Write the CSV + csv_writer.write("csv_out.csv",v_pos,v_prp); + + bool test = compare("csv_out.csv","csv_out_test.csv"); + BOOST_REQUIRE_EQUAL(true,test); + + +} + +BOOST_AUTO_TEST_SUITE_END() + +#endif diff --git a/src/GraphMLWriter.hpp b/src/GraphMLWriter.hpp index 02046c9..6801c05 100644 --- a/src/GraphMLWriter.hpp +++ b/src/GraphMLWriter.hpp @@ -1,10 +1,8 @@ #ifndef GRAPHML_WRITER_HPP #define GRAPHML_WRITER_HPP -#include "map_graph.hpp" +#include "Graph/map_graph.hpp" #include <iostream> -#include <boost/fusion/include/mpl.hpp> -#include <boost/fusion/include/for_each.hpp> #include <fstream> #include "common.hpp" @@ -425,7 +423,7 @@ struct edge_node void end_node() { // close a node - e_node += "</node>\n"; + e_node += "</edge>\n"; } //! It call the functor for each member @@ -478,7 +476,7 @@ class GraphMLWriter std::string get_vertex_properties_list() { //! vertex property output string - std::string v_out("<key id=\"d0\" for=\"node\" yfiles.type=\"nodegraphics\"/>\n"); + std::string v_out(""); // create a vertex property functor vertex_prop<Graph> vp(v_out); @@ -633,7 +631,7 @@ public: http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd\">\n"; // Graph header to define an header - graph_header = "<graph id=\"" + graph_name + "\" edgedefault=\"directed\">\n"; + graph_header = "<graph id=\"" + graph_name + "\" edgedefault=\"undirected\">\n"; // Graph header end graph_header_end = "</graph>\n"; diff --git a/src/GraphMLWriter_unit_tests.hpp b/src/GraphMLWriter_unit_tests.hpp index 1abc9be..c00a953 100644 --- a/src/GraphMLWriter_unit_tests.hpp +++ b/src/GraphMLWriter_unit_tests.hpp @@ -13,6 +13,7 @@ #include "GraphMLWriter.hpp" #include "VTKWriter.hpp" #include "Graph/CartesianGraphFactory.hpp" +#include "util.hpp" BOOST_AUTO_TEST_SUITE( graphml_writer_test ) @@ -81,34 +82,35 @@ BOOST_AUTO_TEST_CASE( graphml_writer_use) g_csr2.addEdge(2,0); g_csr2.addEdge(3,2); - VTKWriter<Graph_CSR<ne_cp,ne_cp>> gv2(g_csr2); + // Create a graph ML + GraphMLWriter<Graph_CSR<ne_cp,ne_cp>> gv2(g_csr2); + gv2.write("test_graph2.graphml"); - gv2.write("test_graph2.vtk"); + // check that match + + bool test = compare("test_graph2.graphml","test_graph2_test.graphml"); + BOOST_REQUIRE_EQUAL(true,test); //! Create a graph CartesianGraphFactory<3,Graph_CSR<ne_cp,ne_cp>> g_factory; // Cartesian grid - std::vector<size_t> sz; - sz.push_back(GS_SIZE); - sz.push_back(GS_SIZE); - sz.push_back(GS_SIZE); + size_t sz[] = {GS_SIZE,GS_SIZE,GS_SIZE}; // Box Box<3,float> box({0.0,0.0,0.0},{1.0,1.0,1.0}); Graph_CSR<ne_cp,ne_cp> g_csr = g_factory.construct<5,float,2,ne_cp::x,ne_cp::y,ne_cp::z>(sz,box); + // Create a graph ML GraphMLWriter<Graph_CSR<ne_cp,ne_cp>> gw(g_csr); + gw.write("test_graph.graphml"); -// std::cout << std::is_class<ne_cp::attributes>; - - gw.write("test_graph.gml"); - - VTKWriter<Graph_CSR<ne_cp,ne_cp>> gv(g_csr); - gv.write("test_graph.vtk"); + // check that match + test = compare("test_graph.graphml","test_graph_test.graphml"); + BOOST_REQUIRE_EQUAL(true,test); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/VTKWriter.hpp b/src/VTKWriter.hpp new file mode 100644 index 0000000..fa32395 --- /dev/null +++ b/src/VTKWriter.hpp @@ -0,0 +1,252 @@ +/* + * VTKWriter.hpp + * + * Created on: Dec 15, 2014 + * Author: Pietro Incardona + */ + +#ifndef VTKWRITER_HPP_ +#define VTKWRITER_HPP_ + +#include "Graph/map_graph.hpp" +#include <iostream> +#include <boost/fusion/include/mpl.hpp> +#include <boost/fusion/include/for_each.hpp> +#include <fstream> +#include "common.hpp" + +/*! \brief Get the type + * + * It convert T to a string identify the corrispondent type in VTK format + * + */ + +template <typename T> std::string getType() +{ + // Create a property string based on the type of the property + if (typeid(T) == typeid(float)) + return "float"; + else if (typeid(T) == typeid(double)) + return "double"; + else if (typeid(T) == typeid(char)) + return "char"; + else if (typeid(T) == typeid(unsigned char)) + return "unsigned_char"; + else if (typeid(T) == typeid(short)) + return "short"; + else if (typeid(T) == typeid(unsigned short)) + return "unsigned_short"; + else if (typeid(T) == typeid(int)) + return "int"; + else if (typeid(T) == typeid(unsigned int)) + return "unsigned_int"; + else if (typeid(T) == typeid(long int)) + return "long"; + else if (typeid(T) == typeid(unsigned long int)) + return "unsigned_long"; + else if (typeid(T) == typeid(bool)) + return "bit"; + return ""; +} + +/*! \brief Set a conversion map between A and B + * + * Convert A to B + * + * \tparam B destination type + * \tparam A source type + * + */ + +template<typename A> +class convert +{ +public: + template<typename B> static B to(const A & data) + { + return static_cast<B>(data); + } +}; + +/*! \brief Partial specialization when A is a string + * + * + */ + +template<> +class convert<std::string> +{ +public: + template<typename B> static B to(const std::string & data) + { + return atof(data.c_str()); + } +}; + +/*! \brief It specify the VTK output file type + * + */ + +enum file_type +{ + BINARY, + ASCII +}; + +/*! \brief this class is a functor for "for_each" algorithm + * + * This class is a functor for "for_each" algorithm. For each + * element of the boost::vector the operator() is called. + * Is mainly used to create a string containing all the vertex + * properties + * + * \tparam G graph type + * \tparam attr has the vertex attributes + * + */ + +template<typename G, bool attr> +struct vtk_vertex_node +{ + // Vertex spatial type information + typedef typename G::V_type::s_type s_type; + + s_type (& x)[3]; + + // Vertex object container + typename G::V_container & vo; + + // vertex node string + std::string & v_node; + + /*! \brief Constructor + * + * Create a vertex properties list + * + * \param v_node std::string that is filled with the graph properties in the GraphML format + * \param n_obj object container to access its properties for example encapc<...> + * + */ + vtk_vertex_node(std::string & v_node, typename G::V_container & n_obj, s_type (&x)[3]) + :x(x),vo(n_obj),v_node(v_node) + { + }; + + //! \brief Write collected information + void write() + { + v_node += std::to_string(x[0]) + " " + std::to_string(x[1]) + " " + std::to_string(x[2]) + "\n"; + } + + //! It call the functor for each member + template<typename T> + void operator()(T& t) + { + // if the attribute name is x y or z, create a string with the value of the properties and store it + if (G::V_type::attributes::name[T::value] == "x"){x[0] = convert<typename boost::remove_reference<decltype(vo.template get<T::value>())>::type>::template to<s_type>(vo.template get<T::value>());} + else if (G::V_type::attributes::name[T::value] == "y"){x[1] = convert<typename boost::remove_reference<decltype(vo.template get<T::value>())>::type>::template to<s_type>(vo.template get<T::value>());} + else if (G::V_type::attributes::name[T::value] == "z"){x[2] = convert<typename boost::remove_reference<decltype(vo.template get<T::value>())>::type>::template to<s_type>(vo.template get<T::value>());} + } +}; + +/*! \brief this class is a functor for "for_each" algorithm + * + * This class is a functor for "for_each" algorithm. For each + * element of the boost::vector the operator() is called. + * Is mainly used to create a string containing all the vertex + * properties + * + * Specialization when we do not have vertex attributes + * + * \tparam G graph type + * + */ + +template<typename G> +struct vtk_vertex_node<G,false> +{ + // Vertex object container + typename G::V_container & vo; + + // vertex node string + std::string & v_node; + + /*! \brief Constructor + * + * Create a vertex properties list + * + * \param v_node std::string that is filled with the graph properties in the GraphML format + * \param n_obj object container to access its properties for example encapc<...> + * + */ + vtk_vertex_node(std::string & v_node, typename G::V_container & n_obj) + :vo(n_obj),v_node(v_node) + { + }; + + //! It call the functor for each member + template<typename T> + void operator()(T& t) + { + v_node += "0 0 0\n"; + } +}; + + +/*! \brief this class is a functor for "for_each" algorithm + * + * This class is a functor for "for_each" algorithm. For each + * element of the boost::vector the operator() is called. + * Is mainly used to create a string containing all the edge + * properties + * + */ + +template<typename G> +struct vtk_edge_node +{ + // Vertex object container + typename G::E_container & vo; + + // edge node string + std::string & e_node; + + /*! \brief Constructor + * + * Create an edge node + * + * \param e_node std::string that is filled with the graph properties in the GraphML format + * \param n_obj object container to access the object properties for example encapc<...> + * \param n_prop number of properties + * + */ + vtk_edge_node(std::string & e_node, typename G::E_container & n_obj) + :vo(n_obj),e_node(e_node) + { + }; + + /*! \brief Create a new node + * + * \param vc node number + * + */ + void new_node(size_t v_c, size_t s, size_t d) + { + // start a new node + e_node += "2 " + std::to_string(s) + " " + std::to_string(d) + "\n"; + } +}; + +#define GRAPH 1 +#define VECTOR_BOX 2 + +template <typename Graph, unsigned int imp> +class VTKWriter +{ + +}; + +#include "VTKWriter_graph.hpp" +#include "VTKWriter_vector_box.hpp" + +#endif /* VTKWRITER_HPP_ */ diff --git a/src/VTKWriter_graph.hpp b/src/VTKWriter_graph.hpp new file mode 100644 index 0000000..caa4f5d --- /dev/null +++ b/src/VTKWriter_graph.hpp @@ -0,0 +1,539 @@ +/* + * VTKWriter_graph.hpp + * + * Created on: May 5, 2015 + * Author: i-bird + */ + +#ifndef VTKWRITER_GRAPH_HPP_ +#define VTKWRITER_GRAPH_HPP_ + +/*! \brief This class specialize functions in the case the type T + * has or not defined attributes + * + * In C++ partial specialization of a function is not allowed so we have to + * encapsulate this function in a class + * + * \tparam has_attributes parameter that specialize the function in case the vertex + * define or not attributes name + * + * \tparam Graph type of graph we are processing + * \tparam p the property we are going to write + * + */ + +template<bool has_attributes, typename Graph, unsigned int i> +class prop_output +{ +public: + + /*! \brief For each vertex set the value + * + * \tparam i vertex property to print + * + */ + + static std::string get_point_data(Graph & g) + { + //! vertex node output string + std::string v_out; + + //! Get a vertex iterator + auto it = g.getVertexIterator(); + + // if there is the next element + while (it.isNext()) + { + // Print the property + v_out += std::to_string(g.vertex(it.get()).template get<i>()) + "\n"; + + // increment the iterator and counter + ++it; + } + + return v_out; + } + + /*! \brief Given a Graph return the point data header for a typename T + * + * \tparam T type to write + * \param n_node number of the node + * + */ + + static std::string get_point_property_header(size_t prop) + { + //! vertex node output string + std::string v_out; + + // Check if T is a supported format + // for now we support only scalar of native type + + std::string type = getType<typename boost::fusion::result_of::at<typename Graph::V_type::type,boost::mpl::int_<i>>::type>(); + + // if the type is not supported return + if (type.size() == 0) + {return v_out;} + + // Create point data properties + v_out += "SCALARS " + get_attributes() + " " + type + "\n"; + + // Default lookup table + v_out += "LOOKUP_TABLE default\n"; + + // return the vertex list + return v_out; + } + + /*! \brief Get the attributes name + * + */ + + static std::string get_attributes() + { + return Graph::V_type::attributes::name[i]; + } +}; + +/*! \brief This class specialize functions in the case the type T + * has not defined attributes + * + * In C++ partial specialization of a function is not allowed so we have to + * encapsulate this function in a class + * + * \tparam has_attributes parameter that specialize the function in case the vertex + * define or not attributes name + * + * \tparam i id of the property we are going to write + * + */ + +template<typename Graph, unsigned int i> +class prop_output<false,Graph,i> +{ + /*! \brief For each vertex set the value + * + * \tparam i vertex property to print + * + */ + + static std::string get_point_data(Graph & g) + { + //! vertex node output string + std::string v_out; + + //! Get a vertex iterator + auto it = g.getVertexIterator(); + + // if there is the next element + while (it.isNext()) + { + // Print the property + v_out += std::to_string(g.vertex(it.get()).template get<i>()) + "\n"; + + // increment the iterator and counter + ++it; + } + + return v_out; + } + + /*! \brief Given a Graph return the point data header for a typename T + * + * \tparam T type to write + * + * \param n_node number of the node + * \param prop id of the property + * + */ + + static std::string get_point_property_header(size_t prop) + { + //! vertex node output string + std::string v_out; + + // Check if T is a supported format + // for now we support only scalar of native type + + std::string type = getType<boost::fusion::result_of::at<typename Graph::V_type::type,boost::mpl::int_<i>>>("attr" + std::to_string(prop)); + + // if the type is not supported return + if (type.size() == 0) + {return v_out;} + + // Create point data properties + v_out += "SCALARS " + get_attributes() + " " + type + "\n"; + + // Default lookup table + v_out += "LOOKUP_TABLE default\n"; + + // return the vertex list + return v_out; + } + + /*! \brief Get the attributes name + * + * \tparam has_prop true if T has properties name defined + * \tparam T type to process + * + * \param i attribute to get + * + */ + + static std::string get_attributes() + { + return "attr" + std::to_string(i); + } +}; + +/*! \brief this class is a functor for "for_each" algorithm + * + * This class is a functor for "for_each" algorithm. For each + * element of the boost::vector the operator() is called. + * Is mainly used to produce at output for each property + * + * \tparam Graph graph we are processing + * + * \param dim Dimensionality + * \param S type of grid + * + */ + +template<typename Graph> +struct prop_out +{ + // property output string + std::string & v_out; + + // Graph that we are processing + Graph & g; + + /*! \brief constructor + * + * \param v_out string to fill with the vertex properties + * + */ + prop_out(std::string & v_out, Graph & g) + :v_out(v_out),g(g) + {}; + + //! It produce an output for each property + template<typename T> + void operator()(T& t) const + { + // actual string size + size_t sz = v_out.size(); + + // Produce the point properties header + v_out += prop_output<has_attributes<typename Graph::V_type>::value ,Graph,T::value>::get_point_property_header(t); + + // If the output has changed, we have to write the properties + if (v_out.size() != sz) + { + std::string attr = prop_output<has_attributes<typename Graph::V_type>::value,Graph,T::value>::get_attributes(); + + // Produce point data + v_out += prop_output<has_attributes<typename Graph::V_type>::value ,Graph ,T::value>::get_point_data(g); + } + } +}; + +/*! + * + * It write a VTK format file in case for a graph + * + * \tparam Type of graph + * + */ + +template <typename Graph> +class VTKWriter<Graph,GRAPH> +{ + Graph & g; + + /*! \brief It get the vertex properties list + * + * It get the vertex properties list of the vertex defined as VTK header + * + * \return a string that define the vertex properties in graphML format + * + */ + + std::string get_vertex_properties_list() + { + //! vertex property output string + std::string v_out; + + // write the number of vertex + v_out += "VERTICES " + std::to_string(g.getNVertex()) + " " + std::to_string(g.getNVertex() * 2) + "\n"; + + // return the vertex properties string + return v_out; + } + + /*! \brief It get the vertex properties list + * + * It get the vertex properties list of the vertex defined as a VTK header + * + * \return a string that define the vertex properties in graphML format + * + */ + + std::string get_point_properties_list() + { + //! vertex property output string + std::string v_out; + + // write the number of vertex + v_out += "POINTS " + std::to_string(g.getNVertex()) + " float" + "\n"; + + // return the vertex properties string + return v_out; + } + + /*! \brief It get the edge properties list + * + * It get the edge properties list of the edge defined as a GraphML header + * + * \return a string that define the edge properties in graphML format + * + */ + + std::string get_edge_properties_list() + { + //! vertex property output string + std::string e_out; + + // write the number of lines + e_out += "LINES " + std::to_string(g.getNEdge()) + " " + std::to_string(3*g.getNEdge()) + "\n"; + + // return the vertex properties string + return e_out; + } + + /*! \brief Create the VTK point definition + * + * \tparam s_type spatial type of the data + * \tparam attr false x,y,z are set to 0 for each vertex + * + */ + + template <bool attr> std::string get_point_list() + { + //! VTK spatial information + typename Graph::V_type::s_type x[3] = {0,0,0}; + + //! vertex node output string + std::string v_out; + + //! Get a vertex iterator + auto it = g.getVertexIterator(); + + // if there is the next element + while (it.isNext()) + { + // Get vtk vertex node + auto obj = g.vertex(it.get()); + + // create a vertex list functor + vtk_vertex_node<Graph,attr> vn(v_out,obj,x); + + // Iterate through all the vertex and create the vertex list + boost::mpl::for_each< boost::mpl::range_c<int,0,Graph::V_type::max_prop-1> >(vn); + + // write the node string + vn.write(); + + // increment the iterator and counter + ++it; + } + + // return the vertex list + return v_out; + } + + /*! \brief Create the VTK vertex definition + * + * \tparam s_type spatial type of the data + * \tparam attr false x,y,z are set to 0 for each vertex + * + */ + + std::string get_vertex_list() + { + //! vertex node output string + std::string v_out; + + //! For each point create a vertex + for (size_t i = 0 ; i < g.getNVertex() ; i++) + { + v_out += "1 " + std::to_string(i) + "\n"; + } + + // return the vertex list + return v_out; + } + + /*! \brief Get the point data header + * + * \return a string with the point data header for VTK format + * + */ + + std::string get_point_data_header() + { + std::string v_out; + + v_out += "POINT_DATA " + std::to_string(g.getNVertex()) + "\n"; + + return v_out; + } + + /*! \brief Return the edge list + * + * \return the edge list + * + */ + + std::string get_edge_list() + { + //! edge node output string + std::string e_out; + + //! Get an edge iterator + auto it = g.getEdgeIterator(); + + // if there is the next element + while (it.isNext()) + { + // create an edge list functor +// edge_node<Graph> en(e_out,g.edge(it.get())); + + e_out += "2 " + std::to_string(it.source()) + " " + std::to_string(it.target()) + "\n"; + + // increment the operator + ++it; + } + + // return the edge list + return e_out; + } + +public: + + /*! + * + * VTKWriter constructor, it take a graph and write a GraphML format + * + * \param g Graph to write + * + */ + VTKWriter(Graph & g) + :g(g) + {} + + /*! \brief It write a VTK file from a graph + * + * \tparam prp_out which properties to output [default = -1 (all)] + * + * \param file path where to write + * \param name of the graph + * \param file_type specify if it is a VTK BINARY or ASCII file [default = ASCII] + * + */ + + template<int prp = -1> bool write(std::string file, std::string graph_name="Graph", file_type ft = file_type::ASCII) + { + // Check that the Vertex type define x y and z attributes + + if (has_attributes<typename Graph::V_type>::value == false) + { + std::cerr << "Error writing a graph: Vertex must has defines x,y,z properties" << "\n"; + return false; + } + + // Header for the vtk + std::string vtk_header; + // Point list of the VTK + std::string point_list; + // Vertex list of the VTK + std::string vertex_list; + // Graph header + std::string vtk_binary_or_ascii; + // Edge list of the GraphML + std::string edge_list; + // vertex properties header + std::string point_prop_header; + // edge properties header + std::string vertex_prop_header; + // edge properties header + std::string edge_prop_header; + // Data point header + std::string point_data_header; + // Data point + std::string point_data; + + // VTK header + vtk_header = "# vtk DataFile Version 3.0\n" + + graph_name + "\n"; + + // Choose if binary or ASCII + if (ft == file_type::ASCII) + {vtk_header += "ASCII\n";} + else + {vtk_header += "BINARY\n";} + + // Data type for graph is DATASET POLYDATA + vtk_header += "DATASET POLYDATA\n"; + + // point properties header + point_prop_header = get_point_properties_list(); + + // Get point list + point_list = get_point_list<has_attributes<typename Graph::V_type>::value>(); + + // vertex properties header + vertex_prop_header = get_vertex_properties_list(); + + // Get vertex list + vertex_list = get_vertex_list(); + + // Edge properties header + edge_prop_header = get_edge_properties_list(); + + // Get the edge graph list + edge_list = get_edge_list(); + + // Get the point data header + point_data_header = get_point_data_header(); + + // For each property in the vertex type produce a point data + + prop_out<Graph> pp(point_data, g); + + if (prp == -1) + boost::mpl::for_each< boost::mpl::range_c<int,0, Graph::V_type::max_prop> >(pp); + else + boost::mpl::for_each< boost::mpl::range_c<int,prp, prp> >(pp); + + // write the file + std::ofstream ofs(file); + + // Check if the file is open + if (ofs.is_open() == false) + {std::cerr << "Error cannot create the VTK file: " + file;} + + ofs << vtk_header << point_prop_header << point_list << + vertex_prop_header << vertex_list << edge_prop_header << edge_list << point_data_header << point_data; + + // Close the file + + ofs.close(); + + // Completed succefully + return true; + } +}; + + +#endif /* VTKWRITER_GRAPH_HPP_ */ diff --git a/src/VTKWriter_unit_tests.hpp b/src/VTKWriter_unit_tests.hpp new file mode 100644 index 0000000..eb7ed81 --- /dev/null +++ b/src/VTKWriter_unit_tests.hpp @@ -0,0 +1,226 @@ +/* + * VTKWriter_unit_tests.hpp + * + * Created on: May 6, 2015 + * Author: Pietro Incardona + */ + +#ifndef VTKWRITER_UNIT_TESTS_HPP_ +#define VTKWRITER_UNIT_TESTS_HPP_ + +BOOST_AUTO_TEST_SUITE( vtk_writer_test ) + +/* \brief Sub-domain vertex graph node + * + */ + +struct vertex +{ + //! The node contain 3 unsigned long integer for communication computation memory and id + typedef boost::fusion::vector<float,float,float,float,size_t,double,unsigned char,long int> type; + + typedef typename memory_traits_inte<type>::type memory_int; + typedef typename memory_traits_lin<type>::type memory_lin; + + //! type of the positional field + typedef float s_type; + + //! Attributes name + struct attributes + { + static const std::string name[]; + }; + + //! The data + type data; + + //! computation property id in boost::fusion::vector + static const unsigned int x = 0; + //! computation property id in boost::fusion::vector + static const unsigned int y = 1; + //! memory property id in boost::fusion::vector + static const unsigned int z = 2; + //! computation property id in boost::fusion::vector + static const unsigned int prp1 = 3; + //! computation property id in boost::fusion::vector + static const unsigned int prp2 = 4; + //! memory property id in boost::fusion::vector + static const unsigned int prp3 = 5; + //! memory property id in boost::fusion::vector + static const unsigned int prp4 = 6; + //! memory property sub_id in boost::fusion::vector + static const unsigned int prp5 = 7; + + //! total number of properties boost::fusion::vector + static const unsigned int max_prop = 8; + + /*! + * Default constructor + * + */ + vertex() + { + + } + + /*! \brief Initialize the VTKVertex + * + * \param + * + */ + vertex(float x, float y, float z) + { + boost::fusion::at_c<vertex::x>(data) = x; + boost::fusion::at_c<vertex::y>(data) = y; + boost::fusion::at_c<vertex::z>(data) = z; + } +}; + +// use the vertex like the edge +typedef vertex edge; + +const std::string vertex::attributes::name[] = {"x","y","z","prp1","prp2","prp3","prp4","prp5"}; + +BOOST_AUTO_TEST_CASE( vtk_writer_use_graph) +{ + // Create some graphs and output them + + std::cout << "Graph unit test start" << "\n"; + + // Graph + + Graph_CSR<vertex,edge> gr; + + // Create a cube graph + + gr.addVertex(vertex(0.0,0.0,0.0)); + gr.addVertex(vertex(0.0,0.0,1.0)); + gr.addVertex(vertex(0.0,1.0,0.0)); + gr.addVertex(vertex(0.0,1.0,1.0)); + gr.addVertex(vertex(1.0,0.0,0.0)); + gr.addVertex(vertex(1.0,0.0,1.0)); + gr.addVertex(vertex(1.0,1.0,0.0)); + gr.addVertex(vertex(1.0,1.0,1.0)); + + gr.addEdge(0,6); + gr.addEdge(6,4); + gr.addEdge(4,0); + + gr.addEdge(0,2); + gr.addEdge(2,6); + gr.addEdge(6,0); + + gr.addEdge(0,3); + gr.addEdge(3,2); + gr.addEdge(2,0); + + gr.addEdge(0,1); + gr.addEdge(1,3); + gr.addEdge(3,0); + + gr.addEdge(2,7); + gr.addEdge(7,6); + gr.addEdge(6,2); + + gr.addEdge(2,3); + gr.addEdge(3,7); + gr.addEdge(7,2); + + gr.addEdge(4,6); + gr.addEdge(6,7); + gr.addEdge(7,4); + + gr.addEdge(4,7); + gr.addEdge(7,5); + gr.addEdge(5,4); + + gr.addEdge(0,4); + gr.addEdge(4,5); + gr.addEdge(5,0); + + gr.addEdge(0,5); + gr.addEdge(5,1); + gr.addEdge(1,0); + + gr.addEdge(1,5); + gr.addEdge(5,7); + gr.addEdge(7,1); + + gr.addEdge(1,7); + gr.addEdge(7,3); + gr.addEdge(3,1); + + // Write the VTK file + + VTKWriter<Graph_CSR<vertex,edge>,GRAPH> vtk(gr); + vtk.write("vtk_graph.vtk"); + + // check that match + + bool test = compare("vtk_graph.vtk","vtk_graph_test.vtk"); + BOOST_REQUIRE_EQUAL(true,test); +} + +BOOST_AUTO_TEST_CASE( vtk_writer_use_vector_box) +{ + // Create a vector of boxes + openfpm::vector<Box<2,float>> vb; + + vb.add(Box<2,float>({0.2,0.2},{1.0,0.5})); + vb.add(Box<2,float>({0.0,0.0},{0.2,0.2})); + vb.add(Box<2,float>({0.2,0.0},{0.5,0.2})); + vb.add(Box<2,float>({0.5,0.0},{1.0,0.2})); + vb.add(Box<2,float>({0.0,0.2},{0.2,0.5})); + vb.add(Box<2,float>({0.0,0.5},{1.0,1.0})); + + // Create a writer and write + VTKWriter<openfpm::vector<Box<2,float>>,VECTOR_BOX> vtk_box; + vtk_box.add(vb); + vtk_box.write("vtk_box.vtk"); + + // Check that match + bool test = compare("vtk_box.vtk","vtk_box_test.vtk"); + BOOST_REQUIRE_EQUAL(test,true); + + // Create a vector of boxes + openfpm::vector<Box<3,float>> vb2; + + vb2.add(Box<3,float>({0.2,0.2,0.0},{1.0,0.5,0.5})); + vb2.add(Box<3,float>({0.0,0.0,0.0},{0.2,0.2,0.5})); + vb2.add(Box<3,float>({0.2,0.0,0.0},{0.5,0.2,0.5})); + vb2.add(Box<3,float>({0.5,0.0,0.0},{1.0,0.2,0.5})); + vb2.add(Box<3,float>({0.0,0.2,0.0},{0.2,0.5,0.5})); + vb2.add(Box<3,float>({0.0,0.5,0.0},{1.0,1.0,0.5})); + + // Create a writer and write + VTKWriter<openfpm::vector<Box<3,float>>,VECTOR_BOX> vtk_box2; + vtk_box2.add(vb2); + vtk_box2.write("vtk_box_3D.vtk"); + + // Check that match + test = compare("vtk_box_3D.vtk","vtk_box_3D_test.vtk"); + BOOST_REQUIRE_EQUAL(test,true); + + // Create a vector of boxes + openfpm::vector<Box<3,float>> vb3; + vb3.add(Box<3,float>({0.2,0.2,0.5},{1.0,0.5,1.0})); + vb3.add(Box<3,float>({0.0,0.0,0.5},{0.2,0.2,1.0})); + vb3.add(Box<3,float>({0.2,0.0,0.5},{0.5,0.2,1.0})); + vb3.add(Box<3,float>({0.5,0.0,0.5},{1.0,0.2,1.0})); + vb3.add(Box<3,float>({0.0,0.2,0.5},{0.2,0.5,1.0})); + vb3.add(Box<3,float>({0.0,0.5,0.5},{1.0,1.0,1.0})); + + // Create a writer and write + VTKWriter<openfpm::vector<Box<3,float>>,VECTOR_BOX> vtk_box3; + vtk_box3.add(vb2); + vtk_box3.add(vb3); + vtk_box3.write("vtk_box_3D_2.vtk"); + + // Check that match + test = compare("vtk_box_3D_2.vtk","vtk_box_3D_2_test.vtk"); + BOOST_REQUIRE_EQUAL(test,true); +} + +BOOST_AUTO_TEST_SUITE_END() + +#endif /* VTKWRITER_UNIT_TESTS_HPP_ */ diff --git a/src/VTKWriter_vector_box.hpp b/src/VTKWriter_vector_box.hpp new file mode 100644 index 0000000..b217982 --- /dev/null +++ b/src/VTKWriter_vector_box.hpp @@ -0,0 +1,458 @@ +/* + * VTKWriter_vector_box.hpp + * + * Created on: May 5, 2015 + * Author: i-bird + */ + +#ifndef VTKWRITER_VECTOR_BOX_HPP_ +#define VTKWRITER_VECTOR_BOX_HPP_ + +#include <boost/math/special_functions/pow.hpp> +#include "Space/Shape/HyperCube.hpp" +#include <random> +#include "util.hpp" + +template <typename vector> +class v_box +{ +public: + + v_box(const vector & v) + :v(v) + {} + + std::string dataset; + const vector & v; +}; + +/*! + * + * From a basic structure it write a VTK format file in case of a vector of Boxes + * + * \tparam vector type + * + */ + +template <typename vector> +class VTKWriter<vector,VECTOR_BOX> +{ + openfpm::vector<v_box<vector>> v; + + /*! \brief It get the vertex properties list + * + * It get the vertex properties list of the vertex defined as a VTK header + * + * \return a string that define the vertex properties in graphML format + * + */ + + std::string get_point_properties_list() + { + //! vertex property output string + std::string v_out; + + // number of points + size_t np = 0; + + // count the number of points + for (size_t i = 0 ; i < v.size() ; i++) + { + np += v.get(i).v.size() * boost::math::pow<vector::value_type::dims>(2); + } + + // write the number of vertex + v_out += "POINTS " + std::to_string(np) + " float" + "\n"; + + // return the vertex properties string + return v_out; + } + + /*! \brief It get the edge properties list + * + * It get the edge properties list of the edge defined as a GraphML header + * + * \return a string that define the edge properties in graphML format + * + */ + + std::string get_cell_properties_list() + { + //! vertex property output string + std::string e_out; + + //! number of cells and box + size_t nc = 0; + size_t nb = 0; + + // count the number of cells + for (size_t i = 0 ; i < v.size() ; i++) + { + nb += v.get(i).v.size(); + } + + nc = nb * (boost::math::pow<vector::value_type::dims>(2) + 1); + + // write the number of lines + e_out += "CELLS " + std::to_string(nb) + " " + std::to_string(nc) + "\n"; + + // return the vertex properties string + return e_out; + } + + /*! \brief Create the VTK point definition + * + * \tparam s_type spatial type of the data + * \tparam attr false x,y,z are set to 0 for each vertex + * + */ + + std::string get_point_list() + { + //! vertex node output string + std::string v_out; + + //! for each vertex dataset + + for (size_t i = 0 ; i < v.size() ; i++) + { + auto it = v.get(i).v.getIterator(); + + // if there is the next element + while (it.isNext()) + { + // Get the box + auto box = v.get(i).v.get(it.get()); + + // Create an hyper-cube and get the vertex combinations + + HyperCube<vector::value_type::dims> hyp; + std::vector<comb<vector::value_type::dims>> comb = hyp.getCombinations_R(0); + + // Create the box vertex points + + for (size_t j = 0; j < comb.size() ; j++) + { + Point<vector::value_type::dims,float> p; + + for (size_t k = 0 ; k < 3 ; k++) + { + if (k < vector::value_type::dims) + { + if (comb[j].value(k) < 0) + v_out += std::to_string(box.template get<vector::value_type::p1>()[k]) + " "; + else + v_out += std::to_string(box.template get<vector::value_type::p2>()[k]) + " "; + } + else + { + v_out += "0.0"; + } + } + v_out += "\n"; + } + + // increment the iterator and counter + ++it; + } + } + // return the vertex list + return v_out; + } + + /*! \brief Create the VTK vertex definition + * + * \tparam s_type spatial type of the data + * \tparam attr false x,y,z are set to 0 for each vertex + * + */ + + std::string get_cell_list() + { + // base + size_t base = 0; + + //! vertex node output string + std::string v_out; + + //! for each vector in the dataset + + for (size_t i = 0 ; i < v.size() ; i++) + { + auto it = v.get(i).v.getIterator(); + + // for each box + while (it.isNext()) + { + // Output the box vertex id + v_out += std::to_string((size_t)boost::math::pow<vector::value_type::dims>(2)) + " "; + for (size_t k = 0 ; k < boost::math::pow<vector::value_type::dims>(2) ; k++) + { + v_out += " " + std::to_string(base+k); + } + base += boost::math::pow<vector::value_type::dims>(2); + v_out += "\n"; + + ++it; + } + v_out += "\n"; + } + + // return the vertex list + return v_out; + } + + /*! \brief Get the point data header + * + * \return a string with the point data header for VTK format + * + */ + + std::string get_point_data_header() + { + std::string v_out; + + // number of points + size_t np = 0; + + // count the number of points + for (size_t i = 0 ; i < v.size() ; i++) + { + np += v.get(i).v.size() * boost::math::pow<vector::value_type::dims>(2); + } + + + v_out += "POINT_DATA " + std::to_string(np) + "\n"; + + return v_out; + } + + std::string get_cell_types_header() + { + //! vertex property output string + std::string e_out; + + //! number of cells and box + size_t nb = 0; + + // count the number of cells + for (size_t i = 0 ; i < v.size() ; i++) + { + nb += v.get(i).v.size(); + } + + // write the number of lines + e_out += "CELL_TYPES " + std::to_string(nb) + "\n"; + + // return the vertex properties string + return e_out; + } + + std::string get_cell_types_list() + { + // Cell id + size_t cell_id; + if (vector::value_type::dims == 2) + cell_id = 8; + else + cell_id = 11; + + //! vertex node output string + std::string v_out; + + //! for each vector in the dataset + + for (size_t i = 0 ; i < v.size() ; i++) + { + auto it = v.get(i).v.getIterator(); + + // for each box + while (it.isNext()) + { + v_out += std::to_string(cell_id) + "\n"; + + ++it; + } + } + + // return the vertex list + return v_out; + } + + + std::string get_cell_data_header() + { + //! vertex property output string + std::string e_out; + + //! number of cells and box + size_t nb = 0; + + // count the number of cells + for (size_t i = 0 ; i < v.size() ; i++) + { + nb += v.get(i).v.size(); + } + + // write the number of lines + e_out += "CELL_DATA " + std::to_string(nb) + "\n"; + e_out += "COLOR_SCALARS data 4\n"; + + // return the vertex properties string + return e_out; + } + + std::string get_cell_data_list() + { + // random engine + std::default_random_engine generator; + std::uniform_real_distribution<float> d(0.0,1.0); + + //! vertex node output string + std::string v_out; + + size_t col_group = 0; + + //! for each vector in the dataset + for (size_t i = 0 ; i < v.size() ; i++) + { + auto it = v.get(i).v.getIterator(); + + // for each box + while (it.isNext()) + { + // write a color + v_out += getColor(col_group,d,generator).toString() + " 1.0" + "\n"; + + ++it; + } + v_out += "\n"; + col_group++; + } + + // return the vertex list + return v_out; + } + +public: + + /*! + * + * VTKWriter constructor + * + */ + VTKWriter() + {} + + /*! \brief Add box vector dataset + * + * \param v vector to add + * + */ + void add(const vector & vc) + { + v_box<vector> t(vc); + + v.add(t); + } + + /*! \brief It write a VTK file from a graph + * + * \tparam prp_out which properties to output [default = -1 (all)] + * + * \param file path where to write + * \param name of the graph + * \param file_type specify if it is a VTK BINARY or ASCII file [default = ASCII] + * + */ + + template<int prp = -1> bool write(std::string file, std::string graph_name="Graph", file_type ft = file_type::ASCII) + { + // Header for the vtk + std::string vtk_header; + // Point list of the VTK + std::string point_list; + // Vertex list of the VTK + std::string cell_list; + // Graph header + std::string vtk_binary_or_ascii; + // Edge list of the GraphML + std::string edge_list; + // vertex properties header + std::string point_prop_header; + // edge properties header + std::string cell_prop_header; + // edge properties header + std::string edge_prop_header; + // Data point header + std::string point_data_header; + // Data point + std::string point_data; + // Cell type header + std::string cell_types_header; + // Cell type list + std::string cell_types_list; + // Cell data header + std::string cell_data_header; + // Cell data list + std::string cell_data_list; + + // VTK header + vtk_header = "# vtk DataFile Version 3.0\n" + + graph_name + "\n"; + + // Choose if binary or ASCII + if (ft == file_type::ASCII) + {vtk_header += "ASCII\n";} + else + {vtk_header += "BINARY\n";} + + // Data type for graph is DATASET POLYDATA + vtk_header += "DATASET UNSTRUCTURED_GRID\n"; + + // point properties header + point_prop_header = get_point_properties_list(); + + // Get point list + point_list = get_point_list(); + + // cell properties header + cell_prop_header = get_cell_properties_list(); + + // Get cell list + cell_list = get_cell_list(); + + // Get cell types + cell_types_header = get_cell_types_header(); + + // Get cell type list + cell_types_list = get_cell_types_list(); + + // Get cell data header + cell_data_header = get_cell_data_header(); + + // Get cell data list + cell_data_list = get_cell_data_list(); + + // write the file + std::ofstream ofs(file); + + // Check if the file is open + if (ofs.is_open() == false) + {std::cerr << "Error cannot create the VTK file: " + file;} + + ofs << vtk_header << point_prop_header << point_list << + cell_prop_header << cell_list << cell_types_header << cell_types_list << cell_data_header << cell_data_list; + + // Close the file + + ofs.close(); + + // Completed succefully + return true; + } +}; + + + +#endif /* VTKWRITER_VECTOR_BOX_HPP_ */ diff --git a/vtk_graph_test.vtk b/vtk_graph_test.vtk new file mode 100644 index 0000000..4d9aa58 --- /dev/null +++ b/vtk_graph_test.vtk @@ -0,0 +1,140 @@ +# vtk DataFile Version 3.0 +Graph +ASCII +DATASET POLYDATA +POINTS 8 float +0.000000 0.000000 0.000000 +0.000000 0.000000 1.000000 +0.000000 1.000000 0.000000 +0.000000 1.000000 1.000000 +1.000000 0.000000 0.000000 +1.000000 0.000000 1.000000 +1.000000 1.000000 0.000000 +1.000000 1.000000 1.000000 +VERTICES 8 16 +1 0 +1 1 +1 2 +1 3 +1 4 +1 5 +1 6 +1 7 +LINES 36 108 +2 0 6 +2 0 2 +2 0 3 +2 0 1 +2 0 4 +2 0 5 +2 1 3 +2 1 0 +2 1 5 +2 1 7 +2 2 6 +2 2 0 +2 2 7 +2 2 3 +2 3 2 +2 3 0 +2 3 7 +2 3 1 +2 4 0 +2 4 6 +2 4 7 +2 4 5 +2 5 4 +2 5 0 +2 5 1 +2 5 7 +2 6 4 +2 6 0 +2 6 2 +2 6 7 +2 7 6 +2 7 2 +2 7 4 +2 7 5 +2 7 1 +2 7 3 +POINT_DATA 8 +SCALARS x float +LOOKUP_TABLE default +0.000000 +0.000000 +0.000000 +0.000000 +1.000000 +1.000000 +1.000000 +1.000000 +SCALARS y float +LOOKUP_TABLE default +0.000000 +0.000000 +1.000000 +1.000000 +0.000000 +0.000000 +1.000000 +1.000000 +SCALARS z float +LOOKUP_TABLE default +0.000000 +1.000000 +0.000000 +1.000000 +0.000000 +1.000000 +0.000000 +1.000000 +SCALARS prp1 float +LOOKUP_TABLE default +0.000000 +0.000000 +0.000000 +0.000000 +0.000000 +0.000000 +0.000000 +0.000000 +SCALARS prp2 unsigned_long +LOOKUP_TABLE default +0 +0 +0 +0 +0 +0 +0 +0 +SCALARS prp3 double +LOOKUP_TABLE default +0.000000 +0.000000 +0.000000 +0.000000 +0.000000 +0.000000 +0.000000 +0.000000 +SCALARS prp4 unsigned_char +LOOKUP_TABLE default +0 +0 +0 +0 +0 +0 +0 +0 +SCALARS prp5 long +LOOKUP_TABLE default +0 +0 +0 +0 +0 +0 +0 +0 -- GitLab