From 3df851911b6f7f94abec8e9361bff06c341d006f Mon Sep 17 00:00:00 2001 From: saliherdemk Date: Sun, 14 Jul 2024 17:33:27 +0300 Subject: [PATCH] organizers added & edit popup implemented --- canvas.js | 72 ++++++++++++++++++++++++---------------- draggable.js | 33 ++++++++++--------- editOrganizer.js | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ index.html | 9 ++++- js/MLP.js | 33 +++++++++++-------- organizer.js | 13 ++++++++ style.css | 15 +++++++++ 7 files changed, 202 insertions(+), 58 deletions(-) create mode 100644 editOrganizer.js create mode 100644 organizer.js create mode 100644 style.css diff --git a/canvas.js b/canvas.js index 6900a66..c18dde3 100644 --- a/canvas.js +++ b/canvas.js @@ -1,41 +1,25 @@ var mlps = []; +var ogarganizer; +var editOrganizer; function setup() { createCanvas(1920, 1080); + organizer = new Organizer(); + editOrganizer = new EditOrganizer(createGraphics(1920, 1080)); mlps.push(new MLP(4, [3, 5, 1], 600, 100)); - // mlps.push(new MLP(3, [3, 2, 1], 500, 300)); - - // xs = [ - // [2.0, 3.0, -1.0], - // [3.0, -1.0, 0.5], - // [0.5, 1.0, 1.0], - // [1.0, 1.0, -1.0], - // ]; - // ys = [1.0, -1.0, -1.0, 1.0]; - // - // let k = 0; - // let intervalId = setInterval(() => { - // ypred = xs.map((x) => mlp.call(x)); - // let loss = new Value(0); - // for (let i = 0; i < ys.length; i++) { - // loss = loss.add(ypred[i].sub(new Value(ys[i])).pow(new Value(2))); - // } - // - // m.parameters().forEach((p) => (p.grad = 0.0)); - // loss.backprop(); - // m.parameters().forEach((p) => (p.data += -0.1 * p.grad)); - // - // k++; - // if (k >= 20) { - // clearInterval(intervalId); - // } - // }, 500); + mlps.push(new MLP(4, [3, 5, 1], 600, 100)); +} + +function setupPopup() { + organizer.disable(); } function draw() { - background(220); + background(255); mlps.forEach((m) => m.draw()); + + editOrganizer.draw(); } function mousePressed() { @@ -45,3 +29,35 @@ function mousePressed() { function mouseReleased() { mlps.forEach((mlp) => mlp.handleReleased()); } + +function doubleClicked() { + mlps.forEach((mlp) => mlp.handleDoubleClicked()); +} + +// mlps.push(new MLP(3, [3, 2, 1], 500, 300)); +// +// xs = [ +// [2.0, 3.0, -1.0], +// [3.0, -1.0, 0.5], +// [0.5, 1.0, 1.0], +// [1.0, 1.0, -1.0], +// ]; +// ys = [1.0, -1.0, -1.0, 1.0]; +// +// let k = 0; +// let intervalId = setInterval(() => { +// ypred = xs.map((x) => mlp.call(x)); +// let loss = new Value(0); +// for (let i = 0; i < ys.length; i++) { +// loss = loss.add(ypred[i].sub(new Value(ys[i])).pow(new Value(2))); +// } +// +// m.parameters().forEach((p) => (p.grad = 0.0)); +// loss.backprop(); +// m.parameters().forEach((p) => (p.data += -0.1 * p.grad)); +// +// k++; +// if (k >= 20) { +// clearInterval(intervalId); +// } +// }, 500); diff --git a/draggable.js b/draggable.js index e4d37ea..498fe7c 100644 --- a/draggable.js +++ b/draggable.js @@ -6,36 +6,39 @@ class Draggable { } over() { - if ( + this.rollover = mouseX > this.x && mouseX < this.x + this.w && mouseY > this.y && - mouseY < this.y + this.h - ) { - this.rollover = true; - } else { - this.rollover = false; - } + mouseY < this.y + this.h; } pressed(force = false) { - if (this.rollover || force) { + if (!(this.rollover || force)) return; + + if (!organizer.getDragActive() || force) { this.dragging = true; this.offsetX = this.x - mouseX; this.offsetY = this.y - mouseY; - isBusy = this.type == "layer" && !force; - if (this.type == "mlp") { - for (let i = 0; i < this.layers.length; i++) { - this.layers[i].pressed(true); - } + if (this.type === "mlp") { + this.layers.forEach((layer) => layer.pressed(true)); } + + organizer.setDragActive(true); + } + } + + doubleClicked() { + if (this.rollover && !editOrganizer.getSelected()) { + editOrganizer.enable(); + editOrganizer.setSelected(this); } } released() { this.dragging = false; - isBusy = false; + organizer.setDragActive(false); } updateCoordinates() { @@ -45,6 +48,6 @@ class Draggable { this.type == "layer" && this.updateNeuronsCoordinates(); } - this.type == "mlp" && this.updateLayersCoordinates(); + this.type == "mlp" && this.updateBorders(); // This is works because when you drag layer you also drag mlp } } diff --git a/editOrganizer.js b/editOrganizer.js new file mode 100644 index 0000000..4c49b5b --- /dev/null +++ b/editOrganizer.js @@ -0,0 +1,85 @@ +class EditOrganizer { + constructor(canvas) { + this.canvas = canvas; + this.enabled = false; + this.selected = null; + this.selectedCopy = null; + this.originX = (width - 500) / 2; + this.originY = 150; + this.w = 500; + this.h = 500; + this.setup(); + } + + setup() { + let button = createButton("Click me"); + button.position(50, 50); + button.mousePressed(() => { + this.disable(); + }); + } + + showSelected() { + this.selectedCopy.draw(); + } + + draw() { + this.canvas.fill(255); + this.canvas.rect(this.originX, this.originY, this.w, this.h); + this.selected && this.showSelected(); + this.enabled && image(this.canvas, 0, 0); + } + + getSelected() { + return this.selected; + } + + setSelected(layer) { + let x = this.originX + (this.w - layer.w) / 2; + let y = this.originY; + + this.selected = layer; + this.selectedCopy = new Layer( + layer.nin, + layer.nout, + x, + y - 100, + layer.label, + layer.act_func, + ); + + let cnv = this.canvas; + this.selectedCopy.show = () => { + let cpy = this.selectedCopy; + cnv.fill(255); + cnv.rect(cpy.x, cpy.y, 50, cpy.h); + cnv.fill(0); + cnv.text(cpy.label, cpy.x, cpy.y - 10); + cnv.fill(255); + }; + + this.selectedCopy.neurons.forEach((neuron) => { + neuron.show = () => { + cnv.fill(255); + cnv.circle(neuron.x, neuron.y, 25, 25); + cnv.fill(0); + cnv.text(neuron.output?.data.toFixed(2), neuron.x + 30, neuron.y); + cnv.text(neuron.output?.grad.toFixed(2), neuron.x + 30, neuron.y + 25); + fill(255); + }; + }); + } + + disable() { + this.enabled = false; + this.selected = null; + this.selectedCopy = null; + this.canvas.clear(); + } + + enable() { + this.canvas.background(0, 50); + + this.enabled = true; + } +} diff --git a/index.html b/index.html index 4f4e173..a3db0df 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,20 @@ - + + + +
+ +
+ + diff --git a/js/MLP.js b/js/MLP.js index 2700556..f54b304 100644 --- a/js/MLP.js +++ b/js/MLP.js @@ -1,5 +1,3 @@ -var isBusy = false; - class Neuron { constructor(nin, x, y) { this.w = []; @@ -71,6 +69,7 @@ class Layer extends Draggable { super("layer"); this.act_func = act_func; this.nextLayer = null; + this.prevLayer = null; this.label = label; this.x = x; this.w = 50; @@ -118,15 +117,13 @@ class Layer extends Draggable { }); } - upCoordinates(x, y) { - this.x = x; - this.y = y; + setPrevLayer(layer) { + this.prevLayer = layer; } show() { fill(255, 255, 255, 0); rect(this.x, this.y, 50, this.h); - fill(255); fill(0); text(this.label, this.x, this.y - 10); fill(255); @@ -135,8 +132,9 @@ class Layer extends Draggable { draw() { this.show(); this.neurons.forEach((neuron) => neuron.draw()); - this.over(); - this.updateCoordinates(); + + !organizer.getDragActive() && this.over(); + (organizer.getDragActive() || this.dragging) && this.updateCoordinates(); } } @@ -161,16 +159,18 @@ class MLP extends Draggable { ); if (this.layers.length > 0) { - this.layers[this.layers.length - 1].setNextLayer(layer); + let prevLayer = this.layers[this.layers.length - 1]; + prevLayer.setNextLayer(layer); + layer.setPrevLayer(prevLayer); } this.layers.push(layer); } - this.updateLayersCoordinates(); + this.updateBorders(); } - updateLayersCoordinates() { + updateBorders() { let lastX = 0; let firstX = width; let firstY = height; @@ -207,7 +207,7 @@ class MLP extends Draggable { this.layers.forEach((layer) => { layer.pressed(); }); - if (isBusy) return; + if (organizer.getDragActive()) return; this.pressed(); } @@ -218,10 +218,15 @@ class MLP extends Draggable { this.released(); } + handleDoubleClicked() { + this.layers.forEach((layer) => layer.doubleClicked()); + } + draw() { this.layers.forEach((layer) => layer.draw()); this.show(); - !isBusy && this.over(); - (isBusy || this.dragging) && this.updateCoordinates(); + + !organizer.getDragActive() && this.over(); + (organizer.getDragActive() || this.dragging) && this.updateCoordinates(); } } diff --git a/organizer.js b/organizer.js new file mode 100644 index 0000000..ecb0c90 --- /dev/null +++ b/organizer.js @@ -0,0 +1,13 @@ +class Organizer { + constructor() { + this.dragActive = false; + } + + setDragActive(dragActive) { + this.dragActive = dragActive; + } + + getDragActive() { + return this.dragActive; + } +} diff --git a/style.css b/style.css new file mode 100644 index 0000000..6dd492f --- /dev/null +++ b/style.css @@ -0,0 +1,15 @@ +body { + margin: 0; + padding: 0; +} + +#disabled-background { + position: fixed; + width: 100%; + height: 100%; + z-index: 1; + background: rgba(0, 0, 0, 0.2); + display: none; + justify-content: center; + align-items: center; +}