Commit 568b8e05 authored by Colin Myers's avatar Colin Myers Committed by Jeff Baumes
Browse files

ENH: Enhancements to vtkTulipReader and new support for clusters.

1. Fixed previous behaviour whereby properties were assumed to be on
both nodes and edges, leading to empty arrays being added to the
graph when properties are only set for either node or edge.

2. Node and edge pedigree ids are added that retain the id values given in
the input Tulip file.

3. Added double property type.

4. Updated vtkTulipReaderTestProperties to cover 1, 2, and 3 above.

4. Added ability to read clusters.
Clusters (subgraphs) are output as vtkSelectionNodes within the
vtkAnnotationsLayer on output port 1. Nested hierarchies are
flattened.

5. Added a filter to convert a vtkPolygon to a polygon of the convex hull
of the points in the input. The filter ensures visibility by adding extra
points and/or scaling the hull up. Static convenience methods provide a
similar function, with vtkPoints as the input/output. Adding a vtkRenderer
allows a minimum size in pixels to be specified.

6. Added a filter to display graph annotations.
The new filter takes a vtkAnnotations layers containing selections
of vertices from a vtkGraph. Each node of the selection is
represented by it's convex hulli/bounding rectangle.

6. Added a test (TestTulipReaderClusters) that uses new
functionality in vtkTulipReader to read in and display
clusters/annotations, covering 4, 5 and 6 above.

7. Added a test (TestConvexHull2D) to exercise the static methods
in vtkConvexHull2D.

Change-Id: I424a0a02e9ac82fdce78d931819f1dffce25796e
parent 7dc8ad9f
......@@ -50,6 +50,7 @@ vtkConnectivityFilter.cxx
vtkContourFilter.cxx
vtkContourGrid.cxx
vtkConvertSelection.cxx
vtkConvexHull2D.cxx
vtkCubeSource.cxx
vtkCursor2D.cxx
vtkCursor3D.cxx
......@@ -113,6 +114,7 @@ vtkGlyph2D.cxx
vtkGlyph3D.cxx
vtkGlyphSource2D.cxx
vtkGradientFilter.cxx
vtkGraphAnnotationLayersFilter.cxx
vtkGraphGeodesicPath.cxx
vtkGraphLayoutFilter.cxx
vtkGraphToPoints.cxx
......
......@@ -19,6 +19,7 @@ IF (VTK_USE_RENDERING AND VTK_USE_DISPLAY)
TestDensifyPolyData.cxx
TestClipHyperOctree.cxx
TestConvertSelection.cxx
TestConvexHull2D.cxx
TestDelaunay2D.cxx
TestExtraction.cxx
TestExtractSelection.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Module: TestLineSource.cxx
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 "vtkConvexHull2D.h"
#include "vtkPoints.h"
#include "vtkSmartPointer.h"
#include "vtkTestUtilities.h"
#include "vtkMath.h"
#include <vtkstd/limits>
#ifndef ABS
#define ABS(x) ((x) < 0 ? -(x) : (x))
#endif
template<class A>
bool fuzzyCompare1Dweak(A a, A b)
{
return ABS(a - b) < 0.000001;
}
template<class A>
bool fuzzyCompare2Dweak(A a[2], A b[2])
{
return fuzzyCompare1Dweak(a[0], b[0]) &&
fuzzyCompare1Dweak(a[1], b[1]);
}
int TestConvexHull2D(int argc, char* argv[])
{
(void)argc;
(void)argv;
double expectedPoint[3];
double retrievedPoint[3];
//---------------------------------------------------------------------------
// A single point - expected output is a 2x2 square centred on the origin
vtkSmartPointer<vtkPoints> inPoints = vtkSmartPointer<vtkPoints>::New();
vtkSmartPointer<vtkPoints> outPoints = vtkSmartPointer<vtkPoints>::New();
inPoints->SetNumberOfPoints(1);
inPoints->SetPoint(0, 0.0, 0.0, 0.0);
vtkConvexHull2D::CalculateConvexHull(inPoints, outPoints, 2.0);
if (!outPoints->GetNumberOfPoints() == 4)
{
std::cerr << "Error: Single point - expected 4 output points but got " <<
outPoints->GetNumberOfPoints() << "." << std::endl;
return EXIT_FAILURE;
}
outPoints->GetPoint(0, retrievedPoint);
expectedPoint[0] = -1.0; expectedPoint[1] = -1.0; expectedPoint[2] = 0.0;
if (!fuzzyCompare2Dweak(expectedPoint, retrievedPoint))
{
std::cerr << "Error: Single point - unexpected output value for point 0." << std::endl;
return EXIT_FAILURE;
}
outPoints->GetPoint(1, retrievedPoint);
expectedPoint[0] = 1.0; expectedPoint[1] = -1.0; expectedPoint[2] = 0.0;
if (!fuzzyCompare2Dweak(expectedPoint, retrievedPoint))
{
std::cerr << "Error: Single point - unexpected output value for point 1." << std::endl;
return EXIT_FAILURE;
}
outPoints->GetPoint(2, retrievedPoint);
expectedPoint[0] = 1.0; expectedPoint[1] = 1.0; expectedPoint[2] = 0.0;
if (!fuzzyCompare2Dweak(expectedPoint, retrievedPoint))
{
std::cerr << "Error: Single point - unexpected output value for point 2." << std::endl;
return EXIT_FAILURE;
}
outPoints->GetPoint(3, retrievedPoint);
expectedPoint[0] = -1.0; expectedPoint[1] = 1.0; expectedPoint[2] = 0.0;
if (!fuzzyCompare2Dweak(expectedPoint, retrievedPoint))
{
std::cerr << "Error: Single point - unexpected output value for point 3." << std::endl;
return EXIT_FAILURE;
}
//---------------------------------------------------------------------------
// Two points in a line - expected output is a 4x2 rectangle centred on the origin
inPoints = vtkSmartPointer<vtkPoints>::New();
outPoints = vtkSmartPointer<vtkPoints>::New();
inPoints->SetNumberOfPoints(2);
inPoints->SetPoint(0, -2.0, 0.0, 0.0);
inPoints->SetPoint(1, 2.0, 0.0, 0.0);
vtkConvexHull2D::CalculateConvexHull(inPoints, outPoints, 2.0);
if (!outPoints->GetNumberOfPoints() == 4)
{
std::cerr << "Error: Two points in a line - expected 4 output points but got " <<
outPoints->GetNumberOfPoints() << "." << std::endl;
return EXIT_FAILURE;
}
outPoints->GetPoint(0, retrievedPoint);
expectedPoint[0] = -2.0; expectedPoint[1] = -1.0; expectedPoint[2] = 0.0;
if (!fuzzyCompare2Dweak(expectedPoint, retrievedPoint))
{
std::cerr << "Error: Two points in a line - unexpected output value for point 0." << std::endl;
return EXIT_FAILURE;
}
outPoints->GetPoint(1, retrievedPoint);
expectedPoint[0] = 2.0; expectedPoint[1] = -1.0; expectedPoint[2] = 0.0;
if (!fuzzyCompare2Dweak(expectedPoint, retrievedPoint))
{
std::cerr << "Error: Two points in a line - unexpected output value for point 1." << std::endl;
return EXIT_FAILURE;
}
outPoints->GetPoint(2, retrievedPoint);
expectedPoint[0] = 2.0; expectedPoint[1] = 1.0; expectedPoint[2] = 0.0;
if (!fuzzyCompare2Dweak(expectedPoint, retrievedPoint))
{
std::cerr << "Error: Two points in a line - unexpected output value for point 2." << std::endl;
return EXIT_FAILURE;
}
outPoints->GetPoint(3, retrievedPoint);
expectedPoint[0] = -2.0; expectedPoint[1] = 1.0; expectedPoint[2] = 0.0;
if (!fuzzyCompare2Dweak(expectedPoint, retrievedPoint))
{
std::cerr << "Error: Two points in a line - unexpected output value for point 3." << std::endl;
return EXIT_FAILURE;
}
//---------------------------------------------------------------------------
// Five points - expected output is a 2x2 rotated rectangle centred on the origin
inPoints = vtkSmartPointer<vtkPoints>::New();
outPoints = vtkSmartPointer<vtkPoints>::New();
inPoints->SetNumberOfPoints(5);
inPoints->SetPoint(0, 1.9, 2.0, 0.0);
inPoints->SetPoint(1, 2.1, 2.0, 0.0);
inPoints->SetPoint(2, 2.0, 2.1, 0.0);
inPoints->SetPoint(3, 2.0, 1.9, 0.0);
inPoints->SetPoint(4, 2.0, 2.0, 0.0);
vtkConvexHull2D::CalculateConvexHull(inPoints, outPoints, 2.0);
if (!outPoints->GetNumberOfPoints() == 4)
{
std::cerr << "Error: Five points - expected 4 output points but got " <<
outPoints->GetNumberOfPoints() << "." << std::endl;
return EXIT_FAILURE;
}
outPoints->GetPoint(0, retrievedPoint);
expectedPoint[0] = 2.0; expectedPoint[1] = 1.0; expectedPoint[2] = 0.0;
if (!fuzzyCompare2Dweak(expectedPoint, retrievedPoint))
{
std::cerr << "Error: Five points - unexpected output value for point 0." << std::endl;
return EXIT_FAILURE;
}
outPoints->GetPoint(1, retrievedPoint);
expectedPoint[0] = 3.0; expectedPoint[1] = 2.0; expectedPoint[2] = 0.0;
if (!fuzzyCompare2Dweak(expectedPoint, retrievedPoint))
{
std::cerr << "Error: Five points - unexpected output value for point 1." << std::endl;
return EXIT_FAILURE;
}
outPoints->GetPoint(2, retrievedPoint);
expectedPoint[0] = 2.0; expectedPoint[1] = 3.0; expectedPoint[2] = 0.0;
if (!fuzzyCompare2Dweak(expectedPoint, retrievedPoint))
{
std::cerr << "Error: Five points - unexpected output value for point 2." << std::endl;
return EXIT_FAILURE;
}
outPoints->GetPoint(3, retrievedPoint);
expectedPoint[0] = 1.0; expectedPoint[1] = 2.0; expectedPoint[2] = 0.0;
if (!fuzzyCompare2Dweak(expectedPoint, retrievedPoint))
{
std::cerr << "Error: Five points - unexpected output value for point 3." << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkConvexHull2D.cxx
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 "vtkConvexHull2D.h"
#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkCoordinate.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkPointsProjectedHull.h"
#include "vtkPolygon.h"
#include "vtkPolyLine.h"
#include "vtkRenderer.h"
#include "vtkSmartPointer.h"
#include "vtkTransform.h"
#include "vtkTransformPolyDataFilter.h"
vtkStandardNewMacro(vtkConvexHull2D);
//-----------------------------------------------------------------------------
vtkConvexHull2D::vtkConvexHull2D()
{
this->SetNumberOfOutputPorts(2);
this->ScaleFactor = 1.0;
this->Outline = false;
this->MinHullSizeInWorld = 1.0;
this->MinHullSizeInDisplay = 10;
this->HullShape = vtkConvexHull2D::ConvexHull;
this->Coordinate = vtkSmartPointer<vtkCoordinate>::New();
this->Transform = vtkSmartPointer<vtkTransform>::New();
this->OutputTransform = vtkSmartPointer<vtkTransform>::New();
this->OutputTransformFilter = vtkSmartPointer<vtkTransformPolyDataFilter>::New();
this->OutputTransformFilter->SetTransform(this->OutputTransform);
this->OutlineSource = vtkSmartPointer<vtkPolyLine>::New();
this->HullSource = vtkSmartPointer<vtkPolygon>::New();
}
//-----------------------------------------------------------------------------
vtkConvexHull2D::~vtkConvexHull2D()
{
this->SetRenderer(0);
}
//-----------------------------------------------------------------------------
void vtkConvexHull2D::CalculateBoundingRectangle(vtkPoints* inPoints,
vtkPoints* outPoints, double minimumHullSize)
{
minimumHullSize /= 2.0;
inPoints->ComputeBounds();
double bounds[6];
inPoints->GetBounds(bounds);
double xDeficit = minimumHullSize - (bounds[1] - bounds[0]);
if (xDeficit > 0.0)
{
bounds[0] -= xDeficit;
bounds[1] += xDeficit;
}
double yDeficit = minimumHullSize - (bounds[3] - bounds[2]);
if (yDeficit > 0.0)
{
bounds[2] -= yDeficit;
bounds[3] += yDeficit;
}
outPoints->SetNumberOfPoints(4);
outPoints->SetPoint(0, bounds[0], bounds[2], 0.0);
outPoints->SetPoint(1, bounds[1], bounds[2], 0.0);
outPoints->SetPoint(2, bounds[1], bounds[3], 0.0);
outPoints->SetPoint(3, bounds[0], bounds[3], 0.0);
}
//-----------------------------------------------------------------------------
void vtkConvexHull2D::CalculateConvexHull(vtkPoints* inPoints,
vtkPoints* outPoints, double minimumHullSize)
{
if (inPoints->GetNumberOfPoints() == 1 ||
inPoints->GetNumberOfPoints() == 2)
{
vtkConvexHull2D::CalculateBoundingRectangle(inPoints, outPoints,
minimumHullSize);
}
else if (inPoints->GetNumberOfPoints() >= 3)
{
vtkPointsProjectedHull* ppHull = vtkPointsProjectedHull::New();
ppHull->ShallowCopy(inPoints);
int numHullPoints = ppHull->GetSizeCCWHullZ();
double pts[2 * numHullPoints];
ppHull->GetCCWHullZ(pts, numHullPoints);
vtkPoints* hullPoints = vtkPoints::New();
hullPoints->SetNumberOfPoints(numHullPoints);
for (vtkIdType i = 0; i < numHullPoints; ++i)
{
hullPoints->SetPoint(i, pts[2 * i], pts[2 * i + 1], 0.0);
}
ppHull->Delete();
if (numHullPoints < 3)
{
vtkConvexHull2D::CalculateBoundingRectangle(hullPoints, outPoints,
minimumHullSize);
return;
}
double bounds[6];
hullPoints->GetBounds(bounds);
double xScale = std::max(1.0, minimumHullSize / (bounds[1] - bounds[0]));
double yScale = std::max(1.0, minimumHullSize / (bounds[3] - bounds[2]));
if (xScale > 1.0 || yScale > 1.0)
{
double scale[3] = {xScale, yScale, 1.0};
double translate[3] = {
(bounds[0] + (bounds[1] - bounds[0]) / 2.0),
(bounds[2] + (bounds[3] - bounds[2]) / 2.0), 0.0};
vtkTransform* transform = vtkTransform::New();
transform->Translate(translate);
transform->Scale(scale);
transform->Translate(-translate[0], -translate[1], -translate[2]);
transform->TransformPoints(hullPoints, outPoints);
transform->Delete();
}
else
{
outPoints->ShallowCopy(hullPoints);
}
hullPoints->Delete();
}
}
//-----------------------------------------------------------------------------
void vtkConvexHull2D::ResizeHullToMinimumInDisplay(vtkPolyData* hullPolyData)
{
if (this->Renderer && this->Renderer->IsActiveCameraCreated())
{
double bounds[6];
hullPolyData->ComputeBounds();
hullPolyData->GetBounds(bounds);
double leftBottom[2];
double rightTop[2];
double* coord;
this->Coordinate->SetCoordinateSystemToWorld();
this->Coordinate->SetValue(bounds[0], bounds[2], 0.0);
coord = this->Coordinate->GetComputedDoubleDisplayValue(this->Renderer);
leftBottom[0] = coord[0];
leftBottom[1] = coord[1];
this->Coordinate->SetValue(bounds[1], bounds[3], 0.0);
coord = this->Coordinate->GetComputedDoubleDisplayValue(this->Renderer);
rightTop[0] = coord[0];
rightTop[1] = coord[1];
double currentDisplaySize[2] = {rightTop[0] - leftBottom[0],
rightTop[1] - leftBottom[1]};
if (currentDisplaySize[0] == 0.0 || currentDisplaySize[1] == 0.0)
{
vtkWarningMacro(<< "Can not scale a hull with zero display area.")
return;
}
if (currentDisplaySize[0] < this->MinHullSizeInDisplay ||
currentDisplaySize[1] < this->MinHullSizeInDisplay)
{
double scaleFx = std::max(1.0, this->MinHullSizeInDisplay
/ currentDisplaySize[0]);
double scaleFy = std::max(1.0, this->MinHullSizeInDisplay
/ currentDisplaySize[1]);
double scale[3] = {scaleFx, scaleFy, 1.0};
double translate[3] = {
(bounds[0] + (bounds[1] - bounds[0]) / 2.0),
(bounds[2] + (bounds[3] - bounds[2]) / 2.0), 0.0};
this->Transform->Identity();
this->Transform->Translate(translate);
this->Transform->Scale(scale);
this->Transform->Translate(-translate[0], -translate[1], -translate[2]);
vtkPoints* outPoints = vtkPoints::New();
this->Transform->TransformPoints(hullPolyData->GetPoints(), outPoints);
hullPolyData->SetPoints(outPoints);
outPoints->Delete();
}
}
}
//-----------------------------------------------------------------------------
void vtkConvexHull2D::SetRenderer(vtkRenderer* renderer)
{
this->Renderer = renderer;
this->Modified();
}
//-----------------------------------------------------------------------------
vtkRenderer* vtkConvexHull2D::GetRenderer()
{
return this->Renderer;
}
//-----------------------------------------------------------------------------
unsigned long vtkConvexHull2D::GetMTime()
{
if (this->Renderer)
{
return this->Renderer->GetMTime();
}
else
{
return this->MTime;
}
}
//-----------------------------------------------------------------------------
int vtkConvexHull2D::RequestData(vtkInformation *vtkNotUsed(request),
vtkInformationVector **inputVector, vtkInformationVector *outputVector)
{
// Get the input and output.
vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
vtkPolyData* input = vtkPolyData::SafeDownCast(
inInfo->Get(vtkDataObject::DATA_OBJECT()));
vtkPoints* inputPoints = input->GetPoints();
vtkInformation *outInfo0 = outputVector->GetInformationObject(0);
vtkInformation *outInfo1 = outputVector->GetInformationObject(1);
vtkPolyData *outputHull = vtkPolyData::SafeDownCast(outInfo0->Get(
vtkDataObject::DATA_OBJECT()));
vtkPolyData *outputOutline = vtkPolyData::SafeDownCast(outInfo1->Get(
vtkDataObject::DATA_OBJECT()));
// Create filled polygon
vtkPoints* hullPoints = vtkPoints::New();
switch (this->HullShape)
{
case vtkConvexHull2D::BoundingRectangle:
this->CalculateBoundingRectangle(inputPoints, hullPoints,
this->MinHullSizeInWorld);
break;
default: // vtkConvexHull2D::ConvexHull
this->CalculateConvexHull(inputPoints, hullPoints,
this->MinHullSizeInWorld);
break;
}
vtkIdType numHullPoints = hullPoints->GetNumberOfPoints();
vtkIdType* hullPts = new vtkIdType[numHullPoints];
for (int i = 0; i < numHullPoints; ++i)
{
hullPts[i] = i;
}
this->HullSource->Initialize(numHullPoints, hullPts, hullPoints);
delete [] hullPts;
vtkCellArray* hullCells = vtkCellArray::New();
hullCells->InsertNextCell(this->HullSource);
vtkSmartPointer<vtkPolyData> hullPolyData = vtkSmartPointer<vtkPolyData>::New();
hullPolyData->SetPoints(hullPoints);
hullPolyData->SetPolys(hullCells);
hullPoints->Delete();
hullCells->Delete();
// Adjust for the scale-factor
double* centre = hullPolyData->GetCenter();
this->OutputTransform->Identity();
this->OutputTransform->Translate(centre);
this->OutputTransform->Scale(this->ScaleFactor, this->ScaleFactor, this->ScaleFactor);
this->OutputTransform->Translate(-centre[0], -centre[1], -centre[2]);
this->OutputTransformFilter->SetInput(hullPolyData);
this->OutputTransformFilter->Update();
hullPolyData = this->OutputTransformFilter->GetOutput();
// Account for current camera zoom level
this->ResizeHullToMinimumInDisplay(hullPolyData);
// Copy hull to output
outputHull->ShallowCopy(hullPolyData);
if (this->Outline)
{
vtkIdType numOutlinePoints = outputHull->GetNumberOfPoints();
vtkIdType outlinePts[numOutlinePoints + 1];
for (int i = 0; i < numOutlinePoints; ++i)
{
outlinePts[i] = i;
}
outlinePts[numOutlinePoints] = outlinePts[0];
this->OutlineSource->Initialize(numOutlinePoints + 1, outlinePts, outputHull->GetPoints());
vtkSmartPointer<vtkPolyData> outlinePolyData =
vtkSmartPointer<vtkPolyData>::New();
vtkCellArray* outlineCells = vtkCellArray::New();
outlineCells->InsertNextCell(this->OutlineSource);
outlinePolyData->SetPoints(outputHull->GetPoints());
outlinePolyData->SetLines(outlineCells);
outlineCells->Delete();
// Copy outline to output
outputOutline->ShallowCopy(outlinePolyData);
}
return 1;
}
//-----------------------------------------------------------------------------
void vtkConvexHull2D::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "ScaleFactor: " << this->ScaleFactor << "\n";
os << indent << "Outline: " << (this->Outline ? "On" : "Off") << "\n";
os << indent << "HullShape: ";
switch (this->HullShape)
{
case ConvexHull:
os << "ConvexHull\n";
break;
case BoundingRectangle:
os << "BoundingRectangle\n";
break;
default:
os << "Unknown\n";
break;
}
os << indent << "MinHullSizeInDisplay: " << this->MinHullSizeInDisplay << "\n";
os << indent << "MinHullSizeInWorld: " << this->MinHullSizeInWorld << "\n";
os << indent << "Renderer: ";
if (this->Renderer)
{
os << endl;
this->Renderer->PrintSelf(os, indent.GetNextIndent());
}
else
{
os << "(none)" << endl;
}
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkConvexHull2D.h
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.
=========================================================================*/
// .NAME vtkConvexHull2D - Produce filled convex hulls around a set of points.
//
// .SECTION Description
// Produces a vtkPolyData comprised of a filled polygon of the convex hull
// of the input points. You may alternatively choose to output a bounding
// rectangle. Static methods are provided that calculate a (counter-clockwise)
// hull based on a set of input points.
//
// To help maintain the property of <i>guaranteed visibility</i> hulls may be
// artificially scaled by setting MinHullSizeInWorld. This is particularly
// helpful in the case that there are only one or two points as it avoids
// producing a degenerate polygon. This setting is also available as an
// argument to the static methods.
//
// Setting a vtkRenderer on the filter enables the possibility to set
// MinHullSizeInDisplay to the desired number of display pixels to cover in
// each of the x- and y-dimensions.
//
// Setting OutlineOn() additionally produces an outline of the hull on output
// port 1.
//
// .SECTION Note
// This filter operates in the x,y-plane and as such works best with an