Neuron.php.view.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. var createReactClass = window.p5VendorJs.createReactClass;
  2. var h = window.p5VendorJs.React.createElement;
  3. var ReactDOM = window.p5VendorJs.ReactDOM;
  4. // var AsyncTypeahead = window.p5VendorJs.AsyncTypeahead;
  5. // var swal = window.swal;
  6. if (!HTML_ID) throw "Missing HTML_ID";
  7. // if (!d3) throw "Missing d3 (https://d3js.org/d3.v5.min.js)";
  8. var DBG = DBG || false;
  9. var DBG1 = true;
  10. // <svg height="100" width="100">
  11. // <circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
  12. // </svg>
  13. var p5UI__TestNeuron = createReactClass({
  14. _input: null,
  15. _output: null,
  16. getInitialState: function () {
  17. return {
  18. inputText: INITIAL_DATA || '',
  19. receptor: [],
  20. neuron: [],
  21. output_width: 300,
  22. max_receptor_r: 30,
  23. anim_pos: 0,
  24. anim_last_letter: null,
  25. config_read_speed: 100,
  26. config_charge_receptor_at_input: 1,
  27. config_max_receptor_charge: 5,
  28. config_discharge_per_tick: 0.1,
  29. selectedReceptorIdx: null,
  30. };
  31. },
  32. makeReceptor: function (letter, idx, arr) {
  33. var totalReceptors = arr.length;
  34. var cx = Math.ceil(this.state.output_width / (totalReceptors + 1));
  35. var r = Math.min(Math.ceil(this.state.output_width / (totalReceptors + 10) / 2), this.state.max_receptor_r);
  36. var xCenter = cx + cx * idx - r;
  37. return {
  38. letter: letter,
  39. charge: 0,
  40. x: xCenter,
  41. y: 20,
  42. r: r,
  43. }
  44. },
  45. dischargeNeuron: function (neuron) {
  46. return (neuron.charge > 0)
  47. ? Object.assign({}, neuron, {
  48. charge: Math.max(0, neuron.charge - this.state.config_discharge_per_tick),
  49. })
  50. : neuron
  51. ;
  52. },
  53. handleChangeInput: function (event) {
  54. this.setState({
  55. inputText: event.target.value,
  56. })
  57. },
  58. setInputRet: function (reactEl) { this._input = reactEl; },
  59. setOutputRet: function (reactEl) { this._output = reactEl; },
  60. // d3.selectAll("circle").transition()
  61. // .duration(750)
  62. // .delay(function (d, i) { return i * 10; })
  63. // .attr("r", function (d) { return Math.sqrt(d * scale); });
  64. componentDidMount: function () {
  65. this.startAnimation();
  66. },
  67. handleExec: function (event) {
  68. event.preventDefault();
  69. DBG1 && console.log('DBG:handleExec...');
  70. this.startAnimation();
  71. },
  72. startAnimation: function (event) {
  73. var distinct = function (value, idx, self) {
  74. return ( idx === self.indexOf(value) );
  75. };
  76. // var foundLetters = this.state.inputText.split("\n").reduce(function (ret, line) {
  77. // return ret.concat(
  78. // line.split('').filter(distinct)
  79. // ).filter(distinct);
  80. // }, []);
  81. var foundLetters = this.state.inputText.split("").filter(distinct);
  82. DBG1 && console.log('DBG:handleExec:foundLetters', { foundLetters });
  83. this.setState({
  84. anim_pos: 0,
  85. receptor: foundLetters.map(this.makeReceptor),
  86. neuron: [],
  87. });
  88. setTimeout(this.forwardAnim, this.state.config_read_speed)
  89. },
  90. getReceptor: function (char) {
  91. var receptor = this.state.receptor.filter(function (receptor) {
  92. return (char == receptor.letter);
  93. });
  94. if (!receptor.length) throw "BUG: no input receptor found for char '" + char + "'"; // TODO: create on demand?
  95. return receptor[0];
  96. },
  97. getNeuron: function (value) {
  98. var neuron = this.state.neuron.filter(function (neuron) {
  99. return (value == neuron.letter);
  100. });
  101. return (neuron.length) ? neuron[0] : null;
  102. },
  103. makeNueron: function (listNeuron, letter, last_letter) {
  104. var receptor = this.getReceptor(letter);
  105. var prevReceptor = this.getReceptor(last_letter);
  106. var value = '' + letter + last_letter;
  107. var findNeuron = this.getNeuron(value);
  108. var charge = (receptor.charge + prevReceptor.charge) / 2;
  109. if (!findNeuron) {
  110. return listNeuron.concat([
  111. {
  112. value: value,
  113. charge: charge,
  114. x: (receptor.x + prevReceptor.x) / 2,
  115. y: 20 + 20 * value.length,
  116. rx: 10 * value.length,
  117. ry: 10,
  118. source1_x: receptor.x,
  119. source1_y: receptor.y,
  120. source2_x: prevReceptor.x,
  121. source2_y: prevReceptor.y,
  122. }
  123. ])
  124. } else {
  125. return listNeuron.map(function (neuron) {
  126. (neuron.value === value)
  127. ? Object.assign({}, neuron, { charge: neuron.charge + (receptor.charge + prevReceptor.charge) / 2 })
  128. : neuron
  129. ;
  130. })
  131. }
  132. },
  133. forwardAnim: function () {
  134. if (this.state.anim_pos + 1 >= this.state.inputText.length) {
  135. DBG1 && console.log("DBG:anim STOP", { inputLength: this.state.inputText.length, anim_pos: this.state.anim_pos });
  136. return;
  137. }
  138. var anim_pos = this.state.anim_pos + 1;
  139. var char = this.state.inputText.charAt(anim_pos)
  140. DBG1 && console.log("DBG:anim", { anim_pos, char, len: this.state.inputText.length });
  141. // 1. charge receptor with letter `char`
  142. var neuron = this.state.neuron;
  143. var _chargeReceptor = this.state.config_charge_receptor_at_input;
  144. var toChangeReceptor = this.getReceptor(char)
  145. DBG1 && console.log("DBG:anim", { anim_pos, char, len: this.state.inputText.length, toChangeReceptor });
  146. toChangeReceptor.charge += _chargeReceptor;
  147. var receptor = this.state.receptor.map(function (receptor) {
  148. return (char !== receptor.letter) ? receptor : toChangeReceptor;
  149. })
  150. // 2. check if any receptor is over charged
  151. if (toChangeReceptor.charge > this.state.config_max_receptor_charge) {
  152. if (null !== this.state.anim_last_letter && this.state.anim_last_letter !== toChangeReceptor.letter) {
  153. DBG1 && console.log("DBG:check overcharged", { toChangeReceptor, last_letter: this.state.anim_last_letter });
  154. neuron = this.makeNueron(neuron, toChangeReceptor.letter, this.state.anim_last_letter);
  155. // discharge receptors to 0
  156. var toDischarge = [toChangeReceptor.letter, this.state.anim_last_letter];
  157. receptor = receptor.map(function (receptor) {
  158. return (-1 !== toDischarge.indexOf(receptor.letter))
  159. ? Object.assign({}, receptor, { charge: 0 })
  160. : receptor
  161. ;
  162. })
  163. }
  164. }
  165. // 3. discharge all receptors and nodes by 0.1 (config_discharge_per_tick)
  166. receptor = receptor.map(this.dischargeNeuron)
  167. neuron = neuron.map(this.dischargeNeuron)
  168. // 4. create neuron if needed
  169. this.setState({
  170. anim_last_letter: char,
  171. anim_pos: anim_pos,
  172. receptor: receptor,
  173. neuron: neuron,
  174. });
  175. setTimeout(this.forwardAnim, this.state.config_read_speed)
  176. },
  177. selectReceptor: function (receptorIdx) {
  178. this.setState({ selectedReceptorIdx: receptorIdx })
  179. },
  180. handleClickReceptor: function (event) {
  181. var data_receptor_idx = event.target.getAttribute('data_receptor_idx')
  182. DBG1 && console.log('DBG:handleClickReceptor', { data_receptor_idx, target: event.target });
  183. this.selectReceptor(data_receptor_idx)
  184. },
  185. renderConnections: function (neuron, idx) {
  186. return h('g', {}, [
  187. h('line', {
  188. x1: neuron.source1_x, y1: neuron.source1_y,
  189. x2: neuron.x, y2: neuron.y,
  190. style: {
  191. stroke: "#46b8da",
  192. strokeWidth: "2"
  193. },
  194. }),
  195. h('line', {
  196. x1: neuron.source2_x, y1: neuron.source2_y,
  197. x2: neuron.x, y2: neuron.y,
  198. style: {
  199. stroke: "#46b8da",
  200. strokeWidth: "2"
  201. },
  202. }),
  203. ]);
  204. },
  205. renderNeuron: function (neuron, idx) {
  206. var chargePr = (100 * neuron.charge) / this.state.config_max_receptor_charge;
  207. var fontColor = (chargePr < 50) ? "#000" : "#fff";
  208. return h('g', {}, [
  209. h('ellipse', {
  210. cx: neuron.x, cy: neuron.y, rx: neuron.rx, ry: neuron.ry, stroke: "#46b8da", strokeWidth: 1,
  211. fill: (chargePr > 100) ? "#f00" : "hsl(194, 67%, " + (100 - chargePr) + "%)",
  212. // data_neuron_idx: idx,
  213. // onClick: this.handleClickNeuron,
  214. style: { cursor: "pointer" },
  215. }, [
  216. h('title', {}, this.viewNeuronTitle(neuron.value)),
  217. ]),
  218. h('text', {
  219. x: neuron.x, y: neuron.y + 1, fill: fontColor,
  220. dominantBaseline: "middle", textAnchor: "middle",
  221. style: { fontSize: "10px", cursor: "pointer" },
  222. // data_neuron_idx: idx,
  223. // onClick: this.handleClickNeuron,
  224. }, this.viewNeuronValue(neuron.value)),
  225. ]);
  226. },
  227. viewNeuronValue: function (value) {
  228. return value
  229. .replace(new RegExp("\n", "g"), "\\n")
  230. .replace(new RegExp("\t", "g"), "\\t")
  231. .replace(new RegExp(" ", "g"), "_")
  232. ;
  233. },
  234. viewNeuronTitle: function (value) {
  235. return this.viewNeuronValue(value);
  236. },
  237. renderReceptor: function (receptor, idx) {
  238. // var totalReceptors = this.state.receptor.length;
  239. // var cx = Math.ceil(this.state.output_width / ( totalReceptors + 1 ));
  240. // var r = Math.min(Math.ceil(this.state.output_width / ( totalReceptors + 10 ) / 2), this.state.max_receptor_r);
  241. // DBG && console.log('DBG:renderReceptor', { receptor, idx, totalReceptors, r });
  242. // var xCenter = cx + cx * idx - r;
  243. var chargePr = (100 * receptor.charge) / this.state.config_max_receptor_charge;
  244. var fontColor = (chargePr < 50) ? "#000" : "#fff";
  245. // this.state.config_max_receptor_charge = 100%
  246. // receptor.charge = x% (max 100)
  247. return h('g', {}, [
  248. h('circle', {
  249. cx: receptor.x, cy: receptor.y, r: receptor.r, stroke: "#46b8da", strokeWidth: 1,
  250. // fill: "#5bc0de"
  251. // (receptor.charge > this.state.config_max_receptor_charge)
  252. fill: (chargePr > 100) ? "#f00" : "hsl(194, 67%, " + (100 - chargePr) + "%)",
  253. data_receptor_idx: idx,
  254. onClick: this.handleClickReceptor,
  255. style: { cursor: "pointer" },
  256. }, [
  257. h('title', {}, this.viewReceptorTitle(receptor.letter)),
  258. ]),
  259. h('text', {
  260. x: receptor.x, y: receptor.y + 1, fill: fontColor,
  261. dominantBaseline: "middle", textAnchor: "middle",
  262. style: { fontSize: "10px", cursor: "pointer" },
  263. data_receptor_idx: idx,
  264. onClick: this.handleClickReceptor,
  265. }, this.viewReceptorLetter(receptor.letter)),
  266. ]);
  267. },
  268. viewReceptorLetter: function (letter) {
  269. switch (letter) {
  270. case "\t": return "\\t";
  271. case " ": return "_";
  272. case "\n": return "\\n";
  273. default: return letter;
  274. }
  275. },
  276. viewReceptorTitle: function (letter) {
  277. switch (letter) {
  278. case "\t": return "tab";
  279. case " ": return "space";
  280. case "\n": return "new line";
  281. default: return letter;
  282. }
  283. },
  284. renderReceptorInfo: function () {
  285. if (null === this.state.selectedReceptorIdx) return null;
  286. var receptor = this.state.receptor[this.state.selectedReceptorIdx];
  287. return h('div', {}, [
  288. "Receptor: '" + this.viewReceptorTitle(receptor.letter) + "' charge('" + receptor.charge.toFixed(2) + "')",
  289. ]);
  290. },
  291. renderAllReceptorInfo: function () {
  292. return h('div', {}, this.state.receptor.map(this.renderAllReceptorInfo_item));
  293. },
  294. renderAllReceptorInfo_item: function (receptor) {
  295. return h('div', {}, ["Receptor: '" + this.viewReceptorTitle(receptor.letter) + "' charge('" + receptor.charge.toFixed(2) + "')"]);
  296. },
  297. renderAllNeuronInfo: function () {
  298. return h('div', {}, this.state.neuron.map(this.renderAllNeuronInfo_item));
  299. },
  300. renderAllNeuronInfo_item: function (neuron) {
  301. return h('div', {}, ["Neuron: '" + neuron.value + "' charge('" + neuron.charge.toFixed(2) + "')"]);
  302. },
  303. renderInput: function () {
  304. return h('details', { open: true, style: { margin: "12px 0", border: "1px solid #aaa", borderRadius: "6px", padding: "12px", backgroundColor: "#eee", } }, [
  305. h('summary', { style: { cursor: "pointer" } }, "Input"),
  306. h('textarea', {
  307. ref: this.setInputRet,
  308. onChange: this.handleChangeInput,
  309. value: this.state.inputText,
  310. rows: 10,
  311. style: {
  312. width: "100%",
  313. padding: "12px",
  314. marginTop: "6px",
  315. backgroundColor: "#fff",
  316. },
  317. }),
  318. ]);
  319. },
  320. renderConfig: function () {
  321. return h('details', { open: true, style: { margin: "12px 0", border: "1px solid #aaa", borderRadius: "6px", padding: "12px", backgroundColor: "#eee", } }, [
  322. h('summary', { style: { cursor: "pointer" } }, "Config"),
  323. h('div', { style: { padding: "12px", backgroundColor: "#fff", } }, [
  324. h('div', {}, "Read speed:"),
  325. h('input', {
  326. className: "form-control input-sm",
  327. type: "number",
  328. value: this.state.config_read_speed,
  329. name: "config_read_speed",
  330. onChange: this.handleChangeConfig,
  331. }),
  332. h('div', { style: { marginTop: "12px" } }),
  333. h('div', {}, "Charge receptor at input:"),
  334. h('input', {
  335. className: "form-control input-sm",
  336. type: "number",
  337. value: this.state.config_charge_receptor_at_input,
  338. name: "config_charge_receptor_at_input",
  339. onChange: this.handleChangeConfig,
  340. }),
  341. h('div', { style: { marginTop: "12px" } }),
  342. h('div', {}, "Max receptor charge:"),
  343. h('input', {
  344. className: "form-control input-sm",
  345. type: "number",
  346. value: this.state.config_max_receptor_charge,
  347. name: "config_max_receptor_charge",
  348. onChange: this.handleChangeConfig,
  349. }),
  350. h('div', { style: { marginTop: "12px" } }),
  351. h('div', {}, "Discharge per tick:"),
  352. h('input', {
  353. className: "form-control input-sm",
  354. type: "number",
  355. step: "0.1",
  356. value: this.state.config_discharge_per_tick,
  357. name: "config_discharge_per_tick",
  358. onChange: this.handleChangeConfig,
  359. }),
  360. ]),
  361. ]);
  362. },
  363. handleChangeConfig: function (event) {
  364. var value = event.target.value;
  365. var name = event.target.getAttribute('name');
  366. var state = this.state;
  367. state[name] = value;
  368. this.setState(state);
  369. },
  370. render: function () {
  371. return h('div', {}, [
  372. h('table', { className: "table table-border" }, [
  373. h('tbody', {}, [
  374. h('tr', {}, [
  375. h('td', { style: { width: this.state.output_width + 20 } }, [
  376. h('svg', { ref: this.setOutputRet, height: 300, width: this.state.output_width, style: { border: "1px solid #eee" } }, [
  377. this.state.neuron.map(this.renderConnections),
  378. this.state.receptor.map(this.renderReceptor),
  379. this.state.neuron.map(this.renderNeuron),
  380. ]),
  381. ]),
  382. h('td', { style: { verticalAlign: "top" } }, [
  383. this.renderReceptorInfo(),
  384. ]),
  385. h('td', { style: { verticalAlign: "top" } }, [
  386. this.renderAllReceptorInfo(),
  387. this.renderAllNeuronInfo(),
  388. ]),
  389. ]),
  390. ]),
  391. ]),
  392. h('button', { className: "btn btn-primary", onClick: this.handleExec }, "Uruchom"),
  393. h('table', { className: "table table-border" }, [
  394. h('tbody', {}, [
  395. h('tr', {}, [
  396. h('td', { style: { width: "50%" } }, [
  397. this.renderInput(),
  398. ]),
  399. h('td', { style: { width: "50%" } }, [
  400. this.renderConfig(),
  401. ]),
  402. ]),
  403. ]),
  404. ]),
  405. h('div', {}, [
  406. "TODO: receptory...",
  407. ]),
  408. ]);
  409. }
  410. });
  411. ReactDOM.render(h(p5UI__TestNeuron
  412. ), document.getElementById(HTML_ID))