-
Notifications
You must be signed in to change notification settings - Fork 775
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Halfplane Intersection #90
Open
Chillee
wants to merge
48
commits into
kth-competitive-programming:main
Choose a base branch
from
Chillee:halfplane
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+432
−2
Open
Changes from 25 commits
Commits
Show all changes
48 commits
Select commit
Hold shift + click to select a range
93c9b26
Updated onSegment.h and both segment intersections
Chillee 3b6ac0e
Updated headers
Chillee 0a0261b
Updated some line width issues
Chillee bccd1cb
Fixed broken dependency
Chillee d2e700a
removed automatic epsilon checks
Chillee 59f8511
Put it on one line now
Chillee 1bdf870
Moved template up a line to fit parameters on one line
Chillee 2f32d63
Changed tabs to spaces so it'll align
Chillee 28456a1
Fixed some misc issues
Chillee b7a8157
Readded relevant description
Chillee ba9395d
Fixed formatting issue
Chillee fab7b2e
made change
Chillee e99191f
Added first commit of halfPlane
Chillee 95b770b
merged
Chillee 63425c3
Simplified/shortened the code
Chillee 888dd17
merged
Chillee ed75bf9
Fixed some issues
Chillee 8571390
Added some unit tests for halfplane
Chillee e5b85b7
Add failure example for MIT's halfplane code
Chillee 0a4e8d3
Added fuzz tests with SJTU code
Chillee 4df6e0e
Fit stuff within 63 columns
Chillee 19c70ac
Added option for long double in Halfplane tests
Chillee cbd17e8
Abstracted away the angle comparison
Chillee 2eee5c1
Added newline
Chillee a1d85bb
Updated header
Chillee b0b03a9
Responded to comments and tried a macro
Chillee 81f9cd3
removed extraneous deque variables
Chillee d283267
Updated description and such
Chillee 8c6bb7c
Shortened half plane through macros
Chillee bf838c6
Shortened half plane a bit
Chillee 1c47a37
Started fixing the bugs
Chillee 1aa4b39
Fixed bugs/found bugs
Chillee e61acd5
Doing some precision analysis
Chillee 4cb642a
Updated with current version
Chillee 50a3915
Shortened a bit
Chillee 3d7e6f0
removed binary
Chillee a8b3ec7
Removed some debug code
Chillee b6f7c82
merged
Chillee 8971c03
some debugging stuff
Chillee 5f4ff5a
Added some experimentation code
Chillee 3a94b90
merged
Chillee 74faa81
remove unintended
Chillee f3916db
removed test cases
Chillee 4330561
removed test cases
Chillee 908a313
Added my current guess about the condition for failure
Chillee 34c550e
Warning fixes
simonlindholm 5fb4588
Use .angle()
simonlindholm 26b50ee
Fix cmp to not say a < a
simonlindholm File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
|
||
/** | ||
* Author: chilli | ||
* Date: 2019-05-05 | ||
* License: CC0 | ||
* Source: https://github.com/zimpha/algorithmic-library/blob/master/computational-geometry/polygon.cc | ||
* Description: Computes the intersection of a set of half-planes. | ||
* Time: O(n \log n) | ||
* Status: fuzz-tested, submitted on https://maps19.kattis.com/problems/marshlandrescues | ||
* Usage: | ||
*/ | ||
#pragma once | ||
|
||
#include "Point.h" | ||
#include "sideOf.h" | ||
#include "lineIntersection.h" | ||
|
||
typedef Point<double> P; | ||
typedef array<P, 2> Line; | ||
#define sp(a) a[0], a[1] | ||
#define ang(a) atan2((a[1] - a[0]).y, (a[1] - a[0]).x) | ||
|
||
int angDiff(Line a, Line b) { return sgn(ang(a) - ang(b)); } | ||
bool cmp(Line a, Line b) { | ||
auto s = angDiff(a, b); | ||
return s == 0 ? sideOf(sp(b), a[0]) >= 0 : s < 0; | ||
} | ||
vector<P> halfPlaneIntersection(vector<Line> vs) { | ||
sort(all(vs), cmp); | ||
vector<Line> deq(sz(vs) + 5); | ||
vector<P> ans(sz(vs) + 5); | ||
deq[0] = vs[0]; | ||
int dh = 0, dt = 1, ah = 0, at = 0; | ||
for (int i = 1; i < sz(vs); ++i) { | ||
if (angDiff(vs[i], vs[i - 1]) == 0) continue; | ||
while (ah<at && sideOf(sp(vs[i]),ans[at-1]) < 0) at--,dt--; | ||
while (ah<at && sideOf(sp(vs[i]),ans[ah]) < 0) ah++,dh++; | ||
ans[at++] = lineInter(sp(deq[dt - 1]), sp(vs[i])).second; | ||
deq[dt++] = vs[i]; | ||
} | ||
while (ah<at && sideOf(sp(deq[dh]),ans[at-1]) < 0) at--,dt--; | ||
while (ah<at && sideOf(sp(deq[dt]),ans[ah]) < 0) ah++, dh++; | ||
if (dt - dh <= 2) return {}; | ||
ans[at++] = lineInter(sp(deq[dh]), sp(deq[dt - 1])).second; | ||
Chillee marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return {ans.begin() + ah, ans.begin() + at}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,254 @@ | ||
#include <bits/stdc++.h> | ||
using namespace std; | ||
|
||
#define rep(i, a, b) for (int i = a; i < int(b); ++i) | ||
#define trav(a, v) for (auto &a : v) | ||
#define all(x) x.begin(), x.end() | ||
#define sz(x) (int)(x).size() | ||
|
||
typedef long long ll; | ||
typedef pair<int, int> pii; | ||
typedef vector<int> vi; | ||
|
||
#include "../../content/geometry/HalfPlane.h" | ||
#include "../../content/geometry/PolygonArea.h" | ||
|
||
ostream &operator<<(ostream &os, P p) { return cout << "(" << p.x << "," << p.y << ")"; } | ||
|
||
namespace sjtu { | ||
typedef double T; | ||
const T EPS = 1e-8; | ||
inline int sign(T a) { return a < -EPS ? -1 : a > EPS; } | ||
struct Point { | ||
T x, y; | ||
Point() {} | ||
Point(T _x, T _y) : x(_x), y(_y) {} | ||
Point operator+(const Point &p) const { return Point(x + p.x, y + p.y); } | ||
Point operator-(const Point &p) const { return Point(x - p.x, y - p.y); } | ||
Point operator*(T d) const { return Point(x * d, y * d); } | ||
Point operator/(T d) const { return Point(x / d, y / d); } | ||
bool operator<(const Point &p) const { | ||
int c = sign(x - p.x); | ||
if (c) | ||
return c == -1; | ||
return sign(y - p.y) == -1; | ||
} | ||
T dot(const Point &p) const { return x * p.x + y * p.y; } | ||
T det(const Point &p) const { return x * p.y - y * p.x; } | ||
T alpha() const { return atan2(y, x); } | ||
T distTo(const Point &p) const { | ||
T dx = x - p.x, dy = y - p.y; | ||
return hypot(dx, dy); | ||
} | ||
T alphaTo(const Point &p) const { | ||
T dx = x - p.x, dy = y - p.y; | ||
return atan2(dy, dx); | ||
} | ||
// clockwise | ||
Point rotAlpha(const T &alpha, const Point &o = Point(0, 0)) const { | ||
T nx = cos(alpha) * (x - o.x) + sin(alpha) * (y - o.y); | ||
T ny = -sin(alpha) * (x - o.x) + cos(alpha) * (y - o.y); | ||
return Point(nx, ny) + o; | ||
} | ||
Point rot90() const { return Point(-y, x); } | ||
Point unit() { return *this / abs(); } | ||
void read() { scanf("%lf%lf", &x, &y); } | ||
T abs() { return hypot(x, y); } | ||
T abs2() { return x * x + y * y; } | ||
void write() { cout << "(" << x << "," << y << ")" << endl; } | ||
}; | ||
#define cross(p1, p2, p3) ((p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y)) | ||
#define crossOp(p1, p2, p3) sign(cross(p1, p2, p3)) | ||
|
||
Point isSS(Point p1, Point p2, Point q1, Point q2) { | ||
T a1 = cross(q1, q2, p1), a2 = -cross(q1, q2, p2); | ||
return (p1 * a2 + p2 * a1) / (a1 + a2); | ||
} | ||
struct Border { | ||
Point p1, p2; | ||
T alpha; | ||
void setAlpha() { alpha = atan2(p2.y - p1.y, p2.x - p1.x); } | ||
void read() { | ||
p1.read(); | ||
p2.read(); | ||
setAlpha(); | ||
} | ||
}; | ||
|
||
int n; | ||
const int MAX_N_BORDER = 20000 + 10; | ||
Border border[MAX_N_BORDER]; | ||
|
||
bool operator<(const Border &a, const Border &b) { | ||
int c = sign(a.alpha - b.alpha); | ||
if (c != 0) | ||
return c == 1; | ||
return crossOp(b.p1, b.p2, a.p1) >= 0; | ||
} | ||
|
||
bool operator==(const Border &a, const Border &b) { return sign(a.alpha - b.alpha) == 0; } | ||
|
||
void add(T x, T y, T nx, T ny) { | ||
border[n].p1 = Point(x, y); | ||
border[n].p2 = Point(nx, ny); | ||
border[n].setAlpha(); | ||
n++; | ||
} | ||
|
||
Point isBorder(const Border &a, const Border &b) { return isSS(a.p1, a.p2, b.p1, b.p2); } | ||
|
||
Border que[MAX_N_BORDER]; | ||
int qh, qt; | ||
|
||
bool check(const Border &a, const Border &b, const Border &me) { | ||
Point is = isBorder(a, b); | ||
return crossOp(me.p1, me.p2, is) > 0; | ||
} | ||
|
||
void convexIntersection() { | ||
qh = qt = 0; | ||
sort(border, border + n); | ||
n = unique(border, border + n) - border; | ||
for (int i = 0; i < n; ++i) { | ||
Border cur = border[i]; | ||
while (qh + 1 < qt && !check(que[qt - 2], que[qt - 1], cur)) | ||
--qt; | ||
while (qh + 1 < qt && !check(que[qh], que[qh + 1], cur)) | ||
++qh; | ||
que[qt++] = cur; | ||
} | ||
while (qh + 1 < qt && !check(que[qt - 2], que[qt - 1], que[qh])) | ||
--qt; | ||
while (qh + 1 < qt && !check(que[qh], que[qh + 1], que[qt - 1])) | ||
++qh; | ||
} | ||
|
||
T calcArea() { | ||
static Point ps[MAX_N_BORDER]; | ||
int cnt = 0; | ||
|
||
if (qt - qh <= 2) { | ||
return 0; | ||
} | ||
|
||
for (int i = qh; i < qt; ++i) { | ||
int next = i + 1 == qt ? qh : i + 1; | ||
ps[cnt++] = isBorder(que[i], que[next]); | ||
} | ||
|
||
T area = 0; | ||
for (int i = 0; i < cnt; ++i) { | ||
area += ps[i].det(ps[(i + 1) % cnt]); | ||
} | ||
area /= 2; | ||
area = fabsl(area); | ||
return area; | ||
} | ||
|
||
T halfPlaneIntersection(vector<Line> cur) { | ||
n = 0; | ||
for (auto i: cur) | ||
add(i[0].x, i[0].y, i[1].x, i[1].y); | ||
convexIntersection(); | ||
return calcArea(); | ||
} | ||
} // namespace sjtu | ||
|
||
const double INF = 100; | ||
const double EPS = 1e-8; | ||
void addInf(vector<Line> &res, double INF = INF) { | ||
vector<P> infPts({P(INF, INF), P(-INF, INF), P(-INF, -INF), P(INF, -INF)}); | ||
for (int i = 0; i < 4; i++) | ||
res.push_back({infPts[i], infPts[(i + 1) % 4]}); | ||
} | ||
P randPt(int lim = INF) { return P(rand() % lim, rand() % lim); } | ||
void test1() { | ||
vector<Line> t({{P(0, 0), P(5, 0)}, {P(5, -2), P(5, 2)}, {P(5, 2), P(2, 2)}, {P(0, 3), P(0, -3)}}); | ||
auto res = halfPlaneIntersection(t); | ||
assert(polygonArea2(res) == 20); | ||
addInf(t); | ||
res = halfPlaneIntersection(t); | ||
assert(polygonArea2(res) == 20); | ||
} | ||
void testInf() { | ||
vector<Line> t({{P(0, 0), P(5, 0)}}); | ||
addInf(t); | ||
auto res = halfPlaneIntersection(t); | ||
assert(polygonArea2(res) / (4 * INF * INF) == 1); | ||
t = vector<Line>({{P(0, 0), P(5, 0)}, {P(0, 0), P(0, 5)}}); | ||
addInf(t); | ||
res = halfPlaneIntersection(t); | ||
assert(polygonArea2(res) / (2 * INF * INF) == 1); | ||
} | ||
void testLine() { | ||
vector<Line> t({{P(0, 0), P(5, 0)}, {P(5, 0), P(0, 0)}}); | ||
addInf(t); | ||
auto res = halfPlaneIntersection(t); | ||
assert(sz(res) >= 2); | ||
assert(polygonArea2(res) == 0); | ||
} | ||
void testPoint() { | ||
vector<Line> t({{P(0, 0), P(5, 0)}, {P(5, 0), P(0, 0)}, {P(0, 0), P(0, 5)}, {P(0, 5), P(0, 0)}}); | ||
addInf(t); | ||
auto res = halfPlaneIntersection(t); | ||
assert(sz(res) >= 1); | ||
assert(polygonArea2(res) == 0); | ||
} | ||
void testEmpty() { | ||
vector<Line> t( | ||
{{P(0, 0), P(5, 0)}, {P(5, 0), P(0, 0)}, {P(0, 0), P(0, 5)}, {P(0, 5), P(0, 0)}, {P(0, 2), P(5, 2)}}); | ||
addInf(t); | ||
auto res = halfPlaneIntersection(t); | ||
assert(sz(res) == 0); | ||
} | ||
void testRandom() { | ||
int lim = 1e1; | ||
for (int i = 0; i < 10000; i++) { | ||
vector<Line> t; | ||
for (int i = 0; i < 6; i++) { | ||
Line cand{P(0, 0), P(0, 0)}; | ||
while (cand[0] == cand[1]) | ||
cand = {randPt(lim), randPt(lim)}; | ||
t.push_back(cand); | ||
} | ||
addInf(t, lim); | ||
auto res = halfPlaneIntersection(t); | ||
double area = sjtu::halfPlaneIntersection(t); | ||
double diff = abs(2 * area - polygonArea2(res)); | ||
if (diff > EPS) { | ||
cout << diff << ' ' << area << ' ' << endl; | ||
for (auto i : t) | ||
cout << i[0] << "->" << i[1] << ' '; | ||
cout << endl; | ||
for (auto i : res) | ||
cout << i << ','; | ||
cout << endl; | ||
} | ||
assert(diff < EPS); | ||
} | ||
} | ||
void testSelf() { | ||
int lim = 1e2; | ||
} | ||
|
||
int main() { | ||
test1(); | ||
testInf(); | ||
testLine(); | ||
testPoint(); | ||
testEmpty(); | ||
testRandom(); | ||
vector<Line> t({{P(0,0), P(5,0)}, {P(0,1), P(5,1)}}); | ||
reverse(all(t)); | ||
addInf(t); | ||
|
||
auto res = halfPlaneIntersection(t); | ||
for (auto i: res) cout<<i<<' '; | ||
cout<<endl; | ||
// Failure case for MIT's half plane code | ||
// vector<Line> t({{P(9, 8), P(9, 1)}, {P(3, 3), P(3, 5)}, {P(7, 6), P(0, 8)}}); | ||
// addInf(t); | ||
// cout << polygonArea2(halfPlaneIntersection(t)) << endl; | ||
// cout << MIT::Intersection_Area(convert(t)) << endl; | ||
cout << "Tests passed!" << endl; | ||
} |
Empty file.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain this better? What format is the input and output in?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not really sure how to describe the input... I wrote "Input is given as a set of planes, facing left.", but that seems like a pretty poor description.