shithub: libstl

Download patch

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