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