1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkExtractCTHPart.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 "vtkExtractCTHPart.h"
16
17 #include "vtkAppendPolyData.h"
18 #include "vtkBoundingBox.h"
19 #include "vtkCellArray.h"
20 #include "vtkCellData.h"
21 #include "vtkCharArray.h"
22 #include "vtkClipPolyData.h"
23 #include "vtkCompositeDataIterator.h"
24 #include "vtkCompositeDataPipeline.h"
25 #include "vtkCompositeDataSet.h"
26 #include "vtkContourFilter.h"
27 #include "vtkCutter.h"
28 #include "vtkDataSetSurfaceFilter.h"
29 #include "vtkDoubleArray.h"
30 #include "vtkExecutive.h"
31 #include "vtkExtractCTHPart.h"
32 #include "vtkGarbageCollector.h"
33 #include "vtkImageData.h"
34 #include "vtkInformation.h"
35 #include "vtkInformationVector.h"
36 #include "vtkMultiBlockDataSet.h"
37 #include "vtkMultiProcessController.h"
38 #include "vtkNew.h"
39 #include "vtkObjectFactory.h"
40 #include "vtkPlaneCollection.h"
41 #include "vtkPlane.h"
42 #include "vtkPointData.h"
43 #include "vtkPolyData.h"
44 #include "vtkRectilinearGrid.h"
45 #include "vtkSmartPointer.h"
46 #include "vtkTimerLog.h"
47 #include "vtkToolkits.h"
48 #include "vtkUniformGrid.h"
49
50 #include <algorithm>
51 #include <cassert>
52 #include <math.h>
53 #include <string>
54 #include <vector>
55
56 vtkStandardNewMacro(vtkExtractCTHPart);
57 vtkCxxSetObjectMacro(vtkExtractCTHPart,ClipPlane,vtkPlane);
58 vtkCxxSetObjectMacro(vtkExtractCTHPart,Controller,vtkMultiProcessController);
59
60 const double CTH_AMR_SURFACE_VALUE=0.499;
61 const double CTH_AMR_SURFACE_VALUE_FLOAT=1;
62 const double CTH_AMR_SURFACE_VALUE_UNSIGNED_CHAR=255;
63
64 //-----------------------------------------------------------------------------
65 //=============================================================================
66 class vtkExtractCTHPartInternal
67 {
68 public:
69 typedef std::vector<std::string> VolumeArrayNamesType;
70 VolumeArrayNamesType VolumeArrayNames;
71 vtkBoundingBox GlobalInputBounds;
72
73 // Counter used to scale progress events.
74 int TotalNumberOfDatasets;
75 };
76
77 class vtkExtractCTHPart::VectorOfFragments :
78 public std::vector<vtkSmartPointer<vtkPolyData> >
79 {
80 };
81
82 class vtkExtractCTHPart::ScaledProgress
83 {
84 vtkExtractCTHPart* Self;
85 double Shift;
86 double Scale;
87 public:
ScaledProgress(double shift,double scale,vtkExtractCTHPart * self)88 ScaledProgress(double shift, double scale, vtkExtractCTHPart* self)
89 {
90 assert((self != NULL) &&
91 (shift >= 0.0) && (shift <= 1.0) &&
92 (scale >= 0.0) && (scale <= 1.0));
93
94 this->Self = self;
95 this->Shift = self->ProgressShift;
96 this->Scale = self->ProgressScale;
97
98 self->ProgressShift += shift * self->ProgressScale;
99 self->ProgressScale *= scale;
100 //cout << "Shift-Scale Push: " << self->ProgressShift << ", " <<
101 // self->ProgressScale << endl;
102 }
103
~ScaledProgress()104 ~ScaledProgress()
105 {
106 this->WorkDone();
107 }
108
WorkDone()109 void WorkDone()
110 {
111 if (this->Self)
112 {
113 this->Self->ProgressScale = this->Scale;
114 this->Self->ProgressShift = this->Shift;
115 //cout << "Shift-Scale Pop: " << this->Self->ProgressShift << ", " <<
116 // this->Self->ProgressScale << endl;
117 this->Self = NULL;
118 }
119 }
120 };
121
122
123 //=============================================================================
124 //-----------------------------------------------------------------------------
125
126 //-----------------------------------------------------------------------------
vtkExtractCTHPart()127 vtkExtractCTHPart::vtkExtractCTHPart()
128 {
129 this->Internals = new vtkExtractCTHPartInternal();
130 this->ClipPlane = NULL;
131 this->GenerateTriangles = true;
132 this->Capping = true;
133 this->RemoveGhostCells = true;
134 this->VolumeFractionSurfaceValueInternal = CTH_AMR_SURFACE_VALUE;
135 this->VolumeFractionSurfaceValue = CTH_AMR_SURFACE_VALUE;
136 this->ProgressScale = 1.0;
137 this->ProgressShift = 0.0;
138
139 this->Controller = 0;
140 this->SetController(vtkMultiProcessController::GetGlobalController());
141 }
142
143 //-----------------------------------------------------------------------------
~vtkExtractCTHPart()144 vtkExtractCTHPart::~vtkExtractCTHPart()
145 {
146 this->SetController(NULL);
147 this->SetClipPlane(NULL);
148
149 delete this->Internals;
150 this->Internals = 0;
151 }
152
153 //-----------------------------------------------------------------------------
154 // Overload standard modified time function. If clip plane is modified,
155 // then this object is modified as well.
GetMTime()156 unsigned long vtkExtractCTHPart::GetMTime()
157 {
158 unsigned long mTime= this->Superclass::GetMTime();
159 if (this->ClipPlane)
160 {
161 unsigned long time = this->ClipPlane->GetMTime();
162 return time > mTime? time : mTime;
163 }
164
165 return mTime;
166 }
167
168 //-----------------------------------------------------------------------------
RemoveVolumeArrayNames()169 void vtkExtractCTHPart::RemoveVolumeArrayNames()
170 {
171 this->Internals->VolumeArrayNames.clear();
172 this->Modified();
173 }
174
175 //-----------------------------------------------------------------------------
AddVolumeArrayName(const char * arrayName)176 void vtkExtractCTHPart::AddVolumeArrayName(const char* arrayName)
177 {
178 if (arrayName !=0 &&
179 arrayName[0] != 0 &&
180 std::find(this->Internals->VolumeArrayNames.begin(),
181 this->Internals->VolumeArrayNames.end(), std::string(arrayName))==
182 this->Internals->VolumeArrayNames.end())
183 {
184 this->Internals->VolumeArrayNames.push_back(arrayName);
185
186 // ensure that the volume arrays are in determinate order. I should just
187 // change the code to use a std::set.
188 std::sort(this->Internals->VolumeArrayNames.begin(),
189 this->Internals->VolumeArrayNames.end());
190 this->Modified();
191 }
192 }
193
194 //-----------------------------------------------------------------------------
GetNumberOfVolumeArrayNames()195 int vtkExtractCTHPart::GetNumberOfVolumeArrayNames()
196 {
197 return static_cast<int>(this->Internals->VolumeArrayNames.size());
198 }
199
200 //-----------------------------------------------------------------------------
GetVolumeArrayName(int idx)201 const char* vtkExtractCTHPart::GetVolumeArrayName(int idx)
202 {
203 if ( idx < 0 ||
204 idx > static_cast<int>(this->Internals->VolumeArrayNames.size()) )
205 {
206 return 0;
207 }
208
209 return this->Internals->VolumeArrayNames[idx].c_str();
210 }
211
212 //----------------------------------------------------------------------------
FillInputPortInformation(int port,vtkInformation * info)213 int vtkExtractCTHPart::FillInputPortInformation(
214 int port, vtkInformation *info)
215 {
216 if (!this->Superclass::FillInputPortInformation(port,info))
217 {
218 return 0;
219 }
220
221 info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkNonOverlappingAMR");
222 info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkMultiBlockDataSet");
223 info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkRectilinearGrid");
224 return 1;
225 }
226
227 //-----------------------------------------------------------------------------
RequestData(vtkInformation * vtkNotUsed (request),vtkInformationVector ** inputVector,vtkInformationVector * outputVector)228 int vtkExtractCTHPart::RequestData(vtkInformation *vtkNotUsed(request),
229 vtkInformationVector **inputVector, vtkInformationVector *outputVector)
230 {
231 const int number_of_volume_arrays = static_cast<int>(this->Internals->VolumeArrayNames.size());
232 if (number_of_volume_arrays == 0)
233 {
234 // nothing to do.
235 return 1;
236 }
237
238 vtkDataObject* inputDO = vtkDataObject::GetData(inputVector[0], 0);
239 vtkSmartPointer<vtkCompositeDataSet> inputCD = vtkCompositeDataSet::SafeDownCast(inputDO);
240 vtkRectilinearGrid* inputRG = vtkRectilinearGrid::SafeDownCast(inputDO);
241 assert(inputCD != NULL || inputRG != NULL);
242
243 if (inputRG)
244 {
245 vtkNew<vtkMultiBlockDataSet> mb;
246 mb->SetBlock(0, inputRG);
247 inputCD = mb.GetPointer();
248 }
249
250 vtkMultiBlockDataSet* output = vtkMultiBlockDataSet::GetData(outputVector, 0);
251
252 // initialize output multiblock-dataset. It will always have as many blocks as
253 // the number-of-volume arrays requested.
254 output->SetNumberOfBlocks(number_of_volume_arrays);
255
256 // Compute global bounds for the input dataset. This is used to generate
257 // external surface for the dataset.
258 if (!this->ComputeGlobalBounds(inputCD))
259 {
260 vtkErrorMacro("Failed to compute global bounds.");
261 return 0;
262 }
263
264 if (!this->Internals->GlobalInputBounds.IsValid())
265 {
266 // empty input, do nothing.
267 return 1;
268 }
269
270 unsigned int array_index = 0;
271 for (vtkExtractCTHPartInternal::VolumeArrayNamesType::iterator iter =
272 this->Internals->VolumeArrayNames.begin();
273 iter != this->Internals->VolumeArrayNames.end(); ++iter, ++array_index)
274 {
275 // this loop is doing the 1/(num-arrays)'th work for the entire task.
276 ScaledProgress sp(
277 array_index * 1.0 / this->Internals->VolumeArrayNames.size(),
278 1.0/this->Internals->VolumeArrayNames.size(),
279 this);
280
281 output->GetMetaData(array_index)->Set(vtkCompositeDataSet::NAME(), iter->c_str());
282
283 vtkNew<vtkPolyData> contour;
284 vtkGarbageCollector::DeferredCollectionPush();
285 if (this->ExtractContour(contour.GetPointer(), inputCD, iter->c_str()) &&
286 (contour->GetNumberOfPoints() > 0))
287 {
288 // Add extra arrays.
289 vtkNew<vtkIntArray> partArray;
290 partArray->SetName("Part Index");
291 partArray->SetNumberOfComponents(1);
292 partArray->SetNumberOfTuples(contour->GetNumberOfPoints());
293 partArray->FillComponent(0, static_cast<double>(array_index));
294 contour->GetPointData()->AddArray(partArray.GetPointer());
295
296 // I'm not adding the "Name" array that was added in previous
297 // implementation. Don't think that's much of use.
298
299 output->SetBlock(array_index, contour.GetPointer());
300 }
301 vtkGarbageCollector::DeferredCollectionPop();
302 }
303 return 1;
304 }
305
306 //-----------------------------------------------------------------------------
ComputeGlobalBounds(vtkCompositeDataSet * input)307 bool vtkExtractCTHPart::ComputeGlobalBounds(vtkCompositeDataSet *input)
308 {
309 assert("pre: input_exists" && input!=0);
310 this->Internals->GlobalInputBounds.Reset();
311
312 this->Internals->TotalNumberOfDatasets = 0;
313
314 vtkCompositeDataIterator* iter = input->NewIterator();
315 for (iter->InitTraversal(); !iter->IsDoneWithTraversal(); iter->GoToNextItem())
316 {
317 vtkDataSet *ds = vtkDataSet::SafeDownCast(iter->GetCurrentDataObject());
318 if (!ds)// can be null if on another processor
319 {
320 continue;
321 }
322 double realBounds[6];
323 ds->GetBounds(realBounds);
324 this->Internals->GlobalInputBounds.AddBounds(realBounds);
325
326 ++this->Internals->TotalNumberOfDatasets;
327 }
328 iter->Delete();
329
330 // Here we have the bounds according to our local datasets.
331 // If we are not running in parallel then the local
332 // bounds are the global bounds
333 if (!this->Controller || this->Controller->GetNumberOfProcesses() <= 1)
334 {
335 return true;
336 }
337
338 const double *min_point = this->Internals->GlobalInputBounds.GetMinPoint();
339 const double *max_point = this->Internals->GlobalInputBounds.GetMaxPoint();
340 double min_result[3], max_result[3];
341
342 if (!this->Controller->AllReduce(
343 min_point, min_result, 3, vtkCommunicator::MIN_OP))
344 {
345 return false;
346 }
347 if (!this->Controller->AllReduce(
348 max_point, max_result, 3, vtkCommunicator::MAX_OP))
349 {
350 return false;
351 }
352
353 this->Internals->GlobalInputBounds.SetBounds(
354 min_result[0], max_result[0], min_result[1], max_result[1],
355 min_result[2], max_result[2]);
356 // At this point, the global bounds is set in each processor.
357 return true;
358 }
359
360 //-----------------------------------------------------------------------------
361 // return false on error.
ExtractContour(vtkPolyData * output,vtkCompositeDataSet * input,const char * arrayName)362 bool vtkExtractCTHPart::ExtractContour(
363 vtkPolyData* output, vtkCompositeDataSet* input, const char*arrayName)
364 {
365 assert(output!=0 && input!=0 && arrayName!=0 && arrayName[0]!=0);
366
367 bool warn_once = true;
368 vtkSmartPointer<vtkCompositeDataIterator> iter;
369 iter.TakeReference(input->NewIterator());
370
371 // this loop is first 95% of the work.
372 ScaledProgress sp1(0.0, 0.95, this);
373
374 int counter = 0;
375 vtkExtractCTHPart::VectorOfFragments fragments;
376 for (iter->InitTraversal(); !iter->IsDoneWithTraversal(); iter->GoToNextItem(), ++counter)
377 {
378 // each iteration is 1/(total num of datasets)'th for the work.
379 ScaledProgress sp(
380 counter * 1.0/this->Internals->TotalNumberOfDatasets,
381 1.0/this->Internals->TotalNumberOfDatasets, this);
382
383 if (counter % 1000 == 0)
384 {
385 this->TriggerProgressEvent(0.0);
386 }
387
388 vtkDataObject *dataObj = iter->GetCurrentDataObject();
389 vtkRectilinearGrid* rg = vtkRectilinearGrid::SafeDownCast(dataObj);
390 vtkUniformGrid* ug = vtkUniformGrid::SafeDownCast(dataObj);
391
392 if (ug)
393 {
394 if (!this->ExtractClippedContourOnBlock<vtkUniformGrid>(fragments, ug, arrayName))
395 {
396 return false;
397 }
398 }
399 else if (rg)
400 {
401 if (!this->ExtractClippedContourOnBlock<vtkRectilinearGrid>(fragments, rg, arrayName))
402 {
403 return false;
404 }
405 }
406 else if (warn_once && dataObj)
407 {
408 warn_once = false;
409 vtkWarningMacro(<< dataObj->GetClassName() << " will be ignored.");
410 }
411 if ((counter % 1000) == 0)
412 {
413 this->TriggerProgressEvent(1.0);
414 }
415 }
416
417 if (fragments.size() == 0)
418 {
419 // empty contour. Not an error though, hence we don't return false.
420 return true;
421 }
422 sp1.WorkDone();
423
424 // Now, the last .05 % of the work.
425 ScaledProgress sp2(0.95, 0.05, this);
426 this->TriggerProgressEvent(0.0);
427 vtkNew<vtkAppendPolyData> appender;
428 for (size_t cc=0; cc < fragments.size(); cc++)
429 {
430 appender->AddInputData(fragments[cc].GetPointer());
431 }
432 appender->Update();
433 output->ShallowCopy(appender->GetOutputDataObject(0));
434 this->TriggerProgressEvent(1.0);
435 return true;
436 }
437
438 //-----------------------------------------------------------------------------
439 template <class T>
ExtractClippedContourOnBlock(vtkExtractCTHPart::VectorOfFragments & fragments,T * dataset,const char * arrayName)440 bool vtkExtractCTHPart::ExtractClippedContourOnBlock(
441 vtkExtractCTHPart::VectorOfFragments& fragments, T* dataset, const char* arrayName)
442 {
443 assert(arrayName!=0 && arrayName[0]!=0 && dataset != 0);
444
445 vtkDataArray* volumeFractionArray = dataset->GetCellData()->GetArray(arrayName);
446 if (!volumeFractionArray)
447 {
448 // skip this block.
449 return true;
450 }
451
452 // determine the true value to use for the contour based on the data-type.
453 switch (volumeFractionArray->GetDataType())
454 {
455 case VTK_UNSIGNED_CHAR:
456 this->VolumeFractionSurfaceValueInternal =
457 CTH_AMR_SURFACE_VALUE_UNSIGNED_CHAR * this->VolumeFractionSurfaceValue;
458 break;
459
460 default:
461 this->VolumeFractionSurfaceValueInternal =
462 CTH_AMR_SURFACE_VALUE_FLOAT * this->VolumeFractionSurfaceValue;
463 }
464
465 // We create a clone so we can modify the dataset (i.e. add new arrays to it).
466 vtkNew<T> inputClone;
467 inputClone->ShallowCopy(dataset);
468
469 // Convert cell-data-2-point-data so we can contour.
470 vtkNew<vtkDoubleArray> pointVolumeFractionArray;
471 this->ExecuteCellDataToPointData(volumeFractionArray,
472 pointVolumeFractionArray.GetPointer(), inputClone->GetDimensions());
473 inputClone->GetPointData()->SetScalars(pointVolumeFractionArray.GetPointer());
474
475 VectorOfFragments blockFragments;
476 if (!this->ExtractContourOnBlock<T>(blockFragments, inputClone.GetPointer(), arrayName))
477 {
478 return false;
479 }
480
481 if (!this->ClipPlane)
482 {
483 fragments.insert(fragments.end(),
484 blockFragments.begin(), blockFragments.end());
485 return true;
486 }
487
488 // Clip-n-cap the fragments using the clip plane.
489
490 // for the clip.
491 for (size_t cc=0; cc < blockFragments.size(); cc++)
492 {
493 vtkNew<vtkClipPolyData> clipper;
494 clipper->SetClipFunction(this->ClipPlane);
495 clipper->SetInputDataObject(blockFragments[cc]);
496 clipper->Update();
497 fragments.push_back(clipper->GetOutput());
498 }
499
500 //// for the cap.
501 if (this->Capping)
502 {
503 vtkNew<vtkCutter> cutter;
504 cutter->SetCutFunction(this->ClipPlane);
505 cutter->SetGenerateTriangles(this->GenerateTriangles? 1 : 0);
506 cutter->SetInputDataObject(inputClone.GetPointer());
507
508 vtkNew<vtkClipPolyData> scalarClipper;
509 scalarClipper->SetInputConnection(cutter->GetOutputPort());
510 scalarClipper->SetValue(this->VolumeFractionSurfaceValueInternal);
511 scalarClipper->Update();
512 fragments.push_back(scalarClipper->GetOutput());
513 }
514 return true;
515 }
516
517 //-----------------------------------------------------------------------------
518 template <class T>
ExtractContourOnBlock(vtkExtractCTHPart::VectorOfFragments & fragments,T * dataset,const char * arrayName)519 bool vtkExtractCTHPart::ExtractContourOnBlock(
520 vtkExtractCTHPart::VectorOfFragments& fragments, T* dataset, const char* arrayName)
521 {
522 assert(arrayName!=0 && arrayName[0]!=0 && dataset != 0);
523
524 vtkDataArray* volumeFractionArray = dataset->GetPointData()->GetArray(arrayName);
525 assert(volumeFractionArray !=0);
526 assert(dataset->GetPointData()->GetArray(arrayName) !=0);
527
528 // Contour only if necessary.
529 double range[2];
530 volumeFractionArray->GetRange(range);
531 if (range[1] < this->VolumeFractionSurfaceValueInternal)
532 {
533 // this block doesn't have the material of interest.
534 return true;
535 }
536
537 // Extract exterior surface. Adds the surface polydata to fragments, if any.
538 if (this->Capping)
539 {
540 this->ExtractExteriorSurface(fragments, dataset);
541 }
542
543 if (this->ClipPlane == NULL && range[0] > this->VolumeFractionSurfaceValueInternal)
544 {
545 // no need to extract contour.
546 return true;
547 }
548
549 // Extract contour.
550 vtkNew<vtkContourFilter> contourer;
551 contourer->SetInputData(dataset);
552 contourer->SetValue(0, this->VolumeFractionSurfaceValueInternal);
553 contourer->SetComputeScalars(0);
554 contourer->SetGenerateTriangles(this->GenerateTriangles? 1: 0);
555 contourer->SetInputArrayToProcess(0, 0, 0,
556 vtkDataObject::FIELD_ASSOCIATION_POINTS, arrayName);
557 contourer->Update();
558
559 vtkPolyData* output =
560 vtkPolyData::SafeDownCast(contourer->GetOutputDataObject(0));
561 if (!output && output->GetNumberOfPoints()== 0)
562 {
563 return true;
564 }
565 if (!this->RemoveGhostCells &&
566 output->GetCellData()->GetArray("vtkGhostLevels"))
567 {
568 output->GetCellData()->GetArray("vtkGhostLevels")->SetName("OriginalGhostLevels");
569 }
570
571 fragments.push_back(output);
572 return true;
573 }
574
575 //-----------------------------------------------------------------------------
576 // Description:
577 // Append quads for faces of the block that actually on the bounds
578 // of the hierarchical dataset. Deals with ghost cells.
579 template <class T>
ExtractExteriorSurface(vtkExtractCTHPart::VectorOfFragments & fragments,T * input)580 void vtkExtractCTHPart::ExtractExteriorSurface(
581 vtkExtractCTHPart::VectorOfFragments& fragments, T* input)
582 {
583 assert("pre: valid_input" && input!=0 && input->CheckAttributes()==0);
584
585 int result=0;
586 #if 1
587 int dims[3];
588 input->GetDimensions(dims);
589 int ext[6];
590 int originalExtents[6];
591 input->GetExtent(ext);
592 input->GetExtent(originalExtents);
593
594 // vtkUnsignedCharArray *ghostArray=static_cast<vtkUnsignedCharArray *>(input->GetCellData()->GetArray("vtkGhostLevels"));
595
596 // bounds without taking ghost cells into account
597 double bounds[6];
598
599 input->GetBounds(bounds);
600
601 #if 0
602 // block face min x
603 if(this->IsGhostFace(0,0,dims,ghostArray))
604 {
605 // downsize this!
606 bounds[0]+=spacing[0];
607 ++ext[0];
608 }
609 if(this->IsGhostFace(0,1,dims,ghostArray))
610 {
611 // downsize this!
612 bounds[1]-=spacing[0];
613 --ext[1];
614 }
615 if(this->IsGhostFace(1,0,dims,ghostArray))
616 {
617 // downsize this!
618 bounds[2]+=spacing[1];
619 ++ext[2];
620 }
621 if(this->IsGhostFace(1,1,dims,ghostArray))
622 {
623 // downsize this!
624 bounds[3]-=spacing[1];
625 --ext[3];
626 }
627 if(this->IsGhostFace(2,0,dims,ghostArray))
628 {
629 // downsize this!
630 bounds[4]+=spacing[2];
631 ++ext[4];
632 }
633 if(this->IsGhostFace(2,1,dims,ghostArray))
634 {
635 // downsize this!
636 bounds[5]-=spacing[2];
637 --ext[5];
638 }
639 #endif
640 // here, bounds are real block bounds without ghostcells.
641
642 const double *minP = this->Internals->GlobalInputBounds.GetMinPoint();
643 const double *maxP = this->Internals->GlobalInputBounds.GetMaxPoint();
644 #if 0
645 const double epsilon=0.001;
646 int doFaceMinX=fabs(bounds[0]- minP[0])<epsilon;
647 int doFaceMaxX=fabs(bounds[1]- maxP[0])<epsilon;
648 int doFaceMinY=fabs(bounds[2]- minP[1])<epsilon;
649 int doFaceMaxY=fabs(bounds[3]- maxP[1])<epsilon;
650 int doFaceMinZ=fabs(bounds[4]- minP[2])<epsilon;
651 int doFaceMaxZ=fabs(bounds[5]- maxP[2])<epsilon;
652 #endif
653
654 #if 1
655 int doFaceMinX=bounds[0]<= minP[0];
656 int doFaceMaxX=bounds[1]>= maxP[0];
657 int doFaceMinY=bounds[2]<= minP[1];
658 int doFaceMaxY=bounds[3]>= maxP[1];
659 int doFaceMinZ=bounds[4]<= minP[2];
660 int doFaceMaxZ=bounds[5]>= maxP[2];
661 #endif
662 #if 0
663 int doFaceMinX=1;
664 int doFaceMaxX=1;
665 int doFaceMinY=1;
666 int doFaceMaxY=1;
667 int doFaceMinZ=1;
668 int doFaceMaxZ=1;
669 #endif
670 #if 0
671 int doFaceMinX=0;
672 int doFaceMaxX=0;
673 int doFaceMinY=0;
674 int doFaceMaxY=0;
675 int doFaceMinZ=0;
676 int doFaceMaxZ=0;
677 #endif
678 #if 0
679 doFaceMaxX=0;
680 doFaceMaxY=0;
681 doFaceMaxZ=0;
682 #endif
683
684 result=doFaceMinX||doFaceMaxX||doFaceMinY||doFaceMaxY||doFaceMinZ
685 ||doFaceMaxZ;
686
687 if(result)
688 {
689 vtkSmartPointer<vtkPolyData> output = vtkSmartPointer<vtkPolyData>::New();
690
691 vtkIdType numPoints=0;
692 vtkIdType cellArraySize=0;
693
694 // input->GetExtent(ext);
695
696 // Compute an upper bound for the number of points and cells.
697 // xMin face
698 if (doFaceMinX && ext[2] != ext[3] && ext[4] != ext[5] && ext[0] != ext[1])
699 {
700 cellArraySize += 2*(ext[3]-ext[2]+1)*(ext[5]-ext[4]+1);
701 numPoints += (ext[3]-ext[2]+1)*(ext[5]-ext[4]+1);
702 }
703 // xMax face
704 if (doFaceMaxX && ext[2] != ext[3] && ext[4] != ext[5])
705 {
706 cellArraySize += 2*(ext[3]-ext[2]+1)*(ext[5]-ext[4]+1);
707 numPoints += (ext[3]-ext[2]+1)*(ext[5]-ext[4]+1);
708 }
709 // yMin face
710 if (doFaceMinY && ext[0] != ext[1] && ext[4] != ext[5] && ext[2] != ext[3])
711 {
712 cellArraySize += 2*(ext[1]-ext[0]+1)*(ext[5]-ext[4]+1);
713 numPoints += (ext[1]-ext[0]+1)*(ext[5]-ext[4]+1);
714 }
715 // yMax face
716 if (doFaceMaxY && ext[0] != ext[1] && ext[4] != ext[5])
717 {
718 cellArraySize += 2*(ext[1]-ext[0]+1)*(ext[5]-ext[4]+1);
719 numPoints += (ext[1]-ext[0]+1)*(ext[5]-ext[4]+1);
720 }
721 // zMin face
722 if (doFaceMinZ && ext[0] != ext[1] && ext[2] != ext[3] && ext[4] != ext[5])
723 {
724 cellArraySize += 2*(ext[1]-ext[0]+1)*(ext[3]-ext[2]+1);
725 numPoints += (ext[1]-ext[0]+1)*(ext[3]-ext[2]+1);
726 }
727 // zMax face
728 if (doFaceMaxZ && ext[0] != ext[1] && ext[2] != ext[3])
729 {
730 cellArraySize += 2*(ext[1]-ext[0]+1)*(ext[3]-ext[2]+1);
731 numPoints += (ext[1]-ext[0]+1)*(ext[3]-ext[2]+1);
732 }
733
734 vtkCellArray *outPolys = vtkCellArray::New();
735 outPolys->Allocate(cellArraySize);
736 output->SetPolys(outPolys);
737 outPolys->Delete();
738
739 vtkPoints *outPoints = vtkPoints::New();
740 outPoints->Allocate(numPoints);
741 output->SetPoints(outPoints);
742 outPoints->Delete();
743
744 // Allocate attributes for copying.
745 output->GetPointData()->CopyAllocate(input->GetPointData());
746 output->GetCellData()->CopyAllocate(input->GetCellData());
747
748 // Extents are already corrected for ghostcells.
749
750 // make each face that is actually on the ds boundary
751 if(doFaceMinX)
752 {
753 this->ExecuteFaceQuads(input,output,0,originalExtents,ext,0,1,2);
754 }
755 if(doFaceMaxX)
756 {
757 this->ExecuteFaceQuads(input,output,1,originalExtents,ext,0,2,1);
758 }
759 if(doFaceMinY)
760 {
761 this->ExecuteFaceQuads(input,output,0,originalExtents,ext,1,2,0);
762 }
763 if(doFaceMaxY)
764 {
765 this->ExecuteFaceQuads(input,output,1,originalExtents,ext,1,0,2);
766 }
767 if(doFaceMinZ)
768 {
769 this->ExecuteFaceQuads(input,output,0,originalExtents,ext,2,0,1);
770 }
771 if(doFaceMaxZ)
772 {
773 this->ExecuteFaceQuads(input,output,1,originalExtents,ext,2,1,0);
774 }
775 output->Squeeze();
776 assert(output->CheckAttributes() == 0);
777
778 vtkNew<vtkClipPolyData> clipper;
779 clipper->SetInputData(output);
780 clipper->SetValue(this->VolumeFractionSurfaceValueInternal);
781 clipper->Update();
782 fragments.push_back(clipper->GetOutput());
783 }
784 #endif
785 // result=>valid_surface: A=>B !A||B
786 }
787
788 //----------------------------------------------------------------------------
789 // Description:
790 // Is block face on axis0 (either min or max depending on the maxFlag)
791 // composed of only ghost cells?
IsGhostFace(int axis0,int maxFlag,int dims[3],vtkUnsignedCharArray * ghostArray)792 int vtkExtractCTHPart::IsGhostFace(int axis0,
793 int maxFlag,
794 int dims[3],
795 vtkUnsignedCharArray *ghostArray)
796 {
797 assert("pre: valid_axis0" && axis0>=0 && axis0<=2);
798
799 int axis1=axis0+1;
800 if(axis1>2)
801 {
802 axis1=0;
803 }
804 int axis2=axis0+2;
805 if(axis2>2)
806 {
807 axis2=0;
808 }
809
810 int ijk[3]; // index of the cell.
811
812 if(maxFlag)
813 {
814 ijk[axis0]=dims[axis0]-2;
815 }
816 else
817 {
818 ijk[axis0]=0;
819 }
820
821 // We test the center cell of the block face.
822 // in the worst case (2x2 cells), we need to know if at least
823 // three cells are ghost to say that the face is a ghost face.
824
825 ijk[axis1]=dims[axis1]/2-1; // (dims[axis1]-2)/2
826 ijk[axis2]=dims[axis2]/2-1; // (dims[axis2]-2)/2
827 int result=ghostArray->GetValue(vtkStructuredData::ComputeCellId(dims,ijk));
828
829 if(dims[axis1]==3)
830 {
831 // axis1 requires 2 cells to be tested.
832 // if so, axis1index=0 and axis1index+1=1
833 ijk[axis1]=1;
834 result=result &&
835 ghostArray->GetValue(vtkStructuredData::ComputeCellId(dims,ijk));
836 }
837
838 if(dims[axis2]==3)
839 {
840 // herem axis1 may have moved from the previous test.
841 // axis2 requires 2 cells to be tested.
842 // if so, axis2index=0 and axis2index+1=1
843 ijk[axis2]=1;
844 result=result &&
845 ghostArray->GetValue(vtkStructuredData::ComputeCellId(dims,ijk));
846 }
847 return result;
848 }
849
850 //----------------------------------------------------------------------------
851 // Description:
852 // Merly the same implementation than in vtkDataSetSurfaceFilter, without
853 // dealing with the whole extents.
ExecuteFaceQuads(vtkDataSet * input,vtkPolyData * output,int maxFlag,int originalExtents[6],int ext[6],int aAxis,int bAxis,int cAxis)854 void vtkExtractCTHPart::ExecuteFaceQuads(vtkDataSet *input,
855 vtkPolyData *output,
856 int maxFlag,
857 int originalExtents[6],
858 int ext[6],
859 int aAxis,
860 int bAxis,
861 int cAxis)
862 {
863 assert("pre: input_exists" && input!=0);
864 assert("pre: output_exists" && output!=0);
865 assert("pre: originalExtents_exists" && originalExtents!=0);
866 assert("pre: ext_exists" && ext!=0);
867 assert("pre: valid_axes"
868 && aAxis>=0 && aAxis<=2
869 && bAxis>=0 && bAxis<=2
870 && cAxis>=0 && cAxis<=2
871 && aAxis!=bAxis
872 && aAxis!=cAxis
873 && bAxis!=cAxis);
874
875 vtkPoints *outPts;
876 vtkCellArray *outPolys;
877 vtkPointData *inPD, *outPD;
878 vtkCellData *inCD, *outCD;
879 int pInc[3];
880 int qInc[3];
881 int cOutInc;
882 double pt[3];
883 vtkIdType inStartPtId;
884 vtkIdType inStartCellId;
885 vtkIdType outStartPtId;
886 vtkIdType outPtId;
887 vtkIdType inId, outId;
888 int ib, ic;
889 int aA2, bA2, cA2;
890
891 outPts = output->GetPoints();
892 outPD = output->GetPointData();
893 inPD = input->GetPointData();
894 outCD = output->GetCellData();
895 inCD = input->GetCellData();
896
897 pInc[0] = 1;
898 pInc[1] = (originalExtents[1]-originalExtents[0]+1);
899 pInc[2] = (originalExtents[3]-originalExtents[2]+1) * pInc[1];
900 // quad increments (cell incraments, but cInc could be confused with c axis).
901 qInc[0] = 1;
902 qInc[1] = originalExtents[1]-originalExtents[0];
903 // The conditions are for when we have one or more degenerate axes (2d or 1d cells).
904 if (qInc[1] == 0)
905 {
906 qInc[1] = 1;
907 }
908 qInc[2] = (originalExtents[3]-originalExtents[2]) * qInc[1];
909 if (qInc[2] == 0)
910 {
911 qInc[2] = qInc[1];
912 }
913
914 // Temporary variables to avoid many multiplications.
915 aA2 = aAxis<<1; // * 2;
916 bA2 = bAxis<<1; // * 2;
917 cA2 = cAxis<<1; // * 2;
918
919 // We might as well put the test for this face here.
920 if (ext[bA2] == ext[bA2+1] || ext[cA2] == ext[cA2+1])
921 {
922 return;
923 }
924 #if 0
925 if (maxFlag)
926 {
927 if (ext[aA2+1] < wholeExt[aA2+1])
928 {
929 return;
930 }
931 }
932 else
933 { // min faces have a slightly different condition to avoid coincident faces.
934 if (ext[aA2] == ext[aA2+1] || ext[aA2] > wholeExt[aA2])
935 {
936 return;
937 }
938 }
939 #endif
940
941 if(!maxFlag)
942 {
943 if (ext[aA2] == ext[aA2+1])
944 {
945 return;
946 }
947 }
948
949 // Assuming no ghost cells ...
950 // inStartPtId = inStartCellId = 0;
951 inStartPtId=0; //ext[aA2];
952 inStartCellId=0; //ext[aA2];
953
954
955 // I put this confusing conditional to fix a regression test.
956 // If we are creating a maximum face, then we indeed have to offset the input cell Ids.
957 // However, vtkGeometryFilter created a 2d image as a max face, but the cells are copied
958 // as a min face (no offset). Hence maxFlag = 1 and there should be no offset.
959 if (maxFlag && ext[aA2] < ext[1+aA2])
960 {
961 inStartPtId = pInc[aAxis]*(ext[aA2+1]-originalExtents[aA2]); // -ext[aA2]
962 inStartCellId = qInc[aAxis]*(ext[aA2+1]-originalExtents[aA2]-1); // -ext[aA2]
963 }
964
965 outStartPtId = outPts->GetNumberOfPoints();
966 // Make the points for this face.
967 for (ic = ext[cA2]; ic <= ext[cA2+1]; ++ic)
968 {
969 for (ib = ext[bA2]; ib <= ext[bA2+1]; ++ib)
970 {
971 // inId = inStartPtId + (ib-ext[bA2]+originExtents[bAxis])*pInc[bAxis]
972 // + (ic-ext[cA2]+originExtents[cAxis])*pInc[cAxis];
973
974 inId = inStartPtId + (ib-originalExtents[bA2])*pInc[bAxis]
975 + (ic-originalExtents[cA2])*pInc[cAxis];
976
977 input->GetPoint(inId, pt);
978 outId = outPts->InsertNextPoint(pt);
979 // Copy point data.
980 outPD->CopyData(inPD,inId,outId);
981 }
982 }
983
984 // Do the cells.
985 cOutInc = ext[bA2+1] - ext[bA2] + 1;
986
987 outPolys = output->GetPolys();
988
989 // Old method for creating quads (needed for cell data.).
990 for (ic = ext[cA2]; ic < ext[cA2+1]; ++ic)
991 {
992 for (ib = ext[bA2]; ib < ext[bA2+1]; ++ib)
993 {
994 outPtId = outStartPtId + (ib-ext[bA2]) + (ic-ext[cA2])*cOutInc;
995 // inId = inStartCellId + (ib-ext[bA2]+originExtents[bAxis])*qInc[bAxis] + (ic-ext[cA2]+originExtents[cAxis])*qInc[cAxis];
996
997 inId = inStartCellId + (ib-originalExtents[bA2])*qInc[bAxis] + (ic-originalExtents[cA2])*qInc[cAxis];
998
999 outId = outPolys->InsertNextCell(4);
1000 outPolys->InsertCellPoint(outPtId);
1001 outPolys->InsertCellPoint(outPtId+cOutInc);
1002 outPolys->InsertCellPoint(outPtId+cOutInc+1);
1003 outPolys->InsertCellPoint(outPtId+1);
1004
1005 // Copy cell data.
1006 outCD->CopyData(inCD,inId,outId);
1007 }
1008 }
1009 }
1010
1011 //-----------------------------------------------------------------------------
ExecuteCellDataToPointData(vtkDataArray * cellVolumeFraction,vtkDoubleArray * pointVolumeFraction,const int * dims)1012 void vtkExtractCTHPart::ExecuteCellDataToPointData(
1013 vtkDataArray *cellVolumeFraction, vtkDoubleArray *pointVolumeFraction, const int *dims)
1014 {
1015 int count;
1016 int i, j, k;
1017 int iEnd, jEnd, kEnd;
1018 int jInc, kInc;
1019 double *pPoint;
1020
1021 pointVolumeFraction->SetName(cellVolumeFraction->GetName());
1022 pointVolumeFraction->SetNumberOfTuples(dims[0]*dims[1]*dims[2]);
1023
1024 iEnd = dims[0]-1;
1025 jEnd = dims[1]-1;
1026 kEnd = dims[2]-1;
1027
1028 // Deals with none 3D images, otherwise it will never enter into the loop.
1029 // And then the data will be not initialized and the output of the contour
1030 // will be empty.
1031
1032 int dimensionality=3;
1033
1034 if(kEnd==0)
1035 {
1036 --dimensionality;
1037 kEnd=1;
1038 }
1039
1040 // Increments are for the point array.
1041 jInc = dims[0];
1042 kInc = (dims[1]) * jInc;
1043
1044 pPoint = pointVolumeFraction->GetPointer(0);
1045 // pCell = static_cast<double*>(cellVolumeFraction->GetVoidPointer(0));
1046
1047 // Initialize the point data to 0.
1048 memset(pPoint, 0, dims[0]*dims[1]*dims[2]*sizeof(double));
1049
1050 #ifndef NDEBUG
1051 // for debugging and check out of range.
1052 double *endPtr=pPoint+dims[0]*dims[1]*dims[2];
1053 #endif
1054
1055 // float delProgress = (maxProgress - minProgress) / (kEnd*jEnd*iEnd) / 2;
1056 // vtkIdType counter = 0;
1057
1058 int index=0;
1059 // Loop thorugh the cells.
1060 for (k = 0; k < kEnd; ++k)
1061 {
1062 for (j = 0; j < jEnd; ++j)
1063 {
1064 for (i = 0; i < iEnd; ++i)
1065 {
1066 //if (counter % 1000 == 0 && reportProgress)
1067 // {
1068 // this->UpdateProgress(minProgress + delProgress*(i+j*iEnd+k*iEnd*jEnd));
1069 // }
1070 //counter++;
1071 // Add cell value to all points of cell.
1072 double value=cellVolumeFraction->GetTuple1(index);
1073
1074 assert("check: valid_range" && pPoint<endPtr);
1075 assert("check: valid_range" && pPoint+1<endPtr);
1076 assert("check: valid_range" && pPoint+jInc<endPtr);
1077 assert("check: valid_range" && pPoint+jInc+1<endPtr);
1078
1079 *pPoint += value;
1080 pPoint[1] += value;
1081 pPoint[jInc] += value;
1082 pPoint[1+jInc] += value;
1083
1084 if(dimensionality==3)
1085 {
1086 assert("check: valid_range" && pPoint+kInc<endPtr);
1087 assert("check: valid_range" && pPoint+kInc+1<endPtr);
1088 assert("check: valid_range" && pPoint+kInc+jInc<endPtr);
1089 assert("check: valid_range" && pPoint+kInc+jInc+1<endPtr);
1090
1091 pPoint[kInc] += value;
1092 pPoint[kInc+1] += value;
1093 pPoint[kInc+jInc] += value;
1094 pPoint[kInc+jInc+1] += value;
1095 }
1096
1097 // Increment pointers
1098 ++pPoint;
1099 ++index;
1100 }
1101 // Skip over last point to the start of the next row.
1102 ++pPoint;
1103 }
1104 // Skip over the last row to the start of the next plane.
1105 pPoint += jInc;
1106 }
1107
1108 // Now a second pass to normalize the point values.
1109 // Loop through the points.
1110 count = 1;
1111 pPoint = pointVolumeFraction->GetPointer(0);
1112
1113 // because we eventually modified iEnd, jEnd, kEnd to handle the
1114 // 2D image case, we have to recompute them.
1115 iEnd = dims[0]-1;
1116 jEnd = dims[1]-1;
1117 kEnd = dims[2]-1;
1118
1119 // counter = 0;
1120 for (k = 0; k <= kEnd; ++k)
1121 {
1122 // Just a fancy fast way to compute the number of cell neighbors of a
1123 // point.
1124 if (k == 1)
1125 {
1126 count = count << 1;
1127 }
1128 if (k == kEnd && kEnd>0)
1129 {
1130 // only in 3D case, otherwise count may become zero
1131 // and be involved in a division by zero later on
1132 count = count >> 1;
1133 }
1134 for (j = 0; j <= jEnd; ++j)
1135 {
1136 // Just a fancy fast way to compute the number of cell neighbors of a
1137 // point.
1138 if (j == 1)
1139 {
1140 count = count << 1;
1141 }
1142 if (j == jEnd)
1143 {
1144 count = count >> 1;
1145 }
1146 for (i = 0; i <= iEnd; ++i)
1147 {
1148 //if (counter % 1000 == 0 && reportProgress)
1149 // {
1150 // this->UpdateProgress(minProgress + delProgress/2 + delProgress*(i+j*iEnd+k*iEnd*jEnd));
1151 // }
1152 //counter++;
1153 // Just a fancy fast way to compute the number of cell neighbors of a
1154 // point.
1155 if (i == 1)
1156 {
1157 count = count << 1;
1158 }
1159 if (i == iEnd)
1160 {
1161 count = count >> 1;
1162 }
1163 assert("check: valid_range" && pPoint<endPtr);
1164 assert("check: strictly_positive_count" && count>0);
1165 *pPoint = *pPoint / static_cast<double>(count);
1166 ++pPoint;
1167 }
1168 }
1169 }
1170 }
1171
1172 //-----------------------------------------------------------------------------
TriggerProgressEvent(double val)1173 void vtkExtractCTHPart::TriggerProgressEvent(double val)
1174 {
1175 double progress = this->ProgressShift + val*this->ProgressScale;
1176 //cout << "Progress: " << progress << endl;
1177 this->UpdateProgress(progress);
1178 }
1179
1180 //-----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)1181 void vtkExtractCTHPart::PrintSelf(ostream& os, vtkIndent indent)
1182 {
1183 this->Superclass::PrintSelf(os,indent);
1184
1185 os << indent << "VolumeArrayNames: \n";
1186 vtkIndent i2 = indent.GetNextIndent();
1187 std::vector<std::string>::iterator it;
1188 for ( it = this->Internals->VolumeArrayNames.begin();
1189 it != this->Internals->VolumeArrayNames.end();
1190 ++ it )
1191 {
1192 os << i2 << it->c_str() << endl;
1193 }
1194 os << indent << "VolumeFractionSurfaceValue: "
1195 << this->VolumeFractionSurfaceValue << endl;
1196 os << indent << "Capping: " << this->Capping << endl;
1197 os << indent << "GenerateTriangles: " << this->GenerateTriangles << endl;
1198 os << indent << "RemoveGhostCells: " << this->RemoveGhostCells << endl;
1199
1200 if (this->ClipPlane)
1201 {
1202 os << indent << "ClipPlane:\n";
1203 this->ClipPlane->PrintSelf(os, indent.GetNextIndent());
1204 }
1205 else
1206 {
1207 os << indent << "ClipPlane: NULL\n";
1208 }
1209
1210 if ( this->Controller!=0)
1211 {
1212 os << "Controller:" << endl;
1213 this->Controller->PrintSelf(os, indent.GetNextIndent());
1214 }
1215 else
1216 {
1217 os << "No Controller." << endl;
1218 }
1219 }
1220