/***********************************************************************/ /* 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 struct argblk { int justcull; /* if set, don't take stats - just cull */ int reallycull; /* if set, call cull instead of leaving inv comps */ int shape; /* rank 1 and either shape == 1 or shape == data */ float *min; /* pointer to allocated list of mins */ float *max; /* pointer to allocated list of maxs */ int exclude; /* 0 = include, 1 = exclude */ int noconnect; /* 1 = remove connections first before doing include */ }; static Object Object_Include(Object o, struct argblk *); static Field Field_Include(Field f, int justcull, int shape, float *min, float *max, int exclude, int reallycull, int removeconnect); static Error VectorLength(Object o, int *len); static Error VectorExtract(Object o, int len, float *ptr); extern Object _dxfDXEmptyObject(Object o); #define IsEmpty(o) _dxfDXEmptyObject(o) static Field Include_Wrapper(Field f, char *argblk, int size); static Array Include_Array(Array a, struct argblk b); /* 0 = input * 1 = min val * 2 = max val * 3 = exclude flag * 4 = really cull? * 5 = pointwise flag * * 0 = output */ int m_Include(Object *in, Object *out) { struct argblk argblk; int msize, i; int minset = 0; int maxset = 0; int simple_array = 0; /* arg defaults */ argblk.justcull = 0; argblk.reallycull = 1; argblk.shape = 1; argblk.min = NULL; argblk.max = NULL; argblk.exclude = 0; argblk.noconnect = 0; out[0] = NULL; if (!in[0]) { DXSetError(ERROR_BAD_PARAMETER, "#10000", "input"); return ERROR; } if (DXGetObjectClass(in[0]) == CLASS_ARRAY) simple_array++; /* if the caller doesn't set min, max or exclude, then the only useful * thing we can be doing is cull. */ if (!in[1] && !in[2] && !in[3]) argblk.justcull = 1; if (in[1]) { /* DXQueryParameter is useless here. * i want 1 float vector and to know the shape, not give * the shape and get the count... */ if (!VectorLength(in[1], &argblk.shape)) { DXSetError(ERROR_BAD_PARAMETER, "#10640", "minimum value"); return ERROR; } argblk.min = (float *)DXAllocate(argblk.shape * sizeof(float)); if (!argblk.min) goto error; if (!VectorExtract(in[1], argblk.shape, argblk.min)) goto error; minset++; } if (in[2]) { /* same here */ if (!VectorLength(in[2], &msize)) { DXSetError(ERROR_BAD_PARAMETER, "#10640", "maximum value"); return ERROR; } if (minset) { if (msize != argblk.shape) { DXSetError(ERROR_BAD_PARAMETER, "shape of max must match shape of min"); goto error; } } else argblk.shape = msize; argblk.max = (float *)DXAllocate(argblk.shape * sizeof(float)); if (!argblk.max) goto error; if (!VectorExtract(in[2], argblk.shape, argblk.max)) goto error; maxset++; } /* the 4 possibilities here are: * neither min or max are set - shape is 1, limits are minfloat/maxfloat * min is set but not max - set max to same shape, all lims maxfloat * max is set but not min - set min to same shape, all lims minfloat * both min and max are already set - do nothing */ if (!minset && !maxset) { argblk.min = (float *)DXAllocate(sizeof(float)); if (!argblk.min) goto error; *argblk.min = -DXD_MAX_FLOAT; argblk.max = (float *)DXAllocate(sizeof(float)); if (!argblk.max) goto error; *argblk.max = DXD_MAX_FLOAT; } else if (!minset && maxset) { argblk.min = (float *)DXAllocate(argblk.shape * sizeof(float)); if (!argblk.min) goto error; for (i=0; i argblk.max[i]) { DXSetError(ERROR_BAD_PARAMETER, "min greater than max"); goto error; } if (in[3]) { if (!DXExtractInteger(in[3], &argblk.exclude) || (argblk.exclude != 0 && argblk.exclude != 1)) { DXSetError(ERROR_BAD_PARAMETER, "#10070", "exclude flag"); goto error; } } if (in[4]) { if (!DXExtractInteger(in[4], &argblk.reallycull) || (argblk.reallycull != 0 && argblk.reallycull != 1)) { DXSetError(ERROR_BAD_PARAMETER, "#10070", "cull flag"); goto error; } if (simple_array && (argblk.reallycull == 0)) { DXSetError(ERROR_BAD_PARAMETER, "input is an array so invalid data points must be culled out"); goto error; } } if (in[5]) { if (!DXExtractInteger(in[5], &argblk.noconnect) || (argblk.noconnect != 0 && argblk.noconnect != 1)) { DXSetError(ERROR_BAD_PARAMETER, "#10070", "pointwise flag"); goto error; } } /* allow empty objects to pass thru unchanged */ if (!simple_array && IsEmpty(in[0])) { DXWarning("#4000", "input"); out[0] = in[0]; goto done; } /* if the user didn't set max or min, then all they can want us to * do is remove invalid and/or unreferenced points. if the input * is a simple array, there's nothing to do. */ if (argblk.justcull && simple_array) { out[0] = in[0]; goto done; } /* if we aren't just culling, then at least one of the fields in the * input has to have a data component. */ if (!argblk.justcull && !simple_array && !DXExists(in[0], "data")) { DXSetError(ERROR_INVALID_DATA, "#10240", "data"); goto error; } /* make sure the stats are computed for the whole object before * starting the traversal. this makes sure the stats are right * for composite fields. */ if (!argblk.justcull && !simple_array && !DXStatistics((Object)in[0], "data", NULL, NULL, NULL, NULL)) goto error; if (simple_array) out[0] = (Object)Include_Array((Array)in[0], argblk); else out[0] = DXProcessParts(in[0], Include_Wrapper, (char *)&argblk, sizeof(argblk), 1, 1); /* handle the case where all the data is excluded, and the wrapper * has returned an empty field, but process parts has culled it * out so the return is null (but there is no error). */ if(!out[0] && DXGetError()==ERROR_NONE) out[0] = (Object)DXEndField(DXNewField()); done: DXFree((Pointer)argblk.min); DXFree((Pointer)argblk.max); return(out[0] ? OK : ERROR); error: DXFree((Pointer)argblk.min); DXFree((Pointer)argblk.max); out[0] = NULL; return ERROR; } /* */ static Field Include_Wrapper(Field f, char *a, int size) { return (Field)Object_Include((Object)f, (struct argblk *)a); } static Object Object_Include(Object o, struct argblk *ap) { Object newo, subo; char *name; Matrix m; float position; Object subo2; int fixed, z; int i; switch(DXGetObjectClass(o)) { case CLASS_GROUP: /* for each member */ for (i=0; subo = DXGetEnumeratedMember((Group)o, i, NULL); i++) { if (!Object_Include(subo, ap)) continue; } break; case CLASS_FIELD: /* even empty fields have to be copied */ if (DXEmptyField((Field)o)) return DXCopy(o, COPY_STRUCTURE); if (!(newo = DXCopy(o, COPY_STRUCTURE))) return NULL; if (!(o = (Object)Field_Include((Field)newo, ap->justcull, ap->shape, ap->min, ap->max, ap->exclude, ap->reallycull, ap->noconnect))) { DXDelete(newo); return NULL; } break; case CLASS_XFORM: if (!DXGetXformInfo((Xform)o, &subo, &m)) return NULL; if (!(newo=Object_Include(subo, ap))) return NULL; o = (Object)DXNewXform(newo, m); break; case CLASS_SCREEN: if (!DXGetScreenInfo((Screen)o, &subo, &fixed, &z)) return NULL; if (!(newo=Object_Include(subo, ap))) return NULL; o = (Object)DXNewScreen(newo, fixed, z); break; case CLASS_CLIPPED: if (!DXGetClippedInfo((Clipped)o, &subo, &subo2)) return NULL; if (!(newo=Object_Include(subo, ap))) return NULL; o = (Object)DXNewClipped(newo, subo2); break; default: /* any remaining objects can't contain fields, so we can safely * pass them through unchanged. */ break; } return o; } extern Array DXScalarConvert(Array a); static Field Field_Include(Field f, int justcull, int shape, float *min, float *max, int exclude, int reallycull, int removeconnect) { int items; Object o = NULL; Array adata = NULL; Array afloat = NULL; char *attr; char inv_attr[64]; /* at least long enough for "invalid xxx" */ float tmin, tmax; float *fp; Pointer dp; int i, j; int dopositions = 0; int doconnections = 0; int wasconnections = 0; int wasinvalid = 0; InvalidComponentHandle icHandle = NULL; /* see if there is a connections component - if not, don't cull out * unreferenced positions at end. but first, make sure they do want * us to use the component. */ if (DXGetComponentValue(f, "connections") || DXGetComponentValue(f, "faces") || DXGetComponentValue(f, "polylines")) { if (removeconnect) { DXDeleteComponent(f, "connections"); DXDeleteComponent(f, "faces"); DXDeleteComponent(f, "polylines"); } else wasconnections++; } /* look at the data component. */ if (adata = (Array)DXGetComponentValue(f, "data")) { if (!DXGetArrayInfo(adata, &items, NULL, NULL, NULL, NULL)) return NULL; /* make sure you don't reuse attr - it gets used later... */ attr = DXGetString((String)DXGetAttribute((Object)adata, "dep")); if (attr && !strcmp(attr, "positions")) dopositions++; else if (attr && !strcmp(attr, "connections")) doconnections++; else if (attr && !strcmp(attr, "faces")) doconnections++; else if (attr && !strcmp(attr, "polylines")) doconnections++; else DXErrorReturn(ERROR_INVALID_DATA, "data must be dependent on either positions or connections"); /* ...and so does this. */ if (dopositions) { if (DXGetComponentValue(f, "connections")) strcpy(inv_attr, "invalid connections"); else if (DXGetComponentValue(f, "faces")) strcpy(inv_attr, "invalid faces"); else if (DXGetComponentValue(f, "polylines")) strcpy(inv_attr, "invalid polylines"); } else { strcpy(inv_attr, "invalid positions"); if (removeconnect) { DXErrorReturn(ERROR_BAD_PARAMETER, "cannot set pointwise flag to ignore connections because data is dependent on connections"); } } } else { /* no data component */ if (!justcull) return NULL; attr = "positions"; dopositions++; strcpy(inv_attr, "invalid connections"); } /* this used to be sooner, but now we need to know what the data * is dep on before going to the end. */ if (justcull) goto cullonly; /* do some simple checks first - if there is going to be no data, * or all the data will remain, then there is no need to look at * all the points. if the cull flag is set, we can cull out * unreferenced points without having to compute the statistics. */ if (shape == 1) { if (!DXStatistics((Object)f, "data", &tmin, &tmax, NULL, NULL)) return NULL; /* include and all points in range, or exclude & all out of range. * keep all data. still call cull to remove unreferenced points. */ if ((!exclude && (tmin >= *min && tmax <= *max)) || (exclude && (tmin > *max || tmax < *min))) goto cullonly; /* include and all points out of range, or exclude & all in range. * delete the old field and just make an empty new one. * if the flag is set so we can't remove invalid data, we have * to make an invalid component instead. */ if ((!exclude && (tmax < *min || tmin > *max)) || (exclude && (tmin >= *min && tmax <= *max))) { if (reallycull) { o = (Object)DXNewField(); if (!o) goto error; DXCopyAttributes (o, (Object)f); DXDelete ((Object)f); return (DXEndField((Field)o)); } else { byte d = 1; o = (Object)DXNewConstantArray(items, (Pointer)&d, TYPE_BYTE, CATEGORY_REAL, 0); if (dopositions) { DXDeleteComponent(f, "invalid positions"); DXSetStringAttribute(o, "dep", "positions"); if (!DXSetComponentValue(f, "invalid positions", o)) goto error; } else if (doconnections) { DXDeleteComponent(f, inv_attr); DXSetStringAttribute(o, "dep", attr); if (!DXSetComponentValue(f, inv_attr, o)) goto error; } goto cullonly; } } /* convert the data to scalar float if it is anything else. * this is the same routine the statistics code uses. */ if (!DXTypeCheck(adata, TYPE_FLOAT, CATEGORY_REAL, 0)) { if ((afloat = DXScalarConvert(adata)) == NULL) return NULL; if(!(dp = DXGetArrayData(afloat))) goto error; } else { if(!(dp = DXGetArrayData(adata))) goto error; } } else { /* shape != 1, can't do simple rejection with statistics */ if (!DXTypeCheck(adata, TYPE_FLOAT, CATEGORY_REAL, 1, shape)) { if ((afloat = DXArrayConvert(adata, TYPE_FLOAT, CATEGORY_REAL, 1, shape)) == NULL) return NULL; if(!(dp = DXGetArrayData(afloat))) goto error; } else { if(!(dp = DXGetArrayData(adata))) goto error; } } /* for exclude, find out if the field originally came in with * invalid components. see about 20 lines below for more info. */ if (exclude) { if ( (dopositions && DXGetComponentValue(f, "invalid positions")) || (doconnections && DXGetComponentValue(f, "invalid connections")) || (doconnections && DXGetComponentValue(f, "invalid faces")) || (doconnections && DXGetComponentValue(f, "invalid polylines"))) wasinvalid++; } /* create invalid data array to mark which data is being discarded, * or get handle to existing invalid array if it already exists. */ if (dopositions) icHandle = DXCreateInvalidComponentHandle((Object)f, NULL, "positions"); else if (doconnections) icHandle = DXCreateInvalidComponentHandle((Object)f, NULL, attr); /* if test is exclude instead of include, we are going to mark all * the items to keep and then invert the sense of the invalid array * at the end. that means we have to invert the sense of any * existing invalid items now before we start. */ if (wasinvalid) { DXInvertValidity(icHandle); /* now iterate through data. if it's marked invalid now, it used * to be valid. see if any invalid things need to be validated. * then at the end the array will be inverted. */ if (shape == 1) { for (i=0, fp=(float *)dp; i= *min && *fp <= *max)) DXSetElementValid(icHandle, i); } } else { for (i=0, fp=(float *)dp; i *(max+j)) continue; } if (j == shape) DXSetElementValid(icHandle, i); } } } else { /* no invalid components before */ /* iterate through data. keep either the positions or connections * which correspond to good data. then at the end, cull out * everything which has been made invalid. */ if (shape == 1) { for (i=0, fp=(float *)dp; i *max) DXSetElementInvalid(icHandle, i); } } else { for (i=0, fp=(float *)dp; i *(max+j)) { DXSetElementInvalid(icHandle, i); break; } } } } } if (exclude) DXInvertValidity(icHandle); if (! DXSaveInvalidComponent(f, icHandle)) goto error; DXFreeInvalidComponentHandle(icHandle); icHandle = NULL; cullonly: /* see what's been invalidated. * for each thing which deps data, remove the same items. for * anything which refs it, renumber it. for anything which refs * something which defs it, remove before renumbering. */ if (wasconnections) { if (!DXInvalidateConnections((Object)f)) goto error; if (!DXInvalidateUnreferencedPositions((Object)f)) goto error; } /* the default is to cull out the invalid items by really removing them * from the field. if the caller turns off the cull option, be sure * any components which are derived from the data component are deleted * (DXCull does this for you), and remove the opposite invalid component * which may have been created while processing the field. the output * should only have an "invalid xxx" component (if you aren't culling), * where 'xxx' matches what the data is dep on. */ if (reallycull) { if (!DXCull((Object)f)) goto error; } else { DXChangedComponentValues(f, "data"); if (dopositions) DXDeleteComponent(f, inv_attr); else if (doconnections) DXDeleteComponent(f, "invalid positions"); } DXEndField(f); DXDelete((Object)afloat); return f; error: DXDelete((Object)afloat); DXFreeInvalidComponentHandle(icHandle); return NULL; } static Array Include_Array(Array a, struct argblk b) { int items; Array na = NULL; Array afloat = NULL; float tmin, tmax; float *fp; Pointer dp, odp; int i, j, bytes, id; Type t; Category c; int rank, shape[32]; if (!DXGetArrayInfo(a, &items, &t, &c, &rank, shape)) return NULL; /* do some simple checks first - if there is going to be no data, * or all the data will remain, then there is no need to look at * all the points. */ if (b.shape == 1) { if (!DXStatistics((Object)a, "data", &tmin, &tmax, NULL, NULL)) return NULL; /* include and all points in range, or exclude & all out of range. * keep all data, which means returning the input array is fine. */ if ((!b.exclude && (tmin >= *b.min && tmax <= *b.max)) || (b.exclude && (tmin > *b.max || tmax < *b.min))) return a; /* include and all points out of range, or exclude & all in range. * return an empty array of the same type as the input. */ if ((!b.exclude && (tmax < *b.min || tmin > *b.max)) || (b.exclude && (tmin >= *b.min && tmax <= *b.max))) return DXNewArrayV(t, c, rank, shape); /* convert the data to scalar float if it is anything else. * this is the same routine the statistics code uses. */ if (!DXTypeCheck(a, TYPE_FLOAT, CATEGORY_REAL, 0)) { if ((afloat = DXScalarConvert(a)) == NULL) return NULL; if(!(dp = DXGetArrayData(afloat))) goto error; } else { if(!(dp = DXGetArrayData(a))) goto error; } } else { /* shape != 1, can't do simple rejection with statistics */ if (!DXTypeCheck(a, TYPE_FLOAT, CATEGORY_REAL, 1, b.shape)) { if ((afloat = DXArrayConvert(a, TYPE_FLOAT, CATEGORY_REAL, 1, b.shape)) == NULL) return NULL; if(!(dp = DXGetArrayData(afloat))) goto error; } else { if(!(dp = DXGetArrayData(a))) goto error; } } id = 0; odp = DXGetArrayData(a); /* this expands the input if it's regular */ bytes = DXGetItemSize(a); na = DXNewArrayV(t, c, rank, shape); if (!odp || !na || bytes <= 0) goto error; /* now iterate through data. if the data is in range copy it to the output * array for include, out of range for exclude. */ if (!b.exclude) { if (b.shape == 1) { for (i=0, fp=(float *)dp; i= *b.min && *fp <= *b.max) DXAddArrayData(na, id++, 1, (Pointer)((char *)odp + i*bytes)); } } else { for (i=0, fp=(float *)dp; i *(b.max+j)) break; } if (j == b.shape) DXAddArrayData(na, id++, 1, (Pointer)((char *)odp + i*bytes)); } } } else { /* exclude */ if (b.shape == 1) { for (i=0, fp=(float *)dp; i *b.max) DXAddArrayData(na, id++, 1, (Pointer)((char *)odp + i*bytes)); } } else { for (i=0, fp=(float *)dp; i= *(b.min+j) && *(fp+j) <= *(b.max+j)) break; } if (j == b.shape) DXAddArrayData(na, id++, 1, (Pointer)((char *)odp + i*bytes)); } } } done: return na; error: DXDelete((Object)afloat); DXDelete((Object)na); return NULL; } static Error VectorLength(Object o, int *len) { int nitems; int rank, shape[100]; Type t; Category c; if (DXGetObjectClass(o) != CLASS_ARRAY) return ERROR; if (!DXGetArrayInfo((Array)o, &nitems, &t, &c, &rank, shape)) return ERROR; if (nitems > 1 || c != CATEGORY_REAL || rank >=2) return ERROR; if (rank == 0) shape[0] = 1; if (len) *len = shape[0]; return OK; } static Error VectorExtract(Object o, int len, float *ptr) { Array a = NULL; int i; float *fp; if (!(a = DXArrayConvert((Array)o, TYPE_FLOAT, CATEGORY_REAL, 1, len))) return ERROR; fp = (float *)DXGetArrayData(a); if (!fp) goto error; for (i=0; i