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 high level boolean operations. The other modules *
- * should only call this module to perform boolean operations. All the *
- * operations are none-destructives, meaning the given data is not modified. *
- * Note all the polygons of the two given objects must be convex, and the *
- * returned object will also have only convex polygons! *
- *****************************************************************************/
-
- /* #define DEBUG If defined, return intersection polyline object. */
-
- #ifdef __MSDOS__
- #include <graphics.h>
- #include <alloc.h>
- #endif /* __MSDOS__ */
-
- #include <stdio.h>
- #include <ctype.h>
- #include <math.h>
- #include <string.h>
- #include <signal.h>
- #include "program.h"
- #include "allocatg.h"
- #include "objects.h"
- #include "ctrl-brk.h"
- #include "graphgng.h"
- #include "booleang.h"
- #include "booleanl.h"
- #include "convexg.h"
- #include "windowsg.h"
- #include "matherr.h"
-
- #ifndef __MSDOS__
- #include "xgraphic.h"
- #endif /* __MSDOS__ */
-
- int BooleanOutputInterCurve = FALSE; /* Kind of output from boolean oper. */
-
- static jmp_buf LclLongJumpBuffer; /* Used in fatal boolean error. */
- static int FatalErrorType, /* Type of fatal boolean error. */
- BooleanOperation; /* One of BooleanOR, BooleanAND, etc. */
-
- static ObjectStruct *BooleanCombineTwoObjs(ObjectStruct *PObj1,
- ObjectStruct *PObj2);
- static void BooleanFPE(int Sig, int Type, int *RegList);
- static void SetBooleanOutputKind(void);
-
- /*****************************************************************************
- * Perform a boolean OR between two objects. *
- *****************************************************************************/
- ObjectStruct *BooleanOR(ObjectStruct *PObj1, ObjectStruct *PObj2)
- {
- ObjectStruct *PObj;
- PolygonStruct *Pl;
-
- BooleanOperation = BOOL_OPEN_OR;
-
- if (!IS_GEOM_OBJ(PObj1) || !IS_GEOM_OBJ(PObj2))
- FatalError("Boolean: operation on none geometric object(s)\n");
-
- signal(SIGFPE, BooleanFPE); /* Will trap floating point errors. */
-
- if (IS_POLYLINE_GEOM_OBJ(PObj1) && IS_POLYLINE_GEOM_OBJ(PObj2)) {
- if (PObj1 -> U.Pl == NULL) PObj = CopyObject(NULL, PObj2, FALSE);
- else {
- PObj = CopyObject(NULL, PObj1, FALSE); /* Copy Obj1 polylines. */
- Pl = PObj -> U.Pl;
- while (Pl -> Pnext) Pl = Pl -> Pnext;
- Pl -> Pnext = CopyPolygonList(PObj2 -> U.Pl); /* Obj2 polylines. */
- }
- }
- else
- if (IS_POLYLINE_GEOM_OBJ(PObj1) || IS_POLYLINE_GEOM_OBJ(PObj2)) {
- WndwInputWindowPutStr(
- "Boolean: illegal operation on mixed polygon/line geometric object(s).",
- RED);
- PObj = GenGeomObject("", NULL, NULL);
- }
- else {
- ConvexPolyObject(PObj1); /* Make sure all polygons are convex: */
- ConvexPolyObject(PObj2);
-
- SetBooleanOutputKind();
-
- if (setjmp(LclLongJumpBuffer) == 0) { /* Its the setjmp itself call! */
- signal(SIGFPE, BooleanFPE); /* Will trap floating point errors. */
- if (BooleanOutputInterCurve)
- PObj = BooleanLow1Out2(PObj1, PObj2);/* Ret intersection crv.*/
- else
- PObj = BooleanCombineTwoObjs(BooleanLow1Out2(PObj1, PObj2),
- BooleanLow1Out2(PObj2, PObj1));
- }
- else {
- /* We gain control from fatal error long jump - usually we should*/
- /* return empty object, but if error is No intersection between */
- /* the two objects, we assume they have no common volume and */
- /* return a new object consists of the concat. of all polygons! */
- if (FatalErrorType != FTL_BOOL_NO_INTER) {
- PObj = GenGeomObject("", NULL, NULL);/* Return empty object. */
- }
- else {
- if (PObj1 -> U.Pl == NULL) PObj = CopyObject(NULL, PObj2, FALSE);
- else {
- PObj = CopyObject(NULL, PObj1, FALSE);/* Copy Obj1 polys.*/
- Pl = PObj -> U.Pl;
- while (Pl -> Pnext) Pl = Pl -> Pnext;
- Pl -> Pnext = CopyPolygonList(PObj2 -> U.Pl);/*Obj2 poly.*/
- }
- }
- }
- }
-
- signal(SIGFPE, DefaultFPEHandler); /* Default FPE trapping. */
-
- SET_OBJECT_COLOR(PObj, BooleanOutputInterCurve ?
- ICrvColor :
- BoolColor); /* Default bool object color. */
-
- return PObj;
- }
-
- /*****************************************************************************
- * Perform a boolean AND between two objects. *
- *****************************************************************************/
- ObjectStruct *BooleanAND(ObjectStruct *PObj1, ObjectStruct *PObj2)
- {
- ObjectStruct *PObj;
-
- BooleanOperation = BOOL_OPEN_AND;
-
- if (!IS_GEOM_OBJ(PObj1) || !IS_GEOM_OBJ(PObj2))
- FatalError("Boolean: operation on none geometric object(s)\n");
-
- signal(SIGFPE, BooleanFPE); /* Will trap floating point errors. */
-
- if (IS_POLYLINE_GEOM_OBJ(PObj1) || IS_POLYLINE_GEOM_OBJ(PObj2)) {
- WndwInputWindowPutStr(
- "Boolean: illegal operation on polyline geometric object(s).",
- RED);
- PObj = GenGeomObject("", NULL, NULL);
- }
- else {
- ConvexPolyObject(PObj1); /* Make sure all polygons are convex: */
- ConvexPolyObject(PObj2);
-
- SetBooleanOutputKind();
-
- if (setjmp(LclLongJumpBuffer) == 0) { /* Its the setjmp itself call! */
- signal(SIGFPE, BooleanFPE); /* Will trap floating point errors. */
- if (BooleanOutputInterCurve)
- PObj = BooleanLow1In2(PObj1, PObj2);/* Ret intersection crv. */
- else
- PObj = BooleanCombineTwoObjs(BooleanLow1In2(PObj1, PObj2),
- BooleanLow1In2(PObj2, PObj1));
- }
- else {/* We gain control from fatal error long jump - ret empty obj. */
- PObj = GenGeomObject("", NULL, NULL);
- }
- }
-
- signal(SIGFPE, DefaultFPEHandler); /* Default FPE trapping. */
-
- SET_OBJECT_COLOR(PObj, BooleanOutputInterCurve ?
- ICrvColor :
- BoolColor); /* Default bool object color. */
-
- return PObj;
- }
-
- /*****************************************************************************
- * Perform a boolean SUBSTRACT between two objects (PObj1 - PObj2). *
- *****************************************************************************/
- ObjectStruct *BooleanSUB(ObjectStruct *PObj1, ObjectStruct *PObj2)
- {
- ObjectStruct *PObj, *PTemp, *PTempRev;
-
- BooleanOperation = BOOL_OPEN_SUB;
-
- if (!IS_GEOM_OBJ(PObj1) || !IS_GEOM_OBJ(PObj2))
- FatalError("Boolean: operation on none geometric object(s)\n");
-
- signal(SIGFPE, BooleanFPE); /* Will trap floating point errors. */
-
- if (IS_POLYLINE_GEOM_OBJ(PObj1) || IS_POLYLINE_GEOM_OBJ(PObj2)) {
- WndwInputWindowPutStr(
- "Boolean: illegal operation on polyline geometric object(s).",
- RED);
- PObj = GenGeomObject("", NULL, NULL);
- }
- else {
- ConvexPolyObject(PObj1); /* Make sure all polygons are convex: */
- ConvexPolyObject(PObj2);
-
- SetBooleanOutputKind();
-
- if (setjmp(LclLongJumpBuffer) == 0) { /* Its the setjmp itself call! */
- signal(SIGFPE, BooleanFPE); /* Will trap floating point errors. */
- /* The 1 in 2 must be reversed (the inside/outside orientation): */
- if (BooleanOutputInterCurve) {
- PObj = BooleanLow1In2(PObj2, PObj1);/* Ret intersection crv. */
- }
- else {
- PTemp = BooleanLow1In2(PObj2, PObj1);
- PTempRev = BooleanNEG(PTemp);
- MyFree((char *) PTemp, OBJECT_TYPE);
-
- PObj = BooleanCombineTwoObjs(BooleanLow1Out2(PObj1, PObj2),
- PTempRev);
- }
- }
- else {/* We gain control from fatal error long jump - ret empty obj. */
- PObj = GenGeomObject("", NULL, NULL);
- }
- }
-
- signal(SIGFPE, DefaultFPEHandler); /* Default FPE trapping. */
-
- SET_OBJECT_COLOR(PObj, BooleanOutputInterCurve ?
- ICrvColor :
- BoolColor); /* Default bool object color. */
-
- return PObj;
- }
-
- /*****************************************************************************
- * Perform a boolean NEGATION of given object PObj. *
- * Negation is simply reversing the direction of the plane equation of each *
- * polygon - the simplest boolean operation... *
- *****************************************************************************/
- ObjectStruct *BooleanNEG(ObjectStruct *PObj)
- {
- int i;
- ObjectStruct *PTemp;
- PolygonStruct *Pl;
-
- BooleanOperation = BOOL_OPEN_NEG;
-
- SetBooleanOutputKind();
-
- if (!IS_GEOM_OBJ(PObj))
- FatalError("Boolean: operation on none geometric object(s)\n");
-
- signal(SIGFPE, BooleanFPE); /* Will trap floating point errors. */
-
- if (IS_POLYLINE_GEOM_OBJ(PObj)) {
- WndwInputWindowPutStr(
- "Boolean: illegal operation on polyline geometric object(s).",
- RED);
- PTemp = GenGeomObject("", NULL, NULL);
- }
- else {
- PTemp = CopyObject(NULL, PObj, FALSE); /* Make fresh copy of object. */
-
- /* Scans all polygons and reverse plane equation and their vetrex */
- /* list (cross prod. of consecutive edges must be in normal dir.). */
- Pl = PTemp -> U.Pl;
- while (Pl != NULL) {
- for (i=0; i<4; i++) Pl -> Plane[i] = (-Pl -> Plane[i]);
- RST_CONVEX_POLY(Pl);
- Pl = Pl -> Pnext;
- }
- }
-
- signal(SIGFPE, DefaultFPEHandler); /* Default FPE trapping. */
-
- SET_OBJECT_COLOR(PTemp, BooleanOutputInterCurve ?
- ICrvColor :
- BoolColor); /* Default bool object color. */
-
- return PTemp;
- }
-
- /*****************************************************************************
- * Combining two geometric objects, by simply concat. their polygon lists: *
- *****************************************************************************/
- static ObjectStruct *BooleanCombineTwoObjs(ObjectStruct *PObj1,
- ObjectStruct *PObj2)
- {
- PolygonStruct *Pl;
-
- CleanUpPolygonList(&PObj1 -> U.Pl);
- CleanUpPolygonList(&PObj2 -> U.Pl);
-
- if (PObj2 == NULL) return PObj1;
- else {
- if (PObj1 == NULL) return NULL;
- Pl = PObj1 -> U.Pl;
- while (Pl -> Pnext != NULL) Pl = Pl -> Pnext;
- Pl -> Pnext = PObj2 -> U.Pl; /* Concat. the polygons into one list. */
- PObj2 -> U.Pl = NULL; /* And release the second object header. */
- MyFree((char *) PObj2, OBJECT_TYPE);
- return PObj1;
- }
- }
-
- /*****************************************************************************
- * Routine to clean up boolean operation result polygons - delete zero *
- * length edges, and polygons with less than 3 vertices. *
- *****************************************************************************/
- void CleanUpPolygonList(PolygonStruct **PPolygon)
- {
- PolygonStruct *PPHead, *PPLast;
- VertexStruct *PVHead, *PVTemp, *PVNext;
-
- PPLast = PPHead = (*PPolygon);
-
- while (PPHead != NULL) {
- PVHead = PVTemp = PPHead -> V;
- /* Look for zero length edges (V == V -> Pnext): */
- do {
- PVNext = PVTemp -> Pnext;
- if (PT_EQ(PVTemp -> Pt, PVNext -> Pt)) { /* Delete PVNext. */
- PVTemp -> Pnext = PVTemp -> Pnext -> Pnext;
- PVNext -> Pnext = NULL; /* Free only PVNext. */
- if (PVHead == PVNext) { /* If we actually kill header. */
- PPHead -> V = PVHead = PVTemp;
- break;
- }
- MyFree((char *) PVNext, VERTEX_TYPE);
- }
- else PVTemp = PVTemp -> Pnext;
- }
- while (PVTemp != NULL && PVTemp != PVHead);
-
- /* Now test if at list 3 vertices in polygon, otherwise delete it: */
- if (PVHead == PVHead -> Pnext || /* One vertex only. */
- PVHead == PVHead -> Pnext -> Pnext) { /* Two vertices only. */
- if (PPHead == (*PPolygon)) {
- *PPolygon = (*PPolygon) -> Pnext;
- PPHead -> Pnext = NULL;
- MyFree((char *) PPHead, POLYGON_TYPE);
- PPHead = (*PPolygon);
- }
- else {
- PPLast -> Pnext = PPHead -> Pnext;
- PPHead -> Pnext = NULL;
- MyFree((char *) PPHead, POLYGON_TYPE);
- PPHead = PPLast -> Pnext;
- }
- }
- else {
- PPLast = PPHead;
- PPHead = PPHead -> Pnext;
- }
- }
- }
-
- /*****************************************************************************
- * Routine that is called from the floating point package in case of fatal *
- * floating point error. Print error message, and quit the boolean module. *
- *****************************************************************************/
- static void BooleanFPE(int Sig, int Type, int *RegList)
- {
- PrintFPError(Type); /* Print the floating point error type. */
-
- FatalErrorType = FTL_BOOL_FPE;
-
- longjmp(LclLongJumpBuffer, 1);
- }
-
- /*****************************************************************************
- * Routine to select the output kind needed. Output of a booelan operator *
- * can be the real boolean result model, or the intersection curves only. *
- *****************************************************************************/
- static void SetBooleanOutputKind(void)
- {
- struct ObjectStruct *PObj = GetObject("INTERCRV");
-
- if (PObj == NULL || !IS_NUM_OBJ(PObj)) {
- WndwInputWindowPutStr("No numeric object name INTERCRV is defined",
- RED);
- BooleanOutputInterCurve = FALSE;
- }
- else BooleanOutputInterCurve = !APX_EQ(PObj -> U.R, 0.0);
- }
-
- /*****************************************************************************
- * Routine that is called by the boolean low level routines every small *
- * amount of time (syncronically) to test if ^C/^Break was pressed and quit *
- * if so. Note we could do it asyncronically, but this complicate thing too *
- * much, and makes them highly unportable... *
- *****************************************************************************/
- void TestBooleanCtrlBrk(void)
- {
- if (WasCtrlBrk) {
- WndwInputWindowPutStr("Control Break traps - Empty object result", RED);
-
- FatalErrorType = FTL_BOOL_CTRL_BRK;
-
- longjmp(LclLongJumpBuffer, 1);
- }
- }
-
- /*****************************************************************************
- * Routine that is called by the bool-low module in fatal cases errors. *
- * Will print error message and long jump using LclLongJumpBuffer. *
- *****************************************************************************/
- void FatalBooleanError(int ErrorType)
- {
- char s[LINE_LEN_LONG];
-
- FatalErrorType = ErrorType;
-
- switch (ErrorType) {
- case FTL_BOOL_NO_INTER:
- /* If operation is union (OR), then if no intersection we assume */
- /* the objects have no common volume and simply combine them! */
- if (BooleanOperation != BOOL_OPEN_OR) {
- sprintf(s, "Boolean: %s",
- "Objects do not intersect - Empty object result");
- WndwInputWindowPutStr(s, RED);
- }
- break;
- default:
- sprintf(s, "Boolean: Undefined Fatal Error (%d !?)", ErrorType);
- WndwInputWindowPutStr(s, RED);
- break;
- }
-
- longjmp(LclLongJumpBuffer, 1);
- }