#include <config.h>
#include <algorithm>
#include "Triangle.h"
const Triangle Triangle::INVALID = Triangle();
Triangle::Triangle() {}
Triangle::Triangle(const Position& positionA, const Position& positionB, const Position& positionC) :
myA(positionA),
myB(positionB),
myC(positionC) {
myBoundary.add(positionA);
myBoundary.add(positionB);
myBoundary.add(positionC);
}
Triangle::~Triangle() {}
bool
Triangle::isPositionWithin(const Position& pos) const {
return isPositionWithin(myA, myB, myC, pos);
}
bool
Triangle::isBoundaryFullWithin(const Boundary& boundary) const {
return isPositionWithin(Position(boundary.xmax(), boundary.ymax())) &&
isPositionWithin(Position(boundary.xmin(), boundary.ymin())) &&
isPositionWithin(Position(boundary.xmax(), boundary.ymin())) &&
isPositionWithin(Position(boundary.xmin(), boundary.ymax()));
}
bool
Triangle::intersectWithShape(const PositionVector& shape) const {
return intersectWithShape(shape, shape.getBoxBoundary());
}
bool
Triangle::intersectWithShape(const PositionVector& shape, const Boundary& shapeBoundary) const {
if (shape.around(myA) || shape.around(myB) || shape.around(myC)) {
return true;
}
const int cornerA = isPositionWithin(Position(shapeBoundary.xmax(), shapeBoundary.ymax()));
const int cornerB = isPositionWithin(Position(shapeBoundary.xmin(), shapeBoundary.ymin()));
if ((cornerA + cornerB) == 2) {
return true;
}
const int cornerC = isPositionWithin(Position(shapeBoundary.xmax(), shapeBoundary.ymin()));
if ((cornerA + cornerB + cornerC) == 2) {
return true;
}
const int cornerD = isPositionWithin(Position(shapeBoundary.xmin(), shapeBoundary.ymax()));
if ((cornerA + cornerB + cornerC + cornerD) == 2) {
return true;
}
for (int i = 0; i < ((int)shape.size() - 1); i++) {
if (lineIntersectsTriangle(shape[i], shape[i + 1])) {
return true;
}
}
return false;
}
bool
Triangle::intersectWithCircle(const Position& center, const double radius) const {
const auto squaredRadius = radius * radius;
return ((center.distanceSquaredTo2D(myA) <= squaredRadius) ||
(center.distanceSquaredTo2D(myB) <= squaredRadius) ||
(center.distanceSquaredTo2D(myC) <= squaredRadius) ||
isPositionWithin(center) ||
lineIntersectCircle(myA, myB, center, radius) ||
lineIntersectCircle(myB, myC, center, radius) ||
lineIntersectCircle(myC, myA, center, radius));
}
const Boundary&
Triangle::getBoundary() const {
return myBoundary;
}
const PositionVector
Triangle::getShape() const {
return PositionVector({myA, myB, myC});
}
std::vector<Triangle>
Triangle::triangulate(PositionVector shape) {
std::vector<Triangle> triangles;
shape.openPolygon();
if (shape.size() >= 3) {
while (shape.size() > 3) {
int shapeSize = (int)shape.size();
int earIndex = -1;
for (int i = 0; (i < shapeSize) && (earIndex == -1); i++) {
const auto& earA = shape[(i + shapeSize - 1) % shapeSize];
const auto& earB = shape[i];
const auto& earC = shape[(i + 1) % shapeSize];
if (isEar(earA, earB, earC, shape)) {
earIndex = i;
}
}
if (earIndex != -1) {
triangles.push_back(Triangle(
shape[(earIndex + shapeSize - 1) % shapeSize],
shape[earIndex],
shape[(earIndex + 1) % shapeSize])
);
shape.erase(shape.begin() + earIndex);
} else {
triangles.push_back(Triangle(shape[0], shape[1], shape[2]));
shape.erase(shape.begin() + 1);
}
}
triangles.push_back(Triangle(shape[0], shape[1], shape[2]));
}
return triangles;
}
bool
Triangle::operator==(const Triangle& other) const {
return myA == other.myA && myB == other.myB && myC == other.myC;
}
bool
Triangle::operator!=(const Triangle& other) const {
return !(*this == other);
}
bool
Triangle::isPositionWithin(const Position& A, const Position& B, const Position& C, const Position& pos) {
const double crossAB = crossProduct(A, B, pos);
const double crossBC = crossProduct(B, C, pos);
const double crossCA = crossProduct(C, A, pos);
return ((crossAB >= 0) && (crossBC >= 0) && (crossCA >= 0)) ||
((crossAB <= 0) && (crossBC <= 0) && (crossCA <= 0));
}
bool
Triangle::isEar(const Position& a, const Position& b, const Position& c, const PositionVector& shape) {
if (crossProduct(a, b, c) <= 0) {
return false;
}
for (const auto& pos : shape) {
if ((pos != a) && (pos != b) && (pos != c) && isPositionWithin(a, b, c, pos)) {
return false;
}
}
return true;
}
double
Triangle::crossProduct(const Position& a, const Position& b, const Position& c) {
return (b.x() - a.x()) * (c.y() - a.y()) - (b.y() - a.y()) * (c.x() - a.x());
}
int
Triangle::orientation(const Position& p, const Position& q, const Position& r) const {
const double val = (q.y() - p.y()) * (r.x() - q.x()) - (q.x() - p.x()) * (r.y() - q.y());
if (val > 0) {
return 1;
} else if (val < 0) {
return -1;
} else {
return 0;
}
}
bool
Triangle::onSegment(const Position& p, const Position& q, const Position& r) const {
return (q.x() >= std::min(p.x(), r.x()) && q.x() <= std::max(p.x(), r.x()) &&
q.y() >= std::min(p.y(), r.y()) && q.y() <= std::max(p.y(), r.y()));
}
bool
Triangle::segmentsIntersect(const Position& p1, const Position& q1, const Position& p2, const Position& q2) const {
const int o1 = orientation(p1, q1, p2);
const int o2 = orientation(p1, q1, q2);
const int o3 = orientation(p2, q2, p1);
const int o4 = orientation(p2, q2, q1);
if ((o1 != o2) && (o3 != o4)) {
return true;
} else if ((o1 == 0) && onSegment(p1, p2, q1)) {
return true;
} else if ((o2 == 0) && onSegment(p1, q2, q1)) {
return true;
} else if ((o3 == 0) && onSegment(p2, p1, q2)) {
return true;
} else if ((o4 == 0) && onSegment(p2, q1, q2)) {
return true;
} else {
return false;
}
}
bool
Triangle::lineIntersectsTriangle(const Position& p1, const Position& p2) const {
return segmentsIntersect(p1, p2, myA, myB) ||
segmentsIntersect(p1, p2, myB, myC) ||
segmentsIntersect(p1, p2, myC, myA);
}
bool
Triangle::lineIntersectCircle(const Position& posA, const Position& posB, const Position& center, const double radius) const {
const double dx = posB.x() - posA.x();
const double dy = posB.y() - posA.y();
const double a = dx * dx + dy * dy;
const double b = 2 * (dx * (posA.x() - center.x()) + dy * (posA.y() - center.y()));
const double c = (posA.x() - center.x()) * (posA.x() - center.x()) + (posA.y() - center.y()) * (posA.y() - center.y()) - radius * radius;
const double discriminant = (b * b - 4 * a * c);
if (discriminant >= 0) {
const double sqrtDiscriminant = sqrt(discriminant);
const double t1 = (-b + sqrtDiscriminant) / (2 * a);
const double t2 = (-b - sqrtDiscriminant) / (2 * a);
return (t1 >= 0 && t1 <= 1) || (t2 >= 0 && t2 <= 1);
} else {
return false;
}
}