├── big_vis ├── wiki.html └── words.html ├── img ├── Cho-TimePhrase-TSNE.png ├── MNIST-Graph-100.png ├── MNIST-Graph-Raw.png ├── MNIST-PCA-Conv1.png ├── MNIST-PCA-Conv2.png ├── MNIST-PCA-R5.png ├── MNIST-PCA-Sigmoid5.png ├── MNIST-PCA-raw.png ├── MNIST-tSNE-DigitsInImage.png ├── RepMap.png ├── Translation2-Backwards.png ├── Translation2-RepArrow.png ├── WordGender.png ├── fig.png ├── netvis-mnist-100S.png ├── netvis-mnist-5R-PCA.png ├── netvis-mnist-5S-PCA.png ├── netvis-simple-2S.png ├── netvis-simple-NoHid.png ├── simple2_0.png ├── simple2_1.png ├── simple2_data.png ├── simple2_linear.png ├── wiki-pic-major.png ├── wiki-pics-both.png └── words-pic.png ├── index.md └── js ├── BasicVis.js ├── CostLayout-worker-3D.js ├── CostLayout-worker.js ├── MnistVis.js ├── NetVis.js ├── data ├── ArxivEmbed-sub5.js ├── ArxivEmbed.js ├── MNIST-SNE-good.js ├── MNIST-reps.js ├── MNIST.js ├── TranslationEnglishEmbed-N1.js ├── WikiEmbed-sub2.js ├── WikiEmbed-sub5.js ├── WikiEmbed.js ├── WordEmbed-10000.js ├── WordEmbed-2000.js ├── WordEmbed-5000.js ├── WordEmbed-50000.js ├── WordEmbed-Meta.js ├── WordEmbed-Vecs.js ├── WordEmbed.js ├── mnist_pca.js └── mnist_pca.js~ └── foreign ├── TrackballControls.js ├── TrackballControls.js~ ├── d3.v3.min.js ├── jquery-1.7.0.min.js ├── jquery-ui.css └── jquery-ui.min.js /big_vis/wiki.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 38 | 39 | 40 | 41 | 42 | 43 |
44 |

Wikipedia Paragraph Vectors

45 |
46 |
47 | 48 |
49 |
50 |
51 | Color articles by Wikipedia category (eg. films):

52 |
53 |
54 |
55 |
56 | Wikipedia Paragraph Vectors Visualized with t-SNE
57 | (Hover over a point to see the title. Click to open article.) 58 |
59 |
60 | 61 | 62 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /big_vis/words.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 38 | 39 | 40 | 41 | 42 | 43 |
44 |

Word Embedding

45 |
46 |
47 | 48 |
49 |
50 |
51 | Color words by WordNet synset (eg. *region.n.03*):

52 |
53 |
54 |
55 |
56 | Word Embedding Visualized with t-SNE
57 | (Hover over a point to see the word.) 58 |
59 |
60 | 61 | 62 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /img/Cho-TimePhrase-TSNE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/Cho-TimePhrase-TSNE.png -------------------------------------------------------------------------------- /img/MNIST-Graph-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/MNIST-Graph-100.png -------------------------------------------------------------------------------- /img/MNIST-Graph-Raw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/MNIST-Graph-Raw.png -------------------------------------------------------------------------------- /img/MNIST-PCA-Conv1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/MNIST-PCA-Conv1.png -------------------------------------------------------------------------------- /img/MNIST-PCA-Conv2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/MNIST-PCA-Conv2.png -------------------------------------------------------------------------------- /img/MNIST-PCA-R5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/MNIST-PCA-R5.png -------------------------------------------------------------------------------- /img/MNIST-PCA-Sigmoid5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/MNIST-PCA-Sigmoid5.png -------------------------------------------------------------------------------- /img/MNIST-PCA-raw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/MNIST-PCA-raw.png -------------------------------------------------------------------------------- /img/MNIST-tSNE-DigitsInImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/MNIST-tSNE-DigitsInImage.png -------------------------------------------------------------------------------- /img/RepMap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/RepMap.png -------------------------------------------------------------------------------- /img/Translation2-Backwards.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/Translation2-Backwards.png -------------------------------------------------------------------------------- /img/Translation2-RepArrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/Translation2-RepArrow.png -------------------------------------------------------------------------------- /img/WordGender.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/WordGender.png -------------------------------------------------------------------------------- /img/fig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/fig.png -------------------------------------------------------------------------------- /img/netvis-mnist-100S.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/netvis-mnist-100S.png -------------------------------------------------------------------------------- /img/netvis-mnist-5R-PCA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/netvis-mnist-5R-PCA.png -------------------------------------------------------------------------------- /img/netvis-mnist-5S-PCA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/netvis-mnist-5S-PCA.png -------------------------------------------------------------------------------- /img/netvis-simple-2S.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/netvis-simple-2S.png -------------------------------------------------------------------------------- /img/netvis-simple-NoHid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/netvis-simple-NoHid.png -------------------------------------------------------------------------------- /img/simple2_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/simple2_0.png -------------------------------------------------------------------------------- /img/simple2_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/simple2_1.png -------------------------------------------------------------------------------- /img/simple2_data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/simple2_data.png -------------------------------------------------------------------------------- /img/simple2_linear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/simple2_linear.png -------------------------------------------------------------------------------- /img/wiki-pic-major.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/wiki-pic-major.png -------------------------------------------------------------------------------- /img/wiki-pics-both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/wiki-pics-both.png -------------------------------------------------------------------------------- /img/words-pic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colah/Visualizing-Representations/76a9575677b6efa75db94e57577ea0a7507b0681/img/words-pic.png -------------------------------------------------------------------------------- /js/BasicVis.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | var BasicVis = new function() { 4 | 5 | // If we disable strict, we can use some tricks to 6 | // give better error messages. By default, we don't 7 | // do this, to make finding bugs easier and to 8 | // comply with the style guide. 9 | 10 | 'use strict'; 11 | 12 | // In non-quirk browser modes, d3.selection.style("attr", int_val) 13 | // doesn't work! This is because it should be int_val+"px" 14 | // We wrap around the function to catch this case. 15 | 16 | var style_ = d3.selection.prototype.style; 17 | 18 | d3.selection.prototype.style = function(a,b) { 19 | if (arguments.length == 1) { 20 | return style_.call(this, a); 21 | } else if (typeof b == 'number') { 22 | style_.call(this, a, b + "px"); 23 | } else { 24 | style_.call(this, a, b); 25 | } 26 | return this; 27 | }; 28 | 29 | // Utilities! 30 | // ============= 31 | 32 | // make_function 33 | // A lot of arguments can be a constant 34 | // or function. This turns constants into 35 | // functions. 36 | 37 | var make_function = function(val) { 38 | if (typeof val == 'function') { 39 | return val; 40 | } else { 41 | return function() {return val;}; 42 | } 43 | }; 44 | 45 | // VisElement 46 | // This is a super class for all 47 | // our visualization elements 48 | 49 | this.VisElement = function() { 50 | 51 | this.updateTimeout = null; 52 | 53 | this.layout = function() {}; 54 | this.render = function() {}; 55 | 56 | this.update = function() { 57 | this.layout(); 58 | this.render(); 59 | }; 60 | 61 | this.scheduleUpdate = function(n) { 62 | var this_ = this; 63 | n = n || 10; 64 | var update = function() { 65 | this_.layout(); 66 | this_.render(); 67 | this_.updateTimeout = null; 68 | }; 69 | if (this.updateTimeout) clearTimeout(this.updateTimeout); 70 | this.updateTimeout = setTimeout(update, n); 71 | }; 72 | 73 | this.bindToWindowResize = function() { 74 | var this_ = this; 75 | var scheduleUpdate = function() { 76 | this_.scheduleUpdate(); 77 | }; 78 | $(window).resize(scheduleUpdate); 79 | }; 80 | 81 | // make_selector 82 | // We'll use this in all our constructors 83 | // to get a d3 selection to build at. 84 | 85 | this.make_selector = function(s) { 86 | var caller = ''; 87 | //var caller = arguments.callee.caller.name; 88 | if (!d3) throw Error(caller + '(): Depends on the D3 library,' + 89 | ' which does not seem to be in scope.'); 90 | if (typeof s == 'string') { 91 | var str = s; 92 | s = d3.select(s); 93 | if (s.empty()) throw Error(caller + '(): selector \'' + str + 94 | '\' doesn\'t seem to correspond to an element.'); 95 | return s; 96 | } else if (typeof s == 'object') { 97 | if ('node' in s) { 98 | // s seems to be a d3 selector 99 | return s; 100 | } else if ('jquery' in s) { 101 | // s seems to be a jquery object 102 | throw TypeError(caller + '(): selector can\'t be a JQuery object;' + 103 | ' please use a string or d3.select().'); 104 | } 105 | } 106 | throw TypeError(caller + '(): Given selector of type ' + typeof s + 107 | ' is not a valid selector; please use a string or d3.select().'); 108 | }; 109 | 110 | }; 111 | 112 | this.VisElement.prototype = new Object(); 113 | 114 | 115 | 116 | // Container 117 | //========================= 118 | 119 | 120 | 121 | this.Container = function Container(s) { 122 | this.s = this.make_selector(s); 123 | this.inner = this.s.append('div'); 124 | this._children = []; 125 | this._children_divs = []; 126 | return this; 127 | }; 128 | 129 | this.Container.prototype = new this.VisElement(); 130 | 131 | this.Container.prototype.new_child = function(constructor) { 132 | var child_div = this.inner.append('div'); 133 | child_div.pos = function pos(v) { 134 | child_div 135 | .style('left', v[0]) 136 | .style('top', v[1]); 137 | return child_div; 138 | }; 139 | child_div.size = function size(v) { 140 | child_div 141 | .style('width', v[0]) 142 | .style('height', v[1]); 143 | return child_div; 144 | }; 145 | var child = new constructor(child_div); 146 | child.div = child_div; 147 | this._children_divs.push(child_div); 148 | this._children.push(child); 149 | this.scheduleUpdate(); 150 | return child; 151 | }; 152 | 153 | 154 | this.Container.prototype.layout = function layout() { 155 | var W = parseInt(this.s.style('width')); 156 | this.inner 157 | .style('width', 1.0 * W) 158 | .style('position', 'relative'); 159 | if (!this.child_layout) 160 | throw Error('Container: Must implement child_layout()' + 161 | ' to position and size child divs.'); 162 | for (var i = 0; i < this._children.length; i++) { 163 | this._children_divs[i] 164 | .style('position', 'absolute'); 165 | } 166 | this.child_layout(); 167 | for (var i = 0; i < this._children.length; i++) { 168 | this._children[i].layout(); 169 | } 170 | return this; 171 | }; 172 | 173 | this.Container.prototype.render = function render() { 174 | for (var i = 0; i < this._children.length; i++) { 175 | this._children[i].render(); 176 | } 177 | return this; 178 | }; 179 | 180 | // Equation 181 | //==================== 182 | 183 | 184 | this.Equation = function Equation(s) { 185 | 186 | if (typeof MathJax === 'undefined') 187 | throw Error('Equation(): Equation depends on the MathJax library,' + 188 | ' which does not seem to be in scope.'); 189 | 190 | this.s = this.make_selector(s); 191 | this.inner = this.s.append('div'); 192 | this._text = ''; 193 | 194 | // Create empty MathJax Equation: 195 | // Create an empty 196 | // Then tell MathJax to look for it when it has time. 197 | // (In case page is already loaded when we're constructed.) 198 | this.inner.append('script') 199 | .attr('type', 'math/tex') 200 | .text(''); 201 | MathJax.Hub.Queue(['Process', MathJax.Hub, this.inner.node()]); 202 | 203 | return this; 204 | }; 205 | 206 | this.Equation.prototype = new this.VisElement(); 207 | 208 | this.Equation.prototype.layout = function layout() {return this;}; 209 | 210 | this.Equation.prototype.render = function render() { 211 | var node = this.inner.node(); 212 | var eq = MathJax.Hub.getAllJax(node); 213 | // MathJax might not be done rendering! 214 | // Has it made our ElementJax yet? 215 | if (!eq.length) { 216 | // No? Let's try again later. 217 | this.scheduleUpdate(100); 218 | } else { 219 | // We schedule updating an equation wiht MathJax. 220 | MathJax.Hub.Queue(['Text', eq[0], this._text]); 221 | } 222 | return this; 223 | }; 224 | 225 | this.Equation.prototype.latex = function(val) { 226 | if (!arguments.length) return this._text; 227 | this._text = val; 228 | this.scheduleUpdate(); 229 | return this; 230 | }; 231 | 232 | //ImgDisplay 233 | //================================================================ 234 | 235 | this.ImgDisplay = function ImgDisplay(s) { 236 | this.s = this.make_selector(s); 237 | this.canvas = this.s.append('canvas'); 238 | this._data = {}; 239 | this._data.shape = null; 240 | this._data.imgs = null; 241 | }; 242 | 243 | this.ImgDisplay.prototype = new this.VisElement(); 244 | 245 | this.ImgDisplay.prototype.layout = function layout() { 246 | var W = parseInt(this.s.style('width')); 247 | this.canvas 248 | .attr('style', 'image-rendering:-moz-crisp-edges;' + 249 | 'image-rendering: -o-crisp-edges;' + 250 | 'image-rendering:-webkit-optimize-contrast;' + 251 | '-ms-interpolation-mode:nearest-neighbor;' + 252 | 'image-rendering: pixelated;') 253 | .style('border', '1px solid #000000') 254 | .style('width', 1.0 * W) 255 | .style('height', 1.0 * W); 256 | return this; 257 | }; 258 | 259 | this.ImgDisplay.prototype.render = function() {return this;}; 260 | 261 | this.ImgDisplay.prototype.show = function(i) { 262 | 263 | var i = parseInt(i); 264 | var imgs = this._data.imgs; 265 | var shape = this._data.shape; 266 | 267 | if (shape.length == 2) { 268 | var X = shape[0]; 269 | var Y = shape[1]; 270 | } else if (shape.length == 3) { 271 | var X = shape[1]; 272 | var Y = shape[2]; 273 | } 274 | 275 | var ctx = this.canvas[0][0].getContext('2d'); 276 | var img = ctx.getImageData(0, 0, X, Y); 277 | var imgData = img.data; 278 | 279 | if (!this._data.imgs || !this._data.shape) { 280 | throw Error('ImgDisplay.show(): Must set ImgDisplay.imgs() ' + 281 | 'and ImgDisplay.shape() before showing image.'); 282 | } 283 | 284 | var imgSize = 1; 285 | for (var n = 0; n < shape.length; n++) { 286 | imgSize *= shape[n]; 287 | } 288 | 289 | if (imgs.length < imgSize * (i + 1)) { 290 | throw Error('ImgDisplay.show(): Requested image ' + i + 291 | ' out of bounds of ImgDisplay.imgs().'); 292 | } 293 | 294 | if (shape.length == 2) { 295 | for (var dx = 0; dx < X; ++dx) 296 | for (var dy = 0; dy < Y; ++dy) { 297 | var pos = dx + shape[0] * dy; 298 | var s = 256 * (1 - imgs[imgSize * i + pos]); 299 | imgData[4 * pos + 0] = s; 300 | imgData[4 * pos + 1] = s; 301 | imgData[4 * pos + 2] = s; 302 | imgData[4 * pos + 3] = 255; 303 | } 304 | } else if (shape.length == 3) { 305 | for (var c = 0; c < 3; ++c ) 306 | for (var dx = 0; dx < X; ++dx) 307 | for (var dy = 0; dy < Y; ++dy) { 308 | var pos = dx + shape[1] * dy; 309 | var s = 256 * (1 - imgs[imgSize * i + pos + shape[1]*shape[2]*c]); 310 | imgData[4 * pos + ((3-c+2)%3)] = s; 311 | imgData[4 * pos + 3] = 255; 312 | } 313 | } 314 | 315 | ctx.putImageData(img, 0, 0); 316 | 317 | return this; 318 | }; 319 | 320 | this.ImgDisplay.prototype.imgs = function(val) { 321 | if (!arguments.length) return this._data.imgs; 322 | this._data.imgs = val; 323 | return this; 324 | }; 325 | 326 | this.ImgDisplay.prototype.shape = function(val) { 327 | if (!arguments.length) return this._data.shape; 328 | if (val.length == 2) { 329 | this.canvas 330 | .attr('width', val[0]) 331 | .attr('height', val[1]); 332 | } else { 333 | this.canvas 334 | .attr('width', val[1]) 335 | .attr('height', val[2]); 336 | } 337 | this._data.shape = val; 338 | return this; 339 | }; 340 | 341 | 342 | //MatrixSelector 343 | //================================================================ 344 | 345 | 346 | this.MatrixSelector = function MatrixSelector(s) { 347 | this.s = this.make_selector(s); 348 | this.svg = this.s.append('svg'); 349 | this._data = {}; 350 | this._data.shape = null; 351 | this._data.value = [-1, -1]; 352 | this._data.pixels = null; 353 | this.value = function(val) { 354 | if (!arguments.length) return this._data.value; 355 | this._data.value = val; 356 | this.value.change(val); 357 | this.scheduleUpdate(); 358 | return this; 359 | }; 360 | this.value.change = function() {}; 361 | }; 362 | 363 | this.MatrixSelector.prototype = new this.VisElement(); 364 | 365 | this.MatrixSelector.prototype.layout = function layout() { 366 | var W = parseInt(this.s.style('width')); 367 | this.svg 368 | .style('border', '1px solid #000000') 369 | .style('width', 1.0 * W) 370 | .style('height', 1.0 * W); 371 | return this; 372 | }; 373 | 374 | this.MatrixSelector.prototype.render = function() { 375 | 376 | var pixels = this._data.pixels; 377 | var shape = this._data.shape; 378 | if (!shape) throw Error('ImgDisplay.render():' + 379 | ' Set shape first with ImgDisplay.shape()'); 380 | var value = this._data.value; 381 | var this_ = this; 382 | 383 | var selection = this.svg.selectAll('rect') 384 | .data(pixels); 385 | 386 | var W = parseInt(this.s.style('width')); 387 | var H = parseInt(this.s.style('height')); 388 | 389 | // create new rects on svg 390 | selection.enter().append('rect') 391 | .style('fill', 'blue') 392 | .on('click', function(d, i) { 393 | this_.value(d); 394 | }); 395 | // remove old ones from svg 396 | selection.exit().remove(); 397 | // update/reset rects properties 398 | selection 399 | .attr('width', W / shape[0]) 400 | .attr('height', H / shape[1]) 401 | .attr('x', function(d, i) { return W * d[0] / shape[0]; }) 402 | .attr('y', function(d, i) { return H * (1 - d[1] / shape[1]); }) 403 | .classed('hover_show', function(d, i) 404 | {return d[0] != value[0] || d[1] != value[1];}); 405 | 406 | return this; 407 | 408 | }; 409 | 410 | this.MatrixSelector.prototype.shape = function(val) { 411 | if (!arguments.length) return this._data.shape; 412 | if (!val[0] || !val[1]) 413 | throw Error('shape(): shape must be an array of length 2 or 3.' + 414 | ' For example, [28, 28] or [32, 32, 3]'); 415 | this._data.shape = val; 416 | this._data.pixels = []; 417 | for (var i = 0; i < val[0]; i++) { 418 | for (var j = 0; j < val[1] + 1; j++) { 419 | this._data.pixels.push([i, j]); 420 | } 421 | } 422 | return this; 423 | }; 424 | 425 | 426 | 427 | // ScatterPlot 428 | // ============================ 429 | 430 | 431 | this.ScatterPlot = function ScatterPlot(s) { 432 | this.s = this.make_selector(s); 433 | this.svg = this.s.append('svg'); 434 | this.zoom_g = this.svg.append('g'); 435 | 436 | this._data = {}; 437 | this._data.N = 0; 438 | this._data.scale = 1; 439 | this._data.color = function() {return 'rgb(50,50,50)';}; 440 | this._data.x = function() {return 0;}; 441 | this._data.y = function() {return 0;}; 442 | this._data.size = function() {return 0;}; 443 | this._data.mouseover = function() {}; 444 | 445 | this._data.xrange = null; 446 | this._data.yrange = null; 447 | 448 | this.xmap = d3.scale.linear(); 449 | this.ymap = d3.scale.linear(); 450 | 451 | var this_ = this; 452 | 453 | this.zoom = d3.behavior.zoom() 454 | .on("zoom", function() {this_._zoomed();}); 455 | 456 | this.xrange.fit = function(data) { 457 | var x1 = d3.min(data); 458 | var x2 = d3.max(data); 459 | var dx = x2 - x1; 460 | this_.xrange([x1-0.02*dx, x2+0.02*dx]); 461 | return this_; 462 | }; 463 | 464 | this.yrange.fit = function(data) { 465 | var x1 = d3.min(data); 466 | var x2 = d3.max(data); 467 | var dx = x2 - x1; 468 | this_.yrange([x1-0.02*dx, x2+0.02*dx]); 469 | return this_; 470 | }; 471 | 472 | 473 | }; 474 | 475 | this.ScatterPlot.prototype = new this.VisElement(); 476 | 477 | this.ScatterPlot.prototype.layout = function layout() { 478 | var W = parseInt(this.s.style('width')); 479 | this.svg 480 | .style('width', W) 481 | .style('height', W); 482 | var H = parseInt(this.s.style('height')); 483 | var D = Math.min(W, H) / 2 - 2; 484 | this.xmap.range([W / 2 - D, W / 2 + D]); 485 | this.ymap.range([H / 2 - D, H / 2 + D]); 486 | return this; 487 | }; 488 | 489 | 490 | this.ScatterPlot.prototype.render = function() { 491 | var data = this._data; 492 | var this_ = this; 493 | var selection = this.zoom_g.selectAll('circle') 494 | .data(d3.range(data.N)); 495 | this.points = selection; 496 | 497 | 498 | var W = parseInt(this.svg.style('width')); 499 | var H = parseInt(this.svg.style('height')); 500 | var D = Math.min(W, H) / 2 - 2; 501 | 502 | 503 | // create new circles on svg 504 | selection.enter().append('circle') 505 | .attr('r', 0) 506 | .classed({'highlight' : true}) 507 | .on('mouseover', this._data.mouseover); 508 | var size = data.size()/Math.pow(data.scale, 0.7); 509 | // remove old circles from svg 510 | selection.exit().remove(); 511 | // update/reset circle properties 512 | selection.transition().duration(200) 513 | .attr('cx', function(d, i) { return this_.xmap(data.x(i)); }) 514 | .attr('cy', function(d, i) { return this_.ymap(data.y(i)); }); 515 | selection 516 | .attr('r', size) 517 | .attr('fill', data.color); 518 | 519 | return this; 520 | 521 | }; 522 | 523 | this.ScatterPlot.prototype.N = function(val) { 524 | if (!arguments.length) return this._data.N; 525 | this._data.N = val; 526 | this.scheduleUpdate(); 527 | return this; 528 | }; 529 | 530 | this.ScatterPlot.prototype.color = function(val) { 531 | if (!arguments.length) return this._data.color; 532 | this._data.color = make_function(val); 533 | this.scheduleUpdate(); 534 | return this; 535 | }; 536 | 537 | this.ScatterPlot.prototype.size = function(val) { 538 | if (!arguments.length) return this._data.size; 539 | this._data.size = make_function(val); 540 | this.scheduleUpdate(); 541 | return this; 542 | }; 543 | 544 | this.ScatterPlot.prototype.x = function(val) { 545 | if (!arguments.length) return this._data.x; 546 | this._data.x = make_function(val); 547 | this.scheduleUpdate(); 548 | return this; 549 | }; 550 | 551 | this.ScatterPlot.prototype.y = function(val) { 552 | if (!arguments.length) return this._data.y; 553 | this._data.y = make_function(val); 554 | this.scheduleUpdate(); 555 | return this; 556 | }; 557 | 558 | this.ScatterPlot.prototype.xrange = function(val) { 559 | if (!arguments.length) return this._data.xrange; 560 | if (!(val.length == 2)) { 561 | if (val.length > 5) 562 | throw Error('xrange(): yrange must be an array of length 2.' + 563 | ' For example, [-1, 1]. Did you mean to use xrange.fit()?'); 564 | throw Error('xrange(): yrange must be an array of length 2.' + 565 | ' For example, [-1, 1].'); 566 | } 567 | this._data.xrange = val; 568 | this.xmap.domain(val); 569 | this.scheduleUpdate(); 570 | return this; 571 | }; 572 | 573 | this.ScatterPlot.prototype.yrange = function(val) { 574 | if (!arguments.length) return this._data.yrange; 575 | if (!(val.length == 2)) { 576 | if (val.length > 5) 577 | throw Error('yrange(): yrange must be an array of length 2.' + 578 | ' For example, [-1, 1]. Did you mean to use yrange.fit()?'); 579 | throw Error('yrange(): yrange must be an array of length 2.' + 580 | ' For example, [-1, 1].'); 581 | } 582 | this._data.yrange = val; 583 | this.ymap.domain(val); 584 | this.scheduleUpdate(); 585 | return this; 586 | }; 587 | 588 | this.ScatterPlot.prototype.mouseover = function(val) { 589 | if (!arguments.length) return this._data.mouseover; 590 | this._data.mouseover = val; 591 | this.scheduleUpdate(); 592 | return this; 593 | }; 594 | 595 | this.ScatterPlot.prototype.enable_zoom = function() { 596 | this.svg.call(this.zoom); 597 | return this; 598 | }; 599 | 600 | this.ScatterPlot.prototype._zoomed = function() { 601 | this.zoom_g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale +")"); 602 | this._data.scale = d3.event.scale; 603 | this.scheduleUpdate(); 604 | }; 605 | 606 | 607 | // Overlap 608 | // ========================= 609 | 610 | this.Overlap = function Overlap(s) { 611 | this.s = this.make_selector(s); 612 | this.inner = this.s.append('div'); 613 | this._children = []; 614 | this._children_divs = []; 615 | return this; 616 | }; 617 | 618 | this.Overlap.prototype = new this.VisElement(); 619 | 620 | this.Overlap.prototype.layout = function layout() { 621 | var W = parseInt(this.s.style('width')); 622 | this.inner 623 | .style('width', 1.0 * W) 624 | .style('height', 1.0 * W) 625 | .classed('overlap_inner', true) 626 | .style('position', 'relative'); 627 | for (var i = 0; i < this._children_divs.length; i++) { 628 | this._children_divs[i] 629 | .style('position', 'absolute') 630 | .style('width', 1.0 * W) 631 | .style('height', 1.0 * W) 632 | .style('top', 0) 633 | .style('left', 0); 634 | } 635 | for (var i = 0; i < this._children.length; i++) { 636 | this._children[i].layout(); 637 | } 638 | return this; 639 | }; 640 | 641 | this.Overlap.prototype.render = function render() { 642 | for (var i = 0; i < this._children.length; i++) { 643 | this._children[i].render(); 644 | } 645 | return this; 646 | }; 647 | 648 | this.Overlap.prototype.new_child = function(constructor) { 649 | var child_div = this.inner.append('div'); 650 | var child = new constructor(child_div); 651 | this._children_divs.push(child_div); 652 | this._children.push(child); 653 | this.scheduleUpdate(); 654 | return child; 655 | }; 656 | 657 | 658 | // ToolTip 659 | // ======================================== 660 | 661 | this.Tooltip = function Tooltip() { 662 | this.div = d3.select('body').append('div') 663 | .style("position", "absolute"); 664 | this.hide(); 665 | this.timeout = null; 666 | var this_ = this; 667 | this.div.on("mouseover", function() { 668 | var pos = [d3.event.pageX + 10, d3.event.pageY + 10 ]; 669 | this_.move(pos); 670 | }); 671 | return this; 672 | }; 673 | 674 | this.Tooltip.prototype = new Object(); 675 | 676 | this.Tooltip.prototype.size = function(val) { 677 | if (!arguments.length) return this.div.style("width"); 678 | this.div.style("width", val); 679 | return this; 680 | }; 681 | 682 | this.Tooltip.prototype.move = function(val) { 683 | this.div 684 | .style("left", val[0]) 685 | .style("top", val[1]); 686 | return this; 687 | }; 688 | 689 | this.Tooltip.prototype.unhide = function() { 690 | this.div 691 | .style("visibility", "visible") 692 | .style("z-index", "10"); 693 | //throw Error("just debugging"); 694 | return this; 695 | }; 696 | 697 | this.Tooltip.prototype.hide = function() { 698 | this.div.style("visibility", "hidden"); 699 | return this; 700 | }; 701 | 702 | this.Tooltip.prototype.bind = function(s, cond) { 703 | var this_ = this; 704 | var timeout = null; 705 | var show = function(i) { 706 | if (cond && ! cond(i) ) { 707 | return; 708 | } 709 | clearTimeout(timeout); 710 | this_.timeout = null; 711 | var pos = [d3.event.pageX + 10, d3.event.pageY + 10 ]; 712 | this_.move(pos); 713 | this_.display(i); 714 | this_.unhide(); 715 | }; 716 | s.on("mouseover", show); 717 | s.on("mousemove", show); 718 | s.on("mouseout", function(i) { 719 | if (!this_.timeout) 720 | this_.timeout = setTimeout(function() {this_.hide(); this_.timeout = null;}, 300); 721 | }); 722 | }; 723 | 724 | this.Tooltip.prototype.bind_move = function(s) { 725 | var this_ = this; 726 | s.on("mousemove", function() { 727 | var pos = [d3.event.pageX + 10, d3.event.pageY + 10 ]; 728 | this_.move(pos); 729 | }); 730 | }; 731 | 732 | // ImgTooltip 733 | //========================================= 734 | 735 | this.ImgTooltip = function ImgTooltip() { 736 | BasicVis.Tooltip.call(this); 737 | this.div; 738 | // .style("border", "1px solid black"); 739 | this.img_display = new BasicVis.ImgDisplay(this.div); 740 | this.size("40px"); 741 | return this; 742 | }; 743 | 744 | this.ImgTooltip.prototype = Object.create(this.Tooltip.prototype); 745 | 746 | this.ImgTooltip.prototype.size = function size(val) { 747 | if (!arguments.length) return this.div.style("width"); 748 | this.div.style("width", val); 749 | this.img_display.scheduleUpdate(); 750 | return this; 751 | }; 752 | 753 | 754 | this.ImgTooltip.prototype.display = function display(i) { 755 | this.img_display.show(i); 756 | return this; 757 | }; 758 | 759 | // TextTooltip 760 | //========================================= 761 | 762 | this.TextTooltip = function TextTooltip() { 763 | BasicVis.Tooltip.call(this); 764 | this._labels = []; 765 | this.div 766 | .style("background-color", "white") 767 | .style("font-size", "130%") 768 | .style("padding-left", "2px") 769 | .style("padding-right", "2px") 770 | .style("padding-top", "1px") 771 | .style("padding-bottom", "1px") 772 | .style("border", "1px solid black"); 773 | return this; 774 | }; 775 | 776 | this.TextTooltip.prototype = Object.create(this.Tooltip.prototype); 777 | 778 | this.TextTooltip.prototype.display = function display(i) { 779 | var labels = this._labels; 780 | if (i < labels.length){ 781 | this.div.text(labels[i]); 782 | } else { 783 | this.div.text(""); 784 | } 785 | return this; 786 | }; 787 | 788 | // GraphPlot3 789 | // ================================== 790 | 791 | this.GraphPlot3 = function(s, init_z_pos) { 792 | this.s = this.make_selector(s); 793 | this.inner1 = this.s.append("div"); 794 | this.inner2 = this.inner1.append("div"); 795 | var inner_dom = this.inner2[0][0]; 796 | 797 | this.points = []; 798 | this.lines = []; 799 | 800 | this.mouse = new THREE.Vector2(-1, -1); 801 | this.INTERSECTED = null; 802 | 803 | this.point_event_funcs = {} 804 | 805 | //ThreeJS stuff 806 | this.camera = new THREE.PerspectiveCamera(60, 1.0/1.0, 0.01, 8000); 807 | this.camera.position.set(init_z_pos || 400, 0, 0); 808 | this.controls = new THREE.TrackballControls(this.camera, inner_dom); 809 | this.scene = new THREE.Scene(); 810 | this.projector = new THREE.Projector(); 811 | this.raycaster = new THREE.Raycaster(); 812 | this.renderer = new THREE.WebGLRenderer(); 813 | 814 | inner_dom.appendChild(this.renderer.domElement); 815 | 816 | this.make_materials(); 817 | 818 | var this_ = this; 819 | this.inner1.on("mousemove", function() { 820 | var W = parseInt(this_.s.style('width')); 821 | var H = parseInt(this_.s.style('height')); 822 | var X = d3.event.offsetX || d3.event.layerX || 0; 823 | var Y = d3.event.offsetY || d3.event.layerY || 0; 824 | this_.mouse.x = 2*X/W - 1; 825 | this_.mouse.y = -2*Y/H + 1; 826 | }); 827 | 828 | /*this.points.on = function on(event_name, f) { 829 | this_.point_event_funcs[event_name] = f; 830 | };*/ 831 | 832 | } 833 | 834 | this.GraphPlot3.prototype = new this.VisElement(); 835 | 836 | this.GraphPlot3.prototype.make_materials = function() { 837 | 838 | this.materials = { points: {}, selected_points: {}, lines: {} }; 839 | 840 | this.point_classes = []; 841 | 842 | var material = new THREE.MeshLambertMaterial( { color: 0x777777 } ); 843 | this.materials.points["default"] = material; 844 | 845 | var material = new THREE.MeshLambertMaterial( { color: 0x999999 } ); 846 | this.materials.selected_points["default"] = material; 847 | 848 | var material = new THREE.LineBasicMaterial( { color: 0x555555, linewidth: 1.8 } ); 849 | this.materials.lines["default"] = material; 850 | 851 | for (var i = 0; i < 10; i++) { 852 | var color = d3.hsl(360*i/10.0,0.5,0.5).toString(); 853 | var material = new THREE.MeshLambertMaterial({ color: color }); 854 | this.materials.points[i] = material; 855 | 856 | var color = d3.hsl(360*i/10.0,0.8,0.8).toString(); 857 | var material = new THREE.MeshLambertMaterial({ color: color }); 858 | this.materials.selected_points[i] = material; 859 | 860 | var color = d3.hsl(360*i/10.0,0.4,0.4).toString(); 861 | var material = new THREE.LineBasicMaterial({ color: color, linewidth: 1.8 }); 862 | this.materials.lines[i] = material; 863 | 864 | } 865 | } 866 | 867 | this.GraphPlot3.prototype.layout = function layout() { 868 | var W = parseInt(this.s.style('width')); 869 | var H = parseInt(this.s.style('height')); 870 | H = W*0.6; 871 | this.s.style('height', H); 872 | this.camera.aspect = W/H; 873 | this.camera.updateProjectionMatrix(); 874 | this.renderer.setClearColor('#FFFFFF'); 875 | this.renderer.setSize(W, H); 876 | this.renderer.sortObjects = false; 877 | this.controls.handleResize(); 878 | this.camera.position.z = 70; 879 | this._render(); 880 | 881 | var light = new THREE.DirectionalLight( 0xffffff, 2 ); 882 | light.position.set( 80, 0, 0 ).normalize(); 883 | this.scene.add( light ); 884 | 885 | var light = new THREE.DirectionalLight( 0xffffff ); 886 | light.position.set( 0, 0, 0 ).normalize(); 887 | this.scene.add( light ); 888 | 889 | var light = new THREE.DirectionalLight( 0xffffff ); 890 | light.position.set( -60, 40, 0 ).normalize(); 891 | this.scene.add( light ); 892 | 893 | var light = new THREE.DirectionalLight( 0xffffff ); 894 | light.position.set( -40, -40, 50 ).normalize(); 895 | this.scene.add( light ); 896 | 897 | var light = new THREE.DirectionalLight( 0xffffff ); 898 | light.position.set( -40, 0, -50 ).normalize(); 899 | this.scene.add( light ); 900 | 901 | }; 902 | 903 | 904 | 905 | this.GraphPlot3.prototype.make_points = function make_points(n) { 906 | var geometry = new THREE.SphereGeometry( 3, 15, 15 ); 907 | for ( var i = 0; i < n; i ++ ) { 908 | var k = this.point_classes[i]; 909 | k = k != undefined ? k : "default"; 910 | var object = new THREE.Mesh( geometry, this.materials.points[k] ); 911 | object.i = i; 912 | object.normal_material = this.materials.points[k]; 913 | object.selected_material = this.materials.selected_points[k]; 914 | this.scene.add( object ); 915 | this.points.push(object); 916 | } 917 | }; 918 | 919 | this.GraphPlot3.prototype.make_edges = function make_edges(edges) { 920 | for (var n in edges) { 921 | var edge = edges[n]; 922 | var i = edge[0], j = edge[1]; 923 | var yi = this.point_classes[i]; 924 | var yj = this.point_classes[j]; 925 | 926 | yi = yi != undefined ? yi : "default"; 927 | yj = yj != undefined ? yj : "default"; 928 | 929 | var material_ind = (yi == yj) ? yi : "default"; 930 | var material = this.materials.lines[material_ind]; 931 | 932 | var geometry = new THREE.Geometry(); 933 | geometry.dynamic = true; 934 | geometry.vertices.push(new THREE.Vector3(0,0,0)); 935 | geometry.vertices.push(new THREE.Vector3(0,0,0)); 936 | 937 | var line = new THREE.Line(geometry, material); 938 | line.i = i; line.j = j; 939 | line.is_point = false; 940 | this.scene.add(line); 941 | this.lines.push(line); 942 | } 943 | }; 944 | 945 | this.GraphPlot3.prototype._animate = function _animate() { 946 | this.controls.update(); 947 | var this_ = this; 948 | requestAnimationFrame(function(){this_._animate();}); 949 | this._render(); 950 | }; 951 | 952 | this.GraphPlot3.prototype._render = function _render() { 953 | 954 | var mouse3 = new THREE.Vector3(this.mouse.x, this.mouse.y, 1); 955 | var cam_pos = this.camera.position; 956 | this.projector.unprojectVector(mouse3, this.camera); 957 | this.raycaster.set(cam_pos, mouse3.sub(cam_pos).normalize()); 958 | var intersects = this.raycaster.intersectObjects(this.points); 959 | 960 | var new_intersected = (intersects.length > 0) ? 961 | intersects[0].object : null; 962 | 963 | if (this.INTERSECTED != new_intersected) { 964 | if (this.INTERSECTED != null){ 965 | if (this.point_event_funcs["mouseout"]) 966 | this.point_event_funcs["mouseout"](this.INTERSECTED.i) 967 | this.INTERSECTED.material = this.INTERSECTED.normal_material; 968 | } 969 | this.INTERSECTED = new_intersected; 970 | if (this.INTERSECTED != null) { 971 | if (this.point_event_funcs["mouseover"]) 972 | this.point_event_funcs["mouseover"](this.INTERSECTED.i) 973 | this.INTERSECTED.material = this.INTERSECTED.selected_material; 974 | } 975 | } 976 | 977 | this.renderer.render(this.scene, this.camera); 978 | }; 979 | 980 | this.GraphPlot3.prototype.position = function position(pos) { 981 | var K = 7; 982 | for (var n = 0; n < this.points.length; n++) { 983 | this.points[n].position.x = pos[3*n ]*K; 984 | this.points[n].position.y = pos[3*n+1]*K; 985 | this.points[n].position.z = pos[3*n+2]*K; 986 | } 987 | for (var n = 0; n < this.lines.length; n++) { 988 | var line = this.lines[n]; 989 | var i = line.i, j = line.j; 990 | line.geometry.vertices[0].set(K*pos[3*i], K*pos[3*i+1], K*pos[3*i+2]); 991 | line.geometry.vertices[1].set(K*pos[3*j], K*pos[3*j+1], K*pos[3*j+2]); 992 | line.geometry.verticesNeedUpdate = true; 993 | } 994 | }; 995 | 996 | 997 | }; 998 | 999 | 1000 | 1001 | -------------------------------------------------------------------------------- /js/CostLayout-worker-3D.js: -------------------------------------------------------------------------------- 1 | /*var i = 0; 2 | 3 | var data = new Float32Array(100); 4 | 5 | function timedCount() { 6 | i = i + 1; 7 | postMessage({ msg: "update", embed: data}); 8 | setTimeout("timedCount()",500); 9 | } 10 | 11 | timedCount();*/ 12 | 13 | 14 | var cost = null; 15 | var xs = null, N = null, D = null, Kstep = 1, Kmu = 1; 16 | var ds_orig = null; 17 | var ds_graph = null; 18 | var grad = null; 19 | var momentum = null; 20 | var embed = null; 21 | var edges = []; 22 | var temp = null; 23 | var pij = null; 24 | var qij = null; 25 | 26 | var normal = function() { 27 | var N = 15, x = 0; 28 | for (var n = 0; n < N; n++) { 29 | x += Math.random(); 30 | } 31 | x = 2*x - N; 32 | x = x/N; 33 | return x; 34 | }; 35 | 36 | function calc_ds() { 37 | for (var i = 0; i < N; i++) 38 | for (var j = 0; j < N; j++) { 39 | var sum = 0; 40 | for (var d = 0; d < D; d++){ 41 | var diff = xs[D*i + d] - xs[D*j + d]; 42 | sum += diff*diff; 43 | } 44 | ds_orig[i + N*j] = Math.sqrt(sum); 45 | } 46 | } 47 | 48 | function normalize(arr, N) { 49 | var sum = 0; 50 | for (var m = 0; m < N; m++) 51 | sum += arr[m]; 52 | if (sum > 0) { 53 | for (var m = 0; m < N; m++) 54 | arr[m] /= sum; 55 | } 56 | } 57 | 58 | function const_variance(k) { 59 | for (var n = 0; n < N; n++) { 60 | variances[n] = k; 61 | } 62 | } 63 | 64 | function calc_perps(target) { 65 | function test_perp(n, s) { 66 | for (var m = 0; m < N; m++) { 67 | var d = ds_orig[n+N*m]; 68 | temp[m] = Math.exp(-d*d/2/s/s); 69 | } 70 | temp[n] = 0; 71 | normalize(temp, N); 72 | var entropy = 0; 73 | for (var m = 0; m < N; m++) 74 | entropy += temp[m] * Math.log(temp[m]+0.000001) 75 | entropy *= -1/Math.log(2); 76 | return Math.pow(2, entropy); 77 | } 78 | for (var n = 0; n < N; n++) { 79 | var var0 = 0.1, perp0 = test_perp(0.1), 80 | var1 = 10.0, perp1 = test_perp(10.0); 81 | var mid; 82 | for (var k = 0; k < 10; k++) { 83 | mid = (var0 + var1)/2.0; 84 | var mid_perp = test_perp(n, mid); 85 | if (mid_perp < target){ 86 | var0 = mid; perp0 = mid_perp; 87 | } else { 88 | var1 = mid; perp1 = mid_perp; 89 | } 90 | } 91 | variances[n] = mid; 92 | } 93 | 94 | } 95 | 96 | function calc_pij() { 97 | for (var n = 0; n < N; n++) { 98 | var s = variances[n]; 99 | for (var m = 0; m < N; m++) { 100 | var d = ds_orig[n+N*m]; 101 | temp[m] = Math.exp(-d*d/2/s/s); 102 | } 103 | temp[n] = 0; 104 | normalize(temp, N); 105 | for (var m = 0; m < N; m++) { 106 | pij[N*n+m] = temp[m]; 107 | } 108 | } 109 | } 110 | 111 | function calc_pij_sym() { 112 | calc_pij(); 113 | for (var i = 0; i < N; i++) 114 | for (var j = 0; j < i; j++) { 115 | pij[N*i+j] += pij[N*j+i]; 116 | pij[N*i+j] /= 2*N; 117 | pij[N*j+i] = pij[N*i+j]; 118 | } 119 | /*for (var n = 0; n < N; n++) { 120 | var s = variances[n]; 121 | for (var m = 0; m < N; m++) { 122 | var d = ds_orig[n+N*m]; 123 | pij[N*n+m][m] = Math.exp(-d*d/2/s/s); 124 | } 125 | temp[n] = 0; 126 | } 127 | normalize(pij, N*N);*/ 128 | } 129 | 130 | function calc_qij() { 131 | for (var n = 0; n < N; n++) { 132 | for (var m = 0; m < N; m++) { 133 | var x1 = embed[3*n ], x2 = embed[3*m]; 134 | var y1 = embed[3*n+1], y2 = embed[3*m+1]; 135 | var z1 = embed[3*n+2], z2 = embed[3*m+2]; 136 | var dx = x2 - x1, dy = y2 - y1, dz = z2 - z1; 137 | var d2 = dx*dx + dy*dy + dz*dz; 138 | temp[m] = Math.exp(-d2/2); 139 | } 140 | temp[n] = 0; 141 | normalize(temp, N); 142 | for (var m = 0; m < N; m++) { 143 | qij[N*n+m] = temp[m]; 144 | } 145 | } 146 | } 147 | 148 | function calc_qij_sym() { 149 | for (var n = 0; n < N; n++) { 150 | for (var m = 0; m < N; m++) { 151 | var x1 = embed[3*n ], x2 = embed[3*m]; 152 | var y1 = embed[3*n+1], y2 = embed[3*m+1]; 153 | var z1 = embed[3*n+2], z2 = embed[3*m+2]; 154 | var dx = x2 - x1, dy = y2 - y1, dz = z2 - z1; 155 | var d2 = dx*dx + dy*dy + dz*dz; 156 | qij[N*n+m] = Math.exp(-d2/2); 157 | } 158 | qij[n] = 0; 159 | } 160 | normalize(qij, N*N); 161 | } 162 | 163 | function calc_qij_sym_t() { 164 | for (var n = 0; n < N; n++) { 165 | for (var m = 0; m < N; m++) { 166 | var x1 = embed[3*n ], x2 = embed[3*m]; 167 | var y1 = embed[3*n+1], y2 = embed[3*m+1]; 168 | var z1 = embed[3*n+2], z2 = embed[3*m+2]; 169 | var dx = x2 - x1, dy = y2 - y1, dz = z2 - z1; 170 | var d2 = dx*dx + dy*dy + dz*dz; 171 | qij[N*n+m] = 1.0/(1+d2); 172 | } 173 | temp[n] = 0; 174 | } 175 | normalize(qij, N*N); 176 | } 177 | 178 | 179 | function calc_ds_graph() { 180 | function is_edge(p) { 181 | for (var n = 0; n < edges.length; n++) { 182 | var edge = edges[n]; 183 | if (edge[0] == p[0] && edge[1] == p[1])// || edge[0] == p[1] && edge[1] == p[0]) 184 | return true; 185 | } 186 | return false; 187 | } 188 | for (var i = 0; i < N; i++) 189 | for (var j = 0; j < N; j++) { 190 | if ( is_edge([i,j]) || is_edge([j,i]) ) { 191 | ds_graph[i + N*j] = ds_orig[i + N*j]; 192 | } else { 193 | ds_graph[i + N*j] = 10000; 194 | } 195 | } 196 | for (var m = 0; m < N; m++) 197 | for (var i = 0; i < N; i++) 198 | for (var j = 0; j < N; j++) { 199 | var d_path = ds_graph[i + N*m] + ds_graph[m + N*j]; 200 | if (d_path < ds_graph[i + N*j]) { 201 | ds_graph[i + N*j] = d_path; 202 | } 203 | } 204 | 205 | } 206 | 207 | function threshold_edges(thresh){ 208 | edges = []; 209 | for (var i = 0; i < N; i++) 210 | for (var j = 0; j < i; j++) { 211 | if (ds_orig[i + N*j] < thresh) { 212 | edges.push([i,j]); 213 | } 214 | } 215 | } 216 | 217 | function knn_edges(K){ 218 | edges = []; 219 | for (var i = 0; i < N; i++) { 220 | var knn = []; 221 | // stupid, dumb, easy hack for testing: 222 | for (var k = 0; k < K; k++) { 223 | var x = null, xd = 100000; 224 | for (var j = 0; j < N; j++) { 225 | var D = ds_orig[i + N*j]; 226 | if ( i!=j && knn.indexOf(j) == -1 && D < xd) { 227 | x = j; 228 | xd = D; 229 | } 230 | } 231 | knn.push(x); 232 | edges.push([i, x]); 233 | } 234 | } 235 | } 236 | 237 | function opt(cost){ 238 | 239 | // Set Gradient to zero 240 | for (var i = 0; i < N; i++) { 241 | grad[3*i] = 0; 242 | grad[3*i+1] = 0; 243 | grad[3*i+2] = 0; 244 | } 245 | 246 | // Calculate new gradient 247 | if (cost == "MDS") { 248 | for (var i = 0; i < N; i++) 249 | for (var j = 0; j < i; j++) { 250 | var D = ds_orig[i + N*j]; 251 | var x1 = embed[3*i ], x2 = embed[3*j]; 252 | var y1 = embed[3*i+1], y2 = embed[3*j+1]; 253 | var z1 = embed[3*i+2], z2 = embed[3*j+2]; 254 | var dx = x2 - x1, dy = y2 - y1, dz = z2 - z1; 255 | var d2 = dx*dx + dy*dy + dz*dz; 256 | var d = Math.sqrt(d2) + 0.001; 257 | var K = 2*(d-D)/d; 258 | grad[3*i ] += K*dx; 259 | grad[3*i+1] += K*dy; 260 | grad[3*i+2] += K*dz; 261 | grad[3*j ] -= K*dx; 262 | grad[3*j+1] -= K*dy; 263 | grad[3*j+2] -= K*dz; 264 | } 265 | } else if (cost == "sammon") { 266 | for (var i = 0; i < N; i++) 267 | for (var j = 0; j < i; j++) { 268 | var D = ds_orig[i + N*j]; 269 | var x1 = embed[3*i ], x2 = embed[3*j]; 270 | var y1 = embed[3*i+1], y2 = embed[3*j+1]; 271 | var z1 = embed[3*i+2], z2 = embed[3*j+2]; 272 | var dx = x2 - x1, dy = y2 - y1, dz = z2 - z1; 273 | var d2 = dx*dx + dy*dy + dz*dz; 274 | var d = Math.sqrt(d2) + 0.001; 275 | var K = 2*(d-D)/d/D; 276 | grad[3*i ] += K*dx; 277 | grad[3*i+1] += K*dy; 278 | grad[3*i+2] += K*dz; 279 | grad[3*j ] -= K*dx; 280 | grad[3*j+1] -= K*dy; 281 | grad[3*j+2] -= K*dz; 282 | } 283 | } else if (cost == "MDS_exp") { 284 | for (var i = 0; i < N; i++) 285 | for (var j = 0; j < i; j++) { 286 | var D = ds_orig[i + N*j]; 287 | var x1 = embed[3*i ], x2 = embed[3*j]; 288 | var y1 = embed[3*i+1], y2 = embed[3*j+1]; 289 | var z1 = embed[3*i+2], z2 = embed[3*j+2]; 290 | var dx = x2 - x1, dy = y2 - y1, dz = z2 - z1; 291 | var d2 = dx*dx + dy*dy + dz*dz; 292 | var d = Math.sqrt(d2) + 0.001; 293 | if (D > d) { 294 | var K = 2*(d-D)/d; 295 | } else { 296 | var K = 2*(d-D)/d*25/D/D; 297 | } 298 | grad[3*i ] += K*dx; 299 | grad[3*i+1] += K*dy; 300 | grad[3*i+2] += K*dz; 301 | grad[3*j ] -= K*dx; 302 | grad[3*j+1] -= K*dy; 303 | grad[3*j+2] -= K*dz; 304 | } 305 | } else if (cost == "SNE_sym") { 306 | calc_qij_sym(); 307 | for (var i = 0; i < N; i++) 308 | for (var j = 0; j < N; j++) { 309 | if (i != j) { 310 | var x1 = embed[3*i ], x2 = embed[3*j]; 311 | var y1 = embed[3*i+1], y2 = embed[3*j+1]; 312 | var z1 = embed[3*i+2], z2 = embed[3*j+2]; 313 | var dx = x2 - x1, dy = y2 - y1, dz = z2 - z1; 314 | var K = 4*(pij[N*i+j] - qij[N*i+j] ); 315 | grad[3*i ] += K*dx; 316 | grad[3*i+1] += K*dy; 317 | grad[3*i+2] += K*dz; 318 | } 319 | } 320 | } else if (cost == "tSNE") { 321 | calc_qij_sym_t(); 322 | for (var i = 0; i < N; i++) 323 | for (var j = 0; j < N; j++) { 324 | if (i != j) { 325 | var x1 = embed[3*i ], x2 = embed[3*j]; 326 | var y1 = embed[3*i+1], y2 = embed[3*j+1]; 327 | var z1 = embed[3*i+2], z2 = embed[3*j+2]; 328 | var dx = x2 - x1, dy = y2 - y1, dz = z2 - z1; 329 | var d2 = dx*dx + dy*dy + dz*dz; 330 | var K = 4*(pij[N*i+j] - qij[N*i+j] )/(1+d2); 331 | grad[3*i ] += K*dx; 332 | grad[3*i+1] += K*dy; 333 | grad[3*i+2] += K*dz; 334 | } 335 | } 336 | } else if (cost == "graph") { 337 | for (var i = 0; i < N; i++) 338 | for (var j = 0; j < i; j++) { 339 | var x1 = embed[3*i ], x2 = embed[3*j]; 340 | var y1 = embed[3*i+1], y2 = embed[3*j+1]; 341 | var z1 = embed[3*i+2], z2 = embed[3*j+2]; 342 | var dx = x2 - x1, dy = y2 - y1, dz = z2 - z1; 343 | var d2 = dx*dx + dy*dy + dz*dz; 344 | var d = Math.sqrt(d2) + 0.001; 345 | var K = -1/d/d; 346 | grad[3*i ] += K*dx; 347 | grad[3*i+1] += K*dy; 348 | grad[3*i+2] += K*dz; 349 | grad[3*j ] -= K*dx; 350 | grad[3*j+1] -= K*dy; 351 | grad[3*j+2] -= K*dz; 352 | } 353 | for (var n in edges) { 354 | var i = edges[n][0], j = edges[n][1]; 355 | var x1 = embed[3*i ], x2 = embed[3*j]; 356 | var y1 = embed[3*i+1], y2 = embed[3*j+1]; 357 | var z1 = embed[3*i+2], z2 = embed[3*j+2]; 358 | var dx = x2 - x1, dy = y2 - y1, dz = z2 - z1; 359 | var d2 = dx*dx + dy*dy + dz*dz; 360 | var d = Math.sqrt(d2) + 0.001; 361 | var K = 10*(d-1); 362 | grad[3*i ] += K*dx; 363 | grad[3*i+1] += K*dy; 364 | grad[3*i+2] += K*dz; 365 | grad[3*j ] -= K*dx; 366 | grad[3*j+1] -= K*dy; 367 | grad[3*j+2] -= K*dz; 368 | } 369 | } else if (cost == "graph_ds") { 370 | for (var i = 0; i < N; i++) 371 | for (var j = 0; j < i; j++) { 372 | var x1 = embed[3*i ], x2 = embed[3*j]; 373 | var y1 = embed[3*i+1], y2 = embed[3*j+1]; 374 | var z1 = embed[3*i+2], z2 = embed[3*j+2]; 375 | var dx = x2 - x1, dy = y2 - y1, dz = z2 - z1; 376 | var d2 = dx*dx + dy*dy + dz*dz; 377 | var d = Math.sqrt(d2) + 0.001; 378 | var K = -1/d/d; 379 | grad[3*i ] += K*dx; 380 | grad[3*i+1] += K*dy; 381 | grad[3*i+2] += K*dz; 382 | grad[3*j ] -= K*dx; 383 | grad[3*j+1] -= K*dy; 384 | grad[3*j+2] -= K*dz; 385 | } 386 | for (var n in edges) { 387 | var i = edges[n][0], j = edges[n][1]; 388 | var D = ds_orig[i + N*j]; 389 | var x1 = embed[3*i ], x2 = embed[3*j]; 390 | var y1 = embed[3*i+1], y2 = embed[3*j+1]; 391 | var z1 = embed[3*i+2], z2 = embed[3*j+2]; 392 | var dx = x2 - x1, dy = y2 - y1, dz = z2 - z1; 393 | var d2 = dx*dx + dy*dy + dz*dz; 394 | var d = Math.sqrt(d2) + 0.001; 395 | var K = 10*(d-D*D/25)*D*D/25; 396 | grad[3*i ] += K*dx; 397 | grad[3*i+1] += K*dy; 398 | grad[3*i+2] += K*dz; 399 | grad[3*j ] -= K*dx; 400 | grad[3*j+1] -= K*dy; 401 | grad[3*j+2] -= K*dz; 402 | } 403 | } 404 | 405 | // Calculate gradient norm 406 | var grad_norm = 0.0; 407 | for (var i = 0; i < N; i++) { 408 | grad_norm += grad[3*i ]*grad[3*i ]; 409 | grad_norm += grad[3*i+1]*grad[3*i+1]; 410 | grad_norm += grad[3*i+2]*grad[3*i+2]; 411 | } 412 | grad_norm = Math.sqrt(grad_norm); 413 | 414 | // Update! 415 | for (var i = 0; i < N; i++) { 416 | var Kmu2 = (1-Kmu)/grad_norm; 417 | momentum[3*i] = Kmu*momentum[3*i] + Kmu2*grad[3*i]; 418 | momentum[3*i+1] = Kmu*momentum[3*i+1] + Kmu2*grad[3*i+1]; 419 | momentum[3*i+2] = Kmu*momentum[3*i+2] + Kmu2*grad[3*i+2]; 420 | } 421 | 422 | for (var i = 0; i < N; i++) { 423 | var K = 0.002*Math.sqrt(N/(0.1+grad_norm)); 424 | embed[3*i] += Kstep*momentum[3*i]; 425 | embed[3*i+1] += Kstep*momentum[3*i+1]; 426 | embed[3*i+2] += Kstep*momentum[3*i+2]; 427 | } 428 | 429 | } 430 | 431 | 432 | self.onmessage = function(e) { 433 | data = e.data 434 | switch (data.cmd) { 435 | case "init": 436 | 437 | xs = data.xs; 438 | N = data.N; 439 | D = data.D; 440 | 441 | cost = data.cost || "MDS"; 442 | 443 | ds_orig = new Float32Array(N*N); 444 | grad = new Float32Array(3*N); 445 | momentum = new Float32Array(3*N); 446 | embed = new Float32Array(3*N); 447 | 448 | for (var n = 0; n < embed.length; n++) { 449 | embed[n] = normal(); 450 | } 451 | 452 | calc_ds(); 453 | 454 | if (cost == "graph") { 455 | //knn_edges(3); 456 | //threshold_edges(5.0); 457 | knn_edges(3); 458 | } else if (cost == "graph_ds") { 459 | knn_edges(3); 460 | } else if (cost == "isomap") { 461 | knn_edges(3); 462 | ds_graph = new Float32Array(N*N); 463 | calc_ds_graph(); 464 | ds_orig = ds_graph; 465 | postMessage({msg: "info", ds_graph: ds_graph}) 466 | cost = "MDS"; 467 | } else if (cost == "SNE") { 468 | temp = new Float32Array(N); 469 | pij = new Float32Array(N*N); 470 | qij = new Float32Array(N*N); 471 | variances = new Float32Array(N); 472 | calc_perps(40); 473 | calc_pij(); 474 | } else if (cost == "SNE_sym") { 475 | temp = new Float32Array(N); 476 | pij = new Float32Array(N*N); 477 | qij = new Float32Array(N*N); 478 | variances = new Float32Array(N); 479 | calc_perps(data.perplexity || 20); 480 | calc_pij_sym(); 481 | } else if (cost == "tSNE") { 482 | temp = new Float32Array(N); 483 | pij = new Float32Array(N*N); 484 | qij = new Float32Array(N*N); 485 | variances = new Float32Array(N); 486 | calc_perps(40); 487 | calc_pij_sym(); 488 | } 489 | 490 | postMessage({ msg: "ready"}); 491 | postMessage({msg: "edges", edges: edges}) 492 | postMessage({ msg: "update", embed: embed}); 493 | break; 494 | 495 | case "reset": 496 | for (var n = 0; n < embed.length; n++) { 497 | embed[n] = 8*normal(); 498 | } 499 | postMessage({ msg: "update", embed: embed}); 500 | break; 501 | 502 | case "run": 503 | 504 | var steps = data.steps; 505 | var skip = data.skip || 1; 506 | Kstep = data.K_step || data.Kstep || data.K || 1.0; 507 | Kmu = data.K_mu || data.Kmu || 0.0; 508 | 509 | for (var n = 0; n < steps; n++) { 510 | if (n % skip == 0) { 511 | postMessage({ msg: "update", embed: embed}); 512 | } 513 | opt(cost); 514 | } 515 | 516 | postMessage({ msg: "update", embed: embed}); 517 | postMessage({ msg: "done"}); 518 | break; 519 | 520 | } 521 | } 522 | -------------------------------------------------------------------------------- /js/CostLayout-worker.js: -------------------------------------------------------------------------------- 1 | /*var i = 0; 2 | 3 | var data = new Float32Array(100); 4 | 5 | function timedCount() { 6 | i = i + 1; 7 | postMessage({ msg: "update", embed: data}); 8 | setTimeout("timedCount()",500); 9 | } 10 | 11 | timedCount();*/ 12 | 13 | 14 | var cost = null; 15 | var xs = null, N = null, D = null, Ne = null, Kstep = 1, Kmu = 1; 16 | var ds_orig = null; 17 | var ds_graph = null; 18 | var grad = null; 19 | var momentum = null; 20 | var variances = null; 21 | var embed = null; 22 | var edges = []; 23 | var edges_arr = null; 24 | var temp = null; 25 | var pij = null; 26 | var qij = null; 27 | var init_s = null; 28 | 29 | var normal = function() { 30 | var N = 15, x = 0; 31 | for (var n = 0; n < N; n++) { 32 | x += Math.random(); 33 | } 34 | x = 2*x - N; 35 | x = x/N; 36 | return x; 37 | }; 38 | 39 | function calc_ds() { 40 | for (var i = 0; i < N; i++) 41 | for (var j = 0; j < N; j++) { 42 | var sum = 0; 43 | for (var d = 0; d < D; d++){ 44 | var diff = xs[D*i + d] - xs[D*j + d]; 45 | sum += diff*diff; 46 | } 47 | ds_orig[i + N*j] = Math.sqrt(sum); 48 | } 49 | } 50 | 51 | function normalize(arr, N) { 52 | var sum = 0; 53 | for (var m = 0; m < N; m++) 54 | sum += arr[m]; 55 | if (sum > 0) { 56 | for (var m = 0; m < N; m++) 57 | arr[m] /= sum; 58 | } 59 | } 60 | 61 | function const_variance(k) { 62 | for (var n = 0; n < N; n++) { 63 | variances[n] = k; 64 | } 65 | } 66 | 67 | function calc_perps(target) { 68 | function test_perp(n, s) { 69 | for (var m = 0; m < N; m++) { 70 | var d = ds_orig[n+N*m]; 71 | temp[m] = Math.exp(-d*d/2/s/s); 72 | } 73 | temp[n] = 0; 74 | normalize(temp, N); 75 | var entropy = 0; 76 | for (var m = 0; m < N; m++) 77 | entropy += temp[m] * Math.log(temp[m]+0.000001) 78 | entropy *= -1/Math.log(2); 79 | return Math.pow(2, entropy); 80 | } 81 | for (var n = 0; n < N; n++) { 82 | var var0 = 0.1, perp0 = test_perp(0.1), 83 | var1 = 10.0, perp1 = test_perp(10.0); 84 | var mid; 85 | for (var k = 0; k < 10; k++) { 86 | mid = (var0 + var1)/2.0; 87 | var mid_perp = test_perp(n, mid); 88 | if (mid_perp < target){ 89 | var0 = mid; perp0 = mid_perp; 90 | } else { 91 | var1 = mid; perp1 = mid_perp; 92 | } 93 | } 94 | variances[n] = mid; 95 | } 96 | 97 | } 98 | 99 | function calc_pij() { 100 | for (var n = 0; n < N; n++) { 101 | var s = variances[n]; 102 | for (var m = 0; m < N; m++) { 103 | var d = ds_orig[n+N*m]; 104 | temp[m] = Math.exp(-d*d/2/s/s); 105 | } 106 | temp[n] = 0; 107 | normalize(temp, N); 108 | for (var m = 0; m < N; m++) { 109 | pij[N*n+m] = temp[m]; 110 | } 111 | } 112 | } 113 | 114 | function calc_pij_sym() { 115 | calc_pij(); 116 | for (var i = 0; i < N; i++) 117 | for (var j = 0; j < i; j++) { 118 | pij[N*i+j] += pij[N*j+i]; 119 | pij[N*i+j] /= 2*N; 120 | pij[N*j+i] = pij[N*i+j]; 121 | } 122 | /*for (var n = 0; n < N; n++) { 123 | var s = variances[n]; 124 | for (var m = 0; m < N; m++) { 125 | var d = ds_orig[n+N*m]; 126 | pij[N*n+m][m] = Math.exp(-d*d/2/s/s); 127 | } 128 | temp[n] = 0; 129 | } 130 | normalize(pij, N*N);*/ 131 | } 132 | 133 | function calc_qij() { 134 | for (var n = 0; n < N; n++) { 135 | for (var m = 0; m < N; m++) { 136 | var x1 = embed[2*n ], x2 = embed[2*m]; 137 | var y1 = embed[2*n+1], y2 = embed[2*m+1]; 138 | var dx = x2 - x1, dy = y2 - y1; 139 | var d2 = dx*dx + dy*dy; 140 | temp[m] = Math.exp(-d2/2); 141 | } 142 | temp[n] = 0; 143 | normalize(temp, N); 144 | for (var m = 0; m < N; m++) { 145 | qij[N*n+m] = temp[m]; 146 | } 147 | } 148 | } 149 | 150 | function calc_qij_sym() { 151 | for (var n = 0; n < N; n++) { 152 | for (var m = 0; m < N; m++) { 153 | var x1 = embed[2*n ], x2 = embed[2*m]; 154 | var y1 = embed[2*n+1], y2 = embed[2*m+1]; 155 | var dx = x2 - x1, dy = y2 - y1; 156 | var d2 = dx*dx + dy*dy; 157 | qij[N*n+m] = Math.exp(-d2/2); 158 | } 159 | qij[n] = 0; 160 | } 161 | normalize(qij, N*N); 162 | } 163 | 164 | function calc_qij_sym_t() { 165 | for (var n = 0; n < N; n++) { 166 | for (var m = 0; m < N; m++) { 167 | var x1 = embed[2*n ], x2 = embed[2*m]; 168 | var y1 = embed[2*n+1], y2 = embed[2*m+1]; 169 | var dx = x2 - x1, dy = y2 - y1; 170 | var d2 = dx*dx + dy*dy; 171 | qij[N*n+m] = 1.0/(1+d2); 172 | } 173 | qij[n] = 0; 174 | } 175 | normalize(qij, N*N); 176 | } 177 | 178 | 179 | function calc_ds_graph() { 180 | function is_edge(p) { 181 | for (var n = 0; n < edges.length; n++) { 182 | var edge = edges[n]; 183 | if (edge[0] == p[0] && edge[1] == p[1])// || edge[0] == p[1] && edge[1] == p[0]) 184 | return true; 185 | } 186 | return false; 187 | } 188 | for (var i = 0; i < N; i++) 189 | for (var j = 0; j < N; j++) { 190 | if ( is_edge([i,j]) || is_edge([j,i]) ) { 191 | ds_graph[i + N*j] = ds_orig[i + N*j]; 192 | } else { 193 | ds_graph[i + N*j] = 10000; 194 | } 195 | } 196 | for (var m = 0; m < N; m++) 197 | for (var i = 0; i < N; i++) 198 | for (var j = 0; j < N; j++) { 199 | var d_path = ds_graph[i + N*m] + ds_graph[m + N*j]; 200 | if (d_path < ds_graph[i + N*j]) { 201 | ds_graph[i + N*j] = d_path; 202 | } 203 | } 204 | 205 | } 206 | 207 | function threshold_edges(thresh){ 208 | edges = []; 209 | for (var i = 0; i < N; i++) 210 | for (var j = 0; j < i; j++) { 211 | if (ds_orig[i + N*j] < thresh) { 212 | edges.push([i,j]); 213 | } 214 | } 215 | } 216 | 217 | function knn_edges(K){ 218 | edges = []; 219 | for (var i = 0; i < N; i++) { 220 | var knn = []; 221 | // stupid, dumb, easy hack for testing: 222 | for (var k = 0; k < K; k++) { 223 | var x = null, xd = 100000; 224 | for (var j = 0; j < N; j++) { 225 | var D = ds_orig[i + N*j]; 226 | if ( i!=j && knn.indexOf(j) == -1 && D < xd) { 227 | x = j; 228 | xd = D; 229 | } 230 | } 231 | knn.push(x); 232 | edges.push([i, x]); 233 | } 234 | } 235 | } 236 | 237 | function make_edges_arr(thresh){ 238 | edges, edges_arr; 239 | edges_arr = new Uint32Array(2*edges.length); 240 | for (var n in edges) { 241 | edges_arr[2*n] = edges[n][0]; 242 | edges_arr[2*n+1] = edges[n][1]; 243 | } 244 | } 245 | 246 | function opt(cost){ 247 | 248 | // Set Gradient to zero 249 | for (var i = 0; i < N; i++) { 250 | grad[2*i] = 0; 251 | grad[2*i+1] = 0; 252 | } 253 | 254 | // Calculate new gradient 255 | if (cost == "MDS") { 256 | for (var i = 0; i < N; i++) 257 | for (var j = 0; j < i; j++) { 258 | var D = ds_orig[i + N*j]; 259 | var x1 = embed[2*i ], x2 = embed[2*j]; 260 | var y1 = embed[2*i+1], y2 = embed[2*j+1]; 261 | var dx = x2 - x1, dy = y2 - y1; 262 | var d2 = dx*dx + dy*dy; 263 | var d = Math.sqrt(d2) + 0.001; 264 | var K = 2*(d-D)/d; 265 | grad[2*i] += K*dx; 266 | grad[2*i+1] += K*dy; 267 | grad[2*j] -= K*dx; 268 | grad[2*j+1] -= K*dy; 269 | } 270 | } else if (cost == "sammon") { 271 | for (var i = 0; i < N; i++) 272 | for (var j = 0; j < i; j++) { 273 | var D = ds_orig[i + N*j]; 274 | var x1 = embed[2*i ], x2 = embed[2*j]; 275 | var y1 = embed[2*i+1], y2 = embed[2*j+1]; 276 | var dx = x2 - x1, dy = y2 - y1; 277 | var d2 = dx*dx + dy*dy; 278 | var d = Math.sqrt(d2) + 0.001; 279 | var K = 2*(d-D)/d/D; 280 | grad[2*i] += K*dx; 281 | grad[2*i+1] += K*dy; 282 | grad[2*j] -= K*dx; 283 | grad[2*j+1] -= K*dy; 284 | } 285 | } else if (cost == "graph") { 286 | for (var i = 0; i < N; i++) 287 | for (var j = 0; j < i; j++) { 288 | var x1 = embed[2*i ], x2 = embed[2*j]; 289 | var y1 = embed[2*i+1], y2 = embed[2*j+1]; 290 | var dx = x2 - x1, dy = y2 - y1; 291 | var d2 = dx*dx + dy*dy; 292 | var d = Math.sqrt(d2) + 0.001; 293 | var K = -1/d/d; 294 | grad[2*i] += K*dx; 295 | grad[2*i+1] += K*dy; 296 | grad[2*j] -= K*dx; 297 | grad[2*j+1] -= K*dy; 298 | } 299 | for (var n = 0; n < Ne; n++) { 300 | var i = edges_arr[2*n], j = edges_arr[2*n+1]; 301 | var x1 = embed[2*i ], x2 = embed[2*j]; 302 | var y1 = embed[2*i+1], y2 = embed[2*j+1]; 303 | var dx = x2 - x1, dy = y2 - y1; 304 | var d2 = dx*dx + dy*dy; 305 | var d = Math.sqrt(d2) + 0.001; 306 | var K = 10*(d-1); 307 | grad[2*i] += K*dx; 308 | grad[2*i+1] += K*dy; 309 | grad[2*j] -= K*dx; 310 | grad[2*j+1] -= K*dy; 311 | } 312 | } else if (cost == "SNE") { 313 | calc_qij(); 314 | for (var i = 0; i < N; i++) 315 | for (var j = 0; j < N; j++) { 316 | if (i != j) { 317 | var x1 = embed[2*i ], x2 = embed[2*j]; 318 | var y1 = embed[2*i+1], y2 = embed[2*j+1]; 319 | var dx = x2 - x1, dy = y2 - y1; 320 | var K = 2*(pij[N*i+j] - qij[N*i+j] 321 | + pij[N*j+i] - qij[N*j+i] ); 322 | grad[2*i] += K*dx; 323 | grad[2*i+1] += K*dy; 324 | } 325 | } 326 | } else if (cost == "SNE_sym") { 327 | calc_qij_sym(); 328 | for (var i = 0; i < N; i++) 329 | for (var j = 0; j < N; j++) { 330 | if (i != j) { 331 | var x1 = embed[2*i ], x2 = embed[2*j]; 332 | var y1 = embed[2*i+1], y2 = embed[2*j+1]; 333 | var dx = x2 - x1, dy = y2 - y1; 334 | var K = 4*(pij[N*i+j] - qij[N*i+j] ); 335 | grad[2*i] += K*dx; 336 | grad[2*i+1] += K*dy; 337 | } 338 | } 339 | } else if (cost == "tSNE") { 340 | calc_qij_sym_t(); 341 | for (var i = 0; i < N; i++) 342 | for (var j = 0; j < N; j++) { 343 | if (i != j) { 344 | var x1 = embed[2*i ], x2 = embed[2*j]; 345 | var y1 = embed[2*i+1], y2 = embed[2*j+1]; 346 | var dx = x2 - x1, dy = y2 - y1; 347 | var d2 = dx*dx + dy*dy; 348 | var K = 4*(pij[N*i+j] - qij[N*i+j] )/(1+d2); 349 | grad[2*i] += K*dx; 350 | grad[2*i+1] += K*dy; 351 | } 352 | } 353 | } else if (cost == "custom") { 354 | for (var i = 0; i < N; i++) 355 | for (var j = 0; j < i; j++) { 356 | var D = ds_orig[i + N*j]; 357 | var x1 = embed[2*i ], x2 = embed[2*j]; 358 | var y1 = embed[2*i+1], y2 = embed[2*j+1]; 359 | var dx = x2 - x1, dy = y2 - y1; 360 | var d2 = dx*dx + dy*dy; 361 | var d = Math.sqrt(d2) + 0.001; 362 | var Dm = Math.min(D,d); 363 | var K = 2*(d-D)/d/(Dm+0.01);///(D+0.01)/(D+0.01); 364 | grad[2*i] += K*dx; 365 | grad[2*i+1] += K*dy; 366 | grad[2*j] -= K*dx; 367 | grad[2*j+1] -= K*dy; 368 | } 369 | } 370 | 371 | // Calculate gradient norm 372 | var grad_norm = 0.0; 373 | for (var i = 0; i < N; i++) { 374 | grad_norm += grad[2*i ]*grad[2*i ]; 375 | grad_norm += grad[2*i+1]*grad[2*i+1]; 376 | } 377 | grad_norm = Math.sqrt(grad_norm); 378 | 379 | // Update! 380 | for (var i = 0; i < N; i++) { 381 | var Kmu2 = (1-Kmu)/grad_norm; 382 | momentum[2*i] = Kmu*momentum[2*i] + Kmu2*grad[2*i]; 383 | momentum[2*i+1] = Kmu*momentum[2*i+1] + Kmu2*grad[2*i+1]; 384 | } 385 | 386 | for (var i = 0; i < N; i++) { 387 | embed[2*i] += Kstep*momentum[2*i]; 388 | embed[2*i+1] += Kstep*momentum[2*i+1]; 389 | } 390 | 391 | } 392 | 393 | 394 | function add_noise(k) { 395 | for (var i = 0; i < N; i++) { 396 | embed[2*i] += k*normal(); 397 | embed[2*i+1] += k*normal(); 398 | } 399 | 400 | } 401 | 402 | 403 | self.onmessage = function(e) { 404 | data = e.data 405 | switch (data.cmd) { 406 | case "init": 407 | 408 | N = data.N; 409 | 410 | if (data.xs) { 411 | xs = data.xs; 412 | D = data.D; 413 | ds_orig = new Float32Array(N*N); 414 | calc_ds(); 415 | } else if (data.ds) { 416 | ds_orig = data.ds; 417 | } 418 | 419 | cost = data.cost || "MDS"; 420 | 421 | grad = new Float32Array(2*N); 422 | momentum = new Float32Array(2*N); 423 | 424 | embed = new Float32Array(2*N); 425 | init_s = data.init_s || 8; 426 | for (var n = 0; n < embed.length; n++) { 427 | embed[n] = init_s*normal(); 428 | } 429 | 430 | if (cost == "graph") { 431 | knn_edges(3); 432 | } else if (cost == "isomap") { 433 | knn_edges(3); 434 | ds_graph = new Float32Array(N*N); 435 | calc_ds_graph(); 436 | ds_orig = ds_graph; 437 | postMessage({msg: "info", ds_graph: ds_graph}) 438 | cost = "MDS"; 439 | } else if (cost == "SNE") { 440 | temp = new Float32Array(N); 441 | pij = new Float32Array(N*N); 442 | qij = new Float32Array(N*N); 443 | variances = new Float32Array(N); 444 | calc_perps(data.perplexity || 20); 445 | calc_pij(); 446 | } else if (cost == "SNE_sym") { 447 | temp = new Float32Array(N); 448 | pij = new Float32Array(N*N); 449 | qij = new Float32Array(N*N); 450 | variances = new Float32Array(N); 451 | calc_perps(data.perplexity || 20); 452 | calc_pij_sym(); 453 | } else if (cost == "tSNE") { 454 | temp = new Float32Array(N); 455 | pij = new Float32Array(N*N); 456 | qij = new Float32Array(N*N); 457 | variances = new Float32Array(N); 458 | calc_perps(data.perplexity || 20); 459 | calc_pij_sym(); 460 | } 461 | 462 | Ne = edges.length; 463 | make_edges_arr(); 464 | 465 | postMessage({ msg: "ready"}); 466 | postMessage({msg: "edges", edges: edges_arr}) 467 | postMessage({ msg: "update", embed: embed}); 468 | break; 469 | 470 | case "reset": 471 | for (var n = 0; n < embed.length; n++) { 472 | embed[n] = 8*normal(); 473 | } 474 | postMessage({ msg: "update", embed: embed}); 475 | break; 476 | 477 | case "run": 478 | 479 | var steps = data.steps; 480 | var skip = data.skip || 1; 481 | Kstep = data.K_step || data.Kstep || data.K || 1.0; 482 | Kmu = data.K_mu || data.Kmu || 0.0; 483 | 484 | var decay = 1.0; 485 | for (var n = 0; n < steps; n++) { 486 | if (n % skip == 0) { 487 | postMessage({ msg: "update", embed: embed}); 488 | } 489 | opt(cost); 490 | //add_noise(1.0*decay); 491 | decay *= 0.995; 492 | } 493 | 494 | postMessage({ msg: "update", embed: embed}); 495 | postMessage({ msg: "done"}); 496 | break; 497 | 498 | } 499 | } 500 | -------------------------------------------------------------------------------- /js/MnistVis.js: -------------------------------------------------------------------------------- 1 | var ImgPixelSelector = function(s) { 2 | shape = [28,28]; 3 | this.s = s; 4 | this.overlap = new BasicVis.Overlap(s); 5 | this.img_display = this.overlap.new_child(BasicVis.ImgDisplay) 6 | .imgs(mnist_xs) 7 | .shape(shape) 8 | .show(6); 9 | this.pixel_selector = this.overlap.new_child(BasicVis.MatrixSelector) 10 | .shape(shape); 11 | this.layout = function() {this.overlap.layout();} 12 | this.update = function() {this.overlap.update();} 13 | } 14 | 15 | ImgPixelSelector.prototype = new BasicVis.VisElement(); 16 | 17 | 18 | var BasisPlotMNIST = function(s) { 19 | shape = [28,28]; 20 | this.s = s; 21 | var b0 = null, b1 = null; 22 | this.scatter = new BasicVis.ScatterPlot(s) 23 | .N(500) 24 | .xrange([0,1]) 25 | .yrange([0,1]) 26 | .size(4) 27 | .color(function(i){return d3.hsl(360*mnist_ys[i]/10.0,0.5,0.5);}); 28 | 29 | this.scatter.svg.style('border', '1px solid #000000'); 30 | 31 | proj = function(i, v){ 32 | var offset = v[0] + 28*(28-v[1]); 33 | var s = mnist_xs[784*i + offset]; 34 | var k = 10 + (3*v[0] + 5*v[1]) % 11; 35 | if (s == 0) s += 0.02*(i%k)/k+0.01; 36 | if (s == 1) s -= 0.02*(i%k)/k+0.01; 37 | return s; 38 | } 39 | 40 | proj2 = function(n, v){ 41 | var s = 0; 42 | for (var i = 0; i < 784; i++) { 43 | s += v[i]*mnist_xs[784*n + i]; 44 | } 45 | return s; 46 | } 47 | 48 | this.b0 = function(v) { 49 | if (!arguments.length) return b0; 50 | b0 = v; 51 | if (v.length == 2) { 52 | this.scatter 53 | .x(function (i) {return proj(i,v);}); 54 | } else if (v.length == 784) { 55 | this.scatter 56 | .x(function (i) {return proj2(i,v);}); 57 | } 58 | return this; 59 | } 60 | 61 | this.b1 = function(v) { 62 | if (!arguments.length) return b1; 63 | b1 = v; 64 | if (v.length == 2) { 65 | this.scatter 66 | .y(function (i) {return 1-proj(i,v);}); 67 | } else if (v.length == 784) { 68 | this.scatter 69 | .y(function (i) {return 1-proj2(i,v);}); 70 | } 71 | return this; 72 | } 73 | 74 | this.layout = function() {this.scatter.layout();} 75 | this.render = function() {this.scatter.render();} 76 | 77 | } 78 | 79 | BasisPlotMNIST.prototype = new BasicVis.VisElement(); 80 | 81 | //=============================================== 82 | 83 | var RawExploreMNIST = function RawExploreMNIST(s) { 84 | 85 | BasicVis.Container.call(this, s); 86 | 87 | this.plot = this.new_child( BasisPlotMNIST ); 88 | this.x = this.new_child( ImgPixelSelector ); 89 | this.y = this.new_child( ImgPixelSelector ); 90 | 91 | this.axxDiv = this.inner.append('div'); 92 | this.axyDiv = this.inner.append('div'); 93 | 94 | try { 95 | // Fails if MathJax isn't loaded. 96 | this.eqx = this.new_child( BasicVis.Equation ); 97 | this.eqy = this.new_child( BasicVis.Equation ); 98 | } 99 | catch(err) { 100 | // Not much we can do to recover; let's just try to avoid breaking the rest of the page 101 | // by continuing rendering. 102 | } 103 | 104 | var this_ = this; 105 | 106 | this.plot.scatter.mouseover(function(i) { this_.x.img_display.show(i); this_.y.img_display.show(i);}); 107 | 108 | platex = function(v) { return 'p_{' + v[0] + ',' + (v[1]-1) +'}';}; 109 | this.x.pixel_selector.value.change = function(v) {this_.plot.b0(v); this_.eqx.latex(platex(v)); }; 110 | this.y.pixel_selector.value.change = function(v) {this_.plot.b1(v); this_.eqy.latex(platex(v)); }; 111 | 112 | }; 113 | 114 | RawExploreMNIST.prototype = Object.create(BasicVis.Container.prototype); 115 | 116 | RawExploreMNIST.prototype.child_layout = function child_layout() { 117 | 118 | 119 | W = parseInt(this.s.style('width')); 120 | 121 | var plot = W/2; 122 | var side = W/4; 123 | var pick = 2/3*side; 124 | var gutter = (side-pick)/2; 125 | 126 | this.inner 127 | .style('width', W) 128 | .style('height', plot+side); 129 | 130 | this.plot.div 131 | .pos([side, 0]) 132 | .size([plot, plot]); 133 | 134 | this.y.div 135 | .pos([gutter, gutter]) 136 | .size([pick, pick]); 137 | 138 | this.x.div 139 | .pos([plot + gutter, plot + gutter]) 140 | .size([pick, pick]); 141 | 142 | this.axyDiv 143 | .attr('style', 'background: -moz-linear-gradient(right, white, black); background: linear-gradient(to right, white , black);') 144 | .style('border', '1px solid #000000') 145 | .style('position', 'absolute') 146 | .style('top', plot+1) 147 | .style('left', side) 148 | .style('width', plot) 149 | .style('height', gutter/3); 150 | 151 | this.axxDiv 152 | .attr('style', 'background: -moz-linear-gradient(top, white, black); background: linear-gradient(to top, white , black);') 153 | .style('border', '1px solid #000000') 154 | .style('position', 'absolute') 155 | .style('top', 0) 156 | .style('left', side - gutter/3 -1) 157 | .style('width', gutter/3) 158 | .style('height', plot); 159 | 160 | this.eqy.div 161 | .style('top', plot - W/4/3 ) 162 | .style('left', side - W/4/3 - 20 ); 163 | 164 | this.eqx.div 165 | .style('top', plot + W/4/3 - 10) 166 | .style('left', side + W/4/3); 167 | 168 | return this; 169 | } 170 | 171 | 172 | var PlotReducedMNIST = function PlotReducedMNIST(s) { 173 | 174 | BasicVis.Container.call(this, s); 175 | 176 | var shape = [28, 28]; 177 | 178 | this.scatter = this.new_child(BasicVis.ScatterPlot) 179 | .N(500) 180 | .xrange.fit(mnist_sne) 181 | .yrange.fit(mnist_sne) 182 | .x(function(i) {return mnist_sne[2*i + 20*500];}) 183 | .y(function(i) {return mnist_sne[2*i+1 + 20*500];}) 184 | .size(4) 185 | .color(function(i){return d3.hsl(360*mnist_ys[i]/10.0,0.5,0.5);}); 186 | this.img_display = this.new_child(BasicVis.ImgDisplay) 187 | .imgs(mnist_xs) 188 | .shape(shape); 189 | 190 | var this_ = this; 191 | 192 | this.scatter.mouseover(function(i) { this_.img_display.show(i); }); 193 | 194 | }; 195 | 196 | PlotReducedMNIST.prototype = Object.create(BasicVis.Container.prototype); 197 | 198 | PlotReducedMNIST.prototype.child_layout = function child_layout() { 199 | 200 | W = parseInt(this.s.style('width')); 201 | 202 | var plot = W/2; 203 | var side = W/4; 204 | var display = 3/4*side; 205 | var gutter = (side - display)/2; 206 | 207 | this.inner 208 | .style('width', W) 209 | .style('height', plot); 210 | 211 | this.scatter.div 212 | .pos([side, 0]) 213 | .size([plot, plot]); 214 | 215 | this.img_display.div 216 | .pos([side+plot+gutter, 0]) 217 | .size([display, display]); 218 | 219 | return this; 220 | } 221 | 222 | 223 | // RepresentationSpacePlotMNIST 224 | //=========================== 225 | 226 | var RepresentationSpacePlotMNIST = function RepresentationSpacePlotMNIST(s) { 227 | BasicVis.Container.call(this, s); 228 | var shape = [28,28]; 229 | 230 | this.info = this.inner.append("div"); 231 | 232 | this.rep_plot = this.new_child(BasicVis.ScatterPlot) 233 | .N(mnist_reps_rep.names.length) 234 | .xrange.fit(mnist_reps_rep.pca) 235 | .yrange.fit(mnist_reps_rep.pca) 236 | .x(function(i) {return mnist_reps_rep.pca[2*i ];}) 237 | .y(function(i) {return mnist_reps_rep.pca[2*i+1];}) 238 | .color(function(i){ 239 | var name = mnist_reps_rep.names[i]; 240 | var n = 200, k = -1; 241 | if (name.indexOf("_net") > -1) {k = 0;} 242 | if (name.indexOf("_netR") > -1) {k = 1;} 243 | if (name.indexOf("_convnet") > -1) {k = 2;} 244 | if (name.indexOf("_netR200-") > -1) {k = 3;} 245 | if (name.indexOf("_netR40-") > -1) {k = 3;} 246 | 247 | if (name.indexOf("_net1_") > -1) {n = 1; } 248 | if (name.indexOf("_net2_") > -1) {n = 2; } 249 | if (name.indexOf("_net5_") > -1) {n = 5; } 250 | if (name.indexOf("_net20_") > -1) {n = 20; } 251 | if (name.indexOf("_net100_") > -1) {n = 100;} 252 | 253 | if (name.indexOf("_netR1_") > -1) {n = 1; } 254 | if (name.indexOf("_netR2_") > -1) {n = 2; } 255 | if (name.indexOf("_netR5_") > -1) {n = 5; } 256 | if (name.indexOf("_netR20_") > -1) {n = 20; } 257 | if (name.indexOf("_netR100_") > -1) {n = 100;} 258 | 259 | return d3.hsl(360*(k/5.0),0.4+0.7*Math.log(n-0.9)/Math.log(200),0.5); 260 | //return d3.hsl(360*(k/5.0 + 0.3*Math.pow(n+1,0.2)/Math.pow(200,0.2)),0.5,0.5); 261 | }); 262 | 263 | this.rep_display = this.new_child(BasicVis.ScatterPlot) 264 | .N(0) 265 | .color(function(i){return d3.hsl(360*mnist_ys[i]/10.0,0.5,0.5);}); 266 | 267 | var rep_display = this.rep_display; 268 | 269 | this.rep_display.show = function show(name) { 270 | var rep = mnist_reps[name]; 271 | rep_display 272 | .N(500) 273 | .xrange.fit(rep) 274 | .yrange.fit(rep) 275 | .x(function(i) {return rep[2*i ];}) 276 | .y(function(i) {return rep[2*i+1];}); 277 | } 278 | 279 | this.img_display = this.new_child(BasicVis.ImgDisplay) 280 | .imgs(mnist_xs) 281 | .shape(shape); 282 | 283 | var this_ = this; 284 | 285 | this.rep_plot.mouseover(function(i) { 286 | var name = mnist_reps_rep.names[i]; 287 | 288 | var human_name = function(name){ 289 | var n = -1, np = -1, k = -1, maxpool = false; 290 | 291 | if (name.indexOf("_net") > -1) {k = 0;} 292 | if (name.indexOf("_netR") > -1) {k = 1;} 293 | if (name.indexOf("_conv") > -1) {k = 2;} 294 | if (name.indexOf("_netR200-") > -1) {k = 3;} 295 | if (name.indexOf("_netR40-") > -1) {k = 3;} 296 | if (name.indexOf("_netR10-") > -1) {k = 3;} 297 | 298 | if (name.indexOf("200-100") > -1) {np = 200; n = 100;} 299 | if (name.indexOf("40-20") > -1) {np = 40; n = 20;} 300 | if (name.indexOf("10-5") > -1) {np = 10; n = 5;} 301 | 302 | 303 | if (name.indexOf("max2") > -1) {maxpool = true;} 304 | 305 | if (name.indexOf("net100") > -1) {n = 100;} 306 | else if (name.indexOf("net200") > -1) {n = 200;} 307 | else if (name.indexOf("net10") > -1) {n = 10; } 308 | else if (name.indexOf("net20") > -1) {n = 20; } 309 | else if (name.indexOf("net1") > -1) {n = 1; } 310 | else if (name.indexOf("net2") > -1) {n = 2; } 311 | else if (name.indexOf("net5") > -1) {n = 5; } 312 | else if (name.indexOf("net8") > -1) {n = 8; } 313 | 314 | 315 | if (name.indexOf("_netR1_") > -1) {n = 1; } 316 | if (name.indexOf("_netR2_") > -1) {n = 2; } 317 | if (name.indexOf("_netR5_") > -1) {n = 5; } 318 | if (name.indexOf("_netR10_") > -1) {n = 10; } 319 | if (name.indexOf("_netR20_") > -1) {n = 20; } 320 | if (name.indexOf("_netR100_") > -1) {n = 100;} 321 | if (name.indexOf("_netR200_") > -1) {n = 200;} 322 | 323 | if (n > -1 && k == 0) 324 | return "Sigmoid Layer (" + n + " units)"; 325 | if (n > -1 && k == 1) 326 | return "ReLU Layer (" + n + " units)"; 327 | if (n > -1 && k == 2 && maxpool) 328 | return "Conv Layer ("+ n +" features; 5x5 patch; max 2x2)"; 329 | if (n > -1 && k == 2) 330 | return "Conv Layer ("+ n +" features; 5x5 patch)"; 331 | if (n > -1 && k == 3) 332 | return "Two ReLU Layers
(" + np + " units; " + n + " units)"; 333 | if (name == "mnist_raw") 334 | return "Raw MNIST"; 335 | if (name.indexOf("netR[40, 20]") > -1) 336 | return "Two ReLU Layers
(40 units; 20 units)"; 337 | if (n == -1 || k > 1) 338 | return name; 339 | } 340 | 341 | this_.info.html("
"+ human_name(name) + "
"); 342 | this_.rep_display.show(name); 343 | }); 344 | this.rep_display.mouseover(function(i) { this_.img_display.show(i); }); 345 | 346 | } 347 | 348 | RepresentationSpacePlotMNIST.prototype = Object.create(BasicVis.Container.prototype); 349 | 350 | RepresentationSpacePlotMNIST.prototype.child_layout = function child_layout() { 351 | 352 | W = parseInt(this.s.style('width')); 353 | 354 | var gutter = W/20; 355 | var main = W - 4*gutter; 356 | var R = 2/3; 357 | var K = 1/(R+R*R+R*R*R); 358 | var rep_plot = K*main*R; 359 | var rep_display = K*main*R*R; 360 | var img_display = K*main*R*R*R; 361 | 362 | this.inner 363 | .style('width', W) 364 | .style('height', rep_plot); 365 | 366 | this.rep_plot.size(W/250);//.size(W/150); 367 | this.rep_plot.div 368 | .style('border', '1px solid #000000') 369 | .pos([gutter, 0]) 370 | .size([rep_plot, rep_plot]); 371 | 372 | this.rep_display.size(W/300); 373 | this.rep_display.div 374 | .style('border', '1px solid #000000') 375 | .pos([rep_plot+2*gutter, (rep_plot-rep_display)/2]) 376 | .size([rep_display, rep_display]); 377 | 378 | this.img_display.div 379 | .style('border', '1px solid #000000') 380 | .pos([rep_plot+rep_display+3*gutter, (rep_plot - img_display)/2]) 381 | .size([img_display, img_display]); 382 | 383 | this.info 384 | .style('position', 'absolute') 385 | .style('font-size', '80%') 386 | .style('width', rep_display) 387 | .style('height', (rep_plot-rep_display)/3) 388 | .style('left', rep_plot+2*gutter) 389 | .style('top', (rep_plot-rep_display)*2/3 + rep_display); 390 | 391 | return this; 392 | 393 | } 394 | 395 | 396 | 397 | 398 | // ======================================== 399 | 400 | var PlotDataMNIST = function PlotDataMNIST(s) { 401 | 402 | BasicVis.Container.call(this, s); 403 | 404 | var shape = [28, 28]; 405 | var W = parseInt(this.s.style('width')); 406 | 407 | this.scatter = this.new_child(BasicVis.ScatterPlot); 408 | this.scatter.div.size([W*(1-3/20)*2/3, W*(1-3/20)*2/3]); 409 | this.scatter 410 | .N(0) 411 | .size(4) 412 | .color(function(i){return d3.hsl(360*mnist_ys[i]/10.0,0.5,0.5);}); 413 | 414 | this.select = this.inner.append("select"); 415 | 416 | var this_ = this; 417 | 418 | this.select.on("change", function() { 419 | this_.display(this.value); 420 | }); 421 | 422 | }; 423 | 424 | PlotDataMNIST.prototype = Object.create(BasicVis.Container.prototype); 425 | 426 | PlotDataMNIST.prototype.data = function data(val) { 427 | this._data = val; 428 | this.select.html(""); 429 | for (var key in this._data) { 430 | var inp = this.select 431 | .append("option") 432 | .attr("value", key) 433 | .text(key); 434 | } 435 | var key0 = Object.keys(val)[10]; 436 | this.display(key0); 437 | var this_=this; 438 | } 439 | 440 | PlotDataMNIST.prototype.display = function display(i) { 441 | this.sne = this._data[i]; 442 | var this_ = this; 443 | console.log(this_.sne[0], this_.sne.length); 444 | console.log(this); 445 | var a = 1.05*d3.min(this_.sne), b = 1.05*d3.max(this_.sne); 446 | console.log(a,b); 447 | this.scatter 448 | //.N(sne.length/2) 449 | .N(2000) 450 | .xrange([a,b]) 451 | .yrange([a,b]) 452 | .x(function(i) {return this_.sne[2*i ];}) 453 | .y(function(i) {return this_.sne[2*i+1];}); 454 | this.scatter.update(); 455 | //this.scheduleUpdate(); 456 | } 457 | 458 | PlotDataMNIST.prototype.child_layout = function child_layout() { 459 | 460 | W = parseInt(this.s.style('width')); 461 | 462 | var margin = W/20; 463 | var main = W - 3*margin; 464 | var plot = 2/3*main; 465 | var side = 1/3*main; 466 | 467 | this.inner 468 | .style('width', W) 469 | .style('height', plot); 470 | 471 | this.scatter.div 472 | .style("border", "1px solid black") 473 | .pos([side + 2*margin, 0]) 474 | .size([plot, plot]); 475 | 476 | this.select 477 | .style("position", "absolute") 478 | .style("width", side) 479 | .style("left", margin) 480 | .style("top", 0) 481 | .style("height", plot) 482 | .attr("multiple", "multiple"); 483 | 484 | return this; 485 | } 486 | 487 | 488 | 489 | //MatrixDisplay 490 | //================================================================ 491 | 492 | 493 | MatrixDisplay = function MatrixDisplay(s) { 494 | this.s = this.make_selector(s); 495 | this.svg = this.s.append('svg'); 496 | this._data = {}; 497 | this._data.shape = null; 498 | this._data.pixels = null; 499 | this.pixel_values = [[]]; 500 | }; 501 | 502 | MatrixDisplay.prototype = new BasicVis.VisElement(); 503 | 504 | MatrixDisplay.prototype.layout = function layout() { 505 | var W = parseInt(this.s.style('width')); 506 | this.svg 507 | .style('border', '1px solid #000000') 508 | .style('width', 1.0 * W) 509 | .style('height', 1.0 * W); 510 | return this; 511 | }; 512 | 513 | MatrixDisplay.prototype.render = function() { 514 | 515 | var pixels = this._data.pixels; 516 | var shape = this._data.shape; 517 | if (!shape) throw Error('ImgDisplay.render():' + 518 | ' Set shape first with ImgDisplay.shape()'); 519 | var pixel_values = this.pixel_values; 520 | var this_ = this; 521 | 522 | var selection = this.svg.selectAll('rect') 523 | .data(pixels); 524 | 525 | var W = parseInt(this.s.style('width')); 526 | var H = parseInt(this.s.style('height')); 527 | 528 | // create new rects on svg 529 | selection.enter().append('rect'); 530 | // remove old ones from svg 531 | selection.exit().remove(); 532 | // update/reset rects properties 533 | selection 534 | .attr('width', W / shape[0]) 535 | .attr('height', H / shape[1]) 536 | .attr('x', function(d, i) { return W * d[0] / shape[0]; }) 537 | .attr('y', function(d, i) { return H * (1 - d[1] / shape[1]); }) 538 | .style("opacity", function(d,i) { var v = pixel_values[d[0]][d[1]-1]; return 0.2+0.5*v*v;}) 539 | .style("fill", function(d, i) { var v = pixel_values[d[0]][d[1]-1]; return d3.rgb(Math.max(70, 100 - 150*v) ,70, Math.max(70, 100 + 150*v))}); 540 | return this; 541 | 542 | }; 543 | 544 | MatrixDisplay.prototype.shape = function(val) { 545 | if (!arguments.length) return this._data.shape; 546 | if (!val[0] || !val[1]) 547 | throw Error('shape(): shape must be an array of length 2 or 3.' + 548 | ' For example, [28, 28] or [32, 32, 3]'); 549 | this._data.shape = val; 550 | this._data.pixels = []; 551 | this.pixel_values = []; 552 | for (var i = 0; i < val[0]; i++) { 553 | var temp = []; 554 | for (var j = 0; j < val[1] + 1; j++) { 555 | this._data.pixels.push([i, j]); 556 | temp.push(0.0); 557 | } 558 | this.pixel_values.push(temp); 559 | } 560 | return this; 561 | }; 562 | 563 | // ======== 564 | 565 | var ImgPixelDisplay = function(s) { 566 | shape = [28,28]; 567 | this.s = s; 568 | this.overlap = new BasicVis.Overlap(s); 569 | this.img_display = this.overlap.new_child(BasicVis.ImgDisplay) 570 | .imgs(mnist_xs) 571 | .shape(shape) 572 | .show(6); 573 | this.pixel_display = this.overlap.new_child(MatrixDisplay) 574 | .shape(shape); 575 | this.layout = function() {this.overlap.layout();} 576 | this.update = function() {this.overlap.update();} 577 | } 578 | 579 | ImgPixelDisplay.prototype = new BasicVis.VisElement(); 580 | 581 | 582 | 583 | // =================== 584 | 585 | 586 | var DirExploreMNIST = function DirExploreMNIST(s) { 587 | 588 | BasicVis.Container.call(this, s); 589 | 590 | this.plot = this.new_child( BasisPlotMNIST ); 591 | this.x = this.new_child( ImgPixelDisplay ); 592 | this.y = this.new_child( ImgPixelDisplay ); 593 | 594 | this.axxDiv = this.inner.append('div'); 595 | this.axyDiv = this.inner.append('div'); 596 | 597 | var this_ = this; 598 | 599 | this.plot.scatter.mouseover(function(i) { this_.x.img_display.show(i); this_.y.img_display.show(i);}); 600 | 601 | }; 602 | 603 | DirExploreMNIST.prototype = Object.create(BasicVis.Container.prototype); 604 | 605 | DirExploreMNIST.prototype.child_layout = function child_layout() { 606 | 607 | W = parseInt(this.s.style('width')); 608 | 609 | var plot = W/2; 610 | var side = W/4; 611 | var pick = 2/3*side; 612 | var gutter = (side-pick)/2; 613 | 614 | this.inner 615 | .style('width', W) 616 | .style('height', plot+side); 617 | 618 | this.plot.div 619 | .pos([side, 0]) 620 | .size([plot, plot]); 621 | 622 | this.y.div 623 | .pos([gutter, gutter]) 624 | .size([pick, pick]); 625 | 626 | this.x.div 627 | .pos([plot + gutter, plot + gutter]) 628 | .size([pick, pick]); 629 | 630 | this.axyDiv 631 | .attr('style', 'background: -moz-linear-gradient(right, red, gray, blue); background: linear-gradient(to right, red, #E3E3E3, blue);') 632 | .style('border', '1px solid #000000') 633 | .style('position', 'absolute') 634 | .style('top', plot+1) 635 | .style('left', side) 636 | .style('width', plot) 637 | .style('height', gutter/3); 638 | 639 | this.axxDiv 640 | .attr('style', 'background: -moz-linear-gradient(top, red, gray, blue); background: linear-gradient(to top, red, #E3E3E3, blue);') 641 | .style('border', '1px solid #000000') 642 | .style('position', 'absolute') 643 | .style('top', 0) 644 | .style('left', side - gutter/3 -1) 645 | .style('width', gutter/3) 646 | .style('height', plot); 647 | 648 | return this; 649 | } 650 | 651 | 652 | // Graph Layout -- mostly for use with a Worker 653 | //====== 654 | 655 | var GraphLayout = function(s, range) { 656 | this.sne = new Float32Array(2*1000); 657 | var this_ = this; 658 | 659 | range = range || 25; 660 | 661 | this.scatter = new BasicVis.ScatterPlot(s); 662 | this.scatter 663 | .N(this.sne.length/2) 664 | .xrange([-range, range])//.fit(sne) 665 | .yrange([-range, range])//.fit(sne) 666 | .x(function(i) {return this_.sne[2*i ];}) 667 | .y(function(i) {return this_.sne[2*i+1];}) 668 | .size(4.5) 669 | .color(function(i){return d3.hsl(360*mnist_ys[i]/10.0,0.5,0.5);}) 670 | .enable_zoom() 671 | .bindToWindowResize(); 672 | this.scatter.s.style("border", "1px black solid"); 673 | 674 | this.scatter.layout(); 675 | this.s = this.scatter.s; 676 | 677 | this.fast_reposition = function() { 678 | var points = this.scatter.points[0]; 679 | var xmap = this.scatter.xmap; 680 | var ymap = this.scatter.ymap; 681 | for (var i = 0; i < points.length; i++) { 682 | points[i].setAttribute("cx", xmap(this_.sne[2*i])); 683 | points[i].setAttribute("cy", ymap(this_.sne[2*i+1])); 684 | } 685 | }; 686 | 687 | this.make_edges = function () { 688 | var edges = this.edges; 689 | var selection = this.scatter.zoom_g.selectAll("line").data(d3.range(edges.length/2)); 690 | selection.enter().insert("line", ":first-child") 691 | .style("stroke-width", 1.25) 692 | .style("stroke", function(edge, n) { 693 | var i = edges[2*n], j = edges[2*n+1]; 694 | if (mnist_ys[i] == mnist_ys[j]){ 695 | return d3.hsl(360*mnist_ys[i]/10.0,0.35,0.35); 696 | } else { 697 | return d3.rgb(80, 80, 80); 698 | }}); 699 | this.lines = selection[0]; 700 | } 701 | 702 | this.fast_reposition_edges = function() { 703 | var xmap = this.scatter.xmap; 704 | var ymap = this.scatter.ymap; 705 | for (var n = 0; n < this.lines.length; n++) { 706 | var line = this.lines[n]; 707 | var i = this.edges[2*n], j = this.edges[2*n+1]; 708 | line.setAttribute("x1", xmap(this.sne[2*i ])); 709 | line.setAttribute("y1", ymap(this.sne[2*i+1])); 710 | line.setAttribute("x2", xmap(this.sne[2*j ])); 711 | line.setAttribute("y2", ymap(this.sne[2*j+1])); 712 | } 713 | 714 | }; 715 | 716 | this.layout = function() { 717 | this.scatter.layout(); 718 | }; 719 | 720 | this.render = function() { 721 | this.scatter.render(); 722 | this.make_edges(); 723 | }; 724 | 725 | this.rerender = function() { 726 | this.fast_reposition(); 727 | this.fast_reposition_edges(); 728 | }; 729 | 730 | }; 731 | 732 | //================== 733 | 734 | var AnimationWrapper = function(anim) { 735 | this.anim = anim; 736 | this.s = anim.s; 737 | this.button = this.s.append('div'); 738 | this.has_run = false; 739 | 740 | this.layout = function() { 741 | this.s.style('position', 'relative'); 742 | var W = parseInt(this.s.style('width')); 743 | var H = parseInt(this.s.style('height')); 744 | this.button 745 | .style("border", "1px black solid") 746 | .style('width', 60) 747 | .style('height', 25 ) 748 | .style('position', 'absolute') 749 | .style('left', W/40) 750 | .style('bottom', H/60) 751 | .style('border-radius', 6) 752 | .style("cursor", "default") 753 | .style("text-align", "center") 754 | .style("vertical-align", "middle") 755 | .style("background", "#DDDDDD") 756 | .style("z-index", 10) 757 | .text('play'); 758 | var this_ = this; 759 | this.button.on("click", function() {this_.on_click(); }); 760 | }; 761 | 762 | this.run = function() {}; 763 | this.reset = function() { this.W.postMessage({cmd: "reset"}); }; 764 | 765 | this.hide = function() { 766 | this.button.style('visibility', 'hidden'); 767 | } 768 | this.unhide = function() { 769 | this.button.style('visibility', 'visible'); 770 | } 771 | 772 | this.on_click = function () { 773 | if (this.has_run) { 774 | this.reset(); 775 | this.has_run = false; 776 | } 777 | this.hide(); 778 | this.run(); 779 | } 780 | this.on_done = function () { 781 | this.has_run = true; 782 | this.unhide(); 783 | } 784 | 785 | this.bindToWorker = function(W) { 786 | this.W = W; 787 | var this_ = this; 788 | var obj = this.anim; 789 | W.onmessage = function(e) { 790 | data = e.data; 791 | switch (data.msg) { 792 | case "update": 793 | obj.sne = data.embed; 794 | window.requestAnimationFrame(function() { obj.rerender();}); 795 | break; 796 | case "ready": 797 | break; 798 | case "edges": 799 | obj.edges = data.edges; 800 | obj.make_edges(); 801 | window.requestAnimationFrame(function() { obj.rerender();}); 802 | case "done": 803 | this_.on_done(); 804 | break; 805 | } 806 | }; 807 | }; 808 | 809 | } 810 | -------------------------------------------------------------------------------- /js/NetVis.js: -------------------------------------------------------------------------------- 1 | 2 | // NetworkLayout 3 | //================== 4 | 5 | var NetworkLayout = function NetworkLayout(s) { 6 | 7 | BasicVis.Container.call(this, s); 8 | 9 | this.svg = this.inner.append("svg"); 10 | this.repsg = this.svg.append("g"); 11 | this.label1g = this.svg.append("g"); 12 | this.label2g = this.svg.append("g"); 13 | this.rep_div_cont = this.inner.append('div'); 14 | this.inner.style('position', 'relative'); 15 | 16 | //this.plot = this.new_child( BasisPlotMNIST ); 17 | 18 | this.layers = null; 19 | 20 | }; 21 | 22 | NetworkLayout.prototype = Object.create(BasicVis.Container.prototype); 23 | 24 | NetworkLayout.prototype.layout = function() { 25 | 26 | }; 27 | 28 | NetworkLayout.prototype.render = function() { 29 | 30 | var layers = this.layers; 31 | if (!layers) return this; 32 | 33 | var edges = []; 34 | for (var l = 0; l < layers.length-1; l++) 35 | for (var i = 0; i < layers[l ].n; i++) 36 | for (var j = 0; j < layers[l+1].n; j++) { 37 | edges.push([l, i, j]); 38 | } 39 | 40 | var points = []; 41 | for (var l = 0; l < layers.length; l++) 42 | for (var i = 0; i < layers[l ].n; i++) { 43 | points.push([l, i]); 44 | } 45 | 46 | var W = parseInt(this.s.style('width')); 47 | this.svg.style('width', W); 48 | var max_width = d3.max(layers, function(l) {return l.n;}) - 1; 49 | 50 | var text_space = 26; 51 | var dot_clear = 15; 52 | //var bot_H = main_H*(1- max_width*max_width/(5+2*max_width*max_width)); 53 | var rep_box = W/Math.max(4, layers.length)/1.3; 54 | var bot_H = rep_box*1.1; 55 | var top_H = W/2 * max_width*max_width/(5+2*max_width*max_width); 56 | var side_margin = bot_H/2; 57 | 58 | var main_H = bot_H + top_H+1; 59 | var H = main_H + 2*text_space + 4; 60 | this.svg.style('height', H); 61 | 62 | var xmap = d3.scale.linear() 63 | .domain([0, layers.length -1]) 64 | .range([side_margin, W-side_margin]); 65 | var ymap = d3.scale.linear() 66 | .domain([-max_width, max_width]) 67 | .range([dot_clear, main_H - dot_clear - bot_H]); 68 | 69 | var lines = this.svg.selectAll("line") 70 | .data(edges); 71 | lines.enter().append("line"); 72 | lines 73 | .style("stroke-width", 2) 74 | .style("stroke", d3.rgb(80, 80, 80) ) 75 | .attr("x1", function(d) { return xmap(d[0]); } ) 76 | .attr("y1", function(d) { return ymap(2*d[1] - layers[d[0]].n + 1); } ) 77 | .attr("x2", function(d) { return xmap(d[0]+1);} ) 78 | .attr("y2", function(d) { return ymap(2*d[2] - layers[d[0]+1].n + 1); } ); 79 | 80 | var circles = this.svg.selectAll("circle") 81 | .data(points); 82 | circles.enter().append('circle'); 83 | circles 84 | .attr("r", 4) 85 | .attr("cx", function(d) { return xmap(d[0]); } ) 86 | .attr("cy", function(d) { return ymap(2*d[1] - layers[d[0]].n + 1); } ) 87 | .attr('fill', d3.rgb(60, 60, 60)); 88 | 89 | var labels1 = this.label1g.selectAll("text") 90 | .data(layers); 91 | labels1.enter().append("text"); 92 | labels1.text(function(d) {return d.label;} ); 93 | labels1 94 | .attr("x", function(d, i) { var W = parseInt(d3.select(this).style('width')); return xmap(i) - W/2; } ) 95 | .attr("y", function(d, i) { var H = parseInt(d3.select(this).style('height')); return main_H + H + (text_space-H)/2; } ); 96 | 97 | var labels2 = this.label2g.selectAll("text") 98 | .data(layers); 99 | labels2.enter().append("text"); 100 | labels2.text(function(d) {return d.label2;} ); 101 | labels2 102 | .attr("x", function(d, i) { var W = parseInt(d3.select(this).style('width')); return xmap(i) - W/2; } ) 103 | .attr("y", function(d, i) { var H = parseInt(d3.select(this).style('height')); return main_H + H + text_space + (text_space-H)/2; } ); 104 | 105 | var repsT = this.repsg.selectAll("g") 106 | .data(layers); 107 | var repsT_enter = repsT.enter().append("g"); 108 | 109 | var reps_enter = repsT_enter.append('g') 110 | .style('visibility', function(d) { return d.hide_rep? 'hidden' : 'visible';}); 111 | var reps_enter_hidden = repsT_enter.append('g') 112 | .style('visibility', function(d) { return d.hide_rep? 'visible' : 'hidden';}); 113 | 114 | reps_enter.append('line') 115 | .attr('y1', main_H - bot_H + (bot_H - rep_box)/2) 116 | //.attr('y2', main_H - bot_H) 117 | .attr('y2', function (d,i) { return ymap(d.n-1) + dot_clear;}) 118 | .attr('x1', function (d,i) { return xmap(i);}) 119 | .attr('x2', function (d,i) { return xmap(i);}) 120 | .attr('stroke-dasharray', '5,5') 121 | .attr('style', 'fill: none; stroke: black; stroke-width: 2;'); 122 | 123 | reps_enter.append('rect') 124 | .attr('y', main_H - bot_H + (bot_H - rep_box)/2) 125 | .attr('x', function (d,i) { return xmap(i) - rep_box/2;}) 126 | .attr('rx', rep_box/20) 127 | .attr('ry', rep_box/20) 128 | .attr('width', rep_box) 129 | .attr('height', rep_box) 130 | .attr('style', 'fill: none; stroke: black; stroke-width: 2.5;'); 131 | 132 | reps_enter_hidden.append('line') 133 | .attr('y1', main_H) 134 | //.attr('y2', main_H - bot_H) 135 | .attr('y2', function (d,i) { return ymap(d.n-1) + dot_clear;}) 136 | .attr('x1', function (d,i) { return xmap(i);}) 137 | .attr('x2', function (d,i) { return xmap(i);}) 138 | .attr('stroke-dasharray', '5,5') 139 | .attr('style', 'fill: none; stroke: black; stroke-width: 2;'); 140 | 141 | var rep_div_selec = this.rep_div_cont.selectAll('div') 142 | .data(layers); 143 | rep_div_selec.enter().append('div') 144 | .style('position', 'absolute') 145 | .style('left', function (d,i) { return (xmap(i) - 0.95*rep_box/2)+"px";}) 146 | .style('top', (main_H - bot_H + (bot_H - 0.95*rep_box)/2) + "px") 147 | .style('width', 0.95*rep_box) 148 | .style('height', 0.95*rep_box); 149 | this.rep_divs = []; 150 | for (var n = 0; n < layers.length; n++) { 151 | this.rep_divs.push(d3.select(rep_div_selec[0][n])); 152 | } 153 | //.attr('style', 'fill: none; stroke: black; stroke-width: 4; stroke-linecap: round;'); 154 | 155 | }; 156 | 157 | 158 | //========================= 159 | 160 | function display_embed(data, div, fix_width) { 161 | var sne = data["vs_sne"]; 162 | var toks = data["toks"]; 163 | var urls = data["urls"]; 164 | var cats = data["cats"]; 165 | var tok_cats = data["tok_cats"]; 166 | div = d3.select(div); 167 | 168 | var this_ = this; 169 | 170 | var W = parseInt(div.style('width')); 171 | 172 | if (W > 500) { 173 | var class_N = 6; 174 | } else { 175 | var class_N = 1; 176 | } 177 | 178 | this_.categories = []; 179 | 180 | var opacity = 0.2 + 0.4*Math.pow(0.01, Math.max(1, sne.length - 3000)/20000.0); 181 | console.log(opacity, sne.length, Math.max(1, sne.length - 3000)/20000.0); 182 | 183 | var scatter = new BasicVis.ScatterPlot(div.select(".sne")) 184 | .N(sne.length/2) 185 | //.enable_zoom() 186 | .xrange.fit(sne) 187 | .yrange.fit(sne) 188 | .x(function(i) {return sne[2*i ];}) 189 | .y(function(i) {return sne[2*i+1];}) 190 | .size(2.3) 191 | .color(function(i){ 192 | var k = -1; 193 | if (tok_cats[i]) { 194 | for (var catn in this_.categories) { 195 | if (tok_cats[i].indexOf(this_.categories[catn]) != -1) {k = catn;} 196 | } 197 | } 198 | if ( k == -1) { return "rgba(150,150,150," + opacity + ")"; } 199 | return d3.hsl(360*k/class_N,0.5,0.5); 200 | }); 201 | scatter.bindToWindowResize() 202 | 203 | this.scatter = scatter; 204 | 205 | scatter.recolor = function() { 206 | var data = scatter._data; 207 | scatter.points 208 | .attr('fill', data.color); 209 | }; 210 | 211 | scatter.update(); 212 | 213 | 214 | this_.tooltip = new BasicVis.TextTooltip(); 215 | this_.tooltip._labels = toks; 216 | this_.tooltip.bind(scatter.points); 217 | this_.tooltip.bind_move(scatter.s); 218 | this_.tooltip.div.style("font-size", "85%"); 219 | if (fix_width) { 220 | this_.tooltip.div.style('width', W/2 + "px"); 221 | } 222 | 223 | if (urls) { 224 | scatter.points.on("click", function(i){ 225 | window.open(urls[i]); 226 | }); 227 | } 228 | 229 | 230 | 231 | 232 | var category_div_container = function(cont) { 233 | 234 | function new_cat_div () { 235 | var n = this_.categories.length; 236 | var inner = $("
").appendTo(cont).css("margin-bottom", "10px"); 237 | var sq = $("
").appendTo(inner); 238 | sq.css("width", "10%").css("height", "15px").css("display", "inline-block"); 239 | sq.css("background-color", "hsl(" + 360*n/class_N + ",50%,50%)" ); 240 | var div = $("").appendTo(inner); 241 | div.css("display", "inline-block"); 242 | div.css("width", "80%"); 243 | div.css("margin-left", "5%") 244 | div.css("font-size", "90%"); 245 | //$("
").appendTo(cont); 246 | category_div(div); 247 | } 248 | 249 | var category_div = function(div){ 250 | 251 | var n = this_.categories.length; 252 | this_.categories.push(""); 253 | 254 | catChange = function(e, ui){ 255 | if (ui && ui.item && ui.item.value) { 256 | var s = ui.item.value || this.value; 257 | } else { 258 | var s = this.value; 259 | } 260 | if (cats.indexOf(s) == -1 && s != "") return; 261 | this_.categories[n] = cats.indexOf(s); 262 | setTimeout(function() {scatter.recolor();}, 0); 263 | //if (this_.categories.length == n + 1 && s != "") 264 | // new_cat_div(); 265 | } 266 | 267 | var getMatchList = function(req, resp) { 268 | var term = req.term; 269 | var max_matches = 14; 270 | if (term.length == 0) { 271 | var matches = cats.slice(0, max_matches); 272 | if (cats.length > max_matches){ 273 | matches.push('...'); 274 | } 275 | } else { 276 | var term_esc = term; 277 | var regex = new RegExp(term_esc, 'i'); //ignore case 278 | var matches = []; 279 | for (var i = 0; i < cats.length; i++) { 280 | if (!regex.test(cats[i])) 281 | continue; 282 | matches.push(cats[i]); 283 | if (matches.length >= max_matches) { 284 | matches.push('...'); 285 | break; 286 | } 287 | } 288 | } 289 | resp(matches); 290 | } 291 | 292 | div.autocomplete({ 293 | delay: 1, 294 | source: getMatchList, 295 | select: catChange, 296 | change: catChange 297 | }); 298 | div.change(catChange); 299 | 300 | } 301 | 302 | for (var j = 0; j < class_N; j++) { 303 | new_cat_div(); 304 | } 305 | 306 | setTimeout(function() { 307 | var H_main = parseInt(div.style("height")); 308 | var H_cont = parseInt(cont.style("height")); 309 | cont.style("top", (H_main-H_cont)/2 + "px") 310 | }, 1); 311 | 312 | } 313 | 314 | 315 | category_div_container(div.select(".legend")); 316 | $(".ui-autocomplete").css("font-size", "90%").css("text-align", "left"); 317 | 318 | } 319 | 320 | 321 | 322 | //=============== 323 | 324 | var friendly_reps = {}; 325 | 326 | (function() { 327 | 328 | var rep_names = Object.keys(mnist_reps).sort(); 329 | var human_name = function(name){ 330 | var n = -1, np = -1, k = -1, i = -1, maxpool = false; 331 | 332 | if (name.indexOf("_N0") > -1) {i = 0;} 333 | if (name.indexOf("_N1") > -1) {i = 1;} 334 | if (name.indexOf("_N2") > -1) {i = 2;} 335 | if (name.indexOf("_N3") > -1) {i = 3;} 336 | if (name.indexOf("_N4") > -1) {i = 4;} 337 | 338 | if (name.indexOf("_net") > -1) {k = 0;} 339 | if (name.indexOf("_netR") > -1) {k = 1;} 340 | if (name.indexOf("_conv") > -1) {k = 2;} 341 | if (name.indexOf("_netR200-") > -1) {k = 3;} 342 | if (name.indexOf("_netR40-") > -1) {k = 3;} 343 | if (name.indexOf("_netR10-") > -1) {k = 3;} 344 | 345 | if (name.indexOf("200-100") > -1) {np = 200; n = 100;} 346 | if (name.indexOf("40-20") > -1) {np = 40; n = 20;} 347 | if (name.indexOf("10-5") > -1) {np = 10; n = 5;} 348 | 349 | 350 | if (name.indexOf("max2") > -1) {maxpool = true;} 351 | 352 | if (name.indexOf("net100") > -1) {n = 100;} 353 | else if (name.indexOf("net200") > -1) {n = 200;} 354 | else if (name.indexOf("net10") > -1) {n = 10; } 355 | else if (name.indexOf("net20") > -1) {n = 20; } 356 | else if (name.indexOf("net1") > -1) {n = 1; } 357 | else if (name.indexOf("net2") > -1) {n = 2; } 358 | else if (name.indexOf("net5") > -1) {n = 5; } 359 | else if (name.indexOf("net8") > -1) {n = 8; } 360 | 361 | 362 | if (name.indexOf("_netR1_") > -1) {n = 1; } 363 | if (name.indexOf("_netR2_") > -1) {n = 2; } 364 | if (name.indexOf("_netR5_") > -1) {n = 5; } 365 | if (name.indexOf("_netR10_") > -1) {n = 10; } 366 | if (name.indexOf("_netR20_") > -1) {n = 20; } 367 | if (name.indexOf("_netR100_") > -1) {n = 100;} 368 | if (name.indexOf("_netR200_") > -1) {n = 200;} 369 | 370 | if (i > 1) 371 | return null; 372 | if (n > -1 && k == 0) 373 | return "Sigmoid Layer (" + n + " units) - " +i; 374 | if (n > -1 && k == 1) 375 | return "ReLU Layer (" + n + " units) - " +i; 376 | if (n > -1 && k == 2 && maxpool) 377 | return "Conv Layer ("+ n +" features; 5x5 patch; max 2x2) - " +i; 378 | if (n > -1 && k == 2) 379 | return "Conv Layer ("+ n +" features; 5x5 patch) - " +i; 380 | if (n > -1 && k == 3) 381 | return "Two ReLU Layers (" + np + " units; " + n + " units) - " +i; 382 | if (name == "mnist_raw") 383 | return "Raw MNIST"; 384 | if (name.indexOf("netR[40,20]") > -1) 385 | return "Two ReLU Layers (40 units; 20 units) - " +i; 386 | if (n == -1 || k > 1) 387 | return name; 388 | } 389 | 390 | 391 | for (var i in rep_names){ 392 | var name = rep_names[i]; 393 | var name2 = human_name(name); 394 | if (name2) 395 | friendly_reps[name2] = mnist_reps[name]; 396 | } 397 | 398 | })(); 399 | 400 | -------------------------------------------------------------------------------- /js/data/MNIST-SNE-good.js: -------------------------------------------------------------------------------- 1 | 2 | "use strict"; 3 | 4 | /* Array of bytes to base64 string decoding */ 5 | 6 | function b64ToUint6 (nChr) { 7 | 8 | return nChr > 64 && nChr < 91 ? 9 | nChr - 65 10 | : nChr > 96 && nChr < 123 ? 11 | nChr - 71 12 | : nChr > 47 && nChr < 58 ? 13 | nChr + 4 14 | : nChr === 43 ? 15 | 62 16 | : nChr === 47 ? 17 | 63 18 | : 19 | 0; 20 | } 21 | 22 | function base64DecToArr (sBase64, nBlocksSize) { 23 | 24 | var 25 | sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length, 26 | nOutLen = nBlocksSize ? Math.ceil((nInLen * 3 + 1 >> 2) / nBlocksSize) * nBlocksSize : nInLen * 3 + 1 >> 2, taBytes = new Uint8Array(nOutLen); 27 | 28 | for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) { 29 | nMod4 = nInIdx & 3; 30 | nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4; 31 | if (nMod4 === 3 || nInLen - nInIdx === 1) { 32 | for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) { 33 | taBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255; 34 | } 35 | nUint24 = 0; 36 | 37 | } 38 | } 39 | 40 | return taBytes; 41 | } 42 | 43 | 44 | var mnist_sne = new Float32Array( base64DecToArr("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").buffer ); -------------------------------------------------------------------------------- /js/data/mnist_pca.js: -------------------------------------------------------------------------------- 1 | mnist_pca = { 2 | W: new Float32Array( base64DecToArr("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" ).buffer ), 3 | mean: new Float32Array( base64DecToArr( "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" 4 | ).buffer ) 5 | } 6 | -------------------------------------------------------------------------------- /js/data/mnist_pca.js~: -------------------------------------------------------------------------------- 1 | mnist_pca = { 2 | W: "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", 3 | mean: "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"} 4 | -------------------------------------------------------------------------------- /js/foreign/TrackballControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Eberhard Graether / http://egraether.com/ 3 | * @author Mark Lundin / http://mark-lundin.com 4 | */ 5 | 6 | THREE.TrackballControls = function ( object, domElement ) { 7 | 8 | var _this = this; 9 | var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 }; 10 | 11 | this.object = object; 12 | this.domElement = ( domElement !== undefined ) ? domElement : document; 13 | 14 | // API 15 | 16 | this.enabled = true; 17 | 18 | this.screen = { left: 0, top: 0, width: 0, height: 0 }; 19 | 20 | this.rotateSpeed = 1.0; 21 | this.zoomSpeed = 1.2; 22 | this.panSpeed = 0.3; 23 | 24 | this.noRotate = false; 25 | this.noZoom = false; 26 | this.noPan = false; 27 | this.noRoll = false; 28 | 29 | this.staticMoving = false; 30 | this.dynamicDampingFactor = 0.2; 31 | 32 | this.minDistance = 0; 33 | this.maxDistance = Infinity; 34 | 35 | this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ]; 36 | 37 | // internals 38 | 39 | this.target = new THREE.Vector3(); 40 | 41 | var EPS = 0.000001; 42 | 43 | var lastPosition = new THREE.Vector3(); 44 | 45 | var _state = STATE.NONE, 46 | _prevState = STATE.NONE, 47 | 48 | _eye = new THREE.Vector3(), 49 | 50 | _rotateStart = new THREE.Vector3(), 51 | _rotateEnd = new THREE.Vector3(), 52 | 53 | _zoomStart = new THREE.Vector2(), 54 | _zoomEnd = new THREE.Vector2(), 55 | 56 | _touchZoomDistanceStart = 0, 57 | _touchZoomDistanceEnd = 0, 58 | 59 | _panStart = new THREE.Vector2(), 60 | _panEnd = new THREE.Vector2(); 61 | 62 | // for reset 63 | 64 | this.target0 = this.target.clone(); 65 | this.position0 = this.object.position.clone(); 66 | this.up0 = this.object.up.clone(); 67 | 68 | // events 69 | 70 | var changeEvent = { type: 'change' }; 71 | var startEvent = { type: 'start'}; 72 | var endEvent = { type: 'end'}; 73 | 74 | 75 | // methods 76 | 77 | this.handleResize = function () { 78 | 79 | if ( this.domElement === document ) { 80 | 81 | this.screen.left = 0; 82 | this.screen.top = 0; 83 | this.screen.width = window.innerWidth; 84 | this.screen.height = window.innerHeight; 85 | 86 | } else { 87 | 88 | var box = this.domElement.getBoundingClientRect(); 89 | // adjustments come from similar code in the jquery offset() function 90 | var d = this.domElement.ownerDocument.documentElement; 91 | this.screen.left = box.left + window.pageXOffset - d.clientLeft; 92 | this.screen.top = box.top + window.pageYOffset - d.clientTop; 93 | this.screen.width = box.width; 94 | this.screen.height = box.height; 95 | 96 | } 97 | 98 | }; 99 | 100 | this.handleEvent = function ( event ) { 101 | 102 | if ( typeof this[ event.type ] == 'function' ) { 103 | 104 | this[ event.type ]( event ); 105 | 106 | } 107 | 108 | }; 109 | 110 | var getMouseOnScreen = ( function () { 111 | 112 | var vector = new THREE.Vector2(); 113 | 114 | return function ( pageX, pageY ) { 115 | 116 | vector.set( 117 | ( pageX - _this.screen.left ) / _this.screen.width, 118 | ( pageY - _this.screen.top ) / _this.screen.height 119 | ); 120 | 121 | return vector; 122 | 123 | }; 124 | 125 | }() ); 126 | 127 | var getMouseOnScreenByEvent = ( function () { 128 | 129 | var vector = new THREE.Vector2(); 130 | 131 | return function (e) { 132 | console.log("getMouseOnScreenByEvent called!"); 133 | var X = e.offsetX || ( e.pageX - _this.screen.left ); 134 | var Y = e.offsetY || ( e.pageY - _this.screen.top ); 135 | console.log(e, X, Y); 136 | vector.set( 137 | X / _this.screen.width, 138 | Y / _this.screen.height 139 | ); 140 | 141 | return vector; 142 | 143 | }; 144 | 145 | }() ); 146 | 147 | var getMouseProjectionOnBall = ( function () { 148 | 149 | var vector = new THREE.Vector3(); 150 | var objectUp = new THREE.Vector3(); 151 | var mouseOnBall = new THREE.Vector3(); 152 | 153 | return function ( pageX, pageY ) { 154 | 155 | mouseOnBall.set( 156 | ( pageX - _this.screen.width * 0.5 - _this.screen.left ) / (_this.screen.width*.5), 157 | ( _this.screen.height * 0.5 + _this.screen.top - pageY ) / (_this.screen.height*.5), 158 | 0.0 159 | ); 160 | 161 | var length = mouseOnBall.length(); 162 | 163 | if ( _this.noRoll ) { 164 | 165 | if ( length < Math.SQRT1_2 ) { 166 | 167 | mouseOnBall.z = Math.sqrt( 1.0 - length*length ); 168 | 169 | } else { 170 | 171 | mouseOnBall.z = .5 / length; 172 | 173 | } 174 | 175 | } else if ( length > 1.0 ) { 176 | 177 | mouseOnBall.normalize(); 178 | 179 | } else { 180 | 181 | mouseOnBall.z = Math.sqrt( 1.0 - length * length ); 182 | 183 | } 184 | 185 | _eye.copy( _this.object.position ).sub( _this.target ); 186 | 187 | vector.copy( _this.object.up ).setLength( mouseOnBall.y ) 188 | vector.add( objectUp.copy( _this.object.up ).cross( _eye ).setLength( mouseOnBall.x ) ); 189 | vector.add( _eye.setLength( mouseOnBall.z ) ); 190 | 191 | return vector; 192 | 193 | }; 194 | 195 | }() ); 196 | 197 | var getMouseProjectionOnBallByEvent = ( function () { 198 | 199 | var vector = new THREE.Vector3(); 200 | var objectUp = new THREE.Vector3(); 201 | var mouseOnBall = new THREE.Vector3(); 202 | 203 | return function ( e ) { 204 | 205 | var X = e.offsetX || ( e.pageX - _this.screen.left ); 206 | var Y = e.offsetY || ( e.pageY - _this.screen.top ); 207 | 208 | mouseOnBall.set( 209 | ( X - _this.screen.width * 0.5 ) / (_this.screen.width*.5), 210 | ( _this.screen.height * 0.5 - Y ) / (_this.screen.height*.5), 211 | 0.0 212 | ); 213 | 214 | var length = mouseOnBall.length(); 215 | 216 | if ( _this.noRoll ) { 217 | 218 | if ( length < Math.SQRT1_2 ) { 219 | 220 | mouseOnBall.z = Math.sqrt( 1.0 - length*length ); 221 | 222 | } else { 223 | 224 | mouseOnBall.z = .5 / length; 225 | 226 | } 227 | 228 | } else if ( length > 1.0 ) { 229 | 230 | mouseOnBall.normalize(); 231 | 232 | } else { 233 | 234 | mouseOnBall.z = Math.sqrt( 1.0 - length * length ); 235 | 236 | } 237 | 238 | _eye.copy( _this.object.position ).sub( _this.target ); 239 | 240 | vector.copy( _this.object.up ).setLength( mouseOnBall.y ) 241 | vector.add( objectUp.copy( _this.object.up ).cross( _eye ).setLength( mouseOnBall.x ) ); 242 | vector.add( _eye.setLength( mouseOnBall.z ) ); 243 | 244 | return vector; 245 | 246 | }; 247 | 248 | }() ); 249 | 250 | this.rotateCamera = (function(){ 251 | 252 | var axis = new THREE.Vector3(), 253 | quaternion = new THREE.Quaternion(); 254 | 255 | 256 | return function () { 257 | 258 | var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() ); 259 | 260 | if ( angle ) { 261 | 262 | axis.crossVectors( _rotateStart, _rotateEnd ).normalize(); 263 | 264 | angle *= _this.rotateSpeed; 265 | 266 | quaternion.setFromAxisAngle( axis, -angle ); 267 | 268 | _eye.applyQuaternion( quaternion ); 269 | _this.object.up.applyQuaternion( quaternion ); 270 | 271 | _rotateEnd.applyQuaternion( quaternion ); 272 | 273 | if ( _this.staticMoving ) { 274 | 275 | _rotateStart.copy( _rotateEnd ); 276 | 277 | } else { 278 | 279 | quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) ); 280 | _rotateStart.applyQuaternion( quaternion ); 281 | 282 | } 283 | 284 | } 285 | } 286 | 287 | }()); 288 | 289 | this.zoomCamera = function () { 290 | 291 | if ( _state === STATE.TOUCH_ZOOM_PAN ) { 292 | 293 | var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd; 294 | _touchZoomDistanceStart = _touchZoomDistanceEnd; 295 | _eye.multiplyScalar( factor ); 296 | 297 | } else { 298 | 299 | var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed; 300 | 301 | if ( factor !== 1.0 && factor > 0.0 ) { 302 | 303 | _eye.multiplyScalar( factor ); 304 | 305 | if ( _this.staticMoving ) { 306 | 307 | _zoomStart.copy( _zoomEnd ); 308 | 309 | } else { 310 | 311 | _zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor; 312 | 313 | } 314 | 315 | } 316 | 317 | } 318 | 319 | }; 320 | 321 | this.panCamera = (function(){ 322 | 323 | var mouseChange = new THREE.Vector2(), 324 | objectUp = new THREE.Vector3(), 325 | pan = new THREE.Vector3(); 326 | 327 | return function () { 328 | 329 | mouseChange.copy( _panEnd ).sub( _panStart ); 330 | 331 | if ( mouseChange.lengthSq() ) { 332 | 333 | mouseChange.multiplyScalar( _eye.length() * _this.panSpeed ); 334 | 335 | pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x ); 336 | pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) ); 337 | 338 | _this.object.position.add( pan ); 339 | _this.target.add( pan ); 340 | 341 | if ( _this.staticMoving ) { 342 | 343 | _panStart.copy( _panEnd ); 344 | 345 | } else { 346 | 347 | _panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) ); 348 | 349 | } 350 | 351 | } 352 | } 353 | 354 | }()); 355 | 356 | this.checkDistances = function () { 357 | 358 | if ( !_this.noZoom || !_this.noPan ) { 359 | 360 | if ( _eye.lengthSq() > _this.maxDistance * _this.maxDistance ) { 361 | 362 | _this.object.position.addVectors( _this.target, _eye.setLength( _this.maxDistance ) ); 363 | 364 | } 365 | 366 | if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) { 367 | 368 | _this.object.position.addVectors( _this.target, _eye.setLength( _this.minDistance ) ); 369 | 370 | } 371 | 372 | } 373 | 374 | }; 375 | 376 | this.update = function () { 377 | 378 | _eye.subVectors( _this.object.position, _this.target ); 379 | 380 | if ( !_this.noRotate ) { 381 | 382 | _this.rotateCamera(); 383 | 384 | } 385 | 386 | if ( !_this.noZoom ) { 387 | 388 | _this.zoomCamera(); 389 | 390 | } 391 | 392 | if ( !_this.noPan ) { 393 | 394 | _this.panCamera(); 395 | 396 | } 397 | 398 | _this.object.position.addVectors( _this.target, _eye ); 399 | 400 | _this.checkDistances(); 401 | 402 | _this.object.lookAt( _this.target ); 403 | 404 | if ( lastPosition.distanceToSquared( _this.object.position ) > EPS ) { 405 | 406 | _this.dispatchEvent( changeEvent ); 407 | 408 | lastPosition.copy( _this.object.position ); 409 | 410 | } 411 | 412 | }; 413 | 414 | this.reset = function () { 415 | 416 | _state = STATE.NONE; 417 | _prevState = STATE.NONE; 418 | 419 | _this.target.copy( _this.target0 ); 420 | _this.object.position.copy( _this.position0 ); 421 | _this.object.up.copy( _this.up0 ); 422 | 423 | _eye.subVectors( _this.object.position, _this.target ); 424 | 425 | _this.object.lookAt( _this.target ); 426 | 427 | _this.dispatchEvent( changeEvent ); 428 | 429 | lastPosition.copy( _this.object.position ); 430 | 431 | }; 432 | 433 | // listeners 434 | 435 | function keydown( event ) { 436 | 437 | if ( _this.enabled === false ) return; 438 | 439 | window.removeEventListener( 'keydown', keydown ); 440 | 441 | _prevState = _state; 442 | 443 | if ( _state !== STATE.NONE ) { 444 | 445 | return; 446 | 447 | } else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ) { 448 | 449 | _state = STATE.ROTATE; 450 | 451 | } else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ) { 452 | 453 | _state = STATE.ZOOM; 454 | 455 | } else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ) { 456 | 457 | _state = STATE.PAN; 458 | 459 | } 460 | 461 | } 462 | 463 | function keyup( event ) { 464 | 465 | if ( _this.enabled === false ) return; 466 | 467 | _state = _prevState; 468 | 469 | window.addEventListener( 'keydown', keydown, false ); 470 | 471 | } 472 | 473 | function mousedown( event ) { 474 | 475 | if ( _this.enabled === false ) return; 476 | 477 | event.preventDefault(); 478 | event.stopPropagation(); 479 | 480 | if ( _state === STATE.NONE ) { 481 | 482 | _state = event.button; 483 | 484 | } 485 | 486 | if ( _state === STATE.ROTATE && !_this.noRotate ) { 487 | 488 | _rotateStart.copy( getMouseProjectionOnBallByEvent( event ) ); 489 | _rotateEnd.copy( _rotateStart ); 490 | 491 | } else if ( _state === STATE.ZOOM && !_this.noZoom ) { 492 | 493 | _zoomStart.copy( getMouseOnScreenByEvent( event ) ); 494 | _zoomEnd.copy(_zoomStart); 495 | 496 | } else if ( _state === STATE.PAN && !_this.noPan ) { 497 | 498 | _panStart.copy( getMouseOnScreenByEvent( event ) ); 499 | _panEnd.copy(_panStart) 500 | 501 | } 502 | 503 | document.addEventListener( 'mousemove', mousemove, false ); 504 | document.addEventListener( 'mouseup', mouseup, false ); 505 | 506 | _this.dispatchEvent( startEvent ); 507 | 508 | } 509 | 510 | function mousemove( event ) { 511 | 512 | if ( _this.enabled === false ) return; 513 | 514 | event.preventDefault(); 515 | event.stopPropagation(); 516 | 517 | if ( _state === STATE.ROTATE && !_this.noRotate ) { 518 | 519 | _rotateEnd.copy( getMouseProjectionOnBallByEvent( event ) ); 520 | 521 | } else if ( _state === STATE.ZOOM && !_this.noZoom ) { 522 | 523 | _zoomEnd.copy( getMouseOnScreenByEvent( event ) ); 524 | 525 | } else if ( _state === STATE.PAN && !_this.noPan ) { 526 | 527 | _panEnd.copy( getMouseOnScreenByEvent( event ) ); 528 | 529 | } 530 | 531 | } 532 | 533 | function mouseup( event ) { 534 | 535 | if ( _this.enabled === false ) return; 536 | 537 | event.preventDefault(); 538 | event.stopPropagation(); 539 | 540 | _state = STATE.NONE; 541 | 542 | document.removeEventListener( 'mousemove', mousemove ); 543 | document.removeEventListener( 'mouseup', mouseup ); 544 | _this.dispatchEvent( endEvent ); 545 | 546 | } 547 | 548 | function mousewheel( event ) { 549 | 550 | if ( _this.enabled === false ) return; 551 | 552 | event.preventDefault(); 553 | event.stopPropagation(); 554 | 555 | var delta = 0; 556 | 557 | if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9 558 | 559 | delta = event.wheelDelta / 40; 560 | 561 | } else if ( event.detail ) { // Firefox 562 | 563 | delta = - event.detail / 3; 564 | 565 | } 566 | 567 | _zoomStart.y += delta * 0.01; 568 | _this.dispatchEvent( startEvent ); 569 | _this.dispatchEvent( endEvent ); 570 | 571 | } 572 | 573 | function touchstart( event ) { 574 | 575 | if ( _this.enabled === false ) return; 576 | 577 | switch ( event.touches.length ) { 578 | 579 | case 1: 580 | _state = STATE.TOUCH_ROTATE; 581 | _rotateStart.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); 582 | _rotateEnd.copy( _rotateStart ); 583 | break; 584 | 585 | case 2: 586 | _state = STATE.TOUCH_ZOOM_PAN; 587 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 588 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 589 | _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy ); 590 | 591 | var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; 592 | var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; 593 | _panStart.copy( getMouseOnScreen( x, y ) ); 594 | _panEnd.copy( _panStart ); 595 | break; 596 | 597 | default: 598 | _state = STATE.NONE; 599 | 600 | } 601 | _this.dispatchEvent( startEvent ); 602 | 603 | 604 | } 605 | 606 | function touchmove( event ) { 607 | 608 | if ( _this.enabled === false ) return; 609 | 610 | event.preventDefault(); 611 | event.stopPropagation(); 612 | 613 | switch ( event.touches.length ) { 614 | 615 | case 1: 616 | _rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); 617 | break; 618 | 619 | case 2: 620 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 621 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 622 | _touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy ); 623 | 624 | var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; 625 | var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; 626 | _panEnd.copy( getMouseOnScreen( x, y ) ); 627 | break; 628 | 629 | default: 630 | _state = STATE.NONE; 631 | 632 | } 633 | 634 | } 635 | 636 | function touchend( event ) { 637 | 638 | if ( _this.enabled === false ) return; 639 | 640 | switch ( event.touches.length ) { 641 | 642 | case 1: 643 | _rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); 644 | _rotateStart.copy( _rotateEnd ); 645 | break; 646 | 647 | case 2: 648 | _touchZoomDistanceStart = _touchZoomDistanceEnd = 0; 649 | 650 | var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; 651 | var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; 652 | _panEnd.copy( getMouseOnScreen( x, y ) ); 653 | _panStart.copy( _panEnd ); 654 | break; 655 | 656 | } 657 | 658 | _state = STATE.NONE; 659 | _this.dispatchEvent( endEvent ); 660 | 661 | } 662 | 663 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 664 | 665 | this.domElement.addEventListener( 'mousedown', mousedown, false ); 666 | 667 | this.domElement.addEventListener( 'mousewheel', mousewheel, false ); 668 | this.domElement.addEventListener( 'DOMMouseScroll', mousewheel, false ); // firefox 669 | 670 | this.domElement.addEventListener( 'touchstart', touchstart, false ); 671 | this.domElement.addEventListener( 'touchend', touchend, false ); 672 | this.domElement.addEventListener( 'touchmove', touchmove, false ); 673 | 674 | window.addEventListener( 'keydown', keydown, false ); 675 | window.addEventListener( 'keyup', keyup, false ); 676 | 677 | this.handleResize(); 678 | 679 | // force an update at start 680 | this.update(); 681 | 682 | }; 683 | 684 | THREE.TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 685 | -------------------------------------------------------------------------------- /js/foreign/TrackballControls.js~: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Eberhard Graether / http://egraether.com/ 3 | * @author Mark Lundin / http://mark-lundin.com 4 | */ 5 | 6 | THREE.TrackballControls = function ( object, domElement ) { 7 | 8 | var _this = this; 9 | var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 }; 10 | 11 | this.object = object; 12 | this.domElement = ( domElement !== undefined ) ? domElement : document; 13 | 14 | // API 15 | 16 | this.enabled = true; 17 | 18 | this.screen = { left: 0, top: 0, width: 0, height: 0 }; 19 | 20 | this.rotateSpeed = 1.0; 21 | this.zoomSpeed = 1.2; 22 | this.panSpeed = 0.3; 23 | 24 | this.noRotate = false; 25 | this.noZoom = false; 26 | this.noPan = false; 27 | this.noRoll = false; 28 | 29 | this.staticMoving = false; 30 | this.dynamicDampingFactor = 0.2; 31 | 32 | this.minDistance = 0; 33 | this.maxDistance = Infinity; 34 | 35 | this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ]; 36 | 37 | // internals 38 | 39 | this.target = new THREE.Vector3(); 40 | 41 | var EPS = 0.000001; 42 | 43 | var lastPosition = new THREE.Vector3(); 44 | 45 | var _state = STATE.NONE, 46 | _prevState = STATE.NONE, 47 | 48 | _eye = new THREE.Vector3(), 49 | 50 | _rotateStart = new THREE.Vector3(), 51 | _rotateEnd = new THREE.Vector3(), 52 | 53 | _zoomStart = new THREE.Vector2(), 54 | _zoomEnd = new THREE.Vector2(), 55 | 56 | _touchZoomDistanceStart = 0, 57 | _touchZoomDistanceEnd = 0, 58 | 59 | _panStart = new THREE.Vector2(), 60 | _panEnd = new THREE.Vector2(); 61 | 62 | // for reset 63 | 64 | this.target0 = this.target.clone(); 65 | this.position0 = this.object.position.clone(); 66 | this.up0 = this.object.up.clone(); 67 | 68 | // events 69 | 70 | var changeEvent = { type: 'change' }; 71 | var startEvent = { type: 'start'}; 72 | var endEvent = { type: 'end'}; 73 | 74 | 75 | // methods 76 | 77 | this.handleResize = function () { 78 | 79 | if ( this.domElement === document ) { 80 | 81 | this.screen.left = 0; 82 | this.screen.top = 0; 83 | this.screen.width = window.innerWidth; 84 | this.screen.height = window.innerHeight; 85 | 86 | } else { 87 | 88 | var box = this.domElement.getBoundingClientRect(); 89 | // adjustments come from similar code in the jquery offset() function 90 | var d = this.domElement.ownerDocument.documentElement; 91 | this.screen.left = box.left + window.pageXOffset - d.clientLeft; 92 | this.screen.top = box.top + window.pageYOffset - d.clientTop; 93 | this.screen.width = box.width; 94 | this.screen.height = box.height; 95 | 96 | } 97 | 98 | }; 99 | 100 | this.handleEvent = function ( event ) { 101 | 102 | if ( typeof this[ event.type ] == 'function' ) { 103 | 104 | this[ event.type ]( event ); 105 | 106 | } 107 | 108 | }; 109 | 110 | var getMouseOnScreen = ( function () { 111 | 112 | var vector = new THREE.Vector2(); 113 | 114 | return function ( pageX, pageY ) { 115 | 116 | vector.set( 117 | ( pageX - _this.screen.left ) / _this.screen.width, 118 | ( pageY - _this.screen.top ) / _this.screen.height 119 | ); 120 | 121 | return vector; 122 | 123 | }; 124 | 125 | }() ); 126 | 127 | var getMouseOnScreenByEvent = ( function () { 128 | 129 | var vector = new THREE.Vector2(); 130 | 131 | return function (e) { 132 | console.log("getMouseOnScreenByEvent called!"); 133 | var X = e.offsetX || ( e.pageX - _this.screen.left ); 134 | var Y = e.offsetY || ( e.pageY - _this.screen.top ); 135 | console.log(e, X, Y); 136 | vector.set( 137 | X / _this.screen.width, 138 | Y / _this.screen.height 139 | ); 140 | 141 | return vector; 142 | 143 | }; 144 | 145 | }() ); 146 | 147 | var getMouseProjectionOnBall = ( function () { 148 | 149 | var vector = new THREE.Vector3(); 150 | var objectUp = new THREE.Vector3(); 151 | var mouseOnBall = new THREE.Vector3(); 152 | 153 | return function ( pageX, pageY ) { 154 | 155 | mouseOnBall.set( 156 | ( pageX - _this.screen.width * 0.5 - _this.screen.left ) / (_this.screen.width*.5), 157 | ( _this.screen.height * 0.5 + _this.screen.top - pageY ) / (_this.screen.height*.5), 158 | 0.0 159 | ); 160 | 161 | var length = mouseOnBall.length(); 162 | 163 | if ( _this.noRoll ) { 164 | 165 | if ( length < Math.SQRT1_2 ) { 166 | 167 | mouseOnBall.z = Math.sqrt( 1.0 - length*length ); 168 | 169 | } else { 170 | 171 | mouseOnBall.z = .5 / length; 172 | 173 | } 174 | 175 | } else if ( length > 1.0 ) { 176 | 177 | mouseOnBall.normalize(); 178 | 179 | } else { 180 | 181 | mouseOnBall.z = Math.sqrt( 1.0 - length * length ); 182 | 183 | } 184 | 185 | _eye.copy( _this.object.position ).sub( _this.target ); 186 | 187 | vector.copy( _this.object.up ).setLength( mouseOnBall.y ) 188 | vector.add( objectUp.copy( _this.object.up ).cross( _eye ).setLength( mouseOnBall.x ) ); 189 | vector.add( _eye.setLength( mouseOnBall.z ) ); 190 | 191 | return vector; 192 | 193 | }; 194 | 195 | }() ); 196 | 197 | this.rotateCamera = (function(){ 198 | 199 | var axis = new THREE.Vector3(), 200 | quaternion = new THREE.Quaternion(); 201 | 202 | 203 | return function () { 204 | 205 | var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() ); 206 | 207 | if ( angle ) { 208 | 209 | axis.crossVectors( _rotateStart, _rotateEnd ).normalize(); 210 | 211 | angle *= _this.rotateSpeed; 212 | 213 | quaternion.setFromAxisAngle( axis, -angle ); 214 | 215 | _eye.applyQuaternion( quaternion ); 216 | _this.object.up.applyQuaternion( quaternion ); 217 | 218 | _rotateEnd.applyQuaternion( quaternion ); 219 | 220 | if ( _this.staticMoving ) { 221 | 222 | _rotateStart.copy( _rotateEnd ); 223 | 224 | } else { 225 | 226 | quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) ); 227 | _rotateStart.applyQuaternion( quaternion ); 228 | 229 | } 230 | 231 | } 232 | } 233 | 234 | }()); 235 | 236 | this.zoomCamera = function () { 237 | 238 | if ( _state === STATE.TOUCH_ZOOM_PAN ) { 239 | 240 | var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd; 241 | _touchZoomDistanceStart = _touchZoomDistanceEnd; 242 | _eye.multiplyScalar( factor ); 243 | 244 | } else { 245 | 246 | var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed; 247 | 248 | if ( factor !== 1.0 && factor > 0.0 ) { 249 | 250 | _eye.multiplyScalar( factor ); 251 | 252 | if ( _this.staticMoving ) { 253 | 254 | _zoomStart.copy( _zoomEnd ); 255 | 256 | } else { 257 | 258 | _zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor; 259 | 260 | } 261 | 262 | } 263 | 264 | } 265 | 266 | }; 267 | 268 | this.panCamera = (function(){ 269 | 270 | var mouseChange = new THREE.Vector2(), 271 | objectUp = new THREE.Vector3(), 272 | pan = new THREE.Vector3(); 273 | 274 | return function () { 275 | 276 | mouseChange.copy( _panEnd ).sub( _panStart ); 277 | 278 | if ( mouseChange.lengthSq() ) { 279 | 280 | mouseChange.multiplyScalar( _eye.length() * _this.panSpeed ); 281 | 282 | pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x ); 283 | pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) ); 284 | 285 | _this.object.position.add( pan ); 286 | _this.target.add( pan ); 287 | 288 | if ( _this.staticMoving ) { 289 | 290 | _panStart.copy( _panEnd ); 291 | 292 | } else { 293 | 294 | _panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) ); 295 | 296 | } 297 | 298 | } 299 | } 300 | 301 | }()); 302 | 303 | this.checkDistances = function () { 304 | 305 | if ( !_this.noZoom || !_this.noPan ) { 306 | 307 | if ( _eye.lengthSq() > _this.maxDistance * _this.maxDistance ) { 308 | 309 | _this.object.position.addVectors( _this.target, _eye.setLength( _this.maxDistance ) ); 310 | 311 | } 312 | 313 | if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) { 314 | 315 | _this.object.position.addVectors( _this.target, _eye.setLength( _this.minDistance ) ); 316 | 317 | } 318 | 319 | } 320 | 321 | }; 322 | 323 | this.update = function () { 324 | 325 | _eye.subVectors( _this.object.position, _this.target ); 326 | 327 | if ( !_this.noRotate ) { 328 | 329 | _this.rotateCamera(); 330 | 331 | } 332 | 333 | if ( !_this.noZoom ) { 334 | 335 | _this.zoomCamera(); 336 | 337 | } 338 | 339 | if ( !_this.noPan ) { 340 | 341 | _this.panCamera(); 342 | 343 | } 344 | 345 | _this.object.position.addVectors( _this.target, _eye ); 346 | 347 | _this.checkDistances(); 348 | 349 | _this.object.lookAt( _this.target ); 350 | 351 | if ( lastPosition.distanceToSquared( _this.object.position ) > EPS ) { 352 | 353 | _this.dispatchEvent( changeEvent ); 354 | 355 | lastPosition.copy( _this.object.position ); 356 | 357 | } 358 | 359 | }; 360 | 361 | this.reset = function () { 362 | 363 | _state = STATE.NONE; 364 | _prevState = STATE.NONE; 365 | 366 | _this.target.copy( _this.target0 ); 367 | _this.object.position.copy( _this.position0 ); 368 | _this.object.up.copy( _this.up0 ); 369 | 370 | _eye.subVectors( _this.object.position, _this.target ); 371 | 372 | _this.object.lookAt( _this.target ); 373 | 374 | _this.dispatchEvent( changeEvent ); 375 | 376 | lastPosition.copy( _this.object.position ); 377 | 378 | }; 379 | 380 | // listeners 381 | 382 | function keydown( event ) { 383 | 384 | if ( _this.enabled === false ) return; 385 | 386 | window.removeEventListener( 'keydown', keydown ); 387 | 388 | _prevState = _state; 389 | 390 | if ( _state !== STATE.NONE ) { 391 | 392 | return; 393 | 394 | } else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ) { 395 | 396 | _state = STATE.ROTATE; 397 | 398 | } else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ) { 399 | 400 | _state = STATE.ZOOM; 401 | 402 | } else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ) { 403 | 404 | _state = STATE.PAN; 405 | 406 | } 407 | 408 | } 409 | 410 | function keyup( event ) { 411 | 412 | if ( _this.enabled === false ) return; 413 | 414 | _state = _prevState; 415 | 416 | window.addEventListener( 'keydown', keydown, false ); 417 | 418 | } 419 | 420 | function mousedown( event ) { 421 | 422 | if ( _this.enabled === false ) return; 423 | 424 | event.preventDefault(); 425 | event.stopPropagation(); 426 | 427 | if ( _state === STATE.NONE ) { 428 | 429 | _state = event.button; 430 | 431 | } 432 | 433 | if ( _state === STATE.ROTATE && !_this.noRotate ) { 434 | 435 | _rotateStart.copy( getMouseProjectionOnBall( event.pageX, event.pageY ) ); 436 | _rotateEnd.copy( _rotateStart ); 437 | 438 | } else if ( _state === STATE.ZOOM && !_this.noZoom ) { 439 | 440 | _zoomStart.copy( getMouseOnScreenByEvent( event ) ); 441 | _zoomEnd.copy(_zoomStart); 442 | 443 | } else if ( _state === STATE.PAN && !_this.noPan ) { 444 | 445 | _panStart.copy( getMouseOnScreenByEvent( event ) ); 446 | _panEnd.copy(_panStart) 447 | 448 | } 449 | 450 | document.addEventListener( 'mousemove', mousemove, false ); 451 | document.addEventListener( 'mouseup', mouseup, false ); 452 | 453 | _this.dispatchEvent( startEvent ); 454 | 455 | } 456 | 457 | function mousemove( event ) { 458 | 459 | if ( _this.enabled === false ) return; 460 | 461 | event.preventDefault(); 462 | event.stopPropagation(); 463 | 464 | if ( _state === STATE.ROTATE && !_this.noRotate ) { 465 | 466 | _rotateEnd.copy( getMouseProjectionOnBall( event.pageX, event.pageY ) ); 467 | 468 | } else if ( _state === STATE.ZOOM && !_this.noZoom ) { 469 | 470 | _zoomEnd.copy( getMouseOnScreenByEvent( event ) ); 471 | 472 | } else if ( _state === STATE.PAN && !_this.noPan ) { 473 | 474 | _panEnd.copy( getMouseOnScreenByEvent( event ) ); 475 | 476 | } 477 | 478 | } 479 | 480 | function mouseup( event ) { 481 | 482 | if ( _this.enabled === false ) return; 483 | 484 | event.preventDefault(); 485 | event.stopPropagation(); 486 | 487 | _state = STATE.NONE; 488 | 489 | document.removeEventListener( 'mousemove', mousemove ); 490 | document.removeEventListener( 'mouseup', mouseup ); 491 | _this.dispatchEvent( endEvent ); 492 | 493 | } 494 | 495 | function mousewheel( event ) { 496 | 497 | if ( _this.enabled === false ) return; 498 | 499 | event.preventDefault(); 500 | event.stopPropagation(); 501 | 502 | var delta = 0; 503 | 504 | if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9 505 | 506 | delta = event.wheelDelta / 40; 507 | 508 | } else if ( event.detail ) { // Firefox 509 | 510 | delta = - event.detail / 3; 511 | 512 | } 513 | 514 | _zoomStart.y += delta * 0.01; 515 | _this.dispatchEvent( startEvent ); 516 | _this.dispatchEvent( endEvent ); 517 | 518 | } 519 | 520 | function touchstart( event ) { 521 | 522 | if ( _this.enabled === false ) return; 523 | 524 | switch ( event.touches.length ) { 525 | 526 | case 1: 527 | _state = STATE.TOUCH_ROTATE; 528 | _rotateStart.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); 529 | _rotateEnd.copy( _rotateStart ); 530 | break; 531 | 532 | case 2: 533 | _state = STATE.TOUCH_ZOOM_PAN; 534 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 535 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 536 | _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy ); 537 | 538 | var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; 539 | var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; 540 | _panStart.copy( getMouseOnScreen( x, y ) ); 541 | _panEnd.copy( _panStart ); 542 | break; 543 | 544 | default: 545 | _state = STATE.NONE; 546 | 547 | } 548 | _this.dispatchEvent( startEvent ); 549 | 550 | 551 | } 552 | 553 | function touchmove( event ) { 554 | 555 | if ( _this.enabled === false ) return; 556 | 557 | event.preventDefault(); 558 | event.stopPropagation(); 559 | 560 | switch ( event.touches.length ) { 561 | 562 | case 1: 563 | _rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); 564 | break; 565 | 566 | case 2: 567 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 568 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 569 | _touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy ); 570 | 571 | var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; 572 | var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; 573 | _panEnd.copy( getMouseOnScreen( x, y ) ); 574 | break; 575 | 576 | default: 577 | _state = STATE.NONE; 578 | 579 | } 580 | 581 | } 582 | 583 | function touchend( event ) { 584 | 585 | if ( _this.enabled === false ) return; 586 | 587 | switch ( event.touches.length ) { 588 | 589 | case 1: 590 | _rotateEnd.copy( getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ) ); 591 | _rotateStart.copy( _rotateEnd ); 592 | break; 593 | 594 | case 2: 595 | _touchZoomDistanceStart = _touchZoomDistanceEnd = 0; 596 | 597 | var x = ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2; 598 | var y = ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2; 599 | _panEnd.copy( getMouseOnScreen( x, y ) ); 600 | _panStart.copy( _panEnd ); 601 | break; 602 | 603 | } 604 | 605 | _state = STATE.NONE; 606 | _this.dispatchEvent( endEvent ); 607 | 608 | } 609 | 610 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 611 | 612 | this.domElement.addEventListener( 'mousedown', mousedown, false ); 613 | 614 | this.domElement.addEventListener( 'mousewheel', mousewheel, false ); 615 | this.domElement.addEventListener( 'DOMMouseScroll', mousewheel, false ); // firefox 616 | 617 | this.domElement.addEventListener( 'touchstart', touchstart, false ); 618 | this.domElement.addEventListener( 'touchend', touchend, false ); 619 | this.domElement.addEventListener( 'touchmove', touchmove, false ); 620 | 621 | window.addEventListener( 'keydown', keydown, false ); 622 | window.addEventListener( 'keyup', keyup, false ); 623 | 624 | this.handleResize(); 625 | 626 | // force an update at start 627 | this.update(); 628 | 629 | }; 630 | 631 | THREE.TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 632 | --------------------------------------------------------------------------------