2
* ***** BEGIN GPL LICENSE BLOCK *****
4
* This program is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU General Public License
6
* as published by the Free Software Foundation; either version 2
7
* of the License, or (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software Foundation,
16
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19
* All rights reserved.
21
* The Original Code is: all of this file.
23
* Contributor(s): none yet.
25
* ***** END GPL LICENSE BLOCK *****
28
/** \file boolop/intern/BOP_BSPNode.cpp
29
* \ingroup boolopintern
33
#include "BOP_MathUtils.h"
34
#include "BOP_BSPNode.h"
35
#include "MT_assert.h"
36
#include "MT_MinMax.h"
40
* Constructs a new BSP node.
41
* @param plane split plane.
43
BOP_BSPNode::BOP_BSPNode(const MT_Plane3& plane)
52
* Destroys a BSP tree.
54
BOP_BSPNode::~BOP_BSPNode()
56
if (m_inChild!=NULL) delete m_inChild;
57
if (m_outChild!=NULL) delete m_outChild;
61
* Adds a new face to this BSP tree.
62
* @param pts vector containing face points
63
* @param plane face plane.
66
unsigned int BOP_BSPNode::addFace(const BOP_BSPPoints& pts,
67
const MT_Plane3& plane )
69
unsigned int newDeep = 0;
72
// find out if any points on the "face" lie in either half-space
73
BOP_IT_BSPPoints ptsEnd = pts.end();
74
for(BOP_IT_BSPPoints itp=pts.begin();itp!=ptsEnd;itp++){
75
tag = (BOP_TAG) ((int) tag | (int)testPoint(*itp));
78
if (tag == ON) { } // face lies on hyperplane: do nothing
79
else if ((tag & IN) != 0 && (tag & OUT) == 0) { // face is entirely on inside
80
if (m_inChild != NULL)
81
newDeep = m_inChild->addFace(pts, plane) + 1;
83
m_inChild = new BOP_BSPNode(plane);
86
} else if ((tag & OUT) != 0 && (tag & IN) == 0) { // face is entirely on outside
87
if (m_outChild != NULL)
88
newDeep = m_outChild->addFace(pts, plane) + 1;
90
m_outChild = new BOP_BSPNode(plane);
93
} else { // face lies in both half-spaces: split it
94
BOP_BSPPoints inside, outside;
95
MT_Point3 lpoint= pts[pts.size()-1];
96
BOP_TAG ltag = testPoint(lpoint);
97
BOP_TAG tstate = ltag;
99
// classify each line segment, looking for endpoints which lie on different
100
// sides of the hyperplane.
103
for(BOP_IT_BSPPoints itp=pts.begin();itp!=ptsEnd;itp++){
104
MT_Point3 npoint= *itp;
105
BOP_TAG ntag = testPoint(npoint);
107
if(ltag != ON) { // last point not on hyperplane
109
if (m_inChild != NULL) inside.push_back(lpoint);
111
if (m_outChild != NULL) outside.push_back(lpoint);
113
if(ntag != ON && ntag != tstate) { // last, self in different half-spaces
114
MT_Point3 mpoint = BOP_intersectPlane( m_plane, lpoint, npoint );
115
if (m_inChild != NULL) inside.push_back(mpoint);
116
if (m_outChild != NULL) outside.push_back(mpoint);
119
} else { // last point on hyperplane, so we're switching
121
// boundary point belong to both faces
122
if (m_inChild != NULL) inside.push_back(lpoint);
123
if (m_outChild != NULL) outside.push_back(lpoint);
124
tstate = ntag; // state changes to new point tag
126
lpoint = npoint; // save point, tag for next iteration
130
if (m_inChild != NULL)
131
newDeep = m_inChild->addFace(inside, plane) + 1;
133
m_inChild = new BOP_BSPNode(plane);
136
if (m_outChild != NULL)
137
newDeep = MT_max(newDeep, m_outChild->addFace(outside, plane) + 1);
139
m_outChild = new BOP_BSPNode(plane);
140
newDeep = MT_max(newDeep,(unsigned int)2);
144
// update the deep attribute
145
m_deep = MT_max(m_deep,newDeep);
151
* Tests the point situation respect the node plane.
152
* @param p point to test.
153
* @return TAG result: IN, OUT or ON.
155
BOP_TAG BOP_BSPNode::testPoint(const MT_Point3& p) const
157
return BOP_createTAG(BOP_classify(p,m_plane));
162
* Classifies a face using its coordinates and plane.
163
* @param p1 first point.
164
* @param p2 second point.
165
* @param p3 third point.
166
* @param plane face plane.
167
* @return TAG result: IN, OUT or IN&OUT.
169
BOP_TAG BOP_BSPNode::classifyFace(const MT_Point3& p1,
172
const MT_Plane3& plane) const
175
MT_Point3 auxp1, auxp2;
176
BOP_TAG auxtag1, auxtag2, auxtag3;
178
switch(BOP_createTAG(testPoint(p1),testPoint(p2),testPoint(p3))) {
179
// Classify the face on the IN side
181
return classifyFaceIN(p1, p2, p3, plane);
188
return BOP_addON(classifyFaceIN(p1, p2, p3, plane));
190
// Classify the face on the OUT side
192
return classifyFaceOUT(p1, p2, p3, plane);
199
return BOP_addON(classifyFaceOUT(p1, p2, p3, plane));
201
// Classify the ON face depending on it plane normal
203
if (hasSameOrientation(plane))
204
return BOP_addON(classifyFaceIN(p1, p2, p3, plane));
206
return BOP_addON(classifyFaceOUT(p1, p2, p3, plane));
208
// Classify the face IN/OUT and one vertex ON
209
// becouse only one ON, only one way to subdivide the face
211
auxp1 = BOP_intersectPlane(m_plane, p1, p2);
212
auxtag1 = classifyFaceIN( p1, auxp1 , p3, plane);
213
auxtag2 = classifyFaceOUT(auxp1, p2, p3, plane);
214
return (BOP_compTAG(auxtag1,auxtag2)?BOP_addON(auxtag1):INOUT);
217
auxp1 = BOP_intersectPlane(m_plane, p1, p2);
218
auxtag1 = classifyFaceOUT(p1, auxp1, p3, plane);
219
auxtag2 = classifyFaceIN( auxp1, p2, p3, plane);
220
return (BOP_compTAG(auxtag1,auxtag2)?BOP_addON(auxtag1):INOUT);
223
auxp1 = BOP_intersectPlane(m_plane, p1, p3);
224
auxtag1 = classifyFaceIN( p1, p2, auxp1, plane);
225
auxtag2 = classifyFaceOUT(p2, p3, auxp1, plane);
226
return (BOP_compTAG(auxtag1,auxtag2)?BOP_addON(auxtag1):INOUT);
229
auxp1 = BOP_intersectPlane(m_plane, p1, p3);
230
auxtag1 = classifyFaceOUT(p1, p2, auxp1, plane);
231
auxtag2 = classifyFaceIN( p2, p3, auxp1, plane);
232
return (BOP_compTAG(auxtag1,auxtag2)?BOP_addON(auxtag1):INOUT);
235
auxp1 = BOP_intersectPlane(m_plane, p2, p3);
236
auxtag1 = classifyFaceIN( p1, p2, auxp1, plane);
237
auxtag2 = classifyFaceOUT(auxp1, p3, p1, plane);
238
return (BOP_compTAG(auxtag1,auxtag2)?BOP_addON(auxtag1):INOUT);
241
auxp1 = BOP_intersectPlane(m_plane, p2, p3);
242
auxtag1 = classifyFaceOUT(p1, p2, auxp1, plane);
243
auxtag2 = classifyFaceIN( auxp1, p3, p1, plane);
244
return (BOP_compTAG(auxtag1,auxtag2)?BOP_addON(auxtag1):INOUT);
246
// Classify IN/OUT face without ON vertices.
247
// Two ways to divide the triangle,
248
// will chose the least degenerated sub-triangles.
250
auxp1 = BOP_intersectPlane(m_plane, p1, p2);
251
auxp2 = BOP_intersectPlane(m_plane, p1, p3);
253
// f1: p1 auxp1 , auxp1 auxp2
254
auxtag1 = classifyFaceIN(p1, auxp1, auxp2, plane);
256
// f2: auxp1 p2 , p2 auxp2; f3: p2 p3 , p3 auxp2 ||
257
// f2: auxp1 p3, p3 auxp2; f3: p2 p3 , p3 auxp1
258
if (BOP_isInsideCircle(p2, p3, auxp1, auxp2)) {
259
auxtag2 = classifyFaceOUT(auxp1, p2, auxp2, plane);
260
auxtag3 = classifyFaceOUT(p2, p3, auxp2, plane);
263
auxtag2 = classifyFaceOUT(auxp1, p3, auxp2, plane);
264
auxtag3 = classifyFaceOUT(p2, p3, auxp1, plane);
266
return (BOP_compTAG(auxtag1,auxtag2)&&BOP_compTAG(auxtag2,auxtag3)?auxtag1:INOUT);
269
auxp1 = BOP_intersectPlane(m_plane, p1, p2);
270
auxp2 = BOP_intersectPlane(m_plane, p1, p3);
272
// f1: p1 auxp1 , auxp1 auxp2
273
auxtag1 = classifyFaceOUT(p1, auxp1, auxp2, plane);
275
// f2: auxp1 p2 , p2 auxp2; f3: p2 p3 , p3 auxp2 ||
276
// f2: auxp1 p3, p3 auxp2; f3: p2 p3 , p3 auxp1
277
if (BOP_isInsideCircle(p2, p3, auxp1, auxp2)) {
278
auxtag2 = classifyFaceIN(auxp1, p2, auxp2, plane);
279
auxtag3 = classifyFaceIN(p2, p3, auxp2, plane);
282
auxtag2 = classifyFaceIN(auxp1, p3, auxp2, plane);
283
auxtag3 = classifyFaceIN(p2, p3, auxp1, plane);
285
return (BOP_compTAG(auxtag1,auxtag2)&&BOP_compTAG(auxtag2,auxtag3)?auxtag1:INOUT);
288
auxp1 = BOP_intersectPlane(m_plane, p2, p1);
289
auxp2 = BOP_intersectPlane(m_plane, p2, p3);
291
// f1: auxp1 p2 , p2 auxp2
292
auxtag1 = classifyFaceIN(auxp1, p2, auxp2, plane);
294
// f2: p1 auxp1 , auxp1 auxp2; f3: p1 auxp2 , auxp2 p3 ||
295
// f2: p3 auxp1, auxp1 auxp2 f3:p1 auxp1, auxp1 p3
296
if (BOP_isInsideCircle(p1, p3, auxp1, auxp2)) {
297
auxtag2 = classifyFaceOUT(p1, auxp1, auxp2, plane);
298
auxtag3 = classifyFaceOUT(p1, auxp2, p3, plane);
301
auxtag2 = classifyFaceOUT(p3, auxp1, auxp2, plane);
302
auxtag3 = classifyFaceOUT(p1, auxp1, p3, plane);
304
return (BOP_compTAG(auxtag1,auxtag2)&&BOP_compTAG(auxtag2,auxtag3)?auxtag1:INOUT);
307
auxp1 = BOP_intersectPlane(m_plane, p2, p1);
308
auxp2 = BOP_intersectPlane(m_plane, p2, p3);
310
// f1: auxp1 p2 , p2 auxp2
311
auxtag1 = classifyFaceOUT(auxp1, p2, auxp2, plane);
313
// f2: p1 auxp1 , auxp1 auxp2; f3: p1 auxp2 , auxp2 p3 ||
314
// f2: p3 auxp1, auxp1 auxp2 f3:p1 auxp1, auxp1 p3
315
if (BOP_isInsideCircle(p1, p3, auxp1, auxp2)) {
316
auxtag2 = classifyFaceIN(p1, auxp1, auxp2, plane);
317
auxtag3 = classifyFaceIN(p1, auxp2, p3, plane);
320
auxtag2 = classifyFaceIN(p3, auxp1, auxp2, plane);
321
auxtag3 = classifyFaceIN(p1, auxp1, p3, plane);
323
return (BOP_compTAG(auxtag1,auxtag2)&&BOP_compTAG(auxtag2,auxtag3)?auxtag1:INOUT);
326
auxp1 = BOP_intersectPlane(m_plane, p3, p1);
327
auxp2 = BOP_intersectPlane(m_plane, p3, p2);
329
// f1: auxp1 auxp2 , auxp2 p3
330
auxtag1 = classifyFaceIN(auxp1, auxp2, p3, plane);
332
// f2: p1 p2 , p2 auxp2; f3:p1 auxp2 , auxp2 auxp1 ||
333
// f2: p1 p2, p2 auxp1; f3:p2 auxp2, auxp2 auxp1
334
if (BOP_isInsideCircle(p1, p2, auxp1, auxp2)) {
335
auxtag2 = classifyFaceOUT(p1, p2, auxp2, plane);
336
auxtag3 = classifyFaceOUT(p1, auxp2, auxp1, plane);
339
auxtag2 = classifyFaceOUT(p1, p2, auxp1, plane);
340
auxtag3 = classifyFaceOUT(p2, auxp2, auxp1, plane);
342
return (BOP_compTAG(auxtag1,auxtag2)&&BOP_compTAG(auxtag2,auxtag3)?auxtag1:INOUT);
345
auxp1 = BOP_intersectPlane(m_plane, p3, p1);
346
auxp2 = BOP_intersectPlane(m_plane, p3, p2);
348
// f1: auxp1 auxp2 , auxp2 p3
349
auxtag1 = classifyFaceOUT(auxp1, auxp2, p3, plane);
351
// f2: p1 p2 , p2 auxp2; f3:p1 auxp2 , auxp2 auxp1 ||
352
// f2: p1 p2, p2 auxp1; f3:p2 auxp2, auxp2 auxp1
353
if (BOP_isInsideCircle(p1, p2, auxp1, auxp2)) {
354
auxtag2 = classifyFaceIN(p1, p2, auxp2, plane);
355
auxtag3 = classifyFaceIN(p1, auxp2, auxp1, plane);
358
auxtag2 = classifyFaceIN(p1, p2, auxp1, plane);
359
auxtag3 = classifyFaceIN(p2, auxp2, auxp1, plane);
361
return (BOP_compTAG(auxtag1,auxtag2)&&BOP_compTAG(auxtag2,auxtag3)?auxtag1:INOUT);
369
* Classifies a face through IN subtree.
370
* @param p1 firts face vertex.
371
* @param p2 second face vertex.
372
* @param p3 third face vertex.
373
* @param plane face plane.
375
BOP_TAG BOP_BSPNode::classifyFaceIN(const MT_Point3& p1,
378
const MT_Plane3& plane) const
380
if (m_inChild != NULL)
381
return m_inChild->classifyFace(p1, p2, p3, plane);
387
* Classifies a face through OUT subtree.
388
* @param p1 firts face vertex.
389
* @param p2 second face vertex.
390
* @param p3 third face vertex.
391
* @param plane face plane.
393
BOP_TAG BOP_BSPNode::classifyFaceOUT(const MT_Point3& p1,
396
const MT_Plane3& plane) const
398
if (m_outChild != NULL)
399
return m_outChild->classifyFace(p1, p2, p3, plane);
405
* Simplified classification (optimized but requires that the face is not
406
* INOUT; only works correctly with faces completely IN or OUT).
407
* @param p1 firts face vertex.
408
* @param p2 second face vertex.
409
* @param p3 third face vertex.
410
* @param plane face plane.
411
* @return TAG result: IN or OUT.
413
BOP_TAG BOP_BSPNode::simplifiedClassifyFace(const MT_Point3& p1,
416
const MT_Plane3& plane) const
420
BOP_TAG tag = BOP_createTAG(testPoint(p1),testPoint(p2),testPoint(p3));
422
if ((tag & IN_IN_IN) != 0) {
423
if ((tag & OUT_OUT_OUT) != 0) {
424
if (splitTriangle(ret,m_plane,p1,p2,p3,tag)<0)
425
return simplifiedClassifyFaceIN(ret[0],ret[1],ret[2],plane);
427
return simplifiedClassifyFaceOUT(ret[0],ret[1],ret[2],plane);
430
return simplifiedClassifyFaceIN(p1,p2,p3,plane);
434
if ((tag & OUT_OUT_OUT) != 0) {
435
return simplifiedClassifyFaceOUT(p1,p2,p3,plane);
438
if (hasSameOrientation(plane)) {
439
return simplifiedClassifyFaceIN(p1,p2,p3,plane);
442
return simplifiedClassifyFaceOUT(p1,p2,p3,plane);
451
* Simplified classify through IN subtree.
452
* @param p1 firts face vertex.
453
* @param p2 second face vertex.
454
* @param p3 third face vertex.
455
* @param plane face plane.
457
BOP_TAG BOP_BSPNode::simplifiedClassifyFaceIN(const MT_Point3& p1,
460
const MT_Plane3& plane) const
462
if (m_inChild != NULL)
463
return m_inChild->simplifiedClassifyFace(p1, p2, p3, plane);
469
* Simplified classify through OUT subtree.
470
* @param p1 firts face vertex.
471
* @param p2 second face vertex.
472
* @param p3 third face vertex.
473
* @param plane face plane.
475
BOP_TAG BOP_BSPNode::simplifiedClassifyFaceOUT(const MT_Point3& p1,
478
const MT_Plane3& plane) const
480
if (m_outChild != NULL)
481
return m_outChild->simplifiedClassifyFace(p1, p2, p3, plane);
487
* Determine if the input plane have the same orientation of the node plane.
488
* @param plane plane to test.
489
* @return TRUE if have the same orientation, FALSE otherwise.
491
bool BOP_BSPNode::hasSameOrientation(const MT_Plane3& plane) const
493
return (BOP_orientation(m_plane,plane)>0);
497
* Comparation between both childrens.
498
* @return 0 equal deep, 1 inChild more deep than outChild and -1 otherwise.
500
int BOP_BSPNode::compChildren() const
502
unsigned int deep1 = (m_inChild == NULL?0:m_inChild->getDeep());
503
unsigned int deep2 = (m_outChild == NULL?0:m_outChild->getDeep());
507
else if (deep1 < deep2)
514
* Extract a subtriangle from input triangle, is used for simplified classification.
515
* The subtriangle is obtained spliting the input triangle by input plane.
516
* @param res output subtriangle result.
517
* @param plane spliter plane.
518
* @param p1 first triangle point.
519
* @param p2 second triangle point.
520
* @param p3 third triangle point.
521
* @param tag triangle orientation respect the plane.
523
int BOP_BSPNode::splitTriangle(MT_Point3* res,
524
const MT_Plane3& plane,
528
const BOP_TAG tag) const
532
if (compChildren()<0) {
533
// f1: p1 new p3 || new = splitedge(p1,p2)
535
res[1] = BOP_intersectPlane( plane, p1, p2 );
539
// f1: p2 new p3 || new = splitedge(p1,p2)
542
res[2] = BOP_intersectPlane( plane, p1, p2 );
546
if (compChildren()<0) {
547
// f1: p2 new p3 || new = splitedge(p1,p2)
550
res[2] = BOP_intersectPlane( plane, p1, p2 );
553
// f1: p1 new p3 || new = splitedge(p1,p2)
555
res[1] = BOP_intersectPlane( plane, p1, p2 );
560
if (compChildren()<0) {
561
// f1: p1 p2 new || new = splitedge(p1,p3)
564
res[2] = BOP_intersectPlane( plane, p1, p3 );
567
// f1: p2 p3 new || new = splitedge(p1,p3)
570
res[2] = BOP_intersectPlane( plane, p1, p3 );
574
if (compChildren()<0) {
575
// f1: p2 p3 new || new = splitedge(p1,p3)
578
res[2] = BOP_intersectPlane( plane, p1, p3 );
581
// f1: p1 p2 new || new = splitedge(p1,p3)
584
res[2] = BOP_intersectPlane( plane, p1, p3 );
588
if (compChildren()<0) {
589
// f1: p1 p2 new || new = splitedge(p2,p3)
592
res[2] = BOP_intersectPlane( plane, p2, p3 );
595
// f1: p1 p3 new || new = splitedge(p2,p3)
597
res[1] = BOP_intersectPlane( plane, p2, p3 );
602
if (compChildren()<0) {
603
// f1: p1 p2 new || new = splitedge(p2,p3)
605
res[1] = BOP_intersectPlane( plane, p2, p3 );
609
// f1: p1 p2 new || new = splitedge(p2,p3)
612
res[2] = BOP_intersectPlane( plane, p2, p3 );
616
if (compChildren()<=0) {
617
// f1: p1 new1 new2 || new1 = splitedge(p1,p2) new2 = splitedge(p1,p3)
619
res[1] = BOP_intersectPlane( plane, p1, p2 );
620
res[2] = BOP_intersectPlane( plane, p1, p3 );
623
// f1: p1 new1 new2 || new1 = splitedge(p1,p2) new2 = splitedge(p1,p3)
624
res[0] = BOP_intersectPlane( plane, p1, p2 );
630
if (compChildren()<0) {
631
// f1: p1 new1 new2 || new1 = splitedge(p1,p2) new2 = splitedge(p1,p3)
632
res[0] = BOP_intersectPlane( plane, p1, p2 );
637
// f1: p1 new1 new2 || new1 = splitedge(p1,p2) new2 = splitedge(p1,p3)
639
res[1] = BOP_intersectPlane( plane, p1, p2 );
640
res[2] = BOP_intersectPlane( plane, p1, p3 );
644
if (compChildren()<=0) {
645
// f1: new1 p2 new2 || new1 = splitedge(p2,p1) new2 = splitedge(p2,p3)
646
res[0] = BOP_intersectPlane( plane, p2, p1 );
648
res[2] = BOP_intersectPlane( plane, p2, p3 );
651
// f1: new1 p2 new2 || new1 = splitedge(p2,p1) new2 = splitedge(p2,p3)
653
res[1] = BOP_intersectPlane( plane, p2, p1 );
654
res[2] = BOP_intersectPlane( plane, p2, p3 );
658
if (compChildren()<0) {
659
// f1: new1 p2 new2 || new1 = splitedge(p2,p1) new2 = splitedge(p2,p3)
661
res[1] = BOP_intersectPlane( plane, p2, p1 );
662
res[2] = BOP_intersectPlane( plane, p2, p3 );
665
// f1: new1 p2 new2 || new1 = splitedge(p2,p1) new2 = splitedge(p2,p3)
666
res[0] = BOP_intersectPlane( plane, p2, p1 );
668
res[2] = BOP_intersectPlane( plane, p2, p3 );
672
if (compChildren()<=0) {
673
// f1: new1 new2 p2 || new1 = splitedge(p3,p1) new2 = splitedge(p3,p2)
674
res[0] = BOP_intersectPlane( plane, p3, p1 );
675
res[1] = BOP_intersectPlane( plane, p3, p2 );
679
// f1: new1 new2 p2 || new1 = splitedge(p3,p1) new2 = splitedge(p3,p2)
680
res[0] = BOP_intersectPlane( plane, p3, p1 );
686
if (compChildren()<0) {
687
// f1: new1 new2 p2 || new1 = splitedge(p3,p1) new2 = splitedge(p3,p2)
688
res[0] = BOP_intersectPlane( plane, p3, p1 );
693
// f1: new1 new2 p2 || new1 = splitedge(p3,p1) new2 = splitedge(p3,p2)
694
res[0] = BOP_intersectPlane( plane, p3, p1 );
695
res[1] = BOP_intersectPlane( plane, p3, p2 );
707
void BOP_BSPNode::print(unsigned int deep)
709
std::cout << "(" << deep << "," << m_plane << ")," << std::endl;
710
if (m_inChild != NULL)
711
m_inChild->print(deep + 1);
713
std::cout << "(" << deep+1 << ",None)," << std::endl;
714
if (m_outChild != NULL)
715
m_outChild->print(deep + 1);
717
std::cout << "(" << deep+1 << ",None)," << std::endl;