Commit 6e8750d6 authored by Cory Quammen's avatar Cory Quammen

ENH: Added some useful member functions to vtkRect<T>

Added methods to:

- expand a rect to contain a point

- expand a rect to contain another rect

- checks whether another rect intersects with this one

Added a test for these and existing member functions and constructors.

Added Min and Max static functions to vtkMath and tests for them.
Co-Authored-By: David Gobbi's avatarDavid Gobbi <david.gobbi@gmail.com>
parent 06ad5dec
......@@ -381,6 +381,36 @@ int TestMath(int,char *[])
}
}
// test Min
int iMin = 0;
int iMax = 1;
if (iMin != vtkMath::Min(iMin, iMax))
{
vtkGenericWarningMacro("Min(" << iMin << ", " << iMax << " != " << iMin);
return 1;
}
double dMin = 3.0;
double dMax = 4.1;
if (dMin != vtkMath::Min(dMin, dMax))
{
vtkGenericWarningMacro("Min(" << dMin << ", " << dMax << " != " << dMin);
return 1;
}
// test Max
if (iMax != vtkMath::Max(iMin, iMax))
{
vtkGenericWarningMacro("Max(" << iMin << ", " << iMax << " != " << iMax);
return 1;
}
if (dMax != vtkMath::Max(dMin, dMax))
{
vtkGenericWarningMacro("Max(" << dMin << ", " << dMax << " != " << dMax);
return 1;
}
// test is-power-of-two
const static vtkTypeUInt64 isPowerOfTwoInputs[16] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 255, 256, 257,
......
......@@ -115,6 +115,16 @@ public:
// If x is zero, then the return value will be zero.
static int CeilLog2(vtkTypeUInt64 x);
// Description:
// Returns the minimum of the two arguments provided.
template<class T>
static T Min(const T & a, const T & b);
// Description:
// Returns the maximum of the two arugments provided.
template<class T>
static T Max(const T & a, const T & b);
// Description:
// Returns true if integer is a power of two.
static bool IsPowerOfTwo(vtkTypeUInt64 x);
......@@ -1021,6 +1031,20 @@ inline int vtkMath::Ceil(double x)
return i + ( i < x );
}
//----------------------------------------------------------------------------
template<class T>
inline T vtkMath::Min(const T & a, const T & b)
{
return (a < b ? a : b);
}
//----------------------------------------------------------------------------
template<class T>
inline T vtkMath::Max(const T & a, const T & b)
{
return (a > b ? a : b);
}
//----------------------------------------------------------------------------
inline float vtkMath::Normalize(float x[3])
{
......
......@@ -29,6 +29,7 @@ vtk_add_test_cxx(${vtk-module}CxxTests tests
TestPolyhedron0.cxx
TestPolyhedron1.cxx
TestQuadraticPolygon.cxx
TestRect.cxx
TestSelectionSubtract.cxx
TestTable.cxx
TestTreeBFSIterator.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Module: TestRect.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 "vtkRect.h"
namespace {
//----------------------------------------------------------------------------
template<class T>
int TestAddPoint(vtkRect<T> & expandRect,
T x, T y,
const vtkRect<T> & expected)
{
int returnValue = 0;
std::cout << "Adding point (" << x << ", " << y << ") to rect "
<< expandRect << " ... ";
expandRect.AddPoint(x, y);
if (expandRect.GetX() != expected.GetX())
{
returnValue = EXIT_FAILURE;
std::cout << "AddPoint()/GetX() ";
}
if (expandRect.GetY() != expected.GetY())
{
returnValue = EXIT_FAILURE;
std::cout << "AddPoint()/GetY() ";
}
if (expandRect.GetWidth() != expected.GetWidth())
{
returnValue = EXIT_FAILURE;
std::cout << "AddPoint()/GetWidth() ";
}
if (expandRect.GetHeight() != expected.GetHeight())
{
returnValue = EXIT_FAILURE;
std::cout << "AddPoint()/GetHeight() ";
}
if (returnValue != EXIT_SUCCESS)
{
std::cout << "failed. Expected " << expected << ", got "
<< expandRect << "." << std::endl;
}
else
{
std::cout << "passed." << std::endl;
}
return returnValue;
}
//----------------------------------------------------------------------------
template<class T>
int TestAddRect(vtkRect<T> & expandRect,
vtkRect<T> & addRect,
const vtkRect<T> & expected)
{
int returnValue = 0;
std::cout << "Adding rect " << addRect << " to " << expandRect << " ... ";
expandRect.AddRect(addRect);
if (expandRect.GetX() != expected.GetX())
{
returnValue = EXIT_FAILURE;
std::cout << "AddRect()/GetX() ";
}
if (expandRect.GetY() != expected.GetY())
{
returnValue = EXIT_FAILURE;
std::cout << "AddRect()/GetY() ";
}
if (expandRect.GetWidth() != expected.GetWidth())
{
returnValue = EXIT_FAILURE;
std::cout << "AddRect()/GetWidth() ";
}
if (expandRect.GetHeight() != expected.GetHeight())
{
returnValue = EXIT_FAILURE;
std::cout << "AddRect()/GetHeight() ";
}
if (returnValue != EXIT_SUCCESS)
{
std::cout << "failed. Expected " << expected << ", got "
<< expandRect << "." << std::endl;
}
else
{
std::cout << "passed." << std::endl;
}
return returnValue;
}
} // end anonymous namespace
//----------------------------------------------------------------------------
int TestRect(int, char *[])
{
int result = 0;
// Test constructor/getter agreement ---------------------------------------
vtkRectf rectf(2.0f, 3.0f, 4.0f, 5.0f);
if (rectf.GetX() != 2.0f)
{
std::cout << "GetX() failed\n";
std::cout << rectf << std::endl;
return EXIT_FAILURE;
}
if (rectf.GetY() != 3.0f)
{
std::cout << "GetY() failed\n";
std::cout << rectf << std::endl;
return EXIT_FAILURE;
}
if (rectf.GetWidth() != 4.0f)
{
std::cout << "GetWidth() failed\n";
std::cout << rectf << std::endl;
return EXIT_FAILURE;
}
if (rectf.GetHeight() != 5.0f)
{
std::cout << "GetHeight() failed\n";
std::cout << rectf << std::endl;
return EXIT_FAILURE;
}
// Test Setters/getters ----------------------------------------------------
rectf.SetX(1.0f);
if (rectf.GetX() != 1.0f)
{
std::cout << "SetX()/GetX() failed\n";
std::cout << rectf << std::endl;
return EXIT_FAILURE;
}
rectf.SetY(8.0f);
if (rectf.GetY() != 8.0f)
{
std::cout << "SetY()/GetY() failed\n";
std::cout << rectf << std::endl;
return EXIT_FAILURE;
}
rectf.SetWidth(7.0f);
if (rectf.GetWidth() != 7.0f)
{
std::cout << "SetWidth()/GetWidth() failed\n";
std::cout << rectf << std::endl;
return EXIT_FAILURE;
}
rectf.SetHeight(9.0f);
if (rectf.GetHeight() != 9.0f)
{
std::cout << "SetHeight()/GetHeight() failed\n";
std::cout << rectf << std::endl;
return EXIT_FAILURE;
}
// Test AddPoint() ----------------------------------------------------------
vtkRectd expectedRect;
vtkRectd expandRect = vtkRectd(0.0, 0.0, 0.0, 0.0);
expectedRect = vtkRectd(-1.0, 0.0, 1.0, 1.0);
result += TestAddPoint(expandRect, -1.0, 1.0, expectedRect);
expectedRect = vtkRectd(-1.0, -3.0, 3.0, 4.0);
result += TestAddPoint(expandRect, 2.0, -3.0, expectedRect);
// Test AddRect() -----------------------------------------------------------
vtkRectd addRect;
// These five cases should exercise all the branches in vtkRect::AddRect().
expandRect = vtkRectd(0, 0, 4, 4);
addRect = vtkRectd(-1, 3, 2, 2);
expectedRect = vtkRectd(-1, 0, 5, 5);
result += TestAddRect(expandRect, addRect, expectedRect);
expandRect = vtkRectd(0, 0, 4, 4);
addRect = vtkRectd(3, 0, 2, 4);
expectedRect = vtkRectd(0, 0, 5, 4);
result += TestAddRect(expandRect, addRect, expectedRect);
expandRect = vtkRectd(0, 0, 4, 4);
addRect = vtkRectd(0, -1, 4, 2);
expectedRect = vtkRectd(0, -1, 4, 5);
result += TestAddRect(expandRect, addRect, expectedRect);
expandRect = vtkRectd(0, 0, 4, 4);
addRect = vtkRectd(1, 1, 2, 2);
expectedRect = vtkRectd(0, 0, 4, 4);
result += TestAddRect(expandRect, addRect, expectedRect);
// Test IntersectsWith() -----------------------------------------------------
vtkRecti recti(2, 3, 2, 1);
vtkRecti doesntIntersect(-1, -2, 3, 4);
if (recti.IntersectsWith(doesntIntersect))
{
std::cout << "Should not have intersected\n";
std::cout << "recti:\n";
std::cout << recti << "\n";
std::cout << "doesntIntersect:\n";
std::cout << doesntIntersect << "\n";
return EXIT_FAILURE;
}
vtkRecti intersects(3, 2, 3, 4);
if (!recti.IntersectsWith(intersects))
{
std::cout << "Should have intersected\n";
std::cout << "recti:\n";
std::cout << recti << "\n";
std::cout << "intersect:\n";
std::cout << intersects << "\n";
}
if (result != EXIT_SUCCESS)
{
result = EXIT_FAILURE;
}
return result;
}
......@@ -26,6 +26,8 @@
#include "vtkVector.h"
#include "vtkMath.h" // for Min, Max
template<typename T>
class vtkRect : public vtkVector<T, 4>
{
......@@ -129,6 +131,122 @@ public:
{
return vtkVector2<T>(this->GetRight(), this->GetTop());
}
// Description:
// Expand this rect to contain the point passed in.
void AddPoint(const T point[2])
{
// This code is written like this to ensure that adding a point gives
// exactly the same result as AddRect(vtkRect(x,y,0,0)
if (point[0] < this->GetX())
{
T dx = this->GetX() - point[0];
this->SetX(point[0]);
this->SetWidth(dx + this->GetWidth());
}
else if (point[0] > this->GetX())
{
// this->GetX() is already correct
T dx = point[0] - this->GetX();
this->SetWidth(vtkMath::Max(dx, this->GetWidth()));
}
if (point[1] < this->GetY())
{
T dy = this->GetY() - point[1];
this->SetY(point[1]);
this->SetHeight(dy + this->GetHeight());
}
else if (point[1] > this->GetY())
{
// this->GetY() is already correct
T dy = point[1] - this->GetY();
this->SetHeight(vtkMath::Max(dy, this->GetHeight()));
}
}
// Description:
// Expand this rect to contain the point passed in.
void AddPoint(T x, T y)
{
T point[2] = {x, y};
this->AddPoint(point);
}
// Description:
// Expand this rect to contain the rect passed in.
void AddRect(const vtkRect<T> & rect)
{
if (rect.GetX() < this->GetX())
{
T dx = this->GetX() - rect.GetX();
this->SetX(rect.GetX());
this->SetWidth(vtkMath::Max(dx + this->GetWidth(), rect.GetWidth()));
}
else if (rect.GetX() > this->GetX())
{
T dx = rect.GetX() - this->GetX();
// this->GetX() is already correct
this->SetWidth(vtkMath::Max(dx + rect.GetWidth(), this->GetWidth()));
}
else
{
// this->GetX() is already correct
this->SetWidth(vtkMath::Max(rect.GetWidth(), this->GetWidth()));
}
if (rect.GetY() < this->GetY())
{
T dy = this->GetY() - rect.GetY();
this->SetY(rect.GetY());
this->SetHeight(vtkMath::Max(dy + this->GetHeight(), rect.GetHeight()));
}
else if (rect.GetY() > this->GetY())
{
T dy = rect.GetY() - this->GetY();
// this->GetY() is already correct
this->SetHeight(vtkMath::Max(dy + rect.GetHeight(), this->GetHeight()));
}
else
{
// this->GetY() is already correct
this->SetHeight(vtkMath::Max(rect.GetHeight(), this->GetHeight()));
}
}
// Description:
// Returns true if the rect argument overlaps this rect.
// If the upper bound of one rect is equal to the lower bound of
// the other rect, then this will return false (in that case, the
// rects would be considered to be adjacent but not overlapping).
bool IntersectsWith(const vtkRect<T> & rect)
{
bool intersects = true;
if (rect.GetX() < this->GetX())
{
T dx = this->GetX() - rect.GetX();
intersects &= (dx < rect.GetWidth());
}
else if (rect.GetX() > this->GetX())
{
T dx = rect.GetX() - this->GetX();
intersects &= (dx < this->GetWidth());
}
if (rect.GetY() < this->GetY())
{
T dy = this->GetY() - rect.GetY();
intersects &= (dy < rect.GetHeight());
}
else if (rect.GetY() > this->GetY())
{
T dy = rect.GetY() - this->GetY();
intersects &= (dy < this->GetHeight());
}
return intersects;
}
};
class vtkRecti : public vtkRect<int>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment