Blender V4.5
StrokeRep.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9
10#include <cmath>
11
12#include "Stroke.h"
14#include "StrokeIterators.h"
15#include "StrokeRenderer.h"
16#include "StrokeRep.h"
17
18#include "BKE_global.hh"
19
20using namespace std;
21
22namespace Freestyle {
23
24//
25// STROKE VERTEX REP
27
29{
30 _point2d = iBrother._point2d;
31 _texCoord = iBrother._texCoord;
33 _color = iBrother._color;
34 _alpha = iBrother._alpha;
35}
36
37//
38// STRIP
40
41Strip::Strip(const vector<StrokeVertex *> &iStrokeVertices,
42 bool hasTex,
43 bool tipBegin,
44 bool tipEnd,
45 float texStep)
46{
47 createStrip(iStrokeVertices);
48
49 setVertexColor(iStrokeVertices);
50
51 if (hasTex) {
52 // We compute both kinds of coordinates to use different kinds of textures
53 computeTexCoord(iStrokeVertices, texStep);
54 computeTexCoordWithTips(iStrokeVertices, tipBegin, tipEnd, texStep);
55 }
56}
57
58Strip::Strip(const Strip &iBrother)
59{
60 if (!iBrother._vertices.empty()) {
61 for (vertex_container::const_iterator v = iBrother._vertices.begin(),
62 vend = iBrother._vertices.end();
63 v != vend;
64 ++v)
65 {
66 _vertices.push_back(new StrokeVertexRep(**v));
67 }
68 }
70}
71
72Strip::~Strip()
73{
74 if (!_vertices.empty()) {
75 for (vertex_container::iterator v = _vertices.begin(), vend = _vertices.end(); v != vend; ++v)
76 {
77 delete (*v);
78 }
79 _vertices.clear();
80 }
81}
82
84// Strip creation
86
87#define EPS_SINGULARITY_RENDERER 0.05
88#define ZERO 0.00001
89#define MAX_RATIO_LENGTH_SINGU 2
90#define HUGE_COORD 1.0e4
91
92static bool notValid(Vec2r p)
93{
94 return (p[0] != p[0]) || (p[1] != p[1]) || (fabs(p[0]) > HUGE_COORD) ||
95 (fabs(p[1]) > HUGE_COORD) || (p[0] < -HUGE_COORD) || (p[1] < -HUGE_COORD);
96}
97
98#if 0
99static real crossP(const Vec2r &A, const Vec2r &B)
100{
101 return A[0] * B[1] - A[1] * B[0];
102}
103#endif
104
105void Strip::createStrip(const vector<StrokeVertex *> &iStrokeVertices)
106{
107 // computeParameterization();
108 if (iStrokeVertices.size() < 2) {
109 if (G.debug & G_DEBUG_FREESTYLE) {
110 cout << "Warning: strip has less than 2 vertices" << endl;
111 }
112 return;
113 }
114 _vertices.reserve(2 * iStrokeVertices.size());
115 if (!_vertices.empty()) {
116 for (vertex_container::iterator v = _vertices.begin(), vend = _vertices.end(); v != vend; ++v)
117 {
118 delete (*v);
119 }
120 _vertices.clear();
121 }
122 _averageThickness = 0.0;
123
124 vector<StrokeVertex *>::const_iterator v, vend, v2, vPrev;
125 StrokeVertex *sv, *sv2, *svPrev;
126 int orientationErrors = 0;
127
128 // special case of first vertex
129 v2 = v = iStrokeVertices.begin();
130 ++v2;
131 sv = *v;
132 vPrev = v; // in case the stroke has only 2 vertices;
133 sv2 = *v2;
134 Vec2r dir(sv2->getPoint() - sv->getPoint());
135 Vec2r orthDir(-dir[1], dir[0]);
136 if (orthDir.norm() > ZERO) {
137 orthDir.normalize();
138 }
139 Vec2r stripDir(orthDir);
140 // check whether the orientation was user defined
141 if (sv->attribute().isAttributeAvailableVec2f("orientation")) {
142 Vec2r userDir = sv->attribute().getAttributeVec2f("orientation");
143 if (userDir.norm() > 1e-6) {
144 userDir.normalize();
145 real dp = userDir * orthDir;
146 if (dp < 0) {
147 userDir = userDir * (-1.0f);
148 }
149 stripDir = userDir;
150 }
151 else {
152 ++orientationErrors;
153 }
154 }
155 const float *thickness = sv->attribute().getThickness();
156 _vertices.push_back(new StrokeVertexRep(sv->getPoint() + thickness[1] * stripDir));
157 _vertices.push_back(new StrokeVertexRep(sv->getPoint() - thickness[0] * stripDir));
158
159#if 0
160 Vec2r userDir = _stroke->getBeginningOrientation();
161 if (userDir != Vec2r(0, 0)) {
162 userDir.normalize();
163 real o1 = (orthDir * userDir);
164 real o2 = crossP(orthDir, userDir);
165 real orientation = o1 * o2;
166 if (orientation > 0) {
167 // then the vertex to move is v0
168 if (o1 > 0) {
169 _vertex[0] = _vertex[1] + userDir;
170 }
171 else {
172 _vertex[0] = _vertex[1] - userDir;
173 }
174 }
175 if (orientation < 0) {
176 // then we must move v1
177 if (o1 < 0) {
178 _vertex[1] = _vertex[0] + userDir;
179 }
180 else {
181 _vertex[1] = _vertex[0] - userDir;
182 }
183 }
184 }
185#endif
186
187 int i = 2; // 2 because we have already processed the first vertex
188
189 for (vend = iStrokeVertices.end(), ++v, ++v2; v2 != vend; vPrev = v++, ++v2) {
190 sv = (*v);
191 sv2 = (*v2);
192 svPrev = (*vPrev);
193 Vec2r p(sv->getPoint()), p2(sv2->getPoint()), pPrev(svPrev->getPoint());
194
195 // direction and orthogonal vector to the next segment
196 Vec2r dir(p2 - p);
197 float dirNorm = dir.norm();
198 dir.normalize();
199 Vec2r orthDir(-dir[1], dir[0]);
200 Vec2r stripDir = orthDir;
201 if (sv->attribute().isAttributeAvailableVec2f("orientation")) {
202 Vec2r userDir = sv->attribute().getAttributeVec2f("orientation");
203 if (userDir.norm() > 1e-6) {
204 userDir.normalize();
205 real dp = userDir * orthDir;
206 if (dp < 0) {
207 userDir = userDir * (-1.0f);
208 }
209 stripDir = userDir;
210 }
211 else {
212 ++orientationErrors;
213 }
214 }
215
216 // direction and orthogonal vector to the previous segment
217 Vec2r dirPrev(p - pPrev);
218 float dirPrevNorm = dirPrev.norm();
219 dirPrev.normalize();
220 Vec2r orthDirPrev(-dirPrev[1], dirPrev[0]);
221 Vec2r stripDirPrev = orthDirPrev;
222 if (svPrev->attribute().isAttributeAvailableVec2f("orientation")) {
223 Vec2r userDir = svPrev->attribute().getAttributeVec2f("orientation");
224 if (userDir.norm() > 1e-6) {
225 userDir.normalize();
226 real dp = userDir * orthDir;
227 if (dp < 0) {
228 userDir = userDir * (-1.0f);
229 }
230 stripDirPrev = userDir;
231 }
232 else {
233 ++orientationErrors;
234 }
235 }
236
237 const float *thickness = sv->attribute().getThickness();
238 _averageThickness += thickness[0] + thickness[1];
239 Vec2r pInter;
240 int interResult;
241
242 interResult = GeomUtils::intersect2dLine2dLine(Vec2r(pPrev + thickness[1] * stripDirPrev),
243 Vec2r(p + thickness[1] * stripDirPrev),
244 Vec2r(p + thickness[1] * stripDir),
245 Vec2r(p2 + thickness[1] * stripDir),
246 pInter);
247 if (interResult == GeomUtils::DO_INTERSECT) {
248 _vertices.push_back(new StrokeVertexRep(pInter));
249 }
250 else {
251 _vertices.push_back(new StrokeVertexRep(p + thickness[1] * stripDir));
252 }
253 ++i;
254
255 interResult = GeomUtils::intersect2dLine2dLine(Vec2r(pPrev - thickness[0] * stripDirPrev),
256 Vec2r(p - thickness[0] * stripDirPrev),
257 Vec2r(p - thickness[0] * stripDir),
258 Vec2r(p2 - thickness[0] * stripDir),
259 pInter);
260 if (interResult == GeomUtils::DO_INTERSECT) {
261 _vertices.push_back(new StrokeVertexRep(pInter));
262 }
263 else {
264 _vertices.push_back(new StrokeVertexRep(p - thickness[0] * stripDir));
265 }
266 ++i;
267
268 // if the angle is obtuse, we simply average the directions to avoid the singularity
269 stripDir = stripDir + stripDirPrev;
270 if ((dirNorm < ZERO) || (dirPrevNorm < ZERO) || (stripDir.norm() < ZERO)) {
271 stripDir[0] = 0;
272 stripDir[1] = 0;
273 }
274 else {
275 stripDir.normalize();
276 }
277
278 Vec2r vec_tmp(_vertices[i - 2]->point2d() - p);
279 if ((vec_tmp.norm() > thickness[1] * MAX_RATIO_LENGTH_SINGU) || (dirNorm < ZERO) ||
280 (dirPrevNorm < ZERO) || notValid(_vertices[i - 2]->point2d()) ||
281 (fabs(stripDir * dir) < EPS_SINGULARITY_RENDERER))
282 {
283 _vertices[i - 2]->setPoint2d(p + thickness[1] * stripDir);
284 }
285
286 vec_tmp = _vertices[i - 1]->point2d() - p;
287 if ((vec_tmp.norm() > thickness[0] * MAX_RATIO_LENGTH_SINGU) || (dirNorm < ZERO) ||
288 (dirPrevNorm < ZERO) || notValid(_vertices[i - 1]->point2d()) ||
289 (fabs(stripDir * dir) < EPS_SINGULARITY_RENDERER))
290 {
291 _vertices[i - 1]->setPoint2d(p - thickness[0] * stripDir);
292 }
293 } // end of for
294
295 // special case of last vertex
296 sv = *v;
297 sv2 = *vPrev;
298 dir = Vec2r(sv->getPoint() - sv2->getPoint());
299 orthDir = Vec2r(-dir[1], dir[0]);
300 if (orthDir.norm() > ZERO) {
301 orthDir.normalize();
302 }
303 Vec2r stripDirLast(orthDir);
304 // check whether the orientation was user defined
305 if (sv->attribute().isAttributeAvailableVec2f("orientation")) {
306 Vec2r userDir = sv->attribute().getAttributeVec2f("orientation");
307 if (userDir.norm() > 1e-6) {
308 userDir.normalize();
309 real dp = userDir * orthDir;
310 if (dp < 0) {
311 userDir = userDir * (-1.0f);
312 }
313 stripDirLast = userDir;
314 }
315 else {
316 ++orientationErrors;
317 }
318 }
319 const float *thicknessLast = sv->attribute().getThickness();
320 _vertices.push_back(new StrokeVertexRep(sv->getPoint() + thicknessLast[1] * stripDirLast));
321 ++i;
322 _vertices.push_back(new StrokeVertexRep(sv->getPoint() - thicknessLast[0] * stripDirLast));
323 ++i;
324
325#if 0
326 int n = i - 1;
327 // check whether the orientation of the extremity was user defined
328 userDir = _stroke->getEndingOrientation();
329 if (userDir != Vec2r(0, 0)) {
330 userDir.normalize();
331 real o1 = (orthDir * userDir);
332 real o2 = crossP(orthDir, userDir);
333 real orientation = o1 * o2;
334 if (orientation > 0) {
335 // then the vertex to move is vn
336 if (o1 < 0) {
337 _vertex[n] = _vertex[n - 1] + userDir;
338 }
339 else {
340 _vertex[n] = _vertex[n - 1] - userDir;
341 }
342 }
343 if (orientation < 0) {
344 // then we must move vn-1
345 if (o1 > 0) {
346 _vertex[n - 1] = _vertex[n] + userDir;
347 }
348 else {
349 _vertex[n - 1] = _vertex[n] - userDir;
350 }
351 }
352 }
353#endif
354
355 _averageThickness /= float(iStrokeVertices.size() - 2);
356 // I did not use the first and last vertex for the average
357 if (iStrokeVertices.size() < 3) {
358 _averageThickness = 0.5 * (thicknessLast[1] + thicknessLast[0] + thickness[0] + thickness[1]);
359 }
360
361 if (orientationErrors > 0) {
362 if (G.debug & G_DEBUG_FREESTYLE) {
363 cout << "Warning: " << orientationErrors
364 << " invalid zero-length orientation vector(s) found.\n";
365 }
366 }
367
368 if (i != 2 * int(iStrokeVertices.size())) {
369 if (G.debug & G_DEBUG_FREESTYLE) {
370 cout << "Warning: problem with stripe size\n";
371 }
372 }
373
374 cleanUpSingularities(iStrokeVertices);
375}
376
377// CLEAN UP
379
380void Strip::cleanUpSingularities(const vector<StrokeVertex *> &iStrokeVertices)
381{
382 int k;
383 int sizeStrip = _vertices.size();
384
385 for (k = 0; k < sizeStrip; k++) {
386 if (notValid(_vertices[k]->point2d())) {
387 if (G.debug & G_DEBUG_FREESTYLE) {
388 cout << "Warning: strip vertex " << k << " non valid" << endl;
389 }
390 return;
391 }
392 }
393
394 // return;
395 if (iStrokeVertices.size() < 2) {
396 return;
397 }
398 int i = 0, j;
399 vector<StrokeVertex *>::const_iterator v, vend, v2;
400 StrokeVertex *sv, *sv2;
401
402 bool singu1 = false, singu2 = false;
403 int timeSinceSingu1 = 0, timeSinceSingu2 = 0;
404
405 // special case of first vertex
406 v = iStrokeVertices.begin();
407 for (vend = iStrokeVertices.end(); v != vend; v++) {
408 v2 = v;
409 ++v2;
410 if (v2 == vend) {
411 break;
412 }
413 sv = (*v);
414 sv2 = (*v2);
415 Vec2r p(sv->getPoint()), p2(sv2->getPoint());
416
417 Vec2r dir(p2 - p);
418 if (dir.norm() > ZERO) {
419 dir.normalize();
420 }
421 Vec2r dir1, dir2;
422 dir1 = _vertices[2 * i + 2]->point2d() - _vertices[2 * i]->point2d();
423 dir2 = _vertices[2 * i + 3]->point2d() - _vertices[2 * i + 1]->point2d();
424
425 if ((dir1 * dir) < -ZERO) {
426 singu1 = true;
427 timeSinceSingu1++;
428 }
429 else {
430 if (singu1) {
431 int toto = i - timeSinceSingu1;
432 if (toto < 0) {
433 cerr << "Stephane dit \"Toto\"" << endl;
434 }
435 // traverse all the vertices of the singularity and average them
436 Vec2r avP(0.0, 0.0);
437 for (j = i - timeSinceSingu1; j <= i; j++) {
438 avP = Vec2r(avP + _vertices[2 * j]->point2d());
439 }
440 avP = Vec2r(1.0 / float(timeSinceSingu1 + 1) * avP);
441 for (j = i - timeSinceSingu1; j <= i; j++) {
442 _vertices[2 * j]->setPoint2d(avP);
443 }
444 //_vertex[2 * j] = _vertex[2 * i];
445 singu1 = false;
446 timeSinceSingu1 = 0;
447 }
448 }
449 if ((dir2 * dir) < -ZERO) {
450 singu2 = true;
451 timeSinceSingu2++;
452 }
453 else {
454 if (singu2) {
455 int toto = i - timeSinceSingu2;
456 if (toto < 0) {
457 cerr << "Stephane dit \"Toto\"" << endl;
458 }
459 // traverse all the vertices of the singularity and average them
460 Vec2r avP(0.0, 0.0);
461 for (j = i - timeSinceSingu2; j <= i; j++) {
462 avP = Vec2r(avP + _vertices[2 * j + 1]->point2d());
463 }
464 avP = Vec2r(1.0 / float(timeSinceSingu2 + 1) * avP);
465 for (j = i - timeSinceSingu2; j <= i; j++) {
466 _vertices[2 * j + 1]->setPoint2d(avP);
467 }
468 //_vertex[2 * j + 1] = _vertex[2 * i + 1];
469 singu2 = false;
470 timeSinceSingu2 = 0;
471 }
472 }
473 i++;
474 }
475
476 if (singu1) {
477 // traverse all the vertices of the singularity and average them
478 Vec2r avP(0.0, 0.0);
479 for (j = i - timeSinceSingu1; j < i; j++) {
480 avP = Vec2r(avP + _vertices[2 * j]->point2d());
481 }
482 avP = Vec2r(1.0 / float(timeSinceSingu1) * avP);
483 for (j = i - timeSinceSingu1; j < i; j++) {
484 _vertices[2 * j]->setPoint2d(avP);
485 }
486 }
487 if (singu2) {
488 // traverse all the vertices of the singularity and average them
489 Vec2r avP(0.0, 0.0);
490 for (j = i - timeSinceSingu2; j < i; j++) {
491 avP = Vec2r(avP + _vertices[2 * j + 1]->point2d());
492 }
493 avP = Vec2r(1.0 / float(timeSinceSingu2) * avP);
494 for (j = i - timeSinceSingu2; j < i; j++) {
495 _vertices[2 * j + 1]->setPoint2d(avP);
496 }
497 }
498
499 for (k = 0; k < sizeStrip; k++) {
500 if (notValid(_vertices[k]->point2d())) {
501 if (G.debug & G_DEBUG_FREESTYLE) {
502 cout << "Warning: strip vertex " << k << " non valid after cleanup" << endl;
503 }
504 return;
505 }
506 }
507}
508
509// Vertex color (RGBA)
511
512void Strip::setVertexColor(const vector<StrokeVertex *> &iStrokeVertices)
513{
514 vector<StrokeVertex *>::const_iterator v, vend;
515 StrokeVertex *sv;
516 int i = 0;
517 for (v = iStrokeVertices.begin(), vend = iStrokeVertices.end(); v != vend; v++) {
518 sv = (*v);
519 _vertices[i]->setColor(Vec3r(sv->attribute().getColorRGB()));
520 _vertices[i]->setAlpha(sv->attribute().getAlpha());
521 i++;
522 _vertices[i]->setColor(Vec3r(sv->attribute().getColorRGB()));
523 _vertices[i]->setAlpha(sv->attribute().getAlpha());
524 i++;
525#if 0
526 cerr << "col=(" << sv->attribute().getColor()[0] << ", " << sv->attribute().getColor()[1]
527 << ", " << sv->attribute().getColor()[2] << ")" << endl;
528#endif
529 }
530}
531
532// Texture coordinates
534
535void Strip::computeTexCoord(const vector<StrokeVertex *> &iStrokeVertices, float texStep)
536{
537 vector<StrokeVertex *>::const_iterator v, vend;
538 StrokeVertex *sv;
539 int i = 0;
540 for (v = iStrokeVertices.begin(), vend = iStrokeVertices.end(); v != vend; v++) {
541 sv = (*v);
542 _vertices[i]->setTexCoord(
543 Vec2r((real)(sv->curvilinearAbscissa() / (_averageThickness * texStep)), 0));
544 i++;
545 _vertices[i]->setTexCoord(
546 Vec2r((real)(sv->curvilinearAbscissa() / (_averageThickness * texStep)), -1));
547 i++;
548 }
549}
550
551void Strip::computeTexCoordWithTips(const vector<StrokeVertex *> &iStrokeVertices,
552 bool tipBegin,
553 bool tipEnd,
554 float texStep)
555{
556 vector<StrokeVertex *>::const_iterator v, vend;
557 StrokeVertex *sv = nullptr;
558 StrokeVertexRep *tvRep[2] = {nullptr};
559
560 float l, fact, t;
561 float u = 0, uPrev = 0;
562 int tiles;
563 int i = 0;
564 float spacedThickness = _averageThickness * texStep;
565
566 v = iStrokeVertices.begin();
567 vend = iStrokeVertices.end();
568 l = (*v)->strokeLength() / spacedThickness;
569 tiles = std::roundf(l); // round to the nearest
570 fact = (float(tiles) + 0.5) / l;
571
572#if 0
573 cerr << "l=" << l << " tiles=" << tiles << " _averageThicnkess=" << _averageThickness
574 << " strokeLength=" << (*v)->strokeLength() << endl;
575#endif
576
577 vector<StrokeVertexRep *>::iterator currentSV = _vertices.begin();
578 StrokeVertexRep *svRep;
579 if (tipBegin) {
580 for (; v != vend; v++) {
581 sv = (*v);
582 svRep = *currentSV;
583 u = sv->curvilinearAbscissa() / spacedThickness * fact;
584 if (u > 0.25) {
585 break;
586 }
587
588 svRep->setTexCoord(Vec2r((real)u, -0.5), true);
589 i++;
590 ++currentSV;
591
592 svRep = *currentSV;
593 svRep->setTexCoord(Vec2r((real)u, -1), true);
594 i++;
595 ++currentSV;
596 uPrev = u;
597 }
598
599 if (v != vend && i >= 2) {
600 // first transition vertex
601 if (fabs(u - uPrev) > ZERO) {
602 t = (0.25 - uPrev) / (u - uPrev);
603 }
604 else {
605 t = 0;
606 }
607 for (int k = 0; k < 2; k++) {
608 tvRep[k] = new StrokeVertexRep((1 - t) * _vertices[i - 2]->point2d() +
609 t * _vertices[i]->point2d());
610 tvRep[k]->setTexCoord((1 - t) * _vertices[i - 2]->texCoord() +
611 t * _vertices[i]->texCoord());
612 // v coord is -0.5 for tvRep[0], -1.0 for tvRep[1]
613 tvRep[k]->setTexCoord(Vec2r(0.25, -0.5 * (k + 1)), true);
614 tvRep[k]->setColor((1 - t) * _vertices[i - 2]->color() +
615 t * Vec3r(sv->attribute().getColorRGB()));
616 tvRep[k]->setAlpha((1 - t) * _vertices[i - 2]->alpha() + t * sv->attribute().getAlpha());
617 i++;
618 }
619 for (int k = 0; k < 2; k++) {
620 currentSV = _vertices.insert(currentSV, tvRep[k]);
621 ++currentSV;
622 }
623
624 // copy the vertices with different texture coordinates
625 for (int k = 0; k < 2; k++) {
626 tvRep[k] = new StrokeVertexRep(*(_vertices[i - 2]));
627 // v coord is 0.0 for tvRep[0], -0.5 for tvRep[1]
628 tvRep[k]->setTexCoord(Vec2r(0.0, -0.5 * k), true);
629 i++;
630 }
631 for (int k = 0; k < 2; k++) {
632 currentSV = _vertices.insert(currentSV, tvRep[k]);
633 ++currentSV;
634 }
635 }
636 }
637 uPrev = 0;
638
639 // body of the stroke
640 for (; v != vend; v++) {
641 sv = (*v);
642 svRep = *currentSV;
643 u = sv->curvilinearAbscissa() / spacedThickness * fact - 0.25;
644 if (u > tiles) {
645 break;
646 }
647
648 svRep->setTexCoord(Vec2r((real)u, 0), true);
649 i++;
650 ++currentSV;
651
652 svRep = *currentSV;
653 svRep->setTexCoord(Vec2r((real)u, -0.5), true);
654 i++;
655 ++currentSV;
656
657 uPrev = u;
658 }
659
660 if (tipEnd) {
661 if (v != vend && i >= 2) {
662 // second transition vertex
663 if (fabs(u - uPrev) > ZERO) {
664 t = (float(tiles) - uPrev) / (u - uPrev);
665 }
666 else {
667 t = 0;
668 }
669 for (int k = 0; k < 2; k++) {
670 tvRep[k] = new StrokeVertexRep((1 - t) * _vertices[i - 2]->point2d() +
671 t * _vertices[i]->point2d());
672 tvRep[k]->setTexCoord((1 - t) * _vertices[i - 2]->texCoord() +
673 t * _vertices[i]->texCoord());
674 // v coord is 0.0 for tvRep[0], -0.5 for tvRep[1]
675 tvRep[k]->setTexCoord(Vec2r((real)tiles, -0.5 * k), true);
676 tvRep[k]->setColor((1 - t) * _vertices[i - 2]->color() +
677 t * Vec3r(sv->attribute().getColorRGB()));
678 tvRep[k]->setAlpha((1 - t) * _vertices[i - 2]->alpha() + t * sv->attribute().getAlpha());
679 i++;
680 }
681 for (int k = 0; k < 2; k++) {
682 currentSV = _vertices.insert(currentSV, tvRep[k]);
683 ++currentSV;
684 }
685
686 // copy the vertices with different texture coordinates
687 for (int k = 0; k < 2; k++) {
688 tvRep[k] = new StrokeVertexRep(*(_vertices[i - 2]));
689 // v coord is -0.5 for tvRep[0], -1.0 for tvRep[1]
690 tvRep[k]->setTexCoord(Vec2r(0.75, -0.5 * (k + 1)), true);
691 i++;
692 }
693 for (int k = 0; k < 2; k++) {
694 currentSV = _vertices.insert(currentSV, tvRep[k]);
695 ++currentSV;
696 }
697 }
698
699 // end tip
700 for (; v != vend; v++) {
701 sv = (*v);
702 svRep = *currentSV;
703 u = 0.75 + sv->curvilinearAbscissa() / spacedThickness * fact - float(tiles) - 0.25;
704
705 svRep->setTexCoord(Vec2r((real)u, -0.5), true);
706 i++;
707 ++currentSV;
708
709 svRep = *currentSV;
710 svRep->setTexCoord(Vec2r((real)u, -1), true);
711 i++;
712 ++currentSV;
713 }
714 }
715
716#if 0
717 cerr << "u=" << u << " i=" << i << "/" << _sizeStrip << endl;
718
719 for (i = 0; i < _sizeStrip; i++) {
720 _alpha[i] = 1.0;
721 }
722
723 for (i = 0; i < _sizeStrip; i++) {
724 cerr << "(" << _texCoord[i][0] << ", " << _texCoord[i][1] << ") ";
725 }
726 cerr << endl;
727
728 Vec2r vec_tmp;
729 for (i = 0; i < _sizeStrip / 2; i++) {
730 vec_tmp = _vertex[2 * i] - _vertex[2 * i + 1];
731 }
732 if (vec_tmp.norm() > 4 * _averageThickness) {
733 cerr << "Warning (from Fredo): There is a pb in the texture coordinates computation" << endl;
734 }
735#endif
736}
737
738//
739// StrokeRep
741
743{
744 _stroke = nullptr;
746 _nodeTree = nullptr;
747 _hasTex = false;
748 _textureStep = 1.0;
749 for (int a = 0; a < MAX_MTEX; a++) {
750 _mtex[a] = nullptr;
751 }
753 if (ptm) {
755 }
756#if 0
757 _averageTextureAlpha = 0.5; // default value
758 if (_strokeType == OIL_STROKE) {
759 _averageTextureAlpha = 0.75;
760 }
761 if (_strokeType >= NO_BLEND_STROKE) {
762 _averageTextureAlpha = 1.0;
763 }
764#endif
765}
766
768{
769 _stroke = iStroke;
770 _strokeType = iStroke->getMediumType();
771 _nodeTree = iStroke->getNodeTree();
772 _hasTex = iStroke->hasTex();
773 _textureId = iStroke->getTextureId();
774 _textureStep = iStroke->getTextureStep();
775 for (int a = 0; a < MAX_MTEX; a++) {
776 if (iStroke->getMTex(a)) {
777 _mtex[a] = iStroke->getMTex(a);
778 }
779 else {
780 _mtex[a] = nullptr;
781 }
782 }
783 if (_textureId == 0) {
785 if (ptm) {
787 }
788 }
789
790#if 0
791 _averageTextureAlpha = 0.5; // default value
792 if (_strokeType == OIL_STROKE) {
793 _averageTextureAlpha = 0.75;
794 }
795 if (_strokeType >= NO_BLEND_STROKE) {
796 _averageTextureAlpha = 1.0;
797 }
798#endif
799 create();
800}
801
803{
804 // soc unused - int i = 0;
805 _stroke = iBrother._stroke;
806 _strokeType = iBrother._strokeType;
807 _textureId = iBrother._textureId;
808 _textureStep = iBrother._textureStep;
809 _nodeTree = iBrother._nodeTree;
810 _hasTex = iBrother._hasTex;
811 for (int a = 0; a < MAX_MTEX; a++) {
812 if (iBrother._mtex[a]) {
813 _mtex[a] = iBrother._mtex[a];
814 }
815 else {
816 _mtex[a] = nullptr;
817 }
818 }
819 for (vector<Strip *>::const_iterator s = iBrother._strips.begin(), send = iBrother._strips.end();
820 s != send;
821 ++s)
822 {
823 _strips.push_back(new Strip(**s));
824 }
825}
826
828{
829 if (!_strips.empty()) {
830 for (vector<Strip *>::iterator s = _strips.begin(), send = _strips.end(); s != send; ++s) {
831 delete (*s);
832 }
833 _strips.clear();
834 }
835}
836
838{
839 vector<StrokeVertex *> strip;
842
843 bool first = true;
844 bool end = false;
845 while (v != vend) {
846 while ((v != vend) && !(*v).attribute().isVisible()) {
847 ++v;
848 first = false;
849 }
850 while ((v != vend) && (*v).attribute().isVisible()) {
851 strip.push_back(&(*v));
852 ++v;
853 }
854 if (v != vend) {
855 // add the last vertex and create
856 strip.push_back(&(*v));
857 }
858 else {
859 end = true;
860 }
861 if (!strip.empty() && (strip.size() > 1)) {
862 _strips.push_back(new Strip(strip, _hasTex, first, end, _textureStep));
863 strip.clear();
864 }
865 first = false;
866 }
867}
868
869void StrokeRep::Render(const StrokeRenderer *iRenderer)
870{
871 iRenderer->RenderStrokeRep(this);
872}
873
874} /* namespace Freestyle */
@ G_DEBUG_FREESTYLE
Iterators used to iterate over the elements of the Stroke. Can't be used in python.
Iterators used to iterate over the elements of the Stroke.
Classes to render a stroke with OpenGL.
#define EPS_SINGULARITY_RENDERER
Definition StrokeRep.cpp:87
#define MAX_RATIO_LENGTH_SINGU
Definition StrokeRep.cpp:89
#define HUGE_COORD
Definition StrokeRep.cpp:90
#define ZERO
Definition StrokeRep.cpp:88
Class to define the representation of a stroke (for display purpose).
Classes to define a stroke.
#define MAX_MTEX
Definition Stroke.h:31
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
#define A
void setVertexColor(const std::vector< StrokeVertex * > &iStrokeVertices)
void computeTexCoord(const std::vector< StrokeVertex * > &iStrokeVertices, float texStep)
void cleanUpSingularities(const std::vector< StrokeVertex * > &iStrokeVertices)
vertex_container _vertices
Definition StrokeRep.h:122
float _averageThickness
Definition StrokeRep.h:123
void computeTexCoordWithTips(const std::vector< StrokeVertex * > &iStrokeVertices, bool tipBegin, bool tipEnd, float texStep)
int sizeStrip() const
Definition StrokeRep.h:145
void createStrip(const std::vector< StrokeVertex * > &iStrokeVertices)
Strip(const std::vector< StrokeVertex * > &iStrokeVertices, bool hasTex=false, bool tipBegin=false, bool tipEnd=false, float texStep=1.0)
Definition StrokeRep.cpp:41
const float * getThickness() const
Definition Stroke.h:134
const float * getColor() const
Definition Stroke.h:95
Vec3f getColorRGB() const
Definition Stroke.h:119
float getAlpha() const
Definition Stroke.h:125
bool isAttributeAvailableVec2f(const char *iName) const
Definition Stroke.cpp:277
Vec2f getAttributeVec2f(const char *iName) const
Definition Stroke.cpp:227
virtual void RenderStrokeRep(StrokeRep *iStrokeRep) const =0
Stroke::MediumType _strokeType
Definition StrokeRep.h:162
virtual void create()
vector< Strip * > _strips
Definition StrokeRep.h:161
MTex * _mtex[MAX_MTEX]
Definition StrokeRep.h:165
virtual void Render(const StrokeRenderer *iRenderer)
bNodeTree * _nodeTree
Definition StrokeRep.h:166
void setTexCoord(const Vec2r &p, bool tips=false)
Definition StrokeRep.h:87
void setColor(const Vec3r &p)
Definition StrokeRep.h:97
float curvilinearAbscissa() const
Definition Stroke.h:384
Vec2r getPoint() const
Definition Stroke.h:360
const StrokeAttribute & attribute() const
Definition Stroke.h:372
bool hasTex() const
Definition Stroke.h:654
MTex * getMTex(int idx)
Definition Stroke.h:642
uint getTextureId()
Definition Stroke.h:630
bNodeTree * getNodeTree()
Definition Stroke.h:648
float getTextureStep()
Definition Stroke.h:636
MediumType getMediumType() const
Definition Stroke.h:624
StrokeInternal::StrokeVertexIterator strokeVerticesEnd()
Definition Stroke.cpp:765
StrokeInternal::StrokeVertexIterator strokeVerticesBegin(float t=0.0f)
Definition Stroke.cpp:756
static TextureManager * getInstance()
Vec< T, N > & normalize()
Definition VecMat.h:102
value_type norm() const
Definition VecMat.h:92
ccl_gpu_kernel_postfix ccl_global KernelWorkTile * tiles
ccl_device_inline float2 fabs(const float2 a)
#define B
#define G(x, y, z)
intersection_test intersect2dLine2dLine(const Vec2r &p1, const Vec2r &p2, const Vec2r &p3, const Vec2r &p4, Vec2r &res)
VecMat::Vec2< real > Vec2r
Definition Geom.h:24
VecMat::Vec3< real > Vec3r
Definition Geom.h:30
inherits from class Rep
Definition AppCanvas.cpp:20
static bool notValid(Vec2r p)
Definition StrokeRep.cpp:92
double real
Definition Precision.h:14
i
Definition text_draw.cc:230