|
|
@@ -13,47 +13,336 @@ var DBG1 = true;
|
|
|
// </svg>
|
|
|
|
|
|
|
|
|
-var p5UI__TestNeuron = createReactClass({
|
|
|
- _input: null,
|
|
|
- _output: null,
|
|
|
- getInitialState: function () {
|
|
|
- return {
|
|
|
- inputText: INITIAL_DATA || '',
|
|
|
- receptor: [],
|
|
|
- neuron: [],
|
|
|
- output_width: 300,
|
|
|
- max_receptor_r: 30,
|
|
|
- anim_pos: 0,
|
|
|
- anim_last_letter: null,
|
|
|
- config_read_speed: 100,
|
|
|
- config_charge_receptor_at_input: 1,
|
|
|
- config_max_receptor_charge: 5,
|
|
|
- config_discharge_per_tick: 0.1,
|
|
|
+var DEFAULT_CONFIG = {
|
|
|
+ ui_output_width: 800,
|
|
|
+ ui_max_receptor_r: 12,
|
|
|
+ ui_space_y: 30,
|
|
|
+ config_read_speed: 500,
|
|
|
+ config_charge_receptor_at_input: 1,
|
|
|
+ config_max_receptor_charge: 1.5,
|
|
|
+ config_discharge_per_tick: 0.1,
|
|
|
+ config_discharge_max_in_new_neuron_from_one: 0.7,
|
|
|
+}
|
|
|
+var DEFAULT_NEURON = {
|
|
|
+ value: '',
|
|
|
+ charge: 0,
|
|
|
+ maxCharge: DEFAULT_CONFIG.config_max_receptor_charge,
|
|
|
+ uiShape: "circle",
|
|
|
+ ui: { cx: 0, cy: 0 },
|
|
|
+ source: [],
|
|
|
+ next: [],
|
|
|
+}
|
|
|
|
|
|
- selectedReceptorIdx: null,
|
|
|
- };
|
|
|
- },
|
|
|
- makeReceptor: function (letter, idx, arr) {
|
|
|
+function state__getInitial(state, letters) {
|
|
|
+ function makeReceptor(letter, idx, arr) {
|
|
|
var totalReceptors = arr.length;
|
|
|
- var cx = Math.ceil(this.state.output_width / (totalReceptors + 1));
|
|
|
- var r = Math.min(Math.ceil(this.state.output_width / (totalReceptors + 10) / 2), this.state.max_receptor_r);
|
|
|
+ var cx = Math.ceil(state.ui_output_width / (totalReceptors + 1));
|
|
|
+ var r = Math.min(Math.ceil(state.ui_output_width / (totalReceptors + 10) / 2), state.ui_max_receptor_r);
|
|
|
var xCenter = cx + cx * idx - r;
|
|
|
|
|
|
- return {
|
|
|
- letter: letter,
|
|
|
- charge: 0,
|
|
|
- x: xCenter,
|
|
|
- y: 20,
|
|
|
- r: r,
|
|
|
+ return Object.assign({}, DEFAULT_NEURON, {
|
|
|
+ value: letter,
|
|
|
+ maxCharge: state.config_max_receptor_charge,
|
|
|
+ uiShape: "circle",
|
|
|
+ ui: {
|
|
|
+ cx: xCenter,
|
|
|
+ cy: 40,
|
|
|
+ r: r,
|
|
|
+ },
|
|
|
+ next: [],
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ receptor: letters.map(makeReceptor),
|
|
|
+ }
|
|
|
+}
|
|
|
+function state__readFromInput(state, char) {
|
|
|
+ // 1. charge receptor with letter `char`
|
|
|
+ var listNeuron = state.neuron;
|
|
|
+ var listReceptor = state.receptor;
|
|
|
+ var mapLetterToReceptor = {}
|
|
|
+ for (var i = 0, l = listReceptor.length; i < l; i++) {
|
|
|
+ mapLetterToReceptor[listReceptor[i].value] = listReceptor[i];
|
|
|
+ }
|
|
|
+ // var toChangeReceptor = getReceptor(listReceptor, char)
|
|
|
+ var toChangeReceptor = mapLetterToReceptor[char]
|
|
|
+ DBG && console.log("DBG:anim", { char, len: state.inputText.length, toChangeReceptor, mapLetterToReceptor: mapLetterToReceptor });
|
|
|
+ toChangeReceptor.charge += state.config_charge_receptor_at_input;
|
|
|
+
|
|
|
+ // 2. check if any receptor is over charged
|
|
|
+ if (toChangeReceptor.charge > state.config_max_receptor_charge) {
|
|
|
+ var closestNodeWithCharge = findReceptorClosestNodeWithCharge(state, toChangeReceptor)
|
|
|
+ DBG1 && console.log("DBG:overcharge.1:", { char, len: state.inputText.length, closestNodeWithCharge, receptor: state.receptor });
|
|
|
+ if (!closestNodeWithCharge) {
|
|
|
+ { // TODO: export to funtion makeNeuronFromOneNode(state, listReceptor, listNeuron, toChangeReceptor)
|
|
|
+ var neuron = makeNeuronFromOneNode(state, toChangeReceptor)
|
|
|
+ // TODO: neuron may already exists? check neuron.source if already has defined next
|
|
|
+ var alreadyExistNeuron = toChangeReceptor.next.filter(function (nextNeuron) {
|
|
|
+ return (nextNeuron.source.length === 1);
|
|
|
+ })
|
|
|
+ DBG1 && console.log("DBG:overcharge.2:", { alreadyExistNeuron });
|
|
|
+ if (alreadyExistNeuron.length) {
|
|
|
+ alreadyExistNeuron[0].charge += toChangeReceptor.charge
|
|
|
+ toChangeReceptor.charge = 0
|
|
|
+ } else {
|
|
|
+ toChangeReceptor.next.push(neuron)
|
|
|
+ listNeuron.push(neuron)
|
|
|
+ toChangeReceptor.charge = 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else { // (closestNodeWithCharge)
|
|
|
+ { // TODO: export to funtion makeNeuronFromTwoNodes(state, listReceptor, listNeuron, toChangeReceptor, closestNodeWithCharge)
|
|
|
+ var neuron = makeNeuronFromTwoNodes(state, toChangeReceptor, closestNodeWithCharge)
|
|
|
+ // TODO: neuron may already exists? check neuron.source if already has defined next
|
|
|
+ var alreadyExistNeuron = toChangeReceptor.next.filter(function (nextNeuron) {
|
|
|
+ return (nextNeuron.source.length === 2 && (
|
|
|
+ (nextNeuron.source[0].value === toChangeReceptor.value && nextNeuron.source[1].value === closestNodeWithCharge.value)
|
|
|
+ || (nextNeuron.source[0].value === closestNodeWithCharge.value && nextNeuron.source[1].value === toChangeReceptor.value)
|
|
|
+ ));
|
|
|
+ })
|
|
|
+ DBG1 && console.log("DBG:overcharge.2:", { alreadyExistNeuron });
|
|
|
+ if (alreadyExistNeuron.length) {
|
|
|
+ alreadyExistNeuron[0].charge += toChangeReceptor.charge + closestNodeWithCharge.charge
|
|
|
+ toChangeReceptor.charge = 0
|
|
|
+ closestNodeWithCharge.charge = 0
|
|
|
+ } else {
|
|
|
+ toChangeReceptor.next.push(neuron)
|
|
|
+ closestNodeWithCharge.next.push(neuron)
|
|
|
+ listNeuron.push(neuron)
|
|
|
+ toChangeReceptor.charge = 0
|
|
|
+ closestNodeWithCharge.charge = 0
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- },
|
|
|
- dischargeNeuron: function (neuron) {
|
|
|
+
|
|
|
+ // TODO: dont use last letter -- use findReceptorClosestNodeWithCharge
|
|
|
+ // if no closest neuron with charge then create new neuron over receptor
|
|
|
+ // if (null !== state.animLastLetter && state.animLastLetter !== toChangeReceptor.letter) {
|
|
|
+ // DBG1 && console.log("DBG:check overcharged", { toChangeReceptor, closestNodeWithCharge: closestNodeWithCharge, last_letter: state.animLastLetter });
|
|
|
+ // listNeuron = makeNueron(listReceptor, listNeuron, toChangeReceptor.letter, state.animLastLetter);
|
|
|
+ // // discharge receptors to 0
|
|
|
+ // var toDischarge = [toChangeReceptor.letter, state.animLastLetter];
|
|
|
+ // listReceptor = listReceptor.map(function (receptor) {
|
|
|
+ // return (-1 !== toDischarge.indexOf(receptor.letter))
|
|
|
+ // ? Object.assign({}, receptor, { charge: 0 })
|
|
|
+ // : receptor
|
|
|
+ // ;
|
|
|
+ // })
|
|
|
+ // }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. discharge all receptors and nodes by 0.1 (config_discharge_per_tick)
|
|
|
+ var dischargeNode = makeDischargeNodeFun(state);
|
|
|
+ return {
|
|
|
+ receptor: listReceptor.map(dischargeNode),
|
|
|
+ neuron: listNeuron.map(dischargeNode),
|
|
|
+ }
|
|
|
+}
|
|
|
+function state__fromOverchargedNeurons(state, overchargedNeurons) {
|
|
|
+ var listNeuron = state.neuron;
|
|
|
+ var listReceptor = state.receptor;
|
|
|
+
|
|
|
+ overchargedNeurons = overchargedNeurons.sort(sortNeuronByChargeCallback)
|
|
|
+ DBG1 && console.log("DBG:state:overcharged", { overchargedNeurons })
|
|
|
+ if (overchargedNeurons.length === 1) {
|
|
|
+ var toChangeReceptor = overchargedNeurons[0]
|
|
|
+ { // TODO: export to funtion makeNeuronFromOneNode(state, listReceptor, listNeuron, toChangeReceptor)
|
|
|
+ var neuron = makeNeuronFromOneNode(state, toChangeReceptor)
|
|
|
+ // TODO: neuron may already exists? check neuron.source if already has defined next
|
|
|
+ var alreadyExistNeuron = toChangeReceptor.next.filter(function (nextNeuron) {
|
|
|
+ return (nextNeuron.source.length === 1);
|
|
|
+ })
|
|
|
+ DBG1 && console.log("DBG:overcharge.2:", { alreadyExistNeuron });
|
|
|
+ if (alreadyExistNeuron.length) {
|
|
|
+ alreadyExistNeuron[0].charge += toChangeReceptor.charge
|
|
|
+ toChangeReceptor.charge = 0
|
|
|
+ } else {
|
|
|
+ toChangeReceptor.next.push(neuron)
|
|
|
+ listNeuron.push(neuron)
|
|
|
+ toChangeReceptor.charge = 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ var toChangeReceptor = overchargedNeurons[0]
|
|
|
+ var closestNodeWithCharge = overchargedNeurons[1]
|
|
|
+ { // TODO: export to funtion makeNeuronFromTwoNodes(state, listReceptor, listNeuron, toChangeReceptor, closestNodeWithCharge)
|
|
|
+ var neuron = makeNeuronFromTwoNodes(state, toChangeReceptor, closestNodeWithCharge)
|
|
|
+ // TODO: neuron may already exists? check neuron.source if already has defined next
|
|
|
+ var alreadyExistNeuron = toChangeReceptor.next.filter(function (nextNeuron) {
|
|
|
+ return (nextNeuron.source.length === 2 && (
|
|
|
+ (nextNeuron.source[0].value === toChangeReceptor.value && nextNeuron.source[1].value === closestNodeWithCharge.value)
|
|
|
+ || (nextNeuron.source[0].value === closestNodeWithCharge.value && nextNeuron.source[1].value === toChangeReceptor.value)
|
|
|
+ ));
|
|
|
+ })
|
|
|
+ DBG1 && console.log("DBG:overcharge.2:", { alreadyExistNeuron });
|
|
|
+ if (alreadyExistNeuron.length) {
|
|
|
+ alreadyExistNeuron[0].charge += toChangeReceptor.charge + closestNodeWithCharge.charge
|
|
|
+ toChangeReceptor.charge = 0
|
|
|
+ closestNodeWithCharge.charge = 0
|
|
|
+ } else {
|
|
|
+ toChangeReceptor.next.push(neuron)
|
|
|
+ closestNodeWithCharge.next.push(neuron)
|
|
|
+ listNeuron.push(neuron)
|
|
|
+ toChangeReceptor.charge = 0
|
|
|
+ closestNodeWithCharge.charge = 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ receptor: listReceptor,
|
|
|
+ neuron: listNeuron,
|
|
|
+ }
|
|
|
+}
|
|
|
+function sortNeuronByChargeCallback(n1, n2) {
|
|
|
+ if (n1.charge > n2.charge) return 1;
|
|
|
+ if (n1.charge < n2.charge) return -1;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+function getReceptor(listReceptor, char) {
|
|
|
+ var receptor = listReceptor.filter(function (receptor) {
|
|
|
+ return (char == receptor.value);
|
|
|
+ });
|
|
|
+ if (!receptor.length) throw "BUG: no input receptor found for char '" + char + "'"; // TODO: create on demand?
|
|
|
+ return receptor[0];
|
|
|
+}
|
|
|
+function findReceptorClosestNodeWithCharge(state, node) {
|
|
|
+ var skipSelfValue = node.value;
|
|
|
+ var maxChargedReceptor = state.receptor.reduce(function (ret, receptor) {
|
|
|
+ if (receptor.value === skipSelfValue) return ret;
|
|
|
+ if (0 === receptor.charge) return ret;
|
|
|
+ if (!ret) return receptor;
|
|
|
+ return (receptor.charge > ret.charge) ? receptor : ret;
|
|
|
+ }, null)
|
|
|
+ return maxChargedReceptor;
|
|
|
+}
|
|
|
+function makeNeuronFromOneNode(state, sourceNode) {
|
|
|
+ return Object.assign({}, DEFAULT_NEURON, {
|
|
|
+ value: '' + (state.neuron.length + 1),
|
|
|
+ charge: 0, // sourceNode.charge,
|
|
|
+ maxCharge: sourceNode.maxCharge * state.config_discharge_max_in_new_neuron_from_one, // maybe?: sourceNode.charge
|
|
|
+ uiShape: "ellipse",
|
|
|
+ ui: {
|
|
|
+ cx: sourceNode.ui.cx,
|
|
|
+ cy: sourceNode.ui.cy + 10 + state.ui_space_y,
|
|
|
+ rx: 10,
|
|
|
+ ry: 10,
|
|
|
+ },
|
|
|
+ source: [sourceNode],
|
|
|
+ });
|
|
|
+}
|
|
|
+function makeNeuronFromTwoNodes(state, first, second) {
|
|
|
+ var value = '' + first.value + second.value;
|
|
|
+ return Object.assign({}, DEFAULT_NEURON, {
|
|
|
+ value: value,
|
|
|
+ charge: 0, // (first.charge + second.charge) / 2,
|
|
|
+ maxCharge: ((first.maxCharge + second.maxCharge) / 2) * state.config_discharge_max_in_new_neuron_from_one,
|
|
|
+ uiShape: "ellipse",
|
|
|
+ ui: {
|
|
|
+ cx: (first.ui.cx + second.ui.cx) / 2,
|
|
|
+ cy: first.ui.cy + 10 + state.ui_space_y,
|
|
|
+ rx: 10 * value.length,
|
|
|
+ ry: 10,
|
|
|
+ },
|
|
|
+ source: [first, second],
|
|
|
+ });
|
|
|
+}
|
|
|
+function getNeuron(listNeuron, value) {
|
|
|
+ var neuron = listNeuron.filter(function (neuron) {
|
|
|
+ return (value == neuron.letter);
|
|
|
+ });
|
|
|
+ return (neuron.length) ? neuron[0] : null;
|
|
|
+}
|
|
|
+function makeDischargeNodeFun(state) {
|
|
|
+ return function dischargeNode(neuron) {
|
|
|
return (neuron.charge > 0)
|
|
|
- ? Object.assign({}, neuron, {
|
|
|
- charge: Math.max(0, neuron.charge - this.state.config_discharge_per_tick),
|
|
|
- })
|
|
|
- : neuron
|
|
|
- ;
|
|
|
+ ? Object.assign({}, neuron, {
|
|
|
+ charge: Math.max(0, neuron.charge - state.config_discharge_per_tick),
|
|
|
+ })
|
|
|
+ : neuron
|
|
|
+ ;
|
|
|
+ }
|
|
|
+}
|
|
|
+function isNeuronOvercharged(neuron) {
|
|
|
+ return (neuron.charge > neuron.maxCharge);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+function LOG__render(listLogEntries) {
|
|
|
+ return h('details', { open: false, style: { margin: "12px 0", border: "1px solid #aaa", borderRadius: "6px", padding: "12px", backgroundColor: "#eee", } }, [
|
|
|
+ h('summary', { style: { cursor: "pointer" } }, "Log"),
|
|
|
+ h('div', { style: { border: "1px solid #eee", backgroundColor: "#fff" } }, [
|
|
|
+ h('table', { className: "table table-border" }, [
|
|
|
+ h('thead', {}, LOG__renderTheadLog(listLogEntries)),
|
|
|
+ h('tbody', {}, LOG__renderTbodyLog(listLogEntries)),
|
|
|
+ ]),
|
|
|
+ ]),
|
|
|
+ ]);
|
|
|
+}
|
|
|
+function LOG__renderTheadLog(listLogEntries) {
|
|
|
+ if (!listLogEntries.length) return null;
|
|
|
+ var lastLog = listLogEntries[listLogEntries.length - 1]
|
|
|
+ var colsR = lastLog.receptor.map(function (node) { return node.value; })
|
|
|
+ var colsN = lastLog.neuron.map(function (node) { return node.value; })
|
|
|
+ return [
|
|
|
+ h('tr', {},
|
|
|
+ [h('th', {}, "Lp.")]
|
|
|
+ .concat(
|
|
|
+ colsR.map(function (label) { return h('th', {}, "R:'" + label + "'"); })
|
|
|
+ )
|
|
|
+ .concat(
|
|
|
+ colsN.map(function (label) { return h('th', {}, "N:'" + label + "'"); })
|
|
|
+ )
|
|
|
+ )
|
|
|
+ ];
|
|
|
+}
|
|
|
+function LOG__renderTbodyLog(listLogEntries) {
|
|
|
+ if (!listLogEntries.length) return null;
|
|
|
+ var lastLog = listLogEntries[listLogEntries.length - 1]
|
|
|
+ var colsR = lastLog.receptor.map(function (node) { return node.value; })
|
|
|
+ var colsN = lastLog.neuron.map(function (node) { return node.value; })
|
|
|
+ return listLogEntries.map(function (log, lpLog) {
|
|
|
+ var row = colsR.map(function (label, idx) { return log.receptor[idx].charge; })
|
|
|
+ .concat(
|
|
|
+ colsN.map(function (label, idx) { return (log.neuron[idx]) ? log.neuron[idx].charge : null; })
|
|
|
+ )
|
|
|
+ return h('tr', {}, [h('th', { style: { color: "#ddd" } }, '' + (lpLog + 1) + '.')]
|
|
|
+ .concat(
|
|
|
+ row.map(LOG__renderCellNodeChargeLog)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ })
|
|
|
+}
|
|
|
+function LOG__renderCellNodeChargeLog(charge) {
|
|
|
+ if (null === charge) return null;
|
|
|
+ return h('td', {
|
|
|
+ style: {
|
|
|
+ color: (charge > 0) ? "#000" : "#aaa",
|
|
|
+ },
|
|
|
+ }, charge.toFixed(2));
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+var NeuronView = createReactClass({
|
|
|
+ _input: null,
|
|
|
+ _output: null,
|
|
|
+ _animTimoutID: null,
|
|
|
+ _log: [],
|
|
|
+ getInitialState: function () {
|
|
|
+
|
|
|
+ return Object.assign({}, DEFAULT_CONFIG, {
|
|
|
+ inputText: this.props.initialData || '', // TODO: INITIAL_DATA
|
|
|
+ receptor: [],
|
|
|
+ neuron: [],
|
|
|
+ doAnim: false,
|
|
|
+ animPos: 0,
|
|
|
+ animLastLetter: null,
|
|
|
+ selectedReceptorIdx: null,
|
|
|
+ });
|
|
|
},
|
|
|
|
|
|
handleChangeInput: function (event) {
|
|
|
@@ -63,21 +352,30 @@ var p5UI__TestNeuron = createReactClass({
|
|
|
},
|
|
|
setInputRet: function (reactEl) { this._input = reactEl; },
|
|
|
setOutputRet: function (reactEl) { this._output = reactEl; },
|
|
|
- // d3.selectAll("circle").transition()
|
|
|
- // .duration(750)
|
|
|
- // .delay(function (d, i) { return i * 10; })
|
|
|
- // .attr("r", function (d) { return Math.sqrt(d * scale); });
|
|
|
componentDidMount: function () {
|
|
|
this.startAnimation();
|
|
|
},
|
|
|
handleExec: function (event) {
|
|
|
event.preventDefault();
|
|
|
DBG1 && console.log('DBG:handleExec...');
|
|
|
- this.startAnimation();
|
|
|
+ if (this._animTimoutID) clearTimeout(this._animTimoutID);
|
|
|
+ this.setState({
|
|
|
+ neuron: [],
|
|
|
+ receptor: [],
|
|
|
+ })
|
|
|
+ setTimeout(this.startAnimation, this.state.config_read_speed * 2)
|
|
|
+ },
|
|
|
+ handlePause: function (event) {
|
|
|
+ event.preventDefault();
|
|
|
+ this.setState({ doAnim: false })
|
|
|
+ },
|
|
|
+ handleStart: function (event) {
|
|
|
+ event.preventDefault();
|
|
|
+ this.forwardAnim(true)
|
|
|
},
|
|
|
startAnimation: function (event) {
|
|
|
var distinct = function (value, idx, self) {
|
|
|
- return ( idx === self.indexOf(value) );
|
|
|
+ return (idx === self.indexOf(value));
|
|
|
};
|
|
|
// var foundLetters = this.state.inputText.split("\n").reduce(function (ret, line) {
|
|
|
// return ret.concat(
|
|
|
@@ -85,104 +383,78 @@ var p5UI__TestNeuron = createReactClass({
|
|
|
// ).filter(distinct);
|
|
|
// }, []);
|
|
|
var foundLetters = this.state.inputText.split("").filter(distinct);
|
|
|
- DBG1 && console.log('DBG:handleExec:foundLetters', { foundLetters });
|
|
|
- this.setState({
|
|
|
- anim_pos: 0,
|
|
|
- receptor: foundLetters.map(this.makeReceptor),
|
|
|
- neuron: [],
|
|
|
- });
|
|
|
+ DBG1 && console.warn('DBG:handleExec:foundLetters', { foundLetters });
|
|
|
+
|
|
|
+ this._log = []
|
|
|
+ this.setState(Object.assign(
|
|
|
+ {},
|
|
|
+ state__getInitial(this.state, foundLetters),
|
|
|
+ {
|
|
|
+ animPos: 0,
|
|
|
+ neuron: [],
|
|
|
+ doAnim: true,
|
|
|
+ }
|
|
|
+ ), function () {
|
|
|
+ console.warn({
|
|
|
+ receptor: this.state.receptor.map(function (n) { return n }),
|
|
|
+ neuron: this.state.receptor.map(function (n) { return n }),
|
|
|
+ })
|
|
|
+ }.bind(this));
|
|
|
+
|
|
|
setTimeout(this.forwardAnim, this.state.config_read_speed)
|
|
|
},
|
|
|
getReceptor: function (char) {
|
|
|
var receptor = this.state.receptor.filter(function (receptor) {
|
|
|
- return (char == receptor.letter);
|
|
|
+ return (char == receptor.value);
|
|
|
});
|
|
|
if (!receptor.length) throw "BUG: no input receptor found for char '" + char + "'"; // TODO: create on demand?
|
|
|
return receptor[0];
|
|
|
},
|
|
|
- getNeuron: function (value) {
|
|
|
- var neuron = this.state.neuron.filter(function (neuron) {
|
|
|
- return (value == neuron.letter);
|
|
|
- });
|
|
|
- return (neuron.length) ? neuron[0] : null;
|
|
|
- },
|
|
|
- makeNueron: function (listNeuron, letter, last_letter) {
|
|
|
- var receptor = this.getReceptor(letter);
|
|
|
- var prevReceptor = this.getReceptor(last_letter);
|
|
|
- var value = '' + letter + last_letter;
|
|
|
- var findNeuron = this.getNeuron(value);
|
|
|
- var charge = (receptor.charge + prevReceptor.charge) / 2;
|
|
|
- if (!findNeuron) {
|
|
|
- return listNeuron.concat([
|
|
|
- {
|
|
|
- value: value,
|
|
|
- charge: charge,
|
|
|
- x: (receptor.x + prevReceptor.x) / 2,
|
|
|
- y: 20 + 20 * value.length,
|
|
|
- rx: 10 * value.length,
|
|
|
- ry: 10,
|
|
|
- source1_x: receptor.x,
|
|
|
- source1_y: receptor.y,
|
|
|
- source2_x: prevReceptor.x,
|
|
|
- source2_y: prevReceptor.y,
|
|
|
- }
|
|
|
- ])
|
|
|
+ forwardAnim: function (forceUpdateDoAnim) {
|
|
|
+ this._logState(this.state)
|
|
|
+ var doAnim = (arguments.length > 0) ? forceUpdateDoAnim : this.state.doAnim;
|
|
|
+ var animPos = this.state.animPos;
|
|
|
+ var overchargedNeurons = this.state.neuron.filter(isNeuronOvercharged)
|
|
|
+ if (overchargedNeurons.length === 0) {
|
|
|
+ animPos += 1;
|
|
|
} else {
|
|
|
- return listNeuron.map(function (neuron) {
|
|
|
- (neuron.value === value)
|
|
|
- ? Object.assign({}, neuron, { charge: neuron.charge + (receptor.charge + prevReceptor.charge) / 2 })
|
|
|
- : neuron
|
|
|
- ;
|
|
|
- })
|
|
|
+ DBG1 && console.log("DBG:anim TODO: neuron overcharged", { overchargedNeurons });
|
|
|
}
|
|
|
- },
|
|
|
- forwardAnim: function () {
|
|
|
- if (this.state.anim_pos + 1 >= this.state.inputText.length) {
|
|
|
- DBG1 && console.log("DBG:anim STOP", { inputLength: this.state.inputText.length, anim_pos: this.state.anim_pos });
|
|
|
+
|
|
|
+ if (animPos >= this.state.inputText.length) {
|
|
|
+ DBG1 && console.log("DBG:anim STOP - end of input", { inputLength: this.state.inputText.length, animPos: this.state.animPos });
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- var anim_pos = this.state.anim_pos + 1;
|
|
|
- var char = this.state.inputText.charAt(anim_pos)
|
|
|
- DBG1 && console.log("DBG:anim", { anim_pos, char, len: this.state.inputText.length });
|
|
|
-
|
|
|
- // 1. charge receptor with letter `char`
|
|
|
- var neuron = this.state.neuron;
|
|
|
- var _chargeReceptor = this.state.config_charge_receptor_at_input;
|
|
|
- var toChangeReceptor = this.getReceptor(char)
|
|
|
- DBG1 && console.log("DBG:anim", { anim_pos, char, len: this.state.inputText.length, toChangeReceptor });
|
|
|
- toChangeReceptor.charge += _chargeReceptor;
|
|
|
- var receptor = this.state.receptor.map(function (receptor) {
|
|
|
- return (char !== receptor.letter) ? receptor : toChangeReceptor;
|
|
|
- })
|
|
|
- // 2. check if any receptor is over charged
|
|
|
- if (toChangeReceptor.charge > this.state.config_max_receptor_charge) {
|
|
|
- if (null !== this.state.anim_last_letter && this.state.anim_last_letter !== toChangeReceptor.letter) {
|
|
|
- DBG1 && console.log("DBG:check overcharged", { toChangeReceptor, last_letter: this.state.anim_last_letter });
|
|
|
- neuron = this.makeNueron(neuron, toChangeReceptor.letter, this.state.anim_last_letter);
|
|
|
- // discharge receptors to 0
|
|
|
- var toDischarge = [toChangeReceptor.letter, this.state.anim_last_letter];
|
|
|
- receptor = receptor.map(function (receptor) {
|
|
|
- return (-1 !== toDischarge.indexOf(receptor.letter))
|
|
|
- ? Object.assign({}, receptor, { charge: 0 })
|
|
|
- : receptor
|
|
|
- ;
|
|
|
- })
|
|
|
+ var char = this.state.inputText.charAt(animPos)
|
|
|
+ DBG && console.log("DBG:anim", { animPos, char, len: this.state.inputText.length, doAnim });
|
|
|
+
|
|
|
+ this.setState(Object.assign(
|
|
|
+ {},
|
|
|
+ (overchargedNeurons.length) ? state__fromOverchargedNeurons(this.state, overchargedNeurons) : state__readFromInput(this.state, char),
|
|
|
+ {
|
|
|
+ animLastLetter: char,
|
|
|
+ animPos: animPos,
|
|
|
+ doAnim: doAnim,
|
|
|
}
|
|
|
- }
|
|
|
- // 3. discharge all receptors and nodes by 0.1 (config_discharge_per_tick)
|
|
|
- receptor = receptor.map(this.dischargeNeuron)
|
|
|
- neuron = neuron.map(this.dischargeNeuron)
|
|
|
- // 4. create neuron if needed
|
|
|
+ ));
|
|
|
|
|
|
- this.setState({
|
|
|
- anim_last_letter: char,
|
|
|
- anim_pos: anim_pos,
|
|
|
- receptor: receptor,
|
|
|
- neuron: neuron,
|
|
|
- });
|
|
|
+ if (doAnim) {
|
|
|
+ this._animTimoutID = setTimeout(this.forwardAnim, this.state.config_read_speed)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ _logState: function (state) {
|
|
|
+ function getNodeInfo(node) {
|
|
|
+ return {
|
|
|
+ value: node.value,
|
|
|
+ charge: node.charge,
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- setTimeout(this.forwardAnim, this.state.config_read_speed)
|
|
|
+ this._log.push({
|
|
|
+ receptor: state.receptor.map(getNodeInfo),
|
|
|
+ neuron: state.neuron.map(getNodeInfo),
|
|
|
+ })
|
|
|
},
|
|
|
selectReceptor: function (receptorIdx) {
|
|
|
this.setState({ selectedReceptorIdx: receptorIdx })
|
|
|
@@ -194,45 +466,48 @@ var p5UI__TestNeuron = createReactClass({
|
|
|
},
|
|
|
|
|
|
renderConnections: function (neuron, idx) {
|
|
|
+ DBG1 && console.log('DBG:renderConnections', { neuron, p1: neuron.source[0].ui, p2: (neuron.source.length > 1) ? neuron.source[1].ui : null });
|
|
|
return h('g', {}, [
|
|
|
h('line', {
|
|
|
- x1: neuron.source1_x, y1: neuron.source1_y,
|
|
|
- x2: neuron.x, y2: neuron.y,
|
|
|
- style: {
|
|
|
- stroke: "#46b8da",
|
|
|
- strokeWidth: "2"
|
|
|
- },
|
|
|
- }),
|
|
|
- h('line', {
|
|
|
- x1: neuron.source2_x, y1: neuron.source2_y,
|
|
|
- x2: neuron.x, y2: neuron.y,
|
|
|
+ x1: neuron.source[0].ui.cx, y1: neuron.source[0].ui.cy,
|
|
|
+ x2: neuron.ui.cx, y2: neuron.ui.cy,
|
|
|
style: {
|
|
|
stroke: "#46b8da",
|
|
|
strokeWidth: "2"
|
|
|
},
|
|
|
}),
|
|
|
+ (neuron.source.length > 1)
|
|
|
+ ? h('line', {
|
|
|
+ x1: neuron.source[1].ui.cx, y1: neuron.source[1].ui.cy,
|
|
|
+ x2: neuron.ui.cx, y2: neuron.ui.cy,
|
|
|
+ style: {
|
|
|
+ stroke: "#46b8da",
|
|
|
+ strokeWidth: "2"
|
|
|
+ },
|
|
|
+ })
|
|
|
+ : null,
|
|
|
]);
|
|
|
},
|
|
|
renderNeuron: function (neuron, idx) {
|
|
|
var chargePr = (100 * neuron.charge) / this.state.config_max_receptor_charge;
|
|
|
var fontColor = (chargePr < 50) ? "#000" : "#fff";
|
|
|
return h('g', {}, [
|
|
|
- h('ellipse', {
|
|
|
- cx: neuron.x, cy: neuron.y, rx: neuron.rx, ry: neuron.ry, stroke: "#46b8da", strokeWidth: 1,
|
|
|
+ h(neuron.uiShape, Object.assign({}, neuron.ui, {
|
|
|
+ stroke: "#46b8da", strokeWidth: 1,
|
|
|
fill: (chargePr > 100) ? "#f00" : "hsl(194, 67%, " + (100 - chargePr) + "%)",
|
|
|
// data_neuron_idx: idx,
|
|
|
// onClick: this.handleClickNeuron,
|
|
|
style: { cursor: "pointer" },
|
|
|
- }, [
|
|
|
- h('title', {}, this.viewNeuronTitle(neuron.value)),
|
|
|
- ]),
|
|
|
+ }), [
|
|
|
+ h('title', {}, this.viewNeuronTitle(neuron.value)),
|
|
|
+ ]),
|
|
|
h('text', {
|
|
|
- x: neuron.x, y: neuron.y + 1, fill: fontColor,
|
|
|
+ x: neuron.ui.cx, y: neuron.ui.cy + 1, fill: fontColor,
|
|
|
dominantBaseline: "middle", textAnchor: "middle",
|
|
|
style: { fontSize: "10px", cursor: "pointer" },
|
|
|
// data_neuron_idx: idx,
|
|
|
// onClick: this.handleClickNeuron,
|
|
|
- }, this.viewNeuronValue(neuron.value)),
|
|
|
+ }, neuron.charge.toFixed(1)),
|
|
|
]);
|
|
|
},
|
|
|
viewNeuronValue: function (value) {
|
|
|
@@ -240,40 +515,48 @@ var p5UI__TestNeuron = createReactClass({
|
|
|
.replace(new RegExp("\n", "g"), "\\n")
|
|
|
.replace(new RegExp("\t", "g"), "\\t")
|
|
|
.replace(new RegExp(" ", "g"), "_")
|
|
|
- ;
|
|
|
+ ;
|
|
|
},
|
|
|
viewNeuronTitle: function (value) {
|
|
|
return this.viewNeuronValue(value);
|
|
|
},
|
|
|
renderReceptor: function (receptor, idx) {
|
|
|
+ // orange: hsl(40, 100%, 50%) --> hsl(0, 100%, 50%) -> red (overcharged)
|
|
|
// var totalReceptors = this.state.receptor.length;
|
|
|
- // var cx = Math.ceil(this.state.output_width / ( totalReceptors + 1 ));
|
|
|
- // var r = Math.min(Math.ceil(this.state.output_width / ( totalReceptors + 10 ) / 2), this.state.max_receptor_r);
|
|
|
+ // var cx = Math.ceil(this.state.ui_output_width / ( totalReceptors + 1 ));
|
|
|
+ // var r = Math.min(Math.ceil(this.state.ui_output_width / ( totalReceptors + 10 ) / 2), this.state.ui_max_receptor_r);
|
|
|
// DBG && console.log('DBG:renderReceptor', { receptor, idx, totalReceptors, r });
|
|
|
// var xCenter = cx + cx * idx - r;
|
|
|
var chargePr = (100 * receptor.charge) / this.state.config_max_receptor_charge;
|
|
|
- var fontColor = (chargePr < 50) ? "#000" : "#fff";
|
|
|
+ // var shapeColor = (chargePr > 100) ? "#f00" : "hsl(194, 67%, " + (100 - chargePr) + "%)"; // blue -> dark blue -> red
|
|
|
+ var shapeColor = (chargePr > 100) ? "#f00" : "hsl(" + (40 - chargePr * 2 / 5).toFixed() + ", 100%, 50%)"; // orange -> dark orange -> red
|
|
|
+ var fontColor = "#fff"; // (chargePr < 50) ? "#000" : "#fff";
|
|
|
// this.state.config_max_receptor_charge = 100%
|
|
|
// receptor.charge = x% (max 100)
|
|
|
return h('g', {}, [
|
|
|
- h('circle', {
|
|
|
- cx: receptor.x, cy: receptor.y, r: receptor.r, stroke: "#46b8da", strokeWidth: 1,
|
|
|
- // fill: "#5bc0de"
|
|
|
- // (receptor.charge > this.state.config_max_receptor_charge)
|
|
|
- fill: (chargePr > 100) ? "#f00" : "hsl(194, 67%, " + (100 - chargePr) + "%)",
|
|
|
+ h(receptor.uiShape, Object.assign({}, receptor.ui, {
|
|
|
+ stroke: "#46b8da", strokeWidth: 1,
|
|
|
+ fill: shapeColor,
|
|
|
data_receptor_idx: idx,
|
|
|
onClick: this.handleClickReceptor,
|
|
|
style: { cursor: "pointer" },
|
|
|
- }, [
|
|
|
- h('title', {}, this.viewReceptorTitle(receptor.letter)),
|
|
|
- ]),
|
|
|
+ }), [
|
|
|
+ h('title', {}, this.viewReceptorTitle(receptor.value)),
|
|
|
+ ]),
|
|
|
h('text', {
|
|
|
- x: receptor.x, y: receptor.y + 1, fill: fontColor,
|
|
|
+ x: receptor.ui.cx, y: receptor.ui.cy + 1, fill: fontColor,
|
|
|
dominantBaseline: "middle", textAnchor: "middle",
|
|
|
style: { fontSize: "10px", cursor: "pointer" },
|
|
|
data_receptor_idx: idx,
|
|
|
onClick: this.handleClickReceptor,
|
|
|
- }, this.viewReceptorLetter(receptor.letter)),
|
|
|
+ }, receptor.charge.toFixed(1)),
|
|
|
+ h('text', {
|
|
|
+ x: receptor.ui.cx, y: receptor.ui.cy - 20, fill: "#000",
|
|
|
+ dominantBaseline: "middle", textAnchor: "middle",
|
|
|
+ style: { fontSize: "10px", cursor: "pointer" },
|
|
|
+ data_receptor_idx: idx,
|
|
|
+ onClick: this.handleClickReceptor,
|
|
|
+ }, this.viewReceptorLetter(receptor.value)),
|
|
|
]);
|
|
|
},
|
|
|
viewReceptorLetter: function (letter) {
|
|
|
@@ -296,14 +579,14 @@ var p5UI__TestNeuron = createReactClass({
|
|
|
if (null === this.state.selectedReceptorIdx) return null;
|
|
|
var receptor = this.state.receptor[this.state.selectedReceptorIdx];
|
|
|
return h('div', {}, [
|
|
|
- "Receptor: '" + this.viewReceptorTitle(receptor.letter) + "' charge('" + receptor.charge.toFixed(2) + "')",
|
|
|
+ "Receptor: '" + this.viewReceptorTitle(receptor.value) + "' charge('" + receptor.charge.toFixed(2) + "')",
|
|
|
]);
|
|
|
},
|
|
|
renderAllReceptorInfo: function () {
|
|
|
return h('div', {}, this.state.receptor.map(this.renderAllReceptorInfo_item));
|
|
|
},
|
|
|
renderAllReceptorInfo_item: function (receptor) {
|
|
|
- return h('div', {}, ["Receptor: '" + this.viewReceptorTitle(receptor.letter) + "' charge('" + receptor.charge.toFixed(2) + "')"]);
|
|
|
+ return h('div', {}, ["Receptor: '" + this.viewReceptorTitle(receptor.value) + "' charge('" + receptor.charge.toFixed(2) + "')"]);
|
|
|
},
|
|
|
renderAllNeuronInfo: function () {
|
|
|
return h('div', {}, this.state.neuron.map(this.renderAllNeuronInfo_item));
|
|
|
@@ -378,13 +661,36 @@ var p5UI__TestNeuron = createReactClass({
|
|
|
state[name] = value;
|
|
|
this.setState(state);
|
|
|
},
|
|
|
+ renderLog: function () {
|
|
|
+ return LOG__render(this._log)
|
|
|
+ },
|
|
|
+ handleRefreshLog: function () {
|
|
|
+ if (!this._log.length) return;
|
|
|
+ var lastLog = this._log[this._log.length - 1]
|
|
|
+ console.table(
|
|
|
+ [
|
|
|
+ lastLog.receptor.map(function (node) { return "R:'" + node.value + "'"; })
|
|
|
+ .concat(
|
|
|
+ lastLog.neuron.map(function (node) { return "N:'" + node.value + "'"; })
|
|
|
+ )
|
|
|
+ ].concat(
|
|
|
+ this._log.map(function (log) {
|
|
|
+ return log.receptor.map(function (node) { return node.charge })
|
|
|
+ .concat(
|
|
|
+ log.neuron.map(function (node) { return node.charge })
|
|
|
+ )
|
|
|
+ })
|
|
|
+ )
|
|
|
+ )
|
|
|
+ },
|
|
|
render: function () {
|
|
|
+ DBG1 && console.log("DBG:render", { state: this.state });
|
|
|
return h('div', {}, [
|
|
|
- h('table', { className: "table table-border" }, [
|
|
|
+ h('table', { className: "table table-border", p5_node_id: "p5-neuron-output-table" }, [
|
|
|
h('tbody', {}, [
|
|
|
h('tr', {}, [
|
|
|
- h('td', { style: { width: this.state.output_width + 20 } }, [
|
|
|
- h('svg', { ref: this.setOutputRet, height: 300, width: this.state.output_width, style: { border: "1px solid #eee" } }, [
|
|
|
+ h('td', { style: { width: this.state.ui_output_width + 20 } }, [
|
|
|
+ h('svg', { ref: this.setOutputRet, height: 300, width: this.state.ui_output_width, style: { border: "1px solid #eee" } }, [
|
|
|
this.state.neuron.map(this.renderConnections),
|
|
|
this.state.receptor.map(this.renderReceptor),
|
|
|
this.state.neuron.map(this.renderNeuron),
|
|
|
@@ -392,15 +698,22 @@ var p5UI__TestNeuron = createReactClass({
|
|
|
]),
|
|
|
h('td', { style: { verticalAlign: "top" } }, [
|
|
|
this.renderReceptorInfo(),
|
|
|
- ]),
|
|
|
- h('td', { style: { verticalAlign: "top" } }, [
|
|
|
this.renderAllReceptorInfo(),
|
|
|
this.renderAllNeuronInfo(),
|
|
|
]),
|
|
|
]),
|
|
|
]),
|
|
|
]),
|
|
|
- h('button', { className: "btn btn-primary", onClick: this.handleExec }, "Uruchom"),
|
|
|
+ h('div', {}, [
|
|
|
+ h('button', { className: "btn btn-primary", onClick: this.handleExec }, "Uruchom"),
|
|
|
+ " ",
|
|
|
+ this.state.doAnim
|
|
|
+ ? h('button', { className: "btn btn-default", onClick: this.handlePause }, [h('i', { className: "glyphicon glyphicon-pause" }), "Stop"])
|
|
|
+ : h('button', { className: "btn btn-default", onClick: this.handleStart }, [h('i', { className: "glyphicon glyphicon-play" }), "Start"])
|
|
|
+ ,
|
|
|
+ h('button', { className: "btn btn-default", onClick: this.handleRefreshLog }, [h('i', { className: "glyphicon glyphicon-refresh" }), " ", "Log"])
|
|
|
+ ]),
|
|
|
+ this.renderLog(),
|
|
|
h('table', { className: "table table-border" }, [
|
|
|
h('tbody', {}, [
|
|
|
h('tr', {}, [
|
|
|
@@ -421,6 +734,6 @@ var p5UI__TestNeuron = createReactClass({
|
|
|
}
|
|
|
});
|
|
|
|
|
|
-ReactDOM.render(h(p5UI__TestNeuron
|
|
|
-
|
|
|
-), document.getElementById(HTML_ID))
|
|
|
+ReactDOM.render(h(NeuronView, {
|
|
|
+ initialData: INITIAL_DATA || "",
|
|
|
+}), document.getElementById(HTML_ID))
|