1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4 
5 Copyright (c) 2006-2019, assimp team
6 
7 
8 All rights reserved.
9 
10 Redistribution and use of this software in source and binary forms,
11 with or without modification, are permitted provided that the
12 following conditions are met:
13 
14 * Redistributions of source code must retain the above
15   copyright notice, this list of conditions and the
16   following disclaimer.
17 
18 * Redistributions in binary form must reproduce the above
19   copyright notice, this list of conditions and the
20   following disclaimer in the documentation and/or other
21   materials provided with the distribution.
22 
23 * Neither the name of the assimp team, nor the names of its
24   contributors may be used to endorse or promote products
25   derived from this software without specific prior
26   written permission of the assimp team.
27 
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 
40 ----------------------------------------------------------------------
41 */
42 
43 /** @file  IFCUtil.cpp
44  *  @brief Implementation of conversion routines for some common Ifc helper entities.
45  */
46 
47 #ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
48 
49 #include "Importer/IFC/IFCUtil.h"
50 #include "Common/PolyTools.h"
51 #include "PostProcessing/ProcessHelper.h"
52 #include <assimp/Defines.h>
53 
54 namespace Assimp {
55 namespace IFC {
56 
57 // ------------------------------------------------------------------------------------------------
Transform(const IfcMatrix4 & mat)58 void TempOpening::Transform(const IfcMatrix4& mat) {
59     if(profileMesh) {
60         profileMesh->Transform(mat);
61     }
62     if(profileMesh2D) {
63         profileMesh2D->Transform(mat);
64     }
65     extrusionDir *= IfcMatrix3(mat);
66 }
67 
68 // ------------------------------------------------------------------------------------------------
ToMesh()69 aiMesh* TempMesh::ToMesh()
70 {
71     ai_assert(mVerts.size() == std::accumulate(mVertcnt.begin(),mVertcnt.end(),size_t(0)));
72 
73     if (mVerts.empty()) {
74         return NULL;
75     }
76 
77     std::unique_ptr<aiMesh> mesh(new aiMesh());
78 
79     // copy vertices
80     mesh->mNumVertices = static_cast<unsigned int>(mVerts.size());
81     mesh->mVertices = new aiVector3D[mesh->mNumVertices];
82     std::copy(mVerts.begin(),mVerts.end(),mesh->mVertices);
83 
84     // and build up faces
85     mesh->mNumFaces = static_cast<unsigned int>(mVertcnt.size());
86     mesh->mFaces = new aiFace[mesh->mNumFaces];
87 
88     for(unsigned int i = 0,n=0, acc = 0; i < mesh->mNumFaces; ++n) {
89         aiFace& f = mesh->mFaces[i];
90         if (!mVertcnt[n]) {
91             --mesh->mNumFaces;
92             continue;
93         }
94 
95         f.mNumIndices = mVertcnt[n];
96         f.mIndices = new unsigned int[f.mNumIndices];
97         for(unsigned int a = 0; a < f.mNumIndices; ++a) {
98             f.mIndices[a] = acc++;
99         }
100 
101         ++i;
102     }
103 
104     return mesh.release();
105 }
106 
107 // ------------------------------------------------------------------------------------------------
Clear()108 void TempMesh::Clear()
109 {
110     mVerts.clear();
111     mVertcnt.clear();
112 }
113 
114 // ------------------------------------------------------------------------------------------------
Transform(const IfcMatrix4 & mat)115 void TempMesh::Transform(const IfcMatrix4& mat)
116 {
117     for(IfcVector3& v : mVerts) {
118         v *= mat;
119     }
120 }
121 
122 // ------------------------------------------------------------------------------
Center() const123 IfcVector3 TempMesh::Center() const
124 {
125     return (mVerts.size() == 0) ? IfcVector3(0.0f, 0.0f, 0.0f) : (std::accumulate(mVerts.begin(),mVerts.end(),IfcVector3()) / static_cast<IfcFloat>(mVerts.size()));
126 }
127 
128 // ------------------------------------------------------------------------------------------------
Append(const TempMesh & other)129 void TempMesh::Append(const TempMesh& other)
130 {
131     mVerts.insert(mVerts.end(),other.mVerts.begin(),other.mVerts.end());
132     mVertcnt.insert(mVertcnt.end(),other.mVertcnt.begin(),other.mVertcnt.end());
133 }
134 
135 // ------------------------------------------------------------------------------------------------
RemoveDegenerates()136 void TempMesh::RemoveDegenerates()
137 {
138     // The strategy is simple: walk the mesh and compute normals using
139     // Newell's algorithm. The length of the normals gives the area
140     // of the polygons, which is close to zero for lines.
141 
142     std::vector<IfcVector3> normals;
143     ComputePolygonNormals(normals, false);
144 
145     bool drop = false;
146     size_t inor = 0;
147 
148     std::vector<IfcVector3>::iterator vit = mVerts.begin();
149     for (std::vector<unsigned int>::iterator it = mVertcnt.begin(); it != mVertcnt.end(); ++inor) {
150         const unsigned int pcount = *it;
151 
152         if (normals[inor].SquareLength() < 1e-10f) {
153             it = mVertcnt.erase(it);
154             vit = mVerts.erase(vit, vit + pcount);
155 
156             drop = true;
157             continue;
158         }
159 
160         vit += pcount;
161         ++it;
162     }
163 
164     if(drop) {
165         IFCImporter::LogDebug("removing degenerate faces");
166     }
167 }
168 
169 // ------------------------------------------------------------------------------------------------
ComputePolygonNormal(const IfcVector3 * vtcs,size_t cnt,bool normalize)170 IfcVector3 TempMesh::ComputePolygonNormal(const IfcVector3* vtcs, size_t cnt, bool normalize)
171 {
172     std::vector<IfcFloat> temp((cnt+2)*3);
173     for( size_t vofs = 0, i = 0; vofs < cnt; ++vofs )
174     {
175         const IfcVector3& v = vtcs[vofs];
176         temp[i++] = v.x;
177         temp[i++] = v.y;
178         temp[i++] = v.z;
179     }
180 
181     IfcVector3 nor;
182     NewellNormal<3, 3, 3>(nor, static_cast<int>(cnt), &temp[0], &temp[1], &temp[2]);
183     return normalize ? nor.Normalize() : nor;
184 }
185 
186 // ------------------------------------------------------------------------------------------------
ComputePolygonNormals(std::vector<IfcVector3> & normals,bool normalize,size_t ofs) const187 void TempMesh::ComputePolygonNormals(std::vector<IfcVector3>& normals,
188     bool normalize,
189     size_t ofs) const
190 {
191     size_t max_vcount = 0;
192     std::vector<unsigned int>::const_iterator begin = mVertcnt.begin()+ofs, end = mVertcnt.end(),  iit;
193     for(iit = begin; iit != end; ++iit) {
194         max_vcount = std::max(max_vcount,static_cast<size_t>(*iit));
195     }
196 
197     std::vector<IfcFloat> temp((max_vcount+2)*4);
198     normals.reserve( normals.size() + mVertcnt.size()-ofs );
199 
200     // `NewellNormal()` currently has a relatively strange interface and need to
201     // re-structure things a bit to meet them.
202     size_t vidx = std::accumulate(mVertcnt.begin(),begin,0);
203     for(iit = begin; iit != end; vidx += *iit++) {
204         if (!*iit) {
205             normals.push_back(IfcVector3());
206             continue;
207         }
208         for(size_t vofs = 0, cnt = 0; vofs < *iit; ++vofs) {
209             const IfcVector3& v = mVerts[vidx+vofs];
210             temp[cnt++] = v.x;
211             temp[cnt++] = v.y;
212             temp[cnt++] = v.z;
213 #ifdef ASSIMP_BUILD_DEBUG
214             temp[cnt] = std::numeric_limits<IfcFloat>::quiet_NaN();
215 #endif
216             ++cnt;
217         }
218 
219         normals.push_back(IfcVector3());
220         NewellNormal<4,4,4>(normals.back(),*iit,&temp[0],&temp[1],&temp[2]);
221     }
222 
223     if(normalize) {
224         for(IfcVector3& n : normals) {
225             n.Normalize();
226         }
227     }
228 }
229 
230 // ------------------------------------------------------------------------------------------------
231 // Compute the normal of the last polygon in the given mesh
ComputeLastPolygonNormal(bool normalize) const232 IfcVector3 TempMesh::ComputeLastPolygonNormal(bool normalize) const
233 {
234     return ComputePolygonNormal(&mVerts[mVerts.size() - mVertcnt.back()], mVertcnt.back(), normalize);
235 }
236 
237 struct CompareVector
238 {
operator ()Assimp::IFC::CompareVector239     bool operator () (const IfcVector3& a, const IfcVector3& b) const
240     {
241         IfcVector3 d = a - b;
242         IfcFloat eps = 1e-6;
243         return d.x < -eps || (std::abs(d.x) < eps && d.y < -eps) || (std::abs(d.x) < eps && std::abs(d.y) < eps && d.z < -eps);
244     }
245 };
246 struct FindVector
247 {
248     IfcVector3 v;
FindVectorAssimp::IFC::FindVector249     FindVector(const IfcVector3& p) : v(p) { }
operator ()Assimp::IFC::FindVector250     bool operator () (const IfcVector3& p) { return FuzzyVectorCompare(1e-6)(p, v); }
251 };
252 
253 // ------------------------------------------------------------------------------------------------
FixupFaceOrientation()254 void TempMesh::FixupFaceOrientation()
255 {
256     const IfcVector3 vavg = Center();
257 
258     // create a list of start indices for all faces to allow random access to faces
259     std::vector<size_t> faceStartIndices(mVertcnt.size());
260     for( size_t i = 0, a = 0; a < mVertcnt.size(); i += mVertcnt[a], ++a )
261         faceStartIndices[a] = i;
262 
263     // list all faces on a vertex
264     std::map<IfcVector3, std::vector<size_t>, CompareVector> facesByVertex;
265     for( size_t a = 0; a < mVertcnt.size(); ++a )
266     {
267         for( size_t b = 0; b < mVertcnt[a]; ++b )
268             facesByVertex[mVerts[faceStartIndices[a] + b]].push_back(a);
269     }
270     // determine neighbourhood for all polys
271     std::vector<size_t> neighbour(mVerts.size(), SIZE_MAX);
272     std::vector<size_t> tempIntersect(10);
273     for( size_t a = 0; a < mVertcnt.size(); ++a )
274     {
275         for( size_t b = 0; b < mVertcnt[a]; ++b )
276         {
277             size_t ib = faceStartIndices[a] + b, nib = faceStartIndices[a] + (b + 1) % mVertcnt[a];
278             const std::vector<size_t>& facesOnB = facesByVertex[mVerts[ib]];
279             const std::vector<size_t>& facesOnNB = facesByVertex[mVerts[nib]];
280             // there should be exactly one or two faces which appear in both lists. Our face and the other side
281             std::vector<size_t>::iterator sectstart = tempIntersect.begin();
282             std::vector<size_t>::iterator sectend = std::set_intersection(
283                 facesOnB.begin(), facesOnB.end(), facesOnNB.begin(), facesOnNB.end(), sectstart);
284 
285             if( std::distance(sectstart, sectend) != 2 )
286                 continue;
287             if( *sectstart == a )
288                 ++sectstart;
289             neighbour[ib] = *sectstart;
290         }
291     }
292 
293     // now we're getting started. We take the face which is the farthest away from the center. This face is most probably
294     // facing outwards. So we reverse this face to point outwards in relation to the center. Then we adapt neighbouring
295     // faces to have the same winding until all faces have been tested.
296     std::vector<bool> faceDone(mVertcnt.size(), false);
297     while( std::count(faceDone.begin(), faceDone.end(), false) != 0 )
298     {
299         // find the farthest of the remaining faces
300         size_t farthestIndex = SIZE_MAX;
301         IfcFloat farthestDistance = -1.0;
302         for( size_t a = 0; a < mVertcnt.size(); ++a )
303         {
304             if( faceDone[a] )
305                 continue;
306             IfcVector3 faceCenter = std::accumulate(mVerts.begin() + faceStartIndices[a],
307                 mVerts.begin() + faceStartIndices[a] + mVertcnt[a], IfcVector3(0.0)) / IfcFloat(mVertcnt[a]);
308             IfcFloat dst = (faceCenter - vavg).SquareLength();
309             if( dst > farthestDistance ) { farthestDistance = dst; farthestIndex = a; }
310         }
311 
312         // calculate its normal and reverse the poly if its facing towards the mesh center
313         IfcVector3 farthestNormal = ComputePolygonNormal(mVerts.data() + faceStartIndices[farthestIndex], mVertcnt[farthestIndex]);
314         IfcVector3 farthestCenter = std::accumulate(mVerts.begin() + faceStartIndices[farthestIndex],
315             mVerts.begin() + faceStartIndices[farthestIndex] + mVertcnt[farthestIndex], IfcVector3(0.0))
316             / IfcFloat(mVertcnt[farthestIndex]);
317         // We accept a bit of negative orientation without reversing. In case of doubt, prefer the orientation given in
318         // the file.
319         if( (farthestNormal * (farthestCenter - vavg).Normalize()) < -0.4 )
320         {
321             size_t fsi = faceStartIndices[farthestIndex], fvc = mVertcnt[farthestIndex];
322             std::reverse(mVerts.begin() + fsi, mVerts.begin() + fsi + fvc);
323             std::reverse(neighbour.begin() + fsi, neighbour.begin() + fsi + fvc);
324             // because of the neighbour index belonging to the edge starting with the point at the same index, we need to
325             // cycle the neighbours through to match the edges again.
326             // Before: points A - B - C - D with edge neighbour p - q - r - s
327             // After: points D - C - B - A, reversed neighbours are s - r - q - p, but the should be
328             //                r   q   p   s
329             for( size_t a = 0; a < fvc - 1; ++a )
330                 std::swap(neighbour[fsi + a], neighbour[fsi + a + 1]);
331         }
332         faceDone[farthestIndex] = true;
333         std::vector<size_t> todo;
334         todo.push_back(farthestIndex);
335 
336         // go over its neighbour faces recursively and adapt their winding order to match the farthest face
337         while( !todo.empty() )
338         {
339             size_t tdf = todo.back();
340             size_t vsi = faceStartIndices[tdf], vc = mVertcnt[tdf];
341             todo.pop_back();
342 
343             // check its neighbours
344             for( size_t a = 0; a < vc; ++a )
345             {
346                 // ignore neighbours if we already checked them
347                 size_t nbi = neighbour[vsi + a];
348                 if( nbi == SIZE_MAX || faceDone[nbi] )
349                     continue;
350 
351                 const IfcVector3& vp = mVerts[vsi + a];
352                 size_t nbvsi = faceStartIndices[nbi], nbvc = mVertcnt[nbi];
353                 std::vector<IfcVector3>::iterator it = std::find_if(mVerts.begin() + nbvsi, mVerts.begin() + nbvsi + nbvc, FindVector(vp));
354                 ai_assert(it != mVerts.begin() + nbvsi + nbvc);
355                 size_t nb_vidx = std::distance(mVerts.begin() + nbvsi, it);
356                 // two faces winded in the same direction should have a crossed edge, where one face has p0->p1 and the other
357                 // has p1'->p0'. If the next point on the neighbouring face is also the next on the current face, we need
358                 // to reverse the neighbour
359                 nb_vidx = (nb_vidx + 1) % nbvc;
360                 size_t oursideidx = (a + 1) % vc;
361                 if( FuzzyVectorCompare(1e-6)(mVerts[vsi + oursideidx], mVerts[nbvsi + nb_vidx]) )
362                 {
363                     std::reverse(mVerts.begin() + nbvsi, mVerts.begin() + nbvsi + nbvc);
364                     std::reverse(neighbour.begin() + nbvsi, neighbour.begin() + nbvsi + nbvc);
365                     for( size_t a = 0; a < nbvc - 1; ++a )
366                         std::swap(neighbour[nbvsi + a], neighbour[nbvsi + a + 1]);
367                 }
368 
369                 // either way we're done with the neighbour. Mark it as done and continue checking from there recursively
370                 faceDone[nbi] = true;
371                 todo.push_back(nbi);
372             }
373         }
374 
375         // no more faces reachable from this part of the surface, start over with a disjunct part and its farthest face
376     }
377 }
378 
379 // ------------------------------------------------------------------------------------------------
RemoveAdjacentDuplicates()380 void TempMesh::RemoveAdjacentDuplicates() {
381     bool drop = false;
382     std::vector<IfcVector3>::iterator base = mVerts.begin();
383     for(unsigned int& cnt : mVertcnt) {
384         if (cnt < 2){
385             base += cnt;
386             continue;
387         }
388 
389         IfcVector3 vmin,vmax;
390         ArrayBounds(&*base, cnt ,vmin,vmax);
391 
392 
393         const IfcFloat epsilon = (vmax-vmin).SquareLength() / static_cast<IfcFloat>(1e9);
394         //const IfcFloat dotepsilon = 1e-9;
395 
396         //// look for vertices that lie directly on the line between their predecessor and their
397         //// successor and replace them with either of them.
398 
399         //for(size_t i = 0; i < cnt; ++i) {
400         //  IfcVector3& v1 = *(base+i), &v0 = *(base+(i?i-1:cnt-1)), &v2 = *(base+(i+1)%cnt);
401         //  const IfcVector3& d0 = (v1-v0), &d1 = (v2-v1);
402         //  const IfcFloat l0 = d0.SquareLength(), l1 = d1.SquareLength();
403         //  if (!l0 || !l1) {
404         //      continue;
405         //  }
406 
407         //  const IfcFloat d = (d0/std::sqrt(l0))*(d1/std::sqrt(l1));
408 
409         //  if ( d >= 1.f-dotepsilon ) {
410         //      v1 = v0;
411         //  }
412         //  else if ( d < -1.f+dotepsilon ) {
413         //      v2 = v1;
414         //      continue;
415         //  }
416         //}
417 
418         // drop any identical, adjacent vertices. this pass will collect the dropouts
419         // of the previous pass as a side-effect.
420         FuzzyVectorCompare fz(epsilon);
421         std::vector<IfcVector3>::iterator end = base+cnt, e = std::unique( base, end, fz );
422         if (e != end) {
423             cnt -= static_cast<unsigned int>(std::distance(e, end));
424             mVerts.erase(e,end);
425             drop  = true;
426         }
427 
428         // check front and back vertices for this polygon
429         if (cnt > 1 && fz(*base,*(base+cnt-1))) {
430             mVerts.erase(base+ --cnt);
431             drop  = true;
432         }
433 
434         // removing adjacent duplicates shouldn't erase everything :-)
435         ai_assert(cnt>0);
436         base += cnt;
437     }
438     if(drop) {
439         IFCImporter::LogDebug("removing duplicate vertices");
440     }
441 }
442 
443 // ------------------------------------------------------------------------------------------------
Swap(TempMesh & other)444 void TempMesh::Swap(TempMesh& other)
445 {
446     mVertcnt.swap(other.mVertcnt);
447     mVerts.swap(other.mVerts);
448 }
449 
450 // ------------------------------------------------------------------------------------------------
IsTrue(const::Assimp::STEP::EXPRESS::BOOLEAN & in)451 bool IsTrue(const ::Assimp::STEP::EXPRESS::BOOLEAN& in)
452 {
453     return (std::string)in == "TRUE" || (std::string)in == "T";
454 }
455 
456 // ------------------------------------------------------------------------------------------------
ConvertSIPrefix(const std::string & prefix)457 IfcFloat ConvertSIPrefix(const std::string& prefix)
458 {
459     if (prefix == "EXA") {
460         return 1e18f;
461     }
462     else if (prefix == "PETA") {
463         return 1e15f;
464     }
465     else if (prefix == "TERA") {
466         return 1e12f;
467     }
468     else if (prefix == "GIGA") {
469         return 1e9f;
470     }
471     else if (prefix == "MEGA") {
472         return 1e6f;
473     }
474     else if (prefix == "KILO") {
475         return 1e3f;
476     }
477     else if (prefix == "HECTO") {
478         return 1e2f;
479     }
480     else if (prefix == "DECA") {
481         return 1e-0f;
482     }
483     else if (prefix == "DECI") {
484         return 1e-1f;
485     }
486     else if (prefix == "CENTI") {
487         return 1e-2f;
488     }
489     else if (prefix == "MILLI") {
490         return 1e-3f;
491     }
492     else if (prefix == "MICRO") {
493         return 1e-6f;
494     }
495     else if (prefix == "NANO") {
496         return 1e-9f;
497     }
498     else if (prefix == "PICO") {
499         return 1e-12f;
500     }
501     else if (prefix == "FEMTO") {
502         return 1e-15f;
503     }
504     else if (prefix == "ATTO") {
505         return 1e-18f;
506     }
507     else {
508         IFCImporter::LogError("Unrecognized SI prefix: " + prefix);
509         return 1;
510     }
511 }
512 
513 // ------------------------------------------------------------------------------------------------
ConvertColor(aiColor4D & out,const Schema_2x3::IfcColourRgb & in)514 void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourRgb& in)
515 {
516     out.r = static_cast<float>( in.Red );
517     out.g = static_cast<float>( in.Green );
518     out.b = static_cast<float>( in.Blue );
519     out.a = static_cast<float>( 1.f );
520 }
521 
522 // ------------------------------------------------------------------------------------------------
ConvertColor(aiColor4D & out,const Schema_2x3::IfcColourOrFactor & in,ConversionData & conv,const aiColor4D * base)523 void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourOrFactor& in,ConversionData& conv,const aiColor4D* base)
524 {
525     if (const ::Assimp::STEP::EXPRESS::REAL* const r = in.ToPtr<::Assimp::STEP::EXPRESS::REAL>()) {
526         out.r = out.g = out.b = static_cast<float>(*r);
527         if(base) {
528             out.r *= static_cast<float>( base->r );
529             out.g *= static_cast<float>( base->g );
530             out.b *= static_cast<float>( base->b );
531             out.a = static_cast<float>( base->a );
532         }
533         else out.a = 1.0;
534     }
535     else if (const Schema_2x3::IfcColourRgb* const rgb = in.ResolveSelectPtr<Schema_2x3::IfcColourRgb>(conv.db)) {
536         ConvertColor(out,*rgb);
537     }
538     else {
539         IFCImporter::LogWarn("skipping unknown IfcColourOrFactor entity");
540     }
541 }
542 
543 // ------------------------------------------------------------------------------------------------
ConvertCartesianPoint(IfcVector3 & out,const Schema_2x3::IfcCartesianPoint & in)544 void ConvertCartesianPoint(IfcVector3& out, const Schema_2x3::IfcCartesianPoint& in)
545 {
546     out = IfcVector3();
547     for(size_t i = 0; i < in.Coordinates.size(); ++i) {
548         out[static_cast<unsigned int>(i)] = in.Coordinates[i];
549     }
550 }
551 
552 // ------------------------------------------------------------------------------------------------
ConvertVector(IfcVector3 & out,const Schema_2x3::IfcVector & in)553 void ConvertVector(IfcVector3& out, const Schema_2x3::IfcVector& in)
554 {
555     ConvertDirection(out,in.Orientation);
556     out *= in.Magnitude;
557 }
558 
559 // ------------------------------------------------------------------------------------------------
ConvertDirection(IfcVector3 & out,const Schema_2x3::IfcDirection & in)560 void ConvertDirection(IfcVector3& out, const Schema_2x3::IfcDirection& in)
561 {
562     out = IfcVector3();
563     for(size_t i = 0; i < in.DirectionRatios.size(); ++i) {
564         out[static_cast<unsigned int>(i)] = in.DirectionRatios[i];
565     }
566     const IfcFloat len = out.Length();
567     if (len<1e-6) {
568         IFCImporter::LogWarn("direction vector magnitude too small, normalization would result in a division by zero");
569         return;
570     }
571     out /= len;
572 }
573 
574 // ------------------------------------------------------------------------------------------------
AssignMatrixAxes(IfcMatrix4 & out,const IfcVector3 & x,const IfcVector3 & y,const IfcVector3 & z)575 void AssignMatrixAxes(IfcMatrix4& out, const IfcVector3& x, const IfcVector3& y, const IfcVector3& z)
576 {
577     out.a1 = x.x;
578     out.b1 = x.y;
579     out.c1 = x.z;
580 
581     out.a2 = y.x;
582     out.b2 = y.y;
583     out.c2 = y.z;
584 
585     out.a3 = z.x;
586     out.b3 = z.y;
587     out.c3 = z.z;
588 }
589 
590 // ------------------------------------------------------------------------------------------------
ConvertAxisPlacement(IfcMatrix4 & out,const Schema_2x3::IfcAxis2Placement3D & in)591 void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement3D& in)
592 {
593     IfcVector3 loc;
594     ConvertCartesianPoint(loc,in.Location);
595 
596     IfcVector3 z(0.f,0.f,1.f),r(1.f,0.f,0.f),x;
597 
598     if (in.Axis) {
599         ConvertDirection(z,*in.Axis.Get());
600     }
601     if (in.RefDirection) {
602         ConvertDirection(r,*in.RefDirection.Get());
603     }
604 
605     IfcVector3 v = r.Normalize();
606     IfcVector3 tmpx = z * (v*z);
607 
608     x = (v-tmpx).Normalize();
609     IfcVector3 y = (z^x);
610 
611     IfcMatrix4::Translation(loc,out);
612     AssignMatrixAxes(out,x,y,z);
613 }
614 
615 // ------------------------------------------------------------------------------------------------
ConvertAxisPlacement(IfcMatrix4 & out,const Schema_2x3::IfcAxis2Placement2D & in)616 void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement2D& in)
617 {
618     IfcVector3 loc;
619     ConvertCartesianPoint(loc,in.Location);
620 
621     IfcVector3 x(1.f,0.f,0.f);
622     if (in.RefDirection) {
623         ConvertDirection(x,*in.RefDirection.Get());
624     }
625 
626     const IfcVector3 y = IfcVector3(x.y,-x.x,0.f);
627 
628     IfcMatrix4::Translation(loc,out);
629     AssignMatrixAxes(out,x,y,IfcVector3(0.f,0.f,1.f));
630 }
631 
632 // ------------------------------------------------------------------------------------------------
ConvertAxisPlacement(IfcVector3 & axis,IfcVector3 & pos,const Schema_2x3::IfcAxis1Placement & in)633 void ConvertAxisPlacement(IfcVector3& axis, IfcVector3& pos, const Schema_2x3::IfcAxis1Placement& in)
634 {
635     ConvertCartesianPoint(pos,in.Location);
636     if (in.Axis) {
637         ConvertDirection(axis,in.Axis.Get());
638     }
639     else {
640         axis = IfcVector3(0.f,0.f,1.f);
641     }
642 }
643 
644 // ------------------------------------------------------------------------------------------------
ConvertAxisPlacement(IfcMatrix4 & out,const Schema_2x3::IfcAxis2Placement & in,ConversionData & conv)645 void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement& in, ConversionData& conv)
646 {
647     if(const Schema_2x3::IfcAxis2Placement3D* pl3 = in.ResolveSelectPtr<Schema_2x3::IfcAxis2Placement3D>(conv.db)) {
648         ConvertAxisPlacement(out,*pl3);
649     }
650     else if(const Schema_2x3::IfcAxis2Placement2D* pl2 = in.ResolveSelectPtr<Schema_2x3::IfcAxis2Placement2D>(conv.db)) {
651         ConvertAxisPlacement(out,*pl2);
652     }
653     else {
654         IFCImporter::LogWarn("skipping unknown IfcAxis2Placement entity");
655     }
656 }
657 
658 // ------------------------------------------------------------------------------------------------
ConvertTransformOperator(IfcMatrix4 & out,const Schema_2x3::IfcCartesianTransformationOperator & op)659 void ConvertTransformOperator(IfcMatrix4& out, const Schema_2x3::IfcCartesianTransformationOperator& op)
660 {
661     IfcVector3 loc;
662     ConvertCartesianPoint(loc,op.LocalOrigin);
663 
664     IfcVector3 x(1.f,0.f,0.f),y(0.f,1.f,0.f),z(0.f,0.f,1.f);
665     if (op.Axis1) {
666         ConvertDirection(x,*op.Axis1.Get());
667     }
668     if (op.Axis2) {
669         ConvertDirection(y,*op.Axis2.Get());
670     }
671     if (const Schema_2x3::IfcCartesianTransformationOperator3D* op2 = op.ToPtr<Schema_2x3::IfcCartesianTransformationOperator3D>()) {
672         if(op2->Axis3) {
673             ConvertDirection(z,*op2->Axis3.Get());
674         }
675     }
676 
677     IfcMatrix4 locm;
678     IfcMatrix4::Translation(loc,locm);
679     AssignMatrixAxes(out,x,y,z);
680 
681 
682     IfcVector3 vscale;
683     if (const Schema_2x3::IfcCartesianTransformationOperator3DnonUniform* nuni = op.ToPtr<Schema_2x3::IfcCartesianTransformationOperator3DnonUniform>()) {
684         vscale.x = nuni->Scale?op.Scale.Get():1.f;
685         vscale.y = nuni->Scale2?nuni->Scale2.Get():1.f;
686         vscale.z = nuni->Scale3?nuni->Scale3.Get():1.f;
687     }
688     else {
689         const IfcFloat sc = op.Scale?op.Scale.Get():1.f;
690         vscale = IfcVector3(sc,sc,sc);
691     }
692 
693     IfcMatrix4 s;
694     IfcMatrix4::Scaling(vscale,s);
695 
696     out = locm * out * s;
697 }
698 
699 
700 } // ! IFC
701 } // ! Assimp
702 
703 #endif
704