1 /*! \file kbool/include/kbool/booleng.h 2 \author Klaas Holwerda 3 4 Copyright: 2001-2004 (C) Klaas Holwerda 5 6 Licence: see kboollicense.txt 7 8 RCS-ID: $Id: booleng.h,v 1.3 2005/06/11 19:25:12 frm Exp $ 9 */ 10 11 #ifndef BOOLENG_H 12 #define BOOLENG_H 13 14 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 15 #pragma interface 16 #endif 17 18 19 #include <stdio.h> 20 #include <limits.h> 21 22 23 #ifdef A2DKBOOLMAKINGDLL 24 #define A2DKBOOLDLLEXP WXEXPORT 25 #define A2DKBOOLDLLEXP_DATA(type) WXEXPORT type 26 #define A2DKBOOLDLLEXP_CTORFN 27 #elif defined(WXUSINGDLL) 28 #define A2DKBOOLDLLEXP WXIMPORT 29 #define A2DKBOOLDLLEXP_DATA(type) WXIMPORT type 30 #define A2DKBOOLDLLEXP_CTORFN 31 #else // not making nor using DLL 32 #define A2DKBOOLDLLEXP 33 #define A2DKBOOLDLLEXP_DATA(type) type 34 #define A2DKBOOLDLLEXP_CTORFN 35 #endif 36 37 #define KBOOL_VERSION "1.8" 38 39 #define KBOOL_DEBUG 0 40 #define KBOOL_LOG 0 41 #define KBOOL_INT64 1 42 43 class KBoolLink; 44 45 #define LINELENGTH 200 46 47 #ifdef MAXDOUBLE 48 #undef MAXDOUBLE 49 #endif 50 #define MAXDOUBLE 1.7976931348623158e+308 51 52 #ifdef KBOOL_INT64 53 54 #if defined(__UNIX__) || defined(__GNUG__) 55 56 typedef long long B_INT; // 8 bytes integer 57 //#define MAXB_INT LONG_LONG_MAX 58 //#define MINB_INT LONG_LONG_MIN // 8 bytes integer 59 #ifndef MAXB_INT 60 const B_INT MAXB_INT = (0x7fffffffffffffffLL); // 8 bytes integer 61 #endif 62 #ifndef MINB_INT 63 const B_INT MINB_INT = (0x8000000000000000LL); 64 #endif 65 66 #else //defined(__UNIX__) || defined(__GNUG__) 67 68 typedef __int64 B_INT; // 8 bytes integer 69 #undef MAXB_INT 70 #undef MINB_INT 71 72 const B_INT MAXB_INT = (0x7fffffffffffffff); // 8 bytes integer 73 const B_INT MINB_INT = (0x8000000000000000); 74 75 #endif //defined(__UNIX__) || defined(__GNUG__) 76 77 #else //KBOOL_INT64 78 79 #if defined(__UNIX__) || defined(__GNUG__) 80 typedef long B_INT; // 4 bytes integer 81 const B_INT MAXB_INT = (0x7fffffffL); // 4 bytes integer 82 const B_INT MINB_INT = (0x80000000L); 83 #else 84 typedef long B_INT; // 4 bytes integer 85 const B_INT MAXB_INT = (0x7fffffff); // 4 bytes integer 86 const B_INT MINB_INT = (0x80000000); 87 #endif 88 89 #endif //KBOOL_INT64 90 91 B_INT babs(B_INT); 92 93 #ifdef M_PI 94 #undef M_PI 95 #endif 96 #define M_PI (3.1415926535897932384626433832795028841972) 97 98 #ifdef M_PI_2 99 #undef M_PI_2 100 #endif 101 #define M_PI_2 1.57079632679489661923 102 103 #ifdef M_PI_4 104 #undef M_PI_4 105 #endif 106 #define M_PI_4 0.785398163397448309616 107 108 #ifndef NULL 109 #define NULL 0 110 #endif 111 112 B_INT bmin(B_INT const value1, B_INT const value2); 113 B_INT bmax(B_INT const value1, B_INT const value2); 114 115 B_INT bmin(B_INT value1, B_INT value2); 116 B_INT bmax(B_INT value1, B_INT value2); 117 118 #include <string.h> 119 120 //! errors in the boolean algorithm will be thrown using this class 121 class A2DKBOOLDLLEXP Bool_Engine_Error 122 { 123 public: 124 Bool_Engine_Error(const char* message, const char* header=0, int degree = 9, int fatal = 0); 125 Bool_Engine_Error(const Bool_Engine_Error& a); 126 ~Bool_Engine_Error(); 127 char* GetErrorMessage(); 128 char* GetHeaderMessage(); 129 int GetErrorDegree(); 130 int GetFatal(); 131 132 protected: 133 char* _message; 134 char* _header; 135 int _degree; 136 int _fatal; 137 }; 138 139 140 #define KBOOL_LOGFILE "kbool.log" 141 142 enum kbEdgeType 143 { 144 KB_OUTSIDE_EDGE, /*!< edge of the outside contour of a polygon */ 145 KB_INSIDE_EDGE, /*!< edge of the inside hole a polygon */ 146 KB_FALSE_EDGE /*!< edge to connect holes into polygons */ 147 } ; 148 149 enum GroupType 150 { 151 GROUP_A, /*!< to set Group A for polygons */ 152 GROUP_B /*!< to set Group A for polygons */ 153 }; 154 155 enum BOOL_OP 156 { 157 BOOL_NON, /*!< No operation */ 158 BOOL_OR, /*!< boolean OR operation */ 159 BOOL_AND, /*!< boolean AND operation */ 160 BOOL_EXOR, /*!< boolean EX_OR operation */ 161 BOOL_A_SUB_B, /*!< boolean Group A - Group B operation */ 162 BOOL_B_SUB_A, /*!< boolean Group B - Group A operation */ 163 BOOL_CORRECTION, /*!< polygon correction/offset operation */ 164 BOOL_SMOOTHEN, /*!< smooth operation */ 165 BOOL_MAKERING /*!< create a ring on all polygons */ 166 }; 167 168 class GraphList; 169 class Graph; 170 class KBoolLink; 171 class Node; 172 template<class Type> class TDLI; 173 174 //! boolean engine to perform operation on two sets of polygons. 175 /* 176 First the engine needs to be filled with polygons. 177 The first operand in the operation is called group A polygons, the second group B. 178 The boolean operation ( BOOL_OR, BOOL_AND, BOOL_EXOR, BOOL_A_SUB_B, BOOL_B_SUB_A ) 179 are based on the two sets of polygons in group A and B. 180 The other operation on group A only. 181 182 At the end of the operation the resulting polygons can be extracted. 183 */ 184 class A2DKBOOLDLLEXP Bool_Engine { 185 186 public: 187 188 //! constructor 189 Bool_Engine(); 190 191 //! destructor 192 virtual ~Bool_Engine(); 193 GetVersion()194 const char* GetVersion() { return KBOOL_VERSION; } 195 196 //! reports progress of algorithm. 197 virtual void SetState( const char* = 0 ); 198 199 //! called at an internal error. 200 virtual void error(const char *text, const char *title); 201 202 //! called at an internal generated possible error. 203 virtual void info(const char *text, const char *title); 204 205 bool Do_Operation(BOOL_OP operation); 206 207 208 //! distance within which points and lines will be snapped towards lines and other points 209 /* 210 The algorithm takes into account gaps and inaccuracies caused by rounding to integer coordinates 211 in the original data. 212 Imagine two rectangles one with a side ( 0,0 ) ( 2.0, 17.0 ) 213 and the other has a side ( 0,0 ) ( 1.0, 8.5 ) 214 If for some reason those coordinates where round to ( 0,0 ) ( 2, 17 ) ( 0,0 ) ( 1, 9 ), 215 there will be clearly a gap or overlap that was not intended. 216 Even without rounding this effect takes place since there is always a minimum significant bit 217 also when using doubles. 218 219 If the user used as minimum accuracy 0.001, you need to choose Marge > 0.001 220 The boolean engine scales up the input data with GetDGrid() * GetGrid() and rounds the result to 221 integer, So (assuming GRID = 100 DGRID = 1000) a vertex of 123.001 in the user data will 222 become 12300100 internal. 223 At the end of the algorithm the internal vertexes are scaled down again with GetDGrid() * GetGrid(), 224 so 12300103 becomes 123.00103 eventually. 225 So indeed the minimum accuracy might increase, you are free to round again if needed. 226 */ 227 void SetMarge(double marge); 228 double GetMarge(); 229 230 //! input points are scaled up with GetDGrid() * GetGrid() 231 /* 232 Grid makes sure that the integer data used within the algorithm has room for extra intersections 233 smaller than the smallest number within the input data. 234 The input data scaled up with DGrid is related to the accuracy the user has in his input data. 235 Another scaling with Grid is applied on top of it to create space in the integer number for 236 even smaller numbers. 237 */ 238 void SetGrid(B_INT grid); 239 240 //! See SetGrid 241 B_INT GetGrid(); 242 243 //! input points are scaled up with GetDGrid() * GetGrid() 244 /* 245 The input data scaled up with DGrid is related to the accuracy the user has in his input data. 246 User data with a minimum accuracy of 0.001, means set the DGrid to 1000. 247 The input data may contain data with a minimum accuracy much smaller, but by setting the DGrid 248 everything smaller than 1/DGrid is rounded. 249 250 DGRID is only meant to make fractional parts of input data which can be 251 doubles, part of the integers used in vertexes within the boolean algorithm. 252 And therefore DGRID bigger than 1 is not usefull, you would only loose accuracy. 253 Within the algorithm all input data is multiplied with DGRID, and the result 254 is rounded to an integer. 255 */ 256 void SetDGrid(double dgrid); 257 258 //! See SetDGrid 259 double GetDGrid(); 260 261 //! When doing a correction operation ( also known as process offset ) 262 //! this defines the detail in the rounded corners. 263 /* 264 Depending on the round factor the corners of the polygon may be rounding within the correction 265 algorithm. The detail within this rounded corner is set here. 266 It defines the deviation the generated segments in arc like polygon may have towards the ideal 267 rounded corner using a perfect arc. 268 */ 269 void SetCorrectionAber(double aber); 270 271 //! see SetCorrectionAber 272 double GetCorrectionAber(); 273 274 //! When doing a correction operation ( also known as process offset ) 275 //! this defines the amount of correction. 276 /* 277 The correction algorithm can apply positive and negative offset to polygons. 278 It takes into account closed in areas within a polygon, caused by overlapping/selfintersecting 279 polygons. So holes form that way are corrected proberly, but the overlapping parts itself 280 are left alone. An often used trick to present polygons with holes by linking to the outside 281 boundary, is therefore also handled properly. 282 The algoritm first does a boolean OR operation on the polygon, and seperates holes and 283 outside contours. 284 After this it creates a ring shapes on the above holes and outside contours. 285 This ring shape is added or subtracted from the holes and outside contours. 286 The result is the corrected polygon. 287 If the correction factor is > 0, the outside contours will become larger, while the hole contours 288 will become smaller. 289 */ 290 void SetCorrectionFactor(double aber); 291 292 //! see SetCorrectionFactor 293 double GetCorrectionFactor(); 294 295 //! used within the smooth algorithm to define how much the smoothed curve may deviate 296 //! from the original. 297 void SetSmoothAber(double aber); 298 299 //! see SetSmoothAber 300 double GetSmoothAber(); 301 302 //! segments of this size will be left alone in the smooth algorithm. 303 void SetMaxlinemerge(double maxline); 304 305 //! see SetMaxlinemerge 306 double GetMaxlinemerge(); 307 308 //! Polygon may be filled in different ways (alternate and winding rule). 309 //! This here defines which method will be assumed within the algorithm. 310 void SetWindingRule(bool rule); 311 312 //! see SetWindingRule 313 bool GetWindingRule(); 314 315 //! the smallest accuracy used within the algorithm for comparing two real numbers. 316 double GetAccur(); 317 318 //! Used with in correction/offset algorithm. 319 /* 320 When the polygon contains sharp angles ( < 90 ), after a positive correction the 321 extended parrallel constructed offset lines may leed to extreme offsets on the angles. 322 The length of the crossing generated by the parrallel constructed offset lines 323 towards the original point in the polygon is compared to the offset which needs to be applied. 324 The Roundfactor then decides if this corner will be rounded. 325 A Roundfactor of 1 means that the resulting offset will not be bigger then the correction factor 326 set in the algorithm. Meaning even straight 90 degrees corners will be rounded. 327 A Roundfactor of > sqrt(2) is where 90 corners will be left alone, and smaller corners will be rounded. 328 */ 329 void SetRoundfactor(double roundfac); 330 331 //! see SetRoundfactor 332 double GetRoundfactor(); 333 334 // the following are only be used within the algorithm, 335 // since they are scaled with m_DGRID 336 337 //! only used internal. 338 void SetInternalMarge( B_INT marge ); 339 //! only used internal. 340 B_INT GetInternalMarge(); 341 342 //! only used internal. 343 double GetInternalCorrectionAber(); 344 345 //! only used internal. 346 double GetInternalCorrectionFactor(); 347 348 //! only used internal. 349 double GetInternalSmoothAber(); 350 351 //! only used internal. 352 B_INT GetInternalMaxlinemerge(); 353 354 //! in this mode polygons add clockwise, or contours, 355 /*! 356 and polygons added counter clockwise or holes. 357 */ SetOrientationEntryMode(bool orientationEntryMode)358 void SetOrientationEntryMode( bool orientationEntryMode ) { m_orientationEntryMode = orientationEntryMode; } 359 360 //! see SetOrientationEntryMode() GetOrientationEntryMode()361 bool GetOrientationEntryMode() { return m_orientationEntryMode; } 362 363 //! if set true holes are linked into outer contours by double overlapping segments. 364 /*! 365 This mode is needed when the software using the boolean algorithm does 366 not understand hole polygons. In that case a contour and its holes form one 367 polygon. In cases where software understands the concept of holes, contours 368 are clockwise oriented, while holes are anticlockwise oriented. 369 The output of the boolean operations, is following those rules also. 370 But even if extracting the polygons from the engine, each segment is marked such 371 that holes and non holes and linksegments to holes can be recognized. 372 */ SetLinkHoles(bool doLinkHoles)373 void SetLinkHoles( bool doLinkHoles ) { m_doLinkHoles = doLinkHoles; } 374 375 //! see SetLinkHoles() GetLinkHoles()376 bool GetLinkHoles() { return m_doLinkHoles; } 377 378 //!lof file will be created when set True 379 void SetLog( bool OnOff ); 380 381 //! used to write to log file 382 void Write_Log(const char *); 383 //! used to write to log file 384 void Write_Log(const char *, const char *); 385 //! used to write to log file 386 void Write_Log(const char *, double); 387 //! used to write to log file 388 void Write_Log(const char *, B_INT); 389 GetLogFile()390 FILE* GetLogFile() { return m_logfile; } 391 392 // methods used to add polygons to the eng using points 393 394 //! Start adding a polygon to the engine 395 /* 396 The boolean operation work on two groups of polygons ( group A or B ), 397 other algorithms are only using group A. 398 399 You add polygons like this to the engine. 400 401 // foreach point in a polygon ... 402 if (booleng->StartPolygonAdd(GROUP_A)) 403 { 404 booleng->AddPoint(100,100); 405 booleng->AddPoint(-100,100); 406 booleng->AddPoint(-100,-100); 407 booleng->AddPoint(100,-100); 408 } 409 booleng->EndPolygonAdd(); 410 411 \param A_or_B defines if the new polygon will be of group A or B 412 413 Holes or added by adding an inside polygons with opposite orientation compared 414 to another polygon added. 415 So the contour polygon ClockWise, then add counterclockwise polygons for holes, and visa versa. 416 BUT only if m_orientationEntryMode is set true, else all polygons are redirected, and become 417 individual areas without holes. 418 Holes in such a case must be linked into the contour using two extra segments. 419 */ 420 bool StartPolygonAdd( GroupType A_or_B ); 421 422 //! see StartPolygonAdd 423 bool AddPoint(double x, double y, int user_data); 424 425 //! see StartPolygonAdd 426 bool EndPolygonAdd(); 427 428 // methods used to extract polygons from the eng by getting its points 429 430 //! Use after StartPolygonGet() GetNumPointsInPolygon()431 int GetNumPointsInPolygon() { return m_numPtsInPolygon ; } 432 433 //! get resulting polygons at end of an operation 434 /*! 435 // foreach resultant polygon in the booleng ... 436 while ( booleng->StartPolygonGet() ) 437 { 438 // foreach point in the polygon 439 while ( booleng->PolygonHasMorePoints() ) 440 { 441 fprintf(stdout,"x = %f\t", booleng->GetPolygonXPoint()); 442 fprintf(stdout,"y = %f\n", booleng->GetPolygonYPoint()); 443 } 444 booleng->EndPolygonGet(); 445 } 446 */ 447 bool StartPolygonGet(); 448 449 //! see StartPolygonGet 450 /*! 451 This iterates through the first graph in the graphlist. 452 Setting the current Node properly by following the links in the graph 453 through its nodes. 454 */ 455 bool PolygonHasMorePoints(); 456 457 //! see StartPolygonGet 458 double GetPolygonXPoint(); 459 460 //! see StartPolygonGet 461 double GetPolygonYPoint(); 462 463 //! in the resulting polygons this tells if the current polygon segment is one 464 //! used to link holes into the outer contour of the surrounding polygon 465 bool GetHoleConnectionSegment(); 466 467 //! in the resulting polygons this tells if the current polygon segment is part 468 //! of a hole within a polygon. 469 bool GetHoleSegment(); 470 471 //! an other way to get the type of segment. 472 kbEdgeType GetPolygonPointEdgeType(); 473 474 int GetPolygonPointUserData(); 475 476 //! see StartPolygonGet() 477 /*! 478 Removes a graph from the graphlist. 479 Called after an extraction of an output polygon was done. 480 */ 481 void EndPolygonGet(); 482 483 private: 484 485 bool m_doLog; 486 487 //! contains polygons in graph form 488 GraphList* m_graphlist; 489 490 double m_MARGE; 491 B_INT m_GRID; 492 double m_DGRID; 493 double m_CORRECTIONABER; 494 double m_CORRECTIONFACTOR; 495 double m_SMOOTHABER; 496 double m_MAXLINEMERGE; 497 bool m_WINDINGRULE; 498 double m_ACCUR; 499 double m_ROUNDFACTOR; 500 501 bool m_orientationEntryMode; 502 503 bool m_doLinkHoles; 504 505 //! used in the StartPolygonAdd, AddPt, EndPolygonAdd sequence 506 Graph* m_GraphToAdd; 507 //! used in the StartPolygonAdd, AddPt, EndPolygonAdd sequence 508 Node* m_lastNodeToAdd; 509 //! used in the StartPolygonAdd, AddPt, EndPolygonAdd sequence 510 Node* m_firstNodeToAdd; 511 512 //! the current group type ( group A or B ) 513 GroupType m_groupType; 514 515 //! used in extracting the points from the resultant polygons 516 Graph* m_getGraph; 517 //! used in extracting the points from the resultant polygons 518 KBoolLink* m_getLink; 519 //! used in extracting the points from the resultant polygons 520 Node* m_getNode; 521 //! used in extracting the points from the resultant polygons 522 double m_PolygonXPoint; 523 //! used in extracting the points from the resultant polygons 524 double m_PolygonYPoint; 525 //! used in extracting the points from the resultant polygons 526 int m_numPtsInPolygon; 527 //! used in extracting the points from the resultant polygons 528 int m_numNodesVisited; 529 530 FILE* m_logfile; 531 532 public: 533 534 //! use in Node to iterate links. 535 TDLI<KBoolLink>* _linkiter; 536 537 //! how many time run intersections fase. 538 unsigned int m_intersectionruns; 539 540 }; 541 542 #endif 543