/***********************************************************************/ /* 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 "objectClass.h" #include "internals.h" /* * DXBoundingBox returns a bounding box of an object. The box is not * necessarily aligned to world coordinates if a transform has been applied. * * The top level bounding box routine just calls _dxf_BoundingBox with * a null transform; the transform argument is for propagating the * transforms downward. */ #define VALID_POSITIONS_ONLY 1 #define ALL_POSITIONS 0 Object DXBoundingBox(Object o, Point *box) { if (DXGetError()!=ERROR_NONE) DXErrorReturn(ERROR_INTERNAL, "DXBoundingBox called with error code set"); return _dxfBoundingBox(o, box, NULL, ALL_POSITIONS); } Object DXValidPositionsBoundingBox(Object o, Point *box) { if (DXGetError()!=ERROR_NONE) DXErrorReturn(ERROR_INTERNAL, "DXBoundingBox called with error code set"); return _dxfBoundingBox(o, box, NULL, VALID_POSITIONS_ONLY); } /* * compute a bounding box from a set of points */ static expand(float *out, int dim, int nboxpts, float *min, float *max) { int i, j, k, m, n; n = dim * nboxpts; for (i=0, m=n/2; i max[j]) max[j] = in[i+j]; } } } } else { n = dim * npoints; for (i=0; i max[j]) max[j] = in[i+j]; } } } /* expand min/max out to nboxpts corners */ expand(out, dim, nboxpts, min, max); } /* * compute a bounding box from a group */ Object _dxfGroup_BoundingBox(Group g, Point *box, Matrix *m, int validity) { Object o; int i, j, k, n=0, align=0; Point min, max, p; Matrix mat, inv; /* compute the collective min/max of components */ for (i=0; o=DXGetEnumeratedMember(g, i, NULL); i++) { /* get the component bounding box */ if (!_dxfBoundingBox(o, box, m, validity)) { if (DXGetError()!=ERROR_NONE) return NULL; /* a real error occured */ continue; /* or, the field had no bounding box */ } /* * Align group box to first box. We do this by considering * box[0] to be the origin of a coordinate system and box[1]-box[0], * box[2]-box[0] and box[4]-box[0] to be the axes. We transform * all the bounding boxes into this coordinate system and form * a bounding box there, and then transform that bounding box back * to the original coordinate system when we're done. * * Modification: only do this if all dimensions are non-zero. */ if (box) { if (n==0) { Vector v1, v2, v4; float l1, l2, l4; v1 = DXSub(box[1], box[0]), l1 = fabs(DXLength(v1)); v2 = DXSub(box[2], box[0]), l2 = fabs(DXLength(v2)); v4 = DXSub(box[4], box[0]), l4 = fabs(DXLength(v4)); /* XXX - use an epsilon here */ if (l1!=0.0 && l2!=0.0 && l4!=0.0) { *(Point*) mat.A[0] = v1; *(Point*) mat.A[1] = v2; *(Point*) mat.A[2] = v4; *(Point*) mat.b = box[0]; inv = DXInvert(mat); align = 1; } min.x = min.y = min.z = DXD_MAX_FLOAT; max.x = max.y = max.z = -DXD_MAX_FLOAT; } for (j=0; j<8; j++) { p = align? DXApply(box[j],inv) : box[j]; if (p.xmax.x) max.x = p.x; if (p.ymax.y) max.y = p.y; if (p.zmax.z) max.z = p.z; } n++; } } /* * DXTransform the bounding box back to the original * coordinate system (see above). */ if (box) { if (n==0) return NULL; for (i=0; i<8; i++) { p = DXPt(i&1?max.x:min.x, i&2?max.y:min.y, i&4?max.z:min.z); box[i] = align? DXApply(p, mat) : p; } } return (Object) g; } Object _dxfCompositeField_BoundingBox(Group g, Point *box, Matrix *m, int validity) { Object o; int i, j, k, n=0; Point min, max, *p; min.x = min.y = min.z = DXD_MAX_FLOAT; max.x = max.y = max.z = -DXD_MAX_FLOAT; /* compute the collective min/max of components */ for (i=0; o=DXGetEnumeratedMember(g, i, NULL); i++) { /* get the component bounding box */ if (!_dxfBoundingBox(o, box, NULL, validity)) { if (DXGetError()!=ERROR_NONE) return NULL; /* a real error occured */ continue; /* or, the field had no bounding box */ } /* * Align group box to first box. We do this by considering * box[0] to be the origin of a coordinate system and box[1]-box[0], * box[2]-box[0] and box[4]-box[0] to be the axes. We transform * all the bounding boxes into this coordinate system and form * a bounding box there, and then transform that bounding box back * to the original coordinate system when we're done. * * Modification: only do this if all dimensions are non-zero. */ if (box) { for (j=0, p=box; j<8; j++, p++) { if (p->xx; if (p->x>max.x) max.x = p->x; if (p->yy; if (p->y>max.y) max.y = p->y; if (p->zz; if (p->z>max.z) max.z = p->z; } n++; } } /* * DXTransform the bounding box back to the original * coordinate system (see above). */ if (box) { if (n==0) return NULL; for (i=0; i<8; i++) { if (m) { box[i] = DXApply(DXPt(i&1?max.x:min.x, i&2?max.y:min.y, i&4?max.z:min.z), *m); } else { box[i] = DXPt(i&1?max.x:min.x, i&2?max.y:min.y, i&4?max.z:min.z); } } } return (Object) g; } /* * compute a bounding box from a field */ static Error _copyout(Point *out, Matrix *m, Array a) { int i, rank, dim; float *in; if (!DXGetArrayInfo(a, NULL, NULL, NULL, &rank, &dim)) return NULL; if (rank==0) dim = 1; in = (float *) DXGetArrayData(a); if (!in) return NULL; if (dim==3) { if (m) for (i=0; i<8; i++) out[i] = DXApply(((Point*)in)[i], *m); else for (i=0; i<8; i++) out[i] = ((Point*)in)[i]; } else if (dim==2) { if (m) for (i=0; i<4; i++) out[2*i] = out[2*i+1] = DXApply(DXPt(in[2*i], in[2*i+1], 0), *m); else for (i=0; i<4; i++) out[2*i] = out[2*i+1] = DXPt(in[2*i], in[2*i+1], 0); } else if (dim==1) { if (m) for (i=0; i<2; i++) out[4*i] = out[4*i+1] = out[4*i+2] = out[4*i+3] = DXApply(DXPt(in[i],0,0), *m); else for (i=0; i<2; i++) out[4*i] = out[4*i+1] = out[4*i+2] = out[4*i+3] = DXPt(in[i],0,0); } else { DXSetError(ERROR_BAD_PARAMETER, "positions must be 3-dimensional"); return NULL; } return OK; } /* * Check or figure out dimensionality */ static Error check(Array a, int *dim, int *nboxpts) { Type type; Category category; int rank, i; /* type check */ if (!DXGetArrayInfo(a, NULL, &type, &category, &rank, NULL)) return ERROR; if (type!=TYPE_FLOAT || category!=CATEGORY_REAL || (rank!=0 && rank!=1)) DXErrorReturn(ERROR_BAD_TYPE, "bad positions/box type in bounding box"); /* get info */ if (rank==0) *dim = 1; else if (!DXGetArrayInfo(a, NULL, NULL, NULL, NULL, dim)) return NULL; *nboxpts = 1; for (i=0; i<*dim; i++) *nboxpts *= 2; return OK; } /* XXX */ #define MAX_DIMS 25 Object _dxfField_BoundingBox(Field f, Point *box, Matrix *m, int validity) { Array points_array, box_array; float *in, *out; int i, j, k, dim, nboxpts, npoints, n, counts[MAX_DIMS]; float origins[MAX_DIMS], deltas[MAX_DIMS*MAX_DIMS]; float min[MAX_DIMS], max[MAX_DIMS]; InvalidComponentHandle ich = NULL; int do_invalids = 0; /* * If its an empty field, then return NULL but no error. */ if (DXEmptyField(f)) goto null_return; /* points_array XXX return NULL if no points, but not an error */ points_array = (Array) DXGetComponentValue(f, POSITIONS); if (!points_array) goto null_return; if (validity == VALID_POSITIONS_ONLY) { int invalids_present = (int)DXGetComponentValue(f, "invalid positions"); /* * if theres a valid_box component, or there are no invalids and * there is a box component, then just return it, applying * transform if any */ box_array = (Array) DXGetComponentValue(f, VBOX); if (! box_array && ! invalids_present) box_array = (Array)DXGetComponentValue(f, BOX); if (box_array && box) goto done; if (invalids_present) { ich = DXCreateInvalidComponentHandle((Object)f, NULL, "positions"); if (! ich) goto null_return; do_invalids = 1; } else { validity = ALL_POSITIONS; do_invalids = 0; } } else if (NULL != (box_array = (Array)DXGetComponentValue(f, BOX))) goto done; /* get dim, nboxpts */ if (!check(points_array, &dim, &nboxpts)) goto null_return; if (dim>MAX_DIMS) { DXSetError(ERROR_BAD_PARAMETER, "dimensionality of object is too high"); goto null_return; } DXGetArrayInfo(points_array, &npoints, NULL, NULL, NULL, NULL); if (npoints==0 || (do_invalids && npoints == DXGetInvalidCount(ich))) goto null_return; /* * allocate array */ box_array = DXNewArray(TYPE_FLOAT, CATEGORY_REAL, 1, dim); if (!DXAddArrayData(box_array, 0, nboxpts, NULL)) goto null_return; if (do_invalids) { DXSetComponentValue(f, VBOX, (Object)box_array); DXSetAttribute((Object)box_array, DER, (Object)DXMakeStringList(2, POSITIONS, INVALID_POSITIONS)); } else { DXSetComponentValue(f, BOX, (Object)box_array); DXSetAttribute((Object)box_array, DER, O_POSITIONS); } out = (float *) DXGetArrayData(box_array); if (!out) return ERROR; /* is it regular or irregular? */ if (DXQueryGridPositions(points_array, NULL, counts, origins, deltas)) { /* * Check to see if the corners of the box are * valid. If so, we can just use the fast mechanism */ if (do_invalids) { /* * quick out if the corners are valid */ int index, i, j, strides[MAX_DIMS]; for (i = 0; i < dim; i++) { strides[i] = counts[i] - 1; for (j = i+1; j < dim; j++) strides[i] *= counts[j]; } do_invalids = 0; for (i = 0; !do_invalids && i < 1<0) mx += m; else mn += m; } o = origins[i]; min[i] = o + mn; max[i] = o + mx; } /* * expand min/max out to nboxpts corners */ expand(out, dim, nboxpts, min, max); goto done; } } if (DXGetArrayClass(points_array) == CLASS_ARRAY) { in = (float *) DXGetArrayData(points_array); if (!in) return NULL; DXGetArrayInfo(points_array, &npoints, NULL, NULL, NULL, NULL); if (npoints==0) return NULL; /* compute bounding box from points */ bbox(out, in, dim, nboxpts, npoints, ich); } else { int i, j; float min[MAX_DIMS], max[MAX_DIMS], *pt; ArrayHandle handle = DXCreateArrayHandle(points_array); Pointer scratch = DXAllocate(DXGetItemSize(points_array)); if (! handle || ! scratch) { DXFreeArrayHandle(handle); DXFree(scratch); return ERROR; } DXGetArrayInfo(points_array, &npoints, NULL, NULL, NULL, NULL); if (npoints==0) { DXFreeArrayHandle(handle); DXFree(scratch); return NULL; } for (i = 0; i < dim; i++) { min[i] = DXD_MAX_FLOAT; max[i] = -DXD_MAX_FLOAT; } if (ich) for (i = 0; i < npoints; i++) { if (! DXIsElementInvalid(ich, i)) { pt = (float *)DXCalculateArrayEntry(handle, i, scratch); for (j = 0; j < dim; j++) { if (pt[j] < min[j]) min[j] = pt[j]; if (pt[j] > max[j]) max[j] = pt[j]; } } } else for (i = 0; i < npoints; i++) { pt = (float *)DXIterateArray(handle, i, (Pointer)pt, scratch); for (j = 0; j < dim; j++) { if (pt[j] < min[j]) min[j] = pt[j]; if (pt[j] > max[j]) max[j] = pt[j]; } } DXFreeArrayHandle(handle); DXFree(scratch); /* expand min/max out to nboxpts corners */ expand(out, dim, nboxpts, min, max); } done: if (ich) DXFreeInvalidComponentHandle(ich); if (box && !_copyout(box, m, box_array)) goto null_return; return (Object) f; null_return: if (ich) DXFreeInvalidComponentHandle(ich); return NULL; } /* * bounding box of xform node */ Object _dxfXform_BoundingBox(Xform x, Point *box, Matrix *mp, int validity) { Matrix m; Object o; /* get this node's matrix and object */ if (!DXGetXformInfo(x, &o, &m)) return NULL; /* concatenate this node's matrix m with incoming matrix *mp */ if (mp) m = DXConcatenate(m, *mp); /* compute o's bounding box */ if (!_dxfBoundingBox(o, box, &m, validity)) return NULL; return (Object) x; } /* * Bounding box of clipped node. Note: ignore bounding box of * clipping object; because it is generally based on the bounding * box of the transformed clipped object and then has its bbox * recomputed by DXEndField, it will have an unnecessarily large * bounding box; but this is ok, because its bounding box is of no * interest anyway. */ Object _dxfClipped_BoundingBox(Clipped c, Point *box, Matrix *mp, int validity) { Object render; /* use bounding box of render object, ignoring clipping object */ if (!DXGetClippedInfo(c, &render, NULL)) return NULL; if (!_dxfBoundingBox(render, box, mp, validity)) return NULL; return (Object) c; } /* * Screen objects are in completely different coordinates, so * we don't include them in DXBoundingBox. */ Object _dxfScreen_BoundingBox(Screen s, Point *box, Matrix *mp, int validity) { Object o; int fixed, i; if (!DXGetScreenInfo(s, &o, &fixed, NULL)) return NULL; if (fixed==SCREEN_WORLD) { static Point zero = {0, 0, 0}, p; p = mp? DXApply(zero, *mp) : zero; if (box) for (i=0; i<8; i++) box[i] = p; return (Object) s; } else return NULL; }