Coverage Report

Created: 2025-06-22 07:30

/src/assimp/code/AssetLib/IFC/IFCUtil.h
Line
Count
Source (jump to first uncovered line)
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  IFC.cpp
43
 *  @brief Implementation of the Industry Foundation Classes loader.
44
 */
45
46
#ifndef INCLUDED_IFCUTIL_H
47
#define INCLUDED_IFCUTIL_H
48
49
#include "IFCReaderGen_2x3.h"
50
#include "IFCLoader.h"
51
#include "AssetLib/Step/STEPFile.h"
52
53
#include <assimp/mesh.h>
54
#include <assimp/material.h>
55
56
#include <utility>
57
58
struct aiNode;
59
60
namespace Assimp {
61
namespace IFC {
62
63
    typedef double IfcFloat;
64
65
    // IfcFloat-precision math data types
66
    typedef aiVector2t<IfcFloat> IfcVector2;
67
    typedef aiVector3t<IfcFloat> IfcVector3;
68
    typedef aiMatrix4x4t<IfcFloat> IfcMatrix4;
69
    typedef aiMatrix3x3t<IfcFloat> IfcMatrix3;
70
    typedef aiColor4t<IfcFloat> IfcColor4;
71
72
73
// ------------------------------------------------------------------------------------------------
74
// Helper for std::for_each to delete all heap-allocated items in a container
75
// ------------------------------------------------------------------------------------------------
76
template<typename T>
77
struct delete_fun {
78
0
    void operator()(T* del) {
79
0
        delete del;
80
0
    }
Unexecuted instantiation: Assimp::IFC::delete_fun<aiMesh>::operator()(aiMesh*)
Unexecuted instantiation: Assimp::IFC::delete_fun<aiMaterial>::operator()(aiMaterial*)
Unexecuted instantiation: Assimp::IFC::delete_fun<aiNode>::operator()(aiNode*)
81
};
82
83
84
85
// ------------------------------------------------------------------------------------------------
86
// Helper used during mesh construction. Aids at creating aiMesh'es out of relatively few polygons.
87
// ------------------------------------------------------------------------------------------------
88
struct TempMesh {
89
    std::vector<IfcVector3> mVerts;
90
    std::vector<unsigned int> mVertcnt;
91
92
    // utilities
93
    aiMesh* ToMesh();
94
    void Clear();
95
    void Transform(const IfcMatrix4& mat);
96
    IfcVector3 Center() const;
97
    void Append(const TempMesh& other);
98
    bool IsEmpty() const;
99
    void RemoveAdjacentDuplicates();
100
    void RemoveDegenerates();
101
    void FixupFaceOrientation();
102
    static IfcVector3 ComputePolygonNormal(const IfcVector3* vtcs, size_t cnt, bool normalize = true);
103
    IfcVector3 ComputeLastPolygonNormal(bool normalize = true) const;
104
    void ComputePolygonNormals(std::vector<IfcVector3>& normals, bool normalize = true, size_t ofs = 0) const;
105
    void Swap(TempMesh& other);
106
};
107
108
inline
109
0
bool TempMesh::IsEmpty() const {
110
0
    return mVerts.empty() && mVertcnt.empty();
111
0
}
112
113
114
// ------------------------------------------------------------------------------------------------
115
// Temporary representation of an opening in a wall or a floor
116
// ------------------------------------------------------------------------------------------------
117
struct TempOpening
118
{
119
    const IFC::Schema_2x3::IfcSolidModel *solid;
120
    IfcVector3 extrusionDir;
121
122
    std::shared_ptr<TempMesh> profileMesh;
123
    std::shared_ptr<TempMesh> profileMesh2D;
124
125
    // list of points generated for this opening. This is used to
126
    // create connections between two opposing holes created
127
    // from a single opening instance (two because walls tend to
128
    // have two sides). If !empty(), the other side of the wall
129
    // has already been processed.
130
    std::vector<IfcVector3> wallPoints;
131
132
    // ------------------------------------------------------------------------------
133
    TempOpening()
134
        : solid()
135
0
        , extrusionDir()
136
0
        , profileMesh()
137
0
    {
138
0
    }
139
140
    // ------------------------------------------------------------------------------
141
    TempOpening(const IFC::Schema_2x3::IfcSolidModel *solid, IfcVector3 extrusionDir,
142
            std::shared_ptr<TempMesh> profileMesh,
143
            std::shared_ptr<TempMesh> profileMesh2D) :
144
0
            solid(solid), extrusionDir(extrusionDir), profileMesh(std::move(profileMesh)), profileMesh2D(std::move(profileMesh2D)) {
145
0
    }
146
147
    // ------------------------------------------------------------------------------
148
    void Transform(const IfcMatrix4& mat); // defined later since TempMesh is not complete yet
149
150
151
152
    // ------------------------------------------------------------------------------
153
    // Helper to sort openings by distance from a given base point
154
    struct DistanceSorter {
155
156
0
        DistanceSorter(const IfcVector3& base) : base(base) {}
157
158
0
        bool operator () (const TempOpening& a, const TempOpening& b) const {
159
0
            return (a.profileMesh->Center()-base).SquareLength() < (b.profileMesh->Center()-base).SquareLength();
160
0
        }
161
162
        IfcVector3 base;
163
    };
164
};
165
166
167
// ------------------------------------------------------------------------------------------------
168
// Intermediate data storage during conversion. Keeps everything and a bit more.
169
// ------------------------------------------------------------------------------------------------
170
struct ConversionData
171
{
172
    ConversionData(const STEP::DB& db, const IFC::Schema_2x3::IfcProject& proj, aiScene* out,const IFCImporter::Settings& settings)
173
0
        : len_scale(1.0)
174
0
        , angle_scale(-1.0)
175
0
        , db(db)
176
0
        , proj(proj)
177
0
        , out(out)
178
0
        , settings(settings)
179
        , apply_openings()
180
        , collect_openings()
181
0
    {}
182
183
0
    ~ConversionData() {
184
0
        std::for_each(meshes.begin(),meshes.end(),delete_fun<aiMesh>());
185
0
        std::for_each(materials.begin(),materials.end(),delete_fun<aiMaterial>());
186
0
    }
187
188
    IfcFloat len_scale, angle_scale;
189
    bool plane_angle_in_radians;
190
191
    const STEP::DB& db;
192
    const IFC::Schema_2x3::IfcProject& proj;
193
    aiScene* out;
194
195
    IfcMatrix4 wcs;
196
    std::vector<aiMesh*> meshes;
197
    std::vector<aiMaterial*> materials;
198
199
    struct MeshCacheIndex {
200
        const IFC::Schema_2x3::IfcRepresentationItem* item; unsigned int matindex;
201
0
        MeshCacheIndex() : item(nullptr), matindex(0) { }
202
0
        MeshCacheIndex(const IFC::Schema_2x3::IfcRepresentationItem* i, unsigned int mi) : item(i), matindex(mi) { }
203
0
        bool operator == (const MeshCacheIndex& o) const { return item == o.item && matindex == o.matindex; }
204
0
        bool operator < (const MeshCacheIndex& o) const { return item < o.item || (item == o.item && matindex < o.matindex); }
205
    };
206
    typedef std::map<MeshCacheIndex, std::set<unsigned int> > MeshCache;
207
    MeshCache cached_meshes;
208
209
    typedef std::map<const IFC::Schema_2x3::IfcSurfaceStyle*, unsigned int> MaterialCache;
210
    MaterialCache cached_materials;
211
212
    const IFCImporter::Settings& settings;
213
214
    // Intermediate arrays used to resolve openings in walls: only one of them
215
    // can be given at a time. apply_openings if present if the current element
216
    // is a wall and needs its openings to be poured into its geometry while
217
    // collect_openings is present only if the current element is an
218
    // IfcOpeningElement, for which all the geometry needs to be preserved
219
    // for later processing by a parent, which is a wall.
220
    std::vector<TempOpening>* apply_openings;
221
    std::vector<TempOpening>* collect_openings;
222
223
    std::set<uint64_t> already_processed;
224
};
225
226
227
// ------------------------------------------------------------------------------------------------
228
// Binary predicate to compare vectors with a given, quadratic epsilon.
229
// ------------------------------------------------------------------------------------------------
230
struct FuzzyVectorCompare {
231
232
0
    FuzzyVectorCompare(IfcFloat epsilon) : epsilon(epsilon) {}
233
0
    bool operator()(const IfcVector3& a, const IfcVector3& b) {
234
0
        return std::abs((a-b).SquareLength()) < epsilon;
235
0
    }
236
237
    const IfcFloat epsilon;
238
};
239
240
241
// ------------------------------------------------------------------------------------------------
242
// Ordering predicate to totally order R^2 vectors first by x and then by y
243
// ------------------------------------------------------------------------------------------------
244
struct XYSorter {
245
246
    // sort first by X coordinates, then by Y coordinates
247
0
    bool operator () (const IfcVector2&a, const IfcVector2& b) const {
248
0
        if (a.x == b.x) {
249
0
            return a.y < b.y;
250
0
        }
251
0
        return a.x < b.x;
252
0
    }
253
};
254
255
256
257
// conversion routines for common IFC entities, implemented in IFCUtil.cpp
258
void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourRgb& in);
259
void ConvertColor(aiColor4D& out, const Schema_2x3::IfcColourOrFactor& in,ConversionData& conv,const aiColor4D* base);
260
void ConvertCartesianPoint(IfcVector3& out, const Schema_2x3::IfcCartesianPoint& in);
261
void ConvertDirection(IfcVector3& out, const Schema_2x3::IfcDirection& in);
262
void ConvertVector(IfcVector3& out, const Schema_2x3::IfcVector& in);
263
void AssignMatrixAxes(IfcMatrix4& out, const IfcVector3& x, const IfcVector3& y, const IfcVector3& z);
264
void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement3D& in);
265
void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement2D& in);
266
void ConvertAxisPlacement(IfcVector3& axis, IfcVector3& pos, const IFC::Schema_2x3::IfcAxis1Placement& in);
267
void ConvertAxisPlacement(IfcMatrix4& out, const Schema_2x3::IfcAxis2Placement& in, ConversionData& conv);
268
void ConvertTransformOperator(IfcMatrix4& out, const Schema_2x3::IfcCartesianTransformationOperator& op);
269
bool IsTrue(const Assimp::STEP::EXPRESS::BOOLEAN& in);
270
IfcFloat ConvertSIPrefix(const std::string& prefix);
271
272
273
// IFCProfile.cpp
274
bool ProcessProfile(const Schema_2x3::IfcProfileDef& prof, TempMesh& meshout, ConversionData& conv);
275
bool ProcessCurve(const Schema_2x3::IfcCurve& curve,  TempMesh& meshout, ConversionData& conv);
276
277
// IFCMaterial.cpp
278
unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionData& conv, bool forceDefaultMat);
279
280
// IFCGeometry.cpp
281
IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVector3& norOut);
282
bool ProcessRepresentationItem(const Schema_2x3::IfcRepresentationItem& item, unsigned int matid, std::set<unsigned int>& mesh_indices, ConversionData& conv);
283
void AssignAddedMeshes(std::set<unsigned int>& mesh_indices,aiNode* nd,ConversionData& /*conv*/);
284
285
void ProcessSweptAreaSolid(const Schema_2x3::IfcSweptAreaSolid& swept, TempMesh& meshout,
286
                           ConversionData& conv);
287
288
void ProcessExtrudedAreaSolid(const Schema_2x3::IfcExtrudedAreaSolid& solid, TempMesh& result,
289
                              ConversionData& conv, bool collect_openings);
290
291
// IFCBoolean.cpp
292
293
void ProcessBoolean(const Schema_2x3::IfcBooleanResult& boolean, TempMesh& result, ConversionData& conv);
294
void ProcessBooleanHalfSpaceDifference(const Schema_2x3::IfcHalfSpaceSolid* hs, TempMesh& result,
295
                                       const TempMesh& first_operand,
296
                                       ConversionData& conv);
297
298
void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const Schema_2x3::IfcPolygonalBoundedHalfSpace* hs, TempMesh& result,
299
                                                       const TempMesh& first_operand,
300
                                                       ConversionData& conv);
301
void ProcessBooleanExtrudedAreaSolidDifference(const Schema_2x3::IfcExtrudedAreaSolid* as, TempMesh& result,
302
                                               const TempMesh& first_operand,
303
                                               ConversionData& conv);
304
305
306
// IFCOpenings.cpp
307
308
bool GenerateOpenings(std::vector<TempOpening>& openings,
309
                      TempMesh& curmesh,
310
                      bool check_intersection,
311
                      bool generate_connection_geometry,
312
                      const IfcVector3& wall_extrusion_axis = IfcVector3(0,1,0));
313
314
315
316
// IFCCurve.cpp
317
318
// ------------------------------------------------------------------------------------------------
319
// Custom exception for use by members of the Curve class
320
// ------------------------------------------------------------------------------------------------
321
class CurveError {
322
public:
323
    CurveError(const std::string& s)
324
0
    : mStr(s) {
325
        // empty
326
0
    }
327
328
    std::string mStr;
329
};
330
331
// ------------------------------------------------------------------------------------------------
332
// Temporary representation for an arbitrary sub-class of IfcCurve. Used to sample the curves
333
// to obtain a list of line segments.
334
// ------------------------------------------------------------------------------------------------
335
class Curve {
336
protected:
337
    Curve(const Schema_2x3::IfcCurve& base_entity, ConversionData& conv)
338
0
    : base_entity(base_entity)
339
0
    , conv(conv) {
340
        // empty
341
0
    }
342
343
public:
344
    typedef std::pair<IfcFloat, IfcFloat> ParamRange;
345
346
0
    virtual ~Curve() = default;
347
348
    // check if a curve is closed
349
    virtual bool IsClosed() const = 0;
350
351
    // evaluate the curve at the given parametric position
352
    virtual IfcVector3 Eval(IfcFloat p) const = 0;
353
354
    // try to match a point on the curve to a given parameter
355
    // for self-intersecting curves, the result is not ambiguous and
356
    // it is undefined which parameter is returned.
357
    virtual bool ReverseEval(const IfcVector3& val, IfcFloat& paramOut) const;
358
359
    // get the range of the curve (both inclusive).
360
    // +inf and -inf are valid return values, the curve is not bounded in such a case.
361
    virtual std::pair<IfcFloat,IfcFloat> GetParametricRange() const = 0;
362
    IfcFloat GetParametricRangeDelta() const;
363
364
    // estimate the number of sample points that this curve will require
365
    virtual size_t EstimateSampleCount(IfcFloat start,IfcFloat end) const;
366
367
    // intelligently sample the curve based on the current settings
368
    // and append the result to the mesh
369
    virtual void SampleDiscrete(TempMesh& out,IfcFloat start,IfcFloat end) const;
370
371
#ifdef ASSIMP_BUILD_DEBUG
372
    // check if a particular parameter value lies within the well-defined range
373
    bool InRange(IfcFloat) const;
374
#endif
375
    static Curve* Convert(const IFC::Schema_2x3::IfcCurve&,ConversionData& conv);
376
377
protected:
378
    const Schema_2x3::IfcCurve& base_entity;
379
    ConversionData& conv;
380
};
381
382
383
// --------------------------------------------------------------------------------
384
// A BoundedCurve always holds the invariant that GetParametricRange()
385
// never returns infinite values.
386
// --------------------------------------------------------------------------------
387
class BoundedCurve : public Curve {
388
public:
389
    BoundedCurve(const Schema_2x3::IfcBoundedCurve& entity, ConversionData& conv)
390
0
        : Curve(entity,conv)
391
0
    {}
392
393
public:
394
395
    bool IsClosed() const;
396
397
public:
398
399
    // sample the entire curve
400
    void SampleDiscrete(TempMesh& out) const;
401
    using Curve::SampleDiscrete;
402
};
403
404
// IfcProfile.cpp
405
bool ProcessCurve(const Schema_2x3::IfcCurve& curve,  TempMesh& meshout, ConversionData& conv);
406
}
407
}
408
409
#endif