Metropoli BBS
VIEWER: iritprsr.c MODE: TEXT (ASCII)
/*****************************************************************************
* Generic parser for the "Irit" solid modeller.				     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Sep. 1991   *
*****************************************************************************/

#ifdef __MSDOS__
#include <stdlib.h>
#endif /* __MSDOS__ */

#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
#include <setjmp.h>
#include "irit_sm.h"
#include "iritprsr.h"

#define LOAD_COLOR              14		/* Index color 1 by default. */

#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_POINTLIST,
    TOKEN_OBJECT,
    TOKEN_COLOR,
    TOKEN_RGB,
    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. */
    IP_NO_ERR = 0,

    IP_ERR_NUMBER_EXPECTED,
    IP_ERR_OPEN_PAREN_EXPECTED,
    IP_ERR_CLOSE_PAREN_EXPECTED,
    IP_ERR_LIST_COMP_UNDEF,
    IP_ERR_UNDEF_EXPR_HEADER,
    IP_ERR_PT_TYPE_EXPECTED,
    IP_ERR_OBJECT_EMPTY,
    IP_ERR_MIXED_TYPES,
    IP_STR_NOT_IN_QUOTES,
    IP_ERR_OBJECT_EXPECTED,
    IP_ERR_CAGD_LIB_ERR,
    IP_ERR_STACK_OVERFLOW,
    IP_ERR_DEGEN_POLYGON,

    IP_WRN_OBJ_NAME_TRUNC = 100
} IritPrsrErrType;

static int  IPGlblLineCount = 0;     /* Used to locate errors in input file. */
static IritPrsrErrType IPGlblParserError = IP_NO_ERR;   /* Last err # found. */
static char IPGlblTokenError[LINE_LEN_LONG];  /* Last token 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_LONG];/* Unget tokens.*/

static IPObjectStruct *AllSrfs = NULL;
static IPObjectStruct *AllCrvs = NULL;
static IPObjectStruct *AllPolys = NULL;

int IritPrsrPolyListCirc = TRUE;
int IritPrsrWasViewMat = FALSE,
    IritPrsrWasPrspMat = FALSE;
MatrixType IritPrsrViewMat = {		      /* Isometric view, by default. */
    { -0.707107, -0.408248, 0.577350, 0.000000 },
    {  0.707107, -0.408248, 0.577350, 0.000000 },
    {  0.000000,  0.816496, 0.577350, 0.000000 },
    {  0.000000,  0.000000, 0.000000, 1.000000 }
};
MatrixType IritPrsrPrspMat = {
    { 1, 0, 0, 0 },
    { 0, 1, 0, 0 },
    { 0, 0, 0.1, -0.35 },
    { 0, 0, 0.35, 1.0 }
};

static void UnGetToken(char *StringToken);
static void GetStringToken(FILE *f, char *StringToken);
static TokenType GetToken(FILE *f, char *StringToken);
static void GetVertexAttributes(IPVertexStruct *PVertex, FILE *f);
static void GetPolygonAttributes(IPPolygonStruct *PPolygon, FILE *f);
static void GetObjectAttributes(IPObjectStruct *PObject, FILE *f);
static void GetPointData(FILE *f, IPPolygonStruct *PPolygon);
static void IPUpdatePolyPlane(IPPolygonStruct *PPoly);
static void ParserError(IritPrsrErrType ErrNum, char *Msg);

static void IritPrsrGetAllObjects(FILE *f, IPObjectStruct *PObjParent);
static void GetCloseParenToken(FILE *f);
static void SkipToCloseParenToken(FILE *f);
static void GetNumericToken(FILE *f, RealType *r);
static void IritPrsrGetAuxObject(FILE *f, IPObjectStruct *PObj);

/*****************************************************************************
* Routine to read the data from	a given	file.				     *
*****************************************************************************/
IPObjectStruct *IritPrsrGetObjects(FILE *f)
{
    IPObjectStruct *PObjs, *PTmp;

    AllSrfs = NULL;
    AllCrvs = NULL;
    AllPolys = NULL;

    /* If the following gain control and is non zero - its from error! */
    if (setjmp(LclLongJumpBuffer) != 0) {
	if (f != NULL) fclose(f);
	return NULL;
    }

    GlblToken =	0;			 /* Used in UnGetToken token buffer. */
    IPGlblParserError = IP_NO_ERR;			    /* Reset errors. */
    GlblLineCount = 1;				      /* Reset line counter. */

    PTmp = IritPrsrNewObjectStruct();
    IritPrsrGetAllObjects(f, PTmp);

    if (AllCrvs != NULL || AllSrfs != NULL) {
	if ((PObjs = IritPrsrProcessFreeForm(AllCrvs, AllSrfs)) != NULL)
    	{
	    for (PTmp = PObjs; PTmp -> Pnext != NULL; PTmp = PTmp -> Pnext) {
		if (!IP_HAS_OBJ_COLOR(PTmp)) {
		    PTmp -> Color = LOAD_COLOR;
		    IP_SET_OBJ_COLOR(PTmp);
		}
	    }
	    PTmp -> Pnext = AllPolys;
	    AllPolys = PObjs;
    	}
    }

    fclose(f);
    return AllPolys;
}

/*****************************************************************************
* 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 IritPrsrGetAllObjects(FILE *f, IPObjectStruct *PObjParent)
{
    char StringToken[LINE_LEN_LONG];
    TokenType Token;
    int	WasObjectToken = FALSE,
	Quit = FALSE;
    IPObjectStruct *PObj;

    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:
		WasObjectToken = TRUE;
		PObj = IritPrsrNewObjectStruct();

		/* The following handle optional attributes in record. */
		if (GetToken(f, StringToken) == TOKEN_OPEN_PAREN)
		    GetObjectAttributes(PObj, f);
		else
		{
		    UnGetToken(StringToken);
		}

		if (!IP_HAS_OBJ_COLOR(PObj)) {
		    PObj -> Color = LOAD_COLOR;
		    IP_SET_OBJ_COLOR(PObj);
		}

		if (GetToken(f, StringToken) == TOKEN_OTHER &&
		    strcmp(StringToken, "NONE") != 0)
		    strcpy(PObj -> Name, StringToken);

		IritPrsrGetAllObjects(f, PObj);

		GetCloseParenToken(f);
		if (PObjParent) {
		    /* This object contains other object - delete it since   */
		    /* we are flattening the structure here.		     */
		    free((VoidPtr) PObjParent);
		    PObjParent = NULL;
		}
		break;
	    default:
		if (WasObjectToken) {
		    ParserError(IP_ERR_OBJECT_EXPECTED, StringToken);
		}
		UnGetToken(StringToken);
		UnGetToken("[");
		IritPrsrGetAuxObject(f, PObjParent);
		Quit = TRUE;
		break;
	}
    }
}

/*****************************************************************************
* Routine to get close paren token from f.				     *
*****************************************************************************/
static void GetCloseParenToken(FILE *f)
{
    char StringToken[LINE_LEN_LONG];

    if (GetToken(f, StringToken) != TOKEN_CLOSE_PAREN)
	ParserError(IP_ERR_CLOSE_PAREN_EXPECTED, StringToken);
}

/*****************************************************************************
* Routine to get close paren token from f.				     *
*****************************************************************************/
static void SkipToCloseParenToken(FILE *f)
{
    char StringToken[LINE_LEN_LONG];

    while (!feof(f) && GetToken(f, StringToken) != TOKEN_CLOSE_PAREN);
}

/*****************************************************************************
* Routine to get one numeric token into r.				     *
*****************************************************************************/
static void GetNumericToken(FILE *f, RealType *r)
{
    char StringToken[LINE_LEN_LONG];

    GetToken(f, StringToken);
#   ifdef DOUBLE
	if (sscanf(StringToken, "%lf", r) != 1)
#   else
	if (sscanf(StringToken, "%f", r) != 1)
#   endif /* DOUBLE */
	    ParserError(IP_ERR_NUMBER_EXPECTED, StringToken);
}

/*****************************************************************************
* Routine to read the content of a single object. Return TRUE if data is     *
* useful for this parser, FALSE if data should be purged.		     *
*****************************************************************************/
static void IritPrsrGetAuxObject(FILE *f, IPObjectStruct *PObj)
{
    int	i, j, ErrLine;
    TokenType Token;
    char *ErrStr, StringToken[LINE_LEN_LONG];
    IPPolygonStruct *PPolygon;
    CagdCrvStruct *PCurve;
    CagdSrfStruct *PSurface;

    PObj -> Type = IP_OBJ_UNDEF;

    while (GetToken(f, StringToken) == TOKEN_OPEN_PAREN) {
	switch (Token = GetToken(f, StringToken)) {
	    case TOKEN_POLYGON:
	    case TOKEN_POLYLINE:
	    case TOKEN_POINTLIST:
		PPolygon = IritPrsrNewPolygonStruct();
		switch (Token) {
		    case TOKEN_POLYGON:
			PPolygon -> Type = IP_POLYGON;
			break;
		    case TOKEN_POLYLINE:
			PPolygon -> Type = IP_POLYLINE;
			break;
		    case TOKEN_POINTLIST:
			PPolygon -> Type = IP_POINTLIST;
			break;
		}

		/* The following handle the optional attributes in struct.   */
		if (GetToken(f, StringToken) == TOKEN_OPEN_PAREN)
		    GetPolygonAttributes(PPolygon, f);
		else
		    UnGetToken(StringToken);

		/* The following handles reading the vertices. */
		GetPointData(f, PPolygon);

		if (PPolygon -> Type == IP_POLYGON &&
		    !IP_HAS_POLY_PLANE(PPolygon))
		    IPUpdatePolyPlane(PPolygon);

		PPolygon -> Pnext = PObj -> U.PPolygon;
		PObj -> U.PPolygon = PPolygon;
		PObj -> Type = IP_OBJ_POLY;
		break;
	    case TOKEN_MATRIX:
		if (strcmp(PObj -> Name, "VIEW_MAT") == 0) {
		    IritPrsrWasViewMat = TRUE;
		    for (i = 0; i < 4; i++)
			for (j = 0; j < 4; j++)
			    GetNumericToken(f, &IritPrsrViewMat[i][j]);
		    GetCloseParenToken(f);
		}
		else if (strcmp(PObj -> Name, "PRSP_MAT") == 0) {
		    IritPrsrWasPrspMat = TRUE;
		    for (i = 0; i < 4; i++)
			for (j = 0; j < 4; j++)
			    GetNumericToken(f, &IritPrsrPrspMat[i][j]);
		    GetCloseParenToken(f);
		}
		else
		    SkipToCloseParenToken(f);
		break;
	    case TOKEN_SURFACE:
		ErrLine = GlblLineCount;
		PSurface = CagdSrfReadFromFile2(f, &ErrStr, &ErrLine);
		GlblLineCount = ErrLine;

		if (ErrStr != NULL) {
		    ParserError(IP_ERR_CAGD_LIB_ERR, ErrStr);
		    break;
		}

		if (PSurface != NULL) {
		    PSurface -> Pnext = PObj -> U.PSrfs;
		    PObj -> U.PSrfs = PSurface;
		}
		PObj -> Type = IP_OBJ_SURFACE;
		break;
	    case TOKEN_CURVE:
		ErrLine = GlblLineCount;
		PCurve = CagdCrvReadFromFile2(f, &ErrStr, &ErrLine);
		GlblLineCount = ErrLine;

		if (ErrStr != NULL) {
		    ParserError(IP_ERR_CAGD_LIB_ERR, ErrStr);
		    break;
		}

		if (PCurve != NULL) {
		    PCurve -> Pnext = PObj -> U.PCrvs;
		    PObj -> U.PCrvs = PCurve;
		}
		PObj -> Type = IP_OBJ_CURVE;
		break;
	    case TOKEN_NUMBER:
	    case TOKEN_STRING:
	    case TOKEN_VECTOR:
	    case TOKEN_CTLPT:
		SkipToCloseParenToken(f);
		break;
	    default:
		ParserError(IP_ERR_UNDEF_EXPR_HEADER, StringToken);
		break;
	} /* Of switch. */
    } /* Of while. */

    switch (Token) {
    	case TOKEN_POLYGON:
    	case TOKEN_POLYLINE:
    	case TOKEN_POINTLIST:
	    PObj -> Pnext = AllPolys;
	    AllPolys = PObj;
    	    break;
    	case TOKEN_SURFACE:
	    PObj -> Pnext = AllSrfs;
	    AllSrfs = PObj;
    	    break;
    	case TOKEN_CURVE:
	    PObj -> Pnext = AllCrvs;
	    AllCrvs = PObj;
    	    break;
    	default:
    	    free((VoidPtr) PObj);
    	    break;
    }

    UnGetToken(StringToken);
}

/*****************************************************************************
*   Routine to unget one token (on stack of UNGET_STACK_SIZE levels!)	     *
*****************************************************************************/
static void UnGetToken(char *StringToken)
{
    if (GlblToken >= UNGET_STACK_SIZE)
	 ParserError(IP_ERR_STACK_OVERFLOW, "");

    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_POINTLIST,
	TOKEN_OBJECT,
	TOKEN_COLOR,
	TOKEN_RGB,
	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",
	"POINTLIST",
	"OBJECT",
	"COLOR",
	"RGB",
	"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.					     *
*****************************************************************************/
static void GetVertexAttributes(IPVertexStruct *PVertex, FILE *f)
{
    int i;
    RealType Len;
    char StringToken[LINE_LEN_LONG];

    do {
	switch (GetToken(f, StringToken)) {
	    case TOKEN_INTERNAL:
		GetCloseParenToken(f);
		IP_SET_VRTX_INTERNAL(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]);

		/* Make sure it is normalized. */
		Len = PT_LENGTH(PVertex -> Normal);
		for (i = 0; i < 3; i++) PVertex -> Normal[i] /= Len;

		GetCloseParenToken(f);
		IP_SET_VRTX_NORMAL(PVertex);
		break;
	    default: /* Ignore this option! */
		SkipToCloseParenToken(f);
		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.					     *
*****************************************************************************/
static void GetPolygonAttributes(IPPolygonStruct *PPolygon, FILE *f)
{
    int i;
    RealType Len;
    char StringToken[LINE_LEN_LONG];

    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]);

		/* Make sure it is normalized. */
		Len = PT_LENGTH(PPolygon -> Plane);
		for (i = 0; i < 4; i++) PPolygon -> Plane[i] /= Len;

		GetCloseParenToken(f);
		IP_SET_POLY_PLANE(PPolygon);
		break;
	    default:
		SkipToCloseParenToken(f);
		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.					     *
*****************************************************************************/
static void GetObjectAttributes(IPObjectStruct *PObject, FILE *f)
{
    int	i, j;
    char StringToken[LINE_LEN_LONG];

    do {
	switch (GetToken(f, StringToken)) {
	    case TOKEN_RGB:
		/* The following handles reading of 3 coord. of rgb.	*/
		for (i = 0; i < 3; i++) {
		    GetToken(f, StringToken);
		    if (sscanf(StringToken, "%d", &j) != 1)
			ParserError(IP_ERR_NUMBER_EXPECTED, StringToken);
		    PObject -> RGB[i] = (unsigned char) j;
		}
		GetCloseParenToken(f);
		IP_SET_OBJ_RGB(PObject);
		break;
	    case TOKEN_COLOR:
		GetToken(f, StringToken);
		if (sscanf(StringToken, "%d", &i) != 1)
		    ParserError(IP_ERR_NUMBER_EXPECTED, StringToken);
		GetCloseParenToken(f);
		PObject -> Color = i;
		IP_SET_OBJ_COLOR(PObject);
		break;
	    default:
		if ((i = PObject -> Attrs.NumStrAttribs) < MAX_NUM_ATTRS)
		{
		   PObject -> Attrs.StrAttrName[i] = strdup(StringToken);
		   if (GetToken(f, StringToken) == TOKEN_CLOSE_PAREN) {
		       UnGetToken(StringToken);
		       PObject -> Attrs.StrAttrData[i] = "";
		   }
		   else {
		       PObject -> Attrs.StrAttrData[i] = strdup(StringToken);
		   }
		   PObject -> Attrs.NumStrAttribs++;
		}
		SkipToCloseParenToken(f);
		break;
	}
    }
    while (GetToken(f, StringToken) == TOKEN_OPEN_PAREN);

    if (!IP_HAS_OBJ_COLOR(PObject)) {
	PObject -> Color = LOAD_COLOR;
	IP_SET_OBJ_COLOR(PObject);
    }

    UnGetToken(StringToken);
}

/*****************************************************************************
* Routine to read poly* vertex information.				     *
*****************************************************************************/
static void GetPointData(FILE *f, IPPolygonStruct *PPolygon)
{
    int i, j, Length;
    char StringToken[LINE_LEN_LONG];
    IPVertexStruct *V,
	*VTail = NULL;

    if (GetToken(f, StringToken) != TOKEN_OTHER ||
	sscanf(StringToken, "%d", &Length) != 1)
	ParserError(IP_ERR_NUMBER_EXPECTED, StringToken);

    for (i = 0; i < Length; i++) {
	if (GetToken(f, StringToken) != TOKEN_OPEN_PAREN)
	    ParserError(IP_ERR_OPEN_PAREN_EXPECTED, StringToken);

	V = IritPrsrNewVertexStruct();

	/* The following handle the optional attributes in struct. */
	if (GetToken(f, StringToken) == TOKEN_OPEN_PAREN)
	    GetVertexAttributes(V, f);
	else
	    UnGetToken(StringToken);

	for (j = 0; j < 3; j++)				/* Read coordinates. */
	    GetNumericToken(f, &V -> Coord[j]);

	GetCloseParenToken(f);

	if (!IP_HAS_VRTX_NORMAL(V))
	    PT_COPY(V -> Normal, PPolygon -> Plane);

	if (VTail == NULL)
	    PPolygon -> PVertex = VTail = V;
	else {
	    VTail -> Pnext = V;
	    VTail = V;
	}
    }

    if (IritPrsrPolyListCirc && PPolygon -> Type == IP_POLYGON)
	VTail -> Pnext = PPolygon -> PVertex;

    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 IPUpdatePolyPlane(IPPolygonStruct *PPoly)
{
    int i;
    RealType Len, V1[3], V2[3];
    IPVertexStruct *V = PPoly -> PVertex;

    if (V == NULL || V -> Pnext == NULL || V -> Pnext -> Pnext == NULL)
	ParserError(IP_ERR_DEGEN_POLYGON, "");

    PT_SUB(V1, V -> Coord, V -> Pnext -> Coord);
    V = V -> Pnext;
    PT_SUB(V2, V -> Coord, V -> Pnext -> Coord);

    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 -> PVertex -> Coord));

    /* 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 set a string attribute. A string attribute consists of an     *
* attribute name (string) and data (also string).			     *
*   If Data = "", the attribute with name Name is been freed.		     *
*   If attribute by the given name already exists, it is replaced.	     *
*****************************************************************************/
void IritPrsrSetStrAttrib(IPObjectStruct *PObj, char *Name, char *Data)
{
    int i;
    IPAttributeStruct *Attr = &PObj -> Attrs;

    for (i = 0; i < Attr -> NumStrAttribs; i++) {
	if (strcmp(Name, Attr -> StrAttrName[i]) == 0) {
	    /* If Data is an empty string, remove this entry. */
	    if (strlen(Data) == 0) {
		free((VoidPtr) Attr -> StrAttrName[i]);
		free((VoidPtr) Attr -> StrAttrData[i]);
		for ( ; i < (int) Attr -> NumStrAttribs - 2; i++) {
		    Attr -> StrAttrName[i] = Attr -> StrAttrName[i + 1];
		    Attr -> StrAttrData[i] = Attr -> StrAttrData[i + 1];
		}
		Attr -> NumStrAttribs--;
	    }
	    else {
		free((VoidPtr) Attr -> StrAttrData[i]);
		Attr -> StrAttrData[i] = strdup(Data);
	    }

	    return;
	}
    }

    /* O.k. it is a new attribute. */
    if (Attr -> NumStrAttribs >= MAX_NUM_ATTRS) {
	return;
    }
    Attr -> StrAttrName[Attr -> NumStrAttribs] = strdup(Name);
    Attr -> StrAttrData[Attr -> NumStrAttribs++] = strdup(Data);
}

/*****************************************************************************
*   Routine to get a string attribute. A string attribute consists of an     *
* attribute name (string) and data (also string).			     *
*   Returns a pointer to data if string name is found, NULL otherwise.	     *
*****************************************************************************/
char *IritPrsrGetStrAttrib(IPObjectStruct *PObj, char *Name)
{
    int i;
    IPAttributeStruct *Attr = &PObj -> Attrs;

    /* If Name is an empty string - print all attributes. */
    if (Name == NULL || strlen(Name) == 0) return NULL;

    for (i = 0; i < Attr -> NumStrAttribs; i++)
	if (strcmp(Name, Attr -> StrAttrName[i]) == 0)
    	    return Attr -> StrAttrData[i];

    return NULL;
}

/*****************************************************************************
* Routine to allocate a new VertexStruct.				     *
*****************************************************************************/
IPVertexStruct *IritPrsrNewVertexStruct(void)
{
    IPVertexStruct *V = (IPVertexStruct *) malloc(sizeof(IPVertexStruct));
    V -> Pnext = NULL;
    V -> VAux = NULL;
    V -> VTags = 0;
    return V;
}

/*****************************************************************************
* Routine to allocate a new PolygonStruct.				     *
*****************************************************************************/
IPPolygonStruct *IritPrsrNewPolygonStruct(void)
{
    IPPolygonStruct *P = (IPPolygonStruct *) malloc(sizeof(IPPolygonStruct));
    P -> Pnext = NULL;
    P -> PVertex = NULL;
    P -> PAux = NULL;
    P -> PTags = 0;
    return P;
}

/*****************************************************************************
* Routine to allocate a new ObjectStruct.				     *
*****************************************************************************/
IPObjectStruct *IritPrsrNewObjectStruct(void)
{
    IPObjectStruct *O = (IPObjectStruct *) malloc(sizeof(IPObjectStruct));
    O -> Pnext = NULL;
    O -> U.PPolygon = NULL;
    O -> OAux = NULL;
    O -> Attrs.NumStrAttribs = 0;
    O -> OTags = 0;
    O -> Name[0] = 0;
    O -> Color = LOAD_COLOR;
    O -> FFPolylines = O -> FFPolygons = NULL;
    return O;
}

/*****************************************************************************
* Routine to print pasring error according to ErrNum and set GlblParserError.*
*****************************************************************************/
static void ParserError(IritPrsrErrType ErrNum, char *Msg)
{
    IPGlblLineCount = GlblLineCount;
    IPGlblParserError = ErrNum;
    strcpy(IPGlblTokenError, 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 IritPrsrParseError(char **ErrorMsg)
{
    IritPrsrErrType Temp;
    char TempCopy[LINE_LEN_LONG];

    if ((Temp = IPGlblParserError) == IP_NO_ERR) return FALSE;

    strcpy(TempCopy, IPGlblTokenError);
    IPGlblParserError = IP_NO_ERR;

    switch (Temp) {
	case IP_ERR_NUMBER_EXPECTED:
	    sprintf(IPGlblTokenError, "Line %d: Numeric data expected - found %s",
		IPGlblLineCount, TempCopy);
	    break;
	case IP_ERR_OPEN_PAREN_EXPECTED:
	    sprintf(IPGlblTokenError, "Line %d: '[' expected - found '%s'",
		IPGlblLineCount, TempCopy);
	    break;
	case IP_ERR_CLOSE_PAREN_EXPECTED:
	    sprintf(IPGlblTokenError, "Line %d: ']' expected - found '%s'",
		IPGlblLineCount, TempCopy);
	    break;
	case IP_ERR_LIST_COMP_UNDEF:
	    sprintf(IPGlblTokenError, "Line %d: Undefined list element - \"%s\"",
		IPGlblLineCount, TempCopy);
	    break;
	case IP_ERR_UNDEF_EXPR_HEADER:
	    sprintf(IPGlblTokenError, "Line %d: Undefined TOKEN - \"%s\"",
		IPGlblLineCount, TempCopy);
	    break;
	case IP_ERR_PT_TYPE_EXPECTED:
	    sprintf(IPGlblTokenError, "Line %d: Point type expected",
		IPGlblLineCount);
	    break;
	case IP_ERR_OBJECT_EMPTY:
	    sprintf(IPGlblTokenError, "Line %d: Empty object found",
		IPGlblLineCount);
	    break;
	case IP_ERR_MIXED_TYPES:
	    sprintf(IPGlblTokenError,
		    "Line %d: Mixed data types in same object",
		    IPGlblLineCount);
	    break;
	case IP_STR_NOT_IN_QUOTES:
	    sprintf(IPGlblTokenError,
		    "Line %d: String not in quotes (%s)",
		    IPGlblLineCount, TempCopy);
	    break;
	case IP_ERR_OBJECT_EXPECTED:
	    sprintf(IPGlblTokenError,
		    "Line %d: 'OBJECT' expected, found '%s'",
		    IPGlblLineCount, TempCopy);
	    break;
	case IP_ERR_CAGD_LIB_ERR:
	    sprintf(IPGlblTokenError, "Line %d: %s",
		    IPGlblLineCount, TempCopy);
	    break;
	case IP_ERR_STACK_OVERFLOW:
	    sprintf(IPGlblTokenError, "Line %d: Parser Stack overflow",
		    IPGlblLineCount);
	    break;
	default:
	    sprintf(IPGlblTokenError,
		    "Line %d: Data file parser - undefined error",
		    IPGlblLineCount);
	    break;
    }

    *ErrorMsg = IPGlblTokenError;

    return TRUE;
}
[ RETURN TO DIRECTORY ]