From 185adf8df1037567e73a793df7325ddc0ab16277 Mon Sep 17 00:00:00 2001 From: Felix Pankratz Date: Wed, 29 Dec 2021 01:37:56 +0100 Subject: [PATCH] hyphae 4 pixelflut :) --- hyphae_pixelflut.py | 155 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 hyphae_pixelflut.py diff --git a/hyphae_pixelflut.py b/hyphae_pixelflut.py new file mode 100644 index 0000000..e1583e7 --- /dev/null +++ b/hyphae_pixelflut.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 + +import cairo +import math +import random +from utils import circle_fill +from utils import random_color +from pixelflut import surface_to_pixelflut + +WIDTH, HEIGHT = 1920, 1080 +ANGLE_RANDOM_MIN = -0.6 +ANGLE_RANDOM_MAX = 0.6 +# how much to shrink each consecutive circle +SHRINK = 0.00002 + +class Branch(): + def __init__(self, idx, ctx, x, y, r, ang): + #ctx.set_source_rgb(255, 0, 0) + self.nodes = [Node(ctx, x, y, r, ang, dry=True)] + #ctx.set_source_rgb(0,0, 0) + self.idx = idx + self.ctx = ctx + self.ended = False + self.ignores = [] + self.first = True + + def _last_node(self): + return self.nodes[-1] + def set_ignores(self, ignores): + self.ignores = ignores + def place_next(self, branches): + if not self.ended: + last = self._last_node() + next_x = last.r * math.sin(last.ang) + last.x + next_y = last.r * math.cos(last.ang) + last.y + next_r = last.r - SHRINK + # too small? + if next_r < 0.000000001: + self.ended = True + return False + # did we hit canvas edge? + # if next_x + next_r > 1 or next_x - next_r < 0: + if (math.pow(next_x - 0.5, 2) + math.pow(next_y - 0.5, 2)) > math.pow(0.4, 2): + self.ended = True + return False + if next_y + next_r > 1 or next_y - next_r < 0: + self.ended = True + return False + last_nodes = self.nodes[-2:] + # did we hit another circle? + for branch in branches: + for node in branch.nodes: + if node not in last_nodes and node not in self.ignores: #!= last and node != self.nodes[-2]: + if circles_intersect(node.x, node.y, node.r, + next_x, next_y, next_r): + self.ended = True + return False + + next_ang = last.ang + (random.uniform(ANGLE_RANDOM_MIN, ANGLE_RANDOM_MAX) * (1 - 40*last.r)) + self.nodes.append(Node(self.ctx, next_x, next_y, next_r, next_ang)) + if self.first: + self.first = False + first = self.nodes[0] + circle_fill(self.ctx, first.x, first.y, first.r) + return True + +def circles_intersect(x1, y1, r1, x2, y2, r2): + distance = math.sqrt(math.pow(abs(x2 - x1), 2) + math.pow(abs(y2 - y1), 2)) + combined_r = r1 + r2 + return distance < combined_r + + +class Node(): + def __init__(self, ctx, x, y, r, ang, dry=False): + self.x = x + self.y = y + self.r = r + self.ang = ang + if not dry: + circle_fill(ctx, x, y, r) + def __ne__(self, other): + return self.x != other.x or self.y != other.y or self.r != other.r or self.ang != other.ang + +def grow_subs(ctx, subs, branches): + source = ctx.get_source() + new_subs = [] + created = False + for branch in subs: + # create a sub branch based on length + sub_branches = len(branch.nodes) // 7 * 2 + #print(f'creating {sub_branches} subs') + if sub_branches > 1: + created = True + + for i in range(sub_branches): + for n in range(60): # attempts at growing branches + start_node = random.choice(branch.nodes) + # start perpendicular to our last angle + start_angle = start_node.ang + random.choice([90, -90]) + random.uniform(-30, 30) + start_x = (start_node.r * 1.2) * math.sin(start_angle) + start_node.x + start_y = (start_node.r * 1.2) * math.cos(start_angle) + start_node.y + start_r = start_node.r * 0.8 + new_branch = Branch(i, ctx, start_x, start_y, start_r, start_angle) + if new_branch.place_next(branches): + break + new_branch.set_ignores(branch.nodes) + branches.append(new_branch) + new_subs.append(new_branch) + if not created: + return + while not all([branch.ended for branch in new_subs]): + for branch in new_subs: + branch.place_next([b for b in branches if b != branch]) + return new_subs +def main(): + + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT) + ctx = cairo.Context(surface) + + ctx.scale(WIDTH, HEIGHT) # Normalizing the canvas + + # place seeds + branches = [] + #for b in range(6): + # start_x = random.uniform(0.2, 0.8) + # start_y = random.uniform(0.2, 0.8) + start_r = 0.01 + # start_angle = random.randint(0, 360) + # branches.append(Branch(b, ctx, start_x, start_y, start_r, start_angle)) + branches.append(Branch(0, ctx, 0.5, 0.4, start_r, 180)) + branches.append(Branch(1, ctx, 0.4, 0.5, start_r, 270)) + branches.append(Branch(2, ctx, 0.6, 0.5, start_r, 90)) + branches.append(Branch(3, ctx, 0.5, 0.6, start_r, 0)) + # grow initial branches + while not all([b.ended for b in branches]): + for branch in branches: + branch.place_next(branches) + # start branching off + #subs = [] + subs = branches + try: + for x in range(8): + if subs == None: + return + r, g, b = random_color() + ctx.set_source_rgb(r, g, b) + subs = grow_subs(ctx, subs, branches) + surface_to_pixelflut(surface) + #surface.write_to_png("out/hyphae.png") # Output to PNG + #input('next') + finally: + surface.write_to_png("out/hyphae.png") # Output to PNG + +if __name__ == '__main__': + main()