Two things that we have learnt from introducing Field to the Processing community. Firstly, "Processing without the stop-and-start button" is an easy sell (in fact, everything else in Field, like timelines, Python, and embedded GUI elements sometimes seems like icing on this cake). The more experienced somebody is with the Processing PDE the the easier it is for them to "get" why dynamically interacting with your applet is a good idea.
But the other thing we learned is that the people who do get what Field can do for their work are also the people that have heavily invested in Processing. They have their own Processing code that they like to work with.
Field is not another software empire in the making; Field is not a replacement for Processing; Field wants you to use your own code. Let's look at 4 different ways of adding increasing amounts of Field to your Processing world.
We'll be using a completely random Processing Applet we grabbed from OpenProcessing, here.html because it's just the right size for our example.
It has two files. The first called "school.pde":
// Random Walker (Vectors)
// Daniel Shiffman <http://www.shiffman.net>
// The Nature of Code, Spring 2009
Walker[] walkers = new Walker[1300];
Walker w;
void setup() {
size(400,400);
frameRate(30);
smooth();
// Create a walker object
for(int i = 0; i < walkers.length; i++){
walkers[i] = new Walker(random(60,170), 90);
}
}
void draw() {
background(255);
// Run the walker object
for(int i = 0; i < walkers.length; i++){
walkers[i].walk();
walkers[i].render();
}
}
And a second, called "walker.pde":
// Random Walker (No Vectors)
// Daniel Shiffman <http://www.shiffman.net>
// The Nature of Code, Spring 2009
// A random walker class!
public class Walker {
PVector loc;
float radius;
float angle;
float wiggle;
float wAngle;
float value;
Walker(float _radius, float _value) {
value = _value;
println(value);
radius = _radius;
angle = random(0,360);
wAngle = random(0,360);
loc = new PVector((cos(radians(angle)) * radius) + width/2,(sin(radians(angle)) * radius) + height/2);
}
void render() {
stroke(20,200);
fill(value);
//rectMode(CENTER);
ellipse(loc.x,loc.y,10,10);
}
// Randomly move up, down, left, right, or stay in one place
void walk() {
//PVector vel = new PVector(random(-range,range),random(-range,range));
//loc.add(vel);
// add wiggle
wAngle += 5;
wiggle = cos(radians(wAngle)) * 2;
angle += .5+(wiggle/10);
loc = new PVector((cos(radians(angle)) * (radius + wiggle)) + width/2,(sin(radians(angle)) * (radius + wiggle)) + height/2);
// Stay on the screen
loc.x = constrain(loc.x,0,width-1);
loc.y = constrain(loc.y,0,height-1);
}
}
For the purposes of this page we're going to pretend that this applet is something much bigger — and something that we've invested so much time in crafting that we even if we feel that Field is the greatest thing ever, we are reluctant to abandon this code or rewrite it in Field. Instead we are going to examine a few different ways of integrating this code into Field with only incremental changes:
Here's a secret: Processing is just Java and Field knows how to talk to Java things. Thus, Field knows how to wtalk to your Processing applet already. You just need to tell Field where it is.
First, export a completely unprepared, completely vanilla Processing applet using the PDE's export button:
This gets you a nice little directory with your .pde files, the .java source versions of them and, most importantly a .jar containing your work.
Now we tell Field about that jar in the Plugin Manager:
And, after a restart of Field we can write the following code:
import school
school.main([])
And, after executing that, we get our applet:
This proves that Field can make an applet just as well as any web-browser. What's next?
Let's go back to the source code in PDE and add two lines. The line that says:
void setup() {
should be edited to read:
static school here;
void setup(){
here = this;
Now export this again and restart Field.
Now we can write:
import school
school.main([])
theSchool = school.here
We've introduced a variable inside the Processing code that lets us grab hold of the Applet that Processing makes. Now that we have the applet, we can do absolutely anything we like to the running applet. For example, to play with the shape of the "school":
for w in theSchool.walkers:
w.radius=100+Math.random()*30
w.value=200
Ta da! A manipulation of a running Processing applet, no stop-compile-go button required. And no porting your old Processing/Java code to Field/Python.
You are free to manipulate radius &c from the timelines, inside animation loops, from sliders, and so on. Much of Field is at your disposal:
The method above is appropriate when we have a large Processing applet that we'd like to "poke" at either to refine it or debug it — you can set (and print) any field that's part of the applet from inside Field. But it takes the applet as a given — you can't really change what it does.
To get more structural control over the applet we are going to have to use a little more Field. If we take a closer look at our example Applet we'll see that it's draw method is pretty straightforward — it makes some calls to other things in order to do it's drawing. This is quite often the case — you end up with a fairly small 'draw' method that makes calls to your code to do the computation and then a few more calls straight to the Processing libraries to draw things.
We can often reuse the "heavy lifting" of the computation while recoding the drawing in Field using Field's support for embedding Processing Applets (see SimpleProcessingTutorial).
Let's try this. Two boxes, the first just initializes everything and needs to be run once:
import school
from school import *
s = school()
s.width, s.height = p.width, p.height
for n in range(len(s.walkers)):
s.walkers[n] = Walker(s, random(60,170), 90)
That's a rewrite of the "meat" of school.setup()
. Now for draw()
:
def drawApplet():
background(255)
for w in s.walkers:
w.walk()
stroke(20,200);
fill(w.value);
ellipse(w.loc.x,w.loc.y,10,10);
_r = drawApplet
That's a rewrite of draw()
. Note that we've reused all of the non-Processing specific code inside the Walker class, and moved the drawing code to Field/Python. In the common case where w.walk()
is some kind of big, super-sophisticated thing that requires the speed of pure-Java to run, while the actual drawing code is something that we want to actively tweak and refine, this split works very well. We keep our fast "simulation" code completely untouched (and completely fast) while moving our drawing code into the dynamic, rewrite it as you go world of Field.
If we like we can go further and split drawApplet
into two boxes — one that calls w.walk()
and another that draws the drawing. Now we can keep the "simulation" running while starting and restarting the drawing code.
Obviously its possible to structure your Processing Applet where this split between computation and drawing doesn't exist — you have Processing drawing code littered throughout your computation. At that point you are a little cornered — you might want to think about restructuring your code a little. Ultimately it will be easier for you to reuse components.
draw()
in FieldBut if you do have a applet that's split into drawing bits and computation bits, you are now free to code the drawing part in a completely different drawing system (while keeping the computation part in your old code). For example — you can use the Field graphics system.
Three boxes. The first, run once, sets things up:
import school
from school import *
s = school()
s.width, s.height = p.width, p.height
for n in range(len(s.walkers)):
s.walkers[n] = Walker2(s, p.random(60,170), 90)
canvas = getFullscreenCanvas()
shader = makeShaderFromElement(_self)
canvas << shader
points = pointContainer()
shader << points
Another to run the "simulation":
def simulate():
for w in s.walkers:
w.walk()
_r = simulate
And another to feed the data to the graphics system:
def draw():
with points:
for w in s.walkers:
points.nextVertex(Vector3(w.loc.x, w.loc.y, 0))
_r = draw
Together they yield:
Clearly if we head in this direction we end up replacing Processing's PDE and drawing library with Field, it's drawing library and possibly a IDE like Eclipse to develop the pure Java part. This should come as no surprise: Field was originally created, and is very much still in use, as an environment to run along side large Java development tools like Eclipse.
But there's a slightly different route we can also take that's worth mentioning — we can embed the Processing PDE parser directly in Field.
We basically copy and paste the PDE code right into a Field box:
Walker2[] walkers = new Walker2[1300];
Walker2 w;
void setup() {
frameRate(30);
smooth();
for(int i = 0; i < walkers.length; i++){
walkers[i] = new Walker2(random(60,170), 90);
}
}
void draw() {
background(255);
for(int i = 0; i < walkers.length; i++){
walkers[i].walk();
walkers[i].render();
}
}
class Walker2 {
PVector loc;
float radius;
float angle;
float wiggle;
float wAngle;
float value;
Walker2(float _radius, float _value) {
value = _value;
println(value);
radius = _radius;
angle = random(0,360);
wAngle = random(0,360);
loc = new PVector((cos(radians(angle)) * radius) + width/2,(sin(radians(angle)) * radius) + height/2);
}
void render() {
stroke(20,200);
fill(value);
//rectMode(CENTER);
ellipse(loc.x,loc.y,10,10);
}
// Randomly move up, down, left, right, or stay in one place
void walk() {
//PVector vel = new PVector(random(-range,range),random(-range,range));
//loc.add(vel);
// add wiggle
wAngle += 5;
wiggle = cos(radians(wAngle)) * 2;
angle += .5+(wiggle/10);
loc = new PVector((cos(radians(angle)) * (radius + wiggle)) + width/2,(sin(radians(angle)) * (radius + wiggle)) + height/2);
// Stay on the screen
loc.x = constrain(loc.x,0,width-1);
loc.y = constrain(loc.y,0,height-1);
}
}
All we've done here is remove the call to size()
which can cause problems with Field.
And:
If we follow the instructions here, we can just option-click this box and have it run. Sure, the option-clicking is essentially a stop-compiled-go button, but a) we have all the code in the same application, b) we can use the odd slider here and there, c) we can have other functions executing at the same time "inside" draw()
from other boxes. So this is another, incremental way, of getting Field involved in your Processing-based practice.