def reflect(shape, position, _angle, keep_original): """Mirrors and copies the geometry across an invisible axis.""" if shape is None: return None new_shape = shape.cloneAndClear() for contour in shape.contours: c = Contour() for point in contour.points: d = distance(point.x, point.y, position.x, position.y) a = angle(point.x, point.y, position.x, position.y) x, y = coordinates(position.x, position.y, d * cos(radians(a - _angle)), 180 + _angle) d = distance(point.x, point.y, x, y) a = angle(point.x, point.y, x, y) px, py = coordinates(point.x, point.y, d * 2, a) c.addPoint(Point(px, py, point.type)) if contour.closed: c.close() new_shape.add(c) if keep_original: g = Geometry() g.add(shape) g.add(new_shape) return g return new_shape
def _pack(circles, damping=0.1, padding=2, exclude=[]): circles.sort(lambda a, b: a.offset < b.offset) # Repulsive force: move away from intersecting circles. for i in range(len(circles)): circle1 = circles[i] for j in range(i+1, len(circles)): circle2 = circles[j] d = distance(circle1.x, circle1.y, circle2.x, circle2.y) if d == 0: d = 0.001 r = circle1.radius + circle2.radius + padding if d**2 < r**2 - 0.01: dx = circle2.x - circle1.x dy = circle2.y - circle1.y vx = (dx / d) * (r-d) * 0.5 vy = (dy / d) * (r-d) * 0.5 if circle1 not in exclude: circle1.x -= vx circle1.y -= vy if circle2 not in exclude: circle2.x += vx circle2.y += vy # Attractive force: all circles move to center. for circle in circles: if circle not in exclude: vx = circle.x * damping vy = circle.y * damping circle.x -= vx circle.y -= vy
def _distance_to_point(shape): try: return distance(shape.x, shape.y, point.x, point.y) except AttributeError: centroid = shape.bounds.centroid return distance(centroid.x, centroid.y, point.x, point.y)
def intersects(self, other): d = distance(self.x, self.y, other.x, other.y) return d < self.radius + other.radius
def contains(self, x, y): return distance(self.x, self.y, x, y) <= self.radius
def _offset(self): return distance(self.x, self.y, 0, 0)