1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkVoxelContoursToSurfaceFilter.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 "vtkVoxelContoursToSurfaceFilter.h"
16 
17 #include "vtkAppendPolyData.h"
18 #include "vtkCellArray.h"
19 #include "vtkContourFilter.h"
20 #include "vtkFloatArray.h"
21 #include "vtkInformation.h"
22 #include "vtkInformationVector.h"
23 #include "vtkObjectFactory.h"
24 #include "vtkPointData.h"
25 #include "vtkPolyData.h"
26 #include "vtkStructuredPoints.h"
27 
28 vtkStandardNewMacro(vtkVoxelContoursToSurfaceFilter);
29 
vtkVoxelContoursToSurfaceFilter()30 vtkVoxelContoursToSurfaceFilter::vtkVoxelContoursToSurfaceFilter()
31 {
32   this->MemoryLimitInBytes = 10000000;
33   this->Spacing[0] = 1.0;
34   this->Spacing[1] = 1.0;
35   this->Spacing[2] = 1.0;
36   this->LineList = new double[4 * 1000];
37   this->LineListLength = 0;
38   this->LineListSize = 1000;
39   this->SortedXList = nullptr;
40   this->SortedYList = nullptr;
41   this->WorkingList = nullptr;
42   this->IntersectionList = nullptr;
43   this->SortedListSize = 0;
44 }
45 
~vtkVoxelContoursToSurfaceFilter()46 vtkVoxelContoursToSurfaceFilter::~vtkVoxelContoursToSurfaceFilter()
47 {
48   delete[] this->LineList;
49   delete[] this->SortedXList;
50   delete[] this->SortedYList;
51   delete[] this->WorkingList;
52   delete[] this->IntersectionList;
53 }
54 
AddLineToLineList(double x1,double y1,double x2,double y2)55 void vtkVoxelContoursToSurfaceFilter::AddLineToLineList(double x1, double y1, double x2, double y2)
56 {
57   // Do we need to increase the size of our list?
58   if (this->LineListLength >= this->LineListSize)
59   {
60     // Double the space we had before
61     double* newList = new double[this->LineListSize * 4 * 2];
62     memcpy(newList, this->LineList, 4 * this->LineListSize * sizeof(double));
63     delete[] this->LineList;
64     this->LineList = newList;
65     this->LineListSize *= 2;
66   }
67 
68   // Now we are sure we have space - add the line
69   this->LineList[4 * this->LineListLength + 0] = x1;
70   this->LineList[4 * this->LineListLength + 1] = y1;
71   this->LineList[4 * this->LineListLength + 2] = x2;
72   this->LineList[4 * this->LineListLength + 3] = y2;
73   this->LineListLength++;
74 }
75 
SortLineList()76 void vtkVoxelContoursToSurfaceFilter::SortLineList()
77 {
78   int i, j;
79   double tmp[4];
80   double tmpval;
81 
82   // Make sure we have enough space in our sorted list
83   if (this->SortedListSize < this->LineListLength)
84   {
85     delete[] this->SortedXList;
86     delete[] this->SortedYList;
87     delete[] this->WorkingList;
88     delete[] this->IntersectionList;
89 
90     this->SortedXList = new double[4 * this->LineListLength];
91     this->SortedYList = new double[4 * this->LineListLength];
92     this->SortedListSize = this->LineListLength;
93 
94     // Create the space we'll need for our working list of indices
95     // The is the list of lines that we are currently considering
96     // for intersections. Lines move in then out of the list as
97     // we pass the first then the second endpoint. This will be
98     // used during the CastXLines and CastYLines methods, and is
99     // the same size as the number of lines.
100     this->WorkingList = new int[this->LineListLength];
101 
102     // Create the space we'll need for the intersection list
103     // There can't be more intersections than there are possible
104     // lines. Although it is highly doubtful we'll actually use
105     // all this space, it isn't much and it makes the code simpler
106     // not to have to worry about exceeding the bounds. This will be
107     // used during the CastXLines and CastYLines methods, and is
108     // the same size as the number of lines.
109     this->IntersectionList = new double[this->LineListLength];
110   }
111 
112   // Copy the lines into the lists
113   memcpy(this->SortedXList, this->LineList, 4 * this->LineListLength * sizeof(double));
114   memcpy(this->SortedYList, this->LineList, 4 * this->LineListLength * sizeof(double));
115 
116   // Now sort on x and y
117   // Use a simple bubble sort - will improve if necessary
118   for (i = 0; i < this->LineListLength; i++)
119   {
120     // swap x entry if necessary to keep min x the first endpoint
121     if (this->SortedXList[4 * i + 0] > this->SortedXList[4 * i + 2])
122     {
123       tmpval = this->SortedXList[4 * i];
124       this->SortedXList[4 * i] = this->SortedXList[4 * i + 2];
125       this->SortedXList[4 * i + 2] = tmpval;
126       tmpval = this->SortedXList[4 * i + 1];
127       this->SortedXList[4 * i + 1] = this->SortedXList[4 * i + 3];
128       this->SortedXList[4 * i + 3] = tmpval;
129     }
130 
131     // swap y entry if necessary to keep min y the first endpoint
132     if (this->SortedYList[4 * i + 1] > this->SortedYList[4 * i + 3])
133     {
134       tmpval = this->SortedYList[4 * i];
135       this->SortedYList[4 * i] = this->SortedYList[4 * i + 2];
136       this->SortedYList[4 * i + 2] = tmpval;
137       tmpval = this->SortedYList[4 * i + 1];
138       this->SortedYList[4 * i + 1] = this->SortedYList[4 * i + 3];
139       this->SortedYList[4 * i + 3] = tmpval;
140     }
141 
142     // Sort x list
143     for (j = i; j > 0; j--)
144     {
145       if (this->SortedXList[j * 4] < this->SortedXList[(j - 1) * 4])
146       {
147         memcpy(tmp, this->SortedXList + j * 4, 4 * sizeof(double));
148         memcpy(this->SortedXList + j * 4, this->SortedXList + (j - 1) * 4, 4 * sizeof(double));
149         memcpy(this->SortedXList + (j - 1) * 4, tmp, 4 * sizeof(double));
150       }
151       else
152       {
153         break;
154       }
155     }
156 
157     // Sort y list
158     for (j = i; j > 0; j--)
159     {
160       if (this->SortedYList[j * 4 + 1] < this->SortedYList[(j - 1) * 4 + 1])
161       {
162         memcpy(tmp, this->SortedYList + j * 4, 4 * sizeof(double));
163         memcpy(this->SortedYList + j * 4, this->SortedYList + (j - 1) * 4, 4 * sizeof(double));
164         memcpy(this->SortedYList + (j - 1) * 4, tmp, 4 * sizeof(double));
165       }
166       else
167       {
168         break;
169       }
170     }
171   }
172 }
173 
CastLines(float * slicePtr,double gridOrigin[3],int gridSize[3],int type)174 void vtkVoxelContoursToSurfaceFilter::CastLines(
175   float* slicePtr, double gridOrigin[3], int gridSize[3], int type)
176 {
177   double axis1, axis2;
178   double d1, d2;
179   int index;
180   int i, j;
181   double tmp;
182   double* line;
183   float* currSlicePtr;
184   int currSlice;
185   int currentIntersection;
186   double sign;
187   double* sortedList;
188   double low1, low2, high1, high2;
189   int increment1, increment2;
190   int offset1, offset2, offset3, offset4;
191 
192   // this is the x direction
193   if (type == 0)
194   {
195     low1 = gridOrigin[0];
196     high1 = gridOrigin[0] + (double)gridSize[0];
197     low2 = gridOrigin[1];
198     high2 = gridOrigin[1] + (double)gridSize[1];
199     increment1 = gridSize[0];
200     increment2 = 1;
201     sortedList = this->SortedXList;
202     offset1 = 0;
203     offset2 = 2;
204     offset3 = 1;
205     offset4 = 3;
206   }
207   // This is the y direction
208   else
209   {
210     low1 = gridOrigin[1];
211     high1 = gridOrigin[1] + (double)gridSize[1];
212     low2 = gridOrigin[0];
213     high2 = gridOrigin[0] + (double)gridSize[0];
214     increment1 = 1;
215     increment2 = gridSize[0];
216     sortedList = this->SortedYList;
217     offset1 = 1;
218     offset2 = 3;
219     offset3 = 0;
220     offset4 = 2;
221   }
222 
223   // Initialize the working list to nothing. We will start
224   // looking at index = 0 for the next line to add to the
225   // working list
226   this->WorkingListLength = 0;
227   index = 0;
228 
229   // Loop through the x or y lines
230   for (axis1 = low1, currSlice = 0; axis1 < high1; axis1 += 1.0, currSlice++)
231   {
232     // Initialize the intersection list to nothing
233     this->IntersectionListLength = 0;
234 
235     // Add lines to the working list if necessary
236     for (; index < this->LineListLength; index++)
237     {
238       if (sortedList[4 * index + offset1] < axis1)
239       {
240         this->WorkingList[this->WorkingListLength] = index;
241         this->WorkingListLength++;
242       }
243       else
244       {
245         break;
246       }
247     }
248 
249     // Do the intersections, removing lines from the
250     // working list if necessary
251     for (i = 0; i < this->WorkingListLength; i++)
252     {
253       line = sortedList + 4 * this->WorkingList[i];
254 
255       // Yes, it intersects, add it to the intersection list
256       if (line[offset1] < axis1 && line[offset2] > axis1)
257       {
258         // Compute the intersection distance
259         // For x lines this is y = y1 + (y2 - y1)*((x - x1)/(x2 - x1))
260         // For y lines this is x = x1 + (x2 - x1)*((y - y1)/(y2 - y1))
261         this->IntersectionList[this->IntersectionListLength] = line[offset3] +
262           (line[offset4] - line[offset3]) *
263             ((axis1 - line[offset1]) / (line[offset2] - line[offset1]));
264 
265         // Make sure this distance is sorted
266         for (j = this->IntersectionListLength; j > 0; j--)
267         {
268           if (this->IntersectionList[j] < this->IntersectionList[j - 1])
269           {
270             tmp = this->IntersectionList[j];
271             this->IntersectionList[j] = this->IntersectionList[j - 1];
272             this->IntersectionList[j - 1] = tmp;
273           }
274           else
275           {
276             break;
277           }
278         }
279         this->IntersectionListLength++;
280       }
281       // No, it doesn't intersect, remove it from the working list
282       else
283       {
284         for (j = i; j < (this->WorkingListLength - 1); j++)
285         {
286           this->WorkingList[j] = this->WorkingList[j + 1];
287         }
288         this->WorkingListLength--;
289         i--;
290       }
291     }
292 
293     // Now we have all the intersections for the x or y line, in sorted
294     // order. Use them to fill in distances (as long as there are
295     // any)
296     if (this->IntersectionListLength)
297     {
298       currSlicePtr = slicePtr + currSlice * increment2;
299       currentIntersection = 0;
300       // We are starting outside which has a negative distance
301       sign = -1.0;
302       for (axis2 = low2; axis2 < high2; axis2 += 1.0)
303       {
304         while (currentIntersection < this->IntersectionListLength &&
305           this->IntersectionList[currentIntersection] < axis2)
306 
307         {
308           currentIntersection++;
309 
310           // Each time we cross a line we are moving across an
311           // inside/outside boundary
312           sign *= -1.0;
313         }
314         // We are now positioned at an x or y value between currentIntersection
315         // and currentIntersection - 1 (except at boundaries where we are
316         // before intersection 0 or after the last intersection)
317 
318         if (currentIntersection == 0)
319         {
320           d1 = axis2 - this->IntersectionList[currentIntersection];
321           *currSlicePtr = (*currSlicePtr > d1) ? (*currSlicePtr) : (d1);
322         }
323         else if (currentIntersection == this->IntersectionListLength)
324         {
325           d1 = this->IntersectionList[currentIntersection - 1] - axis2;
326           *currSlicePtr = (*currSlicePtr > d1) ? (*currSlicePtr) : (d1);
327         }
328         else
329         {
330           d1 = axis2 - this->IntersectionList[currentIntersection - 1];
331           d2 = this->IntersectionList[currentIntersection] - axis2;
332           d1 = (d1 < d2) ? (d1) : (d2);
333           if (type == 0)
334           {
335             *currSlicePtr = sign * d1;
336           }
337           else
338           {
339             *currSlicePtr = (sign * (*currSlicePtr) < d1) ? (*currSlicePtr) : (sign * d1);
340           }
341         }
342 
343         currSlicePtr += increment1;
344       }
345     }
346   }
347 }
348 
PushDistances(float * volumePtr,int gridSize[3],int chunkSize)349 void vtkVoxelContoursToSurfaceFilter::PushDistances(
350   float* volumePtr, int gridSize[3], int chunkSize)
351 {
352   int i, j, k;
353   float* vptr;
354 
355   // Push distances along x (both ways) and y (both ways) on each slice
356   for (k = 0; k < chunkSize; k++)
357   {
358     // Do the x rows
359     for (j = 0; j < gridSize[1]; j++)
360     {
361       vptr = volumePtr + k * gridSize[0] * gridSize[1] + j * gridSize[0];
362       vptr++;
363 
364       // first one way
365       for (i = 1; i < gridSize[0]; i++)
366       {
367         if (*vptr > 0 && *(vptr - 1) + 1 < *(vptr))
368         {
369           *vptr = *(vptr - 1) + 1;
370         }
371         else if (*vptr < 0 && *(vptr - 1) - 1 > *(vptr))
372         {
373           *vptr = *(vptr - 1) - 1;
374         }
375         vptr++;
376       }
377 
378       vptr -= 2;
379       i -= 2;
380 
381       // then the other
382       for (; i >= 0; i--)
383       {
384         if (*vptr > 0 && *(vptr + 1) + 1 < *(vptr))
385         {
386           *vptr = *(vptr + 1) + 1;
387         }
388         else if (*vptr < 0 && *(vptr + 1) - 1 > *(vptr))
389         {
390           *vptr = *(vptr + 1) - 1;
391         }
392       }
393     }
394 
395     // Do the y columns
396     for (i = 0; i < gridSize[0]; i++)
397     {
398       vptr = volumePtr + k * gridSize[0] * gridSize[1] + i;
399 
400       vptr += gridSize[0];
401 
402       // first one way
403       for (j = 1; j < gridSize[1]; j++)
404       {
405         if (*vptr > 0 && *(vptr - gridSize[0]) + 1 < *(vptr))
406         {
407           *vptr = *(vptr - gridSize[0]) + 1;
408         }
409         else if (*vptr < 0 && *(vptr - gridSize[0]) - 1 > *(vptr))
410         {
411           *vptr = *(vptr - gridSize[0]) - 1;
412         }
413         vptr += gridSize[0];
414       }
415 
416       vptr -= 2 * gridSize[0];
417       j -= 2;
418 
419       // then the other
420       for (; j >= 0; j--)
421       {
422         if (*vptr > 0 && *(vptr + gridSize[0]) + 1 < *(vptr))
423         {
424           *vptr = *(vptr + gridSize[0]) + 1;
425         }
426         else if (*vptr < 0 && *(vptr + gridSize[0]) - 1 > *(vptr))
427         {
428           *vptr = *(vptr + gridSize[0]) - 1;
429         }
430       }
431     }
432   }
433 }
434 
435 // Append data sets into single unstructured grid
RequestData(vtkInformation * vtkNotUsed (request),vtkInformationVector ** inputVector,vtkInformationVector * outputVector)436 int vtkVoxelContoursToSurfaceFilter::RequestData(vtkInformation* vtkNotUsed(request),
437   vtkInformationVector** inputVector, vtkInformationVector* outputVector)
438 {
439   // get the info objects
440   vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
441   vtkInformation* outInfo = outputVector->GetInformationObject(0);
442 
443   // get the input and output
444   vtkPolyData* input = vtkPolyData::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()));
445   vtkPolyData* output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));
446 
447   vtkCellArray* inputPolys = input->GetPolys();
448   int gridSize[3];
449   double gridOrigin[3];
450   double contourBounds[6];
451   int chunkSize;
452   int currentSlice, lastSlice, currentIndex;
453   int i, j;
454   int numberOfInputCells;
455   int currentInputCellIndex;
456   vtkIdType npts = 0;
457   const vtkIdType* pts = nullptr;
458   double point1[3], point2[3];
459   double currentZ;
460   vtkStructuredPoints* volume;
461   float *volumePtr, *slicePtr;
462   vtkContourFilter* contourFilter;
463   vtkPolyData* contourOutput;
464   vtkAppendPolyData* appendFilter;
465 
466   vtkDebugMacro(<< "Creating surfaces from contours");
467 
468   // Get the bounds of the input contours
469   input->GetBounds(contourBounds);
470 
471   if (contourBounds[0] > contourBounds[1])
472   { // empty input
473     return 1;
474   }
475 
476   // From the bounds, compute the grid size, and origin
477 
478   // The origin of the grid should be (-0.5, -0.5, 0.0) away from the
479   // lower bounds of the contours. This is because we want the grid
480   // to lie halfway between integer endpoint locations of the line
481   // segments on each plane. Also, we want an extra plane on each end
482   // for capping
483   gridOrigin[0] = contourBounds[0] - 0.5;
484   gridOrigin[1] = contourBounds[2] - 0.5;
485   gridOrigin[2] = contourBounds[4] - 1.0;
486 
487   // The difference between the bounds, plus one to account a
488   // sample on the first and last location, plus one to account
489   // for the larger grid size ( the 0.5 unit border ) On Z, we
490   // want to sample exactly on the contours so we don't need to
491   // add the extra 1, but we have added two extra planes so we
492   // need another 2.
493   gridSize[0] = (int)(contourBounds[1] - contourBounds[0] + 2);
494   gridSize[1] = (int)(contourBounds[3] - contourBounds[2] + 2);
495   gridSize[2] = (int)(contourBounds[5] - contourBounds[4] + 3);
496 
497   // How many slices in a chunk? This will later be decremented
498   // by one to account for the fact that the last slice in the
499   // previous chuck is copied to the first slice in the next chunk.
500   // Stay within memory limit. There are 4 bytes per double.
501   chunkSize = this->MemoryLimitInBytes / (gridSize[0] * gridSize[1] * 4);
502   if (chunkSize > gridSize[2])
503   {
504     chunkSize = gridSize[2];
505   }
506 
507   currentSlice = 0;
508   currentZ = contourBounds[4] - 1.0;
509   currentIndex = 0;
510   lastSlice = gridSize[2] - 1;
511   numberOfInputCells = inputPolys->GetNumberOfCells();
512   currentInputCellIndex = 0;
513 
514   volume = vtkStructuredPoints::New();
515   volume->SetDimensions(gridSize[0], gridSize[1], chunkSize);
516   volume->SetSpacing(this->Spacing);
517   volume->AllocateScalars(VTK_FLOAT, 1);
518   vtkDataArray* volumeArrayDA = volume->GetPointData()->GetScalars();
519   vtkFloatArray* volumeArray = vtkArrayDownCast<vtkFloatArray>(volumeArrayDA);
520   assert(volumeArray);
521   volumePtr = volumeArray->GetPointer(0);
522 
523   contourFilter = vtkContourFilter::New();
524   contourFilter->SetInputData(volume);
525   contourFilter->SetNumberOfContours(1);
526   contourFilter->SetValue(0, 0.0);
527 
528   appendFilter = vtkAppendPolyData::New();
529 
530   inputPolys->InitTraversal();
531   inputPolys->GetNextCell(npts, pts);
532 
533   while (currentSlice <= lastSlice)
534   {
535     // Make sure the origin of the volume is in the right
536     // place so that the appended polydata all matches up
537     // nicely.
538     volume->SetOrigin(gridOrigin[0], gridOrigin[1],
539       gridOrigin[2] + this->Spacing[2] * (currentSlice - (currentSlice != 0)));
540 
541     for (i = currentIndex; i < chunkSize; i++)
542     {
543       slicePtr = volumePtr + i * gridSize[0] * gridSize[1];
544 
545       // Clear out the slice memory - set it all to a large negative
546       // value indicating no surfaces are nearby, and we assume we
547       // are outside of any surface
548       for (j = 0; j < gridSize[0] * gridSize[1]; j++)
549       {
550         *(slicePtr + j) = -9.99e10;
551       }
552 
553       // If we are past the end, don't do anything
554       if (currentSlice > lastSlice)
555       {
556         continue;
557       }
558 
559       this->LineListLength = 0;
560 
561       // Read in the lines for the contours on this slice
562       while (currentInputCellIndex < numberOfInputCells)
563       {
564         // Check if we are still on the right z slice
565         input->GetPoint(pts[0], point1);
566         if (point1[2] != currentZ)
567         {
568           break;
569         }
570 
571         // This contour is on the right z slice - add the lines
572         // to our list
573         for (j = 0; j < npts; j++)
574         {
575           input->GetPoint(pts[j], point1);
576           input->GetPoint(pts[(j + 1) % npts], point2);
577           this->AddLineToLineList(point1[0], point1[1], point2[0], point2[1]);
578         }
579 
580         inputPolys->GetNextCell(npts, pts);
581         currentInputCellIndex++;
582       }
583 
584       // Sort the contours in x and y
585       this->SortLineList();
586 
587       // Cast lines in x and y filling in distance
588       this->CastLines(slicePtr, gridOrigin, gridSize, 0);
589       this->CastLines(slicePtr, gridOrigin, gridSize, 1);
590 
591       // Move on to the next slice
592       currentSlice++;
593       currentIndex++;
594       currentZ += 1.0;
595     }
596 
597     this->PushDistances(volumePtr, gridSize, chunkSize);
598 
599     // Update the contour filter and grab the output
600     // Make a new output for it, then grab the output and
601     // add it to the append filter, then delete the output
602     // which is ok since it was registered by the appendFilter
603     contourOutput = vtkPolyData::New();
604     contourFilter->Update();
605     contourOutput->ShallowCopy(contourFilter->GetOutput());
606     appendFilter->AddInputData(contourOutput);
607     contourOutput->Delete();
608 
609     if (currentSlice <= lastSlice)
610     {
611       // Copy last slice to first slice
612       memcpy(volumePtr, volumePtr + (chunkSize - 1) * gridSize[0] * gridSize[1],
613         sizeof(float) * gridSize[0] * gridSize[1]);
614 
615       // reset currentIndex to 1
616       currentIndex = 1;
617     }
618   }
619 
620   appendFilter->Update();
621 
622   // Grab the appended data as the output to this filter
623   output->SetPoints(appendFilter->GetOutput()->GetPoints());
624   output->SetVerts(appendFilter->GetOutput()->GetVerts());
625   output->SetLines(appendFilter->GetOutput()->GetLines());
626   output->SetPolys(appendFilter->GetOutput()->GetPolys());
627   output->SetStrips(appendFilter->GetOutput()->GetStrips());
628   output->GetPointData()->PassData(appendFilter->GetOutput()->GetPointData());
629 
630   contourFilter->Delete();
631   appendFilter->Delete();
632   volume->Delete();
633 
634   return 1;
635 }
636 
PrintSelf(ostream & os,vtkIndent indent)637 void vtkVoxelContoursToSurfaceFilter::PrintSelf(ostream& os, vtkIndent indent)
638 {
639   this->Superclass::PrintSelf(os, indent);
640 
641   os << indent << "Memory Limit (in bytes): " << this->MemoryLimitInBytes << endl;
642 
643   os << indent << "Spacing: " << this->Spacing[0] << " " << this->Spacing[1] << " "
644      << this->Spacing[2] << endl;
645 }
646