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