star-stl

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

commit 408de0cef4631a5e209dfb2025b598ecec890312
parent 857156284196db738b01e51802a6d28aca66a805
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed,  6 Jan 2016 16:14:02 +0100

Test and fix the load functions

Diffstat:
Mcmake/CMakeLists.txt | 5+++++
Msrc/sstl.c | 84++++++++++++++++++++++++++++++++++++++++---------------------------------------
Asrc/test_sstl_load.c | 191+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 239 insertions(+), 41 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -92,6 +92,11 @@ if(NOT NO_TEST) add_executable(test_sstl ${SSTL_SOURCE_DIR}/test_sstl.c) target_link_libraries(test_sstl sstl) add_test(test_sstl test_sstl) + + add_executable(test_sstl_load ${SSTL_SOURCE_DIR}/test_sstl_load.c) + target_link_libraries(test_sstl_load sstl) + add_test(test_sstl_load test_sstl) + rcmake_set_test_runtime_dirs(test_sstl _runtime_dirs) endif(NOT NO_TEST) diff --git a/src/sstl.c b/src/sstl.c @@ -86,7 +86,9 @@ streamer_init(struct streamer* streamer, FILE* stream, const char* name) memset(streamer, 0, sizeof(struct streamer)); streamer->stream = stream; streamer->name = name; - streamer->iline = 1; + streamer->iline = 0; + streamer->buf = sa_add(streamer->buf, 128); + ASSERT(streamer->buf); } static void @@ -105,6 +107,7 @@ streamer_read_line(struct streamer* streamer) for(;(line=fgets(streamer->buf, (int)sa_size(streamer->buf), streamer->stream)); ++streamer->iline) { + /* Ensure that the whole line is read */ while(!strrchr(line, '\n')) { char* remain = sa_add(streamer->buf, buf_chunk); @@ -115,8 +118,14 @@ streamer_read_line(struct streamer* streamer) if(!fgets(remain, (int)buf_chunk, streamer->stream)) /* EOF */ break; } - if(strcspn(streamer->buf, " \t") != 0) /* Not empty line */ + + if(strspn(streamer->buf, " \t\r\n") != strlen(streamer->buf)) { /* Not empty */ + /* Remove newline character(s) */ + size_t last_char = strlen(line); + while(last_char-- && (line[last_char]=='\n' || line[last_char]=='\r')); + line[last_char + 1] = '\0'; break; + } } ++streamer->iline; return line; @@ -169,8 +178,8 @@ parse_float3 res_T res = RES_OK; ASSERT(str && vert && filename); - tk = strtok(str, " \t"); FOR_EACH(i, 0, 3) { + tk = strtok(i==0 ? str : NULL, " \t"); if(!tk) { print_log(sstl, LOG_ERROR, "%s:%lu: expecting 3D coordinates.\n", filename, (unsigned long)iline); @@ -183,7 +192,6 @@ parse_float3 filename, (unsigned long)iline, tk); return res; } - tk = strtok(NULL, " \t"); } tk = strtok(NULL, "\0"); if(tk) { /* Unexpected remaining chars */ @@ -214,7 +222,7 @@ parse_solid_name } tk = strtok(NULL, " \t"); - if(!tk) { + if(tk) { solid->name = sa_add(solid->name, strlen(tk)+1/*NULL char*/); if(!solid->name) { print_log(sstl, LOG_ERROR, @@ -225,7 +233,7 @@ parse_solid_name } strcpy(solid->name, tk); - if((tk = strtok(NULL, "\0")) && strcspn(tk, " \t")) { /* Trailing chars */ + if((tk = strtok(NULL, "\0")) && strspn(tk, " \t\r\n") != strlen(tk)) { print_log(sstl, LOG_WARNING, "%s:%lu: malformed \"solid [NAME]\" directive.\n"); } @@ -242,16 +250,17 @@ static INLINE res_T parse_solid_vertex (struct sstl* sstl, struct solid* solid, - size_t* index, + size_t* const index, char* line, const char* filename, const size_t iline) { struct vertex vertex; + size_t* found_id; res_T res = RES_OK; ASSERT(sstl && solid && index); - if(!line || !strcmp(strtok(line, " \t"), "vertex")) { + if(!line || strcmp(strtok(line, " \t"), "vertex")) { print_log(sstl, LOG_ERROR, "%s:%lu: missing a \"vertex X Y Z\" directive.\n", filename, (unsigned long)iline); @@ -262,9 +271,11 @@ parse_solid_vertex if(res != RES_OK) return res; /* Look for an already registered vertex position */ - index = htable_vertex_find(&sstl->vertex2id, &vertex); + found_id = htable_vertex_find(&sstl->vertex2id, &vertex); - if(!index) { + if(found_id) { + *index = *found_id; + } else { /* Add a new vertex */ *index = sa_size(solid->indices); res = htable_vertex_set(&sstl->vertex2id, &vertex, index); @@ -273,8 +284,11 @@ parse_solid_vertex "%s:%lu: couldn't register a vertex position.\n", filename, (unsigned long)iline); } + f3_set(sa_add(solid->vertices, 3), vertex.xyz); } + sa_push(solid->indices, *index); + return RES_OK; } @@ -299,7 +313,7 @@ parse_outer_loop } tk = strtok(NULL, "\0"); - if(tk && strcspn(tk, " \t")) { /* Invalid remaining chars */ + if(tk && strspn(tk, " \t\r\n") != strlen(tk)) { /* Invalid remaining chars */ print_log(sstl, LOG_WARNING, "%s:%lu: malformed \"outer loop\" directive.\n", filename, (unsigned long)iline); @@ -326,7 +340,7 @@ parse_directive } tk = strtok(NULL, " \0"); - if(tk && strcspn(tk, " \t")) { /* Invalid remaining chars */ + if(tk && strspn(tk, " \t\r\n") != strlen(tk)) { /* Invalid remaining chars */ print_log(sstl, LOG_WARNING, "%s:%lu: malformed \"%s\" directive.\n", filename, (unsigned long)iline, directive); @@ -348,26 +362,19 @@ load_stream(struct sstl* sstl, FILE* stream, const char* stream_name) streamer_init(&streamer, stream, stream_name); clear(sstl); - if(!sstl || !stream) { - res = RES_BAD_ARG; - goto error; - } - line = streamer_read_line(&streamer); res = parse_solid_name(sstl, &solid, line, streamer.name, streamer.iline); if(res != RES_OK) goto error; for(;;) { /* Parse the solid facets */ - float normal[3], v0[3], v1[3], N[3], len; + float normal[3]; size_t facet[3]; int ivertex; - solid = SOLID_NULL; - line = streamer_read_line(&streamer); - if(!(line = streamer_read_line(&streamer))) { + if(!line) { print_log(sstl, LOG_ERROR, "%s:%lu: missing directive.\n", - streamer.name, (unsigned long)streamer.iline); + streamer.name, (unsigned long)streamer.iline); res = RES_BAD_ARG; goto error; } @@ -380,11 +387,11 @@ load_stream(struct sstl* sstl, FILE* stream, const char* stream_name) /* Parse the facet normal directive */ if(strcmp(tk, "facet") - || !(tk = strtok(NULL, " \t")) - || strcmp(tk, "normal")) { + || !(tk = strtok(NULL, " \t")) + || strcmp(tk, "normal")) { print_log(sstl, LOG_ERROR, - "%s:%lu: missing the \"facet normal X Y Z\" directive.\n", - streamer.name, (unsigned long)streamer.iline); + "%s:%lu: missing or malformed \"facet normal X Y Z\" directive.\n", + streamer.name, (unsigned long)streamer.iline); res = RES_BAD_ARG; goto error; } @@ -405,20 +412,15 @@ load_stream(struct sstl* sstl, FILE* stream, const char* stream_name) if(res != RES_OK) goto error; } - /* Register the facet if it is not degenerated */ - f3_sub(v0, solid.vertices + facet[1]*3, solid.vertices + facet[0]*3); - f3_sub(v1, solid.vertices + facet[2]*3, solid.vertices + facet[0]*3); - f3_cross(N, v0, v1); - len = f3_dot(N, N); - if(len != 0.f) { /* triangle is not degenerated */ - if(!f3_is_normalized(normal)) { /* Use geometry normal */ - f3_divf(normal, N, (float)sqrt(len)); - } - f3_set(sa_add(solid.normals, 3), normal); - sa_push(solid.indices, facet[0]); - sa_push(solid.indices, facet[1]); - sa_push(solid.indices, facet[2]); + if(!f3_is_normalized(normal)) { /* Use geometry normal */ + float v0[3], v1[3]; + /* Vertices are CCW ordered and the normal follows the right handed rule */ + f3_sub(v0, solid.vertices + facet[1]*3, solid.vertices + facet[0]*3); + f3_sub(v1, solid.vertices + facet[2]*3, solid.vertices + facet[0]*3); + f3_cross(normal, v0, v1); + f3_normalize(normal, normal); } + f3_set(sa_add(solid.normals, 3), normal); line = streamer_read_line(&streamer); res = parse_directive(sstl, "endloop", line, streamer.name, streamer.iline); @@ -433,12 +435,12 @@ load_stream(struct sstl* sstl, FILE* stream, const char* stream_name) tk = strtok(NULL, " \t"); if(tk && strcmp(tk, solid.name)) { print_log(sstl, LOG_WARNING, - "%s:%lu: missing or inconsistent \"endsolid\" name.\n", + "%s:%lu: inconsistent \"endsolid\" name.\n", streamer.name, (unsigned long)streamer.iline); } } - if((tk = strtok(NULL, " \0")) && strcspn(tk, " \t")) { /* Invalid chars */ + if((tk = strtok(NULL, " \0")) && strspn(tk, " \t\r\n") != strlen(tk)) { print_log(sstl, LOG_WARNING, "%s:%lu: malformed \"endsolid\" directive\n", streamer.name, streamer.iline); diff --git a/src/test_sstl_load.c b/src/test_sstl_load.c @@ -0,0 +1,191 @@ +/* Copyright (C) |Meso|Star> 2015-2016 (contact@meso-star.com) + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#include "sstl.h" +#include "test_sstl_utils.h" + +#include <rsys/logger.h> + +static void +test_basic(struct sstl* sstl) +{ + static const char* test0 = + "solid\n" + " facet normal 0.0 -1.0 0.0\n" + " outer loop\n" + " vertex 0.0 0.0 0.0\n" + " vertex 1.0 0.0 0.0\n" + " vertex 0.0 0.0 1.0\n" + " endloop\n" + " endfacet\n" + "endsolid"; + static const char* test1 = + "solid my_solid\n" + "\n" + " facet normal 0.0 -1.0 0.0\n" + " outer loop hophophophophop\n" + " vertex\t 0.0 0.0 0.0\n" + " vertex 1.0 0.0 0.0 \taaa\n" + " vertex 0.0 0.0 1.0\n" + " endloop \n" + " endfacet \t\t\t noise\n" + "endsolid pouet\n"; + static const char* test2 = + "solid my_solid\n" + "endsolid my_solid\n"; + + static const char* bad[] = { + "solid\n" + " facet normal 0.0 -1.0 0.0\n" + " outer loop\n" + " vertex 0.0 0.0 0.0\n" + " vertex 1.0 0.0 0.0\n" + " vertex 0.0 0.0 1.0\n" + " endloop\n" + " endfacet\n" + , + " facet normal 0.0 -1.0 0.0\n" + " outer loop\n" + " vertex 0.0 0.0 0.0\n" + " vertex 1.0 0.0 0.0\n" + " vertex 0.0 0.0 1.0\n" + " endloop\n" + " endfacet\n" + "endsolid\n" + , + "solid\n" + " facet 0.0 -1.0 0.0\n" + " outer loop\n" + " vertex 0.0 0.0 0.0\n" + " vertex 1.0 0.0 0.0\n" + " vertex 0.0 0.0 1.0\n" + " endloop\n" + " endfacet\n" + "endsolid\n" + , + "solid\n" + " normal 0.0 -1.0 0.0\n" + " outer loop\n" + " vertex 0.0 0.0 0.0\n" + " vertex 1.0 0.0 0.0\n" + " vertex 0.0 0.0 1.0\n" + " endloop\n" + " endfacet\n" + "endsolid\n" + , + "solid\n" + " facet normal 0.0 -1.0 0.0\n" + " outer loop\n" + " vertex 0.0 0.0 a.0\n" + " vertex 1.0 0.0 0.0\n" + " vertex 0.0 0.0 1.0\n" + " endloop\n" + " endfacet\n" + "endsolid\n" + , + "solid\n" + " facet normal 0.0 -1.0 0.0\n" + " outer loop\n" + " vertex 1.0 0.0 0.0\n" + " vertex 0.0 0.0 1.0\n" + " endloop\n" + " endfacet\n" + "endsolid\n" + , + "solid\n" + " facet normal 0.0 -1.0 0.0\n" + " vertex 0.0 0.0 0.0\n" + " vertex 1.0 0.0 0.0\n" + " vertex 0.0 0.0 1.0\n" + " endfacet\n" + "endsolid\n" + }; + const size_t nbads = sizeof(bad)/sizeof(const char*); + + FILE* file; + size_t i; + + NCHECK(sstl, NULL); + + file = fopen("test_basic.stl", "w"); + NCHECK(file, NULL); + fwrite(test0, sizeof(char), strlen(test0), file); + fclose(file); + + CHECK(sstl_load(NULL, NULL), RES_BAD_ARG); + CHECK(sstl_load(sstl, NULL), RES_BAD_ARG); + CHECK(sstl_load(NULL, "test_basic.stl"), RES_BAD_ARG); + CHECK(sstl_load(sstl, "none.stl"), RES_IO_ERR); + CHECK(sstl_load(sstl, "test_basic.stl"), RES_OK); + + file = tmpfile(); + NCHECK(file, NULL); + fwrite(test1, sizeof(char), strlen(test1), file); + rewind(file); + CHECK(sstl_load_stream(NULL, NULL), RES_BAD_ARG); + CHECK(sstl_load_stream(sstl, NULL), RES_BAD_ARG); + CHECK(sstl_load_stream(NULL, file), RES_BAD_ARG); + CHECK(sstl_load_stream(sstl, file), RES_OK); + fclose(file); + + file = tmpfile(); + fwrite(test2, sizeof(char), strlen(test2), file); + rewind(file); + CHECK(sstl_load_stream(sstl, file), RES_OK); + fclose(file); + + FOR_EACH(i, 0, nbads) { + file = tmpfile(); + fwrite(bad[i], sizeof(char), strlen(bad[i]), file); + rewind(file); + CHECK(sstl_load_stream(sstl, file), RES_BAD_ARG); + fclose(file); + } +} + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct sstl* sstl; + (void)argc, (void)argv; + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + CHECK(sstl_create(NULL, &allocator, 1, &sstl), RES_OK); + + test_basic(sstl); + + CHECK(sstl_ref_put(sstl), RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHECK(mem_allocated_size(), 0); + return 0; +} +