Neuron.php.view.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  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 DEFAULT_CONFIG = {
  14. ui_output_width: 800,
  15. ui_max_receptor_r: 12,
  16. ui_space_y: 30,
  17. config_read_speed: 500,
  18. config_charge_receptor_at_input: 1,
  19. config_max_receptor_charge: 1.5,
  20. config_discharge_per_tick: 0.1,
  21. config_discharge_max_in_new_neuron_from_one: 0.7,
  22. }
  23. var DEFAULT_NEURON = {
  24. value: '',
  25. charge: 0,
  26. maxCharge: DEFAULT_CONFIG.config_max_receptor_charge,
  27. uiShape: "circle",
  28. ui: { cx: 0, cy: 0 },
  29. source: [],
  30. next: [],
  31. }
  32. function state__getInitial(state, letters) {
  33. function makeReceptor(letter, idx, arr) {
  34. var totalReceptors = arr.length;
  35. var cx = Math.ceil(state.ui_output_width / (totalReceptors + 1));
  36. var r = Math.min(Math.ceil(state.ui_output_width / (totalReceptors + 10) / 2), state.ui_max_receptor_r);
  37. var xCenter = cx + cx * idx - r;
  38. return Object.assign({}, DEFAULT_NEURON, {
  39. value: letter,
  40. maxCharge: state.config_max_receptor_charge,
  41. uiShape: "circle",
  42. ui: {
  43. cx: xCenter,
  44. cy: 40,
  45. r: r,
  46. },
  47. next: [],
  48. })
  49. }
  50. return {
  51. receptor: letters.map(makeReceptor),
  52. }
  53. }
  54. function state__readFromInput(state, char) {
  55. // 1. charge receptor with letter `char`
  56. var listNeuron = state.neuron;
  57. var listReceptor = state.receptor;
  58. var mapLetterToReceptor = {}
  59. for (var i = 0, l = listReceptor.length; i < l; i++) {
  60. mapLetterToReceptor[listReceptor[i].value] = listReceptor[i];
  61. }
  62. // var toChangeReceptor = getReceptor(listReceptor, char)
  63. var toChangeReceptor = mapLetterToReceptor[char]
  64. DBG && console.log("DBG:anim", { char, len: state.inputText.length, toChangeReceptor, mapLetterToReceptor: mapLetterToReceptor });
  65. toChangeReceptor.charge += state.config_charge_receptor_at_input;
  66. // 2. check if any receptor is over charged
  67. if (toChangeReceptor.charge > state.config_max_receptor_charge) {
  68. var closestNodeWithCharge = findReceptorClosestNodeWithCharge(state, toChangeReceptor)
  69. DBG1 && console.log("DBG:overcharge.1:", { char, len: state.inputText.length, closestNodeWithCharge, receptor: state.receptor });
  70. if (!closestNodeWithCharge) {
  71. { // TODO: export to funtion makeNeuronFromOneNode(state, listReceptor, listNeuron, toChangeReceptor)
  72. var neuron = makeNeuronFromOneNode(state, toChangeReceptor)
  73. // TODO: neuron may already exists? check neuron.source if already has defined next
  74. var alreadyExistNeuron = toChangeReceptor.next.filter(function (nextNeuron) {
  75. return (nextNeuron.source.length === 1);
  76. })
  77. DBG1 && console.log("DBG:overcharge.2:", { alreadyExistNeuron });
  78. if (alreadyExistNeuron.length) {
  79. alreadyExistNeuron[0].charge += toChangeReceptor.charge
  80. toChangeReceptor.charge = 0
  81. } else {
  82. toChangeReceptor.next.push(neuron)
  83. listNeuron.push(neuron)
  84. toChangeReceptor.charge = 0
  85. }
  86. }
  87. } else { // (closestNodeWithCharge)
  88. { // TODO: export to funtion makeNeuronFromTwoNodes(state, listReceptor, listNeuron, toChangeReceptor, closestNodeWithCharge)
  89. var neuron = makeNeuronFromTwoNodes(state, toChangeReceptor, closestNodeWithCharge)
  90. // TODO: neuron may already exists? check neuron.source if already has defined next
  91. var alreadyExistNeuron = toChangeReceptor.next.filter(function (nextNeuron) {
  92. return (nextNeuron.source.length === 2 && (
  93. (nextNeuron.source[0].value === toChangeReceptor.value && nextNeuron.source[1].value === closestNodeWithCharge.value)
  94. || (nextNeuron.source[0].value === closestNodeWithCharge.value && nextNeuron.source[1].value === toChangeReceptor.value)
  95. ));
  96. })
  97. DBG1 && console.log("DBG:overcharge.2:", { alreadyExistNeuron });
  98. if (alreadyExistNeuron.length) {
  99. alreadyExistNeuron[0].charge += toChangeReceptor.charge + closestNodeWithCharge.charge
  100. toChangeReceptor.charge = 0
  101. closestNodeWithCharge.charge = 0
  102. } else {
  103. toChangeReceptor.next.push(neuron)
  104. closestNodeWithCharge.next.push(neuron)
  105. listNeuron.push(neuron)
  106. toChangeReceptor.charge = 0
  107. closestNodeWithCharge.charge = 0
  108. }
  109. }
  110. }
  111. // TODO: dont use last letter -- use findReceptorClosestNodeWithCharge
  112. // if no closest neuron with charge then create new neuron over receptor
  113. // if (null !== state.animLastLetter && state.animLastLetter !== toChangeReceptor.letter) {
  114. // DBG1 && console.log("DBG:check overcharged", { toChangeReceptor, closestNodeWithCharge: closestNodeWithCharge, last_letter: state.animLastLetter });
  115. // listNeuron = makeNueron(listReceptor, listNeuron, toChangeReceptor.letter, state.animLastLetter);
  116. // // discharge receptors to 0
  117. // var toDischarge = [toChangeReceptor.letter, state.animLastLetter];
  118. // listReceptor = listReceptor.map(function (receptor) {
  119. // return (-1 !== toDischarge.indexOf(receptor.letter))
  120. // ? Object.assign({}, receptor, { charge: 0 })
  121. // : receptor
  122. // ;
  123. // })
  124. // }
  125. }
  126. // 3. discharge all receptors and nodes by 0.1 (config_discharge_per_tick)
  127. var dischargeNode = makeDischargeNodeFun(state);
  128. return {
  129. receptor: listReceptor.map(dischargeNode),
  130. neuron: listNeuron.map(dischargeNode),
  131. }
  132. }
  133. function state__fromOverchargedNeurons(state, overchargedNeurons) {
  134. var listNeuron = state.neuron;
  135. var listReceptor = state.receptor;
  136. overchargedNeurons = overchargedNeurons.sort(sortNeuronByChargeCallback)
  137. DBG1 && console.log("DBG:state:overcharged", { overchargedNeurons })
  138. if (overchargedNeurons.length === 1) {
  139. var toChangeReceptor = overchargedNeurons[0]
  140. { // TODO: export to funtion makeNeuronFromOneNode(state, listReceptor, listNeuron, toChangeReceptor)
  141. var neuron = makeNeuronFromOneNode(state, toChangeReceptor)
  142. // TODO: neuron may already exists? check neuron.source if already has defined next
  143. var alreadyExistNeuron = toChangeReceptor.next.filter(function (nextNeuron) {
  144. return (nextNeuron.source.length === 1);
  145. })
  146. DBG1 && console.log("DBG:overcharge.2:", { alreadyExistNeuron });
  147. if (alreadyExistNeuron.length) {
  148. alreadyExistNeuron[0].charge += toChangeReceptor.charge
  149. toChangeReceptor.charge = 0
  150. } else {
  151. toChangeReceptor.next.push(neuron)
  152. listNeuron.push(neuron)
  153. toChangeReceptor.charge = 0
  154. }
  155. }
  156. } else {
  157. var toChangeReceptor = overchargedNeurons[0]
  158. var closestNodeWithCharge = overchargedNeurons[1]
  159. { // TODO: export to funtion makeNeuronFromTwoNodes(state, listReceptor, listNeuron, toChangeReceptor, closestNodeWithCharge)
  160. var neuron = makeNeuronFromTwoNodes(state, toChangeReceptor, closestNodeWithCharge)
  161. // TODO: neuron may already exists? check neuron.source if already has defined next
  162. var alreadyExistNeuron = toChangeReceptor.next.filter(function (nextNeuron) {
  163. return (nextNeuron.source.length === 2 && (
  164. (nextNeuron.source[0].value === toChangeReceptor.value && nextNeuron.source[1].value === closestNodeWithCharge.value)
  165. || (nextNeuron.source[0].value === closestNodeWithCharge.value && nextNeuron.source[1].value === toChangeReceptor.value)
  166. ));
  167. })
  168. DBG1 && console.log("DBG:overcharge.2:", { alreadyExistNeuron });
  169. if (alreadyExistNeuron.length) {
  170. alreadyExistNeuron[0].charge += toChangeReceptor.charge + closestNodeWithCharge.charge
  171. toChangeReceptor.charge = 0
  172. closestNodeWithCharge.charge = 0
  173. } else {
  174. toChangeReceptor.next.push(neuron)
  175. closestNodeWithCharge.next.push(neuron)
  176. listNeuron.push(neuron)
  177. toChangeReceptor.charge = 0
  178. closestNodeWithCharge.charge = 0
  179. }
  180. }
  181. }
  182. return {
  183. receptor: listReceptor,
  184. neuron: listNeuron,
  185. }
  186. }
  187. function sortNeuronByChargeCallback(n1, n2) {
  188. if (n1.charge > n2.charge) return 1;
  189. if (n1.charge < n2.charge) return -1;
  190. return 0;
  191. }
  192. function getReceptor(listReceptor, char) {
  193. var receptor = listReceptor.filter(function (receptor) {
  194. return (char == receptor.value);
  195. });
  196. if (!receptor.length) throw "BUG: no input receptor found for char '" + char + "'"; // TODO: create on demand?
  197. return receptor[0];
  198. }
  199. function findReceptorClosestNodeWithCharge(state, node) {
  200. var skipSelfValue = node.value;
  201. var maxChargedReceptor = state.receptor.reduce(function (ret, receptor) {
  202. if (receptor.value === skipSelfValue) return ret;
  203. if (0 === receptor.charge) return ret;
  204. if (!ret) return receptor;
  205. return (receptor.charge > ret.charge) ? receptor : ret;
  206. }, null)
  207. return maxChargedReceptor;
  208. }
  209. function makeNeuronFromOneNode(state, sourceNode) {
  210. return Object.assign({}, DEFAULT_NEURON, {
  211. value: '' + (state.neuron.length + 1),
  212. charge: 0, // sourceNode.charge,
  213. maxCharge: sourceNode.maxCharge * state.config_discharge_max_in_new_neuron_from_one, // maybe?: sourceNode.charge
  214. uiShape: "ellipse",
  215. ui: {
  216. cx: sourceNode.ui.cx,
  217. cy: sourceNode.ui.cy + 10 + state.ui_space_y,
  218. rx: 10,
  219. ry: 10,
  220. },
  221. source: [sourceNode],
  222. });
  223. }
  224. function makeNeuronFromTwoNodes(state, first, second) {
  225. var value = '' + first.value + second.value;
  226. return Object.assign({}, DEFAULT_NEURON, {
  227. value: value,
  228. charge: 0, // (first.charge + second.charge) / 2,
  229. maxCharge: ((first.maxCharge + second.maxCharge) / 2) * state.config_discharge_max_in_new_neuron_from_one,
  230. uiShape: "ellipse",
  231. ui: {
  232. cx: (first.ui.cx + second.ui.cx) / 2,
  233. cy: first.ui.cy + 10 + state.ui_space_y,
  234. rx: 10 * value.length,
  235. ry: 10,
  236. },
  237. source: [first, second],
  238. });
  239. }
  240. function getNeuron(listNeuron, value) {
  241. var neuron = listNeuron.filter(function (neuron) {
  242. return (value == neuron.letter);
  243. });
  244. return (neuron.length) ? neuron[0] : null;
  245. }
  246. function makeDischargeNodeFun(state) {
  247. return function dischargeNode(neuron) {
  248. return (neuron.charge > 0)
  249. ? Object.assign({}, neuron, {
  250. charge: Math.max(0, neuron.charge - state.config_discharge_per_tick),
  251. })
  252. : neuron
  253. ;
  254. }
  255. }
  256. function isNeuronOvercharged(neuron) {
  257. return (neuron.charge > neuron.maxCharge);
  258. }
  259. function LOG__render(listLogEntries) {
  260. return h('details', { open: false, style: { margin: "12px 0", border: "1px solid #aaa", borderRadius: "6px", padding: "12px", backgroundColor: "#eee", } }, [
  261. h('summary', { style: { cursor: "pointer" } }, "Log"),
  262. h('div', { style: { border: "1px solid #eee", backgroundColor: "#fff" } }, [
  263. h('table', { className: "table table-border" }, [
  264. h('thead', {}, LOG__renderTheadLog(listLogEntries)),
  265. h('tbody', {}, LOG__renderTbodyLog(listLogEntries)),
  266. ]),
  267. ]),
  268. ]);
  269. }
  270. function LOG__renderTheadLog(listLogEntries) {
  271. if (!listLogEntries.length) return null;
  272. var lastLog = listLogEntries[listLogEntries.length - 1]
  273. var colsR = lastLog.receptor.map(function (node) { return node.value; })
  274. var colsN = lastLog.neuron.map(function (node) { return node.value; })
  275. return [
  276. h('tr', {},
  277. [h('th', {}, "Lp.")]
  278. .concat(
  279. colsR.map(function (label) { return h('th', {}, "R:'" + label + "'"); })
  280. )
  281. .concat(
  282. colsN.map(function (label) { return h('th', {}, "N:'" + label + "'"); })
  283. )
  284. )
  285. ];
  286. }
  287. function LOG__renderTbodyLog(listLogEntries) {
  288. if (!listLogEntries.length) return null;
  289. var lastLog = listLogEntries[listLogEntries.length - 1]
  290. var colsR = lastLog.receptor.map(function (node) { return node.value; })
  291. var colsN = lastLog.neuron.map(function (node) { return node.value; })
  292. return listLogEntries.map(function (log, lpLog) {
  293. var row = colsR.map(function (label, idx) { return log.receptor[idx].charge; })
  294. .concat(
  295. colsN.map(function (label, idx) { return (log.neuron[idx]) ? log.neuron[idx].charge : null; })
  296. )
  297. return h('tr', {}, [h('th', { style: { color: "#ddd" } }, '' + (lpLog + 1) + '.')]
  298. .concat(
  299. row.map(LOG__renderCellNodeChargeLog)
  300. )
  301. )
  302. })
  303. }
  304. function LOG__renderCellNodeChargeLog(charge) {
  305. if (null === charge) return null;
  306. return h('td', {
  307. style: {
  308. color: (charge > 0) ? "#000" : "#aaa",
  309. },
  310. }, charge.toFixed(2));
  311. }
  312. var NeuronView = createReactClass({
  313. _input: null,
  314. _output: null,
  315. _animTimoutID: null,
  316. _log: [],
  317. getInitialState: function () {
  318. return Object.assign({}, DEFAULT_CONFIG, {
  319. inputText: this.props.initialData || '', // TODO: INITIAL_DATA
  320. receptor: [],
  321. neuron: [],
  322. doAnim: false,
  323. animPos: 0,
  324. animLastLetter: null,
  325. selectedReceptorIdx: null,
  326. });
  327. },
  328. handleChangeInput: function (event) {
  329. this.setState({
  330. inputText: event.target.value,
  331. })
  332. },
  333. setInputRet: function (reactEl) { this._input = reactEl; },
  334. setOutputRet: function (reactEl) { this._output = reactEl; },
  335. componentDidMount: function () {
  336. this.startAnimation();
  337. },
  338. handleExec: function (event) {
  339. event.preventDefault();
  340. DBG1 && console.log('DBG:handleExec...');
  341. if (this._animTimoutID) clearTimeout(this._animTimoutID);
  342. this.setState({
  343. neuron: [],
  344. receptor: [],
  345. })
  346. setTimeout(this.startAnimation, this.state.config_read_speed * 2)
  347. },
  348. handlePause: function (event) {
  349. event.preventDefault();
  350. this.setState({ doAnim: false })
  351. },
  352. handleStart: function (event) {
  353. event.preventDefault();
  354. this.forwardAnim(true)
  355. },
  356. startAnimation: function (event) {
  357. var distinct = function (value, idx, self) {
  358. return (idx === self.indexOf(value));
  359. };
  360. // var foundLetters = this.state.inputText.split("\n").reduce(function (ret, line) {
  361. // return ret.concat(
  362. // line.split('').filter(distinct)
  363. // ).filter(distinct);
  364. // }, []);
  365. var foundLetters = this.state.inputText.split("").filter(distinct);
  366. DBG1 && console.warn('DBG:handleExec:foundLetters', { foundLetters });
  367. this._log = []
  368. this.setState(Object.assign(
  369. {},
  370. state__getInitial(this.state, foundLetters),
  371. {
  372. animPos: 0,
  373. neuron: [],
  374. doAnim: true,
  375. }
  376. ), function () {
  377. console.warn({
  378. receptor: this.state.receptor.map(function (n) { return n }),
  379. neuron: this.state.receptor.map(function (n) { return n }),
  380. })
  381. }.bind(this));
  382. setTimeout(this.forwardAnim, this.state.config_read_speed)
  383. },
  384. getReceptor: function (char) {
  385. var receptor = this.state.receptor.filter(function (receptor) {
  386. return (char == receptor.value);
  387. });
  388. if (!receptor.length) throw "BUG: no input receptor found for char '" + char + "'"; // TODO: create on demand?
  389. return receptor[0];
  390. },
  391. forwardAnim: function (forceUpdateDoAnim) {
  392. this._logState(this.state)
  393. var doAnim = (arguments.length > 0) ? forceUpdateDoAnim : this.state.doAnim;
  394. var animPos = this.state.animPos;
  395. var overchargedNeurons = this.state.neuron.filter(isNeuronOvercharged)
  396. if (overchargedNeurons.length === 0) {
  397. animPos += 1;
  398. } else {
  399. DBG1 && console.log("DBG:anim TODO: neuron overcharged", { overchargedNeurons });
  400. }
  401. if (animPos >= this.state.inputText.length) {
  402. DBG1 && console.log("DBG:anim STOP - end of input", { inputLength: this.state.inputText.length, animPos: this.state.animPos });
  403. return;
  404. }
  405. var char = this.state.inputText.charAt(animPos)
  406. DBG && console.log("DBG:anim", { animPos, char, len: this.state.inputText.length, doAnim });
  407. this.setState(Object.assign(
  408. {},
  409. (overchargedNeurons.length) ? state__fromOverchargedNeurons(this.state, overchargedNeurons) : state__readFromInput(this.state, char),
  410. {
  411. animLastLetter: char,
  412. animPos: animPos,
  413. doAnim: doAnim,
  414. }
  415. ));
  416. if (doAnim) {
  417. this._animTimoutID = setTimeout(this.forwardAnim, this.state.config_read_speed)
  418. }
  419. },
  420. _logState: function (state) {
  421. function getNodeInfo(node) {
  422. return {
  423. value: node.value,
  424. charge: node.charge,
  425. }
  426. }
  427. this._log.push({
  428. receptor: state.receptor.map(getNodeInfo),
  429. neuron: state.neuron.map(getNodeInfo),
  430. })
  431. },
  432. selectReceptor: function (receptorIdx) {
  433. this.setState({ selectedReceptorIdx: receptorIdx })
  434. },
  435. handleClickReceptor: function (event) {
  436. var data_receptor_idx = event.target.getAttribute('data_receptor_idx')
  437. DBG1 && console.log('DBG:handleClickReceptor', { data_receptor_idx, target: event.target });
  438. this.selectReceptor(data_receptor_idx)
  439. },
  440. renderConnections: function (neuron, idx) {
  441. DBG1 && console.log('DBG:renderConnections', { neuron, p1: neuron.source[0].ui, p2: (neuron.source.length > 1) ? neuron.source[1].ui : null });
  442. return h('g', {}, [
  443. h('line', {
  444. x1: neuron.source[0].ui.cx, y1: neuron.source[0].ui.cy,
  445. x2: neuron.ui.cx, y2: neuron.ui.cy,
  446. style: {
  447. stroke: "#46b8da",
  448. strokeWidth: "2"
  449. },
  450. }),
  451. (neuron.source.length > 1)
  452. ? h('line', {
  453. x1: neuron.source[1].ui.cx, y1: neuron.source[1].ui.cy,
  454. x2: neuron.ui.cx, y2: neuron.ui.cy,
  455. style: {
  456. stroke: "#46b8da",
  457. strokeWidth: "2"
  458. },
  459. })
  460. : null,
  461. ]);
  462. },
  463. renderNeuron: function (neuron, idx) {
  464. var chargePr = (100 * neuron.charge) / this.state.config_max_receptor_charge;
  465. var fontColor = (chargePr < 50) ? "#000" : "#fff";
  466. return h('g', {}, [
  467. h(neuron.uiShape, Object.assign({}, neuron.ui, {
  468. stroke: "#46b8da", strokeWidth: 1,
  469. fill: (chargePr > 100) ? "#f00" : "hsl(194, 67%, " + (100 - chargePr) + "%)",
  470. // data_neuron_idx: idx,
  471. // onClick: this.handleClickNeuron,
  472. style: { cursor: "pointer" },
  473. }), [
  474. h('title', {}, this.viewNeuronTitle(neuron.value)),
  475. ]),
  476. h('text', {
  477. x: neuron.ui.cx, y: neuron.ui.cy + 1, fill: fontColor,
  478. dominantBaseline: "middle", textAnchor: "middle",
  479. style: { fontSize: "10px", cursor: "pointer" },
  480. // data_neuron_idx: idx,
  481. // onClick: this.handleClickNeuron,
  482. }, neuron.charge.toFixed(1)),
  483. ]);
  484. },
  485. viewNeuronValue: function (value) {
  486. return value
  487. .replace(new RegExp("\n", "g"), "\\n")
  488. .replace(new RegExp("\t", "g"), "\\t")
  489. .replace(new RegExp(" ", "g"), "_")
  490. ;
  491. },
  492. viewNeuronTitle: function (value) {
  493. return this.viewNeuronValue(value);
  494. },
  495. renderReceptor: function (receptor, idx) {
  496. // orange: hsl(40, 100%, 50%) --> hsl(0, 100%, 50%) -> red (overcharged)
  497. // var totalReceptors = this.state.receptor.length;
  498. // var cx = Math.ceil(this.state.ui_output_width / ( totalReceptors + 1 ));
  499. // var r = Math.min(Math.ceil(this.state.ui_output_width / ( totalReceptors + 10 ) / 2), this.state.ui_max_receptor_r);
  500. // DBG && console.log('DBG:renderReceptor', { receptor, idx, totalReceptors, r });
  501. // var xCenter = cx + cx * idx - r;
  502. var chargePr = (100 * receptor.charge) / this.state.config_max_receptor_charge;
  503. // var shapeColor = (chargePr > 100) ? "#f00" : "hsl(194, 67%, " + (100 - chargePr) + "%)"; // blue -> dark blue -> red
  504. var shapeColor = (chargePr > 100) ? "#f00" : "hsl(" + (40 - chargePr * 2 / 5).toFixed() + ", 100%, 50%)"; // orange -> dark orange -> red
  505. var fontColor = "#fff"; // (chargePr < 50) ? "#000" : "#fff";
  506. // this.state.config_max_receptor_charge = 100%
  507. // receptor.charge = x% (max 100)
  508. return h('g', {}, [
  509. h(receptor.uiShape, Object.assign({}, receptor.ui, {
  510. stroke: "#46b8da", strokeWidth: 1,
  511. fill: shapeColor,
  512. data_receptor_idx: idx,
  513. onClick: this.handleClickReceptor,
  514. style: { cursor: "pointer" },
  515. }), [
  516. h('title', {}, this.viewReceptorTitle(receptor.value)),
  517. ]),
  518. h('text', {
  519. x: receptor.ui.cx, y: receptor.ui.cy + 1, fill: fontColor,
  520. dominantBaseline: "middle", textAnchor: "middle",
  521. style: { fontSize: "10px", cursor: "pointer" },
  522. data_receptor_idx: idx,
  523. onClick: this.handleClickReceptor,
  524. }, receptor.charge.toFixed(1)),
  525. h('text', {
  526. x: receptor.ui.cx, y: receptor.ui.cy - 20, fill: "#000",
  527. dominantBaseline: "middle", textAnchor: "middle",
  528. style: { fontSize: "10px", cursor: "pointer" },
  529. data_receptor_idx: idx,
  530. onClick: this.handleClickReceptor,
  531. }, this.viewReceptorLetter(receptor.value)),
  532. ]);
  533. },
  534. viewReceptorLetter: function (letter) {
  535. switch (letter) {
  536. case "\t": return "\\t";
  537. case " ": return "_";
  538. case "\n": return "\\n";
  539. default: return letter;
  540. }
  541. },
  542. viewReceptorTitle: function (letter) {
  543. switch (letter) {
  544. case "\t": return "tab";
  545. case " ": return "space";
  546. case "\n": return "new line";
  547. default: return letter;
  548. }
  549. },
  550. renderReceptorInfo: function () {
  551. if (null === this.state.selectedReceptorIdx) return null;
  552. var receptor = this.state.receptor[this.state.selectedReceptorIdx];
  553. return h('div', {}, [
  554. "Receptor: '" + this.viewReceptorTitle(receptor.value) + "' charge('" + receptor.charge.toFixed(2) + "')",
  555. ]);
  556. },
  557. renderAllReceptorInfo: function () {
  558. return h('div', {}, this.state.receptor.map(this.renderAllReceptorInfo_item));
  559. },
  560. renderAllReceptorInfo_item: function (receptor) {
  561. return h('div', {}, ["Receptor: '" + this.viewReceptorTitle(receptor.value) + "' charge('" + receptor.charge.toFixed(2) + "')"]);
  562. },
  563. renderAllNeuronInfo: function () {
  564. return h('div', {}, this.state.neuron.map(this.renderAllNeuronInfo_item));
  565. },
  566. renderAllNeuronInfo_item: function (neuron) {
  567. return h('div', {}, ["Neuron: '" + neuron.value + "' charge('" + neuron.charge.toFixed(2) + "')"]);
  568. },
  569. renderInput: function () {
  570. return h('details', { open: true, style: { margin: "12px 0", border: "1px solid #aaa", borderRadius: "6px", padding: "12px", backgroundColor: "#eee", } }, [
  571. h('summary', { style: { cursor: "pointer" } }, "Input"),
  572. h('textarea', {
  573. ref: this.setInputRet,
  574. onChange: this.handleChangeInput,
  575. value: this.state.inputText,
  576. rows: 10,
  577. style: {
  578. width: "100%",
  579. padding: "12px",
  580. marginTop: "6px",
  581. backgroundColor: "#fff",
  582. },
  583. }),
  584. ]);
  585. },
  586. renderConfig: function () {
  587. return h('details', { open: true, style: { margin: "12px 0", border: "1px solid #aaa", borderRadius: "6px", padding: "12px", backgroundColor: "#eee", } }, [
  588. h('summary', { style: { cursor: "pointer" } }, "Config"),
  589. h('div', { style: { padding: "12px", backgroundColor: "#fff", } }, [
  590. h('div', {}, "Read speed:"),
  591. h('input', {
  592. className: "form-control input-sm",
  593. type: "number",
  594. value: this.state.config_read_speed,
  595. name: "config_read_speed",
  596. onChange: this.handleChangeConfig,
  597. }),
  598. h('div', { style: { marginTop: "12px" } }),
  599. h('div', {}, "Charge receptor at input:"),
  600. h('input', {
  601. className: "form-control input-sm",
  602. type: "number",
  603. value: this.state.config_charge_receptor_at_input,
  604. name: "config_charge_receptor_at_input",
  605. onChange: this.handleChangeConfig,
  606. }),
  607. h('div', { style: { marginTop: "12px" } }),
  608. h('div', {}, "Max receptor charge:"),
  609. h('input', {
  610. className: "form-control input-sm",
  611. type: "number",
  612. value: this.state.config_max_receptor_charge,
  613. name: "config_max_receptor_charge",
  614. onChange: this.handleChangeConfig,
  615. }),
  616. h('div', { style: { marginTop: "12px" } }),
  617. h('div', {}, "Discharge per tick:"),
  618. h('input', {
  619. className: "form-control input-sm",
  620. type: "number",
  621. step: "0.1",
  622. value: this.state.config_discharge_per_tick,
  623. name: "config_discharge_per_tick",
  624. onChange: this.handleChangeConfig,
  625. }),
  626. ]),
  627. ]);
  628. },
  629. handleChangeConfig: function (event) {
  630. var value = event.target.value;
  631. var name = event.target.getAttribute('name');
  632. var state = this.state;
  633. state[name] = value;
  634. this.setState(state);
  635. },
  636. renderLog: function () {
  637. return LOG__render(this._log)
  638. },
  639. handleRefreshLog: function () {
  640. if (!this._log.length) return;
  641. var lastLog = this._log[this._log.length - 1]
  642. console.table(
  643. [
  644. lastLog.receptor.map(function (node) { return "R:'" + node.value + "'"; })
  645. .concat(
  646. lastLog.neuron.map(function (node) { return "N:'" + node.value + "'"; })
  647. )
  648. ].concat(
  649. this._log.map(function (log) {
  650. return log.receptor.map(function (node) { return node.charge })
  651. .concat(
  652. log.neuron.map(function (node) { return node.charge })
  653. )
  654. })
  655. )
  656. )
  657. },
  658. render: function () {
  659. DBG1 && console.log("DBG:render", { state: this.state });
  660. return h('div', {}, [
  661. h('table', { className: "table table-border", p5_node_id: "p5-neuron-output-table" }, [
  662. h('tbody', {}, [
  663. h('tr', {}, [
  664. h('td', { style: { width: this.state.ui_output_width + 20 } }, [
  665. h('svg', { ref: this.setOutputRet, height: 300, width: this.state.ui_output_width, style: { border: "1px solid #eee" } }, [
  666. this.state.neuron.map(this.renderConnections),
  667. this.state.receptor.map(this.renderReceptor),
  668. this.state.neuron.map(this.renderNeuron),
  669. ]),
  670. ]),
  671. h('td', { style: { verticalAlign: "top" } }, [
  672. this.renderReceptorInfo(),
  673. this.renderAllReceptorInfo(),
  674. this.renderAllNeuronInfo(),
  675. ]),
  676. ]),
  677. ]),
  678. ]),
  679. h('div', {}, [
  680. h('button', { className: "btn btn-primary", onClick: this.handleExec }, "Uruchom"),
  681. " ",
  682. this.state.doAnim
  683. ? h('button', { className: "btn btn-default", onClick: this.handlePause }, [h('i', { className: "glyphicon glyphicon-pause" }), "Stop"])
  684. : h('button', { className: "btn btn-default", onClick: this.handleStart }, [h('i', { className: "glyphicon glyphicon-play" }), "Start"])
  685. ,
  686. h('button', { className: "btn btn-default", onClick: this.handleRefreshLog }, [h('i', { className: "glyphicon glyphicon-refresh" }), " ", "Log"])
  687. ]),
  688. this.renderLog(),
  689. h('table', { className: "table table-border" }, [
  690. h('tbody', {}, [
  691. h('tr', {}, [
  692. h('td', { style: { width: "50%" } }, [
  693. this.renderInput(),
  694. ]),
  695. h('td', { style: { width: "50%" } }, [
  696. this.renderConfig(),
  697. ]),
  698. ]),
  699. ]),
  700. ]),
  701. h('div', {}, [
  702. "TODO: receptory...",
  703. ]),
  704. ]);
  705. }
  706. });
  707. ReactDOM.render(h(NeuronView, {
  708. initialData: INITIAL_DATA || "",
  709. }), document.getElementById(HTML_ID))