Neuron.php.view.js 26 KB

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