vrshoot

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