From 409eb6cee754b26f130ee12d9d1f18b12d55df24 Mon Sep 17 00:00:00 2001 From: aramis_acg Date: Mon, 18 Jul 2011 01:13:40 +0000 Subject: [PATCH] - IFC: IfcTrimmingCurve sampling code now supports trimming by points rather than by parameter values. git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@1052 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- code/IFCCurve.cpp | 105 +++++++++++++++++++++++++++++++++++++------- code/IFCProfile.cpp | 8 +++- code/IFCUtil.h | 20 +++++++++ 3 files changed, 115 insertions(+), 18 deletions(-) diff --git a/code/IFCCurve.cpp b/code/IFCCurve.cpp index 428a9a747..b9806f5ef 100644 --- a/code/IFCCurve.cpp +++ b/code/IFCCurve.cpp @@ -80,11 +80,14 @@ public: bool IsClosed() const { return true; } - + // -------------------------------------------------- size_t EstimateSampleCount(float a, float b) const { ai_assert(InRange(a) && InRange(b)); - return static_cast( ceil((b-a) / conv.settings.conicSamplingAngle) ); + + a = fmod(a,360.f); + b = fmod(b,360.f); + return static_cast( fabs(ceil(( b-a)) / conv.settings.conicSamplingAngle) ); } // -------------------------------------------------- @@ -340,6 +343,7 @@ public: TrimmedCurve(const IfcTrimmedCurve& entity, ConversionData& conv) : BoundedCurve(entity,conv) , entity(entity) + , ok() { base = boost::shared_ptr(Curve::Convert(entity.BasisCurve,conv)); @@ -350,34 +354,39 @@ public: // two representations they prefer, even though an information invariant // claims that they must be identical if both are present. // oh well. - bool ok = false; + bool have_param = false, have_point = false; + aiVector3D point; BOOST_FOREACH(const Entry sel,entity.Trim1) { if (const EXPRESS::REAL* const r = sel->ToPtr()) { range.first = *r; - ok = true; + have_param = true; break; } - } - if (!ok) { - IFCImporter::LogError("trimming by curve points not currently supported, skipping first cut point"); - range.first = base->GetParametricRange().first; - if (range.first == std::numeric_limits::infinity()) { - range.first = 0; + else if (const IfcCartesianPoint* const r = sel->ResolveSelectPtr(conv.db)) { + ConvertCartesianPoint(point,*r); + have_point = true; } } - ok = false; + 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; BOOST_FOREACH(const Entry sel,entity.Trim2) { if (const EXPRESS::REAL* const r = sel->ToPtr()) { range.second = *r; - ok = true; + have_param = true; break; } + else if (const IfcCartesianPoint* const r = sel->ResolveSelectPtr(conv.db)) { + ConvertCartesianPoint(point,*r); + have_point = true; + } } - if (!ok) { - IFCImporter::LogError("trimming by curve points not currently supported, skipping second cut point"); - range.second = base->GetParametricRange().second; - if (range.second == std::numeric_limits::infinity()) { - range.second = 0; + if (!have_param) { + if (!have_point || !base->ReverseEval(point,range.second)) { + throw CurveError("IfcTrimmedCurve: failed to read second trim parameter, ignoring curve"); } } @@ -430,6 +439,7 @@ private: ParamRange range; float maxval; bool agree_sense; + bool ok; boost::shared_ptr base; }; @@ -556,6 +566,67 @@ size_t Curve :: EstimateSampleCount(float a, float b) const return 16; } +// ------------------------------------------------------------------------------------------------ +float RecursiveSearch(const Curve* cv, const aiVector3D& val, float a, float b, unsigned int samples, float treshold, unsigned int recurse = 0, unsigned int max_recurse = 15) +{ + ai_assert(samples>1); + + const float delta = (b-a)/samples, inf = std::numeric_limits::infinity(); + float min_point[2] = {a,b}, min_diff[2] = {inf,inf}; + float runner = a; + + for (unsigned int i = 0; i < samples; ++i, runner += delta) { + const float 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; + } + } + + ai_assert(min_diff[0] != inf && min_diff[1] != inf); + if ( fabs(a-min_point[0]) < treshold || recurse >= max_recurse) { + return min_point[0]; + } + + // fix for closed curves to take their wrap-over into account + if (cv->IsClosed() && fabs(min_point[0]-min_point[1]) > cv->GetParametricRangeDelta()*0.5 ) { + const Curve::ParamRange& range = cv->GetParametricRange(); + const float wrapdiff = (cv->Eval(range.first)-val).SquareLength(); + + if (wrapdiff < min_diff[0]) { + const float 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,treshold,recurse+1,max_recurse); +} + +// ------------------------------------------------------------------------------------------------ +bool Curve :: ReverseEval(const aiVector3D& val, float& 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 treshold from curve topology + const float treshold = 1e-4; + const unsigned int samples = 16; + + const ParamRange& range = GetParametricRange(); + paramOut = RecursiveSearch(this,val,range.first,range.second,samples,treshold); + + return true; +} + // ------------------------------------------------------------------------------------------------ void Curve :: SampleDiscrete(TempMesh& out,float a, float b) const { diff --git a/code/IFCProfile.cpp b/code/IFCProfile.cpp index 2c641e97d..953e4fcc2 100644 --- a/code/IFCProfile.cpp +++ b/code/IFCProfile.cpp @@ -73,7 +73,13 @@ bool ProcessCurve(const IfcCurve& curve, TempMesh& meshout, ConversionData& con // we must have a bounded curve at this point if (const BoundedCurve* bc = dynamic_cast(cv.get())) { - bc->SampleDiscrete(meshout); + try { + bc->SampleDiscrete(meshout); + } + catch(const CurveError& cv) { + IFCImporter::LogError(cv.s+ " (error occurred while processing curve)"); + return false; + } meshout.vertcnt.push_back(meshout.verts.size()); return true; } diff --git a/code/IFCUtil.h b/code/IFCUtil.h index 75c3aeb61..92b04d0e1 100644 --- a/code/IFCUtil.h +++ b/code/IFCUtil.h @@ -194,6 +194,21 @@ void AssignAddedMeshes(std::vector& mesh_indices,aiNode* nd,Conver // IFCCurve.cpp +// ------------------------------------------------------------------------------------------------ +// Custom exception for use by members of the Curve class +// ------------------------------------------------------------------------------------------------ +class CurveError +{ +public: + CurveError(const std::string& s) + : s(s) + { + } + + std::string s; +}; + + // ------------------------------------------------------------------------------------------------ // Temporary representation for an arbitrary sub-class of IfcCurve. Used to sample the curves // to obtain a list of line segments. @@ -219,6 +234,11 @@ public: // evaluate the curve at the given parametric position virtual aiVector3D Eval(float p) const = 0; + // try to match a point on the curve to a given parameter + // for self-intersecting curves, the result is not ambiguous and + // it is undefined which parameter is returned. + virtual bool ReverseEval(const aiVector3D& val, float& paramOut) const; + // get the range of the curve (both inclusive). // +inf and -inf are valid return values, the curve is not bounded in such a case. virtual std::pair GetParametricRange() const = 0;