As you have discovered the picking library is very easy to use especially for a newbie to Processing. There are other libraries and this animation shows picking spheres in a 3D rotating space and was created using the Shapes3D library and the sketch code below.
As you can see the code is more complex and instead of using Processing’s sphere()
method the library supports a wide range of shapes including Ellipsoid
.
Maybe you are not ready to try this library yet but it might be something to keep in mind as you get more used to Processing. Shapes3D has its own website for detailed information on it’s API and use.
/**
* ========================================================
* Shape picking with a mouse click or a selection marquee
* ========================================================
*
* This sketch demonstrates how you can use a
* marquee selection and find all shapes/shape
* parts inside the selection rectangle.
*
* Simple click and drag a selection area, when you
* release the mouse button all ball faces inside the
* selection area will be given a new random colour.
*
* For a single ball a simple click on it works.
* Notice there is a slight pause on first use,
* this is caused by Shapes3D creating the pick
* graphics buffer and will not happen again unless
* you resize the display.
*
* created by Peter Lager 2024
*/
import shapes3d.*;
import shapes3d.contour.*;
import shapes3d.org.apache.commons.math.*;
import shapes3d.org.apache.commons.math.geometry.*;
import shapes3d.path.*;
import shapes3d.utils.*;
int nbrBalls = 20;
Ellipsoid[] ball = new Ellipsoid[20];
int[] bcolor = new int[ball.length];
float bsize, a, d = 50, c = 120;
// Marquee variables
int mx0, my0, mx1, my1;
boolean mvisible = false;
void setup() {
size(400, 400, P3D);
cursor(CROSS);
textSize(20);
textAlign(CENTER, CENTER);
for (int i = 0; i < ball.length; i++) {
bcolor[i] = randomColor();
bsize = 5 + (int)random(12);
ball[i] = new Ellipsoid(bsize, bsize, bsize, 20, 12);
ball[i].moveTo(random(-d, d), random(-d, d), random(-d, d));
ball[i].fill(bcolor[i]);
ball[i].drawMode(S3D.SOLID);
ball[i].tag = "#:" + i;
}
}
void draw() {
background(64);
push();
lights();
a += 0.006;
camera(c * sin(a), 10, c * cos(a), 0, 0, 0, 0, 1, 0);
for (int i = 0; i < ball.length; i++) {
ball[i].draw(getGraphics());
}
pop();
push();
// Draw 2D stuff above 3D scene
hint(DISABLE_DEPTH_TEST);
// Draw selection area
if (mvisible) {
stroke(255);
strokeWeight(2);
fill(255, 160);
rect(mx0, my0, mx1 - mx0, my1-my0);
}
noStroke();
fill(0);
rect(0, height - 30, width, 30);
fill(160, 255, 160);
text("[R] to restore original colors", 0, height - 34, width, 30);
hint(ENABLE_DEPTH_TEST);
pop();
}
// Select single ball with mouse click
void mouseClicked() {
Picked p = Shape3D.pick(this, getGraphics(), mouseX, mouseY);
if (p != null)
println("Balls selected " + p.shape.tag);
}
void mousePressed() {
mx0 = mx1 = mouseX;
my0 = my1 = mouseY;
mvisible = true;
}
void mouseDragged() {
mx1 = mouseX;
my1 = mouseY;
}
// Find all balls that intersect with a selection area
void mouseReleased() {
Picked[] picked = Shape3D.pick(this, getGraphics(), mx0, my0, mx1, my1);
if (picked.length > 0) {
int c = randomColor();
println("Balls intersecting with marquee selection ");
for (Picked p : picked) {
p.shape.fill(c);
println(p.shape.tag);
}
println("---------------------------------");
}
mvisible = false;
}
void keyReleased() {
if (key == 'r') {
for (int i = 0; i < ball.length; i++) {
ball[i].fill(bcolor[i]);
}
}
}
int randomColor() {
return color(random(100, 220), random(100, 220), random(100, 220));
}