1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkVolumeOutlineSource.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 "vtkVolumeOutlineSource.h"
16 
17 #include "vtkCellArray.h"
18 #include "vtkCellArrayIterator.h"
19 #include "vtkCellData.h"
20 #include "vtkDataSet.h"
21 #include "vtkDemandDrivenPipeline.h"
22 #include "vtkIdList.h"
23 #include "vtkImageData.h"
24 #include "vtkInformation.h"
25 #include "vtkInformationVector.h"
26 #include "vtkMath.h"
27 #include "vtkNew.h"
28 #include "vtkObjectFactory.h"
29 #include "vtkPoints.h"
30 #include "vtkPolyData.h"
31 #include "vtkStreamingDemandDrivenPipeline.h"
32 #include "vtkUnsignedCharArray.h"
33 #include "vtkVolumeMapper.h"
34 
35 vtkStandardNewMacro(vtkVolumeOutlineSource);
36 
37 vtkCxxSetObjectMacro(vtkVolumeOutlineSource, VolumeMapper, vtkVolumeMapper);
38 
39 //------------------------------------------------------------------------------
vtkVolumeOutlineSource()40 vtkVolumeOutlineSource::vtkVolumeOutlineSource()
41 {
42   this->VolumeMapper = nullptr;
43   this->GenerateScalars = 0;
44   this->GenerateOutline = 1;
45   this->GenerateFaces = 0;
46   this->ActivePlaneId = -1;
47 
48   this->Color[0] = 1.0;
49   this->Color[1] = 0.0;
50   this->Color[2] = 0.0;
51 
52   this->ActivePlaneColor[0] = 1.0;
53   this->ActivePlaneColor[1] = 1.0;
54   this->ActivePlaneColor[2] = 0.0;
55 
56   this->SetNumberOfInputPorts(0);
57 }
58 
59 //------------------------------------------------------------------------------
~vtkVolumeOutlineSource()60 vtkVolumeOutlineSource::~vtkVolumeOutlineSource()
61 {
62   if (this->VolumeMapper)
63   {
64     this->VolumeMapper->Delete();
65     this->VolumeMapper = nullptr;
66   }
67 }
68 
69 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)70 void vtkVolumeOutlineSource::PrintSelf(ostream& os, vtkIndent indent)
71 {
72   this->Superclass::PrintSelf(os, indent);
73 
74   os << indent << "VolumeMapper: ";
75   if (this->VolumeMapper)
76   {
77     os << this->VolumeMapper << "\n";
78   }
79   else
80   {
81     os << "(none)\n";
82   }
83 
84   os << indent << "GenerateFaces: " << (this->GenerateFaces ? "On\n" : "Off\n");
85 
86   os << indent << "GenerateOutline: " << (this->GenerateOutline ? "On\n" : "Off\n");
87 
88   os << indent << "GenerateScalars: " << (this->GenerateScalars ? "On\n" : "Off\n");
89 
90   os << indent << "Color: " << this->Color[0] << ", " << this->Color[1] << ", " << this->Color[2]
91      << "\n";
92 
93   os << indent << "ActivePlaneId: " << this->ActivePlaneId << "\n";
94 
95   os << indent << "ActivePlaneColor: " << this->ActivePlaneColor[0] << ", "
96      << this->ActivePlaneColor[1] << ", " << this->ActivePlaneColor[2] << "\n";
97 }
98 
99 //------------------------------------------------------------------------------
ComputeCubePlanes(double planes[3][4],double croppingPlanes[6],double bounds[6])100 int vtkVolumeOutlineSource::ComputeCubePlanes(
101   double planes[3][4], double croppingPlanes[6], double bounds[6])
102 {
103   // Combine the CroppingRegionPlanes and the Bounds to create
104   // a single array.  For each dimension, store the planes in
105   // the following order: lo_bound, lo_crop_plane, hi_crop_plane, hi_bound.
106   // Also do range checking to ensure that the cropping planes
107   // are clamped to the bound limits.
108 
109   for (int i = 0; i < 3; i++)
110   {
111     int j0 = 2 * i;
112     int j1 = 2 * i + 1;
113 
114     double a = bounds[j0];
115     double b = croppingPlanes[j0];
116     double c = croppingPlanes[j1];
117     double d = bounds[j1];
118 
119     // Sanity check
120     if (a > d || b > c)
121     {
122       return 0;
123     }
124 
125     // Clamp cropping planes to bounds
126     if (b < a)
127     {
128       b = a;
129     }
130     if (b > d)
131     {
132       b = d;
133     }
134     if (c < a)
135     {
136       c = a;
137     }
138     if (c > d)
139     {
140       c = d;
141     }
142 
143     planes[i][0] = a;
144     planes[i][1] = b;
145     planes[i][2] = c;
146     planes[i][3] = d;
147   }
148 
149   return 1;
150 }
151 
152 //------------------------------------------------------------------------------
ComputePipelineMTime(vtkInformation * vtkNotUsed (request),vtkInformationVector ** vtkNotUsed (inputVector),vtkInformationVector * vtkNotUsed (outputVector),int vtkNotUsed (requestFromOutputPort),vtkMTimeType * mtime)153 int vtkVolumeOutlineSource::ComputePipelineMTime(vtkInformation* vtkNotUsed(request),
154   vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* vtkNotUsed(outputVector),
155   int vtkNotUsed(requestFromOutputPort), vtkMTimeType* mtime)
156 {
157   vtkMTimeType mTime = this->GetMTime();
158   if (this->VolumeMapper)
159   {
160     vtkMTimeType mapperMTime = this->VolumeMapper->GetMTime();
161     if (mapperMTime > mTime)
162     {
163       mTime = mapperMTime;
164     }
165     vtkDemandDrivenPipeline* input =
166       vtkDemandDrivenPipeline::SafeDownCast(this->VolumeMapper->GetInputExecutive());
167     if (input)
168     {
169       // Need to do this because we are not formally connected
170       // to the Mapper's pipeline
171       input->UpdateInformation();
172       vtkMTimeType pipelineMTime = input->GetPipelineMTime();
173       if (pipelineMTime > mTime)
174       {
175         mTime = pipelineMTime;
176       }
177     }
178   }
179 
180   *mtime = mTime;
181 
182   return 1;
183 }
184 
185 //------------------------------------------------------------------------------
RequestInformation(vtkInformation * vtkNotUsed (request),vtkInformationVector ** vtkNotUsed (inputVector),vtkInformationVector * vtkNotUsed (outputVector))186 int vtkVolumeOutlineSource::RequestInformation(vtkInformation* vtkNotUsed(request),
187   vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* vtkNotUsed(outputVector))
188 {
189   // Get the mapper's input, since this is the most convenient
190   // place to do so.
191 
192   if (!this->VolumeMapper)
193   {
194     vtkWarningMacro("No VolumeMapper has been set.");
195     return 1;
196   }
197 
198   vtkInformation* mapInfo = this->VolumeMapper->GetInputInformation();
199 
200   if (!mapInfo)
201   {
202     vtkWarningMacro("The VolumeMapper does not have an input set.");
203     return 1;
204   }
205 
206   // Don't have to update mapper's input, since it was done in
207   // ComputePipelineMTime.
208   // data->UpdateInformation();
209 
210   // Don't call GetBounds because we need WholeExtent, while
211   // GetBounds only returns the bounds for Extent.
212 
213   double spacing[3];
214   double origin[3];
215   int extent[6];
216 
217   mapInfo->Get(vtkDataObject::SPACING(), spacing);
218   mapInfo->Get(vtkDataObject::ORIGIN(), origin);
219   mapInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), extent);
220 
221   for (int i = 0; i < 3; i++)
222   {
223     int j0 = 2 * i;
224     int j1 = j0 + 1;
225 
226     if (extent[j0] > extent[j1])
227     {
228       vtkMath::UninitializeBounds(this->Bounds);
229       break;
230     }
231 
232     if (spacing[i] > 0)
233     {
234       this->Bounds[j0] = origin[i] + spacing[i] * extent[j0];
235       this->Bounds[j1] = origin[i] + spacing[i] * extent[j1];
236     }
237     else
238     {
239       this->Bounds[j0] = origin[i] + spacing[i] * extent[j1];
240       this->Bounds[j1] = origin[i] + spacing[i] * extent[j0];
241     }
242 
243     this->CroppingRegionPlanes[j0] = this->Bounds[j0];
244     this->CroppingRegionPlanes[j1] = this->Bounds[j1];
245   }
246 
247   this->CroppingRegionFlags = 0x0002000;
248 
249   this->Cropping = this->VolumeMapper->GetCropping();
250   if (this->Cropping)
251   {
252     this->CroppingRegionFlags = this->VolumeMapper->GetCroppingRegionFlags();
253     this->VolumeMapper->GetCroppingRegionPlanes(this->CroppingRegionPlanes);
254   }
255 
256   return 1;
257 }
258 
259 //------------------------------------------------------------------------------
RequestData(vtkInformation * vtkNotUsed (request),vtkInformationVector ** vtkNotUsed (inputVector),vtkInformationVector * outputVector)260 int vtkVolumeOutlineSource::RequestData(vtkInformation* vtkNotUsed(request),
261   vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* outputVector)
262 {
263   // get the info object
264   vtkInformation* outInfo = outputVector->GetInformationObject(0);
265 
266   // get the output
267   vtkPolyData* output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));
268 
269   vtkDebugMacro(<< "Creating cropping region outline");
270 
271   // For each of the 3 dimensions, there are 4 planes: two bounding planes
272   // on the outside, and two cropping region planes inside.
273   double planes[3][4];
274 
275   if (!this->VolumeMapper || !this->VolumeMapper->GetInput() ||
276     !this->ComputeCubePlanes(planes, this->CroppingRegionPlanes, this->Bounds))
277   {
278     // If the bounds or the cropping planes are invalid, clear the data
279     output->SetPoints(nullptr);
280     output->SetLines(nullptr);
281     output->GetCellData()->SetScalars(nullptr);
282 
283     return 1;
284   }
285 
286   // Compute the tolerance for considering points or planes to be coincident
287   double tol = 0;
288   for (int planeDim = 0; planeDim < 3; planeDim++)
289   {
290     double d = planes[planeDim][3] - planes[planeDim][0];
291     tol += d * d;
292   }
293   tol = sqrt(tol) * 1e-5;
294 
295   // Create an array to nudge crop planes over to the bounds if they are
296   // within tolerance of the bounds
297   int tolPtId[3][4];
298   vtkVolumeOutlineSource::NudgeCropPlanesToBounds(tolPtId, planes, tol);
299 
300   // The all-important cropping flags
301   int flags = this->CroppingRegionFlags;
302 
303   // The active plane, which gets a special color for its scalars
304   int activePlane = this->ActivePlaneId;
305   if (activePlane > 5)
306   {
307     activePlane = -1;
308   }
309 
310   // Convert the colors to unsigned char for scalars
311   unsigned char colors[2][3];
312   vtkVolumeOutlineSource::CreateColorValues(colors, this->Color, this->ActivePlaneColor);
313 
314   // Create the scalars used to color the lines
315   vtkUnsignedCharArray* scalars = nullptr;
316 
317   if (this->GenerateScalars)
318   {
319     scalars = vtkUnsignedCharArray::New();
320     scalars->SetNumberOfComponents(3);
321   }
322 
323   // Generate all the lines for the outline.
324   vtkCellArray* lines = nullptr;
325 
326   if (this->GenerateOutline)
327   {
328     lines = vtkCellArray::New();
329     vtkVolumeOutlineSource::GenerateLines(lines, scalars, colors, activePlane, flags, tolPtId);
330   }
331 
332   // Generate the polys for the outline
333   vtkCellArray* polys = nullptr;
334 
335   if (this->GenerateFaces)
336   {
337     polys = vtkCellArray::New();
338     vtkVolumeOutlineSource::GeneratePolys(polys, scalars, colors, activePlane, flags, tolPtId);
339   }
340 
341   // Generate the points that are used by the lines.
342   vtkPoints* points = vtkPoints::New();
343   vtkVolumeOutlineSource::GeneratePoints(points, lines, polys, planes, tol);
344 
345   output->SetPoints(points);
346   points->Delete();
347 
348   output->SetPolys(polys);
349   if (polys)
350   {
351     polys->Delete();
352   }
353 
354   output->SetLines(lines);
355   if (lines)
356   {
357     lines->Delete();
358   }
359 
360   output->GetCellData()->SetScalars(scalars);
361   if (scalars)
362   {
363     scalars->Delete();
364   }
365 
366   return 1;
367 }
368 
369 //------------------------------------------------------------------------------
GeneratePolys(vtkCellArray * polys,vtkUnsignedCharArray * scalars,unsigned char colors[2][3],int activePlane,int flags,int tolPtId[3][4])370 void vtkVolumeOutlineSource::GeneratePolys(vtkCellArray* polys, vtkUnsignedCharArray* scalars,
371   unsigned char colors[2][3], int activePlane, int flags, int tolPtId[3][4])
372 {
373   // Loop over the three dimensions and create the face rectangles
374   for (int dim0 = 0; dim0 < 3; dim0++)
375   {
376     // Compute the other two dimension indices
377     int dim1 = (dim0 + 1) % 3;
378     int dim2 = (dim0 + 2) % 3;
379 
380     // Indices into the cubes
381     int idx[3];
382 
383     // Loop over the "dim+2" dimension
384     for (int i = 0; i < 4; i++)
385     {
386       idx[dim2] = i;
387 
388       // Loop over the "dim+1" dimension
389       for (int j = 0; j < 3; j++)
390       {
391         idx[dim1] = j;
392 
393         // Make sure that the rect dim is not less than tolerance
394         if ((j == 0 && tolPtId[dim1][1] == 0) || (j == 2 && tolPtId[dim1][2] == 3))
395         {
396           continue;
397         }
398 
399         // Loop over rectangle along the "dim" dimension
400         for (int k = 0; k < 3; k++)
401         {
402           idx[dim0] = k;
403 
404           // Make sure that the rect dim is not less than tolerance
405           if ((k == 0 && tolPtId[dim0][1] == 0) || (k == 2 && tolPtId[dim0][2] == 3))
406           {
407             continue;
408           }
409 
410           // The points in the rectangle, which are nudged over to the
411           // volume bounds if the cropping planes are within tolerance
412           // of the volume bounds.
413           int pointId[4];
414           pointId[0] = (tolPtId[2][idx[2]] * 16 + tolPtId[1][idx[1]] * 4 + tolPtId[0][idx[0]]);
415           idx[dim0] = k + 1;
416           pointId[1] = (tolPtId[2][idx[2]] * 16 + tolPtId[1][idx[1]] * 4 + tolPtId[0][idx[0]]);
417           idx[dim1] = j + 1;
418           pointId[2] = (tolPtId[2][idx[2]] * 16 + tolPtId[1][idx[1]] * 4 + tolPtId[0][idx[0]]);
419           idx[dim0] = k;
420           pointId[3] = (tolPtId[2][idx[2]] * 16 + tolPtId[1][idx[1]] * 4 + tolPtId[0][idx[0]]);
421           idx[dim1] = j;
422 
423           // Loop through the two cubes adjacent to the rectangle,
424           // in order to determine whether the rectangle is internal:
425           // only external faces will be drawn.  The "bitCheck"
426           // holds a bit for each of these two cubes.
427           int bitCheck = 0;
428           int cidx[3];
429           cidx[dim0] = idx[dim0];
430           cidx[dim1] = idx[dim1];
431           for (int ii = 0; ii < 2; ii++)
432           {
433             // First get idx[dim2]-1, then idx[dim2]
434             cidx[dim2] = idx[dim2] + ii - 1;
435             int flagval = 0;
436             if (cidx[dim2] >= 0 && cidx[dim2] < 3)
437             {
438               int flagbit = cidx[2] * 9 + cidx[1] * 3 + cidx[0];
439               flagval = ((flags >> flagbit) & 1);
440             }
441             bitCheck <<= 1;
442             bitCheck |= flagval;
443           }
444 
445           // Whether we need to create a face depends on bitCheck.
446           // Values 00, 11 don't need lines, while 01 and 10 do.
447 
448           // If our rect isn't an internal rect
449           if (bitCheck != 0x0 && bitCheck != 0x3)
450           {
451             // Check if the rect is on our active plane
452             int active = 0;
453             if (activePlane >= 0)
454             {
455               int planeDim = (activePlane >> 1);    // same as "/ 2"
456               int planeIdx = 1 + (activePlane & 1); // same as "% 2"
457               if (planeDim == dim2 && i == planeIdx)
458               {
459                 active = 1;
460               }
461             }
462 
463             // Insert the rectangle with the correct sense
464             polys->InsertNextCell(4);
465             if (bitCheck == 0x2)
466             {
467               polys->InsertCellPoint(pointId[0]);
468               polys->InsertCellPoint(pointId[1]);
469               polys->InsertCellPoint(pointId[2]);
470               polys->InsertCellPoint(pointId[3]);
471             }
472             else // (bitCheck == 0x1)
473             {
474               polys->InsertCellPoint(pointId[3]);
475               polys->InsertCellPoint(pointId[2]);
476               polys->InsertCellPoint(pointId[1]);
477               polys->InsertCellPoint(pointId[0]);
478             }
479 
480             // Color the face
481             if (scalars)
482             {
483               scalars->InsertNextTypedTuple(colors[active]);
484             }
485           }
486 
487         } // loop over k
488       }   // loop over j
489     }     // loop over i
490   }       // loop over dim0
491 }
492 
493 //------------------------------------------------------------------------------
GenerateLines(vtkCellArray * lines,vtkUnsignedCharArray * scalars,unsigned char colors[2][3],int activePlane,int flags,int tolPtId[3][4])494 void vtkVolumeOutlineSource::GenerateLines(vtkCellArray* lines, vtkUnsignedCharArray* scalars,
495   unsigned char colors[2][3], int activePlane, int flags, int tolPtId[3][4])
496 {
497   // Loop over the three dimensions and create the lines
498   for (int dim0 = 0; dim0 < 3; dim0++)
499   {
500     // Compute the other two dimension indices
501     int dim1 = (dim0 + 1) % 3;
502     int dim2 = (dim0 + 2) % 3;
503 
504     // Indices into the cubes
505     int idx[3];
506 
507     // Loop over the "dim+2" dimension
508     for (int i = 0; i < 4; i++)
509     {
510       idx[dim2] = i;
511 
512       // Loop over the "dim+1" dimension
513       for (int j = 0; j < 4; j++)
514       {
515         idx[dim1] = j;
516 
517         // Loop over line segments along the "dim" dimension
518         for (int k = 0; k < 3; k++)
519         {
520           idx[dim0] = k;
521 
522           // Make sure that the segment length is not less than tolerance
523           if ((k == 0 && tolPtId[dim0][1] == 0) || (k == 2 && tolPtId[dim0][2] == 3))
524           {
525             continue;
526           }
527 
528           // The endpoints of the segment, which are nudged over to the
529           // volume bounds if the cropping planes are within tolerance
530           // of the volume bounds.
531           int pointId0 = (tolPtId[2][idx[2]] * 16 + tolPtId[1][idx[1]] * 4 + tolPtId[0][idx[0]]);
532           idx[dim0] = k + 1;
533           int pointId1 = (tolPtId[2][idx[2]] * 16 + tolPtId[1][idx[1]] * 4 + tolPtId[0][idx[0]]);
534           idx[dim0] = k;
535 
536           // Loop through the four cubes adjacent to the line segment,
537           // in order to determine whether the line segment is on an
538           // edge: only the edge lines will be drawn.  The "bitCheck"
539           // holds a bit for each of these four cubes.
540           int bitCheck = 0;
541           int cidx[3];
542           cidx[dim0] = idx[dim0];
543           for (int ii = 0; ii < 2; ii++)
544           {
545             // First get idx[dim1]-1, then idx[dim1]
546             cidx[dim1] = idx[dim1] + ii - 1;
547             for (int jj = 0; jj < 2; jj++)
548             {
549               // First get idx[dim2]-1, then idx[dim2], but reverse
550               // the order when ii loop is on its second iteration
551               cidx[dim2] = idx[dim2] + (ii ^ jj) - 1;
552               int flagval = 0;
553               if (cidx[dim1] >= 0 && cidx[dim1] < 3 && cidx[dim2] >= 0 && cidx[dim2] < 3)
554               {
555                 int flagbit = cidx[2] * 9 + cidx[1] * 3 + cidx[0];
556                 flagval = ((flags >> flagbit) & 1);
557               }
558               bitCheck <<= 1;
559               bitCheck |= flagval;
560             }
561           }
562 
563           // Whether we need a line depends on the value of bitCheck.
564           // Values 0000, 0011, 0110, 1100, 1001, 1111 don't need lines.
565           // Build a bitfield to check our bitfield values against, each
566           // set bit in this new bitfield corresponds to a non-edge case.
567           const int noLineValues =
568             ((1 << 0x0) | (1 << 0x3) | (1 << 0x6) | (1 << 0x9) | (1 << 0xc) | (1 << 0xf));
569 
570           // If our line segment is an edge, there is lots of work to do.
571           if (((noLineValues >> bitCheck) & 1) == 0)
572           {
573             // Check if the line segment is on our active plane
574             int active = 0;
575             if (activePlane >= 0)
576             {
577               int planeDim = (activePlane >> 1);    // same as "/ 2"
578               int planeIdx = 1 + (activePlane & 1); // same as "% 2"
579               if ((planeDim == dim2 && i == planeIdx) || (planeDim == dim1 && j == planeIdx))
580               {
581                 active = 1;
582               }
583             }
584 
585             // Check to make sure line segment isn't already there
586             int foundDuplicate = 0;
587             lines->InitTraversal();
588             vtkIdType npts;
589             const vtkIdType* pts;
590             for (int cellId = 0; lines->GetNextCell(npts, pts); cellId++)
591             {
592               if (pts[0] == pointId0 && pts[1] == pointId1)
593               {
594                 // Change color if current segment is on active plane
595                 if (scalars && active)
596                 {
597                   scalars->SetTypedTuple(cellId, colors[active]);
598                 }
599                 foundDuplicate = 1;
600                 break;
601               }
602             }
603 
604             if (!foundDuplicate)
605             {
606               // Insert the line segment
607               lines->InsertNextCell(2);
608               lines->InsertCellPoint(pointId0);
609               lines->InsertCellPoint(pointId1);
610 
611               // Color the line segment
612               if (scalars)
613               {
614                 scalars->InsertNextTypedTuple(colors[active]);
615               }
616             }
617           }
618 
619         } // loop over k
620       }   // loop over j
621     }     // loop over i
622   }       // loop over dim0
623 }
624 
625 //------------------------------------------------------------------------------
GeneratePoints(vtkPoints * points,vtkCellArray * lines,vtkCellArray * polys,double planes[3][4],double tol)626 void vtkVolumeOutlineSource::GeneratePoints(
627   vtkPoints* points, vtkCellArray* lines, vtkCellArray* polys, double planes[3][4], double tol)
628 {
629   // Use a bitfield to store which of the 64 points we need.
630   // Two 32-bit ints are a convenient, portable way to do this.
631   unsigned int pointBits1 = 0;
632   unsigned int pointBits2 = 0;
633 
634   vtkIdType npts;
635   const vtkIdType* pts;
636   vtkCellArray* cellArrays[2];
637   cellArrays[0] = lines;
638   cellArrays[1] = polys;
639 
640   for (int arrayId = 0; arrayId < 2; arrayId++)
641   {
642     if (cellArrays[arrayId])
643     {
644       cellArrays[arrayId]->InitTraversal();
645       while (cellArrays[arrayId]->GetNextCell(npts, pts))
646       {
647         for (int ii = 0; ii < npts; ii++)
648         {
649           int pointId = pts[ii];
650           if (pointId < 32)
651           {
652             pointBits1 |= (1 << pointId);
653           }
654           else
655           {
656             pointBits2 |= (1 << (pointId - 32));
657           }
658         }
659       }
660     }
661   }
662 
663   // Create the array of up to 64 points, and use the pointBits bitfield
664   // to find out which points were used.  It is also necessary to go through
665   // and update the cells with the modified point ids.
666   unsigned int pointBits = pointBits1;
667   int ptId = 0;
668   int newPtId = 0;
669 
670   vtkNew<vtkIdList> repCell;
671   for (int i = 0; i < 4; i++)
672   {
673     // If we're halfway done, switch over to the next 32 bits
674     if (i == 2)
675     {
676       pointBits = pointBits2;
677     }
678 
679     for (int j = 0; j < 4; j++)
680     {
681       for (int k = 0; k < 4; k++)
682       {
683         // Check to see if this point was actually used
684         if ((pointBits & 1))
685         {
686           // Add or subtract tolerance as an offset to help depth check
687           double x = planes[0][k] + tol * (1 - 2 * (k < 2));
688           double y = planes[1][j] + tol * (1 - 2 * (j < 2));
689           double z = planes[2][i] + tol * (1 - 2 * (i < 2));
690 
691           points->InsertNextPoint(x, y, z);
692 
693           for (int arrayId = 0; arrayId < 2; arrayId++)
694           {
695             // Go through the cells, substitute old Id for new Id
696             vtkCellArray* cells = cellArrays[arrayId];
697             if (cells)
698             {
699               auto cellIter = vtk::TakeSmartPointer(cells->NewIterator());
700               for (cellIter->GoToFirstCell(); !cellIter->IsDoneWithTraversal();
701                    cellIter->GoToNextCell())
702               {
703                 cellIter->GetCurrentCell(repCell);
704                 for (int ii = 0; ii < repCell->GetNumberOfIds(); ii++)
705                 {
706                   if (repCell->GetId(ii) == ptId)
707                   {
708                     repCell->SetId(ii, newPtId);
709                   }
710                 }
711                 cellIter->ReplaceCurrentCell(repCell);
712               }
713             }
714           }
715           newPtId++;
716         }
717         pointBits >>= 1;
718         ptId++;
719       }
720     }
721   }
722 }
723 
724 //------------------------------------------------------------------------------
NudgeCropPlanesToBounds(int tolPtId[3][4],double planes[3][4],double tol)725 void vtkVolumeOutlineSource::NudgeCropPlanesToBounds(
726   int tolPtId[3][4], double planes[3][4], double tol)
727 {
728   for (int dim = 0; dim < 3; dim++)
729   {
730     tolPtId[dim][0] = 0;
731     tolPtId[dim][1] = 1;
732     tolPtId[dim][2] = 2;
733     tolPtId[dim][3] = 3;
734     if (planes[dim][1] - planes[dim][0] < tol)
735     {
736       tolPtId[dim][1] = 0;
737     }
738     if (planes[dim][3] - planes[dim][2] < tol)
739     {
740       tolPtId[dim][2] = 3;
741     }
742   }
743 }
744 
745 //------------------------------------------------------------------------------
CreateColorValues(unsigned char colors[2][3],double color1[3],double color2[3])746 void vtkVolumeOutlineSource::CreateColorValues(
747   unsigned char colors[2][3], double color1[3], double color2[3])
748 {
749   // Convert the two colors to unsigned char
750   double* dcolors[2];
751   dcolors[0] = color1;
752   dcolors[1] = color2;
753 
754   for (int i = 0; i < 2; i++)
755   {
756     for (int j = 0; j < 3; j++)
757     {
758       double val = dcolors[i][j];
759       if (val < 0)
760       {
761         val = 0;
762       }
763       if (val > 1)
764       {
765         val = 1;
766       }
767       colors[i][j] = static_cast<unsigned char>(val * 255);
768     }
769   }
770 }
771