This is polymerge.c in view mode; [Download] [Up]
/* * Squeeze OFF files. * Merges collinear edges and coplanar faces. */ #include <stdio.h> #include <string.h> #include <math.h> #include <stdlib.h> /* for qsort(), malloc() */ extern char *strdup(const char *); #define VSUB(a, b, dst) (dst)->x = (a)->x - (b)->x, \ (dst)->y = (a)->y - (b)->y, \ (dst)->z = (a)->z - (b)->z #define VDOT(a, b) (a)->x*(b)->x + (a)->y*(b)->y + (a)->z*(b)->z #define VCROSS(a, b, dst) (dst)->x = (a)->y * (b)->z - (a)->z * (b)->y, \ (dst)->y = (a)->z * (b)->x - (a)->x * (b)->z, \ (dst)->z = (a)->x * (b)->y - (a)->y * (b)->x float EPS_LINEAR= 0.0001; /* Max unit-vector cross product for collinearity */ float EPS_NORMAL= 0.0009; /* Max sin^2 theta between normals for coplanarity */ float EPS_POINT= 0.00001;/* Max distance between coincident vertices */ #define F_VERT 0x1 #define F_EDGE 0x2 #define F_FACE 0x4 #define F_EVOLVER 0x8 typedef struct vertex V; typedef struct face F; typedef struct faceedge Fe; typedef struct edge E; typedef struct point { float x, y, z, w; } P; struct vertex { P p; int ref; int index; float *more; }; struct faceedge { F *face; Fe *prev, *next; /* double links in face-edges of this face */ E *edge; int sign; /* direction w.r.t. edge */ P n; /* adopted surface normal at this edge */ Fe *elink; }; struct face { Fe *fedges; char *affix; }; struct edge { V *v[2]; /* vertex pointers */ E *link; /* hash table link */ P to; /* normalized edge direction vector */ Fe *feds; /* faceedge's on this edge */ int index; /* for numbering edges for .fe format */ }; int nv, nf, tossedf, nhash; V *Vs; /* All vertices */ F *Fs; /* All faces */ E **ehash; /* Array of hash-table head pointers */ int nhash; int flags = F_VERT|F_EDGE|F_FACE; int debug = 0; int vdim = 3; Fe *fedge( F *f, V *v0, V *v1 ); #define New(t) (t *) malloc(sizeof(t)) #define NewN(t, N) (t *) malloc((N)*sizeof(t)) main(argc, argv) char *argv[]; { register int i; int tnv, tne, any; int binary = 0, vertsize = 0; char *C = "", *N = "", *four = ""; extern int optind; extern char *optarg; while((i = getopt(argc, argv, "bdv:e:f:VEF")) != EOF) { switch(i) { case 'b': flags |= F_EVOLVER; break; case 'd': debug = 1; break; case 'v': EPS_POINT = atof(optarg); break; case 'e': EPS_LINEAR = atof(optarg); EPS_LINEAR *= EPS_LINEAR; break; case 'f': EPS_NORMAL = atof(optarg); EPS_NORMAL *= EPS_NORMAL; break; case 'V': flags &= ~F_VERT; break; case 'E': flags &= ~F_EDGE; break; case 'F': flags &= ~F_FACE; break; default: fprintf(stderr, "\ Usage: polymerge [-v vertex_thresh] [-e edge_thresh] [-f face_thresh]\n\ [-V][-E][-F][-d][-b] [infile.off]\n\ Merges coincident vertices, collinear edges, coplanar faces of an OFF object.\n\ -v vertex_thresh: max separation for \"coincident\" vertices (default %g)\n\ -e edge_thresh : max sin(theta) for \"collinear\" edges (default %g)\n\ -f face_thresh : max sin(theta) for \"coplanar\" facet normals (default %.4g)\n\ -V -E -F : don't try to merge vertices/edges/faces\n\ -b : create output in evolver .fe format\n\ -d : debug\n", EPS_POINT, sqrt(EPS_LINEAR), sqrt(EPS_NORMAL)); exit(1); } } if(optind < argc) { if(freopen(argv[argc-1], "r", stdin) == NULL) { fprintf(stderr, "polymerge: can't open input: "); perror(argv[argc-1]); exit(1); } } vdim = 3; for(;;) { switch( fnextc(stdin, 0) ) { case 'N': N = "N"; vertsize += 3; goto swallow; case 'C': C = "C"; vertsize += 4; goto swallow; case '4': four = "4"; vdim = 4; flags &= ~(F_EDGE|F_FACE); goto swallow; swallow: case '{': case '=': fgetc(stdin); break; case 'a': /* Appearance? Silently swallow keyword */ fexpectstr(stdin, "appearance"); if(fnextc(stdin, 0) == '{') { int c, brack = 0; do { if((c = fgetc(stdin)) == '{') brack++; else if(c == '}') brack--; } while(brack > 0 && c != EOF); } break; default: goto done; } } done: fexpecttoken(stdin, "OFF"); /* swallow optional OFF prefix */ if(fexpecttoken(stdin, "BINARY") == 0) { binary = 1; fnextc(stdin, 1); fgetc(stdin); } if(fgetni(stdin, 1, &nv, 0) <= 0 || fgetni(stdin, 1, &nf, 0) <= 0 || fgetni(stdin, 1, &nhash, 0) <= 0 || nv <= 0 || nf <= 0) { fprintf(stderr, "polymerge: Input not an OFF file.\n"); exit(1); } nhash = (nf + nv + 4) / 2; if(nhash < 16) nhash = 17; if(!(nhash & 1)) nhash++; ehash = NewN(E *, nhash); bzero((char *)ehash, nhash*sizeof(E *)); Vs = NewN(V, nv); Fs = NewN(F, nf); for(i = 0; i < nv; i++) { V *vp = &Vs[i]; vp->p.w = 1; if(fgetnf(stdin, vdim, &vp->p, binary) < vdim) { badvert: fprintf(stderr, "polymerge: error reading vertex %d/%d\n", i,nv); exit(1); } if(vertsize) { Vs[i].more = NewN(float, vertsize); if(fgetnf(stdin, vertsize, Vs[i].more, binary) < vertsize) goto badvert; } Vs[i].ref = 0; Vs[i].index = i; } /* * Combine vertices */ if(flags & F_VERT) vmerge(); /* * Load faces * The fedge() calls here assume we've already merged all appropriate * vertices. */ tossedf = 0; for(i = 0; i < nf; i++) { register F *fp = &Fs[i]; register Fe *fe; int nfv, k, c; int v, ov, v0; Fe head; char *cp; char aff[512]; if(fgetni(stdin, 1, &nfv, binary) <= 0 || nfv <= 0) { fprintf(stderr, "polymerge: error reading face %d/%d\n", i, nf); exit(1); } head.prev = head.next = &head; fp->fedges = &head; fgetni(stdin, 1, &v, binary); if(v < 0 || v >= nv) { fprintf(stderr, "polymerge: bad vertex %d on face %d\n", v, i); exit(1); } v0 = ov = Vs[v].index; /* Use common vertex if merged */ for(k = 1; k < nfv; k++, ov = v) { fgetni(stdin, 1, &v, binary); if(v < 0 || v >= nv) { fprintf(stderr, "polymerge: bad vertex %d on face %d\n", v, i); exit(1); } v = Vs[v].index; /* Use common vertex if merged */ if(ov == v) continue; head.prev->next = fe = fedge(fp, &Vs[ov], &Vs[v]); fe->prev = head.prev; fe->next = &head; head.prev = fe; } if(v != v0) { fe = fedge(fp, &Vs[v], &Vs[v0]); head.prev->next = fe; fe->prev = head.prev; fe->next = &head; head.prev = fe; } head.next->prev = head.prev; if(head.next == &head) { /* * Degenerate face here */ fp->fedges = NULL; tossedf++; debug && printf("# Face %d degenerate already\n", i); } else { head.prev->next = fp->fedges = head.next; } if(binary) { int nfc; float c; fgetni(stdin, 1, &nfc, binary); if(nfc > 4) { fprintf(stderr, "Bad face color count 0x%x", nfc); exit(1); } for(cp = aff; --nfc >= 0; cp += strlen(cp)) { fgetnf(stdin, 1, &c, binary); sprintf(cp, " %g", c); } } else { (void) fnextc(stdin, 1); for(cp = aff; (c = getc(stdin)) != EOF && c != '\n' && cp < &aff[511]; ) *cp++ = c; } *cp = '\0'; fp->affix = (cp > aff) ? strdup(aff) : ""; } /* * Compute face normals -- actually corner normals, since we avoid * assuming faces are planar. As a side effect, we detect & join * collinear edges. */ if(flags & F_EDGE) { for(i = 0; i < nf; i++) { normalize(&Fs[i]); } } /* * Locate edges bounding faces with the same normal direction. */ if(flags & F_FACE) { do { any = 0; for(i = 0; i < nhash; i++) { E *e; for(e = ehash[i]; e != NULL; e = e->link) { register Fe *fe, *fee; for(fe = e->feds; fe != NULL; fe = fe->elink) { for(fee = fe->elink; fee != NULL; fee = fee->elink) { register float dn; dn = VDOT(&fe->n, &fee->n); if(fabs(1 - dn*dn) < EPS_NORMAL) { /* * OK, merge fee into fe */ femerge(fe, fee); any++; goto another; } } } another: ; } } debug && printf("# %d faces merged this pass.\n", any); } while(any); } /* * Scan for unused edges. */ for(i = 0; i < nv; i++) Vs[i].ref = 0; tne = 0; for(i = 0; i < nhash; i++) { E *e; for(e = ehash[i]; e != NULL; e = e->link) { if(e->feds != NULL) { e->v[0]->ref++; e->v[1]->ref++; e->index = ++tne; } } } /* * Renumber used vertices. */ if(flags & 1) { tnv = 0; for(i = 0; i < nv; i++) Vs[i].index = Vs[i].ref ? tnv++ : -i-1; } else { tnv = nv; } if (flags & F_EVOLVER) /* Produce Brakke's evolver .fe format */ { int j=0; if (vdim == 4) printf("space_dimension 4\n"); printf("vertices\n"); for(i = 0; i < nv; i++) { register V *v = &Vs[i]; int k; if(v->ref || !(flags & 1)) { v->index = ++j; printf("%d\t%g %g %g", v->index, v->p.x, v->p.y, v->p.z); if(vdim == 4) printf(" %g", v->p.w); if(vertsize) { printf(" "); for(k = 0; k < vertsize; k++) printf(" %g", v->more[k]); } printf("\n"); } } printf("\nedges\n"); for(i = 0; i < nhash; i++) { E *e; for(e = ehash[i]; e != NULL; e = e->link) if(e->feds != NULL) printf("%d\t%d %d\n", e->index,e->v[0]->index,e->v[1]->index); } printf("\nfaces\n"); j=0; for(i=0; i<nf; i++) { register Fe *fe, *fee; int k, nfv; fe = Fs[i].fedges; if(fe == NULL) continue; for(fee = fe, k = 1; (fee = fee->next) != fe; k++) ; if ((nfv = k)<3) continue; /* don't print faces of less than 3 sides */ printf("%d", ++j); for(fee = fe, k = nfv; --k >= 0; fee = fee->next) printf(" %d", (1-2*fee->sign)*fee->edge->index); printf("\n"); } } else /* Produce OFF format */ { printf("%s%s%sOFF\n%d %d %d\n", C, N, four, tnv, nf - tossedf, tne); for(i = 0; i < nv; i++) { register V *v = &Vs[i]; int k; if(v->ref || !(flags & F_VERT)) { printf("%g %g %g", v->p.x, v->p.y, v->p.z); if(vdim == 4) printf(" %g", v->p.w); if(vertsize) { printf(" "); for(k = 0; k < vertsize; k++) printf(" %g", v->more[k]); } if(debug) printf("\t# %d [%d] #%d", v->index, v->ref, i); printf("\n"); } } printf("\n"); /* ho hum */ for(i=0; i<nf; i++) { register Fe *fe, *fee; int k, nfv; fe = Fs[i].fedges; if(fe == NULL) continue; for(fee = fe, k = 1; (fee = fee->next) != fe; k++) ; nfv = k; printf("%d", nfv); for(fee = fe, k = nfv; --k >= 0; fee = fee->next) printf(" %d", fee->edge->v[fee->sign]->index); printf("\t%s", Fs[i].affix); if(debug) { printf(" #"); for(fee = fe, k = nfv; --k >= 0; fee = fee->next) printf(" %d", fee->edge->v[fee->sign] - Vs); } printf("\n"); } } exit(1); } /* * Add a new faceedge */ Fe * fedge(F *f, V *v0, V *v1) { Fe *fe; E *e; int t; int i0, i1; float r; fe = New(Fe); fe->face = f; fe->sign = 0; i0 = v0->index; i1 = v1->index; if(i0 > i1) { V *tv = v0; v0 = v1; v1 = tv; i0 = v0->index; i1 = v1->index; fe->sign = 1; } t = (unsigned long)(v0->index + v1->index + v0->index*v1->index) % nhash; /* Symmetric hash function */ for(e = ehash[t]; e != NULL; e = e->link) if(e->v[0]->index == i0 && e->v[1]->index == i1) goto gotit; e = New(E); e->v[0] = v0; e->v[1] = v1; e->feds = NULL; VSUB(&v0->p, &v1->p, &e->to); r = VDOT(&e->to, &e->to); if(r != 0) { r = 1/sqrt(r); e->to.x *= r; e->to.y *= r; e->to.z *= r; } else debug && printf("# Coincident: %d == %d [%g %g %g]\n", v0->index, v1->index, v0->p.x,v0->p.y,v0->p.z); e->link = ehash[t]; ehash[t] = e; gotit: fe->edge = e; fe->elink = e->feds; e->feds = fe; return fe; } /* * Remove a faceedge from its edge list */ unfedge(fe) register Fe *fe; { register Fe **fepp; for(fepp = &fe->edge->feds; *fepp != NULL; fepp = &(*fepp)->elink) { if(*fepp == fe) { *fepp = fe->elink; break; } } free(fe); } /* * Merge two faces * We delete these face-edges from both faces */ femerge(fe1, fe2) register Fe *fe1, *fe2; { F *f1, *f2; register Fe *tfe; if(fe1->face == fe2->face) { debug && printf("# Merging two edges of face %d -- tossing it.\n", fe1->face - Fs); deface(fe1->face); return; } if(fe1->sign == fe2->sign) { /* * Messy. To merge these, we need to reverse all the links * in one of the two faces. */ Fe *xfe; tfe = fe2; do { tfe->sign ^= 1; xfe = tfe->next; tfe->next = tfe->prev; tfe->prev = xfe; tfe = xfe; } while(tfe != fe2); } fe1->prev->next = fe2->next; fe2->next->prev = fe1->prev; fe1->next->prev = fe2->prev; fe2->prev->next = fe1->next; f1 = fe1->face; f2 = fe2->face; if(f1->fedges == fe1) f1->fedges = fe2->next; if(debug) printf("# Merged face %d into %d (vertices %d %d) n1.n2 %g\n", f2-Fs, f1-Fs, fe1->edge->v[fe1->sign]->index, fe1->edge->v[1 - fe1->sign]->index, VDOT(&fe1->n, &fe2->n)); tfe = fe2->next; if(tfe == NULL) { fprintf(stderr, "polymerge: face f2 already deleted?\n"); } else { do { tfe->face = f1; } while((tfe = tfe->next) != fe1->next); } f2->fedges = NULL; tossedf++; unfedge(fe1); unfedge(fe2); /* * Join collinear edges, recompute normals (might have changed a bit). */ normalize(f1); } #define PRETTY(x) ((int)(x) - 0x10000000) fecheck(fe) Fe *fe; { register Fe *fee; int ne; int onface = 0; register F *f; register E *e; if(fe == NULL) return; f = fe->face; fprintf(stderr,"0x%x: on face %d (%x); ", fe, f - Fs, f); fee = fe; do { fprintf(stderr," %s%x[%d%s%d] ", (f->fedges == fee) ? "*" : "", PRETTY(fee), fee->edge->v[0]->index, fee->sign ? "<-" : "->", fee->edge->v[1]->index); if(fee->face != f) fprintf(stderr," Fe %x: face %d (%x) != %x\n", PRETTY(fee), fee->face - Fs, fee->face, f); if(fee->next->prev != fee) fprintf(stderr," Fe %x: next %x next->prev %x\n", PRETTY(fee), PRETTY(fee->next), PRETTY(fee->next->prev)); e = fee->edge; fee = fee->next; } while(fee != fe); fprintf(stderr, "\n"); } E * echeck(v0, v1) register int v0, v1; { register E *e; int t = (v0 + v1 + v0*v1) % nhash; /* Symmetric hash function */ if(v0 > v1) v0 ^= v1, v1 ^= v0, v0 ^= v1; for(e = ehash[t]; e != NULL; e = e->link) { if(e->v[0] == &Vs[v0] && e->v[1] == &Vs[v1]) { register Fe *fe; fprintf(stderr, "E 0x%x %d-%d (%d-%d) %x...\n", e, v0,v1, e->v[0]->index, e->v[1]->index, PRETTY(e->feds)); for(fe = e->feds; fe != NULL; fe = fe->elink) { fecheck(fe); } } } return e; } normalize(f) F *f; { register Fe *fe; fe = f->fedges; if(fe == NULL) return; if(fe->prev == fe->next) { debug && printf("# Face %d already degenerate -- tossing it.\n", f - Fs); deface(f); return; } do { /* loop over edges on this face */ P n; float r; for(;;) { register Fe *fn, *fee; register P *pp, *qp; pp = &fe->edge->to; fee = fe->next; qp = &fee->edge->to; VCROSS(pp, qp, &n); r = VDOT(&n, &n); if(r > EPS_LINEAR) break; /* * Join collinear edges; produce a new edge */ fn = fedge(f, fe->edge->v[fe->sign], fee->edge->v[1-fee->sign]); fee->next->prev = fe->prev->next = fn; fn->next = fee->next; fn->prev = fe->prev; if(fe == f->fedges || fee == f->fedges) f->fedges = fn; /* preserve headiness */ unfedge(fee); if(fee != fe) unfedge(fe); if(fn->prev == fn->next) { /* * This face became degenerate -- toss it. */ debug && printf("# degenerate face %d\n", f - Fs); deface(f); return; } fe = fn; } r = 1/sqrt(r); if(n.x < 0 || (n.x == 0 && n.y < 0 || (n.y == 0 && n.z < 0))) r = -r; /* Canonicalize */ fe->n.x = n.x*r; fe->n.y = n.y*r; fe->n.z = n.z*r; } while((fe = fe->next) != f->fedges); } /* * Delete a face, erasing all edges. */ deface(f) F *f; { register Fe *fe, *fee; fe = f->fedges; if(fe == NULL) return; do { fee = fe->next; unfedge(fe); fe = fee; } while(fe != f->fedges); f->fedges = NULL; tossedf++; } vcmp(p, q) V **p, **q; { register V *vp, *vq; register float d; vp = *p; vq = *q; d = vp->p.x - vq->p.x; if(d < 0) return -1; if(d > 0) return 1; d = vp->p.y - vq->p.y; if(d < 0) return -1; if(d > 0) return 1; d = vp->p.z - vq->p.z; if(d < 0) return -1; if(d > 0) return 1; if(vp->p.w == vq->p.w) return 0; return(vp->p.w < vq->p.w ? -1 : 1); } vmerge() { V **vp; int i, j; register V *a, *b; int nexti; vp = NewN(V *, nv); for(i = 0, a = Vs; i < nv; i++) { a->ref = 0; vp[i] = a++; } qsort(vp, nv, sizeof(V *), vcmp); /* * Now all matches will occur within X-runs */ for(i = 0; i < nv; i = nexti ? nexti : i+1) { nexti = 0; a = vp[i]; if(a->ref) continue; for(j = i; ++j < nv; ) { b = vp[j]; if(b->ref) continue; if(b->p.x - a->p.x > EPS_POINT) break; if(fabs(a->p.y - b->p.y) < EPS_POINT && fabs(a->p.z - b->p.z) < EPS_POINT && (vdim == 3 || fabs(a->p.w - b->p.w) < EPS_POINT)) { debug && printf("# Vtx %d->%d\n", b->index, a->index); b->index = a->index; b->ref++; } else if(!nexti) nexti = j; } } free(vp); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.