1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkProjectSphereFilter.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 "vtkProjectSphereFilter.h"
16
17 #include "vtkCell.h"
18 #include "vtkCellArray.h"
19 #include "vtkCellData.h"
20 #include "vtkDoubleArray.h"
21 #include "vtkGenericCell.h"
22 #include "vtkIdList.h"
23 #include "vtkInformation.h"
24 #include "vtkInformationVector.h"
25 #include "vtkMath.h"
26 #include "vtkMergePoints.h"
27 #include "vtkNew.h"
28 #include "vtkObjectFactory.h"
29 #include "vtkPointData.h"
30 #include "vtkPoints.h"
31 #include "vtkPolyData.h"
32 #include "vtkUnsignedCharArray.h"
33 #include "vtkUnstructuredGrid.h"
34
35 #include <map>
36
37 namespace
38 {
ConvertXYZToLatLonDepth(double xyz[3],double lonLatDepth[3],double center[3])39 void ConvertXYZToLatLonDepth(double xyz[3], double lonLatDepth[3], double center[3])
40 {
41 double dist2 = vtkMath::Distance2BetweenPoints(xyz, center);
42 lonLatDepth[2] = sqrt(dist2);
43 double radianAngle = atan2(xyz[1]-center[1], xyz[0]-center[0]);
44 lonLatDepth[0] = radianAngle*180./vtkMath::Pi()-180.;
45 lonLatDepth[1] = 90.-acos((xyz[2]-center[2])/lonLatDepth[2])*180./vtkMath::Pi();
46 }
47
48 template<class data_type>
TransformVector(double * transformMatrix,data_type * data)49 void TransformVector(
50 double* transformMatrix, data_type* data)
51 {
52 double d0 = static_cast<double>(data[0]);
53 double d1 = static_cast<double>(data[1]);
54 double d2 = static_cast<double>(data[2]);
55 data[0] = static_cast<data_type>(
56 transformMatrix[0]*d0+transformMatrix[1]*d1+transformMatrix[2]*d2);
57 data[1] = static_cast<data_type>(
58 transformMatrix[3]*d0+transformMatrix[4]*d1+transformMatrix[5]*d2);
59 data[2] = static_cast<data_type>(
60 transformMatrix[6]*d0+transformMatrix[7]*d1+transformMatrix[8]*d2);
61 }
62 } // end anonymous namespace
63
64 vtkStandardNewMacro(vtkProjectSphereFilter);
65
66 //-----------------------------------------------------------------------------
vtkProjectSphereFilter()67 vtkProjectSphereFilter::vtkProjectSphereFilter() : SplitLongitude(-180)
68 {
69 this->Center[0] = this->Center[1] = this->Center[2] = 0;
70 this->KeepPolePoints = false;
71 this->TranslateZ = false;
72 }
73
74 //-----------------------------------------------------------------------------
75 vtkProjectSphereFilter::~vtkProjectSphereFilter() = default;
76
77 //-----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)78 void vtkProjectSphereFilter::PrintSelf(ostream &os, vtkIndent indent)
79 {
80 this->Superclass::PrintSelf(os, indent);
81
82 double center[3];
83 this->GetCenter(center);
84
85 os << indent << "Center: ("
86 << center[0] << ", "
87 << center[1] << ", "
88 << center[2] << ")\n";
89 os << indent << "KeepPolePoints " << this->GetKeepPolePoints() << "\n";
90 os << indent << "TranslateZ " << this->GetTranslateZ() << "\n";
91 }
92
93 //-----------------------------------------------------------------------------
FillInputPortInformation(int vtkNotUsed (port),vtkInformation * info)94 int vtkProjectSphereFilter::FillInputPortInformation(int vtkNotUsed(port),
95 vtkInformation *info)
96 {
97 info->Remove(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE());
98 info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData");
99 info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkUnstructuredGrid");
100 return 1;
101 }
102
103 //-----------------------------------------------------------------------------
RequestData(vtkInformation * vtkNotUsed (request),vtkInformationVector ** inputVector,vtkInformationVector * outputVector)104 int vtkProjectSphereFilter::RequestData(vtkInformation *vtkNotUsed(request),
105 vtkInformationVector **inputVector,
106 vtkInformationVector *outputVector)
107 {
108 vtkDebugMacro("RequestData");
109
110 vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
111 vtkInformation *outInfo = outputVector->GetInformationObject(0);
112
113 vtkPointSet *input
114 = vtkPointSet::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT()));
115 if(vtkPolyData* poly = vtkPolyData::SafeDownCast(input))
116 {
117 if( poly->GetVerts()->GetNumberOfCells() > 0 ||
118 poly->GetLines()->GetNumberOfCells() > 0 ||
119 poly->GetStrips()->GetNumberOfCells() > 0 )
120 {
121 vtkErrorMacro("Can only deal with vtkPolyData polys.");
122 return 0;
123 }
124 }
125
126 vtkPointSet *output
127 = vtkPointSet::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));
128
129 vtkNew<vtkIdList> polePointIds;
130 this->TransformPointInformation(input, output, polePointIds.GetPointer());
131 this->TransformCellInformation(input, output, polePointIds.GetPointer());
132 output->GetFieldData()->ShallowCopy(input->GetFieldData());
133
134 vtkDebugMacro("Leaving RequestData");
135
136 return 1;
137 }
138
139 //-----------------------------------------------------------------------------
TransformPointInformation(vtkPointSet * input,vtkPointSet * output,vtkIdList * polePointIds)140 void vtkProjectSphereFilter::TransformPointInformation(
141 vtkPointSet* input, vtkPointSet* output, vtkIdList* polePointIds)
142 {
143 polePointIds->Reset();
144
145 // Deep copy point data since TransformPointInformation modifies
146 // the point data
147 output->GetPointData()->DeepCopy(input->GetPointData());
148
149 vtkNew<vtkPoints> points;
150 points->SetDataTypeToDouble();
151 points->SetNumberOfPoints(input->GetNumberOfPoints());
152
153 double zTranslation = ( this->TranslateZ == true ?
154 this->GetZTranslation(input) : 0. );
155
156 output->SetPoints(points.GetPointer());
157 vtkIdType numberOfPoints = input->GetNumberOfPoints();
158 double minDist2ToCenterLine = VTK_DOUBLE_MAX;
159 for(vtkIdType i=0;i<numberOfPoints;i++)
160 {
161 double coordIn[3], coordOut[3];
162 input->GetPoint(i, coordIn);
163 ConvertXYZToLatLonDepth(coordIn, coordOut, this->Center);
164 // if we allow the user to specify SplitLongitude we have to make
165 // sure that we respect their choice since the output of atan
166 // is from -180 to 180.
167 if(coordOut[0] < this->SplitLongitude)
168 {
169 coordOut[0] += 360.;
170 }
171 coordOut[2] -= zTranslation;
172
173 // acbauer -- a hack to make the grid look better by forcing it to be flat.
174 // leaving this in for now even though it's commented out. if I figure out
175 // a proper way to do this i'll replace it.
176 // if(this->TranslateZ)
177 // {
178 // coordOut[2] = 0;
179 // }
180 points->SetPoint(i, coordOut);
181
182 // keep track of the ids of the points that are closest to the
183 // centerline between -90 and 90 latitude. this is done as a single
184 // pass algorithm.
185 double dist2 =
186 (coordIn[0]-this->Center[0])*(coordIn[0]-this->Center[0])+
187 (coordIn[1]-this->Center[1])*(coordIn[1]-this->Center[1]);
188 if(dist2 < minDist2ToCenterLine)
189 {
190 // we found a closer point so throw out the previous closest
191 // point ids.
192 minDist2ToCenterLine = dist2;
193 polePointIds->SetNumberOfIds(1);
194 polePointIds->SetId(0, i);
195 }
196 else if(dist2 == minDist2ToCenterLine)
197 {
198 // this point is just as close as the current closest point
199 // so we just add it to our list.
200 polePointIds->InsertNextId(i);
201 }
202 this->TransformTensors(i, coordIn, output->GetPointData());
203 }
204 this->ComputePointsClosestToCenterLine(minDist2ToCenterLine, polePointIds);
205 }
206
207 //-----------------------------------------------------------------------------
TransformCellInformation(vtkPointSet * input,vtkPointSet * output,vtkIdList * polePointIds)208 void vtkProjectSphereFilter::TransformCellInformation(
209 vtkPointSet* input, vtkPointSet* output, vtkIdList* polePointIds)
210 {
211 // a map from the old point to the newly created point for split cells
212 std::map<vtkIdType,vtkIdType> boundaryMap;
213
214 double TOLERANCE = .0001;
215 vtkNew<vtkMergePoints> locator;
216 locator->InitPointInsertion(output->GetPoints(), output->GetBounds(),
217 output->GetNumberOfPoints());
218 double coord[3];
219 for(vtkIdType i=0;i<output->GetNumberOfPoints();i++)
220 {
221 // this is a bit annoying but required for building up the locator properly
222 // otherwise it won't either know these points exist or will start
223 // counting new points at index 0.
224 output->GetPoint(i, coord);
225 locator->InsertNextPoint(coord);
226 }
227
228 vtkIdType numberOfCells = input->GetNumberOfCells();
229 vtkCellArray* connectivity = nullptr;
230 vtkUnstructuredGrid* ugrid = vtkUnstructuredGrid::SafeDownCast(output);
231 vtkPolyData* poly = vtkPolyData::SafeDownCast(output);
232 if(ugrid)
233 {
234 ugrid->Allocate(numberOfCells);
235 connectivity = ugrid->GetCells();
236 }
237 else if(poly)
238 {
239 poly->Allocate(numberOfCells);
240 connectivity = poly->GetPolys();
241 }
242 output->GetCellData()->CopyAllOn();
243 output->GetCellData()->CopyAllocate(input->GetCellData(),
244 input->GetNumberOfCells());
245 vtkPointData* pointData = output->GetPointData();
246 pointData->CopyAllOn();
247 pointData->CopyAllocate(pointData, output->GetNumberOfPoints());
248
249 vtkNew<vtkIdList> cellPoints;
250 vtkNew<vtkIdList> skippedCells;
251 vtkIdType mostPointsInCell = 0;
252 for(vtkIdType cellId=0;cellId<numberOfCells;cellId++)
253 {
254 bool onLeftBoundary = false;
255 bool onRightBoundary = false;
256 bool leftSideInterior = false; //between SplitLongitude and SplitLongitude+90
257 bool rightSideInterior = false;// between SplitLongitude+270 and SplitLongitude+360
258 bool middleInterior = false; //between SplitLongitude+90 and SplitLongitude+270
259
260 bool skipCell = false;
261 bool splitCell = false;
262 double xyz[3];
263 input->GetCellPoints(cellId, cellPoints.GetPointer());
264 mostPointsInCell = (mostPointsInCell > cellPoints->GetNumberOfIds() ?
265 mostPointsInCell : cellPoints->GetNumberOfIds() );
266 for(vtkIdType pt=0;pt<cellPoints->GetNumberOfIds();pt++)
267 {
268 output->GetPoint(cellPoints->GetId(pt), xyz);
269 if(xyz[0] < this->SplitLongitude+TOLERANCE)
270 {
271 onLeftBoundary = true;
272 }
273 else if(xyz[0] > this->SplitLongitude+360.-TOLERANCE)
274 {
275 onRightBoundary = true;
276 }
277 else if(xyz[0] < this->SplitLongitude+90.)
278 {
279 leftSideInterior = true;
280 }
281 else if(xyz[0] > this->SplitLongitude+270.)
282 {
283 rightSideInterior = true;
284 }
285 else
286 {
287 middleInterior = true;
288 }
289 if(polePointIds->IsId(cellPoints->GetId(pt)) != -1 && this->KeepPolePoints == false)
290 {
291 skipCell = true;
292 skippedCells->InsertNextId(cellId);
293 continue;
294 }
295 }
296 if(skipCell)
297 {
298 continue;
299 }
300 if( (onLeftBoundary || onRightBoundary ) && rightSideInterior && leftSideInterior)
301 { // this cell stretches across the split longitude
302 splitCell = true;
303 }
304 else if( onLeftBoundary && rightSideInterior )
305 {
306 for(vtkIdType pt=0;pt<cellPoints->GetNumberOfIds();pt++)
307 {
308 output->GetPoint(cellPoints->GetId(pt), xyz);
309 if(xyz[0] < this->SplitLongitude+TOLERANCE)
310 {
311 std::map<vtkIdType,vtkIdType>::iterator it =
312 boundaryMap.find(cellPoints->GetId(pt));
313 if(it == boundaryMap.end())
314 { // need to create another point
315 xyz[0] += 360.;
316 vtkIdType id = locator->InsertNextPoint(xyz);
317 boundaryMap[cellPoints->GetId(pt)] = id;
318 pointData->CopyData(pointData, cellPoints->GetId(pt), id);
319 cellPoints->SetId(pt, id);
320 }
321 else
322 {
323 cellPoints->SetId(pt, it->second);
324 }
325 }
326 }
327 }
328 else if( onRightBoundary && leftSideInterior )
329 {
330 for(vtkIdType pt=0;pt<cellPoints->GetNumberOfIds();pt++)
331 {
332 output->GetPoint(cellPoints->GetId(pt), xyz);
333 if(xyz[0] > this->SplitLongitude+360.-TOLERANCE)
334 {
335 std::map<vtkIdType,vtkIdType>::iterator it =
336 boundaryMap.find(cellPoints->GetId(pt));
337 if(it == boundaryMap.end())
338 { // need to create another point
339 xyz[0] -= 360.;
340 vtkIdType id = locator->InsertNextPoint(xyz);
341 boundaryMap[cellPoints->GetId(pt)] = id;
342 pointData->CopyData(pointData, cellPoints->GetId(pt), id);
343 cellPoints->SetId(pt, id);
344 }
345 else
346 {
347 cellPoints->SetId(pt, it->second);
348 }
349 }
350 }
351 }
352 else if( (onLeftBoundary || onRightBoundary ) && middleInterior)
353 {
354 splitCell = true;
355 }
356 else if( leftSideInterior && rightSideInterior)
357 {
358 splitCell = true;
359 }
360 if(splitCell)
361 {
362 this->SplitCell(input, output, cellId, locator.GetPointer(), connectivity, 0);
363 this->SplitCell(input, output, cellId, locator.GetPointer(), connectivity, 1);
364 }
365 else if(ugrid)
366 {
367 ugrid->InsertNextCell(input->GetCellType(cellId), cellPoints.GetPointer());
368 output->GetCellData()->CopyData(input->GetCellData(), cellId,
369 output->GetNumberOfCells()-1);
370 }
371 else if(poly)
372 {
373 poly->InsertNextCell(input->GetCellType(cellId), cellPoints.GetPointer());
374 output->GetCellData()->CopyData(input->GetCellData(), cellId,
375 output->GetNumberOfCells()-1);
376 }
377 }
378
379 if(poly)
380 {
381 // we have to rebuild the polydata cell data structures since when
382 // we split a cell we don't do it right away due to the expense
383 poly->DeleteCells();
384 poly->BuildCells();
385 }
386
387 // deal with cell data
388 std::vector<double> weights(mostPointsInCell);
389 vtkIdType skipCounter = 0;
390 for(vtkIdType cellId=0;cellId<input->GetNumberOfCells();cellId++)
391 {
392 if(skippedCells->IsId(cellId) != -1)
393 {
394 skippedCells->DeleteId(cellId);
395 skipCounter++;
396 continue;
397 }
398 int subId = 0;
399 double parametricCenter[3];
400 vtkCell* cell = input->GetCell(cellId);
401 cell->GetParametricCenter(parametricCenter);
402 cell->EvaluateLocation(subId, parametricCenter, coord, &weights[0]);
403 this->TransformTensors(cellId-skipCounter, coord, output->GetCellData());
404 }
405 }
406
407 //-----------------------------------------------------------------------------
TransformTensors(vtkIdType pointId,double * coord,vtkDataSetAttributes * dataArrays)408 void vtkProjectSphereFilter::TransformTensors(
409 vtkIdType pointId, double* coord, vtkDataSetAttributes* dataArrays )
410 {
411 double theta = atan2(
412 sqrt( (coord[0]-this->Center[0])*(coord[0]-this->Center[0]) +
413 (coord[1]-this->Center[1])*(coord[1]-this->Center[1]) ),
414 coord[2]-this->Center[2] );
415 double phi = atan2( coord[1]-this->Center[1], coord[0]-this->Center[0] );
416 double sinTheta = sin(theta);
417 double cosTheta = cos(theta);
418 double sinPhi = sin(phi);
419 double cosPhi = cos(phi);
420 double transformMatrix[9] = {
421 -sinPhi, cosPhi, 0.,
422 cosTheta*cosPhi, cosTheta*sinPhi, -sinTheta,
423 sinTheta*cosPhi, sinTheta*sinPhi, cosTheta };
424 for(int i=0;i<dataArrays->GetNumberOfArrays();i++)
425 {
426 vtkDataArray* array = dataArrays->GetArray(i);
427 if(array->GetNumberOfComponents() == 3)
428 {
429 switch (array->GetDataType())
430 {
431 vtkTemplateMacro(TransformVector(
432 transformMatrix,
433 static_cast<VTK_TT *>(array->GetVoidPointer(pointId*array->GetNumberOfComponents())) ) );
434 }
435 }
436 }
437 }
438
439 //-----------------------------------------------------------------------------
GetZTranslation(vtkPointSet * input)440 double vtkProjectSphereFilter::GetZTranslation(vtkPointSet* input)
441 {
442 double maxRadius2 = 0; // squared radius
443 double coord[3];
444 for(vtkIdType i=0;i<input->GetNumberOfPoints();i++)
445 {
446 input->GetPoint(i, coord);
447 double dist2 = vtkMath::Distance2BetweenPoints(coord, this->Center);
448 if(dist2 > maxRadius2)
449 {
450 maxRadius2 = dist2;
451 }
452 }
453 return sqrt(maxRadius2);
454 }
455
456 //-----------------------------------------------------------------------------
SplitCell(vtkPointSet * input,vtkPointSet * output,vtkIdType inputCellId,vtkIncrementalPointLocator * locator,vtkCellArray * connectivity,int splitSide)457 void vtkProjectSphereFilter::SplitCell(
458 vtkPointSet* input, vtkPointSet* output, vtkIdType inputCellId,
459 vtkIncrementalPointLocator* locator, vtkCellArray* connectivity,
460 int splitSide)
461 {
462 // i screw up the canonical ordering of the cell but apparently this
463 // gets fixed by vtkCell::Clip().
464 vtkCell* cell = input->GetCell(inputCellId);
465 vtkNew<vtkDoubleArray> cellScalars;
466 cellScalars->SetNumberOfTuples(cell->GetNumberOfPoints());
467 double coord[3];
468 for(vtkIdType pt=0;pt<cell->GetNumberOfPoints();pt++)
469 {
470 output->GetPoint(cell->GetPointId(pt), coord);
471 if(splitSide == 0 && coord[0] > this->SplitLongitude+180.)
472 {
473 coord[0] -= 360.;
474 }
475 else if(splitSide == 1 && coord[0] < this->SplitLongitude+180.)
476 {
477 coord[0] += 360.;
478 }
479 cellScalars->SetValue(pt, coord[0]);
480 cell->GetPoints()->SetPoint(pt, coord);
481 }
482 vtkIdType numberOfCells = output->GetNumberOfCells();
483 double splitLocation = (splitSide == 0 ? -180. : 180.);
484 cell->Clip(splitLocation, cellScalars.GetPointer(), locator, connectivity,
485 output->GetPointData(), output->GetPointData(), input->GetCellData(),
486 inputCellId, output->GetCellData(), splitSide);
487 // if the grid was an unstructured grid we have to update the cell
488 // types and locations for the created cells.
489 if(vtkUnstructuredGrid* ugrid = vtkUnstructuredGrid::SafeDownCast(output))
490 {
491 this->SetCellInformation(ugrid, cell, output->GetNumberOfCells()-numberOfCells);
492 }
493 }
494
495 //-----------------------------------------------------------------------------
SetCellInformation(vtkUnstructuredGrid * output,vtkCell * cell,vtkIdType numberOfNewCells)496 void vtkProjectSphereFilter::SetCellInformation(
497 vtkUnstructuredGrid* output, vtkCell* cell, vtkIdType numberOfNewCells)
498 {
499 for(vtkIdType i=0;i<numberOfNewCells;i++)
500 {
501 vtkIdType prevCellId = output->GetNumberOfCells()+i-numberOfNewCells-1;
502 vtkIdType newCellId = prevCellId + 1;
503 vtkIdType *pts, numPts;
504 vtkIdType loc = output->GetCellLocationsArray()->GetValue(prevCellId);
505 output->GetCells()->GetCell(loc,numPts,pts);
506
507 output->GetCellLocationsArray()->InsertNextValue(loc+numPts+1);
508 output->GetCells()->GetCell(loc+numPts+1,numPts,pts);
509 if(cell->GetCellDimension() == 0)
510 {
511 if(numPts > 2)
512 {
513 output->GetCellTypesArray()->InsertValue(newCellId, VTK_POLY_VERTEX);
514 }
515 else
516 {
517 vtkErrorMacro("Cannot handle 0D cell with " << numPts << " number of points.");
518 }
519 }
520 else if(cell->GetCellDimension() == 1)
521 {
522 if(numPts == 2)
523 {
524 output->GetCellTypesArray()->InsertValue(newCellId, VTK_LINE);
525 }
526 else if(numPts > 2)
527 {
528 output->GetCellTypesArray()->InsertValue(newCellId, VTK_POLY_LINE);
529 }
530 else
531 {
532 vtkErrorMacro("Cannot handle 1D cell with " << numPts << " number of points.");
533 }
534 }
535 else if(cell->GetCellDimension() == 2)
536 {
537 if(numPts == 3)
538 {
539 output->GetCellTypesArray()->InsertValue(newCellId, VTK_TRIANGLE);
540 }
541 else if(numPts > 3 && cell->GetCellType() == VTK_TRIANGLE_STRIP)
542 {
543 output->GetCellTypesArray()->InsertValue(newCellId, VTK_TRIANGLE_STRIP);
544 }
545 else if(numPts == 4)
546 {
547 output->GetCellTypesArray()->InsertValue(newCellId, VTK_QUAD);
548 }
549 else
550 {
551 vtkErrorMacro("Cannot handle 2D cell with " << numPts << " number of points.");
552 }
553 }
554 else // 3D cell
555 {
556 if(numPts == 4)
557 {
558 output->GetCellTypesArray()->InsertValue(newCellId, VTK_TETRA);
559 }
560 else if(numPts == 5)
561 {
562 output->GetCellTypesArray()->InsertValue(newCellId, VTK_PYRAMID);
563 }
564 else if(numPts == 6)
565 {
566 output->GetCellTypesArray()->InsertValue(newCellId, VTK_WEDGE);
567 }
568 else if(numPts == 8)
569 {
570 output->GetCellTypesArray()->InsertValue(newCellId, VTK_HEXAHEDRON);
571 }
572 else
573 {
574 vtkErrorMacro("Unknown 3D cell type.");
575 }
576 }
577 }
578 }
579