star-stl

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

sstl.c (7811B)


      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.h"
     19 #include "sstl_c.h"
     20 
     21 #include <rsys/cstr.h>
     22 #include <rsys/rsys.h>
     23 #include <rsys/float3.h>
     24 #include <rsys/stretchy_array.h>
     25 
     26 #include <errno.h>
     27 #include <stdio.h>
     28 
     29 /*******************************************************************************
     30  * Helper functions
     31  ******************************************************************************/
     32 static res_T
     33 file_type
     34   (struct sstl* sstl,
     35    FILE* fp,
     36    const char* name,
     37    enum sstl_type* type)
     38 {
     39   char buf[1024];
     40   size_t sz = 0;
     41   long fpos = 0;
     42   res_T res = RES_OK;
     43 
     44   ASSERT(sstl && fp && name && type);
     45 
     46   if(!file_is_seekable(fp)) {
     47     ERROR(sstl,
     48       "%s: the file refers to a pipe, a FIFO or a socket. "
     49       "Its type (i.e. ASCII or binary) cannot be defined on the fly.\n",
     50       name);
     51     res = RES_BAD_ARG;
     52     goto error;
     53   }
     54 
     55   if((fpos = ftell(fp)) < 0) {
     56     ERROR(sstl, "%s: unable to query file position -- %s\n",
     57       name, strerror(errno));
     58     res = RES_IO_ERR;
     59     goto error;
     60   }
     61 
     62   /* Search for the NUL character in the first bytes of the file. If there is
     63    * one, the file is assumed to be binary. This is a 'simple and stupid', yet
     64    * robust method, used for example by some grep implementations */
     65   sz = fread(buf, 1, sizeof(buf), fp);
     66   if(memchr(buf, '\0', sz)) {
     67     *type = SSTL_BINARY;
     68   } else {
     69     *type = SSTL_ASCII;
     70   }
     71 
     72   if(fseek(fp, fpos, SEEK_SET) < 0) {
     73     ERROR(sstl, "%s: unable to set file position -- %s\n",
     74       name, strerror(errno));
     75     res = RES_IO_ERR;
     76     goto error;
     77   }
     78 
     79 exit:
     80   return res;
     81 error:
     82   goto exit;
     83 }
     84 
     85 static res_T
     86 load_stream
     87   (struct sstl* sstl,
     88    FILE* fp,
     89    const char* name,
     90    enum sstl_type type)
     91 {
     92   res_T res = RES_OK;
     93   ASSERT((unsigned)type <= SSTL_NONE__);
     94 
     95   if(!sstl || !fp || !name) { res = RES_BAD_ARG; goto error; }
     96 
     97   if(type == SSTL_NONE__) {
     98     res = file_type(sstl, fp, name, &type);
     99     if(res != RES_OK) goto error;
    100   }
    101 
    102   if((res = str_set(&sstl->filename, name)) != RES_OK) {
    103     ERROR(sstl, "Error copying file name '%s' -- %s\n",
    104       name, res_to_cstr(res));
    105     goto error;
    106   }
    107 
    108   switch(type) {
    109     case SSTL_ASCII: res = load_stream_ascii(sstl, fp, name); break;
    110     case SSTL_BINARY: res = load_stream_binary(sstl, fp, name); break;
    111     default: FATAL("Unreachable code\n"); break;
    112   }
    113   if(res != RES_OK) goto error;
    114 
    115 exit:
    116   return res;
    117 error:
    118   goto exit;
    119 }
    120 
    121 static res_T
    122 load(struct sstl* sstl, const char* filename, const enum sstl_type type)
    123 {
    124   FILE* stream = NULL;
    125   res_T res = RES_OK;
    126 
    127   ASSERT((unsigned)type <= SSTL_NONE__);
    128 
    129   if(!sstl || !filename) { res = RES_BAD_ARG; goto error; }
    130 
    131   stream = fopen(filename, "r");
    132   if(!stream) {
    133     ERROR(sstl, "Error opening file %s -- %s\n", filename, strerror(errno));
    134     res = RES_IO_ERR;
    135     goto error;
    136   }
    137 
    138   res = load_stream(sstl, stream, filename, type);
    139   if(res != RES_OK) goto error;
    140 
    141 exit:
    142   if(stream) CHK(fclose(stream) == 0);
    143   return res;
    144 error:
    145   goto exit;
    146 }
    147 
    148 static void
    149 sstl_release(ref_T* ref)
    150 {
    151   struct sstl* sstl;
    152   ASSERT(ref);
    153   sstl = CONTAINER_OF(ref, struct sstl, ref);
    154   str_release(&sstl->filename);
    155   str_release(&sstl->name);
    156   htable_vertex_release(&sstl->vertex2id);
    157   sa_release(sstl->vertices);
    158   sa_release(sstl->normals);
    159   sa_release(sstl->indices);
    160   MEM_RM(sstl->allocator, sstl);
    161 }
    162 
    163 /*******************************************************************************
    164  * Local functions
    165  ******************************************************************************/
    166 res_T
    167 register_vertex(struct sstl* sstl, const float v[3])
    168 {
    169   struct vertex vtx;
    170   unsigned* found = NULL;
    171   unsigned id = 0;
    172   res_T res = RES_OK;
    173   ASSERT(sstl && v);
    174 
    175   /* Check if the input vertex is already registered */
    176   f3_set(vtx.xyz, v);
    177   found = htable_vertex_find(&sstl->vertex2id, &vtx);
    178 
    179   if(found) { /* The vertex already exists */
    180     id = *found;
    181 
    182   } else { /* The vertex is a new one */
    183     id = (unsigned)sa_size(sstl->vertices)/3;
    184     res = htable_vertex_set(&sstl->vertex2id, &vtx, &id);
    185     if(res != RES_OK) goto error;
    186 
    187     /* Add a new vertex */
    188     f3_set(sa_add(sstl->vertices, 3), vtx.xyz);
    189   }
    190 
    191   /* Register the vertex index */
    192   sa_push(sstl->indices, id);
    193 
    194 exit:
    195   return res;
    196 error:
    197   goto exit;
    198 }
    199 
    200 /*******************************************************************************
    201  * Exported functions
    202  ******************************************************************************/
    203 res_T
    204 sstl_create
    205   (struct logger* log,
    206    struct mem_allocator* mem_allocator,
    207    const int verbose,
    208    struct sstl** out_sstl)
    209 {
    210   struct mem_allocator* allocator = NULL;
    211   struct logger* logger = NULL;
    212   struct sstl* sstl = NULL;
    213   res_T res = RES_OK;
    214 
    215   if(!out_sstl) {
    216     res = RES_BAD_ARG;
    217     goto error;
    218   }
    219 
    220   allocator = mem_allocator ? mem_allocator : &mem_default_allocator;
    221   logger = log ? log : LOGGER_DEFAULT;
    222 
    223   sstl = MEM_CALLOC(allocator, 1, sizeof(struct sstl));
    224   if(!sstl) {
    225     if(verbose) {
    226       logger_print(logger, LOG_ERROR,
    227         "Couldn't allocate the Star-STL device.\n");
    228     }
    229     res = RES_MEM_ERR;
    230     goto error;
    231   }
    232 
    233   ref_init(&sstl->ref);
    234   sstl->allocator = allocator;
    235   sstl->logger = logger;
    236   sstl->verbose = verbose;
    237   htable_vertex_init(allocator, &sstl->vertex2id);
    238   str_init(allocator, &sstl->filename);
    239   str_init(allocator, &sstl->name);
    240 
    241 exit:
    242   if(out_sstl) *out_sstl = sstl;
    243   return res;
    244 error:
    245   if(sstl) {
    246     SSTL(ref_put(sstl));
    247     sstl = NULL;
    248   }
    249   goto exit;
    250 }
    251 
    252 res_T
    253 sstl_ref_get(struct sstl* sstl)
    254 {
    255   if(!sstl) return RES_BAD_ARG;
    256   ref_get(&sstl->ref);
    257   return RES_OK;
    258 }
    259 
    260 res_T
    261 sstl_ref_put(struct sstl* sstl)
    262 {
    263   if(!sstl) return RES_BAD_ARG;
    264   ref_put(&sstl->ref, sstl_release);
    265   return RES_OK;
    266 }
    267 
    268 res_T
    269 sstl_load(struct sstl* sstl, const char* filename)
    270 {
    271   return load(sstl, filename, SSTL_NONE__);
    272 }
    273 
    274 res_T
    275 sstl_load_ascii(struct sstl* sstl, const char* filename)
    276 {
    277   return load(sstl, filename, SSTL_ASCII);
    278 }
    279 
    280 res_T
    281 sstl_load_binary(struct sstl* sstl, const char* filename)
    282 {
    283   return load(sstl, filename, SSTL_BINARY);
    284 }
    285 
    286 res_T
    287 sstl_load_stream(struct sstl* sstl, FILE* fp, const char* name)
    288 {
    289   return load_stream(sstl, fp, name, SSTL_NONE__);
    290 }
    291 
    292 res_T
    293 sstl_load_stream_ascii(struct sstl* sstl, FILE* fp, const char* name)
    294 {
    295   return load_stream(sstl, fp, name, SSTL_ASCII);
    296 }
    297 
    298 res_T
    299 sstl_load_stream_binary(struct sstl* sstl, FILE* fp, const char* name)
    300 {
    301   return load_stream(sstl, fp, name, SSTL_BINARY);
    302 }
    303 
    304 res_T
    305 sstl_get_desc(struct sstl* sstl, struct sstl_desc* desc)
    306 {
    307   if(!sstl || !desc) return RES_BAD_ARG;
    308 
    309   ASSERT(sa_size(sstl->vertices) % 3 == 0);
    310   ASSERT(sa_size(sstl->normals) % 3 == 0);
    311   ASSERT(sa_size(sstl->indices) % 3 == 0);
    312 
    313   desc->filename = str_cget(&sstl->filename);
    314   desc->solid_name = str_len(&sstl->name) ? str_cget(&sstl->name) : NULL;
    315   desc->vertices_count = sa_size(sstl->vertices) / 3/*#coords*/;
    316   desc->triangles_count = sa_size(sstl->indices) / 3/*#ids*/;
    317   desc->vertices = sstl->vertices;
    318   desc->indices = sstl->indices;
    319   desc->normals = sstl->normals;
    320   desc->type = sstl->type;
    321   return RES_OK;
    322 }