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.

299 lines
8.4 KiB

2 years ago
/*=========================================================================
Program: Visualization Toolkit
Module: $RCSfile: vtkGraphLayoutFilter.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 "vtkGraphLayoutFilter.h"
#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkMath.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkPolyData.h"
vtkCxxRevisionMacro(vtkGraphLayoutFilter, "$Revision: 1.14 $");
vtkStandardNewMacro(vtkGraphLayoutFilter);
vtkGraphLayoutFilter::vtkGraphLayoutFilter()
{
this->GraphBounds[0] = this->GraphBounds[2] = this->GraphBounds[4] = -0.5;
this->GraphBounds[1] = this->GraphBounds[3] = this->GraphBounds[5] = 0.5;
this->MaxNumberOfIterations = 50;
this->CoolDownRate = 10.0;
this->AutomaticBoundsComputation = 1;
this->ThreeDimensionalLayout = 1;
}
// A vertex contains a position and a displacement.
typedef struct _vtkLayoutVertex
{
double x[3];
double d[3];
} vtkLayoutVertex;
// An edge consists of two vertices joined together.
// This struct acts as a "pointer" to those two vertices.
typedef struct _vtkLayoutEdge
{
int t;
int u;
} vtkLayoutEdge;
// Cool-down function.
static inline double CoolDown(double t, double r)
{
return t-(t/r);
}
static inline double forceAttract(double x, double k)
{
return (x * x) / k;
}
static inline double forceRepulse(double x, double k)
{
if (x != 0.0)
{
return k * k / x;
}
else
{
return VTK_DOUBLE_MAX;
}
}
int vtkGraphLayoutFilter::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()));
vtkPoints *pts = input->GetPoints();
vtkCellArray *lines = input->GetLines();
int numLines = lines->GetNumberOfCells(); //Number of lines/edges.
int numPts = input->GetNumberOfPoints(); //Number of points/vertices.
double diff[3], len; //The difference vector.
int i, j, l; //Iteration variables.
vtkIdType npts = 0;
vtkIdType *cellPts = 0;
double fa, fr, minimum;
vtkDebugMacro(<<"Drawing graph");
if ( numPts <= 0 || numLines <= 0)
{
vtkErrorMacro(<<"No input");
return 1;
}
// Generate bounds automatically if necessary. It's the same
// as the input bounds.
if ( this->AutomaticBoundsComputation )
{
pts->GetBounds(this->GraphBounds);
}
for (i=0; i<3; i++)
{
if ( this->GraphBounds[2*i+1] <= this->GraphBounds[2*i] )
{
this->GraphBounds[2*i+1] = this->GraphBounds[2*i] + 1;
}
}
// Allocate memory for structures. Break polylines into line segments.
numLines=0;
for (i=0, lines->InitTraversal(); lines->GetNextCell(npts, cellPts); i++)
{
numLines += npts - 1;
}
vtkLayoutVertex *v = new vtkLayoutVertex [numPts];
vtkLayoutEdge *e = new vtkLayoutEdge [numLines];
// Get the points, either x,y,0 or x,y,z
for (i=0; i<numPts; i++)
{
pts->GetPoint(i,v[i].x);
if ( ! this->ThreeDimensionalLayout )
{
v[i].x[2] = 0;
}
}
// Get the edges
numLines=0;
for (i=0, lines->InitTraversal(); lines->GetNextCell(npts, cellPts); i++)
{
for (j=0; j<npts-1; j++)
{
e[numLines].t = cellPts[j];
e[numLines++].u = cellPts[j+1];
}
}
// More variable definitions:
double volume = (this->GraphBounds[1] - this->GraphBounds[0]) *
(this->GraphBounds[3] - this->GraphBounds[2]) *
(this->GraphBounds[5] - this->GraphBounds[4]);
double temp = sqrt( (this->GraphBounds[1]-this->GraphBounds[0])*
(this->GraphBounds[1]-this->GraphBounds[0]) +
(this->GraphBounds[3]-this->GraphBounds[2])*
(this->GraphBounds[3]-this->GraphBounds[2]) +
(this->GraphBounds[5]-this->GraphBounds[4])*
(this->GraphBounds[5]-this->GraphBounds[4]) );
// The optimal distance between vertices.
double k = pow((double)volume/numPts,0.33333);
// Begin iterations.
double norm;
for(i=0; i<this->MaxNumberOfIterations; i++)
{
// Calculate the repulsive forces.
for(j=0; j<numPts; j++)
{
v[j].d[0] = 0.0;
v[j].d[1] = 0.0;
v[j].d[2] = 0.0;
for(l=0; l<numPts; l++)
{
if (j != l)
{
diff[0] = v[j].x[0] - v[l].x[0];
diff[1] = v[j].x[1] - v[l].x[1];
diff[2] = v[j].x[2] - v[l].x[2];
norm = vtkMath::Normalize(diff);
fr = forceRepulse(norm,k);
v[j].d[0] += diff[0] * fr;
v[j].d[1] += diff[1] * fr;
v[j].d[2] += diff[2] * fr;
}
}
}
// Calculate the attractive forces.
for (j=0; j<numLines; j++)
{
diff[0] = v[e[j].u].x[0] - v[e[j].t].x[0];
diff[1] = v[e[j].u].x[1] - v[e[j].t].x[1];
diff[2] = v[e[j].u].x[2] - v[e[j].t].x[2];
norm = vtkMath::Normalize(diff);
fa = forceAttract(norm,k);
v[e[j].u].d[0] -= diff[0] * fa;
v[e[j].u].d[1] -= diff[1] * fa;
v[e[j].u].d[2] -= diff[2] * fa;
v[e[j].t].d[0] += diff[0] * fa;
v[e[j].t].d[1] += diff[1] * fa;
v[e[j].t].d[2] += diff[2] * fa;
}
// Combine the forces for a new configuration
for (j=0; j<numPts; j++)
{
norm = vtkMath::Normalize(v[j].d);
minimum = (norm < temp ? norm : temp);
v[j].x[0] += v[j].d[0] * minimum;
v[j].x[1] += v[j].d[1] * minimum;
v[j].x[2] += v[j].d[2] * minimum;
}
// Reduce temperature as layout approaches a better configuration.
temp = CoolDown(temp, this->CoolDownRate);
}
// Get the bounds of the graph and scale and translate to
// bring them within the bounds specified.
vtkPoints *newPts = vtkPoints::New();
newPts->SetNumberOfPoints(numPts);
for (i=0; i<numPts; i++)
{
newPts->SetPoint(i,v[i].x);
}
double bounds[6], sf[3], x[3], xNew[3];
double center[3], graphCenter[3];
newPts->GetBounds(bounds);
for (i=0; i<3; i++)
{
if ( (len=(bounds[2*i+1] - bounds[2*i])) == 0.0 )
{
len = 1.0;
}
sf[i] = (this->GraphBounds[2*i+1] - this->GraphBounds[2*i]) / len;
center[i] = (bounds[2*i+1] + bounds[2*i])/2.0;
graphCenter[i] = (this->GraphBounds[2*i+1] + this->GraphBounds[2*i])/2.0;
}
double scale = sf[0];
scale = (scale < sf[1] ? scale : sf[1]);
scale = (scale < sf[2] ? scale : sf[2]);
for (i=0; i<numPts; i++)
{
newPts->GetPoint(i, x);
for (j=0; j<3; j++)
{
xNew[j] = graphCenter[j] + scale*(x[j]-center[j]);
}
newPts->SetPoint(i,xNew);
}
// Send the data to output.
output->SetPoints(newPts);
output->SetLines(lines);
output->GetPointData()->PassData(input->GetPointData());
output->GetCellData()->PassData(input->GetCellData());
// Clean up.
newPts->Delete();
delete [] v;
delete [] e;
return 1;
}
void vtkGraphLayoutFilter::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os,indent);
os << indent << "AutomaticBoundsComputation: "
<< (this->AutomaticBoundsComputation ? "On\n" : "Off\n");
os << indent << "GraphBounds: \n";
os << indent << " Xmin,Xmax: (" << this->GraphBounds[0] << ", "
<< this->GraphBounds[1] << ")\n";
os << indent << " Ymin,Ymax: (" << this->GraphBounds[2] << ", "
<< this->GraphBounds[3] << ")\n";
os << indent << " Zmin,Zmax: (" << this->GraphBounds[4] << ", "
<< this->GraphBounds[5] << ")\n";
os << indent << "MaxNumberOfIterations: "
<< this->MaxNumberOfIterations << endl;
os << indent << "CoolDownRate: "
<< this->CoolDownRate << endl;
os << indent << "Three Dimensional Layout: "
<< (this->ThreeDimensionalLayout ? "On\n" : "Off\n");
}