diff options
Diffstat (limited to 'source/blender/freestyle/intern/stroke')
48 files changed, 14688 insertions, 0 deletions
diff --git a/source/blender/freestyle/intern/stroke/AdvancedFunctions0D.cpp b/source/blender/freestyle/intern/stroke/AdvancedFunctions0D.cpp new file mode 100644 index 00000000000..9d955722ac7 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/AdvancedFunctions0D.cpp @@ -0,0 +1,117 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/freestyle/intern/stroke/AdvancedFunctions0D.cpp + * \ingroup freestyle + * \brief Functions taking 0D input + * \author Stephane Grabli + * \author Emmanuel Turquin + * \date 01/07/2003 + */ + +#include "AdvancedFunctions0D.h" +#include "Canvas.h" + +#include "../view_map/Functions0D.h" +#include "../view_map/SteerableViewMap.h" + +namespace Functions0D { + +int DensityF0D::operator()(Interface0DIterator& iter) +{ + Canvas* canvas = Canvas::getInstance(); + int bound = _filter.getBound(); + + if ((iter->getProjectedX()-bound < 0) || (iter->getProjectedX()+bound>canvas->width()) || + (iter->getProjectedY()-bound < 0) || (iter->getProjectedY()+bound>canvas->height())) { + result = 0.0; + return 0; + } + + RGBImage image; + canvas->readColorPixels((int)iter->getProjectedX() - bound, (int)iter->getProjectedY() - bound, + _filter.maskSize(), _filter.maskSize(), image); + result = _filter.getSmoothedPixel<RGBImage>(&image, (int)iter->getProjectedX(), (int)iter->getProjectedY()); + + return 0; +} + + +int LocalAverageDepthF0D::operator()(Interface0DIterator& iter) +{ + Canvas * iViewer = Canvas::getInstance(); + int bound = _filter.getBound(); + + if ((iter->getProjectedX()-bound < 0) || (iter->getProjectedX()+bound>iViewer->width()) || + (iter->getProjectedY()-bound < 0) || (iter->getProjectedY()+bound>iViewer->height())) { + result = 0.0; + return 0; + } + + GrayImage image; + iViewer->readDepthPixels((int)iter->getProjectedX() - bound, (int)iter->getProjectedY()-bound, + _filter.maskSize(), _filter.maskSize(), image); + result = _filter.getSmoothedPixel(&image, (int)iter->getProjectedX(), (int)iter->getProjectedY()); + + return 0; +} + +int ReadMapPixelF0D::operator()(Interface0DIterator& iter) +{ + Canvas * canvas = Canvas::getInstance(); + result = canvas->readMapPixel(_mapName, _level, (int)iter->getProjectedX(), (int)iter->getProjectedY()); + return 0; +} + +int ReadSteerableViewMapPixelF0D::operator()(Interface0DIterator& iter) +{ + SteerableViewMap *svm = Canvas::getInstance()->getSteerableViewMap(); + result = svm->readSteerableViewMapPixel(_orientation, _level, (int)iter->getProjectedX(), + (int)iter->getProjectedY()); + return 0; +} + +int ReadCompleteViewMapPixelF0D::operator()(Interface0DIterator& iter) +{ + SteerableViewMap *svm = Canvas::getInstance()->getSteerableViewMap(); + result = svm->readCompleteViewMapPixel(_level,(int)iter->getProjectedX(), (int)iter->getProjectedY()); + return 0; +} + +int GetViewMapGradientNormF0D::operator()(Interface0DIterator& iter) +{ + SteerableViewMap *svm = Canvas::getInstance()->getSteerableViewMap(); + float pxy = svm->readCompleteViewMapPixel(_level, (int)iter->getProjectedX(), (int)iter->getProjectedY()); + float gx = svm->readCompleteViewMapPixel(_level, (int)iter->getProjectedX() + _step, (int)iter->getProjectedY()) + - pxy; + float gy = svm->readCompleteViewMapPixel(_level, (int)iter->getProjectedX(), (int)iter->getProjectedY() + _step) + - pxy; + result = Vec2f(gx, gy).norm(); + return 0; +} + +} // end of namespace Functions0D diff --git a/source/blender/freestyle/intern/stroke/AdvancedFunctions0D.h b/source/blender/freestyle/intern/stroke/AdvancedFunctions0D.h new file mode 100644 index 00000000000..2875402700b --- /dev/null +++ b/source/blender/freestyle/intern/stroke/AdvancedFunctions0D.h @@ -0,0 +1,229 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_ADVANCED_FUNCTIONS_0D_H__ +#define __FREESTYLE_ADVANCED_FUNCTIONS_0D_H__ + +/** \file blender/freestyle/intern/stroke/AdvancedFunctions0D.h + * \ingroup freestyle + * \brief Functions taking 0D input + * \author Stephane Grabli + * \author Emmanuel Turquin + * \date 01/07/2003 + */ + +#include "../image/GaussianFilter.h" +#include "../image/Image.h" + +#include "../view_map/Functions0D.h" + +// +// Functions definitions +// +/////////////////////////////////////////////////////////// + +namespace Functions0D { + +// DensityF0D +/*! Returns the density of the (result) image evaluated at an Interface0D. + * This density is evaluated using a pixels square window around the evaluation point and integrating + * these values using a gaussian. + */ +class LIB_STROKE_EXPORT DensityF0D : public UnaryFunction0D<double> +{ +public: + /*! Builds the functor from the gaussian sigma value. + * \param sigma + * sigma indicates the x value for which the gaussian function is 0.5. It leads to the window size value. + * (the larger, the smoother) + */ + DensityF0D(double sigma = 2) : UnaryFunction0D<double>() + { + _filter.setSigma((float)sigma); + } + + /*! Returns the string "DensityF0D" */ + string getName() const + { + return "DensityF0D"; + } + + /*! The () operator. */ + int operator()(Interface0DIterator& iter); + +private: + GaussianFilter _filter; +}; + +// LocalAverageDepthF0D +/*! Returns the average depth around a point. + * The result is obtained by querying the depth buffer on a window around that point. + */ +class LIB_STROKE_EXPORT LocalAverageDepthF0D : public UnaryFunction0D<double> +{ +private: + GaussianFilter _filter; + +public: + /*! Builds the functor from the size of the mask that will be used. */ + LocalAverageDepthF0D(real maskSize = 5.0f) : UnaryFunction0D<double>() + { + _filter.setSigma((float)maskSize / 2.0f); + } + + /*! Returns the string "LocalAverageDepthF0D" */ + string getName() const + { + return "LocalAverageDepthF0D"; + } + + /*! the () operator. */ + int operator()(Interface0DIterator& iter); +}; + +// ReadMapPixel +/*! Reads a pixel in a map. */ +class LIB_STROKE_EXPORT ReadMapPixelF0D : public UnaryFunction0D<float> +{ +private: + const char * _mapName; + int _level; + +public: + /*! Builds the functor from name of the + * Map that must be read. + * \param iMapName + * The name of the map. + * \param level + * The level of the pyramid from which the pixel must be read. + */ + ReadMapPixelF0D(const char *iMapName, int level) : UnaryFunction0D<float>() + { + _mapName = iMapName; + _level = level; + } + + /*! Returns the string "ReadMapPixelF0D" */ + string getName() const + { + return "ReadMapPixelF0D"; + } + + /*! the () operator. */ + int operator()(Interface0DIterator& iter); +}; + +// ReadSteerableViewMapPixel +/*! Reads a pixel in one of the level of one of the steerable viewmaps. */ +class LIB_STROKE_EXPORT ReadSteerableViewMapPixelF0D : public UnaryFunction0D<float> +{ +private: + unsigned _orientation; + int _level; + +public: + /*! Builds the functor + * \param nOrientation + * The integer belonging to [0,4] indicating the orientation (E,NE,N,NW) we are interested in. + * \param level + * The level of the pyramid from which the pixel must be read. + */ + ReadSteerableViewMapPixelF0D(unsigned nOrientation, int level) : UnaryFunction0D<float>() + { + _orientation = nOrientation; + _level = level; + } + + /*! Returns the string "ReadSteerableViewMapPixelF0D" */ + string getName() const + { + return "ReadSteerableViewMapPixelF0D"; + } + + /*! the () operator. */ + int operator()(Interface0DIterator& iter); +}; + +// ReadCompleteViewMapPixel +/*! Reads a pixel in one of the level of the complete viewmap. */ +class LIB_STROKE_EXPORT ReadCompleteViewMapPixelF0D : public UnaryFunction0D<float> +{ +private: + int _level; + +public: + /*! Builds the functor + * \param level + * The level of the pyramid from which the pixel must be read. + */ + ReadCompleteViewMapPixelF0D(int level) : UnaryFunction0D<float>() + { + _level = level; + } + + /*! Returns the string "ReadCompleteViewMapPixelF0D" */ + string getName() const + { + return "ReadCompleteViewMapPixelF0D"; + } + + /*! the () operator. */ + int operator()(Interface0DIterator& iter); +}; + +// GetViewMapGradientNormF0D +/*! Returns the norm of the gradient of the global viewmap density image. */ +class LIB_STROKE_EXPORT GetViewMapGradientNormF0D: public UnaryFunction0D< float> +{ +private: + int _level; + float _step; + +public: + /*! Builds the functor + * \param level + * The level of the pyramid from which the pixel must be read. + */ + GetViewMapGradientNormF0D(int level) : UnaryFunction0D<float>() + { + _level = level; + _step = (float)pow(2.0, _level); + } + + /*! Returns the string "GetOccludeeF0D" */ + string getName() const + { + return "GetViewMapGradientNormF0D"; + } + + /*! the () operator. */ + int operator()(Interface0DIterator& iter); +}; + +} // end of namespace Functions0D + +#endif // __FREESTYLE_ADVANCED_FUNCTIONS_0D_H__ diff --git a/source/blender/freestyle/intern/stroke/AdvancedFunctions1D.cpp b/source/blender/freestyle/intern/stroke/AdvancedFunctions1D.cpp new file mode 100644 index 00000000000..2df4d19195e --- /dev/null +++ b/source/blender/freestyle/intern/stroke/AdvancedFunctions1D.cpp @@ -0,0 +1,138 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/freestyle/intern/stroke/AdvancedFunctions1D.cpp + * \ingroup freestyle + * \brief Functions taking 1D input + * \author Stephane Grabli + * \author Emmanuel Turquin + * \date 01/07/2003 + */ + +#include "AdvancedFunctions1D.h" +#include "Canvas.h" + +#include "../view_map/SteerableViewMap.h" + +// FIXME +namespace Functions1D { + +int GetSteerableViewMapDensityF1D::operator()(Interface1D& inter) +{ + SteerableViewMap * svm = Canvas::getInstance()->getSteerableViewMap(); + Interface0DIterator it = inter.pointsBegin(_sampling); + Interface0DIterator itnext = it; + ++itnext; + FEdge *fe; + unsigned nSVM; + vector<float> values; + + while (!itnext.isEnd()) { + Interface0D& i0D = (*it); + Interface0D& i0Dnext = (*itnext); + fe = i0D.getFEdge(i0Dnext); + if (fe == 0) { + cerr << "GetSteerableViewMapDensityF1D warning: no FEdge between " << i0D.getId() << " and " + << i0Dnext.getId() << endl; + // compute the direction between these two ??? + Vec2f dir = i0Dnext.getPoint2D() - i0D.getPoint2D(); + nSVM = svm->getSVMNumber(dir); + } + else { + nSVM = svm->getSVMNumber(fe->getId().getFirst()); + } + Vec2r m((i0D.getProjectedX() + i0Dnext.getProjectedX()) / 2.0, + (i0D.getProjectedY() + i0Dnext.getProjectedY()) / 2.0); + values.push_back(svm->readSteerableViewMapPixel(nSVM, _level, (int)m[0], (int)m[1])); + ++it; + ++itnext; + } + + float res, res_tmp; + vector<float>::iterator v = values.begin(), vend = values.end(); + unsigned size = 1; + switch (_integration) { + case MIN: + res = *v; + ++v; + for (; v != vend; ++v) { + res_tmp = *v; + if (res_tmp < res) + res = res_tmp; + } + break; + case MAX: + res = *v; + ++v; + for (; v != vend; ++v) { + res_tmp = *v; + if (res_tmp > res) + res = res_tmp; + } + break; + case FIRST: + res = *v; + break; + case LAST: + --vend; + res = *vend; + break; + case MEAN: + default: + res = *v; + ++v; + for (; v != vend; ++v, ++size) + res += *v; + res /= (size ? size : 1); + break; + } + result = res; + return 0; +} + +int GetDirectionalViewMapDensityF1D::operator()(Interface1D& inter) +{ + //soc unsigned size; + result = integrate(_fun, inter.pointsBegin(_sampling), inter.pointsEnd(_sampling), _integration); + return 0; +} + +int GetCompleteViewMapDensityF1D::operator()(Interface1D& inter) +{ + //soc unsigned size; + /* Id id = inter.getId(); */ /* UNUSED */ + result = integrate(_fun, inter.pointsBegin(_sampling), inter.pointsEnd(_sampling), _integration); + return 0; +} + +int GetViewMapGradientNormF1D::operator()(Interface1D& inter) +{ + result = integrate(_func, inter.pointsBegin(_sampling), inter.pointsEnd(_sampling), _integration); + return 0; +} + +} // Functions1D namespace diff --git a/source/blender/freestyle/intern/stroke/AdvancedFunctions1D.h b/source/blender/freestyle/intern/stroke/AdvancedFunctions1D.h new file mode 100644 index 00000000000..35f516cd781 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/AdvancedFunctions1D.h @@ -0,0 +1,294 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_ADVANCED_FUNCTIONS_1D_H__ +#define __FREESTYLE_ADVANCED_FUNCTIONS_1D_H__ + +/** \file blender/freestyle/intern/stroke/AdvancedFunctions1D.h + * \ingroup freestyle + * \brief Functions taking 1D input + * \author Stephane Grabli + * \author Emmanuel Turquin + * \date 01/07/2003 + */ + +#include "AdvancedFunctions0D.h" + +#include "../view_map/Functions1D.h" + +// +// Functions definitions +// +/////////////////////////////////////////////////////////// + +namespace Functions1D { + +// DensityF1D +/*! Returns the density evaluated for an Interface1D. + * The density is evaluated for a set of points along the Interface1D (using the DensityF0D functor) with a + * user-defined sampling and then integrated into a single value using a user-defined integration method. + */ +class DensityF1D : public UnaryFunction1D<double> +{ +private: + float _sampling; + +public: + /*! Builds the functor. + * \param sigma + * Thesigma used in DensityF0D and determining the window size used in each density query. + * \param iType + * The integration method used to compute a single value from a set of values. + * \param sampling + * The resolution used to sample the chain: the corresponding 0D function is evaluated at each sample point and the + * result is obtained by combining the resulting values into a single one, following the method specified by iType. + */ + DensityF1D(double sigma = 2, IntegrationType iType = MEAN, float sampling = 2.0f) + : UnaryFunction1D<double>(iType), _fun(sigma) + { + _sampling = sampling; + } + + /*! Destructor */ + virtual ~DensityF1D() {} + + /*! Returns the string "DensityF1D"*/ + string getName() const + { + return "DensityF1D"; + } + + /*! the () operator.*/ + int operator()(Interface1D& inter) + { + result = integrate(_fun, inter.pointsBegin(_sampling), inter.pointsEnd(_sampling), _integration); + return 0; + } + +private: + Functions0D::DensityF0D _fun; +}; + +// LocalAverageDepthF1D +/*! Returns the average depth evaluated for an Interface1D. + * The average depth is evaluated for a set of points along the Interface1D (using the LocalAverageDepthF0D functor) + * with a user-defined sampling and then integrated into a single value using a user-defined integration method. + */ +class LocalAverageDepthF1D : public UnaryFunction1D<double> +{ +public: + /*! Builds the functor. + * \param sigma + * The sigma used in DensityF0D and determining the window size used in each density query. + * \param iType + * The integration method used to compute a single value from a set of values. + */ + LocalAverageDepthF1D(real sigma, IntegrationType iType = MEAN) + : UnaryFunction1D<double>(iType), _fun(sigma) + { + } + + /*! Returns the string "LocalAverageDepthF1D" */ + string getName() const + { + return "LocalAverageDepthF1D"; + } + + /*! the () operator. */ + int operator()(Interface1D& inter) + { + result = integrate(_fun, inter.verticesBegin(), inter.verticesEnd(), _integration); + return 0; + } + +private: + Functions0D::LocalAverageDepthF0D _fun; +}; + +// GetCompleteViewMapDensity +/*! Returns the density evaluated for an Interface1D in the complete viewmap image. + * The density is evaluated for a set of points along the Interface1D (using the ReadCompleteViewMapPixelF0D functor) + * and then integrated into a single value using a user-defined integration method. + */ +class LIB_STROKE_EXPORT GetCompleteViewMapDensityF1D : public UnaryFunction1D<double> +{ +public: + /*! Builds the functor. + * \param level + * The level of the pyramid from which + * the pixel must be read. + * \param iType + * The integration method used to compute + * a single value from a set of values. + * \param sampling + * The resolution used to sample the chain: the corresponding 0D function + * is evaluated at each sample point and the result is obtained by + * combining the resulting values into a single one, following the + * method specified by iType. + */ + GetCompleteViewMapDensityF1D(unsigned level, IntegrationType iType = MEAN, float sampling = 2.0f) + : UnaryFunction1D<double>(iType), _fun(level) + { + _sampling = sampling; + } + + /*! Returns the string "GetCompleteViewMapDensityF1D" */ + string getName() const + { + return "GetCompleteViewMapDensityF1D"; + } + + /*! the () operator. */ + int operator()(Interface1D& inter); + +private: + Functions0D::ReadCompleteViewMapPixelF0D _fun; + float _sampling; +}; + +// GetDirectionalViewMapDensity +/*! Returns the density evaluated for an Interface1D in of the steerable viewmaps image. + * The direction telling which Directional map to choose is explicitely specified by the user. + * The density is evaluated for a set of points along the Interface1D (using the ReadSteerableViewMapPixelF0D functor) + * and then integrated into a single value using a user-defined integration method. + */ +class LIB_STROKE_EXPORT GetDirectionalViewMapDensityF1D : public UnaryFunction1D<double> +{ +public: + /*! Builds the functor. + * \param iOrientation + * The number of the directional map we must work with. + * \param level + * The level of the pyramid from which the pixel must be read. + * \param iType + * The integration method used to compute a single value from a set of values. + * \param sampling + * The resolution used to sample the chain: the corresponding 0D function is evaluated at each sample point and the + * result is obtained by combining the resulting values into a single one, following the method specified by iType. + */ + GetDirectionalViewMapDensityF1D(unsigned iOrientation, unsigned level, IntegrationType iType = MEAN, + float sampling = 2.0f) + : UnaryFunction1D<double>(iType), _fun(iOrientation, level) + { + _sampling = sampling; + } + + /*! Returns the string "GetDirectionalViewMapDensityF1D" */ + string getName() const + { + return "GetDirectionalViewMapDensityF1D"; + } + + /*! the () operator. */ + int operator()(Interface1D& inter); + +private: + Functions0D::ReadSteerableViewMapPixelF0D _fun; + float _sampling; +}; + +// GetSteerableViewMapDensityF1D +/*! Returns the density of the viewmap for a given Interface1D. The density of each FEdge is evaluated + * in the proper steerable ViewMap depending on its oorientation. + */ +class LIB_STROKE_EXPORT GetSteerableViewMapDensityF1D : public UnaryFunction1D<real> +{ +private: + int _level; + float _sampling; + +public: + /*! Builds the functor from the level of the pyramid from which the pixel must be read. + * \param level + * The level of the pyramid from which the pixel must be read. + * \param iType + * The integration method used to compute a single value from a set of values. + * \param sampling + * The resolution used to sample the chain: the corresponding 0D function is evaluated at each sample point and the + * result is obtained by combining the resulting values into a single one, following the method specified by iType. + */ + GetSteerableViewMapDensityF1D(int level,IntegrationType iType = MEAN, float sampling = 2.0f) + : UnaryFunction1D<real>(iType) + { + _level = level; + _sampling = sampling; + } + + /*! Destructor */ + virtual ~GetSteerableViewMapDensityF1D() {} + + /*! Returns the string "GetSteerableViewMapDensityF1D" */ + string getName() const + { + return "GetSteerableViewMapDensityF1D"; + } + + /*! the () operator. */ + int operator()(Interface1D& inter); +}; + +// GetViewMapGradientNormF1D +/*! Returns the density of the viewmap for a given Interface1D. The density of each FEdge is evaluated in + * the proper steerable ViewMap depending on its oorientation. + */ +class LIB_STROKE_EXPORT GetViewMapGradientNormF1D : public UnaryFunction1D<real> +{ +private: + int _level; + float _sampling; + Functions0D::GetViewMapGradientNormF0D _func; + +public: + /*! Builds the functor from the level of the pyramid from which the pixel must be read. + * \param level + * The level of the pyramid from which the pixel must be read. + * \param iType + * The integration method used to compute a single value from a set of values. + * \param sampling + * The resolution used to sample the chain: the corresponding 0D function is evaluated at each sample point and the + * result is obtained by combining the resulting values into a single one, following the method specified by iType. + */ + GetViewMapGradientNormF1D(int level,IntegrationType iType = MEAN, float sampling = 2.0f) + : UnaryFunction1D<real>(iType), _func(level) + { + _level = level; + _sampling = sampling; + } + + /*! Returns the string "GetSteerableViewMapDensityF1D" */ + string getName() const + { + return "GetViewMapGradientNormF1D"; + } + + /*! the () operator. */ + int operator()(Interface1D& inter); +}; + +} // end of namespace Functions1D + +#endif // __FREESTYLE_ADVANCED_FUNCTIONS_1D_H__ diff --git a/source/blender/freestyle/intern/stroke/AdvancedPredicates1D.h b/source/blender/freestyle/intern/stroke/AdvancedPredicates1D.h new file mode 100644 index 00000000000..9235598da78 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/AdvancedPredicates1D.h @@ -0,0 +1,95 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_ADVANCED_PREDICATES_1D_H__ +#define __FREESTYLE_ADVANCED_PREDICATES_1D_H__ + +/** \file blender/freestyle/intern/stroke/AdvancedPredicates1D.h + * \ingroup freestyle + * \brief Class gathering stroke creation algorithms + * \author Stephane Grabli + * \date 01/07/2003 + */ + +#include <string> + +#include "AdvancedFunctions1D.h" +#include "Predicates1D.h" + +#include "../view_map/Interface1D.h" + +// +// Predicates definitions +// +/////////////////////////////////////////////////////////// + +namespace Predicates1D { + +// DensityLowerThanUP1D +/*! Returns true if the density evaluated for the +* Interface1D is less than a user-defined density value. +*/ +class DensityLowerThanUP1D : public UnaryPredicate1D +{ +public: + /*! Builds the functor. + * \param threshold + * The value of the threshold density. + * Any Interface1D having a density lower than this threshold will match. + * \param sigma + * The sigma value defining the density evaluation window size used in the DensityF0D functor. + */ + DensityLowerThanUP1D(double threshold, double sigma = 2) + { + _threshold = threshold; + _sigma = sigma; + } + + /*! Returns the string "DensityLowerThanUP1D" */ + string getName() const + { + return "DensityLowerThanUP1D"; + } + + /*! The () operator. */ + int operator()(Interface1D& inter) + { + Functions1D::DensityF1D fun(_sigma); + if (fun(inter) < 0) + return -1; + result = (fun.result < _threshold); + return 0; + } + +private: + double _sigma; + double _threshold; +}; + +} // end of namespace Predicates1D + +#endif // __FREESTYLE_ADVANCED_PREDICATES_1D_H__ diff --git a/source/blender/freestyle/intern/stroke/AdvancedStrokeShaders.cpp b/source/blender/freestyle/intern/stroke/AdvancedStrokeShaders.cpp new file mode 100644 index 00000000000..e631b9f4471 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/AdvancedStrokeShaders.cpp @@ -0,0 +1,404 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/freestyle/intern/stroke/AdvancedStrokeShaders.cpp + * \ingroup freestyle + * \brief Fredo's stroke shaders + * \author Fredo Durand + * \date 17/12/2002 + */ + +#include "AdvancedStrokeShaders.h" +#include "StrokeIterators.h" + +#include "../system/PseudoNoise.h" +#include "../system/RandGen.h" + +///////////////////////////////////////// +// +// CALLIGRAPHICS SHADER +// +///////////////////////////////////////// + +CalligraphicShader::CalligraphicShader(real iMinThickness, real iMaxThickness, const Vec2f &iOrientation, bool clamp) +: StrokeShader() +{ + _minThickness = iMinThickness; + _maxThickness = iMaxThickness; + _orientation = iOrientation; + _orientation.normalize(); + _clamp = clamp; +} + +float ksinToto = 0.0f; + +int CalligraphicShader::shade(Stroke &ioStroke) const +{ + Interface0DIterator v; + Functions0D::VertexOrientation2DF0D fun; + StrokeVertex *sv; + for (v = ioStroke.verticesBegin(); !v.isEnd(); ++v) { + real thickness; + if (fun(v) < 0) + return -1; + + Vec2f vertexOri(fun.result); + Vec2r ori2d(-vertexOri[1], vertexOri[0]); + ori2d.normalizeSafe(); + real scal = ori2d * _orientation; + sv = dynamic_cast<StrokeVertex*>(&(*v)); + if (_clamp && (scal<0)) { + scal = 0.0; + sv->attribute().setColor(1, 1, 1); + } + else { + scal = fabs(scal); + sv->attribute().setColor(0, 0, 0); + } + thickness = _minThickness + scal * (_maxThickness - _minThickness); + if (thickness < 0.0) + thickness = 0.0; + sv->attribute().setThickness(thickness / 2.0, thickness / 2.0); + } + + return 0; +} + +#if 0 +void TipRemoverShader::shade(Stroke &ioStroke) const +{ + StrokeInternal::StrokeVertexIterator v, vend; + for (v = ioStroke.strokeVerticesBegin(), vend = ioStroke.strokeVerticesEnd(); v != vend; ++v) { + if (((*v)->curvilinearAbscissa() < _tipLength) || + (((*v)->strokeLength() - (*v)->curvilinearAbscissa()) < _tipLength)) { + (*v)->attribute().setThickness(0.0, 0.0); + (*v)->attribute().setColor(1, 1, 1); + } + } +} +#endif + +///////////////////////////////////////// +// +// SPATIAL NOISE SHADER +// +///////////////////////////////////////// + +static const unsigned NB_VALUE_NOISE = 512; + +SpatialNoiseShader::SpatialNoiseShader(float ioamount, float ixScale, int nbOctave, bool smooth, bool pureRandom) +: StrokeShader() +{ + _amount = ioamount; + if (ixScale == 0) + _xScale = 0; + else + _xScale = 1.0 / ixScale / real(NB_VALUE_NOISE); + _nbOctave = nbOctave; + _smooth = smooth; + _pureRandom = pureRandom; +} + +int SpatialNoiseShader::shade(Stroke &ioStroke) const +{ + Interface0DIterator v, v2; + v = ioStroke.verticesBegin(); + Vec2r p(v->getProjectedX(), v->getProjectedY()); + v2 = v; + ++v2; + Vec2r p0(v2->getProjectedX(), v2->getProjectedY()); + p0 = p + 2 * (p - p0); + StrokeVertex *sv; + sv = dynamic_cast<StrokeVertex*>(&(*v)); + real initU = sv->strokeLength() * real(NB_VALUE_NOISE); + if (_pureRandom) + initU += RandGen::drand48() * real(NB_VALUE_NOISE); + + Functions0D::VertexOrientation2DF0D fun; + while (!v.isEnd()) { + sv = dynamic_cast<StrokeVertex*>(&(*v)); + Vec2r p(sv->getPoint()); + if (fun(v) < 0) + return -1; + Vec2r vertexOri(fun.result); + Vec2r ori2d(vertexOri[0], vertexOri[1]); + ori2d = Vec2r(p - p0); + ori2d.normalizeSafe(); + + PseudoNoise mynoise; + real bruit; + + if (_smooth) + bruit = mynoise.turbulenceSmooth(_xScale * sv->curvilinearAbscissa() + initU, _nbOctave); + else + bruit = mynoise.turbulenceLinear(_xScale * sv->curvilinearAbscissa() + initU, _nbOctave); + + Vec2r noise(-ori2d[1] * _amount * bruit, ori2d[0] * _amount * bruit); + + sv->setPoint(p[0] + noise[0], p[1] + noise[1]); + p0 = p; + + ++v; + } + + return 0; +} + +///////////////////////////////////////// +// +// SMOOTHING SHADER +// +///////////////////////////////////////// + +SmoothingShader::SmoothingShader(int ionbIteration, real iFactorPoint, real ifactorCurvature, + real iFactorCurvatureDifference, real iAnisoPoint, real iAnisoNormal, + real iAnisoCurvature, real iCarricatureFactor) +: StrokeShader() +{ + _nbIterations = ionbIteration; + _factorCurvature = ifactorCurvature; + _factorCurvatureDifference = iFactorCurvatureDifference; + _anisoNormal = iAnisoNormal; + _anisoCurvature = iAnisoCurvature; + _carricatureFactor = iCarricatureFactor; + _factorPoint = iFactorPoint; + _anisoPoint = iAnisoPoint; +} + +int SmoothingShader::shade(Stroke &ioStroke) const +{ + // cerr << " Smoothing a stroke " << endl; + + Smoother smoother(ioStroke); + smoother.smooth(_nbIterations, _factorPoint, _factorCurvature, _factorCurvatureDifference, _anisoPoint, + _anisoNormal, _anisoCurvature, _carricatureFactor); + return 0; +} + +// SMOOTHER +//////////////////////////// + +Smoother::Smoother(Stroke &ioStroke) +{ + _stroke = &ioStroke; + + _nbVertices = ioStroke.vertices_size(); + _vertex = new Vec2r[_nbVertices]; + _curvature = new real[_nbVertices]; + _normal = new Vec2r[_nbVertices]; + StrokeInternal::StrokeVertexIterator v, vend; + int i = 0; + for (v = ioStroke.strokeVerticesBegin(), vend = ioStroke.strokeVerticesEnd(); v != vend; ++v, ++i) { + _vertex[i] = (v)->getPoint(); + } + Vec2r vec_tmp(_vertex[0] - _vertex[_nbVertices - 1]); + _isClosedCurve = (vec_tmp.norm() < M_EPSILON); + + _safeTest = (_nbVertices > 4); +} + +void Smoother::smooth(int nbIteration, real iFactorPoint, real ifactorCurvature, real iFactorCurvatureDifference, + real iAnisoPoint, real iAnisoNormal, real iAnisoCurvature, real iCarricatureFactor) +{ + _factorCurvature = ifactorCurvature; + _factorCurvatureDifference = iFactorCurvatureDifference; + _anisoNormal = iAnisoNormal; + _anisoCurvature = iAnisoCurvature; + _carricatureFactor = iCarricatureFactor; + _factorPoint = iFactorPoint; + _anisoPoint = iAnisoPoint; + + for (int i = 0; i < nbIteration; ++i) + iteration (); + copyVertices(); +} + +static real edgeStopping(real x, real sigma) +{ + if (sigma == 0.0) + return 1.0; + return exp(-x * x / (sigma * sigma)); +} + +void Smoother::iteration() +{ + computeCurvature(); + for (int i = 1; i < (_nbVertices - 1); ++i) { + real motionNormal = _factorCurvature * _curvature[i] * edgeStopping(_curvature[i], _anisoNormal); + + real diffC1 = _curvature[i] - _curvature[i - 1]; + real diffC2 = _curvature[i] - _curvature[i + 1]; + real motionCurvature = edgeStopping(diffC1, _anisoCurvature) * diffC1 + + edgeStopping(diffC2, _anisoCurvature) * diffC2; //_factorCurvatureDifference; + motionCurvature *= _factorCurvatureDifference; + //motionCurvature = _factorCurvatureDifference * (diffC1 + diffC2); + if (_safeTest) + _vertex[i] = Vec2r(_vertex[i] + (motionNormal + motionCurvature) * _normal[i]); + Vec2r v1(_vertex[i - 1] - _vertex[i]); + Vec2r v2(_vertex[i + 1] - _vertex[i]); + real d1 = v1.norm(); + real d2 = v2.norm(); + _vertex[i] = Vec2r(_vertex[i] + + _factorPoint * edgeStopping(d2, _anisoPoint) * (_vertex[i - 1] - _vertex[i]) + + _factorPoint * edgeStopping(d1, _anisoPoint) * (_vertex[i + 1] - _vertex[i])); + } + + if (_isClosedCurve) { + real motionNormal = _factorCurvature * _curvature[0] * edgeStopping(_curvature[0], _anisoNormal); + + real diffC1 = _curvature[0] - _curvature[_nbVertices - 2]; + real diffC2 = _curvature[0] - _curvature[1]; + real motionCurvature = edgeStopping(diffC1, _anisoCurvature) * diffC1 + + edgeStopping(diffC2, _anisoCurvature) * diffC2; //_factorCurvatureDifference; + motionCurvature *= _factorCurvatureDifference; + //motionCurvature = _factorCurvatureDifference * (diffC1 + diffC2); + _vertex[0] = Vec2r(_vertex[0] + (motionNormal + motionCurvature) * _normal[0]); + _vertex[_nbVertices - 1] = _vertex[0]; + } +} + + +void Smoother::computeCurvature() +{ + int i; + Vec2r BA, BC, normalCurvature; + for (i = 1; i < (_nbVertices - 1); ++i) { + BA = _vertex[i - 1] - _vertex[i]; + BC = _vertex[i + 1] - _vertex[i]; + real lba = BA.norm(), lbc = BC.norm(); + BA.normalizeSafe(); + BC.normalizeSafe(); + normalCurvature = BA + BC; + + _normal[i] = Vec2r(-(BC - BA)[1], (BC - BA)[0]); + _normal[i].normalizeSafe(); + + _curvature[i] = normalCurvature * _normal[i]; + if (lba + lbc > M_EPSILON) + _curvature[i] /= (0.5 * lba + lbc); + } + _curvature[0] = _curvature[1]; + _curvature[_nbVertices - 1] = _curvature[_nbVertices - 2]; + Vec2r di(_vertex[1] - _vertex[0]); + _normal[0] = Vec2r(-di[1], di[0]); + _normal[0].normalizeSafe(); + di = _vertex[_nbVertices - 1] - _vertex[_nbVertices - 2]; + _normal[_nbVertices - 1] = Vec2r(-di[1], di[0]); + _normal[_nbVertices - 1].normalizeSafe(); + + if (_isClosedCurve) { + BA = _vertex[_nbVertices - 2] - _vertex[0]; + BC = _vertex[1] - _vertex[0]; + real lba = BA.norm(), lbc = BC.norm(); + BA.normalizeSafe(); + BC.normalizeSafe(); + normalCurvature = BA + BC; + + _normal[i] = Vec2r(-(BC - BA)[1], (BC - BA)[0]); + _normal[i].normalizeSafe(); + + _curvature[i] = normalCurvature * _normal[i]; + if (lba + lbc > M_EPSILON) + _curvature[i] /= (0.5 * lba + lbc); + + _normal[_nbVertices - 1] = _normal[0]; + _curvature[_nbVertices - 1] = _curvature[0]; + } +} + +void Smoother::copyVertices() +{ + int i = 0; + StrokeInternal::StrokeVertexIterator v, vend; + for (v = _stroke->strokeVerticesBegin(), vend = _stroke->strokeVerticesEnd(); v != vend; ++v) { + const Vec2r p0((v)->getPoint()); + const Vec2r p1(_vertex[i]); + Vec2r p(p0 + _carricatureFactor * (p1 - p0)); + + (v)->setPoint(p[0], p[1]); + ++i; + } +} + +#if 0 // FIXME + +///////////////////////////////////////// +// +// OMISSION SHADER +// +///////////////////////////////////////// + +OmissionShader::OmissionShader(real sizeWindow, real thrVari, real thrFlat, real lFlat) +{ + _sizeWindow = sizeWindow; + _thresholdVariation = thrVari; + _thresholdFlat = thrFlat; + _lengthFlat = lFlat; +} + +int OmissionShader::shade(Stroke &ioStroke) const +{ + Omitter omi(ioStroke); + omi.omit(_sizeWindow, _thresholdVariation, _thresholdFlat, _lengthFlat); + + return 0; +} + + +// OMITTER +/////////////////////////// + +Omitter::Omitter(Stroke &ioStroke) : Smoother(ioStroke) +{ + StrokeInternal::StrokeVertexIterator v, vend; + int i = 0; + for (v = ioStroke.strokeVerticesBegin(), vend = ioStroke.strokeVerticesEnd(); v != vend; ++v, ++i) { + _u[i] = (v)->curvilinearAbscissa(); + } +} + +void Omitter::omit(real sizeWindow, real thrVari, real thrFlat, real lFlat) +{ + _sizeWindow=sizeWindow; + _thresholdVariation=thrVari; + _thresholdFlat=thrFlat; + _lengthFlat=lFlat; + + for (int i = 1; i < _nbVertices-1; ++i) { + if (_u[i] < _lengthFlat) + continue; + // is the previous segment flat? + int j = i - 1; + while ((j >= 0) && (_u[i] - _u[j] < _lengthFlat)) { + if ((_normal[j] * _normal[i]) < _thresholdFlat) + ; // FIXME + --j; + } + } +} + +#endif diff --git a/source/blender/freestyle/intern/stroke/AdvancedStrokeShaders.h b/source/blender/freestyle/intern/stroke/AdvancedStrokeShaders.h new file mode 100644 index 00000000000..a957a3f7155 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/AdvancedStrokeShaders.h @@ -0,0 +1,226 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_ADVANCED_STROKE_SHADERS_H__ +#define __FREESTYLE_ADVANCED_STROKE_SHADERS_H__ + +/** \file blender/freestyle/intern/stroke/AdvancedStrokeShaders.h + * \ingroup freestyle + * \brief Fredo's stroke shaders + * \author Fredo Durand + * \date 17/12/2002 + */ + +#include "BasicStrokeShaders.h" + +/*! [ Thickness Shader ]. + * Assigns thicknesses to the stroke vertices so that the stroke looks like made with a calligraphic tool. + * i.e. The stroke will be the thickest in a main direction, the thinest in the direction perpendicular to this one, + * and an interpolation inbetween. + */ +class LIB_STROKE_EXPORT CalligraphicShader : public StrokeShader +{ +public: + /*! Builds the shader. + * \param iMinThickness + * The minimum thickness in the direction perpandicular to the main direction. + * \param iMaxThickness + * The maximum thickness in the main direction. + * \param iOrientation + * The 2D vector giving the main direction. + * \param clamp + * Tells ??? + */ + CalligraphicShader(real iMinThickness, real iMaxThickness, const Vec2f &iOrientation, bool clamp); + + /*! Destructor. */ + virtual ~CalligraphicShader() {} + + /*! The shading method */ + virtual int shade(Stroke &ioStroke) const; + +protected: + real _maxThickness; + real _minThickness; + Vec2f _orientation; + bool _clamp; +}; + +/*! [ Geometry Shader ]. + * Spatial Noise stroke shader. + * Moves the vertices to make the stroke more noisy. + * @see \htmlonly <a href=noise/noise.html>noise/noise.html</a> \endhtmlonly + */ +class LIB_STROKE_EXPORT SpatialNoiseShader : public StrokeShader +{ +public: + /*! Builds the shader. + * \param iAmount + * The amplitude of the noise. + * \param ixScale + * The noise frequency + * \param nbOctave + * The number of octaves + * \param smooth + * If you want the noise to be smooth + * \param pureRandom + * If you don't want any coherence + */ + SpatialNoiseShader(float iAmount, float ixScale, int nbOctave, bool smooth, bool pureRandom); + + /*! Destructor. */ + virtual ~SpatialNoiseShader() {} + + /*! The shading method. */ + virtual int shade(Stroke &ioStroke) const; + +protected: + float _amount; + float _xScale; + int _nbOctave; + bool _smooth; + bool _pureRandom; +}; + +/*! [ Geometry Shader ]. + * Smoothes the stroke. + * (Moves the vertices to make the stroke smoother). + * Uses curvature flow to converge towards a curve of constant curvature. The diffusion method we use is anisotropic + * to prevent the diffusion accross corners. + * @see \htmlonly <a href=/smoothing/smoothing.html>smoothing/smoothing.html</a> endhtmlonly + */ +class LIB_STROKE_EXPORT SmoothingShader : public StrokeShader +{ +public: + /*! Builds the shader. + * \param iNbIteration + * The number of iterations. (400) + * \param iFactorPoint + * 0 + * \param ifactorCurvature + * 0 + * \param iFactorCurvatureDifference + * 0.2 + * \param iAnisoPoint + * 0 + * \param iAnisNormal + * 0 + * \param iAnisoCurvature + * 0 + * \param icarricatureFactor + * 1 + */ + SmoothingShader(int iNbIteration, real iFactorPoint, real ifactorCurvature, real iFactorCurvatureDifference, + real iAnisoPoint, real iAnisoNormal, real iAnisoCurvature, real icarricatureFactor); + + /*! Destructor. */ + virtual ~SmoothingShader() {} + + /*! The shading method. */ + virtual int shade(Stroke &ioStroke) const; + +protected: + int _nbIterations; + real _factorPoint; + real _factorCurvature; + real _factorCurvatureDifference; + real _anisoPoint; + real _anisoNormal; + real _anisoCurvature; + real _carricatureFactor; +}; + +class LIB_STROKE_EXPORT Smoother +{ +public: + Smoother(Stroke &ioStroke); + + virtual ~Smoother() {} + + void smooth(int nbIterations, real iFactorPoint, real ifactorCurvature, real iFactorCurvatureDifference, + real iAnisoPoint, real iAnisoNormal, real iAnisoCurvature, real icarricatureFactor); + + void computeCurvature(); + +protected: + real _factorPoint; + real _factorCurvature; + real _factorCurvatureDifference; + real _anisoPoint; + real _anisoNormal; + real _anisoCurvature; + real _carricatureFactor; + + void iteration(); + void copyVertices (); + + Stroke *_stroke; + int _nbVertices; + Vec2r *_vertex; + Vec2r *_normal; + real *_curvature; + bool *_isFixedVertex; + + bool _isClosedCurve; + bool _safeTest; +}; + +class LIB_STROKE_EXPORT Omitter : public Smoother +{ +public: + Omitter(Stroke &ioStroke); + + virtual ~Omitter() {} + + void omit(real sizeWindow, real thrVari, real thrFlat, real lFlat); + +protected: + real *_u; + + real _sizeWindow; + real _thresholdVariation; + real _thresholdFlat; + real _lengthFlat; +}; + +/*! Omission shader */ +class LIB_STROKE_EXPORT OmissionShader : public StrokeShader +{ +public: + OmissionShader(real sizeWindow, real thrVari, real thrFlat, real lFlat); + virtual ~OmissionShader() {} + + virtual int shade(Stroke &ioStroke) const; + +protected: + real _sizeWindow; + real _thresholdVariation; + real _thresholdFlat; + real _lengthFlat; +}; + +#endif // __FREESTYLE_ADVANCED_STROKE_SHADERS_H__ diff --git a/source/blender/freestyle/intern/stroke/BasicStrokeShaders.cpp b/source/blender/freestyle/intern/stroke/BasicStrokeShaders.cpp new file mode 100644 index 00000000000..dd6e6dc317e --- /dev/null +++ b/source/blender/freestyle/intern/stroke/BasicStrokeShaders.cpp @@ -0,0 +1,1134 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/freestyle/intern/stroke/BasicStrokeShaders.cpp + * \ingroup freestyle + * \brief Class gathering basic stroke shaders + * \author Stephane Grabli + * \date 17/12/2002 + */ + +#include <fstream> + +#include "AdvancedFunctions0D.h" +#include "AdvancedFunctions1D.h" +#include "BasicStrokeShaders.h" +#include "StrokeIO.h" +#include "StrokeIterators.h" +#include "StrokeRenderer.h" + +#include "../system/PseudoNoise.h" +#include "../system/RandGen.h" +#include "../system/StringUtils.h" + +#include "../view_map/Functions0D.h" +#include "../view_map/Functions1D.h" + +#include "BKE_global.h" + +//soc #include <qimage.h> +//soc #include <QString> + +extern "C" { +# include "IMB_imbuf.h" +# include "IMB_imbuf_types.h" +} + +// Internal function + +#if 0 // soc +void convert(const QImage& iImage, float **oArray, unsigned &oSize) +{ + oSize = iImage.width(); + *oArray = new float[oSize]; + for (unsigned int i = 0; i < oSize; ++i) { + QRgb rgb = iImage.pixel(i,0); + (*oArray)[i] = ((float)qBlue(rgb)) / 255.0f; + } +} +#endif + +static void convert(ImBuf *imBuf, float **oArray, unsigned &oSize) +{ + oSize = imBuf->x; + *oArray = new float[oSize]; + + char *pix; + for (unsigned int i = 0; i < oSize; ++i) { + pix = (char*) imBuf->rect + i * 4; + (*oArray)[i] = ((float) pix[2]) / 255.0f; + } +} + +namespace StrokeShaders { + +// +// Thickness modifiers +// +////////////////////////////////////////////////////////// + +int ConstantThicknessShader::shade(Stroke& stroke) const +{ + StrokeInternal::StrokeVertexIterator v, vend; + int i = 0; + int size = stroke.strokeVerticesSize(); + for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend; ++v) { + // XXX What's the use of i here? And is not the thickness always overriden by the last line of the loop? + if ((1 == i) || (size - 2 == i)) + v->attribute().setThickness(_thickness / 4.0, _thickness / 4.0); + if ((0 == i) || (size - 1 == i)) + v->attribute().setThickness(0, 0); + + v->attribute().setThickness(_thickness / 2.0, _thickness / 2.0); + } + return 0; +} + +int ConstantExternThicknessShader::shade(Stroke& stroke) const +{ + StrokeInternal::StrokeVertexIterator v, vend; + int i = 0; + int size = stroke.strokeVerticesSize(); + for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend; ++v) { + // XXX What's the use of i here? And is not the thickness always overriden by the last line of the loop? + if ((1 == i) || (size - 2 == i)) + v->attribute().setThickness(_thickness / 2.0, 0); + if ((0 == i) || (size - 1 == i)) + v->attribute().setThickness(0, 0); + + v->attribute().setThickness(_thickness, 0); + } + return 0; +} + +int IncreasingThicknessShader::shade(Stroke& stroke) const +{ + int n = stroke.strokeVerticesSize() - 1, i; + StrokeInternal::StrokeVertexIterator v, vend; + for (i = 0, v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); + v != vend; + ++v, ++i) + { + float t; + if (i < (float)n / 2.0f) + t = (1.0 - (float)i / (float)n) * _ThicknessMin + (float)i / (float)n * _ThicknessMax; + else + t = (1.0 - (float)i / (float)n) * _ThicknessMax + (float)i / (float)n * _ThicknessMin; + v->attribute().setThickness(t / 2.0, t / 2.0); + } + return 0; +} + +int ConstrainedIncreasingThicknessShader::shade(Stroke& stroke) const +{ + float slength = stroke.getLength2D(); + float maxT = min(_ratio*slength,_ThicknessMax); + int n = stroke.strokeVerticesSize() - 1, i; + StrokeInternal::StrokeVertexIterator v, vend; + for (i = 0, v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); + v != vend; + ++v, ++i) + { + // XXX Why not using an if/else here? Else, if last condition is true, everything else is computed for nothing! + float t; + if (i < (float)n / 2.0f) + t = (1.0 - (float)i / (float)n) * _ThicknessMin + (float)i / (float)n * maxT; + else + t = (1.0 - (float)i / (float)n) * maxT + (float)i / (float)n * _ThicknessMin; + v->attribute().setThickness(t / 2.0, t / 2.0); + if (i == n - 1) + v->attribute().setThickness(_ThicknessMin / 2.0, _ThicknessMin / 2.0); + } + return 0; +} + + +int LengthDependingThicknessShader::shade(Stroke& stroke) const +{ + float step = (_maxThickness - _minThickness) / 3.0f; + float l = stroke.getLength2D(); + float thickness = 0.0f; + if (l > 300.0f) + thickness = _minThickness + 3.0f * step; + else if ((l < 300.0f) && (l > 100.0f)) + thickness = _minThickness + 2.0f * step; + else if ((l < 100.0f) && (l > 50.0f)) + thickness = _minThickness + 1.0f * step; + else // else if (l < 50.0f), tsst... + thickness = _minThickness; + + StrokeInternal::StrokeVertexIterator v, vend; + int i = 0; + int size = stroke.strokeVerticesSize(); + for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend; ++v) { + // XXX What's the use of i here? And is not the thickness always overriden by the last line of the loop? + if ((1 == i) || (size - 2 == i)) + v->attribute().setThickness(thickness / 4.0, thickness / 4.0); + if ((0 == i) || (size - 1 == i)) + v->attribute().setThickness(0, 0); + + v->attribute().setThickness(thickness / 2.0, thickness / 2.0); + } + return 0; +} + + +ThicknessVariationPatternShader::ThicknessVariationPatternShader(const string pattern_name, float iMinThickness, + float iMaxThickness, bool stretch) +: StrokeShader() +{ + _stretch = stretch; + _minThickness = iMinThickness; + _maxThickness = iMaxThickness; + ImBuf *image = NULL; //soc + vector<string> pathnames; + StringUtils::getPathName(TextureManager::Options::getPatternsPath(), pattern_name, pathnames); + for (vector<string>::const_iterator j = pathnames.begin(); j != pathnames.end(); ++j) { + ifstream ifs(j->c_str()); + if (ifs.is_open()) { + //soc image.load(j->c_str()); + /* OCIO_TODO: support different input color space */ + image = IMB_loadiffname(j->c_str(), 0, NULL); + break; + } + } + if (image == NULL) //soc + cerr << "Error: cannot find pattern \"" << pattern_name << "\" - check the path in the Options" << endl; + else + convert(image, &_aThickness, _size); +} + + +int ThicknessVariationPatternShader::shade(Stroke& stroke) const +{ + StrokeInternal::StrokeVertexIterator v, vend; + float *array = NULL; + /* int size; */ /* UNUSED */ + array = _aThickness; + /* size = _size; */ /* UNUSED */ + int vert_size = stroke.strokeVerticesSize(); + int sig = 0; + unsigned index; + const float* originalThickness; + for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend; ++v) { + originalThickness = v->attribute().getThickness(); + if (_stretch) { + float tmp = v->u() * (_size - 1); + index = (unsigned)floor(tmp); + if ((tmp - index) > (index + 1 - tmp)) + ++index; + } + else { + index = (unsigned)floor(v->curvilinearAbscissa()); + } + index %= _size; + float thicknessR = array[index] * originalThickness[0]; + float thicknessL = array[index] * originalThickness[1]; + if (thicknessR+thicknessL < _minThickness) { + thicknessL = _minThickness/2.0f; + thicknessR = _minThickness/2.0f; + } + if (thicknessR+thicknessL > _maxThickness) { + thicknessL = _maxThickness/2.0f; + thicknessR = _maxThickness/2.0f; + } + if ((sig == 0) || (sig == vert_size - 1)) + v->attribute().setThickness(1, 1); + else + v->attribute().setThickness(thicknessR, thicknessL); + ++sig; + } + return 0; +} + + +static const unsigned NB_VALUE_NOISE = 512; + +ThicknessNoiseShader::ThicknessNoiseShader() : StrokeShader() +{ + _amplitude = 1.0f; + _scale = 1.0f / 2.0f / (float)NB_VALUE_NOISE; +} + +ThicknessNoiseShader::ThicknessNoiseShader(float iAmplitude, float iPeriod) : StrokeShader() +{ + _amplitude = iAmplitude; + _scale = 1.0f / iPeriod / (float)NB_VALUE_NOISE; +} + +int ThicknessNoiseShader::shade(Stroke& stroke) const +{ + StrokeInternal::StrokeVertexIterator v = stroke.strokeVerticesBegin(), vend; + real initU1 = v->strokeLength() * real(NB_VALUE_NOISE) + RandGen::drand48() * real(NB_VALUE_NOISE); + real initU2 = v->strokeLength() * real(NB_VALUE_NOISE) + RandGen::drand48() * real(NB_VALUE_NOISE); + + real bruit, bruit2; + PseudoNoise mynoise, mynoise2; + for (vend = stroke.strokeVerticesEnd(); v != vend; ++v) { + bruit = mynoise.turbulenceSmooth(_scale * v->curvilinearAbscissa() + initU1, 2); // 2 : nbOctaves + bruit2 = mynoise2.turbulenceSmooth(_scale * v->curvilinearAbscissa() + initU2, 2); // 2 : nbOctaves + const float *originalThickness = v->attribute().getThickness(); + float r = bruit * _amplitude + originalThickness[0]; + float l = bruit2 * _amplitude + originalThickness[1]; + v->attribute().setThickness(r, l); + } + + return 0; +} + +// +// Color shaders +// +/////////////////////////////////////////////////////////////////////////////// + +int ConstantColorShader::shade(Stroke& stroke) const +{ + StrokeInternal::StrokeVertexIterator v, vend; + for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend; ++v) { + v->attribute().setColor(_color[0], _color[1], _color[2]); + v->attribute().setAlpha(_color[3]); + } + return 0; +} + +int IncreasingColorShader::shade(Stroke& stroke) const +{ + StrokeInternal::StrokeVertexIterator v, vend; + int n = stroke.strokeVerticesSize() - 1, yo; + float newcolor[4]; + for (yo = 0, v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); + v != vend; + ++v, ++yo) + { + for (int i = 0; i < 4; ++i) { + newcolor[i] = (1.0 - (float) yo / (float)n) * _colorMin[i] + (float)yo / (float)n * _colorMax[i]; + } + v->attribute().setColor(newcolor[0], newcolor[1], newcolor[2]); + v->attribute().setAlpha(newcolor[3]); + } + return 0; +} + +ColorVariationPatternShader::ColorVariationPatternShader(const string pattern_name, bool stretch) : StrokeShader() +{ + _stretch = stretch; + ImBuf *image = NULL; //soc + vector<string> pathnames; + StringUtils::getPathName(TextureManager::Options::getPatternsPath(), pattern_name, pathnames); + for (vector<string>::const_iterator j = pathnames.begin(); j != pathnames.end(); ++j) { + ifstream ifs(j->c_str()); + if (ifs.is_open()) { + /* OCIO_TODO: support different input color space */ + image = IMB_loadiffname(j->c_str(), 0, NULL); //soc + break; + } + } + if (image == NULL) //soc + cerr << "Error: cannot find pattern \"" << pattern_name << "\" - check the path in the Options" << endl; + else + convert(image, &_aVariation, _size); +} + +int ColorVariationPatternShader::shade(Stroke& stroke) const +{ + StrokeInternal::StrokeVertexIterator v, vend; + unsigned index; + for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend; ++v) { + const float *originalColor = v->attribute().getColor(); + if (_stretch) { + float tmp = v->u() * (_size - 1); + index = (unsigned)floor(tmp); + if ((tmp - index) > (index + 1 - tmp)) + ++index; + } + else { + index = (unsigned)floor(v->curvilinearAbscissa()); + } + index %= _size; + float r = _aVariation[index] * originalColor[0]; + float g = _aVariation[index] * originalColor[1]; + float b = _aVariation[index] * originalColor[2]; + v->attribute().setColor(r, g, b); + } + return 0; +} + +int MaterialColorShader::shade(Stroke& stroke) const +{ + Interface0DIterator v, vend; + Functions0D::MaterialF0D fun; + StrokeVertex *sv; + for (v = stroke.verticesBegin(), vend = stroke.verticesEnd(); v != vend; ++v) { + if (fun(v) < 0) + return -1; + const float *diffuse = fun.result.diffuse(); + sv = dynamic_cast<StrokeVertex*>(&(*v)); + sv->attribute().setColor(diffuse[0] * _coefficient, diffuse[1] * _coefficient, diffuse[2] * _coefficient); + sv->attribute().setAlpha(diffuse[3]); + } + return 0; +} + + +int CalligraphicColorShader::shade(Stroke& stroke) const +{ + Interface0DIterator v; + Functions0D::VertexOrientation2DF0D fun; + StrokeVertex *sv; + for (v = stroke.verticesBegin(); !v.isEnd(); ++v) { + if (fun(v) < 0) + return -1; + Vec2f vertexOri(fun.result); + Vec2d ori2d(-vertexOri[1], vertexOri[0]); + ori2d.normalizeSafe(); + real scal = ori2d * _orientation; + sv = dynamic_cast<StrokeVertex*>(&(*v)); + if ((scal < 0)) + sv->attribute().setColor(0, 0, 0); + else + sv->attribute().setColor(1, 1, 1); + } + return 0; +} + + +ColorNoiseShader::ColorNoiseShader() : StrokeShader() +{ + _amplitude = 1.0f; + _scale = 1.0f / 2.0f / (float)NB_VALUE_NOISE; +} + +ColorNoiseShader::ColorNoiseShader(float iAmplitude, float iPeriod) : StrokeShader() +{ + _amplitude = iAmplitude; + _scale = 1.0f / iPeriod / (float)NB_VALUE_NOISE; +} + +int ColorNoiseShader::shade(Stroke& stroke) const +{ + StrokeInternal::StrokeVertexIterator v = stroke.strokeVerticesBegin(), vend; + real initU = v->strokeLength() * real(NB_VALUE_NOISE) + RandGen::drand48() * real(NB_VALUE_NOISE); + + real bruit; + PseudoNoise mynoise; + for (vend = stroke.strokeVerticesEnd(); v != vend; ++v) { + bruit = mynoise.turbulenceSmooth(_scale * v->curvilinearAbscissa() + initU, 2); // 2 : nbOctaves + const float *originalColor = v->attribute().getColor(); + float r = bruit * _amplitude + originalColor[0]; + float g = bruit * _amplitude + originalColor[1]; + float b = bruit * _amplitude + originalColor[2]; + v->attribute().setColor(r, g, b); + } + + return 0; +} + + +// +// Texture Shaders +// +/////////////////////////////////////////////////////////////////////////////// + +int TextureAssignerShader::shade(Stroke& stroke) const +{ +#if 0 + getBrushTextureIndex(TEXTURES_DIR "/brushes/charcoalAlpha.bmp", Stroke::HUMID_MEDIUM); + getBrushTextureIndex(TEXTURES_DIR "/brushes/washbrushAlpha.bmp", Stroke::HUMID_MEDIUM); + getBrushTextureIndex(TEXTURES_DIR "/brushes/oil.bmp", Stroke::HUMID_MEDIUM); + getBrushTextureIndex(TEXTURES_DIR "/brushes/oilnoblend.bmp", Stroke::HUMID_MEDIUM); + getBrushTextureIndex(TEXTURES_DIR "/brushes/charcoalAlpha.bmp", Stroke::DRY_MEDIUM); + getBrushTextureIndex(TEXTURES_DIR "/brushes/washbrushAlpha.bmp", Stroke::DRY_MEDIUM); + getBrushTextureIndex(TEXTURES_DIR "/brushes/opaqueDryBrushAlpha.bmp", Stroke::OPAQUE_MEDIUM); + getBrushTextureIndex(TEXTURES_DIR "/brushes/opaqueBrushAlpha.bmp", Stroke::OPAQUE_MEDIUM); +#endif + + TextureManager *instance = TextureManager::getInstance(); + if (!instance) + return 0; + string pathname; + Stroke::MediumType mediumType; + bool hasTips = false; + switch (_textureId) { + case 0: + //pathname = TextureManager::Options::getBrushesPath() + "/charcoalAlpha.bmp"; + pathname = "/charcoalAlpha.bmp"; + mediumType = Stroke::HUMID_MEDIUM; + hasTips = false; + break; + case 1: + pathname = "/washbrushAlpha.bmp"; + mediumType = Stroke::HUMID_MEDIUM; + hasTips = true; + break; + case 2: + pathname = "/oil.bmp"; + mediumType = Stroke::HUMID_MEDIUM; + hasTips = true; + break; + case 3: + pathname = "/oilnoblend.bmp"; + mediumType = Stroke::HUMID_MEDIUM; + hasTips = true; + break; + case 4: + pathname = "/charcoalAlpha.bmp"; + mediumType = Stroke::DRY_MEDIUM; + hasTips = false; + break; + case 5: + mediumType = Stroke::DRY_MEDIUM; + hasTips = true; + break; + case 6: + pathname = "/opaqueDryBrushAlpha.bmp"; + mediumType = Stroke::OPAQUE_MEDIUM; + hasTips = true; + break; + case 7: + pathname = "/opaqueBrushAlpha.bmp"; + mediumType = Stroke::OPAQUE_MEDIUM; + hasTips = true; + break; + default: + pathname = "/smoothAlpha.bmp"; + mediumType = Stroke::OPAQUE_MEDIUM; + hasTips = false; + break; + } + unsigned int texId = instance->getBrushTextureIndex(pathname, mediumType); + stroke.setMediumType(mediumType); + stroke.setTips(hasTips); + stroke.setTextureId(texId); + return 0; +} + +// FIXME +int StrokeTextureShader::shade(Stroke& stroke) const +{ + TextureManager *instance = TextureManager::getInstance(); + if (!instance) + return 0; + string pathname = TextureManager::Options::getBrushesPath() + "/" + _texturePath; + unsigned int texId = instance->getBrushTextureIndex(pathname, _mediumType); + stroke.setMediumType(_mediumType); + stroke.setTips(_tips); + stroke.setTextureId(texId); + return 0; +} + +// +// Geometry Shaders +// +/////////////////////////////////////////////////////////////////////////////// + +int BackboneStretcherShader::shade(Stroke& stroke) const +{ + float l = stroke.getLength2D(); + if (l <= 1.0e-6) + return 0; + + StrokeInternal::StrokeVertexIterator v0 = stroke.strokeVerticesBegin(); + StrokeInternal::StrokeVertexIterator v1 = v0; + ++v1; + StrokeInternal::StrokeVertexIterator vn = stroke.strokeVerticesEnd(); + --vn; + StrokeInternal::StrokeVertexIterator vn_1 = vn; + --vn_1; + + + Vec2d first((v0)->x(), (v0)->y()); + Vec2d last((vn)->x(), (vn)->y()); + + Vec2d d1(first-Vec2d((v1)->x(), (v1)->y())); + d1.normalize(); + Vec2d dn(last-Vec2d((vn_1)->x(), (vn_1)->y())); + dn.normalize(); + + Vec2d newFirst(first + _amount * d1); + (v0)->setPoint(newFirst[0], newFirst[1]); + Vec2d newLast(last + _amount * dn); + (vn)->setPoint(newLast[0], newLast[1]); + + stroke.UpdateLength(); + return 0; +} + +int SamplingShader::shade(Stroke& stroke) const +{ + stroke.Resample(_sampling); + stroke.UpdateLength(); + return 0; +} + +int ExternalContourStretcherShader::shade(Stroke& stroke) const +{ + //float l = stroke.getLength2D(); + Interface0DIterator it; + Functions0D::Normal2DF0D fun; + StrokeVertex* sv; + for (it = stroke.verticesBegin(); !it.isEnd(); ++it) { + if (fun(it) < 0) + return -1; + Vec2f n(fun.result); + sv = dynamic_cast<StrokeVertex*>(&(*it)); + Vec2d newPoint(sv->x() + _amount * n.x(), sv->y() + _amount * n.y()); + sv->setPoint(newPoint[0], newPoint[1]); + } + stroke.UpdateLength(); + return 0; +} + +int BSplineShader::shade(Stroke& stroke) const +{ + if (stroke.strokeVerticesSize() < 4) + return 0; + + // Find the new vertices + vector<Vec2d> newVertices; + double t = 0.0; + float _sampling = 5.0f; + + StrokeInternal::StrokeVertexIterator p0, p1, p2, p3, end; + p0 = stroke.strokeVerticesBegin(); + p1 = p0; + p2 = p1; + p3 = p2; + end = stroke.strokeVerticesEnd(); + double a[4], b[4]; + int n = 0; + while (p1 != end) { +#if 0 + if (p1 == end) + p1 = p0; +#endif + if (p2 == end) + p2 = p1; + if (p3 == end) + p3 = p2; + // compute new matrix + a[0] = (-(p0)->x() + 3 * (p1)->x() - 3 * (p2)->x() + (p3)->x()) / 6.0; + a[1] = (3 * (p0)->x() - 6 * (p1)->x() + 3 * (p2)->x()) / 6.0; + a[2] = (-3 * (p0)->x() + 3 * (p2)->x()) / 6.0; + a[3] = ((p0)->x() + 4 * (p1)->x() + (p2)->x()) / 6.0; + + b[0] = (-(p0)->y() + 3 * (p1)->y() - 3 * (p2)->y() + (p3)->y()) / 6.0; + b[1] = (3 * (p0)->y() - 6 * (p1)->y() + 3 * (p2)->y()) / 6.0; + b[2] = (-3 * (p0)->y() + 3 * (p2)->y()) / 6.0; + b[3] = ((p0)->y() + 4 * (p1)->y() + (p2)->y()) / 6.0; + + // draw the spline depending on resolution: + Vec2d p1p2((p2)->x() - (p1)->x(), (p2)->y() - (p1)->y()); + double norm = p1p2.norm(); + //t = _sampling / norm; + t = 0; + while (t < 1) { + newVertices.push_back(Vec2d((a[3] + t * (a[2] + t * (a[1] + t * a[0]))), + (b[3] + t * (b[2] + t * (b[1] + t * b[0]))))); + t = t + _sampling / norm; + } + if (n > 2) { + ++p0; + ++p1; + ++p2; + ++p3; + } + else { + if (n == 0) + ++p3; + if (n == 1) { + ++p2; + ++p3; + } + if (n == 2) { + ++p1; + ++p2; + ++p3; + } + ++n; + } + } + //last point: + newVertices.push_back(Vec2d((p0)->x(), (p0)->y())); + + int originalSize = newVertices.size(); + _sampling = stroke.ComputeSampling(originalSize); + + // Resample and set x,y coordinates + stroke.Resample(_sampling); + int newsize = stroke.strokeVerticesSize(); + + int nExtraVertex = 0; + if (newsize < originalSize) { + cerr << "Warning: unsufficient resampling" << endl; + } + else { + nExtraVertex = newsize - originalSize; + } + + // assigns the new coordinates: + vector<Vec2d>::iterator p = newVertices.begin(), pend = newVertices.end(); + vector<Vec2d>::iterator last = p; + n = 0; + StrokeInternal::StrokeVertexIterator it, itend; + for (it = stroke.strokeVerticesBegin(), itend = stroke.strokeVerticesEnd(); + (it != itend) && (p != pend); + ++it, ++p, ++n) + { + it->setX(p->x()); + it->setY(p->y()); + last = p; + } + + // nExtraVertex should stay unassigned + for (int i = 0; i < nExtraVertex; ++i, ++it, ++n) { + it->setX(last->x()); + it->setY(last->y()); + if (it.isEnd()) { + // XXX Shouldn't we break in this case??? + cerr << "Warning: Problem encountered while creating B-spline" << endl; + } + } + stroke.UpdateLength(); + return 0; +} + +//!! Bezier curve stroke shader +int BezierCurveShader::shade(Stroke& stroke) const +{ + if (stroke.strokeVerticesSize() < 4) + return 0; + + // Build the Bezier curve from this set of data points: + vector<Vec2d> data; + StrokeInternal::StrokeVertexIterator v = stroke.strokeVerticesBegin(), vend; + data.push_back(Vec2d(v->x(), v->y())); //first one + StrokeInternal::StrokeVertexIterator previous = v; + ++v; + for (vend = stroke.strokeVerticesEnd(); v != vend; ++v) { + if (!((fabs(v->x() - (previous)->x()) < M_EPSILON) && ((fabs(v->y() - (previous)->y()) < M_EPSILON)))) + data.push_back(Vec2d(v->x(), v->y())); + previous = v; + } + +#if 0 + Vec2d tmp; + bool equal = false; + if (data.front() == data.back()) { + tmp = data.back(); + data.pop_back(); + equal = true; + } +#endif + // here we build the bezier curve + BezierCurve bcurve(data, _error); + + // bad performances are here !!! // FIXME + vector<Vec2d> CurveVertices; + vector<BezierCurveSegment*>& bsegments = bcurve.segments(); + vector<BezierCurveSegment*>::iterator s = bsegments.begin(), send; + vector<Vec2d>& segmentsVertices = (*s)->vertices(); + vector<Vec2d>::iterator p, pend; + // first point + CurveVertices.push_back(segmentsVertices[0]); + for (send = bsegments.end(); s != send; ++s) { + segmentsVertices = (*s)->vertices(); + p = segmentsVertices.begin(); + ++p; + for (pend = segmentsVertices.end(); p != pend; ++p) { + CurveVertices.push_back((*p)); + } + } + +#if 0 + if (equal) { + if (data.back() == data.front()) { + vector<Vec2d>::iterator d = data.begin(), dend; + if (G.debug & G_DEBUG_FREESTYLE) { + cout << "ending point = starting point" << endl; + cout << "---------------DATA----------" << endl; + for (dend = data.end(); d != dend; ++d) { + cout << d->x() << "-" << d->y() << endl; + } + cout << "--------------BEZIER RESULT----------" << endl; + for (d = CurveVertices.begin(), dend = CurveVertices.end(); d != dend; ++d) { + cout << d->x() << "-" << d->y() << endl; + } + } + } + } +#endif + + // Resample the Stroke depending on the number of vertices of the bezier curve: + int originalSize = CurveVertices.size(); +#if 0 + float sampling = stroke.ComputeSampling(originalSize); + stroke.Resample(sampling); +#endif + stroke.Resample(originalSize); + int newsize = stroke.strokeVerticesSize(); + int nExtraVertex = 0; + if (newsize < originalSize) { + cerr << "Warning: unsufficient resampling" << endl; + } + else { +#if 0 + if (G.debug & G_DEBUG_FREESTYLE) { + cout << "Oversampling" << endl; + } +#endif + nExtraVertex = newsize - originalSize; + if (nExtraVertex != 0) { + if (G.debug & G_DEBUG_FREESTYLE) { + cout << "Bezier Shader : Stroke " << stroke.getId() << " have not been resampled" << endl; + } + } + } + + // assigns the new coordinates: + p = CurveVertices.begin(); + vector<Vec2d>::iterator last = p; + int n; + StrokeInternal::StrokeVertexIterator it, itend; +#if 0 + for (; p != pend; ++n, ++p); +#endif + for (n = 0, it = stroke.strokeVerticesBegin(), itend = stroke.strokeVerticesEnd(), pend = CurveVertices.end(); + (it != itend) && (p != pend); + ++it, ++p, ++n) + { + it->setX(p->x()); + it->setY(p->y()); +#if 0 + double x = p->x(); + double y = p->y(); + if (G.debug & G_DEBUG_FREESTYLE) { + cout << "x = " << x << "-" << "y = " << y << endl; + } +#endif + last = p; + } + stroke.UpdateLength(); + + // Deal with extra vertices: + if (nExtraVertex == 0) + return 0; + + // nExtraVertex should stay unassigned + vector<StrokeAttribute> attributes; + vector<StrokeVertex*> verticesToRemove; + for (int i = 0; i < nExtraVertex; ++i, ++it, ++n) { + verticesToRemove.push_back(&(*it)); + if (it.isEnd()) { + // XXX Shocking! :P Shouldn't we break in this case??? + if (G.debug & G_DEBUG_FREESTYLE) { + cout << "fucked up" << endl; + } + } + } + for (it = stroke.strokeVerticesBegin(); it != itend; ++it) { + attributes.push_back(it->attribute()); + } + + for (vector<StrokeVertex*>::iterator vr = verticesToRemove.begin(), vrend = verticesToRemove.end(); + vr != vrend; + ++vr) + { + stroke.RemoveVertex(*vr); + } + + vector<StrokeAttribute>::iterator a = attributes.begin(), aend = attributes.end(); + int index = 0; + int index1 = (int)floor((float)originalSize / 2.0); + int index2 = index1 + nExtraVertex; + for (it = stroke.strokeVerticesBegin(), itend = stroke.strokeVerticesEnd(); + (it != itend) && (a != aend); + ++it) + { + (it)->setAttribute(*a); + if ((index <= index1) || (index > index2)) + ++a; + ++index; + } + return 0; +} + +int InflateShader::shade(Stroke& stroke) const +{ + // we're computing the curvature variance of the stroke. (Combo 5) + // If it's too high, forget about it + Functions1D::Curvature2DAngleF1D fun; + if (fun(stroke) < 0) + return -1; + if (fun.result > _curvatureThreshold) + return 0; + + Functions0D::VertexOrientation2DF0D ori_fun; + Functions0D::Curvature2DAngleF0D curv_fun; + Functions1D::Normal2DF1D norm_fun; + Interface0DIterator it; + StrokeVertex *sv; + for (it = stroke.verticesBegin(); !it.isEnd(); ++it) { + if (ori_fun(it) < 0) + return -1; + Vec2f ntmp(ori_fun.result); + Vec2f n(ntmp.y(), -ntmp.x()); + if (norm_fun(stroke) < 0) + return -1; + Vec2f strokeN(norm_fun.result); + if (n*strokeN < 0) { + n[0] = -n[0]; + n[1] = -n[1]; + } + sv = dynamic_cast<StrokeVertex*>(&(*it)); + float u = sv->u(); + float t = 4.0f * (0.25f - (u - 0.5) * (u - 0.5)); + if (curv_fun(it) < 0) + return -1; + float curvature_coeff = (M_PI - curv_fun.result) / M_PI; + Vec2d newPoint(sv->x() + curvature_coeff * t * _amount * n.x(), + sv->y() + curvature_coeff * t * _amount * n.y()); + sv->setPoint(newPoint[0], newPoint[1]); + } + stroke.UpdateLength(); + return 0; +} + +class CurvePiece +{ +public: + StrokeInternal::StrokeVertexIterator _begin; + StrokeInternal::StrokeVertexIterator _last; + Vec2d A; + Vec2d B; + int size; + float _error; + + CurvePiece(StrokeInternal::StrokeVertexIterator b, StrokeInternal::StrokeVertexIterator l, int iSize) + { + _begin = b; + _last = l; + A = Vec2d((_begin)->x(), (_begin)->y()); + B = Vec2d((_last)->x(), (_last)->y()); + size = iSize; + } + + float error() + { + float maxE = 0.0f; + for (StrokeInternal::StrokeVertexIterator it = _begin; it != _last; ++it) { + Vec2d P(it->x(), it->y()); + float d = GeomUtils::distPointSegment(P, A, B); + if (d > maxE) + maxE = d; + } + _error = maxE; + return maxE; + } + + //! Subdivides the curve into two pieces. + // The first piece is this same object (modified) + // The second piece is returned by the method + CurvePiece *subdivide() + { + StrokeInternal::StrokeVertexIterator it = _begin; + int actualSize = (size > 1) ? size / 2 : 1; + for (int i = 0; i <= actualSize; ++it, ++i); + + CurvePiece *second = new CurvePiece(it, _last, size - actualSize + 1); + size = actualSize; + _last = it; + B = Vec2d((_last)->x(), (_last)->y()); + return second; + } +}; + +int PolygonalizationShader::shade(Stroke& stroke) const +{ + vector<CurvePiece*> _pieces; + vector<CurvePiece*> _results; + vector<CurvePiece*>::iterator cp, cpend; + + // Compute first approx: + StrokeInternal::StrokeVertexIterator a = stroke.strokeVerticesBegin(); + StrokeInternal::StrokeVertexIterator b = stroke.strokeVerticesEnd(); + --b; + int size = stroke.strokeVerticesSize(); + + CurvePiece *piece = new CurvePiece(a, b, size); + _pieces.push_back(piece); + + while (!_pieces.empty()) { + piece = _pieces.back(); + _pieces.pop_back(); + if (piece->error() > _error) { + CurvePiece *second = piece->subdivide(); + _pieces.push_back(second); + _pieces.push_back(piece); + } + else { + _results.push_back(piece); + } + } + + // actually modify the geometry for each piece: + for (cp = _results.begin(), cpend = _results.end(); cp != cpend; ++cp) { + a = (*cp)->_begin; + b = (*cp)->_last; + Vec2d u = (*cp)->B - (*cp)->A; + Vec2d n(u[1], -u[0]); + n.normalize(); + //Vec2d n(0, 0); + float offset = ((*cp)->_error); + StrokeInternal::StrokeVertexIterator v; + for (v = a; v != b; ++v) { + v->setPoint((*cp)->A.x() + v->u() * u.x() + n.x() * offset, + (*cp)->A.y() + v->u() * u.y() + n.y() * offset); + } +#if 0 + u.normalize(); + (*a)->setPoint((*a)->x() - u.x() * 10, (*a)->y() - u.y() * 10); +#endif + } + stroke.UpdateLength(); + + // delete stuff + for (cp = _results.begin(), cpend = _results.end(); cp != cpend; ++cp) { + delete (*cp); + } + _results.clear(); + return 0; +} + +int GuidingLinesShader::shade(Stroke& stroke) const +{ + Functions1D::Normal2DF1D norm_fun; + StrokeInternal::StrokeVertexIterator a = stroke.strokeVerticesBegin(); + StrokeInternal::StrokeVertexIterator b = stroke.strokeVerticesEnd(); + --b; + int size = stroke.strokeVerticesSize(); + CurvePiece piece(a, b, size); + + Vec2d u = piece.B - piece.A; + Vec2f n(u[1], -u[0]); + n.normalize(); + if (norm_fun(stroke) < 0) + return -1; + Vec2f strokeN(norm_fun.result); + if (n * strokeN < 0) { + n[0] = -n[0]; + n[1] = -n[1]; + } + float offset = (piece.error()) / 2.0f * _offset; + StrokeInternal::StrokeVertexIterator v, vend; + for (v = a, vend = stroke.strokeVerticesEnd(); v != vend; ++v) { + v->setPoint(piece.A.x() + v->u() * u.x() + n.x() * offset, + piece.A.y() + v->u() * u.y() + n.y() * offset); + } + stroke.UpdateLength(); + return 0; +} + +///////////////////////////////////////// +// +// Tip Remover +// +///////////////////////////////////////// + + +TipRemoverShader::TipRemoverShader(real tipLength) : StrokeShader() +{ + _tipLength = tipLength; +} + +int TipRemoverShader::shade(Stroke& stroke) const +{ + int originalSize = stroke.strokeVerticesSize(); + + if (originalSize < 4) + return 0; + + StrokeInternal::StrokeVertexIterator v, vend; + vector<StrokeVertex*> verticesToRemove; + vector<StrokeAttribute> oldAttributes; + for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend; ++v) { + if ((v->curvilinearAbscissa() < _tipLength) || (v->strokeLength() - v->curvilinearAbscissa() < _tipLength)) { + verticesToRemove.push_back(&(*v)); + } + oldAttributes.push_back(v->attribute()); + } + + if (originalSize - verticesToRemove.size() < 2) + return 0; + + vector<StrokeVertex*>::iterator sv, svend; + for (sv = verticesToRemove.begin(), svend = verticesToRemove.end(); sv != svend; ++sv) { + stroke.RemoveVertex((*sv)); + } + + // Resample so that our new stroke have the same number of vertices than before + stroke.Resample(originalSize); + + if ((int)stroke.strokeVerticesSize() != originalSize) //soc + cerr << "Warning: resampling problem" << endl; + + // assign old attributes to new stroke vertices: + vector<StrokeAttribute>::iterator a = oldAttributes.begin(), aend = oldAttributes.end(); +#if 0 + if (G.debug & G_DEBUG_FREESTYLE) { + cout << "-----------------------------------------------" << endl; + } +#endif + for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); + (v != vend) && (a != aend); + ++v, ++a) + { + v->setAttribute(*a); +#if 0 + if (G.debug & G_DEBUG_FREESTYLE) { + cout << "thickness = " << (*a).getThickness()[0] << "-" << (*a).getThickness()[1] << endl; + } +#endif + } + // we're done! + return 0; +} + +int streamShader::shade(Stroke& stroke) const +{ + if (G.debug & G_DEBUG_FREESTYLE) { + cout << stroke << endl; + } + return 0; +} + +int fstreamShader::shade(Stroke& stroke) const +{ + _stream << stroke << endl; + return 0; +} + +} // end of namespace StrokeShaders diff --git a/source/blender/freestyle/intern/stroke/BasicStrokeShaders.h b/source/blender/freestyle/intern/stroke/BasicStrokeShaders.h new file mode 100644 index 00000000000..58647861390 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/BasicStrokeShaders.h @@ -0,0 +1,902 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_BASIC_STROKE_SHADERS_H__ +#define __FREESTYLE_BASIC_STROKE_SHADERS_H__ + +/** \file blender/freestyle/intern/stroke/BasicStrokeShaders.h + * \ingroup freestyle + * \brief Class gathering basic stroke shaders + * \author Stephane Grabli + * \date 17/12/2002 + */ + +#include <fstream> + +#include "Stroke.h" +#include "StrokeShader.h" + +#include "../geometry/Bezier.h" +#include "../geometry/Geom.h" + +using namespace std; +using namespace Geometry; + +namespace StrokeShaders { + +// +// Thickness modifiers +// +////////////////////////////////////////////////////// + +/*! [ Thickness Shader ]. + * Assigns an absolute constant thickness to every vertices of the Stroke. + */ +class LIB_STROKE_EXPORT ConstantThicknessShader : public StrokeShader +{ +public: + /*! Builds the shader. + * \param thickness + * The thickness that must be assigned to the stroke. + */ + ConstantThicknessShader(float thickness) : StrokeShader() + { + _thickness = thickness; + } + + /*! Destructor. */ + virtual ~ConstantThicknessShader() {} + + /*! Returns the string "ConstantThicknessShader".*/ + virtual string getName() const + { + return "ConstantThicknessShader"; + } + + /*! The shading method. */ + virtual int shade(Stroke& stroke) const; + +private: + float _thickness; +}; + +/* [ Thickness Shader ]. + * Assigns an absolute constant external thickness to every vertices of the Stroke. The external thickness of a point + * is its thickness from the point to the strip border in the direction pointing outside the object the + * Stroke delimitates. + */ +class LIB_STROKE_EXPORT ConstantExternThicknessShader : public StrokeShader +{ +public: + ConstantExternThicknessShader(float thickness) : StrokeShader() + { + _thickness = thickness; + } + + virtual ~ConstantExternThicknessShader() {} + + virtual string getName() const + { + return "ConstantExternThicknessShader"; + } + + virtual int shade(Stroke& stroke) const; + +private: + float _thickness; +}; + +/*! [ Thickness Shader ]. + * Assigns thicknesses values such as the thickness increases from a thickness value A to a thickness value B between + * the first vertex to the midpoint vertex and then decreases from B to a A between this midpoint vertex + * and the last vertex. + * The thickness is linearly interpolated from A to B. + */ +class LIB_STROKE_EXPORT IncreasingThicknessShader : public StrokeShader +{ +public: + /*! Builds the shader. + * \param iThicknessMin + * The first thickness value. + * \param iThicknessMax + * The second thickness value. + */ + IncreasingThicknessShader(float iThicknessMin, float iThicknessMax) : StrokeShader() + { + _ThicknessMin = iThicknessMin; + _ThicknessMax = iThicknessMax; + } + + /*! Destructor.*/ + virtual ~IncreasingThicknessShader() {} + + virtual string getName() const + { + return "IncreasingThicknessShader"; + } + + /*! The shading method. */ + virtual int shade(Stroke& stroke) const; + +private: + float _ThicknessMin; + float _ThicknessMax; +}; + +/*! [ Thickness shader ]. + * Same as previous but here we allow the user to control the ratio thickness/length so that we don't get + * fat short lines + */ +class LIB_STROKE_EXPORT ConstrainedIncreasingThicknessShader : public StrokeShader +{ +private: + float _ThicknessMin; + float _ThicknessMax; + float _ratio; + +public: + /*! Builds the shader. + * \param iThicknessMin + * The first thickness value. + * \param iThicknessMax + * The second thickness value. + * \param iRatio + * The ration thickness/length we don't want to exceed. + */ + ConstrainedIncreasingThicknessShader(float iThicknessMin, float iThicknessMax, float iRatio) : StrokeShader() + { + _ThicknessMin = iThicknessMin; + _ThicknessMax = iThicknessMax; + _ratio = iRatio; + } + + /*! Destructor.*/ + virtual ~ConstrainedIncreasingThicknessShader() {} + + virtual string getName() const + { + return "ConstrainedIncreasingThicknessShader"; + } + + /*! The shading method. */ + virtual int shade(Stroke& stroke) const; +}; + +/* [ Thickness Shader ]. + * Modifys the thickness in a relative way depending on its length. + */ +class LIB_STROKE_EXPORT LengthDependingThicknessShader : public StrokeShader +{ +private: + float _minThickness; + float _maxThickness; + // We divide the strokes in 4 categories: + // l > 300 + // 100 < l < 300 + // 50 < l < 100 + // l < 50 + +public: + LengthDependingThicknessShader(float iMinThickness, float iMaxThickness) : StrokeShader() + { + _minThickness = iMinThickness; + _maxThickness = iMaxThickness; + } + + virtual ~LengthDependingThicknessShader() {} + + virtual string getName() const + { + return "LengthDependingThicknessShader"; + } + + virtual int shade(Stroke& stroke) const; +}; + +/*! [ Thickness Shader ]. +* Applys a pattern (texture) to vary thickness. +* The new thicknesses are the result of the multiplication +* of the pattern and the original thickness +*/ +class LIB_STROKE_EXPORT ThicknessVariationPatternShader : public StrokeShader +{ +public: + /*! Builds the shader. + * \param pattern_name + * The texture file name. + * \param iMinThickness + * The minimum thickness we don't want to exceed. + * \param iMaxThickness + * The maximum thickness we don't want to exceed. + * \param stretch + * Tells whether the pattern texture must be stretched or repeted to fit the stroke. + */ + ThicknessVariationPatternShader(const string pattern_name, float iMinThickness = 1.0f, float iMaxThickness = 5.0f, + bool stretch = true); + + /*! Destructor.*/ + virtual ~ThicknessVariationPatternShader() + { + if (0 != _aThickness) { + delete[] _aThickness; + _aThickness = 0; + } + } + + virtual string getName() const + { + return "ThicknessVariationPatternShader"; + } + + /*! The shading method. */ + virtual int shade(Stroke& stroke) const; + +private: + float *_aThickness; // array of thickness values, in % of the max (i.e comprised between 0 and 1) + unsigned _size; + float _minThickness; + float _maxThickness; + bool _stretch; +}; + +/*! [ Thickness Shader ]. + * Adds some noise to the stroke thickness. + * \see \htmlonly <a href=noise/noise.html>noise/noise.html</a>\endhtmlonly + */ +class LIB_STROKE_EXPORT ThicknessNoiseShader : public StrokeShader +{ +private: + float _amplitude; + float _scale; + +public: + ThicknessNoiseShader(); + + /*! Builds a Thickness Noise Shader + * \param iAmplitude + * The amplitude of the noise signal + * \param iPeriod + * The period of the noise signal + */ + ThicknessNoiseShader(float iAmplitude, float iPeriod); + + virtual string getName() const + { + return "ThicknessNoiseShader"; + } + + /*! The shading method. */ + virtual int shade(Stroke& stroke) const; +}; + + +// +// Color shaders +// +///////////////////////////////////////////////////////// +/*! [ Color Shader ]. + * Assigns a constant color to every vertices of the Stroke. + */ +class LIB_STROKE_EXPORT ConstantColorShader : public StrokeShader +{ +public: + /*! Builds the shader from a user-specified color. + * \param iR + * The red component + * \param iG + * The green component + * \param iB + * The blue component + * \param iAlpha + * The alpha value + */ + ConstantColorShader(float iR, float iG, float iB, float iAlpha = 1.0f) : StrokeShader() + { + _color[0] = iR; + _color[1] = iG; + _color[2] = iB; + _color[3] = iAlpha; + } + + virtual string getName() const + { + return "ConstantColorShader"; + } + + /*! The shading method. */ + virtual int shade(Stroke& stroke) const; + +private: + float _color[4]; +}; + +/*! [ Color Shader ]. + * Assigns a varying color to the stroke. + * The user specifies 2 colors A and B. The stroke color will change linearly from A to B between the + * first and the last vertex. + */ +class LIB_STROKE_EXPORT IncreasingColorShader : public StrokeShader +{ +private: + float _colorMin[4]; + float _colorMax[4]; + +public: + /*! Builds the shader from 2 user-specified colors. + * \param iRm + * The first color red component + * \param iGm + * The first color green component + * \param iBm + * The first color blue component + * \param iAlpham + * The first color alpha value + * \param iRM + * The second color red component + * \param iGM + * The second color green component + * \param iBM + * The second color blue component + * \param iAlphaM + * The second color alpha value + */ + IncreasingColorShader(float iRm, float iGm, float iBm, float iAlpham, + float iRM, float iGM, float iBM, float iAlphaM) + : StrokeShader() + { + _colorMin[0] = iRm; + _colorMin[1] = iGm; + _colorMin[2] = iBm; + _colorMin[3] = iAlpham; + + _colorMax[0] = iRM; + _colorMax[1] = iGM; + _colorMax[2] = iBM; + _colorMax[3] = iAlphaM; + } + + virtual string getName() const + { + return "IncreasingColorShader"; + } + + /*! The shading method. */ + virtual int shade(Stroke& stroke) const; +}; + +/*! [ Color Shader ]. + * Applys a pattern to vary original color. + * The new color is the result of the multiplication of the pattern and the original color + */ +class LIB_STROKE_EXPORT ColorVariationPatternShader : public StrokeShader +{ +public: + /*! Builds the shader from the pattern texture file name. + * \param pattern_name + * The file name of the texture file to use as pattern + * \param stretch + * Tells whether the texture must be strecthed or repeted to fit the stroke. + */ + ColorVariationPatternShader(const string pattern_name, bool stretch = true); + + /*! Destructor */ + virtual ~ColorVariationPatternShader() + { + if (0 != _aVariation) { + delete[] _aVariation; + _aVariation = 0; + } + } + + virtual string getName() const + { + return "ColorVariationPatternShader"; + } + + /*! The shading method. */ + virtual int shade(Stroke& stroke) const; + +private: + float *_aVariation; // array of coef values, in % of the max (i.e comprised between 0 and 1) + unsigned _size; + bool _stretch; +}; + +/* [ Color Shader ]. + * Assigns a color to the stroke depending on the material of the shape to which ot belongs to. (Disney shader) + */ +class LIB_STROKE_EXPORT MaterialColorShader : public StrokeShader +{ +private: + float _coefficient; + +public: + MaterialColorShader(float coeff = 1.0f) : StrokeShader() + { + _coefficient = coeff; + } + + virtual string getName() const + { + return "MaterialColorShader"; + } + + virtual int shade(Stroke& stroke) const; +}; + +class LIB_STROKE_EXPORT CalligraphicColorShader : public StrokeShader +{ +private: + int _textureId; + Vec2d _orientation; + +public: + CalligraphicColorShader(const Vec2d &iOrientation) : StrokeShader() + { + _orientation = iOrientation; + _orientation.normalize(); + } + + virtual string getName() const + { + return "CalligraphicColorShader"; + } + + virtual int shade(Stroke& stroke) const; +}; + +/*! [ Color Shader ]. + * Shader to add noise to the stroke colors. + */ +class LIB_STROKE_EXPORT ColorNoiseShader : public StrokeShader +{ +private: + float _amplitude; + float _scale; + +public: + ColorNoiseShader(); + + /*! Builds a Color Noise Shader + * \param iAmplitude + * The amplitude of the noise signal + * \param iPeriod + * The period of the noise signal + */ + ColorNoiseShader(float iAmplitude, float iPeriod); + + virtual string getName() const + { + return "ColorNoiseShader"; + } + + /*! The shading method. */ + virtual int shade(Stroke& stroke) const; +}; + +// +// Texture Shaders +// +/////////////////////////////////////////////////////////////////////////////// +/*! [ Texture Shader ]. +* Assigns a texture to the stroke in order to simulate +* its marks system. This shader takes as input an integer value +* telling which texture and blending mode to use among a set of +* predefined textures. +* Here are the different presets: +* 0) -> /brushes/charcoalAlpha.bmp, HUMID_MEDIUM +* 1) -> /brushes/washbrushAlpha.bmp, HUMID_MEDIUM +* 2) -> /brushes/oil.bmp, HUMID_MEDIUM +* 3) -> /brushes/oilnoblend.bmp, HUMID_MEDIUM +* 4) -> /brushes/charcoalAlpha.bmp, DRY_MEDIUM +* 5) -> /brushes/washbrushAlpha.bmp, DRY_MEDIUM +* 6) -> /brushes/opaqueDryBrushAlpha.bmp, OPAQUE_MEDIUM +* 7) -> /brushes/opaqueBrushAlpha.bmp, Stroke::OPAQUE_MEDIUM +* Any other value will lead to the following preset: +* default) -> /brushes/smoothAlpha.bmp, OPAQUE_MEDIUM. +*/ +class LIB_STROKE_EXPORT TextureAssignerShader : public StrokeShader // FIXME +{ +private: + int _textureId; + +public: + /*! Builds the shader. + * \param id + * The number of the preset to use. + */ + TextureAssignerShader(int id) : StrokeShader() + { + _textureId = id; + } + + virtual string getName() const + { + return "TextureAssignerShader"; + } + + /*! The shading method */ + virtual int shade(Stroke& stroke) const; +}; + +/*! [ Texture Shader ]. +* Assigns a texture and a blending mode to the stroke +* in order to simulate its marks system. +*/ +class LIB_STROKE_EXPORT StrokeTextureShader : public StrokeShader +{ +private: + string _texturePath; + Stroke::MediumType _mediumType; + bool _tips; // 0 or 1 + +public: + /*! Builds the shader from the texture file name and the blending mode to use. + * \param textureFile + * The the texture file name. + * \attention The textures must be placed in the $FREESTYLE_DIR/data/textures/brushes directory. + * \param mediumType + * The medium type and therefore, the blending mode that must be used for the rendering of this stroke. + * \param iTips + * Tells whether the texture includes tips or not. + * If it is the case, the texture image must respect the following format: + * \verbatim + * __________ + * | | + * | A | + * |__________| + * | | | + * | B | C | + * |_____|____| + * + * \endverbatim + * - A : The stroke's corpus texture + * - B : The stroke's left extremity texture + * - C : The stroke's right extremity texture + */ + StrokeTextureShader(const string textureFile, Stroke::MediumType mediumType = Stroke::OPAQUE_MEDIUM, + bool iTips = false) + : StrokeShader() + { + _texturePath = textureFile; + _mediumType = mediumType; + _tips = iTips; + } + + virtual string getName() const + { + return "StrokeTextureShader"; + } + + /*! The shading method */ + virtual int shade(Stroke& stroke) const; +}; + + +// +// Geometry Shaders +// +/////////////////////////////////////////////////////////////////////////////// +/*! [ Geometry Shader ]. + * Stretches the stroke at its two extremities and following the respective directions: v(1)v(0) and v(n-1)v(n). + */ +class LIB_STROKE_EXPORT BackboneStretcherShader : public StrokeShader +{ +private: + float _amount; + +public: + /*! Builds the shader. + * \param iAmount + * The stretching amount value. + */ + BackboneStretcherShader(float iAmount = 2.0f) : StrokeShader() + { + _amount = iAmount; + } + + virtual string getName() const + { + return "BackboneStretcherShader"; + } + + /*! The shading method */ + virtual int shade(Stroke& stroke) const; +}; + +/*! [ Geometry Shader. ] + * Resamples the stroke. + * @see Stroke::Resample(float). + */ +class LIB_STROKE_EXPORT SamplingShader: public StrokeShader +{ +private: + float _sampling; + +public: + /*! Builds the shader. + * \param sampling + * The sampling to use for the stroke resampling + */ + SamplingShader(float sampling) : StrokeShader() + { + _sampling = sampling; + } + + virtual string getName() const + { + return "SamplingShader"; + } + + /*! The shading method */ + virtual int shade(Stroke& stroke) const; +}; + + +class LIB_STROKE_EXPORT ExternalContourStretcherShader : public StrokeShader +{ +private: + float _amount; + +public: + ExternalContourStretcherShader(float iAmount = 2.0f) : StrokeShader() + { + _amount = iAmount; + } + + virtual string getName() const + { + return "ExternalContourStretcherShader"; + } + + virtual int shade(Stroke& stroke) const; +}; + +// B-Spline stroke shader +class LIB_STROKE_EXPORT BSplineShader: public StrokeShader +{ +public: + BSplineShader() : StrokeShader() {} + + virtual string getName() const + { + return "BSplineShader"; + } + + virtual int shade(Stroke& stroke) const; +}; + + +// Bezier curve stroke shader +/*! [ Geometry Shader ]. + * Transforms the stroke backbone geometry so that it corresponds to a Bezier Curve approximation of the + * original backbone geometry. + * @see \htmlonly <a href=bezier/bezier.html>bezier/bezier.html</a> \endhtmlonly + */ +class LIB_STROKE_EXPORT BezierCurveShader : public StrokeShader +{ +private: + float _error; + +public: + /*! Builds the shader. + * \param error + * The error we're allowing for the approximation. + * This error is the max distance allowed between the new curve and the original geometry. + */ + BezierCurveShader(float error = 4.0) : StrokeShader() + { + _error = error; + } + + virtual string getName() const + { + return "BezierCurveShader"; + } + + /*! The shading method */ + virtual int shade(Stroke& stroke) const; +}; + +/* Shader to inflate the curves. It keeps the extreme points positions and moves the other ones along the 2D normal. + * The displacement value is proportional to the 2d curvature at the considered point (the higher the curvature, + * the smaller the displacement) and to a value specified by the user. + */ +class LIB_STROKE_EXPORT InflateShader : public StrokeShader +{ +private: + float _amount; + float _curvatureThreshold; + +public: + /*! Builds an inflate shader + * \param iAmount + * A multiplicative coefficient that acts on the amount and direction of displacement + * \param iThreshold + * The curves having a 2d curvature > iThreshold at one of their points is not inflated + */ + InflateShader(float iAmount, float iThreshold) : StrokeShader() + { + _amount = iAmount; + _curvatureThreshold = iThreshold; + } + + virtual string getName() const + { + return "InflateShader"; + } + + /*! The shading method */ + virtual int shade(Stroke& stroke) const; +}; + +/*! [ Geometry Shader ]. + * Shader to modify the Stroke geometry so that it looks more "polygonal". + * The basic idea is to start from the minimal stroke approximation consisting in a line joining the first vertex + * to the last one and to subdivide using the original stroke vertices until a certain error is reached. + */ +class LIB_STROKE_EXPORT PolygonalizationShader : public StrokeShader +{ +private: + float _error; + +public: + /*! Builds the shader. + * \param iError + * The error we want our polygonal approximation to have with respect to the original geometry. + * The smaller, the closer the new stroke to the orinal one. + * This error corresponds to the maximum distance between the new stroke and the old one. + */ + PolygonalizationShader(float iError) : StrokeShader() + { + _error = iError; + } + + virtual string getName() const + { + return "PolygonalizationShader"; + } + + /*! The shading method */ + virtual int shade(Stroke& stroke) const; +}; + + +/*! [ Geometry Shader ]. + * Shader to modify the Stroke geometry so that it corresponds to its main direction line. + * This shader must be used together with the splitting operator using the curvature criterion. + * Indeed, the precision of the approximation will depend on the size of the stroke's pieces. + * The bigger the pieces, the rougher the approximation. + */ +class LIB_STROKE_EXPORT GuidingLinesShader : public StrokeShader +{ +private: + float _offset; + +public: + /*! Builds a Guiding Lines shader + * \param iOffset + * The line that replaces the stroke is initially in the middle of the initial stroke "bbox". + * iOffset is the value of the displacement which is applied to this line along its normal. + */ + GuidingLinesShader(float iOffset) : StrokeShader() + { + _offset = iOffset; + } + + virtual string getName() const + { + return "GuidingLinesShader"; + } + + /*! The shading method */ + virtual int shade(Stroke& stroke) const; +}; + +/*! [ Geometry Shader ]. + * Removes the stroke's extremities. + */ +class LIB_STROKE_EXPORT TipRemoverShader : public StrokeShader +{ +public: + /*! Builds the shader. + * \param tipLength + * The length of the piece of stroke we want to remove at each extremity. + */ + TipRemoverShader (real tipLength); + + /*! Destructor. */ + virtual ~TipRemoverShader () {} + + /*! The shading method */ + virtual string getName() const + { + return "TipRemoverShader"; + } + + virtual int shade(Stroke &stroke) const; + +protected: + real _tipLength; +}; + +/*! [ output Shader ]. + * streams the Stroke + */ +class LIB_STROKE_EXPORT streamShader : public StrokeShader +{ +public: + /*! Destructor. */ + virtual ~streamShader() {} + + /*! Returns the string "streamShader".*/ + virtual string getName() const + { + return "streamShader"; + } + + /*! The shading method. */ + virtual int shade(Stroke& stroke) const; +}; + +/*! [ output Shader ]. + * streams the Stroke in a file + */ +class LIB_STROKE_EXPORT fstreamShader : public StrokeShader +{ +protected: + mutable ofstream _stream; + +public: + /*! Builds the shader from the output file name */ + fstreamShader(const char *iFileName) : StrokeShader() + { + _stream .open(iFileName); + if (!_stream.is_open()) { + cerr << "couldn't open file " << iFileName << endl; + } + } + + /*! Destructor. */ + virtual ~fstreamShader() + { + _stream.close(); + } + + /*! Returns the string "fstreamShader".*/ + virtual string getName() const + { + return "fstreamShader"; + } + + /*! The shading method. */ + virtual int shade(Stroke& stroke) const; +}; + +} // end of namespace StrokeShaders + +#endif // __FREESTYLE_BASIC_STROKE_SHADERS_H__ diff --git a/source/blender/freestyle/intern/stroke/Canvas.cpp b/source/blender/freestyle/intern/stroke/Canvas.cpp new file mode 100644 index 00000000000..4427192a7b0 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Canvas.cpp @@ -0,0 +1,477 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/freestyle/intern/stroke/Canvas.cpp + * \ingroup freestyle + * \brief Class to define a canvas designed to draw style modules + * \author Stephane Grabli + * \date 20/10/2002 + */ + +#include <sstream> +#include <vector> + +#include "Canvas.h" +#include "StrokeRenderer.h" +#include "StyleModule.h" + +#include "../image/Image.h" +#include "../image/GaussianFilter.h" +#include "../image/ImagePyramid.h" + +#include "../system/FreestyleConfig.h" +#include "../system/TimeStamp.h" +#include "../system/PseudoNoise.h" + +#include "../view_map/SteerableViewMap.h" + +#include "BKE_global.h" + +//soc #include <qimage.h> +//soc #include <QString> + +extern "C" { +# include "IMB_imbuf.h" +# include "IMB_imbuf_types.h" +} + +using namespace std; + +LIB_STROKE_EXPORT +Canvas *Canvas::_pInstance = 0; + +LIB_STROKE_EXPORT +const char *Canvas::_MapsPath = 0; + +Canvas::Canvas() +{ + _SelectedFEdge = 0; + _pInstance = this; + PseudoNoise::init(42); + _Renderer = 0; + _current_sm = NULL; + _steerableViewMap = new SteerableViewMap(NB_STEERABLE_VIEWMAP - 1); + _basic = false; +} + +Canvas::Canvas(const Canvas& iBrother) +{ + _SelectedFEdge = iBrother._SelectedFEdge; + _pInstance = this; + PseudoNoise::init(42); + _Renderer = iBrother._Renderer; + _current_sm = iBrother._current_sm; + _steerableViewMap = new SteerableViewMap(*(iBrother._steerableViewMap)); + _basic = iBrother._basic; +} + +Canvas::~Canvas() +{ + _pInstance = 0; + + Clear(); + if (_Renderer) { + delete _Renderer; + _Renderer = 0; + } + // FIXME: think about an easy control for the maps memory management... + if (!_maps.empty()) { + for (mapsMap::iterator m = _maps.begin(), mend = _maps.end(); m != mend; ++m) { + delete ((*m).second); + } + _maps.clear(); + } + if (_steerableViewMap) + delete _steerableViewMap; +} + +void Canvas::preDraw() {} + +void Canvas::Draw() +{ + if (_StyleModules.empty()) + return; + preDraw(); + TimeStamp *timestamp = TimeStamp::instance(); + + for (unsigned int i = 0; i < _StyleModules.size(); ++i) { + _current_sm = _StyleModules[i]; + + if (i < _Layers.size() && _Layers[i]) + delete _Layers[i]; + + _Layers[i] = _StyleModules[i]->execute(); + if (!_Layers[i]) + continue; + + stroke_count += _Layers[i]->strokes_size(); + + timestamp->increment(); + } + postDraw(); +} + +void Canvas::postDraw() +{ + update(); +} + + +void Canvas::Clear() +{ + if (!_Layers.empty()) { + for (deque<StrokeLayer*>::iterator sl = _Layers.begin(), slend = _Layers.end(); sl != slend; ++sl) { + if (*sl) + delete (*sl); + } + _Layers.clear(); + } + + if (!_StyleModules.empty()) { + for (deque<StyleModule*>::iterator s = _StyleModules.begin(), send = _StyleModules.end(); s != send; ++s) { + if (*s) + delete (*s); + } + _StyleModules.clear(); + } + if (_steerableViewMap) + _steerableViewMap->Reset(); + + stroke_count = 0; +} + +void Canvas::Erase() +{ + if (!_Layers.empty()) { + for (deque<StrokeLayer*>::iterator sl = _Layers.begin(), slend = _Layers.end(); sl != slend; ++sl) { + if (*sl) + (*sl)->clear(); + } + } + if (_steerableViewMap) + _steerableViewMap->Reset(); + update(); + + stroke_count = 0; +} + +void Canvas::PushBackStyleModule(StyleModule *iStyleModule) +{ + StrokeLayer* layer = new StrokeLayer(); + _StyleModules.push_back(iStyleModule); + _Layers.push_back(layer); +} + +void Canvas::InsertStyleModule(unsigned index, StyleModule *iStyleModule) +{ + unsigned size = _StyleModules.size(); + StrokeLayer *layer = new StrokeLayer(); + if ((_StyleModules.empty()) || (index == size)) { + _StyleModules.push_back(iStyleModule); + _Layers.push_back(layer); + return; + } + _StyleModules.insert(_StyleModules.begin() + index, iStyleModule); + _Layers.insert(_Layers.begin() + index, layer); +} + +void Canvas::RemoveStyleModule(unsigned index) +{ + unsigned int i = 0; + if (!_StyleModules.empty()) { + for (deque<StyleModule*>::iterator s = _StyleModules.begin(), send = _StyleModules.end(); + s != send; + ++s, ++i) + { + if (i == index) { + // remove shader + if (*s) + delete *s; + _StyleModules.erase(s); + break; + } + } + } + + if (!_Layers.empty()) { + i = 0; + for (deque<StrokeLayer*>::iterator sl = _Layers.begin(), slend = _Layers.end(); + sl != slend; + ++sl, ++i) + { + if (i == index) { + // remove layer + if (*sl) + delete *sl; + _Layers.erase(sl); + break; + } + } + } +} + + +void Canvas::SwapStyleModules(unsigned i1, unsigned i2) +{ + StyleModule *tmp; + tmp = _StyleModules[i1]; + _StyleModules[i1] = _StyleModules[i2]; + _StyleModules[i2] = tmp; + + StrokeLayer *tmp2; + tmp2 = _Layers[i1]; + _Layers[i1] = _Layers[i2]; + _Layers[i2] = tmp2; +} + +void Canvas::ReplaceStyleModule(unsigned index, StyleModule *iStyleModule) +{ + unsigned i = 0; + for (deque<StyleModule*>::iterator s = _StyleModules.begin(), send = _StyleModules.end(); s != send; ++s, ++i) { + if (i == index) { + if (*s) + delete *s; + *s = iStyleModule; + break; + } + } +} + +void Canvas::setVisible(unsigned index, bool iVisible) +{ + _StyleModules[index]->setDisplayed(iVisible); +} + +void Canvas::setModified(unsigned index, bool iMod) +{ + _StyleModules[index]->setModified(iMod); +} + +void Canvas::resetModified(bool iMod/* = false */) +{ + unsigned int size = _StyleModules.size(); + for (unsigned int i = 0; i < size; ++i) + setModified(i, iMod); +} + +void Canvas::causalStyleModules(vector<unsigned>& vec, unsigned index) +{ + unsigned int size = _StyleModules.size(); + + for (unsigned int i = index; i < size; ++i) { + if (_StyleModules[i]->getCausal()) + vec.push_back(i); + } +} + +void Canvas::Render(const StrokeRenderer *iRenderer) +{ + for (unsigned int i = 0; i < _StyleModules.size(); ++i) { + if (!_StyleModules[i]->getDisplayed() || !_Layers[i]) + continue; + _Layers[i]->Render(iRenderer); + } +} + +void Canvas::RenderBasic(const StrokeRenderer *iRenderer) +{ + for (unsigned int i = 0; i < _StyleModules.size(); ++i) { + if (!_StyleModules[i]->getDisplayed() || !_Layers[i]) + continue; + _Layers[i]->RenderBasic(iRenderer); + } +} + +void Canvas::loadMap(const char *iFileName, const char *iMapName, unsigned int iNbLevels, float iSigma) +{ + // check whether this map was already loaded: + if (!_maps.empty()) { + mapsMap::iterator m = _maps.find(iMapName); + if (m != _maps.end()) { + // lazy check for size changes + ImagePyramid * pyramid = (*m).second; + if ((pyramid->width() != width()) || (pyramid->height() != height())) { + delete pyramid; + } + else { + return; + } + } + } + + string filePath; + if (_MapsPath) { + filePath = _MapsPath; + filePath += iFileName; + } + else { + filePath = iFileName; + } + +#if 0 //soc + QImage *qimg; + QImage newMap(filePath.c_str()); + if (newMap.isNull()) { + cerr << "Could not load image file " << filePath << endl; + return; + } + qimg = &newMap; +#endif + /* OCIO_TODO: support different input color space */ + ImBuf *qimg = IMB_loadiffname(filePath.c_str(), 0, NULL); + if (qimg == 0) { + cerr << "Could not load image file " << filePath << endl; + return; + } + +#if 0 // soc + // resize + QImage scaledImg; + if ((newMap.width() != width()) || (newMap.height() != height())) { + scaledImg = newMap.scaled(width(), height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + qimg = &scaledImg; + } +#endif + ImBuf *scaledImg; + if ((qimg->x != width()) || (qimg->y != height())) { + scaledImg = IMB_dupImBuf(qimg); + IMB_scaleImBuf(scaledImg, width(), height()); + } + + + // deal with color image +#if 0 + if (newMap->depth() != 8) { + int w = newMap->width(); + int h = newMap->height(); + QImage *tmp = new QImage(w, h, 8); + for (unsigned int y = 0; y < h; ++y) { + for (unsigned int x = 0; x < w; ++x) { + int c = qGray(newMap->pixel(x, y)); + tmp->setPixel(x, y, c); + } + } + delete newMap; + newMap = tmp; + } +#endif + + int x, y; + int w = qimg->x; + int h = qimg->y; + int rowbytes = w * 4; + GrayImage tmp(w, h); + char *pix; + + for (y = 0; y < h; ++y) { + for (x = 0; x < w; ++x) { + pix = (char*)qimg->rect + y * rowbytes + x * 4; + float c = (pix[0] * 11 + pix[1] * 16 + pix[2] * 5) / 32; + tmp.setPixel(x, y, c); + } + } + +#if 0 + GrayImage blur(w, h); + GaussianFilter gf(4.0f); + //int bound = gf.getBound(); + for (y = 0; y < h; ++y) { + for (x = 0; x < w; ++x) { + int c = gf.getSmoothedPixel<GrayImage>(&tmp, x, y); + blur.setPixel(x, y, c); + } + } +#endif + + GaussianPyramid *pyramid = new GaussianPyramid(tmp, iNbLevels, iSigma); + int ow = pyramid->width(0); + int oh = pyramid->height(0); + string base(iMapName); //soc + for (int i = 0; i < pyramid->getNumberOfLevels(); ++i) { + // save each image: +#if 0 + w = pyramid.width(i); + h = pyramid.height(i); +#endif + + //soc QImage qtmp(ow, oh, QImage::Format_RGB32); + ImBuf *qtmp = IMB_allocImBuf(ow, oh, 32, IB_rect); + + //int k = (1 << i); + for (y = 0; y < oh; ++y) { + for (x = 0; x < ow; ++x) { + int c = pyramid->pixel(x, y, i); // 255 * pyramid->pixel(x, y, i); + //soc qtmp.setPixel(x, y, qRgb(c, c, c)); + pix = (char*)qtmp->rect + y * rowbytes + x * 4; + pix[0] = pix [1] = pix[2] = c; + } + } + //soc qtmp.save(base + QString::number(i) + ".bmp", "BMP"); + stringstream filename; + filename << base; + filename << i << ".bmp"; + qtmp->ftype = BMP; + IMB_saveiff(qtmp, const_cast<char *>(filename.str().c_str()), 0); + } + +#if 0 + QImage *qtmp = new QImage(w, h, 32); + for (y = 0; y < h; ++y) { + for (x = 0; x < w; ++x) { + int c = (int)blur.pixel(x, y); + qtmp->setPixel(x, y, qRgb(c, c, c)); + } + } + delete newMap; + newMap = qtmp; +#endif + + _maps[iMapName] = pyramid; + //newMap->save("toto.bmp", "BMP"); +} + +float Canvas::readMapPixel(const char *iMapName, int level, int x, int y) +{ + if (_maps.empty()) { + if (G.debug & G_DEBUG_FREESTYLE) { + cout << "readMapPixel warning: no map was loaded "<< endl; + } + return -1; + } + mapsMap::iterator m = _maps.find(iMapName); + if (m == _maps.end()) { + if (G.debug & G_DEBUG_FREESTYLE) { + cout << "readMapPixel warning: no map was loaded with the name " << iMapName << endl; + } + return -1; + } + ImagePyramid *pyramid = (*m).second; + if ((x < 0) || (x >= pyramid->width()) || (y < 0) || (y >= pyramid->height())) + return 0; + + return pyramid->pixel(x, height() - 1 - y, level); +} diff --git a/source/blender/freestyle/intern/stroke/Canvas.h b/source/blender/freestyle/intern/stroke/Canvas.h new file mode 100644 index 00000000000..9931bf3c97e --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Canvas.h @@ -0,0 +1,249 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_CANVAS_H__ +#define __FREESTYLE_CANVAS_H__ + +/** \file blender/freestyle/intern/stroke/Canvas.h + * \ingroup freestyle + * \brief Class to define a canvas designed to draw style modules + * \author Stephane Grabli + * \date 20/10/2002 + */ + +#include <cstring> +#include <deque> +#include <map> +#include <vector> + +#include "StrokeLayer.h" + +#include "../geometry/BBox.h" +#include "../geometry/Geom.h" + +#include "../system/FreestyleConfig.h" + +using namespace Geometry; + +struct ltstr +{ + bool operator()(const char* s1, const char* s2) const + { + return strcmp(s1, s2) < 0; + } +}; + +class InformationMap; +class StrokeRenderer; +class ViewMap; +class ViewEdge; +class FEdge; +class RGBImage; +class GrayImage; +class QImage; +class ImagePyramid; +class SteerableViewMap; +class StyleModule; + +/*! Class to define the canvas on which strokes are drawn. + * It's used to store state information about the drawing. + */ +class LIB_STROKE_EXPORT Canvas +{ +public: + /*! Returns a pointer on the Canvas instance */ + static Canvas *getInstance() + { + return _pInstance; + } + + typedef std::map<const char*, ImagePyramid*, ltstr> mapsMap; + static const int NB_STEERABLE_VIEWMAP = 5; + +protected: + static Canvas *_pInstance; + std::deque<StrokeLayer*> _Layers; + std::deque<StyleModule*> _StyleModules; + FEdge *_SelectedFEdge; + + StrokeRenderer *_Renderer; + StyleModule *_current_sm; + mapsMap _maps; + static const char *_MapsPath; + SteerableViewMap *_steerableViewMap; + bool _basic; + +public: + /* Builds the Canvas */ + Canvas(); + /* Copy constructor */ + Canvas(const Canvas& iBrother); + /* Destructor */ + virtual ~Canvas(); + + /* operations that need to be done before a draw */ + virtual void preDraw(); + + /* Draw the canvas using the current shader */ + virtual void Draw(); + + /* operations that need to be done after a draw */ + virtual void postDraw(); + + /* Renders the created strokes */ + virtual void Render(const StrokeRenderer *iRenderer); + /* Basic Renders the created strokes */ + virtual void RenderBasic(const StrokeRenderer *iRenderer); + /* Renders a stroke */ + virtual void RenderStroke(Stroke *iStroke) = 0; + + /* init the canvas */ + virtual void init() = 0; + + /* Clears the Canvas (shaders stack, layers stack...) */ + void Clear(); + + /* Erases the layers */ + virtual void Erase(); + + /* Reads a pixel area from the canvas */ + virtual void readColorPixels(int x, int y,int w, int h, RGBImage& oImage) const = 0; + /* Reads a depth pixel area from the canvas */ + virtual void readDepthPixels(int x, int y,int w, int h, GrayImage& oImage) const = 0; + + /* update the canvas (display) */ + virtual void update() = 0; + + /* checks whether the canvas is empty or not */ + bool isEmpty() const + { + return (_Layers.empty()); + } + + /* Maps management */ + /*! Loads an image map. The map will be scaled (without preserving the ratio in order to fit the actual + * canvas size.). + * The image must be a gray values image... + * \param iFileName + * The name of the image file + * \param iMapName + * The name that will be used to access this image + * \param iNbLevels + * The number of levels in the map pyramid. (default = 4). + * If iNbLevels == 0, the complete pyramid is built. + */ + void loadMap(const char *iFileName, const char *iMapName, unsigned iNbLevels = 4, float iSigma = 1.0f); + + /*! Reads a pixel value in a map. + * Returns a value between 0 and 1. + * \param iMapName + * The name of the map + * \param level + * The level of the pyramid from which the pixel must be read. + * \param x + * The abscissa of the desired pixel specified in level0 coordinate system. The origin is the lower left corner. + * \param y + * The ordinate of the desired pixel specified in level0 coordinate system. The origin is the lower left corner. + */ + float readMapPixel(const char *iMapName, int level, int x, int y); + + /*! Sets the steerable viewmap */ + void loadSteerableViewMap(SteerableViewMap * iSVM) + { + _steerableViewMap = iSVM; + } + + /*! Returns the steerable VM */ + SteerableViewMap * getSteerableViewMap() + { + return _steerableViewMap; + } + + /*! accessors */ + inline const FEdge *selectedFEdge() const + { + return _SelectedFEdge; + } + + inline FEdge *selectedFEdge() + { + return _SelectedFEdge; + } + + virtual int width() const = 0; + virtual int height() const = 0; + virtual BBox<Vec3r> scene3DBBox() const = 0; + + inline const StrokeRenderer *renderer() const + { + return _Renderer; + } + + inline StyleModule *getCurrentStyleModule() + { + return _current_sm; + } + + virtual bool getRecordFlag() const + { + return false; + } + + int stroke_count; + + /*! modifiers */ + inline void setSelectedFEdge(FEdge *iFEdge) + { + _SelectedFEdge = iFEdge; + } + + /*! inserts a shader at pos index+1 */ + void PushBackStyleModule(StyleModule *iStyleModule); + void InsertStyleModule(unsigned index, StyleModule *iStyleModule); + void RemoveStyleModule(unsigned index); + void SwapStyleModules(unsigned i1, unsigned i2); + void ReplaceStyleModule(unsigned index, StyleModule *iStyleModule); + void setVisible(unsigned index, bool iVisible) ; + +#if 0 + inline void setDensityMap(InformationMap<RGBImage> *iMap) + { + _DensityMap = iMap; + } +#endif + + inline void AddLayer(StrokeLayer *iLayer) + { + _Layers.push_back(iLayer); + } + + void resetModified(bool iMod = false); + void causalStyleModules(std::vector<unsigned>& vec, unsigned index = 0); + void setModified(unsigned index, bool b); +}; + +#endif // __FREESTYLE_CANVAS_H__ diff --git a/source/blender/freestyle/intern/stroke/Chain.cpp b/source/blender/freestyle/intern/stroke/Chain.cpp new file mode 100644 index 00000000000..59cebbd5da0 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Chain.cpp @@ -0,0 +1,156 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/freestyle/intern/stroke/Chain.h + * \ingroup freestyle + * \brief Class to define a chain of viewedges. + * \author Stephane Grabli + * \date 09/01/2003 + */ + +#include "Chain.h" + +#include "../view_map/ViewMapAdvancedIterators.h" +#include "../view_map/ViewMapIterators.h" + +void Chain::push_viewedge_back(ViewEdge *iViewEdge, bool orientation) +{ + ViewEdge::vertex_iterator v; + ViewEdge::vertex_iterator vend; + ViewEdge::vertex_iterator vfirst; + Vec3r previous, current; + if (true == orientation) { + v = iViewEdge->vertices_begin(); + vfirst = v; + vend = iViewEdge->vertices_end(); + } + else { + v = iViewEdge->vertices_last(); + vfirst = v; + vend = iViewEdge->vertices_end(); + } + + if (!_Vertices.empty()) { + previous = _Vertices.back()->point2d(); + if (orientation) + ++v; + else + --v; + // Ensure the continuity of underlying FEdges + CurvePoint *cp = _Vertices.back(); // assumed to be instantiated as new CurvePoint(iSVertex, 0, 0.f); + SVertex *sv_first = (*vfirst); + FEdge *fe = _fedgeB->duplicate(); + fe->setVertexB(sv_first); + fe->vertexA()->shape()->AddEdge(fe); + fe->vertexA()->AddFEdge(fe); + fe->vertexB()->AddFEdge(fe); + cp->setA(sv_first); + } + else { + previous = (*v)->point2d(); + } + do { + current = (*v)->point2d(); + Curve::push_vertex_back(*v); + //_Length += (current - previous).norm(); + previous = current; + if (orientation) + ++v; + else + --v; + } while ((v != vend) && (v != vfirst)); + + if (v == vfirst) { + //Add last one: + current = (*v)->point2d(); + Curve::push_vertex_back(*v); + //_Length += (current - previous).norm(); + } + + _fedgeB = (orientation) ? iViewEdge->fedgeB() : iViewEdge->fedgeA(); +} + +void Chain::push_viewedge_front(ViewEdge *iViewEdge, bool orientation) +{ + orientation = !orientation; + ViewEdge::vertex_iterator v; + ViewEdge::vertex_iterator vend; + ViewEdge::vertex_iterator vfirst; + Vec3r previous, current; + if (true == orientation) { + v = iViewEdge->vertices_begin(); + vfirst = v; + vend = iViewEdge->vertices_end(); + } + else { + v = iViewEdge->vertices_last(); + vfirst = v; + vend = iViewEdge->vertices_end(); + } + + if (!_Vertices.empty()) { + previous = _Vertices.front()->point2d(); + if (orientation) + ++v; + else + --v; + // Ensure the continuity of underlying FEdges + CurvePoint *cp = _Vertices.front(); // assumed to be instantiated as new CurvePoint(iSVertex, 0, 0.f); + SVertex *sv_last = cp->A(); + SVertex *sv_curr = (*v); + FEdge *fe = (orientation) ? iViewEdge->fedgeA() : iViewEdge->fedgeB(); + FEdge *fe2 = fe->duplicate(); + fe2->setVertexA(sv_curr); + fe2->setVertexB(sv_last); + sv_last->AddFEdge(fe2); + sv_curr->AddFEdge(fe2); + sv_curr->shape()->AddEdge(fe2); + } + else { + previous = (*v)->point2d(); + } + do { + current = (*v)->point2d(); + Curve::push_vertex_front((*v)); + //_Length += (current - previous).norm(); + previous = current; + if (orientation) + ++v; + else + --v; + } while ((v != vend) && (v != vfirst)); + + if (v == vfirst) { + //Add last one: + current = (*v)->point2d(); + Curve::push_vertex_front(*v); + //_Length += (current - previous).norm(); + } + + if (!_fedgeB) + _fedgeB = (orientation) ? iViewEdge->fedgeB() : iViewEdge->fedgeA(); +} diff --git a/source/blender/freestyle/intern/stroke/Chain.h b/source/blender/freestyle/intern/stroke/Chain.h new file mode 100644 index 00000000000..425761d28ef --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Chain.h @@ -0,0 +1,116 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_CHAIN_H__ +#define __FREESTYLE_CHAIN_H__ + +/** \file blender/freestyle/intern/stroke/Chain.h + * \ingroup freestyle + * \brief Class to define a chain of viewedges. + * \author Stephane Grabli + * \date 09/01/2003 + */ + +#include "Curve.h" + +#include "../view_map/ViewMap.h" + +/*! Class to represent a 1D elements issued from the chaining process. + * A Chain is the last step before the Stroke and is used in the Splitting and Creation processes. + */ +class Chain : public Curve +{ +protected: + // tmp + Id * _splittingId; + FEdge *_fedgeB; // the last FEdge of the ViewEdge passed to the last call for push_viewedge_back(). + +public: + /*! Defult constructor. */ + Chain() : Curve() + { + _splittingId = 0; + _fedgeB = 0; + } + + /*! Builds a chain from its Id. */ + Chain(const Id& id) : Curve(id) + { + _splittingId = 0; + _fedgeB = 0; + } + + /*! Copy Constructor */ + Chain(const Chain& iBrother) : Curve(iBrother) + { + _splittingId = iBrother._splittingId; + _fedgeB = iBrother._fedgeB; + } + + /*! Destructor. */ + virtual ~Chain() { + // only the last splitted deletes this id + if (_splittingId) { + if (*_splittingId == _Id) + delete _splittingId; + } + } + + /*! Returns the string "Chain" */ + virtual string getExactTypeName() const + { + return "Chain"; + } + + /*! Adds a ViewEdge at the end of the chain + * \param iViewEdge + * The ViewEdge that must be added. + * \param orientation + * The orientation with which this ViewEdge must be processed. + */ + void push_viewedge_back(ViewEdge *iViewEdge, bool orientation); + + /*! Adds a ViewEdge at the beginning of the chain + * \param iViewEdge + * The ViewEdge that must be added. + * \param orientation + * The orientation with which this ViewEdge must be processed. + */ + void push_viewedge_front(ViewEdge *iViewEdge, bool orientation); + + inline void setSplittingId(Id *sid) + { + _splittingId = sid; + } + + inline Id* getSplittingId() + { + return _splittingId; + } +}; + +#endif // __FREESTYLE_CHAIN_H__ diff --git a/source/blender/freestyle/intern/stroke/ChainingIterators.cpp b/source/blender/freestyle/intern/stroke/ChainingIterators.cpp new file mode 100644 index 00000000000..1883e494eab --- /dev/null +++ b/source/blender/freestyle/intern/stroke/ChainingIterators.cpp @@ -0,0 +1,202 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/freestyle/intern/stroke/ChainingIterators.cpp + * \ingroup freestyle + * \brief Chaining iterators + * \author Stephane Grabli + * \date 01/07/2003 + */ + +#include "ChainingIterators.h" + +#include "../system/TimeStamp.h" + +ViewEdge* AdjacencyIterator::operator*() +{ + return (*_internalIterator).first; +} + +bool AdjacencyIterator::isIncoming() const +{ + return (*_internalIterator).second; +} + +int AdjacencyIterator::increment() +{ + ++_internalIterator; + while((!_internalIterator.isEnd()) && (!isValid((*_internalIterator).first))) + ++_internalIterator; + return 0; +} + +bool AdjacencyIterator::isValid(ViewEdge* edge) +{ + if (_restrictToSelection) { + if (edge->getTimeStamp() != TimeStamp::instance()->getTimeStamp()) + return false; + } + if (_restrictToUnvisited) { + if (edge->getChainingTimeStamp() > TimeStamp::instance()->getTimeStamp()) + return false; + } + return true; +} + +int ChainingIterator::increment() +{ + _increment = true; + ViewVertex * vertex = getVertex(); + if (!vertex) { + _edge = 0; + return 0; + } + AdjacencyIterator it = AdjacencyIterator(vertex, _restrictToSelection, _restrictToUnvisited); + if (it.isEnd()) { + _edge = 0; + return 0; + } + if (traverse(it) < 0) + return -1; + _edge = result; + if(_edge == 0) + return 0; + if(_edge->A() == vertex) + _orientation = true; + else + _orientation = false; + return 0; +} + +int ChainingIterator::decrement() +{ + _increment = false; + ViewVertex * vertex = getVertex(); + if (!vertex) { + _edge = 0; + return 0; + } + AdjacencyIterator it = AdjacencyIterator(vertex, _restrictToSelection, _restrictToUnvisited); + if (it.isEnd()) { + _edge = 0; + return 0; + } + if (traverse(it) < 0) + return -1; + _edge = result; + if(_edge == 0) + return 0; + if(_edge->B() == vertex) + _orientation = true; + else + _orientation = false; + return 0; +} + +// +// ChainSilhouetteIterators +// +/////////////////////////////////////////////////////////// + +int ChainSilhouetteIterator::traverse(const AdjacencyIterator& ait) +{ + AdjacencyIterator it(ait); + ViewVertex* nextVertex = getVertex(); + // we can't get a NULL nextVertex here, it was intercepted before + if (nextVertex->getNature() & Nature::T_VERTEX) { + TVertex * tvertex = (TVertex*)nextVertex; + ViewEdge *mate = (tvertex)->mate(getCurrentEdge()); + while (!it.isEnd()) { + ViewEdge *ve = *it; + if (ve == mate) { + result = ve; + return 0; + } + ++it; + } + result = 0; + return 0; + } + if (nextVertex->getNature() & Nature::NON_T_VERTEX) { + //soc NonTVertex * nontvertex = (NonTVertex*)nextVertex; + ViewEdge * newEdge(0); + // we'll try to chain the edges by keeping the same nature... + // the preseance order is : SILHOUETTE, BORDER, CREASE, SUGGESTIVE, VALLEY, RIDGE + Nature::EdgeNature natures[6] = { + Nature::SILHOUETTE, + Nature::BORDER, + Nature::CREASE, + Nature::SUGGESTIVE_CONTOUR, + Nature::VALLEY, + Nature::RIDGE + }; + for (unsigned int i = 0; i < 6; ++i) { + if (getCurrentEdge()->getNature() & natures[i]) { + int n = 0; + while (!it.isEnd()) { + ViewEdge *ve = *it; + if(ve->getNature() & natures[i]) { + ++n; + newEdge = ve; + } + ++it; + } + if (n == 1) { + result = newEdge; + } + else { + result = 0; + } + return 0; + } + } + } + result = 0; + return 0; +} + +int ChainPredicateIterator::traverse(const AdjacencyIterator& ait) +{ + AdjacencyIterator it(ait); + // Iterates over next edges to see if one of them respects the predicate: + while (!it.isEnd()) { + ViewEdge *ve = *it; + if (_unary_predicate->operator()(*ve) < 0) + return -1; + if (_unary_predicate->result) { + if (_binary_predicate->operator()(*(getCurrentEdge()), *(ve)) < 0) + return -1; + if (_binary_predicate->result) { + result = ve; + return 0; + } + } + ++it; + } + result = 0; + return 0; +} diff --git a/source/blender/freestyle/intern/stroke/ChainingIterators.h b/source/blender/freestyle/intern/stroke/ChainingIterators.h new file mode 100644 index 00000000000..66a57061ec9 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/ChainingIterators.h @@ -0,0 +1,407 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_CHAINING_ITERATORS_H__ +#define __FREESTYLE_CHAINING_ITERATORS_H__ + +/** \file blender/freestyle/intern/stroke/ChainingIterators.h + * \ingroup freestyle + * \brief Chaining iterators + * \author Stephane Grabli + * \date 01/07/2003 + */ + +#include <iostream> + +#include "Predicates1D.h" + +#include "../python/Director.h" + +#include "../system/Iterator.h" //soc + +#include "../view_map/ViewMap.h" +#include "../view_map/ViewMapIterators.h" +#include "../view_map/ViewMapAdvancedIterators.h" + +//using namespace ViewEdgeInternal; + +// +// Adjacency iterator used in the chaining process +// +/////////////////////////////////////////////////////////// +class LIB_STROKE_EXPORT AdjacencyIterator : public Iterator +{ +protected: + ViewVertexInternal::orientedViewEdgeIterator _internalIterator; + bool _restrictToSelection; + bool _restrictToUnvisited; + +public: + AdjacencyIterator() + { + _restrictToSelection = true; + _restrictToUnvisited = true; + } + + AdjacencyIterator(ViewVertex *iVertex, bool iRestrictToSelection = true, bool iRestrictToUnvisited = true) + { + _restrictToSelection = iRestrictToSelection; + _restrictToUnvisited = iRestrictToUnvisited; + _internalIterator = iVertex->edgesBegin(); + while ((!_internalIterator.isEnd()) && (!isValid((*_internalIterator).first))) + ++_internalIterator; + } + + AdjacencyIterator(const AdjacencyIterator& iBrother) + { + _internalIterator = iBrother._internalIterator; + _restrictToSelection = iBrother._restrictToSelection; + _restrictToUnvisited = iBrother._restrictToUnvisited; + } + + AdjacencyIterator& operator=(const AdjacencyIterator& iBrother) + { + _internalIterator = iBrother._internalIterator; + _restrictToSelection = iBrother._restrictToSelection; + _restrictToUnvisited = iBrother._restrictToUnvisited; + return *this; + } + + virtual ~AdjacencyIterator() {} + + virtual string getExactTypeName() const + { + return "AdjacencyIterator"; + } + + virtual inline bool isEnd() const + { + return _internalIterator.isEnd(); + } + + virtual inline bool isBegin() const + { + return _internalIterator.isBegin(); + } + + /*! Returns true if the current ViewEdge is is coming towards the iteration vertex. False otherwise. */ + bool isIncoming() const; + + /*! Returns a *pointer* to the pointed ViewEdge. */ + virtual ViewEdge* operator*(); + + virtual ViewEdge* operator->() + { + return operator*(); + } + + virtual AdjacencyIterator& operator++() + { + increment(); + return *this; + } + + virtual AdjacencyIterator operator++(int) + { + AdjacencyIterator tmp(*this); + increment(); + return tmp; + } + + virtual int increment(); + + virtual int decrement() + { + cerr << "Warning: method decrement() not implemented" << endl; + return 0; + } + +protected: + bool isValid(ViewEdge* edge); +}; + +// +// Base class for Chaining Iterators +// +/////////////////////////////////////////////////////////// + +/*! Base class for chaining iterators. + * This class is designed to be overloaded in order to describe chaining rules. + * It makes the works of chaining rules description easier. + * The two main methods that need to overloaded are traverse() and init(). + * traverse() tells which ViewEdge to follow, among the adjacent ones. + * If you specify restriction rules (such as "Chain only ViewEdges of the selection"), they will be included + * in the adjacency iterator. (i.e, the adjacent iterator will only stop on "valid" edges). + */ +class LIB_STROKE_EXPORT ChainingIterator : public ViewEdgeInternal::ViewEdgeIterator +{ +protected: + bool _restrictToSelection; + bool _restrictToUnvisited; + bool _increment; //true if we're currently incrementing, false when decrementing + +public: + ViewEdge *result; + PyObject *py_c_it; + + /*! Builds a Chaining Iterator from the first ViewEdge used for iteration and its orientation. + * \param iRestrictToSelection + * Indicates whether to force the chaining to stay within the set of selected ViewEdges or not. + * \param iRestrictToUnvisited + * Indicates whether a ViewEdge that has already been chained must be ignored ot not. + * \param begin + * The ViewEdge from which to start the chain. + * \param orientation + * The direction to follow to explore the graph. If true, the direction indicated by the first ViewEdge is used. + */ + ChainingIterator(bool iRestrictToSelection = true, bool iRestrictToUnvisited = true, ViewEdge* begin = 0, + bool orientation = true) + : ViewEdgeIterator(begin, orientation) + { + _restrictToSelection = iRestrictToSelection; + _restrictToUnvisited = iRestrictToUnvisited; + _increment = true; + py_c_it = 0; + } + + /*! Copy constructor */ + ChainingIterator(const ChainingIterator& brother) + : ViewEdgeIterator(brother) + { + _restrictToSelection = brother._restrictToSelection; + _restrictToUnvisited = brother._restrictToUnvisited; + _increment = brother._increment; + py_c_it = brother.py_c_it; + } + + /*! Returns the string "ChainingIterator" */ + virtual string getExactTypeName() const + { + return "ChainingIterator"; + } + + /*! Inits the iterator context. + * This method is called each time a new chain is started. + * It can be used to reset some history information that you might want to keep. + */ + virtual int init() + { + return Director_BPy_ChainingIterator_init(this); + } + + /*! This method iterates over the potential next ViewEdges and returns the one that will be followed next. + * returns the next ViewEdge to follow or 0 when the end of the chain is reached. + * \param it + * The iterator over the ViewEdges adjacent to the end vertex of the current ViewEdge. + * The Adjacency iterator reflects the restriction rules by only iterating over the valid ViewEdges. + */ + virtual int traverse(const AdjacencyIterator &it) + { + return Director_BPy_ChainingIterator_traverse(this, const_cast<AdjacencyIterator &>(it)); + } + + /* accessors */ + /*! Returns true if the orientation of the current ViewEdge corresponds to its natural orientation */ + //inline bool getOrientation() const {} + + /*! Returns the vertex which is the next crossing */ + inline ViewVertex * getVertex() + { + if (_increment) { + if (_orientation) { + return _edge->B(); + } + else { + return _edge->A(); + } + } + else { + if (_orientation) { + return _edge->A(); + } + else { + return _edge->B(); + } + } + } + + /*! Returns true if the current iteration is an incrementation */ + inline bool isIncrementing() const + { + return _increment; + } + + /* increments.*/ + virtual int increment(); + virtual int decrement(); +}; + +// +// Chaining iterators definitions +// +/////////////////////////////////////////////////////////// + +/*! A ViewEdge Iterator used to follow ViewEdges the most naturally. + * For example, it will follow visible ViewEdges of same nature. + * As soon, as the nature or the visibility changes, the iteration stops (by setting the pointed ViewEdge to 0). + * In the case of an iteration over a set of ViewEdge that are both Silhouette and Crease, there will be a precedence + * of the silhouette over the crease criterion. + */ +class LIB_STROKE_EXPORT ChainSilhouetteIterator : public ChainingIterator +{ +public: + /*! Builds a ChainSilhouetteIterator from the first ViewEdge used for iteration and its orientation. + * \param iRestrictToSelection + * Indicates whether to force the chaining to stay within the set of selected ViewEdges or not. + * \param begin + * The ViewEdge from where to start the iteration. + * \param orientation + * If true, we'll look for the next ViewEdge among the ViewEdges that surround the ending ViewVertex of begin. + * If false, we'll search over the ViewEdges surrounding the ending ViewVertex of begin. + */ + ChainSilhouetteIterator(bool iRestrictToSelection = true, ViewEdge* begin = NULL, bool orientation = true) + : ChainingIterator(iRestrictToSelection, true, begin, orientation) + { + } + + /*! Copy constructor */ + ChainSilhouetteIterator(const ChainSilhouetteIterator& brother) : ChainingIterator(brother) {} + + /*! Returns the string "ChainSilhouetteIterator" */ + virtual string getExactTypeName() const + { + return "ChainSilhouetteIterator"; + } + + /*! This method iterates over the potential next ViewEdges and returns the one that will be followed next. + * When reaching the end of a chain, 0 is returned. + */ + virtual int traverse(const AdjacencyIterator& it); + + /*! Inits the iterator context */ + virtual int init() + { + return 0; + } +}; + +// +// ChainPredicateIterator +// +/////////////////////////////////////////////////////////// + +/*! A "generic" user-controlled ViewEdge iterator. This iterator is in particular built from a unary predicate and + * a binary predicate. + * First, the unary predicate is evaluated for all potential next ViewEdges in order to only keep the ones respecting + * a certain constraint. + * Then, the binary predicate is evaluated on the current ViewEdge together with each ViewEdge of the previous + * selection. The first ViewEdge respecting both the unary predicate and the binary predicate is kept as the next one. + * If none of the potential next ViewEdge respects these 2 predicates, 0 is returned. + */ +class LIB_STROKE_EXPORT ChainPredicateIterator : public ChainingIterator +{ +protected: + BinaryPredicate1D *_binary_predicate; // the caller is responsible for the deletion of this object + UnaryPredicate1D *_unary_predicate; // the caller is responsible for the deletion of this object + +public: + /*! Builds a ChainPredicateIterator from a starting ViewEdge and its orientation. + * \param iRestrictToSelection + * Indicates whether to force the chaining to stay within the set of selected ViewEdges or not. + * \param iRestrictToUnvisited + * Indicates whether a ViewEdge that has already been chained must be ignored ot not. + * \param begin + * The ViewEdge from where to start the iteration. + * \param orientation + * If true, we'll look for the next ViewEdge among the ViewEdges that surround the ending ViewVertex of begin. + * If false, we'll search over the ViewEdges surrounding the ending ViewVertex of begin. + */ + ChainPredicateIterator(bool iRestrictToSelection = true, bool iRestrictToUnvisited = true, ViewEdge* begin = NULL, + bool orientation = true) + : ChainingIterator(iRestrictToSelection, iRestrictToUnvisited, begin, orientation) + { + _binary_predicate = 0; + _unary_predicate = 0; + } + + /*! Builds a ChainPredicateIterator from a unary predicate, a binary predicate, a starting ViewEdge and + * its orientation. + * \param iRestrictToSelection + * Indicates whether to force the chaining to stay within the set of selected ViewEdges or not. + * \param iRestrictToUnvisited + * Indicates whether a ViewEdge that has already been chained must be ignored ot not. + * \param upred + * The unary predicate that the next ViewEdge must satisfy. + * \param bpred + * The binary predicate that the next ViewEdge must satisfy together with the actual pointed ViewEdge. + * \param begin + * The ViewEdge from where to start the iteration. + * \param orientation + * If true, we'll look for the next ViewEdge among the ViewEdges that surround the ending ViewVertex of begin. + * If false, we'll search over the ViewEdges surrounding the ending ViewVertex of begin. + */ + ChainPredicateIterator(UnaryPredicate1D& upred, BinaryPredicate1D& bpred, bool iRestrictToSelection = true, + bool iRestrictToUnvisited = true, ViewEdge* begin = NULL, bool orientation = true) + : ChainingIterator(iRestrictToSelection, iRestrictToUnvisited, begin, orientation) + { + _unary_predicate = &upred; + _binary_predicate = &bpred; + } + + /*! Copy constructor */ + ChainPredicateIterator(const ChainPredicateIterator& brother) : ChainingIterator(brother) + { + _unary_predicate = brother._unary_predicate; + _binary_predicate = brother._binary_predicate; + } + + /*! Destructor. */ + virtual ~ChainPredicateIterator() + { + _unary_predicate = 0; + _binary_predicate = 0; + } + + /*! Returns the string "ChainPredicateIterator" */ + virtual string getExactTypeName() const + { + return "ChainPredicateIterator"; + } + + /*! This method iterates over the potential next ViewEdges and returns the one that will be followed next. + * When reaching the end of a chain, 0 is returned. + */ + virtual int traverse(const AdjacencyIterator &it); + + /*! Inits the iterator context */ + virtual int init() + { + return 0; + } +}; + +#endif // __FREESTYLE_CHAINING_ITERATORS_H__ diff --git a/source/blender/freestyle/intern/stroke/ContextFunctions.cpp b/source/blender/freestyle/intern/stroke/ContextFunctions.cpp new file mode 100644 index 00000000000..7723c57921d --- /dev/null +++ b/source/blender/freestyle/intern/stroke/ContextFunctions.cpp @@ -0,0 +1,87 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/freestyle/intern/stroke/ContextFunctions.cpp + * \ingroup freestyle + * \brief Functions related to context queries + * \brief Interface to access the context related information. + * \author Stephane Grabli + * \date 20/12/2003 + */ + +#include "ContextFunctions.h" + +#include "../view_map/SteerableViewMap.h" + +#include "../system/TimeStamp.h" + +namespace ContextFunctions { + +unsigned GetTimeStampCF() +{ + return TimeStamp::instance()->getTimeStamp(); +} + +unsigned GetCanvasWidthCF() +{ + return Canvas::getInstance()->width(); +} + +unsigned GetCanvasHeightCF() +{ + return Canvas::getInstance()->height(); +} + +void LoadMapCF(const char *iFileName, const char *iMapName, unsigned iNbLevels, float iSigma) +{ + return Canvas::getInstance()->loadMap(iFileName, iMapName, iNbLevels,iSigma); +} + +float ReadMapPixelCF(const char *iMapName, int level, unsigned x, unsigned y) +{ + Canvas *canvas = Canvas::getInstance(); + return canvas->readMapPixel(iMapName, level, x, y); +} + +float ReadCompleteViewMapPixelCF(int level, unsigned x, unsigned y) +{ + SteerableViewMap *svm = Canvas::getInstance()->getSteerableViewMap(); + return svm->readCompleteViewMapPixel(level, x, y); +} + +float ReadDirectionalViewMapPixelCF(int iOrientation, int level, unsigned x, unsigned y) +{ + SteerableViewMap *svm = Canvas::getInstance()->getSteerableViewMap(); + return svm->readSteerableViewMapPixel(iOrientation, level, x, y); +} + +FEdge * GetSelectedFEdgeCF() +{ + return Canvas::getInstance()->selectedFEdge(); +} + +} // ContextFunctions namespace diff --git a/source/blender/freestyle/intern/stroke/ContextFunctions.h b/source/blender/freestyle/intern/stroke/ContextFunctions.h new file mode 100644 index 00000000000..0718ebb1d6a --- /dev/null +++ b/source/blender/freestyle/intern/stroke/ContextFunctions.h @@ -0,0 +1,120 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_CONTEXT_FUNCTIONS_H__ +#define __FREESTYLE_CONTEXT_FUNCTIONS_H__ + +/** \file blender/freestyle/intern/stroke/ContextFunctions.h + * \ingroup freestyle + * \brief Functions related to context queries + * \brief Interface to access the context related information. + * \author Stephane Grabli + * \date 20/12/2003 + */ + +#include "Canvas.h" + +#include "../image/GaussianFilter.h" +#include "../image/Image.h" + +// +// Context Functions definitions +// +/////////////////////////////////////////////////////////// +/*! namespace containing all the Context related functions */ +namespace ContextFunctions { + +// GetTimeStamp +/*! Returns the system time stamp */ +LIB_STROKE_EXPORT +unsigned GetTimeStampCF(); + +// GetCanvasWidth +/*! Returns the canvas width */ +LIB_STROKE_EXPORT +unsigned GetCanvasWidthCF(); + +// GetCanvasHeight +/*! Returns the canvas width */ +LIB_STROKE_EXPORT +unsigned GetCanvasHeightCF(); + +// Load map +/*! Loads an image map for further reading */ +LIB_STROKE_EXPORT +void LoadMapCF(const char *iFileName, const char *iMapName, unsigned iNbLevels = 4, float iSigma = 1.0f); + +// ReadMapPixel +/*! Reads a pixel in a user-defined map + * \return the floating value stored for that pixel + * \param iMapName + * The name of the map + * \param level + * The level of the pyramid in which we wish to read the pixel + * \param x + * The x-coordinate of the pixel we wish to read. The origin is in the lower-left corner. + * \param y + * The y-coordinate of the pixel we wish to read. The origin is in the lower-left corner. + */ +LIB_STROKE_EXPORT +float ReadMapPixelCF(const char *iMapName, int level, unsigned x, unsigned y); + +// ReadCompleteViewMapPixel +/*! Reads a pixel in the complete view map + * \return the floating value stored for that pixel + * \param level + * The level of the pyramid in which we wish to read the pixel + * \param x + * The x-coordinate of the pixel we wish to read. The origin is in the lower-left corner. + * \param y + * The y-coordinate of the pixel we wish to read. The origin is in the lower-left corner. + */ +LIB_STROKE_EXPORT +float ReadCompleteViewMapPixelCF(int level, unsigned x, unsigned y); + +// ReadOrientedViewMapPixel +/*! Reads a pixel in one of the oriented view map images + * \return the floating value stored for that pixel + * \param iOrientation + * The number telling which orientation we want to check + * \param level + * The level of the pyramid in which we wish to read the pixel + * \param x + * The x-coordinate of the pixel we wish to read. The origin is in the lower-left corner. + * \param y + * The y-coordinate of the pixel we wish to read. The origin is in the lower-left corner. + */ +LIB_STROKE_EXPORT +float ReadDirectionalViewMapPixelCF(int iOrientation, int level, unsigned x, unsigned y); + +// DEBUG +LIB_STROKE_EXPORT +FEdge *GetSelectedFEdgeCF(); + +} // end of namespace ContextFunctions + +#endif // __FREESTYLE_CONTEXT_FUNCTIONS_H__ diff --git a/source/blender/freestyle/intern/stroke/Curve.cpp b/source/blender/freestyle/intern/stroke/Curve.cpp new file mode 100644 index 00000000000..30d651421a4 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Curve.cpp @@ -0,0 +1,931 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/freestyle/intern/stroke/Curve.cpp + * \ingroup freestyle + * \brief Class to define a container for curves + * \author Stephane Grabli + * \date 11/01/2003 + */ + +#include "Curve.h" +#include "CurveAdvancedIterators.h" +#include "CurveIterators.h" + +#include "BKE_global.h" + +/**********************************/ +/* */ +/* */ +/* CurvePoint */ +/* */ +/* */ +/**********************************/ + +CurvePoint::CurvePoint() +{ + __A = 0; + __B = 0; + _t2d = 0; +} + +CurvePoint::CurvePoint(SVertex *iA, SVertex *iB, float t) +{ + __A = iA; + __B = iB; + _t2d = t; + if ((iA == 0) && (t == 1.0f)) { + _Point2d = __B->point2d(); + _Point3d = __B->point3d(); + } + else if ((iB == 0) && (t == 0.0f)) { + _Point2d = __A->point2d(); + _Point3d = __A->point3d(); + } + else { + _Point2d = __A->point2d() + _t2d * (__B->point2d() - __A->point2d()); + _Point3d = __A->point3d() + _t2d * (__B->point3d() - __A->point3d()); + } +} + +CurvePoint::CurvePoint(CurvePoint *iA, CurvePoint *iB, float t3) +{ + __A = 0; + __B = 0; + float t1 = iA->t2d(); + float t2 = iB->t2d(); + if ((iA->A() == iB->A()) && (iA->B() == iB->B()) && + (iA->A() != 0) && (iA->B() != 0) && (iB->A() != 0) && (iB->B() != 0)) + { + __A = iA->A(); + __B = iB->B(); + _t2d = t1 + t2 * t3 - t1 * t3; + } + else if ((iA->B() == 0) && (iB->B() == 0)) { + __A = iA->A(); + __B = iB->A(); + _t2d = t3; + } + else if ((iA->t2d() == 0) && (iB->t2d() == 0)) { + __A = iA->A(); + __B = iB->A(); + _t2d = t3; + } + else if (iA->A() == iB->A()) { +iA_A_eq_iB_A: + if (iA->t2d() == 0) { + __A = iB->A(); + __B = iB->B(); + _t2d = t3; + } + else if (iB->t2d() == 0) { + __A = iA->A(); + __B = iA->B(); + _t2d = t3; + } + } + else if (iA->B() == iB->B()) { +iA_B_eq_iB_B: + if (iA->t2d() == 1) { + __A = iB->A(); + __B = iB->B(); + _t2d = t3; + } + else if (iB->t2d() == 1) { + __A = iA->A(); + __B = iA->B(); + _t2d = t3; + } + } + else if (iA->B() == iB->A()) { +iA_B_eq_iB_A: + if ((iA->t2d() != 1.0f) && (iB->t2d() == 0.0f)) { + __A = iA->A(); + __B = iA->B(); + _t2d = t1 + t3 - t1 * t3; + //_t2d = t3; + } + else if ((iA->t2d() == 1.0f) && (iB->t2d() != 0.0f)) { + __A = iB->A(); + __B = iB->B(); + //_t2d = t3; + _t2d = t2 * t3; + } + else if ((iA->getPoint2D() - iA->getPoint2D()).norm() < 1.0e-6) { + __A = iB->A(); + __B = iB->B(); + //_t2d = t3; + _t2d = t2 * t3; + } + } + else if (iA->A() != 0 && iB->A() != 0 && (iA->A()->point3d() - iB->A()->point3d()).norm() < 1.0e-6) { + goto iA_A_eq_iB_A; + } + else if (iA->B() != 0 && iB->B() != 0 && (iA->B()->point3d() - iB->B()->point3d()).norm() < 1.0e-6) { + goto iA_B_eq_iB_B; + } + else if (iA->B() != 0 && iB->A() != 0 && (iA->B()->point3d() - iB->A()->point3d()).norm() < 1.0e-6) { + goto iA_B_eq_iB_A; + } + + if (!__A || !__B) { + if (G.debug & G_DEBUG_FREESTYLE) { + printf("iA A 0x%p p (%f, %f)\n", iA->A(), iA->A()->getPoint2D().x(), iA->A()->getPoint2D().y()); + printf("iA B 0x%p p (%f, %f)\n", iA->B(), iA->B()->getPoint2D().x(), iA->B()->getPoint2D().y()); + printf("iB A 0x%p p (%f, %f)\n", iB->A(), iB->A()->getPoint2D().x(), iB->A()->getPoint2D().y()); + printf("iB B 0x%p p (%f, %f)\n", iB->B(), iB->B()->getPoint2D().x(), iB->B()->getPoint2D().y()); + printf("iA t2d %f p (%f, %f)\n", iA->t2d(), iA->getPoint2D().x(), iA->getPoint2D().y()); + printf("iB t2d %f p (%f, %f)\n", iB->t2d(), iB->getPoint2D().x(), iB->getPoint2D().y()); + } + cerr << "Fatal error in CurvePoint::CurvePoint(CurvePoint *iA, CurvePoint *iB, float t3)" << endl; + } + assert(__A != 0 && __B != 0); + +#if 0 + _Point2d = __A->point2d() + _t2d * (__B->point2d() - __A->point2d()); + _Point3d = __A->point3d() + _t2d * (__B->point3d() - __A->point3d()); +#endif + + _Point2d = iA->point2d() + t3 * (iB->point2d() - iA->point2d()); + _Point3d = __A->point3d() + _t2d * (__B->point3d() - __A->point3d()); +} + +CurvePoint::CurvePoint(const CurvePoint& iBrother) +{ + __A = iBrother.__A; + __B = iBrother.__B; + _t2d = iBrother._t2d; + _Point2d = iBrother._Point2d; + _Point3d = iBrother._Point3d; +} + +CurvePoint& CurvePoint::operator=(const CurvePoint& iBrother) +{ + __A = iBrother.__A; + __B = iBrother.__B; + _t2d = iBrother._t2d; + _Point2d = iBrother._Point2d; + _Point3d = iBrother._Point3d; + return *this; +} + + +FEdge *CurvePoint::fedge() +{ + if (getNature() & Nature::T_VERTEX) + return 0; + return __A->fedge(); +} + + +FEdge *CurvePoint::getFEdge(Interface0D& inter) +{ + CurvePoint* iVertexB = dynamic_cast<CurvePoint*>(&inter); + if (!iVertexB) { + cerr << "Warning: CurvePoint::getFEdge() failed to cast the given 0D element to CurvePoint." << endl; + return 0; + } + if (((__A == iVertexB->__A) && (__B == iVertexB->__B)) || + ((__A == iVertexB->__B) && (__B == iVertexB->__A))) + { + return __A->getFEdge(*__B); + } + if (__B == 0) { + if (iVertexB->__B == 0) + return __A->getFEdge(*(iVertexB->__A)); + else if (iVertexB->__A == __A) + return __A->getFEdge(*(iVertexB->__B)); + else if (iVertexB->__B == __A) + return __A->getFEdge(*(iVertexB->__A)); + } + if (iVertexB->__B == 0) { + if (iVertexB->__A == __A) + return __B->getFEdge(*(iVertexB->__A)); + else if (iVertexB->__A == __B) + return __A->getFEdge(*(iVertexB->__A)); + } + if (__B == iVertexB->__A) { + if ((_t2d != 1) && (iVertexB->_t2d == 0)) + return __A->getFEdge(*__B); + if ((_t2d == 1) && (iVertexB->_t2d != 0)) + return iVertexB->__A->getFEdge(*(iVertexB->__B)); + } + if (__B == iVertexB->__B) { + if ((_t2d != 1) && (iVertexB->_t2d == 1)) + return __A->getFEdge(*__B); + if ((_t2d == 1) && (iVertexB->_t2d != 1)) + return iVertexB->__A->getFEdge(*(iVertexB->__B)); + } + if (__A == iVertexB->__A) { + if ((_t2d == 0) && (iVertexB->_t2d != 0)) + return iVertexB->__A->getFEdge(*(iVertexB->__B)); + if ((_t2d != 0) && (iVertexB->_t2d == 0)) + return __A->getFEdge(*__B); + } + if (__A == iVertexB->__B) { + if ((_t2d == 0) && (iVertexB->_t2d != 1)) + return iVertexB->__A->getFEdge(*(iVertexB->__B)); + if ((_t2d != 0) && (iVertexB->_t2d == 1)) + return __A->getFEdge(*__B); + } +#if 0 + if (G.debug & G_DEBUG_FREESTYLE) { + printf("__A 0x%p p (%f, %f)\n", __A, __A->getPoint2D().x(), __A->getPoint2D().y()); + printf("__B 0x%p p (%f, %f)\n", __B, __B->getPoint2D().x(), __B->getPoint2D().y()); + printf("iVertexB->A() 0x%p p (%f, %f)\n", iVertexB->A(), iVertexB->A()->getPoint2D().x(), + iVertexB->A()->getPoint2D().y()); + printf("iVertexB->B() 0x%p p (%f, %f)\n", iVertexB->B(), iVertexB->B()->getPoint2D().x(), + iVertexB->B()->getPoint2D().y()); + printf("_t2d %f p (%f, %f)\n", _t2d, getPoint2D().x(), getPoint2D().y()); + printf("iVertexB->t2d() %f p (%f, %f)\n", iVertexB->t2d(), iVertexB->getPoint2D().x(), iVertexB->getPoint2D().y()); + } +#endif + cerr << "Warning: CurvePoint::getFEdge() failed." << endl; + + return NULL; +} + + +Vec3r CurvePoint::normal() const +{ + if (__B == 0) + return __A->normal(); + if (__A == 0) + return __B->normal(); + Vec3r Na = __A->normal(); + if (Exception::getException()) + Na = Vec3r(0, 0, 0); + Vec3r Nb = __B->normal(); + if (Exception::getException()) + Nb = Vec3r(0, 0, 0); + // compute t3d: + real t3d = SilhouetteGeomEngine::ImageToWorldParameter(__A->getFEdge(*__B), _t2d); + return ((1 - t3d) * Na + t3d * Nb); +} + + +#if 0 +Material CurvePoint::material() const +{ + if (__A == 0) + return __B->material(); + return __A->material(); +} + +Id CurvePoint::shape_id() const +{ + if (__A == 0) + return __B->shape_id(); + return __A->shape_id(); +} +#endif + + +const SShape *CurvePoint::shape() const +{ + if (__A == 0) + return __B->shape(); + return __A->shape(); +} + +#if 0 +float CurvePoint::shape_importance() const +{ + if (__A == 0) + return __B->shape_importance(); + return __A->shape_importance(); +} + + +const unsigned CurvePoint::qi() const +{ + if (__A == 0) + return __B->qi(); + if (__B == 0) + return __A->qi(); + return __A->getFEdge(*__B)->qi(); +} +#endif + +occluder_container::const_iterator CurvePoint::occluders_begin() const +{ + if (__A == 0) + return __B->occluders_begin(); + if (__B == 0) + return __A->occluders_begin(); + return __A->getFEdge(*__B)->occluders_begin(); +} + +occluder_container::const_iterator CurvePoint::occluders_end() const +{ + if (__A == 0) + return __B->occluders_end(); + if (__B == 0) + return __A->occluders_end(); + return __A->getFEdge(*__B)->occluders_end(); +} + +bool CurvePoint::occluders_empty() const +{ + if (__A == 0) + return __B->occluders_empty(); + if (__B == 0) + return __A->occluders_empty(); + return __A->getFEdge(*__B)->occluders_empty(); +} + +int CurvePoint::occluders_size() const +{ + if (__A == 0) + return __B->occluders_size(); + if (__B == 0) + return __A->occluders_size(); + return __A->getFEdge(*__B)->occluders_size(); +} + +const SShape *CurvePoint::occluded_shape() const +{ + if (__A == 0) + return __B->occluded_shape(); + if (__B == 0) + return __A->occluded_shape(); + return __A->getFEdge(*__B)->occluded_shape(); +} + +const Polygon3r& CurvePoint::occludee() const +{ + if (__A == 0) + return __B->occludee(); + if (__B == 0) + return __A->occludee(); + return __A->getFEdge(*__B)->occludee(); +} + +const bool CurvePoint::occludee_empty() const +{ + if (__A == 0) + return __B->occludee_empty(); + if (__B == 0) + return __A->occludee_empty(); + return __A->getFEdge(*__B)->occludee_empty(); +} + +real CurvePoint::z_discontinuity() const +{ + if (__A == 0) + return __B->z_discontinuity(); + if (__B == 0) + return __A->z_discontinuity(); + if (__A->getFEdge(*__B) == 0) + return 0.0; + + return __A->getFEdge(*__B)->z_discontinuity(); +} + +#if 0 +float CurvePoint::local_average_depth() const +{ + return local_average_depth_function<CurvePoint >(this); +} + +float CurvePoint::local_depth_variance() const +{ + return local_depth_variance_function<CurvePoint >(this); +} + +real CurvePoint::local_average_density(float sigma) const +{ + //return local_average_density<CurvePoint >(this); + return density_function<CurvePoint >(this); +} + +Vec3r shaded_color() const; + +Vec3r CurvePoint::orientation2d() const +{ + if (__A == 0) + return __B->orientation2d(); + if (__B == 0) + return __A->orientation2d(); + return __B->point2d() - __A->point2d(); +} + +Vec3r CurvePoint::orientation3d() const +{ + if (__A == 0) + return __B->orientation3d(); + if (__B == 0) + return __A->orientation3d(); + return __B->point3d() - __A->point3d(); +} + +real curvature2d() const +{ + return viewedge()->curvature2d((_VertexA->point2d() + _VertexB->point2d()) / 2.0); +} + +Vec3r CurvePoint::curvature2d_as_vector() const +{ +#if 0 + Vec3r edgeA = (_FEdges[0])->orientation2d().normalize(); + Vec3r edgeB = (_FEdges[1])->orientation2d().normalize(); + return edgeA + edgeB; +#endif + if (__A == 0) + return __B->curvature2d_as_vector(); + if (__B == 0) + return __A->curvature2d_as_vector(); + return ((1 - _t2d) * __A->curvature2d_as_vector() + _t2d * __B->curvature2d_as_vector()); +} + +real CurvePoint::curvature2d_as_angle() const +{ +#if 0 + Vec3r edgeA = (_FEdges[0])->orientation2d(); + Vec3r edgeB = (_FEdges[1])->orientation2d(); + Vec2d N1(-edgeA.y(), edgeA.x()); + N1.normalize(); + Vec2d N2(-edgeB.y(), edgeB.x()); + N2.normalize(); + return acos((N1 * N2)); +#endif + if (__A == 0) + return __B->curvature2d_as_angle(); + if (__B == 0) + return __A->curvature2d_as_angle(); + return ((1 - _t2d) * __A->curvature2d_as_angle() + _t2d * __B->curvature2d_as_angle()); +} +#endif + +real CurvePoint::curvatureFredo() const +{ + if (__A == 0) + return __B->curvatureFredo(); + if (__B == 0) + return __A->curvatureFredo(); + return ((1 - _t2d) * __A->curvatureFredo() + _t2d * __B->curvatureFredo()); +} + +Vec2d CurvePoint::directionFredo () const +{ + if (__A == 0) + return __B->directionFredo(); + if (__B == 0) + return __A->directionFredo(); + return ((1 - _t2d) * __A->directionFredo() + _t2d * __B->directionFredo()); +} + +/**********************************/ +/* */ +/* */ +/* Curve */ +/* */ +/* */ +/**********************************/ + +/* for functions */ + +Curve::~Curve() +{ + if (!_Vertices.empty()) { + for (vertex_container::iterator it = _Vertices.begin(), itend = _Vertices.end(); it != itend; ++it) { + delete (*it); + } + _Vertices.clear(); + } +} + +/*! iterators access */ +Curve::point_iterator Curve::points_begin(float step) +{ + vertex_container::iterator second = _Vertices.begin(); + ++second; + return point_iterator(_Vertices.begin(), second, _Vertices.begin(), _Vertices.end(), _nSegments, step, 0.0f, 0.0f); + //return point_iterator(_Vertices.begin(), second, _nSegments, step, 0.0f, 0.0f); +} + +Curve::const_point_iterator Curve::points_begin(float step) const +{ + vertex_container::const_iterator second = _Vertices.begin(); + ++second; + return const_point_iterator(_Vertices.begin(), second, _Vertices.begin(), _Vertices.end(), + _nSegments, step, 0.0f, 0.0f); + //return const_point_iterator(_Vertices.begin(), second, _nSegments, step, 0.0f, 0.0f); +} + +Curve::point_iterator Curve::points_end(float step) +{ + return point_iterator(_Vertices.end(), _Vertices.end(), _Vertices.begin(), _Vertices.end(), + _nSegments, step, 1.0f, _Length); + //return point_iterator(_Vertices.end(), _Vertices.end(), _nSegments, step, 1.0f, _Length); +} + +Curve::const_point_iterator Curve::points_end(float step) const +{ + return const_point_iterator(_Vertices.end(), _Vertices.end(), _Vertices.begin(), _Vertices.end(), + _nSegments, step, 1.0f, _Length); + //return const_point_iterator(_Vertices.end(), _Vertices.end(), _nSegments, step, 1.0f, _Length); +} + +// Adavnced Iterators access +Curve::point_iterator Curve::vertices_begin() +{ + return points_begin(0); +} + +Curve::const_point_iterator Curve::vertices_begin() const +{ + return points_begin(0); +} + +Curve::point_iterator Curve::vertices_end() +{ + return points_end(0); +} + +Curve::const_point_iterator Curve::vertices_end() const +{ + return points_end(0); +} + +// specialized iterators access +CurveInternal::CurvePointIterator Curve::curvePointsBegin(float t) +{ + vertex_container::iterator second = _Vertices.begin(); + ++second; + return CurveInternal::CurvePointIterator(_Vertices.begin(), second, _Vertices.begin(), _Vertices.end(), + 0, _nSegments, _Length, t, 0.0f, 0.0f); +} + +CurveInternal::CurvePointIterator Curve::curvePointsEnd(float t) +{ + vertex_container::iterator last = _Vertices.end(); + --last; + return CurveInternal::CurvePointIterator(last, _Vertices.end(), _Vertices.begin(), _Vertices.end(), + _nSegments, _nSegments, _Length, t, 0.0f, _Length); +} + +CurveInternal::CurvePointIterator Curve::curveVerticesBegin() +{ + return curvePointsBegin(0); +} + +CurveInternal::CurvePointIterator Curve::curveVerticesEnd() +{ + return curvePointsEnd(0); +} + +Interface0DIterator Curve::pointsBegin(float t) +{ + vertex_container::iterator second = _Vertices.begin(); + ++second; + Interface0DIterator ret(new CurveInternal::CurvePointIterator(_Vertices.begin(), second, _Vertices.begin(), + _Vertices.end(), 0, _nSegments, _Length, + t, 0.0f, 0.0f)); + return ret; +} + +Interface0DIterator Curve::pointsEnd(float t) +{ + vertex_container::iterator last = _Vertices.end(); + --last; + Interface0DIterator ret(new CurveInternal::CurvePointIterator(last, _Vertices.end(), _Vertices.begin(), + _Vertices.end(), _nSegments, _nSegments, + _Length, t, 0.0f, _Length)); + return ret; +} + +Interface0DIterator Curve::verticesBegin() +{ + return pointsBegin(0); +} + +Interface0DIterator Curve::verticesEnd() +{ + return pointsEnd(0); +} + +#if 0 +Vec3r shaded_color(int iCombination = 0) const; + +Vec3r Curve::orientation2d(point_iterator it) const +{ + return (*it)->orientation2d(); +} + +template <class BaseVertex> +Vec3r Curve::orientation2d(int iCombination) const +{ + return edge_orientation2d_function<Curve >(this, iCombination); +} + +Vec3r Curve::orientation3d(point_iterator it) const +{ + return (*it)->orientation3d(); +} + +Vec3r Curve::orientation3d(int iCombination) const +{ + return edge_orientation3d_function<Curve >(this, iCombination); +} + +real curvature2d(point_iterator it) const +{ + return (*it)->curvature2d(); +} + +real curvature2d(int iCombination = 0) const; + +Material Curve::material() const +{ + const_vertex_iterator v = vertices_begin(), vend = vertices_end(); + const Material& mat = (*v)->material(); + for (; v != vend; ++v) { + if ((*v)->material() != mat) + Exception::raiseException(); + } + return mat; +} + +int Curve::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_; +} + +occluder_container::const_iterator occluders_begin() const +{ + return _FEdgeA->occluders().begin(); +} + +occluder_container::const_iterator occluders_end() const +{ + return _FEdgeA->occluders().end(); +} + +int Curve::occluders_size() const +{ + return qi(); +} + +bool Curve::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; +} + +const Polygon3r& occludee() const +{ + return *(_FEdgeA->aFace()); +} + +const SShape *Curve::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 Curve::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; +} +real Curve::z_discontinuity(int iCombination) const +{ + return z_discontinuity_edge_function<Curve>(this, iCombination); +} + +int Curve::shape_id() const +{ + const_vertex_iterator v = vertices_begin(), vend = vertices_end(); + Id id = (*v)->shape_id(); + for (; v != vend; ++v) { + if ((*v)->shape_id() != id) + Exception::raiseException(); + } + return id.first; +} + + +const SShape *Curve::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; +} + + +occluder_container::const_iterator Curve::occluders_begin() const +{ + const_vertex_iterator v = vertices_begin(); + return (*v)->occluders_begin(); +} + + +occluder_container::const_iterator Curve::occluders_end() const +{ + const_vertex_iterator v = vertices_end(); + return (*v)->occluders_end(); +} + +Vec3r Curve::curvature2d_as_vector(int iCombination) const +{ + return curvature2d_as_vector_edge_function<Curve>(this, iCombination); +} + +real Curve::curvature2d_as_angle(int iCombination) const +{ + return curvature2d_as_angle_edge_function<Curve>(this, iCombination); +} + +float Curve::shape_importance(int iCombination) const +{ + return shape_importance_edge_function<Curve>(this, iCombination); +} + +float Curve::local_average_depth(int iCombination) const +{ + return local_average_depth_edge_function<Curve>(this, iCombination); +} + +float Curve::local_depth_variance(int iCombination ) const +{ + return local_depth_variance_edge_function<Curve>(this, iCombination); +#if 0 + local_depth_variance_functor<Point> functor; + float result; + Evaluate<float, local_depth_variance_functor<Point> >(&functor, iCombination, result); + return result; +#endif +} + +real Curve::local_average_density(float sigma, int iCombination ) const +{ + return density_edge_function<Curve>(this, iCombination); +#if 0 + density_functor<Point> functor; + real result; + Evaluate<real, density_functor<Point> >(&functor, iCombination, result); + return result; +#endif +} +#endif + +#define EPS_CURVA_DIR 0.01 + +void Curve::computeCurvatureAndOrientation () +{ +#if 0 + const_vertex_iterator v = vertices_begin(), vend = vertices_end(), v2, prevV, v0; + Vec2d p0, p1, p2; + Vec3r p; + + p = (*v)->point2d(); + p0 = Vec2d(p[0], p[1]); + prevV = v; + ++v; + p = (*v)->point2d(); + p1 = Vec2d(p[0], p[1]); + Vec2d prevDir(p1 - p0); + + for (; v! = vend; ++v) { + v2 = v; + ++v2; + if (v2 == vend) + break; + Vec3r p2 = (*v2)->point2d(); + + Vec2d BA = p0 - p1; + Vec2d BC = p2 - p1; + real lba = BA.norm(), lbc = BC.norm(); + BA.normalizeSafe(); + BC.normalizeSafe(); + Vec2d normalCurvature = BA + BC; + Vec2d dir = Vec2d(BC - BA); + Vec2d normal = Vec2d(-dir[1], dir[0]); + + normal.normalizeSafe(); + real curvature = normalCurvature * normal; + if (lba + lbc > MY_EPSILON) + curvature /= (0.5 * lba + lbc); + if (dir.norm() < MY_EPSILON) + dir = 0.1 * prevDir; + (*v)->setCurvatureFredo(curvature); + (*v)->setDirectionFredo(dir); + + prevV = v; + p0 = p1; + p1 = p2; + prevDir = dir; + prevDir.normalize(); + } + (*v)->setCurvatureFredo((*prevV)->curvatureFredo()); + (*v)->setDirectionFredo((*v)->point2d() - (*prevV)->point2d()); + v0 = vertices_begin(); + v2 = v0; + ++v2; + (*v0)->setCurvatureFredo((*v2)->curvatureFredo()); + (*v0)->setDirectionFredo((*v2)->point2d() - (*v0)->point2d()); + + //closed curve case one day... + + // + return; + + //numerical degeneracy verification... we'll see later + const_vertex_iterator vLastReliable = vertices_begin(); + + v = vertices_begin(); + p = (*v)->point2d(); + p0 = Vec2d(p[0], p[1]); + prevV = v; + ++v; + p = (*v)->point2d(); + p1 = Vec2d(p[0], p[1]); + bool isReliable = false; + if ((p1 - p0).norm > EPS_CURVA) { + vLastReliable = v; + isReliable = true; + } + + for (; v != vend; ++v) { + v2 = v; + ++v2; + if (v2 == vend) + break; + Vec3r p2 = (*v2)->point2d(); + + Vec2d BA = p0 - p1; + Vec2d BC = p2 - p1; + real lba = BA.norm(), lbc = BC.norm(); + + if ((lba + lbc) < EPS_CURVA) { + isReliable = false; + cerr << "/"; + } + else { + if (!isReliable) { //previous points were not reliable + const_vertex_iterator vfix = vLastReliable; + ++vfix; + for (; vfix != v; ++vfix) { + (*vfix)->setCurvatureFredo((*v)->curvatureFredo()); + (*vfix)->setDirectionFredo((*v)->directionFredo()); + } + } + isReliable = true; + vLastReliable = v; + } + prevV = v; + p0 = p1; + p1 = p2; + } +#endif +} diff --git a/source/blender/freestyle/intern/stroke/Curve.h b/source/blender/freestyle/intern/stroke/Curve.h new file mode 100644 index 00000000000..f451d83d65b --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Curve.h @@ -0,0 +1,589 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_CURVE_H__ +#define __FREESTYLE_CURVE_H__ + +/** \file blender/freestyle/intern/stroke/Curve.h + * \ingroup freestyle + * \brief Class to define a container for curves + * \author Stephane Grabli + * \date 11/01/2003 + */ + +#include <deque> + +#include "../geometry/Geom.h" + +//#include "../scene_graph/FrsMaterial.h" + +#include "../view_map/Interface0D.h" +#include "../view_map/Interface1D.h" +#include "../view_map/Silhouette.h" +#include "../view_map/SilhouetteGeomEngine.h" + +#include "../system/BaseIterator.h" + +using namespace std; +using namespace Geometry; + +/**********************************/ +/* */ +/* */ +/* CurvePoint */ +/* */ +/* */ +/**********************************/ + +/*! Class to represent a point of a curve. + * A CurvePoint can be any point of a 1D curve (it doesn't have to be a vertex of the curve). + * Any Interface1D is built upon ViewEdges, themselves built upon FEdges. Therefore, a curve is basically + * a polyline made of a list SVertex. + * Thus, a CurvePoint is built by lineraly interpolating two SVertex. + * CurvePoint can be used as virtual points while querying 0D information along a curve at a given resolution. + */ +class LIB_STROKE_EXPORT CurvePoint : public Interface0D +{ +public: // Implementation of Interface0D + /*! Returns the string "CurvePoint"*/ + virtual string getExactTypeName() const + { + return "CurvePoint"; + } + + // Data access methods + /*! Returns the 3D X coordinate of the point */ + virtual real getX() const + { + return _Point3d.x(); + } + + /*! Returns the 3D Y coordinate of the point */ + virtual real getY() const + { + return _Point3d.y(); + } + + /*! Returns the 3D Z coordinate of the point */ + virtual real getZ() const + { + return _Point3d.z(); + } + + /*! Returns the 3D point. */ + virtual Vec3f getPoint3D() const + { + return _Point3d; + } + + /*! Returns the projected 3D X coordinate of the point */ + virtual real getProjectedX() const + { + return _Point2d.x(); + } + + /*! Returns the projected 3D Y coordinate of the point */ + virtual real getProjectedY() const + { + return _Point2d.y(); + } + + /*! Returns the projected 3D Z coordinate of the point */ + virtual real getProjectedZ() const + { + return _Point2d.z(); + } + + /*! Returns the 2D point. */ + virtual Vec2f getPoint2D() const + { + return Vec2f((float)_Point2d.x(), (float)_Point2d.y()); + } + + virtual FEdge* getFEdge(Interface0D& inter); + + /*! Returns the CurvePoint's Id */ + virtual Id getId() const + { + Id id; + if (_t2d == 0) + return __A->getId(); + else if (_t2d == 1) + return __B->getId(); + return id; + } + + /*! Returns the CurvePoint's Nature */ + virtual Nature::VertexNature getNature() const + { + Nature::VertexNature nature = Nature::POINT; + if (_t2d == 0) + nature |= __A->getNature(); + else if (_t2d == 1) + nature |= __B->getNature(); + return nature; + } + + /*! Cast the Interface0D in SVertex if it can be. */ + virtual SVertex * castToSVertex() + { + if (_t2d == 0) + return __A; + else if (_t2d == 1) + return __B; + return Interface0D::castToSVertex(); + } + + /*! Cast the Interface0D in ViewVertex if it can be. */ + virtual ViewVertex * castToViewVertex() + { + if (_t2d == 0) + return __A->castToViewVertex(); + else if (_t2d == 1) + return __B->castToViewVertex(); + return Interface0D::castToViewVertex(); + } + + /*! Cast the Interface0D in NonTVertex if it can be. */ + virtual NonTVertex * castToNonTVertex() + { + if (_t2d == 0) + return __A->castToNonTVertex(); + else if (_t2d == 1) + return __B->castToNonTVertex(); + return Interface0D::castToNonTVertex(); + } + + /*! Cast the Interface0D in TVertex if it can be. */ + virtual TVertex * castToTVertex() + { + if (_t2d == 0) + return __A->castToTVertex(); + else if (_t2d == 1) + return __B->castToTVertex(); + return Interface0D::castToTVertex(); + } + +public: + typedef SVertex vertex_type; + +protected: + SVertex *__A; + SVertex *__B; + float _t2d; + //float _t3d; + Vec3r _Point2d; + Vec3r _Point3d; + +public: + /*! Defult Constructor. */ + CurvePoint(); + + /*! Builds a CurvePoint from two SVertex and an interpolation parameter. + * \param iA + * The first SVertex + * \param iB + * The second SVertex + * \param t2d + * A 2D interpolation parameter used to linearly interpolate \a iA and \a iB + */ + CurvePoint(SVertex *iA, SVertex *iB, float t2d); + + /*! Builds a CurvePoint from two CurvePoint and an interpolation parameter. + * \param iA + * The first CurvePoint + * \param iB + * The second CurvePoint + * \param t2d + * The 2D interpolation parameter used to linearly interpolate \a iA and \a iB. + */ + CurvePoint(CurvePoint *iA, CurvePoint *iB, float t2d); + + //CurvePoint(SVertex *iA, SVertex *iB, float t2d, float t3d); + + /*! Copy Constructor. */ + CurvePoint(const CurvePoint& iBrother); + + /*! Operator = */ + CurvePoint& operator=(const CurvePoint& iBrother); + + /*! Destructor */ + virtual ~CurvePoint() {} + + /*! Operator == */ + bool operator==(const CurvePoint& b) + { + return ((__A == b.__A) && (__B == b.__B) && (_t2d == b._t2d)); + } + + /* accessors */ + /*! Returns the first SVertex upon which the CurvePoint is built. */ + inline SVertex *A() + { + return __A; + } + + /*! Returns the second SVertex upon which the CurvePoint is built. */ + inline SVertex *B() + { + return __B; + } + + /*! Returns the interpolation parameter. */ + inline float t2d() const + { + return _t2d; + } + +#if 0 + inline const float t3d() const + { + return _t3d; + } +#endif + + /* modifiers */ + /*! Sets the first SVertex upon which to build the CurvePoint. */ + inline void setA(SVertex *iA) + { + __A = iA; + } + + /*! Sets the second SVertex upon which to build the CurvePoint. */ + inline void setB(SVertex *iB) + { + __B = iB; + } + + /*! Sets the 2D interpolation parameter to use. */ + inline void setT2d(float t) + { + _t2d = t; + } + +#if 0 + inline void SetT3d(float t) + { + _t3d = t; + } +#endif + + /* Information access interface */ + + FEdge *fedge(); + + inline const Vec3r& point2d() const + { + return _Point2d; + } + + inline const Vec3r& point3d() const + { + return _Point3d; + } + + Vec3r normal() const; + //FrsMaterial material() const; + //Id shape_id() const; + const SShape *shape() const; + //float shape_importance() const; + + //const unsigned qi() const; + occluder_container::const_iterator occluders_begin() const; + occluder_container::const_iterator occluders_end() const; + bool occluders_empty() const; + int occluders_size() const; + const Polygon3r& occludee() const; + const SShape *occluded_shape() const; + const bool occludee_empty() const; + real z_discontinuity() const; +#if 0 + float local_average_depth() const; + float local_depth_variance() const; + real local_average_density(float sigma = 2.3f) const; + Vec3r shaded_color() const; + Vec3r orientation2d() const; + Vec3r orientation3d() const; + + real curvature2d() const + { + return viewedge()->curvature2d((_VertexA->point2d() + _VertexB->point2d()) / 2.0); + } + + Vec3r curvature2d_as_vector() const; + /*! angle in radians */ + real curvature2d_as_angle() const; +#endif + + real curvatureFredo() const; + Vec2d directionFredo() const; +}; + + +/**********************************/ +/* */ +/* */ +/* Curve */ +/* */ +/* */ +/**********************************/ + +namespace CurveInternal { + +class CurvePoint_const_traits; +class CurvePoint_nonconst_traits; +template<class Traits> class __point_iterator; +class CurvePointIterator; + +} // end of namespace CurveInternal + +/*! Base class for curves made of CurvePoints. + * SVertex is the type of the initial curve vertices. + * A Chain is a specialization of a Curve. + */ +class LIB_STROKE_EXPORT Curve : public Interface1D +{ +public: + typedef CurvePoint Vertex; + typedef CurvePoint Point; + typedef Point point_type; + typedef Vertex vertex_type; + typedef deque<Vertex*> vertex_container; + + /* Iterator to iterate over a vertex edges */ + + typedef CurveInternal::__point_iterator<CurveInternal::CurvePoint_nonconst_traits > point_iterator; + typedef CurveInternal::__point_iterator<CurveInternal::CurvePoint_const_traits > const_point_iterator; + typedef point_iterator vertex_iterator ; + typedef const_point_iterator const_vertex_iterator ; + +protected: + vertex_container _Vertices; + double _Length; + Id _Id; + unsigned _nSegments; // number of segments + +public: + /*! Default Constructor. */ + Curve() + { + _Length = 0; + _Id = 0; + _nSegments = 0; + } + + /*! Builds a Curve from its id */ + Curve(const Id& id) + { + _Length = 0; + _Id = id; + _nSegments = 0; + } + + /*! Copy Constructor. */ + Curve(const Curve& iBrother) + { + _Length = iBrother._Length; + _Vertices = iBrother._Vertices; + _Id = iBrother._Id; + _nSegments = 0; + } + + /*! Destructor. */ + virtual ~Curve(); + + /*! Returns the string "Curve" */ + virtual string getExactTypeName() const + { + return "Curve"; + } + + /* fredo's curvature storage */ + void computeCurvatureAndOrientation(); + + /*! Adds a single vertex (CurvePoint) at the end of the Curve */ + inline void push_vertex_back(Vertex *iVertex) + { + if (!_Vertices.empty()) { + Vec3r vec_tmp(iVertex->point2d() - _Vertices.back()->point2d()); + _Length += vec_tmp.norm(); + ++_nSegments; + } + Vertex *new_vertex = new Vertex(*iVertex); + _Vertices.push_back(new_vertex); + } + + /*! Adds a single vertex (SVertex) at the end of the Curve */ + inline void push_vertex_back(SVertex *iVertex) + { + if (!_Vertices.empty()) { + Vec3r vec_tmp(iVertex->point2d() - _Vertices.back()->point2d()); + _Length += vec_tmp.norm(); + ++_nSegments; + } + Vertex *new_vertex = new Vertex(iVertex, 0, 0); + _Vertices.push_back(new_vertex); + } + + /*! Adds a single vertex (CurvePoint) at the front of the Curve */ + inline void push_vertex_front(Vertex *iVertex) + { + if (!_Vertices.empty()) { + Vec3r vec_tmp(iVertex->point2d() - _Vertices.front()->point2d()); + _Length += vec_tmp.norm(); + ++_nSegments; + } + Vertex *new_vertex = new Vertex(*iVertex); + _Vertices.push_front(new_vertex); + } + + /*! Adds a single vertex (SVertex) at the front of the Curve */ + inline void push_vertex_front(SVertex *iVertex) + { + if (!_Vertices.empty()) { + Vec3r vec_tmp(iVertex->point2d() - _Vertices.front()->point2d()); + _Length += vec_tmp.norm(); + ++_nSegments; + } + Vertex *new_vertex = new Vertex(iVertex, 0, 0); + _Vertices.push_front(new_vertex); + } + + /*! Returns true is the Curve doesn't have any Vertex yet. */ + inline bool empty() const + { + return _Vertices.empty(); + } + + /*! Returns the 2D length of the Curve. */ + inline real getLength2D() const + { + return _Length; + } + + /*! Returns the Id of the 1D element. */ + virtual Id getId() const + { + return _Id; + } + + /*! Returns the number of segments in the polyline constituing the Curve. */ + inline unsigned int nSegments() const + { + return _nSegments; + } + + inline void setId(const Id& id) + { + _Id = id; + } + + /* Information access interface */ + +#if 0 + inline Vec3r shaded_color(int iCombination = 0) const; + inline Vec3r orientation2d(point_iterator it) const; + Vec3r orientation2d(int iCombination = 0) const; + Vec3r orientation3d(point_iterator it) const; + Vec3r orientation3d(int iCombination = 0) const; + + real curvature2d(point_iterator it) const + { + return (*it)->curvature2d(); + } + + real curvature2d(int iCombination = 0) const; + FrsMaterial material() const; + int qi() const; + occluder_container::const_iterator occluders_begin() const; + occluder_container::const_iterator occluders_end() const; + int occluders_size() const; + bool occluders_empty() const; + + const Polygon3r& occludee() const + { + return *(_FEdgeA->aFace()); + } + + const SShape *occluded_shape() const; + const bool occludee_empty() const; + real z_discontinuity(int iCombination = 0) const; + int shape_id() const; + const SShape *shape() const; + float shape_importance(int iCombination = 0) const; + float local_average_depth(int iCombination = 0) const; + float local_depth_variance(int iCombination = 0) const; + real local_average_density(float sigma = 2.3f, int iCombination = 0) const; + Vec3r curvature2d_as_vector(int iCombination = 0) const; + /*! angle in radians */ + real curvature2d_as_angle(int iCombination = 0) const; +#endif + + /* advanced iterators access */ + point_iterator points_begin(float step = 0); + const_point_iterator points_begin(float step = 0) const; + point_iterator points_end(float step = 0); + const_point_iterator points_end(float step = 0) const; + + /* methods given for convenience */ + point_iterator vertices_begin(); + const_point_iterator vertices_begin() const; + point_iterator vertices_end(); + const_point_iterator vertices_end() const; + + // specialized iterators access + CurveInternal::CurvePointIterator curvePointsBegin(float t = 0.0f); + CurveInternal::CurvePointIterator curvePointsEnd(float t = 0.0f); + + CurveInternal::CurvePointIterator curveVerticesBegin(); + CurveInternal::CurvePointIterator curveVerticesEnd(); + + // Iterators access + /*! Returns an Interface0DIterator pointing onto the first vertex of the Curve and that can iterate + * over the \a vertices of the Curve. + */ + virtual Interface0DIterator verticesBegin(); + + /*! Returns an Interface0DIterator pointing after the last vertex of the Curve and that can iterate + * over the \a vertices of the Curve. + */ + virtual Interface0DIterator verticesEnd(); + + /*! Returns an Interface0DIterator pointing onto the first point of the Curve and that can iterate + * over the \a points of the Curve at any resolution. + * At each iteration a virtual temporary CurvePoint is created. + */ + virtual Interface0DIterator pointsBegin(float t = 0.0f); + + /*! Returns an Interface0DIterator pointing after the last point of the Curve and that can iterate + * over the \a points of the Curve at any resolution. + * At each iteration a virtual temporary CurvePoint is created. + */ + virtual Interface0DIterator pointsEnd(float t = 0.0f); +}; + +#endif // __FREESTYLE_CURVE_H__ diff --git a/source/blender/freestyle/intern/stroke/CurveAdvancedIterators.h b/source/blender/freestyle/intern/stroke/CurveAdvancedIterators.h new file mode 100644 index 00000000000..0c053bf1845 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/CurveAdvancedIterators.h @@ -0,0 +1,383 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_CURVE_ADVANCED_ITERATORS_H__ +#define __FREESTYLE_CURVE_ADVANCED_ITERATORS_H__ + +/** \file blender/freestyle/intern/stroke/CurveAdvancedIterators.h + * \ingroup freestyle + * \brief Iterators used to iterate over the elements of the Curve. Can't be used in python + * \author Stephane Grabli + * \date 01/08/2003 + */ + +#include "Stroke.h" + +namespace CurveInternal { + +class CurvePoint_const_traits : public Const_traits<CurvePoint*> +{ +public: + typedef deque<CurvePoint*> vertex_container; + typedef vertex_container::const_iterator vertex_container_iterator; + typedef SVertex vertex_type; +}; + +class CurvePoint_nonconst_traits : public Nonconst_traits<CurvePoint*> +{ +public: + typedef deque<CurvePoint*> vertex_container; + typedef vertex_container::iterator vertex_container_iterator; + typedef SVertex vertex_type; +}; + +/**********************************/ +/* */ +/* */ +/* CurvePoint Iterator */ +/* */ +/* */ +/**********************************/ + + +/*! iterator on a curve. Allows an iterating outside initial vertices. A CurvePoint is instanciated an returned + * when the iterator is dereferenced. + */ +template<class Traits> +class __point_iterator : public IteratorBase<Traits, BidirectionalIteratorTag_Traits> +{ +public: + typedef __point_iterator <Traits> Self; + typedef typename Traits::vertex_container_iterator vertex_container_iterator; + typedef typename Traits::vertex_type vertex_type; + typedef CurvePoint Point; + typedef Point point_type; + + typedef __point_iterator<CurvePoint_nonconst_traits> iterator; + typedef __point_iterator<CurvePoint_const_traits> const_iterator; + +#if 0 + typedef Vertex vertex_type ; + typedef vertex_container_iterator vertex_iterator_type; + typedef CurvePoint<Vertex> Point; + typedef Point point_type; +#endif + typedef IteratorBase<Traits,BidirectionalIteratorTag_Traits> parent_class; +#if 0 +#if defined(__GNUC__) && (__GNUC__ < 3) + typedef bidirectional_iterator<CurvePoint<Vertex>, ptrdiff_t> bidirectional_point_iterator; +#else + typedef iterator<bidirectional_iterator_tag, CurvePoint<Vertex>, ptrdiff_t> bidirectional_point_iterator; +#endif +#endif + friend class Curve; +#if 0 + friend class Curve::vertex_iterator; + friend class __point_iterator<CurvePoint_nonconst_traits>; + friend class iterator; +#endif +//protected: +public: + float _CurvilinearLength; + float _step; + vertex_container_iterator __A; + vertex_container_iterator __B; + vertex_container_iterator _begin; + vertex_container_iterator _end; + int _n; + int _currentn; + float _t; + mutable Point *_Point; + +public: + inline __point_iterator(float step = 0.0f) : parent_class() + { + _step = step; + _CurvilinearLength = 0.0f; + _t = 0.0f; + _Point = 0; + _n = 0; + _currentn = 0; + } + + inline __point_iterator(const iterator& iBrother) : parent_class() + { + __A = iBrother.__A; + __B = iBrother.__B; + _begin = iBrother._begin; + _end = iBrother._end; + _CurvilinearLength = iBrother._CurvilinearLength; + _step = iBrother._step; + _t = iBrother._t; + if (iBrother._Point == 0) + _Point = 0; + else + _Point = new Point(*(iBrother._Point)); + _n = iBrother._n; + _currentn = iBrother._currentn; + } + + inline __point_iterator(const const_iterator& iBrother) : parent_class() + { + __A = iBrother.__A; + __B = iBrother.__B; + _begin = iBrother._begin; + _end = iBrother._end; + _CurvilinearLength = iBrother._CurvilinearLength; + _step = iBrother._step; + _t = iBrother._t; + if (iBrother._Point == 0) + _Point = 0; + else + _Point = new Point(*(iBrother._Point)); + _n = iBrother._n; + _currentn = iBrother._currentn; + } + + inline Self& operator=(const Self& iBrother) + { + //((bidirectional_point_iterator*)this)->operator=(iBrother); + __A = iBrother.__A; + __B = iBrother.__B; + _begin = iBrother._begin; + _end = iBrother._end; + _CurvilinearLength = iBrother._CurvilinearLength; + _step = iBrother._step; + _t = iBrother._t; + if (iBrother._Point == 0) + _Point = 0; + else + _Point = new Point(*(iBrother._Point)); + _n = iBrother._n; + _currentn = iBrother._currentn; + return *this; + } + + virtual ~__point_iterator() + { + if (_Point != 0) + delete _Point; + } + +//protected: //FIXME +public: + inline __point_iterator(vertex_container_iterator iA, vertex_container_iterator iB, + vertex_container_iterator ibegin, vertex_container_iterator iend, + int currentn, int n, float step, float t = 0.0f, float iCurvilinearLength = 0.0f) + : parent_class() + { + __A = iA; + __B = iB; + _begin = ibegin; + _end = iend; + _CurvilinearLength = iCurvilinearLength; + _step = step; + _t = t; + _Point = 0; + _n = n; + _currentn = currentn; + } + +public: + // operators + inline Self& operator++() // operator corresponding to ++i + { + increment(); + return *this; + } + + /* Operator corresponding to i++, i.e. it returns the value *and then* increments. + * That’s why we store the value in a temp. + */ + inline Self operator++(int) + { + Self tmp = *this; + increment(); + return tmp; + } + + inline Self& operator--() // operator corresponding to --i + { + decrement(); + return *this; + } + + inline Self operator--(int) // operator corresponding to i-- + { + Self tmp = *this; + decrement(); + return tmp; + } + + // comparibility + virtual bool operator!=(const Self& b) const + { + return ((__A != b.__A) || (__B != b.__B) || (_t != b._t)); + } + + virtual bool operator==(const Self& b) const + { + return !(*this != b); + } + + // dereferencing + virtual typename Traits::reference operator*() const + { + if (_Point != 0) { + delete _Point; + _Point = 0; + } + if ((_currentn < 0) || (_currentn >= _n)) + return _Point; // 0 in this case + return (_Point = new Point(*__A, *__B, _t)); + } + + virtual typename Traits::pointer operator->() const + { + return &(operator*()); + } + + virtual bool begin() const + { + if ((__A == _begin) && (_t < (float)M_EPSILON)) + return true; + return false; + } + + virtual bool end() const + { + if ((__B == _end)) + return true; + return false; + } + +protected: + virtual void increment() + { + if (_Point != 0) { + delete _Point; + _Point = 0; + } + if ((_currentn == _n - 1) && (_t == 1.0f)) { + // we're setting the iterator to end + ++__A; + ++__B; + ++_currentn; + _t = 0.0f; + return; + } + + if (0 == _step) { // means we iterate over initial vertices + Vec3r vec_tmp((*__B)->point2d() - (*__A)->point2d()); + _CurvilinearLength += vec_tmp.norm(); + if (_currentn == _n-1) { + _t = 1.0f; + return; + } + ++__B; + ++__A; + ++_currentn; + return; + } + + // compute the new position: + Vec3r vec_tmp2((*__A)->point2d() - (*__B)->point2d()); + float normAB = vec_tmp2.norm(); + + if (normAB > M_EPSILON) { + _CurvilinearLength += _step; + _t = _t + _step / normAB; + } + else { + _t = 1.0f; // AB is a null segment, we're directly at its end + } + //if normAB ~= 0, we don't change these values + if (_t >= 1) { + _CurvilinearLength -= normAB * (_t - 1); + if (_currentn == _n - 1) { + _t = 1.0f; + } + else { + _t = 0.0f; + ++_currentn; + ++__A; + ++__B; + } + } + } + + virtual void decrement() + { + if (_Point != 0) { + delete _Point; + _Point = 0; + } + + if (_t == 0.0f) { // we're at the beginning of the edge + _t = 1.0f; + --_currentn; + --__A; + --__B; + if (_currentn == _n - 1) + return; + } + + if (0 == _step) { // means we iterate over initial vertices + Vec3r vec_tmp((*__B)->point2d() - (*__A)->point2d()); + _CurvilinearLength -= vec_tmp.norm(); + _t = 0; + return; + } + + // compute the new position: + Vec3r vec_tmp2((*__A)->point2d() - (*__B)->point2d()); + float normAB = vec_tmp2.norm(); + + if (normAB >M_EPSILON) { + _CurvilinearLength -= _step; + _t = _t - _step / normAB; + } + else { + _t = -1.0f; // We just need a negative value here + } + + // round value + if (fabs(_t) < (float)M_EPSILON) + _t = 0.0f; + if (_t < 0) { + if (_currentn == 0) + _CurvilinearLength = 0.0f; + else + _CurvilinearLength += normAB * (-_t); + _t = 0.0f; + } + } +}; + +} // end of namespace CurveInternal + +#endif // __FREESTYLE_CURVE_ADVANCED_ITERATORS_H__ diff --git a/source/blender/freestyle/intern/stroke/CurveIterators.h b/source/blender/freestyle/intern/stroke/CurveIterators.h new file mode 100644 index 00000000000..7d9e0e00ef1 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/CurveIterators.h @@ -0,0 +1,302 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_CURVE_ITERATORS_H__ +#define __FREESTYLE_CURVE_ITERATORS_H__ + +/** \file blender/freestyle/intern/stroke/CurveIterators.h + * \ingroup freestyle + * \brief Iterators used to iterate over the elements of the Curve + * \author Stephane Grabli + * \date 01/08/2003 + */ + +#include "Curve.h" +#include "Stroke.h" + +namespace CurveInternal { + +/*! iterator on a curve. Allows an iterating outside +* initial vertices. A CurvePoint is instanciated an returned +* when the iterator is dereferenced. +*/ + +class CurvePointIterator : public Interface0DIteratorNested +{ +public: + friend class ::Curve; + +public: + float _CurvilinearLength; + float _step; + ::Curve::vertex_container::iterator __A; + ::Curve::vertex_container::iterator __B; + ::Curve::vertex_container::iterator _begin; + ::Curve::vertex_container::iterator _end; + int _n; + int _currentn; + float _t; + mutable CurvePoint _Point; + float _CurveLength; + +public: + inline CurvePointIterator(float step = 0.0f) : Interface0DIteratorNested() + { + _step = step; + _CurvilinearLength = 0.0f; + _t = 0.0f; + //_Point = 0; + _n = 0; + _currentn = 0; + _CurveLength = 0; + } + + inline CurvePointIterator(const CurvePointIterator& iBrother) : Interface0DIteratorNested() + { + __A = iBrother.__A; + __B = iBrother.__B; + _begin = iBrother._begin; + _end = iBrother._end; + _CurvilinearLength = iBrother._CurvilinearLength; + _step = iBrother._step; + _t = iBrother._t; + _Point = iBrother._Point; + _n = iBrother._n; + _currentn = iBrother._currentn; + _CurveLength = iBrother._CurveLength; + } + + inline CurvePointIterator& operator=(const CurvePointIterator& iBrother) + { + __A = iBrother.__A; + __B = iBrother.__B; + _begin = iBrother._begin; + _end = iBrother._end; + _CurvilinearLength = iBrother._CurvilinearLength; + _step = iBrother._step; + _t = iBrother._t; + _Point = iBrother._Point; + _n = iBrother._n; + _currentn = iBrother._currentn; + _CurveLength = iBrother._CurveLength; + return *this; + } + + virtual ~CurvePointIterator() {} + +protected: + inline CurvePointIterator(::Curve::vertex_container::iterator iA, ::Curve::vertex_container::iterator iB, + ::Curve::vertex_container::iterator ibegin, ::Curve::vertex_container::iterator iend, + int currentn, int n, float iCurveLength, float step, float t = 0.0f, + float iCurvilinearLength = 0.0f) + : Interface0DIteratorNested() + { + __A = iA; + __B = iB; + _begin = ibegin; + _end = iend; + _CurvilinearLength = iCurvilinearLength; + _step = step; + _t = t; + _n = n; + _currentn = currentn; + _CurveLength = iCurveLength; + } + +public: + virtual CurvePointIterator* copy() const + { + return new CurvePointIterator(*this); + } + + inline Interface0DIterator castToInterface0DIterator() const + { + Interface0DIterator ret(new CurveInternal::CurvePointIterator(*this)); + return ret; + } + + virtual string getExactTypeName() const + { + return "CurvePointIterator"; + } + + // operators + inline CurvePointIterator& operator++() // operator corresponding to ++i + { + increment(); + return *this; + } + + inline CurvePointIterator& operator--() // operator corresponding to --i + { + decrement(); + return *this; + } + + // comparibility + virtual bool operator==(const Interface0DIteratorNested& b) const + { + const CurvePointIterator* it_exact = dynamic_cast<const CurvePointIterator*>(&b); + if (!it_exact) + return false; + return ((__A == it_exact->__A) && (__B == it_exact->__B) && (_t == it_exact->_t)); + } + + // dereferencing + virtual CurvePoint& operator*() + { + return (_Point = CurvePoint(*__A, *__B, _t)); + } + + virtual CurvePoint* operator->() + { + return &(operator*()); + } + + virtual bool isBegin() const + { + if ((__A == _begin) && (_t < (float)M_EPSILON)) + return true; + return false; + } + + virtual bool isEnd() const + { + if (__B == _end) + return true; + return false; + } + +//protected: + virtual int increment() + { + if ((_currentn == _n - 1) && (_t == 1.0f)) { + // we're setting the iterator to end + ++__A; + ++__B; + ++_currentn; + _t = 0.0f; + return 0; + } + + if (0 == _step) { // means we iterate over initial vertices + Vec3r vec_tmp((*__B)->point2d() - (*__A)->point2d()); + _CurvilinearLength += (float)vec_tmp.norm(); + if (_currentn == _n - 1) { + _t = 1.0f; + return 0; + } + ++__B; + ++__A; + ++_currentn; + return 0; + } + + // compute the new position: + Vec3r vec_tmp2((*__A)->point2d() - (*__B)->point2d()); + float normAB = (float)vec_tmp2.norm(); + + if (normAB > M_EPSILON) { + _CurvilinearLength += _step; + _t = _t + _step / normAB; + } + else { + _t = 1.0f; // AB is a null segment, we're directly at its end + } + //if normAB ~= 0, we don't change these values + if (_t >= 1) { + _CurvilinearLength -= normAB * (_t - 1); + if (_currentn == _n - 1) { + _t = 1.0f; + } + else { + _t = 0.0f; + ++_currentn; + ++__A; + ++__B; + } + } + return 0; + } + + virtual int decrement() + { + if (_t == 0.0f) { //we're at the beginning of the edge + _t = 1.0f; + --_currentn; + --__A; + --__B; + if (_currentn == _n - 1) + return 0; + } + + if (0 == _step) { // means we iterate over initial vertices + Vec3r vec_tmp((*__B)->point2d() - (*__A)->point2d()); + _CurvilinearLength -= (float)vec_tmp.norm(); + _t = 0; + return 0; + } + + // compute the new position: + Vec3r vec_tmp2((*__A)->point2d() - (*__B)->point2d()); + float normAB = (float)vec_tmp2.norm(); + + if (normAB > M_EPSILON) { + _CurvilinearLength -= _step; + _t = _t - _step / normAB; + } + else { + _t = -1.0f; // We just need a negative value here + } + + // round value + if (fabs(_t) < (float)M_EPSILON) + _t = 0.0f; + if (_t < 0) { + if (_currentn == 0) + _CurvilinearLength = 0.0f; + else + _CurvilinearLength += normAB * (-_t); + _t = 0.0f; + } + return 0; + } + + virtual float t() const + { + return _CurvilinearLength; + } + + virtual float u() const + { + return _CurvilinearLength / _CurveLength; + } +}; + +} // end of namespace CurveInternal + +#endif // __FREESTYLE_CURVE_ITERATORS_H__ diff --git a/source/blender/freestyle/intern/stroke/Modifiers.h b/source/blender/freestyle/intern/stroke/Modifiers.h new file mode 100644 index 00000000000..daa02002b7b --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Modifiers.h @@ -0,0 +1,74 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_MODIFIERS_H__ +#define __FREESTYLE_MODIFIERS_H__ + +/** \file blender/freestyle/intern/stroke/Modifiers.h + * \ingroup freestyle + * \brief modifiers... + * \author Stephane Grabli + * \date 05/01/2003 + */ + +#include "TimeStamp.h" + +/* ----------------------------------------- * + * * + * modifiers * + * * + * ----------------------------------------- */ + +/*! Base class for modifiers. + * Modifiers are used in the Operators in order to "mark" the processed Interface1D. + */ +template<class Edge> +struct EdgeModifier : public unary_function<Edge, void> +{ + /*! Default construction */ + EdgeModifier() : unary_function<Edge, void>() {} + + /*! the () operator */ + virtual void operator()(Edge& iEdge) {} +}; + +/*! Modifier that sets the time stamp of an Interface1D to the time stamp of the system. */ +template<class Edge> +struct TimestampModifier : public EdgeModifier<Edge> +{ + /*! Default constructor */ + TimestampModifier() : EdgeModifier<Edge>() {} + + /*! The () operator. */ + virtual void operator()(Edge& iEdge) + { + TimeStamp *timestamp = TimeStamp::instance(); + iEdge.setTimeStamp(timestamp->getTimeStamp()); + } +}; + +#endif // MODIFIERS_H diff --git a/source/blender/freestyle/intern/stroke/Module.h b/source/blender/freestyle/intern/stroke/Module.h new file mode 100644 index 00000000000..da6708e6ea9 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Module.h @@ -0,0 +1,82 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_MODULE_H__ +#define __FREESTYLE_MODULE_H__ + +/** \file blender/freestyle/intern/stroke/Module.h + * \ingroup freestyle + * \brief Set the type of the module + * \author Emmanuel Turquin + * \date 01/07/2003 + */ + +#include "Canvas.h" +#include "StyleModule.h" + +class Module +{ +public: + static void setAlwaysRefresh(bool b = true) + { + getCurrentStyleModule()->setAlwaysRefresh(b); + } + + static void setCausal(bool b = true) + { + getCurrentStyleModule()->setCausal(b); + } + + static void setDrawable(bool b = true) + { + getCurrentStyleModule()->setDrawable(b); + } + + static bool getAlwaysRefresh() + { + return getCurrentStyleModule()->getAlwaysRefresh(); + } + + static bool getCausal() + { + return getCurrentStyleModule()->getCausal(); + } + + static bool getDrawable() + { + return getCurrentStyleModule()->getDrawable(); + } + +private: + static StyleModule* getCurrentStyleModule() + { + Canvas *canvas = Canvas::getInstance(); + return canvas->getCurrentStyleModule(); + } +}; + +#endif // __FREESTYLE_MODULE_H__ diff --git a/source/blender/freestyle/intern/stroke/Operators.cpp b/source/blender/freestyle/intern/stroke/Operators.cpp new file mode 100644 index 00000000000..e35a811fcab --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Operators.cpp @@ -0,0 +1,1286 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/freestyle/intern/stroke/Operators.cpp + * \ingroup freestyle + * \brief Class gathering stroke creation algorithms + * \author Stephane Grabli + * \author Emmanuel Turquin + * \date 01/07/2003 + */ + +#include <algorithm> +#include <stdexcept> + +#include "Operators.h" +#include "Canvas.h" +#include "Stroke.h" + +#include "BKE_global.h" + +LIB_STROKE_EXPORT Operators::I1DContainer Operators::_current_view_edges_set; +LIB_STROKE_EXPORT Operators::I1DContainer Operators::_current_chains_set; +LIB_STROKE_EXPORT Operators::I1DContainer *Operators::_current_set = NULL; +LIB_STROKE_EXPORT Operators::StrokesContainer Operators::_current_strokes_set; + +int Operators::select(UnaryPredicate1D& pred) +{ + if (!_current_set) + return 0; + if (_current_set->empty()) + return 0; + I1DContainer new_set; + I1DContainer rejected; + Functions1D::ChainingTimeStampF1D cts; + Functions1D::TimeStampF1D ts; + I1DContainer::iterator it = _current_set->begin(); + I1DContainer::iterator itbegin = it; + while (it != _current_set->end()) { + Interface1D *i1d = *it; + cts(*i1d); // mark everyone's chaining time stamp anyway + if (pred(*i1d) < 0) { + new_set.clear(); + rejected.clear(); + return -1; + } + if (pred.result) { + new_set.push_back(i1d); + ts(*i1d); + } + else { + rejected.push_back(i1d); + } + ++it; + } + if ((*itbegin)->getExactTypeName() != "ViewEdge") { + for (it = rejected.begin(); it != rejected.end(); ++it) + delete *it; + } + rejected.clear(); + _current_set->clear(); + *_current_set = new_set; + return 0; +} + + +int Operators::chain(ViewEdgeInternal::ViewEdgeIterator& it, UnaryPredicate1D& pred, UnaryFunction1D_void& modifier) +{ + if (_current_view_edges_set.empty()) + return 0; + + unsigned id = 0; + ViewEdge *edge; + I1DContainer new_chains_set; + + for (I1DContainer::iterator it_edge = _current_view_edges_set.begin(); + it_edge != _current_view_edges_set.end(); + ++it_edge) + { + if (pred(**it_edge) < 0) + goto error; + if (pred.result) + continue; + + edge = dynamic_cast<ViewEdge*>(*it_edge); + it.setBegin(edge); + it.setCurrentEdge(edge); + + Chain *new_chain = new Chain(id); + ++id; + while (TRUE) { + new_chain->push_viewedge_back(*it, it.getOrientation()); + if (modifier(**it) < 0) { + delete new_chain; + goto error; + } + ++it; + if (it.isEnd()) + break; + if (pred(**it) < 0) { + delete new_chain; + goto error; + } + if (pred.result) + break; + } + new_chains_set.push_back(new_chain); + } + + if (!new_chains_set.empty()) { + for (I1DContainer::iterator it = new_chains_set.begin(); it != new_chains_set.end(); ++it) { + _current_chains_set.push_back(*it); + } + new_chains_set.clear(); + _current_set = &_current_chains_set; + } + return 0; + +error: + for (I1DContainer::iterator it = new_chains_set.begin(); it != new_chains_set.end(); ++it) { + delete (*it); + } + new_chains_set.clear(); + return -1; +} + + +int Operators::chain(ViewEdgeInternal::ViewEdgeIterator& it, UnaryPredicate1D& pred) +{ + if (_current_view_edges_set.empty()) + return 0; + + unsigned id = 0; + Functions1D::IncrementChainingTimeStampF1D ts; + Predicates1D::EqualToChainingTimeStampUP1D pred_ts(TimeStamp::instance()->getTimeStamp() + 1); + ViewEdge *edge; + I1DContainer new_chains_set; + + for (I1DContainer::iterator it_edge = _current_view_edges_set.begin(); + it_edge != _current_view_edges_set.end(); + ++it_edge) + { + if (pred(**it_edge) < 0) + goto error; + if (pred.result) + continue; + if (pred_ts(**it_edge) < 0) + goto error; + if (pred_ts.result) + continue; + + edge = dynamic_cast<ViewEdge*>(*it_edge); + it.setBegin(edge); + it.setCurrentEdge(edge); + + Chain *new_chain = new Chain(id); + ++id; + while (TRUE) { + new_chain->push_viewedge_back(*it, it.getOrientation()); + ts(**it); + ++it; + if (it.isEnd()) + break; + if (pred(**it) < 0) { + delete new_chain; + goto error; + } + if (pred.result) + break; + if (pred_ts(**it) < 0) { + delete new_chain; + goto error; + } + if (pred_ts.result) + break; + } + new_chains_set.push_back(new_chain); + } + + if (!new_chains_set.empty()) { + for (I1DContainer::iterator it = new_chains_set.begin(); it != new_chains_set.end(); ++it) { + _current_chains_set.push_back(*it); + } + new_chains_set.clear(); + _current_set = &_current_chains_set; + } + return 0; + +error: + for (I1DContainer::iterator it = new_chains_set.begin(); it != new_chains_set.end(); ++it) { + delete (*it); + } + new_chains_set.clear(); + return -1; +} + + +#if 0 +void Operators::bidirectionalChain(ViewEdgeIterator& it, UnaryPredicate1D& pred, UnaryFunction1D_void& modifier) +{ + if (_current_view_edges_set.empty()) + return; + + unsigned id = 0; + ViewEdge *edge; + Chain *new_chain; + + for (I1DContainer::iterator it_edge = _current_view_edges_set.begin(); + it_edge != _current_view_edges_set.end(); + ++it_edge) + { + if (pred(**it_edge)) + continue; + + edge = dynamic_cast<ViewEdge*>(*it_edge); + it.setBegin(edge); + it.setCurrentEdge(edge); + + Chain *new_chain = new Chain(id); + ++id; +#if 0 // FIXME + ViewEdgeIterator it_back(it); + --it_back; +#endif + do { + new_chain->push_viewedge_back(*it, it.getOrientation()); + modifier(**it); + ++it; + } while (!it.isEnd() && !pred(**it)); + it.setBegin(edge); + it.setCurrentEdge(edge); + --it; + while (!it.isEnd() && !pred(**it)) { + new_chain->push_viewedge_front(*it, it.getOrientation()); + modifier(**it); + --it; + } + + _current_chains_set.push_back(new_chain); + } + + if (!_current_chains_set.empty()) + _current_set = &_current_chains_set; +} + +void Operators::bidirectionalChain(ViewEdgeIterator& it, UnaryPredicate1D& pred) +{ + if (_current_view_edges_set.empty()) + return; + + unsigned id = 0; + Functions1D::IncrementChainingTimeStampF1D ts; + Predicates1D::EqualToChainingTimeStampUP1D pred_ts(TimeStamp::instance()->getTimeStamp() + 1); + + ViewEdge *edge; + Chain *new_chain; + + for (I1DContainer::iterator it_edge = _current_view_edges_set.begin(); + it_edge != _current_view_edges_set.end(); + ++it_edge) + { + if (pred(**it_edge) || pred_ts(**it_edge)) + continue; + + edge = dynamic_cast<ViewEdge*>(*it_edge); + it.setBegin(edge); + it.setCurrentEdge(edge); + + Chain *new_chain = new Chain(id); + ++id; +#if 0 //FIXME + ViewEdgeIterator it_back(it); + --it_back; +#endif + do { + new_chain->push_viewedge_back(*it, it.getOrientation()); + ts(**it); + ++it; + } while (!it.isEnd() && !pred(**it) && !pred_ts(**it)); + it.setBegin(edge); + it.setCurrentEdge(edge); + --it; + while (!it.isEnd() && !pred(**it) && !pred_ts(**it)) { + new_chain->push_viewedge_front(*it, it.getOrientation()); + ts(**it); + --it; + } + + _current_chains_set.push_back(new_chain); + } + + if (!_current_chains_set.empty()) + _current_set = &_current_chains_set; +} +#endif + +int Operators::bidirectionalChain(ChainingIterator& it, UnaryPredicate1D& pred) +{ + if (_current_view_edges_set.empty()) + return 0; + + unsigned id = 0; + Functions1D::IncrementChainingTimeStampF1D ts; + Predicates1D::EqualToChainingTimeStampUP1D pred_ts(TimeStamp::instance()->getTimeStamp() + 1); + ViewEdge *edge; + I1DContainer new_chains_set; + + for (I1DContainer::iterator it_edge = _current_view_edges_set.begin(); + it_edge != _current_view_edges_set.end(); + ++it_edge) + { + if (pred(**it_edge) < 0) + goto error; + if (pred.result) + continue; + if (pred_ts(**it_edge) < 0) + goto error; + if (pred_ts.result) + continue; + + edge = dynamic_cast<ViewEdge*>(*it_edge); + // re-init iterator + it.setBegin(edge); + it.setCurrentEdge(edge); + it.setOrientation(true); + if (it.init() < 0) + goto error; + + Chain *new_chain = new Chain(id); + ++id; +#if 0 // FIXME + ViewEdgeIterator it_back(it); + --it_back; +#endif + while (TRUE) { + new_chain->push_viewedge_back(*it, it.getOrientation()); + ts(**it); + if (it.increment() < 0) { + delete new_chain; + goto error; + } + if (it.isEnd()) + break; + if (pred(**it) < 0) { + delete new_chain; + goto error; + } + if (pred.result) + break; + } + it.setBegin(edge); + it.setCurrentEdge(edge); + it.setOrientation(true); + if (it.decrement() < 0) { + delete new_chain; + goto error; + } + while (!it.isEnd()) { + if (pred(**it) < 0) { + delete new_chain; + goto error; + } + if (pred.result) + break; + new_chain->push_viewedge_front(*it, it.getOrientation()); + ts(**it); + if (it.decrement() < 0) { + delete new_chain; + goto error; + } + } + new_chains_set.push_back(new_chain); + } + + if (!new_chains_set.empty()) { + for (I1DContainer::iterator it = new_chains_set.begin(); it != new_chains_set.end(); ++it) { + _current_chains_set.push_back(*it); + } + new_chains_set.clear(); + _current_set = &_current_chains_set; + } + return 0; + +error: + for (I1DContainer::iterator it = new_chains_set.begin(); it != new_chains_set.end(); ++it) { + delete (*it); + } + new_chains_set.clear(); + return -1; +} + +int Operators::bidirectionalChain(ChainingIterator& it) +{ + if (_current_view_edges_set.empty()) + return 0; + + unsigned id = 0; + Functions1D::IncrementChainingTimeStampF1D ts; + Predicates1D::EqualToChainingTimeStampUP1D pred_ts(TimeStamp::instance()->getTimeStamp() + 1); + ViewEdge *edge; + I1DContainer new_chains_set; + + for (I1DContainer::iterator it_edge = _current_view_edges_set.begin(); + it_edge != _current_view_edges_set.end(); + ++it_edge) + { + if (pred_ts(**it_edge) < 0) + goto error; + if (pred_ts.result) + continue; + + edge = dynamic_cast<ViewEdge*>(*it_edge); + // re-init iterator + it.setBegin(edge); + it.setCurrentEdge(edge); + it.setOrientation(true); + if (it.init() < 0) + goto error; + + Chain *new_chain = new Chain(id); + ++id; +#if 0 // FIXME + ViewEdgeIterator it_back(it); + --it_back; +#endif + do { + new_chain->push_viewedge_back(*it, it.getOrientation()); + ts(**it); + if (it.increment() < 0) { // FIXME + delete new_chain; + goto error; + } + } while (!it.isEnd()); + it.setBegin(edge); + it.setCurrentEdge(edge); + it.setOrientation(true); + if (it.decrement() < 0) { // FIXME + delete new_chain; + goto error; + } + while (!it.isEnd()) { + new_chain->push_viewedge_front(*it, it.getOrientation()); + ts(**it); + if (it.decrement() < 0) { // FIXME + delete new_chain; + goto error; + } + } + new_chains_set.push_back(new_chain); + } + + if (!new_chains_set.empty()) { + for (I1DContainer::iterator it = new_chains_set.begin(); it != new_chains_set.end(); ++it) { + _current_chains_set.push_back(*it); + } + new_chains_set.clear(); + _current_set = &_current_chains_set; + } + return 0; + +error: + for (I1DContainer::iterator it = new_chains_set.begin(); it != new_chains_set.end(); ++it) { + delete (*it); + } + new_chains_set.clear(); + return -1; +} + +int Operators::sequentialSplit(UnaryPredicate0D& pred, float sampling) +{ + if (_current_chains_set.empty()) { + cerr << "Warning: current set empty" << endl; + return 0; + } + CurvePoint *point; + Chain *new_curve; + I1DContainer splitted_chains; + Interface0DIterator first; + Interface0DIterator end; + Interface0DIterator last; + Interface0DIterator it; + I1DContainer::iterator cit = _current_chains_set.begin(), citend = _current_chains_set.end(); + for (; cit != citend; ++cit) { + Id currentId = (*cit)->getId(); + new_curve = new Chain(currentId); + first = (*cit)->pointsBegin(sampling); + end = (*cit)->pointsEnd(sampling); + last = end; + --last; + it = first; + + point = dynamic_cast<CurvePoint*>(&(*it)); + new_curve->push_vertex_back(point); + ++it; + for (; it!= end; ++it) { + point = dynamic_cast<CurvePoint*>(&(*it)); + new_curve->push_vertex_back(point); + if (pred(it) < 0) { + delete new_curve; + goto error; + } + if (pred.result && (it != last)) { + splitted_chains.push_back(new_curve); + currentId.setSecond(currentId.getSecond() + 1); + new_curve = new Chain(currentId); + new_curve->push_vertex_back(point); + } + } + if (new_curve->nSegments() == 0) { + delete new_curve; + return 0; + } + + splitted_chains.push_back(new_curve); + } + + // Update the current set of chains: + cit = _current_chains_set.begin(); + for (; cit != citend; ++cit) { + delete (*cit); + } + _current_chains_set.clear(); +#if 0 + _current_chains_set = splitted_chains; +#else + for (cit = splitted_chains.begin(), citend = splitted_chains.end(); cit != citend; ++cit) { + if ((*cit)->getLength2D() < M_EPSILON) { + delete (*cit); + continue; + } + _current_chains_set.push_back(*cit); + } +#endif + splitted_chains.clear(); + + if (!_current_chains_set.empty()) + _current_set = &_current_chains_set; + return 0; + +error: + cit = splitted_chains.begin(); + citend = splitted_chains.end(); + for (; cit != citend; ++cit) { + delete (*cit); + } + splitted_chains.clear(); + return -1; +} + +int Operators::sequentialSplit(UnaryPredicate0D& startingPred, UnaryPredicate0D& stoppingPred, float sampling) +{ + if (_current_chains_set.empty()) { + cerr << "Warning: current set empty" << endl; + return 0; + } + CurvePoint *point; + Chain *new_curve; + I1DContainer splitted_chains; + Interface0DIterator first; + Interface0DIterator end; + Interface0DIterator last; + Interface0DIterator itStart; + Interface0DIterator itStop; + I1DContainer::iterator cit = _current_chains_set.begin(), citend = _current_chains_set.end(); + for (; cit != citend; ++cit) { + Id currentId = (*cit)->getId(); + first = (*cit)->pointsBegin(sampling); + end = (*cit)->pointsEnd(sampling); + last = end; + --last; + itStart = first; + do { + itStop = itStart; + ++itStop; + + new_curve = new Chain(currentId); + currentId.setSecond(currentId.getSecond() + 1); + + point = dynamic_cast<CurvePoint*>(&(*itStart)); + new_curve->push_vertex_back(point); + do { + point = dynamic_cast<CurvePoint*>(&(*itStop)); + new_curve->push_vertex_back(point); + ++itStop; + if (itStop == end) + break; + if (stoppingPred(itStop) < 0) { + delete new_curve; + goto error; + } + } while (!stoppingPred.result); + if (itStop != end) { + point = dynamic_cast<CurvePoint*>(&(*itStop)); + new_curve->push_vertex_back(point); + } + if (new_curve->nSegments() == 0) { + delete new_curve; + } + else { + splitted_chains.push_back(new_curve); + } + // find next start + do{ + ++itStart; + if (itStart == end) + break; + if (startingPred(itStart) < 0) + goto error; + } while (!startingPred.result); + } while ((itStart!=end) && (itStart!=last)); + } + + // Update the current set of chains: + cit = _current_chains_set.begin(); + for (; cit != citend; ++cit) { + delete (*cit); + } + _current_chains_set.clear(); +#if 0 + _current_chains_set = splitted_chains; +#else + for (cit = splitted_chains.begin(), citend = splitted_chains.end(); cit != citend; ++cit) { + if ((*cit)->getLength2D() < M_EPSILON) { + delete (*cit); + continue; + } + _current_chains_set.push_back(*cit); + } +#endif + splitted_chains.clear(); + + if (!_current_chains_set.empty()) + _current_set = &_current_chains_set; + return 0; + +error: + cit = splitted_chains.begin(); + citend = splitted_chains.end(); + for (; cit != citend; ++cit) { + delete (*cit); + } + splitted_chains.clear(); + return -1; +} + +#include "CurveIterators.h" + +// Internal function +static int __recursiveSplit(Chain *_curve, UnaryFunction0D<double>& func, UnaryPredicate1D& pred, float sampling, + Operators::I1DContainer& newChains, Operators::I1DContainer& splitted_chains) +{ + if (((_curve->nSegments() == 1) && (sampling == 0)) || (_curve->getLength2D() <= sampling)) { + newChains.push_back(_curve); + return 0; + } + + CurveInternal::CurvePointIterator first = _curve->curvePointsBegin(sampling); + CurveInternal::CurvePointIterator second = first; + ++second; + CurveInternal::CurvePointIterator end = _curve->curvePointsEnd(sampling); + CurveInternal::CurvePointIterator it = second; + CurveInternal::CurvePointIterator split = second; + Interface0DIterator it0d = it.castToInterface0DIterator(); + real _min = FLT_MAX; // func(it0d); + ++it; + CurveInternal::CurvePointIterator next = it; + ++next; + + bool bsplit = false; + for (; ((it != end) && (next != end)); ++it, ++next) { + it0d = it.castToInterface0DIterator(); + if (func(it0d) < 0) + return -1; + if (func.result < _min) { + _min = func.result; + split = it; + bsplit = true; + } + } + + if (!bsplit) { // we didn't find any minimum + newChains.push_back(_curve); + return 0; + } + + // retrieves the current splitting id + Id *newId = _curve->getSplittingId(); + if (newId == 0) { + newId = new Id(_curve->getId()); + _curve->setSplittingId(newId); + } + + Chain *new_curve_a = new Chain(*newId); + newId->setSecond(newId->getSecond() + 1); + new_curve_a->setSplittingId(newId); + Chain *new_curve_b = new Chain(*newId); + newId->setSecond(newId->getSecond() + 1); + new_curve_b->setSplittingId(newId); + + CurveInternal::CurvePointIterator vit = _curve->curveVerticesBegin(), vitend = _curve->curveVerticesEnd(); + CurveInternal::CurvePointIterator vnext = vit; + ++vnext; + + for (; (vit != vitend) && (vnext != vitend) && (vnext._CurvilinearLength < split._CurvilinearLength); + ++vit, ++vnext) + { + new_curve_a->push_vertex_back(&(*vit)); + } + if ((vit == vitend) || (vnext == vitend)) { + if (G.debug & G_DEBUG_FREESTYLE) { + cout << "The split takes place in bad location" << endl; + } + newChains.push_back(_curve); + delete new_curve_a; + delete new_curve_b; + return 0; + } + + // build the two resulting chains + new_curve_a->push_vertex_back(&(*vit)); + new_curve_a->push_vertex_back(&(*split)); + new_curve_b->push_vertex_back(&(*split)); + + for (vit = vnext; vit != vitend; ++vit) + new_curve_b->push_vertex_back(&(*vit)); + + // let's check whether one or two of the two new curves satisfy the stopping condition or not. + // (if one of them satisfies it, we don't split) + if (pred(*new_curve_a) < 0 || (!pred.result && pred(*new_curve_b) < 0)) { + delete new_curve_a; + delete new_curve_b; + return -1; + } + if (pred.result) { + // we don't actually create these two chains + newChains.push_back(_curve); + delete new_curve_a; + delete new_curve_b; + return 0; + } + // here we know we'll split _curve: + splitted_chains.push_back(_curve); + + __recursiveSplit(new_curve_a, func, pred, sampling, newChains, splitted_chains); + __recursiveSplit(new_curve_b, func, pred, sampling, newChains, splitted_chains); + return 0; +} + +int Operators::recursiveSplit(UnaryFunction0D<double>& func, UnaryPredicate1D& pred, float sampling) +{ + if (_current_chains_set.empty()) { + cerr << "Warning: current set empty" << endl; + return 0; + } + + Chain *currentChain = 0; + I1DContainer splitted_chains; + I1DContainer newChains; + I1DContainer::iterator cit = _current_chains_set.begin(), citend = _current_chains_set.end(); + for (; cit != citend; ++cit) { + currentChain = dynamic_cast<Chain*>(*cit); + if (!currentChain) + continue; + // let's check the first one: + if (pred(*currentChain) < 0) + return -1; + if (!pred.result) { + __recursiveSplit(currentChain, func, pred, sampling, newChains, splitted_chains); + } + else { + newChains.push_back(currentChain); + } + } + // Update the current set of chains: + if (!splitted_chains.empty()) { + for (cit = splitted_chains.begin(), citend = splitted_chains.end(); cit != citend; ++cit) { + delete (*cit); + } + splitted_chains.clear(); + } + + _current_chains_set.clear(); +#if 0 + _current_chains_set = newChains; +#else + for (cit = newChains.begin(), citend = newChains.end(); cit != citend; ++cit) { + if ((*cit)->getLength2D() < M_EPSILON) { + delete (*cit); + continue; + } + _current_chains_set.push_back(*cit); + } +#endif + newChains.clear(); + + if (!_current_chains_set.empty()) + _current_set = &_current_chains_set; + return 0; +} + + +// recursive split with pred 0D +static int __recursiveSplit(Chain *_curve, UnaryFunction0D<double>& func, UnaryPredicate0D& pred0d, + UnaryPredicate1D& pred, float sampling, + Operators::I1DContainer& newChains, Operators::I1DContainer& splitted_chains) +{ + if (((_curve->nSegments() == 1) && (sampling == 0)) || (_curve->getLength2D() <= sampling)) { + newChains.push_back(_curve); + return 0; + } + + CurveInternal::CurvePointIterator first = _curve->curvePointsBegin(sampling); + CurveInternal::CurvePointIterator second = first; + ++second; + CurveInternal::CurvePointIterator end = _curve->curvePointsEnd(sampling); + CurveInternal::CurvePointIterator it = second; + CurveInternal::CurvePointIterator split = second; + Interface0DIterator it0d = it.castToInterface0DIterator(); +#if 0 + real _min = func(it0d); + ++it; +#endif + real _min = FLT_MAX; + ++it; + real mean = 0.f; + //soc unused - real variance = 0.0f; + unsigned count = 0; + CurveInternal::CurvePointIterator next = it; + ++next; + + bool bsplit = false; + for (; ((it != end) && (next != end)); ++it, ++next) { + ++count; + it0d = it.castToInterface0DIterator(); + if (pred0d(it0d) < 0) + return -1; + if (!pred0d.result) + continue; + if (func(it0d) < 0) + return -1; + mean += func.result; + if (func.result < _min) { + _min = func.result; + split = it; + bsplit = true; + } + } + mean /= (float)count; + + //if ((!bsplit) || (mean - _min > mean)) { // we didn't find any minimum + if (!bsplit) { // we didn't find any minimum + newChains.push_back(_curve); + return 0; + } + + // retrieves the current splitting id + Id *newId = _curve->getSplittingId(); + if (newId == NULL) { + newId = new Id(_curve->getId()); + _curve->setSplittingId(newId); + } + + Chain *new_curve_a = new Chain(*newId); + newId->setSecond(newId->getSecond() + 1); + new_curve_a->setSplittingId(newId); + Chain *new_curve_b = new Chain(*newId); + newId->setSecond(newId->getSecond() + 1); + new_curve_b->setSplittingId(newId); + + CurveInternal::CurvePointIterator vit = _curve->curveVerticesBegin(), vitend = _curve->curveVerticesEnd(); + CurveInternal::CurvePointIterator vnext = vit; + ++vnext; + + for (; + (vit != vitend) && (vnext != vitend) && (vnext._CurvilinearLength < split._CurvilinearLength); + ++vit, ++vnext) + { + new_curve_a->push_vertex_back(&(*vit)); + } + if ((vit == vitend) || (vnext == vitend)) { + if (G.debug & G_DEBUG_FREESTYLE) { + cout << "The split takes place in bad location" << endl; + } + newChains.push_back(_curve); + delete new_curve_a; + delete new_curve_b; + return 0; + } + + // build the two resulting chains + new_curve_a->push_vertex_back(&(*vit)); + new_curve_a->push_vertex_back(&(*split)); + new_curve_b->push_vertex_back(&(*split)); + + for (vit = vnext; vit != vitend; ++vit) + new_curve_b->push_vertex_back(&(*vit)); + + // let's check whether one or two of the two new curves satisfy the stopping condition or not. + // (if one of them satisfies it, we don't split) + if (pred(*new_curve_a) < 0 || (!pred.result && pred(*new_curve_b) < 0)) { + delete new_curve_a; + delete new_curve_b; + return -1; + } + if (pred.result) { + // we don't actually create these two chains + newChains.push_back(_curve); + delete new_curve_a; + delete new_curve_b; + return 0; + } + // here we know we'll split _curve: + splitted_chains.push_back(_curve); + + __recursiveSplit(new_curve_a, func, pred0d, pred, sampling, newChains, splitted_chains); + __recursiveSplit(new_curve_b, func, pred0d, pred, sampling, newChains, splitted_chains); + return 0; +} + +int Operators::recursiveSplit(UnaryFunction0D<double>& func, UnaryPredicate0D& pred0d, UnaryPredicate1D& pred, + float sampling) +{ + if (_current_chains_set.empty()) { + cerr << "Warning: current set empty" << endl; + return 0; + } + + Chain *currentChain = 0; + I1DContainer splitted_chains; + I1DContainer newChains; + I1DContainer::iterator cit = _current_chains_set.begin(), citend = _current_chains_set.end(); + for (; cit != citend; ++cit) { + currentChain = dynamic_cast<Chain*>(*cit); + if (!currentChain) + continue; + // let's check the first one: + if (pred(*currentChain) < 0) + return -1; + if (!pred.result) { + __recursiveSplit(currentChain, func, pred0d, pred, sampling, newChains, splitted_chains); + } + else { + newChains.push_back(currentChain); + } + } + // Update the current set of chains: + if (!splitted_chains.empty()) { + for (cit = splitted_chains.begin(), citend = splitted_chains.end(); cit != citend; ++cit) { + delete (*cit); + } + splitted_chains.clear(); + } + + _current_chains_set.clear(); +#if 0 + _current_chains_set = newChains; +#else + for (cit = newChains.begin(), citend = newChains.end(); cit != citend; ++cit) { + if ((*cit)->getLength2D() < M_EPSILON) { + delete (*cit); + continue; + } + _current_chains_set.push_back(*cit); + } +#endif + newChains.clear(); + + if (!_current_chains_set.empty()) + _current_set = &_current_chains_set; + return 0; +} + +// Internal class +class PredicateWrapper +{ +public: + inline PredicateWrapper(BinaryPredicate1D& pred) + { + _pred = &pred; + } + + inline bool operator()(Interface1D *i1, Interface1D *i2) + { + if ((*_pred)(*i1, *i2) < 0) + throw std::runtime_error("comparison failed"); + return _pred->result; + } + +private: + BinaryPredicate1D *_pred; +}; + +int Operators::sort(BinaryPredicate1D& pred) +{ + if (!_current_set) + return 0; + PredicateWrapper wrapper(pred); + try { + std::sort(_current_set->begin(), _current_set->end(), wrapper); + } + catch (std::runtime_error &e) { + cerr << "Warning: Operator.sort(): " << e.what() << endl; + return -1; + } + return 0; +} + +static Stroke *createStroke(Interface1D& inter) +{ + Stroke *stroke = new Stroke; + stroke->setId(inter.getId()); + + float currentCurvilignAbscissa = 0.0f; + + Interface0DIterator it = inter.verticesBegin(), itend = inter.verticesEnd(); + Interface0DIterator itfirst = it; + + Vec2r current(it->getPoint2D()); + Vec2r previous = current; + SVertex *sv; + CurvePoint *cp; + StrokeVertex *stroke_vertex = NULL; + bool hasSingularity = false; + + do { + cp = dynamic_cast<CurvePoint*>(&(*it)); + if (!cp) { + sv = dynamic_cast<SVertex*>(&(*it)); + if (!sv) { + cerr << "Warning: unexpected Vertex type" << endl; + continue; + } + stroke_vertex = new StrokeVertex(sv); + } + else { + stroke_vertex = new StrokeVertex(cp); + } + current = stroke_vertex->getPoint2D(); + Vec2r vec_tmp(current - previous); + real dist = vec_tmp.norm(); + if (dist < 1.0e-6) + hasSingularity = true; + currentCurvilignAbscissa += dist; + stroke_vertex->setCurvilinearAbscissa(currentCurvilignAbscissa); + stroke->push_back(stroke_vertex); + previous = current; + ++it; + } while ((it != itend) && (it != itfirst)); + + if (it == itfirst) { + // Add last vertex: + cp = dynamic_cast<CurvePoint*>(&(*it)); + if (!cp) { + sv = dynamic_cast<SVertex*>(&(*it)); + if (!sv) + cerr << "Warning: unexpected Vertex type" << endl; + else + stroke_vertex = new StrokeVertex(sv); + } + else { + stroke_vertex = new StrokeVertex(cp); + } + current = stroke_vertex->getPoint2D(); + Vec2r vec_tmp(current - previous); + real dist = vec_tmp.norm(); + if (dist < 1.0e-6) + hasSingularity = true; + currentCurvilignAbscissa += dist; + stroke_vertex->setCurvilinearAbscissa(currentCurvilignAbscissa); + stroke->push_back(stroke_vertex); + } + // Discard the stroke if the number of stroke vertices is less than two + if (stroke->strokeVerticesSize() < 2) { + delete stroke; + return NULL; + } + stroke->setLength(currentCurvilignAbscissa); + if (hasSingularity) { + // Try to address singular points such that the distance between two subsequent vertices + // are smaller than epsilon. + Interface0DIterator v = stroke->verticesBegin(); + Interface0DIterator vnext = v; + ++vnext; + Vec2r next((*v).getPoint2D()); + while (!vnext.isEnd()) { + current = next; + next = (*vnext).getPoint2D(); + if ((next - current).norm() < 1.0e-6) { + Interface0DIterator vprevious = v; + if (!vprevious.isBegin()) + --vprevious; + + // collect a set of overlapping vertices (except the first one) + std::vector<Interface0D *> overlapping_vertices; + do { + overlapping_vertices.push_back(&(*vnext)); + current = next; + ++v; + ++vnext; + if (vnext.isEnd()) + break; + next = (*vnext).getPoint2D(); + } while ((next - current).norm() < 1.0e-6); + + Vec2r target; + bool reverse; + if (!vnext.isEnd()) { + target = (*vnext).getPoint2D(); + reverse = false; + } + else if (!vprevious.isBegin()) { + target = (*vprevious).getPoint2D(); + reverse = true; + } + else { + // Discard the stroke because all stroke vertices are overlapping + delete stroke; + return NULL; + } + Vec2r dir(target - current); + real dist = dir.norm(); + real len = 1.0e-3; // default offset length + int nvert = overlapping_vertices.size(); + if (dist < len * nvert) { + len = dist / (nvert + 1); + } + dir.normalize(); + Vec2r offset(dir * len); +#if 0 + if (G.debug & G_DEBUG_FREESTYLE) { + cout << "#vert " << nvert << " len " << len << " reverse? " << reverse << endl; + } +#endif + // add the offset to the overlapping vertices + StrokeVertex *sv; + std::vector<Interface0D *>::iterator it = overlapping_vertices.begin(), + itend = overlapping_vertices.end(); + if (!reverse) { + int n = 1; + for (; it != itend; ++it) { + sv = dynamic_cast<StrokeVertex*>(*it); + sv->setPoint(sv->getPoint() + offset * n); + ++n; + } + } + else { + int n = nvert; + for (; it != itend; ++it) { + sv = dynamic_cast<StrokeVertex*>(*it); + sv->setPoint(sv->getPoint() + offset * n); + --n; + } + } + + if (vnext.isEnd()) + break; + } + ++v; + ++vnext; + } + } + { + // Check if the stroke no longer contains singular points + Interface0DIterator v = stroke->verticesBegin(); + Interface0DIterator vnext = v; + ++vnext; + Vec2r next((*v).getPoint2D()); + bool warning = false; + while (!vnext.isEnd()) { + current = next; + next = (*vnext).getPoint2D(); + if ((next - current).norm() < 1.0e-6) { + warning = true; + break; + } + ++v; + ++vnext; + } + if (warning && G.debug & G_DEBUG_FREESTYLE) { + printf("Warning: stroke contains singular points.\n"); + } + } + return stroke; +} + + +inline int applyShading(Stroke& stroke, vector<StrokeShader*>& shaders) +{ + for (vector<StrokeShader*>::iterator it = shaders.begin(); it != shaders.end(); ++it) { + if ((*it)->shade(stroke) < 0) { + return -1; + } + } + return 0; +} + + +int Operators::create(UnaryPredicate1D& pred, vector<StrokeShader*> shaders) +{ + //Canvas* canvas = Canvas::getInstance(); + if (!_current_set) { + cerr << "Warning: current set empty" << endl; + return 0; + } + StrokesContainer new_strokes_set; + for (Operators::I1DContainer::iterator it = _current_set->begin(); it != _current_set->end(); ++it) { + if (pred(**it) < 0) + goto error; + if (!pred.result) + continue; + + Stroke *stroke = createStroke(**it); + if (stroke) { + if (applyShading(*stroke, shaders) < 0) { + delete stroke; + goto error; + } + //canvas->RenderStroke(stroke); + new_strokes_set.push_back(stroke); + } + } + + for (StrokesContainer::iterator it = new_strokes_set.begin(); it != new_strokes_set.end(); ++it) { + _current_strokes_set.push_back(*it); + } + new_strokes_set.clear(); + return 0; + +error: + for (StrokesContainer::iterator it = new_strokes_set.begin(); it != new_strokes_set.end(); ++it) { + delete (*it); + } + new_strokes_set.clear(); + return -1; +} + +void Operators::reset() +{ + ViewMap *vm = ViewMap::getInstance(); + if (!vm) { + cerr << "Error: no ViewMap computed yet" << endl; + return; + } + _current_view_edges_set.clear(); + for (I1DContainer::iterator it = _current_chains_set.begin(); it != _current_chains_set.end(); ++it) + delete *it; + _current_chains_set.clear(); +#if 0 + _current_view_edges_set.insert(_current_view_edges_set.begin(), + vm->ViewEdges().begin(), + vm->ViewEdges().end()); +#else + ViewMap::viewedges_container& vedges = vm->ViewEdges(); + ViewMap::viewedges_container::iterator ve = vedges.begin(), veend = vedges.end(); + for (; ve != veend; ++ve) { + if ((*ve)->getLength2D() < M_EPSILON) + continue; + _current_view_edges_set.push_back(*ve); + } +#endif + _current_set = &_current_view_edges_set; + _current_strokes_set.clear(); +} diff --git a/source/blender/freestyle/intern/stroke/Operators.h b/source/blender/freestyle/intern/stroke/Operators.h new file mode 100644 index 00000000000..aafbc2350d9 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Operators.h @@ -0,0 +1,274 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_OPERATORS_H__ +#define __FREESTYLE_OPERATORS_H__ + +/** \file blender/freestyle/intern/stroke/Operators.h + * \ingroup freestyle + * \brief Class gathering stroke creation algorithms + * \author Stephane Grabli + * \author Emmanuel Turquin + * \date 01/07/2003 + */ + +#include <iostream> +#include <vector> + +#include "Chain.h" +#include "ChainingIterators.h" +#include "Predicates0D.h" +#include "Predicates1D.h" +#include "StrokeShader.h" + +#include "../system/TimeStamp.h" + +#include "../view_map/Interface1D.h" +#include "../view_map/ViewMap.h" + +/*! Class defining the operators used in a style module. + * There are 4 classes of operators: Selection, Chaining, Splitting and Creating. All these operators are + * user controlled in the scripting language through Functors, Predicates and Shaders that are taken as arguments. + */ +class LIB_STROKE_EXPORT Operators { + +public: + typedef vector<Interface1D*> I1DContainer; + typedef vector<Stroke*> StrokesContainer; + + // + // Operators + // + //////////////////////////////////////////////// + + /*! Selects the ViewEdges of the ViewMap verifying a specified condition. + * \param pred The predicate expressing this condition + */ + static int select(UnaryPredicate1D& pred); + + /*! Builds a set of chains from the current set of ViewEdges. + * Each ViewEdge of the current list starts a new chain. The chaining operator then iterates over the ViewEdges + * of the ViewMap using the user specified iterator. + * This operator only iterates using the increment operator and is therefore unidirectional. + * \param it + * The iterator on the ViewEdges of the ViewMap. It contains the chaining rule. + * \param pred + * The predicate on the ViewEdge that expresses the stopping condition. + * \param modifier + * A function that takes a ViewEdge as argument and that is used to modify the processed ViewEdge + * state (the timestamp incrementation is a typical illustration of such a modifier) + */ + static int chain(ViewEdgeInternal::ViewEdgeIterator& it, UnaryPredicate1D& pred, UnaryFunction1D_void& modifier); + + /*! Builds a set of chains from the current set of ViewEdges. + * Each ViewEdge of the current list starts a new chain. The chaining operator then iterates over the ViewEdges + * of the ViewMap using the user specified iterator. + * This operator only iterates using the increment operator and is therefore unidirectional. + * This chaining operator is different from the previous one because it doesn't take any modifier as argument. + * Indeed, the time stamp (insuring that a ViewEdge is processed one time) is automatically managed in this case. + * \param it + * The iterator on the ViewEdges of the ViewMap. It contains the chaining rule. + * \param pred + * The predicate on the ViewEdge that expresses the stopping condition. + */ + static int chain(ViewEdgeInternal::ViewEdgeIterator& it, UnaryPredicate1D& pred); + + /*! Builds a set of chains from the current set of ViewEdges. + * Each ViewEdge of the current list potentially starts a new chain. The chaining operator then iterates over + * the ViewEdges of the ViewMap using the user specified iterator. + * This operator iterates both using the increment and decrement operators and is therefore bidirectional. + * This operator works with a ChainingIterator which contains the chaining rules. It is this last one which can + * be told to chain only edges that belong to the selection or not to process twice a ViewEdge during the chaining. + * Each time a ViewEdge is added to a chain, its chaining time stamp is incremented. This allows you to keep track + * of the number of chains to which a ViewEdge belongs to. + * \param it + * The ChainingIterator on the ViewEdges of the ViewMap. It contains the chaining rule. + * \param pred + * The predicate on the ViewEdge that expresses the stopping condition. + */ + static int bidirectionalChain(ChainingIterator& it, UnaryPredicate1D& pred); + + /*! The only difference with the above bidirectional chaining algorithm is that we don't need to pass a stopping + * criterion. This might be desirable when the stopping criterion is already contained in the iterator definition. + * Builds a set of chains from the current set of ViewEdges. + * Each ViewEdge of the current list potentially starts a new chain. The chaining operator then iterates over + * the ViewEdges of the ViewMap using the user specified iterator. + * This operator iterates both using the increment and decrement operators and is therefore bidirectional. + * This operator works with a ChainingIterator which contains the chaining rules. It is this last one which can be + * told to chain only edges that belong to the selection or not to process twice a ViewEdge during the chaining. + * Each time a ViewEdge is added to a chain, its chaining time stamp is incremented. This allows you to keep track + * of the number of chains to which a ViewEdge belongs to. + * \param it + * The ChainingIterator on the ViewEdges of the ViewMap. It contains the chaining rule. + */ + static int bidirectionalChain(ChainingIterator& it); + + /*! Splits each chain of the current set of chains in a sequential way. + * The points of each chain are processed (with a specified sampling) sequentially. + * Each time a user specified starting condition is verified, a new chain begins and ends as soon as a + * user-defined stopping predicate is verified. + * This allows chains overlapping rather than chains partitioning. + * The first point of the initial chain is the first point of one of the resulting chains. + * The splitting ends when no more chain can start. + * \param startingPred + * The predicate on a point that expresses the starting condition + * \param stoppingPred + * The predicate on a point that expresses the stopping condition + * \param sampling + * The resolution used to sample the chain for the predicates evaluation. (The chain is not actually + * resampled, a virtual point only progresses along the curve using this resolution) + */ + static int sequentialSplit(UnaryPredicate0D& startingPred, UnaryPredicate0D& stoppingPred, float sampling = 0.0f); + + /*! Splits each chain of the current set of chains in a sequential way. + * The points of each chain are processed (with a specified sampling) sequentially and each time a user + * specified condition is verified, the chain is split into two chains. + * The resulting set of chains is a partition of the initial chain + * \param pred + * The predicate on a point that expresses the splitting condition + * \param sampling + * The resolution used to sample the chain for the predicate evaluation. (The chain is not actually + * resampled, a virtual point only progresses along the curve using this resolution) + */ + static int sequentialSplit(UnaryPredicate0D& pred, float sampling = 0.0f); + + /*! Splits the current set of chains in a recursive way. + * We process the points of each chain (with a specified sampling) to find the point minimizing a specified + * function. The chain is split in two at this point and the two new chains are processed in the same way. + * The recursivity level is controlled through a predicate 1D that expresses a stopping condition + * on the chain that is about to be processed. + * \param func + * The Unary Function evaluated at each point of the chain. + * The splitting point is the point minimizing this function + * \param pred + * The Unary Predicate ex pressing the recursivity stopping condition. + * This predicate is evaluated for each curve before it actually gets split. + * If pred(chain) is true, the curve won't be split anymore. + * \param sampling + * The resolution used to sample the chain for the predicates evaluation. (The chain is not actually + * resampled, a virtual point only progresses along the curve using this resolution) + */ + static int recursiveSplit(UnaryFunction0D<double>& func, UnaryPredicate1D& pred, float sampling = 0); + + /*! Splits the current set of chains in a recursive way. + * We process the points of each chain (with a specified sampling) to find the point minimizing a specified + * function. The chain is split in two at this point and the two new chains are processed in the same way. + * The user can specify a 0D predicate to make a first selection on the points that can potentially be split. + * A point that doesn't verify the 0D predicate won't be candidate in realizing the min. + * The recursivity level is controlled through a predicate 1D that expresses a stopping condition + * on the chain that is about to be processed. + * \param func + * The Unary Function evaluated at each point of the chain. + * The splitting point is the point minimizing this function + * \param pred0d + * The Unary Predicate 0D used to select the candidate points where the split can occur. + * For example, it is very likely that would rather have your chain splitting around its middle point + * than around one of its extremities. A 0D predicate working on the curvilinear abscissa allows + * to add this kind of constraints. + * \param pred + * The Unary Predicate ex pressing the recursivity stopping condition. + * This predicate is evaluated for each curve before it actually gets split. + * If pred(chain) is true, the curve won't be split anymore. + * \param sampling + * The resolution used to sample the chain for the predicates evaluation. (The chain is not actually + * resampled, a virtual point only progresses along the curve using this resolution) + */ + static int recursiveSplit(UnaryFunction0D<double>& func, UnaryPredicate0D& pred0d, UnaryPredicate1D& pred, + float sampling = 0.0f); + + /*! Sorts the current set of chains (or viewedges) according to the comparison predicate given as argument. + * \param pred + * The binary predicate used for the comparison + */ + static int sort(BinaryPredicate1D& pred); + + /*! Creates and shades the strokes from the current set of chains. + * A predicate can be specified to make a selection pass on the chains. + * \param pred + * The predicate that a chain must verify in order to be transform as a stroke + * \param shaders + * The list of shaders used to shade the strokes + */ + static int create(UnaryPredicate1D& pred, vector<StrokeShader*> shaders); + + // + // Data access + // + //////////////////////////////////////////////// + + static ViewEdge *getViewEdgeFromIndex(unsigned i) + { + return dynamic_cast<ViewEdge*>(_current_view_edges_set[i]); + } + + static Chain *getChainFromIndex(unsigned i) + { + return dynamic_cast<Chain*>(_current_chains_set[i]); + } + + static Stroke *getStrokeFromIndex(unsigned i) + { + return _current_strokes_set[i]; + } + + static unsigned getViewEdgesSize() + { + return _current_view_edges_set.size(); + } + + static unsigned getChainsSize() + { + return _current_chains_set.size(); + } + + static unsigned getStrokesSize() + { + return _current_strokes_set.size(); + } + + // + // Not exported in Python + // + ////////////////////////////////////////////////// + + static StrokesContainer *getStrokesSet() + { + return &_current_strokes_set; + } + + static void reset(); + +private: + Operators() {} + + static I1DContainer _current_view_edges_set; + static I1DContainer _current_chains_set; + static I1DContainer *_current_set; + static StrokesContainer _current_strokes_set; +}; + +#endif // __FREESTYLE_OPERATORS_H__ diff --git a/source/blender/freestyle/intern/stroke/PSStrokeRenderer.cpp b/source/blender/freestyle/intern/stroke/PSStrokeRenderer.cpp new file mode 100644 index 00000000000..0b527f9f2af --- /dev/null +++ b/source/blender/freestyle/intern/stroke/PSStrokeRenderer.cpp @@ -0,0 +1,106 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/freestyle/intern/stroke/PSStrokeRenderer.cpp + * \ingroup freestyle + * \brief Class to define the Postscript rendering of a stroke + * \author Stephane Grabli + * \date 10/26/2004 + */ + +#include "Canvas.h" +#include "PSStrokeRenderer.h" + +PSStrokeRenderer::PSStrokeRenderer(const char *iFileName) : StrokeRenderer() +{ + if (!iFileName) + iFileName = "freestyle.ps"; + // open the stream: + _ofstream.open(iFileName, ios::out); + if (!_ofstream.is_open()) { + cerr << "couldn't open the output file " << iFileName << endl; + } + _ofstream << "%!PS-Adobe-2.0 EPSF-2.0" << endl; + _ofstream << "%%Creator: Freestyle (http://artis.imag.fr/Software/Freestyle)" << endl; + _ofstream << "%%BoundingBox: " << 0 << " "<< 0 << " " << Canvas::getInstance()->width() << " " + << Canvas::getInstance()->height() << endl; + _ofstream << "%%EndComments" << endl; +} + +PSStrokeRenderer::~PSStrokeRenderer() +{ + Close(); +} + +void PSStrokeRenderer::RenderStrokeRep(StrokeRep *iStrokeRep) const +{ + RenderStrokeRepBasic(iStrokeRep); +} + +void PSStrokeRenderer::RenderStrokeRepBasic(StrokeRep *iStrokeRep) const +{ + vector<Strip*>& strips = iStrokeRep->getStrips(); + Strip::vertex_container::iterator v[3]; + StrokeVertexRep *svRep[3]; + Vec3r color[3]; + for (vector<Strip*>::iterator s = strips.begin(), send = strips.end(); s != send; ++s) { + Strip::vertex_container& vertices = (*s)->vertices(); + v[0] = vertices.begin(); + v[1] = v[0]; + ++(v[1]); + v[2] = v[1]; + ++(v[2]); + + while (v[2] != vertices.end()) { + svRep[0] = *(v[0]); + svRep[1] = *(v[1]); + svRep[2] = *(v[2]); + + color[0] = svRep[0]->color(); + //color[1] = svRep[1]->color(); + //color[2] = svRep[2]->color(); + + _ofstream << "newpath" << endl; + _ofstream << (color[0])[0] << " " << (color[0])[1] << " " << (color[0])[2] << " setrgbcolor" <<endl; + _ofstream << svRep[0]->point2d()[0] << " " <<svRep[0]->point2d()[1] << " moveto" << endl; + _ofstream << svRep[1]->point2d()[0] << " " <<svRep[1]->point2d()[1] << " lineto" << endl; + _ofstream << svRep[2]->point2d()[0] << " " <<svRep[2]->point2d()[1] << " lineto" << endl; + _ofstream << "closepath" << endl; + _ofstream << "fill" << endl; + + ++v[0]; + ++v[1]; + ++v[2]; + } + } +} + +void PSStrokeRenderer::Close() +{ + if (_ofstream.is_open()) + _ofstream.close(); +} diff --git a/source/blender/freestyle/intern/stroke/PSStrokeRenderer.h b/source/blender/freestyle/intern/stroke/PSStrokeRenderer.h new file mode 100644 index 00000000000..e01ec531471 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/PSStrokeRenderer.h @@ -0,0 +1,69 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_PS_STROKE_RENDERER_H__ +#define __FREESTYLE_PS_STROKE_RENDERER_H__ + +/** \file blender/freestyle/intern/stroke/PSStrokeRenderer.h + * \ingroup freestyle + * \brief Class to define the Postscript rendering of a stroke + * \author Stephane Grabli + * \date 10/26/2004 + */ + +#include <fstream> + +#include "StrokeRenderer.h" + +#include "../system/FreestyleConfig.h" + +/**********************************/ +/* */ +/* */ +/* PSStrokeRenderer */ +/* */ +/* */ +/**********************************/ + +class LIB_STROKE_EXPORT PSStrokeRenderer : public StrokeRenderer +{ +public: + PSStrokeRenderer(const char * iFileName = 0); + virtual ~PSStrokeRenderer(); + + /*! Renders a stroke rep */ + virtual void RenderStrokeRep(StrokeRep *iStrokeRep) const; + virtual void RenderStrokeRepBasic(StrokeRep *iStrokeRep) const; + + /*! Closes the output PS file */ + void Close(); + +protected: + mutable ofstream _ofstream; +}; + +#endif // __FREESTYLE_PS_STROKE_RENDERER_H__ diff --git a/source/blender/freestyle/intern/stroke/Predicates0D.h b/source/blender/freestyle/intern/stroke/Predicates0D.h new file mode 100644 index 00000000000..796bbdef0f6 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Predicates0D.h @@ -0,0 +1,186 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_PREDICATES_0D_H__ +#define __FREESTYLE_PREDICATES_0D_H__ + +/** \file blender/freestyle/intern/stroke/Predicates0D.h + * \ingroup freestyle + * \brief Class gathering stroke creation algorithms + * \author Stephane Grabli + * \author Emmanuel Turquin + * \date 01/07/2003 + */ + +#include "../python/Director.h" + +#include "../view_map/Functions0D.h" + +// +// UnaryPredicate0D (base class for predicates in 0D) +// +/////////////////////////////////////////////////////////// + +/*! Base class for Unary Predicates that work on Interface0DIterator. + * A UnaryPredicate0D is a functor that evaluates a condition on a Interface0DIterator and returns + * true or false depending on whether this condition is satisfied or not. + * The UnaryPredicate0D is used by calling its () operator. + * Any inherited class must overload the () operator. + */ +class UnaryPredicate0D +{ +public: + bool result; + PyObject *py_up0D; + + /*! Default constructor. */ + UnaryPredicate0D() + { + py_up0D = 0; + } + + /*! Destructor. */ + virtual ~UnaryPredicate0D() {} + + /*! Returns the string of the name of the UnaryPredicate0D. */ + virtual string getName() const + { + return "UnaryPredicate0D"; + } + + /*! The () operator. Must be overload by inherited classes. + * \param it + * The Interface0DIterator pointing onto the Interface0D at which we wish to evaluate the predicate. + * \return true if the condition is satisfied, false otherwise. + */ + virtual int operator()(Interface0DIterator& it) + { + return Director_BPy_UnaryPredicate0D___call__(this, it); + } +}; + + +// +// BinaryPredicate0D (base class for predicates in 0D) +// +/////////////////////////////////////////////////////////// + +/*! Base class for Binary Predicates working on Interface0D. + * A BinaryPredicate0D is typically an ordering relation between two Interface0D. + * It evaluates a relation between 2 Interface0D and returns true or false. + * It is used by calling the () operator. + */ +class BinaryPredicate0D +{ +public: + bool result; + PyObject *py_bp0D; + + /*! Default constructor. */ + BinaryPredicate0D() + { + py_bp0D = 0; + } + + /*! Destructor. */ + virtual ~BinaryPredicate0D() {} + + /*! Returns the string of the name of the binary predicate. */ + virtual string getName() const + { + return "BinaryPredicate0D"; + } + + /*! The () operator. Must be overload by inherited classes. + * It evaluates a relation between 2 Interface0D. + * \param inter1 + * The first Interface0D. + * \param inter2 + * The second Interface0D. + * \return true or false. + */ + virtual int operator()(Interface0D& inter1, Interface0D& inter2) + { + return Director_BPy_BinaryPredicate0D___call__(this, inter1, inter2); + } +}; + + +// +// Predicates definitions +// +/////////////////////////////////////////////////////////// + +namespace Predicates0D { + +// TrueUP0D +/*! Returns true any time */ +class TrueUP0D : public UnaryPredicate0D +{ +public: + /*! Default constructor. */ + TrueUP0D() {} + + /*! Returns the string "TrueUP0D"*/ + string getName() const + { + return "TrueUP0D"; + } + + /*! The () operator. */ + int operator()(Interface0DIterator&) + { + result = true; + return 0; + } +}; + +// FalseUP0D +/*! Returns false any time */ +class FalseUP0D : public UnaryPredicate0D +{ +public: + /*! Default constructor. */ + FalseUP0D() {} + + /*! Returns the string "FalseUP0D"*/ + string getName() const + { + return "FalseUP0D"; + } + + /*! The () operator. */ + int operator()(Interface0DIterator&) + { + result = false; + return 0; + } +}; + +} // end of namespace Predicates0D + +#endif // __FREESTYLE_PREDICATES_0D_H__ diff --git a/source/blender/freestyle/intern/stroke/Predicates1D.h b/source/blender/freestyle/intern/stroke/Predicates1D.h new file mode 100644 index 00000000000..18e4328c4d5 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Predicates1D.h @@ -0,0 +1,589 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_PREDICATES_1D_H__ +#define __FREESTYLE_PREDICATES_1D_H__ + +/** \file blender/freestyle/intern/stroke/Predicates1D.h + * \ingroup freestyle + * \brief Class gathering stroke creation algorithms + * \author Stephane Grabli + * \author Emmanuel Turquin + * \date 01/07/2003 + */ + +#include <string> + +#include "AdvancedFunctions1D.h" + +#include "../python/Director.h" + +#include "../system/TimeStamp.h" + +#include "../view_map/Interface1D.h" +#include "../view_map/Functions1D.h" + +// +// UnaryPredicate1D (base class for predicates in 1D) +// +/////////////////////////////////////////////////////////// + +/*! Base class for Unary Predicates that work on Interface1D. + * A UnaryPredicate1D is a functor that evaluates a condition on a Interface1D and returns + * true or false depending on whether this condition is satisfied or not. + * The UnaryPredicate1D is used by calling its () operator. + * Any inherited class must overload the () operator. + */ +class UnaryPredicate1D +{ +public: + bool result; + PyObject *py_up1D; + + /*! Default constructor. */ + UnaryPredicate1D() + { + py_up1D = NULL; + } + + /*! Destructor. */ + virtual ~UnaryPredicate1D() {} + + /*! Returns the string of the name of the UnaryPredicate1D. */ + virtual string getName() const + { + return "UnaryPredicate1D"; + } + + /*! The () operator. Must be overload by inherited classes. + * \param inter + * The Interface1D on which we wish to evaluate the predicate. + * \return true if the condition is satisfied, false otherwise. + */ + virtual int operator()(Interface1D& inter) + { + return Director_BPy_UnaryPredicate1D___call__(this, inter); + } +}; + + +// +// BinaryPredicate1D (base class for predicates in 1D) +// +/////////////////////////////////////////////////////////// + +/*! Base class for Binary Predicates working on Interface1D. + * A BinaryPredicate1D is typically an ordering relation between two Interface1D. + * It evaluates a relation between 2 Interface1D and returns true or false. + * It is used by calling the () operator. + */ +class BinaryPredicate1D +{ +public: + bool result; + PyObject *py_bp1D; + + /*! Default constructor. */ + BinaryPredicate1D() + { + py_bp1D = NULL; + } + + /*! Destructor. */ + virtual ~BinaryPredicate1D() {} + + /*! Returns the string of the name of the binary predicate. */ + virtual string getName() const + { + return "BinaryPredicate1D"; + } + + /*! The () operator. Must be overload by inherited classes. + * It evaluates a relation between 2 Interface1D. + * \param inter1 + * The first Interface1D. + * \param inter2 + * The second Interface1D. + * \return true or false. + */ + virtual int operator()(Interface1D& inter1, Interface1D& inter2) + { + return Director_BPy_BinaryPredicate1D___call__(this, inter1, inter2); + } +}; + + +// +// Predicates definitions +// +/////////////////////////////////////////////////////////// + +namespace Predicates1D { + +// TrueUP1D +/*! Returns true */ +class TrueUP1D : public UnaryPredicate1D +{ +public: + /*! Constructor */ + TrueUP1D() {} + + /*! Returns the string "TrueUP1D"*/ + string getName() const + { + return "TrueUP1D"; + } + + /*! the () operator */ + int operator()(Interface1D&) + { + result = true; + return 0; + } +}; + +// FalseUP1D +/*! Returns false */ +class FalseUP1D : public UnaryPredicate1D +{ +public: + /*! Constructor */ + FalseUP1D() {} + + /*! Returns the string "FalseUP1D"*/ + string getName() const + { + return "FalseUP1D"; + } + + /*! the () operator */ + int operator()(Interface1D&) + { + result = false; + return 0; + } +}; + +// QuantitativeInvisibilityUP1D +/*! Returns true if the Quantitative Invisibility evaluated at an Interface1D, using the QuantitativeInvisibilityF1D + * functor, equals a certain user-defined value. + */ +class QuantitativeInvisibilityUP1D : public UnaryPredicate1D +{ +public: + /*! Builds the Predicate. + * \param qi + * The Quantitative Invisibility you want the Interface1D to have + */ + QuantitativeInvisibilityUP1D(unsigned qi = 0) : _qi(qi) {} + + /*! Returns the string "QuantitativeInvisibilityUP1D" */ + string getName() const + { + return "QuantitativeInvisibilityUP1D"; + } + + /*! the () operator */ + int operator()(Interface1D& inter) + { + Functions1D::QuantitativeInvisibilityF1D func; + if (func(inter) < 0) + return -1; + result = (func.result == _qi); + return 0; + } + +private: + unsigned _qi; +}; + +// ContourUP1D +/*! Returns true if the Interface1D is a contour. + * An Interface1D is a contour if it is borded by a different shape on each of its sides. + */ +class ContourUP1D : public UnaryPredicate1D +{ +private: + Functions1D::CurveNatureF1D _getNature; + +public: + /*! Returns the string "ContourUP1D"*/ + string getName() const + { + return "ContourUP1D"; + } + + /*! The () operator. */ + int operator()(Interface1D& inter) + { + if (_getNature(inter) < 0) + return -1; + if ((_getNature.result & Nature::SILHOUETTE) || (_getNature.result & Nature::BORDER)) { + Interface0DIterator it = inter.verticesBegin(); + for (; !it.isEnd(); ++it) { + if (Functions0D::getOccludeeF0D(it) != Functions0D::getShapeF0D(it)) { + result = true; + return 0; + } + } + } + result = false; + return 0; + } +}; + +// ExternalContourUP1D +/*! Returns true if the Interface1D is an external contour. + * An Interface1D is an external contour if it is borded by no shape on one of its sides. + */ +class ExternalContourUP1D : public UnaryPredicate1D +{ +private: + Functions1D::CurveNatureF1D _getNature; + +public: + /*! Returns the string "ExternalContourUP1D" */ + string getName() const + { + return "ExternalContourUP1D"; + } + + /*! The () operator. */ + int operator()(Interface1D& inter) + { + if (_getNature(inter) < 0) + return -1; + if ((_getNature.result & Nature::SILHOUETTE) || (_getNature.result & Nature::BORDER)) { + set<ViewShape*> occluded; + Functions1D::getOccludeeF1D(inter, occluded); + for (set<ViewShape*>::iterator os = occluded.begin(), osend = occluded.end(); os != osend; ++os) { + if ((*os) == 0) { + result = true; + return 0; + } + } + } + result = false; + return 0; + } +}; + +// EqualToTimeStampUP1D +/*! Returns true if the Interface1D's time stamp is equal to a certain user-defined value. */ +class EqualToTimeStampUP1D : public UnaryPredicate1D +{ +protected: + unsigned _timeStamp; + +public: + EqualToTimeStampUP1D(unsigned ts) : UnaryPredicate1D() + { + _timeStamp = ts; + } + + /*! Returns the string "EqualToTimeStampUP1D"*/ + string getName() const + { + return "EqualToTimeStampUP1D"; + } + + /*! The () operator. */ + int operator()(Interface1D& inter) + { + result = (inter.getTimeStamp() == _timeStamp); + return 0; + } +}; + +// EqualToChainingTimeStampUP1D +/*! Returns true if the Interface1D's time stamp is equal to a certain user-defined value. */ +class EqualToChainingTimeStampUP1D : public UnaryPredicate1D +{ +protected: + unsigned _timeStamp; + +public: + EqualToChainingTimeStampUP1D(unsigned ts) : UnaryPredicate1D() + { + _timeStamp = ts; + } + + /*! Returns the string "EqualToChainingTimeStampUP1D"*/ + string getName() const + { + return "EqualToChainingTimeStampUP1D"; + } + + /*! The () operator. */ + int operator()(Interface1D& inter) + { + ViewEdge *edge = dynamic_cast<ViewEdge*>(&inter); + if (!edge) + { + result = false; + return 0; + } + result = (edge->getChainingTimeStamp() >= _timeStamp); + return 0; + } +}; + +// ShapeUP1D +/*! Returns true if the shape to which the Interface1D belongs to has the same Id as the one specified by the user. */ +class ShapeUP1D: public UnaryPredicate1D +{ +private: + Id _id; + +public: + /*! Builds the Predicate. + * \param idFirst + * The first Id component. + * \param idSecond + * The second Id component. + */ + ShapeUP1D(unsigned idFirst, unsigned idSecond = 0) : UnaryPredicate1D() + { + _id = Id(idFirst, idSecond); + } + + /*! Returns the string "ShapeUP1D"*/ + string getName() const + { + return "ShapeUP1D"; + } + + /*! The () operator. */ + int operator()(Interface1D& inter) + { + set<ViewShape*> shapes; + Functions1D::getShapeF1D(inter, shapes); + for (set<ViewShape*>::iterator s = shapes.begin(), send = shapes.end(); s != send; ++s) { + if ((*s)->getId() == _id) { + result = true; + return 0; + } + } + result = false; + return 0; + } +}; + +// WithinImageBoundaryUP1D +/*! Returns true if the Interface1D is (partly) within the image boundary. */ +class WithinImageBoundaryUP1D: public UnaryPredicate1D +{ +private: + real _xmin, _ymin, _xmax, _ymax; + +public: + /*! Builds the Predicate. + * \param xmin + * The X lower bound of the image boundary. + * \param ymin + * The Y lower bound of the image boundary. + * \param xmax + * The X upper bound of the image boundary. + * \param ymax + * The Y upper bound of the image boundary. + */ + WithinImageBoundaryUP1D(const real xmin, const real ymin, const real xmax, const real ymax) + : _xmin(xmin), _ymin(ymin), _xmax(xmax), _ymax(ymax) + { + } + + /*! Returns the string "WithinImageBoundaryUP1D" */ + string getName() const + { + return "WithinImageBoundaryUP1D"; + } + + /*! The () operator. */ + int operator()(Interface1D& inter) + { + // 1st pass: check if a point is within the image boundary. + Interface0DIterator it = inter.verticesBegin(), itend = inter.verticesEnd(); + for (; it != itend; ++it) { + real x = (*it).getProjectedX(); + real y = (*it).getProjectedY(); + if (_xmin <= x && x <= _xmax && _ymin <= y && y <= _ymax) { + result = true; + return 0; + } + } + // 2nd pass: check if a line segment intersects with the image boundary. + it = inter.verticesBegin(); + if (it != itend) { + Vec2r pmin(_xmin, _ymin); + Vec2r pmax(_xmax, _ymax); + Vec2r prev((*it).getPoint2D()); + ++it; + for (; it != itend; ++it) { + Vec2r p((*it).getPoint2D()); + if (GeomUtils::intersect2dSeg2dArea (pmin, pmax, prev, p)) { + result = true; + return 0; + } + prev = p; + } + } + result = false; + return 0; + } +}; + +// +// Binary Predicates definitions +// +/////////////////////////////////////////////////////////// + +// TrueBP1D +/*! Returns true. */ +class TrueBP1D : public BinaryPredicate1D +{ +public: + /*! Returns the string "TrueBP1D" */ + string getName() const + { + return "TrueBP1D"; + } + + /*! The () operator. */ + int operator()(Interface1D& i1, Interface1D& i2) + { + result = true; + return 0; + } +}; + +// FalseBP1D +/*! Returns false. */ +class FalseBP1D : public BinaryPredicate1D +{ +public: + /*! Returns the string "FalseBP1D" */ + string getName() const + { + return "FalseBP1D"; + } + + /*! The () operator. */ + int operator()(Interface1D& i1, Interface1D& i2) + { + result = false; + return 0; + } +}; + +// Length2DBP1D +/*! Returns true if the 2D length of the Interface1D i1 is less than the 2D length of the Interface1D i2. */ +class Length2DBP1D : public BinaryPredicate1D +{ +public: + /*! Returns the string "Length2DBP1D" */ + string getName() const + { + return "Length2DBP1D"; + } + + /*! The () operator. */ + int operator()(Interface1D& i1, Interface1D& i2) + { + result = (i1.getLength2D() > i2.getLength2D()); + return 0; + } +}; + +// SameShapeIdBP1D +/*! Returns true if the Interface1D i1 and i2 belong to the same shape. */ +class SameShapeIdBP1D : public BinaryPredicate1D +{ +public: + /*! Returns the string "SameShapeIdBP1D" */ + string getName() const + { + return "SameShapeIdBP1D"; + } + + /*! The () operator. */ + int operator()(Interface1D& i1, Interface1D& i2) + { + set<ViewShape*> shapes1; + Functions1D::getShapeF1D(i1, shapes1); + set<ViewShape*> shapes2; + Functions1D::getShapeF1D(i2, shapes2); + // FIXME:// n2 algo, can do better... + for (set<ViewShape*>::iterator s = shapes1.begin(), send = shapes1.end(); s != send; ++s) { + Id current = (*s)->getId(); + for (set<ViewShape*>::iterator s2 = shapes2.begin(), s2end = shapes2.end(); s2 != s2end; ++s2) { + if ((*s2)->getId() == current) { + result = true; + return 0; + } + } + } + result = false; + return 0; + } +}; + +// ViewMapGradientNormBP1D +/*! Returns true if the evaluation of the Gradient norm Function is higher for Interface1D i1 than for i2. */ +class ViewMapGradientNormBP1D : public BinaryPredicate1D +{ +private: + Functions1D::GetViewMapGradientNormF1D _func; + +public: + ViewMapGradientNormBP1D(int level, IntegrationType iType = MEAN, float sampling = 2.0) + : BinaryPredicate1D(), _func(level, iType, sampling) + { + } + + /*! Returns the string "ViewMapGradientNormBP1D" */ + string getName() const + { + return "ViewMapGradientNormBP1D"; + } + + /*! The () operator. */ + int operator()(Interface1D& i1, Interface1D& i2) + { + if (_func(i1) < 0) + return -1; + real n1 = _func.result; + if (_func(i2) < 0) + return -1; + real n2 = _func.result; + result = (n1 > n2); + return 0; + } +}; + +} // end of namespace Predicates1D + +#endif // __FREESTYLE_PREDICATES_1D_H__ diff --git a/source/blender/freestyle/intern/stroke/QInformationMap.h b/source/blender/freestyle/intern/stroke/QInformationMap.h new file mode 100644 index 00000000000..38a126d0f66 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/QInformationMap.h @@ -0,0 +1,73 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_Q_INFORMATION_MAP_H__ +#define __FREESTYLE_Q_INFORMATION_MAP_H__ + +/** \file blender/freestyle/intern/stroke/QInformationMap.h + * \ingroup freestyle + * \brief Class defining an information map using a QImage + * \author Stephane Grabli + * \date 04/01/2003 + */ + +#include <qimage.h> + +#include "InformationMap.h" + +class QInformationMap : public InformationMap +{ +private: + QImage _map; // the image or a piece of image + +public: + QInformationMap(); + QInformationMap(const QImage&); + QInformationMap(const QInformationMap&); + QInformationMap& operator=(const QInformationMap&); + + //float getSmoothedPixel(int x, int y, float sigma = 0.2f);1 + virtual float getMean(int x, int y); + virtual void retrieveMeanAndVariance(int x, int y, float &oMean, float &oVariance); + + inline const QImage& map() const + { + return _map; + } + + inline void setMap(const QImage& iMap, float iw, float ih) + { + _map = iMap.copy(); + _w = iw; + _h = ih; + } + +protected: + virtual float computeGaussian(int x, int y); +}; + +#endif // __FREESTYLE_Q_INFORMATION_MAP_H__ diff --git a/source/blender/freestyle/intern/stroke/Stroke.cpp b/source/blender/freestyle/intern/stroke/Stroke.cpp new file mode 100644 index 00000000000..7e6aa62ac59 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Stroke.cpp @@ -0,0 +1,954 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** 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 "StrokeAdvancedIterators.h" +#include "StrokeIterators.h" +#include "StrokeRenderer.h" + +#include "BKE_global.h" + +/**********************************/ +/* */ +/* */ +/* 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 = true; + + // 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(), it2end = a2._userAttributesReal->end(); + for (; it1 != it1end; ++it1) { + (*_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(), it2end = a2._userAttributesVec2f->end(); + for (; it1 != it1end; ++it1) { + (*_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(), it2end = a2._userAttributesVec3f->end(); + for (; it1 != it1end; ++it1) { + (*_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; + _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; + _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)); + 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; + } +}; + +void Stroke::Resample(int iNPoints) +{ + int vertsize = strokeVerticesSize(); + if (iNPoints <= vertsize) + return; + + StrokeInternal::StrokeVertexIterator it = strokeVerticesBegin(); + StrokeInternal::StrokeVertexIterator next = it; + ++next; + StrokeInternal::StrokeVertexIterator itend = strokeVerticesEnd(); + + vertex_container newVertices; + real t = 0.0f; + StrokeVertex *newVertex = NULL; + vector<StrokeSegment> 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((iNPoints - strokeVerticesSize()) * 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 + int NPointsToAdd = iNPoints - vertsize; + bool checkEveryone = false; + while (N < NPointsToAdd) { + for (vector<StrokeSegment>::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 = true; + N++; + if (N == NPointsToAdd) + break; + } + } + checkEveryone = true; + } + //actually resample: + for (vector<StrokeSegment>::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(); + + if (_rep) { + delete _rep; + _rep = new StrokeRep(this); + } +} + +void Stroke::Resample(float iSampling) +{ + //cerr << "old size :" << strokeVerticesSize() << endl; + if (iSampling == 0) + return; + if (iSampling >= _sampling) + return; + + _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(); + + if (_rep) { + delete _rep; + _rep = new StrokeRep(this); + } +} + +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 it = _Vertices.begin(), itend = _Vertices.end(); + + 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->RenderStrokeRepBasic(_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<Stroke, const_vertex_iterator>(this, it); +} + +Vec3r Stroke::orientation2d(int iCombination) const +{ + return edge_orientation2d_function<Stroke>(*this, iCombination); +} + +inline Vec3r Stroke::orientation3d(const_vertex_iterator it) const +{ + return iterator_edge_orientation3d_function<Stroke, const_vertex_iterator>(*this, it); +} + +Vec3r Stroke::orientation3d(int iCombination) const +{ + return edge_orientation3d_function<Stroke>(*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<Stroke>(*this, iCombination); +} + +Vec3r Stroke::curvature2d_as_vector(int iCombination) const +{ + return curvature2d_as_vector_edge_function<Stroke>(*this, iCombination); +} + +real Stroke::curvature2d_as_angle(int iCombination) const +{ + return curvature2d_as_angle_edge_function<Stroke>(*this, iCombination); +} + +float Stroke::shape_importance(int iCombination) const +{ + return shape_importance_edge_function<Stroke>(*this, iCombination); +} + + +float Stroke::local_average_depth(int iCombination ) const +{ + return local_average_depth_edge_function<Stroke >(*this, iCombination); +} + +float Stroke::local_depth_variance(int iCombination) const +{ + return local_depth_variance_edge_function<Stroke>(*this, iCombination); +} + +real Stroke::local_average_density(float sigma , int iCombination ) const +{ + return density_edge_function<Stroke>(*this, iCombination); +} +#endif diff --git a/source/blender/freestyle/intern/stroke/Stroke.h b/source/blender/freestyle/intern/stroke/Stroke.h new file mode 100644 index 00000000000..51733a058a9 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Stroke.h @@ -0,0 +1,822 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_STROKE_H__ +#define __FREESTYLE_STROKE_H__ + +/** \file blender/freestyle/intern/stroke/Stroke.h + * \ingroup freestyle + * \brief Classes to define a stroke + * \author Stephane Grabli + * \date 09/09/2002 + */ + +#include <map> +#include <vector> + +#include "Curve.h" + +#include "../view_map/Interface1D.h" +#include "../view_map/Silhouette.h" + +#include "../system/FreestyleConfig.h" +#include "../system/StringUtils.h" + + +// +// StrokeAttribute +// +//////////////////////////////////////////////////////// + +/*! Class to define an attribute associated to a Stroke Vertex. + * This attribute stores the color, alpha and thickness values for a Stroke Vertex. + */ +class LIB_STROKE_EXPORT StrokeAttribute +{ +public: + /*! default constructor */ + StrokeAttribute(); + + /*! Copy constructor */ + StrokeAttribute(const StrokeAttribute& iBrother); + + /*! Builds a stroke vertex attribute from a set of parameters. + * \param iRColor + * The Red Component value. + * \param iGColor + * The Green Component value. + * \param iBColor + * The Blue Component value. + * \param iAlpha + * The transparency value + * \param iRThickness + * The thickness of the stroke on the right + * \param iLThickness + * The Thickness of the stroke on the left + */ + StrokeAttribute(float iRColor, float iGColor, float iBColor, float iAlpha, float iRThickness, float iLThickness); + + /*! Interpolation constructor. + * Builds a StrokeAttribute from two StrokeAttributes and an interpolation parameter. + * \param a1 + * The first Attribute. + * \param a2 + * The second parameter. + * \param t + * The interpolation parameter. + */ + StrokeAttribute(const StrokeAttribute& a1, const StrokeAttribute& a2, float t); + + /*! destructor */ + virtual ~StrokeAttribute(); + + /* operators */ + /*! operator = */ + StrokeAttribute& operator=(const StrokeAttribute& iBrother); + + /* accessors */ + /*! Returns the attribute's color. + * \return The array of 3 floats containing the R,G,B values of the attribute's color. + */ + inline const float *getColor() const + { + return _color; + } + + /*! Returns the R color component. */ + inline const float getColorR() const + { + return _color[0]; + } + + /*! Returns the G color component. */ + inline const float getColorG() const + { + return _color[1]; + } + + /*! Returns the B color component. */ + inline const float getColorB() const + { + return _color[2]; + } + + /*! Returns the RGB color components. */ + inline Vec3f getColorRGB() const + { + return Vec3f(_color[0], _color[1], _color[2]); + } + + /*! Returns the alpha color component. */ + inline float getAlpha() const + { + return _alpha; + } + + /*! Returns the attribute's thickness. + * \return an array of 2 floats. the first value is the thickness on the right of the vertex when following + * the stroke, the second one is the thickness on the left. + */ + inline const float *getThickness() const + { + return _thickness; + } + + /*! Returns the thickness on the right of the vertex when following the stroke. */ + inline const float getThicknessR() const + { + return _thickness[0]; + } + + /*! Returns the thickness on the left of the vertex when following the stroke. */ + inline const float getThicknessL() const + { + return _thickness[1]; + } + + /*! Returns the thickness on the right and on the left of the vertex when following the stroke. */ + inline Vec2f getThicknessRL() const + { + return Vec2f(_thickness[0], _thickness[1]); + } + + /*! Returns true if the strokevertex is visible, false otherwise */ + inline bool isVisible() const + { + return _visible; + } + + /*! Returns an attribute of type real + * \param iName + * The name of the attribute + */ + float getAttributeReal(const char *iName) const; + + /*! Returns an attribute of type Vec2f + * \param iName + * The name of the attribute + */ + Vec2f getAttributeVec2f(const char *iName) const; + + /*! Returns an attribute of type Vec3f + * \param iName + * The name of the attribute + */ + Vec3f getAttributeVec3f(const char *iName) const; + + /*! Checks whether the attribute iName is availbale */ + bool isAttributeAvailableReal(const char *iName) const; + + /*! Checks whether the attribute iName is availbale */ + bool isAttributeAvailableVec2f(const char *iName) const; + + /*! Checks whether the attribute iName is availbale */ + bool isAttributeAvailableVec3f(const char *iName) const; + + /* modifiers */ + /*! sets the attribute's color. + * \param r + * The new R value. + * \param g + * The new G value. + * \param b + * The new B value. + */ + inline void setColor(float r, float g, float b) + { + _color[0] = r; + _color[1] = g; + _color[2] = b; + } + + /*! sets the attribute's color. + * \param iRGB + * The new RGB values. + */ + inline void setColor(const Vec3f& iRGB) + { + _color[0] = iRGB[0]; + _color[1] = iRGB[1]; + _color[2] = iRGB[2]; + } + + /*! sets the attribute's alpha value. + * \param alpha + * The new alpha value. + */ + inline void setAlpha(float alpha) + { + _alpha = alpha; + } + + /*! sets the attribute's thickness. + * \param tr + * The thickness on the right of the vertex when following the stroke. + * \param tl + * The thickness on the left of the vertex when following the stroke. + */ + inline void setThickness(float tr, float tl) + { + _thickness[0] = tr; + _thickness[1] = tl; + } + + /*! sets the attribute's thickness. + * \param tRL + * The thickness on the right and on the left of the vertex when following the stroke. + */ + inline void setThickness(const Vec2f& tRL) + { + _thickness[0] = tRL[0]; + _thickness[1] = tRL[1]; + } + + /*! sets the visible flag. True means visible. */ + inline void setVisible(bool iVisible) + { + _visible = iVisible; + } + + /*! Adds a user defined attribute of type real + * If there is no attribute of name iName, it is added. + * Otherwise, the new value replaces the old one. + * \param iName + * The name of the attribute + * \param att + * The attribute's value + */ + void setAttributeReal(const char *iName, float att); + + /*! Adds a user defined attribute of type Vec2f + * If there is no attribute of name iName, it is added. + * Otherwise, the new value replaces the old one. + * \param iName + * The name of the attribute + * \param att + * The attribute's value + */ + void setAttributeVec2f(const char *iName, const Vec2f& att); + + /*! Adds a user defined attribute of type Vec3f + * If there is no attribute of name iName, it is added. + * Otherwise, the new value replaces the old one. + * \param iName + * The name of the attribute + * \param att + * The attribute's value + */ + void setAttributeVec3f(const char *iName, const Vec3f& att); + +private: + typedef std::map<const char*, float, StringUtils::ltstr> realMap; + typedef std::map<const char*, Vec2f, StringUtils::ltstr> Vec2fMap; + typedef std::map<const char*, Vec3f, StringUtils::ltstr> Vec3fMap; + + float _color[3]; //! the color + float _alpha; //! alpha + float _thickness[2]; //! the thickness on the right and on the left of the backbone vertex (the stroke is oriented) + bool _visible; + realMap *_userAttributesReal; + Vec2fMap *_userAttributesVec2f; + Vec3fMap *_userAttributesVec3f; +}; + + +// +// StrokeVertex +// +//////////////////////////////////////////////////////// + +/*! Class to define a stroke vertex. */ +class LIB_STROKE_EXPORT StrokeVertex : public CurvePoint +{ +public: // Implementation of Interface0D + /*! Returns the string "StrokeVertex" */ + virtual string getExactTypeName() const + { + return "StrokeVertex"; + } + +private: + StrokeAttribute _Attribute; //! The attribute associated to the vertex + float _CurvilignAbscissa; //! the curvilign abscissa + float _StrokeLength; // stroke length + +public: + /*! default constructor */ + StrokeVertex(); + + /*! Copy constructor */ + StrokeVertex(const StrokeVertex& iBrother); + + /*! Builds a stroke vertex from a SVertex */ + StrokeVertex(SVertex *iSVertex); + + /*! Builds a stroke vertex from a CurvePoint */ + StrokeVertex(CurvePoint *iPoint); + + /*! Builds Stroke Vertex from 2 stroke vertices and an interpolation parameter*/ + StrokeVertex(StrokeVertex *iA, StrokeVertex *iB, float t3); + + /*! Builds a stroke from a view vertex and an attribute */ + StrokeVertex(SVertex *iSVertex, const StrokeAttribute& iAttribute); + + /*! destructor */ + virtual ~StrokeVertex(); + + /* operators */ + /*! operator = */ + StrokeVertex& operator=(const StrokeVertex& iBrother); + + /* accessors */ + /*! Returns the 2D point x coordinate */ + inline real x() const + { + return _Point2d[0]; + } + + /*! Returns the 2D point y coordinate */ + inline real y() const + { + return _Point2d[1]; + } + + /*! Returns the 2D point coordinates as a Vec2d */ + Vec2f getPoint () + { + return Vec2f((float)point2d()[0], (float)point2d()[1]); + } + + /*! Returns the ith 2D point coordinate (i=0 or 1)*/ + inline real operator[](const int i) const + { + return _Point2d[i]; + } + + /*! Returns the StrokeAttribute for this StrokeVertex */ + inline const StrokeAttribute& attribute() const + { + return _Attribute; + } + + /*! Returns a non-const reference to the StrokeAttribute of this StrokeVertex */ + inline StrokeAttribute& attribute() + { + return _Attribute; + } + + /*! Returns the curvilinear abscissa */ + inline float curvilinearAbscissa() const + { + return _CurvilignAbscissa; + } + + /*! Returns the length of the Stroke to which this StrokeVertex belongs */ + inline float strokeLength() const + { + return _StrokeLength; + } + + /*! Returns the curvilinear abscissa of this StrokeVertex in the Stroke */ + inline float u() const + { + return _CurvilignAbscissa / _StrokeLength; + } + + /* modifiers */ + /*! sets the 2D x value */ + inline void setX(real x) + { + _Point2d[0] = x; + } + + /*! sets the 2D y value */ + inline void setY(real y) + { + _Point2d[1] = y; + } + + /*! sets the 2D x and y values */ + inline void setPoint(real x, real y) + { + _Point2d[0] = x; + _Point2d[1] = y; + } + + /*! sets the 2D x and y values */ + inline void setPoint(const Vec2f& p) + { + _Point2d[0] = p[0]; + _Point2d[1] = p[1]; + } + + /*! Returns a reference to the ith 2D point coordinate (i=0 or 1) */ + inline real& operator[](const int i) + { + return _Point2d[i]; + } + + /*! sets the attribute. */ + inline void setAttribute(const StrokeAttribute& iAttribute) + { + _Attribute = iAttribute; + } + + /*! sets the curvilinear abscissa of this StrokeVertex in the Stroke */ + inline void setCurvilinearAbscissa(float iAbscissa) + { + _CurvilignAbscissa = iAbscissa; + } + + /*! sets the Stroke's length (it's only a value stored by the Stroke Vertex, it won't change the real + * Stroke's length.) + */ + inline void setStrokeLength(float iLength) + { + _StrokeLength = iLength; + } + + /* interface definition */ + /* inherited */ +}; + + +// +// Stroke +// +//////////////////////////////////////////////////////// + +class StrokeRenderer; +class StrokeRep; + +namespace StrokeInternal { + +class vertex_const_traits; +class vertex_nonconst_traits; +template<class Traits> class vertex_iterator_base; +class StrokeVertexIterator; + +} // end of namespace StrokeInternal + +/*! Class to define a stroke. + * A stroke is made of a set of 2D vertices (StrokeVertex), regularly spaced out. + * This set of vertices defines the stroke's backbone geometry. + * Each of these stroke vertices defines the stroke's shape and appearance at this vertex position. + */ +class LIB_STROKE_EXPORT Stroke : public Interface1D +{ +public: // Implementation of Interface1D + /*! Returns the string "Stroke" */ + virtual string getExactTypeName() const + { + return "Stroke"; + } + + // Data access methods + + /*! Returns the Id of the Stroke */ + virtual Id getId() const + { + return _id; + } + + /*! The different blending modes available to similate the interaction media-medium. */ + typedef enum { + DRY_MEDIUM, /*!< To simulate a dry medium such as Pencil or Charcoal.*/ + HUMID_MEDIUM, /*!< To simulate ink painting (color substraction blending).*/ + OPAQUE_MEDIUM, /*!< To simulate an opaque medium (oil, spray...).*/ + } MediumType; + +public: + typedef std::deque<StrokeVertex*> vertex_container; // the vertices container + typedef std::vector<ViewEdge*> viewedge_container; // the viewedges container + typedef StrokeInternal::vertex_iterator_base<StrokeInternal::vertex_nonconst_traits > vertex_iterator; + typedef StrokeInternal::vertex_iterator_base<StrokeInternal::vertex_const_traits> const_vertex_iterator; + +public: + //typedef StrokeVertex vertex_type; + +private: + vertex_container _Vertices; //! The stroke's backbone vertices + Id _id; + float _Length; // The stroke length + viewedge_container _ViewEdges; + float _sampling; + StrokeRenderer *_renderer; // mark implementation OpenGL renderer + MediumType _mediumType; + unsigned int _textureId; + bool _tips; + Vec2r _extremityOrientations[2]; // the orientations of the first and last extermity + StrokeRep *_rep; + +public: + /*! default constructor */ + Stroke(); + + /*! copy constructor */ + Stroke(const Stroke& iBrother); + + /*! Builds a stroke from a set of StrokeVertex. + * This constructor is templated by an iterator type. + * This iterator type must allow the vertices parsing using the ++ operator. + * \param iBegin + * The iterator pointing to the first vertex. + * \param iEnd + * The iterator pointing to the end of the vertex list. + */ + template<class InputVertexIterator> + Stroke(InputVertexIterator iBegin, InputVertexIterator iEnd); + + /*! Destructor */ + virtual ~Stroke(); + + /* operators */ + /*! operator = */ + Stroke& operator=(const Stroke& iBrother); + + /*! Compute the sampling needed to get iNVertices vertices. + * If the specified number of vertices is less than the actual number of vertices, the actual sampling value + * is returned. (To remove Vertices, use the RemoveVertex() method of this class). + * \param iNVertices + * The number of StrokeVertices we eventually want in our Stroke. + * \return the sampling that must be used in the Resample(float) method. + * @see Resample(int) + * @see Resample(float) + */ + float ComputeSampling(int iNVertices); + + /*! Resampling method. + * Resamples the curve so that it eventually has iNPoints. That means it is going to add iNPoints-vertices_size, + * if vertices_size is the number of points we already have. + * If vertices_size >= iNPoints, no resampling is done. + * \param iNPoints + * The number of vertices we eventually want in our stroke. + */ + void Resample(int iNPoints); + + /*! Resampling method. + * Resamples the curve with a given sampling. + * If this sampling is < to the actual sampling value, no resampling is done. + * \param iSampling + * The new sampling value. + */ + void Resample(float iSampling); + + /*! Removes the stroke vertex iVertex + * from the stroke. + * The length and curvilinear abscissa are updated + * consequently. + */ + void RemoveVertex(StrokeVertex *iVertex); + + /*! Inserts the stroke vertex iVertex in the stroke before next. + * The length, curvilinear abscissa are updated consequently. + * \param iVertex + * The StrokeVertex to insert in the Stroke. + * \param next + * A StrokeVertexIterator pointing to the StrokeVeretx before which iVertex must be inserted. + */ + void InsertVertex(StrokeVertex *iVertex, StrokeInternal::StrokeVertexIterator next); + + /*! Updates the 2D length of the Stroke */ + void UpdateLength(); + + /* Render method */ + void ScaleThickness(float iFactor); + void Render(const StrokeRenderer *iRenderer); + void RenderBasic(const StrokeRenderer *iRenderer); + + /* Iterator definition */ + + /* accessors */ + /*! Returns the 2D length of the Stroke */ + inline real getLength2D() const + { + return _Length; + } + + /*! Returns a reference to the time stamp value of the stroke. */ + /*! Returns the MediumType used for this Stroke. */ + inline MediumType getMediumType() const + { + return _mediumType; + } + + /*! Returns the id of the texture used to simulate th marks system for this Stroke */ + inline unsigned int getTextureId() {return _textureId;} + + /*! Returns true if this Stroke uses a texture with tips, false otherwise. */ + inline bool hasTips() const + { + return _tips; + } + + /* these advanced iterators are used only in C++ */ + inline int vertices_size() const + { + return _Vertices.size(); + } + + inline viewedge_container::const_iterator viewedges_begin() const + { + return _ViewEdges.begin(); + } + + inline viewedge_container::iterator viewedges_begin() + { + return _ViewEdges.begin(); + } + + inline viewedge_container::const_iterator viewedges_end() const + { + return _ViewEdges.end(); + } + + inline viewedge_container::iterator viewedges_end() + { + return _ViewEdges.end(); + } + + inline int viewedges_size() const + { + return _ViewEdges.size(); + } + + inline Vec2r getBeginningOrientation() const + { + return _extremityOrientations[0]; + } + + inline real getBeginningOrientationX() const + { + return _extremityOrientations[0].x(); + } + + inline real getBeginningOrientationY() const + { + return _extremityOrientations[0].y(); + } + + inline Vec2r getEndingOrientation() const + { + return _extremityOrientations[1]; + } + + inline real getEndingOrientationX() const + { + return _extremityOrientations[1].x(); + } + + inline real getEndingOrientationY() const + { + return _extremityOrientations[1].y(); + } + + + /* modifiers */ + /*! sets the Id of the Stroke. */ + inline void setId(const Id& id) + { + _id = id; + } + + /*! sets the 2D length of the Stroke. */ + void setLength(float iLength); + + /*! sets the medium type that must be used for this Stroke. */ + inline void setMediumType(MediumType iType) + { + _mediumType = iType; + } + + /*! sets the texture id to be used to simulate the marks system for this Stroke. */ + inline void setTextureId(unsigned int id) + { + _textureId = id; + } + + /*! sets the flag telling whether this stroke is using a texture with tips or not. */ + inline void setTips(bool iTips) + { + _tips = iTips; + } + + inline void push_back(StrokeVertex *iVertex) + { + _Vertices.push_back(iVertex); + } + + inline void push_front(StrokeVertex *iVertex) + { + _Vertices.push_front(iVertex); + } + + inline void AddViewEdge(ViewEdge *iViewEdge) + { + _ViewEdges.push_back(iViewEdge); + } + + inline void setBeginningOrientation(const Vec2r& iOrientation) + { + _extremityOrientations[0] = iOrientation; + } + + inline void setBeginningOrientation(real x, real y) + { + _extremityOrientations[0] = Vec2r(x, y); + } + + inline void setEndingOrientation(const Vec2r& iOrientation) + { + _extremityOrientations[1] = iOrientation; + } + + inline void setEndingOrientation(real x, real y) + { + _extremityOrientations[1] = Vec2r(x, y); + } + + /* Information access interface */ + + // embedding vertex iterator + const_vertex_iterator vertices_begin() const; + vertex_iterator vertices_begin(float t = 0.0f); + const_vertex_iterator vertices_end() const; + vertex_iterator vertices_end(); + + /*! Returns a StrokeVertexIterator pointing on the first StrokeVertex of the Stroke. One can specifly a sampling + * value to resample the Stroke on the fly if needed. + * \param t + * The resampling value with which we want our Stroke to be resampled. + * If 0 is specified, no resampling is done. + */ + StrokeInternal::StrokeVertexIterator strokeVerticesBegin(float t = 0.0f); + + /*! Returns a StrokeVertexIterator pointing after the last StrokeVertex of the Stroke. */ + StrokeInternal::StrokeVertexIterator strokeVerticesEnd(); + + /*! Returns the number of StrokeVertex constituing the Stroke. */ + inline unsigned int strokeVerticesSize() const + { + return _Vertices.size(); + } + + /*! Returns the i-th StrokeVertex constituting the Stroke. */ + inline StrokeVertex& strokeVerticeAt(unsigned int i) + { + return *(_Vertices.at(i)); + } + + // Iterator access (Interface1D) + /*! Returns an Interface0DIterator pointing on the first StrokeVertex of the Stroke. */ + virtual Interface0DIterator verticesBegin(); + + /*! Returns an Interface0DIterator pointing after the last StrokeVertex of the Stroke. */ + virtual Interface0DIterator verticesEnd(); + + virtual Interface0DIterator pointsBegin(float t = 0.0f); + virtual Interface0DIterator pointsEnd(float t = 0.0f); +}; + + +// +// Implementation +// +//////////////////////////////////////////////////////// + + +template<class InputVertexIterator> +Stroke::Stroke(InputVertexIterator iBegin, InputVertexIterator iEnd) +{ + for (InputVertexIterator v = iBegin, vend = iEnd; v != vend; v++) { + _Vertices.push_back(*v); + } + _Length = 0; + _id = 0; +} + +#endif // __FREESTYLE_STROKE_H__ diff --git a/source/blender/freestyle/intern/stroke/StrokeAdvancedIterators.h b/source/blender/freestyle/intern/stroke/StrokeAdvancedIterators.h new file mode 100644 index 00000000000..adaa5548bf9 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeAdvancedIterators.h @@ -0,0 +1,191 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_STROKE_ADVANCED_ITERATORS_H__ +#define __FREESTYLE_STROKE_ADVANCED_ITERATORS_H__ + +/** \file blender/freestyle/intern/stroke/StrokeAdvancedIterators.h + * \ingroup freestyle + * \brief Iterators used to iterate over the elements of the Stroke. Can't be used in python + * \author Stephane Grabli + * \author Emmanuel Turquin + * \date 01/07/2003 + */ + +#include "Stroke.h" + +namespace StrokeInternal { + +class vertex_const_traits : public Const_traits<StrokeVertex*> +{ +public: + typedef std::deque<StrokeVertex*> vertex_container; + typedef vertex_container::const_iterator vertex_container_iterator; +}; + +class vertex_nonconst_traits : public Nonconst_traits<StrokeVertex*> +{ +public: + typedef std::deque<StrokeVertex*> vertex_container; //! the vertices container + typedef vertex_container::iterator vertex_container_iterator; +}; + + +template<class Traits> +class vertex_iterator_base : public IteratorBase<Traits,BidirectionalIteratorTag_Traits> +{ +public: + typedef vertex_iterator_base<Traits> Self; + +protected: + typedef IteratorBase<Traits,BidirectionalIteratorTag_Traits> parent_class; + typedef typename Traits::vertex_container_iterator vertex_container_iterator; + typedef vertex_iterator_base<vertex_nonconst_traits> iterator; + typedef vertex_iterator_base<vertex_const_traits> const_iterator; + +//protected: +public: + vertex_container_iterator _it; + vertex_container_iterator _begin; + vertex_container_iterator _end; + +public: + friend class Stroke; + //friend class vertex_iterator; + + inline vertex_iterator_base() : parent_class() {} + + inline vertex_iterator_base(const iterator& iBrother) : parent_class() + { + _it = iBrother._it; + _begin = iBrother._begin; + _end = iBrother._end; + } + + inline vertex_iterator_base(const const_iterator& iBrother) : parent_class() + { + _it = iBrother._it; + _begin = iBrother._begin; + _end = iBrother._end; + } + +//protected: //FIXME +public: + inline vertex_iterator_base(vertex_container_iterator it, vertex_container_iterator begin, + vertex_container_iterator end) + : parent_class() + { + _it = it; + _begin = begin; + _end = end; + } + +public: + virtual ~vertex_iterator_base() {} + + virtual bool begin() const + { + return (_it == _begin) ? true : false; + } + + virtual bool end() const + { + return (_it == _end) ? true : false; + } + + // operators + inline Self& operator++() // operator corresponding to ++i + { + ++_it; + return *(this); + } + + /* Operator corresponding to i++, i.e. which returns the value *and then* increments. + * That's why we store the value in a temp. + */ + inline Self operator++(int) + { + Self tmp = *this; + ++_it; + return tmp; + } + + inline Self& operator--() // operator corresponding to --i + { + --_it; + return *(this); + } + + inline Self operator--(int) + { + Self tmp = *this; + --_it; + return tmp; + } + + // comparibility + virtual bool operator!=(const Self& b) const + { + return (_it != b._it); + } + + virtual bool operator==(const Self& b) const + { + return !(*this != b); + } + + // dereferencing + virtual typename Traits::reference operator*() const + { + return *(_it); + } + + virtual typename Traits::pointer operator->() const + { + return &(operator*()); + } + + /*! accessors */ + inline vertex_container_iterator it() const + { + return _it; + } + + inline vertex_container_iterator getBegin() const + { + return _begin; + } + + inline vertex_container_iterator getEnd() const + { + return _end; + } +}; + +} // end of namespace StrokeInternal + +#endif // __FREESTYLE_STROKE_ADVANCED_ITERATORS_H__ diff --git a/source/blender/freestyle/intern/stroke/StrokeIO.cpp b/source/blender/freestyle/intern/stroke/StrokeIO.cpp new file mode 100644 index 00000000000..a101ec67618 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeIO.cpp @@ -0,0 +1,73 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/freestyle/intern/stroke/StrokeIO.cpp + * \ingroup freestyle + * \brief Functions to manage I/O for the stroke + * \author Stephane Grabli + * \date 03/02/2004 + */ + +#include "StrokeAdvancedIterators.h" + +#include "StrokeIO.h" + +ostream& operator<<(ostream& out, const StrokeAttribute& iStrokeAttribute) +{ + out << " StrokeAttribute" << endl; + out << " color : (" << iStrokeAttribute.getColorR() << "," << iStrokeAttribute.getColorG() + << "," << iStrokeAttribute.getColorB() << ")" << endl; + out << " alpha : " << iStrokeAttribute.getAlpha() << endl; + out << " thickness : " << iStrokeAttribute.getThicknessR() << ", " << iStrokeAttribute.getThicknessL() << endl; + out << " visible : " << iStrokeAttribute.isVisible() << endl; + return out; +} + +ostream& operator<<(ostream& out, const StrokeVertex& iStrokeVertex) +{ + out << " StrokeVertex" << endl; + out << " id : " << iStrokeVertex.getId() << endl; + out << " curvilinear length : " << iStrokeVertex.curvilinearAbscissa() << endl; + out << " 2d coordinates : (" << iStrokeVertex.getProjectedX() << "," << iStrokeVertex.getProjectedY() + << "," << iStrokeVertex.getProjectedZ() << ")" << endl; + out << " 3d coordinates : (" << iStrokeVertex.getX() << "," << iStrokeVertex.getY() + << "," << iStrokeVertex.getZ() << ")" << endl; + out << iStrokeVertex.attribute() << endl; + return out; +} + +ostream& operator<<(ostream& out, const Stroke& iStroke) +{ + out << "Stroke" << endl; + out << " id : " << iStroke.getId() << endl; + out << " length : " << iStroke.getLength2D() << endl; + out << " medium type : " << iStroke.getMediumType() << endl; + for(Stroke::const_vertex_iterator v = iStroke.vertices_begin(), vend = iStroke.vertices_end(); v != vend; ++v) { + out << *(*v) << endl; + } + return out; +} diff --git a/source/blender/freestyle/intern/stroke/StrokeIO.h b/source/blender/freestyle/intern/stroke/StrokeIO.h new file mode 100644 index 00000000000..789c9c92c93 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeIO.h @@ -0,0 +1,53 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_STROKE_IO_H__ +#define __FREESTYLE_STROKE_IO_H__ + +/** \file blender/freestyle/intern/stroke/StrokeIO.h + * \ingroup freestyle + * \brief Functions to manage I/O for the stroke + * \author Stephane Grabli + * \date 03/02/2004 + */ + +#include <iostream> + +#include "Stroke.h" + +#include "../system/FreestyleConfig.h" + +LIB_STROKE_EXPORT +ostream& operator<<(ostream& out, const StrokeAttribute& iStrokeAttribute); + +LIB_STROKE_EXPORT +ostream& operator<<(ostream& out, const StrokeVertex& iStrokeVertex); + +LIB_STROKE_EXPORT +ostream& operator<<(ostream& out, const Stroke& iStroke); + +#endif // __FREESTYLE_STROKE_IO_H__ diff --git a/source/blender/freestyle/intern/stroke/StrokeIterators.h b/source/blender/freestyle/intern/stroke/StrokeIterators.h new file mode 100644 index 00000000000..37f6a8843bd --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeIterators.h @@ -0,0 +1,229 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_STROKE_ITERATORS_H__ +#define __FREESTYLE_STROKE_ITERATORS_H__ + +/** \file blender/freestyle/intern/stroke/StrokeIterators.h + * \ingroup freestyle + * \brief Iterators used to iterate over the elements of the Stroke + * \author Stephane Grabli + * \date 01/07/2003 + */ + +#include "Stroke.h" + +namespace StrokeInternal { + +// +// StrokeVertexIterator +// +///////////////////////////////////////////////// + +/*! Class defining an iterator designed to iterate over the StrokeVertex of a Stroke. + * An instance of a StrokeVertexIterator can only be obtained from a Stroke by calling strokeVerticesBegin() or + * strokeVerticesEnd(). + * It is iterating over the same vertices as an Interface0DIterator. + * The difference resides in the object access. Indeed, an Interface0DIterator allows only an access to an + * Interface0D whereas we could need to access the specialized StrokeVertex type. In this case, one + * should use a StrokeVertexIterator. + * The castToInterface0DIterator() method is useful to get an Interface0DIterator from a StrokeVertexIterator in + * order to call any functions of the type UnaryFunction0D. + * \attention In the scripting language, you must call \code it2 = StrokeVertexIterator(it1) \endcode instead of + * \code it2 = it1 \endcode where \a it1 and \a it2 are 2 StrokeVertexIterator. + * Otherwise, incrementing \a it1 will also increment \a it2. + */ +class StrokeVertexIterator : public Interface0DIteratorNested +{ +public: + /*! Default constructor. */ + StrokeVertexIterator() {} + + /*! Copy constructor. */ + StrokeVertexIterator(const StrokeVertexIterator& vi) + { + _it = vi._it; + _begin = vi._begin; + _end = vi._end; + } + + StrokeVertexIterator(const ::Stroke::vertex_container::iterator& it, + const ::Stroke::vertex_container::iterator& begin, + const ::Stroke::vertex_container::iterator& end) + { + _it = it; + _begin = begin; + _end = end; + } + + virtual ~StrokeVertexIterator() {} + + /*! Casts this StrokeVertexIterator into an Interface0DIterator. + * Useful for any call to a function of the type UnaryFunction0D. + */ + inline Interface0DIterator castToInterface0DIterator() const + { + Interface0DIterator ret(new StrokeVertexIterator(*this)); + return ret; + } + + /*! operator= + * \attention In the scripting language, you must call \code it2 = StrokeVertexIterator(it1) \endcode instead of + * \code it2 = it1 \endcode, where \a it1 and \a it2 are 2 StrokeVertexIterator. + * Otherwise, incrementing \a it1 will also increment \a it2. + */ + StrokeVertexIterator& operator=(const StrokeVertexIterator& vi) + { + _it = vi._it; + _begin = vi._begin; + _end = vi._end; + return *this; + } + + /*! Returns the string "StrokeVertexIterator". */ + virtual string getExactTypeName() const + { + return "StrokeVertexIterator"; + } + + /*! Returns a reference to the pointed StrokeVertex. + * In the scripting language, you must call "getObject()"instead. + */ + virtual StrokeVertex& operator*() + { + return **_it; + } + + /*! Returns a pointer to the pointed StrokeVertex. + * Can't be called in the scripting language. + */ + virtual StrokeVertex* operator->() + { + return &(operator*()); + } + + /*! Increments. In the scripting language, call "increment()". */ + virtual StrokeVertexIterator& operator++() + { + increment(); + return *this; + } + + /*! Increments. In the scripting language, call "increment()". */ + virtual StrokeVertexIterator operator++(int) + { + StrokeVertexIterator ret(*this); + increment(); + return ret; + } + + /*! Decrements. In the scripting language, call "decrement()". */ + virtual StrokeVertexIterator& operator--() + { + decrement(); + return *this; + } + + /*! Decrements. In the scripting language, call "decrement()". */ + virtual StrokeVertexIterator operator--(int) + { + StrokeVertexIterator ret(*this); + decrement(); + return ret; + } + + /*! Increments. */ + virtual int increment() + { + ++_it; + return 0; + } + + /*! Decrements. */ + virtual int decrement() + { + --_it; + return 0; + } + + /*! Returns true if the pointed StrokeVertex is the first of the Stroke. */ + bool isBegin() const + { + return _it == _begin; + } + + /*! Returns true if the pointed StrokeVertex is after the last StrokeVertex of the Stroke. */ + bool isEnd() const + { + return _it == _end; + } + + /*! operator == */ + virtual bool operator==(const Interface0DIteratorNested& it) const + { + const StrokeVertexIterator* it_exact = dynamic_cast<const StrokeVertexIterator*>(&it); + if (!it_exact) + return false; + return (_it == it_exact->_it); + } + + /*! Returns the curvilinear abscissa of the current point */ + virtual float t() const + { + return (*_it)->curvilinearAbscissa(); + } + + /*! Returns the point's parameter in the stroke */ + virtual float u() const + { + return (*_it)->u(); + } + + /*! Cloning method */ + virtual StrokeVertexIterator* copy() const + { + return new StrokeVertexIterator(*this); + } + + // + // Not exported in Python + // + ////////////////////////////////////////////////// + const ::Stroke::vertex_container::iterator& getIt() + { + return _it; + } + +private: + ::Stroke::vertex_container::iterator _it; + ::Stroke::vertex_container::iterator _begin; + ::Stroke::vertex_container::iterator _end; +}; + +} // end of namespace StrokeInternal + +#endif // __FREESTYLE_STROKE_ITERATORS_H__ diff --git a/source/blender/freestyle/intern/stroke/StrokeLayer.cpp b/source/blender/freestyle/intern/stroke/StrokeLayer.cpp new file mode 100644 index 00000000000..b12477c3e34 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeLayer.cpp @@ -0,0 +1,70 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/freestyle/intern/stroke/StrokeLayer.cpp + * \ingroup freestyle + * \brief Class to define a layer of strokes. + * \author Stephane Grabli + * \date 18/12/2002 + */ + +#include "Canvas.h" +#include "Stroke.h" +#include "StrokeLayer.h" + +StrokeLayer::~StrokeLayer() +{ + clear(); +} + +void StrokeLayer::ScaleThickness(float iFactor) +{ + for (StrokeLayer::stroke_container::iterator s = _strokes.begin(), send = _strokes.end(); s != send; ++s) { + (*s)->ScaleThickness(iFactor); + } +} + +void StrokeLayer::Render(const StrokeRenderer *iRenderer) +{ + for (StrokeLayer::stroke_container::iterator s = _strokes.begin(), send = _strokes.end(); s != send; ++s) { + (*s)->Render(iRenderer); + } +} + +void StrokeLayer::RenderBasic(const StrokeRenderer *iRenderer) +{ + for (StrokeLayer::stroke_container::iterator s = _strokes.begin(), send = _strokes.end(); s != send; ++s) { + (*s)->RenderBasic(iRenderer); + } +} + +void StrokeLayer::clear() +{ + for (stroke_container::iterator s = _strokes.begin(), send = _strokes.end(); s != send; ++s) + delete *s; + _strokes.clear(); +} diff --git a/source/blender/freestyle/intern/stroke/StrokeLayer.h b/source/blender/freestyle/intern/stroke/StrokeLayer.h new file mode 100644 index 00000000000..ff830db337a --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeLayer.h @@ -0,0 +1,106 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_STROKE_LAYER_H__ +#define __FREESTYLE_STROKE_LAYER_H__ + +/** \file blender/freestyle/intern/stroke/StrokeLayer.h + * \ingroup freestyle + * \brief Class to define a layer of strokes. + * \author Stephane Grabli + * \date 18/12/2002 + */ + +#include <deque> + +class Stroke; +class StrokeRenderer; +class StrokeLayer +{ +public: + typedef std::deque<Stroke*> stroke_container; + +protected: + stroke_container _strokes; + +public: + StrokeLayer() {} + + StrokeLayer(const stroke_container& iStrokes) + { + _strokes = iStrokes; + } + + StrokeLayer(const StrokeLayer& iBrother) + { + _strokes = iBrother._strokes; + } + + virtual ~StrokeLayer(); + + /*! Render method */ + void ScaleThickness(float iFactor); + void Render(const StrokeRenderer *iRenderer); + void RenderBasic(const StrokeRenderer *iRenderer); + + /*! clears the layer */ + void clear(); + + /*! accessors */ + inline stroke_container::iterator strokes_begin() + { + return _strokes.begin(); + } + + inline stroke_container::iterator strokes_end() + { + return _strokes.end(); + } + + inline int strokes_size() const + { + return _strokes.size(); + } + + inline bool empty() const + { + return _strokes.empty(); + } + + /*! modifiers */ + inline void setStrokes(stroke_container& iStrokes) + { + _strokes = iStrokes; + } + + inline void AddStroke(Stroke *iStroke) + { + _strokes.push_back(iStroke); + } +}; + +#endif // __FREESTYLE_STROKE_LAYER_H__ diff --git a/source/blender/freestyle/intern/stroke/StrokeRenderer.cpp b/source/blender/freestyle/intern/stroke/StrokeRenderer.cpp new file mode 100644 index 00000000000..56c5f2b5001 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeRenderer.cpp @@ -0,0 +1,138 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/freestyle/intern/stroke/StrokeRenderer.cpp + * \ingroup freestyle + * \brief Classes to render a stroke with OpenGL + * \author Fredo Durand + * \date 09/09/2002 + */ + +#include "StrokeRenderer.h" + +#include "../geometry/GeomUtils.h" + +using namespace std; + +/**********************************/ +/* */ +/* */ +/* StrokeRenderer */ +/* */ +/* */ +/**********************************/ + +LIB_STROKE_EXPORT +TextureManager *StrokeRenderer::_textureManager = 0; + +StrokeRenderer::StrokeRenderer() {} + +StrokeRenderer::~StrokeRenderer() {} + +bool StrokeRenderer::loadTextures() +{ + _textureManager->load(); + return true; +} + + +/**********************************/ +/* */ +/* */ +/* TextureManager */ +/* */ +/* */ +/**********************************/ + + +LIB_STROKE_EXPORT +TextureManager* TextureManager::_pInstance = 0; + +LIB_STROKE_EXPORT +string TextureManager::_patterns_path; + +LIB_STROKE_EXPORT +string TextureManager::_brushes_path; + +TextureManager::TextureManager() +{ + _hasLoadedTextures = false; + _pInstance = this; + _defaultTextureId = 0; +} + +TextureManager::~TextureManager() +{ + if (!_brushesMap.empty()) + _brushesMap.clear(); + _pInstance = 0; +} + +void TextureManager::load() +{ + if (_hasLoadedTextures) + return; + loadStandardBrushes(); + _hasLoadedTextures = true; +} + +unsigned TextureManager::getBrushTextureIndex(string name, Stroke::MediumType loadingMode) +{ + BrushTexture bt(name,loadingMode); + brushesMap::iterator b = _brushesMap.find(bt); + if (b == _brushesMap.end()) { + unsigned texId = loadBrush(name, loadingMode); + _brushesMap[bt] = texId; + return texId; + // XXX! + cerr << "brush file " << name << " not found" << endl; + return 0; + } + else { + return _brushesMap[bt]; + } +} + +void TextureManager::Options::setPatternsPath(const string& path) +{ + _patterns_path = path; +} + +string TextureManager::Options::getPatternsPath() +{ + return _patterns_path; +} + +void TextureManager::Options::setBrushesPath(const string& path) +{ + _brushes_path = path; +} + +string TextureManager::Options::getBrushesPath() +{ + return _brushes_path; +} diff --git a/source/blender/freestyle/intern/stroke/StrokeRenderer.h b/source/blender/freestyle/intern/stroke/StrokeRenderer.h new file mode 100644 index 00000000000..ab451a14496 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeRenderer.h @@ -0,0 +1,145 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_STROKE_RENDERER_H__ +#define __FREESTYLE_STROKE_RENDERER_H__ + +/** \file blender/freestyle/intern/stroke/StrokeRenderer.h + * \ingroup freestyle + * \brief Classes to render a stroke with OpenGL + * \author Fredo Durand + * \date 09/09/2002 + */ + +#include <map> +#include <string.h> +#include <utility> +#include <vector> + +#include "Stroke.h" +#include "StrokeRep.h" + +#include "../system/FreestyleConfig.h" + +/**********************************/ +/* */ +/* */ +/* TextureManager */ +/* */ +/* */ +/**********************************/ + + +/*! Class to load textures */ +class LIB_STROKE_EXPORT TextureManager +{ +public: + TextureManager (); + virtual ~TextureManager (); + + static TextureManager *getInstance() + { + return _pInstance; + } + + void load(); + unsigned getBrushTextureIndex(string name, Stroke::MediumType iType = Stroke::OPAQUE_MEDIUM); + + inline bool hasLoaded() const + { + return _hasLoadedTextures; + } + + inline unsigned int getDefaultTextureId() const + { + return _defaultTextureId; + } + + struct LIB_STROKE_EXPORT Options + { + static void setPatternsPath(const string& path); + static string getPatternsPath(); + + static void setBrushesPath(const string& path); + static string getBrushesPath(); + }; + +protected: + virtual void loadStandardBrushes() = 0; + virtual unsigned loadBrush(string fileName, Stroke::MediumType = Stroke::OPAQUE_MEDIUM) = 0; + + typedef std::pair<string,Stroke::MediumType> BrushTexture; + struct cmpBrushTexture + { + bool operator()(const BrushTexture& bt1, const BrushTexture& bt2) const + { + int r = strcmp(bt1.first.c_str(), bt2.first.c_str()); + if (r != 0) + return (r < 0); + else + return (bt1.second < bt2.second); + } + }; + typedef std::map<BrushTexture, unsigned, cmpBrushTexture> brushesMap; + + static TextureManager *_pInstance; + bool _hasLoadedTextures; + brushesMap _brushesMap; + static string _patterns_path; + static string _brushes_path; + unsigned int _defaultTextureId; +}; + + +/**********************************/ +/* */ +/* */ +/* StrokeRenderer */ +/* */ +/* */ +/**********************************/ + +/*! Class to render a stroke. Creates a triangle strip and stores it strip is lazily created at the first rendering */ +class LIB_STROKE_EXPORT StrokeRenderer +{ +public: + StrokeRenderer(); + virtual ~StrokeRenderer(); + + /*! Renders a stroke rep */ + virtual void RenderStrokeRep(StrokeRep *iStrokeRep) const = 0; + virtual void RenderStrokeRepBasic(StrokeRep *iStrokeRep) const = 0; + + // initializes the texture manager + // lazy, checks if it has already been done + static bool loadTextures(); + + //static unsigned int getTextureIndex(unsigned int index); + static TextureManager *_textureManager; +}; + +#endif // __FREESTYLE_STROKE_RENDERER_H__ diff --git a/source/blender/freestyle/intern/stroke/StrokeRep.cpp b/source/blender/freestyle/intern/stroke/StrokeRep.cpp new file mode 100644 index 00000000000..bc117925a4c --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeRep.cpp @@ -0,0 +1,789 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/freestyle/intern/stroke/StrokeRep.cpp + * \ingroup freestyle + * \brief Class to define the representation of a stroke (for display purpose) + * \author Stephane Grabli + * \date 05/03/2003 + */ + +#include "Stroke.h" +#include "StrokeAdvancedIterators.h" +#include "StrokeIterators.h" +#include "StrokeRenderer.h" +#include "StrokeRep.h" + +using namespace std; + +// +// STROKE VERTEX REP +///////////////////////////////////// + +StrokeVertexRep::StrokeVertexRep(const StrokeVertexRep& iBrother) +{ + _point2d = iBrother._point2d; + _texCoord = iBrother._texCoord; + _color = iBrother._color; + _alpha = iBrother._alpha; +} + +// +// STRIP +///////////////////////////////////// + +Strip::Strip(const vector<StrokeVertex*>& iStrokeVertices, bool hasTips, bool beginTip, bool endTip) +{ + createStrip(iStrokeVertices); + if (!hasTips) + computeTexCoord (iStrokeVertices); + else + computeTexCoordWithTips (iStrokeVertices, beginTip, endTip); +} + +Strip::Strip(const Strip& iBrother) +{ + if (!iBrother._vertices.empty()) { + for (vertex_container::const_iterator v = iBrother._vertices.begin(), vend = iBrother._vertices.end(); + v != vend; + ++v) + { + _vertices.push_back(new StrokeVertexRep(**v)); + } + } + _averageThickness = iBrother._averageThickness; +} + +Strip::~Strip() +{ + if (!_vertices.empty()) { + for (vertex_container::iterator v = _vertices.begin(), vend = _vertices.end(); v != vend; ++v) { + delete (*v); + } + _vertices.clear(); + } +} + +////////////////////////// +// Strip creation +////////////////////////// + +#define EPS_SINGULARITY_RENDERER 0.05 +#define ZERO 0.00001 +#define MAX_RATIO_LENGTH_SINGU 2 +#define HUGE_COORD 1.0e4 + +static bool notValid (Vec2r p) +{ + return (p[0] != p[0]) || (p[1] != p[1]) || (fabs(p[0]) > HUGE_COORD) || (fabs(p[1]) > HUGE_COORD) || + (p[0] < -HUGE_COORD) || (p[1] < -HUGE_COORD); +} + +static real crossP(const Vec2r& A, const Vec2r& B) +{ + return A[0] * B[1] - A[1] * B[0]; +} + +void Strip::createStrip (const vector<StrokeVertex*>& iStrokeVertices) +{ + //computeParameterization(); + if (iStrokeVertices.size() < 2) { + cerr << "Warning: strip has less than 2 vertices" << endl; + return; + } + _vertices.reserve(2 * iStrokeVertices.size()); + if (!_vertices.empty()) { + for (vertex_container::iterator v = _vertices.begin(), vend = _vertices.end(); v != vend; ++v) { + delete (*v); + } + _vertices.clear(); + } + _averageThickness = 0.0; + + vector<StrokeVertex*>::const_iterator v ,vend, v2, vPrev; + StrokeVertex *sv, *sv2, *svPrev; + + //special case of first vertex + v = iStrokeVertices.begin(); + sv = *v; + vPrev = v; //in case the stroke has only 2 vertices; + ++v; + sv2 = *v; + Vec2r dir(sv2->getPoint() - sv->getPoint()); + Vec2r orthDir(-dir[1], dir[0]); + if (orthDir.norm() > ZERO) + orthDir.normalize(); + Vec2r stripDir(orthDir); + // check whether the orientation was user defined + if (sv->attribute().isAttributeAvailableVec2f("orientation")) { + Vec2r userDir = sv->attribute().getAttributeVec2f("orientation"); + userDir.normalize(); + real dp = userDir * orthDir; + if (dp < 0) + userDir = userDir * (-1.0f); + stripDir = userDir; + } + const float *thickness = sv->attribute().getThickness(); + _vertices.push_back(new StrokeVertexRep(sv->getPoint() + thickness[1] * stripDir)); + _vertices.push_back(new StrokeVertexRep(sv->getPoint() - thickness[0] * stripDir)); + +#if 0 + Vec2r userDir = _stroke->getBeginningOrientation(); + if (userDir != Vec2r(0, 0)) { + userDir.normalize(); + real o1 = (orthDir * userDir); + real o2 = crossP(orthDir, userDir); + real orientation = o1 * o2; + if (orientation > 0) { + // then the vertex to move is v0 + if (o1 > 0) + _vertex[0] = _vertex[1] + userDir; + else + _vertex[0] = _vertex[1] - userDir; + } + if (orientation < 0) { + // then we must move v1 + if (o1 < 0) + _vertex[1] = _vertex[0] + userDir; + else + _vertex[1] = _vertex[0] - userDir; + } + } +#endif + + int i = 2; // 2 because we have already processed the first vertex + + for (vend = iStrokeVertices.end(); v != vend; ++v) { + v2 = v; + ++v2; + if (v2 == vend) + break; + sv = (*v); + sv2 = (*v2); + svPrev = (*vPrev); + Vec2r p(sv->getPoint()), p2(sv2->getPoint()), pPrev(svPrev->getPoint()); + + //direction and orthogonal vector to the next segment + Vec2r dir(p2 - p); + float dirNorm = dir.norm(); + dir.normalize(); + Vec2r orthDir(-dir[1], dir[0]); + Vec2r stripDir = orthDir; + if (sv->attribute().isAttributeAvailableVec2f("orientation")) { + Vec2r userDir = sv->attribute().getAttributeVec2f("orientation"); + userDir.normalize(); + real dp = userDir * orthDir; + if (dp < 0) + userDir = userDir * (-1.0f); + stripDir = userDir; + } + + //direction and orthogonal vector to the previous segment + Vec2r dirPrev(p - pPrev); + float dirPrevNorm = dirPrev.norm(); + dirPrev.normalize(); + Vec2r orthDirPrev(-dirPrev[1], dirPrev[0]); + Vec2r stripDirPrev = orthDirPrev; + if (svPrev->attribute().isAttributeAvailableVec2f("orientation")) { + Vec2r userDir = svPrev->attribute().getAttributeVec2f("orientation"); + userDir.normalize(); + real dp = userDir * orthDir; + if (dp < 0) + userDir = userDir * (-1.0f); + stripDirPrev = userDir; + } + + const float *thickness = sv->attribute().getThickness(); + _averageThickness += thickness[0] + thickness[1]; + Vec2r pInter; + int interResult; + + interResult = GeomUtils::intersect2dLine2dLine(Vec2r(pPrev + thickness[1] * stripDirPrev), + Vec2r(p + thickness[1] * stripDirPrev), + Vec2r(p + thickness[1] * stripDir), + Vec2r(p2 + thickness[1] * stripDir), + pInter); + if (interResult == GeomUtils::DO_INTERSECT) + _vertices.push_back(new StrokeVertexRep(pInter)); + else + _vertices.push_back(new StrokeVertexRep(p + thickness[1] * stripDir)); + ++i; + + interResult = GeomUtils::intersect2dLine2dLine(Vec2r(pPrev - thickness[0] * stripDirPrev), + Vec2r(p - thickness[0] * stripDirPrev), + Vec2r(p - thickness[0] * stripDir), + Vec2r(p2 - thickness[0] * stripDir), + pInter); + if (interResult == GeomUtils::DO_INTERSECT) + _vertices.push_back(new StrokeVertexRep(pInter)); + else + _vertices.push_back(new StrokeVertexRep(p - thickness[0] * stripDir)); + ++i; + + // if the angle is obtuse, we simply average the directions to avoid the singularity + stripDir = stripDir + stripDirPrev; + if ((dirNorm < ZERO) || (dirPrevNorm < ZERO) || (stripDir.norm() < ZERO)) { + stripDir[0] = 0; + stripDir[1] = 0; + } + else { + stripDir.normalize(); + } + + Vec2r vec_tmp(_vertices[i - 2]->point2d() - p); + if ((vec_tmp.norm() > thickness[1] * MAX_RATIO_LENGTH_SINGU) || (dirNorm < ZERO) || (dirPrevNorm < ZERO) || + notValid(_vertices[i - 2]->point2d()) || (fabs(stripDir * dir) < EPS_SINGULARITY_RENDERER)) + { + _vertices[i - 2]->setPoint2d(p + thickness[1] * stripDir); + } + + vec_tmp = _vertices[i - 1]->point2d() - p; + if ((vec_tmp.norm() > thickness[0] * MAX_RATIO_LENGTH_SINGU) || (dirNorm < ZERO) || (dirPrevNorm < ZERO) || + notValid(_vertices[i - 1]->point2d()) || (fabs(stripDir * dir) < EPS_SINGULARITY_RENDERER)) + { + _vertices[i - 1]->setPoint2d(p - thickness[0] * stripDir); + } + + vPrev = v; + } // end of for + + //special case of last vertex + sv = *v; + sv2 = *vPrev; + dir = Vec2r(sv->getPoint() - sv2->getPoint()); + orthDir = Vec2r(-dir[1], dir[0]); + if (orthDir.norm() > ZERO) + orthDir.normalize(); + Vec2r stripDirLast(orthDir); + // check whether the orientation was user defined + if (sv->attribute().isAttributeAvailableVec2f("orientation")) { + Vec2r userDir = sv->attribute().getAttributeVec2f("orientation"); + userDir.normalize(); + real dp = userDir * orthDir; + if (dp < 0) + userDir = userDir * (-1.0f); + stripDirLast = userDir; + } + const float *thicknessLast = sv->attribute().getThickness(); + _vertices.push_back(new StrokeVertexRep(sv->getPoint() + thicknessLast[1] * stripDirLast)); + ++i; + _vertices.push_back(new StrokeVertexRep(sv->getPoint() - thicknessLast[0] * stripDirLast)); + ++i; + +#if 0 + int n = i - 1; + // check whether the orientation of the extremity was user defined + userDir = _stroke->getEndingOrientation(); + if (userDir != Vec2r(0, 0)) { + userDir.normalize(); + real o1 = (orthDir * userDir); + real o2 = crossP(orthDir, userDir); + real orientation = o1 * o2; + if (orientation > 0) { + // then the vertex to move is vn + if (o1 < 0) + _vertex[n] = _vertex[n - 1] + userDir; + else + _vertex[n] = _vertex[n - 1] - userDir; + } + if (orientation < 0) { + // then we must move vn-1 + if (o1 > 0) + _vertex[n - 1] = _vertex[n] + userDir; + else + _vertex[n - 1] = _vertex[n] - userDir; + } + } +#endif + + _averageThickness /= float(iStrokeVertices.size() - 2); + //I did not use the first and last vertex for the average + if (iStrokeVertices.size() < 3) + _averageThickness = 0.5 * (thicknessLast[1] + thicknessLast[0] + thickness[0] + thickness[1]); + + if (i != 2 * (int)iStrokeVertices.size()) + cerr << "Warning: problem with stripe size\n"; + + cleanUpSingularities (iStrokeVertices); +} + +// CLEAN UP +///////////////////////// + +void Strip::cleanUpSingularities (const vector<StrokeVertex*>& iStrokeVertices) +{ + int k; + int sizeStrip = _vertices.size(); + + for (k = 0; k < sizeStrip; k++) { + if (notValid(_vertices[k]->point2d())) { + cerr << "Warning: strip vertex " << k << " non valid" << endl; + return; + } + } + + //return; + if (iStrokeVertices.size() < 2) + return; + int i = 0, j; + vector<StrokeVertex*>::const_iterator v ,vend, v2, vPrev; + StrokeVertex *sv, *sv2; //soc unused - *svPrev; + + bool singu1 = false, singu2 = false; + int timeSinceSingu1 = 0, timeSinceSingu2 = 0; + + //special case of first vertex + v = iStrokeVertices.begin(); + for (vend = iStrokeVertices.end(); v != vend; v++) { + v2 = v; + ++v2; + if (v2 == vend) + break; + sv = (*v); + sv2 = (*v2); + Vec2r p(sv->getPoint()), p2(sv2->getPoint()); + + Vec2r dir(p2-p); + if (dir.norm() > ZERO) + dir.normalize(); + Vec2r dir1, dir2; + dir1 = _vertices[2 * i + 2]->point2d() - _vertices[2 * i]->point2d(); + dir2 = _vertices[2 * i + 3]->point2d() - _vertices[2 * i + 1]->point2d(); + + if ((dir1 * dir) < -ZERO) { + singu1 = true; + timeSinceSingu1++; + } + else { + if (singu1) { + int toto = i - timeSinceSingu1; + if (toto < 0) + cerr << "Stephane dit \"Toto\"" << endl; + //traverse all the vertices of the singularity and average them + Vec2r avP(0.0, 0.0); + for (j = i - timeSinceSingu1; j < i + 1; j++) + avP=Vec2r(avP + _vertices[2 * j]->point2d()); + avP = Vec2r( 1.0 / float(timeSinceSingu1 + 1) * avP); + for (j = i - timeSinceSingu1; j < i + 1; j++) + _vertices[2 * j]->setPoint2d(avP); + //_vertex[2 * j] = _vertex[2 * i]; + singu1 = false; + timeSinceSingu1 = 0; + } + } + if ((dir2 * dir) < -ZERO) { + singu2 = true; + timeSinceSingu2++; + } + else { + if (singu2) { + int toto = i - timeSinceSingu2; + if (toto < 0) + cerr << "Stephane dit \"Toto\"" << endl; + //traverse all the vertices of the singularity and average them + Vec2r avP(0.0, 0.0); + for (j = i - timeSinceSingu2; j < i + 1; j++) + avP = Vec2r(avP + _vertices[2 * j + 1]->point2d()); + avP = Vec2r(1.0 / float(timeSinceSingu2 + 1) * avP); + for (j = i - timeSinceSingu2; j < i + 1; j++) + _vertices[2 * j + 1]->setPoint2d(avP); + //_vertex[2 * j + 1] = _vertex[2 * i + 1]; + singu2 = false; + timeSinceSingu2 = 0; + } + } + i++; + } + + if (singu1) { + //traverse all the vertices of the singularity and average them + Vec2r avP(0.0, 0.0); + for (j = i - timeSinceSingu1; j < i; j++) + avP = Vec2r(avP + _vertices[2 * j]->point2d()); + avP = Vec2r(1.0 / float(timeSinceSingu1) * avP); + for (j = i - timeSinceSingu1; j < i; j++) + _vertices[2 * j]->setPoint2d(avP); + } + if (singu2) { + //traverse all the vertices of the singularity and average them + Vec2r avP(0.0, 0.0); + for (j = i - timeSinceSingu2; j < i; j++) + avP = Vec2r(avP + _vertices[2 * j + 1]->point2d()); + avP = Vec2r(1.0 / float(timeSinceSingu2) * avP); + for (j = i - timeSinceSingu2; j < i; j++) + _vertices[2 * j + 1]->setPoint2d(avP); + } + + for (k=0; k<sizeStrip; k++) { + if (notValid(_vertices[k]->point2d())) { + cerr << "Warning: strip vertex " << k << " non valid after cleanup" << endl; + return; + } + } +} + + +// Texture coordinates +//////////////////////////////// + +void Strip::computeTexCoord (const vector<StrokeVertex*>& iStrokeVertices) +{ + vector<StrokeVertex*>::const_iterator v ,vend; + StrokeVertex *sv; + int i = 0; + for (v = iStrokeVertices.begin(), vend = iStrokeVertices.end(); v != vend; v++) { + sv = (*v); + _vertices[i]->setTexCoord(Vec2r((real)(sv->curvilinearAbscissa() / _averageThickness), 0)); + _vertices[i]->setColor(Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1], + sv->attribute().getColor()[2])); + _vertices[i]->setAlpha(sv->attribute().getAlpha()); + i++; + _vertices[i]->setTexCoord(Vec2r((real)(sv->curvilinearAbscissa() / _averageThickness), 1)); + _vertices[i]->setColor(Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1], + sv->attribute().getColor()[2])); + _vertices[i]->setAlpha(sv->attribute().getAlpha()); + i++; +#if 0 + cerr << "col=("<<sv->attribute().getColor()[0] << ", " + << sv->attribute().getColor()[1] << ", " << sv->attribute().getColor()[2] << ")" << endl; +#endif + } +} + +void Strip::computeTexCoordWithTips (const vector<StrokeVertex*>& iStrokeVertices, bool tipBegin, bool tipEnd) +{ + //soc unused - unsigned int sizeStrip = _vertices.size() + 8; //for the transition between the tip and the body + vector<StrokeVertex*>::const_iterator v, vend; + StrokeVertex *sv = NULL; + + v = iStrokeVertices.begin(); + vend = iStrokeVertices.end(); + float l = (*v)->strokeLength() / _averageThickness; + int tiles = int(l); + float fact = (float(tiles) + 0.5) / l; + //soc unused - float uTip2 = float(tiles) + 0.25; + float u = 0; + float uPrev = 0; + int i = 0; + float t; + StrokeVertexRep *tvRep1, *tvRep2; + +#if 0 + cerr << "l=" << l << " tiles=" << tiles << " _averageThicnkess=" + << _averageThickness << " strokeLength=" << (*v)->strokeLength() << endl; +#endif + + vector<StrokeVertexRep*>::iterator currentSV = _vertices.begin(); + StrokeVertexRep *svRep; + if (tipBegin) { + for (; v != vend; v++) { + sv = (*v); + svRep = *currentSV; + u = sv->curvilinearAbscissa() / _averageThickness * fact; + if (u > 0.25) + break; + + svRep->setTexCoord(Vec2r((real)u, 0.5)); + svRep->setColor(Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1], + sv->attribute().getColor()[2])); + svRep->setAlpha(sv->attribute().getAlpha()); + i++; + ++currentSV; + + svRep = *currentSV; + svRep->setTexCoord(Vec2r((real)u, 1)); + svRep->setColor(Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1], + sv->attribute().getColor()[2])); + svRep->setAlpha(sv->attribute().getAlpha()); + i++; + ++currentSV; + uPrev = u; + } + //first transition vertex + + if (fabs(u - uPrev) > ZERO) + t = (0.25 - uPrev) / (u - uPrev); + else + t = 0; +#if 0 + if (!tiles) + t = 0.5; +#endif + tvRep1 = new StrokeVertexRep(Vec2r((1 - t) * _vertices[i - 2]->point2d() + t * _vertices[i]->point2d())); + tvRep1->setTexCoord(Vec2r(0.25, 0.5)); + tvRep1->setColor(Vec3r((1 - t) * _vertices[i - 2]->color() + + t * Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1], + sv->attribute().getColor()[2]))); + tvRep1->setAlpha((1 - t) * _vertices[i - 2]->alpha() + t * sv->attribute().getAlpha()); + i++; + + tvRep2 = new StrokeVertexRep(Vec2r((1 - t) * _vertices[i - 2]->point2d() + t *_vertices[i]->point2d())); + tvRep2->setTexCoord(Vec2r(0.25, 1)); + tvRep2->setColor(Vec3r((1 - t) * _vertices[i - 2]->color() + + t * Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1], + sv->attribute().getColor()[2]))); + tvRep2->setAlpha((1 - t) * _vertices[i - 2]->alpha() + t * sv->attribute().getAlpha()); + i++; + + currentSV = _vertices.insert(currentSV, tvRep1); + ++currentSV; + currentSV = _vertices.insert(currentSV, tvRep2); + ++currentSV; + + // copy the vertices with different texture coordinates + tvRep1 = new StrokeVertexRep(_vertices[i - 2]->point2d()); + tvRep1->setTexCoord(Vec2r(0.25, 0)); + tvRep1->setColor(_vertices[i - 2]->color()); + tvRep1->setAlpha(_vertices[i - 2]->alpha()); + i++; + + tvRep2 = new StrokeVertexRep(_vertices[i - 2]->point2d()); + tvRep2->setTexCoord(Vec2r(0.25, 0.5)); + tvRep2->setColor(_vertices[i - 2]->color()); + tvRep2->setAlpha(_vertices[i - 2]->alpha()); + i++; + + currentSV = _vertices.insert(currentSV, tvRep1); + ++currentSV; + currentSV = _vertices.insert(currentSV, tvRep2); + ++currentSV; + } + uPrev = 0; + + //body of the stroke + for (; v != vend; v++) { + sv = (*v); + svRep = *currentSV; + u = sv->curvilinearAbscissa() / _averageThickness * fact - 0.25; + if (u > tiles) + break; + + svRep->setTexCoord(Vec2r((real)u, 0)); + svRep->setColor(Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1], + sv->attribute().getColor()[2])); + svRep->setAlpha(sv->attribute().getAlpha()); + i++; + ++currentSV; + + svRep = *currentSV; + svRep->setTexCoord(Vec2r((real)u, 0.5)); + svRep->setColor(Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1], + sv->attribute().getColor()[2])); + svRep->setAlpha(sv->attribute().getAlpha()); + i++; + ++currentSV; + + uPrev = u; + } + if (tipEnd) { + //second transition vertex + if ((fabs(u - uPrev) > ZERO)) + t = (float(tiles) - uPrev) / (u - uPrev); + else + t = 0; + + tvRep1 = new StrokeVertexRep(Vec2r((1 - t) * _vertices[i - 2]->point2d() + t * _vertices[i]->point2d())); + tvRep1->setTexCoord(Vec2r((real)tiles, 0)); + tvRep1->setColor(Vec3r((1 - t) * _vertices[i - 2]->color() + + t * Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1], + sv->attribute().getColor()[2]))); + tvRep1->setAlpha((1 - t) * _vertices[i - 2]->alpha() + t * sv->attribute().getAlpha()); + i++; + + tvRep2 = new StrokeVertexRep(Vec2r((1 - t) * _vertices[i - 2]->point2d() + t * _vertices[i]->point2d())); + tvRep2->setTexCoord(Vec2r((real)tiles, 0.5)); + tvRep2->setColor(Vec3r((1 - t) * _vertices[i - 2]->color() + + t * Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1], + sv->attribute().getColor()[2]))); + tvRep2->setAlpha((1 - t) * _vertices[i - 2]->alpha() + t * sv->attribute().getAlpha()); + i++; + + currentSV = _vertices.insert(currentSV, tvRep1); + ++currentSV; + currentSV = _vertices.insert(currentSV, tvRep2); + ++currentSV; + + //copy the vertices with different texture coordinates + tvRep1 = new StrokeVertexRep(_vertices[i - 2]->point2d()); + tvRep1->setTexCoord(Vec2r(0.75, 0.5)); + tvRep1->setColor(_vertices[i - 2]->color()); + tvRep1->setAlpha(_vertices[i - 2]->alpha()); + i++; + + tvRep2 = new StrokeVertexRep(_vertices[i - 2]->point2d()); + tvRep2->setTexCoord(Vec2r(0.75, 1)); + tvRep2->setColor(_vertices[i - 2]->color()); + tvRep2->setAlpha(_vertices[i - 2]->alpha()); + i++; + + currentSV = _vertices.insert(currentSV, tvRep1); + ++currentSV; + currentSV = _vertices.insert(currentSV, tvRep2); + ++currentSV; + + //end tip + for (; v != vend; v++) { + sv = (*v); + svRep = *currentSV; + u = 0.75 + sv->curvilinearAbscissa() / _averageThickness * fact - float(tiles) - 0.25; + + svRep->setTexCoord(Vec2r((real)u, 0.5)); + svRep->setColor(Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1], + sv->attribute().getColor()[2])); + svRep->setAlpha(sv->attribute().getAlpha()); + i++; + ++currentSV; + + svRep = *currentSV; + svRep->setTexCoord(Vec2r((real)u, 1)); + svRep->setColor(Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1], + sv->attribute().getColor()[2])); + svRep->setAlpha(sv->attribute().getAlpha()); + i++; + ++currentSV; + } + } + +#if 0 + cerr << "u=" << u << " i=" << i << "/" << _sizeStrip << endl; + + for (i = 0; i < _sizeStrip; i++) + _alpha[i] = 1.0; + + for (i = 0; i < _sizeStrip; i++) + cerr << "(" << _texCoord[i][0] << ", " << _texCoord[i][1] << ") "; + cerr << endl; + + Vec2r vec_tmp; + for (i = 0; i < _sizeStrip / 2; i++) + vec_tmp = _vertex[2 * i] - _vertex[2 * i + 1]; + if (vec_tmp.norm() > 4 * _averageThickness) + cerr << "Warning (from Fredo): There is a pb in the texture coordinates computation" << endl; +#endif +} + +// +// StrokeRep +///////////////////////////////////// + +StrokeRep::StrokeRep() +{ + _stroke = 0; + _strokeType = Stroke::OPAQUE_MEDIUM; + TextureManager *ptm = TextureManager::getInstance(); + if (ptm) + _textureId = ptm->getDefaultTextureId(); +#if 0 + _averageTextureAlpha = 0.5; //default value + if (_strokeType == OIL_STROKE) + _averageTextureAlpha = 0.75; + if (_strokeType >= NO_BLEND_STROKE) + _averageTextureAlpha = 1.0 +#endif +} + +StrokeRep::StrokeRep(Stroke *iStroke) +{ + _stroke = iStroke; + _strokeType = iStroke->getMediumType(); + _textureId = iStroke->getTextureId(); + if (_textureId == 0) { + TextureManager *ptm = TextureManager::getInstance(); + if (ptm) + _textureId = ptm->getDefaultTextureId(); + } + +#if 0 + _averageTextureAlpha = 0.5; //default value + if (_strokeType == OIL_STROKE) + _averageTextureAlpha = 0.75; + if (_strokeType >= NO_BLEND_STROKE) + _averageTextureAlpha = 1.0; +#endif + create(); +} + +StrokeRep::StrokeRep(const StrokeRep& iBrother) +{ + //soc unused - int i = 0; + _stroke = iBrother._stroke; + _strokeType = iBrother._strokeType; + _textureId = iBrother._textureId; + for (vector<Strip*>::const_iterator s = iBrother._strips.begin(), send = iBrother._strips.end(); + s != send; + ++s) + { + _strips.push_back(new Strip(**s)); + } +} + +StrokeRep::~StrokeRep() +{ + if (!_strips.empty()) { + for (vector<Strip*>::iterator s = _strips.begin(), send = _strips.end(); s != send; ++s) { + delete (*s); + } + _strips.clear(); + } +} + +void StrokeRep::create() +{ + vector<StrokeVertex*> strip; + StrokeInternal::StrokeVertexIterator v = _stroke->strokeVerticesBegin(); + StrokeInternal::StrokeVertexIterator vend = _stroke->strokeVerticesEnd(); + + bool first = true; + bool end = false; + while (v != vend) { + while ((v != vend) && (!(*v).attribute().isVisible())) { + ++v; + first = false; + } + while ((v != vend) && ((*v).attribute().isVisible())) { + strip.push_back(&(*v)); + ++v; + } + if (v != vend) { + // add the last vertex and create + strip.push_back(&(*v)); + } + else { + end = true; + } + if ((!strip.empty()) && (strip.size() > 1)) { + _strips.push_back(new Strip(strip, _stroke->hasTips(), first, end)); + strip.clear(); + } + first = false; + } +} + +void StrokeRep::Render(const StrokeRenderer *iRenderer) +{ + iRenderer->RenderStrokeRep(this); +} diff --git a/source/blender/freestyle/intern/stroke/StrokeRep.h b/source/blender/freestyle/intern/stroke/StrokeRep.h new file mode 100644 index 00000000000..c8d84cb237a --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeRep.h @@ -0,0 +1,217 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_STROKE_REP_H__ +#define __FREESTYLE_STROKE_REP_H__ + +/** \file blender/freestyle/intern/stroke/StrokeRep.h + * \ingroup freestyle + * \brief Class to define the representation of a stroke (for display purpose) + * \author Stephane Grabli + * \date 05/03/2003 + */ + +#include "Stroke.h" + +#include "../geometry/Geom.h" + +using namespace Geometry; + +#if 0 +//symbolic constant to call the appropriate renderers and textures +#define NO_TEXTURE_WITH_BLEND_STROKE -2 +#define NO_TEXTURE_STROKE -1 +#define PSEUDO_CHARCOAL_STROKE 0 +#define WASH_BRUSH_STROKE 1 +#define OIL_STROKE 2 +#define NO_BLEND_STROKE 3 +#define CHARCOAL_MIN_STROKE 4 +#define BRUSH_MIN_STROKE 5 +#define OPAQUE_DRY_STROKE 6 +#define OPAQUE_STROKE 7 + +#define DEFAULT_STROKE 0 + +#define NUMBER_STROKE_RENDERER 8 + +#endif + +class StrokeVertexRep +{ +public: + StrokeVertexRep(){} + + StrokeVertexRep(const Vec2r& iPoint2d) + { + _point2d = iPoint2d; + } + + StrokeVertexRep(const StrokeVertexRep& iBrother); + + virtual ~StrokeVertexRep(){} + + inline Vec2r& point2d() + { + return _point2d; + } + + inline Vec2r& texCoord() + { + return _texCoord; + } + + inline Vec3r& color() + { + return _color; + } + + inline float alpha() + { + return _alpha; + } + + inline void setPoint2d(const Vec2r& p) + { + _point2d = p; + } + + inline void setTexCoord(const Vec2r& p) + { + _texCoord = p; + } + + inline void setColor(const Vec3r& p) + { + _color = p; + } + + inline void setAlpha(float a) + { + _alpha = a; + } + +protected: + Vec2r _point2d; + Vec2r _texCoord; + Vec3r _color; + float _alpha; +}; + +class Strip +{ +public: + typedef std::vector<StrokeVertexRep*> vertex_container; + +protected: + vertex_container _vertices; + float _averageThickness; + +public: + Strip(const std::vector<StrokeVertex*>& iStrokeVertices, bool hasTips = false, + bool tipBegin = false, bool tipEnd = false); + Strip(const Strip& iBrother); + virtual ~Strip(); + +protected: + void createStrip(const std::vector<StrokeVertex*>& iStrokeVertices); + void cleanUpSingularities(const std::vector<StrokeVertex*>& iStrokeVertices); + void computeTexCoord (const std::vector<StrokeVertex*>& iStrokeVertices); + void computeTexCoordWithTips (const std::vector<StrokeVertex*>& iStrokeVertices, bool tipBegin, bool tipEnd); + +public: + inline int sizeStrip() const + { + return _vertices.size(); + } + + inline vertex_container& vertices() + { + return _vertices; + } +}; + +class StrokeRep +{ +protected: + Stroke *_stroke; + vector<Strip*> _strips; + Stroke::MediumType _strokeType; + unsigned int _textureId; + + // float _averageTextureAlpha; + +public: + StrokeRep(); + StrokeRep(const StrokeRep&); + StrokeRep(Stroke *iStroke); + virtual ~StrokeRep(); + + /*! Creates the strips */ + virtual void create(); + + /*! Renders the stroke using a Renderer */ + virtual void Render(const StrokeRenderer *iRenderer); + + /*! accessors */ + inline Stroke::MediumType getMediumType() const + { + return _strokeType; + } + + inline unsigned getTextureId() const + { + return _textureId; + } + + inline vector<Strip*>& getStrips() + { + return _strips; + } + + inline unsigned int getNumberOfStrips() const + { + return _strips.size(); + } + + inline Stroke * getStroke() + { + return _stroke; + } + + /*! modifiers */ + inline void setMediumType(Stroke::MediumType itype) + { + _strokeType = itype; + } + + inline void setTextureId(unsigned textureId) + { + _textureId = textureId; + } +}; + +#endif // __FREESTYLE_STROKE_REP_H__ diff --git a/source/blender/freestyle/intern/stroke/StrokeShader.h b/source/blender/freestyle/intern/stroke/StrokeShader.h new file mode 100644 index 00000000000..c8364f7a737 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeShader.h @@ -0,0 +1,107 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_STROKE_SHADERS_H__ +#define __FREESTYLE_STROKE_SHADERS_H__ + +/** \file blender/freestyle/intern/stroke/StrokeShader.h + * \ingroup freestyle + * \brief Class defining StrokeShader + * \author Stephane Grabli + * \author Emmanuel Turquin + * \date 01/07/2003 + */ + +#include <iostream> +#include <vector> + +#include "../python/Director.h" + +// +// StrokeShader base class +// +////////////////////////////////////////////////////// + +class Stroke; + +/*! Base class for Stroke Shaders. + * Any Stroke Shader must inherit from this class and overload the shade() method. + * A StrokeShader is designed to modify any Stroke's attribute such as Thickness, Color, + * Geometry, Texture, Blending mode... + * The basic way to achieve this operation consists in iterating over the StrokeVertices of the Stroke + * and to modify each one's StrokeAttribute. + * Here is a python code example of such an iteration: + * \code + * it = ioStroke.strokeVerticesBegin() + * while not it.isEnd(): + * att = it.getObject().attribute() + * ## perform here any attribute modification + * it.increment() + * \endcode + * Here is a C++ code example of such an iteration: + * \code + * for(StrokeInternal::StrokeVertexIterator v = ioStroke.strokeVerticesBegin(), vend = ioStroke.strokeVerticesEnd(); + * v != vend; + * ++v) + * { + * StrokeAttribute& att = v->attribute(); + * // perform any attribute modification here... + * } + * \endcode + */ +class LIB_STROKE_EXPORT StrokeShader +{ +public: + PyObject *py_ss; + + /*! Default constructor. */ + StrokeShader() + { + py_ss = 0; + } + + /*! Destructor. */ + virtual ~StrokeShader() {} + + /*! Returns the string corresponding to the shader's name. */ + virtual string getName() const + { + return "StrokeShader"; + } + + /*! The shading method. This method must be overloaded by inherited classes. + * \param ioStroke + * The stroke we wish to shade. this Stroke is modified by the Shader (which typically + * modifies the Stroke's attribute's values such as Color, Thickness, Geometry...) + */ + virtual int shade(Stroke& ioStroke) const + { + return Director_BPy_StrokeShader_shade( const_cast<StrokeShader *>(this), ioStroke); + } +}; + +#endif // __FREESTYLE_STROKE_SHADERS_H__ diff --git a/source/blender/freestyle/intern/stroke/StrokeTesselator.cpp b/source/blender/freestyle/intern/stroke/StrokeTesselator.cpp new file mode 100644 index 00000000000..43cf1818fb0 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeTesselator.cpp @@ -0,0 +1,94 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/freestyle/intern/stroke/StrokeTesselator.cpp + * \ingroup freestyle + * \brief Class to build a Node Tree designed to be displayed from a set of strokes structure. + * \author Stephane Grabli + * \date 26/03/2002 + */ + +#include "StrokeAdvancedIterators.h" +#include "StrokeTesselator.h" + +#include "../scene_graph/OrientedLineRep.h" +#include "../scene_graph/NodeGroup.h" +#include "../scene_graph/NodeShape.h" + +LineRep *StrokeTesselator::Tesselate(Stroke *iStroke) +{ + if (0 == iStroke) + return 0; + + LineRep* line; + line = new OrientedLineRep(); + + Stroke::vertex_iterator v,vend; + if (2 == iStroke->vertices_size()) { + line->setStyle(LineRep::LINES); + v = iStroke->vertices_begin(); + StrokeVertex *svA = (*v); + v++; + StrokeVertex *svB = (*v); + Vec3r A((*svA)[0], (*svA)[1], 0); + Vec3r B((*svB)[0], (*svB)[1], 0); + line->AddVertex(A); + line->AddVertex(B); + } + else { + if (_overloadFrsMaterial) + line->setFrsMaterial(_FrsMaterial); + + line->setStyle(LineRep::LINE_STRIP); + + for (v = iStroke->vertices_begin(), vend = iStroke->vertices_end(); v != vend; v++) { + StrokeVertex *sv = (*v); + Vec3r V((*sv)[0], (*sv)[1], 0); + line->AddVertex(V); + } + } + line->setId(iStroke->getId()); + line->ComputeBBox(); + + return line; +} + +template<class StrokeVertexIterator> +NodeGroup *StrokeTesselator::Tesselate(StrokeVertexIterator begin, StrokeVertexIterator end) +{ + NodeGroup *group = new NodeGroup; + NodeShape *tshape = new NodeShape; + group->AddChild(tshape); + //tshape->material().setDiffuse(0.0f, 0.0f, 0.0f, 1.0f); + tshape->setFrsMaterial(_FrsMaterial); + + for (StrokeVertexIterator c = begin, cend = end; c != cend; c++) { + tshape->AddRep(Tesselate((*c))); + } + + return group; +} diff --git a/source/blender/freestyle/intern/stroke/StrokeTesselator.h b/source/blender/freestyle/intern/stroke/StrokeTesselator.h new file mode 100644 index 00000000000..e04f75a6f3e --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeTesselator.h @@ -0,0 +1,78 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_STROKE_TESSELATOR_H__ +#define __FREESTYLE_STROKE_TESSELATOR_H__ + +/** \file blender/freestyle/intern/stroke/StrokeTesselator.h + * \ingroup freestyle + * \brief Class to build a Node Tree designed to be displayed from a set of strokes structure. + * \author Stephane Grabli + * \date 26/03/2002 + */ + +#include "Stroke.h" + +#include "../scene_graph/LineRep.h" + +class StrokeTesselator +{ +public: + inline StrokeTesselator() + { + _FrsMaterial.setDiffuse(0, 0, 0, 1); + _overloadFrsMaterial = false; + } + + virtual ~StrokeTesselator() {} + + /*! Builds a line rep contained from a Stroke */ + LineRep *Tesselate(Stroke *iStroke); + + /*! Builds a set of lines rep contained under a a NodeShape, itself contained under a NodeGroup + * from a set of strokes. + */ + template<class StrokeIterator> + NodeGroup *Tesselate(StrokeIterator begin, StrokeIterator end); + + inline void setFrsMaterial(const FrsMaterial& iMaterial) + { + _FrsMaterial = iMaterial; + _overloadFrsMaterial = true; + } + + inline const FrsMaterial& frs_material() const + { + return _FrsMaterial; + } + +private: + FrsMaterial _FrsMaterial; + bool _overloadFrsMaterial; +}; + +#endif // __FREESTYLE_STROKE_TESSELATOR_H__ diff --git a/source/blender/freestyle/intern/stroke/StyleModule.h b/source/blender/freestyle/intern/stroke/StyleModule.h new file mode 100644 index 00000000000..12f645f125b --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StyleModule.h @@ -0,0 +1,184 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2010 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __FREESTYLE_STYLE_MODULE_H__ +#define __FREESTYLE_STYLE_MODULE_H__ + +/** \file blender/freestyle/intern/stroke/StyleModule.h + * \ingroup freestyle + * \brief Class representing a style module + * \author Stephane Grabli + * \author Emmanuel Turquin + * \date 01/07/2003 + */ + +#include <iostream> +#include <string> + +#include "Operators.h" +#include "StrokeLayer.h" +#include "StrokeShader.h" + +#include "../system/Interpreter.h" +#include "../system/StringUtils.h" + +using namespace std; + +class StyleModule +{ +public: + StyleModule(const string& file_name, Interpreter *inter) : _file_name(file_name) + { + _always_refresh = false; + _causal = false; + _drawable = true; + _modified = true; + _displayed = true; + _inter = inter; + } + + virtual ~StyleModule() {} + + StrokeLayer *execute() + { + if (!_inter) { + cerr << "Error: no interpreter was found to execute the script" << endl; + return NULL; + } + + if (!_drawable) { + cerr << "Error: not drawable" << endl; + return NULL; + } + + Operators::reset(); + + if (interpret()) { + cerr << "Error: interpretation failed" << endl; + Operators::reset(); + return NULL; + } + + Operators::StrokesContainer *strokes_set = Operators::getStrokesSet(); + if (strokes_set->empty()) { + cerr << "Error: strokes set empty" << endl; + Operators::reset(); + return NULL; + } + + StrokeLayer *sl = new StrokeLayer; + for (Operators::StrokesContainer::iterator it = strokes_set->begin(); it != strokes_set->end(); ++it) + sl->AddStroke(*it); + + Operators::reset(); + return sl; + } + +protected: + virtual int interpret() + { + return _inter->interpretFile(_file_name); + } + +public: + // accessors + const string getFileName() const + { + return _file_name; + } + + bool getAlwaysRefresh() const + { + return _always_refresh; + } + + bool getCausal() const + { + return _causal; + } + + bool getDrawable() const + { + return _drawable; + } + + bool getModified() const + { + return _modified; + } + + bool getDisplayed() const + { + return _displayed; + } + + // modifiers + void setFileName(const string& file_name) + { + _file_name = file_name; + } + + void setAlwaysRefresh(bool b = true) + { + _always_refresh = b; + } + + void setCausal(bool b = true) + { + _causal = b; + } + + void setDrawable(bool b = true) + { + _drawable = b; + } + + void setModified(bool b = true) + { + if (_always_refresh) + return; + _modified = b; + } + + void setDisplayed(bool b = true) + { + _displayed = b; + } + +private: + string _file_name; + bool _always_refresh; + bool _causal; + bool _drawable; + bool _modified; + bool _displayed; + +protected: + Interpreter *_inter; +}; + +#endif // __FREESTYLE_STYLE_MODULE_H__ diff --git a/source/blender/freestyle/intern/stroke/TextStrokeRenderer.cpp b/source/blender/freestyle/intern/stroke/TextStrokeRenderer.cpp new file mode 100644 index 00000000000..ea5af287bbe --- /dev/null +++ b/source/blender/freestyle/intern/stroke/TextStrokeRenderer.cpp @@ -0,0 +1,73 @@ + +// +// Copyright (C) : Please refer to the COPYRIGHT file distributed +// with this source distribution. +// +// 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +/////////////////////////////////////////////////////////////////////////////// + +# include "TextStrokeRenderer.h" +# include "Canvas.h" +# include "StrokeIterators.h" + +TextStrokeRenderer::TextStrokeRenderer(const char* iFileName) +:StrokeRenderer(){ + if(!iFileName) + iFileName = "freestyle.txt"; + // open the stream: + _ofstream.open(iFileName, ios::out); + if(!_ofstream.is_open()){ + cerr << "couldn't open the output file " << iFileName << endl; + } + _ofstream << "%!FREESTYLE" << endl; + _ofstream << "%Creator: Freestyle (http://artis.imag.fr/Software/Freestyle)" << endl; + // Bounding box + _ofstream << 0 << " "<< 0 << " " << Canvas::getInstance()->width() << " " << Canvas::getInstance()->height() << endl; + _ofstream << "%u x y z tleft tright r g b ..." << endl; +} + +TextStrokeRenderer::~TextStrokeRenderer(){ + Close(); +} + +void TextStrokeRenderer::RenderStrokeRep(StrokeRep *iStrokeRep) const{ + RenderStrokeRepBasic(iStrokeRep); +} + +void TextStrokeRenderer::RenderStrokeRepBasic(StrokeRep *iStrokeRep) const{ + Stroke *stroke = iStrokeRep->getStroke(); + if(!stroke){ + cerr << "no stroke associated with Rep" << endl; + return; + } + + StrokeInternal::StrokeVertexIterator v = stroke->strokeVerticesBegin(); + StrokeAttribute att; + while(!v.isEnd()){ + att = v->attribute(); + _ofstream << v->u() << " " << v->getProjectedX() << " " << v->getProjectedY() << " " << v->getProjectedZ() << " " \ + << att.getThicknessL() << " " << att.getThicknessR() << " " \ + << att.getColorR() << " " << att.getColorG() << " " << att.getColorB() << " "; + ++v; + } + _ofstream << endl; +} + +void TextStrokeRenderer::Close(){ + if(_ofstream.is_open()) + _ofstream.close(); +} + diff --git a/source/blender/freestyle/intern/stroke/TextStrokeRenderer.h b/source/blender/freestyle/intern/stroke/TextStrokeRenderer.h new file mode 100644 index 00000000000..cc6f5c05073 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/TextStrokeRenderer.h @@ -0,0 +1,68 @@ +// +// Filename : TextStrokeRenderer.h +// Author(s) : Stephane Grabli +// Purpose : Class to define the text rendering of a stroke +// Format: +// x y width height // bbox +// //list of vertices : +// t x y z t1 t2 r g b alpha ... +// ... +// Date of creation : 01/14/2005 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// Copyright (C) : Please refer to the COPYRIGHT file distributed +// with this source distribution. +// +// 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef TEXTSTROKERENDERER_H +# define TEXTSTROKERENDERER_H + +# include "StrokeRenderer.h" +# include "../system/FreestyleConfig.h" +# include <fstream> + +/**********************************/ +/* */ +/* */ +/* TextStrokeRenderer */ +/* */ +/* */ +/**********************************/ + +class LIB_STROKE_EXPORT TextStrokeRenderer : public StrokeRenderer +{ +public: + TextStrokeRenderer(const char * iFileName = 0); + virtual ~TextStrokeRenderer(); + + /*! Renders a stroke rep */ + virtual void RenderStrokeRep(StrokeRep *iStrokeRep) const; + virtual void RenderStrokeRepBasic(StrokeRep *iStrokeRep) const; + + /*! Closes the output file */ + void Close(); + +protected: + mutable ofstream _ofstream; +}; + +#endif // TEXTSTROKERENDERER_H + |