// 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 = primaryColor[2], nodeSelectionColor = 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.frameDuration = time / (this.layout.data.batches * this.layout.data.epochs / this.step); } Animator.prototype.start = function(bat, ep, callback, finishedCb) { var self = this; this.epoch = ep; this.batch = bat; this.finishedCb = finishedCb; wrapper = function() { callback(self.batch, self.epoch, self.frameDuration); self.animate(); }; this.pid = setInterval(wrapper, this.frameDuration); }; Animator.prototype.stop = function() { if (this.finishedCb) this.finishedCb(); clearInterval(this.pid); }; 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; this.stop(); } } }; return Animator; })(); /* main */ var Visualizer = (function() { var _weightScaler = 10; // size of thickest line function Visualizer(svg) { this.loader = new Loader(); this.svg = svg; this.stepHooks = []; this.stopHooks = []; } Visualizer.prototype.getSteps = function() { return this.layout.data.batches * this.layout.data.epochs; }; Visualizer.prototype.getDatashape = function() { return this.layout.data; }; 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.stopAnimation = function() { this.animator.stop(); }; Visualizer.prototype.animate = function(animationTime, resolution, step) { // animationTime in seconds per frame // resolution in epochs per frame var self = this; var batch = Math.floor(step / this.layout.data.epochs), epoch = step % this.layout.data.epochs; this.animator = new Animator(this.layout, animationTime * 1000, resolution); this.animator.start(batch, epoch, function(cbatch, cepoch) { self.show(cbatch, cepoch); }, function() { self.stopHooks.forEach(function(hook){ hook(); }); }); }; Visualizer.prototype.hookStepper = function(hook) { this.stepHooks.push(hook); }; Visualizer.prototype.hookStop = function(hook) { this.stopHooks.push(hook); }; 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) { var self = this; 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; }); this.stepHooks.forEach(function(hook){ hook(batch * self.layout.data.epochs + epoch); }); }; Visualizer.prototype.reposition = function() { var width = parseInt(this.svg.style("width")) || 800, height = parseInt(this.svg.style("height")) || 600; var node = this.svg.selectAll(".node"); this.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; }); this.domLink .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; }); }; 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]); this.domLink = 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) { var src = d3.select("#node-" + d.source.id); if (src.attr("data-highlight") != "true") src.style("fill", nodeColor); var tar = d3.select("#node-" + d.target.id); if (tar.attr("data-highlight") != "true") tar.style("fill", nodeColor); var me = d3.select(this); if (me.attr("data-highlight") != "true") me.style("stroke", linkColor); }); var node = this.svg.selectAll(".node") .data(this.network.nodes) .enter().append("circle") .attr("class", function(d) { return "node layer-" + d.layer; }) .attr("id", function(d) { return "node-" + d.id; }) .style("fill", nodeColor) .style("stroke-width", 0) .style("stroke", nodeSelectionColor) .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)[0].forEach(function (el) { var src = d3.select(el); if (src.attr("data-highlight") != "true") src.style("stroke", linkColor); }); d3.selectAll(".target-" + d.id)[0].forEach(function (el) { var tar = d3.select(el); if (tar.attr("data-highlight") != "true") tar.style("stroke", linkColor); }); var me = d3.select(this); if (me.attr("data-highlight") != "true") me.style("fill", nodeColor); }) .on("click", function(d) { var me = d3.select(this); if (me.attr("data-highlight") == "true") { d3.selectAll(".source-" + d.id).attr("data-highlight", "false"); //d3.selectAll(".target-" + d.id).attr("data-highlight", "false"); me.attr("data-highlight", "false") .style("stroke-width", 0); } else { d3.selectAll(".source-" + d.id).attr("data-highlight", "true"); //d3.selectAll(".target-" + d.id).attr("data-highlight", "true"); me.attr("data-highlight", "true") .style("stroke-width", d.r / 2); } }); this.reposition(); }; return Visualizer; })();