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