def run(self): for (p1, p2, p3) in itertools.combinations(self.point_list, 3): # Counterclockwise orientation of points. if orientation(p1, p2, p3) < 0: p2, p3 = p3, p2 elif orientation(p1, p2, p3) == 0: # collinear points continue triangle = Triangle(p1, p2, p3) if all(not triangle.in_circumcircle(p4) for p4 in self.point_list if p4 != p1 and p4 != p2 and p4 != p3): self.tc.insert(triangle)
def legalize(self, point, segment): #print ( "legalize {} {}".format(point, segment) ) # Trzeba znalezc trojkaty sasiadujace z krawedzia. tlist = self.tc.search(segment.center()) if len(tlist) == 1: # przypadek (i) de Berga # Nie przekrecamy krawedzi duzego trojkata, jest legalna. #print ( "big triangle segment" ) assert segment.pt1 in self.big_nodes and segment.pt2 in self.big_nodes return #print ( "tlist {}".format(tlist) ) assert len(tlist) == 2 t1 = tlist.pop() t2 = tlist.pop() if point in t2: # chcemy point in t1 t1, t2 = t2, t1 pt3 = t2.third_node(segment.pt1, segment.pt2) illegal = t1.in_circumcircle(pt3) if pt3 in self.big_nodes: #print ( "{} in big_nodes".format(pt3) ) # Trzeba sprawdzic, czy koniec krawedzi nie jest z big triangle. if segment.pt1 in self.big_nodes: # przypadek (iv) de Berga # Dokladnie dwa indeksy sa ujemne, jeden z krawedzi. illegal = False if segment.pt1 < pt3 else True #print ( "illegal 2 {}".format(illegal) ) elif segment.pt2 in self.big_nodes: # przypadek (iv) de Berga # Dokladnie dwa indeksy sa ujemne, jeden z krawedzi. illegal = False if segment.pt2 < pt3 else True #print ( "illegal 2 {}".format(illegal) ) else: # przypadek (iii) de Berga # Dokladnie jeden indeks ujemny (pt3), ale nie przy krawedzi. illegal = False # krawedz jest legalna #print ( "illegal 1 {}".format(illegal) ) else: # jeden koniec krawedzi moze byc z big triangle if segment.pt1 in self.big_nodes or segment.pt2 in self.big_nodes: # Przypadek (iii) de Berga, jeden indeks ujemny z krawedzi. illegal = True #print ( "illegal 1 {}".format(illegal) ) else: # cztery indeksy sa dodatnie, przypadek (ii) de Berga, pass # procedujemy normalnie if illegal: if orientation(point, segment.pt1, pt3) * orientation( point, segment.pt2, pt3) > 0: # Czworokat wklesly! Nie przekrecamy! #print ( "concave quadrilateral!" ) illegal = False if illegal: # jezeli krawedz nielegalna # Przekrecamy krawedz (edge flipping). #print ( "segment flip" ) self.tc.remove(t1) self.tc.remove(t2) self.tc.insert(Triangle(segment.pt1, point, pt3)) self.tc.insert(Triangle(segment.pt2, point, pt3)) self.legalize(point, Segment(segment.pt1, pt3)) self.legalize(point, Segment(segment.pt2, pt3))
def winding_number(polygon, point): """Return the winding number for a point.""" wn = 0 n = len(polygon.point_list) for i in xrange(n): a = polygon.point_list[i] b = polygon.point_list[(i + 1) % n] if a.y <= point.y: if b.y > point.y and orientation(a, b, point) > 0: wn += 1 # upward edge else: # a.y > point.y if b.y <= point.y and orientation(a, b, point) < 0: wn -= 1 # downward edge return wn
def test_orientation(self): self.assertEqual(orientation(self.p00, self.p11, self.p22), 0) self.assertEqual(orientation(self.p00, self.p10, self.p22), 1) self.assertEqual(orientation(self.p00, self.p10, self.p11), 1) self.assertEqual(orientation(self.p10, self.p11, self.p01), 1) self.assertEqual(orientation(self.p00, self.p01, self.p22), -1) self.assertEqual(orientation(self.p00, self.p01, self.p11), -1) self.assertEqual(orientation(self.p01, self.p11, self.p10), -1)
def itersegments_oriented(self): """Generate oriented segments (the face is on the right).""" if orientation(self.pt1, self.pt2, self.pt3) > 0: yield Segment(self.pt1, self.pt3) yield Segment(self.pt3, self.pt2) yield Segment(self.pt2, self.pt1) else: yield Segment(self.pt1, self.pt2) yield Segment(self.pt2, self.pt3) yield Segment(self.pt3, self.pt1)
def iterpoints(self): """Generate all points on demand (counterclockwise).""" if orientation(self.pt1, self.pt2, self.pt3) > 0: yield self.pt1 yield self.pt2 yield self.pt3 else: yield self.pt1 yield self.pt3 yield self.pt2
def in_circumcircle(self, point): """Check if point is inside triangle circumcircle. Formula is taken from https://en.wikipedia.org/wiki/Delaunay_triangulation#Algorithms """ # Preparing parameters for calculating det for 3x3 matrix. a = self.pt1 - point b = self.pt2 - point c = self.pt3 - point det = (a*a) * b.cross(c) - (b*b) * a.cross(c) + (c*c) * a.cross(b) if orientation(self.pt1, self.pt2, self.pt3) > 0: return det > 0 else: return det < 0
def is_convex(self, test_is_simple=True): """Test if a polygon is convex.""" if test_is_simple: # mozemy pominac test if not self.is_simple(): # Nie mamy wersji dla wielokata zlozonego. raise ValueError("polygon is not simple") # Obliczamy orientacje. orient = self.orientation() # Sprawdzamy katy wewnetrzne. n = len(self.point_list) for i in xrange(n): pt1 = self.point_list[i] pt2 = self.point_list[(i + 1) % n] pt3 = self.point_list[(i + 2) % n] if orientation(pt1, pt2, pt3) * orient < 0: return False return True
def run(self): """Executable pseudocode.""" # Find the lowest y-coordinate and leftmost point. p_min = min(self.point_list, key=lambda p: (p.y, p.x)) # Sort points by polar angle with p_min, if several points have # the same polar angle then only keep the farthest. self.point_list.sort( key=lambda p: ((p - p_min).alpha(), (p - p_min) * (p - p_min))) # After sorting p_min will be at the beginning of point_list. for point in self.point_list: # Pop the last point from the stack if we turn clockwise # to reach this point. while len(self.convex_hull) > 1 and orientation( self.convex_hull[-2], self.convex_hull[-1], point) <= 0: self.convex_hull.pop() self.convex_hull.append(point)
def run(self): """Executable pseudocode.""" p_min = min(self.point_list, key=lambda p: (p.y, p.x)) self.point_list.sort( key=lambda p: ((p - p_min).alpha(), (p - p_min) * (p - p_min))) m = 1 # index of the last point included to convex hull i = 2 # index of the next point to test while i < len(self.point_list): if orientation(self.point_list[m - 1], self.point_list[m], self.point_list[i]) > 0: # left turn # self.point_list[m] outside m += 1 swap(self.point_list, m, i) i += 1 else: # self.point_list[m] inside or on the edge if m > 1: # do not move i, because we need to test m m -= 1 else: # we can move i, but still m=1 swap(self.point_list, m, i) i += 1 self.convex_hull = self.point_list[0:m + 1]
def __contains__(self, other): """Test if a point is in a triangle.""" if isinstance(other, Point): # Chyba wystarczy sprawdzic, czy punkt jest po tej samej # stronie boku, co przeciwlegly wierzcholek. # Trojkat jest domkniety, zawiera swoj brzeg. # Tu mozna tez uzyc oriented_area(), bo chodzi o znak. a12 = orientation(self.pt1, self.pt2, self.pt3) b12 = orientation(self.pt1, self.pt2, other) a23 = orientation(self.pt2, self.pt3, self.pt1) b23 = orientation(self.pt2, self.pt3, other) a31 = orientation(self.pt3, self.pt1, self.pt2) b31 = orientation(self.pt3, self.pt1, other) return (a12 * b12 >= 0) and (a23 * b23 >= 0) and (a31 * b31 >= 0) elif isinstance(other, Segment): return other.pt1 in self and other.pt2 in self else: raise ValueError()
def orientation(self, test_is_simple=True): """Simple polygon orientation.""" if test_is_simple: # mozemy pominac test if not self.is_simple(): # Nie mamy wersji dla wielokata zlozonego. raise ValueError("polygon is not simple") # First find rightmost lowest vertex of the polygon. n = len(self.point_list) rmin = 0 xmin = self.point_list[0].x ymin = self.point_list[0].y for i in xrange(1, n): if self.point_list[i].y > ymin: continue if self.point_list[i].y == ymin: if self.point_list[i].x < xmin: continue rmin = i xmin = self.point_list[i].x ymin = self.point_list[i].y # Test orientation at the rmin vertex. return orientation(self.point_list[(rmin + n - 1) % n], self.point_list[rmin], self.point_list[(rmin + 1) % n])
def orientation(self): """Triangle orientation.""" return orientation(self.pt1, self.pt2, self.pt3)