I'm new to both languages, and I'm trying to convert this program I found on github and edited, to p5.js so I can include it in a webpage. I tried following guides and replacing void() with function(), int i with var i etc.. but there seems to be something wrong. The first code is the original .pde and the second one is my attempt at converting it. Many thanks!
final int STAGE_WIDTH = 1200;
final int STAGE_HEIGHT = 950;
final int NB_PARTICLES = 60000;
final float MAX_PARTICLE_SPEED = 5;
final int MIN_LIFE_TIME = 20;
final int MAX_LIFE_TIME = 80;
final String IMAGE_PATH = "starrynight.jpg";
myVector tabParticles[];
float particleSize = 1.2;
PImage myImage;
int imageW;
int imageH;
color myPixels[];
FlowField ff;
GUI gui;
void setup()
{
size(1200, 950, P3D);
background(0);
initializeImage();
initializeParticles();
ff = new FlowField(5);
gui = new GUI(this);
gui.setup();
}
void initializeImage()
{
myImage = loadImage(IMAGE_PATH);
imageW = myImage.width;
imageH = myImage.height;
myPixels = new color[imageW * imageH];
myImage.loadPixels();
myPixels = myImage.pixels;
image(myImage, 0, 0);
}
void setParticle(int i) {
tabParticles[i] = new myVector((int)random(imageW), (int)random(imageH));
tabParticles[i].prevX = tabParticles[i].x;
tabParticles[i].prevY = tabParticles[i].y;
tabParticles[i].count = (int)random(MIN_LIFE_TIME, MAX_LIFE_TIME);
tabParticles[i].myColor = myPixels[(int)(tabParticles[i].y)*imageW + (int)(tabParticles[i].x)];
}
void initializeParticles()
{
tabParticles = new myVector[NB_PARTICLES];
for (int i = 0; i < NB_PARTICLES; i++)
{
setParticle(i);
}
}
void draw()
{
ff.setRadius(gui.getR());
ff.setForce(gui.getF());
particleSize = gui.getS();
float vx;
float vy;
PVector v;
for (int i = 0; i < NB_PARTICLES; i++)
{
tabParticles[i].prevX = tabParticles[i].x;
tabParticles[i].prevY = tabParticles[i].y;
v = ff.lookup(tabParticles[i].x, tabParticles[i].y);
vx = v.x;
vy = v.y;
vx = constrain(vx, -MAX_PARTICLE_SPEED, MAX_PARTICLE_SPEED);
vy = constrain(vy, -MAX_PARTICLE_SPEED, MAX_PARTICLE_SPEED);
tabParticles[i].x += vx;
tabParticles[i].y += vy;
tabParticles[i].count--;
if ((tabParticles[i].x < 0) || (tabParticles[i].x > imageW-1) ||
(tabParticles[i].y < 0) || (tabParticles[i].y > imageH-1) ||
tabParticles[i].count < 0) {
setParticle(i);
}
strokeWeight(1.5*particleSize);
stroke(tabParticles[i].myColor, 250);
line(tabParticles[i].prevX, tabParticles[i].prevY, tabParticles[i].x, tabParticles[i].y);
}
ff.updateField();
}
void mouseDragged() {
if(mouseX>950 && mouseY>830) return;
ff.onMouseDrag();
}
void keyPressed() {
//if (key =='s' || key == 'S') {
// ff.saveField();
//}
}
class myVector extends PVector
{
myVector (float p_x, float p_y) {
super(p_x, p_y);
}
float prevX;
float prevY;
int count;
color myColor;
}
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
class FlowField {
PVector[][] field;
PVector[][] tempField;
int cols, rows;
int resolution;
int affectRadius;
float force;
File file = new File(dataPath("field.txt"));
FlowField(int r) {
resolution = r;
cols = 1200 / resolution;
rows = 950 / resolution;
field = new PVector[cols][rows];
tempField = new PVector[cols][rows];
init();
affectRadius = 3;
force = 1;
}
void setRadius(int r) {
affectRadius = r;
}
void setForce(float f) {
force = f;
}
void init() {
try {
for (int i=0; i<cols; i++) {
for (int j=0; j<rows; j++) {
tempField[i][j] = new PVector(0, 0);
}
}
readField();
}
catch(Exception e) {
for (int i=0; i<cols; i++) {
for (int j=0; j<rows; j++) {
field[i][j] = new PVector(0, 0);
}
}
}
}
PVector lookup(float x, float y) {
int column = int(constrain(x/resolution, 0, cols-1));
int row = int(constrain(y/resolution, 0, rows-1));
return PVector.add(field[column][row],tempField[column][row]);
}
void drawBrush() {
pushStyle();
noFill();
stroke(255, 255, 255);
ellipse(mouseX, mouseY, affectRadius*10, affectRadius*10);
popStyle();
}
void drawField(float x, float y, PVector v) {
int column = int(constrain(x/resolution, 0, cols-1));
int row = int(constrain(y/resolution, 0, rows-1));
for (int i=-affectRadius; i<=affectRadius; i++) {
for (int j=-affectRadius; j<=affectRadius; j++) {
if (i*i+j*j<affectRadius*affectRadius) {
try {
tempField[column+i][row+j].add(v).mult(0.9);
}
catch(Exception e) {
}
}
}
}
}
void updateField(){
for (int i=0; i<cols; i++) {
for (int j=0; j<rows; j++) {
tempField[i][j].mult(0.992);
}
}
}
void onMouseDrag() {
PVector direc = new PVector(mouseX-pmouseX, mouseY-pmouseY).normalize();
drawField(pmouseX, pmouseY, direc.mult(force));
}
void saveField() {
try {
FileWriter out = new FileWriter(file);
for (int i=0; i<cols; i++) {
for (int j=0; j<rows; j++) {
out.write(field[i][j].x+","+field[i][j].y+"\t");
}
out.write("\r\n");
}
out.close();
}
catch(Exception e) {
}
}
void readField() throws IOException {
try {
BufferedReader in = new BufferedReader(new FileReader(file));
String line;
for (int i = 0; (line = in.readLine()) != null; i++) {
String[] temp = line.split("\t");
for (int j=0; j<temp.length; j++) {
String[] xy = temp[j].split(",");
float x = Float.parseFloat(xy[0]);
float y = Float.parseFloat(xy[1]);
field[i][j] = new PVector(x, y);
}
}
in.close();
}
catch(Exception e) {
throw new IOException("no field.txt");
}
}
}
import controlP5.*;
class GUI {
ControlP5 cp5;
Slider sliderR;
Slider sliderF;
Slider sliderS;
GUI(PApplet thePApplet){
cp5 = new ControlP5(thePApplet);
}
void setup(){
cp5.setColorBackground(0x141414);
sliderR = cp5.addSlider("Radius")
.setPosition(980,890)
.setRange(1,20)
.setValue(12).setSize(150,25);
sliderF = cp5.addSlider("Force")
.setPosition(980,918)
.setRange(0.1,0.5)
.setValue(0.3).setSize(150,25);
sliderS = cp5.addSlider("Particle Size")
.setPosition(980,862)
.setRange(0.8,2)
.setValue(1.5).setSize(150,25);
}
int getR(){
return int(sliderR.getValue());
}
float getF(){
return sliderF.getValue();
}
float getS(){
return sliderS.getValue();
}
}
final var STAGE_WIDTH = 1200;
final var STAGE_HEIGHT = 950;
final var NB_PARTICLES = 60000;
final let MAX_PARTICLE_SPEED = 5;
final var MIN_LIFE_TIME = 20;
final var MAX_LIFE_TIME = 80;
final let IMAGE_PATH = "starrynight.jpg";
myVector tabParticles[];
let particleSize = 1.2;
PImage myImage;
var imageW;
var imageH;
color myPixels[];
FlowField ff;
GUI gui;
function setup()
{
var canvas = createCanvas(1200, 950, P3D);
canvas.parent('canvasForHTML');
background(0);
initializeImage();
initializeParticles();
ff = new FlowField(5);
gui = new GUI(this);
gui.setup();
}
function preload() { img = loadImage('data/starrynight.jpg');
}
function initializeImage()
{ imageW = myImage.width;
imageH = myImage.height;
myPixels = new color[imageW * imageH];
myImage.loadPixels();
myPixels = myImage.pixels;
image(myImage, 0, 0);
}
function setParticle(var i) {
tabParticles[i] = new myVector((var)random(imageW), (var)random(imageH));
tabParticles[i].prevX = tabParticles[i].x;
tabParticles[i].prevY = tabParticles[i].y;
tabParticles[i].count = (var)random(MIN_LIFE_TIME, MAX_LIFE_TIME);
tabParticles[i].myColor = myPixels[(var)(tabParticles[i].y)*imageW + (var)(tabParticles[i].x)];
}
function initializeParticles()
{
tabParticles = new myVector[NB_PARTICLES];
for (var i = 0; i < NB_PARTICLES; i++)
{
setParticle(i);
}
}
function draw()
{
ff.setRadius(gui.getR());
ff.setForce(gui.getF());
particleSize = gui.getS();
let vx;
let vy;
PVector v;
for (var i = 0; i < NB_PARTICLES; i++)
{
tabParticles[i].prevX = tabParticles[i].x;
tabParticles[i].prevY = tabParticles[i].y;
v = ff.lookup(tabParticles[i].x, tabParticles[i].y);
vx = v.x;
vy = v.y;
vx = constrain(vx, -MAX_PARTICLE_SPEED, MAX_PARTICLE_SPEED);
vy = constrain(vy, -MAX_PARTICLE_SPEED, MAX_PARTICLE_SPEED);
tabParticles[i].x += vx;
tabParticles[i].y += vy;
tabParticles[i].count--;
if ((tabParticles[i].x < 0) || (tabParticles[i].x > imageW-1) ||
(tabParticles[i].y < 0) || (tabParticles[i].y > imageH-1) ||
tabParticles[i].count < 0) {
setParticle(i);
}
strokeWeight(1.5*particleSize);
stroke(tabParticles[i].myColor, 250);
line(tabParticles[i].prevX, tabParticles[i].prevY, tabParticles[i].x, tabParticles[i].y);
}
ff.updateField();
}
function mouseDragged() {
if(mouseX>950 && mouseY>830) return;
ff.onMouseDrag();
}
function keyPressed() {
//if (key =='s' || key == 'S') {
// ff.saveField();
//}
}
class myVector extends PVector
{
myVector (let p_x, let p_y) {
super(p_x, p_y);
}
let prevX;
let prevY;
var count;
color myColor;
}
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
class FlowField {
PVector[][] field;
PVector[][] tempField;
var cols, rows;
var resolution;
var affectRadius;
let force;
File file = new File(dataPath("field.txt"));
FlowField(var r) {
resolution = r;
cols = 1200 / resolution;
rows = 950 / resolution;
field = new PVector[cols][rows];
tempField = new PVector[cols][rows];
init();
affectRadius = 3;
force = 1;
}
function setRadius(var r) {
affectRadius = r;
}
function setForce(let f) {
force = f;
}
function init() {
try {
for (var i=0; i<cols; i++) {
for (var j=0; j<rows; j++) {
tempField[i][j] = new PVector(0, 0);
}
}
readField();
}
catch(Exception e) {
for (var i=0; i<cols; i++) {
for (var j=0; j<rows; j++) {
field[i][j] = new PVector(0, 0);
}
}
}
}
PVector lookup(let x, let y) {
var column = var(constrain(x/resolution, 0, cols-1));
var row = var(constrain(y/resolution, 0, rows-1));
return PVector.add(field[column][row],tempField[column][row]);
}
function drawBrush() {
pushStyle();
noFill();
stroke(255, 255, 255);
ellipse(mouseX, mouseY, affectRadius*10, affectRadius*10);
popStyle();
}
function drawField(let x, let y, PVector v) {
var column = var(constrain(x/resolution, 0, cols-1));
var row = var(constrain(y/resolution, 0, rows-1));
for (var i=-affectRadius; i<=affectRadius; i++) {
for (var j=-affectRadius; j<=affectRadius; j++) {
if (i*i+j*j<affectRadius*affectRadius) {
try {
tempField[column+i][row+j].add(v).mult(0.9);
}
catch(Exception e) {
}
}
}
}
}
function updateField(){
for (var i=0; i<cols; i++) {
for (var j=0; j<rows; j++) {
tempField[i][j].mult(0.992);
}
}
}
function onMouseDrag() {
PVector direc = new PVector(mouseX-pmouseX, mouseY-pmouseY).normalize();
drawField(pmouseX, pmouseY, direc.mult(force));
}
function saveField() {
try {
FileWriter out = new FileWriter(file);
for (var i=0; i<cols; i++) {
for (var j=0; j<rows; j++) {
out.write(field[i][j].x+","+field[i][j].y+"\t");
}
out.write("\r\n");
}
out.close();
}
catch(Exception e) {
}
}
function readField() throws IOException {
try {
BufferedReader in = new BufferedReader(new FileReader(file));
let line;
for (var i = 0; (line = in.readLine()) != null; i++) {
let[] temp = line.split("\t");
for (var j=0; j<temp.length; j++) {
let[] xy = temp[j].split(",");
let x = let.parselet(xy[0]);
let y = let.parselet(xy[1]);
field[i][j] = new PVector(x, y);
}
}
in.close();
}
catch(Exception e) {
throw new IOException("no field.txt");
}
}
}
import controlP5.*;
class GUI {
ControlP5 cp5;
Slider sliderR;
Slider sliderF;
Slider sliderS;
GUI(PApplet thePApplet){
cp5 = new ControlP5(thePApplet);
}
function setup(){
cp5.setColorBackground(0x141414);
sliderR = cp5.addSlider("Radius")
.setPosition(980,890)
.setRange(1,20)
.setValue(12).setSize(150,25);
sliderF = cp5.addSlider("Force")
.setPosition(980,918)
.setRange(0.1,0.5)
.setValue(0.3).setSize(150,25);
sliderS = cp5.addSlider("Particle Size")
.setPosition(980,862)
.setRange(0.8,2)
.setValue(1.5).setSize(150,25);
}
var getR(){
return var(sliderR.getValue());
}
let getF(){
return sliderF.getValue();
}
let getS(){
return sliderS.getValue();
}
}
I felt like doing nothing this evening, so here you go:
Also, I recommend getting the "Better Comments" extension/plugin for your code editor of choice, since I used those here a lot
const STAGE_WIDTH = 1200;
const STAGE_HEIGHT = 950;
const NB_PARTICLES = 60000;
const MAX_PARTICLE_SPEED = 5;
const MIN_LIFE_TIME = 20;
const MAX_LIFE_TIME = 80;
const IMAGE_PATH = "starrynight.jpg";
let tabParticles = [];
let particleSize = 1.2;
let myImage;
let imageW;
let imageH;
let myPixels = [];
let ff;
let gui;
function setup()
{
size(1200, 950, WEBGL);
background(0);
initializeImage();
initializeParticles();
ff = new FlowField(5);
// ! CHANGE THIS AND ALL OF THE OTHER GUI STUFF THAT COME AFTER IT!
// gui = new GUI(this);
// gui.setup();
}
function initializeImage()
{
myImage = loadImage(IMAGE_PATH);
imageW = myImage.width;
imageH = myImage.height;
myPixels = new color[imageW * imageH]; // ? dunno
// myImage.loadPixels();
// myPixels = myImage.pixels;
image(myImage, 0, 0);
}
function setParticle(i) {
tabParticles[i] = new myVector(random(imageW), random(imageH));
tabParticles[i].prevX = tabParticles[i].x;
tabParticles[i].prevY = tabParticles[i].y;
tabParticles[i].count = random(MIN_LIFE_TIME, MAX_LIFE_TIME);
tabParticles[i].myColor = myPixels[(int)(tabParticles[i].y)*imageW + (int)(tabParticles[i].x)];
}
function initializeParticles()
{
// tabParticles = new myVector[NB_PARTICLES];
for(let i = 0; i < NB_PARTICLES; i ++)
tabParticles.push(new myVector())
for (let i = 0; i < NB_PARTICLES; i++)
{
setParticle(i);
}
}
function draw()
{
ff.setRadius(gui.getR());
ff.setForce(gui.getF());
particleSize = gui.getS();
let vx;
let vy;
let v;
for (let i = 0; i < NB_PARTICLES; i++)
{
tabParticles[i].prevX = tabParticles[i].x;
tabParticles[i].prevY = tabParticles[i].y;
v = ff.lookup(tabParticles[i].x, tabParticles[i].y);
vx = v.x;
vy = v.y;
vx = constrain(vx, -MAX_PARTICLE_SPEED, MAX_PARTICLE_SPEED);
vy = constrain(vy, -MAX_PARTICLE_SPEED, MAX_PARTICLE_SPEED);
tabParticles[i].x += vx;
tabParticles[i].y += vy;
tabParticles[i].count--;
if ((tabParticles[i].x < 0) || (tabParticles[i].x > imageW-1) ||
(tabParticles[i].y < 0) || (tabParticles[i].y > imageH-1) ||
tabParticles[i].count < 0) {
setParticle(i);
}
strokeWeight(1.5*particleSize);
stroke(tabParticles[i].myColor, 250);
line(tabParticles[i].prevX, tabParticles[i].prevY, tabParticles[i].x, tabParticles[i].y);
}
ff.updateField();
}
function mouseDragged() {
if(mouseX>950 && mouseY>830) return;
ff.onMouseDrag();
}
function keyPressed() {
//if (key =='s' || key == 'S') {
// ff.saveField();
//}
}
class myVector extends PVector
{
constructor (p_x, p_y) {
super(p_x, p_y);
this.prevX
this.prevY
this.count
this.myColor
}
}
class FlowField {
constructor(r) {
this.field; // PVector[][]
this.tempField; // PVector[][]
this.cols; this.rows; // int
this.resolution; // int
this.affectRadius; // int
this.force; // float
this.file = new File(dataPath("field.txt")); // File
// ! You'll need to have a preload loadStrings(filepath) or fetch(filename).then(blob => blob.text()).then(text => ... )
// ! can't be bothered to do this right now
throw "Ult1: change this!!! also delete this line (just incase: ~129th line)"
resolution = r;
cols = 1200 / resolution;
rows = 950 / resolution;
field = new PVector[cols][rows];
tempField = new PVector[cols][rows];
init();
affectRadius = 3;
force = 1;
}
setRadius(r) {
affectRadius = r;
}
setForce(f) {
force = f;
}
// FROM HERE ON I CONVERTED IT WHILE HALF ASLEEP (11pm)
init() {
try {
for (let i=0; i<cols; i++) {
for (let j=0; j<rows; j++) {
tempField[i][j] = createVector()
}
}
readField();
}
catch(e) {
for (let i=0; i<cols; i++) {
for (let j=0; j<rows; j++) {
field[i][j] = createVector()
}
}
}
}
lookup(x, y) {
let column = int(constrain(x/resolution, 0, cols-1));
let row = int(constrain(y/resolution, 0, rows-1));
return p5.Vector.add(field[column][row],tempField[column][row]);
}
drawBrush() {
pushStyle();
noFill();
stroke(255, 255, 255);
ellipse(mouseX, mouseY, affectRadius*10, affectRadius*10);
popStyle();
}
drawField(x, y, v) {
let column = int(constrain(x/resolution, 0, cols-1));
let row = int(constrain(y/resolution, 0, rows-1));
for (let i=-affectRadius; i<=affectRadius; i++) {
for (let j=-affectRadius; j<=affectRadius; j++) {
if (i*i+j*j<affectRadius*affectRadius) {
try {
tempField[column+i][row+j].add(v).mult(0.9);
}
catch(e) {
}
}
}
}
}
updateField(){
for (let i=0; i<cols; i++) {
for (let j=0; j<rows; j++) {
tempField[i][j].mult(0.992);
}
}
}
onMouseDrag() {
let direc = createVector(mouseX-pmouseX, mouseY-pmouseY).normalize()
drawField(pmouseX, pmouseY, direc.mult(force));
}
saveField() {
try {
// FileWriter out = new FileWriter(file);
throw "FileWriter doesn't exist in Javascript, line: 215"
// ! this doesn't exist! in javascript, I don't remember how to do this, but you can just search it on Google
for (let i=0; i<cols; i++) {
for (let j=0; j<rows; j++) {
out.write(field[i][j].x+","+field[i][j].y+"\t");
}
out.write("\r\n");
}
out.close();
}
catch(e) {
}
}
readField() {
throw "once again, BufferedReader is just not a thing, but it's for file reading, so you can just loadString(\"file\") or fetch... line: 230"
try {
let _in = new BufferedReader(new FileReader(file));
let line;
for (let i = 0; (line = _in.readLine()) != null; i++) {
let temp = line.split("\t");
for (let j=0; j<temp.length; j++) {
let xy = temp[j].split(",");
let x = Float.parseFloat(xy[0]);
let y = Float.parseFloat(xy[1]);
field[i][j] = createVector(x, y);
}
}
_in.close();
}
catch(e) {
throw new IOException("no field.txt");
}
}
}
// I think you should just do this with html to be honest
// copy this into your body (below the <script src="sketch.js"></script>, or above it):
/*
<div id="GUI">
<label for="Radius">Radius</label>
<input type="range" name="Radius" id="slider-radius" min="1" max="20" step="0.5" value="10">
<br>
<label for="Force">Force</label>
<input type="range" name="Force" id="slider-force" min="0.1" max="0.5" step="0.01" value="0.3">
<br>
<label for="Particle-size">Particle size</label>
<input type="range" name="Particle-size" id="slider-particle-size" min="0.8" max="2" step="0.05" value="1.5">
</div>
<style>
html, body {
background-color: #141414;
}
label {
color: #ccc;
}
</style>
*/
// color: #ccc is the same as text-color = #CCCCCC in some other language, maybe...
class GUI {
constructor(){
throw "don't declare the GUI class, instead do GUI.getR(), GUI.getF() ... line: 288, you should be able to see line # in your console"
}
static getR(){
return document.getElementById("slider-radius").value;
}
static getF(){
return document.getElementById("slider-force").value
}
static getS(){
return document.getElementById("slider-particle-size").value
}
}
// static = same between ALL the classes(instances) of a class thing, so:
// class A { static x = 7; this.y = 5 }
// * console.log(A.x) -> 7
// ! console.log(A.y) -> probably null, since you need to do console.log(new A().y)
// here I have it to keep the GUI class, but there is no need to keep this stuff
You will still need to do A LOT of work to get this going, like translating classes, for example class myVector extends PVector {...}. In p5.js there is no PVector, but I'm pretty sure there's p5.Vector though, so class myVector extends p5.Vector might work. File reading and writing will also be messed up, since JavaScript doesn't have the whole new File() and all the stuff like that.
You will also need to deal with CORS, good luck! I also changed most of the GUI stuff to .html stuff and a static class, bit hard words there, but can't really change them to anything.
Anyways, I probably made mistakes here!
It's 11:40 pm here by now, so yeah! this is very more or less transferrrr
Related
I would like to use x to output a list of all connected webcams with ID. Unfortunately, I always get the following error message (see picture).
Does anyone have an idea what this could be? I am thankful for any help!
Here is my code:
const devices = [];
var list;
let video;
function setup() {
list = navigator.mediaDevices.enumerateDevices().then(gotDevices);
var constraints1 = {
video: {
deviceId: {
exact: list[0].id
},
}
};
canvas = createCanvas(width,height);
background(255);
video = createCapture(constraints1);
}
function gotDevices(deviceInfos) {
for (let i = 0; i !== deviceInfos.length; ++i) {
const deviceInfo = deviceInfos[i];
if (deviceInfo.kind == 'videoinput') {
devices.push({
label: deviceInfo.label,
id: deviceInfo.deviceId
});
}
}
return devices;
}
-------------------------------EDIT (Current status)-----------------------
var deviceList = [];
function preload() {
navigator.mediaDevices.enumerateDevices().then(getDevices);
}
function setup() {
var constraints = {
video: {
deviceId: {
exact: deviceList[1].id
},
}
};
canvas = createCanvas(width, height);
background(255);
video = createCapture(constraints);
//console.log(deviceList);
}
function getDevices(devices) {
//arrayCopy(devices, deviceList);
for (let i = 0; i < devices.length; ++i) {
let deviceInfo = devices[i];
//Only get videodevices and push them into deviceList
if (deviceInfo.kind == 'videoinput') {
deviceList.push({
label: deviceInfo.label,
id: deviceInfo.deviceId
});
// console.log("Device name :", devices[i].label);
// console.log("DeviceID :", devices[i].deviceId);
}
}
}
Unfortunately, I get the following error here:
The information that you are looking for is down in the 'getDevices' function. The following runs on my system and will show the device name and id in the console window. It will also create a global array for audio and video devices that you may access in setup(); Note that the deviceList is obtained in preload() which is run only once before the rest of your code.
var deviceList = [];
function preload() {
navigator.mediaDevices.enumerateDevices().then(getDevices);
}
function setup() {
var constraints = {
video:
{
}
};
canvas = createCanvas(width, height);
background(255);
video = createCapture(constraints);
//console.log(deviceList);
for (let x = 0; x < deviceList.length; x++) {
console.log(deviceList[x]);
}
}
function getDevices(devices) {
// console.log(devices); // To see all devices
arrayCopy(devices, deviceList);
for (let i = 0; i < devices.length; ++i) {
let deviceInfo = devices[i];
if (deviceInfo.kind == 'videoinput') {
console.log("Device name :", devices[i].label);
console.log("DeviceID :", devices[i].deviceId);
}
}
}
DropDownList of Devices
// N.B. Will not run in Processing IDE with Safari - Requires p5.js web editor and Chrome browser
// Loads deviceList array into pullDown list
// Drop Down List parts => a.)display field, b.)arrow, c.)listItems
// Syntax: List(x, y, w, h, itemH, txtSize)
let list;
let selectedItem = -1;
let drop = false;
let itemY = [];
var deviceList = [];
function preload() {
navigator.mediaDevices.enumerateDevices().then(getDevices);
}
class List {
constructor(x, y, w, h, itemH, txtSize) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.itemH = itemH;
this.txtSize = txtSize;
this.arrwX = this.x + this.w;
this.arrwY = this.y;
this.arrwH = this.h;
}
openVideoInput (videoSelected) {
var constraints = {
video: {
deviceId: {
exact: deviceList[videoSelected].id
},
}
};
createCapture(constraints);
}
press(mx, my) {
// arrow touches
if ((mx >= this.arrwX) && (mx <= this.arrwX+this.arrwH) && (my >= this.arrwY) && (my <= this.arrwY+this.arrwH)) {
if (drop == true) {
drop = false;
} else {
drop = true;
}
} // list touches
if (drop) {
if (deviceList.length > 0) {
for (let j = 0; j < deviceList.length; j++) {
if ((mx >= this.x) && (mx <= this.x + this.w) && (my >= itemY[j] ) && (my <= itemY[j] + this.itemH)) {
selectedItem = j;
console.log("selectedItem :", selectedItem);
list.openVideoInput(selectedItem);
drop = false;
}
}
}
}
}
displayFieldString(title) {
fill(255); // background color
rect(this.x, this.y, this.w, this.h);
fill(0); // text color
textSize(this.txtSize);
text(title, this.x + 10, this.y + this.txtSize);
}
display() {
if (selectedItem == -1) {
this.displayFieldString("Select video input:");
} else {
this.displayFieldString(deviceList[selectedItem].label);
}
// arrow
fill(255); // arrow background color
rect(this.arrwX, this.arrwY, this.arrwH, this.arrwH);
fill(0, 255, 0); // arrow color
triangle(this.arrwX+5, this.arrwY+5, this.arrwX+this.arrwH-5, this.arrwY+5, this.arrwX+this.arrwH/2, this.arrwY+this.arrwH-5);
// listItems
if ((deviceList.length > 0) && (drop)) {
for (let j = 0; j < deviceList.length; j++) {
itemY[j] = (this.y + this.h) + j*this.itemH;
fill(255);
rect(this.x, itemY[j], this.w, this.itemH);
fill(0);
textSize(this.txtSize);
text(deviceList[j].label, this.x + 10, itemY[j] + this.txtSize);
}
}
if (!drop) {
rect(this.x, this.y + this.h, this.w, 0);
}
}
}
function setup() {
createCanvas(400, 200);
list = new List(30, 30, 320, 24, 24, 16);
}
function draw() {
background(220);
list.display();
}
function getDevices(devices) {
for (let i = 0; i < devices.length; ++i) {
let deviceInfo = devices[i];
//Only get videodevices and push them into deviceList
if (deviceInfo.kind == 'videoinput') {
deviceList.push( {
label:deviceInfo.label,
id:deviceInfo.deviceId
}
);
}
}
}
function mousePressed() {
list.press(mouseX, mouseY);
}
Ok, now that I've looked further on the Internet for a solution and also found one, I thought I'd post it here too. Maybe it still helps someone else. The problem was that the array deviceList[] is not completely filled when the code reaches the following line:
deviceList[1].id within setup()
The easiest way to solve this problem was to postpone the automatically initialization of the p5js library until your callback getDevices() has finished its job. Thanks again to apodidae for his help.
var deviceList = [];
navigator.mediaDevices.enumerateDevices().then(getDevices);
const setup = function () {
var constraints = {
video: {
deviceId: {
exact: deviceList[0].id
},
}
};
var constraints1 = {
video: {
deviceId: {
exact: deviceList[1].id
},
}
};
canvas = createCanvas(width, height);
background(255);
video = createCapture(constraints);
video2 = createCapture(constraints1);
console.log(deviceList);
};
const draw = function () {
};
function startP5() {
globalThis.setup = setup; // place callback setup() into global context
globalThis.draw = draw;
new p5;
}
function getDevices(devices) {
//arrayCopy(devices, deviceList);
for (let i = 0; i < devices.length; ++i) {
let deviceInfo = devices[i];
//Only get videodevices and push them into deviceList
if (deviceInfo.kind == 'videoinput') {
deviceList.push({
label: deviceInfo.label,
id: deviceInfo.deviceId
});
// console.log("Device name :", devices[i].label);
// console.log("DeviceID :", devices[i].deviceId);
}
}
startP5();
}
Here again the link to the other discussion
Recently i have used code in project that is still working it it http://kreditka.testovi-site.pw. But when i tried to reuse javascript the default rules js got broken and i got an error in console explaining that varibale cannot be found. Is it somehow related that sme o the code is not resusable and each line should be rewritten one by one for defferent projects?
(function () {
console.log("()");
"use strict";
var polosa = document.querySelector("#polosa");
alert(polosa);
var x = document.querySelectorAll("#slide1");
var o;
for (o = 0; o < x.length; o++) {
x[o].style.left = o*300+"px";
}
var x = document.querySelectorAll("#slide");
var a;
for (a = 0; a < x.length; a++) {
x[a].style.left = a*300+"px";
}
var doc = document.querySelectorAll('.btn-click');
for (var i = 0; i < doc.length; i++) {
doc[i].addEventListener("click", highlightThis, true);
}
var doc1 = document.querySelectorAll('.btn1-click');
for (var j = 0; j < doc.length; j++) {
doc1[j].addEventListener("click", highlightThis1, true);
}
var polosa = document.querySelector('#polosa');
polosa.style.left = '0px';
var left = 0;
var sliderItem = document.querySelector('#slide');
function highlightThis(event) {
console.log("highlightThis");
var currentActiveButton = document.querySelector(".btn-click.btn-slide-active");
currentActiveButton.classList.toggle("btn-slide-active");
event.target.classList.toggle("btn-slide-active");
console.log(event.target.dataset.value);
shiftSlide({
prev: currentActiveButton.dataset.value,
next: event.target.dataset.value
});
}
function highlightThis1(event) {
console.log("highlightThis1");
var currentActiveButton1 = document.querySelector(".btn1-click.btn-slide-active");
currentActiveButton1.classList.toggle("btn-slide-active");
event.target.classList.toggle("btn-slide-active");
console.log(event.target.dataset.value);
shiftSlide1({
prev: currentActiveButton1.dataset.value,
next: event.target.dataset.value
});
}
function shiftSlide(obj) {
var polosa = document.querySelector('#polosa');
console.log("shiftSlide");
var currentSliderPosition = parseInt(polosa.style.left);
if (obj.prev === obj.next) {
return
} else {
var left = (obj.prev - obj.next) * 300;
polosa.style.left = currentSliderPosition + left + "px";
}
}
function shiftSlide1(obj) {
var polosa = document.querySelector('#polosa1');
console.log("shiftSlide");
var currentSliderPosition = parseInt(polosa1.style.left);
if (obj.prev === obj.next) {
return
} else {
var left = (obj.prev - obj.next) * 300;
polosa1.style.left = currentSliderPosition + left + "px";
}
}
})();
i've got my code to work on a Storage Management
Page Replacement Methods assignment. And my code works through Netbeans but it doesn't compile through CMD.
Do you know why?
public class Paging {
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws IOException, IOException, IOException, IOException {
BufferedReader obj = new BufferedReader(new InputStreamReader(System.in));
try {
URL url = new URL("www.csc.liv.ac.uk/~ped/COMP104/COMP104-2016-17/Page_Trace_Oldest");
URL url3 = new URL("www.csc.liv.ac.uk/~ped/COMP104/COMP104-2016-17/Page_Trace_Random");
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
String str;
while ((str = in.readLine()) != null) {
str = in.readLine().toString();
System.out.println(str);
}
in.close();
} catch (MalformedURLException e) {
} catch (IOException e) {
}
URL url2 = new URL("www.csc.liv.ac.uk/~ped/COMP104/COMP104-2016-17/Page_Trace_LRU");
BufferedReader in = new BufferedReader(new InputStreamReader(url2.openStream()));
String str2;
while ((str2 = in.readLine()) != null) {
str2 = in.readLine().toString();
System.out.println(str2);
}
in.close();
URL url3 = new URL("www.csc.liv.ac.uk/~ped/COMP104/COMP104-2016-17/Page_Trace_Random");
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
String str3;
while ((str3 = in.readLine()) != null) {
str2 = in.readLine().toString();
System.out.println(str3);
}
in.close();
int f, page = 0, ch, pgf = 0, n = 0, chn = 0;
boolean flag;
int pages[];
do {
System.out.println("Menu");
System.out.println("1.FIFO");
System.out.println("2.LRU");
System.out.println("3.LFU");
System.out.println("4.EXIT");
System.out.println("ENTER YOUR CHOICE: ");
try {
ch = Integer.parseInt(obj.readLine());
} catch (IOException ex) {
Logger.getLogger(Paging.class.getName()).log(Level.SEVERE, null, ex);
}
switch (ch) {
case 1:
int pt = 0;
System.out.println("Enter no. of frames (1-8): ");
f = Integer.parseInt(obj.readLine());
int frame[] = new int[f];
for (int i = 0; i < f; i++) {
frame[i] = -1;
}
System.out.println("enter the no of pages ");
n = Integer.parseInt(obj.readLine());
pages = new int[n];
System.out.println("enter the page no ");
for (int j = 0; j < n; j++) {
pages[j] = Integer.parseInt(obj.readLine());
}
do {
int pg = 0;
for (pg = 0; pg < n; pg++) {
page = pages[pg];
flag = true;
for (int j = 0; j < f; j++) {
if (page == frame[j]) {
flag = false;
break;
}
}
if (flag) {
frame[pt] = page;
pt++;
if (pt == f) {
pt = 0;
}
System.out.print("frame :");
for (int j = 0; j < f; j++) {
System.out.print(frame[j] + " ");
}
System.out.println();
pgf++;
} else {
System.out.print("frame :");
for (int j = 0; j < f; j++) {
System.out.print(frame[j] + " ");
}
System.out.println();
}
chn++;
}
} while (chn != n);
System.out.println("Page fault:" + pgf);
break;
case 2:
int k = 0;
System.out.println("enter no. of frames: ");
{
try {
f = Integer.parseInt(obj.readLine());
} catch (IOException ex) {
Logger.getLogger(Paging.class.getName()).log(Level.SEVERE, null, ex);
}
}
int frame1[] = new int[f];
int a[] = new int[f];
int b[] = new int[f];
for (int i = 0; i < f; i++) {
frame1[i] = -1;
a[i] = -1;
b[i] = -1;
}
System.out.println("enter the no of pages ");
{
try {
n = Integer.parseInt(obj.readLine());
} catch (IOException ex) {
Logger.getLogger(Paging.class.getName()).log(Level.SEVERE, null, ex);
}
}
pages = new int[n];
System.out.println("enter the page no ");
for (int j = 0; j < n; j++) {
try {
pages[j] = Integer.parseInt(obj.readLine());
} catch (IOException ex) {
Logger.getLogger(Paging.class.getName()).log(Level.SEVERE, null, ex);
}
}
do {
int pg = 0;
for (pg = 0; pg < n; pg++) {
page = pages[pg];
flag = true;
for (int j = 0; j < f; j++) {
if (page == frame1[j]) {
flag = false;
break;
}
}
for (int j = 0; j < f && flag; j++) {
if (frame1[j] == a[f - 1]) {
k = j;
break;
}
}
if (flag) {
frame1[k] = page;
System.out.println("frame :");
for (int j = 0; j < f; j++) {
System.out.print(frame1[j] + " ");
}
pgf++;
System.out.println();
} else {
System.out.println("frame :");
for (int j = 0; j < f; j++) {
System.out.print(frame1[j] + " ");
}
System.out.println();
}
int p = 1;
b[0] = page;
for (int j = 0; j < a.length; j++) {
if (page != a[j] && p < f) {
b[p] = a[j];
p++;
}
}
for (int j = 0; j < f; j++) {
a[j] = b[j];
}
chn++;
}
} while (chn != n);
System.out.println("Page fault:" + pgf);
break;
case 3:
k = 0;
pgf = 0;
int sml;
System.out.println("enter no. of frames: ");
{
try {
f = Integer.parseInt(obj.readLine());
} catch (IOException ex) {
Logger.getLogger(Paging.class.getName()).log(Level.SEVERE, null, ex);
}
}
int frame2[] = new int[f];
int cnt[] = new int[f];
flag = true;
for (int i = 0; i < f; i++) {
frame2[i] = -1;
cnt[i] = 0;
}
System.out.println("enter the no of pages ");
{
try {
n = Integer.parseInt(obj.readLine());
} catch (IOException ex) {
Logger.getLogger(Paging.class.getName()).log(Level.SEVERE, null, ex);
}
}
pages = new int[n];
System.out.println("enter the page no ");
for (int j = 0; j < n; j++) {
try {
pages[j] = Integer.parseInt(obj.readLine());
} catch (IOException ex) {
Logger.getLogger(Paging.class.getName()).log(Level.SEVERE, null, ex);
}
}
do {
int pg = 0;
for (pg = 0; pg < n; pg++) {
page = pages[pg];
flag = true;
for (int j = 0; j < f; j++) {
if (page == frame2[j]) {
flag = false;
cnt[j]++;
break;
}
}
if (flag) {
sml = cnt[0];
for (int j = 0; j < f; j++) {
if (cnt[j] < sml) {
sml = cnt[j];
break;
}
}
for (int j = 0; j < f; j++) {
if (sml == cnt[j]) {
frame2[j] = page;
k = j;
break;
}
}
cnt[k] = 1;
System.out.print("frame :");
for (int j = 0; j < f; j++) {
System.out.print(frame2[j] + " ");
System.out.println();
pgf++;
}
} else {
System.out.print("frame :");
for (int j = 0; j < f; j++) {
System.out.print(frame2[j] + " ");
}
System.out.println();
}
chn++;
}
} while (chn != n);
System.out.println("Page fault:" + pgf);
break;
case 4:
break;
}
} while (ch != 4);
}
}
The error i'm getting is that :
You're creating 2 BufferedReader objects with the same name.
URL url2 = new URL("www.csc.liv.ac.uk/~ped/COMP104/COMP104-2016-17/Page_Trace_LRU");
BufferedReader in = new BufferedReader(new InputStreamReader(url2.openStream()));
and
URL url3 = new URL("www.csc.liv.ac.uk/~ped/COMP104/COMP104-2016-17/Page_Trace_Random");
BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
I'm attempting to make a genetic algorithm simulation in JavaScript using the P5.JS library, but i'm experiencing some issues. Here's what i have so far:
//var popS = 2;
var popu;
//var balls = [];
var target;
function setup() {
createCanvas(800, 300);
popu = new Population();
target = createVector(width - 15, height / 2);
}
function draw() {
background(50);
popu.run();
var ballsC = 0;
for (var a = 0; a < popu.balls.length; a++) {
if (popu.balls[a].done == true){
ballsC ++;
}
}
if (ballsC >= popu.popS) {
//popu = new Population();
popu.evaluate();
//popu.selection();
}
fill(255, 0, 30);
noStroke();
ellipse(target.x, target.y, 20, 20);
}
function DNA() {
this.genes = [];
this.changes = 7;//random(2, 50);
for (var a = 0; a < this.changes; a++) {
this.genes[a] = random(0, 15);
}
this.crossover = function (pB) {
var newdna = new DNA();
var mid = floor(random(0, this.genes.length));
for (var a = 0; a < this.genes.length; a++) {
if (a < mid) {
newdna.genes[a] = this.genes[a];
}else {
newdna.genes[a] = pB.genes[a];
}
}
return newdna;
}
}
function Population() {
this.balls = [];
this.popS = 50;
this.maxfit = 0;
this.matingpool = [];
for (var a = 0; a < this.popS; a++) {
this.balls[a] = new Ball();
}
this.evaluate = function() {
for (var a = 0; a < this.balls.length; a++) {
this.balls[a].calcF();
if (this.balls[a].fitness > this.maxfit) {
this.maxfit = this.balls[a].fitness;
}
}
this.matingpool = [];
for (var b = 0; b < this.balls.length; b++) {
var n = this.balls[b].fitness * 100;
for (var c = 0; c < n; c++) {
this.matingpool.push(this.balls[c]);
}
}
this.selection();
}
this.selection = function () {
var newBalls = [];
for (var a = 0; a < this.balls.length; a++) {
var parentA = this.matingpool[floor(random(0, this.matingpool.length))];
var parentB = this.matingpool[floor(random(0, this.matingpool.length))];
var child = parentA.dna.crossover(parentB.dna);
newBalls[a] = new Ball(child);
}
this.balls = newBalls;
}
this.run = function() {
for (var a = 0; a < this.balls.length; a++) {
this.balls[a].update();
this.balls[a].checkCol();
this.balls[a].show();
}
}
}
function Ball(dna) {
this.pos = createVector(10, height / 2);
this.speed = createVector(2, 2.5);
this.mul = -1;
this.time = 0;
this.a = 0;
if (dna) {
this.dna = dna;
} else {
this.dna = new DNA();
}
this.done = false;
this.fitness = 0;
this.reached;
this.update = function() {
if (this.done == false) {
if (this.time >= this.dna.genes[this.a]) {
this.a++;
this.time = 0;
this.mul *= -1;
}
this.speed.set(2, 2.5 * this.mul);
this.pos.add(this.speed);
}
}
this.show = function() {
this.time += 0.1;
fill(255, 70);
noStroke();
ellipse(this.pos.x, this.pos.y, 10, 10);
}
this.checkCol = function() {
if (this.pos.y > height || this.pos.y < 0 || this.pos.x > width) {
//print("col");
this.done = true;
}
if (dist(this.pos.x, this.pos.y, target.x, target.y) <= (10 / 2) + (20 / 2)) {
//print("done!");
this.done = true;
this.reached = true;
}
}
this.calcF = function() {
var a = dist(this.pos.x, this.pos.y, target.x, target.y);
var b = this.dna.genes.length;
var c = 0;
if (this.reached){
c = 1;
}
this.fitness = map(map(a, 0, width, 1, 0) + map(b, 2, 50, 1, 0) + c, 0, 3, 0, 1);
}
}
This is the most essential part of the code:
var popu;
function setup() {
createCanvas(800, 300);
popu = new Population();
}
function draw() {
background(50);
//popu = new Population();
popu.evaluate();
//popu.selection();
}
function DNA() {
this.genes = [];
this.changes = 7; //random(2, 50);
for (var a = 0; a < this.changes; a++) {
this.genes[a] = random(0, 15);
}
this.crossover = function(pB) {
var newdna = new DNA();
var mid = floor(random(0, this.genes.length));
for (var a = 0; a < this.genes.length; a++) {
if (a < mid) {
newdna.genes[a] = this.genes[a];
} else {
newdna.genes[a] = pB.genes[a];
}
}
return newdna;
}
}
function Population() {
this.balls = [];
this.popS = 50;
this.maxfit = 0;
this.matingpool = [];
for (var a = 0; a < this.popS; a++) {
this.balls[a] = new Ball();
}
this.evaluate = function() {
this.matingpool = [];
for (var b = 0; b < this.balls.length; b++) {
var n = this.balls[b].fitness * 100;
for (var c = 0; c < n; c++) {
this.matingpool.push(this.balls[c]);
}
}
this.selection();
}
this.selection = function() {
var newBalls = [];
for (var a = 0; a < this.balls.length; a++) {
var parentA = this.matingpool[floor(random(0, this.matingpool.length))];
var parentB = this.matingpool[floor(random(0, this.matingpool.length))];
var child = parentA.dna.crossover(parentB.dna);
newBalls[a] = new Ball(child);
}
this.balls = newBalls;
}
}
function Ball(dna) {
this.pos = createVector(10, height / 2);
this.speed = createVector(2, 2.5);
this.mul = -1;
this.time = 0;
this.a = 0;
if (dna) {
this.dna = dna;
} else {
this.dna = new DNA();
}
this.done = false;
this.fitness = 0;
this.reached;
}
So whenever it gets to here:
this.selection = function () {
var newBalls = [];
for (var a = 0; a < this.balls.length; a++) {
var parentA = random(this.matingpool);
var parentB = random(this.matingpool);
var child = parentA.dna.crossover(parentB.dna);
newBalls[a] = new Ball(child);
}
this.balls = newBalls;
}
i get the error: "Cannot read property 'dna' of undefined", why on earth is this happening?? When i use the debugger in chrome i can clearly see that matingpool has 2000 elements but when i try to get a random one it returns "undefined".
var parentA = random(this.matingpool);
var parentB = random(this.matingpool);
The weird thing is that parentB works, but parentA dosn't.
Any help is much appreciated. Entire code running here: http://codepen.io/felipe_mare/pen/bgOYMN
If it helps, i sometimes get the error: "Cannot read property '0' of undefined" instead, at line 138
this.update = function() {
if (this.done == false) {
//line 138
if (this.time >= this.dna.genes[this.a]) {
//line 138
this.a++;
this.time = 0;
this.mul *= -1;
}
this.speed.set(2, 2.5 * this.mul);
this.pos.add(this.speed);
}
}
In the future, please please please try to narrow your questions down to an MCVE. I understand that this was a complicated problem to debug, but you'll have much better luck if you try to narrow your problem down to a minimal (under 20 lines) example. Most of the time, you'll end up finding your error in the process of creating the MCVE.
But your problem is actually when you create the matingpool array, here:
this.matingpool = [];
for (var b = 0; b < this.balls.length; b++) {
var n = this.balls[b].fitness * 100;
for (var c = 0; c < n; c++) {
this.matingpool.push(this.balls[c]);
}
}
If I add a print statement inside the inner for loop, like this:
this.matingpool = [];
for (var b = 0; b < this.balls.length; b++) {
var n = this.balls[b].fitness * 100;
for (var c = 0; c < n; c++) {
console.log("pushing: " + this.balls[c]);
this.matingpool.push(this.balls[c]);
}
}
Then I see that you're pushing undefined into the array a bunch of times:
(1178) pushing: [object Object]
(3) pushing: undefined
(482) pushing: [object Object]
(3) pushing: undefined
(216) pushing: [object Object]
Then you choose randomly from this array, which is why your error shows up in random places in your code.
You're going to have to debug this further to figure out why that's happening- it seems weird that you're looping based on a fitness instead of an array length, but I don't really understand the code well enough to be sure. In any case, hopefully this gets you on the right track.
I have coded a neural network in JavaScript and implemented the Backpropagation algorithm described here.
Here is the code (typescript):
/**
* Net
*/
export class Net {
private layers: Layer[] = [];
private inputLayer: Layer;
private outputLayer: Layer;
public error: number = Infinity;
private eta: number = 0.15;
private alpha: number = 0.5;
constructor(...topology: number[]) {
topology.forEach((topologyLayer, iTL) => {
var nextLayerNeuronNumber = topology[iTL + 1] || 0;
this.layers.push(new Layer(topologyLayer, nextLayerNeuronNumber));
});
this.inputLayer = this.layers[0];
this.outputLayer = this.layers[this.layers.length - 1];
}
public loadWeights(weights) {
/*
[
[Layer
[Node weights, ..., ...]
]
]
*/
for (var iL = 0; iL < weights.length; iL++) {
var neuronWeights = weights[iL];
var layer = this.layers[iL];
for (var iN = 0; iN < neuronWeights.length; iN++) {
// Neuron
var connections = neuronWeights[iN];
for (var iC = 0; iC < connections.length; iC++) {
var connection = connections[iC];
this.layer(iL).neuron(iN).setWeights(iC, connection);
}
}
}
}
public train(data: number[][], iterartions = 2000) {
var inputs = this.inputLayer.neurons.length - 1;
for (var ite = 0; ite < iterartions; ite++) {
data.forEach(node => {
var inputData = [];
var outputData = [];
for (var i = 0; i < node.length; i++) {
if (i < inputs) {
inputData.push(node[i])
} else {
outputData.push(node[i])
}
}
this.feedForward(...inputData);
this.backProb(...outputData);
});
}
return this.calcDataError(data);
}
private calcDataError(data){
var overallDataErrorSum = 0;
var inputs = this.inputLayer.neurons.length - 1;
data.forEach(node => {
var outputData = node.splice(inputs);
var inputData = node;
this.feedForward(...inputData);
overallDataErrorSum += this.getNetError(outputData);
});
overallDataErrorSum /= data.length;
return overallDataErrorSum;
}
public saveWeights() {
// Ignore output layer
var ret = []
for (var iL = 0; iL < this.layers.length - 1; iL++) {
var layer = this.layers[iL];
var layer_ret = [];
layer.neurons.forEach(neuron => {
layer_ret.push(neuron.connections.map(c => c.weight));
});
ret.push(layer_ret);
}
return ret;
}
feedForward(...inputs: number[]) {
if (inputs.length != this.inputLayer.neurons.length - 1) return false;
this.inputLayer.neurons.forEach((neuron, i) => {
if (!neuron.isBias) {
neuron.output(inputs[i]);
}
});
this.layers.forEach((layer, i) => {
// Skip Input Layer
if (i > 0) {
var prevLayer = this.layers[i - 1]
layer.neurons.forEach(neuron => {
neuron.calcOutput(prevLayer);
});
}
});
}
public getNetError(targetVals) {
// Calc delta error of outputs
var deltas = [];
this.outputLayer.neurons.forEach((neuron, iN) => {
if (!neuron.isBias) {
neuron.calcOutputDelta(targetVals[iN]);
deltas.push(neuron.delta);
}
});
deltas = deltas.map(d => Math.pow(d, 2));
var sum = 0;
deltas.forEach(d => sum += d);
return sum / deltas.length;
}
backProb(...targetVals: number[]) {
// Calc delta error of outputs
this.outputLayer.neurons.forEach((neuron, iN) => {
if (!neuron.isBias) {
neuron.calcOutputDelta(targetVals[iN]);
}
});
// Backprop delta error through hidden layers
for (var iL = this.layers.length - 2; iL > 0; iL--) {
var layer = this.layers[iL];
var nextLayer = this.layers[iL + 1]
layer.neurons.forEach(neuron => {
neuron.calcHiddenDelta(nextLayer);
});
}
// Update weights
for (var iL = 1; iL < this.layers.length; iL++) {
var layer = this.layers[iL];
var prevLayer = this.layers[iL - 1];
layer.neurons.forEach(neuron => {
if (!neuron.isBias) {
neuron.updateWeights(prevLayer, this.eta);
}
});
}
this.error = this.getNetError(targetVals);
return this.error;
}
getOutputs(...inputs: number[]) {
var ret = [];
this.outputLayer.neurons.forEach(neuron => {
if (!neuron.isBias) {
ret.push(neuron.output())
}
});
return ret;
}
getResults(...inputs: number[]) {
this.feedForward(...inputs)
return this.getOutputs();
}
layer(i) {
return this.layers[i];
}
}
/**
* Layer
*/
class Layer {
public neurons: Neuron[] = [];
constructor(neuronNumber: number, nextLayerNeuronNumber: number) {
for (var iN = 0; iN < neuronNumber + 1; iN++) {
// +1 for bias neuron, which is last
if (iN < neuronNumber) {
// Create normal neuron
this.neurons.push(new Neuron(nextLayerNeuronNumber, iN, false));
} else {
this.neurons.push(new Neuron(nextLayerNeuronNumber, iN, true));
}
}
}
neuron(i) {
return this.neurons[i];
}
bias() {
return this.neurons[this.neurons.length - 1];
}
}
/**
* Neuron
*/
class Neuron {
public connections: Connection[] = [];
private outputVal: number;
public delta: number;
constructor(outputsTo: number, private index, public isBias = false) {
// Creates connections
for (var c = 0; c < outputsTo; c++) {
this.connections.push(new Connection());
}
this.outputVal = isBias ? 1 : 0;
}
calcOutput(prevLayer: Layer) {
// Only calcOutput when neuron is not a bias neuron
if (!this.isBias) {
var sum = 0;
prevLayer.neurons.forEach(prevLayerNeuron => {
sum += prevLayerNeuron.output() * prevLayerNeuron.getWeights(this.index).weight;
});
this.output(this.activationFunction(sum));
}
}
private activationFunction(x) {
//return Math.tanh(x);
return 1 / (1 + Math.exp(-x))
//return x;
};
private activationFunctionDerivative(x) {
// Small approximation of tanh derivative
//return 1 - x * x
// Sigmoid
var s = this.activationFunction(x);
return s * (1 - s);
// With general derivative formula where h = 1e-10
/*var h = 0.0001;
var dx = ((this.activationFunction(x + h) - this.activationFunction(x))/h)
return dx;*/
//return 1
};
// Backprop // Todo // Understand
public calcOutputDelta(targetVal) {
// Bias output neurons do not have delta error
if (!this.isBias) {
this.delta = targetVal - this.output();
}
}
public calcHiddenDelta(nextLayer: Layer) {
var sum = 0;
// Go through all neurons of next layer excluding bias
nextLayer.neurons.forEach((neuron, iN) => {
if (!neuron.isBias) {
sum += neuron.delta * this.getWeights(iN).weight;
}
});
this.delta = sum;
}
public updateWeights(prevLayer: Layer, eta: number) {
prevLayer.neurons.forEach((neuron, iN) => {
var weight = neuron.getWeights(this.index).weight;
var newWeight =
weight + // old weight
eta * // learning weight
this.delta * // delta error
this.activationFunctionDerivative(neuron.output())
neuron.getWeights(this.index).weight = newWeight;
});
}
// Backprop end
output(s?) {
if (s && !this.isBias) {
this.outputVal = s;
return this.outputVal;
} else {
return this.outputVal;
}
}
getWeights(i) {
return this.connections[i];
}
setWeights(i, s) {
return this.connections[i].weight = s;
}
}
/**
* Connection
*/
class Connection {
public weight: number;
public deltaWeight: number;
constructor() {
this.weight = Math.random();
this.deltaWeight = 0;
}
}
When training it for just one set of data, it works just fine. (example from here)
import {Net} from './ml';
var myNet = new Net(2, 2, 2);
var weights = [
[
[0.15, 0.25],
[0.20, 0.30],
[0.35, 0.35]
],
[
[0.40, 0.50],
[0.45, 0.55],
[0.60, 0.60]
]
];
// Just loads the weights given in the example
myNet.loadWeights(weights)
var error = myNet.train([[0.05, 0.10, 0.01, 0.99]]);
console.log('Error: ', error);
console.log(myNet.getResults(0.05, 0.10));
Console prints:
Error: 0.0000020735174706210714
[ 0.011556397089327321, 0.9886867357304885 ]
Basically, that's pretty good, right?
Then, I wanted to teach the network the XOR problem:
import {Net} from './ml';
var myNet = new Net(2, 3, 1);
var trainigData = [
[0, 0, 0],
[1, 0, 1],
[0, 1, 1],
[1, 1, 0]
]
var error = myNet.train(trainigData)
console.log('Error: ', error);
console.log('Input: 0, 0: ', myNet.getResults(0, 0));
console.log('Input: 1, 0: ', myNet.getResults(1, 0));
Here the network fails:
Error: 0.2500007370167383
Input: 0, 0: [ 0.5008584967899313 ]
Input: 1, 0: [ 0.5008584967899313 ]
What am I doing wrong?
Firstly perform gradient checks on the entire batch (meaining on the function calculating gradients on the batch), if you have not done so already. This will ensure you know what the problem is.
If gradients are not correctly computed, taking into account that your implementation works on single data sets, you are most likely mixing some values in the backwards pass.
If gradients are correctly computed, there is an error in your update function.
A working implementation of backpropagation for neural networks in javaScript can be found here
Here is the code snippet of the trainStep function using backpropagation
function trainStepBatch(details){
//we compute forward pass
//for each training sample in the batch
//and stored in the batch array
var batch=[];
var ks=[];
for(var a=0;a<details.data.in.length;a++){
var results=[];
var k=1;
results[0]={output:details.data.in[a]};
for(var i=1;i<this.layers.length;i++){
results[i]=layers[this.layers[i].type].evalForGrad(this.layers[i],results[i-1].output);
k++;
}
batch[a]=results;
ks[a]=k;
}
//We compute the backward pass
//first derivative of the cost function given the output
var grad=[];
for(i in batch)grad[i]={grad:costs[details.cost].df(batch[i][ks[i]-1].output,details.data.out[i])};
//for each layer we compute the backwards pass
//on the results of all forward passes at a given layer
for(var i=this.layers.length-1;i>0;i--){
var grads=[];
var test=true;
for(a in batch){
grads[a]=layers[this.layers[i].type].grad(this.layers[i],batch[a][i],batch[a][i-1],grad[a]);
if(grads[a]==null)test=false;
else grads[a].layer=i;
}
//we perform the update
if(test)stepBatch(this.layers[i].par,grads,details.stepSize);
}
}
And for the stepBatch function
function stepBatch(params,grads, stepSize){
for(i in params.w){
for(j in params.w[i]){
for(a in grads){
params.w[i][j]-=stepSize*grads[a].dw[i][j];
}
}
}
for(i in params.b){
for(a in grads){
params[a]-=stepSize*grads[a].db[i];
}
}
function stepBatch(params,grads, stepSize){
for(i in params.w){
for(j in params.w[i]){
for(a in grads){
params.w[i][j]-=stepSize*grads[a].dw[i][j];
}
}
}
for(i in params.b){
for(a in grads){
params[a]-=stepSize*grads[a].db[i];
}
}
}