star-stl

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

sstl_ascii.c (7989B)


      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 /* strtok_r support */
     17 
     18 #include "sstl_c.h"
     19 
     20 #include <rsys/cstr.h>
     21 #include <rsys/stretchy_array.h>
     22 #include <rsys/text_reader.h>
     23 
     24 #include <string.h>
     25 
     26 /*******************************************************************************
     27  * Helper functions
     28  ******************************************************************************/
     29 static res_T
     30 parse_float3
     31   (struct sstl* sstl,
     32    const struct txtrdr* txtrdr,
     33    char* str,
     34    char** ctx,
     35    float vec[3])
     36 {
     37   char* x = NULL;
     38   char* y = NULL;
     39   char* z = NULL;
     40   res_T res = RES_OK;
     41   ASSERT(sstl && txtrdr && vec);
     42 
     43   if(!(x = strtok_r(str,  " \t", ctx))
     44   || !(y = strtok_r(NULL, " \t", ctx))
     45   || !(z = strtok_r(NULL, " \t", ctx))) {
     46     res = RES_BAD_ARG;
     47     goto error;
     48   }
     49 
     50   if((res = cstr_to_float(x, vec+0)) != RES_OK) goto error;
     51   if((res = cstr_to_float(y, vec+1)) != RES_OK) goto error;
     52   if((res = cstr_to_float(z, vec+2)) != RES_OK) goto error;
     53 
     54 exit:
     55   return res;
     56 error:
     57   ERROR(sstl, "%s:%lu: invalid vector component\n",
     58     txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr));
     59   goto exit;
     60 }
     61 
     62 static res_T
     63 parse_word
     64   (struct sstl* sstl,
     65    const struct txtrdr* txtrdr,
     66    const char* word,
     67    char* str,
     68    char** ctx)
     69 {
     70   char* tk = NULL;
     71   ASSERT(sstl && txtrdr && ctx);
     72 
     73   tk = strtok_r(str, " \t", ctx);
     74   if(!tk || strcmp(tk, word) != 0) {
     75     ERROR(sstl, "%s:%lu: expect the \"%s\" keyword\n",
     76       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
     77       word);
     78     return RES_BAD_ARG;
     79   }
     80 
     81   return RES_OK;
     82 }
     83 
     84 static INLINE res_T
     85 parse_nothing
     86   (struct sstl* sstl,
     87    const struct txtrdr* txtrdr,
     88    char* str,
     89    char** ctx)
     90 {
     91   char* tk = NULL;
     92 
     93   ASSERT(txtrdr && ctx);
     94   (void)sstl; /* Avoid "unused variable" warning */
     95 
     96   tk = strtok_r(str, " \t", ctx);
     97   if(tk != NULL) {
     98     WARN(sstl, "%s:%lu: unexpected text \"%s\"\n",
     99       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk);
    100   }
    101   return RES_OK;
    102 }
    103 
    104 static res_T
    105 parse_vec3
    106   (struct sstl* sstl,
    107    struct txtrdr* txtrdr,
    108    const char* name,
    109    char* str,
    110    char** ctx,
    111    float vec[3])
    112 {
    113   res_T res = RES_BAD_ARG;
    114   ASSERT(sstl && txtrdr && name && ctx);
    115 
    116   if((res = parse_word(sstl, txtrdr, name, str, ctx)) != RES_OK) goto error;
    117   if((res = parse_float3(sstl, txtrdr, NULL, ctx, vec)) != RES_OK) goto error;
    118   if((res = parse_nothing(sstl, txtrdr, NULL, ctx)) != RES_OK) goto error;
    119 
    120 exit:
    121   return res;
    122 error:
    123   goto exit;
    124 }
    125 
    126 static res_T
    127 parse_facet(struct sstl* sstl, struct txtrdr* txtrdr, char* normal)
    128 {
    129   float v[3][3] = {0};
    130   float* N = NULL;
    131   char* line = NULL;
    132   char* ctx = NULL;
    133   int i = 0;
    134   res_T res = RES_OK;
    135   ASSERT(sstl && txtrdr && normal);
    136 
    137   N = sa_add(sstl->normals, 3);
    138   if((res = parse_vec3(sstl, txtrdr, "normal", normal, &ctx, N)) != RES_OK) goto error;
    139 
    140   #define READ_LINE { \
    141     if((res = txtrdr_read_line(txtrdr)) != RES_OK) { \
    142       ERROR(sstl, "%s: error reading line -- %s\n", \
    143         txtrdr_get_name(txtrdr), res_to_cstr(res)); \
    144       goto error; \
    145     }  \
    146     if(!(line = txtrdr_get_line(txtrdr))) { \
    147       ERROR(sstl, "%s: unexpected end of file\n", txtrdr_get_name(txtrdr)); \
    148       res = RES_BAD_ARG; \
    149       goto error; \
    150     } \
    151   } (void)0
    152 
    153   READ_LINE;
    154   if((res = parse_word(sstl, txtrdr, "outer", line, &ctx)) != RES_OK) goto error;
    155   if((res = parse_word(sstl, txtrdr, "loop",  NULL, &ctx)) != RES_OK) goto error;
    156   if((res = parse_nothing(sstl, txtrdr, NULL, &ctx)) != RES_OK) goto error;
    157 
    158   FOR_EACH(i, 0, 3) {
    159     READ_LINE;
    160     res = parse_vec3(sstl, txtrdr, "vertex", line, &ctx, v[i]);
    161     if(res != RES_OK) goto error;
    162 
    163     res = register_vertex(sstl, v[i]);
    164     if(res != RES_OK) {
    165       ERROR(sstl, "%s:%lu: vertex registration error -- %s\n",
    166         txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    167         res_to_cstr(res));
    168       goto error;
    169     }
    170   }
    171 
    172   READ_LINE;
    173   if((res = parse_word(sstl, txtrdr, "endloop", line, &ctx)) != RES_OK) goto error;
    174   if((res = parse_nothing(sstl, txtrdr, NULL, &ctx)) != RES_OK) goto error;
    175 
    176   READ_LINE;
    177   if((res = parse_word(sstl, txtrdr, "endfacet", line, &ctx)) != RES_OK) goto error;
    178   if((res = parse_nothing(sstl, txtrdr, NULL, &ctx)) != RES_OK) goto error;
    179 
    180   #undef READ_LINE
    181 
    182   /* If necessary, automatically calculate the surface normal. */
    183   if(!f3_is_normalized(N)) calculate_normal(N, v[0], v[1], v[2]);
    184 
    185 exit:
    186   return res;
    187 error:
    188   goto exit;
    189 }
    190 
    191 static res_T
    192 parse_solid(struct sstl* sstl, struct txtrdr* txtrdr)
    193 {
    194   char* line = NULL;
    195   char* tk = NULL;
    196   char* tk_ctx = NULL;
    197   res_T res = RES_OK;
    198   ASSERT(sstl && txtrdr);
    199 
    200   line = txtrdr_get_line(txtrdr);
    201   ASSERT(line != NULL);
    202 
    203   tk = strtok_r(line, " \t", &tk_ctx);
    204   ASSERT(tk);
    205   if(strcmp(tk, "solid")) {
    206     ERROR(sstl, "%s:%lu: the \"solid [name]\" directive is missing\n",
    207       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr));
    208     res = RES_BAD_ARG;
    209     goto error;
    210   }
    211 
    212   tk = strtok_r(NULL, "", &tk_ctx);
    213   if(tk != NULL && (res = str_set(&sstl->name, tk)) != RES_OK) {
    214     ERROR(sstl, "%s:%lu: error duplicating solid name -- %s\n",
    215       txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr),
    216       res_to_cstr(res));
    217     goto error;
    218   }
    219 
    220   for(;;) {
    221     if((res = txtrdr_read_line(txtrdr)) != RES_OK) {
    222       ERROR(sstl, "%s: error reading line -- %s\n",
    223         str_cget(&sstl->name), res_to_cstr(res));
    224       goto error;
    225     }
    226 
    227     if((line = txtrdr_get_line(txtrdr)) == NULL) {
    228       ERROR(sstl, "%s: the \"endsolid [name]\" directive is missing\n",
    229         txtrdr_get_name(txtrdr));
    230       res = RES_BAD_ARG;
    231       goto error;
    232     }
    233 
    234     tk = strtok_r(line, " \t", &tk_ctx);
    235 
    236     if(!strcmp(tk, "facet")) {
    237       res = parse_facet(sstl, txtrdr, strtok_r(NULL, "", &tk_ctx));
    238       if(res != RES_OK) goto error;
    239 
    240     } else if(!strcmp(tk, "endsolid")) {
    241       break; /* Stop on "endsolid" directive */
    242 
    243     } else {
    244       ERROR(sstl, "%s:%lu: invalid directive \"%s\"\n",
    245         txtrdr_get_name(txtrdr), (unsigned long)txtrdr_get_line_num(txtrdr), tk);
    246       res = RES_BAD_ARG;
    247       goto error;
    248     }
    249   }
    250 
    251 exit:
    252   return res;
    253 error:
    254   goto exit;
    255 }
    256 
    257 /*******************************************************************************
    258  * Local functions
    259  ******************************************************************************/
    260 res_T
    261 load_stream_ascii
    262   (struct sstl* sstl,
    263    FILE* stream,
    264    const char* stream_name)
    265 {
    266   struct txtrdr* txtrdr = NULL;
    267   res_T res = RES_OK;
    268 
    269   ASSERT(sstl && stream && stream_name);
    270 
    271   clear(sstl);
    272 
    273   res = txtrdr_stream(sstl->allocator, stream, stream_name, '#', &txtrdr);
    274   if(res != RES_OK) {
    275     ERROR(sstl, "%s: error creating text reader -- %s\n",
    276       stream_name, res_to_cstr(res));
    277     goto error;
    278   }
    279 
    280   if((res = txtrdr_read_line(txtrdr)) != RES_OK) {
    281     ERROR(sstl, "%s: error reading line -- %s\n", stream_name, res_to_cstr(res));
    282     goto error;
    283   }
    284 
    285   if(txtrdr_get_cline(txtrdr) != NULL) {  /* File is not empty */
    286     if((res = parse_solid(sstl, txtrdr)) != RES_OK) goto error;
    287   }
    288 
    289   sstl->type = SSTL_ASCII;
    290 
    291 exit:
    292   htable_vertex_purge(&sstl->vertex2id); /* Purge the helper structure */
    293   if(txtrdr) txtrdr_ref_put(txtrdr);
    294   return res;
    295 error:
    296   clear(sstl);
    297   goto exit;
    298 }