A more complicated py5 sketch

There is a Newton Fractal sketch on Jorg Kantel blog that I converted to run with py5:-

import py5
import cmath

imgx = 512
imgy = 512

# Drawing area
# xa = 1.126
xa = -2.0
xb = 2.0
ya = -2.0
yb = 2.0

maxIt = 20 # max iterations allowed
h = 1e-6   # stepsize for numerical derivative
eps = 1e-3 # max error allowed

def f(z):
    # return cmath.sin(z)
    # return z*z*z*z*z*z - 1.0
    return z*(z*z*z*z*z*z - 1.0)
    
def settings():
	py5.size(imgx, imgy)    

def setup():
    global img    
    img = py5.create_image(py5.width, py5.height, 1) # 1 = RGB
    py5.no_loop()

def draw():
    global img
    py5.load_pixels()
    img.load_pixels()
    for y in range(imgy):
        zy = y*(yb - ya)/(imgy - 1) + ya
        for x in range(imgx):
            zx = x*(xb - xa)/(imgx - 1) + xa
            z = complex(zx, zy)
            for i in range(maxIt):
                # Complex numerical derivative
                dz = (f(z + complex(h, h)) - f(z))/complex(h, h)
                if dz != 0:
                    z0 =  z - f(z)/dz     # Newton iteration
                if abs(z0 - z) < eps:
                    # Stop when close enough to any root
                    break
                z = z0
                
            loc = x + y * py5.width
            # pixels[loc] = color(i%5*64, i%9*32, i%17*16)   
            py5.pixels[loc] = py5.color(i%5*64, i%17*16, i%9*32)
    py5.update_pixels()
    
py5.run_sketch()

Here is sketch running from geany on my RaspberryPI4 see New Python Processing project: Py5 - #11 by monkstone

2 Likes

Very cool!

BTW: there’s a run_sketch.py file located in lib/python3.X/site-packages/py5_tools/tools/ that you can use to run py5 in Imported mode. This requires no import py5 line or py5. prefixes. Also, it supports ‘static’ mode sketches – so if you don’t want animation, you can skip the setup() and draw() functions.

I compressed the code a little to avoid invoking a scroll pane :wink:

xa, xb, ya, yb = -2.0, 2.0, -2.0, 2.0
maxIt, h, eps = 20, 1e-6, 1e-3
def f(z): return z*(z*z*z*z*z*z - 1.0)

size(512, 512)
load_pixels()

for y in range(height):
    zy = y*(yb - ya)/(height - 1) + ya
    for x in range(width):
        zx = x*(xb - xa)/(width - 1) + xa
        z = complex(zx, zy)
        for i in range(maxIt):
            dz = (f(z + complex(h, h)) - f(z))/complex(h, h)
            if dz != 0: z0 = z - f(z)/dz
            if abs(z0 - z) < eps: break
            z = z0
        loc = x + y * width 
        pixels[loc] = color(i%5*64, i%17*16, i%9*32)

update_pixels()
2 Likes

I tested your version with both java-16-graalvm and java-16-openjdk on my linux box adding print(millis()) after update_pixels, and again no performance improvement with graalvm cf my experience with ruby-processing. Installed a whole pile faster on linux box (Archlinux) cf RaspberryPI, I needed to add pip as well as wheel this time though:-

sudo pacman -S python-pip
sudo pacman -S python-wheel
1 Like

Regarding py5 performance, Jim Schmitz has written some interesting notes. It might be interesting to explore writing java extensions, I have written such extensions for my ruby-processing projects (but jruby support for extensions is probably better established).

1 Like