diff --git a/index.html b/index.html index c91bf59..06a1d0e 100644 --- a/index.html +++ b/index.html @@ -188,6 +188,7 @@ + diff --git a/js/Draw/MLPView.js b/js/Draw/MLPView.js index 99c3d00..ebeca6d 100644 --- a/js/Draw/MLPView.js +++ b/js/Draw/MLPView.js @@ -9,16 +9,13 @@ class MlpView extends Draggable { this.propsShown = true; this.selected = false; this.initialized = false; - this.buttons = []; - this.initializeButtons(); + this.controlButtons = []; + this.initButton; + this.createToggleMlpButton(); this.initializeLayers(initialLayer); this.updateBorders(); } - getToggleMlpButton() { - return this.buttons[0]; - } - isInitialized() { return this.initialized; } @@ -27,12 +24,34 @@ class MlpView extends Draggable { const btn = new TextButton("Initialize MLP", () => { this.toggleMlp(); }); - btn.setDimensions(100, 25).disable(); - return btn; + btn.setDimensions(100, 35).disable(); + this.initButton = btn; + } + + createGoOnceButton() { + const btn = new TextButton("Go Once", () => { + console.log("go once"); + }); + btn.setDimensions(75, 35).setTheme("green"); + this.controlButtons.push(btn); + } + createTogglePlayButton() { + const btn = new TextButton("Play", () => { + console.log("play"); + }); + btn.setDimensions(75, 35).setTheme("cyan"); + this.controlButtons.push(btn); + } + + destroyControlButtons() { + this.controlButtons.forEach((b) => b.destroy()); // probably no needed + this.controlButtons = []; } - initializeButtons() { - this.buttons.push(this.createToggleMlpButton()); + createControlButtons() { + this.createGoOnceButton(); + this.createTogglePlayButton(); + this.updateButtonsCoordinates(); } initializeLayers(initialLayer) { @@ -42,21 +61,15 @@ class MlpView extends Draggable { } toggleMlp() { - if (this.initialized) { - this.destroyMlp(); - this.updateToggleMlpButton("Initialize MLP", "blue"); - } else { - this.createMlp(); - this.updateToggleMlpButton("Terminate MLP", "red"); - } + this.isInitialized() ? this.destroyMlp() : this.initializeMlp(); this.toggleLockLayers(); } updateToggleMlpButton(text, theme) { - this.getToggleMlpButton().setText(text).setTheme(theme); + this.initButton.setText(text).setTheme(theme); } - createMlp() { + initializeMlp() { const layers = this.layers; const mlp = new MLP([], this.lr, this.batchSize); for (let i = 1; i < layers.length; i++) { @@ -70,6 +83,8 @@ class MlpView extends Draggable { } this.setOrigin(mlp); this.initialized = true; + this.updateToggleMlpButton("Terminate MLP", "red"); + this.createControlButtons(); } clearOrigin() { @@ -81,6 +96,8 @@ class MlpView extends Draggable { this.origin.destroy(); this.clearOrigin(); this.initialized = false; + this.updateToggleMlpButton("Initialize MLP", "blue"); + this.destroyControlButtons(); } select() { @@ -102,11 +119,14 @@ class MlpView extends Draggable { } updateButtonsCoordinates() { - const buttons = this.buttons; - buttons[0].setCoordinates( - this.x + this.w - this.buttons[0].w, - this.y + this.h + 5, - ); + const initBtn = this.initButton; + const y = this.y + this.h + 5; + initBtn.setCoordinates(this.x + this.w - initBtn.w, y); + + this.controlButtons.forEach((b, i) => { + const x = this.x + (this.w - b.w) / 2 + (i % 2 ? 1 : -1) * 40; + b.setCoordinates(x, y); + }); } setLabel(label) { @@ -174,8 +194,8 @@ class MlpView extends Draggable { } checkMlpCompleted() { - const initializeButton = this.buttons[0]; - this.isCompleted() ? initializeButton.enable() : initializeButton.disable(); + const initBtn = this.initButton; + this.isCompleted() ? initBtn.enable() : initBtn.disable(); } setOrigin(obj) { @@ -192,15 +212,6 @@ class MlpView extends Draggable { this.destroy(); } - destroy() { - this.getLayers().forEach((l) => l.destroy()); - this.setLayers([]); - if (editMLPOrganizer.getSelected() == this) { - editMLPOrganizer.disable(); - } - mainOrganizer.removeMlpView(this); - } - pushLayer(layer) { layer.parent = this; this.getLayers().push(layer); @@ -237,7 +248,8 @@ class MlpView extends Draggable { this.getLayers().forEach((layer) => layer.pressed()); if (iManager.isBusy()) return; - this.buttons.forEach((btn) => btn.handlePressed()); + this.controlButtons.forEach((btn) => btn.handlePressed()); + this.initButton.handlePressed(); this.pressed(); } @@ -281,6 +293,15 @@ class MlpView extends Draggable { // So we need to call handleRelease to free iManager } + destroy() { + this.getLayers().forEach((l) => l.destroy()); + this.setLayers([]); + if (editMLPOrganizer.getSelected() == this) { + editMLPOrganizer.disable(); + } + mainOrganizer.removeMlpView(this); + } + showProps() { const commands = [ { @@ -314,6 +335,7 @@ class MlpView extends Draggable { this.show(); this.isPropsShown() && this.showProps(); this.getLayers().forEach((layer) => layer.draw()); - this.buttons.forEach((btn) => btn.draw()); + this.controlButtons.forEach((btn) => btn.draw()); + this.initButton.draw(); } } diff --git a/js/Draw/Minors/Buttons/CanvasButton.js b/js/Draw/Minors/Buttons/CanvasButton.js index 0f36b18..cb11bd1 100644 --- a/js/Draw/Minors/Buttons/CanvasButton.js +++ b/js/Draw/Minors/Buttons/CanvasButton.js @@ -32,7 +32,6 @@ class CanvasButton { destroy() { this.onClick = null; - this.img = null; } setCoordinates(x, y) { diff --git a/js/Draw/Minors/Buttons/TextButton.js b/js/Draw/Minors/Buttons/TextButton.js index ded2830..ca41509 100644 --- a/js/Draw/Minors/Buttons/TextButton.js +++ b/js/Draw/Minors/Buttons/TextButton.js @@ -13,17 +13,11 @@ class TextButton extends CanvasButton { getTheme() { const alpha = this.isDisabled() ? 125 : 255; - let defaultColor; - let activeColor; - if (this.theme == "blue") { - defaultColor = [0, 86, 179, alpha]; - activeColor = [0, 123, 255, alpha]; - } else if (this.theme == "red") { - defaultColor = [244, 63, 94, alpha]; - activeColor = [199, 44, 72, alpha]; - } - - return { defaultColor, activeColor }; + const { defaultColor, activeColor } = themeManager.getTheme(this.theme); + return { + defaultColor: [...defaultColor, alpha], + activeColor: [...activeColor, alpha], + }; } setText(text) { diff --git a/js/Draw/Minors/Line.js b/js/Draw/Minors/Line.js index 800af2c..44cf00d 100644 --- a/js/Draw/Minors/Line.js +++ b/js/Draw/Minors/Line.js @@ -19,7 +19,7 @@ class Line { } getWeight() { - return this.w?.data.toFixed(4).toString() ?? ""; + return this.w?.getFixedData(4) ?? ""; } destroy() { @@ -48,7 +48,7 @@ class Line { let offsetX = (lineLength - totalTextWidth) / 2; let x = x1 + cos(angle) * offsetX; - let y = y1 + sin(angle) * offsetX; + let y = y1 - 2 + sin(angle) * offsetX; for (let i = 0; i < text.length; i++) { const commands = [ @@ -57,6 +57,7 @@ class Line { args: [x, y], }, { func: "rotate", args: [angle] }, + { func: "textSize", args: [8] }, { func: "text", args: [text[i], 0, 0] }, ]; executeDrawingCommands(commands); diff --git a/js/Draw/NeuronView.js b/js/Draw/NeuronView.js index e21e642..a12f508 100644 --- a/js/Draw/NeuronView.js +++ b/js/Draw/NeuronView.js @@ -5,6 +5,7 @@ class NeuronView { this.hidden = false; this.lines = []; this.origin = null; + this.r = 25; } clearOrigin() { @@ -51,19 +52,26 @@ class NeuronView { this.instance = null; } - show() { + showOriginProps() { + const origin = this.origin; + const o = origin.output; + const b = origin.b; const commands = [ - { func: "fill", args: [255] }, - { func: "circle", args: [this.x, this.y, 25, 25] }, - { func: "fill", args: [0] }, - { - func: "text", - args: [this.output?.data.toFixed(2), this.x + 30, this.y], - }, + { func: "textAlign", args: [CENTER, CENTER] }, + { func: "textSize", args: [8] }, { func: "text", - args: [this.output?.grad.toFixed(2), this.x + 30, this.y + 25], + args: [b.getFixedData(2), this.x, this.y - this.r / 2 - 5], }, + { func: "text", args: [o.getFixedData(2), this.x, this.y] }, + ]; + executeDrawingCommands(commands); + } + + show() { + const commands = [ + { func: "fill", args: [255] }, + { func: "circle", args: [this.x, this.y, this.r] }, ]; executeDrawingCommands(commands); } @@ -72,6 +80,7 @@ class NeuronView { if (!this.hidden) { this.lines.forEach((line) => line.draw()); this.show(); + this.origin && this.showOriginProps(); } } } diff --git a/js/MLP/Neuron.js b/js/MLP/Neuron.js index 28e0b66..663e4cf 100644 --- a/js/MLP/Neuron.js +++ b/js/MLP/Neuron.js @@ -5,6 +5,7 @@ class Neuron { () => new Value(Math.random() * 2 - 1), ); this.b = new Value(Math.random() * 2 - 1); + this.output = new Value(0); this.act_func = ActivationFunction.TANH; } diff --git a/js/MLP/Value.js b/js/MLP/Value.js index 72b93c0..f65edb9 100644 --- a/js/MLP/Value.js +++ b/js/MLP/Value.js @@ -8,6 +8,14 @@ class Value { this.backward = () => {}; } + getFixedData(fixedNum) { + return this.data.toFixed(fixedNum); + } + + getFixedGrad() { + return this.grad.toFixed(4); + } + convert(val) { return val instanceof Value ? val : new Value(val); } diff --git a/js/Managers/themeManager.js b/js/Managers/themeManager.js new file mode 100644 index 0000000..77a9ff5 --- /dev/null +++ b/js/Managers/themeManager.js @@ -0,0 +1,53 @@ +class ThemeManager { + constructor() { + let CYAN; + let BLUE; + let ROSE; + let YELLOW; + let GREEN; + let WHITE; + let GRAY; + let SKY; + } + + getTheme(themeColor) { + let defaultColor; + let activeColor; + + switch (themeColor) { + case "blue": + defaultColor = [0, 86, 179]; + activeColor = [0, 123, 255]; + break; + case "red": + defaultColor = [244, 63, 94]; + activeColor = [199, 44, 72]; + break; + case "cyan": + defaultColor = [5, 156, 176]; + activeColor = [6, 182, 212]; + break; + case "yellow": + defaultColor = [200, 155, 19]; + activeColor = [250, 204, 21]; + break; + case "green": + defaultColor = [60, 141, 96]; + activeColor = [74, 222, 128]; + break; + case "white": + defaultColor = [255, 255, 255]; + activeColor = [230, 230, 230]; + break; + case "gray": + defaultColor = [107, 114, 128]; + activeColor = [74, 74, 74]; + break; + default: + defaultColor = [74, 144, 226]; + activeColor = [28, 100, 242]; + break; + } + return { defaultColor, activeColor }; + } +} diff --git a/js/canvas.js b/js/canvas.js index c615526..5f72dc1 100644 --- a/js/canvas.js +++ b/js/canvas.js @@ -4,6 +4,7 @@ let editMLPOrganizer; let iManager; let tableOrganizer; let datasetOrganizer; +let themeManager; let canvasManager; @@ -11,6 +12,7 @@ let mainSketch = function (p) { p.setup = function () { p.createCanvas(p.windowWidth, p.windowHeight).parent(document.body); + themeManager = new ThemeManager(); canvasManager = new CanvasManager(p); mainOrganizer = new MainOrganizer(); editMLPOrganizer = new EditMLPOrganizer();