Processing
pde sketches get pre-processed to java
Part of the attraction of processing is that it hides a lot of java boilerplate stuff from the user. In vanilla processing all sketches get pre-processed to java prior to compilation:-
So sketch_161005a.pde (an auto-named sketch in the processing ide), gets transformed as follows.
MyClass var;
void setup(){
size(200, 200);
var = new MyClass(23);
}
void draw(){
background(255, 0, 0);
fill(0, 0, 200);
ellipse(100, 100, 90, 120);
}
class MyClass{
float var;
MyClass(float var){
this.var = var;
}
}
See the generated java code below, note all those imports
, the class wrapper
, and that MyClass
becomes a java inner class. Also note that size
has moved to settings (the processing guys decided not make this change from processing-2.0 to processing-3.0 explicit, in propane and propane we do expect size to be in settings).
import processing.core.*;
import processing.data.*;
import processing.event.*;
import processing.opengl.*;
import java.util.HashMap;
import java.util.ArrayList;
import java.io.File;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
public class sketch_161005a extends PApplet {
MyClass var;
public void setup(){
var = new MyClass(23);
}
public void draw(){
background(255, 0, 0);
fill(0, 0, 200);
ellipse(100, 100, 90, 120);
}
class MyClass{
float var;
MyClass(float var){
this.var = var;
}
}
public void settings() { size(200, 200); }
static public void main(String[] passedArgs) {
String[] appletArgs = new String[] { "sketch_161005a" };
if (passedArgs != null) {
PApplet.main(concat(appletArgs, passedArgs));
} else {
PApplet.main(appletArgs);
}
}
}
java reflection gets used a lot in processing
It is an unfortunate truth that java reflection methods are popular with both processing developers and some developers of supporting libraries. It is unfortunate because we have to go through hoops to use these methods in propane (and ruby-processing). The captureEvent
and videoEvent
are examples of reflection methods from the processing video developers. But we have made these readily available to propane users as a simple library load load_library :video_event
. This is what you would do (if we had not created the video_event
library):-
- create a java class
VideoInterface
- compile the java class (including classpath)
- create a jar (and place it in the same folder as your sketch)
- modify the sketch to both
require
thejar
and toinclude
the interface.
VideoInterface.java
See [javadoc here][video]
package monkstone.videoevent;
import processing.video.Movie;
import processing.video.Capture;
/**
* This interface makes it easier/possible to use the reflection methods
* from Movie and Capture classes in Processing::App in propane
* @author Martin Prout
*/
public interface VideoInterface {
/**
* Used to implement reflection method in PApplet
* @see processing.video.Movie
* @param movie Movie
*/
public void movieEvent(Movie movie);
/**
* Used to implement reflection method in PApplet
* @see processing.video.Capture
* @param capture Capture
*/
public void captureEvent(Capture capture);
}
To match packaging the java file needs to be nested in monkstone/videoevent
folders (it is unwise not to have a package) and compile and jar as follows, requires jdk11+.
# NB: classpath needs to be a fully qualified path to jars (not as below)
javac -cp video.jar:core.jar monkstone/videoevent/VideoInterface.java
jar -cvf video_event.jar monkstone
See below a sketch which using this VideoEvent interface, see the version using the propane provided :video_event
library here
black_white_capture.rb
require 'propane'
require_relative 'video_event'
class BlackAndWhiteCapture < Propane::App
load_libraries :video
include_package 'processing.video'
include Java::MonkstoneVideoevent::VideoInterface
attr_reader :cam, :my_shader
def setup
sketch_title 'Black & White Capture'
@my_shader = load_shader('bwfrag.glsl')
start_capture(width, height)
end
def start_capture(w, h)
@cam = Capture.new(self, w, h)
cam.start
end
def draw
image(cam, 0, 0)
return if mouse_pressed?
filter(my_shader)
end
# using snake case to match java reflection method
def captureEvent(c)
c.read
end
def settings
size(960, 544, P2D)
end
end
BlackAndWhiteCapture.new
Now where this knowledge becomes really useful, is when you want to use another library, say the vanilla processing carnivore library whose packetEvent
method also depends on java relection. Here is a suitable CarnivoreListener class.
CarnivoreListener.java
package monkstone;
/*
* Best to use a package to avoid namespace clashes, create your own
*/
public interface CarnivoreListener {
/*
* @param packet the CarnivorePacket, a reflection method
*/
public void packetEvent(org.rsg.carnivore.CarnivorePacket packet);
}
Which we compile as before
# NB: classpath needs to be a fully qualified path to jar (not as below)
javac -cp carnivore.jar monkstone/CarnivoreListener.java
jar -cvf carnivore_listener.jar monkstone
Here is an example sketch:-
carnivore1.rb
# A Simple Carnivore Client -- print packets in Processing console
#
# Note: requires Carnivore Library for Processing (https://r-s-g.org/carnivore)
#
# + Windows: first install winpcap (https://winpcap.org)
# + Mac: first open a Terminal and execute this commmand: sudo chmod 777 /dev/bpf*
# you need to do this each time you reboot Mac
# + Linux: run with difficulty (run with sudo rights arghh!!!) also install libpcap
require 'propane'
class CarnivoreListener < Propane::App
load_library :carnivore
include_package 'org.rsg.carnivore'
java_import 'org.rsg.lib.Log'
require_relative 'carnivore_listener'
include Java::Monkstone::CarnivoreListener
attr_reader :c
def settings
size(600, 400)
end
def setup
sketch_title 'Carnivore Example'
background(255)
@c = CarnivoreP5.new(self)
Log.setDebug(true) # comment out for quiet mode
# c.setVolumeLimit(4) #limit the output volume (optional)
# c.setShouldSkipUDP(true) #tcp packets only (optional)
end
def draw
end
# Called each time a new packet arrives
def packetEvent(p)
puts(format('(%s packet) %s > %s', p.strTransportProtocol, p.senderSocket, p.receiverSocket))
# puts(format('Payload: %s', p.ascii))
# puts("---------------------------\n")
end
end
CarnivoreListener.new
Another example of reflection usage the vanilla processing selectInput
utility, that enable use of the native file chooser:-
Native File Chooser
What a native file chooser sketch looks like in java, the selectInput
callback relies on java reflection under the hood. We have to explicity provide such a signature to use this feature in propane hence FileChooser library.
public class chooser extends PApplet {
public void setup() {
selectInput("Select a file to process:", "fileSelected");
}
public void fileSelected(File selection) {
if (selection == null) {
println("Window was closed or the user hit cancel.");
} else {
println("User selected " + selection.getAbsolutePath());
}
}
static public void main(String[] passedArgs) {
String[] appletArgs = new String[] { "chooser" };
if (passedArgs != null) {
PApplet.main(concat(appletArgs, passedArgs));
} else {
PApplet.main(appletArgs);
}
}
}
Read more about java reflection here.