1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkRibbonFilter.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkRibbonFilter.h"
16 
17 #include "vtkCellArray.h"
18 #include "vtkCellData.h"
19 #include "vtkFloatArray.h"
20 #include "vtkMath.h"
21 #include "vtkInformation.h"
22 #include "vtkInformationVector.h"
23 #include "vtkObjectFactory.h"
24 #include "vtkPointData.h"
25 #include "vtkPoints.h"
26 #include "vtkPolyData.h"
27 #include "vtkPolyLine.h"
28 
29 vtkStandardNewMacro(vtkRibbonFilter);
30 
31 // Construct ribbon so that width is 0.1, the width does
32 // not vary with scalar values, and the width factor is 2.0.
vtkRibbonFilter()33 vtkRibbonFilter::vtkRibbonFilter()
34 {
35   this->Width = 0.5;
36   this->Angle = 0.0;
37   this->VaryWidth = 0;
38   this->WidthFactor = 2.0;
39 
40   this->DefaultNormal[0] = this->DefaultNormal[1] = 0.0;
41   this->DefaultNormal[2] = 1.0;
42 
43   this->UseDefaultNormal = 0;
44 
45   this->GenerateTCoords = 0;
46   this->TextureLength = 1.0;
47 
48   // by default process active point scalars
49   this->SetInputArrayToProcess(0,0,0,vtkDataObject::FIELD_ASSOCIATION_POINTS,
50                                vtkDataSetAttributes::SCALARS);
51 
52   // by default process active point vectors
53   this->SetInputArrayToProcess(1,0,0,vtkDataObject::FIELD_ASSOCIATION_POINTS,
54                                vtkDataSetAttributes::NORMALS);
55 }
56 
57 vtkRibbonFilter::~vtkRibbonFilter() = default;
58 
59 
RequestData(vtkInformation * vtkNotUsed (request),vtkInformationVector ** inputVector,vtkInformationVector * outputVector)60 int vtkRibbonFilter::RequestData(
61   vtkInformation *vtkNotUsed(request),
62   vtkInformationVector **inputVector,
63   vtkInformationVector *outputVector)
64 {
65   // get the info objects
66   vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
67   vtkInformation *outInfo = outputVector->GetInformationObject(0);
68 
69   // get the input and output
70   vtkPolyData *input = vtkPolyData::SafeDownCast(
71     inInfo->Get(vtkDataObject::DATA_OBJECT()));
72   vtkPolyData *output = vtkPolyData::SafeDownCast(
73     outInfo->Get(vtkDataObject::DATA_OBJECT()));
74 
75   vtkPointData *pd=input->GetPointData();
76   vtkPointData *outPD=output->GetPointData();
77   vtkCellData *cd=input->GetCellData();
78   vtkCellData *outCD=output->GetCellData();
79   vtkCellArray *inLines;
80   vtkDataArray *inNormals;
81   vtkDataArray *inScalars = this->GetInputArrayToProcess(0,inputVector);
82 
83   vtkPoints *inPts;
84   vtkIdType numPts;
85   vtkIdType numLines;
86   vtkIdType numNewPts, numNewCells;
87   vtkPoints *newPts;
88   int deleteNormals=0;
89   vtkFloatArray *newNormals;
90   vtkIdType i;
91   double range[2];
92   vtkCellArray *newStrips;
93   vtkIdType npts=0, *pts=nullptr;
94   vtkIdType offset=0;
95   vtkFloatArray *newTCoords=nullptr;
96   int abort=0;
97   vtkIdType inCellId;
98 
99   // Check input and initialize
100   //
101   vtkDebugMacro(<<"Creating ribbon");
102 
103   if ( !(inPts=input->GetPoints()) ||
104       (numPts = inPts->GetNumberOfPoints()) < 1 ||
105       !(inLines = input->GetLines()) ||
106        (numLines = inLines->GetNumberOfCells()) < 1 )
107   {
108     return 1;
109   }
110 
111   // Create the geometry and topology
112   numNewPts = 2 * numPts;
113   newPts = vtkPoints::New();
114   newPts->Allocate(numNewPts);
115   newNormals = vtkFloatArray::New();
116   newNormals->SetNumberOfComponents(3);
117   newNormals->Allocate(3*numNewPts);
118   newStrips = vtkCellArray::New();
119   newStrips->Allocate(newStrips->EstimateSize(1,numNewPts));
120   vtkCellArray *singlePolyline = vtkCellArray::New();
121 
122   // Point data: copy scalars, vectors, tcoords. Normals may be computed here.
123   outPD->CopyNormalsOff();
124   if ( (this->GenerateTCoords == VTK_TCOORDS_FROM_SCALARS && inScalars) ||
125        this->GenerateTCoords == VTK_TCOORDS_FROM_LENGTH ||
126        this->GenerateTCoords == VTK_TCOORDS_FROM_NORMALIZED_LENGTH )
127   {
128     newTCoords = vtkFloatArray::New();
129     newTCoords->SetNumberOfComponents(2);
130     newTCoords->Allocate(numNewPts);
131     outPD->CopyTCoordsOff();
132   }
133   outPD->CopyAllocate(pd,numNewPts);
134 
135   int generateNormals = 0;
136   inNormals = this->GetInputArrayToProcess(1,inputVector);
137   if ( !inNormals || this->UseDefaultNormal )
138   {
139     deleteNormals = 1;
140     inNormals = vtkFloatArray::New();
141     inNormals->SetNumberOfComponents(3);
142     inNormals->SetNumberOfTuples(numPts);
143 
144     if ( this->UseDefaultNormal )
145     {
146       for ( i=0; i < numPts; i++)
147       {
148         inNormals->SetTuple(i,this->DefaultNormal);
149       }
150     }
151     else
152     {
153       // Normal generation has been moved to lower in the function.
154       // This allows each different polylines to share vertices, but have
155       // their normals (and hence their ribbons) calculated independently
156       generateNormals = 1;
157     }
158   }
159 
160   // If varying width, get appropriate info.
161   //
162   if ( this->VaryWidth && inScalars )
163   {
164     inScalars->GetRange(range,0);
165     if ((range[1] - range[0]) == 0.0)
166     {
167       vtkWarningMacro(<< "Scalar range is zero!");
168       range[1] = range[0] + 1.0;
169     }
170   }
171 
172   // Copy selected parts of cell data; certainly don't want normals
173   //
174   numNewCells = inLines->GetNumberOfCells();
175   outCD->CopyNormalsOff();
176   outCD->CopyAllocate(cd,numNewCells);
177 
178   //  Create points along each polyline that are connected into NumberOfSides
179   //  triangle strips. Texture coordinates are optionally generated.
180   //
181   this->Theta = vtkMath::RadiansFromDegrees( this->Angle );
182   vtkPolyLine *lineNormalGenerator = vtkPolyLine::New();
183   for (inCellId=0, inLines->InitTraversal();
184        inLines->GetNextCell(npts,pts) && !abort; inCellId++)
185   {
186     this->UpdateProgress((double)inCellId/numLines);
187     abort = this->GetAbortExecute();
188 
189     if (npts < 2)
190     {
191       vtkWarningMacro(<< "Less than two points in line!");
192       continue; //skip tubing this polyline
193     }
194 
195     // If necessary calculate normals, each polyline calculates its
196     // normals independently, avoiding conflicts at shared vertices.
197     if (generateNormals)
198     {
199       singlePolyline->Reset(); //avoid instantiation
200       singlePolyline->InsertNextCell(npts,pts);
201       if ( !lineNormalGenerator->GenerateSlidingNormals(inPts,singlePolyline,
202                                                         inNormals) )
203       {
204         vtkWarningMacro(<< "No normals for line!");
205         continue; //skip tubing this polyline
206       }
207     }
208 
209     // Generate the points around the polyline. The strip is not created
210     // if the polyline is bad.
211     //
212     if ( !this->GeneratePoints(offset,npts,pts,inPts,newPts,pd,outPD,
213                                newNormals,inScalars,range,inNormals) )
214     {
215       vtkWarningMacro(<< "Could not generate points!");
216       continue; //skip ribboning this polyline
217     }
218 
219     // Generate the strip for this polyline
220     //
221     this->GenerateStrip(offset,npts,pts,inCellId,cd,outCD,newStrips);
222 
223     // Generate the texture coordinates for this polyline
224     //
225     if ( newTCoords )
226     {
227       this->GenerateTextureCoords(offset,npts,pts,inPts,inScalars,newTCoords);
228     }
229 
230     // Compute the new offset for the next polyline
231     offset = this->ComputeOffset(offset,npts);
232 
233   }//for all polylines
234 
235   singlePolyline->Delete();
236 
237   // Update ourselves
238   //
239   if ( deleteNormals )
240   {
241     inNormals->Delete();
242   }
243 
244   if ( newTCoords )
245   {
246     outPD->SetTCoords(newTCoords);
247     newTCoords->Delete();
248   }
249 
250   output->SetPoints(newPts);
251   newPts->Delete();
252 
253   output->SetStrips(newStrips);
254   newStrips->Delete();
255 
256   outPD->SetNormals(newNormals);
257   newNormals->Delete();
258   lineNormalGenerator->Delete();
259 
260   output->Squeeze();
261 
262   return 1;
263 }
264 
GeneratePoints(vtkIdType offset,vtkIdType npts,vtkIdType * pts,vtkPoints * inPts,vtkPoints * newPts,vtkPointData * pd,vtkPointData * outPD,vtkFloatArray * newNormals,vtkDataArray * inScalars,double range[2],vtkDataArray * inNormals)265 int vtkRibbonFilter::GeneratePoints(vtkIdType offset,
266                                   vtkIdType npts, vtkIdType *pts,
267                                   vtkPoints *inPts, vtkPoints *newPts,
268                                   vtkPointData *pd, vtkPointData *outPD,
269                                   vtkFloatArray *newNormals,
270                                   vtkDataArray *inScalars, double range[2],
271                                   vtkDataArray *inNormals)
272 {
273   vtkIdType j;
274   int i;
275   double p[3];
276   double pNext[3];
277   double sNext[3] = {0, 0, 0};
278   double sPrev[3];
279   double n[3];
280   double s[3], sp[3], sm[3], v[3];
281   //double bevelAngle;
282   double w[3];
283   double nP[3];
284   double sFactor=1.0;
285   vtkIdType ptId=offset;
286 
287   // Use "averaged" segment to create beveled effect.
288   // Watch out for first and last points.
289   //
290   for (j=0; j < npts; j++)
291   {
292     if ( j == 0 ) //first point
293     {
294       inPts->GetPoint(pts[0],p);
295       inPts->GetPoint(pts[1],pNext);
296       for (i=0; i<3; i++)
297       {
298         sNext[i] = pNext[i] - p[i];
299         sPrev[i] = sNext[i];
300       }
301     }
302     else if ( j == (npts-1) ) //last point
303     {
304       for (i=0; i<3; i++)
305       {
306         sPrev[i] = sNext[i];
307         p[i] = pNext[i];
308       }
309     }
310     else
311     {
312       for (i=0; i<3; i++)
313       {
314         p[i] = pNext[i];
315       }
316       inPts->GetPoint(pts[j+1],pNext);
317       for (i=0; i<3; i++)
318       {
319         sPrev[i] = sNext[i];
320         sNext[i] = pNext[i] - p[i];
321       }
322     }
323 
324     inNormals->GetTuple(pts[j], n);
325 
326     if ( vtkMath::Normalize(sNext) == 0.0 )
327     {
328       vtkWarningMacro(<<"Coincident points!");
329       return 0;
330     }
331 
332     for (i=0; i<3; i++)
333     {
334       s[i] = (sPrev[i] + sNext[i]) / 2.0; //average vector
335     }
336     // if s is zero then just use sPrev cross n
337     if (vtkMath::Normalize(s) == 0.0)
338     {
339       vtkWarningMacro(<< "Using alternate bevel vector");
340       vtkMath::Cross(sPrev,n,s);
341       if (vtkMath::Normalize(s) == 0.0)
342       {
343         vtkWarningMacro(<< "Using alternate bevel vector");
344       }
345     }
346 /*
347     if ( (bevelAngle = vtkMath::Dot(sNext,sPrev)) > 1.0 )
348       {
349       bevelAngle = 1.0;
350       }
351     if ( bevelAngle < -1.0 )
352       {
353       bevelAngle = -1.0;
354       }
355     bevelAngle = acos((double)bevelAngle) / 2.0; //(0->90 degrees)
356     if ( (bevelAngle = cos(bevelAngle)) == 0.0 )
357       {
358       bevelAngle = 1.0;
359       }
360 
361     bevelAngle = this->Width / bevelAngle; //keep ribbon constant width
362 */
363     vtkMath::Cross(s,n,w);
364     if ( vtkMath::Normalize(w) == 0.0)
365     {
366       vtkWarningMacro(<<"Bad normal s = " <<s[0]<<" "<<s[1]<<" "<< s[2]
367                       << " n = " << n[0] << " " << n[1] << " " << n[2]);
368       return 0;
369     }
370 
371     vtkMath::Cross(w,s,nP); //create orthogonal coordinate system
372     vtkMath::Normalize(nP);
373 
374     // Compute a scale factor based on scalars or vectors
375     if ( inScalars && this->VaryWidth ) // varying by scalar values
376     {
377       sFactor = 1.0 + ((this->WidthFactor - 1.0) *
378                 (inScalars->GetComponent(pts[j],0) - range[0])
379                        / (range[1]-range[0]));
380     }
381 
382     for (i=0; i<3; i++)
383     {
384       v[i] = (w[i]*cos(this->Theta) + nP[i]*sin(this->Theta));
385       sp[i] = p[i] + this->Width * sFactor * v[i];
386       sm[i] = p[i] - this->Width * sFactor * v[i];
387     }
388     newPts->InsertPoint(ptId,sm);
389     newNormals->InsertTuple(ptId,nP);
390     outPD->CopyData(pd,pts[j],ptId);
391     ptId++;
392     newPts->InsertPoint(ptId,sp);
393     newNormals->InsertTuple(ptId,nP);
394     outPD->CopyData(pd,pts[j],ptId);
395     ptId++;
396   }//for all points in polyline
397 
398   return 1;
399 }
400 
GenerateStrip(vtkIdType offset,vtkIdType npts,vtkIdType * vtkNotUsed (pts),vtkIdType inCellId,vtkCellData * cd,vtkCellData * outCD,vtkCellArray * newStrips)401 void vtkRibbonFilter::GenerateStrip(vtkIdType offset, vtkIdType npts,
402                                     vtkIdType* vtkNotUsed(pts),
403                                     vtkIdType inCellId,
404                                     vtkCellData *cd, vtkCellData *outCD,
405                                     vtkCellArray *newStrips)
406 {
407   vtkIdType i, idx, outCellId;
408 
409   outCellId = newStrips->InsertNextCell(npts*2);
410   outCD->CopyData(cd,inCellId,outCellId);
411   for (i=0; i < npts; i++)
412   {
413     idx = 2*i;
414     newStrips->InsertCellPoint(offset+idx);
415     newStrips->InsertCellPoint(offset+idx+1);
416   }
417 }
418 
GenerateTextureCoords(vtkIdType offset,vtkIdType npts,vtkIdType * pts,vtkPoints * inPts,vtkDataArray * inScalars,vtkFloatArray * newTCoords)419 void vtkRibbonFilter::GenerateTextureCoords(vtkIdType offset,
420                                             vtkIdType npts, vtkIdType *pts,
421                                             vtkPoints *inPts,
422                                             vtkDataArray *inScalars,
423                                             vtkFloatArray *newTCoords)
424 {
425   vtkIdType i;
426   int k;
427   double tc;
428 
429   double s0, s;
430   //The first texture coordinate is always 0.
431   for ( k=0; k < 2; k++)
432   {
433     newTCoords->InsertTuple2(offset+k,0.0,0.0);
434   }
435   if ( this->GenerateTCoords == VTK_TCOORDS_FROM_SCALARS && inScalars)
436   {
437     s0 = inScalars->GetTuple1(pts[0]);
438     for (i=1; i < npts; i++)
439     {
440       s = inScalars->GetTuple1(pts[i]);
441       tc = (s - s0) / this->TextureLength;
442       for ( k=0; k < 2; k++)
443       {
444         newTCoords->InsertTuple2(offset+i*2+k,tc,0.0);
445       }
446     }
447   }
448   else if ( this->GenerateTCoords == VTK_TCOORDS_FROM_LENGTH )
449   {
450     double xPrev[3], x[3], len=0.0;
451     inPts->GetPoint(pts[0],xPrev);
452     for (i=1; i < npts; i++)
453     {
454       inPts->GetPoint(pts[i],x);
455       len += sqrt(vtkMath::Distance2BetweenPoints(x,xPrev));
456       tc = len / this->TextureLength;
457       for ( k=0; k < 2; k++)
458       {
459         newTCoords->InsertTuple2(offset+i*2+k,tc,0.0);
460       }
461       xPrev[0]=x[0]; xPrev[1]=x[1]; xPrev[2]=x[2];
462     }
463   }
464   else if ( this->GenerateTCoords == VTK_TCOORDS_FROM_NORMALIZED_LENGTH )
465   {
466     double xPrev[3], x[3], length=0.0, len=0.0;
467     inPts->GetPoint(pts[0],xPrev);
468     for (i=1; i < npts; i++)
469     {
470       inPts->GetPoint(pts[i],x);
471       length += sqrt(vtkMath::Distance2BetweenPoints(x,xPrev));
472       xPrev[0]=x[0]; xPrev[1]=x[1]; xPrev[2]=x[2];
473     }
474 
475     inPts->GetPoint(pts[0],xPrev);
476     for (i=1; i < npts; i++)
477     {
478       inPts->GetPoint(pts[i],x);
479       len += sqrt(vtkMath::Distance2BetweenPoints(x,xPrev));
480       tc = len / length;
481       for ( k=0; k < 2; k++)
482       {
483         newTCoords->InsertTuple2(offset+i*2+k,tc,0.0);
484       }
485       xPrev[0]=x[0]; xPrev[1]=x[1]; xPrev[2]=x[2];
486     }
487   }
488 }
489 
490 // Compute the number of points in this ribbon
ComputeOffset(vtkIdType offset,vtkIdType npts)491 vtkIdType vtkRibbonFilter::ComputeOffset(vtkIdType offset, vtkIdType npts)
492 {
493   offset += 2 * npts;
494   return offset;
495 }
496 
497 // Description:
498 // Return the method of generating the texture coordinates.
GetGenerateTCoordsAsString(void)499 const char *vtkRibbonFilter::GetGenerateTCoordsAsString(void)
500 {
501   if ( this->GenerateTCoords == VTK_TCOORDS_OFF )
502   {
503     return "GenerateTCoordsOff";
504   }
505   else if ( this->GenerateTCoords == VTK_TCOORDS_FROM_SCALARS )
506   {
507     return "GenerateTCoordsFromScalar";
508   }
509   else if ( this->GenerateTCoords == VTK_TCOORDS_FROM_LENGTH )
510   {
511     return "GenerateTCoordsFromLength";
512   }
513   else
514   {
515     return "GenerateTCoordsFromNormalizedLength";
516   }
517 }
518 
PrintSelf(ostream & os,vtkIndent indent)519 void vtkRibbonFilter::PrintSelf(ostream& os, vtkIndent indent)
520 {
521   this->Superclass::PrintSelf(os,indent);
522 
523   os << indent << "Width: " << this->Width << "\n";
524   os << indent << "Angle: " << this->Angle << "\n";
525   os << indent << "VaryWidth: " << (this->VaryWidth ? "On\n" : "Off\n");
526   os << indent << "Width Factor: " << this->WidthFactor << "\n";
527   os << indent << "Use Default Normal: " << this->UseDefaultNormal << "\n";
528   os << indent << "Default Normal: " << "( "
529      << this->DefaultNormal[0] << ", "
530      << this->DefaultNormal[1] << ", "
531      << this->DefaultNormal[2] << " )\n";
532 
533   os << indent << "Generate TCoords: "
534      << this->GetGenerateTCoordsAsString() << endl;
535   os << indent << "Texture Length: " << this->TextureLength << endl;
536 }
537 
538