vrshoot
view libs/assimp/IFCBoolean.cpp @ 0:b2f14e535253
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 01 Feb 2014 19:58:19 +0200 |
parents | |
children |
line source
1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
5 Copyright (c) 2006-2010, assimp team
6 All rights reserved.
8 Redistribution and use of this software in source and binary forms,
9 with or without modification, are permitted provided that the
10 following conditions are met:
12 * Redistributions of source code must retain the above
13 copyright notice, this list of conditions and the
14 following disclaimer.
16 * Redistributions in binary form must reproduce the above
17 copyright notice, this list of conditions and the
18 following disclaimer in the documentation and/or other
19 materials provided with the distribution.
21 * Neither the name of the assimp team, nor the names of its
22 contributors may be used to endorse or promote products
23 derived from this software without specific prior
24 written permission of the assimp team.
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 ----------------------------------------------------------------------
39 */
41 /** @file IFCBoolean.cpp
42 * @brief Implements a subset of Ifc boolean operations
43 */
45 #include "AssimpPCH.h"
47 #ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
48 #include "IFCUtil.h"
49 #include "PolyTools.h"
50 #include "ProcessHelper.h"
52 #include <iterator>
54 namespace Assimp {
55 namespace IFC {
57 // ------------------------------------------------------------------------------------------------
58 enum Intersect {
59 Intersect_No,
60 Intersect_LiesOnPlane,
61 Intersect_Yes
62 };
64 // ------------------------------------------------------------------------------------------------
65 Intersect IntersectSegmentPlane(const IfcVector3& p,const IfcVector3& n, const IfcVector3& e0,
66 const IfcVector3& e1,
67 IfcVector3& out)
68 {
69 const IfcVector3 pdelta = e0 - p, seg = e1-e0;
70 const IfcFloat dotOne = n*seg, dotTwo = -(n*pdelta);
72 if (fabs(dotOne) < 1e-6) {
73 return fabs(dotTwo) < 1e-6f ? Intersect_LiesOnPlane : Intersect_No;
74 }
76 const IfcFloat t = dotTwo/dotOne;
77 // t must be in [0..1] if the intersection point is within the given segment
78 if (t > 1.f || t < 0.f) {
79 return Intersect_No;
80 }
81 out = e0+t*seg;
82 return Intersect_Yes;
83 }
85 // ------------------------------------------------------------------------------------------------
86 void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& result,
87 const TempMesh& first_operand,
88 ConversionData& conv)
89 {
90 ai_assert(hs != NULL);
92 const IfcPlane* const plane = hs->BaseSurface->ToPtr<IfcPlane>();
93 if(!plane) {
94 IFCImporter::LogError("expected IfcPlane as base surface for the IfcHalfSpaceSolid");
95 return;
96 }
98 // extract plane base position vector and normal vector
99 IfcVector3 p,n(0.f,0.f,1.f);
100 if (plane->Position->Axis) {
101 ConvertDirection(n,plane->Position->Axis.Get());
102 }
103 ConvertCartesianPoint(p,plane->Position->Location);
105 if(!IsTrue(hs->AgreementFlag)) {
106 n *= -1.f;
107 }
109 // clip the current contents of `meshout` against the plane we obtained from the second operand
110 const std::vector<IfcVector3>& in = first_operand.verts;
111 std::vector<IfcVector3>& outvert = result.verts;
113 std::vector<unsigned int>::const_iterator begin = first_operand.vertcnt.begin(),
114 end = first_operand.vertcnt.end(), iit;
116 outvert.reserve(in.size());
117 result.vertcnt.reserve(first_operand.vertcnt.size());
119 unsigned int vidx = 0;
120 for(iit = begin; iit != end; vidx += *iit++) {
122 unsigned int newcount = 0;
123 for(unsigned int i = 0; i < *iit; ++i) {
124 const IfcVector3& e0 = in[vidx+i], e1 = in[vidx+(i+1)%*iit];
126 // does the next segment intersect the plane?
127 IfcVector3 isectpos;
128 const Intersect isect = IntersectSegmentPlane(p,n,e0,e1,isectpos);
129 if (isect == Intersect_No || isect == Intersect_LiesOnPlane) {
130 if ( (e0-p).Normalize()*n > 0 ) {
131 outvert.push_back(e0);
132 ++newcount;
133 }
134 }
135 else if (isect == Intersect_Yes) {
136 if ( (e0-p).Normalize()*n > 0 ) {
137 // e0 is on the right side, so keep it
138 outvert.push_back(e0);
139 outvert.push_back(isectpos);
140 newcount += 2;
141 }
142 else {
143 // e0 is on the wrong side, so drop it and keep e1 instead
144 outvert.push_back(isectpos);
145 ++newcount;
146 }
147 }
148 }
150 if (!newcount) {
151 continue;
152 }
154 IfcVector3 vmin,vmax;
155 ArrayBounds(&*(outvert.end()-newcount),newcount,vmin,vmax);
157 // filter our IfcFloat points - those may happen if a point lies
158 // directly on the intersection line. However, due to IfcFloat
159 // precision a bitwise comparison is not feasible to detect
160 // this case.
161 const IfcFloat epsilon = (vmax-vmin).SquareLength() / 1e6f;
162 FuzzyVectorCompare fz(epsilon);
164 std::vector<IfcVector3>::iterator e = std::unique( outvert.end()-newcount, outvert.end(), fz );
166 if (e != outvert.end()) {
167 newcount -= static_cast<unsigned int>(std::distance(e,outvert.end()));
168 outvert.erase(e,outvert.end());
169 }
170 if (fz(*( outvert.end()-newcount),outvert.back())) {
171 outvert.pop_back();
172 --newcount;
173 }
174 if(newcount > 2) {
175 result.vertcnt.push_back(newcount);
176 }
177 else while(newcount-->0) {
178 result.verts.pop_back();
179 }
181 }
182 IFCImporter::LogDebug("generating CSG geometry by plane clipping (IfcBooleanClippingResult)");
183 }
185 // ------------------------------------------------------------------------------------------------
186 // Check if e0-e1 intersects a sub-segment of the given boundary line.
187 // note: this functions works on 3D vectors, but performs its intersection checks solely in xy.
188 bool IntersectsBoundaryProfile( const IfcVector3& e0, const IfcVector3& e1, const std::vector<IfcVector3>& boundary,
189 std::vector<size_t>& intersected_boundary_segments,
190 std::vector<IfcVector3>& intersected_boundary_points,
191 bool half_open = false,
192 bool* e0_hits_border = NULL)
193 {
194 ai_assert(intersected_boundary_segments.empty());
195 ai_assert(intersected_boundary_points.empty());
197 if(e0_hits_border) {
198 *e0_hits_border = false;
199 }
201 const IfcVector3& e = e1 - e0;
203 for (size_t i = 0, bcount = boundary.size(); i < bcount; ++i) {
204 // boundary segment i: b0-b1
205 const IfcVector3& b0 = boundary[i];
206 const IfcVector3& b1 = boundary[(i+1) % bcount];
208 const IfcVector3& b = b1 - b0;
210 // segment-segment intersection
211 // solve b0 + b*s = e0 + e*t for (s,t)
212 const IfcFloat det = (-b.x * e.y + e.x * b.y);
213 if(fabs(det) < 1e-6) {
214 // no solutions (parallel lines)
215 continue;
216 }
218 const IfcFloat x = b0.x - e0.x;
219 const IfcFloat y = b0.y - e0.y;
221 const IfcFloat s = (x*e.y - e.x*y)/det;
222 const IfcFloat t = (x*b.y - b.x*y)/det;
224 #ifdef _DEBUG
225 const IfcVector3 check = b0 + b*s - (e0 + e*t);
226 ai_assert((IfcVector2(check.x,check.y)).SquareLength() < 1e-5);
227 #endif
229 // for a valid intersection, s-t should be in range [0,1].
230 // note that for t (i.e. the segment point) we only use a
231 // half-sided epsilon because the next segment should catch
232 // this case.
233 const IfcFloat epsilon = 1e-6;
234 if (t >= -epsilon && (t <= 1.0+epsilon || half_open) && s >= -epsilon && s <= 1.0) {
236 if (e0_hits_border && !*e0_hits_border) {
237 *e0_hits_border = fabs(t) < 1e-5f;
238 }
240 const IfcVector3& p = e0 + e*t;
242 // only insert the point into the list if it is sufficiently
243 // far away from the previous intersection point. This way,
244 // we avoid duplicate detection if the intersection is
245 // directly on the vertex between two segments.
246 if (!intersected_boundary_points.empty() && intersected_boundary_segments.back()==i-1 ) {
247 const IfcVector3 diff = intersected_boundary_points.back() - p;
248 if(IfcVector2(diff.x, diff.y).SquareLength() < 1e-7) {
249 continue;
250 }
251 }
252 intersected_boundary_segments.push_back(i);
253 intersected_boundary_points.push_back(p);
254 }
255 }
257 return !intersected_boundary_segments.empty();
258 }
261 // ------------------------------------------------------------------------------------------------
262 // note: this functions works on 3D vectors, but performs its intersection checks solely in xy.
263 bool PointInPoly(const IfcVector3& p, const std::vector<IfcVector3>& boundary)
264 {
265 // even-odd algorithm: take a random vector that extends from p to infinite
266 // and counts how many times it intersects edges of the boundary.
267 // because checking for segment intersections is prone to numeric inaccuracies
268 // or double detections (i.e. when hitting multiple adjacent segments at their
269 // shared vertices) we do it thrice with different rays and vote on it.
271 // the even-odd algorithm doesn't work for points which lie directly on
272 // the border of the polygon. If any of our attempts produces this result,
273 // we return false immediately.
275 std::vector<size_t> intersected_boundary_segments;
276 std::vector<IfcVector3> intersected_boundary_points;
277 size_t votes = 0;
279 bool is_border;
280 IntersectsBoundaryProfile(p, p + IfcVector3(1.0,0,0), boundary,
281 intersected_boundary_segments,
282 intersected_boundary_points, true, &is_border);
284 if(is_border) {
285 return false;
286 }
288 votes += intersected_boundary_segments.size() % 2;
290 intersected_boundary_segments.clear();
291 intersected_boundary_points.clear();
293 IntersectsBoundaryProfile(p, p + IfcVector3(0,1.0,0), boundary,
294 intersected_boundary_segments,
295 intersected_boundary_points, true, &is_border);
297 if(is_border) {
298 return false;
299 }
301 votes += intersected_boundary_segments.size() % 2;
303 intersected_boundary_segments.clear();
304 intersected_boundary_points.clear();
306 IntersectsBoundaryProfile(p, p + IfcVector3(0.6,-0.6,0.0), boundary,
307 intersected_boundary_segments,
308 intersected_boundary_points, true, &is_border);
310 if(is_border) {
311 return false;
312 }
314 votes += intersected_boundary_segments.size() % 2;
315 //ai_assert(votes == 3 || votes == 0);
316 return votes > 1;
317 }
320 // ------------------------------------------------------------------------------------------------
321 void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const IfcPolygonalBoundedHalfSpace* hs, TempMesh& result,
322 const TempMesh& first_operand,
323 ConversionData& conv)
324 {
325 ai_assert(hs != NULL);
327 const IfcPlane* const plane = hs->BaseSurface->ToPtr<IfcPlane>();
328 if(!plane) {
329 IFCImporter::LogError("expected IfcPlane as base surface for the IfcHalfSpaceSolid");
330 return;
331 }
333 // extract plane base position vector and normal vector
334 IfcVector3 p,n(0.f,0.f,1.f);
335 if (plane->Position->Axis) {
336 ConvertDirection(n,plane->Position->Axis.Get());
337 }
338 ConvertCartesianPoint(p,plane->Position->Location);
340 if(!IsTrue(hs->AgreementFlag)) {
341 n *= -1.f;
342 }
344 n.Normalize();
346 // obtain the polygonal bounding volume
347 boost::shared_ptr<TempMesh> profile = boost::shared_ptr<TempMesh>(new TempMesh());
348 if(!ProcessCurve(hs->PolygonalBoundary, *profile.get(), conv)) {
349 IFCImporter::LogError("expected valid polyline for boundary of boolean halfspace");
350 return;
351 }
353 IfcMatrix4 proj_inv;
354 ConvertAxisPlacement(proj_inv,hs->Position);
356 // and map everything into a plane coordinate space so all intersection
357 // tests can be done in 2D space.
358 IfcMatrix4 proj = proj_inv;
359 proj.Inverse();
361 // clip the current contents of `meshout` against the plane we obtained from the second operand
362 const std::vector<IfcVector3>& in = first_operand.verts;
363 std::vector<IfcVector3>& outvert = result.verts;
365 std::vector<unsigned int>::const_iterator begin = first_operand.vertcnt.begin(),
366 end = first_operand.vertcnt.end(), iit;
368 outvert.reserve(in.size());
369 result.vertcnt.reserve(first_operand.vertcnt.size());
371 std::vector<size_t> intersected_boundary_segments;
372 std::vector<IfcVector3> intersected_boundary_points;
374 // TODO: the following algorithm doesn't handle all cases.
375 unsigned int vidx = 0;
376 for(iit = begin; iit != end; vidx += *iit++) {
377 if (!*iit) {
378 continue;
379 }
381 unsigned int newcount = 0;
382 bool was_outside_boundary = !PointInPoly(proj * in[vidx], profile->verts);
384 // used any more?
385 //size_t last_intersected_boundary_segment;
386 IfcVector3 last_intersected_boundary_point;
388 bool extra_point_flag = false;
389 IfcVector3 extra_point;
391 IfcVector3 enter_volume;
392 bool entered_volume_flag = false;
394 for(unsigned int i = 0; i < *iit; ++i) {
395 // current segment: [i,i+1 mod size] or [*extra_point,i] if extra_point_flag is set
396 const IfcVector3& e0 = extra_point_flag ? extra_point : in[vidx+i];
397 const IfcVector3& e1 = extra_point_flag ? in[vidx+i] : in[vidx+(i+1)%*iit];
399 // does the current segment intersect the polygonal boundary?
400 const IfcVector3& e0_plane = proj * e0;
401 const IfcVector3& e1_plane = proj * e1;
403 intersected_boundary_segments.clear();
404 intersected_boundary_points.clear();
406 const bool is_outside_boundary = !PointInPoly(e1_plane, profile->verts);
407 const bool is_boundary_intersection = is_outside_boundary != was_outside_boundary;
409 IntersectsBoundaryProfile(e0_plane, e1_plane, profile->verts,
410 intersected_boundary_segments,
411 intersected_boundary_points);
413 ai_assert(!is_boundary_intersection || !intersected_boundary_segments.empty());
415 // does the current segment intersect the plane?
416 // (no extra check if this is an extra point)
417 IfcVector3 isectpos;
418 const Intersect isect = extra_point_flag ? Intersect_No : IntersectSegmentPlane(p,n,e0,e1,isectpos);
420 #ifdef _DEBUG
421 if (isect == Intersect_Yes) {
422 const IfcFloat f = fabs((isectpos - p)*n);
423 ai_assert(f < 1e-5);
424 }
425 #endif
427 const bool is_white_side = (e0-p)*n >= -1e-6;
429 // e0 on good side of plane? (i.e. we should keep all geometry on this side)
430 if (is_white_side) {
431 // but is there an intersection in e0-e1 and is e1 in the clipping
432 // boundary? In this case, generate a line that only goes to the
433 // intersection point.
434 if (isect == Intersect_Yes && !is_outside_boundary) {
435 outvert.push_back(e0);
436 ++newcount;
438 outvert.push_back(isectpos);
439 ++newcount;
441 /*
442 // this is, however, only a line that goes to the plane, but not
443 // necessarily to the point where the bounding volume on the
444 // black side of the plane is hit. So basically, we need another
445 // check for [isectpos-e1], which should yield an intersection
446 // point.
447 extra_point_flag = true;
448 extra_point = isectpos;
450 was_outside_boundary = true;
451 continue; */
453 // [isectpos, enter_volume] potentially needs extra points.
454 // For this, we determine the intersection point with the
455 // bounding volume and project it onto the plane.
456 /*
457 const IfcVector3& enter_volume_proj = proj * enter_volume;
458 const IfcVector3& enter_isectpos = proj * isectpos;
460 intersected_boundary_segments.clear();
461 intersected_boundary_points.clear();
463 IntersectsBoundaryProfile(enter_volume_proj, enter_isectpos, profile->verts,
464 intersected_boundary_segments,
465 intersected_boundary_points);
467 if(!intersected_boundary_segments.empty()) {
469 vec = vec + ((p - vec) * n) * n;
470 }
471 */
473 //entered_volume_flag = true;
474 }
475 else {
476 outvert.push_back(e0);
477 ++newcount;
478 }
479 }
480 // e0 on bad side of plane, e1 on good (i.e. we should remove geometry on this side,
481 // but only if it is within the bounding volume).
482 else if (isect == Intersect_Yes) {
483 // is e0 within the clipping volume? Insert the intersection point
484 // of [e0,e1] and the plane instead of e0.
485 if(was_outside_boundary) {
486 outvert.push_back(e0);
487 }
488 else {
489 if(entered_volume_flag) {
490 const IfcVector3& fix_point = enter_volume + ((p - enter_volume) * n) * n;
491 outvert.push_back(fix_point);
492 ++newcount;
493 }
495 outvert.push_back(isectpos);
496 }
497 entered_volume_flag = false;
498 ++newcount;
499 }
500 else { // no intersection with plane or parallel; e0,e1 are on the bad side
502 // did we just pass the boundary line to the poly bounding?
503 if (is_boundary_intersection) {
505 // and are now outside the clipping boundary?
506 if (is_outside_boundary) {
507 // in this case, get the point where the clipping boundary
508 // was entered first. Then, get the point where the clipping
509 // boundary volume was left! These two points with the plane
510 // normal form another plane that intersects the clipping
511 // volume. There are two ways to get from the first to the
512 // second point along the intersection curve, try to pick the
513 // one that lies within the current polygon.
515 // TODO this approach doesn't handle all cases
517 // ...
519 IfcFloat d = 1e20;
520 IfcVector3 vclosest;
521 BOOST_FOREACH(const IfcVector3& v, intersected_boundary_points) {
522 const IfcFloat dn = (v-e1_plane).SquareLength();
523 if (dn < d) {
524 d = dn;
525 vclosest = v;
526 }
527 }
529 vclosest = proj_inv * vclosest;
530 if(entered_volume_flag) {
531 const IfcVector3& fix_point = vclosest + ((p - vclosest) * n) * n;
532 outvert.push_back(fix_point);
533 ++newcount;
535 entered_volume_flag = false;
536 }
538 outvert.push_back(vclosest);
539 ++newcount;
541 //outvert.push_back(e1);
542 //++newcount;
543 }
544 else {
545 entered_volume_flag = true;
547 // we just entered the clipping boundary. Record the point
548 // and the segment where we entered and also generate this point.
549 //last_intersected_boundary_segment = intersected_boundary_segments.front();
550 //last_intersected_boundary_point = intersected_boundary_points.front();
552 outvert.push_back(e0);
553 ++newcount;
555 IfcFloat d = 1e20;
556 IfcVector3 vclosest;
557 BOOST_FOREACH(const IfcVector3& v, intersected_boundary_points) {
558 const IfcFloat dn = (v-e0_plane).SquareLength();
559 if (dn < d) {
560 d = dn;
561 vclosest = v;
562 }
563 }
565 enter_volume = proj_inv * vclosest;
566 outvert.push_back(enter_volume);
567 ++newcount;
568 }
569 }
570 // if not, we just keep the vertex
571 else if (is_outside_boundary) {
572 outvert.push_back(e0);
573 ++newcount;
575 entered_volume_flag = false;
576 }
577 }
579 was_outside_boundary = is_outside_boundary;
580 extra_point_flag = false;
581 }
583 if (!newcount) {
584 continue;
585 }
587 IfcVector3 vmin,vmax;
588 ArrayBounds(&*(outvert.end()-newcount),newcount,vmin,vmax);
590 // filter our IfcFloat points - those may happen if a point lies
591 // directly on the intersection line. However, due to IfcFloat
592 // precision a bitwise comparison is not feasible to detect
593 // this case.
594 const IfcFloat epsilon = (vmax-vmin).SquareLength() / 1e6f;
595 FuzzyVectorCompare fz(epsilon);
597 std::vector<IfcVector3>::iterator e = std::unique( outvert.end()-newcount, outvert.end(), fz );
599 if (e != outvert.end()) {
600 newcount -= static_cast<unsigned int>(std::distance(e,outvert.end()));
601 outvert.erase(e,outvert.end());
602 }
603 if (fz(*( outvert.end()-newcount),outvert.back())) {
604 outvert.pop_back();
605 --newcount;
606 }
607 if(newcount > 2) {
608 result.vertcnt.push_back(newcount);
609 }
610 else while(newcount-->0) {
611 result.verts.pop_back();
612 }
614 }
615 IFCImporter::LogDebug("generating CSG geometry by plane clipping with polygonal bounding (IfcBooleanClippingResult)");
616 }
618 // ------------------------------------------------------------------------------------------------
619 void ProcessBooleanExtrudedAreaSolidDifference(const IfcExtrudedAreaSolid* as, TempMesh& result,
620 const TempMesh& first_operand,
621 ConversionData& conv)
622 {
623 ai_assert(as != NULL);
625 // This case is handled by reduction to an instance of the quadrify() algorithm.
626 // Obviously, this won't work for arbitrarily complex cases. In fact, the first
627 // operand should be near-planar. Luckily, this is usually the case in Ifc
628 // buildings.
630 boost::shared_ptr<TempMesh> meshtmp = boost::shared_ptr<TempMesh>(new TempMesh());
631 ProcessExtrudedAreaSolid(*as,*meshtmp,conv,false);
633 std::vector<TempOpening> openings(1, TempOpening(as,IfcVector3(0,0,0),meshtmp,boost::shared_ptr<TempMesh>()));
635 result = first_operand;
637 TempMesh temp;
639 std::vector<IfcVector3>::const_iterator vit = first_operand.verts.begin();
640 BOOST_FOREACH(unsigned int pcount, first_operand.vertcnt) {
641 temp.Clear();
643 temp.verts.insert(temp.verts.end(), vit, vit + pcount);
644 temp.vertcnt.push_back(pcount);
646 // The algorithms used to generate mesh geometry sometimes
647 // spit out lines or other degenerates which must be
648 // filtered to avoid running into assertions later on.
650 // ComputePolygonNormal returns the Newell normal, so the
651 // length of the normal is the area of the polygon.
652 const IfcVector3& normal = temp.ComputeLastPolygonNormal(false);
653 if (normal.SquareLength() < static_cast<IfcFloat>(1e-5)) {
654 IFCImporter::LogWarn("skipping degenerate polygon (ProcessBooleanExtrudedAreaSolidDifference)");
655 continue;
656 }
658 GenerateOpenings(openings, std::vector<IfcVector3>(1,IfcVector3(1,0,0)), temp, false, true);
659 result.Append(temp);
661 vit += pcount;
662 }
664 IFCImporter::LogDebug("generating CSG geometry by geometric difference to a solid (IfcExtrudedAreaSolid)");
665 }
667 // ------------------------------------------------------------------------------------------------
668 void ProcessBoolean(const IfcBooleanResult& boolean, TempMesh& result, ConversionData& conv)
669 {
670 // supported CSG operations:
671 // DIFFERENCE
672 if(const IfcBooleanResult* const clip = boolean.ToPtr<IfcBooleanResult>()) {
673 if(clip->Operator != "DIFFERENCE") {
674 IFCImporter::LogWarn("encountered unsupported boolean operator: " + (std::string)clip->Operator);
675 return;
676 }
678 // supported cases (1st operand):
679 // IfcBooleanResult -- call ProcessBoolean recursively
680 // IfcSweptAreaSolid -- obtain polygonal geometry first
682 // supported cases (2nd operand):
683 // IfcHalfSpaceSolid -- easy, clip against plane
684 // IfcExtrudedAreaSolid -- reduce to an instance of the quadrify() algorithm
687 const IfcHalfSpaceSolid* const hs = clip->SecondOperand->ResolveSelectPtr<IfcHalfSpaceSolid>(conv.db);
688 const IfcExtrudedAreaSolid* const as = clip->SecondOperand->ResolveSelectPtr<IfcExtrudedAreaSolid>(conv.db);
689 if(!hs && !as) {
690 IFCImporter::LogError("expected IfcHalfSpaceSolid or IfcExtrudedAreaSolid as second clipping operand");
691 return;
692 }
694 TempMesh first_operand;
695 if(const IfcBooleanResult* const op0 = clip->FirstOperand->ResolveSelectPtr<IfcBooleanResult>(conv.db)) {
696 ProcessBoolean(*op0,first_operand,conv);
697 }
698 else if (const IfcSweptAreaSolid* const swept = clip->FirstOperand->ResolveSelectPtr<IfcSweptAreaSolid>(conv.db)) {
699 ProcessSweptAreaSolid(*swept,first_operand,conv);
700 }
701 else {
702 IFCImporter::LogError("expected IfcSweptAreaSolid or IfcBooleanResult as first clipping operand");
703 return;
704 }
706 if(hs) {
708 const IfcPolygonalBoundedHalfSpace* const hs_bounded = clip->SecondOperand->ResolveSelectPtr<IfcPolygonalBoundedHalfSpace>(conv.db);
709 if (hs_bounded) {
710 ProcessPolygonalBoundedBooleanHalfSpaceDifference(hs_bounded, result, first_operand, conv);
711 }
712 else {
713 ProcessBooleanHalfSpaceDifference(hs, result, first_operand, conv);
714 }
715 }
716 else {
717 ProcessBooleanExtrudedAreaSolidDifference(as, result, first_operand, conv);
718 }
719 }
720 else {
721 IFCImporter::LogWarn("skipping unknown IfcBooleanResult entity, type is " + boolean.GetClassName());
722 }
723 }
725 } // ! IFC
726 } // ! Assimp
728 #endif