Created
August 31, 2018 02:47
-
-
Save nickwan/e469d702c26951d05e348e8b7adcdfd5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### Mostly taken from this blog post | |
# http://superfluoussextant.com/circlepusher.html | |
import random | |
from PIL import Image, ImageDraw | |
import math | |
class Circle: | |
def __init__(self, radius, location, color): | |
""" | |
:param radius: radius of circle in pixels | |
:param location: two tuple of x,y | |
""" | |
self.radius = radius | |
self.location = location | |
self.angle = random.randint(0, 360) | |
self.curve = random.randint(-45, 45)/100 | |
self.active = True | |
self.color = color | |
@property | |
def x(self): | |
return self.location[0] | |
@property | |
def y(self): | |
return self.location[1] | |
def draw(self): | |
if not self.active: | |
return | |
image_draw.ellipse((self.x - self.radius, | |
self.y - self.radius, | |
self.x + self.radius, | |
self.y + self.radius), | |
fill=self.color) | |
def push(self): | |
if not self.active: | |
return | |
if self.should_make_baby(): | |
circles.append(self.make_baby()) | |
# Let's step by 1/4 of the radius each time | |
step = self.radius / 4 | |
rad_angle = math.radians(self.angle) | |
next_step = (self.x + step*math.cos(rad_angle), | |
self.y + step*math.sin(rad_angle)) | |
# Stepping by 1/4 of the radius will put us still inside our current radius, so let's look a bit further ahead | |
big_step = (self.x + self.radius*2*math.cos(rad_angle), | |
self.y + self.radius*2*math.sin(rad_angle)) | |
if self.within_bounds(next_step) and self.free_spot(big_step): | |
# if self.within_bounds(next_step): | |
self.location = next_step | |
else: | |
self.active = False | |
if self in circles: | |
circles.remove(self) | |
self.angle = (self.angle + self.curve) % 360 | |
@staticmethod | |
def within_bounds(location): | |
if location[0] < 0 or location[0] > image_bounds[0] or location[1] < 0 or location[1] > image_bounds[1]: | |
return False | |
return True | |
def free_spot(self, spot): | |
# Simply check the canvas to see if the passed spot is white | |
return self.within_bounds(spot) and image.getpixel(spot) == (0,0,0) | |
@staticmethod | |
def should_make_baby(): | |
# 1/50 chance to make a baby | |
return not random.randint(0, 15) | |
def make_baby(self): | |
return Circle(self.radius*.75, self.location, self.color) | |
image_bounds = (600, 600) | |
image = Image.new('RGB', image_bounds, 'black') | |
image_draw = ImageDraw.Draw(image) | |
circles = [] | |
for _ in range(13): | |
circles.append(Circle(random.randint(2,8), | |
(random.randint(0, image_bounds[0]), | |
random.randint(0, image_bounds[1])), | |
(random.randint(0,255), | |
random.randint(0,255), | |
random.randint(0,255)))) | |
for _ in range(1000): | |
for circle in circles: | |
circle.draw() | |
circle.push() | |
image.save('img/01.png') | |
image.show() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment