/src/assimp/code/AssetLib/IFC/IFCCurve.cpp
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 IFCProfile.cpp |
43 | | /// @brief Read profile and curves entities from IFC files |
44 | | |
45 | | #ifndef ASSIMP_BUILD_NO_IFC_IMPORTER |
46 | | #include "IFCUtil.h" |
47 | | |
48 | | namespace Assimp { |
49 | | namespace IFC { |
50 | | |
51 | | namespace { |
52 | | |
53 | | // -------------------------------------------------------------------------------- |
54 | | // Conic is the base class for Circle and Ellipse |
55 | | // -------------------------------------------------------------------------------- |
56 | | class Conic : public Curve { |
57 | | public: |
58 | | // -------------------------------------------------- |
59 | 0 | Conic(const Schema_2x3::IfcConic& entity, ConversionData& conv) : Curve(entity,conv) { |
60 | 0 | IfcMatrix4 trafo; |
61 | 0 | ConvertAxisPlacement(trafo,*entity.Position,conv); |
62 | | |
63 | | // for convenience, extract the matrix rows |
64 | 0 | location = IfcVector3(trafo.a4,trafo.b4,trafo.c4); |
65 | 0 | p[0] = IfcVector3(trafo.a1,trafo.b1,trafo.c1); |
66 | 0 | p[1] = IfcVector3(trafo.a2,trafo.b2,trafo.c2); |
67 | 0 | p[2] = IfcVector3(trafo.a3,trafo.b3,trafo.c3); |
68 | 0 | } |
69 | | |
70 | | // -------------------------------------------------- |
71 | 0 | bool IsClosed() const override { |
72 | 0 | return true; |
73 | 0 | } |
74 | | |
75 | | // -------------------------------------------------- |
76 | 0 | size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const override { |
77 | 0 | ai_assert( InRange( a ) ); |
78 | 0 | ai_assert( InRange( b ) ); |
79 | |
|
80 | 0 | a *= conv.angle_scale; |
81 | 0 | b *= conv.angle_scale; |
82 | |
|
83 | 0 | a = std::fmod(a,static_cast<IfcFloat>( AI_MATH_TWO_PI )); |
84 | 0 | b = std::fmod(b,static_cast<IfcFloat>( AI_MATH_TWO_PI )); |
85 | 0 | const IfcFloat setting = static_cast<IfcFloat>( AI_MATH_PI * conv.settings.conicSamplingAngle / 180.0 ); |
86 | 0 | return static_cast<size_t>( std::ceil(std::abs( b-a)) / setting); |
87 | 0 | } |
88 | | |
89 | | // -------------------------------------------------- |
90 | 0 | ParamRange GetParametricRange() const override { |
91 | 0 | return std::make_pair(static_cast<IfcFloat>( 0. ), static_cast<IfcFloat>( AI_MATH_TWO_PI / conv.angle_scale )); |
92 | 0 | } |
93 | | |
94 | | protected: |
95 | | IfcVector3 location, p[3]; |
96 | | }; |
97 | | |
98 | | // -------------------------------------------------------------------------------- |
99 | | // Circle |
100 | | // -------------------------------------------------------------------------------- |
101 | | class Circle : public Conic { |
102 | | public: |
103 | | // -------------------------------------------------- |
104 | 0 | Circle(const Schema_2x3::IfcCircle& entity, ConversionData& conv) : Conic(entity,conv) , entity(entity) {} |
105 | | |
106 | | // -------------------------------------------------- |
107 | | ~Circle() override = default; |
108 | | |
109 | | // -------------------------------------------------- |
110 | 0 | IfcVector3 Eval(IfcFloat u) const override { |
111 | 0 | u = -conv.angle_scale * u; |
112 | 0 | return location + static_cast<IfcFloat>(entity.Radius)*(static_cast<IfcFloat>(std::cos(u))*p[0] + |
113 | 0 | static_cast<IfcFloat>(std::sin(u))*p[1]); |
114 | 0 | } |
115 | | |
116 | | private: |
117 | | const Schema_2x3::IfcCircle& entity; |
118 | | }; |
119 | | |
120 | | // -------------------------------------------------------------------------------- |
121 | | // Ellipse |
122 | | // -------------------------------------------------------------------------------- |
123 | | class Ellipse : public Conic { |
124 | | public: |
125 | | // -------------------------------------------------- |
126 | | Ellipse(const Schema_2x3::IfcEllipse& entity, ConversionData& conv) |
127 | 0 | : Conic(entity,conv) |
128 | 0 | , entity(entity) { |
129 | | // empty |
130 | 0 | } |
131 | | |
132 | | // -------------------------------------------------- |
133 | 0 | IfcVector3 Eval(IfcFloat u) const override { |
134 | 0 | u = -conv.angle_scale * u; |
135 | 0 | return location + static_cast<IfcFloat>(entity.SemiAxis1)*static_cast<IfcFloat>(std::cos(u))*p[0] + |
136 | 0 | static_cast<IfcFloat>(entity.SemiAxis2)*static_cast<IfcFloat>(std::sin(u))*p[1]; |
137 | 0 | } |
138 | | |
139 | | private: |
140 | | const Schema_2x3::IfcEllipse& entity; |
141 | | }; |
142 | | |
143 | | // -------------------------------------------------------------------------------- |
144 | | // Line |
145 | | // -------------------------------------------------------------------------------- |
146 | | class Line : public Curve { |
147 | | public: |
148 | | // -------------------------------------------------- |
149 | | Line(const Schema_2x3::IfcLine& entity, ConversionData& conv) |
150 | 0 | : Curve(entity,conv) { |
151 | 0 | ConvertCartesianPoint(p,entity.Pnt); |
152 | 0 | ConvertVector(v,entity.Dir); |
153 | 0 | } |
154 | | |
155 | | // -------------------------------------------------- |
156 | 0 | bool IsClosed() const override { |
157 | 0 | return false; |
158 | 0 | } |
159 | | |
160 | | // -------------------------------------------------- |
161 | 0 | IfcVector3 Eval(IfcFloat u) const override { |
162 | 0 | return p + u*v; |
163 | 0 | } |
164 | | |
165 | | // -------------------------------------------------- |
166 | 0 | size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const override { |
167 | 0 | ai_assert( InRange( a ) ); |
168 | 0 | ai_assert( InRange( b ) ); |
169 | | // two points are always sufficient for a line segment |
170 | 0 | return a==b ? 1 : 2; |
171 | 0 | } |
172 | | |
173 | | |
174 | | // -------------------------------------------------- |
175 | 0 | void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const override { |
176 | 0 | ai_assert( InRange( a ) ); |
177 | 0 | ai_assert( InRange( b ) ); |
178 | |
|
179 | 0 | if (a == b) { |
180 | 0 | out.mVerts.push_back(Eval(a)); |
181 | 0 | return; |
182 | 0 | } |
183 | 0 | out.mVerts.reserve(out.mVerts.size()+2); |
184 | 0 | out.mVerts.push_back(Eval(a)); |
185 | 0 | out.mVerts.push_back(Eval(b)); |
186 | 0 | } |
187 | | |
188 | | // -------------------------------------------------- |
189 | 0 | ParamRange GetParametricRange() const override { |
190 | 0 | const IfcFloat inf = std::numeric_limits<IfcFloat>::infinity(); |
191 | |
|
192 | 0 | return std::make_pair(-inf,+inf); |
193 | 0 | } |
194 | | |
195 | | private: |
196 | | IfcVector3 p,v; |
197 | | }; |
198 | | |
199 | | // -------------------------------------------------------------------------------- |
200 | | // CompositeCurve joins multiple smaller, bounded curves |
201 | | // -------------------------------------------------------------------------------- |
202 | | class CompositeCurve : public BoundedCurve { |
203 | | typedef std::pair< std::shared_ptr< BoundedCurve >, bool > CurveEntry; |
204 | | |
205 | | public: |
206 | | // -------------------------------------------------- |
207 | | CompositeCurve(const Schema_2x3::IfcCompositeCurve& entity, ConversionData& conv) |
208 | 0 | : BoundedCurve(entity,conv) |
209 | 0 | , total() { |
210 | 0 | curves.reserve(entity.Segments.size()); |
211 | 0 | for(const Schema_2x3::IfcCompositeCurveSegment& curveSegment :entity.Segments) { |
212 | | // according to the specification, this must be a bounded curve |
213 | 0 | std::shared_ptr< Curve > cv(Curve::Convert(curveSegment.ParentCurve,conv)); |
214 | 0 | std::shared_ptr< BoundedCurve > bc = std::dynamic_pointer_cast<BoundedCurve>(cv); |
215 | |
|
216 | 0 | if (!bc) { |
217 | 0 | IFCImporter::LogError("expected segment of composite curve to be a bounded curve"); |
218 | 0 | continue; |
219 | 0 | } |
220 | | |
221 | 0 | if ( (std::string)curveSegment.Transition != "CONTINUOUS" ) { |
222 | 0 | IFCImporter::LogVerboseDebug("ignoring transition code on composite curve segment, only continuous transitions are supported"); |
223 | 0 | } |
224 | |
|
225 | 0 | curves.emplace_back(bc,IsTrue(curveSegment.SameSense) ); |
226 | 0 | total += bc->GetParametricRangeDelta(); |
227 | 0 | } |
228 | |
|
229 | 0 | if (curves.empty()) { |
230 | 0 | throw CurveError("empty composite curve"); |
231 | 0 | } |
232 | 0 | } |
233 | | |
234 | | // -------------------------------------------------- |
235 | 0 | IfcVector3 Eval(IfcFloat u) const override { |
236 | 0 | if (curves.empty()) { |
237 | 0 | return IfcVector3(); |
238 | 0 | } |
239 | | |
240 | 0 | IfcFloat acc = 0; |
241 | 0 | for(const CurveEntry& entry : curves) { |
242 | 0 | const ParamRange& range = entry.first->GetParametricRange(); |
243 | 0 | const IfcFloat delta = std::abs(range.second-range.first); |
244 | 0 | if (u < acc+delta) { |
245 | 0 | return entry.first->Eval( entry.second ? (u-acc) + range.first : range.second-(u-acc)); |
246 | 0 | } |
247 | | |
248 | 0 | acc += delta; |
249 | 0 | } |
250 | | // clamp to end |
251 | 0 | return curves.back().first->Eval(curves.back().first->GetParametricRange().second); |
252 | 0 | } |
253 | | |
254 | | // -------------------------------------------------- |
255 | 0 | size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const override { |
256 | 0 | ai_assert( InRange( a ) ); |
257 | 0 | ai_assert( InRange( b ) ); |
258 | 0 | size_t cnt = 0; |
259 | |
|
260 | 0 | IfcFloat acc = 0; |
261 | 0 | for(const CurveEntry& entry : curves) { |
262 | 0 | const ParamRange& range = entry.first->GetParametricRange(); |
263 | 0 | const IfcFloat delta = std::abs(range.second-range.first); |
264 | 0 | if (a <= acc+delta && b >= acc) { |
265 | 0 | const IfcFloat at = std::max(static_cast<IfcFloat>( 0. ),a-acc), bt = std::min(delta,b-acc); |
266 | 0 | cnt += entry.first->EstimateSampleCount( entry.second ? at + range.first : range.second - bt, entry.second ? bt + range.first : range.second - at ); |
267 | 0 | } |
268 | |
|
269 | 0 | acc += delta; |
270 | 0 | } |
271 | |
|
272 | 0 | return cnt; |
273 | 0 | } |
274 | | |
275 | | // -------------------------------------------------- |
276 | 0 | void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const override { |
277 | 0 | ai_assert( InRange( a ) ); |
278 | 0 | ai_assert( InRange( b ) ); |
279 | |
|
280 | 0 | const size_t cnt = EstimateSampleCount(a,b); |
281 | 0 | out.mVerts.reserve(out.mVerts.size() + cnt); |
282 | |
|
283 | 0 | for(const CurveEntry& entry : curves) { |
284 | 0 | const size_t curCnt = out.mVerts.size(); |
285 | 0 | entry.first->SampleDiscrete(out); |
286 | |
|
287 | 0 | if (!entry.second && curCnt != out.mVerts.size()) { |
288 | 0 | std::reverse(out.mVerts.begin() + curCnt, out.mVerts.end()); |
289 | 0 | } |
290 | 0 | } |
291 | 0 | } |
292 | | |
293 | | // -------------------------------------------------- |
294 | 0 | ParamRange GetParametricRange() const override { |
295 | 0 | return std::make_pair(static_cast<IfcFloat>( 0. ),total); |
296 | 0 | } |
297 | | |
298 | | private: |
299 | | std::vector< CurveEntry > curves; |
300 | | IfcFloat total; |
301 | | }; |
302 | | |
303 | | // -------------------------------------------------------------------------------- |
304 | | // TrimmedCurve can be used to trim an unbounded curve to a bounded range |
305 | | // -------------------------------------------------------------------------------- |
306 | | class TrimmedCurve : public BoundedCurve { |
307 | | public: |
308 | | // -------------------------------------------------- |
309 | | TrimmedCurve(const Schema_2x3::IfcTrimmedCurve& entity, ConversionData& conv) |
310 | 0 | : BoundedCurve(entity,conv), |
311 | 0 | base(std::shared_ptr<const Curve>(Curve::Convert(entity.BasisCurve,conv))) |
312 | 0 | { |
313 | 0 | typedef std::shared_ptr<const STEP::EXPRESS::DataType> Entry; |
314 | | |
315 | | // for some reason, trimmed curves can either specify a parametric value |
316 | | // or a point on the curve, or both. And they can even specify which of the |
317 | | // two representations they prefer, even though an information invariant |
318 | | // claims that they must be identical if both are present. |
319 | | // oh well. |
320 | 0 | bool have_param = false, have_point = false; |
321 | 0 | IfcVector3 point; |
322 | 0 | for(const Entry& sel :entity.Trim1) { |
323 | 0 | if (const ::Assimp::STEP::EXPRESS::REAL* const r = sel->ToPtr<::Assimp::STEP::EXPRESS::REAL>()) { |
324 | 0 | range.first = *r; |
325 | 0 | have_param = true; |
326 | 0 | break; |
327 | 0 | } |
328 | 0 | else if (const Schema_2x3::IfcCartesianPoint* const curR = sel->ResolveSelectPtr<Schema_2x3::IfcCartesianPoint>(conv.db)) { |
329 | 0 | ConvertCartesianPoint(point, *curR); |
330 | 0 | have_point = true; |
331 | 0 | } |
332 | 0 | } |
333 | 0 | if (!have_param) { |
334 | 0 | if (!have_point || !base->ReverseEval(point,range.first)) { |
335 | 0 | throw CurveError("IfcTrimmedCurve: failed to read first trim parameter, ignoring curve"); |
336 | 0 | } |
337 | 0 | } |
338 | 0 | have_param = false, have_point = false; |
339 | 0 | for(const Entry& sel :entity.Trim2) { |
340 | 0 | if (const ::Assimp::STEP::EXPRESS::REAL* const r = sel->ToPtr<::Assimp::STEP::EXPRESS::REAL>()) { |
341 | 0 | range.second = *r; |
342 | 0 | have_param = true; |
343 | 0 | break; |
344 | 0 | } |
345 | 0 | else if (const Schema_2x3::IfcCartesianPoint* const curR = sel->ResolveSelectPtr<Schema_2x3::IfcCartesianPoint>(conv.db)) { |
346 | 0 | ConvertCartesianPoint(point, *curR); |
347 | 0 | have_point = true; |
348 | 0 | } |
349 | 0 | } |
350 | 0 | if (!have_param) { |
351 | 0 | if (!have_point || !base->ReverseEval(point,range.second)) { |
352 | 0 | throw CurveError("IfcTrimmedCurve: failed to read second trim parameter, ignoring curve"); |
353 | 0 | } |
354 | 0 | } |
355 | | |
356 | 0 | agree_sense = IsTrue(entity.SenseAgreement); |
357 | 0 | if( !agree_sense ) { |
358 | 0 | std::swap(range.first,range.second); |
359 | 0 | } |
360 | | |
361 | | // "NOTE In case of a closed curve, it may be necessary to increment t1 or t2 |
362 | | // by the parametric length for consistency with the sense flag." |
363 | 0 | if (base->IsClosed()) { |
364 | 0 | if( range.first > range.second ) { |
365 | 0 | range.second += base->GetParametricRangeDelta(); |
366 | 0 | } |
367 | 0 | } |
368 | |
|
369 | 0 | maxval = range.second-range.first; |
370 | 0 | ai_assert(maxval >= 0); |
371 | 0 | } |
372 | | |
373 | | // -------------------------------------------------- |
374 | 0 | IfcVector3 Eval(IfcFloat p) const override { |
375 | 0 | ai_assert(InRange(p)); |
376 | 0 | return base->Eval( TrimParam(p) ); |
377 | 0 | } |
378 | | |
379 | | // -------------------------------------------------- |
380 | 0 | size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const override { |
381 | 0 | ai_assert( InRange( a ) ); |
382 | 0 | ai_assert( InRange( b ) ); |
383 | 0 | return base->EstimateSampleCount(TrimParam(a),TrimParam(b)); |
384 | 0 | } |
385 | | |
386 | | // -------------------------------------------------- |
387 | 0 | void SampleDiscrete(TempMesh& out,IfcFloat a,IfcFloat b) const override { |
388 | 0 | ai_assert(InRange(a)); |
389 | 0 | ai_assert(InRange(b)); |
390 | 0 | return base->SampleDiscrete(out,TrimParam(a),TrimParam(b)); |
391 | 0 | } |
392 | | |
393 | | // -------------------------------------------------- |
394 | 0 | ParamRange GetParametricRange() const override { |
395 | 0 | return std::make_pair(static_cast<IfcFloat>( 0. ),maxval); |
396 | 0 | } |
397 | | |
398 | | private: |
399 | | // -------------------------------------------------- |
400 | 0 | IfcFloat TrimParam(IfcFloat f) const { |
401 | 0 | return agree_sense ? f + range.first : range.second - f; |
402 | 0 | } |
403 | | |
404 | | private: |
405 | | ParamRange range; |
406 | | IfcFloat maxval; |
407 | | bool agree_sense; |
408 | | |
409 | | std::shared_ptr<const Curve> base; |
410 | | }; |
411 | | |
412 | | |
413 | | // -------------------------------------------------------------------------------- |
414 | | // PolyLine is a 'curve' defined by linear interpolation over a set of discrete points |
415 | | // -------------------------------------------------------------------------------- |
416 | | class PolyLine : public BoundedCurve { |
417 | | public: |
418 | | // -------------------------------------------------- |
419 | | PolyLine(const Schema_2x3::IfcPolyline& entity, ConversionData& conv) |
420 | 0 | : BoundedCurve(entity,conv) |
421 | 0 | { |
422 | 0 | points.reserve(entity.Points.size()); |
423 | |
|
424 | 0 | IfcVector3 t; |
425 | 0 | for(const Schema_2x3::IfcCartesianPoint& cp : entity.Points) { |
426 | 0 | ConvertCartesianPoint(t,cp); |
427 | 0 | points.push_back(t); |
428 | 0 | } |
429 | 0 | } |
430 | | |
431 | | // -------------------------------------------------- |
432 | 0 | IfcVector3 Eval(IfcFloat p) const override { |
433 | 0 | ai_assert(InRange(p)); |
434 | |
|
435 | 0 | const size_t b = static_cast<size_t>(std::floor(p)); |
436 | 0 | if (b == points.size()-1) { |
437 | 0 | return points.back(); |
438 | 0 | } |
439 | | |
440 | 0 | const IfcFloat d = p-static_cast<IfcFloat>(b); |
441 | 0 | return points[b+1] * d + points[b] * (static_cast<IfcFloat>( 1. )-d); |
442 | 0 | } |
443 | | |
444 | | // -------------------------------------------------- |
445 | 0 | size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const override { |
446 | 0 | ai_assert(InRange(a)); |
447 | 0 | ai_assert(InRange(b)); |
448 | 0 | return static_cast<size_t>( std::ceil(b) - std::floor(a) ); |
449 | 0 | } |
450 | | |
451 | | // -------------------------------------------------- |
452 | 0 | ParamRange GetParametricRange() const override { |
453 | 0 | return std::make_pair(static_cast<IfcFloat>( 0. ),static_cast<IfcFloat>(points.size()-1)); |
454 | 0 | } |
455 | | |
456 | | private: |
457 | | std::vector<IfcVector3> points; |
458 | | }; |
459 | | |
460 | | } // anon |
461 | | |
462 | | // ------------------------------------------------------------------------------------------------ |
463 | 0 | Curve* Curve::Convert(const IFC::Schema_2x3::IfcCurve& curve,ConversionData& conv) { |
464 | 0 | if(curve.ToPtr<Schema_2x3::IfcBoundedCurve>()) { |
465 | 0 | if(const Schema_2x3::IfcPolyline* c = curve.ToPtr<Schema_2x3::IfcPolyline>()) { |
466 | 0 | return new PolyLine(*c,conv); |
467 | 0 | } |
468 | 0 | if(const Schema_2x3::IfcTrimmedCurve* c = curve.ToPtr<Schema_2x3::IfcTrimmedCurve>()) { |
469 | 0 | return new TrimmedCurve(*c,conv); |
470 | 0 | } |
471 | 0 | if(const Schema_2x3::IfcCompositeCurve* c = curve.ToPtr<Schema_2x3::IfcCompositeCurve>()) { |
472 | 0 | return new CompositeCurve(*c,conv); |
473 | 0 | } |
474 | 0 | } |
475 | | |
476 | 0 | if(curve.ToPtr<Schema_2x3::IfcConic>()) { |
477 | 0 | if(const Schema_2x3::IfcCircle* c = curve.ToPtr<Schema_2x3::IfcCircle>()) { |
478 | 0 | return new Circle(*c,conv); |
479 | 0 | } |
480 | 0 | if(const Schema_2x3::IfcEllipse* c = curve.ToPtr<Schema_2x3::IfcEllipse>()) { |
481 | 0 | return new Ellipse(*c,conv); |
482 | 0 | } |
483 | 0 | } |
484 | | |
485 | 0 | if(const Schema_2x3::IfcLine* c = curve.ToPtr<Schema_2x3::IfcLine>()) { |
486 | 0 | return new Line(*c,conv); |
487 | 0 | } |
488 | | |
489 | | // XXX OffsetCurve2D, OffsetCurve3D not currently supported |
490 | 0 | return nullptr; |
491 | 0 | } |
492 | | |
493 | | #ifdef ASSIMP_BUILD_DEBUG |
494 | | // ------------------------------------------------------------------------------------------------ |
495 | 0 | bool Curve::InRange(IfcFloat u) const { |
496 | 0 | const ParamRange range = GetParametricRange(); |
497 | 0 | if (IsClosed()) { |
498 | 0 | return true; |
499 | 0 | } |
500 | 0 | const IfcFloat epsilon = Math::getEpsilon<float>(); |
501 | 0 | return u - range.first > -epsilon && range.second - u > -epsilon; |
502 | 0 | } |
503 | | #endif |
504 | | |
505 | | // ------------------------------------------------------------------------------------------------ |
506 | 0 | IfcFloat Curve::GetParametricRangeDelta() const { |
507 | 0 | const ParamRange& range = GetParametricRange(); |
508 | 0 | return std::abs(range.second - range.first); |
509 | 0 | } |
510 | | |
511 | | // ------------------------------------------------------------------------------------------------ |
512 | 0 | size_t Curve::EstimateSampleCount(IfcFloat a, IfcFloat b) const { |
513 | 0 | (void)(a); (void)(b); |
514 | 0 | ai_assert( InRange( a ) ); |
515 | 0 | ai_assert( InRange( b ) ); |
516 | | |
517 | | // arbitrary default value, deriving classes should supply better-suited values |
518 | 0 | return 16; |
519 | 0 | } |
520 | | |
521 | | // ------------------------------------------------------------------------------------------------ |
522 | | IfcFloat RecursiveSearch(const Curve* cv, const IfcVector3& val, IfcFloat a, IfcFloat b, |
523 | 0 | unsigned int samples, IfcFloat threshold, unsigned int recurse = 0, unsigned int max_recurse = 15) { |
524 | 0 | ai_assert(samples>1); |
525 | |
|
526 | 0 | const IfcFloat delta = (b-a)/samples, inf = std::numeric_limits<IfcFloat>::infinity(); |
527 | 0 | IfcFloat min_point[2] = {a,b}, min_diff[2] = {inf,inf}; |
528 | 0 | IfcFloat runner = a; |
529 | |
|
530 | 0 | for (unsigned int i = 0; i < samples; ++i, runner += delta) { |
531 | 0 | const IfcFloat diff = (cv->Eval(runner)-val).SquareLength(); |
532 | 0 | if (diff < min_diff[0]) { |
533 | 0 | min_diff[1] = min_diff[0]; |
534 | 0 | min_point[1] = min_point[0]; |
535 | |
|
536 | 0 | min_diff[0] = diff; |
537 | 0 | min_point[0] = runner; |
538 | 0 | } |
539 | 0 | else if (diff < min_diff[1]) { |
540 | 0 | min_diff[1] = diff; |
541 | 0 | min_point[1] = runner; |
542 | 0 | } |
543 | 0 | } |
544 | |
|
545 | 0 | #ifndef __INTEL_LLVM_COMPILER |
546 | 0 | ai_assert( min_diff[ 0 ] != inf ); |
547 | 0 | ai_assert( min_diff[ 1 ] != inf ); |
548 | 0 | #endif // __INTEL_LLVM_COMPILER |
549 | 0 | if ( std::fabs(a-min_point[0]) < threshold || recurse >= max_recurse) { |
550 | 0 | return min_point[0]; |
551 | 0 | } |
552 | | |
553 | | // fix for closed curves to take their wrap-over into account |
554 | 0 | if (cv->IsClosed() && std::fabs(min_point[0]-min_point[1]) > cv->GetParametricRangeDelta()*0.5 ) { |
555 | 0 | const Curve::ParamRange& range = cv->GetParametricRange(); |
556 | 0 | const IfcFloat wrapdiff = (cv->Eval(range.first)-val).SquareLength(); |
557 | |
|
558 | 0 | if (wrapdiff < min_diff[0]) { |
559 | 0 | const IfcFloat t = min_point[0]; |
560 | 0 | min_point[0] = min_point[1] > min_point[0] ? range.first : range.second; |
561 | 0 | min_point[1] = t; |
562 | 0 | } |
563 | 0 | } |
564 | |
|
565 | 0 | return RecursiveSearch(cv,val,min_point[0],min_point[1],samples,threshold,recurse+1,max_recurse); |
566 | 0 | } |
567 | | |
568 | | // ------------------------------------------------------------------------------------------------ |
569 | | bool Curve::ReverseEval(const IfcVector3& val, IfcFloat& paramOut) const |
570 | 0 | { |
571 | | // note: the following algorithm is not guaranteed to find the 'right' parameter value |
572 | | // in all possible cases, but it will always return at least some value so this function |
573 | | // will never fail in the default implementation. |
574 | | |
575 | | // XXX derive threshold from curve topology |
576 | 0 | static const IfcFloat threshold = 1e-4f; |
577 | 0 | static const unsigned int samples = 16; |
578 | |
|
579 | 0 | const ParamRange& range = GetParametricRange(); |
580 | 0 | paramOut = RecursiveSearch(this,val,range.first,range.second,samples,threshold); |
581 | |
|
582 | 0 | return true; |
583 | 0 | } |
584 | | |
585 | | // ------------------------------------------------------------------------------------------------ |
586 | 0 | void Curve::SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const { |
587 | 0 | ai_assert( InRange( a ) ); |
588 | 0 | ai_assert( InRange( b ) ); |
589 | |
|
590 | 0 | const size_t cnt = std::max(static_cast<size_t>(0),EstimateSampleCount(a,b)); |
591 | 0 | out.mVerts.reserve( out.mVerts.size() + cnt + 1); |
592 | |
|
593 | 0 | IfcFloat p = a, delta = (b-a)/cnt; |
594 | 0 | for(size_t i = 0; i <= cnt; ++i, p += delta) { |
595 | 0 | out.mVerts.push_back(Eval(p)); |
596 | 0 | } |
597 | 0 | } |
598 | | |
599 | | // ------------------------------------------------------------------------------------------------ |
600 | 0 | bool BoundedCurve::IsClosed() const { |
601 | 0 | return false; |
602 | 0 | } |
603 | | |
604 | | // ------------------------------------------------------------------------------------------------ |
605 | 0 | void BoundedCurve::SampleDiscrete(TempMesh& out) const { |
606 | 0 | const ParamRange& range = GetParametricRange(); |
607 | 0 | #ifndef __INTEL_LLVM_COMPILER |
608 | 0 | ai_assert( range.first != std::numeric_limits<IfcFloat>::infinity() ); |
609 | 0 | ai_assert( range.second != std::numeric_limits<IfcFloat>::infinity() ); |
610 | 0 | #endif // __INTEL_LLVM_COMPILER |
611 | |
|
612 | 0 | return SampleDiscrete(out,range.first,range.second); |
613 | 0 | } |
614 | | |
615 | | } // IFC |
616 | | } // Assimp |
617 | | |
618 | | #endif // ASSIMP_BUILD_NO_IFC_IMPORTER |