Blender V4.3
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
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
struct Strip Strip
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert Invert a color
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:33
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
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:126
float _averageThickness
Definition StrokeRep.h:127
void computeTexCoordWithTips(const std::vector< StrokeVertex * > &iStrokeVertices, bool tipBegin, bool tipEnd, float texStep)
int sizeStrip() const
Definition StrokeRep.h:149
void createStrip(const std::vector< StrokeVertex * > &iStrokeVertices)
const float * getThickness() const
Definition Stroke.h:136
const float * getColor() const
Definition Stroke.h:97
Vec3f getColorRGB() const
Definition Stroke.h:121
float getAlpha() const
Definition Stroke.h:127
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:168
virtual void create()
vector< Strip * > _strips
Definition StrokeRep.h:167
MTex * _mtex[MAX_MTEX]
Definition StrokeRep.h:171
virtual void Render(const StrokeRenderer *iRenderer)
bNodeTree * _nodeTree
Definition StrokeRep.h:172
void setTexCoord(const Vec2r &p, bool tips=false)
Definition StrokeRep.h:89
void setColor(const Vec3r &p)
Definition StrokeRep.h:99
float curvilinearAbscissa() const
Definition Stroke.h:388
Vec2r getPoint() const
Definition Stroke.h:364
const StrokeAttribute & attribute() const
Definition Stroke.h:376
bool hasTex() const
Definition Stroke.h:660
MTex * getMTex(int idx)
Definition Stroke.h:648
uint getTextureId()
Definition Stroke.h:636
bNodeTree * getNodeTree()
Definition Stroke.h:654
float getTextureStep()
Definition Stroke.h:642
MediumType getMediumType() const
Definition Stroke.h:630
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:104
value_type norm() const
Definition VecMat.h:94
draw_view in_light_buf[] float
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
static uint a[3]
Definition RandGen.cpp:82
double real
Definition Precision.h:14