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