def test_nonIntersectSimplePolygonsOnEndPoints(self): a = Point(0, 0) b = Point(-2, 2) c = Point(2, 2) d = Point(-2, -2) e = Point(2, -2) p = Polygon([a, b, c]) q = Polygon([a, d, e]) self.assertFalse(p.intersect(q))
def drawPolygon(self, p, fill): """Draw polygon onto canvas.""" if p is None: self.intersection = Polygon() self.canvas.create_text(self.canvas.winfo_width() / 2, self.canvas.winfo_height() / 2, text='no intersection', fill=fill) else: # create single list of (x,y) coordinates full = [None] * 2 * p.numEdges() idx = 0 for pt in p: full[idx] = pt.x() full[idx + 1] = self.toTk(pt.y()) idx += 2 self.canvas.create_polygon(full, fill=fill) # show points in gray for pt in p: self.canvas.create_oval(pt.x() - 4, self.toCartesian(pt.y() - 4), pt.x() + 4, self.toCartesian(pt.y() + 4), fill='gray')
def test_weakSimple(self): """See Wikipedia for entry on weakly simple polygon.""" m = Point(0, 0) a = Point(0, 4) b = Point(8, 4) c = Point(8, 0) d = Point(4, 0) e = Point(4, 1) f = Point(6, 1) g = Point(6, 3) h = Point(2, 3) j = Point(2, 1) k = Point(4, 1) # same as e l = Point(4, 0) # same as d p = Polygon([a, b, c, d, e, f, g, h, j, k, l, m]) self.assertTrue(p.simple())
def test_slide(self): """Example to use with slide presentation.""" square = Polygon([ Point(0, 8), Point(0, 0), Point(8, 0), Point(8, 8)]) triangle = Polygon([ Point(8, 10), Point(4, 6), Point(12, 6)]) self.assertTrue (triangle.convex()) self.assertTrue (square.convex()) p = convexIntersect(square, triangle) q = convexIntersect(triangle, square) self.assertEqual(p, q) real = Polygon([ Point(8, 6), Point(8, 8), Point(6, 8), Point(4, 6) ]) self.assertTrue (samePolygon(p, real)) self.assertTrue (samePolygon(real, p))
def test_defective(self): """ This test case detected defect in algorithm. Resolving it explained how to choose whether to advance p or q before an intersection had been discovered. """ p = Polygon([ Point(241, 243), Point(353, 210), Point(393, 245), Point(375, 398), Point(257, 303) ]) q = Polygon([ Point(108, 189), Point(268, 116), Point(456, 180), Point(434, 226), Point(125, 486) ]) self.assertTrue(p.convex()) self.assertTrue(q.convex()) i1 = convexIntersect(p, q) i2 = convexIntersect(q, p) self.assertTrue(samePolygon(i1, i2))
def __init__(self, master): """App to construct polygon by successive left-mouse clicks.""" master.title( "Left press to add point to polygon. Right press to start new one." ) self.master = master # keep track of polygons being created self.polygons = [Polygon()] self.canvas = Canvas(master, width=512, height=512) self.canvas.bind("<Button-1>", self.add) self.canvas.bind("<Button-2>", self.finalize) self.canvas.pack()
def test_enclosing(self): """Detect when polygon wholly contains another polygon.""" square = Polygon( [Point(-8, -8), Point(8, -8), Point(8, 8), Point(-8, 8)]) triangle = Polygon([Point(-3, 0), Point(3, 0), Point(0, 3)]) self.assertTrue(triangle.convex()) self.assertTrue(square.convex()) p = convexIntersect(square, triangle) q = convexIntersect(triangle, square) self.assertEqual(p, q) real = Polygon([Point(-3, 0), Point(3, 0), Point(0, 3)]) self.assertTrue(samePolygon(p, real)) self.assertTrue(samePolygon(real, p))
def test_noIntersection(self): """Detect no intersection.""" square = Polygon( [Point(-8, -8), Point(8, -8), Point(8, 8), Point(-8, 8)]) triangle = Polygon([Point(23, 0), Point(25, 0), Point(24, 1)]) self.assertTrue(triangle.convex()) self.assertTrue(square.convex()) p = convexIntersect(square, triangle) q = convexIntersect(triangle, square) self.assertEqual(p, q) self.assertIsNone(p)
def test_intersectNoPointsInCommon(self): square = Polygon( [Point(-2, -2), Point(2, -2), Point(2, 2), Point(-2, 2)]) triangle = Polygon([Point(-3, 0), Point(3, 0), Point(0, 3)]) self.assertTrue(triangle.convex()) self.assertTrue(square.convex()) p = convexIntersect(square, triangle) q = convexIntersect(triangle, square) self.assertTrue(samePolygon(p, q)) real = Polygon([ Point(2, 0), Point(2, 1), Point(1, 2), Point(-1, 2), Point(-2, 1), Point(-2, 0) ]) self.assertTrue(samePolygon(p, real)) self.assertTrue(samePolygon(real, p))
def test_intersect(self): # Note: only works if convex polygons in # normal form, with points/edges in counter-clockwise # fashion. square = Polygon( [Point(-2, -2), Point(2, -2), Point(2, 2), Point(-2, 2)]) triangle = Polygon([Point(-2, -2), Point(2, -2), Point(0, 4)]) self.assertTrue(triangle.convex()) self.assertTrue(square.convex()) p = convexIntersect(square, triangle) q = convexIntersect(triangle, square) self.assertTrue(samePolygon(p, q)) real = Polygon( [Point(2 / 3, 2), Point(-2 / 3, 2), Point(-2, -2), Point(2, -2)]) self.assertTrue(samePolygon(p, real)) self.assertTrue(samePolygon(real, p))
def computeHull (points): """ Compute the convex hull for given points and return as polygon. Returned polygon is in 'counter-clockwise' fashion, with the interior "to the left" of each edge. """ # sort by x coordinate (and if ==, by y coordinate). n = len(points) points = sorted(points, key=lambda pt:[pt.x(), pt.y()]) if n < 3: return Polygon(points) # Compute upper hull by starting with rightmost two points upper = Polygon ([points[-1], points[-2]]) for i in range(n-3, -1, -1): upper.add (points[i].x(), points[i].y()) while upper.numPoints() >=3 and lastThreeNonLeft(upper): upper.remove(-2) # Compute lower hull by starting with leftmost two points lower = Polygon ([points[0], points[1]]) for i in range(2, n): lower.add (points[i].x(), points[i].y()) while lower.numPoints() >=3 and lastThreeNonLeft(lower): lower.remove(-2) # Merge into upper (skip first and last to avoid duplication) and return. upper.remove(-1) upper.remove(0) for pt in upper: lower.add(pt.x(), pt.y()) return lower
def test_simpleonvexShape(self): p = Polygon([Point(-2, 2), Point(0, -2), Point(2, 2)]) self.assertTrue(p.convex())
def test_nonConvexShape(self): p = Polygon([Point(-2, 2), Point(0, -2), Point(2, 2), Point(0, 0)]) self.assertFalse(p.convex())
def convexIntersect(p, q): """ Compute and return polygon resulting from the intersection of two convext polygons, p and q. """ intersection = Polygon() pn = p.numEdges() qn = q.numEdges() k = 1 inside = None # can't know inside until intersection first = None # remember 1st intersection to know when to stop firstp = pe = p.edges()[0] # get first edge of p and q firstq = qe = q.edges()[0] while k < 2*(pn + qn): pt = pe.intersect(qe) if pt is not None: if first == None: first = pt elif pt == first: # stop when find first intersection again break intersection.add(pt.x(), pt.y()) if inhalfplane(pe.tail(), qe): inside = p else: inside = q # Identify relationship between edges; either we advance # p or we advance q, based on whether p's current edge # is aiming at qe (or vice versa). advancep = advanceq = False if (aim(pe,qe) and aim(qe,pe)) or (not aim(pe,qe) and not aim(qe,pe)): if inside is p: advanceq = True elif inside is q: advancep = True else: # no intersection yet. Choose based on # which one is "outside" if inhalfplane(pe.tail(), qe): advanceq = True else: advancep = True elif aim(pe, qe): advancep = True elif aim(qe, pe): advanceq = True ## if aim(pe, qe): ## if aim(qe, pe): ## if inside is p: ## advanceq = True ## elif inside is q: ## advancep = True ## else: ## advancep = True # arbitrary pick ## else: ## advancep = True ## else: ## if aim(qe, pe): ## advanceq = True ## else: ## if inside is p: ## advanceq = True ## elif inside is q: ## advancep = True ## else: ## advancep = True # arbitrary pick if advancep: if inside is p: intersection.add(pe.tail().x(), pe.tail().y()) pe = pe.next() elif advanceq: if inside is q: intersection.add(qe.tail().x(), qe.tail().y()) qe = qe.next() k += 1 if intersection.numPoints() == 0: if containedWithin(firstp.tail(), q): return p elif containedWithin(firstq.tail(), p): return q else: return None # Return computed intersection return intersection
def clear(self, event): """Clear all polygons and start again.""" self.polygons = [Polygon()] self.visit()
def finalize(self, event): """Finalize polygon and add a new one.""" self.polygons.insert(0, Polygon()) self.visit()