aboutsummaryrefslogtreecommitdiffstats
path: root/demos/MiscDemos/jahuwaldt/gl/VirtualSphere.java
diff options
context:
space:
mode:
Diffstat (limited to 'demos/MiscDemos/jahuwaldt/gl/VirtualSphere.java')
-rw-r--r--demos/MiscDemos/jahuwaldt/gl/VirtualSphere.java255
1 files changed, 255 insertions, 0 deletions
diff --git a/demos/MiscDemos/jahuwaldt/gl/VirtualSphere.java b/demos/MiscDemos/jahuwaldt/gl/VirtualSphere.java
new file mode 100644
index 0000000..2ec7917
--- /dev/null
+++ b/demos/MiscDemos/jahuwaldt/gl/VirtualSphere.java
@@ -0,0 +1,255 @@
+/*
+* VirtualSphere -- A class that implements the Virtual Sphere algorithm for 3D rotation.
+*
+* Copyright (C) 2001 by Joseph A. Huwaldt <[email protected]>.
+* All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Library General Public
+* License as published by the Free Software Foundation; either
+* version 2 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Library General Public License for more details.
+**/
+
+package jahuwaldt.gl;
+
+
+import java.awt.*;
+
+
+/**
+* Implements the Virtual Sphere algorithm for 3D rotation using a 2D input device.
+* See paper "A Study in Interactive 3-D Rotation Using 2-D Control Devices" by
+* Michael Chen, S. Joy Mountford and Abigail Sellen published in the ACM Siggraph '88
+* proceedings (Volume 22, Number 4, August 1988) for more detail. The code here
+* provides a much simpler implementation than that described in the paper.
+* This is also known by as the "virtual track ball" or "cue ball" interface.
+*
+* <p> Ported from C to Java by Joseph A. Huwaldt, February 19, 2001 </p>
+* <p> Original C version had the following comments:
+* Author: Michael Chen, Human Interface Group / ATG,
+* Copyright � 1991-1993 Apple Computer, Inc. All rights reserved.
+* Part of Virtual Sphere Sample Code Release v1.1.
+* </p>
+*
+* <p> Modified by: Joseph A.Huwaldt </p>
+*
+* @author: Joseph A. Huwaldt Date: Feburary 19, 2001
+* @version February 20, 2001
+**/
+public class VirtualSphere {
+
+ // Some constants for convenience.
+ private static final int X = 0;
+ private static final int Y = 1;
+ private static final int Z = 2;
+
+ /**
+ * Storage for 3D point information passed between methods.
+ **/
+ private final float[] op = new float[3], oq = new float[3], a = new float[3];
+
+ /**
+ * Calculate a rotation matrix based on the axis and angle of rotation
+ * from the last 2 locations of the mouse relative to the Virtual
+ * Sphere cue circle.
+ *
+ * @param pnt1 The 1st mouse location in the window.
+ * @param pnt2 The 2nd mouse location in the window.
+ * @param cueCenter The center of the virtual sphere in the window.
+ * @param cueRadius The radius of the virtual sphere.
+ * @param rotMatrix Preallocated rotation matrix to be filled in
+ * by this method. Must have 16 floating point
+ * elements. This matrix will be overwritten by
+ * this method.
+ * @return A reference to the input rotMatrix is returned with the elements filled in.
+ **/
+ public float[] makeRotationMtx(Point pnt1, Point pnt2, Point cueCenter, int cueRadius,
+ float[] rotMatrix) {
+
+ // Vectors op and oq are defined as class variables to avoid wastefull memory allocations.
+
+ // Project mouse points to 3-D points on the +z hemisphere of a unit sphere.
+ pointOnUnitSphere (pnt1, cueCenter, cueRadius, op);
+ pointOnUnitSphere (pnt2, cueCenter, cueRadius, oq);
+
+ /* Consider the two projected points as vectors from the center of the
+ * unit sphere. Compute the rotation matrix that will transform vector
+ * op to oq. */
+ setRotationMatrix(rotMatrix, op, oq);
+
+ return rotMatrix;
+ }
+
+
+ /**
+ * Draw a 2D representation of the virtual sphere to the specified graphics context.
+ * The representation includes a circle for the perimiter of the virtual sphere
+ * and a cross-hair indicating where the mouse pointer is over the virtual sphere.
+ *
+ * @param gc The graphics context to be drawn into.
+ * @param mousePnt The location in window of the mouse.
+ * @param cueCenter The location in the window of the center of the virtual sphere.
+ * @param cueRadius The radius (in pixels) of the virtual sphere in the window.
+ **/
+ public void draw(Graphics gc, Point mousePnt, Point cueCenter, int cueRadius) {
+
+ // Draw the outline of the virtual sphere.
+ int x = cueCenter.x - cueRadius;
+ int y = cueCenter.y - cueRadius;
+ int diameter = 2*cueRadius;
+ gc.drawOval(x, y, diameter, diameter);
+
+ int mouseX = mousePnt.x - cueCenter.x;
+ int mouseY = cueCenter.y - mousePnt.y;
+ int mouseX2 = mouseX*mouseX;
+ int mouseY2 = mouseY*mouseY;
+ int cueRadius2 = cueRadius*cueRadius;
+
+ // Draw the mouse cue if we are over the sphere.
+ if (mouseX2 + mouseY2 < cueRadius2) {
+
+ // Draw the vertical cue line.
+ if (Math.abs(mouseX) > 4) {
+ // Draw a vertical elliptical arc through the mouse point.
+ double a = Math.sqrt(mouseX2/(1 - mouseY2/(float)cueRadius2));
+ double newMouseX = mouseX*cueRadius/a;
+ int angle = (int)(Math.atan2(mouseY, newMouseX)*180/Math.PI);
+ int width = (int)(2*a);
+ int nx = cueCenter.x - (int)a;
+ gc.drawArc(nx, y, width, diameter, angle - 10, 20);
+
+ } else {
+ // Mouse X near zero is a special case, just draw a vertical line.
+ float vy = mouseY/(float)cueRadius;
+ float vy2 = vy*vy;
+ double vz = Math.sqrt(1 - vy2);
+ double angle = Math.atan2(vy, vz) - 10*Math.PI/180;
+ double length = Math.sqrt(vy2 + vz*vz)*cueRadius;
+ int yl = cueCenter.y - (int)(length*Math.sin(angle));
+ int yh = cueCenter.y - (int)(length*Math.sin(angle + 20*Math.PI/180));
+ gc.drawLine(mousePnt.x, yl, mousePnt.x, yh);
+ }
+
+ // Draw the horizontal cue line.
+ if (Math.abs(mouseY) > 4) {
+ // Draw a horizontal elliptical arc through the mouse point.
+ double a = Math.sqrt(mouseY2/(1 - mouseX2/(float)cueRadius2));
+ double newMouseY = mouseY*cueRadius/a;
+ int angle = (int)(Math.atan2(newMouseY, mouseX)*180/Math.PI);
+ int width = (int)(2*a);
+ y = cueCenter.y - (int)a;
+ gc.drawArc(x, y, diameter, width, angle - 10, 20);
+
+ } else {
+ // Mouse Y near zero is a special case, just draw a horizontal line.
+ float vx = mouseX/(float)cueRadius;
+ float vx2 = vx*vx;
+ double vz = Math.sqrt(1 - vx2);
+ double angle = Math.atan2(vx, vz) - 10*Math.PI/180;
+ double length = Math.sqrt(vx2 + vz*vz)*cueRadius;
+ int xl = cueCenter.x + (int)(length*Math.sin(angle));
+ int xh = cueCenter.x + (int)(length*Math.sin(angle + 20*Math.PI/180));
+ gc.drawLine(xl, mousePnt.y, xh, mousePnt.y);
+ }
+ }
+ }
+
+ /**
+ * Project a 2D point on a circle to a 3D point on the +z hemisphere of a unit sphere.
+ * If the 2D point is outside the circle, it is first mapped to the nearest point on
+ * the circle before projection.
+ * Orthographic projection is used, though technically the field of view of the camera
+ * should be taken into account. However, the discrepancy is neglegible.
+ *
+ * @param p Window point to be projected onto the sphere.
+ * @param cueCenter Location of center of virtual sphere in window.
+ * @param cueRadius The radius of the virtual sphere.
+ * @param v Storage for the 3D projected point created by this method.
+ **/
+ private static void pointOnUnitSphere(Point p, Point cueCenter, int cueRadius, float[] v) {
+
+ /* Turn the mouse points into vectors relative to the center of the circle
+ * and normalize them. Note we need to flip the y value since the 3D coordinate
+ * has positive y going up. */
+ float vx = (p.x - cueCenter.x)/(float)cueRadius;
+ float vy = (cueCenter.y - p.y)/(float)cueRadius;
+ float lengthSqared = vx*vx + vy*vy;
+
+ /* Project the point onto the sphere, assuming orthographic projection.
+ * Points beyond the virtual sphere are normalized onto
+ * edge of the sphere (where z = 0). */
+ float vz = 0;
+ if (lengthSqared < 1)
+ vz = (float)Math.sqrt(1.0 - lengthSqared);
+
+ else {
+ float length = (float)Math.sqrt(lengthSqared);
+ vx /= length;
+ vy /= length;
+ }
+
+ v[X] = vx;
+ v[Y] = vy;
+ v[Z] = vz;
+ }
+
+ /**
+ * Computes a rotation matrix that would map (rotate) vectors op onto oq.
+ * The rotation is about an axis perpendicular to op and oq.
+ * Note this routine won't work if op or oq are zero vectors, or if they
+ * are parallel or antiparallel to each other.
+ *
+ * <p> Modification of Michael Pique's formula in
+ * Graphics Gems Vol. 1. Andrew Glassner, Ed. Addison-Wesley. </p>
+ *
+ * @param rotationMatrix The 16 element rotation matrix to be filled in.
+ * @param op The 1st 3D vector.
+ * @param oq The 2nd 3D vector.
+ **/
+ private void setRotationMatrix(float[] rotationMatrix, float[] op, float[] oq) {
+
+ // Vector a is defined as a class variable to avoid wastefull memory allocations.
+
+ GLTools.crossProduct3D(op, oq, a);
+ float s = GLTools.length3D(a);
+ float c = GLTools.dotProduct3D(op, oq);
+ float t = 1 - c;
+
+ float ax = a[X];
+ float ay = a[Y];
+ float az = a[Z];
+ if (s > 0) {
+ ax /= s;
+ ay /= s;
+ az /= s;
+ }
+
+ float tax = t*ax;
+ float taxay = tax*ay, taxaz = tax*az;
+ float saz = s*az, say = s*ay;
+ rotationMatrix[0] = tax*ax + c;
+ rotationMatrix[1] = taxay + saz;
+ rotationMatrix[2] = taxaz - say;
+
+ float tay = t*ay;
+ float tayaz = tay*az;
+ float sax = s*ax;
+ rotationMatrix[4] = taxay - saz;
+ rotationMatrix[5] = tay*ay + c;
+ rotationMatrix[6] = tayaz + sax;
+
+ rotationMatrix[8] = taxaz + say;
+ rotationMatrix[9] = tayaz - sax;
+ rotationMatrix[10] = t*az*az + c;
+
+ rotationMatrix[3] = rotationMatrix[7] = rotationMatrix[11] =
+ rotationMatrix[12] = rotationMatrix[13] = rotationMatrix[14] = 0;
+ rotationMatrix[15] = 1;
+ }
+}
+