20120406

Jython and Feedback Fractals in 42 Lines

Python is known for its simple, readable syntax. Java is known for its cross-platform support, including the relatively full-featured swing API for writing graphical user interfaces. Python, however, lacks an attractive built-in cross-platform graphical user interface toolkit, and Java is often criticized for having verbose syntax and clumsy support for closures. The solution? Jython. Jython is an implementation of the Python interpreter on the Java virtual machine. It provides easy access to all of the standard Java libraries, with Python's slick and readable syntax. Thus, it allows you to rapidly develop cross-platform applications with graphical user interfaces. If you already know Python syntax and have worked with the Java swing API before, you can get started immediately. If you don't know Python, you can also get started immediately, the syntax is very easy to pick up. Learning how to work with Swing might take a bit more time.

To demonstrate Jython, here is a small Julia set fractal feedback rendering demo. There are only 42 lines of actual code, and it was genuinely fun to write. This will display a small window that actively renders Julia set animations using the feedback method. The app tracks the mouse position and changes the Julia set parameter accordingly.

#!/usr/bin/env jython

from javax.swing import *
from java.awt import *
from java.awt.image import *
from java.awt.event import *

FUNCTION = lambda z:z**2 # Recurrence ( should be even )
FIELDS = 4 # Field size in complex plane
S = 256 # Buffer size, must be power of 2
CENTER = complex(1,1)*FIELDS*0.5 # Center of plane ( defaults to 0 )
CLIP = S-1 # Used for wrapping buffer coordinates
LEN = S*S # Total buffer size
QUIT = LEN/2 # Amount of buffer to actually compute

def render(source,target,mapping,hue,offx,offy):
'''
Uses the previous frame, along with the cached mapping, to compute the
next frame. Off-screen pixels default to the color given by hue.
offx and offy define a constant shift of the recurrence function
'''
color = Color.getHSBColor(float(hue),1.,1.).RGB
miny,maxy = -offy,S-offy
for i in xrange(QUIT):
(x,y) = mapping[i]
if y>=miny and y<maxy:
c = source.getElem(((x+offx)&CLIP)+((y+offy)*S))
else:
c = color
target.setElem(i,c)
target.setElem(LEN-i-1,c)

def computePoint(x,y):
'''
Sends a point in buffer coordinates through the mapping function,
converting back to buffer coordinates before returning
'''
z = FUNCTION(complex(x,y)/S*FIELDS-CENTER)*S/FIELDS
return int(z.real+0.5)+S,int(z.imag+0.5)

class FractalMouseListener(MouseMotionAdapter):
'''
Updates the constant offset to correspond to the mouse location
'''
def mouseMoved(self,e):
global offx,offy,F
offx,offy = e.x*S/F.width,e.y*S/F.height

# Program start :
# declare two buffers for rendering ( buffer flipping approach )
# extract the dataBuffers underlying the BuffferedImages, for speedy access
# precompute the complex mapping in terms of buffer coordinates
# set the initial offset and to 0

img,buf = BufferedImage(S,S,BufferedImage.TYPE_INT_RGB),BufferedImage(S,S,BufferedImage.TYPE_INT_RGB)
mapping = [computePoint(x,y) for y in xrange(S) for x in xrange(S)]
offx,offy = 0,0
hue = 0.0

# Windowing commands :
# Declare a new Jpanel, and give it our mouse motion listener
# Declare a new JFrame to contain the fractal JPanel, show it

F = JPanel()
F.addMouseMotionListener(FractalMouseListener())
jf = JFrame('Demo',defaultCloseOperation=JFrame.EXIT_ON_CLOSE,contentPane=F,size=(S,S),visible=1)

# Now, as long as the program is running, loop and render frames
# We advance the hue each frame, flip the buffers
# Render the next frame of the fractal and send it to the screen

while 1:
hue = hue + 0.05
img,buf = buf,img
render(img.raster.dataBuffer,buf.raster.dataBuffer,mapping,hue,offx,offy)
F.graphics.drawImage(img,0,0,F.width,F.height,None)


2 comments:

  1. Hi Michael,

    This is sweet code. Thanks for writing it! Using jython 2.5.2 on Windows with java version "1.6.0_03", I needed to change the 'import' statements so that there were explicit references to the Java classes:

    from javax.swing import JPanel, JFrame
    from java.awt import Color
    from java.awt.image import BufferedImage
    from java.awt.event import MouseMotionAdapter

    Regards,

    Kevin

    ReplyDelete
  2. thanks! hmm, its unfortunate that the "import *" idiom didn't work. I have found that Jython is still a but rough around some edges ( ternary if didn't work on the version I tested in ubuntu ), but it was still far more pleasant than writing the same in pure Java.

    ReplyDelete