class Visibility_polygon_class(object): def __init__(self): self.origin = (0, 0) self.refvec = (0, 1) self.segments = [] self.event_queue = [] self.status = 0 self.status = SortedDict() self.visibility_polygon = [] def order_segments(self, arg): return arg # Starts here! def get_visibility_polygon(self, segments, origin): self.origin = origin self.segments = segments self.add_bounding_box() self.create_event_queue_from_segments() self.sort_event_queue() self.initialize_status() #return self.status self.perform_sweep() return self.visibility_polygon def add_bounding_box(self): # Find extreme points margin = 40 top_y = 400 + margin #uppermost_point_index(self.event_queue) bottom_y = 130 - margin #lowermost_point_index(self.event_queue) right_x = 600 + margin #rightmost_point_index(self.event_queue) left_x = 200 - margin #leftmost_point_index(self.event_queue) # Create the bounding box and add it to event queue s1 = Segment(Point(right_x, top_y), Point(right_x, bottom_y)) s2 = Segment(Point(right_x - 5, bottom_y), Point(left_x, bottom_y)) s3 = Segment(Point(left_x, bottom_y + 5), Point(left_x, top_y)) s4 = Segment(Point(left_x + 5, top_y), Point(right_x, top_y + 5)) p = [s1, s2, s3, s4] self.segments.extend(p) # Create an event queue with all points and their connections (not sorted yet!) def create_event_queue_from_segments(self): for s in self.segments: p1 = EventPoint(s.p1) p2 = EventPoint(s.p2) p1.twin = p2 p2.twin = p1 self.event_queue.append(p1) self.event_queue.append(p2) # Create event queue def sort_event_queue(self): # Sort the points in clockwise order self.event_queue = sorted(self.event_queue, key=self.get_key) def initialize_status(self): sweep_ray = Ray(self.origin, self.event_queue[0].p) i = 0 for ep in self.event_queue: if ep.type == DEFAULT_VERTEX: segment = Segment(ep.p, ep.twin.p) intersection_point = sweep_ray.intersection(segment) if len(intersection_point) > 0: # If the segments first point is the current event-point if intersection_point[ 0] == ep.p: # if the point is on the initial ray if len( Ray(self.origin, self.event_queue[ i + 1].p).intersection(segment) ) > 0: #if the point was a start point self.initialize_segment(ep, intersection_point) else: self.initialize_segment(ep.twin, intersection_point) else: self.initialize_segment(ep.twin, intersection_point) else: # Event-points not hit by the ray gets a type ep.type = START_VERTEX ep.twin.type = END_VERTEX i += 1 def initialize_segment(self, ep, intersection_point): status_segment = StatusSegment(ep, ep.twin, self.origin) status_segment.current_distance = distance(intersection_point[0], self.origin) ep.status_segment = status_segment ep.twin.status_segment = status_segment ep.type = START_VERTEX ep.twin.type = END_VERTEX if ep != self.event_queue[0]: self.status.update( {status_segment.current_distance: status_segment}) def perform_sweep(self): print("\nStatus at start: " + str(len(self.status))) for ep in self.event_queue: print("\nStatus: " + str(len(self.status))) if ep.type == START_VERTEX: print("START_VERTEX") status_segment = StatusSegment(ep, ep.twin, self.origin) print("current segment: " + str(status_segment.segment) + str(status_segment.current_distance)) ep.status_segment = status_segment ep.twin.status_segment = status_segment if self.status.__len__() == 0: self.status.update( {status_segment.current_distance: status_segment}) self.visibility_polygon.append(ep.p) print("empty status. Append") else: first_in_status = self.status.peekitem(index=0) print("first in status and distance: " + str(first_in_status[1].segment) + ": " + str(first_in_status[1].current_distance)) current_ray = Ray(self.origin, ep.p) intersection_point = current_ray.intersection( first_in_status[1].segment) first_in_status[1].current_distance = distance( intersection_point[0], self.origin) print("First in status new distance: " + str(first_in_status[1].current_distance)) self.status.update( {status_segment.current_distance: status_segment}) # insert the new segment to status self.status.__delitem__(first_in_status[0]) self.status.update({ first_in_status[1].current_distance: first_in_status[1] }) #update the key distance to the origin new_first_in_status = self.status.peekitem(index=0) if new_first_in_status[1] != first_in_status[1]: self.visibility_polygon.append(intersection_point[0]) self.visibility_polygon.append(ep.p) print("normal status. Append") elif ep.type == END_VERTEX: print("END_VERTEX") first_in_status = self.status.peekitem(index=0) print("first in status and distance: " + str(first_in_status[1].segment) + ": " + str(first_in_status[1].current_distance)) self.status.__delitem__(ep.status_segment.current_distance) print("ep status segment and distance: " + str(ep.status_segment.segment) + ": " + str(ep.status_segment.current_distance)) if self.status.__len__() == 0: self.visibility_polygon.append(ep.p) print("empty status. Append") else: new_first_in_status = self.status.peekitem(index=0) if new_first_in_status[1] != first_in_status[1]: current_ray = Ray(self.origin, ep.p) intersection_point = current_ray.intersection( new_first_in_status[1].segment) self.visibility_polygon.append(ep.p) self.visibility_polygon.append(intersection_point[0]) print("normal status. Append") # Gets key for sorting def get_key(self, point): return self.clockwiseangle_and_distance(point.p) # returns the angle and length vector from the origin to the point def clockwiseangle_and_distance(self, point): # Vector between point and the origin: v = p - o vector = [point[0] - self.origin[0], point[1] - self.origin[1]] # Length of vector: ||v|| lenvector = math.hypot(vector[0], vector[1]) # If length is zero there is no angle if lenvector == 0: return -math.pi, 0 # Normalize vector: v/||v|| normalized = [vector[0] / lenvector, vector[1] / lenvector] dotprod = normalized[0] * self.refvec[0] + normalized[1] * self.refvec[ 1] # x1*x2 + y1*y2 diffprod = self.refvec[1] * normalized[0] - self.refvec[ 0] * normalized[1] # x1*y2 - y1*x2 angle = math.atan2(diffprod, dotprod) # Negative angles represent counter-clockwise angles so we need to subtract them # from 2*pi (360 degrees) if angle < 0: return 2 * math.pi + angle, lenvector # I return first the angle because that's the primary sorting criterium # but if two vectors have the same angle then the shorter distance should come first. return angle, lenvector
class LeafSet(object): __slots__ = ('peers', 'capacity') __passthru = {'get', 'clear', 'pop', 'popitem', 'peekitem', 'key'} __iters = {'keys', 'values', 'items'} def __init__(self, my_key, iterable=(), capacity=8): try: iterable = iterable.items() # view object except AttributeError: pass tuple_itemgetter = Peer.distance(my_key, itemgetter(0)) key_itemgetter = Peer.distance(my_key) self.capacity = capacity self.peers = SortedDict(key_itemgetter) if iterable: l = sorted(iterable, key=tuple_itemgetter) self.peers.update(islice(l, capacity)) def clear(self): self.peers.clear() def prune(self): extra = len(self) - self.capacity for i in range(extra): self.peers.popitem(last=True) def update(self, iterable): try: iterable = iterable.items() # view object except AttributeError: pass iterable = iter(iterable) items = tuple(islice(iterable, 500)) while items: self.peers.update(items) items = tuple(islice(iterable, 500)) def setdefault(self, *args, **kwargs): self.peers.setdefault(*args, **kwargs) self.prune() def __setitem__(self, *args, **kwargs): self.peers.__setitem__(*args, **kwargs) self.prune() def __getitem__(self, *args, **kwargs): return self.peers.__getitem__(*args, **kwargs) def __delitem__(self, *args, **kwargs): return self.peers.__delitem__(*args, **kwargs) def __iter__(self, *args, **kwargs): return self.peers.__iter__(*args, **kwargs) def __reversed__(self, *args, **kwargs): return self.peers.__reversed__(*args, **kwargs) def __contains__(self, *args, **kwargs): return self.peers.__contains__(*args, **kwargs) def __len__(self, *args, **kwargs): return self.peers.__len__(*args, **kwargs) def __getattr__(self, key): if key in self.__class__.__passthru: return getattr(self.peers, key) elif key in self.__class__.__iters: return getattr(self.peers, 'iter' + key) else: return super().__getattr__(key) def __repr__(self): return '<%s keys=%r capacity=%d/%d>' % ( self.__class__.__name__, list(self), len(self), self.capacity)
class DotMap(MutableMapping): def __init__(self, *args, **kwargs): self._map = SortedDict() if args: d = args[0] if type(d) is dict: for k, v in self.__call_items(d): if type(v) is dict: v = DotMap(v) self._map[k] = v if kwargs: for k, v in self.__call_items(kwargs): self._map[k] = v @staticmethod def __call_items(obj): if hasattr(obj, 'iteritems') and ismethod(getattr(obj, 'iteritems')): return obj.iteritems() else: return obj.items() def items(self): return self.iteritems() def iteritems(self): return self.__call_items(self._map) def __iter__(self): return self._map.__iter__() def __setitem__(self, k, v): self._map[k] = v def __getitem__(self, k): if k not in self._map: # automatically extend to new DotMap self[k] = DotMap() return self._map[k] def __setattr__(self, k, v): if k == '_map': super(DotMap, self).__setattr__(k, v) else: self[k] = v def __getattr__(self, k): if k == '_map': return self._map else: return self[k] def __delattr__(self, key): return self._map.__delitem__(key) def __contains__(self, k): return self._map.__contains__(k) def __str__(self): items = [] for k, v in self.__call_items(self._map): items.append('{0}={1}'.format(k, repr(v))) out = 'DotMap({0})'.format(', '.join(items)) return out def __repr__(self): return str(self) def to_dict(self): d = {} for k, v in self.items(): if type(v) is DotMap: v = v.to_dict() d[k] = v return d def pprint(self): pprint(self.to_dict()) # proper dict subclassing def values(self): return self._map.values() @staticmethod def parse_other(other): if type(other) is DotMap: return other._map else: return other def __cmp__(self, other): other = DotMap.parse_other(other) return self._map.__cmp__(other) def __eq__(self, other): other = DotMap.parse_other(other) if not isinstance(other, dict): return False return self._map.__eq__(other) def __ge__(self, other): other = DotMap.parse_other(other) return self._map.__ge__(other) def __gt__(self, other): other = DotMap.parse_other(other) return self._map.__gt__(other) def __le__(self, other): other = DotMap.parseOther(other) return self._map.__le__(other) def __lt__(self, other): other = DotMap.parse_other(other) return self._map.__lt__(other) def __ne__(self, other): other = DotMap.parse_other(other) return self._map.__ne__(other) def __delitem__(self, key): return self._map.__delitem__(key) def __len__(self): return self._map.__len__() def copy(self): return self def get(self, key, default=None): return self._map.get(key, default) def has_key(self, key): return key in self._map def iterkeys(self): return self._map.iterkeys() def itervalues(self): return self._map.itervalues() def keys(self): return self._map.keys() def pop(self, key, default=None): return self._map.pop(key, default) def setdefault(self, key, default=None): return self._map.setdefault(key, default) def viewitems(self): if version_info.major == 2 and version_info.minor >= 7: return self._map.viewitems() else: return self._map.items() def viewkeys(self): if version_info.major == 2 and version_info.minor >= 7: return self._map.viewkeys() else: return self._map.keys() def viewvalues(self): if version_info.major == 2 and version_info.minor >= 7: return self._map.viewvalues() else: return self._map.values() @classmethod def fromkeys(cls, seq, value=None): d = DotMap() d._map = SortedDict.fromkeys(seq, value) return d