/*
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * ***** END GPL LICENSE BLOCK *****
 */

/** \file blender/freestyle/intern/stroke/StrokeRep.cpp
 *  \ingroup freestyle
 *  \brief Class to define the representation of a stroke (for display purpose)
 *  \author Stephane Grabli
 *  \date 05/03/2003
 */

#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;
	_color = iBrother._color;
	_alpha = iBrother._alpha;
}

//
// STRIP
/////////////////////////////////////

Strip::Strip(const vector<StrokeVertex*>& iStrokeVertices, bool hasTips, bool beginTip, bool endTip)
{
	createStrip(iStrokeVertices);
	if (!hasTips)
		computeTexCoord (iStrokeVertices);
	else
		computeTexCoordWithTips (iStrokeVertices, beginTip, endTip);
}

Strip::Strip(const Strip& iBrother)
{
	if (!iBrother._vertices.empty()) {
		for (vertex_container::const_iterator v = iBrother._vertices.begin(), vend = iBrother._vertices.end();
		     v != vend;
		     ++v)
		{
			_vertices.push_back(new StrokeVertexRep(**v));
		}
	}
	_averageThickness = iBrother._averageThickness;
}

Strip::~Strip()
{
	if (!_vertices.empty()) {
		for (vertex_container::iterator v = _vertices.begin(), vend = _vertices.end(); v != vend; ++v) {
			delete (*v);
		}
		_vertices.clear();
	}
}

//////////////////////////
// Strip creation
//////////////////////////

#define EPS_SINGULARITY_RENDERER 0.05
#define ZERO 0.00001
#define MAX_RATIO_LENGTH_SINGU 2
#define HUGE_COORD 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
	v = iStrokeVertices.begin();
	sv = *v;
	vPrev = v; //in case the stroke has only 2 vertices;
	++v;
	sv2 = *v;
	Vec2r dir(sv2->getPoint() - sv->getPoint());
	Vec2r orthDir(-dir[1], dir[0]);
	if (orthDir.norm() > ZERO)
		orthDir.normalize();
	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 != vend; ++v) {
		v2 = v;
		++v2;
		if (v2 == vend)
			break;
		sv = (*v);
		sv2 = (*v2);
		svPrev = (*vPrev);
		Vec2r p(sv->getPoint()), p2(sv2->getPoint()), pPrev(svPrev->getPoint());

		//direction and orthogonal vector to the next segment
		Vec2r dir(p2 - p);
		float dirNorm = dir.norm();
		dir.normalize();
		Vec2r orthDir(-dir[1], dir[0]);
		Vec2r stripDir = orthDir;
		if (sv->attribute().isAttributeAvailableVec2f("orientation")) {
			Vec2r userDir = sv->attribute().getAttributeVec2f("orientation");
			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);
		}

		vPrev = v;
	} // end of for

	//special case of last vertex
	sv = *v;
	sv2 = *vPrev;
	dir = Vec2r(sv->getPoint() - sv2->getPoint());
	orthDir = Vec2r(-dir[1], dir[0]);
	if (orthDir.norm() > ZERO)
		orthDir.normalize();
	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, vPrev;
	StrokeVertex *sv, *sv2; //soc unused -  *svPrev;

	bool singu1 = false, singu2 = false;
	int timeSinceSingu1 = 0, timeSinceSingu2 = 0;

	//special case of first vertex
	v = iStrokeVertices.begin();
	for (vend = iStrokeVertices.end(); v != vend; v++) {
		v2 = v;
		++v2;
		if (v2 == vend)
			break;
		sv = (*v);
		sv2 = (*v2);
		Vec2r p(sv->getPoint()), p2(sv2->getPoint());

		Vec2r dir(p2 - p);
		if (dir.norm() > ZERO)
			dir.normalize();
		Vec2r dir1, dir2;
		dir1 = _vertices[2 * i + 2]->point2d() - _vertices[2 * i]->point2d();
		dir2 = _vertices[2 * i + 3]->point2d() - _vertices[2 * i + 1]->point2d();

		if ((dir1 * dir) < -ZERO) {
			singu1 = true;
			timeSinceSingu1++;
		}
		else {
			if (singu1) {
				int toto = i - timeSinceSingu1;
				if (toto < 0)
					cerr << "Stephane dit \"Toto\"" << endl;
				//traverse all the vertices of the singularity and average them
				Vec2r avP(0.0, 0.0);
				for (j = i - timeSinceSingu1; j < i + 1; j++)
					avP = Vec2r(avP + _vertices[2 * j]->point2d());
				avP = Vec2r( 1.0 / float(timeSinceSingu1 + 1) * avP);
				for (j = i - timeSinceSingu1; j < i + 1; j++)
					_vertices[2 * j]->setPoint2d(avP);
				//_vertex[2 * j] = _vertex[2 * i];
				singu1 = false;
				timeSinceSingu1 = 0;
			}
		}
		if ((dir2 * dir) < -ZERO) {
			singu2 = true;
			timeSinceSingu2++;
		}
		else {
			if (singu2) {
				int toto = i - timeSinceSingu2;
				if (toto < 0)
					cerr << "Stephane dit \"Toto\"" << endl;
				//traverse all the vertices of the singularity and average them
				Vec2r avP(0.0, 0.0);
				for (j = i - timeSinceSingu2; j < i + 1; j++)
					avP = Vec2r(avP + _vertices[2 * j + 1]->point2d());
				avP = Vec2r(1.0 / float(timeSinceSingu2 + 1) * avP);
				for (j = i - timeSinceSingu2; j < i + 1; j++)
					_vertices[2 * j + 1]->setPoint2d(avP);
				//_vertex[2 * j + 1] = _vertex[2 * i + 1];
				singu2 = false;
				timeSinceSingu2 = 0;
			}
		}
		i++;
	}

	if (singu1) {
		//traverse all the vertices of the singularity and average them
		Vec2r avP(0.0, 0.0);
		for (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;
		}
	}
}


// Texture coordinates
////////////////////////////////

void Strip::computeTexCoord (const vector<StrokeVertex *>& iStrokeVertices)
{
	vector<StrokeVertex *>::const_iterator v, vend;
	StrokeVertex *sv;
	int i = 0;
	for (v = iStrokeVertices.begin(), vend = iStrokeVertices.end(); v != vend; v++) {
		sv = (*v);
		_vertices[i]->setTexCoord(Vec2r((real)(sv->curvilinearAbscissa() / _averageThickness), 0));
		_vertices[i]->setColor(Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1],
		                             sv->attribute().getColor()[2]));
		_vertices[i]->setAlpha(sv->attribute().getAlpha());
		i++;
		_vertices[i]->setTexCoord(Vec2r((real)(sv->curvilinearAbscissa() / _averageThickness), 1));
		_vertices[i]->setColor(Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1],
		                             sv->attribute().getColor()[2]));
		_vertices[i]->setAlpha(sv->attribute().getAlpha());
		i++;
#if 0
		cerr << "col=("<<sv->attribute().getColor()[0] << ", "
		     << sv->attribute().getColor()[1] << ", " << sv->attribute().getColor()[2] << ")" << endl;
#endif
	}
}

void Strip::computeTexCoordWithTips (const vector<StrokeVertex*>& iStrokeVertices, bool tipBegin, bool tipEnd)
{
	//soc unused - unsigned int sizeStrip = _vertices.size() + 8; //for the transition between the tip and the body
	vector<StrokeVertex*>::const_iterator v, vend;
	StrokeVertex *sv = NULL;

	v = iStrokeVertices.begin();
	vend = iStrokeVertices.end();
	float l = (*v)->strokeLength() / _averageThickness;
	int tiles = int(l);
	float fact = (float(tiles) + 0.5) / l;
	//soc unused - float uTip2 = float(tiles) + 0.25;
	float u = 0; 
	float uPrev = 0;
	int i = 0;
	float t;
	StrokeVertexRep *tvRep1, *tvRep2;

#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() / _averageThickness * fact;
			if (u > 0.25)
				break;

			svRep->setTexCoord(Vec2r((real)u, 0.5));
			svRep->setColor(Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1],
			                      sv->attribute().getColor()[2]));
			svRep->setAlpha(sv->attribute().getAlpha());
			i++;
			++currentSV;

			svRep = *currentSV;
			svRep->setTexCoord(Vec2r((real)u, 1));
			svRep->setColor(Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1],
			                      sv->attribute().getColor()[2]));
			svRep->setAlpha(sv->attribute().getAlpha());
			i++;
			++currentSV;
			uPrev = u;
		}
		//first transition vertex

		if (fabs(u - uPrev) > ZERO)
			t = (0.25 - uPrev) / (u - uPrev);
		else
			t = 0;
#if 0
		if (!tiles)
			t = 0.5;
#endif
		tvRep1 = new StrokeVertexRep(Vec2r((1 - t) * _vertices[i - 2]->point2d() + t * _vertices[i]->point2d()));
		tvRep1->setTexCoord(Vec2r(0.25, 0.5));
		tvRep1->setColor(Vec3r((1 - t) * _vertices[i - 2]->color() +
		                       t * Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1],
		                                 sv->attribute().getColor()[2])));
		tvRep1->setAlpha((1 - t) * _vertices[i - 2]->alpha() + t * sv->attribute().getAlpha());
		i++;

		tvRep2 = new StrokeVertexRep(Vec2r((1 - t) * _vertices[i - 2]->point2d() + t  *_vertices[i]->point2d()));
		tvRep2->setTexCoord(Vec2r(0.25, 1));
		tvRep2->setColor(Vec3r((1 - t) * _vertices[i - 2]->color() +
		                       t * Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1],
		                                 sv->attribute().getColor()[2])));
		tvRep2->setAlpha((1 - t) * _vertices[i - 2]->alpha() + t * sv->attribute().getAlpha());
		i++;

		currentSV = _vertices.insert(currentSV, tvRep1);
		++currentSV;
		currentSV = _vertices.insert(currentSV, tvRep2);
		++currentSV;

		// copy the vertices with different texture coordinates
		tvRep1 = new StrokeVertexRep(_vertices[i - 2]->point2d());
		tvRep1->setTexCoord(Vec2r(0.25, 0));
		tvRep1->setColor(_vertices[i - 2]->color());
		tvRep1->setAlpha(_vertices[i - 2]->alpha());
		i++;

		tvRep2 = new StrokeVertexRep(_vertices[i - 2]->point2d());
		tvRep2->setTexCoord(Vec2r(0.25, 0.5));
		tvRep2->setColor(_vertices[i - 2]->color());
		tvRep2->setAlpha(_vertices[i - 2]->alpha());
		i++;

		currentSV = _vertices.insert(currentSV, tvRep1);
		++currentSV;
		currentSV = _vertices.insert(currentSV, tvRep2);
		++currentSV;
	}
	uPrev = 0;

	//body of the stroke
	for (; v != vend; v++) {
		sv = (*v);
		svRep = *currentSV;
		u = sv->curvilinearAbscissa() / _averageThickness * fact - 0.25;
		if (u > tiles)
			break;

		svRep->setTexCoord(Vec2r((real)u, 0));
		svRep->setColor(Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1],
		                      sv->attribute().getColor()[2]));
		svRep->setAlpha(sv->attribute().getAlpha());
		i++;
		++currentSV;

		svRep = *currentSV;
		svRep->setTexCoord(Vec2r((real)u, 0.5));
		svRep->setColor(Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1],
		                      sv->attribute().getColor()[2]));
		svRep->setAlpha(sv->attribute().getAlpha());
		i++;
		++currentSV;

		uPrev = u;
	}
	if (tipEnd) {
		//second transition vertex
		if ((fabs(u - uPrev) > ZERO))
			t = (float(tiles) - uPrev) / (u - uPrev);
		else
			t = 0;

		tvRep1 = new StrokeVertexRep(Vec2r((1 - t) * _vertices[i - 2]->point2d() + t * _vertices[i]->point2d()));
		tvRep1->setTexCoord(Vec2r((real)tiles, 0));
		tvRep1->setColor(Vec3r((1 - t) * _vertices[i - 2]->color() +
		                       t * Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1],
		                                 sv->attribute().getColor()[2])));
		tvRep1->setAlpha((1 - t) * _vertices[i - 2]->alpha() + t * sv->attribute().getAlpha());
		i++;

		tvRep2 = new StrokeVertexRep(Vec2r((1 - t) * _vertices[i - 2]->point2d() + t * _vertices[i]->point2d()));
		tvRep2->setTexCoord(Vec2r((real)tiles, 0.5));
		tvRep2->setColor(Vec3r((1 - t) * _vertices[i - 2]->color() +
		                       t * Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1],
		                                 sv->attribute().getColor()[2])));
		tvRep2->setAlpha((1 - t) * _vertices[i - 2]->alpha() + t * sv->attribute().getAlpha());
		i++;

		currentSV = _vertices.insert(currentSV, tvRep1);
		++currentSV;
		currentSV = _vertices.insert(currentSV, tvRep2);
		++currentSV;

		//copy the vertices with different texture coordinates
		tvRep1 = new StrokeVertexRep(_vertices[i - 2]->point2d());
		tvRep1->setTexCoord(Vec2r(0.75, 0.5));
		tvRep1->setColor(_vertices[i - 2]->color());
		tvRep1->setAlpha(_vertices[i - 2]->alpha());
		i++;

		tvRep2 = new StrokeVertexRep(_vertices[i - 2]->point2d());
		tvRep2->setTexCoord(Vec2r(0.75, 1));
		tvRep2->setColor(_vertices[i - 2]->color());
		tvRep2->setAlpha(_vertices[i - 2]->alpha());
		i++;

		currentSV = _vertices.insert(currentSV, tvRep1);
		++currentSV;
		currentSV = _vertices.insert(currentSV, tvRep2);
		++currentSV;

		//end tip
		for (; v != vend; v++) {
			sv = (*v); 
			svRep = *currentSV;
			u = 0.75 + sv->curvilinearAbscissa() / _averageThickness * fact - float(tiles) - 0.25;

			svRep->setTexCoord(Vec2r((real)u, 0.5));
			svRep->setColor(Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1],
			                      sv->attribute().getColor()[2]));
			svRep->setAlpha(sv->attribute().getAlpha());
			i++;
			++currentSV;

			svRep = *currentSV;
			svRep->setTexCoord(Vec2r((real)u, 1));
			svRep->setColor(Vec3r(sv->attribute().getColor()[0], sv->attribute().getColor()[1],
			                      sv->attribute().getColor()[2]));
			svRep->setAlpha(sv->attribute().getAlpha());
			i++;
			++currentSV;
		}
	}

#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 = 0;
	_strokeType = Stroke::OPAQUE_MEDIUM;
	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();
	_textureId = iStroke->getTextureId();
	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;
	for (vector<Strip*>::const_iterator s = iBrother._strips.begin(), send = iBrother._strips.end();
	     s != send;
	     ++s)
	{
		_strips.push_back(new Strip(**s));
	}
}

StrokeRep::~StrokeRep()
{
	if (!_strips.empty()) {
		for (vector<Strip*>::iterator s = _strips.begin(), send = _strips.end(); s != send; ++s) {
			delete (*s);
		}
		_strips.clear();
	}
}

void StrokeRep::create()
{
	vector<StrokeVertex*> strip;
	StrokeInternal::StrokeVertexIterator v = _stroke->strokeVerticesBegin();
	StrokeInternal::StrokeVertexIterator vend = _stroke->strokeVerticesEnd();

	bool first = true;
	bool end = false;
	while (v != vend) {
		while ((v != vend) && (!(*v).attribute().isVisible())) {
			++v;
			first = false;
		}
		while ((v != vend) && ((*v).attribute().isVisible())) {
			strip.push_back(&(*v));
			++v;
		}
		if (v != vend) {
			// add the last vertex and create
			strip.push_back(&(*v));
		}
		else {
			end = true;
		}
		if ((!strip.empty()) && (strip.size() > 1)) {
			_strips.push_back(new Strip(strip, _stroke->hasTips(), first, end));
			strip.clear();
		}
		first = false;
	}
}

void StrokeRep::Render(const StrokeRenderer *iRenderer)
{
	iRenderer->RenderStrokeRep(this);
}

} /* namespace Freestyle */
