2015-05-19 03:48:29 +00:00
|
|
|
/*
|
|
|
|
Open Asset Import Library (assimp)
|
|
|
|
----------------------------------------------------------------------
|
|
|
|
|
2020-01-20 13:53:12 +00:00
|
|
|
Copyright (c) 2006-2020, assimp team
|
2018-01-28 18:42:05 +00:00
|
|
|
|
2017-05-09 17:57:36 +00:00
|
|
|
|
2015-05-19 03:48:29 +00:00
|
|
|
All rights reserved.
|
|
|
|
|
2015-05-19 03:52:10 +00:00
|
|
|
Redistribution and use of this software in source and binary forms,
|
|
|
|
with or without modification, are permitted provided that the
|
2015-05-19 03:48:29 +00:00
|
|
|
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.
|
|
|
|
|
2015-05-19 03:52:10 +00:00
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
2015-05-19 03:48:29 +00:00
|
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
2015-05-19 03:52:10 +00:00
|
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
2015-05-19 03:48:29 +00:00
|
|
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
2015-05-19 03:52:10 +00:00
|
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
2015-05-19 03:48:29 +00:00
|
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
2015-05-19 03:52:10 +00:00
|
|
|
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
|
2015-05-19 03:48:29 +00:00
|
|
|
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 {
|
2017-10-29 19:28:59 +00:00
|
|
|
namespace IFC {
|
|
|
|
namespace {
|
2015-05-19 03:48:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
// Conic is the base class for Circle and Ellipse
|
|
|
|
// --------------------------------------------------------------------------------
|
2017-10-29 19:28:59 +00:00
|
|
|
class Conic : public Curve {
|
2015-05-19 03:48:29 +00:00
|
|
|
public:
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
2018-01-13 09:27:45 +00:00
|
|
|
Conic(const Schema_2x3::IfcConic& entity, ConversionData& conv)
|
2017-10-29 19:28:59 +00:00
|
|
|
: Curve(entity,conv) {
|
2015-05-19 03:57:13 +00:00
|
|
|
IfcMatrix4 trafo;
|
|
|
|
ConvertAxisPlacement(trafo,*entity.Position,conv);
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
// 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);
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
|
|
|
bool IsClosed() const {
|
|
|
|
return true;
|
|
|
|
}
|
2015-05-19 03:52:10 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
|
|
|
size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
|
2017-10-29 19:28:59 +00:00
|
|
|
ai_assert( InRange( a ) );
|
|
|
|
ai_assert( InRange( b ) );
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
a *= conv.angle_scale;
|
|
|
|
b *= conv.angle_scale;
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
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);
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
|
|
|
ParamRange GetParametricRange() const {
|
|
|
|
return std::make_pair(static_cast<IfcFloat>( 0. ), static_cast<IfcFloat>( AI_MATH_TWO_PI / conv.angle_scale ));
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
|
|
|
protected:
|
2015-05-19 03:57:13 +00:00
|
|
|
IfcVector3 location, p[3];
|
2015-05-19 03:48:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
// Circle
|
|
|
|
// --------------------------------------------------------------------------------
|
2017-10-29 19:28:59 +00:00
|
|
|
class Circle : public Conic {
|
2015-05-19 03:48:29 +00:00
|
|
|
public:
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
2018-01-13 09:27:45 +00:00
|
|
|
Circle(const Schema_2x3::IfcCircle& entity, ConversionData& conv)
|
2015-05-19 03:57:13 +00:00
|
|
|
: Conic(entity,conv)
|
|
|
|
, entity(entity)
|
|
|
|
{
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
|
|
|
IfcVector3 Eval(IfcFloat u) const {
|
|
|
|
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]);
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
|
|
|
private:
|
2018-01-13 09:27:45 +00:00
|
|
|
const Schema_2x3::IfcCircle& entity;
|
2015-05-19 03:48:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
// Ellipse
|
|
|
|
// --------------------------------------------------------------------------------
|
2017-10-29 19:28:59 +00:00
|
|
|
class Ellipse : public Conic {
|
2015-05-19 03:48:29 +00:00
|
|
|
public:
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
2018-01-13 09:27:45 +00:00
|
|
|
Ellipse(const Schema_2x3::IfcEllipse& entity, ConversionData& conv)
|
2017-10-29 19:28:59 +00:00
|
|
|
: Conic(entity,conv)
|
|
|
|
, entity(entity) {
|
|
|
|
// empty
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
|
|
|
IfcVector3 Eval(IfcFloat u) const {
|
|
|
|
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];
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
|
|
|
private:
|
2018-01-13 09:27:45 +00:00
|
|
|
const Schema_2x3::IfcEllipse& entity;
|
2015-05-19 03:48:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
// Line
|
|
|
|
// --------------------------------------------------------------------------------
|
2017-10-29 19:28:59 +00:00
|
|
|
class Line : public Curve {
|
2015-05-19 03:48:29 +00:00
|
|
|
public:
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
2018-01-13 09:27:45 +00:00
|
|
|
Line(const Schema_2x3::IfcLine& entity, ConversionData& conv)
|
2017-10-29 19:28:59 +00:00
|
|
|
: Curve(entity,conv) {
|
2015-05-19 03:57:13 +00:00
|
|
|
ConvertCartesianPoint(p,entity.Pnt);
|
|
|
|
ConvertVector(v,entity.Dir);
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
|
|
|
bool IsClosed() const {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
IfcVector3 Eval(IfcFloat u) const {
|
|
|
|
return p + u*v;
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
|
2017-10-29 19:28:59 +00:00
|
|
|
ai_assert( InRange( a ) );
|
|
|
|
ai_assert( InRange( b ) );
|
2015-05-19 03:57:13 +00:00
|
|
|
// two points are always sufficient for a line segment
|
|
|
|
return a==b ? 1 : 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------
|
2017-10-29 19:28:59 +00:00
|
|
|
void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const {
|
|
|
|
ai_assert( InRange( a ) );
|
|
|
|
ai_assert( InRange( b ) );
|
2015-05-19 03:57:13 +00:00
|
|
|
|
|
|
|
if (a == b) {
|
2018-01-13 09:27:45 +00:00
|
|
|
out.mVerts.push_back(Eval(a));
|
2015-05-19 03:57:13 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-01-13 09:27:45 +00:00
|
|
|
out.mVerts.reserve(out.mVerts.size()+2);
|
|
|
|
out.mVerts.push_back(Eval(a));
|
|
|
|
out.mVerts.push_back(Eval(b));
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
ParamRange GetParametricRange() const {
|
|
|
|
const IfcFloat inf = std::numeric_limits<IfcFloat>::infinity();
|
|
|
|
|
|
|
|
return std::make_pair(-inf,+inf);
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
|
|
|
private:
|
2015-05-19 03:57:13 +00:00
|
|
|
IfcVector3 p,v;
|
2015-05-19 03:48:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
// CompositeCurve joins multiple smaller, bounded curves
|
|
|
|
// --------------------------------------------------------------------------------
|
2017-10-29 19:28:59 +00:00
|
|
|
class CompositeCurve : public BoundedCurve {
|
2016-04-05 21:23:53 +00:00
|
|
|
typedef std::pair< std::shared_ptr< BoundedCurve >, bool > CurveEntry;
|
2015-05-19 03:48:29 +00:00
|
|
|
|
|
|
|
public:
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
2018-01-13 09:27:45 +00:00
|
|
|
CompositeCurve(const Schema_2x3::IfcCompositeCurve& entity, ConversionData& conv)
|
2017-10-29 19:28:59 +00:00
|
|
|
: BoundedCurve(entity,conv)
|
|
|
|
, total() {
|
2015-05-19 03:57:13 +00:00
|
|
|
curves.reserve(entity.Segments.size());
|
2018-01-13 09:27:45 +00:00
|
|
|
for(const Schema_2x3::IfcCompositeCurveSegment& curveSegment :entity.Segments) {
|
2015-05-19 03:57:13 +00:00
|
|
|
// according to the specification, this must be a bounded curve
|
2016-04-05 21:23:53 +00:00
|
|
|
std::shared_ptr< Curve > cv(Curve::Convert(curveSegment.ParentCurve,conv));
|
|
|
|
std::shared_ptr< BoundedCurve > bc = std::dynamic_pointer_cast<BoundedCurve>(cv);
|
2015-05-19 03:57:13 +00:00
|
|
|
|
|
|
|
if (!bc) {
|
|
|
|
IFCImporter::LogError("expected segment of composite curve to be a bounded curve");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( (std::string)curveSegment.Transition != "CONTINUOUS" ) {
|
2020-05-18 10:55:14 +00:00
|
|
|
IFCImporter::LogVerboseDebug("ignoring transition code on composite curve segment, only continuous transitions are supported");
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
curves.push_back( CurveEntry(bc,IsTrue(curveSegment.SameSense)) );
|
|
|
|
total += bc->GetParametricRangeDelta();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (curves.empty()) {
|
|
|
|
throw CurveError("empty composite curve");
|
|
|
|
}
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
|
|
|
IfcVector3 Eval(IfcFloat u) const {
|
|
|
|
if (curves.empty()) {
|
|
|
|
return IfcVector3();
|
|
|
|
}
|
|
|
|
|
|
|
|
IfcFloat acc = 0;
|
2016-04-05 20:53:54 +00:00
|
|
|
for(const CurveEntry& entry : curves) {
|
2015-05-19 03:57:13 +00:00
|
|
|
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 {
|
2017-10-29 19:28:59 +00:00
|
|
|
ai_assert( InRange( a ) );
|
|
|
|
ai_assert( InRange( b ) );
|
2015-05-19 03:57:13 +00:00
|
|
|
size_t cnt = 0;
|
|
|
|
|
|
|
|
IfcFloat acc = 0;
|
2016-04-05 20:53:54 +00:00
|
|
|
for(const CurveEntry& entry : curves) {
|
2015-05-19 03:57:13 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------
|
2017-10-29 19:28:59 +00:00
|
|
|
void SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const {
|
|
|
|
ai_assert( InRange( a ) );
|
|
|
|
ai_assert( InRange( b ) );
|
2015-05-19 03:57:13 +00:00
|
|
|
|
|
|
|
const size_t cnt = EstimateSampleCount(a,b);
|
2018-01-13 09:27:45 +00:00
|
|
|
out.mVerts.reserve(out.mVerts.size() + cnt);
|
2015-05-19 03:57:13 +00:00
|
|
|
|
2016-04-05 20:53:54 +00:00
|
|
|
for(const CurveEntry& entry : curves) {
|
2020-03-08 20:24:01 +00:00
|
|
|
const size_t curCnt = out.mVerts.size();
|
2015-05-19 03:57:13 +00:00
|
|
|
entry.first->SampleDiscrete(out);
|
|
|
|
|
2020-03-08 20:24:01 +00:00
|
|
|
if (!entry.second && curCnt != out.mVerts.size()) {
|
|
|
|
std::reverse(out.mVerts.begin() + curCnt, out.mVerts.end());
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
ParamRange GetParametricRange() const {
|
|
|
|
return std::make_pair(static_cast<IfcFloat>( 0. ),total);
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
|
|
|
private:
|
2015-05-19 03:57:13 +00:00
|
|
|
std::vector< CurveEntry > curves;
|
|
|
|
IfcFloat total;
|
2015-05-19 03:48:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
// TrimmedCurve can be used to trim an unbounded curve to a bounded range
|
|
|
|
// --------------------------------------------------------------------------------
|
2017-10-29 19:28:59 +00:00
|
|
|
class TrimmedCurve : public BoundedCurve {
|
2015-05-19 03:48:29 +00:00
|
|
|
public:
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
2018-01-13 09:27:45 +00:00
|
|
|
TrimmedCurve(const Schema_2x3::IfcTrimmedCurve& entity, ConversionData& conv)
|
2019-09-18 06:04:46 +00:00
|
|
|
: BoundedCurve(entity,conv),
|
|
|
|
base(std::shared_ptr<const Curve>(Curve::Convert(entity.BasisCurve,conv)))
|
2015-05-19 03:57:13 +00:00
|
|
|
{
|
2016-04-05 21:23:53 +00:00
|
|
|
typedef std::shared_ptr<const STEP::EXPRESS::DataType> Entry;
|
2015-05-19 03:57:13 +00:00
|
|
|
|
|
|
|
// 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;
|
2020-01-30 23:42:30 +00:00
|
|
|
for(const Entry& sel :entity.Trim1) {
|
2018-01-13 09:27:45 +00:00
|
|
|
if (const ::Assimp::STEP::EXPRESS::REAL* const r = sel->ToPtr<::Assimp::STEP::EXPRESS::REAL>()) {
|
2015-05-19 03:57:13 +00:00
|
|
|
range.first = *r;
|
|
|
|
have_param = true;
|
|
|
|
break;
|
|
|
|
}
|
2020-03-08 20:24:01 +00:00
|
|
|
else if (const Schema_2x3::IfcCartesianPoint* const curR = sel->ResolveSelectPtr<Schema_2x3::IfcCartesianPoint>(conv.db)) {
|
|
|
|
ConvertCartesianPoint(point, *curR);
|
2015-05-19 03:57:13 +00:00
|
|
|
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;
|
2020-01-30 23:42:30 +00:00
|
|
|
for(const Entry& sel :entity.Trim2) {
|
2018-01-13 09:27:45 +00:00
|
|
|
if (const ::Assimp::STEP::EXPRESS::REAL* const r = sel->ToPtr<::Assimp::STEP::EXPRESS::REAL>()) {
|
2015-05-19 03:57:13 +00:00
|
|
|
range.second = *r;
|
|
|
|
have_param = true;
|
|
|
|
break;
|
|
|
|
}
|
2020-03-08 20:24:01 +00:00
|
|
|
else if (const Schema_2x3::IfcCartesianPoint* const curR = sel->ResolveSelectPtr<Schema_2x3::IfcCartesianPoint>(conv.db)) {
|
|
|
|
ConvertCartesianPoint(point, *curR);
|
2015-05-19 03:57:13 +00:00
|
|
|
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);
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
|
|
|
IfcVector3 Eval(IfcFloat p) const {
|
|
|
|
ai_assert(InRange(p));
|
|
|
|
return base->Eval( TrimParam(p) );
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
|
2017-10-29 19:28:59 +00:00
|
|
|
ai_assert( InRange( a ) );
|
|
|
|
ai_assert( InRange( b ) );
|
2015-05-19 03:57:13 +00:00
|
|
|
return base->EstimateSampleCount(TrimParam(a),TrimParam(b));
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
void SampleDiscrete(TempMesh& out,IfcFloat a,IfcFloat b) const {
|
|
|
|
ai_assert(InRange(a) && InRange(b));
|
|
|
|
return base->SampleDiscrete(out,TrimParam(a),TrimParam(b));
|
|
|
|
}
|
|
|
|
|
|
|
|
// --------------------------------------------------
|
|
|
|
ParamRange GetParametricRange() const {
|
|
|
|
return std::make_pair(static_cast<IfcFloat>( 0. ),maxval);
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
|
|
|
private:
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
|
|
|
IfcFloat TrimParam(IfcFloat f) const {
|
|
|
|
return agree_sense ? f + range.first : range.second - f;
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
|
|
|
private:
|
2015-05-19 03:57:13 +00:00
|
|
|
ParamRange range;
|
|
|
|
IfcFloat maxval;
|
|
|
|
bool agree_sense;
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2016-04-05 21:23:53 +00:00
|
|
|
std::shared_ptr<const Curve> base;
|
2015-05-19 03:48:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// --------------------------------------------------------------------------------
|
|
|
|
// PolyLine is a 'curve' defined by linear interpolation over a set of discrete points
|
|
|
|
// --------------------------------------------------------------------------------
|
2017-10-29 19:28:59 +00:00
|
|
|
class PolyLine : public BoundedCurve {
|
2015-05-19 03:48:29 +00:00
|
|
|
public:
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
2018-01-13 09:27:45 +00:00
|
|
|
PolyLine(const Schema_2x3::IfcPolyline& entity, ConversionData& conv)
|
2015-05-19 03:57:13 +00:00
|
|
|
: BoundedCurve(entity,conv)
|
|
|
|
{
|
|
|
|
points.reserve(entity.Points.size());
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
IfcVector3 t;
|
2018-01-13 09:27:45 +00:00
|
|
|
for(const Schema_2x3::IfcCartesianPoint& cp : entity.Points) {
|
2015-05-19 03:57:13 +00:00
|
|
|
ConvertCartesianPoint(t,cp);
|
|
|
|
points.push_back(t);
|
|
|
|
}
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
|
|
|
IfcVector3 Eval(IfcFloat p) const {
|
|
|
|
ai_assert(InRange(p));
|
2015-05-19 03:52:10 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
const size_t b = static_cast<size_t>(std::floor(p));
|
|
|
|
if (b == points.size()-1) {
|
|
|
|
return points.back();
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
const IfcFloat d = p-static_cast<IfcFloat>(b);
|
|
|
|
return points[b+1] * d + points[b] * (static_cast<IfcFloat>( 1. )-d);
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
|
|
|
size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
|
|
|
|
ai_assert(InRange(a) && InRange(b));
|
|
|
|
return static_cast<size_t>( std::ceil(b) - std::floor(a) );
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
// --------------------------------------------------
|
|
|
|
ParamRange GetParametricRange() const {
|
|
|
|
return std::make_pair(static_cast<IfcFloat>( 0. ),static_cast<IfcFloat>(points.size()-1));
|
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
|
|
|
|
private:
|
2015-05-19 03:57:13 +00:00
|
|
|
std::vector<IfcVector3> points;
|
2015-05-19 03:48:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
} // anon
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
2018-01-13 09:27:45 +00:00
|
|
|
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>()) {
|
2015-05-19 03:57:13 +00:00
|
|
|
return new PolyLine(*c,conv);
|
|
|
|
}
|
2018-01-13 09:27:45 +00:00
|
|
|
if(const Schema_2x3::IfcTrimmedCurve* c = curve.ToPtr<Schema_2x3::IfcTrimmedCurve>()) {
|
2015-05-19 03:57:13 +00:00
|
|
|
return new TrimmedCurve(*c,conv);
|
|
|
|
}
|
2018-01-13 09:27:45 +00:00
|
|
|
if(const Schema_2x3::IfcCompositeCurve* c = curve.ToPtr<Schema_2x3::IfcCompositeCurve>()) {
|
2015-05-19 03:57:13 +00:00
|
|
|
return new CompositeCurve(*c,conv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-13 09:27:45 +00:00
|
|
|
if(curve.ToPtr<Schema_2x3::IfcConic>()) {
|
|
|
|
if(const Schema_2x3::IfcCircle* c = curve.ToPtr<Schema_2x3::IfcCircle>()) {
|
2015-05-19 03:57:13 +00:00
|
|
|
return new Circle(*c,conv);
|
|
|
|
}
|
2018-01-13 09:27:45 +00:00
|
|
|
if(const Schema_2x3::IfcEllipse* c = curve.ToPtr<Schema_2x3::IfcEllipse>()) {
|
2015-05-19 03:57:13 +00:00
|
|
|
return new Ellipse(*c,conv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-13 09:27:45 +00:00
|
|
|
if(const Schema_2x3::IfcLine* c = curve.ToPtr<Schema_2x3::IfcLine>()) {
|
2015-05-19 03:57:13 +00:00
|
|
|
return new Line(*c,conv);
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX OffsetCurve2D, OffsetCurve3D not currently supported
|
|
|
|
return NULL;
|
2015-05-19 03:48:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef ASSIMP_BUILD_DEBUG
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
2017-10-29 19:28:59 +00:00
|
|
|
bool Curve::InRange(IfcFloat u) const {
|
2015-05-19 03:57:13 +00:00
|
|
|
const ParamRange range = GetParametricRange();
|
|
|
|
if (IsClosed()) {
|
|
|
|
return true;
|
|
|
|
}
|
2019-09-17 19:58:46 +00:00
|
|
|
const IfcFloat epsilon = Math::getEpsilon<float>();
|
2015-05-19 03:57:13 +00:00
|
|
|
return u - range.first > -epsilon && range.second - u > -epsilon;
|
2015-05-19 03:48:29 +00:00
|
|
|
}
|
2015-05-19 03:52:10 +00:00
|
|
|
#endif
|
2015-05-19 03:48:29 +00:00
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
2017-10-29 19:28:59 +00:00
|
|
|
IfcFloat Curve::GetParametricRangeDelta() const {
|
2015-05-19 03:57:13 +00:00
|
|
|
const ParamRange& range = GetParametricRange();
|
|
|
|
return std::abs(range.second - range.first);
|
2015-05-19 03:48:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
2017-10-29 19:28:59 +00:00
|
|
|
size_t Curve::EstimateSampleCount(IfcFloat a, IfcFloat b) const {
|
2017-11-03 09:35:02 +00:00
|
|
|
(void)(a); (void)(b);
|
2017-10-29 19:28:59 +00:00
|
|
|
ai_assert( InRange( a ) );
|
|
|
|
ai_assert( InRange( b ) );
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
// arbitrary default value, deriving classes should supply better suited values
|
|
|
|
return 16;
|
2015-05-19 03:48:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
2017-10-29 19:28:59 +00:00
|
|
|
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) {
|
2015-05-19 03:57:13 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-29 19:28:59 +00:00
|
|
|
ai_assert( min_diff[ 0 ] != inf );
|
|
|
|
ai_assert( min_diff[ 1 ] != inf );
|
2015-05-19 03:57:13 +00:00
|
|
|
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);
|
2015-05-19 03:48:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
2017-10-29 19:28:59 +00:00
|
|
|
bool Curve::ReverseEval(const IfcVector3& val, IfcFloat& paramOut) const
|
2015-05-19 03:48:29 +00:00
|
|
|
{
|
2015-05-19 03:57:13 +00:00
|
|
|
// 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.
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
// XXX derive threshold from curve topology
|
2017-10-29 19:28:59 +00:00
|
|
|
static const IfcFloat threshold = 1e-4f;
|
|
|
|
static const unsigned int samples = 16;
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
const ParamRange& range = GetParametricRange();
|
|
|
|
paramOut = RecursiveSearch(this,val,range.first,range.second,samples,threshold);
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
return true;
|
2015-05-19 03:48:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
2017-10-29 19:28:59 +00:00
|
|
|
void Curve::SampleDiscrete(TempMesh& out,IfcFloat a, IfcFloat b) const {
|
|
|
|
ai_assert( InRange( a ) );
|
|
|
|
ai_assert( InRange( b ) );
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
const size_t cnt = std::max(static_cast<size_t>(0),EstimateSampleCount(a,b));
|
2018-01-13 09:27:45 +00:00
|
|
|
out.mVerts.reserve( out.mVerts.size() + cnt + 1);
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
IfcFloat p = a, delta = (b-a)/cnt;
|
|
|
|
for(size_t i = 0; i <= cnt; ++i, p += delta) {
|
2018-01-13 09:27:45 +00:00
|
|
|
out.mVerts.push_back(Eval(p));
|
2015-05-19 03:57:13 +00:00
|
|
|
}
|
2015-05-19 03:48:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
2017-10-29 19:28:59 +00:00
|
|
|
bool BoundedCurve::IsClosed() const {
|
2015-05-19 03:57:13 +00:00
|
|
|
return false;
|
2015-05-19 03:48:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
2017-10-29 19:28:59 +00:00
|
|
|
void BoundedCurve::SampleDiscrete(TempMesh& out) const {
|
2015-05-19 03:57:13 +00:00
|
|
|
const ParamRange& range = GetParametricRange();
|
2017-10-29 19:28:59 +00:00
|
|
|
ai_assert( range.first != std::numeric_limits<IfcFloat>::infinity() );
|
|
|
|
ai_assert( range.second != std::numeric_limits<IfcFloat>::infinity() );
|
2015-05-19 03:48:29 +00:00
|
|
|
|
2015-05-19 03:57:13 +00:00
|
|
|
return SampleDiscrete(out,range.first,range.second);
|
2015-05-19 03:48:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // IFC
|
|
|
|
} // Assimp
|
|
|
|
|
|
|
|
#endif // ASSIMP_BUILD_NO_IFC_IMPORTER
|