Cloned library of VTK-5.0.0 with extra build files for internal package management.
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.
 
 
 
 
 
 

640 lines
19 KiB

/*=========================================================================
Program: Visualization Toolkit
Module: $RCSfile: vtkSelectPolyData.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 "vtkSelectPolyData.h"
#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkCharArray.h"
#include "vtkExecutive.h"
#include "vtkFloatArray.h"
#include "vtkGarbageCollector.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkLine.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkPoints.h"
#include "vtkPolyData.h"
#include "vtkPolygon.h"
#include "vtkTriangleFilter.h"
#include "vtkTriangleStrip.h"
vtkCxxRevisionMacro(vtkSelectPolyData, "$Revision: 1.34 $");
vtkStandardNewMacro(vtkSelectPolyData);
vtkCxxSetObjectMacro(vtkSelectPolyData,Loop,vtkPoints);
// Description:
// Instantiate object with InsideOut turned off.
vtkSelectPolyData::vtkSelectPolyData()
{
this->GenerateSelectionScalars = 0;
this->InsideOut = 0;
this->Loop = NULL;
this->SelectionMode = VTK_INSIDE_SMALLEST_REGION;
this->ClosestPoint[0] = this->ClosestPoint[1] = this->ClosestPoint[2] = 0.0;
this->GenerateUnselectedOutput = 0;
this->SetNumberOfOutputPorts(3);
vtkPolyData *output2 = vtkPolyData::New();
this->GetExecutive()->SetOutputData(1, output2);
output2->Delete();
vtkPolyData *output3 = vtkPolyData::New();
this->GetExecutive()->SetOutputData(2, output3);
output3->Delete();
}
vtkSelectPolyData::~vtkSelectPolyData()
{
if (this->Loop)
{
this->Loop->Delete();
}
}
vtkPolyData *vtkSelectPolyData::GetUnselectedOutput()
{
if (this->GetNumberOfOutputPorts() < 2)
{
return NULL;
}
return vtkPolyData::SafeDownCast(
this->GetExecutive()->GetOutputData(1));
}
vtkPolyData *vtkSelectPolyData::GetSelectionEdges()
{
if (this->GetNumberOfOutputPorts() < 3)
{
return NULL;
}
return vtkPolyData::SafeDownCast(
this->GetExecutive()->GetOutputData(2));
}
int vtkSelectPolyData::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 numPts, numLoopPts;
vtkPolyData *triMesh;
vtkPointData *inPD, *outPD=output->GetPointData();
vtkCellData *inCD, *outCD=output->GetCellData();
vtkIdType closest=0, numPolys, i, j;
int k;
vtkIdList *loopIds, *edgeIds, *neighbors;
double x[3], xLoop[3], closestDist2, dist2, t;
double x0[3], x1[3], vec[3], dir[3], neiX[3];
vtkCellArray *inPolys;
vtkPoints *inPts;
int numNei;
vtkIdType id, currentId = 0, nextId, pt1, pt2, numCells, neiId;
vtkIdType *cells, *pts, npts, numMeshLoopPts, prevId;
unsigned short int ncells;
int mark, s1, s2, val;
// Initialize and check data
vtkDebugMacro(<<"Selecting data...");
this->GetUnselectedOutput()->Initialize();
this->GetSelectionEdges()->Initialize();
if ( (numPts = input->GetNumberOfPoints()) < 1 )
{
vtkErrorMacro("Input contains no points");
return 1;
}
if ( this->Loop == NULL ||
(numLoopPts=this->Loop->GetNumberOfPoints()) < 3 )
{
vtkErrorMacro("Please define a loop with at least three points");
return 1;
}
// Okay, now we build unstructured representation. Make sure we're
// working with triangles.
vtkTriangleFilter *tf=vtkTriangleFilter::New();
tf->SetInput(input);
tf->PassLinesOff();
tf->PassVertsOff();
tf->Update();
triMesh = tf->GetOutput();
triMesh->Register(this);
tf->Delete();
inPD = triMesh->GetPointData();
inCD = triMesh->GetCellData();
numPts = triMesh->GetNumberOfPoints();
inPts = triMesh->GetPoints();
inPolys = triMesh->GetPolys();
numPolys = inPolys->GetNumberOfCells();
if ( numPolys < 1 )
{
vtkErrorMacro("This filter operates on surface primitives");
tf->Delete();
return 1;
}
this->Mesh = vtkPolyData::New();
this->Mesh->SetPoints(inPts); //inPts
this->Mesh->SetPolys(inPolys);
this->Mesh->BuildLinks(); //to do neighborhood searching
numCells = this->Mesh->GetNumberOfCells();
// First thing to do is find the closest mesh points to the loop
// points. This creates a list of point ids.
loopIds = vtkIdList::New();
loopIds->SetNumberOfIds(numLoopPts);
for ( i=0; i < numLoopPts; i++)
{
this->Loop->GetPoint(i,xLoop);
closest = -1;
closestDist2 = VTK_DOUBLE_MAX;
for ( j=0; j < numPts; j++)
{
inPts->GetPoint(j,x);
dist2 = vtkMath::Distance2BetweenPoints(x, xLoop);
if ( dist2 < closestDist2 )
{
closest = j;
closestDist2 = dist2;
}
} //for all input points
loopIds->SetId(i,closest);
} //for all loop points
// Now that we've got point ids, we build the loop. Start with the
// first two points in the loop (which define a line), and find the
// mesh edge that is directed along the line, and whose
// end point is closest to the line. Continue until loop closes in on
// itself.
edgeIds = vtkIdList::New();
edgeIds->Allocate(numLoopPts*10,1000);
neighbors = vtkIdList::New();
neighbors->Allocate(10000);
edgeIds->InsertNextId(loopIds->GetId(0));
for ( i=0; i < numLoopPts; i++ )
{
currentId = loopIds->GetId(i);
nextId = loopIds->GetId((i+1)%numLoopPts);
prevId = (-1);
inPts->GetPoint(currentId, x);
inPts->GetPoint(currentId, x0);
inPts->GetPoint(nextId, x1);
for (j=0; j<3; j++)
{
vec[j] = x1[j] - x0[j];
}
// track edge
for (id=currentId; id != nextId; )
{
this->GetPointNeighbors(id,neighbors); //points connected by edge
numNei = neighbors->GetNumberOfIds();
closest = -1;
closestDist2 = VTK_DOUBLE_MAX;
for (j=0; j<numNei; j++)
{
neiId = neighbors->GetId(j);
inPts->GetPoint(neiId, neiX);
for (k=0; k<3; k++)
{
dir[k] = neiX[k] - x[k];
}
if ( neiId != prevId && vtkMath::Dot(dir,vec) > 0.0 ) //candidate
{
dist2 = vtkLine::DistanceToLine(neiX, x0, x1);
if ( dist2 < closestDist2 )
{
closest = neiId;
closestDist2 = dist2;
}
}//in direction of line
}//for all neighbors
if ( closest < 0 )
{
vtkErrorMacro(<<"Can't follow edge");
return 1;
}
else
{
edgeIds->InsertNextId(closest);
prevId = id;
id = closest;
inPts->GetPoint(id, x);
}
}//for tracking edge
}//for all edges of loop
// mainly for debugging
numMeshLoopPts = edgeIds->GetNumberOfIds();
vtkCellArray *selectionEdges=vtkCellArray::New();
selectionEdges->Allocate(selectionEdges->EstimateSize(1,numMeshLoopPts),100);
selectionEdges->InsertNextCell(numMeshLoopPts);
for (i=0; i<numMeshLoopPts; i++)
{
selectionEdges->InsertCellPoint(edgeIds->GetId(i));
}
this->GetSelectionEdges()->SetPoints(inPts);
this->GetSelectionEdges()->SetLines(selectionEdges);
selectionEdges->Delete();
// Phew...we've defined loop, now want to do a fill so we can extract the
// inside from the outside. Depending on GenerateSelectionScalars flag,
// we either set the distance of the points to the selection loop as
// zero (GenerateSelectionScalarsOff) or we evaluate the distance of these
// points to the lines. (Later we'll use a connected traversal to compute
// the distances to the remaining points.)
// Next, prepare to mark off inside/outside and on boundary of loop.
// Mark the boundary of the loop using point marks. Also initialize
// the advancing front (used to mark traversal/compute scalars).
// Prepare to compute the advancing front
vtkIntArray *cellMarks = vtkIntArray::New();
cellMarks->SetNumberOfValues(numCells);
for (i=0; i<numCells; i++) //mark unvisited
{
cellMarks->SetValue(i,VTK_LARGE_INTEGER);
}
vtkIntArray *pointMarks = vtkIntArray::New();
pointMarks->SetNumberOfValues(numPts);
for (i=0; i<numPts; i++) //mark unvisited
{
pointMarks->SetValue(i,VTK_LARGE_INTEGER);
}
vtkIdList *currentFront = vtkIdList::New(), *tmpFront;
vtkIdList *nextFront = vtkIdList::New();
for (i=0; i<numMeshLoopPts; i++)
{
id = edgeIds->GetId(i);
pointMarks->SetValue(id, 0); //marks the start of the front
currentFront->InsertNextId(id);
}
// Traverse the front as long as we can. We're basically computing a
// topological distance.
int maxFront=0;
vtkIdType maxFrontCell=(-1);
int currentFrontNumber=1, numPtsInFront;
while ( (numPtsInFront = currentFront->GetNumberOfIds()) )
{
for (i=0; i < numPtsInFront; i++)
{
id = currentFront->GetId(i);
this->Mesh->GetPointCells(id, ncells, cells);
for (j=0; j<ncells; j++)
{
id = cells[j];
if ( cellMarks->GetValue(id) == VTK_LARGE_INTEGER )
{
if ( currentFrontNumber > maxFront )
{
maxFrontCell = id;
}
cellMarks->SetValue(id, currentFrontNumber);
this->Mesh->GetCellPoints(id,npts,pts);
for (k=0; k<npts; k++)
{
if ( pointMarks->GetValue(pts[k]) == VTK_LARGE_INTEGER )
{
pointMarks->SetValue(pts[k], 1);
nextFront->InsertNextId(pts[k]);
}
}
}
} //for cells surrounding point
} //all points in front
currentFrontNumber++;
tmpFront = currentFront;
currentFront = nextFront;
nextFront = tmpFront;
nextFront->Reset();
} //while still advancing
// Okay, now one of the regions is filled with negative values. This fill
// operation assumes that everthing is connected.
if ( this->SelectionMode == VTK_INSIDE_CLOSEST_POINT_REGION )
{// find closest point and use as a seed
for (closestDist2=VTK_DOUBLE_MAX, j=0; j < numPts; j++)
{
inPts->GetPoint(j,x);
dist2 = vtkMath::Distance2BetweenPoints(x, this->ClosestPoint);
// get closest point not on the boundary
if ( dist2 < closestDist2 && pointMarks->GetValue(j) != 0 )
{
closest = j;
closestDist2 = dist2;
}
} //for all input points
this->Mesh->GetPointCells(closest, ncells, cells);
}
// We do the fill as a moving front. This is an alternative to recursion. The
// fill negates one region of the mesh on one side of the loop.
currentFront->Reset(); nextFront->Reset();
currentFront->InsertNextId(maxFrontCell);
vtkIdType numCellsInFront;
cellMarks->SetValue(maxFrontCell,-1);
while ( (numCellsInFront = currentFront->GetNumberOfIds()) > 0 )
{
for (i=0; i < numCellsInFront; i++)
{
id = currentFront->GetId(i);
this->Mesh->GetCellPoints(id, npts, pts);
for (j=0; j<3; j++)
{
pt1 = pts[j];
pt2 = pts[(j+1)%3];
s1 = pointMarks->GetValue(pt1);
s2 = pointMarks->GetValue(pt2);
if ( s1 != 0 )
{
pointMarks->SetValue(pt1, -1);
}
if ( ! (s1 == 0 && s2 == 0) )
{
this->Mesh->GetCellEdgeNeighbors(id, pt1, pt2, neighbors);
numNei = neighbors->GetNumberOfIds();
for (k=0; k<numNei; k++)
{
neiId = neighbors->GetId(k);
val = cellMarks->GetValue(neiId);
if ( val != -1 ) //-1 is what we're filling with
{
cellMarks->SetValue(neiId,-1);
nextFront->InsertNextId(neiId);
}
}
}//if can cross boundary
}//for all edges of cell
} //all cells in front
tmpFront = currentFront;
currentFront = nextFront;
nextFront = tmpFront;
nextFront->Reset();
} //while still advancing
// Now may have to invert fill value depending on what we wan to extract
if ( this->SelectionMode == VTK_INSIDE_SMALLEST_REGION )
{
for (i=0; i < numCells; i++)
{
mark = cellMarks->GetValue(i);
cellMarks->SetValue(i, -mark);
}
for (i=0; i < numPts; i++)
{
mark = pointMarks->GetValue(i);
pointMarks->SetValue(i, -mark);
}
}
// If generating selection scalars, we now have to modify the scalars to
// approximate a distance function. Otherwise, we can create the output.
if ( ! this->GenerateSelectionScalars )
{//spit out all the negative cells
vtkCellArray *newPolys=vtkCellArray::New();
newPolys->Allocate(numCells/2, numCells/2);
for (i=0; i< numCells; i++)
{
if ( (cellMarks->GetValue(i) < 0) ||
(cellMarks->GetValue(i) > 0 && this->InsideOut) )
{
this->Mesh->GetCellPoints(i, npts, pts);
newPolys->InsertNextCell(npts,pts);
}
}
output->SetPoints(inPts);
output->SetPolys(newPolys);
outPD->PassData(inPD);
newPolys->Delete();
if ( this->GenerateUnselectedOutput )
{
vtkCellArray *unPolys=vtkCellArray::New();
unPolys->Allocate(numCells/2, numCells/2);
for (i=0; i< numCells; i++)
{
if ( (cellMarks->GetValue(i) >= 0) ||
(cellMarks->GetValue(i) < 0 && this->InsideOut) )
{
this->Mesh->GetCellPoints(i, npts, pts);
unPolys->InsertNextCell(npts,pts);
}
}
this->GetUnselectedOutput()->SetPoints(inPts);
this->GetUnselectedOutput()->SetPolys(unPolys);
this->GetUnselectedOutput()->GetPointData()->PassData(inPD);
unPolys->Delete();
}
}
else //modify scalars to generate selection scalars
{
vtkFloatArray *selectionScalars=vtkFloatArray::New();
selectionScalars->SetNumberOfTuples(numPts);
// compute distance to lines. Really this should be computed based on
// the connected fill distance.
for (j=0; j < numPts; j++) //compute minimum distance to loop
{
if ( pointMarks->GetValue(j) != 0 )
{
inPts->GetPoint(j,x);
for ( closestDist2=VTK_DOUBLE_MAX, i=0; i < numLoopPts; i++ )
{
this->Loop->GetPoint(i, x0);
this->Loop->GetPoint((i+1)%numLoopPts, x1);
dist2 = vtkLine::DistanceToLine(x, x0, x1, t, xLoop);
if ( dist2 < closestDist2 )
{
closestDist2 = dist2;
}
}//for all loop edges
closestDist2 = sqrt((double)closestDist2);
selectionScalars->SetComponent(j,0,
closestDist2*pointMarks->GetValue(j));
}
}
// Now, determine the sign of those points "on the boundary" to give a
// better approximation to the scalar field.
for (j=0; j < numMeshLoopPts; j++)
{
id = edgeIds->GetId(j);
inPts->GetPoint(id, x);
for ( closestDist2=VTK_DOUBLE_MAX, i=0; i < numLoopPts; i++ )
{
this->Loop->GetPoint(i, x0);
this->Loop->GetPoint((i+1)%numLoopPts, x1);
dist2 = vtkLine::DistanceToLine(x, x0, x1, t, xLoop);
if ( dist2 < closestDist2 )
{
closestDist2 = dist2;
neiX[0] = xLoop[0]; neiX[1] = xLoop[1]; neiX[2] = xLoop[2];
}
}//for all loop edges
closestDist2 = sqrt((double)closestDist2);
// find neighbor not on boundary and compare negative/positive values
// to see what makes the most sense
this->GetPointNeighbors(id, neighbors);
numNei = neighbors->GetNumberOfIds();
for (dist2=0.0, i=0; i<numNei; i++)
{
neiId = neighbors->GetId(i);
if ( pointMarks->GetValue(neiId) != 0 ) //find the furthest away
{
if ( fabs(selectionScalars->GetComponent(neiId,0)) > dist2 )
{
currentId = neiId;
dist2 = fabs(selectionScalars->GetComponent(neiId,0));
}
}
}
inPts->GetPoint(currentId, x0);
if ( vtkMath::Distance2BetweenPoints(x0,x) <
vtkMath::Distance2BetweenPoints(x0,neiX) )
{
closestDist2 = closestDist2 * pointMarks->GetValue(currentId);
}
else
{
closestDist2 = -closestDist2 * pointMarks->GetValue(currentId);
}
selectionScalars->SetComponent(id,0,closestDist2);
}//for all boundary points
output->CopyStructure(this->Mesh); //pass geometry/topology unchanged
int idx = outPD->AddArray(selectionScalars);
outPD->SetActiveAttribute(idx, vtkDataSetAttributes::SCALARS);
outPD->CopyScalarsOff();
outPD->PassData(inPD);
outCD->PassData(inCD);
selectionScalars->Delete();
}
// Clean up and update output
triMesh->UnRegister(this);
this->Mesh->Delete();
neighbors->Delete();
edgeIds->Delete();
loopIds->Delete();
cellMarks->Delete();
pointMarks->Delete();
currentFront->Delete();
nextFront->Delete();
return 1;
}
void vtkSelectPolyData::GetPointNeighbors (vtkIdType ptId, vtkIdList *nei)
{
unsigned short ncells;
int i, j;
vtkIdType *cells, *pts, npts;
nei->Reset();
this->Mesh->GetPointCells(ptId, ncells, cells);
for (i=0; i<ncells; i++)
{
this->Mesh->GetCellPoints(cells[i], npts, pts);
for (j=0; j<3; j++)
{
if ( pts[j] != ptId )
{
nei->InsertUniqueId(pts[j]);
}
}
}
}
unsigned long int vtkSelectPolyData::GetMTime()
{
unsigned long mTime=this->Superclass::GetMTime();
unsigned long time;
if ( this->Loop != NULL )
{
time = this->Loop->GetMTime();
mTime = ( time > mTime ? time : mTime );
}
return mTime;
}
void vtkSelectPolyData::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os,indent);
os << indent << "Generate Unselected Output: "
<< (this->GenerateUnselectedOutput ? "On\n" : "Off\n");
os << indent << "Inside Mode: ";
os << this->GetSelectionModeAsString() << "\n";
os << indent << "Closest Point: (" << this->ClosestPoint[0] << ", "
<< this->ClosestPoint[1] << ", " << this->ClosestPoint[2] << ")\n";
os << indent << "Generate Selection Scalars: "
<< (this->GenerateSelectionScalars ? "On\n" : "Off\n");
os << indent << "Inside Out: " << (this->InsideOut ? "On\n" : "Off\n");
if ( this->Loop )
{
os << indent << "Loop of " << this->Loop->GetNumberOfPoints()
<< "points defined\n";
}
else
{
os << indent << "Loop not defined\n";
}
}