1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkShepardMethod.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 "vtkShepardMethod.h"
16 
17 #include "vtkFloatArray.h"
18 #include "vtkImageData.h"
19 #include "vtkInformation.h"
20 #include "vtkInformationVector.h"
21 #include "vtkMath.h"
22 #include "vtkObjectFactory.h"
23 #include "vtkPointData.h"
24 #include "vtkSMPTools.h"
25 #include "vtkStreamingDemandDrivenPipeline.h"
26 
27 vtkStandardNewMacro(vtkShepardMethod);
28 
29 //------------------------------------------------------------------------------
30 // Thread the algorithm by processing each z-slice independently as each
31 // point is procssed. (As input points are processed, their influence is felt
32 // across a cuboid domain - a splat footprint. The slices that make up the
33 // cuboid splat are processed in parallel.) Note also that the scalar data is
34 // processed via templating.
35 class vtkShepardAlgorithm
36 {
37 public:
38   int* Dims;
39   vtkIdType SliceSize;
40   double *Origin, *Spacing;
41   float* OutScalars;
42   double* Sum;
43 
vtkShepardAlgorithm(double * origin,double * spacing,int * dims,float * outS,double * sum)44   vtkShepardAlgorithm(double* origin, double* spacing, int* dims, float* outS, double* sum)
45     : Dims(dims)
46     , Origin(origin)
47     , Spacing(spacing)
48     , OutScalars(outS)
49     , Sum(sum)
50   {
51     this->SliceSize = this->Dims[0] * this->Dims[1];
52   }
53 
54   class SplatP2
55   {
56   public:
57     vtkShepardAlgorithm* Algo;
58     vtkIdType XMin, XMax, YMin, YMax, ZMin, ZMax;
59     double S, X[3];
SplatP2(vtkShepardAlgorithm * algo)60     SplatP2(vtkShepardAlgorithm* algo)
61       : Algo(algo)
62     {
63     }
SetBounds(vtkIdType min[3],vtkIdType max[3])64     void SetBounds(vtkIdType min[3], vtkIdType max[3])
65     {
66       this->XMin = min[0];
67       this->XMax = max[0];
68       this->YMin = min[1];
69       this->YMax = max[1];
70       this->ZMin = min[2];
71       this->ZMax = max[2];
72     }
operator ()(vtkIdType slice,vtkIdType end)73     void operator()(vtkIdType slice, vtkIdType end)
74     {
75       vtkIdType i, j, jOffset, kOffset, idx;
76       double cx[3], distance2, *sum = this->Algo->Sum;
77       float* outS = this->Algo->OutScalars;
78       const double* origin = this->Algo->Origin;
79       const double* spacing = this->Algo->Spacing;
80       for (; slice < end; ++slice)
81       {
82         // Loop over all sample points in volume within footprint and
83         // evaluate the splat
84         cx[2] = origin[2] + spacing[2] * slice;
85         kOffset = slice * this->Algo->SliceSize;
86         for (j = this->YMin; j <= this->YMax; j++)
87         {
88           cx[1] = origin[1] + spacing[1] * j;
89           jOffset = j * this->Algo->Dims[0];
90           for (i = this->XMin; i <= this->XMax; i++)
91           {
92             idx = kOffset + jOffset + i;
93             cx[0] = origin[0] + spacing[0] * i;
94 
95             distance2 = vtkMath::Distance2BetweenPoints(this->X, cx);
96 
97             // When the sample point and interpolated point are coincident,
98             // then the interpolated point takes on the value of the sample
99             // point.
100             if (distance2 == 0.0)
101             {
102               sum[idx] = VTK_DOUBLE_MAX; // mark the point as hit
103               outS[idx] = this->S;
104             }
105             else if (sum[idx] < VTK_DOUBLE_MAX)
106             {
107               sum[idx] += 1.0 / distance2;
108               outS[idx] += this->S / distance2;
109             }
110 
111           } // i
112         }   // j
113       }     // k within splat footprint
114     }
115   };
116 
117   class SplatPN
118   {
119   public:
120     vtkShepardAlgorithm* Algo;
121     vtkIdType XMin, XMax, YMin, YMax, ZMin, ZMax;
122     double P, S, X[3];
SplatPN(vtkShepardAlgorithm * algo,double p)123     SplatPN(vtkShepardAlgorithm* algo, double p)
124       : Algo(algo)
125       , P(p)
126     {
127     }
SetBounds(vtkIdType min[3],vtkIdType max[3])128     void SetBounds(vtkIdType min[3], vtkIdType max[3])
129     {
130       this->XMin = min[0];
131       this->XMax = max[0];
132       this->YMin = min[1];
133       this->YMax = max[1];
134       this->ZMin = min[2];
135       this->ZMax = max[2];
136     }
operator ()(vtkIdType slice,vtkIdType end)137     void operator()(vtkIdType slice, vtkIdType end)
138     {
139       vtkIdType i, j, jOffset, kOffset, idx;
140       double cx[3], distance, dp, *sum = this->Algo->Sum;
141       float* outS = this->Algo->OutScalars;
142       const double* origin = this->Algo->Origin;
143       const double* spacing = this->Algo->Spacing;
144       for (; slice < end; ++slice)
145       {
146         // Loop over all sample points in volume within footprint and
147         // evaluate the splat
148         cx[2] = origin[2] + spacing[2] * slice;
149         kOffset = slice * this->Algo->SliceSize;
150         for (j = this->YMin; j <= this->YMax; j++)
151         {
152           cx[1] = origin[1] + spacing[1] * j;
153           jOffset = j * this->Algo->Dims[0];
154           for (i = this->XMin; i <= this->XMax; i++)
155           {
156             idx = kOffset + jOffset + i;
157             cx[0] = origin[0] + spacing[0] * i;
158 
159             distance = sqrt(vtkMath::Distance2BetweenPoints(this->X, cx));
160 
161             // When the sample point and interpolated point are coincident,
162             // then the interpolated point takes on the value of the sample
163             // point.
164             if (distance == 0.0)
165             {
166               sum[idx] = VTK_DOUBLE_MAX; // mark the point as hit
167               outS[idx] = this->S;
168             }
169             else if (sum[idx] < VTK_DOUBLE_MAX)
170             {
171               dp = pow(distance, this->P);
172               sum[idx] += 1.0 / dp;
173               outS[idx] += this->S / dp;
174             }
175 
176           } // i
177         }   // j
178       }     // k within splat footprint
179     }
180   };
181 
182   class Interpolate
183   {
184   public:
185     vtkShepardAlgorithm* Algo;
186     double NullValue;
Interpolate(vtkShepardAlgorithm * algo,double nullV)187     Interpolate(vtkShepardAlgorithm* algo, double nullV)
188       : Algo(algo)
189       , NullValue(nullV)
190     {
191     }
operator ()(vtkIdType ptId,vtkIdType endPtId)192     void operator()(vtkIdType ptId, vtkIdType endPtId)
193     {
194       float* outS = this->Algo->OutScalars;
195       const double* sum = this->Algo->Sum;
196       for (; ptId < endPtId; ++ptId)
197       {
198         if (sum[ptId] >= VTK_DOUBLE_MAX)
199         {
200           ; // previously set, precise hit
201         }
202         else if (sum[ptId] != 0.0)
203         {
204           outS[ptId] /= sum[ptId];
205         }
206         else
207         {
208           outS[ptId] = this->NullValue;
209         }
210       }
211     }
212   };
213 }; // Shepard algorithm
214 
215 //------------------------------------------------------------------------------
216 // Construct with sample dimensions=(50,50,50) and so that model bounds are
217 // automatically computed from input. Null value for each unvisited output
218 // point is 0.0. Maximum distance is 0.25.
vtkShepardMethod()219 vtkShepardMethod::vtkShepardMethod()
220 {
221   this->MaximumDistance = 0.25;
222 
223   this->ModelBounds[0] = 0.0;
224   this->ModelBounds[1] = 0.0;
225   this->ModelBounds[2] = 0.0;
226   this->ModelBounds[3] = 0.0;
227   this->ModelBounds[4] = 0.0;
228   this->ModelBounds[5] = 0.0;
229 
230   this->SampleDimensions[0] = 50;
231   this->SampleDimensions[1] = 50;
232   this->SampleDimensions[2] = 50;
233 
234   this->NullValue = 0.0;
235 
236   this->PowerParameter = 2.0;
237 }
238 
239 //------------------------------------------------------------------------------
240 // Compute ModelBounds from input geometry.
ComputeModelBounds(double origin[3],double spacing[3])241 double vtkShepardMethod::ComputeModelBounds(double origin[3], double spacing[3])
242 {
243   const double* bounds;
244   double maxDist;
245   int i, adjustBounds = 0;
246 
247   // compute model bounds if not set previously
248   if (this->ModelBounds[0] >= this->ModelBounds[1] ||
249     this->ModelBounds[2] >= this->ModelBounds[3] || this->ModelBounds[4] >= this->ModelBounds[5])
250   {
251     adjustBounds = 1;
252     vtkDataSet* ds = vtkDataSet::SafeDownCast(this->GetInput());
253     // ds better be non null otherwise something is very wrong here
254     bounds = ds->GetBounds();
255   }
256   else
257   {
258     bounds = this->ModelBounds;
259   }
260 
261   for (maxDist = 0.0, i = 0; i < 3; i++)
262   {
263     if ((bounds[2 * i + 1] - bounds[2 * i]) > maxDist)
264     {
265       maxDist = bounds[2 * i + 1] - bounds[2 * i];
266     }
267   }
268   maxDist *= this->MaximumDistance;
269 
270   // adjust bounds so model fits strictly inside (only if not set previously)
271   if (adjustBounds)
272   {
273     for (i = 0; i < 3; i++)
274     {
275       this->ModelBounds[2 * i] = bounds[2 * i] - maxDist;
276       this->ModelBounds[2 * i + 1] = bounds[2 * i + 1] + maxDist;
277     }
278   }
279 
280   // Set volume origin and data spacing
281   for (i = 0; i < 3; i++)
282   {
283     origin[i] = this->ModelBounds[2 * i];
284     spacing[i] =
285       (this->ModelBounds[2 * i + 1] - this->ModelBounds[2 * i]) / (this->SampleDimensions[i] - 1);
286   }
287 
288   return maxDist;
289 }
290 
291 //------------------------------------------------------------------------------
RequestInformation(vtkInformation * vtkNotUsed (request),vtkInformationVector ** vtkNotUsed (inputVector),vtkInformationVector * outputVector)292 int vtkShepardMethod::RequestInformation(vtkInformation* vtkNotUsed(request),
293   vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* outputVector)
294 {
295   // get the info objects
296   vtkInformation* outInfo = outputVector->GetInformationObject(0);
297 
298   int i;
299   double ar[3], origin[3];
300 
301   outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), 0, this->SampleDimensions[0] - 1,
302     0, this->SampleDimensions[1] - 1, 0, this->SampleDimensions[2] - 1);
303 
304   for (i = 0; i < 3; i++)
305   {
306     origin[i] = this->ModelBounds[2 * i];
307     if (this->SampleDimensions[i] <= 1)
308     {
309       ar[i] = 1;
310     }
311     else
312     {
313       ar[i] =
314         (this->ModelBounds[2 * i + 1] - this->ModelBounds[2 * i]) / (this->SampleDimensions[i] - 1);
315     }
316   }
317   outInfo->Set(vtkDataObject::ORIGIN(), origin, 3);
318   outInfo->Set(vtkDataObject::SPACING(), ar, 3);
319 
320   vtkDataObject::SetPointDataActiveScalarInfo(outInfo, VTK_FLOAT, 1);
321   return 1;
322 }
323 
324 //------------------------------------------------------------------------------
RequestData(vtkInformation * vtkNotUsed (request),vtkInformationVector ** inputVector,vtkInformationVector * outputVector)325 int vtkShepardMethod::RequestData(vtkInformation* vtkNotUsed(request),
326   vtkInformationVector** inputVector, vtkInformationVector* outputVector)
327 {
328   // get the input
329   vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
330   vtkDataSet* input = vtkDataSet::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()));
331 
332   // get the output
333   vtkInformation* outInfo = outputVector->GetInformationObject(0);
334   vtkImageData* output = vtkImageData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));
335 
336   // We need to allocate our own scalars since we are overriding
337   // the superclasses "Execute()" method.
338   output->SetExtent(outInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()));
339   output->AllocateScalars(outInfo);
340 
341   vtkIdType ptId, i;
342   double *sum, spacing[3], origin[3];
343   double maxDistance;
344   vtkDataArray* inScalars;
345   vtkIdType numPts, numNewPts;
346   vtkIdType min[3], max[3];
347   vtkFloatArray* newScalars = vtkArrayDownCast<vtkFloatArray>(output->GetPointData()->GetScalars());
348 
349   vtkDebugMacro(<< "Executing Shepard method");
350 
351   // Check input
352   //
353   if ((numPts = input->GetNumberOfPoints()) < 1)
354   {
355     vtkErrorMacro(<< "Points must be defined!");
356     return 1;
357   }
358 
359   if ((inScalars = input->GetPointData()->GetScalars()) == nullptr)
360   {
361     vtkErrorMacro(<< "Scalars must be defined!");
362     return 1;
363   }
364   float* newS = static_cast<float*>(newScalars->GetVoidPointer(0));
365 
366   newScalars->SetName(inScalars->GetName());
367 
368   // Allocate and set up output
369   //
370   numNewPts = this->SampleDimensions[0] * this->SampleDimensions[1] * this->SampleDimensions[2];
371 
372   sum = new double[numNewPts];
373   std::fill_n(sum, numNewPts, 0.0);
374   std::fill_n(newS, numNewPts, 0.0);
375 
376   maxDistance = this->ComputeModelBounds(origin, spacing);
377   outInfo->Set(vtkDataObject::ORIGIN(), origin, 3);
378   outInfo->Set(vtkDataObject::SPACING(), spacing, 3);
379 
380   // Could easily be templated for output scalar type
381   vtkShepardAlgorithm algo(origin, spacing, this->SampleDimensions, newS, sum);
382 
383   // Traverse all input points. Depending on power parameter
384   // different paths are taken.
385   //
386   if (this->PowerParameter == 2.0) // distance2
387   {
388     vtkShepardAlgorithm::SplatP2 splatF(&algo);
389     for (ptId = 0; ptId < numPts; ptId++)
390     {
391       if (!(ptId % 1000))
392       {
393         vtkDebugMacro(<< "Inserting point #" << ptId);
394         this->UpdateProgress(ptId / numPts);
395         if (this->GetAbortExecute())
396         {
397           break;
398         }
399       }
400 
401       input->GetPoint(ptId, splatF.X);
402       splatF.S = inScalars->GetComponent(ptId, 0);
403 
404       for (i = 0; i < 3; i++) // compute dimensional bounds in data set
405       {
406         min[i] = static_cast<int>(
407           static_cast<double>((splatF.X[i] - maxDistance) - origin[i]) / spacing[i]);
408         max[i] = static_cast<int>(
409           static_cast<double>((splatF.X[i] + maxDistance) - origin[i]) / spacing[i]);
410         min[i] = (min[i] < 0 ? 0 : min[i]);
411         max[i] = (max[i] >= this->SampleDimensions[i] ? this->SampleDimensions[i] - 1 : max[i]);
412       }
413 
414       splatF.SetBounds(min, max);
415       vtkSMPTools::For(min[2], max[2] + 1, splatF);
416     }
417   } // power parameter p=2
418 
419   else // have to take roots etc so it runs slower
420   {
421     vtkShepardAlgorithm::SplatPN splatF(&algo, this->PowerParameter);
422     for (ptId = 0; ptId < numPts; ptId++)
423     {
424       if (!(ptId % 1000))
425       {
426         vtkDebugMacro(<< "Inserting point #" << ptId);
427         this->UpdateProgress(ptId / numPts);
428         if (this->GetAbortExecute())
429         {
430           break;
431         }
432       }
433 
434       input->GetPoint(ptId, splatF.X);
435       splatF.S = inScalars->GetComponent(ptId, 0);
436 
437       for (i = 0; i < 3; i++) // compute dimensional bounds in data set
438       {
439         min[i] = static_cast<int>(
440           static_cast<double>((splatF.X[i] - maxDistance) - origin[i]) / spacing[i]);
441         max[i] = static_cast<int>(
442           static_cast<double>((splatF.X[i] + maxDistance) - origin[i]) / spacing[i]);
443         min[i] = (min[i] < 0 ? 0 : min[i]);
444         max[i] = (max[i] >= this->SampleDimensions[i] ? this->SampleDimensions[i] - 1 : max[i]);
445       }
446 
447       splatF.SetBounds(min, max);
448       vtkSMPTools::For(min[2], max[2] + 1, splatF);
449     }
450   } // p != 2
451 
452   // Run through scalars and compute final values
453   //
454   vtkShepardAlgorithm::Interpolate interpolate(&algo, this->NullValue);
455   vtkSMPTools::For(0, numNewPts, interpolate);
456 
457   // Clean up
458   //
459   delete[] sum;
460 
461   return 1;
462 }
463 
464 //------------------------------------------------------------------------------
465 // Set the i-j-k dimensions on which to sample the distance function.
SetSampleDimensions(int i,int j,int k)466 void vtkShepardMethod::SetSampleDimensions(int i, int j, int k)
467 {
468   int dim[3];
469 
470   dim[0] = i;
471   dim[1] = j;
472   dim[2] = k;
473 
474   this->SetSampleDimensions(dim);
475 }
476 
477 //------------------------------------------------------------------------------
478 // Set the i-j-k dimensions on which to sample the distance function.
SetSampleDimensions(int dim[3])479 void vtkShepardMethod::SetSampleDimensions(int dim[3])
480 {
481   int dataDim, i;
482 
483   vtkDebugMacro(<< " setting SampleDimensions to (" << dim[0] << "," << dim[1] << "," << dim[2]
484                 << ")");
485 
486   if (dim[0] != this->SampleDimensions[0] || dim[1] != this->SampleDimensions[1] ||
487     dim[2] != this->SampleDimensions[2])
488   {
489     if (dim[0] < 1 || dim[1] < 1 || dim[2] < 1)
490     {
491       vtkErrorMacro(<< "Bad Sample Dimensions, retaining previous values");
492       return;
493     }
494 
495     for (dataDim = 0, i = 0; i < 3; i++)
496     {
497       if (dim[i] > 1)
498       {
499         dataDim++;
500       }
501     }
502 
503     if (dataDim < 3)
504     {
505       vtkErrorMacro(<< "Sample dimensions must define a 3D volume!");
506       return;
507     }
508 
509     for (i = 0; i < 3; i++)
510     {
511       this->SampleDimensions[i] = dim[i];
512     }
513 
514     this->Modified();
515   }
516 }
517 
518 //------------------------------------------------------------------------------
FillInputPortInformation(int vtkNotUsed (port),vtkInformation * info)519 int vtkShepardMethod::FillInputPortInformation(int vtkNotUsed(port), vtkInformation* info)
520 {
521   info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet");
522   return 1;
523 }
524 
525 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)526 void vtkShepardMethod::PrintSelf(ostream& os, vtkIndent indent)
527 {
528   this->Superclass::PrintSelf(os, indent);
529 
530   os << indent << "Maximum Distance: " << this->MaximumDistance << "\n";
531 
532   os << indent << "Sample Dimensions: (" << this->SampleDimensions[0] << ", "
533      << this->SampleDimensions[1] << ", " << this->SampleDimensions[2] << ")\n";
534 
535   os << indent << "ModelBounds: \n";
536   os << indent << "  Xmin,Xmax: (" << this->ModelBounds[0] << ", " << this->ModelBounds[1] << ")\n";
537   os << indent << "  Ymin,Ymax: (" << this->ModelBounds[2] << ", " << this->ModelBounds[3] << ")\n";
538   os << indent << "  Zmin,Zmax: (" << this->ModelBounds[4] << ", " << this->ModelBounds[5] << ")\n";
539 
540   os << indent << "Null Value: " << this->NullValue << "\n";
541 
542   os << indent << "Power Parameter: " << this->PowerParameter << "\n";
543 }
544