/***************************************************************************** * "Irit" - the 3d polygonal solid modeller. * * * * Written by: Gershon Elber Ver 0.2, Mar. 1990 * ****************************************************************************** * Data File Interpreter module - handle data files - to/from geom objects * *****************************************************************************/ #ifdef USE_VARARGS #include #else #include #endif /* USE_VARARGS */ #include #include #include #include #include "program.h" #include "cagd_lib.h" #include "allocate.h" #include "attribut.h" #include "dataprsr.h" /* Visible to the other modules. */ #include "geomat3d.h" #include "objects.h" #include "windows.h" #include "graphgen.h" #define UNGET_STACK_SIZE 5 /* Internal stack size. */ typedef enum { /* List of all possible tokens enumerated. */ TOKEN_NONE, TOKEN_OPEN_PAREN, TOKEN_CLOSE_PAREN, TOKEN_E2, TOKEN_P2, TOKEN_E3, TOKEN_P3, TOKEN_NUMBER, TOKEN_STRING, TOKEN_VECTOR, TOKEN_MATRIX, TOKEN_CTLPT, TOKEN_VERTEX, TOKEN_POLYGON, TOKEN_POLYLINE, TOKEN_OBJECT, TOKEN_COLOR, TOKEN_INTERNAL, TOKEN_NORMAL, TOKEN_PLANE, TOKEN_CURVE, TOKEN_SURFACE, TOKEN_OTHER = 100, /* Probably names & numbers. */ TOKEN_EOF = -1 } TokenType; typedef enum { /* Possible error code during data parsing. */ DP_NO_ERR = 0, DP_ERR_NUMBER_EXPECTED, DP_ERR_OPEN_PAREN_EXPECTED, DP_ERR_CLOSE_PAREN_EXPECTED, DP_ERR_LIST_COMP_UNDEF, DP_ERR_UNDEF_EXPR_HEADER, DP_ERR_PT_TYPE_EXPECTED, DP_ERR_OBJECT_EMPTY, DP_ERR_EMPTY_NAME, DP_ERR_OPEN_FAILED, DP_ERR_MIXED_TYPES, DP_STR_NOT_IN_QUOTES, DP_ERR_CAGD_LIB_ERR, DP_WRN_OBJ_NAME_TRUNC = 100 } DataPrsrErrType; static int DPGlblLineCount = 0; /* Used to locate errors in input file. */ static DataPrsrErrType DPGlblParserError = DP_NO_ERR; /* Last err # found. */ static char DPGlblTokenError[LINE_LEN]; /* Last token where error was found. */ static jmp_buf LclLongJumpBuffer; /* Used in error traping. */ static int GlblToken = 0, /* Used by the parser, to unget token. */ GlblLineCount = 1; /* Used to locate errors in input file. */ static char GlblStringToken[UNGET_STACK_SIZE][LINE_LEN];/* Save unget tokens.*/ static FILE *OutputFile = NULL; static void UnGetToken(char *StringToken); static void GetStringToken(FILE *f, char *StringToken); static TokenType GetToken(FILE *f, char *StringToken); static void GetVertexAttributes(VertexStruct *PVertex, FILE *f, int *HasNormal); static void GetPolygonAttributes(PolygonStruct *PPolygon, FILE *f, int *HasPlane); static void GetObjectAttributes(ObjectStruct *PObject, FILE *f); static void GetPointData(FILE *f, PolygonStruct *PPolygon, int IsPolyline, int *HasNormal); static void DPUpdatePolyPlane(PolygonStruct *PPoly); static void ParserError(DataPrsrErrType ErrNum, char *Msg); static void DataPrsrPutAllObjects(ObjectStruct *PObj, int Indent); static char *Real2Str(RealType R); static void DataPrsrGetAllObjects(FILE *f, ObjectStruct *PObjParent); static void GetCloseParenToken(FILE *f); static void GetNumericToken(FILE *f, RealType *r); static void DataPrsrGetAuxObject(FILE *f, ObjectStruct *PObj); #ifdef USE_VARARGS static void IFprintf(int Indent, char *va_alist, ...); #else static void IFprintf(int Indent, char *Format, ...); #endif /* USE_VARARGS */ /***************************************************************************** * Routine to print the data from given object into given file FileName. * * If FileName is NULL or empty, print to screen using WndwInputWindowPutStr. * *****************************************************************************/ void DataPrsrPutObject(char *FileName, ObjectStruct *PObj) { char *Pchar; char FullFileName[LINE_LEN]; DPGlblParserError = DP_NO_ERR; /* If the following gain control and is non zero - its from error! */ if (setjmp(LclLongJumpBuffer) != 0) { if (OutputFile != NULL) fclose(OutputFile); return; } if (FileName && (int) strlen(FileName) > 0) { if ((Pchar = strchr(FileName, '.')) != NULL) *Pchar = 0; /* Make sure no file type is given. */ if (strlen(FileName) == 0) ParserError(DP_ERR_EMPTY_NAME, ""); strcpy(FullFileName, FileName); strcat(FullFileName, ".dat"); if ((OutputFile = fopen(FullFileName, "w")) == NULL) ParserError(DP_ERR_OPEN_FAILED, FullFileName); } else OutputFile = NULL; DataPrsrPutAllObjects(PObj, 0); if (OutputFile) { fclose(OutputFile); OutputFile = NULL; } return; } /***************************************************************************** * Routine to read the data from a given file. * *****************************************************************************/ ObjectStruct *DataPrsrGetObjects(char *FileName) { char FullFileName[LINE_LEN]; FILE *f = NULL; ObjectStruct *PObj, *PTmp; /* If the following gain control and is non zero - its from error! */ if (setjmp(LclLongJumpBuffer) != 0) { if (f != NULL) fclose(f); return NULL; } if (strlen(FileName) == 0) ParserError(DP_ERR_EMPTY_NAME, ""); strcpy(FullFileName, FileName); if (strrchr(FullFileName, '.') == NULL) strcat(FullFileName, ".dat"); if ((f = fopen(FullFileName, "r")) == NULL) ParserError(DP_ERR_OPEN_FAILED, FullFileName); GlblToken = 0; /* Used in UnGetToken token buffer. */ DPGlblParserError = DP_NO_ERR; /* Reset errors. */ GlblLineCount = 1; /* Reset line counter. */ PObj = AllocObject("", OBJ_LIST_OBJ, NULL); DataPrsrGetAllObjects(f, PObj); if (PObj -> ObjType == OBJ_LIST_OBJ && PObj -> U.PObjList[1] == NULL) { /* Only one object in list - return the object instead. */ PTmp = PObj -> U.PObjList[0]; PObj -> U.PObjList[0] = NULL; MyFree((char *) PObj, ALLOC_OBJECT); PObj = PTmp; } fclose(f); return PObj; } /***************************************************************************** * Same as fprintf but with indentation. Prints into global OutputFile unless * * it is NULL in which it prints to screen using WndwInputWindowPutStr * *****************************************************************************/ #ifdef USE_VARARGS static void IFprintf(int Indent, char *va_alist, ...) { char *Format, Line[LINE_LEN_LONG]; int i; va_list ArgPtr; va_start(ArgPtr); Format = va_arg(ArgPtr, char *); #else static void IFprintf(int Indent, char *Format, ...) { char Line[LINE_LEN_LONG]; int i; va_list ArgPtr; va_start(ArgPtr, Format); #endif /* USE_VARARGS */ for (i = 0; i < Indent; i++) Line[i] = ' '; vsprintf(&Line[Indent], Format, ArgPtr); va_end(ArgPtr); if (OutputFile != NULL) fputs(Line, OutputFile); else WndwInputWindowPutStr(Line); } /***************************************************************************** * Routine to print the data from given geometry object. * *****************************************************************************/ static void DataPrsrPutAllObjects(ObjectStruct *PObj, int Indent) { int i; char *ErrStr = NULL; CagdRType *Coords; PolygonStruct *PPolygon; VertexStruct *PVertex; if (IS_GEOM_OBJ(PObj)) { AttributeStruct *Attr = &(PObj -> U.Attr); IFprintf(Indent, "[OBJECT [COLOR %d]", GetObjectColor(PObj)); if (Attr -> NumStrAttribs > 0) { IFprintf(0, "\n"); for (i = 0; i < Attr -> NumStrAttribs; i++) IFprintf(Indent + 8, i == Attr -> NumStrAttribs - 1 ? "[%s %s]" : "[%s %s]\n", Attr -> StrAttrName[i], Attr -> StrAttrData[i]); IFprintf(0, " %s\n", strlen(PObj -> Name) ? PObj -> Name : "NONE"); } else IFprintf(0, " %s\n", strlen(PObj -> Name) ? PObj -> Name : "NONE"); } else IFprintf(Indent, "[OBJECT %s\n", strlen(PObj -> Name) ? PObj -> Name : "NONE"); Indent += 4; switch (PObj -> ObjType) { case POLY_OBJ: for (PPolygon = PObj -> U.Pl.P; PPolygon != NULL; PPolygon = PPolygon -> Pnext) { if (PPolygon -> V == NULL) FatalError("Dump: Attemp to dump empty polygon, exit\n"); for (PVertex = PPolygon -> V -> Pnext, i = 1; PVertex != PPolygon -> V && PVertex != NULL; PVertex = PVertex -> Pnext, i++); if (IS_POLYLINE_OBJ(PObj)) IFprintf(Indent, "[POLYLINE %d\n", i); else IFprintf(Indent, "[POLYGON [PLANE %s %s %s %s] %d\n", Real2Str(PPolygon -> Plane[0]), Real2Str(PPolygon -> Plane[1]), Real2Str(PPolygon -> Plane[2]), Real2Str(PPolygon -> Plane[3]), i); PVertex = PPolygon -> V; do { /* Assume at least one edge in polygon! */ if (IS_POLYLINE_OBJ(PObj)) IFprintf(Indent + 4, "[%s%s %s %s]\n", IS_INTERNAL_EDGE(PVertex) ? "[INTERNAL] " : "", Real2Str(PVertex -> Pt[0]), Real2Str(PVertex -> Pt[1]), Real2Str(PVertex -> Pt[2])); else IFprintf(Indent + 4, "[%s[NORMAL %s %s %s] %s %s %s]\n", IS_INTERNAL_EDGE(PVertex) ? "[INTERNAL] " : "", Real2Str(PVertex -> Normal[0]), Real2Str(PVertex -> Normal[1]), Real2Str(PVertex -> Normal[2]), Real2Str(PVertex -> Pt[0]), Real2Str(PVertex -> Pt[1]), Real2Str(PVertex -> Pt[2])); PVertex = PVertex -> Pnext; } while (PVertex != PPolygon -> V && PVertex != NULL); IFprintf(Indent, "]\n"); /* Close the polygon. */ } break; case NUMERIC_OBJ: IFprintf(Indent, "[NUMBER %s]\n", Real2Str(PObj -> U.R)); break; case VECTOR_OBJ: IFprintf(Indent, "[VECTOR %s %s %s]\n", Real2Str(PObj -> U.Vec[0]), Real2Str(PObj -> U.Vec[1]), Real2Str(PObj -> U.Vec[2])); break; case CTLPT_OBJ: Coords = PObj -> U.CtlPt.Coords; switch (PObj -> U.CtlPt.PtType) { case CAGD_PT_E2_TYPE: IFprintf(Indent, "[CTLPT %s %s %s]\n", "E2", Real2Str(Coords[1]), Real2Str(Coords[2])); break; case CAGD_PT_P2_TYPE: IFprintf(Indent, "[CTLPT %s %s %s %s]\n", "P2", Real2Str(Coords[0]), Real2Str(Coords[1]), Real2Str(Coords[2])); break; case CAGD_PT_E3_TYPE: IFprintf(Indent, "[CTLPT %s %s %s %s]\n", "E3", Real2Str(Coords[1]), Real2Str(Coords[2]), Real2Str(Coords[3])); break; case CAGD_PT_P3_TYPE: IFprintf(Indent, "[CTLPT %s %s %s %s %s]\n", "P3", Real2Str(Coords[0]), Real2Str(Coords[1]), Real2Str(Coords[2]), Real2Str(Coords[3])); break; default: WndwInputWindowPutStr("Dump: Undefined point type"); break; } break; case MATRIX_OBJ: IFprintf(Indent, "[MATRIX\n"); for (i = 0; i < 4; i++) IFprintf(Indent + 8, "%s %s %s %s%s\n", Real2Str(PObj -> U.Mat[i][0]), Real2Str(PObj -> U.Mat[i][1]), Real2Str(PObj -> U.Mat[i][2]), Real2Str(PObj -> U.Mat[i][3]), i == 3 ? "]" : ""); break; case STRING_OBJ: IFprintf(Indent, "[STRING \"%s\"]\n", PObj -> U.Str); break; case OBJ_LIST_OBJ: for (i = 0; PObj -> U.PObjList[i] != NULL; i++) DataPrsrPutAllObjects(PObj -> U.PObjList[i], Indent); break; case CURVE_OBJ: CagdCrvWriteToFile2(PObj -> U.Crv.Crv, OutputFile, Indent, NULL, &ErrStr); break; case SURFACE_OBJ: CagdSrfWriteToFile2(PObj -> U.Srf.Srf, OutputFile, Indent, NULL, &ErrStr); break; default: WndwInputWindowPutStr( "Dump: Attemp to dump undefine object type."); break; } Indent -= 4; IFprintf(Indent, "]\n"); /* Close the object. */ if (ErrStr != NULL) ParserError(DP_ERR_CAGD_LIB_ERR, ErrStr); } /***************************************************************************** * Convert a real number into a string. * * The routine maintains 6 different buffers simultanuously so up to 6 calls * * can be issued from same printf... * *****************************************************************************/ static char *Real2Str(RealType R) { static int j, k, i = 0; static char Buffer[6][LINE_LEN_SHORT], Line[LINE_LEN]; if (ABS(R) < EPSILON) R = 0.0; /* Round off very small numbers. */ # ifdef DOUBLE sprintf(Buffer[i], "%-8.6lg", R); # else sprintf(Buffer[i], "%-8.6g", R); # endif /* DOUBLE */ for (k = 0; !isdigit(Buffer[i][k]) && k < LINE_LEN; k++); if (k >= LINE_LEN) { sprintf(Line, "Conversion of real number (%f) failed.\n", R); FatalError(Line); } for (j = strlen(Buffer[i]) - 1; Buffer[i][j] == ' ' && j > k; j--); if (strchr(Buffer[i], '.') != NULL) for (; Buffer[i][j] == '0' && j > k; j--); Buffer[i][j+1] = 0; j = i; i = (i + 1) % 6; return Buffer[j]; } /***************************************************************************** * Routine to read the geometry data from a given file. Reads "[OBJECT ..." * * prefixes only and invoke the auxiliary routine. * * Note objects may be recursively defined. * *****************************************************************************/ static void DataPrsrGetAllObjects(FILE *f, ObjectStruct *PObjParent) { char StringToken[LINE_LEN]; TokenType Token; int ObjCount = 0, Quit = FALSE; ObjectStruct *PObj, *OldPObj; while (!Quit) { while ((Token = GetToken(f, StringToken)) != TOKEN_OPEN_PAREN && Token != TOKEN_CLOSE_PAREN && Token != TOKEN_EOF); if (Token == TOKEN_CLOSE_PAREN || Token == TOKEN_EOF) { if (Token == TOKEN_CLOSE_PAREN) UnGetToken(StringToken); Quit = TRUE; break; } switch (GetToken(f, StringToken)) { case TOKEN_OBJECT: PObjParent -> ObjType = OBJ_LIST_OBJ; PObj = AllocObject("", UNDEF_OBJ, NULL); /* The following handle optional attributes in record. */ if (GetToken(f, StringToken) == TOKEN_OPEN_PAREN) GetObjectAttributes(PObj, f); else { SetObjectColor(PObj, GlblLoadColor); UnGetToken(StringToken); } if (GetToken(f, StringToken) == TOKEN_OTHER && strcmp(StringToken, "NONE") != 0) strcpy(PObj -> Name, StringToken); DataPrsrGetAllObjects(f, PObj); GetCloseParenToken(f); if (PObj -> ObjType == UNDEF_OBJ) ParserError(DP_ERR_OBJECT_EMPTY, ""); PObjParent -> U.PObjList[ObjCount++] = PObj; if ((int) strlen(PObj -> Name) > 0) { if ((OldPObj = GetObject(PObj -> Name)) != NULL) DeleteObject(OldPObj, TRUE); InsertObject(PObj); PObj -> Count++; } break; default: UnGetToken(StringToken); UnGetToken("["); DataPrsrGetAuxObject(f, PObjParent); Quit = TRUE; break; } } if (PObjParent -> ObjType == UNDEF_OBJ) ParserError(DP_ERR_OBJECT_EMPTY, ""); else if (PObjParent -> ObjType == OBJ_LIST_OBJ) PObjParent -> U.PObjList[ObjCount++] = NULL; } /***************************************************************************** * Routine to get close paren token from f. * *****************************************************************************/ static void GetCloseParenToken(FILE *f) { char StringToken[LINE_LEN]; if (GetToken(f, StringToken) != TOKEN_CLOSE_PAREN) ParserError(DP_ERR_CLOSE_PAREN_EXPECTED, StringToken); } /***************************************************************************** * Routine to get one numeric token into r. * *****************************************************************************/ static void GetNumericToken(FILE *f, RealType *r) { char StringToken[LINE_LEN]; GetToken(f, StringToken); # ifdef DOUBLE if (sscanf(StringToken, "%lf", r) != 1) # else if (sscanf(StringToken, "%f", r) != 1) # endif /* DOUBLE */ ParserError(DP_ERR_NUMBER_EXPECTED, StringToken); } /***************************************************************************** * Routine to read the geometry data from a given file. * *****************************************************************************/ static void DataPrsrGetAuxObject(FILE *f, ObjectStruct *PObj) { int i, j, ErrLine, HasPlane, HasNormal; char *p1, *p2, *ErrStr, StringToken[LINE_LEN]; CagdRType *Coords; VertexStruct *PVertex; PolygonStruct *PPolygon; CagdCrvStruct *PCurve; CagdSrfStruct *PSurface; while (GetToken(f, StringToken) == TOKEN_OPEN_PAREN) { switch (GetToken(f, StringToken)) { case TOKEN_POLYLINE: switch (PObj -> ObjType) { case POLY_OBJ: if (!IS_POLYLINE_OBJ(PObj)) ParserError(DP_ERR_MIXED_TYPES, ""); break; case UNDEF_OBJ: PObj -> ObjType = POLY_OBJ; PObj -> U.Pl.P = NULL; SET_POLYLINE_OBJ(PObj); break; default: ParserError(DP_ERR_MIXED_TYPES, ""); break; } PPolygon = AllocPolygon(0, 0, NULL, NULL); /* The following handle the optional attributes in struct. */ if (GetToken(f, StringToken) == TOKEN_OPEN_PAREN) GetPolygonAttributes(PPolygon, f, &HasPlane); else UnGetToken(StringToken); /* The following handles reading the vertices. */ GetPointData(f, PPolygon, TRUE, &HasNormal); PPolygon -> Pnext = PObj -> U.Pl.P; PObj -> U.Pl.P = PPolygon; break; case TOKEN_POLYGON: switch (PObj -> ObjType) { case POLY_OBJ: if (IS_POLYLINE_OBJ(PObj)) ParserError(DP_ERR_MIXED_TYPES, ""); break; case UNDEF_OBJ: PObj -> ObjType = POLY_OBJ; PObj -> U.Pl.P = NULL; RST_POLYLINE_OBJ(PObj); break; default: ParserError(DP_ERR_MIXED_TYPES, ""); break; } PPolygon = AllocPolygon(0, 0, NULL, NULL); /* The following handle the optional attributes in struct. */ HasPlane = HasNormal = FALSE; if (GetToken(f, StringToken) == TOKEN_OPEN_PAREN) GetPolygonAttributes(PPolygon, f, &HasPlane); else UnGetToken(StringToken); /* The following handles reading the vertices. */ GetPointData(f, PPolygon, FALSE, &HasNormal); if (!HasPlane) DPUpdatePolyPlane(PPolygon); if (!HasNormal) { /* Update all normals to be the same as plane eqn. */ PVertex = PPolygon -> V; do { PT_COPY(PVertex -> Normal, PPolygon -> Plane); PVertex = PVertex -> Pnext; } while (PVertex != PPolygon -> V && PVertex != NULL); } PPolygon -> Pnext = PObj -> U.Pl.P; PObj -> U.Pl.P = PPolygon; break; case TOKEN_NUMBER: switch (PObj -> ObjType) { case NUMERIC_OBJ: break; case UNDEF_OBJ: PObj -> ObjType = NUMERIC_OBJ; break; default: ParserError(DP_ERR_MIXED_TYPES, ""); break; } GetNumericToken(f, &PObj -> U.R); GetCloseParenToken(f); break; case TOKEN_STRING: switch (PObj -> ObjType) { case STRING_OBJ: break; case UNDEF_OBJ: PObj -> ObjType = STRING_OBJ; break; default: ParserError(DP_ERR_MIXED_TYPES, ""); break; } GetToken(f, StringToken); if ((p1 = strchr(StringToken, '"')) == NULL || (p2 = strrchr(StringToken, '"')) == p1) ParserError(DP_STR_NOT_IN_QUOTES, StringToken); /* Convert the end quote to EOS and copy string. */ *p2 = 0; strcpy(PObj -> U.Str, &p1[1]); GetCloseParenToken(f); break; case TOKEN_VECTOR: switch (PObj -> ObjType) { case VECTOR_OBJ: break; case UNDEF_OBJ: PObj -> ObjType = VECTOR_OBJ; break; default: ParserError(DP_ERR_MIXED_TYPES, ""); break; } for (i = 0; i < 3; i++) GetNumericToken(f, &PObj -> U.Vec[i]); GetCloseParenToken(f); break; case TOKEN_MATRIX: switch (PObj -> ObjType) { case MATRIX_OBJ: break; case UNDEF_OBJ: PObj -> ObjType = MATRIX_OBJ; break; default: ParserError(DP_ERR_MIXED_TYPES, ""); break; } for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) GetNumericToken(f, &PObj -> U.Mat[i][j]); GetCloseParenToken(f); break; case TOKEN_CTLPT: switch (PObj -> ObjType) { case CTLPT_OBJ: break; case UNDEF_OBJ: PObj -> ObjType = CTLPT_OBJ; break; default: ParserError(DP_ERR_MIXED_TYPES, ""); break; } switch (GetToken(f, StringToken)) { case TOKEN_E2: PObj -> U.CtlPt.PtType = CAGD_PT_E2_TYPE; j = 2; i = 1; break; case TOKEN_P2: PObj -> U.CtlPt.PtType = CAGD_PT_P2_TYPE; j = 3; i = 0; break; case TOKEN_E3: PObj -> U.CtlPt.PtType = CAGD_PT_E3_TYPE; j = 3; i = 1; break; case TOKEN_P3: PObj -> U.CtlPt.PtType = CAGD_PT_P3_TYPE; j = 4; i = 0; break; default: WndwInputWindowPutStr("Read: Undefined point type"); break; } Coords = PObj -> U.CtlPt.Coords; for ( ; j > 0; i++, j--) GetNumericToken(f, &Coords[i]); GetCloseParenToken(f); break; case TOKEN_SURFACE: switch (PObj -> ObjType) { case SURFACE_OBJ: break; case UNDEF_OBJ: PObj -> ObjType = SURFACE_OBJ; PObj -> U.Srf.Srf = NULL; PObj -> U.Srf.PLPolys = NULL; PObj -> U.Srf.CtlMesh = NULL; PObj -> U.Srf.Polygons = NULL; break; default: ParserError(DP_ERR_MIXED_TYPES, ""); break; } ErrLine = GlblLineCount; PSurface = CagdSrfReadFromFile2(f, &ErrStr, &ErrLine); GlblLineCount = ErrLine; if (ErrStr != NULL) { ParserError(DP_ERR_CAGD_LIB_ERR, ErrStr); break; } PSurface -> Pnext = PObj -> U.Srf.Srf; PObj -> U.Srf.Srf = PSurface; break; case TOKEN_CURVE: switch (PObj -> ObjType) { case CURVE_OBJ: break; case UNDEF_OBJ: PObj -> ObjType = CURVE_OBJ; PObj -> U.Crv.Crv = NULL; PObj -> U.Crv.PLPolys = NULL; PObj -> U.Crv.CtlPoly = NULL; break; default: ParserError(DP_ERR_MIXED_TYPES, ""); break; } ErrLine = GlblLineCount; PCurve = CagdCrvReadFromFile2(f, &ErrStr, &ErrLine); GlblLineCount = ErrLine; if (ErrStr != NULL) { ParserError(DP_ERR_CAGD_LIB_ERR, ErrStr); break; } PCurve -> Pnext = PObj -> U.Crv.Crv; PObj -> U.Crv.Crv = PCurve; break; default: ParserError(DP_ERR_UNDEF_EXPR_HEADER, StringToken); break; } /* Of switch. */ } /* Of while. */ UnGetToken(StringToken); } /***************************************************************************** * Routine to unget one token (on stack of UNGET_STACK_SIZE levels!) * *****************************************************************************/ static void UnGetToken(char *StringToken) { if (GlblToken >= UNGET_STACK_SIZE) FatalError("Parser Internal stack overflow...\n"); strcpy(GlblStringToken[GlblToken], StringToken); GlblToken++; /* GlblToken exists - Something in it (no overflow check). */ } /***************************************************************************** * Routine to get the next token out of the input file f. * * Returns the next token found, as StringToken. * * Note: StringToken must be allocated before calling this routine! * *****************************************************************************/ static void GetStringToken(FILE *f, char *StringToken) { int len; char c, *LocalStringToken; if (GlblToken) { /* get first the unget token */ GlblToken--; strcpy(StringToken, GlblStringToken[GlblToken]); return; } /* skip white spaces: */ while ((!feof(f)) && (((c = getc(f)) == ' ') || (c == '\t') || (c == '\n'))) if (c == '\n') GlblLineCount++; /* Count the lines. */ LocalStringToken = StringToken; if (c == '[') /* Its a token by itself so return it. */ *LocalStringToken++ = c; /* Copy the token into string. */ else { if (!feof(f)) do *LocalStringToken++ = c; /* Copy the token into string. */ while ((!feof(f)) && ((c = getc(f)) != ' ') && (c != '\t') && (c != '\n')); if (c == '\n') ungetc(c, f); /* Save it to be counted next time. */ } *LocalStringToken = 0; /* Put eos. */ /* The following handles the spacial case were we have XXXX] - we must */ /* split it into two token XXXX and ], UnGetToken(']') and return XXXX: */ if ((StringToken[len = strlen(StringToken)-1] == ']') && (len > 0)) { /* Return CloseParan */ UnGetToken(&StringToken[len]); /* Save next token. */ StringToken[len] = 0; /* Set end of string on "]". */ } } /***************************************************************************** * Routine to get the next token out of the input file f as token number. * * Note: StringToken must be allocated before calling this routine! * *****************************************************************************/ static TokenType GetToken(FILE *f, char *StringToken) { static int IntTokens[] = { TOKEN_OPEN_PAREN, TOKEN_CLOSE_PAREN, TOKEN_VERTEX, TOKEN_POLYGON, TOKEN_POLYLINE, TOKEN_OBJECT, TOKEN_COLOR, TOKEN_INTERNAL, TOKEN_NORMAL, TOKEN_PLANE, TOKEN_CURVE, TOKEN_SURFACE, TOKEN_E2, TOKEN_P2, TOKEN_E3, TOKEN_P3, TOKEN_NUMBER, TOKEN_STRING, TOKEN_VECTOR, TOKEN_MATRIX, TOKEN_CTLPT, 0 }; static char *StrTokens[] = { "[", "]", "VERTEX", "POLYGON", "POLYLINE", "OBJECT", "COLOR", "INTERNAL", "NORMAL", "PLANE", "CURVE", "SURFACE", "E2", "P2", "E3", "P3", "NUMBER", "STRING", "VECTOR", "MATRIX", "CTLPT", NULL }; int i; GetStringToken(f, StringToken); if (feof(f)) return TOKEN_EOF; for (i = 0; StrTokens[i] != NULL; i++) if (strcmp(StringToken, StrTokens[i]) == 0) return IntTokens[i]; return TOKEN_OTHER; /* Must be number or name. */ } /***************************************************************************** * Routine to read from input file f the following [ATTR ...] [ATTR ...]. * * Note the '[' was allready read. * * Current supported attributes: [INTERNAL] - internal edge (IRIT output). * *****************************************************************************/ static void GetVertexAttributes(VertexStruct *PVertex, FILE *f, int *HasNormal) { int i; char StringToken[LINE_LEN]; do { switch (GetToken(f, StringToken)) { case TOKEN_INTERNAL: if (GetToken(f, StringToken) != TOKEN_CLOSE_PAREN) ParserError(DP_ERR_CLOSE_PAREN_EXPECTED, StringToken); SET_INTERNAL_EDGE(PVertex); break; case TOKEN_NORMAL: /* The following handles reading 3 coord. of vertex normal. */ for (i = 0; i < 3; i++) GetNumericToken(f, &PVertex -> Normal[i]); GetCloseParenToken(f); *HasNormal = TRUE; break; default: /* Ignore this option! */ while (GetToken(f, StringToken) != TOKEN_CLOSE_PAREN); break; } } while (GetToken(f, StringToken) == TOKEN_OPEN_PAREN); UnGetToken(StringToken); } /***************************************************************************** * Routine to read from input file f the following [ATTR ...] [ATTR ...]. * * Note the '[' was allready read. * * Current supported attributes: [PLANE A B C D]. * *****************************************************************************/ static void GetPolygonAttributes(PolygonStruct *PPolygon, FILE *f, int *HasPlane) { int i; char StringToken[LINE_LEN]; do { switch (GetToken(f, StringToken)) { case TOKEN_PLANE: /* The following handles reading of 4 coord. of plane eqn.. */ for (i = 0; i < 4; i++) GetNumericToken(f, &PPolygon -> Plane[i]); GetCloseParenToken(f); *HasPlane = TRUE; break; default: while (GetToken(f, StringToken) != TOKEN_CLOSE_PAREN); break; } } while (GetToken(f, StringToken) == TOKEN_OPEN_PAREN); UnGetToken(StringToken); } /***************************************************************************** * Routine to read from input file f the following [ATTR ...] [ATTR ...]. * * Note the '[' was allready read. * * Current supported attributes: [COLOR C] - set color. * * All other attributes are treated as String attributes. * *****************************************************************************/ static void GetObjectAttributes(ObjectStruct *PObject, FILE *f) { int i; char StringToken[LINE_LEN], Data[LINE_LEN], Name[LINE_LEN]; do { switch (GetToken(f, StringToken)) { case TOKEN_COLOR: GetToken(f, StringToken); if (sscanf(StringToken, "%d", &i) != 1) ParserError(DP_ERR_NUMBER_EXPECTED, StringToken); GetCloseParenToken(f); SetObjectColor(PObject, i); break; default: strcpy(Name, StringToken); strcpy(Data, " "); while (GetToken(f, StringToken) != TOKEN_CLOSE_PAREN) { strcat(Data, StringToken); strcat(Data, " "); } SetObjectStrAttrib(PObject, Name, Data); break; } } while (GetToken(f, StringToken) == TOKEN_OPEN_PAREN); UnGetToken(StringToken); } /***************************************************************************** * Routine to read poly* vertex information. * *****************************************************************************/ static void GetPointData(FILE *f, PolygonStruct *PPolygon, int IsPolyline, int *HasNormal) { int i, j, Length; char StringToken[LINE_LEN]; VertexStruct *V, *VTail = NULL; if (GetToken(f, StringToken) != TOKEN_OTHER || sscanf(StringToken, "%d", &Length) != 1) ParserError(DP_ERR_NUMBER_EXPECTED, StringToken); for (i = 0; i < Length; i++) { if (GetToken(f, StringToken) != TOKEN_OPEN_PAREN) ParserError(DP_ERR_OPEN_PAREN_EXPECTED, StringToken); V = AllocVertex(0, 0, NULL, NULL); /* The following handle the optional attributes in struct. */ *HasNormal = FALSE; if (GetToken(f, StringToken) == TOKEN_OPEN_PAREN) GetVertexAttributes(V, f, HasNormal); else UnGetToken(StringToken); for (j = 0; j < 3; j++) /* Read coordinates. */ GetNumericToken(f, &V -> Pt[j]); GetCloseParenToken(f); if (!*HasNormal) PT_COPY(V -> Normal, PPolygon -> Plane); if (VTail == NULL) PPolygon -> V = VTail = V; else { VTail -> Pnext = V; VTail = V; } } VTail -> Pnext = IsPolyline ? NULL : PPolygon -> V; GetCloseParenToken(f); } /***************************************************************************** * Routine to update the Plane equation of the given polygon by the order * * of the first 3 vertices of that polygon. * *****************************************************************************/ static void DPUpdatePolyPlane(PolygonStruct *PPoly) { int i; VectorType V1, V2; RealType Len; VertexStruct *V; V = PPoly -> V; PT_SUB(V1, V -> Pt, V -> Pnext -> Pt); V = V -> Pnext; PT_SUB(V2, V -> Pt, V -> Pnext -> Pt); PPoly -> Plane[0] = V1[1] * V2[2] - V2[1] * V1[2]; PPoly -> Plane[1] = V1[2] * V2[0] - V2[2] * V1[0]; PPoly -> Plane[2] = V1[0] * V2[1] - V2[0] * V1[1]; PPoly -> Plane[3] = (-DOT_PROD(PPoly -> Plane, PPoly -> V -> Pt)); /* Normalize the plane such that the normal has length of 1: */ Len = PT_LENGTH(PPoly -> Plane); for (i = 0; i < 4; i++) PPoly -> Plane[i] /= Len; } /***************************************************************************** * Routine to print pasring error according to ErrNum and set GlblParserError.* *****************************************************************************/ static void ParserError(DataPrsrErrType ErrNum, char *Msg) { DPGlblLineCount = GlblLineCount; DPGlblParserError = ErrNum; strcpy(DPGlblTokenError, Msg); /* Keep the message in safe place... */ longjmp(LclLongJumpBuffer, 1); /* Jump to... */ } /***************************************************************************** * Returns TRUE if error happened, FALSE otherwise. * * If error, then ErrorMsg is updated to point on static str describing it. * *****************************************************************************/ int DataPrsrParseError(char **ErrorMsg) { DataPrsrErrType Temp; char TempCopy[LINE_LEN]; if ((Temp = DPGlblParserError) == DP_NO_ERR) return FALSE; strcpy(TempCopy, DPGlblTokenError); DPGlblParserError = DP_NO_ERR; switch (Temp) { case DP_ERR_NUMBER_EXPECTED: sprintf(DPGlblTokenError, "Line %d: Numeric data expected - found %s", DPGlblLineCount, TempCopy); break; case DP_ERR_OPEN_PAREN_EXPECTED: sprintf(DPGlblTokenError, "Line %d: '[' expected - found '%s'", DPGlblLineCount, TempCopy); break; case DP_ERR_CLOSE_PAREN_EXPECTED: sprintf(DPGlblTokenError, "Line %d: ']' expected - found '%s'", DPGlblLineCount, TempCopy); break; case DP_ERR_LIST_COMP_UNDEF: sprintf(DPGlblTokenError, "Line %d: Undefined list element - \"%s\"", DPGlblLineCount, TempCopy); break; case DP_ERR_UNDEF_EXPR_HEADER: sprintf(DPGlblTokenError, "Line %d: Undefined TOKEN - \"%s\"", DPGlblLineCount, TempCopy); break; case DP_ERR_PT_TYPE_EXPECTED: sprintf(DPGlblTokenError, "Line %d: Point type expected", DPGlblLineCount); break; case DP_ERR_OBJECT_EMPTY: sprintf(DPGlblTokenError, "Line %d: Empty object found", DPGlblLineCount); break; case DP_ERR_EMPTY_NAME: sprintf(DPGlblTokenError, "Given file name is empty"); break; case DP_ERR_OPEN_FAILED: sprintf(DPGlblTokenError, "Fail to open file - %s", TempCopy); break; case DP_ERR_MIXED_TYPES: sprintf(DPGlblTokenError, "Line %d: Mixed data types in same object", DPGlblLineCount); break; case DP_STR_NOT_IN_QUOTES: sprintf(DPGlblTokenError, "Line %d: String not in quotes (%s)", DPGlblLineCount, TempCopy); break; case DP_ERR_CAGD_LIB_ERR: sprintf(DPGlblTokenError, "Line %d: %s", DPGlblLineCount, TempCopy); break; default: sprintf(DPGlblTokenError, "Line %d: Data file parser - undefined error", DPGlblLineCount); break; } *ErrorMsg = DPGlblTokenError; return TRUE; }