Skip to main content
Code Review

Return to Answer

Fixed confusing antecedent
Source Link
200_success
  • 145.6k
  • 22
  • 190
  • 479

Then, for each square (which I'll call a Block), sort the four vertices according to their angle if itthey were expressed in polar coordinates. The first and last vertex after sorting are the ones you are looking for. One advantage of this solution over yours is that the squares do not have to be rotationally aligned with the x and y axes. The main benefit is that there are many fewer cases to be covered — the computer, rather than the programmer, does the hard work.

Then, for each square (which I'll call a Block), sort the four vertices according to their angle if it were expressed in polar coordinates. The first and last vertex after sorting are the ones you are looking for. One advantage of this solution over yours is that the squares do not have to be rotationally aligned with the x and y axes. The main benefit is that there are many fewer cases to be covered — the computer, rather than the programmer, does the hard work.

Then, for each square (which I'll call a Block), sort the four vertices according to their angle if they were expressed in polar coordinates. The first and last vertex after sorting are the ones you are looking for. One advantage of this solution over yours is that the squares do not have to be rotationally aligned with the x and y axes. The main benefit is that there are many fewer cases to be covered — the computer, rather than the programmer, does the hard work.

#include <algorithm> and link to ideone
Source Link
200_success
  • 145.6k
  • 22
  • 190
  • 479

Tested using clang++ -std=c++11 -g -o cr31597 cr31597.cpp and on ideone .

// Tested#include using
//<algorithm> clang++ -std=c++11 -g -o// cr31597for cr31597.cpp
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;
}
// Tested using
// clang++ -std=c++11 -g -o cr31597 cr31597.cpp

#include <iostream>
#include <vector>
#include <math.h>
//////////////////////////////////////////////////////////////////////
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;
}

Tested using clang++ -std=c++11 -g -o cr31597 cr31597.cpp and on ideone .

#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;
}
Source Link
200_success
  • 145.6k
  • 22
  • 190
  • 479

I can't follow your code at all, mainly because I don't understand your terminology. What'a a GrapplingHookPathPoint? anchoredBlock? anchorPoint? problem?

Anyway, I'll describe how I would approach the problem instead.

Let's assume that all coordinates are expressed relative to the desired origin point. If not, make it so.

Then, for each square (which I'll call a Block), sort the four vertices according to their angle if it were expressed in polar coordinates. The first and last vertex after sorting are the ones you are looking for. One advantage of this solution over yours is that the squares do not have to be rotationally aligned with the x and y axes. The main benefit is that there are many fewer cases to be covered — the computer, rather than the programmer, does the hard work.

I'd like to avoid having to call atan2(), both because trigonometric functions are computationally intensive and because arctangents fail at 90°. Instead of sorting by the angle, I can just sort by the cosine of the angle, as long as all of the points are on the same side of the X-axis. (The cosine of the angle can be calculated using the rule AB = |A| |B| cos θ .) If a square straddles the X-axis, I can try using the Y-axis instead. If a square straddles both the X- and the Y-axis, then it must contain the origin, which is an error.

// Tested using
// clang++ -std=c++11 -g -o cr31597 cr31597.cpp
#include <iostream>
#include <vector>
#include <math.h>
//////////////////////////////////////////////////////////////////////
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;
}
lang-cpp

AltStyle によって変換されたページ (->オリジナル) /