/***********************************************************************/ /* 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 #define Return_Internal_Error \ DXSetError(ERROR_INTERNAL, "at file %s, line %d", __FILE__, __LINE__); \ return ERROR /******* the general idea: 1) what is this object? if a group, list some of the member names ( < 10? ) if a series, list some of the series pos (") if a list, list some of the values (") 2) if this is a potentially renderable thing (field, group, etc) what is the bbox? what is the data max/min? how many data values, what kind? what kind of connections are there? Ndim? *** what kind of colors (float/byte, full list/delayed) *** 3) is it renderable at this point? if not, why not? if so, what is going to be rendered? *******/ /* reasons why an object might not be renderable. */ struct renderinfo { int nfields; /* need to be more than one to be renderable */ int nlights; /* if > 1, default lighting disabled */ int badobjs; /* if > 1, will get error from render */ Class *badclass; /* list of bad obj types */ int badpos; /* pos <= 0 or > 3d, error */ int nocolors; /* if missing colors, error from render */ }; struct nopicture { int nodep; /* no errors, but no picture either */ int novalid; int nopos; /* no positions component (empty field) */ }; struct nD { int c0; /* types of connections in the input */ int c1; int c2; int c3; /* points, lines, surfaces, volumes */ int cplus; /* more than 3d connections */ int cbad; /* an unrecognized type or malformed component */ int cmissing; /* missing elem type attribute */ }; struct nColors { int cubyte; /* types of color representations in the input */ int cfloat; int cbytemap; int cunknown; }; struct info { struct renderinfo ri; struct nopicture np; struct nD nd; struct nColors nc; }; static Error saytype(Object o, char *opt); static Error saydata(Object o, char *opt); static Error sayrender(Object o, char *opt); static Error sayclass(Object o, char *intro, int descend); static Error saymembers(Group g); static Error traverse(Object o, struct info *ip, int nosee); static Error validfield(Field f, struct info *ip, int nosee); #if 0 static int queryNDconnections(Field f, int n); #endif static Error validcount(Field f, char *component, int *icount); #if 0 static Error atleast1field(Object o); static Error atleast1image(Object o); static Error atleast1contype(Object o, int n); #endif static int qimage(Object o); #define MEMBERCOUNT 10 /* print info about this many members */ /* figure a way to add a constant string to the front of this */ #define DescribeMsg DXMessage /* 0 = input * 1 = string flags: "structure", "details", "render", "all" * (default "all") * * which mean: tell me about the input structure * tell me about the geometry (positions, connections, bbox) * tell me about the renderability */ int m_Describe(Object *in, Object *out) { char *opt = "all"; if (!in[0]) { DescribeMsg("Object Description:"); DescribeMsg("No input object was specified, or the input object is NULL."); return OK; } if (in[1] && !DXExtractString(in[1], &opt)) { DXErrorReturn(ERROR_BAD_PARAMETER, "Options string must be one of 'structure', `details', `render', or `all'"); } /* ??? i should lcase opt, but have to make a copy first. */ if (strcmp(opt, "all") && strcmp(opt, "structure") && strcmp(opt, "details") && strcmp(opt, "render")) { DXErrorReturn(ERROR_BAD_PARAMETER, "Options string must be one of 'structure', `details', `render', or `all'"); } DXBeginLongMessage(); if (!strcmp(opt, "all")) DescribeMsg("Object Description:\n"); if (!strcmp(opt, "all") || !strcmp(opt, "structure")) { if (!strcmp(opt, "structure")) DescribeMsg("Object Structure:\n"); if (!saytype(in[0], opt)) goto done; } if (!strcmp(opt, "all") || !strcmp(opt, "details")) { if (!strcmp(opt, "details")) DescribeMsg("Object Details:\n"); if (!saydata(in[0], opt)) goto done; } if (!strcmp(opt, "all") || !strcmp(opt, "render")) { if (!strcmp(opt, "render")) DescribeMsg("Object Renderability:\n"); if (!sayrender(in[0], opt)) goto done; } DescribeMsg("\n"); DXEndLongMessage(); done: return (DXGetError()==ERROR_NONE ? OK : ERROR); } #define IS "is a" #define IS_V "is an" static Error saytype(Object o, char *opt) { return sayclass(o, "Input object", 1); } static Error sayclass(Object o, char *intro, int descend) { int count; int i; switch (DXGetObjectClass(o)) { case CLASS_GROUP: if (!DXGetMemberCount((Group)o, &count)) return ERROR; switch(DXGetGroupClass((Group)o)) { case CLASS_GROUP: DescribeMsg("%s %s Group which contains %d member%s.\n", intro, IS, count, (count==1 ? "" : "s")); break; case CLASS_COMPOSITEFIELD: DescribeMsg("%s %s Partitioned Field which contains %d partition%s.\n", intro, IS, count, (count==1 ? "" : "s")); break; case CLASS_MULTIGRID: DescribeMsg("%s %s Multigrid Group which contains %d member%s.\n", intro, IS, count, (count==1 ? "" : "s")); break; case CLASS_SERIES: DescribeMsg("%s %s Series Group which contains %d series member%s.\n", intro, IS, count, (count==1 ? "" : "s")); DescribeMsg("To work with one member at a time use the `Select' module\n"); break; default: DescribeMsg("%s %s Group Object but of unrecognized Group Type.\n", intro, IS); break; } if (descend) saymembers((Group)o); break; case CLASS_FIELD: DescribeMsg("%s %s Field, the basic data carrying structure in DX.\n", intro, IS); break; case CLASS_ARRAY: DescribeMsg("%s %s Array Object, containing a list of data values.\n", intro, IS_V); switch (DXGetArrayClass((Array)o)) { case CLASS_ARRAY: break; case CLASS_PRODUCTARRAY: case CLASS_MESHARRAY: case CLASS_PATHARRAY: case CLASS_CONSTANTARRAY: case CLASS_REGULARARRAY: DescribeMsg("These values are stored in a compact format to save space.\n"); break; default: DescribeMsg("The Array is of an unrecognized type.\n"); break; } /* ??? add code here ??? - print the first N values? */ break; case CLASS_STRING: DescribeMsg("%s %s String, a list of character values\n", intro, IS); DescribeMsg("It contains the value `%s'\n", DXGetString((String)o)); break; case CLASS_CAMERA: DescribeMsg("%s %s Camera, used to set the viewpoint for Rendering.\n", intro, IS); break; case CLASS_XFORM: DescribeMsg("%s %s Transformed object, meaning that a transformation will be applied to the final object before rendering.\n", intro, IS); /* sayclass of xformed obj? */ break; case CLASS_OBJECT: DescribeMsg("%s %s Generic Object, of unrecognized type.\n", intro, IS); break; case CLASS_LIGHT: DescribeMsg("%s %s Light, used to set the Lighting parameters for Rendering.\n", intro, IS); break; case CLASS_CLIPPED: DescribeMsg("%s %s Clipped Object, which means that at Rendering time part of the object will be invisible.\n", intro, IS); /* sayclass of clipped obj? */ break; case CLASS_INTERPOLATOR: DescribeMsg("%s %s Interpolator. Internal use only.\n", intro, IS); break; case CLASS_SCREEN: DescribeMsg("%s %s Screen Object, meaning this object stays aligned with the screen.\n", intro, IS); /* say something about the screen obj? */ break; case CLASS_MAX: DescribeMsg("Unrecognized Object. Object type above Class Max.\n"); break; case CLASS_MIN: DescribeMsg("Unrecognized Object. Object type below Class Min.\n"); break; case CLASS_PRIVATE: DescribeMsg("%s %s Private object, defined by a user.\n", intro, IS); break; case CLASS_DELETED: DescribeMsg("%s %s Deleted Object. Should not happen.\n", intro, IS); break; default: DescribeMsg("%s %s Unrecognized object. Bad return from GetObjectType().\n", intro, IS); break; } return OK; } /* print something about each member, up to MEMBERCOUNT items */ static Error saymembers(Group g) { int i, count; char *name; Object subo, firstsubo; int same; Class grouptype; float position; if (!DXGetMemberCount(g, &count)) return ERROR; /* no members, nothing to do here */ if (count <= 0) return OK; /* save group type; we'll use it later on */ grouptype = DXGetGroupClass(g); switch (grouptype) { case CLASS_GROUP: if (count <= MEMBERCOUNT) { for (i=0; i 0) { /* ??? call vectorstats here if rank >= 1 * look at the code in histogram; does rank=1, nothing * about larger. */ DescribeMsg("(These are the scalar statistics which will be used by modules\n"); DescribeMsg("which need scalar values. The length is computed for vectors and\n"); DescribeMsg("the determinant for matricies.)\n"); } } return OK; } #define NOTREADY "Input is not ready to be rendered because" #define READY "Input is ready to be rendered" static Error sayrender(Object o, char *opt) { struct info info; int i; /* zero out the struct */ memset((char *)&info, '\0', sizeof(struct info)); /* and fill it with info */ if (!traverse(o, &info, 0)) goto error; /* sort out what we've found. first report on things which * will prevent rendering. */ if (info.ri.badpos > 0) { DescribeMsg("%s %d Field(s) in the Input object have bad positions (less than 1D or greater than 3D).\n", NOTREADY, info.ri.badpos); DescribeMsg("If > 3D, use the `Slice' module or `Mark' and `Compute' to reduce them to 3D or less.\n"); goto done; } if (info.ri.nocolors > 0) { if (info.ri.nocolors == 1) DescribeMsg("%s the Field does not have colors yet.\n", NOTREADY); else DescribeMsg("%s %d Fields in the Input object do not have colors yet.\n", NOTREADY, info.ri.nocolors); DescribeMsg("Use the `AutoColor', `AutoGreyScale', or `Color' modules to add colors.\n"); goto done; } if (info.np.nodep > 0) { DescribeMsg("%s the `colors' component does not have a `dep' attribute\n", NOTREADY); DescribeMsg("to indicate whether they are per vertex or per cell\n"); DescribeMsg("Use the modules in the Structuring category to add the attribute.\n"); goto done; } if (info.nc.cunknown > 0) { DescribeMsg("%s the Input contained Fields with an unsupported `color' component format.\n", NOTREADY); DescribeMsg("Use the `Color', `AutoColor', or `AutoGreyScale' to create a valid `color' component.\n"); goto done; } /* warn about empty fields and fields were all items are invalid */ if (info.np.novalid > 0) { if (info.ri.nfields == 0) { DescribeMsg("%s no valid items were found in any of the Field(s) in the input.\n", NOTREADY); goto done; } else { DescribeMsg("%d Fields(s) contain no valid items and will be ignored.\n", info.np.novalid); } } if (info.np.nopos > 0) { if (info.ri.nfields == 0) { DescribeMsg("%s none of the Field(s) in the Input object have a `positions' component.\n", NOTREADY); DescribeMsg("Fields without positions are intentionally ignored without error by almost all modules.\n"); DescribeMsg("Use the `Mark' or `Replace' modules to create positions.\n"); goto done; } else { DescribeMsg("%d Field(s) contain no positions and will be ignored.\n", info.np.nopos); } } if (info.ri.badobjs > 0) { for (i=0; i 0) { if (info.nd.cbad == 1) DescribeMsg("%s the Field has a bad `connections' component.\n", NOTREADY); else DescribeMsg("%s %d Fields in the Input object have a bad `connections' component.\n", NOTREADY, info.nd.cbad); goto done; } if (info.nd.cmissing > 0) { if (info.nd.cmissing == 1) DescribeMsg("%s the Field has no `element type' attribute on the `connections' component.\n", NOTREADY); else DescribeMsg("%s %d Fields have no `element type' attribute on the `connections' component.\n", NOTREADY, info.nd.cmissing); DescribeMsg("The renderer must know what type of geometry is in each Field (e.g. `triangles', `lines').\n"); DescribeMsg("This attribute can be added using the modules in the Structuring category.\n"); goto done; } if (info.nd.cplus > 0) { if (info.nd.cplus == 1) DescribeMsg("%s the Field has a `connections' component of more than 3D.\n", NOTREADY); else DescribeMsg("%s %d Fields have a `connections' component of more than 3D.\n", NOTREADY, info.nd.cplus); DescribeMsg("The `Slice' module can be used to reduce the input to 3D or lower.\n"); goto done; } /* ok, we've checked for bad things. how about nothing? */ if (info.ri.nfields <= 0) { DescribeMsg("There is nothing to render in the input because it does not contain any Field objects.\n"); DescribeMsg("Field objects are usually created by the `Import' module\n"); DescribeMsg("or by the modules in the `Realization' category.\n"); goto done; } /* ok, praps it can be rendered after all */ /* warn about disabling the default lighting. */ if (info.ri.nlights > 0) DescribeMsg("%d Light%s found in the Input; the default lighting will not be used.\n", info.ri.nlights, info.ri.nlights > 1 ? "s were" : " was"); /* if already image, suggest Display (w/no camera???) */ if (qimage(o)) { DescribeMsg("Input is already an Image. It can be displayed fastest using the `Display' module with no `Camera' input.\n"); DescribeMsg("Use the `Image' module, which will be slower, if you want to resize the image, rotate it, zoom in or out,\n"); DescribeMsg("or combine the image with other renderable items.\n"); } /* connections? points, lines, surfaces, volumes */ if (info.nd.c0 > 0) { DescribeMsg("Input contains a set of scattered points. They will be rendered\n"); DescribeMsg("as individual pixels. Use the `AutoGlyph' or `Glyph'\n"); DescribeMsg("modules to make larger objects, or use the `Connect' module\n"); DescribeMsg("for 2D data or the `AutoRegrid' or `Regrid' modules for 3D data.\n"); } /* lines? */ if (info.nd.c1 > 0) { DescribeMsg("Input contains a set of lines. They will be rendered as a single\n"); DescribeMsg("pixel-wide line. Use the `Tube' or `Ribbon' modules\n"); DescribeMsg("to make larger objects.\n"); } /* surfaces */ if (info.nd.c2 > 0) { DescribeMsg("Input contains a surface. Planar (flat) surfaces exactly aligned\n"); DescribeMsg("along the viewpoint will NOT be visible. To see the\n"); DescribeMsg("surface move the viewpoint slightly off-axis.\n"); /* something about front/back colors and hardware rendering */ } /* volumes */ if (info.nd.c3) { DescribeMsg("Input contains a volume and will be rendered using volume rendering techniques.\n"); DescribeMsg("These are the slowest types of objects to render, and they produce\n"); DescribeMsg("images with `fuzzy cloud' effects. To get a sharp image, convert\n"); DescribeMsg("part of the volume into a surface using the `ShowBoundary',\n"); DescribeMsg("`MapToPlane', or `Isosurface' modules. The software renderer usually\n"); DescribeMsg("gives better results for volumes since most hardware cards do not support volume rendering.\n"); } /* and last, tell them about colors */ if (info.nc.cbytemap > 0) { DescribeMsg("Input contains Fields with Delayed colors. Delayed colors use a 256-entry lookup table of colors,\n"); DescribeMsg("with the data values as the index into the table.\n"); DescribeMsg("Data must be of type `ubyte' to use Delayed colors.\n"); DescribeMsg("Each entry in the lookup table itself is a 3-float triplet specifying the color.\n"); DescribeMsg("Valid color values are between 0 and 1 for each of Red, Green and Blue.\n"); } if (info.nc.cubyte > 0) { DescribeMsg("Input contains Fields with Byte colors. Byte colors use 3-byte triplets per data value.\n"); DescribeMsg("Valid color values are between 0 and 255 for each of Red, Green and Blue.\n"); } if (info.nc.cfloat > 0) { DescribeMsg("Input contains Fields with Float colors. Float colors use 3-float triplets per data value.\n"); DescribeMsg("Valid color values are between 0 and 1 for each of Red, Green and Blue.\n"); } done: DXFree((Pointer)info.ri.badclass); return OK; error: DXFree((Pointer)info.ri.badclass); return ERROR; } #if 0 /* return all fields which aren't empty. */ static Field getpartplus(Object o, int *i, int *count) { int again = 1; Field f; while (again) { f = DXGetPart(o, *i); if (!f) return NULL; (*i)++; if (DXEmptyField(f)) continue; (*count)++; return f; } /* not reached */ } /* unlike Inquire, say yes if you find any */ static int queryNDconnections(Field f, int n) { char *cp; Object con; if (!(con = DXGetComponentValue(f, "connections"))) { if (n == 0) return 1; return 0; } if (DXGetObjectClass(con) != CLASS_ARRAY) return 0; if (!DXGetStringAttribute(con, "element type", &cp)) return 0; if (!strcmp(cp, "lines") && (n == 1)) return 1; if ((!strcmp(cp, "triangles") || !strcmp(cp, "quads")) && (n == 2)) return 1; if ((!strcmp(cp, "tetrahedra") || !strcmp(cp, "cubes")) && (n == 3)) return 1; if (!strncmp(cp, "cubes", 5) && (strlen(cp) > 5) && (n > 3)) return 1; return 0; } #endif /* return how many items in this field are valid */ static Error validcount(Field f, char *component, int *icount) { Array a = NULL; char *dep = NULL; char *invalid = NULL; InvalidComponentHandle ih; /* assume none and be happily surprised when it finds some */ *icount = 0; /* this must be called at the field level. if you want to recurse * through a hierarchy, do the recursion above this subroutine. */ if (DXGetObjectClass((Object)f) != CLASS_FIELD) { Return_Internal_Error; } a = (Array)DXGetComponentValue(f, component); if (!a) { DXSetError(ERROR_INVALID_DATA, "field has no `%s' component", component); return ERROR; } /* follow a dependency. */ if (!DXGetStringAttribute((Object)a, "dep", &dep)) { DXSetError(ERROR_INVALID_DATA, "The `%s' component does not have a `dep' attribute", component); return ERROR; } if (!DXGetArrayInfo(a, icount, NULL, NULL, NULL, NULL)) return ERROR; #define INVLEN 10 /* strlen("invalid "); */ if (!(invalid = (char *)DXAllocate(strlen(dep) + INVLEN))) return ERROR; strcpy(invalid, "invalid "); strcat(invalid, dep); /* if no invalid component, all valid */ if (!DXGetComponentValue(f, invalid)) { DXFree((Pointer)invalid); return OK; } DXFree((Pointer)invalid); ih = DXCreateInvalidComponentHandle((Object)f, NULL, dep); if (!ih) return ERROR; *icount = DXGetValidCount(ih); DXFreeInvalidComponentHandle(ih); return OK; } /* make sure the field contains colors and that > 1 are valid. * i guess we can look to see that the positions are 1D <= p <= 3D * as well. and we could look at connections here as well. */ static Error validfield(Field f, struct info *ip, int nosee) { int icount = 0; Array colors, pos, conn; char *elem = NULL; char *dep = NULL; char *invalid = NULL; InvalidComponentHandle ih; /* check for fields - recurse above this */ if (DXGetObjectClass((Object)f) != CLASS_FIELD) { Return_Internal_Error; } /* check pos first */ pos = (Array)DXGetComponentValue(f, "positions"); if (!pos) { ip->np.nopos++; return OK; } if (!DXGetArrayInfo(pos, &icount, NULL, NULL, NULL, NULL)) return ERROR; if (icount <= 0) { ip->np.nopos++; return OK; } /* pos must be vector, rank 1, 1 <= shape <= 3 */ if ((!DXTypeCheck(pos, TYPE_FLOAT, CATEGORY_REAL, 1, 1)) && (!DXTypeCheck(pos, TYPE_FLOAT, CATEGORY_REAL, 1, 2)) && (!DXTypeCheck(pos, TYPE_FLOAT, CATEGORY_REAL, 1, 3))) { ip->ri.badpos++; return OK; } /* check Nd of conn here. if "nosee" is true, don't count the * Nd of the connections because normal modules won't "see" these * as normal fields. */ if (!nosee) { conn = (Array)DXGetComponentValue(f, "connections"); if (!conn) ip->nd.c0++; else if (DXGetObjectClass((Object)conn) != CLASS_ARRAY) ip->nd.cbad++; else if (!DXGetStringAttribute((Object)conn, "element type", &elem)) ip->nd.cmissing++; else if (!strcmp(elem, "lines")) ip->nd.c1++; else if (!strcmp(elem, "triangles") || !strcmp(elem, "quads")) ip->nd.c2++; else if (!strcmp(elem, "tetrahedra") || !strcmp(elem, "cubes")) ip->nd.c3++; else if (!strncmp(elem, "cubes", 5) && (strlen(elem) > 5)) ip->nd.cplus++; else ip->nd.cbad++; } /* expect colors and follow dependency */ colors = (Array)DXGetComponentValue(f, "colors"); if (!colors) { ip->ri.nocolors++; return OK; } else { /* check for colormap component */ if (DXGetComponentValue(f, "color map") != NULL) ip->nc.cbytemap++; else if (DXTypeCheck(colors, TYPE_UBYTE, CATEGORY_REAL, 1, 3)) ip->nc.cubyte++; else if (DXTypeCheck(colors, TYPE_FLOAT, CATEGORY_REAL, 1, 3)) ip->nc.cfloat++; else ip->nc.cunknown++; } /* follow colors dependency. it will be unusual to find no dep. */ if (!DXGetStringAttribute((Object)colors, "dep", &dep)) { ip->np.nodep++; return OK; } if (!DXGetArrayInfo(colors, &icount, NULL, NULL, NULL, NULL)) return ERROR; if (icount == 0) { ip->np.novalid++; return OK; } #define INVLEN 10 /* strlen("invalid "); */ if (!(invalid = (char *)DXAllocate(strlen(dep) + INVLEN))) return ERROR; strcpy(invalid, "invalid "); strcat(invalid, dep); /* if no invalid component, all valid */ if (!DXGetComponentValue(f, invalid)) { DXFree((Pointer)invalid); ip->ri.nfields++; return OK; } DXFree((Pointer)invalid); ih = DXCreateInvalidComponentHandle((Object)f, NULL, dep); if (!ih) return ERROR; icount = DXGetValidCount(ih); DXFreeInvalidComponentHandle(ih); if (icount == 0) { ip->np.novalid++; return OK; } ip->ri.nfields++; return OK; } /* accumulate info about what's what */ static Error traverse(Object o, struct info *ip, int nosee) { Object subo, old; Class curclass; int i; switch (curclass = DXGetObjectClass(o)) { case CLASS_FIELD: return validfield((Field)o, ip, nosee); case CLASS_GROUP: /* traverse members */ for (i=0; subo = DXGetEnumeratedMember((Group)o, i, NULL); i++) { if (!traverse(subo, ip, nosee)) return ERROR; } break; case CLASS_SCREEN: if (!DXGetScreenInfo((Screen)o, &subo, NULL, NULL)) return ERROR; return traverse(subo, ip, 1); case CLASS_XFORM: if (!DXGetXformInfo((Xform)o, &subo, NULL)) return ERROR; return traverse(subo, ip, nosee); case CLASS_CLIPPED: if (!DXGetClippedInfo((Clipped)o, &subo, NULL)) return ERROR; return traverse(subo, ip, nosee); /* is this right? */ case CLASS_LIGHT: ip->ri.nlights++; break; case CLASS_INTERPOLATOR: case CLASS_MAX: case CLASS_MIN: case CLASS_PRIVATE: case CLASS_DELETED: case CLASS_STRING: case CLASS_ARRAY: case CLASS_CAMERA: default: for (i=0; iri.badobjs; i++) { if (ip->ri.badclass[i] == curclass) return OK; } ip->ri.badobjs++; ip->ri.badclass = (Class *)DXReAllocate((Pointer)ip->ri.badclass, sizeof(Class) * ip->ri.badobjs); ip->ri.badclass[ip->ri.badobjs - 1] = curclass; break; } return OK; } static int qimage(Object o) { int n; Array a; char *s; /* an image has got to be a field or a composite field */ if (DXGetObjectClass(o) != CLASS_FIELD) { if (DXGetObjectClass(o) != CLASS_GROUP) return 0; if (DXGetGroupClass((Group)o) != CLASS_COMPOSITEFIELD) return 0; o = DXGetEnumeratedMember((Group)o, 0, NULL); } /* check to see if it's the right shape, etc */ /* check positions */ a = (Array) DXGetComponentValue((Field)o, "positions"); if (!a || !DXQueryGridPositions(a, &n, NULL, NULL, NULL) || n!=2) return 0; /* check connections */ a = (Array) DXGetComponentValue((Field)o, "connections"); if (!a || !DXQueryGridConnections(a, &n, NULL) || n!=2) return 0; /* is there a colors component */ a = (Array) DXGetComponentValue((Field)o, "colors"); if (!a) return 0; /* check dep */ s = DXGetString((String)DXGetAttribute((Object)a, "dep")); if (s && strcmp(s, "positions")) return 0; return 1; } #if 0 static Error atleast1field(Object o) { Object subo, old; int i; switch (DXGetObjectClass(o)) { case CLASS_FIELD: return OK; case CLASS_GROUP: /* traverse members */ for (i=0; subo = DXGetEnumeratedMember((Group)o, i, NULL); i++) if (atleast1field(subo)) return OK; break; case CLASS_SCREEN: if (!DXGetScreenInfo((Screen)o, &subo, NULL, NULL)) return ERROR; return atleast1field(subo); case CLASS_XFORM: if (!DXGetXformInfo((Xform)o, &subo, NULL)) return ERROR; return atleast1field(subo); case CLASS_CLIPPED: if (!DXGetClippedInfo((Clipped)o, &subo, NULL)) return ERROR; return atleast1field(subo); default: break; } return ERROR; } static Error atleast1image(Object o) { Object subo, old; int i; switch (DXGetObjectClass(o)) { case CLASS_FIELD: if (qimage(o)) return OK; break; case CLASS_GROUP: /* traverse members */ for (i=0; subo = DXGetEnumeratedMember((Group)o, i, NULL); i++) if (atleast1image(subo)) return OK; break; case CLASS_SCREEN: if (!DXGetScreenInfo((Screen)o, &subo, NULL, NULL)) return ERROR; return atleast1image(subo); case CLASS_XFORM: if (!DXGetXformInfo((Xform)o, &subo, NULL)) return ERROR; return atleast1image(subo); case CLASS_CLIPPED: if (!DXGetClippedInfo((Clipped)o, &subo, NULL)) return ERROR; return atleast1image(subo); default: break; } return ERROR; } static Error atleast1contype(Object o, int n) { Object subo, old; int i; switch (DXGetObjectClass(o)) { case CLASS_FIELD: if (queryNDconnections((Field)o, n)) return OK; break; case CLASS_GROUP: /* traverse members */ for (i=0; subo = DXGetEnumeratedMember((Group)o, i, NULL); i++) if (atleast1contype(subo, n)) return OK; break; case CLASS_SCREEN: /* these don't act like other objs; don't count them for this */ break; case CLASS_XFORM: if (!DXGetXformInfo((Xform)o, &subo, NULL)) return ERROR; return atleast1contype(subo, n); case CLASS_CLIPPED: if (!DXGetClippedInfo((Clipped)o, &subo, NULL)) return ERROR; return atleast1contype(subo, n); default: break; } return ERROR; } #endif