What is confusing is how this simple script works fine:
function A() {
this.value = 0;
}
A.prototype.foo = function() {
console.log(this.value);
};
function B() {
this.value = 1;
this.foo();
}
B.prototype = Object.create(A.prototype);
B.prototype.bar = function() {
console.log(this instanceof A);
}
new B().bar();
// outputs 1, true
However, this larger script gives an error this.loadDimensions is not a function:
Basically, there is a Player class, which inherits from a MovingComponent class, which inherits from a VisibleComponent class. They all have methods attached to them.
const PX_SZ = 4, MAX_HEIGHT = 100, MIN_HEIGHT = 300;
var resources = {};
resources.sprites = {};
resources.sprites.player = new Image();
resources.sprites.player.src = "resources/sprites/player.png";
resources.sprites['default'] = new Image();
resources.sprites['default'].src = "resources/sprites/default.png";
resources.sprites.items = {};
resources.sprites.backgroundEntities = {};
var itemsTemp = ['default', 'coin0'];
for (var i=0; i<itemsTemp.length; i++) {
var item = itemsTemp[i];
resources.sprites.items[item] = new Image();
resources.sprites.items[item].src = "resources/sprites/items/" + item + ".png";
}
var backgroundEntitiesTemp = ['tree0'];
for (var i=0; i<backgroundEntitiesTemp.length; i++) {
var ent = backgroundEntitiesTemp[i];
resources.sprites.backgroundEntities[ent] = new Image();
resources.sprites.backgroundEntities[ent].src = "resources/sprites/background-entities/" + ent + ".png";
}
var canvas, ctx;
var player = new Player();
var keys = {};
var game = new Game(Math.floor(Math.random()*1000000));
var world = new World();
/** #class */
function Game(seed) {
this.seed = seed;
}
/** #class */
function World() {
this.gravity = 0.4;
this.chances = {
items: {
coin0: 0.005
},
backgroundEntities: {
tree0: 0.05
}
};
this.itemsFloating = [];
this.backgroundEntities = [];
// for spawning
this.exploredRightBound = 0;
this.exploredLeftBound = 0;
}
World.prototype.generate = function(left, right) {
if (left >= right) throw "left >= right in World#generate(left,right)";
for (x = left; x < right; x += PX_SZ) {
// world generation code here
// coin0
var level = getGroundHeightAt(x)
if (Math.random() <= this.chances.items.coin0) {
var item = new ItemFloating("coin0", x, level-20);
this.itemsFloating.push(item);
}
if (Math.random() <= this.chances.backgroundEntities.tree0) {
var ent = new BackgroundEntity("tree0", x, level-resources.sprites.backgroundEntities.tree0.height);
this.backgroundEntities.push(ent);
}
}
};
/**
* #class
* anything that has a sprite attached to it
*/
function VisibleComponent() {
this.sprite = resources.sprites['default'];
}
VisibleComponent.prototype.loadDimensions = function() {
console.log('load');
};
VisibleComponent.prototype.draw = function() {
ctx.drawImage(this.sprite, this.x, this.y, this.width, this.height);
};
/** #class */
function Item(name="default") {
VisibleComponent.call(this);
this.name = name || "default";
this.sprite = resources.sprites.items[name];
this.loadDimensions();
}
Item.prototype = Object.create(VisibleComponent.prototype);
/** #class */
function ItemFloating(name, x, y) {
Item.call(this, name);
this.name = name;
this.x = x;
this.y = y;
this.loadDimensions(); // (when ready of now)
}
ItemFloating.prototype = Object.create(Item.prototype);
/** #class */
function BackgroundEntity(name="default", x=0, y=0) {
VisibleComponent.call(this);
this.name = name;
this.x = x;
this.y = y;
this.width = 1;
this.height = 1;
this.sprite = resources.sprites.backgroundEntities[this.name];
this.loadDimensions();
}
BackgroundEntity.prototype = Object.create(VisibleComponent.prototype);
/** #class */
function MovingEntity(x=0, y=0) {
VisibleComponent.call(this);
this.x = x;
this.y = y;
this.width = 1;
this.height = 1;
}
MovingEntity.prototype = Object.create(VisibleComponent.prototype);
MovingEntity.prototype.collisionWith = function(ent) {
return ((this.x>=ent.x&&this.x<=ent.x+ent.width) || (ent.x>=this.x&&ent.x<=this.x+this.width))
&& ((this.y>=ent.y&&this.y<=ent.y+ent.height) || (ent.y>=this.y&&ent.y<=this.y+this.height));
};
/** #class */
function Player() {
MovingEntity.call(this);
this.inventory = {};
console.log(this instanceof VisibleComponent);
this.speed = 4;
this.jumpSpeed = 8;
this.vspeed = 0;
this.sprite = resources.sprites.player;
this.loadDimensions();
this.direction = "right";
}
Player.prototype = Object.create(MovingEntity.prototype);
Player.prototype.draw = function() {
ctx.save();
ctx.translate(this.x, this.y);
if (this.direction == "left") ctx.scale(-1, 1); // flip over y-axis
ctx.translate(-this.sprite.width, 0);
ctx.drawImage(this.sprite, 0, 0, this.width, this.height);
ctx.restore();
}
Player.prototype.move = function() {
if (keys['ArrowLeft']) {
this.x -= this.speed;
this.direction = "left";
var leftEdge = this.x-canvas.width/2-this.width/2;
if (leftEdge < world.exploredLeftBound) {
world.generate(leftEdge, world.exploredLeftBound);
world.exploredLeftBound = leftEdge;
}
}
if (keys['ArrowRight']) {
this.x += this.speed;
this.direction = "right";
var rightEdge = this.x+canvas.width/2+this.width/2;
if (rightEdge > world.exploredRightBound) {
world.generate(world.exploredRightBound, rightEdge);
world.exploredRightBound = rightEdge;
}
}
var level = getGroundHeightAt(this.x+this.width/2);
if (this.y + this.height < level) {
this.vspeed -= world.gravity;
} else if (this.y + this.height > level) {
this.y = level - this.height;
this.vspeed = 0;
}
if (keys[' '] && this.y+this.height == getGroundHeightAt(this.x+this.width/2)) this.vspeed += this.jumpSpeed;
this.y -= this.vspeed;
for (var i=0; i<world.itemsFloating.length; i++) {
var item = world.itemsFloating[i];
if (this.collisionWith(item)) {
if (this.inventory.hasOwnProperty(item.name)) this.inventory[item.name]++;
else this.inventory[item.name] = 1;
world.itemsFloating.splice(i, 1);
}
}
};
I'm fairly new to javascript inheritance, so I don't understand what I'm doing wrong. Also, since the first script worked, I figured there's something I'm just overlooking in my second script. Any help would be appreciated.
EDIT
In the beginning of the file, I declare player as a new Player(). resources contains Image instances that point to various image files. ctx and canvas are pretty self-explanatory globals.
Also, player isn't recognized as an instance of MovingEntity or VisibleComponent, even though Player's prototype is set to Object.create(MovingEntity.prototype), which has its prototype set to Object.create(VisibleComponent.prototype).
One other thing to mention is that in the definition of loadDimensions() in VisibleComponent, either the onload property of this.sprite is set to a function, or addEventListener() is called for 'load', depending on whether this.sprite has loaded (width != 0) or not.
In the beginning of the file, I declare player as a new Player().
That's the problem, you need to call the constructor after having set up your class. It currently doesn't throw an error about Player not being a function because the declaration is hoisted, but the prototype is not yet initialised with the value you expect so it indeed does not have a .loadDimensions() method yet.
The function in question is rendering the neural network represented on the right side of this image based on a fairly simple data structure.
Trying to evolve:
After evolving:
Each dot represents a neuron, each line a connection. There's a problem with which neurons and connections are being rendered and which aren't, and I've been wrestling with this problem for 5 hours straight, no coffee breaks. One of you is likely going to point out one tiny stupid mistake causing the issue and I'll likely proceed to pull my hair out.
Put simply: Connections run from the top down. I only want to render complete paths from top to bottom. No dots (neurons) in the middle should be rendered if no connections lead to them from the top. If a top layer neuron connects to a middle layer neuron but that middle layer neuron doesn't connect to a bottom layer neuron, that connection isn't doing anything so it shouldn't be rendered.
As you can see, there are top level neurons that are rendered with no connections at all, and middle level neurons which are rendered with no connection to the top. All connections are top-down, no connections flow upward. The network is feed-forward in other words.
The data structure passed to this function is brain which is the same brain passed to the following Neuron and Connection constructors which are listed in abridged form only showing the properties relevant to the function in question (edited from the original with further attempts to fix the problem:
Neuron
function Neuron(brain, layer) {
var that = this;
brain.counter++;
brain.globalReferenceNeurons[brain.counter] = this;
this.active = true; //as the brain mutates, some neurons and
//connections are disabled via this property
this.layer = layer;
this.id = brain.counter;
this.connected = {};
this.connections = {};
this.connect = function(target) {
if (that.active == true) {
new Connection(brain, this, target, function(id, connection) {
brain.globalReferenceConnections[id] = connection;
that.connections[id] = connection;
});
}
};
}
Connection
function Connection(brain, source, target, callback) {
if (source.layer < target.layer) {
brain.counter++;
brain.globalReferenceConnections[brain.counter] = this;
this.active = true; //as the brain mutates, some neurons and
//connections are disabled via this property
this.id = brain.counter;
this.source = source;
this.target = target;
target.connected[this.id] = this; //connected references
//incoming connections to a neuron
callback(this.id, this);
}
}
As you can see, brain.globalReferenceNeurons contains the the data needed to render the neural network in the picture.
And here's the rendering function in question (updated again):
function renderBrain(brain, context, canvas) {
context.clearRect(0, 0, canvas.width, canvas.height);
var width = canvas.width;
var height = canvas.height;
var layers = brain.layers;
var heightDivision = height / layers;
var layerList = [];
for (var i1 = 0; i1 < brain.layers; i1++) {
layerList.push([]);
for (var prop1 in brain.globalReferenceNeurons) {
if (brain.globalReferenceNeurons[prop1].layer === i1) {
layerList[i1].push(brain.globalReferenceNeurons[prop1]);
}
}
}
function renderLayer(layer, layerCount, layerTotal) {
var length = layer.length;
var widthDivision = width / length;
var neuronCount = 0;
for (var i1 = 0; i1 < layer.length; i1++) {
neuronCount++;
const getActiveProps = obj => Object.keys(obj).filter(k => obj[k].active)
function hasActivePathAhead(obj, count) {
if (!count) {
count = 0;
}
if (obj.active) {
var targets = getActiveProps(obj.connections);
if (obj.layer === 2) {
return true;
} else if (obj.connections[targets[count]]) {
for (var i1 = 0; i1 < targets.length; i1++) {
var result = hasActivePathAhead(obj.connections[targets[count]].target,
count + 1);
return result;
}
return false;
} else {
return false;
}
} else {
return false;
}
}
function hasActivePathBehind(obj, count) {
if (!count) {
count = 0;
}
if (obj.active) {
var sources = getActiveProps(obj.connected);
if (obj.layer === 0) {
return true;
} else if (obj.connected[sources[count]]) {
for (var i1 = 0; i1 < sources.length; i1++) {
var result =
hasActivePathBehind(obj.connected[sources[count]].source, count + 1);
return result;
}
return false;
} else {
return false;
}
} else {
return false;
}
}
if (hasActivePathAhead(layer[i1]) && hasActivePathBehind(layer[i1])) {
context.beginPath();
context.arc((widthDivision * neuronCount)
- (0.5 * widthDivision),
(heightDivision * layerCount)
- (heightDivision * 0.5),
5, 0, 2 * Math.PI, false);
context.fillStyle = '#adf442';
context.fill();
context.lineWidth = 2;
context.strokeStyle = '#56cc41';
context.stroke();
var connectionCount = 0;
for (var i2 = 0; i2 < Object.keys(layer[i1].connections).length; i2++) {
var connection =
layer[i1].connections[Object.keys(layer[i1].connections)[i2]];
if (hasActivePathAhead(connection.target)
&& hasActivePathBehind(connection.target)) {
var targetLayer = connection.target.layer;
var index = layerList[targetLayer].findIndex(function(e) {
return e == connection.target
});
if (index > -1) {
var targetLayerLength = Object.keys(layerList[targetLayer]).length;
var targetLayerWidthDivision = width / targetLayerLength;
var p1 = {
x: (widthDivision * neuronCount) - (0.5 * widthDivision),
y: (heightDivision * layerCount) - (heightDivision * 0.5)
};
var p2 = {
x: (index * targetLayerWidthDivision)
+ (0.5 * targetLayerWidthDivision),
y: (targetLayer * heightDivision)
+ (heightDivision * 0.5)
};
connectionCount++;
context.beginPath();
context.moveTo(p1.x, p1.y);
context.lineTo(p2.x, p2.y);
context.lineWidth = 1;
context.stroke();
}
}
}
}
}
}
var layerCount = 0;
for (i1 = 0; i1 < layerList.length; i1++) {
layerCount++;
renderLayer(layerList[i1], layerCount, layerList.length);
}
}
"Working" test example: https://jsfiddle.net/au2Lt6na/4/
For 5 hours I've tinkered with this function trying to pinpoint the issue and for the life of me I haven't been able to figure it out. Can anyone tell me what's causing the rendering of non-top-to-bottom neural pathways?
Note: I've spent many more hours over the past few days trying to fix this, writing totally new ways of figuring out which paths are complete from top to bottom and it still suffers from the same issues as before. I'm missing something here.
for (var i1 = 0; i1 < targets.length; i1++) {
var result = hasActivePathAhead(obj.connections[targets[count]].target,
count + 1);
return result;
}
This snippet is weird. You may need this instead:
for (var i1 = 0; i1 < targets.length; i1++) {
var result = hasActivePathAhead(obj.connections[targets[count]].target,
count + 1);
if(result){
return true;
}
}
And the usage of count is weird there. I think the count should not be passed as a param, since it's used to index a target or source.
I think the snippet should be like this:
else if (targets.length) {
var target;
for (var i1 = 0; i1 < targets.length; i1++) {
target=targets[i1];
var result = hasActivePathAhead(obj.connections[target].target);
if(result){
return true;
}
}
return false;
}
Live demo here:
function Neuron(brain, layer) {
var that = this;
brain.counter++;
brain.globalReferenceNeurons[brain.counter] = this;
this.active = true; //as the brain mutates, some neurons and
//connections are disabled via this property
this.layer = layer;
this.id = brain.counter;
this.connected = {};
this.connections = {};
this.connect = function (target) {
if (that.active == true) {
new Connection(brain, this, target, function (id, connection) {
brain.globalReferenceConnections[id] = connection;
that.connections[id] = connection;
});
}
};
}
function Connection(brain, source, target, callback) {
if (source.layer < target.layer) {
brain.counter++;
brain.globalReferenceConnections[brain.counter] = this;
this.active = true; //as the brain mutates, some neurons and
//connections are disabled via this property
this.id = brain.counter;
this.source = source;
this.target = target;
target.connected[this.id] = this;
callback(this.id, this);
}
}
function renderBrain(brain, context, canvas) {
context.clearRect(0, 0, canvas.width, canvas.height);
var width = canvas.width;
var height = canvas.height;
var layers = brain.layers;
var heightDivision = height / layers;
var layerList = [];
for (var i1 = 0; i1 < brain.layers; i1++) {
layerList.push([]);
for (var prop1 in brain.globalReferenceNeurons) {
if (brain.globalReferenceNeurons[prop1].layer === i1) {
layerList[i1].push(brain.globalReferenceNeurons[prop1]);
}
}
}
function renderLayer(layer, layerCount, layerTotal) {
var length = layer.length;
var widthDivision = width / length;
var neuronCount = 0;
for (var i1 = 0; i1 < layer.length; i1++) {
neuronCount++;
const getActiveProps = obj => Object.keys(obj).filter(k => obj[k].active)
function hasActivePathAhead(obj) {
if (obj.active) {
var targets = getActiveProps(obj.connections);
if (obj.layer === 2) {
return true;
} else if (targets.length) {
var target;
for (var i1 = 0; i1 < targets.length; i1++) {
target = targets[i1];
var result = hasActivePathAhead(obj.connections[target].target);
if (result) {
return true;
}
}
return false;
} else {
return false;
}
} else {
return false;
}
}
function hasActivePathBehind(obj) {
if (obj.active) {
var sources = getActiveProps(obj.connected);
if (obj.layer === 0) {
return true;
} else if (sources.length) {
var source;
for (var i1 = 0; i1 < sources.length; i1++) {
source = sources[i1];
var result =
hasActivePathBehind(obj.connected[source].source);
return result;
}
return false;
} else {
return false;
}
} else {
return false;
}
}
if (hasActivePathAhead(layer[i1]) && hasActivePathBehind(layer[i1])) {
context.beginPath();
context.arc((widthDivision * neuronCount) -
(0.5 * widthDivision),
(heightDivision * layerCount) -
(heightDivision * 0.5),
5, 0, 2 * Math.PI, false);
context.fillStyle = '#adf442';
context.fill();
context.lineWidth = 2;
context.strokeStyle = '#56cc41';
context.stroke();
var connectionCount = 0;
for (var i2 = 0; i2 < Object.keys(layer[i1].connections).length; i2++) {
var connection =
layer[i1].connections[Object.keys(layer[i1].connections)[i2]];
if (hasActivePathAhead(connection.target) &&
hasActivePathBehind(connection.target)) {
var targetLayer = connection.target.layer;
var index = layerList[targetLayer].findIndex(function (e) {
return e == connection.target
});
if (index > -1) {
var targetLayerLength = Object.keys(layerList[targetLayer]).length;
var targetLayerWidthDivision = width / targetLayerLength;
var p1 = {
x: (widthDivision * neuronCount) - (0.5 * widthDivision),
y: (heightDivision * layerCount) - (heightDivision * 0.5)
};
var p2 = {
x: (index * targetLayerWidthDivision) +
(0.5 * targetLayerWidthDivision),
y: (targetLayer * heightDivision) +
(heightDivision * 0.5)
};
connectionCount++;
context.beginPath();
context.moveTo(p1.x, p1.y);
context.lineTo(p2.x, p2.y);
context.lineWidth = 1;
context.stroke();
}
}
}
}
}
}
var layerCount = 0;
for (i1 = 0; i1 < layerList.length; i1++) {
layerCount++;
renderLayer(layerList[i1], layerCount, layerList.length);
}
}
var brain = {
counter: 0,
layers: 3,
globalReferenceNeurons: {},
globalReferenceConnections: {},
}
var layer0 = [new Neuron(brain, 0), new Neuron(brain, 0), new Neuron(brain, 0),new Neuron(brain, 0), new Neuron(brain, 0)];
var layer1 = [new Neuron(brain, 1), new Neuron(brain, 1), new Neuron(brain, 1)];
var layer2 = [new Neuron(brain, 2), new Neuron(brain, 2), new Neuron(brain, 2), new Neuron(brain, 2)];
layer0[0].connect(layer1[1]);
layer0[1].connect(layer1[0]);
layer0[3].connect(layer1[0]);
layer1[0].connect(layer2[0]);
layer1[2].connect(layer2[2]);
layer1[1].connect(layer2[3]);
var canvas = document.getElementById('cav');
var ctx = canvas.getContext('2d');
renderBrain(brain, ctx, canvas);
<canvas id="cav" width="600" height="400"></canvas>
Could not fit into the comment and as you do not have a working example we can only guess.
Your recursion does not look right to me. The count variable makes no sense and you have several levels of redundancy checking for active 3 times for each iteration and not vetting when indexing into the key arrays with count.
Without working code and as your variable nomenclature is confusing this is only a guess at how to fix. Same applies to hasActivePathBehind
Ignore the following code
function hasActivePathAhead(obj) {
if (obj.active) {
if (obj.layer === 2) {
return true;
}
var targets = getActiveProps(obj.connections);
for (var i = 0; i < targets.length; i++) {
if(hasActivePathAhead(obj.connections[targets[i]].target)){
return true;
}
}
}
return false;
}
UPDATE and working fix.
Update because accepted answer does not work, it should have used a more rigorous test.
As you have provided a fiddle in the comments I had a look as see that you have removed the count from the functions. Though the functions are still incorrect and not working, the error is elsewhere in the code. The code is overly complex hiding the nature of the bug.
There is too much wrong to go into detail so I have just started from scratch.
Rather than test every node for a backward and forward link I traverse the layers forward from top to bottom. This negates the need to check backward connections. I have a function that checks if a node is connected to the bottom, a function that draws all nodes from a node to the bottom, and a function that draws all active nodes at a layer (active is connected from that layer down)
You can optimise it by adding a flag to nodes indicating that they have already been rendered as the code as is can render some nodes several times. But I did not add that as I did not want to modify the data structure you had. Or you can add a Map that holds node pairs that have been rendered and check that to see if a node pair needs to be rendered.
Using your fiddle as a template here is a working version using the randomised paths as provided in the fiddle.
function Neuron(brain, layer) {
var that = this;
brain.counter++;
brain.globalReferenceNeurons[brain.counter] = this;
this.active = true; //as the brain mutates, some neurons and
//connections are disabled via this property
this.layer = layer;
this.id = brain.counter;
this.connected = {};
this.connections = {};
this.connect = function (target) {
if (that.active == true) {
new Connection(brain, this, target, function (id, connection) {
brain.globalReferenceConnections[id] = connection;
that.connections[id] = connection;
});
}
};
}
function Connection(brain, source, target, callback) {
if (source.layer < target.layer) {
brain.counter++;
brain.globalReferenceConnections[brain.counter] = this;
this.active = true; //as the brain mutates, some neurons and
//connections are disabled via this property
this.id = brain.counter;
this.source = source;
this.target = target;
target.connected[this.id] = this;
callback(this.id, this);
}
}
function renderBrain(brain, ctx, canvas) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
var width = canvas.width;
var height = canvas.height;
var layers = brain.layers;
var heightDiv = height / layers;
var layerList = [];
for (var i1 = 0; i1 < brain.layers; i1++) {
layerList.push([]);
for (var prop1 in brain.globalReferenceNeurons) {
if (brain.globalReferenceNeurons[prop1].layer === i1) {
layerList[i1].push(brain.globalReferenceNeurons[prop1]);
}
}
}
var coord; // to hold node coordinates defined here to prevent pointless memory allocation dealocation cycle
// Gets the node position based on its ID and layer position
function nodePosition(node,coord = {}){
var pos;
pos = node.id - layerList[node.layer][0].id; // get pos from node id (if this does not hold true you should include the node position in the node data is it is important)
coord.x = (width / layerList[node.layer].length) * (pos + 0.5);
coord.y = heightDiv * (node.layer + 0.5);
return coord;
}
// draws a node
function drawNode(node){
ctx.strokeStyle = '#56cc41';
ctx.fillStyle = '#adf442';
ctx.lineWidth = 2;
coord = nodePosition(node,coord);
ctx.beginPath();
ctx.arc(coord.x,coord.y, 5, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
}
// draws a link between two nodes
function drawLink(node,node1){
ctx.strokeStyle = '#56cc41';
ctx.lineWidth = 1;
coord = nodePosition(node,coord);
ctx.beginPath();
ctx.moveTo(coord.x,coord.y);
coord = nodePosition(node1,coord);
ctx.lineTo(coord.x,coord.y);
ctx.stroke();
}
// returns true if the path from this node jas a connection that leads to the end
function isPathActive(node){
var paths, i, nextNode;
if(node.active){
if(node.layer === 2){ // is node at end
return true;
}
paths = Object.keys(node.connections).map(key => node.connections[key]);
for(i = 0; i < paths.length; i ++){
nextNode = paths[i].target;
if(nextNode.active){
if(nextNode.layer === 2){
return true;
}
if(isPathActive(nextNode)){
return true;
}
}
}
}
return false;
}
// renders from a node all active pathes to end
function renderPath(node){
var i;
paths = Object.keys(node.connections).map(key => node.connections[key]);
for(i = 0; i < paths.length; i ++){
nextNode = paths[i].target;
if(isPathActive(nextNode)){
drawLink(node,nextNode)
renderPath(nextNode);
}
}
drawNode(node,i+ 1)
}
// renders from top layer all active paths
function renderActivePaths(layer){
var i;
for(i = 0; i < layer.length; i ++){
if(isPathActive(layer[i])){
renderPath(layer[i])
}
}
}
renderActivePaths(layerList[0]);
}
var brain = {
counter: 0,
layers: 3,
globalReferenceNeurons: {},
globalReferenceConnections: {},
}
var layer0 = [new Neuron(brain, 0), new Neuron(brain, 0), new Neuron(brain, 0),
new Neuron(brain, 0), new Neuron(brain, 0), new Neuron(brain, 0),
new Neuron(brain, 0), new Neuron(brain, 0),new Neuron(brain, 0),
new Neuron(brain, 0)]; //10
var layer1 = [new Neuron(brain, 1), new Neuron(brain, 1), new Neuron(brain, 1),
new Neuron(brain, 1), new Neuron(brain, 1), new Neuron(brain, 1),
new Neuron(brain, 1), new Neuron(brain, 1), new Neuron(brain, 1),
new Neuron(brain, 1), new Neuron(brain, 1), new Neuron(brain, 1),
new Neuron(brain, 1)]; //13
var layer2 = [new Neuron(brain, 2), new Neuron(brain, 2)]; //2
layer0[0].connect(layer1[0]);
layer0[1].connect(layer1[1]);
layer0[2].connect(layer1[2]);
layer0[3].connect(layer1[3]);
layer0[4].connect(layer1[4]);
layer0[5].connect(layer1[5]);
layer0[6].connect(layer1[6]);
layer0[7].connect(layer1[7]);
layer0[8].connect(layer1[8]);
layer0[9].connect(layer1[9]);
layer0[0].connect(layer1[3]);
layer0[1].connect(layer1[4]);
layer0[2].connect(layer1[5]);
layer0[3].connect(layer1[6]);
layer0[4].connect(layer1[7]);
layer0[5].connect(layer1[8]);
layer0[6].connect(layer1[9]);
layer0[7].connect(layer1[10]);
layer0[8].connect(layer1[11]);
layer0[9].connect(layer1[12]);
layer1[0].connect(layer2[0]);
layer1[1].connect(layer2[1]);
layer1[2].connect(layer2[0]);
layer1[3].connect(layer2[1]);
layer1[4].connect(layer2[0]);
layer1[5].connect(layer2[1]);
layer1[6].connect(layer2[0]);
layer1[7].connect(layer2[1]);
layer1[8].connect(layer2[0]);
layer1[9].connect(layer2[1]);
layer1[10].connect(layer2[0]);
layer1[11].connect(layer2[1]);
layer1[12].connect(layer2[0]);
//works! until...
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
for (prop in brain.globalReferenceNeurons) {
var rand = getRandomInt(1,6);
var neuron = brain.globalReferenceNeurons[prop];
if (rand == 1 && neuron.layer != 2) neuron.active = false;
}
for (prop in brain.globalReferenceConnections) {
var rand = getRandomInt(1,6);
var connection = brain.globalReferenceConnections[prop];
if (rand == 1) connection.active = false;
}
renderBrain(brain, canvas.getContext("2d"), canvas);
<canvas id="canvas" width= 512 height = 200></canvas>
I have problem with canvas, I'm trying to draw images but is not working as you can see I'm loading images into array and wait for all is loaded after this I'm changing my images each iteration but is not drawing any one, please look at my code. I cannot find error :(
(() => {
"use strict";
const images = [];
const promises = [];
const url = './assets/';
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
const FPS = 30;
const INTERVAL = 10000 / FPS;
const canvasDraw = () => {
let i = 0;
setInterval(() => {
context.drawImage(images[i] , 300, 300);
i++;
if (i === images.length) i = 0;
}, INTERVAL);
};
const loadImage = (image) => {
return new Promise((resolve) => {
const img = new Image();
img.src = url + image + '.png';
img.onload = function () {
images.push(img);
resolve();
};
});
};
for(let i = 1; i < 14; i++) {
promises.push(loadImage(i));
}
Promise
.all(promises)
.then(() => {
canvasDraw();
});
})();
and my html file contains canvas like this one
<canvas id="canvas"></canvas>
You need to give your canvas a width and height.
Using placeholder images:
(() => {
"use strict";
const images = [];
const promises = [];
const url = './assets/';
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
const FPS = 30;
const INTERVAL = 10000 / FPS;
const canvasDraw = () => {
let i = 0;
setInterval(() => {
context.drawImage(images[i] , 0, 0);
i++;
if (i === images.length) i = 0;
}, INTERVAL);
};
const loadImage = (image) => {
return new Promise((resolve) => {
const img = new Image();
img.src = 'https://placehold.it/' + (image * 20) + 'x' + (image * 20);
img.onload = function () {
images.push(img);
resolve();
};
});
};
for(let i = 1; i < 14; i++) {
promises.push(loadImage(i));
}
Promise
.all(promises)
.then(() => {
canvasDraw();
});
})();
<canvas id="canvas" width="300" height="300"></canvas>
Depending on what you're doing, you may want to clear the canvas between renderings.
(() => {
"use strict";
const images = [];
const promises = [];
const url = './assets/';
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
const FPS = 30;
const INTERVAL = 10000 / FPS;
const canvasDraw = () => {
let i = 0;
setInterval(() => {
context.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
context.drawImage(images[i] , 0, 0);
i++;
if (i === images.length) i = 0;
}, INTERVAL);
};
const loadImage = (image) => {
return new Promise((resolve) => {
const img = new Image();
img.src = 'https://placehold.it/' + (image * 20) + 'x' + (image * 20);
img.onload = function () {
images.push(img);
resolve();
};
});
};
for(let i = 1; i < 14; i++) {
promises.push(loadImage(i));
}
Promise
.all(promises)
.then(() => {
canvasDraw();
});
})();
<canvas id="canvas" width="300" height="300"></canvas>
I'm trying to render an image from a sprite sheet using JS. The curious thing is, unless the object that does the rendering is global, it doesn't work (see code and comments). The behaviour is identical in both FF and Chrome.
resetGame() is executed on page load.
var TILE_SIZE = 24;
function CharacterImage(imageSource)
{
var tile_x = 0;
var tile_y = 0;
var img = new Image();
img.src = imageSource;
this.render = function(ctx, x, y)
{
ctx.drawImage(img, tile_x, tile_y, TILE_SIZE, TILE_SIZE,
x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE);
}
}
function Hero(canvas, image)
{
var ctx = canvas.getContext("2d");
var img = image;
this.render = function()
{
var x = 1;
var y = 1;
img.render(ctx, x, y);
}
}
// If the heroImage is constructed here, instead of within the function below,
// the image is rendered as expected.
var heroImage = new CharacterImage("img/sf2-characters.png");
function resetGame()
{
var heroCanvas = document.getElementById("heroLayer");
// On the otherhand, if the object is constructed here, instead of
// globally, the rendering doesn't work.
var heroImage = new CharacterImage("img/sf2-characters.png");
var hero = new Hero(heroCanvas, heroImage);
hero.render();
}
Oh hang on I think I see what's happening. The image needs time to load, so you should somehow bind an event to the loading of the image. This could be done as for example:
var TILE_SIZE = 24;
function CharacterImage(imageSource)
{
var tile_x = 0;
var tile_y = 0;
var img = new Image();
img.src = imageSource;
this.render = function(ctx, x, y)
{
ctx.drawImage(img, tile_x, tile_y, TILE_SIZE, TILE_SIZE,
x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE);
}
// Set up a "load" event for the img
this.loaded = function(callback) {
img.addEventListener('load', callback);
}
}
function resetGame()
{
var heroCanvas = document.getElementById("heroLayer");
var heroImage = new CharacterImage("img/sf2-characters.png");
var hero;
// Initiate the "load" event
heroImage.loaded(function() {
hero = new Hero(heroCanvas, heroImage);
hero.render();
};
}
What you'll probably want though is some sort of preloader "class"/event that keeps track of everything being loaded before you actually continue with rendering. It could look something like this.
var TILE_SIZE=60;
function Sprite(imageSource)
{
this.img = new Image();
this.img.src = imageSource;
this.position = { x:0, y:0 };
}
Sprite.prototype = {
isLoaded: function() {
return this.img.complete;
},
onLoad: function(callback) {
if (typeof callback !== "function") return;
if (this.isLoaded()) {
callback();
}
else {
this.img.removeEventListener('load', callback);
this.img.addEventListener('load', callback);
}
},
moveBy: function(x, y) {
this.position.x += x;
this.position.y += y;
},
render: function(ctx) {
if (!this.isLoaded()) return;
ctx.drawImage(this.img, this.position.x * TILE_SIZE, this.position.y * TILE_SIZE, TILE_SIZE, TILE_SIZE);
}
};
function SpriteList()
{
this.list = {};
}
SpriteList.prototype = {
isLoaded: function() {
for (var i in this.list) {
if (!this.list[i].isLoaded()) {
return false;
}
}
return true;
},
_onLoadFunc: null,
onLoad: function(callback) {
this._onLoadFunc = callback;
this.onImageLoaded();
},
onImageLoaded: function() {
if (this.isLoaded() && typeof this._onLoadFunc === "function") {
this._onLoadFunc();
}
},
add: function(name, sprite) {
this.list[name] = sprite;
sprite.onLoad(this.onImageLoaded.bind(this));
},
get: function(name) {
return this.list[name];
}
};
var sprites = new SpriteList();
sprites.add("player", new Sprite("http://www.fillmurray.com/200/200"));
sprites.add("enemy", new Sprite("http://www.fillmurray.com/100/100"));
sprites.add("pickup", new Sprite("http://www.fillmurray.com/60/60"));
sprites.get("pickup").moveBy(1,2);
sprites.get("enemy").moveBy(2,0);
sprites.onLoad(function() {
document.getElementById("loading").innerHTML = "Loaded!";
var c = document.getElementById("ctx");
var ctx = c.getContext("2d");
sprites.get("player").render(ctx);
sprites.get("enemy").render(ctx);
sprites.get("pickup").render(ctx);
});
<div id="loading">Loading...</div>
<canvas id="ctx" width="200" height="200">
Anyways, that's why your code isn't firing, probably.