vrshoot

diff libs/assimp/IFCOpenings.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/IFCOpenings.cpp	Sat Feb 01 19:58:19 2014 +0200
     1.3 @@ -0,0 +1,1744 @@
     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  IFCOpenings.cpp
    1.45 + *  @brief Implements a subset of Ifc CSG operations for pouring 
    1.46 +  *    holes for windows and doors into walls.
    1.47 + */
    1.48 +
    1.49 +#include "AssimpPCH.h"
    1.50 +
    1.51 +#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
    1.52 +#include "IFCUtil.h"
    1.53 +#include "PolyTools.h"
    1.54 +#include "ProcessHelper.h"
    1.55 +
    1.56 +#include "../contrib/poly2tri/poly2tri/poly2tri.h"
    1.57 +#include "../contrib/clipper/clipper.hpp"
    1.58 +
    1.59 +#include <iterator>
    1.60 +
    1.61 +namespace Assimp {
    1.62 +	namespace IFC {
    1.63 +
    1.64 +		using ClipperLib::ulong64;
    1.65 +		// XXX use full -+ range ...
    1.66 +		const ClipperLib::long64 max_ulong64 = 1518500249; // clipper.cpp / hiRange var
    1.67 +
    1.68 +		//#define to_int64(p)  (static_cast<ulong64>( std::max( 0., std::min( static_cast<IfcFloat>((p)), 1.) ) * max_ulong64 ))
    1.69 +#define to_int64(p)  (static_cast<ulong64>(static_cast<IfcFloat>((p) ) * max_ulong64 ))
    1.70 +#define from_int64(p) (static_cast<IfcFloat>((p)) / max_ulong64)
    1.71 +#define one_vec (IfcVector2(static_cast<IfcFloat>(1.0),static_cast<IfcFloat>(1.0)))
    1.72 +
    1.73 +
    1.74 +		// fallback method to generate wall openings
    1.75 +		bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std::vector<IfcVector3>& nors, 
    1.76 +			TempMesh& curmesh);
    1.77 +
    1.78 +
    1.79 +typedef std::pair< IfcVector2, IfcVector2 > BoundingBox;
    1.80 +typedef std::map<IfcVector2,size_t,XYSorter> XYSortedField;
    1.81 +
    1.82 +
    1.83 +// ------------------------------------------------------------------------------------------------
    1.84 +void QuadrifyPart(const IfcVector2& pmin, const IfcVector2& pmax, XYSortedField& field, 
    1.85 +	const std::vector< BoundingBox >& bbs, 
    1.86 +	std::vector<IfcVector2>& out)
    1.87 +{
    1.88 +	if (!(pmin.x-pmax.x) || !(pmin.y-pmax.y)) {
    1.89 +		return;
    1.90 +	}
    1.91 +
    1.92 +	IfcFloat xs = 1e10, xe = 1e10;	
    1.93 +	bool found = false;
    1.94 +
    1.95 +	// Search along the x-axis until we find an opening
    1.96 +	XYSortedField::iterator start = field.begin();
    1.97 +	for(; start != field.end(); ++start) {
    1.98 +		const BoundingBox& bb = bbs[(*start).second];
    1.99 +		if(bb.first.x >= pmax.x) {
   1.100 +			break;
   1.101 +		} 
   1.102 +
   1.103 +		if (bb.second.x > pmin.x && bb.second.y > pmin.y && bb.first.y < pmax.y) {
   1.104 +			xs = bb.first.x;
   1.105 +			xe = bb.second.x;
   1.106 +			found = true;
   1.107 +			break;
   1.108 +		}
   1.109 +	}
   1.110 +
   1.111 +	if (!found) {
   1.112 +		// the rectangle [pmin,pend] is opaque, fill it
   1.113 +		out.push_back(pmin);
   1.114 +		out.push_back(IfcVector2(pmin.x,pmax.y));
   1.115 +		out.push_back(pmax);
   1.116 +		out.push_back(IfcVector2(pmax.x,pmin.y));
   1.117 +		return;
   1.118 +	}
   1.119 +
   1.120 +	xs = std::max(pmin.x,xs);
   1.121 +	xe = std::min(pmax.x,xe);
   1.122 +
   1.123 +	// see if there's an offset to fill at the top of our quad
   1.124 +	if (xs - pmin.x) {
   1.125 +		out.push_back(pmin);
   1.126 +		out.push_back(IfcVector2(pmin.x,pmax.y));
   1.127 +		out.push_back(IfcVector2(xs,pmax.y));
   1.128 +		out.push_back(IfcVector2(xs,pmin.y));
   1.129 +	}
   1.130 +
   1.131 +	// search along the y-axis for all openings that overlap xs and our quad
   1.132 +	IfcFloat ylast = pmin.y;
   1.133 +	found = false;
   1.134 +	for(; start != field.end(); ++start) {
   1.135 +		const BoundingBox& bb = bbs[(*start).second];
   1.136 +		if (bb.first.x > xs || bb.first.y >= pmax.y) {
   1.137 +			break;
   1.138 +		}
   1.139 +
   1.140 +		if (bb.second.y > ylast) {
   1.141 +
   1.142 +			found = true;
   1.143 +			const IfcFloat ys = std::max(bb.first.y,pmin.y), ye = std::min(bb.second.y,pmax.y);
   1.144 +			if (ys - ylast > 0.0f) {
   1.145 +				QuadrifyPart( IfcVector2(xs,ylast), IfcVector2(xe,ys) ,field,bbs,out);
   1.146 +			}
   1.147 +
   1.148 +			// the following are the window vertices
   1.149 +
   1.150 +			/*wnd.push_back(IfcVector2(xs,ys));
   1.151 +			wnd.push_back(IfcVector2(xs,ye));
   1.152 +			wnd.push_back(IfcVector2(xe,ye));
   1.153 +			wnd.push_back(IfcVector2(xe,ys));*/
   1.154 +			ylast = ye;
   1.155 +		}
   1.156 +	}
   1.157 +	if (!found) {
   1.158 +		// the rectangle [pmin,pend] is opaque, fill it
   1.159 +		out.push_back(IfcVector2(xs,pmin.y));
   1.160 +		out.push_back(IfcVector2(xs,pmax.y));
   1.161 +		out.push_back(IfcVector2(xe,pmax.y));
   1.162 +		out.push_back(IfcVector2(xe,pmin.y));
   1.163 +		return;
   1.164 +	}
   1.165 +	if (ylast < pmax.y) {
   1.166 +		QuadrifyPart( IfcVector2(xs,ylast), IfcVector2(xe,pmax.y) ,field,bbs,out);
   1.167 +	}
   1.168 +
   1.169 +	// now for the whole rest
   1.170 +	if (pmax.x-xe) {
   1.171 +		QuadrifyPart(IfcVector2(xe,pmin.y), pmax ,field,bbs,out);
   1.172 +	}
   1.173 +}
   1.174 +
   1.175 +typedef std::vector<IfcVector2> Contour;
   1.176 +typedef std::vector<bool> SkipList; // should probably use int for performance reasons
   1.177 +
   1.178 +struct ProjectedWindowContour
   1.179 +{
   1.180 +	Contour contour;
   1.181 +	BoundingBox bb;
   1.182 +	SkipList skiplist;
   1.183 +	bool is_rectangular;
   1.184 +
   1.185 +
   1.186 +	ProjectedWindowContour(const Contour& contour, const BoundingBox& bb, bool is_rectangular)
   1.187 +		: contour(contour)
   1.188 +		, bb(bb)
   1.189 +		, is_rectangular(is_rectangular)
   1.190 +	{}
   1.191 +
   1.192 +
   1.193 +	bool IsInvalid() const {
   1.194 +		return contour.empty();
   1.195 +	}
   1.196 +
   1.197 +	void FlagInvalid() {
   1.198 +		contour.clear();
   1.199 +	}
   1.200 +
   1.201 +	void PrepareSkiplist() {
   1.202 +		skiplist.resize(contour.size(),false);
   1.203 +	}
   1.204 +};
   1.205 +
   1.206 +typedef std::vector< ProjectedWindowContour > ContourVector;
   1.207 +
   1.208 +// ------------------------------------------------------------------------------------------------
   1.209 +bool BoundingBoxesOverlapping( const BoundingBox &ibb, const BoundingBox &bb ) 
   1.210 +{
   1.211 +	// count the '=' case as non-overlapping but as adjacent to each other
   1.212 +	return ibb.first.x < bb.second.x && ibb.second.x > bb.first.x &&
   1.213 +		ibb.first.y < bb.second.y && ibb.second.y > bb.first.y;
   1.214 +}
   1.215 +
   1.216 +// ------------------------------------------------------------------------------------------------
   1.217 +bool IsDuplicateVertex(const IfcVector2& vv, const std::vector<IfcVector2>& temp_contour)
   1.218 +{
   1.219 +	// sanity check for duplicate vertices
   1.220 +	BOOST_FOREACH(const IfcVector2& cp, temp_contour) {
   1.221 +		if ((cp-vv).SquareLength() < 1e-5f) {
   1.222 +			return true;
   1.223 +		}
   1.224 +	}  
   1.225 +	return false;
   1.226 +}
   1.227 +
   1.228 +// ------------------------------------------------------------------------------------------------
   1.229 +void ExtractVerticesFromClipper(const ClipperLib::Polygon& poly, std::vector<IfcVector2>& temp_contour, 
   1.230 +	bool filter_duplicates = false)
   1.231 +{
   1.232 +	temp_contour.clear();
   1.233 +	BOOST_FOREACH(const ClipperLib::IntPoint& point, poly) {
   1.234 +		IfcVector2 vv = IfcVector2( from_int64(point.X), from_int64(point.Y));
   1.235 +		vv = std::max(vv,IfcVector2());
   1.236 +		vv = std::min(vv,one_vec);
   1.237 +
   1.238 +		if (!filter_duplicates || !IsDuplicateVertex(vv, temp_contour)) {
   1.239 +			temp_contour.push_back(vv);
   1.240 +		}
   1.241 +	}
   1.242 +}
   1.243 +
   1.244 +// ------------------------------------------------------------------------------------------------
   1.245 +BoundingBox GetBoundingBox(const ClipperLib::Polygon& poly)
   1.246 +{
   1.247 +	IfcVector2 newbb_min, newbb_max;
   1.248 +	MinMaxChooser<IfcVector2>()(newbb_min, newbb_max);
   1.249 +
   1.250 +	BOOST_FOREACH(const ClipperLib::IntPoint& point, poly) {
   1.251 +		IfcVector2 vv = IfcVector2( from_int64(point.X), from_int64(point.Y));
   1.252 +
   1.253 +		// sanity rounding
   1.254 +		vv = std::max(vv,IfcVector2());
   1.255 +		vv = std::min(vv,one_vec);
   1.256 +
   1.257 +		newbb_min = std::min(newbb_min,vv);
   1.258 +		newbb_max = std::max(newbb_max,vv);
   1.259 +	}
   1.260 +	return BoundingBox(newbb_min, newbb_max);
   1.261 +}
   1.262 +
   1.263 +// ------------------------------------------------------------------------------------------------
   1.264 +void InsertWindowContours(const ContourVector& contours,
   1.265 +	const std::vector<TempOpening>& openings,
   1.266 +	TempMesh& curmesh)
   1.267 +{
   1.268 +	// fix windows - we need to insert the real, polygonal shapes into the quadratic holes that we have now
   1.269 +	for(size_t i = 0; i < contours.size();++i) {
   1.270 +		const BoundingBox& bb = contours[i].bb;
   1.271 +		const std::vector<IfcVector2>& contour = contours[i].contour;
   1.272 +		if(contour.empty()) {
   1.273 +			continue;
   1.274 +		}
   1.275 +
   1.276 +		// check if we need to do it at all - many windows just fit perfectly into their quadratic holes,
   1.277 +		// i.e. their contours *are* already their bounding boxes.
   1.278 +		if (contour.size() == 4) {
   1.279 +			std::set<IfcVector2,XYSorter> verts;
   1.280 +			for(size_t n = 0; n < 4; ++n) {
   1.281 +				verts.insert(contour[n]);
   1.282 +			}
   1.283 +			const std::set<IfcVector2,XYSorter>::const_iterator end = verts.end();
   1.284 +			if (verts.find(bb.first)!=end && verts.find(bb.second)!=end
   1.285 +				&& verts.find(IfcVector2(bb.first.x,bb.second.y))!=end 
   1.286 +				&& verts.find(IfcVector2(bb.second.x,bb.first.y))!=end 
   1.287 +				) {
   1.288 +					continue;
   1.289 +			}
   1.290 +		}
   1.291 +
   1.292 +		const IfcFloat diag = (bb.first-bb.second).Length();
   1.293 +		const IfcFloat epsilon = diag/1000.f;
   1.294 +
   1.295 +		// walk through all contour points and find those that lie on the BB corner
   1.296 +		size_t last_hit = -1, very_first_hit = -1;
   1.297 +		IfcVector2 edge;
   1.298 +		for(size_t n = 0, e=0, size = contour.size();; n=(n+1)%size, ++e) {
   1.299 +
   1.300 +			// sanity checking
   1.301 +			if (e == size*2) {
   1.302 +				IFCImporter::LogError("encountered unexpected topology while generating window contour");
   1.303 +				break;
   1.304 +			}
   1.305 +
   1.306 +			const IfcVector2& v = contour[n];
   1.307 +
   1.308 +			bool hit = false;
   1.309 +			if (fabs(v.x-bb.first.x)<epsilon) {
   1.310 +				edge.x = bb.first.x;
   1.311 +				hit = true;
   1.312 +			}
   1.313 +			else if (fabs(v.x-bb.second.x)<epsilon) {
   1.314 +				edge.x = bb.second.x;
   1.315 +				hit = true;
   1.316 +			}
   1.317 +
   1.318 +			if (fabs(v.y-bb.first.y)<epsilon) {
   1.319 +				edge.y = bb.first.y;
   1.320 +				hit = true;
   1.321 +			}
   1.322 +			else if (fabs(v.y-bb.second.y)<epsilon) {
   1.323 +				edge.y = bb.second.y;
   1.324 +				hit = true;
   1.325 +			}
   1.326 +
   1.327 +			if (hit) {
   1.328 +				if (last_hit != (size_t)-1) {
   1.329 +
   1.330 +					const size_t old = curmesh.verts.size();
   1.331 +					size_t cnt = last_hit > n ? size-(last_hit-n) : n-last_hit;
   1.332 +					for(size_t a = last_hit, e = 0; e <= cnt; a=(a+1)%size, ++e) {
   1.333 +						// hack: this is to fix cases where opening contours are self-intersecting.
   1.334 +						// Clipper doesn't produce such polygons, but as soon as we're back in
   1.335 +						// our brave new floating-point world, very small distances are consumed
   1.336 +						// by the maximum available precision, leading to self-intersecting
   1.337 +						// polygons. This fix makes concave windows fail even worse, but
   1.338 +						// anyway, fail is fail.
   1.339 +						if ((contour[a] - edge).SquareLength() > diag*diag*0.7) {
   1.340 +							continue;
   1.341 +						}
   1.342 +						curmesh.verts.push_back(IfcVector3(contour[a].x, contour[a].y, 0.0f));
   1.343 +					}
   1.344 +
   1.345 +					if (edge != contour[last_hit]) {
   1.346 +
   1.347 +						IfcVector2 corner = edge;
   1.348 +
   1.349 +						if (fabs(contour[last_hit].x-bb.first.x)<epsilon) {
   1.350 +							corner.x = bb.first.x;
   1.351 +						}
   1.352 +						else if (fabs(contour[last_hit].x-bb.second.x)<epsilon) {
   1.353 +							corner.x = bb.second.x;
   1.354 +						}
   1.355 +
   1.356 +						if (fabs(contour[last_hit].y-bb.first.y)<epsilon) {
   1.357 +							corner.y = bb.first.y;
   1.358 +						}
   1.359 +						else if (fabs(contour[last_hit].y-bb.second.y)<epsilon) {
   1.360 +							corner.y = bb.second.y;
   1.361 +						}
   1.362 +
   1.363 +						curmesh.verts.push_back(IfcVector3(corner.x, corner.y, 0.0f));
   1.364 +					}
   1.365 +					else if (cnt == 1) {
   1.366 +						// avoid degenerate polygons (also known as lines or points)
   1.367 +						curmesh.verts.erase(curmesh.verts.begin()+old,curmesh.verts.end());
   1.368 +					}
   1.369 +
   1.370 +					if (const size_t d = curmesh.verts.size()-old) {
   1.371 +						curmesh.vertcnt.push_back(d);
   1.372 +						std::reverse(curmesh.verts.rbegin(),curmesh.verts.rbegin()+d);
   1.373 +					}
   1.374 +					if (n == very_first_hit) {
   1.375 +						break;
   1.376 +					}
   1.377 +				}
   1.378 +				else {
   1.379 +					very_first_hit = n;
   1.380 +				}
   1.381 +
   1.382 +				last_hit = n;
   1.383 +			}
   1.384 +		}
   1.385 +	}
   1.386 +}
   1.387 +
   1.388 +// ------------------------------------------------------------------------------------------------
   1.389 +void MergeWindowContours (const std::vector<IfcVector2>& a, 
   1.390 +	const std::vector<IfcVector2>& b, 
   1.391 +	ClipperLib::ExPolygons& out) 
   1.392 +{
   1.393 +	out.clear();
   1.394 +
   1.395 +	ClipperLib::Clipper clipper;
   1.396 +	ClipperLib::Polygon clip;
   1.397 +
   1.398 +	BOOST_FOREACH(const IfcVector2& pip, a) {
   1.399 +		clip.push_back(ClipperLib::IntPoint(  to_int64(pip.x), to_int64(pip.y) ));
   1.400 +	}
   1.401 +
   1.402 +	if (ClipperLib::Orientation(clip)) {
   1.403 +		std::reverse(clip.begin(), clip.end());
   1.404 +	}
   1.405 +
   1.406 +	clipper.AddPolygon(clip, ClipperLib::ptSubject);
   1.407 +	clip.clear();
   1.408 +
   1.409 +	BOOST_FOREACH(const IfcVector2& pip, b) {
   1.410 +		clip.push_back(ClipperLib::IntPoint(  to_int64(pip.x), to_int64(pip.y) ));
   1.411 +	}
   1.412 +
   1.413 +	if (ClipperLib::Orientation(clip)) {
   1.414 +		std::reverse(clip.begin(), clip.end());
   1.415 +	}
   1.416 +
   1.417 +	clipper.AddPolygon(clip, ClipperLib::ptSubject);
   1.418 +	clipper.Execute(ClipperLib::ctUnion, out,ClipperLib::pftNonZero,ClipperLib::pftNonZero);
   1.419 +}
   1.420 +
   1.421 +// ------------------------------------------------------------------------------------------------
   1.422 +// Subtract a from b
   1.423 +void MakeDisjunctWindowContours (const std::vector<IfcVector2>& a, 
   1.424 +	const std::vector<IfcVector2>& b, 
   1.425 +	ClipperLib::ExPolygons& out) 
   1.426 +{
   1.427 +	out.clear();
   1.428 +
   1.429 +	ClipperLib::Clipper clipper;
   1.430 +	ClipperLib::Polygon clip;
   1.431 +
   1.432 +	BOOST_FOREACH(const IfcVector2& pip, a) {
   1.433 +		clip.push_back(ClipperLib::IntPoint(  to_int64(pip.x), to_int64(pip.y) ));
   1.434 +	}
   1.435 +
   1.436 +	if (ClipperLib::Orientation(clip)) {
   1.437 +		std::reverse(clip.begin(), clip.end());
   1.438 +	}
   1.439 +
   1.440 +	clipper.AddPolygon(clip, ClipperLib::ptClip);
   1.441 +	clip.clear();
   1.442 +
   1.443 +	BOOST_FOREACH(const IfcVector2& pip, b) {
   1.444 +		clip.push_back(ClipperLib::IntPoint(  to_int64(pip.x), to_int64(pip.y) ));
   1.445 +	}
   1.446 +
   1.447 +	if (ClipperLib::Orientation(clip)) {
   1.448 +		std::reverse(clip.begin(), clip.end());
   1.449 +	}
   1.450 +
   1.451 +	clipper.AddPolygon(clip, ClipperLib::ptSubject);
   1.452 +	clipper.Execute(ClipperLib::ctDifference, out,ClipperLib::pftNonZero,ClipperLib::pftNonZero);
   1.453 +}
   1.454 +
   1.455 +// ------------------------------------------------------------------------------------------------
   1.456 +void CleanupWindowContour(ProjectedWindowContour& window)
   1.457 +{
   1.458 +	std::vector<IfcVector2> scratch;
   1.459 +	std::vector<IfcVector2>& contour = window.contour;
   1.460 +
   1.461 +	ClipperLib::Polygon subject;
   1.462 +	ClipperLib::Clipper clipper;
   1.463 +	ClipperLib::ExPolygons clipped;
   1.464 +
   1.465 +	BOOST_FOREACH(const IfcVector2& pip, contour) {
   1.466 +		subject.push_back(ClipperLib::IntPoint(  to_int64(pip.x), to_int64(pip.y) ));
   1.467 +	}
   1.468 +
   1.469 +	clipper.AddPolygon(subject,ClipperLib::ptSubject);
   1.470 +	clipper.Execute(ClipperLib::ctUnion,clipped,ClipperLib::pftNonZero,ClipperLib::pftNonZero);
   1.471 +
   1.472 +	// This should yield only one polygon or something went wrong 
   1.473 +	if (clipped.size() != 1) {
   1.474 +
   1.475 +		// Empty polygon? drop the contour altogether
   1.476 +		if(clipped.empty()) {
   1.477 +			IFCImporter::LogError("error during polygon clipping, window contour is degenerate");
   1.478 +			window.FlagInvalid();
   1.479 +			return;
   1.480 +		}
   1.481 +
   1.482 +		// Else: take the first only
   1.483 +		IFCImporter::LogError("error during polygon clipping, window contour is not convex");
   1.484 +	}
   1.485 +
   1.486 +	ExtractVerticesFromClipper(clipped[0].outer, scratch);
   1.487 +	// Assume the bounding box doesn't change during this operation
   1.488 +}
   1.489 +
   1.490 +// ------------------------------------------------------------------------------------------------
   1.491 +void CleanupWindowContours(ContourVector& contours)
   1.492 +{
   1.493 +	// Use PolyClipper to clean up window contours
   1.494 +	try {
   1.495 +		BOOST_FOREACH(ProjectedWindowContour& window, contours) {
   1.496 +			CleanupWindowContour(window);
   1.497 +		}
   1.498 +	}
   1.499 +	catch (const char* sx) {
   1.500 +		IFCImporter::LogError("error during polygon clipping, window shape may be wrong: (Clipper: " 
   1.501 +			+ std::string(sx) + ")");
   1.502 +	}
   1.503 +}
   1.504 +
   1.505 +// ------------------------------------------------------------------------------------------------
   1.506 +void CleanupOuterContour(const std::vector<IfcVector2>& contour_flat, TempMesh& curmesh)
   1.507 +{
   1.508 +	std::vector<IfcVector3> vold;
   1.509 +	std::vector<unsigned int> iold;
   1.510 +
   1.511 +	vold.reserve(curmesh.verts.size());
   1.512 +	iold.reserve(curmesh.vertcnt.size());
   1.513 +
   1.514 +	// Fix the outer contour using polyclipper
   1.515 +	try {
   1.516 +
   1.517 +		ClipperLib::Polygon subject;
   1.518 +		ClipperLib::Clipper clipper;
   1.519 +		ClipperLib::ExPolygons clipped;
   1.520 +
   1.521 +		ClipperLib::Polygon clip;
   1.522 +		clip.reserve(contour_flat.size());
   1.523 +		BOOST_FOREACH(const IfcVector2& pip, contour_flat) {
   1.524 +			clip.push_back(ClipperLib::IntPoint(  to_int64(pip.x), to_int64(pip.y) ));
   1.525 +		}
   1.526 +
   1.527 +		if (!ClipperLib::Orientation(clip)) {
   1.528 +			std::reverse(clip.begin(), clip.end());
   1.529 +		}
   1.530 +
   1.531 +		// We need to run polyclipper on every single polygon -- we can't run it one all
   1.532 +		// of them at once or it would merge them all together which would undo all
   1.533 +		// previous steps
   1.534 +		subject.reserve(4);
   1.535 +		size_t index = 0;
   1.536 +		size_t countdown = 0;
   1.537 +		BOOST_FOREACH(const IfcVector3& pip, curmesh.verts) {
   1.538 +			if (!countdown) {
   1.539 +				countdown = curmesh.vertcnt[index++];
   1.540 +				if (!countdown) {
   1.541 +					continue;
   1.542 +				}
   1.543 +			}
   1.544 +			subject.push_back(ClipperLib::IntPoint(  to_int64(pip.x), to_int64(pip.y) ));
   1.545 +			if (--countdown == 0) {
   1.546 +				if (!ClipperLib::Orientation(subject)) {
   1.547 +					std::reverse(subject.begin(), subject.end());
   1.548 +				}
   1.549 +
   1.550 +				clipper.AddPolygon(subject,ClipperLib::ptSubject);
   1.551 +				clipper.AddPolygon(clip,ClipperLib::ptClip);
   1.552 +
   1.553 +				clipper.Execute(ClipperLib::ctIntersection,clipped,ClipperLib::pftNonZero,ClipperLib::pftNonZero);
   1.554 +
   1.555 +				BOOST_FOREACH(const ClipperLib::ExPolygon& ex, clipped) {
   1.556 +					iold.push_back(ex.outer.size());
   1.557 +					BOOST_FOREACH(const ClipperLib::IntPoint& point, ex.outer) {
   1.558 +						vold.push_back(IfcVector3(
   1.559 +							from_int64(point.X), 
   1.560 +							from_int64(point.Y),
   1.561 +							0.0f));
   1.562 +					}
   1.563 +				}
   1.564 +
   1.565 +				subject.clear();
   1.566 +				clipped.clear();
   1.567 +				clipper.Clear();
   1.568 +			}
   1.569 +		}
   1.570 +	}
   1.571 +	catch (const char* sx) {
   1.572 +		IFCImporter::LogError("Ifc: error during polygon clipping, wall contour line may be wrong: (Clipper: " 
   1.573 +			+ std::string(sx) + ")");
   1.574 +
   1.575 +		return;
   1.576 +	}
   1.577 +
   1.578 +	// swap data arrays
   1.579 +	std::swap(vold,curmesh.verts);
   1.580 +	std::swap(iold,curmesh.vertcnt);
   1.581 +}
   1.582 +
   1.583 +typedef std::vector<TempOpening*> OpeningRefs;
   1.584 +typedef std::vector<OpeningRefs > OpeningRefVector;
   1.585 +
   1.586 +typedef std::vector<std::pair<
   1.587 +	ContourVector::const_iterator, 
   1.588 +	Contour::const_iterator> 
   1.589 +> ContourRefVector; 
   1.590 +
   1.591 +// ------------------------------------------------------------------------------------------------
   1.592 +bool BoundingBoxesAdjacent(const BoundingBox& bb, const BoundingBox& ibb)
   1.593 +{
   1.594 +	// TODO: I'm pretty sure there is a much more compact way to check this
   1.595 +	const IfcFloat epsilon = 1e-5f;
   1.596 +	return	(fabs(bb.second.x - ibb.first.x) < epsilon && bb.first.y <= ibb.second.y && bb.second.y >= ibb.first.y) ||
   1.597 +		(fabs(bb.first.x - ibb.second.x) < epsilon && ibb.first.y <= bb.second.y && ibb.second.y >= bb.first.y) || 
   1.598 +		(fabs(bb.second.y - ibb.first.y) < epsilon && bb.first.x <= ibb.second.x && bb.second.x >= ibb.first.x) ||
   1.599 +		(fabs(bb.first.y - ibb.second.y) < epsilon && ibb.first.x <= bb.second.x && ibb.second.x >= bb.first.x);
   1.600 +}
   1.601 +
   1.602 +// ------------------------------------------------------------------------------------------------
   1.603 +// Check if m0,m1 intersects n0,n1 assuming same ordering of the points in the line segments
   1.604 +// output the intersection points on n0,n1
   1.605 +bool IntersectingLineSegments(const IfcVector2& n0, const IfcVector2& n1, 
   1.606 +	const IfcVector2& m0, const IfcVector2& m1,
   1.607 +	IfcVector2& out0, IfcVector2& out1)
   1.608 +{
   1.609 +	const IfcVector2& n0_to_n1 = n1 - n0;
   1.610 +
   1.611 +	const IfcVector2& n0_to_m0 = m0 - n0;
   1.612 +	const IfcVector2& n1_to_m1 = m1 - n1;
   1.613 +
   1.614 +	const IfcVector2& n0_to_m1 = m1 - n0;
   1.615 +
   1.616 +	const IfcFloat e = 1e-5f;
   1.617 +	const IfcFloat smalle = 1e-9f;
   1.618 +
   1.619 +	static const IfcFloat inf = std::numeric_limits<IfcFloat>::infinity();
   1.620 +
   1.621 +	if (!(n0_to_m0.SquareLength() < e*e || fabs(n0_to_m0 * n0_to_n1) / (n0_to_m0.Length() * n0_to_n1.Length()) > 1-1e-5 )) {
   1.622 +		return false;
   1.623 +	}
   1.624 +
   1.625 +	if (!(n1_to_m1.SquareLength() < e*e || fabs(n1_to_m1 * n0_to_n1) / (n1_to_m1.Length() * n0_to_n1.Length()) > 1-1e-5 )) {
   1.626 +		return false;
   1.627 +	}
   1.628 +
   1.629 +	IfcFloat s0;
   1.630 +	IfcFloat s1;
   1.631 +
   1.632 +	// pick the axis with the higher absolute difference so the result
   1.633 +	// is more accurate. Since we cannot guarantee that the axis with
   1.634 +	// the higher absolute difference is big enough as to avoid
   1.635 +	// divisions by zero, the case 0/0 ~ infinity is detected and
   1.636 +	// handled separately.
   1.637 +	if(fabs(n0_to_n1.x) > fabs(n0_to_n1.y)) {
   1.638 +		s0 = n0_to_m0.x / n0_to_n1.x;
   1.639 +		s1 = n0_to_m1.x / n0_to_n1.x;
   1.640 +
   1.641 +		if (fabs(s0) == inf && fabs(n0_to_m0.x) < smalle) {
   1.642 +			s0 = 0.;
   1.643 +		}
   1.644 +		if (fabs(s1) == inf && fabs(n0_to_m1.x) < smalle) {
   1.645 +			s1 = 0.;
   1.646 +		}
   1.647 +	}
   1.648 +	else {
   1.649 +		s0 = n0_to_m0.y / n0_to_n1.y;
   1.650 +		s1 = n0_to_m1.y / n0_to_n1.y;
   1.651 +
   1.652 +		if (fabs(s0) == inf && fabs(n0_to_m0.y) < smalle) {
   1.653 +			s0 = 0.;
   1.654 +		}
   1.655 +		if (fabs(s1) == inf && fabs(n0_to_m1.y) < smalle) {
   1.656 +			s1 = 0.;
   1.657 +		}
   1.658 +	}
   1.659 +
   1.660 +	if (s1 < s0) {
   1.661 +		std::swap(s1,s0);
   1.662 +	}
   1.663 +
   1.664 +	s0 = std::max(0.0,s0);
   1.665 +	s1 = std::max(0.0,s1);
   1.666 +
   1.667 +	s0 = std::min(1.0,s0);
   1.668 +	s1 = std::min(1.0,s1);
   1.669 +
   1.670 +	if (fabs(s1-s0) < e) {
   1.671 +		return false;
   1.672 +	}
   1.673 +
   1.674 +	out0 = n0 + s0 * n0_to_n1;
   1.675 +	out1 = n0 + s1 * n0_to_n1;
   1.676 +
   1.677 +	return true;
   1.678 +}
   1.679 +
   1.680 +// ------------------------------------------------------------------------------------------------
   1.681 +void FindAdjacentContours(ContourVector::iterator current, const ContourVector& contours)
   1.682 +{
   1.683 +	const IfcFloat sqlen_epsilon = static_cast<IfcFloat>(1e-8);
   1.684 +	const BoundingBox& bb = (*current).bb;
   1.685 +
   1.686 +	// What is to be done here is to populate the skip lists for the contour
   1.687 +	// and to add necessary padding points when needed.
   1.688 +	SkipList& skiplist = (*current).skiplist;
   1.689 +
   1.690 +	// First step to find possible adjacent contours is to check for adjacent bounding
   1.691 +	// boxes. If the bounding boxes are not adjacent, the contours lines cannot possibly be.
   1.692 +	for (ContourVector::const_iterator it = contours.begin(), end = contours.end(); it != end; ++it) {
   1.693 +		if ((*it).IsInvalid()) {
   1.694 +			continue;
   1.695 +		}
   1.696 +
   1.697 +		// this left here to make clear we also run on the current contour
   1.698 +		// to check for overlapping contour segments (which can happen due
   1.699 +		// to projection artifacts).
   1.700 +		//if(it == current) {
   1.701 +		//	continue;
   1.702 +		//}
   1.703 +
   1.704 +		const bool is_me = it == current;
   1.705 +
   1.706 +		const BoundingBox& ibb = (*it).bb;
   1.707 +
   1.708 +		// Assumption: the bounding boxes are pairwise disjoint or identical
   1.709 +		ai_assert(is_me || !BoundingBoxesOverlapping(bb, ibb));
   1.710 +
   1.711 +		if (is_me || BoundingBoxesAdjacent(bb, ibb)) {
   1.712 +
   1.713 +			// Now do a each-against-everyone check for intersecting contour
   1.714 +			// lines. This obviously scales terribly, but in typical real
   1.715 +			// world Ifc files it will not matter since most windows that
   1.716 +			// are adjacent to each others are rectangular anyway.
   1.717 +
   1.718 +			Contour& ncontour = (*current).contour;
   1.719 +			const Contour& mcontour = (*it).contour;
   1.720 +
   1.721 +			for (size_t n = 0; n < ncontour.size(); ++n) {
   1.722 +				const IfcVector2& n0 = ncontour[n];
   1.723 +				const IfcVector2& n1 = ncontour[(n+1) % ncontour.size()];
   1.724 +
   1.725 +				for (size_t m = 0, mend = (is_me ? n : mcontour.size()); m < mend; ++m) {
   1.726 +					ai_assert(&mcontour != &ncontour || m < n);
   1.727 +
   1.728 +					const IfcVector2& m0 = mcontour[m];
   1.729 +					const IfcVector2& m1 = mcontour[(m+1) % mcontour.size()];
   1.730 +
   1.731 +					IfcVector2 isect0, isect1;
   1.732 +					if (IntersectingLineSegments(n0,n1, m0, m1, isect0, isect1)) {
   1.733 +
   1.734 +						if ((isect0 - n0).SquareLength() > sqlen_epsilon) {
   1.735 +							++n;
   1.736 +
   1.737 +							ncontour.insert(ncontour.begin() + n, isect0);	
   1.738 +							skiplist.insert(skiplist.begin() + n, true);
   1.739 +						}
   1.740 +						else {
   1.741 +							skiplist[n] = true;
   1.742 +						}
   1.743 +
   1.744 +						if ((isect1 - n1).SquareLength() > sqlen_epsilon) {
   1.745 +							++n;
   1.746 +
   1.747 +							ncontour.insert(ncontour.begin() + n, isect1);
   1.748 +							skiplist.insert(skiplist.begin() + n, false);
   1.749 +						}
   1.750 +					}
   1.751 +				}
   1.752 +			}
   1.753 +		}
   1.754 +	}
   1.755 +}
   1.756 +
   1.757 +// ------------------------------------------------------------------------------------------------
   1.758 +AI_FORCE_INLINE bool LikelyBorder(const IfcVector2& vdelta)
   1.759 +{
   1.760 +	const IfcFloat dot_point_epsilon = static_cast<IfcFloat>(1e-5);
   1.761 +	return fabs(vdelta.x * vdelta.y) < dot_point_epsilon;
   1.762 +}
   1.763 +
   1.764 +// ------------------------------------------------------------------------------------------------
   1.765 +void FindBorderContours(ContourVector::iterator current)
   1.766 +{
   1.767 +	const IfcFloat border_epsilon_upper = static_cast<IfcFloat>(1-1e-4);
   1.768 +	const IfcFloat border_epsilon_lower = static_cast<IfcFloat>(1e-4);
   1.769 +
   1.770 +	bool outer_border = false;
   1.771 +	bool start_on_outer_border = false;
   1.772 +
   1.773 +	SkipList& skiplist = (*current).skiplist;
   1.774 +	IfcVector2 last_proj_point;
   1.775 +
   1.776 +	const Contour::const_iterator cbegin = (*current).contour.begin(), cend = (*current).contour.end();
   1.777 +
   1.778 +	for (Contour::const_iterator cit = cbegin; cit != cend; ++cit) {
   1.779 +		const IfcVector2& proj_point = *cit;
   1.780 +
   1.781 +		// Check if this connection is along the outer boundary of the projection
   1.782 +		// plane. In such a case we better drop it because such 'edges' should
   1.783 +		// not have any geometry to close them (think of door openings).
   1.784 +		if (proj_point.x <= border_epsilon_lower || proj_point.x >= border_epsilon_upper ||
   1.785 +			proj_point.y <= border_epsilon_lower || proj_point.y >= border_epsilon_upper) {
   1.786 +
   1.787 +				if (outer_border) {
   1.788 +					ai_assert(cit != cbegin);
   1.789 +					if (LikelyBorder(proj_point - last_proj_point)) {
   1.790 +						skiplist[std::distance(cbegin, cit) - 1] = true;
   1.791 +					}
   1.792 +				}
   1.793 +				else if (cit == cbegin) {
   1.794 +					start_on_outer_border = true;
   1.795 +				}
   1.796 +		
   1.797 +				outer_border = true;
   1.798 +		}
   1.799 +		else {
   1.800 +			outer_border = false;
   1.801 +		}
   1.802 +
   1.803 +		last_proj_point = proj_point;
   1.804 +	}
   1.805 +
   1.806 +	// handle last segment
   1.807 +	if (outer_border && start_on_outer_border) {
   1.808 +		const IfcVector2& proj_point = *cbegin;
   1.809 +		if (LikelyBorder(proj_point - last_proj_point)) {
   1.810 +			skiplist[skiplist.size()-1] = true;
   1.811 +		}
   1.812 +	}
   1.813 +}
   1.814 +
   1.815 +// ------------------------------------------------------------------------------------------------
   1.816 +AI_FORCE_INLINE bool LikelyDiagonal(IfcVector2 vdelta)
   1.817 +{
   1.818 +	vdelta.x = fabs(vdelta.x);
   1.819 +	vdelta.y = fabs(vdelta.y);
   1.820 +	return (fabs(vdelta.x-vdelta.y) < 0.8 * std::max(vdelta.x, vdelta.y));
   1.821 +}
   1.822 +
   1.823 +// ------------------------------------------------------------------------------------------------
   1.824 +void FindLikelyCrossingLines(ContourVector::iterator current)
   1.825 +{
   1.826 +	SkipList& skiplist = (*current).skiplist;
   1.827 +	IfcVector2 last_proj_point;
   1.828 +
   1.829 +	const Contour::const_iterator cbegin = (*current).contour.begin(), cend = (*current).contour.end();
   1.830 +	for (Contour::const_iterator cit = cbegin; cit != cend; ++cit) {
   1.831 +		const IfcVector2& proj_point = *cit;
   1.832 +
   1.833 +		if (cit != cbegin) {
   1.834 +			IfcVector2 vdelta = proj_point - last_proj_point;
   1.835 +			if (LikelyDiagonal(vdelta)) {
   1.836 +				skiplist[std::distance(cbegin, cit) - 1] = true;
   1.837 +			}
   1.838 +		} 
   1.839 +
   1.840 +		last_proj_point = proj_point;
   1.841 +	}
   1.842 +
   1.843 +	// handle last segment
   1.844 +	if (LikelyDiagonal(*cbegin - last_proj_point)) {
   1.845 +		skiplist[skiplist.size()-1] = true;
   1.846 +	}
   1.847 +}
   1.848 +
   1.849 +// ------------------------------------------------------------------------------------------------
   1.850 +size_t CloseWindows(ContourVector& contours, 		  
   1.851 +	const IfcMatrix4& minv, 
   1.852 +	OpeningRefVector& contours_to_openings, 
   1.853 +	TempMesh& curmesh)
   1.854 +{
   1.855 +	size_t closed = 0;
   1.856 +	// For all contour points, check if one of the assigned openings does
   1.857 +	// already have points assigned to it. In this case, assume this is
   1.858 +	// the other side of the wall and generate connections between
   1.859 +	// the two holes in order to close the window. 
   1.860 +
   1.861 +	// All this gets complicated by the fact that contours may pertain to
   1.862 +	// multiple openings(due to merging of adjacent or overlapping openings). 
   1.863 +	// The code is based on the assumption that this happens symmetrically
   1.864 +	// on both sides of the wall. If it doesn't (which would be a bug anyway)
   1.865 +	// wrong geometry may be generated.
   1.866 +	for (ContourVector::iterator it = contours.begin(), end = contours.end(); it != end; ++it) {
   1.867 +		if ((*it).IsInvalid()) {
   1.868 +			continue;
   1.869 +		}
   1.870 +		OpeningRefs& refs = contours_to_openings[std::distance(contours.begin(), it)];
   1.871 +
   1.872 +		bool has_other_side = false;
   1.873 +		BOOST_FOREACH(const TempOpening* opening, refs) {
   1.874 +			if(!opening->wallPoints.empty()) {
   1.875 +				has_other_side = true;
   1.876 +				break;
   1.877 +			}
   1.878 +		}
   1.879 +
   1.880 +		if (has_other_side) {
   1.881 +
   1.882 +			ContourRefVector adjacent_contours;
   1.883 +
   1.884 +			// prepare a skiplist for this contour. The skiplist is used to
   1.885 +			// eliminate unwanted contour lines for adjacent windows and
   1.886 +			// those bordering the outer frame.
   1.887 +			(*it).PrepareSkiplist();
   1.888 +
   1.889 +			FindAdjacentContours(it, contours);
   1.890 +			FindBorderContours(it);
   1.891 +
   1.892 +			// if the window is the result of a finite union or intersection of rectangles,
   1.893 +			// there shouldn't be any crossing or diagonal lines in it. Such lines would
   1.894 +			// be artifacts caused by numerical inaccuracies or other bugs in polyclipper
   1.895 +			// and our own code. Since rectangular openings are by far the most frequent
   1.896 +			// case, it is worth filtering for this corner case.
   1.897 +			if((*it).is_rectangular) {
   1.898 +				FindLikelyCrossingLines(it);
   1.899 +			}
   1.900 +
   1.901 +			ai_assert((*it).skiplist.size() == (*it).contour.size());
   1.902 +
   1.903 +			SkipList::const_iterator skipbegin = (*it).skiplist.begin();
   1.904 +
   1.905 +			curmesh.verts.reserve(curmesh.verts.size() + (*it).contour.size() * 4);
   1.906 +			curmesh.vertcnt.reserve(curmesh.vertcnt.size() + (*it).contour.size());
   1.907 +
   1.908 +			// XXX this algorithm is really a bit inefficient - both in terms
   1.909 +			// of constant factor and of asymptotic runtime.
   1.910 +			std::vector<bool>::const_iterator skipit = skipbegin;
   1.911 +
   1.912 +			IfcVector3 start0;
   1.913 +			IfcVector3 start1;
   1.914 +
   1.915 +			IfcVector2 last_proj; 
   1.916 +			//const IfcVector2& first_proj; 
   1.917 +
   1.918 +			const Contour::const_iterator cbegin = (*it).contour.begin(), cend = (*it).contour.end();
   1.919 +
   1.920 +			bool drop_this_edge = false;
   1.921 +			for (Contour::const_iterator cit = cbegin; cit != cend; ++cit, drop_this_edge = *skipit++) {
   1.922 +				const IfcVector2& proj_point = *cit;
   1.923 +
   1.924 +				// Locate the closest opposite point. This should be a good heuristic to
   1.925 +				// connect only the points that are really intended to be connected.
   1.926 +				IfcFloat best = static_cast<IfcFloat>(1e10);
   1.927 +				IfcVector3 bestv;
   1.928 +
   1.929 +				/* debug code to check for unwanted diagonal lines in window contours
   1.930 +				if (cit != cbegin) {
   1.931 +					const IfcVector2& vdelta = proj_point - last_proj;
   1.932 +					if (fabs(vdelta.x-vdelta.y) < 0.5 * std::max(vdelta.x, vdelta.y)) {
   1.933 +						//continue;
   1.934 +					}
   1.935 +				} */
   1.936 +
   1.937 +				const IfcVector3& world_point = minv * IfcVector3(proj_point.x,proj_point.y,0.0f);
   1.938 +				
   1.939 +				last_proj = proj_point;
   1.940 +
   1.941 +				BOOST_FOREACH(const TempOpening* opening, refs) {
   1.942 +					BOOST_FOREACH(const IfcVector3& other, opening->wallPoints) {
   1.943 +						const IfcFloat sqdist = (world_point - other).SquareLength();
   1.944 +						
   1.945 +						if (sqdist < best) {
   1.946 +							// avoid self-connections
   1.947 +							if(sqdist < 1e-5) {
   1.948 +								continue;
   1.949 +							}
   1.950 +
   1.951 +							bestv = other;
   1.952 +							best = sqdist;							
   1.953 +						}
   1.954 +					}
   1.955 +				}
   1.956 +
   1.957 +				if (drop_this_edge) {
   1.958 +					curmesh.verts.pop_back();
   1.959 +					curmesh.verts.pop_back();
   1.960 +				}
   1.961 +				else {
   1.962 +					curmesh.verts.push_back(cit == cbegin ? world_point : bestv);
   1.963 +					curmesh.verts.push_back(cit == cbegin ? bestv : world_point);
   1.964 +
   1.965 +					curmesh.vertcnt.push_back(4);
   1.966 +					++closed;
   1.967 +				}
   1.968 +
   1.969 +				if (cit == cbegin) {
   1.970 +					start0 = world_point;
   1.971 +					start1 = bestv;
   1.972 +					continue;
   1.973 +				}
   1.974 +
   1.975 +				curmesh.verts.push_back(world_point);
   1.976 +				curmesh.verts.push_back(bestv);
   1.977 +
   1.978 +				if (cit == cend - 1) {
   1.979 +					drop_this_edge = *skipit;
   1.980 +
   1.981 +					// Check if the final connection (last to first element) is itself
   1.982 +					// a border edge that needs to be dropped.
   1.983 +					if (drop_this_edge) {
   1.984 +						--closed;
   1.985 +						curmesh.vertcnt.pop_back();
   1.986 +						curmesh.verts.pop_back();
   1.987 +						curmesh.verts.pop_back();
   1.988 +					}
   1.989 +					else {
   1.990 +						curmesh.verts.push_back(start1);
   1.991 +						curmesh.verts.push_back(start0);
   1.992 +					}
   1.993 +				}
   1.994 +			}
   1.995 +			/*
   1.996 +			BOOST_FOREACH(TempOpening* opening, refs) {
   1.997 +				//opening->wallPoints.clear();
   1.998 +			}*/
   1.999 +
  1.1000 +		}
  1.1001 +		else {
  1.1002 +			
  1.1003 +			const Contour::const_iterator cbegin = (*it).contour.begin(), cend = (*it).contour.end();
  1.1004 +			BOOST_FOREACH(TempOpening* opening, refs) {
  1.1005 +				ai_assert(opening->wallPoints.empty());
  1.1006 +				opening->wallPoints.reserve(opening->wallPoints.capacity() + (*it).contour.size());
  1.1007 +				for (Contour::const_iterator cit = cbegin; cit != cend; ++cit) {
  1.1008 +
  1.1009 +					const IfcVector2& proj_point = *cit;
  1.1010 +					opening->wallPoints.push_back(minv * IfcVector3(proj_point.x,proj_point.y,0.0f));
  1.1011 +				}
  1.1012 +			}
  1.1013 +		}
  1.1014 +	}
  1.1015 +	return closed;
  1.1016 +}
  1.1017 +
  1.1018 +// ------------------------------------------------------------------------------------------------
  1.1019 +void Quadrify(const std::vector< BoundingBox >& bbs, TempMesh& curmesh)
  1.1020 +{
  1.1021 +	ai_assert(curmesh.IsEmpty());
  1.1022 +
  1.1023 +	std::vector<IfcVector2> quads;
  1.1024 +	quads.reserve(bbs.size()*4);
  1.1025 +
  1.1026 +	// sort openings by x and y axis as a preliminiary to the QuadrifyPart() algorithm
  1.1027 +	XYSortedField field;
  1.1028 +	for (std::vector<BoundingBox>::const_iterator it = bbs.begin(); it != bbs.end(); ++it) {
  1.1029 +		if (field.find((*it).first) != field.end()) {
  1.1030 +			IFCImporter::LogWarn("constraint failure during generation of wall openings, results may be faulty");
  1.1031 +		}
  1.1032 +		field[(*it).first] = std::distance(bbs.begin(),it);
  1.1033 +	}
  1.1034 +
  1.1035 +	QuadrifyPart(IfcVector2(),one_vec,field,bbs,quads);
  1.1036 +	ai_assert(!(quads.size() % 4));
  1.1037 +
  1.1038 +	curmesh.vertcnt.resize(quads.size()/4,4);
  1.1039 +	curmesh.verts.reserve(quads.size());
  1.1040 +	BOOST_FOREACH(const IfcVector2& v2, quads) {
  1.1041 +		curmesh.verts.push_back(IfcVector3(v2.x, v2.y, static_cast<IfcFloat>(0.0)));
  1.1042 +	}
  1.1043 +}
  1.1044 +
  1.1045 +// ------------------------------------------------------------------------------------------------
  1.1046 +void Quadrify(const ContourVector& contours, TempMesh& curmesh)
  1.1047 +{
  1.1048 +	std::vector<BoundingBox> bbs;
  1.1049 +	bbs.reserve(contours.size());
  1.1050 +
  1.1051 +	BOOST_FOREACH(const ContourVector::value_type& val, contours) {
  1.1052 +		bbs.push_back(val.bb);
  1.1053 +	}
  1.1054 +
  1.1055 +	Quadrify(bbs, curmesh);
  1.1056 +}
  1.1057 +
  1.1058 +// ------------------------------------------------------------------------------------------------
  1.1059 +IfcMatrix4 ProjectOntoPlane(std::vector<IfcVector2>& out_contour, const TempMesh& in_mesh, 
  1.1060 +	bool &ok, IfcVector3& nor_out)
  1.1061 +{
  1.1062 +	const std::vector<IfcVector3>& in_verts = in_mesh.verts;
  1.1063 +	ok = true;
  1.1064 +
  1.1065 +	IfcMatrix4 m = IfcMatrix4(DerivePlaneCoordinateSpace(in_mesh, ok, nor_out));
  1.1066 +	if(!ok) {
  1.1067 +		return IfcMatrix4();
  1.1068 +	}
  1.1069 +#ifdef _DEBUG
  1.1070 +	const IfcFloat det = m.Determinant();
  1.1071 +	ai_assert(fabs(det-1) < 1e-5);
  1.1072 +#endif
  1.1073 +
  1.1074 +	IfcFloat zcoord = 0;
  1.1075 +	out_contour.reserve(in_verts.size());
  1.1076 +
  1.1077 +
  1.1078 +	IfcVector3 vmin, vmax;
  1.1079 +	MinMaxChooser<IfcVector3>()(vmin, vmax);
  1.1080 +
  1.1081 +	// Project all points into the new coordinate system, collect min/max verts on the way
  1.1082 +	BOOST_FOREACH(const IfcVector3& x, in_verts) {
  1.1083 +		const IfcVector3& vv = m * x;
  1.1084 +		// keep Z offset in the plane coordinate system. Ignoring precision issues
  1.1085 +		// (which  are present, of course), this should be the same value for
  1.1086 +		// all polygon vertices (assuming the polygon is planar).
  1.1087 +
  1.1088 +		// XXX this should be guarded, but we somehow need to pick a suitable
  1.1089 +		// epsilon
  1.1090 +		// if(coord != -1.0f) {
  1.1091 +		//	assert(fabs(coord - vv.z) < 1e-3f);
  1.1092 +		// }
  1.1093 +		zcoord += vv.z;
  1.1094 +		vmin = std::min(vv, vmin);
  1.1095 +		vmax = std::max(vv, vmax);
  1.1096 +
  1.1097 +		out_contour.push_back(IfcVector2(vv.x,vv.y));
  1.1098 +	}
  1.1099 +
  1.1100 +	zcoord /= in_verts.size();
  1.1101 +
  1.1102 +	// Further improve the projection by mapping the entire working set into
  1.1103 +	// [0,1] range. This gives us a consistent data range so all epsilons
  1.1104 +	// used below can be constants.
  1.1105 +	vmax -= vmin;
  1.1106 +	BOOST_FOREACH(IfcVector2& vv, out_contour) {
  1.1107 +		vv.x  = (vv.x - vmin.x) / vmax.x;
  1.1108 +		vv.y  = (vv.y - vmin.y) / vmax.y;
  1.1109 +
  1.1110 +		// sanity rounding
  1.1111 +		vv = std::max(vv,IfcVector2());
  1.1112 +		vv = std::min(vv,one_vec);
  1.1113 +	}
  1.1114 +
  1.1115 +	IfcMatrix4 mult;
  1.1116 +	mult.a1 = static_cast<IfcFloat>(1.0) / vmax.x;
  1.1117 +	mult.b2 = static_cast<IfcFloat>(1.0) / vmax.y;
  1.1118 +
  1.1119 +	mult.a4 = -vmin.x * mult.a1;
  1.1120 +	mult.b4 = -vmin.y * mult.b2;
  1.1121 +	mult.c4 = -zcoord;
  1.1122 +	m = mult * m;
  1.1123 +
  1.1124 +	// debug code to verify correctness
  1.1125 +#ifdef _DEBUG
  1.1126 +	std::vector<IfcVector2> out_contour2;
  1.1127 +	BOOST_FOREACH(const IfcVector3& x, in_verts) {
  1.1128 +		const IfcVector3& vv = m * x;
  1.1129 +
  1.1130 +		out_contour2.push_back(IfcVector2(vv.x,vv.y));
  1.1131 +		ai_assert(fabs(vv.z) < vmax.z + 1e-8);
  1.1132 +	} 
  1.1133 +
  1.1134 +	for(size_t i = 0; i < out_contour.size(); ++i) {
  1.1135 +		ai_assert((out_contour[i]-out_contour2[i]).SquareLength() < 1e-6);
  1.1136 +	}
  1.1137 +#endif
  1.1138 +
  1.1139 +	return m;
  1.1140 +}
  1.1141 +
  1.1142 +// ------------------------------------------------------------------------------------------------
  1.1143 +bool GenerateOpenings(std::vector<TempOpening>& openings,
  1.1144 +	const std::vector<IfcVector3>& nors, 
  1.1145 +	TempMesh& curmesh,
  1.1146 +	bool check_intersection,
  1.1147 +	bool generate_connection_geometry,
  1.1148 +	const IfcVector3& wall_extrusion_axis)
  1.1149 +{
  1.1150 +	OpeningRefVector contours_to_openings;
  1.1151 +
  1.1152 +	// Try to derive a solid base plane within the current surface for use as 
  1.1153 +	// working coordinate system. Map all vertices onto this plane and 
  1.1154 +	// rescale them to [0,1] range. This normalization means all further
  1.1155 +	// epsilons need not be scaled.
  1.1156 +	bool ok = true;
  1.1157 +
  1.1158 +	std::vector<IfcVector2> contour_flat;
  1.1159 +
  1.1160 +	IfcVector3 nor;
  1.1161 +	const IfcMatrix4& m = ProjectOntoPlane(contour_flat, curmesh,  ok, nor);
  1.1162 +	if(!ok) {
  1.1163 +		return false;
  1.1164 +	}
  1.1165 +
  1.1166 +	// Obtain inverse transform for getting back to world space later on
  1.1167 +	const IfcMatrix4 minv = IfcMatrix4(m).Inverse();
  1.1168 +
  1.1169 +	// Compute bounding boxes for all 2D openings in projection space
  1.1170 +	ContourVector contours;
  1.1171 +
  1.1172 +	std::vector<IfcVector2> temp_contour;
  1.1173 +	std::vector<IfcVector2> temp_contour2;
  1.1174 +
  1.1175 +	IfcVector3 wall_extrusion_axis_norm = wall_extrusion_axis;
  1.1176 +	wall_extrusion_axis_norm.Normalize();
  1.1177 +
  1.1178 +	BOOST_FOREACH(TempOpening& opening,openings) {
  1.1179 +
  1.1180 +		// extrusionDir may be 0,0,0 on case where the opening mesh is not an
  1.1181 +		// IfcExtrudedAreaSolid but something else (i.e. a brep)
  1.1182 +		IfcVector3 norm_extrusion_dir = opening.extrusionDir;
  1.1183 +		if (norm_extrusion_dir.SquareLength() > 1e-10) {
  1.1184 +			norm_extrusion_dir.Normalize();
  1.1185 +		}
  1.1186 +		else {
  1.1187 +			norm_extrusion_dir = IfcVector3();
  1.1188 +		}
  1.1189 +
  1.1190 +		TempMesh* profile_data =  opening.profileMesh.get();
  1.1191 +		bool is_2d_source = false;
  1.1192 +		if (opening.profileMesh2D && norm_extrusion_dir.SquareLength() > 0) {
  1.1193 +			
  1.1194 +			if(fabs(norm_extrusion_dir * wall_extrusion_axis_norm) < 0.1) {
  1.1195 +				// horizontal extrusion
  1.1196 +				if (fabs(norm_extrusion_dir * nor) > 0.9) {
  1.1197 +					profile_data = opening.profileMesh2D.get();
  1.1198 +					is_2d_source = true;
  1.1199 +				}
  1.1200 +				else {
  1.1201 +					//continue;
  1.1202 +				}
  1.1203 +			}
  1.1204 +			else {
  1.1205 +				// vertical extrusion
  1.1206 +				if (fabs(norm_extrusion_dir * nor) > 0.9) {
  1.1207 +					continue;
  1.1208 +				}
  1.1209 +				continue;
  1.1210 +			}
  1.1211 +		}
  1.1212 +		std::vector<IfcVector3> profile_verts = profile_data->verts;
  1.1213 +		std::vector<unsigned int> profile_vertcnts = profile_data->vertcnt;
  1.1214 +		if(profile_verts.size() <= 2) {
  1.1215 +			continue;	
  1.1216 +		}	
  1.1217 +
  1.1218 +		// The opening meshes are real 3D meshes so skip over all faces
  1.1219 +		// clearly facing into the wrong direction. Also, we need to check
  1.1220 +		// whether the meshes do actually intersect the base surface plane.
  1.1221 +		// This is done by recording minimum and maximum values for the
  1.1222 +		// d component of the plane equation for all polys and checking
  1.1223 +		// against surface d.
  1.1224 +
  1.1225 +		// Use the sign of the dot product of the face normal to the plane
  1.1226 +		// normal to determine to which side of the difference mesh a
  1.1227 +		// triangle belongs. Get independent bounding boxes and vertex
  1.1228 +		// sets for both sides and take the better one (we can't just
  1.1229 +		// take both - this would likely cause major screwup of vertex
  1.1230 +		// winding, producing errors as late as in CloseWindows()).
  1.1231 +		IfcFloat dmin, dmax;
  1.1232 +		MinMaxChooser<IfcFloat>()(dmin,dmax);
  1.1233 +
  1.1234 +		temp_contour.clear();
  1.1235 +		temp_contour2.clear();
  1.1236 +
  1.1237 +		IfcVector2 vpmin,vpmax;
  1.1238 +		MinMaxChooser<IfcVector2>()(vpmin,vpmax);
  1.1239 +
  1.1240 +		IfcVector2 vpmin2,vpmax2;
  1.1241 +		MinMaxChooser<IfcVector2>()(vpmin2,vpmax2);
  1.1242 +
  1.1243 +		for (size_t f = 0, vi_total = 0, fend = profile_vertcnts.size(); f < fend; ++f) {
  1.1244 +
  1.1245 +			bool side_flag = true;
  1.1246 +			if (!is_2d_source) {
  1.1247 +				const IfcVector3& face_nor = ((profile_verts[vi_total+2] - profile_verts[vi_total]) ^
  1.1248 +					(profile_verts[vi_total+1] - profile_verts[vi_total])).Normalize();
  1.1249 +
  1.1250 +				const IfcFloat abs_dot_face_nor = abs(nor * face_nor);
  1.1251 +				if (abs_dot_face_nor < 0.9) {
  1.1252 +					vi_total += profile_vertcnts[f];
  1.1253 +					continue;
  1.1254 +				}
  1.1255 +
  1.1256 +				side_flag = nor * face_nor > 0;
  1.1257 +			}
  1.1258 +
  1.1259 +			for (unsigned int vi = 0, vend = profile_vertcnts[f]; vi < vend; ++vi, ++vi_total) {
  1.1260 +				const IfcVector3& x = profile_verts[vi_total];
  1.1261 +
  1.1262 +				const IfcVector3& v = m * x;
  1.1263 +				IfcVector2 vv(v.x, v.y);
  1.1264 +
  1.1265 +				//if(check_intersection) {
  1.1266 +					dmin = std::min(dmin, v.z);
  1.1267 +					dmax = std::max(dmax, v.z);
  1.1268 +				//}
  1.1269 +
  1.1270 +				// sanity rounding
  1.1271 +				vv = std::max(vv,IfcVector2());
  1.1272 +				vv = std::min(vv,one_vec);
  1.1273 +
  1.1274 +				if(side_flag) {
  1.1275 +					vpmin = std::min(vpmin,vv);
  1.1276 +					vpmax = std::max(vpmax,vv);
  1.1277 +				}
  1.1278 +				else {
  1.1279 +					vpmin2 = std::min(vpmin2,vv);
  1.1280 +					vpmax2 = std::max(vpmax2,vv);
  1.1281 +				}
  1.1282 +
  1.1283 +				std::vector<IfcVector2>& store = side_flag ? temp_contour : temp_contour2;
  1.1284 +
  1.1285 +				if (!IsDuplicateVertex(vv, store)) {
  1.1286 +					store.push_back(vv);
  1.1287 +				}		
  1.1288 +			}
  1.1289 +		}
  1.1290 +
  1.1291 +		if (temp_contour2.size() > 2) {
  1.1292 +			ai_assert(!is_2d_source);
  1.1293 +			const IfcVector2 area = vpmax-vpmin;
  1.1294 +			const IfcVector2 area2 = vpmax2-vpmin2;
  1.1295 +			if (temp_contour.size() <= 2 || fabs(area2.x * area2.y) > fabs(area.x * area.y)) {
  1.1296 +				temp_contour.swap(temp_contour2);
  1.1297 +
  1.1298 +				vpmax = vpmax2;
  1.1299 +				vpmin = vpmin2;			
  1.1300 +			}
  1.1301 +		}
  1.1302 +		if(temp_contour.size() <= 2) {
  1.1303 +			continue;
  1.1304 +		}
  1.1305 +
  1.1306 +		// TODO: This epsilon may be too large
  1.1307 +		const IfcFloat epsilon = fabs(dmax-dmin) * 0.0001;
  1.1308 +		if (!is_2d_source && check_intersection && (0 < dmin-epsilon || 0 > dmax+epsilon)) {
  1.1309 +			continue;
  1.1310 +		}
  1.1311 +
  1.1312 +		BoundingBox bb = BoundingBox(vpmin,vpmax);
  1.1313 +
  1.1314 +		// Skip over very small openings - these are likely projection errors
  1.1315 +		// (i.e. they don't belong to this side of the wall)
  1.1316 +		if(fabs(vpmax.x - vpmin.x) * fabs(vpmax.y - vpmin.y) < static_cast<IfcFloat>(1e-10)) {
  1.1317 +			continue;
  1.1318 +		}
  1.1319 +		std::vector<TempOpening*> joined_openings(1, &opening);
  1.1320 +
  1.1321 +		bool is_rectangle = temp_contour.size() == 4;
  1.1322 +
  1.1323 +		// See if this BB intersects or is in close adjacency to any other BB we have so far.
  1.1324 +		for (ContourVector::iterator it = contours.begin(); it != contours.end(); ) {
  1.1325 +			const BoundingBox& ibb = (*it).bb;
  1.1326 +
  1.1327 +			if (BoundingBoxesOverlapping(ibb, bb)) {
  1.1328 +
  1.1329 +				if (!(*it).is_rectangular) {
  1.1330 +					is_rectangle = false;
  1.1331 +				}
  1.1332 +
  1.1333 +				const std::vector<IfcVector2>& other = (*it).contour;
  1.1334 +				ClipperLib::ExPolygons poly;
  1.1335 +
  1.1336 +				// First check whether subtracting the old contour (to which ibb belongs)
  1.1337 +				// from the new contour (to which bb belongs) yields an updated bb which
  1.1338 +				// no longer overlaps ibb
  1.1339 +				MakeDisjunctWindowContours(other, temp_contour, poly);
  1.1340 +				if(poly.size() == 1) {
  1.1341 +					
  1.1342 +					const BoundingBox& newbb = GetBoundingBox(poly[0].outer);
  1.1343 +					if (!BoundingBoxesOverlapping(ibb, newbb )) {
  1.1344 +						 // Good guy bounding box
  1.1345 +						 bb = newbb ;
  1.1346 +
  1.1347 +						 ExtractVerticesFromClipper(poly[0].outer, temp_contour, false);
  1.1348 +						 continue;
  1.1349 +					}
  1.1350 +				}
  1.1351 +
  1.1352 +				// Take these two overlapping contours and try to merge them. If they 
  1.1353 +				// overlap (which should not happen, but in fact happens-in-the-real-
  1.1354 +				// world [tm] ), resume using a single contour and a single bounding box.
  1.1355 +				MergeWindowContours(temp_contour, other, poly);
  1.1356 +
  1.1357 +				if (poly.size() > 1) { 
  1.1358 +					return TryAddOpenings_Poly2Tri(openings, nors, curmesh);
  1.1359 +				}
  1.1360 +				else if (poly.size() == 0) {
  1.1361 +					IFCImporter::LogWarn("ignoring duplicate opening");
  1.1362 +					temp_contour.clear();
  1.1363 +					break;
  1.1364 +				}
  1.1365 +				else {
  1.1366 +					IFCImporter::LogDebug("merging overlapping openings");				
  1.1367 +					ExtractVerticesFromClipper(poly[0].outer, temp_contour, false);
  1.1368 +
  1.1369 +					// Generate the union of the bounding boxes
  1.1370 +					bb.first = std::min(bb.first, ibb.first);
  1.1371 +					bb.second = std::max(bb.second, ibb.second);
  1.1372 +
  1.1373 +					// Update contour-to-opening tables accordingly
  1.1374 +					if (generate_connection_geometry) {
  1.1375 +						std::vector<TempOpening*>& t = contours_to_openings[std::distance(contours.begin(),it)]; 
  1.1376 +						joined_openings.insert(joined_openings.end(), t.begin(), t.end());
  1.1377 +
  1.1378 +						contours_to_openings.erase(contours_to_openings.begin() + std::distance(contours.begin(),it));
  1.1379 +					}
  1.1380 +
  1.1381 +					contours.erase(it);
  1.1382 +
  1.1383 +					// Restart from scratch because the newly formed BB might now
  1.1384 +					// overlap any other BB which its constituent BBs didn't
  1.1385 +					// previously overlap.
  1.1386 +					it = contours.begin();
  1.1387 +					continue;
  1.1388 +				}
  1.1389 +			}
  1.1390 +			++it;
  1.1391 +		}
  1.1392 +
  1.1393 +		if(!temp_contour.empty()) {
  1.1394 +			if (generate_connection_geometry) {
  1.1395 +				contours_to_openings.push_back(std::vector<TempOpening*>(
  1.1396 +					joined_openings.begin(),
  1.1397 +					joined_openings.end()));
  1.1398 +			}
  1.1399 +
  1.1400 +			contours.push_back(ProjectedWindowContour(temp_contour, bb, is_rectangle));
  1.1401 +		}
  1.1402 +	}
  1.1403 +
  1.1404 +	// Check if we still have any openings left - it may well be that this is
  1.1405 +	// not the cause, for example if all the opening candidates don't intersect
  1.1406 +	// this surface or point into a direction perpendicular to it.
  1.1407 +	if (contours.empty()) {
  1.1408 +		return false;
  1.1409 +	}
  1.1410 +
  1.1411 +	curmesh.Clear();
  1.1412 +
  1.1413 +	// Generate a base subdivision into quads to accommodate the given list
  1.1414 +	// of window bounding boxes.
  1.1415 +	Quadrify(contours,curmesh);
  1.1416 +
  1.1417 +	// Run a sanity cleanup pass on the window contours to avoid generating
  1.1418 +	// artifacts during the contour generation phase later on.
  1.1419 +	CleanupWindowContours(contours);
  1.1420 +
  1.1421 +	// Previously we reduced all windows to rectangular AABBs in projection
  1.1422 +	// space, now it is time to fill the gaps between the BBs and the real
  1.1423 +	// window openings.
  1.1424 +	InsertWindowContours(contours,openings, curmesh);
  1.1425 +
  1.1426 +	// Clip the entire outer contour of our current result against the real
  1.1427 +	// outer contour of the surface. This is necessary because the result
  1.1428 +	// of the Quadrify() algorithm is always a square area spanning
  1.1429 +	// over [0,1]^2 (i.e. entire projection space).
  1.1430 +	CleanupOuterContour(contour_flat, curmesh);
  1.1431 +
  1.1432 +	// Undo the projection and get back to world (or local object) space
  1.1433 +	BOOST_FOREACH(IfcVector3& v3, curmesh.verts) {
  1.1434 +		v3 = minv * v3;
  1.1435 +	}
  1.1436 +
  1.1437 +	// Generate window caps to connect the symmetric openings on both sides
  1.1438 +	// of the wall.
  1.1439 + 	if (generate_connection_geometry) {
  1.1440 +		CloseWindows(contours, minv, contours_to_openings, curmesh);
  1.1441 +	}
  1.1442 +	return true;
  1.1443 +}
  1.1444 +
  1.1445 +// ------------------------------------------------------------------------------------------------
  1.1446 +bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std::vector<IfcVector3>& nors, 
  1.1447 +	TempMesh& curmesh)
  1.1448 +{
  1.1449 +	IFCImporter::LogWarn("forced to use poly2tri fallback method to generate wall openings");
  1.1450 +	std::vector<IfcVector3>& out = curmesh.verts;
  1.1451 +
  1.1452 +	bool result = false;
  1.1453 +
  1.1454 +	// Try to derive a solid base plane within the current surface for use as 
  1.1455 +	// working coordinate system. 
  1.1456 +	bool ok;
  1.1457 +	IfcVector3 nor;
  1.1458 +	const IfcMatrix3& m = DerivePlaneCoordinateSpace(curmesh, ok, nor);
  1.1459 +	if (!ok) {
  1.1460 +		return false;
  1.1461 +	}
  1.1462 +
  1.1463 +	const IfcMatrix3 minv = IfcMatrix3(m).Inverse();
  1.1464 +
  1.1465 +
  1.1466 +	IfcFloat coord = -1;
  1.1467 +
  1.1468 +	std::vector<IfcVector2> contour_flat;
  1.1469 +	contour_flat.reserve(out.size());
  1.1470 +
  1.1471 +	IfcVector2 vmin, vmax;
  1.1472 +	MinMaxChooser<IfcVector2>()(vmin, vmax);
  1.1473 +	
  1.1474 +	// Move all points into the new coordinate system, collecting min/max verts on the way
  1.1475 +	BOOST_FOREACH(IfcVector3& x, out) {
  1.1476 +		const IfcVector3 vv = m * x;
  1.1477 +
  1.1478 +		// keep Z offset in the plane coordinate system. Ignoring precision issues
  1.1479 +		// (which  are present, of course), this should be the same value for
  1.1480 +		// all polygon vertices (assuming the polygon is planar).
  1.1481 +
  1.1482 +
  1.1483 +		// XXX this should be guarded, but we somehow need to pick a suitable
  1.1484 +		// epsilon
  1.1485 +		// if(coord != -1.0f) {
  1.1486 +		//	assert(fabs(coord - vv.z) < 1e-3f);
  1.1487 +		// }
  1.1488 +
  1.1489 +		coord = vv.z;
  1.1490 +
  1.1491 +		vmin = std::min(IfcVector2(vv.x, vv.y), vmin);
  1.1492 +		vmax = std::max(IfcVector2(vv.x, vv.y), vmax);
  1.1493 +
  1.1494 +		contour_flat.push_back(IfcVector2(vv.x,vv.y));
  1.1495 +	}
  1.1496 +		
  1.1497 +	// With the current code in DerivePlaneCoordinateSpace, 
  1.1498 +	// vmin,vmax should always be the 0...1 rectangle (+- numeric inaccuracies) 
  1.1499 +	// but here we won't rely on this.
  1.1500 +
  1.1501 +	vmax -= vmin;
  1.1502 +
  1.1503 +	// If this happens then the projection must have been wrong.
  1.1504 +	assert(vmax.Length());
  1.1505 +
  1.1506 +	ClipperLib::ExPolygons clipped;
  1.1507 +	ClipperLib::Polygons holes_union;
  1.1508 +
  1.1509 +
  1.1510 +	IfcVector3 wall_extrusion;
  1.1511 +	bool do_connections = false, first = true;
  1.1512 +
  1.1513 +	try {
  1.1514 +
  1.1515 +		ClipperLib::Clipper clipper_holes;
  1.1516 +		size_t c = 0;
  1.1517 +
  1.1518 +		BOOST_FOREACH(const TempOpening& t,openings) {
  1.1519 +			const IfcVector3& outernor = nors[c++];
  1.1520 +			const IfcFloat dot = nor * outernor;
  1.1521 +			if (fabs(dot)<1.f-1e-6f) {
  1.1522 +				continue;
  1.1523 +			}
  1.1524 +
  1.1525 +			const std::vector<IfcVector3>& va = t.profileMesh->verts;
  1.1526 +			if(va.size() <= 2) {
  1.1527 +				continue;	
  1.1528 +			}
  1.1529 +		
  1.1530 +			std::vector<IfcVector2> contour;
  1.1531 +
  1.1532 +			BOOST_FOREACH(const IfcVector3& xx, t.profileMesh->verts) {
  1.1533 +				IfcVector3 vv = m *  xx, vv_extr = m * (xx + t.extrusionDir);
  1.1534 +				
  1.1535 +				const bool is_extruded_side = fabs(vv.z - coord) > fabs(vv_extr.z - coord);
  1.1536 +				if (first) {
  1.1537 +					first = false;
  1.1538 +					if (dot > 0.f) {
  1.1539 +						do_connections = true;
  1.1540 +						wall_extrusion = t.extrusionDir;
  1.1541 +						if (is_extruded_side) {
  1.1542 +							wall_extrusion = - wall_extrusion;
  1.1543 +						}
  1.1544 +					}
  1.1545 +				}
  1.1546 +
  1.1547 +				// XXX should not be necessary - but it is. Why? For precision reasons?
  1.1548 +				vv = is_extruded_side ? vv_extr : vv;
  1.1549 +				contour.push_back(IfcVector2(vv.x,vv.y));
  1.1550 +			}
  1.1551 +
  1.1552 +			ClipperLib::Polygon hole;
  1.1553 +			BOOST_FOREACH(IfcVector2& pip, contour) {
  1.1554 +				pip.x  = (pip.x - vmin.x) / vmax.x;
  1.1555 +				pip.y  = (pip.y - vmin.y) / vmax.y;
  1.1556 +
  1.1557 +				hole.push_back(ClipperLib::IntPoint(  to_int64(pip.x), to_int64(pip.y) ));
  1.1558 +			}
  1.1559 +
  1.1560 +			if (!ClipperLib::Orientation(hole)) {
  1.1561 +				std::reverse(hole.begin(), hole.end());
  1.1562 +			//	assert(ClipperLib::Orientation(hole));
  1.1563 +			}
  1.1564 +
  1.1565 +			/*ClipperLib::Polygons pol_temp(1), pol_temp2(1);
  1.1566 +			pol_temp[0] = hole;
  1.1567 +
  1.1568 +			ClipperLib::OffsetPolygons(pol_temp,pol_temp2,5.0);
  1.1569 +			hole = pol_temp2[0];*/
  1.1570 +
  1.1571 +			clipper_holes.AddPolygon(hole,ClipperLib::ptSubject);
  1.1572 +		}
  1.1573 +
  1.1574 +		clipper_holes.Execute(ClipperLib::ctUnion,holes_union,
  1.1575 +			ClipperLib::pftNonZero,
  1.1576 +			ClipperLib::pftNonZero);
  1.1577 +
  1.1578 +		if (holes_union.empty()) {
  1.1579 +			return false;
  1.1580 +		}
  1.1581 +
  1.1582 +		// Now that we have the big union of all holes, subtract it from the outer contour
  1.1583 +		// to obtain the final polygon to feed into the triangulator.
  1.1584 +		{
  1.1585 +			ClipperLib::Polygon poly;
  1.1586 +			BOOST_FOREACH(IfcVector2& pip, contour_flat) {
  1.1587 +				pip.x  = (pip.x - vmin.x) / vmax.x;
  1.1588 +				pip.y  = (pip.y - vmin.y) / vmax.y;
  1.1589 +
  1.1590 +				poly.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) ));
  1.1591 +			}
  1.1592 +
  1.1593 +			if (ClipperLib::Orientation(poly)) {
  1.1594 +				std::reverse(poly.begin(), poly.end());
  1.1595 +			}
  1.1596 +			clipper_holes.Clear();
  1.1597 +			clipper_holes.AddPolygon(poly,ClipperLib::ptSubject);
  1.1598 +
  1.1599 +			clipper_holes.AddPolygons(holes_union,ClipperLib::ptClip);
  1.1600 +			clipper_holes.Execute(ClipperLib::ctDifference,clipped,
  1.1601 +				ClipperLib::pftNonZero,
  1.1602 +				ClipperLib::pftNonZero);
  1.1603 +		}
  1.1604 +
  1.1605 +	}
  1.1606 +	catch (const char* sx) {
  1.1607 +		IFCImporter::LogError("Ifc: error during polygon clipping, skipping openings for this face: (Clipper: " 
  1.1608 +			+ std::string(sx) + ")");
  1.1609 +
  1.1610 +		return false;
  1.1611 +	}
  1.1612 +
  1.1613 +	std::vector<IfcVector3> old_verts;
  1.1614 +	std::vector<unsigned int> old_vertcnt;
  1.1615 +
  1.1616 +	old_verts.swap(curmesh.verts);
  1.1617 +	old_vertcnt.swap(curmesh.vertcnt);
  1.1618 +
  1.1619 +
  1.1620 +	// add connection geometry to close the adjacent 'holes' for the openings
  1.1621 +	// this should only be done from one side of the wall or the polygons 
  1.1622 +	// would be emitted twice.
  1.1623 +	if (false && do_connections) {
  1.1624 +
  1.1625 +		std::vector<IfcVector3> tmpvec;
  1.1626 +		BOOST_FOREACH(ClipperLib::Polygon& opening, holes_union) {
  1.1627 +
  1.1628 +			assert(ClipperLib::Orientation(opening));
  1.1629 +
  1.1630 +			tmpvec.clear();
  1.1631 +
  1.1632 +			BOOST_FOREACH(ClipperLib::IntPoint& point, opening) {
  1.1633 +
  1.1634 +				tmpvec.push_back( minv * IfcVector3(
  1.1635 +					vmin.x + from_int64(point.X) * vmax.x, 
  1.1636 +					vmin.y + from_int64(point.Y) * vmax.y,
  1.1637 +					coord));
  1.1638 +			}
  1.1639 +
  1.1640 +			for(size_t i = 0, size = tmpvec.size(); i < size; ++i) {
  1.1641 +				const size_t next = (i+1)%size;
  1.1642 +
  1.1643 +				curmesh.vertcnt.push_back(4);
  1.1644 +
  1.1645 +				const IfcVector3& in_world = tmpvec[i];
  1.1646 +				const IfcVector3& next_world = tmpvec[next];
  1.1647 +
  1.1648 +				// Assumptions: no 'partial' openings, wall thickness roughly the same across the wall
  1.1649 +				curmesh.verts.push_back(in_world);
  1.1650 +				curmesh.verts.push_back(in_world+wall_extrusion);
  1.1651 +				curmesh.verts.push_back(next_world+wall_extrusion);
  1.1652 +				curmesh.verts.push_back(next_world);
  1.1653 +			}
  1.1654 +		}
  1.1655 +	}
  1.1656 +	
  1.1657 +	std::vector< std::vector<p2t::Point*> > contours;
  1.1658 +	BOOST_FOREACH(ClipperLib::ExPolygon& clip, clipped) {
  1.1659 +		
  1.1660 +		contours.clear();
  1.1661 +
  1.1662 +		// Build the outer polygon contour line for feeding into poly2tri
  1.1663 +		std::vector<p2t::Point*> contour_points;
  1.1664 +		BOOST_FOREACH(ClipperLib::IntPoint& point, clip.outer) {
  1.1665 +			contour_points.push_back( new p2t::Point(from_int64(point.X), from_int64(point.Y)) );
  1.1666 +		}
  1.1667 +
  1.1668 +		p2t::CDT* cdt ;
  1.1669 +		try {
  1.1670 +			// Note: this relies on custom modifications in poly2tri to raise runtime_error's
  1.1671 +			// instead if assertions. These failures are not debug only, they can actually
  1.1672 +			// happen in production use if the input data is broken. An assertion would be
  1.1673 +			// inappropriate.
  1.1674 +			cdt = new p2t::CDT(contour_points);
  1.1675 +		}
  1.1676 +		catch(const std::exception& e) {
  1.1677 +			IFCImporter::LogError("Ifc: error during polygon triangulation, skipping some openings: (poly2tri: " 
  1.1678 +				+ std::string(e.what()) + ")");
  1.1679 +			continue;
  1.1680 +		}
  1.1681 +		
  1.1682 +
  1.1683 +		// Build the poly2tri inner contours for all holes we got from ClipperLib
  1.1684 +		BOOST_FOREACH(ClipperLib::Polygon& opening, clip.holes) {
  1.1685 +			
  1.1686 +			contours.push_back(std::vector<p2t::Point*>());
  1.1687 +			std::vector<p2t::Point*>& contour = contours.back();
  1.1688 +
  1.1689 +			BOOST_FOREACH(ClipperLib::IntPoint& point, opening) {
  1.1690 +				contour.push_back( new p2t::Point(from_int64(point.X), from_int64(point.Y)) );
  1.1691 +			}
  1.1692 +
  1.1693 +			cdt->AddHole(contour);
  1.1694 +		}
  1.1695 +		
  1.1696 +		try {
  1.1697 +			// Note: See above
  1.1698 +			cdt->Triangulate();
  1.1699 +		}
  1.1700 +		catch(const std::exception& e) {
  1.1701 +			IFCImporter::LogError("Ifc: error during polygon triangulation, skipping some openings: (poly2tri: " 
  1.1702 +				+ std::string(e.what()) + ")");
  1.1703 +			continue;
  1.1704 +		}
  1.1705 +
  1.1706 +		const std::vector<p2t::Triangle*>& tris = cdt->GetTriangles();
  1.1707 +
  1.1708 +		// Collect the triangles we just produced
  1.1709 +		BOOST_FOREACH(p2t::Triangle* tri, tris) {
  1.1710 +			for(int i = 0; i < 3; ++i) {
  1.1711 +
  1.1712 +				const IfcVector2& v = IfcVector2( 
  1.1713 +					static_cast<IfcFloat>( tri->GetPoint(i)->x ), 
  1.1714 +					static_cast<IfcFloat>( tri->GetPoint(i)->y )
  1.1715 +				);
  1.1716 +
  1.1717 +				assert(v.x <= 1.0 && v.x >= 0.0 && v.y <= 1.0 && v.y >= 0.0);
  1.1718 +				const IfcVector3 v3 = minv * IfcVector3(vmin.x + v.x * vmax.x, vmin.y + v.y * vmax.y,coord) ; 
  1.1719 +
  1.1720 +				curmesh.verts.push_back(v3);
  1.1721 +			}
  1.1722 +			curmesh.vertcnt.push_back(3);
  1.1723 +		}
  1.1724 +
  1.1725 +		result = true;
  1.1726 +	}
  1.1727 +
  1.1728 +	if (!result) {
  1.1729 +		// revert -- it's a shame, but better than nothing
  1.1730 +		curmesh.verts.insert(curmesh.verts.end(),old_verts.begin(), old_verts.end());
  1.1731 +		curmesh.vertcnt.insert(curmesh.vertcnt.end(),old_vertcnt.begin(), old_vertcnt.end());
  1.1732 +
  1.1733 +		IFCImporter::LogError("Ifc: revert, could not generate openings for this wall");
  1.1734 +	}
  1.1735 +
  1.1736 +	return result;
  1.1737 +}
  1.1738 +
  1.1739 +
  1.1740 +	} // ! IFC
  1.1741 +} // ! Assimp
  1.1742 +
  1.1743 +#undef to_int64
  1.1744 +#undef from_int64
  1.1745 +#undef one_vec
  1.1746 +
  1.1747 +#endif 
  1.1748 \ No newline at end of file