#include <algorithm> // for std::sort
#include <iostream>
#include <vector>
#include <math.h> // for sqrt
//////////////////////////////////////////////////////////////////////
class Vec2F {
public:
float x, y;
static const Vec2F ORIGIN, I, J;
Vec2F() : x(0), y(0) {}
Vec2F(float x, float y) : x(x), y(y) {}
Vec2F(const Vec2F &other) : x(other.x), y(other.y) {}
Vec2F &operator=(const Vec2F &other) {
if (this != &other) {
x = other.x;
y = other.y;
}
return *this;
}
bool operator==(const Vec2F &other) const {
return x == other.x && y == other.y;
}
const Vec2F operator+(const Vec2F &other) const {
return Vec2F(x + other.x, y + other.y);
}
Vec2F operator-() const {
return Vec2F(-x, -y);
}
Vec2F operator-(const Vec2F &other) const {
return *this + -other;
}
Vec2F operator*(float scale) const {
return Vec2F(scale * x, scale * y);
}
float cross(const Vec2F &other) const {
return x * other.y - y * other.x;
}
float dot(const Vec2F &other) const {
return x * other.x + y * other.y;
}
float magnitude() const {
return sqrt(x * x + y * y);
}
float sinOfAngleRelTo(const Vec2F &ref) const {
return ref.cross(*this) / ref.magnitude() / this->magnitude();
}
float cosOfAngleRelTo(const Vec2F &ref) const {
return ref.dot(*this) / ref.magnitude() / this->magnitude();
}
friend std::ostream &operator<<(std::ostream &out, const Vec2F &v) {
return out << '(' << v.x << ", " << v.y << ')';
}
};
const Vec2F Vec2F::ORIGIN(0, 0),
Vec2F::I(1, 0),
Vec2F::J(0, 1);
//////////////////////////////////////////////////////////////////////
class Block {
public:
const std::vector<Vec2F> vertices;
// Block is specified by its southwest corner
Block(const Vec2F &sw) {
Vec2F v[] = {
sw + Vec2F::J, sw + Vec2F(1, 1),
sw, sw + Vec2F::I,
};
const_cast<std::vector<Vec2F>& >(vertices) =
std::vector<Vec2F>(v, v + sizeof(v) / sizeof(Vec2F));
}
Block(const Block &other) : vertices(other.vertices) {}
friend std::ostream &operator<<(std::ostream &out, const Block &b) {
return out << "Block<" << b.vertices[2] << " - " << b.vertices[1]
<< " centered at "
<< b.vertices[2] + (b.vertices[1] - b.vertices[2]) * 0.5
<< '>';
}
private:
Block &operator=(const Block &other);
};
//////////////////////////////////////////////////////////////////////
// Comparator for Vec2F to be used to sort points by how much their angle
// deviates from a reference vector
class AxisComparator {
public:
static const AxisComparator HORIZ, VERT;
const Vec2F ref;
AxisComparator(const Vec2F &ref) : ref(ref) {}
// Returns true if a should be ordered before b
bool operator()(const Vec2F &a, const Vec2F &b) const {
float cosAngleA = a.cosOfAngleRelTo(ref);
float cosAngleB = b.cosOfAngleRelTo(ref);
if (cosAngleA == cosAngleB) {
// Same angle; the longer vector is considered more extreme.
return cosAngleA < 0 ? a.magnitude() > b.magnitude()
: a.magnitude() < b.magnitude();
} else {
return cosAngleA < cosAngleB;
}
}
// Returns true if at least some pair of vectors are on opposite sides of
// ref, or if at least one of them is aligned with ref
bool straddles(const std::vector<Vec2F> &vv) const {
int side = 0;
for (auto i = vv.begin(); i != vv.end(); ++i) {
float sinAngle = (*i).sinOfAngleRelTo(ref);
if (sinAngle == 0.0) { // aligned with ref
return true;
} else if (side == 0) { // 1st time through
side = (sinAngle < 0) ? -1 : +1;
} else if (side == +1 && sinAngle < 0.0) { // wrong side
return true;
} else if (side == -1 && sinAngle > 0.0) { // wrong side
return true;
}
}
return false;
}
};
const AxisComparator AxisComparator::HORIZ(Vec2F::I),
AxisComparator::VERT(Vec2F::J);
//////////////////////////////////////////////////////////////////////
class Grapple {
public:
const Block block;
private:
std::vector<Vec2F> vertices;
const Vec2F *v1, *v2;
public:
Grapple(const Block &b) : block(b), vertices(b.vertices) {
if (!AxisComparator::HORIZ.straddles(vertices)) {
std::sort(vertices.begin(), vertices.end(), AxisComparator::HORIZ);
v1 = &vertices.front();
v2 = &vertices.back();
} else if (!AxisComparator::VERT.straddles(vertices)) {
std::sort(vertices.begin(), vertices.end(), AxisComparator::VERT);
v1 = &vertices.front();
v2 = &vertices.back();
return;
} else {
v1 = v2 = NULL;
}
}
bool possible() const {
return v1 && v2;
}
const Vec2F &vertex1() const {
return *v1;
}
const Vec2F &vertex2() const {
return *v2;
}
};
//////////////////////////////////////////////////////////////////////
int main() {
Vec2F tests[] = {
Vec2F(-3.0, -1.0), // < 9 o'clock
Vec2F(-3.5, +2.5), // 10:30
Vec2F(-0.5, +4.0), // 12 o'clock
Vec2F(+2.5, +2.5), // 1:30
Vec2F(+4.0, -0.5), // 3 o'clock
Vec2F(-0.5, -0.5), // centered at origin
Vec2F(+0.0, -0.0), // corner at origin
};
for (int i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i) {
Block b(tests[i]);
Grapple g(b);
if (g.possible()) {
std::cout << "Grappling vertices for " << g.block << ": " << g.vertex1() << " and " << g.vertex2() << std::endl;
} else {
std::cout << g.block << " contains the origin!" << std::endl;
}
}
return 0;
}