vrshoot

diff 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 diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/libs/assimp/IFCBoolean.cpp	Sat Feb 01 19:58:19 2014 +0200
     1.3 @@ -0,0 +1,729 @@
     1.4 +/*
     1.5 +Open Asset Import Library (assimp)
     1.6 +----------------------------------------------------------------------
     1.7 +
     1.8 +Copyright (c) 2006-2010, assimp team
     1.9 +All rights reserved.
    1.10 +
    1.11 +Redistribution and use of this software in source and binary forms, 
    1.12 +with or without modification, are permitted provided that the 
    1.13 +following conditions are met:
    1.14 +
    1.15 +* Redistributions of source code must retain the above
    1.16 +  copyright notice, this list of conditions and the
    1.17 +  following disclaimer.
    1.18 +
    1.19 +* Redistributions in binary form must reproduce the above
    1.20 +  copyright notice, this list of conditions and the
    1.21 +  following disclaimer in the documentation and/or other
    1.22 +  materials provided with the distribution.
    1.23 +
    1.24 +* Neither the name of the assimp team, nor the names of its
    1.25 +  contributors may be used to endorse or promote products
    1.26 +  derived from this software without specific prior
    1.27 +  written permission of the assimp team.
    1.28 +
    1.29 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
    1.30 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
    1.31 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    1.32 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
    1.33 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.34 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
    1.35 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    1.36 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
    1.37 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
    1.38 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
    1.39 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.40 +
    1.41 +----------------------------------------------------------------------
    1.42 +*/
    1.43 +
    1.44 +/** @file  IFCBoolean.cpp
    1.45 + *  @brief Implements a subset of Ifc boolean operations
    1.46 + */
    1.47 +
    1.48 +#include "AssimpPCH.h"
    1.49 +
    1.50 +#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
    1.51 +#include "IFCUtil.h"
    1.52 +#include "PolyTools.h"
    1.53 +#include "ProcessHelper.h"
    1.54 +
    1.55 +#include <iterator>
    1.56 +
    1.57 +namespace Assimp {
    1.58 +	namespace IFC {
    1.59 +		
    1.60 +// ------------------------------------------------------------------------------------------------
    1.61 +enum Intersect {
    1.62 +	Intersect_No,
    1.63 +	Intersect_LiesOnPlane,
    1.64 +	Intersect_Yes
    1.65 +};
    1.66 +
    1.67 +// ------------------------------------------------------------------------------------------------
    1.68 +Intersect IntersectSegmentPlane(const IfcVector3& p,const IfcVector3& n, const IfcVector3& e0, 
    1.69 +								const IfcVector3& e1, 
    1.70 +								IfcVector3& out) 
    1.71 +{
    1.72 +	const IfcVector3 pdelta = e0 - p, seg = e1-e0;
    1.73 +	const IfcFloat dotOne = n*seg, dotTwo = -(n*pdelta);
    1.74 +
    1.75 +	if (fabs(dotOne) < 1e-6) {
    1.76 +		return fabs(dotTwo) < 1e-6f ? Intersect_LiesOnPlane : Intersect_No;
    1.77 +	}
    1.78 +
    1.79 +	const IfcFloat t = dotTwo/dotOne;
    1.80 +	// t must be in [0..1] if the intersection point is within the given segment
    1.81 +	if (t > 1.f || t < 0.f) {
    1.82 +		return Intersect_No;
    1.83 +	}
    1.84 +	out = e0+t*seg;
    1.85 +	return Intersect_Yes;
    1.86 +}
    1.87 +
    1.88 +// ------------------------------------------------------------------------------------------------
    1.89 +void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& result, 
    1.90 +									   const TempMesh& first_operand, 
    1.91 +									   ConversionData& conv)
    1.92 +{
    1.93 +	ai_assert(hs != NULL);
    1.94 +
    1.95 +	const IfcPlane* const plane = hs->BaseSurface->ToPtr<IfcPlane>();
    1.96 +	if(!plane) {
    1.97 +		IFCImporter::LogError("expected IfcPlane as base surface for the IfcHalfSpaceSolid");
    1.98 +		return;
    1.99 +	}
   1.100 +
   1.101 +	// extract plane base position vector and normal vector
   1.102 +	IfcVector3 p,n(0.f,0.f,1.f);
   1.103 +	if (plane->Position->Axis) {
   1.104 +		ConvertDirection(n,plane->Position->Axis.Get());
   1.105 +	}
   1.106 +	ConvertCartesianPoint(p,plane->Position->Location);
   1.107 +
   1.108 +	if(!IsTrue(hs->AgreementFlag)) {
   1.109 +		n *= -1.f;
   1.110 +	}
   1.111 +
   1.112 +	// clip the current contents of `meshout` against the plane we obtained from the second operand
   1.113 +	const std::vector<IfcVector3>& in = first_operand.verts;
   1.114 +	std::vector<IfcVector3>& outvert = result.verts;
   1.115 +
   1.116 +	std::vector<unsigned int>::const_iterator begin = first_operand.vertcnt.begin(), 
   1.117 +		end = first_operand.vertcnt.end(), iit;
   1.118 +
   1.119 +	outvert.reserve(in.size());
   1.120 +	result.vertcnt.reserve(first_operand.vertcnt.size());
   1.121 +
   1.122 +	unsigned int vidx = 0;
   1.123 +	for(iit = begin; iit != end; vidx += *iit++) {
   1.124 +
   1.125 +		unsigned int newcount = 0;
   1.126 +		for(unsigned int i = 0; i < *iit; ++i) {
   1.127 +			const IfcVector3& e0 = in[vidx+i], e1 = in[vidx+(i+1)%*iit];
   1.128 +
   1.129 +			// does the next segment intersect the plane?
   1.130 +			IfcVector3 isectpos;
   1.131 +			const Intersect isect = IntersectSegmentPlane(p,n,e0,e1,isectpos);
   1.132 +			if (isect == Intersect_No || isect == Intersect_LiesOnPlane) {
   1.133 +				if ( (e0-p).Normalize()*n > 0 ) {
   1.134 +					outvert.push_back(e0);
   1.135 +					++newcount;
   1.136 +				}
   1.137 +			}
   1.138 +			else if (isect == Intersect_Yes) {
   1.139 +				if ( (e0-p).Normalize()*n > 0 ) {
   1.140 +					// e0 is on the right side, so keep it 
   1.141 +					outvert.push_back(e0);
   1.142 +					outvert.push_back(isectpos);
   1.143 +					newcount += 2;
   1.144 +				}
   1.145 +				else {
   1.146 +					// e0 is on the wrong side, so drop it and keep e1 instead
   1.147 +					outvert.push_back(isectpos);
   1.148 +					++newcount;
   1.149 +				}
   1.150 +			}
   1.151 +		}	
   1.152 +
   1.153 +		if (!newcount) {
   1.154 +			continue;
   1.155 +		}
   1.156 +
   1.157 +		IfcVector3 vmin,vmax;
   1.158 +		ArrayBounds(&*(outvert.end()-newcount),newcount,vmin,vmax);
   1.159 +
   1.160 +		// filter our IfcFloat points - those may happen if a point lies
   1.161 +		// directly on the intersection line. However, due to IfcFloat
   1.162 +		// precision a bitwise comparison is not feasible to detect
   1.163 +		// this case.
   1.164 +		const IfcFloat epsilon = (vmax-vmin).SquareLength() / 1e6f;
   1.165 +		FuzzyVectorCompare fz(epsilon);
   1.166 +
   1.167 +		std::vector<IfcVector3>::iterator e = std::unique( outvert.end()-newcount, outvert.end(), fz );
   1.168 +
   1.169 +		if (e != outvert.end()) {
   1.170 +			newcount -= static_cast<unsigned int>(std::distance(e,outvert.end()));
   1.171 +			outvert.erase(e,outvert.end());
   1.172 +		}
   1.173 +		if (fz(*( outvert.end()-newcount),outvert.back())) {
   1.174 +			outvert.pop_back();
   1.175 +			--newcount;
   1.176 +		}
   1.177 +		if(newcount > 2) {
   1.178 +			result.vertcnt.push_back(newcount);
   1.179 +		}
   1.180 +		else while(newcount-->0) {
   1.181 +			result.verts.pop_back();
   1.182 +		}
   1.183 +
   1.184 +	}
   1.185 +	IFCImporter::LogDebug("generating CSG geometry by plane clipping (IfcBooleanClippingResult)");
   1.186 +}
   1.187 +
   1.188 +// ------------------------------------------------------------------------------------------------
   1.189 +// Check if e0-e1 intersects a sub-segment of the given boundary line.
   1.190 +// note: this functions works on 3D vectors, but performs its intersection checks solely in xy.
   1.191 +bool IntersectsBoundaryProfile( const IfcVector3& e0, const IfcVector3& e1, const std::vector<IfcVector3>& boundary,
   1.192 +	std::vector<size_t>& intersected_boundary_segments,
   1.193 +	std::vector<IfcVector3>& intersected_boundary_points,
   1.194 +	bool half_open = false,
   1.195 +	bool* e0_hits_border = NULL)
   1.196 +{
   1.197 +	ai_assert(intersected_boundary_segments.empty());
   1.198 +	ai_assert(intersected_boundary_points.empty());
   1.199 +
   1.200 +	if(e0_hits_border) {
   1.201 +		*e0_hits_border = false;
   1.202 +	}
   1.203 +
   1.204 +	const IfcVector3& e = e1 - e0;
   1.205 +
   1.206 +	for (size_t i = 0, bcount = boundary.size(); i < bcount; ++i) {
   1.207 +		// boundary segment i: b0-b1
   1.208 +		const IfcVector3& b0 = boundary[i];
   1.209 +		const IfcVector3& b1 = boundary[(i+1) % bcount];
   1.210 +
   1.211 +		const IfcVector3& b = b1 - b0;
   1.212 +
   1.213 +		// segment-segment intersection
   1.214 +		// solve b0 + b*s = e0 + e*t for (s,t)
   1.215 +		const IfcFloat det = (-b.x * e.y + e.x * b.y);
   1.216 +		if(fabs(det) < 1e-6) {
   1.217 +			// no solutions (parallel lines)
   1.218 +			continue;
   1.219 +		}
   1.220 +
   1.221 +		const IfcFloat x = b0.x - e0.x;
   1.222 +		const IfcFloat y = b0.y - e0.y;
   1.223 +
   1.224 +		const IfcFloat s = (x*e.y - e.x*y)/det;
   1.225 +		const IfcFloat t = (x*b.y - b.x*y)/det;
   1.226 +
   1.227 +#ifdef _DEBUG
   1.228 +		const IfcVector3 check = b0 + b*s  - (e0 + e*t);
   1.229 +		ai_assert((IfcVector2(check.x,check.y)).SquareLength() < 1e-5);
   1.230 +#endif
   1.231 +
   1.232 +		// for a valid intersection, s-t should be in range [0,1].
   1.233 +		// note that for t (i.e. the segment point) we only use a
   1.234 +		// half-sided epsilon because the next segment should catch
   1.235 +		// this case.
   1.236 +		const IfcFloat epsilon = 1e-6;
   1.237 +		if (t >= -epsilon && (t <= 1.0+epsilon || half_open) && s >= -epsilon && s <= 1.0) {
   1.238 +
   1.239 +			if (e0_hits_border && !*e0_hits_border) {
   1.240 +				*e0_hits_border = fabs(t) < 1e-5f;
   1.241 +			}
   1.242 +	
   1.243 +			const IfcVector3& p = e0 + e*t;
   1.244 +
   1.245 +			// only insert the point into the list if it is sufficiently
   1.246 +			// far away from the previous intersection point. This way,
   1.247 +			// we avoid duplicate detection if the intersection is
   1.248 +			// directly on the vertex between two segments.
   1.249 +			if (!intersected_boundary_points.empty() && intersected_boundary_segments.back()==i-1 ) {
   1.250 +				const IfcVector3 diff = intersected_boundary_points.back() - p;
   1.251 +				if(IfcVector2(diff.x, diff.y).SquareLength() < 1e-7) {
   1.252 +					continue;
   1.253 +				}
   1.254 +			}
   1.255 +			intersected_boundary_segments.push_back(i);
   1.256 +			intersected_boundary_points.push_back(p);
   1.257 +		}
   1.258 +	}
   1.259 +
   1.260 +	return !intersected_boundary_segments.empty();
   1.261 +}
   1.262 +
   1.263 +
   1.264 +// ------------------------------------------------------------------------------------------------
   1.265 +// note: this functions works on 3D vectors, but performs its intersection checks solely in xy.
   1.266 +bool PointInPoly(const IfcVector3& p, const std::vector<IfcVector3>& boundary)
   1.267 +{
   1.268 +	// even-odd algorithm: take a random vector that extends from p to infinite
   1.269 +	// and counts how many times it intersects edges of the boundary.
   1.270 +	// because checking for segment intersections is prone to numeric inaccuracies
   1.271 +	// or double detections (i.e. when hitting multiple adjacent segments at their
   1.272 +	// shared vertices) we do it thrice with different rays and vote on it.
   1.273 +
   1.274 +	// the even-odd algorithm doesn't work for points which lie directly on
   1.275 +	// the border of the polygon. If any of our attempts produces this result,
   1.276 +	// we return false immediately.
   1.277 +
   1.278 +	std::vector<size_t> intersected_boundary_segments;
   1.279 +	std::vector<IfcVector3> intersected_boundary_points;
   1.280 +	size_t votes = 0;
   1.281 +
   1.282 +	bool is_border;
   1.283 +	IntersectsBoundaryProfile(p, p + IfcVector3(1.0,0,0), boundary, 
   1.284 +		intersected_boundary_segments, 
   1.285 +		intersected_boundary_points, true, &is_border);
   1.286 +
   1.287 +	if(is_border) {
   1.288 +		return false;
   1.289 +	}
   1.290 +
   1.291 +	votes += intersected_boundary_segments.size() % 2;
   1.292 +
   1.293 +	intersected_boundary_segments.clear();
   1.294 +	intersected_boundary_points.clear();
   1.295 +
   1.296 +	IntersectsBoundaryProfile(p, p + IfcVector3(0,1.0,0), boundary, 
   1.297 +		intersected_boundary_segments, 
   1.298 +		intersected_boundary_points, true, &is_border);
   1.299 +
   1.300 +	if(is_border) {
   1.301 +		return false;
   1.302 +	}
   1.303 +
   1.304 +	votes += intersected_boundary_segments.size() % 2;
   1.305 +
   1.306 +	intersected_boundary_segments.clear();
   1.307 +	intersected_boundary_points.clear();
   1.308 +
   1.309 +	IntersectsBoundaryProfile(p, p + IfcVector3(0.6,-0.6,0.0), boundary, 
   1.310 +		intersected_boundary_segments, 
   1.311 +		intersected_boundary_points, true, &is_border);
   1.312 +
   1.313 +	if(is_border) {
   1.314 +		return false;
   1.315 +	}
   1.316 +
   1.317 +	votes += intersected_boundary_segments.size() % 2;
   1.318 +	//ai_assert(votes == 3 || votes == 0);
   1.319 +	return votes > 1;
   1.320 +}
   1.321 +
   1.322 +
   1.323 +// ------------------------------------------------------------------------------------------------
   1.324 +void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const IfcPolygonalBoundedHalfSpace* hs, TempMesh& result, 
   1.325 +													   const TempMesh& first_operand, 
   1.326 +													   ConversionData& conv)
   1.327 +{
   1.328 +	ai_assert(hs != NULL);
   1.329 +
   1.330 +	const IfcPlane* const plane = hs->BaseSurface->ToPtr<IfcPlane>();
   1.331 +	if(!plane) {
   1.332 +		IFCImporter::LogError("expected IfcPlane as base surface for the IfcHalfSpaceSolid");
   1.333 +		return;
   1.334 +	}
   1.335 +
   1.336 +	// extract plane base position vector and normal vector
   1.337 +	IfcVector3 p,n(0.f,0.f,1.f);
   1.338 +	if (plane->Position->Axis) {
   1.339 +		ConvertDirection(n,plane->Position->Axis.Get());
   1.340 +	}
   1.341 +	ConvertCartesianPoint(p,plane->Position->Location);
   1.342 +
   1.343 +	if(!IsTrue(hs->AgreementFlag)) {
   1.344 +		n *= -1.f;
   1.345 +	}
   1.346 +
   1.347 +	n.Normalize();
   1.348 +
   1.349 +	// obtain the polygonal bounding volume
   1.350 +	boost::shared_ptr<TempMesh> profile = boost::shared_ptr<TempMesh>(new TempMesh());
   1.351 +	if(!ProcessCurve(hs->PolygonalBoundary, *profile.get(), conv)) {
   1.352 +		IFCImporter::LogError("expected valid polyline for boundary of boolean halfspace");
   1.353 +		return;
   1.354 +	}
   1.355 +
   1.356 +	IfcMatrix4 proj_inv;
   1.357 +	ConvertAxisPlacement(proj_inv,hs->Position);
   1.358 +
   1.359 +	// and map everything into a plane coordinate space so all intersection
   1.360 +	// tests can be done in 2D space.
   1.361 +	IfcMatrix4 proj = proj_inv;
   1.362 +	proj.Inverse();
   1.363 +
   1.364 +	// clip the current contents of `meshout` against the plane we obtained from the second operand
   1.365 +	const std::vector<IfcVector3>& in = first_operand.verts;
   1.366 +	std::vector<IfcVector3>& outvert = result.verts;
   1.367 +
   1.368 +	std::vector<unsigned int>::const_iterator begin = first_operand.vertcnt.begin(), 
   1.369 +		end = first_operand.vertcnt.end(), iit;
   1.370 +
   1.371 +	outvert.reserve(in.size());
   1.372 +	result.vertcnt.reserve(first_operand.vertcnt.size());
   1.373 +
   1.374 +	std::vector<size_t> intersected_boundary_segments;
   1.375 +	std::vector<IfcVector3> intersected_boundary_points;
   1.376 +
   1.377 +	// TODO: the following algorithm doesn't handle all cases. 
   1.378 +	unsigned int vidx = 0;
   1.379 +	for(iit = begin; iit != end; vidx += *iit++) {
   1.380 +		if (!*iit) {
   1.381 +			continue;
   1.382 +		}
   1.383 +
   1.384 +		unsigned int newcount = 0;
   1.385 +		bool was_outside_boundary = !PointInPoly(proj * in[vidx], profile->verts);
   1.386 +
   1.387 +		// used any more?
   1.388 +		//size_t last_intersected_boundary_segment;
   1.389 +		IfcVector3 last_intersected_boundary_point;
   1.390 +
   1.391 +		bool extra_point_flag = false;
   1.392 +		IfcVector3 extra_point;
   1.393 +
   1.394 +		IfcVector3 enter_volume;
   1.395 +		bool entered_volume_flag = false;
   1.396 +
   1.397 +		for(unsigned int i = 0; i < *iit; ++i) {
   1.398 +			// current segment: [i,i+1 mod size] or [*extra_point,i] if extra_point_flag is set
   1.399 +			const IfcVector3& e0 = extra_point_flag ? extra_point : in[vidx+i];
   1.400 +			const IfcVector3& e1 = extra_point_flag ? in[vidx+i] : in[vidx+(i+1)%*iit];
   1.401 +
   1.402 +			// does the current segment intersect the polygonal boundary?
   1.403 +			const IfcVector3& e0_plane = proj * e0;
   1.404 +			const IfcVector3& e1_plane = proj * e1;
   1.405 +
   1.406 +			intersected_boundary_segments.clear();
   1.407 +			intersected_boundary_points.clear();
   1.408 +
   1.409 +			const bool is_outside_boundary = !PointInPoly(e1_plane, profile->verts);
   1.410 +			const bool is_boundary_intersection = is_outside_boundary != was_outside_boundary;
   1.411 +			
   1.412 +			IntersectsBoundaryProfile(e0_plane, e1_plane, profile->verts, 
   1.413 +				intersected_boundary_segments, 
   1.414 +				intersected_boundary_points);
   1.415 +		
   1.416 +			ai_assert(!is_boundary_intersection || !intersected_boundary_segments.empty());
   1.417 +
   1.418 +			// does the current segment intersect the plane?
   1.419 +			// (no extra check if this is an extra point)
   1.420 +			IfcVector3 isectpos;
   1.421 +			const Intersect isect =  extra_point_flag ? Intersect_No : IntersectSegmentPlane(p,n,e0,e1,isectpos);
   1.422 +
   1.423 +#ifdef _DEBUG
   1.424 +			if (isect == Intersect_Yes) {
   1.425 +				const IfcFloat f = fabs((isectpos - p)*n);
   1.426 +				ai_assert(f < 1e-5);
   1.427 +			}
   1.428 +#endif
   1.429 +
   1.430 +			const bool is_white_side = (e0-p)*n >= -1e-6;
   1.431 +
   1.432 +			// e0 on good side of plane? (i.e. we should keep all geometry on this side)
   1.433 +			if (is_white_side) {
   1.434 +				// but is there an intersection in e0-e1 and is e1 in the clipping
   1.435 +				// boundary? In this case, generate a line that only goes to the
   1.436 +				// intersection point.
   1.437 +				if (isect == Intersect_Yes  && !is_outside_boundary) {
   1.438 +					outvert.push_back(e0);
   1.439 +					++newcount;
   1.440 +
   1.441 +					outvert.push_back(isectpos);
   1.442 +					++newcount;
   1.443 +					
   1.444 +					/*
   1.445 +					// this is, however, only a line that goes to the plane, but not
   1.446 +					// necessarily to the point where the bounding volume on the
   1.447 +					// black side of the plane is hit. So basically, we need another 
   1.448 +					// check for [isectpos-e1], which should yield an intersection
   1.449 +					// point.
   1.450 +					extra_point_flag = true;
   1.451 +					extra_point = isectpos;
   1.452 +
   1.453 +					was_outside_boundary = true; 
   1.454 +					continue; */
   1.455 +
   1.456 +					// [isectpos, enter_volume] potentially needs extra points.
   1.457 +					// For this, we determine the intersection point with the
   1.458 +					// bounding volume and project it onto the plane. 
   1.459 +					/*
   1.460 +					const IfcVector3& enter_volume_proj = proj * enter_volume;
   1.461 +					const IfcVector3& enter_isectpos = proj * isectpos;
   1.462 +
   1.463 +					intersected_boundary_segments.clear();
   1.464 +					intersected_boundary_points.clear();
   1.465 +
   1.466 +					IntersectsBoundaryProfile(enter_volume_proj, enter_isectpos, profile->verts, 
   1.467 +						intersected_boundary_segments, 
   1.468 +						intersected_boundary_points);
   1.469 +
   1.470 +					if(!intersected_boundary_segments.empty()) {
   1.471 +
   1.472 +						vec = vec + ((p - vec) * n) * n;
   1.473 +					}
   1.474 +					*/				
   1.475 +
   1.476 +					//entered_volume_flag = true;
   1.477 +				}
   1.478 +				else {
   1.479 +					outvert.push_back(e0);
   1.480 +					++newcount;
   1.481 +				}
   1.482 +			}
   1.483 +			// e0 on bad side of plane, e1 on good (i.e. we should remove geometry on this side,
   1.484 +			// but only if it is within the bounding volume).
   1.485 +			else if (isect == Intersect_Yes) {
   1.486 +				// is e0 within the clipping volume? Insert the intersection point
   1.487 +				// of [e0,e1] and the plane instead of e0.
   1.488 +				if(was_outside_boundary) {
   1.489 +					outvert.push_back(e0);
   1.490 +				}
   1.491 +				else {
   1.492 +					if(entered_volume_flag) {
   1.493 +						const IfcVector3& fix_point = enter_volume + ((p - enter_volume) * n) * n;
   1.494 +						outvert.push_back(fix_point);
   1.495 +						++newcount;
   1.496 +					}
   1.497 +
   1.498 +					outvert.push_back(isectpos);	
   1.499 +				}
   1.500 +				entered_volume_flag = false;
   1.501 +				++newcount;
   1.502 +			}
   1.503 +			else { // no intersection with plane or parallel; e0,e1 are on the bad side
   1.504 +			
   1.505 +				// did we just pass the boundary line to the poly bounding?
   1.506 +				if (is_boundary_intersection) {
   1.507 +
   1.508 +					// and are now outside the clipping boundary?
   1.509 +					if (is_outside_boundary) {
   1.510 +						// in this case, get the point where the clipping boundary
   1.511 +						// was entered first. Then, get the point where the clipping
   1.512 +						// boundary volume was left! These two points with the plane
   1.513 +						// normal form another plane that intersects the clipping
   1.514 +						// volume. There are two ways to get from the first to the
   1.515 +						// second point along the intersection curve, try to pick the
   1.516 +						// one that lies within the current polygon.
   1.517 +
   1.518 +						// TODO this approach doesn't handle all cases
   1.519 +
   1.520 +						// ...
   1.521 +
   1.522 +						IfcFloat d = 1e20;
   1.523 +						IfcVector3 vclosest;
   1.524 +						BOOST_FOREACH(const IfcVector3& v, intersected_boundary_points) {
   1.525 +							const IfcFloat dn = (v-e1_plane).SquareLength();
   1.526 +							if (dn < d) {
   1.527 +								d = dn;
   1.528 +								vclosest = v;
   1.529 +							}
   1.530 +						}
   1.531 +
   1.532 +						vclosest = proj_inv * vclosest;
   1.533 +						if(entered_volume_flag) {
   1.534 +							const IfcVector3& fix_point = vclosest + ((p - vclosest) * n) * n;
   1.535 +							outvert.push_back(fix_point);
   1.536 +							++newcount;
   1.537 +
   1.538 +							entered_volume_flag = false;
   1.539 +						}
   1.540 +
   1.541 +						outvert.push_back(vclosest);
   1.542 +						++newcount;
   1.543 +
   1.544 +						//outvert.push_back(e1);
   1.545 +						//++newcount;
   1.546 +					}
   1.547 +					else {
   1.548 +						entered_volume_flag = true;
   1.549 +
   1.550 +						// we just entered the clipping boundary. Record the point
   1.551 +						// and the segment where we entered and also generate this point.
   1.552 +						//last_intersected_boundary_segment = intersected_boundary_segments.front();
   1.553 +						//last_intersected_boundary_point = intersected_boundary_points.front();
   1.554 +
   1.555 +						outvert.push_back(e0);
   1.556 +						++newcount;
   1.557 +
   1.558 +						IfcFloat d = 1e20;
   1.559 +						IfcVector3 vclosest;
   1.560 +						BOOST_FOREACH(const IfcVector3& v, intersected_boundary_points) {
   1.561 +							const IfcFloat dn = (v-e0_plane).SquareLength();
   1.562 +							if (dn < d) {
   1.563 +								d = dn;
   1.564 +								vclosest = v;
   1.565 +							}
   1.566 +						}
   1.567 +
   1.568 +						enter_volume = proj_inv * vclosest;
   1.569 +						outvert.push_back(enter_volume);
   1.570 +						++newcount;
   1.571 +					}
   1.572 +				}				
   1.573 +				// if not, we just keep the vertex
   1.574 +				else if (is_outside_boundary) {
   1.575 +					outvert.push_back(e0);
   1.576 +					++newcount;
   1.577 +
   1.578 +					entered_volume_flag = false;
   1.579 +				}
   1.580 +			}
   1.581 +
   1.582 +			was_outside_boundary = is_outside_boundary;
   1.583 +			extra_point_flag = false;
   1.584 +		}
   1.585 +	
   1.586 +		if (!newcount) {
   1.587 +			continue;
   1.588 +		}
   1.589 +		
   1.590 +		IfcVector3 vmin,vmax;
   1.591 +		ArrayBounds(&*(outvert.end()-newcount),newcount,vmin,vmax);
   1.592 +
   1.593 +		// filter our IfcFloat points - those may happen if a point lies
   1.594 +		// directly on the intersection line. However, due to IfcFloat
   1.595 +		// precision a bitwise comparison is not feasible to detect
   1.596 +		// this case.
   1.597 +		const IfcFloat epsilon = (vmax-vmin).SquareLength() / 1e6f;
   1.598 +		FuzzyVectorCompare fz(epsilon);
   1.599 +
   1.600 +		std::vector<IfcVector3>::iterator e = std::unique( outvert.end()-newcount, outvert.end(), fz );
   1.601 +
   1.602 +		if (e != outvert.end()) {
   1.603 +			newcount -= static_cast<unsigned int>(std::distance(e,outvert.end()));
   1.604 +			outvert.erase(e,outvert.end());
   1.605 +		}
   1.606 +		if (fz(*( outvert.end()-newcount),outvert.back())) {
   1.607 +			outvert.pop_back();
   1.608 +			--newcount;
   1.609 +		} 
   1.610 +		if(newcount > 2) {
   1.611 +			result.vertcnt.push_back(newcount);
   1.612 +		}
   1.613 +		else while(newcount-->0) {
   1.614 +			result.verts.pop_back();
   1.615 +		}
   1.616 +
   1.617 +	}
   1.618 +	IFCImporter::LogDebug("generating CSG geometry by plane clipping with polygonal bounding (IfcBooleanClippingResult)");	
   1.619 +}
   1.620 +
   1.621 +// ------------------------------------------------------------------------------------------------
   1.622 +void ProcessBooleanExtrudedAreaSolidDifference(const IfcExtrudedAreaSolid* as, TempMesh& result, 
   1.623 +											   const TempMesh& first_operand, 
   1.624 +											   ConversionData& conv)
   1.625 +{
   1.626 +	ai_assert(as != NULL);
   1.627 +
   1.628 +	// This case is handled by reduction to an instance of the quadrify() algorithm.
   1.629 +	// Obviously, this won't work for arbitrarily complex cases. In fact, the first
   1.630 +	// operand should be near-planar. Luckily, this is usually the case in Ifc 
   1.631 +	// buildings.
   1.632 +
   1.633 +	boost::shared_ptr<TempMesh> meshtmp = boost::shared_ptr<TempMesh>(new TempMesh());
   1.634 +	ProcessExtrudedAreaSolid(*as,*meshtmp,conv,false);
   1.635 +
   1.636 +	std::vector<TempOpening> openings(1, TempOpening(as,IfcVector3(0,0,0),meshtmp,boost::shared_ptr<TempMesh>()));
   1.637 +
   1.638 +	result = first_operand;
   1.639 +
   1.640 +	TempMesh temp;
   1.641 +
   1.642 +	std::vector<IfcVector3>::const_iterator vit = first_operand.verts.begin();
   1.643 +	BOOST_FOREACH(unsigned int pcount, first_operand.vertcnt) {
   1.644 +		temp.Clear();
   1.645 +
   1.646 +		temp.verts.insert(temp.verts.end(), vit, vit + pcount);
   1.647 +		temp.vertcnt.push_back(pcount);
   1.648 +
   1.649 +		// The algorithms used to generate mesh geometry sometimes
   1.650 +		// spit out lines or other degenerates which must be
   1.651 +		// filtered to avoid running into assertions later on.
   1.652 +
   1.653 +		// ComputePolygonNormal returns the Newell normal, so the
   1.654 +		// length of the normal is the area of the polygon.
   1.655 +		const IfcVector3& normal = temp.ComputeLastPolygonNormal(false);
   1.656 +		if (normal.SquareLength() < static_cast<IfcFloat>(1e-5)) {
   1.657 +			IFCImporter::LogWarn("skipping degenerate polygon (ProcessBooleanExtrudedAreaSolidDifference)");
   1.658 +			continue;
   1.659 +		}
   1.660 +
   1.661 +		GenerateOpenings(openings, std::vector<IfcVector3>(1,IfcVector3(1,0,0)), temp, false, true);
   1.662 +		result.Append(temp);
   1.663 +
   1.664 +		vit += pcount;
   1.665 +	}
   1.666 +
   1.667 +	IFCImporter::LogDebug("generating CSG geometry by geometric difference to a solid (IfcExtrudedAreaSolid)");
   1.668 +}
   1.669 +
   1.670 +// ------------------------------------------------------------------------------------------------
   1.671 +void ProcessBoolean(const IfcBooleanResult& boolean, TempMesh& result, ConversionData& conv)
   1.672 +{
   1.673 +	// supported CSG operations:
   1.674 +	//   DIFFERENCE
   1.675 +	if(const IfcBooleanResult* const clip = boolean.ToPtr<IfcBooleanResult>()) {
   1.676 +		if(clip->Operator != "DIFFERENCE") {
   1.677 +			IFCImporter::LogWarn("encountered unsupported boolean operator: " + (std::string)clip->Operator);
   1.678 +			return;
   1.679 +		}
   1.680 +
   1.681 +		// supported cases (1st operand):
   1.682 +		//  IfcBooleanResult -- call ProcessBoolean recursively
   1.683 +		//  IfcSweptAreaSolid -- obtain polygonal geometry first
   1.684 +
   1.685 +		// supported cases (2nd operand):
   1.686 +		//  IfcHalfSpaceSolid -- easy, clip against plane
   1.687 +		//  IfcExtrudedAreaSolid -- reduce to an instance of the quadrify() algorithm
   1.688 +
   1.689 +
   1.690 +		const IfcHalfSpaceSolid* const hs = clip->SecondOperand->ResolveSelectPtr<IfcHalfSpaceSolid>(conv.db);
   1.691 +		const IfcExtrudedAreaSolid* const as = clip->SecondOperand->ResolveSelectPtr<IfcExtrudedAreaSolid>(conv.db);
   1.692 +		if(!hs && !as) {
   1.693 +			IFCImporter::LogError("expected IfcHalfSpaceSolid or IfcExtrudedAreaSolid as second clipping operand");
   1.694 +			return;
   1.695 +		}
   1.696 +
   1.697 +		TempMesh first_operand;
   1.698 +		if(const IfcBooleanResult* const op0 = clip->FirstOperand->ResolveSelectPtr<IfcBooleanResult>(conv.db)) {
   1.699 +			ProcessBoolean(*op0,first_operand,conv);
   1.700 +		}
   1.701 +		else if (const IfcSweptAreaSolid* const swept = clip->FirstOperand->ResolveSelectPtr<IfcSweptAreaSolid>(conv.db)) {
   1.702 +			ProcessSweptAreaSolid(*swept,first_operand,conv);
   1.703 +		}
   1.704 +		else {
   1.705 +			IFCImporter::LogError("expected IfcSweptAreaSolid or IfcBooleanResult as first clipping operand");
   1.706 +			return;
   1.707 +		}
   1.708 +
   1.709 +		if(hs) {
   1.710 +
   1.711 +			const IfcPolygonalBoundedHalfSpace* const hs_bounded = clip->SecondOperand->ResolveSelectPtr<IfcPolygonalBoundedHalfSpace>(conv.db);
   1.712 +			if (hs_bounded) {
   1.713 +				ProcessPolygonalBoundedBooleanHalfSpaceDifference(hs_bounded, result, first_operand, conv);
   1.714 +			}
   1.715 +			else {
   1.716 +				ProcessBooleanHalfSpaceDifference(hs, result, first_operand, conv);
   1.717 +			}
   1.718 +		}
   1.719 +		else {
   1.720 +			ProcessBooleanExtrudedAreaSolidDifference(as, result, first_operand, conv);
   1.721 +		}
   1.722 +	}
   1.723 +	else {
   1.724 +		IFCImporter::LogWarn("skipping unknown IfcBooleanResult entity, type is " + boolean.GetClassName());
   1.725 +	}
   1.726 +}
   1.727 +
   1.728 +} // ! IFC
   1.729 +} // ! Assimp
   1.730 +
   1.731 +#endif 
   1.732 +