Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
f85d424c7b | |||
6d41db8051 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +0,0 @@
|
||||
out/
|
||||
__pycache__/
|
||||
nft/
|
49
gui.py
Normal file
49
gui.py
Normal file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python3
|
||||
from tkinter import *
|
||||
from tkinter import ttk
|
||||
|
||||
def init_gui(root):
|
||||
|
||||
def btn_apply_clicked():
|
||||
try:
|
||||
width = int(input_width.get())
|
||||
height = int(input_height.get())
|
||||
print(f'apply clicked, resolution: {width}x{height}')
|
||||
|
||||
except:
|
||||
print('Please enter a valid resolution')
|
||||
|
||||
root.title('Generative Art')
|
||||
algos = ['waves', 'hyphae']
|
||||
|
||||
frame_top = ttk.Frame(root, padding=10)
|
||||
frame_top.pack(side=TOP)
|
||||
#frame_top.grid()
|
||||
#ttk.Label(frame_top, text="Hello World!").grid(column=0, row=0)
|
||||
|
||||
Combo = ttk.Combobox(frame_top, values = algos)
|
||||
Combo.set('Pick an algorithm')
|
||||
Combo.grid(column=0, row=0)
|
||||
|
||||
input_width = ttk.Entry(frame_top, width=6)
|
||||
input_width.insert(0, 'Width')
|
||||
input_width.grid(column=1, row=0)
|
||||
input_height = ttk.Entry(frame_top, width=6)
|
||||
input_height.insert(0, 'Height')
|
||||
input_height.grid(column=2, row=0)
|
||||
|
||||
ttk.Button(frame_top, text="Apply", command=btn_apply_clicked).grid(column=3, row=0)
|
||||
ttk.Button(frame_top, text="Quit", command=root.destroy).grid(column=4, row=0)
|
||||
|
||||
frame_left = ttk.Frame(root)
|
||||
frame_left.pack(side=LEFT)
|
||||
frame_right = ttk.Frame(root)
|
||||
frame_right.pack(side=RIGHT)
|
||||
|
||||
def main():
|
||||
root = Tk()
|
||||
init_gui(root)
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -6,7 +6,7 @@ import random
|
||||
from utils import circle_fill
|
||||
from utils import random_color
|
||||
|
||||
WIDTH, HEIGHT = 500, 500
|
||||
WIDTH, HEIGHT = 1000, 1000
|
||||
ANGLE_RANDOM_MIN = -0.6
|
||||
ANGLE_RANDOM_MAX = 0.6
|
||||
# how much to shrink each consecutive circle
|
||||
@ -87,6 +87,7 @@ def grow_subs(ctx, subs, branches):
|
||||
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
|
||||
|
||||
@ -142,6 +143,8 @@ def main():
|
||||
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')
|
||||
finally:
|
||||
surface.write_to_png("out/hyphae.png") # Output to PNG
|
||||
|
||||
|
217
hyphae_nft.py
217
hyphae_nft.py
@ -1,217 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import cairo
|
||||
import math
|
||||
import random
|
||||
import threading
|
||||
import time
|
||||
import socket
|
||||
import colorsys
|
||||
from utils import circle_fill
|
||||
from utils import circle_clip_fill
|
||||
from utils import random_color
|
||||
import numpy
|
||||
|
||||
WIDTH, HEIGHT = 1024, 1024
|
||||
ANGLE_RANDOM_MIN = -0.6
|
||||
ANGLE_RANDOM_MAX = 0.6
|
||||
# how much to shrink each consecutive circle
|
||||
SHRINK = 0.00004
|
||||
start_r = 0.01
|
||||
|
||||
NODE_ARRAY = None
|
||||
|
||||
start_hue = 0
|
||||
|
||||
# should be ~ 1 px
|
||||
MIN_RADIUS = ((1/WIDTH) + (1/HEIGHT)) / 2
|
||||
|
||||
def chunker(seq, size):
|
||||
return (seq[pos:pos + size] for pos in range(0, len(seq), size))
|
||||
|
||||
class Branch():
|
||||
def __init__(self, idx, ctx, x, y, r, ang, mother=None):
|
||||
#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
|
||||
self.mother = mother
|
||||
|
||||
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 < MIN_RADIUS:
|
||||
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?
|
||||
# search radius is radius of biggest possible circle + our radius
|
||||
search_radius = next_r + start_r
|
||||
search_box_x1 = next_x - search_radius
|
||||
search_box_x2 = next_x + search_radius
|
||||
search_box_y1 = next_y - search_radius
|
||||
search_box_y2 = next_y + search_radius
|
||||
filtered = NODE_ARRAY[NODE_ARRAY['x'] > search_box_x1]
|
||||
filtered = filtered[filtered['x'] < search_box_x2]
|
||||
filtered = filtered[filtered['y'] > search_box_y1]
|
||||
filtered = filtered[filtered['y'] < search_box_y2]
|
||||
# remove previous nodes
|
||||
for ln in last_nodes:
|
||||
filtered = filtered[ (filtered['x'] != ln.x) & (filtered['y'] != ln.y) ]
|
||||
for possible_hit in filtered:
|
||||
if circles_intersect(*possible_hit, 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]
|
||||
if self.mother:
|
||||
self.ctx.set_operator(cairo.Operator.DEST_OVER)
|
||||
circle_fill(self.ctx, first.x, first.y, first.r)
|
||||
self.ctx.set_operator(cairo.Operator.OVER)
|
||||
else:
|
||||
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):
|
||||
global NODE_ARRAY
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.r = r
|
||||
self.ang = ang
|
||||
if not dry:
|
||||
NODE_ARRAY = numpy.append(NODE_ARRAY,
|
||||
numpy.array([(x, y, r)],
|
||||
dtype=[('x', 'float'), ('y', 'float'), ('r', 'float')])
|
||||
)
|
||||
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_sub(ctx, branch, branches, new_subs):
|
||||
# create a sub branch based on length
|
||||
sub_branches = len(branch.nodes) // 8 * 2
|
||||
if sub_branches == 0: return
|
||||
#sub_branches = 4
|
||||
#print(f'creating {sub_branches} subs')
|
||||
for i in range(sub_branches):
|
||||
for n in range(20): # attempts at growing branches
|
||||
found_ang = False
|
||||
start_node = random.choice(branch.nodes)
|
||||
for a_try in range(10):
|
||||
# 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(0, ctx, start_x, start_y, start_r, start_angle, mother=start_node)
|
||||
if new_branch.place_next(branches):
|
||||
found_ang = True
|
||||
break
|
||||
if found_ang:
|
||||
break
|
||||
grow_branch_until_ended(new_branch, branches)
|
||||
|
||||
new_branch.set_ignores(branch.nodes)
|
||||
branches.append(new_branch)
|
||||
new_subs.append(new_branch)
|
||||
|
||||
def grow_branch_until_ended(branch, branches):
|
||||
while not branch.ended:
|
||||
branch.place_next([b for b in branches if b != branch])
|
||||
|
||||
def grow_subs(ctx, subs, branches):
|
||||
source = ctx.get_source()
|
||||
new_subs = []
|
||||
for branch in subs:
|
||||
grow_sub(ctx, branch, branches, new_subs)
|
||||
#for branch in new_subs:
|
||||
# grow_branch_until_ended(branch, branches)
|
||||
return new_subs
|
||||
|
||||
def main():
|
||||
global start_hue
|
||||
global NODE_ARRAY
|
||||
for run in range(32):
|
||||
random.seed()
|
||||
start_hue = random.uniform(0, 1)
|
||||
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT)
|
||||
ctx = cairo.Context(surface)
|
||||
|
||||
ctx.scale(WIDTH, HEIGHT) # Normalizing the canvas
|
||||
|
||||
|
||||
# place seeds
|
||||
branches = []
|
||||
|
||||
r, g, b = colorsys.hsv_to_rgb(start_hue, 1.0, 1.0)
|
||||
#r, g, b = random_color()
|
||||
ctx.set_source_rgb(r, g, b)
|
||||
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))
|
||||
NODE_ARRAY = numpy.array([
|
||||
(0.5, 0.4, start_r),
|
||||
(0.4, 0.5, start_r),
|
||||
(0.6, 0.5, start_r),
|
||||
(0.5, 0.6, start_r)
|
||||
], dtype = [('x', 'float'), ('y', 'float'), ('r', 'float')])
|
||||
|
||||
# grow initial branches
|
||||
print('growing initial branches')
|
||||
for branch in branches:
|
||||
grow_branch_until_ended(branch, branches)
|
||||
subs = branches
|
||||
rarity = 3 + random.randint(0, 5)
|
||||
print(f'rarity: {rarity}')
|
||||
try:
|
||||
for x in range(rarity):
|
||||
if subs == None:
|
||||
return
|
||||
r, g, b = colorsys.hsv_to_rgb(start_hue + x * 0.05, 1.0, 1.0)
|
||||
ctx.set_source_rgb(r, g, b)
|
||||
subs = grow_subs(ctx, subs, branches)
|
||||
|
||||
print(f'iteration {x} done')
|
||||
#surface.write_to_png("out/hyphae.png") # Output to PNG
|
||||
r, g, b = colorsys.hsv_to_rgb(start_hue - 0.05, 1.0, 0.2)
|
||||
ctx.set_source_rgb(r, g, b)
|
||||
ctx.set_operator(cairo.Operator.DEST_OVER)
|
||||
circle_fill(ctx, 0.5, 0.5, 0.4)
|
||||
finally:
|
||||
surface.write_to_png(f"out/hyphae_{run}_{rarity}.png") # Output to PNG
|
||||
print(f'run {run} complete')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,229 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import cairo
|
||||
import math
|
||||
import random
|
||||
import threading
|
||||
import time
|
||||
import socket
|
||||
import colorsys
|
||||
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
|
||||
|
||||
hitmap = list()
|
||||
|
||||
DRAW_MAP = None
|
||||
|
||||
start_hue = random.uniform(0, 1)
|
||||
|
||||
SERVER_IP = '192.168.178.75'
|
||||
#SERVER_IP = '127.0.0.1'
|
||||
SERVER_PORT = 1234
|
||||
#SERVER_PORT = 1337
|
||||
|
||||
def chunker(seq, size):
|
||||
return (seq[pos:pos + size] for pos in range(0, len(seq), size))
|
||||
|
||||
def surface_to_pixelflut():
|
||||
global DRAW_MAP
|
||||
while True:
|
||||
data = DRAW_MAP.get_data()
|
||||
pixels = data.hex()
|
||||
x, y = 0, 0
|
||||
#to_send = list()
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
# for chunk in chunker(pixels, 65536):
|
||||
pxstr = ''
|
||||
for hexpx in chunker(pixels, 8):
|
||||
if x > 1919:
|
||||
x = 0
|
||||
y += 1
|
||||
x += 1
|
||||
if hexpx[6:8] == '00':
|
||||
continue
|
||||
pxstr += f'PX {x} {y} {hexpx[:6]}\n'
|
||||
#to_send.append(pxstr)
|
||||
#for px in to_send:
|
||||
# s.sendall(px.encode())
|
||||
print('!! socket opened !!')
|
||||
s.connect((SERVER_IP, SERVER_PORT))
|
||||
s.sendall(pxstr.encode())
|
||||
#print(f'sent {pxstr}')
|
||||
print('!! transmission to pixelflut finished !!')
|
||||
|
||||
|
||||
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_sub(ctx, branch, branches, new_subs):
|
||||
# create a sub branch based on length
|
||||
sub_branches = len(branch.nodes) // 7 * 2
|
||||
if sub_branches == 0: return
|
||||
#sub_branches = 4
|
||||
#print(f'creating {sub_branches} subs')
|
||||
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(0, 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)
|
||||
|
||||
def grow_branch_until_ended(branch, branches):
|
||||
while not branch.ended:
|
||||
branch.place_next([b for b in branches if b != branch])
|
||||
|
||||
def grow_subs(ctx, subs, branches):
|
||||
source = ctx.get_source()
|
||||
new_subs = []
|
||||
#threads = list()
|
||||
print('spawning sub growing threads')
|
||||
for branch in subs:
|
||||
# x = threading.Thread(target=grow_sub, args=(ctx, branch, branches, new_subs))
|
||||
# threads.append(x)
|
||||
# x.start()
|
||||
grow_sub(ctx, branch, branches, new_subs)
|
||||
#for tidx, thread in enumerate(threads):
|
||||
# thread.join()
|
||||
print('all threads finished')
|
||||
print('starting place_next threads')
|
||||
#pl_threads = list()
|
||||
for branch in new_subs:
|
||||
grow_branch_until_ended(branch, branches)
|
||||
print('place_next done')
|
||||
|
||||
#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():
|
||||
global DRAW_MAP
|
||||
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
|
||||
r, g, b = colorsys.hsv_to_rgb(start_hue, 1.0, 1.0)
|
||||
#r, g, b = random_color()
|
||||
ctx.set_source_rgb(r, g, b)
|
||||
# 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
|
||||
print('growing 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
|
||||
DRAW_MAP = surface
|
||||
t = threading.Thread(target=surface_to_pixelflut, daemon=True)
|
||||
t.start()
|
||||
try:
|
||||
for x in range(8):
|
||||
if subs == None:
|
||||
return
|
||||
r, g, b = colorsys.hsv_to_rgb(start_hue + x * 0.05, 1.0, 1.0)
|
||||
#r, g, b = random_color()
|
||||
ctx.set_source_rgb(r, g, b)
|
||||
subs = grow_subs(ctx, subs, branches)
|
||||
|
||||
print(f'iteration {x} done, sending to pixelflut')
|
||||
DRAW_MAP = surface
|
||||
#surface.write_to_png("out/hyphae.png") # Output to PNG
|
||||
finally:
|
||||
surface.write_to_png("out/hyphae.png") # Output to PNG
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
62
moon.py
62
moon.py
@ -1,62 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import cairo
|
||||
import math
|
||||
import random
|
||||
from utils import circle_fill
|
||||
from utils import draw_crater
|
||||
from utils import random_color
|
||||
from utils import moon_shade
|
||||
|
||||
WIDTH, HEIGHT = 100, 100
|
||||
|
||||
def main():
|
||||
|
||||
import sys
|
||||
seed = sys.argv[1]
|
||||
random.seed(seed)
|
||||
|
||||
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT)
|
||||
ctx = cairo.Context(surface)
|
||||
|
||||
ctx.scale(WIDTH, HEIGHT) # Normalizing the canvas
|
||||
ctx.set_source_rgb(0.9, 0.9, 0.9)
|
||||
# draw "full" moon
|
||||
circle_fill(ctx, 0.5, 0.5, 0.5)
|
||||
|
||||
# make sure we only draw "inside"
|
||||
ctx.set_operator(cairo.Operator.ATOP)
|
||||
|
||||
|
||||
|
||||
# draw craters
|
||||
ctx.set_source_rgb(0.5, 0.5, 0.5)
|
||||
for c in range(30):
|
||||
#c_col = random.uniform(0.4, 0.6)
|
||||
#ctx.set_source_rgb(c_col, c_col, c_col)
|
||||
c_x = random.uniform(0.0, 1.0)
|
||||
c_y = random.uniform(0.0, 1.0)
|
||||
c_r = random.uniform(0.01, 0.1)
|
||||
draw_crater(ctx, c_x, c_y, c_r)
|
||||
|
||||
# draw "aura"
|
||||
a_r, a_g, a_b = random_color()
|
||||
p = cairo.RadialGradient(0.5, 0.5, 0.25, 0.5, 0.5, 0.5)
|
||||
#p.add_color_stop_rgba(0.0, 0.9, 0.9, 0.9, 0.0)
|
||||
p.add_color_stop_rgba(0.0, a_r, a_g, a_b, 0.0)
|
||||
p.add_color_stop_rgba(0.3, a_r, a_g, a_b, 0.0)
|
||||
p.add_color_stop_rgba(1.0, a_r, a_g, a_b, 0.8)
|
||||
ctx.set_source(p)
|
||||
ctx.arc(0.5, 0.5, 0.5, 0, math.pi*2)
|
||||
#ctx.arc(0.5, 0.5, 0.3, 0, math.pi*2)
|
||||
ctx.fill()
|
||||
|
||||
# draw "shade" of the phase
|
||||
ctx.set_source_rgba(0.1, 0.1, 0.1, 0.95)
|
||||
phase_pos = random.uniform(-1.2, 1.2)
|
||||
#circle_fill(ctx, phase_pos, 0.5, 0.5)
|
||||
moon_shade(ctx, phase_pos)
|
||||
surface.write_to_png(f"nft/{seed}_moon.png") # Output to PNG
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
BIN
out/hyphae.png
BIN
out/hyphae.png
Binary file not shown.
Before Width: | Height: | Size: 661 KiB |
37
pixelflut.py
37
pixelflut.py
@ -1,37 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import io
|
||||
import socket
|
||||
sys.path.append('..')
|
||||
from waves import create_wpotd
|
||||
|
||||
#SERVER_IP = '192.168.178.75'
|
||||
SERVER_IP = '127.0.0.1'
|
||||
SERVER_PORT = 1234
|
||||
|
||||
def chunker(seq, size):
|
||||
return (seq[pos:pos + size] for pos in range(0, len(seq), size))
|
||||
|
||||
def surface_to_pixelflut():
|
||||
global DRAW_MAP
|
||||
while True:
|
||||
data = DRAW_MAP.get_data()
|
||||
pixels = data.hex()
|
||||
x, y = 0, 0
|
||||
to_send = list()
|
||||
for hexpx in chunker(pixels, 8):
|
||||
if x > 1919:
|
||||
x = 0
|
||||
y += 1
|
||||
x += 1
|
||||
if hexpx[6:8] == '00':
|
||||
continue
|
||||
pxstr = f'PX {x} {y} {hexpx[:6]}\n'
|
||||
to_send.append(pxstr)
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.connect((SERVER_IP, SERVER_PORT))
|
||||
print('!! socket opened !!')
|
||||
for px in to_send:
|
||||
s.sendall(px.encode())
|
||||
#print(f'sent {pxstr}')
|
||||
print('!! transmission to pixelflut finished !!')
|
37
utils.py
37
utils.py
@ -5,46 +5,11 @@ def circle(ctx,x,y,r):
|
||||
ctx.arc(x,y,r,0,math.pi*2.)
|
||||
ctx.stroke()
|
||||
|
||||
|
||||
def circle_fill(ctx,x,y,r):
|
||||
ctx.arc(x,y,r,0,math.pi*2.)
|
||||
ctx.fill()
|
||||
|
||||
def draw_crater(ctx,x,y,r):
|
||||
for n in range(3):
|
||||
off_x = random.uniform(-0.01, 0.01)
|
||||
off_y = random.uniform(-0.01, 0.01)
|
||||
ctx.arc(x + off_x, y + off_y, r, 0, math.pi*2.)
|
||||
ctx.fill()
|
||||
|
||||
def moon_shade(ctx, phase, nothern=True):
|
||||
#phase=-0.4
|
||||
#x_max = 0.7
|
||||
x_max = 1.
|
||||
# the shadow always has to "hug" one side - pick which
|
||||
ctx.move_to(0.5, 0.0)
|
||||
if phase > 0:
|
||||
ctx.curve_to(-0.2, 0, -0.2, 1, 0.5, 1)
|
||||
elif phase < 0:
|
||||
ctx.curve_to(1.2, 0, 1.2, 1, 0.5, 1)
|
||||
else:
|
||||
return
|
||||
#ctx.close_path()
|
||||
#ctx.fill()
|
||||
|
||||
#ctx.move_to(0.5, 0.0)
|
||||
#ctx.curve_to(x_max*phase, 0.5-(0.5*phase), x_max*phase, 0.5+(0.5*phase), 0.5, 1)
|
||||
#ctx.curve_to(0.5 + (x_max*phase), 0.5+(0.5*phase), 0.5+ (x_max*phase), 0.5-(0.5*phase), 0.5, 0)
|
||||
|
||||
# helper x is the same for both points
|
||||
h_x = abs(x_max * phase)
|
||||
h_y = abs(h_x - 0.5)
|
||||
|
||||
#y_c = (phase * 0.5)
|
||||
#ctx.curve_to(x_max*phase, 0.5+abs(0.5*phase-0.5), (x_max*phase), 0.5-abs(0.5*phase-0.5), 0.5, 0)
|
||||
ctx.curve_to(h_x, 0.5 + h_y, h_x, 0.5-h_y, 0.5, 0)
|
||||
#ctx.close_path()
|
||||
ctx.fill()
|
||||
|
||||
|
||||
def random_color():
|
||||
red = random.uniform(0, 1)
|
||||
|
150
waves.py
150
waves.py
@ -2,79 +2,97 @@
|
||||
|
||||
import cairo
|
||||
import math
|
||||
from datetime import datetime
|
||||
import datetime
|
||||
import calendar
|
||||
import random
|
||||
import colorsys
|
||||
import argparse
|
||||
from utils import random_color
|
||||
|
||||
|
||||
# dimensions of the output image
|
||||
WIDTH, HEIGHT = 1920, 1080
|
||||
# how much should the phases be offset?
|
||||
WAVE_OFFSET = math.pi / random.choice([1, 2, 4]) #-1.9
|
||||
# amplitude of the sine wave
|
||||
#AMPLITUDE = 9
|
||||
# only 1 color with shades?
|
||||
MONOCHROME = True
|
||||
# works only if monochrome is set - uses todays date as base for the color
|
||||
DATE_BASED_COLOR = True
|
||||
DATE_BASED_AMPLITUDE = True
|
||||
DATE_BASED_COUNT = True
|
||||
# background black? White otherwise:
|
||||
DARK_BG = True
|
||||
# precision of the calculation
|
||||
PRECISION = 10000
|
||||
|
||||
|
||||
def get_color_from_date(date: datetime) -> tuple[float, float, float]:
|
||||
"""Return a color based on the progress through the year."""
|
||||
def days_color(date):
|
||||
import colorsys
|
||||
# a day between 1 and 365 (inclusive)
|
||||
today = date.timetuple().tm_yday
|
||||
days_in_year = 365 + calendar.isleap(date.year)
|
||||
year = date.year
|
||||
days_in_year = 365 + calendar.isleap(year)
|
||||
# between 0 and 1, how far through the year are we?
|
||||
progress = today / days_in_year
|
||||
progress = today/days_in_year
|
||||
return colorsys.hsv_to_rgb(progress, 1, 0.9)
|
||||
|
||||
|
||||
def get_amplitude_from_date(date: datetime, waves) -> float:
|
||||
"""Return the amplitude of waves, based on progress through the month."""
|
||||
def days_amp(date, waves):
|
||||
day = date.day
|
||||
days_in_month = calendar.monthrange(date.year, date.month)[1]
|
||||
max_amp = 1 / waves / 2
|
||||
return date.day / days_in_month * max_amp
|
||||
max_amp = 1/waves/2
|
||||
return day/days_in_month * max_amp
|
||||
|
||||
def days_count(date):
|
||||
return date.month
|
||||
|
||||
def create_wpotd(
|
||||
width: int,
|
||||
height: int,
|
||||
frequency: int,
|
||||
wave_offset: float,
|
||||
date: datetime = datetime.now(),
|
||||
dark: bool = True,
|
||||
) -> cairo.ImageSurface:
|
||||
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
|
||||
def create_wpotd(output):
|
||||
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, WIDTH, HEIGHT)
|
||||
ctx = cairo.Context(surface)
|
||||
|
||||
ctx.scale(width, height) # Normalizing the canvas
|
||||
# ctx.set_antialias(cairo.Antialias.BEST)
|
||||
ctx.scale(WIDTH, HEIGHT) # Normalizing the canvas
|
||||
#ctx.set_antialias(cairo.Antialias.BEST)
|
||||
|
||||
lastpoints = [(x / PRECISION, 0) for x in range(PRECISION + 1)]
|
||||
step_size = 1/PRECISION
|
||||
lastpoints = [(x/PRECISION, 0) for x in range(PRECISION+1)]
|
||||
|
||||
waves = date.month
|
||||
amplitude = get_amplitude_from_date(date, waves)
|
||||
|
||||
r, g, b = get_color_from_date(date)
|
||||
alpha_step = 1 / waves
|
||||
|
||||
# background color
|
||||
if dark:
|
||||
ctx.set_source_rgb(0, 0, 0)
|
||||
#date = datetime.datetime.strptime('2021-06-30', '%Y-%m-%d')
|
||||
date = datetime.datetime.today()
|
||||
frequency = random.randint(10, 40)
|
||||
if DATE_BASED_COUNT:
|
||||
waves = days_count(date)
|
||||
else:
|
||||
ctx.set_source_rgb(255, 255, 255)
|
||||
ctx.rectangle(0, 0, 1, 1)
|
||||
ctx.fill()
|
||||
waves = 12
|
||||
if DATE_BASED_AMPLITUDE:
|
||||
amplitude = days_amp(date, waves)
|
||||
else:
|
||||
amplitude = 25
|
||||
|
||||
wave_height = 1 / waves
|
||||
step_size = 1 / PRECISION
|
||||
if MONOCHROME:
|
||||
if DATE_BASED_COLOR:
|
||||
r, g, b = days_color(date) #datetime.datetime.now())
|
||||
else:
|
||||
r, g, b = random_color()
|
||||
alpha_step = 1/waves
|
||||
if DARK_BG:
|
||||
# make bg black
|
||||
ctx.rectangle(0, 0, 1, 1)
|
||||
ctx.set_source_rgb(0, 0, 0)
|
||||
ctx.fill()
|
||||
|
||||
for wave_index in range(waves + 1):
|
||||
wave_height = 1/waves
|
||||
for num in range(waves+1):
|
||||
if not MONOCHROME:
|
||||
r, g, b = random_color()
|
||||
points = []
|
||||
x = 0
|
||||
while x < 1:
|
||||
# step along, create points along the wave
|
||||
y = amplitude * math.sin(frequency * x + (wave_index * wave_offset))
|
||||
points.append((x, (y + (0.5 + wave_index) * wave_height)))
|
||||
y = amplitude * math.sin(frequency * x + (num * WAVE_OFFSET) )
|
||||
points.append((x, ( y + (0.5+num)*wave_height)))
|
||||
x += step_size
|
||||
# print(f'Draw {len(points)} points for curve {num}')
|
||||
#print(f'Draw {len(points)} points for curve {num}')
|
||||
if not MONOCHROME:
|
||||
ctx.set_source_rgb(r, g, b)
|
||||
else:
|
||||
# make more transparent toward bottom
|
||||
ctx.set_source_rgba(r, g, b, 1 - (alpha_step * wave_index))
|
||||
ctx.set_source_rgba(r, g, b, 1 - (alpha_step * num)) # make more transparent toward bottom
|
||||
# draw waves
|
||||
ctx.move_to(*points[0])
|
||||
for p in points[1:]:
|
||||
@ -86,45 +104,11 @@ def create_wpotd(
|
||||
ctx.line_to(*points[0])
|
||||
ctx.fill()
|
||||
lastpoints = points
|
||||
return surface
|
||||
|
||||
surface.write_to_png(output) # Output to PNG
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("width", type=int, help="Width of the image")
|
||||
parser.add_argument("height", type=int, help="Height of the image")
|
||||
parser
|
||||
parser.add_argument("--dark", help="Draw on dark background", action="store_true")
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--offset",
|
||||
type=float,
|
||||
help="How much the waves should be offset to each other",
|
||||
)
|
||||
parser.add_argument("-f", "--frequency", type=int, help="Frequency of the waves")
|
||||
parser.add_argument(
|
||||
"--stdout", help="Write output image to stdout", action="store_true"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
print(args)
|
||||
if not args.offset:
|
||||
args.offset = math.pi / random.choice([1, 2, 4])
|
||||
if not args.frequency:
|
||||
args.frequency = random.randint(10, 40)
|
||||
output = create_wpotd(
|
||||
args.width,
|
||||
args.height,
|
||||
dark=args.dark,
|
||||
wave_offset=args.offset,
|
||||
frequency=args.frequency,
|
||||
)
|
||||
if args.stdout:
|
||||
import sys
|
||||
create_wpotd('out/waves.png')
|
||||
|
||||
output.write_to_png(sys.stdout.buffer)
|
||||
else:
|
||||
output.write_to_png("out/waves.png")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
12
wpotd.py
12
wpotd.py
@ -1,12 +1,4 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import io
|
||||
sys.path.append('..')
|
||||
from waves import create_wpotd
|
||||
|
||||
buf = io.BytesIO()
|
||||
create_wpotd(buf)
|
||||
sys.stdout.write('Content-Type: image/png\r\n\r\n')
|
||||
sys.stdout.flush()
|
||||
sys.stdout.buffer.write(buf.getvalue())
|
||||
print('Content-Type: image/png')
|
||||
print(open('out/waves.png', 'rb').read())
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user