test_sstl_load_ascii.c (14579B)
1 /* Copyright (C) 2015, 2016, 2019, 2021, 2023, 2025 |Méso|Star> (contact@meso-star.com) 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 3 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU Lesser General Public License for more details. 12 * 13 * You should have received a copy of the GNU Lesser General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 15 16 #define _POSIX_C_SOURCE 200112L /* fork */ 17 18 #include "sstl.h" 19 20 #include <rsys/float3.h> 21 #include <rsys/mem_allocator.h> 22 23 #include <string.h> 24 #include <unistd.h> /* fork, pipe */ 25 26 /******************************************************************************* 27 * Helper functions 28 ******************************************************************************/ 29 static void 30 check_api(struct sstl* sstl) 31 { 32 const char* filename = "test.stl"; 33 FILE* fp = NULL; 34 struct sstl_desc desc = SSTL_DESC_NULL; 35 36 CHK((fp = fopen(filename, "w+")) != NULL); 37 rewind(fp); 38 39 #define CHECK_EMPTY_FILE { \ 40 CHK(sstl_get_desc(sstl, &desc) == RES_OK); \ 41 CHK(!strcmp(desc.filename, filename)); \ 42 CHK(desc.type == SSTL_ASCII); \ 43 CHK(desc.solid_name == NULL); \ 44 CHK(desc.vertices_count == 0); \ 45 CHK(desc.triangles_count == 0); \ 46 } (void)0 47 48 CHK(sstl_load(sstl, NULL) == RES_BAD_ARG); 49 CHK(sstl_load(NULL, filename) == RES_BAD_ARG); 50 CHK(sstl_load(sstl, "none.stl") == RES_IO_ERR); 51 CHK(sstl_load(sstl, filename) == RES_OK); /* Empty file should be OK */ 52 53 CHK(sstl_get_desc(sstl, NULL) == RES_BAD_ARG); 54 CHK(sstl_get_desc(NULL, &desc) == RES_BAD_ARG); 55 CHECK_EMPTY_FILE; 56 57 CHK(sstl_load_ascii(sstl, NULL) == RES_BAD_ARG); 58 CHK(sstl_load_ascii(NULL, filename) == RES_BAD_ARG); 59 CHK(sstl_load_ascii(sstl, "none.stl") == RES_IO_ERR); 60 CHK(sstl_load_ascii(sstl, filename) == RES_OK); /* Empty file should be OK */ 61 CHECK_EMPTY_FILE; 62 63 CHK(sstl_load_stream(NULL, fp, filename) == RES_BAD_ARG); 64 CHK(sstl_load_stream(sstl, NULL, filename) == RES_BAD_ARG); 65 CHK(sstl_load_stream(sstl, fp, NULL) == RES_BAD_ARG); 66 CHK(sstl_load_stream(sstl, fp, filename) == RES_OK); 67 CHECK_EMPTY_FILE; 68 69 CHK(sstl_load_stream_ascii(NULL, fp, filename) == RES_BAD_ARG); 70 CHK(sstl_load_stream_ascii(sstl, NULL, filename) == RES_BAD_ARG); 71 CHK(sstl_load_stream_ascii(sstl, fp, NULL) == RES_BAD_ARG); 72 CHK(sstl_load_stream_ascii(sstl, fp, filename) == RES_OK); 73 CHECK_EMPTY_FILE; 74 75 #undef CHECK_EMPTY_FILE 76 77 CHK(fclose(fp) == 0); 78 } 79 80 static void 81 check_no_triangle(struct sstl* sstl) 82 { 83 static const char* stl = 84 "solid my_solid\n" 85 "endsolid my_solid\n"; 86 const char* filename = "empty.stl"; 87 FILE* fp = NULL; 88 struct sstl_desc desc = SSTL_DESC_NULL; 89 90 CHK(sstl); 91 92 CHK((fp = fopen(filename, "w+")) != NULL); 93 CHK(fwrite(stl, sizeof(char), strlen(stl), fp) == strlen(stl)); 94 rewind(fp); 95 96 CHK(sstl_load_stream(sstl, fp, filename) == RES_OK); 97 CHK(sstl_get_desc(sstl, &desc) == RES_OK); 98 CHK(desc.type == SSTL_ASCII); 99 CHK(!strcmp(desc.filename, filename)); 100 CHK(!strcmp(desc.solid_name, "my_solid")); 101 CHK(desc.vertices_count == 0); 102 CHK(desc.triangles_count == 0); 103 104 CHK(fclose(fp) == 0); 105 } 106 107 static void 108 check_1_triangle(struct sstl* sstl) 109 { 110 static const char* stl = 111 "solid\n" 112 " facet normal 0.0 -1.0 0.0\n" 113 " outer loop\n" 114 " vertex 0.0 0.0 0.0\n" 115 " vertex 1.0 0.0 0.0\n" 116 " vertex 0.0 0.0 1.0\n" 117 " endloop\n" 118 " endfacet\n" 119 "endsolid"; 120 const char* filename = "1_triangle.stl"; 121 FILE* fp = NULL; 122 float v[3] = {0,0,0}; 123 struct sstl_desc desc = SSTL_DESC_NULL; 124 125 CHK(sstl); 126 127 CHK((fp = fopen(filename, "w")) != NULL); 128 CHK(fwrite(stl, sizeof(char), strlen(stl), fp) == strlen(stl)); 129 CHK(fclose(fp) == 0); 130 131 CHK(sstl_load_ascii(sstl, filename) == RES_OK); 132 CHK(sstl_get_desc(sstl, &desc) == RES_OK); 133 134 CHK(desc.type == SSTL_ASCII); 135 CHK(!strcmp(desc.filename, filename)); 136 CHK(desc.solid_name == NULL); 137 CHK(desc.vertices_count == 3); 138 CHK(desc.triangles_count == 1); 139 CHK(desc.indices[0] == 0); 140 CHK(desc.indices[1] == 1); 141 CHK(desc.indices[2] == 2); 142 CHK(f3_eq(desc.vertices + 0*3, f3(v, 0.f, 0.f, 0.f)) == 1); 143 CHK(f3_eq(desc.vertices + 1*3, f3(v, 1.f, 0.f, 0.f)) == 1); 144 CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 1.f)) == 1); 145 CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1); 146 } 147 148 static void 149 check_1_triangle_with_noise(struct sstl* sstl) 150 { 151 static const char* stl = 152 "solid My Solid\n" 153 "\n" 154 " facet normal 0.0 -1.0 0.0\n" 155 " outer loop hophophophophop\n" 156 " vertex\t 0.0 0.0 0.0\n" 157 " vertex 1.0 0.0 0.0 \taaa\n" 158 " vertex 0.0 0.0 1.0\n" 159 " endloop \n" 160 " endfacet \t\t\t noise\n" 161 "endsolid pouet\n"; 162 const char* filename = "1_triangle_with_noise.stl"; 163 FILE* fp = NULL; 164 float v[3] = {0,0,0}; 165 struct sstl_desc desc = SSTL_DESC_NULL; 166 167 CHK(sstl); 168 169 CHK((fp = fopen(filename, "w")) != NULL); 170 CHK(fwrite(stl, sizeof(char), strlen(stl), fp) == strlen(stl)); 171 CHK(fclose(fp) == 0); 172 173 CHK(sstl_load(sstl, filename) == RES_OK); 174 175 CHK(sstl_get_desc(sstl, &desc) == RES_OK); 176 CHK(desc.type == SSTL_ASCII); 177 CHK(!strcmp(desc.filename, filename)); 178 CHK(!strcmp(desc.solid_name, "My Solid")); 179 CHK(desc.vertices_count == 3); 180 CHK(desc.triangles_count == 1); 181 CHK(desc.indices[0] == 0); 182 CHK(desc.indices[1] == 1); 183 CHK(desc.indices[2] == 2); 184 CHK(f3_eq(desc.vertices + 0*3, f3(v, 0.f, 0.f, 0.f)) == 1); 185 CHK(f3_eq(desc.vertices + 1*3, f3(v, 1.f, 0.f, 0.f)) == 1); 186 CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 1.f)) == 1); 187 CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1); 188 } 189 190 static void 191 check_1_triangle_no_normal(struct sstl* sstl) 192 { 193 static const char* stl = 194 "solid\n" 195 " facet normal 0.0 0.0 0.0\n" 196 " outer loop\n" 197 " vertex 0.0 0.0 0.0\n" 198 " vertex 1.0 0.0 0.0\n" 199 " vertex 0.0 0.0 1.0\n" 200 " endloop\n" 201 " endfacet\n" 202 "endsolid"; 203 const char* filename = "1_triangle_no_normal.stl"; 204 FILE* fp = NULL; 205 float v[3] = {0,0,0}; 206 struct sstl_desc desc = SSTL_DESC_NULL; 207 208 CHK((fp = fopen(filename, "w+")) != NULL); 209 CHK(fwrite(stl, sizeof(char), strlen(stl), fp) == strlen(stl)); 210 rewind(fp); 211 212 CHK(sstl_load_stream(sstl, fp, filename) == RES_OK); 213 214 CHK(sstl_get_desc(sstl, &desc) == RES_OK); 215 CHK(desc.type == SSTL_ASCII); 216 CHK(!strcmp(desc.filename, filename)); 217 CHK(desc.solid_name == NULL); 218 CHK(desc.vertices_count == 3); 219 CHK(desc.triangles_count == 1); 220 CHK(desc.indices[0] == 0); 221 CHK(desc.indices[1] == 1); 222 CHK(desc.indices[2] == 2); 223 CHK(f3_eq(desc.vertices + 0*3, f3(v, 0.f, 0.f, 0.f)) == 1); 224 CHK(f3_eq(desc.vertices + 1*3, f3(v, 1.f, 0.f, 0.f)) == 1); 225 CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 1.f)) == 1); 226 227 /* Normal is automatically calculated */ 228 CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1); 229 230 CHK(fclose(fp) == 0); 231 } 232 233 static void 234 check_invalid_file(struct sstl* sstl) 235 { 236 static const char* bad[] = { 237 /* "endsolid" is missing */ 238 "solid\n" 239 " facet normal 0.0 -1.0 0.0\n" 240 " outer loop\n" 241 " vertex 0.0 0.0 0.0\n" 242 " vertex 1.0 0.0 0.0\n" 243 " vertex 0.0 0.0 1.0\n" 244 " endloop\n" 245 " endfacet\n", 246 247 /* "solid" is missing */ 248 " facet normal 0.0 -1.0 0.0\n" 249 " outer loop\n" 250 " vertex 0.0 0.0 0.0\n" 251 " vertex 1.0 0.0 0.0\n" 252 " vertex 0.0 0.0 1.0\n" 253 " endloop\n" 254 " endfacet\n" 255 "endsolid\n", 256 257 /* "normal" is missing */ 258 "solid\n" 259 " facet 0.0 -1.0 0.0\n" 260 " outer loop\n" 261 " vertex 0.0 0.0 0.0\n" 262 " vertex 1.0 0.0 0.0\n" 263 " vertex 0.0 0.0 1.0\n" 264 " endloop\n" 265 " endfacet\n" 266 "endsolid\n", 267 268 /* "facet" is missing */ 269 "solid\n" 270 " normal 0.0 -1.0 0.0\n" 271 " outer loop\n" 272 " vertex 0.0 0.0 0.0\n" 273 " vertex 1.0 0.0 0.0\n" 274 " vertex 0.0 0.0 1.0\n" 275 " endloop\n" 276 " endfacet\n" 277 "endsolid\n", 278 279 /* invalid vertex coordinate */ 280 "solid\n" 281 " facet normal 0.0 -1.0 0.0\n" 282 " outer loop\n" 283 " vertex 0.0 0.0 a.0\n" 284 " vertex 1.0 0.0 0.0\n" 285 " vertex 0.0 0.0 1.0\n" 286 " endloop\n" 287 " endfacet\n" 288 "endsolid\n", 289 290 /* Not enough vertices */ 291 "solid\n" 292 " facet normal 0.0 -1.0 0.0\n" 293 " outer loop\n" 294 " vertex 1.0 0.0 0.0\n" 295 " vertex 0.0 0.0 1.0\n" 296 " endloop\n" 297 " endfacet\n" 298 "endsolid\n", 299 300 /* "outer loop" is missing */ 301 "solid\n" 302 " facet normal 0.0 -1.0 0.0\n" 303 " vertex 0.0 0.0 0.0\n" 304 " vertex 1.0 0.0 0.0\n" 305 " vertex 0.0 0.0 1.0\n" 306 " endfacet\n" 307 "endsolid\n" 308 }; 309 const size_t nbads = sizeof(bad)/sizeof(const char*); 310 FILE* fp = NULL; 311 size_t i; 312 313 CHK(sstl != NULL); 314 315 FOR_EACH(i, 0, nbads) { 316 CHK((fp = tmpfile()) != NULL); 317 CHK(fwrite(bad[i], sizeof(char), strlen(bad[i]), fp) == strlen(bad[i])); 318 rewind(fp); 319 CHK(sstl_load_stream(sstl, fp, "invalid StL") == RES_BAD_ARG); 320 CHK(fclose(fp) == 0); 321 } 322 } 323 324 static void 325 check_tetrahedron(struct sstl* sstl) 326 { 327 static const char* tetrahedron[] = { 328 "solid cube corner\n", 329 " facet normal 0.0 -1.0 0.0\n", 330 " outer loop\n", 331 " vertex 0.0 0.0 0.0\n", 332 " vertex 0.1 0.0 0.0\n", 333 " vertex 0.0 0.0 0.1\n", 334 " endloop\n", 335 " endfacet\n", 336 " facet normal 0.0 0.0 -1.0\n", 337 " outer loop\n", 338 " vertex 0.0 0.0 0.0\n", 339 " vertex 0.0 0.1 0.0\n", 340 " vertex 0.1 0.0 0.0\n", 341 " endloop\n", 342 " endfacet\n", 343 " facet normal -1.0 0.0 0.0\n", 344 " outer loop\n", 345 " vertex 0.0 0.0 0.0\n", 346 " vertex 0.0 0.0 0.1\n", 347 " vertex 0.0 0.1 0.0\n", 348 " endloop\n", 349 " endfacet\n", 350 " facet normal 0.577 0.577 0.577\n", 351 " outer loop\n", 352 " vertex 0.1 0.0 0.0\n", 353 " vertex 0.0 0.1 0.0\n", 354 " vertex 0.0 0.0 0.1\n", 355 " endloop\n", 356 " endfacet\n", 357 "endsolid\n" 358 }; 359 FILE* fp = NULL; 360 const char* filename = "Tetrahedron"; 361 const size_t nlines = sizeof(tetrahedron)/sizeof(const char*); 362 struct sstl_desc desc = SSTL_DESC_NULL; 363 float v[3]; 364 size_t i; 365 366 CHK(sstl != NULL); 367 368 CHK((fp = tmpfile()) != NULL); 369 FOR_EACH(i, 0, nlines) { 370 const size_t n = strlen(tetrahedron[i]); 371 CHK(fwrite(tetrahedron[i], sizeof(char), n, fp) == n); 372 } 373 rewind(fp); 374 375 CHK(sstl_load_stream_ascii(sstl, fp, filename) == RES_OK); 376 CHK(fclose(fp) == 0); 377 378 CHK(sstl_get_desc(sstl, &desc) == RES_OK); 379 CHK(!strcmp(desc.filename, filename)); 380 CHK(!strcmp(desc.solid_name, "cube corner")); 381 CHK(desc.type == SSTL_ASCII); 382 CHK(desc.vertices_count == 4); 383 CHK(desc.triangles_count == 4); 384 385 CHK(f3_eq(desc.vertices + desc.indices[0]*3, f3(v, 0.0f, 0.0f, 0.0f)) == 1); 386 CHK(f3_eq(desc.vertices + desc.indices[1]*3, f3(v, 0.1f, 0.0f, 0.0f)) == 1); 387 CHK(f3_eq(desc.vertices + desc.indices[2]*3, f3(v, 0.0f, 0.0f, 0.1f)) == 1); 388 CHK(f3_eq(desc.vertices + desc.indices[3]*3, f3(v, 0.0f, 0.0f, 0.0f)) == 1); 389 CHK(f3_eq(desc.vertices + desc.indices[4]*3, f3(v, 0.0f, 0.1f, 0.0f)) == 1); 390 CHK(f3_eq(desc.vertices + desc.indices[5]*3, f3(v, 0.1f, 0.0f, 0.0f)) == 1); 391 CHK(f3_eq(desc.vertices + desc.indices[6]*3, f3(v, 0.0f, 0.0f, 0.0f)) == 1); 392 CHK(f3_eq(desc.vertices + desc.indices[7]*3, f3(v, 0.0f, 0.0f, 0.1f)) == 1); 393 CHK(f3_eq(desc.vertices + desc.indices[8]*3, f3(v, 0.0f, 0.1f, 0.0f)) == 1); 394 CHK(f3_eq(desc.vertices + desc.indices[9]*3, f3(v, 0.1f, 0.0f, 0.0f)) == 1); 395 CHK(f3_eq(desc.vertices + desc.indices[10]*3, f3(v, 0.0f, 0.1f, 0.0f)) == 1); 396 CHK(f3_eq(desc.vertices + desc.indices[11]*3, f3(v, 0.0f, 0.0f, 0.1f)) == 1); 397 398 CHK(f3_eq(desc.normals + 0*3, f3(v, 0.f,-1.f, 0.f)) == 1); 399 CHK(f3_eq(desc.normals + 1*3, f3(v, 0.f, 0.f,-1.f)) == 1); 400 CHK(f3_eq(desc.normals + 2*3, f3(v,-1.f, 0.f, 0.f)) == 1); 401 f3_normalize(v, f3(v, 1.f, 1.f, 1.f)); 402 CHK(f3_eq_eps(desc.normals + 3*3, v, 1.e-6f) == 1); 403 } 404 405 static void 406 check_no_seekable_file(struct sstl* sstl) 407 { 408 const char* stl = 409 "solid Triangle\n" 410 " facet normal 0.0 -1.0 0.0\n" 411 " outer loop\n" 412 " vertex 1.0 0.0 0.0\n" 413 " vertex 0.0 0.0 1.0\n" 414 " vertex 0.0 0.0 0.0\n" 415 " endloop\n" 416 " endfacet\n" 417 "endsolid"; 418 int fd[2] = {0,0}; 419 pid_t pid = 0; 420 421 CHK(pipe(fd) == 0); 422 CHK((pid = fork()) != -1); 423 424 if(pid == 0) { /* Child process */ 425 CHK(close(fd[0]) == 0); /* Close the unused input stream */ 426 CHK(sstl_ref_put(sstl) == RES_OK); /* Release the unused sstl */ 427 428 CHK(write(fd[1], stl, strlen(stl)) == (int)strlen(stl)); 429 CHK(close(fd[1]) == 0); 430 exit(0); 431 432 } else { /* Parent process */ 433 struct sstl_desc desc = SSTL_DESC_NULL; 434 float v[3]; 435 FILE* fp = NULL; 436 const char* filename = "Piped StL"; 437 CHK(close(fd[1]) == 0); /* Close the unused output stream */ 438 439 CHK(fp = fdopen(fd[0], "r")); 440 CHK(sstl_load_stream(sstl, fp, filename) == RES_BAD_ARG); 441 CHK(sstl_load_stream_ascii(sstl, fp, filename) == RES_OK); 442 CHK(fclose(fp) == 0); 443 444 CHK(sstl_get_desc(sstl, &desc) == RES_OK); 445 CHK(desc.type == SSTL_ASCII); 446 CHK(!strcmp(desc.filename, filename)); 447 CHK(!strcmp(desc.solid_name, "Triangle")); 448 CHK(desc.vertices_count == 3); 449 CHK(desc.triangles_count == 1); 450 CHK(desc.indices[0] == 0); 451 CHK(desc.indices[1] == 1); 452 CHK(desc.indices[2] == 2); 453 CHK(f3_eq(desc.vertices + 0*3, f3(v, 1.f, 0.f, 0.f)) == 1); 454 CHK(f3_eq(desc.vertices + 1*3, f3(v, 0.f, 0.f, 1.f)) == 1); 455 CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 0.f)) == 1); 456 CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1); 457 } 458 } 459 460 /******************************************************************************* 461 * The test 462 ******************************************************************************/ 463 int 464 main(int argc, char** argv) 465 { 466 struct sstl* sstl = NULL; 467 (void)argc, (void)argv; 468 469 CHK(sstl_create(NULL, NULL, 3, &sstl) == RES_OK); 470 471 check_api(sstl); 472 check_no_triangle(sstl); 473 check_1_triangle(sstl); 474 check_1_triangle_with_noise(sstl); 475 check_1_triangle_no_normal(sstl); 476 check_invalid_file(sstl); 477 check_tetrahedron(sstl); 478 check_no_seekable_file(sstl); 479 480 CHK(sstl_ref_put(sstl) == RES_OK); 481 CHK(mem_allocated_size() == 0); 482 return 0; 483 }