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