shithub: libstl

Download patch

ref: d5147306c711858fe4c0380949c36f8358af7f3e
parent: 2a1566a9ccda2ec5a256b16306c04cf8b43c14b5
author: rodri <rgl@antares-labs.eu>
date: Sat May 24 20:19:49 EDT 2025

add writestl(2)

--- a/stl.2.man
+++ b/stl.2.man
@@ -1,6 +1,7 @@
 .TH STL 2
 .SH NAME
 readstl,
+writestl,
 freestl
 \- STL parser
 .SH SYNOPSIS
@@ -10,6 +11,11 @@
 #include <libc.h>
 #include <stl.h>
 
+enum {
+	STLTEXT,
+	STLBINARY,
+};
+
 typedef struct Stltri Stltri;
 typedef struct Stl Stl;
 
@@ -29,6 +35,7 @@
 };
 
 Stl *readstl(int fd);
+usize writestl(int fd, Stl *stl, int fmt);
 void freestl(Stl *stl);
 .EE
 .SH DESCRIPTION
@@ -40,9 +47,11 @@
 or solid.
 .PP
 .B Readstl
-takes an fd open for reading, and returns an allocated Stl structure
-filled with the model it contains.  If the file couldn't be parsed, it
-will write the reason to the errstr and return nil.
+takes an
+.I fd
+open for reading, and returns an allocated Stl structure filled with
+the model it contains.  If the file couldn't be parsed, it will write
+the reason to the errstr and return nil.
 .PP
 .B Freestl
 frees an Stl structure created by
@@ -58,6 +67,16 @@
 .B Stltri
 respectively are not interpreted in any way, and are provided as
 blobs; it's up to the user to give them some meaning.
+.PP
+.B Writestl
+takes an
+.I fd
+open for writing and dumps the
+.I stl
+structure with the given
+.I fmt
+(STLTEXT or STLBINARY).  If an error
+occurs, it will write the reason to the errstr and return zero.
 .SH SOURCE
 .B /sys/src/libstl
 .SH SEE ALSO
--- a/stl.c
+++ b/stl.c
@@ -71,6 +71,20 @@
 }
 
 static int
+Bputs(Biobuf *b, u16int s)
+{
+	uchar buf[2];
+
+	buf[0] = s;
+	buf[1] = s >> 8;
+	if(Bwrite(b, buf, 2) != 2){
+		werrstr("could not put 2 bytes");
+		return -1;
+	}
+	return 0;
+}
+
+static int
 Bgetl(Biobuf *b, u32int *l)
 {
 	uchar buf[4];
@@ -84,6 +98,22 @@
 }
 
 static int
+Bputl(Biobuf *b, u32int l)
+{
+	uchar buf[4];
+
+	buf[0] = l;
+	buf[1] = l >> 8;
+	buf[2] = l >> 16;
+	buf[3] = l >> 24;
+	if(Bwrite(b, buf, 4) != 4){
+		werrstr("could not put 4 bytes");
+		return -1;
+	}
+	return 0;
+}
+
+static int
 Bgetf(Biobuf *b, float *f)
 {
 	u32int l;
@@ -96,7 +126,21 @@
 	return 0;
 }
 
+static int
+Bputf(Biobuf *b, float f)
+{
+	u32int l;
+
+	l = *(u32int*)&f;
+	if(Bputl(b, l) < 0){
+		werrstr("Bputl: %r");
+		return -1;
+	}
+	return 0;
+}
+
 static int bunpack(Biobuf*, char*, ...);
+static int bpack(Biobuf*, char*, ...);
 
 static int
 vbunpack(Biobuf *b, char *fmt, va_list a)
@@ -145,6 +189,52 @@
 }
 
 static int
+vbpack(Biobuf *b, char *fmt, va_list a)
+{
+	u16int s;
+	u32int l;
+	float f, *v;
+	void *p;
+
+	for(;;){
+		switch(*fmt++){
+		case '\0':
+			return 0;
+		case 's':
+			s = va_arg(a, ushort);
+			if(Bputs(b, s) < 0)
+				goto error;
+			break;
+		case 'l':
+			l = va_arg(a, ulong);
+			if(Bputl(b, l) < 0)
+				goto error;
+			break;
+		case 'f':
+			f = va_arg(a, double);
+			if(Bputf(b, f) < 0)
+				goto error;
+			break;
+		case 'v':
+			v = va_arg(a, float*);
+			if(bpack(b, "fff", v[0], v[1], v[2]) < 0)
+				goto error;
+			break;
+		case '[':
+			p = va_arg(a, void*);
+			s = va_arg(a, ushort);
+			if(Bwrite(b, p, s) != s){
+				werrstr("Bwrite: could not write %ud bytes", s);
+				goto error;
+			}
+			break;
+		}
+	}
+error:
+	return -1;
+}
+
+static int
 bunpack(Biobuf *b, char *fmt, ...)
 {
 	va_list a;
@@ -157,6 +247,19 @@
 	return n;
 }
 
+static int
+bpack(Biobuf *b, char *fmt, ...)
+{
+	va_list a;
+	int n;
+
+	va_start(a, fmt);
+	n = vbpack(b, fmt, a);
+	va_end(a);
+
+	return n;
+}
+
 static char *
 getline(Biobuf *b)
 {
@@ -434,6 +537,84 @@
 out:
 	Bterm(bin);
 	return stl;
+}
+
+static usize
+writetxt(Biobuf *b, Stl *stl)
+{
+	usize n;
+	int i, j;
+
+	n = Bprint(b, "solid");
+	if(strlen((char*)stl->hdr) > 0)
+		n += Bprint(b, " %s", (char*)stl->hdr);
+	n += Bprint(b, "\n");
+
+	for(i = 0; i < stl->ntris; i++){
+		n += Bprint(b, "facet normal %g %g %g\n",
+			stl->tris[i]->n[0], stl->tris[i]->n[1], stl->tris[i]->n[2]);
+		n += Bprint(b, "\touter loop\n");
+		for(j = 0; j < 3; j++)
+			n += Bprint(b, "\t\tvertex %g %g %g\n",
+				stl->tris[i]->v[j][0], stl->tris[i]->v[j][1], stl->tris[i]->v[j][2]);
+		n += Bprint(b, "\tendloop\n");
+		n += Bprint(b, "endfacet\n");
+	}
+
+	n += Bprint(b, "endsolid\n");
+	return n;
+}
+
+static usize
+writebin(Biobuf *b, Stl *stl)
+{
+	Stltri **tri;
+	usize n;
+
+	if(bpack(b, "[l", stl->hdr, sizeof(stl->hdr), stl->ntris) < 0){
+		werrstr("hdr pack: %r");
+		return 0;
+	}
+	n = sizeof(stl->hdr) + 4;
+
+	for(tri = stl->tris; tri < stl->tris+stl->ntris; tri++){
+		if(bpack(b, "vvvvs", (*tri)->n, (*tri)->v+0, (*tri)->v+1, (*tri)->v+2, (*tri)->attrlen) < 0){
+			werrstr("tri pack0: %r");
+			return 0;
+		}
+		n += 4*3*4+2;
+		if(bpack(b, "[", (*tri)->attrs, (*tri)->attrlen) < 0){
+			werrstr("tri pack1: %r");
+			return 0;
+		}
+		n += (*tri)->attrlen;
+	}
+	return n;
+}
+
+usize
+writestl(int fd, Stl *stl, int fmt)
+{
+	Biobuf *b;
+	usize n;
+
+	b = Bfdopen(fd, OWRITE);
+	if(b == nil)
+		sysfatal("Bfdopen: %r");
+
+	n = 0;
+	switch(fmt){
+	case STLTEXT:
+		n = writetxt(b, stl);
+		break;
+	case STLBINARY:
+		n = writebin(b, stl);
+		break;
+	default:
+		werrstr("unknown format '%d'", fmt);
+	}
+	Bterm(b);
+	return n;
 }
 
 void
--- a/stl.h
+++ b/stl.h
@@ -10,6 +10,11 @@
  * 	end
 */
 
+enum {
+	STLTEXT,
+	STLBINARY,
+};
+
 typedef struct Stltri Stltri;
 typedef struct Stl Stl;
 
@@ -29,4 +34,5 @@
 };
 
 Stl *readstl(int);
+usize writestl(int, Stl*, int);
 void freestl(Stl*);
--- a/test/main.c
+++ b/test/main.c
@@ -7,7 +7,7 @@
 void
 usage(void)
 {
-	fprint(2, "usage: %s [file]\n", argv0);
+	fprint(2, "usage: %s [-o [tb]] [file]\n", argv0);
 	exits("usage");
 }
 
@@ -14,7 +14,7 @@
 static void
 printfv(float *f)
 {
-	print("%f\t%f\t%f", f[0], f[1], f[2]);
+	fprint(2, "%f\t%f\t%f", f[0], f[1], f[2]);
 }
 
 static void
@@ -22,20 +22,20 @@
 {
 	int i;
 
-	print("header: %.*s\n", sizeof(stl->hdr), (char*)stl->hdr);
-	print("ntris: %ud\n", stl->ntris);
-	print("tris:\n");
+	fprint(2, "header: %.*s\n", sizeof(stl->hdr), (char*)stl->hdr);
+	fprint(2, "ntris: %ud\n", stl->ntris);
+	fprint(2, "tris:\n");
 	for(i = 0; i < stl->ntris; i++){
-		print("\t%08d\n\t\tn\t", i);
+		fprint(2, "\t%08d\n\t\tn\t", i);
 		printfv(stl->tris[i]->n);
-		print("\n\t\tp0\t");
+		fprint(2, "\n\t\tp0\t");
 		printfv(stl->tris[i]->v[0]);
-		print("\n\t\tp1\t");
+		fprint(2, "\n\t\tp1\t");
 		printfv(stl->tris[i]->v[1]);
-		print("\n\t\tp2\t");
+		fprint(2, "\n\t\tp2\t");
 		printfv(stl->tris[i]->v[2]);
-		print("\n\t\tattrlen\t%d", stl->tris[i]->attrlen);
-		print("\n\t\tattrs: %.*s\n", stl->tris[i]->attrlen, (char*)stl->tris[i]->attrs);
+		fprint(2, "\n\t\tattrlen\t%d", stl->tris[i]->attrlen);
+		fprint(2, "\n\t\tattrs: %.*s\n", stl->tris[i]->attrlen, (char*)stl->tris[i]->attrs);
 	}
 }
 
@@ -43,11 +43,20 @@
 main(int argc, char *argv[])
 {
 	Stl *stl;
-	char *f;
-	int fd;
+	char *f, *fmt;
+	int fd, ofmt;
 
 	f = fd0;
+	ofmt = -1;
 	ARGBEGIN{
+	case 'o':
+		fmt = EARGF(usage());
+		switch(*fmt){
+		case 't': ofmt = STLTEXT; break;
+		case 'b': ofmt = STLBINARY; break;
+		default: sysfatal("unknown format '%d'", *fmt);
+		}
+		break;
 	default: usage();
 	}ARGEND;
 	if(argc > 1)
@@ -64,6 +73,8 @@
 	close(fd);
 
 	printstl(stl);
+	if(ofmt >= 0)
+		writestl(1, stl, ofmt);
 	freestl(stl);
 	exits(nil);
 }
binary files /dev/null b/test/teapot.stl differ
--