Skip to content

Instantly share code, notes, and snippets.

@vilmosioo
Last active November 27, 2017 11:13
Show Gist options
  • Save vilmosioo/5318327 to your computer and use it in GitHub Desktop.
Save vilmosioo/5318327 to your computer and use it in GitHub Desktop.
Performing the ArcBall rotation effect. This example code is written in Java and used with OpenGL but it can be adapted to other technologies. Many thanks to the great developers that helped me use this. The idea of the ArcBall rotation and how to implement it are NOT mine but it took some time to integrated with my application. I hope this gist…
public class ArcBall {
private static final float Epsilon = 1.0e-5f;
Vector3f StVec; // Saved click vector
Vector3f EnVec; // Saved drag vector
float adjustWidth; // Mouse bounds width
float adjustHeight; // Mouse bounds height
public ArcBall(float NewWidth, float NewHeight) {
StVec = new Vector3f();
EnVec = new Vector3f();
setBounds(NewWidth, NewHeight);
}
public void mapToSphere(Point point, Vector3f vector) {
// Copy paramter into temp point
Point2f tempPoint = new Point2f(point.x, point.y);
// Adjust point coords and scale down to range of [-1 ... 1]
tempPoint.x = (tempPoint.x * this.adjustWidth) - 1.0f;
tempPoint.y = 1.0f - (tempPoint.y * this.adjustHeight);
// Compute the square of the length of the vector to the point from the
// center
float length = (tempPoint.x * tempPoint.x) + (tempPoint.y * tempPoint.y);
// If the point is mapped outside of the sphere... (length > radius
// squared)
if (length > 1.0f) {
// Compute a normalizing factor (radius / sqrt(length))
float norm = (float) (1.0 / Math.sqrt(length));
// Return the "normalized" vector, a point on the sphere
vector.x = tempPoint.x * norm;
vector.y = tempPoint.y * norm;
vector.z = 0.0f;
} else // Else it's on the inside
{
// Return a vector to a point mapped inside the sphere sqrt(radius
// squared - length)
vector.x = tempPoint.x;
vector.y = tempPoint.y;
vector.z = (float) Math.sqrt(1.0f - length);
}
}
public void setBounds(float NewWidth, float NewHeight) {
assert ((NewWidth > 1.0f) && (NewHeight > 1.0f));
// Set adjustment factor for width/height
adjustWidth = 1.0f / ((NewWidth - 1.0f) * 0.5f);
adjustHeight = 1.0f / ((NewHeight - 1.0f) * 0.5f);
}
// Mouse down
public void click(Point NewPt) {
mapToSphere(NewPt, this.StVec);
}
// Mouse drag, calculate rotation
public void drag(Point NewPt, Quat4f NewRot) {
// Map the point to the sphere
this.mapToSphere(NewPt, EnVec);
// Return the quaternion equivalent to the rotation
if (NewRot != null) {
Vector3f Perp = new Vector3f();
// Compute the vector perpendicular to the begin and end vectors
Vector3f.cross(Perp, StVec, EnVec);
// Compute the length of the perpendicular vector
if (Perp.length() > Epsilon) // if its non-zero
{
// We're ok, so return the perpendicular vector as the transform
// after all
NewRot.x = Perp.x;
NewRot.y = Perp.y;
NewRot.z = Perp.z;
// In the quaternion values, w is cosine (theta / 2), where
// theta is rotation angle
NewRot.w = Vector3f.dot(StVec, EnVec);
} else // if its zero
{
// The begin and end vectors coincide, so return an identity
// transform
NewRot.x = NewRot.y = NewRot.z = NewRot.w = 0.0f;
}
}
}
}
public class Matrix4f {
float M00;
float M10;
float M20;
float M30;
float M01;
float M11;
float M21;
float M31;
float M02;
float M12;
float M22;
float M32;
float M03;
float M13;
float M23;
float M33;
public Matrix4f() {
setIdentity();
}
public void get(float[] dest) {
dest[0] = M00;
dest[1] = M10;
dest[2] = M20;
dest[3] = M30;
dest[4] = M01;
dest[5] = M11;
dest[6] = M21;
dest[7] = M31;
dest[8] = M02;
dest[9] = M12;
dest[10] = M22;
dest[11] = M32;
dest[12] = M03;
dest[13] = M13;
dest[14] = M23;
dest[15] = M33;
}
public void setZero() {
M00 = M01 = M02 = M03 = M10 = M11 = M12 = M13 = M20 = M21 = M22 = M23 = M30 = M31 = M32 = M33 = 0.0f;
}
public void setIdentity() {
setZero();
M00 = M11 = M22 = M33 = 1.0f;
}
public void setRotation(Quat4f q1) {
float n, s;
float xs, ys, zs;
float wx, wy, wz;
float xx, xy, xz;
float yy, yz, zz;
n = (q1.x * q1.x) + (q1.y * q1.y) + (q1.z * q1.z) + (q1.w * q1.w);
s = (n > 0.0f) ? (2.0f / n) : 0.0f;
xs = q1.x * s;
ys = q1.y * s;
zs = q1.z * s;
wx = q1.w * xs;
wy = q1.w * ys;
wz = q1.w * zs;
xx = q1.x * xs;
xy = q1.x * ys;
xz = q1.x * zs;
yy = q1.y * ys;
yz = q1.y * zs;
zz = q1.z * zs;
M00 = 1.0f - (yy + zz);
M01 = xy - wz;
M02 = xz + wy;
M03 = 0f;
M10 = xy + wz;
M11 = 1.0f - (xx + zz);
M12 = yz - wx;
M13 = 0f;
M20 = xz - wy;
M21 = yz + wx;
M22 = 1.0f - (xx + yy);
M23 = 0f;
M30 = 0f;
M31 = 0f;
M32 = 0f;
M33 = 1f;
}
public final void set(Matrix4f m1) {
M00 = m1.M00;
M01 = m1.M01;
M02 = m1.M02;
M03 = m1.M03;
M10 = m1.M10;
M11 = m1.M11;
M12 = m1.M12;
M13 = m1.M13;
M20 = m1.M20;
M21 = m1.M21;
M22 = m1.M22;
M23 = m1.M23;
M30 = m1.M30;
M31 = m1.M31;
M32 = m1.M32;
M33 = m1.M33;
}
/**
* Sets the value of this matrix to the result of multiplying the two
* argument matrices together.
*
* @param m1
* the first matrix
* @param m2
* the second matrix
*/
public final void mul(Matrix4f m1, Matrix4f m2) {
// alias-safe way.
set(m1.M00 * m2.M00 + m1.M01 * m2.M10 + m1.M02 * m2.M20 + m1.M03 * m2.M30, m1.M00 * m2.M01 + m1.M01 * m2.M11 + m1.M02 * m2.M21 + m1.M03 * m2.M31, m1.M00 * m2.M02 + m1.M01 * m2.M12 + m1.M02 * m2.M22 + m1.M03 * m2.M32, m1.M00 * m2.M03 + m1.M01 * m2.M13 + m1.M02 * m2.M23 + m1.M03 * m2.M33,
m1.M10 * m2.M00 + m1.M11 * m2.M10 + m1.M12 * m2.M20 + m1.M13 * m2.M30, m1.M10 * m2.M01 + m1.M11 * m2.M11 + m1.M12 * m2.M21 + m1.M13 * m2.M31, m1.M10 * m2.M02 + m1.M11 * m2.M12 + m1.M12 * m2.M22 + m1.M13 * m2.M32, m1.M10 * m2.M03 + m1.M11 * m2.M13 + m1.M12 * m2.M23 + m1.M13 * m2.M33,
m1.M20 * m2.M00 + m1.M21 * m2.M10 + m1.M22 * m2.M20 + m1.M23 * m2.M30, m1.M20 * m2.M01 + m1.M21 * m2.M11 + m1.M22 * m2.M21 + m1.M23 * m2.M31, m1.M20 * m2.M02 + m1.M21 * m2.M12 + m1.M22 * m2.M22 + m1.M23 * m2.M32, m1.M20 * m2.M03 + m1.M21 * m2.M13 + m1.M22 * m2.M23 + m1.M23 * m2.M33,
m1.M30 * m2.M00 + m1.M31 * m2.M10 + m1.M32 * m2.M20 + m1.M33 * m2.M30, m1.M30 * m2.M01 + m1.M31 * m2.M11 + m1.M32 * m2.M21 + m1.M33 * m2.M31, m1.M30 * m2.M02 + m1.M31 * m2.M12 + m1.M32 * m2.M22 + m1.M33 * m2.M32, m1.M30 * m2.M03 + m1.M31 * m2.M13 + m1.M32 * m2.M23 + m1.M33 * m2.M33);
}
/**
* Sets 16 values
*
* @param m00
* @param m01
* @param m02
* @param m03
* @param m10
* @param m11
* @param m12
* @param m13
* @param m20
* @param m21
* @param m22
* @param m23
* @param m30
* @param m31
* @param m32
* @param m33
*/
public void set(float m00, float m01, float m02, float m03, float m10, float m11, float m12, float m13, float m20, float m21, float m22, float m23, float m30, float m31, float m32, float m33) {
this.M00 = m00;
this.M01 = m01;
this.M02 = m02;
this.M03 = m03;
this.M10 = m10;
this.M11 = m11;
this.M12 = m12;
this.M13 = m13;
this.M20 = m20;
this.M21 = m21;
this.M22 = m22;
this.M23 = m23;
this.M30 = m30;
this.M31 = m31;
this.M32 = m32;
this.M33 = m33;
}
}
public class Point2f {
public float x, y;
public Point2f(float x, float y) {
this.x = x;
this.y = y;
}
}
public class Quat4f {
public float x, y, z, w;
public Quat4f() {
this(0, 0, 0, 0);
}
public Quat4f(double angle, float x, float y, float z) {
double s = Math.sqrt(x * x + y * y + z * z);
double c = 1.0 / s;
x *= c;
y *= c;
z *= c;
float omega = (float) (-0.5f * angle);
s = (float) Math.sin(omega);
this.x = (float) (s * x);
this.y = (float) (s * y);
this.z = (float) (s * z);
this.w = (float) Math.cos(omega);
}
}
public class Vector3f {
public float x, y, z;
public static void cross(Vector3f Result, Vector3f v1, Vector3f v2) {
Result.x = (v1.y * v2.z) - (v1.z * v2.y);
Result.y = (v1.z * v2.x) - (v1.x * v2.z);
Result.z = (v1.x * v2.y) - (v1.y * v2.x);
}
public static float dot(Vector3f v1, Vector3f v2) {
return (v1.x * v2.x) + (v1.y * v2.y) + (v1.z + v2.z);
}
public float length() {
return (float) Math.sqrt(x * x + y * y + z * z);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment