def not_collides(self, other, ratio=False, symmetric=False, resolution=None): if resolution is None: resolution = default_resolution topleft1 = self.topleft bottomright1 = self.bottomright topleft2 = other.topleft bottomright2 = other.bottomright if topleft1.x < topleft2.x and topleft1.y < topleft2.y and bottomright1.x > bottomright2.x and bottomright1.y > bottomright2.y: if not ratio: return False elif symmetric: return 0.0 else: return (0.0, 0.0) elif bottomright1.distance(topleft1) < bottomright2.distance(topleft2): topleft, bottomright = topleft1, bottomright1 else: topleft, bottomright = topleft2, bottomright2 topleft *= resolution bottomright *= resolution average_resolution = 0.5 * (resolution.x + resolution.y) if ratio: granularity = 1.0 / resolution.x / resolution.y collision = 0.0 for _, point in Point.range(topleft, bottomright, resolution): distance1 = min( average_resolution * self.distance(point - self.center), 1.0) distance2 = max( 1.0 - average_resolution * other.distance(point - other.center), 0.0) average_distance = 0.5 * (distance1 + distance2) if average_distance > 0.95: collision += granularity * average_distance if symmetric: return min(collision / self.shape.area, collision / other.shape.area) else: return (collision / self.shape.area, collision / other.shape.area) else: min_distance = 1.0 / average_resolution for c, point in Point.range(topleft, bottomright, resolution): if (self.distance(point - self.center) > min_distance) and ( other.distance(point - other.center) <= min_distance): return True
def draw(self, world_array, world_size, draw_fn=None): shift = Point(2.0 / world_size.x, 2.0 / world_size.y) scale = 1.0 + 2.0 * shift topleft = (((self.topleft) / scale) * world_size).max(Point.izero) # + 0.5 * shift bottomright = (((self.bottomright + 2.0 * shift) / scale) * world_size).min(world_size) if draw_fn is None: color = self.color.get_color() world_length = min(*world_size) for (x, y), point in Point.range(topleft, bottomright, world_size): point = point * scale - shift offset = point - self.center distance = self.distance(offset) if distance == 0.0: # assert offset in self world_array[y, x] = self.texture.get_color(color, offset) else: # assert offset not in self distance = max(1.0 - distance * world_length, 0.0) world_array[y, x] = distance * self.texture.get_color(color, offset) + (1.0 - distance) * world_array[y, x] # if distance == 0.0: # centrality = self.centrality(offset) # world_array[y, x] = (centrality, centrality, centrality) + (1.0 - centrality) * self.texture.get_color(color, offset) # else: # assert self.centrality(offset) == 0.0 else: for (x, y), point in Point.range(topleft, bottomright, world_size): point = point * scale - shift offset = point - self.center world_array[y, x] = draw_fn(value=world_array[y, x], entity=self, offset=offset) bounding_box = False if bounding_box: # draw bounding box assert draw_fn is None x1 = world_size + 1 x2 = -1 y1 = world_size + 1 y2 = -1 for (x, y), point in Point.range(topleft, bottomright, world_size): if x < x1: x1 = x if x > x2: x2 = x if y < y1: y1 = y if y > y2: y2 = y for (x, y), point in Point.range(topleft, bottomright, world_size): if x == x1 or x == x2 or y == y1 or y == y2: world_array[y, x] = color
def test(): for shape in Shape.get_shapes(): shape_cls = Shape.get_shape(name=shape) shape_obj = shape_cls.random_instance(size_range=(1.0, 1.0), distortion_range=(2.0, 2.0)) contains = 0 for point in Point.range(start=Point(-50, -50), end=Point(50, 50)): if point / 100 in shape_obj: contains += 1 estimated_area = contains / 10000 assert shape_obj.area <= 1.0, (shape, shape_obj.area) assert shape_obj.area == shape_cls.relative_area() or shape_obj.area == shape_cls.relative_area() / empirical_distortion_multiplier, (shape, shape_obj.area, shape_cls.relative_area()) assert abs(shape_obj.area - estimated_area) < 0.011, (shape, abs(shape_obj.area - estimated_area)) return True
def collides(self, other, ratio=False, symmetric=False, resolution=None): if other.id == self.id: if not ratio: return True elif symmetric: return 1.0 else: return (1.0, 1.0) if other.id in self.collisions and self.id in other.collisions: if not ratio: return min(self.collisions[other.id], other.collisions[self.id]) > 0.0 elif symmetric: return min(self.collisions[other.id], other.collisions[self.id]) else: return (self.collisions[other.id], other.collisions[self.id]) topleft1 = self.topleft bottomright1 = self.bottomright topleft2 = other.topleft bottomright2 = other.bottomright if bottomright1.x < topleft2.x or topleft1.x > bottomright2.x or bottomright1.y < topleft2.y or topleft1.y > bottomright2.y: if other.id is not None: self.collisions[other.id] = 0.0 if self.id is not None: other.collisions[self.id] = 0.0 if not ratio: return False elif symmetric: return 0.0 else: return (0.0, 0.0) else: topleft, bottomright = topleft1.max(topleft2), bottomright1.min(bottomright2) if resolution is None: resolution = default_resolution topleft *= resolution bottomright *= resolution average_resolution = 0.5 * (resolution.x + resolution.y) if ratio: granularity = 1.0 / resolution.x / resolution.y collision = 0.0 for _, point in Point.range(topleft, bottomright, resolution): distance1 = max(1.0 - average_resolution * self.distance(point - self.center), 0.0) distance2 = max(1.0 - average_resolution * other.distance(point - other.center), 0.0) average_distance = 0.5 * (distance1 + distance2) if average_distance > 0.95: collision += granularity * average_distance collision1 = collision / self.shape.area collision2 = collision / other.shape.area if other.id is not None: self.collisions[other.id] = collision1 if self.id is not None: other.collisions[self.id] = collision2 if symmetric: return min(collision1, collision2) else: return (collision1, collision2) else: min_distance = 1.0 / average_resolution for _, point in Point.range(topleft, bottomright, resolution): if (self.distance(point - self.center) <= min_distance) and (other.distance(point - other.center) <= min_distance): return True