vrshoot
view libs/assimp/IFCCurve.cpp @ 0:b2f14e535253
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 01 Feb 2014 19:58:19 +0200 |
parents | |
children |
line source
1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
5 Copyright (c) 2006-2012, assimp team
6 All rights reserved.
8 Redistribution and use of this software in source and binary forms,
9 with or without modification, are permitted provided that the
10 following conditions are met:
12 * Redistributions of source code must retain the above
13 copyright notice, this list of conditions and the
14 following disclaimer.
16 * Redistributions in binary form must reproduce the above
17 copyright notice, this list of conditions and the
18 following disclaimer in the documentation and/or other
19 materials provided with the distribution.
21 * Neither the name of the assimp team, nor the names of its
22 contributors may be used to endorse or promote products
23 derived from this software without specific prior
24 written permission of the assimp team.
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 ----------------------------------------------------------------------
39 */
41 /** @file IFCProfile.cpp
42 * @brief Read profile and curves entities from IFC files
43 */
45 #include "AssimpPCH.h"
47 #ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
48 #include "IFCUtil.h"
50 namespace Assimp {
51 namespace IFC {
52 namespace {
55 // --------------------------------------------------------------------------------
56 // Conic is the base class for Circle and Ellipse
57 // --------------------------------------------------------------------------------
58 class Conic : public Curve
59 {
61 public:
63 // --------------------------------------------------
64 Conic(const IfcConic& entity, ConversionData& conv)
65 : Curve(entity,conv)
66 {
67 IfcMatrix4 trafo;
68 ConvertAxisPlacement(trafo,*entity.Position,conv);
70 // for convenience, extract the matrix rows
71 location = IfcVector3(trafo.a4,trafo.b4,trafo.c4);
72 p[0] = IfcVector3(trafo.a1,trafo.b1,trafo.c1);
73 p[1] = IfcVector3(trafo.a2,trafo.b2,trafo.c2);
74 p[2] = IfcVector3(trafo.a3,trafo.b3,trafo.c3);
75 }
77 public:
79 // --------------------------------------------------
80 bool IsClosed() const {
81 return true;
82 }
84 // --------------------------------------------------
85 size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
86 ai_assert(InRange(a) && InRange(b));
88 a *= conv.angle_scale;
89 b *= conv.angle_scale;
91 a = fmod(a,static_cast<IfcFloat>( AI_MATH_TWO_PI ));
92 b = fmod(b,static_cast<IfcFloat>( AI_MATH_TWO_PI ));
93 const IfcFloat setting = static_cast<IfcFloat>( AI_MATH_PI * conv.settings.conicSamplingAngle / 180.0 );
94 return static_cast<size_t>( ceil(abs( b-a)) / setting);
95 }
97 // --------------------------------------------------
98 ParamRange GetParametricRange() const {
99 return std::make_pair(static_cast<IfcFloat>( 0. ), static_cast<IfcFloat>( AI_MATH_TWO_PI / conv.angle_scale ));
100 }
102 protected:
103 IfcVector3 location, p[3];
104 };
107 // --------------------------------------------------------------------------------
108 // Circle
109 // --------------------------------------------------------------------------------
110 class Circle : public Conic
111 {
113 public:
115 // --------------------------------------------------
116 Circle(const IfcCircle& entity, ConversionData& conv)
117 : Conic(entity,conv)
118 , entity(entity)
119 {
120 }
122 public:
124 // --------------------------------------------------
125 IfcVector3 Eval(IfcFloat u) const {
126 u = -conv.angle_scale * u;
127 return location + static_cast<IfcFloat>(entity.Radius)*(static_cast<IfcFloat>(::cos(u))*p[0] +
128 static_cast<IfcFloat>(::sin(u))*p[1]);
129 }
131 private:
132 const IfcCircle& entity;
133 };
136 // --------------------------------------------------------------------------------
137 // Ellipse
138 // --------------------------------------------------------------------------------
139 class Ellipse : public Conic
140 {
142 public:
144 // --------------------------------------------------
145 Ellipse(const IfcEllipse& entity, ConversionData& conv)
146 : Conic(entity,conv)
147 , entity(entity)
148 {
149 }
151 public:
153 // --------------------------------------------------
154 IfcVector3 Eval(IfcFloat u) const {
155 u = -conv.angle_scale * u;
156 return location + static_cast<IfcFloat>(entity.SemiAxis1)*static_cast<IfcFloat>(::cos(u))*p[0] +
157 static_cast<IfcFloat>(entity.SemiAxis2)*static_cast<IfcFloat>(::sin(u))*p[1];
158 }
160 private:
161 const IfcEllipse& entity;
162 };
165 // --------------------------------------------------------------------------------
166 // Line
167 // --------------------------------------------------------------------------------
168 class Line : public Curve
169 {
171 public:
173 // --------------------------------------------------
174 Line(const IfcLine& entity, ConversionData& conv)
175 : Curve(entity,conv)
176 , entity(entity)
177 {
178 ConvertCartesianPoint(p,entity.Pnt);
179 ConvertVector(v,entity.Dir);
180 }
182 public:
184 // --------------------------------------------------
185 bool IsClosed() const {
186 return false;
187 }
189 // --------------------------------------------------
190 IfcVector3 Eval(IfcFloat u) const {
191 return p + u*v;
192 }
194 // --------------------------------------------------
195 size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
196 ai_assert(InRange(a) && InRange(b));
197 // two points are always sufficient for a line segment
198 return a==b ? 1 : 2;
199 }
202 // --------------------------------------------------
203 void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const
204 {
205 ai_assert(InRange(a) && InRange(b));
207 if (a == b) {
208 out.verts.push_back(Eval(a));
209 return;
210 }
211 out.verts.reserve(out.verts.size()+2);
212 out.verts.push_back(Eval(a));
213 out.verts.push_back(Eval(b));
214 }
216 // --------------------------------------------------
217 ParamRange GetParametricRange() const {
218 const IfcFloat inf = std::numeric_limits<IfcFloat>::infinity();
220 return std::make_pair(-inf,+inf);
221 }
223 private:
224 const IfcLine& entity;
225 IfcVector3 p,v;
226 };
228 // --------------------------------------------------------------------------------
229 // CompositeCurve joins multiple smaller, bounded curves
230 // --------------------------------------------------------------------------------
231 class CompositeCurve : public BoundedCurve
232 {
234 typedef std::pair< boost::shared_ptr< BoundedCurve >, bool > CurveEntry;
236 public:
238 // --------------------------------------------------
239 CompositeCurve(const IfcCompositeCurve& entity, ConversionData& conv)
240 : BoundedCurve(entity,conv)
241 , entity(entity)
242 , total()
243 {
244 curves.reserve(entity.Segments.size());
245 BOOST_FOREACH(const IfcCompositeCurveSegment& curveSegment,entity.Segments) {
246 // according to the specification, this must be a bounded curve
247 boost::shared_ptr< Curve > cv(Curve::Convert(curveSegment.ParentCurve,conv));
248 boost::shared_ptr< BoundedCurve > bc = boost::dynamic_pointer_cast<BoundedCurve>(cv);
250 if (!bc) {
251 IFCImporter::LogError("expected segment of composite curve to be a bounded curve");
252 continue;
253 }
255 if ( (std::string)curveSegment.Transition != "CONTINUOUS" ) {
256 IFCImporter::LogDebug("ignoring transition code on composite curve segment, only continuous transitions are supported");
257 }
259 curves.push_back( CurveEntry(bc,IsTrue(curveSegment.SameSense)) );
260 total += bc->GetParametricRangeDelta();
261 }
263 if (curves.empty()) {
264 throw CurveError("empty composite curve");
265 }
266 }
268 public:
270 // --------------------------------------------------
271 IfcVector3 Eval(IfcFloat u) const {
272 if (curves.empty()) {
273 return IfcVector3();
274 }
276 IfcFloat acc = 0;
277 BOOST_FOREACH(const CurveEntry& entry, curves) {
278 const ParamRange& range = entry.first->GetParametricRange();
279 const IfcFloat delta = abs(range.second-range.first);
280 if (u < acc+delta) {
281 return entry.first->Eval( entry.second ? (u-acc) + range.first : range.second-(u-acc));
282 }
284 acc += delta;
285 }
286 // clamp to end
287 return curves.back().first->Eval(curves.back().first->GetParametricRange().second);
288 }
290 // --------------------------------------------------
291 size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
292 ai_assert(InRange(a) && InRange(b));
293 size_t cnt = 0;
295 IfcFloat acc = 0;
296 BOOST_FOREACH(const CurveEntry& entry, curves) {
297 const ParamRange& range = entry.first->GetParametricRange();
298 const IfcFloat delta = abs(range.second-range.first);
299 if (a <= acc+delta && b >= acc) {
300 const IfcFloat at = std::max(static_cast<IfcFloat>( 0. ),a-acc), bt = std::min(delta,b-acc);
301 cnt += entry.first->EstimateSampleCount( entry.second ? at + range.first : range.second - bt, entry.second ? bt + range.first : range.second - at );
302 }
304 acc += delta;
305 }
307 return cnt;
308 }
310 // --------------------------------------------------
311 void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const
312 {
313 ai_assert(InRange(a) && InRange(b));
315 const size_t cnt = EstimateSampleCount(a,b);
316 out.verts.reserve(out.verts.size() + cnt);
318 BOOST_FOREACH(const CurveEntry& entry, curves) {
319 const size_t cnt = out.verts.size();
320 entry.first->SampleDiscrete(out);
322 if (!entry.second && cnt != out.verts.size()) {
323 std::reverse(out.verts.begin()+cnt,out.verts.end());
324 }
325 }
326 }
328 // --------------------------------------------------
329 ParamRange GetParametricRange() const {
330 return std::make_pair(static_cast<IfcFloat>( 0. ),total);
331 }
333 private:
334 const IfcCompositeCurve& entity;
335 std::vector< CurveEntry > curves;
337 IfcFloat total;
338 };
341 // --------------------------------------------------------------------------------
342 // TrimmedCurve can be used to trim an unbounded curve to a bounded range
343 // --------------------------------------------------------------------------------
344 class TrimmedCurve : public BoundedCurve
345 {
347 public:
349 // --------------------------------------------------
350 TrimmedCurve(const IfcTrimmedCurve& entity, ConversionData& conv)
351 : BoundedCurve(entity,conv)
352 , entity(entity)
353 , ok()
354 {
355 base = boost::shared_ptr<const Curve>(Curve::Convert(entity.BasisCurve,conv));
357 typedef boost::shared_ptr<const STEP::EXPRESS::DataType> Entry;
359 // for some reason, trimmed curves can either specify a parametric value
360 // or a point on the curve, or both. And they can even specify which of the
361 // two representations they prefer, even though an information invariant
362 // claims that they must be identical if both are present.
363 // oh well.
364 bool have_param = false, have_point = false;
365 IfcVector3 point;
366 BOOST_FOREACH(const Entry sel,entity.Trim1) {
367 if (const EXPRESS::REAL* const r = sel->ToPtr<EXPRESS::REAL>()) {
368 range.first = *r;
369 have_param = true;
370 break;
371 }
372 else if (const IfcCartesianPoint* const r = sel->ResolveSelectPtr<IfcCartesianPoint>(conv.db)) {
373 ConvertCartesianPoint(point,*r);
374 have_point = true;
375 }
376 }
377 if (!have_param) {
378 if (!have_point || !base->ReverseEval(point,range.first)) {
379 throw CurveError("IfcTrimmedCurve: failed to read first trim parameter, ignoring curve");
380 }
381 }
382 have_param = false, have_point = false;
383 BOOST_FOREACH(const Entry sel,entity.Trim2) {
384 if (const EXPRESS::REAL* const r = sel->ToPtr<EXPRESS::REAL>()) {
385 range.second = *r;
386 have_param = true;
387 break;
388 }
389 else if (const IfcCartesianPoint* const r = sel->ResolveSelectPtr<IfcCartesianPoint>(conv.db)) {
390 ConvertCartesianPoint(point,*r);
391 have_point = true;
392 }
393 }
394 if (!have_param) {
395 if (!have_point || !base->ReverseEval(point,range.second)) {
396 throw CurveError("IfcTrimmedCurve: failed to read second trim parameter, ignoring curve");
397 }
398 }
400 agree_sense = IsTrue(entity.SenseAgreement);
401 if( !agree_sense ) {
402 std::swap(range.first,range.second);
403 }
405 // "NOTE In case of a closed curve, it may be necessary to increment t1 or t2
406 // by the parametric length for consistency with the sense flag."
407 if (base->IsClosed()) {
408 if( range.first > range.second ) {
409 range.second += base->GetParametricRangeDelta();
410 }
411 }
413 maxval = range.second-range.first;
414 ai_assert(maxval >= 0);
415 }
417 public:
419 // --------------------------------------------------
420 IfcVector3 Eval(IfcFloat p) const {
421 ai_assert(InRange(p));
422 return base->Eval( TrimParam(p) );
423 }
425 // --------------------------------------------------
426 size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
427 ai_assert(InRange(a) && InRange(b));
428 return base->EstimateSampleCount(TrimParam(a),TrimParam(b));
429 }
431 // --------------------------------------------------
432 void SampleDiscrete(TempMesh& out,IfcFloat a,IfcFloat b) const {
433 ai_assert(InRange(a) && InRange(b));
434 return base->SampleDiscrete(out,TrimParam(a),TrimParam(b));
435 }
437 // --------------------------------------------------
438 ParamRange GetParametricRange() const {
439 return std::make_pair(static_cast<IfcFloat>( 0. ),maxval);
440 }
442 private:
444 // --------------------------------------------------
445 IfcFloat TrimParam(IfcFloat f) const {
446 return agree_sense ? f + range.first : range.second - f;
447 }
450 private:
451 const IfcTrimmedCurve& entity;
452 ParamRange range;
453 IfcFloat maxval;
454 bool agree_sense;
455 bool ok;
457 boost::shared_ptr<const Curve> base;
458 };
461 // --------------------------------------------------------------------------------
462 // PolyLine is a 'curve' defined by linear interpolation over a set of discrete points
463 // --------------------------------------------------------------------------------
464 class PolyLine : public BoundedCurve
465 {
467 public:
469 // --------------------------------------------------
470 PolyLine(const IfcPolyline& entity, ConversionData& conv)
471 : BoundedCurve(entity,conv)
472 , entity(entity)
473 {
474 points.reserve(entity.Points.size());
476 IfcVector3 t;
477 BOOST_FOREACH(const IfcCartesianPoint& cp, entity.Points) {
478 ConvertCartesianPoint(t,cp);
479 points.push_back(t);
480 }
481 }
483 public:
485 // --------------------------------------------------
486 IfcVector3 Eval(IfcFloat p) const {
487 ai_assert(InRange(p));
489 const size_t b = static_cast<size_t>(floor(p));
490 if (b == points.size()-1) {
491 return points.back();
492 }
494 const IfcFloat d = p-static_cast<IfcFloat>(b);
495 return points[b+1] * d + points[b] * (static_cast<IfcFloat>( 1. )-d);
496 }
498 // --------------------------------------------------
499 size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
500 ai_assert(InRange(a) && InRange(b));
501 return static_cast<size_t>( ceil(b) - floor(a) );
502 }
504 // --------------------------------------------------
505 ParamRange GetParametricRange() const {
506 return std::make_pair(static_cast<IfcFloat>( 0. ),static_cast<IfcFloat>(points.size()-1));
507 }
509 private:
510 const IfcPolyline& entity;
511 std::vector<IfcVector3> points;
512 };
515 } // anon
518 // ------------------------------------------------------------------------------------------------
519 Curve* Curve :: Convert(const IFC::IfcCurve& curve,ConversionData& conv)
520 {
521 if(curve.ToPtr<IfcBoundedCurve>()) {
522 if(const IfcPolyline* c = curve.ToPtr<IfcPolyline>()) {
523 return new PolyLine(*c,conv);
524 }
525 if(const IfcTrimmedCurve* c = curve.ToPtr<IfcTrimmedCurve>()) {
526 return new TrimmedCurve(*c,conv);
527 }
528 if(const IfcCompositeCurve* c = curve.ToPtr<IfcCompositeCurve>()) {
529 return new CompositeCurve(*c,conv);
530 }
531 //if(const IfcBSplineCurve* c = curve.ToPtr<IfcBSplineCurve>()) {
532 // return new BSplineCurve(*c,conv);
533 //}
534 }
536 if(curve.ToPtr<IfcConic>()) {
537 if(const IfcCircle* c = curve.ToPtr<IfcCircle>()) {
538 return new Circle(*c,conv);
539 }
540 if(const IfcEllipse* c = curve.ToPtr<IfcEllipse>()) {
541 return new Ellipse(*c,conv);
542 }
543 }
545 if(const IfcLine* c = curve.ToPtr<IfcLine>()) {
546 return new Line(*c,conv);
547 }
549 // XXX OffsetCurve2D, OffsetCurve3D not currently supported
550 return NULL;
551 }
553 #ifdef _DEBUG
554 // ------------------------------------------------------------------------------------------------
555 bool Curve :: InRange(IfcFloat u) const
556 {
557 const ParamRange range = GetParametricRange();
558 if (IsClosed()) {
559 return true;
560 //ai_assert(range.first != std::numeric_limits<IfcFloat>::infinity() && range.second != std::numeric_limits<IfcFloat>::infinity());
561 //u = range.first + fmod(u-range.first,range.second-range.first);
562 }
563 const IfcFloat epsilon = 1e-5;
564 return u - range.first > -epsilon && range.second - u > -epsilon;
565 }
566 #endif
568 // ------------------------------------------------------------------------------------------------
569 IfcFloat Curve :: GetParametricRangeDelta() const
570 {
571 const ParamRange& range = GetParametricRange();
572 return abs(range.second - range.first);
573 }
575 // ------------------------------------------------------------------------------------------------
576 size_t Curve :: EstimateSampleCount(IfcFloat a, IfcFloat b) const
577 {
578 ai_assert(InRange(a) && InRange(b));
580 // arbitrary default value, deriving classes should supply better suited values
581 return 16;
582 }
584 // ------------------------------------------------------------------------------------------------
585 IfcFloat RecursiveSearch(const Curve* cv, const IfcVector3& val, IfcFloat a, IfcFloat b, unsigned int samples, IfcFloat threshold, unsigned int recurse = 0, unsigned int max_recurse = 15)
586 {
587 ai_assert(samples>1);
589 const IfcFloat delta = (b-a)/samples, inf = std::numeric_limits<IfcFloat>::infinity();
590 IfcFloat min_point[2] = {a,b}, min_diff[2] = {inf,inf};
591 IfcFloat runner = a;
593 for (unsigned int i = 0; i < samples; ++i, runner += delta) {
594 const IfcFloat diff = (cv->Eval(runner)-val).SquareLength();
595 if (diff < min_diff[0]) {
596 min_diff[1] = min_diff[0];
597 min_point[1] = min_point[0];
599 min_diff[0] = diff;
600 min_point[0] = runner;
601 }
602 else if (diff < min_diff[1]) {
603 min_diff[1] = diff;
604 min_point[1] = runner;
605 }
606 }
608 ai_assert(min_diff[0] != inf && min_diff[1] != inf);
609 if ( fabs(a-min_point[0]) < threshold || recurse >= max_recurse) {
610 return min_point[0];
611 }
613 // fix for closed curves to take their wrap-over into account
614 if (cv->IsClosed() && fabs(min_point[0]-min_point[1]) > cv->GetParametricRangeDelta()*0.5 ) {
615 const Curve::ParamRange& range = cv->GetParametricRange();
616 const IfcFloat wrapdiff = (cv->Eval(range.first)-val).SquareLength();
618 if (wrapdiff < min_diff[0]) {
619 const IfcFloat t = min_point[0];
620 min_point[0] = min_point[1] > min_point[0] ? range.first : range.second;
621 min_point[1] = t;
622 }
623 }
625 return RecursiveSearch(cv,val,min_point[0],min_point[1],samples,threshold,recurse+1,max_recurse);
626 }
628 // ------------------------------------------------------------------------------------------------
629 bool Curve :: ReverseEval(const IfcVector3& val, IfcFloat& paramOut) const
630 {
631 // note: the following algorithm is not guaranteed to find the 'right' parameter value
632 // in all possible cases, but it will always return at least some value so this function
633 // will never fail in the default implementation.
635 // XXX derive threshold from curve topology
636 const IfcFloat threshold = 1e-4f;
637 const unsigned int samples = 16;
639 const ParamRange& range = GetParametricRange();
640 paramOut = RecursiveSearch(this,val,range.first,range.second,samples,threshold);
642 return true;
643 }
645 // ------------------------------------------------------------------------------------------------
646 void Curve :: SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const
647 {
648 ai_assert(InRange(a) && InRange(b));
650 const size_t cnt = std::max(static_cast<size_t>(0),EstimateSampleCount(a,b));
651 out.verts.reserve( out.verts.size() + cnt );
653 IfcFloat p = a, delta = (b-a)/cnt;
654 for(size_t i = 0; i < cnt; ++i, p += delta) {
655 out.verts.push_back(Eval(p));
656 }
657 }
659 // ------------------------------------------------------------------------------------------------
660 bool BoundedCurve :: IsClosed() const
661 {
662 return false;
663 }
665 // ------------------------------------------------------------------------------------------------
666 void BoundedCurve :: SampleDiscrete(TempMesh& out) const
667 {
668 const ParamRange& range = GetParametricRange();
669 ai_assert(range.first != std::numeric_limits<IfcFloat>::infinity() && range.second != std::numeric_limits<IfcFloat>::infinity());
671 return SampleDiscrete(out,range.first,range.second);
672 }
674 } // IFC
675 } // Assimp
677 #endif // ASSIMP_BUILD_NO_IFC_IMPORTER