Freestyle: Fix for a potential infinite loop in stroke resampling by vertex count.

Changes were made in Stroke::Resample(int) in C++ to prevent a potential infinite loop
caused by an inconsistency between Stroke::_Length and the stroke length computed
based on stroke vertices.  Such a stroke length inconsistency is usually caused by missing
calls of Stroke::UpdateLength() (i.e., API implementation bugs), but also may occur due
to scripting errors in user-defined style modules.  This commit is meant to help script
writters to identify the latter error cases.  Now Stroke.resample(int) may raise a runtime
error to signal an error condition.
This commit is contained in:
2014-06-04 14:51:39 +09:00
parent 5ee55caba5
commit 812515b623
3 changed files with 30 additions and 14 deletions

View File

@@ -151,10 +151,16 @@ static PyObject *Stroke_resample(BPy_Stroke *self, PyObject *args, PyObject *kwd
float f; float f;
if (PyArg_ParseTupleAndKeywords(args, kwds, "i", (char **)kwlist_1, &i)) { if (PyArg_ParseTupleAndKeywords(args, kwds, "i", (char **)kwlist_1, &i)) {
self->s->Resample(i); if (self->s->Resample(i) < 0) {
PyErr_SetString(PyExc_RuntimeError, "Stroke resampling (by vertex count) failed");
return NULL;
}
} }
else if (PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "f", (char **)kwlist_2, &f)) { else if (PyErr_Clear(), PyArg_ParseTupleAndKeywords(args, kwds, "f", (char **)kwlist_2, &f)) {
self->s->Resample(f); if (self->s->Resample(f) < 0) {
PyErr_SetString(PyExc_RuntimeError, "Stroke resampling (by vertex interval) failed");
return NULL;
}
} }
else { else {
PyErr_SetString(PyExc_TypeError, "invalid argument"); PyErr_SetString(PyExc_TypeError, "invalid argument");

View File

@@ -508,11 +508,11 @@ public:
} }
}; };
void Stroke::Resample(int iNPoints) int Stroke::Resample(int iNPoints)
{ {
int vertsize = strokeVerticesSize(); int NPointsToAdd = iNPoints - strokeVerticesSize();
if (iNPoints <= vertsize) if (NPointsToAdd <= 0)
return; return 0;
StrokeInternal::StrokeVertexIterator it = strokeVerticesBegin(); StrokeInternal::StrokeVertexIterator it = strokeVerticesBegin();
StrokeInternal::StrokeVertexIterator next = it; StrokeInternal::StrokeVertexIterator next = it;
@@ -531,7 +531,7 @@ void Stroke::Resample(int iNPoints)
Vec2r b((next)->getPoint()); Vec2r b((next)->getPoint());
Vec2r vec_tmp(b - a); Vec2r vec_tmp(b - a);
real norm_var = vec_tmp.norm(); real norm_var = vec_tmp.norm();
int numberOfPointsToAdd = (int)floor((iNPoints - strokeVerticesSize()) * norm_var / _Length); int numberOfPointsToAdd = (int)floor(NPointsToAdd * norm_var / _Length);
float csampling = norm_var / (float)(numberOfPointsToAdd + 1); float csampling = norm_var / (float)(numberOfPointsToAdd + 1);
strokeSegments.push_back(StrokeSegment(it, next, norm_var, numberOfPointsToAdd, csampling)); strokeSegments.push_back(StrokeSegment(it, next, norm_var, numberOfPointsToAdd, csampling));
N += numberOfPointsToAdd; N += numberOfPointsToAdd;
@@ -543,9 +543,10 @@ void Stroke::Resample(int iNPoints)
meanlength /= (float)nsegments; meanlength /= (float)nsegments;
// if we don't have enough points let's resample finer some segments // if we don't have enough points let's resample finer some segments
int NPointsToAdd = iNPoints - vertsize;
bool checkEveryone = false; bool checkEveryone = false;
bool resampled;
while (N < NPointsToAdd) { while (N < NPointsToAdd) {
resampled = false;
for (vector<StrokeSegment>::iterator s = strokeSegments.begin(), send = strokeSegments.end(); s != send; ++s) { for (vector<StrokeSegment>::iterator s = strokeSegments.begin(), send = strokeSegments.end(); s != send; ++s) {
if (s->_sampling == 0.0f) if (s->_sampling == 0.0f)
continue; continue;
@@ -556,14 +557,20 @@ void Stroke::Resample(int iNPoints)
//resample //resample
s->_n = s->_n + 1; s->_n = s->_n + 1;
s->_sampling = s->_length / (float)(s->_n + 1); s->_sampling = s->_length / (float)(s->_n + 1);
s->_resampled = true; s->_resampled = resampled = true;
N++; N++;
if (N == NPointsToAdd) if (N == NPointsToAdd)
break; break;
} }
} }
if (checkEveryone && !resampled)
break;
checkEveryone = true; checkEveryone = true;
} }
if (N < NPointsToAdd) {
// fatal error, likely because _Length is inconsistent with the stroke length computed with the vertices
return -1;
}
//actually resample: //actually resample:
for (vector<StrokeSegment>::iterator s = strokeSegments.begin(), send = strokeSegments.end(); s != send; ++s) { for (vector<StrokeSegment>::iterator s = strokeSegments.begin(), send = strokeSegments.end(); s != send; ++s) {
newVertices.push_back(&(*(s->_begin))); newVertices.push_back(&(*(s->_begin)));
@@ -598,15 +605,17 @@ void Stroke::Resample(int iNPoints)
delete _rep; delete _rep;
_rep = new StrokeRep(this); _rep = new StrokeRep(this);
} }
return 0;
} }
void Stroke::Resample(float iSampling) int Stroke::Resample(float iSampling)
{ {
//cerr << "old size :" << strokeVerticesSize() << endl; //cerr << "old size :" << strokeVerticesSize() << endl;
if (iSampling == 0) if (iSampling == 0)
return; return 0;
if (iSampling >= _sampling) if (iSampling >= _sampling)
return; return 0;
_sampling = iSampling; _sampling = iSampling;
// Resample... // Resample...
@@ -655,6 +664,7 @@ void Stroke::Resample(float iSampling)
delete _rep; delete _rep;
_rep = new StrokeRep(this); _rep = new StrokeRep(this);
} }
return 0;
} }
void Stroke::RemoveAllVertices() void Stroke::RemoveAllVertices()

View File

@@ -588,7 +588,7 @@ public:
* \param iNPoints * \param iNPoints
* The number of vertices we eventually want in our stroke. * The number of vertices we eventually want in our stroke.
*/ */
void Resample(int iNPoints); int Resample(int iNPoints);
/*! Resampling method. /*! Resampling method.
* Resamples the curve with a given sampling. * Resamples the curve with a given sampling.
@@ -596,7 +596,7 @@ public:
* \param iSampling * \param iSampling
* The new sampling value. * The new sampling value.
*/ */
void Resample(float iSampling); int Resample(float iSampling);
/*! Removes all vertices from the Stroke. /*! Removes all vertices from the Stroke.
*/ */