1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-28 23:06:24 +02:00
vcmi/lib/Rect.cpp

148 lines
3.9 KiB
C++
Raw Normal View History

/*
* Rect.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "Rect.h"
#include "int3.h"
VCMI_LIB_NAMESPACE_BEGIN
Point::Point(const int3 & a)
: x(a.x)
, y(a.y)
{
}
/// Returns rect union - rect that covers both this rect and provided rect
Rect Rect::include(const Rect & other) const
{
Point topLeft{
std::min(this->left(), other.left()),
std::min(this->top(), other.top())
};
Point bottomRight{
std::max(this->right(), other.right()),
std::max(this->bottom(), other.bottom())
};
return Rect(topLeft, bottomRight - topLeft);
}
Rect Rect::createCentered( const Point & around, const Point & dimensions )
{
return Rect(around - dimensions/2, dimensions);
}
Rect Rect::createAround(const Rect &r, int width)
{
return Rect(r.x - width, r.y - width, r.w + width * 2, r.h + width * 2);
}
Rect Rect::createCentered( const Rect & rect, const Point & dimensions)
{
return createCentered(rect.center(), dimensions);
}
bool Rect::intersectionTest(const Rect & other) const
{
// this rect is above other rect
if(this->bottom() < other.top())
return false;
// this rect is below other rect
if(this->top() > other.bottom() )
return false;
// this rect is to the left of other rect
if(this->right() < other.left())
return false;
// this rect is to the right of other rect
if(this->left() > other.right())
return false;
return true;
}
/// Algorithm to test whether line segment between points line1-line2 will intersect with
/// rectangle specified by top-left and bottom-right points
/// Note that in order to avoid floating point rounding errors algorithm uses integers with no divisions
bool Rect::intersectionTest(const Point & line1, const Point & line2) const
{
// check whether segment is located to the left of our rect
if (line1.x < left() && line2.x < left())
return false;
// check whether segment is located to the right of our rect
if (line1.x > right() && line2.x > right())
return false;
// check whether segment is located on top of our rect
if (line1.y < top() && line2.y < top())
return false;
// check whether segment is located below of our rect
if (line1.y > bottom() && line2.y > bottom())
return false;
Point vector { line2.x - line1.x, line2.y - line1.y};
// compute position of corners relative to our line
int tlTest = vector.y*topLeft().x - vector.x*topLeft().y + (line2.x*line1.y-line1.x*line2.y);
int trTest = vector.y*bottomRight().x - vector.x*topLeft().y + (line2.x*line1.y-line1.x*line2.y);
int blTest = vector.y*topLeft().x - vector.x*bottomRight().y + (line2.x*line1.y-line1.x*line2.y);
int brTest = vector.y*bottomRight().x - vector.x*bottomRight().y + (line2.x*line1.y-line1.x*line2.y);
// if all points are on the left of our line then there is no intersection
if ( tlTest > 0 && trTest > 0 && blTest > 0 && brTest > 0 )
return false;
// if all points are on the right of our line then there is no intersection
if ( tlTest < 0 && trTest < 0 && blTest < 0 && brTest < 0 )
return false;
// if all previous checks failed, this means that there is an intersection between line and AABB
return true;
}
Rect Rect::intersect(const Rect & other) const
{
if(intersectionTest(other))
{
Point topLeft{
std::max(this->left(), other.left()),
std::max(this->top(), other.top())
};
Point bottomRight{
std::min(this->right(), other.right()),
std::min(this->bottom(), other.bottom())
};
return Rect(topLeft, bottomRight - topLeft);
}
else
{
return Rect();
}
}
int Rect::distanceTo(const Point & target) const
{
int distanceX = std::max({left() - target.x, 0, target.x - right()});
int distanceY = std::max({top() - target.y, 0, target.y - bottom()});
return Point(distanceX, distanceY).length();
}
VCMI_LIB_NAMESPACE_END