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