summaryrefslogtreecommitdiffstats
path: root/src/stl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/stl.c')
-rw-r--r--src/stl.c366
1 files changed, 366 insertions, 0 deletions
diff --git a/src/stl.c b/src/stl.c
new file mode 100644
index 0000000..a4640b3
--- /dev/null
+++ b/src/stl.c
@@ -0,0 +1,366 @@
+#include "stl.h"
+
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#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;
+ }
+}