-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
382 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Oops, something went wrong.