Listing the "Blender Foundation" as copyright holder implied the Blender Foundation holds copyright to files which may include work from many developers. While keeping copyright on headers makes sense for isolated libraries, Blender's own code may be refactored or moved between files in a way that makes the per file copyright holders less meaningful. Copyright references to the "Blender Foundation" have been replaced with "Blender Authors", with the exception of `./extern/` since these this contains libraries which are more isolated, any changed to license headers there can be handled on a case-by-case basis. Some directories in `./intern/` have also been excluded: - `./intern/cycles/` it's own `AUTHORS` file is planned. - `./intern/opensubdiv/`. An "AUTHORS" file has been added, using the chromium projects authors file as a template. Design task: #110784 Ref !110783.
1017 lines
25 KiB
C++
1017 lines
25 KiB
C++
/* SPDX-FileCopyrightText: 2008-2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup freestyle
|
|
* \brief Classes to define a stroke
|
|
*/
|
|
|
|
#include "Stroke.h"
|
|
#include "StrokeAdvancedIterators.h"
|
|
#include "StrokeIterators.h"
|
|
#include "StrokeRenderer.h"
|
|
|
|
#include "BKE_global.h"
|
|
#include "BKE_node.hh"
|
|
|
|
namespace Freestyle {
|
|
|
|
/**********************************/
|
|
/* */
|
|
/* */
|
|
/* StrokeAttribute */
|
|
/* */
|
|
/* */
|
|
/**********************************/
|
|
|
|
StrokeAttribute::StrokeAttribute()
|
|
{
|
|
int i;
|
|
_alpha = 1.0f;
|
|
_thickness[0] = 1.0f;
|
|
_thickness[1] = 1.0f;
|
|
for (i = 0; i < 3; ++i) {
|
|
_color[i] = 0.2f;
|
|
}
|
|
_color[0] = 0.8f;
|
|
_userAttributesReal = nullptr;
|
|
_userAttributesVec2f = nullptr;
|
|
_userAttributesVec3f = nullptr;
|
|
_visible = true;
|
|
}
|
|
|
|
StrokeAttribute::StrokeAttribute(const StrokeAttribute &iBrother)
|
|
{
|
|
_alpha = iBrother._alpha;
|
|
_thickness[0] = iBrother._thickness[0];
|
|
_thickness[1] = iBrother._thickness[1];
|
|
for (int i = 0; i < 3; ++i) {
|
|
_color[i] = iBrother._color[i];
|
|
}
|
|
_visible = iBrother._visible;
|
|
if (iBrother._userAttributesReal) {
|
|
_userAttributesReal = new realMap(*iBrother._userAttributesReal);
|
|
}
|
|
else {
|
|
_userAttributesReal = nullptr;
|
|
}
|
|
if (iBrother._userAttributesVec2f) {
|
|
_userAttributesVec2f = new Vec2fMap(*iBrother._userAttributesVec2f);
|
|
}
|
|
else {
|
|
_userAttributesVec2f = nullptr;
|
|
}
|
|
if (iBrother._userAttributesVec3f) {
|
|
_userAttributesVec3f = new Vec3fMap(*iBrother._userAttributesVec3f);
|
|
}
|
|
else {
|
|
_userAttributesVec3f = nullptr;
|
|
}
|
|
}
|
|
|
|
StrokeAttribute::StrokeAttribute(float iRColor,
|
|
float iGColor,
|
|
float iBColor,
|
|
float iAlpha,
|
|
float iRThickness,
|
|
float iLThickness)
|
|
{
|
|
_color[0] = iRColor;
|
|
_color[1] = iGColor;
|
|
_color[2] = iBColor;
|
|
|
|
_alpha = iAlpha;
|
|
|
|
_thickness[0] = iRThickness;
|
|
_thickness[1] = iLThickness;
|
|
|
|
_visible = true;
|
|
|
|
_userAttributesReal = nullptr;
|
|
_userAttributesVec2f = nullptr;
|
|
_userAttributesVec3f = nullptr;
|
|
}
|
|
|
|
StrokeAttribute::StrokeAttribute(const StrokeAttribute &a1, const StrokeAttribute &a2, float t)
|
|
{
|
|
_alpha = (1 - t) * a1._alpha + t * a2._alpha;
|
|
_thickness[0] = (1 - t) * a1._thickness[0] + t * a2._thickness[0];
|
|
_thickness[1] = (1 - t) * a1._thickness[1] + t * a2._thickness[1];
|
|
for (int i = 0; i < 3; ++i) {
|
|
_color[i] = (1 - t) * a1._color[i] + t * a2._color[i];
|
|
}
|
|
|
|
_visible = a1.isVisible();
|
|
|
|
// FIXME: to be checked (and enhanced)
|
|
if ((a1._userAttributesReal) && (a2._userAttributesReal)) {
|
|
if (a1._userAttributesReal->size() == a2._userAttributesReal->size()) {
|
|
_userAttributesReal = new realMap;
|
|
realMap::iterator it1 = a1._userAttributesReal->begin(),
|
|
it1end = a1._userAttributesReal->end();
|
|
realMap::iterator it2 = a2._userAttributesReal->begin();
|
|
for (; it1 != it1end; ++it1, ++it2) {
|
|
(*_userAttributesReal)[(*it1).first] = ((1 - t) * (*it1).second + t * (*it2).second);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
_userAttributesReal = nullptr;
|
|
}
|
|
if ((a1._userAttributesVec2f) && (a2._userAttributesVec2f)) {
|
|
if (a1._userAttributesVec2f->size() == a2._userAttributesVec2f->size()) {
|
|
_userAttributesVec2f = new Vec2fMap;
|
|
Vec2fMap::iterator it1 = a1._userAttributesVec2f->begin(),
|
|
it1end = a1._userAttributesVec2f->end();
|
|
Vec2fMap::iterator it2 = a2._userAttributesVec2f->begin();
|
|
for (; it1 != it1end; ++it1, ++it2) {
|
|
(*_userAttributesVec2f)[(*it1).first] = ((1 - t) * (*it1).second + t * (*it2).second);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
_userAttributesVec2f = nullptr;
|
|
}
|
|
if ((a1._userAttributesVec3f) && (a2._userAttributesVec3f)) {
|
|
if (a1._userAttributesVec3f->size() == a2._userAttributesVec3f->size()) {
|
|
_userAttributesVec3f = new Vec3fMap;
|
|
Vec3fMap::iterator it1 = a1._userAttributesVec3f->begin(),
|
|
it1end = a1._userAttributesVec3f->end();
|
|
Vec3fMap::iterator it2 = a2._userAttributesVec3f->begin();
|
|
for (; it1 != it1end; ++it1, ++it2) {
|
|
(*_userAttributesVec3f)[(*it1).first] = ((1 - t) * (*it1).second + t * (*it2).second);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
_userAttributesVec3f = nullptr;
|
|
}
|
|
}
|
|
|
|
StrokeAttribute::~StrokeAttribute()
|
|
{
|
|
if (_userAttributesReal) {
|
|
_userAttributesReal->clear();
|
|
delete _userAttributesReal;
|
|
}
|
|
if (_userAttributesVec2f) {
|
|
_userAttributesVec2f->clear();
|
|
delete _userAttributesVec2f;
|
|
}
|
|
if (_userAttributesVec3f) {
|
|
_userAttributesVec3f->clear();
|
|
delete _userAttributesVec3f;
|
|
}
|
|
}
|
|
|
|
StrokeAttribute &StrokeAttribute::operator=(const StrokeAttribute &iBrother)
|
|
{
|
|
int i;
|
|
_alpha = iBrother._alpha;
|
|
_thickness[0] = iBrother._thickness[0];
|
|
_thickness[1] = iBrother._thickness[1];
|
|
for (i = 0; i < 3; ++i) {
|
|
_color[i] = iBrother._color[i];
|
|
}
|
|
_visible = iBrother._visible;
|
|
if (iBrother._userAttributesReal) {
|
|
if (!_userAttributesReal) {
|
|
_userAttributesReal = new realMap;
|
|
}
|
|
_userAttributesReal = new realMap(*(iBrother._userAttributesReal));
|
|
}
|
|
else {
|
|
_userAttributesReal = nullptr;
|
|
}
|
|
if (iBrother._userAttributesVec2f) {
|
|
if (!_userAttributesVec2f) {
|
|
_userAttributesVec2f = new Vec2fMap;
|
|
}
|
|
_userAttributesVec2f = new Vec2fMap(*(iBrother._userAttributesVec2f));
|
|
}
|
|
else {
|
|
_userAttributesVec2f = nullptr;
|
|
}
|
|
if (iBrother._userAttributesVec3f) {
|
|
if (!_userAttributesVec3f) {
|
|
_userAttributesVec3f = new Vec3fMap;
|
|
}
|
|
_userAttributesVec3f = new Vec3fMap(*(iBrother._userAttributesVec3f));
|
|
}
|
|
else {
|
|
_userAttributesVec3f = nullptr;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
float StrokeAttribute::getAttributeReal(const char *iName) const
|
|
{
|
|
if (!_userAttributesReal) {
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "StrokeAttribute warning: no real attribute was defined" << endl;
|
|
}
|
|
return 0.0f;
|
|
}
|
|
realMap::iterator a = _userAttributesReal->find(iName);
|
|
if (a == _userAttributesReal->end()) {
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "StrokeAttribute warning: no real attribute was added with the name " << iName
|
|
<< endl;
|
|
}
|
|
return 0.0f;
|
|
}
|
|
return (*a).second;
|
|
}
|
|
|
|
Vec2f StrokeAttribute::getAttributeVec2f(const char *iName) const
|
|
{
|
|
if (!_userAttributesVec2f) {
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "StrokeAttribute warning: no Vec2f attribute was defined" << endl;
|
|
}
|
|
return 0;
|
|
}
|
|
Vec2fMap::iterator a = _userAttributesVec2f->find(iName);
|
|
if (a == _userAttributesVec2f->end()) {
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "StrokeAttribute warning: no Vec2f attribute was added with the name " << iName
|
|
<< endl;
|
|
}
|
|
return 0;
|
|
}
|
|
return (*a).second;
|
|
}
|
|
|
|
Vec3f StrokeAttribute::getAttributeVec3f(const char *iName) const
|
|
{
|
|
if (!_userAttributesVec3f) {
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "StrokeAttribute warning: no Vec3f attribute was defined" << endl;
|
|
}
|
|
return 0;
|
|
}
|
|
Vec3fMap::iterator a = _userAttributesVec3f->find(iName);
|
|
if (a == _userAttributesVec3f->end()) {
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "StrokeAttribute warning: no Vec3f attribute was added with the name " << iName
|
|
<< endl;
|
|
}
|
|
return 0;
|
|
}
|
|
return (*a).second;
|
|
}
|
|
|
|
bool StrokeAttribute::isAttributeAvailableReal(const char *iName) const
|
|
{
|
|
if (!_userAttributesReal) {
|
|
return false;
|
|
}
|
|
realMap::iterator a = _userAttributesReal->find(iName);
|
|
if (a == _userAttributesReal->end()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool StrokeAttribute::isAttributeAvailableVec2f(const char *iName) const
|
|
{
|
|
if (!_userAttributesVec2f) {
|
|
return false;
|
|
}
|
|
Vec2fMap::iterator a = _userAttributesVec2f->find(iName);
|
|
if (a == _userAttributesVec2f->end()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool StrokeAttribute::isAttributeAvailableVec3f(const char *iName) const
|
|
{
|
|
if (!_userAttributesVec3f) {
|
|
return false;
|
|
}
|
|
Vec3fMap::iterator a = _userAttributesVec3f->find(iName);
|
|
if (a == _userAttributesVec3f->end()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void StrokeAttribute::setAttributeReal(const char *iName, float att)
|
|
{
|
|
if (!_userAttributesReal) {
|
|
_userAttributesReal = new realMap;
|
|
}
|
|
(*_userAttributesReal)[iName] = att;
|
|
}
|
|
|
|
void StrokeAttribute::setAttributeVec2f(const char *iName, const Vec2f &att)
|
|
{
|
|
if (!_userAttributesVec2f) {
|
|
_userAttributesVec2f = new Vec2fMap;
|
|
}
|
|
(*_userAttributesVec2f)[iName] = att;
|
|
}
|
|
|
|
void StrokeAttribute::setAttributeVec3f(const char *iName, const Vec3f &att)
|
|
{
|
|
if (!_userAttributesVec3f) {
|
|
_userAttributesVec3f = new Vec3fMap;
|
|
}
|
|
(*_userAttributesVec3f)[iName] = att;
|
|
}
|
|
|
|
/**********************************/
|
|
/* */
|
|
/* */
|
|
/* StrokeVertex */
|
|
/* */
|
|
/* */
|
|
/**********************************/
|
|
|
|
StrokeVertex::StrokeVertex()
|
|
{
|
|
_CurvilignAbscissa = 0.0f;
|
|
_StrokeLength = 0.0f;
|
|
}
|
|
|
|
StrokeVertex::StrokeVertex(const StrokeVertex &iBrother) : CurvePoint(iBrother)
|
|
{
|
|
_Attribute = iBrother._Attribute;
|
|
_CurvilignAbscissa = 0.0f;
|
|
_StrokeLength = 0.0f;
|
|
}
|
|
|
|
StrokeVertex::StrokeVertex(SVertex *iSVertex) : CurvePoint(iSVertex, nullptr, 0.0f)
|
|
{
|
|
_CurvilignAbscissa = 0.0f;
|
|
_StrokeLength = 0.0f;
|
|
}
|
|
|
|
StrokeVertex::StrokeVertex(CurvePoint *iPoint) : CurvePoint(*iPoint)
|
|
{
|
|
_CurvilignAbscissa = 0.0f;
|
|
_StrokeLength = 0.0f;
|
|
}
|
|
|
|
StrokeVertex::StrokeVertex(StrokeVertex *iA, StrokeVertex *iB, float t3) : CurvePoint(iA, iB, t3)
|
|
{
|
|
// interpolate attributes:
|
|
_Attribute = StrokeAttribute(iA->attribute(), iB->attribute(), t3);
|
|
_CurvilignAbscissa = (1 - t3) * iA->curvilinearAbscissa() + t3 * iB->curvilinearAbscissa();
|
|
_StrokeLength = iA->strokeLength();
|
|
}
|
|
|
|
StrokeVertex::StrokeVertex(SVertex *iSVertex, const StrokeAttribute &iAttribute)
|
|
: CurvePoint(iSVertex, nullptr, 0.0f)
|
|
{
|
|
_Attribute = iAttribute;
|
|
_CurvilignAbscissa = 0.0f;
|
|
_StrokeLength = 0.0f;
|
|
}
|
|
|
|
StrokeVertex &StrokeVertex::operator=(const StrokeVertex &iBrother)
|
|
{
|
|
((CurvePoint *)this)->operator=(iBrother);
|
|
_Attribute = iBrother._Attribute;
|
|
|
|
_CurvilignAbscissa = 0.0f;
|
|
|
|
_StrokeLength = 0.0f;
|
|
return *this;
|
|
}
|
|
|
|
/**********************************/
|
|
/* */
|
|
/* */
|
|
/* Stroke */
|
|
/* */
|
|
/* */
|
|
/**********************************/
|
|
|
|
Stroke::Stroke()
|
|
{
|
|
_Length = 0;
|
|
_id = 0;
|
|
_sampling = FLT_MAX;
|
|
//_mediumType = DEFAULT_STROKE;
|
|
_mediumType = OPAQUE_MEDIUM;
|
|
_textureId = 0;
|
|
_textureStep = 1.0;
|
|
for (int a = 0; a < MAX_MTEX; a++) {
|
|
_mtex[a] = nullptr;
|
|
}
|
|
_nodeTree = nullptr;
|
|
_tips = false;
|
|
_rep = nullptr;
|
|
}
|
|
|
|
Stroke::Stroke(const Stroke &iBrother) : Interface1D(iBrother)
|
|
{
|
|
for (vertex_container::const_iterator v = iBrother._Vertices.begin(),
|
|
vend = iBrother._Vertices.end();
|
|
v != vend;
|
|
v++)
|
|
{
|
|
_Vertices.push_back(*v);
|
|
}
|
|
_Length = 0;
|
|
_id = iBrother._id;
|
|
_ViewEdges = iBrother._ViewEdges;
|
|
_sampling = iBrother._sampling;
|
|
_mediumType = iBrother._mediumType;
|
|
_textureId = iBrother._textureId;
|
|
_textureStep = iBrother._textureStep;
|
|
for (int a = 0; a < MAX_MTEX; a++) {
|
|
_mtex[a] = iBrother._mtex[a];
|
|
}
|
|
_nodeTree = iBrother._nodeTree;
|
|
_tips = iBrother._tips;
|
|
if (iBrother._rep) {
|
|
_rep = new StrokeRep(*(iBrother._rep));
|
|
}
|
|
else {
|
|
_rep = nullptr;
|
|
}
|
|
}
|
|
|
|
Stroke::~Stroke()
|
|
{
|
|
if (!_Vertices.empty()) {
|
|
for (vertex_container::iterator v = _Vertices.begin(), vend = _Vertices.end(); v != vend; v++)
|
|
{
|
|
delete (*v);
|
|
}
|
|
_Vertices.clear();
|
|
}
|
|
|
|
_ViewEdges.clear();
|
|
if (_rep) {
|
|
delete _rep;
|
|
_rep = nullptr;
|
|
}
|
|
}
|
|
|
|
Stroke &Stroke::operator=(const Stroke &iBrother)
|
|
{
|
|
if (!_Vertices.empty()) {
|
|
_Vertices.clear();
|
|
}
|
|
|
|
for (vertex_container::const_iterator v = iBrother._Vertices.begin(),
|
|
vend = iBrother._Vertices.end();
|
|
v != vend;
|
|
v++)
|
|
{
|
|
_Vertices.push_back(*v);
|
|
}
|
|
_Length = iBrother._Length;
|
|
_id = iBrother._id;
|
|
_ViewEdges = iBrother._ViewEdges;
|
|
_sampling = iBrother._sampling;
|
|
delete _rep;
|
|
if (iBrother._rep) {
|
|
_rep = new StrokeRep(*(iBrother._rep));
|
|
}
|
|
else {
|
|
_rep = nullptr;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void Stroke::setLength(float iLength)
|
|
{
|
|
_Length = iLength;
|
|
for (vertex_container::iterator v = _Vertices.begin(), vend = _Vertices.end(); v != vend; ++v) {
|
|
(*v)->setStrokeLength(iLength);
|
|
}
|
|
}
|
|
|
|
float Stroke::ComputeSampling(int iNVertices)
|
|
{
|
|
if (iNVertices <= int(_Vertices.size())) { // soc
|
|
return _sampling;
|
|
}
|
|
|
|
float sampling = _Length / float(iNVertices - _Vertices.size() + 1);
|
|
return sampling;
|
|
}
|
|
|
|
class StrokeSegment {
|
|
public:
|
|
StrokeInternal::StrokeVertexIterator _begin;
|
|
StrokeInternal::StrokeVertexIterator _end;
|
|
float _length;
|
|
int _n;
|
|
float _sampling;
|
|
bool _resampled;
|
|
|
|
StrokeSegment(StrokeInternal::StrokeVertexIterator ibegin,
|
|
StrokeInternal::StrokeVertexIterator iend,
|
|
float ilength,
|
|
int in,
|
|
float isampling)
|
|
{
|
|
_begin = ibegin;
|
|
_end = iend;
|
|
_length = ilength;
|
|
_n = in;
|
|
_sampling = isampling;
|
|
_resampled = false;
|
|
}
|
|
};
|
|
|
|
int Stroke::Resample(int iNPoints)
|
|
{
|
|
int NPointsToAdd = iNPoints - strokeVerticesSize();
|
|
if (NPointsToAdd <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
StrokeInternal::StrokeVertexIterator it = strokeVerticesBegin();
|
|
StrokeInternal::StrokeVertexIterator next = it;
|
|
++next;
|
|
StrokeInternal::StrokeVertexIterator itend = strokeVerticesEnd();
|
|
|
|
vertex_container newVertices;
|
|
real t = 0.0f;
|
|
StrokeVertex *newVertex = nullptr;
|
|
vector<StrokeSegment> strokeSegments;
|
|
int N = 0;
|
|
float meanlength = 0;
|
|
int nsegments = 0;
|
|
while ((it != itend) && (next != itend)) {
|
|
Vec2r a((it)->getPoint());
|
|
Vec2r b((next)->getPoint());
|
|
Vec2r vec_tmp(b - a);
|
|
real norm_var = vec_tmp.norm();
|
|
int numberOfPointsToAdd = int(floor(NPointsToAdd * norm_var / _Length));
|
|
float csampling = norm_var / float(numberOfPointsToAdd + 1);
|
|
strokeSegments.emplace_back(it, next, norm_var, numberOfPointsToAdd, csampling);
|
|
N += numberOfPointsToAdd;
|
|
meanlength += norm_var;
|
|
++nsegments;
|
|
++it;
|
|
++next;
|
|
}
|
|
meanlength /= float(nsegments);
|
|
|
|
// if we don't have enough points let's resample finer some segments
|
|
bool checkEveryone = false;
|
|
bool resampled;
|
|
while (N < NPointsToAdd) {
|
|
resampled = false;
|
|
for (vector<StrokeSegment>::iterator s = strokeSegments.begin(), send = strokeSegments.end();
|
|
s != send;
|
|
++s)
|
|
{
|
|
if (s->_sampling == 0.0f) {
|
|
continue;
|
|
}
|
|
|
|
if (s->_resampled == false) {
|
|
if ((!checkEveryone) && (s->_length < meanlength)) {
|
|
continue;
|
|
}
|
|
// resample
|
|
s->_n = s->_n + 1;
|
|
s->_sampling = s->_length / float(s->_n + 1);
|
|
s->_resampled = resampled = true;
|
|
N++;
|
|
if (N == NPointsToAdd) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (checkEveryone && !resampled) {
|
|
break;
|
|
}
|
|
checkEveryone = true;
|
|
}
|
|
if (N < NPointsToAdd) {
|
|
// fatal error, likely because _Length is inconsistent with the stroke length computed with the
|
|
// vertices
|
|
return -1;
|
|
}
|
|
// actually resample:
|
|
for (vector<StrokeSegment>::iterator s = strokeSegments.begin(), send = strokeSegments.end();
|
|
s != send;
|
|
++s)
|
|
{
|
|
newVertices.push_back(&*(s->_begin));
|
|
if (s->_sampling < _sampling) {
|
|
_sampling = s->_sampling;
|
|
}
|
|
|
|
t = s->_sampling / s->_length;
|
|
for (int i = 0; i < s->_n; ++i) {
|
|
newVertex = new StrokeVertex(&*(s->_begin), &*(s->_end), t);
|
|
newVertices.push_back(newVertex);
|
|
t += s->_sampling / s->_length;
|
|
}
|
|
it = s->_begin;
|
|
next = s->_end;
|
|
}
|
|
|
|
// add last:
|
|
++it;
|
|
++next;
|
|
if ((it != itend) && (next == itend) /* && (t == 0.0f) */) {
|
|
newVertices.push_back(&(*it));
|
|
}
|
|
|
|
int newsize = newVertices.size();
|
|
if (newsize != iNPoints) {
|
|
cerr << "Warning: incorrect points number" << endl;
|
|
}
|
|
|
|
_Vertices.clear();
|
|
_Vertices = newVertices;
|
|
newVertices.clear();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Stroke::Resample(float iSampling)
|
|
{
|
|
// cerr << "old size :" << strokeVerticesSize() << endl;
|
|
if (iSampling == 0) {
|
|
return 0;
|
|
}
|
|
if (iSampling >= _sampling) {
|
|
return 0;
|
|
}
|
|
|
|
_sampling = iSampling;
|
|
// Resample...
|
|
// real curvilinearLength = 0.0f;
|
|
vertex_container newVertices;
|
|
real t = 0.0f;
|
|
const real limit = 0.99;
|
|
StrokeVertex *newVertex = nullptr;
|
|
StrokeInternal::StrokeVertexIterator it = strokeVerticesBegin();
|
|
StrokeInternal::StrokeVertexIterator next = it;
|
|
++next;
|
|
StrokeInternal::StrokeVertexIterator itend = strokeVerticesEnd();
|
|
while ((it != itend) && (next != itend)) {
|
|
newVertices.push_back(&(*it));
|
|
Vec2r a((it)->getPoint());
|
|
Vec2r b((next)->getPoint());
|
|
Vec2r vec_tmp(b - a);
|
|
real norm_var = vec_tmp.norm();
|
|
if (norm_var <= _sampling) {
|
|
// curvilinearLength += norm_var;
|
|
++it;
|
|
++next;
|
|
continue;
|
|
}
|
|
|
|
// curvilinearLength += _sampling;
|
|
t = _sampling / norm_var;
|
|
while (t < limit) {
|
|
newVertex = new StrokeVertex(&(*it), &(*next), t);
|
|
// newVertex->setCurvilinearAbscissa(curvilinearLength);
|
|
newVertices.push_back(newVertex);
|
|
t = t + _sampling / norm_var;
|
|
}
|
|
++it;
|
|
++next;
|
|
}
|
|
// add last:
|
|
if ((it != itend) && (next == itend) /* && (t == 0.0f) */) {
|
|
newVertices.push_back(&(*it));
|
|
}
|
|
|
|
_Vertices.clear();
|
|
_Vertices = newVertices;
|
|
newVertices.clear();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void Stroke::RemoveAllVertices()
|
|
{
|
|
vertex_container::iterator it = _Vertices.begin(), itend = _Vertices.end();
|
|
for (; it != itend; ++it) {
|
|
delete (*it);
|
|
}
|
|
_Vertices.clear();
|
|
UpdateLength();
|
|
}
|
|
|
|
void Stroke::RemoveVertex(StrokeVertex *iVertex)
|
|
{
|
|
vertex_container::iterator it = _Vertices.begin(), itend = _Vertices.end();
|
|
for (; it != itend; ++it) {
|
|
if ((*it) == iVertex) {
|
|
delete iVertex;
|
|
it = _Vertices.erase(it); // it is now the element just after the erased element
|
|
break;
|
|
}
|
|
}
|
|
UpdateLength();
|
|
}
|
|
|
|
void Stroke::InsertVertex(StrokeVertex *iVertex, StrokeInternal::StrokeVertexIterator next)
|
|
{
|
|
vertex_container::iterator itnext = next.getIt();
|
|
_Vertices.insert(itnext, iVertex);
|
|
UpdateLength();
|
|
}
|
|
|
|
void Stroke::UpdateLength()
|
|
{
|
|
// recompute curvilinear abscissa and stroke length
|
|
float curvabsc = 0.0f;
|
|
vertex_container::iterator it = _Vertices.begin(), itend = _Vertices.end();
|
|
vertex_container::iterator previous = it;
|
|
for (; it != itend; ++it) {
|
|
curvabsc += ((*it)->getPoint() - (*previous)->getPoint()).norm();
|
|
(*it)->setCurvilinearAbscissa(curvabsc);
|
|
previous = it;
|
|
}
|
|
_Length = curvabsc;
|
|
for (it = _Vertices.begin(); it != itend; ++it) {
|
|
(*it)->setStrokeLength(_Length);
|
|
}
|
|
}
|
|
|
|
//! embedding vertex iterator
|
|
Stroke::const_vertex_iterator Stroke::vertices_begin() const
|
|
{
|
|
return const_vertex_iterator(_Vertices.begin(), _Vertices.begin(), _Vertices.end());
|
|
}
|
|
|
|
Stroke::const_vertex_iterator Stroke::vertices_end() const
|
|
{
|
|
return const_vertex_iterator(_Vertices.end(), _Vertices.begin(), _Vertices.end());
|
|
}
|
|
|
|
Stroke::vertex_iterator Stroke::vertices_end()
|
|
{
|
|
return vertex_iterator(_Vertices.end(), _Vertices.begin(), _Vertices.end());
|
|
}
|
|
|
|
StrokeInternal::StrokeVertexIterator Stroke::strokeVerticesBegin(float t)
|
|
{
|
|
if ((t != 0) && (t < _sampling)) {
|
|
Resample(t);
|
|
}
|
|
return StrokeInternal::StrokeVertexIterator(
|
|
this->_Vertices.begin(), this->_Vertices.begin(), this->_Vertices.end());
|
|
}
|
|
|
|
StrokeInternal::StrokeVertexIterator Stroke::strokeVerticesEnd()
|
|
{
|
|
return StrokeInternal::StrokeVertexIterator(
|
|
this->_Vertices.end(), this->_Vertices.begin(), this->_Vertices.end());
|
|
}
|
|
|
|
Interface0DIterator Stroke::verticesBegin()
|
|
{
|
|
Interface0DIterator ret(new StrokeInternal::StrokeVertexIterator(
|
|
this->_Vertices.begin(), this->_Vertices.begin(), this->_Vertices.end()));
|
|
return ret;
|
|
}
|
|
|
|
Interface0DIterator Stroke::verticesEnd()
|
|
{
|
|
Interface0DIterator ret(new StrokeInternal::StrokeVertexIterator(
|
|
this->_Vertices.end(), this->_Vertices.begin(), this->_Vertices.end()));
|
|
return ret;
|
|
}
|
|
|
|
Interface0DIterator Stroke::pointsBegin(float /*t*/)
|
|
{
|
|
return verticesBegin(); // FIXME
|
|
}
|
|
|
|
Interface0DIterator Stroke::pointsEnd(float /*t*/)
|
|
{
|
|
return verticesEnd();
|
|
}
|
|
|
|
void Stroke::ScaleThickness(float iFactor)
|
|
{
|
|
for (vertex_container::iterator it = _Vertices.begin(), itend = _Vertices.end(); it != itend;
|
|
++it) {
|
|
StrokeAttribute &attr = (*it)->attribute();
|
|
attr.setThickness(iFactor * attr.getThicknessR(), iFactor * attr.getThicknessL());
|
|
}
|
|
}
|
|
|
|
void Stroke::Render(const StrokeRenderer *iRenderer)
|
|
{
|
|
if (!_rep) {
|
|
_rep = new StrokeRep(this);
|
|
}
|
|
iRenderer->RenderStrokeRep(_rep);
|
|
}
|
|
|
|
void Stroke::RenderBasic(const StrokeRenderer *iRenderer)
|
|
{
|
|
if (!_rep) {
|
|
_rep = new StrokeRep(this);
|
|
}
|
|
iRenderer->RenderStrokeRep(_rep);
|
|
}
|
|
|
|
Stroke::vertex_iterator Stroke::vertices_begin(float sampling)
|
|
{
|
|
// Resample if necessary
|
|
if ((sampling != 0) && (sampling < _sampling)) {
|
|
Resample(sampling);
|
|
}
|
|
return vertex_iterator(_Vertices.begin(), _Vertices.begin(), _Vertices.end());
|
|
// return _Vertices.begin();
|
|
}
|
|
|
|
#if 0
|
|
Stroke::vertex_iterator Stroke::vertices_last()
|
|
{
|
|
vertex_iterator res = vertices_begin();
|
|
vertex_iterator next = res;
|
|
++next;
|
|
while (!next.end()) {
|
|
++next;
|
|
++res;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
Stroke::const_vertex_iterator Stroke::vertices_last() const
|
|
{
|
|
const_vertex_iterator res = vertices_begin();
|
|
const_vertex_iterator next = res;
|
|
++next;
|
|
while (!next.end()) {
|
|
++next;
|
|
++res;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
Stroke::vertex_container::reverse_iterator Stroke::vertices_last(float sampling)
|
|
{
|
|
// Resample if necessary
|
|
if (sampling < _sampling) {
|
|
Resample(sampling);
|
|
}
|
|
return _Vertices.rbegin();
|
|
}
|
|
|
|
inline Vec3r shaded_color(int iCombination = 0) const;
|
|
|
|
inline Vec<3, real> Stroke::orientation2d(const_vertex_iterator it) const
|
|
{
|
|
return iterator_edge_orientation2d_function<Stroke, const_vertex_iterator>(this, it);
|
|
}
|
|
|
|
Vec3r Stroke::orientation2d(int iCombination) const
|
|
{
|
|
return edge_orientation2d_function<Stroke>(*this, iCombination);
|
|
}
|
|
|
|
inline Vec3r Stroke::orientation3d(const_vertex_iterator it) const
|
|
{
|
|
return iterator_edge_orientation3d_function<Stroke, const_vertex_iterator>(*this, it);
|
|
}
|
|
|
|
Vec3r Stroke::orientation3d(int iCombination) const
|
|
{
|
|
return edge_orientation3d_function<Stroke>(*this, iCombination);
|
|
}
|
|
|
|
Material Stroke::material() const
|
|
{
|
|
const_vertex_iterator v = vertices_begin(), vend = strokeVerticesEnd();
|
|
Material mat = (*v)->material();
|
|
for (; v != vend; ++v) {
|
|
if (mat != (*v)->material()) {
|
|
Exception::raiseException();
|
|
}
|
|
}
|
|
return mat;
|
|
}
|
|
|
|
int Stroke::qi() const
|
|
{
|
|
const_vertex_iterator v = vertices_begin(), vend = vertices_end();
|
|
int qi_ = (*v)->qi();
|
|
for (; v != vend; ++v) {
|
|
if ((*v)->qi() != qi_) {
|
|
Exception::raiseException();
|
|
}
|
|
}
|
|
return qi_;
|
|
}
|
|
|
|
inline occluder_container::const_iterator occluders_begin() const
|
|
{
|
|
return _FEdgeA->occluders().begin();
|
|
}
|
|
|
|
inline occluder_container::const_iterator occluders_end() const
|
|
{
|
|
return _FEdgeA->occluders().end();
|
|
}
|
|
|
|
int Stroke::occluders_size() const
|
|
{
|
|
return qi();
|
|
}
|
|
|
|
bool Stroke::occluders_empty() const
|
|
{
|
|
const_vertex_iterator v = vertices_begin(), vend = vertices_end();
|
|
bool empty = (*v)->occluders_empty();
|
|
for (; v != vend; ++v) {
|
|
if ((*v)->occluders_empty() != empty) {
|
|
Exception::raiseException();
|
|
}
|
|
}
|
|
return empty;
|
|
}
|
|
|
|
# if 0
|
|
inline const polygon3d &occludee() const
|
|
{
|
|
return *(_FEdgeA->aFace());
|
|
}
|
|
# endif
|
|
|
|
const SShape *Stroke::occluded_shape() const
|
|
{
|
|
const_vertex_iterator v = vertices_begin(), vend = vertices_end();
|
|
const SShape *sshape = (*v)->occluded_shape();
|
|
for (; v != vend; ++v) {
|
|
if ((*v)->occluded_shape() != sshape) {
|
|
Exception::raiseException();
|
|
}
|
|
}
|
|
return sshape;
|
|
}
|
|
|
|
const bool Stroke::occludee_empty() const
|
|
{
|
|
const_vertex_iterator v = vertices_begin(), vend = vertices_end();
|
|
bool empty = (*v)->occludee_empty();
|
|
for (; v != vend; ++v) {
|
|
if ((*v)->occludee_empty() != empty) {
|
|
Exception::raiseException();
|
|
}
|
|
}
|
|
return empty;
|
|
}
|
|
|
|
const SShape *Stroke::shape() const
|
|
{
|
|
const_vertex_iterator v = vertices_begin(), vend = vertices_end();
|
|
const SShape *sshape = (*v)->shape();
|
|
for (; v != vend; ++v) {
|
|
if ((*v)->shape() != sshape) {
|
|
Exception::raiseException();
|
|
}
|
|
}
|
|
return sshape;
|
|
}
|
|
|
|
real Stroke::z_discontinuity(int iCombination) const
|
|
{
|
|
return z_discontinuity_edge_function<Stroke>(*this, iCombination);
|
|
}
|
|
|
|
Vec3r Stroke::curvature2d_as_vector(int iCombination) const
|
|
{
|
|
return curvature2d_as_vector_edge_function<Stroke>(*this, iCombination);
|
|
}
|
|
|
|
real Stroke::curvature2d_as_angle(int iCombination) const
|
|
{
|
|
return curvature2d_as_angle_edge_function<Stroke>(*this, iCombination);
|
|
}
|
|
|
|
float Stroke::shape_importance(int iCombination) const
|
|
{
|
|
return shape_importance_edge_function<Stroke>(*this, iCombination);
|
|
}
|
|
|
|
float Stroke::local_average_depth(int iCombination) const
|
|
{
|
|
return local_average_depth_edge_function<Stroke>(*this, iCombination);
|
|
}
|
|
|
|
float Stroke::local_depth_variance(int iCombination) const
|
|
{
|
|
return local_depth_variance_edge_function<Stroke>(*this, iCombination);
|
|
}
|
|
|
|
real Stroke::local_average_density(float sigma, int iCombination) const
|
|
{
|
|
return density_edge_function<Stroke>(*this, iCombination);
|
|
}
|
|
#endif
|
|
|
|
} /* namespace Freestyle */
|