123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618 |
- /*
- Open Asset Import Library (assimp)
- ----------------------------------------------------------------------
- Copyright (c) 2006-2025, assimp team
- All rights reserved.
- Redistribution and use of this software in source and binary forms,
- with or without modification, are permitted provided that the
- following conditions are met:
- * Redistributions of source code must retain the above
- copyright notice, this list of conditions and the
- following disclaimer.
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the
- following disclaimer in the documentation and/or other
- materials provided with the distribution.
- * Neither the name of the assimp team, nor the names of its
- contributors may be used to endorse or promote products
- derived from this software without specific prior
- written permission of the assimp team.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- ----------------------------------------------------------------------
- */
- /// @file IFCProfile.cpp
- /// @brief Read profile and curves entities from IFC files
- #ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
- #include "IFCUtil.h"
- namespace Assimp {
- namespace IFC {
- namespace {
- // --------------------------------------------------------------------------------
- // Conic is the base class for Circle and Ellipse
- // --------------------------------------------------------------------------------
- class Conic : public Curve {
- public:
- // --------------------------------------------------
- Conic(const Schema_2x3::IfcConic& entity, ConversionData& conv) : Curve(entity,conv) {
- IfcMatrix4 trafo;
- ConvertAxisPlacement(trafo,*entity.Position,conv);
- // for convenience, extract the matrix rows
- location = IfcVector3(trafo.a4,trafo.b4,trafo.c4);
- p[0] = IfcVector3(trafo.a1,trafo.b1,trafo.c1);
- p[1] = IfcVector3(trafo.a2,trafo.b2,trafo.c2);
- p[2] = IfcVector3(trafo.a3,trafo.b3,trafo.c3);
- }
- // --------------------------------------------------
- bool IsClosed() const override {
- return true;
- }
- // --------------------------------------------------
- size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const override {
- ai_assert( InRange( a ) );
- ai_assert( InRange( b ) );
- a *= conv.angle_scale;
- b *= conv.angle_scale;
- a = std::fmod(a,static_cast<IfcFloat>( AI_MATH_TWO_PI ));
- b = std::fmod(b,static_cast<IfcFloat>( AI_MATH_TWO_PI ));
- const IfcFloat setting = static_cast<IfcFloat>( AI_MATH_PI * conv.settings.conicSamplingAngle / 180.0 );
- return static_cast<size_t>( std::ceil(std::abs( b-a)) / setting);
- }
- // --------------------------------------------------
- ParamRange GetParametricRange() const override {
- return std::make_pair(static_cast<IfcFloat>( 0. ), static_cast<IfcFloat>( AI_MATH_TWO_PI / conv.angle_scale ));
- }
- protected:
- IfcVector3 location, p[3];
- };
- // --------------------------------------------------------------------------------
- // Circle
- // --------------------------------------------------------------------------------
- class Circle : public Conic {
- public:
- // --------------------------------------------------
- Circle(const Schema_2x3::IfcCircle& entity, ConversionData& conv) : Conic(entity,conv) , entity(entity) {}
- // --------------------------------------------------
- ~Circle() override = default;
- // --------------------------------------------------
- IfcVector3 Eval(IfcFloat u) const override {
- u = -conv.angle_scale * u;
- return location + static_cast<IfcFloat>(entity.Radius)*(static_cast<IfcFloat>(std::cos(u))*p[0] +
- static_cast<IfcFloat>(std::sin(u))*p[1]);
- }
- private:
- const Schema_2x3::IfcCircle& entity;
- };
- // --------------------------------------------------------------------------------
- // Ellipse
- // --------------------------------------------------------------------------------
- class Ellipse : public Conic {
- public:
- // --------------------------------------------------
- Ellipse(const Schema_2x3::IfcEllipse& entity, ConversionData& conv)
- : Conic(entity,conv)
- , entity(entity) {
- // empty
- }
- // --------------------------------------------------
- IfcVector3 Eval(IfcFloat u) const override {
- u = -conv.angle_scale * u;
- return location + static_cast<IfcFloat>(entity.SemiAxis1)*static_cast<IfcFloat>(std::cos(u))*p[0] +
- static_cast<IfcFloat>(entity.SemiAxis2)*static_cast<IfcFloat>(std::sin(u))*p[1];
- }
- private:
- const Schema_2x3::IfcEllipse& entity;
- };
- // --------------------------------------------------------------------------------
- // Line
- // --------------------------------------------------------------------------------
- class Line : public Curve {
- public:
- // --------------------------------------------------
- Line(const Schema_2x3::IfcLine& entity, ConversionData& conv)
- : Curve(entity,conv) {
- ConvertCartesianPoint(p,entity.Pnt);
- ConvertVector(v,entity.Dir);
- }
- // --------------------------------------------------
- bool IsClosed() const override {
- return false;
- }
- // --------------------------------------------------
- IfcVector3 Eval(IfcFloat u) const override {
- return p + u*v;
- }
- // --------------------------------------------------
- size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const override {
- ai_assert( InRange( a ) );
- ai_assert( InRange( b ) );
- // two points are always sufficient for a line segment
- return a==b ? 1 : 2;
- }
- // --------------------------------------------------
- void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const override {
- ai_assert( InRange( a ) );
- ai_assert( InRange( b ) );
- if (a == b) {
- out.mVerts.push_back(Eval(a));
- return;
- }
- out.mVerts.reserve(out.mVerts.size()+2);
- out.mVerts.push_back(Eval(a));
- out.mVerts.push_back(Eval(b));
- }
- // --------------------------------------------------
- ParamRange GetParametricRange() const override {
- const IfcFloat inf = std::numeric_limits<IfcFloat>::infinity();
- return std::make_pair(-inf,+inf);
- }
- private:
- IfcVector3 p,v;
- };
- // --------------------------------------------------------------------------------
- // CompositeCurve joins multiple smaller, bounded curves
- // --------------------------------------------------------------------------------
- class CompositeCurve : public BoundedCurve {
- typedef std::pair< std::shared_ptr< BoundedCurve >, bool > CurveEntry;
- public:
- // --------------------------------------------------
- CompositeCurve(const Schema_2x3::IfcCompositeCurve& entity, ConversionData& conv)
- : BoundedCurve(entity,conv)
- , total() {
- curves.reserve(entity.Segments.size());
- for(const Schema_2x3::IfcCompositeCurveSegment& curveSegment :entity.Segments) {
- // according to the specification, this must be a bounded curve
- std::shared_ptr< Curve > cv(Curve::Convert(curveSegment.ParentCurve,conv));
- std::shared_ptr< BoundedCurve > bc = std::dynamic_pointer_cast<BoundedCurve>(cv);
- if (!bc) {
- IFCImporter::LogError("expected segment of composite curve to be a bounded curve");
- continue;
- }
- if ( (std::string)curveSegment.Transition != "CONTINUOUS" ) {
- IFCImporter::LogVerboseDebug("ignoring transition code on composite curve segment, only continuous transitions are supported");
- }
- curves.emplace_back(bc,IsTrue(curveSegment.SameSense) );
- total += bc->GetParametricRangeDelta();
- }
- if (curves.empty()) {
- throw CurveError("empty composite curve");
- }
- }
- // --------------------------------------------------
- IfcVector3 Eval(IfcFloat u) const override {
- if (curves.empty()) {
- return IfcVector3();
- }
- IfcFloat acc = 0;
- for(const CurveEntry& entry : curves) {
- const ParamRange& range = entry.first->GetParametricRange();
- const IfcFloat delta = std::abs(range.second-range.first);
- if (u < acc+delta) {
- return entry.first->Eval( entry.second ? (u-acc) + range.first : range.second-(u-acc));
- }
- acc += delta;
- }
- // clamp to end
- return curves.back().first->Eval(curves.back().first->GetParametricRange().second);
- }
- // --------------------------------------------------
- size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const override {
- ai_assert( InRange( a ) );
- ai_assert( InRange( b ) );
- size_t cnt = 0;
- IfcFloat acc = 0;
- for(const CurveEntry& entry : curves) {
- const ParamRange& range = entry.first->GetParametricRange();
- const IfcFloat delta = std::abs(range.second-range.first);
- if (a <= acc+delta && b >= acc) {
- const IfcFloat at = std::max(static_cast<IfcFloat>( 0. ),a-acc), bt = std::min(delta,b-acc);
- cnt += entry.first->EstimateSampleCount( entry.second ? at + range.first : range.second - bt, entry.second ? bt + range.first : range.second - at );
- }
- acc += delta;
- }
- return cnt;
- }
- // --------------------------------------------------
- void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const override {
- ai_assert( InRange( a ) );
- ai_assert( InRange( b ) );
- const size_t cnt = EstimateSampleCount(a,b);
- out.mVerts.reserve(out.mVerts.size() + cnt);
- for(const CurveEntry& entry : curves) {
- const size_t curCnt = out.mVerts.size();
- entry.first->SampleDiscrete(out);
- if (!entry.second && curCnt != out.mVerts.size()) {
- std::reverse(out.mVerts.begin() + curCnt, out.mVerts.end());
- }
- }
- }
- // --------------------------------------------------
- ParamRange GetParametricRange() const override {
- return std::make_pair(static_cast<IfcFloat>( 0. ),total);
- }
- private:
- std::vector< CurveEntry > curves;
- IfcFloat total;
- };
- // --------------------------------------------------------------------------------
- // TrimmedCurve can be used to trim an unbounded curve to a bounded range
- // --------------------------------------------------------------------------------
- class TrimmedCurve : public BoundedCurve {
- public:
- // --------------------------------------------------
- TrimmedCurve(const Schema_2x3::IfcTrimmedCurve& entity, ConversionData& conv)
- : BoundedCurve(entity,conv),
- base(std::shared_ptr<const Curve>(Curve::Convert(entity.BasisCurve,conv)))
- {
- typedef std::shared_ptr<const STEP::EXPRESS::DataType> Entry;
- // for some reason, trimmed curves can either specify a parametric value
- // or a point on the curve, or both. And they can even specify which of the
- // two representations they prefer, even though an information invariant
- // claims that they must be identical if both are present.
- // oh well.
- bool have_param = false, have_point = false;
- IfcVector3 point;
- for(const Entry& sel :entity.Trim1) {
- if (const ::Assimp::STEP::EXPRESS::REAL* const r = sel->ToPtr<::Assimp::STEP::EXPRESS::REAL>()) {
- range.first = *r;
- have_param = true;
- break;
- }
- else if (const Schema_2x3::IfcCartesianPoint* const curR = sel->ResolveSelectPtr<Schema_2x3::IfcCartesianPoint>(conv.db)) {
- ConvertCartesianPoint(point, *curR);
- have_point = true;
- }
- }
- if (!have_param) {
- if (!have_point || !base->ReverseEval(point,range.first)) {
- throw CurveError("IfcTrimmedCurve: failed to read first trim parameter, ignoring curve");
- }
- }
- have_param = false, have_point = false;
- for(const Entry& sel :entity.Trim2) {
- if (const ::Assimp::STEP::EXPRESS::REAL* const r = sel->ToPtr<::Assimp::STEP::EXPRESS::REAL>()) {
- range.second = *r;
- have_param = true;
- break;
- }
- else if (const Schema_2x3::IfcCartesianPoint* const curR = sel->ResolveSelectPtr<Schema_2x3::IfcCartesianPoint>(conv.db)) {
- ConvertCartesianPoint(point, *curR);
- have_point = true;
- }
- }
- if (!have_param) {
- if (!have_point || !base->ReverseEval(point,range.second)) {
- throw CurveError("IfcTrimmedCurve: failed to read second trim parameter, ignoring curve");
- }
- }
- agree_sense = IsTrue(entity.SenseAgreement);
- if( !agree_sense ) {
- std::swap(range.first,range.second);
- }
- // "NOTE In case of a closed curve, it may be necessary to increment t1 or t2
- // by the parametric length for consistency with the sense flag."
- if (base->IsClosed()) {
- if( range.first > range.second ) {
- range.second += base->GetParametricRangeDelta();
- }
- }
- maxval = range.second-range.first;
- ai_assert(maxval >= 0);
- }
- // --------------------------------------------------
- IfcVector3 Eval(IfcFloat p) const override {
- ai_assert(InRange(p));
- return base->Eval( TrimParam(p) );
- }
- // --------------------------------------------------
- size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const override {
- ai_assert( InRange( a ) );
- ai_assert( InRange( b ) );
- return base->EstimateSampleCount(TrimParam(a),TrimParam(b));
- }
- // --------------------------------------------------
- void SampleDiscrete(TempMesh& out,IfcFloat a,IfcFloat b) const override {
- ai_assert(InRange(a));
- ai_assert(InRange(b));
- return base->SampleDiscrete(out,TrimParam(a),TrimParam(b));
- }
- // --------------------------------------------------
- ParamRange GetParametricRange() const override {
- return std::make_pair(static_cast<IfcFloat>( 0. ),maxval);
- }
- private:
- // --------------------------------------------------
- IfcFloat TrimParam(IfcFloat f) const {
- return agree_sense ? f + range.first : range.second - f;
- }
- private:
- ParamRange range;
- IfcFloat maxval;
- bool agree_sense;
- std::shared_ptr<const Curve> base;
- };
- // --------------------------------------------------------------------------------
- // PolyLine is a 'curve' defined by linear interpolation over a set of discrete points
- // --------------------------------------------------------------------------------
- class PolyLine : public BoundedCurve {
- public:
- // --------------------------------------------------
- PolyLine(const Schema_2x3::IfcPolyline& entity, ConversionData& conv)
- : BoundedCurve(entity,conv)
- {
- points.reserve(entity.Points.size());
- IfcVector3 t;
- for(const Schema_2x3::IfcCartesianPoint& cp : entity.Points) {
- ConvertCartesianPoint(t,cp);
- points.push_back(t);
- }
- }
- // --------------------------------------------------
- IfcVector3 Eval(IfcFloat p) const override {
- ai_assert(InRange(p));
- const size_t b = static_cast<size_t>(std::floor(p));
- if (b == points.size()-1) {
- return points.back();
- }
- const IfcFloat d = p-static_cast<IfcFloat>(b);
- return points[b+1] * d + points[b] * (static_cast<IfcFloat>( 1. )-d);
- }
- // --------------------------------------------------
- size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const override {
- ai_assert(InRange(a));
- ai_assert(InRange(b));
- return static_cast<size_t>( std::ceil(b) - std::floor(a) );
- }
- // --------------------------------------------------
- ParamRange GetParametricRange() const override {
- return std::make_pair(static_cast<IfcFloat>( 0. ),static_cast<IfcFloat>(points.size()-1));
- }
- private:
- std::vector<IfcVector3> points;
- };
- } // anon
- // ------------------------------------------------------------------------------------------------
- Curve* Curve::Convert(const IFC::Schema_2x3::IfcCurve& curve,ConversionData& conv) {
- if(curve.ToPtr<Schema_2x3::IfcBoundedCurve>()) {
- if(const Schema_2x3::IfcPolyline* c = curve.ToPtr<Schema_2x3::IfcPolyline>()) {
- return new PolyLine(*c,conv);
- }
- if(const Schema_2x3::IfcTrimmedCurve* c = curve.ToPtr<Schema_2x3::IfcTrimmedCurve>()) {
- return new TrimmedCurve(*c,conv);
- }
- if(const Schema_2x3::IfcCompositeCurve* c = curve.ToPtr<Schema_2x3::IfcCompositeCurve>()) {
- return new CompositeCurve(*c,conv);
- }
- }
- if(curve.ToPtr<Schema_2x3::IfcConic>()) {
- if(const Schema_2x3::IfcCircle* c = curve.ToPtr<Schema_2x3::IfcCircle>()) {
- return new Circle(*c,conv);
- }
- if(const Schema_2x3::IfcEllipse* c = curve.ToPtr<Schema_2x3::IfcEllipse>()) {
- return new Ellipse(*c,conv);
- }
- }
- if(const Schema_2x3::IfcLine* c = curve.ToPtr<Schema_2x3::IfcLine>()) {
- return new Line(*c,conv);
- }
- // XXX OffsetCurve2D, OffsetCurve3D not currently supported
- return nullptr;
- }
- #ifdef ASSIMP_BUILD_DEBUG
- // ------------------------------------------------------------------------------------------------
- bool Curve::InRange(IfcFloat u) const {
- const ParamRange range = GetParametricRange();
- if (IsClosed()) {
- return true;
- }
- const IfcFloat epsilon = Math::getEpsilon<float>();
- return u - range.first > -epsilon && range.second - u > -epsilon;
- }
- #endif
- // ------------------------------------------------------------------------------------------------
- IfcFloat Curve::GetParametricRangeDelta() const {
- const ParamRange& range = GetParametricRange();
- return std::abs(range.second - range.first);
- }
- // ------------------------------------------------------------------------------------------------
- size_t Curve::EstimateSampleCount(IfcFloat a, IfcFloat b) const {
- (void)(a); (void)(b);
- ai_assert( InRange( a ) );
- ai_assert( InRange( b ) );
- // arbitrary default value, deriving classes should supply better-suited values
- return 16;
- }
- // ------------------------------------------------------------------------------------------------
- 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) {
- ai_assert(samples>1);
- const IfcFloat delta = (b-a)/samples, inf = std::numeric_limits<IfcFloat>::infinity();
- IfcFloat min_point[2] = {a,b}, min_diff[2] = {inf,inf};
- IfcFloat runner = a;
- for (unsigned int i = 0; i < samples; ++i, runner += delta) {
- const IfcFloat diff = (cv->Eval(runner)-val).SquareLength();
- if (diff < min_diff[0]) {
- min_diff[1] = min_diff[0];
- min_point[1] = min_point[0];
- min_diff[0] = diff;
- min_point[0] = runner;
- }
- else if (diff < min_diff[1]) {
- min_diff[1] = diff;
- min_point[1] = runner;
- }
- }
- #ifndef __INTEL_LLVM_COMPILER
- ai_assert( min_diff[ 0 ] != inf );
- ai_assert( min_diff[ 1 ] != inf );
- #endif // __INTEL_LLVM_COMPILER
- if ( std::fabs(a-min_point[0]) < threshold || recurse >= max_recurse) {
- return min_point[0];
- }
- // fix for closed curves to take their wrap-over into account
- if (cv->IsClosed() && std::fabs(min_point[0]-min_point[1]) > cv->GetParametricRangeDelta()*0.5 ) {
- const Curve::ParamRange& range = cv->GetParametricRange();
- const IfcFloat wrapdiff = (cv->Eval(range.first)-val).SquareLength();
- if (wrapdiff < min_diff[0]) {
- const IfcFloat t = min_point[0];
- min_point[0] = min_point[1] > min_point[0] ? range.first : range.second;
- min_point[1] = t;
- }
- }
- return RecursiveSearch(cv,val,min_point[0],min_point[1],samples,threshold,recurse+1,max_recurse);
- }
- // ------------------------------------------------------------------------------------------------
- bool Curve::ReverseEval(const IfcVector3& val, IfcFloat& paramOut) const
- {
- // note: the following algorithm is not guaranteed to find the 'right' parameter value
- // in all possible cases, but it will always return at least some value so this function
- // will never fail in the default implementation.
- // XXX derive threshold from curve topology
- static const IfcFloat threshold = 1e-4f;
- static const unsigned int samples = 16;
- const ParamRange& range = GetParametricRange();
- paramOut = RecursiveSearch(this,val,range.first,range.second,samples,threshold);
- return true;
- }
- // ------------------------------------------------------------------------------------------------
- void Curve::SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const {
- ai_assert( InRange( a ) );
- ai_assert( InRange( b ) );
- const size_t cnt = std::max(static_cast<size_t>(0),EstimateSampleCount(a,b));
- out.mVerts.reserve( out.mVerts.size() + cnt + 1);
- IfcFloat p = a, delta = (b-a)/cnt;
- for(size_t i = 0; i <= cnt; ++i, p += delta) {
- out.mVerts.push_back(Eval(p));
- }
- }
- // ------------------------------------------------------------------------------------------------
- bool BoundedCurve::IsClosed() const {
- return false;
- }
- // ------------------------------------------------------------------------------------------------
- void BoundedCurve::SampleDiscrete(TempMesh& out) const {
- const ParamRange& range = GetParametricRange();
- #ifndef __INTEL_LLVM_COMPILER
- ai_assert( range.first != std::numeric_limits<IfcFloat>::infinity() );
- ai_assert( range.second != std::numeric_limits<IfcFloat>::infinity() );
- #endif // __INTEL_LLVM_COMPILER
- return SampleDiscrete(out,range.first,range.second);
- }
- } // IFC
- } // Assimp
- #endif // ASSIMP_BUILD_NO_IFC_IMPORTER
|