def test_point_equality(self): assert self.point_a == Point(0.0, 1.0) assert self.point_a != Point(1.0, 0.0) assert self.edge_a == self.edge_b assert self.edge_a == self.edge_a assert self.edge_a == self.edge_c assert self.edge_a == self.edge_d
def setup_method(self, method): self.g = vg.VisGraph() self.point_a = Point(0, 0) self.point_b = Point(4, 0) self.point_c = Point(2, 4) self.point_d = Point(1, 0.5) self.g.build([[self.point_a, self.point_b, self.point_c]])
def test_collin2(self): self.point_g = Point(2, 5) self.point_h = Point(3, 5) graph = Graph([[self.point_g, self.point_h, self.point_c], [self.point_d, self.point_e, self.point_f]]) visible = visible_vertices(Point(1, 4), graph, None, None) assert visible == [self.point_g, self.point_e, self.point_c]
def intersect_point(p1, p2, edge): """Return intersect Point where the edge from p1, p2 intersects edge""" if p1 in edge: return p1 if p2 in edge: return p2 if edge.p1.x == edge.p2.x: if p1.x == p2.x: return None pslope = (p1.y - p2.y) / (p1.x - p2.x) intersect_x = edge.p1.x intersect_y = pslope * (intersect_x - p1.x) + p1.y return Point(intersect_x, intersect_y) if p1.x == p2.x: eslope = (edge.p1.y - edge.p2.y) / (edge.p1.x - edge.p2.x) intersect_x = p1.x intersect_y = eslope * (intersect_x - edge.p1.x) + edge.p1.y return Point(intersect_x, intersect_y) pslope = (p1.y - p2.y) / (p1.x - p2.x) eslope = (edge.p1.y - edge.p2.y) / (edge.p1.x - edge.p2.x) if eslope == pslope: return None intersect_x = (eslope * edge.p1.x - pslope * p1.x + p1.y - edge.p1.y) / (eslope - pslope) intersect_y = eslope * (intersect_x - edge.p1.x) + edge.p1.y return Point(intersect_x, intersect_y)
def test_point_in_polygon(): g = vg.VisGraph() point_a = Point(0, 0) point_b = Point(4, 0) point_c = Point(2, 4) point_d = Point(1, 0.5) g.build([[point_a, point_b, point_c]]) assert g.point_in_polygon(point_d) != -1
def test_collin3(self): point_g = Point(2.0, 2.0) point_h = Point(3.5, 5.0) point_i = Point(2.5, 2.0) graph = Graph([[self.point_a, self.point_b, self.point_c], [point_g, point_h, point_i], [self.point_d, self.point_e, self.point_f]]) visible = visible_vertices(Point(1, 4), graph, None, None) assert visible == [point_h, self.point_a, self.point_c]
def test_closest_point_edge_point(self): """Test where the cp is a end-point of a polygon edge. Can end up with cp extending into polygon instead of outside it.""" g = vg.VisGraph() g.build([[Point(0, 1), Point(2, 0), Point(1, 1), Point(2, 2)]]) p = Point(1, 0.9) pid = g.point_in_polygon(p) cp = g.closest_point(p, pid, length=0.001) assert g.point_in_polygon(cp) == -1
def setup_method(self, method): self.point_a = Point(0.0, 1.0) self.point_b = Point(1.0, 2.0) self.point_c = Point(0.0, 1.0) self.point_d = Point(1.0, 2.0) self.edge_a = Edge(self.point_a, self.point_b) self.edge_b = Edge(self.point_b, self.point_a) self.edge_c = Edge(self.point_c, self.point_d) self.edge_d = Edge(self.point_d, self.point_c)
def test_collin4(self): graph = Graph([[Point(1, 1), Point(2, 3), Point(3, 1), Point(2, 2)], [Point(2, 4)]]) visible = visible_vertices(Point(2, 1), graph, None, None) assert visible == [Point(3, 1), Point(2, 2), Point(1, 1)]
def test_collin1(self): graph = Graph([[self.point_a, self.point_b, self.point_c], [self.point_d, self.point_e, self.point_f]]) visible = visible_vertices(Point(1, 4), graph, None, None) assert visible == [ self.point_a, self.point_c, self.point_d, self.point_f ]
def polygon_crossing(p1, poly_edges): """Returns True if Point p1 is internal to the polygon The polygon is defined by the Edges in poly_edges. Uses crossings algorithm and takes into account edges that are collinear to p1.""" p2 = Point(INF, p1.y) intersect_count = 0 co_flag = False co_dir = 0 for edge in poly_edges: if p1.y < edge.p1.y and p1.y < edge.p2.y: continue if p1.y > edge.p1.y and p1.y > edge.p2.y: continue # Deal with points colinear to p1 co0 = (ccw(p1, edge.p1, p2) == 0) and (edge.p1.x > p1.x) co1 = (ccw(p1, edge.p2, p2) == 0) and (edge.p2.x > p1.x) co_point = edge.p1 if co0 else edge.p2 if co0 or co1: if edge.get_adjacent(co_point).y > p1.y: co_dir += 1 else: co_dir -= 1 if co_flag: if co_dir == 0: intersect_count += 1 co_flag = False co_dir = 0 else: co_flag = True elif edge_intersect(p1, p2, edge): intersect_count += 1 if intersect_count % 2 == 0: return False return True
def edge_in_polygon(p1, p2, graph): if p1.polygon_id != p2.polygon_id: return False if p1.polygon_id == -1 or p2.polygon_id == -1: return False mid_point = Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2) return polygon_crossing(mid_point, graph.polygons[p1.polygon_id])
def test_collin11(self): graph = Graph([[ Point(0, 0), Point(3, 0), Point(3, 2), Point(2, 3), Point(2, 1), Point(1, 1), Point(1, 2), Point(0, 2) ]]) pip = point_in_polygon(Point(0.5, 1), graph) assert pip > -1
def edge_in_polygon(p1, p2, graph): """Return true if the edge from p1 to p2 is interior to any polygon in graph.""" if p1.polygon_id != p2.polygon_id: return False if p1.polygon_id == -1 or p2.polygon_id == -1: return False mid_point = Point((p1.x + p2.x) / 2, (p1.y + p2.y) / 2) return polygon_crossing(mid_point, graph.polygons[p1.polygon_id])
def test_collin5(self): r = 0.2 # Radius of polygon n = 4 # Sides of polygon c = Point(1.0, 1.0) # Center of polygon verts = [] for i in range(n): verts.append( Point(r * cos(2 * pi * i / n - pi / 4) + c.x, r * sin(2 * pi * i / n - pi / 4) + c.y)) g = vg.VisGraph() g.build([verts]) s = Point(0, 0) t = Point(1.7, 1.7) shortest = g.shortest_path(s, t) visible = visible_vertices(t, g.graph, s, None) assert verts[3] not in visible assert verts[1] not in shortest assert verts[3] not in shortest
def test_edge_intersect_function(): point_a = Point(3.0, 5.0) point_b = Point(5.0, 3.0) point_c = Point(4.0, 2.0) point_d = Point(4.0, 5.0) point_e = Point(5.0, 4.0) point_f = Point(3.0, 4.0) point_g = Point(4.0, 1.0) point_h = Point(6.0, 4.0) point_i = Point(4.0, 4.0) edge = Edge(point_a, point_b) assert edge_intersect(point_c, point_d, edge) is True assert edge_intersect(point_c, point_e, edge) is True assert edge_intersect(point_f, point_e, edge) is True assert edge_intersect(point_g, point_b, edge) is True assert edge_intersect(point_c, point_h, edge) is True assert edge_intersect(point_h, point_i, edge) is True
def setup_method(self, method): self.point_a = Point(0, 1) self.point_b = Point(1, 0) self.point_c = Point(2, 3) self.point_d = Point(3, 2) self.point_e = Point(3.5, 0.5) self.point_f = Point(4.5, 3.5)
def setup_method(self, method): self.point_a = Point(0.0, 1.0) self.point_b = Point(1.0, 0.0) self.point_c = Point(2.0, 3.0) self.point_d = Point(3.0, 2.0) self.point_e = Point(3.5, 0.5) self.point_f = Point(4.5, 3.5)
def test_point_edge_distance_function(): point_a = Point(3.0, 1.0) point_b = Point(3.0, 5.0) point_c = Point(2.0, 2.0) point_d = Point(4.0, 4.0) point_e = Point(1.0, 1.0) point_f = Point(1.0, 2.0) point_g = Point(3.0, 4.0) point_h = Point(2.0, 5.0) edge = Edge(point_a, point_b) edge2 = Edge(point_c, point_d) edge3 = Edge(point_e, point_b) assert point_edge_distance(point_c, point_d, edge) == 1.4142135623730951 assert point_edge_distance(point_a, point_b, edge2) == 2.0 assert point_edge_distance(point_f, point_g, edge3) == 1.4142135623730951 assert point_edge_distance(point_h, point_g, edge3) == 0.9428090415820635
def polygon_crossing(p1, poly_edges): if not poly_edges: return True a = poly_edges.pop() polys = [[a.p1.x, a.p1.y], [a.p2.x, a.p2.y]] last = a.p2 while poly_edges: curr = None for edge in poly_edges: if edge.p1 == last: curr = edge poly_edges.remove(curr) polys.append([curr.p2.x, curr.p2.y]) last = curr.p2 poly = Polygon(polys) return poly.contains(Point(p1.x, p1.y))
def test_angle_function(): center = Point(1.0, 1.0) point_a = Point(3.0, 1.0) point_b = Point(1.0, 0) point_c = Point(0.0, 2.0) point_d = Point(2.0, 2.0) point_e = Point(2.0, 0.0) point_f = Point(0.0, 0.0) assert angle(center, point_a) == 0 assert angle(center, point_b) == pi * 3 / 2 assert degrees(angle(center, point_c)) == 135 assert degrees(angle(center, point_d)) == 45 assert degrees(angle(center, point_e)) == 315 assert degrees(angle(center, point_f)) == 225
def polygon_crossing(p1, poly_edges): """Returns True if Point p1 is internal to the polygon. The polygon is defined by the Edges in poly_edges. Uses crossings algorithm and takes into account edges that are collinear to p1.""" p2 = Point(INF, p1.y) intersect_count = 0 for edge in poly_edges: if p1.y < edge.p1.y and p1.y < edge.p2.y: continue if p1.y > edge.p1.y and p1.y > edge.p2.y: continue if p1.x > edge.p1.x and p1.x > edge.p2.x: continue # Deal with points collinear to p1 edge_p1_collinear = (ccw(p1, edge.p1, p2) == COLLINEAR) edge_p2_collinear = (ccw(p1, edge.p2, p2) == COLLINEAR) if edge_p1_collinear and edge_p2_collinear: continue if edge_p1_collinear or edge_p2_collinear: collinear_point = edge.p1 if edge_p1_collinear else edge.p2 if edge.get_adjacent(collinear_point).y > p1.y: intersect_count += 1 elif edge_intersect(p1, p2, edge): intersect_count += 1 if intersect_count % 2 == 0: return False return True
def closest_point(p, graph, polygon_id, length=0.001): """Assumes p is interior to the polygon with polygon_id. Returns the closest point c outside the polygon to p, where the distance from c to the intersect point from p to the edge of the polygon is length.""" polygon_edges = graph.polygons[polygon_id] close_point = None close_edge = None close_dist = None # Finds point closest to p, but on a edge of the polygon. # Solution from http://stackoverflow.com/a/6177788/4896361 for i, e in enumerate(polygon_edges): num = ((p.x - e.p1.x) * (e.p2.x - e.p1.x) + (p.y - e.p1.y) * (e.p2.y - e.p1.y)) denom = ((e.p2.x - e.p1.x)**2 + (e.p2.y - e.p1.y)**2) u = num / denom pu = Point(e.p1.x + u * (e.p2.x - e.p1.x), e.p1.y + u * (e.p2.y - e.p1.y)) pc = pu if u < 0: pc = e.p1 elif u > 1: pc = e.p2 d = edge_distance(p, pc) if i == 0 or d < close_dist: close_dist = d close_point = pc close_edge = e # Extend the newly found point so it is outside the polygon by `length`. if close_point in close_edge: c = close_edge.p1 if close_point == close_edge.p1 else close_edge.p2 edges = list(graph[c]) v1 = unit_vector(c, edges[0].get_adjacent(c)) v2 = unit_vector(c, edges[1].get_adjacent(c)) vsum = unit_vector(Point(0, 0), Point(v1.x + v2.x, v1.y + v2.y)) close1 = Point(c.x + (vsum.x * length), c.y + (vsum.y * length)) close2 = Point(c.x - (vsum.x * length), c.y - (vsum.y * length)) if point_in_polygon(close1, graph) == -1: return close1 return close2 else: v = unit_vector(p, close_point) return Point(close_point.x + v.x * length, close_point.y + v.y * length)
def round_point(self, point): return Point(round(point.x, self.precision), round(point.y, self.precision))
def visible_vertices(point, graph, origin=None, destination=None, scan='full'): """Returns list of Points in graph visible by point. If origin and/or destination Points are given, these will also be checked for visibility. scan 'full' will check for visibility against all points in graph, 'half' will check for visibility against half the points. This saves running time when building a complete visibility graph, as the points that are not checked will eventually be 'point'. """ edges = graph.get_edges() points = graph.get_points() if origin: points.append(origin) if destination: points.append(destination) points.sort(key=lambda p: (angle(point, p), edge_distance(point, p))) # Initialize open_edges with any intersecting edges on the half line from # point along the positive x-axis open_edges = [] point_inf = Point(INF, point.y) for e in edges: if point in e: continue if edge_intersect(point, point_inf, e): if on_segment(point, e.p1, point_inf): continue if on_segment(point, e.p2, point_inf): continue k = EdgeKey(point, point_inf, e) insort(open_edges, k) visible = [] prev = None prev_visible = None for p in points: if p == point: continue if scan == 'half' and angle(point, p) > pi: break # Remove clock wise edges incident on p if open_edges: for edge in graph[p]: if ccw(point, p, edge.get_adjacent(p)) == -1: k = EdgeKey(point, p, edge) index = bisect(open_edges, k) - 1 if len(open_edges) > 0 and open_edges[index] == k: del open_edges[index] # Check if p is visible from point is_visible = False # ...Non-collinear points if prev is None or ccw(point, prev, p) != 0 or not on_segment(point, prev, p): if len(open_edges) == 0: is_visible = True elif not edge_intersect(point, p, open_edges[0].edge): is_visible = True # ...For collinear points, if previous point was not visible, p is not elif not prev_visible: is_visible = False # ...For collinear points, if previous point was visible, need to check # that the edge from prev to p does not intersect any open edge. else: is_visible = True for e in open_edges: if prev not in e.edge and edge_intersect(prev, p, e.edge): is_visible = False break if is_visible and edge_in_polygon(prev, p, graph): is_visible = False # Check if the visible edge is interior to its polygon if is_visible and p not in graph.get_adjacent_points(point): is_visible = not edge_in_polygon(point, p, graph) if is_visible: visible.append(p) # Add counter clock wise edges incident on p to open_edges for edge in graph[p]: if (point not in edge) and ccw(point, p, edge.get_adjacent(p)) == 1: k = EdgeKey(point, p, edge) insort(open_edges, k) prev = p prev_visible = is_visible return visible
def test_angle2_function(): polys = [[ Point(353.6790486272709, 400.99387840984855), Point(351.1303807396073, 398.8696192603927), Point(349.5795890382704, 397.8537806679034), Point(957.1067811865476, -207.10678118654744), Point(-457.10678118654766, -207.10678118654744), Point(-457.10678118654744, 1207.1067811865476), Point(957.1067811865476, 1207.1067811865473), Point(353.52994294901674, 606.0798841165788), Point(354.0988628008279, 604.098862800828), Point(354.52550331744527, 601.3462324760635), Point(352.6969055662087, 602.6943209889012), Point(351.22198101804634, 603.781672670995), Point(247.0, 500.0), Point(341.8964635104416, 405.50444716676054), Point(349.24224903733045, 410.671256247085), Point(350.84395848060774, 407.17766877398697) ]] v = vg.VisGraph() v.build(polys)
def unit_vector(c, p): magnitude = edge_distance(c, p) return Point((p.x - c.x) / magnitude, (p.y - c.y) / magnitude)
def build_mod(self, input, workers=1, status=True, ref_point=None): """Build visibility graph based on a list of polygons. Modified version. Takes into account the case where one polygon surrounds the rest To test this, a reference-point is given to check the existance of such polygon The input must be a list of polygons, where each polygon is a list of in-order (clockwise or counter clockwise) Points. It only one polygon, it must still be a list in a list, i.e. [[Point(0,0), Point(2,0), Point(2,1)]]. Take advantage of processors with multiple cores by setting workers to the number of subprocesses you want. Defaults to 1, i.e. no subprocess will be started. Set status=False to turn off the statusbar when building. """ self.graph = Graph(input) self.visgraph = Graph([]) # Check if surrounding polygon exists surPoly = [] if ref_point: sPoly = pointVec_in_polygon(ref_point, self.graph) surPoly = sPoly if sPoly.__len__() == 2: #choose the outer polygon pp0 = list(self.graph.polygons[sPoly[0]]) pp1 = list(self.graph.polygons[sPoly[1]]) x0=[] for e in pp0: x0.append(e.p1.x) x0.append(e.p2.x) x1=[] for e in pp1: x1.append(e.p1.x) x1.append(e.p2.x) if min(x0)<min(x1): surPoly = sPoly[0] else: surPoly = sPoly[1] if surPoly: surPoly = surPoly[0] surPolyEdges = list(self.graph.polygons[surPoly]) points = self.graph.get_points() batch_size = 10 if workers == 1: for batch in tqdm([points[i:i + batch_size] for i in xrange(0, len(points), batch_size)], disable=not status): for edge in _vis_graph(self.graph, batch): if surPoly != -1: # skip an edge outside the surrounding polygon edgePtsInPoly = 0 for n in range(surPolyEdges.__len__()): if surPolyEdges[n].__contains__(edge.p1) or surPolyEdges[n].__contains__(edge.p2): edgePtsInPoly += 1 if edgePtsInPoly == 4: # both edge points on surrounding polygon, but not same edge midP = Point((edge.p1.x + edge.p2.x)/2. , (edge.p1.y + edge.p2.y)/2. ) if not polygon_crossing(midP, self.graph.polygons[surPoly]): continue self.visgraph.add_edge(edge) else: pool = Pool(workers) batches = [(self.graph, points[i:i + batch_size]) for i in xrange(0, len(points), batch_size)] results = list(tqdm(pool.imap(_vis_graph_wrapper, batches), total=len(batches), disable=not status)) for result in results: for edge in result: self.visgraph.add_edge(edge)
def edge_intersect(p1, p2, edge): edge2 = LineString([Point(p1.x, p1.y), Point(p2.x, p2.y)]) edge1 = LineString( [Point(edge.p1.x, edge.p1.y), Point(edge.p2.x, edge.p2.y)]) return edge1.intersects(edge2)
def on_segment(p, q, r): line = LineString([Point(p.x, p.y), Point(r.x, r.y)]) return line.contains(Point(q.x, q.y))