Initial commit
This commit is contained in:
275
engines/crab/Polygon.cpp
Normal file
275
engines/crab/Polygon.cpp
Normal file
@@ -0,0 +1,275 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code is based on the CRAB engine
|
||||
*
|
||||
* Copyright (c) Arvind Raja Yadav
|
||||
*
|
||||
* Licensed under MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include "graphics/screen.h"
|
||||
#include "crab/crab.h"
|
||||
#include "crab/Polygon.h"
|
||||
|
||||
namespace Crab {
|
||||
|
||||
// Calculate the distance between [minA, maxA] and [minB, maxB]
|
||||
// The distance will be negative if the intervals overlap
|
||||
float IntervalDistance(float minA, float maxA, float minB, float maxB) {
|
||||
if (minA < minB)
|
||||
return minB - maxA;
|
||||
return minA - maxB;
|
||||
}
|
||||
|
||||
void Polygon2D::addPoint(const Vector2f &ref, const Common::String &x, const Common::String &y, Vector2f &min, Vector2f &max) {
|
||||
Vector2f p;
|
||||
p.x = ref.x + stringToNumber<float>(x);
|
||||
p.y = ref.y + stringToNumber<float>(y);
|
||||
|
||||
if (p.x < min.x)
|
||||
min.x = p.x;
|
||||
if (p.x > max.x)
|
||||
max.x = p.x;
|
||||
|
||||
if (p.y < min.y)
|
||||
min.y = p.y;
|
||||
if (p.y > max.y)
|
||||
max.y = p.y;
|
||||
|
||||
_point.push_back(p);
|
||||
}
|
||||
|
||||
void Polygon2D::load(rapidxml::xml_node<char> *node, Rect &bounds) {
|
||||
Vector2f ref;
|
||||
ref.load(node);
|
||||
|
||||
// Converting a polygon to an axis aligned bounding box is easy - just record the minimum and maximum values of x and y
|
||||
// for the vertices of the polygon, then minimum = top left corner, max - min = dimensions
|
||||
Vector2f min(std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
|
||||
Vector2f max(-std::numeric_limits<float>::max(), -std::numeric_limits<float>::max());
|
||||
|
||||
_point.clear();
|
||||
rapidxml::xml_node<char> *polynode = node->first_node("polygon");
|
||||
if (polynode != nullptr) {
|
||||
Common::String points, x, y;
|
||||
loadStr(points, "points", polynode);
|
||||
|
||||
bool comma = false;
|
||||
for (const auto &i : points) {
|
||||
if (i == ',')
|
||||
comma = true;
|
||||
else if (i == ' ') {
|
||||
addPoint(ref, x, y, min, max);
|
||||
comma = false;
|
||||
x.clear();
|
||||
y.clear();
|
||||
} else if (comma)
|
||||
y += i;
|
||||
else
|
||||
x += i;
|
||||
}
|
||||
|
||||
addPoint(ref, x, y, min, max);
|
||||
|
||||
bounds.x = min.x;
|
||||
bounds.y = min.y;
|
||||
bounds.w = max.x - min.x;
|
||||
bounds.h = max.y - min.y;
|
||||
}
|
||||
|
||||
setEdge();
|
||||
}
|
||||
|
||||
void Polygon2D::setEdge() {
|
||||
_edge.clear();
|
||||
Vector2f p1, p2, res;
|
||||
for (uint i = 0; i < _point.size(); i++) {
|
||||
p1 = _point[i];
|
||||
if (i + 1 >= _point.size())
|
||||
p2 = _point[0];
|
||||
else
|
||||
p2 = _point[i + 1];
|
||||
|
||||
res.x = p2.x - p1.x;
|
||||
res.y = p2.y - p1.y;
|
||||
_edge.push_back(res);
|
||||
}
|
||||
}
|
||||
|
||||
Vector2f Polygon2D::center() const {
|
||||
Vector2f total;
|
||||
for (uint i = 0; i < _point.size(); i++) {
|
||||
total.x += _point[i].x;
|
||||
total.y += _point[i].y;
|
||||
}
|
||||
|
||||
Vector2f ret;
|
||||
if (_point.size() > 0) {
|
||||
ret.x = total.x / _point.size();
|
||||
ret.y = total.y / _point.size();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Polygon2D::offset(const float &x, const float &y) {
|
||||
for (auto &i : _point) {
|
||||
i.x += x;
|
||||
i.y += y;
|
||||
}
|
||||
}
|
||||
|
||||
void Polygon2D::project(const Vector2f &axis, float &min, float &max) const {
|
||||
// To project a point on an axis use the dot product
|
||||
float d = axis.dotProduct(_point[0]);
|
||||
min = d;
|
||||
max = d;
|
||||
|
||||
for (auto i = _point.begin(); i != _point.end(); ++i) {
|
||||
d = i->dotProduct(axis);
|
||||
|
||||
if (d < min)
|
||||
min = d;
|
||||
else if (d > max)
|
||||
max = d;
|
||||
}
|
||||
}
|
||||
|
||||
PolygonCollisionResult Polygon2D::collide(const Rect &rect) const {
|
||||
Polygon2D polyB;
|
||||
Vector2f p;
|
||||
p.x = rect.x;
|
||||
p.y = rect.y;
|
||||
polyB._point.push_back(p);
|
||||
p.x = rect.x + rect.w;
|
||||
p.y = rect.y;
|
||||
polyB._point.push_back(p);
|
||||
p.x = rect.x + rect.w;
|
||||
p.y = rect.y + rect.h;
|
||||
polyB._point.push_back(p);
|
||||
p.x = rect.x;
|
||||
p.y = rect.y + rect.h;
|
||||
polyB._point.push_back(p);
|
||||
polyB.setEdge();
|
||||
|
||||
return collide(polyB);
|
||||
}
|
||||
|
||||
PolygonCollisionResult Polygon2D::collide(const Polygon2D &polyB) const {
|
||||
PolygonCollisionResult result;
|
||||
result._intersect = true;
|
||||
|
||||
int edgeCountA = _edge.size();
|
||||
int edgeCountB = polyB._edge.size();
|
||||
float minIntervalDistance = std::numeric_limits<float>::max();
|
||||
Vector2f translationAxis;
|
||||
Vector2f e;
|
||||
|
||||
// Loop through all the edges of both polygons
|
||||
for (int edgeIndex = 0; edgeIndex < edgeCountA + edgeCountB; edgeIndex++) {
|
||||
if (edgeIndex < edgeCountA)
|
||||
e = _edge[edgeIndex];
|
||||
else
|
||||
e = polyB._edge[edgeIndex - edgeCountA];
|
||||
|
||||
// ===== 1. Find if the Polygon2Ds are currently intersecting =====
|
||||
|
||||
// Find the axis perpendicular to the current edge
|
||||
Vector2f axis(-e.y, e.x);
|
||||
axis.normalize();
|
||||
|
||||
// Find the projection of the Polygon2D on the current axis
|
||||
float minA = 0;
|
||||
float minB = 0;
|
||||
float maxA = 0;
|
||||
float maxB = 0;
|
||||
project(axis, minA, maxA);
|
||||
polyB.project(axis, minB, maxB);
|
||||
|
||||
// Check if the Polygon2D projections are currently intersecting
|
||||
float intervalDistance = IntervalDistance(minA, maxA, minB, maxB);
|
||||
if (intervalDistance > 0) {
|
||||
// If the Polygon2Ds are not intersecting and won't intersect, exit the loop
|
||||
result._intersect = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if the current interval distance is the minimum one. If so store
|
||||
// the interval distance and the current distance.
|
||||
// This will be used to calculate the minimum translation vector
|
||||
intervalDistance = abs(intervalDistance);
|
||||
if (intervalDistance < minIntervalDistance) {
|
||||
minIntervalDistance = intervalDistance;
|
||||
translationAxis = axis;
|
||||
|
||||
Vector2f d, ca, cb;
|
||||
ca = center();
|
||||
cb = polyB.center();
|
||||
|
||||
d.x = ca.x - cb.x;
|
||||
d.y = ca.y - cb.y;
|
||||
|
||||
if (d.dotProduct(translationAxis) < 0) {
|
||||
translationAxis.x = -translationAxis.x;
|
||||
translationAxis.y = -translationAxis.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The minimum translation vector can be used to push the Polygon2Ds apart.
|
||||
// First moves the Polygon2Ds by their velocity
|
||||
// then move polyA by MinimumTranslationVector.
|
||||
if (result._intersect) {
|
||||
result._mtv.x = translationAxis.x * minIntervalDistance;
|
||||
result._mtv.y = translationAxis.y * minIntervalDistance;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Polygon2D::contains(const float &x, const float &y) {
|
||||
bool result = false;
|
||||
|
||||
for (uint i = 0, j = _point.size() - 1; i < _point.size(); j = i++) {
|
||||
if (((_point[i].y > y) != (_point[j].y > y)) &&
|
||||
(x < (_point[j].x - _point[i].x) * (y - _point[i].y) / (_point[j].y - _point[i].y) + _point[i].x))
|
||||
result = !result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Polygon2D::draw(const int &xOffset, const int &yOffset, const uint8 &r, const uint8 &g, const uint8 &b, const uint8 &a) {
|
||||
Vector2f p1, p2;
|
||||
for (uint i = 0; i < _point.size(); i++) {
|
||||
p1 = _point[i];
|
||||
if (i + 1 >= _point.size())
|
||||
p2 = _point[0];
|
||||
else
|
||||
p2 = _point[i + 1];
|
||||
|
||||
g_engine->_screen->drawLine(p1.x + xOffset, p1.y + yOffset, p2.x + xOffset, p2.y + yOffset, g_engine->_format.ARGBToColor(a, r, g, b));
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Crab
|
||||
Reference in New Issue
Block a user