Files
test/source/blender/freestyle/intern/stroke/StrokeRep.cpp
Campbell Barton e955c94ed3 License Headers: Set copyright to "Blender Authors", add AUTHORS
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.
2023-08-16 00:20:26 +10:00

875 lines
24 KiB
C++

/* 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.h"
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 (G.debug & 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 (G.debug & G_DEBUG_FREESTYLE) {
cout << "Warning: " << orientationErrors
<< " invalid zero-length orientation vector(s) found.\n";
}
}
if (i != 2 * int(iStrokeVertices.size())) {
if (G.debug & 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 (G.debug & 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 (G.debug & 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 */