mirror of https://github.com/axmolengine/axmol.git
645 lines
17 KiB
C++
645 lines
17 KiB
C++
|
|
#include "BoundingBoxData.h"
|
|
#include "DisplayData.h"
|
|
|
|
|
|
DRAGONBONES_NAMESPACE_BEGIN
|
|
|
|
void BoundingBoxData::_onClear()
|
|
{
|
|
color = 0x000000;
|
|
width = 0.0f;
|
|
height = 0.0f;
|
|
}
|
|
|
|
int RectangleBoundingBoxData::_computeOutCode(float x, float y, float xMin, float yMin, float xMax, float yMax)
|
|
{
|
|
int code = OutCode::InSide; // initialised as being inside of [[clip window]]
|
|
|
|
if (x < xMin) // to the left of clip window
|
|
{
|
|
code |= OutCode::Left;
|
|
}
|
|
else if (x > xMax) // to the right of clip window
|
|
{
|
|
code |= OutCode::Right;
|
|
}
|
|
|
|
if (y < yMin) // below the clip window
|
|
{
|
|
code |= OutCode::Top;
|
|
}
|
|
else if (y > yMax) // above the clip window
|
|
{
|
|
code |= OutCode::Bottom;
|
|
}
|
|
|
|
return code;
|
|
}
|
|
|
|
int RectangleBoundingBoxData::rectangleIntersectsSegment(
|
|
float xA, float yA, float xB, float yB,
|
|
float xMin, float yMin, float xMax, float yMax,
|
|
Point* intersectionPointA,
|
|
Point* intersectionPointB,
|
|
Point* normalRadians
|
|
)
|
|
{
|
|
const auto inSideA = xA > xMin && xA < xMax && yA > yMin && yA < yMax;
|
|
const auto inSideB = xB > xMin && xB < xMax && yB > yMin && yB < yMax;
|
|
|
|
if (inSideA && inSideB)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
auto intersectionCount = 0;
|
|
auto outcode0 = RectangleBoundingBoxData::_computeOutCode(xA, yA, xMin, yMin, xMax, yMax);
|
|
auto outcode1 = RectangleBoundingBoxData::_computeOutCode(xB, yB, xMin, yMin, xMax, yMax);
|
|
|
|
while (true)
|
|
{
|
|
if ((outcode0 | outcode1) == 0) // Bitwise OR is 0. Trivially accept and get out of loop
|
|
{
|
|
intersectionCount = 2;
|
|
break;
|
|
}
|
|
else if ((outcode0 & outcode1) != 0) // Bitwise AND is not 0. Trivially reject and get out of loop
|
|
{
|
|
break;
|
|
}
|
|
|
|
// failed both tests, so calculate the line segment to clip
|
|
// from an outside point to an intersection with clip edge
|
|
auto x = 0.0f;
|
|
auto y = 0.0f;
|
|
auto normalRadian = 0.0f;
|
|
|
|
// At least one endpoint is outside the clip rectangle; pick it.
|
|
const auto outcodeOut = outcode0 != 0 ? outcode0 : outcode1;
|
|
|
|
// Now find the intersection point;
|
|
if ((outcodeOut & OutCode::Top) != 0) // point is above the clip rectangle
|
|
{
|
|
x = xA + (xB - xA) * (yMin - yA) / (yB - yA);
|
|
y = yMin;
|
|
|
|
if (normalRadians != nullptr)
|
|
{
|
|
normalRadian = -Transform::PI * 0.5f;
|
|
}
|
|
}
|
|
else if ((outcodeOut & OutCode::Bottom) != 0) // point is below the clip rectangle
|
|
{
|
|
x = xA + (xB - xA) * (yMax - yA) / (yB - yA);
|
|
y = yMax;
|
|
|
|
if (normalRadians != nullptr)
|
|
{
|
|
normalRadian = Transform::PI * 0.5;
|
|
}
|
|
}
|
|
else if ((outcodeOut & OutCode::Right) != 0) // point is to the right of clip rectangle
|
|
{
|
|
y = yA + (yB - yA) * (xMax - xA) / (xB - xA);
|
|
x = xMax;
|
|
|
|
if (normalRadians != nullptr)
|
|
{
|
|
normalRadian = 0;
|
|
}
|
|
}
|
|
else if ((outcodeOut & OutCode::Left) != 0) // point is to the left of clip rectangle
|
|
{
|
|
y = yA + (yB - yA) * (xMin - xA) / (xB - xA);
|
|
x = xMin;
|
|
|
|
if (normalRadians != nullptr)
|
|
{
|
|
normalRadian = Transform::PI;
|
|
}
|
|
}
|
|
|
|
// Now we move outside point to intersection point to clip
|
|
// and get ready for next pass.
|
|
if (outcodeOut == outcode0)
|
|
{
|
|
xA = x;
|
|
yA = y;
|
|
outcode0 = RectangleBoundingBoxData::_computeOutCode(xA, yA, xMin, yMin, xMax, yMax);
|
|
|
|
if (normalRadians != nullptr)
|
|
{
|
|
normalRadians->x = normalRadian;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
xB = x;
|
|
yB = y;
|
|
outcode1 = RectangleBoundingBoxData::_computeOutCode(xB, yB, xMin, yMin, xMax, yMax);
|
|
|
|
if (normalRadians != nullptr)
|
|
{
|
|
normalRadians->y = normalRadian;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (intersectionCount)
|
|
{
|
|
if (inSideA)
|
|
{
|
|
intersectionCount = 2; // 10
|
|
|
|
if (intersectionPointA != nullptr)
|
|
{
|
|
intersectionPointA->x = xB;
|
|
intersectionPointA->y = yB;
|
|
}
|
|
|
|
if (intersectionPointB != nullptr)
|
|
{
|
|
intersectionPointB->x = xB;
|
|
intersectionPointB->y = xB;
|
|
}
|
|
|
|
if (normalRadians != nullptr)
|
|
{
|
|
normalRadians->x = normalRadians->y + Transform::PI;
|
|
}
|
|
}
|
|
else if (inSideB)
|
|
{
|
|
intersectionCount = 1; // 01
|
|
|
|
if (intersectionPointA != nullptr)
|
|
{
|
|
intersectionPointA->x = xA;
|
|
intersectionPointA->y = yA;
|
|
}
|
|
|
|
if (intersectionPointB != nullptr)
|
|
{
|
|
intersectionPointB->x = xA;
|
|
intersectionPointB->y = yA;
|
|
}
|
|
|
|
if (normalRadians != nullptr)
|
|
{
|
|
normalRadians->y = normalRadians->x + Transform::PI;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
intersectionCount = 3; // 11
|
|
if (intersectionPointA != nullptr)
|
|
{
|
|
intersectionPointA->x = xA;
|
|
intersectionPointA->y = yA;
|
|
}
|
|
|
|
if (intersectionPointB != nullptr)
|
|
{
|
|
intersectionPointB->x = xB;
|
|
intersectionPointB->y = yB;
|
|
}
|
|
}
|
|
}
|
|
|
|
return intersectionCount;
|
|
}
|
|
|
|
void RectangleBoundingBoxData::_onClear()
|
|
{
|
|
BoundingBoxData::_onClear();
|
|
|
|
type = BoundingBoxType::Rectangle;
|
|
}
|
|
|
|
bool RectangleBoundingBoxData::containsPoint(float pX, float pY)
|
|
{
|
|
const auto widthH = width * 0.5f;
|
|
if (pX >= -widthH && pX <= widthH)
|
|
{
|
|
const auto heightH = height * 0.5f;
|
|
if (pY >= -heightH && pY <= heightH)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int RectangleBoundingBoxData::intersectsSegment(
|
|
float xA, float yA, float xB, float yB,
|
|
Point* intersectionPointA,
|
|
Point* intersectionPointB,
|
|
Point* normalRadians
|
|
)
|
|
{
|
|
const auto widthH = width * 0.5f;
|
|
const auto heightH = height * 0.5f;
|
|
const auto intersectionCount = RectangleBoundingBoxData::rectangleIntersectsSegment(
|
|
xA, yA, xB, yB,
|
|
-widthH, -heightH, widthH, heightH,
|
|
intersectionPointA, intersectionPointB, normalRadians
|
|
);
|
|
|
|
return intersectionCount;
|
|
}
|
|
|
|
int EllipseBoundingBoxData::ellipseIntersectsSegment(
|
|
float xA, float yA, float xB, float yB,
|
|
float xC, float yC, float widthH, float heightH,
|
|
Point* intersectionPointA,
|
|
Point* intersectionPointB,
|
|
Point* normalRadians
|
|
)
|
|
{
|
|
const auto d = widthH / heightH;
|
|
const auto dd = d * d;
|
|
|
|
yA *= d;
|
|
yB *= d;
|
|
|
|
const auto dX = xB - xA;
|
|
const auto dY = yB - yA;
|
|
const auto lAB = sqrt(dX * dX + dY * dY);
|
|
const auto xD = dX / lAB;
|
|
const auto yD = dY / lAB;
|
|
const auto a = (xC - xA) * xD + (yC - yA) * yD;
|
|
const auto aa = a * a;
|
|
const auto ee = xA * xA + yA * yA;
|
|
const auto rr = widthH * widthH;
|
|
const auto dR = rr - ee + aa;
|
|
auto intersectionCount = 0;
|
|
|
|
if (dR >= 0.0f)
|
|
{
|
|
const auto dT = sqrt(dR);
|
|
const auto sA = a - dT;
|
|
const auto sB = a + dT;
|
|
const auto inSideA = sA < 0.0f ? -1 : (sA <= lAB ? 0 : 1);
|
|
const auto inSideB = sB < 0.0f ? -1 : (sB <= lAB ? 0 : 1);
|
|
const auto sideAB = inSideA * inSideB;
|
|
|
|
if (sideAB < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
else if (sideAB == 0)
|
|
{
|
|
if (inSideA == -1)
|
|
{
|
|
intersectionCount = 2; // 10
|
|
xB = xA + sB * xD;
|
|
yB = (yA + sB * yD) / d;
|
|
|
|
if (intersectionPointA != nullptr)
|
|
{
|
|
intersectionPointA->x = xB;
|
|
intersectionPointA->y = yB;
|
|
}
|
|
|
|
if (intersectionPointB != nullptr)
|
|
{
|
|
intersectionPointB->x = xB;
|
|
intersectionPointB->y = yB;
|
|
}
|
|
|
|
if (normalRadians != nullptr)
|
|
{
|
|
normalRadians->x = atan2(yB / rr * dd, xB / rr);
|
|
normalRadians->y = normalRadians->x + Transform::PI;
|
|
}
|
|
}
|
|
else if (inSideB == 1)
|
|
{
|
|
intersectionCount = 1; // 01
|
|
xA = xA + sA * xD;
|
|
yA = (yA + sA * yD) / d;
|
|
|
|
if (intersectionPointA != nullptr)
|
|
{
|
|
intersectionPointA->x = xA;
|
|
intersectionPointA->y = yA;
|
|
}
|
|
|
|
if (intersectionPointB != nullptr)
|
|
{
|
|
intersectionPointB->x = xA;
|
|
intersectionPointB->y = yA;
|
|
}
|
|
|
|
if (normalRadians != nullptr)
|
|
{
|
|
normalRadians->x = atan2(yA / rr * dd, xA / rr);
|
|
normalRadians->y = normalRadians->x + Transform::PI;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
intersectionCount = 3; // 11
|
|
|
|
if (intersectionPointA != nullptr)
|
|
{
|
|
intersectionPointA->x = xA + sA * xD;
|
|
intersectionPointA->y = (yA + sA * yD) / d;
|
|
|
|
if (normalRadians != nullptr)
|
|
{
|
|
normalRadians->x = atan2(intersectionPointA->y / rr * dd, intersectionPointA->x / rr);
|
|
}
|
|
}
|
|
|
|
if (intersectionPointB != nullptr)
|
|
{
|
|
intersectionPointB->x = xA + sB * xD;
|
|
intersectionPointB->y = (yA + sB * yD) / d;
|
|
|
|
if (normalRadians != nullptr)
|
|
{
|
|
normalRadians->y = atan2(intersectionPointB->y / rr * dd, intersectionPointB->x / rr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return intersectionCount;
|
|
}
|
|
|
|
void EllipseBoundingBoxData::_onClear()
|
|
|
|
{
|
|
BoundingBoxData::_onClear();
|
|
|
|
type = BoundingBoxType::Ellipse;
|
|
}
|
|
|
|
bool EllipseBoundingBoxData::containsPoint(float pX, float pY)
|
|
{
|
|
const auto widthH = width * 0.5f;
|
|
if (pX >= -widthH && pX <= widthH)
|
|
{
|
|
const auto heightH = height * 0.5f;
|
|
if (pY >= -heightH && pY <= heightH)
|
|
{
|
|
pY *= widthH / heightH;
|
|
return sqrt(pX * pX + pY * pY) <= widthH;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int EllipseBoundingBoxData::intersectsSegment(
|
|
float xA, float yA, float xB, float yB,
|
|
Point* intersectionPointA,
|
|
Point* intersectionPointB,
|
|
Point* normalRadians
|
|
)
|
|
{
|
|
const auto intersectionCount = EllipseBoundingBoxData::ellipseIntersectsSegment(
|
|
xA, yA, xB, yB,
|
|
0.0f, 0.0f, width * 0.5f, height * 0.5f,
|
|
intersectionPointA, intersectionPointB, normalRadians
|
|
);
|
|
|
|
return intersectionCount;
|
|
}
|
|
|
|
int PolygonBoundingBoxData::polygonIntersectsSegment(
|
|
float xA, float yA, float xB, float yB,
|
|
const std::vector<float>& vertices,
|
|
Point* intersectionPointA,
|
|
Point* intersectionPointB,
|
|
Point* normalRadians)
|
|
{
|
|
if (xA == xB)
|
|
{
|
|
xA = xB + 0.000001f;
|
|
}
|
|
|
|
if (yA == yB)
|
|
{
|
|
yA = yB + 0.000001f;
|
|
}
|
|
|
|
const auto count = vertices.size();
|
|
const auto dXAB = xA - xB;
|
|
const auto dYAB = yA - yB;
|
|
const auto llAB = xA * yB - yA * xB;
|
|
auto intersectionCount = 0;
|
|
auto xC = vertices[count - 2];
|
|
auto yC = vertices[count - 1];
|
|
auto dMin = 0.0f;
|
|
auto dMax = 0.0f;
|
|
auto xMin = 0.0f;
|
|
auto yMin = 0.0f;
|
|
auto xMax = 0.0f;
|
|
auto yMax = 0.0f;
|
|
|
|
for (std::size_t i = 0; i < count; i += 2)
|
|
{
|
|
const auto xD = vertices[i];
|
|
const auto yD = vertices[i + 1];
|
|
|
|
if (xC == xD)
|
|
{
|
|
xC = xD + 0.000001f;
|
|
}
|
|
|
|
if (yC == yD)
|
|
{
|
|
yC = yD + 0.000001f;
|
|
}
|
|
|
|
const auto dXCD = xC - xD;
|
|
const auto dYCD = yC - yD;
|
|
const auto llCD = xC * yD - yC * xD;
|
|
const auto ll = dXAB * dYCD - dYAB * dXCD;
|
|
const auto x = (llAB * dXCD - dXAB * llCD) / ll;
|
|
|
|
if (((x >= xC && x <= xD) || (x >= xD && x <= xC)) && (dXAB == 0.0f || (x >= xA && x <= xB) || (x >= xB && x <= xA)))
|
|
{
|
|
const auto y = (llAB * dYCD - dYAB * llCD) / ll;
|
|
if (((y >= yC && y <= yD) || (y >= yD && y <= yC)) && (dYAB == 0.0f || (y >= yA && y <= yB) || (y >= yB && y <= yA)))
|
|
{
|
|
if (intersectionPointB != nullptr)
|
|
{
|
|
float d = x - xA;
|
|
if (d < 0.0f)
|
|
{
|
|
d = -d;
|
|
}
|
|
|
|
if (intersectionCount == 0)
|
|
{
|
|
dMin = d;
|
|
dMax = d;
|
|
xMin = x;
|
|
yMin = y;
|
|
xMax = x;
|
|
yMax = y;
|
|
|
|
if (normalRadians != nullptr)
|
|
{
|
|
normalRadians->x = atan2(yD - yC, xD - xC) - Transform::PI * 0.5f;
|
|
normalRadians->y = normalRadians->x;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (d < dMin)
|
|
{
|
|
dMin = d;
|
|
xMin = x;
|
|
yMin = y;
|
|
|
|
if (normalRadians != nullptr)
|
|
{
|
|
normalRadians->x = atan2(yD - yC, xD - xC) - Transform::PI * 0.5f;
|
|
}
|
|
}
|
|
|
|
if (d > dMax)
|
|
{
|
|
dMax = d;
|
|
xMax = x;
|
|
yMax = y;
|
|
|
|
if (normalRadians != nullptr) {
|
|
normalRadians->y = atan2(yD - yC, xD - xC) - Transform::PI * 0.5f;
|
|
}
|
|
}
|
|
}
|
|
|
|
intersectionCount++;
|
|
}
|
|
else
|
|
{
|
|
xMin = x;
|
|
yMin = y;
|
|
xMax = x;
|
|
yMax = y;
|
|
intersectionCount++;
|
|
|
|
if (normalRadians != nullptr)
|
|
{
|
|
normalRadians->x = atan2(yD - yC, xD - xC) - Transform::PI * 0.5f;
|
|
normalRadians->y = normalRadians->x;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
xC = xD;
|
|
yC = yD;
|
|
}
|
|
|
|
if (intersectionCount == 1)
|
|
{
|
|
if (intersectionPointA != nullptr)
|
|
{
|
|
intersectionPointA->x = xMin;
|
|
intersectionPointA->y = yMin;
|
|
}
|
|
|
|
if (intersectionPointB != nullptr)
|
|
{
|
|
intersectionPointB->x = xMin;
|
|
intersectionPointB->y = yMin;
|
|
}
|
|
|
|
if (normalRadians != nullptr)
|
|
{
|
|
normalRadians->y = normalRadians->x + Transform::PI;
|
|
}
|
|
}
|
|
else if (intersectionCount > 1)
|
|
{
|
|
intersectionCount++;
|
|
|
|
if (intersectionPointA != nullptr)
|
|
{
|
|
intersectionPointA->x = xMin;
|
|
intersectionPointA->y = yMin;
|
|
}
|
|
|
|
if (intersectionPointB != nullptr)
|
|
{
|
|
intersectionPointB->x = xMax;
|
|
intersectionPointB->y = yMax;
|
|
}
|
|
}
|
|
|
|
return intersectionCount;
|
|
}
|
|
|
|
void PolygonBoundingBoxData::_onClear()
|
|
{
|
|
BoundingBoxData::_onClear();
|
|
|
|
if (weight != nullptr)
|
|
{
|
|
weight->returnToPool();
|
|
}
|
|
|
|
type = BoundingBoxType::Polygon;
|
|
x = 0.0f;
|
|
y = 0.0f;
|
|
vertices.clear();
|
|
weight = nullptr;
|
|
}
|
|
|
|
bool PolygonBoundingBoxData::containsPoint(float pX, float pY)
|
|
{
|
|
auto isInSide = false;
|
|
if (pX >= x && pX <= width && pY >= y && pY <= height)
|
|
{
|
|
for (std::size_t i = 0, l = vertices.size(), iP = l - 2; i < l; i += 2)
|
|
{
|
|
const auto yA = vertices[iP + 1];
|
|
const auto yB = vertices[i + 1];
|
|
if ((yB < pY && yA >= pY) || (yA < pY && yB >= pY))
|
|
{
|
|
const auto xA = vertices[iP];
|
|
const auto xB = vertices[i];
|
|
if ((pY - yB) * (xA - xB) / (yA - yB) + xB < pX)
|
|
{
|
|
isInSide = !isInSide;
|
|
}
|
|
}
|
|
|
|
iP = i;
|
|
}
|
|
}
|
|
|
|
return isInSide;
|
|
}
|
|
|
|
int PolygonBoundingBoxData::intersectsSegment(
|
|
float xA, float yA, float xB, float yB,
|
|
Point* intersectionPointA,
|
|
Point* intersectionPointB,
|
|
Point* normalRadians
|
|
)
|
|
{
|
|
auto intersectionCount = 0;
|
|
if (RectangleBoundingBoxData::rectangleIntersectsSegment(xA, yA, xB, yB, x, y, x + width, y + height, nullptr, nullptr, nullptr) != 0) {
|
|
intersectionCount = PolygonBoundingBoxData::polygonIntersectsSegment(
|
|
xA, yA, xB, yB,
|
|
vertices,
|
|
intersectionPointA, intersectionPointB, normalRadians
|
|
);
|
|
}
|
|
|
|
return intersectionCount;
|
|
}
|
|
|
|
DRAGONBONES_NAMESPACE_END
|