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