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_binary.c (14716B)


      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 header[80] = {0};
     33   const uint32_t ntris = 0;
     34   const char* filename = "test.stl";
     35   FILE* fp = NULL;
     36   struct sstl_desc desc = SSTL_DESC_NULL;
     37 
     38   CHK((fp = fopen(filename, "w+")) != NULL);
     39   rewind(fp);
     40 
     41   CHK(sstl_load_binary(sstl, NULL) == RES_BAD_ARG);
     42   CHK(sstl_load_binary(NULL, filename) == RES_BAD_ARG);
     43   CHK(sstl_load_binary(sstl, "none.stl") == RES_IO_ERR);
     44   /* A binary cannot be empty */
     45   CHK(sstl_load_binary(sstl, filename) == RES_BAD_ARG);
     46 
     47   CHK(sstl_load_stream_binary(NULL, fp, filename) == RES_BAD_ARG);
     48   CHK(sstl_load_stream_binary(sstl, NULL, filename) == RES_BAD_ARG);
     49   CHK(sstl_load_stream_binary(sstl, fp, NULL) == RES_BAD_ARG);
     50   /* A binary cannot be empty */
     51   CHK(sstl_load_stream_binary(sstl, fp, filename) == RES_BAD_ARG);
     52 
     53   /* Write the minimum data required by a binary StL */
     54   CHK(fwrite(header, sizeof(header), 1, fp) == 1);
     55   CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
     56   rewind(fp);
     57 
     58   CHK(sstl_load_binary(sstl, filename) == RES_OK);
     59   CHK(sstl_get_desc(sstl, &desc) == RES_OK);
     60   CHK(desc.type == SSTL_BINARY);
     61   CHK(!strcmp(desc.filename, filename));
     62   CHK(desc.solid_name == NULL);
     63   CHK(desc.vertices_count == 0);
     64   CHK(desc.triangles_count == 0);
     65 
     66   CHK(sstl_load_stream_binary(sstl, fp, filename) == RES_OK);
     67   CHK(sstl_get_desc(sstl, &desc) == RES_OK);
     68   CHK(desc.type == SSTL_BINARY);
     69   CHK(!strcmp(desc.filename, filename));
     70   CHK(desc.solid_name == NULL);
     71   CHK(desc.vertices_count == 0);
     72   CHK(desc.triangles_count == 0);
     73 
     74   CHK(fclose(fp) == 0);
     75 }
     76 
     77 static void
     78 check_1_triangle(struct sstl* sstl)
     79 {
     80   char header[80] = {0};
     81   const uint32_t ntris = 1;
     82   const float normal[3] = {0.f, -1.f, 0.f};
     83   const float verts[9] = {
     84     0.f, 0.f, 0.f,
     85     1.f, 0.f, 0.f,
     86     0.f, 0.f, 1.f
     87   };
     88   const uint16_t nattrs = 0;
     89   const char* filename = "1_triangle.stl";
     90   FILE* fp = NULL;
     91   float v[3] = {0,0,0};
     92   struct sstl_desc desc = SSTL_DESC_NULL;
     93 
     94   CHK(sstl);
     95 
     96   CHK((fp = fopen(filename, "w")) != NULL);
     97   CHK(fwrite(header, sizeof(header), 1, fp) == 1);
     98   CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
     99   CHK(fwrite(normal, sizeof(normal), 1, fp) == 1);
    100   CHK(fwrite(verts, sizeof(verts), 1, fp) == 1);
    101   CHK(fwrite(&nattrs, sizeof(nattrs), 1, fp) == 1);
    102   CHK(fclose(fp) == 0);
    103 
    104   CHK(sstl_load(sstl, filename) == RES_OK);
    105   CHK(sstl_get_desc(sstl, &desc) == RES_OK);
    106 
    107   CHK(desc.type == SSTL_BINARY);
    108   CHK(!strcmp(desc.filename, filename));
    109   CHK(desc.solid_name == NULL);
    110   CHK(desc.vertices_count == 3);
    111   CHK(desc.triangles_count == 1);
    112   CHK(desc.indices[0] == 0);
    113   CHK(desc.indices[1] == 1);
    114   CHK(desc.indices[2] == 2);
    115   CHK(f3_eq(desc.vertices + 0*3, f3(v, 0.f, 0.f, 0.f)) == 1);
    116   CHK(f3_eq(desc.vertices + 1*3, f3(v, 1.f, 0.f, 0.f)) == 1);
    117   CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 1.f)) == 1);
    118   CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1);
    119 }
    120 
    121 static void
    122 check_1_triangle_no_normal(struct sstl* sstl)
    123 {
    124   char header[80] = {0};
    125   const uint32_t ntris = 1;
    126   const float normal[3] = {0.f, 0.f, 0.f};
    127   const float verts[9] = {
    128     0.f, 0.f, 0.f,
    129    -1.f, 0.f, 0.f,
    130     0.f, 0.f,-1.f
    131   };
    132   const uint16_t nattrs = 0;
    133   const char* filename = "1_triangle_no_normal.stl";
    134   FILE* fp = NULL;
    135   float v[3] = {0,0,0};
    136   struct sstl_desc desc = SSTL_DESC_NULL;
    137 
    138   CHK((fp = fopen(filename, "w+")) != NULL);
    139   CHK(fwrite(header, sizeof(header), 1, fp) == 1);
    140   CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
    141   CHK(fwrite(normal, sizeof(normal), 1, fp) == 1);
    142   CHK(fwrite(verts, sizeof(verts), 1, fp) == 1);
    143   CHK(fwrite(&nattrs, sizeof(nattrs), 1, fp) == 1);
    144   rewind(fp);
    145 
    146   CHK(sstl_load_stream(sstl, fp, filename) == RES_OK);
    147 
    148   CHK(sstl_get_desc(sstl, &desc) == RES_OK);
    149   CHK(desc.type == SSTL_BINARY);
    150   CHK(!strcmp(desc.filename, filename));
    151   CHK(desc.solid_name == NULL);
    152   CHK(desc.vertices_count == 3);
    153   CHK(desc.triangles_count == 1);
    154   CHK(desc.indices[0] == 0);
    155   CHK(desc.indices[1] == 1);
    156   CHK(desc.indices[2] == 2);
    157   CHK(f3_eq(desc.vertices + 0*3, f3(v, 0.f, 0.f, 0.f)) == 1);
    158   CHK(f3_eq(desc.vertices + 1*3, f3(v,-1.f, 0.f, 0.f)) == 1);
    159   CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f,-1.f)) == 1);
    160 
    161   /* Normal is automatically calculated */
    162   CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1);
    163 
    164   CHK(fclose(fp) == 0);
    165 }
    166 
    167 static void
    168 check_invalid_file(struct sstl* sstl)
    169 {
    170   const char header[80] = {0};
    171   const uint32_t ntris = 1;
    172   const float N[3] = {0,0,0};
    173   const float v0[3] = {0,0,0};
    174   const float v1[3] = {1,0,0};
    175   const float v2[3] = {0,0,1};
    176   const uint16_t nattrs = 0;
    177   FILE* fp = NULL;
    178   float v[3] = {0,0,0};
    179   struct sstl_desc desc = SSTL_DESC_NULL;
    180 
    181   /* First, check that the file should be OK if all the data has been correctly
    182    * written, to make sure that the tests really check what we expect, i.e. a
    183    * bad formatting and not a faulty data set */
    184   CHK((fp = tmpfile()) != NULL);
    185   CHK(fwrite(&header, sizeof(char), sizeof(header), fp) == sizeof(header));
    186   CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
    187   CHK(fwrite(N, sizeof(N), 1, fp) == 1);
    188   CHK(fwrite(v0, sizeof(v0), 1, fp) == 1);
    189   CHK(fwrite(v1, sizeof(v1), 1, fp) == 1);
    190   CHK(fwrite(v2, sizeof(v2), 1, fp) == 1);
    191   CHK(fwrite(&nattrs, sizeof(nattrs), 1, fp) == 1);
    192   rewind(fp);
    193   CHK(sstl_load_stream(sstl, fp, "Valid StL") == RES_OK);
    194   CHK(fclose(fp) == 0);
    195 
    196   CHK(sstl_get_desc(sstl, &desc) == RES_OK);
    197   CHK(desc.type == SSTL_BINARY);
    198   CHK(!strcmp(desc.filename, "Valid StL"));
    199   CHK(desc.vertices_count == 3);
    200   CHK(desc.triangles_count == 1);
    201   CHK(desc.indices[0] == 0);
    202   CHK(desc.indices[1] == 1);
    203   CHK(desc.indices[2] == 2);
    204   CHK(f3_eq(desc.vertices + 0*3, f3(v, 0.f, 0.f, 0.f)) == 1);
    205   CHK(f3_eq(desc.vertices + 1*3, f3(v, 1.f, 0.f, 0.f)) == 1);
    206   CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 1.f)) == 1);
    207   CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1);
    208 
    209   /* Header is too small */
    210   CHK((fp = tmpfile()) != NULL);
    211   CHK(fwrite(&header, sizeof(char), sizeof(header)-1, fp) == sizeof(header)-1);
    212   CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
    213   rewind(fp);
    214   CHK(sstl_load_stream(sstl, fp, "Invalid StL") == RES_BAD_ARG);
    215   CHK(fclose(fp) == 0);
    216 
    217   /* Triangle is missing */
    218   CHK((fp = tmpfile()) != NULL);
    219   CHK(fwrite(&header, sizeof(char), sizeof(header), fp) == sizeof(header));
    220   CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
    221   rewind(fp);
    222   CHK(sstl_load_stream(sstl, fp, "Invalid StL") == RES_BAD_ARG);
    223   CHK(fclose(fp) == 0);
    224 
    225   /* Triangle normal is missing */
    226   CHK((fp = tmpfile()) != NULL);
    227   CHK(fwrite(&header, sizeof(char), sizeof(header), fp) == sizeof(header));
    228   CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
    229   CHK(fwrite(v0, sizeof(v0), 1, fp) == 1);
    230   CHK(fwrite(v1, sizeof(v1), 1, fp) == 1);
    231   CHK(fwrite(v2, sizeof(v2), 1, fp) == 1);
    232   CHK(fwrite(&nattrs, sizeof(nattrs), 1, fp) == 1);
    233   rewind(fp);
    234   CHK(sstl_load_stream_binary(sstl, fp, "Invalid StL") == RES_BAD_ARG);
    235   CHK(fclose(fp) == 0);
    236 
    237   /* One vertex of the triangle is wrongly written */
    238   CHK((fp = tmpfile()) != NULL);
    239   CHK(fwrite(&header, sizeof(char), sizeof(header), fp) == sizeof(header));
    240   CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
    241   CHK(fwrite(N, sizeof(N), 1, fp) == 1);
    242   CHK(fwrite(v0, sizeof(v0), 1, fp) == 1);
    243   CHK(fwrite(v1, sizeof(v1)-1/*One byte is missing*/, 1, fp) == 1);
    244   CHK(fwrite(v2, sizeof(v2), 1, fp) == 1);
    245   CHK(fwrite(&nattrs, sizeof(nattrs), 1, fp) == 1);
    246   rewind(fp);
    247   CHK(sstl_load_stream(sstl, fp, "Invalid StL") == RES_BAD_ARG);
    248   CHK(fclose(fp) == 0);
    249 
    250   /* The #attribs is missing */
    251   CHK((fp = tmpfile()) != NULL);
    252   CHK(fwrite(&header, sizeof(char), sizeof(header), fp) == sizeof(header));
    253   CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
    254   CHK(fwrite(N, sizeof(N), 1, fp) == 1);
    255   CHK(fwrite(v0, sizeof(v0), 1, fp) == 1);
    256   CHK(fwrite(v1, sizeof(v1), 1, fp) == 1);
    257   CHK(fwrite(v2, sizeof(v2), 1, fp) == 1);
    258   rewind(fp);
    259   CHK(sstl_load_stream(sstl, fp, "Invalid StL") == RES_BAD_ARG);
    260   CHK(fclose(fp) == 0);
    261 }
    262 
    263 static void
    264 check_tetrahedron(struct sstl* sstl)
    265 {
    266   const char header[80] = {0};
    267   float v[3] = {0,0,0};
    268   const uint32_t ntris = 4;
    269   const uint16_t nattrs = 0;
    270   FILE* fp = NULL;
    271   struct sstl_desc desc = SSTL_DESC_NULL;
    272 
    273   CHK(sstl != NULL);
    274 
    275   CHK((fp = tmpfile()) != NULL);
    276   CHK(fwrite(header, sizeof(header), 1, fp) == 1);
    277   CHK(fwrite(&ntris, sizeof(ntris), 1, fp) == 1);
    278 
    279   CHK(fwrite(f3(v, 0.0f,-1.0f, 0.0f), sizeof(v), 1, fp) == 1);
    280   CHK(fwrite(f3(v, 0.0f, 0.0f, 0.0f), sizeof(v), 1, fp) == 1);
    281   CHK(fwrite(f3(v, 0.1f, 0.0f, 0.0f), sizeof(v), 1, fp) == 1);
    282   CHK(fwrite(f3(v, 0.0f, 0.0f, 0.1f), sizeof(v), 1, fp) == 1);
    283   CHK(fwrite(&nattrs, sizeof(nattrs), 1, fp) == 1);
    284 
    285   CHK(fwrite(f3(v, 0.0f, 0.0f,-1.f), sizeof(v), 1, fp) == 1);
    286   CHK(fwrite(f3(v, 0.0f, 0.0f, 0.0f), sizeof(v), 1, fp) == 1);
    287   CHK(fwrite(f3(v, 0.0f, 0.1f, 0.0f), sizeof(v), 1, fp) == 1);
    288   CHK(fwrite(f3(v, 0.1f, 0.0f, 0.0f), sizeof(v), 1, fp) == 1);
    289   CHK(fwrite(&nattrs, sizeof(nattrs), 1, fp) == 1);
    290 
    291   CHK(fwrite(f3(v,-1.0f, 0.0f, 0.0f), sizeof(v), 1, fp) == 1);
    292   CHK(fwrite(f3(v, 0.0f, 0.0f, 0.0f), sizeof(v), 1, fp) == 1);
    293   CHK(fwrite(f3(v, 0.0f, 0.0f, 0.1f), sizeof(v), 1, fp) == 1);
    294   CHK(fwrite(f3(v, 0.0f, 0.1f, 0.0f), sizeof(v), 1, fp) == 1);
    295   CHK(fwrite(&nattrs, sizeof(nattrs), 1, fp) == 1);
    296 
    297   CHK(fwrite(f3(v, 0.577f, 0.577f, 0.577f), sizeof(v), 1, fp) == 1);
    298   CHK(fwrite(f3(v, 0.1f, 0.0f, 0.0f), sizeof(v), 1, fp) == 1);
    299   CHK(fwrite(f3(v, 0.0f, 0.1f, 0.0f), sizeof(v), 1, fp) == 1);
    300   CHK(fwrite(f3(v, 0.0f, 0.0f, 0.1f), sizeof(v), 1, fp) == 1);
    301   CHK(fwrite(&nattrs, sizeof(nattrs), 1, fp) == 1);
    302 
    303   rewind(fp);
    304 
    305   CHK(sstl_load_stream(sstl, fp, "Tetrahedron") == RES_OK);
    306   CHK(fclose(fp) == 0);
    307 
    308   CHK(sstl_get_desc(sstl, &desc) == RES_OK);
    309   CHK(desc.type == SSTL_BINARY);
    310   CHK(!strcmp(desc.filename, "Tetrahedron"));
    311   CHK(desc.solid_name == NULL);
    312   CHK(desc.vertices_count == 4);
    313   CHK(desc.triangles_count == 4);
    314 
    315   CHK(f3_eq(desc.vertices + desc.indices[0]*3, f3(v, 0.0f, 0.0f, 0.0f)) == 1);
    316   CHK(f3_eq(desc.vertices + desc.indices[1]*3, f3(v, 0.1f, 0.0f, 0.0f)) == 1);
    317   CHK(f3_eq(desc.vertices + desc.indices[2]*3, f3(v, 0.0f, 0.0f, 0.1f)) == 1);
    318   CHK(f3_eq(desc.vertices + desc.indices[3]*3, f3(v, 0.0f, 0.0f, 0.0f)) == 1);
    319   CHK(f3_eq(desc.vertices + desc.indices[4]*3, f3(v, 0.0f, 0.1f, 0.0f)) == 1);
    320   CHK(f3_eq(desc.vertices + desc.indices[5]*3, f3(v, 0.1f, 0.0f, 0.0f)) == 1);
    321   CHK(f3_eq(desc.vertices + desc.indices[6]*3, f3(v, 0.0f, 0.0f, 0.0f)) == 1);
    322   CHK(f3_eq(desc.vertices + desc.indices[7]*3, f3(v, 0.0f, 0.0f, 0.1f)) == 1);
    323   CHK(f3_eq(desc.vertices + desc.indices[8]*3, f3(v, 0.0f, 0.1f, 0.0f)) == 1);
    324   CHK(f3_eq(desc.vertices + desc.indices[9]*3, f3(v, 0.1f, 0.0f, 0.0f)) == 1);
    325   CHK(f3_eq(desc.vertices + desc.indices[10]*3, f3(v, 0.0f, 0.1f, 0.0f)) == 1);
    326   CHK(f3_eq(desc.vertices + desc.indices[11]*3, f3(v, 0.0f, 0.0f, 0.1f)) == 1);
    327 
    328   CHK(f3_eq(desc.normals + 0*3, f3(v, 0.f,-1.f, 0.f)) == 1);
    329   CHK(f3_eq(desc.normals + 1*3, f3(v, 0.f, 0.f,-1.f)) == 1);
    330   CHK(f3_eq(desc.normals + 2*3, f3(v,-1.f, 0.f, 0.f)) == 1);
    331   f3_normalize(v, f3(v, 1.f, 1.f, 1.f));
    332   CHK(f3_eq_eps(desc.normals + 3*3, v, 1.e-6f) == 1);
    333 }
    334 
    335 static void
    336 check_no_seekable_file(struct sstl* sstl)
    337 {
    338   float v[3] = {0,0,0};
    339   int fd[2] = {0,0};
    340   pid_t pid = 0;
    341 
    342   CHK(pipe(fd) == 0);
    343   CHK((pid = fork()) != -1);
    344 
    345   if(pid == 0) {  /* Child process */
    346     const char header[80] = {0};
    347     const uint32_t ntris = 1;
    348     const uint16_t nattrs = 0;
    349 
    350     CHK(close(fd[0]) == 0); /* Close the unused input stream */
    351     CHK(sstl_ref_put(sstl) == RES_OK); /* Release the unused sstl */
    352 
    353     /* Write the binary StL */
    354     CHK(write(fd[1], header, sizeof(header)) == sizeof(header));
    355     CHK(write(fd[1], &ntris, sizeof(ntris)) == sizeof(ntris));
    356     CHK(write(fd[1], f3(v, 0.f,-1.f, 0.f), sizeof(v)) == sizeof(v));
    357     CHK(write(fd[1], f3(v, 1.f, 0.f, 0.f), sizeof(v)) == sizeof(v));
    358     CHK(write(fd[1], f3(v, 0.f, 0.f, 1.f), sizeof(v)) == sizeof(v));
    359     CHK(write(fd[1], f3(v, 0.f, 0.f, 0.f), sizeof(v)) == sizeof(v));
    360     CHK(write(fd[1], &nattrs, sizeof(nattrs)) == sizeof(nattrs));
    361 
    362     CHK(close(fd[1]) == 0);
    363     exit(0);
    364 
    365   } else { /* Parent process */
    366     struct sstl_desc desc = SSTL_DESC_NULL;
    367     FILE* fp = NULL;
    368     CHK(close(fd[1]) == 0); /* Close the unused output stream */
    369 
    370     CHK(fp = fdopen(fd[0], "r"));
    371     CHK(sstl_load_stream(sstl, fp, "Piped StL") == RES_BAD_ARG);
    372     CHK(sstl_load_stream_binary(sstl, fp, "Piped StL") == RES_OK);
    373     CHK(fclose(fp) == 0);
    374 
    375     CHK(sstl_get_desc(sstl, &desc) == RES_OK);
    376     CHK(desc.type == SSTL_BINARY);
    377     CHK(!strcmp(desc.filename, "Piped StL"));
    378     CHK(desc.solid_name == NULL);
    379     CHK(desc.vertices_count == 3);
    380     CHK(desc.triangles_count == 1);
    381     CHK(desc.indices[0] == 0);
    382     CHK(desc.indices[1] == 1);
    383     CHK(desc.indices[2] == 2);
    384     CHK(f3_eq(desc.vertices + 0*3, f3(v, 1.f, 0.f, 0.f)) == 1);
    385     CHK(f3_eq(desc.vertices + 1*3, f3(v, 0.f, 0.f, 1.f)) == 1);
    386     CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 0.f)) == 1);
    387     CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1);
    388   }
    389 }
    390 
    391 
    392 /*******************************************************************************
    393  * The test
    394  ******************************************************************************/
    395 int
    396 main(int argc, char** argv)
    397 {
    398   struct sstl* sstl = NULL;
    399   (void)argc, (void)argv;
    400 
    401   CHK(sstl_create(NULL, NULL, 3, &sstl) == RES_OK);
    402 
    403   check_api(sstl);
    404   check_1_triangle(sstl);
    405   check_1_triangle_no_normal(sstl);
    406   check_invalid_file(sstl);
    407   check_tetrahedron(sstl);
    408   check_no_seekable_file(sstl);
    409 
    410   CHK(sstl_ref_put(sstl) == RES_OK);
    411   CHK(mem_allocated_size() == 0);
    412   return 0;
    413 }