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_writer.c (11770B)


      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/logger.h>
     22 #include <rsys/mem_allocator.h>
     23 
     24 #include <string.h>
     25 #include <unistd.h> /* fork, pipe */
     26 
     27 /*******************************************************************************
     28  * Helper functions
     29  ******************************************************************************/
     30 static void
     31 log_stream(const char* msg, void* ctx)
     32 {
     33   ASSERT(msg);
     34   (void)msg, (void)ctx;
     35   printf("logger %s", msg);
     36 }
     37 
     38 static void
     39 check_api(const enum sstl_type type)
     40 {
     41   struct logger logger;
     42   struct mem_allocator allocator;
     43   struct sstl_writer_create_args args = SSTL_WRITER_CREATE_ARGS_DEFAULT;
     44   struct sstl_writer* writer = NULL;
     45   const char* filename = "test.stl";
     46 
     47   CHK(sstl_writer_create(NULL, &writer) == RES_BAD_ARG);
     48   CHK(sstl_writer_create(&args, NULL) == RES_BAD_ARG);
     49   CHK(sstl_writer_create(&args, &writer) == RES_BAD_ARG);
     50 
     51   args.filename = filename;
     52   args.type = type;
     53   args.verbose = 3;
     54   CHK(sstl_writer_create(&args, &writer) == RES_OK);
     55 
     56   CHK(sstl_write_facet(NULL, &SSTL_FACET_NULL) == RES_BAD_ARG);
     57   CHK(sstl_write_facet(writer, NULL) == RES_BAD_ARG);
     58   CHK(sstl_write_facet(writer, &SSTL_FACET_NULL) == RES_OK);
     59 
     60   CHK(sstl_writer_ref_get(NULL) == RES_BAD_ARG);
     61   CHK(sstl_writer_ref_get(writer) == RES_OK);
     62   CHK(sstl_writer_ref_put(NULL) == RES_BAD_ARG);
     63   CHK(sstl_writer_ref_put(writer) == RES_OK);
     64   CHK(sstl_writer_ref_put(writer) == RES_OK);
     65 
     66   args.triangles_count = 0;
     67   CHK(sstl_writer_create(&args, &writer) == RES_OK);
     68   CHK(sstl_write_facet(writer, &SSTL_FACET_NULL) == RES_BAD_OP);
     69   CHK(sstl_writer_ref_put(writer) == RES_OK);
     70 
     71   mem_init_proxy_allocator(&allocator, &mem_default_allocator);
     72   CHK(logger_init(&allocator, &logger) == RES_OK);
     73   logger_set_stream(&logger, LOG_OUTPUT, log_stream, NULL);
     74   logger_set_stream(&logger, LOG_ERROR, log_stream, NULL);
     75   logger_set_stream(&logger, LOG_WARNING, log_stream, NULL);
     76 
     77   args.allocator = &allocator;
     78   args.logger = &logger;
     79   args.triangles_count = 1;
     80   CHK(sstl_writer_create(&args, &writer) == RES_OK);
     81   CHK(sstl_write_facet(writer, &SSTL_FACET_NULL) == RES_OK);
     82   CHK(sstl_write_facet(writer, &SSTL_FACET_NULL) == RES_BAD_OP);
     83   CHK(sstl_writer_ref_put(writer) == RES_OK);
     84 
     85   logger_release(&logger);
     86   CHK(MEM_ALLOCATED_SIZE(&allocator) == 0);
     87   mem_shutdown_proxy_allocator(&allocator);
     88 }
     89 
     90 static void
     91 check_1_triangle(struct sstl* sstl, const enum sstl_type type)
     92 {
     93   struct sstl_desc desc = SSTL_DESC_NULL;
     94   struct sstl_facet facet = SSTL_FACET_NULL;
     95   struct sstl_writer_create_args args = SSTL_WRITER_CREATE_ARGS_DEFAULT;
     96   struct sstl_writer* writer = NULL;
     97 
     98   float v[3] = {0,0,0};
     99   const char* filename = "1_triangle.stl";
    100 
    101   args.filename = filename;
    102   args.type = type;
    103   args.verbose = 3;
    104   CHK(sstl_writer_create(&args, &writer) == RES_OK);
    105 
    106   f3_splat(facet.normal, 0); /* Let sstl calculate it automatically */
    107   f3(facet.vertices[0], 0.f, 0.f, 0.f);
    108   f3(facet.vertices[1], 1.f, 0.f, 0.f);
    109   f3(facet.vertices[2], 0.f, 0.f, 1.f);
    110   CHK(sstl_write_facet(writer, &facet) == RES_OK);
    111 
    112   CHK(sstl_writer_ref_put(writer) == RES_OK);
    113 
    114   CHK(sstl_load(sstl, filename) == RES_OK);
    115   CHK(sstl_get_desc(sstl, &desc) == RES_OK);
    116 
    117   CHK(desc.type == type);
    118   CHK(!strcmp(desc.filename, filename));
    119   CHK(desc.solid_name == NULL);
    120   CHK(desc.vertices_count == 3);
    121   CHK(desc.triangles_count == 1);
    122   CHK(desc.indices[0] == 0);
    123   CHK(desc.indices[1] == 1);
    124   CHK(desc.indices[2] == 2);
    125   CHK(f3_eq(desc.vertices + 0*3, f3(v, 0.f, 0.f, 0.f)) == 1);
    126   CHK(f3_eq(desc.vertices + 1*3, f3(v, 1.f, 0.f, 0.f)) == 1);
    127   CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 1.f)) == 1);
    128   CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1);
    129 
    130   /* The triangle is missing. A warning is signalled but no error is returned:
    131    * author finalization is assumed to always succeed. Partial finalization
    132    * corrupts the output, and it would be too hasardous to try returning to the
    133    * previous state. Finally, detecting a finalization error prevents the writer
    134    * from being released, adding a memory leak to the finalization error, which
    135    * could be impossible to resolve. */
    136   args.filename = filename;
    137   args.type = SSTL_BINARY;
    138   args.triangles_count = 1;
    139   args.verbose = 3;
    140   CHK(sstl_writer_create(&args, &writer) == RES_OK);
    141   CHK(sstl_writer_ref_put(writer) == RES_OK);
    142 }
    143 
    144 static void
    145 check_tetrahedron(struct sstl* sstl, const enum sstl_type type)
    146 {
    147   struct sstl_desc desc = SSTL_DESC_NULL;
    148   struct sstl_facet facet = SSTL_FACET_NULL;
    149   struct sstl_writer_create_args args = SSTL_WRITER_CREATE_ARGS_DEFAULT;
    150   struct sstl_writer* writer = NULL;
    151 
    152   float v[3] = {0,0,0};
    153   FILE* fp = NULL;
    154   const char* filename = "Tetrahedron";
    155 
    156   CHK((fp = tmpfile()) != NULL);
    157   args.filename = filename;
    158   args.stream = fp;
    159   args.type = type;
    160   args.solid_name = "cube corner";
    161   args.verbose = 3;
    162   CHK(sstl_writer_create(&args, &writer) == RES_OK);
    163 
    164   f3(facet.normal, 0.f,-1.f, 0.f);
    165   f3(facet.vertices[0], 0.0f, 0.0f, 0.0f);
    166   f3(facet.vertices[1], 0.1f, 0.0f, 0.0f);
    167   f3(facet.vertices[2], 0.0f, 0.0f, 0.1f);
    168   CHK(sstl_write_facet(writer, &facet) == RES_OK);
    169 
    170   f3(facet.normal, 0.f, 0.f, -1.f);
    171   f3(facet.vertices[0], 0.0f, 0.0f, 0.0f);
    172   f3(facet.vertices[1], 0.0f, 0.1f, 0.0f);
    173   f3(facet.vertices[2], 0.1f, 0.0f, 0.0f);
    174   CHK(sstl_write_facet(writer, &facet) == RES_OK);
    175 
    176   f3(facet.normal,-1.f, 0.f, 0.f);
    177   f3(facet.vertices[0], 0.0f, 0.0f, 0.0f);
    178   f3(facet.vertices[1], 0.0f, 0.0f, 0.1f);
    179   f3(facet.vertices[2], 0.0f, 0.1f, 0.0f);
    180   CHK(sstl_write_facet(writer, &facet) == RES_OK);
    181 
    182   f3(facet.normal, 0.577f, 0.577f, 0.577f);
    183   f3(facet.vertices[0], 0.1f, 0.0f, 0.0f);
    184   f3(facet.vertices[1], 0.0f, 0.1f, 0.0f);
    185   f3(facet.vertices[2], 0.0f, 0.0f, 0.1f);
    186   CHK(sstl_write_facet(writer, &facet) == RES_OK);
    187 
    188   CHK(sstl_writer_ref_put(writer) == RES_OK);
    189 
    190   rewind(fp);
    191   CHK(sstl_load_stream(sstl, fp, filename) == RES_OK);
    192   CHK(fclose(fp) == 0);
    193 
    194   CHK(sstl_get_desc(sstl, &desc) == RES_OK);
    195   CHK(!strcmp(desc.filename, filename));
    196   CHK(type == SSTL_BINARY || !strcmp(desc.solid_name, "cube corner"));
    197   CHK(desc.type == type);
    198   CHK(desc.vertices_count == 4);
    199   CHK(desc.triangles_count == 4);
    200 
    201   CHK(f3_eq(desc.vertices + desc.indices[0]*3, f3(v, 0.0f, 0.0f, 0.0f)) == 1);
    202   CHK(f3_eq(desc.vertices + desc.indices[1]*3, f3(v, 0.1f, 0.0f, 0.0f)) == 1);
    203   CHK(f3_eq(desc.vertices + desc.indices[2]*3, f3(v, 0.0f, 0.0f, 0.1f)) == 1);
    204   CHK(f3_eq(desc.vertices + desc.indices[3]*3, f3(v, 0.0f, 0.0f, 0.0f)) == 1);
    205   CHK(f3_eq(desc.vertices + desc.indices[4]*3, f3(v, 0.0f, 0.1f, 0.0f)) == 1);
    206   CHK(f3_eq(desc.vertices + desc.indices[5]*3, f3(v, 0.1f, 0.0f, 0.0f)) == 1);
    207   CHK(f3_eq(desc.vertices + desc.indices[6]*3, f3(v, 0.0f, 0.0f, 0.0f)) == 1);
    208   CHK(f3_eq(desc.vertices + desc.indices[7]*3, f3(v, 0.0f, 0.0f, 0.1f)) == 1);
    209   CHK(f3_eq(desc.vertices + desc.indices[8]*3, f3(v, 0.0f, 0.1f, 0.0f)) == 1);
    210   CHK(f3_eq(desc.vertices + desc.indices[9]*3, f3(v, 0.1f, 0.0f, 0.0f)) == 1);
    211   CHK(f3_eq(desc.vertices + desc.indices[10]*3, f3(v, 0.0f, 0.1f, 0.0f)) == 1);
    212   CHK(f3_eq(desc.vertices + desc.indices[11]*3, f3(v, 0.0f, 0.0f, 0.1f)) == 1);
    213 
    214   CHK(f3_eq(desc.normals + 0*3, f3(v, 0.f,-1.f, 0.f)) == 1);
    215   CHK(f3_eq(desc.normals + 1*3, f3(v, 0.f, 0.f,-1.f)) == 1);
    216   CHK(f3_eq(desc.normals + 2*3, f3(v,-1.f, 0.f, 0.f)) == 1);
    217   f3_normalize(v, f3(v, 1.f, 1.f, 1.f));
    218   CHK(f3_eq_eps(desc.normals + 3*3, v, 1.e-6f) == 1);
    219 }
    220 
    221 static void
    222 process_write(const enum sstl_type type, FILE* fp, const char* filename)
    223 {
    224   struct sstl_writer_create_args args = SSTL_WRITER_CREATE_ARGS_DEFAULT;
    225   struct sstl_facet facet = SSTL_FACET_NULL;
    226   struct sstl_writer* writer = NULL;
    227 
    228   args.solid_name = "Triangle";
    229   args.filename = filename;
    230   args.stream = fp;
    231   args.type = type;
    232   args.verbose = 3;
    233 
    234   switch(type) {
    235     case SSTL_ASCII:
    236       CHK(sstl_writer_create(&args, &writer) == RES_OK);
    237       break;
    238 
    239     case SSTL_BINARY:
    240       /* Binary StL can be written only if the number of triangles is known in
    241        * advance */
    242       CHK(sstl_writer_create(&args, &writer) == RES_BAD_ARG);
    243       args.triangles_count = 1;
    244       CHK(sstl_writer_create(&args, &writer) == RES_OK);
    245       break;
    246 
    247     default: FATAL("Unreachable code\n"); break;
    248   }
    249 
    250   f3(facet.normal, 0.f,-1.f, 0.f);
    251   f3(facet.vertices[0], 1.f, 0.f, 0.f);
    252   f3(facet.vertices[1], 0.f, 0.f, 1.f);
    253   f3(facet.vertices[2], 0.f, 0.f, 0.f);
    254   CHK(sstl_write_facet(writer, &facet) == RES_OK);
    255 
    256   CHK(sstl_writer_ref_put(writer) == RES_OK);
    257 }
    258 
    259 static void
    260 process_read
    261   (struct sstl* sstl,
    262    const enum sstl_type type,
    263    FILE* fp,
    264    const char* filename)
    265 {
    266   struct sstl_desc desc = SSTL_DESC_NULL;
    267   float v[3];
    268 
    269   switch(type) {
    270     case SSTL_ASCII:
    271       CHK(sstl_load_stream_ascii(sstl, fp, filename) == RES_OK);
    272       break;
    273     case SSTL_BINARY:
    274       CHK(sstl_load_stream_binary(sstl, fp, filename) == RES_OK);
    275       break;
    276     default: FATAL("Unreachable code\n"); break;
    277   }
    278 
    279   CHK(sstl_get_desc(sstl, &desc) == RES_OK);
    280   CHK(desc.type == type);
    281   CHK(!strcmp(desc.filename, filename));
    282   CHK(type == SSTL_ASCII || desc.solid_name == NULL);
    283   CHK(type == SSTL_BINARY || !strcmp(desc.solid_name, "Triangle"));
    284   CHK(desc.vertices_count == 3);
    285   CHK(desc.triangles_count == 1);
    286   CHK(desc.indices[0] == 0);
    287   CHK(desc.indices[1] == 1);
    288   CHK(desc.indices[2] == 2);
    289   CHK(f3_eq(desc.vertices + 0*3, f3(v, 1.f, 0.f, 0.f)) == 1);
    290   CHK(f3_eq(desc.vertices + 1*3, f3(v, 0.f, 0.f, 1.f)) == 1);
    291   CHK(f3_eq(desc.vertices + 2*3, f3(v, 0.f, 0.f, 0.f)) == 1);
    292   CHK(f3_eq(desc.normals, f3(v, 0.f, -1.f, 0.f)) == 1);
    293 }
    294 
    295 static void
    296 check_no_seekable_file(struct sstl* sstl, const enum sstl_type type)
    297 {
    298   int fd[2] = {0,0};
    299   FILE* fp = NULL;
    300   const char* filename = "Pipe";
    301   pid_t pid = 0;
    302 
    303   CHK(pipe(fd) == 0);
    304   CHK((pid = fork()) != -1);
    305 
    306   if(pid == 0) {  /* Child process */
    307     CHK(close(fd[0]) == 0); /* Close the unused input stream */
    308     CHK(sstl_ref_put(sstl) == RES_OK); /* Release the unused sstl */
    309     CHK((fp = fdopen(fd[1], "w")) != NULL);
    310     process_write(type, fp, filename);
    311     CHK(fclose(fp) == 0);
    312     exit(0);
    313 
    314   } else { /* Parent process */
    315     CHK(close(fd[1]) == 0); /* Close the unused output stream */
    316     CHK(fp = fdopen(fd[0], "r"));
    317     process_read(sstl, type, fp, filename);
    318     CHK(fclose(fp) == 0);
    319   }
    320 }
    321 
    322 /*******************************************************************************
    323  * The test
    324  ******************************************************************************/
    325 int
    326 main(int argc, char** argv)
    327 {
    328   struct sstl* sstl = NULL;
    329   (void)argc, (void)argv;
    330 
    331   CHK(sstl_create(NULL, NULL, 3, &sstl) == RES_OK);
    332 
    333   check_api(SSTL_ASCII);
    334   check_api(SSTL_BINARY);
    335   check_1_triangle(sstl, SSTL_ASCII);
    336   check_1_triangle(sstl, SSTL_BINARY);
    337   check_tetrahedron(sstl, SSTL_ASCII);
    338   check_tetrahedron(sstl, SSTL_BINARY);
    339   check_no_seekable_file(sstl, SSTL_ASCII);
    340   check_no_seekable_file(sstl, SSTL_BINARY);
    341 
    342   CHK(sstl_ref_put(sstl) == RES_OK);
    343   CHK(mem_allocated_size() == 0);
    344   return 0;
    345 }