@@ -29,106 +29,134 @@ void TestFileCreation::tearDown() {
2929}
3030
3131void TestFileCreation::test_ply_file () {
32- // set number of threads to 1
33- omp_set_num_threads (1 );
34-
35- // read scalar field
36- ScalarField sf (" co_2pi_x.cub" , ScalarFieldInputFileType::SFF_CUB);
37- sf.read ();
38-
39- // perform marching cubes algorithm
40- IsoSurface is (&sf);
41- is.marching_cubes (0.01 );
42-
43- // construct mesh
44- IsoSurfaceMesh ism (&sf, &is);
45- ism.construct_mesh (true );
32+ const auto ref = this ->generate_mesh ();
33+ this ->assert_ply_shape (" test.ply" , ref);
34+ }
4635
47- // create file
48- ism.write_to_file (" test.ply" , " co molecule 2pi_x orbital test" , " molecule" );
36+ void TestFileCreation::test_stl_file () {
37+ const auto ref = this ->generate_mesh ();
38+ this ->assert_stl_shape (" test.stl" , ref);
39+ }
4940
50- // test md5sum
51- CPPUNIT_ASSERT_EQUAL (this ->md5 (" test.ply" ), std::string (" ec143f6ebf9b72761803c3b091f54bf3" ));
41+ void TestFileCreation::test_obj_file () {
42+ const auto ref = this ->generate_mesh ();
43+ this ->assert_obj_shape (" test.obj" , ref);
5244}
5345
54- void TestFileCreation::test_stl_file () {
46+ TestFileCreation::MeshReference TestFileCreation::generate_mesh () const {
5547 // set number of threads to 1
5648 omp_set_num_threads (1 );
5749
5850 // read scalar field
5951 ScalarField sf (" co_2pi_x.cub" , ScalarFieldInputFileType::SFF_CUB);
6052 sf.read ();
61-
53+
6254 // perform marching cubes algorithm
6355 IsoSurface is (&sf);
6456 is.marching_cubes (0.01 );
65-
57+
6658 // construct mesh
6759 IsoSurfaceMesh ism (&sf, &is);
6860 ism.construct_mesh (true );
6961
70- // create file
62+ // create files for all supported output formats
63+ ism.write_to_file (" test.ply" , " co molecule 2pi_x orbital test" , " molecule" );
7164 ism.write_to_file (" test.stl" , " co molecule 2pi_x orbital test" , " molecule" );
65+ ism.write_to_file (" test.obj" , " co molecule 2pi_x orbital test" , " molecule" );
7266
73- // test md5sum
74- CPPUNIT_ASSERT_EQUAL (this ->md5 (" test.stl" ), std::string (" c2194ba639caf5092654862bb9f93298" ));
67+ return {
68+ ism.get_vertices ().size (),
69+ ism.get_normals ().size (),
70+ ism.get_texcoords ().size () / 6
71+ };
7572}
7673
77- void TestFileCreation::test_obj_file () {
78- // set number of threads to 1
79- omp_set_num_threads (1 );
74+ void TestFileCreation::assert_obj_shape (const std::string& filename, const MeshReference& ref) const {
75+ CPPUNIT_ASSERT (std::filesystem::exists (filename));
76+
77+ std::ifstream infile (filename);
78+ CPPUNIT_ASSERT (infile.good ());
79+
80+ std::string line;
81+ size_t vertex_lines = 0 ;
82+ size_t normal_lines = 0 ;
83+ size_t face_lines = 0 ;
84+
85+ while (std::getline (infile, line)) {
86+ if (line.rfind (" v " , 0 ) == 0 ) {
87+ vertex_lines++;
88+ } else if (line.rfind (" vn " , 0 ) == 0 ) {
89+ normal_lines++;
90+ } else if (line.rfind (" f " , 0 ) == 0 ) {
91+ face_lines++;
92+ }
93+ }
8094
81- // read scalar field
82- ScalarField sf (" co_2pi_x.cub" , ScalarFieldInputFileType::SFF_CUB);
83- sf.read ();
84-
85- // perform marching cubes algorithm
86- IsoSurface is (&sf);
87- is.marching_cubes (0.01 );
88-
89- // construct mesh
90- IsoSurfaceMesh ism (&sf, &is);
91- ism.construct_mesh (true );
95+ CPPUNIT_ASSERT_EQUAL (ref.vertices , vertex_lines);
96+ CPPUNIT_ASSERT_EQUAL (ref.normals , normal_lines);
97+ CPPUNIT_ASSERT_EQUAL (ref.triangles , face_lines);
98+ }
9299
93- // create file
94- ism.write_to_file (" test.obj" , " co molecule 2pi_x orbital test" , " molecule" );
100+ void TestFileCreation::assert_ply_shape (const std::string& filename, const MeshReference& ref) const {
101+ CPPUNIT_ASSERT (std::filesystem::exists (filename));
102+
103+ std::ifstream infile (filename, std::ios::binary);
104+ CPPUNIT_ASSERT (infile.good ());
105+
106+ std::string line;
107+ size_t header_size = 0 ;
108+ size_t header_vertices = 0 ;
109+ size_t header_faces = 0 ;
110+ bool seen_ply = false ;
111+ bool seen_binary_format = false ;
112+
113+ while (std::getline (infile, line)) {
114+ header_size += line.size () + 1 ;
115+
116+ if (line == " ply" ) {
117+ seen_ply = true ;
118+ }
119+ if (line == " format binary_little_endian 1.0" || line == " format binary_big_endian 1.0" ) {
120+ seen_binary_format = true ;
121+ }
122+ if (line.rfind (" element vertex " , 0 ) == 0 ) {
123+ header_vertices = std::stoul (line.substr (15 ));
124+ }
125+ if (line.rfind (" element face " , 0 ) == 0 ) {
126+ header_faces = std::stoul (line.substr (13 ));
127+ }
128+ if (line == " end_header" ) {
129+ break ;
130+ }
131+ }
132+
133+ CPPUNIT_ASSERT (seen_ply);
134+ CPPUNIT_ASSERT (seen_binary_format);
135+ CPPUNIT_ASSERT_EQUAL (ref.vertices , header_vertices);
136+ CPPUNIT_ASSERT_EQUAL (ref.triangles , header_faces);
95137
96- // test md5sum
97- CPPUNIT_ASSERT_EQUAL (this ->md5 (" test.obj" ), std::string (" e2b3e09f9c010dac99a7bc0137c187ec" ));
138+ const size_t expected_binary_size =
139+ ref.vertices * (6 * sizeof (float )) +
140+ ref.triangles * (sizeof (uint8_t ) + 3 * sizeof (unsigned int ));
141+
142+ CPPUNIT_ASSERT_EQUAL (header_size + expected_binary_size, (size_t )std::filesystem::file_size (filename));
98143}
99144
100- /* *
101- * @brief Calculate MD5 checksum of a file
102- *
103- * @param[in] name Path to the file
104- *
105- * @return 32 byte string containing md5 checksum
106- */
107- std::string TestFileCreation::md5 (const std::string& filename) {
108- // read the file
109- std::ifstream mfile (filename, std::ios::binary | std::ios::ate);
110- std::streamsize size = mfile.tellg ();
111- mfile.seekg (0 , std::ios::beg);
112- char buffer[size];
113- mfile.read (buffer, size);
114- mfile.close ();
115-
116- // output variable for hash
117- unsigned char hash[MD5_DIGEST_LENGTH];
118-
119- // calculate the md5 hash
120- EVP_MD_CTX *ctx = EVP_MD_CTX_create ();
121- EVP_MD_CTX_init (ctx);
122- const EVP_MD *md_type = EVP_md5 ();
123- EVP_DigestInit_ex (ctx, md_type, NULL );
124- EVP_DigestUpdate (ctx, buffer, size);
125- EVP_DigestFinal_ex (ctx, hash, NULL );
126- EVP_MD_CTX_destroy (ctx);
127-
128- // output as a 32-byte hex-string
129- std::stringstream ss;
130- for (int i = 0 ; i < MD5_DIGEST_LENGTH; i++){
131- ss << std::hex << std::setw (2 ) << std::setfill (' 0' ) << static_cast <int >( hash[i] );
132- }
133- return ss.str ();
134- }
145+ void TestFileCreation::assert_stl_shape (const std::string& filename, const MeshReference& ref) const {
146+ CPPUNIT_ASSERT (std::filesystem::exists (filename));
147+
148+ std::ifstream infile (filename, std::ios::binary);
149+ CPPUNIT_ASSERT (infile.good ());
150+
151+ // skip header
152+ infile.seekg (80 , std::ios::beg);
153+
154+ uint32_t triangle_count = 0 ;
155+ infile.read (reinterpret_cast <char *>(&triangle_count), sizeof (uint32_t ));
156+ CPPUNIT_ASSERT (infile.good ());
157+
158+ CPPUNIT_ASSERT_EQUAL (static_cast <uint32_t >(ref.triangles ), triangle_count);
159+
160+ const size_t expected_size = 80 + sizeof (uint32_t ) + ref.triangles * (12 * sizeof (float ) + sizeof (uint16_t ));
161+ CPPUNIT_ASSERT_EQUAL (expected_size, (size_t )std::filesystem::file_size (filename));
162+ }
0 commit comments