X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=moebius2.git;a=blobdiff_plain;f=output.c;fp=output.c;h=afdc3df5d02bfef56bfdd87d2a730459cc0daa00;hp=0000000000000000000000000000000000000000;hb=6bcc34b96b6674e9ece1c339ada0557f7d2fd1b5;hpb=c620934d56d69abb34aeb475b09471f811d39898 diff --git a/output.c b/output.c new file mode 100644 index 0000000..afdc3df --- /dev/null +++ b/output.c @@ -0,0 +1,436 @@ +/* + * Generates an STL file + * usage: ./output+ + * thickness is in original moebius unit circle units + * unit circle diameter is in whatever dimensions the STL file uses + * + * We have to: + * - add some thickness + * - define a shape for the rim so that it is solid + * - scale the coordinates + * - translate the coordinates so they're all positive + */ +/* + * Re STL, see: + * http://www.ennex.com/~fabbers/StL.asp + * http://en.wikipedia.org/wiki/STL_%28file_format%29 + * saved here as stl-format-wikipedia.txt + */ + +/* + * We add thickness by adding vertices as follows: + * + * :axis of symmetry + * sides | : + * swap| : + * | : + * *F | : *F *F *F *F + * | : + * *E *G | :*E *G *E *G *E *G *E + * | : + * *D ____________|*D ____________ *D ____________ *D ____________ *D __ + * /\ 3 |/\ 0 /\ /\ /\ + * / \ / :\ / \ / \ / \ + * \ *A 4/ \: \5 *B / \ *B / \ *B / \ + * \ / \ \ 1 / \ / \ / + * \ / :\ \ / \ / \ / + * *B \ / *B2| \2 1/ 0*A \ / *A \ / *A + * \ / : | \ / . ' \ / \ / + * _______ *C _____ :_|___ *C' __________ *C ___________ *C _________ + * /\ : | 3 /\. 0 /\ /\ + * / \ :3| / \ ` . / \ / \ + * *A / \ *A | /4 5\ 5*B / \ *B / \ *B + * / \ 1: / / \ / \ / \ + * / \ :/ / 4 \ / \ / \ + * / *B2 2\ / /1 *A \ / *A \ / *A \ / + * \ / \/:/ 0 \ / \ / \ / + * *C ____________|*C ____________ *C ___________ *C ___________ *C __ + * /\ 3 | : 0 /\ /\ /\ + * / \ / :\ / \ / \ / \ + * \ *A3 4/ \: \5 5*B / \ *B / \ *B / \ + * \ / \ \ / \ / \ / + * \ / 4:\ \ / \ / \ / + * *B \ / *B | \ / *A \ / *A \ / *A + * \ / : | \ / . ' \ / \ / + * _______ *C _____ :_|___ *C' __________ *C ___________ *C _________ + * /\ : | 3 /\. 0 /\ /\ + * / \ : | / \ ` . / \ / \ + * : + * . . : . . . . . . + * : + + * \ / : | \ / . ' \ / \ / + * _______ *C _____ :_|___ *C' __________ *C ___________ *C _________ + * /\ : | 3 /\. 0 /\ /\ + * / \ : | / \ ` . / \ / \ + * *A / \ *A | /4 5\ *B / \ *B / \ *B + * / \ 1: / / \ / \ / \ + * / \ :/ / \ / \ / \ + * / *B2 2\ / /1 0*A \ / *A \ / *A \ / + * \ / \/:/ \ / \ / \ / + * *D ____________|*D ____________ *D ___________ *D ___________ *D __ + * 3 | : 0 + * *E *G | :*E *G *E *G *E *G *E + * | : + * *F | : *F *F *F *F + * | : + * + * Each A,B,C,D represents two vertices - one on each side of the + * surface. Each E,F,G represents a several vertices in an arc around + * the rim. Digits are `e' values for edges or relative AB + * outvertices. + * + * The constructions we use are: + * + * A, B: Extend the normal vector of the containing intriangle + * from its centroid for thickness. + * + * C: Take mean position (centroid) of all surrounding + * computed A and B, and extend from base point in + * direction of that centroid for thickness. + * + * D, E, F: + * Compute notional rim vector R as mean of the two + * adjoining rim edges, and consider plane P as + * that passing through the base point normal to R. + * Project the centroid of the two adjoining non-rim + * vertices onto P. Now D(EF)+ED is the semicircle + * in P centred on the base point with radius thickness + * and which is opposite that centroid. + * + * G: Each F is the mean of those two adjacent Es with the + * same angle in their respective Ps. + * + * The outtriangles are: + * + * For each non-rim vertex on each side, the six triangles formed by + * its C and the surrounding A's and B's. + * + * For each rim vertex on each side, the two triangles formed by its + * D and the nearest As and Bs (two As and one B or vice versa). + * + * For each rim edge on each side, the triangle formed by that edge's + * ends' Ds and the corresponding A or B. + * + * For each G, the six triangles formed by that G and the adjacent + * four Fs (or two Fs and two Ds) and two Es. + */ + +#include "mgraph.h" +#include "common.h" + +/*---------- declarations and useful subroutines ----------*/ + +#define FOR_SIDE for (side=0; side<2; side++) + +typedef struct { + double p[D3]; +} OutVertex; + +#define NG 10 +#define NDEF (NG*2+1) + +static OutVertex ovAB[N][2][2]; /* indices: vertex to W, A=0 B=1, side */ +static OutVertex ovC[N][2]; +static OutVertex ovDEF[X][2][NDEF]; /* x, !!y, angle */ +static OutVertex ovG[X][2][NG]; /* x, !!y, angle; for G to the East of x */ + +static Vertices in; + +static double thick; /* in input units */ +static double scale; + +static void normalise_thick(double a[D3]) { + /* multiplies a by a scalar so that its magnitude is thick */ + int k; + double multby= thick / magnD(a); + K a[k] *= multby; +} + +static void triangle_normal(double normal[D3], const double a[D3], + const double b[D3], const double c[D3]) { + double ab[D3], ac[D3]; + int k; + + K ab[k]= b[k] - a[k]; + K ac[k]= c[k] - a[k]; + xprod(normal,ab,ac); +} + +static OutVertex *invertex2outvertexab(int v0, int e, int side) { + int vref, ab; + switch (e) { + case 0: vref= v0 ; ab=0; break; + case 1: vref=EDGE_END2(v0,2); ab=1; break; + case 2: vref=EDGE_END2(v0,3); ab=0; break; + case 3: vref=EDGE_END2(v0,3); ab=1; break; + case 4: vref=EDGE_END2(v0,4); ab=0; break; + case 5: vref= v0 ; ab=1; break; + default: abort(); + } + if (vref<0) return 0; + int sw= VERTICES_SPAN_JOIN_P(v0,vref); + return &ovAB[vref][ab^sw][side^sw]; +} + +/*---------- output vertices ----------*/ + +static void compute_outvertices(void) { + int v0,k,side,ab,x,y; + + FOR_VERTEX(v0) { + for (ab=0; ab<2; ab++) { + int v1= EDGE_END2(v0, ab?5:0); + int v2= EDGE_END2(v0, ab?0:1); + if (v1<0 || v2<0) { + K ovAB[v0][ab][0].p[k]= ovAB[v0][ab][1].p[k]= NAN; + continue; + } + double normal[D3], centroid[D3]; + triangle_normal(normal, in[v0],in[v1],in[v2]); + normalise_thick(normal); + K centroid[k]= (in[v0][k] + in[v1][k] + in[v2][k]) / 3.0; + K ovAB[v0][ab][0].p[k]= centroid[k] + normal[k]; + K ovAB[v0][ab][1].p[k]= centroid[k] - normal[k]; + } + } + FOR_VERTEX(v0) { + double centroid[D3]; + int vw= EDGE_END2(v0,3); + int vnw= EDGE_END2(v0,2); + int vsw= EDGE_END2(v0,4); + if (vnw<0 || vsw<0 || vw<0) { + K ovC[v0][0].p[k]= ovC[v0][1].p[k]= NAN; + continue; + } + FOR_SIDE { + K adjust[k]= 0; + FOR_VPEDGE(e) { + OutVertex *ovab= invertex2outvertexab(v0,e,side); + K adjust[k] += ovab->k[k]; + } + K adjust[k] /= 6; + K adjust[k] -= in[v0][k]; + normalise_thick(adjust); + K ovC[v0][side].p[k]= in[v0][k] + adjust[k]; + } + } + FOR_RIM_VERTEX(y,x,v0) { + double rim[D3], inner[D3], radius_cos[D3];, radius_sin[D3]; + int vback, vfwd, around; + + /* compute mean rim vector, which is just the vector between + * the two adjacent rim vertex (ignoring the base vertex) */ + vback= EDGE_END2(v0,3); + vfwd= EDGE_END2(v0,0); + assert(vback>=0 && vfwd>=0); + K rim[k]= in[fwd][k] - in[vback][k]; + + /* compute the inner centroid */ + vback= EDGE_END2(v0,4); + if (vback>=0) { /* North rim */ + vfwd= EDGE_END2(v0,5); + } else { /* South rim */ + vback= EDGE_END2(v0,2); + vfwd= EDGE_END2(v0,1); + } + assert(vback>=0 && vfwd>=0); + K inner[k]= (in[vback][k] + in[vfwd][k]) / 2; + K inner[k] -= in[v0][k]; + + /* we compute the radius cos and sin vectors by cross producting + * the vector to the inner with the rim, and then again, and + * normalising. */ + xprod(radius_cos,rim,inner); + xprod(radius_sin,rim,radius_cos); + normalise_thick(radius_cos); + normalise_thick(radius_sin); + + for (around=0; around= 0); + for (around=0; around=0); + gs= ovG [v0 & XMASK][rimy]; + defs= ovDEF[v0 & XMASK][rimy]; + defs1= ovDEF[v1 & XMASK][rimy]; + defs1aroundmap= VERTICES_SPAN_JOIN_P(v0,v1) + ? defs_aroundmap_swap : int_identity_function; + + for (aroung=0; aroung=0; i--) WR(value.b[i]); +#elif defined(LITTLE_ENDIAN) + ieee754single f= d; + WR(f); +#else +# error not little or big endian! +#endif +} + +static uint32_t nouttriangles; +static uint32_t nouttriangles_counted; + +static void outtriangle(int rev, OutVertex *a, OutVertex *b, OutVertex *c) { + if (rev) { outtriangle(0, c,b,a); return; } + nouttriangles++; + if (!~nouttriangles_counted) return; + + triangle_normal(normal, a.p, b.p, c.p); + double multby= 1/magnD(normal); + K normal[k] *= multby; + + K wf(normal[k]); + K wf(a.p[k]); + K wf(b.p[k]); + K wf(c.p[k]); + uint16_t attrbc; + WR(attrbc); +} + +static void write_file(void) { + static const char header[80]= "#!/usr/bin/meshlab\n" "binary STL file\n"; + + if (isatty(stdout)) die("will not write binary stl to tty!"); + + WR(header); + + nouttriangles_counted=~(uint32_t)0; + nouttriangles=0; + outtriangles(); + WR(nouttriangles); + + nouttriangles_counted= nouttriangles; + nouttriangles=0; + outtriangles(); + assert(nouttriangles == nouttriangles_counted); + + if (fflush(stdout)) diee("fflush stdout"); +} + + +/*---------- main program etc. ----------*/ + +int main(int argc, const char *const *argv) { + int r; + + if (argc!=3 || argv[1][0]=='-') { fputs("bad usage\n",stderr); exit(8); } + thick= atof(argv[1]); + scale= atof(argv[2]) * 0.5; /* circle is unit radius but arg is diameter */ + + errno= 0; r= fread(&in,sizeof(in),1,stdin); + if (r!=1) diee("fread"); + + compute_outvertices(); + scaleshift_outvertices(); + write_file(); + if (fclose(stdout)) diee("fclose stdout"); + return 0; +}