# TrackBall: rotating 3D objects by dragging mouse

I wrote this today for a project I’m working on. Maybe useful for someone?

The idea is to drag objects in 3D to rotate them.

(watch video)

``````// A collection of trackballs. They can be rotated and
// they keep the rotation info.
ArrayList<TrackBall> tbs = new ArrayList<TrackBall>();

// Which of the objects is closer to the mouse, to rotate that one
TrackBall closest = null;

void setup() {
size(600, 600, P3D);
noStroke();

// Create a bunch of TrackBalls
for (int i=0; i<30; i++) {
TrackBall tb = new TrackBall();
tb.setPos(random(600), random(600));
}
}

void draw() {
background(#220A27);
lights();

// Draw cubes using the rotation information stored
// in the trackballs
for (int i=0; i<tbs.size(); i++) {
TrackBall tb = tbs.get(i);
pushMatrix();
translate(tb.pos.x, tb.pos.y);
applyMatrix(tb.rotationMat);
fill(lerpColor(#0FB5FF, #E8E37C, i / (float)tbs.size()));
box(50 + 20 * (i%5));
popMatrix();
}
}

// When pressing the mouse find which TrackBall is closer to
// the mouse
void mousePressed() {
closest = null;
float distance = 999999;
for (TrackBall tb : tbs) {
float d = dist(mouseX, mouseY, tb.pos.x, tb.pos.y);
if(d < distance) {
distance = d;
closest = tb;
}
}
}

// When dragging the mouse tell about the dragging to
// the closest TrackBall, so it updates its internal
// rotation information.
void mouseDragged() {
if(closest != null) {
closest.drag(pmouseX, pmouseY, mouseX, mouseY);
}
}

// Inspired by
// https://stackoverflow.com/questions/11314384/virtual-trackball-implementation
// Using Toxiclibs for the Quaternion.
import toxi.geom.*;

// This TrackBall class allows rotating 3D objects by dragging.

class TrackBall {
public PMatrix3D rotationMat;
public Vec2D pos = new Vec2D();

private int frameCountDragging;
private Quaternion rotationQuat, tempQuat;

TrackBall() {
rotationQuat = new Quaternion();
tempQuat = new Quaternion();
rotationMat = new PMatrix3D();
}

// Specify the 2D coordinates of the object in the screen
// This could be upgraded to 3D eventually.
public void setPos(float x, float y) {
pos.set(x, y);
}

// Call this method when dragging the mouse
// with previous and current mouse position
public void drag(int x0, int y0, int x1, int y1) {
Vec3D v2 = project(x0 - pos.x, y0 - pos.y);
Vec3D v1 = project(x1 - pos.x, y1 - pos.y);

float dt = constrain(v1.distanceTo(v2) / width, -1, 1);

Vec3D n = v2.cross(v1).normalizeTo(dt);

if (frameCountDragging++ == 50) {
frameCountDragging = 0;
rotationQuat.normalize();
}

rotationQuat = tempQuat
.set(cos(asin(dt)), n)
.multiply(rotationQuat);
rotationMat.set(rotationQuat.toMatrix4x4().toFloatArray(null));
}

private Vec3D project (float x, float y) {
float radius = width / 2;

Vec3D v = new Vec3D(x, y, 0);

float len = v.magnitude();
float tr = radius / sqrt(2);

v.z = len < tr ? sqrt(radius * radius - len * len) : tr * tr / len;

return v;
}
}
``````

I’m sure it can be improved. I leave that as an exercise for the reader

2 Likes

Very nice! Thanks so much for sharing.

My first thought is that this TrackBall rotation method could probably be combined with the Picking Library – specifically the SimpleCubes example – to replace this:

``````  float distance = 999999;
for (TrackBall tb : tbs) {
float d = dist(mouseX, mouseY, tb.pos.x, tb.pos.y);
if(d < distance) {
distance = d;
closest = tb;
}
}
``````
1 Like