ref: ed5804b467eb0958aade53a475b4975762dcedf8
parent: 222bf07cdb99049713271bba455e07e5e4e55e71
author: rodri <rgl@antares-labs.eu>
date: Sat Mar 15 17:44:37 EDT 2025
add ascii format support. turn vertices into 3x3 matrix
--- a/readme.md
+++ b/readme.md
@@ -1,3 +1,3 @@
# libstl
-Libstl provides a parser for the STL (binary) file format.
+Libstl provides a parser for the STL text and binary file formats.
--- a/stl.c
+++ b/stl.c
@@ -3,8 +3,28 @@
#include <bio.h>
#include "stl.h"
-static int bunpack(Biobuf*, char*, ...);
+#define min(a,b) ((a)<(b)?(a):(b))
+typedef struct Line Line;
+struct Line
+{
+ char *file;
+ ulong line;
+};
+
+static void
+error(Line *l, char *fmt, ...)
+{
+ va_list va;
+ char buf[ERRMAX], *bp;
+
+ va_start(va, fmt);
+ bp = seprint(buf, buf + sizeof(buf), "line %lud: ", l->line);
+ vseprint(bp, buf + sizeof(buf), fmt, va);
+ va_end(va);
+ werrstr("%s", buf);
+}
+
static int
Bgets(Biobuf *b, u16int *s)
{
@@ -44,6 +64,8 @@
return 0;
}
+static int bunpack(Biobuf*, char*, ...);
+
static int
vbunpack(Biobuf *b, char *fmt, va_list a)
{
@@ -103,26 +125,183 @@
return n;
}
-Stl *
-readstl(int fd)
+static char *
+getline(Line *l, Biobuf *b)
{
- Biobuf *bin;
+ char *line;
+
+ line = Brdstr(b, '\n', 1);
+ if(line == nil){
+ werrstr("could not read a line");
+ return nil;
+ }
+ l->line++;
+ return line;
+}
+
+static Stl *
+parsetxt(Biobuf *bin)
+{
+ Line ctx;
Stltri *tri;
Stl *stl;
- u32int i, ntris;
+ char *line, *f[5];
+ int nf, i;
- bin = Bfdopen(fd, OREAD);
- if(bin == nil){
- werrstr("Bfdopen: %r");
+ memset(&ctx, 0, sizeof(Line));
+
+ /* header */
+ if((line = getline(&ctx, bin)) == nil){
+ error(&ctx, "getline: %r");
return nil;
}
stl = mallocz(sizeof(Stl), 1);
if(stl == nil){
+ error(&ctx, "mallocz0: %r");
+ free(line);
+ return nil;
+ }
+ memmove(stl->hdr, line, min(sizeof(stl->hdr), Blinelen(bin)));
+
+ for(;;){
+ /* facet normal i j k */
+ if((line = getline(&ctx, bin)) == nil){
+badline0:
+ error(&ctx, "getline: %r");
+ goto error;
+ }
+ nf = tokenize(line, f, nelem(f));
+repeat:
+ if(nf != 5){
+notenough0:
+ error(&ctx, "not enough fields %d", nf);
+cleanup0:
+ free(line);
+ goto error;
+ }
+ if(strcmp(f[0], "facet") == 0 && strcmp(f[1], "normal") == 0){
+ tri = mallocz(sizeof(Stltri), 1);
+ if(tri == nil){
+ werrstr("mallocz1: %r");
+ goto cleanup0;
+ }
+ tri->n[0] = strtod(f[2], nil);
+ tri->n[1] = strtod(f[3], nil);
+ tri->n[2] = strtod(f[4], nil);
+ }else{
+ error(&ctx, "expected \"facet normal n₀ n₁ n₂\"");
+ goto cleanup0;
+ }
+ free(line);
+
+ /* outer loop */
+ if((line = getline(&ctx, bin)) == nil){
+badline1:
+ free(tri);
+ goto badline0;
+ }
+ nf = tokenize(line, f, nelem(f));
+ if(nf != 2){
+notenough1:
+ free(tri);
+ goto notenough0;
+ }
+ if(strcmp(f[0], "outer") != 0 && strcmp(f[1], "loop") != 0){
+ error(&ctx, "expected \"outer loop\"");
+cleanup1:
+ free(tri);
+ goto cleanup0;
+ }
+ free(line);
+
+ /* [3]vertex x y z */
+ for(i = 0; i < 3; i++){
+ if((line = getline(&ctx, bin)) == nil)
+ goto badline1;
+ nf = tokenize(line, f, nelem(f));
+ if(nf != 4)
+ goto notenough1;
+ if(strcmp(f[0], "vertex") == 0){
+ tri->v[i][0] = strtod(f[1], nil);
+ tri->v[i][1] = strtod(f[2], nil);
+ tri->v[i][2] = strtod(f[3], nil);
+ }else{
+ error(&ctx, "expected \"vertex x y z\"");
+ goto cleanup1;
+ }
+ free(line);
+ }
+
+ /* endloop */
+ if((line = getline(&ctx, bin)) == nil){
+ goto badline1;
+ }
+ nf = tokenize(line, f, nelem(f));
+ if(nf != 1){
+ goto notenough1;
+ }
+ if(strcmp(f[0], "endloop") != 0){
+ error(&ctx, "expected \"endloop\"");
+ goto cleanup1;
+ }
+ free(line);
+
+ /* endfacet */
+ if((line = getline(&ctx, bin)) == nil){
+ goto badline1;
+ }
+ nf = tokenize(line, f, nelem(f));
+ if(nf != 1){
+ goto notenough1;
+ }
+ if(strcmp(f[0], "endfacet") == 0){
+ stl->tris = realloc(stl->tris, (stl->ntris+1)*sizeof(*stl->tris));
+ if(stl->tris == nil){
+ error(&ctx, "realloc1: %r");
+ goto cleanup1;
+ }
+ stl->tris[stl->ntris++] = tri;
+ }else{
+ error(&ctx, "expected \"endfacet\"");
+ goto cleanup1;
+ }
+ free(line);
+
+ /* endsolid? */
+ if((line = getline(&ctx, bin)) == nil){
+ goto badline0;
+ }
+ nf = tokenize(line, f, nelem(f));
+ if(nf < 2){
+ goto notenough0;
+ }
+ if(nf == 2 && strcmp(f[0], "endsolid") == 0)
+ break;
+ else
+ goto repeat;
+ }
+
+ return stl;
+error:
+ freestl(stl);
+ return nil;
+}
+
+static Stl *
+parsebin(Biobuf *bin, char *magic)
+{
+ Stltri *tri;
+ Stl *stl;
+ u32int i, ntris;
+
+ stl = mallocz(sizeof(Stl), 1);
+ if(stl == nil){
werrstr("mallocz0: %r");
- goto error;
+ return nil;
}
- if(bunpack(bin, "[l", stl->hdr, sizeof(stl->hdr), &ntris) < 0){
+ memmove(stl->hdr, magic, 5);
+ if(bunpack(bin, "[l", stl->hdr+5, sizeof(stl->hdr)-5, &ntris) < 0){
werrstr("hdr bunpack: %r");
goto error;
}
@@ -133,7 +312,7 @@
werrstr("mallocz1: %r");
goto error;
}
- if(bunpack(bin, "vvvvs", tri->n, tri->p0, tri->p1, tri->p2, &tri->attrlen) < 0){
+ if(bunpack(bin, "vvvvs", tri->n, tri->v[0], tri->v[1], tri->v[2], &tri->attrlen) < 0){
free(tri);
werrstr("tri bunpack0: %r");
goto error;
@@ -158,12 +337,43 @@
stl->tris[stl->ntris++] = tri;
}
- Bterm(bin);
return stl;
error:
- Bterm(bin);
freestl(stl);
return nil;
+}
+
+Stl *
+readstl(int fd)
+{
+ Biobuf *bin;
+ Stl *stl;
+ char magic[5+1];
+
+ stl = nil;
+
+ bin = Bfdopen(fd, OREAD);
+ if(bin == nil){
+ werrstr("Bfdopen: %r");
+ return nil;
+ }
+
+ if(Bread(bin, magic, 5) != 5){
+ werrstr("Bread: could not read 5 bytes");
+ goto out;
+ }
+ magic[5] = 0;
+
+ if(strcmp(magic, "solid") == 0){
+ if((stl = parsetxt(bin)) == nil)
+ werrstr("parsetxt: %r");
+ }else
+ if((stl = parsebin(bin, magic)) == nil)
+ werrstr("parsebin: %r");
+
+out:
+ Bterm(bin);
+ return stl;
}
void
--- a/stl.h
+++ b/stl.h
@@ -16,9 +16,7 @@
struct Stltri
{
float n[3];
- float p0[3];
- float p1[3];
- float p2[3];
+ float v[3][3];
u16int attrlen;
u8int attrs[];
};
--- a/test/main.c
+++ b/test/main.c
@@ -29,11 +29,11 @@
print("\t%08d\n\t\tn\t", i);
printfv(stl->tris[i]->n);
print("\n\t\tp0\t");
- printfv(stl->tris[i]->p0);
+ printfv(stl->tris[i]->v[0]);
print("\n\t\tp1\t");
- printfv(stl->tris[i]->p1);
+ printfv(stl->tris[i]->v[1]);
print("\n\t\tp2\t");
- printfv(stl->tris[i]->p2);
+ printfv(stl->tris[i]->v[2]);
print("\n\t\tattrlen\t%d\n", stl->tris[i]->attrlen);
print("\n\t\tattrs: %.*s\n", stl->tris[i]->attrlen, (char*)stl->tris[i]->attrs);
}