home *** CD-ROM | disk | FTP | other *** search
- /*****************************************************************************
- * "Irit" - the 3d polygonal solid modeller. *
- * *
- * Written by: Gershon Elber Ver 0.2, Mar. 1990 *
- ******************************************************************************
- * Module to handle the low level boolean operations. The routines in this *
- * module should be accessed from "bool-hi.c" module only. *
- * Note the polygons of the two given objects may not be convex, and must *
- * be subdivided into convex ones in the boolean upper level routines (module *
- * bool-hi.c). All the routines of this module expects objects with convex *
- * polygons only although they might return objects with non convex polygons, *
- * but marked as so (via polygons CONVEX tags - see Irit.h)! *
- * Because Bool-low.c module was too big, it was subdivided to two: *
- * Bool1Low.c - mainly handles the intersection polyline between the oper. *
- * Bool2Low.c - mainly handles the polygon extraction from operands given the *
- * polyline of intersection and the adjacencies (see ADJACNCY.C) *
- * Note we said mainly has routines CAN call one another! *
- *****************************************************************************/
-
- /* #define DEBUG If defined, return intersection POLYLINE object. */
- /* #define DEBUG2 If defined, defines some printing routines. */
- /* #define DEBUG3 Print messages to entry/exit from routines. */
-
- #ifdef __MSDOS__
- #include <alloc.h>
- #include <dos.h>
- #endif /* __MSDOS__ */
-
- #include <stdio.h>
- #include <ctype.h>
- #include <math.h>
- #include <string.h>
- #include "program.h"
- #include "allocatg.h"
- #include "convexg.h"
- #include "objects.h"
- #include "booleang.h"
- #include "booleanl.h"
- #include "geomat3d.h"
-
- static int TestAinB(VertexStruct *V, PolygonStruct *Pl, int AinB);
- static struct PolygonStruct *ClipOpenLoopFromPoly(PolygonStruct *Pl,
- InterSegListStruct *OLoop, int AinB);
- static void ClosedLoopsToPolys(InterSegListStruct *PClosed, PolygonStruct *Pl);
- static VertexStruct *CutPolygonAtRay(PolygonStruct *Pl, PointType Pt);
- static CombineClosedLoops(PolygonStruct *Pl, InterSegListStruct *PClosed,
- int AinB);
- static void PushAdjOnStack(PolygonStruct *Pl, PolygonStruct *AdjStack[],
- int *StackPointer);
- static struct PolygonStruct *ChainPolyLists(PolygonStruct *Pl1,
- PolygonStruct *Pl2);
-
- #ifdef DEBUG2
- static void PrintVrtxList(VertexStruct *V);
- static void PrintInterList(InterSegmentStruct *PInt);
- #endif /* DEBUG2 */
-
- /*****************************************************************************
- * Test on which side of polygon Pl, given Point V is, and according to *
- * the requiremnet (AinB) returns TRUE/FALSE. *
- * If test on V fails, we tries the next one V -> Pnext until success... *
- *****************************************************************************/
- static int TestAinB(VertexStruct *V, PolygonStruct *Pl, int AinB)
- {
- int In;
- RealType Distance;
- VertexStruct *VHead = V;
-
- do {
- Distance = Pl -> Plane[0] * V -> Pt[0] +
- Pl -> Plane[1] * V -> Pt[1] +
- Pl -> Plane[2] * V -> Pt[2] + Pl -> Plane[3];
- In = Distance > 0.0;
- V = V -> Pnext;
- }
- while (ABS(Distance) < EPSILON && V != NULL && V != VHead);
-
- return (In && AinB) || (!In && !AinB); /* I wish I has logical XOR ... */
- }
-
- /*****************************************************************************
- * Convert an inter loop into an open vertex list. *
- *****************************************************************************/
- struct VertexStruct *InterLoopToVrtxList(InterSegmentStruct *PIHead)
- {
- VertexStruct *VHead, *V;
-
- VHead = V = AllocVertex(0, 0, NULL, NULL);
-
- PT_COPY(VHead -> Pt, PIHead -> PtSeg[0]);
-
- while (PIHead != NULL) {
- V -> Pnext = AllocVertex(0, 0, NULL, NULL);
- V = V -> Pnext;
- PT_COPY(V -> Pt, PIHead -> PtSeg[1]);
-
- PIHead = PIHead -> Pnext;
- }
-
- V -> Pnext = NULL;
-
- return VHead;
- }
-
- /*****************************************************************************
- * Clip an open loop from a polygon: *
- * 1. Clip the section (S) of the polygon between the two loop end points and *
- * replace it by the loop itself. *
- * 2. If the polygon formed from S and the loop should be in the output *
- * (as tested by AinB) return that polygon. Otherwise return NULL. *
- * The open loop itself (excluding the header OLoop) is freed. *
- * Note it is assumed (ordered by the sorting routines above) that the open *
- * loop starts from the second end back to first: *
- * *
- * L1-----------------------L2 *
- * | | *
- * |L0 |L3 *
- * *---------------*-------+----*-------------*----+-----------* *
- * P0 P1 P2 P3 P0 *
- *****************************************************************************/
- static struct PolygonStruct *ClipOpenLoopFromPoly(PolygonStruct *Pl,
- InterSegListStruct *OLoop, int AinB)
- {
- int GenClipPoly; /* TRUE if needs to form polygon from S & OLoop. */
- PointType Pt;
- VertexStruct *VStart, *VEnd, *VEnd1, /* Corresponds to L0 and L3... */
- *ClipPoly, /* The clipped element from polygon. */
- *PLoop, *PRevLoop, /* The loop itself as vertex list. */
- *PLoopEnd, *PLoopEnd1, *Ptemp1, *Ptemp2;
- InterSegmentStruct *PISeg, *PItemp;
- PolygonStruct *ClipPl;
-
- #ifdef DEBUG3
- printf("Enter ClipOpenLoopFromPoly\n");
- #endif /* DEBUG3 */
-
- PISeg = OLoop -> PISeg;
- VStart = PISeg -> V[0];
- while (PISeg -> Pnext != NULL) PISeg = PISeg -> Pnext;
- VEnd = PISeg -> V[1];
- if (VStart == NULL || VEnd == NULL)
- FatalError("ClipOpenLoopFromPoly: None open loop\n");
- VEnd1 = VEnd; /* Find the pointer thats points on VEnd. */
- while (VEnd1 -> Pnext != VEnd) VEnd1 = VEnd1 -> Pnext;
-
- PLoop = InterLoopToVrtxList(OLoop -> PISeg);/* Make vertex list out of it*/
- PLoopEnd = PLoop; /* Prepare pointer on last vertex. */
- while (PLoopEnd -> Pnext != NULL) PLoopEnd = PLoopEnd -> Pnext;
- PLoopEnd1 = PLoop;
- while (PLoopEnd1 -> Pnext != PLoopEnd) PLoopEnd1 = PLoopEnd1 -> Pnext;
-
- if (VStart != VEnd) {
- /* Now we test if we need to create a polygon formed from S & open */
- /* loop by testing on which side of the polygon that caused */
- /* intersection L0L1, point P2 is, and compare with requirement AinB.*/
- GenClipPoly = TestAinB(VStart -> Pnext, OLoop -> PISeg -> Pl, AinB);
-
- /* Keep the clipped VertexList P2, P3 & substitute with open loop: */
- /* Note we must keep vertex VEnd in the polygon as another InterSeg. */
- /* may point on it, so we replace InterSeg point (L3) by (P3) and */
- /* leave VEnd intact. */
- Ptemp1 = VEnd -> Pnext; /* Save that pointer temporary. */
- VEnd -> Pnext = NULL; /* Close the clipped vertex list. */
-
- PT_COPY(Pt, VEnd -> Pt);
- PT_COPY(VEnd -> Pt, PLoopEnd -> Pt);
- PT_COPY(PLoopEnd -> Pt, Pt);
- VEnd1 -> Pnext = PLoopEnd;
- PLoopEnd1 -> Pnext = VEnd;
- PLoopEnd -> Count = VEnd -> Count;
- PLoopEnd -> Tags = VEnd -> Tags;
-
- Ptemp2 = VEnd;
- VEnd = PLoopEnd;
- PLoopEnd = Ptemp2;
-
- ClipPoly = VStart -> Pnext;
-
- /* New ClipPoly is isolated (Open loop of P2, P3 only). Save */
- /* reversed list of open loop if we need to form an S/OLoop polygon, */
- /* otherwise free ClipPoly. Chain the OLoop instead of S. */
- if (GenClipPoly) PRevLoop = GenReverseVrtxList(PLoop);
- else MyFree((char *) ClipPoly, VERTEX_TYPE);
- VStart -> Pnext = PLoop; /* Chain the OLoop instead of S. */
- PLoopEnd -> Pnext = Ptemp1;
- }
- else { /* VStart == VEnd */
- /* Now we test if we need to create a polygon formed from S & open */
- /* loop by testing on which side of the polygon that caused */
- /* intersection L0L1, point L3 is, and compare with requirement AinB.*/
- GenClipPoly = TestAinB(PLoopEnd, OLoop -> PISeg -> Pl, AinB);
-
- /* In this case the clipped part is empty, so its simpler: */
- ClipPoly = NULL;
-
- /* Save reversed list of open loop if we need to form an S/OLoop */
- /* polygon. Chain the OLoop instead of S. */
- if (GenClipPoly) PRevLoop = GenReverseVrtxList(PLoop);
-
- PLoopEnd -> Pnext = VEnd -> Pnext;
- VStart -> Pnext = PLoop; /* Chain the OLoop instead of S. */
- }
-
- /* Time to free the InterSegment list pointed by OLoop: */
- PISeg = OLoop -> PISeg;
- while (PISeg != NULL) {
- PItemp = PISeg;
- PISeg = PISeg -> Pnext;
- MyFree((char *) PItemp, OTHER_TYPE);
- }
- OLoop -> PISeg = NULL; /* To be on the safe side... */
-
- /* There is a chance that Pl -> V will point on vertex in the clipped */
- /* part so we update it to point on VStart, which for sure is in polygon.*/
- Pl -> V = VStart;
- if (GenClipPoly) { /* Generate the polygon from ClipPoly & PRevLoop: */
- PLoopEnd = PRevLoop;
- while (PLoopEnd -> Pnext != NULL) PLoopEnd = PLoopEnd -> Pnext;
-
- if (ClipPoly == NULL) {
- PLoopEnd -> Pnext = PRevLoop; /* Close that loop and return it. */
- ClipPl = AllocPolygon(0, 0, PRevLoop, NULL);
- PLANE_COPY(ClipPl -> Plane, Pl -> Plane);
- RST_CONVEX_POLY(ClipPl); /* May be not convex now. */
- return ClipPl;
- }
-
- PLoopEnd -> Pnext = ClipPoly;
- PLoopEnd -> Tags = VStart -> Tags;
- PLoopEnd -> Count = VStart -> Count;
- Ptemp1 = ClipPoly;
- while (Ptemp1 -> Pnext != NULL) Ptemp1 = Ptemp1 -> Pnext;
- Ptemp1 -> Pnext = PRevLoop;
-
- ClipPl = AllocPolygon(0, 0, ClipPoly, NULL);
- PLANE_COPY(ClipPl -> Plane, Pl -> Plane);
- RST_CONVEX_POLY(ClipPl); /* May be not convex now. */
- return ClipPl;
- }
- else return NULL;
- }
-
- /*****************************************************************************
- * Find the intersection of the ray fired from Pt to +X direction with the *
- * given polygon. Note Pt MUST be in the polygon. Two vertices equal to *
- * ray/polygon intersection point are added to polygon vertex list, and a *
- * pointer to the first one is also returned. This routine is exclusively *
- * used by the CombineClosedLoops below. *
- * The polygon is NOT assumed to be convex and we look for the minimum X *
- * intersection. The polygon might not be convex as a result of combinning *
- * some other closed loop before the current one... *
- *****************************************************************************/
- static VertexStruct *CutPolygonAtRay(PolygonStruct *Pl, PointType Pt)
- {
- int OnVertex;
- RealType MinX = INFINITY, X;
- VertexStruct *V, *Vnext, *VMinX = NULL;
-
- V = Pl -> V;
- do {
- Vnext = V -> Pnext;
-
- /* A polygon edge might intersect the ray iff one of the following: */
- /* 1. The first vertex is exactly on the ray Y level. (if this is */
- /* true for the second edge, it will be detected next iteration). */
- /* 2. The first vertex is below ray Y level, and second is above. */
- /* 3. The first vertex is above ray Y level, and second is below. */
- if (APX_EQ(V -> Pt[1], Pt[1])) { /* Case 1 above. */
- if (MinX > V -> Pt[0] && Pt[0] < V -> Pt[0]) {
- OnVertex = TRUE;
- MinX = V -> Pt[0];
- VMinX = V;
- }
- }
- else
- if ((V -> Pt[1] < Pt[1] && Vnext -> Pt[1] > Pt[1]) ||/* Case 2 above.*/
- (V -> Pt[1] > Pt[1] && Vnext -> Pt[1] < Pt[1])) {/* Case 3 above.*/
- X = ((Vnext -> Pt[1] - Pt[1]) * V -> Pt[0] +
- (Pt[1] - V -> Pt[1]) * Vnext -> Pt[0]) /
- (Vnext -> Pt[1] - V -> Pt[1]);
- if (MinX > X && Pt[0] < X) {
- OnVertex = FALSE;
- MinX = X;
- VMinX = V;
- }
-
- }
-
- V = Vnext;
- }
- while (V != NULL && V != Pl -> V);
-
- if ((V = VMinX) == NULL)
- FatalError("CutPolygonAtRay: fail to find intersection");
-
- /* Now that we have the intersection point - create two new vertices */
- /* (one if OnVertex), insert them (it) after V and return the first. */
- if (OnVertex) {
- V -> Pnext = AllocVertex(V -> Count, V -> Tags, NULL, V -> Pnext);
- PT_COPY(V -> Pnext -> Pt, V -> Pt);
- V -> Tags = V -> Count = 0;
- }
- else {
- V -> Pnext = AllocVertex(V -> Count, V -> Tags, NULL, V -> Pnext);
- Vnext = V -> Pnext;
- Vnext -> Pt[0] = MinX; /* X - as intersection point found. */
- Vnext -> Pt[1] = Pt[1]; /* Y - must be as ray Y level. */
- Vnext -> Pt[2] = V -> Pt[2]; /* Z - all polygon has same Z value. */
-
- V -> Pnext = AllocVertex(0, 0, NULL, V -> Pnext);
- V = V -> Pnext;
- PT_COPY(V -> Pt, Vnext -> Pt);
- }
-
- return V;
- }
-
- /*****************************************************************************
- * Convert the given closed loop list to polygons, and return them. the *
- * original given polygon vertex list is freed, and the first loop is subst. *
- * instead. *
- *****************************************************************************/
- static void ClosedLoopsToPolys(InterSegListStruct *PClosed, PolygonStruct *Pl)
- {
- int LoopNum = 0;
- VertexStruct *V, *VHead;
- InterSegmentStruct *PISeg, *PItemp;
- InterSegListStruct *PClosedTemp;
-
- MyFree((char *) Pl -> V, VERTEX_TYPE);
- Pl -> Pnext = NULL;
- SET_INOUTPUT_POLY(Pl); /* Mark the polygon as in output. */
-
- while (PClosed != NULL) {
- /* Convert the InterList to vertex list and free the first: */
- V = VHead = InterLoopToVrtxList(PClosed -> PISeg);
- if (V -> Pnext == NULL || V -> Pnext -> Pnext == NULL)
- FatalError("ClosedLoopsToPolys: Closed loop with less than 3 vertices");
-
- PISeg = PClosed -> PISeg; /* Time to free the InterSegmentList: */
- while (PISeg != NULL) {
- PItemp = PISeg;
- PISeg = PISeg -> Pnext;
- MyFree((char *) PItemp, OTHER_TYPE);
- }
-
- while (V -> Pnext -> Pnext != NULL) V = V -> Pnext; /* Go to last pt */
- MyFree((char *) V -> Pnext, VERTEX_TYPE);/* and free - same as first.*/
- V -> Pnext = VHead; /* Make vertex list circular. */
-
- if (LoopNum++) {
- Pl -> Pnext = AllocPolygon(0, 0, VHead, Pl -> Pnext);
- PLANE_COPY(Pl -> Pnext -> Plane, Pl -> Plane);
- }
- else {
- Pl -> V = VHead;
- }
-
- PClosedTemp = PClosed;
- PClosed = PClosed -> Pnext;
- MyFree((char *) PClosedTemp, OTHER_TYPE);
- }
- }
-
- /*****************************************************************************
- * This routines cuts the given polygon into its interior closed loops by *
- * adding an edge from the polygon boundary to each of its closed loops. *
- * For example: *
- * *
- * +-----------------------+ +-----------------------+ *
- * | | | | *
- * | / \ / \ | | / \________ / \ | *
- * | \ / | | | | \ / | |_____| *
- * | _ \ / | --> | _ \ / | *
- * | / \_ | --> | / \_ | *
- * | | | | | | |_____________| *
- * | \__/ | | \__/ | *
- * | | | | *
- * +-----------------------+ +-----------------------+ *
- * *
- * Algorithm: *
- * 1. Transform the polygon and the closed loops to a plane parallel to XY *
- * plane (Z = Const plane). *
- * 2. For each loop find its MaxX while keeping the information on the *
- * vertex on that extremum. *
- * 3. For the loop with the biggest MaxX: *
- * 3.1. Use that extremum vertex (which must be non concave corner) to *
- * test if loop is in the reverse direction the polygon itself is, *
- * and reverse it if not. *
- * 3.2. Fire a ray from the extremum vertex, to the +X direction outside *
- * of the loop till it intersect the polygon, break the polygon at *
- * that point and add two edges to beginning of loop from breaking *
- * point and from end of loop to breaking point (beginning/end point *
- * of loop is the extremum vertex point). *
- * 4. Repeat step 3, with all loops. *
- * 5. Transfrom the new polygon back (using the inverse matrix of step 1) *
- * to its original orientation. *
- * *
- * Returns TRUE iff the original polygon boundary is in output. *
- * *
- *****************************************************************************/
- static int CombineClosedLoops(PolygonStruct *Pl, InterSegListStruct *PClosed,
- int AinB)
- {
- RealType MaxX;
- PointType V1, V2, Normal, PlNormal;
- MatrixType RotMat;
- VertexStruct *V, *Vnext, *Vprev, *VHead, *VMaxX, VStruct;
- InterSegListStruct *PClosedTemp, *PClosedMaxX, *PClosedLast,
- *PClosedMaxXLast;
- InterSegmentStruct *PISeg, *PItemp, *PISegMaxX;
-
- Pl -> Pnext = NULL; /* Make sure this polygon is disconnected. */
-
- /* Stage 1 - Transform the vertices to a plane parallel to XY plane: */
- GenRotateMatrix(RotMat, Pl -> Plane);
- V = VHead = Pl -> V;
- do { /* Transform the polygon itself. */
- MatMultVecby4by4(V -> Pt, V -> Pt, RotMat);
- V = V -> Pnext;
- }
- while (V != NULL && V != VHead);
-
- PClosedTemp = PClosed;
- while (PClosedTemp != NULL) { /* And transform the loops also. */
- PISeg = PClosed -> PISeg;
- while (PISeg != NULL) {
- MatMultVecby4by4(PISeg -> PtSeg[0], PISeg -> PtSeg[0], RotMat);
- MatMultVecby4by4(PISeg -> PtSeg[1], PISeg -> PtSeg[1], RotMat);
-
- PISeg = PISeg -> Pnext;
- }
-
- PClosedTemp = PClosedTemp -> Pnext;
- }
- if (!MatInverseMatrix(RotMat, RotMat)) /* Find the inverse matrix. */
- FatalError("CombineClosedLoops: Inverse matrix does not exits");
-
- /* Evalaute the normal to the polygon (which must be convex!). Note we */
- /* cannt simply use the Plane normal as the polygon was transformed. */
- V = Pl -> V;
- do {
- Vnext = V -> Pnext;
- PT_SUB(V1, Vnext -> Pt, V -> Pt);
- PT_SUB(V2, Vnext -> Pnext -> Pt, Vnext -> Pt);
- VecCrossProd(PlNormal, V1, V2);
- V = Vnext;
- }
- while (PT_LENGTH(PlNormal) < EPSILON);
-
- PClosedTemp = PClosed;
- while (PClosedTemp != NULL) {
- /* Stage 2 - find MaxX extremum value of given loop, test the loop */
- /* direction, and reverse it if wrong. Note we convert the loop */
- /* given as InterSegListStruct list into VertexStruct list first. */
- PISegMaxX = PISeg = PClosedTemp -> PISeg;
- MaxX = PISeg -> PtSeg[0][0]; /* Get first vertex X val. */
- PISeg = PISeg -> Pnext;
- while (PISeg)
- {
- if (PISeg -> PtSeg[0][0] > MaxX) {
- MaxX = PISeg -> PtSeg[0][0];
- PISegMaxX = PISeg;
- }
- PISeg = PISeg -> Pnext;
- }
- PClosedTemp -> PISegMaxX = PISegMaxX;
-
- PClosedTemp = PClosedTemp -> Pnext;
- }
-
- /* Stage 3/4 - find the loop with biggest MaxX and combine it with the */
- /* polygon itself. Do it for all closed loops, in list: */
- PClosedTemp = PClosed;
- while (PClosed != NULL) {
- /* Find the one with maximum MaxX, and delete it from PClosed list. */
- PClosedLast = PClosedMaxX = PClosedTemp = PClosed;
- MaxX = PClosedMaxX -> PISegMaxX -> PtSeg[0][0];
- while (PClosedTemp != NULL)
- {
- if (PClosedTemp -> PISegMaxX -> PtSeg[0][0] > MaxX) {
- PClosedMaxX = PClosedTemp;
- PClosedMaxXLast = PClosedLast;
- }
- PClosedLast = PClosedTemp;
- PClosedTemp = PClosedTemp -> Pnext;
- }
- if (PClosedMaxX == PClosed) PClosed = PClosed -> Pnext; /* Del. from */
- else PClosedMaxXLast -> Pnext = PClosedMaxX -> Pnext;/* PClosed list.*/
-
- /* Create a vertex list from the loop: */
- V = VHead = InterLoopToVrtxList(PClosedMaxX -> PISeg);
- if (V -> Pnext == NULL || V -> Pnext -> Pnext == NULL)
- FatalError("CombineClosedLoops: Closed loop with less than 3 vertices");
-
- V = VHead;
- while (V -> Pnext -> Pnext != NULL) V = V -> Pnext; /* Go to last pt */
- MyFree((char *) V -> Pnext, VERTEX_TYPE);/* and free - same as first.*/
- V -> Pnext = VHead; /* Make vertex list circular. */
-
- PISegMaxX = PClosedMaxX -> PISegMaxX;
-
- /* Now test if the vertex list should be reversed. Find a vertex */
- /* which is below MaxX but its successor is on MaxX. The 3 vertices */
- /* V, V -> Pnext (on MaxX), V -> Pnext -> Pnext, must form convex */
- /* corner which we use to test if the loop needs to be reversed: */
- while (APX_EQ(V -> Pt[0], MaxX)) V = V -> Pnext;
- while (!APX_EQ(V -> Pnext -> Pt[0], MaxX)) V = V -> Pnext;
- VMaxX = V -> Pnext;
- PT_COPY(VStruct.Pt, V -> Pt); /* Prepare in point in REAL position. */
- MatMultVecby4by4(VStruct.Pt, VStruct.Pt, RotMat);
- if (TestAinB(&VStruct, PISegMaxX -> Pl, AinB)) {
- /* The Inside of the object is actually the loop itself. In that */
- /* case we simply return all the loops converted into polygon. */
- /* This case is simple... */
- MyFree((char *) VHead, VERTEX_TYPE); /* Free loop vertex list. */
- PClosedMaxX -> Pnext = PClosed;/* Put back first loop into list. */
- PClosedTemp = PClosed = PClosedMaxX;
- while (PClosedTemp != NULL) { /* Transform the loops back. */
- PISeg = PClosed -> PISeg;
- while (PISeg != NULL) {
- MatMultVecby4by4(PISeg -> PtSeg[0], PISeg -> PtSeg[0], RotMat);
- MatMultVecby4by4(PISeg -> PtSeg[1], PISeg -> PtSeg[1], RotMat);
-
- PISeg = PISeg -> Pnext;
- }
- PClosedTemp = PClosedTemp -> Pnext;
- }
-
- ClosedLoopsToPolys(PClosedMaxX, Pl);/* And convert them to polys.*/
- return FALSE; /* Boundary is NOT part of object result. */
- }
- PT_SUB(V1, VMaxX -> Pt, V -> Pt);
- PT_SUB(V2, VMaxX -> Pnext -> Pt, VMaxX -> Pt);
- VecCrossProd(Normal, V1, V2);
- if (DOT_PROD(Normal, PlNormal) > 0) { /* Need to reverse list. */
- Vprev = VHead;
- V = Vprev -> Pnext;
- Vnext = V -> Pnext;
- do {
- V -> Pnext = Vprev;/* Reverse to point on prev instead next. */
- Vprev = V;
- V = Vnext;
- Vnext = V -> Pnext;
- }
- while (Vprev != VHead);
- }
-
- PISeg = PClosedMaxX -> PISeg; /* Time to free the InterSegmentList: */
- while (PISeg != NULL) {
- PItemp = PISeg;
- PISeg = PISeg -> Pnext;
- MyFree((char *) PItemp, OTHER_TYPE);
- }
- MyFree((char *) PClosedMaxX, OTHER_TYPE);
-
- /* O.k. lets fire a ray from VMaxX to +X direction and see where it */
- /* intersects the polygon. The routine CutPolygonAtRay will add two */
- /* vertices at the ray intersection into polygon vertex list and */
- /* return a pointer to first one, so we can chain our loop directly */
- /* between these two new vertices. */
- V = CutPolygonAtRay(Pl, VMaxX -> Pt);
- Vnext = V -> Pnext;
- /* Introduce a copy of VMaxX and successor to VMaxX: */
- VMaxX -> Pnext = AllocVertex(VMaxX -> Count, VMaxX -> Tags, NULL,
- VMaxX -> Pnext);
- PT_COPY(VMaxX -> Pnext -> Pt, VMaxX -> Pt);
- V -> Pnext = VMaxX -> Pnext;
- SET_INTERNAL_EDGE(V);
- VMaxX -> Pnext = Vnext;
- SET_INTERNAL_EDGE(VMaxX);
- }
-
- /* Stage 5 - Time to rotate polygon back to its original position. */
- V = VHead = Pl -> V;
- do {
- MatMultVecby4by4(V -> Pt, V -> Pt, RotMat);
- V = V -> Pnext;
- }
- while (V != NULL && V != VHead);
-
- SET_INOUTPUT_POLY(Pl); /* Mark the polygon as in output. */
- RST_CONVEX_POLY(Pl); /* This polygon is not convex any more. */
-
- return TRUE;
- }
-
- /*****************************************************************************
- * Push on the adjacency stack, all adjacent polygons which are complete *
- * (no intersection) and are adjacent to complete edges (originated from *
- * input polygons) of the given polygon. *
- *****************************************************************************/
- static void PushAdjOnStack(PolygonStruct *Pl, PolygonStruct *AdjStack[],
- int *StackPointer)
- {
- struct VertexStruct *V = Pl -> V;
-
- do {
- /* If you wants to propagate iff both vertices are original then */
- /* uncomment the next line. */
- if (IS_ORIGINAL_VRTX(V) /* && IS_ORIGINAL_VRTX(V -> Pnext) */
- && V -> PAdj != NULL)
- AdjStack[++*StackPointer] = V -> PAdj;
- if (*StackPointer >= ADJ_STACK_SIZE) {
- FatalError("Adjacency stack overflow, object too big\n");
- }
- V = V -> Pnext;
- }
- while (V != Pl -> V);
- }
-
- /*****************************************************************************
- * Chain two Polygon lists into one. For fast processing it is prefered the *
- * first one to be to shorter. Returns pointer to chained list. *
- *****************************************************************************/
- static struct PolygonStruct *ChainPolyLists(PolygonStruct *Pl1,
- PolygonStruct *Pl2)
- {
- PolygonStruct *Ptemp;
-
- if (Pl1 == NULL) return Pl2;
- else
- if (Pl2 == NULL) return Pl1;
- else {
- Ptemp = Pl1;
- while (Ptemp -> Pnext != NULL) Ptemp = Ptemp -> Pnext;
- Ptemp -> Pnext = Pl2;
- return Pl1;
- }
- }
-
- /*****************************************************************************
- * This routine coordinates all the extraction of the polygons from the *
- * intersecting lists. Does it in the following steps: *
- * 1. Mark all polygons with no intersection at all as complete polygons. *
- * (this is cause that this polygon will be totally in or out, according *
- * to inter-polygon adjacencies propagation...) *
- * Also Mark them as undefined (if in output or undefined) yet. *
- * Uses PolygonStruct Tags to save these bits. *
- * 2. 2.1. Convert the unordered segment list of each polygon to closed loops *
- * (if create a hole in polygon) or open (if crosses its boundary). *
- * 2.2. Order the open loops along the perimeter of the polygon (note *
- * these loops cannt intersect. For example (5 vertices polygon): *
- * -----------------------------L3 *
- * | ---------------L1 -----L2 | --------L4 --L5 *
- * | | | | | | | | | | *
- * P0 ------ P1 ------- P2 ----- P3 -------- P4 ------ P5 -------- P0 *
- * Note L1, L2 are enclosed in L3 loop, and that the order is circular*
- * 2.3. "Break" the polygon at each open loop that has no enclosed loops *
- * in it. For example we can start at L1, L2, L4, L5 and then L3. *
- * "Break" means - replace the vertex chain between the two loop end *
- * points, with the loops itself. Depends upon the relation required *
- * we may need to output a new polygon form from the deleted chain *
- * and that loop. In addition we may form a new polygon from last *
- * loop and was was left from the original polygon *
- * For each formed polygon, for each complete edge of it (i.e. edge *
- * which was originally in the polygon) test the adjacent polygon *
- * if it is complete (as marked in 1.) and if in or undefined (marked *
- * undefined in 1.) is still undefined: *
- * 2.3.1. set it to be in. *
- * 2.3.2. push it on adjacency stack. *
- * 2.4. For each closed loop - find in which polygon (from polygons *
- * created in 2.3.) it is enclosed, and decompose it. *
- * 3. While adjacencies stack not empty do: *
- * 3.1. pop top polygon from stack and output it. *
- * 3.2. For each of its edges (which obviousely must be complete edges) *
- * if adjacent polygon is complete and undefined: *
- * 3.3.1. set it to be in. *
- * 3.3.2. push it on adjacency stack. *
- * 3.3 go back to 3. *
- * *
- * The above algorithm defines in as in output, but dont be confused with *
- * the required inter-object AinB (or AoutB if FALSE), which used to *
- * determine which side of the trimming loop should be output. *
- * Note this routine may return non-convex polygons (but marked as so) even *
- * though the input for the booleans must be convex polygons only! *
- * In order to keep the given object unchanged, a whole new copy off the *
- * polygon list is made. The polygons of the list that are not in the output *
- * are freed: a global list of all polygons (pointers) is used to scan them *
- * in the end and free the unused ones (list PolysPtr). *
- *****************************************************************************/
- ObjectStruct *ExtractPolygons(ObjectStruct *PObj, int AinB)
- {
- int NumOfPolys = 0, StackPointer = -1;
- struct PolygonStruct *AdjStack[ADJ_STACK_SIZE], **PolysPtr,
- *Pl, *PlHead, *PlNext, *OutputPl = NULL, *SplPl, *NewPl;
- struct VertexStruct *V, *Vnext;
- struct InterSegListStruct *PClosed, *POpen, *Ptemp;
-
- #ifdef DEBUG3
- printf("Enter ExtractPolygons\n");
- #endif /* DEBUG3 */
-
- TestBooleanCtrlBrk();
-
- /* Stage 1. - mark all polygons as needed: */
- PlHead = Pl = PObj -> U.Pl;
- /* Gen. a copy of Pl, so we can modify the original polygon list: */
- PObj -> U.Pl = CopyPolygonList(Pl);
-
- while (Pl != NULL) {
- NumOfPolys++; /* Count number of polygons in original object. */
- if (Pl -> PAux != NULL) { /* The intersection list is not empty! */
- RST_COMPLETE_POLY(Pl); /* Mark it as non complete polygon. */
- V = Pl -> V;
- do {
- SET_ORIGINAL_VRTX(V); /* Mark vertices from original object. */
- V = V -> Pnext;
- }
- while (V != Pl -> V);
- }
- else {
- SET_COMPLETE_POLY(Pl); /* Mark it as complete polygon. */
- RST_INOUTPUT_POLY(Pl); /* And as undefined (if in output). */
- RST_ADJPUSHED_POLY(Pl); /* And as not pushed on adj. stack. */
- }
- Pl = Pl -> Pnext;
- }
-
- /* Stage 2. - scan the polygons and subdivide the intersecting ones: */
- Pl = PlHead;
-
- /* We will save pointers to ALL polygons in list so we could free in the */
- /* end the ones that are not in the output list, and therefore unused. */
- PolysPtr = (PolygonStruct **)
- MyMalloc(sizeof(PolygonStruct *) * NumOfPolys, OTHER_TYPE);
- NumOfPolys = 0;
-
- while (Pl != NULL) {
- TestBooleanCtrlBrk();
-
- PolysPtr[NumOfPolys++] = Pl; /* Save pointer to this polygon. */
-
- PlNext = Pl -> Pnext;
-
- if (!IS_COMPLETE_POLY(Pl)) {/* They are intersections with this one. */
- /* Convert the intersecting segments into open/closed loops. */
- LoopsFromInterList(Pl, &PClosed, &POpen);
-
- if (PClosed != NULL && POpen != NULL)
- FatalError("ExtractPolygons: Polygon with both open & closed loops - not supported\n");
-
- if (POpen != NULL) {
- /* Sort the Open loops into an order we can handle... */
- SortOpenInterList(Pl, &POpen);
- SplPl = NewPl = NULL; /* Keep the list of split polygons. */
-
- while (POpen != NULL) { /* Clip the open loops from polygon: */
- /* Note ClipOpenLoopFromPoly also frees the InterSegment */
- /* list pointed by POpen (but not POpen itself). */
- NewPl = ClipOpenLoopFromPoly(Pl, POpen, AinB);
- if (NewPl != NULL) { /* If loop clipped a new polygon, */
- NewPl -> Pnext = SplPl;/* add to split polygon list. */
- SplPl = NewPl;
- /* And push adj. polygons of complete edges on stack.*/
- PushAdjOnStack(NewPl, AdjStack, &StackPointer);
- }
- Ptemp = POpen;
- POpen = POpen -> Pnext;
- MyFree((char *) Ptemp, OTHER_TYPE);
- }
- /* Last clip generated nothing (NewPl == NULL) so part that */
- /* left from Pl (PlCopy) is IN output list! Add this poly: */
- if (NewPl == NULL) {
- Pl -> Pnext = SplPl; /* And chain what was left from it. */
- SplPl = Pl;
- /* And push adjacent polygons of complete edges on stack.*/
- PushAdjOnStack(Pl, AdjStack, &StackPointer);
- SET_INOUTPUT_POLY(Pl);/* So we wouldnt free that in end. */
- RST_CONVEX_POLY(Pl); /* May be not convex now. */
- }
- OutputPl = ChainPolyLists(SplPl, OutputPl);
- }
- else { /* PClosed != NULL */
- /* Make a "cut" from the loop(s) +-------+ +---+---+ */
- /* to boundary if possible, and | | | | */
- /* converting Pl to a nonconvex | / \ | -> | / \__| */
- /* polygon, that has an edge (the | \ / | -> | \ / | */
- /* cut) which is shared twice in | | | | */
- /* the same polygon +-------+ +-------+ */
- if (CombineClosedLoops(Pl, PClosed, AinB)) {
- /* If returned with TRUE - the polygon boundary is in */
- /* output, so add all its neighbours to adj. stack. */
- PushAdjOnStack(Pl, AdjStack, &StackPointer);
- }
-
- OutputPl = ChainPolyLists(Pl, OutputPl);
- }
- }
- Pl = PlNext;
- }
-
- /* Stage 3. - handling adjacencies and propagate them in polygons: */
- /* Pop off the elements from the stack, and propagate them using their */
- /* adjacencies. */
- while (StackPointer >= 0) {
- Pl = AdjStack[StackPointer--]; /* Pop top element. */
- if (!IS_COMPLETE_POLY(Pl) || /* Ignore non complete polygons. */
- IS_INOUTPUT_POLY(Pl)) continue; /* If already handled. */
-
- SET_INOUTPUT_POLY(Pl); /* Mark this one as handled for next time. */
-
- V = Pl -> V; /* Push all adjacent ones that not handled yet. */
- do {
- if (V -> PAdj &&
- IS_COMPLETE_POLY(V -> PAdj) &&
- !IS_INOUTPUT_POLY(V -> PAdj) &&
- !IS_ADJPUSHED_POLY(V -> PAdj)) {
- SET_ADJPUSHED_POLY(V -> PAdj);
- AdjStack[++StackPointer] = V -> PAdj; /* Push it on stack. */
- if (StackPointer >= ADJ_STACK_SIZE)
- FatalError("Adjacency stack overflow, object too big\n");
- }
- V = V -> Pnext;
- }
- while (V != Pl -> V);
-
- Pl -> Pnext = OutputPl; /* And chain it into output list. */
- OutputPl = Pl;
- }
-
- /* Free all polygons which are not in the output list: */
- while (--NumOfPolys >= 0) {
- if (!IS_INOUTPUT_POLY(PolysPtr[NumOfPolys])) {
- PolysPtr[NumOfPolys] -> Pnext = NULL; /* Free only this polygon. */
- MyFree((char *) (PolysPtr[NumOfPolys]), POLYGON_TYPE);
- }
- }
- MyFree((char *) PolysPtr, OTHER_TYPE);
-
- /* Another floating point kludge: a polygon may have zero length edge so */
- /* search for those and remove them - someone may die because of one... */
- Pl = OutputPl;
- while (Pl != NULL) {
- V = Pl -> V;
- do {
- Vnext = V -> Pnext;
- if (PT_EQ(V -> Pt, Vnext -> Pt)) {
- /* Ahh - we got you. Simply skip Vnext vertex and free it: */
- V -> Pnext = Vnext -> Pnext;
- /* Update polygon vertex pointer if point on freed vertex: */
- if (Pl -> V == Vnext) Pl -> V = Vnext -> Pnext;
- Vnext -> Pnext = NULL; /* Dont free too much! */
- MyFree((char *) Vnext, VERTEX_TYPE);
- Vnext = V -> Pnext;
- }
- V = Vnext;
- }
- while (V != Pl -> V && V != NULL);
-
- Pl = Pl -> Pnext;
- }
-
- #ifdef DEBUG3
- printf("Exit ExtractPolygons\n");
- #endif /* DEBUG3 */
-
- return GenGeomObject("", OutputPl, NULL); /* Return resulting object. */
- }
-
- #ifdef DEBUG2
-
- /*****************************************************************************
- * Print the content of the given polygon, to standard output. *
- *****************************************************************************/
- static void PrintVrtxList(VertexStruct *V)
- {
- struct VertexStruct *VHead = V;
-
- do {
- printf(" %12lf %12lf %12lf", V -> Pt[0], V -> Pt[1], V -> Pt[2]);
- if (IS_INTERNAL_EDGE(V))
- printf(" (Internal)\n");
- else printf("\n");
- V = V -> Pnext;
- }
- while (V!= NULL && V != VHead);
- }
-
- /*****************************************************************************
- * Print the content of the given InterSegment list, to standard output. *
- *****************************************************************************/
- static void PrintInterList(InterSegmentStruct *PInt)
- {
- printf("INTER SEGMENT LIST:\n");
-
- if (PInt) printf("Entry vertex pointer %p\n", PInt -> V[0]);
- while (PInt) {
- printf("%9lg %9lg %9lg (%04x), %9lg %9lg %9lg (%04x)\n",
- PInt->PtSeg[0][0], PInt->PtSeg[0][1], PInt->PtSeg[0][2],
- FP_SEG(PInt->V[0]),
- PInt->PtSeg[1][0], PInt->PtSeg[1][1], PInt->PtSeg[1][2],
- FP_SEG(PInt->V[1]));
- if (PInt -> Pnext == NULL)
- printf("Exit vertex pointer %p\n", PInt -> V[1]);
-
- PInt = PInt -> Pnext;
- }
- }
-
- #endif /* DEBUG2 */
-