dromaeo-3d-cube-modern.js 8.5 KB

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