Skip to content

Commit

Permalink
Merge branch 'arcBallIntegration'
Browse files Browse the repository at this point in the history
  • Loading branch information
kellyegan committed Aug 12, 2014
2 parents 77d1302 + 0c76dca commit c2f264b
Show file tree
Hide file tree
Showing 3 changed files with 382 additions and 37 deletions.
155 changes: 155 additions & 0 deletions ArcBall.pde
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@

import draw3D.geo.Quaternion;
import processing.core.*;

/**
* ArcBall is an interface for rotate 3D objects. It allows the rotation of a 3D
* object from a 2D input by simulating touching a sphere (arcball) that can be
* spun in any direction.
*
* @author Kelly Egan
*
*/
public class ArcBall implements PConstants{

/**
* Constants for calling specific views when using setViews
*/
public final Quaternion FRONT = new Quaternion(1.0f, 0.0f, 0.0f, 0.0f);
public final Quaternion TOP = new Quaternion(-PApplet.sqrt(2.0f)/2.0f, PApplet.sqrt(2.0f)/2.0f, 0, 0);
public final Quaternion LEFT = new Quaternion(PApplet.sqrt(2.0f)/2.0f, 0, PApplet.sqrt(2.0f)/2.0f, 0);
public final Quaternion RIGHT = new Quaternion(-PApplet.sqrt(2.0f) / 2.0f, 0f, PApplet.sqrt(2.0f) / 2.0f, 0f);
public final Quaternion BOTTOM = new Quaternion(PApplet.sqrt(2.0f)/2.0f, PApplet.sqrt(2.0f)/2.0f, 0, 0);
public final Quaternion BACK = new Quaternion(0.0f, 0.0f, 1.0f, 0.0f);

private PApplet p;

//Center position and radius of ArcBall
private PVector center;
private float radius;

//Start and end point on ArcBall surface of rotation
private PVector startPoint, endPoint;

private boolean dragging;

public Quaternion currentRotation, startRotation, deltaRotation;
public Quaternion quaternion;

/**
* Construct an ArcBall given its center position and radius
*
* @param p parent PApplet
* @param center_x x coordinate of the center of ArcBall
* @param center_y y coordinate of the center of the ArcBall
* @param radius radius of the ArcBall
*/
public ArcBall(PApplet p, float center_x, float center_y, float radius){
this.p = p;

center = new PVector(center_x, center_y);

this.radius = radius;

startPoint = new PVector();
endPoint = new PVector();

quaternion = new Quaternion();
currentRotation = new Quaternion();
startRotation = new Quaternion();
deltaRotation = new Quaternion();


}

/**
* Called when dragging of the ArcBall begins.
* @param x screen position on x axis
* @param y screen position on y axis
*/
public void dragStart(float x, float y){
startPoint = screenToArcball(x, y);
startRotation.set(currentRotation);
deltaRotation.setToIdentity();
dragging = true;
}

/**
* Called during dragging of ArcBall
* @param x
* @param y
*/
public void dragging(float x, float y){
endPoint = screenToArcball(x, y);
quaternion.rotationBetween(startPoint, endPoint, deltaRotation);
}

/**
* Called when draggin ends
*/
public void dragEnd() {
dragging = false;
}



/**
* Update currentRotation with data from dragging
*/
public void update(){
// if( dragging ) {
currentRotation = quaternion.mult(deltaRotation, startRotation);
// } else {
// Quaternion.mult(deltaRotation, currentRotation, currentRotation);
// deltaRotation = deltaRotation.power(0.8f);
// }
applyRotation(currentRotation);
}

/**
* Project screen coordinates onto ArcBall sphere. If coordinate
* is outside of the sphere treat as edge of sphere.
*
* @param x
* @param y
* @return
*/
public PVector screenToArcball(float x, float y){
PVector result = new PVector();
result.x = (x - center.x) / radius;
result.y = (y - center.y) / radius;

float mag = result.magSq();

if (mag > 1.0f){
result.normalize();
} else {
result.z = PApplet.sqrt(1.0f - mag);
}

return result;
}

/**
* Set the view to one of six stand views
*
* @param viewIndex
*/
public void setView( Quaternion view ) {
startRotation.set(view);
deltaRotation.setToIdentity();
}

public void applyRotation(Quaternion q){
float[] axisAngle = q.toAngleAxis();
p.rotate(axisAngle[0], axisAngle[1], axisAngle[2], axisAngle[3]);
}

public float[] getRotation() {
return currentRotation.toAngleAxis();
}

public float[] getInverseRotation() {
return currentRotation.inverse().toAngleAxis();
}
}
149 changes: 149 additions & 0 deletions Quaternion.pde
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@

import processing.core.*;

public class Quaternion implements PConstants {
public float w, x, y, z;

/**
* Contructs a new Quaternion and it set to the identity
*/
public Quaternion() {
setToIdentity();
}

public Quaternion(float w, float x, float y, float z) {
set(w, x, y, z);
}

/**
* Set the value of Quaternions components.
*
* @param w scalar component
* @param x vector components x value
* @param y vector components y value
* @param z vector components z value
*/
public void set(float w, float x, float y, float z) {
this.w = w;
this.x = x;
this.y = y;
this.z = z;
}

/**
* Set the value of a Quaternions components with the values of another Quaternion
* @param q
*/
public void set(Quaternion q) {
this.set(q.w, q.x, q.y, q.z);
}

public void set(float s, PVector v) {
set(s, v.x, v.y, v.z);
}

/**
* Sets Quaternion to the identity Quaternion
*/
public void setToIdentity() {
set( 1.0f, 0.0f, 0.0f, 0.0f );
}

public void mult(Quaternion q1, Quaternion q2, Quaternion target) {
float s = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z;
float vx = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y;
float vy = q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z;
float vz = q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x;
target.set(s, vx, vy, vz);
}

public Quaternion mult(Quaternion q1, Quaternion q2) {
Quaternion result = new Quaternion();
this.mult(q1, q2, result);
return result;
}

public Quaternion mult(float s) {
return new Quaternion(w*s, x*s, y*s, z*s);
}

public void inverse(Quaternion source, Quaternion target) {
target.set( source.w, -source.x, -source.y, -source.z );
}

public Quaternion inverse() {
return new Quaternion(this.w, -this.x, -this.y, -this.z);
}

/**
* Computes the power of the quaternion, defined as follows:
* let Q=(cos(alpha), sin(alpha) u),
* then Q^t = (cos(t*alpha), sin(t*alpha)u).
*/
public Quaternion power(float exponent) {
float theta2 = PApplet.acos(w);
theta2 *= exponent;
PVector U = new PVector(x,y,z);
U.normalize();
U.mult(PApplet.sin(theta2));
return new Quaternion(PApplet.cos(theta2),U.x, U.y, U.z);
}

public String toString() {
return "(" + w + ", " + x + ", " + y + ", " + z + ")";
}

/**
* Set target Quaternion to the rotation between two unit vectors (shortest arc)
*
* @param start normalized starting point on rotation
* @param end normalized ending point of rotation
*/
public void rotationBetween( PVector start, PVector end, Quaternion target ) {
float dot = PVector.dot(start, end);

if( dot < -0.999999) {
//Start and end vectors are opposites rotation should be 180 degrees
PVector xAxis = new PVector(1, 0, 0);
PVector cross = xAxis.cross(start);
//If cross product is zero rotate around Y instead of x
if( cross.mag() > 0.000001) {
target.set(0.0f, 1.0f, 0.0f, 0.0f); //180 degree rotation around the x-axis
} else {
target.set(0.0f, 0.0f, 1.0f, 0.0f); //180 degree rotation around y-axis
}

} else if ( dot > 0.999999) {
//Start and end vectors are the same rotation should do nothing
target.setToIdentity();
} else {
//Start and end vectors are not identical or opposite
target.set( dot, start.cross(end) );
}
}

public void fromAngleAxis( float angle, PVector axis, Quaternion target) {
float s = PApplet.sin(angle/2);
target.set(PApplet.cos(angle/2), axis.x * s, axis.y * s, axis.z * s);
}

/**
* Convert Quaternion to an angle and Axis pair
* @return float array whos first value is angle and remaining values represent the axis
*/
public float[] toAngleAxis() {
float[] result = new float[4];

float sa = (float) Math.sqrt(1.0f - w * w);
if (sa < EPSILON) {
sa = 1.0f;
}

result[0] = (float) Math.acos(w) * 2.0f;
result[1] = x / sa;
result[2] = y / sa;
result[3] = z / sa;

return result;
}
}
Loading

0 comments on commit c2f264b

Please sign in to comment.