Over the years I work with processing I learned a few useful tricks to help me at points where Processing seems to hit it’s limits. I want to share a few of these tricks with you:
Save current state of the sketch
If you are working on e.g. a game and you wish to have save states you often have to make some methods that try to convert your data into Strings and back. Updating them can be quite anoying so it might be better to have a approach you put down once and never have to worry about again. For that exact purpose I have these two methods:
//saves the data
void saveData(String relPath) {
Object e=this;
//get all non-transient fields
HashMap<String, Object> ntf=new HashMap();
java.lang.reflect.Field[] field_=e.getClass().getDeclaredFields();
try {
for (int i=0; i<field_.length; i++) {
field_[i].setAccessible(true);
if (!java.lang.reflect.Modifier.isTransient(field_[i].getModifiers()))
ntf.put(field_[i].getName(), field_[i].get(e));
}
}
catch (Exception ex) {
ex.printStackTrace();
}
//serializes the hash-map
try {
saveStrings(sketchPath(relPath), new String[]{});
java.io.FileOutputStream fileout=new java.io.FileOutputStream(sketchPath(relPath));
java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(fileout);
out.writeObject(ntf);
out.close();
}
catch(IOException ex) {
ex.printStackTrace();
}
}
//loads the data
void loadData(String relPath) {
//load HashMap from file
HashMap<String, Object> r=new HashMap();
try {
java.io.FileInputStream fileout=new java.io.FileInputStream(sketchPath(relPath));
java.io.ObjectInputStream out=new java.io.ObjectInputStream(fileout);
r=(HashMap<String, Object>) out.readObject();
out.close();
//Load contents of the HashMap into the sketch
Object o=this;
java.lang.reflect.Field[] field_=o.getClass().getDeclaredFields();
for (int i=0; i<field_.length; i++) {
try {
field_[i].setAccessible(true);
//load contents only in non-transient fields
if (!java.lang.reflect.Modifier.isTransient(field_[i].getModifiers())) field_[i].set(o, r.get(field_[i].getName()));
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
catch(IOException e) {
e.printStackTrace();
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
Example
Each field you don’t want to save must be marked with the transient keyword!
int x=(int) random(0,10);
int y=(int) random(0,10);
transient int z=(int) random(0,10);
void setup() {
}
void draw() {
println(x, y, z);
}
void keyPressed() {
if (key=='s') saveData("data/data.txt");
if (key=='l') loadData("data/data.txt");
}
void saveData(String relPath) {
Object e=this;
//get all non-transient fields
HashMap<String, Object> ntf=new HashMap();
java.lang.reflect.Field[] field_=e.getClass().getDeclaredFields();
try {
for (int i=0; i<field_.length; i++) {
field_[i].setAccessible(true);
if (!java.lang.reflect.Modifier.isTransient(field_[i].getModifiers())) ntf.put(field_[i].getName(), field_[i].get(e));
}
}
catch (Exception ex) {
ex.printStackTrace();
}
//serializes the hash-map
try {
saveStrings(sketchPath(relPath), new String[]{});
java.io.FileOutputStream fileout=new java.io.FileOutputStream(sketchPath(relPath));
java.io.ObjectOutputStream out=new java.io.ObjectOutputStream(fileout);
out.writeObject(ntf);
out.close();
}
catch(IOException ex) {
ex.printStackTrace();
}
}
void loadData(String relPath) {
//load HashMap from file
HashMap<String, Object> r=new HashMap();
try {
java.io.FileInputStream fileout=new java.io.FileInputStream(sketchPath(relPath));
java.io.ObjectInputStream out=new java.io.ObjectInputStream(fileout);
r=(HashMap<String, Object>) out.readObject();
out.close();
//Load contents of the HashMap into the sketch
Object o=this;
java.lang.reflect.Field[] field_=o.getClass().getDeclaredFields();
for (int i=0; i<field_.length; i++) {
try {
field_[i].setAccessible(true);
//load contents only in non-transient fields
if (!java.lang.reflect.Modifier.isTransient(field_[i].getModifiers())) field_[i].set(o, r.get(field_[i].getName()));
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
catch(IOException e) {
e.printStackTrace();
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
Call methods using Strings
This is actually commonly used with vanilla java. The great thing is if you need to assign strings to certain methods you can use this methods instead of switch/caseObject call(String method) {
try {
//uses reflection to find and invoke the method
java.lang.reflect.Method functions=this.getClass().getMethod(method, new Class[]{});
functions.setAccessible(true);
return functions.invoke(this, new Object[]{});
}
catch(Exception e) {
e.printStackTrace();
return null;
}
}
Object call(String method, Object... input) {
Class[] classes=new Class[input.length];
for (int i=0; i<classes.length; i++) classes[i]=input[i].getClass();
//searching for methods with a certain input differentiates bettween primitives and Objects
//so all combinations must be checked beginning with the "all primitives"-variant
boolean iter[]=new boolean[classes.length+1];
for (boolean n : iter) n=false;
while (!iter[iter.length-1]) {
Class[] current=new Class[classes.length];
for (int i=0; i<classes.length; i++)if (!iter[i]) {
if (classes[i]==(Integer.class)) current[i]=int.class;
else if (classes[i]==Float.class) current[i]=float.class;
else if (classes[i]==Long.class) current[i]=long.class;
else if (classes[i]==Short.class) current[i]=short.class;
else if (classes[i]==Double.class) current[i]=double.class;
else if (classes[i]==Boolean.class) current[i]=boolean.class;
else current[i]=classes[i];
} else current[i]=classes[i];
try {
java.lang.reflect.Method functions=this.getClass().getMethod(method, current);
functions.setAccessible(true);
return functions.invoke(this, input);
}
catch(Exception e) {
}
boolean stack=iter[0];
iter[0]=!iter[0];
for (int i=1; i<iter.length&&stack; i++) {
stack=iter[i];
iter[i]=!iter[i];
}
}
return null;
}
The great thing about this is also that you are able to call private methods from libraries/.java tabs.
Example
void setup() {
}
void draw() {
//invoke the first method
call("method1");
//invoke the second method with arguments
call("method2",1,3);
}
void method1(){
println("test");
}
void method2(int x,int y){
println(x,y);
}
Object call(String method) {
try {
//uses reflection to find and invoke the method
java.lang.reflect.Method functions=this.getClass().getMethod(method, new Class[]{});
functions.setAccessible(true);
return functions.invoke(this, new Object[]{});
}
catch(Exception e) {
e.printStackTrace();
return null;
}
}
Object call(String method, Object... input) {
Class[] classes=new Class[input.length];
for (int i=0; i<classes.length; i++) classes[i]=input[i].getClass();
//searching for methods with a certain input differentiates bettween primitives and Objects
//so all combinations must be checked beginning with the "all primitives"-variant
boolean iter[]=new boolean[classes.length+1];
for (boolean n : iter) n=false;
while (!iter[iter.length-1]) {
Class[] current=new Class[classes.length];
for (int i=0; i<classes.length; i++)if (!iter[i]) {
if (classes[i]==(Integer.class)) current[i]=int.class;
else if (classes[i]==Float.class) current[i]=float.class;
else if (classes[i]==Long.class) current[i]=long.class;
else if (classes[i]==Short.class) current[i]=short.class;
else if (classes[i]==Double.class) current[i]=double.class;
else if (classes[i]==Boolean.class) current[i]=boolean.class;
else current[i]=classes[i];
} else current[i]=classes[i];
try {
java.lang.reflect.Method functions=this.getClass().getMethod(method, current);
functions.setAccessible(true);
return functions.invoke(this, input);
}
catch(Exception e) {
}
boolean stack=iter[0];
iter[0]=!iter[0];
for (int i=1; i<iter.length&&stack; i++) {
stack=iter[i];
iter[i]=!iter[i];
}
}
return null;
}
Use swing component with Processing
Swing offers a neat way of creating user interfaces. However if you try to directly add them to processing you usually don't know where to add them or fail. These next methods are supposed to resolve this issue:Add components to the side of the sketch
This offers a way to add an preferbly formatted Container to the side of the sketch:javax.swing.JFrame merge(java.awt.Container content, int w) {
String where="East";
if(w==RIGHT) where="West";
PApplet programm=this;
//Create new window with content
javax.swing.JFrame window=new javax.swing.JFrame();
window.add(content);
//Transfer from Original window to new one
javax.swing.JFrame mainFrame=(javax.swing.JFrame) ((processing.awt.PSurfaceAWT.SmoothCanvas)programm.getSurface().getNative()).getFrame();
java.awt.Container processing_content=mainFrame.getContentPane();
processing_content.setPreferredSize(new java.awt.Dimension(programm.width, programm.height));
window.add(processing_content, where);
window.setSize(new java.awt.Dimension(programm.width+content.getPreferredSize().width, Math.max(programm.height, content.getPreferredSize().height)));
//Hide tracess
mainFrame.setVisible(false);
window.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
window.setIconImage(mainFrame.getIconImage());
window.setName(mainFrame.getName());
window.setVisible(true);
return window;
}
Example
import javax.swing.*;
JTextField textF=new JTextField(10);
void setup() {
size(300, 300);
merge(textF, LEFT);
fill(0);
}
void draw() {
background(255);
text(textF.getText(), 30, height/2);
}
javax.swing.JFrame merge(java.awt.Container content, int w) {
String where="East";
if(w==RIGHT) where="West";
PApplet programm=this;
//Create new window with content
javax.swing.JFrame window=new javax.swing.JFrame();
window.add(content);
//Transfer from Original window to new one
javax.swing.JFrame mainFrame=(javax.swing.JFrame) ((processing.awt.PSurfaceAWT.SmoothCanvas)programm.getSurface().getNative()).getFrame();
java.awt.Container processing_content=mainFrame.getContentPane();
processing_content.setPreferredSize(new java.awt.Dimension(programm.width, programm.height));
window.add(processing_content, where);
window.setSize(new java.awt.Dimension(programm.width+content.getPreferredSize().width, Math.max(programm.height, content.getPreferredSize().height)));
//Hide tracess
mainFrame.setVisible(false);
window.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
window.setIconImage(mainFrame.getIconImage());
window.setName(mainFrame.getName());
window.setVisible(true);
return window;
}
Free place swing components
You might also want to place your swing-components freely. That can be done with these methods:javax.swing.JFrame generated;
java.awt.Container processing_content;
javax.swing.JFrame newWindow() {
PApplet programm=this;
//Create new window with content
javax.swing.JFrame window=new javax.swing.JFrame();
window.getContentPane().setLayout(null);
//Transfer from Original window to new one
javax.swing.JFrame mainFrame=(javax.swing.JFrame) ((processing.awt.PSurfaceAWT.SmoothCanvas)programm.getSurface().getNative()).getFrame();
processing_content=mainFrame.getContentPane();
processing_content.setPreferredSize(new java.awt.Dimension(programm.width, programm.height));
processing_content.setBounds(new java.awt.Rectangle(0, 0, width, height));
window.setSize(width, height);
//Hide tracess
mainFrame.setVisible(false);
window.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
window.setIconImage(mainFrame.getIconImage());
window.setName(mainFrame.getName());
window.setVisible(true);
generated=window;
return window;
}
void addContainer(java.awt.Container cont, int x, int y) {
generated.add(cont, null);
cont.setBounds(new java.awt.Rectangle(x, y, cont.getPreferredSize().width, cont.getPreferredSize().height));
generated.add(processing_content, null);
}
void addContainer(java.awt.Container cont, int x, int y, int w, int h) {
generated.add(cont, null);
cont.setBounds(new java.awt.Rectangle(x, y, w, h));
generated.add(processing_content, null);
}
void callMethodOnClick(javax.swing.AbstractButton button, final String method) {
final PApplet ref=this;
button.addActionListener(
new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
//uses reflection to find and invoke the method
try {
java.lang.reflect.Method functions=ref.getClass().getMethod(method, new Class[]{});
functions.setAccessible(true);
functions.invoke(ref, new Object[]{});
}
catch(Exception ex) {
ex.printStackTrace();
}
}
}
);
}
These methods also implement a way of easily straping a method onto a button.
Example
import javax.swing.*;
JTextField textF=new JTextField(10);
void setup() {
size(500, 500);
newWindow();
JButton jb=new JButton("reset");
addContainer(jb, 40, 40, 80, 50);
addContainer(textF, 150, 40);
callMethodOnClick(jb,"reset");
fill(0);
}
void draw() {
background(frameCount);
text(textF.getText(), 30, height/2);
}
void reset() {
textF.setText("");
}
javax.swing.JFrame generated;
java.awt.Container processing_content;
javax.swing.JFrame newWindow() {
PApplet programm=this;
//Create new window with content
javax.swing.JFrame window=new javax.swing.JFrame();
window.getContentPane().setLayout(null);
//Transfer from Original window to new one
javax.swing.JFrame mainFrame=(javax.swing.JFrame) ((processing.awt.PSurfaceAWT.SmoothCanvas)programm.getSurface().getNative()).getFrame();
processing_content=mainFrame.getContentPane();
processing_content.setPreferredSize(new java.awt.Dimension(programm.width, programm.height));
processing_content.setBounds(new java.awt.Rectangle(0, 0, width, height));
window.setSize(width, height);
//Hide tracess
mainFrame.setVisible(false);
window.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
window.setIconImage(mainFrame.getIconImage());
window.setName(mainFrame.getName());
window.setVisible(true);
generated=window;
return window;
}
void addContainer(java.awt.Container cont, int x, int y) {
generated.add(cont, null);
cont.setBounds(new java.awt.Rectangle(x, y, cont.getPreferredSize().width, cont.getPreferredSize().height));
generated.add(processing_content, null);
}
void addContainer(java.awt.Container cont, int x, int y, int w, int h) {
generated.add(cont, null);
cont.setBounds(new java.awt.Rectangle(x, y, w, h));
generated.add(processing_content, null);
}
void callMethodOnClick(javax.swing.AbstractButton button, final String method) {
final PApplet ref=this;
button.addActionListener(
new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
//uses reflection to find and invoke the method
try {
java.lang.reflect.Method functions=ref.getClass().getMethod(method, new Class[]{});
functions.setAccessible(true);
functions.invoke(ref, new Object[]{});
}
catch(Exception ex) {
ex.printStackTrace();
}
}
}
);
}
Muting print/println
using print/println is an essential of debugging a program can however easily hinder performance. There is however a easy way of "muting" the print command using these methods:java.io.PrintStream outp;
void mute() {
outp=System.out;
System.setOut(new java.io.PrintStream(new OutputStream() {
public void write(int arg) {
}
}
));
}
void unmute() {
System.setOut(outp);
}
Example
void setup() {
}
void draw() {
println(random(0, 10));
}
void keyPressed() {
if (key=='m') {
println("muting...");
mute();
}
if (key=='u') {
unmute();
println("unmuted");
}
}
java.io.PrintStream outp;
void mute() {
outp=System.out;
System.setOut(new java.io.PrintStream(new OutputStream() {
public void write(int arg) {
}
}
));
}
void unmute() {
System.setOut(outp);
}
Buildt-in refresh in classes (Deprecated)
When making complicated sketches you often have classes with a paint-function. Including theses refreshs require you to often keep track of your Objects and refresh them in the draw-section. Library-makers might know the `registerMethod` method. But this doesn't work in normal processing-code. Here are the methods that can automate the refresh (please read the example):
ArrayList<Object> refs=new ArrayList();
ArrayList<String> mets=new ArrayList();
void strapMethod(String methodName, Object referenceName) {
mets.add(methodName);
refs.add(referenceName);
}
void runMethods() {
for (int i=0; i<refs.size(); i++) {
try {
java.lang.reflect.Method m=refs.get(i).getClass().getMethod(mets.get(i), new Class[]{});
m.setAccessible(true);
m.invoke(refs.get(i),new Object[]{});
}
catch(Exception e) {
e.printStackTrace();
}
}
}
Example
void setup() {
size(500, 500);
for (int i=0; i<20; i++) new RandomCirles((int) random(0, width), (int) random(0, height));
}
void draw() {
background(255);
runMethods();
}
PApplet ref=this;
class RandomCirles {
int x, y;
RandomCirles(int x_, int y_) {
x=x_;
y=y_;
strapMethod("draw",this);
}
void draw() {
randWalk();
paint();
}
void randWalk() {
x+=(int) random(-2, 2);
y+=(int) random(-2, 2);
}
void paint() {
ellipse(x, y, 30, 30);
}
}
ArrayList<Object> refs=new ArrayList();
ArrayList<String> mets=new ArrayList();
void strapMethod(String methodName, Object referenceName) {
mets.add(methodName);
refs.add(referenceName);
}
void runMethods() {
for (int i=0; i<refs.size(); i++) {
try {
java.lang.reflect.Method m=refs.get(i).getClass().getMethod(mets.get(i), new Class[]{});
m.setAccessible(true);
m.invoke(refs.get(i),new Object[]{});
}
catch(Exception e) {
e.printStackTrace();
}
}
}
I hope I could provide a few interesting ways to use processing. If you have any question about these feel free to ask! I might make a reply explaining why I had to do things not as straightforward as one might assume them to be.
(Edit: after trying a few things I noticed registerMethod
does work if you just declare the class public. I just make an example for registerMethod:
Example
void setup() {
size(500, 500);
for (int i=0; i<20; i++) new RandomCirles((int) random(0, width), (int) random(0, height));
}
void draw() {
background(255);
}
PApplet ref=this;
public class RandomCirles {
int x, y;
RandomCirles(int x_, int y_) {
x=x_;
y=y_;
ref.registerMethod("draw",this);
}
void draw() {
randWalk();
paint();
}
void randWalk() {
x+=(int) random(-2, 2);
y+=(int) random(-2, 2);
}
void paint() {
ellipse(x, y, 30, 30);
}
}