Where does these nice patterns come from?

129 Views Asked by At

I've played around some with complex numbers in python and created a program that creates beautiful patterns using $E_n = e^{i(2\pi)/(R_n+1)} = A+Bi$ where R is a set of values between $0$ to $2\pi$ with an arbitary number of datapoints and then plotting $C_n = A_n \mod \sin(t) + B_n \mod \cos(t)$. I'm not that good with math and my question is: What is it that forms these patterns? Thanks

Here's the code:

Links to video & images: https://i.stack.imgur.com/pDauU.jpg https://i.stack.imgur.com/Gp9kU.jpg

# Import necessary libraries 
from PIL import Image, ImageDraw 

import numpy as np
import math
import time
import random
from numpy import complex, array 
import colorsys 
import os

# setting the width of the output image as 1024 

WIDTH = 720
HEIGHT = 720
SCALE = 1.0
SPEED = 1/(10000) # SMALLER IS FASTER

RESOLUTION = 14*10**4

## Automatically set
PHI = ( 1 + math.sqrt(5) ) / 2
SPEED *= 10**4 
RANDNAME = "TODOMAKERANDOM"

def make_frame(frame, length):
    # creating the new image in RGB mode 
    img = Image.new('RGB', (WIDTH, HEIGHT)) 
    pixels = img.load()

    # 20**4 arbitary speed
    vframe = (frame/(length*SPEED))*2*math.pi
    
    
    R = np.linspace(0, 2*math.pi, RESOLUTION) # real numbers 0 to 2*pi, width number of datapoints
    C = np.zeros((RESOLUTION),dtype=complex) # complex numbers, width number of data points
    CR = np.zeros(RESOLUTION)  # real parts of complex numbers, width number of ...
    CI = np.zeros(RESOLUTION)  # imaginary parts of complex nunmbers, width ...



    for i, v in enumerate(R):
        eiv = np.power(math.e, 1j*v)
        # 0.1 IS SCALE
        C[i] = complex(eiv.real % SCALE * math.sin(i*vframe), eiv.imag % SCALE * math.cos(i*frame)) #math.sin(i*phi*vframe) #  % math.pi *  (X2)

    for i, c in enumerate(C):
        #print("max: ", max(C.real))
        c = c/(2+2j) + 0.5j + 0.5

        x = math.floor(c.real*img.size[0]-1)
        x = x if x > 0 else 0
        x = x if x < img.size[0] else img.size[0]-1

        y = math.floor(c.imag*img.size[1]-1)
        y = y if y > 0 else 0
        y = y if y < img.size[1] else img.size[1]-1

        s = 2#int(random.random()*4+1)#2
        
        for n in range(s**2):
            x1 = x - s + n % s
            y1 = y - s + math.floor(n / s)

            oldhue = colorsys.rgb_to_hsv(pixels[x1, y1][0], pixels[x1, y1][1], pixels[x1, y1][2])
            


            rgbhue = colorsys.hsv_to_rgb(
                    min(1.0, oldhue[0]+0.01), 
                    min(1.0, oldhue[0]+0.95), 
                    min(1.0, oldhue[0]+0.2)
            )

            res = tuple(
                ele1 * ele2 for ele1, ele2 in zip(
                    rgbhue,
                    (255, 255, 255)
                )
            )


            res = (int(res[0]), int(res[1]), int(res[2]))


            if (rgbhue != (0, 0, 0)):
                pixels[x1, y1] = res#(colorsys.hsv_to_rgb(30, 1.0, 1.0)*255)

    print("frame: ", frame)

    if not os.path.exists(RANDNAME):
        os.mkdir(RANDNAME)
    img.save(RANDNAME+"/frame_"+str(frame)+".png")


start = 1
stop = 500

for i in range(start, stop):
    try:
        make_frame(i, abs(stop-start))
    except KeyboardInterrupt:
        break#exit(1)


os.system("ffmpeg -i "+RANDNAME+"/frame_%d.png -c:v libx264 -vf fps=25 -pix_fmt yuv420p "+RANDNAME+"/out.mp4 -y")