Sophie

Sophie

distrib > Mandriva > 10.0-com > i586 > by-pkgid > bc65b4ce8f85cbc2395fca5de38ca0e1 > files > 8

evolvotron-0.2.2-1mdk.i586.rpm

INTRODUCTION
============
Evolvotron is interactive "generative art" software to evolve
images/textures/patterns through an iterative process of random
mutation and user-selection driven evolution.  If you like lava-lamps,
and still think the Mandelbrot set is cool, this could be the software
for you.

It uses C++ (and STL) & Qt, and is multithreaded (using Qt's threading API).

Home page: http://www.bottlenose.demon.co.uk/share/evolvotron

Author: timday at timday dot com

If you manage to make practical use of evolvotron, especially
if evolvotron derived imagery makes it into print or other
mass media, I'd love to hear about it: please email!

Have fun
Tim

LICENSE
=======
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

[The license should be in the LICENSE file in the same directory as this
README]

BUILDING
========
There's no reason it shouldn't work on any platform with a correctly
set up qmake.
 
You do NOT need to be root until the (optional) final install stage.

In the top level directory, you can either do the "traditional"

  ./configure
  make

or just have both done for you by doing

  ./BUILD

[Note that BUILD also adds $QTDIR/bin to the path while it runs the 
commands, and is probably the easiest way to build if your PATH doesn't 
include a directory containing qmake and you like it that way.]

Make will recurse into and build the libevolvotron directory
(which is 99% of the work) and some additional subdirectories with 
executables.  Among other things, this will give you an "evolvotron" 
executable which you can run immediately with
  ./evolvotron/evolvotron
and/or copy where you like (see INSTALL section).

BUILD PROBLEMS
==============

Short story:

1. Check you have your QTDIR environment variable defined correctly.
2. Check your PATH will find qmake (i.e do "which qmake") 
   (adding $QTDIR/bin to your path will fix this on most distros; note that
   the BUILD script does this while it runs).

Long story:

Check you have your QTDIR environment defined, and possibly also your
QMAKESPEC too.  The former should point to your Qt installation.  The latter
will be something like "linux-g++" if needed; check the Qt docs for other
architectures or perhaps look in your QT installation's mkspecs directory.
(RedHat defines QTDIR for the normal user environment, and seems to work fine
without explicitly setting QMAKESPEC.  A Mandrake user reports having to set both.)

The author develops on 
  RedHat 8 (gcc 3.2, qt 3.0.5),
  RedHat 9 (gcc 3.2.2, qt 3.1.1)
and sometimes also tests on on RedHat 7.3 (gcc 2.96, qt 3.0.5).
(So the closer your system is to these, the less likely you are to have problems).

Most build failures are simply because the necessary Qt build tools aren't in your path:
  which qmake
  which moc
should both find something.  RedHat makes things easy by putting these binaries in
/usr/bin, but many distros leave them in $QTDIR/bin.
You should either add this to your path or use the BUILD script 
(which sets it for the commands it runs).

A Debian (3.0r1) user reports Debian's Qt package not including qmake.
They eventually got hold of a version from somewhere (Trolltech?).
Attempting a regular build they apparently encountered a header/library
version incompatibility (old headers, new library) which was solved by hand
editing the qmake-generated Makefile and adding "-I- -I. " in front of
the other -I paths.

If you have gcc/c++ problems:

If you have to change anything, the chances are it should be changed in
common.pro.  Remember that any Makefiles are generated by qmake from
.pro files and therefore hacking on Makefiles is a fruitless excercise.

Some source releases have had problems with other versions of gcc than
the ones I test on.  A COMPLETE record of a failed build would be
appreciated (including the initial display of the gcc version).
If you can fix it, the patches would be even better!

BUILD OPTIONS
=============
A few things are available through the configure script.

Usage:
  ./configure [p4|p3|xp] [fs]

(Any command line arguments given to ./BUILD are passed through to configure,
so you can do e.g "./BUILD fs p4").

If you rerun configure or build with a different option to the last time
you almost certainly need to do a "make distclean" to clean up old
code and Makefiles. 

FUNCTIONALITY
-------------
Specify 'fs' on the ./configure commandline to enable
fullscreen support (which is off by default).  I don't 
know which version of Qt this appeared with, but it works 
on RH9 and fails to build on RH8.

If you select fullscreen support but your version of Qt is too
old, the build will fail.

PERFORMANCE
-----------
There are already some generally effective overrides for 
Qt's fairly conservative (at least on RedHat) compile options 
in the fracplanet.pro file (where QMAKE_CXXFLAGS_RELEASE is
modified).  If you change them, do a "make distclean" before 
you rebuild to make sure Makefiles are rebuilt. 

The easiest way to get some additional CPU-specific optimisations
is to use the configuration option:
  ./configure <cpu>
where <cpu> is p3,p4 or xp as appropriate e.g ./configure p4
and rebuild from clean (again, "make distclean" is a good idea).

If you attempt to run the resulting executable on incompatible
hardware, it will probably crash horribly.

So you can judge for yourself whether this is worth bothering with
here's the times for a 2.4GHz P4 to run a 2048x2048 evolvotron_render
(gcc 3.2 compiled) on a benchmark image function:
  Qt defaults 
    -march=i386 -mcpu=i686 -O2                               30.6s
  With evolvotron's overrides (now applied by default):
    -O2 -> -O3:                                              28.6s
    add -fomit-frame-pointer                                 28.2s
    add -funroll-loops                                       26.8s
    add -ffast-math                                          26.5s
  With 'p4' configure option:
    arch/cpu -> -march=pentium4 -mfpmath=sse -msse2          23.6s
  With modification to common.pro:
    add -finline-limit=4000                                  19.4s
(but that last one involves a 32m build time (!!!), cf ~2m for the others)
[NB This was done a while ago; things have probably changed a bit,
especially after some major changes to the function code around version 0.2.0]

BUILDING ON OTHER PLATFORMS
===========================

Linc Davis reports:
  "I built it on a Mac with Qt installed via Fink (if you know what that is.)
  All I had to do is add '$(QTDIR)/include/qt' to the include path and then
  run make."

Paolo Greppi built it on OSX like this:
 - get qt for osx http://www.trolltech.com/download/qt/mac.html
 - configure qt with -thread to compile threading support
   (takes a lot of time)
 - remember to:
        sudo ln -sf /usr/local/qt/lib/libqt-mt.3.dylib /usr/lib
 - and add this to .tcshrc in your home directory:
        setenv QTDIR /usr/local/qt
        setenv PATH $QTDIR/bin:$PATH
        setenv DYLD_LIBRARY_PATH $QTDIR/lib
 - modify the Makefile in evolvotron adding -lqt-mt at the end of LIBS
 - cd libevolvotron
   mv mutatable_image_computer_farm.c mutatable_image_computer_farm.cpp
   mv mutatable_image_computer_task.c mutatable_image_computer_task.cpp
 - make ... while compiling, it keeps saying
   c++: unrecognized option `-pthread'
   (but this doesn't seem to matter)

INSTALL
=======
Doing
  make install
will attempt to install the executable in the INSTALLPATH directory defined in
common.pro.  This is set to ~/bin by default (which will give you a "personal
install" of evolvotron assuming you have such a directory in your PATH).
If you change INSTALLPATH to /usr/local/bin then you'll need to "su" before
the make install.

NB If you change a ".pro" file, you'll need to ./configure again to make changes
take effect.

INSTALL PROBLEMS
================
Users of some distros (Mandrake?) report problems with the install code
generated by qmake.

If you'd rather just do it by hand, all you need to do is copy the executables
./evolvotron/evolvotron
./evolvotron_render/evolvotron_render
./evolvotron_mutate/evolvotron_mutate
to wherever you like (e.g ~/bin, /usr/local/bin).
There are no extra supporting files which need to be in special places.

USAGE
=====

For the default 2D image mode, you will need a fast machine or patience.
For the optional animation mode, you will need both.
(If you're desperate, a little more performance can be obtained by
messing with the compile flags: see the BUILD OPTIONS section above).

On starting the application, a grid of images is displayed.
(Resize or maximise the application if you like, but the more
pixels have to be calculated, the slower it will be.)

Simply repeat the following until bored:
 - Click (singleclick) on an image you like to 
   spawn the next generation of its mutant offspring.
 - Wait until variations on it are regenerated in sufficient 
   detail that you can decide which one you like best again.

IMPORTANT: Initially you should select images with some sort of variation.
If you select a uniform image, you may get stuck in a degenerate zone with
little to mutate and therefore little chance of escape to a more interesting
area.  You can always reset/restart from the "File" menu (the difference is
that "reset" also resets the mutation parameters to their default values).
Selecting one of the "warp" options from the contect menu (right-click on
an image) can also help by introducing an additional opportunity for
mutation on subsequent spawns.

Note that various spirals, grids and tiles, although complex looking,
are actually implemented by a single function node and may leave you stuck too.

COMMAND LINE OPTIONS
====================
NB You cannot simply munge single character options into one big string
(i.e "-FM" is not the same as "-F -M") and space IS required between 
options and numeric arguments (ie use "-t 4" not "-t4") 

USER OPTIONS
------------
  -geometry <width>x<height>
	The usual Qt/X11 option to set on-screen size in pixels

  -g <cols> <rows>
	Sets number of image display cells (defaults to 6 by 5)

  -t <threads>
	Sets number of compute threads (defaults to 2)

  -F
	[NB Only available with fullscreen build option; ignored otherwise]
	Start in "fullscreen" mode (NB for Qt on X11 this means 
	a screen-filling borderless/undecorated window is used; 
	it's not simply maximising the window, and it's not the 
	sort of framebuffer hijacking used by SDL games).  The Qt 
	documentation claims some window managers may not be entirely
	cooperative with this (in which case sorry, you're on your own).  
	evolvotron actions which bring up dialog boxes (e.g save) seem
	to generally behave fairly sensibly but child windows 
	(e.g from "Big") can show some "interesting" behaviour.  
	Fullscreen mode can be toggled within the application using ctrl-f.  
	The Esc key will also exit it.

  -M	
	[NB Only available with fullscreen build option; ignored otherwise]
	Start with menu and status bar hidden.  Nice with -F.
	Hiding can be toggled within the application using ctrl-m.
	The Esc key will also bring them back.

ANIMATION OPTIONS
-----------------

  -f <frames>
	Number of frames in animations (defaults to 1 i.e no animation)

  -r <framerate>
	Rate at which frames are displayed (integer).  (Defaults to 8).

POWER-USER & DEBUG OPTIONS
--------------------------
  -v 
	Verbose mode, writes various things to application stderr 
	(mainly to assist debugging).
        Probably most useful for getting a list of supported function 
	names for use with the -x/-X options.

  -x <functionname>
	Force a specific function type to be used at the top level 
	of all function trees.  The specifed function is still wrapped 
	by spatial and colour warping functions which may disguise 
	it considerably.  A list of all the function names understood 
	by evolvotron is output during app startup when the -v option 
	is specified.
	Example: evolvotron -x FunctionSpiralLinear

 -X <functionname>
	Similar to -x except that the specified function is NOT wrapped 
	by space/colour transforms.  NB For functions without leaf nodes 
	or parameters (e.g FunctionSphericalToCartesian) this doesn't
	leave any scope for variation or future mutation.
        Example: evolvotron -X FunctionKaleidoscope

Note that you can also specify iterative & fractal function types as an argument to -x/-X,
even when they are disabled (as they are by default) in the mutation parameters.

MOUSE CONTROL
============= 

LEFT-CLICK
----------
A left-click on an image in the main window spawns the mutant offspring
of that image to all the other (non-locked) displays in the grid. 

RIGHT-CLICK CONTEXT MENU
------------------------
Right clicking on an image gets you a few more options:

 - "Respawn" regenerates just the current image from whatever it was
   spawned from (and using recolour or warp, if that's what was used
   to produce it).
   The main use of this is to make your grid of images look nice
   for screendumps, by regenerating any which aren't up to scratch.
   NB May not work as expected after an "undo".

 - "Spawn" is the same as clicking an image.  It generates mutated
   images to all unlocked images in the grid.

 - "Recolour" to produce different coloured variants of the selected image

 - "Warp"'s sub-options produce variants of the image which have been
   zoomed/rotated/panned.

 - "Lock" to prevent an image from being overwritten by spawns from other
   images (select again to toggle).

 - "Big" to produce a blow-up of the image in a single window.
   Submenu items select either a freely resizable window or
   a scrollable view of a fixed size image.
   If the application is running in fullscreen mode (NB this is
   NOT the same as "Maximised": see the -F command line option)
   then the "Big" image will also be fullscreen (the "Resizeable" 
   mode is probably what you want in this case as the image will
   automatically be rendered at the correct resolution). 

 - "Save image" to save the image in a file (.ppm or .png format).
   Note that this is most useful on a "Big" blown-up image: if you
   save a small grid image, the size you see on the screen is the size
   you get in the file.  Note that the save won't be allowed until the
   full resolution image has been generated.

 - "Save function" to store the function to an XML file.

 - "Load function" to load a stored function from an XML file.
   NB if the file was saved from a different version numbered
   evolvotron, a warning message will be generated.
   Save/load of functions is an experimental feature and you should
   not count on future versions of evolvotron being able to load
   files saved from old versions, or producing the same image
   from a loaded function.  Attempting to load functions from later
   versions into earlier versions is even less likely to succeed.

MIDDLE MOUSE BUTTON
-------------------
[NB This feature will probably only be of practical use to those with multi-GHz machines].

You can use the middle mouse button to drag-adjust individual images.
This is useful for "final composition" type tweaks, e.g centering an
image's most interesting feature, or just for satisfying your curiosity 
about what's off the edge of the image.

It also works on "Big" images, although it's virtually unusable without
a bit of practice on smaller, faster ones (just boldly make the adjustment
you want, release the button... and wait).

Changes made can be rolled-back on the main Edit->Undo menu item,
one drag-action at a time.

An unmodified middle-mouse drag pans the image around following
the mouse motion.

A SHIFT-middle drag zooms the image in and out with scaling
proportional to the distance from the centre of the image.  Beware of
generating huge zooms by clicking too near the centre of the image.

An ALT-SHIFT-middle drag is similar but anisotropic: the scaling
may be different in X and Y.  Warning: this technique is very
sensitive and can be quite tricky to use!  In particular,
if you initially click near the centre axes of the image the zoom factor
can be HUGE, so the best way to start using this is to click about halfway
on a diagonal between the image centre and a corner and gently move in and
out radially.  Dragging from one side of the image to the other flips it over
(the degenerate case of infinite zoom at the centre is handled cleanly I think).
If it all goes horribly wrong, undo and try again.

A CTRL-middle drag rotates the image about its centre.

A CTRL-ALT-middle drag shears the image (the best way to see what
this does is to click in the corner of an image and move the mouse
horizontally or vertically).

KEYBOARD CONTROL
================

MAIN WINDOW
-----------

Ctrl-f [NB only available with fullscreen build option] toggles 
full-screen mode (on X11, Qt does this by asking the
window manager for a screen-filling undecorated window, and the
documentation contains some dire warnings about problems with broken
window managers).  See also "-F" command line option.
Fullscreen mode propagates to "Big" image display windows.
NB The application may completely disappear from the screen for 
a brief interval while switching mode.

Ctrl-m [NB only available with fullscreen build option]
toggles status and menu-bar hiding, which can be nice 
when in full-screen or window-maximised mode.  See also "-M" 
command line option.

Esc [NB only available with fullscreen build option] kills 
full-screen and/or menu-hiding, putting the application
into its normal default state. 

Ctrl-r does a restart (for convenience when the menu bar
is hidden, because this is such a common operation).

Ctrl-z does an undo.

"BIG IMAGE" WINDOWS
-------------------
The image display windows created by selecting "Big" from a
context menu also have a couple of keyboard operations:

Ctrl-f [NB only available with fullscreen build option] toggles 
full-screen mode.  When returning to normal mode, if the main app 
window was fullscreen then it will also drop back to normal mode.

Esc [NB only available with fullscreen build option]
completely closes a fullscreen-mode "big" window.

GUI ELEMENTS
============

MAIN MENU BAR
-------------
On the application's main menu-bar, the Edit menu lets you undo
previous full-grid spawns and middle-button adjustments.
There is a large but limited number of levels of undo; note that
locking is overriden, and that locking/unlocking a display is
not currently recorded in the undo history), and bring
up a dialog to modify the mutation parameters (see "useful tips"
and "advanced usage" below).

STATUS BAR
----------
An area on the status bar shows how many "tasks" are outstanding.  
Each "task" is the recomputation of an image at some resolution.
Tasks are prioritised by their number of pixels  (small image =>higher
priority).  This is why, if the main grid is still recomputing,
recalculationa of a "big" image will appear to freeze after it
has reached a certain resolution, at least until other lower
resolution tasks have completed. 

TIPS
====
- Don't start a session with any preconceived ideas about the kind
  of image you want to get out of it.  You will be disappointed.
- I get the best results when I click the image which most
  immediately catches my eye as they start appearing.  If you stop
  to think about it too much then things seem to go downhill.
- If you seem to be just getting the same old spirals and grids
  all the time, stop clicking on spirals and grids! 
  (The same goes for random mush).
- Don't get too hung up on using the warp and middle-mouse drag
  adjustments every iteration... use those tools for final
  polishing of your masterpiece.
- You can quickly cycle through a lot of initial images (until
  you find one with real potential) by bashing on the menu
  accelerator keys Alt-f,Alt-r to repeatedly restart.
  (ctrl-r also does this now).

ANIMATION
=========
As of version 0.2.0 evolvotron contains some experimental support
for generation of animations (although so far the results have been
pretty disappointing IMHO, but it's still early days).

NB THIS IS EVEN MORE COMPUTATIONALLY AND MEMORY INTENSIVE THAN
THE CONVENTIONAL STATIC IMAGE MODE.

Simply supply a -f <frames> command line option and evolvotron will
generate animated sequences with the specified number of frames.
These will be displayed at the frame rate specified by the optional
-r <framerate> option (default 8).  So "evolvotron -f 24" will generate
3 second long animations.  Animations reverse direction at each end
to avoid a sudden jump.

If you save an animation, multiple files will be saved with
.fnnnnnn (where nnnnnn is the zero-filled frame number) 
inserted in each filename before the filetype qualifier.
This is a bit awkward, but is a consequence of Qt lacking
support for saving movie format files, so I suggest
you use the ImageMagick tools to convert to a more usable
format (GIF or MNG) for now.

For example, if you enter foo.ppm as the filename to save,
files foo.f000000.ppm, foo.f000001.ppm... will be saved.
You can convert these to a MNG (a multi-frame PNG) playing at
approx. 8 frames per second with: 
  convert -delay 12 foo.f??????.ppm foo.mng
(I have had some problems with .mng format; try .gif instead if this is the case).

ADVANCED USAGE
==============
Evolvotron's idea of an image is a function which converts
XYZ co-ordinates to an RGB colour (however we can only display
a 2D plane for now so the input Z is fixed to zero, or varied 
with time when animating).

The image functions are constructed from trees of function nodes.
(In the mathematical expression 1+(2*x) the "+" and the "*" would
be function nodes.)  Evolvotron's functions tend to correspond to
geometric or colour-space operations or anything else which can be
applied to a 3D vector.

By mutating the structure of the function tree (adding random
branches, for example) and the values of the constant embedded
within it, the image can be changed.

The mutation parameters are under control from the dialog accessible via the
Edit->Mutation Parameters... menu, and also from buttons on the status bar).

There are two kinds of mutation: perturbations to the magnitude of constants, 
and structural mutations which rearrage the function tree of an image.  
Four types of structural mutations are currently implemented:
replacement of a function branch by a new random stub (a "Glitch" mutation),
a random shuffle of a node's arguments, the insertion of random nodes
between a node and it's children and the substitution of a node with one
of a different type (but with child nodes unaffected where possible).
The probability (per function node) of these mutations is controlled
from spinboxes on the dialog (expressed as chances-in-a-million), 
as is the size of perturbations to constants.

It is useful to think of the constant perturbations as being a thermal effect
(hence the "heat" and "cool" buttons), while structural alterations are
more drastic and are caused by high energy gamma rays or something
(hence "irradiate" and "shield" buttons to adjust the probability of
structual mutations). 

So why would you want to change the mutation parameters from the initial
defaults ?  Basically, if you're getting too much variation in spawned images 
(this tends to happen after many generations of images, by which time the
function trees have grown quite large and therefore are experiencing a lot
of mutations) then cool and/or shield.
If all the images look too similar, heat and/or irradiate.

2 types of function node are considered fundamental: constant nodes
(which return a constant) and position nodes (which should really be
called identity nodes) which return their position argument.  There
are two slider controls to affect things realted to these:
 - "proportion constant" controls the proportion of fundamental nodes
   which are constants.  Changing this from its default value of 0.5 doesn't
   actually seem to have much effect.
 - "identity supression" causes all identity nodes to actually include a
   random transform; the main effect of this is that images are less commonly
   obviously centred on the origin or aligned with the axes.  I think this
   is a good thing, so identity supression is at 1.0 by default.

OTHER OPTIONS
=============
On the mutation parameters dialog there are tick boxes to enable two classes of
function which are disabled by default.  These are "iterative" functions and
"fractal" functions (Mandelbrot and Julia set type things).  The main reason
for their being optional is that, in the former case they're expensive, and
in the latter case they're expensive and ugly.  Note that if fractal functions
are enabled, then iterative functions are enabled too.

OTHER EXECUTABLES
=================
This release also builds two other executables:

 - evolvotron_render
   Usage: evolvotron_render [-v] [-s <width> <height>] [-f <frames>] [<file.ppm>|<file.png>] < <file.xml>
   Reads a XML function description from it's standard input and renders it to the
   file specified (suffix determines type, defaults to ppm if not recognised) at the
   specified resolution.  Image size is specified by -s option and defaults to 512x512.
   The -f option generates multi-frame animations.  .fnnnnnn is inserted/appended to
   the specified filename in the same way as animations saved from evolvotron.
   (You can use this on functions which weren't evolved in animation mode, but there's
   no guarantee they have any interesting time/z variation).
   Use the -v option to monitor the progress of a long animation run.
   [NB This app does not use multiple compute threads].

 - evolvotron_mutate
   Usage: evolvotron_mutate [-g | < <file.xml>] > <file.xml>
   With the -g (generate, genesis?) option, creates a new random
   evolvotron function and writes its XML description to standard out.
   Without the -g option, reads an XML function description from
   standard input, mutates it (using the same algorithm and parameters
   as evolvotron in its default state) and writes the new function's XML
   representation to the standard output.

 Examples:
  Evolving and mutating on the command line:
    evolvotron_mutate -g | tee fn.xml | evolvotron_render /tmp/xxx.ppm ; display /tmp/xxx.ppm
    cat fn.xml | evolvotron_mutate | evolvotron_render /tmp/xxx.ppm ; display /tmp/xxx.ppm

  Animating a function ani.xml produced from evolvotron in animation mode:
    cat ani.xml | evolvotron_render -f 100 -v -s 256 256 ani.ppm ; animate ani.f??????.ppm

FUTURE DEVELOPMENTS
===================
Check the TODO file first before you send me suggestions!

Please don't ask me to port evolvotron to proprietary platforms.
You are of course Free under the terms of the GPL to do so yourself, 
but please reed
  http://www.fefe.de/nowindows/ 
first.

BUILDING CODE DOCUMENTATION
===========================
If you have doxygen (and graphviz too) and want to build
the source code documentation, execute 
  ./mkdoc
at the top level.  The code documentation then appears in ./doc/html/

THANKS
======
...to
  Dmitry Kirsanov
  Jonathan Melhuish
  Karl Robillard
  Linc Davis
  Paolo Greppi  
  Marcin Wojtczuk
for feedback, suggestions and patches.

...to the participants in a SIGGRAPH conference panel many
years ago who first got me interested in this kind of thing.

...to www.di.fm, www.somafm.com and Trance4Ever for music to code to.

WHY ?
=====
I have always admired those who have the skill to wield a pen or paintbrush
and fill a sheet of paper or a canvas with some striking image from their
imagination.  Unfortunately I lack the patience to learn such skills, 
and probably the necessary manual dexterity and imagination too.  
Evolvotron, and its predecessors developed on and off over a decade 
since I first came across the idea, are an attempt to compensate for 
this using the skills I do have  i.e some mathematical sensibility 
and the ability to write working code (well, sometimes).  If you like 
an image it produces, then as far as I'm concerned that's as satisfying 
a result as if you liked something I'd drawn myself.

Tim