From 74eec1f384e4fa7387cc8b4935ef019655537bb3 Mon Sep 17 00:00:00 2001 From: projectPythonator Date: Wed, 29 Jul 2020 12:07:10 -0600 Subject: [PATCH 01/10] added in CH_andrew AGIS DANIELS EDITS I made this compressed version of the code from c++ and a python version on wikibooks toVec was moved to sub as toVec(a,b)== b-a tested this on 2 kattis problems my own version works on tested on 4 problems but it requires area and perimeter to work ccw and cross are almost the same in python as in c++ i added in a few things into the point class for it to solve the two convex hull problems on kattis that i tested on --- ch7/polygon.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/ch7/polygon.py b/ch7/polygon.py index 7108b2e..54cf62f 100644 --- a/ch7/polygon.py +++ b/ch7/polygon.py @@ -10,11 +10,19 @@ # double RAD_to_DEG(double r) { return r*180.0 / M_PI; } class point: - x = 0 # default values - y = 0 + #x = 0 # default values + #y = 0 def __init__(self, x, y): # constructor self.x = x self.y = y + + def __sub__(self, b): #tovec(a,b)==b-a + return point(self.x-b.x, self.y-b.y) + def __lt__(self, b): #self 0) +# note python i used class opperators for tovec (Agis Daniels) # // note: to accept collinear points, we have to change the `> 0' # // returns true if point r is on the left side of line pq # bool ccw(point p, point q, point r) { @@ -162,6 +173,18 @@ def perimeter(P): # return S; // return the result # } +#compressed version of the code below and the link below +#https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain +def CH_Andrew(ps): + P, H=sorted(set(ps)), [] + if len(P)<=1: return P # if only one unique point just return point + def f(B): #f is a mapping of the two loops since its dup code + for p in P: + while len(H)>B and not ccw(H[-2], H[-1], p): H.pop() + H.append(p) + H.pop() + f(1); P=P[::-1]; f(len(H)+1); return H #4 line low, rev, up, ret +#c++ implementation below # vector CH_Andrew(vector &Pts) { // overall O(n log n) # int n = Pts.size(), k = 0; # vector H(2*n); From 31559401cb694b49396fd20708583249ee0db09e Mon Sep 17 00:00:00 2001 From: projectPythonator Date: Thu, 30 Jul 2020 06:53:54 -0600 Subject: [PATCH 02/10] consistency Agis Daniels for consistency reasons i removed sub and re added vec ccw already used it in a difference file so i just used it from the points line .py as well < had a more elegant version from that file too so i used it here too retested and still holds ac on kattis --- ch7/polygon.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ch7/polygon.py b/ch7/polygon.py index 54cf62f..cf31122 100644 --- a/ch7/polygon.py +++ b/ch7/polygon.py @@ -16,10 +16,8 @@ def __init__(self, x, y): # constructor self.x = x self.y = y - def __sub__(self, b): #tovec(a,b)==b-a - return point(self.x-b.x, self.y-b.y) def __lt__(self, b): #self 0) +def ccw(p, q, r): return (cross(toVec(p,q),toVec(p,r)) > 0) # note python i used class opperators for tovec (Agis Daniels) # // note: to accept collinear points, we have to change the `> 0' # // returns true if point r is on the left side of line pq From fc12bae0c9e42fcd862f128ddc28f8996de86329 Mon Sep 17 00:00:00 2001 From: projectPythonator Date: Thu, 30 Jul 2020 07:17:34 -0600 Subject: [PATCH 03/10] added in the area Agis Daniels nice one liner for area i called cross product function to avoid the need to retype the code again should help avoid typos when implementing it from paper the less than for point seems to work nicely with floats :) also added a oneliner version of the perimeter function above the code as well --- ch7/polygon.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ch7/polygon.py b/ch7/polygon.py index cf31122..2db6ddd 100644 --- a/ch7/polygon.py +++ b/ch7/polygon.py @@ -16,8 +16,7 @@ def __init__(self, x, y): # constructor self.x = x self.y = y - def __lt__(self, b): #self &P) { # double ans = 0.0; @@ -186,8 +188,8 @@ def CH_Andrew(ps): def f(B): #f is a mapping of the two loops since its dup code for p in P: while len(H)>B and not ccw(H[-2], H[-1], p): H.pop() - H.append(p) - H.pop() + H.append(p) + H.pop() f(1); P=P[::-1]; f(len(H)+1); return H #4 line low, rev, up, ret #c++ implementation below # vector CH_Andrew(vector &Pts) { // overall O(n log n) From e3408724bbf780e0d1fd9bd6dd48d444d1fb4348 Mon Sep 17 00:00:00 2001 From: projectPythonator Date: Thu, 30 Jul 2020 07:35:37 -0600 Subject: [PATCH 04/10] added in dot normsq and angle Agis Daniels they already existed in other files polyline.py is where i took these ones from also why is origin needed in the alt version of polygon area ?? --- ch7/polygon.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ch7/polygon.py b/ch7/polygon.py index 2db6ddd..28272c3 100644 --- a/ch7/polygon.py +++ b/ch7/polygon.py @@ -60,10 +60,18 @@ def area(P): # return fabs(ans)/2.0; // only do / 2.0 here # } + + +def dot(a, b): return a.x * b.x + a.y * b.y # double dot(vec a, vec b) { return (a.x*b.x + a.y*b.y); } +def norm_sq(v): return v.x * v.x + v.y * v.y # double norm_sq(vec v) { return v.x*v.x + v.y*v.y; } +def angle(a, o, b): + oa = toVec(o, a) + ob = toVec(o, b) + return math.acos(dot(oa, ob) / math.sqrt(norm_sq(oa) * norm_sq(ob))) # double angle(point a, point o, point b) { // returns angle aob in rad # vec oa = toVec(o, a), ob = toVec(o, b); # return acos(dot(oa, ob) / sqrt(norm_sq(oa) * norm_sq(ob))); } From f3c5849c4b9227a03f8b90d64a498058840d9a24 Mon Sep 17 00:00:00 2001 From: projectPythonator Date: Thu, 30 Jul 2020 07:43:46 -0600 Subject: [PATCH 05/10] added in code for collinear Agis Daniels already existed in pointlines file so i just used the one from there --- ch7/polygon.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ch7/polygon.py b/ch7/polygon.py index 28272c3..29b6507 100644 --- a/ch7/polygon.py +++ b/ch7/polygon.py @@ -3,6 +3,8 @@ import math + +EPS = 1e-9 # const double EPS = 1e-9; # double DEG_to_RAD(double d) { return d*M_PI / 180.0; } @@ -96,6 +98,7 @@ def ccw(p, q, r): return (cross(toVec(p,q),toVec(p,r)) > 0) # return cross(toVec(p, q), toVec(p, r)) > 0; # } +def collinear(p, q, r): return abs(cross(toVec(p, q), toVec(p, r))) < EPS # // returns true if point r is on the same line as the line pq # bool collinear(point p, point q, point r) { # return fabs(cross(toVec(p, q), toVec(p, r))) < EPS; From 35386a955aa8bf778195ca1a11cc7e99cb367f18 Mon Sep 17 00:00:00 2001 From: projectPythonator Date: Thu, 30 Jul 2020 07:58:31 -0600 Subject: [PATCH 06/10] added in isConvex Agis Daniels the code i use in my own implementation is similar so i can just swap out my call with ccw as its similar to the c++ code :) --- ch7/polygon.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ch7/polygon.py b/ch7/polygon.py index 29b6507..b227732 100644 --- a/ch7/polygon.py +++ b/ch7/polygon.py @@ -104,6 +104,14 @@ def collinear(p, q, r): return abs(cross(toVec(p, q), toVec(p, r))) < EPS # return fabs(cross(toVec(p, q), toVec(p, r))) < EPS; # } +def isConvex(P): + e,s=len(P),1 + if e<4: retrun False + t1=ccw(P[0],P[1],P[2]) # first turn + for i in range(s, e-1): + if ccw(P[i],P[i+1],P[1 if i+2==n else i+2]) != t1: + return False + return True # // returns true if we always make the same turn # // while examining all the edges of the polygon one by one # bool isConvex(const vector &P) { From 989a01f2d0ac9f9ed7cf38467fbc271ac9ebf59c Mon Sep 17 00:00:00 2001 From: projectPythonator Date: Thu, 30 Jul 2020 08:31:13 -0600 Subject: [PATCH 07/10] insidePolygon Agis Daniels im a bit iffy on insidePolygon as i tried to implment it before both like this and super mega compressed version and it had some errors for some reason the second loop had errors i mean will need to ask suhendry about it --- ch7/polygon.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ch7/polygon.py b/ch7/polygon.py index b227732..8eb7d28 100644 --- a/ch7/polygon.py +++ b/ch7/polygon.py @@ -125,6 +125,18 @@ def isConvex(P): # return true; // otherwise -> convex # } +def insidePolygon(pt, P): + if len(P)<4: return -1 + n, ans, s=len(P), False, 0.0 + for i in range(n-1): + a, b=P[i], P[i+1] + if abs(dist(a, pt) + dist(pt, b) - dist(a, b)) < EPS: + ans=True + if ans: return 0 + for i in range(n-1): + a=angle(P[i], pt, P[i+1]) + s+= a if ccw(pt, P[i], P[i+1]) else -a + return 1 if abs(s) > math.pi else -1 # // returns 1/0/-1 if point p is inside/on (vertex/edge)/outside of # // either convex/concave polygon P # int insidePolygon(point pt, const vector &P) { From bd78be6ed2dc6c0935eb4f473cad76c7e830b641 Mon Sep 17 00:00:00 2001 From: projectPythonator Date: Sun, 2 Aug 2020 13:04:06 -0600 Subject: [PATCH 08/10] removed two lines Agis Daniels edit be sure to remove these two lines and set them to 0 in the __init__ function to my understanding those are class variables shared by all instances of the class as stated in this link https://docs.python.org/3/tutorial/classes.html#class-and-instance-variables --- ch7/polygon.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ch7/polygon.py b/ch7/polygon.py index 8eb7d28..44fb41b 100644 --- a/ch7/polygon.py +++ b/ch7/polygon.py @@ -12,8 +12,6 @@ # double RAD_to_DEG(double r) { return r*180.0 / M_PI; } class point: - #x = 0 # default values - #y = 0 def __init__(self, x, y): # constructor self.x = x self.y = y From 4c262ca29262e8391d70aa01130c1d10ed1c9b2b Mon Sep 17 00:00:00 2001 From: projectPythonator Date: Thu, 13 Aug 2020 15:29:10 -0600 Subject: [PATCH 09/10] rest of functions implmented I coded up the rest of the functions while trying to stay true to the source code from c++ i ran into some bugs for CH_Graham scan which i need help understanding --- ch7/polygon.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/ch7/polygon.py b/ch7/polygon.py index 44fb41b..fcc3271 100644 --- a/ch7/polygon.py +++ b/ch7/polygon.py @@ -104,7 +104,7 @@ def collinear(p, q, r): return abs(cross(toVec(p, q), toVec(p, r))) < EPS def isConvex(P): e,s=len(P),1 - if e<4: retrun False + if e<4: return False t1=ccw(P[0],P[1],P[2]) # first turn for i in range(s, e-1): if ccw(P[i],P[i+1],P[1 if i+2==n else i+2]) != t1: @@ -155,6 +155,10 @@ def insidePolygon(pt, P): # return fabs(sum) > M_PI ? 1 : -1; // 360d->in, 0d->out # } +def lineInterSectSeg(p,q,A,B): + a, b, c=B.y-A.y, A.x-B.x, cross(B,A) + u, v=abs(a*p.x+b*p.y+c), abs(a*q.x+b*q.y+x) + return point((p.x*v+q.x*u)/(u+v), (p.y*v+q.y*u)/(u+v)) # // compute the intersection point between line segment p-q and line A-B # point lineIntersectSeg(point p, point q, point A, point B) { # double a = B.y-A.y, b = A.x-B.x, c = B.x*A.y - A.x*B.y; @@ -163,6 +167,15 @@ def insidePolygon(pt, P): # return point((p.x*v + q.x*u) / (u+v), (p.y*v + q.y*u) / (u+v)); # } +def cutPolygon(A, B, Q): + P=[] + for i in range(len(Q)): + l1, l2=cross(toVec(A, B),toVec(A, Q[i])), 0 + if i!=len(Q)-1: l2=cross(toVec(A, B), toVec(A, Q[i+1])) + if l1>-EPS: P.append(Q[i]) + if l1*l2<-EPS: P.append(lineInterSectSeg(Q[i], Q[i+1], A, B)) + if P and P[-1]!=P[0]: P.append(P[0]) + return P # // cuts polygon Q along the line formed by point A->point B (order matters) # // (note: the last point must be the same as the first point) # vector cutPolygon(point A, point B, const vector &Q) { @@ -179,6 +192,30 @@ def insidePolygon(pt, P): # return P; # } +def CH_Graham(Pts): + P=[p for p in Pts] + n=len(P) + if n<4: + if P[0]!=P[-1]: P.append(P[0]) + return P + + def ccw_cmp(a, b): return ccw(P[0], a, b) + def cmp_class(f): + class K: + def __init__(F, o): F.pt=o + def __lt__(F, o): return f(F.pt, o.pt) + P0=P.index(min(P, key=lambda p: (p.y,-p.x))) + P[0],P[P0]=P[P0],P[0] + + P=[P[0]]+sorted(P[1:], key=cmp_class(ccw_cmp)) + + S, i=[P[-1],P[0]], 2 + while i CH_Graham(vector &Pts) { // overall O(n log n) # vector P(Pts); // copy all points # int n = (int)P.size(); @@ -289,7 +326,7 @@ def f(B): #f is a mapping of the two loops since its dup code # //1 P0 P2 # //0 1 2 3 4 5 6 7 8 9 - # P = cutPolygon(P[2], P[4], P); +P = cutPolygon(P[2], P[4], P); # printf("Perimeter = %.2lf\n", perimeter(P)); // smaller now, 29.15 # printf("Area = %.2lf\n", area(P)); // 40.00 @@ -302,8 +339,13 @@ def f(B): #f is a mapping of the two loops since its dup code # //2 | P_in | # //1 P0--------------P1 # //0 1 2 3 4 5 6 7 8 9 - - # P = CH_Graham(P); // now this is a rectangle +for p in P: + print(p.x,p.y) +P = CH_Graham(P); #// now this is a rectangle +print(len(P)) +for p in P: + print(p.x,p.y) +print(perimeter(P)) # printf("Perimeter = %.2lf\n", perimeter(P)); // precisely 28.00 # printf("Area = %.2lf\n", area(P)); // precisely 48.00 # printf("Is convex = %d\n", isConvex(P)); // true From 668d24c61ccbd4d0f72264a38bea91ae655a8fed Mon Sep 17 00:00:00 2001 From: projectPythonator Date: Thu, 13 Aug 2020 15:30:56 -0600 Subject: [PATCH 10/10] Update polygon.py --- ch7/polygon.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ch7/polygon.py b/ch7/polygon.py index fcc3271..a47f5a3 100644 --- a/ch7/polygon.py +++ b/ch7/polygon.py @@ -339,12 +339,7 @@ def f(B): #f is a mapping of the two loops since its dup code # //2 | P_in | # //1 P0--------------P1 # //0 1 2 3 4 5 6 7 8 9 -for p in P: - print(p.x,p.y) P = CH_Graham(P); #// now this is a rectangle -print(len(P)) -for p in P: - print(p.x,p.y) print(perimeter(P)) # printf("Perimeter = %.2lf\n", perimeter(P)); // precisely 28.00 # printf("Area = %.2lf\n", area(P)); // precisely 48.00