diff options
author | Maxime Curioni <maxime.curioni@gmail.com> | 2008-05-08 23:16:40 +0400 |
---|---|---|
committer | Maxime Curioni <maxime.curioni@gmail.com> | 2008-05-08 23:16:40 +0400 |
commit | 64e4a3ec9aed6c8abe095e2cd1fe1552f7cde51c (patch) | |
tree | 6c77358bd447b6c2d215324ef48fc12d1f5ae5ca /source/blender/freestyle/intern/stroke | |
parent | cf2e1e2857cfc5b3c2848c7fc6c9d919ac72fabb (diff) | |
parent | 106974a9d2d5caa5188322507980e3d57d2e3517 (diff) |
soc-2008-mxcurioni: merged changes to revision 14747, cosmetic changes for source/blender/freestyle
Diffstat (limited to 'source/blender/freestyle/intern/stroke')
50 files changed, 12991 insertions, 0 deletions
diff --git a/source/blender/freestyle/intern/stroke/AdvancedFunctions0D.cpp b/source/blender/freestyle/intern/stroke/AdvancedFunctions0D.cpp new file mode 100755 index 00000000000..c22e183eccb --- /dev/null +++ b/source/blender/freestyle/intern/stroke/AdvancedFunctions0D.cpp @@ -0,0 +1,85 @@ + +// +// 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 "../view_map/Functions0D.h" +# include "AdvancedFunctions0D.h" +# include "../view_map/SteerableViewMap.h" +# include "Canvas.h" + +namespace Functions0D { + + double 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())) + return 0.0; + RGBImage image; + canvas->readColorPixels((int)iter->getProjectedX() - bound, + (int)iter->getProjectedY() - bound, + _filter.maskSize(), + _filter.maskSize(), + image); + return _filter.getSmoothedPixel<RGBImage>(&image, (int)iter->getProjectedX(), + (int)iter->getProjectedY()); + } + + + double 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())) + return 0.0; + GrayImage image ; + iViewer->readDepthPixels((int)iter->getProjectedX()-bound,(int)iter->getProjectedY()-bound,_filter.maskSize(),_filter.maskSize(),image); + return _filter.getSmoothedPixel(&image, (int)iter->getProjectedX(), (int)iter->getProjectedY()); + } + + float ReadMapPixelF0D::operator()(Interface0DIterator& iter) { + Canvas * canvas = Canvas::getInstance(); + return canvas->readMapPixel(_mapName, _level, (int)iter->getProjectedX(), (int)iter->getProjectedY()); + } + + float ReadSteerableViewMapPixelF0D::operator()(Interface0DIterator& iter) { + SteerableViewMap *svm = Canvas::getInstance()->getSteerableViewMap(); + float v = svm->readSteerableViewMapPixel(_orientation, _level,(int)iter->getProjectedX(), (int)iter->getProjectedY()); + return v; + } + + float ReadCompleteViewMapPixelF0D::operator()(Interface0DIterator& iter) { + SteerableViewMap *svm = Canvas::getInstance()->getSteerableViewMap(); + float v = svm->readCompleteViewMapPixel(_level,(int)iter->getProjectedX(), (int)iter->getProjectedY()); + return v; + } + + float 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; + float f = Vec2f(gx,gy).norm(); + return f; + } +} // 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 100755 index 00000000000..d92575cf110 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/AdvancedFunctions0D.h @@ -0,0 +1,209 @@ +// +// Filename : AdvancedFunctions0D.h +// Author(s) : Stephane Grabli +// Emmanuel Turquin +// Purpose : Functions taking 0D input +// Date of creation : 01/07/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 ADVANCED_FUNCTIONS0D_HPP +# define ADVANCED_FUNCTIONS0D_HPP + + +# include "../image/Image.h" +# include "../image/GaussianFilter.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. */ + double 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.f) : UnaryFunction0D<double>() { + _filter.SetSigma((float)maskSize/2.f); + } + /*! Returns the string "LocalAverageDepthF0D"*/ + string getName() const { + return "LocalAverageDepthF0D"; + } + /*! the () operator.*/ + double 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.*/ + float 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.*/ + float 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.*/ + float 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.*/ + float operator()(Interface0DIterator& iter); + }; +} // end of namespace Functions0D + +#endif // ADVANCED_FUNCTIONS0D_HPP diff --git a/source/blender/freestyle/intern/stroke/AdvancedFunctions1D.cpp b/source/blender/freestyle/intern/stroke/AdvancedFunctions1D.cpp new file mode 100755 index 00000000000..cf2982606e0 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/AdvancedFunctions1D.cpp @@ -0,0 +1,108 @@ + +// +// 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 "AdvancedFunctions1D.h" +# include "../view_map/SteerableViewMap.h" +# include "Canvas.h" + +// FIXME +namespace Functions1D { + + real 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; + } + return res; + } + + double GetDirectionalViewMapDensityF1D::operator()(Interface1D& inter) { + unsigned size; + double res = integrate(_fun, inter.pointsBegin(_sampling), inter.pointsEnd(_sampling), _integration); + return res; + } + + double GetCompleteViewMapDensityF1D::operator()(Interface1D& inter) { + unsigned size; + Id id = inter.getId(); + double res = integrate(_fun, inter.pointsBegin(_sampling), inter.pointsEnd(_sampling), _integration); + return res; + } + + real GetViewMapGradientNormF1D::operator()(Interface1D& inter){ + return integrate(_func, inter.pointsBegin(_sampling), inter.pointsEnd(_sampling), _integration); + } +} + diff --git a/source/blender/freestyle/intern/stroke/AdvancedFunctions1D.h b/source/blender/freestyle/intern/stroke/AdvancedFunctions1D.h new file mode 100755 index 00000000000..65b0eec36fd --- /dev/null +++ b/source/blender/freestyle/intern/stroke/AdvancedFunctions1D.h @@ -0,0 +1,286 @@ +// +// Filename : AdvancedFunctions1D.h +// Author(s) : Stephane Grabli, Emmanuel Turquin +// Purpose : Functions taking 1D input +// Date of creation : 01/07/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 ADVANCED_FUNCTIONS1D_HPP +# define ADVANCED_FUNCTIONS1D_HPP + + +# 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.f) : UnaryFunction1D<double>(iType), _fun(sigma) { + _sampling = sampling; + } + /*! Destructor */ + virtual ~DensityF1D(){} + + /*! Returns the string "DensityF1D"*/ + string getName() const { + return "DensityF1D"; + } + /*! the () operator.*/ + double operator()(Interface1D& inter) { + return integrate(_fun, inter.pointsBegin(_sampling), inter.pointsEnd(_sampling), _integration); + } + 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.*/ + double operator()(Interface1D& inter) { + return integrate(_fun, inter.verticesBegin(), inter.verticesEnd(), _integration); + } + 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.f) + : UnaryFunction1D<double>(iType), _fun(level){_sampling = sampling;} + /*! Returns the string "GetCompleteViewMapDensityF1D"*/ + string getName() const { + return "GetCompleteViewMapDensityF1D"; + } + /*! the () operator.*/ + double 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.f) + : UnaryFunction1D<double>(iType), _fun(iOrientation,level){_sampling = sampling;} + /*! Returns the string "GetDirectionalViewMapDensityF1D"*/ + string getName() const { + return "GetDirectionalViewMapDensityF1D"; + } + /*! the () operator.*/ + double 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.f) : UnaryFunction1D<real>(iType) { + _level = level; + _sampling = sampling; + } + /*! Destructor */ + virtual ~GetSteerableViewMapDensityF1D(){} + + /*! Returns the string "GetSteerableViewMapDensityF1D"*/ + string getName() const { + return "GetSteerableViewMapDensityF1D"; + } + /*! the () operator.*/ + real 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.f) + : UnaryFunction1D<real>(iType), _func(level) { + _level = level; + _sampling = sampling; + } + + /*! Returns the string "GetSteerableViewMapDensityF1D"*/ + string getName() const { + return "GetViewMapGradientNormF1D"; + } + /*! the () operator.*/ + real operator()(Interface1D& inter); + }; +} // end of namespace Functions1D + +#endif // ADVANCED_FUNCTIONS1D_HPP diff --git a/source/blender/freestyle/intern/stroke/AdvancedPredicates1D.h b/source/blender/freestyle/intern/stroke/AdvancedPredicates1D.h new file mode 100755 index 00000000000..f348f8593e5 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/AdvancedPredicates1D.h @@ -0,0 +1,81 @@ +// +// Filename : AdvancedPredicates1D.h +// Author(s) : Stephane Grabli +// Purpose : Class gathering stroke creation algorithms +// Date of creation : 01/07/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 ADVANCED_PREDICATES1D_H +# define ADVANCED_PREDICATES1D_H + +# include <string> +# include "../view_map/Interface1D.h" +# include "AdvancedFunctions1D.h" +# include "Predicates1D.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. */ + bool operator()(Interface1D& inter) { + Functions1D::DensityF1D fun(_sigma); + return (fun(inter) < _threshold); + } + private: + double _sigma; + double _threshold; + }; + +} // end of namespace Predicates1D + +#endif // ADVANCED_PREDICATES1D_H diff --git a/source/blender/freestyle/intern/stroke/AdvancedStrokeShaders.cpp b/source/blender/freestyle/intern/stroke/AdvancedStrokeShaders.cpp new file mode 100755 index 00000000000..b1c5317c5f1 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/AdvancedStrokeShaders.cpp @@ -0,0 +1,423 @@ + +// +// 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 "../system/PseudoNoise.h" +#include "../system/RandGen.h" +#include "AdvancedStrokeShaders.h" +#include "StrokeIterators.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; + +void +CalligraphicShader::shade(Stroke &ioStroke) const +{ + Interface0DIterator v; + Functions0D::VertexOrientation2DF0D fun; + StrokeVertex* sv; + for(v=ioStroke.verticesBegin(); + !v.isEnd(); + ++v) + { + real thickness; + Vec2f vertexOri(fun(v)); + 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); + } + +} + +//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); +// } +// } +// +//} + + + +///////////////////////////////////////// +// +// 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; +} +void +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()); + Vec2r vertexOri(fun(v)); + 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; + } + +} + + + +///////////////////////////////////////// +// +// 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; +} + + + +void +SmoothingShader::shade(Stroke &ioStroke) const +{ + //cerr<<" Smoothing a stroke "<<endl; + + Smoother smoother(ioStroke); + smoother.smooth(_nbIterations, _factorPoint, _factorCurvature, _factorCurvatureDifference, + _anisoPoint, _anisoNormal, _anisoCurvature, _carricatureFactor); +} + +// 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) + { + _vertex[i]=(v)->getPoint(); + i++; + } + 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(); +} + +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++; + } + +} + + +///////////////////////////////////////// +// +// OMISSION SHADER +// +///////////////////////////////////////// + +OmissionShader::OmissionShader (real sizeWindow, real thrVari, real thrFlat, real lFlat) +{ + _sizeWindow=sizeWindow; + _thresholdVariation=thrVari; + _thresholdFlat=thrFlat; + _lengthFlat=lFlat; +} + +void +OmissionShader::shade(Stroke &ioStroke) const +{ + Omitter omi(ioStroke); + omi.omit(_sizeWindow, _thresholdVariation, _thresholdFlat, _lengthFlat); + +} + + +// OMITTER +/////////////////////////// + +Omitter::Omitter (Stroke &ioStroke) + :Smoother (ioStroke) +{ + StrokeInternal::StrokeVertexIterator v, vend; + int i=0; + for(v=ioStroke.strokeVerticesBegin(), vend=ioStroke.strokeVerticesEnd(); + v!=vend; + ++v) + { + _u[i]=(v)->curvilinearAbscissa(); + i++; + } + +} + +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--; + } + + } +} diff --git a/source/blender/freestyle/intern/stroke/AdvancedStrokeShaders.h b/source/blender/freestyle/intern/stroke/AdvancedStrokeShaders.h new file mode 100755 index 00000000000..b624ccd3e6b --- /dev/null +++ b/source/blender/freestyle/intern/stroke/AdvancedStrokeShaders.h @@ -0,0 +1,222 @@ +// +// Filename : AdvancedStrokeShaders.h +// Author : Fredo Durand +// Purpose : Fredo's stroke shaders +// Date of creation : 17/12/2002 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 ADVANCEDSTROKESHADERS_H +# define ADVANCEDSTROKESHADERS_H + +# 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 void 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 void 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 void 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 void shade(Stroke &ioStroke) const; + +protected: + + real _sizeWindow; + real _thresholdVariation; + real _thresholdFlat; + real _lengthFlat; +}; + +#endif // ADVANCEDSTROKESHADERS_H diff --git a/source/blender/freestyle/intern/stroke/BasicStrokeShaders.cpp b/source/blender/freestyle/intern/stroke/BasicStrokeShaders.cpp new file mode 100755 index 00000000000..c3827ae3e36 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/BasicStrokeShaders.cpp @@ -0,0 +1,1100 @@ + +// +// 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 <fstream> +#include <qimage.h> +#include "BasicStrokeShaders.h" +#include "../system/PseudoNoise.h" +#include "../system/RandGen.h" +#include "../view_map/Functions0D.h" +#include "../view_map/Functions1D.h" +#include "AdvancedFunctions0D.h" +#include "AdvancedFunctions1D.h" +#include "StrokeIterators.h" +#include "../system/StringUtils.h" +#include "StrokeRenderer.h" +#include "StrokeIO.h" +#include <QString> + +// Internal function +void convert(const QImage& iImage, float **oArray, unsigned &oSize) { + oSize = iImage.width(); + *oArray = new float[oSize]; + for(unsigned i=0; i<oSize; ++i) { + QRgb rgb = iImage.pixel(i,0); + (*oArray)[i] = ((float)qBlue(rgb))/255.f; + } +} + +namespace StrokeShaders { + + // + // Thickness modifiers + // + ////////////////////////////////////////////////////////// + + void 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) + { + 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); + } + } + + void 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) + { + 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); + } + } + + void IncreasingThicknessShader::shade(Stroke& stroke) const + { + + int n=stroke.strokeVerticesSize()-1; + StrokeInternal::StrokeVertexIterator v, vend; + int i=0; + for(v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd(); + v!=vend; + + ++v) + { + float t; + if(i < (float)n/2.f) + 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); + ++i; + } + } + + void ConstrainedIncreasingThicknessShader::shade(Stroke& stroke) const + { + float slength = stroke.getLength2D(); + float maxT = min(_ratio*slength,_ThicknessMax); + int n=stroke.strokeVerticesSize()-1; + StrokeInternal::StrokeVertexIterator v, vend; + int i=0; + for(v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd(); + v!=vend; + + ++v) + { + float t; + if(i < (float)n/2.f) + 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); + ++i; + } + } + + + void LengthDependingThicknessShader::shade(Stroke& stroke) const + { + float step = (_maxThickness-_minThickness)/3.f; + float l = stroke.getLength2D(); + float thickness; + if(l>300.f) + thickness = _minThickness+3.f*step; + else if((l< 300.f) && (l>100.f)) + thickness = _minThickness+2.f*step; + else if((l<100.f) && (l>50.f)) + thickness = _minThickness+1.f*step; + else if(l< 50.f) + thickness = _minThickness; + + StrokeInternal::StrokeVertexIterator v, vend; + int i=0; + int size = stroke.strokeVerticesSize(); + for(v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd(); + v!=vend; + ++v) + { + 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); + } + } + + + ThicknessVariationPatternShader::ThicknessVariationPatternShader(const string pattern_name, + float iMinThickness, + float iMaxThickness, + bool stretch) + : StrokeShader() { + _stretch = stretch; + _minThickness = iMinThickness; + _maxThickness = iMaxThickness; + QImage image; + 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()) { + image.load(j->c_str()); + break; + } + } + if (image.isNull()) + cerr << "Error: cannot find pattern \"" << pattern_name + << "\" - check the path in the Options" << endl; + else + convert(image, &_aThickness, _size); + } + + void ThicknessVariationPatternShader::shade(Stroke& stroke) const + { + StrokeInternal::StrokeVertexIterator v, vend; + float *array = 0; + int size; + array = _aThickness; + size = _size; + // } + 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.f; + thicknessR = _minThickness/2.f; + } + if(thicknessR+thicknessL > _maxThickness) + { + thicknessL = _maxThickness/2.f; + thicknessR = _maxThickness/2.f; + } + if((sig==0) || (sig==vert_size-1)) + v->attribute().setThickness(1, 1); + else + v->attribute().setThickness(thicknessR, thicknessL); + ++sig; + } + } + + + static const unsigned NB_VALUE_NOISE = 512; + ThicknessNoiseShader::ThicknessNoiseShader() + :StrokeShader() + {_amplitude=1.f;_scale=1.f/2.f/(float)NB_VALUE_NOISE;} + + ThicknessNoiseShader::ThicknessNoiseShader(float iAmplitude, float iPeriod) + :StrokeShader() + {_amplitude=iAmplitude;_scale=1.f/iPeriod/(float)NB_VALUE_NOISE;} + + + void ThicknessNoiseShader::shade(Stroke& stroke) const + { + StrokeInternal::StrokeVertexIterator v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd(); + 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(; + 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); + } + + } + + // + // Color shaders + // + /////////////////////////////////////////////////////////////////////////////// + + void 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]); + } + } + + void IncreasingColorShader::shade(Stroke& stroke) const + { + StrokeInternal::StrokeVertexIterator v, vend; + int n=stroke.strokeVerticesSize()-1; + int yo=0; + float newcolor[4]; + for(v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd(); + v!=vend; + + ++v) + { + 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]); + ++yo; + } + } + + ColorVariationPatternShader::ColorVariationPatternShader(const string pattern_name, + bool stretch) + : StrokeShader() { + _stretch = stretch; + QImage image; + 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()) { + image.load(j->c_str()); + break; + } + } + if (image.isNull()) + cerr << "Error: cannot find pattern \"" << pattern_name + << "\" - check the path in the Options" << endl; + else + convert(image, &_aVariation, _size); + } + + void 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); + } + } + + void MaterialColorShader::shade(Stroke& stroke) const + { + Interface0DIterator v, vend; + Functions0D::MaterialF0D fun; + StrokeVertex *sv; + for(v=stroke.verticesBegin(), vend=stroke.verticesEnd(); + v!=vend; + ++v) + { + const float *diffuse = fun(v).diffuse(); + sv = dynamic_cast<StrokeVertex*>(&(*v)); + sv->attribute().setColor(diffuse[0]*_coefficient, diffuse[1]*_coefficient, diffuse[2]*_coefficient); + sv->attribute().setAlpha(diffuse[3]); + } + } + + + void CalligraphicColorShader::shade(Stroke& stroke) const + { + Interface0DIterator v; + Functions0D::VertexOrientation2DF0D fun; + StrokeVertex* sv; + for(v=stroke.verticesBegin(); + !v.isEnd(); + ++v) + { + Vec2f vertexOri(fun(v)); + 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); + } + } + + + ColorNoiseShader::ColorNoiseShader() + :StrokeShader() + {_amplitude=1.f;_scale=1.f/2.f/(float)NB_VALUE_NOISE;} + + ColorNoiseShader::ColorNoiseShader(float iAmplitude, float iPeriod) + :StrokeShader() + {_amplitude=iAmplitude;_scale=1.f/iPeriod/(float)NB_VALUE_NOISE;} + + + void ColorNoiseShader::shade(Stroke& stroke) const + { + StrokeInternal::StrokeVertexIterator v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd(); + real initU=v->strokeLength()*real(NB_VALUE_NOISE)+RandGen::drand48()*real(NB_VALUE_NOISE); + + real bruit; + PseudoNoise mynoise; + for(; + 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); + } + + } + + + // + // Texture Shaders + // + /////////////////////////////////////////////////////////////////////////////// + + void TextureAssignerShader::shade(Stroke& stroke) const + { + // 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); + + TextureManager * instance = TextureManager::getInstance(); + if(!instance) + return; + 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); + } + + // FIXME + void StrokeTextureShader::shade(Stroke& stroke) const + { + TextureManager * instance = TextureManager::getInstance(); + if(!instance) + return; + string pathname = TextureManager::Options::getBrushesPath() + "/" + _texturePath; + unsigned int texId = instance->getBrushTextureIndex(pathname, _mediumType); + stroke.SetMediumType(_mediumType); + stroke.SetTips(_tips); + stroke.SetTextureId(texId); + } + + // + // Geometry Shaders + // + /////////////////////////////////////////////////////////////////////////////// + + void BackboneStretcherShader::shade(Stroke& stroke) const + { + float l=stroke.getLength2D(); + if(l <= 50) + return; + + 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]); + } + + void SamplingShader::shade(Stroke& stroke) const + { + stroke.Resample(_sampling); + } + + void ExternalContourStretcherShader::shade(Stroke& stroke) const + { + float l=stroke.getLength2D(); + Interface0DIterator it=stroke.verticesBegin(); + Functions0D::Normal2DF0D fun; + StrokeVertex* sv; + while (!it.isEnd()) + { + Vec2f n(fun(it)); + sv = dynamic_cast<StrokeVertex*>(&(*it)); + Vec2d newPoint(sv->x()+_amount*n.x(), sv->y()+_amount*n.y()); + sv->SetPoint(newPoint[0], newPoint[1]); + ++it; + } + } + + void BSplineShader::shade(Stroke& stroke) const + { + if(stroke.strokeVerticesSize() < 4) + return; + + // Find the new vertices + vector<Vec2d> newVertices; + double t=0.f; + float _sampling = 5.f; + + 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(p1 == end) + // p1 = p0; + 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,pend; + p=newVertices.begin();pend=newVertices.end(); + vector<Vec2d>::iterator last = p; + n=0; + StrokeInternal::StrokeVertexIterator it=stroke.strokeVerticesBegin(), itend=stroke.strokeVerticesEnd(); + it=stroke.strokeVerticesBegin(); + for(; + ((it!=itend) && (p!=pend)); + ++it) + { + it->SetX(p->x()); + it->SetY(p->y()); + last = p; + ++p; + ++n; + } + // nExtraVertex should stay unassigned + for(int i=0; i< nExtraVertex; ++i) + { + it->SetX(last->x()); + it->SetY(last->y()); + if(it.isEnd()) + cerr << "Warning: Problem encountered while creating B-spline" << endl; + ++it; + ++n; + } + } + + //!! Bezier curve stroke shader + void BezierCurveShader::shade(Stroke& stroke) const + { + if(stroke.strokeVerticesSize() < 4) + return; + + // Build the Bezier curve from this set of data points: + vector<Vec2d> data; + StrokeInternal::StrokeVertexIterator v=stroke.strokeVerticesBegin(), vend=stroke.strokeVerticesEnd(); + data.push_back(Vec2d(v->x(), v->y()));//first one + StrokeInternal::StrokeVertexIterator previous = v;++v; + for(; + 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; + } + + // Vec2d tmp;bool equal=false; + // if(data.front() == data.back()) + // { + // tmp = data.back(); + // data.pop_back(); + // equal=true; + // } + // 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=bsegments.end(); + vector<Vec2d>& segmentsVertices = (*s)->vertices(); + vector<Vec2d>::iterator p,pend; + // first point + CurveVertices.push_back(segmentsVertices[0]); + for(; + s!=send; + ++s) + { + segmentsVertices = (*s)->vertices(); + p=segmentsVertices.begin(); pend=segmentsVertices.end(); + ++p; + for(; + p!=pend; + ++p) + { + CurveVertices.push_back((*p)); + } + } + + //if(equal) + // if(data.back() == data.front()) + // { + // vector<Vec2d>::iterator d=data.begin(), dend=data.end(); + // cout << "ending point = starting point" << endl; + // cout << "---------------DATA----------" << endl; + // for(; + // d!=dend; + // ++d) + // { + // cout << d->x() << "-" << d->y() << endl; + // } + // cout << "--------------BEZIER RESULT----------" << endl; + // d=CurveVertices.begin(), dend=CurveVertices.end(); + // for(;d!=dend;++d) + // { + // cout << d->x() << "-" << d->y() << endl; + // } + // } + + // Resample the Stroke depending on the number of + // vertices of the bezier curve: + int originalSize = CurveVertices.size(); + //float sampling = stroke.ComputeSampling(originalSize); + //stroke.Resample(sampling); + stroke.Resample(originalSize); + int newsize = stroke.strokeVerticesSize(); + int nExtraVertex=0; + if(newsize < originalSize) + cerr << "Warning: unsufficient resampling" << endl; + else + { + //cout << "Oversampling" << endl; + nExtraVertex = newsize - originalSize; + if(nExtraVertex != 0) + cout << "Bezier Shader : Stroke " << stroke.getId() << " have not been resampled" << endl; + } + + // assigns the new coordinates: + p=CurveVertices.begin();pend=CurveVertices.end(); + vector<Vec2d>::iterator last = p; + int n=0; + StrokeInternal::StrokeVertexIterator it=stroke.strokeVerticesBegin(), itend=stroke.strokeVerticesEnd(); + // while(p!=pend) + // { + // ++n; + // ++p; + // } + it=stroke.strokeVerticesBegin(); + for(; + ((it!=itend) && (p!=pend)); + ++it) + { + it->SetX(p->x()); + it->SetY(p->y()); + // double x = p->x(); + // double y = p->y(); + // cout << "x = " << x << "-" << "y = " << y << endl; + last = p; + ++p; + ++n; + } + + // Deal with extra vertices: + if(nExtraVertex == 0) + return; + + // nExtraVertex should stay unassigned + vector<StrokeAttribute> attributes; + vector<StrokeVertex*> verticesToRemove; + for(int i=0; i< nExtraVertex; ++i) + { + verticesToRemove.push_back(&(*it)); + if(it.isEnd()) + cout << "fucked up" << endl; + ++it; + ++n; + } + it=stroke.strokeVerticesBegin(); + for(; + it!=itend; + ++it) + { + attributes.push_back(it->attribute()); + } + + for(vector<StrokeVertex*>::iterator vr=verticesToRemove.begin(), vrend=verticesToRemove.end(); + vr!=vrend; + ++vr) + { + stroke.RemoveVertex(*vr); + } + it=stroke.strokeVerticesBegin(); + itend=stroke.strokeVerticesEnd(); + 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!=itend) && (a!=aend); + ++it) + { + (it)->SetAttribute(*a); + if((index <= index1)||(index>index2)) + ++a; + ++index; + } + } + + void 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) > _curvatureThreshold) + return; + + Functions0D::VertexOrientation2DF0D ori_fun; + Functions0D::Curvature2DAngleF0D curv_fun; + Functions1D::Normal2DF1D norm_fun; + Interface0DIterator it=stroke.verticesBegin(); + StrokeVertex* sv; + while (!it.isEnd()) + { + Vec2f ntmp = ori_fun(it); + Vec2f n(ntmp.y(), -ntmp.x()); + Vec2f strokeN(norm_fun(stroke)); + if(n*strokeN < 0) + { + n[0] = -n[0]; + n[1] = -n[1]; + } + sv = dynamic_cast<StrokeVertex*>(&(*it)); + float u=sv->u(); + float t = 4.f*(0.25f - (u-0.5)*(u-0.5)); + float curvature_coeff = (M_PI-curv_fun(it))/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]); + ++it; + } + } + + 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.f; + 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 = 1; + for(int i=0; i<size/2; ++i) + { + ++it; + ++actualSize; + } + CurvePiece * second = new CurvePiece(it, _last, size-actualSize+1); + size = actualSize; + _last = it; + B = Vec2d((_last)->x(), (_last)->y()); + return second; + } + }; + + void 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,vlast; + 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); + } + // u.normalize(); + // (*a)->SetPoint((*a)->x()-u.x()*10, (*a)->y()-u.y()*10); + } + + // delete stuff + for(cp=_results.begin(), cpend=_results.end(); + cp!=cpend; + ++cp) + { + delete (*cp); + } + _results.clear(); + } + + void 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(); + Vec2f strokeN(norm_fun(stroke)); + if(n*strokeN < 0) + { + n[0] = -n[0]; + n[1] = -n[1]; + } + float offset = (piece.error())/2.f*_offset; + StrokeInternal::StrokeVertexIterator v=a,vend=stroke.strokeVerticesEnd(); + for(; + 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); + } + } + + ///////////////////////////////////////// + // + // Tip Remover + // + ///////////////////////////////////////// + + + TipRemoverShader::TipRemoverShader(real tipLength) + : StrokeShader() + { + _tipLength = tipLength; + } + + void + TipRemoverShader::shade(Stroke& stroke) const + { + int originalSize = stroke.strokeVerticesSize(); + + if(originalSize<4) + return; + + StrokeInternal::StrokeVertexIterator v, vend; + vector<StrokeVertex*> verticesToRemove; + vector<StrokeAttribute> oldAttributes; + v=stroke.strokeVerticesBegin(); vend=stroke.strokeVerticesEnd(); + for(; + 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; + + vector<StrokeVertex*>::iterator sv=verticesToRemove.begin(), svend=verticesToRemove.end(); + for(; + sv!=svend; + ++sv) + { + stroke.RemoveVertex((*sv)); + } + + // Resample so that our new stroke have the same + // number of vertices than before + stroke.Resample(originalSize); + + if(stroke.strokeVerticesSize() != originalSize) + cerr << "Warning: resampling problem" << endl; + + // assign old attributes to new stroke vertices: + v=stroke.strokeVerticesBegin(); vend=stroke.strokeVerticesEnd(); + vector<StrokeAttribute>::iterator a=oldAttributes.begin(), aend=oldAttributes.end(); + //cout << "-----------------------------------------------" << endl; + for(;(v!=vend)&&(a!=aend);++v,++a) + { + v->SetAttribute(*a); + //cout << "thickness = " << (*a).getThickness()[0] << "-" << (*a).getThickness()[1] << endl; + } + // we're done! + } + + void streamShader::shade(Stroke& stroke) const{ + cout << stroke << endl; + } + void fstreamShader::shade(Stroke& stroke) const{ + _stream << stroke << endl; + } + +} // 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 100755 index 00000000000..f68971a3966 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/BasicStrokeShaders.h @@ -0,0 +1,791 @@ +// +// Filename : BasicStrokeShaders.h +// Author : Stephane Grabli +// Purpose : Class gathering basic stroke shaders +// Date of creation : 17/12/2002 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 BASIC_STROKE_SHADERS_H +# define BASIC_STROKE_SHADERS_H + +# include "Stroke.h" +# include "../geometry/Geom.h" +# include "../geometry/Bezier.h" +# include "StrokeShader.h" +# include <fstream> + +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 void 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 void 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() {} + /*! The shading method. */ + virtual void 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() {} + /*! The shading method. */ + virtual void 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 void 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.f, + float iMaxThickness = 5.f, + bool stretch = true); + /*! Destructor.*/ + virtual ~ThicknessVariationPatternShader() + { + if(0 != _aThickness) + { + delete [] _aThickness; + _aThickness = 0; + } + } + /*! The shading method. */ + virtual void 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); + /*! The shading method. */ + virtual void 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.f) + : StrokeShader() + { + _color[0] = iR; + _color[1] = iG; + _color[2] = iB; + _color[3] = iAlpha; + } + + virtual string getName() const { + return "ConstantColorShader"; + } + /*! The shading method. */ + virtual void 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; + } + /*! The shading method. */ + virtual void 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; + } + } + /*! The shading method. */ + virtual void 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.f) + : StrokeShader() + {_coefficient=coeff;} + + virtual void 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 void 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); + /*! The shading method. */ + virtual void 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; + } + /*! The shading method */ + virtual void 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; + } + /*! The shading method */ + virtual void 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.f) + : StrokeShader() + { + _amount = iAmount; + } + /*! The shading method */ + virtual void 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; + } + /*! The shading method */ + virtual void shade(Stroke& stroke) const; + }; + + + class LIB_STROKE_EXPORT ExternalContourStretcherShader : public StrokeShader + { + private: + float _amount; + public: + ExternalContourStretcherShader(float iAmount=2.f) + : StrokeShader() + { + _amount = iAmount; + } + + virtual void shade(Stroke& stroke) const; + }; + + // B-Spline stroke shader + class LIB_STROKE_EXPORT BSplineShader: public StrokeShader + { + public: + BSplineShader() + : StrokeShader() + {} + + virtual void 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;} + + /*! The shading method */ + virtual void 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; + } + /*! The shading method */ + virtual void 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;} + /*! The shading method */ + virtual void 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;} + /*! The shading method */ + virtual void 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 void 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 void 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()){ + cout << "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 void shade(Stroke& stroke) const; + }; +} // end of namespace StrokeShaders + +#endif // 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 100755 index 00000000000..ecb76c4a015 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Canvas.cpp @@ -0,0 +1,427 @@ + +// +// 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 <vector> +#include "../system/FreestyleConfig.h" +#include "StrokeRenderer.h" +#include "../system/TimeStamp.h" +#include "../system/PseudoNoise.h" +#include "Canvas.h" +#include <qimage.h> +#include <QString> +#include "../image/Image.h" +#include "../image/GaussianFilter.h" +#include "../image/ImagePyramid.h" +#include "../view_map/SteerableViewMap.h" +#include "StyleModule.h" + +using namespace std; + +LIB_STROKE_EXPORT +Canvas * Canvas::_pInstance = 0; + +LIB_STROKE_EXPORT +const char * Canvas::_MapsPath = 0; + +using namespace std; + +Canvas::Canvas() +{ + _SelectedFEdge = 0; + _pInstance = this; + PseudoNoise::init(42); + _paperTextureIndex = 0; + _Renderer = 0; + _drawPaper = true; + _current_sm = NULL; + _steerableViewMap = new SteerableViewMap(NB_STEERABLE_VIEWMAP-1); +} + +Canvas::Canvas(const Canvas& iBrother) +{ + _SelectedFEdge = iBrother._SelectedFEdge; + _pInstance = this; + PseudoNoise::init(42); + _paperTextureIndex = iBrother._paperTextureIndex; + _Renderer = iBrother._Renderer; + _drawPaper = iBrother._drawPaper; + _current_sm = iBrother._current_sm; + _steerableViewMap = new SteerableViewMap(*(iBrother._steerableViewMap)); + +} + +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 i = 0; i < _StyleModules.size(); i++) { + _current_sm = _StyleModules[i]; + if (!_StyleModules[i]->getModified()) + { + if (_StyleModules[i]->getDrawable() && _Layers[i]) + _Layers[i]->Render(_Renderer); + continue; + } + if (i < _Layers.size() && _Layers[i]) + delete _Layers[i]; + + _Layers[i] = _StyleModules[i]->execute(); + + if (_StyleModules[i]->getDrawable() && _Layers[i]) + _Layers[i]->Render(_Renderer); + + 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(); +} + +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(); +} + +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 i=0; + if (!_StyleModules.empty()) + { + for(deque<StyleModule*>::iterator s=_StyleModules.begin(), send=_StyleModules.end(); + s!=send; + ++s) + { + if(i == index) + { + // remove shader + if (*s) + delete *s; + _StyleModules.erase(s); + break; + } + ++i; + } + } + i=0; + if(!_Layers.empty()) + { + for(deque<StrokeLayer*>::iterator sl=_Layers.begin(), slend=_Layers.end(); + sl!=slend; + ++sl) + { + if(i == index) + { + // remove layer + if (*sl) + delete *sl; + _Layers.erase(sl); + break; + } + ++i; + } + } +} + + +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) + { + if(i == index) + { + if (*s) + delete *s; + *s = iStyleModule; + break; + } + ++i; + } +} + +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 size = _StyleModules.size(); + for(unsigned i = 0; i < size; ++i) + setModified(i,iMod); +} + +void Canvas::causalStyleModules(vector<unsigned>& vec, unsigned index) { + unsigned size = _StyleModules.size(); + + for(unsigned i = index; i < size; ++i) + if (_StyleModules[i]->getCausal()) + vec.push_back(i); +} + +void Canvas::changePaperTexture(bool increment) +{ + if(increment) + _paperTextureIndex = (_paperTextureIndex+1) % TextureManager::getPaperTexturesNumber(); + else + { + _paperTextureIndex--; + if(_paperTextureIndex < 0) + _paperTextureIndex = TextureManager::getPaperTexturesNumber() - 1; + } +} + +void Canvas::Render(const StrokeRenderer *iRenderer) +{ + for (unsigned 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 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; + } + QImage * qimg; + QImage newMap(filePath.c_str()); + if(newMap.isNull()){ + cout << "Could not load image file " << filePath << endl; + return; + } + qimg = &newMap; + + //resize + QImage scaledImg; + if((newMap.width()!=width()) || (newMap.height()!=height())){ + scaledImg = newMap.scaled(width(), height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + qimg = &scaledImg; + } + + // deal with color image + // if(newMap->depth() != 8){ + // int w = newMap->width(); + // int h = newMap->height(); + // QImage *tmp = new QImage(w, h, 8); + // for(unsigned y=0;y<h;++y){ + // for(unsigned x=0;x<w;++x){ + // int c = qGray(newMap->pixel(x,y)); + // tmp->setPixel(x,y,c); + // } + // } + // delete newMap; + // newMap = tmp; + // } + + unsigned x,y; + int w = qimg->width(); + int h = qimg->height(); + GrayImage tmp(w,h); + for(y=0; y<h;++y){ + for(x=0;x<w;++x){ + float c = qGray(qimg->pixel(x,y));// /255.f; + tmp.setPixel(x,y,c); + } + } + + // GrayImage blur(w,h); + // GaussianFilter gf(4.f); + // //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); + // } + // } + + GaussianPyramid *pyramid = new GaussianPyramid(tmp, iNbLevels, iSigma); + int ow = pyramid->width(0); + int oh = pyramid->height(0); + QString base(iMapName); + for(unsigned i=0; i<pyramid->getNumberOfLevels(); ++i){ + // save each image: + // w = pyramid.width(i); + // h = pyramid.height(i); + QImage qtmp(ow, oh, QImage::Format_RGB32); + //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); + qtmp.setPixel(x,y,qRgb(c,c,c)); + } + } + qtmp.save(base+QString::number(i)+".bmp", "BMP"); + } + // 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; + // + _maps[iMapName] = pyramid; + // newMap->save("toto.bmp", "BMP"); +} + +float Canvas::readMapPixel(const char *iMapName, int level, int x, int y){ + if(_maps.empty()){ + cout << "readMapPixel warning: no map was loaded "<< endl; + return -1; + } + mapsMap::iterator m = _maps.find(iMapName); + if(m==_maps.end()){ + 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 100755 index 00000000000..cae50162933 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Canvas.h @@ -0,0 +1,198 @@ +// +// Filename : Canvas.h +// Author(s) : Stephane Grabli +// Purpose : Class to define a canvas designed to draw style modules +// Date of creation : 20/10/2002 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 CANVAS_H +# define CANVAS_H + +# include <deque> +# include <vector> +# include <map> +# include "../system/FreestyleConfig.h" +# include "StrokeLayer.h" +# include "../geometry/BBox.h" +# include "../geometry/Geom.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; + int _paperTextureIndex; + bool _drawPaper; + StrokeRenderer *_Renderer; + StyleModule* _current_sm; + mapsMap _maps; + static const char * _MapsPath; + SteerableViewMap *_steerableViewMap; + +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.f); + + /*! 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; + inline int currentPaperTextureIndex() const {return _paperTextureIndex;} + 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;} + + /*! modifiers */ + inline void SetSelectedFEdge(FEdge *iFEdge) {_SelectedFEdge = iFEdge;} + /*! inserts a shader at pos index+1 */ + 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) ; + //inline void SetDensityMap(InformationMap<RGBImage>* iMap) {_DensityMap = iMap;} + inline void AddLayer(StrokeLayer *iLayer) {_Layers.push_back(iLayer);} + inline void SetCurrentPaperTextureIndex(int i) {_paperTextureIndex = i;} + void changePaperTexture(bool increment=true) ; + /*! enables/disables paper texture */ + inline void togglePaperTexture() {_drawPaper = !_drawPaper;} + void resetModified(bool iMod=false); + void causalStyleModules(std::vector<unsigned>& vec, unsigned index = 0); + void setModified(unsigned index, bool b); +}; + +#endif // CANVAS_H diff --git a/source/blender/freestyle/intern/stroke/Chain.cpp b/source/blender/freestyle/intern/stroke/Chain.cpp new file mode 100755 index 00000000000..3776cd58a03 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Chain.cpp @@ -0,0 +1,126 @@ + +// +// 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 "Chain.h" +#include "../view_map/ViewMapIterators.h" +#include "../view_map/ViewMapAdvancedIterators.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; + } + 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(); + } +} + +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; + } + 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(); + } +} + + + diff --git a/source/blender/freestyle/intern/stroke/Chain.h b/source/blender/freestyle/intern/stroke/Chain.h new file mode 100755 index 00000000000..042437a4154 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Chain.h @@ -0,0 +1,82 @@ +// +// Filename : Chain.h +// Author(s) : Stephane Grabli +// Purpose : Class to define a chain of viewedges. +// Date of creation : 09/01/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 CHAIN_H +# define CHAIN_H + +# include "../view_map/ViewMap.h" +# include "Curve.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; +public: + /*! Defult constructor. */ + Chain() : Curve() {_splittingId=0;} + /*! Builds a chain from its Id. */ + Chain(const Id& id) : Curve(id) {_splittingId=0;} + /*! Copy Constructor */ + Chain(const Chain& iBrother) : Curve(iBrother) {_splittingId=iBrother._splittingId;} + /*! Destructor. */ + virtual ~Chain() { + // only the last splitted deletes this id + if(_splittingId){ + if(*_splittingId == _Id) + delete _splittingId; + } + } + + /*! 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 // CHAIN_H diff --git a/source/blender/freestyle/intern/stroke/ChainingIterators.cpp b/source/blender/freestyle/intern/stroke/ChainingIterators.cpp new file mode 100755 index 00000000000..206b6eb7364 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/ChainingIterators.cpp @@ -0,0 +1,147 @@ + +// +// 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 "ChainingIterators.h" +#include "../system/TimeStamp.h" + +ViewEdge* AdjacencyIterator::operator*() { + return (*_internalIterator).first; +} +bool AdjacencyIterator::isIncoming() const{ + return (*_internalIterator).second; +} + +void AdjacencyIterator::increment(){ + ++_internalIterator; + while((!_internalIterator.isEnd()) && (!isValid((*_internalIterator).first))) + ++_internalIterator; +} + +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; +} + +void ChainingIterator::increment() { + _increment = true; + ViewVertex * vertex = getVertex(); + if(!vertex){ + _edge = 0; + return; + } + AdjacencyIterator it = AdjacencyIterator(vertex, _restrictToSelection, _restrictToUnvisited); + if(it.isEnd()) + _edge = 0; + else + _edge = traverse(it); + if(_edge == 0) + return; + if(_edge->A() == vertex) + _orientation = true; + else + _orientation = false; +} + +void ChainingIterator::decrement() { + _increment = false; + ViewVertex * vertex = getVertex(); + if(!vertex){ + _edge = 0; + return; + } + AdjacencyIterator it = AdjacencyIterator(vertex, _restrictToSelection, _restrictToUnvisited); + if(it.isEnd()) + _edge = 0; + else + _edge = traverse(it); + if(_edge == 0) + return; + if(_edge->B() == vertex) + _orientation = true; + else + _orientation = false; +} + +// +// ChainSilhouetteIterators +// +/////////////////////////////////////////////////////////// + +ViewEdge * 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) + return ve; + ++it; + } + return 0; + } + if(nextVertex->getNature() & Nature::NON_T_VERTEX){ + 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 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){ + return newEdge; + }else{ + return 0; + } + } + } + } + return 0; +} + +ViewEdge * 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)(*ve)) && ((*_binary_predicate)(*(getCurrentEdge()),*(ve)))) + return ve; + ++it; + } + return 0; +} diff --git a/source/blender/freestyle/intern/stroke/ChainingIterators.h b/source/blender/freestyle/intern/stroke/ChainingIterators.h new file mode 100755 index 00000000000..1e946855dce --- /dev/null +++ b/source/blender/freestyle/intern/stroke/ChainingIterators.h @@ -0,0 +1,364 @@ +// +// Filename : ChainingIterators +// Author : Stephane Grabli +// Purpose : Chaining iterators +// Date of creation : 01/07/2003 +// +/////////////////////////////////////////////////////////////////////////////// + +// +// 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 CHAININGITERATORS_H +# define CHAININGITERATORS_H + +# include <iostream> +# include "../view_map/ViewMap.h" +# include "../view_map/ViewMapIterators.h" +# include "../view_map/ViewMapAdvancedIterators.h" +# include "Predicates1D.h" + +//using namespace ViewEdgeInternal; + +// +// Adjacency iterator used in the chaining process +// +/////////////////////////////////////////////////////////// +class LIB_STROKE_EXPORT AdjacencyIterator{ +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(){ + } + + inline bool isEnd(){ + return _internalIterator.isEnd(); + } + inline bool isBegin(){ + 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; + } + void increment(); + +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: + /*! 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; + } + + /*! Copy constructor */ + ChainingIterator(const ChainingIterator& brother) + : ViewEdgeIterator(brother) { + _restrictToSelection = brother._restrictToSelection; + _restrictToUnvisited = brother._restrictToUnvisited; + _increment = brother._increment; + } + + /*! 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 void init(){} + + /*! 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 ViewEdge * traverse(const AdjacencyIterator &it){ + cerr << "Warning: the traverse method was not defined" << endl; + return 0; + } + + /* 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 void increment() ; + virtual void 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 ViewEdge * traverse(const AdjacencyIterator& it); + +}; + +// +// 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 ViewEdge * traverse(const AdjacencyIterator &it); +}; + +#endif // CHAININGITERATORS_H diff --git a/source/blender/freestyle/intern/stroke/ContextFunctions.cpp b/source/blender/freestyle/intern/stroke/ContextFunctions.cpp new file mode 100755 index 00000000000..b55da1fb0f8 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/ContextFunctions.cpp @@ -0,0 +1,60 @@ + +// +// 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 "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(); + } +} diff --git a/source/blender/freestyle/intern/stroke/ContextFunctions.h b/source/blender/freestyle/intern/stroke/ContextFunctions.h new file mode 100755 index 00000000000..37c98656fa7 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/ContextFunctions.h @@ -0,0 +1,124 @@ +// +// Filename : AdvancedFunctions0D.h +// Author(s) : Stephane Grabli +// Purpose : Functions related to context queries +// Date of creation : 20/12/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 CONTEXT_FUNCTIONS_HPP +# define CONTEXT_FUNCTIONS_HPP + +# include "Canvas.h" +# include "../image/Image.h" +# include "../image/GaussianFilter.h" + +/*! \file ContextFunctions.h + * Interface to access the context related + * information. + */ +// +// Context Functions definitions +// +/////////////////////////////////////////////////////////// +/*! namespace containing all the Context related functions */ +namespace ContextFunctions { + + // GetTimeStamp + LIB_STROKE_EXPORT + /*! Returns the system time stamp */ + 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.f); + + // 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 // CONTEXT_FUNCTIONS_HPP + diff --git a/source/blender/freestyle/intern/stroke/Curve.cpp b/source/blender/freestyle/intern/stroke/Curve.cpp new file mode 100755 index 00000000000..f7b255c3ef4 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Curve.cpp @@ -0,0 +1,818 @@ + +// +// 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 "Curve.h" +#include "CurveIterators.h" +#include "CurveAdvancedIterators.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.f)) + { + _Point2d=__B->point2d(); + _Point3d=__B->point3d(); + } + else if((iB == 0) && (t == 0.f)) + { + _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) +{ + 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()){ + 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()){ + 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()) + { + if((iA->t2d() != 1.f) && (iB->t2d() == 0.f)) + { + __A = iA->A(); + __B = iA->B(); + _t2d=t1+t3-t1*t3; + //_t2d = t3; + } + else if((iA->t2d() == 1.f) && (iB->t2d() != 0.f)) + { + __A = iB->A(); + __B = iB->B(); + //_t2d = t3; + _t2d=t2*t3; + } + + } + //_Point2d=__A->point2d()+_t2d*(__B->point2d()-__A->point2d()); + //_Point3d=__A->point3d()+_t2d*(__B->point3d()-__A->point3d()); + + _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) + return 0; + if(((__A == iVertexB->__A) && (__B == iVertexB->__B)) + || + ((__A == iVertexB->__B) && (__B == iVertexB->__A))) + return __A->getFEdge(*__B); + else 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)); + } + else if(iVertexB->__B == 0) + { + if(iVertexB->__A == __A) + return __B->getFEdge(*(iVertexB->__A)); + else if(iVertexB->__A == __B) + return __A->getFEdge(*(iVertexB->__A)); + } + else 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)); + } + else 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)); + } + else 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); + } + else 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); + } + + cerr << "Warning: you should not be there..." << endl; + + return 0; +} + + + 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); +} + + + // 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(); +// } + + + const SShape * CurvePoint::shape() const +{ + if(__A == 0) + return __B->shape(); + return __A->shape(); +} + + + +// 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(); + //} + + + 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(); +} +// +// 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 +//{ +// // Vec3r edgeA = (_FEdges[0])->orientation2d().normalize(); +// // Vec3r edgeB = (_FEdges[1])->orientation2d().normalize(); +// // return edgeA+edgeB; +// // +// 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 +//{ +// // 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)); +// +// 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()); +//} + + +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.f, 0.f); + //return point_iterator(_Vertices.begin(), second, _nSegments, step, 0.f, 0.f); +} +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.f, 0.f); + //return const_point_iterator(_Vertices.begin(), second, _nSegments, step, 0.f, 0.f); +} +Curve::point_iterator Curve::points_end(float step) +{ + return point_iterator(_Vertices.end(), _Vertices.end(), _Vertices.begin(), _Vertices.end(), _nSegments, step, 1.f, _Length); + //return point_iterator(_Vertices.end(), _Vertices.end(), _nSegments, step, 1.f, _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.f, _Length); + //return const_point_iterator(_Vertices.end(), _Vertices.end(), _nSegments, step, 1.f, _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.f, 0.f); +} + +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.f, _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.f, 0.f)); + 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.f, _Length)); + return ret; +} + +Interface0DIterator Curve::verticesBegin(){ + return pointsBegin(0); +} + +Interface0DIterator Curve::verticesEnd(){ + return pointsEnd(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); */ +/* // local_depth_variance_functor<Point> functor; */ +/* // float result; */ +/* // Evaluate<float, local_depth_variance_functor<Point> >(&functor, iCombination, result); */ +/* // return result; */ +/* } */ + +/* */ +/* real Curve::local_average_density(float sigma, int iCombination ) const */ +/* { */ +/* return density_edge_function<Curve >(this, iCombination); */ +/* // density_functor<Point> functor; */ +/* // real result; */ +/* // Evaluate<real, density_functor<Point> >(&functor, iCombination, result); */ +/* // return result; */ +/* } */ + +#define EPS_CURVA_DIR 0.01 + + +void Curve::computeCurvatureAndOrientation () +{ +// 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; +// } + +} diff --git a/source/blender/freestyle/intern/stroke/Curve.h b/source/blender/freestyle/intern/stroke/Curve.h new file mode 100755 index 00000000000..400f27e5d5a --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Curve.h @@ -0,0 +1,463 @@ +// +// Filename : Curve.h +// Author(s) : Stephane Grabli +// Purpose : Class to define a container for curves +// Date of creation : 11/01/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 CURVE_H +# define CURVE_H + +# include <deque> +# include "../system/BaseIterator.h" +# include "../geometry/Geom.h" +//# include "../scene_graph/Material.h" +# include "../view_map/Silhouette.h" +# include "../view_map/SilhouetteGeomEngine.h" +# include "../view_map/Interface0D.h" +# include "../view_map/Interface1D.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;} + //inline const float t3d() const {return _t3d;} + + /* 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;} + //inline void SetT3d(float t) {_t3d = t;} + + /* Information access interface */ + + FEdge *fedge() ; + inline const Vec3r& point2d() const {return _Point2d;} + inline const Vec3r& point3d() const {return _Point3d;} + Vec3r normal() const ; + //Material 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 ; + // 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 ; + + 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() ; + + /* + 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 + * oplyline constituing the Curve. + */ + inline unsigned int nSegments() const {return _nSegments;} + + inline void setId(const Id& id){_Id = id;} + /* Information access interface */ + + + //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 ; + //Material 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 ; + + /* 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.f); + CurveInternal::CurvePointIterator curvePointsEnd(float t=0.f); + + 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.f); + /*! 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.f); +}; + + + +#endif diff --git a/source/blender/freestyle/intern/stroke/CurveAdvancedIterators.h b/source/blender/freestyle/intern/stroke/CurveAdvancedIterators.h new file mode 100755 index 00000000000..dfc9f2719f8 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/CurveAdvancedIterators.h @@ -0,0 +1,378 @@ +// +// Filename : CurveAdvancedIterators.h +// Author(s) : Stephane Grabli +// Purpose : Iterators used to iterate over the elements of the Curve +// Can't be used in python +// Date of creation : 01/08/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 ADVANCEDCURVEITERATORS_H +# define ADVANCEDCURVEITERATORS_H + +# 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; + + // public: + // typedef Vertex vertex_type ; + // typedef vertex_container_iterator vertex_iterator_type; + // typedef CurvePoint<Vertex> Point; + // typedef Point point_type; + typedef IteratorBase<Traits,BidirectionalIteratorTag_Traits> parent_class; + //# 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 + friend class Curve; + //friend class Curve::vertex_iterator; + //friend class __point_iterator<CurvePoint_nonconst_traits >; + //friend class iterator; + //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: + + public: + inline __point_iterator(float step = 0.f) + : parent_class() + { + _step = step; + _CurvilinearLength = 0.f; + _t = 0.f; + _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.f, float iCurvilinearLength = 0.f) + : 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; + } + inline Self operator++(int) // opérateur correspondant à i++ + { // c.a.d qui renvoie la valeur *puis* incrémente. + Self tmp = *this; // C'est pour cela qu'on stocke la valeur + increment(); // dans un temporaire. + return tmp; + } + inline Self& operator--() // operator corresponding to ++i + { + decrement(); + return *this; + } + inline Self operator--(int) // opérateur correspondant à i++ + { // c.a.d qui renvoie la valeur *puis* incrémente. + Self tmp = *this; // C'est pour cela qu'on stocke la valeur + decrement(); // dans un temporaire. + 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*());} + + public: + 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.f)) + { + // we're setting the iterator to end + ++__A; + ++__B; + ++_currentn; + _t = 0.f; + 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.f; + 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.f; // 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.f; + else + { + _t = 0.f; + ++_currentn; + ++__A;++__B; + } + } + } + virtual void decrement() + { + if(_Point != 0) + { + delete _Point; + _Point = 0; + } + + if(_t == 0.f) //we're at the beginning of the edge + { + _t = 1.f; + --_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.f; // We just need a negative value here + + // round value + if(fabs(_t) < (float)M_EPSILON) + _t = 0.0; + if(_t < 0) + { + if(_currentn == 0) + _CurvilinearLength = 0.f; + else + _CurvilinearLength += normAB*(-_t); + _t = 0.f; + } + } + }; + + + +} // end of namespace StrokeInternal + + +#endif // ADVANCEDCURVEITERATORS_H diff --git a/source/blender/freestyle/intern/stroke/CurveIterators.h b/source/blender/freestyle/intern/stroke/CurveIterators.h new file mode 100755 index 00000000000..92f8bf065f4 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/CurveIterators.h @@ -0,0 +1,295 @@ +// +// Filename : CurveIterators.h +// Author(s) : Stephane Grabli +// Purpose : Iterators used to iterate over the elements of the Curve +// Date of creation : 01/08/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 CURVEITERATORS_H +# define CURVEITERATORS_H + +#include "Stroke.h" +#include "Curve.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: + + public: + inline CurvePointIterator(float step = 0.f) + : Interface0DIteratorNested() + { + _step = step; + _CurvilinearLength = 0.f; + _t = 0.f; + //_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.f, float iCurvilinearLength = 0.f) + : 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*());} + public: + 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 void increment() + { + if((_currentn == _n-1) && (_t == 1.f)) + { + // we're setting the iterator to end + ++__A; + ++__B; + ++_currentn; + _t = 0.f; + return; + } + + 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.f; + return; + } + ++__B; + ++__A; + ++_currentn; + return; + } + + // 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.f; // 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.f; + else + { + _t = 0.f; + ++_currentn; + ++__A;++__B; + } + } + } + virtual void decrement() + { + if(_t == 0.f) //we're at the beginning of the edge + { + _t = 1.f; + --_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 -= (float)vec_tmp.norm(); + _t = 0; + return; + } + + // 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.f; // We just need a negative value here + + // round value + if(fabs(_t) < (float)M_EPSILON) + _t = 0.0; + if(_t < 0) + { + if(_currentn == 0) + _CurvilinearLength = 0.f; + else + _CurvilinearLength += normAB*(-_t); + _t = 0.f; + } + } + + virtual float t() const{ + return _CurvilinearLength; + } + virtual float u() const{ + return _CurvilinearLength/_CurveLength; + } + }; + + + +} // end of namespace StrokeInternal + +#endif // CURVEITERATORS_H diff --git a/source/blender/freestyle/intern/stroke/Modifiers.h b/source/blender/freestyle/intern/stroke/Modifiers.h new file mode 100755 index 00000000000..c3be65ffc89 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Modifiers.h @@ -0,0 +1,71 @@ +// +// Filename : Modifiers.h +// Author : Stephane Grabli +// Purpose : modifiers... +// Date of creation : 05/01/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 MODIFIERS_H +# define MODIFIERS_H + +# 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 100755 index 00000000000..591bb157392 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Module.h @@ -0,0 +1,72 @@ +// +// Filename : Module.h +// Author(s) : Emmanuel Turquin +// Purpose : Set the type of the module +// Date of creation : 01/07/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 MODULE_H +# define MODULE_H + +# 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 // MODULE_H diff --git a/source/blender/freestyle/intern/stroke/Operators.cpp b/source/blender/freestyle/intern/stroke/Operators.cpp new file mode 100755 index 00000000000..121dee3ba26 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Operators.cpp @@ -0,0 +1,862 @@ + +// +// 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 <algorithm> +#include "Operators.h" +#include "Canvas.h" +#include "Stroke.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; + +void Operators::select(UnaryPredicate1D& pred) { + if (!_current_set) + return; + if(_current_set->empty()) + return; + 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)){ + 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; +} + + +void Operators::chain(ViewEdgeInternal::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; + do { + new_chain->push_viewedge_back(*it, it.getOrientation()); + modifier(**it); + ++it; + } while (!it.isEnd() && !pred(**it)); + + _current_chains_set.push_back(new_chain); + } + + if (!_current_chains_set.empty()) + _current_set = &_current_chains_set; +} + + +void Operators::chain(ViewEdgeInternal::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; + do { + new_chain->push_viewedge_back(*it, it.getOrientation()); + ts(**it); + ++it; + } while (!it.isEnd() && !pred(**it) && !pred_ts(**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, +// 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; +// //ViewEdgeIterator it_back(it);--it_back; // FIXME +// 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; + // //ViewEdgeIterator it_back(it);--it_back;//FIXME + // 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; + //} + +void Operators::bidirectionalChain(ChainingIterator& 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); + // re-init iterator + it.setBegin(edge); + it.setCurrentEdge(edge); + it.setOrientation(true); + it.init(); + + Chain* new_chain = new Chain(id);++id; + //ViewEdgeIterator it_back(it);--it_back;//FIXME + do { + new_chain->push_viewedge_back(*it, it.getOrientation()); + ts(**it); + it.increment(); // FIXME + } while (!it.isEnd() && !pred(**it)); + it.setBegin(edge); + it.setCurrentEdge(edge); + it.setOrientation(true); + it.decrement(); // FIXME + while (!it.isEnd() && !pred(**it)) { + new_chain->push_viewedge_front(*it, it.getOrientation()); + ts(**it); + it.decrement();// FIXME + } + _current_chains_set.push_back(new_chain); + } + + if (!_current_chains_set.empty()) + _current_set = &_current_chains_set; +} + +void Operators::bidirectionalChain(ChainingIterator& it) { + 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_ts(**it_edge)) + continue; + + edge = dynamic_cast<ViewEdge*>(*it_edge); + // re-init iterator + it.setBegin(edge); + it.setCurrentEdge(edge); + it.setOrientation(true); + it.init(); + + Chain* new_chain = new Chain(id);++id; + //ViewEdgeIterator it_back(it);--it_back;//FIXME + do { + new_chain->push_viewedge_back(*it, it.getOrientation()); + ts(**it); + it.increment(); // FIXME + } while (!it.isEnd()); + it.setBegin(edge); + it.setCurrentEdge(edge); + it.setOrientation(true); + it.decrement(); // FIXME + while (!it.isEnd()) { + new_chain->push_viewedge_front(*it, it.getOrientation()); + ts(**it); + it.decrement();// FIXME + } + _current_chains_set.push_back(new_chain); + } + + if (!_current_chains_set.empty()) + _current_set = &_current_chains_set; +} + +void Operators::sequentialSplit(UnaryPredicate0D& pred, + float sampling) +{ + if (_current_chains_set.empty()) { + cerr << "Warning: current set empty" << endl; + return; + } + 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)) && (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; + } + + 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(); + _current_chains_set = splitted_chains; + splitted_chains.clear(); + + if (!_current_chains_set.empty()) + _current_set = &_current_chains_set; +} + +void Operators::sequentialSplit(UnaryPredicate0D& startingPred, UnaryPredicate0D& stoppingPred, + float sampling) +{ + if (_current_chains_set.empty()) { + cerr << "Warning: current set empty" << endl; + return; + } + 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; + }while((itStop!=end) && (!stoppingPred(itStop))); + 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; + }while((itStart!=end) && (!startingPred(itStart))); + }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(); + _current_chains_set = splitted_chains; + splitted_chains.clear(); + + if (!_current_chains_set.empty()) + _current_set = &_current_chains_set; +} + +#include "CurveIterators.h" + +// Internal function +void __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; + } + + 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;++it;//func(it0d);++it; + CurveInternal::CurvePointIterator next = it;++next; + real tmp; + + bool bsplit = false; + for(; ((it != end) && (next != end)); ++it,++next){ + it0d = it.CastToInterface0DIterator(); + tmp = func(it0d); + if(tmp < _min){ + _min = tmp; + split = it; + bsplit = true; + } + } + + if(!bsplit){ // we didn't find any minimum + newChains.push_back(_curve); + return; + } + + // 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)&&(split._CurvilinearLength-vit._CurvilinearLength> 0.001); ++vit,++vnext){ + new_curve_a->push_vertex_back(&(*vit)); + } + if((vit==vitend) || (vnext == vitend)){ + cout << "The split takes place in bad location" << endl; + newChains.push_back(_curve); + delete new_curve_a; + delete new_curve_b; + return; + } + + // build the two resulting chains + if(fabs(vit._CurvilinearLength-split._CurvilinearLength) > 0.001){ + new_curve_a->push_vertex_back(&(*split)); + new_curve_b->push_vertex_back(&(*split)); + } + else{ + new_curve_a->push_vertex_back(&(*vit)); + } + + for(;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)) || (pred(*new_curve_b))){ + // we don't actually create these two chains + newChains.push_back(_curve); + delete new_curve_a; + delete new_curve_b; + return; + } + // 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); +} + +void Operators::recursiveSplit(UnaryFunction0D<double>& func, UnaryPredicate1D& pred, float sampling) +{ + if (_current_chains_set.empty()) { + cerr << "Warning: current set empty" << endl; + return; + } + + 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)){ + __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(); + _current_chains_set = newChains; + newChains.clear(); + + if (!_current_chains_set.empty()) + _current_set = &_current_chains_set; +} + + +// recursive split with pred 0D +void __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; + } + + 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 = func(it0d);++it; + real _min = FLT_MAX;++it; + real mean = 0.f; + real variance = 0.f; + unsigned count = 0; + CurveInternal::CurvePointIterator next = it;++next; + real tmp; + + bool bsplit = false; + for(; ((it != end) && (next != end)); ++it,++next){ + ++count; + it0d = it.CastToInterface0DIterator(); + if(!pred0d(it0d)) + continue; + tmp = func(it0d); + mean += tmp; + if(tmp < _min){ + _min = tmp; + 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; + } + + // 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)&&(split._CurvilinearLength-vit._CurvilinearLength> 0.001); ++vit,++vnext){ + new_curve_a->push_vertex_back(&(*vit)); + } + if((vit==vitend) || (vnext == vitend)){ + cout << "The split takes place in bad location" << endl; + newChains.push_back(_curve); + delete new_curve_a; + delete new_curve_b; + return; + } + + // build the two resulting chains + if(fabs(vit._CurvilinearLength-split._CurvilinearLength) > 0.001){ + new_curve_a->push_vertex_back(&(*split)); + new_curve_b->push_vertex_back(&(*split)); + } + else{ + new_curve_a->push_vertex_back(&(*vit)); + } + + for(;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)) || (pred(*new_curve_b))){ + // we don't actually create these two chains + newChains.push_back(_curve); + delete new_curve_a; + delete new_curve_b; + return; + } + // 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); +} + +void Operators::recursiveSplit(UnaryFunction0D<double>& func, UnaryPredicate0D& pred0d, UnaryPredicate1D& pred, float sampling) +{ + if (_current_chains_set.empty()) { + cerr << "Warning: current set empty" << endl; + return; + } + + 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)){ + __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(); + _current_chains_set = newChains; + newChains.clear(); + + if (!_current_chains_set.empty()) + _current_set = &_current_chains_set; +} +// Internal class +class PredicateWrapper +{ +public: + + inline PredicateWrapper(BinaryPredicate1D& pred) { + _pred = &pred; + } + + inline bool operator()(Interface1D* i1, Interface1D* i2) { + return (*_pred)(*i1, *i2); + } + +private: + + BinaryPredicate1D* _pred; +}; + +void Operators::sort(BinaryPredicate1D& pred) { + if (!_current_set) + return; + std::sort(_current_set->begin(), _current_set->end(), PredicateWrapper(pred)); +} + +Stroke* createStroke(Interface1D& inter) { + Stroke* stroke = new Stroke; + stroke->SetId(inter.getId()); + + float currentCurvilignAbscissa = 0.f; + + Interface0DIterator it = inter.verticesBegin(), itend = inter.verticesEnd(); + Interface0DIterator itfirst = it; + + Vec3r current(it->getProjectedX(), it->getProjectedY(), it->getProjectedZ()); + Vec3r previous = current; + SVertex* sv; + CurvePoint* cp; + StrokeVertex* stroke_vertex; + + 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->point2d(); + Vec3r vec_tmp(current - previous); + currentCurvilignAbscissa += vec_tmp.norm(); + 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->point2d(); + Vec3r vec_tmp(current - previous); + currentCurvilignAbscissa += vec_tmp.norm(); + stroke_vertex->SetCurvilinearAbscissa(currentCurvilignAbscissa); + stroke->push_back(stroke_vertex); + } + stroke->SetLength(currentCurvilignAbscissa); + return stroke; +} + + +inline void applyShading(Stroke& stroke, vector<StrokeShader*>& shaders) { + for (vector<StrokeShader*>::iterator it = shaders.begin(); + it != shaders.end(); + ++it) + (*it)->shade(stroke); +} + + +void Operators::create(UnaryPredicate1D& pred, vector<StrokeShader*> shaders) { + Canvas* canvas = Canvas::getInstance(); + if (!_current_set) { + cerr << "Warning: current set empty" << endl; + return; + } + for (Operators::I1DContainer::iterator it = _current_set->begin(); + it != _current_set->end(); + ++it) { + if (!pred(**it)) + continue; + Stroke* stroke = createStroke(**it); + if (stroke) { + applyShading(*stroke, shaders); + canvas->RenderStroke(stroke); + _current_strokes_set.push_back(stroke); + } + } +} + + +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(); + _current_view_edges_set.insert(_current_view_edges_set.begin(), + vm->ViewEdges().begin(), + vm->ViewEdges().end()); + _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 100755 index 00000000000..96ecd0aa75b --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Operators.h @@ -0,0 +1,311 @@ +// +// Filename : Operators.h +// Author(s) : Stephane Grabli, Emmanuel Turquin +// Purpose : Class gathering stroke creation algorithms +// Date of creation : 01/07/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 OPERATORS_H +# define OPERATORS_H + +# include <vector> +# include <iostream> +# include "../view_map/Interface1D.h" +# include "Predicates1D.h" +# include "Predicates0D.h" +# include "../view_map/ViewMap.h" +# include "Chain.h" +# include "ChainingIterators.h" +# include "../system/TimeStamp.h" +# include "StrokeShader.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 void 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 void 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 void 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 void 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 void 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 void sequentialSplit(UnaryPredicate0D& startingPred, UnaryPredicate0D& stoppingPred, + float sampling = 0.f); + + /*! 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 void sequentialSplit(UnaryPredicate0D& pred, + float sampling = 0.f); + + /*! 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 void 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 void recursiveSplit(UnaryFunction0D<double>& func, UnaryPredicate0D& pred0d, UnaryPredicate1D& pred, float sampling = 0); + + /*! 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 void 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 void 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 // OPERATORS_H diff --git a/source/blender/freestyle/intern/stroke/PSStrokeRenderer.cpp b/source/blender/freestyle/intern/stroke/PSStrokeRenderer.cpp new file mode 100755 index 00000000000..dc9c94e0e11 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/PSStrokeRenderer.cpp @@ -0,0 +1,89 @@ + +// +// 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 "PSStrokeRenderer.h" +# include "Canvas.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 100755 index 00000000000..821c016ab91 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/PSStrokeRenderer.h @@ -0,0 +1,63 @@ +// +// Filename : PSStrokeRenderer.h +// Author(s) : Stephane Grabli +// Purpose : Class to define the Postscript rendering of a stroke +// Date of creation : 10/26/2004 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 PSSTROKERENDERER_H +# define PSSTROKERENDERER_H + +# include "../system/FreestyleConfig.h" +# include "StrokeRenderer.h" +# include <fstream> + +/**********************************/ +/* */ +/* */ +/* 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 // PSSTROKERENDERER_H + diff --git a/source/blender/freestyle/intern/stroke/Predicates0D.h b/source/blender/freestyle/intern/stroke/Predicates0D.h new file mode 100755 index 00000000000..4c2dfacdf98 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Predicates0D.h @@ -0,0 +1,160 @@ +// +// Filename : Predicates0D.h +// Author(s) : Stephane Grabli, Emmanuel Turquin +// Purpose : Class gathering stroke creation algorithms +// Date of creation : 01/07/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 PREDICATES0D_H +# define PREDICATES0D_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: + /*! Default constructor. */ + UnaryPredicate0D() {} + /*! 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 bool operator()(Interface0DIterator& it) { + cerr << "Warning: operator() not implemented" << endl; + return false; + } +}; + + +// +// 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: + /*! Default constructor. */ + BinaryPredicate0D() {} + /*! 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 bool operator()(Interface0D& inter1, Interface0D& inter2) { + cerr << "Warning: operator() not implemented" << endl; + return false; + } +}; + + +// +// 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. */ + bool operator()(Interface0DIterator&) { + return true; + } + }; + + // FalseUP0D + /*! Returns false any time */ + class FalseUP0D : public UnaryPredicate0D + { + public: + /*! Default constructor. */ + FalseUP0D() {} + /*! Returns the string "FalseUP0D"*/ + string getName() const { + return "FalseUP0D"; + } + /*! The () operator. */ + bool operator()(Interface0DIterator&) { + return false; + } + }; + +} // end of namespace Predicates0D + +#endif // PREDICATES0D_H diff --git a/source/blender/freestyle/intern/stroke/Predicates1D.h b/source/blender/freestyle/intern/stroke/Predicates1D.h new file mode 100755 index 00000000000..cf9a3283ae4 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Predicates1D.h @@ -0,0 +1,438 @@ +// +// Filename : Predicates1D.h +// Author(s) : Stephane Grabli, Emmanuel Turquin +// Purpose : Class gathering stroke creation algorithms +// Date of creation : 01/07/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 PREDICATES1D_H +# define PREDICATES1D_H + +# include <string> +# include "../system/TimeStamp.h" +# include "../view_map/Interface1D.h" +# include "../view_map/Functions1D.h" +# include "AdvancedFunctions1D.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: + /*! Default constructor. */ + UnaryPredicate1D() {} + /*! 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 bool operator()(Interface1D& inter) { + cerr << "Warning: operator() not implemented" << endl; + return false; + } +}; + + +// +// 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: + /*! Default constructor. */ + BinaryPredicate1D() {} + /*! 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 bool operator()(Interface1D& inter1, Interface1D& inter2) { + cerr << "Warning: operator() not implemented" << endl; + return false; + } +}; + + +// +// 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 */ + bool operator()(Interface1D&) { + return true; + } + }; + + // FalseUP1D + /*! Returns false */ + class FalseUP1D : public UnaryPredicate1D + { + public: + /*! Constructor */ + FalseUP1D() {} + /*! Returns the string "FalseUP1D"*/ + string getName() const { + return "FalseUP1D"; + } + /*! the () operator */ + bool operator()(Interface1D&) { + return false; + } + }; + + // 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 */ + bool operator()(Interface1D& inter) { + Functions1D::QuantitativeInvisibilityF1D func; + return (func(inter) == _qi); + } + 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. */ + bool operator()(Interface1D& inter) { + if((_getNature(inter) & Nature::SILHOUETTE) || (_getNature(inter) & Nature::BORDER)){ + Interface0DIterator it=inter.verticesBegin(); + for(; !it.isEnd(); ++it){ + if(Functions0D::getOccludeeF0D(it) != Functions0D::getShapeF0D(it)) + return true; + } + } + return false; + } + }; + + // 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. */ + bool operator()(Interface1D& inter) { + if((_getNature(inter) & Nature::SILHOUETTE) || (_getNature(inter) & 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) + return true; + } + } + return false; + } + }; + + // 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. */ + bool operator()(Interface1D& inter) { + return (inter.getTimeStamp() == _timeStamp); + } + }; + + // 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. */ + bool operator()(Interface1D& inter) { + ViewEdge* edge = dynamic_cast<ViewEdge*>(&inter); + if (!edge) + return false; + return (edge->getChainingTimeStamp() >= _timeStamp); + } + }; + + // 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. */ + bool 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) + return true; + } + return false; + } + }; + + // + // Binary Predicates definitions + // + /////////////////////////////////////////////////////////// + + // TrueBP1D + /*! Returns true. */ + class TrueBP1D : public BinaryPredicate1D + { + public: + /*! Returns the string "TrueBP1D"*/ + string getName() const { + return "TrueBP1D"; + } + /*! The () operator. */ + bool operator()(Interface1D& i1, Interface1D& i2) { + return true; + } + }; + + // FalseBP1D + /*! Returns false. */ + class FalseBP1D : public BinaryPredicate1D + { + public: + /*! Returns the string "FalseBP1D"*/ + string getName() const { + return "FalseBP1D"; + } + /*! The () operator. */ + bool operator()(Interface1D& i1, Interface1D& i2) { + return false; + } + }; + + // 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. */ + bool operator()(Interface1D& i1, Interface1D& i2) { + return (i1.getLength2D() > i2.getLength2D()); + } + }; + + // 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. */ + bool 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) + return true; + } + } + return false; + } + }; + + // 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. */ + bool operator()(Interface1D& i1, Interface1D& i2) { + return (_func(i1) > _func(i2)); + } + }; +} // end of namespace Predicates1D + +#endif // PREDICATES1D_H diff --git a/source/blender/freestyle/intern/stroke/QInformationMap.h b/source/blender/freestyle/intern/stroke/QInformationMap.h new file mode 100755 index 00000000000..2542bdba147 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/QInformationMap.h @@ -0,0 +1,58 @@ +// +// Filename : QInformationMap.h +// Author : Stephane Grabli +// Purpose : Class defining an information map using a QImage +// Date of creation : 04/01/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 QINFORMATIONMAP_H +# define QINFORMATIONMAP_H + +# 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) + 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 // QINFORMATIONMAP_H diff --git a/source/blender/freestyle/intern/stroke/Stroke.cpp b/source/blender/freestyle/intern/stroke/Stroke.cpp new file mode 100755 index 00000000000..d6ff4d255c4 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Stroke.cpp @@ -0,0 +1,949 @@ + +// +// 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 "Stroke.h" +#include "StrokeRenderer.h" +#include "StrokeIterators.h" +#include "StrokeAdvancedIterators.h" + + /**********************************/ + /* */ + /* */ + /* StrokeAttribute */ + /* */ + /* */ + /**********************************/ + +StrokeAttribute::StrokeAttribute() +{ + int i; + _alpha = 1.f; + _thickness[0] = 1.f; + _thickness[1] = 1.f; + for(i=0; i<3; ++i) + _color[i] = 0.2f; + _color[0]=0.8; + _userAttributesReal = 0; + _userAttributesVec2f = 0; + _userAttributesVec3f = 0; + _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 = 0; + if(iBrother._userAttributesVec2f) + _userAttributesVec2f = new Vec2fMap(*iBrother._userAttributesVec2f); + else + _userAttributesVec2f = 0; + if(iBrother._userAttributesVec3f) + _userAttributesVec3f = new Vec3fMap(*iBrother._userAttributesVec3f); + else + _userAttributesVec3f = 0; +} +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 = 0; + _userAttributesVec2f = 0; + _userAttributesVec3f = 0; +} +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: a verifier (et a ameliorer) + 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 = 0; + } + 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 = 0; + } + 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 = 0; + } + +} +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 = 0; + } + if(iBrother._userAttributesVec2f){ + if(!_userAttributesVec2f) + _userAttributesVec2f = new Vec2fMap; + _userAttributesVec2f = new Vec2fMap(*(iBrother._userAttributesVec2f)); + }else{ + _userAttributesVec2f = 0; + } + if(iBrother._userAttributesVec3f){ + if(!_userAttributesVec3f) + _userAttributesVec3f = new Vec3fMap; + _userAttributesVec3f = new Vec3fMap(*(iBrother._userAttributesVec3f)); + }else{ + _userAttributesVec3f = 0; + } + return *this; +} + +float StrokeAttribute::getAttributeReal(const char *iName) const{ + if(!_userAttributesReal){ + cout << "StrokeAttribute warning: no real attribute was defined"<< endl; + return 0; + } + realMap::iterator a = _userAttributesReal->find(iName); + if(a ==_userAttributesReal->end()){ + cout << "StrokeAttribute warning: no real attribute was added with the name " << iName << endl; + return 0; + } + return (*a).second; +} +Vec2f StrokeAttribute::getAttributeVec2f(const char *iName) const{ + if(!_userAttributesVec2f){ + cout << "StrokeAttribute warning: no Vec2f attribute was defined "<< endl; + return 0; + } + Vec2fMap::iterator a = _userAttributesVec2f->find(iName); + if(a ==_userAttributesVec2f->end()){ + 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){ + cout << "StrokeAttribute warning: no Vec3f attribute was defined"<< endl; + return 0; + } + Vec3fMap::iterator a = _userAttributesVec3f->find(iName); + if(a ==_userAttributesVec3f->end()){ + 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.f; + + _StrokeLength = 0.f; +} + + + +StrokeVertex::StrokeVertex(const StrokeVertex& iBrother) + +:CurvePoint(iBrother) +{ + _Attribute = iBrother._Attribute; + + _CurvilignAbscissa = 0.f; + + _StrokeLength = 0.f; +} +StrokeVertex::StrokeVertex(SVertex *iSVertex) + +:CurvePoint(iSVertex,0,0.f) + +{ + + _CurvilignAbscissa = 0.f; + + _StrokeLength = 0.f; + +} + + + +StrokeVertex::StrokeVertex(CurvePoint *iPoint) + +:CurvePoint(*iPoint) + +{ + + _CurvilignAbscissa = 0.f; + + _StrokeLength = 0.f; + +} + + + +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.f) + +{ + + _Attribute = iAttribute; + + _CurvilignAbscissa = 0.f; + + _StrokeLength = 0.f; + +} +StrokeVertex::~StrokeVertex() +{ +} + +StrokeVertex& StrokeVertex::operator=(const StrokeVertex& iBrother) +{ + ((CurvePoint*)this)->operator=(iBrother); + _Attribute = iBrother._Attribute; + + _CurvilignAbscissa = 0.f; + + _StrokeLength = 0.f; + return *this; +} + + /**********************************/ + /* */ + /* */ + /* Stroke */ + /* */ + /* */ + /**********************************/ + +Stroke::Stroke() +{ + _Length = 0; + _id = 0; + _sampling = FLT_MAX; + //_mediumType = DEFAULT_STROKE; + _mediumType = OPAQUE_MEDIUM; + _textureId = 0; + _tips = false; + _rep = 0; +} + +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; + _rep = new StrokeRep(*(iBrother._rep)); +} + +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 != 0) + { + delete _rep; + _rep = 0; + } +} + +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 <= _Vertices.size()) + 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.f; + StrokeVertex * newVertex = 0; + vector<StrokeSegment> strokeSegments; + int N=0; + float meanlength = 0; + int nsegments = 0; + while(((it!=itend)&&(next!=itend))) + { + Vec3r a((it)->point2d()); + Vec3r b((next)->point2d()); + Vec3r 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.f) + 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.f)) + 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.f; + vertex_container newVertices; + real t=0.f; + StrokeVertex * newVertex = 0; + StrokeInternal::StrokeVertexIterator it = strokeVerticesBegin(); + StrokeInternal::StrokeVertexIterator next = it;++next; + StrokeInternal::StrokeVertexIterator itend = strokeVerticesEnd(); + while(((it!=itend)&&(next!=itend))) + { + newVertices.push_back(&(*it)); + Vec3r a((it)->point2d()); + Vec3r b((next)->point2d()); + Vec3r 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; + float limit = 0.99f; + 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; + } + } + // recompute various values (length, curvilign abscissa) + float curvabsc = 0.f; + it=_Vertices.begin(); + itend=_Vertices.end(); + vertex_container::iterator previous=it; + for(; + (it!=itend); + ++it) + { + if(it != previous) + curvabsc += ((*it)->point2d()-(*previous)->point2d()).norm(); + (*it)->SetCurvilinearAbscissa(curvabsc); + previous = it; + } + _Length = curvabsc; + it=_Vertices.begin(); + for(; + (it!=itend); + ++it) + { + (*it)->SetStrokeLength(_Length); + } +} + +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); + // recompute various values (length, curvilign abscissa) + float curvabsc = 0.f; + it=_Vertices.begin(); + itend=_Vertices.end(); + vertex_container::iterator previous=it; + for(; + (it!=itend); + ++it) + { + curvabsc += ((*it)->point2d()-(*previous)->point2d()).norm(); + (*it)->SetCurvilinearAbscissa(curvabsc); + previous = it; + } + _Length = curvabsc; + for(; + (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::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(); +} +// +//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; +//} +////inline const polygon3d& occludee() const {return *(_FEdgeA->aFace());} +//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); +// } diff --git a/source/blender/freestyle/intern/stroke/Stroke.h b/source/blender/freestyle/intern/stroke/Stroke.h new file mode 100755 index 00000000000..a5cf51f8224 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/Stroke.h @@ -0,0 +1,584 @@ +// +// Filename : Stroke.h +// Author(s) : Stephane Grabli +// Purpose : Classes to define a stroke +// Date of creation : 09/09/2002 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 STROKE_H +# define STROKE_H + +# include <vector> +# include <map> +# include "../system/FreestyleConfig.h" +# include "../view_map/Silhouette.h" +# include "Curve.h" +# include "../view_map/Interface1D.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. + * Is 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); + + /* Render method */ + 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.f); + 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.f); + /*! 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();} + + // 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.f); + virtual Interface0DIterator pointsEnd(float t=0.f); +}; + + + +// +// 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 // STROKE_H diff --git a/source/blender/freestyle/intern/stroke/StrokeAdvancedIterators.h b/source/blender/freestyle/intern/stroke/StrokeAdvancedIterators.h new file mode 100755 index 00000000000..279a0b12089 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeAdvancedIterators.h @@ -0,0 +1,142 @@ +// +// Filename : StrokeAdvancedIterators.h +// Author(s) : Stephane Grabli +// Purpose : Iterators used to iterate over the elements of the Stroke +// Can't be used in python +// Date of creation : 01/07/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 STROKEADVANCEDITERATORS_H +# define STROKEADVANCEDITERATORS_H + +# 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); + } + inline Self operator++(int) // opérateur correspondant à i++ + { + Self tmp = *this; // C'est pour cela qu'on stocke la valeur + ++_it; // dans un temporaire. + return tmp; + } + inline Self& operator--() // operator corresponding to ++i + { + --_it; + return *(this); + } + inline Self operator--(int) // opérateur correspondant à i++ + { // c.a.d qui renvoie la valeur *puis* incrémente. + Self tmp = *this; // C'est pour cela qu'on stocke la valeur + --_it; // dans un temporaire. + 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 // STROKEADVANCEDITERATORS_H + + diff --git a/source/blender/freestyle/intern/stroke/StrokeIO.cpp b/source/blender/freestyle/intern/stroke/StrokeIO.cpp new file mode 100755 index 00000000000..7f540939fa1 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeIO.cpp @@ -0,0 +1,55 @@ +// +// 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 "StrokeIO.h" +#include "StrokeAdvancedIterators.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; +}
\ No newline at end of file diff --git a/source/blender/freestyle/intern/stroke/StrokeIO.h b/source/blender/freestyle/intern/stroke/StrokeIO.h new file mode 100755 index 00000000000..42b99420f4a --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeIO.h @@ -0,0 +1,47 @@ +// +// Filename : StrokeIO.h +// Author(s) : Stephane Grabli +// Purpose : Functions to manage I/O for the stroke +// Date of creation : 03/02/2004 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 STROKEIO_H +# define STROKEIO_H + +# include <iostream> +# include "../system/FreestyleConfig.h" +# include "Stroke.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 // STROKEIO_H
\ No newline at end of file diff --git a/source/blender/freestyle/intern/stroke/StrokeIterators.h b/source/blender/freestyle/intern/stroke/StrokeIterators.h new file mode 100755 index 00000000000..9cc08a0532a --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeIterators.h @@ -0,0 +1,227 @@ +// +// Filename : StrokeIterators.h +// Author(s) : Stephane Grabli +// Purpose : Iterators used to iterate over the elements of the Stroke +// Date of creation : 01/07/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 STROKEITERATORS_H +# define STROKEITERATORS_H + +# 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 void increment() { + ++_it; + } + + /*! Decrements. */ + virtual void decrement() { + --_it; + } + + /*! 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 // STROKEITERATORS_H + + diff --git a/source/blender/freestyle/intern/stroke/StrokeLayer.cpp b/source/blender/freestyle/intern/stroke/StrokeLayer.cpp new file mode 100755 index 00000000000..8b469399ca8 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeLayer.cpp @@ -0,0 +1,55 @@ + +// +// 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 "StrokeLayer.h" +#include "Stroke.h" +#include "Canvas.h" + +StrokeLayer::~StrokeLayer() +{ + clear(); +} + +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 100755 index 00000000000..b89b77a85a7 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeLayer.h @@ -0,0 +1,75 @@ +// +// Filename : StrokeLayer.h +// Author : Stephane Grabli +// Purpose : Class to define a layer of strokes. +// Date of creation : 18/12/2002 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 STROKELAYER_H +# define STROKELAYER_H + +# 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 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 // STROKELAYER_H diff --git a/source/blender/freestyle/intern/stroke/StrokeRenderer.cpp b/source/blender/freestyle/intern/stroke/StrokeRenderer.cpp new file mode 100755 index 00000000000..e747fb4f5cd --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeRenderer.cpp @@ -0,0 +1,146 @@ + +// +// 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 "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; +} + +// unsigned int StrokeRenderer::getPaperTextureIndex(unsigned int index) +// { + // return _textureManager->getPaperTextureIndex(index); +// } + + +/**********************************/ +/* */ +/* */ +/* TextureManager */ +/* */ +/* */ +/**********************************/ + + +LIB_STROKE_EXPORT +TextureManager* TextureManager::_pInstance = 0; + +LIB_STROKE_EXPORT +vector<string> TextureManager::_papertextures; + +LIB_STROKE_EXPORT +string TextureManager::_patterns_path; + +LIB_STROKE_EXPORT +string TextureManager::_brushes_path; + +TextureManager::TextureManager () +{ + _papertexname = NULL; + _hasLoadedTextures=false; + _pInstance = this; + _defaultTextureId = 0; +} + +TextureManager::~TextureManager () +{ + if (_papertexname) + delete _papertexname; + if(!_brushesMap.empty()) + _brushesMap.clear(); + _pInstance = 0; +} + +void TextureManager::load() +{ + if(_hasLoadedTextures) + return ; + loadPapers(); + 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; + cout << "brush file " << name << " not found" << endl; + return 0; + }else{ + return _brushesMap[bt]; + } +} + +vector<string>& TextureManager::Options::getPaperTextures() { + return _papertextures; +} + +void TextureManager::Options::setPaperTextures(const vector<string>& sl) { + _papertextures = sl; +} + +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; +} + +unsigned TextureManager::getPaperTexturesNumber() { + return _papertextures.size(); +} + diff --git a/source/blender/freestyle/intern/stroke/StrokeRenderer.h b/source/blender/freestyle/intern/stroke/StrokeRenderer.h new file mode 100755 index 00000000000..d192fe30145 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeRenderer.h @@ -0,0 +1,140 @@ +// +// Filename : StrokeRenderer.h +// Author(s) : Fredo Durand +// Purpose : Classes to render a stroke with OpenGL +// Date of creation : 09/09/2002 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 STROKE_RENDERER_H +# define STROKE_RENDERER_H + +# include <vector> +# include <map> +# include <utility> +# include "../system/FreestyleConfig.h" +# include "Stroke.h" +# include "StrokeRep.h" +# include <string.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) ; + unsigned getPaperTextureIndex(unsigned i) { return _papertexname[i]; } + + static unsigned getPaperTexturesNumber(); + inline bool hasLoaded() const {return _hasLoadedTextures;} + inline unsigned int getDefaultTextureId() const {return _defaultTextureId;} + + struct LIB_STROKE_EXPORT Options + { + static void setPaperTextures(const vector<string>& sl); + static vector<string>& getPaperTextures(); + + static void setPatternsPath(const string& path); + static string getPatternsPath(); + + static void setBrushesPath(const string& path); + static string getBrushesPath(); + }; + + protected: + virtual void loadPapers() = 0; + 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; + unsigned* _papertexname; + static string _patterns_path; + static string _brushes_path; + static vector<string> _papertextures; + 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 unsigned int getPaperTextureIndex(unsigned int index) ; + static TextureManager *_textureManager; +}; + + +#endif // STROKE_RENDERER_H diff --git a/source/blender/freestyle/intern/stroke/StrokeRep.cpp b/source/blender/freestyle/intern/stroke/StrokeRep.cpp new file mode 100755 index 00000000000..055e1fb1be4 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeRep.cpp @@ -0,0 +1,820 @@ + +// +// 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 "StrokeRep.h" +#include "Stroke.h" +#include "StrokeRenderer.h" +#include "StrokeAdvancedIterators.h" +#include "StrokeIterators.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 1e4 + +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); +} + +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(); + const float *thickness = sv->attribute().getThickness(); + _vertices.push_back(new StrokeVertexRep(sv->getPoint()+thickness[1]*orthDir)); + _vertices.push_back(new StrokeVertexRep(sv->getPoint()-thickness[0]*orthDir)); + + Vec2r stripDir(orthDir); + // check whether the orientation + // was user defined + if(sv->attribute().isAttributeAvailableVec2f("orientation")){ + Vec2r userDir = sv->attribute().getAttributeVec2f("orientation"); + userDir.normalize(); + Vec2r t(orthDir[1], -orthDir[0]); + real dp1 = userDir*orthDir; + real dp2 = userDir*t; + real h = (thickness[1]+thickness[0])/dp1; + real x = fabs(h*dp2/2.0); + if(dp1>0){ + //i'm in the upper part of the unit circle + if(dp2>0){ + //i'm in the upper-right part of the unit circle + // i must move vertex 1 + _vertices[1]->setPoint2d(_vertices[0]->point2d()-userDir*(h)); + //_vertices[0]->setPoint2d(_vertices[0]->point2d()+t*x); + //_vertices[1]->setPoint2d(_vertices[1]->point2d()-t*x); + }else{ + //i'm in the upper-left part of the unit circle + // i must move vertex 0 + _vertices[0]->setPoint2d(_vertices[1]->point2d()+userDir*(h)); + //_vertices[0]->setPoint2d(_vertices[0]->point2d()-t*x); + //_vertices[1]->setPoint2d(_vertices[1]->point2d()+t*x); + } + }else{ + //i'm in the lower part of the unit circle + if(dp2>0){ + //i'm in the lower-right part of the unit circle + // i must move vertex 0 + //_vertices[0]->setPoint2d(_vertices[1]->point2d()-userDir*(h)); + _vertices[0]->setPoint2d(_vertices[0]->point2d()-t*x); + //_vertices[1]->setPoint2d(_vertices[1]->point2d()+t*x); + }else{ + //i'm in the lower-left part of the unit circle + // i must move vertex 1 + _vertices[1]->setPoint2d(_vertices[0]->point2d()+userDir*(h)); + //_vertices[0]->setPoint2d(_vertices[0]->point2d()-t*x); + //_vertices[1]->setPoint2d(_vertices[1]->point2d()-t*x); + } + } + } + + + // 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; + // } + // } + + 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.f); + 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.f); + 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[1]*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(); + const float *thicknessLast = sv->attribute().getThickness(); + _vertices.push_back(new StrokeVertexRep(sv->getPoint()+thicknessLast[1]*orthDir)); + ++i; + _vertices.push_back(new StrokeVertexRep(sv->getPoint()-thicknessLast[0]*orthDir)); + int n = i; + ++i; + + // check whether the orientation + // was user defined + if(sv->attribute().isAttributeAvailableVec2f("orientation")){ + Vec2r userDir = sv->attribute().getAttributeVec2f("orientation"); + userDir.normalize(); + Vec2r t(orthDir[1], -orthDir[0]); + real dp1 = userDir*orthDir; + real dp2 = userDir*t; + real h = (thicknessLast[1]+thicknessLast[0])/dp1; + real x = fabs(h*dp2/2.0); + if(dp1>0){ + //i'm in the upper part of the unit circle + if(dp2>0){ + //i'm in the upper-right part of the unit circle + // i must move vertex n-1 + _vertices[n-1]->setPoint2d(_vertices[n]->point2d()-userDir*(h)); + //_vertices[n-1]->setPoint2d(_vertices[n-1]->point2d()+t*x); + //_vertices[n]->setPoint2d(_vertices[n]->point2d()-t*x); + }else{ + //i'm in the upper-left part of the unit circle + // i must move vertex n + _vertices[n]->setPoint2d(_vertices[n-1]->point2d()+userDir*(h)); + //_vertices[n-1]->setPoint2d(_vertices[n-1]->point2d()-t*x); + //_vertices[n]->setPoint2d(_vertices[n]->point2d()+t*x); + } + }else{ + //i'm in the lower part of the unit circle + if(dp2>0){ + //i'm in the lower-right part of the unit circle + // i must move vertex n + _vertices[n]->setPoint2d(_vertices[n-1]->point2d()-userDir*(h)); + //_vertices[n-1]->setPoint2d(_vertices[n-1]->point2d()-t*x); + //_vertices[n]->setPoint2d(_vertices[n]->point2d()+t*x); + }else{ + //i'm in the lower-left part of the unit circle + // i must move vertex n-1 + _vertices[n-1]->setPoint2d(_vertices[n]->point2d()+userDir*(h)); + //_vertices[n-1]->setPoint2d(_vertices[n-1]->point2d()+t*x); + //_vertices[n]->setPoint2d(_vertices[n]->point2d()-t*x); + } + } + } + + + // 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; + // } + // } + + _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*iStrokeVertices.size()) + cerr << "Warning: problem with stripe size\n"; + + cleanUpSingularities (iStrokeVertices); +} + +// CLEAN UP +///////////////////////// + +void +Strip::cleanUpSingularities (const vector<StrokeVertex*>& iStrokeVertices) +{ + int k; + unsigned 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, *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 (int 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++; + // cerr<<"col=("<<sv->attribute().getColor()[0]<<", " + // <<sv->attribute().getColor()[1]<<", "<<sv->attribute().getColor()[2]<<")"<<endl; + } +} + +void +Strip::computeTexCoordWithTips (const vector<StrokeVertex*>& iStrokeVertices, bool tipBegin, bool tipEnd) +{ + unsigned int sizeStrip = _vertices.size()+8; //for the transition between the tip and the body + vector<StrokeVertex*>::const_iterator v ,vend; + StrokeVertex *sv; + + v=iStrokeVertices.begin(); + vend=iStrokeVertices.end(); + float l=(*v)->strokeLength()/_averageThickness; + int tiles=int(l); + float fact=(float(tiles)+0.5)/l; + float uTip2=float(tiles)+0.25; + float u=0; + float uPrev; + int i=0; + float t; + StrokeVertexRep *tvRep1, *tvRep2; + + // cerr<<"l="<<l<<" tiles="<<tiles<<" _averageThicnkess=" + // <<_averageThickness<<" strokeLength="<<(*v)->strokeLength()<<endl; + // + 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 (!tiles) t=0.5; + 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; + } + } + //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; +} + +// +// StrokeRep +///////////////////////////////////// + +StrokeRep::StrokeRep() +{ + _stroke = 0; + _strokeType=Stroke::OPAQUE_MEDIUM; + TextureManager * ptm = TextureManager::getInstance(); + if(ptm) + _textureId = ptm->getDefaultTextureId(); + // _averageTextureAlpha=0.5; //default value + // if (_strokeType==OIL_STROKE) + // _averageTextureAlpha=0.75; + // if (_strokeType>=NO_BLEND_STROKE) + // _averageTextureAlpha=1.0 +} + +StrokeRep::StrokeRep(Stroke *iStroke) +{ + _stroke = iStroke; + _strokeType = iStroke->getMediumType(); + _textureId = iStroke->getTextureId(); + if(_textureId == 0){ + TextureManager * ptm = TextureManager::getInstance(); + if(ptm) + _textureId = ptm->getDefaultTextureId(); + } + + // _averageTextureAlpha=0.5; //default value + // if (_strokeType==OIL_STROKE) + // _averageTextureAlpha=0.75; + // if (_strokeType>=NO_BLEND_STROKE) + // _averageTextureAlpha=1.0; + create(); +} + +StrokeRep::StrokeRep(const StrokeRep& iBrother) +{ + 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 100755 index 00000000000..129769e5489 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeRep.h @@ -0,0 +1,138 @@ +// +// Filename : StrokeRep.h +// Author(s) : Stephane Grabli +// Purpose : Class to define the representation of a stroke +// (for display purpose) +// Date of creation : 05/03/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 STROKEREP_H +# define STROKEREP_H + +# include "../geometry/Geom.h" +using namespace Geometry; + +//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 + +#include "Stroke.h" + +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 // STROKEREP_H diff --git a/source/blender/freestyle/intern/stroke/StrokeShader.h b/source/blender/freestyle/intern/stroke/StrokeShader.h new file mode 100755 index 00000000000..fa1289f6e0f --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeShader.h @@ -0,0 +1,119 @@ +// +// Filename : StrokeShader.h +// Author(s) : Stephane Grabli, Emmanuel Turquin +// Purpose : Class defining StrokeShader +// Date of creation : 01/07/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 SHADERS_H +# define SHADERS_H + +# include <iostream> +# include <vector> + +// +// 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 it.isEnd() == 0: + * 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: + /*! Default constructor. */ + StrokeShader() {} + /*! 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. + * The shading method 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 it.isEnd() == 0: + * 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 + * \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 void shade(Stroke& ioStroke) const { + cerr << "Warning: method shade() not implemented" << endl; + } + +}; + +# endif // SHADERS_H diff --git a/source/blender/freestyle/intern/stroke/StrokeTesselator.cpp b/source/blender/freestyle/intern/stroke/StrokeTesselator.cpp new file mode 100755 index 00000000000..6d0f5aa847c --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeTesselator.cpp @@ -0,0 +1,88 @@ + +// +// 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 "../scene_graph/OrientedLineRep.h" +#include "../scene_graph/NodeGroup.h" +#include "../scene_graph/NodeShape.h" +#include "StrokeTesselator.h" +#include "StrokeAdvancedIterators.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(_overloadMaterial) + line->SetMaterial(_Material); + + 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.f, 0.f, 0.f, 1.f); + tshape->SetMaterial(_Material); + + 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 100755 index 00000000000..767d82d3d98 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StrokeTesselator.h @@ -0,0 +1,67 @@ +// +// Filename : StrokeTesselator.h +// Author(s) : Stephane Grabli +// Purpose : Class to build a Node Tree designed to be displayed +// from a set of strokes structure. +// Date of creation : 26/03/2002 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 STROKETESSELATOR_H +# define STROKETESSELATOR_H + +# include "../scene_graph/LineRep.h" +# include "Stroke.h" + +class StrokeTesselator +{ +public: + + inline StrokeTesselator() {_Material.SetDiffuse(0,0,0,1);_overloadMaterial=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 SetMaterial(const Material& iMaterial) {_Material=iMaterial;_overloadMaterial=true;} + inline const Material& material() const {return _Material;} + +private: + + Material _Material; + bool _overloadMaterial; +}; + +#endif // STROKETESSELATOR_H + diff --git a/source/blender/freestyle/intern/stroke/StyleModule.h b/source/blender/freestyle/intern/stroke/StyleModule.h new file mode 100755 index 00000000000..3d39e53515b --- /dev/null +++ b/source/blender/freestyle/intern/stroke/StyleModule.h @@ -0,0 +1,144 @@ +// +// Filename : StyleModule.h +// Author(s) : Stephane Grabli, Emmanuel Turquin +// Purpose : Class representing a style module +// Date of creation : 01/07/2003 +// +/////////////////////////////////////////////////////////////////////////////// + + +// +// 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 STYLE_MODULE_H +# define STYLE_MODULE_H + +# include <iostream> +# include <string> +# include "../system/StringUtils.h" +# include "StrokeLayer.h" +# include "../system/Interpreter.h" +# include "Operators.h" +# include "StrokeShader.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; + } + + ~StyleModule() {} + + StrokeLayer* execute() { + if (!_inter) { + cerr << "Error: no interpreter was found to execute the script" << endl; + return NULL; + } + Operators::reset(); + if (_inter->interpretFile(_file_name)) + return NULL; + Operators::StrokesContainer* strokes_set = Operators::getStrokesSet(); + if (!_drawable || strokes_set->empty()) + return NULL; + StrokeLayer* sl = new StrokeLayer; + for (Operators::StrokesContainer::iterator it = strokes_set->begin(); + it != strokes_set->end(); + ++it) + sl->AddStroke(*it); + + return sl; + } + + // 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; + Interpreter* _inter; +}; + +#endif // STYLE_MODULE_H diff --git a/source/blender/freestyle/intern/stroke/TextStrokeRenderer.cpp b/source/blender/freestyle/intern/stroke/TextStrokeRenderer.cpp new file mode 100755 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 100755 index 00000000000..ef610d63bdd --- /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 "../system/FreestyleConfig.h" +# include "StrokeRenderer.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 + diff --git a/source/blender/freestyle/intern/stroke/src.pri b/source/blender/freestyle/intern/stroke/src.pri new file mode 100755 index 00000000000..09707f86dbc --- /dev/null +++ b/source/blender/freestyle/intern/stroke/src.pri @@ -0,0 +1,54 @@ +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# W A R N I N G ! ! ! # +# a u t h o r i z e d p e r s o n a l o n l y # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +STROKE_DIR = ../stroke + +SOURCES *= $${STROKE_DIR}/AdvancedFunctions0D.cpp \ + $${STROKE_DIR}/AdvancedFunctions1D.cpp \ + $${STROKE_DIR}/AdvancedStrokeShaders.cpp \ + $${STROKE_DIR}/BasicStrokeShaders.cpp \ + $${STROKE_DIR}/Canvas.cpp \ + $${STROKE_DIR}/Chain.cpp \ + $${STROKE_DIR}/ChainingIterators.cpp \ + $${STROKE_DIR}/ContextFunctions.cpp \ + $${STROKE_DIR}/Operators.cpp \ + $${STROKE_DIR}/PSStrokeRenderer.cpp \ + $${STROKE_DIR}/Stroke.cpp \ + $${STROKE_DIR}/StrokeIO.cpp \ + $${STROKE_DIR}/StrokeLayer.cpp \ + $${STROKE_DIR}/StrokeRenderer.cpp \ + $${STROKE_DIR}/StrokeRep.cpp \ + $${STROKE_DIR}/StrokeTesselator.cpp \ + $${STROKE_DIR}/TextStrokeRenderer.cpp \ + $${STROKE_DIR}/Curve.cpp + +HEADERS *= $${STROKE_DIR}/AdvancedFunctions0D.h \ + $${STROKE_DIR}/AdvancedFunctions1D.h \ + $${STROKE_DIR}/AdvancedPredicates1D.h \ + $${STROKE_DIR}/AdvancedStrokeShaders.h \ + $${STROKE_DIR}/BasicStrokeShaders.h \ + $${STROKE_DIR}/Canvas.h \ + $${STROKE_DIR}/Chain.h \ + $${STROKE_DIR}/ChainingIterators.h \ + $${STROKE_DIR}/ContextFunctions.h \ + $${STROKE_DIR}/Curve.h \ + $${STROKE_DIR}/CurveIterators.h \ + $${STROKE_DIR}/CurveAdvancedIterators.h \ + $${STROKE_DIR}/Module.h \ + $${STROKE_DIR}/Operators.h \ + $${STROKE_DIR}/Predicates1D.h \ + $${STROKE_DIR}/Predicates0D.h \ + $${STROKE_DIR}/PSStrokeRenderer.h \ + $${STROKE_DIR}/Stroke.h \ + $${STROKE_DIR}/StrokeIO.h \ + $${STROKE_DIR}/StrokeIterators.h \ + $${STROKE_DIR}/StrokeAdvancedIterators.h \ + $${STROKE_DIR}/StrokeShader.h \ + $${STROKE_DIR}/StrokeLayer.h \ + $${STROKE_DIR}/StrokeRenderer.h \ + $${STROKE_DIR}/StrokeRep.h \ + $${STROKE_DIR}/StrokeTesselator.h \ + $${STROKE_DIR}/StyleModule.h \ + $${STROKE_DIR}/TextStrokeRenderer.h diff --git a/source/blender/freestyle/intern/stroke/stroke.pro b/source/blender/freestyle/intern/stroke/stroke.pro new file mode 100755 index 00000000000..90c0cc96592 --- /dev/null +++ b/source/blender/freestyle/intern/stroke/stroke.pro @@ -0,0 +1,89 @@ +# This file should be viewed as a -*- mode: Makefile -*- + +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# W A R N I N G ! ! ! # +# a u t h o r i z e d p e r s o n a l o n l y # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + +include(../Config.pri) + +TEMPLATE = lib + +TARGET = $${LIB_STROKE} +VERSION = $${APPVERSION} +TARGET_VERSION_EXT = $${APPVERSION_MAJ}.$${APPVERSION_MID} + +# +# CONFIG +# +####################################### + +CONFIG *= dll + +# +# DEFINES +# +####################################### + +win32:DEFINES *= MAKE_LIB_STROKE_DLL + +# +# INCLUDE PATH +# +####################################### + +#INCLUDEPATH *= ../geometry ../image ../system ../view_map \ +# ../winged_edge ../scene_graph + +# +# BUILD DIRECTORIES +# +####################################### + +BUILD_DIR = ../../build + +OBJECTS_DIR = $${BUILD_DIR}/$${REL_OBJECTS_DIR} +!win32:DESTDIR = $${BUILD_DIR}/$${REL_DESTDIR}/lib +win32:DESTDIR = $${BUILD_DIR}/$${REL_DESTDIR} + +# +# LIBS +# +####################################### + +win32:LIBS *= $${DESTDIR}/$${LIB_GEOMETRY}$${LIBVERSION}.lib \ + $${DESTDIR}/$${LIB_IMAGE}$${LIBVERSION}.lib \ + $${DESTDIR}/$${LIB_SCENE_GRAPH}$${LIBVERSION}.lib \ + $${DESTDIR}/$${LIB_SYSTEM}$${LIBVERSION}.lib \ + $${DESTDIR}/$${LIB_WINGED_EDGE}$${LIBVERSION}.lib \ + $${DESTDIR}/$${LIB_VIEW_MAP}$${LIBVERSION}.lib + +!win32 { + lib_bundle { + LIBS += -F$${DESTDIR} -framework $${LIB_GEOMETRY} -framework $${LIB_IMAGE} -framework $${LIB_SCENE_GRAPH} -framework $${LIB_SYSTEM} -framework $${LIB_WINGED_EDGE} -framework $${LIB_VIEW_MAP} + } else { + LIBS *= -L$${DESTDIR} -l$${LIB_GEOMETRY} -l$${LIB_IMAGE} -l$${LIB_SCENE_GRAPH} \ + -l$${LIB_SYSTEM} -l$${LIB_WINGED_EDGE} -l$${LIB_VIEW_MAP} + } +} + +# +# INSTALL +# +####################################### + +LIB_DIR = ../../lib +# install library +target.path = $$LIB_DIR +# "make install" configuration options +INSTALLS += target + +# +# SOURCES & HEADERS +# +####################################### + + +!static { + include(src.pri) +} |