def test_rect_from_point_width_height (self): rect = Shapes.rect(Point(100,100), 300, 300) self.assertEqual(4, len(rect.segments)) self.assertEqual(Point(100,100), rect.head) self.assertEqual(Point(400,100), rect.segments[0]['p2']) self.assertEqual(Point(400,400), rect.segments[1]['p2']) self.assertEqual(Point(100,400), rect.segments[2]['p2'])
def with_frame(self, color, width: int): Shapes.rect(Point(0, 0), self.surface.width(), width)\ .fill(color).reg(self) Shapes.rect(Point(self.surface.width() - width, 0), width, self.surface.height())\ .fill(color).reg(self) Shapes.rect(Point(0, self.surface.height() - width), self.surface.width(), width)\ .fill(color).reg(self) Shapes.rect(Point(0, 0), width, self.surface.height()).fill(color).reg(self) return self
def curve(p0: Point, p1: Point, p2: Point, p3: Point) -> Shape: s = Shape.Shape() p0 = p0 if type(p0) == Point else Point(p0[0], p0[1]) c1 = p1 if type(p1) == Point else Point(p1[0], p1[1]) c2 = p2 if type(p2) == Point else Point(p2[0], p2[1]) p2 = p3 if type(p3) == Point else Point(p3[0], p3[1]) s.head = p0 s.add(c2, c1, p2) s.closed = False return s
def test_interpolate(self): self.assertEqual((10, 10), Point.interpolate(Point(0, 0), Point(20, 20))) self.assertEqual((-5, -5), Point.interpolate(Point(0, 0), Point(-10, -10))) self.assertEqual((10, 30), Point.interpolate(Point(10, 10), Point(10, 50)))
def __init__(self, pos: Point = Point(0, 0), speed: Point = Point(0, 0), ttl: float = -1): self.position = pos self.speed = speed self.initial_speed = speed.cp() self.acceleration = Point(0, 0) self.ttl = ttl self.alive = True self.trace = [self.position]
def test_subd(self): rect = Shapes.rect(Point(100, 100), 300, 300) rect.subd(0.5) self.assertEqual(8, len(rect.segments)) self.assertEqual(Point(100, 100), rect.head) self.assertEqual(Point(250, 100), rect.segments[0]['p2']) self.assertEqual(Point(400, 100), rect.segments[1]['p2']) self.assertEqual(Point(400, 250), rect.segments[2]['p2']) self.assertEqual(Point(400, 400), rect.segments[3]['p2']) self.assertEqual(Point(250, 400), rect.segments[4]['p2']) self.assertEqual(Point(100, 400), rect.segments[5]['p2']) self.assertEqual(Point(100, 250), rect.segments[6]['p2']) self.assertEqual(Point(100, 100), rect.segments[7]['p2'])
def adjust(self): for i in range(1, len(self.segments)): s0 = self.segments[i - 1] d = Point.distance(s0['c2'], s0['p2']) nc1 = Utils.proj(s0['c2'], s0['p2'], 2 * d) self.segments[i]['c1'] = nc1 return self
def __init__(self, pos: Point = Point(0, 0), mass: float = 100.0, decay: float = 0.0): self.pos = pos self.mass = mass self.decay = decay
def grid(self, xrows, yrows, width, height) -> plist: points = plist([], self.seed) xspan = width / xrows yspan = height / yrows for ypoints in range(1, yrows): for xpoints in range(1, xrows): points.append(Point(xpoints * xspan, ypoints * yspan)) return points
def submit(self, fs: list[Field]): total_acc = Point(0, 0) for field in fs: v = field.pos - self.position mag = (v.x * v.x) + (v.y * v.y) force = field.mass / mag total_acc = total_acc + v * force self.acceleration = total_acc return self
def contour_points(self, points: list[Point], width: float): outer = [] inner = [] outer.append(Point.off(points[0], points[1], width)) inner.append(Point.off(points[0], points[1], -width)) pairs = list(zip(points[::1], points[1::1])) for i in range(0, len(pairs)): pair = pairs[i] p1 = Point.off(pair[1], pair[0], -width) p2 = Point.off(pair[1], pair[0], width) #if i < len(pairs)-1: # nxt = pairs[i+1][0] # p1 = Point.rotate(p1, pair[1], Point.angle(pair[1], nxt)) # p2 = Point.rotate(p2, pair[1], Point.angle(pair[1], nxt)) outer.append(p1) inner.append(p2) self.left = outer self.right = inner return outer, inner
def catmull_rom(ps: plist, res): """Computes Catmull-Rom Spline for given support points and resolution. Args: p_x: array of x-coords p_y: array of y-coords res: resolution of a segment (including the start point, but not the endpoint of the segment) """ # create arrays for spline points p_x = [p.x for p in ps] p_y = [p.y for p in ps] x_intpol = np.empty(res * (len(p_x) - 1) + 1) y_intpol = np.empty(res * (len(p_x) - 1) + 1) # set the last x- and y-coord, the others will be set in the loop x_intpol[-1] = p_x[-1] y_intpol[-1] = p_y[-1] # loop over segments (we have n-1 segments for n points) for i in range(len(p_x) - 1): # set x-coords x_intpol[i * res:(i + 1) * res] = np.linspace(p_x[i], p_x[i + 1], res, endpoint=False) if i == 0: # need to estimate an additional support point before the first y_intpol[:res] = np.array([ catmull_rom_one_point( x, p_y[0] - (p_y[1] - p_y[0]), # estimated start point, p_y[0], p_y[1], p_y[2]) for x in np.linspace(0., 1., res, endpoint=False) ]) elif i == len(p_x) - 2: # need to estimate an additional support point after the last y_intpol[i * res:-1] = np.array([ catmull_rom_one_point( x, p_y[i - 1], p_y[i], p_y[i + 1], p_y[i + 1] + (p_y[i + 1] - p_y[i]) # estimated end point ) for x in np.linspace(0., 1., res, endpoint=False) ]) else: y_intpol[i * res:(i + 1) * res] = np.array([ catmull_rom_one_point(x, p_y[i - 1], p_y[i], p_y[i + 1], p_y[i + 2]) for x in np.linspace(0., 1., res, endpoint=False) ]) return [Point(x_intpol[i], y_intpol[i]) for i in range(0, len(x_intpol))]
def rotate(self, deg: float, origin: Point = None): points = [self.head] points.extend( np.concatenate([[s['c1'], s['c2'], s['p2']] for s in self.segments])) points = list(map(lambda p: (p.x, p.y), points)) origin = self.center() if origin is None else origin origin = (origin.x, origin.y) angle = np.deg2rad(deg) r = np.array([[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]]) o = np.atleast_2d(origin) p = np.atleast_2d(points) rotpoints = np.squeeze((r.dot(p.T - o.T) + o.T).T) self.head = Point(rotpoints[0][0], rotpoints[0][1]) self.segments.clear() for i in range(1, len(rotpoints) - 2, 3): c1 = Point(rotpoints[i][0], rotpoints[i][1]) c2 = Point(rotpoints[i + 1][0], rotpoints[i + 1][1]) p2 = Point(rotpoints[i + 2][0], rotpoints[i + 2][1]) self.segments.append({'c1': c1, 'c2': c2, 'p2': p2}) return self
def linear(self, outer: list[Point], inner: list[Point]): shape = Shape(outer[0]) for i in range(1, len(outer)): start = outer[i - 1] end = outer[i] d = Point.distance(start, end) shape.add(Utils.proj(start, end, d / 3), Utils.proj(start, end, 2 * d / 3), end) last_innpoint = inner[-1] d = Point.distance(end, last_innpoint) shape.add(Utils.proj(end, last_innpoint, d / 3), Utils.proj(end, last_innpoint, 2 * d / 3), last_innpoint) inner.reverse() for i in range(1, len(inner)): start = inner[i - 1] end = inner[i] d = Point.distance(start, end) shape.add(Utils.proj(start, end, d / 3), Utils.proj(start, end, 2 * d / 3), end) d = Point.distance(end, outer[0]) shape.add(Utils.proj(end, outer[0], d / 3), Utils.proj(end, outer[0], 2 * d / 3), outer[0]) return shape
class Emitter: pos: Point = Point(0, 0) speed: Point = 0.0 xsize: int = 10 ysize: int = 10 particle_life: float = 10 spread: float = 1.0 rate: float = 5.0 rnd: numpy.random = random() def emit_particle(self): part_pos = self.pos + (self.xsize * self.rnd(), self.ysize * self.rnd()) return Particle(pos=part_pos, speed=self.speed, ttl=self.particle_life)
def rect(p0: Point, *args): """ rect (Point, width, height) rect (Point, Point, width) """ assert len(args) == 2 or len(args) == 1 param1 = args[0] param2 = args[1] if type(param1) == Point: assert type(param2) == int or type(param2) == float p1, height = args dist = Point.distance(p0, p1) angle = Point.angle(p0, p1) return rect(Point(p0.x, p0.y - height/2), dist, height)\ .rotate(angle, p0) else: width, height = args p1 = Point (p0.x+width, p0.y) p2 = Point (p1.x, p1.y + height) p3 = Point (p0.x, p0.y + height) return Shape.Shape.line(p0, p1).addPoint(p2).addPoint(p3).addPoint(p0)
def spline(pts: list, numpoints: float = 100) -> plist: ps = Spline.bspline(pts.as_tuples(), numpoints, 4) return plist([Point(p[0], p[1]) for p in ps])
def test_angle(self): a = Point.angle(Point(50, 50), Point(100, 100)) self.assertEqual(a, 45.0)
def test_rotate(self): self.assertEqual((0, 100), Point.rotate(Point(100, 0), Point(0, 0), 90)) self.assertEqual((-100, 0), Point.rotate(Point(100, 0), Point(0, 0), 180))
def circle(p: Point, r: float) -> Shape: c = 0.551915024494 * r circle = Shape.Shape(Point(0, -r)) circle.add(Point(-c, -r), Point(-r, -c), Point(-r, 0)) circle.add(Point(-r, c), Point(-c, r), Point(0, r)) circle.add(Point(c, r), Point(r, c), Point(r, 0)) circle.add(Point(r, -c), Point(c, -r), Point(0, -r)) return circle.translate(p)
def furthest(self, p: Point) -> Point: dists = [Point.distance(p, pt) for pt in self if p != pt] cl = max(dists) return self[dists.index(cl)]
def test_tang(self): p0 = Point(0, 0) p1 = Point(20, 20) tang = Point.tang(p0, p0, p1, p1, 0.5) print(tang)
class Shape: def __init__(self, start_point: Point = None): self.head = start_point if start_point else None self.segments: list = [] self.closed = False self.zindex = 0 self.style = {} # fill, stroke, stroke_width def tail(self) -> Point: return self.segments[-1]['p2'] def addPoint(self, p: Point): last_point = self.tail() self.segments.append({'c1': p, 'c2': last_point, 'p2': p}) return self def add(self, p0: Point, p1: Point, p2: Point): self.segments.append({'c1': p0, 'c2': p1, 'p2': p2}) return self def close(self): self.closed = True return self def fill(self, fill: str, alpha=1): fill = resolve(fill) alpha = resolve(alpha) color: colour.Color = colour.hex2rgb(fill) self.style['fill'] = Color4f(color[0], color[1], color[2], alpha) return self def stroke(self, color: str, alpha=1, width=5): stroke_color = resolve(color) alpha = resolve(alpha) stroke_width = resolve(width) color: colour.Color = colour.hex2rgb(stroke_color) self.style['stroke'] = Color4f(color[0], color[1], color[2], alpha) self.style['stroke_width'] = stroke_width if stroke_width else None return self @staticmethod def line(p0: Point = (0, 0), p1: Point = (0, 0)): p0 = p0 if type(p0) == Point else Point(p0[0], p0[1]) p1 = p1 if type(p1) == Point else Point(p1[0], p1[1]) return Shapes.curve(p0, p0, p1, p1) def draw(self) -> Path: path = Path() path.moveTo(self.head) for seg in self.segments: path.cubicTo(seg['c1'], seg['c2'], seg['p2']) path.close() if self.closed else None return path def rotate(self, deg: float, origin: Point = None): points = [self.head] points.extend( np.concatenate([[s['c1'], s['c2'], s['p2']] for s in self.segments])) points = list(map(lambda p: (p.x, p.y), points)) origin = self.center() if origin is None else origin origin = (origin.x, origin.y) angle = np.deg2rad(deg) r = np.array([[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]]) o = np.atleast_2d(origin) p = np.atleast_2d(points) rotpoints = np.squeeze((r.dot(p.T - o.T) + o.T).T) self.head = Point(rotpoints[0][0], rotpoints[0][1]) self.segments.clear() for i in range(1, len(rotpoints) - 2, 3): c1 = Point(rotpoints[i][0], rotpoints[i][1]) c2 = Point(rotpoints[i + 1][0], rotpoints[i + 1][1]) p2 = Point(rotpoints[i + 2][0], rotpoints[i + 2][1]) self.segments.append({'c1': c1, 'c2': c2, 'p2': p2}) return self def center(self) -> Point: points = [self.head] + [s['p2'] for s in self.segments] x = np.average([p.x for p in points]) y = np.average([p.y for p in points]) return Point(x, y) def translate(self, p: Point): self.head = self.head + p for seg in self.segments: seg['c1'] = seg['c1'] + p seg['c2'] = seg['c2'] + p seg['p2'] = seg['p2'] + p return self def cp(self): new_shape = Shape() new_shape.head = self.head.cp() new_shape.segments = [{ 'c2': s['c2'], 'c1': s['c1'], 'p2': s['p2'] } for s in self.segments] new_shape.closed = self.closed new_shape.zindex = self.zindex new_shape.style = self.style.copy() return new_shape def subd(self, t: float): prev_p1 = self.head new_segs = [] for seg in self.segments: a = prev_p1 d = seg['p2'] b = seg['c1'] c = seg['c2'] e = ((b - a) * t) + a f = ((c - b) * t) + b g = ((d - c) * t) + c h = ((f - e) * t) + e j = ((g - f) * t) + f k = ((j - h) * t) + h new_segs.append({'c1': e, 'c2': h, 'p2': k}) new_segs.append({'c1': j, 'c2': g, 'p2': d}) prev_p1 = d self.segments = new_segs return self def reg(self, canvas: Canvas): canvas.add(self) return self def cpoints(self) -> list[Point]: return [self.head] + [seg['p2'] for seg in self.segments] def adjust(self): for i in range(1, len(self.segments)): s0 = self.segments[i - 1] d = Point.distance(s0['c2'], s0['p2']) nc1 = Utils.proj(s0['c2'], s0['p2'], 2 * d) self.segments[i]['c1'] = nc1 return self def noise(self, func): self.head = func(self, self.head) for index, seg in enumerate(self.segments): func(index, seg) return self
def test_rect_from_two_points (self): rect = Shapes.rect(Point(100, 100), Point(100, 400), 20) self.assertEqual(4, len(rect.segments))
def test_proj(self): mid = Point.mid(Point(100,100), Point(200,200)) self.assertEqual(Point(150,150), mid)
def line(p0: Point = (0, 0), p1: Point = (0, 0)): p0 = p0 if type(p0) == Point else Point(p0[0], p0[1]) p1 = p1 if type(p1) == Point else Point(p1[0], p1[1]) return Shapes.curve(p0, p0, p1, p1)
def center(self) -> Point: points = [self.head] + [s['p2'] for s in self.segments] x = np.average([p.x for p in points]) y = np.average([p.y for p in points]) return Point(x, y)
def test_point_at(self): p = Point.point_at(Point(0, 0), Point(0, 0), Point(100, 100), Point(100, 100), 0.5) self.assertEqual(Point(50, 50), p) p = Point.point_at(Point(100, 100), Point(100, 100), Point(1000, 100), Point(1000, 100), 0.2) self.assertEqual(Point(193.6, 100), p)
def test_interpolate(self): mylist = plist([Point(0, 0), Point(20, 30)]) points = mylist.interpolate() self.assertEqual(3, len(points)) self.assertEqual(Point(10, 15), points[1])