/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkFeatureEdges.cxx,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkFeatureEdges.h" #include "vtkFloatArray.h" #include "vtkMath.h" #include "vtkMergePoints.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkObjectFactory.h" #include "vtkPolyData.h" #include "vtkPolygon.h" #include "vtkStreamingDemandDrivenPipeline.h" #include "vtkTriangleStrip.h" #include "vtkUnsignedCharArray.h" #include "vtkCellArray.h" #include "vtkCellData.h" #include "vtkPointData.h" vtkCxxRevisionMacro(vtkFeatureEdges, "$Revision: 1.72 $"); vtkStandardNewMacro(vtkFeatureEdges); // Construct object with feature angle = 30; all types of edges, except // manifold edges, are extracted and colored. vtkFeatureEdges::vtkFeatureEdges() { this->FeatureAngle = 30.0; this->BoundaryEdges = 1; this->FeatureEdges = 1; this->NonManifoldEdges = 1; this->ManifoldEdges = 0; this->Coloring = 1; this->Locator = NULL; } vtkFeatureEdges::~vtkFeatureEdges() { if ( this->Locator ) { this->Locator->UnRegister(this); this->Locator = NULL; } } // Generate feature edges for mesh int vtkFeatureEdges::RequestData( vtkInformation *vtkNotUsed(request), vtkInformationVector **inputVector, vtkInformationVector *outputVector) { // get the info objects vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); vtkInformation *outInfo = outputVector->GetInformationObject(0); // get the input and ouptut vtkPolyData *input = vtkPolyData::SafeDownCast( inInfo->Get(vtkDataObject::DATA_OBJECT())); vtkPolyData *output = vtkPolyData::SafeDownCast( outInfo->Get(vtkDataObject::DATA_OBJECT())); vtkPoints *inPts; vtkPoints *newPts; vtkFloatArray *newScalars = NULL; vtkCellArray *newLines; vtkPolyData *Mesh; int i; vtkIdType j, numNei, cellId; vtkIdType numBEdges, numNonManifoldEdges, numFedges, numManifoldEdges; double scalar, n[3], x1[3], x2[3]; double cosAngle = 0; vtkIdType lineIds[2]; vtkIdType npts = 0; vtkIdType *pts = 0; vtkCellArray *inPolys, *inStrips, *newPolys; vtkFloatArray *polyNormals = NULL; vtkIdType numPts, numCells, numPolys, numStrips, nei; vtkIdList *neighbors; vtkIdType p1, p2, newId; vtkPointData *pd=input->GetPointData(), *outPD=output->GetPointData(); vtkCellData *cd=input->GetCellData(), *outCD=output->GetCellData(); unsigned char* ghostLevels=0; unsigned char updateLevel = (unsigned char)(output->GetUpdateGhostLevel()); vtkDebugMacro(<<"Executing feature edges"); vtkDataArray* temp = 0; if (cd) { temp = cd->GetArray("vtkGhostLevels"); } if ( (!temp) || (temp->GetDataType() != VTK_UNSIGNED_CHAR) || (temp->GetNumberOfComponents() != 1)) { vtkDebugMacro("No appropriate ghost levels field available."); } else { ghostLevels = ((vtkUnsignedCharArray*)temp)->GetPointer(0); } // Check input // inPts=input->GetPoints(); numCells = input->GetNumberOfCells(); numPolys = input->GetNumberOfPolys(); numStrips = input->GetNumberOfStrips(); if ( (numPts=input->GetNumberOfPoints()) < 1 || !inPts || (numPolys < 1 && numStrips < 1) ) { vtkDebugMacro(<<"No input data!"); return 1; } if ( !this->BoundaryEdges && !this->NonManifoldEdges && !this->FeatureEdges && !this->ManifoldEdges ) { vtkDebugMacro(<<"All edge types turned off!"); } // Build cell structure. Might have to triangulate the strips. Mesh = vtkPolyData::New(); Mesh->SetPoints(inPts); inPolys=input->GetPolys(); if ( numStrips > 0 ) { newPolys = vtkCellArray::New(); if ( numPolys > 0 ) { newPolys->DeepCopy(inPolys); } else { newPolys->Allocate(newPolys->EstimateSize(numStrips,5)); } inStrips = input->GetStrips(); for ( inStrips->InitTraversal(); inStrips->GetNextCell(npts,pts); ) { vtkTriangleStrip::DecomposeStrip(npts, pts, newPolys); } Mesh->SetPolys(newPolys); newPolys->Delete(); } else { newPolys = inPolys; Mesh->SetPolys(newPolys); } Mesh->BuildLinks(); // Allocate storage for lines/points (arbitrary allocation sizes) // newPts = vtkPoints::New(); newPts->Allocate(numPts/10,numPts); newLines = vtkCellArray::New(); newLines->Allocate(numPts/10); if ( this->Coloring ) { newScalars = vtkFloatArray::New(); newScalars->SetName("Edge Types"); newScalars->Allocate(numCells/10,numCells); } outPD->CopyAllocate(pd, numPts); outCD->CopyAllocate(cd, numCells); // Get our locator for merging points // if ( this->Locator == NULL ) { this->CreateDefaultLocator(); } this->Locator->InitPointInsertion (newPts, input->GetBounds()); // Loop over all polygons generating boundary, non-manifold, // and feature edges // if ( this->FeatureEdges ) { polyNormals = vtkFloatArray::New(); polyNormals->SetNumberOfComponents(3); polyNormals->Allocate(3*newPolys->GetNumberOfCells()); for (cellId=0, newPolys->InitTraversal(); newPolys->GetNextCell(npts,pts); cellId++) { vtkPolygon::ComputeNormal(inPts,npts,pts,n); polyNormals->InsertTuple(cellId,n); } cosAngle = cos ((double) vtkMath::DegreesToRadians() * this->FeatureAngle); } neighbors = vtkIdList::New(); neighbors->Allocate(VTK_CELL_SIZE); int abort=0; vtkIdType progressInterval=numCells/20+1; numBEdges = numNonManifoldEdges = numFedges = numManifoldEdges = 0; for (cellId=0, newPolys->InitTraversal(); newPolys->GetNextCell(npts,pts) && !abort; cellId++) { if ( ! (cellId % progressInterval) ) //manage progress / early abort { this->UpdateProgress ((double)cellId / numCells); abort = this->GetAbortExecute(); } for (i=0; i < npts; i++) { p1 = pts[i]; p2 = pts[(i+1)%npts]; Mesh->GetCellEdgeNeighbors(cellId,p1,p2, neighbors); numNei = neighbors->GetNumberOfIds(); if ( this->BoundaryEdges && numNei < 1 ) { if (ghostLevels && ghostLevels[cellId] > updateLevel) { continue; } else { numBEdges++; scalar = 0.0; } } else if ( this->NonManifoldEdges && numNei > 1 ) { // check to make sure that this edge hasn't been created before for (j=0; j < numNei; j++) { if ( neighbors->GetId(j) < cellId ) { break; } } if ( j >= numNei ) { if (ghostLevels && ghostLevels[cellId] > updateLevel) { continue; } else { numNonManifoldEdges++; scalar = 0.222222; } } else { continue; } } else if ( this->FeatureEdges && numNei == 1 && (nei=neighbors->GetId(0)) > cellId ) { double neiTuple[3]; double cellTuple[3]; polyNormals->GetTuple(nei, neiTuple); polyNormals->GetTuple(cellId, cellTuple); if ( vtkMath::Dot(neiTuple, cellTuple) <= cosAngle ) { if (ghostLevels && ghostLevels[cellId] > updateLevel) { continue; } else { numFedges++; scalar = 0.444444; } } else { continue; } } else if ( this->ManifoldEdges && numNei == 1 && neighbors->GetId(0) > cellId ) { if (ghostLevels && ghostLevels[cellId] > updateLevel) { continue; } else { numManifoldEdges++; scalar = 0.666667; } } else { continue; } // Add edge to output Mesh->GetPoint(p1, x1); Mesh->GetPoint(p2, x2); if ( this->Locator->InsertUniquePoint(x1, lineIds[0]) ) { outPD->CopyData (pd,p1,lineIds[0]); } if ( this->Locator->InsertUniquePoint(x2, lineIds[1]) ) { outPD->CopyData (pd,p2,lineIds[1]); } newId = newLines->InsertNextCell(2,lineIds); outCD->CopyData (cd,cellId,newId); if ( this->Coloring ) { newScalars->InsertTuple(newId, &scalar); } } } vtkDebugMacro(<<"Created " << numBEdges << " boundary edges, " << numNonManifoldEdges << " non-manifold edges, " << numFedges << " feature edges, " << numManifoldEdges << " manifold edges"); // Update ourselves. // if ( this->FeatureEdges ) { polyNormals->Delete(); } Mesh->Delete(); output->SetPoints(newPts); newPts->Delete(); neighbors->Delete(); output->SetLines(newLines); newLines->Delete(); if ( this->Coloring ) { int idx = outCD->AddArray(newScalars); outCD->SetActiveAttribute(idx, vtkDataSetAttributes::SCALARS); newScalars->Delete(); } return 1; } void vtkFeatureEdges::CreateDefaultLocator() { if ( this->Locator == NULL ) { this->Locator = vtkMergePoints::New(); } } // Specify a spatial locator for merging points. By // default an instance of vtkMergePoints is used. void vtkFeatureEdges::SetLocator(vtkPointLocator *locator) { if ( this->Locator == locator ) { return; } if ( this->Locator ) { this->Locator->UnRegister(this); this->Locator = NULL; } if ( locator ) { locator->Register(this); } this->Locator = locator; this->Modified(); } unsigned long int vtkFeatureEdges::GetMTime() { unsigned long mTime=this->Superclass::GetMTime(); unsigned long time; if ( this->Locator != NULL ) { time = this->Locator->GetMTime(); mTime = ( time > mTime ? time : mTime ); } return mTime; } int vtkFeatureEdges::RequestUpdateExtent( vtkInformation *vtkNotUsed(request), vtkInformationVector **inputVector, vtkInformationVector *outputVector) { // get the info objects vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); vtkInformation *outInfo = outputVector->GetInformationObject(0); int numPieces, ghostLevel; numPieces = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES()); ghostLevel = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS()); if (numPieces > 1) { inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS(), ghostLevel + 1); } return 1; } void vtkFeatureEdges::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); os << indent << "Feature Angle: " << this->FeatureAngle << "\n"; os << indent << "Boundary Edges: " << (this->BoundaryEdges ? "On\n" : "Off\n"); os << indent << "Feature Edges: " << (this->FeatureEdges ? "On\n" : "Off\n"); os << indent << "Non-Manifold Edges: " << (this->NonManifoldEdges ? "On\n" : "Off\n"); os << indent << "Manifold Edges: " << (this->ManifoldEdges ? "On\n" : "Off\n"); os << indent << "Coloring: " << (this->Coloring ? "On\n" : "Off\n"); if ( this->Locator ) { os << indent << "Locator: " << this->Locator << "\n"; } else { os << indent << "Locator: (none)\n"; } }