summaryrefslogtreecommitdiffstats
path: root/plug-ins/map-object/arcball.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--plug-ins/map-object/arcball.c515
1 files changed, 515 insertions, 0 deletions
diff --git a/plug-ins/map-object/arcball.c b/plug-ins/map-object/arcball.c
new file mode 100644
index 0000000..1f509d4
--- /dev/null
+++ b/plug-ins/map-object/arcball.c
@@ -0,0 +1,515 @@
+/************************************/
+/* ArcBall.c (c) Ken Shoemake, 1993 */
+/* Modified by Tom Bech, 1996 */
+/************************************/
+
+#include "config.h"
+
+#include <libgimp/gimp.h>
+
+#include "arcball.h"
+
+/* Global variables */
+/* ================ */
+
+Quat qOne = { 0, 0, 0, 1 };
+
+static HVect center;
+static double radius;
+static Quat qNow, qDown, qDrag;
+static HVect vNow, vDown, vFrom, vTo, vrFrom, vrTo;
+static HMatrix mNow, mDown;
+static unsigned int showResult, dragging;
+static ConstraintSet sets[NSets];
+static int setSizes[NSets];
+static AxisSet axisSet;
+static int axisIndex;
+
+static HMatrix mId =
+{
+ { 1, 0, 0, 0 },
+ { 0, 1, 0, 0 },
+ { 0, 0, 1, 0 },
+ { 0, 0, 0, 1 }
+};
+
+static double otherAxis[][4] =
+{
+ {-0.48, 0.80, 0.36, 1}
+};
+
+/* Internal methods */
+/* ================ */
+
+static void Qt_ToMatrix(Quat q,HMatrix out);
+static Quat Qt_Conj(Quat q);
+static Quat Qt_Mul(Quat qL, Quat qR);
+static Quat Qt_FromBallPoints(HVect from, HVect to);
+static void Qt_ToBallPoints(Quat q, HVect *arcFrom, HVect *arcTo);
+
+static HVect V3_(double x, double y, double z);
+static double V3_Norm(HVect v);
+static HVect V3_Unit(HVect v);
+static HVect V3_Scale(HVect v, double s);
+static HVect V3_Negate(HVect v);
+/*
+static HVect V3_Add(HVect v1, HVect v2);
+*/
+static HVect V3_Sub(HVect v1, HVect v2);
+static double V3_Dot(HVect v1, HVect v2);
+/*
+static HVect V3_Cross(HVect v1, HVect v2);
+static HVect V3_Bisect(HVect v0, HVect v1);
+*/
+
+static HVect MouseOnSphere(HVect mouse, HVect ballCenter, double ballRadius);
+static HVect ConstrainToAxis(HVect loose, HVect axis);
+static int NearestConstraintAxis(HVect loose, HVect *axes, int nAxes);
+
+/* Establish reasonable initial values for controller. */
+/* =================================================== */
+
+void
+ArcBall_Init (void)
+{
+ int i;
+
+ center = qOne;
+ radius = 1.0;
+ vDown = vNow = qOne;
+ qDown = qNow = qOne;
+ for (i=15; i>=0; i--)
+ ((double *)mNow)[i] = ((double *)mDown)[i] = ((double *)mId)[i];
+
+ showResult = dragging = FALSE;
+ axisSet = NoAxes;
+ sets[CameraAxes] = mId[X];
+ setSizes[CameraAxes] = 3;
+ sets[BodyAxes] = mDown[X];
+ setSizes[BodyAxes] = 3;
+ sets[OtherAxes] = otherAxis[X];
+ setSizes[OtherAxes] = 1;
+}
+
+/* Set the center and size of the controller. */
+/* ========================================== */
+
+void
+ArcBall_Place (HVect Center,
+ double Radius)
+{
+ center = Center;
+ radius = Radius;
+}
+
+/* Incorporate new mouse position. */
+/* =============================== */
+
+void
+ArcBall_Mouse (HVect v_Now)
+{
+ vNow = v_Now;
+}
+
+/* Choose a constraint set, or none. */
+/* ================================= */
+
+void
+ArcBall_UseSet (AxisSet axis_Set)
+{
+ if (!dragging) axisSet = axis_Set;
+}
+
+/* Using vDown, vNow, dragging, and axisSet, compute rotation etc. */
+/* =============================================================== */
+
+void
+ArcBall_Update (void)
+{
+ int setSize = setSizes[axisSet];
+ HVect *set = (HVect *)(sets[axisSet]);
+
+ vFrom = MouseOnSphere(vDown, center, radius);
+ vTo = MouseOnSphere(vNow, center, radius);
+ if (dragging)
+ {
+ if (axisSet!=NoAxes)
+ {
+ vFrom = ConstrainToAxis(vFrom, set[axisIndex]);
+ vTo = ConstrainToAxis(vTo, set[axisIndex]);
+ }
+ qDrag = Qt_FromBallPoints(vFrom, vTo);
+ qNow = Qt_Mul(qDrag, qDown);
+ }
+ else
+ {
+ if (axisSet!=NoAxes) axisIndex = NearestConstraintAxis(vTo, set, setSize);
+ }
+ Qt_ToBallPoints(qDown, &vrFrom, &vrTo);
+ Qt_ToMatrix(Qt_Conj(qNow), mNow); /* Gives transpose for GL. */
+}
+
+/* Return rotation matrix defined by controller use. */
+/* ================================================= */
+
+void
+ArcBall_Value (HMatrix m_Now)
+{
+ ArcBall_CopyMat (mNow, m_Now);
+}
+
+/* Extract rotation angles from matrix */
+/* =================================== */
+
+void
+ArcBall_Values (double *alpha,
+ double *beta,
+ double *gamma)
+{
+ if ((*beta=asin(-mNow[0][2]))!=0.0)
+ {
+ *gamma=atan2(mNow[1][2],mNow[2][2]);
+ *alpha=atan2(mNow[0][1],mNow[0][0]);
+ }
+ else
+ {
+ *gamma=atan2(mNow[1][0],mNow[1][1]);
+ *alpha=0.0;
+ }
+}
+
+/* Begin drag sequence. */
+/* ==================== */
+
+void
+ArcBall_BeginDrag (void)
+{
+ dragging = TRUE;
+ vDown = vNow;
+}
+
+/* Stop drag sequence. */
+/* =================== */
+
+void
+ArcBall_EndDrag (void)
+{
+ dragging = FALSE;
+ qDown = qNow;
+
+ ArcBall_CopyMat (mNow, mDown);
+}
+
+/*===================*/
+/***** BallAux.c *****/
+/*===================*/
+
+/* Return quaternion product qL * qR. Note: order is important! */
+/* To combine rotations, use the product Mul(qSecond, qFirst), */
+/* which gives the effect of rotating by qFirst then qSecond. */
+/* ============================================================= */
+
+static Quat
+Qt_Mul (Quat qL,
+ Quat qR)
+{
+ Quat qq;
+ qq.w = qL.w*qR.w - qL.x*qR.x - qL.y*qR.y - qL.z*qR.z;
+ qq.x = qL.w*qR.x + qL.x*qR.w + qL.y*qR.z - qL.z*qR.y;
+ qq.y = qL.w*qR.y + qL.y*qR.w + qL.z*qR.x - qL.x*qR.z;
+ qq.z = qL.w*qR.z + qL.z*qR.w + qL.x*qR.y - qL.y*qR.x;
+ return (qq);
+}
+
+/* Construct rotation matrix from (possibly non-unit) quaternion. */
+/* Assumes matrix is used to multiply column vector on the left: */
+/* vnew = mat vold. Works correctly for right-handed coordinate */
+/* system and right-handed rotations. */
+/* ============================================================== */
+
+static void
+Qt_ToMatrix (Quat q,
+ HMatrix out)
+{
+ double Nq = q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w;
+ double s = (Nq > 0.0) ? (2.0 / Nq) : 0.0;
+ double xs = q.x*s, ys = q.y*s, zs = q.z*s;
+ double wx = q.w*xs, wy = q.w*ys, wz = q.w*zs;
+ double xx = q.x*xs, xy = q.x*ys, xz = q.x*zs;
+ double yy = q.y*ys, yz = q.y*zs, zz = q.z*zs;
+ out[X][X] = 1.0 - (yy + zz); out[Y][X] = xy + wz; out[Z][X] = xz - wy;
+ out[X][Y] = xy - wz; out[Y][Y] = 1.0 - (xx + zz); out[Z][Y] = yz + wx;
+ out[X][Z] = xz + wy; out[Y][Z] = yz - wx; out[Z][Z] = 1.0 - (xx + yy);
+ out[X][W] = out[Y][W] = out[Z][W] = out[W][X] = out[W][Y] = out[W][Z] = 0.0;
+ out[W][W] = 1.0;
+}
+
+/* Return conjugate of quaternion. */
+/* =============================== */
+
+static Quat
+Qt_Conj (Quat q)
+{
+ Quat qq;
+ qq.x = -q.x; qq.y = -q.y; qq.z = -q.z; qq.w = q.w;
+ return (qq);
+}
+
+/* Return vector formed from components */
+/* ==================================== */
+
+static HVect
+V3_ (double x,
+ double y,
+ double z)
+{
+ HVect v;
+ v.x = x; v.y = y; v.z = z; v.w = 0;
+ return (v);
+}
+
+/* Return norm of v, defined as sum of squares of components */
+/* ========================================================= */
+
+static double
+V3_Norm (HVect v)
+{
+ return ( v.x*v.x + v.y*v.y + v.z*v.z );
+}
+
+/* Return unit magnitude vector in direction of v */
+/* ============================================== */
+
+static HVect
+V3_Unit (HVect v)
+{
+ static HVect u = {0, 0, 0, 0};
+ double vlen = sqrt(V3_Norm(v));
+
+ if (vlen != 0.0)
+ {
+ u.x = v.x/vlen;
+ u.y = v.y/vlen;
+ u.z = v.z/vlen;
+ }
+ return (u);
+}
+
+/* Return version of v scaled by s */
+/* =============================== */
+
+static HVect
+V3_Scale (HVect v,
+ double s)
+{
+ HVect u;
+ u.x = s*v.x; u.y = s*v.y; u.z = s*v.z; u.w = v.w;
+ return (u);
+}
+
+/* Return negative of v */
+/* ==================== */
+
+static HVect
+V3_Negate (HVect v)
+{
+ static HVect u = {0, 0, 0, 0};
+ u.x = -v.x; u.y = -v.y; u.z = -v.z;
+ return (u);
+}
+
+/* Return sum of v1 and v2 */
+/* ======================= */
+/*
+static HVect
+V3_Add (HVect v1,
+ HVect v2)
+{
+ static HVect v = {0, 0, 0, 0};
+ v.x = v1.x+v2.x; v.y = v1.y+v2.y; v.z = v1.z+v2.z;
+ return (v);
+}
+*/
+/* Return difference of v1 minus v2 */
+/* ================================ */
+
+static HVect
+V3_Sub (HVect v1,
+ HVect v2)
+{
+ static HVect v = {0, 0, 0, 0};
+ v.x = v1.x-v2.x; v.y = v1.y-v2.y; v.z = v1.z-v2.z;
+ return (v);
+}
+
+/* Halve arc between unit vectors v0 and v1. */
+/* ========================================= */
+/*
+static HVect
+V3_Bisect (HVect v0,
+ HVect v1)
+{
+ HVect v = {0, 0, 0, 0};
+ double Nv;
+
+ v = V3_Add(v0, v1);
+ Nv = V3_Norm(v);
+ if (Nv < 1.0e-5) v = V3_(0, 0, 1);
+ else v = V3_Scale(v, 1/sqrt(Nv));
+ return (v);
+}
+*/
+
+/* Return dot product of v1 and v2 */
+/* =============================== */
+
+static double
+V3_Dot (HVect v1,
+ HVect v2)
+{
+ return (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);
+}
+
+
+/* Return cross product, v1 x v2 */
+/* ============================= */
+/*
+static HVect
+V3_Cross (HVect v1,
+ HVect v2)
+{
+ static HVect v = {0, 0, 0, 0};
+ v.x = v1.y*v2.z-v1.z*v2.y;
+ v.y = v1.z*v2.x-v1.x*v2.z;
+ v.z = v1.x*v2.y-v1.y*v2.x;
+ return (v);
+}
+*/
+
+void
+ArcBall_CopyMat (HMatrix inm,
+ HMatrix outm)
+{
+ int x=0,y=0;
+
+ for (x=0;x<4;x++)
+ {
+ for (y=0;y<4;y++)
+ {
+ outm[y][x]=inm[y][x];
+ }
+ }
+}
+
+/*=====================================================*/
+/**** BallMath.c - Essential routines for ArcBall. ****/
+/*=====================================================*/
+
+/* Convert window coordinates to sphere coordinates. */
+/* ================================================= */
+
+static HVect
+MouseOnSphere (HVect mouse,
+ HVect ballCenter,
+ double ballRadius)
+{
+ HVect ballMouse;
+ register double mag;
+
+ ballMouse.x = (mouse.x - ballCenter.x) / ballRadius;
+ ballMouse.y = (mouse.y - ballCenter.y) / ballRadius;
+ mag = ballMouse.x*ballMouse.x + ballMouse.y*ballMouse.y;
+ if (mag > 1.0)
+ {
+ register double scale = 1.0/sqrt(mag);
+ ballMouse.x *= scale; ballMouse.y *= scale;
+ ballMouse.z = 0.0;
+ }
+ else ballMouse.z = sqrt(1 - mag);
+ ballMouse.w = 0.0;
+ return (ballMouse);
+}
+
+/* Construct a unit quaternion from two points on unit sphere */
+/* ========================================================== */
+
+static Quat
+Qt_FromBallPoints (HVect from,
+ HVect to)
+{
+ Quat qu;
+ qu.x = from.y*to.z - from.z*to.y;
+ qu.y = from.z*to.x - from.x*to.z;
+ qu.z = from.x*to.y - from.y*to.x;
+ qu.w = from.x*to.x + from.y*to.y + from.z*to.z;
+ return (qu);
+}
+
+/* Convert a unit quaternion to two points on unit sphere */
+/* ====================================================== */
+
+static void
+Qt_ToBallPoints (Quat q,
+ HVect *arcFrom,
+ HVect *arcTo)
+{
+ double s;
+
+ s = sqrt(q.x*q.x + q.y*q.y);
+ if (s == 0.0) *arcFrom = V3_(0.0, 1.0, 0.0);
+ else *arcFrom = V3_(-q.y/s, q.x/s, 0.0);
+ arcTo->x = q.w*arcFrom->x - q.z*arcFrom->y;
+ arcTo->y = q.w*arcFrom->y + q.z*arcFrom->x;
+ arcTo->z = q.x*arcFrom->y - q.y*arcFrom->x;
+ if (q.w < 0.0) *arcFrom = V3_(-arcFrom->x, -arcFrom->y, 0.0);
+}
+
+/* Force sphere point to be perpendicular to axis. */
+/* =============================================== */
+
+static HVect
+ConstrainToAxis (HVect loose,
+ HVect axis)
+{
+ HVect onPlane;
+ register double norm;
+
+ onPlane = V3_Sub(loose, V3_Scale(axis, V3_Dot(axis, loose)));
+ norm = V3_Norm(onPlane);
+ if (norm > 0.0)
+ {
+ if (onPlane.z < 0.0) onPlane = V3_Negate(onPlane);
+ return ( V3_Scale(onPlane, 1/sqrt(norm)) );
+ }
+ /* else drop through */
+ /* ================= */
+
+ if (axis.z == 1) onPlane = V3_(1.0, 0.0, 0.0);
+ else onPlane = V3_Unit(V3_(-axis.y, axis.x, 0.0));
+ return (onPlane);
+}
+
+/* Find the index of nearest arc of axis set. */
+/* ========================================== */
+
+static int
+NearestConstraintAxis (HVect loose,
+ HVect *axes,
+ int nAxes)
+{
+ HVect onPlane;
+ register double max, dot;
+ register int i, nearest;
+ max = -1; nearest = 0;
+
+ for (i=0; i<nAxes; i++)
+ {
+ onPlane = ConstrainToAxis(loose, axes[i]);
+ dot = V3_Dot(onPlane, loose);
+ if (dot>max)
+ {
+ max = dot; nearest = i;
+ }
+ }
+ return (nearest);
+}