/***********************************************************************/ /* 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 "fieldClass.h" #include "internals.h" static Array add(Field f, int start, int n, Pointer c, Object ct, int per) { Array a = (Array) DXGetComponentValue(f, CONNECTIONS); if (!a) { a = DXNewArray(TYPE_INT, CATEGORY_REAL, 1, per); if (!a) return NULL; DXSetComponentValue(f, CONNECTIONS, (Object) a); DXSetAttribute((Object)a, ELEMENT_TYPE, ct); } else { Object t; t = DXGetComponentAttribute(f, CONNECTIONS, ELEMENT_TYPE); if (!t) DXErrorReturn(ERROR_BAD_PARAMETER, "Connections component is missing element type attribute"); if (t!=ct) { char *s, *cs; s = DXGetString((String)t); cs = DXGetString((String)ct); if (!s || !cs) return NULL; if (strcmp(cs,s) != 0) DXErrorReturn(ERROR_BAD_PARAMETER, "Connections type conflict"); } } if (!DXAddArrayData(a, start, n, c)) return NULL; return a; } /* * begin/end cell */ Field _dxfBeginCell(Field f) { CHECK(f, CLASS_FIELD); f->cell = 1; return f; } Field _dxfEndCell(Field f) { CHECK(f, CLASS_FIELD); f->cell = 0; return f; } /* * beginface, next point, end face */ Field _dxfBeginFace(Field f) { CHECK(f, CLASS_FIELD); f->npts = 0; return f; } Field _dxfNextPoint(Field f, PointId id) { CHECK(f, CLASS_FIELD); if (f->npts==f->pts_alloc) { int n = f->pts_alloc? f->pts_alloc*3/2 : 100; PointId *p = (PointId *) DXReAllocate((Pointer)f->pts, n*sizeof(int)); if (!p) return NULL; f->pts = p; f->pts_alloc = n; } f->pts[f->npts++] = id; } /* * For volume faces, canonically decompose the face * into triangles and put into hash table, distinguishing * between surface and inner faces. * * XXX - what if hash table fills up? * doesn't preserve sense of surface faces */ static Field _EndFaceVolume(Field f) { int npoints, min, i, dir, h; Triangle t; if (f->npts<3) return NULL; /* create hash table */ if (!f->hash) { DXMarkTime("start faces"); if (!DXGetArrayInfo((Array)DXGetComponentValue(f, POSITIONS), &npoints, NULL, NULL, NULL, NULL)) DXErrorReturn(ERROR_MISSING_DATA, "points component missing in EndFace"); for (f->hash_alloc=1; f->hash_alloc<10*npoints; f->hash_alloc*=2) continue; f->hash = (struct hash *) DXAllocate(f->hash_alloc*sizeof(struct hash)); if (!f->hash) return NULL; memset(f->hash, 0, f->hash_alloc*sizeof(struct hash)); /* free old surface and inner faces */ DXSetComponentValue(f, SURFACE, NULL); DXSetComponentValue(f, INNER, NULL); } /* canonical starting point is min vertex number */ for (min=0, i=1; inpts; i++) if (f->pts[i] < f->pts[min]) min = i; #define PT(i) (f->pts[(i)%(f->npts)]) /* canonical direction: pick smallest neighbor of min */ dir = PT(min+1)npts-2 triangles */ for (i=1; inpts-1; i++) { /* here's the triangle */ t.p = PT(min); t.q = PT(min+i+(dir)); t.r = PT(min+i+(1-dir)); /* put in hash table */ h = (t.p + 17*t.q + 513*t.r) & (f->hash_alloc-1); while (f->hash[h].count && (f->hash[h].tri.p!=t.p || f->hash[h].tri.q!=t.q || f->hash[h].tri.r!=t.r)) h = (h+1) & (f->hash_alloc-1); f->hash[h].tri = t; f->hash[h].count += 1; if (f->hash[h].count==1) /* new face: */ f->nsurface += 1; /* assume surface */ else if (f->hash[h].count&1) { /* count now odd, was even: */ f->nsurface += 1; /* now surface */ f->ninner -= 1; /* was inner */ } else { /* count now even, was odd: */ f->ninner += 1; /* now inner */ f->nsurface -= 1; /* was surface */ } } f->npts = 0; return f; } /* * For surface faces, just put the faces into "triangle" component. */ static Field _EndFaceSurface(Field f) { int n, i; Triangle t; Array a; /* allocate "triangle" component */ a = add(f, 0, 0, NULL, O_TRIANGLES, 3); if (!a) return NULL; /* starting face number */ if (!DXGetArrayInfo(a, &n, NULL, NULL, NULL, NULL)) DXErrorReturn(ERROR_INTERNAL, "DXGetArrayInfo failed in EndFace!"); /* add f->npts-2 triangles starting at face n */ for (i=1; inpts-1; i++, n++) { t.p = f->pts[0]; t.q = f->pts[i]; t.r = f->pts[i+1]; if (!DXAddArrayData(a, n, 1, (Pointer)&t)) return NULL; } f->npts = 0; return f; } Field _dxfEndFace(Field f) { CHECK(f, CLASS_FIELD); if (f->cell) return _EndFaceVolume(f); else return _EndFaceSurface(f); } /* * end field */ static int trace = 0; void _dxfTraceField(int t) { trace = t; } /* * These variables allow for sharing of strings */ Field DXEndField(Field f) { Triangle *surface, *inner; int npoints=0, i; int ns, ni, np, nc, n; Array a, ap, ac; Object o; char *name; static struct { char **component; /* the component */ char **attribute; /* its attribute */ Object *value; /* the attribute's value */ char **name; /* dependent's name */ int n; /* number of items in value */ } *s, standard[] = { /* standard comp attributes */ &POSITIONS, &DEP, &O_POSITIONS, &POSITIONS, -1, &CONNECTIONS, &DEP, &O_CONNECTIONS, &CONNECTIONS, -1, &CONNECTIONS, &REF, &O_POSITIONS, &POSITIONS, -1, &DATA, &DEP, &O_POSITIONS, &POSITIONS, -1, &COLORS, &DEP, &O_POSITIONS, &POSITIONS, -1, &IMAGE, &DEP, &O_POSITIONS, &POSITIONS, -1, &FRONT_COLORS, &DEP, &O_POSITIONS, &POSITIONS, -1, &BACK_COLORS, &DEP, &O_POSITIONS, &POSITIONS, -1, &OPACITIES, &DEP, &O_POSITIONS, &POSITIONS, -1, &TANGENTS, &DEP, &O_POSITIONS, &POSITIONS, -1, &NORMALS, &DEP, &O_POSITIONS, &POSITIONS, -1, &BINORMALS, &DEP, &O_POSITIONS, &POSITIONS, -1, NULL }; CHECK(f, CLASS_FIELD); /* trim component arrays */ for (i=0; o=DXGetEnumeratedComponentValue(f, i, NULL); i++) if (DXGetObjectClass(o)==CLASS_ARRAY) if (!DXTrim((Array)o)) return NULL; /* free ->pts array */ if (f->pts) { DXFree((Pointer)f->pts); f->pts_alloc = 0; } /* copy hash table of faces */ if (f->hash) { DXMarkTime("end faces"); DXMarkTime("start copy"); /* surface */ a = DXNewArray(TYPE_INT, CATEGORY_REAL, 1, 3); if (!a) return NULL; DXSetComponentValue(f, SURFACE, (Object)a); DXSetAttribute((Object)a, REF, O_POSITIONS); DXAddArrayData(a, 0, f->nsurface, NULL); surface = (Triangle *) DXGetArrayData(a); /* inner */ a = DXNewArray(TYPE_INT, CATEGORY_REAL, 1, 3); if (!a) return NULL; DXSetComponentValue(f, INNER, (Object)a); DXSetAttribute((Object)a, REF, O_POSITIONS); DXAddArrayData(a, 0, f->ninner, NULL); inner = (Triangle *) DXGetArrayData(a); /* copy surface and inner faces in */ for (i=0, ns=0, ni=0; ihash_alloc; i++) { if (f->hash[i].count&1) /* odd count => surface face */ surface[ns++] = f->hash[i].tri; else if (f->hash[i].count!=0) /* even count => inner face */ inner[ni++] = f->hash[i].tri; } ASSERT(ns==f->nsurface && ni==f->ninner); DXFree((Pointer)f->hash); f->hash = NULL; f->hash_alloc = 0; DXMarkTime("end copy"); } /* pre-compute bounding box */ if (!DXBoundingBox((Object) f, NULL) && (DXGetError()!=ERROR_NONE)) return NULL; /* * Pre-compute volume element neighbors. * These are done ahead of time to prevent the race condition * that arises if done on-the-fly in rendering. In other * modules, which parallelize by data, it is ok to do them on * the fly. */ if (!DXGetComponentValue(f, NEIGHBORS)) { if (!_dxf_TetraNeighbors(f) && DXGetError()!=ERROR_NONE) return NULL; if (!_dxf_CubeNeighbors(f) && DXGetError()!=ERROR_NONE) return NULL; } /* if we decide at some point to precompute statistics, here is one * place where it makes sense to put the call. the other is in the * DXEndObject() routine, since stats can go parallel and needs to * compute stats on compositefields differently than normal fields. * * we currently do not precompute statistics because it can be expensive * in time for large arrays, and if there are invalid points the * stats have to be computed twice, once with and without considering * the invalids. if the stats aren't needed, this is unnecessary work. * the argument for precomputing them is to avoid the stats call modifying * the input to a module. the statistics() call adds a statistics array to * the input field so subsequent calls for stats do not have to recompute * the same values. normally modifying the input to a module is a very bad * thing because if two tasks in one module modify the same input, there is * no locking at the field level and the result can be garbage. in fact i * think this is the only routine we knowingly allow to modify inputs. * we allow it to avoid precomputing stats on every field every time, and * don't have problems because first we don't allow multiple modules * to execute at the same time so two different modules can't be computing * statistics on the same field at the same time, and second most modules * go parallel by giving different fields to each task, so even if the * tasks are executing at the same time, they are generally working on * different fields. also, many modules need the min/max before going * to the task level, so the stats are computed before going parallel. * it is a potential source of problems however. */ /* i moved the following code down to here. it used to be the first code * in the module, but it makes more sense to be here next to the code * which uses these values. nsc 06jun96 */ /* number of items in value */ np = nc = -1; ap = (Array)DXGetComponentValue(f, POSITIONS); if (ap && !DXGetArrayInfo(ap, &np, NULL, NULL, NULL, NULL)) return NULL; ac = (Array)DXGetComponentValue(f, CONNECTIONS); if (ac && !DXGetArrayInfo(ac, &nc, NULL, NULL, NULL, NULL)) return NULL; for (s=standard; s->component; s++) { if (*s->name == POSITIONS) s->n = np; else if (*s->name == CONNECTIONS) s->n = nc; else s->n = -1; } /* * set standard component attributes * XXX - this depends on knowing that fields always store the * name as a pointer to one of the standard strings */ for (i=0; o=DXGetEnumeratedComponentValue(f, i, &name); i++) { for (s=standard; s->component; s++) { if (name==*s->component && s->n>=0) { if (!DXGetAttribute(o, *s->attribute)) { if (*s->attribute==DEP) { if (!DXGetArrayInfo((Array)o,&n,NULL,NULL,NULL,NULL)) return NULL; if (n!=s->n) { DXSetError(ERROR_INVALID_DATA, "The \"%s\" component has %d elements. " "By default, it depends on the \"%s\" " "component, which has %d elements. " "A valid \"dep\" attribute should " "be specified for the \"%s\" component " "to override the default.", *s->component, n, *s->name, s->n, *s->component); return NULL; } } DXSetAttribute(o, *s->attribute, *s->value); } /* break; */ } } } if (trace) { int npoints=0, nsurface=0, ninner=0; DXGetArrayInfo((Array)DXGetComponentValue(f, POSITIONS), &npoints, NULL, NULL, NULL, NULL); DXGetArrayInfo((Array)DXGetComponentValue(f, SURFACE), &nsurface, NULL, NULL, NULL, NULL); DXGetArrayInfo((Array)DXGetComponentValue(f, INNER), &ninner, NULL, NULL, NULL, NULL); DXDebug("F", "field 0x%x: %d points, %d surface, %d inner", f, npoints, nsurface, ninner); } return f; } /* * ChangedXXX routines */ /* true if there is an attr named `a', the contents are a single string, * and the contents match the target component name. */ #define ATTR(a) (DXGetStringAttribute(c, a, &cp) && !strcmp(cp, component)) /* true if there is an attr named `a', the contents are either a single * string or a string list, and if any member of the stringlist matches * the target component name. */ static int attrlist(Object c, char *a, char *component) { Object o; int l; char *cp; if (!(o = DXGetAttribute(c, a))) return 0; for (l=0; DXExtractNthString(o, l, &cp); l++) if (!strcmp(cp, component)) return 1; return 0; } Field DXChangedComponentStructure(Field f, char *component) { int i, n; Object c, o; char *names[100], *cp; for (i=0, n=0; c=DXGetEnumeratedComponentValue(f, i, &names[n]); i++) if (ATTR(DEP) || ATTR(REF) || attrlist(c, DER, component)) if (strcmp(names[n],component) != 0) n++; ASSERT(n<100); for (i=0; it != POSITIONS_ENTRY) { DXSetError(ERROR_INTERNAL, "DXEndObject: entry type error"); goto error; } if (hptr->r) { if (! DXSetComponentValue(f, "box", (Object)hptr->r)) goto error; str = (Object)DXNewString("positions"); if (! str) goto error; if (! DXSetComponentAttribute(f, "box", "der", str)) { DXDelete(str); goto error; } } } helt.a = DXGetComponentValue(f, "connections"); if (helt.a) { hptr = (HElement *)DXQueryHashElement(hTable, (Key)&helt); if (! hptr) { DXSetError(ERROR_INTERNAL, "DXEndObject: connections entry missing in pass2"); goto error; } if (hptr->t != CONNECTIONS_ENTRY) { DXSetError(ERROR_INTERNAL, "DXEndObject: entry type error"); goto error; } if (hptr->r) { if (!DXSetComponentValue(f, "neighbors", (Object)hptr->r)) goto error; str = (Object)DXNewString("connections"); if (! str) goto error; if (! DXSetComponentAttribute(f, "neighbors", "dep", str)) { DXDelete(str); goto error; } if (! DXSetComponentAttribute(f, "neighbors", "ref", str)) { DXDelete(str); goto error; } } } #ifndef DO_ENDFIELD if (! DXEndField(f)) goto error; #endif } break; } default: break; } return OK; error: return ERROR; } /* * Task executed for each hash table entry. If the entry is of type * POSITIONS_ENTRY, then compute the bounding box and stick it into the * result slot of the hash element. Otherwise, it ought to be of type * CONNECTIONS_ENTRY, so we compute a neighbors component and put it into * the hash element. */ static Error EndObjectTask(Pointer ptr) { HElement *hPtr = *(HElement **)ptr; if (hPtr->t == POSITIONS_ENTRY) { if (DXBoundingBox((Object)(hPtr->f), NULL)) { hPtr->r = (Array)DXGetComponentValue(hPtr->f, "box"); if (! hPtr->r) { if (DXGetError() == ERROR_NONE) DXSetError(ERROR_INTERNAL, "DXBoundingBox succeeded but no box component was found"); goto error; } } else { if (DXGetError() != ERROR_NONE) goto error; hPtr->r = NULL; } } else if (hPtr->t == CONNECTIONS_ENTRY) { hPtr->r = DXNeighbors(hPtr->f); if (DXGetError() != ERROR_NONE) goto error; } else { DXSetError(ERROR_INTERNAL, "unknown entry type"); goto error; } return OK; error: return ERROR; } Object DXEndObject(Object o) { HashTable hTable = NULL; HElement *elt; hTable = DXCreateHash(sizeof(HElement), NULL, NULL); if (! hTable) goto error; /* * Traverse the input, creating a hash entry of type POSITIONS_ENTRY * for each unique positions array and a entry of type CONNECTIONS_ENTRY * for each unique connections array. */ if (! _Recur(o, hTable, PASS1)) goto error; /* * Traverse the hash table. For each element of type CONNECTIONS_ENTRY, * create a task that will create a neighbors array using the template * field. */ if (! DXCreateTaskGroup()) goto error; DXInitGetNextHashElement(hTable); while ((elt = (HElement *)DXGetNextHashElement(hTable)) != NULL) { if (elt->t == CONNECTIONS_ENTRY) if (! DXAddTask(EndObjectTask, (Pointer)&elt, sizeof(Pointer), 1)) { DXAbortTaskGroup(); goto error; } } if (! DXExecuteTaskGroup() || DXGetError() != ERROR_NONE) goto error; /* * Traverse the hash table again. For each element of type * CONNECTIONS_ENTRY, * create a task that will create a box array * using the template field. */ if (! DXCreateTaskGroup()) goto error; DXInitGetNextHashElement(hTable); while ((elt = (HElement *)DXGetNextHashElement(hTable)) != NULL) { if (elt->t == POSITIONS_ENTRY) if (! DXAddTask(EndObjectTask, (Pointer)&elt, sizeof(Pointer), 1)) { DXAbortTaskGroup(); goto error; } } if (! DXExecuteTaskGroup() || DXGetError() != ERROR_NONE) goto error; /* * Traverse the input again. For each field, access the hash table keyed * by the positions component address to get a box component and by the * connections component address to get a neighbors component. Then call * DXEndField on each. */ if (! _Recur(o, hTable, PASS2)) goto error; DXDestroyHash(hTable); return o; error: DXDestroyHash(hTable); return NULL; }