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);}
--
⑨