From a24b803cb9065021f3ff4dfbd780606d8b072bf0 Mon Sep 17 00:00:00 2001 From: schneefux Date: Thu, 31 Mar 2016 16:09:45 +0200 Subject: further abstract weight vis; move dependencies; highlight hovers --- code/weightvis.js | 360 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 360 insertions(+) create mode 100644 code/weightvis.js (limited to 'code/weightvis.js') diff --git a/code/weightvis.js b/code/weightvis.js new file mode 100644 index 0000000..7b4b5ef --- /dev/null +++ b/code/weightvis.js @@ -0,0 +1,360 @@ +// material colors +var + // 50 100 200 300 400 500 + pDeepPurple = ['#ede7f6', '#d1c4e9', '#b39ddb', '#9575cd', '#7e57c2', '#673ab7', + // 600 700 800 900 + '#5e35b1', '#512da8', '#4527a0', '#311b92', + // A100 A200 A400 A700 + '#b388ff', '#7c4dff', '#651fff', '#6200ea'], + // A100 A200 A400 A700 + pGreen = ['#b9f6ca', '#69f0ae', '#00e676', '#00c853'], + pDark = ['#000000', '#212121', '#303030', '#424242'], + pLight = ['#E0E0E0', '#F5F5F5', '#FAFAFA', '#FFFFFF'], + + primaryColor = [pDeepPurple[1], pDeepPurple[5], pDeepPurple[7]], + secondaryColor = pGreen[3], + themeColor = pLight, + + accentColor = secondaryColor, + bgColor = themeColor[3], + + linkColor = primaryColor[0], + linkHighlightColor = primaryColor[1], + nodeColor = primaryColor[1], + nodeHighlightColor = accentColor +; + + +/* network construction */ + +var Network = (function() { + function Network(layout) { + this.layout = layout; + } + + Network.prototype.init = function() { + this._createNodes(); + this._createLinks(); + }; + + Network.prototype.resize = function(env) { + var largestLayerSize = 0; + for (layer=0; layer largestLayerSize) largestLayerSize = this.layout.net[layer].size; + } + this.responsiveRadius = d3.min([env.width, env.height]) / d3.max([this.layout.net.layers, largestLayerSize]); + this._updateNodes(env); + }; + + Network.prototype._createNodes = function() { + // create layers + this.nodes = []; + for (layer=0; layer layout.layers[batch][epoch].max) layout.layers[batch][epoch].max = layerMax; + } + } + } + if (this._finished) this._finished(layout, json); + }; + + return Loader; +})(); + + +/* animation stepping */ + +var Animator = (function() { + function Animator(layout, time, step) { + this.layout = layout; + this.step = step; + this.batch = this.epoch = 0; + this.frameDuration = time / (this.layout.data.batches * this.layout.data.epochs / this.step); + } + + Animator.prototype.start = function(callback) { + var self = this; + wrapper = function() { + callback(self.batch, self.epoch, self.frameDuration); + self.animate(); + }; + this.pid = setInterval(wrapper, this.frameDuration); + }; + + Animator.prototype.animate = function() { + this.epoch += this.step; + if (this.epoch >= this.layout.data.epochs) { + this.epoch = 0; + if (++this.batch>= this.layout.data.batches) { + this.batch = 0; + clearInterval(this.pid); // suicide + console.log("finished"); + } + } + }; + + return Animator; +})(); + + +/* main */ + +var Visualizer = (function() { + var _weightScaler = 10; // size of thickest line + + function Visualizer(svg) { + this.loader = new Loader(); + this.svg = svg; + } + + Visualizer.prototype.getSteps = function() { + return this.layout.data.batches * this.layout.data.epochs; + }; + + Visualizer.prototype.load = function(url, callback) { + var self = this; + function wrapper(layout, data) { + self.layout = layout; + self.data = data; + self._setup(); + callback(); + } + this.loader.load("data/log.json", wrapper); + }; + + Visualizer.prototype.animate = function(animationTime, resolution) { + // animationTime in seconds per frame + // resolution in epochs per frame + var self = this; + this.domLink = this.svg.selectAll(".link"); + + this.animator = new Animator(this.layout, animationTime * 1000, resolution); + this.animator.start(function(batch, epoch){self.show(batch, epoch);}); + }; + + Visualizer.prototype.showStep = function(step) { + var epoch = step % this.layout.data.epochs, + batch = Math.floor(step / this.layout.data.epochs); + this.show(batch, epoch); + }; + + Visualizer.prototype.show = function(batch, epoch, transitionDuration) { + this.network.updateWeights(this.data[batch][epoch], this.layout.layers[batch][epoch]); + if (transitionDuration) { + // 10% extra time per frame for the code execution + this.domLink + .transition() + .duration(transitionDuration / 1.1); + } + this.domLink.style("stroke-width", function(d) { return d.weight * _weightScaler; }); + }; + + Visualizer.prototype._setup = function() { + var self = this; + // create empty elements and determine the visualisation's size + var width = parseInt(this.svg.style("width")) || 800, + height = parseInt(this.svg.style("height")) || 600; + + this.network = new Network(this.layout); + this.network.init(); + this.network.resize({"width": width, "height": height}); + this.network.updateWeights(this.data[0][0], this.layout.layers[0][0]); + + function reposition() { + width = parseInt(self.svg.style("width")) || 800; + height = parseInt(self.svg.style("height")) || 600; + + self.network.resize({"width": width, "height": height}); + + node + .attr("cx", function(d) { return d.x; }) + .attr("cy", function(d) { return d.y; }) + .attr("r", function(d) { return d.r; }); + link + .attr("x1", function(d) { return d.source.x; }) + .attr("y1", function(d) { return d.source.y; }) + .attr("x2", function(d) { return d.target.x; }) + .attr("y2", function(d) { return d.target.y; }); + } + window.addEventListener("resize", reposition, true); + + var link = this.svg.selectAll(".link") + .data(this.network.links) + .enter().append("line") + .attr("class", function(d) { return "link source-" + d.source.id + " target-" + d.target.id; }) + .style("stroke", linkColor) + .style("stroke-width", function(d) { return d.weight * _weightScaler; }) + .on("mouseover", function(d) { + d3.select("#node-" + d.source.id).style("fill", nodeHighlightColor); + d3.select("#node-" + d.target.id).style("fill", nodeHighlightColor); + d3.select(this).style("stroke", linkHighlightColor); + }) + .on("mouseout", function(d) { + d3.select("#node-" + d.source.id).style("fill", nodeColor); + d3.select("#node-" + d.target.id).style("fill", nodeColor); + d3.select(this).style("stroke", linkColor); + }); + var node = this.svg.selectAll(".node") + .data(this.network.nodes) + .enter().append("circle") + .attr("class", "node") + .attr("id", function(d) { return "node-" + d.id; }) + .style("fill", nodeColor) + .on("mouseover", function(d) { + d3.selectAll(".source-" + d.id).style("stroke", linkHighlightColor); + d3.selectAll(".target-" + d.id).style("stroke", linkHighlightColor); + d3.select(this).style("fill", nodeHighlightColor); + }) + .on("mouseout", function(d) { + d3.selectAll(".source-" + d.id).style("stroke", linkColor); + d3.selectAll(".target-" + d.id).style("stroke", linkColor); + d3.select(this).style("fill", nodeColor); + }); + + reposition(); + }; + + return Visualizer; +})(); -- cgit v1.3.1