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.
1378 lines
42 KiB
1378 lines
42 KiB
2 years ago
|
/*=========================================================================
|
||
|
|
||
|
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";
|
||
|
}
|