diff --git a/code/IFCCurve.cpp b/code/IFCCurve.cpp index 67f6046fe..428a9a747 100644 --- a/code/IFCCurve.cpp +++ b/code/IFCCurve.cpp @@ -77,7 +77,18 @@ public: public: // -------------------------------------------------- - std::pair GetParametricRange() const { + 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) ); + } + + // -------------------------------------------------- + ParamRange GetParametricRange() const { return std::make_pair(0.f,360.f); } @@ -161,13 +172,40 @@ public: public: + // -------------------------------------------------- + bool IsClosed() const { + return false; + } + // -------------------------------------------------- aiVector3D Eval(float u) const { return p + u*v; } // -------------------------------------------------- - std::pair GetParametricRange() const { + size_t EstimateSampleCount(float a, float b) const { + ai_assert(InRange(a) && InRange(b)); + // two points are always sufficient for a line segment + return a==b ? 1 : 2; + } + + + // -------------------------------------------------- + void SampleDiscrete(TempMesh& out,float a, float b) const + { + ai_assert(InRange(a) && InRange(b)); + + if (a == b) { + out.verts.push_back(Eval(a)); + return; + } + out.verts.reserve(out.verts.size()+2); + out.verts.push_back(Eval(a)); + out.verts.push_back(Eval(b)); + } + + // -------------------------------------------------- + ParamRange GetParametricRange() const { const float inf = std::numeric_limits::infinity(); return std::make_pair(-inf,+inf); @@ -218,7 +256,7 @@ public: total = 0.f; BOOST_FOREACH(boost::shared_ptr< const BoundedCurve > curve, curves) { - const std::pair range = curve->GetParametricRange(); + const ParamRange range = curve->GetParametricRange(); total += range.second-range.first; } } @@ -233,7 +271,7 @@ public: float acc = 0; BOOST_FOREACH(boost::shared_ptr< const BoundedCurve > curve, curves) { - const std::pair range = curve->GetParametricRange(); + const ParamRange& range = curve->GetParametricRange(); const float delta = range.second-range.first; if (u < acc+delta) { return curve->Eval( (u-acc) + range.first ); @@ -246,22 +284,39 @@ public: } // -------------------------------------------------- - float SuggestNext(float u) const { + size_t EstimateSampleCount(float a, float b) const { + ai_assert(InRange(a) && InRange(b)); + size_t cnt = 0; float acc = 0; BOOST_FOREACH(boost::shared_ptr< const BoundedCurve > curve, curves) { - const std::pair range = curve->GetParametricRange(); + const ParamRange& range = curve->GetParametricRange(); const float delta = range.second-range.first; - if (u < acc+delta) { - return curve->SuggestNext( (u-acc) + range.first ) - range.first + acc; + if (a <= acc+delta && b >= acc) { + cnt += curve->EstimateSampleCount( std::max(0.f,a-acc) + range.first, std::min(delta,b-acc) + range.first ); } + acc += delta; } - return std::numeric_limits::infinity(); + + return cnt; } // -------------------------------------------------- - std::pair GetParametricRange() const { + void SampleDiscrete(TempMesh& out,float a, float b) const + { + ai_assert(InRange(a) && InRange(b)); + + const size_t cnt = EstimateSampleCount(a,b); + out.verts.reserve(out.verts.size() + cnt); + + BOOST_FOREACH(boost::shared_ptr< const BoundedCurve > curve, curves) { + curve->SampleDiscrete(out); + } + } + + // -------------------------------------------------- + ParamRange GetParametricRange() const { return std::make_pair(0.f,total); } @@ -326,48 +381,55 @@ public: } } + 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); } public: // -------------------------------------------------- aiVector3D Eval(float p) const { - p = std::min(range.second, std::max(range.first+p,range.first)); - if (!IsTrue(entity.SenseAgreement)) { - p = range.second - (p-range.first); - } - return base->Eval( p ); + ai_assert(InRange(p)); + return base->Eval( TrimParam(p) ); } // -------------------------------------------------- - float SuggestNext(float u) const { - if (u >= maxval) { - return std::numeric_limits::infinity(); - } - - if (const Line* const l = dynamic_cast(base.get())) { - // a line is, well, a line .. so two points are always sufficient to represent it - return maxval; - } - - if (const Conic* const l = dynamic_cast(base.get())) { - // the suitable sampling density for conics is a configuration property - return std::min(maxval, static_cast( u + maxval/ceil(maxval/conv.settings.conicSamplingAngle)) ); - } - - return BoundedCurve::SuggestNext(u); + size_t EstimateSampleCount(float a, float b) const { + ai_assert(InRange(a) && InRange(b)); + return base->EstimateSampleCount(TrimParam(a),TrimParam(b)); } // -------------------------------------------------- - std::pair GetParametricRange() const { + ParamRange GetParametricRange() const { return std::make_pair(0,maxval); } +private: + + // -------------------------------------------------- + float TrimParam(float f) const { + return agree_sense ? f + range.first : range.second - f; + } + + private: const IfcTrimmedCurve& entity; - std::pair range; + ParamRange range; float maxval; + bool agree_sense; boost::shared_ptr base; }; @@ -399,11 +461,10 @@ public: // -------------------------------------------------- aiVector3D Eval(float p) const { - if (p < 0.f) { - return points.front(); - } + ai_assert(InRange(p)); + const size_t b = static_cast(floor(p)); - if (b >= points.size()-1) { + if (b == points.size()-1) { return points.back(); } @@ -412,15 +473,13 @@ public: } // -------------------------------------------------- - float SuggestNext(float u) const { - if (u > points.size()-1) { - return std::numeric_limits::infinity(); - } - return ::floor(u)+1; + size_t EstimateSampleCount(float a, float b) const { + ai_assert(InRange(a) && InRange(b)); + return static_cast( ceil(b) - floor(a) ); } // -------------------------------------------------- - std::pair GetParametricRange() const { + ParamRange GetParametricRange() const { return std::make_pair(0.f,static_cast(points.size()-1)); } @@ -468,40 +527,62 @@ Curve* Curve :: Convert(const IFC::IfcCurve& curve,ConversionData& conv) return NULL; } +#ifdef _DEBUG // ------------------------------------------------------------------------------------------------ -float BoundedCurve :: SuggestNext(float u) const +bool Curve :: InRange(float u) const { - // the default behavior is to subdivide each curve into approximately 32 linear segments - const unsigned int segments = 32; - - const std::pair range = GetParametricRange(); - const float delta = range.second - range.first, perseg = delta/segments; - - if (u < range.first) { - return range.first; + const ParamRange range = GetParametricRange(); + if (IsClosed()) { + ai_assert(range.first != std::numeric_limits::infinity() && range.second != std::numeric_limits::infinity()); + u = range.first + fmod(u-range.first,range.second-range.first); } + return u >= range.first && u <= range.second; +} +#endif - u = u+perseg; - if (u > range.second) { - return std::numeric_limits::infinity(); +// ------------------------------------------------------------------------------------------------ +float Curve :: GetParametricRangeDelta() const +{ + const ParamRange& range = GetParametricRange(); + return range.second - range.first; +} + +// ------------------------------------------------------------------------------------------------ +size_t Curve :: EstimateSampleCount(float a, float b) const +{ + ai_assert(InRange(a) && InRange(b)); + + // arbitrary default value, deriving classes should supply better suited values + return 16; +} + +// ------------------------------------------------------------------------------------------------ +void Curve :: SampleDiscrete(TempMesh& out,float a, float b) const +{ + ai_assert(InRange(a) && InRange(b)); + + const size_t cnt = std::max(static_cast(0),EstimateSampleCount(a,b)); + out.verts.reserve( out.verts.size() + cnt ); + + float p = a, delta = (b-a)/cnt; + for(size_t i = 0; i < cnt; ++i, p += delta) { + out.verts.push_back(Eval(p)); } +} - return u; +// ------------------------------------------------------------------------------------------------ +bool BoundedCurve :: IsClosed() const +{ + return false; } // ------------------------------------------------------------------------------------------------ void BoundedCurve :: SampleDiscrete(TempMesh& out) const { - const std::pair range = GetParametricRange(); - const float inf = std::numeric_limits::infinity(); + const ParamRange& range = GetParametricRange(); + ai_assert(range.first != std::numeric_limits::infinity() && range.second != std::numeric_limits::infinity()); - size_t cnt = 0; - float u = range.first; - do ++cnt; while( (u = SuggestNext(u)) != inf ); - out.verts.reserve(cnt); - - u = range.first; - do out.verts.push_back(Eval(u)); while( (u = SuggestNext(u)) != inf ); + return SampleDiscrete(out,range.first,range.second); } } // IFC diff --git a/code/IFCUtil.h b/code/IFCUtil.h index d9cb27572..75c3aeb61 100644 --- a/code/IFCUtil.h +++ b/code/IFCUtil.h @@ -209,12 +209,32 @@ protected: public: + typedef std::pair ParamRange; + +public: + + // check if a curve is closed + virtual bool IsClosed() const = 0; + // evaluate the curve at the given parametric position virtual aiVector3D Eval(float p) const = 0; // 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; + float GetParametricRangeDelta() const; + + // estimate the number of sample points that this curve will require + virtual size_t EstimateSampleCount(float start,float end) const; + + // intelligently sample the curve based on the current settings + // and append the result to the mesh + virtual void SampleDiscrete(TempMesh& out,float start,float end) const; + +#ifdef _DEBUG + // check if a particular parameter value lies within the well-defined range + bool InRange(float) const; +#endif public: @@ -241,18 +261,13 @@ public: public: - // given a point on the curve, suggest a suitable next point for - // discrete sampling. The returned parameter value should be - // based on two considerations: - // a) curve geometry is to be preserved - // b) detail level should be chosen based on importer settings - // and (possibly) importance and dimension of the spatial - // structure. - // return +inf if the suggestion is to stop sampling. - virtual float SuggestNext(float u) const; + bool IsClosed() const; - // intelligently sample the curve based on Eval() and SuggestNext() +public: + + // sample the entire curve void SampleDiscrete(TempMesh& out) const; + using Curve::SampleDiscrete; };