def do_intersect(self, s_2: 'Vector') -> bool: # orientation of the (self.tail, self.head, s_2.tail) triangle s_1_orientation_tail = Point.orientation(self.tail, self.head, s_2.tail) # orientation of the (self.tail, self.head, s_2.head) triangle s_1_orientation_head = Point.orientation(self.tail, self.head, s_2.head) # orientation of the (s_2.tail, s_2.head, self.tail) triangle s_2_orientation_tail = Point.orientation(s_2.tail, s_2.head, self.tail) # orientation of the (s_2.tail, s_2.head, self.head) triangle s_2_orientation_head = Point.orientation(s_2.tail, s_2.head, self.head) # general case if s_1_orientation_tail != s_1_orientation_head and s_2_orientation_tail != s_2_orientation_head: return True # collinear case if s_1_orientation_tail == 0 and s_1_orientation_head == 0 and s_2_orientation_tail == 0 and s_2_orientation_head == 0: if self.tail.between(s_2.head, s_2.tail) or self.head.between(s_2.head, s_2.tail) \ or s_2.tail.between(self.head, self.tail) or s_2.head.between(self.head, self.tail): return True return False
def find_centroid(vertices: list) -> Point: centroid: 'Point' = Point() for v in vertices: centroid += Point(*v) return centroid / len(vertices)
def convex_hull_incremental(vertices: list) -> list: hull = list(orientate_vertices(vertices[0], vertices[1], vertices[2])) for i in range(3, len(vertices)): p_i = Point(*vertices[i]) if not p_i.in_poly(hull): union_point_poly(hull, vertices[i]) return hull
def convex_hull_graham_scan(vertices: list) -> list: vertices = simple_poly(vertices) hull = vertices[:2] for v in vertices[2:]: # edge case if only one point is in hull while len(hull) > 1 and Point.orientation( Point(*hull[-2]), Point(*hull[-1]), Point(*v)) < 0: hull.pop() hull.append(v) return hull
def orientate_edges(edges): centroid = find_centroid() for e in edges: if Point.orientation(*e, centroid) < 0: e[1], e[0] = e[0], e[1]
def get_hull_edges(vertices: list) -> list: size: int = len(vertices) edges = [] fail_flag = -2 for i in range(size - 1): for j in range(i + 1, size): current_orientation = None for v in vertices: if v == vertices[i] or v == vertices[j]: continue orientation = Point.orientation(Point(*vertices[i]), Point(*vertices[j]), Point(*v)) if orientation == 0: continue if current_orientation is None: current_orientation = orientation continue if current_orientation != orientation: current_orientation = fail_flag break if current_orientation is None or current_orientation == fail_flag: continue if current_orientation == -1: edges.append((vertices[i], vertices[j])) else: edges.append((vertices[j], vertices[i])) return edges
def convex_hull_quad(vertices: list) -> list: hull = [] for l in vertices: p_l = Point(l[0], l[1]) is_in_hull = True for i in range(len(vertices) - 2): p_i = Point(vertices[i][0], vertices[i][1]) for j in range(i + 1, len(vertices) - 1): p_j = Point(vertices[j][0], vertices[j][1]) for k in range(j + 1, len(vertices)): p_k = Point(vertices[k][0], vertices[k][1]) if p_i == p_l or p_j == p_l or p_k == p_l: continue if p_l.in_triangle(p_i, p_j, p_k): is_in_hull = False break if not is_in_hull: break if not is_in_hull: break if is_in_hull: hull.append(l) return simple_poly(hull)
def sort_hull(edges: list) -> None: for i in range(len(edges) - 1): for j in range(i + 1, len(edges)): if edges[i][1] == edges[j][0]: if i + 1 != j: edges[i + 1], edges[j] = edges[j], edges[i + 1] break i = 0 while i < len(edges) - 1: current_v = edges[i] j = (i + 1) % len(edges) while j < len(edges): next_v = edges[j % len(edges)] if current_v[0] == next_v[0]: if Point.distance(current_v[0], current_v[1]) > Point.distance( next_v[0], next_v[1]): edges.remove(edges[i]) else: edges.remove(edges[j % len(edges)]) else: j += 1 i += 1 return None
def convex_hull_jarvis_march(vertices: list) -> list: i: int = get_point_index(vertices, x=True, max_x=False, max_y=False) vertices[0], vertices[i] = vertices[i], vertices[0] vec = Vector(Point(*vertices[0]), Point(vertices[0][0], vertices[0][1] + 1)) j = vertices.index( min(vertices, key=lambda x: vec.angle_between( Vector(Point(*vertices[0]), Point(*x))))) vertices[1], vertices[j] = vertices[j], vertices[1] vertices.append(vertices[0]) hull_size = 2 vec.tail = Point(*vertices[1]) while True: vec.head = vec.tail vec.tail = Point(*vertices[hull_size]) k = hull_size min_dist = vec.head.distance(vec.tail) for j in range(hull_size, len(vertices)): p_j = Point(*vertices[j]) ori = Point.orientation(vec.head, vec.tail, p_j) curr_dist = vec.tail.distance(p_j) if ori > 0: k = j vec.tail = p_j elif ori == 0 and curr_dist < min_dist: k = j vec.tail = p_j min_dist = curr_dist if vertices[k] == vertices[0]: break else: vertices[hull_size], vertices[k] = vertices[k], vertices[hull_size] hull_size += 1 vertices.pop() return vertices[:hull_size]
def find_closest_point_index(vertices: list, p: tuple) -> int: min_dist_index: int = 0 min_dist = Point.distance(Point(*vertices[0]), Point(*p)) for i in range(1, len(vertices)): current_distance = Point.distance(Point(*vertices[i]), Point(*p)) if current_distance < min_dist: min_dist_index = i min_dist = current_distance return min_dist_index
def simple_poly(vertices: list) -> list: # find point with min y and max x coordinates index = vertices.index(min(vertices, key=lambda x: (x[1], -x[0]))) # swap with first point vertices[0], vertices[index] = vertices[index], vertices[0] vertices = sorted(vertices, key=lambda x: (Vector(Point(*vertices[0]), Point(*x)).slope(), Point(*vertices[0]).euclidean_distance(Point(*x)))) bottom_most = Point(*vertices[0]) k = -1 while Point(*vertices[k]) != bottom_most and \ Vector(bottom_most, Point(*vertices[k])).slope() == Vector(bottom_most, Point(*vertices[k-1])).slope(): k -= 1 return vertices[:k] + vertices[:len(vertices) + k - 1:-1]
def orientation_vector(self): direction_vector: Point = self.direction_vector() return Point(direction_vector.y, -direction_vector.x)