├── .gitignore ├── README.md └── main.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.png 2 | *.jpg 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Field 2 | 3 | Creating computer art by simulating charged particle field lines. 4 | 5 | ![Sample](http://i.imgur.com/9DxjkoX.png) 6 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from math import hypot, atan2, sin, cos, pi, radians 2 | import cairo 3 | import colorsys 4 | import random 5 | 6 | class Model(object): 7 | def __init__(self): 8 | self.particles = [] 9 | def add(self, x, y, m=1.0): 10 | self.particles.append((x, y, m)) 11 | def test(self, x, y): 12 | dx = 0 13 | dy = 0 14 | for px, py, pm in self.particles: 15 | d = hypot(x - px, y - py) 16 | angle = atan2(y - py, x - px) 17 | dx += pm * cos(angle) / d 18 | dy += pm * sin(angle) / d 19 | angle = atan2(dy, dx) + pi / 2 20 | dx = cos(angle) 21 | dy = sin(angle) 22 | return (dx, dy) 23 | 24 | def points(sides): 25 | x = 0.5 26 | y = 0.5 27 | rotation = 0 28 | angle = 2 * pi / sides 29 | rotation = rotation - pi / 2 30 | angles = [angle * i + rotation for i in range(sides)] 31 | d = 0.35 32 | return [(x + cos(a) * d, y + sin(a) * d) for a in angles] 33 | 34 | def draw_path(dc, model, scale, width, r, g, b): 35 | n = 128 36 | f = 1 / 1024.0 37 | sx = random.random() 38 | sy = random.random() 39 | for m in [-1, 1]: 40 | x, y = sx, sy 41 | dc.move_to(x, y) 42 | for j in range(n): 43 | dx, dy = model.test(x, y) 44 | dc.line_to(x, y) 45 | p = 1.0 - (j / (n - 1.0)) ** 2 46 | a = p * 0.3 47 | dc.set_source_rgba(r, g, b, a) 48 | dc.set_line_width(width * p / scale) 49 | if j: 50 | dc.stroke() 51 | dc.move_to(x, y) 52 | x += dx * f * m 53 | y += dy * f * m 54 | if x < -0.1 or y < -0.1 or x > 1.1 or y > 1.1: 55 | break 56 | 57 | def main(): 58 | size = 4096 59 | scale = size 60 | surface = cairo.ImageSurface(cairo.FORMAT_RGB24, size, size) 61 | dc = cairo.Context(surface) 62 | dc.set_line_cap(cairo.LINE_CAP_ROUND) 63 | dc.set_line_join(cairo.LINE_JOIN_ROUND) 64 | dc.scale(scale, scale) 65 | dc.set_source_rgb(0, 0, 0) 66 | dc.paint() 67 | model = Model() 68 | for x, y in points(5): 69 | model.add(x, y) 70 | model.add(0.5, 0.5, 0.1) 71 | for i in range(512): 72 | h = random.random() * 0.06 + 0.04 73 | s = random.random() * 0.8 + 0.2 74 | v = random.random() * 0.2 + 0.8 75 | r, g, b = colorsys.hsv_to_rgb(h, s, v) 76 | w = random.random() * 48 + 16 77 | draw_path(dc, model, scale, w, r, g, b) 78 | surface.write_to_png('output.png') 79 | 80 | if __name__ == '__main__': 81 | main() 82 | --------------------------------------------------------------------------------