/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ #include #include #include #include static Error Check(Object o); static Error GroupCheck(Group g); static Error FieldCheck(Field f); static Error PositionsCheck(Array a); static char *ConnectionsCheck(Array a); static Error AreTetrasOK(Array pos, Array conn); static Error AreTrisOK(Array pos, Array conn); static Error AreLinesOK(Array pos, Array conn); static int TetraVol(Point *p, Point *q, Point *r, Point *s); static int TriArea(Point *p, Point *q, Point *r); extern Error _dxfValidate(Field f); Error _dxfIsCircular(Object o); #define MAXRANK 32 /* look for malformed objects and print messages about them. */ int m_Verify(Object *in, Object *out) { out[0] = NULL; if (!in[0]) { DXSetError(ERROR_BAD_PARAMETER, "#10000", "input"); return ERROR; } if (_dxfIsCircular(in[0]) == ERROR) return ERROR; if (Check(in[0]) == ERROR) return ERROR; out[0] = in[0]; return OK; } static Error Check(Object o) { Object subo, newo, subo2; Matrix m; Array a; int fixed, z; float pos; char *name; int i; switch (DXGetObjectClass(o)) { case CLASS_GROUP: /* for each member */ for (i=0; subo = DXGetEnumeratedMember((Group)o, i, NULL); i++) if (!Check(subo)) return ERROR; if (!GroupCheck((Group)o)) return ERROR; break; case CLASS_FIELD: if (!_dxfValidate((Field)o)) return ERROR; if (!FieldCheck((Field)o)) return ERROR; break; case CLASS_ARRAY: break; case CLASS_SCREEN: if (!DXGetScreenInfo((Screen)o, &subo, &fixed, &z)) return NULL; if (!Check(subo)) return ERROR; break; case CLASS_XFORM: if (!DXGetXformInfo((Xform)o, &subo, &m)) return NULL; if (!Check(subo)) return ERROR; break; case CLASS_CLIPPED: if (!DXGetClippedInfo((Clipped)o, &subo, &subo2)) return NULL; if (!Check(subo)) return ERROR; if (!Check(subo2)) return ERROR; break; default: break; } return OK; } struct mustmatch { Field f; int member; Array p; int posshape; Array c; char *contype; int isreg; int meshoff[MAXRANK]; }; static Error stuffstruct(struct mustmatch *m, Object o, int memnumber) { int r, s[MAXRANK]; /* is this too draconian? */ if (DXGetObjectClass(o) != CLASS_FIELD) { DXSetError(ERROR_INVALID_DATA, "composite field members must be fields"); return ERROR; } m->f = (Field)o; m->member = memnumber; m->p = (Array)DXGetComponentValue(m->f, "positions"); if (m->p) { if (!DXGetArrayInfo(m->p, NULL, NULL, NULL, &r, s)) return ERROR; m->posshape = s[0]; } else m->posshape = 0; m->c = (Array)DXGetComponentValue(m->f, "connections"); if (m->c) { if (!DXGetStringAttribute((Object)m->c, "element type", &m->contype)) { DXSetError(ERROR_INVALID_DATA, "'connections' component is required to have an 'element type' attribute"); return ERROR; } if (DXGetArrayClass(m->c) == CLASS_MESHARRAY) { m->isreg++; if (!DXGetMeshOffsets((MeshArray)m->c, m->meshoff)) return ERROR; } else m->isreg = 0; } else m->contype = NULL; return OK; } static Error cmpstruct(struct mustmatch *m1, struct mustmatch *m2) { int i; /* if they both have positions, make sure the dimensionality matches. */ if (m1->p && m2->p) { if (m1->posshape != m2->posshape) { DXSetError(ERROR_INVALID_DATA, "all members of a composite field must have the same positions dimensionality. member %d is %dD, member %d is %dD", m1->member, m1->posshape, m2->member, m2->posshape); return ERROR; } } /* if neither has connections (both are scattered points fields) * that's fine and we are done. */ if (!m1->c && !m2->c) return OK; /* do they both have a connections component? */ if (!m1->c || !m2->c) { DXSetError(ERROR_INVALID_DATA, "all members of a composite field must have the same type of connections. member %d has %s connection component, member %d has %s connection component", m1->member, m1->c ? "a" : "no", m2->member, m2->c ? "a" : "no"); return ERROR; } /* does the element type match? */ if (!m1->contype || !m2->contype || strcmp(m1->contype, m2->contype)) { DXSetError(ERROR_INVALID_DATA, "all members of a composite field must have the same type of connections. member %d has %s 'element type' attribute, member %d has %s 'element type' attribute", m1->member, m1->contype ? m1->contype : "no", m2->member, m2->contype ? m2->contype : "no"); return ERROR; } /* do the meshoffsets make sense if they are part of a reg grid? */ /* this isn't enough. we need to compare all the meshoffsets with * all the other components and make sure they don't overlap. the * cheap test here will catch if any are identical to the first non- * empty group member. since none should be the same (and 0,0,0 is * the most likely value), this might help some. */ if (m1->isreg && m2->isreg) { for (i=0; iposshape; i++) { if (m1->meshoff[i] != m2->meshoff[i]) break; } if (i == m1->posshape) { DXSetError(ERROR_INVALID_DATA, "composite field members %d and %d should not have identical connection mesh offset values", m1->member, m2->member); return ERROR; } } return OK; } static Error GroupCheck(Group g) { Object subo; struct mustmatch first, next; int needone = 1; int i; switch (DXGetGroupClass(g)) { case CLASS_COMPOSITEFIELD: for (i=0; subo = DXGetEnumeratedMember(g, i, NULL); i++) { /* get info from first nonempty member */ if (needone) { if (!stuffstruct(&first, subo, i)) return ERROR; if (first.p != NULL) { needone = 0; continue; } } /* get info from subo member */ if (!stuffstruct(&next, subo, i)) return ERROR; if (next.p == NULL) continue; /* make sure the positions are all the same shape, and that * all the connections are the same type, and that if the * positions are regular, that the mesh offsets make sense. */ if (!cmpstruct(&first, &next)) return ERROR; } break; case CLASS_MULTIGRID: /* ? */ break; case CLASS_SERIES: /* warn if series positions are not monotonic? */ break; default: /* generic group, or new group type */ /* no restrictions */ break; } return OK; } static Error FieldCheck(Field f) { Array subo, pos; char *name; char *elemtype; int haspositions = 0; int i; pos = (Array)DXGetComponentValue(f, "positions"); if (pos) haspositions++; for (i=0; subo=(Array)DXGetEnumeratedComponentValue(f, i, &name); i++) { if (DXGetObjectClass((Object)subo) != CLASS_ARRAY) { DXSetError(ERROR_INVALID_DATA, "field component `%s' not an array object", name); return ERROR; } if (!strcmp(name, "positions")) { if (!PositionsCheck(subo)) return ERROR; continue; } if (!strcmp(name, "connections")) { if (!haspositions) { DXSetError(ERROR_INVALID_DATA, "field has 'connections' but no 'positions'"); return ERROR; } if (!(elemtype = ConnectionsCheck(subo))) return ERROR; if (!strcmp(elemtype, "lines")) { if (!AreLinesOK(pos, subo)) return ERROR; continue; } if (!strcmp(elemtype, "quads")) { continue; } if (!strcmp(elemtype, "cubes")) { continue; } if (!strcmp(elemtype, "triangles")) { if (!AreTrisOK(pos, subo)) return ERROR; continue; } if (!strcmp(elemtype, "tetrahedra")) { if (!AreTetrasOK(pos, subo)) return ERROR; continue; } } if (!strcmp(name, "faces")) { continue; } if (!strcmp(name, "loops")) { continue; } if (!strcmp(name, "edges")) { continue; } } return OK; } static Error PositionsCheck(Array a) { int items; Type type; Category cat; int rank, shape[MAXRANK]; if (!DXGetArrayInfo(a, &items, &type, &cat, &rank, shape)) return ERROR; if (type != TYPE_FLOAT) { DXSetError(ERROR_INVALID_DATA, "positions must be type float"); return ERROR; } if (cat != CATEGORY_REAL) { DXSetError(ERROR_INVALID_DATA, "positions must be category real"); return ERROR; } if (rank != 1) { DXSetError(ERROR_INVALID_DATA, "positions must be a vector"); return ERROR; } return OK; } static char * ConnectionsCheck(Array a) { int items; Type type; Category cat; int rank, shape[MAXRANK]; char *elemtype; if (!DXGetArrayInfo(a, &items, &type, &cat, &rank, shape)) return ERROR; if (type != TYPE_INT) { DXSetError(ERROR_INVALID_DATA, "connections must be type integer"); return ERROR; } if (cat != CATEGORY_REAL) { DXSetError(ERROR_INVALID_DATA, "connections must be category real"); return ERROR; } if (rank != 1) { DXSetError(ERROR_INVALID_DATA, "connections must be a vector"); return ERROR; } if (!DXGetStringAttribute((Object)a, "element type", &elemtype)) { DXSetError(ERROR_INVALID_DATA, "`connections' component has no `element type' attribute"); return ERROR; } return elemtype; } static Error AreTetrasOK(Array pos, Array conn) { int pitems, citems; int prank, pshape[MAXRANK]; int crank, cshape[MAXRANK]; int i, j, *ip; int handed = 0, this; Point *pp; if (!DXGetArrayInfo(pos, &pitems, NULL, NULL, &prank, pshape)) return ERROR; if (prank != 1 || pshape[0] != 3) { DXSetError(ERROR_INVALID_DATA, "tetrahedra positions must be 3-vectors"); return ERROR; } if (pitems == 0) { DXWarning("positions array contains no items"); return OK; } pp = (Point *)DXGetArrayData(pos); if (!pp) return ERROR; if (!DXGetArrayInfo(conn, &citems, NULL, NULL, &crank, cshape)) return ERROR; if (crank != 1 || cshape[0] != 4) { DXSetError(ERROR_INVALID_DATA, "tetrahedra connections must be 4-vectors"); return ERROR; } if (citems == 0) { DXWarning("connections array contains no items"); return OK; } ip = (int *)DXGetArrayData(conn); if (!ip) return ERROR; for (i=0; i= pitems) { DXSetError(ERROR_INVALID_DATA, "connection index %d out of range (%d); must be 0 to %d", i, ip[j], pitems-1); return ERROR; } } /* find the first tetra which is has either positive or negative * volume, and make sure all others agree. */ this = TetraVol(pp+ip[0], pp+ip[1], pp+ip[2], pp+ip[3]); if (this && !handed) { handed = this; continue; } if (this && this != handed) { DXSetError(ERROR_INVALID_DATA, "the positions in tetrahedra %d are ordered inconsistently compared to other tetrahedra in this field", i); return ERROR; } } return OK; } static Error AreTrisOK(Array pos, Array conn) { int pitems, citems; int prank, pshape[MAXRANK]; int crank, cshape[MAXRANK]; int i, j, *ip; Point *pp; if (!DXGetArrayInfo(pos, &pitems, NULL, NULL, &prank, pshape)) return ERROR; if (prank != 1 || (pshape[0] != 2 && pshape[0] != 3)) { DXSetError(ERROR_INVALID_DATA, "triangle positions must be 2- or 3-vectors"); return ERROR; } if (pitems == 0) { DXWarning("positions array contains no items"); return OK; } pp = (Point *)DXGetArrayData(pos); if (!pp) return ERROR; if (!DXGetArrayInfo(conn, &citems, NULL, NULL, &crank, cshape)) return ERROR; if (crank != 1 || cshape[0] != 3) { DXSetError(ERROR_INVALID_DATA, "triangle connections must be 3-vectors"); return ERROR; } if (citems == 0) { DXWarning("connections array contains no items"); return OK; } ip = (int *)DXGetArrayData(conn); if (!ip) return ERROR; for (i=0; i= pitems) { DXSetError(ERROR_INVALID_DATA, "connection index %d out of range (%d); must be 0 to %d", i, ip[j], pitems-1); return ERROR; } } #if 0 /* this doesn't mean anything */ if (!TriArea(pp+ip[0], pp+ip[1], pp+ip[2])) { DXSetError(ERROR_INVALID_DATA, "the positions in triangle %d are ordered inconsistently", i); return ERROR; } #endif } return OK; } static Error AreLinesOK(Array pos, Array conn) { int pitems, citems; int prank, pshape[MAXRANK]; int crank, cshape[MAXRANK]; int i, j, *ip; float *fp; if (!DXGetArrayInfo(pos, &pitems, NULL, NULL, &prank, pshape)) return ERROR; if (prank != 1 || pshape[0] < 1 || pshape[0] > 3) { DXSetError(ERROR_INVALID_DATA, "line positions must be 1-, 2- or 3-vectors"); return ERROR; } if (pitems == 0) { DXWarning("positions array contains no items"); return OK; } fp = (float *)DXGetArrayData(pos); if (!fp) return ERROR; if (!DXGetArrayInfo(conn, &citems, NULL, NULL, &crank, cshape)) return ERROR; if (crank != 1 || cshape[0] != 2) { DXSetError(ERROR_INVALID_DATA, "line connections must be 2-vectors"); return ERROR; } if (citems == 0) { DXWarning("connections array contains no items"); return OK; } ip = (int *)DXGetArrayData(conn); if (!ip) return ERROR; for (i=0; i= pitems) { DXSetError(ERROR_INVALID_DATA, "connection index %d out of range (%d); must be 0 to %d", i, ip[j], pitems-1); return ERROR; } } } return OK; } /* compute the volume of the tetra and see if it is a positive * or negative number. this returns -1, 0 or 1 for negative, too small * to tell, and positive, respectively. * if the volume is negative, switching the order of any two of * the point indicies will fix the problem. */ static int TetraVol(Point *p, Point *q, Point *r, Point *s) { double xqp, yqp, zqp; double xrp, yrp, zrp; double xsp, ysp, zsp; double vx, vy, vz; double vol; xqp = q->x - p->x; yqp = q->y - p->y; zqp = q->z - p->z; xrp = r->x - p->x; yrp = r->y - p->y; zrp = r->z - p->z; xsp = s->x - p->x; ysp = s->y - p->y; zsp = s->z - p->z; vx = (yrp * zqp) - (yqp * zrp); vy = (zrp * xqp) - (zqp * xrp); vz = (xrp * yqp) - (xqp * yrp); vol = vx*xsp + vy*ysp + vz*zsp; if (fabs(vol) < DXD_FLOAT_EPSILON) return 0; return (vol < 0.0) ? -1 : 1; } /* */ static int TriArea(Point *p, Point *q, Point *r) { double xpq, ypq; double xrq, yrq; double area; xpq = p->x - q->x; ypq = p->y - q->y; xrq = r->x - q->x; yrq = r->y - q->y; area = (xpq * yrq) - (xrq * ypq); if (fabs(area) < DXD_FLOAT_EPSILON) return 0; return (area < 0.0) ? -1 : 1; } /* * try to detect circular references. attrs can be references to things * further up the hierarchy, but direct descendents (like components or * group members) can't refer back to something in their parent chain. */ struct plist { Object this; struct plist *next; }; static Error cpath(struct plist *p, Object o); static Error pushchild(struct plist *p, Object newo); static void popchild(struct plist *p); Error _dxfIsCircular(Object o) { struct plist pl; pl.this = o; pl.next = NULL; return cpath(&pl, o); } static Error cpath(struct plist *p, Object o) { Object child = NULL; char *name = NULL; int i; switch (DXGetObjectClass(o)) { case CLASS_GROUP: for (i=0; child = DXGetEnumeratedMember((Group)o, i, &name); i++) { if (pushchild(p, child) == ERROR) { DXAddMessage("member %d", i); return ERROR; } popchild(p); } return OK; case CLASS_FIELD: for (i=0; child = DXGetEnumeratedComponentValue((Field)o, i, &name); i++) { if (pushchild(p, child) == ERROR) { DXAddMessage("member %d", i); return ERROR; } popchild(p); } return OK; case CLASS_SCREEN: if (!DXGetScreenInfo((Screen)o, &child, NULL, NULL)) return NULL; if (pushchild(p, child) == ERROR) { DXAddMessage("screen object"); return ERROR; } popchild(p); return OK; case CLASS_CLIPPED: if (!DXGetClippedInfo((Clipped)o, &child, NULL)) return NULL; if (pushchild(p, child) == ERROR) { DXAddMessage("clipped object"); return ERROR; } popchild(p); if (!DXGetClippedInfo((Clipped)o, NULL, &child)) return NULL; if (pushchild(p, child) == ERROR) { DXAddMessage("clipping object"); return ERROR; } popchild(p); return OK; case CLASS_XFORM: if (!DXGetXformInfo((Xform)o, &child, NULL)) return NULL; if (pushchild(p, child) == ERROR) { DXAddMessage("transform object"); return ERROR; } popchild(p); return OK; /* all other objects can't contain others */ default: return OK; } /* not reached */ } /* private for debugging */ static void printplist(struct plist *p) { struct plist *pp; pp = p; do { DXMessage("p = 0x%08x: object = 0x%08x, next = 0x%08x", (uint)pp, (uint)pp->this, (uint)pp->next); pp = pp->next; } while (pp); } /* check list on the way down. if you find a duplicate, return error. * otherwise alloc space for a new entry and add this object to the * end of the list. */ static Error pushchild(struct plist *p, Object newo) { struct plist *pl, *pp; pp = p; do { if (newo == pp->this) { DXSetError(ERROR_INVALID_DATA, "circular reference in object"); return ERROR; } if (pp->next) pp = pp->next; } while (pp->next); pl = (struct plist *)DXAllocateLocal(sizeof(struct plist)); if (!pl) return ERROR; pl->this = newo; pl->next = NULL; pp->next = pl; return cpath(p, newo); } /* remove the last entry from the list. */ static void popchild(struct plist *p) { struct plist *pp; pp = p; while (pp->next) { if (!pp->next->next) { DXFree((Pointer)pp->next); pp->next = NULL; return; } pp = pp->next; } /* not reached */ }