#include "stl.h" #include #include #include #include #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define MIN(a, b) ((a) > (b) ? (b) : (a)) enum { STL_ASCII_SOLID, STL_ASCII_SOLID_NAME, STL_ASCII_ENDSOLID_CHECK, STL_ASCII_FACET_NORMAL, STL_ASCII_FACET_NORMAL_I, STL_ASCII_FACET_NORMAL_J, STL_ASCII_FACET_NORMAL_K, STL_ASCII_OUTER_LOOP, STL_ASCII_VERTEX_1, STL_ASCII_VERTEX_1_X, STL_ASCII_VERTEX_1_Y, STL_ASCII_VERTEX_1_Z, STL_ASCII_VERTEX_2, STL_ASCII_VERTEX_2_X, STL_ASCII_VERTEX_2_Y, STL_ASCII_VERTEX_2_Z, STL_ASCII_VERTEX_3, STL_ASCII_VERTEX_3_X, STL_ASCII_VERTEX_3_Y, STL_ASCII_VERTEX_3_Z, STL_ASCII_ENDLOOP, STL_ASCII_ENDFACET, STL_ASCII_ENDSOLID }; enum { STL_BIN_HEADER, STL_BIN_TRIANGLE }; static const uint8_t ascii_fsm[] = { [STL_ASCII_SOLID] = STL_ASCII_SOLID_NAME, [STL_ASCII_SOLID_NAME] = STL_ASCII_ENDSOLID_CHECK, [STL_ASCII_ENDSOLID_CHECK] = STL_ASCII_FACET_NORMAL, [STL_ASCII_FACET_NORMAL] = STL_ASCII_FACET_NORMAL_I, [STL_ASCII_FACET_NORMAL_I] = STL_ASCII_FACET_NORMAL_K, [STL_ASCII_FACET_NORMAL_K] = STL_ASCII_FACET_NORMAL_J, [STL_ASCII_FACET_NORMAL_J] = STL_ASCII_OUTER_LOOP, [STL_ASCII_OUTER_LOOP] = STL_ASCII_VERTEX_1, [STL_ASCII_VERTEX_1] = STL_ASCII_VERTEX_1_X, [STL_ASCII_VERTEX_1_X] = STL_ASCII_VERTEX_1_Y, [STL_ASCII_VERTEX_1_Y] = STL_ASCII_VERTEX_1_Z, [STL_ASCII_VERTEX_1_Z] = STL_ASCII_VERTEX_2, [STL_ASCII_VERTEX_2] = STL_ASCII_VERTEX_2_X, [STL_ASCII_VERTEX_2_X] = STL_ASCII_VERTEX_2_Y, [STL_ASCII_VERTEX_2_Y] = STL_ASCII_VERTEX_2_Z, [STL_ASCII_VERTEX_2_Z] = STL_ASCII_VERTEX_3, [STL_ASCII_VERTEX_3] = STL_ASCII_VERTEX_3_X, [STL_ASCII_VERTEX_3_X] = STL_ASCII_VERTEX_3_Y, [STL_ASCII_VERTEX_3_Y] = STL_ASCII_VERTEX_3_Z, [STL_ASCII_VERTEX_3_Z] = STL_ASCII_ENDLOOP, [STL_ASCII_ENDLOOP] = STL_ASCII_ENDFACET, [STL_ASCII_ENDFACET] = STL_ASCII_ENDSOLID_CHECK, }; static void stl_skip_spn(struct stl *stl, const char *spn) { const char *c; size_t i; c = stl->pos; for (i = 0; i < stl->nleft; i++, c++) { if (!strchr(spn, *c)) break; } stl->pos += i; stl->nleft -= i; } static int stl_read_n(struct stl *stl, size_t n) { size_t cnt; if (stl->buflen >= n) return STL_OK; cnt = MIN(n - stl->buflen, stl->nleft); memcpy(stl->buf + stl->buflen, stl->pos, cnt); stl->buflen += cnt; stl->pos += cnt; stl->nleft -= cnt; if (stl->buflen < n) return STL_INCOMPLETE; return STL_OK; } static int stl_read_expect(struct stl *stl, const char *str, size_t n) { size_t i, cnt; int rc; rc = stl_read_n(stl, n); if (rc) return rc; if (strncmp(stl->buf, str, n)) return STL_INVALID; stl->buflen = 0; return STL_OK; } static int stl_read_until(struct stl *stl, size_t *len, char *any, size_t max) { const char *c; size_t i; c = stl->pos; for (i = 0; i < stl->nleft; i++, c++) { if (strchr(any, *c)) { *len = stl->buflen; stl->buflen = 0; return STL_OK; } if (stl->buflen >= max) return STL_INVALID; stl->buf[stl->buflen++] = *c; stl->pos++; stl->nleft--; } return STL_INCOMPLETE; } static int stl_feed_binary(struct stl *stl, struct stl_result *res, const void *chunk, size_t size) { int rc; if (stl->bin.index == stl->bin.count) return STL_DONE; while (stl->nleft > 0) { switch (stl->bin.state) { case STL_BIN_HEADER: rc = stl_read_n(stl, 84); if (rc) return rc; stl->bin.count = le32toh(*(uint32_t *)(stl->buf + 80)); stl->bin.state = STL_BIN_TRIANGLE; res->type = STL_RES_HEADER; res->header.str = stl->buf; res->header.len = 80; return STL_OK; case STL_BIN_TRIANGLE: rc = stl_read_n(stl, 50); if (rc) return rc; res->type = STL_RES_TRIANGLE; res->tri.normal.x = le32toh(*(uint32_t *)(stl->buf + 0)); res->tri.normal.y = le32toh(*(uint32_t *)(stl->buf + 4)); res->tri.normal.z = le32toh(*(uint32_t *)(stl->buf + 8)); res->tri.vtx[0].x = le32toh(*(uint32_t *)(stl->buf + 12)); res->tri.vtx[0].y = le32toh(*(uint32_t *)(stl->buf + 16)); res->tri.vtx[0].z = le32toh(*(uint32_t *)(stl->buf + 20)); res->tri.vtx[1].x = le32toh(*(uint32_t *)(stl->buf + 24)); res->tri.vtx[1].y = le32toh(*(uint32_t *)(stl->buf + 28)); res->tri.vtx[1].z = le32toh(*(uint32_t *)(stl->buf + 32)); res->tri.vtx[2].x = le32toh(*(uint32_t *)(stl->buf + 36)); res->tri.vtx[2].y = le32toh(*(uint32_t *)(stl->buf + 40)); res->tri.vtx[2].z = le32toh(*(uint32_t *)(stl->buf + 40)); stl->bin.index++; return STL_OK; default: return STL_INVALID_ARG; } } return STL_INCOMPLETE; } static int stl_feed_ascii(struct stl *stl, struct stl_result *res, const void *chunk, size_t size) { char *end; float val; size_t len; int rc; while (stl->nleft > 0) { switch (stl->ascii.state) { case STL_ASCII_SOLID: rc = stl_read_expect(stl, "solid ", 6); if (rc) return rc; break; case STL_ASCII_SOLID_NAME: rc = stl_read_until(stl, &len, "\n", STL_BUFMAX - 1); if (rc) return rc; stl->buf[len] = '\0'; res->type = STL_RES_SOLID_NAME; res->solid_name.str = stl->buf; res->solid_name.len = len; stl->ascii.state = STL_ASCII_FACET_NORMAL; return STL_OK; case STL_ASCII_FACET_NORMAL: if (!stl->buflen) stl_skip_spn(stl, " \t\v\n\r"); rc = stl_read_expect(stl, "facet normal ", 13); if (rc) return rc; break; case STL_ASCII_OUTER_LOOP: if (!stl->buflen) stl_skip_spn(stl, " \t\v\n\r"); rc = stl_read_expect(stl, "outer loop", 10); if (rc) return rc; break; case STL_ASCII_FACET_NORMAL_I: case STL_ASCII_FACET_NORMAL_J: case STL_ASCII_FACET_NORMAL_K: case STL_ASCII_VERTEX_1_X: case STL_ASCII_VERTEX_1_Y: case STL_ASCII_VERTEX_1_Z: case STL_ASCII_VERTEX_2_X: case STL_ASCII_VERTEX_2_Y: case STL_ASCII_VERTEX_2_Z: case STL_ASCII_VERTEX_3_X: case STL_ASCII_VERTEX_3_Y: case STL_ASCII_VERTEX_3_Z: if (!stl->buflen) stl_skip_spn(stl, " \t"); rc = stl_read_until(stl, &len, " \n", STL_BUFMAX - 1); if (rc) return rc; stl->buf[len] = '\0'; val = strtof(stl->buf, &end); if (end && *end) return STL_INVALID; switch (stl->ascii.state) { case STL_ASCII_FACET_NORMAL_I: stl->tri.normal.x = val; break; case STL_ASCII_FACET_NORMAL_J: stl->tri.normal.y = val; break; case STL_ASCII_FACET_NORMAL_K: stl->tri.normal.z = val; break; case STL_ASCII_VERTEX_1_X: stl->tri.vtx[0].x = val; break; case STL_ASCII_VERTEX_1_Y: stl->tri.vtx[0].y = val; break; case STL_ASCII_VERTEX_1_Z: stl->tri.vtx[0].z = val; break; case STL_ASCII_VERTEX_2_X: stl->tri.vtx[1].x = val; break; case STL_ASCII_VERTEX_2_Y: stl->tri.vtx[1].y = val; break; case STL_ASCII_VERTEX_2_Z: stl->tri.vtx[1].z = val; break; case STL_ASCII_VERTEX_3_X: stl->tri.vtx[2].x = val; break; case STL_ASCII_VERTEX_3_Y: stl->tri.vtx[2].y = val; break; case STL_ASCII_VERTEX_3_Z: stl->tri.vtx[2].z = val; break; } break; case STL_ASCII_VERTEX_1: case STL_ASCII_VERTEX_2: case STL_ASCII_VERTEX_3: if (!stl->buflen) stl_skip_spn(stl, " \t\v\n\r"); rc = stl_read_expect(stl, "vertex", 6); if (rc) return rc; break; case STL_ASCII_ENDLOOP: if (!stl->buflen) stl_skip_spn(stl, " \t\v\n\r"); rc = stl_read_expect(stl, "endloop", 7); if (rc) return rc; break; case STL_ASCII_ENDFACET: if (!stl->buflen) stl_skip_spn(stl, " \t\v\n\r"); rc = stl_read_expect(stl, "endfacet", 8); if (rc) return rc; res->type = STL_RES_TRIANGLE; res->tri = stl->tri; stl->ascii.state = STL_ASCII_ENDSOLID_CHECK; return STL_OK; case STL_ASCII_ENDSOLID_CHECK: if (!stl->buflen) stl_skip_spn(stl, " \t\v\n\r"); rc = stl_read_expect(stl, "endsolid", 8); if (!rc) return STL_DONE; break; defualt: return STL_INVALID_ARG; }; stl->ascii.state = ascii_fsm[stl->ascii.state]; } return STL_INCOMPLETE; } void stl_init(struct stl *stl, int type) { stl->type = type; stl->buflen = 0; stl->chunk = NULL; stl->pos = NULL; stl->nleft = 0; } int stl_feed(struct stl *stl, struct stl_result *res, const void *chunk, size_t size) { size_t cnt; int rc; if (stl->chunk != chunk) { if (stl->nleft > 0) return STL_INVALID; stl->chunk = chunk; stl->pos = stl->chunk; stl->nleft = size; } if (stl->type == STL_TYPE_DETECT) { rc = stl_read_expect(stl, "solid ", 6); if (rc == STL_INCOMPLETE) return STL_INCOMPLETE; if (rc == STL_OK) { printf("ascii file\n"); stl->type = STL_TYPE_ASCII; stl->ascii.state = STL_ASCII_SOLID_NAME; } else { printf("binary file\n"); stl->type = STL_TYPE_BINARY; stl->bin.state = STL_BIN_HEADER; stl->bin.index = 0; stl->bin.count = SIZE_MAX; } res->type = STL_RES_FILETYPE; res->filetype = stl->type; return STL_OK; } switch (stl->type) { case STL_TYPE_ASCII: return stl_feed_ascii(stl, res, chunk, size); case STL_TYPE_BINARY: return stl_feed_binary(stl, res, chunk, size); default: return STL_INVALID_ARG; } }