3d-cube.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. // 3D Cube Rotation
  2. // http://www.speich.net/computer/moztesting/3d.htm
  3. // Created by Simon Speich
  4. var Q = new Array();
  5. var MTrans = new Array(); // transformation matrix
  6. var MQube = new Array(); // position information of qube
  7. var I = new Array(); // entity matrix
  8. var Origin = new Object();
  9. var Testing = new Object();
  10. var LoopTimer;
  11. var validation = {
  12. 20: 2889.0000000000045,
  13. 40: 2889.0000000000055,
  14. 80: 2889.000000000005,
  15. 160: 2889.0000000000055
  16. };
  17. var DisplArea = new Object();
  18. DisplArea.Width = 300;
  19. DisplArea.Height = 300;
  20. function DrawLine(From, To) {
  21. var x1 = From.V[0];
  22. var x2 = To.V[0];
  23. var y1 = From.V[1];
  24. var y2 = To.V[1];
  25. var dx = Math.abs(x2 - x1);
  26. var dy = Math.abs(y2 - y1);
  27. var x = x1;
  28. var y = y1;
  29. var IncX1, IncY1;
  30. var IncX2, IncY2;
  31. var Den;
  32. var Num;
  33. var NumAdd;
  34. var NumPix;
  35. if (x2 >= x1) { IncX1 = 1; IncX2 = 1; }
  36. else { IncX1 = -1; IncX2 = -1; }
  37. if (y2 >= y1) { IncY1 = 1; IncY2 = 1; }
  38. else { IncY1 = -1; IncY2 = -1; }
  39. if (dx >= dy) {
  40. IncX1 = 0;
  41. IncY2 = 0;
  42. Den = dx;
  43. Num = dx / 2;
  44. NumAdd = dy;
  45. NumPix = dx;
  46. }
  47. else {
  48. IncX2 = 0;
  49. IncY1 = 0;
  50. Den = dy;
  51. Num = dy / 2;
  52. NumAdd = dx;
  53. NumPix = dy;
  54. }
  55. NumPix = Math.round(Q.LastPx + NumPix);
  56. var i = Q.LastPx;
  57. for (; i < NumPix; i++) {
  58. Num += NumAdd;
  59. if (Num >= Den) {
  60. Num -= Den;
  61. x += IncX1;
  62. y += IncY1;
  63. }
  64. x += IncX2;
  65. y += IncY2;
  66. }
  67. Q.LastPx = NumPix;
  68. }
  69. function CalcCross(V0, V1) {
  70. var Cross = new Array();
  71. Cross[0] = V0[1]*V1[2] - V0[2]*V1[1];
  72. Cross[1] = V0[2]*V1[0] - V0[0]*V1[2];
  73. Cross[2] = V0[0]*V1[1] - V0[1]*V1[0];
  74. return Cross;
  75. }
  76. function CalcNormal(V0, V1, V2) {
  77. var A = new Array(); var B = new Array();
  78. for (var i = 0; i < 3; i++) {
  79. A[i] = V0[i] - V1[i];
  80. B[i] = V2[i] - V1[i];
  81. }
  82. A = CalcCross(A, B);
  83. var Length = Math.sqrt(A[0]*A[0] + A[1]*A[1] + A[2]*A[2]);
  84. for (var i = 0; i < 3; i++) A[i] = A[i] / Length;
  85. A[3] = 1;
  86. return A;
  87. }
  88. function CreateP(X,Y,Z) {
  89. this.V = [X,Y,Z,1];
  90. }
  91. // multiplies two matrices
  92. function MMulti(M1, M2) {
  93. var M = [[],[],[],[]];
  94. var i = 0;
  95. var j = 0;
  96. for (; i < 4; i++) {
  97. j = 0;
  98. for (; j < 4; j++) M[i][j] = M1[i][0] * M2[0][j] + M1[i][1] * M2[1][j] + M1[i][2] * M2[2][j] + M1[i][3] * M2[3][j];
  99. }
  100. return M;
  101. }
  102. //multiplies matrix with vector
  103. function VMulti(M, V) {
  104. var Vect = new Array();
  105. var i = 0;
  106. for (;i < 4; i++) Vect[i] = M[i][0] * V[0] + M[i][1] * V[1] + M[i][2] * V[2] + M[i][3] * V[3];
  107. return Vect;
  108. }
  109. function VMulti2(M, V) {
  110. var Vect = new Array();
  111. var i = 0;
  112. for (;i < 3; i++) Vect[i] = M[i][0] * V[0] + M[i][1] * V[1] + M[i][2] * V[2];
  113. return Vect;
  114. }
  115. // add to matrices
  116. function MAdd(M1, M2) {
  117. var M = [[],[],[],[]];
  118. var i = 0;
  119. var j = 0;
  120. for (; i < 4; i++) {
  121. j = 0;
  122. for (; j < 4; j++) M[i][j] = M1[i][j] + M2[i][j];
  123. }
  124. return M;
  125. }
  126. function Translate(M, Dx, Dy, Dz) {
  127. var T = [
  128. [1,0,0,Dx],
  129. [0,1,0,Dy],
  130. [0,0,1,Dz],
  131. [0,0,0,1]
  132. ];
  133. return MMulti(T, M);
  134. }
  135. function RotateX(M, Phi) {
  136. var a = Phi;
  137. a *= Math.PI / 180;
  138. var Cos = Math.cos(a);
  139. var Sin = Math.sin(a);
  140. var R = [
  141. [1,0,0,0],
  142. [0,Cos,-Sin,0],
  143. [0,Sin,Cos,0],
  144. [0,0,0,1]
  145. ];
  146. return MMulti(R, M);
  147. }
  148. function RotateY(M, Phi) {
  149. var a = Phi;
  150. a *= Math.PI / 180;
  151. var Cos = Math.cos(a);
  152. var Sin = Math.sin(a);
  153. var R = [
  154. [Cos,0,Sin,0],
  155. [0,1,0,0],
  156. [-Sin,0,Cos,0],
  157. [0,0,0,1]
  158. ];
  159. return MMulti(R, M);
  160. }
  161. function RotateZ(M, Phi) {
  162. var a = Phi;
  163. a *= Math.PI / 180;
  164. var Cos = Math.cos(a);
  165. var Sin = Math.sin(a);
  166. var R = [
  167. [Cos,-Sin,0,0],
  168. [Sin,Cos,0,0],
  169. [0,0,1,0],
  170. [0,0,0,1]
  171. ];
  172. return MMulti(R, M);
  173. }
  174. function DrawQube() {
  175. // calc current normals
  176. var CurN = new Array();
  177. var i = 5;
  178. Q.LastPx = 0;
  179. for (; i > -1; i--) CurN[i] = VMulti2(MQube, Q.Normal[i]);
  180. if (CurN[0][2] < 0) {
  181. if (!Q.Line[0]) { DrawLine(Q[0], Q[1]); Q.Line[0] = true; };
  182. if (!Q.Line[1]) { DrawLine(Q[1], Q[2]); Q.Line[1] = true; };
  183. if (!Q.Line[2]) { DrawLine(Q[2], Q[3]); Q.Line[2] = true; };
  184. if (!Q.Line[3]) { DrawLine(Q[3], Q[0]); Q.Line[3] = true; };
  185. }
  186. if (CurN[1][2] < 0) {
  187. if (!Q.Line[2]) { DrawLine(Q[3], Q[2]); Q.Line[2] = true; };
  188. if (!Q.Line[9]) { DrawLine(Q[2], Q[6]); Q.Line[9] = true; };
  189. if (!Q.Line[6]) { DrawLine(Q[6], Q[7]); Q.Line[6] = true; };
  190. if (!Q.Line[10]) { DrawLine(Q[7], Q[3]); Q.Line[10] = true; };
  191. }
  192. if (CurN[2][2] < 0) {
  193. if (!Q.Line[4]) { DrawLine(Q[4], Q[5]); Q.Line[4] = true; };
  194. if (!Q.Line[5]) { DrawLine(Q[5], Q[6]); Q.Line[5] = true; };
  195. if (!Q.Line[6]) { DrawLine(Q[6], Q[7]); Q.Line[6] = true; };
  196. if (!Q.Line[7]) { DrawLine(Q[7], Q[4]); Q.Line[7] = true; };
  197. }
  198. if (CurN[3][2] < 0) {
  199. if (!Q.Line[4]) { DrawLine(Q[4], Q[5]); Q.Line[4] = true; };
  200. if (!Q.Line[8]) { DrawLine(Q[5], Q[1]); Q.Line[8] = true; };
  201. if (!Q.Line[0]) { DrawLine(Q[1], Q[0]); Q.Line[0] = true; };
  202. if (!Q.Line[11]) { DrawLine(Q[0], Q[4]); Q.Line[11] = true; };
  203. }
  204. if (CurN[4][2] < 0) {
  205. if (!Q.Line[11]) { DrawLine(Q[4], Q[0]); Q.Line[11] = true; };
  206. if (!Q.Line[3]) { DrawLine(Q[0], Q[3]); Q.Line[3] = true; };
  207. if (!Q.Line[10]) { DrawLine(Q[3], Q[7]); Q.Line[10] = true; };
  208. if (!Q.Line[7]) { DrawLine(Q[7], Q[4]); Q.Line[7] = true; };
  209. }
  210. if (CurN[5][2] < 0) {
  211. if (!Q.Line[8]) { DrawLine(Q[1], Q[5]); Q.Line[8] = true; };
  212. if (!Q.Line[5]) { DrawLine(Q[5], Q[6]); Q.Line[5] = true; };
  213. if (!Q.Line[9]) { DrawLine(Q[6], Q[2]); Q.Line[9] = true; };
  214. if (!Q.Line[1]) { DrawLine(Q[2], Q[1]); Q.Line[1] = true; };
  215. }
  216. Q.Line = [false,false,false,false,false,false,false,false,false,false,false,false];
  217. Q.LastPx = 0;
  218. }
  219. function Loop() {
  220. if (Testing.LoopCount > Testing.LoopMax) return;
  221. var TestingStr = String(Testing.LoopCount);
  222. while (TestingStr.length < 3) TestingStr = "0" + TestingStr;
  223. MTrans = Translate(I, -Q[8].V[0], -Q[8].V[1], -Q[8].V[2]);
  224. MTrans = RotateX(MTrans, 1);
  225. MTrans = RotateY(MTrans, 3);
  226. MTrans = RotateZ(MTrans, 5);
  227. MTrans = Translate(MTrans, Q[8].V[0], Q[8].V[1], Q[8].V[2]);
  228. MQube = MMulti(MTrans, MQube);
  229. var i = 8;
  230. for (; i > -1; i--) {
  231. Q[i].V = VMulti(MTrans, Q[i].V);
  232. }
  233. DrawQube();
  234. Testing.LoopCount++;
  235. Loop();
  236. }
  237. function Init(CubeSize) {
  238. // init/reset vars
  239. Origin.V = [150,150,20,1];
  240. Testing.LoopCount = 0;
  241. Testing.LoopMax = 50;
  242. Testing.TimeMax = 0;
  243. Testing.TimeAvg = 0;
  244. Testing.TimeMin = 0;
  245. Testing.TimeTemp = 0;
  246. Testing.TimeTotal = 0;
  247. Testing.Init = false;
  248. // transformation matrix
  249. MTrans = [
  250. [1,0,0,0],
  251. [0,1,0,0],
  252. [0,0,1,0],
  253. [0,0,0,1]
  254. ];
  255. // position information of qube
  256. MQube = [
  257. [1,0,0,0],
  258. [0,1,0,0],
  259. [0,0,1,0],
  260. [0,0,0,1]
  261. ];
  262. // entity matrix
  263. I = [
  264. [1,0,0,0],
  265. [0,1,0,0],
  266. [0,0,1,0],
  267. [0,0,0,1]
  268. ];
  269. // create qube
  270. Q[0] = new CreateP(-CubeSize,-CubeSize, CubeSize);
  271. Q[1] = new CreateP(-CubeSize, CubeSize, CubeSize);
  272. Q[2] = new CreateP( CubeSize, CubeSize, CubeSize);
  273. Q[3] = new CreateP( CubeSize,-CubeSize, CubeSize);
  274. Q[4] = new CreateP(-CubeSize,-CubeSize,-CubeSize);
  275. Q[5] = new CreateP(-CubeSize, CubeSize,-CubeSize);
  276. Q[6] = new CreateP( CubeSize, CubeSize,-CubeSize);
  277. Q[7] = new CreateP( CubeSize,-CubeSize,-CubeSize);
  278. // center of gravity
  279. Q[8] = new CreateP(0, 0, 0);
  280. // anti-clockwise edge check
  281. Q.Edge = [[0,1,2],[3,2,6],[7,6,5],[4,5,1],[4,0,3],[1,5,6]];
  282. // calculate squad normals
  283. Q.Normal = new Array();
  284. for (var i = 0; i < Q.Edge.length; i++) Q.Normal[i] = CalcNormal(Q[Q.Edge[i][0]].V, Q[Q.Edge[i][1]].V, Q[Q.Edge[i][2]].V);
  285. // line drawn ?
  286. Q.Line = [false,false,false,false,false,false,false,false,false,false,false,false];
  287. // create line pixels
  288. Q.NumPx = 9 * 2 * CubeSize;
  289. for (var i = 0; i < Q.NumPx; i++) CreateP(0,0,0);
  290. MTrans = Translate(MTrans, Origin.V[0], Origin.V[1], Origin.V[2]);
  291. MQube = MMulti(MTrans, MQube);
  292. var i = 0;
  293. for (; i < 9; i++) {
  294. Q[i].V = VMulti(MTrans, Q[i].V);
  295. }
  296. DrawQube();
  297. Testing.Init = true;
  298. Loop();
  299. // Perform a simple sum-based verification.
  300. var sum = 0;
  301. for (var i = 0; i < Q.length; ++i) {
  302. var vector = Q[i].V;
  303. for (var j = 0; j < vector.length; ++j)
  304. sum += vector[j];
  305. }
  306. if (sum != validation[CubeSize])
  307. throw "Error: bad vector sum for CubeSize = " + CubeSize + "; expected " + validation[CubeSize] + " but got " + sum;
  308. }
  309. for ( var i = 20; i <= 160; i *= 2 ) {
  310. Init(i);
  311. }
  312. Q = null;
  313. MTrans = null;
  314. MQube = null;
  315. I = null;
  316. Origin = null;
  317. Testing = null;
  318. LoopTime = null;
  319. DisplArea = null;