2D Misc

Define

```#include <bits/stdc++.h>
using namespace std;
// this can be safely changed to long double
typedef double ld;

const ld INF = 1e100;
const ld EPS = 1e-12;
const ld PI = acos(-1.0);

// -1: less than 0
// 1: greater than 0
inline int dcmp(ld x) {
return fabs(x) < EPS ? 0 : (x > 0 ? 1 : -1);
}

struct PT {
ld x, y;
PT() { x = 0; y = 0; }
PT(ld x, ld y): x(x), y(y) {}
PT(const PT &p) : x(p.x), y(p.y) {}
PT operator + (const PT &p) const { return PT(x + p.x, y + p.y); }
PT operator - (const PT &p) const { return PT(x - p.x, y - p.y); }
PT operator * (ld c) const { return PT(x * c, y * c); }
PT operator / (ld c) const { return PT(x / c, y / c); }
inline int operator < (const PT& p) const {
return dcmp(x - p.x) < 0 || (dcmp(x - p.x) == 0 && dcmp(y - p.y) < 0);
}
inline bool operator == (const PT& p) const { return dcmp(x - p.x) == 0 && dcmp(y - p.y) == 0; }
inline bool operator != (const PT& p) const { return !((*this) == p); }
};

ld dot(PT p, PT q) { return p.x * q.x + p.y * q.y; }
ld dist2(PT p, PT q) { return dot(p - q, p - q); }
ld cross(PT p, PT q) { return p.x * q.y - p.y * q.x; }
ostream &operator << (ostream &os, const PT &p) {
return (os << "(" << p.x << "," << p.y << ")");
}
istream &operator >> (istream &is, PT &p) {
return (is >> p.x >> p.y);
}
```

Basics

Rotate a point CCW or CW around the origin

```PT RotateCCW90(PT p) { return PT(-p.y, p.x); }
PT RotateCW90(PT p) { return PT(p.y, -p.x); }
PT RotateCCW(PT p, ld t) {
return PT(p.x * cos(t) - p.y * sin(t), p.x * sin(t) + p.y * cos(t));
}
```

Project point \$c\$ onto line through \$a\$ and \$b\$, assuming \$a \ne b\$

```PT ProjectPointLine(PT a, PT b, PT c) {
return a + (b - a) * dot(c - a, b - a) / dot(b - a, b - a);
}
```

Project point \$c\$ onto line segment through \$a\$ and \$b\$

```PT ProjectPointSegment(PT a, PT b, PT c) {
ld r = dot(b - a, b - a);
if (dcmp(r) == 0) return a;
r = dot(c - a, b - a) / r;
if (r < 0) return a;
if (r > 1) return b;
return a + (b - a) * r;
}
```

Is point \$p\$ on line through \$a\$ and \$b\$?

```inline bool PointOnLine(PT a, PT b, PT p) { return dcmp(cross(a - p, b - p)) == 0; }
```

Is point \$p\$ on segment from \$a\$ to \$b\$?

```inline bool PointOnSegment(PT a, PT b, PT p) { return PointOnLine(a, b, p) &&
dcmp(dot(a - p, b - p)) <= 0; }
```
```ld DistanceBetweenPoints(PT a, PT b) { return sqrt(dist2(a, b)); }
ld DistancePointSegment(PT a, PT b, PT c) { return sqrt(dist2(c, ProjectPointSegment(a, b, c))); }
// 3D: compute distance between point(x, y, z) and plane ax+by+cz=d
ld DistancePointPlane(ld x, ld y, ld z, ld a, ld b, ld c, ld d) {
return fabs(a * x + b * y + c * z - d) / sqrt(a * a + b * b + c * c);
}
```

Determine if lines from \$a\$ to \$b\$ and \$c\$ to \$d\$ and parallel or collinear

```bool LinesParallel(PT a, PT b, PT c, PT d) { return dcmp(cross(b - a, c - d)) == 0; }
bool LinesCollinear(PT a, PT b, PT c, PT d) {
return LinesParallel(a, b, c, d) &&
dcmp(cross(a - b, a - c)) == 0 && dcmp(cross(c - d, c - a)) == 0;
}
```

Determine if line segment from \$a\$ to \$b\$ intersects with line segment from \$c\$ to \$d\$ (without points)

```bool SegmentsProperIntersect(PT a, PT b, PT c, PT d) {
ld c1 = cross(b - a, c - a), c2 = cross(b - a, d - a),
c3 = cross(d - c, a - c), c4 = cross(d - c, b - c);
return dcmp(c1) * dcmp(c2) < 0 && dcmp(c3) * dcmp(c4) < 0;
}
```

Determine if line segment from \$a\$ to \$b\$ intersects with line segment from \$c\$ to \$d\$

```bool SegmentsIntersect(PT a, PT b, PT c, PT d) {
return PointOnSegment(c, d, a) || PointOnSegment(c, d, b) ||
PointOnSegment(a, b, c) || PointOnSegment(a, b, d) ||
SegmentsProperIntersect(a, b, c, d);
}
```

Compute intersection of line passing through \$a\$ and \$b\$ with line passing through \$c\$ and \$d\$, assuming that unique intersection exists; for segment intersection, check if semgents intersect first

```PT ComputeLineIntersection(PT a, PT b, PT c, PT d) {
b = b - a; d = c - d; c = c - a;
assert (dcmp(dot(b, b)) > 0 && dcmp(dot(d, d)) > 0);
return a + b * cross(c, d) / cross(b, d);
}
```

Compute center of circle given three points

```PT ComputeCircleCenter(PT a, PT b, PT c) {
b = (a + b) / 2;
c = (a + c) / 2;
return ComputeLineIntersection(b, b + RotateCW90(a - b), c , c + RotateCW90(a - c));
}
```

Determine if point is in a possibly non-convex polygon (by William Randolph Franklin); returns 1 for strictly interior points, 0 for strictly exterior points, and 0 or 1 for the remaining points. Note that it is possible to convert this into an exact test using integer arithmetic by taking care of the division appropriately (making sure to deal with signs properly) and then by writing exact tests for checking point on polygon boundary

```bool PointInPolygon(const vector<PT> &p, PT q) {
bool c = 0;
for (unsigned i = 0; i < p.size(); ++i) {
unsigned j = (i + 1) % p.size();
if (((dcmp(p[i].y - q.y) <= 0 && dcmp(q.y - p[j].y) < 0) ||
(dcmp(p[j].y - q.y) <= 0 && dcmp(q.y - p[i].y) < 0)) &&
dcmp(q.x - (p[i].x + (p[j].x - p[i].x) * (q.y - p[i].y) / (p[j].y - p[i].y))) < 0)
c = !c;
}
return c;
}
```

Determine if point is on the boundary of a polygon

```bool PointOnPolygon (const vector<PT> &p, PT q){
for (unsigned i = 0; i < p.size(); ++i)
if (PointOnSegment(p[i], p[(i + 1) % p.size()], q))
return true;
return false;
}
```

Compute intersection of line through points \$a\$ and \$b\$ with circle centered at \$c\$ with radius \$r > 0\$

```vector<PT> CircleLineIntersection(PT a, PT b, PT c, ld r) {
vector<PT> ret;
b = b - a;
a = a - c;
ld A = dot(b, b), B = dot(a, b), C = dot(a, a) - r * r;
ld D = B * B - A * C;
if (dcmp(D) < 0) return ret;
ret.push_back(c + a + b * (-B + sqrt(D + EPS)) / A);
if (dcmp(D) > 0)
ret.push_back(c + a + b * (-B - sqrt(D)) / A);
return ret;
}
```

Compute intersection of circle centered at \$a\$ with radius \$r\$ with circle centered at \$b\$ with radius \$R\$.

If they have the same center, nothing will be returned.

```vector<PT> CircleCircleIntersection(PT a, PT b, ld r, ld R) {
vector<PT> ret;
ld d = sqrt(dist2(a, b));
if (dcmp(d) == 0 || dcmp(d - (r + R)) > 0 || dcmp(d + min(r, R) - max(r, R)) < 0) return ret;
ld x = (d * d - R * R + r * r) / (2 * d);
ld y = sqrt(r * r - x * x);
PT v = (b - a) / d;
ret.push_back(a + v * x + RotateCCW90(v) * y);
if (dcmp(y) > 0)
ret.push_back(a + v * x - RotateCCW90(v) * y);
return ret;
}
```

Counterclockwise fashion. Note that the centroid is often known as the "center of gravity" or "center of mass".

```ld ComputeSignedArea(const vector<PT> &p) {
ld area = 0;
for (unsigned i = 0; i < p.size(); i++) {
unsigned j = (i + 1) % p.size();
area += p[i].x * p[j].y - p[j].x * p[i].y;
}
return area / 2.0;
}

ld ComputeArea(const vector<PT> &p) {
return fabs(ComputeSignedArea(p));
}
```
```PT ComputeCentroid(const vector<PT> &p) {
PT c(0, 0);
ld scale = 6.0 * ComputeSignedArea(p);
for (unsigned i = 0; i < p.size(); i++) {
unsigned j = (i + 1) % p.size();
c = c + (p[i] + p[j]) * (p[i].x * p[j].y - p[j].x * p[i].y);
}
return c / scale;
}
```

Tests whether or not a given polygon (in CW or CCW order) is simple

```bool IsSimple(const vector <PT> &p) {
for (unsigned i = 0; i < p.size(); i++) {
for (unsigned k = i + 1; k < p.size(); k++) {
unsigned j = (i + 1) % p.size();
unsigned l = (k + 1) % p.size();
if (i == l || j == k) continue;
if (SegmentsIntersect(p[i], p[j], p[k], p[l]))
return false;
}
}
return true;
}
```

More

API 尚不统一。

```int relation(Point p, Circle a) {//点和圆的关系
//0:圆外 1:圆上 2:圆内
double d = dis(p, a.p);
if (dcmp(d - a.r) == 0) return 1;
return (dcmp(d - a.r) < 0 ? 2 : 0);
}

int relation(Line a, Circle b) {//直线和圆的关系
//0:相离 1:相切 2:相交
double p = point_to_line(b.p, a);
if (dcmp(p - b.r) == 0) return 1;
return (dcmp(p - b.r) < 0 ? 2 : 0);
}

int relation(Circle a, Circle v) {//两圆的位置关系
//1:内含 2:内切 3:相交 4:外切 5:相离
double d = dis(a.p, v.p);
if (dcmp(d - a.r - v.r) > 0) return 5;
if (dcmp(d - a.r - v.r) == 0) return 4;
double l = fabs(a.r - v.r);
if (dcmp(d - a.r - v.r) < 0 && dcmp(d - l) > 0) return 3;
if (dcmp(d - l) == 0) return 2;
if (dcmp(d - l) < 0) return 1;
assert (0);
}

double circle_traingle_area(Point a, Point b, Circle c) {//圆心三角形的面积
//a.output (), b.output (), c.output ();
Point p = c.p;
double r = c.r; //cout << cross (p-a, p-b) << endl;
if (dcmp(cross(p - a, p - b)) == 0) return 0;
Point q[5];
int len = 0;
q[len++] = a;
Line l(a, b);
Point p1, p2;
if (line_cirlce_intersection(l, c, q[1], q[2]) == 2) {
if (dcmp(dot(a - q[1], b - q[1])) < 0) q[len++] = q[1];
if (dcmp(dot(a - q[2], b - q[2])) < 0) q[len++] = q[2];
}
q[len++] = b;
if (len == 4 && dcmp(dot(q[0] - q[1], q[2] - q[1])) > 0)
swap(q[1], q[2]);
double res = 0;
for (int i = 0; i < len - 1; i++) {
if (relation(q[i], c) == 0 || relation(q[i + 1], c) == 0) {
double arg = rad(q[i] - p, q[i + 1] - p);
res += r * r * arg / 2.0;
} else {
res += fabs(cross(q[i] - p, q[i + 1] - p)) / 2;
}
}
return res;
}
```

Test Example

```int main() {
// expected: (-5,2)
cerr << RotateCCW90(PT(2,5)) << endl;
// expected: (5,-2)
cerr << RotateCW90(PT(2,5)) << endl;
// expected: (-5,2)
cerr << RotateCCW(PT(2,5),M_PI/2) << endl;
// expected: (5,2)
cerr << ProjectPointLine(PT(-5,-2), PT(10,4), PT(3,7)) << endl;
// expected: (5,2) (7.5,3) (2.5,1)
cerr << ProjectPointSegment(PT(-5,-2), PT(10,4), PT(3,7)) << " "
<< ProjectPointSegment(PT(7.5,3), PT(10,4), PT(3,7)) << " "
<< ProjectPointSegment(PT(-5,-2), PT(2.5,1), PT(3,7)) << endl;
// expected: 6.78903
cerr << DistancePointPlane(4,-4,3,2,-2,5,-8) << endl;
// expected: 1 0 1
cerr << LinesParallel(PT(1,1), PT(3,5), PT(2,1), PT(4,5)) << " "
<< LinesParallel(PT(1,1), PT(3,5), PT(2,0), PT(4,5)) << " "
<< LinesParallel(PT(1,1), PT(3,5), PT(5,9), PT(7,13)) << endl;
// expected: 0 0 1
cerr << LinesCollinear(PT(1,1), PT(3,5), PT(2,1), PT(4,5)) << " "
<< LinesCollinear(PT(1,1), PT(3,5), PT(2,0), PT(4,5)) << " "
<< LinesCollinear(PT(1,1), PT(3,5), PT(5,9), PT(7,13)) << endl;
// expected: 1 1 1 0
cerr << SegmentsIntersect(PT(0,0), PT(2,4), PT(3,1), PT(-1,3)) << " "
<< SegmentsIntersect(PT(0,0), PT(2,4), PT(4,3), PT(0,5)) << " "
<< SegmentsIntersect(PT(0,0), PT(2,4), PT(2,-1), PT(-2,1)) << " "
<< SegmentsIntersect(PT(0,0), PT(2,4), PT(5,5), PT(1,7)) << endl;
// expected: (1,2)
cerr << ComputeLineIntersection(PT(0,0), PT(2,4), PT(3,1), PT(-1,3)) << endl;
// expected: (1,1)
cerr << ComputeCircleCenter(PT(-3,4), PT(6,1), PT(4,5)) << endl;
vector<PT> v;
v.push_back(PT(0,0));
v.push_back(PT(5,0));
v.push_back(PT(5,5));
v.push_back(PT(0,5));
// expected: 1 1 1 0 0
cerr << PointInPolygon(v, PT(2,2)) << " "
<< PointInPolygon(v, PT(2,0)) << " "
<< PointInPolygon(v, PT(0,2)) << " "
<< PointInPolygon(v, PT(5,2)) << " "
<< PointInPolygon(v, PT(2,5)) << endl;
// expected: 0 1 1 1 1
cerr << PointOnPolygon(v, PT(2,2)) << " "
<< PointOnPolygon(v, PT(2,0)) << " "
<< PointOnPolygon(v, PT(0,2)) << " "
<< PointOnPolygon(v, PT(5,2)) << " "
<< PointOnPolygon(v, PT(2,5)) << endl;
// expected: (1,6)
// (5,4) (4,5)
// blank line
// (4,5) (5,4)
// blank line
// (4,5) (5,4)
vector<PT> u = CircleLineIntersection(PT(0,6), PT(2,6), PT(1,1), 5);
for (unsigned i = 0; i < u.size(); i++) cerr << u[i] << " ";
cerr << endl;
u = CircleLineIntersection(PT(0,9), PT(9,0), PT(1,1), 5);
for (unsigned i = 0; i < u.size(); i++) cerr << u[i] << " ";
cerr << endl;
u = CircleCircleIntersection(PT(1,1), PT(10,10), 5, 5);
for (unsigned i = 0; i < u.size(); i++) cerr << u[i] << " ";
cerr << endl;
u = CircleCircleIntersection(PT(1,1), PT(8,8), 5, 5);
for (unsigned i = 0; i < u.size(); i++) cerr << u[i] << " ";
cerr << endl;
u = CircleCircleIntersection(PT(1,1), PT(4.5,4.5), 10, sqrt(2.0)/2.0);
for (unsigned i = 0; i < u.size(); i++) cerr << u[i] << " ";
cerr << endl;
u = CircleCircleIntersection(PT(1,1), PT(4.5,4.5), 5, sqrt(2.0)/2.0);
for (unsigned i = 0; i < u.size(); i++) cerr << u[i] << " ";
cerr << endl;
// area should be 5.0
// centroid should be (1.1666666, 1.166666)
PT pa[] = { PT(0,0), PT(5,0), PT(1,1), PT(0,5) };
vector<PT> p(pa, pa+4);
PT c = ComputeCentroid(p);
cerr << "Area: " << ComputeArea(p) << endl;
cerr << "Centroid: " << c << endl;
}
```