Coverage Report

Created: 2025-06-22 07:30

/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