Skip to content

Commit c2f264b

Browse files
committed
Merge branch 'arcBallIntegration'
2 parents 77d1302 + 0c76dca commit c2f264b

File tree

3 files changed

+382
-37
lines changed

3 files changed

+382
-37
lines changed

ArcBall.pde

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
2+
import draw3D.geo.Quaternion;
3+
import processing.core.*;
4+
5+
/**
6+
* ArcBall is an interface for rotate 3D objects. It allows the rotation of a 3D
7+
* object from a 2D input by simulating touching a sphere (arcball) that can be
8+
* spun in any direction.
9+
*
10+
* @author Kelly Egan
11+
*
12+
*/
13+
public class ArcBall implements PConstants{
14+
15+
/**
16+
* Constants for calling specific views when using setViews
17+
*/
18+
public final Quaternion FRONT = new Quaternion(1.0f, 0.0f, 0.0f, 0.0f);
19+
public final Quaternion TOP = new Quaternion(-PApplet.sqrt(2.0f)/2.0f, PApplet.sqrt(2.0f)/2.0f, 0, 0);
20+
public final Quaternion LEFT = new Quaternion(PApplet.sqrt(2.0f)/2.0f, 0, PApplet.sqrt(2.0f)/2.0f, 0);
21+
public final Quaternion RIGHT = new Quaternion(-PApplet.sqrt(2.0f) / 2.0f, 0f, PApplet.sqrt(2.0f) / 2.0f, 0f);
22+
public final Quaternion BOTTOM = new Quaternion(PApplet.sqrt(2.0f)/2.0f, PApplet.sqrt(2.0f)/2.0f, 0, 0);
23+
public final Quaternion BACK = new Quaternion(0.0f, 0.0f, 1.0f, 0.0f);
24+
25+
private PApplet p;
26+
27+
//Center position and radius of ArcBall
28+
private PVector center;
29+
private float radius;
30+
31+
//Start and end point on ArcBall surface of rotation
32+
private PVector startPoint, endPoint;
33+
34+
private boolean dragging;
35+
36+
public Quaternion currentRotation, startRotation, deltaRotation;
37+
public Quaternion quaternion;
38+
39+
/**
40+
* Construct an ArcBall given its center position and radius
41+
*
42+
* @param p parent PApplet
43+
* @param center_x x coordinate of the center of ArcBall
44+
* @param center_y y coordinate of the center of the ArcBall
45+
* @param radius radius of the ArcBall
46+
*/
47+
public ArcBall(PApplet p, float center_x, float center_y, float radius){
48+
this.p = p;
49+
50+
center = new PVector(center_x, center_y);
51+
52+
this.radius = radius;
53+
54+
startPoint = new PVector();
55+
endPoint = new PVector();
56+
57+
quaternion = new Quaternion();
58+
currentRotation = new Quaternion();
59+
startRotation = new Quaternion();
60+
deltaRotation = new Quaternion();
61+
62+
63+
}
64+
65+
/**
66+
* Called when dragging of the ArcBall begins.
67+
* @param x screen position on x axis
68+
* @param y screen position on y axis
69+
*/
70+
public void dragStart(float x, float y){
71+
startPoint = screenToArcball(x, y);
72+
startRotation.set(currentRotation);
73+
deltaRotation.setToIdentity();
74+
dragging = true;
75+
}
76+
77+
/**
78+
* Called during dragging of ArcBall
79+
* @param x
80+
* @param y
81+
*/
82+
public void dragging(float x, float y){
83+
endPoint = screenToArcball(x, y);
84+
quaternion.rotationBetween(startPoint, endPoint, deltaRotation);
85+
}
86+
87+
/**
88+
* Called when draggin ends
89+
*/
90+
public void dragEnd() {
91+
dragging = false;
92+
}
93+
94+
95+
96+
/**
97+
* Update currentRotation with data from dragging
98+
*/
99+
public void update(){
100+
// if( dragging ) {
101+
currentRotation = quaternion.mult(deltaRotation, startRotation);
102+
// } else {
103+
// Quaternion.mult(deltaRotation, currentRotation, currentRotation);
104+
// deltaRotation = deltaRotation.power(0.8f);
105+
// }
106+
applyRotation(currentRotation);
107+
}
108+
109+
/**
110+
* Project screen coordinates onto ArcBall sphere. If coordinate
111+
* is outside of the sphere treat as edge of sphere.
112+
*
113+
* @param x
114+
* @param y
115+
* @return
116+
*/
117+
public PVector screenToArcball(float x, float y){
118+
PVector result = new PVector();
119+
result.x = (x - center.x) / radius;
120+
result.y = (y - center.y) / radius;
121+
122+
float mag = result.magSq();
123+
124+
if (mag > 1.0f){
125+
result.normalize();
126+
} else {
127+
result.z = PApplet.sqrt(1.0f - mag);
128+
}
129+
130+
return result;
131+
}
132+
133+
/**
134+
* Set the view to one of six stand views
135+
*
136+
* @param viewIndex
137+
*/
138+
public void setView( Quaternion view ) {
139+
startRotation.set(view);
140+
deltaRotation.setToIdentity();
141+
}
142+
143+
public void applyRotation(Quaternion q){
144+
float[] axisAngle = q.toAngleAxis();
145+
p.rotate(axisAngle[0], axisAngle[1], axisAngle[2], axisAngle[3]);
146+
}
147+
148+
public float[] getRotation() {
149+
return currentRotation.toAngleAxis();
150+
}
151+
152+
public float[] getInverseRotation() {
153+
return currentRotation.inverse().toAngleAxis();
154+
}
155+
}

Quaternion.pde

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
2+
import processing.core.*;
3+
4+
public class Quaternion implements PConstants {
5+
public float w, x, y, z;
6+
7+
/**
8+
* Contructs a new Quaternion and it set to the identity
9+
*/
10+
public Quaternion() {
11+
setToIdentity();
12+
}
13+
14+
public Quaternion(float w, float x, float y, float z) {
15+
set(w, x, y, z);
16+
}
17+
18+
/**
19+
* Set the value of Quaternions components.
20+
*
21+
* @param w scalar component
22+
* @param x vector components x value
23+
* @param y vector components y value
24+
* @param z vector components z value
25+
*/
26+
public void set(float w, float x, float y, float z) {
27+
this.w = w;
28+
this.x = x;
29+
this.y = y;
30+
this.z = z;
31+
}
32+
33+
/**
34+
* Set the value of a Quaternions components with the values of another Quaternion
35+
* @param q
36+
*/
37+
public void set(Quaternion q) {
38+
this.set(q.w, q.x, q.y, q.z);
39+
}
40+
41+
public void set(float s, PVector v) {
42+
set(s, v.x, v.y, v.z);
43+
}
44+
45+
/**
46+
* Sets Quaternion to the identity Quaternion
47+
*/
48+
public void setToIdentity() {
49+
set( 1.0f, 0.0f, 0.0f, 0.0f );
50+
}
51+
52+
public void mult(Quaternion q1, Quaternion q2, Quaternion target) {
53+
float s = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z;
54+
float vx = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y;
55+
float vy = q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z;
56+
float vz = q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x;
57+
target.set(s, vx, vy, vz);
58+
}
59+
60+
public Quaternion mult(Quaternion q1, Quaternion q2) {
61+
Quaternion result = new Quaternion();
62+
this.mult(q1, q2, result);
63+
return result;
64+
}
65+
66+
public Quaternion mult(float s) {
67+
return new Quaternion(w*s, x*s, y*s, z*s);
68+
}
69+
70+
public void inverse(Quaternion source, Quaternion target) {
71+
target.set( source.w, -source.x, -source.y, -source.z );
72+
}
73+
74+
public Quaternion inverse() {
75+
return new Quaternion(this.w, -this.x, -this.y, -this.z);
76+
}
77+
78+
/**
79+
* Computes the power of the quaternion, defined as follows:
80+
* let Q=(cos(alpha), sin(alpha) u),
81+
* then Q^t = (cos(t*alpha), sin(t*alpha)u).
82+
*/
83+
public Quaternion power(float exponent) {
84+
float theta2 = PApplet.acos(w);
85+
theta2 *= exponent;
86+
PVector U = new PVector(x,y,z);
87+
U.normalize();
88+
U.mult(PApplet.sin(theta2));
89+
return new Quaternion(PApplet.cos(theta2),U.x, U.y, U.z);
90+
}
91+
92+
public String toString() {
93+
return "(" + w + ", " + x + ", " + y + ", " + z + ")";
94+
}
95+
96+
/**
97+
* Set target Quaternion to the rotation between two unit vectors (shortest arc)
98+
*
99+
* @param start normalized starting point on rotation
100+
* @param end normalized ending point of rotation
101+
*/
102+
public void rotationBetween( PVector start, PVector end, Quaternion target ) {
103+
float dot = PVector.dot(start, end);
104+
105+
if( dot < -0.999999) {
106+
//Start and end vectors are opposites rotation should be 180 degrees
107+
PVector xAxis = new PVector(1, 0, 0);
108+
PVector cross = xAxis.cross(start);
109+
//If cross product is zero rotate around Y instead of x
110+
if( cross.mag() > 0.000001) {
111+
target.set(0.0f, 1.0f, 0.0f, 0.0f); //180 degree rotation around the x-axis
112+
} else {
113+
target.set(0.0f, 0.0f, 1.0f, 0.0f); //180 degree rotation around y-axis
114+
}
115+
116+
} else if ( dot > 0.999999) {
117+
//Start and end vectors are the same rotation should do nothing
118+
target.setToIdentity();
119+
} else {
120+
//Start and end vectors are not identical or opposite
121+
target.set( dot, start.cross(end) );
122+
}
123+
}
124+
125+
public void fromAngleAxis( float angle, PVector axis, Quaternion target) {
126+
float s = PApplet.sin(angle/2);
127+
target.set(PApplet.cos(angle/2), axis.x * s, axis.y * s, axis.z * s);
128+
}
129+
130+
/**
131+
* Convert Quaternion to an angle and Axis pair
132+
* @return float array whos first value is angle and remaining values represent the axis
133+
*/
134+
public float[] toAngleAxis() {
135+
float[] result = new float[4];
136+
137+
float sa = (float) Math.sqrt(1.0f - w * w);
138+
if (sa < EPSILON) {
139+
sa = 1.0f;
140+
}
141+
142+
result[0] = (float) Math.acos(w) * 2.0f;
143+
result[1] = x / sa;
144+
result[2] = y / sa;
145+
result[3] = z / sa;
146+
147+
return result;
148+
}
149+
}

0 commit comments

Comments
 (0)