Browse Source

- 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
aramis_acg 14 years ago
parent
commit
409eb6cee7
3 changed files with 114 additions and 17 deletions
  1. 87 16
      code/IFCCurve.cpp
  2. 7 1
      code/IFCProfile.cpp
  3. 20 0
      code/IFCUtil.h

+ 87 - 16
code/IFCCurve.cpp

@@ -80,11 +80,14 @@ public:
 	bool IsClosed() const {
 	bool IsClosed() const {
 		return true;
 		return true;
 	}
 	}
-
+	
 	// --------------------------------------------------
 	// --------------------------------------------------
 	size_t EstimateSampleCount(float a, float b) const {
 	size_t EstimateSampleCount(float a, float b) const {
 		ai_assert(InRange(a) && InRange(b));
 		ai_assert(InRange(a) && InRange(b));
-		return static_cast<size_t>( ceil((b-a) / conv.settings.conicSamplingAngle) );
+
+		a = fmod(a,360.f);
+		b = fmod(b,360.f);
+		return static_cast<size_t>( fabs(ceil(( b-a)) / conv.settings.conicSamplingAngle) );
 	}
 	}
 
 
 	// --------------------------------------------------
 	// --------------------------------------------------
@@ -340,6 +343,7 @@ public:
 	TrimmedCurve(const IfcTrimmedCurve& entity, ConversionData& conv) 
 	TrimmedCurve(const IfcTrimmedCurve& entity, ConversionData& conv) 
 		: BoundedCurve(entity,conv)
 		: BoundedCurve(entity,conv)
 		, entity(entity)
 		, entity(entity)
+		, ok()
 	{
 	{
 		base = boost::shared_ptr<const Curve>(Curve::Convert(entity.BasisCurve,conv));
 		base = boost::shared_ptr<const Curve>(Curve::Convert(entity.BasisCurve,conv));
 
 
@@ -350,34 +354,39 @@ public:
 		// two representations they prefer, even though an information invariant
 		// two representations they prefer, even though an information invariant
 		// claims that they must be identical if both are present.
 		// claims that they must be identical if both are present.
 		// oh well.
 		// oh well.
-		bool ok = false;
+		bool have_param = false, have_point = false;
+		aiVector3D point;
 		BOOST_FOREACH(const Entry sel,entity.Trim1) {
 		BOOST_FOREACH(const Entry sel,entity.Trim1) {
 			if (const EXPRESS::REAL* const r = sel->ToPtr<EXPRESS::REAL>()) {
 			if (const EXPRESS::REAL* const r = sel->ToPtr<EXPRESS::REAL>()) {
 				range.first = *r;
 				range.first = *r;
-				ok = true;
+				have_param = true;
 				break;
 				break;
 			}
 			}
+			else if (const IfcCartesianPoint* const r = sel->ResolveSelectPtr<IfcCartesianPoint>(conv.db)) {
+				ConvertCartesianPoint(point,*r);
+				have_point = true;
+			}
 		}
 		}
-		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<float>::infinity()) {
-				range.first = 0;
+		if (!have_param) {
+			if (!have_point || !base->ReverseEval(point,range.first)) {
+				throw CurveError("IfcTrimmedCurve: failed to read first trim parameter, ignoring curve");
 			}
 			}
 		}
 		}
-		ok = false;
+		have_param = false, have_point = false;
 		BOOST_FOREACH(const Entry sel,entity.Trim2) {
 		BOOST_FOREACH(const Entry sel,entity.Trim2) {
 			if (const EXPRESS::REAL* const r = sel->ToPtr<EXPRESS::REAL>()) {
 			if (const EXPRESS::REAL* const r = sel->ToPtr<EXPRESS::REAL>()) {
 				range.second = *r;
 				range.second = *r;
-				ok = true;
+				have_param = true;
 				break;
 				break;
 			}
 			}
+			else if (const IfcCartesianPoint* const r = sel->ResolveSelectPtr<IfcCartesianPoint>(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<float>::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;
 	ParamRange range;
 	float maxval;
 	float maxval;
 	bool agree_sense;
 	bool agree_sense;
+	bool ok;
 
 
 	boost::shared_ptr<const Curve> base;
 	boost::shared_ptr<const Curve> base;
 };
 };
@@ -556,6 +566,67 @@ size_t Curve :: EstimateSampleCount(float a, float b) const
 	return 16;
 	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<float>::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
 void Curve :: SampleDiscrete(TempMesh& out,float a, float b) const
 {
 {

+ 7 - 1
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
 	// we must have a bounded curve at this point
 	if (const BoundedCurve* bc = dynamic_cast<const BoundedCurve*>(cv.get())) {
 	if (const BoundedCurve* bc = dynamic_cast<const BoundedCurve*>(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());
 		meshout.vertcnt.push_back(meshout.verts.size());
 		return true;
 		return true;
 	}
 	}

+ 20 - 0
code/IFCUtil.h

@@ -194,6 +194,21 @@ void AssignAddedMeshes(std::vector<unsigned int>& mesh_indices,aiNode* nd,Conver
 
 
 // IFCCurve.cpp
 // 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
 // Temporary representation for an arbitrary sub-class of IfcCurve. Used to sample the curves
 // to obtain a list of line segments.
 // to obtain a list of line segments.
@@ -219,6 +234,11 @@ public:
 	// evaluate the curve at the given parametric position
 	// evaluate the curve at the given parametric position
 	virtual aiVector3D Eval(float p) const = 0;
 	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).
 	// get the range of the curve (both inclusive).
 	// +inf and -inf are valid return values, the curve is not bounded in such a case.
 	// +inf and -inf are valid return values, the curve is not bounded in such a case.
 	virtual std::pair<float,float> GetParametricRange() const = 0;
 	virtual std::pair<float,float> GetParametricRange() const = 0;