#!/usr/bin/env python3 import cairo import math import random from utils import circle_fill from utils import random_color WIDTH, HEIGHT = 1000, 1000 ANGLE_RANDOM_MIN = -0.8 ANGLE_RANDOM_MAX = 0.8 # how much to shrink each consecutive circle SHRINK = 0.0002 class Branch(): def __init__(self, idx, ctx, x, y, r, ang): self.nodes = [Node(ctx, x, y, r, ang)] self.idx = idx self.ctx = ctx self.ended = False def _last_node(self): return self.nodes[-1] 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 # did we hit canvas edge? if next_x + next_r > 1 or next_x - next_r < 0: self.ended = True return if next_y + next_r > 1 or next_y - next_r < 0: self.ended = True return 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: #!= last and node != self.nodes[-2]: if circles_intersect(node.x, node.y, node.r, next_x, next_y, next_r): print(f'intersect: {next_x},{next_y} r: {next_r} with {node}') self.ended = True return next_ang = last.ang + random.uniform(ANGLE_RANDOM_MIN, ANGLE_RANDOM_MAX) print(f'next circle: {next_x},{next_y} r: {next_r} a: {next_ang}') self.nodes.append(Node(self.ctx, next_x, next_y, next_r, next_ang)) 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 print(f'd: {distance} r1 + r2: {combined_r}') return distance < combined_r class Node(): def __init__(self, ctx, x, y, r, ang): self.x = x self.y = y self.r = r self.ang = ang 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 main(): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT) ctx = cairo.Context(surface) ctx.scale(WIDTH, HEIGHT) # Normalizing the canvas branches = [] for b in range(6): start_x = random.uniform(0.3, 0.7) start_y = random.uniform(0.3, 0.7) start_r = 0.02 start_angle = random.randint(0, 360) branches.append(Branch(b, ctx, start_x, start_y, start_r, start_angle)) for x in range(100): for branch in branches: branch.place_next(branches) surface.write_to_png("out/hyphae.png") # Output to PNG if __name__ == '__main__': main()