Add Chromium-only Blender WebEngine parity work
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Functions taking 0D input
|
||||
*/
|
||||
|
||||
#include "AdvancedFunctions0D.h"
|
||||
#include "Canvas.h"
|
||||
|
||||
#include "../view_map/Functions0D.h"
|
||||
#include "../view_map/SteerableViewMap.h"
|
||||
|
||||
namespace Freestyle::Functions0D {
|
||||
|
||||
int DensityF0D::operator()(Interface0DIterator &iter)
|
||||
{
|
||||
Canvas *canvas = Canvas::getInstance();
|
||||
int bound = _filter.getBound();
|
||||
|
||||
if ((iter->getProjectedX() - bound < 0) || (iter->getProjectedX() + bound > canvas->width()) ||
|
||||
(iter->getProjectedY() - bound < 0) || (iter->getProjectedY() + bound > canvas->height()))
|
||||
{
|
||||
result = 0.0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
RGBImage image;
|
||||
canvas->readColorPixels(int(iter->getProjectedX()) - bound,
|
||||
int(iter->getProjectedY()) - bound,
|
||||
_filter.maskSize(),
|
||||
_filter.maskSize(),
|
||||
image);
|
||||
result = _filter.getSmoothedPixel<RGBImage>(
|
||||
&image, int(iter->getProjectedX()), int(iter->getProjectedY()));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LocalAverageDepthF0D::operator()(Interface0DIterator &iter)
|
||||
{
|
||||
Canvas *iViewer = Canvas::getInstance();
|
||||
int bound = _filter.getBound();
|
||||
|
||||
if ((iter->getProjectedX() - bound < 0) || (iter->getProjectedX() + bound > iViewer->width()) ||
|
||||
(iter->getProjectedY() - bound < 0) || (iter->getProjectedY() + bound > iViewer->height()))
|
||||
{
|
||||
result = 0.0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
GrayImage image;
|
||||
iViewer->readDepthPixels(int(iter->getProjectedX()) - bound,
|
||||
int(iter->getProjectedY()) - bound,
|
||||
_filter.maskSize(),
|
||||
_filter.maskSize(),
|
||||
image);
|
||||
result = _filter.getSmoothedPixel(
|
||||
&image, int(iter->getProjectedX()), int(iter->getProjectedY()));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ReadMapPixelF0D::operator()(Interface0DIterator &iter)
|
||||
{
|
||||
Canvas *canvas = Canvas::getInstance();
|
||||
result = canvas->readMapPixel(
|
||||
_mapName, _level, int(iter->getProjectedX()), int(iter->getProjectedY()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ReadSteerableViewMapPixelF0D::operator()(Interface0DIterator &iter)
|
||||
{
|
||||
SteerableViewMap *svm = Canvas::getInstance()->getSteerableViewMap();
|
||||
result = svm->readSteerableViewMapPixel(
|
||||
_orientation, _level, int(iter->getProjectedX()), int(iter->getProjectedY()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ReadCompleteViewMapPixelF0D::operator()(Interface0DIterator &iter)
|
||||
{
|
||||
SteerableViewMap *svm = Canvas::getInstance()->getSteerableViewMap();
|
||||
result = svm->readCompleteViewMapPixel(
|
||||
_level, int(iter->getProjectedX()), int(iter->getProjectedY()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GetViewMapGradientNormF0D::operator()(Interface0DIterator &iter)
|
||||
{
|
||||
SteerableViewMap *svm = Canvas::getInstance()->getSteerableViewMap();
|
||||
float pxy = svm->readCompleteViewMapPixel(
|
||||
_level, int(iter->getProjectedX()), int(iter->getProjectedY()));
|
||||
float gx = svm->readCompleteViewMapPixel(
|
||||
_level, int(iter->getProjectedX()) + _step, int(iter->getProjectedY())) -
|
||||
pxy;
|
||||
float gy = svm->readCompleteViewMapPixel(
|
||||
_level, int(iter->getProjectedX()), int(iter->getProjectedY()) + _step) -
|
||||
pxy;
|
||||
result = Vec2f(gx, gy).norm();
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Freestyle::Functions0D
|
||||
@@ -0,0 +1,198 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Functions taking 0D input
|
||||
*/
|
||||
|
||||
#include "../image/GaussianFilter.h"
|
||||
#include "../image/Image.h"
|
||||
|
||||
#include "../view_map/Functions0D.h"
|
||||
|
||||
//
|
||||
// Functions definitions
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
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 DensityF0D : public UnaryFunction0D<double> {
|
||||
public:
|
||||
/** Builds the functor from the gaussian sigma value.
|
||||
* \param sigma:
|
||||
* sigma indicates the x value for which the gaussian function is 0.5.
|
||||
* It leads to the window size value. (the larger, the smoother)
|
||||
*/
|
||||
DensityF0D(double sigma = 2) : UnaryFunction0D<double>()
|
||||
{
|
||||
_filter.setSigma((float)sigma);
|
||||
}
|
||||
|
||||
/** Returns the string "DensityF0D" */
|
||||
string getName() const
|
||||
{
|
||||
return "DensityF0D";
|
||||
}
|
||||
|
||||
/** The () operator. */
|
||||
int operator()(Interface0DIterator &iter);
|
||||
|
||||
private:
|
||||
GaussianFilter _filter;
|
||||
};
|
||||
|
||||
// LocalAverageDepthF0D
|
||||
/** Returns the average depth around a point.
|
||||
* The result is obtained by querying the depth buffer on a window around that point.
|
||||
*/
|
||||
class LocalAverageDepthF0D : public UnaryFunction0D<double> {
|
||||
private:
|
||||
GaussianFilter _filter;
|
||||
|
||||
public:
|
||||
/** Builds the functor from the size of the mask that will be used. */
|
||||
LocalAverageDepthF0D(real maskSize = 5.0f) : UnaryFunction0D<double>()
|
||||
{
|
||||
_filter.setSigma((float)maskSize / 2.0f);
|
||||
}
|
||||
|
||||
/** Returns the string "LocalAverageDepthF0D" */
|
||||
string getName() const
|
||||
{
|
||||
return "LocalAverageDepthF0D";
|
||||
}
|
||||
|
||||
/** the () operator. */
|
||||
int operator()(Interface0DIterator &iter);
|
||||
};
|
||||
|
||||
// ReadMapPixel
|
||||
/** Reads a pixel in a map. */
|
||||
class ReadMapPixelF0D : public UnaryFunction0D<float> {
|
||||
private:
|
||||
const char *_mapName;
|
||||
int _level;
|
||||
|
||||
public:
|
||||
/** Builds the functor from name of the
|
||||
* Map that must be read.
|
||||
* \param iMapName:
|
||||
* The name of the map.
|
||||
* \param level:
|
||||
* The level of the pyramid from which the pixel must be read.
|
||||
*/
|
||||
ReadMapPixelF0D(const char *iMapName, int level) : UnaryFunction0D<float>()
|
||||
{
|
||||
_mapName = iMapName;
|
||||
_level = level;
|
||||
}
|
||||
|
||||
/** Returns the string "ReadMapPixelF0D" */
|
||||
string getName() const
|
||||
{
|
||||
return "ReadMapPixelF0D";
|
||||
}
|
||||
|
||||
/** the () operator. */
|
||||
int operator()(Interface0DIterator &iter);
|
||||
};
|
||||
|
||||
// ReadSteerableViewMapPixel
|
||||
/** Reads a pixel in one of the level of one of the steerable view-maps. */
|
||||
class ReadSteerableViewMapPixelF0D : public UnaryFunction0D<float> {
|
||||
private:
|
||||
uint _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(uint nOrientation, int level) : UnaryFunction0D<float>()
|
||||
{
|
||||
_orientation = nOrientation;
|
||||
_level = level;
|
||||
}
|
||||
|
||||
/** Returns the string "ReadSteerableViewMapPixelF0D" */
|
||||
string getName() const
|
||||
{
|
||||
return "ReadSteerableViewMapPixelF0D";
|
||||
}
|
||||
|
||||
/** the () operator. */
|
||||
int operator()(Interface0DIterator &iter);
|
||||
};
|
||||
|
||||
// ReadCompleteViewMapPixel
|
||||
/** Reads a pixel in one of the level of the complete viewmap. */
|
||||
class ReadCompleteViewMapPixelF0D : public UnaryFunction0D<float> {
|
||||
private:
|
||||
int _level;
|
||||
|
||||
public:
|
||||
/** Builds the functor
|
||||
* \param level:
|
||||
* The level of the pyramid from which the pixel must be read.
|
||||
*/
|
||||
ReadCompleteViewMapPixelF0D(int level) : UnaryFunction0D<float>()
|
||||
{
|
||||
_level = level;
|
||||
}
|
||||
|
||||
/** Returns the string "ReadCompleteViewMapPixelF0D" */
|
||||
string getName() const
|
||||
{
|
||||
return "ReadCompleteViewMapPixelF0D";
|
||||
}
|
||||
|
||||
/** the () operator. */
|
||||
int operator()(Interface0DIterator &iter);
|
||||
};
|
||||
|
||||
// GetViewMapGradientNormF0D
|
||||
/** Returns the norm of the gradient of the global viewmap density image. */
|
||||
class GetViewMapGradientNormF0D : public UnaryFunction0D<float> {
|
||||
private:
|
||||
int _level;
|
||||
float _step;
|
||||
|
||||
public:
|
||||
/** Builds the functor
|
||||
* \param level:
|
||||
* The level of the pyramid from which the pixel must be read.
|
||||
*/
|
||||
GetViewMapGradientNormF0D(int level) : UnaryFunction0D<float>()
|
||||
{
|
||||
_level = level;
|
||||
_step = (float)pow(2.0, _level);
|
||||
}
|
||||
|
||||
/** Returns the string "GetOccludeeF0D" */
|
||||
string getName() const
|
||||
{
|
||||
return "GetViewMapGradientNormF0D";
|
||||
}
|
||||
|
||||
/** the () operator. */
|
||||
int operator()(Interface0DIterator &iter);
|
||||
};
|
||||
|
||||
} // end of namespace Functions0D
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,117 @@
|
||||
/* SPDX-FileCopyrightText: 2008-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Functions taking 1D input
|
||||
*/
|
||||
|
||||
#include "AdvancedFunctions1D.h"
|
||||
#include "Canvas.h"
|
||||
|
||||
#include "../view_map/SteerableViewMap.h"
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace Freestyle::Functions1D {
|
||||
|
||||
int GetSteerableViewMapDensityF1D::operator()(Interface1D &inter)
|
||||
{
|
||||
SteerableViewMap *svm = Canvas::getInstance()->getSteerableViewMap();
|
||||
Interface0DIterator it = inter.pointsBegin(_sampling);
|
||||
Interface0DIterator itnext = it;
|
||||
++itnext;
|
||||
FEdge *fe;
|
||||
uint nSVM;
|
||||
vector<float> values;
|
||||
|
||||
while (!itnext.isEnd()) {
|
||||
Interface0D &i0D = (*it);
|
||||
Interface0D &i0Dnext = (*itnext);
|
||||
fe = i0D.getFEdge(i0Dnext);
|
||||
if (fe == nullptr) {
|
||||
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();
|
||||
uint size = 1;
|
||||
switch (_integration) {
|
||||
case MIN:
|
||||
res = *v;
|
||||
++v;
|
||||
for (; v != vend; ++v) {
|
||||
res_tmp = *v;
|
||||
if (res_tmp < res) {
|
||||
res = res_tmp;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MAX:
|
||||
res = *v;
|
||||
++v;
|
||||
for (; v != vend; ++v) {
|
||||
res_tmp = *v;
|
||||
if (res_tmp > res) {
|
||||
res = res_tmp;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FIRST:
|
||||
res = *v;
|
||||
break;
|
||||
case LAST:
|
||||
--vend;
|
||||
res = *vend;
|
||||
break;
|
||||
case MEAN:
|
||||
default:
|
||||
res = *v;
|
||||
++v;
|
||||
for (; v != vend; ++v, ++size) {
|
||||
res += *v;
|
||||
}
|
||||
res /= (size ? size : 1);
|
||||
break;
|
||||
}
|
||||
result = res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GetDirectionalViewMapDensityF1D::operator()(Interface1D &inter)
|
||||
{
|
||||
// soc uint size;
|
||||
result = integrate(_fun, inter.pointsBegin(_sampling), inter.pointsEnd(_sampling), _integration);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GetCompleteViewMapDensityF1D::operator()(Interface1D &inter)
|
||||
{
|
||||
// soc uint size;
|
||||
// Id id = inter.getId(); /* UNUSED */
|
||||
result = integrate(_fun, inter.pointsBegin(_sampling), inter.pointsEnd(_sampling), _integration);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GetViewMapGradientNormF1D::operator()(Interface1D &inter)
|
||||
{
|
||||
result = integrate(
|
||||
_func, inter.pointsBegin(_sampling), inter.pointsEnd(_sampling), _integration);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Freestyle::Functions1D
|
||||
@@ -0,0 +1,274 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Functions taking 1D input
|
||||
*/
|
||||
|
||||
#include "AdvancedFunctions0D.h"
|
||||
|
||||
#include "../view_map/Functions1D.h"
|
||||
|
||||
//
|
||||
// Functions definitions
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
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:
|
||||
* 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.
|
||||
* \param sampling:
|
||||
* The resolution used to sample the chain: the corresponding 0D function is evaluated at each
|
||||
* sample point and the result is obtained by combining the resulting values into a single one,
|
||||
* following the method specified by iType.
|
||||
*/
|
||||
DensityF1D(double sigma = 2, IntegrationType iType = MEAN, float sampling = 2.0f)
|
||||
: UnaryFunction1D<double>(iType), _fun(sigma)
|
||||
{
|
||||
_sampling = sampling;
|
||||
}
|
||||
|
||||
/** Destructor */
|
||||
virtual ~DensityF1D() {}
|
||||
|
||||
/** Returns the string "DensityF1D". */
|
||||
string getName() const
|
||||
{
|
||||
return "DensityF1D";
|
||||
}
|
||||
|
||||
/** the () operator. */
|
||||
int operator()(Interface1D &inter)
|
||||
{
|
||||
result = integrate(
|
||||
_fun, inter.pointsBegin(_sampling), inter.pointsEnd(_sampling), _integration);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
Functions0D::DensityF0D _fun;
|
||||
};
|
||||
|
||||
// LocalAverageDepthF1D
|
||||
/** Returns the average depth evaluated for an Interface1D.
|
||||
* The average depth is evaluated for a set of points along the Interface1D (using the
|
||||
* LocalAverageDepthF0D functor) with a user-defined sampling and then integrated into a single
|
||||
* value using a user-defined integration method.
|
||||
*/
|
||||
class LocalAverageDepthF1D : public UnaryFunction1D<double> {
|
||||
public:
|
||||
/** Builds the functor.
|
||||
* \param sigma:
|
||||
* The sigma used in DensityF0D and determining the window size used in each density query.
|
||||
* \param iType:
|
||||
* The integration method used to compute a single value from a set of values.
|
||||
*/
|
||||
LocalAverageDepthF1D(real sigma, IntegrationType iType = MEAN)
|
||||
: UnaryFunction1D<double>(iType), _fun(sigma)
|
||||
{
|
||||
}
|
||||
|
||||
/** Returns the string "LocalAverageDepthF1D" */
|
||||
string getName() const
|
||||
{
|
||||
return "LocalAverageDepthF1D";
|
||||
}
|
||||
|
||||
/** the () operator. */
|
||||
int operator()(Interface1D &inter)
|
||||
{
|
||||
result = integrate(_fun, inter.verticesBegin(), inter.verticesEnd(), _integration);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
Functions0D::LocalAverageDepthF0D _fun;
|
||||
};
|
||||
|
||||
// GetCompleteViewMapDensity
|
||||
/** Returns the density evaluated for an Interface1D in the complete viewmap image.
|
||||
* The density is evaluated for a set of points along the Interface1D (using the
|
||||
* ReadCompleteViewMapPixelF0D functor) and then integrated into a single value using a
|
||||
* user-defined integration method.
|
||||
*/
|
||||
class 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(uint level, IntegrationType iType = MEAN, float sampling = 2.0f)
|
||||
: UnaryFunction1D<double>(iType), _fun(level)
|
||||
{
|
||||
_sampling = sampling;
|
||||
}
|
||||
|
||||
/** Returns the string "GetCompleteViewMapDensityF1D" */
|
||||
string getName() const
|
||||
{
|
||||
return "GetCompleteViewMapDensityF1D";
|
||||
}
|
||||
|
||||
/** the () operator. */
|
||||
int operator()(Interface1D &inter);
|
||||
|
||||
private:
|
||||
Functions0D::ReadCompleteViewMapPixelF0D _fun;
|
||||
float _sampling;
|
||||
};
|
||||
|
||||
// GetDirectionalViewMapDensity
|
||||
/** Returns the density evaluated for an Interface1D in of the steerable view-maps image.
|
||||
* The direction telling which Directional map to choose is explicitly 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 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(uint iOrientation,
|
||||
uint level,
|
||||
IntegrationType iType = MEAN,
|
||||
float sampling = 2.0f)
|
||||
: UnaryFunction1D<double>(iType), _fun(iOrientation, level)
|
||||
{
|
||||
_sampling = sampling;
|
||||
}
|
||||
|
||||
/** Returns the string "GetDirectionalViewMapDensityF1D" */
|
||||
string getName() const
|
||||
{
|
||||
return "GetDirectionalViewMapDensityF1D";
|
||||
}
|
||||
|
||||
/** the () operator. */
|
||||
int operator()(Interface1D &inter);
|
||||
|
||||
private:
|
||||
Functions0D::ReadSteerableViewMapPixelF0D _fun;
|
||||
float _sampling;
|
||||
};
|
||||
|
||||
// GetSteerableViewMapDensityF1D
|
||||
/** Returns the density of the viewmap for a given Interface1D. The density of each FEdge is
|
||||
* evaluated in the proper steerable ViewMap depending on its orientation.
|
||||
*/
|
||||
class GetSteerableViewMapDensityF1D : public UnaryFunction1D<double> {
|
||||
private:
|
||||
int _level;
|
||||
float _sampling;
|
||||
|
||||
public:
|
||||
/** Builds the functor from the level of the pyramid from which the pixel must be read.
|
||||
* \param level:
|
||||
* The level of the pyramid from which the pixel must be read.
|
||||
* \param iType:
|
||||
* The integration method used to compute a single value from a set of values.
|
||||
* \param sampling:
|
||||
* The resolution used to sample the chain: the corresponding 0D function is evaluated at each
|
||||
* sample point and the result is obtained by combining the resulting values into a single one,
|
||||
* following the method specified by iType.
|
||||
*/
|
||||
GetSteerableViewMapDensityF1D(int level, IntegrationType iType = MEAN, float sampling = 2.0f)
|
||||
: UnaryFunction1D<double>(iType)
|
||||
{
|
||||
_level = level;
|
||||
_sampling = sampling;
|
||||
}
|
||||
|
||||
/** Destructor */
|
||||
virtual ~GetSteerableViewMapDensityF1D() {}
|
||||
|
||||
/** Returns the string "GetSteerableViewMapDensityF1D" */
|
||||
string getName() const
|
||||
{
|
||||
return "GetSteerableViewMapDensityF1D";
|
||||
}
|
||||
|
||||
/** the () operator. */
|
||||
int operator()(Interface1D &inter);
|
||||
};
|
||||
|
||||
// GetViewMapGradientNormF1D
|
||||
/** Returns the density of the viewmap for a given Interface1D. The density of each FEdge is
|
||||
* evaluated in the proper steerable ViewMap depending on its orientation.
|
||||
*/
|
||||
class GetViewMapGradientNormF1D : public UnaryFunction1D<double> {
|
||||
private:
|
||||
int _level;
|
||||
float _sampling;
|
||||
Functions0D::GetViewMapGradientNormF0D _func;
|
||||
|
||||
public:
|
||||
/** Builds the functor from the level of the pyramid from which the pixel must be read.
|
||||
* \param level:
|
||||
* The level of the pyramid from which the pixel must be read.
|
||||
* \param iType:
|
||||
* The integration method used to compute a single value from a set of values.
|
||||
* \param sampling:
|
||||
* The resolution used to sample the chain: the corresponding 0D function is evaluated at each
|
||||
* sample point and the result is obtained by combining the resulting values into a single
|
||||
* one, following the method specified by iType.
|
||||
*/
|
||||
GetViewMapGradientNormF1D(int level, IntegrationType iType = MEAN, float sampling = 2.0f)
|
||||
: UnaryFunction1D<double>(iType), _func(level)
|
||||
{
|
||||
_level = level;
|
||||
_sampling = sampling;
|
||||
}
|
||||
|
||||
/** Returns the string "GetSteerableViewMapDensityF1D" */
|
||||
string getName() const
|
||||
{
|
||||
return "GetViewMapGradientNormF1D";
|
||||
}
|
||||
|
||||
/** the () operator. */
|
||||
int operator()(Interface1D &inter);
|
||||
};
|
||||
|
||||
} // end of namespace Functions1D
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,71 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class gathering stroke creation algorithms
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "AdvancedFunctions1D.h"
|
||||
#include "Predicates1D.h"
|
||||
|
||||
#include "../view_map/Interface1D.h"
|
||||
|
||||
//
|
||||
// Predicates definitions
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
namespace Predicates1D {
|
||||
|
||||
// DensityLowerThanUP1D
|
||||
/** Returns true if the density evaluated for the
|
||||
* Interface1D is less than a user-defined density value.
|
||||
*/
|
||||
class DensityLowerThanUP1D : public UnaryPredicate1D {
|
||||
public:
|
||||
/** Builds the functor.
|
||||
* \param threshold:
|
||||
* The value of the threshold density.
|
||||
* Any Interface1D having a density lower than this threshold will match.
|
||||
* \param sigma:
|
||||
* The sigma value defining the density evaluation window size used in the DensityF0D functor.
|
||||
*/
|
||||
DensityLowerThanUP1D(double threshold, double sigma = 2)
|
||||
{
|
||||
_threshold = threshold;
|
||||
_sigma = sigma;
|
||||
}
|
||||
|
||||
/** Returns the string "DensityLowerThanUP1D" */
|
||||
string getName() const
|
||||
{
|
||||
return "DensityLowerThanUP1D";
|
||||
}
|
||||
|
||||
/** The () operator. */
|
||||
int operator()(Interface1D &inter)
|
||||
{
|
||||
Functions1D::DensityF1D fun(_sigma);
|
||||
if (fun(inter) < 0) {
|
||||
return -1;
|
||||
}
|
||||
result = (fun.result < _threshold);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
double _sigma;
|
||||
double _threshold;
|
||||
};
|
||||
|
||||
} // end of namespace Predicates1D
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,356 @@
|
||||
/* SPDX-FileCopyrightText: 2008-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Fredo's stroke shaders
|
||||
*/
|
||||
|
||||
#include "AdvancedStrokeShaders.h"
|
||||
#include "StrokeIterators.h"
|
||||
|
||||
#include "../system/PseudoNoise.h"
|
||||
#include "../system/RandGen.h"
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
/////////////////////////////////////////
|
||||
//
|
||||
// CALLIGRAPHICS SHADER
|
||||
//
|
||||
/////////////////////////////////////////
|
||||
|
||||
CalligraphicShader::CalligraphicShader(real iMinThickness,
|
||||
real iMaxThickness,
|
||||
const Vec2f &iOrientation,
|
||||
bool clamp)
|
||||
{
|
||||
_minThickness = iMinThickness;
|
||||
_maxThickness = iMaxThickness;
|
||||
_orientation = iOrientation;
|
||||
_orientation.normalize();
|
||||
_clamp = clamp;
|
||||
}
|
||||
|
||||
int CalligraphicShader::shade(Stroke &ioStroke) const
|
||||
{
|
||||
Interface0DIterator v;
|
||||
Functions0D::VertexOrientation2DF0D fun;
|
||||
StrokeVertex *sv;
|
||||
for (v = ioStroke.verticesBegin(); !v.isEnd(); ++v) {
|
||||
real thickness;
|
||||
if (fun(v) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Vec2f vertexOri(fun.result);
|
||||
Vec2r ori2d(-vertexOri[1], vertexOri[0]);
|
||||
ori2d.normalizeSafe();
|
||||
real scal = ori2d * _orientation;
|
||||
sv = dynamic_cast<StrokeVertex *>(&(*v));
|
||||
if (_clamp && (scal < 0)) {
|
||||
scal = 0.0;
|
||||
sv->attribute().setColor(1, 1, 1);
|
||||
}
|
||||
else {
|
||||
scal = fabs(scal);
|
||||
sv->attribute().setColor(0, 0, 0);
|
||||
}
|
||||
thickness = _minThickness + scal * (_maxThickness - _minThickness);
|
||||
if (thickness < 0.0) {
|
||||
thickness = 0.0;
|
||||
}
|
||||
sv->attribute().setThickness(thickness / 2.0, thickness / 2.0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
//
|
||||
// SPATIAL NOISE SHADER
|
||||
//
|
||||
/////////////////////////////////////////
|
||||
|
||||
static const uint NB_VALUE_NOISE = 512;
|
||||
|
||||
SpatialNoiseShader::SpatialNoiseShader(
|
||||
float iAmount, float ixScale, int nbOctave, bool smooth, bool pureRandom)
|
||||
{
|
||||
_amount = iAmount;
|
||||
if (ixScale == 0) {
|
||||
_xScale = 0;
|
||||
}
|
||||
else {
|
||||
_xScale = 1.0 / ixScale / real(NB_VALUE_NOISE);
|
||||
}
|
||||
_nbOctave = nbOctave;
|
||||
_smooth = smooth;
|
||||
_pureRandom = pureRandom;
|
||||
}
|
||||
|
||||
int SpatialNoiseShader::shade(Stroke &ioStroke) const
|
||||
{
|
||||
Interface0DIterator v, v2;
|
||||
v = ioStroke.verticesBegin();
|
||||
Vec2r p(v->getProjectedX(), v->getProjectedY());
|
||||
v2 = v;
|
||||
++v2;
|
||||
Vec2r p0(v2->getProjectedX(), v2->getProjectedY());
|
||||
p0 = p + 2 * (p - p0);
|
||||
StrokeVertex *sv;
|
||||
sv = dynamic_cast<StrokeVertex *>(&(*v));
|
||||
real initU = sv->strokeLength() * real(NB_VALUE_NOISE);
|
||||
if (_pureRandom) {
|
||||
initU += RandGen::drand48() * real(NB_VALUE_NOISE);
|
||||
}
|
||||
|
||||
Functions0D::VertexOrientation2DF0D fun;
|
||||
while (!v.isEnd()) {
|
||||
sv = dynamic_cast<StrokeVertex *>(&(*v));
|
||||
Vec2r p(sv->getPoint());
|
||||
if (fun(v) < 0) {
|
||||
return -1;
|
||||
}
|
||||
Vec2r vertexOri(fun.result);
|
||||
Vec2r ori2d(vertexOri[0], vertexOri[1]);
|
||||
ori2d = Vec2r(p - p0);
|
||||
ori2d.normalizeSafe();
|
||||
|
||||
PseudoNoise mynoise;
|
||||
real bruit;
|
||||
|
||||
if (_smooth) {
|
||||
bruit = mynoise.turbulenceSmooth(_xScale * sv->curvilinearAbscissa() + initU, _nbOctave);
|
||||
}
|
||||
else {
|
||||
bruit = mynoise.turbulenceLinear(_xScale * sv->curvilinearAbscissa() + initU, _nbOctave);
|
||||
}
|
||||
|
||||
Vec2r noise(-ori2d[1] * _amount * bruit, ori2d[0] * _amount * bruit);
|
||||
|
||||
sv->setPoint(p[0] + noise[0], p[1] + noise[1]);
|
||||
p0 = p;
|
||||
|
||||
++v;
|
||||
}
|
||||
|
||||
ioStroke.UpdateLength();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
//
|
||||
// SMOOTHING SHADER
|
||||
//
|
||||
/////////////////////////////////////////
|
||||
|
||||
SmoothingShader::SmoothingShader(int iNbIteration,
|
||||
real iFactorPoint,
|
||||
real ifactorCurvature,
|
||||
real iFactorCurvatureDifference,
|
||||
real iAnisoPoint,
|
||||
real iAnisoNormal,
|
||||
real iAnisoCurvature,
|
||||
real iCarricatureFactor)
|
||||
{
|
||||
_nbIterations = iNbIteration;
|
||||
_factorCurvature = ifactorCurvature;
|
||||
_factorCurvatureDifference = iFactorCurvatureDifference;
|
||||
_anisoNormal = iAnisoNormal;
|
||||
_anisoCurvature = iAnisoCurvature;
|
||||
_carricatureFactor = iCarricatureFactor;
|
||||
_factorPoint = iFactorPoint;
|
||||
_anisoPoint = iAnisoPoint;
|
||||
}
|
||||
|
||||
int SmoothingShader::shade(Stroke &ioStroke) const
|
||||
{
|
||||
// cerr << " Smoothing a stroke " << endl;
|
||||
|
||||
Smoother smoother(ioStroke);
|
||||
smoother.smooth(_nbIterations,
|
||||
_factorPoint,
|
||||
_factorCurvature,
|
||||
_factorCurvatureDifference,
|
||||
_anisoPoint,
|
||||
_anisoNormal,
|
||||
_anisoCurvature,
|
||||
_carricatureFactor);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// SMOOTHER
|
||||
////////////////////////////
|
||||
|
||||
Smoother::Smoother(Stroke &ioStroke)
|
||||
{
|
||||
_stroke = &ioStroke;
|
||||
|
||||
_nbVertices = ioStroke.vertices_size();
|
||||
_vertex = new Vec2r[_nbVertices];
|
||||
_curvature = new real[_nbVertices];
|
||||
_normal = new Vec2r[_nbVertices];
|
||||
StrokeInternal::StrokeVertexIterator v, vend;
|
||||
int i = 0;
|
||||
for (v = ioStroke.strokeVerticesBegin(), vend = ioStroke.strokeVerticesEnd(); v != vend;
|
||||
++v, ++i)
|
||||
{
|
||||
_vertex[i] = (v)->getPoint();
|
||||
}
|
||||
Vec2r vec_tmp(_vertex[0] - _vertex[_nbVertices - 1]);
|
||||
_isClosedCurve = (vec_tmp.norm() < M_EPSILON);
|
||||
|
||||
_safeTest = (_nbVertices > 4);
|
||||
}
|
||||
|
||||
Smoother::~Smoother()
|
||||
{
|
||||
delete[] _vertex;
|
||||
delete[] _curvature;
|
||||
delete[] _normal;
|
||||
}
|
||||
|
||||
void Smoother::smooth(int nbIteration,
|
||||
real iFactorPoint,
|
||||
real ifactorCurvature,
|
||||
real iFactorCurvatureDifference,
|
||||
real iAnisoPoint,
|
||||
real iAnisoNormal,
|
||||
real iAnisoCurvature,
|
||||
real iCarricatureFactor)
|
||||
{
|
||||
_factorCurvature = ifactorCurvature;
|
||||
_factorCurvatureDifference = iFactorCurvatureDifference;
|
||||
_anisoNormal = iAnisoNormal;
|
||||
_anisoCurvature = iAnisoCurvature;
|
||||
_carricatureFactor = iCarricatureFactor;
|
||||
_factorPoint = iFactorPoint;
|
||||
_anisoPoint = iAnisoPoint;
|
||||
|
||||
for (int i = 0; i < nbIteration; ++i) {
|
||||
iteration();
|
||||
}
|
||||
copyVertices();
|
||||
}
|
||||
|
||||
static real edgeStopping(real x, real sigma)
|
||||
{
|
||||
if (sigma == 0.0) {
|
||||
return 1.0;
|
||||
}
|
||||
return exp(-x * x / (sigma * sigma));
|
||||
}
|
||||
|
||||
void Smoother::iteration()
|
||||
{
|
||||
computeCurvature();
|
||||
for (int i = 1; i < (_nbVertices - 1); ++i) {
|
||||
real motionNormal = _factorCurvature * _curvature[i] *
|
||||
edgeStopping(_curvature[i], _anisoNormal);
|
||||
|
||||
real diffC1 = _curvature[i] - _curvature[i - 1];
|
||||
real diffC2 = _curvature[i] - _curvature[i + 1];
|
||||
real motionCurvature = edgeStopping(diffC1, _anisoCurvature) * diffC1 +
|
||||
edgeStopping(diffC2, _anisoCurvature) *
|
||||
diffC2; //_factorCurvatureDifference;
|
||||
motionCurvature *= _factorCurvatureDifference;
|
||||
// motionCurvature = _factorCurvatureDifference * (diffC1 + diffC2);
|
||||
if (_safeTest) {
|
||||
_vertex[i] = Vec2r(_vertex[i] + (motionNormal + motionCurvature) * _normal[i]);
|
||||
}
|
||||
Vec2r v1(_vertex[i - 1] - _vertex[i]);
|
||||
Vec2r v2(_vertex[i + 1] - _vertex[i]);
|
||||
real d1 = v1.norm();
|
||||
real d2 = v2.norm();
|
||||
_vertex[i] = Vec2r(
|
||||
_vertex[i] + _factorPoint * edgeStopping(d2, _anisoPoint) * (_vertex[i - 1] - _vertex[i]) +
|
||||
_factorPoint * edgeStopping(d1, _anisoPoint) * (_vertex[i + 1] - _vertex[i]));
|
||||
}
|
||||
|
||||
if (_isClosedCurve) {
|
||||
real motionNormal = _factorCurvature * _curvature[0] *
|
||||
edgeStopping(_curvature[0], _anisoNormal);
|
||||
|
||||
real diffC1 = _curvature[0] - _curvature[_nbVertices - 2];
|
||||
real diffC2 = _curvature[0] - _curvature[1];
|
||||
real motionCurvature = edgeStopping(diffC1, _anisoCurvature) * diffC1 +
|
||||
edgeStopping(diffC2, _anisoCurvature) *
|
||||
diffC2; //_factorCurvatureDifference;
|
||||
motionCurvature *= _factorCurvatureDifference;
|
||||
// motionCurvature = _factorCurvatureDifference * (diffC1 + diffC2);
|
||||
_vertex[0] = Vec2r(_vertex[0] + (motionNormal + motionCurvature) * _normal[0]);
|
||||
_vertex[_nbVertices - 1] = _vertex[0];
|
||||
}
|
||||
}
|
||||
|
||||
void Smoother::computeCurvature()
|
||||
{
|
||||
int i;
|
||||
Vec2r BA, BC, normalCurvature;
|
||||
for (i = 1; i < (_nbVertices - 1); ++i) {
|
||||
BA = _vertex[i - 1] - _vertex[i];
|
||||
BC = _vertex[i + 1] - _vertex[i];
|
||||
real lba = BA.norm(), lbc = BC.norm();
|
||||
BA.normalizeSafe();
|
||||
BC.normalizeSafe();
|
||||
normalCurvature = BA + BC;
|
||||
|
||||
_normal[i] = Vec2r(-(BC - BA)[1], (BC - BA)[0]);
|
||||
_normal[i].normalizeSafe();
|
||||
|
||||
_curvature[i] = normalCurvature * _normal[i];
|
||||
if (lba + lbc > M_EPSILON) {
|
||||
_curvature[i] /= (0.5 * lba + lbc);
|
||||
}
|
||||
}
|
||||
_curvature[0] = _curvature[1];
|
||||
_curvature[_nbVertices - 1] = _curvature[_nbVertices - 2];
|
||||
Vec2r di(_vertex[1] - _vertex[0]);
|
||||
_normal[0] = Vec2r(-di[1], di[0]);
|
||||
_normal[0].normalizeSafe();
|
||||
di = _vertex[_nbVertices - 1] - _vertex[_nbVertices - 2];
|
||||
_normal[_nbVertices - 1] = Vec2r(-di[1], di[0]);
|
||||
_normal[_nbVertices - 1].normalizeSafe();
|
||||
|
||||
if (_isClosedCurve) {
|
||||
BA = _vertex[_nbVertices - 2] - _vertex[0];
|
||||
BC = _vertex[1] - _vertex[0];
|
||||
real lba = BA.norm(), lbc = BC.norm();
|
||||
BA.normalizeSafe();
|
||||
BC.normalizeSafe();
|
||||
normalCurvature = BA + BC;
|
||||
|
||||
_normal[i] = Vec2r(-(BC - BA)[1], (BC - BA)[0]);
|
||||
_normal[i].normalizeSafe();
|
||||
|
||||
_curvature[i] = normalCurvature * _normal[i];
|
||||
if (lba + lbc > M_EPSILON) {
|
||||
_curvature[i] /= (0.5 * lba + lbc);
|
||||
}
|
||||
|
||||
_normal[_nbVertices - 1] = _normal[0];
|
||||
_curvature[_nbVertices - 1] = _curvature[0];
|
||||
}
|
||||
}
|
||||
|
||||
void Smoother::copyVertices()
|
||||
{
|
||||
int i = 0;
|
||||
StrokeInternal::StrokeVertexIterator v, vend;
|
||||
for (v = _stroke->strokeVerticesBegin(), vend = _stroke->strokeVerticesEnd(); v != vend; ++v) {
|
||||
const Vec2r p0((v)->getPoint());
|
||||
const Vec2r p1(_vertex[i]);
|
||||
Vec2r p(p0 + _carricatureFactor * (p1 - p0));
|
||||
|
||||
(v)->setPoint(p[0], p[1]);
|
||||
++i;
|
||||
}
|
||||
_stroke->UpdateLength();
|
||||
}
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,211 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Fredo's stroke shaders
|
||||
*/
|
||||
|
||||
#include "BasicStrokeShaders.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
/** [ 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 thinnest in the
|
||||
* direction perpendicular to this one, and an interpolation in between.
|
||||
*/
|
||||
class CalligraphicShader : public StrokeShader {
|
||||
public:
|
||||
/** Builds the shader.
|
||||
* \param iMinThickness:
|
||||
* The minimum thickness in the direction perpendicular to the main direction.
|
||||
* \param iMaxThickness:
|
||||
* The maximum thickness in the main direction.
|
||||
* \param iOrientation:
|
||||
* The 2D vector giving the main direction.
|
||||
* \param clamp:
|
||||
* Tells ???
|
||||
*/
|
||||
CalligraphicShader(real iMinThickness,
|
||||
real iMaxThickness,
|
||||
const Vec2f &iOrientation,
|
||||
bool clamp);
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~CalligraphicShader() {}
|
||||
|
||||
/** The shading method */
|
||||
virtual int shade(Stroke &ioStroke) const;
|
||||
|
||||
protected:
|
||||
real _maxThickness;
|
||||
real _minThickness;
|
||||
Vec2f _orientation;
|
||||
bool _clamp;
|
||||
};
|
||||
|
||||
/** [ Geometry Shader ].
|
||||
* Spatial Noise stroke shader.
|
||||
* Moves the vertices to make the stroke more noisy.
|
||||
* \see \htmlonly <a href=noise/noise.html>noise/noise.html</a> \endhtmlonly
|
||||
*/
|
||||
class SpatialNoiseShader : public StrokeShader {
|
||||
public:
|
||||
/** Builds the shader.
|
||||
* \param iAmount:
|
||||
* The amplitude of the noise.
|
||||
* \param ixScale:
|
||||
* The noise frequency
|
||||
* \param nbOctave:
|
||||
* The number of octaves
|
||||
* \param smooth:
|
||||
* If you want the noise to be smooth
|
||||
* \param pureRandom:
|
||||
* If you don't want any coherence
|
||||
*/
|
||||
SpatialNoiseShader(float iAmount, float ixScale, int nbOctave, bool smooth, bool pureRandom);
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~SpatialNoiseShader() {}
|
||||
|
||||
/** The shading method. */
|
||||
virtual int shade(Stroke &ioStroke) const;
|
||||
|
||||
protected:
|
||||
float _amount;
|
||||
float _xScale;
|
||||
int _nbOctave;
|
||||
bool _smooth;
|
||||
bool _pureRandom;
|
||||
};
|
||||
|
||||
/** [ Geometry Shader ].
|
||||
* Smooths 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 across corners. \see \htmlonly <a
|
||||
* href=/smoothing/smoothing.html>smoothing/smoothing.html</a> \endhtmlonly
|
||||
*/
|
||||
class 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 iAnisoNormal:
|
||||
* 0
|
||||
* \param iAnisoCurvature:
|
||||
* 0
|
||||
* \param icarricatureFactor:
|
||||
* 1
|
||||
*/
|
||||
SmoothingShader(int iNbIteration,
|
||||
real iFactorPoint,
|
||||
real ifactorCurvature,
|
||||
real iFactorCurvatureDifference,
|
||||
real iAnisoPoint,
|
||||
real iAnisoNormal,
|
||||
real iAnisoCurvature,
|
||||
real icarricatureFactor);
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~SmoothingShader() {}
|
||||
|
||||
/** The shading method. */
|
||||
virtual int shade(Stroke &ioStroke) const;
|
||||
|
||||
protected:
|
||||
int _nbIterations;
|
||||
real _factorPoint;
|
||||
real _factorCurvature;
|
||||
real _factorCurvatureDifference;
|
||||
real _anisoPoint;
|
||||
real _anisoNormal;
|
||||
real _anisoCurvature;
|
||||
real _carricatureFactor;
|
||||
};
|
||||
|
||||
class 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 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 OmissionShader : public StrokeShader {
|
||||
public:
|
||||
OmissionShader(real sizeWindow, real thrVari, real thrFlat, real lFlat);
|
||||
virtual ~OmissionShader() {}
|
||||
|
||||
virtual int shade(Stroke &ioStroke) const;
|
||||
|
||||
protected:
|
||||
real _sizeWindow;
|
||||
real _thresholdVariation;
|
||||
real _thresholdFlat;
|
||||
real _lengthFlat;
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,695 @@
|
||||
/* SPDX-FileCopyrightText: 2008-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class gathering basic stroke shaders
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "AdvancedFunctions0D.h"
|
||||
#include "AdvancedFunctions1D.h"
|
||||
#include "BasicStrokeShaders.h"
|
||||
#include "StrokeIO.h"
|
||||
#include "StrokeIterators.h"
|
||||
#include "StrokeRenderer.h"
|
||||
|
||||
#include "../system/PseudoNoise.h"
|
||||
#include "../system/RandGen.h"
|
||||
#include "../system/StringUtils.h"
|
||||
|
||||
#include "../view_map/Functions0D.h"
|
||||
#include "../view_map/Functions1D.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#include "IMB_imbuf.hh"
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
namespace Freestyle::StrokeShaders {
|
||||
|
||||
//
|
||||
// Thickness modifiers
|
||||
//
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
int ConstantThicknessShader::shade(Stroke &stroke) const
|
||||
{
|
||||
StrokeInternal::StrokeVertexIterator v, vend;
|
||||
int i = 0;
|
||||
int size = stroke.strokeVerticesSize();
|
||||
for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend; ++v) {
|
||||
// XXX What's the use of i here? And is not the thickness always overridden by the last line of
|
||||
// the loop?
|
||||
if ((1 == i) || (size - 2 == i)) {
|
||||
v->attribute().setThickness(_thickness / 4.0, _thickness / 4.0);
|
||||
}
|
||||
if ((0 == i) || (size - 1 == i)) {
|
||||
v->attribute().setThickness(0, 0);
|
||||
}
|
||||
|
||||
v->attribute().setThickness(_thickness / 2.0, _thickness / 2.0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConstantExternThicknessShader::shade(Stroke &stroke) const
|
||||
{
|
||||
StrokeInternal::StrokeVertexIterator v, vend;
|
||||
int i = 0;
|
||||
int size = stroke.strokeVerticesSize();
|
||||
for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend; ++v) {
|
||||
// XXX What's the use of i here? And is not the thickness always overridden by the last line of
|
||||
// the loop?
|
||||
if ((1 == i) || (size - 2 == i)) {
|
||||
v->attribute().setThickness(_thickness / 2.0, 0);
|
||||
}
|
||||
if ((0 == i) || (size - 1 == i)) {
|
||||
v->attribute().setThickness(0, 0);
|
||||
}
|
||||
|
||||
v->attribute().setThickness(_thickness, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IncreasingThicknessShader::shade(Stroke &stroke) const
|
||||
{
|
||||
int n = stroke.strokeVerticesSize() - 1, i;
|
||||
StrokeInternal::StrokeVertexIterator v, vend;
|
||||
for (i = 0, v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend;
|
||||
++v, ++i)
|
||||
{
|
||||
float t;
|
||||
if (i < float(n) / 2.0f) {
|
||||
t = (1.0 - float(i) / float(n)) * _ThicknessMin + float(i) / float(n) * _ThicknessMax;
|
||||
}
|
||||
else {
|
||||
t = (1.0 - float(i) / float(n)) * _ThicknessMax + float(i) / float(n) * _ThicknessMin;
|
||||
}
|
||||
v->attribute().setThickness(t / 2.0, t / 2.0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConstrainedIncreasingThicknessShader::shade(Stroke &stroke) const
|
||||
{
|
||||
float slength = stroke.getLength2D();
|
||||
float maxT = min(_ratio * slength, _ThicknessMax);
|
||||
int n = stroke.strokeVerticesSize() - 1, i;
|
||||
StrokeInternal::StrokeVertexIterator v, vend;
|
||||
for (i = 0, v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend;
|
||||
++v, ++i)
|
||||
{
|
||||
// XXX Why not using an if/else here? Else, if last condition is true, everything else is
|
||||
// computed for nothing!
|
||||
float t;
|
||||
if (i < float(n) / 2.0f) {
|
||||
t = (1.0 - float(i) / float(n)) * _ThicknessMin + float(i) / float(n) * maxT;
|
||||
}
|
||||
else {
|
||||
t = (1.0 - float(i) / float(n)) * maxT + float(i) / float(n) * _ThicknessMin;
|
||||
}
|
||||
v->attribute().setThickness(t / 2.0, t / 2.0);
|
||||
if (i == n - 1) {
|
||||
v->attribute().setThickness(_ThicknessMin / 2.0, _ThicknessMin / 2.0);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LengthDependingThicknessShader::shade(Stroke &stroke) const
|
||||
{
|
||||
float step = (_maxThickness - _minThickness) / 3.0f;
|
||||
float l = stroke.getLength2D();
|
||||
float thickness = 0.0f;
|
||||
if (l > 300.0f) {
|
||||
thickness = _minThickness + 3.0f * step;
|
||||
}
|
||||
else if ((l < 300.0f) && (l > 100.0f)) {
|
||||
thickness = _minThickness + 2.0f * step;
|
||||
}
|
||||
else if ((l < 100.0f) && (l > 50.0f)) {
|
||||
thickness = _minThickness + 1.0f * step;
|
||||
}
|
||||
else { // else if (l < 50.0f), tsst...
|
||||
thickness = _minThickness;
|
||||
}
|
||||
|
||||
StrokeInternal::StrokeVertexIterator v, vend;
|
||||
int i = 0;
|
||||
int size = stroke.strokeVerticesSize();
|
||||
for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend; ++v) {
|
||||
// XXX What's the use of i here? And is not the thickness always overridden by the last line of
|
||||
// the loop?
|
||||
if ((1 == i) || (size - 2 == i)) {
|
||||
v->attribute().setThickness(thickness / 4.0, thickness / 4.0);
|
||||
}
|
||||
if ((0 == i) || (size - 1 == i)) {
|
||||
v->attribute().setThickness(0, 0);
|
||||
}
|
||||
|
||||
v->attribute().setThickness(thickness / 2.0, thickness / 2.0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const uint NB_VALUE_NOISE = 512;
|
||||
|
||||
ThicknessNoiseShader::ThicknessNoiseShader()
|
||||
{
|
||||
_amplitude = 1.0f;
|
||||
_scale = 1.0f / 2.0f / float(NB_VALUE_NOISE);
|
||||
}
|
||||
|
||||
ThicknessNoiseShader::ThicknessNoiseShader(float iAmplitude, float iPeriod)
|
||||
{
|
||||
_amplitude = iAmplitude;
|
||||
_scale = 1.0f / iPeriod / float(NB_VALUE_NOISE);
|
||||
}
|
||||
|
||||
int ThicknessNoiseShader::shade(Stroke &stroke) const
|
||||
{
|
||||
StrokeInternal::StrokeVertexIterator v = stroke.strokeVerticesBegin(), vend;
|
||||
real initU1 = v->strokeLength() * real(NB_VALUE_NOISE) +
|
||||
RandGen::drand48() * real(NB_VALUE_NOISE);
|
||||
real initU2 = v->strokeLength() * real(NB_VALUE_NOISE) +
|
||||
RandGen::drand48() * real(NB_VALUE_NOISE);
|
||||
|
||||
real bruit, bruit2;
|
||||
PseudoNoise mynoise, mynoise2;
|
||||
for (vend = stroke.strokeVerticesEnd(); v != vend; ++v) {
|
||||
bruit = mynoise.turbulenceSmooth(_scale * v->curvilinearAbscissa() + initU1,
|
||||
2); // 2 : nbOctaves
|
||||
bruit2 = mynoise2.turbulenceSmooth(_scale * v->curvilinearAbscissa() + initU2,
|
||||
2); // 2 : nbOctaves
|
||||
const float *originalThickness = v->attribute().getThickness();
|
||||
float r = bruit * _amplitude + originalThickness[0];
|
||||
float l = bruit2 * _amplitude + originalThickness[1];
|
||||
v->attribute().setThickness(r, l);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Color shaders
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int ConstantColorShader::shade(Stroke &stroke) const
|
||||
{
|
||||
StrokeInternal::StrokeVertexIterator v, vend;
|
||||
for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend; ++v) {
|
||||
v->attribute().setColor(_color[0], _color[1], _color[2]);
|
||||
v->attribute().setAlpha(_color[3]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IncreasingColorShader::shade(Stroke &stroke) const
|
||||
{
|
||||
StrokeInternal::StrokeVertexIterator v, vend;
|
||||
int n = stroke.strokeVerticesSize() - 1, yo;
|
||||
float newcolor[4];
|
||||
for (yo = 0, v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend;
|
||||
++v, ++yo)
|
||||
{
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
newcolor[i] = (1.0 - float(yo) / float(n)) * _colorMin[i] +
|
||||
float(yo) / float(n) * _colorMax[i];
|
||||
}
|
||||
v->attribute().setColor(newcolor[0], newcolor[1], newcolor[2]);
|
||||
v->attribute().setAlpha(newcolor[3]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MaterialColorShader::shade(Stroke &stroke) const
|
||||
{
|
||||
Interface0DIterator v, vend;
|
||||
Functions0D::MaterialF0D fun;
|
||||
StrokeVertex *sv;
|
||||
for (v = stroke.verticesBegin(), vend = stroke.verticesEnd(); v != vend; ++v) {
|
||||
if (fun(v) < 0) {
|
||||
return -1;
|
||||
}
|
||||
const float *diffuse = fun.result.diffuse();
|
||||
sv = dynamic_cast<StrokeVertex *>(&(*v));
|
||||
sv->attribute().setColor(
|
||||
diffuse[0] * _coefficient, diffuse[1] * _coefficient, diffuse[2] * _coefficient);
|
||||
sv->attribute().setAlpha(diffuse[3]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ColorNoiseShader::ColorNoiseShader()
|
||||
{
|
||||
_amplitude = 1.0f;
|
||||
_scale = 1.0f / 2.0f / float(NB_VALUE_NOISE);
|
||||
}
|
||||
|
||||
ColorNoiseShader::ColorNoiseShader(float iAmplitude, float iPeriod)
|
||||
{
|
||||
_amplitude = iAmplitude;
|
||||
_scale = 1.0f / iPeriod / float(NB_VALUE_NOISE);
|
||||
}
|
||||
|
||||
int ColorNoiseShader::shade(Stroke &stroke) const
|
||||
{
|
||||
StrokeInternal::StrokeVertexIterator v = stroke.strokeVerticesBegin(), vend;
|
||||
real initU = v->strokeLength() * real(NB_VALUE_NOISE) +
|
||||
RandGen::drand48() * real(NB_VALUE_NOISE);
|
||||
|
||||
real bruit;
|
||||
PseudoNoise mynoise;
|
||||
for (vend = stroke.strokeVerticesEnd(); v != vend; ++v) {
|
||||
bruit = mynoise.turbulenceSmooth(_scale * v->curvilinearAbscissa() + initU,
|
||||
2); // 2 : nbOctaves
|
||||
const float *originalColor = v->attribute().getColor();
|
||||
float r = bruit * _amplitude + originalColor[0];
|
||||
float g = bruit * _amplitude + originalColor[1];
|
||||
float b = bruit * _amplitude + originalColor[2];
|
||||
v->attribute().setColor(r, g, b);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Texture Shaders
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int BlenderTextureShader::shade(Stroke &stroke) const
|
||||
{
|
||||
if (_mtex) {
|
||||
return stroke.setMTex(_mtex);
|
||||
}
|
||||
if (_nodeTree) {
|
||||
stroke.setNodeTree(_nodeTree);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int StrokeTextureStepShader::shade(Stroke &stroke) const
|
||||
{
|
||||
stroke.setTextureStep(_step);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Geometry Shaders
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int BackboneStretcherShader::shade(Stroke &stroke) const
|
||||
{
|
||||
float l = stroke.getLength2D();
|
||||
if (l <= 1.0e-6) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
StrokeInternal::StrokeVertexIterator v0 = stroke.strokeVerticesBegin();
|
||||
StrokeInternal::StrokeVertexIterator v1 = v0;
|
||||
++v1;
|
||||
StrokeInternal::StrokeVertexIterator vn = stroke.strokeVerticesEnd();
|
||||
--vn;
|
||||
StrokeInternal::StrokeVertexIterator vn_1 = vn;
|
||||
--vn_1;
|
||||
|
||||
Vec2d first((v0)->x(), (v0)->y());
|
||||
Vec2d last((vn)->x(), (vn)->y());
|
||||
|
||||
Vec2d d1(first - Vec2d((v1)->x(), (v1)->y()));
|
||||
d1.normalize();
|
||||
Vec2d dn(last - Vec2d((vn_1)->x(), (vn_1)->y()));
|
||||
dn.normalize();
|
||||
|
||||
Vec2d newFirst(first + _amount * d1);
|
||||
(v0)->setPoint(newFirst[0], newFirst[1]);
|
||||
Vec2d newLast(last + _amount * dn);
|
||||
(vn)->setPoint(newLast[0], newLast[1]);
|
||||
|
||||
stroke.UpdateLength();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SamplingShader::shade(Stroke &stroke) const
|
||||
{
|
||||
stroke.Resample(_sampling);
|
||||
stroke.UpdateLength();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ExternalContourStretcherShader::shade(Stroke &stroke) const
|
||||
{
|
||||
// float l = stroke.getLength2D();
|
||||
Interface0DIterator it;
|
||||
Functions0D::Normal2DF0D fun;
|
||||
StrokeVertex *sv;
|
||||
for (it = stroke.verticesBegin(); !it.isEnd(); ++it) {
|
||||
if (fun(it) < 0) {
|
||||
return -1;
|
||||
}
|
||||
Vec2f n(fun.result);
|
||||
sv = dynamic_cast<StrokeVertex *>(&(*it));
|
||||
Vec2d newPoint(sv->x() + _amount * n.x(), sv->y() + _amount * n.y());
|
||||
sv->setPoint(newPoint[0], newPoint[1]);
|
||||
}
|
||||
stroke.UpdateLength();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//!! Bezier curve stroke shader
|
||||
int BezierCurveShader::shade(Stroke &stroke) const
|
||||
{
|
||||
if (stroke.strokeVerticesSize() < 4) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Build the Bezier curve from this set of data points:
|
||||
vector<Vec2d> data;
|
||||
StrokeInternal::StrokeVertexIterator v = stroke.strokeVerticesBegin(), vend;
|
||||
data.emplace_back(v->x(), v->y()); // first one
|
||||
StrokeInternal::StrokeVertexIterator previous = v;
|
||||
++v;
|
||||
for (vend = stroke.strokeVerticesEnd(); v != vend; ++v) {
|
||||
if (!((fabs(v->x() - (previous)->x()) < M_EPSILON) &&
|
||||
(fabs(v->y() - (previous)->y()) < M_EPSILON)))
|
||||
{
|
||||
data.emplace_back(v->x(), v->y());
|
||||
}
|
||||
previous = v;
|
||||
}
|
||||
|
||||
// here we build the bezier curve
|
||||
BezierCurve bcurve(data, _error);
|
||||
|
||||
// bad performances are here !!! // FIXME
|
||||
vector<Vec2d> CurveVertices;
|
||||
vector<BezierCurveSegment *> &bsegments = bcurve.segments();
|
||||
vector<BezierCurveSegment *>::iterator s = bsegments.begin(), send;
|
||||
vector<Vec2d> &segmentsVertices = (*s)->vertices();
|
||||
vector<Vec2d>::iterator p, pend;
|
||||
// first point
|
||||
CurveVertices.push_back(segmentsVertices[0]);
|
||||
for (send = bsegments.end(); s != send; ++s) {
|
||||
segmentsVertices = (*s)->vertices();
|
||||
p = segmentsVertices.begin();
|
||||
++p;
|
||||
for (pend = segmentsVertices.end(); p != pend; ++p) {
|
||||
CurveVertices.push_back(*p);
|
||||
}
|
||||
}
|
||||
|
||||
// Re-sample the Stroke depending on the number of vertices of the bezier curve:
|
||||
int originalSize = CurveVertices.size();
|
||||
#if 0
|
||||
float sampling = stroke.ComputeSampling(originalSize);
|
||||
stroke.Resample(sampling);
|
||||
#endif
|
||||
stroke.Resample(originalSize);
|
||||
int newsize = stroke.strokeVerticesSize();
|
||||
int nExtraVertex = 0;
|
||||
if (newsize < originalSize) {
|
||||
cerr << "Warning: insufficient resampling" << endl;
|
||||
}
|
||||
else {
|
||||
nExtraVertex = newsize - originalSize;
|
||||
if (nExtraVertex != 0) {
|
||||
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
|
||||
cout << "Bezier Shader : Stroke " << stroke.getId() << " have not been resampled" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// assigns the new coordinates:
|
||||
p = CurveVertices.begin();
|
||||
vector<Vec2d>::iterator last = p;
|
||||
StrokeInternal::StrokeVertexIterator it, itend;
|
||||
for (it = stroke.strokeVerticesBegin(),
|
||||
itend = stroke.strokeVerticesEnd(),
|
||||
pend = CurveVertices.end();
|
||||
(it != itend) && (p != pend);
|
||||
++it, ++p)
|
||||
{
|
||||
it->setX(p->x());
|
||||
it->setY(p->y());
|
||||
last = p;
|
||||
}
|
||||
stroke.UpdateLength();
|
||||
|
||||
// Deal with extra vertices:
|
||||
if (nExtraVertex == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// nExtraVertex should stay unassigned
|
||||
vector<StrokeAttribute> attributes;
|
||||
vector<StrokeVertex *> verticesToRemove;
|
||||
for (int i = 0; i < nExtraVertex; ++i, ++it) {
|
||||
verticesToRemove.push_back(&(*it));
|
||||
if (it.isEnd()) {
|
||||
// XXX Shocking! :P Shouldn't we break in this case???
|
||||
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
|
||||
cout << "messed up!" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (it = stroke.strokeVerticesBegin(); it != itend; ++it) {
|
||||
attributes.push_back(it->attribute());
|
||||
}
|
||||
|
||||
for (vector<StrokeVertex *>::iterator vr = verticesToRemove.begin(),
|
||||
vrend = verticesToRemove.end();
|
||||
vr != vrend;
|
||||
++vr)
|
||||
{
|
||||
stroke.RemoveVertex(*vr);
|
||||
}
|
||||
|
||||
vector<StrokeAttribute>::iterator a = attributes.begin(), aend = attributes.end();
|
||||
int index = 0;
|
||||
int index1 = int(floor(float(originalSize) / 2.0));
|
||||
int index2 = index1 + nExtraVertex;
|
||||
for (it = stroke.strokeVerticesBegin(), itend = stroke.strokeVerticesEnd();
|
||||
(it != itend) && (a != aend);
|
||||
++it)
|
||||
{
|
||||
(it)->setAttribute(*a);
|
||||
if ((index <= index1) || (index > index2)) {
|
||||
++a;
|
||||
}
|
||||
++index;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
_error = 0.0f;
|
||||
_begin = b;
|
||||
_last = l;
|
||||
A = Vec2d((_begin)->x(), (_begin)->y());
|
||||
B = Vec2d((_last)->x(), (_last)->y());
|
||||
size = iSize;
|
||||
}
|
||||
|
||||
float error()
|
||||
{
|
||||
float maxE = 0.0f;
|
||||
for (StrokeInternal::StrokeVertexIterator it = _begin; it != _last; ++it) {
|
||||
Vec2d P(it->x(), it->y());
|
||||
float d = GeomUtils::distPointSegment(P, A, B);
|
||||
if (d > maxE) {
|
||||
maxE = d;
|
||||
}
|
||||
}
|
||||
_error = maxE;
|
||||
return maxE;
|
||||
}
|
||||
|
||||
//! Subdivides the curve into two pieces.
|
||||
// The first piece is this same object (modified)
|
||||
// The second piece is returned by the method
|
||||
CurvePiece *subdivide()
|
||||
{
|
||||
StrokeInternal::StrokeVertexIterator it = _begin;
|
||||
int ns = size - 1; // number of segments (ns > 1)
|
||||
int ns1 = ns / 2;
|
||||
int ns2 = ns - ns1;
|
||||
for (int i = 0; i < ns1; ++it, ++i) {
|
||||
/* pass */
|
||||
}
|
||||
|
||||
CurvePiece *second = new CurvePiece(it, _last, ns2 + 1);
|
||||
size = ns1 + 1;
|
||||
_last = it;
|
||||
B = Vec2d((_last)->x(), (_last)->y());
|
||||
return second;
|
||||
}
|
||||
};
|
||||
|
||||
int PolygonalizationShader::shade(Stroke &stroke) const
|
||||
{
|
||||
vector<CurvePiece *> _pieces;
|
||||
vector<CurvePiece *> _results;
|
||||
vector<CurvePiece *>::iterator cp, cpend;
|
||||
|
||||
// Compute first approx:
|
||||
StrokeInternal::StrokeVertexIterator a = stroke.strokeVerticesBegin();
|
||||
StrokeInternal::StrokeVertexIterator b = stroke.strokeVerticesEnd();
|
||||
--b;
|
||||
int size = stroke.strokeVerticesSize();
|
||||
|
||||
CurvePiece *piece = new CurvePiece(a, b, size);
|
||||
_pieces.push_back(piece);
|
||||
|
||||
while (!_pieces.empty()) {
|
||||
piece = _pieces.back();
|
||||
_pieces.pop_back();
|
||||
if (piece->size > 2 && piece->error() > _error) {
|
||||
CurvePiece *second = piece->subdivide();
|
||||
_pieces.push_back(second);
|
||||
_pieces.push_back(piece);
|
||||
}
|
||||
else {
|
||||
_results.push_back(piece);
|
||||
}
|
||||
}
|
||||
|
||||
// actually modify the geometry for each piece:
|
||||
for (cp = _results.begin(), cpend = _results.end(); cp != cpend; ++cp) {
|
||||
a = (*cp)->_begin;
|
||||
b = (*cp)->_last;
|
||||
Vec2d u = (*cp)->B - (*cp)->A;
|
||||
Vec2d n(u[1], -u[0]);
|
||||
n.normalize();
|
||||
// Vec2d n(0, 0);
|
||||
float offset = ((*cp)->_error);
|
||||
StrokeInternal::StrokeVertexIterator v;
|
||||
for (v = a; v != b; ++v) {
|
||||
v->setPoint((*cp)->A.x() + v->u() * u.x() + n.x() * offset,
|
||||
(*cp)->A.y() + v->u() * u.y() + n.y() * offset);
|
||||
}
|
||||
#if 0
|
||||
u.normalize();
|
||||
(*a)->setPoint((*a)->x() - u.x() * 10, (*a)->y() - u.y() * 10);
|
||||
#endif
|
||||
}
|
||||
stroke.UpdateLength();
|
||||
|
||||
// delete stuff
|
||||
for (cp = _results.begin(), cpend = _results.end(); cp != cpend; ++cp) {
|
||||
delete (*cp);
|
||||
}
|
||||
_results.clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GuidingLinesShader::shade(Stroke &stroke) const
|
||||
{
|
||||
Functions1D::Normal2DF1D norm_fun;
|
||||
StrokeInternal::StrokeVertexIterator a = stroke.strokeVerticesBegin();
|
||||
StrokeInternal::StrokeVertexIterator b = stroke.strokeVerticesEnd();
|
||||
--b;
|
||||
int size = stroke.strokeVerticesSize();
|
||||
CurvePiece piece(a, b, size);
|
||||
|
||||
Vec2d u = piece.B - piece.A;
|
||||
Vec2f n(u[1], -u[0]);
|
||||
n.normalize();
|
||||
if (norm_fun(stroke) < 0) {
|
||||
return -1;
|
||||
}
|
||||
Vec2f strokeN(norm_fun.result);
|
||||
if (n * strokeN < 0) {
|
||||
n[0] = -n[0];
|
||||
n[1] = -n[1];
|
||||
}
|
||||
float offset = piece.error() / 2.0f * _offset;
|
||||
StrokeInternal::StrokeVertexIterator v, vend;
|
||||
for (v = a, vend = stroke.strokeVerticesEnd(); v != vend; ++v) {
|
||||
v->setPoint(piece.A.x() + v->u() * u.x() + n.x() * offset,
|
||||
piece.A.y() + v->u() * u.y() + n.y() * offset);
|
||||
}
|
||||
stroke.UpdateLength();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////
|
||||
//
|
||||
// Tip Remover
|
||||
//
|
||||
/////////////////////////////////////////
|
||||
|
||||
TipRemoverShader::TipRemoverShader(real tipLength)
|
||||
{
|
||||
_tipLength = tipLength;
|
||||
}
|
||||
|
||||
int TipRemoverShader::shade(Stroke &stroke) const
|
||||
{
|
||||
int originalSize = stroke.strokeVerticesSize();
|
||||
|
||||
if (originalSize < 4) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
StrokeInternal::StrokeVertexIterator v, vend;
|
||||
vector<StrokeVertex *> verticesToRemove;
|
||||
vector<StrokeAttribute> oldAttributes;
|
||||
for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd(); v != vend; ++v) {
|
||||
if ((v->curvilinearAbscissa() < _tipLength) ||
|
||||
(v->strokeLength() - v->curvilinearAbscissa() < _tipLength))
|
||||
{
|
||||
verticesToRemove.push_back(&(*v));
|
||||
}
|
||||
oldAttributes.push_back(v->attribute());
|
||||
}
|
||||
|
||||
if (originalSize - verticesToRemove.size() < 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
vector<StrokeVertex *>::iterator sv, svend;
|
||||
for (sv = verticesToRemove.begin(), svend = verticesToRemove.end(); sv != svend; ++sv) {
|
||||
stroke.RemoveVertex(*sv);
|
||||
}
|
||||
|
||||
// Resample so that our new stroke have the same number of vertices than before
|
||||
stroke.Resample(originalSize);
|
||||
|
||||
if (int(stroke.strokeVerticesSize()) != originalSize) { // soc
|
||||
cerr << "Warning: resampling problem" << endl;
|
||||
}
|
||||
|
||||
// assign old attributes to new stroke vertices:
|
||||
vector<StrokeAttribute>::iterator a = oldAttributes.begin(), aend = oldAttributes.end();
|
||||
for (v = stroke.strokeVerticesBegin(), vend = stroke.strokeVerticesEnd();
|
||||
(v != vend) && (a != aend);
|
||||
++v, ++a)
|
||||
{
|
||||
v->setAttribute(*a);
|
||||
}
|
||||
// we're done!
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Freestyle::StrokeShaders
|
||||
@@ -0,0 +1,639 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class gathering basic stroke shaders
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "Stroke.h"
|
||||
#include "StrokeShader.h"
|
||||
|
||||
#include "../geometry/Bezier.h"
|
||||
#include "../geometry/Geom.h"
|
||||
|
||||
namespace blender {
|
||||
struct MTex;
|
||||
struct bNodeTree;
|
||||
} // namespace blender
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
using namespace Geometry;
|
||||
|
||||
namespace StrokeShaders {
|
||||
|
||||
//
|
||||
// Thickness modifiers
|
||||
//
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
/** [ Thickness Shader ].
|
||||
* Assigns an absolute constant thickness to every vertices of the Stroke.
|
||||
*/
|
||||
class ConstantThicknessShader : public StrokeShader {
|
||||
public:
|
||||
/** Builds the shader.
|
||||
* \param thickness:
|
||||
* The thickness that must be assigned to the stroke.
|
||||
*/
|
||||
ConstantThicknessShader(float thickness)
|
||||
{
|
||||
_thickness = thickness;
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~ConstantThicknessShader() {}
|
||||
|
||||
/** Returns the string "ConstantThicknessShader". */
|
||||
virtual string getName() const
|
||||
{
|
||||
return "ConstantThicknessShader";
|
||||
}
|
||||
|
||||
/** The shading method. */
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
|
||||
private:
|
||||
float _thickness;
|
||||
};
|
||||
|
||||
/* [ Thickness Shader ].
|
||||
* Assigns an absolute constant external thickness to every vertices of the Stroke. The external
|
||||
* thickness of a point is its thickness from the point to the strip border in the direction
|
||||
* pointing outside the object the Stroke delimiters.
|
||||
*/
|
||||
class ConstantExternThicknessShader : public StrokeShader {
|
||||
public:
|
||||
ConstantExternThicknessShader(float thickness)
|
||||
{
|
||||
_thickness = thickness;
|
||||
}
|
||||
|
||||
virtual ~ConstantExternThicknessShader() {}
|
||||
|
||||
virtual string getName() const
|
||||
{
|
||||
return "ConstantExternThicknessShader";
|
||||
}
|
||||
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
|
||||
private:
|
||||
float _thickness;
|
||||
};
|
||||
|
||||
/** [ Thickness Shader ].
|
||||
* Assigns thicknesses values such as the thickness increases from a thickness value A to a
|
||||
* thickness value B between the first vertex to the midpoint vertex and then decreases from B to a
|
||||
* A between this midpoint vertex and the last vertex. The thickness is linearly interpolated from
|
||||
* A to B.
|
||||
*/
|
||||
class IncreasingThicknessShader : public StrokeShader {
|
||||
public:
|
||||
/** Builds the shader.
|
||||
* \param iThicknessMin:
|
||||
* The first thickness value.
|
||||
* \param iThicknessMax:
|
||||
* The second thickness value.
|
||||
*/
|
||||
IncreasingThicknessShader(float iThicknessMin, float iThicknessMax)
|
||||
{
|
||||
_ThicknessMin = iThicknessMin;
|
||||
_ThicknessMax = iThicknessMax;
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~IncreasingThicknessShader() {}
|
||||
|
||||
virtual string getName() const
|
||||
{
|
||||
return "IncreasingThicknessShader";
|
||||
}
|
||||
|
||||
/** The shading method. */
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
|
||||
private:
|
||||
float _ThicknessMin;
|
||||
float _ThicknessMax;
|
||||
};
|
||||
|
||||
/** [ Thickness shader ].
|
||||
* Same as previous but here we allow the user to control the ratio thickness/length so that we
|
||||
* don't get fat short lines
|
||||
*/
|
||||
class 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)
|
||||
|
||||
{
|
||||
_ThicknessMin = iThicknessMin;
|
||||
_ThicknessMax = iThicknessMax;
|
||||
_ratio = iRatio;
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~ConstrainedIncreasingThicknessShader() {}
|
||||
|
||||
virtual string getName() const
|
||||
{
|
||||
return "ConstrainedIncreasingThicknessShader";
|
||||
}
|
||||
|
||||
/** The shading method. */
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
};
|
||||
|
||||
/* [ Thickness Shader ].
|
||||
* Modifies the thickness in a relative way depending on its length.
|
||||
*/
|
||||
class 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)
|
||||
{
|
||||
_minThickness = iMinThickness;
|
||||
_maxThickness = iMaxThickness;
|
||||
}
|
||||
|
||||
virtual ~LengthDependingThicknessShader() {}
|
||||
|
||||
virtual string getName() const
|
||||
{
|
||||
return "LengthDependingThicknessShader";
|
||||
}
|
||||
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
};
|
||||
|
||||
/** [ Thickness Shader ].
|
||||
* Adds some noise to the stroke thickness.
|
||||
* \see \htmlonly <a href=noise/noise.html>noise/noise.html</a>\endhtmlonly
|
||||
*/
|
||||
class ThicknessNoiseShader : public StrokeShader {
|
||||
private:
|
||||
float _amplitude;
|
||||
float _scale;
|
||||
|
||||
public:
|
||||
ThicknessNoiseShader();
|
||||
|
||||
/** Builds a Thickness Noise Shader
|
||||
* \param iAmplitude:
|
||||
* The amplitude of the noise signal
|
||||
* \param iPeriod:
|
||||
* The period of the noise signal
|
||||
*/
|
||||
ThicknessNoiseShader(float iAmplitude, float iPeriod);
|
||||
|
||||
virtual string getName() const
|
||||
{
|
||||
return "ThicknessNoiseShader";
|
||||
}
|
||||
|
||||
/** The shading method. */
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
};
|
||||
|
||||
//
|
||||
// Color shaders
|
||||
//
|
||||
/////////////////////////////////////////////////////////
|
||||
/** [ Color Shader ].
|
||||
* Assigns a constant color to every vertices of the Stroke.
|
||||
*/
|
||||
class ConstantColorShader : public StrokeShader {
|
||||
public:
|
||||
/** Builds the shader from a user-specified color.
|
||||
* \param iR:
|
||||
* The red component
|
||||
* \param iG:
|
||||
* The green component
|
||||
* \param iB:
|
||||
* The blue component
|
||||
* \param iAlpha:
|
||||
* The alpha value
|
||||
*/
|
||||
ConstantColorShader(float iR, float iG, float iB, float iAlpha = 1.0f)
|
||||
{
|
||||
_color[0] = iR;
|
||||
_color[1] = iG;
|
||||
_color[2] = iB;
|
||||
_color[3] = iAlpha;
|
||||
}
|
||||
|
||||
virtual string getName() const
|
||||
{
|
||||
return "ConstantColorShader";
|
||||
}
|
||||
|
||||
/** The shading method. */
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
|
||||
private:
|
||||
float _color[4];
|
||||
};
|
||||
|
||||
/** [ Color Shader ].
|
||||
* Assigns a varying color to the stroke.
|
||||
* The user specifies 2 colors A and B. The stroke color will change linearly from A to B between
|
||||
* the first and the last vertex.
|
||||
*/
|
||||
class 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)
|
||||
|
||||
{
|
||||
_colorMin[0] = iRm;
|
||||
_colorMin[1] = iGm;
|
||||
_colorMin[2] = iBm;
|
||||
_colorMin[3] = iAlpham;
|
||||
|
||||
_colorMax[0] = iRM;
|
||||
_colorMax[1] = iGM;
|
||||
_colorMax[2] = iBM;
|
||||
_colorMax[3] = iAlphaM;
|
||||
}
|
||||
|
||||
virtual string getName() const
|
||||
{
|
||||
return "IncreasingColorShader";
|
||||
}
|
||||
|
||||
/** The shading method. */
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
};
|
||||
|
||||
/* [ Color Shader ].
|
||||
* Assigns a color to the stroke depending on the material of the shape to which ot belongs to.
|
||||
* (Disney shader)
|
||||
*/
|
||||
class MaterialColorShader : public StrokeShader {
|
||||
private:
|
||||
float _coefficient;
|
||||
|
||||
public:
|
||||
MaterialColorShader(float coeff = 1.0f)
|
||||
{
|
||||
_coefficient = coeff;
|
||||
}
|
||||
|
||||
virtual string getName() const
|
||||
{
|
||||
return "MaterialColorShader";
|
||||
}
|
||||
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
};
|
||||
|
||||
/** [ Color Shader ].
|
||||
* Shader to add noise to the stroke colors.
|
||||
*/
|
||||
class ColorNoiseShader : public StrokeShader {
|
||||
private:
|
||||
float _amplitude;
|
||||
float _scale;
|
||||
|
||||
public:
|
||||
ColorNoiseShader();
|
||||
|
||||
/** Builds a Color Noise Shader
|
||||
* \param iAmplitude:
|
||||
* The amplitude of the noise signal
|
||||
* \param iPeriod:
|
||||
* The period of the noise signal
|
||||
*/
|
||||
ColorNoiseShader(float iAmplitude, float iPeriod);
|
||||
|
||||
virtual string getName() const
|
||||
{
|
||||
return "ColorNoiseShader";
|
||||
}
|
||||
|
||||
/** The shading method. */
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
};
|
||||
|
||||
//
|
||||
// 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 BackboneStretcherShader : public StrokeShader {
|
||||
private:
|
||||
float _amount;
|
||||
|
||||
public:
|
||||
/** Builds the shader.
|
||||
* \param iAmount:
|
||||
* The stretching amount value.
|
||||
*/
|
||||
BackboneStretcherShader(float iAmount = 2.0f)
|
||||
{
|
||||
_amount = iAmount;
|
||||
}
|
||||
|
||||
virtual string getName() const
|
||||
{
|
||||
return "BackboneStretcherShader";
|
||||
}
|
||||
|
||||
/** The shading method */
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
};
|
||||
|
||||
/** [ Geometry Shader. ]
|
||||
* Resamples the stroke.
|
||||
* \see Stroke::Resample(float).
|
||||
*/
|
||||
class SamplingShader : public StrokeShader {
|
||||
private:
|
||||
float _sampling;
|
||||
|
||||
public:
|
||||
/** Builds the shader.
|
||||
* \param sampling:
|
||||
* The sampling to use for the stroke resampling
|
||||
*/
|
||||
SamplingShader(float sampling)
|
||||
{
|
||||
_sampling = sampling;
|
||||
}
|
||||
|
||||
virtual string getName() const
|
||||
{
|
||||
return "SamplingShader";
|
||||
}
|
||||
|
||||
/** The shading method */
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
};
|
||||
|
||||
class ExternalContourStretcherShader : public StrokeShader {
|
||||
private:
|
||||
float _amount;
|
||||
|
||||
public:
|
||||
ExternalContourStretcherShader(float iAmount = 2.0f)
|
||||
{
|
||||
_amount = iAmount;
|
||||
}
|
||||
|
||||
virtual string getName() const
|
||||
{
|
||||
return "ExternalContourStretcherShader";
|
||||
}
|
||||
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
};
|
||||
|
||||
// Bezier curve stroke shader
|
||||
/** [ Geometry Shader ].
|
||||
* Transforms the stroke backbone geometry so that it corresponds to a Bezier Curve approximation
|
||||
* of the original backbone geometry. \see \htmlonly <a
|
||||
* href=bezier/bezier.html>bezier/bezier.html</a> \endhtmlonly
|
||||
*/
|
||||
class 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)
|
||||
{
|
||||
_error = error;
|
||||
}
|
||||
|
||||
virtual string getName() const
|
||||
{
|
||||
return "BezierCurveShader";
|
||||
}
|
||||
|
||||
/** The shading method */
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
};
|
||||
|
||||
/** [ Geometry Shader ].
|
||||
* Shader to modify the Stroke geometry so that it looks more "polygonal".
|
||||
* The basic idea is to start from the minimal stroke approximation consisting in a line joining
|
||||
* the first vertex to the last one and to subdivide using the original stroke vertices until a
|
||||
* certain error is reached.
|
||||
*/
|
||||
class 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 original one.
|
||||
* This error corresponds * to the maximum distance between the new stroke and the old one.
|
||||
*/
|
||||
PolygonalizationShader(float iError)
|
||||
{
|
||||
_error = iError;
|
||||
}
|
||||
|
||||
virtual string getName() const
|
||||
{
|
||||
return "PolygonalizationShader";
|
||||
}
|
||||
|
||||
/** The shading method */
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
};
|
||||
|
||||
/** [ Geometry Shader ].
|
||||
* Shader to modify the Stroke geometry so that it corresponds to its main direction line.
|
||||
* This shader must be used together with the splitting operator using the curvature criterion.
|
||||
* Indeed, the precision of the approximation will depend on the size of the stroke's pieces.
|
||||
* The bigger the pieces, the rougher the approximation.
|
||||
*/
|
||||
class 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)
|
||||
{
|
||||
_offset = iOffset;
|
||||
}
|
||||
|
||||
virtual string getName() const
|
||||
{
|
||||
return "GuidingLinesShader";
|
||||
}
|
||||
|
||||
/** The shading method */
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
};
|
||||
|
||||
/** [ Geometry Shader ].
|
||||
* Removes the stroke's extremities.
|
||||
*/
|
||||
class TipRemoverShader : public StrokeShader {
|
||||
public:
|
||||
/** Builds the shader.
|
||||
* \param tipLength:
|
||||
* The length of the piece of stroke we want to remove at each extremity.
|
||||
*/
|
||||
TipRemoverShader(real tipLength);
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~TipRemoverShader() {}
|
||||
|
||||
/** The shading method */
|
||||
virtual string getName() const
|
||||
{
|
||||
return "TipRemoverShader";
|
||||
}
|
||||
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
|
||||
protected:
|
||||
real _tipLength;
|
||||
};
|
||||
|
||||
/**
|
||||
* Texture Shader.
|
||||
*
|
||||
* Shader to assign texture to the Stroke material.
|
||||
*/
|
||||
class BlenderTextureShader : public StrokeShader {
|
||||
private:
|
||||
blender::MTex *_mtex;
|
||||
blender::bNodeTree *_nodeTree;
|
||||
|
||||
public:
|
||||
/** Builds the shader.
|
||||
* \param mtex:
|
||||
* The blender texture to use.
|
||||
*/
|
||||
BlenderTextureShader(blender::MTex *mtex)
|
||||
{
|
||||
_mtex = mtex;
|
||||
_nodeTree = nullptr;
|
||||
}
|
||||
|
||||
/** Builds the shader.
|
||||
* \param nodetree:
|
||||
* A node tree (of new shading nodes) to define textures.
|
||||
*/
|
||||
BlenderTextureShader(blender::bNodeTree *nodetree)
|
||||
{
|
||||
_nodeTree = nodetree;
|
||||
_mtex = nullptr;
|
||||
}
|
||||
|
||||
virtual string getName() const
|
||||
{
|
||||
return "BlenderTextureShader";
|
||||
}
|
||||
|
||||
/** The shading method */
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Texture Shader.
|
||||
*
|
||||
* Shader to assign texture to the Stroke material.
|
||||
*/
|
||||
class StrokeTextureStepShader : public StrokeShader {
|
||||
private:
|
||||
float _step;
|
||||
|
||||
public:
|
||||
/** Builds the shader.
|
||||
* \param step:
|
||||
* The number of the preset to use.
|
||||
*/
|
||||
StrokeTextureStepShader(float step)
|
||||
{
|
||||
_step = step;
|
||||
}
|
||||
|
||||
virtual string getName() const
|
||||
{
|
||||
return "StrokeTextureStepShader";
|
||||
}
|
||||
|
||||
/** The shading method */
|
||||
virtual int shade(Stroke &stroke) const;
|
||||
};
|
||||
|
||||
} // end of namespace StrokeShaders
|
||||
|
||||
} /* namespace Freestyle */
|
||||
478
blender-5.2.0/source/blender/freestyle/intern/stroke/Canvas.cpp
Normal file
478
blender-5.2.0/source/blender/freestyle/intern/stroke/Canvas.cpp
Normal file
@@ -0,0 +1,478 @@
|
||||
/* SPDX-FileCopyrightText: 2008-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class to define a canvas designed to draw style modules
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "Canvas.h"
|
||||
#include "StrokeRenderer.h"
|
||||
#include "StyleModule.h"
|
||||
|
||||
#include "../image/GaussianFilter.h"
|
||||
#include "../image/Image.h"
|
||||
#include "../image/ImagePyramid.h"
|
||||
|
||||
#include "../system/FreestyleConfig.h"
|
||||
#include "../system/PseudoNoise.h"
|
||||
#include "../system/TimeStamp.h"
|
||||
|
||||
#include "../view_map/SteerableViewMap.h"
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
// soc #include <qimage.h>
|
||||
// soc #include <QString>
|
||||
|
||||
#include "IMB_imbuf.hh"
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
Canvas *Canvas::_pInstance = nullptr;
|
||||
|
||||
const char *Canvas::_MapsPath = nullptr;
|
||||
|
||||
Canvas::Canvas()
|
||||
{
|
||||
_SelectedFEdge = nullptr;
|
||||
_pInstance = this;
|
||||
PseudoNoise::init(42);
|
||||
_Renderer = nullptr;
|
||||
_current_sm = nullptr;
|
||||
_steerableViewMap = new SteerableViewMap(NB_STEERABLE_VIEWMAP - 1);
|
||||
_basic = false;
|
||||
}
|
||||
|
||||
Canvas::Canvas(const Canvas &iBrother)
|
||||
{
|
||||
_SelectedFEdge = iBrother._SelectedFEdge;
|
||||
_pInstance = this;
|
||||
PseudoNoise::init(42);
|
||||
_Renderer = iBrother._Renderer;
|
||||
_current_sm = iBrother._current_sm;
|
||||
_steerableViewMap = new SteerableViewMap(*(iBrother._steerableViewMap));
|
||||
_basic = iBrother._basic;
|
||||
}
|
||||
|
||||
Canvas::~Canvas()
|
||||
{
|
||||
_pInstance = nullptr;
|
||||
|
||||
Clear();
|
||||
if (_Renderer) {
|
||||
delete _Renderer;
|
||||
_Renderer = nullptr;
|
||||
}
|
||||
// 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();
|
||||
}
|
||||
delete _steerableViewMap;
|
||||
}
|
||||
|
||||
void Canvas::preDraw() {}
|
||||
|
||||
void Canvas::Draw()
|
||||
{
|
||||
if (_StyleModules.empty()) {
|
||||
return;
|
||||
}
|
||||
preDraw();
|
||||
TimeStamp *timestamp = TimeStamp::instance();
|
||||
|
||||
for (uint i = 0; i < _StyleModules.size(); ++i) {
|
||||
_current_sm = _StyleModules[i];
|
||||
|
||||
if (i < _Layers.size() && _Layers[i]) {
|
||||
delete _Layers[i];
|
||||
}
|
||||
|
||||
_Layers[i] = _StyleModules[i]->execute();
|
||||
if (!_Layers[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
stroke_count += _Layers[i]->strokes_size();
|
||||
|
||||
timestamp->increment();
|
||||
}
|
||||
postDraw();
|
||||
}
|
||||
|
||||
void Canvas::postDraw()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
void Canvas::Clear()
|
||||
{
|
||||
if (!_Layers.empty()) {
|
||||
for (deque<StrokeLayer *>::iterator sl = _Layers.begin(), slend = _Layers.end(); sl != slend;
|
||||
++sl)
|
||||
{
|
||||
if (*sl) {
|
||||
delete (*sl);
|
||||
}
|
||||
}
|
||||
_Layers.clear();
|
||||
}
|
||||
|
||||
if (!_StyleModules.empty()) {
|
||||
for (deque<StyleModule *>::iterator s = _StyleModules.begin(), send = _StyleModules.end();
|
||||
s != send;
|
||||
++s)
|
||||
{
|
||||
if (*s) {
|
||||
delete (*s);
|
||||
}
|
||||
}
|
||||
_StyleModules.clear();
|
||||
}
|
||||
if (_steerableViewMap) {
|
||||
_steerableViewMap->Reset();
|
||||
}
|
||||
|
||||
stroke_count = 0;
|
||||
}
|
||||
|
||||
void Canvas::Erase()
|
||||
{
|
||||
if (!_Layers.empty()) {
|
||||
for (deque<StrokeLayer *>::iterator sl = _Layers.begin(), slend = _Layers.end(); sl != slend;
|
||||
++sl)
|
||||
{
|
||||
if (*sl) {
|
||||
(*sl)->clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_steerableViewMap) {
|
||||
_steerableViewMap->Reset();
|
||||
}
|
||||
update();
|
||||
|
||||
stroke_count = 0;
|
||||
}
|
||||
|
||||
void Canvas::PushBackStyleModule(StyleModule *iStyleModule)
|
||||
{
|
||||
StrokeLayer *layer = new StrokeLayer();
|
||||
_StyleModules.push_back(iStyleModule);
|
||||
_Layers.push_back(layer);
|
||||
}
|
||||
|
||||
void Canvas::InsertStyleModule(uint index, StyleModule *iStyleModule)
|
||||
{
|
||||
uint 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(uint index)
|
||||
{
|
||||
uint i = 0;
|
||||
if (!_StyleModules.empty()) {
|
||||
for (deque<StyleModule *>::iterator s = _StyleModules.begin(), send = _StyleModules.end();
|
||||
s != send;
|
||||
++s, ++i)
|
||||
{
|
||||
if (i == index) {
|
||||
// remove shader
|
||||
if (*s) {
|
||||
delete *s;
|
||||
}
|
||||
_StyleModules.erase(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!_Layers.empty()) {
|
||||
i = 0;
|
||||
for (deque<StrokeLayer *>::iterator sl = _Layers.begin(), slend = _Layers.end(); sl != slend;
|
||||
++sl, ++i)
|
||||
{
|
||||
if (i == index) {
|
||||
// remove layer
|
||||
if (*sl) {
|
||||
delete *sl;
|
||||
}
|
||||
_Layers.erase(sl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::SwapStyleModules(uint i1, uint 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(uint index, StyleModule *iStyleModule)
|
||||
{
|
||||
uint i = 0;
|
||||
for (deque<StyleModule *>::iterator s = _StyleModules.begin(), send = _StyleModules.end();
|
||||
s != send;
|
||||
++s, ++i)
|
||||
{
|
||||
if (i == index) {
|
||||
if (*s) {
|
||||
delete *s;
|
||||
}
|
||||
*s = iStyleModule;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::setVisible(uint index, bool iVisible)
|
||||
{
|
||||
_StyleModules[index]->setDisplayed(iVisible);
|
||||
}
|
||||
|
||||
void Canvas::setModified(uint index, bool iMod)
|
||||
{
|
||||
_StyleModules[index]->setModified(iMod);
|
||||
}
|
||||
|
||||
void Canvas::resetModified(bool iMod /* = false */)
|
||||
{
|
||||
uint size = _StyleModules.size();
|
||||
for (uint i = 0; i < size; ++i) {
|
||||
setModified(i, iMod);
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::causalStyleModules(vector<uint> &vec, uint index)
|
||||
{
|
||||
uint size = _StyleModules.size();
|
||||
|
||||
for (uint i = index; i < size; ++i) {
|
||||
if (_StyleModules[i]->getCausal()) {
|
||||
vec.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::Render(const StrokeRenderer *iRenderer)
|
||||
{
|
||||
for (uint i = 0; i < _StyleModules.size(); ++i) {
|
||||
if (!_StyleModules[i]->getDisplayed() || !_Layers[i]) {
|
||||
continue;
|
||||
}
|
||||
_Layers[i]->Render(iRenderer);
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::RenderBasic(const StrokeRenderer *iRenderer)
|
||||
{
|
||||
for (uint 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, uint iNbLevels, float iSigma)
|
||||
{
|
||||
// check whether this map was already loaded:
|
||||
if (!_maps.empty()) {
|
||||
mapsMap::iterator m = _maps.find(iMapName);
|
||||
if (m != _maps.end()) {
|
||||
// lazy check for size changes
|
||||
ImagePyramid *pyramid = (*m).second;
|
||||
if ((pyramid->width() != width()) || (pyramid->height() != height())) {
|
||||
delete pyramid;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string filePath;
|
||||
if (_MapsPath) {
|
||||
filePath = _MapsPath;
|
||||
filePath += iFileName;
|
||||
}
|
||||
else {
|
||||
filePath = iFileName;
|
||||
}
|
||||
|
||||
#if 0 // soc
|
||||
QImage *qimg;
|
||||
QImage newMap(filePath.c_str());
|
||||
if (newMap.isNull()) {
|
||||
cerr << "Could not load image file " << filePath << endl;
|
||||
return;
|
||||
}
|
||||
qimg = &newMap;
|
||||
#endif
|
||||
/* OCIO_TODO: support different input color space */
|
||||
blender::ImBuf *qimg = blender::IMB_load_image_from_filepath(filePath.c_str(),
|
||||
blender::ImBufFlags::Zero);
|
||||
if (qimg == nullptr) {
|
||||
cerr << "Could not load image file " << filePath << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0 // soc
|
||||
// resize
|
||||
QImage scaledImg;
|
||||
if ((newMap.width() != width()) || (newMap.height() != height())) {
|
||||
scaledImg = newMap.scaled(width(), height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||
qimg = &scaledImg;
|
||||
}
|
||||
#endif
|
||||
blender::ImBuf *scaledImg;
|
||||
if ((qimg->x != width()) || (qimg->y != height())) {
|
||||
scaledImg = IMB_dupImBuf(qimg);
|
||||
blender::IMB_scale(scaledImg, width(), height(), blender::IMBScaleFilter::Box, false);
|
||||
}
|
||||
|
||||
// deal with color image
|
||||
#if 0
|
||||
if (newMap->depth() != 8) {
|
||||
int w = newMap->width();
|
||||
int h = newMap->height();
|
||||
QImage *tmp = new QImage(w, h, 8);
|
||||
for (uint y = 0; y < h; ++y) {
|
||||
for (uint x = 0; x < w; ++x) {
|
||||
int c = qGray(newMap->pixel(x, y));
|
||||
tmp->setPixel(x, y, c);
|
||||
}
|
||||
}
|
||||
delete newMap;
|
||||
newMap = tmp;
|
||||
}
|
||||
#endif
|
||||
|
||||
int x, y;
|
||||
int w = qimg->x;
|
||||
int h = qimg->y;
|
||||
int rowbytes = w * 4;
|
||||
GrayImage tmp(w, h);
|
||||
uchar *pix;
|
||||
|
||||
uchar *qimg_byte_data = qimg->byte_data_for_write();
|
||||
for (y = 0; y < h; ++y) {
|
||||
for (x = 0; x < w; ++x) {
|
||||
pix = qimg_byte_data + y * rowbytes + x * 4;
|
||||
float c = (pix[0] * 11 + pix[1] * 16 + pix[2] * 5) / 32;
|
||||
tmp.setPixel(x, y, c);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
GrayImage blur(w, h);
|
||||
GaussianFilter gf(4.0f);
|
||||
// int bound = gf.getBound();
|
||||
for (y = 0; y < h; ++y) {
|
||||
for (x = 0; x < w; ++x) {
|
||||
int c = gf.getSmoothedPixel<GrayImage>(&tmp, x, y);
|
||||
blur.setPixel(x, y, c);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
GaussianPyramid *pyramid = new GaussianPyramid(tmp, iNbLevels, iSigma);
|
||||
int ow = pyramid->width(0);
|
||||
int oh = pyramid->height(0);
|
||||
string base(iMapName); // soc
|
||||
for (int i = 0; i < pyramid->getNumberOfLevels(); ++i) {
|
||||
// save each image:
|
||||
#if 0
|
||||
w = pyramid.width(i);
|
||||
h = pyramid.height(i);
|
||||
#endif
|
||||
|
||||
// soc QImage qtmp(ow, oh, QImage::Format_RGB32);
|
||||
blender::ImBuf *qtmp = IMB_allocImBuf(ow, oh, blender::ImBufFlags::ByteData);
|
||||
|
||||
// int k = (1 << i);
|
||||
uchar *qtmp_byte_data = qtmp->byte_data_for_write();
|
||||
for (y = 0; y < oh; ++y) {
|
||||
for (x = 0; x < ow; ++x) {
|
||||
int c = pyramid->pixel(x, y, i); // 255 * pyramid->pixel(x, y, i);
|
||||
// soc qtmp.setPixel(x, y, qRgb(c, c, c));
|
||||
pix = qtmp_byte_data + y * rowbytes + x * 4;
|
||||
pix[0] = pix[1] = pix[2] = c;
|
||||
}
|
||||
}
|
||||
// soc qtmp.save(base + QString::number(i) + ".bmp", "BMP");
|
||||
stringstream filepath;
|
||||
filepath << base;
|
||||
filepath << i << ".bmp";
|
||||
qtmp->ftype = blender::IMB_FTYPE_BMP;
|
||||
IMB_save_image(qtmp, const_cast<char *>(filepath.str().c_str()), blender::ImBufFlags::Zero);
|
||||
}
|
||||
|
||||
#if 0
|
||||
QImage *qtmp = new QImage(w, h, 32);
|
||||
for (y = 0; y < h; ++y) {
|
||||
for (x = 0; x < w; ++x) {
|
||||
int c = int(blur.pixel(x, y));
|
||||
qtmp->setPixel(x, y, qRgb(c, c, c));
|
||||
}
|
||||
}
|
||||
delete newMap;
|
||||
newMap = qtmp;
|
||||
#endif
|
||||
|
||||
_maps[iMapName] = pyramid;
|
||||
// newMap->save("toto.bmp", "BMP");
|
||||
}
|
||||
|
||||
float Canvas::readMapPixel(const char *iMapName, int level, int x, int y)
|
||||
{
|
||||
if (_maps.empty()) {
|
||||
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
|
||||
cout << "readMapPixel warning: no map was loaded " << endl;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
mapsMap::iterator m = _maps.find(iMapName);
|
||||
if (m == _maps.end()) {
|
||||
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
|
||||
cout << "readMapPixel warning: no map was loaded with the name " << iMapName << endl;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
ImagePyramid *pyramid = (*m).second;
|
||||
if ((x < 0) || (x >= pyramid->width()) || (y < 0) || (y >= pyramid->height())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return pyramid->pixel(x, height() - 1 - y, level);
|
||||
}
|
||||
|
||||
} /* namespace Freestyle */
|
||||
237
blender-5.2.0/source/blender/freestyle/intern/stroke/Canvas.h
Normal file
237
blender-5.2.0/source/blender/freestyle/intern/stroke/Canvas.h
Normal file
@@ -0,0 +1,237 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class to define a canvas designed to draw style modules
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "StrokeLayer.h"
|
||||
|
||||
#include "../geometry/BBox.h"
|
||||
#include "../geometry/Geom.h"
|
||||
|
||||
#include "../system/FreestyleConfig.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
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 Canvas {
|
||||
public:
|
||||
/** Returns a pointer on the Canvas instance */
|
||||
static Canvas *getInstance()
|
||||
{
|
||||
return _pInstance;
|
||||
}
|
||||
|
||||
typedef std::map<const char *, ImagePyramid *, ltstr> mapsMap;
|
||||
static const int NB_STEERABLE_VIEWMAP = 5;
|
||||
|
||||
protected:
|
||||
static Canvas *_pInstance;
|
||||
std::deque<StrokeLayer *> _Layers;
|
||||
std::deque<StyleModule *> _StyleModules;
|
||||
FEdge *_SelectedFEdge;
|
||||
|
||||
StrokeRenderer *_Renderer;
|
||||
StyleModule *_current_sm;
|
||||
mapsMap _maps;
|
||||
static const char *_MapsPath;
|
||||
SteerableViewMap *_steerableViewMap;
|
||||
bool _basic;
|
||||
int stroke_count;
|
||||
|
||||
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,
|
||||
uint iNbLevels = 4,
|
||||
float iSigma = 1.0f);
|
||||
|
||||
/** Reads a pixel value in a map.
|
||||
* Returns a value between 0 and 1.
|
||||
* \param iMapName:
|
||||
* The name of the map
|
||||
* \param level:
|
||||
* The level of the pyramid from which the pixel must be read.
|
||||
* \param x:
|
||||
* The abscissa of the desired pixel specified in level0 coordinate system.
|
||||
* The origin is the lower left corner.
|
||||
* \param y:
|
||||
* The ordinate of the desired pixel specified in level0 coordinate system.
|
||||
* The origin is the lower left corner.
|
||||
*/
|
||||
float readMapPixel(const char *iMapName, int level, int x, int y);
|
||||
|
||||
/** Sets the steerable viewmap */
|
||||
void loadSteerableViewMap(SteerableViewMap *iSVM)
|
||||
{
|
||||
_steerableViewMap = iSVM;
|
||||
}
|
||||
|
||||
/** Returns the steerable VM */
|
||||
SteerableViewMap *getSteerableViewMap()
|
||||
{
|
||||
return _steerableViewMap;
|
||||
}
|
||||
|
||||
/** accessors */
|
||||
inline const FEdge *selectedFEdge() const
|
||||
{
|
||||
return _SelectedFEdge;
|
||||
}
|
||||
|
||||
inline FEdge *selectedFEdge()
|
||||
{
|
||||
return _SelectedFEdge;
|
||||
}
|
||||
|
||||
virtual int width() const = 0;
|
||||
virtual int height() const = 0;
|
||||
virtual BBox<Vec2i> border() const = 0;
|
||||
virtual BBox<Vec3r> scene3DBBox() const = 0;
|
||||
|
||||
inline const StrokeRenderer *renderer() const
|
||||
{
|
||||
return _Renderer;
|
||||
}
|
||||
|
||||
inline StyleModule *getCurrentStyleModule()
|
||||
{
|
||||
return _current_sm;
|
||||
}
|
||||
|
||||
virtual bool getRecordFlag() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
inline int getStrokeCount() const
|
||||
{
|
||||
return stroke_count;
|
||||
}
|
||||
|
||||
/** modifiers */
|
||||
inline void setSelectedFEdge(FEdge *iFEdge)
|
||||
{
|
||||
_SelectedFEdge = iFEdge;
|
||||
}
|
||||
|
||||
/** inserts a shader at pos index+1 */
|
||||
void PushBackStyleModule(StyleModule *iStyleModule);
|
||||
void InsertStyleModule(uint index, StyleModule *iStyleModule);
|
||||
void RemoveStyleModule(uint index);
|
||||
void SwapStyleModules(uint i1, uint i2);
|
||||
void ReplaceStyleModule(uint index, StyleModule *iStyleModule);
|
||||
void setVisible(uint index, bool iVisible);
|
||||
|
||||
#if 0
|
||||
inline void setDensityMap(InformationMap<RGBImage> *iMap)
|
||||
{
|
||||
_DensityMap = iMap;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline void AddLayer(StrokeLayer *iLayer)
|
||||
{
|
||||
_Layers.push_back(iLayer);
|
||||
}
|
||||
|
||||
void resetModified(bool iMod = false);
|
||||
void causalStyleModules(std::vector<uint> &vec, uint index = 0);
|
||||
void setModified(uint index, bool iMod);
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Canvas")
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
148
blender-5.2.0/source/blender/freestyle/intern/stroke/Chain.cpp
Normal file
148
blender-5.2.0/source/blender/freestyle/intern/stroke/Chain.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class to define a chain of view-edges.
|
||||
*/
|
||||
|
||||
#include "Chain.h"
|
||||
|
||||
#include "../view_map/ViewMapAdvancedIterators.h"
|
||||
#include "../view_map/ViewMapIterators.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
void Chain::push_viewedge_back(ViewEdge *iViewEdge, bool orientation)
|
||||
{
|
||||
ViewEdge::vertex_iterator v;
|
||||
ViewEdge::vertex_iterator vend;
|
||||
ViewEdge::vertex_iterator vfirst;
|
||||
Vec3r previous, current;
|
||||
if (true == orientation) {
|
||||
v = iViewEdge->vertices_begin();
|
||||
vfirst = v;
|
||||
vend = iViewEdge->vertices_end();
|
||||
}
|
||||
else {
|
||||
v = iViewEdge->vertices_last();
|
||||
vfirst = v;
|
||||
vend = iViewEdge->vertices_end();
|
||||
}
|
||||
|
||||
if (!_Vertices.empty()) {
|
||||
previous = _Vertices.back()->point2d();
|
||||
if (orientation) {
|
||||
++v;
|
||||
}
|
||||
else {
|
||||
--v;
|
||||
}
|
||||
// Ensure the continuity of underlying FEdges
|
||||
CurvePoint *cp =
|
||||
_Vertices.back(); // assumed to be instantiated as new CurvePoint(iSVertex, 0, 0.0f);
|
||||
SVertex *sv_first = (*vfirst);
|
||||
FEdge *fe = _fedgeB->duplicate();
|
||||
fe->setTemporary(true);
|
||||
fe->setVertexB(sv_first);
|
||||
fe->vertexA()->shape()->AddEdge(fe);
|
||||
fe->vertexA()->AddFEdge(fe);
|
||||
fe->vertexB()->AddFEdge(fe);
|
||||
cp->setA(sv_first);
|
||||
}
|
||||
else {
|
||||
previous = (*v)->point2d();
|
||||
}
|
||||
do {
|
||||
current = (*v)->point2d();
|
||||
Curve::push_vertex_back(*v);
|
||||
//_Length += (current - previous).norm();
|
||||
previous = current;
|
||||
if (orientation) {
|
||||
++v;
|
||||
}
|
||||
else {
|
||||
--v;
|
||||
}
|
||||
} while ((v != vend) && (v != vfirst));
|
||||
|
||||
if (v == vfirst) {
|
||||
// Add last one:
|
||||
current = (*v)->point2d();
|
||||
Curve::push_vertex_back(*v);
|
||||
//_Length += (current - previous).norm();
|
||||
}
|
||||
|
||||
_fedgeB = (orientation) ? iViewEdge->fedgeB() : iViewEdge->fedgeA();
|
||||
}
|
||||
|
||||
void Chain::push_viewedge_front(ViewEdge *iViewEdge, bool orientation)
|
||||
{
|
||||
orientation = !orientation;
|
||||
ViewEdge::vertex_iterator v;
|
||||
ViewEdge::vertex_iterator vend;
|
||||
ViewEdge::vertex_iterator vfirst;
|
||||
Vec3r previous, current;
|
||||
if (true == orientation) {
|
||||
v = iViewEdge->vertices_begin();
|
||||
vfirst = v;
|
||||
vend = iViewEdge->vertices_end();
|
||||
}
|
||||
else {
|
||||
v = iViewEdge->vertices_last();
|
||||
vfirst = v;
|
||||
vend = iViewEdge->vertices_end();
|
||||
}
|
||||
|
||||
if (!_Vertices.empty()) {
|
||||
previous = _Vertices.front()->point2d();
|
||||
if (orientation) {
|
||||
++v;
|
||||
}
|
||||
else {
|
||||
--v;
|
||||
}
|
||||
// Ensure the continuity of underlying FEdges
|
||||
CurvePoint *cp =
|
||||
_Vertices.front(); // assumed to be instantiated as new CurvePoint(iSVertex, 0, 0.0f);
|
||||
SVertex *sv_last = cp->A();
|
||||
SVertex *sv_curr = (*v);
|
||||
FEdge *fe = (orientation) ? iViewEdge->fedgeA() : iViewEdge->fedgeB();
|
||||
FEdge *fe2 = fe->duplicate();
|
||||
fe2->setTemporary(true);
|
||||
fe2->setVertexA(sv_curr);
|
||||
fe2->setVertexB(sv_last);
|
||||
sv_last->AddFEdge(fe2);
|
||||
sv_curr->AddFEdge(fe2);
|
||||
sv_curr->shape()->AddEdge(fe2);
|
||||
}
|
||||
else {
|
||||
previous = (*v)->point2d();
|
||||
}
|
||||
do {
|
||||
current = (*v)->point2d();
|
||||
Curve::push_vertex_front(*v);
|
||||
//_Length += (current - previous).norm();
|
||||
previous = current;
|
||||
if (orientation) {
|
||||
++v;
|
||||
}
|
||||
else {
|
||||
--v;
|
||||
}
|
||||
} while ((v != vend) && (v != vfirst));
|
||||
|
||||
if (v == vfirst) {
|
||||
// Add last one:
|
||||
current = (*v)->point2d();
|
||||
Curve::push_vertex_front(*v);
|
||||
//_Length += (current - previous).norm();
|
||||
}
|
||||
|
||||
if (!_fedgeB) {
|
||||
_fedgeB = (orientation) ? iViewEdge->fedgeB() : iViewEdge->fedgeA();
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace Freestyle */
|
||||
96
blender-5.2.0/source/blender/freestyle/intern/stroke/Chain.h
Normal file
96
blender-5.2.0/source/blender/freestyle/intern/stroke/Chain.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class to define a chain of view-edges.
|
||||
*/
|
||||
|
||||
#include "Curve.h"
|
||||
|
||||
#include "../view_map/ViewMap.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
/** Class to represent a 1D elements issued from the chaining process.
|
||||
* A Chain is the last step before the Stroke and is used in the Splitting and Creation processes.
|
||||
*/
|
||||
class Chain : public Curve {
|
||||
protected:
|
||||
// tmp
|
||||
Id *_splittingId;
|
||||
FEdge *
|
||||
_fedgeB; // the last FEdge of the ViewEdge passed to the last call for push_viewedge_back().
|
||||
|
||||
public:
|
||||
/** Default constructor. */
|
||||
Chain() : Curve()
|
||||
{
|
||||
_splittingId = 0;
|
||||
_fedgeB = 0;
|
||||
}
|
||||
|
||||
/** Builds a chain from its Id. */
|
||||
Chain(const Id &id) : Curve(id)
|
||||
{
|
||||
_splittingId = 0;
|
||||
_fedgeB = 0;
|
||||
}
|
||||
|
||||
/** Copy Constructor */
|
||||
Chain(const Chain &iBrother) : Curve(iBrother)
|
||||
{
|
||||
_splittingId = iBrother._splittingId;
|
||||
_fedgeB = iBrother._fedgeB;
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~Chain()
|
||||
{
|
||||
// only the last split deletes this id
|
||||
if (_splittingId) {
|
||||
if (*_splittingId == _Id) {
|
||||
delete _splittingId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the string "Chain" */
|
||||
virtual string getExactTypeName() const
|
||||
{
|
||||
return "Chain";
|
||||
}
|
||||
|
||||
/** Adds a ViewEdge at the end of the chain
|
||||
* \param iViewEdge:
|
||||
* The ViewEdge that must be added.
|
||||
* \param orientation:
|
||||
* The orientation with which this ViewEdge must be processed.
|
||||
*/
|
||||
void push_viewedge_back(ViewEdge *iViewEdge, bool orientation);
|
||||
|
||||
/** Adds a ViewEdge at the beginning of the chain
|
||||
* \param iViewEdge:
|
||||
* The ViewEdge that must be added.
|
||||
* \param orientation:
|
||||
* The orientation with which this ViewEdge must be processed.
|
||||
*/
|
||||
void push_viewedge_front(ViewEdge *iViewEdge, bool orientation);
|
||||
|
||||
inline void setSplittingId(Id *sid)
|
||||
{
|
||||
_splittingId = sid;
|
||||
}
|
||||
|
||||
inline Id *getSplittingId()
|
||||
{
|
||||
return _splittingId;
|
||||
}
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Chain")
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,213 @@
|
||||
/* SPDX-FileCopyrightText: 2008-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Chaining iterators
|
||||
*/
|
||||
|
||||
#include "../python/Director.h"
|
||||
|
||||
#include "ChainingIterators.h"
|
||||
|
||||
#include "../system/TimeStamp.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
ViewEdge *AdjacencyIterator::operator*()
|
||||
{
|
||||
return (*_internalIterator).first;
|
||||
}
|
||||
|
||||
bool AdjacencyIterator::isIncoming() const
|
||||
{
|
||||
return (*_internalIterator).second;
|
||||
}
|
||||
|
||||
int AdjacencyIterator::increment()
|
||||
{
|
||||
++_internalIterator;
|
||||
while (!_internalIterator.isEnd() && !isValid((*_internalIterator).first)) {
|
||||
++_internalIterator;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AdjacencyIterator::isValid(ViewEdge *edge)
|
||||
{
|
||||
if (_restrictToSelection) {
|
||||
if (edge->getTimeStamp() != TimeStamp::instance()->getTimeStamp()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (_restrictToUnvisited) {
|
||||
if (edge->getChainingTimeStamp() > TimeStamp::instance()->getTimeStamp()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int ChainingIterator::init()
|
||||
{
|
||||
return Director_BPy_ChainingIterator_init(this);
|
||||
}
|
||||
|
||||
int ChainingIterator::traverse(const AdjacencyIterator &it)
|
||||
{
|
||||
return Director_BPy_ChainingIterator_traverse(this, const_cast<AdjacencyIterator &>(it));
|
||||
}
|
||||
|
||||
int ChainingIterator::increment()
|
||||
{
|
||||
_increment = true;
|
||||
ViewVertex *vertex = getVertex();
|
||||
if (!vertex) {
|
||||
_edge = nullptr;
|
||||
return 0;
|
||||
}
|
||||
AdjacencyIterator it = AdjacencyIterator(vertex, _restrictToSelection, _restrictToUnvisited);
|
||||
if (it.isEnd()) {
|
||||
_edge = nullptr;
|
||||
return 0;
|
||||
}
|
||||
if (traverse(it) < 0) {
|
||||
return -1;
|
||||
}
|
||||
_edge = result;
|
||||
if (_edge == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
if (_edge->A() == vertex) {
|
||||
_orientation = true;
|
||||
}
|
||||
else {
|
||||
_orientation = false;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ChainingIterator::decrement()
|
||||
{
|
||||
_increment = false;
|
||||
ViewVertex *vertex = getVertex();
|
||||
if (!vertex) {
|
||||
_edge = nullptr;
|
||||
return 0;
|
||||
}
|
||||
AdjacencyIterator it = AdjacencyIterator(vertex, _restrictToSelection, _restrictToUnvisited);
|
||||
if (it.isEnd()) {
|
||||
_edge = nullptr;
|
||||
return 0;
|
||||
}
|
||||
if (traverse(it) < 0) {
|
||||
return -1;
|
||||
}
|
||||
_edge = result;
|
||||
if (_edge == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
if (_edge->B() == vertex) {
|
||||
_orientation = true;
|
||||
}
|
||||
else {
|
||||
_orientation = false;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// ChainSilhouetteIterators
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
int ChainSilhouetteIterator::traverse(const AdjacencyIterator &ait)
|
||||
{
|
||||
AdjacencyIterator it(ait);
|
||||
ViewVertex *nextVertex = getVertex();
|
||||
// we can't get a nullptr nextVertex here, it was intercepted before
|
||||
if (nextVertex->getNature() & Nature::T_VERTEX) {
|
||||
TVertex *tvertex = (TVertex *)nextVertex;
|
||||
ViewEdge *mate = (tvertex)->mate(getCurrentEdge());
|
||||
while (!it.isEnd()) {
|
||||
ViewEdge *ve = *it;
|
||||
if (ve == mate) {
|
||||
result = ve;
|
||||
return 0;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
result = nullptr;
|
||||
return 0;
|
||||
}
|
||||
if (nextVertex->getNature() & Nature::NON_T_VERTEX) {
|
||||
// soc NonTVertex *nontvertex = (NonTVertex*)nextVertex;
|
||||
ViewEdge *newEdge(nullptr);
|
||||
// we'll try to chain the edges by keeping the same nature...
|
||||
// the preseance order is : SILHOUETTE, BORDER, CREASE, MATERIAL_BOUNDARY, EDGE_MARK,
|
||||
// SUGGESTIVE, VALLEY, RIDGE
|
||||
Nature::EdgeNature natures[8] = {
|
||||
Nature::SILHOUETTE,
|
||||
Nature::BORDER,
|
||||
Nature::CREASE,
|
||||
Nature::MATERIAL_BOUNDARY,
|
||||
Nature::EDGE_MARK,
|
||||
Nature::SUGGESTIVE_CONTOUR,
|
||||
Nature::VALLEY,
|
||||
Nature::RIDGE,
|
||||
};
|
||||
int numNatures = blender::ARRAY_SIZE(natures);
|
||||
for (int i = 0; i < numNatures; ++i) {
|
||||
if (getCurrentEdge()->getNature() & natures[i]) {
|
||||
int n = 0;
|
||||
while (!it.isEnd()) {
|
||||
ViewEdge *ve = *it;
|
||||
if (ve->getNature() & natures[i]) {
|
||||
++n;
|
||||
newEdge = ve;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if (n == 1) {
|
||||
result = newEdge;
|
||||
}
|
||||
else {
|
||||
result = nullptr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
result = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ChainPredicateIterator::traverse(const AdjacencyIterator &ait)
|
||||
{
|
||||
if (!_unary_predicate || !_binary_predicate) {
|
||||
return -1;
|
||||
}
|
||||
AdjacencyIterator it(ait);
|
||||
// Iterates over next edges to see if one of them respects the predicate:
|
||||
while (!it.isEnd()) {
|
||||
ViewEdge *ve = *it;
|
||||
if (_unary_predicate->operator()(*ve) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (_unary_predicate->result) {
|
||||
if (_binary_predicate->operator()(*(getCurrentEdge()), *(ve)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (_binary_predicate->result) {
|
||||
result = ve;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
result = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,400 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Chaining iterators
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "Predicates1D.h"
|
||||
|
||||
#include "../system/Iterator.h"
|
||||
|
||||
#include "../view_map/ViewMap.h"
|
||||
#include "../view_map/ViewMapAdvancedIterators.h"
|
||||
#include "../view_map/ViewMapIterators.h"
|
||||
|
||||
// using namespace ViewEdgeInternal;
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
//
|
||||
// Adjacency iterator used in the chaining process
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
class AdjacencyIterator : public Iterator {
|
||||
protected:
|
||||
ViewVertexInternal::orientedViewEdgeIterator _internalIterator;
|
||||
bool _restrictToSelection;
|
||||
bool _restrictToUnvisited;
|
||||
|
||||
public:
|
||||
AdjacencyIterator()
|
||||
{
|
||||
_restrictToSelection = true;
|
||||
_restrictToUnvisited = true;
|
||||
}
|
||||
|
||||
AdjacencyIterator(ViewVertex *iVertex,
|
||||
bool iRestrictToSelection = true,
|
||||
bool iRestrictToUnvisited = true)
|
||||
{
|
||||
_restrictToSelection = iRestrictToSelection;
|
||||
_restrictToUnvisited = iRestrictToUnvisited;
|
||||
_internalIterator = iVertex->edgesBegin();
|
||||
while ((!_internalIterator.isEnd()) && (!isValid((*_internalIterator).first))) {
|
||||
++_internalIterator;
|
||||
}
|
||||
}
|
||||
|
||||
AdjacencyIterator(const AdjacencyIterator &iBrother)
|
||||
{
|
||||
_internalIterator = iBrother._internalIterator;
|
||||
_restrictToSelection = iBrother._restrictToSelection;
|
||||
_restrictToUnvisited = iBrother._restrictToUnvisited;
|
||||
}
|
||||
|
||||
AdjacencyIterator &operator=(const AdjacencyIterator &iBrother)
|
||||
{
|
||||
_internalIterator = iBrother._internalIterator;
|
||||
_restrictToSelection = iBrother._restrictToSelection;
|
||||
_restrictToUnvisited = iBrother._restrictToUnvisited;
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual ~AdjacencyIterator() {}
|
||||
|
||||
virtual string getExactTypeName() const
|
||||
{
|
||||
return "AdjacencyIterator";
|
||||
}
|
||||
|
||||
virtual inline bool isEnd() const
|
||||
{
|
||||
return _internalIterator.isEnd();
|
||||
}
|
||||
|
||||
virtual inline bool isBegin() const
|
||||
{
|
||||
return _internalIterator.isBegin();
|
||||
}
|
||||
|
||||
/** Returns true if the current ViewEdge is coming towards the iteration vertex.
|
||||
* False otherwise. */
|
||||
bool isIncoming() const;
|
||||
|
||||
/** Returns a *pointer* to the pointed ViewEdge. */
|
||||
virtual ViewEdge *operator*();
|
||||
|
||||
virtual ViewEdge *operator->()
|
||||
{
|
||||
return operator*();
|
||||
}
|
||||
|
||||
virtual AdjacencyIterator &operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual AdjacencyIterator operator++(int)
|
||||
{
|
||||
AdjacencyIterator tmp(*this);
|
||||
increment();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
virtual int increment();
|
||||
|
||||
virtual int decrement()
|
||||
{
|
||||
cerr << "Warning: method decrement() not implemented" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool isValid(ViewEdge *edge);
|
||||
};
|
||||
|
||||
//
|
||||
// Base class for Chaining Iterators
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
/** Base class for chaining iterators.
|
||||
* This class is designed to be overloaded in order to describe chaining rules.
|
||||
* It makes the works of chaining rules description easier.
|
||||
* The two main methods that need to overloaded are traverse() and init().
|
||||
* traverse() tells which ViewEdge to follow, among the adjacent ones.
|
||||
* If you specify restriction rules (such as "Chain only ViewEdges of the selection"),
|
||||
* they will be included in the adjacency iterator.
|
||||
* (i.e, the adjacent iterator will only stop on "valid" edges).
|
||||
*/
|
||||
class ChainingIterator : public ViewEdgeInternal::ViewEdgeIterator {
|
||||
protected:
|
||||
bool _restrictToSelection;
|
||||
bool _restrictToUnvisited;
|
||||
bool _increment; // true if we're currently incrementing, false when decrementing
|
||||
|
||||
public:
|
||||
ViewEdge *result;
|
||||
void *py_c_it;
|
||||
|
||||
/** Builds a Chaining Iterator from the first ViewEdge used for iteration and its orientation.
|
||||
* \param iRestrictToSelection:
|
||||
* Indicates whether to force the chaining to stay within
|
||||
* the set of selected ViewEdges or not.
|
||||
* \param iRestrictToUnvisited:
|
||||
* Indicates whether a ViewEdge that has already been chained must be ignored or 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 = nullptr,
|
||||
bool orientation = true)
|
||||
: ViewEdgeIterator(begin, orientation)
|
||||
{
|
||||
_restrictToSelection = iRestrictToSelection;
|
||||
_restrictToUnvisited = iRestrictToUnvisited;
|
||||
_increment = true;
|
||||
py_c_it = nullptr;
|
||||
}
|
||||
|
||||
/** Copy constructor */
|
||||
ChainingIterator(const ChainingIterator &brother) : ViewEdgeIterator(brother)
|
||||
{
|
||||
_restrictToSelection = brother._restrictToSelection;
|
||||
_restrictToUnvisited = brother._restrictToUnvisited;
|
||||
_increment = brother._increment;
|
||||
py_c_it = brother.py_c_it;
|
||||
}
|
||||
|
||||
/** Returns the string "ChainingIterator" */
|
||||
virtual string getExactTypeName() const
|
||||
{
|
||||
return "ChainingIterator";
|
||||
}
|
||||
|
||||
/** Initializes the iterator context.
|
||||
* This method is called each time a new chain is started.
|
||||
* It can be used to reset some history information that you might want to keep.
|
||||
*/
|
||||
virtual int init();
|
||||
|
||||
/** This method iterates over the potential next ViewEdges and returns the one that will be
|
||||
* followed next. returns the next ViewEdge to follow or 0 when the end of the chain is reached.
|
||||
* \param it:
|
||||
* The iterator over the ViewEdges adjacent to the end vertex of the current ViewEdge.
|
||||
* The Adjacency iterator reflects the restriction rules by only iterating over the valid
|
||||
* ViewEdges.
|
||||
*/
|
||||
virtual int traverse(const AdjacencyIterator &it);
|
||||
|
||||
/* accessors */
|
||||
/** Returns true if the orientation of the current ViewEdge corresponds to its natural
|
||||
* orientation */
|
||||
// inline bool getOrientation() const {}
|
||||
|
||||
/** Returns the vertex which is the next crossing */
|
||||
inline ViewVertex *getVertex()
|
||||
{
|
||||
if (_increment) {
|
||||
if (_orientation) {
|
||||
return _edge->B();
|
||||
}
|
||||
else {
|
||||
return _edge->A();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (_orientation) {
|
||||
return _edge->A();
|
||||
}
|
||||
else {
|
||||
return _edge->B();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if the current iteration is an incrementation */
|
||||
inline bool isIncrementing() const
|
||||
{
|
||||
return _increment;
|
||||
}
|
||||
|
||||
/* Increments. */
|
||||
virtual int increment();
|
||||
virtual int decrement();
|
||||
};
|
||||
|
||||
//
|
||||
// Chaining iterators definitions
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
/** A ViewEdge Iterator used to follow ViewEdges the most naturally.
|
||||
* For example, it will follow visible ViewEdges of same nature.
|
||||
* As soon, as the nature or the visibility changes, the iteration stops (by setting the pointed
|
||||
* ViewEdge to 0). In the case of an iteration over a set of ViewEdge that are both Silhouette
|
||||
* and Crease, there will be a precedence of the silhouette over the crease criterion.
|
||||
*/
|
||||
class 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 = nullptr,
|
||||
bool orientation = true)
|
||||
: ChainingIterator(iRestrictToSelection, true, begin, orientation)
|
||||
{
|
||||
}
|
||||
|
||||
/** Copy constructor */
|
||||
ChainSilhouetteIterator(const ChainSilhouetteIterator &brother) : ChainingIterator(brother) {}
|
||||
|
||||
/** Returns the string "ChainSilhouetteIterator" */
|
||||
virtual string getExactTypeName() const
|
||||
{
|
||||
return "ChainSilhouetteIterator";
|
||||
}
|
||||
|
||||
/** This method iterates over the potential next ViewEdges and returns the one that will be
|
||||
* followed next.
|
||||
* When reaching the end of a chain, 0 is returned.
|
||||
*/
|
||||
virtual int traverse(const AdjacencyIterator &it);
|
||||
|
||||
/** Initializes the iterator context */
|
||||
virtual int init()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// ChainPredicateIterator
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
/** A "generic" user-controlled ViewEdge iterator. This iterator is in particular built from a
|
||||
* unary predicate and a binary predicate.
|
||||
* First, the unary predicate is evaluated for all potential next ViewEdges in order to only
|
||||
* keep the ones respecting a certain constraint.
|
||||
* Then, the binary predicate is evaluated on the current ViewEdge together with each ViewEdge
|
||||
* of the previous selection. The first ViewEdge respecting both the unary predicate and the
|
||||
* binary predicate is kept as the next one. If none of the potential next ViewEdge respects
|
||||
* these 2 predicates, 0 is returned.
|
||||
*/
|
||||
class 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 = nullptr,
|
||||
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 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 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(UnaryPredicate1D &upred,
|
||||
BinaryPredicate1D &bpred,
|
||||
bool iRestrictToSelection = true,
|
||||
bool iRestrictToUnvisited = true,
|
||||
ViewEdge *begin = nullptr,
|
||||
bool orientation = true)
|
||||
: ChainingIterator(iRestrictToSelection, iRestrictToUnvisited, begin, orientation)
|
||||
{
|
||||
_unary_predicate = &upred;
|
||||
_binary_predicate = &bpred;
|
||||
}
|
||||
|
||||
/** Copy constructor */
|
||||
ChainPredicateIterator(const ChainPredicateIterator &brother) : ChainingIterator(brother)
|
||||
{
|
||||
_unary_predicate = brother._unary_predicate;
|
||||
_binary_predicate = brother._binary_predicate;
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~ChainPredicateIterator()
|
||||
{
|
||||
_unary_predicate = 0;
|
||||
_binary_predicate = 0;
|
||||
}
|
||||
|
||||
/** Returns the string "ChainPredicateIterator" */
|
||||
virtual string getExactTypeName() const
|
||||
{
|
||||
return "ChainPredicateIterator";
|
||||
}
|
||||
|
||||
/** This method iterates over the potential next ViewEdges and returns the one that will be
|
||||
* followed next. When reaching the end of a chain, 0 is returned.
|
||||
*/
|
||||
virtual int traverse(const AdjacencyIterator &it);
|
||||
|
||||
/** Initializes the iterator context. */
|
||||
virtual int init()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,69 @@
|
||||
/* SPDX-FileCopyrightText: 2012-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Functions related to context queries
|
||||
* \brief Interface to access the context related information.
|
||||
*/
|
||||
|
||||
#include "ContextFunctions.h"
|
||||
|
||||
#include "../view_map/SteerableViewMap.h"
|
||||
|
||||
#include "../system/TimeStamp.h"
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace Freestyle::ContextFunctions {
|
||||
|
||||
uint GetTimeStampCF()
|
||||
{
|
||||
return TimeStamp::instance()->getTimeStamp();
|
||||
}
|
||||
|
||||
uint GetCanvasWidthCF()
|
||||
{
|
||||
return Canvas::getInstance()->width();
|
||||
}
|
||||
|
||||
uint GetCanvasHeightCF()
|
||||
{
|
||||
return Canvas::getInstance()->height();
|
||||
}
|
||||
|
||||
BBox<Vec2i> GetBorderCF()
|
||||
{
|
||||
return Canvas::getInstance()->border();
|
||||
}
|
||||
|
||||
void LoadMapCF(const char *iFileName, const char *iMapName, uint iNbLevels, float iSigma)
|
||||
{
|
||||
return Canvas::getInstance()->loadMap(iFileName, iMapName, iNbLevels, iSigma);
|
||||
}
|
||||
|
||||
float ReadMapPixelCF(const char *iMapName, int level, uint x, uint y)
|
||||
{
|
||||
Canvas *canvas = Canvas::getInstance();
|
||||
return canvas->readMapPixel(iMapName, level, x, y);
|
||||
}
|
||||
|
||||
float ReadCompleteViewMapPixelCF(int level, uint x, uint y)
|
||||
{
|
||||
SteerableViewMap *svm = Canvas::getInstance()->getSteerableViewMap();
|
||||
return svm->readCompleteViewMapPixel(level, x, y);
|
||||
}
|
||||
|
||||
float ReadDirectionalViewMapPixelCF(int iOrientation, int level, uint x, uint y)
|
||||
{
|
||||
SteerableViewMap *svm = Canvas::getInstance()->getSteerableViewMap();
|
||||
return svm->readSteerableViewMapPixel(iOrientation, level, x, y);
|
||||
}
|
||||
|
||||
FEdge *GetSelectedFEdgeCF()
|
||||
{
|
||||
return Canvas::getInstance()->selectedFEdge();
|
||||
}
|
||||
|
||||
} // namespace Freestyle::ContextFunctions
|
||||
@@ -0,0 +1,95 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Functions related to context queries
|
||||
* \brief Interface to access the context related information.
|
||||
*/
|
||||
|
||||
#include "Canvas.h"
|
||||
|
||||
#include "../image/GaussianFilter.h"
|
||||
#include "../image/Image.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
//
|
||||
// Context Functions definitions
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
/** namespace containing all the Context related functions */
|
||||
namespace ContextFunctions {
|
||||
|
||||
// GetTimeStamp
|
||||
/** Returns the system time stamp */
|
||||
uint GetTimeStampCF();
|
||||
|
||||
// GetCanvasWidth
|
||||
/** Returns the canvas width */
|
||||
uint GetCanvasWidthCF();
|
||||
|
||||
// GetCanvasHeight
|
||||
/** Returns the canvas height */
|
||||
uint GetCanvasHeightCF();
|
||||
|
||||
// GetBorder
|
||||
/** Returns the border */
|
||||
BBox<Vec2i> GetBorderCF();
|
||||
|
||||
// Load map
|
||||
/** Loads an image map for further reading */
|
||||
void LoadMapCF(const char *iFileName,
|
||||
const char *iMapName,
|
||||
uint iNbLevels = 4,
|
||||
float iSigma = 1.0f);
|
||||
|
||||
// ReadMapPixel
|
||||
/** Reads a pixel in a user-defined map
|
||||
* \return the floating value stored for that pixel
|
||||
* \param iMapName:
|
||||
* The name of the map
|
||||
* \param level:
|
||||
* The level of the pyramid in which we wish to read the pixel
|
||||
* \param x:
|
||||
* The x-coordinate of the pixel we wish to read. The origin is in the lower-left corner.
|
||||
* \param y:
|
||||
* The y-coordinate of the pixel we wish to read. The origin is in the lower-left corner.
|
||||
*/
|
||||
float ReadMapPixelCF(const char *iMapName, int level, uint x, uint 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.
|
||||
*/
|
||||
float ReadCompleteViewMapPixelCF(int level, uint x, uint 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.
|
||||
*/
|
||||
float ReadDirectionalViewMapPixelCF(int iOrientation, int level, uint x, uint y);
|
||||
|
||||
// DEBUG
|
||||
FEdge *GetSelectedFEdgeCF();
|
||||
|
||||
} // end of namespace ContextFunctions
|
||||
|
||||
} /* namespace Freestyle */
|
||||
1032
blender-5.2.0/source/blender/freestyle/intern/stroke/Curve.cpp
Normal file
1032
blender-5.2.0/source/blender/freestyle/intern/stroke/Curve.cpp
Normal file
File diff suppressed because it is too large
Load Diff
585
blender-5.2.0/source/blender/freestyle/intern/stroke/Curve.h
Normal file
585
blender-5.2.0/source/blender/freestyle/intern/stroke/Curve.h
Normal file
@@ -0,0 +1,585 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class to define a container for curves
|
||||
*/
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "../geometry/Geom.h"
|
||||
|
||||
// #include "../scene_graph/FrsMaterial.h"
|
||||
|
||||
#include "../view_map/Interface0D.h"
|
||||
#include "../view_map/Interface1D.h"
|
||||
#include "../view_map/Silhouette.h"
|
||||
#include "../view_map/SilhouetteGeomEngine.h"
|
||||
|
||||
#include "../system/BaseIterator.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
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 linearly
|
||||
* interpolating two SVertex. CurvePoint can be used as virtual points while querying 0D
|
||||
* information along a curve at a given resolution.
|
||||
*/
|
||||
class 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 Vec3r 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 Vec2r getPoint2D() const
|
||||
{
|
||||
return Vec2r(_Point2d.x(), _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:
|
||||
/** Default Constructor. */
|
||||
CurvePoint();
|
||||
|
||||
/** Builds a CurvePoint from two SVertex and an interpolation parameter.
|
||||
* \param iA:
|
||||
* The first SVertex
|
||||
* \param iB:
|
||||
* The second SVertex
|
||||
* \param t:
|
||||
* A 2D interpolation parameter used to linearly interpolate \a iA and \a iB
|
||||
*/
|
||||
CurvePoint(SVertex *iA, SVertex *iB, float t);
|
||||
|
||||
/** Builds a CurvePoint from two CurvePoint and an interpolation parameter.
|
||||
* \param iA:
|
||||
* The first CurvePoint
|
||||
* \param iB:
|
||||
* The second CurvePoint
|
||||
* \param t:
|
||||
* The 2D interpolation parameter used to linearly interpolate \a iA and \a iB.
|
||||
*/
|
||||
CurvePoint(CurvePoint *iA, CurvePoint *iB, float t);
|
||||
|
||||
// CurvePoint(SVertex *iA, SVertex *iB, float t2d, float t3d);
|
||||
|
||||
/** Copy Constructor. */
|
||||
CurvePoint(const CurvePoint &iBrother);
|
||||
|
||||
/** Operator = */
|
||||
CurvePoint &operator=(const CurvePoint &iBrother);
|
||||
|
||||
/** Destructor */
|
||||
virtual ~CurvePoint() = default;
|
||||
|
||||
/** Operator == */
|
||||
bool operator==(const CurvePoint &b)
|
||||
{
|
||||
return ((__A == b.__A) && (__B == b.__B) && (_t2d == b._t2d));
|
||||
}
|
||||
|
||||
/* accessors */
|
||||
/** Returns the first SVertex upon which the CurvePoint is built. */
|
||||
inline SVertex *A()
|
||||
{
|
||||
return __A;
|
||||
}
|
||||
|
||||
/** Returns the second SVertex upon which the CurvePoint is built. */
|
||||
inline SVertex *B()
|
||||
{
|
||||
return __B;
|
||||
}
|
||||
|
||||
/** Returns the interpolation parameter. */
|
||||
inline float t2d() const
|
||||
{
|
||||
return _t2d;
|
||||
}
|
||||
|
||||
#if 0
|
||||
inline const float t3d() const
|
||||
{
|
||||
return _t3d;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* modifiers */
|
||||
/** Sets the first SVertex upon which to build the CurvePoint. */
|
||||
inline void setA(SVertex *iA)
|
||||
{
|
||||
__A = iA;
|
||||
}
|
||||
|
||||
/** Sets the second SVertex upon which to build the CurvePoint. */
|
||||
inline void setB(SVertex *iB)
|
||||
{
|
||||
__B = iB;
|
||||
}
|
||||
|
||||
/** Sets the 2D interpolation parameter to use. */
|
||||
inline void setT2d(float t)
|
||||
{
|
||||
_t2d = t;
|
||||
}
|
||||
|
||||
#if 0
|
||||
inline void SetT3d(float t)
|
||||
{
|
||||
_t3d = t;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Information access interface */
|
||||
|
||||
FEdge *fedge();
|
||||
|
||||
inline const Vec3r &point2d() const
|
||||
{
|
||||
return _Point2d;
|
||||
}
|
||||
|
||||
inline const Vec3r &point3d() const
|
||||
{
|
||||
return _Point3d;
|
||||
}
|
||||
|
||||
Vec3r normal() const;
|
||||
// FrsMaterial material() const;
|
||||
// Id shape_id() const;
|
||||
const SShape *shape() const;
|
||||
// float shape_importance() const;
|
||||
|
||||
// const uint 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;
|
||||
bool occludee_empty() const;
|
||||
real z_discontinuity() const;
|
||||
#if 0
|
||||
float local_average_depth() const;
|
||||
float local_depth_variance() const;
|
||||
real local_average_density(float sigma = 2.3f) const;
|
||||
Vec3r shaded_color() const;
|
||||
Vec3r orientation2d() const;
|
||||
Vec3r orientation3d() const;
|
||||
|
||||
real curvature2d() const
|
||||
{
|
||||
return viewedge()->curvature2d((_VertexA->point2d() + _VertexB->point2d()) / 2.0);
|
||||
}
|
||||
|
||||
Vec3r curvature2d_as_vector() const;
|
||||
/** angle in radians */
|
||||
real curvature2d_as_angle() const;
|
||||
|
||||
real curvatureFredo() const;
|
||||
Vec2d directionFredo() const;
|
||||
#endif
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:CurvePoint")
|
||||
};
|
||||
|
||||
/**********************************/
|
||||
/* */
|
||||
/* */
|
||||
/* 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 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;
|
||||
uint _nSegments; // number of segments
|
||||
|
||||
public:
|
||||
/** Default Constructor. */
|
||||
Curve()
|
||||
{
|
||||
_Length = 0;
|
||||
_Id = 0;
|
||||
_nSegments = 0;
|
||||
}
|
||||
|
||||
/** Builds a Curve from its id */
|
||||
Curve(const Id &id)
|
||||
{
|
||||
_Length = 0;
|
||||
_Id = id;
|
||||
_nSegments = 0;
|
||||
}
|
||||
|
||||
/** Copy Constructor. */
|
||||
Curve(const Curve &iBrother)
|
||||
{
|
||||
_Length = iBrother._Length;
|
||||
_Vertices = iBrother._Vertices;
|
||||
_Id = iBrother._Id;
|
||||
_nSegments = 0;
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~Curve();
|
||||
|
||||
/** Returns the string "Curve" */
|
||||
virtual string getExactTypeName() const
|
||||
{
|
||||
return "Curve";
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* fredo's curvature storage */
|
||||
void computeCurvatureAndOrientation();
|
||||
#endif
|
||||
|
||||
/** Adds a single vertex (CurvePoint) at the end of the Curve */
|
||||
inline void push_vertex_back(Vertex *iVertex)
|
||||
{
|
||||
if (!_Vertices.empty()) {
|
||||
Vec3r vec_tmp(iVertex->point2d() - _Vertices.back()->point2d());
|
||||
_Length += vec_tmp.norm();
|
||||
++_nSegments;
|
||||
}
|
||||
Vertex *new_vertex = new Vertex(*iVertex);
|
||||
_Vertices.push_back(new_vertex);
|
||||
}
|
||||
|
||||
/** Adds a single vertex (SVertex) at the end of the Curve */
|
||||
inline void push_vertex_back(SVertex *iVertex)
|
||||
{
|
||||
if (!_Vertices.empty()) {
|
||||
Vec3r vec_tmp(iVertex->point2d() - _Vertices.back()->point2d());
|
||||
_Length += vec_tmp.norm();
|
||||
++_nSegments;
|
||||
}
|
||||
Vertex *new_vertex = new Vertex(iVertex, 0, 0);
|
||||
_Vertices.push_back(new_vertex);
|
||||
}
|
||||
|
||||
/** Adds a single vertex (CurvePoint) at the front of the Curve */
|
||||
inline void push_vertex_front(Vertex *iVertex)
|
||||
{
|
||||
if (!_Vertices.empty()) {
|
||||
Vec3r vec_tmp(iVertex->point2d() - _Vertices.front()->point2d());
|
||||
_Length += vec_tmp.norm();
|
||||
++_nSegments;
|
||||
}
|
||||
Vertex *new_vertex = new Vertex(*iVertex);
|
||||
_Vertices.push_front(new_vertex);
|
||||
}
|
||||
|
||||
/** Adds a single vertex (SVertex) at the front of the Curve */
|
||||
inline void push_vertex_front(SVertex *iVertex)
|
||||
{
|
||||
if (!_Vertices.empty()) {
|
||||
Vec3r vec_tmp(iVertex->point2d() - _Vertices.front()->point2d());
|
||||
_Length += vec_tmp.norm();
|
||||
++_nSegments;
|
||||
}
|
||||
Vertex *new_vertex = new Vertex(iVertex, 0, 0);
|
||||
_Vertices.push_front(new_vertex);
|
||||
}
|
||||
|
||||
/** Returns true is the Curve doesn't have any Vertex yet. */
|
||||
inline bool empty() const
|
||||
{
|
||||
return _Vertices.empty();
|
||||
}
|
||||
|
||||
/** Returns the 2D length of the Curve. */
|
||||
inline real getLength2D() const
|
||||
{
|
||||
return _Length;
|
||||
}
|
||||
|
||||
/** Returns the Id of the 1D element. */
|
||||
virtual Id getId() const
|
||||
{
|
||||
return _Id;
|
||||
}
|
||||
|
||||
/** Returns the number of segments in the polyline constituting the Curve. */
|
||||
inline uint nSegments() const
|
||||
{
|
||||
return _nSegments;
|
||||
}
|
||||
|
||||
inline void setId(const Id &id)
|
||||
{
|
||||
_Id = id;
|
||||
}
|
||||
|
||||
/* Information access interface */
|
||||
|
||||
#if 0
|
||||
inline Vec3r shaded_color(int iCombination = 0) const;
|
||||
inline Vec3r orientation2d(point_iterator it) const;
|
||||
Vec3r orientation2d(int iCombination = 0) const;
|
||||
Vec3r orientation3d(point_iterator it) const;
|
||||
Vec3r orientation3d(int iCombination = 0) const;
|
||||
|
||||
real curvature2d(point_iterator it) const
|
||||
{
|
||||
return (*it)->curvature2d();
|
||||
}
|
||||
|
||||
real curvature2d(int iCombination = 0) const;
|
||||
FrsMaterial material() const;
|
||||
int qi() const;
|
||||
occluder_container::const_iterator occluders_begin() const;
|
||||
occluder_container::const_iterator occluders_end() const;
|
||||
int occluders_size() const;
|
||||
bool occluders_empty() const;
|
||||
|
||||
const Polygon3r &occludee() const
|
||||
{
|
||||
return *(_FEdgeA->aFace());
|
||||
}
|
||||
|
||||
const SShape *occluded_shape() const;
|
||||
bool occludee_empty() const;
|
||||
real z_discontinuity(int iCombination = 0) const;
|
||||
int shape_id() const;
|
||||
const SShape *shape() const;
|
||||
float shape_importance(int iCombination = 0) const;
|
||||
float local_average_depth(int iCombination = 0) const;
|
||||
float local_depth_variance(int iCombination = 0) const;
|
||||
real local_average_density(float sigma = 2.3f, int iCombination = 0) const;
|
||||
Vec3r curvature2d_as_vector(int iCombination = 0) const;
|
||||
/** angle in radians */
|
||||
real curvature2d_as_angle(int iCombination = 0) const;
|
||||
#endif
|
||||
|
||||
/* advanced iterators access */
|
||||
point_iterator points_begin(float step = 0);
|
||||
const_point_iterator points_begin(float step = 0) const;
|
||||
point_iterator points_end(float step = 0);
|
||||
const_point_iterator points_end(float step = 0) const;
|
||||
|
||||
/* methods given for convenience */
|
||||
point_iterator vertices_begin();
|
||||
const_point_iterator vertices_begin() const;
|
||||
point_iterator vertices_end();
|
||||
const_point_iterator vertices_end() const;
|
||||
|
||||
// specialized iterators access
|
||||
CurveInternal::CurvePointIterator curvePointsBegin(float t = 0.0f);
|
||||
CurveInternal::CurvePointIterator curvePointsEnd(float t = 0.0f);
|
||||
|
||||
CurveInternal::CurvePointIterator curveVerticesBegin();
|
||||
CurveInternal::CurvePointIterator curveVerticesEnd();
|
||||
|
||||
// Iterators access
|
||||
/** Returns an Interface0DIterator pointing onto the first vertex of the Curve and that can
|
||||
* iterate over the \a vertices of the Curve.
|
||||
*/
|
||||
virtual Interface0DIterator verticesBegin();
|
||||
|
||||
/** Returns an Interface0DIterator pointing after the last vertex of the Curve and that can
|
||||
* iterate over the \a vertices of the Curve.
|
||||
*/
|
||||
virtual Interface0DIterator verticesEnd();
|
||||
|
||||
/** Returns an Interface0DIterator pointing onto the first point of the Curve and that can
|
||||
* iterate over the \a points of the Curve at any resolution. At each iteration a virtual
|
||||
* temporary CurvePoint is created.
|
||||
*/
|
||||
virtual Interface0DIterator pointsBegin(float t = 0.0f);
|
||||
|
||||
/** Returns an Interface0DIterator pointing after the last point of the Curve and that can
|
||||
* iterate over the \a points of the Curve at any resolution. At each iteration a virtual
|
||||
* temporary CurvePoint is created.
|
||||
*/
|
||||
virtual Interface0DIterator pointsEnd(float t = 0.0f);
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Curve")
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,377 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Iterators used to iterate over the elements of the Curve. Can't be used in python
|
||||
*/
|
||||
|
||||
#include "CurveIterators.h"
|
||||
#include "Stroke.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
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
|
||||
* instantiated and returned when the iterator is dereferenced.
|
||||
*/
|
||||
template<class Traits>
|
||||
class __point_iterator : public IteratorBase<Traits, BidirectionalIteratorTag_Traits> {
|
||||
public:
|
||||
typedef __point_iterator<Traits> Self;
|
||||
typedef typename Traits::vertex_container_iterator vertex_container_iterator;
|
||||
typedef typename Traits::vertex_type vertex_type;
|
||||
typedef CurvePoint Point;
|
||||
typedef Point point_type;
|
||||
|
||||
typedef __point_iterator<CurvePoint_nonconst_traits> iterator;
|
||||
typedef __point_iterator<CurvePoint_const_traits> const_iterator;
|
||||
|
||||
#if 0
|
||||
typedef Vertex vertex_type;
|
||||
typedef vertex_container_iterator vertex_iterator_type;
|
||||
typedef CurvePoint<Vertex> Point;
|
||||
typedef Point point_type;
|
||||
#endif
|
||||
typedef IteratorBase<Traits, BidirectionalIteratorTag_Traits> parent_class;
|
||||
#if 0
|
||||
# if defined(__GNUC__) && (__GNUC__ < 3)
|
||||
typedef bidirectional_iterator<CurvePoint<Vertex>, ptrdiff_t> bidirectional_point_iterator;
|
||||
# else
|
||||
typedef iterator<bidirectional_iterator_tag, CurvePoint<Vertex>, ptrdiff_t>
|
||||
bidirectional_point_iterator;
|
||||
# endif
|
||||
#endif
|
||||
friend class Freestyle::Curve;
|
||||
#if 0
|
||||
friend class Curve::vertex_iterator;
|
||||
friend class __point_iterator<CurvePoint_nonconst_traits>;
|
||||
friend class iterator;
|
||||
#endif
|
||||
// protected:
|
||||
public:
|
||||
float _CurvilinearLength;
|
||||
float _step;
|
||||
vertex_container_iterator __A;
|
||||
vertex_container_iterator __B;
|
||||
vertex_container_iterator _begin;
|
||||
vertex_container_iterator _end;
|
||||
int _n;
|
||||
int _currentn;
|
||||
float _t;
|
||||
mutable Point *_Point;
|
||||
|
||||
public:
|
||||
inline __point_iterator(float step = 0.0f) : parent_class()
|
||||
{
|
||||
_step = step;
|
||||
_CurvilinearLength = 0.0f;
|
||||
_t = 0.0f;
|
||||
_Point = 0;
|
||||
_n = 0;
|
||||
_currentn = 0;
|
||||
}
|
||||
|
||||
inline __point_iterator(const iterator &iBrother) : parent_class()
|
||||
{
|
||||
__A = iBrother.__A;
|
||||
__B = iBrother.__B;
|
||||
_begin = iBrother._begin;
|
||||
_end = iBrother._end;
|
||||
_CurvilinearLength = iBrother._CurvilinearLength;
|
||||
_step = iBrother._step;
|
||||
_t = iBrother._t;
|
||||
if (iBrother._Point == 0) {
|
||||
_Point = 0;
|
||||
}
|
||||
else {
|
||||
_Point = new Point(*(iBrother._Point));
|
||||
}
|
||||
_n = iBrother._n;
|
||||
_currentn = iBrother._currentn;
|
||||
}
|
||||
|
||||
inline __point_iterator(const const_iterator &iBrother) : parent_class()
|
||||
{
|
||||
__A = iBrother.__A;
|
||||
__B = iBrother.__B;
|
||||
_begin = iBrother._begin;
|
||||
_end = iBrother._end;
|
||||
_CurvilinearLength = iBrother._CurvilinearLength;
|
||||
_step = iBrother._step;
|
||||
_t = iBrother._t;
|
||||
if (iBrother._Point == 0) {
|
||||
_Point = 0;
|
||||
}
|
||||
else {
|
||||
_Point = new Point(*(iBrother._Point));
|
||||
}
|
||||
_n = iBrother._n;
|
||||
_currentn = iBrother._currentn;
|
||||
}
|
||||
|
||||
inline Self &operator=(const Self &iBrother)
|
||||
{
|
||||
//((bidirectional_point_iterator*)this)->operator=(iBrother);
|
||||
__A = iBrother.__A;
|
||||
__B = iBrother.__B;
|
||||
_begin = iBrother._begin;
|
||||
_end = iBrother._end;
|
||||
_CurvilinearLength = iBrother._CurvilinearLength;
|
||||
_step = iBrother._step;
|
||||
_t = iBrother._t;
|
||||
if (iBrother._Point == 0) {
|
||||
_Point = 0;
|
||||
}
|
||||
else {
|
||||
_Point = new Point(*(iBrother._Point));
|
||||
}
|
||||
_n = iBrother._n;
|
||||
_currentn = iBrother._currentn;
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual ~__point_iterator()
|
||||
{
|
||||
if (_Point != 0) {
|
||||
delete _Point;
|
||||
}
|
||||
}
|
||||
|
||||
// protected: //FIXME
|
||||
public:
|
||||
inline __point_iterator(vertex_container_iterator iA,
|
||||
vertex_container_iterator iB,
|
||||
vertex_container_iterator ibegin,
|
||||
vertex_container_iterator iend,
|
||||
int currentn,
|
||||
int n,
|
||||
float step,
|
||||
float t = 0.0f,
|
||||
float iCurvilinearLength = 0.0f)
|
||||
: parent_class()
|
||||
{
|
||||
__A = iA;
|
||||
__B = iB;
|
||||
_begin = ibegin;
|
||||
_end = iend;
|
||||
_CurvilinearLength = iCurvilinearLength;
|
||||
_step = step;
|
||||
_t = t;
|
||||
_Point = 0;
|
||||
_n = n;
|
||||
_currentn = currentn;
|
||||
}
|
||||
|
||||
public:
|
||||
// operators
|
||||
inline Self &operator++() // operator corresponding to ++i
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* Operator corresponding to i++, i.e. it returns the value *and then* increments.
|
||||
* That’s why we store the value in a temp.
|
||||
*/
|
||||
inline Self operator++(int)
|
||||
{
|
||||
Self tmp = *this;
|
||||
increment();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
inline Self &operator--() // operator corresponding to --i
|
||||
{
|
||||
decrement();
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Self operator--(int) // operator corresponding to i--
|
||||
{
|
||||
Self tmp = *this;
|
||||
decrement();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
// comparability
|
||||
virtual bool operator!=(const Self &b) const
|
||||
{
|
||||
return ((__A != b.__A) || (__B != b.__B) || (_t != b._t));
|
||||
}
|
||||
|
||||
virtual bool operator==(const Self &b) const
|
||||
{
|
||||
return !(*this != b);
|
||||
}
|
||||
|
||||
// dereferencing
|
||||
virtual typename Traits::reference operator*() const
|
||||
{
|
||||
if (_Point != 0) {
|
||||
delete _Point;
|
||||
_Point = 0;
|
||||
}
|
||||
if ((_currentn < 0) || (_currentn >= _n)) {
|
||||
return _Point; // 0 in this case
|
||||
}
|
||||
return (_Point = new Point(*__A, *__B, _t));
|
||||
}
|
||||
|
||||
virtual typename Traits::pointer operator->() const
|
||||
{
|
||||
return &(operator*());
|
||||
}
|
||||
|
||||
virtual bool begin() const
|
||||
{
|
||||
if ((__A == _begin) && (_t < (float)M_EPSILON)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool end() const
|
||||
{
|
||||
if ((__B == _end)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void increment()
|
||||
{
|
||||
if (_Point != 0) {
|
||||
delete _Point;
|
||||
_Point = 0;
|
||||
}
|
||||
if ((_currentn == _n - 1) && (_t == 1.0f)) {
|
||||
// we're setting the iterator to end
|
||||
++__A;
|
||||
++__B;
|
||||
++_currentn;
|
||||
_t = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
if (0 == _step) { // means we iterate over initial vertices
|
||||
Vec3r vec_tmp((*__B)->point2d() - (*__A)->point2d());
|
||||
_CurvilinearLength += vec_tmp.norm();
|
||||
if (_currentn == _n - 1) {
|
||||
_t = 1.0f;
|
||||
return;
|
||||
}
|
||||
++__B;
|
||||
++__A;
|
||||
++_currentn;
|
||||
return;
|
||||
}
|
||||
|
||||
// compute the new position:
|
||||
Vec3r vec_tmp2((*__A)->point2d() - (*__B)->point2d());
|
||||
float normAB = vec_tmp2.norm();
|
||||
|
||||
if (normAB > M_EPSILON) {
|
||||
_CurvilinearLength += _step;
|
||||
_t = _t + _step / normAB;
|
||||
}
|
||||
else {
|
||||
_t = 1.0f; // AB is a null segment, we're directly at its end
|
||||
}
|
||||
// if normAB ~= 0, we don't change these values
|
||||
if (_t >= 1) {
|
||||
_CurvilinearLength -= normAB * (_t - 1);
|
||||
if (_currentn == _n - 1) {
|
||||
_t = 1.0f;
|
||||
}
|
||||
else {
|
||||
_t = 0.0f;
|
||||
++_currentn;
|
||||
++__A;
|
||||
++__B;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void decrement()
|
||||
{
|
||||
if (_Point != 0) {
|
||||
delete _Point;
|
||||
_Point = 0;
|
||||
}
|
||||
|
||||
if (_t == 0.0f) { // we're at the beginning of the edge
|
||||
_t = 1.0f;
|
||||
--_currentn;
|
||||
--__A;
|
||||
--__B;
|
||||
if (_currentn == _n - 1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == _step) { // means we iterate over initial vertices
|
||||
Vec3r vec_tmp((*__B)->point2d() - (*__A)->point2d());
|
||||
_CurvilinearLength -= vec_tmp.norm();
|
||||
_t = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// compute the new position:
|
||||
Vec3r vec_tmp2((*__A)->point2d() - (*__B)->point2d());
|
||||
float normAB = vec_tmp2.norm();
|
||||
|
||||
if (normAB > M_EPSILON) {
|
||||
_CurvilinearLength -= _step;
|
||||
_t = _t - _step / normAB;
|
||||
}
|
||||
else {
|
||||
_t = -1.0f; // We just need a negative value here
|
||||
}
|
||||
|
||||
// round value
|
||||
if (fabs(_t) < (float)M_EPSILON) {
|
||||
_t = 0.0f;
|
||||
}
|
||||
if (_t < 0) {
|
||||
if (_currentn == 0) {
|
||||
_CurvilinearLength = 0.0f;
|
||||
}
|
||||
else {
|
||||
_CurvilinearLength += normAB * (-_t);
|
||||
}
|
||||
_t = 0.0f;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // end of namespace CurveInternal
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,290 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Iterators used to iterate over the elements of the Curve
|
||||
*/
|
||||
|
||||
#include "Curve.h"
|
||||
#include "Stroke.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
namespace CurveInternal {
|
||||
|
||||
/**
|
||||
* Iterator on a curve. Allows an iterating outside
|
||||
* initial vertices. A CurvePoint is instantiated an returned
|
||||
* when the iterator is dereferenced.
|
||||
*/
|
||||
class CurvePointIterator : public Interface0DIteratorNested {
|
||||
public:
|
||||
friend class Freestyle::Curve;
|
||||
|
||||
public:
|
||||
float _CurvilinearLength;
|
||||
float _step;
|
||||
Curve::vertex_container::iterator __A;
|
||||
Curve::vertex_container::iterator __B;
|
||||
Curve::vertex_container::iterator _begin;
|
||||
Curve::vertex_container::iterator _end;
|
||||
int _n;
|
||||
int _currentn;
|
||||
float _t;
|
||||
mutable CurvePoint _Point;
|
||||
float _CurveLength;
|
||||
|
||||
public:
|
||||
inline CurvePointIterator(float step = 0.0f) : Interface0DIteratorNested()
|
||||
{
|
||||
_step = step;
|
||||
_CurvilinearLength = 0.0f;
|
||||
_t = 0.0f;
|
||||
//_Point = 0;
|
||||
_n = 0;
|
||||
_currentn = 0;
|
||||
_CurveLength = 0;
|
||||
}
|
||||
|
||||
inline CurvePointIterator(const CurvePointIterator &iBrother) : Interface0DIteratorNested()
|
||||
{
|
||||
__A = iBrother.__A;
|
||||
__B = iBrother.__B;
|
||||
_begin = iBrother._begin;
|
||||
_end = iBrother._end;
|
||||
_CurvilinearLength = iBrother._CurvilinearLength;
|
||||
_step = iBrother._step;
|
||||
_t = iBrother._t;
|
||||
_Point = iBrother._Point;
|
||||
_n = iBrother._n;
|
||||
_currentn = iBrother._currentn;
|
||||
_CurveLength = iBrother._CurveLength;
|
||||
}
|
||||
|
||||
inline CurvePointIterator &operator=(const CurvePointIterator &iBrother)
|
||||
{
|
||||
__A = iBrother.__A;
|
||||
__B = iBrother.__B;
|
||||
_begin = iBrother._begin;
|
||||
_end = iBrother._end;
|
||||
_CurvilinearLength = iBrother._CurvilinearLength;
|
||||
_step = iBrother._step;
|
||||
_t = iBrother._t;
|
||||
_Point = iBrother._Point;
|
||||
_n = iBrother._n;
|
||||
_currentn = iBrother._currentn;
|
||||
_CurveLength = iBrother._CurveLength;
|
||||
return *this;
|
||||
}
|
||||
|
||||
virtual ~CurvePointIterator() {}
|
||||
|
||||
protected:
|
||||
inline CurvePointIterator(Curve::vertex_container::iterator iA,
|
||||
Curve::vertex_container::iterator iB,
|
||||
Curve::vertex_container::iterator ibegin,
|
||||
Curve::vertex_container::iterator iend,
|
||||
int currentn,
|
||||
int n,
|
||||
float iCurveLength,
|
||||
float step,
|
||||
float t = 0.0f,
|
||||
float iCurvilinearLength = 0.0f)
|
||||
: Interface0DIteratorNested()
|
||||
{
|
||||
__A = iA;
|
||||
__B = iB;
|
||||
_begin = ibegin;
|
||||
_end = iend;
|
||||
_CurvilinearLength = iCurvilinearLength;
|
||||
_step = step;
|
||||
_t = t;
|
||||
_n = n;
|
||||
_currentn = currentn;
|
||||
_CurveLength = iCurveLength;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual CurvePointIterator *copy() const
|
||||
{
|
||||
return new CurvePointIterator(*this);
|
||||
}
|
||||
|
||||
inline Interface0DIterator castToInterface0DIterator() const
|
||||
{
|
||||
Interface0DIterator ret(new CurveInternal::CurvePointIterator(*this));
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual string getExactTypeName() const
|
||||
{
|
||||
return "CurvePointIterator";
|
||||
}
|
||||
|
||||
// operators
|
||||
inline CurvePointIterator &operator++() // operator corresponding to ++i
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline CurvePointIterator &operator--() // operator corresponding to --i
|
||||
{
|
||||
decrement();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// comparability
|
||||
virtual bool operator==(const Interface0DIteratorNested &b) const
|
||||
{
|
||||
const CurvePointIterator *it_exact = dynamic_cast<const CurvePointIterator *>(&b);
|
||||
if (!it_exact) {
|
||||
return false;
|
||||
}
|
||||
return ((__A == it_exact->__A) && (__B == it_exact->__B) && (_t == it_exact->_t));
|
||||
}
|
||||
|
||||
// dereferencing
|
||||
virtual CurvePoint &operator*()
|
||||
{
|
||||
return (_Point = CurvePoint(*__A, *__B, _t));
|
||||
}
|
||||
|
||||
virtual CurvePoint *operator->()
|
||||
{
|
||||
return &(operator*());
|
||||
}
|
||||
|
||||
virtual bool isBegin() const
|
||||
{
|
||||
if ((__A == _begin) && (_t < (float)M_EPSILON)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool isEnd() const
|
||||
{
|
||||
if (__B == _end) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// protected:
|
||||
virtual int increment()
|
||||
{
|
||||
if ((_currentn == _n - 1) && (_t == 1.0f)) {
|
||||
// we're setting the iterator to end
|
||||
++__A;
|
||||
++__B;
|
||||
++_currentn;
|
||||
_t = 0.0f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (0 == _step) { // means we iterate over initial vertices
|
||||
Vec3r vec_tmp((*__B)->point2d() - (*__A)->point2d());
|
||||
_CurvilinearLength += (float)vec_tmp.norm();
|
||||
if (_currentn == _n - 1) {
|
||||
_t = 1.0f;
|
||||
return 0;
|
||||
}
|
||||
++__B;
|
||||
++__A;
|
||||
++_currentn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// compute the new position:
|
||||
Vec3r vec_tmp2((*__A)->point2d() - (*__B)->point2d());
|
||||
float normAB = (float)vec_tmp2.norm();
|
||||
|
||||
if (normAB > M_EPSILON) {
|
||||
_CurvilinearLength += _step;
|
||||
_t = _t + _step / normAB;
|
||||
}
|
||||
else {
|
||||
_t = 1.0f; // AB is a null segment, we're directly at its end
|
||||
}
|
||||
// if normAB ~= 0, we don't change these values
|
||||
if (_t >= 1) {
|
||||
_CurvilinearLength -= normAB * (_t - 1);
|
||||
if (_currentn == _n - 1) {
|
||||
_t = 1.0f;
|
||||
}
|
||||
else {
|
||||
_t = 0.0f;
|
||||
++_currentn;
|
||||
++__A;
|
||||
++__B;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual int decrement()
|
||||
{
|
||||
if (_t == 0.0f) { // we're at the beginning of the edge
|
||||
_t = 1.0f;
|
||||
--_currentn;
|
||||
--__A;
|
||||
--__B;
|
||||
if (_currentn == _n - 1) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == _step) { // means we iterate over initial vertices
|
||||
Vec3r vec_tmp((*__B)->point2d() - (*__A)->point2d());
|
||||
_CurvilinearLength -= (float)vec_tmp.norm();
|
||||
_t = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// compute the new position:
|
||||
Vec3r vec_tmp2((*__A)->point2d() - (*__B)->point2d());
|
||||
float normAB = (float)vec_tmp2.norm();
|
||||
|
||||
if (normAB > M_EPSILON) {
|
||||
_CurvilinearLength -= _step;
|
||||
_t = _t - _step / normAB;
|
||||
}
|
||||
else {
|
||||
_t = -1.0f; // We just need a negative value here
|
||||
}
|
||||
|
||||
// round value
|
||||
if (fabs(_t) < (float)M_EPSILON) {
|
||||
_t = 0.0f;
|
||||
}
|
||||
if (_t < 0) {
|
||||
if (_currentn == 0) {
|
||||
_CurvilinearLength = 0.0f;
|
||||
}
|
||||
else {
|
||||
_CurvilinearLength += normAB * (-_t);
|
||||
}
|
||||
_t = 0.0f;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual float t() const
|
||||
{
|
||||
return _CurvilinearLength;
|
||||
}
|
||||
|
||||
virtual float u() const
|
||||
{
|
||||
return _CurvilinearLength / _CurveLength;
|
||||
}
|
||||
};
|
||||
|
||||
} // end of namespace CurveInternal
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,50 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief modifiers...
|
||||
*/
|
||||
|
||||
#include "TimeStamp.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
/* ----------------------------------------- *
|
||||
* *
|
||||
* 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) {}
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:EdgeModifier")
|
||||
};
|
||||
|
||||
/** 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());
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,61 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Set the type of the module
|
||||
*/
|
||||
|
||||
#include "Canvas.h"
|
||||
#include "StyleModule.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Module")
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
1345
blender-5.2.0/source/blender/freestyle/intern/stroke/Operators.cpp
Normal file
1345
blender-5.2.0/source/blender/freestyle/intern/stroke/Operators.cpp
Normal file
File diff suppressed because it is too large
Load Diff
273
blender-5.2.0/source/blender/freestyle/intern/stroke/Operators.h
Normal file
273
blender-5.2.0/source/blender/freestyle/intern/stroke/Operators.h
Normal file
@@ -0,0 +1,273 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class gathering stroke creation algorithms
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "Chain.h"
|
||||
#include "ChainingIterators.h"
|
||||
#include "Predicates0D.h"
|
||||
#include "Predicates1D.h"
|
||||
#include "StrokeShader.h"
|
||||
|
||||
#include "../system/TimeStamp.h"
|
||||
|
||||
#include "../view_map/Interface1D.h"
|
||||
#include "../view_map/ViewMap.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
/** 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 Operators {
|
||||
|
||||
public:
|
||||
typedef vector<Interface1D *> I1DContainer;
|
||||
typedef vector<Stroke *> StrokesContainer;
|
||||
|
||||
//
|
||||
// Operators
|
||||
//
|
||||
////////////////////////////////////////////////
|
||||
|
||||
/** Selects the ViewEdges of the ViewMap verifying a specified condition.
|
||||
* \param pred: The predicate expressing this condition
|
||||
*/
|
||||
static int select(UnaryPredicate1D &pred);
|
||||
|
||||
/** Builds a set of chains from the current set of ViewEdges.
|
||||
* Each ViewEdge of the current list starts a new chain.
|
||||
* The chaining operator then iterates over the ViewEdges
|
||||
* of the ViewMap using the user specified iterator.
|
||||
* This operator only iterates using the increment operator and is therefore unidirectional.
|
||||
* \param it:
|
||||
* The iterator on the ViewEdges of the ViewMap. It contains the chaining rule.
|
||||
* \param pred:
|
||||
* The predicate on the ViewEdge that expresses the stopping condition.
|
||||
* \param modifier:
|
||||
* A function that takes a ViewEdge as argument and that is used to modify the
|
||||
* processed ViewEdge state (the timestamp incrementation is a typical illustration of
|
||||
* such a modifier)
|
||||
*/
|
||||
static int chain(ViewEdgeInternal::ViewEdgeIterator &it,
|
||||
UnaryPredicate1D &pred,
|
||||
UnaryFunction1D_void &modifier);
|
||||
|
||||
/** Builds a set of chains from the current set of ViewEdges.
|
||||
* Each ViewEdge of the current list starts a new chain. The chaining operator then iterates
|
||||
* over the ViewEdges
|
||||
* of the ViewMap using the user specified iterator.
|
||||
* This operator only iterates using the increment operator and is therefore unidirectional.
|
||||
* This chaining operator is different from the previous one because it doesn't take any
|
||||
* modifier as argument. Indeed, the time stamp (insuring that a ViewEdge is processed one time)
|
||||
* is automatically managed in this case.
|
||||
* \param it:
|
||||
* The iterator on the ViewEdges of the ViewMap. It contains the chaining rule.
|
||||
* \param pred:
|
||||
* The predicate on the ViewEdge that expresses the stopping condition.
|
||||
*/
|
||||
static int chain(ViewEdgeInternal::ViewEdgeIterator &it, UnaryPredicate1D &pred);
|
||||
|
||||
/** Builds a set of chains from the current set of ViewEdges.
|
||||
* Each ViewEdge of the current list potentially starts a new chain. The chaining operator then
|
||||
* iterates over the ViewEdges of the ViewMap using the user specified iterator.
|
||||
* This operator iterates both using the increment and decrement operators and is therefore
|
||||
* bidirectional. This operator works with a ChainingIterator which contains the chaining rules.
|
||||
* It is this last one which can be told to chain only edges that belong to the selection or not
|
||||
* to process twice a ViewEdge during the chaining. Each time a ViewEdge is added to a chain,
|
||||
* its chaining time stamp is incremented. This allows you to keep track of the number of chains
|
||||
* to which a ViewEdge belongs to.
|
||||
* \param it:
|
||||
* The ChainingIterator on the ViewEdges of the ViewMap. It contains the chaining rule.
|
||||
* \param pred:
|
||||
* The predicate on the ViewEdge that expresses the stopping condition.
|
||||
*/
|
||||
static int bidirectionalChain(ChainingIterator &it, UnaryPredicate1D &pred);
|
||||
|
||||
/** The only difference with the above bidirectional chaining algorithm is that we don't need to
|
||||
* pass a stopping criterion. This might be desirable when the stopping criterion is already
|
||||
* contained in the iterator definition. Builds a set of chains from the current set of
|
||||
* ViewEdges. Each ViewEdge of the current list potentially starts a new chain. The chaining
|
||||
* operator then iterates over the ViewEdges of the ViewMap using the user specified iterator.
|
||||
* This operator iterates both using the increment and decrement operators and is therefore
|
||||
* bidirectional. This operator works with a ChainingIterator which contains the chaining rules.
|
||||
* It is this last one which can be told to chain only edges that belong to the selection or not
|
||||
* to process twice a ViewEdge during the chaining. Each time a ViewEdge is added to a chain,
|
||||
* its chaining time stamp is incremented. This allows you to keep track of the number of chains
|
||||
* to which a ViewEdge belongs to.
|
||||
* \param it:
|
||||
* The ChainingIterator on the ViewEdges of the ViewMap. It contains the chaining rule.
|
||||
*/
|
||||
static int bidirectionalChain(ChainingIterator &it);
|
||||
|
||||
/** Splits each chain of the current set of chains in a sequential way.
|
||||
* The points of each chain are processed (with a specified sampling) sequentially.
|
||||
* Each time a user specified starting condition is verified, a new chain begins and ends as
|
||||
* soon as a user-defined stopping predicate is verified.
|
||||
* This allows chains overlapping rather than chains partitioning.
|
||||
* The first point of the initial chain is the first point of one of the resulting chains.
|
||||
* The splitting ends when no more chain can start.
|
||||
* \param startingPred:
|
||||
* The predicate on a point that expresses the starting condition
|
||||
* \param stoppingPred:
|
||||
* The predicate on a point that expresses the stopping condition
|
||||
* \param sampling:
|
||||
* The resolution used to sample the chain for the predicates evaluation.
|
||||
* (The chain is not actually resampled, a virtual point only progresses along the
|
||||
* curve using this resolution)
|
||||
*/
|
||||
static int sequentialSplit(UnaryPredicate0D &startingPred,
|
||||
UnaryPredicate0D &stoppingPred,
|
||||
float sampling = 0.0f);
|
||||
|
||||
/** Splits each chain of the current set of chains in a sequential way.
|
||||
* The points of each chain are processed (with a specified sampling) sequentially and each time
|
||||
* a user specified condition is verified, the chain is split into two chains.
|
||||
* The resulting set of chains is a partition of the initial chain
|
||||
* \param pred:
|
||||
* The predicate on a point that expresses the splitting condition
|
||||
* \param sampling:
|
||||
* The resolution used to sample the chain for the predicate evaluation.
|
||||
* (The chain is not actually resampled, a virtual point only progresses along the
|
||||
* curve using this resolution)
|
||||
*/
|
||||
static int sequentialSplit(UnaryPredicate0D &pred, float sampling = 0.0f);
|
||||
|
||||
/** Splits the current set of chains in a recursive way.
|
||||
* We process the points of each chain (with a specified sampling) to find the point
|
||||
* minimizing a specified function. The chain is split in two at this point and the two new
|
||||
* chains are processed in the same way. The recursivity level is controlled through a
|
||||
* predicate 1D that expresses a stopping condition on the chain that is about to be processed.
|
||||
* \param func:
|
||||
* The Unary Function evaluated at each point of the chain.
|
||||
* The splitting point is the point minimizing this function
|
||||
* \param pred:
|
||||
* The Unary Predicate ex pressing the recursivity stopping condition.
|
||||
* This predicate is evaluated for each curve before it actually gets split.
|
||||
* If pred(chain) is true, the curve won't be split anymore.
|
||||
* \param sampling:
|
||||
* The resolution used to sample the chain for the predicates evaluation. (The chain
|
||||
* is not actually resampled, a virtual point only progresses along the curve using
|
||||
* this resolution)
|
||||
*/
|
||||
static int recursiveSplit(UnaryFunction0D<double> &func,
|
||||
UnaryPredicate1D &pred,
|
||||
float sampling = 0);
|
||||
|
||||
/** Splits the current set of chains in a recursive way.
|
||||
* We process the points of each chain (with a specified sampling) to find the point minimizing
|
||||
* a specified function. The chain is split in two at this point and the two new chains are
|
||||
* processed in the same way. The user can specify a 0D predicate to make a first selection on
|
||||
* the points that can potentially be split. A point that doesn't verify the 0D predicate
|
||||
* won't be candidate in realizing the min. The recursivity level is controlled through a
|
||||
* predicate 1D that expresses a stopping condition on the chain that is about to be processed.
|
||||
* \param func:
|
||||
* The Unary Function evaluated at each point of the chain.
|
||||
* The splitting point is the point minimizing this function
|
||||
* \param pred0d:
|
||||
* The Unary Predicate 0D used to select the candidate points where the split can
|
||||
* occur. For example, it is very likely that would rather have your chain splitting
|
||||
* around its middle point than around one of its extremities. A 0D predicate working
|
||||
* on the curvilinear abscissa allows to add this kind of constraints.
|
||||
* \param pred:
|
||||
* The Unary Predicate ex pressing the recursivity stopping condition.
|
||||
* This predicate is evaluated for each curve before it actually gets split.
|
||||
* If pred(chain) is true, the curve won't be split anymore.
|
||||
* \param sampling:
|
||||
* The resolution used to sample the chain for the predicates evaluation. (The chain
|
||||
* is not actually resampled, a virtual point only progresses along the curve using
|
||||
* this resolution)
|
||||
*/
|
||||
static int recursiveSplit(UnaryFunction0D<double> &func,
|
||||
UnaryPredicate0D &pred0d,
|
||||
UnaryPredicate1D &pred,
|
||||
float sampling = 0.0f);
|
||||
|
||||
/** Sorts the current set of chains (or view-edges)
|
||||
* according to the comparison predicate given as argument.
|
||||
* \param pred:
|
||||
* The binary predicate used for the comparison
|
||||
*/
|
||||
static int sort(BinaryPredicate1D &pred);
|
||||
|
||||
/** Creates and shades the strokes from the current set of chains.
|
||||
* A predicate can be specified to make a selection pass on the chains.
|
||||
* \param pred:
|
||||
* The predicate that a chain must verify in order to be transform as a stroke
|
||||
* \param shaders:
|
||||
* The list of shaders used to shade the strokes
|
||||
*/
|
||||
static int create(UnaryPredicate1D &pred, vector<StrokeShader *> shaders);
|
||||
|
||||
//
|
||||
// Data access
|
||||
//
|
||||
////////////////////////////////////////////////
|
||||
|
||||
static ViewEdge *getViewEdgeFromIndex(uint i)
|
||||
{
|
||||
return dynamic_cast<ViewEdge *>(_current_view_edges_set[i]);
|
||||
}
|
||||
|
||||
static Chain *getChainFromIndex(uint i)
|
||||
{
|
||||
return dynamic_cast<Chain *>(_current_chains_set[i]);
|
||||
}
|
||||
|
||||
static Stroke *getStrokeFromIndex(uint i)
|
||||
{
|
||||
return _current_strokes_set[i];
|
||||
}
|
||||
|
||||
static uint getViewEdgesSize()
|
||||
{
|
||||
return _current_view_edges_set.size();
|
||||
}
|
||||
|
||||
static uint getChainsSize()
|
||||
{
|
||||
return _current_chains_set.size();
|
||||
}
|
||||
|
||||
static uint getStrokesSize()
|
||||
{
|
||||
return _current_strokes_set.size();
|
||||
}
|
||||
|
||||
//
|
||||
// Not exported in Python
|
||||
//
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
static StrokesContainer *getStrokesSet()
|
||||
{
|
||||
return &_current_strokes_set;
|
||||
}
|
||||
|
||||
static void reset(bool removeStrokes = true);
|
||||
|
||||
private:
|
||||
Operators() {}
|
||||
|
||||
static I1DContainer _current_view_edges_set;
|
||||
static I1DContainer _current_chains_set;
|
||||
static I1DContainer *_current_set;
|
||||
static StrokesContainer _current_strokes_set;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Operators")
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,76 @@
|
||||
/* SPDX-FileCopyrightText: 2012-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class to define the Postscript rendering of a stroke
|
||||
*/
|
||||
|
||||
#include "PSStrokeRenderer.h"
|
||||
#include "Canvas.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
PSStrokeRenderer::PSStrokeRenderer(const char *iFileName)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,40 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class to define the Postscript rendering of a stroke
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "StrokeRenderer.h"
|
||||
|
||||
#include "../system/FreestyleConfig.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
/**********************************/
|
||||
/* */
|
||||
/* */
|
||||
/* PSStrokeRenderer */
|
||||
/* */
|
||||
/* */
|
||||
/**********************************/
|
||||
|
||||
class PSStrokeRenderer : public StrokeRenderer {
|
||||
public:
|
||||
PSStrokeRenderer(const char *iFileName = nullptr);
|
||||
|
||||
/** Renders a stroke rep */
|
||||
virtual void RenderStrokeRep(StrokeRep *iStrokeRep) const;
|
||||
virtual void RenderStrokeRepBasic(StrokeRep *iStrokeRep) const;
|
||||
|
||||
protected:
|
||||
mutable ofstream _ofstream;
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,25 @@
|
||||
/* SPDX-FileCopyrightText: 2012-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
*/
|
||||
|
||||
#include "Predicates0D.h"
|
||||
|
||||
#include "../python/Director.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
int UnaryPredicate0D::operator()(Interface0DIterator &it)
|
||||
{
|
||||
return Director_BPy_UnaryPredicate0D___call__(this, it);
|
||||
}
|
||||
|
||||
int BinaryPredicate0D::operator()(Interface0D &inter1, Interface0D &inter2)
|
||||
{
|
||||
return Director_BPy_BinaryPredicate0D___call__(this, inter1, inter2);
|
||||
}
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,153 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class gathering stroke creation algorithms
|
||||
*/
|
||||
|
||||
#include "../view_map/Functions0D.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
//
|
||||
// UnaryPredicate0D (base class for predicates in 0D)
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
/** Base class for Unary Predicates that work on Interface0DIterator.
|
||||
* A UnaryPredicate0D is a functor that evaluates a condition on a Interface0DIterator and returns
|
||||
* true or false depending on whether this condition is satisfied or not.
|
||||
* The UnaryPredicate0D is used by calling its () operator.
|
||||
* Any inherited class must overload the () operator.
|
||||
*/
|
||||
class UnaryPredicate0D {
|
||||
public:
|
||||
bool result;
|
||||
void *py_up0D;
|
||||
|
||||
/** Default constructor. */
|
||||
UnaryPredicate0D()
|
||||
{
|
||||
py_up0D = 0;
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~UnaryPredicate0D() {}
|
||||
|
||||
/** Returns the string of the name of the UnaryPredicate0D. */
|
||||
virtual string getName() const
|
||||
{
|
||||
return "UnaryPredicate0D";
|
||||
}
|
||||
|
||||
/** The () operator. Must be overload by inherited classes.
|
||||
* \param it:
|
||||
* The Interface0DIterator pointing onto the Interface0D at which we wish to evaluate the
|
||||
* predicate. \return true if the condition is satisfied, false otherwise.
|
||||
*/
|
||||
virtual int operator()(Interface0DIterator &it);
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:UnaryPredicate0D")
|
||||
};
|
||||
|
||||
//
|
||||
// BinaryPredicate0D (base class for predicates in 0D)
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
/** Base class for Binary Predicates working on Interface0D.
|
||||
* A BinaryPredicate0D is typically an ordering relation between two Interface0D.
|
||||
* It evaluates a relation between 2 Interface0D and returns true or false.
|
||||
* It is used by calling the () operator.
|
||||
*/
|
||||
class BinaryPredicate0D {
|
||||
public:
|
||||
bool result;
|
||||
void *py_bp0D;
|
||||
|
||||
/** Default constructor. */
|
||||
BinaryPredicate0D()
|
||||
{
|
||||
py_bp0D = 0;
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~BinaryPredicate0D() {}
|
||||
|
||||
/** Returns the string of the name of the binary predicate. */
|
||||
virtual string getName() const
|
||||
{
|
||||
return "BinaryPredicate0D";
|
||||
}
|
||||
|
||||
/** The () operator. Must be overload by inherited classes.
|
||||
* It evaluates a relation between 2 Interface0D.
|
||||
* \param inter1:
|
||||
* The first Interface0D.
|
||||
* \param inter2:
|
||||
* The second Interface0D.
|
||||
* \return true or false.
|
||||
*/
|
||||
virtual int operator()(Interface0D &inter1, Interface0D &inter2);
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:BinaryPredicate0D")
|
||||
};
|
||||
|
||||
//
|
||||
// Predicates definitions
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
namespace Predicates0D {
|
||||
|
||||
// TrueUP0D
|
||||
/** Returns true any time */
|
||||
class TrueUP0D : public UnaryPredicate0D {
|
||||
public:
|
||||
/** Default constructor. */
|
||||
TrueUP0D() {}
|
||||
|
||||
/** Returns the string "TrueUP0D". */
|
||||
string getName() const
|
||||
{
|
||||
return "TrueUP0D";
|
||||
}
|
||||
|
||||
/** The () operator. */
|
||||
int operator()(Interface0DIterator &)
|
||||
{
|
||||
result = true;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// FalseUP0D
|
||||
/** Returns false any time */
|
||||
class FalseUP0D : public UnaryPredicate0D {
|
||||
public:
|
||||
/** Default constructor. */
|
||||
FalseUP0D() {}
|
||||
|
||||
/** Returns the string "FalseUP0D". */
|
||||
string getName() const
|
||||
{
|
||||
return "FalseUP0D";
|
||||
}
|
||||
|
||||
/** The () operator. */
|
||||
int operator()(Interface0DIterator &)
|
||||
{
|
||||
result = false;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // end of namespace Predicates0D
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,25 @@
|
||||
/* SPDX-FileCopyrightText: 2012-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
*/
|
||||
|
||||
#include "Predicates1D.h"
|
||||
|
||||
#include "../python/Director.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
int UnaryPredicate1D::operator()(Interface1D &inter)
|
||||
{
|
||||
return Director_BPy_UnaryPredicate1D___call__(this, inter);
|
||||
}
|
||||
|
||||
int BinaryPredicate1D::operator()(Interface1D &inter1, Interface1D &inter2)
|
||||
{
|
||||
return Director_BPy_BinaryPredicate1D___call__(this, inter1, inter2);
|
||||
}
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,555 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class gathering stroke creation algorithms
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "AdvancedFunctions1D.h"
|
||||
|
||||
#include "../system/TimeStamp.h"
|
||||
|
||||
#include "../view_map/Functions1D.h"
|
||||
#include "../view_map/Interface1D.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
//
|
||||
// UnaryPredicate1D (base class for predicates in 1D)
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
/** Base class for Unary Predicates that work on Interface1D.
|
||||
* A UnaryPredicate1D is a functor that evaluates a condition on a Interface1D and returns
|
||||
* true or false depending on whether this condition is satisfied or not.
|
||||
* The UnaryPredicate1D is used by calling its () operator.
|
||||
* Any inherited class must overload the () operator.
|
||||
*/
|
||||
class UnaryPredicate1D {
|
||||
public:
|
||||
bool result;
|
||||
void *py_up1D;
|
||||
|
||||
/** Default constructor. */
|
||||
UnaryPredicate1D()
|
||||
{
|
||||
py_up1D = nullptr;
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~UnaryPredicate1D() {}
|
||||
|
||||
/** Returns the string of the name of the UnaryPredicate1D. */
|
||||
virtual string getName() const
|
||||
{
|
||||
return "UnaryPredicate1D";
|
||||
}
|
||||
|
||||
/** The () operator. Must be overload by inherited classes.
|
||||
* \param inter:
|
||||
* The Interface1D on which we wish to evaluate the predicate.
|
||||
* \return true if the condition is satisfied, false otherwise.
|
||||
*/
|
||||
virtual int operator()(Interface1D &inter);
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:UnaryPredicate1D")
|
||||
};
|
||||
|
||||
//
|
||||
// BinaryPredicate1D (base class for predicates in 1D)
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
/** Base class for Binary Predicates working on Interface1D.
|
||||
* A BinaryPredicate1D is typically an ordering relation between two Interface1D.
|
||||
* It evaluates a relation between 2 Interface1D and returns true or false.
|
||||
* It is used by calling the () operator.
|
||||
*/
|
||||
class BinaryPredicate1D {
|
||||
public:
|
||||
bool result;
|
||||
void *py_bp1D;
|
||||
|
||||
/** Default constructor. */
|
||||
BinaryPredicate1D()
|
||||
{
|
||||
py_bp1D = nullptr;
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~BinaryPredicate1D() {}
|
||||
|
||||
/** Returns the string of the name of the binary predicate. */
|
||||
virtual string getName() const
|
||||
{
|
||||
return "BinaryPredicate1D";
|
||||
}
|
||||
|
||||
/** The () operator. Must be overload by inherited classes.
|
||||
* It evaluates a relation between 2 Interface1D.
|
||||
* \param inter1:
|
||||
* The first Interface1D.
|
||||
* \param inter2:
|
||||
* The second Interface1D.
|
||||
* \return true or false.
|
||||
*/
|
||||
virtual int operator()(Interface1D &inter1, Interface1D &inter2);
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:BinaryPredicate1D")
|
||||
};
|
||||
|
||||
//
|
||||
// Predicates definitions
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
namespace Predicates1D {
|
||||
|
||||
// TrueUP1D
|
||||
/** Returns true */
|
||||
class TrueUP1D : public UnaryPredicate1D {
|
||||
public:
|
||||
/** Constructor */
|
||||
TrueUP1D() {}
|
||||
|
||||
/** Returns the string "TrueUP1D". */
|
||||
string getName() const
|
||||
{
|
||||
return "TrueUP1D";
|
||||
}
|
||||
|
||||
/** the () operator */
|
||||
int operator()(Interface1D &)
|
||||
{
|
||||
result = true;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// FalseUP1D
|
||||
/** Returns false */
|
||||
class FalseUP1D : public UnaryPredicate1D {
|
||||
public:
|
||||
/** Constructor */
|
||||
FalseUP1D() {}
|
||||
|
||||
/** Returns the string "FalseUP1D". */
|
||||
string getName() const
|
||||
{
|
||||
return "FalseUP1D";
|
||||
}
|
||||
|
||||
/** the () operator */
|
||||
int operator()(Interface1D &)
|
||||
{
|
||||
result = false;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// QuantitativeInvisibilityUP1D
|
||||
/** Returns true if the Quantitative Invisibility evaluated at an Interface1D, using the
|
||||
* QuantitativeInvisibilityF1D functor, equals a certain user-defined value.
|
||||
*/
|
||||
class QuantitativeInvisibilityUP1D : public UnaryPredicate1D {
|
||||
public:
|
||||
/** Builds the Predicate.
|
||||
* \param qi:
|
||||
* The Quantitative Invisibility you want the Interface1D to have
|
||||
*/
|
||||
QuantitativeInvisibilityUP1D(uint qi = 0) : _qi(qi) {}
|
||||
|
||||
/** Returns the string "QuantitativeInvisibilityUP1D" */
|
||||
string getName() const
|
||||
{
|
||||
return "QuantitativeInvisibilityUP1D";
|
||||
}
|
||||
|
||||
/** the () operator */
|
||||
int operator()(Interface1D &inter)
|
||||
{
|
||||
Functions1D::QuantitativeInvisibilityF1D func;
|
||||
if (func(inter) < 0) {
|
||||
return -1;
|
||||
}
|
||||
result = (func.result == _qi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
uint _qi;
|
||||
};
|
||||
|
||||
// ContourUP1D
|
||||
/** Returns true if the Interface1D is a contour.
|
||||
* An Interface1D is a contour if it is bordered by a different shape on each of its sides.
|
||||
*/
|
||||
class ContourUP1D : public UnaryPredicate1D {
|
||||
private:
|
||||
Functions1D::CurveNatureF1D _getNature;
|
||||
|
||||
public:
|
||||
/** Returns the string "ContourUP1D". */
|
||||
string getName() const
|
||||
{
|
||||
return "ContourUP1D";
|
||||
}
|
||||
|
||||
/** The () operator. */
|
||||
int operator()(Interface1D &inter)
|
||||
{
|
||||
if (_getNature(inter) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if ((_getNature.result & Nature::SILHOUETTE) || (_getNature.result & Nature::BORDER)) {
|
||||
Interface0DIterator it = inter.verticesBegin();
|
||||
for (; !it.isEnd(); ++it) {
|
||||
if (Functions0D::getOccludeeF0D(it) != Functions0D::getShapeF0D(it)) {
|
||||
result = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
result = false;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// ExternalContourUP1D
|
||||
/** Returns true if the Interface1D is an external contour.
|
||||
* An Interface1D is an external contour if it is bordered by no shape on one of its sides.
|
||||
*/
|
||||
class ExternalContourUP1D : public UnaryPredicate1D {
|
||||
private:
|
||||
Functions1D::CurveNatureF1D _getNature;
|
||||
|
||||
public:
|
||||
/** Returns the string "ExternalContourUP1D" */
|
||||
string getName() const
|
||||
{
|
||||
return "ExternalContourUP1D";
|
||||
}
|
||||
|
||||
/** The () operator. */
|
||||
int operator()(Interface1D &inter)
|
||||
{
|
||||
if (_getNature(inter) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if ((_getNature.result & Nature::SILHOUETTE) || (_getNature.result & Nature::BORDER)) {
|
||||
set<ViewShape *> occluded;
|
||||
Functions1D::getOccludeeF1D(inter, occluded);
|
||||
for (set<ViewShape *>::iterator os = occluded.begin(), osend = occluded.end(); os != osend;
|
||||
++os)
|
||||
{
|
||||
if ((*os) == 0) {
|
||||
result = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
result = false;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// EqualToTimeStampUP1D
|
||||
/** Returns true if the Interface1D's time stamp is equal to a certain user-defined value. */
|
||||
class EqualToTimeStampUP1D : public UnaryPredicate1D {
|
||||
protected:
|
||||
uint _timeStamp;
|
||||
|
||||
public:
|
||||
EqualToTimeStampUP1D(uint ts) : UnaryPredicate1D()
|
||||
{
|
||||
_timeStamp = ts;
|
||||
}
|
||||
|
||||
/** Returns the string "EqualToTimeStampUP1D". */
|
||||
string getName() const
|
||||
{
|
||||
return "EqualToTimeStampUP1D";
|
||||
}
|
||||
|
||||
/** The () operator. */
|
||||
int operator()(Interface1D &inter)
|
||||
{
|
||||
result = (inter.getTimeStamp() == _timeStamp);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// EqualToChainingTimeStampUP1D
|
||||
/** Returns true if the Interface1D's time stamp is equal to a certain user-defined value. */
|
||||
class EqualToChainingTimeStampUP1D : public UnaryPredicate1D {
|
||||
protected:
|
||||
uint _timeStamp;
|
||||
|
||||
public:
|
||||
EqualToChainingTimeStampUP1D(uint ts) : UnaryPredicate1D()
|
||||
{
|
||||
_timeStamp = ts;
|
||||
}
|
||||
|
||||
/** Returns the string "EqualToChainingTimeStampUP1D". */
|
||||
string getName() const
|
||||
{
|
||||
return "EqualToChainingTimeStampUP1D";
|
||||
}
|
||||
|
||||
/** The () operator. */
|
||||
int operator()(Interface1D &inter)
|
||||
{
|
||||
ViewEdge *edge = dynamic_cast<ViewEdge *>(&inter);
|
||||
if (!edge) {
|
||||
result = false;
|
||||
return 0;
|
||||
}
|
||||
result = (edge->getChainingTimeStamp() >= _timeStamp);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// ShapeUP1D
|
||||
/** Returns true if the shape to which the Interface1D belongs to has the same Id as the one
|
||||
* specified by the user. */
|
||||
class ShapeUP1D : public UnaryPredicate1D {
|
||||
private:
|
||||
Id _id;
|
||||
|
||||
public:
|
||||
/** Builds the Predicate.
|
||||
* \param idFirst:
|
||||
* The first Id component.
|
||||
* \param idSecond:
|
||||
* The second Id component.
|
||||
*/
|
||||
ShapeUP1D(uint idFirst, uint idSecond = 0) : UnaryPredicate1D()
|
||||
{
|
||||
_id = Id(idFirst, idSecond);
|
||||
}
|
||||
|
||||
/** Returns the string "ShapeUP1D". */
|
||||
string getName() const
|
||||
{
|
||||
return "ShapeUP1D";
|
||||
}
|
||||
|
||||
/** The () operator. */
|
||||
int operator()(Interface1D &inter)
|
||||
{
|
||||
set<ViewShape *> shapes;
|
||||
Functions1D::getShapeF1D(inter, shapes);
|
||||
for (set<ViewShape *>::iterator s = shapes.begin(), send = shapes.end(); s != send; ++s) {
|
||||
if ((*s)->getId() == _id) {
|
||||
result = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
result = false;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// WithinImageBoundaryUP1D
|
||||
/** Returns true if the Interface1D is (partly) within the image boundary. */
|
||||
class WithinImageBoundaryUP1D : public UnaryPredicate1D {
|
||||
private:
|
||||
real _xmin, _ymin, _xmax, _ymax;
|
||||
|
||||
public:
|
||||
/** Builds the Predicate.
|
||||
* \param xmin:
|
||||
* The X lower bound of the image boundary.
|
||||
* \param ymin:
|
||||
* The Y lower bound of the image boundary.
|
||||
* \param xmax:
|
||||
* The X upper bound of the image boundary.
|
||||
* \param ymax:
|
||||
* The Y upper bound of the image boundary.
|
||||
*/
|
||||
WithinImageBoundaryUP1D(const real xmin, const real ymin, const real xmax, const real ymax)
|
||||
: _xmin(xmin), _ymin(ymin), _xmax(xmax), _ymax(ymax)
|
||||
{
|
||||
}
|
||||
|
||||
/** Returns the string "WithinImageBoundaryUP1D" */
|
||||
string getName() const
|
||||
{
|
||||
return "WithinImageBoundaryUP1D";
|
||||
}
|
||||
|
||||
/** The () operator. */
|
||||
int operator()(Interface1D &inter)
|
||||
{
|
||||
// 1st pass: check if a point is within the image boundary.
|
||||
Interface0DIterator it = inter.verticesBegin(), itend = inter.verticesEnd();
|
||||
for (; it != itend; ++it) {
|
||||
real x = (*it).getProjectedX();
|
||||
real y = (*it).getProjectedY();
|
||||
if (_xmin <= x && x <= _xmax && _ymin <= y && y <= _ymax) {
|
||||
result = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// 2nd pass: check if a line segment intersects with the image boundary.
|
||||
it = inter.verticesBegin();
|
||||
if (it != itend) {
|
||||
Vec2r pmin(_xmin, _ymin);
|
||||
Vec2r pmax(_xmax, _ymax);
|
||||
Vec2r prev((*it).getPoint2D());
|
||||
++it;
|
||||
for (; it != itend; ++it) {
|
||||
Vec2r p((*it).getPoint2D());
|
||||
if (GeomUtils::intersect2dSeg2dArea(pmin, pmax, prev, p)) {
|
||||
result = true;
|
||||
return 0;
|
||||
}
|
||||
prev = p;
|
||||
}
|
||||
}
|
||||
result = false;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Binary Predicates definitions
|
||||
//
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
// TrueBP1D
|
||||
/** Returns true. */
|
||||
class TrueBP1D : public BinaryPredicate1D {
|
||||
public:
|
||||
/** Returns the string "TrueBP1D" */
|
||||
string getName() const
|
||||
{
|
||||
return "TrueBP1D";
|
||||
}
|
||||
|
||||
/** The () operator. */
|
||||
int operator()(Interface1D & /*i1*/, Interface1D & /*i2*/)
|
||||
{
|
||||
result = true;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// FalseBP1D
|
||||
/** Returns false. */
|
||||
class FalseBP1D : public BinaryPredicate1D {
|
||||
public:
|
||||
/** Returns the string "FalseBP1D" */
|
||||
string getName() const
|
||||
{
|
||||
return "FalseBP1D";
|
||||
}
|
||||
|
||||
/** The () operator. */
|
||||
int operator()(Interface1D & /*i1*/, Interface1D & /*i2*/)
|
||||
{
|
||||
result = false;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Length2DBP1D
|
||||
/** Returns true if the 2D length of the Interface1D i1 is less than the 2D length of the
|
||||
* Interface1D i2. */
|
||||
class Length2DBP1D : public BinaryPredicate1D {
|
||||
public:
|
||||
/** Returns the string "Length2DBP1D" */
|
||||
string getName() const
|
||||
{
|
||||
return "Length2DBP1D";
|
||||
}
|
||||
|
||||
/** The () operator. */
|
||||
int operator()(Interface1D &i1, Interface1D &i2)
|
||||
{
|
||||
result = (i1.getLength2D() > i2.getLength2D());
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// SameShapeIdBP1D
|
||||
/** Returns true if the Interface1D i1 and i2 belong to the same shape. */
|
||||
class SameShapeIdBP1D : public BinaryPredicate1D {
|
||||
public:
|
||||
/** Returns the string "SameShapeIdBP1D" */
|
||||
string getName() const
|
||||
{
|
||||
return "SameShapeIdBP1D";
|
||||
}
|
||||
|
||||
/** The () operator. */
|
||||
int operator()(Interface1D &i1, Interface1D &i2)
|
||||
{
|
||||
set<ViewShape *> shapes1;
|
||||
Functions1D::getShapeF1D(i1, shapes1);
|
||||
set<ViewShape *> shapes2;
|
||||
Functions1D::getShapeF1D(i2, shapes2);
|
||||
// FIXME:// n2 algo, can do better...
|
||||
for (set<ViewShape *>::iterator s = shapes1.begin(), send = shapes1.end(); s != send; ++s) {
|
||||
Id current = (*s)->getId();
|
||||
for (set<ViewShape *>::iterator s2 = shapes2.begin(), s2end = shapes2.end(); s2 != s2end;
|
||||
++s2)
|
||||
{
|
||||
if ((*s2)->getId() == current) {
|
||||
result = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
result = false;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// ViewMapGradientNormBP1D
|
||||
/** Returns true if the evaluation of the Gradient norm Function is higher for Interface1D i1 than
|
||||
* for i2. */
|
||||
class ViewMapGradientNormBP1D : public BinaryPredicate1D {
|
||||
private:
|
||||
Functions1D::GetViewMapGradientNormF1D _func;
|
||||
|
||||
public:
|
||||
ViewMapGradientNormBP1D(int level, IntegrationType iType = MEAN, float sampling = 2.0)
|
||||
: BinaryPredicate1D(), _func(level, iType, sampling)
|
||||
{
|
||||
}
|
||||
|
||||
/** Returns the string "ViewMapGradientNormBP1D" */
|
||||
string getName() const
|
||||
{
|
||||
return "ViewMapGradientNormBP1D";
|
||||
}
|
||||
|
||||
/** The () operator. */
|
||||
int operator()(Interface1D &i1, Interface1D &i2)
|
||||
{
|
||||
if (_func(i1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
real n1 = _func.result;
|
||||
if (_func(i2) < 0) {
|
||||
return -1;
|
||||
}
|
||||
real n2 = _func.result;
|
||||
result = (n1 > n2);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // end of namespace Predicates1D
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,48 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class defining an information map using a QImage
|
||||
*/
|
||||
|
||||
#include <qimage.h>
|
||||
|
||||
#include "InformationMap.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
class QInformationMap : public InformationMap {
|
||||
private:
|
||||
QImage _map; // the image or a piece of image
|
||||
|
||||
public:
|
||||
QInformationMap();
|
||||
QInformationMap(const QImage &);
|
||||
QInformationMap(const QInformationMap &);
|
||||
QInformationMap &operator=(const QInformationMap &);
|
||||
|
||||
// float getSmoothedPixel(int x, int y, float sigma = 0.2f);1
|
||||
virtual float getMean(int x, int y);
|
||||
virtual void retrieveMeanAndVariance(int x, int y, float &oMean, float &oVariance);
|
||||
|
||||
inline const QImage &map() const
|
||||
{
|
||||
return _map;
|
||||
}
|
||||
|
||||
inline void setMap(const QImage &iMap, float iw, float ih)
|
||||
{
|
||||
_map = iMap.copy();
|
||||
_w = iw;
|
||||
_h = ih;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual float computeGaussian(int x, int y);
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
1017
blender-5.2.0/source/blender/freestyle/intern/stroke/Stroke.cpp
Normal file
1017
blender-5.2.0/source/blender/freestyle/intern/stroke/Stroke.cpp
Normal file
File diff suppressed because it is too large
Load Diff
875
blender-5.2.0/source/blender/freestyle/intern/stroke/Stroke.h
Normal file
875
blender-5.2.0/source/blender/freestyle/intern/stroke/Stroke.h
Normal file
@@ -0,0 +1,875 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Classes to define a stroke
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "Curve.h"
|
||||
|
||||
#include "../view_map/Interface1D.h"
|
||||
#include "../view_map/Silhouette.h"
|
||||
|
||||
#include "../system/FreestyleConfig.h"
|
||||
#include "../system/StringUtils.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace blender {
|
||||
struct MTex;
|
||||
struct bNodeTree;
|
||||
} // namespace blender
|
||||
|
||||
#ifndef MAX_MTEX
|
||||
# define MAX_MTEX 18
|
||||
#endif
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
//
|
||||
// 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 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 float getColorR() const
|
||||
{
|
||||
return _color[0];
|
||||
}
|
||||
|
||||
/** Returns the G color component. */
|
||||
inline float getColorG() const
|
||||
{
|
||||
return _color[1];
|
||||
}
|
||||
|
||||
/** Returns the B color component. */
|
||||
inline 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 float getThicknessR() const
|
||||
{
|
||||
return _thickness[0];
|
||||
}
|
||||
|
||||
/** Returns the thickness on the left of the vertex when following the stroke. */
|
||||
inline 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 available */
|
||||
bool isAttributeAvailableReal(const char *iName) const;
|
||||
|
||||
/** Checks whether the attribute iName is available */
|
||||
bool isAttributeAvailableVec2f(const char *iName) const;
|
||||
|
||||
/** Checks whether the attribute iName is available */
|
||||
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;
|
||||
|
||||
//! the color
|
||||
float _color[3];
|
||||
//! alpha
|
||||
float _alpha;
|
||||
//! the thickness on the right and on the left of the backbone vertex (the stroke is oriented)
|
||||
float _thickness[2];
|
||||
bool _visible;
|
||||
realMap *_userAttributesReal;
|
||||
Vec2fMap *_userAttributesVec2f;
|
||||
Vec3fMap *_userAttributesVec3f;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:StrokeAttribute")
|
||||
};
|
||||
|
||||
//
|
||||
// StrokeVertex
|
||||
//
|
||||
////////////////////////////////////////////////////////
|
||||
|
||||
/** Class to define a stroke vertex. */
|
||||
class 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);
|
||||
|
||||
/* 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 Vec2r */
|
||||
inline Vec2r getPoint() const
|
||||
{
|
||||
return getPoint2D();
|
||||
}
|
||||
|
||||
/** Returns the i-th 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 Vec2r &p)
|
||||
{
|
||||
_Point2d[0] = p[0];
|
||||
_Point2d[1] = p[1];
|
||||
}
|
||||
|
||||
/** Returns a reference to the i-th 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 */
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:StrokeVertex")
|
||||
};
|
||||
|
||||
//
|
||||
// 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 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 simulate the interaction media-medium. */
|
||||
enum MediumType {
|
||||
/** To simulate a dry medium such as Pencil or Charcoal. */
|
||||
DRY_MEDIUM,
|
||||
/** To simulate ink painting (color subtraction blending). */
|
||||
HUMID_MEDIUM,
|
||||
/** To simulate an opaque medium (oil, spray...). */
|
||||
OPAQUE_MEDIUM,
|
||||
};
|
||||
|
||||
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;
|
||||
float _textureStep;
|
||||
// StrokeRenderer *_renderer; // mark implementation OpenGL renderer
|
||||
MediumType _mediumType;
|
||||
uint _textureId;
|
||||
blender::MTex *_mtex[MAX_MTEX];
|
||||
blender::bNodeTree *_nodeTree;
|
||||
bool _tips;
|
||||
StrokeRep *_rep;
|
||||
Vec2r _extremityOrientations[2]; // the orientations of the first and last extremity
|
||||
|
||||
public:
|
||||
/** default constructor */
|
||||
Stroke();
|
||||
|
||||
/** copy constructor */
|
||||
Stroke(const Stroke &iBrother);
|
||||
|
||||
/** Builds a stroke from a set of StrokeVertex.
|
||||
* This constructor is templated by an iterator type.
|
||||
* This iterator type must allow the vertices parsing using the ++ operator.
|
||||
* \param iBegin:
|
||||
* The iterator pointing to the first vertex.
|
||||
* \param iEnd:
|
||||
* The iterator pointing to the end of the vertex list.
|
||||
*/
|
||||
template<class InputVertexIterator> Stroke(InputVertexIterator iBegin, InputVertexIterator iEnd);
|
||||
|
||||
/** Destructor */
|
||||
virtual ~Stroke();
|
||||
|
||||
/* operators */
|
||||
/** operator = */
|
||||
Stroke &operator=(const Stroke &iBrother);
|
||||
|
||||
/** Compute the sampling needed to get iNVertices vertices.
|
||||
* If the specified number of vertices is less than the actual number of vertices, the actual
|
||||
* sampling value is returned. (To remove Vertices, use the RemoveVertex() method of this class).
|
||||
* \param iNVertices:
|
||||
* The number of StrokeVertices we eventually want in our Stroke.
|
||||
* \return the sampling that must be used in the Resample(float) method.
|
||||
* \see Resample(int)
|
||||
* \see Resample(float)
|
||||
*/
|
||||
float ComputeSampling(int iNVertices);
|
||||
|
||||
/** Resampling method.
|
||||
* Resamples the curve so that it eventually has iNPoints. That means it is going to add
|
||||
* iNPoints-vertices_size, if vertices_size is the number of points we already have. If
|
||||
* vertices_size >= iNPoints, no resampling is done.
|
||||
* \param iNPoints: The number of vertices we
|
||||
* eventually want in our stroke.
|
||||
*/
|
||||
int 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.
|
||||
*/
|
||||
int Resample(float iSampling);
|
||||
|
||||
/** Removes all vertices from the Stroke.
|
||||
*/
|
||||
void RemoveAllVertices();
|
||||
|
||||
/** 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 StrokeVertex before which iVertex must be inserted.
|
||||
*/
|
||||
void InsertVertex(StrokeVertex *iVertex, StrokeInternal::StrokeVertexIterator next);
|
||||
|
||||
/** Updates the 2D length of the Stroke */
|
||||
void UpdateLength();
|
||||
|
||||
/* Render method */
|
||||
void ScaleThickness(float iFactor);
|
||||
void Render(const StrokeRenderer *iRenderer);
|
||||
void RenderBasic(const StrokeRenderer *iRenderer);
|
||||
|
||||
/* Iterator definition */
|
||||
|
||||
/* accessors */
|
||||
/** Returns the 2D length of the Stroke */
|
||||
inline real getLength2D() const
|
||||
{
|
||||
return _Length;
|
||||
}
|
||||
|
||||
/** Returns a reference to the time stamp value of the stroke. */
|
||||
/** Returns the MediumType used for this Stroke. */
|
||||
inline MediumType getMediumType() const
|
||||
{
|
||||
return _mediumType;
|
||||
}
|
||||
|
||||
/** Returns the id of the texture used to simulate th marks system for this Stroke */
|
||||
inline uint getTextureId()
|
||||
{
|
||||
return _textureId;
|
||||
}
|
||||
|
||||
/** Returns the spacing of texture coordinates along the stroke length */
|
||||
inline float getTextureStep()
|
||||
{
|
||||
return _textureStep;
|
||||
}
|
||||
|
||||
/** Returns the texture used at given index to simulate the marks system for this Stroke */
|
||||
inline blender::MTex *getMTex(int idx)
|
||||
{
|
||||
return _mtex[idx];
|
||||
}
|
||||
|
||||
/** Return the shader node tree to define textures. */
|
||||
inline blender::bNodeTree *getNodeTree()
|
||||
{
|
||||
return _nodeTree;
|
||||
}
|
||||
|
||||
/** Returns true if this Stroke has textures assigned, false otherwise. */
|
||||
inline bool hasTex() const
|
||||
{
|
||||
return (_mtex[0] != nullptr) || _nodeTree;
|
||||
}
|
||||
|
||||
/** 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(uint id)
|
||||
{
|
||||
_textureId = id;
|
||||
}
|
||||
|
||||
/** sets the spacing of texture coordinates along the stroke length. */
|
||||
inline void setTextureStep(float step)
|
||||
{
|
||||
_textureStep = step;
|
||||
}
|
||||
|
||||
/** assigns a blender texture to the first available slot. */
|
||||
inline int setMTex(blender::MTex *mtex)
|
||||
{
|
||||
for (int a = 0; a < MAX_MTEX; a++) {
|
||||
if (!_mtex[a]) {
|
||||
_mtex[a] = mtex;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1; /* no free slots */
|
||||
}
|
||||
|
||||
/** assigns a node tree (of new shading nodes) to define textures. */
|
||||
inline void setNodeTree(blender::bNodeTree *iNodeTree)
|
||||
{
|
||||
_nodeTree = iNodeTree;
|
||||
}
|
||||
|
||||
/** 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 sampling = 0.0f);
|
||||
const_vertex_iterator vertices_end() const;
|
||||
vertex_iterator vertices_end();
|
||||
|
||||
/** Returns a StrokeVertexIterator pointing on the first StrokeVertex of the Stroke. One can
|
||||
* specify a sampling value to re-sample the Stroke on the fly if needed.
|
||||
*
|
||||
* \param t: The resampling value with which we want our Stroke to be resampled.
|
||||
* If 0 is specified, no resampling is done.
|
||||
*/
|
||||
StrokeInternal::StrokeVertexIterator strokeVerticesBegin(float t = 0.0f);
|
||||
|
||||
/** Returns a StrokeVertexIterator pointing after the last StrokeVertex of the Stroke. */
|
||||
StrokeInternal::StrokeVertexIterator strokeVerticesEnd();
|
||||
|
||||
/** Returns the number of StrokeVertex constituting the Stroke. */
|
||||
inline uint strokeVerticesSize() const
|
||||
{
|
||||
return _Vertices.size();
|
||||
}
|
||||
|
||||
/** Returns the i-th StrokeVertex constituting the Stroke. */
|
||||
inline StrokeVertex &strokeVerticeAt(uint i)
|
||||
{
|
||||
return *(_Vertices.at(i));
|
||||
}
|
||||
|
||||
// Iterator access (Interface1D)
|
||||
/** Returns an Interface0DIterator pointing on the first StrokeVertex of the Stroke. */
|
||||
virtual Interface0DIterator verticesBegin();
|
||||
|
||||
/** Returns an Interface0DIterator pointing after the last StrokeVertex of the Stroke. */
|
||||
virtual Interface0DIterator verticesEnd();
|
||||
|
||||
virtual Interface0DIterator pointsBegin(float t = 0.0f);
|
||||
virtual Interface0DIterator pointsEnd(float t = 0.0f);
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Stroke")
|
||||
};
|
||||
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,164 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Iterators used to iterate over the elements of the Stroke. Can't be used in python
|
||||
*/
|
||||
|
||||
#include "Stroke.h"
|
||||
#include "StrokeIterators.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
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 Freestyle::Stroke;
|
||||
// friend class vertex_iterator;
|
||||
|
||||
inline vertex_iterator_base() : parent_class() {}
|
||||
|
||||
inline vertex_iterator_base(const iterator &iBrother) : parent_class()
|
||||
{
|
||||
_it = iBrother._it;
|
||||
_begin = iBrother._begin;
|
||||
_end = iBrother._end;
|
||||
}
|
||||
|
||||
inline vertex_iterator_base(const const_iterator &iBrother) : parent_class()
|
||||
{
|
||||
_it = iBrother._it;
|
||||
_begin = iBrother._begin;
|
||||
_end = iBrother._end;
|
||||
}
|
||||
|
||||
// protected: //FIXME
|
||||
public:
|
||||
inline vertex_iterator_base(vertex_container_iterator it,
|
||||
vertex_container_iterator begin,
|
||||
vertex_container_iterator end)
|
||||
: parent_class()
|
||||
{
|
||||
_it = it;
|
||||
_begin = begin;
|
||||
_end = end;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~vertex_iterator_base() {}
|
||||
|
||||
virtual bool begin() const
|
||||
{
|
||||
return (_it == _begin) ? true : false;
|
||||
}
|
||||
|
||||
virtual bool end() const
|
||||
{
|
||||
return (_it == _end) ? true : false;
|
||||
}
|
||||
|
||||
// operators
|
||||
inline Self &operator++() // operator corresponding to ++i
|
||||
{
|
||||
++_it;
|
||||
return *(this);
|
||||
}
|
||||
|
||||
/* Operator corresponding to i++, i.e. which returns the value *and then* increments.
|
||||
* That's why we store the value in a temp.
|
||||
*/
|
||||
inline Self operator++(int)
|
||||
{
|
||||
Self tmp = *this;
|
||||
++_it;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
inline Self &operator--() // operator corresponding to --i
|
||||
{
|
||||
--_it;
|
||||
return *(this);
|
||||
}
|
||||
|
||||
inline Self operator--(int)
|
||||
{
|
||||
Self tmp = *this;
|
||||
--_it;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
// comparability
|
||||
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
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,56 @@
|
||||
/* SPDX-FileCopyrightText: 2009-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Functions to manage I/O for the stroke
|
||||
*/
|
||||
|
||||
#include "StrokeAdvancedIterators.h"
|
||||
|
||||
#include "StrokeIO.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,26 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Functions to manage I/O for the stroke
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "Stroke.h"
|
||||
|
||||
#include "../system/FreestyleConfig.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
ostream &operator<<(ostream &out, const StrokeAttribute &iStrokeAttribute);
|
||||
|
||||
ostream &operator<<(ostream &out, const StrokeVertex &iStrokeVertex);
|
||||
|
||||
ostream &operator<<(ostream &out, const Stroke &iStroke);
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,217 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Iterators used to iterate over the elements of the Stroke
|
||||
*/
|
||||
|
||||
#include "Stroke.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
namespace StrokeInternal {
|
||||
|
||||
//
|
||||
// StrokeVertexIterator
|
||||
//
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
/** Class defining an iterator designed to iterate over the StrokeVertex of a Stroke.
|
||||
* An instance of a StrokeVertexIterator can only be obtained from a Stroke by calling
|
||||
* strokeVerticesBegin() or strokeVerticesEnd(). It is iterating over the same vertices as an
|
||||
* Interface0DIterator. The difference resides in the object access. Indeed, an Interface0DIterator
|
||||
* allows only an access to an Interface0D whereas we could need to access the specialized
|
||||
* StrokeVertex type. In this case, one should use a StrokeVertexIterator. The
|
||||
* castToInterface0DIterator() method is useful to get an Interface0DIterator from a
|
||||
* StrokeVertexIterator in order to call any functions of the type UnaryFunction0D. \attention In
|
||||
* the scripting language, you must call \code it2 = StrokeVertexIterator(it1) \endcode instead of
|
||||
* \code it2 = it1 \endcode where \a it1 and \a it2 are 2 StrokeVertexIterator.
|
||||
* Otherwise, incrementing \a it1 will also increment \a it2.
|
||||
*/
|
||||
class StrokeVertexIterator : public Interface0DIteratorNested {
|
||||
public:
|
||||
/** Default constructor. */
|
||||
StrokeVertexIterator() {}
|
||||
|
||||
/** Copy constructor. */
|
||||
StrokeVertexIterator(const StrokeVertexIterator &vi)
|
||||
{
|
||||
_it = vi._it;
|
||||
_begin = vi._begin;
|
||||
_end = vi._end;
|
||||
}
|
||||
|
||||
StrokeVertexIterator(const Stroke::vertex_container::iterator &it,
|
||||
const Stroke::vertex_container::iterator &begin,
|
||||
const Stroke::vertex_container::iterator &end)
|
||||
{
|
||||
_it = it;
|
||||
_begin = begin;
|
||||
_end = end;
|
||||
}
|
||||
|
||||
virtual ~StrokeVertexIterator() {}
|
||||
|
||||
/** Casts this StrokeVertexIterator into an Interface0DIterator.
|
||||
* Useful for any call to a function of the type UnaryFunction0D.
|
||||
*/
|
||||
inline Interface0DIterator castToInterface0DIterator() const
|
||||
{
|
||||
Interface0DIterator ret(new StrokeVertexIterator(*this));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** operator=
|
||||
* \attention In the scripting language, you must call \code it2 = StrokeVertexIterator(it1)
|
||||
* \endcode instead of \code it2 = it1 \endcode where \a it1 and \a it2 are 2
|
||||
* StrokeVertexIterator. Otherwise, incrementing \a it1 will also increment \a it2.
|
||||
*/
|
||||
StrokeVertexIterator &operator=(const StrokeVertexIterator &vi)
|
||||
{
|
||||
_it = vi._it;
|
||||
_begin = vi._begin;
|
||||
_end = vi._end;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Returns the string "StrokeVertexIterator". */
|
||||
virtual string getExactTypeName() const
|
||||
{
|
||||
return "StrokeVertexIterator";
|
||||
}
|
||||
|
||||
/** Returns a reference to the pointed StrokeVertex.
|
||||
* In the scripting language, you must call "getObject()"instead.
|
||||
*/
|
||||
virtual StrokeVertex &operator*()
|
||||
{
|
||||
return **_it;
|
||||
}
|
||||
|
||||
/** Returns a pointer to the pointed StrokeVertex.
|
||||
* Can't be called in the scripting language.
|
||||
*/
|
||||
virtual StrokeVertex *operator->()
|
||||
{
|
||||
return &(operator*());
|
||||
}
|
||||
|
||||
/** Increments. In the scripting language, call "increment()". */
|
||||
virtual StrokeVertexIterator &operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Increments. In the scripting language, call "increment()". */
|
||||
virtual StrokeVertexIterator operator++(int)
|
||||
{
|
||||
StrokeVertexIterator ret(*this);
|
||||
increment();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Decrements. In the scripting language, call "decrement()". */
|
||||
virtual StrokeVertexIterator &operator--()
|
||||
{
|
||||
decrement();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Decrements. In the scripting language, call "decrement()". */
|
||||
virtual StrokeVertexIterator operator--(int)
|
||||
{
|
||||
StrokeVertexIterator ret(*this);
|
||||
decrement();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Increments. */
|
||||
virtual int increment()
|
||||
{
|
||||
++_it;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Decrements. */
|
||||
virtual int decrement()
|
||||
{
|
||||
--_it;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Returns true if the pointed StrokeVertex is the first of the Stroke. */
|
||||
bool isBegin() const
|
||||
{
|
||||
return _it == _begin;
|
||||
}
|
||||
|
||||
/** Returns true if the pointed StrokeVertex is the final valid StrokeVertex of the Stroke. */
|
||||
bool atLast()
|
||||
{
|
||||
if (_it == _end) {
|
||||
return false;
|
||||
}
|
||||
|
||||
++_it;
|
||||
bool result = (_it == _end);
|
||||
--_it;
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 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
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,59 @@
|
||||
/* SPDX-FileCopyrightText: 2010-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class to define a layer of strokes.
|
||||
*/
|
||||
|
||||
#include "StrokeLayer.h"
|
||||
#include "Canvas.h"
|
||||
#include "Stroke.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
StrokeLayer::~StrokeLayer()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void StrokeLayer::ScaleThickness(float iFactor)
|
||||
{
|
||||
for (StrokeLayer::stroke_container::iterator s = _strokes.begin(), send = _strokes.end();
|
||||
s != send;
|
||||
++s)
|
||||
{
|
||||
(*s)->ScaleThickness(iFactor);
|
||||
}
|
||||
}
|
||||
|
||||
void StrokeLayer::Render(const StrokeRenderer *iRenderer)
|
||||
{
|
||||
for (StrokeLayer::stroke_container::iterator s = _strokes.begin(), send = _strokes.end();
|
||||
s != send;
|
||||
++s)
|
||||
{
|
||||
(*s)->Render(iRenderer);
|
||||
}
|
||||
}
|
||||
|
||||
void StrokeLayer::RenderBasic(const StrokeRenderer *iRenderer)
|
||||
{
|
||||
for (StrokeLayer::stroke_container::iterator s = _strokes.begin(), send = _strokes.end();
|
||||
s != send;
|
||||
++s)
|
||||
{
|
||||
(*s)->RenderBasic(iRenderer);
|
||||
}
|
||||
}
|
||||
|
||||
void StrokeLayer::clear()
|
||||
{
|
||||
for (stroke_container::iterator s = _strokes.begin(), send = _strokes.end(); s != send; ++s) {
|
||||
delete *s;
|
||||
}
|
||||
_strokes.clear();
|
||||
}
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,85 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class to define a layer of strokes.
|
||||
*/
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
class Stroke;
|
||||
class StrokeRenderer;
|
||||
class StrokeLayer {
|
||||
public:
|
||||
typedef std::deque<Stroke *> stroke_container;
|
||||
|
||||
protected:
|
||||
stroke_container _strokes;
|
||||
|
||||
public:
|
||||
StrokeLayer() {}
|
||||
|
||||
StrokeLayer(const stroke_container &iStrokes)
|
||||
{
|
||||
_strokes = iStrokes;
|
||||
}
|
||||
|
||||
StrokeLayer(const StrokeLayer &iBrother)
|
||||
{
|
||||
_strokes = iBrother._strokes;
|
||||
}
|
||||
|
||||
virtual ~StrokeLayer();
|
||||
|
||||
/** Render method */
|
||||
void ScaleThickness(float iFactor);
|
||||
void Render(const StrokeRenderer *iRenderer);
|
||||
void RenderBasic(const StrokeRenderer *iRenderer);
|
||||
|
||||
/** clears the layer */
|
||||
void clear();
|
||||
|
||||
/** accessors */
|
||||
inline stroke_container::iterator strokes_begin()
|
||||
{
|
||||
return _strokes.begin();
|
||||
}
|
||||
|
||||
inline stroke_container::iterator strokes_end()
|
||||
{
|
||||
return _strokes.end();
|
||||
}
|
||||
|
||||
inline int strokes_size() const
|
||||
{
|
||||
return _strokes.size();
|
||||
}
|
||||
|
||||
inline bool empty() const
|
||||
{
|
||||
return _strokes.empty();
|
||||
}
|
||||
|
||||
/** modifiers */
|
||||
inline void setStrokes(stroke_container &iStrokes)
|
||||
{
|
||||
_strokes = iStrokes;
|
||||
}
|
||||
|
||||
inline void AddStroke(Stroke *iStroke)
|
||||
{
|
||||
_strokes.push_back(iStroke);
|
||||
}
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:StrokeLayer")
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,112 @@
|
||||
/* SPDX-FileCopyrightText: 2008-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Classes to render a stroke with OpenGL
|
||||
*/
|
||||
|
||||
#include "StrokeRenderer.h"
|
||||
|
||||
#include "../geometry/GeomUtils.h"
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
/**********************************/
|
||||
/* */
|
||||
/* */
|
||||
/* StrokeRenderer */
|
||||
/* */
|
||||
/* */
|
||||
/**********************************/
|
||||
|
||||
TextureManager *StrokeRenderer::_textureManager = nullptr;
|
||||
|
||||
StrokeRenderer::~StrokeRenderer() = default;
|
||||
|
||||
bool StrokeRenderer::loadTextures()
|
||||
{
|
||||
_textureManager->load();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**********************************/
|
||||
/* */
|
||||
/* */
|
||||
/* TextureManager */
|
||||
/* */
|
||||
/* */
|
||||
/**********************************/
|
||||
|
||||
TextureManager *TextureManager::_pInstance = nullptr;
|
||||
|
||||
string TextureManager::_patterns_path;
|
||||
|
||||
string TextureManager::_brushes_path;
|
||||
|
||||
TextureManager::TextureManager()
|
||||
{
|
||||
_hasLoadedTextures = false;
|
||||
_pInstance = this;
|
||||
_defaultTextureId = 0;
|
||||
}
|
||||
|
||||
TextureManager::~TextureManager()
|
||||
{
|
||||
if (!_brushesMap.empty()) {
|
||||
_brushesMap.clear();
|
||||
}
|
||||
_pInstance = nullptr;
|
||||
}
|
||||
|
||||
void TextureManager::load()
|
||||
{
|
||||
if (_hasLoadedTextures) {
|
||||
return;
|
||||
}
|
||||
loadStandardBrushes();
|
||||
_hasLoadedTextures = true;
|
||||
}
|
||||
|
||||
uint TextureManager::getBrushTextureIndex(string name, Stroke::MediumType iType)
|
||||
{
|
||||
BrushTexture bt(name, iType);
|
||||
brushesMap::iterator b = _brushesMap.find(bt);
|
||||
if (b == _brushesMap.end()) {
|
||||
uint texId = loadBrush(name, iType);
|
||||
_brushesMap[bt] = texId;
|
||||
return texId;
|
||||
// XXX!
|
||||
cerr << "brush file " << name << " not found" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _brushesMap[bt];
|
||||
}
|
||||
|
||||
void TextureManager::Options::setPatternsPath(const string &path)
|
||||
{
|
||||
_patterns_path = path;
|
||||
}
|
||||
|
||||
string TextureManager::Options::getPatternsPath()
|
||||
{
|
||||
return _patterns_path;
|
||||
}
|
||||
|
||||
void TextureManager::Options::setBrushesPath(const string &path)
|
||||
{
|
||||
_brushes_path = path;
|
||||
}
|
||||
|
||||
string TextureManager::Options::getBrushesPath()
|
||||
{
|
||||
return _brushes_path;
|
||||
}
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,124 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Classes to render a stroke with OpenGL
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <string.h>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "Stroke.h"
|
||||
#include "StrokeRep.h"
|
||||
|
||||
#include "../system/FreestyleConfig.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
/**********************************/
|
||||
/* */
|
||||
/* */
|
||||
/* TextureManager */
|
||||
/* */
|
||||
/* */
|
||||
/**********************************/
|
||||
|
||||
/** Class to load textures */
|
||||
class TextureManager {
|
||||
public:
|
||||
TextureManager();
|
||||
virtual ~TextureManager();
|
||||
|
||||
static TextureManager *getInstance()
|
||||
{
|
||||
return _pInstance;
|
||||
}
|
||||
|
||||
void load();
|
||||
uint getBrushTextureIndex(string name, Stroke::MediumType iType = Stroke::OPAQUE_MEDIUM);
|
||||
|
||||
inline bool hasLoaded() const
|
||||
{
|
||||
return _hasLoadedTextures;
|
||||
}
|
||||
|
||||
inline uint getDefaultTextureId() const
|
||||
{
|
||||
return _defaultTextureId;
|
||||
}
|
||||
|
||||
struct Options {
|
||||
static void setPatternsPath(const string &path);
|
||||
static string getPatternsPath();
|
||||
|
||||
static void setBrushesPath(const string &path);
|
||||
static string getBrushesPath();
|
||||
};
|
||||
|
||||
protected:
|
||||
virtual void loadStandardBrushes() = 0;
|
||||
virtual uint 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, uint, cmpBrushTexture> brushesMap;
|
||||
|
||||
static TextureManager *_pInstance;
|
||||
bool _hasLoadedTextures;
|
||||
brushesMap _brushesMap;
|
||||
static string _patterns_path;
|
||||
static string _brushes_path;
|
||||
uint _defaultTextureId;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:TextureManager")
|
||||
};
|
||||
|
||||
/**********************************/
|
||||
/* */
|
||||
/* */
|
||||
/* StrokeRenderer */
|
||||
/* */
|
||||
/* */
|
||||
/**********************************/
|
||||
|
||||
/** Class to render a stroke. Creates a triangle strip and stores it strip is lazily created at the
|
||||
* first rendering */
|
||||
class StrokeRenderer {
|
||||
public:
|
||||
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 uint getTextureIndex(uint index);
|
||||
static TextureManager *_textureManager;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:StrokeRenderer")
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,874 @@
|
||||
/* SPDX-FileCopyrightText: 2008-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class to define the representation of a stroke (for display purpose)
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "Stroke.h"
|
||||
#include "StrokeAdvancedIterators.h"
|
||||
#include "StrokeIterators.h"
|
||||
#include "StrokeRenderer.h"
|
||||
#include "StrokeRep.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
//
|
||||
// STROKE VERTEX REP
|
||||
/////////////////////////////////////
|
||||
|
||||
StrokeVertexRep::StrokeVertexRep(const StrokeVertexRep &iBrother)
|
||||
{
|
||||
_point2d = iBrother._point2d;
|
||||
_texCoord = iBrother._texCoord;
|
||||
_texCoord_w_tips = iBrother._texCoord_w_tips;
|
||||
_color = iBrother._color;
|
||||
_alpha = iBrother._alpha;
|
||||
}
|
||||
|
||||
//
|
||||
// STRIP
|
||||
/////////////////////////////////////
|
||||
|
||||
Strip::Strip(const vector<StrokeVertex *> &iStrokeVertices,
|
||||
bool hasTex,
|
||||
bool tipBegin,
|
||||
bool tipEnd,
|
||||
float texStep)
|
||||
{
|
||||
createStrip(iStrokeVertices);
|
||||
|
||||
setVertexColor(iStrokeVertices);
|
||||
|
||||
if (hasTex) {
|
||||
// We compute both kinds of coordinates to use different kinds of textures
|
||||
computeTexCoord(iStrokeVertices, texStep);
|
||||
computeTexCoordWithTips(iStrokeVertices, tipBegin, tipEnd, texStep);
|
||||
}
|
||||
}
|
||||
|
||||
Strip::Strip(const Strip &iBrother)
|
||||
{
|
||||
if (!iBrother._vertices.empty()) {
|
||||
for (vertex_container::const_iterator v = iBrother._vertices.begin(),
|
||||
vend = iBrother._vertices.end();
|
||||
v != vend;
|
||||
++v)
|
||||
{
|
||||
_vertices.push_back(new StrokeVertexRep(**v));
|
||||
}
|
||||
}
|
||||
_averageThickness = iBrother._averageThickness;
|
||||
}
|
||||
|
||||
Strip::~Strip()
|
||||
{
|
||||
if (!_vertices.empty()) {
|
||||
for (vertex_container::iterator v = _vertices.begin(), vend = _vertices.end(); v != vend; ++v)
|
||||
{
|
||||
delete (*v);
|
||||
}
|
||||
_vertices.clear();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
// Strip creation
|
||||
//////////////////////////
|
||||
|
||||
#define EPS_SINGULARITY_RENDERER 0.05
|
||||
#define ZERO 0.00001
|
||||
#define MAX_RATIO_LENGTH_SINGU 2
|
||||
#define HUGE_COORD 1.0e4
|
||||
|
||||
static bool notValid(Vec2r p)
|
||||
{
|
||||
return (p[0] != p[0]) || (p[1] != p[1]) || (fabs(p[0]) > HUGE_COORD) ||
|
||||
(fabs(p[1]) > HUGE_COORD) || (p[0] < -HUGE_COORD) || (p[1] < -HUGE_COORD);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static real crossP(const Vec2r &A, const Vec2r &B)
|
||||
{
|
||||
return A[0] * B[1] - A[1] * B[0];
|
||||
}
|
||||
#endif
|
||||
|
||||
void Strip::createStrip(const vector<StrokeVertex *> &iStrokeVertices)
|
||||
{
|
||||
// computeParameterization();
|
||||
if (iStrokeVertices.size() < 2) {
|
||||
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
|
||||
cout << "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;
|
||||
int orientationErrors = 0;
|
||||
|
||||
// special case of first vertex
|
||||
v2 = v = iStrokeVertices.begin();
|
||||
++v2;
|
||||
sv = *v;
|
||||
vPrev = v; // in case the stroke has only 2 vertices;
|
||||
sv2 = *v2;
|
||||
Vec2r dir(sv2->getPoint() - sv->getPoint());
|
||||
Vec2r orthDir(-dir[1], dir[0]);
|
||||
if (orthDir.norm() > ZERO) {
|
||||
orthDir.normalize();
|
||||
}
|
||||
Vec2r stripDir(orthDir);
|
||||
// check whether the orientation was user defined
|
||||
if (sv->attribute().isAttributeAvailableVec2f("orientation")) {
|
||||
Vec2r userDir = sv->attribute().getAttributeVec2f("orientation");
|
||||
if (userDir.norm() > 1e-6) {
|
||||
userDir.normalize();
|
||||
real dp = userDir * orthDir;
|
||||
if (dp < 0) {
|
||||
userDir = userDir * (-1.0f);
|
||||
}
|
||||
stripDir = userDir;
|
||||
}
|
||||
else {
|
||||
++orientationErrors;
|
||||
}
|
||||
}
|
||||
const float *thickness = sv->attribute().getThickness();
|
||||
_vertices.push_back(new StrokeVertexRep(sv->getPoint() + thickness[1] * stripDir));
|
||||
_vertices.push_back(new StrokeVertexRep(sv->getPoint() - thickness[0] * stripDir));
|
||||
|
||||
#if 0
|
||||
Vec2r userDir = _stroke->getBeginningOrientation();
|
||||
if (userDir != Vec2r(0, 0)) {
|
||||
userDir.normalize();
|
||||
real o1 = (orthDir * userDir);
|
||||
real o2 = crossP(orthDir, userDir);
|
||||
real orientation = o1 * o2;
|
||||
if (orientation > 0) {
|
||||
// then the vertex to move is v0
|
||||
if (o1 > 0) {
|
||||
_vertex[0] = _vertex[1] + userDir;
|
||||
}
|
||||
else {
|
||||
_vertex[0] = _vertex[1] - userDir;
|
||||
}
|
||||
}
|
||||
if (orientation < 0) {
|
||||
// then we must move v1
|
||||
if (o1 < 0) {
|
||||
_vertex[1] = _vertex[0] + userDir;
|
||||
}
|
||||
else {
|
||||
_vertex[1] = _vertex[0] - userDir;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int i = 2; // 2 because we have already processed the first vertex
|
||||
|
||||
for (vend = iStrokeVertices.end(), ++v, ++v2; v2 != vend; vPrev = v++, ++v2) {
|
||||
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");
|
||||
if (userDir.norm() > 1e-6) {
|
||||
userDir.normalize();
|
||||
real dp = userDir * orthDir;
|
||||
if (dp < 0) {
|
||||
userDir = userDir * (-1.0f);
|
||||
}
|
||||
stripDir = userDir;
|
||||
}
|
||||
else {
|
||||
++orientationErrors;
|
||||
}
|
||||
}
|
||||
|
||||
// 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");
|
||||
if (userDir.norm() > 1e-6) {
|
||||
userDir.normalize();
|
||||
real dp = userDir * orthDir;
|
||||
if (dp < 0) {
|
||||
userDir = userDir * (-1.0f);
|
||||
}
|
||||
stripDirPrev = userDir;
|
||||
}
|
||||
else {
|
||||
++orientationErrors;
|
||||
}
|
||||
}
|
||||
|
||||
const float *thickness = sv->attribute().getThickness();
|
||||
_averageThickness += thickness[0] + thickness[1];
|
||||
Vec2r pInter;
|
||||
int interResult;
|
||||
|
||||
interResult = GeomUtils::intersect2dLine2dLine(Vec2r(pPrev + thickness[1] * stripDirPrev),
|
||||
Vec2r(p + thickness[1] * stripDirPrev),
|
||||
Vec2r(p + thickness[1] * stripDir),
|
||||
Vec2r(p2 + thickness[1] * stripDir),
|
||||
pInter);
|
||||
if (interResult == GeomUtils::DO_INTERSECT) {
|
||||
_vertices.push_back(new StrokeVertexRep(pInter));
|
||||
}
|
||||
else {
|
||||
_vertices.push_back(new StrokeVertexRep(p + thickness[1] * stripDir));
|
||||
}
|
||||
++i;
|
||||
|
||||
interResult = GeomUtils::intersect2dLine2dLine(Vec2r(pPrev - thickness[0] * stripDirPrev),
|
||||
Vec2r(p - thickness[0] * stripDirPrev),
|
||||
Vec2r(p - thickness[0] * stripDir),
|
||||
Vec2r(p2 - thickness[0] * stripDir),
|
||||
pInter);
|
||||
if (interResult == GeomUtils::DO_INTERSECT) {
|
||||
_vertices.push_back(new StrokeVertexRep(pInter));
|
||||
}
|
||||
else {
|
||||
_vertices.push_back(new StrokeVertexRep(p - thickness[0] * stripDir));
|
||||
}
|
||||
++i;
|
||||
|
||||
// if the angle is obtuse, we simply average the directions to avoid the singularity
|
||||
stripDir = stripDir + stripDirPrev;
|
||||
if ((dirNorm < ZERO) || (dirPrevNorm < ZERO) || (stripDir.norm() < ZERO)) {
|
||||
stripDir[0] = 0;
|
||||
stripDir[1] = 0;
|
||||
}
|
||||
else {
|
||||
stripDir.normalize();
|
||||
}
|
||||
|
||||
Vec2r vec_tmp(_vertices[i - 2]->point2d() - p);
|
||||
if ((vec_tmp.norm() > thickness[1] * MAX_RATIO_LENGTH_SINGU) || (dirNorm < ZERO) ||
|
||||
(dirPrevNorm < ZERO) || notValid(_vertices[i - 2]->point2d()) ||
|
||||
(fabs(stripDir * dir) < EPS_SINGULARITY_RENDERER))
|
||||
{
|
||||
_vertices[i - 2]->setPoint2d(p + thickness[1] * stripDir);
|
||||
}
|
||||
|
||||
vec_tmp = _vertices[i - 1]->point2d() - p;
|
||||
if ((vec_tmp.norm() > thickness[0] * MAX_RATIO_LENGTH_SINGU) || (dirNorm < ZERO) ||
|
||||
(dirPrevNorm < ZERO) || notValid(_vertices[i - 1]->point2d()) ||
|
||||
(fabs(stripDir * dir) < EPS_SINGULARITY_RENDERER))
|
||||
{
|
||||
_vertices[i - 1]->setPoint2d(p - thickness[0] * stripDir);
|
||||
}
|
||||
} // end of for
|
||||
|
||||
// special case of last vertex
|
||||
sv = *v;
|
||||
sv2 = *vPrev;
|
||||
dir = Vec2r(sv->getPoint() - sv2->getPoint());
|
||||
orthDir = Vec2r(-dir[1], dir[0]);
|
||||
if (orthDir.norm() > ZERO) {
|
||||
orthDir.normalize();
|
||||
}
|
||||
Vec2r stripDirLast(orthDir);
|
||||
// check whether the orientation was user defined
|
||||
if (sv->attribute().isAttributeAvailableVec2f("orientation")) {
|
||||
Vec2r userDir = sv->attribute().getAttributeVec2f("orientation");
|
||||
if (userDir.norm() > 1e-6) {
|
||||
userDir.normalize();
|
||||
real dp = userDir * orthDir;
|
||||
if (dp < 0) {
|
||||
userDir = userDir * (-1.0f);
|
||||
}
|
||||
stripDirLast = userDir;
|
||||
}
|
||||
else {
|
||||
++orientationErrors;
|
||||
}
|
||||
}
|
||||
const float *thicknessLast = sv->attribute().getThickness();
|
||||
_vertices.push_back(new StrokeVertexRep(sv->getPoint() + thicknessLast[1] * stripDirLast));
|
||||
++i;
|
||||
_vertices.push_back(new StrokeVertexRep(sv->getPoint() - thicknessLast[0] * stripDirLast));
|
||||
++i;
|
||||
|
||||
#if 0
|
||||
int n = i - 1;
|
||||
// check whether the orientation of the extremity was user defined
|
||||
userDir = _stroke->getEndingOrientation();
|
||||
if (userDir != Vec2r(0, 0)) {
|
||||
userDir.normalize();
|
||||
real o1 = (orthDir * userDir);
|
||||
real o2 = crossP(orthDir, userDir);
|
||||
real orientation = o1 * o2;
|
||||
if (orientation > 0) {
|
||||
// then the vertex to move is vn
|
||||
if (o1 < 0) {
|
||||
_vertex[n] = _vertex[n - 1] + userDir;
|
||||
}
|
||||
else {
|
||||
_vertex[n] = _vertex[n - 1] - userDir;
|
||||
}
|
||||
}
|
||||
if (orientation < 0) {
|
||||
// then we must move vn-1
|
||||
if (o1 > 0) {
|
||||
_vertex[n - 1] = _vertex[n] + userDir;
|
||||
}
|
||||
else {
|
||||
_vertex[n - 1] = _vertex[n] - userDir;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
_averageThickness /= float(iStrokeVertices.size() - 2);
|
||||
// I did not use the first and last vertex for the average
|
||||
if (iStrokeVertices.size() < 3) {
|
||||
_averageThickness = 0.5 * (thicknessLast[1] + thicknessLast[0] + thickness[0] + thickness[1]);
|
||||
}
|
||||
|
||||
if (orientationErrors > 0) {
|
||||
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
|
||||
cout << "Warning: " << orientationErrors
|
||||
<< " invalid zero-length orientation vector(s) found.\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (i != 2 * int(iStrokeVertices.size())) {
|
||||
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
|
||||
cout << "Warning: problem with stripe size\n";
|
||||
}
|
||||
}
|
||||
|
||||
cleanUpSingularities(iStrokeVertices);
|
||||
}
|
||||
|
||||
// CLEAN UP
|
||||
/////////////////////////
|
||||
|
||||
void Strip::cleanUpSingularities(const vector<StrokeVertex *> &iStrokeVertices)
|
||||
{
|
||||
int k;
|
||||
int sizeStrip = _vertices.size();
|
||||
|
||||
for (k = 0; k < sizeStrip; k++) {
|
||||
if (notValid(_vertices[k]->point2d())) {
|
||||
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
|
||||
cout << "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;
|
||||
StrokeVertex *sv, *sv2;
|
||||
|
||||
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; j++) {
|
||||
avP = Vec2r(avP + _vertices[2 * j]->point2d());
|
||||
}
|
||||
avP = Vec2r(1.0 / float(timeSinceSingu1 + 1) * avP);
|
||||
for (j = i - timeSinceSingu1; j <= i; 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; j++) {
|
||||
avP = Vec2r(avP + _vertices[2 * j + 1]->point2d());
|
||||
}
|
||||
avP = Vec2r(1.0 / float(timeSinceSingu2 + 1) * avP);
|
||||
for (j = i - timeSinceSingu2; j <= i; j++) {
|
||||
_vertices[2 * j + 1]->setPoint2d(avP);
|
||||
}
|
||||
//_vertex[2 * j + 1] = _vertex[2 * i + 1];
|
||||
singu2 = false;
|
||||
timeSinceSingu2 = 0;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (singu1) {
|
||||
// traverse all the vertices of the singularity and average them
|
||||
Vec2r avP(0.0, 0.0);
|
||||
for (j = i - timeSinceSingu1; j < i; j++) {
|
||||
avP = Vec2r(avP + _vertices[2 * j]->point2d());
|
||||
}
|
||||
avP = Vec2r(1.0 / float(timeSinceSingu1) * avP);
|
||||
for (j = i - timeSinceSingu1; j < i; j++) {
|
||||
_vertices[2 * j]->setPoint2d(avP);
|
||||
}
|
||||
}
|
||||
if (singu2) {
|
||||
// traverse all the vertices of the singularity and average them
|
||||
Vec2r avP(0.0, 0.0);
|
||||
for (j = i - timeSinceSingu2; j < i; j++) {
|
||||
avP = Vec2r(avP + _vertices[2 * j + 1]->point2d());
|
||||
}
|
||||
avP = Vec2r(1.0 / float(timeSinceSingu2) * avP);
|
||||
for (j = i - timeSinceSingu2; j < i; j++) {
|
||||
_vertices[2 * j + 1]->setPoint2d(avP);
|
||||
}
|
||||
}
|
||||
|
||||
for (k = 0; k < sizeStrip; k++) {
|
||||
if (notValid(_vertices[k]->point2d())) {
|
||||
if (blender::G.debug & blender::G_DEBUG_FREESTYLE) {
|
||||
cout << "Warning: strip vertex " << k << " non valid after cleanup" << endl;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Vertex color (RGBA)
|
||||
////////////////////////////////
|
||||
|
||||
void Strip::setVertexColor(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]->setColor(Vec3r(sv->attribute().getColorRGB()));
|
||||
_vertices[i]->setAlpha(sv->attribute().getAlpha());
|
||||
i++;
|
||||
_vertices[i]->setColor(Vec3r(sv->attribute().getColorRGB()));
|
||||
_vertices[i]->setAlpha(sv->attribute().getAlpha());
|
||||
i++;
|
||||
#if 0
|
||||
cerr << "col=(" << sv->attribute().getColor()[0] << ", " << sv->attribute().getColor()[1]
|
||||
<< ", " << sv->attribute().getColor()[2] << ")" << endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Texture coordinates
|
||||
////////////////////////////////
|
||||
|
||||
void Strip::computeTexCoord(const vector<StrokeVertex *> &iStrokeVertices, float texStep)
|
||||
{
|
||||
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 * texStep)), 0));
|
||||
i++;
|
||||
_vertices[i]->setTexCoord(
|
||||
Vec2r((real)(sv->curvilinearAbscissa() / (_averageThickness * texStep)), -1));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void Strip::computeTexCoordWithTips(const vector<StrokeVertex *> &iStrokeVertices,
|
||||
bool tipBegin,
|
||||
bool tipEnd,
|
||||
float texStep)
|
||||
{
|
||||
vector<StrokeVertex *>::const_iterator v, vend;
|
||||
StrokeVertex *sv = nullptr;
|
||||
StrokeVertexRep *tvRep[2] = {nullptr};
|
||||
|
||||
float l, fact, t;
|
||||
float u = 0, uPrev = 0;
|
||||
int tiles;
|
||||
int i = 0;
|
||||
float spacedThickness = _averageThickness * texStep;
|
||||
|
||||
v = iStrokeVertices.begin();
|
||||
vend = iStrokeVertices.end();
|
||||
l = (*v)->strokeLength() / spacedThickness;
|
||||
tiles = std::roundf(l); // round to the nearest
|
||||
fact = (float(tiles) + 0.5) / l;
|
||||
|
||||
#if 0
|
||||
cerr << "l=" << l << " tiles=" << tiles << " _averageThicnkess=" << _averageThickness
|
||||
<< " strokeLength=" << (*v)->strokeLength() << endl;
|
||||
#endif
|
||||
|
||||
vector<StrokeVertexRep *>::iterator currentSV = _vertices.begin();
|
||||
StrokeVertexRep *svRep;
|
||||
if (tipBegin) {
|
||||
for (; v != vend; v++) {
|
||||
sv = (*v);
|
||||
svRep = *currentSV;
|
||||
u = sv->curvilinearAbscissa() / spacedThickness * fact;
|
||||
if (u > 0.25) {
|
||||
break;
|
||||
}
|
||||
|
||||
svRep->setTexCoord(Vec2r((real)u, -0.5), true);
|
||||
i++;
|
||||
++currentSV;
|
||||
|
||||
svRep = *currentSV;
|
||||
svRep->setTexCoord(Vec2r((real)u, -1), true);
|
||||
i++;
|
||||
++currentSV;
|
||||
uPrev = u;
|
||||
}
|
||||
|
||||
if (v != vend && i >= 2) {
|
||||
// first transition vertex
|
||||
if (fabs(u - uPrev) > ZERO) {
|
||||
t = (0.25 - uPrev) / (u - uPrev);
|
||||
}
|
||||
else {
|
||||
t = 0;
|
||||
}
|
||||
for (int k = 0; k < 2; k++) {
|
||||
tvRep[k] = new StrokeVertexRep((1 - t) * _vertices[i - 2]->point2d() +
|
||||
t * _vertices[i]->point2d());
|
||||
tvRep[k]->setTexCoord((1 - t) * _vertices[i - 2]->texCoord() +
|
||||
t * _vertices[i]->texCoord());
|
||||
// v coord is -0.5 for tvRep[0], -1.0 for tvRep[1]
|
||||
tvRep[k]->setTexCoord(Vec2r(0.25, -0.5 * (k + 1)), true);
|
||||
tvRep[k]->setColor((1 - t) * _vertices[i - 2]->color() +
|
||||
t * Vec3r(sv->attribute().getColorRGB()));
|
||||
tvRep[k]->setAlpha((1 - t) * _vertices[i - 2]->alpha() + t * sv->attribute().getAlpha());
|
||||
i++;
|
||||
}
|
||||
for (int k = 0; k < 2; k++) {
|
||||
currentSV = _vertices.insert(currentSV, tvRep[k]);
|
||||
++currentSV;
|
||||
}
|
||||
|
||||
// copy the vertices with different texture coordinates
|
||||
for (int k = 0; k < 2; k++) {
|
||||
tvRep[k] = new StrokeVertexRep(*(_vertices[i - 2]));
|
||||
// v coord is 0.0 for tvRep[0], -0.5 for tvRep[1]
|
||||
tvRep[k]->setTexCoord(Vec2r(0.0, -0.5 * k), true);
|
||||
i++;
|
||||
}
|
||||
for (int k = 0; k < 2; k++) {
|
||||
currentSV = _vertices.insert(currentSV, tvRep[k]);
|
||||
++currentSV;
|
||||
}
|
||||
}
|
||||
}
|
||||
uPrev = 0;
|
||||
|
||||
// body of the stroke
|
||||
for (; v != vend; v++) {
|
||||
sv = (*v);
|
||||
svRep = *currentSV;
|
||||
u = sv->curvilinearAbscissa() / spacedThickness * fact - 0.25;
|
||||
if (u > tiles) {
|
||||
break;
|
||||
}
|
||||
|
||||
svRep->setTexCoord(Vec2r((real)u, 0), true);
|
||||
i++;
|
||||
++currentSV;
|
||||
|
||||
svRep = *currentSV;
|
||||
svRep->setTexCoord(Vec2r((real)u, -0.5), true);
|
||||
i++;
|
||||
++currentSV;
|
||||
|
||||
uPrev = u;
|
||||
}
|
||||
|
||||
if (tipEnd) {
|
||||
if (v != vend && i >= 2) {
|
||||
// second transition vertex
|
||||
if (fabs(u - uPrev) > ZERO) {
|
||||
t = (float(tiles) - uPrev) / (u - uPrev);
|
||||
}
|
||||
else {
|
||||
t = 0;
|
||||
}
|
||||
for (int k = 0; k < 2; k++) {
|
||||
tvRep[k] = new StrokeVertexRep((1 - t) * _vertices[i - 2]->point2d() +
|
||||
t * _vertices[i]->point2d());
|
||||
tvRep[k]->setTexCoord((1 - t) * _vertices[i - 2]->texCoord() +
|
||||
t * _vertices[i]->texCoord());
|
||||
// v coord is 0.0 for tvRep[0], -0.5 for tvRep[1]
|
||||
tvRep[k]->setTexCoord(Vec2r((real)tiles, -0.5 * k), true);
|
||||
tvRep[k]->setColor((1 - t) * _vertices[i - 2]->color() +
|
||||
t * Vec3r(sv->attribute().getColorRGB()));
|
||||
tvRep[k]->setAlpha((1 - t) * _vertices[i - 2]->alpha() + t * sv->attribute().getAlpha());
|
||||
i++;
|
||||
}
|
||||
for (int k = 0; k < 2; k++) {
|
||||
currentSV = _vertices.insert(currentSV, tvRep[k]);
|
||||
++currentSV;
|
||||
}
|
||||
|
||||
// copy the vertices with different texture coordinates
|
||||
for (int k = 0; k < 2; k++) {
|
||||
tvRep[k] = new StrokeVertexRep(*(_vertices[i - 2]));
|
||||
// v coord is -0.5 for tvRep[0], -1.0 for tvRep[1]
|
||||
tvRep[k]->setTexCoord(Vec2r(0.75, -0.5 * (k + 1)), true);
|
||||
i++;
|
||||
}
|
||||
for (int k = 0; k < 2; k++) {
|
||||
currentSV = _vertices.insert(currentSV, tvRep[k]);
|
||||
++currentSV;
|
||||
}
|
||||
}
|
||||
|
||||
// end tip
|
||||
for (; v != vend; v++) {
|
||||
sv = (*v);
|
||||
svRep = *currentSV;
|
||||
u = 0.75 + sv->curvilinearAbscissa() / spacedThickness * fact - float(tiles) - 0.25;
|
||||
|
||||
svRep->setTexCoord(Vec2r((real)u, -0.5), true);
|
||||
i++;
|
||||
++currentSV;
|
||||
|
||||
svRep = *currentSV;
|
||||
svRep->setTexCoord(Vec2r((real)u, -1), true);
|
||||
i++;
|
||||
++currentSV;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
cerr << "u=" << u << " i=" << i << "/" << _sizeStrip << endl;
|
||||
|
||||
for (i = 0; i < _sizeStrip; i++) {
|
||||
_alpha[i] = 1.0;
|
||||
}
|
||||
|
||||
for (i = 0; i < _sizeStrip; i++) {
|
||||
cerr << "(" << _texCoord[i][0] << ", " << _texCoord[i][1] << ") ";
|
||||
}
|
||||
cerr << endl;
|
||||
|
||||
Vec2r vec_tmp;
|
||||
for (i = 0; i < _sizeStrip / 2; i++) {
|
||||
vec_tmp = _vertex[2 * i] - _vertex[2 * i + 1];
|
||||
}
|
||||
if (vec_tmp.norm() > 4 * _averageThickness) {
|
||||
cerr << "Warning (from Fredo): There is a pb in the texture coordinates computation" << endl;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// StrokeRep
|
||||
/////////////////////////////////////
|
||||
|
||||
StrokeRep::StrokeRep()
|
||||
{
|
||||
_stroke = nullptr;
|
||||
_strokeType = Stroke::OPAQUE_MEDIUM;
|
||||
_nodeTree = nullptr;
|
||||
_hasTex = false;
|
||||
_textureStep = 1.0;
|
||||
for (int a = 0; a < MAX_MTEX; a++) {
|
||||
_mtex[a] = nullptr;
|
||||
}
|
||||
TextureManager *ptm = TextureManager::getInstance();
|
||||
if (ptm) {
|
||||
_textureId = ptm->getDefaultTextureId();
|
||||
}
|
||||
#if 0
|
||||
_averageTextureAlpha = 0.5; // default value
|
||||
if (_strokeType == OIL_STROKE) {
|
||||
_averageTextureAlpha = 0.75;
|
||||
}
|
||||
if (_strokeType >= NO_BLEND_STROKE) {
|
||||
_averageTextureAlpha = 1.0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
StrokeRep::StrokeRep(Stroke *iStroke)
|
||||
{
|
||||
_stroke = iStroke;
|
||||
_strokeType = iStroke->getMediumType();
|
||||
_nodeTree = iStroke->getNodeTree();
|
||||
_hasTex = iStroke->hasTex();
|
||||
_textureId = iStroke->getTextureId();
|
||||
_textureStep = iStroke->getTextureStep();
|
||||
for (int a = 0; a < MAX_MTEX; a++) {
|
||||
if (iStroke->getMTex(a)) {
|
||||
_mtex[a] = iStroke->getMTex(a);
|
||||
}
|
||||
else {
|
||||
_mtex[a] = nullptr;
|
||||
}
|
||||
}
|
||||
if (_textureId == 0) {
|
||||
TextureManager *ptm = TextureManager::getInstance();
|
||||
if (ptm) {
|
||||
_textureId = ptm->getDefaultTextureId();
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
_averageTextureAlpha = 0.5; // default value
|
||||
if (_strokeType == OIL_STROKE) {
|
||||
_averageTextureAlpha = 0.75;
|
||||
}
|
||||
if (_strokeType >= NO_BLEND_STROKE) {
|
||||
_averageTextureAlpha = 1.0;
|
||||
}
|
||||
#endif
|
||||
create();
|
||||
}
|
||||
|
||||
StrokeRep::StrokeRep(const StrokeRep &iBrother)
|
||||
{
|
||||
// soc unused - int i = 0;
|
||||
_stroke = iBrother._stroke;
|
||||
_strokeType = iBrother._strokeType;
|
||||
_textureId = iBrother._textureId;
|
||||
_textureStep = iBrother._textureStep;
|
||||
_nodeTree = iBrother._nodeTree;
|
||||
_hasTex = iBrother._hasTex;
|
||||
for (int a = 0; a < MAX_MTEX; a++) {
|
||||
if (iBrother._mtex[a]) {
|
||||
_mtex[a] = iBrother._mtex[a];
|
||||
}
|
||||
else {
|
||||
_mtex[a] = nullptr;
|
||||
}
|
||||
}
|
||||
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, _hasTex, first, end, _textureStep));
|
||||
strip.clear();
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
void StrokeRep::Render(const StrokeRenderer *iRenderer)
|
||||
{
|
||||
iRenderer->RenderStrokeRep(this);
|
||||
}
|
||||
|
||||
} /* namespace Freestyle */
|
||||
259
blender-5.2.0/source/blender/freestyle/intern/stroke/StrokeRep.h
Normal file
259
blender-5.2.0/source/blender/freestyle/intern/stroke/StrokeRep.h
Normal file
@@ -0,0 +1,259 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class to define the representation of a stroke (for display purpose)
|
||||
*/
|
||||
|
||||
#include "Stroke.h"
|
||||
|
||||
#include "../geometry/Geom.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_material_types.h" // for MAX_MTEX
|
||||
|
||||
namespace blender {
|
||||
struct bNodeTree;
|
||||
struct MTex;
|
||||
} // namespace blender
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
using namespace Geometry;
|
||||
|
||||
#if 0
|
||||
// symbolic constant to call the appropriate renderers and textures
|
||||
# define NO_TEXTURE_WITH_BLEND_STROKE -2
|
||||
# define NO_TEXTURE_STROKE -1
|
||||
# define PSEUDO_CHARCOAL_STROKE 0
|
||||
# define WASH_BRUSH_STROKE 1
|
||||
# define OIL_STROKE 2
|
||||
# define NO_BLEND_STROKE 3
|
||||
# define CHARCOAL_MIN_STROKE 4
|
||||
# define BRUSH_MIN_STROKE 5
|
||||
# define OPAQUE_DRY_STROKE 6
|
||||
# define OPAQUE_STROKE 7
|
||||
|
||||
# define DEFAULT_STROKE 0
|
||||
|
||||
# define NUMBER_STROKE_RENDERER 8
|
||||
|
||||
#endif
|
||||
|
||||
class StrokeVertexRep {
|
||||
public:
|
||||
StrokeVertexRep() {}
|
||||
|
||||
StrokeVertexRep(const Vec2r &iPoint2d)
|
||||
{
|
||||
_point2d = iPoint2d;
|
||||
}
|
||||
|
||||
StrokeVertexRep(const StrokeVertexRep &iBrother);
|
||||
|
||||
virtual ~StrokeVertexRep() {}
|
||||
|
||||
inline Vec2r &point2d()
|
||||
{
|
||||
return _point2d;
|
||||
}
|
||||
|
||||
inline Vec2r &texCoord(bool tips = false)
|
||||
{
|
||||
if (tips) {
|
||||
return _texCoord_w_tips;
|
||||
}
|
||||
else {
|
||||
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, bool tips = false)
|
||||
{
|
||||
if (tips) {
|
||||
_texCoord_w_tips = p;
|
||||
}
|
||||
else {
|
||||
_texCoord = p;
|
||||
}
|
||||
}
|
||||
|
||||
inline void setColor(const Vec3r &p)
|
||||
{
|
||||
_color = p;
|
||||
}
|
||||
|
||||
inline void setAlpha(float a)
|
||||
{
|
||||
_alpha = a;
|
||||
}
|
||||
|
||||
protected:
|
||||
Vec2r _point2d;
|
||||
Vec2r _texCoord;
|
||||
Vec2r _texCoord_w_tips;
|
||||
Vec3r _color;
|
||||
float _alpha;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:StrokeVertexRep")
|
||||
};
|
||||
|
||||
class Strip {
|
||||
public:
|
||||
typedef std::vector<StrokeVertexRep *> vertex_container;
|
||||
|
||||
protected:
|
||||
vertex_container _vertices;
|
||||
float _averageThickness;
|
||||
|
||||
public:
|
||||
Strip(const std::vector<StrokeVertex *> &iStrokeVertices,
|
||||
bool hasTex = false,
|
||||
bool tipBegin = false,
|
||||
bool tipEnd = false,
|
||||
float texStep = 1.0);
|
||||
Strip(const Strip &iBrother);
|
||||
virtual ~Strip();
|
||||
|
||||
protected:
|
||||
void createStrip(const std::vector<StrokeVertex *> &iStrokeVertices);
|
||||
void cleanUpSingularities(const std::vector<StrokeVertex *> &iStrokeVertices);
|
||||
void setVertexColor(const std::vector<StrokeVertex *> &iStrokeVertices);
|
||||
void computeTexCoord(const std::vector<StrokeVertex *> &iStrokeVertices, float texStep);
|
||||
void computeTexCoordWithTips(const std::vector<StrokeVertex *> &iStrokeVertices,
|
||||
bool tipBegin,
|
||||
bool tipEnd,
|
||||
float texStep);
|
||||
|
||||
public:
|
||||
inline int sizeStrip() const
|
||||
{
|
||||
return _vertices.size();
|
||||
}
|
||||
|
||||
inline vertex_container &vertices()
|
||||
{
|
||||
return _vertices;
|
||||
}
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:Strip")
|
||||
};
|
||||
|
||||
class StrokeRep {
|
||||
protected:
|
||||
Stroke *_stroke;
|
||||
vector<Strip *> _strips;
|
||||
Stroke::MediumType _strokeType;
|
||||
uint _textureId;
|
||||
float _textureStep;
|
||||
blender::MTex *_mtex[MAX_MTEX];
|
||||
blender::bNodeTree *_nodeTree;
|
||||
blender::Material *_material;
|
||||
bool _hasTex;
|
||||
|
||||
// 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 uint getTextureId() const
|
||||
{
|
||||
return _textureId;
|
||||
}
|
||||
|
||||
inline blender::MTex *getMTex(int idx) const
|
||||
{
|
||||
return _mtex[idx];
|
||||
}
|
||||
|
||||
inline blender::Material *getMaterial() const
|
||||
{
|
||||
return _material;
|
||||
}
|
||||
|
||||
inline blender::bNodeTree *getNodeTree() const
|
||||
{
|
||||
return _nodeTree;
|
||||
}
|
||||
|
||||
inline bool hasTex() const
|
||||
{
|
||||
return _hasTex;
|
||||
}
|
||||
|
||||
inline vector<Strip *> &getStrips()
|
||||
{
|
||||
return _strips;
|
||||
}
|
||||
|
||||
inline uint getNumberOfStrips() const
|
||||
{
|
||||
return _strips.size();
|
||||
}
|
||||
|
||||
inline Stroke *getStroke()
|
||||
{
|
||||
return _stroke;
|
||||
}
|
||||
|
||||
/** modifiers */
|
||||
inline void setMediumType(Stroke::MediumType itype)
|
||||
{
|
||||
_strokeType = itype;
|
||||
}
|
||||
|
||||
inline void setTextureId(uint textureId)
|
||||
{
|
||||
_textureId = textureId;
|
||||
}
|
||||
|
||||
inline void setMaterial(blender::Material *mat)
|
||||
{
|
||||
_material = mat;
|
||||
}
|
||||
#if 0
|
||||
inline void setMTex(int idx, blender::MTex *mtex_ptr)
|
||||
{
|
||||
_mtex[idx] = mtex_ptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:StrokeRep")
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,20 @@
|
||||
/* SPDX-FileCopyrightText: 2012-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
*/
|
||||
|
||||
#include "StrokeShader.h"
|
||||
|
||||
#include "../python/Director.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
int StrokeShader::shade(Stroke &ioStroke) const
|
||||
{
|
||||
return Director_BPy_StrokeShader_shade(const_cast<StrokeShader *>(this), ioStroke);
|
||||
}
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,79 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class defining StrokeShader
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
//
|
||||
// StrokeShader base class
|
||||
//
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
class Stroke;
|
||||
|
||||
/** Base class for Stroke Shaders.
|
||||
* Any Stroke Shader must inherit from this class and overload the shade() method.
|
||||
* A StrokeShader is designed to modify any Stroke's attribute such as Thickness, Color,
|
||||
* Geometry, Texture, Blending mode...
|
||||
* The basic way to achieve this operation consists in iterating over the StrokeVertices of the
|
||||
* Stroke and to modify each one's StrokeAttribute. Here is a python code example of such an
|
||||
* iteration: \code it = ioStroke.strokeVerticesBegin() while not it.isEnd(): att =
|
||||
* it.getObject().attribute()
|
||||
* ## perform here any attribute modification
|
||||
* it.increment()
|
||||
* \endcode
|
||||
* Here is a C++ code example of such an iteration:
|
||||
* \code
|
||||
* for (StrokeInternal::StrokeVertexIterator v = ioStroke.strokeVerticesBegin(), vend =
|
||||
* ioStroke.strokeVerticesEnd(); v != vend;
|
||||
* ++v)
|
||||
* {
|
||||
* StrokeAttribute& att = v->attribute();
|
||||
* // perform any attribute modification here...
|
||||
* }
|
||||
* \endcode
|
||||
*/
|
||||
class StrokeShader {
|
||||
public:
|
||||
void *py_ss;
|
||||
|
||||
/** Default constructor. */
|
||||
StrokeShader()
|
||||
{
|
||||
py_ss = 0;
|
||||
}
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~StrokeShader() {}
|
||||
|
||||
/** Returns the string corresponding to the shader's name. */
|
||||
virtual string getName() const
|
||||
{
|
||||
return "StrokeShader";
|
||||
}
|
||||
|
||||
/** The shading method. This method must be overloaded by inherited classes.
|
||||
* \param ioStroke:
|
||||
* The stroke we wish to shade. this Stroke is modified by the Shader (which typically
|
||||
* modifies the Stroke's attribute's values such as Color, Thickness, Geometry...)
|
||||
*/
|
||||
virtual int shade(Stroke &ioStroke) const;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:StrokeShader")
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,75 @@
|
||||
/* SPDX-FileCopyrightText: 2008-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class to build a Node Tree designed to be displayed from a set of strokes structure.
|
||||
*/
|
||||
|
||||
#include "StrokeTesselator.h"
|
||||
#include "StrokeAdvancedIterators.h"
|
||||
|
||||
#include "../scene_graph/NodeGroup.h"
|
||||
#include "../scene_graph/NodeShape.h"
|
||||
#include "../scene_graph/OrientedLineRep.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
LineRep *StrokeTesselator::Tesselate(Stroke *iStroke)
|
||||
{
|
||||
if (nullptr == iStroke) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LineRep *line;
|
||||
line = new OrientedLineRep();
|
||||
|
||||
Stroke::vertex_iterator v, vend;
|
||||
if (2 == iStroke->vertices_size()) {
|
||||
line->setStyle(LineRep::LINES);
|
||||
v = iStroke->vertices_begin();
|
||||
StrokeVertex *svA = (*v);
|
||||
v++;
|
||||
StrokeVertex *svB = (*v);
|
||||
Vec3r A((*svA)[0], (*svA)[1], 0);
|
||||
Vec3r B((*svB)[0], (*svB)[1], 0);
|
||||
line->AddVertex(A);
|
||||
line->AddVertex(B);
|
||||
}
|
||||
else {
|
||||
if (_overloadFrsMaterial) {
|
||||
line->setFrsMaterial(_FrsMaterial);
|
||||
}
|
||||
|
||||
line->setStyle(LineRep::LINE_STRIP);
|
||||
|
||||
for (v = iStroke->vertices_begin(), vend = iStroke->vertices_end(); v != vend; v++) {
|
||||
StrokeVertex *sv = (*v);
|
||||
Vec3r V((*sv)[0], (*sv)[1], 0);
|
||||
line->AddVertex(V);
|
||||
}
|
||||
}
|
||||
line->setId(iStroke->getId());
|
||||
line->ComputeBBox();
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
template<class StrokeVertexIterator>
|
||||
NodeGroup *StrokeTesselator::Tesselate(StrokeVertexIterator begin, StrokeVertexIterator end)
|
||||
{
|
||||
NodeGroup *group = new NodeGroup;
|
||||
NodeShape *tshape = new NodeShape;
|
||||
group->AddChild(tshape);
|
||||
// tshape->material().setDiffuse(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
tshape->setFrsMaterial(_FrsMaterial);
|
||||
|
||||
for (StrokeVertexIterator c = begin, cend = end; c != cend; c++) {
|
||||
tshape->AddRep(Tesselate(*c));
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,56 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class to build a Node Tree designed to be displayed from a set of strokes structure.
|
||||
*/
|
||||
|
||||
#include "Stroke.h"
|
||||
|
||||
#include "../scene_graph/LineRep.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
class StrokeTesselator {
|
||||
public:
|
||||
inline StrokeTesselator()
|
||||
{
|
||||
_FrsMaterial.setDiffuse(0, 0, 0, 1);
|
||||
_overloadFrsMaterial = false;
|
||||
}
|
||||
|
||||
virtual ~StrokeTesselator() {}
|
||||
|
||||
/** Builds a line rep contained from a Stroke */
|
||||
LineRep *Tesselate(Stroke *iStroke);
|
||||
|
||||
/** Builds a set of lines rep contained under a NodeShape, itself contained under a NodeGroup
|
||||
* from a set of strokes.
|
||||
*/
|
||||
template<class StrokeIterator> NodeGroup *Tesselate(StrokeIterator begin, StrokeIterator end);
|
||||
|
||||
inline void setFrsMaterial(const FrsMaterial &iMaterial)
|
||||
{
|
||||
_FrsMaterial = iMaterial;
|
||||
_overloadFrsMaterial = true;
|
||||
}
|
||||
|
||||
inline const FrsMaterial &frs_material() const
|
||||
{
|
||||
return _FrsMaterial;
|
||||
}
|
||||
|
||||
private:
|
||||
FrsMaterial _FrsMaterial;
|
||||
bool _overloadFrsMaterial;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:StrokeTesselator")
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,166 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
* \brief Class representing a style module
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "Operators.h"
|
||||
#include "StrokeLayer.h"
|
||||
#include "StrokeShader.h"
|
||||
|
||||
#include "../system/Interpreter.h"
|
||||
#include "../system/StringUtils.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
class StyleModule {
|
||||
public:
|
||||
StyleModule(const string &file_name, Interpreter *inter) : _file_name(file_name)
|
||||
{
|
||||
_always_refresh = false;
|
||||
_causal = false;
|
||||
_drawable = true;
|
||||
_modified = true;
|
||||
_displayed = true;
|
||||
_inter = inter;
|
||||
}
|
||||
|
||||
virtual ~StyleModule() {}
|
||||
|
||||
StrokeLayer *execute()
|
||||
{
|
||||
if (!_inter) {
|
||||
cerr << "Error: no interpreter was found to execute the script" << endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!_drawable) {
|
||||
cerr << "Error: not drawable" << endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Operators::reset();
|
||||
|
||||
if (interpret()) {
|
||||
cerr << "Error: interpretation failed" << endl;
|
||||
Operators::reset();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Operators::StrokesContainer *strokes_set = Operators::getStrokesSet();
|
||||
if (strokes_set->empty()) {
|
||||
cerr << "Error: strokes set empty" << endl;
|
||||
Operators::reset();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
StrokeLayer *sl = new StrokeLayer;
|
||||
for (Operators::StrokesContainer::iterator it = strokes_set->begin(); it != strokes_set->end();
|
||||
++it)
|
||||
{
|
||||
sl->AddStroke(*it);
|
||||
}
|
||||
|
||||
Operators::reset();
|
||||
return sl;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual int interpret()
|
||||
{
|
||||
return _inter->interpretFile(_file_name);
|
||||
}
|
||||
|
||||
public:
|
||||
// accessors
|
||||
const string getFileName() const
|
||||
{
|
||||
return _file_name;
|
||||
}
|
||||
|
||||
bool getAlwaysRefresh() const
|
||||
{
|
||||
return _always_refresh;
|
||||
}
|
||||
|
||||
bool getCausal() const
|
||||
{
|
||||
return _causal;
|
||||
}
|
||||
|
||||
bool getDrawable() const
|
||||
{
|
||||
return _drawable;
|
||||
}
|
||||
|
||||
bool getModified() const
|
||||
{
|
||||
return _modified;
|
||||
}
|
||||
|
||||
bool getDisplayed() const
|
||||
{
|
||||
return _displayed;
|
||||
}
|
||||
|
||||
// modifiers
|
||||
void setFileName(const string &file_name)
|
||||
{
|
||||
_file_name = file_name;
|
||||
}
|
||||
|
||||
void setAlwaysRefresh(bool b = true)
|
||||
{
|
||||
_always_refresh = b;
|
||||
}
|
||||
|
||||
void setCausal(bool b = true)
|
||||
{
|
||||
_causal = b;
|
||||
}
|
||||
|
||||
void setDrawable(bool b = true)
|
||||
{
|
||||
_drawable = b;
|
||||
}
|
||||
|
||||
void setModified(bool b = true)
|
||||
{
|
||||
if (_always_refresh) {
|
||||
return;
|
||||
}
|
||||
_modified = b;
|
||||
}
|
||||
|
||||
void setDisplayed(bool b = true)
|
||||
{
|
||||
_displayed = b;
|
||||
}
|
||||
|
||||
private:
|
||||
string _file_name;
|
||||
bool _always_refresh;
|
||||
bool _causal;
|
||||
bool _drawable;
|
||||
bool _modified;
|
||||
bool _displayed;
|
||||
|
||||
protected:
|
||||
Interpreter *_inter;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:StyleModule")
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,54 @@
|
||||
/* SPDX-FileCopyrightText: 2012-2022 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "TextStrokeRenderer.h"
|
||||
#include "Canvas.h"
|
||||
#include "StrokeIterators.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
TextStrokeRenderer::TextStrokeRenderer(const char *iFileName)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} /* namespace Freestyle */
|
||||
@@ -0,0 +1,52 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup freestyle
|
||||
*/
|
||||
|
||||
//
|
||||
// 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
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "StrokeRenderer.h"
|
||||
|
||||
#include "../system/FreestyleConfig.h"
|
||||
|
||||
namespace Freestyle {
|
||||
|
||||
/**********************************/
|
||||
/* */
|
||||
/* */
|
||||
/* TextStrokeRenderer */
|
||||
/* */
|
||||
/* */
|
||||
/**********************************/
|
||||
|
||||
class TextStrokeRenderer : public StrokeRenderer {
|
||||
public:
|
||||
TextStrokeRenderer(const char *iFileName = nullptr);
|
||||
|
||||
/** Renders a stroke rep */
|
||||
virtual void RenderStrokeRep(StrokeRep *iStrokeRep) const;
|
||||
virtual void RenderStrokeRepBasic(StrokeRep *iStrokeRep) const;
|
||||
|
||||
protected:
|
||||
mutable ofstream _ofstream;
|
||||
};
|
||||
|
||||
} /* namespace Freestyle */
|
||||
Reference in New Issue
Block a user