|
|
|
@ -10,19 +10,23 @@ WIDTH, HEIGHT = 1000, 1000
|
|
|
|
|
ANGLE_RANDOM_MIN = -0.6
|
|
|
|
|
ANGLE_RANDOM_MAX = 0.6
|
|
|
|
|
# how much to shrink each consecutive circle
|
|
|
|
|
SHRINK = 0.00004
|
|
|
|
|
SHRINK = 0.00002
|
|
|
|
|
|
|
|
|
|
class Branch():
|
|
|
|
|
def __init__(self, idx, ctx, x, y, r, ang):
|
|
|
|
|
ctx.set_source_rgb(255, 0, 0)
|
|
|
|
|
#ctx.set_source_rgb(255, 0, 0)
|
|
|
|
|
self.nodes = [Node(ctx, x, y, r, ang, dry=True)]
|
|
|
|
|
ctx.set_source_rgb(0,0, 0)
|
|
|
|
|
#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()
|
|
|
|
@ -34,7 +38,8 @@ class Branch():
|
|
|
|
|
self.ended = True
|
|
|
|
|
return False
|
|
|
|
|
# did we hit canvas edge?
|
|
|
|
|
if next_x + next_r > 1 or next_x - next_r < 0:
|
|
|
|
|
# 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:
|
|
|
|
@ -44,14 +49,18 @@ class Branch():
|
|
|
|
|
# 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 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)
|
|
|
|
|
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):
|
|
|
|
@ -72,31 +81,35 @@ class Node():
|
|
|
|
|
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) // 10
|
|
|
|
|
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(15): # attempts at growing 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(-5, 5)
|
|
|
|
|
start_x = start_node.r * math.sin(start_angle) + start_node.x
|
|
|
|
|
start_y = start_node.r * math.cos(start_angle) + start_node.y
|
|
|
|
|
start_r = start_node.r - 0.002
|
|
|
|
|
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(branches)
|
|
|
|
|
branch.place_next([b for b in branches if b != branch])
|
|
|
|
|
return new_subs
|
|
|
|
|
def main():
|
|
|
|
|
|
|
|
|
@ -110,7 +123,7 @@ def main():
|
|
|
|
|
#for b in range(6):
|
|
|
|
|
# start_x = random.uniform(0.2, 0.8)
|
|
|
|
|
# start_y = random.uniform(0.2, 0.8)
|
|
|
|
|
start_r = 0.015
|
|
|
|
|
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))
|
|
|
|
@ -128,6 +141,7 @@ def main():
|
|
|
|
|
for x in range(8):
|
|
|
|
|
if subs == None:
|
|
|
|
|
return
|
|
|
|
|
ctx.set_source_rgb(0.0, 0.0, 0.1*x)
|
|
|
|
|
subs = grow_subs(ctx, subs, branches)
|
|
|
|
|
surface.write_to_png("out/hyphae.png") # Output to PNG
|
|
|
|
|
input('next')
|
|
|
|
|