/***************************************************************************** * Routines to test all edges for visibility relative to the global polygon * * list, and output the visible ones, to graphics screen or as text file * * * * Written by: Gershon Elber Ver 2.0, Jan. 1989 * *****************************************************************************/ #ifdef __MSDOS__ #include #endif /* __MSDOS__ */ #include #include #include #include #include #include #include "program.h" #include "genmat.h" #include "iritprsr.h" #define MAX_POLYLINE_SIZE 50 /* Maximum size of output polyline. */ #define EDGE_ON_PLANE_EPS -0.003 /* Epsilon considers edge on plane. */ static EdgeStruct *OutEdgeHashTable[EDGE_HASH_TABLE_SIZE]; static void SaveCurrentMat(void); static void OutputEdge(EdgeStruct *PEtemp); static void EmitOutEdgeHashTable(void); static char *Real2Str(RealType R); static EdgeStruct *FoundMatchEdge(int *Colinear, EdgeStruct *PEdge); static int FuzzSamePoints(RealType Pt1[3], RealType Pt2[3]); static int ColinearPoints(RealType Pt1[3], RealType Pt2[3], RealType Pt3[3]); static int VisibleEdge(int EdgeMinY, EdgeStruct *PEdge, IPPolygonStruct *PolyHashTable[]); static int VisibleEdgeOnePoly(EdgeStruct *PEdge, IPPolygonStruct *PPoly); static int InverseMatrix(MatrixType A); /***************************************************************************** * Routine to test for visibility all edges in EdgeHashTable and display or * * output the visible ones only. It is assumed that only totally visible or * * invisible edges are in table (Pass 3 broke all other kind of edges). * *****************************************************************************/ void OutVisibleEdges(void) { int i; long SaveTime = time(NULL); EdgeStruct *PEtemp, *PEtail; fprintf(stderr, "\nPass 4, Edges [%5d] = ", EdgeCount); for (i = 0; i < EDGE_HASH_TABLE_SIZE; i++) OutEdgeHashTable[i] = NULL; if (!InverseMatrix(GlblViewMat)) { fprintf(stderr, "\nNo inverse for matrix transformation, output is in screen space\n"); GenUnitMat(GlblViewMat); /* No inverse transform at all. */ } printf("Automatic Hidden Line generator\t\tGershon Elber\t\tJan. 1989\n"); EdgeCount = 0; /* Output the viewing matrices. */ SaveCurrentMat(); /* Output the OBJECT keyword line: */ printf("[OBJECT [COLOR %d] Hidden\n", HIDDEN_COLOR); for (i = 0; i < EDGE_HASH_TABLE_SIZE; i++) if ((PEtemp = EdgeHashTable[i]) != NULL)/* If any edge in that list. */ while (PEtemp) { EdgeCount++; fprintf(stderr, "\b\b\b\b\b%5d", EdgeCount); PEtail = PEtemp -> Pnext; /* OutputEdge destroy it. */ if ((!PEtemp -> Internal || GlblInternal) && VisibleEdge(i, PEtemp, PolyHashTable)) OutputEdge(PEtemp); PEtemp = PEtail; } EmitOutEdgeHashTable(); /* Output all polylines. */ printf("]\n"); /* Close the Hidden object. */ fprintf(stderr, ", %d seconds.", time(NULL) - SaveTime); } /***************************************************************************** * Routine to save current view trans. IritPrsrViewMat to a generic mat file * *****************************************************************************/ static void SaveCurrentMat(void) { int i, j; printf("[OBJECT MATRICES\n [OBJECT VIEW_MAT\n\t[MATRIX"); for (i = 0; i < 4; i++) { printf("\n\t "); for (j = 0; j < 4; j++) printf("%12lf ", IritPrsrViewMat[i][j]); } printf("\n\t]\n ]\n"); if (IritPrsrWasPrspMat) { printf( " [OBJECT PRSP_MAT\n\t[MATRIX"); for (i = 0; i < 4; i++) { printf("\n\t "); for (j = 0; j < 4; j++) printf("%12lf ", IritPrsrPrspMat[i][j]); } printf("\n\t]\n ]\n"); } printf("]\n\n"); } /***************************************************************************** * Routine to output one edge to the OutEdgeHashTable. * * The edge is inserted into OutEdgeHashTable to be able to match it into * * the longest path possible - connecting edges into edge sequence. * * Each edge is inserted by its Ymin, which will cause the paths be in * * increased Y order... * *****************************************************************************/ static void OutputEdge(EdgeStruct *PEtemp) { int Level; Level = (int) ((PEtemp -> Vertex[0] -> Coord[1] + 1.0) * EDGE_HASH_TABLE_SIZE2); PEtemp -> Pnext = OutEdgeHashTable[Level]; OutEdgeHashTable[Level] = PEtemp; } /***************************************************************************** * Routine to scan the OutEdgeHashTable, and Emit the longest paths found in * * it by searching for consecutive edges and combining colinear edges. * *****************************************************************************/ static void EmitOutEdgeHashTable(void) { int i, j, Colinear, Count; RealType Vec[MAX_POLYLINE_SIZE][3]; EdgeStruct *PEtemp, *PEnext; for (i = 0; i < EDGE_HASH_TABLE_SIZE; i++) /* Scan all the hash table. */ while (OutEdgeHashTable[i]) { /* Scan all edges in entry. */ PEtemp = OutEdgeHashTable[i]; /* Take edge out of table. */ OutEdgeHashTable[i] = OutEdgeHashTable[i] -> Pnext; /* Print first vertices of polyline: */ Count = 0; MultVecby4by4(Vec[Count++], PEtemp -> Vertex[0] -> Coord, GlblViewMat); MultVecby4by4(Vec[Count++], PEtemp -> Vertex[1] -> Coord, GlblViewMat); while ((PEnext = FoundMatchEdge(&Colinear, PEtemp)) != NULL) { if (Colinear) /* Overwrite last entry. */ MultVecby4by4(Vec[Count - 1], PEnext -> Vertex[1] -> Coord, GlblViewMat); else MultVecby4by4(Vec[Count++], PEnext -> Vertex[1] -> Coord, GlblViewMat); if (Count >= MAX_POLYLINE_SIZE) break; PEtemp = PEnext; } printf(" [POLYLINE %d\n", Count); for (j = 0; j < Count; j++) printf("\t[%s %s %s]\n", Real2Str(Vec[j][0]), Real2Str(Vec[j][1]), Real2Str(Vec[j][2])); printf(" ]\n"); } } /***************************************************************************** * Convert a real number into a string. * * The routine mentains 3 different buffers simultanuously so up to 3 calls * * can be issued from same printf... * *****************************************************************************/ static char *Real2Str(RealType R) { static int i = 0; static char Buffer[3][LINE_LEN_LONG]; int j, k; if (ABS(R) < 1e-6) R = 0.0; /* Round off very small numbers. */ sprintf(Buffer[i], "%-8.6lg", R); for (k = 0; !isdigit(Buffer[i][k]) && k < LINE_LEN_LONG; k++); if (k >= LINE_LEN_LONG) { fprintf(stderr, "Conversion of real number failed.\n"); MyExit(3); } 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) % 3; return Buffer[j]; } /***************************************************************************** * Routine to scan the OutEdgeHashTable for a match edge if any, delete it * * from HashTable and return it. if colinear with PEdge set Colinear to TRUE. * * return FALSE if no match found (end of polyline). * *****************************************************************************/ static EdgeStruct *FoundMatchEdge(int *Colinear, EdgeStruct *PEdge) { int Level; EdgeStruct *PEtemp, *PElast; Level = (int) ((PEdge -> Vertex[1] -> Coord[1] + 1.0) * EDGE_HASH_TABLE_SIZE2); PEtemp = PElast = OutEdgeHashTable[Level]; while (PEtemp) { /* First scan for colinear edge. */ if (FuzzSamePoints(PEdge -> Vertex[1] -> Coord, PEtemp -> Vertex[0] -> Coord) && ColinearPoints(PEdge -> Vertex[0] -> Coord, PEdge -> Vertex[1] -> Coord, PEtemp -> Vertex[1] -> Coord)) { *Colinear = TRUE; /* Delete that edge from hash table structure: */ if (PEtemp == PElast) OutEdgeHashTable[Level] = OutEdgeHashTable[Level] -> Pnext; else PElast -> Pnext = PEtemp -> Pnext; if (FuzzSamePoints(PEtemp -> Vertex[0] -> Coord, PEtemp -> Vertex[1] -> Coord)) return FoundMatchEdge(Colinear, PEdge); /* Call recursively! */ else return PEtemp; } PElast = PEtemp; PEtemp = PEtemp -> Pnext; } PEtemp = PElast = OutEdgeHashTable[Level]; while (PEtemp) { /* Next scan for any match edge. */ if (FuzzSamePoints(PEdge -> Vertex[1] -> Coord, PEtemp -> Vertex[0] -> Coord)) { *Colinear = FALSE; /* Delete that edge from hash table structure: */ if (PEtemp == PElast) OutEdgeHashTable[Level] = OutEdgeHashTable[Level] -> Pnext; else PElast -> Pnext = PEtemp -> Pnext; if (FuzzSamePoints(PEtemp -> Vertex[0] -> Coord, PEtemp -> Vertex[1] -> Coord)) return FoundMatchEdge(Colinear, PEdge); /* Call recursively! */ else return PEtemp; } PElast = PEtemp; PEtemp = PEtemp -> Pnext; } return NULL; /* No match - end of polyline. */ } /***************************************************************************** * Routine to test if two points are almost the same, and returns TRUE if so. * *****************************************************************************/ static int FuzzSamePoints(RealType Pt1[3], RealType Pt2[3]) { return (APX_EQ(Pt1[0], Pt2[0]) && APX_EQ(Pt1[1], Pt2[1]) && APX_EQ(Pt1[2], Pt2[2])); } /***************************************************************************** * Routine to test if three points are colinear, and returns TRUE if so... * * Algorithm: cross product should be zero if colinear... * * Note this routine does not return TRUE if Pt2 is not between Pt1..Pt3. * *****************************************************************************/ static int ColinearPoints(RealType Pt1[3], RealType Pt2[3], RealType Pt3[3]) { int i; RealType Xout, Yout, Zout, U[3], V[3], temp; for (i = 0; i < 3; i++) { U[i] = Pt2[i] - Pt1[i]; V[i] = Pt3[i] - Pt2[i]; } temp = sqrt(SQR(U[0]) + SQR(U[1]) + SQR(U[2])); if (APX_EQ(temp, 0.0)) return TRUE; for (i = 0; i < 3; i++) U[i] = U[i] / temp; temp = sqrt(SQR(V[0]) + SQR(V[1]) + SQR(V[2])); if (APX_EQ(temp, 0.0)) return TRUE; for (i = 0; i < 3; i++) V[i] = V[i] / temp; /* Xoutput = Uy * Vz - Uz * Vy. */ Xout = U[1] /* Uy */ * V[2] /* Vz */ - U[2] /* Uz */ * V[1] /* Vy */; /* Youtput = Uz * Vx - Ux * Vz. */ Yout = U[2] /* Uz */ * V[0] /* Vx */ - U[0] /* Ux */ * V[2] /* Vz */; /* Zoutput = Ux * Vy - Uy * Vx. */ Zout = U[0] /* Ux */ * V[1] /* Vy */ - U[1] /* Uy */ * V[0] /* Vx */; return APX_EQ(Xout, 0.0) && APX_EQ(Yout, 0.0) && APX_EQ(Zout, 0.0) && ((MIN(Pt1[0], Pt3[0]) < Pt2[0]) && (MAX(Pt1[0], Pt3[0]) > Pt2[0]) && (MIN(Pt1[1], Pt3[1]) < Pt2[1]) && (MAX(Pt1[1], Pt3[1]) > Pt2[1])); } /***************************************************************************** * Routine to test the visibility of the given edge relative to all polygons * * in polygon list. return TRUE if the edge is visible. It is assumed that * * the edge is whole visible or whole invisible (Pass 3 broke the edge if * * that whas not true). Also it is assumed the polygons are all convex. * * A short cut is made to test the edge only against the needed polygons * * only, using the polygon hash table and Y level sorting. * *****************************************************************************/ static int VisibleEdge(int EdgeMinY, EdgeStruct * PEdge, IPPolygonStruct *PolyHashTable[]) { int EdgeMaxY, i; IPPolygonStruct *PPolyList, *PPolyLast; EdgeMaxY = MIN((MAX(PEdge -> Vertex[0] -> Coord[1], PEdge -> Vertex[1] -> Coord[1]) + 1.0) * EDGE_HASH_TABLE_SIZE2, EDGE_HASH_TABLE_SIZE1); /* Test the edge only in the interval 0..EdgeMaxY as these are the only */ /* which might hide that edge. Also polygons with MaxY less than */ /* EdgeMinY are deleted from PolyHashTable as it is assumed the edges */ /* are scanned in increasing order of the EdgeHashTable. */ for (i = 0; i <= EdgeMaxY; i++) { PPolyList = PPolyLast = PolyHashTable[i]; while (PPolyList) { if ((PPolyList -> Ymax + 1.0) * EDGE_HASH_TABLE_SIZE2 < EdgeMinY) /* Delete this polygon! */ if (PPolyList == PPolyLast) { PolyHashTable[i] = PPolyLast = PPolyList -> Pnext; PPolyList = PPolyList -> Pnext; } else { PPolyList = PPolyList -> Pnext; PPolyLast -> Pnext = PPolyList; } else { /* Polygon still active - test edge against it. */ /* If found one polygon that hides this edge return FALSE... */ if (!VisibleEdgeOnePoly(PEdge, PPolyList)) return FALSE; PPolyLast = PPolyList; PPolyList = PPolyList -> Pnext; } } } return TRUE; } /***************************************************************************** * Routine to test the visibility of the given edge relative to one polygon * * return TRUE if the edge is visible. It is assumed that the edge is whole * * visible or whole invisible (Pass 3 broke the edge if that was not true). * * Also it is assumed the polygons are all convex. * *****************************************************************************/ static int VisibleEdgeOnePoly(EdgeStruct *PEdge, IPPolygonStruct *PPoly) { int i; RealType MidPt[3]; IPVertexStruct *PList; for (i = 0; i < 3; i++) MidPt[i] = /* Calc a mid point on the edge: */ (PEdge -> Vertex[0] -> Coord[i] + PEdge -> Vertex[1] -> Coord[i]) / 2.0; /* Test if edges is out of polygon boundary box: */ if (MidPt[0] > PPoly -> Xmax || MidPt[0] < PPoly -> Xmin || MidPt[1] > PPoly -> Ymax || MidPt[1] < PPoly -> Ymin) return TRUE; if (PPoly -> Plane[0] * MidPt[0] + PPoly -> Plane[1] * MidPt[1] + PPoly -> Plane[2] * MidPt[2] + PPoly -> Plane[3] > EDGE_ON_PLANE_EPS) return TRUE; /* Edge in front of polygon. */ /* We cannt escape it - we now must find if the point is included in */ /* polygon area. We assume the polygon is convex and use the fact */ /* that the polygon list is ordered such that the cross product */ /* of two consecutive vertices and the point is positive for all */ /* consecutive vertices pairs iff the point is in the polygon. */ PList = PPoly -> PVertex; while (PList -> Pnext) { if (CrossProd(PList -> Coord, PList -> Pnext -> Coord, MidPt) < 0) return TRUE; /* Out of polygon! */ PList = PList -> Pnext; } /* Now test last polygon edge (last point, first point in poly list) */ if (CrossProd(PList -> Coord, PPoly -> PVertex -> Coord, MidPt) < 0) return TRUE; /* Out of polygon! */ return FALSE; } /***************************************************************************** * Procedure to calculate the INVERSE of a given MatrixType matrix A which is * * modified to the inverted matrix. Return TRUE if inverse exists. * *****************************************************************************/ static int InverseMatrix(MatrixType A) { MatrixType InvA; int i, j, k; RealType V, D; GenUnitMat(InvA); for (i = 0; i < 4; i++) { V = A[i][i]; /* Find the new pivot. */ k = i; for (j = i + 1; j < 4; j++) if (A[j][i] > V) { /* Find maximum on col i, row i+1..4. */ V = A[j][i]; k = j; } j = k; if (i != j) for (k = 0; k < 4; k++) { /* Swap. */ D = A[i][k]; A[i][k] = A[j][k]; A[j][k] = D; D = InvA[i][k]; InvA[i][k] = InvA[j][k]; InvA[j][k] = D; } for (j = i + 1; j < 4; j++) { /* Eliminate col i from row i+1..4. */ V = A[j][i] / A[i][i]; for (k = 0; k < 4; k++) { A[j][k] -= V * A[i][k]; InvA[j][k] -= V * InvA[i][k]; } } } for (i = 4-1; i >= 0; i--) { /* Back Substitution. */ if (A[i][i] == 0) return FALSE; /* Error. */ for (j = 0; j < i; j++) { /* Eliminate col i from row 1..i-1. */ V = A[j][i] / A[i][i]; for (k = 0; k < 4; k++) { /* A -> [j][k] -= V * A[i][k]; */ InvA[j][k] -= V * InvA[i][k]; } } } for (i = 0; i < 4; i++) /* Normalize the inverse Matrix. */ for (j = 0; j < 4; j++) InvA[i][j] /= A[i][i]; for (i = 0; i < 4; i++) /* Copy inverted matrix to A. */ for (j = 0; j < 4; j++) A[i][j] = InvA[i][j]; return TRUE; }