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.
1720 lines
49 KiB
1720 lines
49 KiB
2 years ago
|
/*=========================================================================
|
||
|
|
||
|
Program: Visualization Toolkit
|
||
|
Module: $RCSfile: vtkDecimatePro.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 "vtkDecimatePro.h"
|
||
|
|
||
|
#include "vtkDoubleArray.h"
|
||
|
#include "vtkLine.h"
|
||
|
#include "vtkMath.h"
|
||
|
#include "vtkInformation.h"
|
||
|
#include "vtkInformationVector.h"
|
||
|
#include "vtkObjectFactory.h"
|
||
|
#include "vtkPlane.h"
|
||
|
#include "vtkPolyData.h"
|
||
|
#include "vtkPriorityQueue.h"
|
||
|
#include "vtkTriangle.h"
|
||
|
#include "vtkCellArray.h"
|
||
|
#include "vtkPointData.h"
|
||
|
#include "vtkCellData.h"
|
||
|
|
||
|
vtkCxxRevisionMacro(vtkDecimatePro, "$Revision: 1.79 $");
|
||
|
vtkStandardNewMacro(vtkDecimatePro);
|
||
|
|
||
|
#define VTK_TOLERANCE 1.0e-05
|
||
|
#define VTK_MAX_TRIS_PER_VERTEX VTK_CELL_SIZE
|
||
|
#define VTK_RECYCLE_VERTEX VTK_DOUBLE_MAX
|
||
|
|
||
|
#define VTK_SIMPLE_VERTEX 1
|
||
|
#define VTK_BOUNDARY_VERTEX 2
|
||
|
#define VTK_INTERIOR_EDGE_VERTEX 3
|
||
|
#define VTK_CORNER_VERTEX 4
|
||
|
#define VTK_CRACK_TIP_VERTEX 5
|
||
|
#define VTK_EDGE_END_VERTEX 6
|
||
|
#define VTK_NON_MANIFOLD_VERTEX 7
|
||
|
#define VTK_DEGENERATE_VERTEX 8
|
||
|
#define VTK_HIGH_DEGREE_VERTEX 9
|
||
|
|
||
|
#define VTK_STATE_UNSPLIT 0
|
||
|
#define VTK_STATE_SPLIT 1
|
||
|
#define VTK_STATE_SPLIT_ALL 2
|
||
|
|
||
|
// Helper functions
|
||
|
static double ComputeSimpleError(double x[3], double normal[3], double point[3]);
|
||
|
static double ComputeEdgeError(double x[3], double x1[3], double x2[3]);
|
||
|
static double ComputeSingleTriangleError(double x[3], double x1[3], double x2[3]);
|
||
|
|
||
|
|
||
|
// Create object with specified reduction of 90% and feature angle of
|
||
|
// 15 degrees. Edge splitting is on, defer splitting is on, and the
|
||
|
// split angle is 75 degrees. Topology preservation is off, delete
|
||
|
// boundary vertices is on, and the maximum error is set to
|
||
|
// VTK_DOUBLE_MAX. The inflection point ratio is 10 and the vertex
|
||
|
// degree is 25. Error accumulation is turned off.
|
||
|
vtkDecimatePro::vtkDecimatePro()
|
||
|
{
|
||
|
this->Neighbors = vtkIdList::New();
|
||
|
this->Neighbors->Allocate(VTK_MAX_TRIS_PER_VERTEX);
|
||
|
this->V = new vtkDecimatePro::VertexArray(VTK_MAX_TRIS_PER_VERTEX+1);
|
||
|
this->T = new vtkDecimatePro::TriArray(VTK_MAX_TRIS_PER_VERTEX+1);
|
||
|
this->EdgeLengths = vtkPriorityQueue::New();
|
||
|
this->EdgeLengths->Allocate(VTK_MAX_TRIS_PER_VERTEX);
|
||
|
|
||
|
this->InflectionPoints = vtkDoubleArray::New();
|
||
|
this->TargetReduction = 0.90;
|
||
|
this->FeatureAngle = 15.0;
|
||
|
this->PreserveTopology = 0;
|
||
|
this->MaximumError = VTK_DOUBLE_MAX;
|
||
|
this->AbsoluteError = VTK_DOUBLE_MAX;
|
||
|
this->ErrorIsAbsolute = 0;
|
||
|
this->AccumulateError = 0;
|
||
|
this->SplitAngle = 75.0;
|
||
|
this->Splitting = 1;
|
||
|
this->PreSplitMesh = 0;
|
||
|
this->Degree = 25;
|
||
|
this->BoundaryVertexDeletion = 1;
|
||
|
this->InflectionPointRatio = 10.0;
|
||
|
|
||
|
this->Queue = NULL;
|
||
|
this->VertexError = NULL;
|
||
|
|
||
|
this->Mesh = NULL;
|
||
|
}
|
||
|
|
||
|
vtkDecimatePro::~vtkDecimatePro()
|
||
|
{
|
||
|
this->InflectionPoints->Delete();
|
||
|
if ( this->Queue )
|
||
|
{
|
||
|
this->Queue->Delete();
|
||
|
}
|
||
|
if ( this->VertexError )
|
||
|
{
|
||
|
this->VertexError->Delete();
|
||
|
}
|
||
|
this->Neighbors->Delete();
|
||
|
this->EdgeLengths->Delete();
|
||
|
delete this->V;
|
||
|
delete this->T;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Reduce triangles in mesh by specified reduction factor.
|
||
|
//
|
||
|
int vtkDecimatePro::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 i, ptId, numPts, numTris, collapseId;
|
||
|
vtkPoints *inPts;
|
||
|
vtkPoints *newPts;
|
||
|
vtkCellArray *inPolys;
|
||
|
vtkCellArray *newPolys;
|
||
|
double error, previousError=0.0, reduction;
|
||
|
int type;
|
||
|
vtkIdType *pts, npts, totalEliminated, numRecycles, numPops;
|
||
|
unsigned short int ncells;
|
||
|
vtkIdType pt1, pt2, cellId, fedges[2];
|
||
|
vtkIdType *cells;
|
||
|
vtkIdList *CollapseTris;
|
||
|
double max, *bounds;
|
||
|
if (!input)
|
||
|
{
|
||
|
vtkErrorMacro(<<"No input!");
|
||
|
return 1;
|
||
|
}
|
||
|
vtkPointData *outputPD=output->GetPointData();
|
||
|
vtkPointData *inPD=input->GetPointData();
|
||
|
vtkPointData *meshPD=0;
|
||
|
vtkIdType *map, numNewPts, totalPts;
|
||
|
vtkIdType newCellPts[3];
|
||
|
int abortExecute=0;
|
||
|
|
||
|
vtkDebugMacro(<<"Executing progressive decimation...");
|
||
|
|
||
|
// Check input
|
||
|
this->NumberOfRemainingTris = numTris = input->GetNumberOfPolys();
|
||
|
if ( ((numPts=input->GetNumberOfPoints()) < 1 || numTris < 1) &&
|
||
|
(this->TargetReduction > 0.0) )
|
||
|
{
|
||
|
vtkErrorMacro(<<"No data to decimate!");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
// Initialize
|
||
|
bounds = input->GetBounds();
|
||
|
for (max=0.0, i=0; i<3; i++)
|
||
|
{
|
||
|
max = ((bounds[2*i+1]-bounds[2*i]) > max ?
|
||
|
(bounds[2*i+1]-bounds[2*i]) : max);
|
||
|
}
|
||
|
if (!this->ErrorIsAbsolute)
|
||
|
{
|
||
|
this->Error = (this->MaximumError >= VTK_DOUBLE_MAX ?
|
||
|
VTK_DOUBLE_MAX : this->MaximumError * max);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this->Error = (this->AbsoluteError >= VTK_DOUBLE_MAX ?
|
||
|
VTK_DOUBLE_MAX : this->AbsoluteError);
|
||
|
}
|
||
|
this->Tolerance = VTK_TOLERANCE * input->GetLength();
|
||
|
this->CosAngle =
|
||
|
cos ((double) vtkMath::DegreesToRadians() * this->FeatureAngle);
|
||
|
this->Split = ( this->Splitting && !this->PreserveTopology );
|
||
|
this->VertexDegree = this->Degree;
|
||
|
this->TheSplitAngle = this->SplitAngle;
|
||
|
this->SplitState = VTK_STATE_UNSPLIT;
|
||
|
|
||
|
// Lets check to make sure there are only triangles in the input.
|
||
|
vtkIdType *pPolys;
|
||
|
pPolys = input->GetPolys()->GetPointer();
|
||
|
for (i = 0; i < numTris; ++i)
|
||
|
{
|
||
|
if (*pPolys != 3)
|
||
|
{
|
||
|
vtkErrorMacro("DecimatePro does not accept polygons that are not triangles.");
|
||
|
output->CopyStructure(input);
|
||
|
output->GetPointData()->PassData(input->GetPointData());
|
||
|
output->GetCellData()->PassData(input->GetCellData());
|
||
|
return 1;
|
||
|
}
|
||
|
pPolys += 4;
|
||
|
}
|
||
|
|
||
|
// Build cell data structure. Need to copy triangle connectivity data
|
||
|
// so we can modify it.
|
||
|
if ( this->TargetReduction > 0.0 )
|
||
|
{
|
||
|
inPts = input->GetPoints();
|
||
|
inPolys = input->GetPolys();
|
||
|
|
||
|
// this static should be eliminated
|
||
|
if (this->Mesh != NULL) {this->Mesh->Delete(); this->Mesh = NULL;}
|
||
|
this->Mesh = vtkPolyData::New();
|
||
|
|
||
|
newPts = vtkPoints::New(); newPts->SetNumberOfPoints(numPts);
|
||
|
newPts->DeepCopy(inPts);
|
||
|
this->Mesh->SetPoints(newPts);
|
||
|
newPts->Delete(); //registered by Mesh and preserved
|
||
|
|
||
|
newPolys = vtkCellArray::New();
|
||
|
newPolys->DeepCopy(inPolys);
|
||
|
this->Mesh->SetPolys(newPolys);
|
||
|
newPolys->Delete(); //registered by Mesh and preserved
|
||
|
|
||
|
meshPD = this->Mesh->GetPointData();
|
||
|
meshPD->DeepCopy(inPD);
|
||
|
meshPD->CopyAllocate(meshPD, input->GetNumberOfPoints());
|
||
|
|
||
|
this->Mesh->BuildLinks();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
output->CopyStructure(input);
|
||
|
output->GetPointData()->PassData(input->GetPointData());
|
||
|
output->GetCellData()->PassData(input->GetCellData());
|
||
|
//vtkWarningMacro(<<"Reduction == 0: passing data through unchanged");
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
// Initialize data structures: priority queue and errors.
|
||
|
this->InitializeQueue(numPts);
|
||
|
|
||
|
if ( this->AccumulateError )
|
||
|
{
|
||
|
this->VertexError = vtkDoubleArray::New();
|
||
|
this->VertexError->Allocate(numPts,((vtkIdType)0.25*numPts));
|
||
|
for (i=0; i<numPts; i++)
|
||
|
{
|
||
|
this->VertexError->SetValue(i, 0.0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If not deferring splitting and splitting on, we'll start off by
|
||
|
// splitting the mesh. This has side effect of inserting vertices.
|
||
|
this->NumCollapses = this->NumMerges = 0;
|
||
|
if ( this->Split && this->PreSplitMesh )
|
||
|
{
|
||
|
vtkDebugMacro(<<"Pre-splitting mesh");
|
||
|
this->SplitState = VTK_STATE_SPLIT;
|
||
|
this->SplitMesh();
|
||
|
}
|
||
|
|
||
|
// Start by traversing all vertices. For each vertex, evaluate the
|
||
|
// local topology/geometry. (Some vertex splitting may be
|
||
|
// necessary to resolve non-manifold geometry or to split edges.)
|
||
|
// Then evaluate the local error for the vertex. The vertex is then
|
||
|
// inserted into the priority queue.
|
||
|
npts = this->Mesh->GetNumberOfPoints();
|
||
|
for ( ptId=0; ptId < npts && !abortExecute ; ptId++ )
|
||
|
{
|
||
|
if ( ! (ptId % 10000) )
|
||
|
{
|
||
|
vtkDebugMacro(<<"Inserting vertex #" << ptId);
|
||
|
this->UpdateProgress (0.25*ptId/npts);//25% spent inserting
|
||
|
abortExecute = this->GetAbortExecute();
|
||
|
}
|
||
|
this->Insert(ptId);
|
||
|
}
|
||
|
this->UpdateProgress (0.25);//25% spent inserting
|
||
|
|
||
|
CollapseTris = vtkIdList::New();
|
||
|
CollapseTris->Allocate(100,100);
|
||
|
|
||
|
// While the priority queue is not empty, retrieve the top vertex from the
|
||
|
// queue and attempt to delete it by performing an edge collapse. This
|
||
|
// in turn will cause modification to the surrounding vertices. For each
|
||
|
// surrounding vertex, evaluate the error and re-insert into the queue.
|
||
|
// (While this is happening we keep track of operations on the data -
|
||
|
// this forms the core of the progressive mesh representation.)
|
||
|
for ( totalEliminated=0, reduction=0.0, numRecycles=0, numPops=0;
|
||
|
reduction < this->TargetReduction && (ptId = this->Pop(error)) >= 0 && !abortExecute;
|
||
|
numPops++)
|
||
|
{
|
||
|
if ( numPops && !(numPops % 5000) )
|
||
|
{
|
||
|
vtkDebugMacro(<<"Deleting vertex #" << numPops);
|
||
|
this->UpdateProgress (0.25 + 0.75*(reduction/this->TargetReduction));
|
||
|
abortExecute = this->GetAbortExecute();
|
||
|
}
|
||
|
|
||
|
this->Mesh->GetPoint(ptId,this->X);
|
||
|
this->Mesh->GetPointCells(ptId,ncells,cells);
|
||
|
|
||
|
if ( ncells > 0 )
|
||
|
{
|
||
|
type = this->EvaluateVertex(ptId, ncells, cells, fedges);
|
||
|
|
||
|
// FindSplit finds the edge to collapse - and if it fails, we
|
||
|
// split the vertex.
|
||
|
collapseId = this->FindSplit (type, fedges, pt1, pt2, CollapseTris);
|
||
|
|
||
|
if ( collapseId >= 0 )
|
||
|
{
|
||
|
if ( this->AccumulateError )
|
||
|
{
|
||
|
this->DistributeError(error);
|
||
|
}
|
||
|
|
||
|
totalEliminated += this->CollapseEdge(type, ptId, collapseId, pt1, pt2,
|
||
|
CollapseTris);
|
||
|
|
||
|
reduction = (double) totalEliminated / numTris;
|
||
|
this->NumberOfRemainingTris = numTris - totalEliminated;
|
||
|
|
||
|
//see whether we've found inflection
|
||
|
if ( numPops == 0 || (previousError == 0.0 && error != 0.0) ||
|
||
|
(previousError != 0.0 &&
|
||
|
fabs(error/previousError) > this->InflectionPointRatio) )
|
||
|
{
|
||
|
this->InflectionPoints->InsertNextValue(numPops);
|
||
|
}
|
||
|
previousError = error;
|
||
|
}
|
||
|
|
||
|
else //Couldn't delete the vertex, so we'll re-insert it for splitting
|
||
|
{
|
||
|
numRecycles++;
|
||
|
this->Insert(ptId,VTK_RECYCLE_VERTEX);
|
||
|
}
|
||
|
|
||
|
}//if cells attached
|
||
|
}//while queue not empty and reduction not satisfied
|
||
|
|
||
|
CollapseTris->Delete();
|
||
|
|
||
|
totalPts = this->Mesh->GetNumberOfPoints();
|
||
|
vtkDebugMacro(<<"\n\tReduction " << reduction << " (" << numTris << " to "
|
||
|
<< numTris - totalEliminated << " triangles)"
|
||
|
<<"\n\tPerformed " << numPops << " vertex pops"
|
||
|
<<"\n\tFound " << this->GetNumberOfInflectionPoints()
|
||
|
<<" inflection points"
|
||
|
<<"\n\tPerformed " << totalPts - numPts << " vertex splits"
|
||
|
<<"\n\tPerformed " << this->NumCollapses << " edge collapses"
|
||
|
<<"\n\tPerformed " << this->NumMerges << " vertex merges"
|
||
|
<<"\n\tRecycled " << numRecycles << " points"
|
||
|
<<"\n\tAdded " << totalPts - numPts << " points ("
|
||
|
<< numPts << " to " << totalPts << " points)");
|
||
|
|
||
|
//
|
||
|
// Create output and release memory
|
||
|
//
|
||
|
vtkDebugMacro (<<"Creating output...");
|
||
|
this->DeleteQueue();
|
||
|
|
||
|
// Grab the points that are left; copy point data. Remember that splitting
|
||
|
// data may have added new points.
|
||
|
map = new vtkIdType[totalPts];
|
||
|
for (i=0; i < totalPts; i++)
|
||
|
{
|
||
|
map[i] = -1;
|
||
|
}
|
||
|
numNewPts = 0;
|
||
|
for (ptId=0; ptId < totalPts; ptId++)
|
||
|
{
|
||
|
this->Mesh->GetPointCells(ptId,ncells,cells);
|
||
|
if ( ncells > 0 )
|
||
|
{
|
||
|
map[ptId] = numNewPts++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
outputPD->CopyAllocate(meshPD,numNewPts);
|
||
|
|
||
|
// Copy points in place
|
||
|
for (ptId=0; ptId < totalPts; ptId++)
|
||
|
{
|
||
|
if ( map[ptId] > -1 )
|
||
|
{
|
||
|
newPts->SetPoint(map[ptId],newPts->GetPoint(ptId));
|
||
|
outputPD->CopyData(meshPD,ptId,map[ptId]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
newPts->SetNumberOfPoints(numNewPts);
|
||
|
newPts->Squeeze();
|
||
|
|
||
|
// Now renumber connectivity
|
||
|
newPolys = vtkCellArray::New();
|
||
|
newPolys->Allocate(newPolys->EstimateSize(3,numTris-totalEliminated));
|
||
|
|
||
|
for (cellId=0; cellId < numTris; cellId++)
|
||
|
{
|
||
|
if ( this->Mesh->GetCellType(cellId) == VTK_TRIANGLE ) // non-null element
|
||
|
{
|
||
|
this->Mesh->GetCellPoints(cellId, npts, pts);
|
||
|
for (i=0; i < 3; i++)
|
||
|
{
|
||
|
newCellPts[i] = map[pts[i]];
|
||
|
}
|
||
|
newPolys->InsertNextCell(npts,newCellPts);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
delete [] map;
|
||
|
output->SetPoints(newPts);
|
||
|
output->SetPolys(newPolys);
|
||
|
if (this->Mesh != NULL) {this->Mesh->Delete(); this->Mesh = NULL;}
|
||
|
newPolys->Delete();
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Computes error to edge (distance squared)
|
||
|
//
|
||
|
static double ComputeEdgeError(double x[3], double x1[3], double x2[3])
|
||
|
{
|
||
|
double projDist = vtkLine::DistanceToLine(x, x1, x2);
|
||
|
double edgeLength = vtkMath::Distance2BetweenPoints(x1,x2);
|
||
|
|
||
|
return (projDist < edgeLength ? projDist : edgeLength);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Computes triangle area
|
||
|
//
|
||
|
static double ComputeSingleTriangleError(double x[3], double x1[3], double x2[3])
|
||
|
{
|
||
|
return vtkTriangle::TriangleArea(x, x1, x2);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Computes error to a cycle of triangles...the average plane (normal and
|
||
|
// point) have been already computed. (Returns distance squared.)
|
||
|
//
|
||
|
static double ComputeSimpleError(double x[3], double normal[3], double point[3])
|
||
|
{
|
||
|
double dist = vtkPlane::DistanceToPlane(x, normal, point);
|
||
|
return dist * dist;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Split the mesh along sharp edges - separates the mesh into pieces.
|
||
|
//
|
||
|
void vtkDecimatePro::SplitMesh()
|
||
|
{
|
||
|
vtkIdType ptId, fedges[2];
|
||
|
int type;
|
||
|
vtkIdType *cells;
|
||
|
unsigned short int ncells;
|
||
|
|
||
|
this->CosAngle = cos ((double) vtkMath::DegreesToRadians() * this->SplitAngle);
|
||
|
for ( ptId=0; ptId < this->Mesh->GetNumberOfPoints(); ptId++ )
|
||
|
{
|
||
|
this->Mesh->GetPoint(ptId,this->X);
|
||
|
this->Mesh->GetPointCells(ptId,ncells,cells);
|
||
|
|
||
|
if ( ncells > 0 &&
|
||
|
((type=this->EvaluateVertex(ptId,ncells,cells,fedges)) == VTK_CORNER_VERTEX ||
|
||
|
type == VTK_INTERIOR_EDGE_VERTEX ||
|
||
|
type == VTK_NON_MANIFOLD_VERTEX) )
|
||
|
{
|
||
|
this->SplitVertex(ptId, type, ncells, cells, 0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#define VTK_FEATURE_ANGLE(tri1,tri2) \
|
||
|
vtkMath::Dot(this->T->Array[tri1].n, this->T->Array[tri2].n)
|
||
|
//
|
||
|
// Evalute the local topology/geometry of a vertex. This is a two-pass
|
||
|
// process: first topology is examined, and then the geometry.
|
||
|
//
|
||
|
int vtkDecimatePro::EvaluateVertex(vtkIdType ptId, unsigned short int numTris,
|
||
|
vtkIdType *tris, vtkIdType fedges[2])
|
||
|
{
|
||
|
vtkIdType numNei, numFEdges;
|
||
|
vtkIdType numVerts;
|
||
|
vtkDecimatePro::LocalTri t;
|
||
|
vtkDecimatePro::LocalVertex sn;
|
||
|
vtkIdType startVertex, nextVertex, numNormals;
|
||
|
int i, j, vtype;
|
||
|
vtkIdType *verts;
|
||
|
double *x1, *x2, *normal;
|
||
|
double v1[3], v2[3], center[3];
|
||
|
//
|
||
|
// The first step is to evaluate topology.
|
||
|
//
|
||
|
|
||
|
// Check cases with high vertex degree
|
||
|
//
|
||
|
if ( numTris >= this->VertexDegree )
|
||
|
{
|
||
|
return VTK_HIGH_DEGREE_VERTEX;
|
||
|
}
|
||
|
|
||
|
// From the adjacency structure we can find the triangles that use the
|
||
|
// vertex. Traverse this structure, gathering all the surrounding vertices
|
||
|
// into an ordered list.
|
||
|
//
|
||
|
this->V->Reset();
|
||
|
this->T->Reset();
|
||
|
|
||
|
sn.FAngle = 0.0;
|
||
|
|
||
|
t.area = 0.0;
|
||
|
t.n[0] = t.n[1] = t.n[2] = 0.0;
|
||
|
t.verts[0] = -1; // Marks the fact that this poly hasn't been replaced
|
||
|
t.verts[1] = -1;
|
||
|
t.verts[2] = -1;
|
||
|
//
|
||
|
// Find the starting edge. Do it very carefully do make sure
|
||
|
// ordering is consistent
|
||
|
// (e.g., polygons ordering/normals remains consistent)
|
||
|
//
|
||
|
this->Mesh->GetCellPoints(*tris,numVerts,verts); // get starting point
|
||
|
for (i=0; i<3; i++)
|
||
|
{
|
||
|
if (verts[i] == ptId)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
sn.id = startVertex = verts[(i+1)%3];
|
||
|
this->Mesh->GetPoint(sn.id, sn.x); //grab coordinates here to save GetPoint() calls
|
||
|
|
||
|
this->V->InsertNextVertex(sn);
|
||
|
|
||
|
nextVertex = -1; // initialize
|
||
|
this->Neighbors->Reset();
|
||
|
this->Neighbors->InsertId(0,*tris);
|
||
|
numNei = 1;
|
||
|
//
|
||
|
// Traverse the edge neighbors and see whether a cycle can be
|
||
|
// completed. Also have to keep track of orientation of faces for
|
||
|
// computing normals.
|
||
|
//
|
||
|
while ( this->T->MaxId < numTris && numNei == 1 && nextVertex != startVertex)
|
||
|
{
|
||
|
t.id = this->Neighbors->GetId(0);
|
||
|
this->T->InsertNextTriangle(t);
|
||
|
|
||
|
this->Mesh->GetCellPoints(t.id,numVerts,verts);
|
||
|
|
||
|
for (j=0; j<3; j++)
|
||
|
{
|
||
|
if (verts[j] != sn.id && verts[j] != ptId)
|
||
|
{
|
||
|
nextVertex = verts[j];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
sn.id = nextVertex;
|
||
|
this->Mesh->GetPoint(sn.id, sn.x);
|
||
|
this->V->InsertNextVertex(sn);
|
||
|
|
||
|
this->Mesh->GetCellEdgeNeighbors(t.id, ptId, nextVertex, this->Neighbors);
|
||
|
numNei = this->Neighbors->GetNumberOfIds();
|
||
|
}
|
||
|
//
|
||
|
// See whether we've run around the loop, hit a boundary, or hit a
|
||
|
// complex spot.
|
||
|
//
|
||
|
if ( nextVertex == startVertex && numNei == 1 )
|
||
|
{
|
||
|
if ( this->T->GetNumberOfTriangles() != numTris ) //touching non-manifold
|
||
|
{
|
||
|
vtype = VTK_NON_MANIFOLD_VERTEX;
|
||
|
}
|
||
|
else //remove last vertex addition
|
||
|
{
|
||
|
this->V->MaxId -= 1;
|
||
|
vtype = VTK_SIMPLE_VERTEX;
|
||
|
}
|
||
|
}
|
||
|
//
|
||
|
// Check for non-manifold cases
|
||
|
//
|
||
|
else if ( numNei > 1 || this->T->GetNumberOfTriangles() > numTris )
|
||
|
{
|
||
|
vtype = VTK_NON_MANIFOLD_VERTEX;
|
||
|
}
|
||
|
//
|
||
|
// Boundary loop - but (luckily) completed semi-cycle
|
||
|
//
|
||
|
else if ( numNei == 0 && this->T->GetNumberOfTriangles() == numTris )
|
||
|
{
|
||
|
this->V->Array[0].FAngle = -1.0; // using cosine of -180 degrees
|
||
|
this->V->Array[this->V->MaxId].FAngle = -1.0;
|
||
|
vtype = VTK_BOUNDARY_VERTEX;
|
||
|
}
|
||
|
//
|
||
|
// Hit a boundary but didn't complete semi-cycle. Gotta go back
|
||
|
// around the other way. Just reset the starting point and go
|
||
|
// back the other way.
|
||
|
//
|
||
|
else
|
||
|
{
|
||
|
t = this->T->GetTriangle(this->T->MaxId);
|
||
|
|
||
|
this->V->Reset();
|
||
|
this->T->Reset();
|
||
|
|
||
|
startVertex = sn.id = nextVertex;
|
||
|
this->Mesh->GetPoint(sn.id, sn.x);
|
||
|
this->V->InsertNextVertex(sn);
|
||
|
|
||
|
nextVertex = -1;
|
||
|
this->Neighbors->Reset();
|
||
|
this->Neighbors->InsertId(0,t.id);
|
||
|
numNei = 1;
|
||
|
//
|
||
|
// Now move from boundary edge around the other way.
|
||
|
//
|
||
|
while ( this->T->MaxId < numTris && numNei == 1 && nextVertex != startVertex)
|
||
|
{
|
||
|
t.id = this->Neighbors->GetId(0);
|
||
|
this->T->InsertNextTriangle(t);
|
||
|
|
||
|
this->Mesh->GetCellPoints(t.id,numVerts,verts);
|
||
|
|
||
|
for (j=0; j<3; j++)
|
||
|
{
|
||
|
if (verts[j] != sn.id && verts[j] != ptId)
|
||
|
{
|
||
|
nextVertex = verts[j];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sn.id = nextVertex;
|
||
|
this->Mesh->GetPoint(sn.id, sn.x);
|
||
|
this->V->InsertNextVertex(sn);
|
||
|
|
||
|
this->Mesh->GetCellEdgeNeighbors(t.id, ptId, nextVertex, this->Neighbors);
|
||
|
numNei = this->Neighbors->GetNumberOfIds();
|
||
|
}
|
||
|
//
|
||
|
// Make sure that there are only two boundaries (i.e., not non-manifold)
|
||
|
//
|
||
|
if ( this->T->GetNumberOfTriangles() == numTris )
|
||
|
{
|
||
|
//
|
||
|
// Because we've reversed order of loop, need to rearrange the order
|
||
|
// of the vertices and polygons to preserve consistent polygons
|
||
|
// ordering / normal orientation.
|
||
|
//
|
||
|
numVerts = this->V->GetNumberOfVertices();
|
||
|
for (i=0; i<(numVerts/2); i++)
|
||
|
{
|
||
|
sn.id = this->V->Array[i].id;
|
||
|
this->V->Array[i].id = this->V->Array[numVerts-i-1].id;
|
||
|
this->V->Array[numVerts-i-1].id = sn.id;
|
||
|
for (j=0; j<3; j++)
|
||
|
{
|
||
|
sn.x[j] = this->V->Array[i].x[j];
|
||
|
this->V->Array[i].x[j] = this->V->Array[numVerts-i-1].x[j];
|
||
|
this->V->Array[numVerts-i-1].x[j] = sn.x[j];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
numTris = this->T->GetNumberOfTriangles();
|
||
|
for (i=0; i<(numTris/2); i++)
|
||
|
{
|
||
|
t.id = this->T->Array[i].id;
|
||
|
this->T->Array[i].id = this->T->Array[numTris-i-1].id;
|
||
|
this->T->Array[numTris-i-1].id = t.id;
|
||
|
}
|
||
|
|
||
|
this->V->Array[0].FAngle = -1.0;
|
||
|
this->V->Array[this->V->MaxId].FAngle = -1.0;
|
||
|
vtype = VTK_BOUNDARY_VERTEX;
|
||
|
}
|
||
|
else // non-manifold
|
||
|
{
|
||
|
vtype = VTK_NON_MANIFOLD_VERTEX;
|
||
|
}
|
||
|
}
|
||
|
//
|
||
|
// If at this point, the vertex is either simple or boundary. Here we do
|
||
|
// a geometric evaluation to find feature edges, if any, and then a
|
||
|
// final classification.
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// Traverse all polygons and generate normals and areas
|
||
|
//
|
||
|
x2 = this->V->Array[0].x;
|
||
|
for (i=0; i<3; i++)
|
||
|
{
|
||
|
v2[i] = x2[i] - this->X[i];
|
||
|
}
|
||
|
|
||
|
this->LoopArea=0.0;
|
||
|
this->Normal[0] = this->Normal[1] = this->Normal[2] = 0.0;
|
||
|
this->Pt[0] = this->Pt[1] = this->Pt[2] = 0.0;
|
||
|
numNormals=0;
|
||
|
|
||
|
for (i=0; i < this->T->GetNumberOfTriangles(); i++)
|
||
|
{
|
||
|
normal = this->T->Array[i].n;
|
||
|
x1 = x2;
|
||
|
x2 = this->V->Array[i+1].x;
|
||
|
|
||
|
for (j=0; j<3; j++)
|
||
|
{
|
||
|
v1[j] = v2[j];
|
||
|
v2[j] = x2[j] - this->X[j];
|
||
|
}
|
||
|
|
||
|
this->T->Array[i].area = vtkTriangle::TriangleArea (this->X, x1, x2);
|
||
|
vtkTriangle::TriangleCenter (this->X, x1, x2, center);
|
||
|
this->LoopArea += this->T->Array[i].area;
|
||
|
|
||
|
vtkMath::Cross (v1, v2, normal);
|
||
|
//
|
||
|
// Get normals. If null, then normal make no contribution to loop.
|
||
|
// The center of the loop is the center of gravity.
|
||
|
//
|
||
|
if ( vtkMath::Normalize(normal) != 0.0 )
|
||
|
{
|
||
|
numNormals++;
|
||
|
for (j=0; j<3; j++)
|
||
|
{
|
||
|
this->Normal[j] += this->T->Array[i].area * normal[j];
|
||
|
this->Pt[j] += this->T->Array[i].area * center[j];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//
|
||
|
// Compute "average" plane normal and plane center. Use an area
|
||
|
// averaged normal calulation
|
||
|
//
|
||
|
if ( !numNormals || this->LoopArea == 0.0 )
|
||
|
{
|
||
|
return VTK_DEGENERATE_VERTEX;
|
||
|
}
|
||
|
|
||
|
for (j=0; j<3; j++)
|
||
|
{
|
||
|
this->Normal[j] /= this->LoopArea;
|
||
|
this->Pt[j] /= this->LoopArea;
|
||
|
}
|
||
|
if ( vtkMath::Normalize(this->Normal) == 0.0 )
|
||
|
{
|
||
|
return VTK_DEGENERATE_VERTEX;
|
||
|
}
|
||
|
//
|
||
|
// Now run through polygons again generating feature angles. (Note
|
||
|
// that if an edge is on the boundary its feature angle has already
|
||
|
// been set to 180.) Also need to keep track whether any feature
|
||
|
// angles exceed the current value.
|
||
|
//
|
||
|
if ( vtype == VTK_BOUNDARY_VERTEX )
|
||
|
{
|
||
|
numFEdges = 2;
|
||
|
fedges[0] = 0;
|
||
|
fedges[1] = this->V->MaxId;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
numFEdges = 0;
|
||
|
}
|
||
|
//
|
||
|
// Compare to cosine of feature angle to avoid cosine extraction
|
||
|
//
|
||
|
if ( vtype == VTK_SIMPLE_VERTEX ) // first edge
|
||
|
{
|
||
|
if ( (this->V->Array[0].FAngle = VTK_FEATURE_ANGLE(0,this->T->MaxId)) <= this->CosAngle )
|
||
|
{
|
||
|
fedges[numFEdges++] = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i=0; i < this->T->MaxId; i++)
|
||
|
{
|
||
|
if ( (this->V->Array[i+1].FAngle = VTK_FEATURE_ANGLE(i,i+1)) <= this->CosAngle )
|
||
|
{
|
||
|
if ( numFEdges >= 2 )
|
||
|
{
|
||
|
numFEdges++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fedges[numFEdges++] = i + 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//
|
||
|
// Final classification
|
||
|
//
|
||
|
if ( vtype == VTK_SIMPLE_VERTEX && numFEdges > 0 )
|
||
|
{
|
||
|
if ( numFEdges == 1 )
|
||
|
{
|
||
|
vtype = VTK_EDGE_END_VERTEX;
|
||
|
}
|
||
|
else if ( numFEdges == 2 )
|
||
|
{
|
||
|
vtype = VTK_INTERIOR_EDGE_VERTEX;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vtype = VTK_CORNER_VERTEX;
|
||
|
}
|
||
|
}
|
||
|
else if ( vtype == VTK_BOUNDARY_VERTEX )
|
||
|
{
|
||
|
if ( numFEdges != 2 )
|
||
|
{
|
||
|
vtype = VTK_CORNER_VERTEX;
|
||
|
}
|
||
|
else
|
||
|
{//see whether this is the tip of a crack
|
||
|
if ( this->V->Array[fedges[0]].x[0] == this->V->Array[fedges[1]].x[0] &&
|
||
|
this->V->Array[fedges[0]].x[1] == this->V->Array[fedges[1]].x[1] &&
|
||
|
this->V->Array[fedges[0]].x[2] == this->V->Array[fedges[1]].x[2])
|
||
|
{
|
||
|
vtype = VTK_CRACK_TIP_VERTEX;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return vtype;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Split the vertex by modifying topological connections.
|
||
|
//
|
||
|
void vtkDecimatePro::SplitVertex(vtkIdType ptId, int type,
|
||
|
unsigned short int numTris, vtkIdType *tris,
|
||
|
int insert)
|
||
|
{
|
||
|
vtkIdType id, fedge1, fedge2, i, j;
|
||
|
vtkIdType tri, veryFirst;
|
||
|
int numSplitTris;
|
||
|
vtkIdType *verts, nverts;
|
||
|
double error;
|
||
|
vtkIdType startTri, p[2];
|
||
|
int maxGroupSize;
|
||
|
vtkPointData* meshPD = this->Mesh->GetPointData();
|
||
|
|
||
|
//
|
||
|
// On an interior edge split along the edge
|
||
|
//
|
||
|
if ( type == VTK_INTERIOR_EDGE_VERTEX ) //when edge splitting is on
|
||
|
{
|
||
|
// Half of loop is left connected to current vertex. Second half is
|
||
|
// split away.
|
||
|
for ( i=0; i < numTris; i++ ) // find first feature edge
|
||
|
{
|
||
|
if ( this->V->Array[i].FAngle <= this->CosAngle )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
fedge1 = i;
|
||
|
for ( i++, numSplitTris=1; this->V->Array[i].FAngle > this->CosAngle; i++ )
|
||
|
{
|
||
|
numSplitTris++;
|
||
|
}
|
||
|
|
||
|
fedge2 = i;
|
||
|
|
||
|
// Now split region
|
||
|
id = this->Mesh->InsertNextLinkedPoint(this->X,numSplitTris);
|
||
|
meshPD->CopyData(meshPD, ptId, id);
|
||
|
for ( i=fedge1; i < fedge2; i++ )
|
||
|
{ //disconnect from existing vertex
|
||
|
tri = this->T->Array[i].id;
|
||
|
this->Mesh->RemoveReferenceToCell(ptId, tri);
|
||
|
this->Mesh->AddReferenceToCell(id, tri);
|
||
|
this->Mesh->ReplaceCellPoint(tri, ptId, id);
|
||
|
}
|
||
|
|
||
|
// Compute error and insert the two vertices (old + split)
|
||
|
error = ComputeEdgeError(this->X, this->V->Array[fedge1].x, this->V->Array[fedge2].x);
|
||
|
if ( this->AccumulateError )
|
||
|
{
|
||
|
this->VertexError->InsertValue(id, this->VertexError->GetValue(ptId));
|
||
|
}
|
||
|
|
||
|
if ( insert )
|
||
|
{
|
||
|
this->Insert(ptId,error);
|
||
|
this->Insert(id,error);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Break corners into separate pieces (along feature edges)
|
||
|
//
|
||
|
else if ( type == VTK_CORNER_VERTEX )
|
||
|
{
|
||
|
// The first piece is left connected to vertex. Just find first
|
||
|
// feature/boundary edge. If on boundary, skip boundary piece.
|
||
|
for ( i=0; i <= this->V->MaxId; i++ ) // find first feature edge
|
||
|
{
|
||
|
if ( this->V->Array[i].FAngle <= this->CosAngle && this->V->Array[i].FAngle != -1.0 )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
for ( veryFirst = fedge1 = i; fedge1 < this->V->MaxId; i = fedge1 = fedge2 )
|
||
|
{
|
||
|
for (i++, numSplitTris=1;
|
||
|
i <= this->V->MaxId && this->V->Array[i].FAngle > this->CosAngle; i++)
|
||
|
{
|
||
|
numSplitTris++;
|
||
|
}
|
||
|
|
||
|
if ( (fedge2 = i) > this->V->MaxId )
|
||
|
{
|
||
|
continue; //must be part of first region
|
||
|
}
|
||
|
|
||
|
// Now split region
|
||
|
id = this->Mesh->InsertNextLinkedPoint(this->X,numSplitTris);
|
||
|
meshPD->CopyData(meshPD, ptId, id);
|
||
|
|
||
|
for ( j=fedge1; j < fedge2; j++ )
|
||
|
{ //disconnect from existing vertex
|
||
|
tri = this->T->Array[j].id;
|
||
|
this->Mesh->RemoveReferenceToCell(ptId, tri);
|
||
|
this->Mesh->AddReferenceToCell(id, tri);
|
||
|
this->Mesh->ReplaceCellPoint(tri, ptId, id);
|
||
|
}
|
||
|
|
||
|
// Compute error for the vertex and insert
|
||
|
error = ComputeEdgeError(this->X, this->V->Array[fedge1].x, this->V->Array[fedge2].x);
|
||
|
if ( this->AccumulateError )
|
||
|
{
|
||
|
this->VertexError->InsertValue(id, this->VertexError->GetValue(ptId));
|
||
|
}
|
||
|
|
||
|
if ( insert )
|
||
|
{
|
||
|
this->Insert(id,error);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// don't forget to compute error for old vertex, and insert into queue
|
||
|
if ( this->V->Array[0].FAngle == -1.0 )
|
||
|
{
|
||
|
error = ComputeEdgeError(this->X, this->V->Array[0].x, this->V->Array[veryFirst].x);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
error = ComputeEdgeError(this->X, this->V->Array[veryFirst].x, this->V->Array[fedge1].x);
|
||
|
}
|
||
|
|
||
|
if ( insert )
|
||
|
{
|
||
|
this->Insert(ptId,error);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Default case just splits off triangle(s) that form manifold groups.
|
||
|
// Note: this code also handles high-degree vertices.
|
||
|
else
|
||
|
{
|
||
|
vtkIdList *triangles = vtkIdList::New();
|
||
|
vtkIdList *cellIds = vtkIdList::New();
|
||
|
vtkIdList *group = vtkIdList::New();
|
||
|
|
||
|
triangles->Allocate(VTK_MAX_TRIS_PER_VERTEX);
|
||
|
cellIds->Allocate(5,10);
|
||
|
group->Allocate(VTK_MAX_TRIS_PER_VERTEX);
|
||
|
|
||
|
//changes in group size control how to split loop
|
||
|
if ( numTris <= 1 )
|
||
|
{
|
||
|
triangles->Delete();
|
||
|
cellIds->Delete();
|
||
|
group->Delete();
|
||
|
return; //prevents infinite recursion
|
||
|
}
|
||
|
maxGroupSize = ( numTris < this->VertexDegree ? numTris : (this->VertexDegree - 1));
|
||
|
|
||
|
if ( type == VTK_NON_MANIFOLD_VERTEX || type == VTK_HIGH_DEGREE_VERTEX )
|
||
|
{
|
||
|
; //use maxGroupSize
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
maxGroupSize /= 2; //prevents infinite recursion
|
||
|
}
|
||
|
|
||
|
for ( i=0; i < numTris; i++ )
|
||
|
{
|
||
|
triangles->InsertId(i,tris[i]);
|
||
|
}
|
||
|
|
||
|
// now group into manifold pieces
|
||
|
for ( i=0; triangles->GetNumberOfIds() > 0; i++ )
|
||
|
{
|
||
|
group->Reset();
|
||
|
startTri = triangles->GetId(0);
|
||
|
group->InsertId(0,startTri);
|
||
|
triangles->DeleteId(startTri);
|
||
|
this->Mesh->GetCellPoints(startTri,nverts,verts);
|
||
|
p[0] = ( verts[0] != ptId ? verts[0] : verts[1] );
|
||
|
p[1] = ( verts[1] != ptId && verts[1] != p[0] ? verts[1] : verts[2] );
|
||
|
|
||
|
//grab manifold group - j index is the forward/backward direction around vertex
|
||
|
for ( j=0; j < 2; j++ )
|
||
|
{
|
||
|
for ( tri=startTri; p[j] >= 0; )
|
||
|
{
|
||
|
this->Mesh->GetCellEdgeNeighbors(tri, ptId, p[j], cellIds);
|
||
|
if ( cellIds->GetNumberOfIds() == 1 &&
|
||
|
triangles->IsId((tri=cellIds->GetId(0))) > -1 &&
|
||
|
group->GetNumberOfIds() < maxGroupSize )
|
||
|
{
|
||
|
group->InsertNextId(tri);
|
||
|
triangles->DeleteId(tri);
|
||
|
this->Mesh->GetCellPoints(tri,nverts,verts);
|
||
|
if ( verts[0] != ptId && verts[0] != p[j] )
|
||
|
{
|
||
|
p[j] = verts[0];
|
||
|
}
|
||
|
else if ( verts[1] != ptId && verts[1] != p[j] )
|
||
|
{
|
||
|
p[j] = verts[1];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
p[j] = verts[2];
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
p[j] = -1;
|
||
|
}
|
||
|
}
|
||
|
}//for both directions
|
||
|
|
||
|
// reconnect group into manifold chunk (first group is left attached)
|
||
|
if ( i != 0 )
|
||
|
{
|
||
|
id = this->Mesh->InsertNextLinkedPoint(this->X,group->GetNumberOfIds());
|
||
|
meshPD->CopyData(meshPD, ptId, id);
|
||
|
|
||
|
for ( j=0; j < group->GetNumberOfIds(); j++ )
|
||
|
{
|
||
|
tri = group->GetId(j);
|
||
|
this->Mesh->RemoveReferenceToCell(ptId, tri);
|
||
|
this->Mesh->AddReferenceToCell(id, tri);
|
||
|
this->Mesh->ReplaceCellPoint(tri, ptId, id);
|
||
|
}
|
||
|
if ( this->AccumulateError )
|
||
|
{
|
||
|
this->VertexError->InsertValue(id, this->VertexError->GetValue(ptId));
|
||
|
}
|
||
|
if ( insert )
|
||
|
{
|
||
|
this->Insert(id);
|
||
|
}
|
||
|
}//if not first group
|
||
|
}//for all groups
|
||
|
//Don't forget to reinsert original vertex
|
||
|
if ( insert )
|
||
|
{
|
||
|
this->Insert(ptId);
|
||
|
}
|
||
|
|
||
|
triangles->Delete();
|
||
|
cellIds->Delete();
|
||
|
group->Delete();
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Find a way to split this loop. If -1 is returned, then we have a real
|
||
|
// bad situation and we'll split the vertex.
|
||
|
//
|
||
|
vtkIdType vtkDecimatePro::FindSplit (int type, vtkIdType fedges[2],
|
||
|
vtkIdType& pt1, vtkIdType& pt2,
|
||
|
vtkIdList *CollapseTris)
|
||
|
{
|
||
|
vtkIdType i, maxI;
|
||
|
double dist2, e2dist2;
|
||
|
vtkIdType numVerts=this->V->MaxId+1;
|
||
|
|
||
|
pt2 = -1;
|
||
|
CollapseTris->SetNumberOfIds(2);
|
||
|
this->EdgeLengths->Reset();
|
||
|
|
||
|
switch (type)
|
||
|
{
|
||
|
case VTK_SIMPLE_VERTEX:
|
||
|
case VTK_EDGE_END_VERTEX:
|
||
|
case VTK_INTERIOR_EDGE_VERTEX:
|
||
|
if ( type == VTK_INTERIOR_EDGE_VERTEX )
|
||
|
{
|
||
|
dist2 = vtkMath::Distance2BetweenPoints(this->X,
|
||
|
this->V->Array[fedges[0]].x);
|
||
|
this->EdgeLengths->Insert(dist2,fedges[0]);
|
||
|
|
||
|
dist2 = vtkMath::Distance2BetweenPoints(this->X,
|
||
|
this->V->Array[fedges[1]].x);
|
||
|
this->EdgeLengths->Insert(dist2,fedges[1]);
|
||
|
}
|
||
|
else // Compute the edge lengths
|
||
|
{
|
||
|
for ( i=0; i < numVerts; i++ )
|
||
|
{
|
||
|
dist2 = vtkMath::Distance2BetweenPoints(this->X,
|
||
|
this->V->Array[i].x);
|
||
|
this->EdgeLengths->Insert(dist2,i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// See whether the collapse is okay
|
||
|
while ( (maxI = this->EdgeLengths->Pop(0, dist2)) >= 0 )
|
||
|
{
|
||
|
if ( this->IsValidSplit(maxI) )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( maxI >= 0 )
|
||
|
{
|
||
|
CollapseTris->SetId(0,this->T->Array[maxI].id);
|
||
|
if ( maxI == 0 )
|
||
|
{
|
||
|
pt1 = this->V->Array[1].id;
|
||
|
pt2 = this->V->Array[this->V->MaxId].id;
|
||
|
CollapseTris->SetId(1,this->T->Array[this->T->MaxId].id);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pt1 = this->V->Array[(maxI+1)%numVerts].id;
|
||
|
pt2 = this->V->Array[maxI-1].id;
|
||
|
CollapseTris->SetId(1,this->T->Array[maxI-1].id);
|
||
|
}
|
||
|
|
||
|
return this->V->Array[maxI].id;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case VTK_BOUNDARY_VERTEX: //--------------------------------------------
|
||
|
CollapseTris->SetNumberOfIds(1);
|
||
|
// Compute the edge lengths
|
||
|
dist2 = vtkMath::Distance2BetweenPoints(this->X, this->V->Array[0].x);
|
||
|
e2dist2 = vtkMath::Distance2BetweenPoints(this->X,this->V->Array[this->V->MaxId].x);
|
||
|
|
||
|
maxI = -1;
|
||
|
if ( dist2 <= e2dist2 )
|
||
|
{
|
||
|
if ( this->IsValidSplit(0) )
|
||
|
{
|
||
|
maxI = 0;
|
||
|
}
|
||
|
else if ( this->IsValidSplit(this->V->MaxId) )
|
||
|
{
|
||
|
maxI = this->V->MaxId;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( this->IsValidSplit(this->V->MaxId) )
|
||
|
{
|
||
|
maxI = this->V->MaxId;
|
||
|
}
|
||
|
else if ( this->IsValidSplit(0) )
|
||
|
{
|
||
|
maxI = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( maxI >= 0 )
|
||
|
{
|
||
|
if ( maxI == 0 )
|
||
|
{
|
||
|
CollapseTris->SetId(0,this->T->Array[0].id);
|
||
|
pt1 = this->V->Array[1].id;
|
||
|
return this->V->Array[0].id;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CollapseTris->SetId(0,this->T->Array[this->T->MaxId].id);
|
||
|
pt1 = this->V->Array[this->V->MaxId-1].id;
|
||
|
return this->V->Array[this->V->MaxId].id;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case VTK_CRACK_TIP_VERTEX: //-------------------------------------------
|
||
|
this->V->MaxId--;
|
||
|
if ( this->IsValidSplit(0) )
|
||
|
{
|
||
|
CollapseTris->SetId(0,this->T->Array[0].id);
|
||
|
pt1 = this->V->Array[1].id;
|
||
|
pt2 = this->V->Array[this->V->MaxId].id;
|
||
|
CollapseTris->SetId(1,this->T->Array[this->T->MaxId].id);
|
||
|
return this->V->Array[0].id;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this->V->MaxId++;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case VTK_DEGENERATE_VERTEX: //-------------------------------------------
|
||
|
// Collapse to the first edge
|
||
|
CollapseTris->SetId(0,this->T->Array[0].id);
|
||
|
pt1 = this->V->Array[1].id;
|
||
|
if ( this->T->MaxId > 0 ) //more than one triangle
|
||
|
{
|
||
|
if ( this->T->MaxId == this->V->MaxId ) //a complete cycle
|
||
|
{
|
||
|
CollapseTris->SetId(1,this->T->Array[this->T->MaxId].id);
|
||
|
pt2 = this->V->Array[this->V->MaxId].id;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CollapseTris->SetNumberOfIds(1);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CollapseTris->SetNumberOfIds(1);
|
||
|
}
|
||
|
return this->V->Array[0].id;
|
||
|
|
||
|
default:
|
||
|
;
|
||
|
}
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Determine whether the loop can be split at the vertex indicated
|
||
|
//
|
||
|
int vtkDecimatePro::IsValidSplit(int index)
|
||
|
{
|
||
|
vtkIdType fedges[2];
|
||
|
int i, sign;
|
||
|
vtkIdType nverts=this->V->MaxId+1, j;
|
||
|
double *x, val, sPt[3], v21[3], sN[3];
|
||
|
vtkIdType l1[VTK_MAX_TRIS_PER_VERTEX], l2[VTK_MAX_TRIS_PER_VERTEX];
|
||
|
vtkIdType n1, n2;
|
||
|
|
||
|
// For a edge collapse to be valid, all edges to that vertex must
|
||
|
// divide the loop cleanly.
|
||
|
fedges[0] = index;
|
||
|
for ( j=0; j < (nverts-3); j++ )
|
||
|
{
|
||
|
fedges[1] = (index + 2 + j) % nverts;
|
||
|
this->SplitLoop (fedges, n1, l1, n2, l2);
|
||
|
|
||
|
// Create splitting plane. Splitting plane is parallel to the loop
|
||
|
// plane normal and contains the splitting vertices fedges[0] and fedges[1].
|
||
|
for (i=0; i<3; i++)
|
||
|
{
|
||
|
sPt[i] = this->V->Array[fedges[0]].x[i];
|
||
|
v21[i] = this->V->Array[fedges[1]].x[i] - sPt[i];
|
||
|
}
|
||
|
|
||
|
vtkMath::Cross (v21,this->Normal,sN);
|
||
|
if ( vtkMath::Normalize(sN) == 0.0 )
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
for (sign=0, i=0; i < n1; i++) // first loop
|
||
|
{
|
||
|
if ( !(l1[i] == fedges[0] || l1[i] == fedges[1]) )
|
||
|
{
|
||
|
x = this->V->Array[l1[i]].x;
|
||
|
val = vtkPlane::Evaluate(sN,sPt,x);
|
||
|
if ( ((double) fabs((double)val)) < this->Tolerance )
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if ( !sign )
|
||
|
{
|
||
|
sign = (val > this->Tolerance ? 1 : -1);
|
||
|
}
|
||
|
else if ( sign != (val > 0 ? 1 : -1) )
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sign *= -1;
|
||
|
for (i=0; i < n2; i++) // second loop
|
||
|
{
|
||
|
if ( !(l2[i] == fedges[0] || l2[i] == fedges[1]) )
|
||
|
{
|
||
|
x = this->V->Array[l2[i]].x;
|
||
|
val = vtkPlane::Evaluate(sN,sPt,x);
|
||
|
if ( ((double) fabs((double)val)) < this->Tolerance )
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
if ( !sign )
|
||
|
{
|
||
|
sign = (val > this->Tolerance ? 1 : -1);
|
||
|
}
|
||
|
else if ( sign != (val > 0 ? 1 : -1) )
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}// Check all splits
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Creates two loops from splitting plane provided
|
||
|
//
|
||
|
void vtkDecimatePro::SplitLoop(vtkIdType fedges[2], vtkIdType& n1,
|
||
|
vtkIdType *l1, vtkIdType& n2, vtkIdType *l2)
|
||
|
{
|
||
|
vtkIdType i;
|
||
|
vtkIdType *loop;
|
||
|
vtkIdType *count;
|
||
|
|
||
|
n1 = n2 = 0;
|
||
|
loop = l1;
|
||
|
count = &n1;
|
||
|
|
||
|
for (i=0; i <= this->V->MaxId; i++)
|
||
|
{
|
||
|
loop[(*count)++] = i;
|
||
|
if ( i == fedges[0] || i == fedges[1] )
|
||
|
{
|
||
|
loop = (loop == l1 ? l2 : l1);
|
||
|
count = (count == &n1 ? &n2 : &n1);
|
||
|
loop[(*count)++] = i;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Collapse the point to the specified vertex. Distribute the error
|
||
|
// and update neighborhood vertices.
|
||
|
int vtkDecimatePro::CollapseEdge(int type, vtkIdType ptId,
|
||
|
vtkIdType collapseId, vtkIdType pt1,
|
||
|
vtkIdType pt2, vtkIdList *CollapseTris)
|
||
|
{
|
||
|
vtkIdType i, numDeleted=CollapseTris->GetNumberOfIds();
|
||
|
vtkIdType ntris=this->T->MaxId+1;
|
||
|
vtkIdType nverts=this->V->MaxId+1;
|
||
|
vtkIdType tri[2];
|
||
|
vtkIdType verts[VTK_MAX_TRIS_PER_VERTEX+1];
|
||
|
|
||
|
this->NumCollapses++;
|
||
|
for ( i=0; i < numDeleted; i++ )
|
||
|
{
|
||
|
tri[i] = CollapseTris->GetId(i);
|
||
|
}
|
||
|
|
||
|
// type == VTK_CRACK_TIP_VERTEX || type == VTK_SIMPLE_VERTEX
|
||
|
if ( numDeleted == 2 )
|
||
|
{
|
||
|
if ( type == VTK_CRACK_TIP_VERTEX ) //got to seal the crack first
|
||
|
{
|
||
|
this->NumMerges++;
|
||
|
this->Mesh->RemoveReferenceToCell(this->V->Array[this->V->MaxId+1].id, tri[1]);
|
||
|
this->Mesh->ReplaceCellPoint(tri[1],this->V->Array[this->V->MaxId+1].id, collapseId);
|
||
|
}
|
||
|
|
||
|
// delete two triangles
|
||
|
this->Mesh->RemoveReferenceToCell(pt1, tri[0]);
|
||
|
this->Mesh->RemoveReferenceToCell(pt2, tri[1]);
|
||
|
this->Mesh->RemoveReferenceToCell(collapseId, tri[0]);
|
||
|
this->Mesh->RemoveReferenceToCell(collapseId, tri[1]);
|
||
|
this->Mesh->DeletePoint(ptId);
|
||
|
this->Mesh->DeleteCell(tri[0]); this->Mesh->DeleteCell(tri[1]);
|
||
|
|
||
|
// update topology to reflect new attachments
|
||
|
this->Mesh->ResizeCellList(collapseId, ntris-2);
|
||
|
for ( i=0; i < ntris; i++ )
|
||
|
{
|
||
|
if ( this->T->Array[i].id != tri[0] && this->T->Array[i].id != tri[1] )
|
||
|
{
|
||
|
this->Mesh->AddReferenceToCell(collapseId, this->T->Array[i].id);
|
||
|
this->Mesh->ReplaceCellPoint(this->T->Array[i].id,ptId,collapseId);
|
||
|
}
|
||
|
}
|
||
|
}//if interior vertex
|
||
|
|
||
|
else // if ( numDeleted == 1 ) e.g., VTK_BOUNDARY_VERTEX
|
||
|
{
|
||
|
// delete one triangle
|
||
|
this->Mesh->RemoveReferenceToCell(pt1, tri[0]);
|
||
|
this->Mesh->RemoveReferenceToCell(collapseId, tri[0]);
|
||
|
this->Mesh->DeletePoint(ptId);
|
||
|
this->Mesh->DeleteCell(tri[0]);
|
||
|
|
||
|
// update topology to reflect new attachments
|
||
|
if ( ntris > 1 )
|
||
|
{
|
||
|
this->Mesh->ResizeCellList(collapseId, ntris-1);
|
||
|
for ( i=0; i < ntris; i++ )
|
||
|
{
|
||
|
if ( this->T->Array[i].id != tri[0] )
|
||
|
{
|
||
|
this->Mesh->AddReferenceToCell(collapseId, this->T->Array[i].id);
|
||
|
this->Mesh->ReplaceCellPoint(this->T->Array[i].id,ptId,collapseId);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} //else boundary vertex
|
||
|
|
||
|
// Update surrounding vertices. Need to copy verts first because the V/T
|
||
|
// arrays might change as points are being reinserted.
|
||
|
//
|
||
|
for ( i=0; i < nverts; i++ )
|
||
|
{
|
||
|
verts[i] = this->V->Array[i].id;
|
||
|
}
|
||
|
for ( i=0; i < nverts; i++ )
|
||
|
{
|
||
|
this->DeleteId(verts[i]);
|
||
|
this->Insert(verts[i]);
|
||
|
}
|
||
|
|
||
|
return numDeleted;
|
||
|
}
|
||
|
|
||
|
// Get a list of inflection points. These are double values 0 < r <= 1.0
|
||
|
// corresponding to reduction level, and there are a total of
|
||
|
// NumberOfInflectionPoints() values. You must provide an array (of
|
||
|
// the correct size) into which the inflection points are written.
|
||
|
void vtkDecimatePro::GetInflectionPoints(double *inflectionPoints)
|
||
|
{
|
||
|
vtkIdType i;
|
||
|
|
||
|
for (i=0; i < this->GetNumberOfInflectionPoints(); i++)
|
||
|
{
|
||
|
inflectionPoints[i] = this->InflectionPoints->GetValue(i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get a list of inflection points. These are double values 0 < r <= 1.0
|
||
|
// corresponding to reduction level, and there are a total of
|
||
|
// NumberOfInflectionPoints() values. You must provide an array (of
|
||
|
// the correct size) into which the inflection points are written.
|
||
|
// This method returns a pointer to a list of inflection points.
|
||
|
double *vtkDecimatePro::GetInflectionPoints()
|
||
|
{
|
||
|
return this->InflectionPoints->GetPointer(0);
|
||
|
}
|
||
|
|
||
|
// Get the number of inflection points. Only returns a valid value
|
||
|
// after the filter has executed.
|
||
|
vtkIdType vtkDecimatePro::GetNumberOfInflectionPoints()
|
||
|
{
|
||
|
return this->InflectionPoints->GetMaxId()+1;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// The following are private functions used to manage the priority
|
||
|
// queue of vertices.
|
||
|
//
|
||
|
|
||
|
void vtkDecimatePro::InitializeQueue(vtkIdType numPts)
|
||
|
{
|
||
|
if ( !this->PreserveTopology && this->Splitting )
|
||
|
{
|
||
|
numPts = (vtkIdType) ((double)numPts*1.25);
|
||
|
}
|
||
|
|
||
|
this->Queue = vtkPriorityQueue::New();
|
||
|
this->Queue->Allocate(numPts, (vtkIdType)((double)0.25*numPts));
|
||
|
}
|
||
|
|
||
|
int vtkDecimatePro::Pop(double &error)
|
||
|
{
|
||
|
vtkIdType ptId;
|
||
|
|
||
|
// Try returning what's in queue
|
||
|
if ( (ptId = this->Queue->Pop(0, error)) >= 0 )
|
||
|
{
|
||
|
if ( error > this->Error )
|
||
|
{
|
||
|
this->Queue->Reset();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return ptId;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// See whether anything's left and split/re-insert if allowed
|
||
|
if ( this->NumberOfRemainingTris > 0 && this->Split &&
|
||
|
this->SplitState == VTK_STATE_UNSPLIT )
|
||
|
{
|
||
|
vtkDebugMacro(<<"Splitting this->Mesh");
|
||
|
|
||
|
this->SplitState = VTK_STATE_SPLIT;
|
||
|
this->SplitMesh();
|
||
|
this->CosAngle = cos ((double) vtkMath::DegreesToRadians() * this->SplitAngle);
|
||
|
|
||
|
// Now that things are split, insert the vertices. (Have to do this
|
||
|
// otherwise error calculation is incorrect.)
|
||
|
for ( ptId=0; ptId < this->Mesh->GetNumberOfPoints(); ptId++ )
|
||
|
{
|
||
|
this->Insert(ptId);
|
||
|
}
|
||
|
|
||
|
if ( (ptId = this->Queue->Pop(0, error)) >= 0 )
|
||
|
{
|
||
|
if ( error > this->Error )
|
||
|
{
|
||
|
this->Queue->Reset();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return ptId;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If here, then this->Mesh splitting hasn't helped or is exhausted. Run thru
|
||
|
// vertices and split them as necessary no matter what.
|
||
|
if ( this->NumberOfRemainingTris > 0 && this->Split && this->SplitState != VTK_STATE_SPLIT_ALL )
|
||
|
{
|
||
|
vtkDebugMacro(<<"Final splitting attempt");
|
||
|
|
||
|
this->SplitState = VTK_STATE_SPLIT_ALL;
|
||
|
for ( ptId=0; ptId < this->Mesh->GetNumberOfPoints(); ptId++ )
|
||
|
{
|
||
|
this->Insert(ptId);
|
||
|
}
|
||
|
|
||
|
if ( (ptId = this->Queue->Pop(0, error)) >= 0 )
|
||
|
{
|
||
|
if ( error > this->Error )
|
||
|
{
|
||
|
this->Queue->Reset();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return ptId;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//every possible point has been processed
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
// Computes error and inserts point into priority queue.
|
||
|
void vtkDecimatePro::Insert(vtkIdType ptId, double error)
|
||
|
{
|
||
|
int type, simpleType;
|
||
|
vtkIdType *cells;
|
||
|
vtkIdType fedges[2];
|
||
|
unsigned short int ncells;
|
||
|
|
||
|
// on value of error, we need to compute it or just insert the point
|
||
|
if ( error < -this->Tolerance )
|
||
|
{
|
||
|
this->Mesh->GetPoint(ptId,this->X);
|
||
|
this->Mesh->GetPointCells(ptId,ncells,cells);
|
||
|
|
||
|
if ( ncells > 0 )
|
||
|
{
|
||
|
simpleType = 0;
|
||
|
type = this->EvaluateVertex(ptId, ncells, cells, fedges);
|
||
|
|
||
|
// Compute error for simple types - split vertex handles others
|
||
|
if ( type == VTK_SIMPLE_VERTEX || type == VTK_EDGE_END_VERTEX ||
|
||
|
type == VTK_CRACK_TIP_VERTEX )
|
||
|
{
|
||
|
simpleType = 1;
|
||
|
error = ComputeSimpleError(this->X,this->Normal,this->Pt);
|
||
|
}
|
||
|
|
||
|
else if ( (type == VTK_INTERIOR_EDGE_VERTEX) ||
|
||
|
(type == VTK_BOUNDARY_VERTEX && this->BoundaryVertexDeletion) )
|
||
|
{
|
||
|
simpleType = 1;
|
||
|
if ( ncells == 1 ) //compute better error for single triangle
|
||
|
{
|
||
|
error = ComputeSingleTriangleError(this->X,this->V->Array[0].x,
|
||
|
this->V->Array[1].x);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
error = ComputeEdgeError(this->X, this->V->Array[fedges[0]].x,
|
||
|
this->V->Array[fedges[1]].x);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( simpleType )
|
||
|
{
|
||
|
if ( this->AccumulateError )
|
||
|
{
|
||
|
error += this->VertexError->GetValue(ptId);
|
||
|
}
|
||
|
this->Queue->Insert(error,ptId);
|
||
|
}
|
||
|
|
||
|
// Type is complex so we break it up (if splitting allowed). A
|
||
|
// side-effect of splitting a vertex is that it inserts it and any
|
||
|
// new vertices into queue.
|
||
|
else if ( this->SplitState == VTK_STATE_SPLIT && type != VTK_DEGENERATE_VERTEX )
|
||
|
{
|
||
|
this->SplitVertex(ptId, type, ncells, cells, 1);
|
||
|
} //not a simple type
|
||
|
|
||
|
} //if cells attached to vertex
|
||
|
} //need to compute the error
|
||
|
|
||
|
// If point is being recycled, see whether we want to split it
|
||
|
else if ( error >= VTK_RECYCLE_VERTEX )
|
||
|
{
|
||
|
//see whether to split it, otherwise it isn't inserted yet
|
||
|
if ( this->SplitState == VTK_STATE_SPLIT_ALL )
|
||
|
{
|
||
|
this->Mesh->GetPoint(ptId,this->X);
|
||
|
this->Mesh->GetPointCells(ptId,ncells,cells);
|
||
|
if ( ncells > 0 )
|
||
|
{
|
||
|
type = this->EvaluateVertex(ptId, ncells, cells, fedges);
|
||
|
this->SplitVertex(ptId, type, ncells, cells, 1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Sometimes the error is computed for us so we insert it appropriately
|
||
|
else
|
||
|
{
|
||
|
if ( this->AccumulateError )
|
||
|
{
|
||
|
error += this->VertexError->GetValue(ptId);
|
||
|
}
|
||
|
this->Queue->Insert(error,ptId);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Compute the error of the point to the new triangulated surface
|
||
|
void vtkDecimatePro::DistributeError(double error)
|
||
|
{
|
||
|
vtkIdType i;
|
||
|
vtkIdType nverts=this->V->MaxId+1;
|
||
|
double previousError;
|
||
|
|
||
|
for (i=0; i < nverts; i++)
|
||
|
{
|
||
|
previousError = this->VertexError->GetValue(this->V->Array[i].id);
|
||
|
this->VertexError->SetValue(this->V->Array[i].id, previousError+error);
|
||
|
}
|
||
|
}
|
||
|
void vtkDecimatePro::DeleteQueue()
|
||
|
{
|
||
|
if (this->Queue)
|
||
|
{
|
||
|
this->Queue->Delete();
|
||
|
}
|
||
|
this->Queue=NULL;
|
||
|
}
|
||
|
|
||
|
double vtkDecimatePro::DeleteId(vtkIdType id)
|
||
|
{
|
||
|
return this->Queue->DeleteId(id);
|
||
|
}
|
||
|
|
||
|
void vtkDecimatePro::Reset()
|
||
|
{
|
||
|
this->Queue->Reset();
|
||
|
}
|
||
|
|
||
|
void vtkDecimatePro::PrintSelf(ostream& os, vtkIndent indent)
|
||
|
{
|
||
|
this->Superclass::PrintSelf(os,indent);
|
||
|
|
||
|
os << indent << "Target Reduction: " << this->TargetReduction << "\n";
|
||
|
os << indent << "Feature Angle: " << this->FeatureAngle << "\n";
|
||
|
|
||
|
os << indent << "Splitting: " << (this->Splitting ? "On\n" : "Off\n");
|
||
|
os << indent << "Split Angle: " << this->SplitAngle << "\n";
|
||
|
os << indent << "Pre-Split Mesh: "
|
||
|
<< (this->PreSplitMesh ? "On\n" : "Off\n");
|
||
|
|
||
|
os << indent << "Degree: " << this->Degree << "\n";
|
||
|
|
||
|
os << indent << "Preserve Topology: "
|
||
|
<< (this->PreserveTopology ? "On\n" : "Off\n");
|
||
|
os << indent << "Maximum Error: " << this->MaximumError << "\n";
|
||
|
os << indent << "Accumulate Error: "
|
||
|
<< (this->AccumulateError ? "On\n" : "Off\n");
|
||
|
os << indent << "Error is Absolute: "
|
||
|
<< (this->ErrorIsAbsolute ? "On\n" : "Off\n");
|
||
|
os << indent << "Absolute Error: " << this->AbsoluteError << "\n";
|
||
|
|
||
|
os << indent << "Boundary Vertex Deletion: "
|
||
|
<< (this->BoundaryVertexDeletion ? "On\n" : "Off\n");
|
||
|
|
||
|
os << indent << "Inflection Point Ratio: "
|
||
|
<< this->InflectionPointRatio << "\n";
|
||
|
os << indent << "Number Of Inflection Points: "
|
||
|
<< this->GetNumberOfInflectionPoints() << "\n";
|
||
|
}
|