Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -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

View File

@@ -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 */

View File

@@ -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

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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

View File

@@ -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 */

View 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 */

View 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 */

View 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 */

View 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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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

View File

@@ -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 */

File diff suppressed because it is too large Load Diff

View 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 */

View File

@@ -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.
* Thats 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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

File diff suppressed because it is too large Load Diff

View 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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

File diff suppressed because it is too large Load Diff

View 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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View 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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */