/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/freestyle/intern/stroke/Stroke.cpp * \ingroup freestyle * \brief Classes to define a stroke * \author Stephane Grabli * \date 09/09/2002 */ #include "Stroke.h" #include "StrokeIterators.h" #include "StrokeAdvancedIterators.h" #include "StrokeRenderer.h" #include "BKE_global.h" #include "BKE_node.h" namespace Freestyle { /**********************************/ /* */ /* */ /* StrokeAttribute */ /* */ /* */ /**********************************/ StrokeAttribute::StrokeAttribute() { int i; _alpha = 1.0f; _thickness[0] = 1.0f; _thickness[1] = 1.0f; for (i = 0; i < 3; ++i) _color[i] = 0.2f; _color[0] = 0.8f; _userAttributesReal = NULL; _userAttributesVec2f = NULL; _userAttributesVec3f = NULL; _visible = true; } StrokeAttribute::StrokeAttribute(const StrokeAttribute& iBrother) { _alpha = iBrother._alpha; _thickness[0] = iBrother._thickness[0]; _thickness[1] = iBrother._thickness[1]; for (int i = 0; i < 3; ++i) _color[i] = iBrother._color[i]; _visible = iBrother._visible; if (iBrother._userAttributesReal) _userAttributesReal = new realMap(*iBrother._userAttributesReal); else _userAttributesReal = NULL; if (iBrother._userAttributesVec2f) _userAttributesVec2f = new Vec2fMap(*iBrother._userAttributesVec2f); else _userAttributesVec2f = NULL; if (iBrother._userAttributesVec3f) _userAttributesVec3f = new Vec3fMap(*iBrother._userAttributesVec3f); else _userAttributesVec3f = NULL; } StrokeAttribute::StrokeAttribute(float iRColor, float iGColor, float iBColor, float iAlpha, float iRThickness, float iLThickness) { _color[0] = iRColor; _color[1] = iGColor; _color[2] = iBColor; _alpha = iAlpha; _thickness[0] = iRThickness; _thickness[1] = iLThickness; _visible = true; _userAttributesReal = NULL; _userAttributesVec2f = NULL; _userAttributesVec3f = NULL; } StrokeAttribute::StrokeAttribute(const StrokeAttribute& a1, const StrokeAttribute& a2, float t) { _alpha = (1 - t) * a1._alpha + t * a2._alpha; _thickness[0] = (1 - t) * a1._thickness[0] + t * a2._thickness[0]; _thickness[1] = (1 - t) * a1._thickness[1] + t * a2._thickness[1]; for (int i = 0; i < 3; ++i) _color[i] = (1 - t) * a1._color[i] + t * a2._color[i]; _visible = a1.isVisible(); // FIXME: to be checked (and enhanced) if ((a1._userAttributesReal) && (a2._userAttributesReal)) { if (a1._userAttributesReal->size() == a2._userAttributesReal->size()) { _userAttributesReal = new realMap; realMap::iterator it1 = a1._userAttributesReal->begin(), it1end = a1._userAttributesReal->end(); realMap::iterator it2 = a2._userAttributesReal->begin(); for (; it1 != it1end; ++it1, ++it2) { (*_userAttributesReal)[(*it1).first] = ((1 - t) * (*it1).second + t * (*it2).second); } } } else { _userAttributesReal = NULL; } if ((a1._userAttributesVec2f) && (a2._userAttributesVec2f)) { if (a1._userAttributesVec2f->size() == a2._userAttributesVec2f->size()) { _userAttributesVec2f = new Vec2fMap; Vec2fMap::iterator it1 = a1._userAttributesVec2f->begin(), it1end = a1._userAttributesVec2f->end(); Vec2fMap::iterator it2 = a2._userAttributesVec2f->begin(); for (; it1 != it1end; ++it1, ++it2) { (*_userAttributesVec2f)[(*it1).first] = ((1 - t) * (*it1).second + t * (*it2).second); } } } else { _userAttributesVec2f = NULL; } if ((a1._userAttributesVec3f) && (a2._userAttributesVec3f)) { if (a1._userAttributesVec3f->size() == a2._userAttributesVec3f->size()) { _userAttributesVec3f = new Vec3fMap; Vec3fMap::iterator it1 = a1._userAttributesVec3f->begin(), it1end = a1._userAttributesVec3f->end(); Vec3fMap::iterator it2 = a2._userAttributesVec3f->begin(); for (; it1 != it1end; ++it1, ++it2) { (*_userAttributesVec3f)[(*it1).first] = ((1 - t) * (*it1).second + t * (*it2).second); } } } else { _userAttributesVec3f = NULL; } } StrokeAttribute::~StrokeAttribute() { if (_userAttributesReal) { _userAttributesReal->clear(); delete _userAttributesReal; } if (_userAttributesVec2f) { _userAttributesVec2f->clear(); delete _userAttributesVec2f; } if (_userAttributesVec3f) { _userAttributesVec3f->clear(); delete _userAttributesVec3f; } } StrokeAttribute& StrokeAttribute::operator=(const StrokeAttribute& iBrother) { int i; _alpha = iBrother._alpha; _thickness[0] = iBrother._thickness[0]; _thickness[1] = iBrother._thickness[1]; for (i = 0; i < 3; ++i) _color[i] = iBrother._color[i]; _visible = iBrother._visible; if (iBrother._userAttributesReal) { if (!_userAttributesReal) _userAttributesReal = new realMap; _userAttributesReal = new realMap(*(iBrother._userAttributesReal)); } else { _userAttributesReal = NULL; } if (iBrother._userAttributesVec2f) { if (!_userAttributesVec2f) _userAttributesVec2f = new Vec2fMap; _userAttributesVec2f = new Vec2fMap(*(iBrother._userAttributesVec2f)); } else { _userAttributesVec2f = NULL; } if (iBrother._userAttributesVec3f) { if (!_userAttributesVec3f) _userAttributesVec3f = new Vec3fMap; _userAttributesVec3f = new Vec3fMap(*(iBrother._userAttributesVec3f)); } else { _userAttributesVec3f = NULL; } return *this; } float StrokeAttribute::getAttributeReal(const char *iName) const { if (!_userAttributesReal) { if (G.debug & G_DEBUG_FREESTYLE) { cout << "StrokeAttribute warning: no real attribute was defined" << endl; } return 0.0f; } realMap::iterator a = _userAttributesReal->find(iName); if (a == _userAttributesReal->end()) { if (G.debug & G_DEBUG_FREESTYLE) { cout << "StrokeAttribute warning: no real attribute was added with the name " << iName << endl; } return 0.0f; } return (*a).second; } Vec2f StrokeAttribute::getAttributeVec2f(const char *iName) const { if (!_userAttributesVec2f) { if (G.debug & G_DEBUG_FREESTYLE) { cout << "StrokeAttribute warning: no Vec2f attribute was defined" << endl; } return 0; } Vec2fMap::iterator a = _userAttributesVec2f->find(iName); if (a == _userAttributesVec2f->end()) { if (G.debug & G_DEBUG_FREESTYLE) { cout << "StrokeAttribute warning: no Vec2f attribute was added with the name " << iName << endl; } return 0; } return (*a).second; } Vec3f StrokeAttribute::getAttributeVec3f(const char *iName) const { if (!_userAttributesVec3f) { if (G.debug & G_DEBUG_FREESTYLE) { cout << "StrokeAttribute warning: no Vec3f attribute was defined" << endl; } return 0; } Vec3fMap::iterator a = _userAttributesVec3f->find(iName); if (a == _userAttributesVec3f->end()) { if (G.debug & G_DEBUG_FREESTYLE) { cout << "StrokeAttribute warning: no Vec3f attribute was added with the name " << iName << endl; } return 0; } return (*a).second; } bool StrokeAttribute::isAttributeAvailableReal(const char *iName) const { if (!_userAttributesReal) { return false; } realMap::iterator a = _userAttributesReal->find(iName); if (a == _userAttributesReal->end()) { return false; } return true; } bool StrokeAttribute::isAttributeAvailableVec2f(const char *iName) const { if (!_userAttributesVec2f) { return false; } Vec2fMap::iterator a = _userAttributesVec2f->find(iName); if (a == _userAttributesVec2f->end()) { return false; } return true; } bool StrokeAttribute::isAttributeAvailableVec3f(const char *iName) const { if (!_userAttributesVec3f) { return false; } Vec3fMap::iterator a = _userAttributesVec3f->find(iName); if (a == _userAttributesVec3f->end()) { return false; } return true; } void StrokeAttribute::setAttributeReal(const char *iName, float att) { if (!_userAttributesReal) _userAttributesReal = new realMap; (*_userAttributesReal)[iName] = att; } void StrokeAttribute::setAttributeVec2f(const char *iName, const Vec2f& att) { if (!_userAttributesVec2f) _userAttributesVec2f = new Vec2fMap; (*_userAttributesVec2f)[iName] = att; } void StrokeAttribute::setAttributeVec3f(const char *iName, const Vec3f& att) { if (!_userAttributesVec3f) _userAttributesVec3f = new Vec3fMap; (*_userAttributesVec3f)[iName] = att; } /**********************************/ /* */ /* */ /* StrokeVertex */ /* */ /* */ /**********************************/ StrokeVertex::StrokeVertex() : CurvePoint() { _CurvilignAbscissa = 0.0f; _StrokeLength = 0.0f; } StrokeVertex::StrokeVertex(const StrokeVertex& iBrother) : CurvePoint(iBrother) { _Attribute = iBrother._Attribute; _CurvilignAbscissa = 0.0f; _StrokeLength = 0.0f; } StrokeVertex::StrokeVertex(SVertex *iSVertex) : CurvePoint(iSVertex, 0, 0.0f) { _CurvilignAbscissa = 0.0f; _StrokeLength = 0.0f; } StrokeVertex::StrokeVertex(CurvePoint *iPoint) : CurvePoint(*iPoint) { _CurvilignAbscissa = 0.0f; _StrokeLength = 0.0f; } StrokeVertex::StrokeVertex(StrokeVertex *iA, StrokeVertex *iB, float t3) : CurvePoint(iA, iB, t3) { // interpolate attributes: _Attribute = StrokeAttribute(iA->attribute(), iB->attribute(), t3); _CurvilignAbscissa = (1 - t3) * iA->curvilinearAbscissa() + t3 * iB->curvilinearAbscissa(); _StrokeLength = iA->strokeLength(); } StrokeVertex::StrokeVertex(SVertex *iSVertex, const StrokeAttribute& iAttribute) : CurvePoint(iSVertex, 0, 0.0f) { _Attribute = iAttribute; _CurvilignAbscissa = 0.0f; _StrokeLength = 0.0f; } StrokeVertex::~StrokeVertex() {} StrokeVertex& StrokeVertex::operator=(const StrokeVertex& iBrother) { ((CurvePoint *)this)->operator=(iBrother); _Attribute = iBrother._Attribute; _CurvilignAbscissa = 0.0f; _StrokeLength = 0.0f; return *this; } /**********************************/ /* */ /* */ /* Stroke */ /* */ /* */ /**********************************/ Stroke::Stroke() { _Length = 0; _id = 0; _sampling = FLT_MAX; //_mediumType = DEFAULT_STROKE; _mediumType = OPAQUE_MEDIUM; _textureId = 0; _textureStep = 1.0; for (int a = 0; a < MAX_MTEX; a++) { _mtex[a] = NULL; } _nodeTree = NULL; _tips = false; _rep = NULL; } Stroke::Stroke(const Stroke& iBrother) { for (vertex_container::const_iterator v = iBrother._Vertices.begin(), vend = iBrother._Vertices.end(); v != vend; v++) { _Vertices.push_back(*v); } _Length = 0; _id = iBrother._id; _ViewEdges = iBrother._ViewEdges; _sampling = iBrother._sampling; _mediumType = iBrother._mediumType; _textureId = iBrother._textureId; _textureStep = iBrother._textureStep; for (int a = 0; a < MAX_MTEX; a++) { if (iBrother._mtex) { _mtex[a] = iBrother._mtex[a]; } else { _mtex[a] = NULL; } } _nodeTree = iBrother._nodeTree; _tips = iBrother._tips; if (iBrother._rep) _rep = new StrokeRep(*(iBrother._rep)); else _rep = NULL; } Stroke::~Stroke() { if (!_Vertices.empty()) { for (vertex_container::iterator v = _Vertices.begin(), vend = _Vertices.end(); v != vend; v++) { delete (*v); } _Vertices.clear(); } _ViewEdges.clear(); if (_rep) { delete _rep; _rep = NULL; } } Stroke& Stroke::operator=(const Stroke& iBrother) { if (!_Vertices.empty()) _Vertices.clear(); for (vertex_container::const_iterator v = iBrother._Vertices.begin(), vend = iBrother._Vertices.end(); v != vend; v++) { _Vertices.push_back(*v); } _Length = iBrother._Length; _id = iBrother._id; _ViewEdges = iBrother._ViewEdges; _sampling = iBrother._sampling; if (_rep) delete _rep; if (iBrother._rep) _rep = new StrokeRep(*(iBrother._rep)); else _rep = NULL; return *this; } void Stroke::setLength(float iLength) { _Length = iLength; for (vertex_container::iterator v = _Vertices.begin(), vend = _Vertices.end(); v != vend; ++v) { (*v)->setStrokeLength(iLength); } } float Stroke::ComputeSampling(int iNVertices) { if (iNVertices <= (int)_Vertices.size()) //soc return _sampling; float sampling = _Length / (float)(iNVertices - _Vertices.size() + 1); return sampling; } class StrokeSegment { public: StrokeInternal::StrokeVertexIterator _begin; StrokeInternal::StrokeVertexIterator _end; float _length; int _n; float _sampling; bool _resampled; StrokeSegment(StrokeInternal::StrokeVertexIterator ibegin, StrokeInternal::StrokeVertexIterator iend, float ilength, int in, float isampling) { _begin = ibegin; _end = iend; _length = ilength; _n = in; _sampling = isampling; _resampled = false; } }; int Stroke::Resample(int iNPoints) { int NPointsToAdd = iNPoints - strokeVerticesSize(); if (NPointsToAdd <= 0) return 0; StrokeInternal::StrokeVertexIterator it = strokeVerticesBegin(); StrokeInternal::StrokeVertexIterator next = it; ++next; StrokeInternal::StrokeVertexIterator itend = strokeVerticesEnd(); vertex_container newVertices; real t = 0.0f; StrokeVertex *newVertex = NULL; vector strokeSegments; int N = 0; float meanlength = 0; int nsegments = 0; while ((it != itend) && (next != itend)) { Vec2r a((it)->getPoint()); Vec2r b((next)->getPoint()); Vec2r vec_tmp(b - a); real norm_var = vec_tmp.norm(); int numberOfPointsToAdd = (int)floor(NPointsToAdd * norm_var / _Length); float csampling = norm_var / (float)(numberOfPointsToAdd + 1); strokeSegments.push_back(StrokeSegment(it, next, norm_var, numberOfPointsToAdd, csampling)); N += numberOfPointsToAdd; meanlength += norm_var; ++nsegments; ++it; ++next; } meanlength /= (float)nsegments; // if we don't have enough points let's resample finer some segments bool checkEveryone = false; bool resampled; while (N < NPointsToAdd) { resampled = false; for (vector::iterator s = strokeSegments.begin(), send = strokeSegments.end(); s != send; ++s) { if (s->_sampling == 0.0f) continue; if (s->_resampled == false) { if ((!checkEveryone) && (s->_length < meanlength)) continue; //resample s->_n = s->_n + 1; s->_sampling = s->_length / (float)(s->_n + 1); s->_resampled = resampled = true; N++; if (N == NPointsToAdd) break; } } if (checkEveryone && !resampled) break; checkEveryone = true; } if (N < NPointsToAdd) { // fatal error, likely because _Length is inconsistent with the stroke length computed with the vertices return -1; } //actually resample: for (vector::iterator s = strokeSegments.begin(), send = strokeSegments.end(); s != send; ++s) { newVertices.push_back(&(*(s->_begin))); if (s->_sampling < _sampling) _sampling = s->_sampling; t = s->_sampling / s->_length; for (int i = 0; i < s->_n; ++i) { newVertex = new StrokeVertex(&(*(s->_begin)), &(*(s->_end)), t); newVertices.push_back(newVertex); t += s->_sampling / s->_length; } it = s->_begin; next = s->_end; } // add last: ++it; ++next; if ((it != itend) && (next == itend)/* && (t == 0.0f)*/) newVertices.push_back(&(*it)); int newsize = newVertices.size(); if (newsize != iNPoints) cerr << "Warning: incorrect points number" << endl; _Vertices.clear(); _Vertices = newVertices; newVertices.clear(); return 0; } int Stroke::Resample(float iSampling) { //cerr << "old size :" << strokeVerticesSize() << endl; if (iSampling == 0) return 0; if (iSampling >= _sampling) return 0; _sampling = iSampling; // Resample... //real curvilinearLength = 0.0f; vertex_container newVertices; real t = 0.0f; const real limit = 0.99; StrokeVertex *newVertex = NULL; StrokeInternal::StrokeVertexIterator it = strokeVerticesBegin(); StrokeInternal::StrokeVertexIterator next = it; ++next; StrokeInternal::StrokeVertexIterator itend = strokeVerticesEnd(); while ((it != itend) && (next != itend)) { newVertices.push_back(&(*it)); Vec2r a((it)->getPoint()); Vec2r b((next)->getPoint()); Vec2r vec_tmp(b - a); real norm_var = vec_tmp.norm(); if (norm_var <= _sampling) { //curvilinearLength += norm_var; ++it; ++next; continue; } //curvilinearLength += _sampling; t = _sampling / norm_var; while (t < limit) { newVertex = new StrokeVertex(&(*it), &(*next), t); //newVertex->setCurvilinearAbscissa(curvilinearLength); newVertices.push_back(newVertex); t = t + _sampling / norm_var; } ++it; ++next; } // add last: if ((it != itend) && (next == itend)/* && (t == 0.f)*/) newVertices.push_back(&(*it)); _Vertices.clear(); _Vertices = newVertices; newVertices.clear(); return 0; } void Stroke::RemoveAllVertices() { vertex_container::iterator it = _Vertices.begin(), itend = _Vertices.end(); for (; it != itend; ++it) delete (*it); _Vertices.clear(); UpdateLength(); } void Stroke::RemoveVertex(StrokeVertex *iVertex) { vertex_container::iterator it = _Vertices.begin(), itend = _Vertices.end(); for (; it != itend; ++it) { if ((*it) == iVertex) { delete iVertex; it = _Vertices.erase(it); // it is now the element just after the erased element break; } } UpdateLength(); } void Stroke::InsertVertex(StrokeVertex *iVertex, StrokeInternal::StrokeVertexIterator next) { vertex_container::iterator itnext = next.getIt(); _Vertices.insert(itnext, iVertex); UpdateLength(); } void Stroke::UpdateLength() { // recompute curvilinear abscissa and stroke length float curvabsc = 0.0f; vertex_container::iterator it = _Vertices.begin(), itend = _Vertices.end(); vertex_container::iterator previous = it; for (; it != itend; ++it) { curvabsc += ((*it)->getPoint() - (*previous)->getPoint()).norm(); (*it)->setCurvilinearAbscissa(curvabsc); previous = it; } _Length = curvabsc; for (it = _Vertices.begin(); it != itend; ++it) { (*it)->setStrokeLength(_Length); } } //! embedding vertex iterator Stroke::const_vertex_iterator Stroke::vertices_begin() const { return const_vertex_iterator(_Vertices.begin(), _Vertices.begin(), _Vertices.end()); } Stroke::const_vertex_iterator Stroke::vertices_end() const { return const_vertex_iterator(_Vertices.end(), _Vertices.begin(), _Vertices.end()); } Stroke::vertex_iterator Stroke::vertices_end() { return vertex_iterator(_Vertices.end(), _Vertices.begin(), _Vertices.end()); } StrokeInternal::StrokeVertexIterator Stroke::strokeVerticesBegin(float t) { if ((t != 0) && (t < _sampling)) Resample(t); return StrokeInternal::StrokeVertexIterator(this->_Vertices.begin(), this->_Vertices.begin(), this->_Vertices.end()); } StrokeInternal::StrokeVertexIterator Stroke::strokeVerticesEnd() { return StrokeInternal::StrokeVertexIterator(this->_Vertices.end(), this->_Vertices.begin(), this->_Vertices.end()); } Interface0DIterator Stroke::verticesBegin() { Interface0DIterator ret(new StrokeInternal::StrokeVertexIterator(this->_Vertices.begin(), this->_Vertices.begin(), this->_Vertices.end())); return ret; } Interface0DIterator Stroke::verticesEnd() { Interface0DIterator ret(new StrokeInternal::StrokeVertexIterator(this->_Vertices.end(), this->_Vertices.begin(), this->_Vertices.end())); return ret; } Interface0DIterator Stroke::pointsBegin(float t) { return verticesBegin(); // FIXME } Interface0DIterator Stroke::pointsEnd(float t) { return verticesEnd(); } void Stroke::ScaleThickness(float iFactor) { for (vertex_container::iterator it = _Vertices.begin(), itend = _Vertices.end(); it != itend; ++it) { StrokeAttribute& attr = (*it)->attribute(); attr.setThickness(iFactor * attr.getThicknessR(), iFactor * attr.getThicknessL()); } } void Stroke::Render(const StrokeRenderer *iRenderer) { if (!_rep) _rep = new StrokeRep(this); iRenderer->RenderStrokeRep(_rep); } void Stroke::RenderBasic(const StrokeRenderer *iRenderer) { if (!_rep) _rep = new StrokeRep(this); iRenderer->RenderStrokeRep(_rep); } Stroke::vertex_iterator Stroke::vertices_begin(float sampling) { // Resample if necessary if ((sampling != 0) && (sampling < _sampling)) Resample(sampling); return vertex_iterator(_Vertices.begin(), _Vertices.begin(), _Vertices.end()); //return _Vertices.begin(); } #if 0 Stroke::vertex_iterator Stroke::vertices_last() { vertex_iterator res = vertices_begin(); vertex_iterator next = res; ++next; while (!next.end()) { ++next; ++res; } return res; } Stroke::const_vertex_iterator Stroke::vertices_last() const { const_vertex_iterator res = vertices_begin(); const_vertex_iterator next = res; ++next; while (!next.end()) { ++next; ++res; } return res; } Stroke::vertex_container::reverse_iterator Stroke::vertices_last(float sampling) { // Resample if necessary if (sampling < _sampling) Resample(sampling); return _Vertices.rbegin(); } inline Vec3r shaded_color(int iCombination = 0) const; inline Vec<3, real> Stroke::orientation2d(const_vertex_iterator it) const { return iterator_edge_orientation2d_function(this, it); } Vec3r Stroke::orientation2d(int iCombination) const { return edge_orientation2d_function(*this, iCombination); } inline Vec3r Stroke::orientation3d(const_vertex_iterator it) const { return iterator_edge_orientation3d_function(*this, it); } Vec3r Stroke::orientation3d(int iCombination) const { return edge_orientation3d_function(*this, iCombination); } Material Stroke::material() const { const_vertex_iterator v = vertices_begin(), vend = strokeVerticesEnd(); Material mat = (*v)->material(); for (; v != vend; ++v) { if (mat != (*v)->material()) Exception::raiseException(); } return mat; } int Stroke::qi() const { const_vertex_iterator v = vertices_begin(), vend = vertices_end(); int qi_= (*v)->qi(); for (; v != vend; ++v) { if ((*v)->qi() != qi_) Exception::raiseException(); } return qi_; } inline occluder_container::const_iterator occluders_begin() const { return _FEdgeA->occluders().begin(); } inline occluder_container::const_iterator occluders_end() const { return _FEdgeA->occluders().end(); } int Stroke::occluders_size() const { return qi(); } bool Stroke::occluders_empty() const { const_vertex_iterator v = vertices_begin(), vend = vertices_end(); bool empty = (*v)->occluders_empty(); for (; v != vend; ++v) { if ((*v)->occluders_empty() != empty) Exception::raiseException(); } return empty; } #if 0 inline const polygon3d& occludee() const { return *(_FEdgeA->aFace()); } #endif const SShape *Stroke::occluded_shape() const { const_vertex_iterator v = vertices_begin(), vend = vertices_end(); const SShape *sshape = (*v)->occluded_shape(); for (; v != vend; ++v) { if ((*v)->occluded_shape() != sshape) Exception::raiseException(); } return sshape; } const bool Stroke::occludee_empty() const { const_vertex_iterator v = vertices_begin(), vend = vertices_end(); bool empty = (*v)->occludee_empty(); for (; v != vend; ++v) { if ((*v)->occludee_empty() != empty) Exception::raiseException(); } return empty; } const SShape *Stroke::shape() const { const_vertex_iterator v = vertices_begin(), vend = vertices_end(); const SShape *sshape = (*v)->shape(); for (; v != vend; ++v) { if ((*v)->shape() != sshape) Exception::raiseException(); } return sshape; } real Stroke::z_discontinuity(int iCombination) const { return z_discontinuity_edge_function(*this, iCombination); } Vec3r Stroke::curvature2d_as_vector(int iCombination) const { return curvature2d_as_vector_edge_function(*this, iCombination); } real Stroke::curvature2d_as_angle(int iCombination) const { return curvature2d_as_angle_edge_function(*this, iCombination); } float Stroke::shape_importance(int iCombination) const { return shape_importance_edge_function(*this, iCombination); } float Stroke::local_average_depth(int iCombination ) const { return local_average_depth_edge_function(*this, iCombination); } float Stroke::local_depth_variance(int iCombination) const { return local_depth_variance_edge_function(*this, iCombination); } real Stroke::local_average_density(float sigma , int iCombination ) const { return density_edge_function(*this, iCombination); } #endif } /* namespace Freestyle */