| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718 |
- var MAKE_STORE_FUNCTION_NAME = MAKE_STORE_FUNCTION_NAME || 'makeNeuronStore'
- var DBG = DBG || false;
- var DBG1 = true;
- // DEFAULT_CONFIG.config_strategy_overcharge: "LEAVE_ALMOST_MAX", // "REMOVE_CHARGE" | "LEAVE_HALF_CHARGE" | "LEAVE_ALMOST_MAX"
- var DEFAULT_NEURON = {
- value: '',
- charge: 0,
- maxCharge: 1.5, // DEFAULT_CONFIG.config_max_neuron_charge,
- uiShape: "circle",
- ui: { cx: 0, cy: 0 },
- source: [],
- next: [],
- }
- function makeDefaultNeuronsStoreState() {
- return {
- receptor: [],
- mapReceptorChar: {},
- neuron: [],
- connection: [],
- doubleConn: [], // connections between two nodes
- charge: [], // TODO: list of [ { charge: number, nodeType: (receptor|neuron), nodeIdx: int }, ... ] groupd by node (type/idx)
- inputReadPos: 0,
- animPos: 0,
- doAnim: true,
- uiOutputHeight: 300,
- }
- }
- var p5Utils__ArrayDistinctFilter = function (value, idx, self) {
- return (idx === self.indexOf(value));
- };
- var p5Utils__ArrayDiff = function (a, b) { // a - b @return array with items from `a` that are not in `b`
- return a.filter(function (x) {
- return (-1 === b.indexOf(x));
- })
- };
- var p5Utils__inputToFlatString = function (input) {
- return input.reduce(function (ret, line) {
- return ret.concat(line).concat(['\n'])
- }, []).join('')
- };
- function makeNeuronStore() {
- DBG && console.log("DBG:NeuronStore:makeNeuronStore");
- var _state = makeDefaultNeuronsStoreState()
- var _config = {}
- var _input = []
- var _animIntervalID = null
- var _callback = null
- function stateReset(input, config) {
- _input = input
- _config = config
- _state = makeDefaultNeuronsStoreState()
- var foundLetters = _input.reduce(function (ret, line) { return ret.concat(line) }, []).filter(p5Utils__ArrayDistinctFilter);
- _state.receptor = foundLetters.sort().map(makeReceptor);
- _state.mapReceptorChar = _state.receptor.map(function (node) { return node.value; });
- if (_animIntervalID) clearInterval(_animIntervalID)
- _notifySubscribers();
- }
- function inputUpdated(newInput) {
- var oldInput = _input
- DBG && console.log("DBG:NeuronStore:inputUpdated", { newInput, oldInput })
- if (!newInput || !newInput.length) {
- // Missing input
- return;
- }
- if (newInput.length < oldInput.length) {
- stateReset(newInput, _config)
- startAnimation()
- return;
- }
- if (p5Utils__inputToFlatString(newInput.slice(0, oldInput.length)) !== p5Utils__inputToFlatString(oldInput)) {
- stateReset(newInput, _config)
- startAnimation()
- return;
- }
- DBG && console.log("DBG:NeuronStore:inputUpdated", { msg: "TODO: add input => add Receptors" })
- _input = newInput
- { // add new receptors if needed
- var addInput = newInput.slice(oldInput.length)
- var newLetters = addInput.reduce(function (ret, line) { return ret.concat(line) }, []).filter(p5Utils__ArrayDistinctFilter)
- var oldLetters = oldInput.reduce(function (ret, line) { return ret.concat(line) }, []).filter(p5Utils__ArrayDistinctFilter)
- var addLetters = p5Utils__ArrayDiff(newLetters.sort(), oldLetters)
- DBG && console.log("DBG:NeuronStore:inputUpdated", { msg: "TODO: add input => add Receptors", addLetters })
- if (!addLetters.length) return;
- var totalReceptors = oldLetters.length + addLetters.length
- var oldFirstCx = Math.ceil(_config.ui_output_width / (oldLetters.length + 1));
- var newFirstCx = Math.ceil(_config.ui_output_width / (totalReceptors + 1));
- var moveXFactor = Number((newFirstCx / oldFirstCx).toFixed(2))
- DBG && console.log("DBG:NeuronStore:inputUpdated", { msg: "TODO: add input => add Receptors", moveXFactor, oldFirstCx, newFirstCx })
- var allLetters = [].concat(oldLetters, addLetters)
- _state.receptor = _state.receptor.map(function (receptor) {
- receptor.ui.cx = receptor.ui.cx * moveXFactor
- return receptor;
- })
- _state.neuron = _state.neuron.map(function (neuron) {
- neuron.ui.cx = neuron.ui.cx * moveXFactor
- return neuron;
- })
- addLetters.forEach(function (letter, idx) {
- _state.receptor.push(makeReceptor(letter, idx + oldLetters.length, allLetters))
- })
- _state.mapReceptorChar = _state.receptor.map(function (node) { return node.value; });
- }
- }
- function inputAddLine(inputLine) { // inputLine: [ [ "A", "B", ... ], ... ]
- DBG && console.log("DBG:NeuronStore:inputAddLine", { msg: "TODO: add input => add Receptors", keys: inputLine, _input: [].concat(_input) })
- var oldInput = [].concat(_input)
- var newLetters = inputLine.reduce(function (ret, line) { return ret.concat(line) }, []).filter(p5Utils__ArrayDistinctFilter)
- _input.push(newLetters)
- { // add new receptors if needed
- var oldLetters = oldInput.reduce(function (ret, line) { return ret.concat(line) }, []).filter(p5Utils__ArrayDistinctFilter)
- var addLetters = p5Utils__ArrayDiff(newLetters.sort(), oldLetters)
- DBG && console.log("DBG:NeuronStore:inputAddLine", { msg: "TODO: add input => add Receptors.2", newLetters, oldLetters, addLetters })
- if (!addLetters.length) return;
- var totalReceptors = oldLetters.length + addLetters.length
- var oldFirstCx = Math.ceil(_config.ui_output_width / (oldLetters.length + 1));
- var newFirstCx = Math.ceil(_config.ui_output_width / (totalReceptors + 1));
- var moveXFactor = Number((newFirstCx / oldFirstCx).toFixed(2))
- DBG && console.log("DBG:NeuronStore:inputAddLine", { msg: "TODO: add input => add Receptors.3", moveXFactor, oldFirstCx, newFirstCx })
- var allLetters = [].concat(oldLetters, addLetters)
- _state.receptor = _state.receptor.map(function (receptor) {
- receptor.ui.cx = receptor.ui.cx * moveXFactor
- return receptor;
- })
- _state.neuron = _state.neuron.map(function (neuron) {
- neuron.ui.cx = neuron.ui.cx * moveXFactor
- return neuron;
- })
- addLetters.forEach(function (letter, idx) {
- _state.receptor.push(makeReceptor(letter, idx + oldLetters.length, allLetters))
- })
- _state.mapReceptorChar = _state.receptor.map(function (node) { return node.value; });
- }
- }
- function readLineFromInput() {
- if (_state.inputReadPos >= _input.length) return null;
- var line = _input[_state.inputReadPos]
- _state.inputReadPos += 1
- return line;
- }
- function forwardAnim() {
- DBG && console.log("DBG:NeuronStore:forwardAnim:doAnim = '" + (_state.doAnim ? 1 : 0) + "'");
- if (!_state.doAnim) return;
- // 1. read from input if its time
- // 2. check overcharged nodes
- // 2.1 check overcharged Receptor
- // 2.2 check overcharged Neuron
- // 3. move all charges (Receptor -> Connection, Connection -> Neuron, Neuron -> Connection)
- // 4. discharge all
- // ad.2. TODO: what to do with Charge when create new Neuron?
- // - idea 1: use Charge to create new Neuron: rm Charge, add Neuron, add Connection
- // 1. read from input if its time
- if (0 === _state.animPos % _config.config_read_speed_multiplier) {
- // read from input = add charge to receptor with given letter
- var line = readLineFromInput()
- if (null === line) {
- DBG && console.log("DBG:NeuronStore:forwardAnim:1(readInput): STOP - end of input / discharge all", { inputLength: _input.length, animPos: _state.animPos })
- } else {
- line.forEach(function (label) {
- var foundReceptorIdx = getReceptorIdx(label)
- DBG && console.log("DBG:NeuronStore:forwardAnim:1(readInput): label('" + label + "', [" + label.charCodeAt(0) + "])", { animPos: _state.animPos, label, len: _input.length, doAnim: _state.doAnim, foundReceptorIdx })
- if (-1 === foundReceptorIdx) {
- DBG && console.warn("BUG: receptor '" + label + "' not found (NeuronStore:forwardAnim:readInput)")
- _state.doAnim = false
- _notifySubscribers()
- return;
- }
- this_charge_add(_config.config_charge_receptor_at_input, { type: 'receptor', idx: foundReceptorIdx })
- })
- _state.animPos += 1
- _notifySubscribers()
- return;
- }
- }
- // 2. check overcharged nodes
- // 2.1 check overcharged Neuron
- // 2.1.1 is overcharged Neuron
- // 2.1.1.1 is another Neuron with Charge
- // 2.1.1.2 ! another Neuron with Charge
- // 2.1.2 ! overcharged Neuron
- // 2.2 check overcharged Receptor
- { // check overcharged nodes
- var overchargedNeuronIdx = _state.neuron.reduce(function (ret, neuron, idx) {
- var charge = getNeuronCharge(idx)
- if (charge < neuron.maxCharge) return ret;
- ret.push({ idx: idx, charge: charge })
- return ret;
- }, [])
- var overchargedReceptorIdx = _state.receptor.reduce(function (ret, receptor, idx) {
- var charge = getReceptorCharge(idx)
- if (charge < _config.config_max_receptor_charge) return ret;
- ret.push({ idx: idx, charge: charge })
- return ret;
- }, [])
- if (overchargedNeuronIdx.length > 0) {
- var overcharged = overchargedNeuronIdx[0]
- var firstNeuron = _state.neuron[overcharged.idx]
- var secondNeuronWithCharge = _state.neuron.reduce(function (ret, neuron, idx) { // find neuron with max charge
- if (idx === overcharged.idx) return ret; // skip firstNeuron
- var charge = getNeuronCharge(idx)
- if (!charge) return ret; // skip Neuron without charge
- if (null === ret) return { idx: idx, charge: charge }
- return (charge > ret.charge) ? { idx: idx, charge: charge } : ret;
- }, null)
- DBG && console.log("DBG:NeuronStore:forwardAnim:2.1(check overcharged)/neuron.1", { overcharged, firstNeuron, secondNeuronWithCharge })
- if (null !== secondNeuronWithCharge) {
- // is already exists - check doubleConn
- var existingConn = _state.doubleConn.reduce(function (ret, dblConn) {
- if (ret) return ret;
- if (dblConn.from[0] === overcharged.idx && dblConn.from[1] === secondNeuronWithCharge.idx) return dblConn;
- // if (dblConn.from[1] === overcharged.idx && dblConn.from[0] === secondNeuronWithCharge.idx) return dblConn;
- return ret;
- }, null)
- var toNode = _state.neuron[secondNeuronWithCharge.idx]
- DBG && console.log("DBG:NeuronStore:forwardAnim:2.1(check overcharged)/neuron.2(secondNeuronWithCharge)", { from: firstNeuron.value, to: toNode.value, existingConn })
- if (existingConn) { // charge Neuron existingConn.to
- var idxNewNeuron = existingConn.to
- this_charge_remove({ type: 'neuron', idx: overcharged.idx })
- this_charge_remove({ type: 'neuron', idx: secondNeuronWithCharge.idx })
- switch (_config.config_strategy_overcharge) {
- case "REMOVE_CHARGE": break;
- case "LEAVE_HALF_CHARGE": {
- this_charge_add(overcharged.charge / 2, { type: 'neuron', idx: overcharged.idx })
- this_charge_add(secondNeuronWithCharge.charge / 2, { type: 'neuron', idx: secondNeuronWithCharge.idx })
- } break;
- case "LEAVE_ALMOST_MAX": {
- this_charge_add(getNeuronAlmostMaxCharge(overcharged.idx), { type: 'neuron', idx: overcharged.idx })
- this_charge_add(getNeuronAlmostMaxCharge(secondNeuronWithCharge.idx), { type: 'neuron', idx: secondNeuronWithCharge.idx })
- } break;
- }
- this_charge_add(overcharged.charge + secondNeuronWithCharge.charge, { type: 'neuron', idx: existingConn.to })
- // _state.animPos += 1
- // dischargeAll()
- _notifySubscribers()
- return;
- } else { // create new
- var newNeuron = makeNeuronFromTwoNeurons(overcharged.idx, overcharged.charge, secondNeuronWithCharge.idx, secondNeuronWithCharge.charge)
- if (!newNeuron) {
- DBG && console.log("DBG:NeuronStore:forwardAnim:2(check overcharged)/neuron.2.1(!newNeuron)", { overcharged })
- // _state.animPos += 1
- dischargeAll()
- _notifySubscribers()
- return;
- } else {
- var idxNewNeuron = this_neuron_add(newNeuron)
- updateOuputHeight(newNeuron)
- var conn1Idx = this_connection_add({ fromType: 'neuron', from: overcharged.idx, to: idxNewNeuron, timesCreated: 1 })
- var conn2Idx = this_connection_add({ fromType: 'neuron', from: secondNeuronWithCharge.idx, to: idxNewNeuron, timesCreated: 1 })
- var dblConnIdx = this_doubleConn_add({ from: [overcharged.idx, secondNeuronWithCharge.idx], to: idxNewNeuron, fromConn: [conn1Idx, conn2Idx] })
- this_charge_remove({ type: 'neuron', idx: overcharged.idx })
- this_charge_remove({ type: 'neuron', idx: secondNeuronWithCharge.idx })
- var firstCharge = getNeuronAlmostMaxCharge(overcharged.idx)
- var secondCharge = getNeuronAlmostMaxCharge(secondNeuronWithCharge.idx)
- this_charge_add((firstCharge + secondCharge) / 2, { type: 'doubleConn', idx: dblConnIdx, connCharge: [firstCharge, secondCharge] })
- switch (_config.config_strategy_overcharge) {
- case "REMOVE_CHARGE": break;
- case "LEAVE_HALF_CHARGE": {
- this_charge_add(overcharged.charge / 2, { type: 'neuron', idx: overcharged.idx })
- this_charge_add(secondNeuronWithCharge.charge / 2, { type: 'neuron', idx: secondNeuronWithCharge.idx })
- } break;
- case "LEAVE_ALMOST_MAX": {
- // this_charge_add(getNeuronAlmostMaxCharge(overcharged.idx), { type: 'neuron', idx: overcharged.idx })
- // this_charge_add(getNeuronAlmostMaxCharge(secondNeuronWithCharge.idx), { type: 'neuron', idx: secondNeuronWithCharge.idx })
- } break;
- }
- // _state.animPos += 1
- // dischargeAll()
- _notifySubscribers()
- return;
- }
- }
- // // _state.animPos += 1
- // // dischargeAll()
- // _notifySubscribers()
- // return;
- } else { // !secondNeuronWithCharge
- // TODO: check if already exists! if yes then charge else create new
- var idxFoundConnection = _state.connection.reduce(function (ret, conn, idx) {
- if (ret > -1) return ret;
- if (conn.fromType === 'neuron' && conn.from === overcharged.idx) return idx;
- return ret;
- }, -1)
- DBG && console.log("DBG:NeuronStore:forwardAnim:2(check overcharged)/neuron.2(!secondNeuronWithCharge)", { idxFoundConnection })
- if (-1 === idxFoundConnection) {
- var newNeuron = makeNeuronFromOneNeuron(overcharged.idx, overcharged.charge)
- if (!newNeuron) {
- DBG && console.log("DBG:NeuronStore:forwardAnim:2(check overcharged)/neuron.2.1(!newNeuron)", { overcharged })
- // _state.animPos += 1
- dischargeAll()
- _notifySubscribers()
- return;
- } else {
- _state.neuron.push(newNeuron)
- updateOuputHeight(newNeuron)
- var idxNewNeuron = _state.neuron.length - 1
- this_connection_add({ fromType: 'neuron', from: overcharged.idx, to: idxNewNeuron, timesCreated: 1 })
- this_charge_remove({ type: 'neuron', idx: overcharged.idx })
- switch (_config.config_strategy_overcharge) {
- case "REMOVE_CHARGE": break;
- case "LEAVE_HALF_CHARGE": {
- this_charge_add(overcharged.charge / 2, { type: 'neuron', idx: overcharged.idx })
- } break;
- case "LEAVE_ALMOST_MAX": {
- this_charge_add(getNeuronAlmostMaxCharge(overcharged.idx), { type: 'neuron', idx: overcharged.idx })
- } break;
- }
- _notifySubscribers()
- return;
- }
- } else {
- _state.connection[idxFoundConnection].timesCreated += 1
- // chargeMove(overcharged.charge, { type: 'receptor', idx: overcharged.idx }, { type: 'connection', idx: idxFoundConnection })
- this_charge_remove({ type: 'neuron', idx: overcharged.idx })
- switch (_config.config_strategy_overcharge) {
- case "REMOVE_CHARGE": break;
- case "LEAVE_HALF_CHARGE": {
- this_charge_add(overcharged.charge / 2, { type: 'neuron', idx: overcharged.idx })
- } break;
- case "LEAVE_ALMOST_MAX": {
- this_charge_add(getNeuronAlmostMaxCharge(overcharged.idx), { type: 'neuron', idx: overcharged.idx })
- } break;
- }
- this_charge_add(overcharged.charge, { type: 'connection', idx: idxFoundConnection })
- }
- _notifySubscribers()
- return;
- }
- // _notifySubscribers()
- // return;
- }
- if (overchargedReceptorIdx.length > 0) {
- while (overchargedReceptorIdx.length) {
- var overcharged = overchargedReceptorIdx.shift()
- // Restriction: only one connection between Receptor and Neuron
- {
- var idxFoundConnection = _state.connection.reduce(function (ret, conn, idx) {
- if (ret > -1) return ret;
- if (conn.fromType === 'receptor' && conn.from === overcharged.idx) return idx;
- return ret;
- }, -1)
- DBG && console.log("DBG:NeuronStore:forwardAnim:2(check overcharged)/receptor", { idxFoundConnection, from: { type: 'receptor', idx: overcharged.idx }, conn: [].concat(_state.connection) })
- if (-1 === idxFoundConnection) {
- var newNeuron = makeNeuronFromOneReceptor(overcharged.idx, overcharged.charge)
- _state.neuron.push(newNeuron)
- var idxNewNeuron = _state.neuron.length - 1
- idxFoundConnection = this_connection_add({ fromType: 'receptor', from: overcharged.idx, to: idxNewNeuron, timesCreated: 1 })
- this_charge_remove({ type: 'receptor', idx: overcharged.idx })
- // switch (_config.config_strategy_overcharge) {
- // case "REMOVE_CHARGE": break;
- // case "LEAVE_HALF_CHARGE": {
- // this_charge_add(overcharged.charge / 2, { type: 'receptor', idx: overcharged.idx })
- // } break;
- // case "LEAVE_ALMOST_MAX": {
- // this_charge_add(getReceptorAlmostMaxCharge(overcharged.idx), { type: 'receptor', idx: overcharged.idx })
- // } break;
- // }
- this_charge_add(overcharged.charge, { type: 'connection', idx: idxFoundConnection })
- } else {
- _state.connection[idxFoundConnection].timesCreated += 1
- // chargeMove(overcharged.charge, { type: 'receptor', idx: overcharged.idx }, { type: 'connection', idx: idxFoundConnection })
- this_charge_remove({ type: 'receptor', idx: overcharged.idx })
- // switch (_config.config_strategy_overcharge) {
- // case "REMOVE_CHARGE": break;
- // case "LEAVE_HALF_CHARGE": {
- // this_charge_add(overcharged.charge / 2, { type: 'receptor', idx: overcharged.idx })
- // } break;
- // case "LEAVE_ALMOST_MAX": {
- // this_charge_add(getReceptorAlmostMaxCharge(overcharged.idx), { type: 'receptor', idx: overcharged.idx })
- // } break;
- // }
- this_charge_add(overcharged.charge, { type: 'connection', idx: idxFoundConnection })
- }
- }
- }
- _notifySubscribers()
- return;
- }
- // DBG && console.log("DBG:NeuronStore:forwardAnim:2(check overcharged)", { overchargedNeuronIdx, overchargedReceptorIdx })
- }
- { // 3. move all charges (Receptor -> Connection, Connection -> Neuron, Neuron -> Connection)
- DBG && console.log("TODO:NeuronStore:forwardAnim:3(move all charges)", { charge: [].concat(_state.charge) })
- var isChargeTypeConnection = function (charge) {
- if ("connection" === charge.nodeType) return true;
- if ("doubleConn" === charge.nodeType) return true;
- return false;
- }
- var toAddChargeNeuronIdxList = _state.charge.filter(isChargeTypeConnection).map(function (charge) {
- DBG && console.log("TODO:NeuronStore:forwardAnim:3(move all charges).LOOP", { charge })
- switch (charge.nodeType) {
- case "connection": return { charge: charge.value, to: _state.connection[charge.node].to };
- case "doubleConn": return { charge: charge.value, to: _state.doubleConn[charge.node].to };
- }
- return null;
- })
- _state.charge = _state.charge.filter(function (charge) { return !isChargeTypeConnection(charge); })
- toAddChargeNeuronIdxList.forEach(function (toAddCharge) {
- this_charge_add(toAddCharge.charge, { type: 'neuron', idx: toAddCharge.to })
- })
- }
- { // 4. discharge all
- dischargeAll()
- }
- _state.animPos += 1
- _notifySubscribers();
- }
- function dischargeAll() {
- _state.charge = _state.charge.map(function (charge) {
- return Object.assign({}, charge, {
- value: Math.max(0, charge.value - _config.config_discharge_per_tick),
- })
- }).filter(function (charge) {
- return charge.value > 0;
- })
- }
- function makeNeuronFromTwoNeurons(fromNeuronIdx, fromNeuronCharge, toNeuronIdx, toNeuronCharge) {
- var fromNode = _state.neuron[fromNeuronIdx]
- var toNode = _state.neuron[toNeuronIdx]
- var maxCharge = getNewNeuronMaxCharge(fromNode)
- DBG && console.log("DBG:NeuronStore:makeNeuronFromTwoNeurons", { fromNode, toNode, maxCharge })
- if (maxCharge < _config.config_discharge_per_tick) return null;
- var uiNewNodePos = { cx: 0, cy: 0 }
- { // closer to node with more charge (first)
- var xDiff = Math.abs(fromNode.ui.cx - toNode.ui.cx)
- var isFirstOnRight = (fromNode.ui.cx > toNode.ui.cx)
- if (0 === xDiff) {
- uiNewNodePos.cx = fromNode.ui.cx + (isFirstOnRight ? -1 : 1) * 20
- } else {
- // fromNeuronCharge + toNeuronCharge -- xDiff
- // toNeuronCharge -- x ==> x = toNeuronCharge * xDiff / (fromNeuronCharge + toNeuronCharge)
- var xToFirst = (toNeuronCharge * xDiff) / (fromNeuronCharge + toNeuronCharge)
- xToFirst = Math.max(40, xToFirst)
- uiNewNodePos.cx = fromNode.ui.cx + (isFirstOnRight ? -1 : 1) * xToFirst
- }
- }
- uiNewNodePos.cy = (fromNode.ui.cy != toNode.ui.cy)
- ? Math.max(fromNode.ui.cy, toNode.ui.cy) + 10 + _config.ui_space_y / 2
- : fromNode.ui.cy + 10 + _config.ui_space_y
- ;
- uiNewNodePos.cy = _state.neuron.reduce(function (ret, neuron) {
- if (uiNewNodePos.cx < neuron.ui.cx - 10) return ret;
- if (uiNewNodePos.cx > neuron.ui.cx + 10) return ret;
- if (ret < neuron.ui.cy - 10) return ret;
- if (ret > neuron.ui.cy + 10) return ret;
- return ret + 10 + _config.ui_space_y / 2;
- }, uiNewNodePos.cy)
- DBG && console.log("DBG:NeuronStore:makeNeuronFromTwoNeurons", { uiNewNodePos, fromNode, toNode, newNodeMaxCharge: getNewNeuronMaxCharge(fromNode) })
- // var value = '' + (_state.neuron.length + 1);
- var value = [toNode.value, fromNode.value].join('')
- return Object.assign({}, DEFAULT_NEURON, {
- value: value,
- maxCharge: getNewNeuronMaxCharge(fromNode),
- uiShape: "ellipse",
- ui: Object.assign({}, uiNewNodePos, {
- rx: 10,
- ry: 10,
- }),
- });
- }
- function makeNeuronFromOneReceptor(fromReceptorIdx, charge) {
- var sourceNode = _state.receptor[fromReceptorIdx]
- // var value = '' + (_state.neuron.length + 1);
- var value = [sourceNode.value, ""].join('') // TODO: same name what Receptor
- DBG && console.log("DBG:NeuronStore:makeNeuronFromOneReceptor", { sourceNode, newNodeMaxCharge: getNewNeuronMaxCharge(sourceNode) })
- return Object.assign({}, DEFAULT_NEURON, {
- value: value,
- maxCharge: getNewNeuronMaxCharge(sourceNode),
- uiShape: "ellipse",
- ui: {
- cx: sourceNode.ui.cx,
- cy: sourceNode.ui.cy + 10 + _config.ui_space_y,
- rx: 10,
- ry: 10,
- },
- });
- }
- function makeNeuronFromOneNeuron(fromNeuronIdx, charge) {
- var sourceNode = _state.neuron[fromNeuronIdx]
- // var value = '' + (_state.neuron.length + 1);
- var value = [sourceNode.value, "^"].join('')
- var maxCharge = getNewNeuronMaxCharge(sourceNode)
- if (maxCharge < _config.config_discharge_per_tick) return null;
- DBG && console.log("DBG:NeuronStore:makeNeuronFromOneNeuron", { sourceNode, newNodeMaxCharge: maxCharge })
- return Object.assign({}, DEFAULT_NEURON, {
- value: value,
- maxCharge: maxCharge,
- uiShape: "ellipse",
- ui: {
- cx: sourceNode.ui.cx,
- cy: sourceNode.ui.cy + 10 + _config.ui_space_y,
- rx: 10,
- ry: 10,
- },
- });
- }
- function getNewNeuronMaxCharge(sourceNode) {
- // return sourceNode.maxCharge * _config.config_discharge_max_in_new_neuron_from_one;
- return sourceNode.maxCharge - _config.config_discharge_per_tick;
- }
- function this_neuron_add(neuron) {
- _state.neuron.push(neuron)
- return _state.neuron.length - 1;
- }
- function this_doubleConn_add(doubleConn) {
- _state.doubleConn.push(doubleConn)
- return _state.doubleConn.length - 1;
- }
- function this_connection_add(conn) {
- _state.connection.push(conn)
- return _state.connection.length - 1;
- }
- function this_charge_add(charge, to) {
- var foundChargeIdx = _state.charge.reduce(function (ret, charge, idx) {
- if (charge.nodeType === to.type && charge.node === to.idx) return idx;
- return ret;
- }, -1)
- DBG && console.log("DBG:NeuronStore:this_charge_add.1", { charge, to, foundChargeIdx })
- var chargeIdx = foundChargeIdx
- if (-1 === foundChargeIdx) {
- _state.charge.push({ value: charge, nodeType: to.type, node: to.idx })
- chargeIdx = _state.charge.length - 1
- } else {
- _state.charge[chargeIdx].value += charge
- }
- if ('doubleConn' === to.type) { // connCharge: [ firstCharge, secondCharge ]
- if (-1 === foundChargeIdx) {
- _state.charge[chargeIdx].connCharge = to.connCharge
- } else {
- _state.charge[chargeIdx].connCharge[0] += to.connCharge[0]
- _state.charge[chargeIdx].connCharge[1] += to.connCharge[1]
- }
- }
- DBG && console.log("DBG:NeuronStore:this_charge_add.2", { state_charge: [].concat(_state.charge) })
- return chargeIdx;
- }
- function this_charge_remove(from) {
- _state.charge = _state.charge.filter(function (charge) {
- return !(from.type === charge.nodeType && from.idx === charge.node);
- })
- }
- function updateOuputHeight(newNeuron) {
- if (newNeuron.ui.cy + 30 > _state.uiOutputHeight) {
- _state.uiOutputHeight = newNeuron.ui.cy + 30
- }
- }
- function dispatch(payload) {
- DBG && console.log("DBG:NeuronStore:dispatch('" + payload.type + "')", payload);
- switch (payload.type) {
- case 'INIT': stateReset(payload.input, payload.config); startAnimation(); break;
- case 'INPUT_UPDATED': inputUpdated(payload.input); break;
- case 'INPUT_ADD_LINE': inputAddLine(payload.input); break;
- case 'PAUSE': _state.doAnim = false; _notifySubscribers(); break;
- case 'PLAY': _state.doAnim = true; break;
- default: {
- DBG && console.warn("Not implemented dispatch action type '" + payload.type + "'")
- }
- }
- }
- function subscribe(callback) {
- _callback = callback; // TODO: array
- return unsubscribe;
- }
- function unsubscribe() {
- _callback = null
- }
- function _notifySubscribers() {
- if (_callback) _callback()
- }
- function makeReceptor(value, idx, arr) { // usage: [].map(makeReceptor)
- var totalReceptors = arr.length;
- var cx = Math.ceil(_config.ui_output_width / (totalReceptors + 1));
- var r = Math.min(Math.ceil(_config.ui_output_width / (totalReceptors + 10) / 2), _config.ui_max_receptor_r);
- var xCenter = cx + cx * idx - r;
- return Object.assign({}, DEFAULT_NEURON, {
- value: value,
- maxCharge: _config.config_max_receptor_charge,
- uiShape: "circle",
- ui: {
- cx: xCenter,
- cy: 40,
- r: r,
- },
- next: [],
- })
- }
- function getReceptorIdx(char) {
- return _state.mapReceptorChar.indexOf(char)
- }
- function startAnimation() {
- _state.doAnim = true
- if (_animIntervalID) clearInterval(_animIntervalID)
- _animIntervalID = setInterval(forwardAnim, _config.config_anim_speed)
- }
- function getReceptorCharge(idx) {
- return _state.charge.filter(function (charge) {
- return ('receptor' === charge.nodeType && idx === charge.node);
- }).reduce(function (ret, charge) {
- return ret + charge.value;
- }, 0)
- }
- function getNeuronCharge(idx) {
- return _state.charge.filter(function (charge) {
- return ('neuron' === charge.nodeType && idx === charge.node);
- }).reduce(function (ret, charge) {
- return ret + charge.value;
- }, 0)
- }
- function getReceptorAlmostMaxCharge(idx) {
- return _state.receptor[idx].maxCharge - _config.config_discharge_per_tick;
- }
- function getNeuronAlmostMaxCharge(idx) {
- DBG && console.log("DBG:getNeuronAlmostMaxCharge(" + idx + ")", { charge: _state.neuron[idx].maxCharge - _config.config_discharge_per_tick })
- return _state.neuron[idx].maxCharge - _config.config_discharge_per_tick;
- }
- function getNodesChargeState() {
- var receptor = [].concat(_state.receptor);
- var neuron = [].concat(_state.neuron);
- _state.charge.forEach(function (charge) {
- // _state.charge: [ { value: number, nodeType: (receptor|neuron), node: int }, ... ] groupd by node (type/idx)
- switch (charge.nodeType) {
- case 'receptor': receptor[charge.node].charge = charge.value; break;
- case 'neuron': neuron[charge.node].charge = charge.value; break;
- }
- })
- function getNodeInfoFunction(type) {
- return function _getNodeInfo(node) {
- return {
- type: type,
- value: node.value,
- charge: node.charge,
- }
- }
- }
- return receptor.map(getNodeInfoFunction('receptor'))
- .concat(
- neuron.map(getNodeInfoFunction('neuron'))
- )
- ;
- }
- return {
- subscribe: subscribe,
- dispatch: dispatch,
- getState: function () { return _state; },
- getNodesChargeState: getNodesChargeState,
- getListReceptor: function () { return _state.receptor; },
- getListNeuron: function () { return _state.neuron; },
- getReceptor: function (idx) { return _state.receptor[idx]; },
- getNeuron: function (idx) { return _state.neuron[idx]; },
- getConnection: function (idx) { return _state.connection[idx]; },
- getDoubleConnection: function (idx) { return _state.doubleConn[idx]; },
- getCharge: function (idx) { return _state.charge[idx]; },
- getListConnection: function () { return _state.connection; },
- getListCharge: function () { return _state.charge; },
- getReceptorCharge: getReceptorCharge,
- getNeuronCharge: getNeuronCharge,
- getInput: function () { return [].concat(_input); },
- }
- }
- // export default makeNeuronStore
- global[MAKE_STORE_FUNCTION_NAME] = makeNeuronStore
|