/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 |