corpus.min.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. /**
  2. * @Author: Bachir Soussi Chiadmi <bach>
  3. * @Date: 18-12-2017
  4. * @Email: bachir@figureslibres.io
  5. * @Filename: corpus.js
  6. * @Last modified by: bach
  7. * @Last modified time: 20-12-2017
  8. * @License: GPL-V3
  9. */
  10. /**
  11. * depends on :
  12. *
  13. * physics.js : http://jonobr1.com/Physics/#
  14. * EaselJS : https://createjs.com/docs/easeljs/modules/EaselJS.html
  15. *
  16. */
  17. (function($) {
  18. EdlpCorpus = function(){
  19. var _$container = $('body');
  20. var _$canvas = $('<canvas id="edlp-map">').appendTo(_$container);
  21. var _canvas = _$canvas[0];
  22. var _ctx = _canvas.getContext('2d');
  23. var _canvas_props = {
  24. 'margin_top':100,
  25. 'margin_right':0,
  26. 'margin_bottom':100,
  27. 'margin_left':0
  28. };
  29. var _physics = new Physics();
  30. // var _stage = new createjs.Stage('edlp-map');
  31. var _nodes = [];
  32. // var _particules = [];
  33. var _p_radius = 3; // nodes radius (real radius, not diametre)
  34. var _p_velocity_factor = 0.5;
  35. var _m_pos = {x:0, y:0};
  36. var _node_hover_id = -1;
  37. var _$entrees_block = $('#block-edlpentreesblock');
  38. var _$entrees_block_termlinks = $('a.term-link', _$entrees_block);
  39. // Colors depend on edlp_vars loaded by edlptheme
  40. // console.log('Corpus : edlp_vars', edlp_vars);
  41. // ____ _ __
  42. // / _/___ (_) /______
  43. // / // __ \/ / __/ ___/
  44. // _/ // / / / / /_(__ )
  45. // /___/_/ /_/_/\__/____/
  46. function init(){
  47. console.log("EdlpCorpus init()");
  48. // console.log("document", document);
  49. initMap();
  50. };
  51. function initMap(){
  52. console.log("EdlpCorpus initMap()");
  53. // console.log('_physics',_physics);
  54. initCanvas();
  55. loadCorpus();
  56. };
  57. // ______
  58. // / ____/___ _____ _ ______ ______
  59. // / / / __ `/ __ \ | / / __ `/ ___/
  60. // / /___/ /_/ / / / / |/ / /_/ (__ )
  61. // \____/\__,_/_/ /_/|___/\__,_/____/
  62. function initCanvas(){
  63. // resize the canvas to fill browser window dynamically
  64. window.addEventListener('resize', onResizeCanvas, false);
  65. onResizeCanvas();
  66. };
  67. function onResizeCanvas() {
  68. _canvas.width = window.innerWidth;
  69. _canvas.height = window.innerHeight;
  70. for (var i = 0; i < _nodes.length; i++) {
  71. _nodes[i].onResizeCanvas();
  72. }
  73. };
  74. // ___ _
  75. // / | (_)___ __ __
  76. // / /| | / / __ `/ |/_/
  77. // / ___ | / / /_/ /> <
  78. // /_/ |_|_/ /\__,_/_/|_|
  79. // /___/
  80. function loadCorpus(){
  81. // console.log('drupalSettings',drupalSettings);
  82. // console.log('window.location', window.location);
  83. var path = window.location.origin + drupalSettings.basepath + "edlp/corpus";
  84. $.getJSON(path, {}, onCorpusLoaded);
  85. };
  86. function onCorpusLoaded(data){
  87. console.log('corpus loaded : data', data);
  88. // console.log('first node', data.nodes[0]);
  89. // buildParticles(data.nodes);
  90. buildNodes(data.nodes);
  91. initEvents();
  92. startAnime();
  93. };
  94. // _ __ __
  95. // / | / /___ ____/ /__ _____
  96. // / |/ / __ \/ __ / _ \/ ___/
  97. // / /| / /_/ / /_/ / __(__ )
  98. // /_/ |_/\____/\__,_/\___/____/
  99. function buildNodes(nodes){
  100. console.log("buildNodes", nodes);
  101. var x,y,d;
  102. for (var i in nodes) {
  103. // console.log('i',i);
  104. d = i < 1 ? true : false;
  105. _nodes.push(new Node(i,nodes[i],d));
  106. // if(i==0)
  107. // break;
  108. }
  109. };
  110. function Node(i,node,d){
  111. this.id = i;
  112. for(key in node)
  113. this[key] = node[key];
  114. this.debug = d;
  115. this.mass = 8;
  116. this.base_radius = _p_radius;
  117. this.g = {
  118. l : 1, // drawing line width
  119. s : 1 // drawing space between squares
  120. };
  121. this.real_radius = this.base_radius + (this.g.l+this.g.s)*this.entrees.length;
  122. this.wall_limits = {
  123. top: _canvas_props.margin_top +this.real_radius,
  124. right: _canvas.width -_canvas_props.margin_right -this.real_radius,
  125. bottom: _canvas.height -_canvas_props.margin_bottom -this.real_radius,
  126. left: _canvas_props.margin_left +this.real_radius
  127. }
  128. this.x = this.wall_limits.left + Math.random()*(this.wall_limits.right - this.wall_limits.left);
  129. this.y = this.wall_limits.top + Math.random()*(this.wall_limits.bottom - this.wall_limits.top);
  130. this.hover = false;
  131. // physics
  132. this.p = _physics.makeParticle(this.mass, this.x, this.y);
  133. this.p.velocity = new Physics.Vector((Math.random()-0.5)*_p_velocity_factor, (Math.random()-0.5)*_p_velocity_factor);
  134. // prototypes
  135. if (typeof Node.initialized == "undefined") {
  136. // Node.prototype.init = function(){
  137. // // console.log("Node : init()", this);
  138. // // this.draw();
  139. // };
  140. Node.prototype.onResizeCanvas = function(){
  141. this.wall_limits = {
  142. top: _canvas_props.margin_top +this.real_radius,
  143. right: _canvas.width -_canvas_props.margin_right -this.real_radius,
  144. bottom: _canvas.height -_canvas_props.margin_bottom -this.real_radius,
  145. left: _canvas_props.margin_left +this.real_radius
  146. }
  147. // TODO: when canvas is smaller what about nodes hors champs, they are coming back alone but it's too long
  148. };
  149. Node.prototype.onUpdate = function(){
  150. this.checkWallBouncing();
  151. this.updatePos();
  152. this.checkMouse();
  153. // if(this.debug)
  154. // console.log("Node pos: ", {x:this.x, y:this.y});
  155. this.draw();
  156. };
  157. Node.prototype.checkWallBouncing = function(){
  158. if(
  159. (this.x < this.wall_limits.left && this.p.velocity.x < 0)
  160. || (this.x > this.wall_limits.right && this.p.velocity.x > 0)
  161. ){
  162. // console.log("bounce x");
  163. this.p.velocity.multiplyScalarXY(-1, 1).multiplyScalar(0.75);
  164. }
  165. if(
  166. (this.y < this.wall_limits.top && this.p.velocity.y < 0)
  167. || (this.y > this.wall_limits.bottom && this.p.velocity.y > 0)
  168. ){
  169. // console.log("bounce y");
  170. this.p.velocity.multiplyScalarXY(1,-1).multiplyScalar(0.75);
  171. }
  172. };
  173. Node.prototype.updatePos = function(){
  174. this.x = this.p.position.x;
  175. this.y = this.p.position.y;
  176. };
  177. Node.prototype.checkMouse = function(){
  178. if( _m_pos.x > this.x - this.real_radius
  179. && _m_pos.x < this.x + this.real_radius
  180. && _m_pos.y > this.y - this.real_radius
  181. && _m_pos.y < this.y + this.real_radius){
  182. if(_node_hover_id == -1 || _node_hover_id == this.id){
  183. console.log("Node hover", this.id);
  184. this.hover = true;
  185. this.highlightEntries();
  186. _node_hover_id = this.id;
  187. }
  188. }else{
  189. this.hover = false;
  190. if (_node_hover_id == this.id) {
  191. _node_hover_id = -1;
  192. _$entrees_block_termlinks.removeClass('highlighted');
  193. }
  194. }
  195. };
  196. Node.prototype.highlightEntries = function(){
  197. _$entrees_block_termlinks.removeClass('highlighted');
  198. var entree;
  199. for (var i = 0; i < this.entrees.length; i++) {
  200. entree = this.entrees[i];
  201. _$entrees_block_termlinks.filter(function(){
  202. return $(this).attr('tid') == entree;
  203. }).addClass('highlighted');
  204. }
  205. }
  206. Node.prototype.draw = function(){
  207. _ctx.beginPath();
  208. _ctx.lineWidth = this.g.l*2;//this.hover ? this.g.l*2 : this.g.l;
  209. var r,d;
  210. for (var i = 0; i < this.entrees.length; i++) {
  211. // _ctx.fillStyle = i == 0 ? _entrees_colors[this.entrees[i]] : "#fff";
  212. // _ctx.strokeStyle = _entrees_colors[this.entrees[i]];
  213. _ctx.fillStyle = i == 0 ? edlp_vars['e_col_'+this.entrees[i]] : "#fff";
  214. _ctx.strokeStyle = edlp_vars['e_col_'+this.entrees[i]];
  215. r = this.base_radius + (this.g.l+this.g.s)*i; // radius
  216. d = r*2; // diametre
  217. if (i == 0) {
  218. _ctx.fillRect(this.x - r,this.y - r,d,d);
  219. }
  220. _ctx.strokeRect(this.x - r,this.y - r,d,d);
  221. }
  222. _ctx.closePath();
  223. // _ctx.beginPath();
  224. // _ctx.fillStyle = this.hover ? "red" : this.debug ? "blue" : "black";
  225. // _ctx.fillRect(this.x, this.y, this.base_radius, this.base_radius);
  226. // _ctx.closePath();
  227. };
  228. Node.initialized = true;
  229. }
  230. // this.init();
  231. };
  232. // ____ __ _
  233. // / __ \/ /_ __ _______(_)_________
  234. // / /_/ / __ \/ / / / ___/ / ___/ ___/
  235. // / ____/ / / / /_/ (__ ) / /__(__ )
  236. // /_/ /_/ /_/\__, /____/_/\___/____/
  237. // /____/
  238. // TODO: how to use particule collision in new OOP Node() contexte
  239. function checkParticulesCollisions(){
  240. // colisions between _particules
  241. for (var p = 0, l = _particules.length; p < l; p++) {
  242. var particule = _particules[p];
  243. // var theta = 360 / (Math.PI * _p_radius);
  244. if(!particule.attracted) continue;
  245. for (var q = 0; q < p; q++) {
  246. if(q==p) continue;
  247. var a = particule;
  248. var b = _particules[q];
  249. if(!b.attracted) continue;
  250. var d = a.distanceTo(b);
  251. // apply new forces if colliding
  252. if(d <= _p_radius*2){
  253. var newVelX1 = (a.velocity.x * (a.mass - b.mass) + (2 * b.mass * b.velocity.x)) / (a.mass + b.mass);
  254. var newVelY1 = (a.velocity.y * (a.mass - b.mass) + (2 * b.mass * b.velocity.y)) / (a.mass + b.mass);
  255. var newVelX2 = (b.velocity.x * (b.mass - a.mass) + (2 * a.mass * a.velocity.x)) / (a.mass + b.mass);
  256. var newVelY2 = (b.velocity.y * (b.mass - a.mass) + (2 * a.mass * a.velocity.y)) / (a.mass + b.mass);
  257. a.velocity.x = newVelX1;
  258. a.velocity.y = newVelY1;
  259. b.velocity.x = newVelX2;
  260. b.velocity.y = newVelY2;
  261. a.velocity.multiplyScalar(0.75);
  262. b.velocity.multiplyScalar(0.75);
  263. // move particles if they overlap
  264. if (d < _p_radius*2) {
  265. var makeup = _p_radius - d/2;
  266. var angle = Math.atan2(b.position.y - a.position.y, b.position.x - a.position.x);
  267. b.position.x += makeup * Math.cos(angle);
  268. b.position.y += makeup * Math.sin(angle);
  269. angle += Math.PI;
  270. a.position.x += makeup * Math.cos(angle);
  271. a.position.y += makeup * Math.sin(angle);
  272. }
  273. }
  274. }
  275. }
  276. };
  277. // ______ __
  278. // / ____/ _____ ____ / /______
  279. // / __/ | | / / _ \/ __ \/ __/ ___/
  280. // / /___ | |/ / __/ / / / /_(__ )
  281. // /_____/ |___/\___/_/ /_/\__/____/
  282. function initEvents(){
  283. _$canvas
  284. .on('mousemove', function(event) {
  285. event.preventDefault();
  286. console.log("onMouseMove");
  287. _m_pos.x = event.originalEvent.clientX;
  288. _m_pos.y = event.originalEvent.clientY;
  289. // console.log("/ _ / - / _ /");
  290. // console.log("Node pos: ", {x:_nodes[0].x, y:_nodes[0].y});
  291. // console.log("Mouse pos: ", {x:_m_pos.x, y:_m_pos.y});
  292. })
  293. .on('click', function(event) {
  294. if(event.target.tagName != "A" && event.target.tagName != "INPUT"){
  295. console.log("Corpus : click", event);
  296. event.preventDefault();
  297. if(_node_hover_id != -1){
  298. console.log("click on node", _nodes[_node_hover_id]);
  299. }
  300. }
  301. });
  302. _$entrees_block_termlinks.on('click', function(event) {
  303. event.preventDefault();
  304. var $li = $(this).parents('li');
  305. if(!$li.is('.opened')){
  306. _$entrees_block_termlinks.parents('li').removeClass('opened');
  307. $li.addClass('opened');
  308. }else{
  309. $li.removeClass('opened');
  310. }
  311. return false;
  312. });
  313. };
  314. // ____ __
  315. // / __ \___ ____ ____/ /__ _____
  316. // / /_/ / _ \/ __ \/ __ / _ \/ ___/
  317. // / _, _/ __/ / / / /_/ / __/ /
  318. // /_/ |_|\___/_/ /_/\__,_/\___/_/
  319. function render(){
  320. _ctx.clearRect(0, 0, _canvas.width, _canvas.height);
  321. for (var i = 0; i < _nodes.length; i++) {
  322. _nodes[i].onUpdate();
  323. }
  324. if(_node_hover_id != -1){
  325. _canvas.style.cursor = 'pointer';
  326. }else{
  327. _canvas.style.cursor = 'auto';
  328. }
  329. };
  330. function startAnime(){
  331. _physics.onUpdate(render);
  332. _physics.play()
  333. };
  334. init();
  335. }
  336. $(document).ready(function($) {
  337. var edlpcorpus = new EdlpCorpus();
  338. });
  339. })(jQuery);