Here’s a little creative coding I did with processingjs a while back. Move the mouse to move the skulls.
OK, part 3 – now for something fun – extending parts 1 + 2 into a simple particle system.
Particles, generally means – a lot of things moving around (the particles) and a way to generate them, an “emitter”
Here we’re going to take the code from the previous two parts and add a couple of things to make a basic particle system.
Note – shoebot, isn’t the fastest; but we do get nice looking results.
Here’s a video of our arrows as particles (arrowsplosion!):
In my last post we made an arrow move around the screen, in this post we’ll look to extend things so it’s easy to make many things move around the screen.
This will make the code a little more complex, but as usual it makes things simpler later on.
This python code runs in shoebot, planar.py is used to handle coordinates
At the end we’ll have two arrows, a blue one controlled with the keyboard and a pink one that moves on it’s own:
Here’s a little shoebot bot to experiment with natural movement.
This uses polar coordinates to decide the direction and velocity of an arrow on the screen.
Polar coordinates mean we can give an object a sense of ‘forward’, ‘back’, ‘left’ and ‘right’
The code below works on the current version of shoebot
With planar.py to handle the directions and velocity
Seeing”Realtime FFT Graph of Audio WAV File or Microphone Input with Python…” on python.reddit.com reminded me of one I’d built in python with shoebot.
While it works OK, I feel like I’m missing a higher level audio library (especially having seen Minim, for C++ and Java).
To run it in shoebot:
sbot -w audiobot.bot
# Major library imports import atexit import pyaudio from numpy import zeros, short, fromstring, array from numpy.fft import fft NUM_SAMPLES = 512 SAMPLING_RATE = 11025 def setup(): size(350, 260) speed(SAMPLING_RATE / NUM_SAMPLES) _stream = None def read_fft(): global _stream pa = None def cleanup_audio(): if _stream: _stream.stop_stream() _stream.close() pa.terminate() if _stream is None: pa = pyaudio.PyAudio() _stream = pa.open(format=pyaudio.paInt16, channels=1, rate=SAMPLING_RATE, input=True, frames_per_buffer=NUM_SAMPLES) atexit.register(cleanup_audio) audio_data = fromstring(_stream.read(NUM_SAMPLES), dtype=short) normalized_data = audio_data / 32768.0 return fft(normalized_data)[1:1+NUM_SAMPLES/2] def flatten_fft(scale = 1.0): """ Produces a nicer graph, I'm not sure if this is correct """ for i, v in enumerate(read_fft()): yield scale * (i * v) / NUM_SAMPLES def triple(audio): '''return bass/mid/treble''' c = audio.copy() c.resize(3, 255 / 3) return c def draw(): '''Draw 3 different colour graphs''' global NUM_SAMPLES audio = array(list(flatten_fft(scale = 80))) freqs = len(audio) bass, mid, treble = triple(audio) colours = (0.5, 1.0, 0.5), (1, 1, 0), (1, 0.2, 0.5) fill(0, 0, 1) rect(0, 0, WIDTH, 400) translate(50, 200) for spectrum, col in zip((bass, mid, treble), colours): fill(col) for i, s in enumerate(spectrum): rect(i, 0, 1, -abs(s)) else: translate(i, 0) audio = array(list(flatten_fft(scale = 80)))
An update on my latest cairo adventures…
When cairo 1.10 comes out we’ll get a RecordingSurface so you can record commands and play them back to another surface, but how to do something similar now ?
Skip to the end if you just want the code, explanation of how I got there ahead:
My first advice was to try using PDFSurface, and set the file object to None.
# Record a red rectangle onto a surface. # # Create another surface and draw a background on it # Draw the first surface onto this surface # from cairo import PDFSurface, ImageSurface, FORMAT_ARGB32, Context recording = PDFSurface(None, 200, 200) target = ImageSurface(FORMAT_ARGB32, 200, 200) # Record a red rectangle cr = Context(recording) cr.set_source_rgb(1.0, 0.0, 1.0) cr.rectangle(20, 20, 10, 10) cr.fill_preserve() cr.set_source_rgb(0.0, 0.0, 1.0) cr.stroke() target_context = Context(target) # Draw background target_context.set_source_rgb(1.0, 1.0, 0) target_context.paint() # Replay recording to target target_context.set_source_surface(recording) target_context.paint() target.write_to_png('output.png')
That seems to work, except when I tried in Windows, when it complained that the file object was wrong.
OK, we can work round that:
def RecordingSurface(w, h): if os.name == 'nt': fobj = 'nul' else: fobj = None return PDFSurface(fobj, w, h)
This seems to be working, but my animation seemed slow… time for some benchmarking; I rendered 1000 frames and got a rough wall clock time:
Hmm… perhaps SVGSurface will be quicker:
This is much better, 20 seconds difference just by changing what kind of surface is returned!
Animation not smooth though
The animation still seemed jerky it occured to me that when the surfaces are disposed they will attempt to out their content to the file object !
Luckily, get_similar_surface() comes to the rescue; it returns a surface not associated with a file object. Using this the original surface can be kept around forever, and never output.
And here it is:
_svg_surface = None def RecordingSurface(*size): ''' We don't have RecordingSurfaces until cairo 1.10, so this kludge is used SVGSurfaces are created, but to stop them ever attempting to output, they are kept in a dict. When a surface is needed, create_similar is called to get a Surface from the SVGSurface of the same size ''' global _svg_surface if os.name == 'nt': fobj = 'nul' else: fobj = None if _svg_surface is None: _svg_surface = SVGSurface(fobj, 0, 0) return _svg_surface.create_similar(cairo.CONTENT_COLOR_ALPHA, *size)
This is really useful, you can record commands and play them back to other surfaces.
Often it’s useful to be able to store up drawing commands so you can use them later somewhere else (or even just pass them to another thread).
This is a simple drawing model, implemented in cairo, hopefully somebody will find it useful.
class DrawQueue: ''' A list of draw commands, stored as callables that, are passed a set of parameters to draw on from the canvas implementation. ''' def __init__(self, render_callables = None): self.render_callables = render_callables or deque() def append(self, render_callable): ''' Add a render callable to the queue ''' self.render_callables.append(render_callable) def render(self, cairo_ctx): ''' Call all the render callables with cairo_ctx ''' for render_callable in self.render_callables: render_callable(cairo_ctx)
The queue just accepts callables (any old function), and calls them when you call render, passing them a cairo context you pass in.
To get useful functions you can call closure functions like these:
def paint_closure(): def paint(ctx): ctx.paint() return paint def fill_closure(): def fill(ctx): ctx.fill() return fill def set_source_rgb_closure(r, g, b): def set_source_rgb(ctx): ctx.set_source_rgb(r, g, b) return set_source_rgb def moveto_closure(x, y): def moveto(ctx): ctx.move_to(x, y) return moveto def rectangle_closure(x, y, w, h): def rectangle(ctx): ctx.rectangle(x, y, w, h) return rectangle
Adding commands to the queue is simple:
dq = DrawQueue() dq.append(set_source_rgb_closure(1, 1, 1)) dq.append(paint_closure()) dq.append(moveto_closure(10, 0)) dq.append(rectangle_closure(0, 0, 20, 20)) dq.append(set_source_rgb_closure(0, 0, 0)) dq.append(fill_closure())
This is the same drawing model I’m using in my branch of shoebot, I’m hoping to expand it to be multithreaded; while a foreground thread adds commands a background thread is executing them.
Here it is all put together in a simple example to draw a black rectangle
from collections import deque import cairo class DrawQueue: ''' A list of draw commands, stored as callables that, are passed a set of parameters to draw on from the canvas implementation. ''' def __init__(self, render_callables = None): self.render_callables = render_callables or deque() def append(self, render_callable): ''' Add a render callable to the queue ''' self.render_callables.append(render_callable) def render(self, cairo_ctx): ''' Call all the render callables with cairo_ctx ''' for render_callable in self.render_callables: render_callable(cairo_ctx) #### drawing closures def paint_closure(): def paint(ctx): ctx.paint() return paint def fill_closure(): def fill(ctx): ctx.fill() return fill def set_source_rgb_closure(r, g, b): def set_source_rgb(ctx): ctx.set_source_rgb(r, g, b) return set_source_rgb def moveto_closure(x, y): def moveto(ctx): ctx.move_to(x, y) return moveto def rectangle_closure(x, y, w, h): def rectangle(ctx): ctx.rectangle(x, y, w, h) return rectangle #### /drawing closures dq = DrawQueue() # Add some commands to the drawing queue dq.append(set_source_rgb_closure(1, 1, 1)) dq.append(paint_closure()) dq.append(moveto_closure(10, 0)) dq.append(rectangle_closure(0, 0, 20, 20)) dq.append(set_source_rgb_closure(0, 0, 0)) dq.append(fill_closure()) # Create a surface and context surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 200, 200) ctx = cairo.Context(surface) # run defered rendering dq.render(ctx) surface.write_to_png('output.png')
Problems I was having with incomplete images have been fixed in the current version of the web library, available in shoebots mecurial repository.
This is great as Shoebot/Nodebox have great libraries for data manipulation, while processing is more focused on graphics.
The result is the attached Netbeans project which demonstrates using the nodebox web library and drawing with processing.
The glue code is in slowcessing.py
Theres a special version of PApplet (PJApplet), and ‘pj_frame’ which can put this in a JFrame.
The other method is ‘shoebot_imports’ adds the shoebot imports to the library path
In case anybody doesn’t want to download the whole project, heres the code:
from slowcessing import PJApplet, pj_frame, shoebot_imports from processing.opengl import * shoebot_imports() import web import thread class ImageQueue(list): """ Download images in the background and add them to a list """ def __init__(self, search, size): list.__init__(self) self._search = search self._image_size = size thread.start_new_thread(self._get_images, ()) def _image_downloaded(self, path): p = PJApplet() self.append(p.loadImage(path)) def _get_images(self): for image in self._search: image.download(self._image_size, asynchronous=False) self._image_downloaded(image.path) class WebTest (PJApplet): def setup(self): self.size(400, 400, self.P3D) self.images = ImageQueue(web.morguefile.search("sweets", max=1), size='small') def draw(self): self.background(0); y = (self.height * 0.2) - self.mouseY * (len(self.images) * 0.58) for image in self.images: self.image(image, 20, y) y += image.height if __name__ == '__main__': pj_frame(WebTest)
from javax.swing import JFrame from processing.core import PApplet class PJApplet(PApplet): # rqd due to PApplet's using frameRate and frameRate(n) etc. def getField(self, name): return PApplet.getDeclaredField(name).get(self) def pj_frame(pj_applet, **kwargs): from time import sleep frame = JFrame(kwargs.get('title', 'Slowcessing')) frame.defaultCloseOperation = kwargs.get('defaultCloseOperation', JFrame.EXIT_ON_CLOSE) frame.resizable = kwargs.get('resizable', False) panel = pj_applet() frame.add(panel) panel.init() while panel.defaultSize and not panel.finished: sleep(0.5) frame.pack() frame.visible = 1 return frame def shoebot_imports(): """ Allow import of the shoebot libraries """ ##APP = 'shoebot' import sys DIR = sys.prefix + '/share/shoebot/locale' ##locale.setlocale(locale.LC_ALL, '') ##gettext.bindtextdomain(APP, DIR) ###gettext.bindtextdomain(APP) ##gettext.textdomain(APP) ##_ = gettext.gettext LIB_DIR = sys.prefix + '/share/shoebot/lib' sys.path.append(LIB_DIR)
There are some things I couldn’t work :
The callback to say that images have been downloaded happens before the whole file is available, for this reason there are grey parts on the images on the first run.
While I did manage to fix things to get this working in Jython and get Morguefile working, I had a lot of trouble understanding what was going on here.
Cheers to Tom De Smedt for fixing these the areas of nodebox-web that I couldn’t
Some parts of PApplet to do with image loading seem to be static, which may also explain problems I was getting with reentrancy.
If you want to have a go, you’ll need to:
Install Netbeans 6.8
Install Jython (2.5 or higher) by installing the Netbeans python module
Add python to the path (if using Netbeans it’s copy is where Netbeans is installed).
Get nodebox-web by downloading shoebot and install it with:
jython setup.py install
Download the PythonOnProcessing (tested on Netbeans 6.8)
Cairo is a 2D vector graphics api used by Firefox, Gtk and other desktop projects.
I’m going to show here that it can also be used to generate web content, using Django.
I’m going to port two examples from the Michael Urmans Cairo Tutorial for PyGTK Programmers.
To understand the cairo and it’s drawing model I’d recommend his his Cairo Tutorial for Python Programmers.
Note: In this example I’ll be generating SVGs… as I.E. (as of 2010) does not support them, you might want to generate PNG or PDF – if you need to do this with cairo, look for one of the many cairo tutorials on the web.
The example django project can be downloaded at the end of the article.
I’ve uploaded my scripts to help workflow to google code, naming the project batch flow.
Heres a summary of some of the things you can do.
Go to a directory in the clipboard.
If you have the address bar enabled in explorer:
Copy the location, go to the prompt and enter ‘pcd’ to go to that folder
Go to the location of a setting in netbeans or eclipse:
Copy the location , go to the prompt and enter ‘pcd’ to go to the folder (or folder containing the file).
Get the current directory without dragging:
dirsave and dirload let you save named bookmarks.
In the 4nt or tcc/le prompt enter “option”, under the “TCStart/TCExit” path, change the location to the location where batch-flow is installed + “\conf”, for instance on my computer batch-flow is installed to c:\usr\batch-flow, so I set it to
Now in new TCC/LE prompts F5-F10 are reserved for directories: Ctrl+Fkey to save, and Alt+Fkey to load. Alt-F12 lists these shortcuts.
Note: Alt-F12 only lists shortcuts on FKeys, to list these and other shortcuts enter dirload /l
batch-flow comes with other handy hotkeys, use ‘alias’ in TCC/LE to see what they are.
It’s annoying after installing a program to have to add it to the path, so there is an ‘addpath’ command to do this.
This is a more general utility for viewing the registry path, you can list it, validate it, check for the location of files within it.
Also useful is ‘regpath /L’ which sets the local prompts path to the one in the registry.
Most of the commands have help builtin, which you can access by using the /? option.