We are about to switch to a new forum software. Until then we have removed the registration on this forum.
Hello, everyone!
In a 2D scenario, I need to implement an object picker using a color buffer as a technique: a color is an unique ID.
It was easy to implement it by creating a new PGraphics where to render my objects using a unique color. However, this only works until I draw the alpha channel at the maximum opacity (stick on the the default: 255); but I'd like to use the alpha channel too, so that I can theoretically handle more elements.
In order to take advantage of the alpha-channel too, I ended up using both a PGraphics and a PImage objects. Briefly, my idea was:
I'd be glad to know if somebody's got suggestions for a "better" (e.g. cleaner, faster, easier, ...) implementation of this picker (taking in account that the aim is to have a color-picker that uses all the ARGB channels).
Here's the code (in the sketch, use 'c' to add a circle and mouse-click to select an element). The juice is in the draw() method.
Thank you! ; )
sketch.pde
import java.util.List;
PGraphics pickingBuffer;
PImage pickingImage;
List<Circle> circles;
Circle ghostCircle;
void setup() {
size(500, 300);
pickingBuffer = createGraphics(width, height);
pickingImage = createImage(width, height, ARGB);
circles = new ArrayList<Circle>();
ghostCircle = new Circle(-1, 0xaaeeeeee);
}
void draw() {
// Picker
//
pickingBuffer.noSmooth();
pickingBuffer.beginDraw();
pickingBuffer.noStroke();
// Iterate over all the elements I need to be drawn
for (Circle c : circles) {
int id = c.getID();
// First, draw the current element on pickingBuffer
// using the full opacity.
// Notice that the 0,0,0,255 color is the id of no elements.
pickingBuffer.background(id2r(0), id2g(0), id2b(0));
c.draw(pickingBuffer, color(id2r(id), id2g(id), id2b(id)));
// Then, iterate over all the pixels of the buffer:
// those that are different from 0,0,0,255, take them
// and write the right opacity to their alpha-channel
pickingBuffer.loadPixels();
pickingImage.loadPixels();
for (int i = 0; i < width * height; i++) {
int px = pickingBuffer.pixels[i];
if (px != 0xff000000) {
pickingImage.pixels[i] = (id2a(id) << 24) | (px & 0x00ffffff);
}
}
pickingImage.updatePixels();
}
pickingBuffer.endDraw();
// Rendering
//
background(128);
for (Circle c : circles) {
if (c.isSelected()) {
stroke(255, 0, 0);
strokeWeight(5);
} else {
noStroke();
}
c.draw(g);
}
ghostCircle.setX(mouseX);
ghostCircle.setY(mouseY);
noStroke();
ghostCircle.draw(g);
}
void keyReleased() {
if (key == 'c') {
Circle c = new Circle(circles.size() + 1,
int(random(0xff000000, 0xffffffff)));
c.setX(mouseX);
c.setY(mouseY);
circles.add(c);
int id = c.getID();
System.out.println("registering "
+ "id:" + id + "\t"
+ "r:" + id2r(id) + ", "
+ "g:" +id2g(id) + ", "
+ "b:" +id2b(id) + ", "
+ "a:" +id2a(id));
}
}
void mousePressed() {
pickingImage.loadPixels();
int argb = pickingImage.pixels[mouseY * width + mouseX];
int a = (argb >> 24) & 0xff;
int r = (argb >> 16) & 0xff;
int g = (argb >> 8) & 0xff;
int b = argb & 0xff;
int id = argb2id(a, r, g, b);
System.out.println("argb: " + argb
+ "\tr: " + r
+ "\tg: " + g
+ "\tb: " + b
+ "\ta: " + a
+ "\tid: " + id);
id = id - 1;
if (id >= 0 && id < circles.size()) {
circles.get(id).toggleSelection();
}
}
int argb2id(int a, int r, int g, int b) {
return (a << 24) | (r << 16) | (g << 8) | b;
}
int id2a(int id) {
return (id & 0xff000000) >>> 24;
}
int id2r(int id) {
return (id & 0x00ff0000) >>> 16;
}
int id2g(int id) {
return (id & 0x0000ff00) >>> 8;
}
int id2b(int id) {
return (id & 0x000000ff);
}
circle.pde
class Circle {
private int id;
private color c;
private PVector position;
private float diameter;
private boolean selected = false;
public int getID() { return id; }
public float getX() { return position.x; }
public void setX(float x) { position.x = x; }
public float getY() { return position.y; }
public void setY(float y) { position.y = y; }
public float getDiameter() { return diameter; }
public void setDiameter(float diameter) {
if (diameter < 0) {
throw new IllegalArgumentException("Diameter must be at least 0");
}
this.diameter = diameter;
}
public boolean isSelected() { return selected; }
public void toggleSelection() {
selected = !selected;
}
public Circle(int id, color c) {
this.id = id;
this.c = c;
position = new PVector(0, 0);
this.diameter = 60;
}
public void draw(PGraphics g) {
draw(g, c);
}
public void draw(PGraphics g, color c) {
g.fill(c);
g.ellipseMode(CENTER);
g.pushMatrix();
g.translate(position.x, position.y);
g.ellipse(0, 0, diameter, diameter);
g.popMatrix();
}
}