I wanted to get a graphics tablet for years, but it was too expensive. Last year I finally made the investment. I bought a Wacom Bamboo in A5 format. Not as precise and feature full as an Intuos, but nice to play with anyway.
Naturally, I looked if I could use it within Processing. I looked at JTablet, but it seemed a bit outdated and problematic. I saw a reference to
JPen and tried it. It had a pair standalone demos, one just dumping lot of textual data about pen manipulation, another quite impressive, given it simplicity, allowing to do simple draws with pressure effects.
I took the source code of the latter, and adapted it to Processing. But at the time, it was Java 1.5 (IIRC), and JPen was 1.6. I tried to recompile it, without success. I went to other things.
Recently, I tried again, with the latest version of Processing and of JPen, and it worked out of the box! And Jpen supports MacOS X now. Some minor code fixes, some uses of the new features, and here is the demo code:
Code:import jpen.event.*;
import jpen.*;
void setup()
{
size(800, 800);
PenManager pm = new PenManager(this);
pm.pen.addListener(new ProcessingPen());
smooth();
background(#FFFFFF);
}
void draw()
{
}
// Most of the code here is taken from the DrawingSurface demo code.
public class ProcessingPen extends PenAdapter
{
boolean bIsDown;
float prevXPos = -1, prevYPos = -1;
public void penButtonEvent(PButtonEvent evt)
{
// See if the pen is down
bIsDown = evt.pen.hasPressedButtons();
/* Or check with a finer granularity.
// Pen pressed is LEFT for me (Bamboo).
if (evt.pen.getButtonValue(PButton.Type.LEFT))
println("LEFT");
if (evt.pen.getButtonValue(PButton.Type.CENTER))
println("CENTER");
// Pen button pressed is RIGHT
if (evt.pen.getButtonValue(PButton.Type.RIGHT))
println("RIGHT");
//*/
}
public void penLevelEvent(PLevelEvent evt)
{
// Get kind of event: does it come from mouse (CURSOR), STYLUS or ERASER?
PKind type = evt.pen.getKind();
// Discard events from mouse
if (type == PKind.valueOf(PKind.Type.CURSOR))
return;
// Get the current cursor location
float xPos = evt.pen.getLevelValue(PLevel.Type.X);
float yPos = evt.pen.getLevelValue(PLevel.Type.Y);
// Set the brush's size, and darkness relative to the pressure
float pressure = evt.pen.getLevelValue(PLevel.Type.PRESSURE);
float brushSize = pressure * 10;
float darkness = 255 * pressure;
// Get the tilt values (not with a Bamboo... so untested!)
float xTilt = evt.pen.getLevelValue(PLevel.Type.TILT_X);
float yTilt = evt.pen.getLevelValue(PLevel.Type.TILT_Y);
// Transform them to azimuthX and altitude, two angles with the projection of the pen against the X-Y plane
// azimuthX is the angle (clockwise direction) between this projection and the X axis. Range: -pi/2 to 3*pi/2.
// altitude is the angle between this projection and the pen itself. Range: 0 to pi/2.
// Might be more pratical to use than raw x/y tilt values.
double[] aa = { 0.0, 0.0 };
PLevel.Type.evalAzimuthXAndAltitude(aa, xTilt, yTilt);
// or just PLevel.Type.evalAzimuthXAndAltitude(aa, evt.pen);
double azimuthX = aa[0];
double altitude = aa[1];
/* If the stylus is being pressed down, we want to draw a black
line onto the screen. If it's the eraser, we want to create
a white line, effectively "erasing" the black line
*/
if (type == PKind.valueOf(PKind.Type.STYLUS))
{
color c = color(0, 0, 255 - darkness);
stroke(c);
}
else if (type == PKind.valueOf(PKind.Type.ERASER))
{
stroke(255, darkness);
}
else
{
return; // IGNORE or CUSTOM...
}
if (!evt.isMovement())
{
// Not a movement, just draw a dot
ellipse(xPos, yPos, brushSize, brushSize);
return;
}
if (!bIsDown)
{
// Pen up, stop current line and down't draw anywthing
prevXPos = -1;
return;
}
if (prevXPos == -1)
{
prevXPos = xPos;
prevYPos = yPos;
}
// Draw a line between the current and previous locations
strokeWeight(brushSize);
line(prevXPos, prevYPos, xPos, yPos);
prevXPos = xPos;
prevYPos = yPos;
}
// When user moves on the big round scroll button
void penScrollEvent(PScrollEvent evt)
{
PScroll.Type type = evt.scroll.getType();
int value = evt.scroll.value;
if (type == PScroll.Type.DOWN)
{
println("Scrolling down " + value);
}
else if (type == PScroll.Type.UP)
{
println("Scrolling up " + value);
}
else if (type == PScroll.Type.CUSTOM)
{
println("Scrolling custom (?) " + value);
}
}
// What is it?
void penKindEvent(PScrollEvent evt)
{
println("Kind Event: " + evt);
}
}
OK, it will be useless for most of you, but those fortunate enough to own such device, I hope this code might be a good starting point.
PS.: 'ac' wrapped JPen in a
library for Processing, you might be interested by it instead of using raw library.