Coverage Report

Created: 2025-12-05 06:25

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