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.
 
 
 
 
 
 

1377 lines
42 KiB

/*=========================================================================
Program: Visualization Toolkit
Module: $RCSfile: vtkQuadricDecimation.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.
=========================================================================*/
// Comments from Brad---
// FIXME: I do not have a very good method for detecting the stability of a
// matrix
// FIXME: improve the combination of boundary constraints and texture
// coordinates
// FIXME: I want to turn off copying an attribute by default and then enable
// it in the ComputeNumberOfComponets function
// ISSUE: CheckPlacement is not really a manifold check, should there be a
// topological manifold check?
// ISSUE: I know I use to think that there was an error in the way Hugues
// desribed the area coefficient, but it now seems wrong to me and seems to
// produce better results with it not squared, may be this should be some
// kind of user parameter? Both seem useful ie uniform area vs. more
// curvature dependent
// ISSUE: policy on attributes, normals should be renomrlized, should texture
// coordinates be clamped? or just indicate that one might want to use the
// array calculator to fix these?
// ISSUE: the initial value of the Attribute weights is one, this is generaly
// not useful, ussually set around .1, but I did this because the the
// toggling on and off sets it to 1 and 0
#include "vtkQuadricDecimation.h"
#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkEdgeTable.h"
#include "vtkDoubleArray.h"
#include "vtkGenericCell.h"
#include "vtkIdList.h"
#include "vtkMath.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkPolyData.h"
#include "vtkPointData.h"
#include "vtkPriorityQueue.h"
#include "vtkTriangle.h"
vtkCxxRevisionMacro(vtkQuadricDecimation, "$Revision: 1.38 $");
vtkStandardNewMacro(vtkQuadricDecimation);
//----------------------------------------------------------------------------
vtkQuadricDecimation::vtkQuadricDecimation()
{
this->Edges = vtkEdgeTable::New();
this->EdgeCosts = vtkPriorityQueue::New();
this->EndPoint1List = vtkIdList::New();
this->EndPoint2List = vtkIdList::New();
this->ErrorQuadrics = NULL;
this->TargetPoints = vtkDoubleArray::New();
this->TargetReduction = 0.9;
this->NumberOfEdgeCollapses = 0;
this->NumberOfComponents = 0;
this->AttributeErrorMetric = 0;
this->ScalarsAttribute = 1;
this->VectorsAttribute = 1;
this->NormalsAttribute = 1;
this->TCoordsAttribute = 1;
this->TensorsAttribute = 1;
this->ScalarsWeight = 0.1;
this->VectorsWeight = 0.1;
this->NormalsWeight = 0.1;
this->TCoordsWeight = 0.1;
this->TensorsWeight = 0.1;
this->ActualReduction = 0.0;
}
//----------------------------------------------------------------------------
vtkQuadricDecimation::~vtkQuadricDecimation()
{
this->Edges->Delete();
this->EdgeCosts->Delete();
this->EndPoint1List->Delete();
this->EndPoint2List->Delete();
this->TargetPoints->Delete();
}
void vtkQuadricDecimation::SetPointAttributeArray(vtkIdType ptId,
const double *x)
{
int i;
this->Mesh->GetPoints()->SetPoint(ptId, x);
for (i = 0; i < this->NumberOfComponents; i++)
{
if (i < this->AttributeComponents[0])
{
this->Mesh->GetPointData()->GetScalars()->
SetComponent(ptId, i, x[3+i]/this->AttributeScale[0]);
}
else if (i < this->AttributeComponents[1])
{
this->Mesh->GetPointData()->GetVectors()->
SetComponent(ptId, i-this->AttributeComponents[0], x[3+i]/this->AttributeScale[1]);
}
else if (i < this->AttributeComponents[2])
{
this->Mesh->GetPointData()->GetNormals()->
SetComponent(ptId, i-this->AttributeComponents[1], x[3+i]/this->AttributeScale[2]);
}
else if (i < this->AttributeComponents[3])
{
this->Mesh->GetPointData()->GetTCoords()->
SetComponent(ptId, i-this->AttributeComponents[2], x[3+i]/this->AttributeScale[3]);
}
else if (i < this->AttributeComponents[4])
{
this->Mesh->GetPointData()->GetTensors()->
SetComponent(ptId, i-this->AttributeComponents[3], x[3+i]/this->AttributeScale[4]);
}
}
}
void vtkQuadricDecimation::GetPointAttributeArray(vtkIdType ptId, double *x)
{
int i;
this->Mesh->GetPoints()->GetPoint(ptId, x);
for (i = 0; i < this->NumberOfComponents; i++)
{
if (i < this->AttributeComponents[0])
{
x[3+i] = this->Mesh->GetPointData()->GetScalars()->
GetComponent(ptId, i) * this->AttributeScale[0];
}
else if (i < this->AttributeComponents[1])
{
x[3+i] = this->Mesh->GetPointData()->GetVectors()->
GetComponent(ptId, i-this->AttributeComponents[0]) * this->AttributeScale[1];
}
else if (i < this->AttributeComponents[2])
{
x[3+i] = this->Mesh->GetPointData()->GetNormals()->
GetComponent(ptId, i-this->AttributeComponents[1]) * this->AttributeScale[2];
}
else if (i < this->AttributeComponents[3])
{
x[3+i] = this->Mesh->GetPointData()->GetTCoords()->
GetComponent(ptId, i-this->AttributeComponents[2]) * this->AttributeScale[3];
}
else if (i < this->AttributeComponents[4])
{
x[3+i] = this->Mesh->GetPointData()->GetTensors()->
GetComponent(ptId, i-this->AttributeComponents[3]) * this->AttributeScale[4];
}
}
}
//----------------------------------------------------------------------------
int vtkQuadricDecimation::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 = input->GetNumberOfPoints();
vtkIdType numTris = input->GetNumberOfPolys();
vtkIdType edgeId, i;
int j;
double cost;
double *x;
vtkCellArray *polys;
vtkDataArray *attrib;
vtkPoints *points;
vtkPointData *pointData;
vtkIdType endPtIds[2];
vtkIdList *outputCellList;
vtkIdType npts, *pts;
vtkIdType numDeletedTris=0;
// check some assuptiona about the data
if (input->GetPolys() == NULL || input->GetPoints() == NULL ||
input->GetPointData() == NULL || input->GetFieldData() == NULL)
{
vtkErrorMacro("Nothing to decimate");
return 1;
}
if (input->GetPolys()->GetMaxCellSize() > 3)
{
vtkErrorMacro("Can only decimate triangles");
return 1;
}
polys = vtkCellArray::New();
points = vtkPoints::New();
pointData = vtkPointData::New();
outputCellList = vtkIdList::New();
// copy the input (only polys) to our working mesh
this->Mesh = vtkPolyData::New();
points->DeepCopy(input->GetPoints());
this->Mesh->SetPoints(points);
points->Delete();
polys->DeepCopy(input->GetPolys());
this->Mesh->SetPolys(polys);
polys->Delete();
if (this->AttributeErrorMetric)
{
this->Mesh->GetPointData()->DeepCopy(input->GetPointData());
}
pointData->Delete();
this->Mesh->GetFieldData()->PassData(input->GetFieldData());
this->Mesh->BuildCells();
this->Mesh->BuildLinks();
this->ErrorQuadrics =
new vtkQuadricDecimation::ErrorQuadric[numPts];
vtkDebugMacro(<<"Computing Edges");
this->Edges->InitEdgeInsertion(numPts, 1); // storing edge id as attribute
this->EdgeCosts->Allocate(this->Mesh->GetPolys()->GetNumberOfCells() * 3);
for (i = 0; i < this->Mesh->GetNumberOfCells(); i++)
{
this->Mesh->GetCellPoints(i, npts, pts);
for (j = 0; j < 3; j++)
{
if (this->Edges->IsEdge(pts[j], pts[(j+1)%3]) == -1)
{
// If this edge has not been processed, get an id for it, add it to
// the edge list (Edges), and add its endpoints to the EndPoint1List
// and EndPoint2List (the 2 endpoints to different lists).
edgeId = this->Edges->GetNumberOfEdges();
this->Edges->InsertEdge(pts[j], pts[(j+1)%3], edgeId);
this->EndPoint1List->InsertId(edgeId, pts[j]);
this->EndPoint2List->InsertId(edgeId, pts[(j+1)%3]);
}
}
}
this->UpdateProgress(0.1);
this->NumberOfComponents = 0;
if (this->AttributeErrorMetric)
{
this->ComputeNumberOfComponents();
}
x = new double [3+this->NumberOfComponents];
this->CollapseCellIds = vtkIdList::New();
this->TempX = new double [3+this->NumberOfComponents];
this->TempQuad = new double[11 + 4 * this->NumberOfComponents];
this->TempB = new double [3 + this->NumberOfComponents];
this->TempA = new double*[3 + this->NumberOfComponents];
this->TempData = new double [(3 + this->NumberOfComponents)*(3 + this->NumberOfComponents)];
for (i = 0; i < 3 + this->NumberOfComponents; i++)
{
this->TempA[i] = this->TempData+i*(3 + this->NumberOfComponents);
}
this->TargetPoints->SetNumberOfComponents(3+this->NumberOfComponents);
vtkDebugMacro(<<"Computing Quadrics");
this->InitializeQuadrics(numPts);
this->AddBoundaryConstraints();
this->UpdateProgress(0.15);
vtkDebugMacro(<<"Computing Costs");
// Compute the cost of and target point for collapsing each edge.
for (i = 0; i < this->Edges->GetNumberOfEdges(); i++)
{
if (this->AttributeErrorMetric)
{
cost = this->ComputeCost2(i, x);
}
else
{
cost = this->ComputeCost(i, x);
}
this->EdgeCosts->Insert(cost, i);
this->TargetPoints->InsertTuple(i, x);
}
this->UpdateProgress(0.20);
// Okay collapse edges until desired reduction is reached
this->ActualReduction = 0.0;
this->NumberOfEdgeCollapses = 0;
edgeId = this->EdgeCosts->Pop(0,cost);
int abort = 0;
while ( !abort && edgeId >= 0 && cost < VTK_DOUBLE_MAX &&
this->ActualReduction < this->TargetReduction )
{
if ( ! (this->NumberOfEdgeCollapses % 10000) )
{
vtkDebugMacro(<<"Collapsing edge#" << this->NumberOfEdgeCollapses);
this->UpdateProgress (0.20 + 0.80*this->NumberOfEdgeCollapses/numPts);
abort = this->GetAbortExecute();
}
endPtIds[0] = this->EndPoint1List->GetId(edgeId);
endPtIds[1] = this->EndPoint2List->GetId(edgeId);
this->TargetPoints->GetTuple(edgeId, x);
// check for a poorly placed point
if ( !this->IsGoodPlacement(endPtIds[0], endPtIds[1], x))
{
vtkDebugMacro(<<"Poor placement detected " << edgeId << " " << cost);
// return the point to the queue but with the max cost so that
// when it is recomputed it will be reconsidered
this->EdgeCosts->Insert(VTK_DOUBLE_MAX, edgeId);
edgeId = this->EdgeCosts->Pop(0, cost);
continue;
}
this->NumberOfEdgeCollapses++;
// Set the new coordinates of point0.
this->SetPointAttributeArray(endPtIds[0], x);
vtkDebugMacro(<<"Cost: " << cost << " Edge: "
<< endPtIds[0] << " " << endPtIds[1]);
// Merge the quadrics of the two points.
this->AddQuadric(endPtIds[1], endPtIds[0]);
this->UpdateEdgeData(endPtIds[0], endPtIds[1]);
// Update the output triangles.
numDeletedTris += this->CollapseEdge(endPtIds[0], endPtIds[1]);
this->ActualReduction = (double) numDeletedTris / numTris;
edgeId = this->EdgeCosts->Pop(0, cost);
}
vtkDebugMacro(<<"Number Of Edge Collapses: "
<< this->NumberOfEdgeCollapses << " Cost: " << cost);
// clean up working data
for (i = 0; i < numPts; i++)
{
delete [] this->ErrorQuadrics[i].Quadric;
}
delete [] this->ErrorQuadrics;
delete [] x;
this->CollapseCellIds->Delete();
delete [] this->TempX;
delete [] this->TempQuad;
delete [] this->TempB;
delete [] this->TempA;
delete [] this->TempData;
// copy the simplified mesh from the working mesh to the output mesh
for (i = 0; i < this->Mesh->GetNumberOfCells(); i++)
{
if (this->Mesh->GetCell(i)->GetCellType() != VTK_EMPTY_CELL)
{
outputCellList->InsertNextId(i);
}
}
output->Reset();
output->Allocate(this->Mesh, outputCellList->GetNumberOfIds());
output->GetPointData()->CopyAllocate(this->Mesh->GetPointData(),1);
output->CopyCells(this->Mesh, outputCellList);
this->Mesh->DeleteLinks();
this->Mesh->Delete();
outputCellList->Delete();
// renormalize, clamp attributes
if (this->AttributeErrorMetric)
{
if (NULL != (attrib = output->GetPointData()->GetNormals()))
{
for (i = 0; i < attrib->GetNumberOfTuples(); i++)
{
vtkMath::Normalize(attrib->GetTuple3(i));
}
}
// might want to add clamping texture coordinates??
}
return 1;
}
//----------------------------------------------------------------------------
void vtkQuadricDecimation::InitializeQuadrics(vtkIdType numPts)
{
vtkPolyData *input = this->Mesh;
double *QEM;
vtkIdType ptId;
int i, j;
vtkCellArray *polys;
vtkIdType npts, *pts=NULL;
double point0[3], point1[3], point2[3];
double n[3];
double tempP1[3], tempP2[3], d, triArea2;
double data[16];
double *A[4], x[4];
int index[4];
A[0] = data;
A[1] = data+4;
A[2] = data+8;
A[3] = data+12;
// allocate local QEM sparce matrix
QEM = new double[11 + 4 * this->NumberOfComponents];
// clear and allocate global QEM array
for (ptId = 0; ptId < numPts; ptId++)
{
this->ErrorQuadrics[ptId].Quadric =
new double[11 + 4 * this->NumberOfComponents];
for (i = 0; i < 11 + 4 * this->NumberOfComponents; i++)
{
this->ErrorQuadrics[ptId].Quadric[i] = 0.0;
}
}
polys = input->GetPolys();
// compute the QEM for each face
for (polys->InitTraversal(); polys->GetNextCell(npts, pts); )
{
input->GetPoint(pts[0], point0);
input->GetPoint(pts[1], point1);
input->GetPoint(pts[2], point2);
for (i = 0; i < 3; i++)
{
tempP1[i] = point1[i] - point0[i];
tempP2[i] = point2[i] - point0[i];
}
vtkMath::Cross(tempP1, tempP2, n);
triArea2 = vtkMath::Normalize(n);
//triArea2 = (triArea2 * triArea2 * 0.25);
triArea2 = triArea2 * 0.5;
// I am unsure whether this should be squared or not??
d = -vtkMath::Dot(n, point0);
// could possible add in angle weights??
// set the geometric part of the QEM
QEM[0] = n[0] * n[0];
QEM[1] = n[0] * n[1];
QEM[2] = n[0] * n[2];
QEM[3] = d * n[0];
QEM[4] = n[1] * n[1];
QEM[5] = n[1] * n[2];
QEM[6] = d * n[1];
QEM[7] = n[2] * n[2];
QEM[8] = d * n[2];
QEM[9] = d * d;
QEM[10] = 1;
if (this->AttributeErrorMetric)
{
for (i = 0; i < 3; i++)
{
A[0][i] = point0[i];
A[1][i] = point1[i];
A[2][i] = point2[i];
A[3][i] = n[i];
}
A[0][3] = A[1][3] = A[2][3] = 1;
A[3][3] = 0;
// should handle poorly condition matrix better
if (vtkMath::LUFactorLinearSystem(A, index, 4))
{
for (i = 0; i < this->NumberOfComponents; i++)
{
x[3] = 0;
if (i < this->AttributeComponents[0])
{
x[0] = input->GetPointData()->GetScalars()->GetComponent(pts[0], i) * this->AttributeScale[0];
x[1] = input->GetPointData()->GetScalars()->GetComponent(pts[1], i) * this->AttributeScale[0];
x[2] = input->GetPointData()->GetScalars()->GetComponent(pts[2], i) * this->AttributeScale[0];
}
else if (i < this->AttributeComponents[1])
{
x[0] = input->GetPointData()->GetVectors()->GetComponent(pts[0], i - this->AttributeComponents[0]) * this->AttributeScale[1];
x[1] = input->GetPointData()->GetVectors()->GetComponent(pts[1], i - this->AttributeComponents[0]) * this->AttributeScale[1];
x[2] = input->GetPointData()->GetVectors()->GetComponent(pts[2], i - this->AttributeComponents[0]) * this->AttributeScale[1];
}
else if (i < this->AttributeComponents[2])
{
x[0] = input->GetPointData()->GetNormals()->GetComponent(pts[0], i - this->AttributeComponents[1]) * this->AttributeScale[2];
x[1] = input->GetPointData()->GetNormals()->GetComponent(pts[1], i - this->AttributeComponents[1]) * this->AttributeScale[2];
x[2] = input->GetPointData()->GetNormals()->GetComponent(pts[2], i - this->AttributeComponents[1]) * this->AttributeScale[2];
}
else if (i < this->AttributeComponents[3])
{
x[0] = input->GetPointData()->GetTCoords()->GetComponent(pts[0], i - this->AttributeComponents[2]) * this->AttributeScale[3];
x[1] = input->GetPointData()->GetTCoords()->GetComponent(pts[1], i - this->AttributeComponents[2])* this->AttributeScale[3];
x[2] = input->GetPointData()->GetTCoords()->GetComponent(pts[2], i - this->AttributeComponents[2])* this->AttributeScale[3];
}
else if (i < this->AttributeComponents[4])
{
x[0] = input->GetPointData()->GetTensors()->GetComponent(pts[0], i - this->AttributeComponents[3])* this->AttributeScale[4];
x[1] = input->GetPointData()->GetTensors()->GetComponent(pts[1], i - this->AttributeComponents[3])* this->AttributeScale[4];
x[2] = input->GetPointData()->GetTensors()->GetComponent(pts[2], i - this->AttributeComponents[3])* this->AttributeScale[4];
}
vtkMath::LUSolveLinearSystem(A, index, x, 4);
// add in the contribution of this element into the QEM
QEM[0] += x[0] * x[0];
QEM[1] += x[0] * x[1];
QEM[2] += x[0] * x[2];
QEM[3] += x[3] * x[0];
QEM[4] += x[1] * x[1];
QEM[5] += x[1] * x[2];
QEM[6] += x[3] * x[1];
QEM[7] += x[2] * x[2];
QEM[8] += x[3] * x[2];
QEM[9] += x[3] * x[3];
QEM[11+i*4] = -x[0];
QEM[12+i*4] = -x[1];
QEM[13+i*4] = -x[2];
QEM[14+i*4] = -x[3];
}
}
else
{
vtkErrorMacro(<<"Unable to factor attribute matrix!");
}
}
// add the QEM to all point of the face
for (i = 0; i < 3; i++)
{
for (j = 0; j < 11 + 4 * this->NumberOfComponents; j++)
{
this->ErrorQuadrics[pts[i]].Quadric[j] += QEM[j] * triArea2;
}
}
}//for all triangles
delete [] QEM;
}
void vtkQuadricDecimation::AddBoundaryConstraints(void)
{
vtkPolyData *input = this->Mesh;
double *QEM;
vtkIdType cellId;
int i, j;
vtkIdType npts, *pts;
double t0[3], t1[3], t2[3];
double e0[3], e1[3], n[3], c, d, w;
vtkIdList *cellIds = vtkIdList::New();
// allocate local QEM space matrix
QEM = new double[11 + 4 * this->NumberOfComponents];
for (cellId = 0; cellId < input->GetNumberOfCells(); cellId++)
{
input->GetCellPoints(cellId, npts, pts);
for (i = 0; i < 3; i++)
{
input->GetCellEdgeNeighbors(cellId, pts[i], pts[(i+1)%3], cellIds);
if (cellIds->GetNumberOfIds() == 0)
{
// this is a boundary
input->GetPoint(pts[(i+2)%3], t0);
input->GetPoint(pts[i], t1);
input->GetPoint(pts[(i+1)%3], t2);
// computing a plane which is orthogonal to line t1, t2 and incident
// with it
for (j = 0; j < 3; j++)
{
e0[j] = t2[j] - t1[j];
}
for (j = 0; j < 3; j++)
{
e1[j] = t0[j] - t1[j];
}
// compute n so that it is orthogonal to e0 and parallel to the
// triangle
c = vtkMath::Dot(e0,e1)/(e0[0]*e0[0]+e0[1]*e0[1]+e0[2]*e0[2]);
for (j = 0; j < 3; j++)
{
n[j] = e1[j] - c*e0[j];
}
vtkMath::Normalize(n);
d = -vtkMath::Dot(n, t1);
w = vtkMath::Norm(e0);
//w *= w;
// area issue ??
// could possible add in angle weights??
QEM[0] = n[0] * n[0];
QEM[1] = n[0] * n[1];
QEM[2] = n[0] * n[2];
QEM[3] = d * n[0];
QEM[4] = n[1] * n[1];
QEM[5] = n[1] * n[2];
QEM[6] = d * n[1];
QEM[7] = n[2] * n[2];
QEM[8] = d * n[2];
QEM[9] = d * d;
QEM[10] = 1;
// need to add orthogonal plane with the other Attributes, but this
// is not clear??
// check to interaction with attribute data
for (j = 0; j < 11; j++)
{
this->ErrorQuadrics[pts[i]].Quadric[j] += QEM[j]*w;
this->ErrorQuadrics[pts[(i+1)%3]].Quadric[j] += QEM[j]*w;
}
}
}
}
cellIds->Delete();
delete [] QEM;
}
//----------------------------------------------------------------------------
void vtkQuadricDecimation::AddQuadric(vtkIdType oldPtId, vtkIdType newPtId)
{
int i;
for (i = 0; i < 11 + 4*this->NumberOfComponents; i++)
{
this->ErrorQuadrics[newPtId].Quadric[i] +=
this->ErrorQuadrics[oldPtId].Quadric[i];
}
}
//----------------------------------------------------------------------------
void vtkQuadricDecimation::FindAffectedEdges(vtkIdType p1Id, vtkIdType p2Id,
vtkIdList *edges)
{
unsigned short ncells;
vtkIdType *cells, npts, *pts, edgeId;
unsigned short i, j;
edges->Reset();
this->Mesh->GetPointCells(p2Id, ncells, cells);
for (i = 0; i < ncells; i++)
{
this->Mesh->GetCellPoints(cells[i], npts, pts);
for (j = 0; j < 3; j++)
{
if (pts[j] != p1Id && pts[j] != p2Id &&
(edgeId = this->Edges->IsEdge(pts[j], p2Id)) >= 0 &&
edges->IsId(edgeId) == -1)
{
edges->InsertNextId(edgeId);
}
}
}
this->Mesh->GetPointCells(p1Id, ncells, cells);
for (i = 0; i < ncells; i++)
{
this->Mesh->GetCellPoints(cells[i], npts, pts);
for (j = 0; j < 3; j++)
{
if (pts[j] != p1Id && pts[j] != p2Id &&
(edgeId = this->Edges->IsEdge(pts[j], p1Id)) >= 0 &&
edges->IsId(edgeId) == -1)
{
edges->InsertNextId(edgeId);
}
}
}
}
// FIXME: memory allocation clean up
void vtkQuadricDecimation::UpdateEdgeData(vtkIdType pt0Id, vtkIdType pt1Id)
{
vtkIdList *changedEdges = vtkIdList::New();
vtkIdType i, edgeId, edge[2];
double cost;
// Find all edges with exactly either of these 2 endpoints.
this->FindAffectedEdges(pt0Id, pt1Id, changedEdges);
// Reset the endpoints for these edges to reflect the new point from the
// collapsed edge.
// Add these new edges to the edge table.
// Remove the the changed edges from the priority queue.
for (i = 0; i < changedEdges->GetNumberOfIds(); i++)
{
edge[0] = this->EndPoint1List->GetId(changedEdges->GetId(i));
edge[1] = this->EndPoint2List->GetId(changedEdges->GetId(i));
// Remove all affected edges from the priority queue.
// This does not include collapsed edge.
this->EdgeCosts->DeleteId(changedEdges->GetId(i));
// Determine the new set of edges
if (edge[0] == pt1Id)
{
if (this->Edges->IsEdge(edge[1], pt0Id) == -1)
{ // The edge will be completely new, add it.
edgeId = this->Edges->GetNumberOfEdges();
this->Edges->InsertEdge(edge[1], pt0Id, edgeId);
this->EndPoint1List->InsertId(edgeId, edge[1]);
this->EndPoint2List->InsertId(edgeId, pt0Id);
// Compute cost (target point/data) and add to priority cue.
if (this->AttributeErrorMetric)
{
cost = this->ComputeCost2(edgeId, this->TempX);
}
else
{
cost = this->ComputeCost(edgeId, this->TempX);
}
this->EdgeCosts->Insert(cost, edgeId);
this->TargetPoints->InsertTuple(edgeId, this->TempX);
}
}
else if (edge[1] == pt1Id)
{ // The edge will be completely new, add it.
if (this->Edges->IsEdge(edge[0], pt0Id) == -1)
{
edgeId = this->Edges->GetNumberOfEdges();
this->Edges->InsertEdge(edge[0], pt0Id, edgeId);
this->EndPoint1List->InsertId(edgeId, edge[0]);
this->EndPoint2List->InsertId(edgeId, pt0Id);
// Compute cost (target point/data) and add to priority cue.
if (this->AttributeErrorMetric)
{
cost = this->ComputeCost2(edgeId, this->TempX);
}
else
{
cost = this->ComputeCost(edgeId, this->TempX);
}
this->EdgeCosts->Insert(cost, edgeId);
this->TargetPoints->InsertTuple(edgeId, this->TempX);
}
}
else
{ // This edge already has one point as the merged point.
if (this->AttributeErrorMetric)
{
cost = this->ComputeCost2(changedEdges->GetId(i), this->TempX);
}
else
{
cost = this->ComputeCost(changedEdges->GetId(i), this->TempX);
}
this->EdgeCosts->Insert(cost, changedEdges->GetId(i));
this->TargetPoints->InsertTuple(changedEdges->GetId(i), this->TempX);
}
}
changedEdges->Delete();
return;
}
//----------------------------------------------------------------------------
double vtkQuadricDecimation::ComputeCost(vtkIdType edgeId, double *x)
{
static const double errorNumber = 1e-10;
double temp[3], A[3][3], b[3];
vtkIdType pointIds[2];
double cost = 0.0;
double *index;
int i, j;
double newPoint [4];
double v[3], c, norm, normTemp, temp2[3];
double pt1[3], pt2[3];
pointIds[0] = this->EndPoint1List->GetId(edgeId);
pointIds[1] = this->EndPoint2List->GetId(edgeId);
for (i = 0; i < 11 + 4 * this->NumberOfComponents; i++)
{
this->TempQuad[i] = this->ErrorQuadrics[pointIds[0]].Quadric[i] +
this->ErrorQuadrics[pointIds[1]].Quadric[i];
}
A[0][0] = this->TempQuad[0];
A[0][1] = A[1][0] = this->TempQuad[1];
A[0][2] = A[2][0] = this->TempQuad[2];
A[1][1] = this->TempQuad[4];
A[1][2] = A[2][1] = this->TempQuad[5];
A[2][2] = this->TempQuad[7];
b[0] = -this->TempQuad[3];
b[1] = -this->TempQuad[6];
b[2] = -this->TempQuad[8];
norm = vtkMath::Norm(A[0]);
normTemp = vtkMath::Norm(A[1]);
norm = norm > normTemp ? norm : normTemp;
normTemp = vtkMath::Norm(A[2]);
norm = norm > normTemp ? norm : normTemp;
if (fabs(vtkMath::Determinant3x3(A))/(norm*norm*norm) > errorNumber)
{
// it would be better to use the normal of the matrix to test singularity??
vtkMath::LinearSolve3x3(A, b, x);
vtkMath::Multiply3x3(A,x,temp);
// error too high, backup plans
}
else
{
// cheapest point along the edge
this->Mesh->GetPoints()->GetPoint(pointIds[0], pt1);
this->Mesh->GetPoints()->GetPoint(pointIds[1], pt2);
v[0] = pt2[0] - pt1[0];
v[1] = pt2[1] - pt1[1];
v[2] = pt2[2] - pt1[2];
// equation for the edge pt1 + c * v
// attempt least squares fit for c for A*(pt1 + c * v) = b
vtkMath::Multiply3x3(A,v,temp2);
if (vtkMath::Dot(temp2, temp2) > errorNumber)
{
vtkMath::Multiply3x3(A,pt1,temp);
for (i = 0; i < 3; i++)
temp[i] = b[i] - temp[i];
c = vtkMath::Dot(temp2, temp) / vtkMath::Dot(temp2, temp2);
for (i = 0; i < 3; i++)
x[i] = pt1[i]+c*v[i];
}
else
{
// use mid point
// might want to change to best of mid and end points??
for (i = 0; i < 3; i++)
{
x[i] = 0.5*(pt1[i]+pt2[i]);
}
}
}
newPoint[0] = x[0];
newPoint[1] = x[1];
newPoint[2] = x[2];
newPoint[3] = 1;
// Compute the cost
// x'*quad*x
index = this->TempQuad;
for (i = 0; i < 4; i++)
{
cost += (*index++)*newPoint[i]*newPoint[i];
for (j = i +1; j < 4; j++)
{
cost += 2.0*(*index++)*newPoint[i]*newPoint[j];
}
}
return cost;
}
//----------------------------------------------------------------------------
double vtkQuadricDecimation::ComputeCost2(vtkIdType edgeId, double *x)
{
// this function is so ugly because the functionality of converting an QEM
// into a dence matrix was not extracted into a separate function and
// neither was multiplication and some other matrix and vector primatives
static const double errorNumber = 1e-10;
vtkIdType pointIds[2];
double cost = 0.0;
int i, j;
int solveOk;
pointIds[0] = this->EndPoint1List->GetId(edgeId);
pointIds[1] = this->EndPoint2List->GetId(edgeId);
for (i = 0; i < 11 + 4 * this->NumberOfComponents; i++)
{
this->TempQuad[i] = this->ErrorQuadrics[pointIds[0]].Quadric[i] +
this->ErrorQuadrics[pointIds[1]].Quadric[i];
}
// copy the temp quad into TempA
// converting from the sparce matrix format into a dence
this->TempA[0][0] = this->TempQuad[0];
this->TempA[0][1] = this->TempA[1][0] = this->TempQuad[1];
this->TempA[0][2] = this->TempA[2][0] = this->TempQuad[2];
this->TempA[1][1] = this->TempQuad[4];
this->TempA[1][2] = this->TempA[2][1] = this->TempQuad[5];
this->TempA[2][2] = this->TempQuad[7];
this->TempB[0] = -this->TempQuad[3];
this->TempB[1] = -this->TempQuad[6];
this->TempB[2] = -this->TempQuad[8];
for (i = 3; i < 3 + this->NumberOfComponents; i++)
{
this->TempA[0][i] = this->TempA[i][0] = this->TempQuad[11+4*(i-3)];
this->TempA[1][i] = this->TempA[i][1] = this->TempQuad[11+4*(i-3)+1];
this->TempA[2][i] = this->TempA[i][2] = this->TempQuad[11+4*(i-3)+2];
this->TempB[i] = -this->TempQuad[11+4*(i-3)+3];
}
for (i = 3; i < 3 + this->NumberOfComponents; i++)
{
for (j = 3; j < 3 + this->NumberOfComponents; j++)
{
if (i == j)
{
this->TempA[i][j] = this->TempQuad[10];
}
else
{
this->TempA[i][j] = 0;
}
}
}
for (i = 0; i < 3 + this->NumberOfComponents; i++)
{
x[i] = this->TempB[i];
}
// solve A*x = b
// this clobers A
// need to develop a quality of the solution test??
solveOk = vtkMath::SolveLinearSystem(this->TempA, x, 3 + this->NumberOfComponents);
// need to copy back into A
this->TempA[0][0] = this->TempQuad[0];
this->TempA[0][1] = this->TempA[1][0] = this->TempQuad[1];
this->TempA[0][2] = this->TempA[2][0] = this->TempQuad[2];
this->TempA[1][1] = this->TempQuad[4];
this->TempA[1][2] = this->TempA[2][1] = this->TempQuad[5];
this->TempA[2][2] = this->TempQuad[7];
for (i = 3; i < 3 + this->NumberOfComponents; i++)
{
this->TempA[0][i] = this->TempA[i][0] = this->TempQuad[11+4*(i-3)];
this->TempA[1][i] = this->TempA[i][1] = this->TempQuad[11+4*(i-3)+1];
this->TempA[2][i] = this->TempA[i][2] = this->TempQuad[11+4*(i-3)+2];
}
for (i = 3; i < 3 + this->NumberOfComponents; i++)
{
for (j = 3; j < 3 + this->NumberOfComponents; j++)
{
if (i == j)
{
this->TempA[i][j] = this->TempQuad[10];
}
else
{
this->TempA[i][j] = 0;
}
}
}
// check for failure to solve the system
if (!solveOk)
{
// cheapest point along the edge
// this should not frequently occour, so I am using dynamic allocation
double *pt1 = new double [3+this->NumberOfComponents];
double *pt2 = new double [3+this->NumberOfComponents];
double *v = new double [3+this->NumberOfComponents];
double *temp = new double [3+this->NumberOfComponents];
double *temp2 = new double [3+this->NumberOfComponents];
double d = 0;
double c = 0;
this->GetPointAttributeArray(pointIds[0], pt1);
this->GetPointAttributeArray(pointIds[1], pt2);
for (i = 0; i < 3+this->NumberOfComponents; ++i)
{
v[i] = pt2[i] - pt2[i];
}
// equation for the edge pt1 + c * v
// attempt least squares fit for c for A*(pt1 + c * v) = b
// temp2 = A*v
for (i = 0; i < 3 + this->NumberOfComponents; ++i)
{
temp2[i] = 0;
for (j = 0; j < 3 + this->NumberOfComponents; ++j)
{
temp2[i] += this->TempA[i][j]*v[j];
}
}
// c = v dot v
for (i = 0; i < 3 + this->NumberOfComponents; ++i)
{
d += temp2[i]*temp2[i];
}
if ( d > errorNumber)
{
// temp = A*pt1
for (i = 0; i < 3 + this->NumberOfComponents; ++i)
{
temp[i] = 0;
for (j = 0; j < 3 + this->NumberOfComponents; ++j)
{
temp[i] += this->TempA[i][j]*pt1[j];
}
}
for (i = 0; i < 3 + this->NumberOfComponents; i++)
{
temp[i] = this->TempB[i] - temp[i];
}
for (i = 0; i < 3 + this->NumberOfComponents; i++)
{
c += temp2[i]*temp[i];
}
c = c/d;
for (i = 0; i < 3 + this->NumberOfComponents; i++)
{
x[i] = pt1[i]+c*v[i];
}
}
else
{
// use mid point
// might want to change to best of mid and end points??
for (i = 0; i < 3 + this->NumberOfComponents; i++)
{
x[i] = 0.5*(pt1[i]+pt2[i]);
}
}
delete[] pt1;
delete[] pt2;
delete[] v;
delete[] temp;
delete[] temp2;
}
// Compute the cost
// x'*A*x - 2*b*x + d
for (i = 0; i < 3+this->NumberOfComponents; i++)
{
cost += this->TempA[i][i]*x[i]*x[i];
for (j = i+1; j < 3+this->NumberOfComponents; j++)
{
cost += 2.0*this->TempA[i][j]*x[i]*x[j];
}
}
for (i = 0; i < 3+this->NumberOfComponents; i++)
{
cost -= 2.0 * this->TempB[i]*x[i];
}
cost += this->TempQuad[9];
return cost;
}
int vtkQuadricDecimation::CollapseEdge(vtkIdType pt0Id, vtkIdType pt1Id)
{
int j, numDeleted=0;
vtkIdType i, npts, *pts, cellId;
this->Mesh->GetPointCells(pt0Id, this->CollapseCellIds);
for (i = 0; i < this->CollapseCellIds->GetNumberOfIds(); i++)
{
cellId = this->CollapseCellIds->GetId(i);
this->Mesh->GetCellPoints(cellId, npts, pts);
for (j = 0; j < 3; j++)
{
if (pts[j] == pt1Id)
{
this->Mesh->RemoveCellReference(cellId);
this->Mesh->DeleteCell(cellId);
numDeleted++;
}
}
}
this->Mesh->GetPointCells(pt1Id, this->CollapseCellIds);
this->Mesh->ResizeCellList(pt0Id, this->CollapseCellIds->GetNumberOfIds());
for (i=0; i < this->CollapseCellIds->GetNumberOfIds(); i++)
{
cellId = this->CollapseCellIds->GetId(i);
this->Mesh->GetCellPoints(cellId, npts, pts);
// making sure we don't already have the triangle we're about to
// change this one to
if (pts[0] == pt1Id && this->Mesh->IsTriangle(pt0Id, pts[1], pts[2]) ||
(pts[1] == pt1Id && this->Mesh->IsTriangle(pts[0], pt0Id, pts[2])) ||
(pts[2] == pt1Id && this->Mesh->IsTriangle(pts[0], pts[1], pt0Id)))
{
this->Mesh->RemoveCellReference(cellId);
this->Mesh->DeleteCell(cellId);
numDeleted++;
}
else
{
this->Mesh->AddReferenceToCell(pt0Id, cellId);
this->Mesh->ReplaceCellPoint(cellId, pt1Id, pt0Id);
}
}
this->Mesh->DeletePoint(pt1Id);
return numDeleted;
}
// triangle t0, t1, t2 and point x
// determins if t0 and x are on the same side of the plane defined by
// t1 and t2, and parallel to the normal of the triangle
int vtkQuadricDecimation::TrianglePlaneCheck(const double t0[3],
const double t1[3],
const double t2[3],
const double *x) {
double e0[3], e1[3], n[3], e2[3];
double c;
int i;
for (i = 0; i < 3; i++)
{
e0[i] = t2[i] - t1[i];
}
for (i = 0; i < 3; i++)
{
e1[i] = t0[i] - t1[i];
}
// projection of e0 onto e1
c = vtkMath::Dot(e0,e1)/(e0[0]*e0[0]+e0[1]*e0[1]+e0[2]*e0[2]);
for (i = 0; i < 3; i++)
{
n[i] = e1[i] - c*e0[i];
}
for ( i = 0; i < 3; i++)
{
e2[i] = x[i] - t1[i];
}
vtkMath::Normalize(n);
vtkMath::Normalize(e2);
if (vtkMath::Dot(n, e2) > 1e-5)
{
return 1;
}
else
{
return 0;
}
}
int vtkQuadricDecimation::IsGoodPlacement(vtkIdType pt0Id, vtkIdType pt1Id,
const double *x)
{
unsigned short ncells, i;
vtkIdType npts, *pts, ptId, *cells;
double pt1[3], pt2[3], pt3[3];
this->Mesh->GetPointCells(pt0Id, ncells, cells);
for (i = 0; i < ncells; i++) {
this->Mesh->GetCellPoints(cells[i], npts, pts);
// assume triangle
if (pts[0] != pt1Id && pts[1] != pt1Id && pts[2] != pt1Id)
{
for (ptId = 0; ptId < 3; ptId++)
{
if (pts[ptId] == pt0Id)
{
this->Mesh->GetPoint(pts[ptId], pt1);
this->Mesh->GetPoint(pts[(ptId+1)%3], pt2);
this->Mesh->GetPoint(pts[(ptId+2)%3], pt3);
if(!this->TrianglePlaneCheck(pt1, pt2, pt3, x))
{
return 0;
}
}
}
}
}
this->Mesh->GetPointCells(pt1Id, ncells, cells);
for (i = 0; i < ncells; i++)
{
this->Mesh->GetCellPoints(cells[i], npts, pts);
// assume triangle
if (pts[0] != pt0Id && pts[1] != pt0Id && pts[2] != pt0Id)
{
for (ptId = 0; ptId < 3; ptId++)
{
if (pts[ptId] == pt1Id)
{
this->Mesh->GetPoint(pts[ptId], pt1);
this->Mesh->GetPoint(pts[(ptId+1)%3], pt2);
this->Mesh->GetPoint(pts[(ptId+2)%3], pt3);
if(!this->TrianglePlaneCheck(pt1, pt2, pt3, x))
{
return 0;
}
}
}
}
}
return 1;
}
void vtkQuadricDecimation::ComputeNumberOfComponents(void)
{
vtkPointData *pd = this->Mesh->GetPointData();
int i, j;
double range[2], maxRange=0.0;
this->NumberOfComponents = 0;
pd->CopyAllOff();
for (i = 0; i < 6; i++)
{
this->AttributeComponents[i] = 0;
this->AttributeScale[i] = 1.0;
}
// Scalar attributes
if (pd->GetScalars() != NULL && this->ScalarsAttribute)
{
for (j = 0; j < pd->GetScalars()->GetNumberOfComponents(); j++)
{
pd->GetScalars()->GetRange(range, j);
maxRange = (maxRange < (range[1] - range[0]) ?
(range[1] - range[0]) : maxRange);
}
if (maxRange != 0.0)
{
this->NumberOfComponents += pd->GetScalars()->GetNumberOfComponents();
pd->CopyScalarsOn();
this->AttributeScale[0] = this->ScalarsWeight/maxRange;
maxRange = 0.0;
}
vtkDebugMacro("scalars "<< this->NumberOfComponents << " "
<< this->AttributeScale[0]);
}
this->AttributeComponents[0] = this->NumberOfComponents;
// Vector attributes
if (pd->GetVectors() != NULL && this->VectorsAttribute)
{
for (j = 0; j < pd->GetVectors()->GetNumberOfComponents(); j++)
{
pd->GetVectors()->GetRange(range, j);
maxRange = (maxRange < (range[1] - range[0]) ?
(range[1] - range[0]) : maxRange);
}
if (maxRange != 0.0)
{
this->NumberOfComponents += pd->GetVectors()->GetNumberOfComponents();
pd->CopyVectorsOn();
this->AttributeScale[1] = this->VectorsWeight/maxRange;
maxRange = 0.0;
}
vtkDebugMacro("vectors "<< this->NumberOfComponents << " "
<< this->AttributeScale[1]);
}
this->AttributeComponents[1] = this->NumberOfComponents;
// Normals attributes -- normals are assumed normalized
if (pd->GetNormals() != NULL && this->NormalsAttribute)
{
this->NumberOfComponents += 3;
pd->CopyNormalsOn();
this->AttributeScale[2] = 0.5*this->NormalsWeight;
vtkDebugMacro("normals "<< this->NumberOfComponents << " "
<< this->AttributeScale[2]);
}
this->AttributeComponents[2] = this->NumberOfComponents;
// Texture coords attributes
if (pd->GetTCoords() != NULL && this->TCoordsAttribute)
{
for (j = 0; j < pd->GetTCoords()->GetNumberOfComponents(); j++)
{
pd->GetTCoords()->GetRange(range, j);
maxRange = (maxRange < (range[1] - range[0]) ?
(range[1] - range[0]) : maxRange);
}
if (maxRange != 0.0)
{
this->NumberOfComponents += pd->GetTCoords()->GetNumberOfComponents();
pd->CopyTCoordsOn();
this->AttributeScale[3] = this->TCoordsWeight/maxRange;
maxRange = 0.0;
}
vtkDebugMacro("tcoords "<< this->NumberOfComponents << " "
<< this->AttributeScale[3]);
}
this->AttributeComponents[3] = this->NumberOfComponents;
// Tensors attributes
if (pd->GetTensors() != NULL && this->TensorsAttribute)
{
for (j = 0; j < 9; j++)
{
pd->GetTensors()->GetRange(range, j);
maxRange = (maxRange < (range[1] - range[0]) ?
(range[1] - range[0]) : maxRange);
}
if (maxRange != 0.0)
{
this->NumberOfComponents += 9;
pd->CopyTensorsOn();
this->AttributeScale[4] = this->TensorsWeight/maxRange;
}
vtkDebugMacro("tensors "<< this->NumberOfComponents << " "
<< this->AttributeScale[4]);
}
this->AttributeComponents[4] = this->NumberOfComponents;
vtkDebugMacro("Number of components: " << this->NumberOfComponents);
}
//----------------------------------------------------------------------------
void vtkQuadricDecimation::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os,indent);
os << indent << "Target Reduction: " << this->TargetReduction << "\n";
os << indent << "Actual Reduction: " << this->ActualReduction << "\n";
os << indent << "Attribute Error Metric: "
<< (this->AttributeErrorMetric ? "On\n" : "Off\n");
os << indent << "Scalars Attribute: "
<< (this->ScalarsAttribute ? "On\n" : "Off\n");
os << indent << "Vectors Attribute: "
<< (this->VectorsAttribute ? "On\n" : "Off\n");
os << indent << "Normals Attribute: "
<< (this->NormalsAttribute ? "On\n" : "Off\n");
os << indent << "TCoords Attribute: "
<< (this->TCoordsAttribute ? "On\n" : "Off\n");
os << indent << "Tensors Attribute: "
<< (this->TensorsAttribute ? "On\n" : "Off\n");
os << indent << "Scalars Weight: " << this->ScalarsWeight << "\n";
os << indent << "Vectors Weight: " << this->VectorsWeight << "\n";
os << indent << "Normals Weight: " << this->NormalsWeight << "\n";
os << indent << "TCoords Weight: " << this->TCoordsWeight << "\n";
os << indent << "Tensors Weight: " << this->TensorsWeight << "\n";
}