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