You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
570 lines
16 KiB
570 lines
16 KiB
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: $RCSfile: vtkPolyDataConnectivityFilter.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 "vtkPolyDataConnectivityFilter.h"
|
|
|
|
#include "vtkCell.h"
|
|
#include "vtkCellArray.h"
|
|
#include "vtkCellData.h"
|
|
#include "vtkFloatArray.h"
|
|
#include "vtkIdList.h"
|
|
#include "vtkIdTypeArray.h"
|
|
#include "vtkMath.h"
|
|
#include "vtkInformation.h"
|
|
#include "vtkInformationVector.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkPointData.h"
|
|
#include "vtkPolyData.h"
|
|
|
|
vtkCxxRevisionMacro(vtkPolyDataConnectivityFilter, "$Revision: 1.40 $");
|
|
vtkStandardNewMacro(vtkPolyDataConnectivityFilter);
|
|
|
|
// Construct with default extraction mode to extract largest regions.
|
|
vtkPolyDataConnectivityFilter::vtkPolyDataConnectivityFilter()
|
|
{
|
|
this->RegionSizes = vtkIdTypeArray::New();
|
|
this->ExtractionMode = VTK_EXTRACT_LARGEST_REGION;
|
|
this->ColorRegions = 0;
|
|
|
|
this->ScalarConnectivity = 0;
|
|
this->ScalarRange[0] = 0.0;
|
|
this->ScalarRange[1] = 1.0;
|
|
|
|
this->ClosestPoint[0] = this->ClosestPoint[1] = this->ClosestPoint[2] = 0.0;
|
|
|
|
this->CellScalars = vtkFloatArray::New();
|
|
this->CellScalars->Allocate(8);
|
|
|
|
this->NeighborCellPointIds = vtkIdList::New();
|
|
this->NeighborCellPointIds->Allocate(8);
|
|
|
|
this->Seeds = vtkIdList::New();
|
|
this->SpecifiedRegionIds = vtkIdList::New();
|
|
}
|
|
|
|
vtkPolyDataConnectivityFilter::~vtkPolyDataConnectivityFilter()
|
|
{
|
|
this->RegionSizes->Delete();
|
|
this->CellScalars->Delete();
|
|
this->NeighborCellPointIds->Delete();
|
|
this->Seeds->Delete();
|
|
this->SpecifiedRegionIds->Delete();
|
|
}
|
|
|
|
int vtkPolyDataConnectivityFilter::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()));
|
|
|
|
vtkIdType cellId, newCellId, i, pt;
|
|
int j;
|
|
vtkIdType numPts, numCells;
|
|
vtkPoints *inPts;
|
|
vtkPoints *newPts;
|
|
vtkIdType *cells, *pts, npts, id, n;
|
|
unsigned short ncells;
|
|
vtkIdType maxCellsInRegion;
|
|
int largestRegionId = 0;
|
|
vtkPointData *pd=input->GetPointData(), *outputPD=output->GetPointData();
|
|
vtkCellData *cd=input->GetCellData(), *outputCD=output->GetCellData();
|
|
|
|
vtkDebugMacro(<<"Executing polygon connectivity filter.");
|
|
|
|
// Check input/allocate storage
|
|
//
|
|
inPts = input->GetPoints();
|
|
|
|
if (inPts == NULL)
|
|
{
|
|
vtkErrorMacro("No points!");
|
|
return 1;
|
|
}
|
|
|
|
numPts = inPts->GetNumberOfPoints();
|
|
numCells = input->GetNumberOfCells();
|
|
|
|
if ( numPts < 1 || numCells < 1 )
|
|
{
|
|
vtkDebugMacro(<<"No data to connect!");
|
|
return 1;
|
|
}
|
|
|
|
// See whether to consider scalar connectivity
|
|
//
|
|
this->InScalars = input->GetPointData()->GetScalars();
|
|
if ( !this->ScalarConnectivity )
|
|
{
|
|
this->InScalars = NULL;
|
|
}
|
|
else
|
|
{
|
|
if ( this->ScalarRange[1] < this->ScalarRange[0] )
|
|
{
|
|
this->ScalarRange[1] = this->ScalarRange[0];
|
|
}
|
|
}
|
|
|
|
// Build cell structure
|
|
//
|
|
this->Mesh = vtkPolyData::New();
|
|
this->Mesh->CopyStructure(input);
|
|
this->Mesh->BuildLinks();
|
|
this->UpdateProgress(0.10);
|
|
|
|
// Initialize. Keep track of points and cells visited.
|
|
//
|
|
this->RegionSizes->Reset();
|
|
this->Visited = new int[numCells];
|
|
for ( i=0; i < numCells; i++ )
|
|
{
|
|
this->Visited[i] = -1;
|
|
}
|
|
this->PointMap = new vtkIdType[numPts];
|
|
for ( i=0; i < numPts; i++ )
|
|
{
|
|
this->PointMap[i] = -1;
|
|
}
|
|
|
|
this->NewScalars = vtkFloatArray::New();
|
|
this->NewScalars->SetNumberOfTuples(numPts);
|
|
newPts = vtkPoints::New();
|
|
newPts->Allocate(numPts);
|
|
|
|
// Traverse all cells marking those visited. Each new search
|
|
// starts a new connected region. Connected region grows
|
|
// using a connected wave propagation.
|
|
//
|
|
this->Wave = vtkIdList::New();
|
|
this->Wave->Allocate(numPts/4+1,numPts);
|
|
this->Wave2 = vtkIdList::New();
|
|
this->Wave2->Allocate(numPts/4+1,numPts);
|
|
|
|
this->PointNumber = 0;
|
|
this->RegionNumber = 0;
|
|
maxCellsInRegion = 0;
|
|
|
|
this->CellIds = vtkIdList::New();
|
|
this->CellIds->Allocate(8, VTK_CELL_SIZE);
|
|
this->PointIds = vtkIdList::New();
|
|
this->PointIds->Allocate(8, VTK_CELL_SIZE);
|
|
|
|
if ( this->ExtractionMode != VTK_EXTRACT_POINT_SEEDED_REGIONS &&
|
|
this->ExtractionMode != VTK_EXTRACT_CELL_SEEDED_REGIONS &&
|
|
this->ExtractionMode != VTK_EXTRACT_CLOSEST_POINT_REGION )
|
|
{ //visit all cells marking with region number
|
|
for (cellId=0; cellId < numCells; cellId++)
|
|
{
|
|
if ( cellId && !(cellId % 5000) )
|
|
{
|
|
this->UpdateProgress (0.1 + 0.8*cellId/numCells);
|
|
}
|
|
|
|
if ( this->Visited[cellId] < 0 )
|
|
{
|
|
this->NumCellsInRegion = 0;
|
|
this->Wave->InsertNextId(cellId);
|
|
this->TraverseAndMark ();
|
|
|
|
if ( this->NumCellsInRegion > maxCellsInRegion )
|
|
{
|
|
maxCellsInRegion = this->NumCellsInRegion;
|
|
largestRegionId = this->RegionNumber;
|
|
}
|
|
|
|
this->RegionSizes->InsertValue(this->RegionNumber++,
|
|
this->NumCellsInRegion);
|
|
this->Wave->Reset();
|
|
this->Wave2->Reset();
|
|
}
|
|
}
|
|
}
|
|
else // regions have been seeded, everything considered in same region
|
|
{
|
|
this->NumCellsInRegion = 0;
|
|
|
|
if ( this->ExtractionMode == VTK_EXTRACT_POINT_SEEDED_REGIONS )
|
|
{
|
|
for (i=0; i < this->Seeds->GetNumberOfIds(); i++)
|
|
{
|
|
pt = this->Seeds->GetId(i);
|
|
if ( pt >= 0 )
|
|
{
|
|
this->Mesh->GetPointCells(pt,ncells,cells);
|
|
for (j=0; j < ncells; j++)
|
|
{
|
|
this->Wave->InsertNextId(cells[j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ( this->ExtractionMode == VTK_EXTRACT_CELL_SEEDED_REGIONS )
|
|
{
|
|
for (i=0; i < this->Seeds->GetNumberOfIds(); i++)
|
|
{
|
|
cellId = this->Seeds->GetId(i);
|
|
if ( cellId >= 0 )
|
|
{
|
|
this->Wave->InsertNextId(cellId);
|
|
}
|
|
}
|
|
}
|
|
else if ( this->ExtractionMode == VTK_EXTRACT_CLOSEST_POINT_REGION )
|
|
{//loop over points, find closest one
|
|
double minDist2, dist2, x[3];
|
|
int minId = 0;
|
|
for (minDist2=VTK_DOUBLE_MAX, i=0; i<numPts; i++)
|
|
{
|
|
inPts->GetPoint(i,x);
|
|
dist2 = vtkMath::Distance2BetweenPoints(x,this->ClosestPoint);
|
|
if ( dist2 < minDist2 )
|
|
{
|
|
minId = i;
|
|
minDist2 = dist2;
|
|
}
|
|
}
|
|
this->Mesh->GetPointCells(minId,ncells,cells);
|
|
for (j=0; j < ncells; j++)
|
|
{
|
|
this->Wave->InsertNextId(cells[j]);
|
|
}
|
|
}
|
|
this->UpdateProgress (0.5);
|
|
|
|
//mark all seeded regions
|
|
this->TraverseAndMark ();
|
|
this->RegionSizes->InsertValue(this->RegionNumber,this->NumCellsInRegion);
|
|
this->UpdateProgress (0.9);
|
|
}//else extracted seeded cells
|
|
|
|
vtkDebugMacro (<<"Extracted " << this->RegionNumber << " region(s)");
|
|
this->Wave->Delete();
|
|
this->Wave2->Delete();
|
|
|
|
// Now that points and cells have been marked, traverse these lists pulling
|
|
// everything that has been visited.
|
|
//
|
|
//Pass through point data that has been visited
|
|
pd = input->GetPointData();
|
|
if ( this->ColorRegions )
|
|
{
|
|
outputPD->CopyScalarsOff();
|
|
}
|
|
outputPD->CopyAllocate(pd);
|
|
outputCD->CopyAllocate(cd);
|
|
|
|
for (i=0; i < numPts; i++)
|
|
{
|
|
if ( this->PointMap[i] > -1 )
|
|
{
|
|
newPts->InsertPoint(this->PointMap[i],inPts->GetPoint(i));
|
|
outputPD->CopyData(pd,i,this->PointMap[i]);
|
|
}
|
|
}
|
|
|
|
// if coloring regions; send down new scalar data
|
|
if ( this->ColorRegions )
|
|
{
|
|
int idx = outputPD->AddArray(this->NewScalars);
|
|
outputPD->SetActiveAttribute(idx, vtkDataSetAttributes::SCALARS);
|
|
}
|
|
this->NewScalars->Delete();
|
|
|
|
output->SetPoints(newPts);
|
|
newPts->Delete();
|
|
|
|
// Create output cells. Have to allocate storage first.
|
|
//
|
|
if ( (n=input->GetVerts()->GetNumberOfCells()) > 0 )
|
|
{
|
|
vtkCellArray *newVerts = vtkCellArray::New();
|
|
newVerts->Allocate(n,n);
|
|
output->SetVerts(newVerts);
|
|
newVerts->Delete();
|
|
}
|
|
if ( (n=input->GetLines()->GetNumberOfCells()) > 0 )
|
|
{
|
|
vtkCellArray *newLines = vtkCellArray::New();
|
|
newLines->Allocate(2*n,n);
|
|
output->SetLines(newLines);
|
|
newLines->Delete();
|
|
}
|
|
if ( (n=input->GetPolys()->GetNumberOfCells()) > 0 )
|
|
{
|
|
vtkCellArray *newPolys = vtkCellArray::New();
|
|
newPolys->Allocate(3*n,n);
|
|
output->SetPolys(newPolys);
|
|
newPolys->Delete();
|
|
}
|
|
if ( (n=input->GetStrips()->GetNumberOfCells()) > 0 )
|
|
{
|
|
vtkCellArray *newStrips = vtkCellArray::New();
|
|
newStrips->Allocate(5*n,n);
|
|
output->SetStrips(newStrips);
|
|
newStrips->Delete();
|
|
}
|
|
|
|
if ( this->ExtractionMode == VTK_EXTRACT_POINT_SEEDED_REGIONS ||
|
|
this->ExtractionMode == VTK_EXTRACT_CELL_SEEDED_REGIONS ||
|
|
this->ExtractionMode == VTK_EXTRACT_CLOSEST_POINT_REGION ||
|
|
this->ExtractionMode == VTK_EXTRACT_ALL_REGIONS)
|
|
{ // extract any cell that's been visited
|
|
for (cellId=0; cellId < numCells; cellId++)
|
|
{
|
|
if ( this->Visited[cellId] >= 0 )
|
|
{
|
|
this->Mesh->GetCellPoints(cellId, npts, pts);
|
|
this->PointIds->Reset();
|
|
for (i=0; i < npts; i++)
|
|
{
|
|
id = this->PointMap[pts[i]];
|
|
this->PointIds->InsertId(i,id);
|
|
}
|
|
newCellId = output->InsertNextCell(this->Mesh->GetCellType(cellId),
|
|
this->PointIds);
|
|
outputCD->CopyData(cd,cellId,newCellId);
|
|
}
|
|
}
|
|
}
|
|
else if ( this->ExtractionMode == VTK_EXTRACT_SPECIFIED_REGIONS )
|
|
{
|
|
for (cellId=0; cellId < numCells; cellId++)
|
|
{
|
|
int inReg, regionId;
|
|
if ( (regionId=this->Visited[cellId]) >= 0 )
|
|
{
|
|
for (inReg=0,i=0; i<this->SpecifiedRegionIds->GetNumberOfIds(); i++)
|
|
{
|
|
if ( regionId == this->SpecifiedRegionIds->GetId(i) )
|
|
{
|
|
inReg = 1;
|
|
break;
|
|
}
|
|
}
|
|
if ( inReg )
|
|
{
|
|
this->Mesh->GetCellPoints(cellId, npts, pts);
|
|
this->PointIds->Reset ();
|
|
for (i=0; i < npts; i++)
|
|
{
|
|
id = this->PointMap[pts[i]];
|
|
this->PointIds->InsertId(i,id);
|
|
}
|
|
newCellId = output->InsertNextCell(this->Mesh->GetCellType(cellId),
|
|
this->PointIds);
|
|
outputCD->CopyData(cd,cellId,newCellId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else //extract largest region
|
|
{
|
|
for (cellId=0; cellId < numCells; cellId++)
|
|
{
|
|
if ( this->Visited[cellId] == largestRegionId )
|
|
{
|
|
this->Mesh->GetCellPoints(cellId, npts, pts);
|
|
this->PointIds->Reset ();
|
|
for (i=0; i < npts; i++)
|
|
{
|
|
id = this->PointMap[pts[i]];
|
|
this->PointIds->InsertId(i,id);
|
|
}
|
|
newCellId = output->InsertNextCell(this->Mesh->GetCellType(cellId),
|
|
this->PointIds);
|
|
outputCD->CopyData(cd,cellId,newCellId);
|
|
}
|
|
}
|
|
}
|
|
|
|
delete [] this->Visited;
|
|
delete [] this->PointMap;
|
|
this->Mesh->Delete();
|
|
output->Squeeze();
|
|
this->CellIds->Delete();
|
|
this->PointIds->Delete();
|
|
|
|
int num = this->GetNumberOfExtractedRegions();
|
|
vtkIdType count = 0;
|
|
|
|
for (int ii = 0; ii < num; ii++)
|
|
{
|
|
count += this->RegionSizes->GetValue (ii);
|
|
}
|
|
vtkDebugMacro (<< "Total # of cells accounted for: " << count);
|
|
vtkDebugMacro (<<"Extracted " << output->GetNumberOfCells() << " cells");
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Mark current cell as visited and assign region number. Note:
|
|
// traversal occurs across shared vertices.
|
|
//
|
|
void vtkPolyDataConnectivityFilter::TraverseAndMark ()
|
|
{
|
|
vtkIdType cellId, ptId, numIds, i;
|
|
int j, k;
|
|
vtkIdType *pts, *cells, npts;
|
|
vtkIdList *tmpWave;
|
|
unsigned short ncells;
|
|
|
|
while ( (numIds=this->Wave->GetNumberOfIds()) > 0 )
|
|
{
|
|
for ( i=0; i < numIds; i++ )
|
|
{
|
|
cellId = this->Wave->GetId(i);
|
|
if ( this->Visited[cellId] < 0 )
|
|
{
|
|
this->Visited[cellId] = this->RegionNumber;
|
|
this->NumCellsInRegion++;
|
|
this->Mesh->GetCellPoints(cellId, npts, pts);
|
|
|
|
for (j=0; j < npts; j++)
|
|
{
|
|
if ( this->PointMap[ptId=pts[j]] < 0 )
|
|
{
|
|
this->PointMap[ptId] = this->PointNumber++;
|
|
this->NewScalars->SetComponent(this->PointMap[ptId], 0,
|
|
this->RegionNumber);
|
|
}
|
|
|
|
this->Mesh->GetPointCells(ptId,ncells,cells);
|
|
|
|
// check connectivity criterion (geometric + scalar)
|
|
for (k=0; k < ncells; k++)
|
|
{
|
|
cellId = cells[k];
|
|
if ( this->InScalars )
|
|
{
|
|
int numScalars, ii;
|
|
double s, range[2];
|
|
|
|
this->Mesh->GetCellPoints(cellId, this->NeighborCellPointIds);
|
|
numScalars = this->NeighborCellPointIds->GetNumberOfIds();
|
|
this->CellScalars->SetNumberOfTuples(numScalars);
|
|
this->InScalars->GetTuples(this->NeighborCellPointIds,
|
|
this->CellScalars);
|
|
range[0] = VTK_DOUBLE_MAX; range[1] = -VTK_DOUBLE_MAX;
|
|
for (ii=0; ii < numScalars; ii++)
|
|
{
|
|
s = this->CellScalars->GetComponent(ii, 0);
|
|
if ( s < range[0] )
|
|
{
|
|
range[0] = s;
|
|
}
|
|
if ( s > range[1] )
|
|
{
|
|
range[1] = s;
|
|
}
|
|
}
|
|
if ( range[1] >= this->ScalarRange[0] &&
|
|
range[0] <= this->ScalarRange[1] )
|
|
{
|
|
this->Wave2->InsertNextId(cellId);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this->Wave2->InsertNextId(cellId);
|
|
}
|
|
}//for all cells using this point
|
|
}//for all points of this cell
|
|
}//if cell not yet visited
|
|
}//for all cells in this wave
|
|
|
|
tmpWave = this->Wave;
|
|
this->Wave = this->Wave2;
|
|
this->Wave2 = tmpWave;
|
|
tmpWave->Reset();
|
|
} //while wave is not empty
|
|
|
|
return;
|
|
}
|
|
|
|
// Obtain the number of connected regions.
|
|
int vtkPolyDataConnectivityFilter::GetNumberOfExtractedRegions()
|
|
{
|
|
return this->RegionSizes->GetMaxId() + 1;
|
|
}
|
|
|
|
// Initialize list of point ids/cell ids used to seed regions.
|
|
void vtkPolyDataConnectivityFilter::InitializeSeedList()
|
|
{
|
|
this->Modified();
|
|
this->Seeds->Reset();
|
|
}
|
|
|
|
// Add a seed id (point or cell id). Note: ids are 0-offset.
|
|
void vtkPolyDataConnectivityFilter::AddSeed(int id)
|
|
{
|
|
this->Modified();
|
|
this->Seeds->InsertNextId(id);
|
|
}
|
|
|
|
// Delete a seed id (point or cell id). Note: ids are 0-offset.
|
|
void vtkPolyDataConnectivityFilter::DeleteSeed(int id)
|
|
{
|
|
this->Modified();
|
|
this->Seeds->DeleteId(id);
|
|
}
|
|
|
|
// Initialize list of region ids to extract.
|
|
void vtkPolyDataConnectivityFilter::InitializeSpecifiedRegionList()
|
|
{
|
|
this->Modified();
|
|
this->SpecifiedRegionIds->Reset();
|
|
}
|
|
|
|
// Add a region id to extract. Note: ids are 0-offset.
|
|
void vtkPolyDataConnectivityFilter::AddSpecifiedRegion(int id)
|
|
{
|
|
this->Modified();
|
|
this->SpecifiedRegionIds->InsertNextId(id);
|
|
}
|
|
|
|
// Delete a region id to extract. Note: ids are 0-offset.
|
|
void vtkPolyDataConnectivityFilter::DeleteSpecifiedRegion(int id)
|
|
{
|
|
this->Modified();
|
|
this->SpecifiedRegionIds->DeleteId(id);
|
|
}
|
|
|
|
void vtkPolyDataConnectivityFilter::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os,indent);
|
|
|
|
os << indent << "Extraction Mode: ";
|
|
os << this->GetExtractionModeAsString() << "\n";
|
|
|
|
os << indent << "Closest Point: (" << this->ClosestPoint[0] << ", "
|
|
<< this->ClosestPoint[1] << ", " << this->ClosestPoint[2] << ")\n";
|
|
|
|
os << indent << "Color Regions: " << (this->ColorRegions ? "On\n" : "Off\n");
|
|
|
|
os << indent << "Scalar Connectivity: "
|
|
<< (this->ScalarConnectivity ? "On\n" : "Off\n");
|
|
|
|
double *range = this->GetScalarRange();
|
|
os << indent << "Scalar Range: (" << range[0] << ", " << range[1] << ")\n";
|
|
}
|
|
|
|
|