ObJS.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. /********************************************************************
  2. * The ObJS objectnitializes the canvas, assignin it to the HTML *
  3. * element, and set the required mouse events. *
  4. * #parameters: none *
  5. * #return: nothing *
  6. ********************************************************************/
  7. function ObJS(file, canv){
  8. /********************************************************************
  9. * Parameters for costumization. *
  10. ********************************************************************/
  11. var backColor = "rgba(200, 200, 200, 1)";
  12. var vertColor = "rgba(10, 10, 10, 1)";
  13. var edgeColor = "rgba(100, 70, 70, 1)";
  14. var faceColor = "rgba(180, 180, 255, .6)";
  15. var faceAlpha = .6;
  16. var textColor = "rgba(0, 0, 0, 1)";
  17. var vertSize = 4; //Make it even: at small scales will look better
  18. var rotationSpeed = 90; //Smaller = faster. 100 is a good speed
  19. var zoomSpeed = 0.161; //0-1. 0.2 or 0.3 are good speeds
  20. /********************************************************************
  21. * Variable containing this object, to be refered from nested *
  22. * functions. *
  23. ********************************************************************/
  24. var thisObJS;
  25. /********************************************************************
  26. * Parameter used to scale the measures in the .obj file. Can be *
  27. * integer or float, greater than 0. 50 is a good default. *
  28. ********************************************************************/
  29. var scale;
  30. /********************************************************************
  31. * Variable to determine if the canvas has been initialized. *
  32. ********************************************************************/
  33. var canvasInitialized = false;
  34. /********************************************************************
  35. * Variable to determine if the mouse button is being holded down. *
  36. ********************************************************************/
  37. var mDown = false;
  38. /********************************************************************
  39. * Variables containing the canvas and its context. *
  40. ********************************************************************/
  41. var canvas;
  42. var ctx;
  43. /********************************************************************
  44. * Variables containing the number of elements. *
  45. ********************************************************************/
  46. var totalVert;
  47. var totalEdge;//TODO: I dont know how to calculate. And I dont need it
  48. var totalFace
  49. /********************************************************************
  50. * Arrays containing the vertices, edges and faces. *
  51. ********************************************************************/
  52. var obj; //Unused
  53. var vert;
  54. var face;
  55. /********************************************************************
  56. * Booleans to determine wich elements are to draw. *
  57. ********************************************************************/
  58. var dVerts = true;
  59. var dEdges = true;
  60. var dFaces = true;
  61. var dBackg = true;
  62. /********************************************************************
  63. * Global variables that will contain the position of the mouse in *
  64. * the previous frame to calculate rotation. *
  65. ********************************************************************/
  66. var pX = null;
  67. var pY = null;
  68. /********************************************************************
  69. * Function that initializes the canvas, assignin it to the HTML *
  70. * element, and set the required mouse events. *
  71. * #parameters: *
  72. * canv (canvas): canvas to use. If omited, the one named *
  73. * named "ObJSCanvas" will be used. *
  74. * #return: nothing *
  75. * #scope: public *
  76. ********************************************************************/
  77. this.initCanvas = function(canv){
  78. if (!canvasInitialized){
  79. if (canv!= null)
  80. canvas = canv;
  81. else
  82. canvas = document.getElementById("ObJSCanvas");
  83. ctx = canvas.getContext("2d");
  84. ctx.translate(canvas.width / 2, canvas.height / 2);
  85. canvas.onmousedown = function(e){
  86. pX = e.offsetX==undefined?e.layerX:e.offsetX;
  87. pY = e.offsetY==undefined?e.layerY:e.offsetY;
  88. mDown = true;
  89. };
  90. canvas.onmouseup = function(e){
  91. if(mDown)
  92. mouseClick(e);
  93. pX = null;
  94. pY = null;
  95. mDown = false;
  96. };
  97. canvas.onmousemove = function(e){
  98. if(!mDown)
  99. return;
  100. var x = e.offsetX==undefined?e.layerX:e.offsetX;
  101. var y = e.offsetY==undefined?e.layerY:e.offsetY;
  102. thisObJS.rotateY(x - pX);
  103. thisObJS.rotateX(y - pY);
  104. pX = x;
  105. pY = y;
  106. draw();
  107. return false;
  108. };
  109. canvas.addEventListener('mousewheel',function(event){
  110. if (event.wheelDelta > 0)
  111. thisObJS.zoom("-");
  112. else
  113. thisObJS.zoom("+");
  114. return false;
  115. }, false);
  116. canvasInitialized = true;
  117. }
  118. };
  119. /********************************************************************
  120. * Main function, called every time a file is to be loaded. *
  121. * #parameters: *
  122. * file (string): name of the file, without path or extension. *
  123. * #return: nothing *
  124. * #scope: public *
  125. ********************************************************************/
  126. this.load = function(file){
  127. //Get file
  128. xmlhttp = new XMLHttpRequest();
  129. xmlhttp.open("GET",file, false);
  130. xmlhttp.send();
  131. var fileContent = xmlhttp.responseText;
  132. initArrays();
  133. readVerts(fileContent);
  134. readFaces(fileContent);
  135. draw();
  136. };
  137. /********************************************************************
  138. * Function switching the elements to draw (dVerts, dEdges, dFaces) *
  139. * #parameters: *
  140. * name (string): key to the element to draw. *
  141. * value (boolean): indicating if the element is to be drawn. *
  142. * #return: nothing *
  143. * #scope: public *
  144. ********************************************************************/
  145. this.drawElement = function(name, value){
  146. switch (name){
  147. case "verts":
  148. dVerts = value;
  149. break;
  150. case "edges":
  151. dEdges = value;
  152. break;
  153. case "faces":
  154. dFaces = value;
  155. break;
  156. case "backg":
  157. dBackg = value;
  158. break;
  159. }
  160. draw();
  161. };
  162. /********************************************************************
  163. * Function that turns a hex color code in RGB. *
  164. * stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb *
  165. * #parameters: *
  166. * hex (string): color code, for example "#33ff99" *
  167. * #return: (Array) r, g, b, with their correspondent color. *
  168. * #scope: private *
  169. ********************************************************************/
  170. var hexToRgb = function(hex) {
  171. var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  172. return result ? {
  173. r: parseInt(result[1], 16),
  174. g: parseInt(result[2], 16),
  175. b: parseInt(result[3], 16)
  176. } : null;
  177. };
  178. /********************************************************************
  179. * Function switching the color of the elements. *
  180. * #parameters: *
  181. * name (string): key to the element to colorize. *
  182. * value (boolean): indicating if the element is to be drawn. *
  183. * #return: nothing *
  184. * #scope: public *
  185. ********************************************************************/
  186. this.colorElement = function(name, value){
  187. var color = hexToRgb(value)
  188. var str = "rgba(" + color.r + ", " + color.g + ", " + color.b + ", 1)"
  189. switch (name){
  190. case "backg":
  191. backColor = "rgba(" + color.r + ", " + color.g + ", " + color.b + ", 1)";
  192. break;
  193. case "verts":
  194. vertColor = "rgba(" + color.r + ", " + color.g + ", " + color.b + ", 1)";
  195. break;
  196. case "edges":
  197. edgeColor = "rgba(" + color.r + ", " + color.g + ", " + color.b + ", 1)";
  198. break;
  199. case "faces":
  200. faceColor = "rgba(" + color.r + ", " + color.g + ", " + color.b + ", " + faceAlpha + ")";
  201. break;
  202. }
  203. draw();
  204. };
  205. /********************************************************************
  206. * Function switching the alpha value of the faces. *
  207. * #parameters: *
  208. * percent (int): Value (0-100) of alpha. Must be processed (0-1) *
  209. * #return: nothing *
  210. * #scope: public *
  211. ********************************************************************/
  212. this.setAlpha = function(percent){
  213. if (percent >= 0 && percent <= 100){
  214. faceAlpha = percent / 100;
  215. faceColor = faceColor.substring(0, faceColor.lastIndexOf(",") + 2) + faceAlpha + ")";
  216. draw();
  217. }
  218. else
  219. console.log("setAlpha(" + percent + "); ERROR: Only values between 0 and 100 are allowed");
  220. };
  221. /********************************************************************
  222. * Function switching the rotation speed. *
  223. * #parameters: *
  224. * percent (int): Value (0-10) of speed. *
  225. * #return: nothing *
  226. * #scope: public *
  227. ********************************************************************/
  228. this.setRotationSpeed = function(percent){
  229. if (percent >= 0 && percent <= 10){
  230. rotationSpeed = Math.round((10 - percent) * 30);
  231. if (rotationSpeed == 0)
  232. rotationSpeed = 1;
  233. }
  234. else
  235. console.log("setRotationSpeed(" + percent + "); ERROR: Only values between 0 and 10 are allowed");
  236. }
  237. /********************************************************************
  238. * Function that rotates elements in the canvas along the Y axis, *
  239. * calculating the new position for each vertex in the vert array. *
  240. * #parameters: *
  241. * des (int): mouse displacement from the last frame. *
  242. * #return: nothing *
  243. * #scope: public *
  244. ********************************************************************/
  245. this.rotateY = function(des){
  246. for (var i = 0; i < vert.length; i ++) {
  247. var x = vert[i][0];
  248. var z = vert[i][2];
  249. vert[i][0] = x * Math.cos(des / rotationSpeed) - z * Math.sin(des / rotationSpeed);
  250. vert[i][2] = z * Math.cos(des / rotationSpeed) + x * Math.sin(des / rotationSpeed);
  251. }
  252. };
  253. /********************************************************************
  254. * Function that rotates elements in the canvas along the X axis, *
  255. * calculating the new position for each vertex in the vert array. *
  256. * #parameters: *
  257. * des (int): mouse displacement from the last frame. *
  258. * #return: nothing *
  259. * #scope: public *
  260. ********************************************************************/
  261. this.rotateX = function(des){
  262. for (var i = 0; i < vert.length; i ++) {
  263. var y = vert[i][1];
  264. var z = vert[i][2];
  265. vert[i][1] = y * Math.cos(des / rotationSpeed) - z * Math.sin(des / rotationSpeed);
  266. vert[i][2] = z * Math.cos(des / rotationSpeed) + y * Math.sin(des / rotationSpeed);
  267. }
  268. };
  269. /********************************************************************
  270. * Function switching the zoom speede of the faces. *
  271. * #parameters: *
  272. * percent (int): Value (0-100) of speed. *
  273. * #return: nothing *
  274. * #scope: public *
  275. ********************************************************************/
  276. this.setZoomSpeed = function(percent){
  277. if (percent >= 0 && percent <= 10)
  278. zoomSpeed = ((percent * 0.8) / 10) + 0.01;
  279. else
  280. console.log("setZoomSpeed(" + percent + "); ERROR: Only values between 0 and 10 are allowed");
  281. }
  282. /********************************************************************
  283. * Function that changes the scale variable, witch results in a *
  284. * zoom in or zoom out effect. *
  285. * #parameters: none *
  286. * #return: nothing *
  287. * #scope: public *
  288. ********************************************************************/
  289. this.zoom = function(dir){
  290. if (dir == "-")
  291. scale = Math.round(scale * (1 + zoomSpeed));
  292. if (dir == "+")
  293. scale = Math.round(scale * (1 - zoomSpeed));
  294. draw();
  295. };
  296. this.clear = function(){
  297. initArrays();
  298. clearCanvas();
  299. }
  300. /********************************************************************
  301. * Function that clears the canvas and gets it ready to draw on it. *
  302. * #parameters: none *
  303. * #return: nothing *
  304. * #scope: public *
  305. ********************************************************************/
  306. var clearCanvas = function(){
  307. ctx.clearRect(0, 0, canvas.width, canvas.height);
  308. if (dBackg)
  309. ctx.fillStyle = backColor;
  310. else
  311. ctx.fillStyle = "#ffffff";
  312. ctx.fillRect(-canvas.width / 2, -canvas.height / 2, canvas.width, canvas.height);
  313. };
  314. /********************************************************************
  315. * Function initializing the arrays obj, vert and face as empty *
  316. * arrays. *
  317. * #parameters: none *
  318. * #return: nothing *
  319. * #scope: private *
  320. ********************************************************************/
  321. var initArrays = function(){
  322. obj = null;
  323. vert = null;
  324. face = null;
  325. obj = new Array();
  326. vert = new Array();
  327. face = new Array();
  328. };
  329. /********************************************************************
  330. * Function tat populates the vert[] array with and array of three *
  331. * elements, containig the coordinates, with the scale applied. *
  332. * #parameters: *
  333. * text (string): the content of the obj file. *
  334. * #return: nothing *
  335. * #scope: private *
  336. ********************************************************************/
  337. var readVerts = function(text){
  338. var line;
  339. var i = 0;
  340. var max = 0;
  341. var dist;
  342. while (text.indexOf("v ") != -1){
  343. line = text.substring(text.indexOf("v "), text.indexOf("\n", text.indexOf("v ")));
  344. vert[i] = new Array(3);
  345. line = line.substring(2);
  346. vert[i][0] = line.substring(0, line.indexOf(" "));
  347. line = line.substring(line.indexOf(" ") + 1);
  348. vert[i][1] = line.substring(0, line.indexOf(" "));
  349. line = line.substring(line.indexOf(" ") + 1);
  350. vert[i][2] = line;
  351. text = text.substring(text.indexOf("v ") + 1)
  352. dist = Math.sqrt(vert[i][0] * vert[i][0] + vert[i][1] * vert[i][1] + vert[i][2] * vert[i][2]);
  353. if (dist > max)
  354. max = dist;
  355. i = i + 1;
  356. }
  357. scale = calculateScale(max);
  358. totalVert = vert.length;
  359. };
  360. /********************************************************************
  361. * Function that automatically calculates the best scale for the *
  362. * modelermined number of elements, containing the verices that *
  363. * form a face, so the bigest dimension from the object can fit in *
  364. * the smallest dimension of the canvas. *
  365. * #parameters: *
  366. * text (int): the farthest vertex, from the center. *
  367. * #return: (int) The scale to be aplied. *
  368. * #scope: private *
  369. ********************************************************************/
  370. var calculateScale = function(max){
  371. var dim;
  372. if (canvas.width < canvas.height)
  373. dim = canvas.width;
  374. else
  375. dim = canvas.height;
  376. var sc = Math.round((0.95 * dim) / (2 * max));
  377. return sc;
  378. };
  379. /********************************************************************
  380. * Function that populates the face[] array with and array of an *
  381. * undetermined number of elements, containing the verices that *
  382. * form a face. *
  383. * #parameters: *
  384. * text (string): the content of the obj file. *
  385. * #return: nothing *
  386. * #scope: private *
  387. ********************************************************************/
  388. var readFaces = function(text){
  389. var finish = false;
  390. var line;
  391. var i = 0;
  392. var j;
  393. while (text.indexOf("f ") != -1){
  394. line = text.substring(text.indexOf("f "), text.indexOf("\n", text.indexOf("f ")));
  395. line = line.substring(2);
  396. face[i] = new Array();
  397. j = 0;
  398. while(line.indexOf(" ") != -1){
  399. face[i][j] = line.substring(0, line.indexOf(" ")) - 1;
  400. line = line.substring(line.indexOf(" ") + 1);
  401. j = j + 1;
  402. }
  403. face[i][j] = line - 1;
  404. i = i + 1;
  405. text = text.substring(text.indexOf("f ") + 1)
  406. }
  407. totalFace = face.length;
  408. };
  409. /********************************************************************
  410. * Function that compares the average Z coordinate of two faces. *
  411. * Must be used when sorting the face array. *
  412. * #parameters: *
  413. * a (Array): One face. *
  414. * b (Array): The other face. *
  415. * #return: (int) -1, 1 or 0. *
  416. * #scope: private *
  417. ********************************************************************/
  418. var faceComparator = function(a,b){
  419. var aZ;
  420. var bZ;
  421. var i = 0;
  422. var sum = 0.0;
  423. while (i < a.length){
  424. sum = sum + parseFloat(vert[a[i]][2]);
  425. i = i + 1;
  426. }
  427. aZ = sum / parseFloat(i);
  428. i = 0;
  429. sum = 0;
  430. while (i < b.length){
  431. sum = sum + parseFloat(1 * vert[b[i]][2]);
  432. i = i + 1;
  433. }
  434. bZ = sum / parseFloat(i);
  435. if (aZ < bZ) return -1;
  436. if (aZ > bZ) return 1;
  437. return 0;
  438. };
  439. /********************************************************************
  440. * Function that writes text in the corner of the canvas. Must be *
  441. * called when everithing has been drawn. *
  442. * #parameters: none *
  443. * #return: nothing *
  444. * #scope: private *
  445. ********************************************************************/
  446. var writeCredits = function(){
  447. ctx.fillStyle = textColor;
  448. ctx.font = "12px Arial";
  449. var h = canvas.height / 2 - 14;
  450. var w = 10 - (canvas.height / 2);
  451. ctx.fillText("ObJS " + totalVert + " vertizes " + totalFace + " faces", w, h);
  452. };
  453. /********************************************************************
  454. * Function that draws th vertizes, faces and edges in the canvas. *
  455. * #parameters: none *
  456. * #return: nothing *
  457. * #scope: private *
  458. ********************************************************************/
  459. var draw = function(){
  460. face = face.sort(faceComparator).reverse();
  461. clearCanvas();
  462. for (var i = 0; i < face.length; i++){
  463. ctx.strokeStyle = edgeColor;
  464. ctx.fillStyle = vertColor;
  465. ctx.beginPath();
  466. ctx.moveTo(scale * vert[face[i][0]][0], scale * vert[face[i][0]][1]);
  467. for (var j = 1; j < face[i].length; j++) {
  468. ctx.lineTo(scale * vert[face[i][j]][0], scale * vert[face[i][j]][1]);
  469. if(dEdges)
  470. ctx.stroke();
  471. if(dVerts){
  472. var x = scale * vert[face[i][j]][0] - (vertSize / 2);
  473. var w = vertSize;
  474. var y = scale * vert[face[i][j]][1] - (vertSize / 2);
  475. var h = vertSize;
  476. ctx.fillRect(x, y, w, h);
  477. }
  478. }
  479. ctx.closePath();
  480. ctx.fillStyle = faceColor;
  481. if(dFaces)
  482. ctx.fill();
  483. }
  484. writeCredits();
  485. };
  486. /********************************************************************
  487. * TODO: Function used when something in the canvas is clicked. *
  488. * #parameters: none *
  489. * #return: nothing *
  490. * #scope: private *
  491. ********************************************************************/
  492. var mouseClick = function(){
  493. };
  494. thisObJS = this;
  495. return this;
  496. }