star-stl

Load STereo Lithography (StL) file format
git clone git://git.meso-star.fr/star-stl.git
Log | Files | Refs | README | LICENSE

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 }