diff --git a/IO/NetCDF/vtkNetCDFCAMReader.cxx b/IO/NetCDF/vtkNetCDFCAMReader.cxx index 322076b239438cda0c93110cdf9249dc5d23f8cb..c6637d5a9ac087e6ae649ffe9ebb3a2d8f388bc4 100644 --- a/IO/NetCDF/vtkNetCDFCAMReader.cxx +++ b/IO/NetCDF/vtkNetCDFCAMReader.cxx @@ -18,6 +18,7 @@ #include "vtkDoubleArray.h" #include "vtkFieldData.h" #include "vtkFloatArray.h" +#include "vtkIncrementalOctreePointLocator.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkMath.h" @@ -29,7 +30,7 @@ #include "vtkStreamingDemandDrivenPipeline.h" #include "vtkUnstructuredGrid.h" -#include <map> +#include <vector> #include <vtk_netcdfcpp.h> namespace @@ -38,14 +39,30 @@ namespace // a cell that wraps from the right side of the domain to the left side) bool IsCellInverted(double points[4][3]) { + // We test the normal 3 points at a time. Not all grids are well-behaved + // i.e. consistenly use 0 or 360. We've had grid where 3 points on the left + // side, and just 1 on the right. Just checking the first 3 points (which is + // what ComputeNormal() does, we may (and do) miss a few cells. + // See BUG #0014897. double normal[3]; - vtkPolygon::ComputeNormal(4, points[0], normal); + vtkPolygon::ComputeNormal(3, points[0], normal); + if(normal[2] > 0) + { + return true; + } + vtkPolygon::ComputeNormal(3, points[1], normal); if(normal[2] > 0) { return true; } return false; } + + template <class T> + inline bool IsZero(const T& val) + { + return std::abs(val) < std::numeric_limits<T>::epsilon(); + } } vtkStandardNewMacro(vtkNetCDFCAMReader); @@ -60,7 +77,6 @@ vtkNetCDFCAMReader::vtkNetCDFCAMReader() this->PointsFile = NULL; this->ConnectivityFile = NULL; this->SingleLevel = 0; - this->CellLayerRight = 1; this->TimeSteps = NULL; this->NumberOfTimeSteps = 0; this->SetNumberOfInputPorts(0); @@ -330,6 +346,8 @@ int vtkNetCDFCAMReader::RequestData( NcVar* lon = this->PointsFile->get_var("lon"); NcVar* lat = this->PointsFile->get_var("lat"); vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New(); + output->SetPoints(points); + long numFilePoints = dimension->size(); if(lat == NULL || lon == NULL) { @@ -378,11 +396,19 @@ int vtkNetCDFCAMReader::RequestData( // domain and only the points on the left boundary are included in // the points file. if a cell uses a point that is on the left // boundary and it should be on the right boundary we will have - // to create that point. that's what boundaryPoints is used for. - // note that if this->CellLayerRight is false then we do the opposite - // and make the 'connecting' cell on the left side of the domain - // we don't actually build the cells yet though. - std::map<vtkIdType, vtkIdType> boundaryPoints; + // to create that point. That's what boundaryPoints is used for. + // The (index + numFilePoints) gives us the new point id, and the value + // for that in this array will correspond to the original point id that the + // boundaryPoint is duplicate of. + std::vector<vtkIdType> boundaryPoints; + + // To avoid creating multiple duplicates, we we a + // vtkIncrementalOctreePointLocator. + vtkSmartPointer<vtkIncrementalOctreePointLocator> locator = + vtkSmartPointer<vtkIncrementalOctreePointLocator>::New(); + locator->SetDataSet(output); // dataset only has points right now. + locator->BuildLocator(); + dimension = this->ConnectivityFile->get_dim("ncells"); if(dimension == NULL) { @@ -411,8 +437,6 @@ int vtkNetCDFCAMReader::RequestData( connectivity->set_cur(0, beginCell); connectivity->get(&(cellConnectivity[0]), 4, numLocalCells); - double leftSide = 1.; - double rightSide = 359.; for(long i=0;i<numLocalCells;i++) { vtkIdType pointIds[4]; @@ -422,45 +446,87 @@ int vtkNetCDFCAMReader::RequestData( pointIds[j] = cellConnectivity[i+j*numLocalCells]-1; points->GetPoint(pointIds[j], coords[j]); } - if(IsCellInverted(coords) == true) + if (IsCellInverted(coords) == true) { - for(int j=0;j<4;j++) + // First decide whether we're putting this cell on the 360 side (right) or on the + // 0 side (left). We decide this based on which side will have the + // smallest protrusion. + double delta = 0.0; + bool anchorLeft = false; + for (int j=0; j < 4; ++j) + { + // We're assured that coords[j][0] is in the range [0, 360]. + // We just that fact to avoid having to do a std::abs() here. + double rightDelta = (360.0 - coords[j][0]); + double leftDelta = coords[j][0]; // i.e. (coords[j][0] - 0.0). + if (IsZero(rightDelta) || IsZero(leftDelta) || rightDelta == leftDelta) + { + // if the point is equidistant from both ends or is one of the ends, + // we let the other points in this cell dictate where the cell should + // anchor since this point can easily be anchored on either side with + // no side effects. + continue; + } + if (rightDelta < leftDelta) + { + if (rightDelta > delta) + { + delta = rightDelta; + anchorLeft = false; + } + } + else + { + if (leftDelta > delta) + { + delta = leftDelta; + anchorLeft = true; + } + } + } + // Once we've decided where we're anchoring we adjust the points. + for (int j=0; j < 4; ++j) { - if(this->CellLayerRight && coords[j][0] < leftSide) + if (anchorLeft) { - std::map<vtkIdType, vtkIdType>::iterator otherPoint = - boundaryPoints.find(pointIds[j]); - if(otherPoint != boundaryPoints.end()) - { // already made point on the right boundary - cellConnectivity[i+j*numLocalCells] = otherPoint->second + 1; + // if coords[j] is closer to right (360), move it to the left. + if ( (360.0 - coords[j][0]) < coords[j][0] ) + { + coords[j][0] -= 360.0; } else - { // need to make point on the right boundary - vtkIdType index = - points->InsertNextPoint(coords[j][0]+360., coords[j][1], coords[j][2]); - cellConnectivity[i+j*numLocalCells] = index+1; - boundaryPoints[pointIds[j]] = index; + { + continue; } } - else if(this->CellLayerRight == 0 && coords[j][0] > rightSide) + else { - std::map<vtkIdType, vtkIdType>::iterator otherPoint = - boundaryPoints.find(pointIds[j]); - if(otherPoint != boundaryPoints.end()) - { // already made point on the right boundary - cellConnectivity[i+j*numLocalCells] = otherPoint->second+1; + // if coords[j] is closer to left (0), move it to the right + if ( coords[j][0] < (360.0 - coords[j][0]) ) + { + coords[j][0] += 360.0; } else - { // need to make point on the right boundary - vtkIdType index = - points->InsertNextPoint(coords[j][0]-360., coords[j][1], coords[j][2]); - cellConnectivity[i+j*numLocalCells] = index+1; - boundaryPoints[pointIds[j]] = index; + { + continue; } } + // Okay, we have moved the coords. Update the boundaryPoints so which + // original point id is this new point id a clone of. + vtkIdType newPtId; + if (locator->InsertUniquePoint(coords[j], newPtId) == 1) + { + // if a new point was indeed inserted, we need to update the + // boundaryPoints to keep track of it. + assert(newPtId >= numFilePoints && pointIds[j] < newPtId); + assert(static_cast<vtkIdType>(boundaryPoints.size()) == (newPtId-numFilePoints)); + boundaryPoints.push_back(pointIds[j]); + } + cellConnectivity[i+j*numLocalCells] = (newPtId + 1); // note: 1-indexed. } } } + locator = NULL; // release the locator memory. // we now have all of the points at a single level. build them up // for the rest of the levels before creating the cells. @@ -484,7 +550,7 @@ int vtkNetCDFCAMReader::RequestData( } points->Modified(); - output->SetPoints(points); + points->Squeeze(); this->SetProgress(.5); // educated guess for progress @@ -601,13 +667,14 @@ int vtkNetCDFCAMReader::RequestData( pointData->CopyAllOn(); pointData->CopyAllocate(output->GetPointData(), output->GetNumberOfPoints()); - for(std::map<vtkIdType, vtkIdType>::const_iterator it= - boundaryPoints.begin();it!=boundaryPoints.end();it++) + + vtkIdType newPtId = numFilePoints; + for (std::vector<vtkIdType>::const_iterator it= + boundaryPoints.begin(); it!=boundaryPoints.end(); ++it, ++newPtId) { for(long lev=0;lev<numLocalCellLevels+1-this->SingleLevel;lev++) { - pointData->CopyData(pointData, it->first+lev*numPointsPerLevel, - it->second+lev*numPointsPerLevel); + pointData->CopyData(pointData, (*it), newPtId); } } @@ -755,6 +822,23 @@ bool vtkNetCDFCAMReader::GetPartitioning( return true; } +//---------------------------------------------------------------------------- +#if !defined(VTK_LEGACY_REMOVE) +void vtkNetCDFCAMReader::SetCellLayerRight(int) +{ + VTK_LEGACY_BODY(vtkNetCDFCAMReader::SetCellLayerRight, "VTK 6.3"); +} +#endif + +//---------------------------------------------------------------------------- +#if !defined(VTK_LEGACY_REMOVE) +int vtkNetCDFCAMReader::GetCellLayerRight() +{ + VTK_LEGACY_BODY(vtkNetCDFCAMReader::GetCellLayerRight, "VTK 6.3"); + return 0; +} +#endif + //---------------------------------------------------------------------------- void vtkNetCDFCAMReader::PrintSelf(ostream& os, vtkIndent indent) { @@ -765,7 +849,6 @@ void vtkNetCDFCAMReader::PrintSelf(ostream& os, vtkIndent indent) (this->ConnectivityFileName ? this->ConnectivityFileName : "(NULL)") << endl; os << indent << "SingleLevel: " << this->SingleLevel << endl; - os << indent << "CellLayerRight: " << this->CellLayerRight << endl; if(this->PointsFile) { os << indent << "PointsFile: " << this->PointsFile << endl; diff --git a/IO/NetCDF/vtkNetCDFCAMReader.h b/IO/NetCDF/vtkNetCDFCAMReader.h index 6100dfc0c015208fa91203902f357b6020fc5208..43f0b637e53e2cd97d702cf3d817a9791c2e1e89 100644 --- a/IO/NetCDF/vtkNetCDFCAMReader.h +++ b/IO/NetCDF/vtkNetCDFCAMReader.h @@ -67,8 +67,10 @@ public: // Specify which "side" of the domain to add the connecting // cells at. 0 indicates left side and 1 indicates right side. // The default is the right side. - vtkSetMacro(CellLayerRight, int); - vtkGetMacro(CellLayerRight, int); + // @deprecated This method is no longer supported. The reader automatically + // decides which side to pad cells on. Using this method has no effect. + VTK_LEGACY(void SetCellLayerRight(int)); + VTK_LEGACY(int GetCellLayerRight()); protected: vtkNetCDFCAMReader(); @@ -110,8 +112,6 @@ private: int SingleLevel; - int CellLayerRight; - double * TimeSteps; long NumberOfTimeSteps;