(function () {
var INIT_QUATERNION = [1, 0, 0, 0];
function RotationController (element) {
this.target = element;
this.mouseX = null;
this.mouseY = null;
this.touchF = false;
this.Qnow = INIT_QUATERNION;
this.Qbef = INIT_QUATERNION;
this.rots = INIT_QUATERNION;
this.gain = 1 / element.offsetWidth ;
this.dx = 0;
this.dy = 0;
this.timerId = null;
this.miniInertia = 1e-7;
}
function rotation (dx, dy) {
var a, b, a0, a1, a2, a3, b0, b1, b2, b3, r, t, as;
if (t = dx * dx + dy * dy) {
r = Math.sqrt (t);
as = Math.sin (r) / r;
a = this.Qnow;
a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3];
b0 = dy * as; b1 = dx * as; b3 = Math.cos (r);
a = this.Qbef;
b = this.Qnow = [
a0 * b3 - a3 * b0 - a2 * b1,
a1 * b3 + a3 * b1 - a2 * b0,
a2 * b3 + a0 * b1 + a1 * b0,
a3 * b3 + a0 * b0 - a1 * b1
];
a0 = a[0]; a1 = a[1]; a2 = a[2]; a3 = a[3];
b0 = b[0]; b1 = b[1]; b2 = b[2]; b3 = b[3];
this.rots = [
a0 * b0 - a1 * b1 - a2 * b2 - a3 * b3,
a0 * b1 + a1 * b0 + a2 * b3 - a3 * b2,
a0 * b2 - a1 * b3 + a2 * b0 + a3 * b1,
a0 * b3 + a1 * b2 - a2 * b1 + a3 * b0
];
this.dx = dx;
this.dy = dy;
}
return t;
}
function inertia () {
var distance = rotation.call (
this,
this.dx - this.dx / 40,
this.dy - this.dy / 40
);
if (this.miniInertia < distance)
this.timerId = setTimeout (inertia.bind (this), 33);
}
function quaternionRotation (material) {
var i, j, x, y, z;
var surface, vertex;
var q = this.rots;
var q0 = q[0], q1 = q[1], q2 = q[2], q3 = q[3];
var a0, a1, a2, a3;
var s = [], rst = [];
for (i = 0; surface = material[i]; i++) {
for (j = 0, s = []; vertex = surface[j]; j++) {
x = vertex[0], y = vertex[1], z = vertex[2];
a0 = q3 * x + q1 * z - q2 * y;
a1 = q3 * y + q2 * x - q0 * z;
a2 = q3 * z + q0 * y - q1 * x;
a3 = -q0 * x - q1 * y - q2 * z;
s[j] = [
a0 * q3 - a3 * q0 - a1 * q2 + a2 * q1,
a1 * q3 - a3 * q1 - a2 * q0 + a0 * q2,
a2 * q3 - a3 * q2 - a0 * q1 + a1 * q0
];
}
rst[i] = s;
}
return rst;
}
function handleEvent (event) {
var e, x, y, dx, dy, a, b, c, e, r, t;
var a0, a1, a2, a3, b0, b1, b2, b3, as;
switch (event.type) {
case 'mouseup' :
case 'mouseout' :
case 'touchend' :
this.touchF = false;
inertia.call (this);
break;
case 'mousedown' :
case 'touchstart' :
if (this.timerId) {
clearTimeout (this.timerId);
this.timerId = null;
}
this.touchF = true;
this.Qnow = INIT_QUATERNION;
this.Qbef = this.rots;
e = event.target.getBoundingClientRect ();
this.mouseX = event.pageX - e.left;
this.mouseY = event.pageY - e.top;
break;
case 'mousemove' :
case 'touchmove' :
event.preventDefault ();
e = event.target.getBoundingClientRect ();
x = event.pageX - e.left;
y = event.pageY - e.top;
if (this.touchF){
dx = (x - this.mouseX) * this.gain;
dy = (y - this.mouseY) * this.gain;
rotation.call (this, dx, dy);
}
this.mouseX = x;
this.mouseY = y;
break;
}
}
function addEvent (event_type) {
this.target.addEventListener (event_type, this, false);
}
function create (target) {
if (1 > arguments.length)
throw new Error ('引数がない');
var obj = new RotationController (target);
var event_list = window.TouchEvent
? ['touchstart', 'touchend', 'touchmove']
: ['mousedown', 'mouseup', 'mousemove', 'mouseout'];
canvas = null;
event_list.forEach (addEvent, obj);
return obj;
}
RotationController.prototype.handleEvent = handleEvent;
RotationController.prototype.quaternionRotation = quaternionRotation;
RotationController.create = create;
this.RotationController = RotationController;
}) ();